Jump to content

Photo

Camel99 Forth Information goes here

Camel99 Forth Concatentive Programming ANS Forth

236 replies to this topic

#226 TheBF OFFLINE  

TheBF

    Dragonstomper

  • Topic Starter
  • 853 posts
  • Location:The Great White North

Posted Fri Dec 14, 2018 1:00 PM

Phwoar! This is really cool. I never got a chance to look at serial. Do you mind if I use this this code (with attribution, of course) for TurboForth?

Would you mind posting some example outputs from BAUD (i.e the value that gets written into BPS) for some example baud rates? I need to check UM/MOD implementation in TF :-)

Also, can you post your code for MS if you don't mind? :-)

It'll give me something to look at over Christmas (along with Lee's recent work of CF7 and variants) now that uni has stopped until January.

Thanks

Mark

 

Of course you can use it, as long as I can re-used the fixes you find. :-)

 

Actually I am working on it on real iron and there is a bug with the baud rate setting for lower rates. 9600 and 19200 worked ok. 

I found a table in an old TI book and it looked linear down to 600 Baud. 

 

I am not sure if I can get handshaking correct for the receive side. I don't have an rs232 breakout box so it's painful.

Without interrupts of course you drop characters easily but for a console port TTYKEY is fast enough for human fingers.

A few machine code words would make it quicker.

 

The MS  code is here in my cross-compiler Forth.

 

https://github.com/b...IB/TICKTOCK.HSF

 

My little ticker is strange because it has to accommodate 

1. My compiler can only build 8K images 

2. I need to support cooperative multitasking

 

But it seems to work well on real hardware giving a consistent time.  I have a constant in the code that compensates for Forth overhead and it might be better to be a variable that could be calibrated to a machine.

It could be much finer grained as a CODE word that stops the system, or I suppose it could trigger an interrupt.

Lots of ways to make it better.  

The TMR! word starts the 9901 running continuously which broke Classic99 but Tursi fixed it for me. (Thanks Tursi)

 

Merry Christmas! :-)



#227 TheBF OFFLINE  

TheBF

    Dragonstomper

  • Topic Starter
  • 853 posts
  • Location:The Great White North

Posted Fri Dec 14, 2018 5:01 PM

Would you mind posting some example outputs from BAUD (i.e the value that gets written into BPS) for some example baud rates? I need to check UM/MOD implementation in TF :-)
 

 

These numbers concur with "Software Development, Geoff Vincent, Jim Gill,  TI Oct 1981.

Attached Files



#228 FarmerPotato OFFLINE  

FarmerPotato

    Chopper Commander

  • 212 posts
  • Location:Austin, TX

Posted Fri Dec 14, 2018 5:36 PM

 

These numbers concur with "Software Development, Geoff Vincent, Jim Gill,  TI Oct 1981.

 

 

I see you're rounding down, in favor of going faster. Correct?

 

I looked at source code and found that TE3 had >1A1 for 1200 baud - rounding 1E6/1200/2 = 416.7 = 1A1 = 1199.04 bps. 

At some point I corrected this to 1A0 for TI-Net BBS which is 1201.92 bps.

 



#229 TheBF OFFLINE  

TheBF

    Dragonstomper

  • Topic Starter
  • 853 posts
  • Location:The Great White North

Posted Fri Dec 14, 2018 5:40 PM

I have that old book here that listed 1A0 as the correct value for 1200 Baud with 3MHz clock. It references the TM990/100 or 101 board.

 

If yours works better I will use it instead. :-)



#230 TheBF OFFLINE  

TheBF

    Dragonstomper

  • Topic Starter
  • 853 posts
  • Location:The Great White North

Posted Mon Dec 31, 2018 4:17 PM

HAPPY NEW YEAR 99ERS

 

The guests have not arrived yet so I thought I would give my best wishes to all the people here for 2019.

 

It has a been good year for CAMEL99 Forth.  I have learned a lot about the old system that I didn't know 35 years ago.

Most of that due to the extraordinary talent of the people who frequent this forum.

 

Based on my recent discussions with Lee I have added BLOCK files to the system. 

It was pretty simple to add them as a layer on top of the ANS Forth file word-set but I still had a round of bug killing during the Christmas holidays.

 

For the curious here is the code to give CAMEL99 Forth style virtual memory blocks.

