Jump to content
IGNORED

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


Lee Stewart

Recommended Posts

With the SAMS I solved the problem of my AI bot being in the next page but looking ahead or behind.

As the SAMS has 4K pages upper and lower I just keep moving the code to the middle like this:

 

1500 IF X>15351 THEN B=B+1 :: X=X-4096 :: GOTO 1600

1510 IF X<8224 THEN B=B-1 :: X=X+4096 :: GOTO 1600

 

1600 CALL AMSBANK(B,B+1) :: GOT0 270

 

 

B is the lower 4K banks of SAMS and B+1 is the upper 4k bank.

X is the address in lower 8K.

IN THE DARK used these lines. How it works is if it is within 32 bytes of the end of lower 8K then swap the two 4K pages up and subtract the address to put it in the middle of lower 8K and going down works just the opposite way.

 

That way a single variable keeps track of the SAMS banks and the other the address.

 

Thanks for your input, Rich; but, I'm not sure how I could use this idea for my problem.

 

...lee

Link to comment
Share on other sites

Thanks for your input, Rich; but, I'm not sure how I could use this idea for my problem.

 

...lee

 

I uses 4 bytes to control the pages and address. Just like you are doing. But in the SAMS not a cart. oh and from XB.

Link to comment
Share on other sites

Does this mean a different definition for BANK that does not expect a number on the stack? I'm not sure I understand how this one would work. :?

 

Yes. It will work the same way as BRANCH and 0BRANCH. If you have a look at those definitions, you should find that their parameters are encoded "in-line", rather than coming from stack.

 

So, rather than:

 

LIT $A0FE BRANCH

 

its compiled as

 

BRANCH $A0FE

 

In practice, the datum is available via the IP (instruction pointer) which is pointing directly at the parameter when BRANCH executes. So, the definition of BRANCH in fbForth is probably nothing more than:

 

A *IP+,IP
B *next

 

Assuming relative addressing, which is the de-facto for most Forth systems. (TF uses absolute addressing, to avoid the overhead of the address calculation (hey, every instruction counts! ;-) )

 

Maybe, I should commit one bank to the dictionary, with every word switching to the bank that holds the remainder of its definition—probably too much bouncing around, huh?

 

That's what TF does, except for a few words (stack and math words in the main) that I wanted to be as fast as possible, and didn't even want the overhead of a bank switch (which is very fast).

 

It would be worth sitting down and writing a little program (in fbForth, of course) to walk the dictionary and calculate its size (the size of the dictionary entries only, including name fields, flags, pointer fields etc.). This should be fairly simple to compute. Do this after you have loaded all the words that you want to include in ROM, and don't forget to subtract the overhead of your dictionary-size-calculation program!

 

It needs to fit within 8192 bytes if you want it all in one bank.

 

You will need add, per dictionary entry, some overhead for the actual code that makes the call into the bank1.

 

For example, in TF, it works like this:

 

<link field to previous link field>

<length and flags field>

'name'

data $+2 \ pointer to assembly code

BL @BANK1 \ call trampoline routine for bank 1

DATA <address> \ address in bank 1 to branch to

 

The return from a routine in bank1, I simply have a B @retB0

 

retB0 is in pad (as is BANK1). It also does a NEXT which saves a LOT of code-space.

 

Here are BANK1 and retB0, taken from the TF source code:

 

;BANK1
; routine to perform a bank switch and branch
bank1 equ exit+6				 ; address of this routine in PAD
 mov *r11,r11			 ; get branch address
 clr @>6000				 ; select bank 1
 b *r11					 ; branch to the desired address

;RETB0
; routine to return to a calling routine in bank 0
retB0 equ bank1+8				 ; address of this routine in PAD
cpypnt clr @>6002				 ; select bank 0
 b *next

 

Both of these routines run from PAD. That's the concept that allows it all to hang together!

 

HTH

Edited by Willsy
Link to comment
Share on other sites

That is why I love the SAMS.

Put the code you are running in a 4K bank to control all the banks and swap out the other banks 4K at a time or go whole hog and do 28K at a time till you get to 1 Meg.

