Jump to content

Photo

Camel99 Forth Information goes here

Camel99 Forth Concatentive Programming ANS Forth

72 replies to this topic

#1 TheBF OFFLINE  

TheBF

    Dragonstomper

  • 557 posts
  • Location:The Great White North

Posted Wed Jan 3, 2018 4:14 PM

I thought it was about time that I create a goto place (no pun intended) for all things Camel99. 

 

So from now on I will put updates here.

 

This update is my first Pong Game. It's a little quirky but it uses sound and sprites with no interrupts or multi-tasking.

It's not that easy to win.  The computer player is very crude but it make enough mistakes so that you can win. :-)

 

Since I never spent much time writing games this has been educational for me.

 

I have added a new simple word to CAMEL99 to create named character patterns.  It's called PATTERN:

"PATTERN:" words return the address (ie: a pointer) to the data that loads into VDP very fast using VWRITE.

 

If you wanted to add PATTERN: to another Forth the code is:

\ PATTERN: lets us define named character patterns

\ usage: 
\  HEX 3C42 A581 A599 423C PATTERN: HAPPY_FACE
\      3C42 A581 99A5 423C PATTERN: SAD_FACE

\ DECIMAL
\ SAD_FACE  159 CHARDEF

: PATTERN: ( u u u u  -- )
           CREATE
           >R >R >R              \ push 3 values so we can reverse order
           ,  R> , R> , R> ,     \ compile 4 ints in VDP useable order
;

The PONG code is in the spoiler.  You have to load CAMEL99 with EA5 option and then paste PONG it into the emulator.

When the codes finishes compiling type RUN.

 

Latest version of CAMEL99 is on GitHub at the URL in the signature.

 

 

Spoiler

Edited by TheBF, Thu Jan 4, 2018 9:25 AM.


#2 Lee Stewart OFFLINE  

Lee Stewart

    River Patroller

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

Posted Thu Jan 4, 2018 4:44 PM

This thread is a good idea!

 

Sorry, I have not been very responsive lately.  First it was getting settled down here in FL, then the holiday season with a week of some of the kids and grandkids, then a week of friends staying with us and now I am a bit under the weather with some bug or other and not much energy.  Of course, the beach and horseshoes got in the way a lot!  I haven’t even updated the fbForth thread or my website in quite a while—needed too large a block of time.  That is my excuse and I am sticking to it.

 

Anyway, I need to download Camel99 Forth to test PONG and then, of course, see about porting it to you-know-what.

 

...lee



#3 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Thu Jan 4, 2018 8:10 PM

I actually think of you when I concoct these programs.

 

This one has one text macro in it that could be replaced for FigForth.

I have grown fond of these things in ANS Forth because they put the low level code into your definitions directly but you can use a high level name.

No COMPILE [COMPILE] POSTPONE confusion.

: [HZ] ( freq -- fcode ) S" HZ>CODE ] LITERAL" EVALUATE ;

\ I think it could be this in FigForth (untested)
: [HZ] ( freq -- fcode ) 
       COMPILE HZ>CODE  
       COMPILE ] 
       [COMPILE] LITERAL ; IMMEDIATE 


#4 Lee Stewart OFFLINE  

Lee Stewart

    River Patroller

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

Posted Fri Jan 5, 2018 12:25 PM

 

I actually think of you when I concoct these programs.

 

This one has one text macro in it that could be replaced for FigForth.

I have grown fond of these things in ANS Forth because they put the low level code into your definitions directly but you can use a high level name.

No COMPILE [COMPILE] POSTPONE confusion.

: [HZ] ( freq -- fcode ) S" HZ>CODE ] LITERAL" EVALUATE ;

\ I think it could be this in FigForth (untested)
: [HZ] ( freq -- fcode ) 
       COMPILE HZ>CODE  
       COMPILE ] 
       [COMPILE] LITERAL ; IMMEDIATE 

 

My head always hurts when I work with COMPILE !  I always have to test that sort of code extensively until it works and I actually understand it.  That figForth code won’t work!  Declaring it immediate will cause LITERAL to execute during the definition of ]HZ , but you want it to execute while defining words that use ]HZ after they have been temporarily set to execution mode by [ .  This definition appears to work:

: [HZ] ( freq -- fcode ) 
       HZ>CODE  
       [COMPILE] ] 
       [COMPILE] LITERAL ; 

