Jump to content
TheBF

Camel99 Forth Information goes here

Recommended Posts

I promised @HOME AUTOMATION that I would deliver something for him to play with the BRICKS demo.

The attached Zip file has CAMEL267  executable and CAM267SC executable for Supercart so it works on Classic99 out of the box.

 

To start the system from E/A cart option 5

 

DSK1.CAMEL267      runs the normal version that loads at >A000

DSJ1.CAM267SC      runs an experimental version that loads into SuperCart RAM at >6000

 

There are 3 folders which are FIAD disks.

  1. DSK1.ITC  Has library files that programs INCLUDE to extend the Forth systems functionality
  2. DSK2.ITC  Has ED9940 editor which sits on top of Forth so there is a little room left for you to test some Forth inside ED9940 (about 3K )
  3. DSK3.ITC  Has demo programs that show how to do things with this darn thing. 

For the brick demo:

There is DSK3.BRICKS which shows how to create a binary program  BRICKEXE that you run E/A 5.

  • Inside Forth type INCLUDE DSK3.BRICKS.
  • Type BYE. 
  • Select Option 5 and type DSK3.BRICKSEXE.

 

There is also DSK3.BRICKSBEEP  which you INCLUDE from inside Forth and type GO to make it ... go. :)

 

To restart the system and reload the font at any time type COLD   ( as in COLD boot)

This is all from my work-in-progress folder. It seems pretty stable for me, but I know where are the demons are. :)

 

------------

Baring My Soul

In this zip file I have also zipped up the source code for the system and the compiler that can re-build it.

I don't really expect anybody to be interested in re-building it but the source code is Forth written in Forth so that's fun to look at.

I don't have a manual for the cross-compiler finished but there is a readme.txt file with instructions and of course I live right here if you need help.

The source code is really to help anybody using the system to better understand how the innards actually work.

 

2.67 Differences from 2.66

  • Completely knew VDP driver. Up to 20% faster on some things 
  • S"   
    • Action is ANS standard for compiling strings into definitions
    • BUT it is non-standard because it is an state smart word. Does different things for compiling and interpreting
    • It now accepts multiple strings in interpreting mode as long they are input on the same line. 
      • Example:   S"  String #1"  S" String#2 "  S" String #3"  <ENTER>
      •                 returns three separate stack strings to the data stack. 
  • Some library files have been made smaller
    • ANSFILES, GRAFIX
  • Slightly faster number printing.
  • Super cart executable program CAM267SC  
  • One source file can build either conventional program or supercart program using compiler switch called CARTRIDGE in CAMEL267.HSF 

By the way the .HSF files are my extension for Harvard Softworks Forth, a DOS Forth from 1989 to 199?  by Jim Kalihan.  I only spoke with him on the phone but he was a great programmer.  HsForth was made to create HUGE model DOS programs and so the system is built into different DOS segments. Hellish complicated but revolutionary at the time. We lost Jim when he was only 61 a few years ago.  RIP.

 

 

CAMEL99.267.zip

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

I was modifying my little snake game and I came across this code:

: MOVE-SNAKE-TAIL
    0 LENGTH @
    DO
       I ]SNAKE-X DUP @ SWAP CELL+ !
       I ]SNAKE-Y DUP @ SWAP CELL+ !
   -1 +LOOP ;

The core of the game came from a website that had Forth running on a browser window.  I severely modified it to work on TI-99.

The original author was new to Forth and so used a conventional way of moving the contents of a couple of arrays by putting them in a loop.

In fact I think I had improved this already by not doing this:

 I ]SNAKE-X @   I 1+ ]SNAKE-X !

I is a calculation and the array is a calculation.  Experienced Forth coders get the address once and DUP it and work with address.

This is because Forth is not an optimizing compiler. It just does what you say. You are the optimizer. :) 

 

So this is better:

I ]SNAKE-X DUP @ SWAP CELL+ !

But this is way better

: MOVE-SNAKE-TAIL
\      src addr    dest addr      size
\     ---------    --------- -------------
    SNAKE-X-HEAD  DUP CELL+  LENGTH @ CELLS  CMOVE>
    SNAKE-Y-HEAD  DUP CELL+  LENGTH @ CELLS  CMOVE>
 ;

CMOVE> is a CODE word that moves bytes starting and the end of a block of memory and moves the end towards the front.

This way if you are copying a block of memory to an address that overlaps the destination you don't erase all that good data.

 

Truth be told I never found a need for this word and I had to debug the first version I wrote but this one works and it makes the snake much faster.

CODE CMOVE>  ( src dst n -- )  \ move chars from end of string and go backwards
            *SP+ R2 MOV,    \ pop DEST into R2
            *SP+ R1 MOV,    \ pop source into R1
             TOS W  MOV,    \ dup n
             W DEC,         \ compute n-1
             W R1 ADD,      \ point to end of source
             W R2 ADD,      \ point to end of destination
             BEGIN,
                TOS DEC,    \ decr the counter in TOS (R5)
             OC WHILE,      \ carry=true until tos goes from 0 to -1
                 *R1 *R2  MOVB,
                  R1 DEC,   \ move the pointers back 
                  R2 DEC,
             REPEAT,
             TOS POP,
             NEXT,
             ENDCODE

 

