Jump to content
IGNORED

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


Lee Stewart

Recommended Posts

I now have the following sound words working in fbForth 2.0:

 

PLAY ( address flag --- )

Play a sound list at address (in RAM). If flag is 0 (FALSE), PLAY will not play the list if another sound list is still playing. If flag <> 0 (TRUE), PLAY will start playing immediately, terminating any current sound list.

PLAYING? ( --- flag )

Leaves 8192 (2000h) (TRUE) if a sound list is currently playing or 0 (FALSE) if no sound list is playing.

 

The above words are adapted from @Willsy's TurboForth words, playSoundList and soundBusy? except for the added flag for PLAY .

 

As soon as I get SOUND working, I will post another beta—maybe tomorrow. SOUND will be pretty much identical to TurboForth's word of that name. It writes directly to the sound chip and has the following stack signature:

 

SOUND ( pitch vol ch# --- )

 

Before I release fbForth 2.0:8, I think I will add >MAP (if it will fit) from TurboForth to allow SAMS use in fbForth 2.0.

 

...lee

  • Like 1
Link to comment
Share on other sites

Here's the pertinent code for driving the sound chip from TF:

 

 

li r7,>8400 ; address of sound chip
; set the channel...
mov *stack+,r1 ; pop channel
mov r1,r8 ; save it
li r0,>9000 ; set msb and volume latch bit
src r1,3 ; move channel into correct bit position
soc r0,r1 ; combine
; set the volume...
mov *stack+,r0 ; pop volume
swpb r0 ; move to high byte
soc r0,r1 ; combine
movb r1,*r7 ; move to sound chip
; get pitch...
andi r1,>e000 ; reset t bit (to latch pitch)
mov *stack+,r0 ; pop pitch
mov r0,r2 ; copy it
andi r0,>000f ; get the low 4 bits
swpb r0 ; move to high byte
soc r0,r1 ; combine
movb r1,*r7 ; move to sound chip
; process noise channel if ch#=3...
ci r8,3 ; noise channel?
jeq sndxit ; if so then just exit
sla r2,4 ; get upper 6 bits in upper byte
movb r2,*r7 ; send to sound chip
sndxit b @retB0
Link to comment
Share on other sites

Adding >MAP would be cool. I was looking at the memory map for fbforth yesterday, and wondering, where it would be safe to page. The middle of the upper 24k seemed to be the only place to likely have an untouched 4k area.

 

It struck me, that it would be maybe sensible to allow relocation of the terminal buffer? and parameter stack? ( I don't have my docs with me. ) down a block or two to make room for pages to be mapped in.

 

The definition stack and parameter stack can continue to grow toward each other, and then the user can map without worrying about code growing into the mapped page?

 

Or is there a better place to page at?

 

-M@

Link to comment
Share on other sites

Adding >MAP would be cool. I was looking at the memory map for fbforth yesterday, and wondering, where it would be safe to page. The middle of the upper 24k seemed to be the only place to likely have an untouched 4k area.

 

It struck me, that it would be maybe sensible to allow relocation of the terminal buffer? and parameter stack? ( I don't have my docs with me. ) down a block or two to make room for pages to be mapped in.

 

The definition stack and parameter stack can continue to grow toward each other, and then the user can map without worrying about code growing into the mapped page?

 

Or is there a better place to page at?

 

-M@

 

Placement advice is what I'm puzzling with as I write. The user can change the stack base by modifying S0 (currently, >FFA0) and the Terminal Input Buffer (which is currently the same as S0 , i.e., >FFA0) by modifying TIB . Your suggestion may be the best solution. I hadn't thought of that. I was leaning, instead, toward suggesting that >D000 or >E000 would be good placement, allowing 4048 and 2000 cells, respectively, for the stack. I will certainly include your suggestion in the manual (when I get around to updating it! |:)) I will get the next beta (with SAMS implemented) out later today, in all likelihood. You can test your suggestions then. :)

 

...lee

Link to comment
Share on other sites

OK...Attached is the latest beta with SAMS capability. >MAP should work as described for TurboForth on Mark's website. Matt, @jedimatt42, had a good suggestion to use the uppermost 4KiB block of the normal RAM expansion as the paging window, after first moving the parameter stack and the Terminal Input Buffer (TIB) down 4KiB. It is easy enough to move those two entities by simply giving S0 and TIB different values from the default of >FFA0 for both. Yes, they both start from the same location! The parameter stack grows downward in RAM from >FFA0 and the TIB starts there and is written in the upward direction. I would recommend that >FFA0 - >1000 = >EFA0 be used for both starting points. This can be effected as follows:

 

HEX

EFA0 S0 !

EFA0 TIB !

 

Changing S0 downward will result in an “! ? empty stack” message, but works. If you need to change it upward, the stack will show a large number of words for the stack depth, which can be cleared with SP! .

 

Here is the latest beta, which shows it as fbForth 2.0:&: fbForth2.0-BETA_20160514SAMS.zip

 

...lee

 

 

  • Like 1
Link to comment
Share on other sites

OK...the latest beta has one additional SAMS word in the resident dictionary, SAMS? , which leaves a flag on the stack for whether a SAMS card is found (FALSE [0] or TRUE [1]). I have moved some word definitions around to free up some critical space in bank 0. The free bytes per bank are now

 

bank bytes

---- -----

0 262

1 276

2 490

3 50

 

Here is the latest beta,which shows it as fbForth 2.0:%: fbForth2.0-BETA_20160520SAMS.zip

 

Please, test this beta and let me know what bugs you find or suggestions you have.

 

I am working on one more idea for sound lists that @Willsy threw at me; and, that is a method to mute the current sound list to play another, presumably much shorter, sound list, which, when done, will unmute the previous sound list—at least, that's my take on what he was suggesting. @Willsy? I am inclined to either limit it to 2 sound lists or have subsequently PLAYed (with muting) sound lists simply kill the second sound list by replacing it. If this effort takes more than a day or two, I think I will defer it to a later build so I can get fbForth 2.0:8 out by next week.

 

...lee

  • Like 1
Link to comment
Share on other sites

I think I've done it! :-o This last (I hope!) beta before release of build #8 allows one sound list to be interrupted by another sound list. The first sound list will be muted until the second one is finished playing. When the first sound list is unmuted, it should be at the same place it would have been playing had it not been interrupted (except for any dropped interrupts). Of course, if the second sound list outlasts the first, there will be nothing to play at the end of the second.

 

PLAY now has a third flag to accomplish the muted interruption. The flag values and how PLAY reacts to them follow:

Flag Action
---- -------------------------------------------------------------------
0 Wait for both sound lists to play.
1 Unconditionally play, killing all previous sound lists.
-1 Plays as sound list #2, setting SND1DS (SL#1 R1) to SDMUTE (regard-
less of SL#1's status) and resetting it to SOUND when SL#2 is done.

As a reminder, PLAY requires on the stack the address of a play list and a flag with one of the above values.

 

The sound list player, PLYLST, invoked by PLAY uses two separate, overlapping workspaces, which use only R0..R3:

 

Workspace name: SND1WS (SL#1); SND2WS (SL#2)
Register Name
SL#1 SL#2 No. Description
------ ------ --- ----------------------------------------------------------------------
SND1ST SND2ST R0: Sound list status; 0 = no list; 1 = loading sound bytes; -1 = counting
SND1DS SND2DS R1: Sound list byte destination initialized to sound chip
SND1AD SND2AD R2: Sound list address
SND1CT SND2CT R3: # of sound bytes to load or sound count = seconds * 60

The muting is accomplished by changing R1 of SL#1 (SND1DS) from SOUND (address of sound processor) to SDMUTE (a do-nothing RAM address) while SL#2 is playing. And, the unmuting, by changing it back to SOUND when SL#2 is done.

 

Here is the latest beta, which shows it as fbForth 2.0:@: fbForth2.0-BETA_20160521SAMS.zip
Please, test this beta and let me know what bugs you find or suggestions you have—especially on real iron.

 

...lee

  • Like 3
Link to comment
Share on other sites

I've been travelling too much on weekends to get to play with much of these goodies, while in my hotel, I'm playing with classic99 QI386, and the >MAP word seems to work as expected.

 

moving s0 and tib seem to work. Again, on emulation. Hopefully this week I'll get some time to fire up the real iron...

 

But I thought I'd let you know, that on classic99, the SAMS? word doesn't seem to work. It puts 0 on the stack with or without the 1024k sams memory option enabled in the emulation.

 

Oh, and to be precise, I was using 2.0:@

 

-M@

  • Like 1
Link to comment
Share on other sites

I've been travelling too much on weekends to get to play with much of these goodies, while in my hotel, I'm playing with classic99 QI386, and the >MAP word seems to work as expected.

 

moving s0 and tib seem to work. Again, on emulation. Hopefully this week I'll get some time to fire up the real iron...

 

But I thought I'd let you know, that on classic99, the SAMS? word doesn't seem to work. It puts 0 on the stack with or without the 1024k sams memory option enabled in the emulation.

 

Oh, and to be precise, I was using 2.0:@

 

-M@

 

I see what is happening, but I'm not certain why. :? The correct flag is written, but it gets zeroed out as soon as mapping is enabled at the end of the test function at startup. I was guessing that the mapping actually happens when the SAMS registers are written; but, now I'm guessing that the registers are written but no actual mapping takes place until mapping is enabled.

 

SAMS? is actually not doing any testing. It simply reads the flag and puts it on the stack. Of course, it's not very useful if it doesn't persist!?! I will change it to write the value to the flag location at the very end of the function to see whether that does the trick. Back in a little...

 

...lee

Link to comment
Share on other sites

OK...I saved the SAMS flag to ARG (>835C), expecting (hoping |:)) that spot wouldn't get trashed during startup. Then, just before the bank switch to launch fbForth, I copy the contents of ARG to SAMSFL (currently, >3ADA). That seems to fix the problem. Here's the updated beta, which now shows as fbForth2.0:#: fbForth2.0-BETA_20160521SAMS2.zip

 

...lee

  • Like 1
Link to comment
Share on other sites

Also, should COLD reset the mapping? It resets S0 and TIB, so I would think it should reset the mapping of the default 32k ram pages.

 

It currently does not appear to reset them.

 

-M@

 

Oops! It, indeed, does not reset the 32KiB RAM pages. I'm a little torn between having COLD reset the 32KiB RAM pages and leaving the changed S0 and TIB , if changed by the user, as the new defaults (which would be simpler). There's always power cycling to insure the original defaults. I certainly have enough room to include a SAMS reset—I could even make it a new word. I suppose it really should reset SAMS; but, I'm flexible. :P

 

...lee

Link to comment
Share on other sites

I would almost say that you should add the SAMS reset as a separate word--that way, if someone needs the mapping registers to remain mapped, they will, and if they don't they won't. Or you could just add a switch to the existing word to force it to reset the SAMS registers too. . .one that defaults to not resetting them. That way, a SAMS reset at COLD is intentional.

Link to comment
Share on other sites

I would almost say that you should add the SAMS reset as a separate word--that way, if someone needs the mapping registers to remain mapped, they will, and if they don't they won't. Or you could just add a switch to the existing word to force it to reset the SAMS registers too. . .one that defaults to not resetting them. That way, a SAMS reset at COLD is intentional.

 

Yeah, it will be easy enough to turn the SAMS startup code into a routine that can both be branched/linked to (BL) at startup and the stub of the relevant SAMS reset word. The only real problem I see with a SAMS reset is the user's likely expectation, in the event of a user mapping screw-up (a technical term, dontcha know!), that COLD will make things all better. If the screw-up is with the lower 8KiB, the user is SOL! For that, s/he will need the power switch.

 

...lee

Link to comment
Share on other sites

Of course, if you put COLD in there in such a way that it stayed in the cartridge but did a normal reset which also reset the SAMS registers, and then returned the standard load set of boot routines to the lower 8K, your system would be able recover from just about anything. . .

Link to comment
Share on other sites

Well, COLD basically restarts the system without doing the power cycle. In fact, COLD is the last routine that the startup code runs. It does the following:

  • Resets the following system variables by copying a table of default values: UCONS S0 R0 U0 TIB WIDTH DP SYS$ CURPOS INTLNK WARNING C/L$ FIRST$ LIMIT$ COLTAB SATR SMTN PDT FPB DISK_BUF PABS SCRN_WIDTH SCRN_START SCRN_END ISR ALTIN ALTOUT VDPMDE BPB BPOFF SPDTAB SCRFNT JMODE WRAP
  • Clears the user dictionary by resetting HERE to point after TASK and by resetting the latest words in the FORTH and ASSEMBLER vocabularies
  • Sets both USE and PREV to FIRST
  • Empties Forth buffers
  • Sets DPL to -1
  • Sets BLK to TIB for terminal input
  • Disables the QUIT key
  • Clears the stack
  • Sets radix ( BASE ) to decimal
  • Clears error count
  • Sets CONTEXT and CURRENT both to the FORTH vocabulary
  • Checks any held-down key for boot disk # or <ENTER> for no LOADing of FBLOCKS block #1
  • Checks for Speech Synthesizer
  • Sets ISR hook
  • Clears user interrupt flag
  • Sets the default VDP mode ( TEXT unless user changed it, then that )
  • Displays boot screen
  • Sets current blocks file to DSKn.FBLOCKS, where n is '1' or whatever number key was held down
  • Sets STATE to 0 for 'executing'
  • Attempts to LOAD block #1 of DSKn.FBLOCKS
  • Executes ABORT , which displays the system name and executes QUIT , which starts the text interpreter, INTERPRET

“Clears the stack” to “Attempts to LOAD block #1...” in the above list was what BOOT performed. I removed BOOT as a separate Forth word (it was only ever called by COLD ) and incorporated it into COLD . What I could easily do is to redefine BOOT as a system restart that would back up even farther than COLD to run the actual startup code from the cartridge. If I require a value on the stack to tell it which mode, TEXT or TEXT80 , to use, it would run exactly like choosing which fbForth option to run. Here is the fbForth Assembler code:

 

HEX

ASM: BOOT ( n --- )

*SP 8348 MOV,

6044 B,

;ASM

 

and its machine code equivalent:

 

HEX

CODE BOOT C819 , 8348 , 0460 , 6044 , NEXT,

 

As you can see, this BOOT is very simple. I think I will include it. I still should, perhaps, change something in COLD for SAMS; but, the above BOOT will definitely restart everything.

 

Of course, MON is even closer to power cycling because that takes you all the way back to the color bar screen!

 

...lee

Link to comment
Share on other sites

OK...the next beta follows—it shows as fbForth 2.0:$: fbForth2.0-BETA_20160522SAMS.zip

 

I now have BOOT doing what I described in my last post.

 

A new word, SAMS! , will reset a SAMS card to startup settings.

 

I am leaning toward not having COLD do anything different from what it currently does with S0 (value from default user variable table), TIB (value from default user variable table) and SAMS mapping (nothing). However, I think I will create a word that will change S0 and TIB to the same new value (forced to a 4KiB boundary) the user chooses and change their default values in the default user variable table. Using this word for the changes, then, would survive COLD but not BOOT . If the user doesn't like the 4KiB-boundary limitation, s/he can always manually change S0 and TIB to any desired values. Likely name of the new word: S0&TIB!

 

More testing, please...

 

...lee

  • Like 2
Link to comment
Share on other sites

I added code to BOOT to force TEXT or TEXT80 VDP mode and to allow its execution without a value on the stack, in which case it will boot to TEXT VDP mode.

 

I also implemented S0&TIB! to force S0 and TIB to each have a value of >AFA0, >BFA0, >CFA0, >DFA0, >EFA0 or >FFA0. If a value less than >A000 is supplied to S0&TIB! , an error message is issued.

 

Both of the above words work as described. If anyone actually wants the cartridge binary now, I will post it. Otherwise, I will wait for a little more testing. ;-)

 