\ blocks.fth  for CAMEL99 Forth     Dec 17 2018 BJFox
\ Based on ideas from HsForth by Jim Kalihan (RIP)

NEEDS .S         FROM DSK1.TOOLS
NEEDS OPEN-FILE  FROM DSK1.ANSFILES
NEEDS .R         FROM DSK1.UDOTR

HEX
2                CONSTANT #BUFF     \ # of active buffers
400              CONSTANT B/BUF
B/BUF 2 CELLS +  CONSTANT B/REC     \ block-record has a 4 byte header
7FFF             CONSTANT $7FFF

3FFF 1-          CONSTANT LIMIT      \ end of buffer memory
LIMIT B/REC #BUFF * - CONSTANT FIRST  \ first buffer record address

DECIMAL 128      CONSTANT B/SEC     \ bytes per sector on TI disk

VARIABLE BLK
VARIABLE PREV      FIRST  PREV !
VARIABLE USE       FIRST  USE  !
VARIABLE LOWBLK
VARIABLE HIGHBLK   79  HIGHBLK !    \ set the highest block
VARIABLE BHNDL                      \ block file handle

HEX
 : CELL-   S" 2- " EVALUATE ; IMMEDIATE

DECIMAL
CREATE ACTIVE  20 ALLOT    \ block file name
: ACTIVE$!  ( f$ len - f$) ACTIVE PLACE ;
: ACTIVE$   ( -- addr len) ACTIVE COUNT ;

: ERASE     ( addr len -- )  0 FILL ;
: BLANKS    ( addr len -- ) BL FILL ;

\ ===================================================
\ interface to ANS File system
HEX
: ?BLOCKS   ( -- )  BHNDL @ 0= ABORT" No open BLOCK file" ;

