Jump to content
IGNORED

fbForth—TI Forth with File-based Block I/O [Post #1 UPDATED: 06/09/2023]


Lee Stewart

Recommended Posts

I have updated post #1 with the latest FBLOCKS file in ZIP format. It now includes the Compact Flash Utilities, CF? , CFMOUNT , CFVOLS discussed a few posts back.

 

I am still working on updating the manual and still hoping to have that done by Fest West time—we shall see. |:)

 

...lee

Link to comment
Share on other sites

The April 28, 2017 update to fbForth 2.0: A File-Based Cartridge Implementation of TI Forth (the manual) has been announced at Fest West 2017 and is posted to post #1 of this thread. It includes all changes since the June 20, 2015 revision. It is up to date as of fbForth 2.0:9 and FBLOCKS 19APR2017.

 

...lee

  • Like 2
Link to comment
Share on other sites

  • 4 weeks later...

I am in the process of getting the manual, fbForth 2.0: A File-Based Cartridge Implementation of TI Forth, in shape to publish as a book that will be available on Amazon.com. My knee surgery (successful and recovering fast!) has slowed this down considerably.

 

While reviewing Chapter 18 “Signed Integer Division”, I noticed several instances of “signed integer division” that should be “symmetric integer division”. I don't think I will post a revised copy of the manual to post #1 until I publish the book. Both symmetric and floored division are signed, so it should be obvious that “signed integer division” should be “symmetric integer division” where it is compared to “floored integer division”.

 

...lee

Link to comment
Share on other sites

  • 2 weeks later...

I noticed at the beginning of this thread that a Forth verson places "NO" in a lot of words of memory. The UCSD p-system places "NO" at a specific memory location when you halt it, to prevent it from starting the next time you warm start your computer.

 

By NO do you mean no-OP instruction?

 

Which Forth system were you looking at?

 

I am wondering if you were looking at threaded code that is just the same numeric value as 'NO'.

There is not inherent reason I can think of for adding NOPs to a Forth system.

 

BF

Link to comment
Share on other sites

By NO do you mean no-OP instruction?

 

Which Forth system were you looking at?

 

I am wondering if you were looking at threaded code that is just the same numeric value as 'NO'.

There is not inherent reason I can think of for adding NOPs to a Forth system.

 

BF

 

Anders' (@apersson850) observation is from TI Forth's definition of MON ("monitor"), the functional equivalent of FCTN+=, which performs a soft system reset of the TI-99/4A. The code fills the lower 8 KiB of expansion RAM with the ASCII code for the capital letters “NO” or 4E4Fh. It effectively fills low RAM with nonsense code. The TI Forth Assembler for MON follows:

 

HEX
CODE MON
0 4E4F LI,
1 2000 LI,
BEGIN,
0 1 *?+ MOV,
1 4000 CI,
EQ UNTIL,
0 @() BLWP,

 

[EDIT]
Note that the code above does not end with NEXT, (as it normally should to return to the Forth interpreter) because the last instruction never returns to Forth.
In the interest of clarity for those who are unfamiliar with TI Forth, the equivalent fbForth code is
HEX
ASM: MON
R0 4E4F LI,
R1 2000 LI,
BEGIN,
R0 *R1+ MOV,
R1 4000 CI,
EQ UNTIL,
0 @() BLWP,
The above has no terminating ;ASM for the reason already cited.
The actual fbForth code for MON only clears the ISR hook before branching to the console's startup code (also, no terminating ;ASM ):
ASM: MON
83C4 CLR,
0 @() BLWP,
[/EDIT]

...lee

Link to comment
Share on other sites

Yes, that's what I referred to. I associated this with a similar behavior in the UCSD p-system. Normally, when you start up the TI 99/4A, and have an active p-code card in the machine, the p-code card will never return control to the machine after executing it's power up routine. That's why they gave the p-code card CRU base address >1F00, so that it would always be the last one to run.

 

