]> git.sur5r.net Git - cc65/commitdiff
Added a new document that describes how to use the cc65 toolset with a
authoruz <uz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Thu, 25 Feb 2010 21:51:00 +0000 (21:51 +0000)
committeruz <uz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Thu, 25 Feb 2010 21:51:00 +0000 (21:51 +0000)
currently unsupported hardware platform. Written and constributed by Bruce
Reidenbach.

git-svn-id: svn://svn.cc65.org/cc65/trunk@4599 b7a2c559-68d2-44c3-8de9-860c34a00d81

doc/Makefile
doc/customizing.sgml [new file with mode: 0644]
doc/index.sgml

index 0ebe6b970078aee60bb652b64ae665229e8fda99..13ea979c796352ff05e9ef1966b9b753a529667d 100644 (file)
@@ -32,6 +32,7 @@ SGML =        apple2.sgml     \
        cl65.sgml       \
        co65.sgml       \
        coding.sgml     \
+        customizing.sgml\
        da65.sgml       \
        debugging.sgml  \
        dio.sgml        \
diff --git a/doc/customizing.sgml b/doc/customizing.sgml
new file mode 100644 (file)
index 0000000..23cf8c5
--- /dev/null
@@ -0,0 +1,730 @@
+<!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&nbsp;=&nbsp;3584&nbsp;=&nbsp;$0E00).  The 2K of ROM space begins at
+address $F800 and goes to $FFFF (size&nbsp;=&nbsp;$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&nbsp;=&nbsp;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&nbsp;()</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 &quot;sbc.cfg&quot;
+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&nbsp;()</tt>.
+The following code was used for the crt0 module, defined in the file
+&quot;crt0.s&quot;:
+
+<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  &quot;zeropage.inc&quot;
+
+; ---------------------------------------------------------------------------
+; Place the startup code in a special segment
+
+.segment  &quot;STARTUP&quot;
+
+; ---------------------------------------------------------------------------
+; A little light 6502 housekeeping
+
+_init:    LDX     #$FF                 ; Initialize stack pointer to $01FF
+          TXS
+          CLD                          ; Clear decimal mode
+
+; ---------------------------------------------------------------------------
+; Set cc65 argument stack pointer
+
+          LDA     #&lt;(__RAM_START__ + __RAM_SIZE__)
+          STA     sp
+          LDA     #&gt;(__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&nbsp;()</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  &quot;STARTUP&quot;
+</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&nbsp;()</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 &quot;supervision.lib&quot; 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 &quot;C:\Program Files\cc65\lib\supervision.lib&quot; 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
+&quot;interrupt.s&quot;.
+
+<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  &quot;CODE&quot;
+
+.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  &quot;CODE&quot;
+</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 &quot;interrupt.s&quot; along with
+the address for the initialization code in crt0 are defined in a file
+named &quot;vectors.s&quot;.  Note that these vectors will be placed in
+memory in their proper little-endian format as:
+
+<p><tt>       $FFFA&nbsp;-&nbsp;$FFFB:</tt> NMI interrupt vector (low byte, high byte)
+<p><tt>       $FFFC&nbsp;-&nbsp;$FFFD:</tt> Reset vector (low byte, high byte)
+<p><tt>       $FFFE&nbsp;-&nbsp;$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  &quot;VECTORS&quot;
+
+.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
+&quot;wait&quot; 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
+&quot;fooled&quot; 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
+&quot;wait.s&quot;, 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  &quot;CODE&quot;
+
+.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&nbsp;()</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 &quot;near&quot;
+modifier.  Similarly, the <tt>_stop</tt> routine can be called from
+within the C-level code via a call to <tt>stop&nbsp;()</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     #&lt;(L0001)  ;  Load A with the high order byte
+        ldx     #&gt;(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     #&lt;(L0001)  ;  Load A with the high order byte
+        ldx     #&gt;(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
+&quot;rs232_tx.s&quot; 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  &quot;CODE&quot;
+
+.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 &quot;Hello
+World! &quot; every time a question mark character is received via a
+call to the hardware driver <tt>rs232_tx&nbsp;()</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 &quot;main.c&quot;.  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 &amp; 0x01)
+#define RX_FIFO_EMPTY (FIFO_STATUS &amp; 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 (&quot;Hello World!&quot;);                //  Transmit &quot;Hello World!&quot;
+      }                                           //  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 &quot;a.out&quot;
+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>
index 5e4bff03879d0a1ad2e0afa95ba651dbddd10070..97d055a7a400e9538df326ddcb2b48bebae5364a 100644 (file)
@@ -65,6 +65,10 @@ Main documentation page, contains links to other available stuff.
   <tag><htmlurl url="make.html" name="make.html"></tag>
   Build programs, using the GNU Make utility.
 
+  <tag><htmlurl url="customizing.html" name="customizing.html"></tag>
+  How to use the cc65 toolset for a custom hardware platform (a target system
+  not currently supported by the cc65 library set).
+
   <tag><htmlurl url="debugging.html" name="debugging.html"></tag>
   Debug programs, using the VICE emulator.