cr1901 wrote:
I feel bad (partially) for reviving a 5 year old thread, but I'm mulling over this exact topic right now.
From what I understand, Forth requires the implementation to access data using addresses which are the same size as the basic cell. This is fine for most processors, but for banked/segmented archs (namely, 65816, 8088, and 286), and more generally archs where address bus width != data bus width, this falls apart.
All solutions provided here sound appealing- I think I'd rather take the code complexity hit and keep performance up by using a natural-width cell for the '816. I don't see the '816 gracefully handling 64-bit data (but who knows- didn't the 8088 have to when paired with an FPU?).
Ideally, at least for data, I'd like to abstract away the banks completely, so cross-bank data is handled correctly (using a MVN/MVP instruction). Dr. Jeffyl's idea of having far versions of !/@ sounds closest to what I want.
Not sure if I can abstract away cross-bank jumps in an '816 Forth (meaning if I wanted to use more than one '816 bank, I'd be forced to manually code it into my Forth programs, adding an environmental dependency), but if anyone has any ideas, I'm all ears!
EDIT: Also, A and X/Y size switching. I suspect there's performance and space reasons to do so during character accesses and large sections of code that don't require 16-bit A X/Y.
First the disclaimer: IANAFE (
I Am
Not
A Forth
Expert). However, I do know a bit about using the 65C816 to manipulate data.
Quote:
From what I understand, Forth requires the implementation to access data using addresses which are the same size as the basic cell. This is fine for most processors, but for banked/segmented archs (namely, 65816, 8088, and 286), and more generally archs where address bus width != data bus width, this falls apart.
It would seem that the best way to handle this requirement would be to internally pad addresses to a constant size of 32 bits, even though the '816 is limited to 24 bit addressing. I say this because 24 bit processing with the '816 is cumbersome, whereas 32 bit processing is relatively simple to implement.
Quote:
All solutions provided here sound appealing- I think I'd rather take the code complexity hit and keep performance up by using a natural-width cell for the '816. I don't see the '816 gracefully handling 64-bit data (but who knows- didn't the 8088 have to when paired with an FPU?).
The '816 handles 64 bit data reasonably well—I use 64 bit arithmetic in my integer math routines with little effort.
Quote:
Ideally, at least for data, I'd like to abstract away the banks completely, so cross-bank data is handled correctly (using a MVN/MVP instruction). Dr. Jeffyl's idea of having far versions of !/@ sounds closest to what I want.
MVN and MVP make sense if a large amount of data is to be moved—the copy rate of 7 cycles per byte is indeed rapid when compared to a conventional load/store loop. However, that performance advantage disappears when copying small blocks of data, as the setup code needed to use MVN and MVP has to be executed with every use, and becomes a significant part of the total execution time of the copy operation. Also, MVN and MVP modify DB, which means it may be necessary to preserve DB prior to initiating the copy. Another factor to consider is that the operands to MVN and MVP specifiy the target and source banks (respectively), hence are part of the executable code. You have to overwrite those operands with your banks as part of the setup, which means you are implementing self-modifying code.
Alternatives to using MVN and MVP are the
[<dp>] and
[<dp>],Y addressing modes, which efface bank boundaries. The segmented architecture of the '816 is essentially a limitation on programs (PB is not incremented when PC is wrapped), not data structures, assuming use of proper programming methods. These addressing modes eliminate having to tinker with DB, since the address at
<dp> includes A16-A23.
Quote:
Not sure if I can abstract away cross-bank jumps in an '816 Forth (meaning if I wanted to use more than one '816 bank, I'd be forced to manually code it into my Forth programs, adding an environmental dependency), but if anyone has any ideas, I'm all ears!
It would make sense for the Forth kernal to occupy the first non-zero bank (i.e., bank $01), leaving plenty of space in bank $00 for direct pages and stacks. That arrangement would leave plenty of room above the kernel for dictionaries, transient data, etc., as RAM would be contiguous from $010000 to the top of installed memory.
Quote:
EDIT: Also, A and X/Y size switching. I suspect there's performance and space reasons to do so during character accesses and large sections of code that don't require 16-bit A X/Y.
I've noted a tendency for 6502 programmers who are new to the '816 to stick with what they know, rather than embrace the "'816 philosophy." In much of my code I leave the registers set to 16 bits and only go to eight bits when a specific function requires byte-at-a-time processing, e.g., display I/O. Specifically, there is no performance penalty in implied register operations when they are set to 16 bits, as all ALU operations in the '816 are 16 bit operations. That is to say, INX always consumes two clock cycles, whether .X and .Y are set to 8 bits or 16 bits.