Jump to content
IGNORED

Unlock your SAMS with Forth!


Willsy

Recommended Posts

Here's an article I'm preparing for the TurboForth website. I thought I'd chuck it out here for 'yall to have a look at. It presents a library that allows Forth programmers to use the SAMS memory expansion very easily indeed. In fact, I'll stick my neck out and say that I can't really think of a way to make it any easier. Comments warmly welcomed. :)


A SAMS Programming Library for TurboForth

 

Introduction
The SAMS memory expansion for the TI-99/4A is a useful, though woefully underutilised device. Despite offering a full megabyte of memory, literally only a handful of programs exist that can take advantage of it. TurboForth has built in support for the SAMS unit, allowing memory pages to be paged in and out. However, it has no inherent programming support for the SAMS unit. This paper discusses a tiny support library written in high-level Forth which allows colon definitions to be hosted in SAMS paged memory. The dictionary entries for the colon definitions continue to be defined and linked in “normal” memory, meaning that a colon definition can be found, ticked, and executed regardless of the SAMS bank in which it is hosted, and regardless of whether it is paged into memory or not. Colon definitions can be written (in any bank) which reference code in any bank, with complete disregard to the mechanics of paging memory in and out; the paging and un-paging of banks is handled completely transparently by the support library.

Declaring the Number of Banks
Before the library can be used, the number of SAMS that you wish to use must be declared. 248 banks are available, numbered from 0 to 247, giving up to 992K or usable memory. The remaining banks of the SAMS constitute the standard 32K of the TI. The declaration is made with the BANKS word.

BANKS ( n -- )


As can be seen from the above stack comment, BANKS requires a value on the stack. For example:

20 BANKS


This actually reserves some memory (in the standard 24K segment, which is not paged) to track the ‘HERE’ pointers for each bank. HERE in Forth parlance gives the address of the next datum to be compiled to memory.

Writing “Banked” Colon Definitions
It is very easy to write a colon definition in a particular bank. Simply use the word SETBANK to set the active bank. All colon definitions will be defined in the selected bank until another bank is selected:

SETBANK ( n -- )


For example:

3 SETBANK


Now, any colon definition defined will be defined in bank 3:

: test ( -- a b c ) 1 2 3 ;


This definition will be compiled into bank 3.

Note that after each colon definition is defined a report is given on the amount of free memory in the bank. A bank is 4K (4096 bytes) in size. Care should be taken not to allow code to spill outside of the 4096 byte limit. Switch to a new bank if you think the next definition will not fit in the available space.

Disabling Banked Compilation
To disable banked compilation and resume compilation in “normal” un-banked memory simply give the value -1 to SETBANK as follows:

-1 SETBANK


At this point, compilation resumes in normal un-paged memory.

Variables, Values and Constants
Variables, values, and constants continue to be compiled into normal un-paged memory regardless of the active bank setting. Due to their small size there’s no particular advantage to putting them in banked memory, and the overhead of a bank switch to obtain their value would not make sense.

Calling Code in Different Banks
It is possible for a word in any bank to refer to a word in any other bank. No special action is required on the part of the programmer. For example:

4 BANKS
 
0 SETBANK
: TOM ( -- ) .” Tom “ ;
 
1 SETBANK
: DICK ( -- ) .” Dick “ ;
 
2 SETBANK
: HARRY ( -- ) .” Harry “ ;
 
3 SETBANK
: TDH ( -- ) CR TOM DICK HARRY CR ;


Here, code in bank 3 refers to code in banks 0, 1 and 2. Although they all occupy the same physical address in the TI’s memory map (the 4K region at >3000 to >3FFF) the banking is all handled automatically.

Nesting of Banked Definitions
Banked words/definitions can call words in other banks, which in turn call words in other banks, which in turn call words in other banks etc. This is achieved through the use of a “bank stack” implemented in the support library which pages the banks in and out as required. No action is required on the part of the programmer.

For example:

0 SETBANK
: TOM ( -- ) ." TOM " ;

1 SETBANK
: DICK ( -- ) TOM ." DICK " ;

2 SETBANK
: HARRY ( -- ) DICK  ." HARRY" ;

3 SETBANK
: GO ( -- ) CR  HARRY  CR  ." FINISHED!!"  CR ;

-1 SETBANK
GO


In the above example, GO, which is in bank 3, immediately calls HARRY in bank 2. HARRY immediately calls DICK in bank 1. DICK immediately calls TOM in bank 0, which displays TOM then exits back to DICK in bank 1, which displays DICK, then exits back to HARRY in bank 2 which displays HARRY, then exits to GO in bank 3, which displays FINISHED!! Then exits. Note that the bank nesting and un-nesting is handled completely transparently.

