MackAsm Program Guide
MackAsm is a simple stack based byte code designed for ease-interpreted execution by an imbedded processor.
Each instruction always encodes to one byte.
The stack is an array of 4-byte signed values. Smaller numbers are sign extended to fill each entry. The last element pushed is called Top Of Stack (TOS). The one under it, is referred to as TOS-1 and so on.
Operands that need more than 1 or 2 bits are passed on the stack. However, the assembler will generate the push instructions for you if they follow the operator.
Example (5-3=2):
push 5 push 3 sub |
= |
push 5 sub 3 |
= |
sub 5, 3 |
These generate the exact same 3-byte of code.
Note: Only defined labels can be pushed. The value pushed is the absolute address of the label. Branch and Call instruction require relative offsets, not absolute addresses.
push n Pushes a constant on the stack.
Op-code 1: _0_n_n_n_n_n_n_n_
Op-code 2: _1_0_n_n_n_n_n_n_ nnnnnnn
Op-code 3: _1_0_1_0_0_0_0_0_ nnnnnnn nnnnnnn
Op-code 4: _1_0_1_0_0_0_0_1_ nnnnnnn nnnnnnn nnnnnnn nnnnnnn
Stack:
|
è |
n |
Description:
0 ≤ n ≤ 63 Op-code 1 is used
-7680 ≤ n ≤ 8191 Op-code 2 is used
-32768 ≤ n ≤ 32767 Op-code 3 is used
else Op-code 4 is used.
Notice that op-code 1 is the only unsigned literal. This is because numbers between 32 and 63 are more common then numbers between -32 and -1.
pop Pop and discard TOS.
Op-code: _1_1_0_0_0_0_0_0_
Stack:
junk |
è |
|
Description:
Sometimes a value on the stack is no longer needed. “pop” will just get ride of it.
clear Empty the stack.
Op-code: _1_1_0_0_0_0_0_1_
Stack:
junk junk … junk |
è |
|
Description:
Like “pop”, “clear” throws away information in the stack. However, “clear” throws out the whole stack. This is needed when an interrupt needs to restart the system.
dup [.-1 | op] Duplicate TOS or TOS-1
Op-code: _1_1_0_0_0_0_1_p_
Stack:
x y |
è |
x
or y x y |
Where p is which stack item to duplicate.
0 = Duplication TOS
1 = Duplication TOS-1
Description:
Duplicate one of the top 4 stack entries.
add [op [,op]] Addition of two numbers.
Op-code: _1_1_0_0_0_1_0_0_
Stack:
y x |
è |
x
+ y |
Description:
Using 4-byte, signed 2-complement math, pop and add TOS-1 and TOS and push the sum.
Example (birthday):
load age
add 1
store age
sub [op [,op]] Subtract TOS from TOS-1.
Op-code: _1_1_0_0_0_1_0_1_
Stack:
y x |
è |
x
- y |
Description:
Using 4-byte, signed 2-complement math, pop and subtract TOS from TOS-1 and push the difference.
Example (undo birthday):
sub age,
1
store age
mul [op [,op]] Multiply TOS to TOS-1.
Op-code: _1_1_0_0_0_1_1_0_
Stack:
y x |
è |
x
* y |
Description:
Using 4-byte, signed 2-complement math, pop and multiply TOS to TOS-1 and push the product.
div [op [,op]] Divide TOS to TOS-1.
Op-code: _1_1_0_0_0_1_1_1_
Stack:
y x |
è |
x
/ y |
Description:
Using 4-byte, signed 2-complement math, pop and divide TOS by TOS-1 and push the product.
Example (undo birthday):
load cents
div 100
store dollars
and, or and xor [op [,op]] Bit Wise Logical Operators
AND Op-code: _1_1_0_0_1_0_0_0_
OR Op-code: _1_1_0_0_1_0_0_1_
X-OR Op-code: _1_1_0_0_1_0_1_0_
Stack:
q p |
è |
p OP q |
Description:
Perform the 32-bit, bit wise, logical operation of the two input operands, and push the result.
arshift [op [,op]] Arithmetic Right Shift
Op-code: _1_1_0_0_1_0_1_1_
Stack:
# of bits to shift 1xxxxx |
è |
1111xx |
Description:
Right shift TOS-1 by TOS number and while preserving the sign bit.
rshift [op [,op]] Bit Wise Right Shift
Op-code: _1_1_0_0_1_1_0_0_
Stack:
# of bits to shift xxxxx |
è |
000xx |
Description:
Right shift TOS-1 by TOS number of bits.
lshift [op [,op]] Bit Wise Left Shift
Op-code: _1_1_0_0_1_1_0_1_
Stack:
# of bits to shift xxxxx |
è |
xx000 |
Description:
Left shift TOS-1 by TOS number of bits
not and neg [op] Logical and Arithmetic Inverse
not Op-code: _1_1_0_0_1_1_1_0_
neg Op-code: _1_1_0_0_1_1_1_1_
Stack:
q p |
è |
p OP q |
Description:
Perform the 32-bit, bit wise, logical operation of the two input operands, and push the result.
There are two branching instructions “b” and “call” (“return” is just an alias for “b”). The only real difference being that “call” pushes a return address and “b” does not. Branching instructions are relative. That is, the address is an offset from the current address (the address of the next instruction). Labels can be used to make this easier. A label is an identifier placed before an instruction with a trailing “:”. A reference to a label required a leading “->”.
Example:
loop:
stuff
b ->loop
b.dnz [count_var] [->label] Decrement and Branch if Not Zero
Op-code: _1_1_0_1_0_0_0_0_
Stack:
Relative Code
Offset |
è |
|
Description:
Branch (change the flow of control) to the offset on the stack.
Example:
store count, 10 // count=10
Loop:
Do something
b.dnz count, Loop // Repeat the loop 10 times.
b {.< | .<= | .= | .<> | .>= | .>} [op [,op]] [->label] Conditional Branch
Op-code: _1_1_0_1_0_<_=_>_
Where “<=>” are the condition bits:
000 = used by b.dnz.
001 = Branch if Greater Than 011 = Branch if Greater Than or Equal
010 = Branch if equal 101 = Branch if Not Equal
100 = Branch if Less Than 110 = Branch if Less Than or Equal
111 = used by b
Stack:
Relative Code Offset Right Side Value Left Side Value |
è |
|
Description:
Conditionally branch to a target label.
This:
1) Pops Code Offset
2) Pop Right and Left Side Values
3) If “Left Side Value” condition “Right Side Value” is true, execute the instruction at “Code Offset” else execute the next instruction.
Example:
b.< age, 21, NoAlcohol
b [->label] or return Branch Always
Op-code: _1_1_0_1_0_1_1_1_
Stack:
Relative Code Offset |
è |
|
Description:
Pop the offset and change the flow of control to that instruction at the current address plus the offset. An offset of 0 makes the branch a NOP, because the flow of control is transferred to the next instruction as normal.
Example:
b Done // Always Branch to the “done”
label.
do something
Done:
return // Always Branch to TOS
syscall [op (, op)*] System Call
Op-code: _1_1_0_1_1_0_0_0_
Stack:
System Routine Code Arg0 Arg1 … |
è |
[optional |
Description:
Call a system routine.
TBD
call {.< | .<= | .= | .<> | .>= | .>} [op [,op]] [->label] Conditional Call
Op-code: _1_1_0_1_1_<_=_>_
Where “<=>” are the condition bits:
000 = used by syscall.
001 = Call if Greater Than 011 = Call if Greater Than or Equal
010 = Call if equal 101 = Call if Not Equal
100 = Call if Less Than 110 = Call if Less Than or Equal
111 = used by call
Stack:
Relative Code Offset Right Side Value Left Side Value |
è |
Return Address |
Description:
Conditional call is just like Conditional b, except it pushed the return address.
If “Left Side Value” condition “Right Side Value” is true, this:
4) Pops Offset
5) Pushes the address of the next instruction
6) Changse the flow of control to the instruction at the current address plus the “Relative Code Offset”.
Example:
call CalculateAge
call.>= 21, GetADrink
call [->label] Call a Subroutine
Op-code: _1_1_0_1_1_1_1_1_
Stack:
Relative Code Offset |
è |
Return Address |
Description:
Pop Offset, push the address of the next instruction, and change the flow of control to the instruction at the current address plus the “Relative Code Offset”.
load [.p][.b|.l] [op] Load 1, 2 or 4 bytes and push it on the stack.
Op-code: _1_1_1_0_m_m_s_s_
Where mm is the type of memory to read from.
00 = Normal data memory
01 = EEPROM memory
10 = reserved (hardware?)
11 = reserved
Where ss is the number of bytes to load.
00 = reserved (for 8-byte?)
01 = load 1 signed byte
10 = load 2 signed bytes
11 = load 4 signed bytes
Stack:
Memory Address |
è |
Memory Value |
Description:
The value on the stack is interpreted as an offset into one of four areas. It is popped off and replaced by the value stored at that offset. Data is stored Big Endian; that is, the most significant byte is stored in to lower offset.
store [.p][.b|.l] [op [, op]] Store 1, 2 or 4 bytes into memory.
Op code: _1_1_1_1_m_m_s_s_
Where mm is the type of memory to read from.
00 = Normal data memory
01 = EEPROM memory
10 = reserved (hardware?)
11 = reserved
Where ss is the number of bytes to load.
00 = reserved (8-bytes?)
01 = Store 1 byte
10 = Store 2 bytes
11 = Store 4 bytes
Stack:
Memory Address Value to be stored |
è |
|
Description:
Store TOS at TOS-1. The 1, 2 or 4 bytes on the top of the stack are stored started at the memory offset stored at TOS-1. Data is stored in Big Endian format; that is, the most significant byte is stored in to lower offset. If only 1 or 2 bytes are being stored, the upper bits are ignored.