But if you let the p-code card start, then stop it by the Halt instruction, it will load "NO" in a certain memory location in 8 K RAM. If you then reboot the 99/4A, the p-code card will find "NO" at this location and then return from the power up routine, without taking over the console.

 

Back when I used the 99/4A a lot, I knew the correct CALL LOAD(xxxx,78,79) from BASIC to disable the p-system, but I don't remember the address any longer. The p-system only stores "NO" in one place, to prevent a start.

 

This has nothing to do with the opcode for JMP 0, sometimes called NOP.

Link to comment
Share on other sites

  • 1 month later...
Not that I really want to suggest that anyone not use the structured Assembler constructs detailed in Sections 9.6 – 9.8 of the fbForth 2.0 Manual (inherited from the TI Forth Manual), but Chapter 9 includes Forth definitions the TMS 9900 Assembler jump instructions ( JMP, , JEQ, , ... ) and I am considering showing how to use them if someone insists or is just curious.


The defining word for JMP, , JEQ, , ... is


HEX : DOP <BUILDS , DOES> @ SWAP 00FF AND OR , ;


and, JEQ, is defined as follows:


HEX 1300 DOP JEQ,


which expects a word offset of -128 – +127—see the ANDing of the offset with >00FF in the definition of defining word DOP above.


That is straightforward enough, but I would like to suggest a way to use labels and have not yet figured that out


Any ideas?


...lee

Link to comment
Share on other sites

For labels I stole an idea from Win32Forth Assembler. It was an older version. Not sure if it still works this way.

It creates pre-defined labels. It's kinda complicated, but it works with one caveat.

Forward jumps can only go to one label. ie for each forward jump, you must use a separate label.

It could be fixed with a stack but I did not get to it yet.

 

Notes:

1. the ]ASMerr stuff inside IF statements could be replaced with ABORT" .

2. CODE words need to start by erasing the jump-table. (CLR-JMPTABLE word)

3. THERE can be replaced with HERE. THERE is my word for "target" HERE for my cross-compiler

 

As noted in the comments, I also create labels in this assembler which are just constants that record the dictionary pointer.

The labels are used by B BLWP and BL.

 

It could use a re-write so that the labels would work with the jump instructions. That would require a little word and then all

jumps would have to be PREFIX because they would have to parse the label string.

 

Hope this is useful.

 

 

 

\ ========================================================================
\ P R E N A M E D   L O C A L   J U M P  L A B E L S

\ Concept taken from Win32Forth assembler.

\ Using these labels makes it simpler to translate existing TI Assembler
\ code to XASM99 RPN Assembler code.

\ These labels are 2 forms:  a 'refer'  and a 'binder'
\ refer: creates a destination label for a jump
\ binder creates a matching label for the 'refer:' labels.
\ Example code is shown below.

\ *NOTE: These are ONLY for use with JMPOP: instructions ABOVE.
\ ie: "program counter relative addressing mode"

\ for Branching with B, BL, or BLWP create a LABEL with L: <text>

HEX 30 value jmptab-size   \ room for 32 labels

jmptab-size cells BUFFER: REF       \ jump address place holders
jmptab-size cells BUFFER: BIND

: clr-jmptable
        ref  jmptab-size cells erase          \ clear the ref jump table
        bind jmptab-size cells erase ;        \ clear the bind jump table

\ simple arrays for forward references
: ]ref  ( ndx -- adr ) cells ref + ;
: ]bind ( ndx -- adr)  cells bind + ;

: >offset ( n -- byte)        \ convert n to a valid TMS9900 jump offset
       DUP ABS  0FF U< not
       if
          cr ." Jump offset= " decimal 2/  3 .r
             6 ]asmerr
       then
       2/ 1+  0FF and  ;


: ]fwd-calc ( ndx -- byte)
        >r
        I ]bind @  I ]ref @ -  2- >offset
        rdrop ;

: ]bwd-calc ( ndx -- byte)
       >r
       I ]bind @  I ]ref @  -  >offset
       rdrop ;

