In this system, we have a single operation— MOV, which moves the contents of one address to another address. These addresses are all absolute and are 16 bits wide, just like their contents.
There is a memory-mapped program counter, which is how control flow is handled.
start:
MOV PC, #start
Here the start:
represents a label for that region, and #start
represents the address of some spot of memory allocated to hold the constant address represented by start.
This functions as an infinite loop, constantly returning the program counter to the same spot, over and over again.
MOV ret, PC
Here we can take the value of the program counter (which now points to whatever instruction immediately follows) and store it at some address ret
.
We can copy the value of some address into some othe rone with
MOV a, b
Numbers are defined by the operations that can be done on them. With a jump table, we can assign different values different properties when operated on. However, we have to eschew the intuitive mapping between their representation and their meaning.
That is, we can considently define arithmetic such that the number 1
is represented as 8200
, and 2
as 8204
, 3
as 8208
and so forth.
We can implement these numbers as a jump table. Lets say we store some value at a particular address, identified as a
. It might be initialized with the value 1
, which may be represented as 8200
.
So it's kind of problematic to have this kind of numerical representation because it's kind of arbitrary and the outside world is much more likely to provide you with ordinary bytes. However, we can (with wires) just wire bits up differently— for instance, one could have an offset so that the low nibble is always zero.
That then gives us enough instructions of leeway to implement some sort of jump table which inlines a finite number of useful instructions.
By moving the value of the variable into the program counter, we can essentially jump to that specific address
MOV PC, a
At that address, we might define some set of conditional expressions
.org 8200
mov inc, #2
mov dec, #0
mov PC, #ret0
Essentially, what we do is we store some pointer at some address which represents a number. We jump to the address at that pointer, which sets some other regions of memory with the appropriate values. We return execution to some ret pointer which is set shortly before calling the number.
In pseudocode, the process of incrementing a value stored at a
mov ret, #next
mov pc, a
next:
mov a, inc
Rather than a single ret
register, we can have several registers depending on the contents of a particular value. For instance, if a number is zero, we can jump to the address stored in ret0
. This changes the code for incrementing values slightly
mov ret, #next
mov ret0, #next
mov pc, a
next:
mov a, inc
But this enables neat control flow abilities
mov ret, #nonzero
mov ret0, #zero
mov pc, a
zero:
; instructions to do if it's zero
nonzero:
; instructions to do if it's nonzero
42 + 19
while b > 0
inc a
dec b
.org a, #42
.org b, #19
mov a, #42
mov b, #19
loop:
mov ret, #next1
mov ret0, #finish
mov pc, b
next1:
mov ret, #next2
mov ret0, #next2
mov pc, a
next2:
mov a, inc
mov ret, #next3
mov ret0, #next3
mov pc, b
next3:
mov b, dec
finish: