Jump to content

TheBF

+AtariAge Subscriber
  • Posts

    4,059
  • Joined

  • Last visited

Posts posted by TheBF

  1. 3 hours ago, Switch1995 said:

    It has been over 6 years since you asked....and my TI LOGO II code has finally finished running:

     

    //DEFINE PROCEDURES FOR EACH SIDE OF SCREEN

    TO TOP :NUM
    IF :NUM = 116 THEN STOP
    SX :NUM
    TOP :NUM + 1
    END

     

    TO RGT :NUM
    IF :NUM = -81 THEN STOP
    SY :NUM
    RGT :NUM - 1
    END

     

    TO BTM :NUM
    IF :NUM = -128 THEN STOP
    SX :NUM
    BTM :NUM - 1
    END

     

    TO LFT :NUM
    IF :NUM = 96 THEN STOP
    SY :NUM
    LFT :NUM + 1
    END

     

    //USE GRAPHICS EDITOR TO CREATE 16X16 ASTERISK

    MAKESHAPE 6

     

    //INITIALIZE SPRITE IN UPPER LEFT CORNER OF SCREEN

    TELL SPRITE
    CARRY 6
    SC :BLACK
    SXY -128 96

     

    //CLEAR SCREEN AND LOOP 100 TIMES

    CS
    REPEAT 100 [TOP -128 RGT 96 BTM 116 LFT -81]

     

     

     

    Updated standings:

     

    Logo II.jpeg

    Hey thanks for this. Great to see a result. 

     

     

    • Like 2
  2. I think I have it.

    I love these stack string pairs in combination the multiple condition loops for strings. 

    The secret is to make all the string functions return a new stack string so you can loop the functions until the string is empty. 

     

    I made a version of SCAN but  for VDP memory. Since /STRING is a code word the loop is pretty quick. 

     

    There is a test harness here that puts stuff in VDP RAM. The video shows the speed to do the 342 bytes of test data. 

     

    It takes 3.5 seconds to process 2K of font memory on my machine from >800. 

    Not blazing but not terrible. It compiles in 256 bytes. 

     

    Edit: Fixed error that did not limit chunks to 127 bytes. 

    Spoiler
    \ vdpencoder.fth  compress VDP as run length encoded data in RAM
    
    NEEDS DUMP FROM DSK1.TOOLS 
    NEEDS VCOUNT FROM DSK1.VDPMEM  \ vdp memory comma allot etc. 
    
    \ =================[ test setup ]====================
    \  compile data into VDP RAM 
    HEX 1000 VP !         \ start of VDP data 
    VHERE 1000 FF VFILL   \ init VDP ram so we can see what changed 
    
    : RPT$,    ( char len -- ) 0 DO DUP VC, LOOP DROP ; 
    : V$,   ( addr len ) TUCK VHERE VPLACE  1+ VALLOT ;
    
    DECIMAL 
    VHERE 
    CHAR A 100 RPT$,
    CHAR B 125 RPT$,
    CHAR C 137 RPT$,
    CHAR D 22 RPT$,
    CHAR E 10 RPT$,
    CHAR F 11 RPT$,
    CHAR G 12 RPT$,
    CHAR H 13 RPT$,
    S" This is a string in the middle" V$, 
    CHAR I 19 RPT$,
    CHAR J 18 RPT$,
    CHAR K 17 RPT$,
    CHAR L 16 RPT$,
    S" This is the second string" V$, 
    CHAR M 15 RPT$,
    CHAR N 14 RPT$,
    CHAR O 88 RPT$,
    
    VHERE SWAP - CONSTANT VDATA-SIZE 
    
    .S 
    \ =====================================================
    
    \ ========[ RUN LENGTH ENCODE VDP RAM to LOW RAM ]==================
    \ low ram memory managers: c, allot etc.
    HERE 
    : HEAP    H @ ;
    : HALLOT  H +! ;
    : HC,     HEAP C! 1 HALLOT ;
    
    VARIABLE BYTES   \ used to limit the chunks to max 127 bytes 
    
    \ scan for c until there are no more. Return new stack string
    : VDPSKIP ( Vaddr len c -- Vaddr' n')
        >R 
        BYTES OFF 
        BEGIN    DUP      WHILE \ len<>0 
           BYTES @ 127 <  WHILE  
           OVER VC@ R@ =  WHILE 
           1 /STRING 
           BYTES 1+! 
        REPEAT 
        THEN THEN 
        R> DROP  ;
    
    : FINDDUPS ( Vaddr len --  NextAddr len  Vaddr len)
        2DUP OVER VC@   ( -- Vaddr len Vaddr len char )
        VDPSKIP         ( -- Vaddr len Vddr' len' )
        2SWAP  2 PICK - ; 
    
    HEX 
    : HRLE,    ( Vaddr len -- ) 80 OR HC, VC@ HC, ;
    : HRLE-$,  ( Vaddr len ) TUCK HEAP SWAP VREAD  HALLOT ; 
    
    \ result is a stack string in low RAM. Can save as a program image.
    : VDP-COMPRESS ( Vaddr len -- Heap len) 
         HEAP >R 
         BEGIN 
            DUP 
        WHILE 
            FINDDUPS ( -- NextAddr len  Vaddr len)
            DUP 2 >
            IF    HRLE,  
            ELSE  HRLE-$,
            THEN
        REPEAT 
        2DROP 
        R>  H @ OVER - ;
    
    HERE SWAP - DECIMAL . .( bytes)
    \ ==================================================
    \ reset and erase the heap 
    HEX 2000 H !
    HEAP 1000 FF FILL
    
    \ HEX  1000 VDATA-SIZE VDP-COMPRESS 

     

     

     

     

     

    • Like 2
  3. 36 minutes ago, Lee Stewart said:

     

    Very nice! I think I can make only one improvement:

     

    : RLE? ( byte -- byte len) DUP 80 AND ;

     

    Because you are not using its value, you don’t need the 0> because any nonzero value tests true.

     

    I am, however, confused about why you used ALIGNED .  We’re parsing bytes here, so even addresses should not be a concern, n’est-ce pas?

     

    ...lee

    You are quite right on both counts.

    1. I was being "formal" with 0>.  :)  (well formed expression and all that) 

    2. The ALIGNED was because the string compiled could be an odd length and my word S,  always aligns the address for Forth.

       It is not needed if you don't compile VDP data with aligning.

     

    I am noodling on how to make the encoder side in Forth. That's seems to be a bit trickier but I think I can get something going. 

     

     

    • Like 3
  4. Over 2 cups of coffee I got this to work.

    I don't think I understand all the details of the encoding method because I didn't study it, but I took a stab at making some test data. 

     

    In this code the RLETYPE and TYPE could be replaced VMBW  and VFILL and it would be pretty fast. 

     

    \ run length decoder
    
    \ Run Length Encoded Write...
    \   ++Each run of 3..127 repeated bytes has a length byte with MSb=1
    \     followed by the repeated byte.
    
    \   ++Each stretch of 1..127 differing (<3 repeats) bytes has a length
    \     byte followed by that length of bytes.
    \
    
    NEEDS DUMP FROM DSK1.TOOLS 
    
    \ test data
    HEX
    : RLE,    ( char len -- )  80 OR C, C, ;
    
    DECIMAL
    CREATE RLEDATA ( -- addr)
    CHAR A 10 RLE,
    CHAR B 16 RLE,
    CHAR C 4 RLE,
    CHAR D 22 RLE,
    CHAR E 10 RLE,
    CHAR F 11 RLE,
    CHAR G 12 RLE,
    CHAR H 13 RLE,
    S" This is a string in the middle" S,
    CHAR I 19 RLE,
    CHAR J 18 RLE,
    CHAR K 17 RLE,
    CHAR L 16 RLE,
    CHAR M 15 RLE,
    CHAR N 14 RLE,
    CHAR O 88 RLE,
    0 ,            \ delimit end of data
    
    \ ---------------------
    HEX
    : RLE? ( byte -- byte len) DUP 80 AND 0> ;
    
    : RLETYPE ( addr len -- addr')
      7F AND       \ mask the RLE bit gives the repeat#
      OVER C@ SWAP ( -- addr char len )
      0 DO
         DUP EMIT
      LOOP
      DROP 
      1+ ;       \ bump address ahead
    
    : DECODE ( addr -- )  \ type to screen for testing
        BEGIN
           COUNT    ( addr len )
        DUP WHILE
            CR    
            RLE?    ( addr len ?)
            IF   RLETYPE
            ELSE 2DUP TYPE + ALIGNED  \ type and advance address
            THEN
        REPEAT
        2DROP 
    ;
    

     

     

     

    • Like 3
  5. Somebody is back at work. :)

    Very neat.

     

    Here is a thought. One of the justifications for having an interpreter is to save space by using higher level "instructions".

    As you have heard me say often the darn 9900 is already at the same level as Forth primitives.

     

    Makes me wonder if a Forth version would be smaller albeit slower, but still have an acceptable runtime delay at startup. 

     

    : RLD ( src dst len -- )  ...   ;

  6. 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
  7. 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
  8. 19 hours ago, GDMike said:

    And can be used multiple times in tipi as a separator.

    In my old program I would start at pos 40 and count "." All the way left to see how many there were.

    Here is the one I am using with lot of comments as I tried to grok it.

     

    I hesitate to offer it since it might add to confusion.

    It's written in my cross-compiler Forth it has some odd additions.

    I also changed how I manage the workspace and the little name buffer.

     

    If nothing else it might help understand some sections as I changed some of the jumps to structured loops.

    And I even found one instruction inside a loop that could be removed to the top of the loop. :)

     

    CAMEL99-ITC/Compiler/cc9900/SRC.ITC/DSRLINKA.HSF at master · bfox9900/CAMEL99-ITC · GitHub

     

     

    • Like 3
  9. 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
  10. 3 hours ago, Lee Stewart said:

    One of the changes I am seriously considering for fbForth 3.0 is revamping the file management system—especially, how DSRLNK works. I am looking  into an implementation of Paolo Bagnaresi’s version as modified by Bill R. Sullivan. I am not sure I want to include Bill’s optional use of CPU RAM PABs and buffers, but I definitely want to use the 5 saved parameters after the first call to DSRLNK so CRU and device/program searches can be minimized with subsequent DSRLNK calls. Whereas Bill suggests a stack for multiple open files, I am thinking of adding those parameters to the head of each PAB.

     

    I am also considering using a linked list of PABs, much as TI Basic does—we’ll see.

     

    ...lee

    I like where you are going.

     

    I have considered adding fields to the PAB as well.  I also feel like there are things that could better use Forth. 

    If you pull code that finds the DOT in the device string and replace it with SCAN in ALC you get a reusable word and no speed reduction. 

     

    I am not sure which 5 parameters you are referring to but the one that seems to be missing in a standard PAB is the address of the "." character.

    I call that the "real pab address" since it is used to reference the pab if I recall correctly. 

     

    • Like 3
  11. 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
  12. 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
  13. Once you have the VDP driver you can make some standard output words. 

    I am testing all this on Camel Forth before making it a library for the recompiler project. 

    I will probably have to relent and use Assembler for the VDP library to make work better but this was a fun exercise. 

     

    Spoiler
    \ Standard Forth output words
    
    VARIABLE C/L       C/L@  C/L !
    VARIABLE COL
    VARIABLE ROW
    VARIABLE CURSOR
    VARIABLE C/SCR     3C0 C/SCR !
    
    20 CONSTANT BL
    
    : >VPOS  ( col row -- vaddr) C/L @ * + ;
    
    : CLIP   ( n lo hi -- n) ROT MIN MAX ;
    
    : 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 VC! 1 COL+! ; 
    : CR     ( -- ) 1 ROW+!  COL OFF ;
    
    : SPACE   BL EMIT ;
    
    : TYPE   ( addr len -- )
            >R
            BEGIN
               COUNT EMIT
               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 ;
    
    : VDPTYPE  ( addr len --) TUCK CURSOR SWAP VWRITE  COL+! ;
    

     

     

    It not FAST doing it this way, putting a slooooow  EMIT in a loop, but it works. 

    (PAGE is painful to watch) :) 

     

    But the text writing speed is not bad if you use the block write word VDPTYPE as seen in the 2nd video 

     

    Test programs

    : TEST
        PAGE
        100
        BEGIN
           S" HELLO WORLD!  " TYPE
           1- DUP
        -UNTIL
        DROP ;
    
    : TEST2
        PAGE
        40
        BEGIN
           S" Hello World!  " VDPTYPE
           1- DUP
        -UNTIL
        DROP
    ;
    

     

    • Like 2
  14. So I wondered what would happen if I wrote an entire VDP driver in Forth? 🙂

    It's not fast. That for certain.

    But to satisfy everyone's curiosity here is a set of VDP words written entirely in Forth except of a code word to turn off the interrupts.

    I added -UNTIL (not until) which is something Chuck invented for Machine Forth because it is faster than 0= UNTIL. 

    I chose to try it without DO/LOOP first because DO LOOP adds some runtime code to the program.

     

    It's pretty tidy, but it would benefit greatly from a code word like  R+!  or R1-! to manage the counter on the return stack 

    As I think about it adding a machine Forth style FOR/NEXT loop would be trivial with R1-! in the system. 

     

    : -UNTIL  POSTPONE WHILE POSTPONE REPEAT ; IMMEDIATE
    
    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 ( RAM-addr VDP-addr cnt -- )
            SWAP WMODE
            >R
            BEGIN
              COUNT VC!+
              R> 1- DUP >R
            -UNTIL
            R> 2DROP ;
    
    
    : VREAD  ( Vaddr Ram cnt --)
             >R
             SWAP RMODE
             BEGIN
               VC@+ OVER C!
               R> 1- DUP >R
             -UNTIL
             R> 2DROP ;
    
    : VFILL  ( vaddr cnt char --)
             SWAP >R
             SWAP WMODE
             BEGIN
               DUP VC!+
               R> 1- DUP >R
             -UNTIL
             R> 2DROP ;
    

     

    • Like 1
    • Thanks 1
  15. So now that the compiler works correctly I took some time to improve the user experience.

    1. As mentioned I consolidated all the preamble into a file called ITC-FORTH.   So you just include that file before any other code is compiled.
    2. I changed IMPORT:   so it knows to search in the CAMEL99 Forth dictionary for primitive words. Before you had to manually add Forth to the search order.
    3. I add the command AUTOSTART so it is simple to specify the routine that will run after COLD builds the Forth stacks and sets the workspace. 

    And with that here is modestly complicated "recompiled" Forth program that compiles and runs.

    \ 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
    
    IMPORT: DUP DROP  1- 0=
    
    COMPILER HEX 
    
    TARGET
    CODE BYE ( --) 0 LIMI,   0 @@ BLWP,    NEXT,  ENDCODE 
    
    : LOOP2 ( -- ) 5000 BEGIN  1- DUP   0= UNTIL   DROP ;
    : LOOP1 ( -- ) 5000 BEGIN  1-  DUP WHILE REPEAT DROP ;
    
    : MAIN  ( -- ) LOOP1 LOOP2 BYE ;
    
    COMPILER 
    AUTOSTART MAIN 
    SAVE DSK7.TEST2
    

     

    That's enough for one day. I am behind on my Viola practicing. :) 

     

    • Like 2
  16. No matter how many times I do these Forth compiler things I always get something messed up initially.

    I guess if it was easy everybody would be doing it. :) 

     

    I had an error in what I compiled into the program when ';' is encountered but thanks to the Classi99 debug window it was easy to see. 

    I had forgotten that EXIT must be a proper CODE word so the Forth compiler can"compile" it correctly.

    I initially gave it a label like DOVAR DOCON etc. 

    The new file is here: RECOMPILER/src/EXECUTORS.FTH at main · bfox9900/RECOMPILER · GitHub

     

    Here is the little program I used to test "nested" colon definitions and it works as advertised now. It compiles to 176 bytes. 

    Something to be aware of is that these programs have no code in scratchpad RAM currently.

    So they will run ~20% slower than Camel99 Forth.  But that can be fixed later. 

     

    I am slowly learning how to simplify the "noise" in the code.   

    I realized that COLD just needs to be a label since I branch directly into it at startup. 

    I think I will wrap up the first 26 lines into a "PREAMBLE" file that sets up the program. 

     

    Things I think I like:

    1. The Virtual machine is a separate file that you load first
    2. The branch and loop compilers are a separate file
    3. The data description words are a separate file 

    In "theory" this means we could change the threading type to direct-threaded with very little trouble.  😇

     

    So with this level of compiler working I should be able to make a little VDP I/O library and write HELLO world next.

    I played too many games with Camel99 VDP I/O to allow it to imported as is.

    (I used a BL sub-routine to set VDP addresses which is not relocatable)

    No worries. I can make something smaller if I don't worry about speed. 

     

    \ NESTTEST.FTH  test nested calls 
    
    COMPILER 
    NEW
    HEX 2000 ORG 
    
    WARNINGS OFF 
    INCLUDE DSK7.EXECUTORS  \ load EXIT DOCOL DOVAR ETC.
    INCLUDE DSK7.BRANCHING  \ compilers: IF THEN BEGIN AGAIN...
    INCLUDE DSK7.ITCTYPES   \ CONSTANT VARIABLE : ;  etc.
    WARNINGS ON 
    
    COMPILER HEX
    
    TARGET 
    VARIABLE BOOT  \ hold the cfa of the word that boots from COLD 
    
    L: COLD               \ COLD runs at boot time to build the Forth VM 
          8300 LWPI,      \ set 9900 workspace
          SP 83FE LI,     \ data stack in scratchpad
          RP 83D0 LI,     \ return stack in scratchpad 
          IP BOOT LI,     \ load interpreter pointer with boot word
    
          R10 _NEXT LI,   \ inner interpreter stays in R10 
          *R10 B,         \ jump into the interpreter 
    
    TARGET ALSO FORTH 
    IMPORT: DUP DROP 1- 
    
    TARGET
    CODE BYE    0 LIMI, 0 @@ BLWP,  ENDCODE 
    
     : SUB3  BEEF ;
     : SUB2  SUB3 ;
     : SUB1  SUB2 ;
    
    : MAIN  
        4000 
        BEGIN  
          1- DUP 
        WHILE    
          SUB1  DROP 
        REPEAT
        DROP 
        BYE 
    ;
    
    COMPILER HEX  
       COLD  2002 T!    \ jumps into cold on startup
    T' MAIN  BOOT T!    \ set the boot variable 
    
    
    SAVE DSK7.NESTTEST
    

     

    • Like 1
  17. Now I remember why I never tried to completely re-write my cross-compiler.  My head is spinning. 

     

    Last Wednesday I got the import utility working. It as taken me until now, working on and off to get this re-compiler to work.

    I just had to get it to build a program and see it run.  :) 

     

    Here is the first recompiled program with all the magic incantations it currently takes to make it go. 

    I will work on improving the noise but it's good in the beginning to understand what is happening. 

    I also have see if my relocation method in Machine Forth can be squeezed into service here.

    Right now the programs are AORG >2000  only. 

     

    The demo starts and runs BYE. :) 

     

    It is real indirect threaded code but there is no dictionary or interpreter or compiler included in the program. 

    That is all provided by the "HOST" Forth.    It compiles to 122 bytes. 

    \ TEST PROGRAM 1
    
    COMPILER 
    NEW
    HEX 2000 ORG 
    
    WARNINGS OFF 
    INCLUDE DSK7.EXECUTORS  \ load EXIT DOCOL DOVAR ETC.
    INCLUDE DSK7.BRANCHING  \ compilers: IF THEN BEGIN AGAIN...
    INCLUDE DSK7.ITCTYPES   \ CONSTANT VARIABLE : ; ETC.
    WARNINGS ON 
    
    TARGET 
    ALSO FORTH  \ import needs to see Forth to find kernel primitives 
    IMPORT: + 
    
    COMPILER HEX
    
    TARGET 
    VARIABLE BOOT  \ hold the cfa of the word that boots from COLD 
    
    \ bye does not end with NEXT so we can' import it 
    CODE BYE   0 LIMI,  0 @@ BLWP,  NEXT,  ENDCODE 
    
    CODE COLD   \ COLD is a key primitive that builds the Forth VM 
          8300 LWPI,               \ set 9900 workspace
          SP 3FFE LI,              \ data stack
          RP 3FE0 LI,              \ return stack
          IP  BOOT  LI,            \ load interpreter pointer with boot word
    
          R10 EXIT CELL+ LI,       \ EXIT + 2 = NEXT -> R10
          *R10 B,                  \ jump to NEXT (inner interpreter)
    ENDCODE
    
    TARGET
    : MAIN  
        BYE  
    ;
    
    COMPILER HEX  T' MAIN  BOOT HOST !       \ set the boot variable 
    COMPILER      T' COLD >BODY 2002 HOST !  
    
    SAVE DSK7.TESTPROG1

     

    For the masochists in the group you can see the source code here and the bin folder has everything in TI-99 format.

     bfox9900/RECOMPILER: Re-compiles Forth code as binary program without interpreter or dictionary (github.com)

     

    Here is a little video showing the process. The program is totally "under-whelming" but its a start.  :) 

     

     

     

     

    • Like 3
  18. Over in another thread @InsaneMultitasker posted some code to detect the presence of a SAMS Card.

    I am always curious about comparing Assembly Language to Forth.

     

    In this case, at least for Camel99 Forth, a couple of extra words are needed from the SAMS library to do this in Forth. 

     

    For the Forth curious, here is what I came up with. (un tested at time of posting)

    Edit: Tested and it works. 

           But SAMSINI in the DSK1.SAMS file runs when the file compiles and it burps with an error if SAMS is not present.

     

    Again I see the low level semantic power of 9900 Assembly language is about the same as Forth primitives.

    The gap widens only when we begin making higher level words that we can concatenate to make yet higher level words. 

     

    Original code

    H0101  data >0101
    SAMSDT DATA PLAYWS,$+2
           clr    @0(R13)
           li    r12,>1E00
           sbo 0
           mov    @>4008,R2      ;save whatever was in the mapper for >4000-4fff
           a      @H0101,@>4008  ;change the value    
           c      r2,@>4008      ;is it still the same? 
           jeq    noams          ;yes. can't be SAMS
           seto   @0(R13)        ;no, assume SAMS
           mov    r2,@>4008      ;restore, for futureproofing
    noams  sbz 0 
           rtwp


    A Forth version

    \ For Camel99 Forth 
    
    NEEDS 'R12 FROM DSK1.SAMS
    
    HEX       
    : SAMSDT ( -- ?)       \ returns true FLAG if present      
           1E00 'R12 !     \ set CRU to SAMS card
           0SBO            \ Enable the card
           4008 @ >R       \ save whatever was in the mapper on RETURN stack 
           0101 4008 +!    \ change the value 
           R@ 4008 @ <>    \ is it different? (compared to top Rstack)
           R> 4008 !       \ restore previous value to SAMS card
           0SBZ            \ disable the card
    ;
    

     

    • Like 2
  19. 6 minutes ago, FarmerPotato said:

    For more historical context: check "Image Based Persistence" under Wikipedia Smalltalk article. Citation 37 looks like a whole (online) book. 

     

    Smalltalk images are similar to (restartable) core dumps


    BSAVE 🙂

     

    Yes Forth systems work the same way. I have the function in a word called SAVESYS  ( OR if you run Forth from SUPER CART I call it SUPERSAVE)  :)

    I didn't use the name BSAVE because that has historical usage for saving a Forth system in BLOCKS. 

     

    Here is the code and an example of a minimal save start up word call GO.  WARM inits Forth and ABORT restarts the interpreter

     

    This newest version saves the program and if LOW RAM has been used ( H variable <> 0)  it saves that too as a separate program file

    with the appropriate header for the E/A5 loader to pull it in.

     

    One day I will add saving VDP RAM and SAMS. VDP is simplest because I have a VDP memory management variable in the kernel (VP) so it will work like LOW RAM.

     

    Spoiler
    CR .( SAVESYS.FTH V2 creates EA5 program Dec 2022 B Fox)
    \ creates a binary program E/A 5 format.
    \ Makes as many files as needed to save the system
    \ Jun 2022 version fixed section overlap. Tested with check sum.
    \ Dec 2022 saves the HEAP (Low RAM) as a file if variable H <> 0
    
    \ Usage example:
    \  INCLUDE DSK2.MYPOGRAM   ( load all your code)
    \  : STARTUP     WARM   CR ." Myprogram ready"  ABORT" ;
    \  LOCK   ( this locks the dictionary to the current size )
    \
    \   INCLUDE DSK1.SAVESYS
    \  ' STARTUP SAVESYS DSK3.MYFILENAME
    
    \ NEEDS DUMP      FROM DSK1.TOOLS
    NEEDS LOCK      FROM DSK1.MARKER
    NEEDS LOAD-FILE FROM DSK1.LOADSAVE  \ we use SAVE-FILE from this library
    NEEDS U.R       FROM DSK1.UDOTR
    
    HERE
    HEX
    A000 CONSTANT HIMEM     \ start of Camel99 Forth program in CPU RAM
    1000 CONSTANT VDPBUFF  \ Programs write to file from VDP Ram
    2000 CONSTANT LOWRAM
    2000 CONSTANT 8K
    8K 3 CELLS - CONSTANT IMGSIZE \ makes space for header cells
      13 CONSTANT PROGRAM     \ file mode for Program files
    
    \ define the file header fields. *THESE ARE VDP ADDRESSES*
    VDPBUFF            CONSTANT MULTIFLAG
    VDPBUFF  1 CELLS + CONSTANT PROGSIZE
    VDPBUFF  2 CELLS + CONSTANT LOADADDR
    VDPBUFF  3 CELLS + CONSTANT CODEBUFF  \ COPY 8K program chunks to here
             3 CELLS   CONSTANT HEADLEN
    
    : HEADER  ( Vaddr size ?) \ store header info in VDP RAM
        MULTIFLAG V!  PROGSIZE V!  LOADADDR V! ;
    
    : END  ( -- addr )
      ORGDP @ DUP C000 < IF HONK CR ." WARNING: missing LOCK directive" THEN ;
    
     \ words to compute Forth system properties
    : SYS-SIZE    ( -- n)  HIMEM  END  SWAP - ;
    : #FILES      ( -- n)  SYS-SIZE 8K /MOD SWAP IF 1+ THEN ;
    : CODECHUNK   ( n -- addr) IMGSIZE * HIMEM + ;
    : CHUNKSIZE   ( n -- n ) CODECHUNK END SWAP -  IMGSIZE MIN ;
    : LASTCHAR++  ( Caddr len --)  1- +  1 SWAP C+! ;
    : HEAPSIZE    ( -- n)  H @ LOWRAM - ;
    : ?PATH  ( addr len -- addr len )
      2DUP  [CHAR] . SCAN NIP 0= ABORT" Path expected" ;
    
    : GET-PATH    ( <text>) BL PARSE-WORD ?PATH  PAD PLACE ;
    
    : FILENAME    ( -- addr len) PAD COUNT ;
    
    VARIABLE FILECOUNT
    
    : SAVE-IMAGE ( addr len Vaddr size -- )
        CR ." Writing file: " FILENAME TYPE
        HEADLEN +  PROGRAM SAVE-FILE
        FILENAME LASTCHAR++
        FILECOUNT 1+! ;
    
    : SAVELO ( -- )
        HEAPSIZE
        IF
            LOWRAM HEAPSIZE DUP>R FALSE HEADER \ heap is last file saved
            LOWRAM CODEBUFF R@ VWRITE          \ copy HEAP to VDP
            FILENAME VDPBUFF R> SAVE-IMAGE
        THEN ;
    
    HEX
    : SAVEHI ( XT -- <textpath> )
        #FILES 0
        ?DO
          \ compute file header values
           I CODECHUNK  I CHUNKSIZE       ( -- addr size )
           I 1+ #FILES <>  HEAPSIZE 0> OR \ multiflag=true if heap has data
           ( addr size ?) HEADER          \ store in file header
          \ Copy to VDP RAM
           LOADADDR V@  CODEBUFF  PROGSIZE V@ HEADLEN +  VWRITE
          \ write VDP to disk"
           FILENAME   VDPBUFF   PROGSIZE V@  SAVE-IMAGE
        LOOP
    ;
    : .BYTES&ADDR ( addr size --)
       DECIMAL 5 U.R ."  bytes, at " HEX ." >" 4 U.R ;
    
    : REPORT
        CR
        CR ." Himem : "  HIMEM  ORGDP @ OVER -  .BYTES&ADDR
        CR ." Heap  : "  LOWRAM  HEAPSIZE  .BYTES&ADDR
        CR ." Saved in " FILECOUNT @ .  ." EA5 files"
        CR
    ;
    
    : SAVESYS ( xt -- <path>)
        BOOT !
        FILECOUNT OFF
        GET-PATH  SAVEHI  SAVELO REPORT ;
    
    HERE SWAP - CR DECIMAL . .( bytes)
    
    \ ----------------
    \  TEST CODE
    INCLUDE DSK1.MALLOC
    
    HEX 800 MALLOC CONSTANT MYBUFFER \ mybuffer is in Low RAM
    
    MYBUFFER 800  CHAR $ FILL
    
    : GO   WARM  ABORT ; \ minimum startup code to start Forth interpreter
    
    LOCK                 \ lock dictionary to current size on re-boot
    
    ' GO SAVESYS DSK7.TESTKERNEL
    

     

     

    • Like 3
  20. 9 hours ago, FarmerPotato said:

    This is fascinating. I've felt stuck over how to "package" a Forth (game) without all the compiler stuff.
     

    Smalltalk approached this problem with a "packager", but Smalltalk's runtime flexibility made it hard to predict what was used and what wasn't. 
     

    Could you compile the dictionary to a dummy address? Say, at >6000 for  a ROM cartridge.  Like, a game written in Forth.
     

    Assuming 32K RAM required.  Variables would need to point to RAM.  Maybe you could have a defining word PADVARIABLE? ALLOT would consume  32K RAM. 
     

    I'm also recalling Lee's fbForth cartridge--where names are not mixed in with the code. 

     

    A really crazy idea I just had is: your IMPORT: is kind of like an assembly REF. What if the  re-compilation made "relocatable" code containing unresolved references?


    I see this is completely unnecessary if compilation just uses a dummy base address, say 6000, while actually building  the image in RAM elsewhere. 

     

    But: Analogous to the TI assembler, a re-compiler would build a REF/DEF table. A REF table entry is a linked list of everywhere the symbol is needed. Each new word gets a DEF with its address. (All addresses to be relative to a base.) 
     

    The recompiler could even infer the IMPORT:   for any word not yet encountered. (Not yet present in the DEF table.) 


    Again just like the E/A loader:  at load/link time, there is a base address, like 6000 or A000, and DEFs are calculated to be absolute addresses.  REFs are then resolved to the corresponding DEF. 

    Probably an overly complicated solution. 

    Anyway, your  work here is a big step toward solving the "packaging" problem!

    I love your excitement.

    You have touched on a bunch of possibilities.  I am going to get something working and then start to think about where it goes

    For example it is relatively simple to change the threading method from indirect threading to direct threading or even sub-routine threading with inlining.

     

    The infer idea could possibly be done with a first pass through the source code and compile the run time code for all the primitives that are required.

    That might be overkill however since it's pretty simple to add primitives to an import statement.

    I am thinking about adding an import statement to every source file and make IMPORT:  a bit smarter so it only compiles a primitive once.

     

    Lots to think about.   And yes I can compiler headers in other memory because I can move the Forth dictionary to other memory.

    I am also considering putting the compiled code into VDP RAM so that I can make programs bigger than 8k, but that will mean putting some of the compiler in SuperCart Ram or SAMS. 

     

    BTW the way I think I will "borrow" that word 'PACKAGE'  for something.  :) 

     

     

    • Like 3
×
×
  • Create New...