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.
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.
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.
This includes both opcodes and assembler directives.
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.
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.
Tells the assembler that 101 is to be treated as binary, and is therefore the decimal value 5.
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.
Commands to the assembler are all preceeded by a period (.).
.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 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.
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 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!
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
A 32 bit signed integer.
A 32 bit unsigned integer.
A 16 bit signed integer.
A 16 bit unsigned integer.
An 8 bit signed integer.
An 8 bit unsigned integer.
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)).
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.
.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.