Writing Assembly

From VO-EM Wiki
Jump to: navigation, search

DLX assembly has a very simple syntax. It is split into four colums using whitespace. There are no curly braces, no dot syntax, and no control statements. The assembler does not even support macros.

Syntax

All DLX assembly follows the following format:

label     command     argument1,argument2,argument3     ;comment

myroutine addui r1,r0,16#F  ;An example that adds 0xF
 ;to r0, and stores it in r1.

a_word .word 2#11110000111100001111000011110000;Sets the current memory address
 ;to the given 32 bit value,
 ;then enters its address in the
 ;symbol table as a_word.

The assembler accepts a relatively large range of whitespace between columns.

Label

Declaring a label will cause an entry to be made in the symbol table. Labels can be used to refer to values or sections of code.

command

This includes both opcodes and assembler directives.

arguments

Depending on the command used, this field may be required, optional, or it may be required to be blank. Registers, immediate values, and value declarations can be made in this field.

comment

All comments must be preceeded by a semicolon (;). The assembler will ignore everything on the current line after a semicolon is detected.

#

The hash sign is used to indicate the base of the number following it to the assembler.

For example,

 2#101

Tells the assembler that 101 is to be treated as binary, and is therefore the decimal value 5.

 16#FF 

Tells the assembler that FF is to be treated as hex, and is therefore the decimal value 255.

It is good practice to always indicate the base you are working in, as 2#1000, 10#1000, and #16#1000 are all extremely different values.

Assembler directives

Commands to the assembler are all preceeded by a period (.).

.start

.start     value
;eg:
main       add     r1,r1,r2
           nop
           j       main
.start     main              

.start tells the VO-EM device which memory address to begin execution from. The value is stored as an unsigned word starting from byte 0x34 of the info square on exported VO-EM cartridge pngs. It is simply stored as .start (value) in .dlx files, however when the VO-EM debugger loads a dlx file, the start address will still be available at byte 0x34 offset from the cartridge info address in memory.

.seg

        .seg      label
;eg:
        .seg      bank3

.seg is used in relocatable assembly to organise segments of code into discrete blocks. These segments are then arranged into an executable by dloc.jar, following the instructions of a .dla file. The most common usage of .seg in VO-EM source code is to tell the assembler which bank of memory to store code or data in. If you use a segment label that is not defined in your .dla file, dloc.jar will fail to relocate the source code.

.abs

        .abs

This command does not accept any labels or arguments. It informs the assembler that the following code segment is not to be made relocatable. If your entire program fits into bank0, you can write it all under a .abs statement.

.align

        .align     value
;eg:
        .align     4 

This command does not accept labels. Using .align will add to the program counter until it is a multiple of the value provided. This is very important to ensure that your values are properly aligned in memory. Consider the following example:

myhalf   .halfu    16#FFFF
myword   .wordu    16#FFFFFFFF
alsohalf .halfu    16#FFFF
         .start    loader
loader   lh        r1,myhalf
         lw        r2,myword

This code segment will throw an IME exception. This is because myhalf has the memory address 0, and takes up 2 bytes. As a result, myword has the address 2, and takes up 4 bytes. As a result, attempting to load myword using lw attempts to load data from the memory address 2. However, lw can only load words from addresses that are multiples of 4! If it wasn't for 'alsohalf', this code would simply fail to execute, as 'loader' would have an address of 6, which is not a multiple of 4 and hence can't be loaded for execution either.

.align solves this problem easily.

myhalf   .halfu    16#FFFF
         .align    4
myword   .wordu    16#FFFFFFFF
alsohalf .halfu    16#FFFF
         .align    4
         .start    loader
loader   lh        r1,myhalf
         lw        r2,myword

myword now has address 4. loader has address C. This program executes correctly!

.equ

This simply sets the value of the label to the argument supplied. No changes are made to the program itself. For example,

fortyfive     .equ    10#45
              addi    r1,r0,fortyfive
              addi    r2,r0,10#45       ;r1 and r2 now contain the same value. 

Variable declaration directives

The following directives all place a given value into the current memory position. Note that you can only use these commands in segments that are actually included in a cartridge's memory range. For example, if you try to declare a value in the ram segment at assembly time, this variable will simply not be there when you execute your program on the VO-EM device.

All of these values take an argument immediately after the declaration, and can have a label.

label    .directive    value
;eg:
mybyte   .byte         16#FF

Note that you can declare multiple values of the same type sequentially using commas, eg:

mybytes  .byte         16#FF,16#32,16#80     ;All three bytes will be stored sequentially

.word

A 32 bit signed integer.

.wordu

A 32 bit unsigned integer.

.half

A 16 bit signed integer.

.halfu

A 16 bit unsigned integer.

.byte

An 8 bit signed integer.

.byteu

An 8 bit unsigned integer.

.ascii

Usage:

mystring      .ascii     "This is a string."

Stores an inputted string as a sequence of ascii-encoded bytes (each character is a single byte). Accepts basic escape characters, such as \n (new line) and \0 (terminating byte (0x00)).

.space

usage

myvar     .space    4    ;where the argument is the number of bytes to reserve

Reserves a given number of bytes for later use, but does not actually write anything to them. Stores the address of the start of these bytes as the value of the label.

This is especially useful for saving space for variables in RAM, as RAM values can not be initialized from the cartridge at load-time.

For example,

          .seg     ram
myvar     .space   4
          .seg     bank0
          sw       myvar+RAM(r27),r1 ;saves the value of r1 into the space reserved for myvar

The above example assumes that "RAM" holds the negative offset of the RAM in memory, and that r27 holds the actual offset.