I may eventually implement EVALUATE for fbForth, but I would need to define a different string word that would append a couple of nulls and not leave a character count on the stack.  The spoiler below is for those craving an explanation.

Spoiler

 

...lee



#5 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Fri Jan 5, 2018 3:03 PM

 

My head always hurts when I work with COMPILE !  I always have to test that sort of code extensively until it works and I actually understand it.  That figForth code won’t work!  Declaring it immediate will cause LITERAL to execute during the definition of ]HZ , but you want it to execute while defining words that use ]HZ after they have been temporarily set to execution mode by [ .  This definition appears to work:

: [HZ] ( freq -- fcode ) 
       HZ>CODE  
       [COMPILE] ] 
       [COMPILE] LITERAL ; 

I may eventually implement EVALUATE for fbForth, but I would need to define a different string word that would append a couple of nulls and not leave a character count on the stack.  The spoiler below is for those craving an explanation.

Spoiler

 

...lee

 

 

Wow. I learned something. I am so long away from FigForth and I never did know about the ASCII 0 thing.

I think the ANS group did a few good things for Forth.  The interpreter mechanism might be one.

 

I bet you are happy to be in Florida right now.  Up here it was -4 F overnight and it came up above 0 F  in the day time!

Woohoo! Its heat wave.

 

And we got 9..10 " of snow and it's still coming down  :-)

 

But the east coast of the US is way worse than the Detroit/Toronto corridor.



#6 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Sat Jan 13, 2018 2:03 PM

Adding 80 Columns to Camel99 Forth

 

Finally got around to adding this functionality for the F18A.

 

Thanks to Willsy for the register data.

 

You can only toggle between 40 column and 80 column after this code is loaded.

GRAPHICS will just give you a warning.

 

Added the simple word TINT so you can change text and screen color.

(uses machine values for colors to keep it simply)

\ 80 column mode for F18A video card (tested on Classic99)
\ Register DATA taken from Turbo Forth by Mark Wills
( default colors changed to green on black screen)

HEX
CREATE 40COL
       00 C, F0 C, 00 C, 0E C, 01 C, 06 C, 02 C, 20 C,

CREATE 80COL
       04 C, 70 C, 03 C, E8 C, 01 C, 06 C, 00 C, 20 C,
       88 C, 00 C, 00 C, 00 C, 94 C, 10 C, 00 C,

: VREGS  ( addr n -- )  0 DO  DUP I + C@  I VWTR  LOOP DROP ;

DECIMAL
: 80COLS ( -- )   80COL 15 VREGS  80 C/L!  PAGE  80 VMODE ! ;

\ we have to over-write the old versions to handle 80cols
: TEXT    ( -- ) 40COL   8 VREGS  TEXT  ;
: COLD    ( -- ) TEXT  COLD ;
: TINT    ( fg bg -- ) SWAP 4 LSHIFT +  7 VWTR ;

: GRAPHICS ( -- ) -1 ABORT" Needs restart" ;



#7 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Sun Jan 14, 2018 9:11 AM

*New and Improved*  80 Columns to Camel99 Forth

 

It didn't sit well with me that I could not change screen modes freely.

After I played around I found out that if I reset to TEXT mode, I could jump to a 80cols or GRAPHICS 

mode with no problem.  It takes just a few milli-seconds longer but you can jump from 80cols

to GRAPHICS and back. Classic99 does not seem to be able to record the 80COLS screens

so you will have to load this code yourself for proof that it works. 

 

Here is the new code: -EDIT- Added count bytes to the data strings which saves a few bytes in the definitions


\ 80 column mode for F18A video card (tested on Classic99)
\ Register DATA taken from Turbo Forth by Mark Wills
( default colors changed to green on black screen)

HEX
CREATE 40COL
       08 C, 00 C, F0 C, 00 C, 0E C, 01 C, 06 C, 02 C, 20 C,

CREATE 80COL
       15 C, 04 C, 70 C, 03 C, E8 C, 01 C, 06 C, 00 C, 
       20 C, 88 C, 00 C, 00 C, 00 C, 94 C, 10 C, 00 C,

: VREGS    ( addr n -- ) 0 DO  DUP I + C@  I VWTR  LOOP DROP ;