\ move file pointer to start of block
: SEEK   ( blk# -- )
         ?BLOCKS
         DUP BLK !
         8*   ( blk# x 8 = sector)
         BHNDL @ REPOSITION-FILE ABORT" SEEK err" ;

\ READ/WRITE TI records for 1 block
: RBLK  ( adr blk# -- adr)
          SEEK
          DUP B/BUF BOUNDS ( end-addr,start-addr)
          DO    
            I B/SEC BHNDL @ READ-LINE ?FILERR  2DROP
          B/SEC +LOOP ;

: WBLK  ( adr blk# -- )
          SEEK
          B/BUF BOUNDS ( end-addr,start-addr)
          DO   
             I B/SEC BHNDL @ WRITE-LINE ?FILERR
          B/SEC +LOOP ;

\ ===================================================
HEX
: UPDATE ( -- ) PREV @ @ 8000 OR  PREV @ ! ;

: +BUF    ( addr1-- addr2)
          B/REC + DUP LIMIT = IF DROP FIRST THEN ;

: BUFFER ( n -- addr )
        USE @ DUP >R       \ get current buffer record & Rpush
        @ 0<               \ has it been updated?
        IF                 \ if true ...
           R@ CELL+        \ get buffer address
           R@ @            \ get the block number
           $7FFF AND  WBLK  \ write data to disk
        THEN R@ !          \ store this in USE record
        R@ PREV !          \ set it as previous record
        R@ +BUF USE !      \ advance to next buffer, make the USE
        R> CELL+ ;         \ return the buffer address

: BLOCK   ( block# --- addr )
        ?BLOCKS
       >R
        PREV @ DUP @  R@ - $7FFF AND
        IF
           BEGIN
              +BUF DUP PREV @ =
              IF
                 DROP R@ BUFFER  R@ RBLK CELL-
              THEN
              DUP @ R@ -  $7FFF AND
           0= UNTIL
           DUP PREV !
           DUP USE @ =
           IF
              DUP +BUF USE !
           THEN
      THEN
      R> DROP CELL+ ;

HEX
: FLUSH ( -- )
        ?BLOCKS
        FIRST           \ start at 1st block record
        #BUFF 0
        DO
           DUP @ 0<    \ is block updated?
           IF          \ yes, write to disk
              DUP @ $7FFF AND OVER 2DUP !
              CELL+ SWAP WBLK
           THEN +BUF   \ then goto next block record
        LOOP
        DROP ;

: EMPTY-BUFFERS ( -- )
        FIRST LIMIT OVER - ERASE
        #BUFF 0
        DO
          $7FFF B/REC I * FIRST + !
        LOOP ;

DECIMAL
: DF128   DISPLAY RELATIVE  B/SEC FIXED ;

:  OPEN-BLOCKS ( file$ len -- )
        2DUP ACTIVE$!
        EMPTY-BUFFERS
        DF128 R/W OPEN-FILE ?FILERR BHNDL ! ;

HEX
: CLOSE-BLOCKS ( -- )
        BHNDL @ ?DUP
        IF CLOSE-FILE ?FILERR
           BHNDL OFF
        THEN ;

\ Usage:  45 S" DSK1.MYBLOCKS" MAKE-BLOCKS
:  MAKE-BLOCKS ( n file len -- )
        OPEN-BLOCKS
        FIRST CELL+ B/BUF BLANKS
        DUP HIGHBLK !
        1+  1
        DO
           FIRST CELL+ I WBLK
        LOOP
        CLOSE-BLOCKS ;


#231 TheBF OFFLINE  

TheBF

    Dragonstomper

  • Topic Starter
  • 853 posts
  • Location:The Great White North

Posted Sun Jan 6, 2019 9:57 PM

PAGED MEMORY OPERATIONS

 

I have scratching my head on how to use the SAMS memory to implement an editor that can handle large files (64K)

I have something that works. It's not blazing fast on writing individual bytes, but it will handle typing speeds.

For block string operations to paged memory one will have to always prevent reading or writing across page boundaries but that's not a big hardship.

 

The design criteria were as follows:

  1. simplify the computation by standardizing on one 4K page in low memory at >3000. (no need to compute the register address)
  2. Only map in a new page when needed (this improved speed by 50%)
  3. Limit direct access to 64K range because that's the simplest to index
  4. use a segment variable to allow selecting other 64K segments
  5. The PAGED word will be combined with standard fetch and store operators to create the final APi

I did it first completely in Forth: (it was a little slow but it worked)

The UM/MOD operation takes an address and a segment number as a 32bit integer and divides it by 4K to give an offset into the page and the bank#

 : PAGED  ( addr -- addr')
         SEG @ 4K UM/MOD  ( -- offset bank#)
         DUP BANK# @ =            \ are we using the same PAGE
         IF
             DROP                 \ Yes! Drop bank# and get out
         ELSE
             DUP FF00 AND ABORT" SAMS Err!"
             DUP BANK# !           \ update bank# variable
             ><                    \ swap bytes, bank# must be in left byte
            1E00 CRU! 0SBO         \ enable SAMS card
           ( bank#) 4006 !         \ store bank in 3K SAMS register
             0SBZ                  \ disable SAMS card         

        THEN  PMEM +               \ then add offset to paged mem block
;
\ paged memory fetch and store
: C@P    ( addr -- n)    PAGED C@ ;  \ fetch a byte
: C!P    ( n addr -- ) PAGED C! ;    \ store a byte

: @P     ( addr -- n)  PAGED @ ;    \ fetch an int
: !P     ( n addr -- ) PAGED ! ;    \ store an int

Then I replaced the conditional part with a CODE word and this sped things up by 40% or so.

CODE ?MAP ( offset bank# -- )
          TOS BANK# @@ CMP,
          EQ IF,
                 TOS POP,             \ no need to switch

          ELSE, ( *THE MAPPER* )
                TOS BANK# @@ MOV,     \ record the NEW bank#
                         TOS SWPB,    \ bank# needs to be in left byte
                    R12 1E00 LI,      \ cru address of SAMS CARD
                           0 SBO,     \ enable SAMS card
                 TOS 4006 @@ MOV,     \ load the >3000 sams register
                           0 SBZ,     \ disable sams card
                         TOS POP,     \ drop the bank#
          ENDIF,
          TOS PMEM AI,                \ add offset to paged mem block
          NEXT,
          ENDCODE

: PAGED ( addr -- addr') SEG @ 4K UM/MOD ?MAP ;

Further speed improvements need to remove the divide operation with another code word.

Doing it in Forth was the same speed as using UM/MOD.


Edited by TheBF, Sun Jan 6, 2019 10:01 PM.


#232 TheBF OFFLINE  

TheBF

    Dragonstomper

  • Topic Starter
  • 853 posts
  • Location:The Great White North

Posted Mon Jan 7, 2019 11:35 AM

PAGED Memory performance in Forth vs CPU RAM Operations
 
Threaded Forth imposes a 3 to 4 times speed penalty on primitive operations when compared to native code instructions.
Adding paged memory to writing one byte, the worst case, seems to incur another 3 times speed penalty using the code in the previous post. (with ?MAP)
Block memory writes however can run at processor speed and in the best case, filling one entire 4K page, the penalty of switching banks disappears.
(Note: FILL , used in BLANKS, is written in Forth Assembler)
 
Results are below:
 


\ testing read write speeds to SAMS memory
NEEDS DUMP   FROM DSK1.TOOLS
NEEDS ELAPSE FROM DSK1.ELAPSE

HEX 
7FFF CONSTANT 32K
FFFF CONSTANT 64K
1000 CONSTANT 4K

: ERASE    0 FILL ;
: BLANKS  BL FILL ;

\ 64k single byte writes to paged memory
: 64KBYTES    64K 0 DO  I     I C!P    LOOP ; ( 46 secs)

\ 64K single byte writes to single address
: 64KBTEST    64K 0 DO  I  3000 C!    LOOP ; ( 14.9 secs)

\ 32K word writes to paged memory
: 32KWORDS    64K 0 DO  I  I    !P   2 +LOOP ; ( 25.5 secs)

\ 32K word writes to single address
: 32KTEST     64K 0 DO  I  3000  !    2 +LOOP ; ( 9.6 secs)

\ 4K block fill to paged memory 
: 64KBLANKS 64K 0 DO I PAGED 4K BLANKS 4K +LOOP ; ( 1.5 secs) 

\ 4K block fill to CPU memory 
: 64KTEST 64K 0 DO 3000 4K BLANKS 4K +LOOP ; ( 1.5 secs)

EDIT: Be careful what you wish for ;-)

 

All the memory is great but out of curiosity I wondered how long it takes for Forth to erase fifteen 64K segments (983,040 bytes)

: 64KERASE    64K 0 DO I PAGED 4K ERASE   4K +LOOP ;

: ERASEALL    10 1 DO  I SEG !  64KERASE   LOOP ;  ( 20.7 secs)

Wow!


Edited by TheBF, Mon Jan 7, 2019 11:52 AM.


#233 TheBF OFFLINE  

TheBF

    Dragonstomper

  • Topic Starter
  • 853 posts
  • Location:The Great White North

Posted Wed Jan 9, 2019 8:57 PM

After trying SAMS code on real iron and failing, I went back to the Micropendium article by Bruce Harrison whereupon I noticed I did not write an Initialization routine. Duh!

 

In Forth that looked like this:

\ * SAMSINI sets card to "power-up" condition
: SAMSINI
       1E00 CRU! 0SBO    \ turn on card
       0                 \ 1st value
       4000 20           \ register address, #regs
       BOUNDS
       DO
           DUP I !       \ I is reg. address
           0101 +        \ next value
       2 +LOOP
       0SBZ              \ turn off card
       DROP
;

Note: 

Something that I have noticed is that the semantic power of 9900 assembler is actually about the same as Forth.

What this means is that for primitive (low level) routines, Assembler code is smaller than Forth "most" of the time. This is not true for less powerful CPUs.

I have to get a native code Forth compiler running.

 

Here is Bruce's AMSINI routine in Forth Assembler:  ( I simplified it slightly by starting with 0 in R1 and changing the instruction order in the loop)

CODE SAMSINI
       R12 1E00 LI, 0 SBO, \ turn on Sams card
       R1 CLR,
       R0 4000 LI,        \ start of memory
       BEGIN,
          R1 R0 *+ MOV,   \ move 2 bytes to mem-mapper
          R1 0101  AI,    \ add 1 page
          R0 4020 CI,     \ all done?
       EQ UNTIL,          \ no, init more
       0 SBZ,             \ turn off SAMS card
       NEXT,              \ return
       ENDCODE

I took the step of creating Bruce's article as a PDF file and adding the Forth code and example for anyone who is interested.

 

 

Attached Files



#234 Willsy OFFLINE  

Willsy

    River Patroller

  • 3,100 posts
  • Location:Uzbekistan (no, really!)

Posted Thu Jan 10, 2019 3:40 PM

You're probably too far down the track now, but some of the SAMS code in TF may be help/interest.
 
Check out http://turboforth.ne...-04-Memory.html (the word >MAP i.e "to mapper")
 

; ; >MAP ( bank address -- )
; If a SAMS card is present, maps memory bank "bank" to address "address"
_sams   mov r12,r11                 ; save address of NEXT
        mov *stack+,r1              ; get address
        andi r1,>f000               ; set to 4k boundary
        srl r1,11                   ; divide by 2048
        ai r1,>4000                 ; convert to SAMS register address
        mov *stack+,r2              ; get bank
        andi r2,>ff                 ; mask off any crap
        mov r2,r0                   ; keep a copy
        sla r2,8                    ; move to high byte
        xor r0,r2                   ; combine r0 & r2. Hi & lo bytes are now identical
        li r12,>1e00                ; cru address of SAMS
        sbo 0                       ; enable SAMS registers
        mov r2,*r1                  ; poke sams register
        sbz 0                       ; disable sams registers
        mov r11,r12                 ; restore address of NEXT
        b @retB0                    ; return to caller
 
Also, the SAMS initialisation code in http://turboforth.ne...Initialise.html
 
; initialise SAMS card if fitted
        li r12,>1e00                ; sams CRU base
        sbo 0                       ; enable access to mapper registers
        sbz 1                       ; disable mapping while we set it up
        li r0,>4004                 ; register for >2000
        li r1,>f8f8                 ; map bank >f8 into >2000
        mov r1,*r0+                 ; do it
        li r1,>f9f9                 ; map bank >f9...
        mov r1,*r0+                 ; ...into >3000
    ; now set up the banks for high memory...
        li r0,>4014                 ; register address
        li r1,>fafa                 ; register value
        li r2,6                     ; loop count
sams    mov r1,*r0+                 ; write to the register
        ai r1,>0101                 ; next register value
        dec r2                      ; finished?
        jne sams                    ; loop if not
        sbo 1                       ; enable mapping
        sbz 0                       ; lock the mapper registers

 



#235 TheBF OFFLINE  

TheBF

    Dragonstomper

  • Topic Starter
  • 853 posts
  • Location:The Great White North

Posted Thu Jan 10, 2019 4:22 PM

Thanks Mark,

 

I looked at your "bible" when I first started trying to figure this out.  :-)  It is always invaluable and such tidy code.  

I have tested my Forth version which uses a single page at >3000 and it seems to work OK.  I also opted to put my block buffers there since I use >2000 upwards as a tiny heap stack for screen scrolls and temp strings.

 

I did up the assembler version and the subbed in the machine code so I don't need the assembler when running on real iron. 

It is about 60..70% faster than the Forth version when accessing byte or word at a time.  I tried re-coding the DIV operation as binary masks etc.

but the number of instructions that it took meant it was over 140 cycles versus 204 or so for the DIV version, so I just optimized the DIV version. :-)

 

The code version looks like this now.

\ SAMS CARD support. 64K segmented memory fetch and store

\ NEEDS DUMP  FROM DSK1.TOOLS  \ debugging only
HERE

HEX
     VARIABLE BANK#      \ current mapped bank
1000 CONSTANT 4K         \ bytes per bank = 4K
3000 CONSTANT PMEM       \ paged memory block location
     VARIABLE SEG        \ holds current 64K segment

\ safely set the 64K segment that you want to use
: SEGMENT ( 1..F -- ) \ don't allow segment 0
          DUP 01 10 WITHIN 0= ABORT" BAD segment selected"
          SEG ! ;  
1 SEGMENT
\ using machine code so we don't need the CRU library
CODE SAMS-OFF  ( --)  \ disable mapped memory
          020C , 1E00 , \ R12 1E00 LI,
          1E01 ,        \ 1 SBZ,
          NEXT,
          ENDCODE

CODE SAMS-ON ( -- )   \ enable mapped memory
          020C , 1E00 , \ R12 1E00 LI,
          1D01 ,        \ 1 SBO,
          NEXT,
          ENDCODE

\ * AMSINI sets ams card to "power-up" condition
CODE SAMSINI
       020C , 1E00 , \ R12 1E00 LI,
       1D00 ,        \ 0 SBO,       ( turn on Sams card )
       04C1 ,        \ R1 CLR,
       0200 , 4000 , \ R0 4000 LI,  ( start of memory)
                     \ BEGIN,
       CC01 ,        \ R1 R0 *+ MOV, ( move to mem-mapper)
       0221 , 0101 , \ R1 0101  AI, ( add 1 page)
       0280 , 4020 , \ R0 4020 CI,  ( all done? )
       16FA ,        \ EQ UNTIL,    ( no, init more)
       1E00 ,        \ 0 SBZ,       ( turn off SAMS card)
       NEXT,         \ return
       ENDCODE

CODE >BANK  ( addr -- offset bank# )
          0200 , 4K ,    \ R0  4K LI,      \ 4K divisor ->R0     14
          C144 ,         \ TOS R5 MOV,     \ address to r5       18
          C120 , SEG ,   \ SEG @@ TOS MOV, \ segment to TOS      22
          3D00 ,         \ R0 TOS DIV,     \ unsigned division  124
          0646 , C585 ,  \ R5 PUSH,        \                     28
          NEXT,          \ 16 BYTES                             204
          ENDCODE

CODE ?MAP ( offset bank# -- )
          8804 , CD90 ,  \ TOS BANK# @@ CMP,
          1602 ,         \ EQ IF,
          C136 ,         \     TOS POP,
          100A ,         \ ELSE, ( *THE MAPPER* )
          C804 , CD90 ,  \    TOS BANK# @@ MOV,
          06C4 ,         \    TOS SWPB,
          020C , 1E00 ,  \    R12 1E00 LI,
          1D00 ,         \    0 SBO,
          C804 , 4006 ,  \    TOS 4006 @@ MOV,
          1E00 ,         \    0 SBZ,
          C136 ,         \    TOS POP,
                         \ ENDIF,
          0224 , PMEM ,  \ TOS PMEM AI,
          NEXT,
          ENDCODE

 : PAGED  ( addr -- addr') >BANK ?MAP ;

\ paged memory fetch and store
: C@P    ( addr -- n)    PAGED C@ ;   \ fetch a byte
: C!P    ( n 32addr -- ) PAGED C! ;   \ store a byte
: @P     ( 32addr -- n)  PAGED @ ;    \ fetch an int
: !P     ( n 32addr -- ) PAGED ! ;    \ store an int

SAMSINI

CR HERE SWAP - DECIMAL . .( bytes) HEX


Edited by TheBF, Thu Jan 10, 2019 4:49 PM.


#236 Lee Stewart OFFLINE  

Lee Stewart

    River Patroller

  • 3,870 posts
  • Location:Silver Run, Maryland

Posted Thu Jan 10, 2019 6:03 PM

I was going to show you how I did SAMS for fbForth 2.0 when I had a chance to grab a few minutes, but I am happy to see that Willsy beat me to it, seeing as how I got the code from him in the first place!  :P

 

...lee



#237 TheBF OFFLINE  

TheBF

    Dragonstomper

  • Topic Starter
  • 853 posts
  • Location:The Great White North

Posted Thu Jan 10, 2019 7:54 PM

I was going to show you how I did SAMS for fbForth 2.0 when I had a chance to grab a few minutes, but I am happy to see that Willsy beat me to it, seeing as how I got the code from him in the first place!  :P

 

...lee

 

We are a supportive lot aren't we? 

(When I looked at the BLOCK code in HsForth I could see that he "reviewed" Fig-Forth or perhaps MVP Forth and changed it up a little so getting external inspiration is a noble tradition) :)

 

My objective was a lot simpler than Willsy's code. I just wanted one window into the SAMS memory rather than the general solution of allowing mapping of anything to anywhere.

 

Topic Shift

While reviewing my own sacred texts from the '80s I found this little "line editor" that I had put in a block for times when I just wanted to make a fast change.

I added to it here to make it a little more functional and so in 446 bytes I got a little editor that works surprisingly well. 

It can PUT a line in a block,  Delete a line, Copy a line and Move a line. It can advance to the next block or previous block. 

It was also very simple to deal with the 40 column screen and allow seeing the Right or Left side of the block using the R and L commands

Combined with direct to VDP RAM writes it's also pretty fast.

 

And it salvages "EVALUATE" to do a line by line LOAD.  The video shows it in action. It am shocked how useful it is albeit not fancy.

 

Spoiler

Attached Files


Edited by TheBF, Thu Jan 10, 2019 8:09 PM.






Also tagged with one or more of these keywords: Camel99, Forth, Concatentive Programming, ANS Forth

0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users