--- /dev/null
+<!doctype linuxdoc system>
+
+<article>
+<title>Defining a Custom cc65 Target
+<author>Bruce Reidenbach
+<date>2010-02-22
+
+<abstract>
+This section provides step-by-step instructions on how to use the cc65
+toolset for a custom hardware platform (a target system not currently
+supported by the cc65 library set).
+</abstract>
+
+<!-- Table of contents -->
+
+<toc>
+
+<!-- Begin the document -->
+
+<sect>Overview<p>
+
+The cc65 toolset provides a set of pre-defined libraries that allow the
+user to target the executable image to a variety of hardware platforms.
+In addition, the user can create a customized environment so that the
+executable can be targeted to a custom platform. The following
+instructions provide step-by-step instructions on how to customize the
+toolset for a target that is not supported by the standard cc65
+installation.
+
+The platform used in this example is a Xilinx Field Programmable Gate
+Array (FPGA) with an embedded 65C02 core. The processor core supports
+the additional opcodes/addressing modes of the 65SC02, along with the
+STP and WAI instructions. These instructions will create a set of files
+to create a custom target, named SBC, for <bf>S</bf>ingle <bf>B</bf>oard
+<bf>C</bf>omputer.
+
+<sect>System Memory Map Definition<p>
+
+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:
+
+<tscreen><code>
+MEMORY {
+ ZP: start = $0, size = $100, type = rw, define = yes;
+ RAM: start = $200, size = $0E00, define = yes;
+ ROM: start = $F800, size = $0800, file = %O;
+}
+</code></tscreen>
+
+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:
+
+<tscreen><code>
+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;
+}
+</code></tscreen>
+
+The meaning of each of these segments is as follows.
+
+<p><tt> ZEROPAGE: </tt>Data in page 0, defined by ZP as starting at $0 with length $100
+<p><tt> DATA: </tt>Initialized data that can be modified by the program, stored in RAM
+<p><tt> BSS: </tt>Uninitialized data stored in RAM (used for variable storage)
+<p><tt> HEAP: </tt>Uninitialized C-level heap storage in RAM, optional
+<p><tt> STARTUP: </tt>The program initialization code, stored in ROM
+<p><tt> INIT: </tt>The code needed to initialize the system, stored in ROM
+<p><tt> CODE: </tt>The program code, stored in ROM
+<p><tt> RODATA: </tt>Initialized data that cannot be modified by the program, stored in ROM
+<p><tt> VECTORS: </tt>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 <tt>run = RAM</tt> 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
+<tt>copydata</tt> 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
+<tt>malloc ()</tt>.
+
+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
+<tt>FEATURES</tt> section defines the required constructor/destructor
+attributes and the <tt>SYMBOLS</tt> section defines the stack size. The
+constructors will be run via a call to <tt>initlib</tt> in the startup
+assembly code and the destructors will be run via an assembly language
+call to <tt>donelib</tt> during program termination.
+
+<tscreen><code>
+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;
+}
+</code></tscreen>
+
+These definitions are placed in a file named "sbc.cfg"
+and are referred to during the ld65 linker stage.
+
+<sect>Startup Code Definition<p>
+
+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 <tt>main ()</tt>.
+The following code was used for the crt0 module, defined in the file
+"crt0.s":
+
+<tscreen><code>
+; ---------------------------------------------------------------------------
+; 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
+</code></tscreen>
+
+The following discussion explains the purpose of several important
+assembler level directives in this file.
+
+<tscreen><verb>
+.export _init, _exit
+</verb></tscreen>
+
+This line instructs the assembler that the symbols <tt>_init</tt> and
+<tt>_exit</tt> are to be accessible from other modules. In this
+example, <tt>_init</tt> is the location that the CPU should jump to when
+reset, and <tt>_exit</tt> is the location that will be called when the
+program is finished.
+
+<tscreen><verb>
+.import _main
+</verb></tscreen>
+
+This line instructs the assembler to import the symbol <tt>_main</tt>
+from another module. cc65 names all C-level routines as
+{underscore}{name} in assembler, thus the <tt>main ()</tt> routine
+in C is named <tt>_main</tt> in the assembler. This is how the startup
+code will link to the C-level code.
+
+<tscreen><verb>
+.export __STARTUP__ : absolute = 1 ; Mark as startup
+</verb></tscreen>
+
+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.
+
+<tscreen><verb>
+.import __RAM_START__, __RAM_SIZE__ ; Linker generated
+</verb></tscreen>
+
+This line imports the RAM starting address and RAM size constants, which
+are used to initialize the cc65 C-level argument stack pointer.
+
+<tscreen><verb>
+.segment "STARTUP"
+</verb></tscreen>
+
+This line instructs the assembler that the code is to be placed in the
+STARTUP segment of memory.
+
+<tscreen><verb>
+ JSR zerobss ; Clear BSS segment
+ JSR copydata ; Initialize DATA segment
+ JSR initlib ; Run constructors
+</verb></tscreen>
+
+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.
+
+<tscreen><verb>
+ JSR _main
+</verb></tscreen>
+
+This is the actual call to the C-level <tt>main ()</tt> routine,
+which is called after the startup code completes.
+
+<tscreen><verb>
+_exit: JSR donelib ; Run destructors
+ BRK
+</verb></tscreen>
+
+This is the code that will be executed when <tt>main ()</tt>
+terminates. The first thing that must be done is run the destructors
+via a call to <tt>donelib</tt>. 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.
+
+<sect>Custom Run-Time Library Creation<p>
+
+The next step in customizing the cc65 toolset is creating a run-time
+library for the targeted hardware. The easiest way to do this is to
+modify a standard library from the cc65 distribution. In this example,
+there is no console I/O, mouse, joystick, etc. in the system, so it is
+most appropriate to use the simplest library as the base, which is for
+the Watara Supervision and is named "supervision.lib" in the
+lib directory of the distribution.
+
+The only modification required is to replace the <tt>crt0</tt> module in
+the supervision.lib library with custom startup code. This is simply
+done by first copying the library and giving it a new name, compiling
+the startup code with ca65, and finally using the ar65 archiver to
+replace the module in the new library. The steps are shown below:
+
+<tscreen><verb>
+$ copy "C:\Program Files\cc65\lib\supervision.lib" sbc.lib
+$ ca65 crt0.s
+$ ar65 a sbc.lib crt0.o
+</verb></tscreen>
+
+<sect>Interrupt Service Routine Definition<p>
+
+For this system, the CPU is put into a wait condition prior to allowing
+interrupt processing. Therefore, the interrupt service routine is very
+simple: return from all valid interrupts. However, as mentioned
+before, the BRK instruction is used to indicate a software fault, which
+will call the same interrupt service routine as the maskable interrupt
+signal IRQ. The interrupt service routine must be able to tell the
+difference between the two, and act appropriately.
+
+The interrupt service routine shown below includes code to detect when a
+BRK instruction has occurred and stops the CPU from further processing.
+The interrupt service routine is in a file named
+"interrupt.s".
+
+<tscreen><code>
+; ---------------------------------------------------------------------------
+; interrupt.s
+; ---------------------------------------------------------------------------
+;
+; Interrupt handler.
+;
+; Checks for a BRK instruction and returns from all valid interrupts.
+
+.import _stop
+.export _irq_int, _nmi_int
+
+.segment "CODE"
+
+.PC02 ; Force 65C02 assembly mode
+
+; ---------------------------------------------------------------------------
+; Non-maskable interrupt (NMI) service routine
+
+_nmi_int: RTI ; Return from all NMI interrupts
+
+; ---------------------------------------------------------------------------
+; Maskable interrupt (IRQ) service routine
+
+_irq_int: PHX ; Save X register contents to stack
+ TSX ; Transfer stack pointer to X
+ PHA ; Save accumulator contents to stack
+ INX ; Increment X so it points to the status
+ INX ; register value saved on the stack
+ LDA $100,X ; Load status register contents
+ AND #$10 ; Isolate B status bit
+ BNE break ; If B = 1, BRK detected
+
+; ---------------------------------------------------------------------------
+; IRQ detected, return
+
+irq: PLA ; Restore accumulator contents
+ PLX ; Restore X register contents
+ RTI ; Return from all IRQ interrupts
+
+; ---------------------------------------------------------------------------
+; BRK detected, stop
+
+break: JMP _stop ; If BRK is detected, something very bad
+ ; has happened, so stop running
+</code></tscreen>
+
+The following discussion explains the purpose of several important
+assembler level directives in this file.
+
+<tscreen><verb>
+.import _stop
+</verb></tscreen>
+
+This line instructs the assembler to import the symbol <tt>_stop</tt>
+from another module. This routine will be called if a BRK instruction
+is encountered, signaling a software fault.
+
+<tscreen><verb>
+.export _irq_int, _nmi_int
+</verb></tscreen>
+
+This line instructs the assembler that the symbols <tt>_irq_int</tt> and
+<tt>_nmi_int</tt> are to be accessible from other modules. In this
+example, the address of these symbols will be placed in the interrupt
+vector table.
+
+<tscreen><verb>
+.segment "CODE"
+</verb></tscreen>
+
+This line instructs the assembler that the code is to be placed in the
+CODE segment of memory. Note that because there are 65C02 mnemonics in
+the assembly code, the assembler is forced to use the 65C02 instruction
+set with the <tt>.PC02</tt> directive.
+
+The final step is to define the interrupt vector memory locations.
+Recall that a segment named VECTORS was defined in the memory
+configuration file, which started at location $FFFA. The addresses of
+the interrupt service routines from "interrupt.s" along with
+the address for the initialization code in crt0 are defined in a file
+named "vectors.s". Note that these vectors will be placed in
+memory in their proper little-endian format as:
+
+<p><tt> $FFFA - $FFFB:</tt> NMI interrupt vector (low byte, high byte)
+<p><tt> $FFFC - $FFFD:</tt> Reset vector (low byte, high byte)
+<p><tt> $FFFE - $FFFF:</tt> IRQ/BRK interrupt vector (low byte, high byte)
+
+using the <tt>.addr</tt> assembler directive. The contents of the file are:
+
+<tscreen><code>
+; ---------------------------------------------------------------------------
+; vectors.s
+; ---------------------------------------------------------------------------
+;
+; Defines the interrupt vector table.
+
+.import _init
+.import _nmi_int, _irq_int
+
+.segment "VECTORS"
+
+.addr _nmi_int ; NMI vector
+.addr _init ; Reset vector
+.addr _irq_int ; IRQ/BRK vector
+</code></tscreen>
+
+The cc65 toolset will replace the address symbols defined here with the
+actual addresses of the routines during the link process.
+
+<sect>Adding Custom Instructions<p>
+
+The cc65 instruction set only supports the WAI (Wait for Interrupt) and
+STP (Stop) instructions when used with the 65816 CPU (accessed via the
+--cpu command line option of the ca65 macro assembler). The 65C02 core
+used in this example supports these two instructions, and in fact the
+system benefits from the use of both the WAI and STP instructions.
+
+In order to use the WAI instruction in this case, a C routine named
+"wait" was created that consists of the WAI opcode followed by
+a subroutine return. It was convenient in this example to put the IRQ
+interrupt enable in this subroutine as well, since interrupts should
+only be enabled when the code is in this wait condition.
+
+For both the WAI and STP instructions, the assembler is
+"fooled" into placing those opcodes into memory by inserting a
+single byte of data that just happens to be the opcode for those
+instructions. The assembly code routines are placed in a file, named
+"wait.s", which is shown below:
+
+<tscreen><code>
+; ---------------------------------------------------------------------------
+; wait.s
+; ---------------------------------------------------------------------------
+;
+; Wait for interrupt and return
+
+.export _wait, _stop
+
+; ---------------------------------------------------------------------------
+; Wait for interrupt: Forces the assembler to emit a WAI opcode ($CB)
+; ---------------------------------------------------------------------------
+
+.segment "CODE"
+
+.proc _wait: near
+
+ CLI ; Enable interrupts
+.byte $CB ; Inserts a WAI opcode
+ RTS ; Return to caller
+
+.endproc
+
+; ---------------------------------------------------------------------------
+; Stop: Forces the assembler to emit a STP opcode ($DB)
+; ---------------------------------------------------------------------------
+
+.proc _stop: near
+
+.byte $DB ; Inserts a STP opcode
+
+.endproc
+</code></tscreen>
+
+The label <tt>_wait</tt>, when exported, can be called by using the
+<tt>wait ()</tt> subroutine call in C. The section is marked as
+code so that it will be stored in read-only memory, and the procedure is
+tagged for 16-bit absolute addressing via the "near"
+modifier. Similarly, the <tt>_stop</tt> routine can be called from
+within the C-level code via a call to <tt>stop ()</tt>. In
+addition, the routine can be called from assembly code by calling
+<tt>_stop</tt> (as was done in the interrupt service routine).
+
+<sect>Hardware Drivers<p>
+
+Oftentimes, it can be advantageous to create small application helpers
+in assembly language to decrease codespace and increase execution speed
+of the overall program. An example of this would be the transfer of
+characters to a FIFO (<bf>F</bf>irst-<bf>I</bf>n,
+<bf>F</bf>irst-<bf>O</bf>ut) storage buffer for transmission over a
+serial port. This simple action could be performed by an assembly
+language driver which would execute much quicker than coding it in C.
+The following discussion outlines a method of interfacing a C program
+with an assembly language subroutine.
+
+The first step in creating the assembly language code for the driver is
+to determine how to pass the C arguments to the assembly language
+routine. The cc65 toolset allows the user to specify whether the data
+is passed to a subroutine via the stack or by the processor registers by
+using the <tt>__fastcall__</tt> function declaration (note that there
+are two underscore characters in front of and two behind the
+<tt>fastcall</tt> declaration). When <tt>__fastcall__</tt> is
+specified, the rightmost argument in the function call is passed to the
+subroutine using the 6502 registers instead of the stack. Note that if
+there is only one argument in the function call, the execution overhead
+required by the stack interface routines is completely avoided.
+
+Without <tt>__fastcall__</tt>, the argument is loaded in the A and X
+registers and then pushed onto the stack via a call to <tt>pushax</tt>.
+The first thing the subroutine does is retrieve the argument from the
+stack via a call to <tt>ldax0sp</tt>, which copies the values into the A
+and X. When the subroutine is finished, the values on the stack must be
+popped off and discarded via a jump to <tt>incsp2</tt>, which includes
+the RTS subroutine return command. This is shown in the following code
+sample.
+
+Calling sequence:
+
+<tscreen><verb>
+ lda #<(L0001) ; Load A with the high order byte
+ ldx #>(L0001) ; Load X with the low order byte
+ jsr pushax ; Push A and X onto the stack
+ jsr _foo ; Call foo, i.e., foo (arg)
+</verb></tscreen>
+
+Subroutine code:
+
+<tscreen><verb>
+_foo: jsr ldax0sp ; Retrieve A and X from the stack
+ sta ptr ; Store A in ptr
+ stx ptr+1 ; Store X in ptr+1
+ ... ; (more subroutine code goes here)
+ jmp incsp2 ; Pop A and X from the stack (includes return)
+</verb></tscreen>
+
+If <tt>__fastcall__</tt> is specified, the argument is loaded into the A
+and X registers as before, but the subroutine is then called
+immediately. The subroutine does not need to retrieve the argument
+since the value is already available in the A and X registers.
+Furthermore, the subroutine can be terminated with an RTS statement
+since there is no stack cleanup which needs to be performed. This is
+shown in the following code sample.
+
+Calling sequence:
+
+<tscreen><verb>
+ lda #<(L0001) ; Load A with the high order byte
+ ldx #>(L0001) ; Load X with the low order byte
+ jsr _foo ; Call foo, i.e., foo (arg)
+</verb></tscreen>
+
+Subroutine code:
+
+<tscreen><verb>
+_foo: sta ptr ; Store A in ptr
+ stx ptr+1 ; Store X in ptr+1
+ ... ; (more subroutine code goes here)
+ rts ; Return from subroutine
+</verb></tscreen>
+
+The hardware driver in this example writes a string of character data to
+a hardware FIFO located at memory location $1000. Each character is
+read and is compared to the C string termination value ($00), which will
+terminate the loop. All other character data is written to the FIFO.
+For convenience, a carriage return/line feed sequence is automatically
+appended to the serial stream. The driver defines a local pointer
+variable which is stored in the zero page memory space in order to allow
+for retrieval of each character in the string via the indirect indexed
+addressing mode.
+
+The assembly language routine is stored in a file names
+"rs232_tx.s" and is shown below:
+
+<tscreen><code>
+; ---------------------------------------------------------------------------
+; rs232_tx.s
+; ---------------------------------------------------------------------------
+;
+; Write a string to the transmit UART FIFO
+
+.export _rs232_tx
+.exportzp _rs232_data: near
+
+.define TX_FIFO $1000 ; Transmit FIFO memory location
+
+.zeropage
+
+_rs232_data: .res 2, $00 ; Reserve a local zero page pointer
+
+.segment "CODE"
+
+.proc _rs232_tx: near
+
+; ---------------------------------------------------------------------------
+; Store pointer to zero page memory and load first character
+
+ sta _rs232_data ; Set zero page pointer to string address
+ stx _rs232_data+1 ; (pointer passed in via the A/X registers)
+ ldy #00 ; Initialize Y to 0
+ lda (_rs232_data) ; Load first character
+
+; ---------------------------------------------------------------------------
+; Main loop: read data and store to FIFO until \0 is encountered
+
+loop: sta TX_FIFO ; Loop: Store character in FIFO
+ iny ; Increment Y index
+ lda (_rs232_data),y ; Get next character
+ bne loop ; If character == 0, exit loop
+
+; ---------------------------------------------------------------------------
+; Append CR/LF to output stream and return
+
+ lda #$0D ; Store CR
+ sta TX_FIFO
+ lda #$0A ; Store LF
+ sta TX_FIFO
+ rts ; Return
+
+.endproc
+</code></tscreen>
+
+<sect>Hello World! Example<p>
+
+The following short example demonstrates programming in C using the cc65
+toolset with a custom run-time environment. In this example, a Xilinx
+FPGA contains a UART which is connected to a 65c02 processor with FIFO
+(<bf>F</bf>irst-<bf>I</bf>n, <bf>F</bf>irst-<bf>O</bf>ut) storage to
+buffer the data. The C program will wait for an interrupt generated by
+the receive UART and then respond by transmitting the string "Hello
+World! " every time a question mark character is received via a
+call to the hardware driver <tt>rs232_tx ()</tt>. The driver
+prototype uses the <tt>__fastcall__</tt> extension to indicate that the
+driver does not use the stack. The FIFO data interface is at address
+$1000 and is defined as the symbolic constant <tt>FIFO_DATA</tt>.
+Writing to <tt>FIFO_DATA</tt> transfers a byte of data into the transmit
+FIFO for subsequent transmission over the serial interface. Reading
+from <tt>FIFO_DATA</tt> transfers a byte of previously received data out
+of the receive FIFO. The FIFO status data is at address $1001 and is
+defined as the symbolic constant <tt>FIFO_STATUS</tt>. For convenience,
+the symbolic constants <tt>TX_FIFO_FULL</tt> (which isolates bit 0 of
+the register) and <tt>RX_FIFO_EMPTY</tt> (which isolates bit 1 of the
+register) have been defined to read the FIFO status.
+
+The following C code is saved in the file "main.c". As this
+example demonstrates, the run-time environment has been set up such that
+all of the behind-the-scene work is transparent to the user.
+
+<tscreen><code>
+#define FIFO_DATA (*(unsigned char *) 0x1000)
+#define FIFO_STATUS (*(unsigned char *) 0x1001)
+
+#define TX_FIFO_FULL (FIFO_STATUS & 0x01)
+#define RX_FIFO_EMPTY (FIFO_STATUS & 0x02)
+
+extern void wait ();
+extern void __fastcall__ rs232_tx (char *str);
+
+int main () {
+ while (1) { // Run forever
+ wait (); // Wait for an RX FIFO interrupt
+
+ while (RX_FIFO_EMPTY == 0) { // While the RX FIFO is not empty
+ if (FIFO_DATA == '?') { // Does the RX character = '?'
+ rs232_tx ("Hello World!"); // Transmit "Hello World!"
+ } // Discard any other RX characters
+ }
+ }
+
+ return (0); // We should never get here!
+}
+</code></tscreen>
+
+<sect>Putting It All Together<p>
+
+The following commands will create a ROM image named "a.out"
+that can be used as the initialization data for the Xilinx Block RAM
+used for code storage:
+
+<tscreen><verb>
+$ cc65 -t none -O --cpu 65sc02 main.c
+$ ca65 --cpu 65sc02 main.s
+$ ca65 --cpu 65sc02 rs232_tx.s
+$ ca65 --cpu 65sc02 interrupt.s
+$ ca65 --cpu 65sc02 vectors.s
+$ ca65 --cpu 65sc02 wait.s
+$ ld65 -C sbc.cfg -m main.map interrupt.o vectors.o wait.o rs232_tx.o
+ main.o sbc.lib
+</verb></tscreen>
+
+During the C-level code compilation phase (<tt>cc65</tt>), assumptions
+about the target system are disabled via the <tt>-t none</tt> command
+line option. During the object module linker phase (<tt>ld65</tt>), the
+target customization is enabled via inclusion of the <tt>sbc.lib</tt>
+file and the selection of the configuration file via the <tt>-C
+sbc.cfg</tt> command line option.
+
+The 65C02 core used most closely matches the cc65 toolset processor
+named 65SC02 (the 65C02 extensions without the bit manipulation
+instructions), so all the commands specify the use of that processor via
+the <tt>--cpu 65sc02</tt> option.
+
+</article>