Extended assembly: introduction
In the previous section, the use of assembly-language was introduced as a convenient alternative to programming in raw machine-code.
However, the program-examples presented so far only used the native IBC-instruction. Making large programs using only IBC can be tedious, error-prone, and may result in near-unreadable code (for humans).
Macros were shown to be a solution to this: often-used instruction-sequences could be wrapped inside a macro-body, to be inserted at arbitrary points in the program.
Based on this macro-mechanism, a set of pseudo-instructions can be created, offering the programmer a larger instruction-repertoire than just IBC.
This section discusses some of these pseudo-instructions. (For a complete set, see the download-section.)
Pseudo-instructions using only IBC
As a start, 3 simple pseudo-instructions are given that make use only of IBC: "not1" (to invert a bit), "set1" (to set a bit to 1), and "cb1" (to clear a bit, and then jump to an address).
(Recall that the "1"-suffix is a reminder to the programmer that these instructions operate on 1 bit of RAM at a time.)
These 3 instructions can be used on their own, or can form the basis for more complex pseudo-instructions, as shown later on.
Inverting a bit is very trivial, and is executed in 1 instruction-cycle:
# # Invert the bit at address "MyAddr". # macro not1 MyAddr { ibc1 $MyAddr done ;# Invert the bit at ;# address "MyAddr". ;# ;# (Whatever the new bit- ;# value (0 or 1), ;# execution always ;# continues at "done".) : done }
Clearing a bit is similar, but can take either 1 or 2 instruction-cycles, depending on the initial bit-value:
# # Clear the bit at address "MyAddr", then unconditionally # jump to program-address "branch". # macro cb1 MyAddr branch { ibc1 $MyAddr $branch ;# Invert the bit at "MyAddr" ;# for the first time. ;# ;# If it was 1 and is now 0 ;# (thus, cleared), jump ;# to "branch". ibc1 $MyAddr $branch ;# If execution reaches here, ;# the bit was apparently ;# already cleared, and is ;# now 1. ;# ;# We clear it by inverting ;# it again, followed ;# by a guaranteed jump to ;# "branch". }
Setting a bit (to 1) can also take either 1 or 2 instruction-cycles:
# # Set the bit at address "MyAddr" to 1. # macro set1 MyAddr { : repeat ibc1 $MyAddr repeat ;# Invert the bit. ;# ;# If it is 0 after ;# inversion, invert ;# it again by executing ;# this instruction a ;# second time. ;# (When execution finally reaches here, ;# the bit is guaranteed to be 1, or set.) }
Local macro-variables
Recall that, from the IBC-instruction's semantics, a bit has to be inverted during each instruction-cycle, even if undesired.
A temporary "dummy"-bit can be used for this inversion to take place on, as shown in the following jump-instruction:
# # Unconditional jump to program-address "branch". # macro b branch { . tmp ;# This statement allocates (or reserves) a variable ;# at the next available RAM-address. (The assembler ;# keeps track of which RAM-addresses are already in ;# use.) ;# ;# No initialisation is done. As soon as the ;# instructions in this macro-body are all inserted, ;# the RAM-address used by "tmp" can be recycled. ;# ;# Therefore, this variable is only valid in this ;# macro-body, and is hence called a "local" ;# variable. cb1 $tmp $branch ;# Clear the data-bit at "tmp", ;# and then jump to program-address ;# "branch". ;# ;# The "tmp" variable is only used ;# because _some_ bit in RAM _has_ ;# to be inverted during each ;# instruction. (In this ;# instruction, such automatic ;# inversion is nothing more than ;# an unwanted byproduct.) }
(In the context of assembly-programs, "to jump" is often called "to branch" - hence the "b" in the names of all jump-related instructions, such as "cb" earlier, and "b" itself.)
Another reason for using local variables within a macro-body, is for temporary storage of helper-variables, e.g. during a computation.