Jump to content

TheBF

+AtariAge Subscriber
  • Content Count

    3,166
  • Joined

  • Last visited

Posts posted by TheBF


  1. Ah. That is a wrinkle.

     

    So this not very Forthy but might save memory. 

    Build a small stack frame for your variables on the data stack and reference them with indexed addressing and collapse it on the way out?

    Not sure if you'd eat more space with code than you do now with variables because indexed addressing eats up extra bytes too.

    I find the 9900 does not surrender to optimization without a fight. :) 

     

     

    • Like 1

  2. While reading comp.lang.forth I saw a link to this page which I think is the best summation of Chuck Moore's approach to programming that I have seen.

     

    It was written by the late Jeff Fox (no relation) who passed away of a heart attack at the age of 62 in 2011.

    Jeff worked closely with Chuck creating software for Chuck's new CPU designs.  He is missed in the Forth community.

     

    http://www.ultratechnology.com/method.htm

     

    I personally like step 9 in this 10 step approach 

     

    9. Code.
       Build custom tools if they help.
       Write code so simple and clear that bugs simply can't happen.
       Make the code "right by design."
       :define using one-liners about this long ;
       Interactively test each Forth word.
       Extend the core language making your custom language and moving
       you toward your solution.
       Return to 1. UNTIL the code solution falls out.

     

    : HARD     REMEMBERING TO MAKE NICE SHORT WORDS  ;  :) 

     

    • Like 4

  3. 1 hour ago, speccery said:

    I would encourage you to try out yourself. With your knowledge of Forth etc it should not be hard to get going. If I get this project implemented the way I am intending, it could be an interesting platform to run for instance a Forth CPU. For that we should use larger FPGA chips than the 1K LUT chip though. With the open source toolchain it is straightforward to try things out, for example on a Raspberry pi, without installing gigabytes worth of software. 

     

    Today feeling much better, I think I have beaten covid the 2nd time as well.

    Cool.  I have an RPI 400 so I have a beginning.

    I don't know but it makes me wonder if the J1 processor could be implemented in 1K LUTs.  It's only 200 lines of Verilog.

     

    https://github.com/jamesbowman/j1/blob/master/verilog/j1.v

     


  4. OK so it's not where I want it to be but if anybody has nothing better to do here is the first cut of MachForth (alpha alpha) 

     

    bfox9900/MachForth: Native code compiler for 9900 modeled on machine Forth by Charles Moore with liberties taken. (github.com)

     

    Browse to the TI-99 folder and make a DSK2 for yourself on Classic99 with the files in that folder. 

    Follow the instructions in the readme.

     

    All the other Demo programs are in the DEMO.SRC folder.  They are PC text files that you can past into MACHFORTH.

    The command NEW.  does not clean up the compiler dictionary at this time so after pasting a program into the compiler a few times you can blow it up.

    For now restart it when that happens. 

     

    I have added a STDLIB file that pulls in VDPLIB, SCREENIO, NUMBERS and BYE.  

    With that you can write you own MachForth program. 

    DOT QUOTE  ( ." )   is not implemented yet so look at the DEMOs if you want to make strings.

     

    The video shows the operation for the simplest HELLO23 program with no libraries used.

     

    I have a lot of documents to make but the compiler source code is there if anybody is curious.

     

     

    • Like 2
    • Thanks 1

  5. Yes it seems everyone will get this thing now.

    Some research indicates that you should now have excellent immunity with the vaccines plus the "wild" virus providing the broadest range of antigens.

    So with a little luck, this could be the last time you get sick with this one.

     

    On another note I am totally envious of your work with FPGAs.  :) 

    Keep posting. It's fun to read about your work.

    • Like 1

  6. 20 minutes ago, speccery said:

    I got covid again. Luckily it seems to be much milder this time around, but still causes tiredness and makes it hard to focus.

    Sorry to hear that.

    I have read that vitamin D seems to be important in fighting this thing. With winter in the north we are not getting enough sunshine typically. Might be worth a try.

    I suspect it's whatever the next variant is in your part of the world so your previous immunity is just slightly off for the new form.

     

    Out of curiosity did you receive any of the vaccine products that are available?

    I did because I have most of the known factors that lead to "bad outcomes" as the doctors put it. (death :) )

    (AB- blood which is the worst, over 65, male) 

     

    • Like 2

  7. As I understand it the source code for this compiler is no longer available?

     

    I makes me wonder if it would be worthwhile to try rebuilding Small C from source.

    I remember studying an old version of Small C for DOS, 20 years ago and it seems pretty understandable.

    Probably a ton of work just the same to make it reliable but in the end the compiler would be understood better no?

    Some of the GCC ASM code could be used to guide the brave person who tries it I should think.

     

    And of course I just checked and someone on the inter-web has "re-constituted" Small C.

    https://github.com/ncb85/SmallC-85

     

    The notes claim that you just need to change the codexxxx.c  file and all will be well.  :) 

    Is this a crazy idea?

     

    • Like 1

  8. Symbolic Addressing makes a Big Difference

     

    Now that the MachForth compiler is getting more stable, I am cleaning up a set of test programs that provide examples of how to do things in MachForth.

    I am slowly getting something ready on Github for those brave souls that might want to play with this thing.

    (It's a toy at the moment because the library code is sparse but it's been a great education in Assembly language for me)

     

    This program shows how using symbolic addressing reduces code size and improves speed when using variables.

    The native Forth way of storing to a variable is:

    (Top of stack is cached in a register here so it's slower than using TOS in memory)

    • Push the TOS cache register to free it for a new use (2 instructions) :( 
    • LI the variable's address into TOS register  ( 1 instruction)
    • Then  store :    MOV *SP,*R4    ( 1 instruction)   
    • and refill TOS:  MOV *SP+,R4   ( 1 instruction)  

    The program code has a table showing the effects of the different operators with and without the optimizer.

    I also tested the equivalent test program on Camel99 Forth.  It's 5X slower. Humbling.

    \ MachFORTH DEMO #5:   Using variables and constants
    
    \ Compiler Preamble
      COMPILER
      NEW.
      HEX 2000 ORIGIN.
    
    \ declare a constant in COMPILER space.
    -3 CONSTANT K
    
      OPT-OFF
    \ Operators                OPT-ON    Bytes   OPT-OFF  Bytes
    \ ------------            ---------  ----- |  -------  -----
    \ Normal   ! and +!      12.30 secs    82  |   15.56    94
    \ Symbolic -> and ->+     9.31 secs    70  |   12.56    82
    
    \ Camel99 Forth          47.70 secs
    
    TARGET
    VARIABLE X
    VARIABLE Y
    VARIABLE Z
    
    PROG: DEMO5
            TRUE             \ put -1 (>FFFF) on data stack
            BEGIN
               1-
            WHILE
               K ->+ X        \ add K to symbolic address
              \ K X +!          ( normal Forth syntax)
    
               Y 1+!
               X @ Y @ + -> Z \ symbolic address store
              \ X @ Y @ +  Z ! ( normal Forth syntax)
            REPEAT
            DROP
    
            NEXT,            \ return to Camel99 Forth
    END.
    
    \ ELAPSE DEMO5 RUN
    

     

     

    • Like 4

  9. Once you have SCREEN I/O words working it's time to make number printing routines.

    I chose to try and port the standard Forth number formatting words so that MachForth would have 16 bit and 32 bit output capability.

     

    I am beginning think that I am just not quite as smart as Chuck Moore ( ya figure?)  :) 

    I don't think I can cope with the limited set of  'IF' statements that he uses in his Machine Forth. 

    I had to create some new words here to make this code work. 

    It's not as pretty as I like so I suspect I will be going back to something that works the way Forth Assembler works on the 9900.

    For now I will live what I have.

     

    Note:

    This machine forth is weird enough that it takes some learning to use it.

    I am thinking I will be morphing it into something that looks more like standard Forth. For now I will make it work, then make it better 

     

    Different Tradeoffs versus ITC

    I changed the standard 'dot' word  that prints a signed integer. It uses ROT which Machine Forth did not have.

    There are very different tradeoffs to be made when you use "nest-able" sub-routines on the 9900.

    They are terribly pregnant taking 2 instructions to enter and 2 instructions to exit.

    In the Forth universe where one virtual instruction can be only one machine instruction that is just not acceptable.

    Most of the "intrinsic instructions" in MachForth are therefore inlined as was done in the original  "OK" version of Machine Forth.

     

    Building the 'dot' word with ROT took 16 extra bytes versus using some optimized words DUP>R and RDROP along with [email protected]

    DUP>R and RDROP are both inline instructions on the 9900 MachForth. :) 

    (I could have made ROT inline but that just seemed wrong)

    \ conventional Forth version
    : ROT    ( n1 n2 n3 --  n2 n3 n1)
         [  2 (SP)    R1 MOV,
           *SP   2 (SP) MOV,
            TOS     *SP MOV,
            R1      TOS MOV, ]
    ;
    
    : .      ( n -- ) DUP ABS  0 <#  #S ROT SIGN  #> TYPE SPACE -;
    

    Smaller MachForth version 

    : .      ( n -- ) DUP>R ABS  0 <#  #S [email protected] SIGN  #> RDROP  TYPE SPACE -;
    

     

    Here are the newly hatched number formatting library.

    Spoiler
    \ NUMBER OUTPUT lib for MachForth    Mar 3 2022  Brian Fox 
    
    COMPILER
    \ constants declared in compiler space
      CHAR 0 CONSTANT '0'
      CHAR - CONSTANT '-'
    HEX 3FFF CONSTANT #BUFF \ top end of low RAM used as digit buffer
    
    \ REQUIRES DSK2.SCREENIO
    
    OPT-ON
    TARGET
    VARIABLE BASE
    VARIABLE HP
    
    : UM/MOD ( ud u1 -- u2 u3 )  \ numerator(32bits), divisor -- rem,quot
             TOS  R0 MOV,     \ divisor->R0
             *SP+ TOS MOV,     \ POP high word into TOS
             *SP   R5 MOV,     \ MOVE low word to r5
              R0  TOS DIV,     \ perform unsigned division
              R5  *SP MOV,     \ push remainder
    ;
    
    DECIMAL
    \ : >DIGIT   9 >IF DROP 7 + THEN DROP '0' +  ; \ does not work ??
    : >DIGIT  ( n -- c)
            [ TOS 9 CI,
              HI IF,           \ if n>9
                  TOS 7 AI,    \ number is not base 10, add 7
              ENDIF,
              TOS '0' AI, ]    \ add ASCII 0 to TOS create char value
    ;
    
    \ : HOLD   ( char -- )  HP DUP 1-! @ C! ;  \ 11 instructions
    : HOLD  ( char -- )  \ 5 instructions
           [ HP @@ DEC,
             HP @@ R1 MOV,
             TOS SWPB,
             TOS R1 ** MOVB, ]
             DROP
    ;
    
    \ [ BASE #@ ] does symbolic addressing to fetch a variable
    : #      ( u -- ud2 )
          0 [ BASE #@ ] UM/MOD >R    \ compute & save high side of 32 bit int.
            [ BASE #@ ] UM/MOD SWAP  \ compute low side, swap quotient & remainder
            >DIGIT HOLD
            R>                       \ high side to TOS
    ;
    
    : <#     ( --) #BUFF -> HP ;  \ TOS->symbolic store. Smaller & faster
    
    \ '=='  is non-destructive comparison of TOS & NOS  ( C R4,*SP )
    : #S     ( ud1 -- ud2)  BEGIN  #  == -UNTIL  ;
    : #>     ( ud1 -- c-addr u) DROP DROP HP @ #BUFF OVER - ;
    : SIGN   ( n -- ) 0 <IF  '-' HOLD  THEN DROP ;
    : UD.    ( d -- ) <#  #S  #> TYPE SPACE -;
    : U.     ( u -- ) 0 UD. -;
    : .      ( n -- ) DUP>R ABS  0 <#  #S [email protected] SIGN  #> RDROP TYPE SPACE -;
    

     

     

    Here is the test program 

    Spoiler
    \ MachForth number output test program     Mar 2 2022  Brian Fox
    
    COMPILER  \ preamble to set up target image
      NEW.
      HEX 2000 ORIGIN.
    
    \ constants declared in compiler space
      CHAR 0 CONSTANT '0'
      CHAR - CONSTANT '-'
        3F00 CONSTANT #BUFF
    
    INCLUDE DSK2.SCREENIO
    INCLUDE DSK2.NUMBERS
    
    CREATE TITLE$  S" Signed  Un-signed" S,
    
    OPT-OFF
    PROG: MAIN
          10 BASE !
          PAGE
          8 9 AT-XY  TITLE$ COUNT TYPE
          32000      \ first number in count
          2001
          FOR
              9 10 AT-XY
              DUP . SPACE DUP U.
              1+
          NEXT
    
          NEXT,
    END.
    
    TARGET
    

     

     

    Weird syntax Reminders:

    [   enables the interpreter and is used to put Assembler code into a colon definition.

       Also can be used to "interpret" a variable, which returns the data address.  Used with SYMBOLIC fetch and store:  #@  #! 

     

    ]   turns the compiler back on. Used when you go back to compiling Forth code after ALC.

    -> emits register to symbolic code to load a variable from top of Forth stack 

    -;   Tail-call optimizer.  Replace a sub-routine call with a branch instruction. Only if the last word is called (not inlined) 

     

     

     

    • Like 2

  10. 44 minutes ago, Tursi said:

    It's the right answer for all projects... even if the warnings /are/ known to be safe, you should turn them off one by one for the specific code block you determined, rather than get people used to ignoring them. I've worked on so many projects that just flood the screen with warnings - how would you ever find a real one, or a new one?

     

    The drive to resolve undefined behavior in C rubs me the wrong way for some reason. The reason things were defined as undefined behavior was not to shrug and say "idunno", but to leave it open to architecture-specific implementation and warn the programmer not to do that. But it seems that telling the programmer something is a bad practice these days isn't acceptable, so instead the language must change. Probably I'm getting old. ;)

     

    When you say it that way it makes a lot more sense to me.

     

    The ANSI Forth standard was purposely not an "implementation spec" in order to accommodate all current or future implementations.

    However this leads to a lot of arguments on what it "really" means.

     

    I know very little about the unspefied behaviour  (I thought the term "nasal demons" was genius) except what I have read over time about GForth and the build team's frustrations with GCC verions.

    I know they push the envelope pretty hard to make the C compiler do their bidding so could be a case of:

     

    "Doctor it hurts when I do this." 

    Doc says" Then don't do that!" 

     

     

     


  11. I read this too.  It will be interesting to see how that goes.

     

    I am fascinated by the discussions in the C community about "nasal demons" (un-specified behaviour).

    This has crossed into discussions on comp.lang.forth because GForth is written with GCC and with each compiler change there were problems.

    This has lead to the possibility of abandoning C to make GForth. 

    It seems the endless pursuit of performance enhancements versus predicability is the issue from what I understand of the conversation. (limited knowledge of C here) 

     


  12. After building what seems to be a stable VDP library and SCREEN I/O words for MachForth I have decided not to follow Chucks lead with the Carry flag based loops. 

    Using the carry flag means that loops iterated 1 extra time. This makes the code strange because you can't use  the natural value of the loop iterator like a string count. You have to decrement it first. I don't like that.

     

    So I have simply used JEQ  and JNE instructions for the default branching.

    Spoiler
    \ compute signed offset & compile into the 2nd byte of any 9900 JUMP instruction
    : RESOLVE ( addrofjmp byte --)  2- 2/ SWAP 1+ C! ;
    : <BACK   ( addr addr' -- ) TUCK -  RESOLVE ;
    
    \ Changed from Chuck's original code and use EQ flag
    : -IF    ( -- $$)   THERE 0 JEQ, ;  \ GOTO then if EQ=true
    : IF     ( -- $$)   THERE 0 JNE, ;  \ GOTO then if EQ=false
    
    \ Chuck used Carry flag conditional. Kept it with new name
    : NC.IF  ( -- $$)   THERE 0 JOC, ;  \ GOTO then if Carry=True
    
    : THEN   ( addr --) THERE OVER - RESOLVE ;
    : ELSE   ( -- $$ )  THERE 0 JMP,  SWAP THEN ;
    
    : BEGIN    THERE ;  IMMEDIATE             \ THERE current dictionary address
    : WHILE     IF SWAP ;                     \ loop while EQ=false
    : -WHILE   -IF SWAP ;                     \ loop while Carry=false
    : AGAIN   ( addr --)  THERE 0 JMP, <BACK ;  \ Jump back always
    : UNTIL   ( addr --)  -IF <BACK ;     \ jump back until EQ=false
    : -UNTIL  ( addr --)   IF <BACK ;     \ jump back until EQ=true
    : REPEAT  ( addr -- ) AGAIN THEN ;
    

     

     

    A complication that I needed for screen I/O  was magnitude comparisons. I didn't quite see how to that in Chuck's machine Forth short of using subtraction.

    That did not appeal to me so I decided to add the words >IF and <IF.

    These use the SIGNED jump instructions and the C (compare) instruction. 

    They are consistent with Machine Forth principal that they do not clean off the top of stack so that it can be used, but they do remove the next item on stack (NOS).  It seems to be a workable way to do this. 

     

    With these I was able to successfully build a working set of Forth screen words. 

    Spoiler
    : =     *SP+  TOS CMP, ;
    : 0=    TOS   0   CI, ;
    
    \ signed comparisons
    : >IF   ( n n -- $$)  =  THERE 0 JLT, ; \ goto then low or eq
    : <IF   ( n n -- $$)  =  THERE 0 JGT, ;
    

     

     

    SCREENIO library code

    Edit: fixed VPOS and EMIT and SCROLL comment

    (A cool thing is the use of tail-call optimization which saves 9 bytes in this simple example) 

    Spoiler
    INCLUDE DSK2.VDPLIB
    
    \ Screen I/O code
    COMPILER
    DECIMAL
     40 CONSTANT C/L    \ chars per line
    960 CONSTANT C/SCR  \ chars per screen
    920 CONSTANT 23LINES
    
    OPT-ON
    
    TARGET
    VARIABLE ROW
    VARIABLE COL
    VARIABLE MDP
    
    : MFHERE  MDP @ ;    \ points to empty memory above CODE
    
    : AT-XY  ( col row --) ROW !  COL ! ;
    : VPOS   ( -- Vaddr) ROW @ C/L *  COL @ + ;
    : PAGE   ( -- ) 0 0 AT-XY  VPOS C/SCR  BL VFILL -;  \ tail call optimize
    
    : SCROLL ( -- ) \ full-screen buffer. wasteful but fast
            0
            DUP  C/L +  MFHERE 23LINES VREAD   \ get 2nd line
            MFHERE      OVER   23LINES VWRITE  \ write to 1st line
            DROP
            0 23 AT-XY
            VPOS C/L BL VFILL -;  \ tail call optimize
    
    : CR    ( -- )
            COL OFF
            ROW DUP 1+! @ 24 >IF  SCROLL  THEN DROP ;
    
    : EMIT  ( c --)
           VPOS VC!
           COL DUP 1+! @ C/L 1- >IF  CR  THEN DROP ;
    
    : TYPE   ( addr len --) FOR  COUNT EMIT  NEXT DROP ;
    
    : SPACE  ( -- )   BL EMIT -; \ tail call optimize
    : SPACES ( n -- ) FOR  SPACE  NEXT ;

     

     

    The code above with OPT-ON uses 656 bytes of code space including the VDPLIB code.  (The POP/PUSH optimizer saves 14 bytes) :)

     

    I cheaped out on SCROLL and just copy all the screen minus the topline into empty memory above the code.

    I will change that in future but it works for now.  It means I have to patch the value in the MDP variable after compiling the program.

     

    Below is the test program that exercised thE screen I/O words. 

     

    At the moment the POP/PUSH optimizer fails on the SCROLLTEST word. (Not sure why) 

    So we just disable it for that line. :)

     

    Spoiler
    \ scroll and screen i/o Test program
    
    COMPILER  \ preamble to set up target image
      NEW.
      HEX 2000 ORIGIN.
    
    INCLUDE DSK2.SCREENIO
    
    : WAIT        65000 FOR NEXT ;
    
    CREATE A$  S" This is line 1. " S,
    CREATE B$  S" 2nd line of text" S,
    
    OPT-OFF
    : SCROLLTEST  CR  300 FOR  A$ COUNT TYPE  NEXT ;
    OPT-ON
    
    DECIMAL
    PROG: MAIN
          PAGE
          0 0 AT-XY
          A$ COUNT TYPE CR
          B$ COUNT TYPE
          WAIT
          SCROLLTEST
    
          NEXT,   \ return to Camel99 Forth
    END.
    
    \ patch TARGET variable that holds end of memory
    COMPILER THERE 4 + [ TARGET MDP ] HOST !
    
    TARGET
    

     

     

    Next I need to write the number printing words.

     

     

     

    • Like 3

  13. 44 minutes ago, FarmerPotato said:

    @apersson850 Jeg vil gerne prøve din kode!

     

     

    Threading is hard. 

     

    Yup.

     

    My choice would be both:

     

    1. cooperative tasking for non-real-time stuff. If your O/S primitives are built to fit this and yield on I/O it just works.
      Context switching is ridiculously efficient.
       
    2. interrupt driven queue of time critical tasks ( you can fight with all that as you see fit)  :)

     


  14. 19 hours ago, FarmerPotato said:

    I’m very grateful! 
     

    My understanding of how  VOCABULARY works is poor. I know it changes the results of a dictionary lookup, and the compiled results aren’t affected by anything later. (CFAs are just pointers.) 

     

    But when you switch vocabularies , does it change some pointers in the dictionary? Is that the problem you had with saving the state, there are some pointers left in the wrong state?

     

    I’ve used vocabulary in FORTI because the note compiler has words A B C D E F G. (And A# and A$ for flat.) 

    These can only execute as words when the vocabulary is active, which is always  inside the compiling word VOICE: — ;

    kind of like assembler, but for music. 


    The A B C words compute a note number, modified by a LOT of context (like key and octave plus ornamentation)  

    They push a word or two onto the dictionary. The VOICE: is used to make a sequence of notes , to be interpreted later by a player.
     

    And outside a VOICE: definition you never, ever want A to be anything but a hexadecimal number! (Even the assembler vocabulary uses A, (A comma )

    still needing a lot of studying to really comprehend what’s going on with VOCABULARY. 

     

     

    It is probably beyond a simple post to fully explain this but here goes.

    (I could not have done this 3 years ago so I guess that's progress for an old guy) :)

     

     

    Since the Forth dictionary is a linked list that begins at the last word defined, that constitutes a vocabulary in the simplest sense.

     

    However...  VOCABULARY  is not part of the ANSI Forth and has been replaced by WORDLIST.

    WORDLIST is a simpler thing, a little 3 field data structure that just returns its address onto the data stack.

    The address is called a 'wid'.  (wordlist identifier)

    : WORDLIST ( -- wid)
       HERE
       0 ,               \ init nfa of last word in wordlist
       WID-LINK @ ,      \ compile link to previous wordlist
       DUP WID-LINK !    \ link to previous wordlist
       0 ,               \ name of this wordlist. Must be patched
    ;
    

     

    To make Forth search a WORDLIST you make it the "CONTEXT" wordlist with SET-CONTEXT.

     

    There are two variables that determine specific roles of  dictionary searches made by the compiler/interpreter.

    VARIABLE CURRENT
    VARIABLE CONTEXT 

    The CONTEXT "WORDLIST" is what the compiler searches to find definitions to build new definitions or to run an immediate command.

    CONTEXT holds a "pointer to a pointer"  to the last word created in a WORDLIST. 

     

    The CURRENT wordlist is the name space (wordlist) that new words will become part of when you define them.

     

     

    The good news is that it's simple to make a vocabulary (which most implementers do) with CREATE/DOES> .

    Just create the wordlist data structure and at run-time have it set itself as the CONTEXT wordlist. :) 

    : VOCABULARY  ( <text> )
       CREATE
       WORDLIST   
       LATEST @ SWAP 4 + !  \ update wordlist name field
       DOES> SET-CONTEXT ;
    

     

    In Fig-Forth or Forth 79  systems FORTH is the default vocabulary that is searched and where new words are built.

    If you invoke another vocabulary by using its name,  the new vocabulary is searched first and when you get to the end of the it, the search continues on into the Forth vocabulary. So they are effectively connected by default.

     

    In ANSI Forth and Forth 83 we are given control of what gets searched and in what order. 

    So if you have FORTH,  ASSEMBLER and EDITOR vocabularies, you can control which one gets searched first, second and third in any order.

    This is more complicated by gives you great flexibility for complex jobs.

     

    In the new model the CONTEXT variable becomes an array of addresses (in my implementation).

    Each array cell points to the last word defined in each specific vocabulary. 

    The system knows the number of vocabularies you want searched and it will search each one in the order that they are placed in the array.

     

    There is still only one CURRENT vocabulary where new definitions are added.

     

    I think I will leave it there for a little digestion.

     

    More info can be found here: WORDLIST - SEARCH (forth-standard.org)

     

    Here is my latest code for WORDLISTS

    Spoiler
    \ wordlist.fth   for CAMEL99 FORTH    Oct 2020 Brian Fox
    \ Code adapted from Web: https://forth-standard.org/standard/search
    \ Dec 2020: Removed SET-CURRENT to save precious bytes
    \ Jan 5, 2020: back migrated some enhancements from CODEX work
    \ Jun 4, 2021: Changed order of patching to work with TTY version
    \ Sep 25, 2021: Corrected SET-CONTEXT, Removed ROOT to save space.
    \ Feb 23, 2022: Added INIT-WORDLISTS for binary program startup
    \ --------
    \ 'wid' is a word-list ID.
    \ In Camel Forth, wid is a pointer to a Name Field Address (NFA)
    \ ie: a counted string of the last word defined in the wordlist.
    
    \ The kernel program has a pre-defined CONTEXT array to hold the
    \ Forth wordlist plus 8 user defined wordlists.
    
    \ NEEDS .S   FROM DSK1.TOOLS ( Debugging)
    
    HERE
    DECIMAL
    CREATE #ORDER  1 ,  \ No. of active wordlists starts at 1
    VARIABLE WID-LINK   \ Pointer to the most recently defined wordlist
    
    CREATE FORTH-WORDLIST    0 ,  0 ,  LATEST @ ,
     FORTH-WORDLIST WID-LINK !  \ set first WID in the chain
    
    : WORDLIST ( -- wid)
       HERE
       0 ,               \ init nfa of last word in wordlist
       WID-LINK @ ,      \ compile link to previous wordlist
       DUP WID-LINK !    \ link to previous wordlist
       0 ,               \ name of this wordlist. Must be patched
    ;
    
    HEX
    : .WID  ( wid -- )
      [ 2 CELLS ] LITERAL + @
      ?DUP 0= IF EXIT THEN   \ name field is empty.
      COUNT 1F AND TYPE SPACE ;
    
    \ : ]CONTEXT ( n -- addr) CELLS CONTEXT + ; \ context as array
    HEX ( Machine code is same size but faster)
    CODE ]CONTEXT ( n -- addr)
         A104 ,            \ TOS TOS ADD,
         0224 , CONTEXT ,  \ TOS CONTEXT AI,
         NEXT,
         ENDCODE
    
    .( .)
    : GET-ORDER ( -- widn ... wid1 n ) \ *reversed order on stack
         #ORDER @  0 DO   #ORDER @ I - 1- ]CONTEXT @   LOOP  #ORDER @  ;
    
    DECIMAL
    : SET-ORDER ( wid1x ... wid1 n -- )  \ n cannot be 0
         DUP 0< IF DROP  FORTH-WORDLIST DUP 2  THEN
         DUP #ORDER !  0 ?DO  I ]CONTEXT !  LOOP
    ;
    
    : ONLY ( -- ) TRUE SET-ORDER ;  \ set search order to FORTH FORTH
    
    : SET-CONTEXT ( wid -- )    \ place 'wid' at beginning of search order
         >R GET-ORDER NIP       \ remove 1st wordlist
         R> SWAP SET-ORDER      \ put 'wid' first
    ;
    
    \ User API ...
    : ALSO        ( -- ) GET-ORDER OVER SWAP 1+ SET-ORDER ;
    : PREVIOUS    ( -- ) GET-ORDER NIP 1- SET-ORDER ;
    : DEFINITIONS ( -- ) CONTEXT @ CURRENT ! ;
    .( .)
    \ non-standard but nice to have
    : VOCABULARY  ( <text> )
       CREATE
       WORDLIST
       LATEST @ SWAP 4 + !  \ update wordlist name field
       DOES> SET-CONTEXT ;
    
    : ORDER ( -- )
       CR  GET-ORDER 0 DO   .WID   LOOP
       CR ." Current: " CURRENT @ .WID CR ;
    
    : FORTH  ( -- ) FORTH-WORDLIST SET-CONTEXT ;
    
    \ patch FORTH-WORDLIST to existing dictionary
       CONTEXT @ @ FORTH-WORDLIST !
    \ set the new search order and current vocabulary
       FORTH-WORDLIST DUP 2 SET-ORDER  DEFINITIONS
    
    \ Forth 2012 6.1.1550, Extend FIND to search all active wordlists
    : FIND12 ( FIND12) ( c-addr -- c-addr 0 | xt 1 | xt -1 )
          FALSE   \ default flag
          CONTEXT #ORDER @ CELLS ( -- addr size)
          BOUNDS
          ?DO
              OVER I @ @ (FIND)
              ?DUP
              IF
                  2SWAP 2DROP
                  LEAVE
              THEN
              DROP
          2 +LOOP ;
    
    ' FIND12 'FIND !
    ONLY FORTH DEFINITIONS
    
    : INIT-WORDLISTS
        ['] FIND12 'FIND !
        CONTEXT @ @ FORTH-WORDLIST !
        ONLY FORTH DEFINITIONS
    ;
    
    INIT-WORDLISTS
    
    CR HERE SWAP - DECIMAL SPACE . .( bytes)
    HEX
    

     

     

     

     

     

    • Like 2

  15. Sometimes I can make myself feel pretty stupid. :) 

     

    I am working on a little debugger Forth for @FarmerPotato  for Geneve 2020 and I wondered if I could incorporate wordlists into the system to hide the Assembler.  It took me down the rabbit hole of how to make sure the dictionary would wake up correctly because I had not made that work in Camel99 yet. :( 

     

    One of the neat things about Forth is that while you are compiling code you have access to the interpreter and can do processing inside the source code as well. This works perfectly when loading source code but if you save that code as a binary executable YOU HAVE TO REMEMBER TO DO IT at runtime. 

     

    So in my WORDLIST implementation I used this feature to initialize the new Forth wordlist in three lines.

    Somehow I missed the importance of these lines all this time. DOH! 

    One of those lines completely changes the dictionary search mechanism.  (how dumb am I)

     

    Anyway I finally can build a big system with vocabularies, save it as a binary program and it starts with everything intact. 

    This has great implications for the machine Forth compiler which needs many vocabularies to partition cross-compiler, cross-assembler and target Forth dictionaries.  Here is what I needed to add

    : INIT-WORDLISTS
        ['] FIND12 'FIND !
        CONTEXT @ @ FORTH-WORDLIST !
        ONLY FORTH DEFINITIONS
    ;
    

     

    And now here is what it takes to build a new Forth with more features:

    \ building a big Forth system with WORD lists       FEB 2022 Brian Fox
    
    \ starting point from the CAMEL99 Kernel 
    INCLUDE DSK1.WORDLISTS
    
    \ load the assembler in it's own vocabulary
    VOCABULARY ASSEMBLER
    ASSEMBLER DEFINITIONS
    INCLUDE DSK1.ASM9900
    
    \ load some tools
    ONLY FORTH DEFINITIONS
    INCLUDE DSK1.TOOLS
    
    \ create a new BOOT word
    : COLD
        WARM             \ Init hardware, restore Forth dictionary
        INIT-WORDLISTS   \ set find mechanism, init FORTH wordlist
        ABORT ;          \ reset the interpreter
    
    LOCK                 \ lock end of dictionary at COLD
    
    INCLUDE DSK1.SAVESYS
    
    ' COLD SAVESYS DSK6.FATFORTH
    

     

    • Like 6

  16. 4 hours ago, Willsy said:

    Is the stack comment for RESOLVE correct? Looks iffy. What is that SWAP doing? ;-)

    You are correct there should also be an address shown which is the location of the jump instruction.

     

    What is SWAP doing?  That's PFM. 

    That is what we called it when I was in an engineering department.   ( Pure f... g magic) :) 

     

    With IF and -IF  I believe the SWAP is shuttling the THERE address from BEGIN to the top, so it can be used by AGAIN UNTIL or -UNTIL.

    If there was a control flow stack the THERE would sitting on top of that stack and SWAP would not be needed.

    That's my story and I'm sticking to it. :)

     

     

    • Like 1
×
×
  • Create New...