You can paste this game in the spoiler into Classic99 with Camel99 Forth V2.67 running.

I finally gave the snake a moveable head. :)   You control it in the code with commands  LEFT FACE, RIGHT FACE, etc.

 

Spoiler
\ snake  a simple game in Forth ported to CAMEL99 Forth
\ DERIVED FROM: https://skilldrick.github.io/easyforth/#snake
\ revised to use CAMEL99/TI-99 features
CR .(   \\\\\\ Version 5.1 \\\\\\\\ )
\   \\  snake sounds and mouse squeak  \\\\\

INCLUDE DSK1.RANDOM
INCLUDE DSK1.GRAFIX
INCLUDE DSK1.CASE
INCLUDE DSK1.ARRAYS

MARKER /SNAKE   \ remove snake, keep library code

CR .( compiling Snake...)
\ =======================================
\ We use direct control of the sound chip
\ rather than sound lists and a player.

\ noise control words
: NOISE   ( n -- ) E0 OR SND! ; \ n selects the noise type

\ noise envelope control
: NOISE-DB   ( db --) F MIN F0 OR SND! ;
: NOISE-OFF  ( -- )   F NOISE-DB ;

HEX
: NOISE-UP ( speed  -- )
          2  F  DO  I NOISE-DB  DUP MS   -1 +LOOP DROP ;

: NOISE-DOWN ( speed -- )
          F  2  DO  I NOISE-DB  DUP MS    LOOP DROP NOISE-OFF ;

\ channel 1 sound control words
DECIMAL
: f(clk) ( -- d) 46324 1  ;   \ this is 111,860 as 32 bit int.

\ >FCODE re-arranges freq. value nibbles (4bits) for the TMS9919
HEX
: >FCODE   ( 0abc -- 0cab)    \ ASM would make this much faster
          DUP 0F AND SWAP      ( -- 000c 0abc)
          4 RSHIFT             ( -- 000c 00ab)
          SWAP ><  ( SWPB)     ( -- 00ab 0c00)
          + ;

: HZ>CODE  ( freq -- fcode )  f(clk) ROT UM/MOD NIP >FCODE 8000 OR  ;

\ *TRICKY STUFF*
\ Calculating the 9919 freq. code takes too long BUT we can convert frequency
\ to 9919 chip code at compile time then compile as 16 bit literal number
\ using this text MACRO
: [HZ] ( freq -- fcode ) S" HZ>CODE ] LITERAL" EVALUATE ;

\ sound channel #1 control words
: FREQ!    ( fcode -- ) SPLIT SND! SND! ;
: ]HZ      ( freq -- ) [HZ] POSTPONE FREQ! ;      \ pre-compiled fcode version
: HZ       ( freq -- )  HZ>CODE SPLIT SND! SND! ; \ runtime calculation version
: DB       ( n -- )    90 OR SND! ;
: MUTE     ( -- )      9F SND! ;

DECIMAL
500 CONSTANT MAXLENGTH

\ x/y coordinate storage for the snake
MAXLENGTH ARRAY ]SNAKE-X
MAXLENGTH ARRAY ]SNAKE-Y

: SNAKE-X-HEAD  ( -- addr)  [ 0 ]SNAKE-X ] LITERAL ;
: SNAKE-Y-HEAD  ( -- addr)  [ 0 ]SNAKE-Y ] LITERAL ;

.( .)
VARIABLE SPEED
VARIABLE PREY-X
VARIABLE PREY-Y
VARIABLE DIRECTION
VARIABLE LENGTH

\ characters used
DECIMAL
136 CONSTANT SHEAD  \ use different color set
42  CONSTANT SNAKE
128 CONSTANT PREY
30  CONSTANT BRICK
BL  CONSTANT WHITE

\ Direction #s
0 CONSTANT LEFT
1 CONSTANT UP
2 CONSTANT RIGHT
3 CONSTANT DOWN

HEX
CREATE HEADLEFT  0C16 , 37FF , FF37 , 160C ,
CREATE HEADUP    1818 , 3C7E , 99FF , 7E3C ,
CREATE HEADRIGHT 3068 , ECFF , FFEC , 6830 ,
CREATE HEADDOWN  3C7E , FF99 , 7E3C , 1818 ,

\ array of head patterns
CREATE HEADS ( n -- addr ) HEADLEFT , HEADUP , HEADRIGHT , HEADDOWN ,

\ set head pattern n to snake's head
: ]HEADPATTERN ( n -- addr) CELLS HEADS + @  SHEAD CHARDEF ;