DECIMAL
\ we have to over-write the old versions to handle 80cols
: TEXT     ( -- ) 40COL COUNT VREGS  TEXT  ;
: COLD     ( -- ) TEXT  COLD ;
: TINT     ( fg bg -- ) SWAP 4 LSHIFT +  7 VWTR ;

: 80COLS   ( -- ) TEXT 80COL COUNT VREGS  80 C/L!  PAGE  80 VMODE ! ;
: GRAPHICS ( -- ) TEXT GRAPHICS ;

Edited by TheBF, Thu Jan 25, 2018 10:09 AM.


#8 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Sun Jan 14, 2018 11:00 PM

Adding Elapsed Time Timer

 

When you are trying to find out if one way to code something is faster than another method this little tool is soooo handy.

 

The code uses the fact that the TI-99 is running a countdown timer for the screen timeout at address >83D6.

Each tick of this timer is .008333 seconds so it will run for about 9 minutes. That is the maximum time we can measure with this method.

 

The first time I saw this word ELAPSE was in FPC Forth by Tom Zimmer.  I have moved it to other Forth systems since then.

 

The video shows ELAPSE in action.

\ ELAPSE.FTH  elapsed time measurment words
\ Thanks to Tom Zimmer for the good ideas in FPC circa 1990
\ Ported to HsForth 08MAR91  Brian Fox Canada

\ Ported to CAMEL99 Nov 29 2017
\ Good for 9 minutes maximum duration

\ *** YOU CANNOT CALL KSCAN WHILE TIMING ***

HEX
83D6 CONSTANT TICKER   \ screen timeout counter increments by 2 /16mS

DECIMAL
: ##      ( n -- )  0 <#  # #  #> TYPE ;

: .MINS   ( h S M  -- )
          BASE @ >R   
          DECIMAL  ## [CHAR] : EMIT  ##  [CHAR] . EMIT ##
          R> BASE ! ;

\ 1 TICK = .008333 mS
: REALTIME ( -- n )  TICKER @  5 6  */ ;  \ changed to 5/6 ratio from input by Lee Stewart. Better now

: >TIME  ( n -- .hh secs mins  )  \ convert n to time
          6000 /MOD   ( -- rem mins) >R  \ push minutes
          100  /MOD   ( -- ms secs)      \ calculate seconds & hundredths
          R> ;                           \ pop minutes

: .ELAPSED ( -- ) CR ." Elapsed time ="  REALTIME >TIME .MINS ;

: ELAPSE  ( -- <forth words> )
           1 PARSE   \ BF edit to allow timing a line of code
           TICKER OFF
           EVALUATE
          .ELAPSED ;

Attached Files


Edited by TheBF, Mon Jan 15, 2018 3:38 PM.


#9 Lee Stewart OFFLINE  

Lee Stewart

    River Patroller

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

Posted Mon Jan 15, 2018 10:57 AM

I don’t know if the accuracy of the result is really improved due to truncation, but the following definition of REALTIME uses 0.833... as the multiplier, exactly:

: REALTIME ( -- n )  TICKER @  5 6 */ ; 

...lee



#10 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Mon Jan 15, 2018 3:33 PM

Well done.

 

Yes it does make a difference.  I ran this code on FB-Forth and CAMEL99 and they go out of sync pretty quickly.

: TEST ( start end -- )
  SWAP 
  DO
     CR  I U.  SPACE  I  5 6 */ U.  SPACE  I 83 100 */  U.
  LOOP ;
  

Thanks.  I will fix it in the original post.

 

 

B



#11 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Mon Jan 15, 2018 3:41 PM

I don’t know if the accuracy of the result is really improved due to truncation, but the following definition of REALTIME uses 0.833... as the multiplier, exactly:

: REALTIME ( -- n )  TICKER @  5 6 */ ; 

...lee

 

Looking at it again I saw another bug for ANS Forth only.

: ELAPSE  ( -- <forth words> )
           1 PARSE
           TICKER OFF
           EVALUATE
          .ELAPSED ;

I had used BL PARSE,  which of course parsed a space delimited word in the input.

1 PARSE will never find a delimiter and so will take in an entire line of text.

 

You help me find bugs without even looking at them.

 

You're a wizard.

 

B



#12 Lee Stewart OFFLINE  

Lee Stewart

    River Patroller

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

