├── .gitignore
├── docs
├── green.png
├── hex.png
├── red.png
└── verbose.png
├── .editorconfig
├── CONTRIBUTING.md
├── src
├── includes
│ ├── config.asm
│ ├── state.asm
│ ├── strings.asm
│ ├── structure.asm
│ ├── screen.asm
│ └── asserts.asm
└── zx-spec.asm
├── CHANGELOG.md
├── test
├── test-hex.asm
├── test-verbose-mix.asm
├── test-failures.asm
└── test-passes.asm
├── .github
└── workflows
│ └── build.yml
├── LICENSE
├── Makefile
├── examples
└── checkout-kata.asm
├── README.md
└── test.py
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | bas2tap/bin/
3 |
--------------------------------------------------------------------------------
/docs/green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhargreaves/zx-spec/HEAD/docs/green.png
--------------------------------------------------------------------------------
/docs/hex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhargreaves/zx-spec/HEAD/docs/hex.png
--------------------------------------------------------------------------------
/docs/red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhargreaves/zx-spec/HEAD/docs/red.png
--------------------------------------------------------------------------------
/docs/verbose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhargreaves/zx-spec/HEAD/docs/verbose.png
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*.asm]
4 | indent_style = tab
5 | indent_size = 8
6 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Pull requests are accepted. Please ensure tests pass in CI before requesting merge.
2 |
--------------------------------------------------------------------------------
/src/includes/config.asm:
--------------------------------------------------------------------------------
1 | zxspec_config_normal_ink_colour equ $07
2 | zxspec_config_normal_paper_colour equ $00
3 | zxspec_config_pass_ink_colour equ $04
4 | zxspec_config_fail_ink_colour equ $02
5 | zxspec_config_banner_ink_colour equ $07
6 |
--------------------------------------------------------------------------------
/src/zx-spec.asm:
--------------------------------------------------------------------------------
1 | jp _zxspec_init
2 | include includes/config.asm
3 | include includes/structure.asm
4 | include includes/screen.asm
5 | include includes/asserts.asm
6 | include includes/state.asm
7 | include includes/strings.asm
8 | _zxspec_init equ $
--------------------------------------------------------------------------------
/src/includes/state.asm:
--------------------------------------------------------------------------------
1 | _zxspec_num_pass db 0
2 | _zxspec_num_fail db 0
3 | _zxspec_shown_names db 0 ; LSB = Group Name, 2nd LSB = Test Name
4 | _zxspec_test_name db 0,0
5 | _zxspec_test_name_length db 0,0
6 | _zxspec_group_name db 0,0
7 | _zxspec_group_name_length db 0,0
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6 |
7 | ## [0.1.0] - 2017-10-29
8 | ### Added
9 | - Added 'verbose output' option
--------------------------------------------------------------------------------
/test/test-hex.asm:
--------------------------------------------------------------------------------
1 | org 8000h
2 | zxspec_config_display_numbers_as_hex equ $FF
3 |
4 | include src/zx-spec.asm
5 |
6 | spec_init
7 |
8 | describe 'assert_byte_equal'
9 | it 'fails for different value'
10 | assert_byte_equal tmp_2, $3B
11 |
12 | describe 'assert_word_equal'
13 | it 'fails for different value'
14 | assert_word_equal tmp_1, $0000
15 | assert_word_equal tmp_1, $ACDC
16 |
17 | spec_end
18 |
19 | ret
20 | tmp_1 dw $FFFF
21 | tmp_2 db $2A
22 | end 8000h
23 |
--------------------------------------------------------------------------------
/test/test-verbose-mix.asm:
--------------------------------------------------------------------------------
1 | org 8000h
2 | zxspec_config_verbose_output equ $FF
3 |
4 | include src/zx-spec.asm
5 |
6 | spec_init
7 |
8 | describe 'assert_pass'
9 | it 'passes test'
10 | assert_pass
11 |
12 | describe 'assert_a_equal'
13 | it 'passes for same value'
14 | ld a,5
15 | assert_a_equal 5
16 |
17 | describe 'assert_a_equal'
18 | it 'fails for different value'
19 | ld a,5
20 | assert_a_equal 255
21 |
22 | it 'fails again'
23 | ld a,5
24 | assert_a_equal 255
25 |
26 | spec_end
27 |
28 | ret
29 | end 8000h
30 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build & Test
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | test:
11 | runs-on: ubuntu-latest
12 |
13 | services:
14 | docker:
15 | image: docker:20.10.7
16 | options: --privileged
17 |
18 | steps:
19 | - name: Checkout code
20 | uses: actions/checkout@v4
21 |
22 | - name: Set up Python
23 | uses: actions/setup-python@v4
24 | with:
25 | python-version: '3'
26 |
27 | - name: Run tests
28 | run: |
29 | echo "Starting Xvfb"
30 | export DISPLAY=:99.0
31 | sudo apt-get update
32 | sudo apt-get install -y xvfb
33 | /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
34 | sleep 3
35 |
36 | echo "Set up environment"
37 | export FUSE="docker run -v $(pwd):/app -v /tmp/.X11-unix:/tmp/.X11-unix --workdir /app --privileged -e DISPLAY=unix$DISPLAY -i rhargreaves/fuse-emulator"
38 |
39 | echo "Running tests"
40 | make test
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Robert Hargreaves
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/includes/strings.asm:
--------------------------------------------------------------------------------
1 | _zxspec_text_banner db _nl, ' '
2 | db 10h, zxspec_config_banner_ink_colour
3 | db ' ZX Spec v0.1.0'
4 | db 10h, zxspec_config_normal_ink_colour
5 | db _nl, _nl
6 | _zxspec_text_banner_end equ $
7 | _zxspec_text_pass db _nl, 'Pass: '
8 | _zxspec_text_pass_end equ $
9 | _zxspec_text_fail db ', Fail: '
10 | _zxspec_text_fail_end equ $
11 | _zxspec_text_total db ', Total: '
12 | _zxspec_text_total_end equ $
13 | _zxspec_text_expected db 'Expected: '
14 | _zxspec_text_expected_end equ $
15 | _zxspec_text_actual db ', Actual: '
16 | _zxspec_text_actual_end equ $
17 | _zxspec_text_exit db '-- ZX SPEC TEST END --'
18 | _zxspec_text_exit_end equ $
19 | _zxspec_text_pass_mark db 10h,zxspec_config_pass_ink_colour
20 | db '.'
21 | db 10h,zxspec_config_normal_ink_colour
22 | _zxspec_text_pass_mark_end equ $
23 | _zxspec_text_fail_mark db 10h,zxspec_config_fail_ink_colour
24 | db 'x'
25 | db 10h,zxspec_config_normal_ink_colour
26 | _zxspec_text_fail_mark_end equ $
27 | _zxspec_text_pass_ink db 10h,zxspec_config_pass_ink_colour
28 | _zxspec_text_pass_ink_end equ $
29 | _zxspec_text_fail_ink db 10h,zxspec_config_fail_ink_colour
30 | _zxspec_text_fail_ink_end equ $
31 | _zxspec_text_normal_ink db 10h,zxspec_config_normal_ink_colour
32 | _zxspec_text_normal_ink_end equ $
--------------------------------------------------------------------------------
/src/includes/structure.asm:
--------------------------------------------------------------------------------
1 | spec_init macro
2 | ld a,zxspec_config_normal_paper_colour ; Set border to background colour to avoid last
3 | ; two lines being wrong colour
4 | call _zxspec_rom_border_int
5 | ld a,zxspec_config_normal_ink_colour ; Set ink colour
6 | ld (_zxspec_attr_p),a
7 | call _zxspec_rom_cl_all ; clear screen
8 | ld a,_zxspec_output_stream ; upper screen
9 | call _zxspec_rom_chan_open ; open channel
10 | _print_text _zxspec_text_banner, _zxspec_text_banner_end
11 | endm
12 |
13 | describe macro group_name
14 | local group_name_start, group_name_end
15 | ld hl,_zxspec_shown_names
16 | res 0,(hl) ; Reset shown group name
17 | jp group_name_end
18 | group_name_start db group_name
19 | group_name_end ld hl,group_name_start
20 | ld (_zxspec_group_name),hl
21 | ld hl,group_name_end - group_name_start
22 | ld (_zxspec_group_name_length),hl
23 | endm
24 |
25 | it macro test_name
26 | local test_name_start, test_name_end
27 | jp test_name_end
28 | test_name_start db test_name
29 | test_name_end ld hl,test_name_start
30 | ld (_zxspec_test_name),hl
31 | ld hl,test_name_end - test_name_start
32 | ld (_zxspec_test_name_length),hl
33 | endm
34 |
35 | spec_end macro
36 | _update_border
37 | call _print_summary
38 | if defined zxspec_test_mode
39 | _print_zxspec_test_end
40 | endif
41 | endm
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | ifeq ($(CI),true)
2 | TTY_ARG :=
3 | else
4 | TTY_ARG := -t
5 | endif
6 |
7 | PASMO := docker run -v $(PWD):/work \
8 | -w /work \
9 | -i $(TTY_ARG) \
10 | ghcr.io/rhargreaves/pasmo:v0.5.5 \
11 | pasmo -I src
12 | BIN := bin
13 | FUSE ?= fuse
14 |
15 | .PHONY: test clean run
16 |
17 | clean:
18 | rm -rf $(BIN)
19 |
20 | $(BIN):
21 | mkdir $(BIN)
22 |
23 | $(BIN)/zx-spec-green.tap: test/test-passes.asm $(BIN)
24 | $(PASMO) --tapbas $< $@
25 |
26 | $(BIN)/zx-spec-red.tap: test/test-failures.asm $(BIN)
27 | $(PASMO) --tapbas $< $@
28 |
29 | $(BIN)/zx-spec-mixture.tap: test/test-verbose-mix.asm $(BIN)
30 | $(PASMO) --tapbas $< $@
31 |
32 | $(BIN)/zx-spec-hex.tap: test/test-hex.asm $(BIN)
33 | $(PASMO) --tapbas $< $@
34 |
35 | $(BIN)/checkout-kata.tap: examples/checkout-kata.asm $(BIN)
36 | $(PASMO) --tapbas $< $@
37 |
38 | $(BIN)/test-passes.tap: test/test-passes.asm $(BIN)
39 | $(PASMO) --equ zxspec_test_mode --tapbas $< $@
40 |
41 | $(BIN)/test-failures.tap: test/test-failures.asm $(BIN)
42 | $(PASMO) --equ zxspec_test_mode --tapbas $< $@
43 |
44 | $(BIN)/test-hex.tap: test/test-hex.asm $(BIN)
45 | $(PASMO) --equ zxspec_test_mode --tapbas $< $@
46 |
47 | $(BIN)/test-verbose-mix.tap: test/test-verbose-mix.asm $(BIN)
48 | $(PASMO) --equ zxspec_test_mode --tapbas $< $@
49 |
50 | test: $(BIN)/test-passes.tap $(BIN)/test-failures.tap $(BIN)/test-hex.tap $(BIN)/test-verbose-mix.tap
51 | ./test.py
52 |
53 | demo-mix: $(BIN)/zx-spec-mixture.tap
54 | $(FUSE) --tape $< --auto-load --no-autosave-settings
55 |
56 | demo-green: $(BIN)/zx-spec-green.tap
57 | $(FUSE) --tape $< --auto-load --no-autosave-settings
58 |
59 | demo-red: $(BIN)/zx-spec-red.tap
60 | $(FUSE) --tape $< --auto-load --no-autosave-settings
61 |
62 | demo-hex: $(BIN)/zx-spec-hex.tap
63 | $(FUSE) --tape $< --auto-load --no-autosave-settings
64 |
65 | checkout-kata: $(BIN)/checkout-kata.tap
66 | $(FUSE) --tape $< --auto-load --no-autosave-settings
67 |
--------------------------------------------------------------------------------
/src/includes/screen.asm:
--------------------------------------------------------------------------------
1 | ; ROM Routine Addresses
2 | _zxspec_rom_cl_all equ 0dafh ; clear screen
3 | _zxspec_rom_chan_open equ 1601h ; open channel
4 | _zxspec_rom_pr_string equ 203ch ; print string (DE = start, BC = length)
5 | _zxspec_rom_stack_bc equ 2d2bh
6 | _zxspec_rom_print_fb equ 2de3h ; Print FP number
7 | _zxspec_rom_border_int equ 229bh ; Set border colour
8 |
9 | ; System Variable Addresses
10 | _zxspec_attr_p equ 5c8dh ; permanent set colours
11 |
12 | ; Stream Constants
13 | _zxspec_screen_stream equ 2
14 | _zxspec_printer_stream equ 3
15 | if defined zxspec_test_mode ; Definable via Pasmo command line
16 | _zxspec_output_stream equ _zxspec_printer_stream
17 | else
18 | _zxspec_output_stream equ _zxspec_screen_stream
19 | endif
20 |
21 | ; Characters
22 | _space equ ' '
23 | _nl equ 13 ; New line character
24 | _cross equ 'x'
25 | _period equ '.'
26 | _comma equ ','
27 | _d_quote equ $22 ; double quote
28 |
29 | ; Border Constants
30 | _zxspec_border_port equ 0feh
31 | _zxspec_red_border equ 2
32 | _zxspec_green_border equ 4
33 |
34 | ; Printing
35 | _print_text macro txt_start, txt_end ; Prints text
36 | _print_text_with_len txt_start,txt_end-txt_start
37 | endm
38 |
39 | _print_text_with_len macro txt_start, txt_len ; Supports NN or (NN)
40 | push de
41 | push bc
42 | ld de,txt_start ; text address
43 | ld bc,txt_len ; string length
44 | push af
45 | call _zxspec_rom_pr_string ; print string
46 | pop af
47 | pop bc
48 | pop de
49 | endm
50 |
51 | _print_value macro addr ; Prints value at memory location
52 | push hl
53 | ld hl,addr
54 | push bc
55 | ld b,0
56 | ld c,(hl)
57 | call _print_bc_as_dec
58 | pop bc
59 | pop hl
60 | endm
61 |
62 | _print_char macro code
63 | push af
64 | ld a,code
65 | rst 16
66 | pop af
67 | endm
68 |
69 | _print_newline macro
70 | _print_char _nl
71 | endm
72 |
73 | _print_total macro
74 | ld hl,_zxspec_num_fail
75 | ld c,(hl)
76 | ld hl,_zxspec_num_pass
77 | ld a,(hl)
78 | add a,c
79 | ld b,0
80 | ld c,a
81 | call _print_bc_as_dec ; print number of total tests
82 | endm
83 |
84 | _print_summary proc
85 | local set_fail_colour, print_line
86 | _print_newline
87 | ld a,(_zxspec_num_fail)
88 | cp 0
89 | jr nz, set_fail_colour
90 | _print_text _zxspec_text_pass_ink, _zxspec_text_pass_ink_end
91 | jr print_line
92 | set_fail_colour _print_text _zxspec_text_fail_ink, _zxspec_text_fail_ink_end
93 | print_line _print_text _zxspec_text_pass, _zxspec_text_pass_end
94 | _print_value _zxspec_num_pass ; print number of passing tests
95 | _print_text _zxspec_text_fail, _zxspec_text_fail_end
96 | _print_value _zxspec_num_fail ; print number of failing tests
97 | _print_text _zxspec_text_total, _zxspec_text_total_end
98 | _print_total
99 | _print_newline
100 | ret
101 | endp
102 |
103 | _print_zxspec_test_end macro
104 | _print_text _zxspec_text_exit, _zxspec_text_exit_end
105 | endm
106 |
107 | _print_bc_as_hex proc
108 | push hl
109 | ld h,b
110 | ld l,c
111 | call _print_hl_as_hex
112 | pop hl
113 | ret
114 | endp
115 |
116 | _print_bc_as_dec proc
117 | push af
118 | push bc
119 | push de
120 | call _zxspec_rom_stack_bc
121 | call _zxspec_rom_print_fb
122 | pop de
123 | pop bc
124 | pop af
125 | ret
126 | endp
127 |
128 | _print_num_in_bc proc
129 | if defined zxspec_config_display_numbers_as_hex
130 | call _print_bc_as_hex
131 | else
132 | call _print_bc_as_dec
133 | endif
134 | ret
135 | endp
136 |
137 | _print_num_in_c proc
138 | if defined zxspec_config_display_numbers_as_hex
139 | push af
140 | call _print_c_as_hex
141 | pop af
142 | else
143 | push bc
144 | ld b,0
145 | call _print_bc_as_dec
146 | pop bc
147 | endif
148 | ret
149 | endp
150 |
151 | _print_hl_as_hex proc
152 | local conv
153 | ld c,h
154 | call _print_c_as_hex
155 | ld c,l
156 | _print_c_as_hex ld a,c
157 | rra
158 | rra
159 | rra
160 | rra
161 | call conv
162 | ld a,c
163 | conv and $0F
164 | add a,$90
165 | daa
166 | adc a,$40
167 | daa
168 | rst 16
169 | ret
170 | endp
171 |
172 | _print_bytes macro start, length
173 | local loop, done
174 | ld b,length ; B = length
175 | ld hl,start ; HL = start
176 | call _print_bytes_r
177 | endm
178 |
179 | _print_bytes_r proc ; Input: HL = start addr, B = length
180 | local loop, done
181 | loop ld a,(hl) ; A = current byte
182 | ld c,a ; Copy A into C
183 | call _print_c_as_hex ; Print C
184 | ld a,b ; Load length into accumulator
185 | cp 1 ; Is 1?
186 | jr z,done ; If 1 - we're done
187 | _print_char _comma ; Print _comma otherwise
188 | inc hl ; Next byte
189 | djnz loop
190 | done ret
191 | endp
192 |
193 | _normal_ink macro
194 | _print_text _zxspec_text_normal_ink, _zxspec_text_normal_ink_end
195 | endm
196 |
197 | _pass_ink macro
198 | _print_text _zxspec_text_pass_ink, _zxspec_text_pass_ink_end
199 | endm
200 |
201 | _fail_ink macro
202 | _print_text _zxspec_text_fail_ink, _zxspec_text_fail_ink_end
203 | endm
204 |
205 | ; Border Painting
206 | _paint_border macro colour
207 | push af
208 | ld a,colour
209 | out (_zxspec_border_port),a
210 | pop af
211 | endm
212 |
213 | _update_border macro
214 | local update_border_end
215 | push af
216 | ld a,(_zxspec_num_fail)
217 | cp 0
218 | jp nz,update_border_end
219 | _paint_border _zxspec_green_border
220 | update_border_end equ $
221 | pop af
222 | endm
--------------------------------------------------------------------------------
/test/test-failures.asm:
--------------------------------------------------------------------------------
1 | org 8000h
2 |
3 | include src/zx-spec.asm
4 |
5 | spec_init
6 |
7 | describe 'assert_fail'
8 | it 'fails test'
9 | assert_fail
10 |
11 | describe 'assert_hl_equal'
12 | it 'fails for different value'
13 | ld hl,500
14 | assert_hl_equal 502
15 | assert_hl_equal 503
16 |
17 | it 'fails for different value'
18 | ld hl,1
19 | assert_hl_equal 2
20 | assert_hl_equal 3
21 |
22 | describe 'assert_bc_equal'
23 | it 'fails for different value'
24 | ld bc,500
25 | assert_bc_equal 502
26 | assert_bc_equal 503
27 |
28 | describe 'assert_de_equal'
29 | it 'fails for different value'
30 | ld de,500
31 | assert_de_equal 502
32 | assert_de_equal 503
33 |
34 | describe 'assert_ix_equal'
35 | it 'fails for different value'
36 | ld ix,$0102
37 | assert_ix_equal $0103
38 | assert_ix_equal $0104
39 |
40 | describe 'assert_a_equal'
41 | it 'fails for different value'
42 | ld a,5
43 | assert_a_equal 250
44 |
45 | describe 'assert_b_equal'
46 | it 'fails for different value'
47 | ld b,5
48 | assert_a_equal 250
49 |
50 | describe 'assert_c_equal'
51 | it 'fails for different value'
52 | ld c,5
53 | assert_a_equal 250
54 |
55 | describe 'assert_d_equal'
56 | it 'fails for different value'
57 | ld d,5
58 | assert_d_equal 250
59 |
60 | describe 'assert_e_equal'
61 | it 'fails for different value'
62 | ld e,5
63 | assert_e_equal 250
64 |
65 | describe 'assert_h_equal'
66 | it 'fails for different value'
67 | ld h,5
68 | assert_h_equal 250
69 |
70 | describe 'assert_l_equal'
71 | it 'fails for different value'
72 | ld l,5
73 | assert_l_equal 250
74 |
75 | describe 'assert_a_not_equal'
76 | it 'fails for same value'
77 | ld a,5
78 | assert_a_not_equal 5
79 |
80 | describe 'assert_b_not_equal'
81 | it 'fails for same value'
82 | ld b,5
83 | assert_b_not_equal 5
84 |
85 | describe 'assert_c_not_equal'
86 | it 'fails for same value'
87 | ld c,5
88 | assert_c_not_equal 5
89 |
90 | describe 'assert_d_not_equal'
91 | it 'fails for same value'
92 | ld d,5
93 | assert_d_not_equal 5
94 |
95 | describe 'assert_e_not_equal'
96 | it 'fails for same value'
97 | ld e,5
98 | assert_e_not_equal 5
99 |
100 | describe 'assert_h_not_equal'
101 | it 'fails for same value'
102 | ld h,5
103 | assert_h_not_equal 5
104 |
105 | describe 'assert_l_not_equal'
106 | it 'fails for same value'
107 | ld l,5
108 | assert_l_not_equal 5
109 |
110 | describe 'assert_hl_not_equal'
111 | it 'fails for same value'
112 | ld hl,$0102
113 | assert_hl_not_equal $0102
114 |
115 | describe 'assert_bc_not_equal'
116 | it 'fails for same value'
117 | ld bc,$0102
118 | assert_bc_not_equal $0102
119 |
120 | describe 'assert_de_not_equal'
121 | it 'fails for same value'
122 | ld de,$0102
123 | assert_de_not_equal $0102
124 |
125 | describe 'assert_ix_not_equal'
126 | it 'fails for same value'
127 | ld ix,$0102
128 | assert_ix_not_equal $0102
129 |
130 | describe 'assert_a_is_zero'
131 | it 'fails for non-zero'
132 | ld a,255
133 | assert_a_is_zero
134 |
135 | describe 'assert_a_is_not_zero'
136 | it 'fails for zero'
137 | ld a,0
138 | assert_a_is_not_zero
139 |
140 | describe 'assert_byte_equal'
141 | it 'fails for different value'
142 | assert_byte_equal tmp_1, $FF
143 |
144 | describe 'assert_byte_not_equal'
145 | it 'fails for same value'
146 | assert_byte_not_equal tmp_1, $CC
147 |
148 | describe 'assert_word_equal'
149 | it 'fails for different value'
150 | assert_word_equal tmp_2, $0102
151 |
152 | describe 'assert_word_not_equal'
153 | it 'fails for same value'
154 | assert_word_not_equal tmp_2, $FFDD
155 |
156 | describe 'assert_str_equal'
157 | it 'fails for different value'
158 | assert_str_equal tmp_str, 'diff test string'
159 |
160 | describe 'assert_str_not_equal'
161 | it 'fails for same value'
162 | assert_str_not_equal tmp_str, 'test string'
163 |
164 | it "fails for same value with termination bit set"
165 | assert_str_not_equal copyright_rom_addr, "\x7f 1982 Sinclair Research Ltd"
166 |
167 | describe 'assert_bytes_equal'
168 | it 'fails for different value'
169 | assert_bytes_equal bytes_1, 8, bytes_3
170 |
171 | describe 'assert_bytes_not_equal'
172 | it 'fails for same value'
173 | assert_bytes_not_equal bytes_1, 8, bytes_2
174 |
175 | describe 'assert_z_set'
176 | it "fails when zero flag reset"
177 | ld a,5
178 | or 0
179 | assert_z_set
180 |
181 | describe 'assert_z_reset'
182 | it "fails when zero flag set"
183 | ld a,0
184 | or a
185 | assert_z_reset
186 |
187 | describe 'assert_carry_set'
188 | it "fails when carry flag reset"
189 | ld a,$00
190 | add a,1
191 | assert_carry_set
192 |
193 | describe 'assert_carry_reset'
194 | it "fails when carry flag set"
195 | ld a,$FF
196 | add a,1
197 | assert_carry_reset
198 |
199 | describe 'assert_s_set'
200 | it "fails when signed flag reset"
201 | ld a,$00
202 | inc a
203 | assert_s_set
204 |
205 | describe 'assert_s_reset'
206 | it "fails when signed flag set"
207 | ld a,$00
208 | dec a
209 | assert_s_reset
210 |
211 | describe 'assert_p_v_set'
212 | it "fails when overflow flag reset"
213 | ld a,$80
214 | inc a
215 | assert_p_v_set
216 |
217 | describe 'assert_p_v_reset'
218 | it "fails when overflow flag set"
219 | ld a,$7F
220 | inc a
221 | assert_p_v_reset
222 |
223 | spec_end
224 |
225 | ret
226 | bytes_1 db $12,$34,$56,$78,$9A,$BC,$DE,$F0
227 | bytes_2 db $12,$34,$56,$78,$9A,$BC,$DE,$F0
228 | bytes_3 db $12,$34,$55,$66,$AA,$BB,$DE,$F0
229 | tmp_1 db $CC
230 | tmp_2 dw $FFDD
231 | tmp_str db 'test string'
232 | tmp_str_end equ $
233 | copyright_rom_addr equ 1539h
234 | end 8000h
235 |
--------------------------------------------------------------------------------
/examples/checkout-kata.asm:
--------------------------------------------------------------------------------
1 | ; Based on http://codekata.com/kata/kata09-back-to-the-checkout/
2 |
3 | ; Item Unit Special
4 | ; Price Price
5 | ; --------------------------
6 | ; A 50 3 for 130
7 | ; B 30 2 for 45
8 | ; C 20
9 | ; D 15
10 |
11 | org 8000h
12 |
13 | include src/zx-spec.asm
14 |
15 | load_items macro items ; Loads item string.
16 | ; Output: HL = start, DE = length
17 | local _start, _end
18 | jp _end
19 | _start db items
20 | _end equ $
21 | ld hl,_start
22 | ld de,_end-_start
23 | endm
24 |
25 | spec_init
26 |
27 | describe 'price'
28 | it 'Returns 0 for no items'
29 | ld hl,0 ; Any mem address
30 | ld de,0 ; Zero length items
31 |
32 | call price
33 |
34 | assert_b_equal 0
35 |
36 | it 'Returns price for item A'
37 | load_items 'A'
38 |
39 | call price
40 |
41 | assert_b_equal 50
42 |
43 | it 'Returns price for item B'
44 | load_items 'B'
45 |
46 | call price
47 |
48 | assert_b_equal 30
49 |
50 | it 'Returns price for item C'
51 | load_items 'C'
52 |
53 | call price
54 |
55 | assert_b_equal 20
56 |
57 | it 'Returns price for item D'
58 | load_items 'D'
59 |
60 | call price
61 |
62 | assert_b_equal 15
63 |
64 | it 'Returns price for AA'
65 | load_items 'AA'
66 |
67 | call price
68 |
69 | assert_b_equal 100
70 |
71 | it 'Returns price for ABCD'
72 | load_items 'ABCD'
73 |
74 | call price
75 |
76 | assert_b_equal 115
77 |
78 | it 'Applies discount for AAA'
79 | load_items 'AAA'
80 |
81 | call price
82 |
83 | assert_b_equal 130
84 |
85 | it 'Applies discount for AAAA'
86 | load_items 'AAAA'
87 |
88 | call price
89 |
90 | assert_b_equal 180
91 |
92 | it 'Applies discount for BB'
93 | load_items 'BB'
94 |
95 | call price
96 |
97 | assert_b_equal 45
98 |
99 | it 'Applies discounts for AAABB'
100 | load_items 'AAABB'
101 |
102 | call price
103 |
104 | assert_b_equal 175
105 |
106 | it 'Applies discounts for BBBB'
107 | load_items 'BBBB'
108 |
109 | call price
110 |
111 | assert_b_equal 90
112 |
113 | it 'Clears item counts'
114 | load_items 'AA'
115 | call price
116 | load_items 'A'
117 | call price
118 |
119 | assert_b_equal 50
120 |
121 | describe 'inc_item'
122 | it 'Increments item count for AA'
123 | call reset_item_count
124 | load_items 'A'
125 |
126 | call inc_item
127 | call inc_item
128 |
129 | assert_byte_equal item_counts, 2
130 |
131 | it 'Increments item count for B'
132 | call reset_item_count
133 | load_items 'B'
134 |
135 | call inc_item
136 |
137 | assert_byte_equal item_counts, 0
138 | assert_byte_equal item_counts+1, 1
139 |
140 | it 'Does not apply discount for AA'
141 | call reset_item_count
142 | load_items 'A'
143 |
144 | ld b,50
145 | call inc_item
146 | call inc_item
147 |
148 | assert_b_equal 50
149 |
150 | it 'Applies discount for AAA'
151 | call reset_item_count
152 | load_items 'A'
153 |
154 | ld b,50
155 | call inc_item
156 | call inc_item
157 | call inc_item
158 |
159 | assert_b_equal 30
160 |
161 | spec_end
162 |
163 | price proc ; The main checkout routine
164 | ; Input: HL = items start address, DE = items length
165 | ; Output: B = total price
166 | local loop, check_item
167 | ld a,e ; Prepare to check items length
168 | cp 0 ; Is items length zero?
169 | jr nz,check_item ; Check item if length non-zero
170 | ld b,0 ; Return early 0 if so.
171 | ret
172 | check_item call reset_item_count
173 | ld b,e ; B = string length
174 | ld a,0 ; Total Price = 0
175 | loop push bc
176 | push af
177 | call unit_price ; Get price of item in HL; store in B
178 | call inc_item ; Increment item count; deduct discounts from B
179 | pop af
180 | add a,b ; Add B to total price
181 | pop bc
182 | inc hl ; Next item
183 | djnz loop ; Decrement B; loop when != 0
184 | ld b,a ; Copy total price into B
185 | ret
186 | endp
187 |
188 | apply_discount macro item_count_index, threshold, discount
189 | ; Checks to see if an item's count (HL) is high enough (threshold) for a discount,
190 | ; applies the discount to the total price (B) & resets the item count.
191 | local done
192 | ld a,(hl) ; Load item count into acc
193 | cp threshold ; Is at threshold for discount?
194 | jr nz,done ; No? We're done.
195 | ld a,b ; Load total price into A
196 | sub discount ; Discount item
197 | ld b,a ; Store total price back into B
198 | ld a,0
199 | ld (item_counts+item_count_index),a ; Reset A item count to 0.
200 | done equ $
201 | endm
202 |
203 | reset_item_count proc ; Resets item counts for items A->D
204 | ld a,0
205 | rept 4, index
206 | ld (item_counts+index),a
207 | endm
208 | ret
209 | endp
210 |
211 | inc_item proc ; Increments item count & applies any discount
212 | ; Input: HL = item address
213 | ; Output: B = total price minus any deductions
214 | local done, is_a, is_b
215 | push hl
216 | ld a,(hl) ; Load char into acc.
217 | sub 'A' ; Convert item into index (Item A = 0, B = 1, C = 2...)
218 | push bc
219 | ld b,0
220 | ld c,a
221 | ld hl,item_counts ; HL = mem address of top of item counts
222 | add hl,bc ; HL = mem address of item count
223 | pop bc
224 | inc (hl) ; Increment item count
225 | ; From here: A = Item Index, (HL) = Item Count
226 | cp 0
227 | jr z,is_a ; Is A?
228 | cp 1
229 | jr z,is_b ; Is B?
230 | jr done ; Otherwise, apply no discount
231 | is_a apply_discount 0, 3, 20
232 | jr done
233 | is_b apply_discount 1, 2, 15
234 | jr done
235 | done pop hl
236 | ret
237 | endp
238 |
239 | cond_ret_price macro item_char, price
240 | cp item_char ; Is item?
241 | ld b,price ; Load up return value
242 | ret z ; Return if item
243 | endm
244 |
245 | unit_price proc ; Gets a unit price for an item
246 | ; Input: HL = item address
247 | ; Output: B = price
248 | ld a,(hl) ; Load item char
249 | cond_ret_price 'A', 50 ; Conditionally return price for specific item
250 | cond_ret_price 'B', 30
251 | cond_ret_price 'C', 20
252 | cond_ret_price 'D', 15
253 | ld b,0 ; Otherwise, return 0
254 | ret
255 | endp
256 |
257 | item_counts db 0,0,0,0 ; Enough room for 4 item SKUs
258 |
259 | end 8000h
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ZX Spec [](https://github.com/rhargreaves/zx-spec/actions/workflows/build.yml)
2 | A framework for test-driving assembly code for the Sinclair ZX Spectrum 48k.
3 |
4 |
5 |
6 |
7 |
8 | ## Usage
9 |
10 | The framework has been written with the [Pasmo assembler](http://pasmo.speccy.org/pasmodoc.html). Simply grab the source in `src` and include `zx-spec.asm` to write tests:
11 |
12 | ```asm
13 | include src/zx-spec.asm
14 |
15 | spec_init
16 |
17 | describe 'ld a,n'
18 | it 'sets A register to value'
19 | ld a,5
20 | assert_a_equal 5
21 |
22 | spec_end
23 | ```
24 |
25 | ### Names and Groupings
26 |
27 | You can optionally name a set of asserts using the `it` macro. This groups the asserts into a single test. In addition to `it`, you can use the `describe` macro to group one or more tests. Nested `describe` or `it` macros are not permitted.
28 |
29 | ### Verbose Output
30 |
31 | By default test descriptions are not output for tests that pass. You can output test descriptions even for passing tests by defining `zxspec_config_verbose_output` as non-zero either on the command-line of Pasmo (`--equ zxspec_config_verbose_output`), or in code before you call `spec_init`:
32 |
33 | ```
34 | zxspec_config_verbose_output equ $FF
35 | ```
36 |
37 | This can be used to generate a form of **living documentation** for your project.
38 |
39 |
40 |
41 |
42 |
43 | ### Hexadecimals
44 |
45 | By default, expected and actual values are displayed as decimals. You can switch to hexadecimal by defining `zxspec_config_display_numbers_as_hex` as non-zero either on the command-line of Pasmo (`--equ zxspec_config_display_numbers_as_hex`), or in code before you call `spec_init`:
46 |
47 | ```
48 | zxspec_config_display_numbers_as_hex equ $FF
49 | ```
50 |
51 | See [test/test-hex.asm](test/test-hex.asm) for example usage. Note that `assert_bytes_equal` always displays expected/actual bytes as a comma-seperated list of hexadecimal pairs, regardless of this setting.
52 |
53 |
54 |
55 |
56 |
57 | ## Assertions
58 |
59 | See [test/test-passes.asm](test/test-passes.asm) for examples.
60 |
61 | You can immediately pass or fail a test by using:
62 |
63 | * `assert_pass`
64 | * `assert_fail`
65 |
66 | ### Registers
67 |
68 | These assertions will preserve register values.
69 |
70 | #### 8-bit
71 |
72 | * `assert_a_equal`
73 | * `assert_a_not_equal`
74 | * `assert_b_equal`
75 | * `assert_b_not_equal`
76 | * `assert_c_equal`
77 | * `assert_c_not_equal`
78 | * `assert_d_equal`
79 | * `assert_d_not_equal`
80 | * `assert_e_equal`
81 | * `assert_e_not_equal`
82 | * `assert_h_equal`
83 | * `assert_h_not_equal`
84 | * `assert_l_equal`
85 | * `assert_l_not_equal`
86 | * `assert_a_is_zero`
87 | * `assert_a_is_not_zero`
88 |
89 | #### 16-bit
90 |
91 | * `assert_bc_equal`
92 | * `assert_bc_not_equal`
93 | * `assert_de_equal`
94 | * `assert_de_not_equal`
95 | * `assert_hl_equal`
96 | * `assert_hl_not_equal`
97 | * `assert_ix_equal`
98 | * `assert_ix_not_equal`
99 |
100 | ##### IY
101 |
102 | Asserting on the IY register value is not currently supported. The IY register is used by Spectrum ROM routines as a index to system variables and is not generally recommended to be used in custom routines due to the added complexity of ensuring its use does not interfere with normal operation.
103 |
104 | #### Flags
105 |
106 | * `assert_z_set`
107 | * `assert_z_reset`
108 | * `assert_carry_set`
109 | * `assert_carry_reset`
110 | * `assert_s_set`
111 | * `assert_s_reset`
112 | * `assert_p_v_set`
113 | * `assert_p_v_reset`
114 |
115 | ### Memory
116 |
117 | Be warned that these assertions will not preserve register values.
118 |
119 | #### Single-Byte
120 |
121 | * `assert_byte_equal`
122 | * `assert_byte_not_equal`
123 |
124 | #### Double-Byte Word
125 |
126 | * `assert_word_equal`
127 | * `assert_word_not_equal`
128 |
129 | #### Strings
130 |
131 | * `assert_str_equal`
132 | * `assert_str_not_equal`
133 |
134 | #### Multiple Bytes
135 |
136 | * `assert_bytes_equal`
137 | * `assert_bytes_not_equal`
138 |
139 | ## Dependencies
140 |
141 | * Python 3 (for running ZX Spec tests)
142 | * Docker (for running Pasmo)
143 | * Fuse Emulator
144 |
145 | **Linux**
146 |
147 | ```sh
148 | $ sudo apt-get install fuse-emulator-common spectrum-roms
149 | ```
150 |
151 | **macOS**
152 |
153 | ```sh
154 | $ brew install homebrew/games/fuse-emulator
155 | ```
156 |
157 | You can also run [Fuse with Docker](https://github.com/rhargreaves/fuse-emulator-docker/blob/master/run-fuse.sh) by setting the `FUSE` environment variable accordingly:
158 |
159 | ```sh
160 | $ export "FUSE=docker run -v /tmp/.X11-unix:/tmp/.X11-unix --privileged -e DISPLAY=unix$DISPLAY -it rhargreaves/fuse-emulator"
161 | ```
162 |
163 | ## Tests
164 |
165 | ZX Spec can be tested by running:
166 |
167 | ```
168 | $ make test
169 | ```
170 |
171 | Two sets of automated tests will run. One set results in all tests passing, and the other set results in all tests failing. The results are sent to an emulated ZX Printer (rather than sent to the display) which is then output by the emulator to a text file. This file is then validated to ensure the framework is working correctly.
172 |
173 | Unfortunately, the speed of the ZX Printer is also emulated resulting in the tests taking a few minutes to complete. I don't currently know of a way to speed this up!
174 |
175 | ## Demos
176 |
177 | You can run a couple of example demos:
178 |
179 | ### When all tests pass...
180 |
181 | ```
182 | $ make demo-green
183 | ```
184 |
185 | ### When all tests fail...
186 |
187 | ```
188 | $ make demo-red
189 | ```
190 |
191 | ### When there's a mixture...
192 |
193 | ```
194 | $ make demo-mix
195 | ```
196 |
197 |
198 |
199 |
200 |
201 | ## Example Kata
202 |
203 | I had a go at completing part of the [Checkout Kata](examples/checkout-kata.asm) using this framework.
204 |
205 | ## Credits
206 |
207 | * This project was inspired by [64spec](http://64bites.com/64spec/) - a Commodore 64 Testing Framework
208 |
--------------------------------------------------------------------------------
/test/test-passes.asm:
--------------------------------------------------------------------------------
1 | org 8000h
2 |
3 | include src/zx-spec.asm
4 |
5 | spec_init
6 |
7 | describe 'assert_pass'
8 | it 'passes test'
9 | assert_pass
10 |
11 | describe 'assert_hl_equal'
12 | it 'passes for same value'
13 | ld hl,$0102
14 | assert_hl_equal $0102
15 |
16 | it "doesn't affect value of hl"
17 | ld hl,$0102
18 | assert_hl_equal $0102
19 | assert_hl_equal $0102
20 |
21 | describe 'assert_bc_equal'
22 | it 'passes for same value'
23 | ld bc,$0102
24 | assert_bc_equal $0102
25 |
26 | it "doesn't affect value of bc"
27 | ld bc,$0102
28 | assert_bc_equal $0102
29 | assert_bc_equal $0102
30 |
31 | describe 'assert_de_equal'
32 | it 'passes for same value'
33 | ld de,$0102
34 | assert_de_equal $0102
35 |
36 | it "doesn't affect value of de"
37 | ld de,$0102
38 | assert_de_equal $0102
39 | assert_de_equal $0102
40 |
41 | describe 'assert_ix_equal'
42 | it 'passes for same value'
43 | ld ix,$0102
44 | assert_ix_equal $0102
45 |
46 | it "doesn't affect value of ix"
47 | ld ix,$0102
48 | assert_ix_equal $0102
49 | assert_ix_equal $0102
50 |
51 | describe 'assert_a_equal'
52 | it 'passes for same value'
53 | ld a,5
54 | assert_a_equal 5
55 |
56 | it "doesn't affect value of a"
57 | ld a,5
58 | assert_a_equal 5
59 | assert_a_equal 5
60 |
61 | describe 'assert_b_equal'
62 | it 'passes for same value'
63 | ld b,5
64 | assert_b_equal 5
65 |
66 | it "doesn't affect value of b"
67 | ld b,5
68 | assert_a_equal 5
69 | assert_a_equal 5
70 |
71 | describe 'assert_c_equal'
72 | it 'passes for same value'
73 | ld c,5
74 | assert_c_equal 5
75 |
76 | it "doesn't affect value of c"
77 | ld c,5
78 | assert_a_equal 5
79 | assert_a_equal 5
80 |
81 | describe 'assert_d_equal'
82 | it 'passes for same value'
83 | ld d,5
84 | assert_d_equal 5
85 |
86 | it "doesn't affect value of d"
87 | ld d,5
88 | assert_d_equal 5
89 | assert_d_equal 5
90 |
91 | describe 'assert_e_equal'
92 | it 'passes for same value'
93 | ld e,5
94 | assert_e_equal 5
95 |
96 | it "doesn't affect value of e"
97 | ld e,5
98 | assert_e_equal 5
99 | assert_e_equal 5
100 |
101 | describe 'assert_h_equal'
102 | it 'passes for same value'
103 | ld h,5
104 | assert_h_equal 5
105 |
106 | it "doesn't affect value of h"
107 | ld h,5
108 | assert_h_equal 5
109 | assert_h_equal 5
110 |
111 | describe 'assert_l_equal'
112 | it 'passes for same value'
113 | ld l,5
114 | assert_l_equal 5
115 |
116 | it "doesn't affect value of l"
117 | ld l,5
118 | assert_l_equal 5
119 | assert_l_equal 5
120 |
121 | describe 'assert_a_not_equal'
122 | it 'passes for different value'
123 | ld a,0
124 | assert_a_not_equal 5
125 |
126 | describe 'assert_b_not_equal'
127 | it 'passes for different value'
128 | ld b,0
129 | assert_b_not_equal 5
130 |
131 | describe 'assert_c_not_equal'
132 | it 'passes for different value'
133 | ld c,0
134 | assert_c_not_equal 5
135 |
136 | describe 'assert_d_not_equal'
137 | it 'passes for different value'
138 | ld d,0
139 | assert_d_not_equal 5
140 |
141 | describe 'assert_e_not_equal'
142 | it 'passes for different value'
143 | ld e,0
144 | assert_e_not_equal 5
145 |
146 | describe 'assert_h_not_equal'
147 | it 'passes for different value'
148 | ld h,0
149 | assert_h_not_equal 5
150 |
151 | describe 'assert_l_not_equal'
152 | it 'passes for different value'
153 | ld l,0
154 | assert_l_not_equal 5
155 |
156 | describe 'assert_hl_not_equal'
157 | it 'passes for different value'
158 | ld hl,$0002
159 | assert_hl_not_equal $0103
160 |
161 | describe 'assert_bc_not_equal'
162 | it 'passes for different value'
163 | ld bc,$0002
164 | assert_bc_not_equal $0103
165 |
166 | describe 'assert_de_not_equal'
167 | it 'passes for different value'
168 | ld de,$0002
169 | assert_de_not_equal $0103
170 |
171 | describe 'assert_ix_not_equal'
172 | it 'passes for different value'
173 | ld ix,$0002
174 | assert_ix_not_equal $0103
175 |
176 | describe 'assert_a_is_zero'
177 | it 'passes for zero'
178 | ld a,0
179 | assert_a_is_zero
180 |
181 | describe 'assert_a_is_not_zero'
182 | it 'passes for non-zero'
183 | ld a,5
184 | assert_a_is_not_zero
185 |
186 | describe 'assert_byte_equal'
187 | it 'passes for same value'
188 | assert_byte_equal tmp_1, $CC
189 |
190 | describe 'assert_byte_not_equal'
191 | it 'passes for different value'
192 | assert_byte_not_equal tmp_1, $DD
193 |
194 | describe 'assert_word_equal'
195 | it 'passes for same value'
196 | assert_word_equal tmp_2, $DDEE
197 |
198 | describe 'assert_word_not_equal'
199 | it 'passes for different value'
200 | assert_word_not_equal tmp_2, $FFAA
201 |
202 | describe 'assert_str_equal'
203 | it 'passes for same value'
204 | assert_str_equal tmp_str, 'test string'
205 |
206 | it 'supports spectrum terminated string'
207 | assert_str_equal copyright_rom_addr, "\x7f 1982 Sinclair Research Ltd"
208 |
209 | describe 'assert_str_not_equal'
210 | it 'passes for different value'
211 | assert_str_not_equal tmp_str, 'diff string'
212 |
213 | describe 'assert_bytes_equal'
214 | it 'passes for same value'
215 | assert_bytes_equal bytes_1, 8, bytes_2
216 |
217 | describe 'assert_bytes_not_equal'
218 | it 'passes for same value'
219 | assert_bytes_not_equal bytes_1, 8, bytes_3
220 |
221 | describe 'assert_z_set'
222 | it "passes when zero flag set"
223 | ld a,0
224 | or a
225 | assert_z_set
226 |
227 | describe 'assert_z_reset'
228 | it "passes when zero flag reset"
229 | ld a,5
230 | or 0
231 | assert_z_reset
232 |
233 | describe 'assert_carry_set'
234 | it "passes when carry flag set"
235 | ld a,$FF
236 | add a,1
237 | assert_carry_set
238 |
239 | describe 'assert_carry_reset'
240 | it "passes when carry flag reset"
241 | ld a,$00
242 | add a,1
243 | assert_carry_reset
244 |
245 | describe 'assert_s_set'
246 | it "passes when signed flag set"
247 | ld a,$00
248 | dec a
249 | assert_s_set
250 |
251 | describe 'assert_s_reset'
252 | it "passes when signed flag reset"
253 | ld a,$00
254 | inc a
255 | assert_s_reset
256 |
257 | describe 'assert_p_v_set'
258 | it "passes when overflow flag set"
259 | ld a,$7F
260 | inc a
261 | assert_p_v_set
262 |
263 | describe 'assert_p_v_reset'
264 | it "passes when overflow flag reset"
265 | ld a,$80
266 | inc a
267 | assert_p_v_reset
268 |
269 | spec_end
270 | ret
271 |
272 |
273 | bytes_1 db $12,$34,$56,$78,$9A,$BC,$DE,$F0
274 | bytes_2 db $12,$34,$56,$78,$9A,$BC,$DE,$F0
275 | bytes_3 db $12,$34,$55,$66,$AA,$BB,$DE,$F0
276 | tmp_1 db $CC
277 | tmp_2 dw $DDEE
278 | tmp_str db 'test string'
279 | tmp_str_end equ $
280 | copyright_rom_addr equ 1539h
281 | end 8000h
282 |
--------------------------------------------------------------------------------
/test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import os
3 | import subprocess
4 | import signal
5 | import time
6 | import glob
7 | import unittest
8 |
9 | ZX_SPEC_OUTPUT_FILE = "printout.txt"
10 | ZX_SPEC_TEST_END_MARKER = "-- ZX SPEC TEST END --"
11 |
12 |
13 | class TestPasses(unittest.TestCase):
14 | @classmethod
15 | def setUpClass(self):
16 | clean()
17 | self.output = run_zx_spec("bin/test-passes.tap")
18 | self.num_tests = 64
19 |
20 | def test_zx_spec_header_displayed(self):
21 | self.assertRegex(self.output, "ZX Spec v")
22 |
23 | def test_marks_show_tests_passed(self):
24 | self.assertRegex(self.output.replace("\n", ""), "\." * self.num_tests)
25 |
26 | def test_all_tests_pass(self):
27 | self.assertRegex(
28 | self.output, "Pass: {0}, Fail: 0, Total: {0}".format(self.num_tests)
29 | )
30 |
31 | def test_framework_exited_correctly(self):
32 | self.assertRegex(self.output, ZX_SPEC_TEST_END_MARKER)
33 |
34 | @classmethod
35 | def tearDownClass(self):
36 | clean()
37 |
38 |
39 | class TestFailures(unittest.TestCase):
40 | @classmethod
41 | def setUpClass(self):
42 | clean()
43 | self.num_tests = 48
44 | self.output = run_zx_spec("bin/test-failures.tap")
45 |
46 | def test_zx_spec_header_displayed(self):
47 | self.assertRegex(self.output, "ZX Spec v")
48 |
49 | def test_shows_failed_tests(self):
50 | self.assertRegex(self.output, "assert_fail")
51 | self.assertRegex(
52 | self.output,
53 | "assert_a_equal\n fails for different value\n\nExpected: 250, Actual: 5",
54 | )
55 | self.assertRegex(
56 | self.output,
57 | "assert_b_equal\n fails for different value\n\nExpected: 250, Actual: 5",
58 | )
59 | self.assertRegex(
60 | self.output,
61 | "assert_c_equal\n fails for different value\n\nExpected: 250, Actual: 5",
62 | )
63 | self.assertRegex(
64 | self.output,
65 | "assert_d_equal\n fails for different value\n\nExpected: 250, Actual: 5",
66 | )
67 | self.assertRegex(
68 | self.output,
69 | "assert_e_equal\n fails for different value\n\nExpected: 250, Actual: 5",
70 | )
71 | self.assertRegex(
72 | self.output,
73 | "assert_h_equal\n fails for different value\n\nExpected: 250, Actual: 5",
74 | )
75 | self.assertRegex(
76 | self.output,
77 | "assert_l_equal\n fails for different value\n\nExpected: 250, Actual: 5",
78 | )
79 | self.assertRegex(
80 | self.output,
81 | "assert_hl_equal\n fails for different value\n\nExpected: 502, Actual: 500",
82 | )
83 | self.assertRegex(
84 | self.output,
85 | "assert_bc_equal\n fails for different value\n\nExpected: 502, Actual: 500",
86 | )
87 | self.assertRegex(
88 | self.output,
89 | "assert_de_equal\n fails for different value\n\nExpected: 502, Actual: 500",
90 | )
91 | self.assertRegex(self.output, "assert_ix_equal\n fails for different value")
92 | self.assertRegex(
93 | self.output,
94 | "assert_byte_equal\n fails for different value\n\nExpected: 255, Actual: 204",
95 | )
96 | self.assertRegex(
97 | self.output,
98 | "assert_word_equal\n fails for different value\n\nExpected: 258, Actual: 65501",
99 | )
100 | self.assertRegex(
101 | self.output,
102 | 'assert_str_equal\n fails for different value\n\nExpected: "diff test string", Ac\ntual: "test string\?\?\?\?\?"',
103 | )
104 | self.assertRegex(
105 | self.output,
106 | "assert_bytes_equal\n fails for different value\n\nExpected: 12,34,55,66,AA,BB,DE,F\n0, Actual: 12,34,56,78,9A,BC,DE,\nF0",
107 | )
108 | self.assertRegex(self.output, "assert_bytes_not_equal\n fails for same value")
109 | self.assertRegex(self.output, "assert_z_set\n fails when zero flag reset")
110 | self.assertRegex(self.output, "assert_z_reset\n fails when zero flag set")
111 | self.assertRegex(self.output, "assert_carry_set\n fails when carry flag reset")
112 | self.assertRegex(self.output, "assert_carry_reset\n fails when carry flag set")
113 | self.assertRegex(self.output, "assert_s_set\n fails when signed flag reset")
114 | self.assertRegex(self.output, "assert_s_reset\n fails when signed flag set")
115 | self.assertRegex(self.output, "assert_p_v_set\n fails when overflow flag reset")
116 | self.assertRegex(self.output, "assert_p_v_reset\n fails when overflow flag set")
117 | self.assertRegex(
118 | self.output, "x\n fails for different value\n\nExpected: 503, Actual: 500"
119 | )
120 | self.assertRegex(
121 | self.output, "x\n fails for different value\n\nExpected: 2, Actual: 1"
122 | )
123 | self.assertRegex(
124 | self.output, "x\n fails for different value\n\nExpected: 3, Actual: 1"
125 | )
126 | self.assertRegex(self.output, "assert_a_not_equal")
127 | self.assertRegex(self.output, "assert_b_not_equal")
128 | self.assertRegex(self.output, "assert_c_not_equal")
129 | self.assertRegex(self.output, "assert_d_not_equal")
130 | self.assertRegex(self.output, "assert_e_not_equal")
131 | self.assertRegex(self.output, "assert_h_not_equal")
132 | self.assertRegex(self.output, "assert_l_not_equal")
133 | self.assertRegex(self.output, "assert_hl_not_equal")
134 | self.assertRegex(self.output, "assert_bc_not_equal")
135 | self.assertRegex(self.output, "assert_de_not_equal")
136 | self.assertRegex(self.output, "assert_ix_not_equal")
137 | self.assertRegex(self.output, "assert_a_is_zero")
138 | self.assertRegex(self.output, "assert_a_is_not_zero")
139 | self.assertRegex(self.output, "assert_byte_not_equal")
140 | self.assertRegex(self.output, "assert_word_not_equal")
141 | self.assertRegex(self.output, "assert_str_not_equal")
142 |
143 | def test_16_bit_numbers_displayed_correctly(self):
144 | self.assertRegex(
145 | self.output,
146 | "assert_word_equal\n fails for different value\n\nExpected: 258, Actual: 65501",
147 | )
148 |
149 | def test_all_tests_failed(self):
150 | self.assertRegex(
151 | self.output, "Pass: 0, Fail: {0}, Total: {0}".format(self.num_tests)
152 | )
153 |
154 | def test_framework_exited_correctly(self):
155 | self.assertRegex(self.output, ZX_SPEC_TEST_END_MARKER)
156 |
157 | @classmethod
158 | def tearDownClass(self):
159 | clean()
160 |
161 |
162 | class TestHexDisplay(unittest.TestCase):
163 | @classmethod
164 | def setUpClass(self):
165 | clean()
166 | self.output = run_zx_spec("bin/test-hex.tap")
167 |
168 | def test_hex_values_are_displayed_correctly(self):
169 | self.assertRegex(
170 | self.output,
171 | "assert_word_equal\n fails for different value\n\nExpected: 0000, Actual: FFFF",
172 | )
173 | self.assertRegex(
174 | self.output, "fails for different value\n\nExpected: ACDC, Actual: FFFF"
175 | )
176 | self.assertRegex(
177 | self.output,
178 | "assert_byte_equal\n fails for different value\n\nExpected: 3B, Actual: 2A",
179 | )
180 |
181 | def test_framework_exited_correctly(self):
182 | self.assertRegex(self.output, ZX_SPEC_TEST_END_MARKER)
183 |
184 | @classmethod
185 | def tearDownClass(self):
186 | clean()
187 |
188 |
189 | class TestVerbose(unittest.TestCase):
190 | @classmethod
191 | def setUpClass(self):
192 | clean()
193 | self.output = run_zx_spec("bin/test-verbose-mix.tap")
194 |
195 | def test_descriptions_displayed_correctly(self):
196 | self.assertRegex(self.output, "assert_pass\n passes test")
197 | self.assertRegex(self.output, "assert_a_equal\n passes for same value")
198 |
199 | def test_framework_exited_correctly(self):
200 | self.assertRegex(self.output, ZX_SPEC_TEST_END_MARKER)
201 |
202 | @classmethod
203 | def tearDownClass(self):
204 | clean()
205 |
206 |
207 | def clean():
208 | for f in glob.glob("printout.*"):
209 | os.remove(f)
210 |
211 |
212 | def printout_txt(filename):
213 | with open(filename, "r") as f:
214 | return f.read()
215 |
216 |
217 | def wait_for_printout(filename):
218 | wait_count = 0
219 | while not os.path.exists(filename):
220 | time.sleep(0.2)
221 | wait_count += 1
222 | if wait_count == 120 * 5:
223 | raise IOError("Output file not produced in time")
224 |
225 |
226 | def wait_for_framework_completion(filename):
227 | wait_count = 0
228 | cursor = 0
229 | while 1:
230 | contents = printout_txt(filename)
231 | if ZX_SPEC_TEST_END_MARKER in contents:
232 | break
233 | print(contents[cursor:], end="")
234 | cursor = len(contents)
235 | time.sleep(0.2)
236 | wait_count += 1
237 | if wait_count == 120 * 5:
238 | raise Exception("Framework did not indicate clean exit in time")
239 |
240 |
241 | def run_zx_spec(tape):
242 | cmd_line = "{0} --no-sound --zxprinter --printer --tape {1} --auto-load --no-autosave-settings".format(
243 | os.getenv("FUSE", "fuse"), tape
244 | )
245 | proc = subprocess.Popen(cmd_line, shell=True, preexec_fn=os.setsid)
246 | wait_for_printout(ZX_SPEC_OUTPUT_FILE)
247 | wait_for_framework_completion(ZX_SPEC_OUTPUT_FILE)
248 | os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
249 | return printout_txt(ZX_SPEC_OUTPUT_FILE)
250 |
251 |
252 | if __name__ == "__main__":
253 | unittest.main(verbosity=2)
254 |
--------------------------------------------------------------------------------
/src/includes/asserts.asm:
--------------------------------------------------------------------------------
1 | ;-------------------------
2 | ; Public
3 | ;-------------------------
4 |
5 | assert_pass macro
6 | call _assert_pass_r
7 | endm
8 |
9 | assert_fail macro
10 | call _assert_fail_r
11 | endm
12 |
13 | assert_a_equal macro val
14 | push bc
15 | ld c,val ; Store expected in C
16 | call _assert_a_equal_r
17 | pop bc
18 | endm
19 |
20 | assert_b_equal macro val
21 | _assert_reg_equal val, b
22 | endm
23 |
24 | assert_c_equal macro val
25 | _assert_reg_equal val, c
26 | endm
27 |
28 | assert_d_equal macro val
29 | _assert_reg_equal val, d
30 | endm
31 |
32 | assert_e_equal macro val
33 | _assert_reg_equal val, e
34 | endm
35 |
36 | assert_h_equal macro val
37 | push hl
38 | _assert_reg_equal val, h
39 | pop hl
40 | endm
41 |
42 | assert_l_equal macro val
43 | push hl
44 | _assert_reg_equal val, l
45 | pop hl
46 | endm
47 |
48 | assert_a_not_equal macro val
49 | push bc
50 | ld c,val
51 | call _assert_a_not_equal_r
52 | pop bc
53 | endm
54 |
55 | assert_b_not_equal macro val
56 | _assert_reg_not_equal val, b
57 | endm
58 |
59 | assert_c_not_equal macro val
60 | _assert_reg_not_equal val, c
61 | endm
62 |
63 | assert_d_not_equal macro val
64 | _assert_reg_not_equal val, d
65 | endm
66 |
67 | assert_e_not_equal macro val
68 | _assert_reg_not_equal val, e
69 | endm
70 |
71 | assert_h_not_equal macro val
72 | _assert_reg_not_equal val, h
73 | endm
74 |
75 | assert_l_not_equal macro val
76 | _assert_reg_not_equal val, l
77 | endm
78 |
79 | assert_a_is_zero macro
80 | assert_a_equal 0
81 | endm
82 |
83 | assert_a_is_not_zero macro
84 | assert_a_not_equal 0
85 | endm
86 |
87 | assert_hl_equal macro val
88 | push bc
89 | ld bc,val
90 | call _assert_hl_equal_r
91 | pop bc
92 | endm
93 |
94 | assert_bc_equal macro val
95 | push hl
96 | ld h,b
97 | ld l,c
98 | assert_hl_equal val
99 | pop hl
100 | endm
101 |
102 | assert_de_equal macro val
103 | push hl
104 | ld h,d
105 | ld l,e
106 | assert_hl_equal val
107 | pop hl
108 | endm
109 |
110 | assert_ix_equal macro val
111 | push de
112 | ld d,ixh
113 | ld e,ixl
114 | assert_de_equal val
115 | pop de
116 | endm
117 |
118 | assert_hl_not_equal macro val
119 | push bc
120 | ld bc,val
121 | call _assert_hl_not_equal_r
122 | pop bc
123 | endm
124 |
125 | assert_bc_not_equal macro val
126 | push hl
127 | ld h,b
128 | ld l,c
129 | assert_hl_not_equal val
130 | pop hl
131 | endm
132 |
133 | assert_de_not_equal macro val
134 | push hl
135 | ld h,d
136 | ld l,e
137 | assert_hl_not_equal val
138 | pop hl
139 | endm
140 |
141 | assert_ix_not_equal macro val
142 | push de
143 | ld d,ixh
144 | ld e,ixl
145 | assert_de_not_equal val
146 | pop de
147 | endm
148 |
149 | assert_z_set macro val
150 | _assert_cond val, z
151 | endm
152 |
153 | assert_z_reset macro val
154 | _assert_cond val, nz
155 | endm
156 |
157 | assert_carry_set macro val
158 | _assert_cond val, c
159 | endm
160 |
161 | assert_carry_reset macro val
162 | _assert_cond val, nc
163 | endm
164 |
165 | assert_s_set macro val
166 | _assert_cond val, m
167 | endm
168 |
169 | assert_s_reset macro val
170 | _assert_cond val, p
171 | endm
172 |
173 | assert_p_v_set macro val
174 | _assert_cond val, pe
175 | endm
176 |
177 | assert_p_v_reset macro val
178 | _assert_cond val, po
179 | endm
180 |
181 | assert_byte_equal macro mem_addr, val
182 | push af
183 | ld a,(mem_addr)
184 | assert_a_equal val
185 | pop af
186 | endm
187 |
188 | assert_byte_not_equal macro mem_addr, val
189 | push af
190 | ld a,(mem_addr)
191 | assert_a_not_equal val
192 | pop af
193 | endm
194 |
195 | assert_word_equal macro mem_addr, val
196 | push hl
197 | ld hl,(mem_addr)
198 | assert_hl_equal val
199 | pop hl
200 | endm
201 |
202 | assert_word_not_equal macro mem_addr, val
203 | push hl
204 | ld hl,(mem_addr)
205 | assert_hl_not_equal val
206 | pop hl
207 | endm
208 |
209 | assert_bytes_equal macro bytes_1_start, bytes_1_length, bytes_2_start
210 | local fail, done
211 | ld b,bytes_1_length ; B = bytes length
212 | ld hl,bytes_1_start ; HL = Actual start
213 | ld de,bytes_2_start ; DE = Expected start
214 | call _comp_bytes ; Compare bytes
215 | jr nz,fail ; If Z not set, bytes not equal, fail test
216 | assert_pass
217 | jr done
218 | fail assert_fail
219 | _fail_ink
220 | _print_text _zxspec_text_expected, _zxspec_text_expected_end
221 | _print_bytes bytes_2_start, bytes_1_length
222 | _print_text _zxspec_text_actual, _zxspec_text_actual_end
223 | _print_bytes bytes_1_start, bytes_1_length
224 | _print_newline
225 | _print_newline
226 | _normal_ink
227 | done equ $
228 | endm
229 |
230 | assert_bytes_not_equal macro bytes_1_start, bytes_1_length, bytes_2_start
231 | local pass, done
232 | ld b,bytes_1_length ; B = bytes length
233 | ld hl,bytes_1_start ; HL = Actual start
234 | ld de,bytes_2_start ; DE = Expected start
235 | call _comp_bytes ; Compare bytes
236 | jr nz,pass ; If Z not set, bytes not equal, pass test
237 | assert_fail
238 | jr done
239 | pass assert_pass
240 | done equ $
241 | endm
242 |
243 | assert_str_equal macro str_addr, val
244 | local val_start, val_end, fail, done
245 | jr val_end
246 | val_start db val
247 | val_end equ $
248 | ld b,val_end-val_start ; B = string length
249 | ld hl,str_addr ; HL = Actual start
250 | ld de,val_start ; DE = Expected start
251 | call _comp_str ; Compare string
252 | jr nz,fail ; If Z not set, string not equal, fail test
253 | assert_pass
254 | jr done
255 | fail assert_fail
256 | _fail_ink
257 | _print_text _zxspec_text_expected, _zxspec_text_expected_end
258 | _print_char _d_quote
259 | _print_text val_start, val_end
260 | _print_char _d_quote
261 | _print_text _zxspec_text_actual, _zxspec_text_actual_end
262 | _print_char _d_quote
263 | _print_text_with_len str_addr, val_end-val_start
264 | _print_char _d_quote
265 | _print_newline
266 | _print_newline
267 | _normal_ink
268 | done equ $
269 | endm
270 |
271 | assert_str_not_equal macro str_addr, val
272 | local val_start, val_end, done, loop, pass
273 | jr val_end
274 | val_start db val
275 | val_end ld b,val_end-val_start ; B = string length
276 | ld hl,str_addr ; HL = Actual start
277 | ld de,val_start ; DE = Expected start
278 | call _comp_str ; Compare string
279 | jr nz,pass ; If Z not set, string not equal, pass test
280 | assert_fail
281 | jr done
282 | pass assert_pass
283 | done equ $
284 | endm
285 |
286 | ;-------------------------
287 | ; Private
288 | ;-------------------------
289 |
290 | _inc_done macro num_done, done_txt_start, done_txt_end
291 | push hl
292 | ld hl,num_done
293 | inc (hl) ; Increment number done
294 | _print_text done_txt_start, done_txt_end ; Print 'done' indicator
295 | pop hl
296 | endm
297 |
298 | _assert_pass_r proc
299 | local print_group_end
300 | _inc_done _zxspec_num_pass, _zxspec_text_pass_mark, _zxspec_text_pass_mark_end ; Increment number passed
301 | if defined zxspec_config_verbose_output
302 | _pass_ink
303 | call _print_description
304 | _normal_ink
305 | endif
306 | ret
307 | endp
308 |
309 | _assert_fail_r proc
310 | local print_group_end
311 | _paint_border _zxspec_red_border
312 | _inc_done _zxspec_num_fail, _zxspec_text_fail_mark, _zxspec_text_fail_mark_end ; Increment number failed
313 | _fail_ink
314 | call _print_description
315 | _normal_ink
316 | ret
317 | endp
318 |
319 | _print_description proc ; Prints test description
320 | local print_group_end
321 | ld hl,_zxspec_shown_names
322 | bit 0,(hl) ; Group name shown already?
323 | jp nz,print_group_end ; Skip if so.
324 | push de
325 | ld de,(_zxspec_group_name_length) ; get string length
326 | inc e ; +1 -1 = 0
327 | dec e ; sets Z if group name undefined
328 | pop de
329 | jp z,print_group_end ; skip printing of name if so
330 | _print_newline
331 | _print_text_with_len (_zxspec_group_name), (_zxspec_group_name_length)
332 | ld hl,_zxspec_shown_names
333 | set 0,(hl) ; Set shown group name
334 | print_group_end _print_newline
335 | _print_char _space ; indent test name
336 | _print_text_with_len (_zxspec_test_name), (_zxspec_test_name_length)
337 | _print_newline
338 | _print_newline
339 | ret
340 | endp
341 |
342 | _assert_cond macro val, cond
343 | local passes, done
344 | jp cond,passes ; pass if flag set
345 | assert_fail ; otherwise, fail
346 | jr done
347 | passes assert_pass
348 | done equ $
349 | endm
350 |
351 | _assert_a_equal_r proc ; C = expected, A = actual
352 | local passes, done
353 | cp c ; does A = expected?
354 | jp z,passes ; pass if so
355 | assert_fail ; otherwise, fail
356 | _fail_ink
357 | _print_text _zxspec_text_expected, _zxspec_text_expected_end
358 | call _print_num_in_c
359 | _print_text _zxspec_text_actual, _zxspec_text_actual_end
360 | ld c,a
361 | call _print_num_in_c
362 | _print_newline
363 | _print_newline
364 | _normal_ink
365 | jr done
366 | passes assert_pass
367 | done ret
368 | endp
369 |
370 | _assert_a_not_equal_r proc ; C = not expected, A = actual
371 | local passes, done
372 | cp c ; does A = not expected?
373 | jr nz,passes ; pass if it doesn't
374 | assert_fail ; otherwise, fail
375 | jr done
376 | passes assert_pass
377 | done ret
378 | endp
379 |
380 | _assert_hl_equal_r proc ; HL = actual, BC = expected
381 | local passes, done
382 | push hl
383 | push hl
384 | sbc hl,bc ; Subtract val from HL
385 | pop hl
386 | jp z,passes ; pass if same
387 | push hl
388 | assert_fail ; otherwise, fail
389 | _fail_ink
390 | _print_text _zxspec_text_expected, _zxspec_text_expected_end
391 | call _print_num_in_bc
392 | _print_text _zxspec_text_actual, _zxspec_text_actual_end
393 | pop bc ; pop HL into BC
394 | call _print_num_in_bc
395 | _print_newline
396 | _print_newline
397 | _normal_ink
398 | jp done
399 | passes assert_pass
400 | done pop hl
401 | ret
402 | endp
403 |
404 | _assert_hl_not_equal_r proc ; HL = actual, BC = unexpected
405 | local passes, done
406 | push hl
407 | sbc hl,bc ; Subtract val from HL
408 | jp nz,passes ; pass if different
409 | assert_fail ; otherwise, fail
410 | jr done
411 | passes assert_pass
412 | done pop hl
413 | ret
414 | endp
415 |
416 | _comp_str proc ; Compares two strings
417 | ; Inputs: B = string length, HL = actual start, DE = expected start
418 | ; Outputs: Z flag: 1 = string equal, 0 = not equal
419 | local loop, done
420 | loop ld c,(hl) ; C = Actual char
421 | res 7,c ; Remove any string termination bit
422 | ld a,(de) ; A = Expected char
423 | cp c ; Compare actual with expected char
424 | jp nz,done ; Not equal.
425 | inc hl ; Next actual char
426 | inc de ; Next expected char
427 | djnz loop ; Decrement string length, loop if <> 0
428 | done ret
429 | endp
430 |
431 | _comp_bytes proc ; Compares two sequence of bytes
432 | ; Inputs: B = length, HL = actual start, DE = expected start
433 | ; Outputs: Z flag: 1 = string equal, 0 = not equal
434 | local loop, done
435 | loop ld c,(hl) ; C = Actual byte
436 | ld a,(de) ; A = Expected byte
437 | cp c ; Compare actual with expected byte
438 | jp nz,done ; Not equal.
439 | inc hl ; Next actual byte
440 | inc de ; Next expected byte
441 | djnz loop ; Decrement length, loop if <> 0
442 | done ret
443 | endp
444 |
445 | _assert_reg_equal macro val, reg
446 | push af ; Backup A
447 | ld a,reg ; Copy register into A
448 | assert_a_equal val
449 | pop af ; Restore A
450 | endm
451 |
452 | _assert_reg_not_equal macro val, reg
453 | push af ; Backup A
454 | ld a,reg ; Copy register into A
455 | assert_a_not_equal val
456 | pop af ; Restore A
457 | endm
--------------------------------------------------------------------------------