Open dansanderson opened 1 year ago
Cool idea from ki-bo: Extend SYS with an explicit MAPL offset parameter, which must be a multiple of 256. When provided, the address must be in the range 2000-7FFF, and the final address is addr + offset. This makes the new PC and the MAPL offset explicit and in control of the caller, at the expense of requiring a bit of understanding of how MAP works, but this would be highly recommended anyway. There's not a good way to hide this behind an abstraction, so instead make it explicit what is happening.
SYS <addr>, [a], [x], [y], [z], [offset]
SYS $2000,,,,,$56100 : REM SYS $58100
SYS $8000,,,,,$50000 : REM ?ILLEGAL QUANTITY ERROR
SYS $2000,,,,,$500FF : REM ?ILLEGAL QUANTITY ERROR
In the first example (the working one), the PC is set to $2000 and MAPL is set to $561(00) with $2000-$7FFF mapped, for an effective address of $58100. The code being called needs to expect both of those register settings, but this way the author knows to expect them because the author is setting them.
The SYS command (via the internal jmp_far routine) uses MAP to access $X.2000-$X.7FFF by setting the MAPL offset to X.0000, based either on the BANK setting or the upper word of the address for large addresses, in the range X = 0 - 5. In order to keep KERNAL ROM, KERNAL variables, character ROM, and I/O registers visible, SYS punts on lower address words outside of that range (e.g. $1600, $8100) for the upper banks, and falls back on unmapped or ROM-mapped regions in bank 0. It always jmp's to the lower word of the requested address after setting MAP.
The MAP offset can actually be any multiple of 256 bytes YYYX.XX00 (where YYY is the 45GS02 extended MAP). When asked to jump to 5.8100, SYS could set the MAPL offset to 05.61(00), map $2000-$3FFF, then jump to $2100. It could do this to reach any address in the system greater than or equal to 0.2000, and could just access 0.0000 - 0.1FFF directly. A major downside of this approach would be that the ML code being called needs to be written to accommodate the offset being used. By restricting SYS to 64K offsets, internal non-relative JMPs can be coded in 16-bit assemblers without knowing how the bank offset is calculated. This keeps the programming model simple, and in line with traditional expectations of Commodore 64 programmers.
Consider a new command, SYSFAR, that can reach any address in the computer using MAPL offsets, with the documented restriction that the ML code being called needs to account for unusual MAPL values. One way this could be accommodated is for the code to always use relative branch instructions (e.g. BRA) within its own code. The 45GS02 offers a 16-bit BRA variant as well.
SYS-called code already has to accommodate the fact that it is running in a mapped region, i.e. it cannot change the MAP register while the PC is in a mapped region. Using SYS to call upper memory should be considered an advanced technique that requires understanding how the memory system works. We might be able to get away with making this the default SYS protocol instead of introducing a new command, but using a new command feels like a good way to make sure the programmer has read the documentation.