The SAMS is so freaking flexible with swaps, you can even duplicate a bank by putting the same memory at two locations at once and when you write to one it writes to the other at the same time.

 

Beats the hell out of the 8K only same place swapping pain in the butt that the carts are stuck with.

Link to comment
Share on other sites

That's true Rich, but Lee is looking to put fbForth on a cartridge so cart banking is a problem that isn't going to go away, even with the SAMS board. I do agree with you though; the SAMS board is a truly brilliant bit of kit and very under-appreciated IMHO. It's very good that most emulators include SAMS emulation.

Edited by Willsy
Link to comment
Share on other sites

Thanks for all this, Mark. It definitely helps.

 

... So, the definition of BRANCH in fbForth is probably nothing more than:

 

A *IP+,IP
B *next

Actually, it's

 

A *IP,IP

B *NEXT

 

... It needs to fit within 8192 bytes if you want it all in one bank.

 

This is gonna be difficult!

 

You will need to add, per dictionary entry, some overhead for the actual code that makes the call into the bank1.

 

There's so much code that uses high-level definitions that it will be very time-consuming (and error prone) converting to ALC. I may look very seriously at the multi-bank dictionary possibility. I could try to keep the words that reference each other in the same bank; but, there're always going to be references that will have to be resolved in other banks. I need cleverness! Ingenuity! :idea: :roll:

 

...lee

Link to comment
Share on other sites

  • 3 weeks later...

OK...Back to the immediate problem, viz., changing block I/O to high-level file I/O (level 3) and how to deal with where the system disk/directory might be if it is not "DSK1". Does E/A Load Option 3 or Option 5 allow anything other than DSKx.filename or DSK.diskname.filename or is that totally dependent on DSRs that are found? If the latter, does E/A, at least, have a size limit for the pathname or does it care?

 

...lee

Link to comment
Share on other sites

The code for EA Cart EA input is just like XB except the buffer is much larger.

60 characters is easy but it can handle much larger pathnames then that I would only have to change this line.

 

[0444] E523 06,E5,57 GETALL CALL  BLNKBU
[0445]			   *  Build PAB with name
[0446] E526 06,E5,85 BLDPAB CALL  VZERO
[0447] E529 BD,E0,02			    DST   @PABPTR,V@>0002(@PABPTR)
	   E52C 1C,1C
[0448] E52E A3,E0,02			    DADD  >0080,V@>0002(@PABPTR)
	   E531 1C,00,80
[0449] E534 BE,E0,08			    ST    >00,V@>0008(@PABPTR)
   E537 1C,00
[0450] E539 BF,E0,04			    DST   >5000,V@>0004(@PABPTR)
	   E53C 1C,50,00
[0451] E53F 87,AF,22			    DCLR  V@>2250
   E542 50
[0452] E543 06,E5,91			    CALL  GETINP
[0453] E546 8F,50					 DCZ   @FAC6
[0454] E548 65,56					 BS    GE3CF
[0455] E54A 34,50,E0 PABNAM MOVE  @FAC6,V*FAC4,V@>000A(@PABPTR)
	   E54D 0A,1C,B0
	   E550 4E
[0456] E551 BC,E0,09				 ST    @FAC7,V@>0009(@PABPTR)
	   E554 1C,51
[0457] E556 00	   GE3CF		 RTN
[0458] E557 A3,1C,00 BLNKBU DADD  >0080,@PABPTR
	   E55A 80
[0459] E55B BE,B0,1C			    ST    >20,V*PABPTR
	   E55E 20
[0460] E55F 35,00,4F				 MOVE  >004F,V*PABPTR,V@>0001(@PABPTR)
	   E562 E0,01,1C
	   E565 B0,1C
[0461] E567 A7,1C,00			    DSUB  >0080,@PABPTR
	   E56A 80
[0462] E56B 00						   RTN
[0463] E56C BE,B0,1C CLOSE   ST    >01,V*PABPTR
	   E56F 01
[0464] E570 BD,56,1C DOIO	  DST   @PABPTR,@VPAB
[0465] E573 A3,56,00			    DADD  >0009,@VPAB
	   E576 09