: FACE  ( direction# -- ) DUP ]HEADPATTERN   DIRECTION ! ;

\ shape data for PREY, brick, mouse and snake chars
HEX
CREATE CLAY   007E , 6A56 , 6A56 , 7E00 ,
CREATE VIPER  3C5E , EBF7 , EBDD , 7E3C ,
CREATE MOUSE  0004 , 3E7B , 7FFC , 8270 ,
CREATE MOUSE2 0008 , 3F7B , 7EFC , 8270 ,  \ mouse looking up
CREATE JUMPMS 84BE , FB7F , 3C42 , 0000 ,

DECIMAL
\ get random x or y position within playable area
: RANDOM-X ( -- n ) C/[email protected]  2-  RND 1+ ;
: RANDOM-Y ( -- n ) L/SCR 2-  RND 1+ ;

\ text macros make drawing faster.
: DRAW-WHITE ( x y -- ) S" >VPOS  BL   SWAP VC! " EVALUATE ; IMMEDIATE
: DRAW-SNAKE ( X Y -- ) S" >VPOS SNAKE SWAP VC! " EVALUATE ; IMMEDIATE
: DRAW-HEAD  ( x y -- ) S" >VPOS SHEAD SWAP VC! " EVALUATE ; IMMEDIATE

: DRAW-PREY ( -- ) PREY  PREY-X @ PREY-Y @ >VPOS VC!  ;
.( .)
: DRAW-WALLS
     0  0 BRICK 31 HCHAR
     0  1 BRICK 22 VCHAR
    31  0 BRICK 24 VCHAR
     0 23 BRICK 31 HCHAR ;

: DRAW-SNAKE
    SNAKE-X-HEAD @  SNAKE-Y-HEAD @ DRAW-HEAD
    LENGTH @ 1
    DO
       I ]SNAKE-X @   I ]SNAKE-Y @ DRAW-SNAKE
    LOOP
    LENGTH @ DUP ]SNAKE-X @  SWAP ]SNAKE-Y @  DRAW-WHITE ;

: INITIALIZE-SNAKE
     4 DUP LENGTH !
     1+ 0
     DO
        12 I - I ]SNAKE-X !
        12 I     ]SNAKE-Y !
     LOOP
     RIGHT FACE  ;

: PLACE-PREY ( y x -- ) PREY-X ! PREY-Y ! ;

: MOVE-SNAKE-HEAD ( n -- )
    DIRECTION @
    CASE
      LEFT  OF  -1 SNAKE-X-HEAD +!  ENDOF
      UP    OF  -1 SNAKE-Y-HEAD +!  ENDOF
      RIGHT OF   1 SNAKE-X-HEAD +!  ENDOF
      DOWN  OF   1 SNAKE-Y-HEAD +!  ENDOF
    ENDCASE ;

\ move each segment of the snake forward by one
DECIMAL
\ : MOVE-SNAKE-TAIL
\    0 LENGTH @
\    DO
\       I ]SNAKE-X DUP @ SWAP CELL+ !
\       I ]SNAKE-Y DUP @ SWAP CELL+ !
\    -1 +LOOP ;

: MOVE-SNAKE-TAIL
\      src addr    dest addr      size
\     ---------    --------- -------------
    SNAKE-X-HEAD  DUP CELL+  LENGTH @ CELLS  CMOVE>
    SNAKE-Y-HEAD  DUP CELL+  LENGTH @ CELLS  CMOVE>
 ;

: MOVE-SNAKE  (  -- )
             MOUSE2 PREY CHARDEF
             4 NOISE  8 NOISE-DB   \ soft white noise
             MOVE-SNAKE-TAIL
             10 NOISE-DB            \ ramp down noise
             MOVE-SNAKE-HEAD
             12 NOISE-DB            \ ramp down noise
             MOUSE PREY CHARDEF
             NOISE-OFF ;
.( .)
DECIMAL
: HORIZONTAL? ( -- ?) DIRECTION @ DUP  LEFT = SWAP RIGHT = OR ;
: VERTICAL?   ( -- ?) DIRECTION @ DUP    UP = SWAP  DOWN = OR ;

: TURN-UP        HORIZONTAL? IF UP    FACE  THEN ;
: TURN-LEFT      VERTICAL?   IF LEFT  FACE  THEN ;
: TURN-DOWN      HORIZONTAL? IF DOWN  FACE  THEN ;
: TURN-RIGHT     VERTICAL?   IF RIGHT FACE  THEN ;

( EXIT THEN gets out of the case statement faster than ENDOF)
: CHANGE-DIRECTION ( key -- )
     CASE
       [CHAR] S OF TURN-LEFT  EXIT THEN \ ENDOF
       [CHAR] E OF TURN-UP    EXIT THEN \ ENDOF
       [CHAR] D OF TURN-RIGHT EXIT THEN \ ENDOF
       [CHAR] X OF TURN-DOWN  EXIT THEN \ ENDOF
    ENDCASE
;

DECIMAL
: SWOOSH   ( -- )
           NOISE-OFF
           5 NOISE
           8 NOISE-UP
           20 NOISE-DOWN ;

: NEW-PREY
    SWOOSH
    PREY-X @ PREY-Y @ DRAW-WHITE
    RANDOM-Y RANDOM-X PLACE-PREY
    DRAW-PREY ;

: GROW-SNAKE  ( -- ) 1 LENGTH +! ;

: DEAD-SNAKE  ( -- )
             NOISE-OFF
             SNAKE SET#  DUP 11 1 COLOR  250 MS   2 1 COLOR
             SHEAD SET#  2 1 COLOR  ;

