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.

 


Arithmetic and Logical  Instructions  (1100)

 

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.

 


Flow Control Instructions (1101)

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
Address of Variable

è

 

 

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
return value]

 

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”.

 


Memory Instructions (1110) and (1111)

 

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.