[0466] E577 06,00,10			    CALL  DSRLNK
[0467] E57A 08						 BYTE  >08
[0468] E57B 6B,EF				    BS    CHKERR
[0469] E57D DA,E0,01			 CLOG  >E0,V@>0001(@PABPTR)
	   E580 1C,E0
[0470] E582 4B,EF				   BR    CHKERR
[0471] E584 00					    RTN
[0472] E585 86,B0,1C VZERO  CLR   V*PABPTR
[0473] E588 35,00,45			   MOVE  >0045,V*PABPTR,V@>0001(@PABPTR)
	   E58B E0,01,1C
	   E58E B0,1C
[0474] E590 00					    RTN
[0475] E591 06,E5,C4 GETINP CALL  GETKEY
[0476] E594 BD,20,24			 DST   @STLN,@CURADD
[0477] E597 BE,00,3C			 ST    >3C,@>8300
[0478] E59A 87,50				  DCLR  @FAC6
[0479] E59C D6,B0,20 GE415  CEQ   SPACE,V*CURADD
   E59F 20
[0480] E5A0 45,B2				 BR    GE42B
[0481] E5A2 91,20				 DINC  @CURADD
[0482] E5A4 92,00				 DEC   @>8300
[0483] E5A6 45,9C			    BR    GE415
[0484] E5A8 BD,20,24		  DST   @STLN,@CURADD
[0485] E5AB DA,48,04		 CLOG  >04,@FLAG
[0486] E5AE 45,C3			   BR    GE43C
[0487] E5B0 45,91			   BR    GETINP
[0488] E5B2 BD,4E,20 GE42B  DST   @CURADD,@FAC4
[0489] E5B5 D6,B0,20 GE42E  CEQ   SPACE,V*CURADD
	   E5B8 20
[0490] E5B9 65,C3			  BS    GE43C
[0491] E5BB 91,50			  DINC  @FAC6
[0492] E5BD 91,20			  DINC  @CURADD
[0493] E5BF 92,00			  DEC   @>8300
[0494] E5C1 45,B5			  BR    GE42E
[0495] E5C3 00	   GE43C  RTN

 

Look at line 477 >E597 ST >3C,@>8300 the >3C is 60 for the length of the line typed in by the USER in the EA Cart.

So you could type a line:

 

SCS.VOLUMENAME.SUBDIRNM.NXTSUBDIR.LNXTSUBDIR.ONEMOREDIR.FILE

 

That is a 60 character line in the EA Cart. I did not modify the line length in the EA cart as it was long enough for me to have multiple subdirectories on my SCSI.

 

Rich

  • Like 2
Link to comment
Share on other sites

Nice one Rich. :thumbsup:

 

Can you tell us where in memory the buffer is? With some care, Lee may be able to load fbForth (or as much of it as is needed to get to the next loading stage) such that the area of memory that holds the filename as entered by the user is still accessible. He can then use that path/filename to look for the blocks file, or (in the event of it not being found) fall back to a different location method (i.e. if not found at the path entered by the user, then try DSK.FBFORTH.BLOCKS etc.) before finally giving up.

 

Edit: Just had a quick look using the Classic99 debugger. After typing in a filename on the Option 3 menu, I can't see it anywhere in CPU RAM (8K, 24K or PAD), and, apart from the screen image table, it's not in VDP either. I'm forced to conclude that E/A thus reads the filename directly from the screen.

 

To be honest, I'd be surprised if that is actually what is happening...??

Edited by Willsy
Link to comment
Share on other sites

Nice one Rich. :thumbsup:

 

Can you tell us where in memory the buffer is? With some care, Lee may be able to load fbForth (or as much of it as is needed to get to the next loading stage) such that the area of memory that holds the filename as entered by the user is still accessible. He can then use that path/filename to look for the blocks file, or (in the event of it not being found) fall back to a different location method (i.e. if not found at the path entered by the user, then try DSK.FBFORTH.BLOCKS etc.) before finally giving up.

 

Edit: Just had a quick look using the Classic99 debugger. After typing in a filename on the Option 3 menu, I can't see it anywhere in CPU RAM (8K, 24K or PAD), and, apart from the screen image table, it's not in VDP either. I'm forced to conclude that E/A thus reads the filename directly from the screen.

 

