Assembly language
Build programs for QCPU
Like most assemblers, each QCPU 2 source line contains some combination of the following four fields:
label: instr operands ; comments col 80
If a line starts with the @
symbol, it refers to an assembler directive.
1 Directives
An assembler can read directives from the QCPU assembly file. Each directive starts with an @
symbol and don’t represent any size in the assembly (except for an @include
or the result of such a directive).
@symbols <file>
: include the symbol definitions of a file, for ABI interfaces@org <address>
: marks the linker offset (--offset <address>
inqcpul
)@define <name> <value>
: a C preprocessor-like define@define <namespace>
: a collection of definitions@define <name> <value>
: adds@namespace.name
@end
@macro <name> [arguments...]
: defines reusable text@end
@region <size>
: marks a region with a fixed size, default filled with zeros@end
@section <type>
: marks the start of a section, managed by the linker- [
instance
: address space interface, usually defined by the linker] globals
: shared by threads, max. 256 bytes, fixed sizegextension
: extension on globals, fixed sizesignals
: process signal vectorstack
: thread-local, initially max. 256 bytestext
: readonly, executable
- [
1.1 Compiler extensions
A kernel may inject extensions into the files, and these are identified by specific macro names.
@macro kext-stack-base
: leading bytes of the stack
An example is kext-stack-base
:
-stack-base
@macro kext.env: reserve u16 1
.args: reserve u16 1
@end
2 References
Labels are used to reference either data or text. A public reference:
can be accessed by any file which is linked against it. In contrast, a .reference:
is a file-local label which can only be accessed by the addresses in the same file. A linker error is thrown if a private label was attempted to be accessed from the outside.
An executable is required to have a public main:
label.
Regardless of access flags, all labels are addressed with the .<label>
syntax.
3 (Pseudo-)instructions
Pseudo-instructions are formulated like normal instructions, but are interpreted by the assembler to produce another output.
u8
: a byteu16
: a wordu24
: a 24-bit wordu32
: a 32-bit word
A couple of special pseudo-instructions manage the encoding of data:
ascii
: like u8, but with explicit ASCII encodingreserve
: uninitialised amount of data
See the instruction set page for a reference.
3.1 Types
@section globals@queue(24) ; macro 'queue' structure
.queue @queuep(queuec)
.queuep
@section gextension@queuec(24) .queuec
3.2 Inline data
A comma between the first operand (that resides in the instruction) and its secondary byte (such as an offset) is short for having a newline and u8 ...
. Having imm ra, 0x01
is therefore a composition of imm ra
and u8 0x01
.
4 Snippet
2 assembly syntax snippet
// QCPU
; this define would be from @symbols "kernel/sysc.s"
; a kext may also be from @symbols "kernel/kernel.s"
0x05
@define fopen 0x07
@define mmap
; shared by threads, fixed size
@section global
.path: ascii "/etc/fstab" 0x00 ; ascii is just u8 with explicit encoding
.pathsz: u8 .pathsz - .path ; expressions
.pathptr: u16 .path ; path gets read as 16 bits
; shared by threads, empty, fixed size
@section empty
.reserved: reserve u16 128
; thread local, fixed size
@section local
.localvar0: reserve u8 24 ; reserve 24 bytes
.localvar1: reserve u16 24 ; reserve 48 bytes
.localvar2: u32 0x1234 0x5678 ; initialise 4 bytes
; readonly, executable
@section text
main: imm rx .path ; 'main' must be a public label for linking
.path'u ; upper byte syntax 'u, for u8 implicitly 'l
imm ry 0b00000000 ; fopen mask
imm rz @fopen ; fopen(pathl, pathh, msk) -> (,, fd)
sysc 0x00 ; size low byte
imm rx 0x04 ; size high byte, 1024
imm ry @mmap ; mmap(sizel, sizeh, fd) -> (addrl, addrh)
sysc 0x0002
msp
ast rx
ast rysp -2 ; store at recently allocated stack location
mstw .spinlock: jmpr .spinlock ; execs should end with sysc @exit, not lock