...lee

Link to comment
Share on other sites

I discovered a problem with S0&TIB! that I should have anticipated. I was aware of the problem with moving the stack base and just zeroed it rather than copy the stack. I figured the user should not expect anything to survive the stack move. That doesn't mean I can't remedy that situation; but, I really don't think that this operation should be expected to preserve the stack. Moving the TIB, however, presents a different kind of problem (the one I should have anticipated |:) ). At the moment the TIB is moved, the interpretive pointer IN is pointing to the next word after S0&TIB! in the TIB waiting to be interpreted or to a null. However, the new TIB has whatever was in that part of memory, which can hardly be expected to contain anything intelligible to the interpreter. I can do one of two things to remedy this. I can check IN and put a null at that offset in the new TIB, which would stop interpretation gracefully—or, I could copy the old TIB (82 bytes) to the new, which would allow interpretation of whatever might have followed S0&TIB! in the original TIB to continue as though nothing had happened. I am inclined to go with the copy. What do you think?

 

...lee

Link to comment
Share on other sites

The copy approach seems to be the most robust option.

 

However, it is a fairly advanced feature, and if nulling the destination tib first is sufficient to avoid run away behavior, making the user prepare the tib beforehand could save you cart space, and would be 100% reasonable.

 

-M@

Link to comment
Share on other sites

The copy approach seems to be the most robust option.

 

However, it is a fairly advanced feature, and if nulling the destination tib first is sufficient to avoid run away behavior, making the user prepare the tib beforehand could save you cart space, and would be 100% reasonable.

 

-M@

 

Well...The copy (16 bytes) consumes only 4 – 6 bytes more than zeroing the relevant 1 or 2 bytes of the new TIB. After adding the copy code, the free space is

 

bank bytes
---- -----
0 218
1 230
2 318
3 50

 

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