: HAPPY-SNAKE ( -- )
             [ SNAKE SET# ] LITERAL
             17 3
             DO
                DUP I 1 COLOR
                I 100 * HZ 0 DB
                40 MS
            LOOP
            MUTE
            ( -- 5)  13 1 COLOR ;
.( .)
DECIMAL
: DECAY       ( n -- ) 16 0 DO  I DB  DUP MS LOOP DROP ;

: SQUEAK      ( -- )
              NOISE-OFF
             [ 3800 ]HZ 0 DB  45 MS  \ pre-computed freq. is faster
               6 DB  25 MS
             [ 3500 ]HZ 75 MS
               8 DB 25 MS
             [ 1300 ]HZ
               11 DB 25 MS
             [ 800 ]HZ
              MUTE ;
DECIMAL
: SCARED-PREY ( -- )
             JUMPMS PREY CHARDEF
             SQUEAK
            [ PREY SET# ] LITERAL  DUP  9 1 COLOR
             2 1 COLOR
             MOUSE PREY CHARDEF ;

: FASTER    ( -- ) SPEED @ 5 -  1 MAX SPEED ! ;

: CHECK-PREY  ( -- )
    SNAKE-X-HEAD @ PREY-X @ =
    SNAKE-Y-HEAD @ PREY-Y @ =  AND
    IF
       SCARED-PREY
       HAPPY-SNAKE
       GROW-SNAKE
       FASTER
       NEW-PREY
    THEN ;

: COLLISION? ( -- ? )
    SNAKE-X-HEAD @ SNAKE-Y-HEAD @ >VPOS [email protected]
    DUP  BRICK =
    SWAP SNAKE = OR  ;

\ utility words for menus
: WAIT-KEY   BEGIN KEY? UNTIL ;
: AT"        POSTPONE AT-XY  POSTPONE ." ;  IMMEDIATE

: INITIALIZE
    PAGE
    4 SCREEN
    MOUSE PREY  CHARDEF
    PREY SET#    2 1 COLOR
    CLAY  BRICK CHARDEF
    BRICK SET#   9 1 COLOR
    VIPER SNAKE CHARDEF
    SNAKE SET#  13 1 COLOR
    SHEAD SET#  7 1 COLOR
    DRAW-WALLS
    INITIALIZE-SNAKE
    RANDOM-Y RANDOM-X PLACE-PREY
;

.( .)
HEX
: PLAY ( -- )
      BEGIN
         SPEED @ MS
         DRAW-SNAKE
         DRAW-PREY
         83C8 OFF   \ set continuous key reading
         KEY?  CHANGE-DIRECTION
         MOVE-SNAKE
         CHECK-PREY
         COLLISION?
      UNTIL
      0C SCREEN
      HONK
      0B 5 AT" GAME OVER"
      HONK
      DEAD-SNAKE ;

: SETLEVEL     ( n --) SPEED ! ;
DECIMAL
: MENU
     PAGE
     5 5 AT" Select Start Level"
     5 8 AT" 1 - SNAIL"
     5 9 AT" 2 - WORM"
    5 10 AT" 3 - SNAKE
    5 11 AT" 4 - VIPER"
    3 23 AT" (It goes faster as you win) "

   5 13 AT-XY
     BEGIN KEY
         CASE
          [CHAR] 1 OF 150 SETLEVEL EXIT  THEN
          [CHAR] 2 OF 110 SETLEVEL EXIT  THEN
          [CHAR] 3 OF  75 SETLEVEL EXIT  THEN
          [CHAR] 4 OF  50 SETLEVEL EXIT  THEN
           HONK
         ENDCASE
      AGAIN ;

DECIMAL
: TITLE  ( -- )
      GRAPHICS
      5  5 AT" THE SNAKE"
      5  7 AT" Use the E,S,D,X keys"
      5  8 AT" to move the snake
      5  9 AT" and catch the mouse."
      5 12 AT" The more he eats,
      5 13 AT" the faster he goes!"
      5 20 AT" Press any key to begin..."
      WAIT-KEY ;
.( .)
: RUN ( -- )
     TITLE
     MENU
     BEGIN
        INITIALIZE
        PLAY
        500 MS KEY? DROP ( wait for key release)
        2 13 AT" Your snake was " LENGTH @ . ." Ft. long"
        2 15 AT" Press ENTER to play again"
        KEY 13 <>
     UNTIL
     NOISE-OFF
     8 20 AT" SSSSSee you later!"
     1500 MS
     GRAPHICS ;

RUN

 

 

 

 

 

 

  • Like 2

Share this post


Link to post
Share on other sites
45 minutes ago, TheBF said:

CMOVE> is a CODE word that moves bytes starting and the end of a block of memory and moves the end towards the front.

This way if you are copying a block of memory to an address that overlaps the destination you don't erase all that good data.

 

I know you know this, but neophytes may be misled by the above comment. The proper direction of copy depends on the nature of the overlap. If the destination block starts at a higher address (as in this case), CMOVE> is safe when they overlap. However, if the destination block starts at a lower address, CMOVE> will clobber the source block along the overlap. For that situation, CMOVE is needed to safely copy overlapping blocks in the forward direction (low to high). 😊

 

...lee

  • Like 2

Share this post


Link to post
Share on other sites

Quite right.  Forth 94 actually proposed the word MOVE which uses a test first to determine if CMOVE or CMOVE> should be used. It was in the Camel Forth but I removed it because the decision was written in Forth and so was little slowdown and it took space that I felt I could better used.

 

: MOVE    ( src dst n -- )
          >R  2DUP SWAP DUP [email protected] +
          WITHIN
          IF    R> CMOVE>
          ELSE  R> CMOVE
          THEN ;

 

From what I can understand  Forth 2012 has done away with this MOVE in favour of MOVE being a native cell size data mover.

I think that's was what MOVE originally did in the old days in PolyForth.

"Plus ca change..."

  • Like 1

Share this post


Link to post
Share on other sites

Must’ve been that way with figForth’s MOVE as well because TI Forth and fbForth use the unconditional, forward MOVE to copy cells.

 

For what it’s worth, TI Forth and fbForth depend on the overlap-destructive behavior of CMOVE in the definition of FILL to copy a single byte to a block of memory.

 

...lee

  • Like 1

Share this post


Link to post
Share on other sites
2 hours ago, Lee Stewart said:

Must’ve been that way with figForth’s MOVE as well because TI Forth and fbForth use the unconditional, forward MOVE to copy cells.

 

For what it’s worth, TI Forth and fbForth depend on the overlap-destructive behavior of CMOVE in the definition of FILL to copy a single byte to a block of memory.

 

...lee

That's a clever space saver!.  I will have to look into that. Thanks

Share this post


Link to post
Share on other sites
4 minutes ago, TheBF said:

That's a clever space saver!.  I will have to look into that. Thanks

 

I don’t need much encouragement! |:)

: FILL   ( addr count byte --- )
                \ S:addr count byte
    SWAP >R     \ S:addr byte R:count
    OVER        \ S:addr byte addr R:count
    C!          \ S:addr R:count
    DUP 1+      \ S:addr addr+1 R:count
    R>          \ S:addr addr+1 count
    1-          \ S:addr addr+1 count-1
    CMOVE  ;

...lee

  • Haha 1

Share this post


Link to post
Share on other sites

You're the best!

 

That adds 16 bytes in Forth.  I am wondering if I could be smaller in ALC with that OVER and DUP and >R R> being replaced by register or stack references?

 

I am going to try putting fill above CMOVE and doing a JMP into it.

This means I could do the same thing with VFILL. :)   

 

 

  • Like 1

