; destination wrapping around the buffer
OUTPUT (64, 8) ; Check destination has been updated
; Output 0x4141 4141 0061 4141
LOAD (destination, copy)
:copy ; Overwrite the copy instruction
COPY-LITERAL (32, 2, $destination)
OUTPUT (copy, 2) ; Output 0x4141
LOAD (byte_copy_left, 72) ; Set up new circular buffer
LOAD (byte_copy_right, 82)
LOAD (destination, 82) ; Set destination to byte_copy_right
MEMSET (72, 10, 65, 1) ; Fill the buffer with 0x41 - 4A
COPY-OFFSET (2, 6, $destination) ; Copy from within circular
; buffer to outside buffer
LOAD (offset, 6)
COPY-OFFSET ($offset, 4, $destination)
; Copy from byte_copy_right
; so reading outside buffer
OUTPUT ($byte_copy_right, 10) ; Output 0x494A 4142 4344 494A 4142,
; which is ’IJABCDIJAB’
LOAD (destination, 80) ; Put destination within the
; buffer
COPY-OFFSET (4, 4, $destination) ; Copy where destination wraps
OUTPUT (destination, 2) ; Output 0x004A
COPY-OFFSET (5, 4, $destination) ; Copy where offset wraps from
; left back around to the right
OUTPUT (destination, 2) ; Output 0x004E
OUTPUT ($byte_copy_left, 10) ; Output the circular buffer
; 0x4748 4845 4647 4748 4546,
; which is ’GHHEFGGHEF’
END-MESSAGE (0, 0, 0, 0, 0, 0, 0)
The output of the code is above, and the cost of execution is 216
UDVM cycles.
2.8. MEMSET
This section gives assembly code to test the MEMSET instruction. The
code is designed to test that the following boundary cases have been
correctly implemented:
1. The MEMSET instruction overwrites the registers byte_copy_left
and byte_copy_right.
2. The output values of the MEMSET instruction do not lie between 0
and 255 inclusive (in which case they must be taken modulo 2^8).
at (64)
:byte_copy_left pad (2)
:byte_copy_right pad (2)
at (128)
LOAD (byte_copy_left, 128) ; sets up a circular buffer
LOAD (byte_copy_right, 129) ; of 1 byte between 0x0080 and 0x0081
MEMSET (64, 129, 0, 1) ; fills up the memory in the range
; 0x0040-0x007f with 0x00, ... 0x3f;
; then it writes successively at
; 0x0080 the following values 0x40, ... 0x80
; as a side effect, the values of
; bcl and bcr are modified.
; before and during the MEMSET:
; byte_copy_left: 0x0080 byte_copy_right: 0x0081
; after the MEMSET:
; byte_copy_left: 0x0001 byte_copy_right: 0x0203
MEMSET (129, 15, 64, 15) ; fills the memory range 0x0080-0x008f
; with values 0x40, 0x4f, ... 0xf4, 0x03, 0x12.
; as a side effect, it overwrites a
; part of the code including itself
OUTPUT (128, 16) ; outputs 0x8040 4f5e 6d7c 8b9a
; a9b8 c7d6 e5f4 0312
END-MESSAGE (0, 0, 0, 0, 0, 0, 0)
The output of the code is 0x8040 4f5e 6d7c 8b9a a9b8 c7d6 e5f4 0312.
Executing the code costs 166 UDVM cycles.
2.9. CRC
This section gives assembly code to test the CRC instruction. The
code does not test any specific boundary cases (as there do not
appear to be any) but focuses instead on verifying the CRC algorithm.
at (64)
:byte_copy_left pad (2)
:byte_copy_right pad (2)
:crc_value pad (2)
:crc_string_a pad (24)
:crc_string_b pad (20)
at (128)
MEMSET (crc_string_a, 24, 1, 1) ; sets up between 0x0046 and 0x005d
; a byte string containing 0x01,
; 0x02, ... 0x18
MEMSET (crc_string_b, 20, 128, 1) ; sets up between 0x005e and 0x0071
; a byte string containing 0x80,
; 0x81, ... 0x93
INPUT-BYTES (2, crc_value, decompression_failure)
; reads in 2 bytes representing
; the CRC value of the byte string
; of 44 bytes starting at 0x0046
CRC ($crc_value, crc_string_a, 44, decompression_failure)
; computes the CRC value of the
; byte string crc_string_a
; concatenated with byte string
; crc_string_b (with a total
; length of 44 bytes).
; if the computed value does
; not match the 2-byte value read
; previously, the program ends
; with DECOMPRESSION-FAILURE.
END-MESSAGE (0, 0, 0, 0, 0, 0, 0)
:decompression_failure
DECOMPRESSION-FAILURE
If the compressed message is 0x62cb, then the code should
successfully terminate with no output, and with a total execution
cost of 95 UDVM cycles. For different 2-byte compressed messages,
the code should terminate with a decompression failure.
2.10. INPUT-BITS
This section gives assembly code to test the INPUT-BITS instruction.
The code is designed to test that the following boundary cases have
been correctly implemented:
1. The INPUT-BITS instruction changes between any of the four
possible bit orderings defined by the input_bit_order register.
2. The INPUT-BITS instruction inputs 0 bits.
3. The INPUT-BITS instruction requests data that lies beyond the end
of the compressed message.
at (64)
:byte_copy_left pad (2)
:byte_copy_right pad (2)
:input_bit_order pad (2)
:result pad (2)
at (128)
:start
INPUT-BITS ($input_bit_order, result, end_of_message) ; reads in
; exactly as many bits as the 2-byte
; value written in the input_bit_order
; register, get out of the loop when
; no more bits are available at input.
OUTPUT (result, 2) ; outputs as a 2-byte integer
; the previously read bits
ADD ($input_bit_order, 1) ; if at the beginning of this loop the
; register input_bit_order is 0,
REMAINDER ($input_bit_order, 7) ; then its value varies periodically
; like this: 2, 4, 6, 1, 3, 5, 7.
ADD ($input_bit_order, 1) ; that gives for the FHP bits: 010,
; 100, 110, 001, 011, 101, 111
JUMP (start) ; run the loop once more
:end_of_message
END-MESSAGE (0, 0, 0, 0, 0, 0, 0)
An example of a compressed message is 0x932e ac71, which decompresses
to give the output 0x0000 0002 0002 0013 0000 0003 001a 0038.
Executing the code costs 66 UDVM cycles.
2.11. INPUT-HUFFMAN
This section gives assembly code to test the INPUT-HUFFMAN
instruction. The code is designed to test that the following
boundary cases have been correctly implemented:
1. The INPUT-HUFFMAN instruction changes between any of the four
possible bit orderings defined by the input_bit_order register.
2. The INPUT-HUFFMAN instruction inputs 0 bits.
3. The INPUT-HUFFMAN instruction requests data that lies beyond the
end of the compressed message.
at (64)
:byte_copy_left pad (2)
:byte_copy_right pad (2)
:input_bit_order pad (2)
:result pad (2)
at (128)
:start
INPUT-HUFFMAN (result, end_of_message, 2, $input_bit_order, 0,
$input_bit_order, $input_bit_order, $input_bit_order, 0, 65535, 0)
OUTPUT (result, 2)
ADD ($input_bit_order, 1)
REMAINDER ($input_bit_order, 7)
ADD ($input_bit_order, 1)
JUMP (start)
:end_of_message
END-MESSAGE (0, 0, 0, 0, 0, 0, 0)
An example of a compressed message is 0x932e ac71 66d8 6f, which
decompresses to give the output 0x0000 0003 0008 04d7 0002 0003 0399
30fe. Executing the code costs 84 UDVM cycles.
As the code is run, the input_bit_order changes through all possible
values to check usage of the H and P bits. The number of bits to
input each time is taken from the value of input_bit_order. The
sequence is the following:
Input_bit_order (bin) Total bits input by Huffman Value
000 0 0
010 2 3
100 4 8
110 12 1239
001
P-bit changed, throw away 6 bits
001 1 2
011 3 3
101 10 921
111 14 12542
010
P-bit changed, throw away 4 bits
010 0 - not enough bits so terminate
2.12. INPUT-BYTES
This section gives assembly code to test the INPUT-BYTES instruction.
The code is designed to test that the following boundary cases have
been correctly implemented:
1. The INPUT-BYTES instruction inputs 0 bytes.
2. The INPUT-BYTES instruction requests data that lies beyond the
end of the compressed message.
3. The INPUT-BYTES instruction is used after part of a byte has been
input (e.g., by the INPUT-BITS instruction).
at (64)
:byte_copy_left pad (2)
:byte_copy_right pad (2)
:input_bit_order pad (2)
:result pad (2)
:output_start pad (4)
:output_end
at (128)
LOAD (byte_copy_left, output_start)
LOAD (byte_copy_right, output_end)
:start
INPUT-BITS ($input_bit_order, result, end_of_message)
OUTPUT (result, 2)
ADD ($input_bit_order, 2)
REMAINDER ($input_bit_order, 7)
INPUT-BYTES ($input_bit_order, output_start, end_of_message)
OUTPUT (output_start, $input_bit_order)
ADD ($input_bit_order, 1)
JUMP (start)
:end_of_message
END-MESSAGE (0, 0, 0, 0, 0, 0, 0)
An example of a compressed message is 0x932e ac71 66d8 6fb1 592b dc9a
9734 d847 a733 874e 1bcb cd51 b5dc 9659 9d6a, which decompresses to
give the output 0x0000 932e 0001 b166 d86f b100 1a2b 0003 9a97 34d8
0007 0001 3387 4e00 08dc 9651 b5dc 9600 599d 6a. Executing the code
costs 130 UDVM cycles.
As the code is run, the input_bit_order changes through all possible
values to check usage of the F and P bits. The number of bits or
bytes to input each time is taken from the value of input_bit_order.
For each INPUT-BYTES instruction, the remaining bits of the byte are
thrown away. The P-bit always changes on the byte boundary so no
bits are thrown away. The sequence is the following:
Input_bit_order (bin) Input bits Input bytes Output
000 0 0x0000
010 2 0x932e
011 3 0x0001
101 5 0xb166 d866 b1
110 6 0x001a
001 1 0x2b
010 2 0x0003
100 4 0x9a97 34d8
101 5 0x0007
000 0
001 1 0x0001
011 3 0x3384 4e
100 4 0x0008
110 6 0xdc96 51b5 dc96
111 7 0x0059
010 2 0x9d6a
011 3 - no bits left so terminate
2.13. Stack Manipulation
This section gives assembly code to test the PUSH, POP, CALL, and
RETURN instructions. The code is designed to test that the following
boundary cases have been correctly implemented:
1. The stack manipulation instructions overwrite the UDVM register
stack_location.
2. The CALL instruction specifies a reference operand rather than an
absolute value.
3. The PUSH instruction pushes the value contained in stack_fill
onto the stack.
4. The stack_location register contains an odd integer.
at (64)
:byte_copy_left pad (2)
:byte_copy_right pad (2)
:input_bit_order pad (2)
:stack_location pad (2)
:next_address pad (2)
at (128)
LOAD (stack_location, 64)
PUSH (2)
PUSH ($64)
PUSH (66) ; Stack now contains 2, 1, 66
; so $stack_location = 66
OUTPUT (64, 8) ; Output 0x0003 0002 0001 0042
POP (64) ; Pop value 66 from address 70 to address 64
POP ($stack_location) ; Pop value 1 from address 68 to address 66
; so stack_fill is overwritten to be 1
POP (stack_location) ; Pop value 1 from address 68 to address 70
OUTPUT (64, 8) ; Output 0x0042 0000 0001 0001
JUMP (address_a)
at (192)
:address_a
LOAD (stack_location, 32)
LOAD (next_address, address_c)
SUBTRACT ($next_address, address_b) ; next_address = 64
CALL (address_b) ; push 204 on stack
at (256)
:address_b
CALL ($next_address) ; push 256 on stack
at (320)
:address_c
LOAD (stack_location, 383)
LOAD (383, 26) ; overwrite $stack_location with 26
MULTILOAD (432, 3, 1, 49153, 32768)
; write bytes so that 433 and 434
; contain 0x01c0 = 448 and
; 435 and 436 contain 0x0180 = 384
RETURN ; pop 383 from the stack and jump
; there = 384, which is lsb of
; stack_fill, which now contains 25,
; which is UDVM instruction RETURN
; pop 448 from the stack and jump
; there
at (448)
END-MESSAGE (0, 0, 0, 0, 0, 0, 0)
The output of the code is 0x0003 0002 0001 0042 0042 0000 0001 0001,
and a total of 40 UDVM cycles are used.
2.14. Program Flow
This section gives assembly code to test the JUMP, COMPARE, and
SWITCH instructions. The code is designed to test that the following
boundary cases have been correctly implemented:
1. The address operands are specified as references to memory
addresses rather than as absolute values.
at (64)
:next_address pad (2)
:counter pad (1)
:counter_lsb pad (1)
:switch_counter pad (2)
at (128)
LOAD (switch_counter, 4)
:address_a
LOAD (next_address, address_c)
SUBTRACT ($next_address, address_b) ; address_c - address_b
OUTPUT (counter_lsb, 1)
:address_b
JUMP ($next_address) ; Jump to address_c
:address_c
ADD ($counter, 1)
LOAD (next_address, address_a)
SUBTRACT ($next_address, address_d) ; address_a - address_d
OUTPUT (counter_lsb, 1)
:address_d
COMPARE ($counter, 6, $next_address, address_c, address_e)
; counter < 6, $next_address gives
; jump to address_a
:address_e
SUBTRACT ($switch_counter, 1) ; switch_counter = 3
LOAD (next_address, address_a)
SUBTRACT ($next_address, address_f) ; address_a - address_f
OUTPUT (counter_lsb, 1)
:address_f
SWITCH (4, $switch_counter, address_g, $next_address, address_c,
address_e)
; when $switch_counter = 1,