To be honest, I'd be surprised if that is actually what is happening...??

 

Ok went back to the source code of the EA Cart and here is all the PABs used with PABPTR (>831C)

 

>1000 is used for EA5 or EA3 to load programs. >1000 is also used for Editor or Assembler to load them.

 

>1100 is used for Assembler to load the Source file. Or >1100 is used for Editor to load the edited file you are working on.

 

>1200 is used for Assembler Object file.

 

>1300 is used for Assembler List file or Print file. Or >1300 is used for Editor print file.

 

Rich

 

P.S. It does read the characters off the screen and puts them in the VPAB with PABPTR as the index.

Just double checked the EA Cart and REA and both use >1000 to load EA5 but be quick as it overwrites it so had to go slow mo to see it.

Edited by RXB
Link to comment
Share on other sites

P.S. It does read the characters off the screen and puts them in the VPAB with PABPTR as the index.

 

That's interesting. After I posted, I thought about it some more. When we're writing in 9900 assembler, because of the TI's stupid architecture, we tend to build things like PABs in CPU memory somewhere and transfer them over to VDP just before executing our DSRLNK code. It then occurred to me, in GPL, the entire memory space: CPU, VDP, and GROM is wide open to the GPL programmer, it really makes no difference what type of memory you are using as a buffer. As a consequence, I concluded that it's entirely reasonable, in a GPL context, to simply copy the path and filename straight from the screen (which is VDP) to the PAB (which is also VDP). It's just a different way of thinking.

 

I really should give GPL a shot some time! :)

Link to comment
Share on other sites

That's interesting. After I posted, I thought about it some more. When we're writing in 9900 assembler, because of the TI's stupid architecture, we tend to build things like PABs in CPU memory somewhere and transfer them over to VDP just before executing our DSRLNK code. It then occurred to me, in GPL, the entire memory space: CPU, VDP, and GROM is wide open to the GPL programmer, it really makes no difference what type of memory you are using as a buffer. As a consequence, I concluded that it's entirely reasonable, in a GPL context, to simply copy the path and filename straight from the screen (which is VDP) to the PAB (which is also VDP). It's just a different way of thinking.

 

I really should give GPL a shot some time! :)

 

I'm pretty sure that the GPL interpreter cannot transfer VRAM to VRAM, directly. After all, it is written in ALC. I think you'll find that any GPL command that copies VRAM to VRAM goes through scratchpad RAM to do it—it's just not reserving a block of RAM the size of the source/destination for the process, like we often do in our own ALC.

 

...lee

Link to comment
Share on other sites

I'm pretty sure that the GPL interpreter cannot transfer VRAM to VRAM, directly. After all, it is written in ALC. I think you'll find that any GPL command that copies VRAM to VRAM goes through scratchpad RAM to do it—it's just not reserving a block of RAM the size of the source/destination for the process, like we often do in our own ALC.

 

It's not even possible in hardware, you at least need a register inbetween while you change VDP address. But GPL itself certainly allows it -- the flexibility is very nice!

 

MOVE instructions are one big part of the GPL interpreter that could use some optimization though.. they set the address on both sides of the move for every byte (except for CPU memory, of course). If it was smart and checked whether it needed to, copies to/from VDP and GROM could be sped up a lot.

Link to comment
Share on other sites

I'm pretty sure that the GPL interpreter cannot transfer VRAM to VRAM, directly. After all, it is written in ALC. I think you'll find that any GPL command that copies VRAM to VRAM goes through scratchpad RAM to do it—it's just not reserving a block of RAM the size of the source/destination for the process, like we often do in our own ALC.

 

...lee

 

Yes but did you know from EA just for the fun of it I wrote a GPL program that used the screen as the VPAB and buffer?

Works great but you see the files on screen flying by pretty fast, I should do more on this.

Link to comment
Share on other sites

It's not even possible in hardware, you at least need a register inbetween while you change VDP address. But GPL itself certainly allows it -- the flexibility is very nice!

 

MOVE instructions are one big part of the GPL interpreter that could use some optimization though.. they set the address on both sides of the move for every byte (except for CPU memory, of course). If it was smart and checked whether it needed to, copies to/from VDP and GROM could be sped up a lot.

 

