zasm can include C source files in your project. These will be compiled using the C compiler sdcc.
In a 'normal' C project you typically start writing C sources and then at some point wonder what the file crt0.s may do and then how to modify it. With zasm you start with an assembler file which mostly does what the crt0.s file should do. zasm also resolves global symbols from the system libraries and links your project into one binary.
The main assembler file must define a Assembler directives: #target Including C Source Files: #target#target, some #code and #data segments, implement the restart vectors, an interrupt and the nmi handler, initialize the system hardware, initialize variables and define _putchar and _getchar, Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif required.
When it comes to compiling a C source, zasm looks for the C compiler. Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifIf it is not in your $PATH then you must tell zasm where it is with the Differences from v3 to v4: Command line options Command Line Options Command Line Options: Command line optionscommand line option -c.
#insert: Examples: #assert: Example: incbin: Examples: #assert: Example:Example:
$> zasm -c /usr/bin/sdcc foo.asm
Next the C compiler needs to know where the system headers are when a C source includes some of them. The C compiler will look at the default locations, but you may pass it the include/ directory from the zasm distribution, which can be done with Differences from v3 to v4: Command line options Command Line Options Command Line Options: Command line optionscommand line option -I.
#insert: Examples: #assert: Example: incbin: Examples: #assert: Example:Example:
$> zasm -I /work/sdcc/include foo.asm
The C compiler sdcc Run test code and test your expectations: .expect register Run test code and test your expectations: .expect ccexpects some segments to be defined in your source.
#insert: Examples: #assert: Example: incbin: Examples: #assert: Example:Example:
_ram_start Pseudo instructions: equ Types of labels: Named values Labels: EQUequ 0x4000 ; wherever
#target rom ; define the code model
Assembler directives: #code Including C Source Files: #code#code _HOME,0 ; code that must not be put in a bank switched part of memory.
Assembler directives: #code Including C Source Files: #code#code _GSINIT ; init code: the compiler adds some code here and there as required
Assembler directives: #code Including C Source Files: #code#code _CODE ; most code and const data go here
Assembler directives: #code Including C Source Files: #code#code _INITIALIZER ; initializer data (in rom) for initialized variables (in ram)
Assembler directives: #code Including C Source Files: #code#code _CABS,*,0 ; referenced but never (?) actually used by sdcc
Assembler directives: #code Including C Source Files: #code#code _GSFINAL,*,0 ; referenced but never (?) actually used by sdcc
Assembler directives: #data Including C Source Files: #data#data _DATA, _ram_start ; uninitialized variables
Assembler directives: #data Including C Source Files: #data#data _INITIALIZED ; variables initialized from _INITIALIZER
Assembler directives: #data Including C Source Files: #data#data _DABS,*,0 ; referenced but never (?) actually used by sdcc
Assembler directives: #data Including C Source Files: #data#data _RSEG,*,0 ; referenced but never (?) actually used by kcc
In this #insert: Examples: #assert: Example: incbin: Examples: #assert: Example:example the _HOME segment starts at address 0 and therefore must start with the handlers for the restart vectors. As an #insert: Examples: #assert: Example: incbin: Examples: #assert: Example:example see Examples/template_rom_with_c_code.asm.
Hint: as one of the very last lines in your source add a ret statement or directly a call to main() in _GSINIT:
Assembler directives: #code Including C Source Files: #code#code _GSINIT ; switch to segment _GSINIT
call _main ; final action in _GSINIT: call main()
rst 0 ; Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif it returns (which it shouldn't)
The major file of your project must be an assembler file. This file can include other assembler files or c source files. C source files must Pseudo instructions: end, .end 8080 pseudo instructions: ENDend with the file name extension '.c'.
#insert: Examples: #assert: Example: incbin: Examples: #assert: Example:Example:
Assembler directives: #include Including C Source Files: #include#include "#insert: Examples: #assert: Example: incbin: Examples: #assert: Example:example.c"
Compiled C source files frequently contain references to procedures or other global symbols in the system libraries. These can be resolved automatically Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif your assembler file includes the system library with the Assembler directives: #include Including C Source Files: #include#include assembler directive after the last included c source file or after the last reference to an undefined symbol from the system library for the first time.
#insert: Examples: #assert: Example: incbin: Examples: #assert: Example:Example:
#include: #include library #include: #include library#include standard library
This will resolve all symbols from the system library known as required at that point in your source.
As a prerequisite zasm must know where to find the system library directory. It is recommended to use the supplied directory from the distribution and then you have to tell zasm where it is. This can be done with the Differences from v3 to v4: Command line options Command Line Options Command Line Options: Command line optionscommand line option -L.
#insert: Examples: #assert: Example: incbin: Examples: #assert: Example:Example:
$> zasm -L /work/sdcc/lib foo.asm
Alternatively you can give a path in the '#include: #include library #include: #include library#include library' directive itself:
#include: #include library #include: #include library#include library "/work/sdcc/lib"
Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifIf you use malloc() etc. then you also need a heap. The actual requirements depend on the implementation of malloc() and free() and this is how it currently should work: (else refer to the implementations of lib/_malloc and lib/_mfree…)
Define data segment _HEAP as the last data segment Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif you want it to occupy all unused ram up to ram Pseudo instructions: end, .end 8080 pseudo instructions: ENDend.
Assembler directives: #data Including C Source Files: #data#data _HEAP ; heap:
__sdcc_heap_start: ; --> sdcc _malloc.c
Pseudo instructions: defs, ds, .ds, .block, .blkb and data 8080 pseudo instructions: DSds _min_heap_size ; minimum required size
Pseudo instructions: defs, ds, .ds, .block, .blkb and data 8080 pseudo instructions: DSds _ram_end-$-1 ; add all unused memory to the heap
__sdcc_heap_end: ; --> sdcc _malloc.c
Pseudo instructions: defs, ds, .ds, .block, .blkb and data 8080 pseudo instructions: DSds 1
System initialization must be done in assembler as far as it cannot be done in C. An important step in system initialization is the initialization of initialized variables. After that main() must be called which should not return.
#insert: Examples: #assert: Example: incbin: Examples: #assert: Example:Example:
_init: ; jump here from reset entry at 0x0000
di
ld sp,_ram_end ; Pseudo instructions: defl, set and '=' Labels: SETset stack pointer
ld bc,_initializer_len ; length of segment _INITIALIZER (must be calculated)
ld de,_INITIALIZED ; start of segment _INITIALIZED
ld hl,_INITIALIZER ; start of segment _INITIALIZER
ld a,b
or c
jr z,$+4
ldir ; copy initializer data into initialized variables
call _GSINIT ; Initialize global variables (whatever the c compiler has put in here)
call _main ; call main() (Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif not done at the Pseudo instructions: end, .end 8080 pseudo instructions: ENDend of _GSINIT itself)
rst 0 ; Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif it returns
C will call the symbols _putchar and _getchar Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif your source includes any print or read function call. These cannot be provided by the standard system library because they are highly specific to your target system. Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifIf C sources link against these symbols then you must provide appropriate definitions.
Eventually you can implement _putchar and _getchar in C, but Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif you need to implement them in assembler, then you need to know how the C compiler passes the arguments.
Argument passing to and from functions may vary with compiler options, so i recommend that you first include dummy definitions for _putchar() and _getchar() in your project and inspect the list file generated by the assembler to see what the compiler does.
#insert: Examples: #assert: Example: incbin: Examples: #assert: Example:Example: in a C file define:
char inchar,outchar;
char getchar(void) { return inchar; }
void putchar(char c) { outchar = c; }
assemble your project and you may see the following assember source for _putchar and _getchar:
;char getchar(void) { return inchar; }
_getchar:
ld hl,#_inchar
ld l,(hl)
ret
;void putchar(char c) { outchar = c; }
_putchar:
push ix
ld ix,#0
add ix,sp
ld a, 4 (ix)
ld (#_outchar),a
pop ix
ret
Use this as a template for your own implementation. For an #insert: Examples: #assert: Example: incbin: Examples: #assert: Example:example see Examples/zx_spectrum_io_rom.s
|