Posted Mon Jan 15, 2018 3:49 PM

...

You help me find bugs without even looking at them.

 

You're a wizard.

 

B

 

Hah!  :-o

 

...lee



#13 Lee Stewart OFFLINE  

Lee Stewart

    River Patroller

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

Posted Mon Jan 15, 2018 4:02 PM

Here it is ported to fbForth 2.0:

 

Spoiler

 

I had to define OFF and make changes in .MINS and ELAPSE .  Two changes I made in .MINS were gratuitous ( BASE->R and R->BASE ).

 

...lee



#14 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Mon Jan 15, 2018 4:04 PM

Cool!

 

It's much handier than using a stop-watch.



#15 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Fri Jan 26, 2018 9:46 AM

Latest additions to CAMEL99 Forth on GitHub:

 

Just in case anybody cares...

https://github.com/bfox9900/CAMEL99

 

 To /DEMO added:

                 PONG.FTH              simple PONG game
                 QUICKSORT.FTH   recursive quicksort integer demo. (about 2..3X faster than COMBSORT)
                 DUTCHFLAG7.FTH on screen combsort demo with multiple inputs
                 
  To /LIB added:

                 80COL.FTH          for F18A hardware or emulators that support it
                 ASM9900.FTH     TMS9900 RPN assembler that loads above the dictionary
                 ATTYPE.FTH      10X faster type to screen location with error checking
                 BOOLEAN.FTH    to create BIT arrays. Slower but space efficient.
                 DATABYTE.FTH  changed to be more like TI-Assembler syntax
                 DEFER99.FTH     Forth 2012 deferred words and support words
                 FASTCASE.FTH  creates VECTOR tables easily. (Like "ON GOSUB" for BASIC programmers)
                 MARKER.FTH      ANS Forth MARKER word.
                 UDOTR.FTH        print un-signed right justified numbers
                 VALUES.FTH       create ANS Forth VALUE type

                 TINYHEAP.FTH   fixes to ANS Forth ALLOCATE implementation in TI Low-mem


Edited by TheBF, Fri Jan 26, 2018 11:35 AM.


#16 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Sun Jan 28, 2018 12:33 PM

The most minimal memory manager.

 

Many times when you are coding you need some memory to hold temporary data.

ANS Forth provides a rather complex memory manager that is a linked list of memory blocks.

It is really inappropriate for a little machine like the TI-99.

 

I had a simple revelation today to manage a memory block for temp storage for people playing with Forth.

 

I have always had a variable called H that points to the LOW MEMORY in the expansion card.

Here is all I need to do make use of that memory in the simplest way.

\ minimalist HEAP memory manager
VARIABLE H   HEX 2100 H !  \ set H to your heap memory block 

: MALLOC      ( n -- addr ) H @  SWAP H +! ;  \ allocate heap and return pointer
: FREE       ( n -- ) NEGATE H +! ;           \ free heap memory

If you type   64 MALLOC  you get a pointer (address) of a memory block.

 

Then when you are done with it you type  64 FREE and it goes back to the pool of memory.

 

It is VERY simplistic. But many times that is all I need.


Edited by TheBF, Sun Jan 28, 2018 12:33 PM.


#17 Willsy OFFLINE  

Willsy

    River Patroller

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

Posted Tue Jan 30, 2018 9:13 AM

It's nice and simple for perhaps a little bit too simplistic.

 

I'd like to be able to reserve memory areas A, B, C & D (they may or may not be of different sizes). Then, perhaps I want to free memory area B, such that the next MALLOC will use that free area, providing that it is large enough to accommodate the amount of memory being requested.

 

I think I know how to do it, I've just not been very motivated lately when it comes to programming. I'm a bit burnt out ;-)

 

On the plus side, I'm finally getting to grips with Open E major slide-blues on my strat, so things are looking up!  :grin:



#18 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Tue Jan 30, 2018 2:29 PM

It is very simple. It's just managing another memory space the same way as the dictionary.  

Doing a re-sizeable memory manager will mean you need to garbage collect at some point.

Since most Forth projects do static memory allocation this still gives more more than that with

out the complexity . :-)

 

I did it for a screen scroll mechanism where I just wanted a buffer at the beginning of the routine

and then destroy it when I was done.  So I didn't even need to give it a name. 

Just the put the address on the stack, thank you.

 

