	title	'Z80 instruction set exerciser for Spectrum'

; zexlax.z80 - Z80 instruction set exerciser
; Copyright (C) 1994  Frank D. Cringle
;
; 03-Nov-2002: Modified to assemble with ZMAC and MAXAM
; Copyright (C) 2002 J.G.Harston
; + labels on equates mustn't have trailing colon
; + macros don't understand <...> sequence, so parameters are passed
;   explicitly
; + ds n,c not supported, so strings are set to full explicity length
; + nonstandard 'cp a,' and 'and a,' changed to 'cp ' and 'and '
;
; 03-Nov-2002: Modified to run on Spectrum
; Copyright (C) 2002 J.G.Harston
;
; This program is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License
; as published by the Free Software Foundation; either version 2
; of the License, or (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

	aseg
	org	8000h

	jp	start

; machine state before test (needs to be at predictably constant address)
msbt:	ds	14
spbt:	ds	2
msbthi	equ	msbt / 0100h
msbtlo	equ	msbt & 0ffh


; For the purposes of this test program, the machine state consists of:
;	a 2 byte memory operand, followed by
;	the registers iy,ix,hl,de,bc,af,sp
; for a total of 16 bytes.

; The program tests instructions (or groups of similar instructions)
; by cycling through a sequence of machine states, executing the test
; instruction for each one and running a 32-bit crc over the resulting
; machine states.  At the end of the sequence the crc is compared to
; an expected value that was found empirically on a real Z80.

; A test case is defined by a descriptor which consists of:
;	a flag mask byte,
;	the base case,
;	the incement vector,
;	the shift vector,
;	the expected crc,
;	a short descriptive message.
;
; The flag mask byte is used to prevent undefined flag bits from
; influencing the results.  Documented flags are as per Mostek Z80
; Technical Manual.
;
; The next three parts of the descriptor are 20 byte vectors
; corresponding to a 4 byte instruction and a 16 byte machine state.
; The first part is the base case, which is the first test case of
; the sequence.  This base is then modified according to the next 2
; vectors.  Each 1 bit in the increment vector specifies a bit to be
; cycled in the form of a binary counter.  For instance, if the byte
; corresponding to the accumulator is set to 0ffh in the increment
; vector, the test will be repeated for all 256 values of the
; accumulator.  Note that 1 bits don't have to be contiguous.  The
; number of test cases 'caused' by the increment vector is equal to
; 2^(number of 1 bits).  The shift vector is similar, but specifies a
; set of bits in the test case that are to be successively inverted.
; Thus the shift vector 'causes' a number of test cases equal to the
; number of 1 bits in it.

; The total number of test cases is the product of those caused by the
; counter and shift vectors and can easily become unweildy.  Each
; individual test case can take a few milliseconds to execute, due to
; the overhead of test setup and crc calculation, so test design is a
; compromise between coverage and execution time.

; This program is designed to detect differences between
; implementations and is not ideal for diagnosing the causes of any
; discrepancies.  However, provided a reference implementation (or
; real system) is available, a failing test case can be isolated by
; hand using a binary search of the test space.


start:	; ld	hl,(6)
	ld	hl,08000h	; put stack at top of 32k
	ld	sp,hl
	ld	de,msg1
	ld	c,9
	call	bdos

	ld	hl,tests	; first test case
loop:	ld	a,(hl)		; end of list ?
	inc	hl
	or	(hl)
	jp	z,done
	dec	hl
	call	stt
	jp	loop
	
done:	ld	de,msg2
	ld	c,9
	call	bdos
stop:
	jp	stop		; wait for reset

tests:
	dw	adc16
	dw	add16
	dw	add16x
	dw	add16y
	dw	alu8i
	dw	alu8r
	dw	alu8rx
	dw	alu8x
	dw	bitx
	dw	bitz80
	dw	cpd1
	dw	cpi1
	dw	daaop	; can't use opcode as label
	dw	inca
	dw	incb
	dw	incbc
	dw	incc
	dw	incd
	dw	incde
	dw	ince
	dw	inch
	dw	inchl
	dw	incix
	dw	inciy
	dw	incl
	dw	incm
	dw	incsp
	dw	incx
	dw	incxh
	dw	incxl
	dw	incyh
	dw	incyl
	dw	ld161
	dw	ld162
	dw	ld163
	dw	ld164
	dw	ld165
	dw	ld166
	dw	ld167
	dw	ld168
	dw	ld16im
	dw	ld16ix
	dw	ld8bd
	dw	ld8im
	dw	ld8imx
	dw	ld8ix1
	dw	ld8ix2
	dw	ld8ix3
	dw	ld8ixy
	dw	ld8rr
	dw	ld8rrx
	dw	lda
	dw	ldd1
	dw	ldd2
	dw	ldi1
	dw	ldi2
	dw	negop	; jgh: can't use opcode as label
	dw	rldop	; jgh: can't use opcode as label
	dw	rot8080
	dw	rotxy
	dw	rotz80
	dw	srz80
	dw	srzx
	dw	st8ix1
	dw	st8ix2
	dw	st8ix3
	dw	stabd
	dw	0

; jgh: macro syntax changed for ZMAC and MAXAM
;	can't use opcodes as labels
;	ZMAC allows &nn as hex, so & removed from local labels
;
tstr	macro	insn1,insn2,insn3,insn4,memop,riy,rix,rhl,rde,rbc,flags,acc,rsp,?lab
?lab:	db	insn1,insn2,insn3,insn4
	dw	memop,riy,rix,rhl,rde,rbc
	db	flags
	db	acc
	dw	rsp
	if	$-?lab ne 20
	error	'missing parameter'
	endif
	endm

tmsg	macro	msg,?lab
?lab:	db	'msg'
	if	$ ge ?lab+31
	error	'message too long'
	else
;	ds	?lab+30-$,'.'	; jgh: ZMAC/MAXAM don't have char parameter
	endif
	db	'$'
	endm

; jgh: ZMAC/MAXAM don't recognise <n,m> syntax for macros, so full parameters given
; jgh: each tmsg has full string, as ZMAC/MAXAM don't have ds n,c pseudo-op

; <adc,sbc> hl,<bc,de,hl,sp> (38,912 cycles)
adc16:	db	0ffh		; flag mask
	tstr	0edh,042h,0,0,0832ch,04f88h,0f22bh,0b339h,07e1fh,01563h,0d3h,089h,0465eh
	tstr	0,038h,0,0,0,0,0,0f821h,0,0,0,0,0	; (1024 cycles)
	tstr	0,0,0,0,0,0,0,-1,-1,-1,0d7h,0,-1	; (38 cycles)
	db	0d4h,08ah,0d5h,019h			; expected crc
	tmsg	'<adc,sbc> hl,<bc,de,hl,sp>...'

; add hl,<bc,de,hl,sp> (19,456 cycles)
add16:	db	0ffh		; flag mask
	tstr	9,0,0,0,0c4a5h,0c4c7h,0d226h,0a050h,058eah,08566h,0c6h,0deh,09bc9h
	tstr	030h,0,0,0,0,0,0,0f821h,0,0,0,0,0	; (512 cycles)
	tstr	0,0,0,0,0,0,0,-1,-1,-1,0d7h,0,-1	; (38 cycles)
	db	0d9h,0a4h,0cah,005h			; expected crc
	tmsg	'add hl,<bc,de,hl,sp>.........'

; add ix,<bc,de,ix,sp> (19,456 cycles)
add16x:	db	0ffh		; flag mask
	tstr	0ddh,9,0,0,0ddach,0c294h,0635bh,033d3h,06a76h,0fa20h,094h,068h,036f5h
	tstr	0,030h,0,0,0,0,0f821h,0,0,0,0,0,0	; (512 cycles)
	tstr	0,0,0,0,0,0,-1,0,-1,-1,0d7h,0,-1	; (38 cycles)
	db	0b1h,0dfh,08eh,0c0h			; expected crc
	tmsg	'add ix,<bc,de,ix,sp>.........'

; add iy,<bc,de,iy,sp> (19,456 cycles)
add16y:	db	0ffh		; flag mask
	tstr	0fdh,9,0,0,0c7c2h,0f407h,051c1h,03e96h,00bf4h,0510fh,092h,01eh,071eah
	tstr	0,030h,0,0,0,0f821h,0,0,0,0,0,0,0	; (512 cycles)
	tstr	0,0,0,0,0,-1,0,0,-1,-1,0d7h,0,-1	; (38 cycles)
	db	039h,0c8h,058h,09bh			; expected crc
	tmsg	'add iy,<bc,de,iy,sp>.........'

; aluop a,nn (28,672 cycles)
alu8i:	db	0ffh		; flag mask
	tstr	0c6h,0,0,0,009140h,07e3ch,07a67h,0df6dh,05b61h,00b29h,010h,066h,085b2h
	tstr	038h,0,0,0,0,0,0,0,0,0,0,-1,0		; (2048 cycles)
	tstr	0,-1,0,0,0,0,0,0,0,0,0d7h,0,0		; (14 cycles)
	db	051h,0c1h,09ch,02eh			; expected crc
	tmsg	'aluop a,nn...................'

; aluop a,<b,c,d,e,h,l,(hl),a> (753,664 cycles)
alu8r:	db	0ffh		; flag mask
	tstr	080h,0,0,0,0c53eh,0573ah,04c4dh,msbt,0e309h,0a666h,0d0h,03bh,0adbbh
	tstr	03fh,0,0,0,0,0,0,0,0,0,0,-1,0		; (16,384 cycles)
	tstr	0,0,0,0,0ffh,0,0,0,-1,-1,0d7h,0,0	; (46 cycles)
	db	006h,0c7h,0aah,08eh			; expected crc
	tmsg	'aluop a,<b,c,d,e,h,l,(hl),a>.'

; aluop a,<ixh,ixl,iyh,iyl> (376,832 cycles)
alu8rx:	db	0ffh		; flag mask
	tstr	0ddh,084h,0,0,0d6f7h,0c76eh,0accfh,02847h,022ddh,0c035h,0c5h,038h,0234bh
	tstr	020h,039h,0,0,0,0,0,0,0,0,0,-1,0	; (8,192 cycles)
	tstr	0,0,0,0,0ffh,0,0,0,-1,-1,0d7h,0,0	; (46 cycles)
	db	0a8h,086h,0cch,044h			; expected crc
	tmsg	'aluop a,<ixh,ixl,iyh,iyl>....'

; aluop a,(<ix,iy>+1) (229,376 cycles)
alu8x:	db	0ffh		; flag mask
	tstr	0ddh,086h,1,0,090b7h,msbt-1,msbt-1,032fdh,0406eh,0c1dch,045h,06eh,0e5fah
	tstr	020h,038h,0,0,0,1,1,0,0,0,0,-1,0	; (16,384 cycles)
	tstr	0,0,0,0,0ffh,0,0,0,0,0,0d7h,0,0		; (14 cycles)
	db	0d3h,0f2h,0d7h,04ah			; expected crc
	tmsg	'aluop a,(<ix,iy>+1)..........'

; bit n,(<ix,iy>+1) (2048 cycles)
bitx:	db	0ffh		; flag mask
	tstr	0ddh,0cbh,1,046h,02075h,msbt-1,msbt-1,03cfch,0a79ah,03d74h,051h,027h,0ca14h
	tstr	020h,0,0,038h,0,0,0,0,0,0,053h,0,0	; (256 cycles)
	tstr	0,0,0,0,0ffh,0,0,0,0,0,0,0,0		; (8 cycles)
	db	083h,053h,04eh,0e1h			; expected crc
	tmsg	'bit n,(<ix,iy>+1)............'

; bit n,<b,c,d,e,h,l,(hl),a> (49,152 cycles)
bitz80:	db	0ffh		; flag mask
	tstr	0cbh,040h,0,0,03ef1h,09dfch,07acch,msbt,0be61h,07a86h,050h,024h,01998h
	tstr	0,03fh,0,0,0,0,0,0,0,0,053h,0,0		; (1024 cycles)
	tstr	0,0,0,0,0ffh,0,0,0,-1,-1,0,-1,0		; (48 cycles)
	db	05eh,002h,00eh,098h			; expected crc
	tmsg	'bit n,<b,c,d,e,h,l,(hl),a>...'

; cpd<r> (1) (6144 cycles)
cpd1:	db	0ffh		; flag mask
	tstr	0edh,0a9h,0,0,0c7b6h,072b4h,018f6h,msbt+17,08dbdh,1,0c0h,030h,094a3h
	tstr	0,010h,0,0,0,0,0,0,0,010,0,-1,0		; (1024 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	013h,04bh,062h,02dh			; expected crc
	tmsg	'cpd<r>.......................'

; cpi<r> (1) (6144 cycles)
cpi1:	db	0ffh		; flag mask
	tstr	0edh,0a1h,0,0,04d48h,0af4ah,0906bh,msbt,04e71h,1,093h,06ah,0907ch
	tstr	0,010h,0,0,0,0,0,0,0,010,0,-1,0		; (1024 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	02dh,0a4h,02dh,019h			; expected crc
	tmsg	'cpi<r>.......................'

; <daa,cpl,scf,ccf>
daaop:	db	0ffh		; flag mask
	tstr	027h,0,0,0,02141h,009fah,01d60h,0a559h,08d5bh,09079h,004h,08eh,0299dh
	tstr	018h,0,0,0,0,0,0,0,0,0,0d7h,-1,0	; (65,536 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0,0,0		; (1 cycle)
	db	06dh,02dh,0d2h,013h			; expected crc
	tmsg	'<daa,cpl,scf,ccf>............'

; <inc,dec> a (3072 cycles)
inca:	db	0ffh		; flag mask
	tstr	03ch,0,0,0,04adfh,0d5d8h,0e598h,08a2bh,0a7b0h,0431bh,044h,05ah,0d030h
	tstr	001h,0,0,0,0,0,0,0,0,0,0,-1,0		; (512 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	081h,0fah,081h,000h			; expected crc
	tmsg	'<inc,dec> a..................'

; <inc,dec> b (3072 cycles)
incb:	db	0ffh		; flag mask
	tstr	004h,0,0,0,0d623h,0432dh,07a61h,08180h,05a86h,01e85h,086h,058h,09bbbh
	tstr	001h,0,0,0,0,0,0,0,0,0ff00h,0,0,0	; (512 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	077h,0f3h,05ah,073h			; expected crc
	tmsg	'<inc,dec> b..................'

; <inc,dec> bc (1536 cycles)
incbc:	db	0ffh		; flag mask
	tstr	003h,0,0,0,0cd97h,044abh,08dc9h,0e3e3h,011cch,0e8a4h,002h,049h,02a4dh
	tstr	008h,0,0,0,0,0,0,0,0,0f821h,0,0,0	; (256 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	0d2h,0aeh,03bh,0ech			; expected crc
	tmsg	'<inc,dec> bc.................'

; <inc,dec> c (3072 cycles)
incc:	db	0ffh		; flag mask
	tstr	00ch,0,0,0,0d789h,00935h,0055bh,09f85h,08b27h,0d208h,095h,005h,00660h
	tstr	001h,0,0,0,0,0,0,0,0,0ffh,0,0,0		; (512 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	01ah,0f6h,012h,0a7h			; expected crc
	tmsg	'<inc,dec> c..................'

; <inc,dec> d (3072 cycles)
incd:	db	0ffh		; flag mask
	tstr	014h,0,0,0,0a0eah,05fbah,065fbh,0981ch,038cch,0debch,043h,05ch,003bdh
	tstr	001h,0,0,0,0,0,0,0,0ff00h,0,0,0,0	; (512 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	0d1h,046h,0bfh,051h			; expected crc
	tmsg	'<inc,dec> d..................'

; <inc,dec> de (1536 cycles)
incde:	db	0ffh		; flag mask
	tstr	013h,0,0,0,0342eh,0131dh,028c9h,00acah,09967h,03a2eh,092h,0f6h,09d54h
	tstr	008h,0,0,0,0,0,0,0,0f821h,0,0,0,0	; (256 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	0aeh,0c6h,0d4h,02ch			; expected crc
	tmsg	'<inc,dec> de.................'

; <inc,dec> e (3072 cycles)
ince:	db	0ffh		; flag mask
	tstr	01ch,0,0,0,0602fh,04c0dh,02402h,0e2f5h,0a0f4h,0a10ah,013h,032h,05925h
	tstr	001h,0,0,0,0,0,0,0,0ffh,0,0,0,0		; (512 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	0cah,08ch,06ah,0c2h			; expected crc
	tmsg	'<inc,dec> e..................'

; <inc,dec> h (3072 cycles)
inch:	db	0ffh		; flag mask
	tstr	024h,0,0,0,01506h,0f2ebh,0e8ddh,0262bh,011a6h,0bc1ah,017h,006h,02818h
	tstr	001h,0,0,0,0,0,0,0ff00h,0,0,0,0,0	; (512 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	056h,00fh,095h,05eh			; expected crc
	tmsg	'<inc,dec> h..................'

; <inc,dec> hl (1536 cycles)
inchl:	db	0ffh		; flag mask
	tstr	023h,0,0,0,0c3f4h,007a5h,01b6dh,04f04h,0e2c2h,0822ah,057h,0e0h,0c3e1h
	tstr	008h,0,0,0,0,0,0,0f821h,0,0,0,0,0	; (256 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	0fch,00dh,06dh,04ah			; expected crc
	tmsg	'<inc,dec> hl.................'

; <inc,dec> ix (1536 cycles)
incix:	db	0ffh		; flag mask
	tstr	0ddh,023h,0,0,0bc3ch,00d9bh,0e081h,0adfdh,09a7fh,096e5h,013h,085h,00be2h
	tstr	0,8,0,0,0,0,0f821h,0,0,0,0,0,0		; (256 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	0a5h,04dh,0beh,031h			; expected crc
	tmsg	'<inc,dec> ix.................'

; <inc,dec> iy (1536 cycles)
inciy:	db	0ffh		; flag mask
	tstr	0fdh,023h,0,0,09402h,0637ah,03182h,0c65ah,0b2e9h,0abb4h,016h,0f2h,06d05h
	tstr	0,8,0,0,0,0f821h,0,0,0,0,0,0,0		; (256 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	050h,05dh,051h,0a3h			; expected crc
	tmsg	'<inc,dec> iy.................'

; <inc,dec> l (3072 cycles)
incl:	db	0ffh		; flag mask
	tstr	02ch,0,0,0,08031h,0a520h,04356h,0b409h,0f4c1h,0dfa2h,0d1h,03ch,03ea2h
	tstr	001h,0,0,0,0,0,0,0ffh,0,0,0,0,0		; (512 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	0a0h,0a1h,0b4h,09fh			; expected crc
	tmsg	'<inc,dec> l..................'

; <inc,dec> (hl) (3072 cycles)
incm:	db	0ffh		; flag mask
	tstr	034h,0,0,0,0b856h,00c7ch,0e53eh,msbt,0877eh,0da58h,015h,05ch,01f37h
	tstr	001h,0,0,0,0ffh,0,0,0,0,0,0,0,0		; (512 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	028h,029h,05eh,0ceh			; expected crc
	tmsg	'<inc,dec> (hl)...............'

; <inc,dec> sp (1536 cycles)
incsp:	db	0ffh		; flag mask
	tstr	033h,0,0,0,0346fh,0d482h,0d169h,0deb6h,0a494h,0f476h,053h,002h,0855bh
	tstr	008h,0,0,0,0,0,0,0,0,0,0,0,0f821h	; (256 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	05dh,0ach,0d5h,027h			; expected crc
	tmsg	'<inc,dec> sp.................'

; <inc,dec> (<ix,iy>+1) (6144 cycles)
incx:	db	0ffh		; flag mask
	tstr	0ddh,034h,1,0,0fa6eh,msbt-1,msbt-1,02c28h,08894h,05057h,016h,033h,0286fh
	tstr	020h,1,0,0,0ffh,0,0,0,0,0,0,0,0		; (1024 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	00bh,095h,0a8h,0eah			; expected crc
	tmsg	'<inc,dec> (<ix,iy>+1)........'

; <inc,dec> ixh (3072 cycles)
incxh:	db	0ffh		; flag mask
	tstr	0ddh,024h,0,0,0b838h,0316ch,0c6d4h,03e01h,08358h,015b4h,081h,0deh,04259h
	tstr	0,1,0,0,0,0ff00h,0,0,0,0,0,0,0		; (512 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	06fh,046h,036h,062h			; expected crc
	tmsg	'<inc,dec> ixh................'

; <inc,dec> ixl (3072 cycles)
incxl:	db	0ffh		; flag mask
	tstr	0ddh,02ch,0,0,04d14h,07460h,076d4h,006e7h,032a2h,0213ch,0d6h,0d7h,099a5h
	tstr	0,1,0,0,0,0ffh,0,0,0,0,0,0,0		; (512 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	002h,07bh,0efh,02ch			; expected crc
	tmsg	'<inc,dec> ixl................'

; <inc,dec> iyh (3072 cycles)
incyh:	db	0ffh		; flag mask
	tstr	0ddh,024h,0,0,02836h,09f6fh,09116h,061b9h,082cbh,0e219h,092h,073h,0a98ch
	tstr	0,1,0,0,0ff00h,0,0,0,0,0,0,0,0		; (512 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	02dh,096h,06ch,0f3h			; expected crc
	tmsg	'<inc,dec> iyh................'

; <inc,dec> iyl (3072 cycles)
incyl:	db	0ffh		; flag mask
	tstr	0ddh,02ch,0,0,0d7c6h,062d5h,0a09eh,07039h,03e7eh,09f12h,090h,0d9h,0220fh
	tstr	0,1,0,0,0ffh,0,0,0,0,0,0,0,0		; (512 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	036h,0c1h,01eh,075h			; expected crc
	tmsg	'<inc,dec> iyl................'

; ld <bc,de>,(nnnn) (32 cycles)
ld161:	db	0ffh		; flag mask
	tstr	0edh,04bh,msbtlo,msbthi,0f9a8h,0f559h,093a4h,0f5edh,06f96h,0d968h,086h,0e6h,04bd8h
	tstr	0,010h,0,0,0,0,0,0,0,0,0,0,0		; (2 cycles)
	tstr	0,0,0,0,-1,0,0,0,0,0,0,0,0		; (16 cycles)
	db	04dh,045h,0a9h,0ach			; expected crc
	tmsg	'ld <bc,de>,(nnnn)............'

; ld hl,(nnnn) (16 cycles)
ld162:	db	0ffh		; flag mask
	tstr	02ah,msbtlo,msbthi,0,09863h,07830h,02077h,0b1feh,0b9fah,0abb8h,004h,006h,06015h
	tstr	0,0,0,0,0,0,0,0,0,0,0,0,0		; (1 cycle)
	tstr	0,0,0,0,-1,0,0,0,0,0,0,0,0		; (16 cycles)
	db	05fh,097h,024h,087h			; expected crc
	tmsg	'ld hl,(nnnn).................'
	
; ld sp,(nnnn) (16 cycles)
ld163:	db	0ffh		; flag mask
	tstr	0edh,07bh,msbtlo,msbthi,08dfch,057d7h,02161h,0ca18h,0c185h,027dah,083h,01eh,0f460h
	tstr	0,0,0,0,0,0,0,0,0,0,0,0,0		; (1 cycles)
	tstr	0,0,0,0,-1,0,0,0,0,0,0,0,0		; (16 cycles)
	db	07ah,0ceh,0a1h,01bh			; expected crc
	tmsg	'ld sp,(nnnn).................'

; ld <ix,iy>,(nnnn) (32 cycles)
ld164:	db	0ffh		; flag mask
	tstr	0ddh,02ah,msbtlo,msbthi,0ded7h,0a6fah,0f780h,0244ch,087deh,0bcc2h,016h,063h,04c96h
	tstr	020h,0,0,0,0,0,0,0,0,0,0,0,0		; (2 cycles)
	tstr	0,0,0,0,-1,0,0,0,0,0,0,0,0		; (16 cycles)
	db	085h,08bh,0f1h,06dh			; expected crc
	tmsg	'ld <ix,iy>,(nnnn)............'
	
; ld (nnnn),<bc,de> (64 cycles)
ld165:	db	0ffh		; flag mask
	tstr	0edh,043h,msbtlo,msbthi,01f98h,0844dh,0e8ach,0c9edh,0c95dh,08f61h,080h,03fh,0c7bfh
	tstr	0,010h,0,0,0,0,0,0,0,0,0,0,0		; (2 cycles)
	tstr	0,0,0,0,0,0,0,0,-1,-1,0,0,0		; (32 cycles)
	db	064h,01eh,087h,015h			; expected crc
	tmsg	'ld (nnnn),<bc,de>............'

; ld (nnnn),hl (16 cycles)
ld166:	db	0ffh		; flag mask
	tstr	022h,msbtlo,msbthi,0,0d003h,07772h,07f53h,03f72h,064eah,0e180h,010h,02dh,035e9h
	tstr	0,0,0,0,0,0,0,0,0,0,0,0,0		; (1 cycle)
	tstr	0,0,0,0,0,0,0,-1,0,0,0,0,0		; (16 cycles)
	db	0a3h,060h,08bh,047h			; expected crc
	tmsg	'ld (nnnn),hl.................'

; ld (nnnn),sp (16 cycles)
ld167:	db	0ffh		; flag mask
	tstr	0edh,073h,msbtlo,msbthi,0c0dch,0d1d6h,0ed5ah,0f356h,0afdah,06ca7h,044h,09fh,03f0ah
	tstr	0,0,0,0,0,0,0,0,0,0,0,0,0		; (1 cycle)
	tstr	0,0,0,0,0,0,0,0,0,0,0,0,-1		; (16 cycles)
	db	016h,058h,05fh,0d7h			; expected crc
	tmsg	'ld (nnnn),sp.................'

; ld (nnnn),<ix,iy> (64 cycles)
ld168:	db	0ffh		; flag mask
	tstr	0ddh,022h,msbtlo,msbthi,06cc3h,00d91h,06900h,08ef8h,0e3d6h,0c3f7h,0c6h,0d9h,0c2dfh
	tstr	020h,0,0,0,0,0,0,0,0,0,0,0,0		; (2 cycles)
	tstr	0,0,0,0,0,-1,-1,0,0,0,0,0,0		; (32 cycles)
	db	0bah,010h,02ah,06bh			; expected crc
	tmsg	'ld (nnnn),<ix,iy>............'

; ld <bc,de,hl,sp>,nnnn (64 cycles)
ld16im:	db	0ffh		; flag mask
	tstr	1,0,0,0,05c1ch,02d46h,08eb9h,06078h,074b1h,0b30eh,046h,0d1h,030cch
	tstr	030h,0,0,0,0,0,0,0,0,0,0,0,0		; (4 cycles)
	tstr	0,0ffh,0ffh,0,0,0,0,0,0,0,0,0,0		; (16 cycles)
	db	0deh,039h,019h,069h			; expected crc
	tmsg	'ld <bc,de,hl,sp>,nnnn........'

; ld <ix,iy>,nnnn (32 cycles)
ld16ix:	db	0ffh		; flag mask
	tstr	0ddh,021h,0,0,087e8h,02006h,0bd12h,0b69bh,07253h,0a1e5h,051h,013h,0f1bdh
	tstr	020h,0,0,0,0,0,0,0,0,0,0,0,0		; (2 cycles)
	tstr	0,0,0ffh,0ffh,0,0,0,0,0,0,0,0,0		; (16 cycles)
	db	022h,07dh,0d5h,025h			; expected crc
	tmsg	'ld <ix,iy>,nnnn..............'

; ld a,<(bc),(de)> (44 cycles)
ld8bd:	db	0ffh		; flag mask
	tstr	00ah,0,0,0,0b3a8h,01d2ah,07f8eh,042ach,msbt,msbt,0c6h,0b1h,0ef8eh
	tstr	010h,0,0,0,0,0,0,0,0,0,0,0,0		; (2 cycles)
	tstr	0,0,0,0,0ffh,0,0,0,0,0,0d7h,-1,0	; (22 cycles)
	db	0b0h,081h,089h,035h			; expected crc
	tmsg	'ld a,<(bc),(de)>.............'

; ld <b,c,d,e,h,l,(hl),a>,nn (64 cycles)
ld8im:	db	0ffh		; flag mask
	tstr	6,0,0,0,0c407h,0f49dh,0d13dh,00339h,0de89h,07455h,053h,0c0h,05509h
	tstr	038h,0,0,0,0,0,0,0,0,0,0,0,0		; (8 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0,-1,0		; (8 cycles)
	db	0f1h,0dah,0b5h,056h			; expected crc
	tmsg	'ld <b,c,d,e,h,l,(hl),a>,nn...'

; ld (<ix,iy>+1),nn (32 cycles)
ld8imx:	db	0ffh		; flag mask
	tstr	0ddh,036h,1,0,01b45h,msbt-1,msbt-1,0d5c1h,061c7h,0bdc4h,0c0h,085h,0cd16h
	tstr	020h,0,0,0,0,0,0,0,0,0,0,0,0		; (2 cycles)
	tstr	0,0,0,-1,0,0,0,0,0,0,0,-1,0		; (16 cycles)
	db	026h,0dbh,047h,07eh			; expected crc
	tmsg	'ld (<ix,iy>+1),nn............'

; ld <b,c,d,e>,(<ix,iy>+1) (512 cycles)
ld8ix1:	db	0ffh		; flag mask
	tstr	0ddh,046h,1,0,0d016h,msbt-1,msbt-1,04260h,07f39h,00404h,097h,04ah,0d085h
	tstr	020h,018h,0,0,0,1,1,0,0,0,0,0,0		; (32 cycles)
	tstr	0,0,0,0,-1,0,0,0,0,0,0,0,0		; (16 cycles)
	db	0cch,011h,006h,0a8h			; expected crc
	tmsg	'ld <b,c,d,e>,(<ix,iy>+1).....'

; ld <h,l>,(<ix,iy>+1) (256 cycles)
ld8ix2:	db	0ffh		; flag mask
	tstr	0ddh,066h,1,0,084e0h,msbt-1,msbt-1,09c52h,0a799h,049b6h,093h,000h,0eeadh
	tstr	020h,008h,0,0,0,1,1,0,0,0,0,0,0		; (16 cycles)
	tstr	0,0,0,0,-1,0,0,0,0,0,0,0,0		; (16 cycles)
	db	0fah,02ah,04dh,003h			; expected crc
	tmsg	'ld <h,l>,(<ix,iy>+1).........'

; ld a,(<ix,iy>+1) (128 cycles)
ld8ix3:	db	0ffh		; flag mask
	tstr	0ddh,07eh,1,0,0d8b6h,msbt-1,msbt-1,0c612h,0df07h,09cd0h,043h,0a6h,0a0e5h
	tstr	020h,0,0,0,0,1,1,0,0,0,0,0,0		; (8 cycles)
	tstr	0,0,0,0,-1,0,0,0,0,0,0,0,0		; (16 cycles)
	db	0a5h,0e9h,0ach,064h			; expected crc
	tmsg	'ld a,(<ix,iy>+1).............'

; ld <ixh,ixl,iyh,iyl>,nn (32 cycles)
ld8ixy:	db	0ffh		; flag mask
	tstr	0ddh,026h,0,0,03c53h,04640h,0e179h,07711h,0c107h,01afah,081h,0adh,05d9bh
	tstr	020h,8,0,0,0,0,0,0,0,0,0,0,0		; (4 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0,-1,0		; (8 cycles)
	db	024h,0e8h,082h,08bh			; expected crc
	tmsg	'ld <ixh,ixl,iyh,iyl>,nn......'

; ld <b,c,d,e,h,l,a>,<b,c,d,e,h,l,a> (3456 cycles)
ld8rr:	db	0ffh		; flag mask
	tstr	040h,0,0,0,072a4h,0a024h,061ach,msbt,082c7h,0718fh,097h,08fh,0ef8eh
	tstr	03fh,0,0,0,0,0,0,0,0,0,0,0,0		; (64 cycles)
	tstr	0,0,0,0,0ffh,0,0,0,-1,-1,0d7h,-1,0	; (54 cycles)
	db	074h,04bh,001h,018h			; expected crc
	tmsg	'ld <bcdehla>,<bcdehla>.......'

; ld <b,c,d,e,ixy,a>,<b,c,d,e,ixy,a> (6912 cycles)
ld8rrx:	db	0ffh		; flag mask
	tstr	0ddh,040h,0,0,0bcc5h,msbt,msbt,msbt,02fc2h,098c0h,083h,01fh,03bcdh
	tstr	020h,03fh,0,0,0,0,0,0,0,0,0,0,0		; (128 cycles)
	tstr	0,0,0,0,0ffh,0,0,0,-1,-1,0d7h,-1,0	; (54 cycles)
	db	047h,08bh,0a3h,06bh			; expected crc
	tmsg	'ld <bcdexya>,<bcdexya>.......'

; ld a,(nnnn) / ld (nnnn),a (44 cycles)
lda:	db	0ffh		; flag mask
	tstr	032h,msbtlo,msbthi,0,0fd68h,0f4ech,044a0h,0b543h,00653h,0cdbah,0d2h,04fh,01fd8h
	tstr	008h,0,0,0,0,0,0,0,0,0,0,0,0		; (2 cycle)
	tstr	0,0,0,0,0ffh,0,0,0,0,0,0d7h,-1,0	; (22 cycles)
	db	0c9h,026h,02dh,0e5h			; expected crc
	tmsg	'ld a,(nnnn) / ld (nnnn),a....'

; ldd<r> (1) (44 cycles)
ldd1:	db	0ffh		; flag mask
	tstr	0edh,0a8h,0,0,09852h,068fah,066a1h,msbt+3,msbt+1,1,0c1h,068h,020b7h
	tstr	0,010h,0,0,0,0,0,0,0,0,0,0,0		; (2 cycles)
	tstr	0,0,0,0,-1,0,0,0,0,0,0d7h,0,0		; (22 cycles)
	db	094h,0f4h,027h,069h			; expected crc
	tmsg	'ldd<r> (1)...................'

; ldd<r> (2) (44 cycles)
ldd2:	db	0ffh		; flag mask
	tstr	0edh,0a8h,0,0,0f12eh,0eb2ah,0d5bah,msbt+3,msbt+1,2,047h,0ffh,0fbe4h
	tstr	0,010h,0,0,0,0,0,0,0,0,0,0,0		; (2 cycles)
	tstr	0,0,0,0,-1,0,0,0,0,0,0d7h,0,0		; (22 cycles)
	db	039h,0ddh,03dh,0e1h			; expected crc
	tmsg	'ldd<r> (2)...................'

; ldi<r> (1) (44 cycles)
ldi1:	db	0ffh		; flag mask
	tstr	0edh,0a0h,0,0,0fe30h,003cdh,06058h,msbt+2,msbt,1,004h,060h,02688h
	tstr	0,010h,0,0,0,0,0,0,0,0,0,0,0		; (2 cycles)
	tstr	0,0,0,0,-1,0,0,0,0,0,0d7h,0,0		; (22 cycles)
	db	0f7h,082h,0b0h,0d1h			; expected crc
	tmsg	'ldi<r> (1)...................'

; ldi<r> (2) (44 cycles)
ldi2:	db	0ffh		; flag mask
	tstr	0edh,0a0h,0,0,04aceh,0c26eh,0b188h,msbt+2,msbt,2,014h,02dh,0a39fh
	tstr	0,010h,0,0,0,0,0,0,0,0,0,0,0		; (2 cycles)
	tstr	0,0,0,0,-1,0,0,0,0,0,0d7h,0,0		; (22 cycles)
	db	0e9h,0eah,0d0h,0aeh			; expected crc
	tmsg	'ldi<r> (2)...................'

; neg (16,384 cycles)
negop:	db	0ffh		; flag mask
	tstr	0edh,044h,0,0,038a2h,05f6bh,0d934h,057e4h,0d2d6h,04642h,043h,05ah,009cch
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,-1,0		; (16,384 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0,0,0		; (1 cycle)
	db	0d6h,038h,0ddh,06ah			; expected crc
	tmsg	'neg..........................'

; <rld,rrd> (7168 cycles)
rldop:	db	0ffh		; flag mask
	tstr	0edh,067h,0,0,091cbh,0c48bh,0fa62h,msbt,0e720h,0b479h,040h,006h,08ae2h
	tstr	0,8,0,0,0ffh,0,0,0,0,0,0,0,0		; (512 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,-1,0		; (14 cycles)
	db	0ffh,082h,03eh,077h			; expected crc
	tmsg	'<rrd,rld>....................'

; <rlca,rrca,rla,rra> (6144 cycles)
rot8080: db	0ffh		; flag mask
	tstr	7,0,0,0,0cb92h,06d43h,00a90h,0c284h,00c53h,0f50eh,091h,0ebh,040fch
	tstr	018h,0,0,0,0,0,0,0,0,0,0,-1,0		; (1024 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0d7h,0,0		; (6 cycles)
	db	09bh,0a3h,080h,07ch			; expected crc
	tmsg	'<rlca,rrca,rla,rra>..........'

; shift/rotate (<ix,iy>+1) (416 cycles)
rotxy:	db	0ffh		; flag mask
	tstr	0ddh,0cbh,1,6,0ddafh,msbt-1,msbt-1,0ff3ch,0dbf6h,094f4h,082h,080h,061d9h
	tstr	020h,0,0,038h,0,0,0,0,0,0,080h,0,0	; (32 cycles)
	tstr	0,0,0,0,0ffh,0,0,0,0,0,057h,0,0		; (13 cycles)
	db	071h,000h,034h,0cbh			; expected crc
	tmsg	'shf/rot (<ix,iy>+1)..........'

; shift/rotate <b,c,d,e,h,l,(hl),a> (6784 cycles)
rotz80:	db	0ffh		; flag mask
	tstr	0cbh,0,0,0,0ccebh,05d4ah,0e007h,msbt,01395h,030eeh,043h,078h,03dadh
	tstr	0,03fh,0,0,0,0,0,0,0,0,080h,0,0		; (128 cycles)
	tstr	0,0,0,0,0ffh,0,0,0,-1,-1,057h,-1,0	; (53 cycles)
	db	0a4h,025h,058h,033h			; expected crc
	tmsg	'shf/rot <b,c,d,e,h,l,(hl),a>.'

; <set,res> n,<b,c,d,e,h,l,(hl),a> (7936 cycles)
srz80:	db	0ffh		; flag mask
	tstr	0cbh,080h,0,0,02cd5h,097abh,039ffh,msbt,0d14bh,06ab2h,053h,027h,0b538h
	tstr	0,07fh,0,0,0,0,0,0,0,0,0,0,0		; (128 cycles)
	tstr	0,0,0,0,0ffh,0,0,0,-1,-1,0d7h,-1,0	; (62 cycles)
	db	08bh,057h,0f0h,008h			; expected crc
	tmsg	'<set,res> n,<bcdehl(hl)a>....'

; <set,res> n,(<ix,iy>+1) (1792 cycles)
srzx:	db	0ffh		; flag mask
	tstr	0ddh,0cbh,1,086h,0fb44h,msbt-1,msbt-1,0ba09h,068beh,032d8h,010h,05eh,0a867h
	tstr	020h,0,0,078h,0,0,0,0,0,0,0,0,0	; (128 cycles)
	tstr	0,0,0,0,0ffh,0,0,0,0,0,0d7h,0,0		;(14 cycles)
	db	0cch,063h,0f9h,08ah			; expected crc
	tmsg	'<set,res> n,(<ix,iy>+1)......'

; ld (<ix,iy>+1),<b,c,d,e> (1024 cycles)
st8ix1:	db	0ffh		; flag mask
	tstr	0ddh,070h,1,0,0270dh,msbt-1,msbt-1,0b73ah,0887bh,099eeh,086h,070h,0ca07h
	tstr	020h,003h,0,0,0,1,1,0,0,0,0,0,0		; (32 cycles)
	tstr	0,0,0,0,0,0,0,0,-1,-1,0,0,0		; (32 cycles)
	db	004h,062h,06ah,0bfh			; expected crc
	tmsg	'ld (<ix,iy>+1),<b,c,d,e>.....'

; ld (<ix,iy>+1),<h,l> (256 cycles)
st8ix2:	db	0ffh		; flag mask
	tstr	0ddh,074h,1,0,0b664h,msbt-1,msbt-1,0e8ach,0b5f5h,0aafeh,012h,010h,09566h
	tstr	020h,001h,0,0,0,1,1,0,0,0,0,0,0		; (16 cycles)
	tstr	0,0,0,0,0,0,0,-1,0,0,0,0,0		; (32 cycles)
	db	06ah,01ah,088h,031h			; expected crc
	tmsg	'ld (<ix,iy>+1),<h,l>.........'

; ld (<ix,iy>+1),a (64 cycles)
st8ix3:	db	0ffh		; flag mask
	tstr	0ddh,077h,1,0,067afh,msbt-1,msbt-1,04f13h,00644h,0bcd7h,050h,0ach,05fafh
	tstr	020h,0,0,0,0,1,1,0,0,0,0,0,0		; (8 cycles)
	tstr	0,0,0,0,0,0,0,0,0,0,0,-1,0		; (8 cycles)
	db	0cch,0beh,05ah,096h			; expected crc
	tmsg	'ld (<ix,iy>+1),a.............'

; ld (<bc,de>),a (96 cycles)
stabd:	db	0ffh		; flag mask
	tstr	2,0,0,0,00c3bh,0b592h,06cffh,0959eh,msbt,msbt+1,0c1h,021h,0bde7h
	tstr	018h,0,0,0,0,0,0,0,0,0,0,0,0		; (4 cycles)
	tstr	0,0,0,0,-1,0,0,0,0,0,0,-1,0		; (24 cycles)
	db	07ah,04ch,011h,04fh			; expected crc
	tmsg	'ld (<bc,de>),a...............'

; start test pointed to by (hl)
stt:	push	hl
	ld	a,(hl)		; get pointer to test
	inc	hl
	ld	h,(hl)
	ld	l,a
	ld	a,(hl)		; flag mask
	ld	(flgmsk+1),a
	inc	hl
	push	hl
	ld	de,20
	add	hl,de		; point to incmask
	ld	de,counter
	call	initmask
	pop	hl
	push	hl
	ld	de,20+20
	add	hl,de		; point to scanmask
	ld	de,shifter
	call	initmask
	ld	hl,shifter
	ld	(hl),1		; first bit
	pop	hl
	push	hl
	ld	de,iut		; copy initial instruction under test
	ld	bc,4
	ldir
	ld	de,msbt		; copy initial machine state
	ld	bc,16
	ldir
	ld	de,20+20+4	; skip incmask, scanmask and expcrc
	add	hl,de
	ex	de,hl
	ld	c,9
	call	bdos		; show test name
	call	initcrc		; initialise crc
; test loop
tlp:	ld	a,(iut)
	cp	076h		; pragmatically avoid halt intructions
	jp	z,tlp2
	and	0dfh
	cp	0ddh
	jp	nz,tlp1
	ld	a,(iut+1)
	cp	076h
tlp1:	call	nz,test		; execute the test instruction
tlp2:	call	count		; increment the counter
	call	nz,shift	; shift the scan bit
	pop	hl		; pointer to test case
	jp	z,tlp3		; done if shift returned NZ
	ld	de,20+20+20
	add	hl,de		; point to expected crc
	call	cmpcrc
	ld	de,okmsg
	jp	z,tlpok
	push	hl		; save pointer to crc
	ld	hl,crcval
	ld	de,ermsg1	; jgh: swap crc= and expected= messages
	ld	c,9
	call	bdos
	call	phex8
	ld	de,ermsg2
	ld	c,9
	call	bdos
	pop	hl		; get pointer to crc back
	call	phex8
	ld	de,crlf
tlpok:	ld	c,9
	call	bdos
	pop	hl
	inc	hl
	inc	hl
	ret

tlp3:	push	hl
	ld	a,1		; initialise count and shift scanners
	ld	(cntbit),a
	ld	(shfbit),a
	ld	hl,counter
	ld	(cntbyt),hl
	ld	hl,shifter
	ld	(shfbyt),hl

	ld	b,4		; bytes in iut field
	pop	hl		; pointer to test case
	push	hl
	ld	de,iut
	call	setup		; setup iut
	ld	b,16		; bytes in machine state
	ld	de,msbt
	call	setup		; setup machine state
	jp	tlp

; set up a field of the test case
; b  = number of bytes
; hl = pointer to base case
; de = destination
setup:	call	subyte
	inc	hl
	dec	b
	jp	nz,setup
	ret

subyte:	push	bc
	push	de
	push	hl
	ld	c,(hl)		; get base byte
	ld	de,20
	add	hl,de		; point to incmask
	ld	a,(hl)
	cp	0
	jp	z,subshf
	ld	b,8		; 8 bits
subclp:	rrca
	push	af
	ld	a,0
	call	c,nxtcbit	; get next counter bit if mask bit was set
	xor	c		; flip bit if counter bit was set
	rrca
	ld	c,a
	pop	af
	dec	b
	jp	nz,subclp
	ld	b,8
subshf:	ld	de,20
	add	hl,de		; point to shift mask
	ld	a,(hl)
	cp	0
	jp	z,substr
	ld	b,8		; 8 bits
sbshf1:	rrca
	push	af
	ld	a,0
	call	c,nxtsbit	; get next shifter bit if mask bit was set
	xor	c		; flip bit if shifter bit was set
	rrca
	ld	c,a
	pop	af
	dec	b
	jp	nz,sbshf1
substr:	pop	hl
	pop	de
	ld	a,c
	ld	(de),a		; mangled byte to destination
	inc	de
	pop	bc
	ret

; get next counter bit in low bit of a
cntbit:	ds	1
cntbyt:	ds	2

nxtcbit: push	bc
	push	hl
	ld	hl,(cntbyt)
	ld	b,(hl)
	ld	hl,cntbit
	ld	a,(hl)
	ld	c,a
	rlca
	ld	(hl),a
	cp	1
	jp	nz,ncb1
	ld	hl,(cntbyt)
	inc	hl
	ld	(cntbyt),hl
ncb1:	ld	a,b
	and	c
	pop	hl
	pop	bc
	ret	z
	ld	a,1
	ret
	
; get next shifter bit in low bit of a
shfbit:	ds	1
shfbyt:	ds	2

nxtsbit: push	bc
	push	hl
	ld	hl,(shfbyt)
	ld	b,(hl)
	ld	hl,shfbit
	ld	a,(hl)
	ld	c,a
	rlca
	ld	(hl),a
	cp	1
	jp	nz,nsb1
	ld	hl,(shfbyt)
	inc	hl
	ld	(shfbyt),hl
nsb1:	ld	a,b
	and	c
	pop	hl
	pop	bc
	ret	z
	ld	a,1
	ret
	

; clear memory at hl, bc bytes
clrmem:	push	af
	push	bc
	push	de
	push	hl
	ld	(hl),0
	ld	d,h
	ld	e,l
	inc	de
	dec	bc
	ldir
	pop	hl
	pop	de
	pop	bc
	pop	af
	ret

; initialise counter or shifter
; de = pointer to work area for counter or shifter
; hl = pointer to mask
initmask:
	push	de
	ex	de,hl
	ld	bc,20+20
	call	clrmem		; clear work area
	ex	de,hl
	ld	b,20		; byte counter
	ld	c,1		; first bit
	ld	d,0		; bit counter
imlp:	ld	e,(hl)
imlp1:	ld	a,e
	and	c
	jp	z,imlp2
	inc	d
imlp2:	ld	a,c
	rlca
	ld	c,a
	cp	1
	jp	nz,imlp1
	inc	hl
	dec	b
	jp	nz,imlp
; got number of 1-bits in mask in reg d
	ld	a,d
	and	0f8h
	rrca
	rrca
	rrca			; divide by 8 (get byte offset)
	ld	l,a
	ld	h,0
	ld	a,d
	and	7		; bit offset
	inc	a
	ld	b,a
	ld	a,080h
imlp3:	rlca
	dec	b
	jp	nz,imlp3
	pop	de
	add	hl,de
	ld	de,20
	add	hl,de
	ld	(hl),a
	ret

; multi-byte counter
count:	push	bc
	push	de
	push	hl
	ld	hl,counter	; 20 byte counter starts here
	ld	de,20		; somewhere in here is the stop bit
	ex	de,hl
	add	hl,de
	ex	de,hl
cntlp:	inc	(hl)
	ld	a,(hl)
	cp	0
	jp	z,cntlp1	; overflow to next byte
	ld	b,a
	ld	a,(de)
	and	b		; test for terminal value
	jp	z,cntend
	ld	(hl),0		; reset to zero
cntend:	pop	bc
	pop	de
	pop	hl
	ret

cntlp1:	inc	hl
	inc	de
	jp	cntlp
	

; multi-byte shifter
shift:	push	bc
	push	de
	push	hl
	ld	hl,shifter	; 20 byte shift register starts here
	ld	de,20		; somewhere in here is the stop bit
	ex	de,hl
	add	hl,de
	ex	de,hl
shflp:	ld	a,(hl)
	or	a
	jp	z,shflp1
	ld	b,a
	ld	a,(de)
	and	b
	jp	nz,shlpe
	ld	a,b
	rlca
	cp	1
	jp	nz,shflp2
	ld	(hl),0
	inc	hl
	inc	de
shflp2:	ld	(hl),a
	xor	a		; set Z
shlpe:	pop	hl
	pop	de
	pop	bc
	ret
shflp1:	inc	hl
	inc	de
	jp	shflp

counter: ds	2*20
shifter: ds	2*20

; test harness
test:	push	af
	push	bc
	push	de
	push	hl
      if	0
	ld	de,crlf
	ld	c,9
	call	bdos
	ld	hl,iut
	ld	b,4
	call	hexstr
	ld	e,' '
	ld	c,2
	call	bdos
	ld	b,16
	ld	hl,msbt
	call	hexstr
      endif	
	di			; disable interrupts
	ld	(spsav),sp	; save stack pointer
	ld	sp,msbt+2	; point to test-case machine state
	pop	iy		; and load all regs
	pop	ix
	pop	hl
	pop	de
	pop	bc
	pop	af
	ld	sp,(spbt)
iut:	ds	4		; max 4 byte instruction under test
	ld	(spat),sp	; save stack pointer
	ld	sp,spat
	push	af		; save other registers
	push	bc
	push	de
	push	hl
	push	ix
	push	iy
	ld	sp,(spsav)	; restore stack pointer
	ei			; enable interrupts
	ld	hl,(msbt)	; copy memory operand
	ld	(msat),hl
	ld	hl,flgsat	; flags after test
	ld	a,(hl)
flgmsk:	and	0d7h		; mask-out irrelevant bits (self-modified code!)
	ld	(hl),a
	ld	b,16		; total of 16 bytes of state
	ld	de,msat
	ld	hl,crcval
tcrc:	ld	a,(de)
	inc	de
	call	updcrc		; accumulate crc of this test case
	dec	b
	jp	nz,tcrc
      if	0
	ld	e,' '
	ld	c,2
	call	bdos
	ld	hl,crcval
	call	phex8
	ld	de,crlf
	ld	c,9
	call	bdos
	ld	hl,msat
	ld	b,16
	call	hexstr
	ld	de,crlf
	ld	c,9
	call	bdos
      endif
	pop	hl
	pop	de
	pop	bc
	pop	af
	ret

; machine state after test
msat:	ds	14	; memop,iy,ix,hl,de,bc,af
spat:	ds	2	; stack pointer after test
; ZMAC/MAXAM doesn't like ':' after label with EQUs
flgsat	equ	spat-2	; flags

spsav:	ds	2	; saved stack pointer

; display hex string (pointer in hl, byte count in b)
hexstr:	ld	a,(hl)
	call	phex2
	inc	hl
	dec	b
	jp	nz,hexstr
	ret

; display hex
; display the big-endian 32-bit value pointed to by hl
phex8:	push	af
	push	bc
	push	hl
	ld	b,4
ph8lp:	ld	a,(hl)
	call	phex2
	inc	hl
	dec	b
	jp	nz,ph8lp
	pop	hl
	pop	bc
	pop	af
	ret

; display byte in a
phex2:	push	af
	rrca
	rrca
	rrca
	rrca
	call	phex1
	pop	af
; fall through	

; display low nibble in a
phex1:	push	af
	push	bc
	push	de
	push	hl
	and	0fh
	cp	10
	jp	c,ph11
	add	a,'a'-'9'-1
ph11:	add	a,'0'
	ld	e,a
	ld	c,2
	call	bdos
	pop	hl
	pop	de
	pop	bc
	pop	af
	ret

bdos	push	af
	push	bc
	push	de
	push	hl
; Accept call 2 (print char) and call 9 (print string)
; Ignore all others
	ld	b,a		; save char (destroys B)
	ld	a,c
	cp	2
	jr	z,prchar
	cp	9
	jr	z,prstring
bdosdone:
	pop	hl
	pop	de
	pop	bc
	pop	af
	ret

; Pass bdos calls to Spectrum system
prchar:
	ld	a,b		; get char back
	cp	10		; ignore LF, CR auto-LFs
	jr	z,bdosdone
	rst	010h
	jr	bdosdone

prstring:
	ld	a,(de)
	cp	'$'
	jr	z,bdosdone
	cp	10
	call	nz,010h
	inc	de
	jr	prstring


msg1:	db	'Z80 instruction exerciser',10,13,10,13,'$'
msg2:	db	'Tests complete$'
okmsg:	db	'OK',10,13,'$'
ermsg1:	db	'   CRC:$'	; was ERROR:
ermsg2:	db	' expected:$'
crlf:	db	10,13,'$'

; compare crc
; hl points to value to compare to crcval
cmpcrc:	push	bc
	push	de
	push	hl
	ld	de,crcval
	ld	b,4
cclp:	ld	a,(de)
	cp	(hl)
	jp	nz,cce
	inc	hl
	inc	de
	dec	b
	jp	nz,cclp
cce:	pop	hl
	pop	de
	pop	bc
	ret

; 32-bit crc routine
; entry: a contains next byte, hl points to crc
; exit:  crc updated
updcrc:	push	af
	push	bc
	push	de
	push	hl
	push	hl
	ld	de,3
	add	hl,de	; point to low byte of old crc
	xor	(hl)	; xor with new byte
	ld	l,a
	ld	h,0
	add	hl,hl	; use result as index into table of 4 byte entries
	add	hl,hl
	ex	de,hl
	ld	hl,crctab
	add	hl,de	; point to selected entry in crctab
	ex	de,hl
	pop	hl
	ld	bc,4	; c = byte count, b = accumulator
crclp:	ld	a,(de)
	xor	b
	ld	b,(hl)
	ld	(hl),a
	inc	de
	inc	hl
	dec	c
	jp	nz,crclp
      if	0
	ld	hl,crcval
	call	phex8
	ld	de,crlf
	ld	c,9
	call	bdos
      endif
	pop	hl
	pop	de
	pop	bc
	pop	af
	ret

initcrc:push	af
	push	bc
	push	hl
	ld	hl,crcval
	ld	a,0ffh
	ld	b,4
icrclp:	ld	(hl),a
	inc	hl
	dec	b
	jp	nz,icrclp
	pop	hl
	pop	bc
	pop	af
	ret

crcval	ds	4

crctab:	db	000h,000h,000h,000h
	db	077h,007h,030h,096h
	db	0eeh,00eh,061h,02ch
	db	099h,009h,051h,0bah
	db	007h,06dh,0c4h,019h
	db	070h,06ah,0f4h,08fh
	db	0e9h,063h,0a5h,035h
	db	09eh,064h,095h,0a3h
	db	00eh,0dbh,088h,032h
	db	079h,0dch,0b8h,0a4h
	db	0e0h,0d5h,0e9h,01eh
	db	097h,0d2h,0d9h,088h
	db	009h,0b6h,04ch,02bh
	db	07eh,0b1h,07ch,0bdh
	db	0e7h,0b8h,02dh,007h
	db	090h,0bfh,01dh,091h
	db	01dh,0b7h,010h,064h
	db	06ah,0b0h,020h,0f2h
	db	0f3h,0b9h,071h,048h
	db	084h,0beh,041h,0deh
	db	01ah,0dah,0d4h,07dh
	db	06dh,0ddh,0e4h,0ebh
	db	0f4h,0d4h,0b5h,051h
	db	083h,0d3h,085h,0c7h
	db	013h,06ch,098h,056h
	db	064h,06bh,0a8h,0c0h
	db	0fdh,062h,0f9h,07ah
	db	08ah,065h,0c9h,0ech
	db	014h,001h,05ch,04fh
	db	063h,006h,06ch,0d9h
	db	0fah,00fh,03dh,063h
	db	08dh,008h,00dh,0f5h
	db	03bh,06eh,020h,0c8h
	db	04ch,069h,010h,05eh
	db	0d5h,060h,041h,0e4h
	db	0a2h,067h,071h,072h
	db	03ch,003h,0e4h,0d1h
	db	04bh,004h,0d4h,047h
	db	0d2h,00dh,085h,0fdh
	db	0a5h,00ah,0b5h,06bh
	db	035h,0b5h,0a8h,0fah
	db	042h,0b2h,098h,06ch
	db	0dbh,0bbh,0c9h,0d6h
	db	0ach,0bch,0f9h,040h
	db	032h,0d8h,06ch,0e3h
	db	045h,0dfh,05ch,075h
	db	0dch,0d6h,00dh,0cfh
	db	0abh,0d1h,03dh,059h
	db	026h,0d9h,030h,0ach
	db	051h,0deh,000h,03ah
	db	0c8h,0d7h,051h,080h
	db	0bfh,0d0h,061h,016h
	db	021h,0b4h,0f4h,0b5h
	db	056h,0b3h,0c4h,023h
	db	0cfh,0bah,095h,099h
	db	0b8h,0bdh,0a5h,00fh
	db	028h,002h,0b8h,09eh
	db	05fh,005h,088h,008h
	db	0c6h,00ch,0d9h,0b2h
	db	0b1h,00bh,0e9h,024h
	db	02fh,06fh,07ch,087h
	db	058h,068h,04ch,011h
	db	0c1h,061h,01dh,0abh
	db	0b6h,066h,02dh,03dh
	db	076h,0dch,041h,090h
	db	001h,0dbh,071h,006h
	db	098h,0d2h,020h,0bch
	db	0efh,0d5h,010h,02ah
	db	071h,0b1h,085h,089h
	db	006h,0b6h,0b5h,01fh
	db	09fh,0bfh,0e4h,0a5h
	db	0e8h,0b8h,0d4h,033h
	db	078h,007h,0c9h,0a2h
	db	00fh,000h,0f9h,034h
	db	096h,009h,0a8h,08eh
	db	0e1h,00eh,098h,018h
	db	07fh,06ah,00dh,0bbh
	db	008h,06dh,03dh,02dh
	db	091h,064h,06ch,097h
	db	0e6h,063h,05ch,001h
	db	06bh,06bh,051h,0f4h
	db	01ch,06ch,061h,062h
	db	085h,065h,030h,0d8h
	db	0f2h,062h,000h,04eh
	db	06ch,006h,095h,0edh
	db	01bh,001h,0a5h,07bh
	db	082h,008h,0f4h,0c1h
	db	0f5h,00fh,0c4h,057h
	db	065h,0b0h,0d9h,0c6h
	db	012h,0b7h,0e9h,050h
	db	08bh,0beh,0b8h,0eah
	db	0fch,0b9h,088h,07ch
	db	062h,0ddh,01dh,0dfh
	db	015h,0dah,02dh,049h
	db	08ch,0d3h,07ch,0f3h
	db	0fbh,0d4h,04ch,065h
	db	04dh,0b2h,061h,058h
	db	03ah,0b5h,051h,0ceh
	db	0a3h,0bch,000h,074h
	db	0d4h,0bbh,030h,0e2h
	db	04ah,0dfh,0a5h,041h
	db	03dh,0d8h,095h,0d7h
	db	0a4h,0d1h,0c4h,06dh
	db	0d3h,0d6h,0f4h,0fbh
	db	043h,069h,0e9h,06ah
	db	034h,06eh,0d9h,0fch
	db	0adh,067h,088h,046h
	db	0dah,060h,0b8h,0d0h
	db	044h,004h,02dh,073h
	db	033h,003h,01dh,0e5h
	db	0aah,00ah,04ch,05fh
	db	0ddh,00dh,07ch,0c9h
	db	050h,005h,071h,03ch
	db	027h,002h,041h,0aah
	db	0beh,00bh,010h,010h
	db	0c9h,00ch,020h,086h
	db	057h,068h,0b5h,025h
	db	020h,06fh,085h,0b3h
	db	0b9h,066h,0d4h,009h
	db	0ceh,061h,0e4h,09fh
	db	05eh,0deh,0f9h,00eh
	db	029h,0d9h,0c9h,098h
	db	0b0h,0d0h,098h,022h
	db	0c7h,0d7h,0a8h,0b4h
	db	059h,0b3h,03dh,017h
	db	02eh,0b4h,00dh,081h
	db	0b7h,0bdh,05ch,03bh
	db	0c0h,0bah,06ch,0adh
	db	0edh,0b8h,083h,020h
	db	09ah,0bfh,0b3h,0b6h
	db	003h,0b6h,0e2h,00ch
	db	074h,0b1h,0d2h,09ah
	db	0eah,0d5h,047h,039h
	db	09dh,0d2h,077h,0afh
	db	004h,0dbh,026h,015h
	db	073h,0dch,016h,083h
	db	0e3h,063h,00bh,012h
	db	094h,064h,03bh,084h
	db	00dh,06dh,06ah,03eh
	db	07ah,06ah,05ah,0a8h
	db	0e4h,00eh,0cfh,00bh
	db	093h,009h,0ffh,09dh
	db	00ah,000h,0aeh,027h
	db	07dh,007h,09eh,0b1h
	db	0f0h,00fh,093h,044h
	db	087h,008h,0a3h,0d2h
	db	01eh,001h,0f2h,068h
	db	069h,006h,0c2h,0feh
	db	0f7h,062h,057h,05dh
	db	080h,065h,067h,0cbh
	db	019h,06ch,036h,071h
	db	06eh,06bh,006h,0e7h
	db	0feh,0d4h,01bh,076h
	db	089h,0d3h,02bh,0e0h
	db	010h,0dah,07ah,05ah
	db	067h,0ddh,04ah,0cch
	db	0f9h,0b9h,0dfh,06fh
	db	08eh,0beh,0efh,0f9h
	db	017h,0b7h,0beh,043h
	db	060h,0b0h,08eh,0d5h
	db	0d6h,0d6h,0a3h,0e8h
	db	0a1h,0d1h,093h,07eh
	db	038h,0d8h,0c2h,0c4h
	db	04fh,0dfh,0f2h,052h
	db	0d1h,0bbh,067h,0f1h
	db	0a6h,0bch,057h,067h
	db	03fh,0b5h,006h,0ddh
	db	048h,0b2h,036h,04bh
	db	0d8h,00dh,02bh,0dah
	db	0afh,00ah,01bh,04ch
	db	036h,003h,04ah,0f6h
	db	041h,004h,07ah,060h
	db	0dfh,060h,0efh,0c3h
	db	0a8h,067h,0dfh,055h
	db	031h,06eh,08eh,0efh
	db	046h,069h,0beh,079h
	db	0cbh,061h,0b3h,08ch
	db	0bch,066h,083h,01ah
	db	025h,06fh,0d2h,0a0h
	db	052h,068h,0e2h,036h
	db	0cch,00ch,077h,095h
	db	0bbh,00bh,047h,003h
	db	022h,002h,016h,0b9h
	db	055h,005h,026h,02fh
	db	0c5h,0bah,03bh,0beh
	db	0b2h,0bdh,00bh,028h
	db	02bh,0b4h,05ah,092h
	db	05ch,0b3h,06ah,004h
	db	0c2h,0d7h,0ffh,0a7h
	db	0b5h,0d0h,0cfh,031h
	db	02ch,0d9h,09eh,08bh
	db	05bh,0deh,0aeh,01dh
	db	09bh,064h,0c2h,0b0h
	db	0ech,063h,0f2h,026h
	db	075h,06ah,0a3h,09ch
	db	002h,06dh,093h,00ah
	db	09ch,009h,006h,0a9h
	db	0ebh,00eh,036h,03fh
	db	072h,007h,067h,085h
	db	005h,000h,057h,013h
	db	095h,0bfh,04ah,082h
	db	0e2h,0b8h,07ah,014h
	db	07bh,0b1h,02bh,0aeh
	db	00ch,0b6h,01bh,038h
	db	092h,0d2h,08eh,09bh
	db	0e5h,0d5h,0beh,00dh
	db	07ch,0dch,0efh,0b7h
	db	00bh,0dbh,0dfh,021h
	db	086h,0d3h,0d2h,0d4h
	db	0f1h,0d4h,0e2h,042h
	db	068h,0ddh,0b3h,0f8h
	db	01fh,0dah,083h,06eh
	db	081h,0beh,016h,0cdh
	db	0f6h,0b9h,026h,05bh
	db	06fh,0b0h,077h,0e1h
	db	018h,0b7h,047h,077h
	db	088h,008h,05ah,0e6h
	db	0ffh,00fh,06ah,070h
	db	066h,006h,03bh,0cah
	db	011h,001h,00bh,05ch
	db	08fh,065h,09eh,0ffh
	db	0f8h,062h,0aeh,069h
	db	061h,06bh,0ffh,0d3h
	db	016h,06ch,0cfh,045h
	db	0a0h,00ah,0e2h,078h
	db	0d7h,00dh,0d2h,0eeh
	db	04eh,004h,083h,054h
	db	039h,003h,0b3h,0c2h
	db	0a7h,067h,026h,061h
	db	0d0h,060h,016h,0f7h
	db	049h,069h,047h,04dh
	db	03eh,06eh,077h,0dbh
	db	0aeh,0d1h,06ah,04ah
	db	0d9h,0d6h,05ah,0dch
	db	040h,0dfh,00bh,066h
	db	037h,0d8h,03bh,0f0h
	db	0a9h,0bch,0aeh,053h
	db	0deh,0bbh,09eh,0c5h
	db	047h,0b2h,0cfh,07fh
	db	030h,0b5h,0ffh,0e9h
	db	0bdh,0bdh,0f2h,01ch
	db	0cah,0bah,0c2h,08ah
	db	053h,0b3h,093h,030h
	db	024h,0b4h,0a3h,0a6h
	db	0bah,0d0h,036h,005h
	db	0cdh,0d7h,006h,093h
	db	054h,0deh,057h,029h
	db	023h,0d9h,067h,0bfh
	db	0b3h,066h,07ah,02eh
	db	0c4h,061h,04ah,0b8h
	db	05dh,068h,01bh,002h
	db	02ah,06fh,02bh,094h
	db	0b4h,00bh,0beh,037h
	db	0c3h,00ch,08eh,0a1h
	db	05ah,005h,0dfh,01bh
	db	02dh,002h,0efh,08dh

