dmsc Posted February 29, 2020 Share Posted February 29, 2020 I finally managed to finish the DLI work, so here is the new FastBasic version, ready for the 2020 tenliners! You can now create a DLI with a simple syntax, capable of changing registers at multiple locations in the screen. The full documentation is in the manual at https://github.com/dmsc/fastbasic/blob/master/manual.md#display-list-interrupts , but here is a little sample of the DLI support, showing how to multiplex two P/M to create 4 moving two-color sprites: ' Player shapes DATA p1() BYTE = $E7,$81,$81,$E7 DATA p2() BYTE = $18,$3C,$3C,$18 ' Players horizontal positions DATA pos() BYTE = $40,$60,$80,$A0 ' Players colors DATA c1() BYTE = $28,$88,$C8,$08 DATA c2() BYTE = $2E,$80,$CE,$06 ' Our DLI writes the position and ' colors to Player 1 and Player 2 DLI SET d3 = pos INTO $D000, pos INTO $D001, DLI = c1 INTO $D012, c2 INTO $D013 ' Setup screen GRAPHICS 0 : PMGRAPHICS 2 ' Setup our 4 DLI and Players FOR I = 8 TO 20 STEP 4 POKE DPEEK(560) + I, 130 MOVE ADR(p1), PMADR(0)+I*4+5,4 MOVE ADR(p2), PMADR(1)+I*4+5,4 NEXT ' Activate DLI DLI d3 ? "Press a Key" ' Move objects! REPEAT PAUSE 0 pos(0) = pos(0) + 2 pos(1) = pos(1) + 1 pos(2) = pos(2) - 1 pos(3) = pos(3) - 2 UNTIL KEY() ' Disable DLI and ends DLI Attached is the resulting XEX, this is the resulting screen: The new release is over github, download at: https://github.com/dmsc/fastbasic/releases/tag/v4.3 Have Fun! dlitest.xex 17 3 Quote Link to comment Share on other sites More sharing options...
Preppie Posted February 29, 2020 Share Posted February 29, 2020 Downloaded, now have to get time to check it out. I'm sure it's amazing. Quote Link to comment Share on other sites More sharing options...
+slx Posted February 29, 2020 Share Posted February 29, 2020 WOW, DLIs in BASIC! Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted February 29, 2020 Share Posted February 29, 2020 Continuous refinement, improvement and optimisation. Great! Quote Link to comment Share on other sites More sharing options...
Wrathchild Posted February 29, 2020 Share Posted February 29, 2020 Can DLI x be used within another DLI to chain them? If so I think you'll need a similar VBI statement within which you'd use a DLI statement to reset the 1st interrupt routine? Quote Link to comment Share on other sites More sharing options...
dmsc Posted February 29, 2020 Author Share Posted February 29, 2020 Hi! 2 hours ago, Wrathchild said: Can DLI x be used within another DLI to chain them? If so I think you'll need a similar VBI statement within which you'd use a DLI statement to reset the 1st interrupt routine? No, but you don't need that, as the DLI takes values from arrays you simply write different values on each call. If you don't need to alter one register, you simply write the same value each time. The DLi counter is already reset on the VBI. Have Fun! Quote Link to comment Share on other sites More sharing options...
+MrFish Posted February 29, 2020 Share Posted February 29, 2020 Cool! I guess it's time to update my site and the manual covers -- which I've now done. 3 Quote Link to comment Share on other sites More sharing options...
Wrathchild Posted February 29, 2020 Share Posted February 29, 2020 (edited) 6 hours ago, dmsc said: No, but you don't need that, as the DLI takes values from arrays you simply write different values on each call. That makes it quite a restrictive model and a not too-realistic use-case of DLIs, i.e. you'll potentially need to limit the number of changes due to time criticality. 6 hours ago, dmsc said: The DLi counter is already reset on the VBI. The 'DLi counter' you are exploiting is the COLRSH register ($4F). Once the attract mode is triggered this is given a value from middle clock register ($13) during the VBI. (e.g. "bx db(COLRSH)>4") Whilst in most cases just colours or sprite positions are being set and so at most will then to stop being animated and give them different colours, this is dangerous as it means that access to DATA BYTE arrays are potentially out of bounds. As the DLI vector isn't refresh by the OS during the Vblank (e.g. checked with "bx write=$200") this means that the last DLI is responsible for setting the first DLIs address. During development this often can cause the effect of 'rolling' when the number of interrupts in the Dlist don't match the number in your chain. Hence setting the first DLI in the VBlank is a good help towards helping to rectify that. I've experimented and demonstrate what I mean in the attached modified example. From this I would say it would be nice if DATA WORD support could be added, e.g. ' Dli addresses DATA dli_adr() WORD = 0,0,0 ' Our DLI writes the position and ' colors to Player 1 and Player 2 DLI SET d3a = pos INTO $D000, pos INTO $D001, DLI = $28 INTO $D012, $2E INTO $D013, DLI = dli_adr INTO $200 DLI SET d3b = pos INTO $D000, pos INTO $D001, DLI = $88 INTO $D012, DLI = dli_adr INTO $200 DLI SET d3c = pos INTO $D000, pos INTO $D001, DLI = $CE INTO $D013, DLI = dli_adr INTO $200 dli_adr(0) = ADR(d3b) dli_adr(1) = ADR(d3c) dli_adr(2) = ADR(d3a) So initially I'd thought that the last line of a dli could be: DLI = DLI d3b but this isn't possible and so considering if the above was working then the dli_adr could be eliminated by using: DLI = d3b INTO $200 but as I understand it the lack of forward reference inhibits writing it that way and so the array is suffices. Which means that actually it is reusable between different screens such as a title page and game screen by applying the addresses of different DLI definitions. Keep up the good work! altdli.bas altdli.xex Edited February 29, 2020 by Wrathchild Elaboration on COLRSH use Quote Link to comment Share on other sites More sharing options...
vitoco Posted February 29, 2020 Share Posted February 29, 2020 (edited) Current implementation of DLIs in FastBasic is a very simple one. It is not managed as a chain of routines, it just enables a single routine that can be called multiple times (many scan lines), so it always changes the same registers, but the values might be constants or obtained from DATA arrays. Of course you can define multiple DLI routines in FastBasic (DLI SET), but you can enable only one at a time, or disable them at all. I was also affraid of the use of COLRSH as the counter when in attract mode for the same reason you are exposing, but I forced that situation in the game I'm working on and I couldn't get a strange behavior. To use complex DLI routines with chains and such, you can always enable them with a custom VBI embedding them as it has always been done in any BASIC flavor (with POKEs ). But it is very interesting what you are proposing. I think that chains could be done using current implementation in this way: ' Dli addresses DATA dli_adr_l() BYTE = 0,0,0 DATA dli_adr_h() BYTE = 0,0,0 ' Our DLI writes the position and ' colors to Player 1 and Player 2 DLI SET d3a = pos INTO $D000, pos INTO $D001, DLI = $28 INTO $D012, $2E INTO $D013, DLI = dli_adr_l INTO $200, dli_adr_h INTO $201 DLI SET d3b = pos INTO $D000, pos INTO $D001, DLI = $88 INTO $D012, DLI = dli_adr_l INTO $200, dli_adr_h INTO $201 DLI SET d3c = pos INTO $D000, pos INTO $D001, DLI = $CE INTO $D013, DLI = dli_adr_l INTO $200, dli_adr_h INTO $201 dli_adr_l(0) = ADR(d3b) mod 256 dli_adr_h(0) = ADR(d3b) / 256 dli_adr_l(1) = ADR(d3c) mod 256 dli_adr_h(1) = ADR(d3c) / 256 dli_adr_l(2) = ADR(d3a) mod 256 dli_adr_h(2) = ADR(d3a) / 256 Then, you can change very different system registers in each DLI, but I hope that the time for that will be enougth. UPDATE: I just downloaded and opened your example, and got almost the same code than I wrote in this post. ? Edited February 29, 2020 by vitoco Update... Quote Link to comment Share on other sites More sharing options...
Wrathchild Posted February 29, 2020 Share Posted February 29, 2020 I think the ideal could be something like this - as if you define the routines in reverse order then you can reference them, and the last does not need to set the first if this is done in the VBI, e.g. DLI SET dli3 = pos INTO $D000, pos INTO $D001, DLI = $CE INTO $D013 DLI SET dli2 = pos INTO $D000, pos INTO $D001, DLI = $88 INTO $D012, DLI = DLI dli3 DLI SET dli1 = pos INTO $D000, pos INTO $D001, DLI = $28 INTO $D012, $2E INTO $D013, DLI = DLI dli2 VBI SET vb1 = DLI dli1 ... VBI vb1 As an example, the VBI can then also be used to call a linked RMT player routine Quote Link to comment Share on other sites More sharing options...
777ismyname Posted March 1, 2020 Share Posted March 1, 2020 Thank you for the new release, @dmsc Quote Link to comment Share on other sites More sharing options...
Wrathchild Posted March 1, 2020 Share Posted March 1, 2020 (edited) I'd forgotten to mention another idea, let the user state WSYNC as in the example you are in Gr.0 and even with a DLI stated each line then you can only update registers every 8 lines. DLI SET graded_dli = $04 INTO $D017, DLI = WSYNC, $06 INTO $D017, DLI = WSYNC, $08 INTO $D017, DLI = WSYNC, $0C INTO $D017, DLI = WSYNC, $0C INTO $D017, DLI = WSYNC, $08 INTO $D017, DLI = WSYNC, $06 INTO $D017, DLI = WSYNC, $04 INTO $D017 The way you've coded things already automatically injects the WSYNC store between the LDA/STA of the first 'INTO' and so the same principle can apply to an INTO preceded with a WSYNC. This would permit multiple WSYNC entries to be used, e.g. re-writing the above: DLI SET graded_dli = $04 INTO $D017, DLI = WSYNC, $06 INTO $D017, DLI = WSYNC, $08 INTO $D017, DLI = WSYNC, $0C INTO $D017, DLI = WSYNC, WSYNC, $08 INTO $D017, DLI = WSYNC, $06 INTO $D017, DLI = WSYNC, $04 INTO $D017 resulting in something like: fb_lbl_graded_dli: .byte 72 .byte 138 .byte 72 .byte 169 .byte $04 .byte 141 .word WSYNC .byte 141 .word 53271 .byte 169 .byte $06 .byte 141 .word WSYNC .byte 141 .word 53271 .byte 169 .byte $08 .byte 141 .word WSYNC .byte 141 .word 53271 .byte 169 .byte $0C .byte 141 .word WSYNC .byte 141 .word 53271 .byte 141 .word WSYNC .byte 169 .byte $08 .byte 141 .word WSYNC .byte 141 .word 53271 .byte 169 .byte $06 .byte 141 .word WSYNC .byte 141 .word 53271 .byte 169 .byte $04 .byte 141 .word WSYNC .byte 141 .word 53271 .byte 230 .byte COLRSH .byte 104 .byte 170 .byte 104 .byte 64 [EDIT] So taking the test.bas, building it with 'fb-int' and then replacing the DLI code in the 'asm' file with that given above and then building that with: cl65 -t atari -C fastbasic.cfg -o test.xex test.asm fastbasic-int.lib gives: test.asm test.bas test.xex Edited March 1, 2020 by Wrathchild Example added Quote Link to comment Share on other sites More sharing options...
dmsc Posted March 1, 2020 Author Share Posted March 1, 2020 Hi! First, thank to all for the suggestions. 1 hour ago, Wrathchild said: That makes it quite a restrictive model and a not too-realistic use-case of DLIs, i.e. you'll potentially need to limit the number of changes due to time criticality. Yes, it is somewhat restrictive, but I designed it to be as simple as possible, it is BASIC after all 1 hour ago, Wrathchild said: The 'DLi counter' you are exploiting is the COLRSH register ($4F). Once the attract mode is triggered this is given a value from middle clock register ($13) during the VBI. (e.g. "bx db(COLRSH)>4") Whilst in most cases just colours or sprite positions are being set and so at most will then to stop being animated and give them different colours, this is dangerous as it means that access to DATA BYTE arrays are potentially out of bounds. Ah, you already saw my little trick I discovered that trick by trying to implement it using VBI and discovered it became complicated really fast..., then reading the sources of ALtirra OS there phaeron says that Pole Position depends on it, so that gives a precedent. As you said, if you use it as intended it does not causes problems, simply the colors would be wrong. Read accesses would be out of bounds, but normally still in RAM. 1 hour ago, Wrathchild said: As the DLI vector isn't refresh by the OS during the Vblank (e.g. checked with "bx write=$200") this means that the last DLI is responsible for setting the first DLIs address. During development this often can cause the effect of 'rolling' when the number of interrupts in the Dlist don't match the number in your chain. Hence setting the first DLI in the VBlank is a good help towards helping to rectify that. Yes. My first implementation of multiple DLIs used this, but it was too much code, as a VBI would need to be installed on DLI activation - but only if it was not already installed - and the VBI needs to be removed on program termination. This made the code a lot bigger and complicated, so I finally discarded the idea. 1 hour ago, Wrathchild said: I've experimented and demonstrate what I mean in the attached modified example. From this I would say it would be nice if DATA WORD support could be added, e.g. ' Dli addresses DATA dli_adr() WORD = 0,0,0 ' Our DLI writes the position and ' colors to Player 1 and Player 2 DLI SET d3a = pos INTO $D000, pos INTO $D001, DLI = $28 INTO $D012, $2E INTO $D013, DLI = dli_adr INTO $200 DLI SET d3b = pos INTO $D000, pos INTO $D001, DLI = $88 INTO $D012, DLI = dli_adr INTO $200 DLI SET d3c = pos INTO $D000, pos INTO $D001, DLI = $CE INTO $D013, DLI = dli_adr INTO $200 dli_adr(0) = ADR(d3b) dli_adr(1) = ADR(d3c) dli_adr(2) = ADR(d3a) Yes, that would be nice. Currently is not easy to support reading from a word array in a DLI, as there is only the X register available, you would need to add a "LDA COLRSH : ASL : TAX ", and then the X register would need to be reloaded if you access word arrays again, this is slow and would need a lot of code in the IDE. And finally, using the index to write into the DLI vector would be dangerous as the index could go out of bounds, as you said above. 1 hour ago, Wrathchild said: So initially I'd thought that the last line of a dli could be: DLI = DLI d3b Adding this it would be possible. 1 hour ago, Wrathchild said: but this isn't possible and so considering if the above was working then the dli_adr could be eliminated by using: DLI = d3b INTO $200 but as I understand it the lack of forward reference inhibits writing it that way and so the array is suffices. IMHO, I prefer the syntax with "DLI", as the "INTO" writes only a byte in all other cases. 1 hour ago, Wrathchild said: Which means that actually it is reusable between different screens such as a title page and game screen by applying the addresses of different DLI definitions. Keep up the good work! altdli.bas 1.25 kB · 2 downloads altdli.xex 1.66 kB · 2 downloads Very good!! You can simplify the "dli_low(0) = ADR(d3b) mod 256" into "dli_low(0) = ADR(d3b)", as you are writing to a byte array and the high byte is discarded. 17 hours ago, Wrathchild said: I think the ideal could be something like this - as if you define the routines in reverse order then you can reference them, and the last does not need to set the first if this is done in the VBI, e.g. DLI SET dli3 = pos INTO $D000, pos INTO $D001, DLI = $CE INTO $D013 DLI SET dli2 = pos INTO $D000, pos INTO $D001, DLI = $88 INTO $D012, DLI = DLI dli3 DLI SET dli1 = pos INTO $D000, pos INTO $D001, DLI = $28 INTO $D012, $2E INTO $D013, DLI = DLI dli2 VBI SET vb1 = DLI dli1 ... VBI vb1 As an example, the VBI can then also be used to call a linked RMT player routine I like that syntax. I could reuse most of the DLI implementation to do the VBI one. What I would try to resolve is that by including a VBI, all FastBasic programs would need to include VBI initialization and cleanup, just in case the program uses it. One question: ¿should VBI vector the immediate or the deferred VBI? 15 hours ago, Wrathchild said: I'd forgotten to mention another idea, let the user state WSYNC as in the example you are in Gr.0 and even with a DLI stated each line then you can only update registers every 8 lines. DLI SET graded_dli = $04 INTO $D017, DLI = WSYNC, $06 INTO $D017, DLI = WSYNC, $08 INTO $D017, DLI = WSYNC, $0C INTO $D017, DLI = WSYNC, WSYNC, $08 INTO $D017, DLI = WSYNC, $06 INTO $D017, DLI = WSYNC, $04 INTO $D017 You can already do this by writing "$00 INTO $D40A", but I understand the using "WSYNC" would mean a little less code. But, I think I would prefer another word, more clear to the BASIC context, like "NEXT": DLI SET graded_dli = $04 INTO $D017, DLI = NEXT $06 INTO $D017, DLI = NEXT $08 INTO $D017, DLI = NEXT $0C INTO $D017 This is easy to implement, as the parser can transform NEXT directly to "STA WSYNC", and you notice that I removed the comma after the NEXT to make easier to parse. To understand that the DLI support inf FastBasic is extremely simple, look at the full changes to the sources the implement it: https://github.com/dmsc/fastbasic/compare/8ea6ad8...37bf4ca It is only one change to the parser file, adding 29 lines total. The line 669 says, when parsing a statement, if the parser founds "DLI" (abbreviated "DL."), parse a "DLI_COMMAND". Then, line 580 is the rule to parse a DLI_COMMAND, with four options: first if there is a "SET", parse the initial part of the DLI, generating the starting code; if there is an "=" sign, parse a continuation, if there is a LABEL, activate the DLI and finally in any other case, deactivate the DLI. Rule "DLI_LDA" defines the part of the DLI that produces a "LDA" (before the word "INTO"), "DLI_STA" defines the second part, and "DLI_CODES" is the comma separated list of DLI_LDA and DLI_STA. Adding the "NEXT" token would mean adding another option to "DLI_LDA", one line with: "Next" emit { 141, &WSYNC } DLI_LDA This detects the word "NEXT", emits the code for "STA WSYNC" and then calls DLI_LDA again to parse the rest of the statement. This is 9 extra bytes in the IDE. Have Fun! 2 Quote Link to comment Share on other sites More sharing options...
Wrathchild Posted March 2, 2020 Share Posted March 2, 2020 (edited) 20 hours ago, dmsc said: One question: ¿should VBI vector the immediate or the deferred VBI? My recommendation would be VVBLKD as out-of-the-box the OS is pointing this ($224) to the Pull Y/X/A and RTI code vectored to by XITVBV. So you would hook this a call to SETVBV with A=7 and XY with the routine's address. At the end of the constructed VBI code, you would end with "JMP XITVBI". Injecting "LDA #0, STA $4F" into the VBI code might be OK to do (although does affect attract mode colours) but an alternative could be to offer a command within the VBI such as "NO_ATTRACT" which performs "LDA #0, STA $4D"? Perhaps the $224.w value can be copied to $238.w when setting up (before the SETVBV call) as Mapping states that as spare? This can then be loaded into XY and SETVBV called to restore. Edited March 2, 2020 by Wrathchild VBI restore 1 Quote Link to comment Share on other sites More sharing options...
dmsc Posted March 5, 2020 Author Share Posted March 5, 2020 Hi! On 3/2/2020 at 9:13 AM, Wrathchild said: My recommendation would be VVBLKD as out-of-the-box the OS is pointing this ($224) to the Pull Y/X/A and RTI code vectored to by XITVBV. So you would hook this a call to SETVBV with A=7 and XY with the routine's address. At the end of the constructed VBI code, you would end with "JMP XITVBI". Injecting "LDA #0, STA $4F" into the VBI code might be OK to do (although does affect attract mode colours) but an alternative could be to offer a command within the VBI such as "NO_ATTRACT" which performs "LDA #0, STA $4D"? Perhaps the $224.w value can be copied to $238.w when setting up (before the SETVBV call) as Mapping states that as spare? This can then be loaded into XY and SETVBV called to restore. Thanks for the ideas. I created an issue on github to keep track of this enhancements: https://github.com/dmsc/fastbasic/issues/21 , https://github.com/dmsc/fastbasic/issues/22 and https://github.com/dmsc/fastbasic/issues/23 Have Fun! 1 Quote Link to comment Share on other sites More sharing options...
gotoAtari Posted March 5, 2020 Share Posted March 5, 2020 So... Wow this is pretty cool! I was actually poking around trying to find out which was faster Turbo Basic or Fast Basic when I came across this. This is pretty impressive for not having to bust out the assembler! Alas turbo basic cannot compete with this! After seeing this post I poked around and got the impression you wrote this in CC65? I used that a few years back and thought it was best used from code structure and then you cleaned up the assembly code to make it fast later? Does CC65 give you decent performance these days without manually patching up the generated code? I was actually wondering if compiled fast basic was faster than CC65 until I read you were using it! :) Later, Pete Quote Link to comment Share on other sites More sharing options...
vitoco Posted March 5, 2020 Share Posted March 5, 2020 I like the idea of a special syntax to force some extra WSYNC in a DLI, buy I don't like the use of the "NEXT" word for it, as it has another meaning in BASIC. Keep "WSYNC" or use "WAIT" ("W."). About the remove of the comma after the WSYNC, I'm not sure if it is a good idea, as I would like to do something like this: DLI SET half = $66 INTO 710, NEXT NEXT NEXT NEXT $84 INTO 710 With the comma, I could say (using "WAIT"): DLI SET half = $66 INTO 710, WAIT, WAIT, WAIT, WAIT, $84 INTO 710 or, better: DLI SET half = $66 INTO 710, WAIT 4, $84 INTO 710 Can the parser repeat the "emit" based on a constant parameter like in "WAIT 4"? About chaining DLIs, I agree the use of another DLI reference: DLI SET dli1 = pos INTO $D000, pos INTO $D001, DLI = $28 INTO $D012, $2E INTO $D013, DLI = DLI dli2 It might be strange that DLIs must be defined in the inverse order, but I understand the language (parser) restrictions. Quote Link to comment Share on other sites More sharing options...
dmsc Posted March 5, 2020 Author Share Posted March 5, 2020 Hi! 2 hours ago, vitoco said: I like the idea of a special syntax to force some extra WSYNC in a DLI, buy I don't like the use of the "NEXT" word for it, as it has another meaning in BASIC. Keep "WSYNC" or use "WAIT" ("W."). About the remove of the comma after the WSYNC, I'm not sure if it is a good idea, as I would like to do something like this: DLI SET half = $66 INTO 710, NEXT NEXT NEXT NEXT $84 INTO 710 With the comma, I could say (using "WAIT"): DLI SET half = $66 INTO 710, WAIT, WAIT, WAIT, WAIT, $84 INTO 710 Well, it is almost the same as DLI SET half = $66 INTO 710, WAIT WAIT WAIT WAIT $84 INTO 710 And you probably don't realize, but the problem is not the "," - it is easy to add the parsing of a comma to the "WAIT" word, the problem is the *spaces* between the WAIT and the ",". Currently, the only way to skip spaces in the parser is to split a rule into multiple rules, spaces are skipped always on the start of any rule, you can't specify skipping spaces on arbitrary locations. This is the reason that there is a rule "EQUAL" that just parses an "=" sign. I thought of adding a bytecode to the parser to skip white spaces, but in my earlier tests it was slower and bigger. Perhaps now that the parser has mucho more states it makes sense again. 2 hours ago, vitoco said: or, better: DLI SET half = $66 INTO 710, WAIT 4, $84 INTO 710 Can the parser repeat the "emit" based on a constant parameter like in "WAIT 4"? Not currently. To make that work you would need to implement a routine that retrieves the number from the already parser program (as the "parse number" command writes the result to the program stream) and then manually emit as many "STA WSYNC" as needed. 2 hours ago, vitoco said: About chaining DLIs, I agree the use of another DLI reference: DLI SET dli1 = pos INTO $D000, pos INTO $D001, DLI = $28 INTO $D012, $2E INTO $D013, DLI = DLI dli2 It might be strange that DLIs must be defined in the inverse order, but I understand the language (parser) restrictions. If you have any suggestions, you can also comment in the github issues, so I don't forget about it. Have Fun! Quote Link to comment Share on other sites More sharing options...
vitoco Posted March 5, 2020 Share Posted March 5, 2020 49 minutes ago, dmsc said: Hi! Well, it is almost the same as DLI SET half = $66 INTO 710, WAIT WAIT WAIT WAIT $84 INTO 710 And you probably don't realize, but the problem is not the "," - it is easy to add the parsing of a comma to the "WAIT" word, the problem is the *spaces* between the WAIT and the ",". Currently, the only way to skip spaces in the parser is to split a rule into multiple rules, spaces are skipped always on the start of any rule, you can't specify skipping spaces on arbitrary locations. This is the reason that there is a rule "EQUAL" that just parses an "=" sign. I thought of adding a bytecode to the parser to skip white spaces, but in my earlier tests it was slower and bigger. Perhaps now that the parser has mucho more states it makes sense again. Not currently. To make that work you would need to implement a routine that retrieves the number from the already parser program (as the "parse number" command writes the result to the program stream) and then manually emit as many "STA WSYNC" as needed. If you have any suggestions, you can also comment in the github issues, so I don't forget about it. Have Fun! You are right, I think I do not realize the problem of "spaces". Is there any difference between: DLISETx=0INTO710,15INTO711 and: DLI SET x = 0 INTO 710 , 15 INTO 711 ??? Of which kind of split are you talking about? Or for "space" you mean a check for an optional argument just before the comma? 49 minutes ago, dmsc said: Not currently. To make that work you would need to implement a routine that retrieves the number from the already parser program (as the "parse number" command writes the result to the program stream) and then manually emit as many "STA WSYNC" as needed. I preferred the "comma" version because of "WAIT 4," won't require extra tests if it says "WAIT 4 INTO 710". I guess that "4 WAIT" will have the same problem in current implementation of the parser. Quote Link to comment Share on other sites More sharing options...
dmsc Posted March 5, 2020 Author Share Posted March 5, 2020 Hi! 25 minutes ago, vitoco said: You are right, I think I do not realize the problem of "spaces". Is there any difference between: DLISETx=0INTO710,15INTO711 and: DLI SET x = 0 INTO 710 , 15 INTO 711 ??? For the parser, yes, it is different. The structure of the parser does not include spaces, because those are optional. For example, if you have a rule that says: MY_RULE: "WORD" MY_RULE "NO" The generated parser will: - For the call of MY_RULE, - Skip any white spaces on the input, - Push the current position in the input, then: - Check if the character 'W', 'O', 'R' and 'D' are present in the input (advancing the position), if true call to MY_RULE, if returns true, keep the new position and state and return true from this rule. - Return to the saved position, then: - Check if the character 'N' and 'O' are present in the input, if true keeps the current position and return true from this rule - None matched, restore saved position (pop from stack) and return false. The main point on the above is, with that construct, any of this examples matches: - NO - WORD NO - WORDNO - WORD WORD NO - WORDWORDWORD NO But, this does not matches: - N O - WO RDNO I hope all makes sense. 25 minutes ago, vitoco said: Of which kind of split are you talking about? Based on the example above, just try to write a parser that matches on this input: - DLI WSYNC , WSYNC,WSYNC How many rules you need? 25 minutes ago, vitoco said: Or for "space" you mean a check for an optional argument just before the comma? I preferred the "comma" version because of "WAIT 4," won't require extra tests if it says "WAIT 4 INTO 710". I guess that "4 WAIT" will have the same problem in current implementation of the parser. Yes, the parser just is not that smart Have Fun! Quote Link to comment Share on other sites More sharing options...
vitoco Posted March 5, 2020 Share Posted March 5, 2020 20 minutes ago, dmsc said: Hi! For the parser, yes, it is different. The structure of the parser does not include spaces, because those are optional. For example, if you have a rule that says: MY_RULE: "WORD" MY_RULE "NO" The generated parser will: - For the call of MY_RULE, - Skip any white spaces on the input, - Push the current position in the input, then: - Check if the character 'W', 'O', 'R' and 'D' are present in the input (advancing the position), if true call to MY_RULE, if returns true, keep the new position and state and return true from this rule. - Return to the saved position, then: - Check if the character 'N' and 'O' are present in the input, if true keeps the current position and return true from this rule - None matched, restore saved position (pop from stack) and return false. The main point on the above is, with that construct, any of this examples matches: - NO - WORD NO - WORDNO - WORD WORD NO - WORDWORDWORD NO But, this does not matches: - N O - WO RDNO I hope all makes sense. Yes... I guess ? 20 minutes ago, dmsc said: Based on the example above, just try to write a parser that matches on this input: - DLI WSYNC , WSYNC,WSYNC How many rules you need? In your example, you use the token "NO" as an input terminator which is tricky in terms of the parser to get a success. Without such terminator for this input, I don't know how to return true for this input. Anyway, knowing that I'm missing something, I guess that you need 3 rules: RULE1: "DLI" RULE2 RULE3 RULE2: "WSYNC" RULE3: "," RULE2 RULE3 Without commas, it should be something like this: RULE1: "DLI" RULE2 RULE2: "WSYNC" RULE2 If I was right, you need an extra rule to parse the comma delimiter. But I don't know how many bytes are those for the compiled parser or table. BTW, sorry about this off-topic! Quote Link to comment Share on other sites More sharing options...
dmsc Posted March 5, 2020 Author Share Posted March 5, 2020 Hi! 3 hours ago, vitoco said: Yes... I guess ? In your example, you use the token "NO" as an input terminator which is tricky in terms of the parser to get a success. Without such terminator for this input, I don't know how to return true for this input. You can use the "E_EOL" rule, this returns true if you already consumed all the input line (up until a end-of-line or a ":"). But normally this is not necessary, as the parser has another feature: if on the last line of a rule you write the special word "pass" then the rule returns true if nothing was matched, making the whole rule optional. This allows writing rules that consume as much space as needed, in my example above you simply replace the "NO" with pass: MY_RULE: "WORD" MY_RULE The above is exactly like the regular expression "\(\s*WORD\)*" . FastBasic parser is based on PEG, and you can express any regular-expression as a PEG, but not any PEG can be expressed as a regular-expression. 3 hours ago, vitoco said: Anyway, knowing that I'm missing something, I guess that you need 3 rules: RULE1: "DLI" RULE2 RULE3 RULE2: "WSYNC" RULE3: "," RULE2 RULE3 Yes, you need three rules. You only missed a terminator (as written, your grammar only parses infinite inputs ), to terminate before the "," you need to add a "pass" to the RULE3. 3 hours ago, vitoco said: Without commas, it should be something like this: RULE1: "DLI" RULE2 RULE2: "WSYNC" RULE2 If I was right, you need an extra rule to parse the comma delimiter. But I don't know how many bytes are those for the compiled parser or table. Yes! So, adding the comma needs an extra rule. Each new rule is: 2 bytes for the rule-address in the parsing tables, and at least two bytes for the rule bytecode - one bytecode to terminate the first line and one bytecode to terminate the rule. And you need one byte to call the rule, so the total is 5 bytes for adding a rule. But there are two other problems: - There is a limit on 127 rules in the parser. Currently the floating-point parser uses 113 rules, the integer-only parser uses 103. So, I still have 14 rules left. - Each additional rule makes the parsing slower and uses 4 bytes of parsing stack (but only when the rule is used). This is specially important on rules that parse a list of values, as you are calling to the same rule for each new value. So, with the first example, each use of the "WSYNC" word calls two rules, using 8 bytes of stack, so the limit is about 30 "WSYNC" words 3 hours ago, vitoco said: BTW, sorry about this off-topic! I like explaining the FastBasic parser, I hope someone could help by optimizing the rules - I do know that there is space for optimizations there! Have Fun! 2 Quote Link to comment Share on other sites More sharing options...
RALPH! Posted March 23, 2020 Share Posted March 23, 2020 Are there ways to include assembler code that compile with the program? That way there not be need use separate assembler and make binary for make into data statements. Mad Pascal and 7800 Basic have ASM statement. Quote Link to comment Share on other sites More sharing options...
Wrathchild Posted March 23, 2020 Share Posted March 23, 2020 Mad Pascal and 7800 Basic are cross-tools, whereas FB can be used as a cross-tool but also run natively. So extending FB to incorporate its own assembler would not seem an appropriate thing to do but @dmsc can elaborate. Quote Link to comment Share on other sites More sharing options...
dmsc Posted March 23, 2020 Author Share Posted March 23, 2020 Hi! 16 hours ago, RALPH! said: Are there ways to include assembler code that compile with the program? That way there not be need use separate assembler and make binary for make into data statements. Mad Pascal and 7800 Basic have ASM statement. As @Wrathchild explained, FastBasic is designed as a native tool (meaning it runs in the Atari itself), and my goal is to always have the integer IDE using less than 8KB of memory. So, adding an integrated assembler is beyond the scope of the standard language. But in the cross-compiler you already can include external assembly files, there is an example at https://github.com/dmsc/fastbasic/blob/master/compiler/USAGE.md#linking-other-assembly-files Basically, you write your assembly function in a separate (.asm) file and then compile both files together in the fb command line. Have Fun! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.