Jump to content
IGNORED

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


Lee Stewart

Recommended Posts

It would do it in a loop until it sees ]data.

That way, broken lines and multiple lines and blank lines would make no difference. You would have to check for special cases such as comments. The simplest way to do that is to do a BL WORD and convert directly to a number. If the conversion doesn't work then check for a comment. If not a comment then error. If an open bracket is detected then enter a state where you throw everything away (BL WORD in a loop) until you get a closed bracket.

Link to comment
Share on other sites

It would do it in a loop until it sees ]data.

That way, broken lines and multiple lines and blank lines would make no difference. You would have to check for special cases such as comments. The simplest way to do that is to do a BL WORD and convert directly to a number. If the conversion doesn't work then check for a comment. If not a comment then error. If an open bracket is detected then enter a state where you throw everything away (BL WORD in a loop) until you get a closed bracket.

 

[Edit: See post #1212 for an updated DATA[ .]

 

That's pretty much what I'm doing already—just was not handling the user hitting “Enter” or entering more than 80 characters before ]DATA closed the array. But, I think I have it, now. I will use QUERY when DATA[ detects a null. QUERY calls EXPECT with the TIB address and 80 characters as the maximum input. QUERY is what INTERPRET uses to get the next line from the terminal. Here is the code:

 

: DATA[ ( -- addr count ) ( Input Stream: n1 ... nn)
STATE @ IF COMPILE DATA[] 0 , THEN HERE 0
BEGIN
IN @ IN_TMP ! BL WORD HERE 1+ C@
CASE
93 OF IN_TMP @ IN ! 1 ENDOF ( handle ]DATA )
40 OF HERE C@ 1 = IF ( handle comment )
[COMPILE] ( 0
ELSE 1 0 ?ERROR
THEN
ENDOF
45 OF HERE 3 + C@ 62 = ( handle --> )
IF [COMPILE] -->
ELSE [NUMBER]
THEN 0
ENDOF
0 OF CR QUERY 0 ENDOF ( handle null [line end])
DROP [NUMBER] 0 1
ENDCASE
UNTIL
; IMMEDIATE

While loading blocks, the line above (in brown) that tests for null should not find a null before it finds ]DATA . A terminating null only occurs at the end of a block. So far, the above code is working for both terminal entry and block loading.

 

...lee

Edited by Lee Stewart
  • Like 2
Link to comment
Share on other sites

While porting @Vorticon’s Jetpac to fbForth, I discovered a bug in SSDT , which is the easiest way for a user to change the Sprite Pattern Descriptor Table in graphics mode to a different location from the the default 800h. The default, 800h, is coincident with the text Pattern Descriptor Table. I thought I had tested SSDT ; but, obviously, I did not! It is easy enough to change the SPDT in code, but it is not trivial. Besides, SSDT not only changes the user variable read by the constant, SPDTAB , but also changes VDP register #6 to the proper value and executes DELALL to initialize sprites.

 

I have other changes to fbForth 2.0 that I want to make; but, I will probably release another build sooner than I intended because of this bug. Oh, well!

 

...lee

  • Like 2
Link to comment
Share on other sites

I have fixed SSDT . I have also fixed BSAVE and BLOAD to save and load the vocabulary link fields of the FORTH and ASSEMBLER vocabularies. This leaves the remaining cartridge space as

 

Bank Free Space

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

0 168 bytes

1 58 bytes

2 147 bytes

3 30 bytes

 

There are a few words I would like to add to the resident dictionary; but, as you can see, there is not much room without some improvement to the efficiency of my code. Finding more space would be a major undertaking, I'm afraid. It would probably be easier to go to a 64KiB cartridge. At that point, I think I might reorganize things a bit. My biggest challenge will likely be managing the resident dictionary itself. As it stands, the resident dictionary's linked lists are wholly contained in Bank 2, which has only 147 bytes left! Each additional word takes a minimum of 10 bytes for a word with a one-character name. Six- and seven-character names take 16 bytes each. I should be able to gain space by moving some code in Bank 2 to Banks 4+ with a 64KiB cartridge.

 

One thing I will definitely try to do if/when I go to a 64KiB EPROM will be to organize the resident dictionary into linkable blocks to reduce the interpreter's search time through blocks of words the user doesn't care about. Words that can be organized into separate blocks are graphics primitives, file I/O, floating point library, 40/80-column editor and font editor. If a user does not need one or more of those blocks of words, the interpreter's search time can be significantly reduced. Of course, I must devise a creative way to manage the linked lists—we'll see.

 

Back to the imminent build #3 of fbForth 2.0, I think I may add VLIST , which is practically the same as TurboForth's WORDS . VLIST is currently loadable from FBLOCKS and is used to list the words in the current dictionary. It occupies 124 bytes of dictionary space. If I include it, there is room for little else.

 

There are a couple more words that won't take up much space, one of which is ELSEOF , which is very similar to Turboforth's DEFAULT: and is part of the CASE ... ENDCASE construct. I will discuss in a future post what ELSEOF does and why I chose not to implement DEFAULT: , though you can probably guess how to use ELSEOF .

 

...lee

  • Like 1
Link to comment
Share on other sites

OK, here is my ELSEOF offering:

 

: ELSEOF ( n -- )
COMPILE DUP [COMPILE] OF
; IMMEDIATE

ELSEOF starts out the same as TurboForth's DEFAULT: , but still requires ENDOF to terminate the clause. To me, ELSEOF is more in the spirit of the rest of the CASE ... ENDCASE construct in Forth. DEFAULT: seems to me to mix the look and feel of the switch statement in C with that of the CASE .. ENDCASE in Forth.

 

Just as with TurboForth's DEFAULT: , fbForth's ELSEOF ... ENDOF must immediately follow the last OF ... ENDOF clause. Also, as with DEFAULT: , any code following ELSEOF ... ENDOF and preceding ENDCASE will never execute because, if all of the OF ... ENDOF clauses fail, the ELSEOF ... ENDOF clause will always succeed. To lift @Willsy's example, the following all do exactly the same thing, i.e., any number passed to TEST other than 1, 2, or 3 will result in 999 being printed:

 

: TEST
CASE
1 OF 100 ENDOF
2 OF 200 ENDOF
3 OF 300 ENDOF
999 SWAP ( any other case——the old way and not very intuitive)
ENDCASE
. ( print result)
;
: TEST
CASE
1 OF 100 ENDOF
2 OF 200 ENDOF
3 OF 300 ENDOF
DUP OF 999 ENDOF ( any other case——@Willsy's way before defining DEFAULT: and much better!)
ENDCASE
. ( print result)
;
: TEST
CASE
1 OF 100 ENDOF
2 OF 200 ENDOF
3 OF 300 ENDOF
ELSEOF 999 ENDOF ( any other case——my new fbForth way)
ENDCASE
. ( print result)
;

I will likely be including ELSEOF in the resident dictionary of the next build, which, hopefully, will be before the Faire.

 

...lee

Link to comment
Share on other sites

Are there already tools that list blocks in fbForth to DV80 files? I could probably do something with 'sed' on the ?windows? box hosting my hdx drive, but wondered if there was already something out there... I saw the thread on using Intellij for editing forth files, which would imply moving the code to and from pc text files...

 

I want to be able to commit any of my code to git in a reasonable manner, while actually using the TI for as much of the workflow as is reasonable.

Link to comment
Share on other sites

Also, COINCALL doesn't behave like I would expect. Is the cursor a sprite? do I need to disable it? I suspect this because I have repeating overlapping sprites with motion going on, and the collisions are almost never detected. But when I started printing out the flag from COINCALL, quite lazily with just a 'DUP .', then I started getting lots of collisions detected even through my intended sprites were not in collisions at the time.

 

If I move the cursor by doing something like 32 8 * 23 + CURPOS ! then that issue goes away, but doing that seems really fishy.

Link to comment
Share on other sites

Also, COINCALL doesn't behave like I would expect. Is the cursor a sprite? do I need to disable it? I suspect this because I have repeating overlapping sprites with motion going on, and the collisions are almost never detected. But when I started printing out the flag from COINCALL, quite lazily with just a 'DUP .', then I started getting lots of collisions detected even through my intended sprites were not in collisions at the time.

 

If I move the cursor by doing something like 32 8 * 23 + CURPOS ! then that issue goes away, but doing that seems really fishy.

 

Post your code. Let's have a look and we'll get out sleuthing heads on!

Link to comment
Share on other sites

Are there already tools that list blocks in fbForth to DV80 files? I could probably do something with 'sed' on the ?windows? box hosting my hdx drive, but wondered if there was already something out there... I saw the thread on using Intellij for editing forth files, which would imply moving the code to and from pc text files...

 

I want to be able to commit any of my code to git in a reasonable manner, while actually using the TI for as much of the workflow as is reasonable.

 

That is on the back burner. It would be simple enough to do. I would probably list them much the same as LIST does to the screen, with block numbers and line numbers.

 

...lee

Link to comment
Share on other sites

Also, COINCALL doesn't behave like I would expect. Is the cursor a sprite? do I need to disable it? I suspect this because I have repeating overlapping sprites with motion going on, and the collisions are almost never detected. But when I started printing out the flag from COINCALL, quite lazily with just a 'DUP .', then I started getting lots of collisions detected even through my intended sprites were not in collisions at the time.

 

If I move the cursor by doing something like 32 8 * 23 + CURPOS ! then that issue goes away, but doing that seems really fishy.

 

The cursor is not a sprite. The flashing is handled by the code that implements KEY . The cursor is ASCII 30, so you should probably avoid using that character in a sprite.

 

CURPOS is a user variable that holds the cursor position and has nothing to do with sprites.

 

COINCALL does nothing more than report the state of the coincidence bit in the VDP status byte at >837B.

 

...lee

Link to comment
Share on other sites

The E/A Manual says this about the coincidence bit:

 

Set if two or more sprites have overlapping pixels, including sprites that are transparent and sprites that are off the bottom of the screen. The flag is cleared by reading the Status Register or by resetting VDP.

 

Though transparent coincident sprites will, indeed, cause the setting of the coincidence bit, coincident sprites, completely off the bottom of the screen, will not.

 

...lee

  • Like 1
Link to comment
Share on other sites

Also, COINCALL doesn't behave like I would expect. Is the cursor a sprite? do I need to disable it? I suspect this because I have repeating overlapping sprites with motion going on, and the collisions are almost never detected. But when I started printing out the flag from COINCALL, quite lazily with just a 'DUP .', then I started getting lots of collisions detected even through my intended sprites were not in collisions at the time.

 

If I move the cursor by doing something like 32 8 * 23 + CURPOS ! then that issue goes away, but doing that seems really fishy.

 

Before working with sprites and after changing to the VDP mode of interest, sprites should be initialized with DELALL .

 

...lee

Link to comment
Share on other sites

Ok, I am performing GRAPHICS, then DELALL, then creating a driver sprite, and 7 rival sprites.

 

This code currently, experiences coincidence immediately in the 'CONTROL' loop and terminates. But if I change block 5 word SHOWLIVES to end with "30 22 .at", then things loop around. I think I just spotted my error, block 4... Do loop end values are not inclusive, so I'm looping from 2 to 9 to create the enemie cars, which creates sprites 2 through 8. but then I set 9 in motion... which should probably be 8... ?

## BLOCK: 1
( TITLE SCREEN )
: .AT ( X,Y -- ) SWAP 32 * + CURPOS ! ;
: ALLCOLOR ( F,B -- ) DUP SCREEN
15 0 DO OVER OVER I COLOR LOOP DROP DROP ;
: TITLE
GRAPHICS
1 13 ALLCOLOR
5 7 .AT ." FORMULA-TI RACING"
9 8 .AT ." MATTHEW SPLETT"
10 11 .AT ." 1984,2015"
20 6 .AT ." PRESS FIRE TO BEGIN" ;
: WAITFIRE BEGIN 1 JCRU 1 = UNTIL ;

( LOAD OTHER BLOCKS )
: LOADALL 7 2 DO I LOAD LOOP ;
LOADALL

## BLOCK: 2
( CAR PATTERNS)
BASE->R HEX
: RACECAR
03C7 CFFF CFCC 0B0B 68 SPCHAR
0B0B 0FCF CFFF C7C4 69 SPCHAR
C0E3 F3FF F333 D0D0 6A SPCHAR
D0D0 F0F3 F3FF E323 6B SPCHAR
;
: CURBS
FBF5 FBF5 FBF5 FBF5 6C CHAR
DFAF DFAF DFAF DFAF 6D CHAR
;



R->BASE

## BLOCK: 3
( DRAW TRACK )
: DRAWTRACK
CLS CURBS
1 15 ALLCOLOR
5 0 24 108 VCHAR
25 0 24 109 VCHAR ;











## BLOCK: 4
( ADD SPRITES )
: SPRSETUP DELALL 2 MAGNIFY ;
: DRIVER 121 160 13 104 1 SPRITE ;
: RNDSPEED ( --n ) 10 RND 3 + ;
: ENEMIES
9 2 DO
  17 I 20 * + 9 I 3 + 104 I SPRITE
  0 RNDSPEED I MOTION
LOOP 9 #MOTION ;

: GOCARS RANDOMIZE SPRSETUP RACECAR DRIVER ENEMIES ;






## BLOCK: 5
( CONTROL LOOP )
5 VARIABLE LIVES
: SHOWLIVES 2 2 .AT LIVES @ . ;
: CRASH -1 LIVES +! SHOWLIVES ;
: CONTROL
0 JMODE !
5 LIVES !
SHOWLIVES
BEGIN
  COINCALL IF CRASH ENDIF
LIVES @ 1 < UNTIL
;





## BLOCK: 6
( LAUNCH GAME )
: DEVMODE 0 #MOTION TEXT ;
: GAME
  TITLE WAITFIRE
  DRAWTRACK
  GOCARS
  CONTROL
  DEVMODE
;
." Enter GAME to begin."






( I wrote a little groovy/java code to create this block dump )

Link to comment
Share on other sites

Ok, I've found that my sprite numbering is off... I've straightened that out... so now I have sprites 0 the driver, and 1 through 7 the cars. and I'm supposed to use 7 + 1 for the #MOTION... so that wasn't the problem...

 

It is the CONTROL word in block 5 that is confusing me. COINCALL is just returning -1 all the time, which as a flag is true.

 

Do I need to redefine characters 0 - 3 since I'm in '2 magnify' mode?

Link to comment
Share on other sites

That was it... DELALL sets all the sprites patterns to 0, but with my magnified sprites, some magic invisible sprites are tripping the COINCALL. So I defined character patterns 0 - 3, to all 0000s, and COINCALL behaved as I would expect.

 

I still feel like that means I'm not initializing the unused sprites correctly... Now I read a little further in the Lee Stewart book, and it suggests I should move the sprite pattern table so it doesn't overlap with the normal pattern table in Graphics mode... Would that have saved me this issue? or would I still have to have made sure that sprite pattern 0 - 3 is all zeros?

Link to comment
Share on other sites

That was it... DELALL sets all the sprites patterns to 0, but with my magnified sprites, some magic invisible sprites are tripping the COINCALL. So I defined character patterns 0 - 3, to all 0000s, and COINCALL behaved as I would expect.

 

I still feel like that means I'm not initializing the unused sprites correctly... Now I read a little further in the Lee Stewart book, and it suggests I should move the sprite pattern table so it doesn't overlap with the normal pattern table in Graphics mode... Would that have saved me this issue? or would I still have to have made sure that sprite pattern 0 - 3 is all zeros?

 

Sprite patterns should not have anything to do with collision unless they are associated with a sprite. The only reason I can contrive for the behavior you see is not using a lower-numbered sprite. Anytime you define a sprite in fbForth, all lower-numbered sprites are checked for whether they have been marked as undefined with their y coordinates set to >D0. If that is the case their y coordinates are set to >C0 (192). The effect of this immediately after invoking GRAPHICS and DELALL , is that all previously undefined sprites will be defined as though by

 

0 192 0 0 0 SPRITE

 

Their motion vectors will also each be 0. With the magnification factor set to 2, character patterns 0 – 3 are, indeed, associated with each of those previously undefined sprites. I checked the collision bit with overlapping, off-screen sprites and it was not set, contrary to the E/A Manual, so I am puzzled. It certainly could be a bug in fbForth. If we find any such bugs, they will not survive! :grin:

 

...lee

Link to comment
Share on other sites

Re sprite patterns, the default situation, as noted by @jedimatt42, is that the PDT and the SPDT are coincident. When the VDP mode is changed to Graphics, the PDT is first filled with 2KiB of >FF, after which the current font is loaded at the starting address of the PDT. The current font will be either 1KiB or 2KiB in length. The default font is 1KiB, with patterns for the first 32 characters (256 bytes for codes 0 – 31) filled with >FF. Obviously, if the font is just 1KiB, the upper 1KiB (pattern codes 128 – 255) will remain all >FFs. Furthermore, if you change the location of the SPDT, it is not initialized for you. It will contain whatever values happen to be at the new location.

 

...lee

Link to comment
Share on other sites

Ok, I am performing GRAPHICS, then DELALL, then creating a driver sprite, and 7 rival sprites.

 

This code currently, experiences coincidence immediately in the 'CONTROL' loop and terminates. But if I change block 5 word SHOWLIVES to end with "30 22 .at", then things loop around. I think I just spotted my error, block 4... Do loop end values are not inclusive, so I'm looping from 2 to 9 to create the enemie cars, which creates sprites 2 through 8. but then I set 9 in motion... which should probably be 8... ?

...

 

Your code has no doubt changed, so I won't get too much into it, yet. Just a couple of comments. The conventional way to show stack effects in Forth is to separate stack entries with spaces, which would change

 

: .AT ( X,Y -- ) SWAP 32 * + CURPOS ! ;

 

to

 

: .AT ( X Y -- ) SWAP 32 * + CURPOS ! ;

 

Also, with Y on the top of the stack (rightmost item), your code should be

 

: .AT ( X Y -- ) 32 * + CURPOS ! ;

 

But, there is already a resident word with a more generalized form of this definition. It is actually written in ALC; but, here is the equivalent high-level Forth:

 

: GOTOXY ( X Y -- ) SCRN_WIDTH @ * + SCRN_START @ + CURPOS ! ;

Of course, it could be that your stack effects are backwards, in which case

 

: .AT ( Y X -- ) SWAP 32 * + CURPOS ! ;

 

could be written

 

: .AT ( Y X -- ) SWAP GOTOXY ;

 

...lee

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