Share this post


Link to post
Share on other sites
On 4/26/2021 at 12:49 AM, TheBF said:

I promised @HOME AUTOMATION that I would deliver something for him to play with the BRICKS demo.

The attached Zip file has CAMEL267  executable and CAM267SC executable for Supercart so it works on Classic99 out of the box.

 

To start the system from E/A cart option 5

 

DSK1.CAMEL267      runs the normal version that loads at >A000

DSJ1.CAM267SC      runs an experimental version that loads into SuperCart RAM at >6000

A simple question: what is the SuperCart? Just wondering if it is something I can build into the StrangeCart... If it is paged RAM, should be simple :) 

Share this post


Link to post
Share on other sites
3 minutes ago, speccery said:

A simple question: what is the SuperCart? Just wondering if it is something I can build into the StrangeCart... If it is paged RAM, should be simple :) 

It is just RAM at >6000.  In mine I just stick 8K.  The card sold by @arcadeshopper has place for switches to map in 4 x 8K blocks.

 

forgot to say in the Editor assembler cart.

  • Like 1

Share this post


Link to post
Share on other sites
A simple question: what is the SuperCart? Just wondering if it is something I can build into the StrangeCart... If it is paged RAM, should be simple  
http://www.mainbyte.com/ti99/supercart/supercart_4bank.html

http://www.mainbyte.com/ti99/supercart/supercart.html

I sell a pcb that does the 4 bank module designed by Jim Fetzner that makes it a lot simpler.

Sent from my LM-V600 using Tapatalk

  • Like 1

Share this post


Link to post
Share on other sites

Unbelievably Simple CO-Routines in any Forth

(that I can think of)

 

I was reading Reddit/Forth this morning and saw this post about work by Albert VanDerHorst's.

https://home.hccnet.nl/a.w.m.van.der.../forthlectures.html

 

How's this for a simple multi-threader for Forth:

(Uses common data and return stack for all threads)

 

ANS/ISO Forth version:

: YIELD 2R> SWAP 2>R ;    \ that's it! that's all it takes.  

 

 

And to prove how versatile it is, here is an example for FbForth:

Explanation: 

  • COUNTER is a thread that just increments X in a loop
  • CONSUMER reads X and prints it until break key is pressed
  • This is cooperative multi-tasking without the expense of separate workspaces and separate stacks
  • Ideal for games that need to baby-sit something while the game is being played without extra overhead


Note: YIELD could be coded in ALC as a simple  RSWAP and be 10X faster for almost no overhead to switch threads :)

 

: YIELD R> R> SWAP >R >R ;    \ that's it! that's all it takes.  

0 VARIABLE X
 
: COUNTER ( n --)
      X ! 
      BEGIN   
        1 X +!   
        YIELD              \ yield control to the consumer  
      AGAIN ;  
 
: CONSUMER   
      CR   
      0 COUNTER            \ init & start the counter thread
      BEGIN   
         X @  .
         YIELD             \ yield control to the COUNTER  
         ?TERMINAL
      UNTIL 
     ." DONE! " 
      R> DROP ;  

 

fbforth-co-routine.png

  • Like 1

Share this post


Link to post
Share on other sites
4 hours ago, TheBF said:

You're the best!

 

That adds 16 bytes in Forth.  I am wondering if I could be smaller in ALC with that OVER and DUP and >R R> being replaced by register or stack references?

 

