Safest Zero Page Locations across Multiple Systems
Safest Zero Page Locations across Multiple Systems
Say I want to write a fairly generic routine usable on multiple 6502 systems, such as Apple, Commodore, BBC, Atari, other popular commerical 8-bit systems, and homebrew systems using popular free monitors and other software. If it needs a few zero page locations, where's the safest place to which to default these? Obviously there's no location that will work for everything, but there are probably some that have fewer collisions than others.
It's probably also worth distinguishing locations safely usable only during a single call to a routine, but likely to be destroyed between calls, and locations that will be preserved across multiple calls to a routine, with other "normal" stuff (such as reading characters from/printing characters to the console) happening in between.
It's probably also worth distinguishing locations safely usable only during a single call to a routine, but likely to be destroyed between calls, and locations that will be preserved across multiple calls to a routine, with other "normal" stuff (such as reading characters from/printing characters to the console) happening in between.
Curt J. Sampson - github.com/0cjs
Re: Safest Zero Page Locations across Multiple Systems
On the BBC, locations $00-$8F are reserved for the running language. BASIC uses up to $6F itself, leaving $70-$8F for the user if he wants to merely return to BASIC without rebooting it - but it's permitted to clobber all of that if you're *not* returning to BASIC afterwards. On most BBCs, $90-$9F are also available as they are reserved for the Econet interface, which is comparatively rare in a home system but was fairly common in school machines.
The C64 uses a 6510, which treats locations $00-$01 specially (it's a built-in I/O port). I don't know any further details offhand, but it'd be wise to avoid those two addresses in generic code.
The C64 uses a 6510, which treats locations $00-$01 specially (it's a built-in I/O port). I don't know any further details offhand, but it'd be wise to avoid those two addresses in generic code.
Re: Safest Zero Page Locations across Multiple Systems
I've a feeling from previous excursions that the Beeb and the Apple II have chosen opposite ends of page zero to leave free, which then doesn't give any overlap.
Re: Safest Zero Page Locations across Multiple Systems
BigEd wrote:
I've a feeling from previous excursions that the Beeb and the Apple II have chosen opposite ends of page zero to leave free, which then doesn't give any overlap.
However I can run Applesoft and BBC BASIC in my Ruby which has a more or less Acorn MOS compatible OS that uses a lot of $90->$FF, but Applesoft currently clobbers some of the zero page stuff I'm using for the filing system - it's on my "to do" list to re-assemble Applesoft to be more RubyOS compatible, but quite far-down as it's only for historical purposes.
EhBASIC runs fairly well though - I recall it only uses < $80.
I think the solution though is to just have a well defined block and make that block start (.ORG or whatever directive) easy to change - e.g. a little documentation and a set of named variables/constants at the top of the source file that lets you change the start of Zero page usage, the start of your data usage and the start of your code.
-Gordon
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/
- barrym95838
- Posts: 2056
- Joined: 30 Jun 2013
- Location: Sacramento, CA, USA
Re: Safest Zero Page Locations across Multiple Systems
drogon wrote:
I think the solution though is to just have a well defined block and make that block start (.ORG or whatever directive) easy to change - e.g. a little documentation and a set of named variables/constants at the top of the source file that lets you change the start of Zero page usage, the start of your data usage and the start of your code.
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!
Mike B. (about me) (learning how to github)
Mike B. (about me) (learning how to github)
Re: Safest Zero Page Locations across Multiple Systems
Yes, I strictly avoid $00 and $01 due to the 6510 thing. For larger programs I've been reserving $00-$15 for "BIOS" stuff and starting my stuff at $16, but in this case I was thinking about small routines (such as an I/O driver) that you might want to share amongst multiple applications and might need 2-4 bytes of easy-to-access storage.
Sad to hear that BBC went the other direction from Apple. (Not that there are a lot of addresses free on an Apple II if you want to work with the monitor, Integer and Applesoft BASIC, DOS and ProDOS.) I guess in the end one needs to settle on specific addresses on a platform-by-platform basis, if one wants to fix them to run with multiple applications.
Sad to hear that BBC went the other direction from Apple. (Not that there are a lot of addresses free on an Apple II if you want to work with the monitor, Integer and Applesoft BASIC, DOS and ProDOS.) I guess in the end one needs to settle on specific addresses on a platform-by-platform basis, if one wants to fix them to run with multiple applications.
Curt J. Sampson - github.com/0cjs
Re: Safest Zero Page Locations across Multiple Systems
cjs wrote:
Yes, I strictly avoid $00 and $01 due to the 6510 thing. For larger programs I've been reserving $00-$15 for "BIOS" stuff and starting my stuff at $16, but in this case I was thinking about small routines (such as an I/O driver) that you might want to share amongst multiple applications and might need 2-4 bytes of easy-to-access storage.
Sad to hear that BBC went the other direction from Apple. (Not that there are a lot of addresses free on an Apple II if you want to work with the monitor, Integer and Applesoft BASIC, DOS and ProDOS.) I guess in the end one needs to settle on specific addresses on a platform-by-platform basis, if one wants to fix them to run with multiple applications.
Sad to hear that BBC went the other direction from Apple. (Not that there are a lot of addresses free on an Apple II if you want to work with the monitor, Integer and Applesoft BASIC, DOS and ProDOS.) I guess in the end one needs to settle on specific addresses on a platform-by-platform basis, if one wants to fix them to run with multiple applications.
The Acorn approach is quite sensible, but it came after all the MS Basics (Apple, OSI, PET), so while they had a lot to look at and possibly base their own thing on, the also had the benefit of a fresh start and their setup worked over all BBC Micros - Beeb, Beeb64K, Master, Electron, Master compact (and whatever else)
Zero page $00 through $9F is for the current application (e.g. BASIC, or a word processor, spreadsheet, or other programming language, etc. which normally lives in $8000 through $BFFF) with $90 through $FF being reserved for the operating system. (BASIC itself reserves $80 through $8F for user programs which is handy because BASIC has a built in assembler)
The OS also claims RAM from $200 through $DFF , however $400 through $7FF are reserved for the current application (e.g. BASIC, wordprocessor, etc.). $800 through $DFF are reserved for buffers for various bits of the OS - sound and video processing, cassette tape buffers, keyboard, function key expansions, user-definable fonts and some other uses.
Utility ROMs can extend the value of PAGE (which starts at $E00) for private workspace - so e.g. a filing system ROM can reserve some private space for buffers and so on - typically the value of PAGE would be $1900 in a system using the Advanced Disk Filing System. So BASIC, on entry, would read the value of PAGE from the OS then use that as the start of program data. The upper value being the bottom of screen memory which started at $7FFF and grew downwards.
So I think that starting fresh put them in a good position to make the system expandable and usable by many programming languages, filing systems, and so on - as long as you stuck to the rules - stick a 2nd processor onto it and (if it's a 6502!) everything gets invisibly copied over and PAGE starts low ($200 IIRC) and the top of memory is way up there - $8000 or even higher if a "Hi BASIC" version is used.
These days - it's easy to start again, and we end up with XKCD 927 https://xkcd.com/927/ or just be prepared to be flexible....
Cheers,
-Gordon
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/
- floobydust
- Posts: 1394
- Joined: 05 Mar 2013
Re: Safest Zero Page Locations across Multiple Systems
Well, Page zero is always a challenge on the 65(C)02. Most of my early experience was with the Commodore machines... Vic-20 and C64. Both of those had very little page zero space free.
Fortunately, when building my own systems, I'm in control, so for better or worse, I tend to structure the memory map for what I can consider logical. For Page zero, my BIOS occupies the top 32 bytes, from $E0 - $FF. My Monitor uses 48 bytes just below the BIOS, from $B0 - $DF. As a result, Page zero from $B0 - $FF is already allocated. My CMOS version of Enhanced Basic uses Page zero from $00 - $85, which is less than the normal Enhanced Basic, as I keep the CHRGOT equivalent routine in ROM, not in Page zero. It's also contiguous space, where the original Enhanced Basic has usage spread throughout Page zero, making it difficult to get it working with any system. J.G. Hartson did an initial condensed page zero allocation for EhBasic (many thanks for this), which I used as a starting point and I made changes to that for my CMOS version.
I also find it useful to use some of the new instructions which Rockwell provided and were later added by WDC, namely, BBSx, BBRx, SMBx, RMBx. I use these quite a bit in my BIOS and Monitor. These allow me to use a single Page zero location for multiple flag bits easily for BIOS functions and Monitor routines, which can save multiple Page zero locations.
As for the rest of my preferred mapping, I use Page $FF for ROM and start with a JUMP table for BIOS functions. Page $FE is for I/O addressing. Based on this, one could actually have upwards for 63.5KB of RAM, provided you can bootstrap some loader in the top 250 bytes, or use some other method to load some functioning code.
Fortunately, when building my own systems, I'm in control, so for better or worse, I tend to structure the memory map for what I can consider logical. For Page zero, my BIOS occupies the top 32 bytes, from $E0 - $FF. My Monitor uses 48 bytes just below the BIOS, from $B0 - $DF. As a result, Page zero from $B0 - $FF is already allocated. My CMOS version of Enhanced Basic uses Page zero from $00 - $85, which is less than the normal Enhanced Basic, as I keep the CHRGOT equivalent routine in ROM, not in Page zero. It's also contiguous space, where the original Enhanced Basic has usage spread throughout Page zero, making it difficult to get it working with any system. J.G. Hartson did an initial condensed page zero allocation for EhBasic (many thanks for this), which I used as a starting point and I made changes to that for my CMOS version.
I also find it useful to use some of the new instructions which Rockwell provided and were later added by WDC, namely, BBSx, BBRx, SMBx, RMBx. I use these quite a bit in my BIOS and Monitor. These allow me to use a single Page zero location for multiple flag bits easily for BIOS functions and Monitor routines, which can save multiple Page zero locations.
As for the rest of my preferred mapping, I use Page $FF for ROM and start with a JUMP table for BIOS functions. Page $FE is for I/O addressing. Based on this, one could actually have upwards for 63.5KB of RAM, provided you can bootstrap some loader in the top 250 bytes, or use some other method to load some functioning code.
Regards, KM
https://github.com/floobydust
https://github.com/floobydust
Re: Safest Zero Page Locations across Multiple Systems
Hi!
On the Atari 8-bit computers the OS reserves half the zero page to itself, locations from $00 to $7F. Also, it you plan to call into the math-pack, it will use locations from $D4 up to $FF (but not all math-pack functions use all that).
If you want your assembly code to be callable from BASIC, you also need to preserve all the BASIC variables, so you only have from $CB to $D3 available - but you can use various ZP locations from the math-pack in that case, for example $D4 and $D5 holds the 16 bit result passed to BASIC from an ASM call.
I have to ask, why do you want fixed ZP locations? If you plan to distribute only binary code, you would also need a fixed absolute location for the code. If you have the source, just redefining the ZP locations addresses would work. And as any I/O will be very different from system to system, it is really hard to write portable binary code.
Have Fun!
cjs wrote:
Say I want to write a fairly generic routine usable on multiple 6502 systems, such as Apple, Commodore, BBC, Atari, other popular commerical 8-bit systems, and homebrew systems using popular free monitors and other software. If it needs a few zero page locations, where's the safest place to which to default these? Obviously there's no location that will work for everything, but there are probably some that have fewer collisions than others.
If you want your assembly code to be callable from BASIC, you also need to preserve all the BASIC variables, so you only have from $CB to $D3 available - but you can use various ZP locations from the math-pack in that case, for example $D4 and $D5 holds the 16 bit result passed to BASIC from an ASM call.
I have to ask, why do you want fixed ZP locations? If you plan to distribute only binary code, you would also need a fixed absolute location for the code. If you have the source, just redefining the ZP locations addresses would work. And as any I/O will be very different from system to system, it is really hard to write portable binary code.
Have Fun!
Re: Safest Zero Page Locations across Multiple Systems
dmsc wrote:
I have to ask, why do you want fixed ZP locations? If you plan to distribute only binary code, you would also need a fixed absolute location for the code. If you have the source, just redefining the ZP locations addresses would work. And as any I/O will be very different from system to system, it is really hard to write portable binary code.
(As a side note, this illuminated for me one of the nice things about the 6800 design: in this style of ABI one can load the 16-bit X index register with the base address of the parameter block, use single-byte static offsets from X to set and read the various parameters, call the routine with that address still in X, and the subroutine would also use static single-byte offsets from X to access those parameters. I still doubt that makes up for having only one index register, though. :-) (The 6502 didn't exactly have multiple index registers usable for copying data, but the ZP,Y addressing mode basically gave you the same thing.))
This all actually makes even more sense for the upper layers: DOS calls also need a fair amount of information, particularly pointers to things, so using a parameter block there makes sense, too and, again, on the 6502 the zero page is the most efficient place to put such a pointer to pass between two routines. Here you really want to be able to have a common location if possible because it would be nice to be able to use the same DOS-using machine-language subroutines from different applications ("langauges," as the BBC OS refers to them) such as Integer BASIC, Applesoft BASIC, Pascal, KRUSADER, whatever.
Clearly this isn't going to happen across all machines, but perhaps one can come up with constant locations for such pointers for individual families: Apple, BBC, Commodore, etc.
Curt J. Sampson - github.com/0cjs
Re: Safest Zero Page Locations across Multiple Systems
cjs wrote:
...passing this as a pointer in a zero page location is clearly more efficient than, e.g., passing the parameter block address in A:X or, slower yet, pushing parameters on the stack.
Re: Safest Zero Page Locations across Multiple Systems
BigEd wrote:
cjs wrote:
...passing this as a pointer in a zero page location is clearly more efficient than, e.g., passing the parameter block address in A:X or, slower yet, pushing parameters on the stack.
That said, there are a few situations where, even for long-running functions, latency to the next call might be critical for performance. For example, in the Block Device Interface I mentioned above, if after reading a block from rotating media you find the next block you want is on the same track but you wait to long to call again to read it, you're going to have to wait until the appropriate sector(s) come around again. (This is a pretty classic disk performance problem, and the reason for interleaving sectors on disks.)
On a floppy disk with standard formatting or something similar, I calculate the gap between the CRC of a sector data field and the start of the sync just before the next sector's index address mark to be about 30 microseconds, or 30 clock cycles on a 1 MHz 6502. I'm toying with ideas for calling the block read routine again quickly enough that you could read the subsequent block/sector(s) without waiting for a full disk rotation, so when you have contiguous sectors allocated to a file you could read them in a single disk revolution, though that's probably not practical, but in such a case the calling convention might make all the difference in whether or not you could do that.
But if you interleave your sectors you have more time; well over 200 cycles for a 2:1 interleave and close to 500 for a 3:1 interleave. If you can use 2:1 instead of 3:1, you speed up loading of a track by about 33%.
Quote:
(It might be worth noting that Acorn's MOS OSWORD family of calls passes the parameter block address in YX and the call type in A. By this means a single call site gives access to a large number of facilities.)
Curt J. Sampson - github.com/0cjs
Re: Safest Zero Page Locations across Multiple Systems
The MOS approach is easy to implement. Your routine knows a zero-page location to use as a trampoline, and is passed the parameter block in Y and X. It takes only two instructions, four bytes, six cycles to move Y and X to zero-page. As a generic approach, this is hard to beat on the 6502.
The '816 has more addressing modes which make this considerably easier, even if you don't make use of the Direct Page register, because you can now sensibly pass parameters on the stack. Not only is the stack no longer limited to 256 bytes, but you can perform stack-relative addressing including indirects. Caveat: there is no stack-indirect-long addressing mode, so the DBR supplies the high byte of the indirect address implicitly.
The '816 has more addressing modes which make this considerably easier, even if you don't make use of the Direct Page register, because you can now sensibly pass parameters on the stack. Not only is the stack no longer limited to 256 bytes, but you can perform stack-relative addressing including indirects. Caveat: there is no stack-indirect-long addressing mode, so the DBR supplies the high byte of the indirect address implicitly.
Re: Safest Zero Page Locations across Multiple Systems
Chromatix wrote:
The MOS approach is easy to implement. Your routine knows a zero-page location to use as a trampoline, and is passed the parameter block in Y and X.
In my initial stab at an API I put the function code in X because then one could use a sequence of DEX, BEQ instructions to dispatch. (I was assuming just a few fixed functions, with the most-often used one being 1, the next being 2, etc.) If you have a large number of functions I suppose you'd probably want to put the code in A so you could shift it left, and use that as an index into a jump list in one of at least two ways.
But if you don't have considerations like that? (E.g., if the code, if any, is in the parameter block?)
Curt J. Sampson - github.com/0cjs
Re: Safest Zero Page Locations across Multiple Systems
cjs wrote:
Chromatix wrote:
The MOS approach is easy to implement. Your routine knows a zero-page location to use as a trampoline, and is passed the parameter block in Y and X.
In my initial stab at an API I put the function code in X because then one could use a sequence of DEX, BEQ instructions to dispatch. (I was assuming just a few fixed functions, with the most-often used one being 1, the next being 2, etc.) If you have a large number of functions I suppose you'd probably want to put the code in A so you could shift it left, and use that as an index into a jump list in one of at least two ways.
But if you don't have considerations like that? (E.g., if the code, if any, is in the parameter block?)
Code: Select all
; All "os variables" are accessed the same way:
; new = (old AND Y) EOR X
; so set Y to zero to set the value, otherwise use it as a mask.Then finally osWord which uses X (low byte) and Y (high byte) to point to a parameter block with A containing the code. e.g. osWord 0 is "get a line of text" and the parameter block containing the address, max length and character ranges. Your data block could be anywhere in RAM. Ruby OS which is mostly Acorn MOS compatible has this, but internally has a short-cut which does:
Code: Select all
_getline:
ldx #<_getlineData
ldy #>_getlineData
lda #0
jmp osWord
_getlineData:
.word keyboardIn ; Address of input buffer
.byte 250 ; Max length
.byte 32 ; Smallest value to accept
.byte 126 ; largest...The filing system "API" is more akin to osWord and again starts from the simple "whole file at a time" up to seek, get/put bytes and fiddle with file attributes and so on. osFile (while file at a time) has X (low) and Y (high) as the pointer to the data block with the command in A - that's the same as osWord in essence. The first word in the osFile parameter block is the address of the data to write from or read into.
Code: Select all
; Offsets into the osFile parameter block
fName = $00
lAddress = $02 ; Load address
xAddress = $06 ; Exec address
sAddress = $0A ; Start address for save, length for load
eAddress = $0E ; End address for save or attributesHope that's of some use..
Cheers,
-Gordon
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/