Fabrication code is an arbitrary higher level language to be compiled into machine code for the DUO Compact.
Each line of fabrication code is comprised of a command name followed by arguments. Each term is separated by a space, and each line is separated by a newline. Overloading is permissible for commands.
The single DUO Compact processor operation is represented by the command NFC. (This name stands for "NOR and fork conditionally".) The command is overloaded with two forms:
NFC [src] [dest]: performs the NOR operation, skips to the next command in sequence regardless of resulting value.
NFC [src] [dest] [pointer1] [pointer2]: performs NOR operation, skips to second pointer if result was zero, otherwise first pointer.
An argument given as a number is treated as an explicit address. A number in parenthesis, such as (29), represents an address to a constant byte in memory. The keyword next may be used as an argument to represent the address of the next command.
A macro is a block of machine code repeated whenever it is called. Macros are intended for simple operations involving a small number of commands. It is more speed efficient in runtime to use macros, but they use a significant amount of space. A macro is declared in this fashion:
To call a macro elsewhere, use the same syntax as in calling a command. Do NOT use recursive macros, or you will surely die.
A static variable is used to store a byte in a pre-allocated space. The block for static variables is the last block of RAM. To declare one or more static variables, use the syntax below:
static [name] [name] [name]...
When the variable name appears as an argument in a command, the variable's address is used as the value. To free space, use this line:
free [name] [name] [name]...
To avoid an excessive number of static variables, use the free command as frequently as possible! Note that both the static and free commands do not affect runtime speed.
Define a label definition with this command:
label [name]
A label points to a location in the program. Call the label name as an argument to refer to the label's address. Also use the syntax labelName+[number] to point to a label with an offset, _labelName to access the lower byte, ^labelName for the upper byte, and !labelName to invert the value.
Use this command to jump to an address or label:
follow [address]
When using the follow command, no extra code is added; instead, the compiler retroactively modifies the previous command to jump to the desired address.
In some cases, you may want to align code to an address. To do so, use this command:
buffer [address]
This will shift all code below until it reaches the given address.
To insert sequential values into memory, use the sequence command with the syntax below:
sequence [value] [value] [value] [value]...
These values should not be in parentheses.
To indicate the start of code in flash memory, use the FLASH keyword. You can also reset the relative address for labels with the RESET_ADDRESS command:
[...code...]
FLASH
RESET_ADDRESS
[...code...]
= EXAMPLES =
This example code writes the value 27 to the debug output port.
macro clear dest
NFC (255) dest
end
macro logicalNOT src dest
clear dest
NFC src dest
end
macro jump pointer
static a
NFC a a pointer pointer
free a
end
macro output src dest
static a
logicalNOT src a
NFC a dest
free a
end
output (27) 49158
label loopStart
jump loopStart
The example below indicates whether the debug input is less than 16.
macro clear dest
NFC (255) dest
end
macro logicalNOT src dest
clear dest
NFC src dest
end
macro logicalANDNOT src1 src2 dest
logicalNOT src1 dest
NFC src2 dest
end
macro jump pointer
static a
NFC a a pointer pointer
free a
end
macro conditionalFork src pointer1 pointer2
static a
logicalNOT src a
NFC a a pointer1 pointer2
free a
end
macro output src dest
static a
logicalNOT src a
NFC a dest
free a
end
label loopStart
static a
logicalANDNOT 49162 (15) a
conditionalFork a case1 case2
free a
label case1
output (0) 49158
jump loopStart
label case2
output (1) 49158
jump loopStart
This next example code adds 1 to the debug input.
macro clear dest
NFC (255) dest
end
macro logicalNOT src dest
clear dest
NFC src dest
end
macro invert dest
NFC dest dest
end
macro getBitAndFork src data address
static a
logicalNOT data a
NFC src a address next
free a
end
macro NFCPairAndJump src1 src2 dest1 dest2 address
NFC src1 dest1
NFC src2 dest2 address address
end
macro increment src dest
static a b
clear a
clear b
getBitAndFork src (1) carry1
getBitAndFork src (2) carry2
getBitAndFork src (4) carry3
getBitAndFork src (8) carry4
getBitAndFork src (16) carry5
getBitAndFork src (32) carry6
getBitAndFork src (64) carry7
getBitAndFork src (128) carry8
NFC (255) dest finish finish
label carry1
NFCPairAndJump (254) (255) a b skip
label carry2
NFCPairAndJump (253) (254) a b skip
label carry3
NFCPairAndJump (251) (252) a b skip
label carry4
NFCPairAndJump (247) (248) a b skip
label carry5
NFCPairAndJump (239) (240) a b skip
label carry6
NFCPairAndJump (223) (224) a b skip
label carry7
NFCPairAndJump (191) (192) a b skip
label carry8
NFCPairAndJump (127) (128) a b skip
label skip
clear dest
NFC src dest
NFC b dest
NFC a dest
invert dest
free a b
label finish
end
macro jump pointer
static a
NFC a a pointer pointer
free a
end
macro output src dest
static a
logicalNOT src a
NFC a dest
free a
end
label loopStart
static a
increment 49162 a
output a 49158
free a
jump loopStart
In this case, it is actually more efficient to use a lookup table when incrementing a byte. The code below uses this method.