This routine was only about 12% slower than copying the entire screen to a big buffer with CODE and writing it back.

I was surprised.  The reason I did it was because in 80 column mode that buffer became 1920 bytes! :-)

 

Notes;

I had a change of heart on 'FREE' and changed it to MFREE to avoid conflict with the ANS name.

C/L@ is  fast variable as you call them, created manually. (chars per line)

: SCROLL      ( -- )
              C/L@ MALLOC ( -- heap)          \ allocate heap for 1 line
              L/SCR 1                         \ loops from 1 to 24
              DO
                 PAUSE
                 I  C/L@ * OVER     ( -- VDPaddr heap)
                 2DUP          C/L@  VREAD    \ read line to heap
                 SWAP C/L@ -   C/L@  VWRITE   \ write HEAP to line above
              LOOP
              0 17 AT-XY  VPOS C/L@ BL VFILL  \ place cursr & clear last line
              DROP                            \ drop heap pointer
              C/L@ MFREE ;                    \ de-allocate heap memory

Open tunings are fun. I went through brief periods playing with a couple.

Always ended up back home.

 

This reminds me... my son borrowed my Strat a year ago and I haven't got it back yet. :_(  



#19 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Tue Jan 30, 2018 3:13 PM

Background Sound List Player

 

I finally got around to finishing a sound list player that does not use interrupts. ​I always wondered how the cooperative task system would work with music ​which is hard real-time.  This one seems to work ok.  I am sure you could ​drag it down if you had a lot of unfriendly tasks running, but with the console ​and a little task that incremented a variable continuously it seemed very happy.  Each string of a sound  list is played without interruption so the delays will always be at the end of string which may be why it sounds ok. 

 

​A very nice feature is the sound-list FiFO (1st in /1st out) queue that feeds the player. ​This lets you "Q" up as many as 16 sound lists and they kick them off.

Another nice thing is that as soon as the Q is empty the TASK goes to sleep so it takes very little servicing from the main program (3 ALC instructions)

 

​You can paste this code into CAMEL99 V1.99 located here: 

 

https://github.com/b...tree/master/bin

 I would not trust it to work with older versions of the system.

(As usual I found a bug in an "improvement" I made to the "MS" time delay word and reverted back to a previous version

 Such a humbling activity)

 

I have update the way I make sound lists so they are very close to assembler now.  The only difference is there is no '>' in front of numbers  so you would have to search and replace that character from any other lists you would like to try.  There are also a few other examples  in the SOUNDS folder on GITHUB.

 

Edit: to have more fun I made the Queue 32 cells long. It works better

​You can run this code:

: QALL   MUNCHMAN >Q  PACMAN >Q
         SMACK >Q  SMACK >Q
         SHPSND >Q 
         FIRE >Q FIRE2 >Q EXPLODE >Q
         CHIME >Q  SW1SND >Q    
         SW2SND >Q
         SND123 >Q  SND4 >Q
         FUEL >Q  ;

BGPLAY  \ it plays all the sounds
Spoiler

Edited by TheBF, Tue Jan 30, 2018 3:49 PM.


#20 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Wed Jan 31, 2018 2:22 PM

Background Sound List Player *UPDATE*

 

​While putting this thing through repeated tests I found out that my RESTART command was leaving the address of the program on the return stack.

 

​That created some very "interesting" sounds indeed when the return stack over-flowed!

 

​So RESTART now cleans that up.

 

​You will also see that just because I could,  the MEMORY for TASK1 in the new version was created with MALLOC.  This allocates memory in LOW-MEM.

​I just wanted to see it work but it is very handy in a memory constrained machine to have the extra space for misc blocks of memory.

 

​Also realized that if my Hardware timer has a 1/60 of a second interval it would simplify working with TI-99 stuff that assumes an interrupt happened.

​I called this unit of time a "jiff' as in it happens in a jiffy.

​This also works better with the cooperative tasker because in 1/60 of a second it can service 4 or 5 simple tasks before the hardware timer expires.

​This means that while some task is running the JIFFY timer, the cooperative tasks are still running smoothly.

 

​All these changes and a new Binary version are on GitHub now.

 

https://github.com/bfox9900/CAMEL99


Edited by TheBF, Wed Jan 31, 2018 2:28 PM.


#21 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Thu Feb 1, 2018 1:41 PM

