QCPU
  • Instruction set
  • CLI
  • Guide
  • Snippets

On this page

  • 1 Directives
    • 1.1 Compiler extensions
  • 2 References
  • 3 (Pseudo-)instructions
    • 3.1 Types
    • 3.2 Inline data
  • 4 Snippet

Assembly language

Build programs for QCPU

assembly
QCPU’s assembly language, linker and compiler extensions.
Published

November 4, 2023

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> in qcpul)
  • @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 size
    • gextension: extension on globals, fixed size
    • signals: process signal vector
    • stack: thread-local, initially max. 256 bytes
    • text: 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:

@macro kext-stack-base
.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 byte
  • u16: a word
  • u24: a 24-bit word
  • u32: a 32-bit word

A couple of special pseudo-instructions manage the encoding of data:

  • ascii: like u8, but with explicit ASCII encoding
  • reserve: uninitialised amount of data

See the instruction set page for a reference.

3.1 Types

@section globals
    .queue      @queue(24)              ; macro 'queue' structure
    .queuep     @queuep(queuec)

@section gextension
    .queuec     @queuec(24)

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


// QCPU 2 assembly syntax snippet

; this define would be from @symbols "kernel/sysc.s"
; a kext may also be from @symbols "kernel/kernel.s"
@define fopen 0x05
@define mmap 0x07

@section global ; shared by threads, fixed size

.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

@section empty ; shared by threads, empty, fixed size

.reserved:  reserve u16 128

@section local ; thread local, fixed size

.localvar0: reserve u8 24           ; reserve 24 bytes
.localvar1: reserve u16 24          ; reserve 48 bytes
.localvar2: u32 0x1234 0x5678       ; initialise 4 bytes

@section text ; readonly, executable

main:       imm   rx    .path       ; 'main' must be a public label for linking
            imm   ry    .path'u     ; upper byte syntax 'u, for u8 implicitly 'l
            imm   rz    0b00000000  ; fopen mask
            sysc  @fopen            ; fopen(pathl, pathh, msk) -> (,, fd)
            imm   rx    0x00        ; size low byte
            imm   ry    0x04        ; size high byte, 1024
            sysc  @mmap             ; mmap(sizel, sizeh, fd) -> (addrl, addrh)
            msp         0x0002
            ast   rx
            ast   ry
            mstw  sp    -2          ; store at recently allocated stack location
.spinlock:  jmpr        .spinlock   ; execs should end with sysc @exit, not lock