Nice Tursi that would speed up GPL by many times as much as I use the MOVE command.

 

Done at the ROMs it would really be a boost to the TI performance in TI Basic and XB too.

Link to comment
Share on other sites

Done at the ROMs it would really be a boost to the TI performance in TI Basic and XB too.

 

I looked at it, but since the ROM is soldered on the TI mainboard it wasn't something I liked the idea of trying to distribute (modified ROM, that is).

 

On the other hand, I've been frustrated by trying to come up with nice ways to distribute my mods, and I've been starting to lean towards just selling off modded consoles instead. ;) I don't know if that would work.. when I put up a pre-modded PS/2 keyboard console, it didn't sell. But maybe with a few more hacks in there...?

Link to comment
Share on other sites

I looked at it, but since the ROM is soldered on the TI mainboard it wasn't something I liked the idea of trying to distribute (modified ROM, that is).

 

On the other hand, I've been frustrated by trying to come up with nice ways to distribute my mods, and I've been starting to lean towards just selling off modded consoles instead. ;) I don't know if that would work.. when I put up a pre-modded PS/2 keyboard console, it didn't sell. But maybe with a few more hacks in there...?

 

Well the hard ware is going to become more and more hard to maintain and keep up long term.

That is also why I have put it all in Storage as it cost much more to keep running then any Emulator.

 

I would love a version of the TI ROMs being updated like the GPL MOVE or some other issues repaired.

Link to comment
Share on other sites

I think I just may write a VMOVE word for fbForth that will take source and destination VRAM addresses—something like VMOVE ( vsrc vdst count --- ), exactly analogous to TI Forth/fbForth's VMBR or VMBW . The major reason I would do this is that I think I want to use VRAM to store information fbForth needs for system path for FBCHARS, FBMSGS and FBLOCKS and their PABs. I will have the room because I won't need the full 1024 bytes for the VRAM buffer for block transfers that TI Forth needed—I only need 128 bytes as a record buffer for level 3 transfers. The reason I would need such a word is that the change to/from bitmap mode requires PABs and the Forth block-transfer buffer to be moved to a different VRAM location. If I'm using them for persistent information, I will need to move that information to the new location as well.

 

...lee

Link to comment
Share on other sites

I think I just may write a VMOVE word for fbForth that will take source and destination VRAM addresses—something like VMOVE ( vsrc vdst count --- ), exactly analogous to TI Forth/fbForth's VMBR or VMBW . The major reason I would do this is that I think I want to use VRAM to store information fbForth needs for system path for FBCHARS, FBMSGS and FBLOCKS and their PABs. I will have the room because I won't need the full 1024 bytes for the VRAM buffer for block transfers that TI Forth needed—I only need 128 bytes as a record buffer for level 3 transfers. The reason I would need such a word is that the change to/from bitmap mode requires PABs and the Forth block-transfer buffer to be moved to a different VRAM location. If I'm using them for persistent information, I will need to move that information to the new location as well.

 

...lee

 

Okay...Here's an implementation of VMOVE using fbForth's TMS9900 Assembler vocabulary:

 

HEX

( move a block of memory from one VDP location to another )

ASM: VMOVE ( vsrc vdst cnt --- )

*SP+ R1 MOV, ( pop cnt to R1 )

*SP+ R3 MOV, ( pop vdst to R3 )

R3 4000 ORI, ( prepare for VDP write )

*SP+ R2 MOV, ( pop vsrc to R2 )

BEGIN, ( copy cnt bytes from vsrc to vdst )

8305 @() 8C02 @() MOVB, ( write LSB of VDP read address )

R2 8C02 @() MOVB, ( write MSB of VDP read address )

R2 INC, ( next VDP read address )

8800 @() R0 MOVB, ( read VDP byte )

8307 @() 8C02 @() MOVB, ( write LSB of VDP write address )

R3 8C02 @() MOVB, ( write MSB of VDP write address )

R3 INC, ( next VDP write address )

R0 8C00 @() MOVB, ( write VDP byte )

R1 DEC, ( decrement count )

EQ UNTIL, ( repeat if not done )

;ASM

 

