HYPPO mode programs
There is a part of the MEGA65 you see every time you start the system, but you mostly do not spend a lot of time or thought on it: MEGAFLASH and the utilities. They are a special sort of programs, as they are executed in the special hypervisor mode of the MEGA65.
Another difference is that they are not loaded from disk, but reside in the initial memory of the MEGA65. In RAM you can find the onboarding utility and megaflash, in colourram the utilities can be found. HYPPO will find and start them, depending on key presses that are detected on startup.
Memory Layout
To write a utility for this mode, you need to keep some things in mind:
they run in a C64 like mode
HYPPO occupies $8000-$BFFF
there is IO and ROM from D000 upwards (but you might be able to unmap it)
in upper memory other utilities can be found
So you need to keep that in mind and also tell your compiler or assembler to not disturb the HYPPO space, as this will doom your MEGA65 to uncontrollably crash after you exit your utility.
Telling CC65 what to do
For cc65 you can create a config file that will keep those areas free and even abort compiling if you exceeded the space available.
First you need to have a startaddress. As explained we assume C64 mode, so we start at $0801:
FEATURES {
STARTADDRESS: default = $0801;
}
Next we will define some symbols used to define our memory areas. This includes the start of the program, a size for our stack, where HYPPO starts and ends, and where the upper memory starts. But there are also to special symbols, which are normally auto generated by MEMORY definition. They define where the top of the stack is in C64 mode and are called __MAIN_START__
and __MAIN_SIZE__
. Added together this forms the top address of the stack, so we need to make sure that those two add up to $CFFF.
Take a look at cc65’s libsrc/c64/crt0.s
, there you can find the once function which will set initial stack pointer by adding the two MAIN symbols
SYMBOLS {
__LOADADDR__: type = import;
__EXEHDR__: type = import;
__PRGSTART__: type = weak, value = $0801;
__STACKSIZE__: type = weak, value = $400;
__HYPPO__: type = weak, value = $8000; # HYPPO uses $8000-$BFFF
__HYPPOEND__: type = weak, value = $C000;
__HIMEM__: type = weak, value = $D000;
__MAIN_START__: type = weak, value = $0801;
__MAIN_SIZE__: type = weak, value = $c7fe;
}
Now on to the memory definition: here we avoid defining a MAIN area, as we want your symbols to be used. We call the area for our program CODE instead. This starts after LOADADDR and HEADER (which is the two byte PRG load address and the BASIC stub to start the machine language program) and has the size set so that it will stop right before the HYPPO at $7FFF.
ZP, LOADADDR, and HEADER come straight from the default c64 config file shipped with cc65.
BSS (this is essentially space for variables) is placed after HYPPO at $C000 and shares this $1000 bytes of RAM with the stack, which grows down from $CFFF (as explained above).
You can easily change this up: if you need more stack you can place BSS below HYPPO, reducing the available space for CODE instead.
MEMORY {
ZP: file = "", define = yes,
start = $0002, size = $001A;
LOADADDR: file = %O,
start = %S - 2, size = $0002;
HEADER: file = %O, define = yes,
start = %S, size = $000D;
CODE: file = %O,
start = __HEADER_LAST__, size = __HYPPO__ - __HEADER_LAST__;
BSS: file = "",
start = __HYPPOEND__,
size = __HIMEM__ - __HYPPOEND__ - __STACKSIZE__ - 1;
}
Last thing to do is to place the stuff cc65 generates into those MEMORY segments. This is done in the SEGMENTS definition. The main difference to the default cc65 config is that we have replaced MAIN with CODE.