TUTORAL: Sound Lists in VDP RAM

 

So with all this infra-structure created to play sound lists It becomes clear that good sound lists take up memory.

In CAMEL99 Forth running in Expansion RAM, memory is at a premium. But there are kilo-bytes free to use in VDP RAM. So what does it take to play sound lists from VDP memory?

 

The following code makes that happen by new making primitive routines that work in VDP RAM while keeping the same structure for the hi-level routines that do the work.

 

ALL references to C@ (char fetch) are replaced with VC@ ( VDP char fetch)

which fetches bytes from VDP RAM. (these are both ASM words)

Also we create a set of VDP memory manager words that ultimately let us make  VCREATE.

 

VCREATE puts a name in Forth dictionary and is really just a CONSTANT. The CONSTANT returns the VDP  address that is next in line to use when we VCREATE the new name.   Simple.

 

We also replicate the Forth word COUNT as VCOUNT which converts a counted string in memory

(a string with the first byte holding the length of the string ) into a memory address and length

on the Forth data stack.  All this of course now must be done from VDP RAM.

 

We make the appropriate replacements in VPLAY$ and VPLAYLIST and we're done.

The player has a different name because it won't work on sound lists in EXPANSION Ram.

(It's possible to used vectored operations (DEFER) to make the player work from either but that's an exercise for somebody ambitious)

 

Next the code makes a BYTE string compiler and with that completed we can compile sound lists into VDP ram.

 

---

Porting Information:

I made use of EVALUATE for the VBYTE routine which is an ANS Forth thing.

If you port this code to FB-Forth or TURBO Forth you could at minimum just replace the commas in the lists 

with VC,  and let Forth drop each byte into VDP RAM this way:

 

VCREATE SNDLIST1   02 VC, 45 VC, 03 VC, 10 VC, 

 

HEX

: SND!     8400  C! ;  ( you need this little word)     

 

You will also make a little delay word to replace JIFFS. 

 

And FB-Forth VARIABLE  needs a leading number:

 

HEX 1000 VARIABLE VP  

---

 

So if you want to play sound lists and save Expansion memory this will do it.

 

Spoiler

Edited by TheBF, Thu Feb 1, 2018 1:43 PM.


#22 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Tue Feb 13, 2018 10:30 PM

Extending Forth to Make the SAMS Card Easy to use for DATA

 

I read in another thread that some people feel  the SAMS card was a pain in the backside, so I thought I would see what I could do using Forth to try and tame the beast.

 

I translated the >MAP word in TurboForth from ALC to Forth using the CRU words that I re-worked from TI-Forth.

\ CRU words in CAMEL99 Forth
CRU!   sets the CRU base address
SBO    sets the current base address bit 0 to one
SBZ    sets the current base address bit 0 to zero.

With those I could create 'MAP'. Of course the ALC version is much faster but I wanted to replicate it in Forth.

\ Forth translation of >MAP in TurboForth
: MAP  ( bank addr -- ) \ ASM converted to Forth
         F000 AND  0B RSHIFT 4000 + \ Compute SAMS register to use
         SWAP 00FF AND  DUP >< OR   \ Hi & lo bytes are now identical
         SWAP ( -- bank address)
         1E00 CRU! SBO              \ enable SAMS card
         !                          \ store bank in SAMS register
         SBZ ;                      \ disable SAMS card. (CRU not changed)

This is a nice word that Willsy created to let you "map any SAMS block of memory into a 4K space in the expansion RAM.

 

Now reading and writing 1Mbyte of memory means that you need 20bit addresses.  This reminded me of the original IBM PC.

The 8086 CPU was a 16 bit machine but you could access extra memory by reading many 64K segments. When the 8086 working with this extended memory it as called a "long address" and it took 2 16 bit registers to hold a long address.

 

The common way the Forth interacted with that memory system was to create a "long address" fetch operator and store operator. They were called '@L'   and '!L'   respectively.

 

So for a first attempt this is what I intended to do.

 

The first word created was >BANK.  This word takes a 32bit address (2 16bit no.s on the stack) and computes the MAPPED address.  It makes use of a nice "mixed" number function call "unsigned-mixed-slash-mod"   or UM/MOD.

 

(Note: This Forth operation is just the 9900 DIV instruction!  Nice CPU :) )

 