\ for my own sanity I am using 'I' as a local variable. NON-standard but it works in HSForth

: refer:  ( n -- <label> ) \ create a destination label for Jump instructions
        create  ,                \ record the index #
        does>   @ >R  ( -- c)
                THERE I ]ref !
                I ]bind @ 0 <>  ( there is a bind so this is a backward jump)
                if
                    I ]bwd-calc  ( -- offset)
                else
                    0
                then
                rdrop ;

: binder: ( n --<label> ) \ create a binder label for fwd references
        create  ,         \ record the index #
        does>   @ >R
                THERE 2- I ]bind !  \ store the address where BINDER is first found
                I ]ref @ 0 <>       \ there is a reference in the code so this is a forward jump
                if
                   I ]fwd-calc ( offset) I ]ref @ 1+  ( -- offset addr) TC!
                then
                rdrop ;

\ create some labels (can't ever imagine needing 9 labels in a code word that I write)
1 refer: @@1    1 binder: @@1:
2 refer: @@2    2 binder: @@2:
3 refer: @@3    3 binder: @@3:
4 refer: @@4    4 binder: @@4:
5 refer: @@5    5 binder: @@5:
6 refer: @@6    6 binder: @@6:
7 refer: @@7    7 binder: @@7:
8 refer: @@8    8 binder: @@8:
9 refer: @@9    9 binder: @@9:

\ ===================================================================
\ P R O G R A M C O U N T E R A D D R E S S I N G
\ This is part of the TI assembler so I include it here for completeness
\ It can be handy to provide an endless loop to FREEZE a program to debug
\ Example of endless loop: 0 +$$ jmp,
: +$$ ( n -- offset ) 2- >offset ; \ +$$ returns the value of program counter+n
: $$ ( -- ) 0 +$$ ;

 

 

 

 

Link to comment
Share on other sites

H-m-m-m...Thanks for that, @TheBF, but I really do think it is far better to use the structured Assembler constructs:

 

IF, … THEN,
IF, … ELSE, … THEN,
BEGIN, … UNTIL,
BEGIN, … AGAIN,
BEGIN, … WHILE, … REPEAT,

rather than the branch and jump instructions,

 

B, BL, BLWP, JMP, JNE, JGT, JLT, JLE, JL, JHE, JH, JLT, JGT, JOC, JEQ, JNO, JOP, JNC, JNO, JOP,

 

because Forth words should be short routines and the structured constructs do not require the difficult-to-manage labels as do the branch and jump instructions. Furthermore, the structured constructs are easier to follow.

 

That said, I think I will work out some explanation of the branch and jump instructions because they are, after all, defined and I think it a disservice to Forth programmers to mention them in Chapter 9 without comment of any kind as was done by the TI Forth developers and, consequently, by me in fbForth.

 

...lee

Link to comment
Share on other sites

Ah I see now that I misunderstood your needs. You want to document them, not actually use them.

 

Then I might suggest that you simply show how the offset for a jump instruction is calculated and then show a definition for something like my +$$ word so they could see it in action. (?)

 

Just my 2 cents Canadian ( that's only worth 1.58 cents USD these days) :-)

 

 

B

  • Like 1
Link to comment
Share on other sites

Maybe sections 6 and 7 of the TF Assembler manual might provide inspiration?

 

Looking at it now, some years after I wrote it, I think I could write a better manual. :?

 

Nope. I think I have what's discussed there pretty well covered in the fbForth 2.0 Manual, @Willsy,

 

Well...I should not be so quick to cast aside your suggestion to look for inspiration there. I was actually thinking of doing something for the jump instructions similar to how the structured constructs manage their jumps, but that would not work for the branch instructions.

 

...lee

Link to comment
Share on other sites

In the manual, I have included information on how to use the Forth Assembler jump instructions, which work differently in the Forth Assembler from how they work in the Editor/Assembler version. I discourage their use, but tell you how to use them anyway. Section 9.9 is the new section.

 

Also, note the corrected jump tokens table in Section 9.7.

 

Feel free to make suggestions/corrections in the attached Chapter 9: fbForth_2.0_Manual_Chapter9_20170722.pdf

 

...lee

Link to comment
Share on other sites

I am in the final stages of publishing my manual, fbForth 2.0: A File-Based Cartridge Implementation of TI Forth. It should be available on CreateSpace.com and Amazon.com in 2 – 3 weeks. I will also bring a few copies to the Chicago Faire for interested attendees. A public “Thank you!” to @jedimatt42 for editing assistance and proofreading.

 

...lee

  • Like 2
Link to comment
Share on other sites

  • 3 weeks later...

FYI—I just discovered a bug in FRAC in the resident dictionary! Its use will crash the system. The word is supposed to yield the fractional part of a floating point (FP) number. Back when I brought the FP library into the same Assembly space as the rest of fbForth 2.0, after reducing the number of labels that made it possible, I had to resolve at least one label conflict (I’m sure there were more). Unfortunately, I did not change the reference in FRAC to the new label, so it jumps into the weeds and blows up! I will fix it in the next build. Meanwhile, the following code will work nicely (note Forth’s response is underlined):

 

: FRAC ( f1 --- f2 ) FDUP TRUNC F- ; FRAC isn't unique ok:0

 

...lee

  • Like 1
Link to comment
Share on other sites

Here are updated, high-level Forth versions of the HCHAR and VCHAR words. They are what I used to model the ALC versions for the next build of fbForth 2.0:

 

: HCHAR ( x y cnt ch --- )
>R \ S:x y cnt R:ch
SCRN_END @ SCRN_START @ - \ S:x y cnt scrn_size R:ch
MIN >R \ cnt <= scrn_size S:x y R:ch cnt
SCRN_WIDTH @ * + \ spos = y * scrn_width + x S:spos R:ch cnt
SCRN_START @ + \ S:spos1 R:ch cnt
DUP SCRN_END @ - 1+ \ S:spos1 spos1-scrn_end+1 R:ch cnt
0> 0 ?ERROR \ abort if spos1 not within screen
SCRN_END @ OVER - R - \ S:spos1 scrn_end-spos1-cnt R:ch cnt
0< \ screen wrap? S:spos1 scrn_end-spos1-cnt flag R:ch cnt
IF \ we're wrapping
SCRN_END @ OVER - \ S:spos1 cnt1 R:ch cnt
R> OVER - \ S:spos1 cnt1 cnt2 R:ch
SCRN_START @ SWAP \ S:spos1 cnt1 spos2 cnt2 R:ch
R VFILL \ S:spos1 cnt1 R:ch
R> VFILL
ELSE \ we're not wrapping
R> R> \ S:spos1 cnt ch
VFILL
THEN ;
: VCHAR ( x y cnt ch --- )
SWAP >R >R \ S:x y R:cnt ch
SCRN_WIDTH @ * + \ spos = y*s_wd+x S:spos R:cnt ch
SCRN_END @ SCRN_START @ - 1- \ smax=s_en-s_st-1
OVER OVER \ S:spos smax spos smax R:cnt ch
> 0 ?ERROR \ abort if spos not within screen
R> ROT ROT R> \ S:ch spos smax cnt
0 DO ( cnt 0 DO) \ S:ch spos smax
>R \ S:ch spos R:smax
OVER OVER \ S:ch spos ch spos R:smax
SCRN_START @ + \ vaddr=s_st+spos S:ch spos ch vaddr R:smax
VSBW \ put ch onscreen S:ch spos R:smax
SCRN_WIDTH @ + \ inc spos to next row S:ch spos R:smax
DUP R > \ spos > smax? S:ch spos flag R:smax
IF \ S:ch spos R:smax
R - \ adjust spos back by smax
SCRN_WIDTH @ \ get s_wd S:ch spos s_wd R:smax
OVER = \ spos=s_wd? S:ch spos flag R:smax
IF \ yes S:ch spos R:smax
DROP 0 \ drop spos and make it 0 S:ch spos R:smax
THEN
THEN \ S:ch spos R:smax
R> \ clean up return stack for loop S:ch spos smax
LOOP
DROP DROP DROP ; \ clean up stack before exit

I will post fbForth Assembler and machine code versions in a day or two for those who might want to include them in FBLOCKS for fbForth 2.0:9—or just want to see how they perform.

 

The ALC version of VCHAR inlines the code for VSBW and rivals the speed of the ALC version of HCHAR !

 

...lee

  • Like 2
Link to comment
Share on other sites

Here are the promised ALC versions of VCHAR and HCHAR :

 

 

 

HEX
ASM: VCHAR   ( x y cnt ch -- )
   *SP+ R1 MOV,          \ pop ASCII char code
   R1 SWPB,              \ move to high byte
   *SP+ R2 MOV,          \ pop copy count
   *SP+ R6 MOV,          \ pop y
   *SP+ R0 MOV,          \ pop x
   SCRN_WIDTH @() R5 MOV,  \ get SCRN_WIDTH to a register
   R5 R6 MPY,            \ SCRN_WIDTH * y = row_pos
   R7 R0 A,              \ scrn_pos = row_pos + x
   SCRN_START @() R6 MOV,  \ get SCRN_START to a register
   R6 R0 A,              \ scrn_buf_addr = scrn_pos + SCRN_START
   SCRN_END @() R4 MOV,  \ get SCRN_END to a register
   R4 R7 MOV,            \ VRAM address 1 past end of screen
   R6 R7 S,              \ calculate screen size
   R7 DEC,               \ screen size-1
   R5 R3 MOV,            \ SCRN_WIDTH to R3
   R6 R3 A,              \ VRAM address of (col,row)=(0,1)
\ registers at this point:
\     R0: VRAM screen buffer address (scrn_buf_addr) for next char position
\     R1: char code in high byte = ch
\     R2: copy count = cnt
\     R3: VRAM address of (col,row)=(0,1)
\     R4: SCRN_END
\     R5: SCRN_WIDTH
\     R6: SCRN_START
\     R7: screen size-1
\ Check that we are on screen
   R0 R4 C,              \ VRAM address outside screen?
   GTE IF,           \ error if so
      SP DECT,           \ reserve stack space
      *SP CLR,           \ message #0 to stack
      39B4 @() BL,       \ branch to Forth's ERROR via BLA2F
      ' ERROR CFA ,      \   ...we won't be back
   THEN,
\ Inline VSBW
   0 LIMI,               \ disable interrupts for rest of routine
   BEGIN,  
      R0 4000 ORI,       \ prepare for write
      R0 SWPB,           \ get LSB of VRAM address to MSB
      R0 8C02 @() MOVB,  \ write LSB of VRAM address via VDPWA
      R0 SWPB,           \ get MSB of VRAM address back to MSB
      R0 8C02 @() MOVB,  \ write MSB of VRAM address via VDPWA
      R1 8C00 @() MOVB,  \ write byte to VRAM via VDPWD
      R0 3FFF ANDI,      \ remove extraneous bit
\ Set up for possible repeat
      R5 R0 A,           \ move down one line
      R0 R4 C,           \ gone off end of screen?
      GTE IF,        \ skip if not
         R7 R0 S,        \ reduce address
\ Force row 0
         R0 R3 C,        \ are we at (col,row)=(0,1)?
         EQ IF,      \ no; go on
            R6 R0 MOV,   \ go to SCRN_START
         THEN,
      THEN,
\ More?
      R2 DEC,            \ decrement count
   EQ  UNTIL,            \ repeat if not finished
;ASM
DECIMAL

HEX
ASM: HCHAR   ( x y cnt ch -- )
      *SP+ R0 MOV,            \ pop ASCII char code
      R0 SWPB,                \ left byte for FILL1 (low VRAM fill routine)
      *SP+ R4 MOV,            \ pop cnt to R4 temporarily
      *SP+ R1 MOV,            \ pop y
      SCRN_WIDTH @() R1 MPY,  \ multiply by SCRN_WIDTH
      *SP+ R2 A,              \ pop & add x
      SCRN_START @() R6 MOV,  \ get SCRN_START to a register
      R6 R2 A,                \ calculate actual VRAM start address for FILL1
      SCRN_END @() R5 MOV,    \ get SCRN_END to a register
      R5 R7 MOV,              \ copy SCRN_END to R7
      R6 R7 S,                \ calculate screen size
      R7 R4 C,                \ scrn_size > cnt?
      LTE IF,            \ yes; jump
         R7 R4 MOV,           \ no; cnt = scrn_size
      THEN,
\ registers at this point:
\     R0: char code (ch) in high byte for FILL1
\     R1: probably 0 from MPY
\     R2: VRAM start address for first pass of FILL1
\     R3: 
\     R4: copy count = cnt
\     R5: SCRN_END
\     R6: SCRN_START
\     R7: screen size
   R2 R5 C,              \ VRAM address outside screen?
   GTE IF,           \ error if so
      SP DECT,           \ reserve stack space
      *SP CLR,           \ message #0 to stack
      39B4 @() BL,       \ branch to Forth's ERROR via BLA2F
      ' ERROR CFA ,      \   ...we won't be back
   THEN,
   R2 R5 S,              \ bytes to end of screen
   0 LIMI,               \ disable interrupts because FILL1 doesn't
   R7 R4 C,              \ scrn_size > cnt?
   LTE IF,           \ jump if yes
      R7 R4 MOV,         \ no; adjust cnt
   THEN,
   BEGIN,
      R4 R1 MOV,         \ put cnt in R1 for FILL1
   GT WHILE,
      R5 R4 S,           \ no; do we wrap to SCRN_START?
      GTE IF,        \ no
         R5 R1 MOV,      \ yes, just go to SCRN_END
      THEN,
      31F0 @() BL,       \ do VFILL via FILL1
      R6 R2 MOV,         \ wrap for next round
      R7 R5 MOV,         \ scrn_size to bytes-to-end-of-screen
   REPEAT,
;ASM
DECIMAL

 

 

 

To compile these definitions, you will need to load the TMS9900 Assembler from FBLOCKS. I will post the machine code versions in a bit. The machine code versions will not need the assembler.

 

...lee

  • Like 1
Link to comment
Share on other sites

And—Here are the machine code versions of VCHAR and HCHAR :

HEX
CODE: VCHAR   ( x y cnt ch --- )
   C079 06C1 C0B9 C1B9 C039 C160 36E8 3985 A007 C1A0 36EA A006
   C120 36EC C1C4 61C6 0607 C0C5 A0C6 8100 1105 0649 04D9 06A0
   39B4 6912 0300 0000 0260 4000 06C0 D800 8C02 06C0 D800 8C02
   D801 8C00 0240 3FFF A005 8100 1104 6007 80C0 1601 C006 0602
   16EB
;CODE

CODE: HCHAR   ( x y cnt ch --- )
   C039 06C0 C139 C079 3860 36E8 A0B9 C1A0 36EA A086 C160 36EC
   C1C5 61C6 8107 1501 C107 8142 1105 0649 04D9 06A0 39B4 6912
   6142 0300 0000 8107 1501 C107 C044 1501 1008 6105 1101 C045
   06A0 31F0 C086 C147 10F5
;CODE
DECIMAL 

These were produced with ASM>CODE (see the manual at fbforth.stewkitt.com) and will only work in fbForth 2.0:9. If you have an earlier version of fbForth 2.0, you can produce your own version with ASM>CODE after first loading the TMS9900 Assembler and ASM>CODE from FBLOCKS and compiling the ALC words from the previous post modified with corrected addresses for BLA2F and FILL1. I can supply those addresses if you tell me what version of fbForth 2.0 you have.

 

...lee

  • Like 1
Link to comment
Share on other sites

  • 1 month later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

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...