BigEd wrote:
Hi BDD
in
another thread you said
BigDumbDinosaur wrote:
I have been thinking about how to adapt Lee's EhBASIC to my
next generation POC computer. The memory mapping scheme I envision for that unit produces 48K banks—up to 256 of them if a full complement of RAM is installed—that are available for programs and data. I could conceivably add code to EhBASIC that would implement a cross-bank variable storage function, with the kernel doling out storage as needed. At 20 MHz, the 65C816 would handle the transfers with alacrity, thus avoiding the principle performance bottleneck seen with BASIC in the C-128.
which intrigued me.
Are you thinking of copying 48k chunks of data from one mapped bank to another? Or are these transfers to and from mass storage? Or to and from a main (single) data bank?
Do you envisage your interpreter living in one 48k bank, and the BASIC program in another, with the variables being found in a collection of many others?
Cheers
Ed
I haven't fully thought it out at this point (the above quote was more-or-less out-loud musing). However, here's what I've been chewing on.
POC V2's design embodies the concept of independent stacks and zero pages for each bank, allowing for true sandboxing of running processes—the kernel would trap attempts to read or write outside of the bank. This will create a suitable environment in which EhBASIC could be run, using a basic memory map somewhat like the following:
Code:
$xx0000-$xx00FF Physical zero (direct) page
$xx0100-$xx01FF Buffer & string work area
$xx0200-$xxXXXX BASIC interpreter engine (size presently unknown)
$xxYY00- Start of BASIC program text
$xxBE00-$xxBFFF Stack, 512 bytes
In the above, the xx in the address is the bank number.
The text of the currently-executing BASIC program would be loaded at the first page boundary immediately following the end of the interpreter proper. For example, if the interpreter occupies RAM up to $xx0FBA, the start of program text would be at $xx1000.
As far as variable storage goes, I've contemplated several possible methods. The first is to allocate two 48K banks for variable storage, one for descriptor storage—which includes storage for the numeric values associated with integer and floating point variables, and the other for storing the text associated with string variables. The cross-bank transfer function I mentioned would actually be part of the kernel and would be located in high common RAM ($00E000-$00FFFF). Cross-bank transfer would work in principle somewhat like the INDSTA and INDFET subroutines in the Commodore 128 kernel, but in a more efficient way—no translation between "bank numbers" and MMU mask patterns. I already have coded an MMU in WinCUPL that simulates on an Atmel ATF2500C CPLD, and am waiting for an opportunity to build the prototype for testing. With the 65C816's 16 bit registers, a cross-bank transfer should be less difficult to code than what was required in the C-128.
An obvious advantage to the above method is variable storage is independent of program storage and hence variable preservation is possible when chaining programs. Also, with a full 48K allocated to string text, more strings could be stored with less frequent garbage collection. With 48K available for variable descriptors, longer variable names would be practical, and there would be room for a lexically sorted variable name index, which would help speed up the location of a variable when many have been created.
A corollary advantage is all RAM above the end of BASIC program text is uncommitted, which means, in the case of the 65C816, the stack can grow downward from the top of the bank, giving plenty of room for FOR-NEXT and GOSUB pushes, etc., as well as the usual stack stuff that occurs in a running program.
The principle downside is the need for cross-bank transfers, which will affect execution speed, since the BASIC interpreter itself could not directly manipulate descriptor or string storage space. Each time access to the variables was required, the cross-bank transfer functions would be required in order to get, put or compare bytes. However, there may be a way to mitigate this to some extent, and that would be for the interpreter, at start-up, to copy code into the variable descriptor bank that could be locally executed to search the descriptor table. I'd have to allocate RAM in that bank for a stack and zero page as well, but those structures could be small (especially the stack, which only has to be one page).
Another way to handle the variable storage would be to use the "traditional" method of allocating space at the end of the BASIC program text, a la Microsoft BASIC and EhBASIC, i.e., between the end of BASIC text and the bottom of the MPU stack. It's actually fairly easy to do, and requires no cross-bank transferring. As all activity occurs within one bank, data movement can be implemented via the 65C816's rapid-acting MVN and MVP instructions—which I use in my M/L monitor to handle the T (memory copy) command.
The downside to setting up variable storage at the end of the program is the same as in the Commodore 64: big programs result in small variable storage space and more frequent garbage collection. Also, preserving variables when chaining programs becomes a problem. If a small program runs a large one, the large one will step on part of the variable descriptor area that was set up by the earlier (smaller) program. The solution to that would be to dynamically relocate the descriptor space to make room for the larger program (or move it downward if a smaller program is run). However, if a lot of strings have been created, upward relocation may cause a collision between the top of descriptor storage and the bottom of string storage.
Yet another method would be a hybrid of the two. Store variable descriptors in the same bank as the BASIC program but string text in a different bank. Cross-bank transfers would be required only to access string text, not variable descriptors.
In all cases, stack growth must be constrained to avoid collision with data structures lying above the end of BASIC text or with the BASIC text itself.
As you can see, it's no simple matter and until I get around to building the next-gen POC and writing a practical operating system for it, it's all conjecture.