Next Previous Contents

2. System Memory Map Definition

The targeted system uses block RAM contained on the XILINX FPGA for the system memory (both RAM and ROM). The block RAMs are available in various aspect ratios, and they will be used in this system as 2K*8 devices. There will be two RAMs used for data storage, starting at location $0000 and growing upwards. There will be one ROM (realized as initialized RAM) used code storage, starting at location $FFFF and growing downwards.

The cc65 toolset requires a memory configuration file to define the memory that is available to the cc65 run-time environment, which is defined as follows:


MEMORY {
    ZP:        start =    $0, size =  $100, type   = rw, define = yes;
    RAM:       start =  $200, size = $0E00, define = yes;
    ROM:       start = $F800, size = $0800, file   = %O;
}

ZP defines the available zero page locations, which in this case starts at $0 and has a length of $100. Keep in mind that certain systems may require access to some zero page locations, so the starting address may need to be adjusted accordingly to prevent cc65 from attempting to reuse those locations. Also, at a minimum, the cc65 run-time environment uses 26 zero page locations, so the smallest zero page size that can be specified is $1A. The usable RAM memory area begins after the 6502 stack storage in page 1, so it is defined as starting at location $200 and filling the remaining 4K of space (4096 - 2 * 256 = 3584 = $0E00). The 2K of ROM space begins at address $F800 and goes to $FFFF (size = $0800).

Next, the memory segments within the memory devices need to be defined. A standard segment definition is used, with one notable exception. The interrupt and reset vector locations need to be defined at locations $FFFA through $FFFF. A special segment named VECTORS is defined that resides at these locations. Later, the interrupt vector map will be created and placed in the VECTORS segment, and the linker will put these vectors at the proper memory locations. The segment definition is:


SEGMENTS {
    ZEROPAGE:  load = ZP,  type = zp,  define   = yes;
    DATA:      load = ROM, type = rw,  define   = yes, run = RAM;
    BSS:       load = RAM, type = bss, define   = yes;
    HEAP:      load = RAM, type = bss, optional = yes;
    STARTUP:   load = ROM, type = ro;
    INIT:      load = ROM, type = ro,  optional = yes;
    CODE:      load = ROM, type = ro;
    RODATA:    load = ROM, type = ro;
    VECTORS:   load = ROM, type = ro,  start    = $FFFA;
}

The meaning of each of these segments is as follows.

ZEROPAGE: Data in page 0, defined by ZP as starting at $0 with length $100

DATA: Initialized data that can be modified by the program, stored in RAM

BSS: Uninitialized data stored in RAM (used for variable storage)

HEAP: Uninitialized C-level heap storage in RAM, optional

STARTUP: The program initialization code, stored in ROM

INIT: The code needed to initialize the system, stored in ROM

CODE: The program code, stored in ROM

RODATA: Initialized data that cannot be modified by the program, stored in ROM

VECTORS: The interrupt vector table, stored in ROM at location $FFFA

A note about initialized data: any variables that require an initial value, such as external (global) variables, require that the initial values be stored in the ROM code image. However, variables stored in ROM cannot change; therefore the data must be moved into variables that are located in RAM. Specifying run = RAM as part of the DATA segment definition will indicate that those variables will require their initialization value to be copied via a call to the copydata routine in the startup assembly code. In addition, there are system level variables that will need to be initialized as well, especially if the heap segment is used via a C-level call to malloc ().

The final section of the definition file contains the data constructors and destructors used for system startup. In addition, if the heap is used, the maximum C-level stack size needs to be defined in order for the system to be able to reliably allocate blocks of memory. The stack size selection must be greater than the maximum amount of storage required to run the program, keeping in mind that the C-level subroutine call stack and all local variables are stored in this stack. The FEATURES section defines the required constructor/destructor attributes and the SYMBOLS section defines the stack size. The constructors will be run via a call to initlib in the startup assembly code and the destructors will be run via an assembly language call to donelib during program termination.


FEATURES {
    CONDES:    segment = STARTUP,
               type    = constructor,
               label   = __CONSTRUCTOR_TABLE__,
               count   = __CONSTRUCTOR_COUNT__;
    CONDES:    segment = STARTUP,
               type    = destructor,
               label   = __DESTRUCTOR_TABLE__,
               count   = __DESTRUCTOR_COUNT__;
}

SYMBOLS {
    # Define the stack size for the application
    __STACKSIZE__:  value = $0200, weak = yes;
}

These definitions are placed in a file named "sbc.cfg" and are referred to during the ld65 linker stage.


Next Previous Contents