Volume 0 - Basics¶
Hello World
¶
This is the shortest Digirule ASM program.
You can key it in by entering the numbers 4,1,8,1,0
, or you can dgasm
and dgsim
it first.
1# DigiruleASM "Hello World"
2
3COPYLA 1
4ADDLA 1
5HALT
The Quick Brown Fox Jumps Over The Lazy Dog¶
This is a pangram phrase where every letter of English is used exactly once.
The following code snippet is the Digirule ASM equivalent where every operation is used exactly once.
1# The quick brown fox jumps over the lazy dog
2
3.EQU status_reg=252
4.EQU zero_bit=0
5.EQU carry_bit=1
6
7SBR 7 R0
8SHIFTRL R0
9SHIFTRR R1
10INCR R0
11COPYRA R0
12XORLA 0xFF
13ANDLA 0xF0
14SUBLA 0x10
15ORLA 0x02
16CALL some_proc
17ADDRA R1
18XORRA R1
19SUBRA R1
20ORRA R1
21INCRJZ R0
22BCRSC zero_bit status_reg
23DECRJZ R1
24CBR zero_bit status_reg
25COPYAR R1
26COPYRR R1 R0
27ADDRPC R0
28HALT
29
30some_proc:
31COPYLR 0x01 R1
32BCRSS zero_bit status_reg
33RETLA 0xF
34RETURN
35
36R0:
37.DB 0
38R1:
39.DB 0
40.DB 0xFF
41.DB 0xFF
42.DB 0xFF
43.DB 0xFF
44.DB 0xFF
45HALT
Assignments¶
Most programming languages have assignments. But, just typing a=42
in an editor, is not enough.
Something must realise the intention. Here is what assignments come down to on the Digirule.
Assigning a literal¶
1# Literal assignment
2# Or what in a higher level language
3# could be written as:
4
5# byte R0=0
6# ...
7# ...
8# R0 = 1
9
10COPYLR 1 R0
11HALT
12
13R0:
14.DB 0
Assigning to expression¶
1# Assigning to expression 1
2# Or what in a higher level language
3# would be written as:
4
5# unsigned char R0=1, R1=42
6# ...
7# R0 = R1
8
9# Obviously an "expression" is not visible here.
10# But, at the ASM level the expression itself
11# has to be explicitly evaluated and this would
12# still be the last step in order to send the
13# result of the expression from some temporary
14# register to the variable
15
16COPYRR R1 R0
17HALT
18
19R0:
20.DB 1
21R1:
22.DB 42
Assigning to expression with indirect addressing¶
1# Assigning to expression 2
2# Or what in a higher level language
3# would be written as:
4
5# byte *f_from = NULL, *f_to = NULL;
6# byte R0 = 1, R1 = 42;
7# ...
8# ...
9# f_from = &R1
10# f_to = &R0
11# *f_to = *f_from
12
13COPYLR R1 f_from # f_from = &R1
14COPYLR R0 f_to # f_to = &R0
15CALL f_copy_ind # *f_from = *f_to
16HALT
17
18R0:
19.DB 1
20R1:
21.DB 42
22
23# Memory copy by indirect addressing via self-modification.
24# We construct a suitable absolute
25# addressing copy instruction (COPYRR) and
26# execute it as a sub-routine over f_from, f_to
27f_copy_ind:
28.DB 7
29f_from:
30.DB 0
31f_to:
32.DB 0
33RETURN
Swapping the values of two variables¶
You can just “swap two variables”, or you can use Parallel Assignment
On the Digirule, it is a matter of 3 or 15 clock ticks.
Remember which algorithm uses so many swaps that it gets all…fizzy?
Swap two variables¶
1# Plain value swapping between two variables
2
3# Swaps are "traditionally" handled in a
4# Towers-of-Hanoi kind of way, using an
5# intermeidate variable.
6
7# Higher level languages might even offer
8# "parallel assignment" by which a swap
9# is expressed as a,b = b,a
10
11# Here, we implement the barebones way of
12# swapping the values of R0,R1, through
13# an intermediate variable R2. In a higher level
14# language this might be written as:
15
16# R2 = R0
17# R0 = R1
18# R1 = R2
19
20COPYRR R0 R2
21COPYRR R1 R0
22COPYRR R2 R1
23HALT
24
25R0:
26.DB 1
27R1:
28.DB 42
29R2:
30.DB 0
Swap with indirect copy¶
1# Indirect value swapping between two variables
2
3# This version achieves the same end-result as swap_simple
4# but uses memory copies via indirect addressing.
5
6# In a higher level language this might be written as:
7# unsigned char *f_from=NULL, *f_to=NULL;
8# unsigned char R0=1, R1=42, R2=0;
9# f_from=&R0;f_to=&R2; *f_to = *f_from;
10# f_from=&R1;f_to=&R0; *f_to = *f_from;
11# f_from=&R2;f_to=&R1; *f_to = *f_from;
12
13COPYLR R0 f_from
14COPYLR R2 f_to
15CALL f_copy_ind
16
17COPYLR R1 f_from
18COPYLR R0 f_to
19CALL f_copy_ind
20
21COPYLR R2 f_from
22COPYLR R1 f_to
23CALL f_copy_ind
24HALT
25
26R0:
27.DB 1
28R1:
29.DB 42
30R2:
31.DB 0
32
33# Memory copy by indirect addressing via self-modification.
34# We construct a suitable absolute
35# addressing copy instruction (COPYRR) and
36# execute it as a sub-routine over f_from, f_to
37f_copy_ind:
38.DB 7
39f_from:
40.DB 0
41f_to:
42.DB 0
43RETURN
Array Indexing¶
Up here, we can write my_array[5]
but this implies not one but two operations:
Discover the address
Fetch from address.
Here is how arrays are expressed on the Digirule.
1# Array indexing
2# Setup an array and retrieve a number from it
3
4# "Arrays" are consecutive memory addresses,
5# with a base address (where they start) and
6# a "stride" that depends on each element's
7# data type.
8
9# Here, we establish an array of 10 values (num_array)
10# and retrieve the 5th element from it (using array_idx).
11# The value is returned in array_idx_value
12
13# In a higher level language, this could be written
14# as:
15# unsigned char num_array[]={1,3,8,12,150,14,38,22,110,20};
16# num_array[5]
17# ...or even more accurately:
18# unsigned char *p_num_array=&num_array
19# *(p_num_array+5)
20
21# Notice here that accessing a particular index is not only a
22# matter of a memory transfer, but an addition too.
23
24# Calculate the memory offset
25COPYLA num_array
26ADDRA array_idx
27# Fetch the value from that memory offset to array_idx_value
28COPYAR f_from
29COPYLR array_idx_value f_to
30CALL f_copy_ind
31HALT
32
33
34f_copy_ind:
35# Memory copy by indirect addressing via self-modification.
36# We construct a suitable absolute
37# addressing copy instruction (COPYRR) and
38# execute it as a sub-routine over f_from, f_to
39.DB 7 # COPYRR opcode
40f_from:
41.DB 0
42f_to:
43.DB 0
44RETURN
45
46array_idx:
47.DB 5
48array_idx_value:
49.DB 0
50num_array:
51.DB 1,3,8,12,150,14,38,22,110,20
Conditional branching & the if
command¶
When you write if (R0 < R1) {} else {};
, how is this evaluated by a CPU?
What does an IF
look like when it comes to actually doing it.
Here it is, on a Digirule, the simplest form of flow control.
1# If (expression) run_block_a else run_block_b :: part 1
2
3# A high level "if" captures an ASM pattern where an
4# expression is evaluated and based on that expression's
5# result deciding whether to execute "run_block_a" or
6# "run_block_b" block of commands.
7
8# In a higher level language, this might be written as:
9# unsigned char R0, R1, R3
10
11.EQU status_reg=252 # The status register on the Digirule
12.EQU carry_bit=1
13
14# IF STARTS HERE
15# To test the expression, we
16# first have to evaluate the
17# expression.
18
19COPYRA R0
20SUBRA R1
21
22# We now check the result of
23# the expression, if the
24# carry bit is set after
25# R0-R1, then R0>R1 else
26# R0<=R1.
27
28BCRSC carry_bit status_reg
29JUMP lt
30JUMP gte
31
32
33# "THEN" BRANCH STARTS HERE
34# then R2 = 0xF0
35
36lt:
37COPYLR 0xF0 R2
38HALT
39
40
41# "ELSE" BRANCH STARTS HERE
42# else R2 = 0xF0
43
44gte:
45COPYLR 0x0F R2
46HALT
47
48
49R0:
50.DB 1
51R1:
52.DB 2
53R2:
54.DB 0xFF
There is a difference between if (R0 < R1)...;
and if (R0<=R1)...;
and on the Digirule
that difference is 2 clock cycles.
EVERY symbol counts.
1# If (expression) run_block_a else run_block_b :: part 2
2
3# This is almost identical to R0<R1 but since the expression
4# now is R0<=R1, we also add an extra branch to show
5# the equality case. Typically, only two exit points
6# from this block are maintained.
7
8
9.EQU status_reg=252 # The status register on the Digirule
10.EQU carry_bit=1
11.EQU zero_bit=0
12
13
14# Evaluate the expression...
15COPYRA R0
16SUBRA R1
17
18# Test its result...
19BCRSC carry_bit status_reg
20JUMP lt
21JUMP gte
22
23# "THEN" BRANCH STARTS HERE
24lt:
25COPYLR 0xF0 R2
26HALT
27
28gte:
29# At this point, we know that R0<R1 does NOT hold.
30# But, the opposite of R0<R1 is not R1>R0 but
31# R1>0 OR R1==R0. Therefore, knowing that
32# R0 IS NOT LESS THAN R1 we proceed here to
33# test if R0==R1.
34BCRSC zero_bit status_reg
35JUMP gte_final
36JUMP gt_final
37HALT
38
39# The two branches are split here just to illustrate
40# the <= distinction. Typically:
41
42# "ELSE" BRANCH STARTS HERE
43
44gte_final:
45COPYLR 0x18 R2
46HALT
47gt_final:
48COPYLR 0x0F R2
49HALT
50
51
52R0:
53.DB 2
54R1:
55.DB 1
56R2:
57.DB 0xFF
Iteration¶
We already saw some simple flow control in Digirule ASM.
Diverting execution depending on an expression soon leads to doing things repeatedly until a condition is met.
Here we re-use that to show``FOR`` and WHILE
ASM code equivalents on a Digirule.
1# For - loop (a.k.a Bounded Iteration)
2
3# At the ASM level, there is only conditional
4# and unconditional branching and therefore
5# just one pattern that leads to iteration.
6
7# FOR and WHILE, the two fundamental ways to
8# express iteration, are higher level constructs.
9
10# FOR is used when the iteration bounds (start
11# and end) are known; and
12# WHILE is used when the bounds are not known but
13# an expression that determines when the loop
14# has to conclude is known.
15
16# There is nothing at the CPU level that actually
17# "does" a FOR, or WHILE.
18
19# In a higher level language, this would be written as:
20
21# unsigned char R0=63;
22# for (R0=4;R0++;R0<22) {}
23
24
25.EQU status_reg=252 # The status register on the Digirule2
26.EQU zero_bit=0
27
28# Set the initial value of the iteration variable R0
29COPYLR 4 R0 # Equivalent to: for (R0=4;...
30loop_start:
31NOP # Equivalent to: {}
32
33INCR R0 # Equivalent to: R0++;...
34
35# End condition satisfied?
36COPYRA R0
37SUBLA 22 # Equivalent to: ...R0<22)
38BCRSS zero_bit status_reg
39JUMP loop_start # Jump to loop_start if it doesn't.
40HALT
41
42R0:
43.DB 63
Copying a memory block¶
1# Copy across memory (arrays).
2
3# An array represents a continuous memory block
4# that begins at some address and has a length.
5
6# In some high level languages, such as C,
7# the array must be homogeneous. That is, every
8# one of its elements can be of one and only one
9# type.
10# In others, such as Python, each
11# element of the array can be an object of a
12# different type.
13
14# These incredibly flexible arrays are, again,
15# higher level constructs. In other words, they
16# are conveniences that are still pieced
17# together and delivered via the same low level
18# operations.
19
20# At the level of the CPU, the only thing that
21# trully _exists_ is mathematical and memory
22# operations.
23
24# Here, copying one array to another is a matter
25# of calling a COPY operation ITERATIVELY for every
26# one of the array's elements. And right now, on the
27# Digirule2, we know how to both do iterations and
28# indirect copies.
29
30# In a higher level language, this could be expressed
31# as:
32# unsigned short R0[] = {0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA};
33# unsigned short R1[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
34# unsigned short *f_from = NULL, *f_to = NULL;
35# unsigned short f_block_len = 6
36# f_from = &R0;
37# f_to = &R1;
38# while (f_block_len){
39# *f_to = *f_from;
40# f_block_len--;
41# f_to++;
42# f_from++;
43
44
45COPYLR R0 f_from # f_from = &R0;
46COPYLR R1 f_to # f_from = &R0;
47CALL f_copy_block
48HALT
49
50
51# Generalises f_copy_ind so that it copies the values
52# of an array of length f_copy_block.
53# To call it, set:
54# f_from to the beginning of the source block
55# f_to to the beginning of the target block
56# f_copy_block to how many elements to copy
57
58f_copy_block:
59CALL f_copy_ind # *f_to = *f_from;
60INCR f_from # *f_from++;
61INCR f_to # *f_to++;
62DECRJZ f_block_len # f_block_len--; AND while (f_block_len){...
63JUMP f_copy_block
64RETURN
65
66
67
68# Memory copy by indirect addressing via self-modification.
69# We construct a suitable absolute
70# addressing copy instruction (COPYRR) and
71# execute it as a sub-routine over f_from, f_to
72
73f_copy_ind:
74.DB 7
75f_from:
76.DB 0
77f_to:
78.DB 0
79RETURN
80
81R0:
82.DB 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA
83
84R1:
85.DB 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
86
87f_block_len:
88.DB 6
Swapping values between two memory blocks¶
1# Swap the values of two arrays around.
2
3# This program extends the idea of swapping
4# the values of two variables (2 1-byte values),
5# to swapping the contents of two arrays.
6
7# This is realised by using a third array that
8# stores the value of one of the variables being
9# swapped.
10
11# In a higher level language, this could be written as:
12# void f_copy_block(unigned short *f_from,
13# unsigned short *f_to,
14# unsigned short f_block_len) {
15# while (f_block_len){
16# *f_to = *f_from;
17# f_to++;
18# f_from++;
19# f_block_len--;
20# }
21# }
22# unsigned int R0[] = {0xFA, 0xBF, 0xAB, 0xFA, 0xBF, 0xAB};
23# unsigned int R1[] = {0xBA, 0xEB, 0xAE, 0xBA, 0xEB, 0xAE};
24# unsigned int R2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
25# f_copy_block(R0,R2,6)
26# f_copy_block(R1,R0,6)
27# f_copy_block(R2,R1,6)
28
29
30COPYLR R0 f_from
31COPYLR R2 f_to
32COPYLR 6 f_block_len
33CALL f_copy_block
34
35COPYLR R1 f_from
36COPYLR R0 f_to
37COPYLR 6 f_block_len
38CALL f_copy_block
39
40COPYLR R2 f_from
41COPYLR R1 f_to
42COPYLR 6 f_block_len
43CALL f_copy_block
44HALT
45
46
47# Generalises f_copy_ind so that it copies the values
48# of an array of length f_copy_block.
49# To call it, set:
50# f_from to the beginning of the source block
51# f_to to the beginning of the target block
52# f_copy_block to how many elements to copy
53
54f_copy_block:
55CALL f_copy_ind
56INCR f_from
57INCR f_to
58DECRJZ f_block_len
59JUMP f_copy_block
60RETURN
61
62# Memory copy by indirect addressing via self-modification.
63# We construct a suitable absolute
64# addressing copy instruction (COPYRR) and
65# execute it as a sub-routine over f_from, f_to
66
67f_copy_ind:
68.DB 7
69f_from:
70.DB 0
71f_to:
72.DB 0
73RETURN
74
75R0:
76.DB 0xFA, 0xBF, 0xAB, 0xFA, 0xBF, 0xAB
77
78R1:
79.DB 0xBA, 0xEB, 0xAE, 0xBA, 0xEB, 0xAE
80
81R2:
82.DB 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
83
84f_block_len:
85.DB 0