-
Notifications
You must be signed in to change notification settings - Fork 281
Using CC65 on the 65C816
Set the direct page register to 0, keep your stack at 0x100-0x1FF, keep your code and data in bank 0. Compile for 65C02 if you want.
If you configure the system this way then the only thing that will break in 16bit mode is setjmp/longjmp because that assumes that S is 8bit (but see below)
Do not put anything in addresses 0-FF. The compiler will try and use zero page for direct references to that data otherwise. You can use it indirectly with care but be careful as the compiler and assembler are rather smart about ZP references.
The compiler runtime contains a piece of code that assumes zero page is at 0. Replace callax in the library with
callax:
sta jmpvec+1
stx jmpvec+2
jmp (jmpvec+1)
and make sure you have the latest cc65 that correctly saves jmpvec+1/2 on IRQ handling.
There is another heloer 'callptr4' with the same assumptions but it doens't seem to be used.
CC65 links in and also generates self modifying code. This all fails horribly when code and data diverge. There are several offenders in the runtime library and you can find them easily because they put themselves in .data (so they are not packed into ROM on some platforms). Worse yet the compiler generates stores to jmpvec+1/2 and a jump to jmpvec inline.
See libsrc/runtime/*.s
In particular take note of callirq and jmpvec within it. This one is the biggest problem as it is also used directly by the C compiler. condes.c also needs attention if used.
Don't try and link the binary for banks other than zero. The bank registers do all that work for you. Instead link the code and data at conflicting addresses but specify different output files and the linker will do the rest for you nicely.
#
# We map the various bank into different files as if they were at the
# bank 0 addresses. We don't want any far references but to rely upon
# bank registers
#
MEMORY {
#Bank0
RAMZ: start = $0000, size = $0100, type = rw, fill = yes;
STACKS: start = $0100, size = $0300, type = rw, fill = yes;
USEG: start = $0400, size = $FA00, type = rw, fill = yes;
IO: start = $FE00, size = $0100, type = rw, fill = yes;
STUB: start = $FF00, size = $00E0, type = rw, fill = yes;
VECTOR: start = $FFE0, size = $0020, type = rw, fill = yes;
#Bank1
MAIN: file = "fuzix.i", start = $0300, size = $EF00, type = rw, fill
= yes;
#Bank2
# DUMMY is a hack so we don't get udata in ZP for now
DUMMY: file = "fuzix.d", start = $0000, size = $0100, type = rw, fill
= yes;
UDATA: file = "fuzix.d", start = $0100, size = $0200, type = rw, fill
= yes;
BSEG: file = "fuzix.d", start = $0300, size = $FD00, type = rw, fill
= yes;
}
SEGMENTS {
ZEROPAGE: load = RAMZ, type = zp, define = yes;
COMMONDATA: load = UDATA, type = bss;
STACK: load = STACKS, type = rw;
START: load = MAIN, type = ro;
CODE: load = MAIN, type = ro, define = yes;
RODATA: load = BSEG, type = ro;
DATA: load = BSEG, type = rw, define = yes;
DISCARD: load = MAIN, type = ro;
DISCARDDATA: load = BSEG, type = ro;
BSS: load = BSEG, type = bss, define = yes;
STUBS: load = STUB, type = ro;
VECTORS: load = VECTOR, type = ro;
}
FILES {
%O: format = bin;
"fuzix.i": format = bin;
"fuzix.d": format = bin;
}
When running in a single bank the Fuzix kernel and applications are linked at an address above 0x100 with the modified callax given above, and save jmpvec+1/2 on IRQ entry. This gets you most of the way there for a single bank binary.
Stacks are kept page aligned and the longjmp routine is replaced with a routine that checks for a 65c816 and if one is present instead of loading S as an 8bit value from the save area it sets atomically to the existing high byte of S plus the saved low byte. That allows us to hide all the differences inside one library routine and keep the same binaries for both.
If code and data differ (currently only supported for the kernel) then we do a sick hack where we ensure that jmpvec occupies the same reserved addresses in both code and data segments. The code version of the routine does
;
; Hack to deal with CC65 not supporting split I/D properly. It tries
; to generate stores to jmpvec+1/+2 them jsr jmpvec assuming they are
; in fact all in the same bank.
;
; FIXME: we need to save 2:jmpvec+1/+2 across interrupts
;
callax: ; FIXME: optimise
.a8
.i8
sta jmpvec+1
stx jmpvec+2
jmpvec:
.a8
.i8
rep #$10
.i16
ldx jmpvec+1 ; in bank 2 not bank 1
dex ; as rts will inc
phx
sep #$10
.i8
rts
Fuzix: because small is beautiful