Ticking and Executing Banked Words
The dictionary entries for banked words are actually compiled into normal, standard, un-banked memory. Only the executable code for a colon definition is compiled into the appropriate bank. This was a deliberate design decision as it allows words to be found with FIND (the word in the Forth system that searches the dictionary for words), allows them to be ticked with ‘ and [‘] and also allows them to be compiled with COMPILE and/or [COMPILE] and executed with EXECUTE with no special treatment required whatsoever.

The Code
The code for the SAMS programming support library is presented below. As written below, it occupies 694 bytes of memory (0.6K). It is a testament to the power and flexibility of Forth that such functionality can be ‘grafted’ into the Forth system, whilst being implemented in Forth itself.

create _bnkstk 20 cells allot  \ bank stack
_bnkstk value _bsp             \ pointer into bank stack
-1 value _bank                 \ current bank
0 value _heres                 \ holds "here" for each bank
0 value _nhere                 \ "normal" here
0 value _maxBank
: >bank ( bank -- ) \ push bank to bank stack
  2 +to _bsp   dup _bsp !   $3000 >SAMS ;
: bank> ( -- ) \ pop bank from bank stack
  -2 +to _bsp   _bsp @ $3000 >SAMS ;
 
: banks ( n -- )  \ reserve space for here pointers for n banks
  here to _heres  
  dup to _maxBank
  dup 0 do $3000 , loop  \ init "here" for each bank to $3000
  cr 4 * n>s type ." K of banked memory reserved." cr ;
 
: b: ( bank -- )
  \ begin compiling a banked definition in bank bank
  _bank -1 <> if
    :
    compile lit _bank ,    
    compile >bank
    compile branch  _bank cells _heres + @ dup ,
    here to _nhere          \ save "normal here"
    h !                     \ set h to _bank's "here"
    _bank $3000 >SAMS       \ map in the appropriate bank
  else : then ;
: _bfree ( -- ) \ determine free memory in the bank...
  $4000  _bank cells _heres + @ -  .
  ." bytes free." cr ;
 
: ;b ( -- ) \ end banked compilation
  compile branch  _nhere ,
  here  _bank cells _heres +  ! \ update here for bank
  _bfree
  _nhere h !                    \ restore h to "normal" memory
  compile bank>
  [compile] ; ;
 
: : ( -- ) \ banked / non-banked compilation
  _bank -1 = if : else b: then ; 
 
: setBank ( bank -- )
  \ sets the bank number that will receive colon definitions
  dup -1 _maxBank within if
      to _bank
      _bank -1 <> if
        cr ." Bank " _bank . ." is now active. "
        _bfree
      else
        cr ." Compiling to standard 32K memory." cr
      then
   else
      true abort" Illegal bank number specified"
   then ;
 
: ;  _bank -1 = if [compile] ; else ;b then ; immediate
  • Like 7
Link to comment
Share on other sites

Wow! That's very impressive, Mark! :thumbsup: :thumbsup: :thumbsup:

 

I wish I could do something like that in fbForth 2.0; but, I'm afraid that, in my effort to preserve compatibility with TI Forth, I may have boxed myself in with the ALC support code, block buffers and the return stack all in low RAM. I'm also not entirely sure how I could maintain dictionaries with your scenario.

 

Perhaps I could gain some insight with the answer to the following question: Does loading your TurboForth Floating Point Library into low memory work transparently with your SAMS Programming Library?

 

I will certainly study your code at length when I get back home next week. I am sure the answer is there. Again, a very nice piece of work!

 

...lee

Link to comment
Share on other sites

Nice. I will definitely have to do a run of these cards again. . .with RXB, Assembly, C, and Forth all supporting it now, it is getting ever-easier to program for it. :)

 

If I can manage to incorporate into fbForth 2.0 what Mark has just done with TurboForth, I will definitely be wanting one+ from you, Jim!

 

...lee

Link to comment
Share on other sites

Does this work on Classic99, I have not tried the it for expanded memory, but it how well is the emulation, since sadly my own AMS is in storage back in Canada, so I don't have one here to use on my real iron machine.

 

Yes. Classic99 has 1024kB SAMS support, all of which is supported by TurboForth.

 

...lee

  • Like 1
Link to comment
Share on other sites

Does this work on Classic99, I have not tried the it for expanded memory, but it how well is the emulation, since sadly my own AMS is in storage back in Canada, so I don't have one here to use on my real iron machine.

Hi Gary. Yes, as Lee notes above, classic99 supports the SAMS 100%

 

In fact, I developed it with classic99 ;-)

  • Like 1
Link to comment
Share on other sites

Nice. I will definitely have to do a run of these cards again. . .with RXB, Assembly, C, and Forth all supporting it now, it is getting ever-easier to program for it. :)