I may eventually implement this in the ALC of the support routines for fbForth; but, the above shows how to do the same thing in fbForth Assembler. Incidentally, the fbForth workspace starts at 8300h. That is why I used 8305h and 8307h for the LSBs of R2 and R3, respectively.

 

...lee

Link to comment
Share on other sites

  • 2 weeks later...

Sorry for the long hiatus—I've been trying to figure out exactly what is the best configuration of VRAM in bitmap mode, now that using level 3 file I/O frees up 896 bytes (I only need 128 bytes of the former 1024-byte Forth buffer). I am contemplating the following for the 1280 bytes from 1B00h–2000h in VRAM:

  1. [128 bytes] Moving the 128-byte Sprite Attribute Table (allows for sprite patterns 0–58 vs. 16–58 before). If I can get away with just one open file (not sure about this), I can have sprite patterns 0-123!
  2. [248 bytes] Storing my true lowercase font from the fbForth program so I don't need to load from file every time bitmap mode is (de)selected. This will also obviate needing FBCHARS—I will put the tiny chars for the 64-column editor back in a block.
  3. [417 bytes] Storing messages from the fbForth program. This will obviate needing FBMSGS.
  4. [60 bytes] Storing the full path of the system FBLOCKS file.
  5. [140–210 bytes] Two or three 70-byte PABs for block files.
  6. [64–96 bytes] Increase value stack from 128 64 bytes to, possibly, 192 96 bytes. (It's 128 bytes in all other modes!)
  7. [128 bytes] fbForth file buffer of 128 bytes.
  8. [0– 95 bytes] Any remainder will be available for additional PABs. (It looks like only one, unless the user uses the minimum path size of 5 characters for device name and maximum of 10 characters for filename, e.g., "DSK2.FILENAME03". Then it would be as many as 3. However, the maximum number of simultaneously open files in bitmap mode is 2 in fbForth and, if I maintain what was done in TI Forth, the maximum is 1 file. This is complicated, though, by the fact that fbForth needs a file open for block I/O that is independent of whatever the user wants to do. So, either I set FILES=2 and allow the user to open one file or I set FILES=1 and disallow any user open files in bitmap mode. Oy!)—Of course, the user can use block files for I/O at will because fbForth will only have one of those open at any given time.

More later...

 

...lee

Edited by Lee Stewart
Link to comment
Share on other sites

Regarding my VRAM musings in the last post, I will need to copy (2), (3), (4) and (5) to different VRAM locations when the VDP mode is changed to/from bitmap mode. That's a total of 865–935 bytes! I don't suppose that's too onerous considering that there will probably only be 1 or 2 such switches in any given program. What do you think? Of course, development of a bitmap program might entail many such switches in any given session. I guess will need to time how long it takes to move that many bytes in VRAM.

 

...lee

Link to comment
Share on other sites

I can save an additional 43 bytes in the persistent VRAM because there's a line in the messages that really doesn't need to be there: "$fbForth --- a TI-Forth/fig-Forth extension" (the '$' is a placeholder for the character count). This is used in the definition of the word TRIAD , which uses MESSAGE to print that line at the bottom of the page of 3 blocks. I can just as easily include it in TRIAD 's definition.

 

Another savings of 31 bytes could be effected by saving only 7 ( instead of 8 ) bytes per character for my true lowercase font because the first byte is always 0. This is what GPL routine 4Ah does. I'm not sure saving those 31 bytes is worth the extra coding, however. A lot depends on whether a user might want a larger file buffer in bitmap mode than the 128 bytes of fbForth's block buffer, which the user should be able to use between block accesses.

 

...lee

Link to comment
Share on other sites

I'm wondering if you can do tricks with the record size? For example, let's pretend you only had 64 bytes available as a record buffer. Add far as I know, you could still still open and read a D/F 128 file just fine as a DF 64, just double the record number and do two record reads. Would have to check that works though.

 

Depending on how your record read words work (and writes) this might not be a problem. If your words require a buffer in cpu ram to hand of the records to then its not a problem. Just do two reads placing the data in the appropriate place in the users buffer with each read.

 

Like I say. .. Would have to try it!

Link to comment
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.
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...