I am going to try putting fill above CMOVE and doing a JMP into it.

This means I could do the same thing with VFILL. :)   

 

CMOVE will make VFILL very slow by setting the VDP Write Address every byte (not even sure you could use the same code). Rather, you should set the starting VRAM address and copy the byte to VRAM in a loop to take advantage of the VDP’s autoincrementing of VRAM.

 

...lee

Share this post


Link to post
Share on other sites
1 hour ago, TheBF said:

Unbelievably Simple CO-Routines in any Forth

(that I can think of)

 

I was reading Reddit/Forth this morning and saw this post about work by Albert VanDerHorst's.

https://home.hccnet.nl/a.w.m.van.der.../forthlectures.html

 

How's this for a simple multi-threader for Forth:

(Uses common data and return stack for all threads)

    . . .

 

What happens if the return stack is used for data in one of the threads?

 

...lee

Share this post


Link to post
Share on other sites

 

34 minutes ago, Lee Stewart said:

 

CMOVE will make VFILL very slow by setting the VDP Write Address every byte (not even sure you could use the same code). Rather, you should set the starting VRAM address and copy the byte to VRAM in a loop to take advantage of the VDP’s autoincrementing of VRAM.

 

...lee

Yes and  I looked into my code and saw that FILL is only 12 bytes long due to our favourite CPU's nice instruction set. 

So I canned that idea.

Share this post


Link to post
Share on other sites
32 minutes ago, Lee Stewart said:

 

What happens if the return stack is used for data in one of the threads?

 

...lee

Caveat emptor  I  guess.  :) 

 

Patient: "Doctor it hurts when I do this"

Doctor: " Well then don't do that!" 

 

  • Haha 1

Share this post


Link to post
Share on other sites
55 minutes ago, Lee Stewart said:

 

What happens if the return stack is used for data in one of the threads?

 

...lee

Somewhere in 1G of files I have here I have a multi-tasker that uses separate stacks and that would be compatible with FbForth.

I have to find that.

Share this post


Link to post
Share on other sites

Maybe You Should Sit Down  

:)

 

This will come as shock but after 4 years I have a first version of a Glossary for the kernel of Camel99 Forth. It's 13 pages even in a condensed form that I am saw in MeCrisp Forth by Mathias Koch.

It does answer many questions for anybody interested.  It's probably good that I waited this long since I was really exploring a dozen different options for many aspects of the system until this time.

 

Edit: Found some words that I missed. I need automation for this job.

CamelForth Glossary.docx - Google Docs.pdf

  • Like 3

Share this post


Link to post
Share on other sites

Bug fix to CAMEL267 kernel file

 

While testing a multi-tasking demo program I found a bug in 2.67.   I forgot to access the number printing buffer in a multi-task friendly way (as a user variable)

This ZIP file has replacement of just the kernel file V2.67B.  Please replace  the CAMEL267 file in your installations.

 

The super cart version is not repaired. It is not happy with the changes yet. 

 

 

Here is the demo program that blows up in 2.67 and will run in 2.67B

\ ONETASK.FTH  simple counter DEMO   Apr 20 2021  Brian fox
\ Puts a running counter on top corner of your screen

NEEDS FORK  FROM DSK1.MTASK99
NEEDS U.R   FROM DSK1.UDOTR

\ make a memory space for our task
CREATE TASK1   USIZE ALLOT \ USIZE is the size of one "user area"

\ FORK copies Forth's entire workspace into TASK1's workspace
TASK1 FORK

VARIABLE X   \ A simple global variable

DECIMAL      \ make the compiler use decimal arithmetic

: COUNTER1 ( -- )    \ A background job for the computer
       DECIMAL       \ make this job use decimal math
       200 TPAD !    \ give this task a PAD 200 bytes above Forth's PAD
       BEGIN
          X 1+!      \ increment x

          33 0 AT-XY \ put THIS task's cursor a column 33, row 0
                     \ each task has its own VROW and VCOL variables

          X @ 6 U.R  \ fetch X and print right justified, 6 columns

          100 MS     \ Wait 100 milli-seconds.
                     \ MS gives time to other tasks while it waits
       AGAIN ;       \ jobs must always loop or do SLEEP PAUSE

' COUNTER1 TASK1 ASSIGN   \ assign COUNTER1 to TASK1
CR
CR .( =====================)
CR .( Demo Instructions:)
CR
CR .( Type  MULTI  to enable tasking)
CR .( Type  TASK1 WAKE )
CR .( counter appears upper left)
CR .( X OFF resets variable)
CR .( TASK1 SLEEP)
CR .( counter stops)
CR .( SINGLE  disables all tasking)
CR

 

CAMEL267B.zip

  • Like 2

Share this post


Link to post
Share on other sites

So here is a crazy thing I was working on last week because of the amount of work it takes to make documents.

 

The MPE company in UK has a product called DOCGEN. It's really fancy and can turn literate programming files into HTML documents.

I don't want to go that far yet but it would be nice to be able to feed all my library files to a thing like that and have it produce some useful text files as a starting point for a Glossary.  I have hundreds of files and probably 700+ words to document. :( 

 

