Next Previous Contents

3. Startup Code Definition

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.


Next Previous Contents