From the assembler's point of view, there is no difference between code and data. Code is entered by using the mnemonics described in the relevant backend chapters.
mfhi t0
nop
nop
mult t0,t1
Data (or instructions) can also be entered using data declaration statements:
DC.W $4E75
DC.B "This is a string",0
The data declaration statements will be called something different depending on the CPU backend. Portable versions are always available.
Portable | 6502 | Z80 | M68K | MIPS | DCPU-16 | SCHIP |
---|---|---|---|---|---|---|
__DCB |
DB |
DB |
DC.B |
DB |
n/a | DB |
__DCW |
DW |
DW |
DC.W |
DH |
DW |
DW |
__DCL |
n/a | n/a | DC.L |
DW |
DL |
n/a |
Data can also be declared by simply including a binary file directly from the file system:
SECTION "Graphics",DATA
Ship: INCBIN "Spaceship.bin"
Code, data and variables are organised in sections. Before any mnemonics or data declarations can be used, a section must be declared.
SECTION "A_Code_Section",CODE
This will switch to the section A_Code_Section
if it is already known (and its type matches), or declare it if it doesn't. DATA
may also be used instead of CODE
, they are synonymous.
When writing a binary file using the assembler, sections are naturally aligned to a CPU specific byte alignment:
6502 | Z80 | M68K | MIPS | 0x10C | SCHIP | 6809 |
---|---|---|---|---|---|---|
1 | 1 | 8 | 8 | 2 | 1 | 1 |
Fixed sections are not aligned. The section alignment can be changed with the -a
option.
Using the CNOP directive, it's possible to align to an alignment that divides evenly into the platform's section alignment. CNOP takes two arguments - an offset
and an alignment
. First the current PC is aligned to a multiple of alignment
bytes, then offset
bytes is added.
Commonly used on M68K assembler, the assembler supports the EVEN directive. This does the same as CNOP 0,2
- it aligns the PC to the next even address.
Variables are usually placed in a BSS
section. A BSS
section cannot contain initialised data, typically only the DS
command is used. However, it is also possible to use the DB
, DW
and DL
commands without any arguments.
SECTION "Variables",BSS
Foo: DB ; Reserve one byte for Foo
Bar: DW ; Reserve a word for Bar
Baz: DS str_SIZEOF ; Reserve str_SIZEOF bytes for Baz```
If the chosen object output format supports it, you can force a section into a specific address:
SECTION "LoadSection",CODE[$F000]
Code: xor a
If the target architecture supports banks, it's possible to specify that a section should be placed in a specific bank. Otherwise the linker will choose.
SECTION "FixedSection",DATA[$1100],BANK[3]
It's also possible to just specify the bank:
SECTION "FixedSection",CODE,BANK[3]
To discover the bank in which a specific symbol has been placed, the BANK() function can be used:
ld a,BANK(Symbol)
At any point the origin address can be changed using the ORG
directive. The origin address is the base
address for any subsequent labels and code. Note that this is different from the load address specified
using the SECTION
directive. ORG
is particularly useful for blocks of code that must be copied
to a specific address at a later time.
SECTION "Example",CODE[$2000]
Entry: nop ; placed at $2000, Entry = $2000
; placed at $2001
Block: ; Block = $2001
ORG $1000
BlockEntry: ; BlockEntry = $1000
ld hl,BlockEntry ; placed at $2001, load $1000 into hl
BlockSize EQU *-BlockEntry
A section may optionally be aligned to a multiple of bytes.
SECTION "Example",CODE,ALIGN[$100]
; This section is aligned to a multiple of $100 bytes
Not all output formats support this option.
The linker supports stripping unused code through its "-s" option. This could lead to undesired behaviour, if a critical piece of data or code is omitted, if nothing references it. This would typically be headers and interrupt vectors. By specifying the ROOT flag, the section is never stripped by the linked.
SECTION "Example",CODE,ROOT
; This section is never removed the linker
Not all output formats support this option.
A section stack is available, which is particularly useful when defining sections in included files (or macros) and it's necessary to preserve the section context for the program that included the file or called the macro.
POPS
and PUSHS
provide the interface to the section stack. PUSHS
will push the current section context on the section stack. POPS
can then later be used to restore it.
- Introduction, goals and background
- Invoking the assembler and basic syntax
- Symbols and labels
- Control structures like
INCLUDE
,MACRO
s and conditional assembling. - Expressions and how they're built
- Printing diagnostic messages, warnings and errors
- Organising code into sections. How to define data.
- The linker