Skip to content

lionkor/MU0-asm

Repository files navigation

MU0-asm

Simple assembler for MU0

See lionkor/MU0 for the emulator

How to build

  1. Clone the repo, with git clone [URL] and enter the directory.

  2. Run cmake . && make.

You now have an executable mu0asm that you can pass an asm file as an argument, like this:

./mu0asm multiply.asm

The assembler will then output two files:

  • a.out - The binary which can be run with an emulator like my MU0: ./MU0 a.out

  • a.asm - A generated assembly file which contains the instructions as understood by the assembler. This includes comments numbering all lines and so on. This assembly may not be given to the assembler as a source file, as it often contains invalid internal symbols the assembler uses. This file is useful to debug the code generated by the assembler, such as when call and ret have been used in the source file.

    All of the code in a.asm is generated by the assembler, it does not use any of the original source code (if it looks the same then you know your syntax was correct).

Syntax

Comments

You can write comments starting with #. Example:

# this is a comment
lda 0x5 # this is also a comment

MU0 instructions

Supported instructions:

LDA S   Load mem[S] into ACC
STO S   Store ACC in mem[S]
ADD S   Add mem[S] to ACC
SUB S   Subtract mem[S] from ACC
JMP S   Jump to mem[S] (pc := mem[S])
JGE S   Jump to mem[S] (pc := mem[S]) if ACC >= 0
JNE S   Jump to mem[S] (pc := mem[S]) if ACC != 0
STP     Stop

Note: JGE S is useless in my MU0 emulator, as no negative numbers are supported. The assembler will likely not understand negative numbers.

S in this case might be any address in the space supported by MU0. S can be noted as hex 0xN or decimal N.

Instructions may be written in upper- or lowercase.

Memory / Variables / Registers / Data

MU0 only has one register ACC, which is what the instructions operate on. It also does not support any immediate mode, so getting data into the program is possible with a d "instruction".

Data segments can be read, written and executed.

Here are examples of data instructions:

d variable_name=0x65
d x_1 = 5
d myvar = 0xAB

$variable_name can then be used inplace of any S in the instruction set. The $ prefix is required wherever a variable is used.

A d data instruction / declaration will appear at the exact place it was put in the code, so it needs to be jumped over:

1 jmp 2         # jump to instruction 2, this is 0
2 d myvar = 6   # myvar is 6
3 lda $myvar    # load from location $myvar (ACC is 6 after this)

d declarations may appear before or after they are used, such as here:

lda $var
jmp .somewhere
d var = 5

I'd recommend putting data at the end of subroutines and in other unreachable places, as an emulator will happily execute data. This can, thus, also be used to create self-modifying code.

You might also put data at the beginning of the program and have a jmp .start as the first line before that.

A simple adder, does a+b=result, in this case 5+10

lda $a
add $b
sto $result
stp

d a = 0x5
d b = 0xA
d result = 0x0

Then we load a, add b, and store the result in result. Then we STP (stop), which hangs the CPU. Data is put at the end, after the STOP, so it won't get executed.

Labels

You can create labels with this syntax:

.my_label:

They follow usual rules of naming, like no spaces, etc. Labels can then be jumped to (this is the usual use-case):

jmp .my_label

You can jump to labels anywhere in the program.

They must start with . and end with :.

Releases

No releases published

Packages

No packages published