Skip to content

Assembler directives

simon987 edited this page Jul 30, 2020 · 11 revisions

Directives

Directives is the name for the group of terms/strings that have special meaning in a MAR program (or any compiled program). These directives are special instruction for the compiler (in assembly we call them an assembler). Directives state certain things about the code that make the compiler generate the machine code in the way the programmer want's to.

The following directives are explained:

EQU

The EQU directive allows you to define compile time constants. If you are familiar with the C pre processor think of it as the #define directive:

#define name 1 // for the rest of the program any instance of 'name' will be replaced with '1'

The syntax for the EQU directive is as followed:

name EQU 0x01 ;; for the rest of the program any instance of 'name' will be replaced with '0x01'

These directives can be used to give a name to a constant value (it never changes). When assembling the compiler will swap all the found instances of this name with the value specified to it. Remember that the name is only known at compile time, after compilation there is no concept of the name you gave to the constant.


NOTE: the EQU directive is (as all instructions are) case insensitive. So equ, eQu and EqU make no difference


So this code:

    INT_LEGS           equ 0x0001
    LEGS_SET_DIRECTION equ 0x0001
    NORTH              equ 0x0000

    MOV A, LEGS_SET_DIRECTION
    MOV B, NORTH
    HWI INT_LEGS

produces the same machine code as:

    MOV A, 0x0001
    MOV B, 0x0000
    HWI 0x0001

but to make code more readable and easier to reason about it is encouraged to use constants. Check this snippet of al sort's of useful constants.

Labels

The label directive associates a label with the memory address of the instruction/directive that follows it. The syntax to use a label is this: name_of_label: <instruction>. Labels behave a bit like the EQU directive. They refer to a certain value. Only this time it is not a constant value but a memory address. This is useful for the JMP family instructions and the DW directive. See the following example:

    MOV A, 0         ;; set register A to the value of 0
infinite_loop:       ;; create a label called 'infinite_loop'
    CMP A, 0         ;; check if register A has the value of 0
    JZ infinite_loop ;; if A has the value of 0 we jump back to the label infinite loop
    ;; the label prefixes the 'CMP A, 0' instruction so the jump goes to that instruction
    ;; because the value in A never changes we are stuck in an infinite loop

So it might look like the JZ is jumping to the infinite_loop label but actually it is jumping to the CMP A, 0 instruction behind it. The labels leave no footprint in the compiled machine code and are replaced by the memory addresses they refer to. Another example of a label used with a function/routine call:

    MOV A, 0xDeaD ;; move 0xDead (valid hex number) into register A
    call someFunc ;; call 'someFunc'

someFunc:         ;; label 'someFunc'
    MOV A, 0xBeeF ;; move 0xBeeF (also valid hex number) into register A
    ret           ;; return from function call

So for the programmer it is quite readable what happens here but on compilation the labels are removed and replaced with the memory addresses so it looks more like this:

    MOV A, 0xDeaD  ;; 0x0000 - instruction memory address
    call   0x0002  ;; 0x0001
    MOV A, 0xBeeF  ;; 0x0002
    ret            ;; 0x0003

NOTE: Instructions are actually 1-3 words (2-6 bytes) of size, but simplified for the above example.


DW

The DW word directive (Define Word) allows the programmer to put a value in memory. The syntax for DW is:

    DW 0x000
    DW 0x000, 0x0001, 0x0002, 0x0003 ;; .... and go on for how long you want

As you can see we can just dump values right at that memory spot. This can be useful for multiple reasons:

  1. You want to pad out certain area's in your program/memory.
  2. You want to put a global and/or static variable in your code.
  3. You want to define constant strings. (Yeah the most useful one) To prevent headaches of finding out where you put the literal in memory you can use labels:
mynumber: DW 0x0000

    MOV A, [mynumber] ;; NOTE: mynumber points to the memory location not the actual value.
                      ;;       use [] to reference the contents of the memory

String literals

You can use the DW to store strings, for example:

my_str: DW "Hello"

; This is assembled to:
;  00 48 00 65 00 6C 00 6C 00 6F 

.text
MOV A, [my_str] ; Value of A is now 'H'

Java escape sequences are handled (\u0000, \". \0 \t etc) and strings are encoded to 16-bit big endian.

You can combine any type of literal values in a single line, for example:

DW "some string", 123, "another string", 0

DUP

The DUP directive (Duplicate) is used as operand for the DW directive. The syntax:

;; [label:] DW amount DUP(value)
someBigStaticMemoryBlock: DW 100 DUP(0x20), 0
;; this create 100 times a 0x20 (the space character) followed by a 0 (null terminator)

It can only be used in combination with the DW directive

Sections

Sections are useful to organize your code.

There is currently two section, .text and .data. By default, the .data section implicitly starts at the beginning of your code and ends where the .text section starts, but you are allowed to reorder them as you wish. You must explicitly specify where the .text section starts.

At the beginning of each tick, your code is executed starting at the first instruction after the .text directive until it reaches the BRK instruction (See Execution cycle).

Notes:

  • There is nothing stopping you from executing code in the .data section (or to store variables in the .text section)
  • There is no padding in between sections

ORG

The ORG directive lets you change where your code is copied during assembly. By default, assembled code is copied at 0x200

Example:

ORG 0x8000
my_var: DW 0x1234

.text
MOV A, my_var ; Value of A is now 0x8000
brk
Clone this wiki locally