In the cc65 toolset, a startup routine must be defined that is executed
when the CPU is reset. This startup code is marked with the STARTUP
segment name, which was defined in the system configuration file as
being in read only memory. The standard convention used in the
predefined libraries is that this code is resident in the crt0 module.
For this custom system, all that needs to be done is to perform a little
bit of 6502 housekeeping, set up the C-level stack pointer, initialize
the memory storage, and call the C-level routine main ()
.
The following code was used for the crt0 module, defined in the file
"crt0.s":
; ---------------------------------------------------------------------------
; crt0.s
; ---------------------------------------------------------------------------
;
; Startup code for cc65 (Single Board Computer version)
.export _init, _exit
.import _main
.export __STARTUP__ : absolute = 1 ; Mark as startup
.import __RAM_START__, __RAM_SIZE__ ; Linker generated
.import copydata, zerobss, initlib, donelib
.include "zeropage.inc"
; ---------------------------------------------------------------------------
; Place the startup code in a special segment
.segment "STARTUP"
; ---------------------------------------------------------------------------
; A little light 6502 housekeeping
_init: LDX #$FF ; Initialize stack pointer to $01FF
TXS
CLD ; Clear decimal mode
; ---------------------------------------------------------------------------
; Set cc65 argument stack pointer
LDA #<(__RAM_START__ + __RAM_SIZE__)
STA sp
LDA #>(__RAM_START__ + __RAM_SIZE__)
STA sp+1
; ---------------------------------------------------------------------------
; Initialize memory storage
JSR zerobss ; Clear BSS segment
JSR copydata ; Initialize DATA segment
JSR initlib ; Run constructors
; ---------------------------------------------------------------------------
; Call main()
JSR _main
; ---------------------------------------------------------------------------
; Back from main (this is also the _exit entry): force a software break
_exit: JSR donelib ; Run destructors
BRK
The following discussion explains the purpose of several important assembler level directives in this file.
.export _init, _exit
This line instructs the assembler that the symbols _init
and
_exit
are to be accessible from other modules. In this
example, _init
is the location that the CPU should jump to when
reset, and _exit
is the location that will be called when the
program is finished.
.import _main
This line instructs the assembler to import the symbol _main
from another module. cc65 names all C-level routines as
{underscore}{name} in assembler, thus the main ()
routine
in C is named _main
in the assembler. This is how the startup
code will link to the C-level code.
.export __STARTUP__ : absolute = 1 ; Mark as startup
This line marks this code as startup code (code that is executed when the processor is reset), which will then be automatically linked into the executable code.
.import __RAM_START__, __RAM_SIZE__ ; Linker generated
This line imports the RAM starting address and RAM size constants, which are used to initialize the cc65 C-level argument stack pointer.
.segment "STARTUP"
This line instructs the assembler that the code is to be placed in the STARTUP segment of memory.
JSR zerobss ; Clear BSS segment
JSR copydata ; Initialize DATA segment
JSR initlib ; Run constructors
These three lines initialize the external (global) and system variables. The first line sets the BSS segment -- the memory locations used for external variables -- to 0. The second line copies the initialization value stored in ROM to the RAM locations used for initialized external variables. The last line runs the constructors that are used to initialize the system run-time variables.
JSR _main
This is the actual call to the C-level main ()
routine,
which is called after the startup code completes.
_exit: JSR donelib ; Run destructors
BRK
This is the code that will be executed when main ()
terminates. The first thing that must be done is run the destructors
via a call to donelib
. Then the program can terminate. In
this example, the program is expected to run forever. Therefore, there
needs to be a way of indicating when something has gone wrong and the
system needs to be shut down, requiring a restart only by a hard reset.
The BRK instruction will be used to indicate a software fault. This is
advantageous because cc65 uses the BRK instruction as the fill byte in
the final binary code. In addition, the hardware has been designed to
force the data lines to $00 for all illegal memory accesses, thereby
also forcing a BRK instruction into the CPU.