+TheBF Posted September 15 Author Share Posted September 15 After a break and some food I have to recant my evil ways. DO LOOP makes the whole thing so simple. HEX 8800 CONSTANT VDPRD \ vdp ram read data 8802 CONSTANT VDPSTS \ vdp status 8C00 CONSTANT VDPWD \ vdp ram write data 8C02 CONSTANT VDPWA \ vdp ram read/write address \ VDP set-address sub-routines CODE 0LIMI 0 LIMI, NEXT, ENDCODE : RMODE ( vdpaddr -- ) DUP 0LIMI VDPWA C! >< VDPWA C! ; : WMODE ( vdpaddr -- ) 4000 OR RMODE ; : VC@+ ( Vdpaddr -- c) VDPRD C@ ; \ read & inc. address : VC!+ ( c --) VDPWD C! ; \ write & inc. address : VC@ ( VDP-adr -- char ) RMODE VDPRD C@ ; : VC! ( c vaddr --) WMODE VC!+ ; \ set address and write \ VDP integer fetch & store : V@ ( VDPadr -- n) VC@ VC@+ FUSE ; : V! ( n vaddr --) >R SPLIT R> VC! VC!+ ; : VWRITE ( addr Vaddr cnt -- ) SWAP WMODE 0 DO COUNT VC!+ LOOP DROP ; : VFILL ( Vaddr cnt char --) ROT WMODE SWAP 0 DO DUP VC!+ LOOP DROP ; : VREAD ( Vaddr Ram cnt --) ROT RMODE BOUNDS DO VC@+ I C! LOOP DROP ; 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted September 16 Author Share Posted September 16 Well as these things seem to go, I had trouble getting my DO LOOP code to work after it "recompiled" as in... it didn't work. That didn't seem obvious to fix so I moved ahead with getting text on the screen. This brought me back to the VDP and video screen control. I decided to simplify and roll the two together. I changed the name VC!+ to EMIT+ because that's what it does. and I remembered a magic word called DUP>R This improves the speed of loops with a counter on the return stack. Here is the STD-OUT.FTH file. It is pretty reasonable in terms of size for what it does. TYPE is adequately fast but VFILL would be a good candidate to recode in Forth Assembler of course. Spoiler \ Standard Forth output words COMPILER HEX TARGET 8800 CONSTANT VDPRD \ vdp ram read data \ 8802 CONSTANT VDPSTS \ vdp status 8C00 CONSTANT VDPWD \ vdp ram write data 8C02 CONSTANT VDPWA \ vdp ram read/write address \ VDP set-address sub-routines CODE 0LIMI 0 LIMI, NEXT, ENDCODE : RMODE ( vdpaddr -- ) DUP 0LIMI VDPWA C! >< VDPWA C! ; : WMODE ( vdpaddr -- ) 4000 OR RMODE ; : EMIT+ ( c --) VDPWD C! ; \ write & inc. address : VFILL ( vaddr len c -- ) ROT WMODE SWAP >R BEGIN DUP EMIT+ R> 1- DUP>R -UNTIL R> 2DROP ; VARIABLE C/L COMPILER 20 C/L T! TARGET VARIABLE COL VARIABLE ROW VARIABLE CURSOR VARIABLE C/SCR COMPILER 3C0 C/SCR T! TARGET 20 CONSTANT BL : CLIP ( n lo hi -- n) ROT MIN MAX ; : >VPOS ( col row -- vaddr) C/L @ * + ; : CURSOR ( -- Vaddr) COL @ ROW @ >VPOS 0 C/SCR @ CLIP ; : COL+! ( n -- ) COL @ + DUP C/SCR @ > IF DROP COL OFF EXIT THEN COL ! ; : ROW+! ( n -- ) ROW @ + 0 23 CLIP ROW ! ; : EMIT ( c --) CURSOR WMODE EMIT+ 1 COL+! ; : CR ( -- ) 1 ROW+! COL OFF ; : SPACE ( -- ) BL EMIT ; : TYPE ( addr len -- ) CURSOR WMODE >R BEGIN COUNT EMIT+ 1 COL+! R> 1- DUP>R -UNTIL R> 2DROP ; : AT-XY ( col row -- ) ROW ! COL ! CURSOR WMODE ; : PAGE 0 C/SCR @ 20 VFILL 0 0 AT-XY ; So with that working I figured out how to make S" work in a cross-compiled definition. That required more spells and elixirs than I bargained for but now I know how to do it. I tested it in this "hello world" program which is back to looking like alphabet soup. I will migrate S" et al back into the compiler I think, but I will need a DEFER word to handle (S") The program compiles to 770 bytes because of the extra features in the STD-OUT file which also required a lot of primitives to be "imported" But on the plus side the actual MAIN program is normal Forth code It looks like I need to make IMPORT: smarter so it only loads primitives that are not already loaded. Then I can put import statements in the library files and forget about them. Spoiler \ TESTPROG2.FTH Demo IMPORT: CODE loops and AUTOSTART Sep 2023 Fox HEX 2000 ORG \ this must be set before compiling any code INCLUDE DSK7.ITC-FORTH \ preamble for indirect threaded Forth \ extend the cross-compiler 1st COMPILER ALSO META DEFINITIONS ( this holds the "immediate" words and support ) HOST: TALIGN ( -- ) THERE ALIGNED H ! ;HOST HOST: S, ( c-addr u -- ) THERE OVER 1+ TALLOT PLACE TALIGN ;HOST \ steal needed kernel primitives COMPILER WARNINGS OFF IMPORT: DUP 2DUP SWAP DROP 2DROP OVER >< ROT IMPORT: >R R> DUP>R IMPORT: 1- 1+ 0= * + > IMPORT: C@ C! COUNT @ ! IMPORT: OR FUSE SPLIT IMPORT: ON OFF MIN MAX ALIGNED COMPILER WARNINGS ON HEX INCLUDE DSK7.STD-OUT COMPILER DECIMAL TARGET : (S") ( -- c-addr u) R> COUNT 2DUP + ALIGNED >R ; \ run-time for S" COMPILER ALSO META DEFINITIONS HOST: S" [CHAR] " PARSE TCOMPILE (S") S, ;HOST IMMEDIATE TARGET : MAIN ( -- ) 768 C/SCR ! PAGE S" HELLO WORLD" TYPE BEGIN AGAIN ; COMPILER AUTOSTART MAIN SAVE DSK7.HELLOWORLD 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted September 16 Share Posted September 16 1 hour ago, TheBF said: TYPE is adequately fast but VFILL would be a good candidate to recode in Forth Assembler of course. You, of course, are welcome to whatever you can use from the VFILL ASL in fbForth101_LowLevelSupport.a99. ...lee 2 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted September 17 Author Share Posted September 17 I have finally decided that the simplest way to run this "meta" compiler as they are called in Forth circles, is to IMPORT all the common Forth code primitives. That way you don't have to do it manually and I don't have to make a program scanner that searches for them in a first pass. The thing is that without the dictionary headers all these words take about 1K bytes. That's a pretty small run time block. The current screen output file uses another ?400 bytes. IMPORT: DUP DROP SWAP OVER ROT -ROT NIP IMPORT: C! C@ COUNT @ ! +! C+! 2! 2@ IMPORT: SP@ SP! RP@ RP! IMPORT: DUP>R >R R> R@ 2>R 2R> IMPORT: ?DUP >< 2DROP 2DUP 2SWAP PICK IMPORT: AND OR XOR IMPORT: 1+ 1- 2+ 2- 2* 4* 8* 2/ IMPORT: 1+! 1-! IMPORT: + - D+ IMPORT: RSHIFT LSHIFT INVERT ABS NEGATE ALIGNED IMPORT: UM* * UM/MOD M/MOD IMPORT: = OVER= 0< U< > < IMPORT: MIN MAX SPLIT FUSE IMPORT: MOVE FILL SKIP SCAN IMPORT: ON OFF IMPORT: BOUNDS /STRING So I rolled everything up into the ITC-FORTH preamble file. Then you just need to include your I/O file. At the moment there is just STD-OUT. And optionally you can import some of the other primitives in the system. I used ?TERMINAL in the demo below. With a different file to define the dictionary headers, I should be able to rebuild the Camel99 kernel on a TI-99! That might be a first? A language rebuilding itself on the 99. hello world looks like this. I think it's turning into a useable thing. \ HELLO.FTH for the recompiler. Demo Sep 16 2023 Fox HEX 2000 ORG \ this must be set before compiling any code INCLUDE DSK7.ITC-FORTH \ preamble for indirect threaded Forth INCLUDE DSK7.STD-OUT IMPORT: ?TERMINAL COMPILER DECIMAL TARGET : MAIN ( -- ) 768 C/SCR ! \ init this variable PAGE S" HELLO WORLD" TYPE BEGIN ?TERMINAL UNTIL BYE ; \ tell the compiler what to do with this COMPILER AUTOSTART MAIN SAVE DSK7.HELLOWORLD HOST \ return to HOST Forth \ you could automatically exit to TI-99 Main page ( BYE ) 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted September 17 Author Share Posted September 17 After I failed to get DO/LOOP working I thought I would take a run a something simpler. Chuck's FOR NEXT loop running on the return stack. That helped me uncovered what was wrong. (the order that you load things is critical when you have words with the same name in these @$@!# cross-compilers) Anyway.. Here is the total code to make a FOR/NEXT loop in ANS Forth. HEX CODE (NEXT) *RP DEC, \ decrement loop ON RSTACK or R15 OC IF, \ test carry flag *IP IP ADD, \ jump back: add offset value to interpreter pointer NEXT, ENDIF, RP INCT, \ remove counter from Rstack IP INCT, \ move past (LOOP)'s in-line parameter NEXT, ENDCODE : FOR ( n -- ) POSTPONE >R HERE ; IMMEDIATE : NEXT ( -- ) POSTPONE (NEXT) HERE - , ; IMMEDIATE And here is what it takes to implement the ANS compliant DO/LOOP I am now understanding why Chuck was not a big fan and why he abandoned DO/LOOP in his later years. TARGET CODE <?DO> ( limit ndx -- ) *SP TOS CMP, 1 $ JNE, TOS POP, TOS POP, IP RPOP, NEXT, +CODE <DO> ( limit indx -- ) 1 $: R0 8000 LI, *SP+ R0 SUB, R0 TOS ADD, R0 RPUSH, TOS RPUSH, TOS POP, NEXT, ENDCODE CODE <+LOOP> TOS *RP ADD, TOS POP, 2 $ JMP, +CODE <LOOP> *RP INC, 2 $: 1 $ JNO, IP INCT, 3 $ JMP, 1 $: *IP IP ADD, NEXT, +CODE UNLOOP 3 $: RP 4 AI, NEXT, ENDCODE CODE I ( -- n) TOS PUSH, *RP TOS MOV, 2 (RP) TOS SUB, NEXT, ENDCODE CODE J ( -- n) TOS PUSH, 4 (RP) TOS MOV, \ outer loop index is on the rstack 6 (RP) TOS SUB, \ index = loopindex - fudge NEXT, ENDCODE VARIABLE LP VARIABLE L0 COMPILER 4 CELLS TALLOT TARGET : >L ( x -- ) ( L: -- x ) 2 LP +! LP @ ! ; \ LP stack grows up : L> ( -- x ) ( L: x -- ) LP @ @ -2 LP +! ; : RAKE ( -- ) ( L: 0 a1 a2 .. aN -- ) BEGIN L> ?DUP WHILE POSTPONE THEN REPEAT ; COMPILER ALSO META DEFINITIONS : DO ( n n -- adr) TCOMPILE <DO> 0 >L POSTPONE BEGIN ; IMMEDIATE : ?DO ( n n -- adr) TCOMPILE <?DO> 0 >L POSTPONE BEGIN ; IMMEDIATE : LEAVE ( -- ) TCOMPILE UNLOOP TCOMPILE BRANCH AHEAD >L ; IMMEDIATE \ complete a DO loop : LOOP ( -- ) TCOMPILE <LOOP> <BACK RAKE ; IMMEDIATE : +LOOP ( -- ) TCOMPILE <+LOOP> <BACK RAKE ; IMMEDIATE PREVIOUS DEFINITIONS Edit: copy paste error. I had the hi-level words twice in the file. 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted September 20 Author Share Posted September 20 I went down the rabbit hole of trying to refine that crude VDP screen driver for the metacompiler. You can ( and I did) waste a lot of time playing with variations on this thing and the conclusion... wait for it... is that CODE is much faster that Forth. But is was fun to see how far could push Forth. The spoiler has the "mostly Forth version. There are only two code words: One to disable interrupts VC!+ which made a big difference. It writes a byte to last VDP address set & auto increments the VDP address You can see that VFILL speed is way slower than ALC but it looks similar to GPL speed. VFILL is only 16 bytes in Forth versus 28 bytes in ALC. The extra code is in RMODE and WMODE and they are re-useable in other words. TYPE is acceptable speed when implemented this way. If I put the FOR NEXT loop counter in a register it would speed up about 10..12% from my testing of FOR NEXT. So there it is. Stuff you already knew but now you have a video for it. Spoiler \ STD-OUT1A.FTH output words in Forth + minimal code Sept 17 2023 COMPILER HEX TARGET 8800 CONSTANT VDPRD \ vdp ram read data \ 8802 CONSTANT VDPSTS \ vdp status 8C00 CONSTANT VDPWD \ vdp ram write data 8C02 CONSTANT VDPWA \ vdp ram read/write address \ VDP set-address sub-routines CODE 0LIMI 0 LIMI, NEXT, ENDCODE : RMODE ( vdpaddr -- ) DUP 0LIMI VDPWA C! >< VDPWA C! ; : WMODE ( vdpaddr -- ) 4000 OR RMODE ; VARIABLE C/L COMPILER 20 C/L T! TARGET VARIABLE COL VARIABLE ROW VARIABLE CURSOR VARIABLE C/SCR COMPILER 3C0 C/SCR T! TARGET 20 CONSTANT BL \ : EMIT+ ( c --) VDPWD C! ; \ write & inc. address CODE VC!+ ( c --) TOS SWPB, TOS VDPWD @@ MOVB, TOS POP, NEXT, ENDCODE : VFILL ( vaddr len c -- ) ROT WMODE SWAP FOR DUP VC!+ NEXT DROP ; : >VPOS ( col row -- vaddr) C/L @ * + ; : CURSOR ( -- Vaddr) COL @ ROW @ >VPOS ; : AT-XY ( col row -- ) ROW ! COL ! CURSOR WMODE ; : PAGE ( -- ) 0 C/SCR @ BL VFILL 0 0 AT-XY ; : ?WRAP ( -- ) COL @ C/SCR @ 1- > IF 0 0 AT-XY THEN ; : ROW+! ( n -- ) ROW @ + 23 > IF ROW OFF EXIT THEN ROW ! ; : EMIT+ ( c -- ) VC!+ COL 1+! ?WRAP ; : EMIT ( c --) CURSOR WMODE EMIT+ ; : CR ( -- ) 1 ROW+! COL OFF ; : SPACE ( -- ) BL EMIT ; : TYPE ( addr len -- ) 1- CURSOR WMODE FOR COUNT EMIT+ NEXT DROP ; COMPILER ALSO META DEFINITIONS HOST: ." [CHAR] " PARSE TCOMPILE (S") TS, TCOMPILE TYPE ;HOST IMMEDIATE I am beginning to get this meta compiler organized and it is better than my DOS one. I have actually learned something after all these years fighting with this stuff. Here is the test program that runs is the video. It's "normal" Forth with a few magic words to keep the compiler happy. With the CORE Forth primitives imported from Camel99 Forth, and the output library above it compiles to 1596 bytes. It could be smaller if I wanted to remove all the primitives that are not used. \ VFILLTEST.FTH using FOR NEXT loop FOR VFILL and TYPE HEX 2000 ORG \ this must be set before compiling any code INCLUDE DSK7.ITC-FORTH \ preamble for indirect threaded Forth INCLUDE DSK7.STD-OUT1A1 \ uses for/next VFILL IMPORT: ?TERMINAL COMPILER DECIMAL TARGET : VFILLTEST 95 FOR 0 C/SCR @ R@ 33 + VFILL NEXT ; : DELAY ( n -- ) FOR R@ DROP NEXT ; TARGET : MAIN ( -- ) 768 C/SCR ! VFILLTEST 5 12 AT-XY ." VFILL in Forth " 5 13 AT-XY ." FOR DUP VC!+ NEXT " 5000 DELAY 0 0 AT-XY BEGIN ." Hello metacompiling world! " ?TERMINAL UNTIL BYE ; COMPILER AUTOSTART MAIN SAVE DSK7.VFILLTEST3 HOST VFILL test mostly Forth.mp4 2 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.