So how would you do that?  Normally if would require quite a bit of string processing and parsing but I think DOCGEN does it something like this.

  1. Make a Forth wordlist that only has the words you want to be sent to the doc file
  2. Redefine those Forth words to actually do the work of generating the output that you require
  3. Change the compiler so that it only understands the keywords and EXECUTEs those words  but ignores all the rest of the text in the file.
  4. Process the file :) 

 

I have the beginnings of this working and I have successfully processed an entire disk of 127 files by reading the catalog into an array.

I just need to get the part done that outputs a text file. I want the text output section to be a useful tool in its one right so it's taking more time.

This is all happening on the TI-99 using Classic99.

 

Example:

The word VARIABLE is re-defined to simply parse the text after the word VARIABLE and type that name followed by some text. 

: VARIABLE  PARSE&TYPE   S" variable " TYPE ;

Same is true for any other keyword you want to capture.

 

DOCGEN also uses some magic codes that are buried in comments to control the output like this: (It has about 20 codes for formatting etc)

I will only use two code for now until I get a better idea of what I can accomplish.

 

Control codes embedded in comments:

\ *G       generate a document header and output the text that follows as the first line
\ **       continuation of the text for more explanations.

And the bracket comments ( -- )  in Forth, I will use to simply print out all the stack diagrams directly from the source code. :) That's a big saving alone.

It does mean to get full benefit I have to go into the files and add the magic codes in some comments.

 

 

Here are some pieces of the code.  The word DOCGEN is below is the MAPACTION.  It reads one string of text and processes it.

 

MAPFILE is a higher order function that reads a file and passes each line to MAPACTION. 

 

If you pass something that is not a DV80 file to MAPACTION it just exits on the OPEN-FILE error. 

This way I can feed any disk catalog to it and it ignores program files and other formats.

I am excited to get this working. ;) 

 

: PARSE&TYPE     CR  BL PARSE-WORD   TYPE SPACE ;

VOCABULARY KEYWORDS
ONLY FORTH ALSO KEYWORDS DEFINITIONS

\ docgen tokens
 CHAR G CHAR * FUSE CONSTANT '*G'   \ "*G" as 2 chars for faster parsing
 CHAR * CHAR * FUSE CONSTANT '**'

\ change the meaning of special Forth words so they output information
: VARIABLE  PARSE&TYPE   S" variable " TYPE ;
: CREATE    PARSE&TYPE   S" create " TYPE ;
: CONSTANT  PARSE&TYPE   S" CONSTANT " TYPE ;
: USER      PARSE&TYPE   S" USER "  TYPE ;
: ARRAY     PARSE&TYPE   S" integer array " TYPE ;
: CARRAY    PARSE&TYPE   S" char array " TYPE ;
: VALUE     PARSE&TYPE   S" value " TYPE ;
: CODE      PARSE&TYPE   S" CODE word " TYPE ;
: :         PARSE&TYPE   S" colon def. " TYPE ;

\ The line comment becomes an interpreter so that normal comments are ignored
\ But tokens are read that can be interpreted

\ *G is a document header.
\ ** is a document line
: \    1 PARSE  ( -- addr len) \ read the entire line
      OVER @
      CASE
        '*G' OF CR ." >"  2 /STRING TYPE  ENDOF
        '**' OF CR ."  "  2 /STRING TYPE   ENDOF
                2DROP  ( default is just a comment)
      ENDCASE
;
: (         ." ( "  [CHAR] ) PARSE TYPE ." ) " ; \ print stack diagram

FORTH