This standard Forth word takes a 32 bit number and divides it by a 16 bit number giving you the dividend and the remainder.

So if we divide a 32 bit address by "bytes-per-bank" (B/BANK) or 4096 (>1000)  we automatically get the bank# to use and the remainder gives us the offset to use in the 4K block.  How easy is that.

 

Example:   HEX  2F0BB  1000 UM/MOD  

Will return:  BB 2F

 

Note: 2F0BB is shown as an example. CAMEL99 Forth must split 32 bit numbers into 2 stack items.

In FB-Forth and many other Forth systems adding a '.' to the end (2F0BB. ) will do this automatically for you.

 

Where 2F is the bank# to use and BB is the OFFSET address in expansion RAM where we mapped the SAMS card.

 

I added an offset to the bank# of >10 (1stbank) so that we don't play with the lowest 64K.  I got strange results with trying to use the lower 64K.

 

Because mapping the block with Forth is a slower than ASM code >BANK also checks to see if we are using the called for bank already. If not, it records the new bank# in the variable BANK# (what else would you call it?) and then does the MAP into >2000 low expansion RAM.

(MBLOCK is  CONSTANT = >2000)

 

If we are already using that bank#  >BANK just drops the number.

Then... it has to OR >2000 with the remainder so that the mapped address is correctly shown inside the 4K memory block that starts at >2000

     VARIABLE BANK#      \ current mapped bank
  10 CONSTANT 1STBANK    \ we don't use the lower 64K
1000 CONSTANT B/BANK     \ bytes per bank = 4K
2000 CONSTANT MBLOCK     \ mapped memory block used is >2000

: >BANK  ( 32bit -- addr)           \ must have 32bit address!!
         B/BANK UM/MOD  1STBANK +   \ -- 'addr bank#+1STBANK
         BANK# @ OVER <>            \ do we need to change banks?
         IF   DUP BANK# !           \ update bank#
              MBLOCK MAP            \ map in the SAMS block

         ELSE DROP                  \ not needed. Drop the bank#

         THEN MBLOCK OR             \ return the address in mapped block
;

So now to read and write SAMS memory from bank >10 to bank >FF (948K bytes) is just this easy.

\ FINAL API:  direct access to 900K memory space
: @L     ( 32addr -- n)  >BANK @ ;    \ fetch an int
: !L     ( n 32addr -- ) >BANK ! ;    \ store an int

: C@L    ( 32addr -- char) >BANK C@ ;   \ fetch a byte
: C!L    ( char 32addr --) >BANK C! ;   \ store a byte

Since the 32bit address is translated into regular memory by >BANK, we read and write integers with '@' and '!'  (fetch and store) just like normal Forth.

And of course we read and write bytes with 'C@' and 'C!'  .

 

Next I will try and create data structures in SAMS memory. 

 

Using the Forth method of extending the language has made using the banked memory pretty simple for basic stuff.

 

>BF


Edited by TheBF, Thu Feb 15, 2018 6:47 AM.


#23 Lee Stewart OFFLINE  

Lee Stewart

    River Patroller

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

Posted Wed Feb 14, 2018 12:24 PM

If I were to port this to fbForth 2.0, I could not use your representation of 32-bit numbers.  Entering “2F0BB” would lop off the ‘2’ because, without help, only 16-bit numbers are accepted.  By “help” I mean an embedded ‘.’ anywhere in the number.  Entering “2F0BB.” would do the trick.  Of course, I could also enter that number as two 16-bit halves:  F0BB 2 (the MSW must be most accessible on the stack).

 

Before I discuss SAMS memory handling in fbForth 2.0, I think I need to revisit my own explanation in my manual under >MAP and also write some words that actually use it to be sure I actually understand it.  I should probably do this before I release fbForth 2.0:11|:)

 

...lee



#24 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Thu Feb 15, 2018 6:29 AM

I mis-lead the reader a little in an attempt to simplify the explanation on the slim chance that an Assembly language programmer looked at this for some ideas. (optimist  ;-) )

 

Yes 2F0BB should have a trailing '.' to be a double in most Forths.   Camel Forth is a minimal system and does not even have support for doubles in the interpreter. That's also on my "to do" list.

 

However the next post will have some tricks to create data structures in SAMS memory and removes the need to manage the 32 bit addresses manually.

 