Yeah - a "programmers special edition" cart would be a fantastic idea. Don't do a run yet though. I need to do a new release of TurboForth as I have found a bug in the SAMS handling. It meant that while developing the SAMS library, I had to develop a replacement word for it in assembly language. Of course, TurboForth (and fbForth) have integral assemblers, so I was able to type in the machine code right there at the keyboard and run it :-)

 

I'll have a new build of TurboForth released this evening. And when you do a run of Programming Edition carts I'll have three or four off you!

Link to comment
Share on other sites

Perhaps I could gain some insight with the answer to the following question: Does loading your TurboForth Floating Point Library into low memory work transparently with your SAMS Programming Library?

 

I will certainly study your code at length when I get back home next week. I am sure the answer is there. Again, a very nice piece of work!

 

...lee

Hiya Lee - Thanks for the kind words!

 

To answer your question re the FP library: Yes and no. You could have both libraries loaded and FP will work fine. However you would have to ensure that words that access the FP are NOT executed from banked memory because the banking would page out some of the FP ALC! However, there is a work around that I can implement to get around that problem. I have a cunning plan! Stand by for a new edition of the SAMS library! That first version didn't last long did it? What have you done Mr. Stewart ;-)

 

Regarding porting it to fbForth - hell yeah. Let rip! It's all good! I'm planning to do a write up of how it works for the website which will aid you in porting. It just uses a magic trick but like all magic tricks: a) it's not magic, and b) it's really simple!

Edited by Willsy
  • Like 1
Link to comment
Share on other sites

Regarding Classic99 and SAMS, here's a program that supports SAMS: a picture slideshow viewer. But the strange thing is that in Classic99 it only shows 2 picture files to choose from, while on my real TI with 128K SAMS it shows about 8-10 files. Is that a problem with the SAMS support or something else?

AMS Slideshow (1997)(Bruce Harrison)(PD).dsk

Link to comment
Share on other sites

Regarding Classic99 and SAMS, here's a program that supports SAMS: a picture slideshow viewer. But the strange thing is that in Classic99 it only shows 2 picture files to choose from, while on my real TI with 128K SAMS it shows about 8-10 files. Is that a problem with the SAMS support or something else?

Hmmm.... does Classic99 show any AMS activity in the debugger window?

Link to comment
Share on other sites

Hmmm... I wonder if Classic99 is failing to catalog the disk properly, rather than it being a AMS problem. In my quite extensive use of the AMS emulation in Classic99 I've never uncovered any problems with it. Experiment: Try a .DSK image with the software and some pictures on it (assuming you aren't already).

Link to comment
Share on other sites

Weird one. I didn't write the AMS support but it's pretty simple, and seems to be working even here. After trying a few other folders, it's something in the file discrimination code. Even my pictures folder (which has hundreds of images) only gets about a dozen onscreen, but I haven't figured out what the difference is between files that show and files that don't. Source is on the disk, but it takes a while to make sense of someone else's assembly. ;)

 

I'll poke at it. But the AMS side seems to be working okay.

 

Edit: confirmed. When I use the TI disk controller, all ten images on the disk show up. I'll figure out what I am reporting wrong. ;)

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

Found it... it was a misconception I had on the directory listings. Since the record type is FIXED, I assumed the filename would be padded to 10 characters like it is on the disk itself. Of course most programs still worked, because they either read the sectors themselves (in which case they dealt with the padding), or they passed it into the DSR which ignored the padding. But this tool was manipulating the filename (_P to _C, etc), and used the returned filename length to figure that out. Since I was always padding to 10 characters, it failed, unless of course the filename actually WAS 10 characters. ;)

 

It was wrong for both FIAD (where I was explicitly padding) and Images (where I was just taking it from the FDR).

 

Thanks for calling it out, that fix will be in the next version.

  • Like 3
Link to comment
Share on other sites

Insidious little bug there, Tursi, especially as you noted that it works almost all of the time with your original method. Edge cases are a killer, but at least it was easy to find once you knew it was there. Many thanks for all of the development you do on Classic99--it is really nice to have fully-supported emulators out there (and kudos to Michael too, for his similar level of dedication in MESS). Being able to validate new code in both of them pretty much assures operation on real iron. :) :) :)

  • Like 3
Link to comment
Share on other sites

Lee,

 

Here's how it works:

 

attachicon.gifbanking.png

 

Thanks! But, like I said earlier, I will look at it more closely when I get home in a couple of days. Just to remind you, fbForth's 4 block buffers occupy all of the lower 4kB of low RAM and the upper 4kB (actually, >3020 – >3FFF) contains fbForth's ISR as well as block I/O code, user variable table, all variables, the TIB*, the return stack and a lot of other low-level ALC system support, including ROM-bank-switching code. I think this would cause a lot of thrashing.

 

...lee

 

*[EDIT: the TIB is not in low RAM—it’s at >FFA0.]

Edited by Lee Stewart
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...