ONLY FORTH DEFINITIONS
\ keyword interpret only executes KEYWORDS. Does nothing for anything else
: DOCGEN ( addr len -- )
         ONLY KEYWORDS
         'SOURCE 2!  >IN OFF
          BEGIN
              BL WORD DUP [email protected] ( -- Caddr len)
          WHILE
              FIND ( -- XT ?)
              IF ( it's a MAGIC word)
                  EXECUTE
              ELSE
                  DROP
              THEN
          REPEAT
          DROP
          ONLY FORTH
;

DEFER MAPACTION
' DOCGEN IS MAPACTION

82 STRING: READBUFF

: REFILL ( -- addr len)
      READBUFF DUP 80 INFILE READ-LINE ?FILERR DROP ;

\ use: START  S" DSK1.MYFILE" ' ANALYZE MAPFILE

: MAPFILE  ( addr len --)
    DV80 R/O OPEN-FILE IF EXIT THEN  TO INFILE
    BEGIN
      REFILL ( -- addr len ) MAPACTION
      #LINES 1+!
      INFILE EOF
    UNTIL
    DROP
    INFILE CLOSE-FILE
    S" *** end of file ***" TYPELN
    CR
;

 

 

  • Like 3

Share this post


Link to post
Share on other sites

As I mentioned I needed a way to re-direct text to an output file in order to make automatic documents.

So here is something that lets me do that.  It's starting to feel like a real programming system now when I can do things like this.

 

It's not really efficient and I have not created a way to only send to the file but it will let me take a run an making a DOCGEN utility

and get some kind of Glossary for my libraries to my thousands of waiting Forth fans. 🤣

 

Here is an example of what I can now do.

\ outfile.fth  test code 

S" DSK6.TESTFILE"  MAKE-OUTPUT ( create a new file )

S" This is record #1" TYPE CR
S" and this is #2" TYPE CR
S" * end of first use. " TYPE CR
CLOSE-OUTPUT

S" DSK6.TESTFILE" OPEN-OUTPUT ( Append an existing file )

S" This is 2nd time we used the file" TYPE CR
S" I hope this works correctly."  TYPE CR
994A U. CR
DEAD U. CR
BEEF U. CR
-99  .  CR
S" End of 2nd use of output file"  TYPE CR

And here is the file that the code generated:

This is record #1
and this is #2
* end of first use. 
This is 2nd time we used the file
I hope this works correctly.
994A 
DEAD 
BEEF 
-99 
End of 2nd use of output file

 

A couple of interesting things at least for me.

  1. The file handle OUTH becomes the flag for whether or not we echo to file
  2. The CR does double duty in that if an output file is opened it executes a FLUSH-BUFFER and writes to disk, clears the VDP counter and clears the VDP record
  3. I use the PAB buffer in VDP RAM directly as the output buffer for the program text
  4. The PAB byte counter is used like a variable to be a pointer where text can be appended to the buffer
  5. The PAB record size in VDP RAM is used for error detection just like I would use a Forth variable 

As chars are put into the buffer with TYPE or EMIT the PAB byte counter is incremented.

The byte counter and the PAB file-buffer field are added to make the VDP address where new text goes into the buffer

If the byte counter exceeds 80 bytes we abort with a message.

 

This method of manipulating VDP RAM directly saves Forth memory space since we have all the routines to read/write bytes, integers and strings for VDP RAM.

And using the internal byte counter in VDP RAM means I don't need another variable to keep track of this.

The speed penalty is not that much compared to the Forth overhead already present so why not?

 

I have also decided to add a new non-standard word to my ANS File code to support the TI-99.

The ANS Forth standard file access modes are:

  • R/O   (read only,, TI-99 INPUT)
  • R/W  (read/write,  TI-99 UPDATE)
  • W/O  (write only,   TI-99 OUTPUT) 

I will add this one to the next release:

  • W/A (write append,  TI-99 APPEND) 

One step closer to a big 'ole glossary.  :)

 

Edit:  1. I am thinking this opens a door to make file pipes of some kind... 

         2. Updated spoiler

 

 

Spoiler
\ OUTFILE.FTH   echo screen output to text file      May 2021  Brian Fox

\ Method: Write data into pab FILE buffer, keeping track of char count
\ Use the char count in the pab as Pointer into the Pab when we write
\ so data is written to [PAB FBUFF] [email protected] [PAB CHARS] [email protected] +
\ Only write to disk when CR is encountered.
\ No control characters allowed. Use spaces for DV80 files.

NEEDS WRITE-FILE  FROM DSK1.ANSFILES
NEEDS VALUE       FROM DSK1.VALUES

HEX
0 VALUE OUTH   \ output file handle
: MAKE-OUTPUT ( a u -- ) \ *G creates a new output file
      DV80 W/O CREATE-FILE ?FILERR  TO OUTH ;

: W/A   APPEND FAM @ ;  \ Not standard Forth but needed for TI file sys.

: OPEN-OUTPUT  ( a u -- ) \ open output file in APPEND mode
      OUTH ABORT" Output file is already open"
      DV80 W/A OPEN-FILE ?FILERR  TO OUTH ;

: CLOSE-OUTPUT ( -- )
      OUTH CLOSE-FILE DROP   0 TO OUTH ;

: [PABCHARS]+! ( n -- ) \ bump the file buffer char count by n
      [PAB CHARS] [email protected]  +   DUP [PAB RECLEN] [email protected] > ABORT" Out buffer full"
      [PAB CHARS] VC! ;  \ update the PAB

: OUTBUFF ( -- Vaddr) [PAB FBUFF] [email protected] [PAB CHARS] [email protected] + ;

: WRITELN ( caddr len -- )
       OUTH DUP 0= ABORT" Output file not open"
       SELECT
       TUCK  ( -- len caddr len )  \ get a copy of the length
       OUTBUFF SWAP VWRITE  \ write string to buffer
       ( len) [PABCHARS]+! ;          \ update buffer char count

: FLUSH-BUFFER ( -- )
       3 FILEOP ?FILERR     \ write to disk
       0 [PAB CHARS] VC!    \ reset byte counter
      [PAB FBUFF] [email protected] 80 0 VFILL  \ erase buffer (debugging)
;

\ ==========================================
\ redefine standard output words to echo to file if output handle is active
: CR   ( -- )  CR   OUTH IF FLUSH-BUFFER THEN ;

: EMIT ( c --)
      DUP EMIT
      OUTH IF PAD C!  PAD 1 WRITELN EXIT
      THEN DROP ;

: TYPE  ( a u --)
      2DUP TYPE
      OUTH IF WRITELN EXIT
      THEN 2DROP ;

: SPACE   BL EMIT ;
: SPACES  ( n -- ) 0 MAX  0 ?DO  SPACE LOOP ;

\ number output with echo
: UD.    ( d -- ) <#  #S  #> TYPE SPACE ;
: U.     ( u -- ) 0 UD. ;
: .      ( n -- ) DUP ABS 0 <#  #S ROT SIGN  #> TYPE SPACE ;

 

 

  • Like 4

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

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

×   Your previous content has been restored.   Clear editor

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

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...