Standby...



#25 TheBF OFFLINE  

TheBF

    Dragonstomper

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

Posted Thu Feb 15, 2018 12:10 PM

Making DATA Structures in SAMS

 

So managing 32 bit pointers in a 16 bit environment is a pain in the butt.  Do we really have to do it that way?

 

The SAMS memory is mapped into our local 16 bit space so we can reach it with 16 bit operations no problem. In this next example, we manage the entire SAMS space the same way as the Forth compiler manages its dictionary. The difference is we need to use a 32 bit pointer to keep track of what we allocated rather than a 16 bit pointer. The mapper does the rest. 

 

CAMEL99 does not have 32 bit variables but we can make one by combining some primitives operations. (Other Forths  may have the word 2VARIABLE for doubles)

EDIT: correction to D,
: D,     ( d -- )   \ compile a 32bit DOUBLE into dictionary
         SWAP , ,  ;   \ change order and compile each single
          
\ create 32bit variable and initialize to zero
CREATE SAMS    0 0 D,

Next we make a word like HERE in Forth that returns the next available memory space except the new word will track SAMS.  Let's call it SHERE.  All it has to do is fetch the 32bit value in the SAMS variable.

 

And with that we need a way to bump the SAMS variable whenever we want to allocate more SAMS memory. We call that SALLOT.  SALLOT uses a nice word called M+ which can take a 32 bit number and add it to a 16 bit number, giving us a 32bit result. Because we use M+ the maximum size we can allocate at one time is 64K (>FFFF).  

\ == simple memory manager ==
\ return the 32bit pointer to next available SAMS memory location
: SHERE   ( -- d)  SAMS 2@ ;

\ allocate n bytes in the SAMS memory space (max 64K)
: SALLOT  ( n -- ) SHERE ROT M+ SAMS 2! ;

With these simple tools we can use CREATE DOES> to make some data types.  

Here is an integer.

: SAMS-INT: (  -- <text>)
\ compile time action:
           CREATE           \ create a word in dictionary
              SHERE D,      \ compile the 32bit SAMS location into the word
              2 SALLOT      \ allot 2 bytes of SAMS space

\ runtime action:  ( -- addr)
           DOES> 2@ >BANK ; \ fetch the 32bit from myself, convert to a bank address

The cool thing now is that even though the SAMS-INT: is in SAMS memory, we can read and write it like a normal Forth variable.

 

Here is a SAMS buffer.

: SAMS-BUFFER: ( n -- <text>) ( 64K is the largest buffer we can allocate)
\ compile time action:
           CREATE           \ create a word in dictionary
              SHERE D,    \ compile the 32bit SAMS location into the word
              SALLOT        \ allot n bytes of SAMS space

\ runtime action: ( -- addr)
           DOES> 2@ >BANK ; \ fetch the 32bit from myself, convert to a bank address

Have you ever wanted to create a continuous array on the TI-99 that can hold 32K integers?

Well now you can.

: SAMS-ARRAY: ( n -- <text>)
\ compile time action:
           CREATE           \ create a word in dictionary
              SHERE D,      \ compile the 32bit SAMS location into the word
              CELLS SALLOT  \ allot n * 2 bytes of SAMS space (int = 2bytes)

\ runtime action:  ( n -- addr)
           DOES> 2@            \ fetch the 32bit base address from myself
                 ROT CELLS M+  \ rotate the index 'n' to top, multiply by cell size & add to base
                 >BANK ;       \ convert to a bank address

And here is how these data structures could be used.

SAMS-INT: X     99 X !
SAMS-INT: Y    100 Y !
SAMS-INT: Z    101 Z ! 

FFFF SAMS-BUFFER: BIGBUFF    
S" This string is the first thing to go into the large buffer in SAMS memory space" BIGBUFF PLACE 

7FFF SAMS-ARRAY: []BIGARRAY

: FILLARRAY
        7FFF 0
        DO
           I  I []BIGARRAY !
        LOOP ;


There are things we could do to speed this up by using code, but it all works as expected and it is pretty cool to have over 900K of continuous data space on the little TI-99!  If we really needed it, we could use the double number word set to access contiguous memory blocks greater than 64K, but at the moment I still don't have a use for memory this big!

 

>BF 


Edited by TheBF, Thu Feb 15, 2018 2:37 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