├── .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 [![Build & Test](https://github.com/rhargreaves/zx-spec/actions/workflows/build.yml/badge.svg)](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 --------------------------------------------------------------------------------