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:

  1. Discover the address

  2. 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