Human Factors of XR: Using Human Factors to Design XR Systems
Erlang assembly
1. {function, max, 3, 2}.
{label,1}.
0x00 {allocate,1,3}.
0x01 {move,{x,2},{y,0}}.
0x02 {call,2,{f,2}}.
0x03 {move,{y,0},{x,1}}.
0x04 {call_last,2,{f,2},1}.
{function, max, 2, 4}.
{label,2}.
0x05 {test,is_lt,{f,3},[{x,1},{x,0}]}.
0x06 return.
{label,3}.
0x07 {move,{x,1},{x,0}}.
0x08 return.
CP normal_exit
IP 0x00
X 7 5 9
Y
We’re calling the max(7,5,9) function.
Arguments are placed in the X registers
and IP points to the first instruction in
the function.
We assume that we spawned a new
process just for this function call, so the
CP points to a special instruction that
will terminate the process normally
when the function returns.
In this example, all instructions have
been marked with fake addresses (they
ignore that operand also use memory),
that we’ll use with CP, IP and jump
operations.
2. {function, max, 3, 2}.
{label,1}.
0x00 {allocate,1,3}.
0x01 {move,{x,2},{y,0}}.
0x02 {call,2,{f,2}}.
0x03 {move,{y,0},{x,1}}.
0x04 {call_last,2,{f,2},1}.
{function, max, 2, 4}.
{label,2}.
0x05 {test,is_lt,{f,3},[{x,1},{x,0}]}.
0x06 return.
{label,3}.
0x07 {move,{x,1},{x,0}}.
0x08 return.
CP normal_exit
IP 0x01
X 7 5 9
Y normal_exit ???
The allocate instruction allocates
one slot (1 is the first operand) on the
stack.
That means that we’re going to call a
function and one term will need to
survive this call.
The second operand says that there are
3 used X registers in case a garbage
collection is needed to complete this
instruction.
3. {function, max, 3, 2}.
{label,1}.
0x00 {allocate,1,3}.
0x01 {move,{x,2},{y,0}}.
0x02 {call,2,{f,2}}.
0x03 {move,{y,0},{x,1}}.
0x04 {call_last,2,{f,2},1}.
{function, max, 2, 4}.
{label,2}.
0x05 {test,is_lt,{f,3},[{x,1},{x,0}]}.
0x06 return.
{label,3}.
0x07 {move,{x,1},{x,0}}.
0x08 return.
CP normal_exit
IP 0x02
X 7 5 9
Y normal_exit 9
Here we simply move the third
argument to the allocated slot on the
stack.
Note that it’s the top of the stack,
except the CP that was saved by the
allocate instruction.
4. {function, max, 3, 2}.
{label,1}.
0x00 {allocate,1,3}.
0x01 {move,{x,2},{y,0}}.
0x02 {call,2,{f,2}}.
0x03 {move,{y,0},{x,1}}.
0x04 {call_last,2,{f,2},1}.
{function, max, 2, 4}.
{label,2}.
0x05 {test,is_lt,{f,3},[{x,1},{x,0}]}.
0x06 return.
{label,3}.
0x07 {move,{x,1},{x,0}}.
0x08 return.
CP 0x03
IP 0x05
X 7 5 9
Y normal_exit 9
The call operation, jumps to another
function, that has been marked by the
label 2.
In order to do this, we set the CP to a
next operation in the current function,
and the IP to a first instruction in the
max/2 function.
Please note that we don’t need to
modify the X registers as the correct
arguments are already there.
5. {function, max, 3, 2}.
{label,1}.
0x00 {allocate,1,3}.
0x01 {move,{x,2},{y,0}}.
0x02 {call,2,{f,2}}.
0x03 {move,{y,0},{x,1}}.
0x04 {call_last,2,{f,2},1}.
{function, max, 2, 4}.
{label,2}.
0x05 {test,is_lt,{f,3},[{x,1},{x,0}]}.
0x06 return.
{label,3}.
0x07 {move,{x,1},{x,0}}.
0x08 return.
CP 0x03
IP 0x06
X 7 5 9
Y normal_exit 9
In this operation we compare
arguments in the X1 and X0 registers.
If X1 < X0 we simply advance the IP.
Otherwise we jump to the label 3.
6. {function, max, 3, 2}.
{label,1}.
0x00 {allocate,1,3}.
0x01 {move,{x,2},{y,0}}.
0x02 {call,2,{f,2}}.
0x03 {move,{y,0},{x,1}}.
0x04 {call_last,2,{f,2},1}.
{function, max, 2, 4}.
{label,2}.
0x05 {test,is_lt,{f,3},[{x,1},{x,0}]}.
0x06 return.
{label,3}.
0x07 {move,{x,1},{x,0}}.
0x08 return.
CP 0x03
IP 0x03
X 7 5 9
Y normal_exit 9
5 is less than 7, therefore we simply
advanced the IP.
The return instruction simply rewrites
CP to IP.
Please note that the result of this
function call is in the X0 register and we
don’t need to put it there as the result is
the first function argument
(max(7, 5) == 7).
7. {function, max, 3, 2}.
{label,1}.
0x00 {allocate,1,3}.
0x01 {move,{x,2},{y,0}}.
0x02 {call,2,{f,2}}.
0x03 {move,{y,0},{x,1}}.
0x04 {call_last,2,{f,2},1}.
{function, max, 2, 4}.
{label,2}.
0x05 {test,is_lt,{f,3},[{x,1},{x,0}]}.
0x06 return.
{label,3}.
0x07 {move,{x,1},{x,0}}.
0x08 return.
CP 0x03
IP 0x04
X 7 9 9
Y normal_exit 9
Here we restore the argument we
saved on the stack.
At the same time we are preparing for
the second call to the max/2 function,
setting its second argument.
8. {function, max, 3, 2}.
{label,1}.
0x00 {allocate,1,3}.
0x01 {move,{x,2},{y,0}}.
0x02 {call,2,{f,2}}.
0x03 {move,{y,0},{x,1}}.
0x04 {call_last,2,{f,2},1}.
{function, max, 2, 4}.
{label,2}.
0x05 {test,is_lt,{f,3},[{x,1},{x,0}]}.
0x06 return.
{label,3}.
0x07 {move,{x,1},{x,0}}.
0x08 return.
CP normal_exit
IP 0x05
X 7 9 9
Y
We’re calling the max/2 function for the
second time, but this time it’s more
tricky.
We don’t need to modify the result, so
we can simply do a tail-recursive call.
Therefore, instead of just jumping to the
function we need to do cleanup.
We clean 1 slot (third operand) on the
stack, we also restore the CP from the
stack and delete it.
9. {function, max, 3, 2}.
{label,1}.
0x00 {allocate,1,3}.
0x01 {move,{x,2},{y,0}}.
0x02 {call,2,{f,2}}.
0x03 {move,{y,0},{x,1}}.
0x04 {call_last,2,{f,2},1}.
{function, max, 2, 4}.
{label,2}.
0x05 {test,is_lt,{f,3},[{x,1},{x,0}]}.
0x06 return.
{label,3}.
0x07 {move,{x,1},{x,0}}.
0x08 return.
CP normal_exit
IP 0x07
X 7 9 9
Y
We again compare the values of X1
and X0.
This time, X1 is not less than X0 so we
jump to the label 3.
10. {function, max, 3, 2}.
{label,1}.
0x00 {allocate,1,3}.
0x01 {move,{x,2},{y,0}}.
0x02 {call,2,{f,2}}.
0x03 {move,{y,0},{x,1}}.
0x04 {call_last,2,{f,2},1}.
{function, max, 2, 4}.
{label,2}.
0x05 {test,is_lt,{f,3},[{x,1},{x,0}]}.
0x06 return.
{label,3}.
0x07 {move,{x,1},{x,0}}.
0x08 return.
CP normal_exit
IP 0x08
X 9 9 9
Y
We’re preparing to return from the
function, by putting the result into the
X0 register.
11. {function, max, 3, 2}.
{label,1}.
0x00 {allocate,1,3}.
0x01 {move,{x,2},{y,0}}.
0x02 {call,2,{f,2}}.
0x03 {move,{y,0},{x,1}}.
0x04 {call_last,2,{f,2},1}.
{function, max, 2, 4}.
{label,2}.
0x05 {test,is_lt,{f,3},[{x,1},{x,0}]}.
0x06 return.
{label,3}.
0x07 {move,{x,1},{x,0}}.
0x08 return.
CP normal_exit
IP normal_exit
X 9 9 9
Y
Now we can finally return from the
function and move to the original
Continuation Pointer.
The result is in the X0 register.