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.