Jump to content
IGNORED

Camel99 Forth Information goes here


TheBF

Recommended Posts

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 ;

 

  • Like 2
Link to comment
Share on other sites

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

 

 

 

 

 

 

hello_world 2023-09-15 11_37_44 PM.png

  • Like 1
Link to comment
Share on other sites

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 )  

 

  • Like 2
Link to comment
Share on other sites

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. :) 

  • Like 2
Link to comment
Share on other sites

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:

  1. One to disable interrupts 
  2. 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 

 

  • Like 2
Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...