	title	'DISZ80 disassembler Z80 mnemonics'

;----------------------------------------------------------------;
; Author:	D. Powys-Lybbe
; Written:	Summer 1984 (modified DIS.ASM)
; Revisions:
;  Nov 1984 - various corrections
;
;----------------------------------------------------------------;
; Module 01 - Program
;----------------------------------------------------------------;

	page

	public	cpm,pass1,pass1a,pass2,pass3

	extrn	sets		; 1st op byte types
	extrn	set$cb		; 0CBH double op byte types
	extrn	sets$dd		; 0DDH double op byte types and mnemonics
	extrn	sets$ed		; 0EDH double op byte types and mnemonics
	extrn	sets$fd		; 0FDH double op byte types and mnemonics
;------	extrn	nemon		; 1st op byte mnemonics
;------	extrn	nemon$cb	; 0CBH double op byte mnemonics
	extrn	set$ddcb	; 0DDH,0CBH triple op byte types and mnemonics
	extrn	set$fdcb	; 0FDH,0CBH triple op byte types and mnemonics

	extrn	progend	; end of program

;----------- start of program --------------------

	cseg
CPM	EQU	$
	LXI	d,DIS
	push	d
	ret


	DB CR,LF
	DB '        PROGRAM DIS to disassemble Z80 programs',CR,LF
	DB '        using Zilog Mnemonics',CR,LF
	DB '        (c) COPYRIGHT D. POWYS-LYBBE 06 FEB 1984',CR,LF
	DB '        MML Systems Ltd., 11 SUN STREET, LONDON. E.C.2.',CR,LF
	DB CR,LF
	DB '        Program reads .COM file and forms a .M80 and .MAP  file'
	DB CR,LF,CR,LF,1ah

lenid	equ	$-cpm
 if lenid lt 256	; minimum size for stack
	ds	256-lenid
 endif

stack	equ	$	; stack overwrites copyright message

;------------;
; File types ;
;------------;

t$m80:	db	'M80'
t$map:	db	'MAP'
t$com:	db	'COM'
t$tmp:	db	'$$$'
t$bak:	db	'BAK'


CR		EQU	0DH
LF		EQU	0AH
TAB		EQU	09H

;--------------;
; BDOS equates ;
;--------------;
BDOS	EQU	0005h
dfcb	EQU	005CH
dmabuf	EQU	0080H

COUT	EQU	2
CIN	EQU	1
CSTAT	EQU	11
CMSG	EQU	9
FOPEN	EQU	15
FCLOSE	EQU	16
FDELETE	EQU	19
FREAD	EQU	20
FWRITE	EQU	21
FCREATE EQU	22
FRENAME	EQU	23
FDMA	EQU	26
;
	.8080		; we are using 8080 codes

;======;
; MAIN ;
;======;
DIS:
	LXI	SP,stack	;Use local stack
;
	lxi	h,CPM		; default load address
	shld	load		; Default LOAD address of COM file
	lxi	d,PROGEND	; must be actual end of program
	call	sdehl		; <HL> = <DE> - <HL>
	shld	OFSET		; OFSET to add to program address
;
	xchg
	lhld	load		; load address
	dad	d		; DMA address set to PROGEND
	SHLD	dmaptr
;
	call	opencom		; open file
	call	readcom		; read file
	call	pass1		; build up symbol table
;
	call	opentmp		; open .TMP output file
	call	pass2
	call	done		; flush buffer and close file
	call	makem80		; and make into M80 file
;
	call	openmap		; open file
	call	pass3
	call	done		; flush buffer and close file
;
	jmp	return		; and terminate

OFSET:	dw	PROGEND		; OFSET to add/subtract to code
load:	dw	cpm		; load start address of COM file
START:	dw	PROGEND		; pointer to start of MAP table
FREE:	dw	cpm		; absolute address of end of COM file + 1
dmaptr:	dw	cpm		; dma address
COUNT:	DS	1		; bytes in output buffer
DATA:	DB	010H		; Next program location can be data

NOSPACE:	DB	'DIRECTORY FULL',0DH,0AH,'$'
OLDFILE:	DB	'OLD .M80 FILE',0DH,0AH,'$'

msgp1:	db	cr,lf,'PASS 1',cr,lf,'$'
msgp2:	db	cr,lf,'PASS 2',cr,lf,'$'
msgp3:	db	cr,lf,'PASS 3',cr,lf,'$'

;------------------------------------;
; Check file exists and read file in ;
;------------------------------------;
opencom:
	lxi	h,t$com
	lxi	d,dfcb+9
	lxi	b,3
	call	copy	; FCB set to COM type
	call	zrofcb		; intialise FCB to zero
;
	LXI	D,dfcb
	MVI	C,FOPEN
	CALL	BDOS	;OPEN FILE
	CPI	255
	rnz		; -ok-
	LXI	D,NOTFOUND
	MVI	c,CMSG
	CALL	BDOS
	JMP	RETURN	;FILE NOT FOUND
NOTFOUND:	DB	'NEW .COM FILE',0DH,0AH,'$'

readcom:
	LHLD	dmaptr	; Get DMA address
;
NXTBUF:		;READ NEXT BUFFER FROM DISK
	XCHG		; Copy DMA into <DE>
	MVI	C,FDMA
	CALL	BDOS	; Set DMA
	LXI	D,dfcb
	MVI	C,FREAD
	CALL	BDOS	;READ RECORD
	ORA	A	; Check if read beyondd last record
	rnz		; -at EOF-
	LHLD	dmaptr	; Get DMA address
	lxi	b,128
	DAD	B	; & increment by 128 bytes
	SHLD	dmaptr
	JMP	NXTBUF

;--------------------------;
; Rename output $$$ to M80 ;
;--------------------------;
makem80:
	lxi	h,t$bak
	lxi	d,dfcb+9
	lxi	b,3
	call	copy	; FCB set to .BAK type
	call	zrofcb		; intialise FCB to zero
;
	lxi	d,dfcb
	mvi	c,FDELETE
	call	bdos	;DELETE .BAK file
;
	lxi	h,dfcb
	lxi	d,dfcb+16
	lxi	b,16
	call	copy	; 2nd 16 bytes of FCB set to .BAK
;
	lxi	h,t$m80
	lxi	d,dfcb+9
	lxi	b,3
	call	copy	; FCB set to .M80 type

	lxi	d,dfcb
	mvi	c,FRENAME
	call	bdos	;RENAME .M80 to .BAK
;
	lxi	h,dfcb
	lxi	d,dfcb+16
	lxi	b,16
	call	copy	; 2nd 16 bytes of FCB set to .M80
;
	lxi	h,t$tmp
	lxi	d,dfcb+9
	lxi	b,3
	call	copy	; FCB set to .$$$ type

	lxi	d,dfcb
	mvi	c,FRENAME
	call	bdos	;RENAME .$$$ to .M80

	ret

;---------------------;
; Create new MAP file ;
;---------------------;
openmap:
	lxi	h,t$map
	lxi	d,dfcb+9
	lxi	b,3
	call	copy	; FCB set to .MAP type
	jmp	create

;-----------------------------;
; flush buffer and close file ;
;-----------------------------;
DONE:	MVI	A,1AH	; ADD EOF
	CALL	CHAR
ABORT:	LDA	COUNT
	ORA	A
	CNZ	WRITE	; Write BUFFER to file
CLOSE:	LXI	D,dfcb
	MVI	C,FCLOSE
	jmp	BDOS

;----------------;
; Return to BDOS ;
;----------------;
RETURN:			;NORMAL RETURN
	mvi	c,0
	jmp	bdos	; system reset (warm boot)

	page


;-----------------------------------------------------------------
;	Each branch address is recorded in a table
;	as a 3 Byte address
;	Byte 1 - Type of branch
;CODE:		00 - END of table
;		01 - Unresolved JUMP or CALL to branch
;		03 - Undefined & Unresolved program branch
;		05 - Program continues
;		07 - CALL branch (unresolved) followed by data byte
;		09 - CALL branch (unresolved) followed by data word
;		0b - CALL branch (unresolved) followed by data string
;		80 - Branch point contains INSTRUCTION
;			(All 01 types should end up as 80 type)
;		82 - Branch point contains undefined INSTRUCTION
;			(All 03 types should end up as 82 type)
;		84 - Address contains INSTRUCTION without label
;			(All 05 types should end up as 84 type)
;		86 - CALL branch followed by data byte
;		88 - CALL branch followed by data word
;		8a - CALL branch followed by data string
;
;DATA:		40 - Branch point contains DATA
;		48 - Branch point contains DATA ADDRESS
;		44 - Unresolved table of program jumps
;		4A - Resolved table of program jumps
;		4c - Unlabeled DATA
;		4e - Unlabeled DATA ADDRESS
;
;		20 - No reference to location
;			(Previous instruction PCHL, JMP, RET or DW)
;		24 - Next unresolved entry in table of program jumps
;		2A - Next resolved entry in table of program jumps
;		10 - EQU address/value for single byte
;		18 - EQU address/value for two byte address
;		1A - EQU address/value for JMP or CALL outside program
;		08 - End of program
;
;	Byte 2 & 3 - Address of branch or end
;
; NOTE
;	The MAP contains the absolute address and NOT the OFSET address
;	BIT 0 of BYTE 1 flags UNRESOLVED instruction
;	HIGH NIBBLE = 1 FOR EQUATES
;	HIGH NIBBLE = 2 FOR UNKNOWN PROGRAM
;	HIGH NIBBLE = 4 FOR DATA
;	HIGH NIBBLE = 8 FOR CODE
;---------------------------------------------------------------------------
INITMAP:
	LHLD	dmaptr	; DMA points to first location after data
	SHLD	START	; MAP to begin at this location
	XCHG		; This location is adjusted to absolute location

	lhld	OFSET
	call	sdehl	; <HL> = <DE> - <HL>
	SHLD	FREE	; which is saved in FREE

	XCHG		; and held in <DE>
	MVI	M,01H	; Set first location to unresolved instruction type
	INX	H
; Set program start adress to CP/M start 0100H
	lda	load	; load LOW address of COM file (default 0100h)
	MOV	M,a
	INX	H
	lda	load+1	; load HIGH address of COM file (default 0100h)
	MOV	M,a
	INX	H

	MVI	M,08H	; A NULL entry is included to
	INX	H	; allow EQU to be inserted
	MOV	M,E	; after end of program
	INX	H
	MOV	M,D

	INX	H
	MVI	M,0	; Table ends with a zero
	INX	H
	MOV	M,E	; Which is set to FREE location
	INX	H
	MOV	M,D
	RET

setload:		; <HL> = load address (default 0100h)
	shld	LOAD	; set load address
	lxi	d,PROGEND
	call	sdehl	; <HL> = <DE> - <HL>
	shld	OFSET	; set OFSET value

	lhld	START	; MAP to begin at this location
	XCHG		; This location is adjusted to absolute location

	lhld	OFSET
	call	sdehl	; <HL> = <DE> - <HL>
	SHLD	FREE	; which is saved in FREE

	XCHG		; and held in <DE>

; Set program start adress to specified load address

	call	setfst
	MVI	M,03H	; Set first location to unresolved instruction type
	INX	H
	lda	load	; load LOW address of COM file (default 0100h)
	MOV	M,a
	INX	H
	lda	load+1	; load HIGH address of COM file (default 0100h)
	MOV	M,a
	INX	H

; Adjust program end address for change in start address

	call	setlst
	INX	H
	MOV	M,e
	INX	H
	MOV	M,d
	INX	H
	RET

setfst:	mov	a,m	; search TABLE for first  program entry point
	cpi	01h	; and assume this was the previous load address
	rz
	cpi	03h
	rz
	inx	h
	inx	h
	inx	h
	jmp	setfst

setlst:	mov	a,m	; search TABLE for NULL entry point
	cpi	08h
	rz
	inx	h
	inx	h
	inx	h
	jmp	setlst

;
;Test if MAP input file exists and read its contents
;
RDMAP:	; Read .MAP file if it exists
	lxi	h,t$map
	LXI	d,dfcb+9	; FCB set to MAP type
	lxi	b,3
	call	copy
	call	zrofcb		; intialise FCB to zero

	LXI	D,dfcb
	MVI	C,FOPEN
	CALL	BDOS	;OPEN FILE
	CPI	255
	rz		; no file

	lxi	h,-1
	shld	maprec

	mvi	c,FDMA
	lxi	d,dmabuf
	call	bdos	; set DMA

	lxi	d,getmsg
	CALL	dsply

;--------------------------------------------------------;
; read .MAP file for previous symbol and direction table
; structure:
; 		0000 /n/ name <cr><lf>
; where: 0000 - hex address
;	 n    - type L, P, B, W, T or S enclosed between slashes
;        name - optional character name
; Note: if /L/ is used then it should be the first item
;--------------------------------------------------------;
getmap:	call	getmline
	rnz		; -no- line
	lxi	h,mline
	call	getmadr
	rnz		; -bad- address
	xchg
	shld	addopt
	xchg
	call	getmtype
	rnz		; -bad- type
	sta	defopt
	inr	a
	jz	getload	; special flag

	lxi	d,addopt	; address
	lda	defopt		; type
	mov	b,a
	call	insx		; add to table
	jmp	getmap

getload:
	lhld	addopt		; LOAD address
	call	setload		; set pointers for this load address
	jmp	getmap

mline:	ds	20	; should be enough
getmsg:	db	cr,lf,'Entry points read from file .MAP$'
maprec:	dw	0	; pointer to next character

getmline:		; fetch next line
	lxi	h,mline
getml2:	push	h
	call	getmchar
	pop	h
	rnz		; no characters left
	mov	m,a
	inx	h
	cpi	cr
	jnz	getml2
	call	getmchar
	cpi	lf
	ret		; return NZ if error

getmrec:		; read next record
	lxi	h,0
	shld	maprec
	lxi	d,dfcb
	mvi	c,FREAD
	call	bdos	; read record
	ora	a
	rnz		; at EOF

getmchar:		; get next character
	lhld	maprec
	mov	d,h
	mov	e,l
	inx	hl
	shld	maprec
	mov	a,e
	cpi	128
	jnc	getmrec
	lxi	h,dmabuf
	dad	d
	mov	a,m
	cpi	1ah	; ctrl-Z
	jz	getmeof
	cmp	a
	ret		; with CF=Z
getmeof:
	ori	0ffh
	ret		; with CF=NZ

getmadr:		; construct address
	mov	a,m
	inx	h
	cpi	' '
	jz	getmadr
	call	unhex
	jc	badmadr
	rlc
	rlc
	rlc
	rlc
	mov	d,a
	mov	a,m
	inx	h
	call	unhex
	jc	badmadr
	ora	d
	mov	d,a

	mov	a,m
	inx	h
	call	unhex
	jc	badmadr
	rlc
	rlc
	rlc
	rlc
	mov	e,a
	mov	a,m
	inx	h
	call	unhex
	jc	badmadr
	ora	e
	mov	e,a
	xra	a
	ret
badmadr:
	ori	-1
	ret


getmtype:		; construct type
	mov	a,m
	inx	h
	cpi	' '
	jz	getmtype
	cpi	'/'
	rnz
	mov	b,m
	inx	h
	mov	a,m
	inx	h
	cpi	'/'
	rnz
	mov	a,b
	cpi	'P'
	mvi	a,03h
	rz		; Program entry point
	mov	a,b
	cpi	'N'
	mvi	a,40h
	rz		; DB entry point
	mov	a,b
	cpi	'B'
	mvi	a,40h
	rz		; DB entry point
	mov	a,b
	cpi	'W'
	mvi	a,48h
	rz		; DW entry point
	mov	a,b
	cpi	'T'
	mvi	a,44h
	rz		; Table of vectors entry point
	mov	a,b
	cpi	'S'
	mvi	a,0bh
	rz		; Subroutine expects string of data terminated by 0
	mov	a,b
	cpi	'L'
	mvi	a,0ffh
	rz		; Load address (default 0100h)
	ret

;
ADDOPT:	DW	0000H
DEFOPT:	DB	003H
NONHEX:	DB	'?',CR,'$'

addhelp:
 db cr,lf
 db ' The dis-assembly may be controlled by predefining the activity'
 db cr,lf
 db ' at an address using one or more of the following commands:'
 db cr,lf
 db cr,lf
 db '    L0000    -  Program load address in HEX'
 db cr,lf
 db '    0000     -  Program entry point in HEX'
 db cr,lf
 db '    P0000    -  Program entry point in HEX'
 db cr,lf
 db '    B0000    -  Byte wide data entry point in HEX'
 db cr,lf
 db '    W0000    -  Word wide data entry point in HEX'
 db cr,lf
 db '    S0000    -  String follows call to this subroutine entry point in HEX'
 db cr,lf
 db '    T0000    -  Table of pointers entry point in HEX'
 db cr,lf
 db '$'

ENTRY:	DB	CR,LF,'Add entry point (0000 to quit): ','$'
NEWLN:	DB	CR,LF,'$'
;
ADDBR:	CALL	OPT
	LXI	D,NEWLN
	CALL	DSPLY
	RET
;
DSPLY:	MVI	C,CMSG
	CALL	BDOS
	RET
;
BAD:	LXI	D,NONHEX
	CALL	DSPLY
;
OPT:	LXI	D,ENTRY
	CALL	DSPLY
	MVI	A,003H
	STA	DEFOPT
OPT5:	LXI	H,ADDOPT
	INX	H
	CALL	CONS
	RZ
	JNC	OPT6
	MOV	A,C
	CPI	'P'
	JZ	DOPT1
	CPI	'B'
	JZ	DOPT2
	CPI	'N'
	JZ	DOPT2
	CPI	'W'
	JZ	DOPT3
	CPI	'T'
	JZ	DOPT4
	CPI	'S'
	JZ	DOPT5
	CPI	'L'
	JZ	DOPT6
	JMP	BAD
DOPT1:	MVI	A,03H	; Program entry point
	JMP	DOPT9
DOPT2:	MVI	A,040H	; DB entry point
	JMP	DOPT9
DOPT3:	MVI	A,048H	; DW entry point
	JMP	DOPT9
DOPT4:	MVI	A,044H	; DW entry point to table of program jumps
	JMP	DOPT9
DOPT5:	MVI	A,00bH	; subroutine requires string
	JMP	DOPT9
DOPT6:			; Load address (default 0100h)
	mvi	a,0ffh	; special function
	JMP	DOPT9
DOPT9:	STA	DEFOPT
	JMP	OPT5
OPT6:	lxi	d,0
	RAL
	RAL
	RAL
	RAL
	MOV	D,A
	CALL	CONS
	JC	BAD
	JNZ	OPT1
OPT0:	MOV	A,D
	ORA	E
	RZ
	JMP	BAD
;
OPT1:	ADD	D
	MOV	M,A
	MOV	D,A
;
	DCX	H
	CALL	CONS
	JC	BAD
	JZ	OPT0
	RAL
	RAL
	RAL
	RAL
	MOV	E,A
	CALL	CONS
	JC	BAD
	JZ	OPT0
;
OPT3:	ADD	E
	MOV	M,A
	JNZ	OPT4
	ADD	D
	RZ
OPT4:	LXI	D,ADDOPT
	LDA	DEFOPT
	MOV	B,A
	inr	a
	jz	opt4a	; special flag
	CALL	INSX
	JMP	OPT
opt4a:	lhld	addopt		; LOAD address
	call	setload		; set pointers for this load address
	jmp	OPT
;
CONS:	PUSH	H
	PUSH	D
CONS1:	mvi	c,cstat
	CALL	Bdos
	ORA	A
	JZ	CONS1
	mvi	c,cin
	CALL	Bdos
	POP	D
	POP	H
	cpi	CR
	RZ		; CF=Z if <RETURN>
UNHEX:	ANI	07FH
	MOV	C,A
	CPI	'A'
	JNC	ATOF
	SUI	'0'
	RC		; CF=C if not numeric
	CPI	10
	CMC	 ; Compliment carry flag
;-	rc		; CF=C if not numeric
	RET		; CF=NZ if numeric

ATOF:	ANI	05FH
	MOV	C,A
	SUI	'A'
	RC
	ADI	10
	CPI	16
	CMC	 ; Compliment carry flag
	RET

;--------------------------------------------------------------
;	First pass through program to find all branch locations
;--------------------------------------------------------------
PASS1:	lxi	d,msgp1
	mvi	c,CMSG
	call	bdos

	SUB	A
	STA	COUNT	; Set output buffer to zero

	CALL	initmap	; Initialise START table

	LXI	D,addhelp
	CALL	DSPLY	; INFO re ENTRY points
	CALL	RDMAP	; Read .MAP file if it exists
	CALL	ADDBR	; Add ENTRY points to force program type code
DO1:
	LHLD	START
LOOP:
	MOV	A,M
	CPI	0	; Check if END of MAP
	jz	pass1a
	ANI	01H	; Branch contains unresolved INSTRUCTION
	JNZ	NEXT
	INX	H
	INX	H
	INX	H
	JMP	LOOP
NEXT:	CALL	CODE
	JMP	DO1
;----------------------------------------------------------------------
;	repeat pass through program to find any program table locations
;----------------------------------------------------------------------
PASS1a:	LHLD	START
LOOP1a:	MOV	A,M
	CPI	0	; Check if END of MAP
	rz		; -yes- completed 2nd pass
;-----	ANI	004H	; Start of table of program pointers
;-----	JNZ	next1a
	cpi	044h	; Start of table of program pointers
	jz	next1a
	cpi	024h	; next unresolved entry in table
	jz	next1a
	INX	H
	INX	H
	INX	H
	JMP	LOOP1a
next1a:	CALL	POINT
	JMP	DO1	; Re-run pass1 for new branch points
;---------------------------------------------------
;	Second pass through program to write to file
;---------------------------------------------------
BEGIN:
	DB	TAB,'.z80',CR,LF
	DB	TAB,'aseg',CR,LF
	db	'BASE',tab,'EQU',tab,'$',cr,lf
	DB	TAB,'cseg',CR,LF
	db	0
PASS2:	lxi	d,msgp2
	mvi	c,CMSG
	call	bdos
	SUB	A
	STA	COUNT	; Set output buffer to zero
	LHLD	START	; Table Pointer
	PUSH	H
	LXI	H,BEGIN
	CALL	STRING
	POP	H
	jmp	do2
end2:	LXI	H,endmsg
	CALL	STRING
	ret
endmsg:	DB	CR,LF
	db	tab,'END',cr,lf
	db	0

;
DO2:	MOV	A,M	; 1st byte is type of MAP address
	CPI	0
	jz	end2	; -DONE-
	INX	H	; Table Pointer set to absolute program ADDRESS
;----------------------------------
;	Test if INSTRUCTION or DATA
;----------------------------------
	CPI	080H	; INSTRUCTION
	JZ	INSTR
	CPI	082H	; Pre-defined INSTRUCTIONS
	JZ	INSTR	; was UDFI
	CPI	084H	; INSTRUCTION without label
	JZ	nlbl
	CPI	086H	; INSTRUCTION
	JZ	INSTR
	CPI	088H	; INSTRUCTION
	JZ	INSTR
	CPI	08aH	; INSTRUCTION
	JZ	INSTR

	CPI	040H	; DATA BYTE
	JZ	DATB
	CPI	048H	; DATA WORD
	JZ	DATW
	CPI	04AH	; DATA WORD
	JZ	DATT
	CPI	04cH	; Unlabeled data byte
	JZ	DATBS
	CPI	04eH	; unlabeled data word
	JZ	DATWS

	CPI	020H	; Unused location
	JZ	SPARE

	CPI	010H	; Equate byte
	JZ	EQUB
	CPI	018H	; Equate address
	JZ	EQUW
	CPI	01AH	; Call/Jump address outside program
	JZ	EQUC
	CPI	008H	; End of COM file
	JZ	PREND
	CALL	QUERY
	JMP	DO2
UDFI:	CALL	UDFD	; Write Undefined label to file from address in M
	JMP	INS0
nlbl:	call	space	; instruction without lable
	jmp	ins0
INSTR:	CALL	LABEL	; Write label to file from address in M
INS0:	CALL	PROG	; Add OFSET to absolute program address in <DE>
INS2:	LDAX	D	; Read Instruction
	INX	D
	CALL	MNEM	; Write mnemonics to file and operand if any
;			If the instruction is a RET or JMP, then
;			DATA flag is non zero
	JZ	INS9	; error

	lda	DATA	; MAP address = program pointer
	ora	a	; Check if last instruction JMP or RET
	jnz	DATC	; -YES-
;			<DE> is set to next instruction
INS1:	CALL	TEST
	JC	INS8
	JNZ	INS4	; MAP address < program pointer
;-----	LDA	DATA	; MAP address = program pointer
;-----	ORA	A	; Check if last instruction JMP or RET
;-----	JNZ	DO2	; YES
	MOV	A,M	; Test next type
	CPI	080H	; Another instruction label
	JZ	DO2
	CPI	082H	; Another instruction label
	JZ	DO2
	cpi	084H	; INSTRUCTION without label
	jz	do2
	cpi	086H	; CALL INSTRUCTION with DB
	jz	do2
	cpi	088H	; CALL INSTRUCTION with DW
	jz	do2
	cpi	08aH	; CALL INSTRUCTION with '..',0
	jz	do2

	CPI	04cH	; Unlabeled data byte following CALL
	JZ	do2
	CPI	04eH	; unlabeled data word following CALL
	JZ	do2

	CPI	020H	; Unused location
	JZ	INS3
	CPI	008H	; End of COM file
	JZ	INS6
	CPI	018H	; EQU word (it should never be EQU byte)
	JZ	INSW
	MOV	B,A
	ANI	040H
	MOV	a,b
	JNZ	INS3	; DB or DW
	CALL	QUERY
	JMP	DO2
INS3:	CALL	WARN	; 'Should be WARN:	Data reference within program
;-----	JMP	INSW	; Write data reference as EQU
	PUSH	H
	PUSH	D
	INX	H
	XCHG
	CALL	WORD
	call	equp
	POP	D
	POP	H
	JMP	INS7
INS4:
;-----	LDA	DATA	; Check instruction type
;-----	ORA	A	; Test if RET or JMP
;-----	JZ	INS5	; -no-
;-----	CALL	QUERY
;-----	JMP	DO2
INS5:	MOV	A,M	; Check type
	CPI	018H	; EQU
	JZ	INSW
	CPI	008H	; NULL
	JZ	INS7
	CPI	020H
	JZ	INS7
	CPI	000H	; END
	JZ	INS6
	CALL	QUERY
	JMP	INSW
INS6:	CALL QUERY
	JMP	DO2
INSW:	PUSH	H
	PUSH	D
	INX	H
	XCHG
	PUSH	D
	CALL	WORD
	POP	D
	CALL	EQUAL
	MVI A,'0'
	CALL	CHAR
	CALL	CONST
	CALL	CRLF
	POP	D
	POP	H
INS7:	INX	H
	INX	H
	INX	H
	JMP	INS1
;
INS8:	CALL	SPACE	; Write SPACE to file
	JMP	INS2
;
INS9:	DCX	D	; Non Z80 instruction
	CALL	SPACE
	JMP	DAT2
;
PROG:	MOV	E,M	; Move address at location M to <DE>
	INX	H
	MOV	D,M	; <DE> is absolute program pointer
	INX	H	; H is now pointing to next entry in MAP
	PUSH	H
	lhld	OFSET	; ofset
	DAD	D
	XCHG		; <DE> is OFSET to actual program location
	POP	H
	RET
;
TEST:	PUSH	H
	INX	H
	MOV	C,M	; Read next MAP address into B,C
	INX	H
	MOV	B,M
	lhld	OFSET	; ofset
	DAD	B
	MOV	B,H
	MOV	C,L	; B,C is MAP address + OFSET
	POP	H
TEST1:	MOV	A,D	; Compare with position of program pointer
	CMP	B
	MOV	A,E
	RC		; MAP address > program pointer
	RNZ		; MAP address < program pointer
	CMP	C
	RET
;
DATW1:	DB	'DW',TAB,'0',0
DATB1:	DB	'DB',TAB,0
DATT1:	DB	'DW',TAB,'X',0
;
DATBS:	call	SPACE	; No label, write TAB to file
	jmp	DAT1
DATB:	CALL	LABEL	; Write label to file from address in M
DAT1:	CALL	PROG	; Add OFSET to absolute program address in <DE>
DAT2:	CALL	TEST
DAT3:	PUSH	H	; Save next branch location
	PUSH	D	; Save program pointer
	CALL	ZERO	; Test if Data is zero and <HL> returns byte count
	POP	D
	JNZ	DAT4
	SUB	A
	ADD	H
	JNZ	SPR2
	ADD	L
	DCR	A
	JNZ	SPR2	; Data is all zero
DAT4:	POP	H
	PUSH	H
	LXI	H,DATB1	; Write 'DB 0..H' to file
	CALL	STRING
	POP	H
	CALL	VALUE	; Write byte as hex text to file
;			<DE> is set to next instruction
	CALL	CRLF
DATC:	CALL	TEST
	JC	DAT6
	JZ	DO2	; MAP address = program pointer
DAT5:	CALL	QUERY	; MAP address < program pointer
	JMP	DO2
DAT6:	CALL	SPACE	; Write TAB to file
	JMP	DAT3
;
DATWS:	call	SPACE	; No label, write TAB to file
	jmp	DATWP
DATW:	CALL	LABEL	; Write label to file from address in M
DATWP:	CALL	PROG	; Add OFSET to absolute program address in <DE>
	INX	D
	CALL	TEST	; Check if next byte is not in table
	DCX	D
	JZ	DAT3	; YES therefore write as DB
	PUSH	H
	LXI	H,DATW1	; Write 'DW 0....H' to file
	CALL	STRING
	POP	H
	CALL	CONST	; Write byte as hex text to file
;			<DE> is set to next instruction
	CALL	CRLF
	CALL	TEST
	CNZ	QUERY
	JMP	DO2
;
DATT:	CALL	LABEL	; Write label to file from address in M
	CALL	PROG	; Add OFSET to absolute program address in <DE>
DAT7:	INX	D
	CALL	TEST	; Check if next byte is not in table
	DCX	D
	JZ	DAT3	; YES therefore write as DB
	PUSH	H
	LXI	H,DATT1	; Write 'DW X....H' to file
	CALL	STRING
	POP	H
	CALL	CONST	; Write byte as hex text to file
;			<DE> is set to next instruction
	CALL	CRLF
	CALL	TEST
	JC	DAT9
	JNZ	DAT5
	MOV	A,M
	CPI	024H
	JZ	DAT8
	CPI	02AH
	JZ	DAT8
	JMP	DO2
DAT8:	INX	H
	INX	H
	INX	H
DAT9:	CALL	SPACE
	JMP	DAT7
;
SPARE:	CALL	UDFD
	CALL	PROG
	CALL	TEST
	PUSH	H	; Save next branch location
	PUSH	D	; Save program pointer
	CALL	ZERO
	POP	D
	JNZ	SPR4	; Data is non zero
SPR2:	MVI	A,'D'
	CALL	CHAR
	MVI	A,'S'
	CALL	CHAR
	MVI	A,TAB
	CALL	CHAR
	MOV	A,H
	CALL	DIGIT
	MOV	A,L
	CALL	DIGIT
	MVI	A,'H'
	CALL	CHAR
	CALL	CRLF
	POP	H
	JMP	DO2
SPR4:	SUB	A
	MOV	L,A
	MOV	H,A
	MVI	A,'D'
	CALL	CHAR
	MVI	A,'B'
	CALL	CHAR
	MVI	A,TAB
	CALL	CHAR
SPR5:	MVI	A,'0'
	CALL	CHAR
	CALL	HEX
	MVI	A,'H'
	CALL	CHAR
	CALL	TEST1
	JZ	SPR7
	JNC	ERROR	; ERROR
	INX	H
	MOV	A,L
	CPI	16
	JZ	SPR6
	MVI	A,','
	CALL	CHAR
	JMP	SPR5
SPR6:	CALL	CRLF
	CALL	SPACE
	JMP	SPR4
SPR7:	CALL	CRLF
	POP	H
	JMP	DO2
ZERO:	MVI	L,1
	MVI	H,0	; Initialise count of spare area
ZER1:	LDAX	D
	ORA	A
	RNZ		; Data is non zero
	INX	D
	CALL	TEST1
	RZ
	INX	H
	JC	ZER1
	JMP	ERROR
;
EQUC:	PUSH	H
	XCHG
	PUSH	D
	CALL	WORD
	POP	D
	CALL	BASE
	MVI	A,'0'
	CALL	CHAR
	CALL	CONST
	CALL	CRLF
	POP	H
	JMP	NULL
;
EQUW:	PUSH	H
	XCHG
	PUSH	D
	CALL	WORD
	POP	D
	CALL	EQUAL
	MVI	A,'0'
	CALL	CHAR
	CALL	CONST
	CALL	CRLF
	POP	H
	JMP	NULL
;
EQUB:	PUSH	H
	XCHG
	PUSH	D
	CALL	HALF
	POP	D
	CALL	EQUAL
	MVI	A,'0'
	CALL	CHAR
	CALL	VALUE
	CALL	CRLF
	POP	H
;
NULL:	INX	H
	INX	H
	JMP	DO2
;
PREND1:	DB	'ORG',TAB,'$',CR,LF,0
;
PREND:	CALL	LABEL
	PUSH	H
	LXI	H,PREND1
	CALL	STRING
	POP	H
	JMP	NULL
;
BASE1:	DB	'BASE+',0
;
BASE:	CALL	EQUAL
	PUSH	H
	LXI	H,BASE1
	CALL	STRING
	POP	H
	RET
;
EQUAL1:	DB	TAB,'EQU',TAB,0
;
EQUAL:	PUSH	H
	LXI	H,EQUAL1
	CALL	STRING
	POP	H
	RET
;
equp1:	DB	TAB,'EQU',TAB,'$',CR,LF,0
;
equp:	PUSH	H
	LXI	H,equp1
	CALL	STRING
	POP	H
	RET

;
; PASS2	Subroutine to write label to file
;
UDFD:	MVI	A,'U'
	JMP	LBL
;
LABEL:	MVI	A,'X'
LBL:	CALL	CHAR
	PUSH	D
	MOV	D,H
	MOV	E,L
	CALL	CONST
	POP	D
	MVI	A,':'
	CALL	CHAR
	CALL	SPACE
	RET
;------------------------------------------------------
;	Third pass through program to write to MAP file
;------------------------------------------------------
PASS3:	lxi	d,msgp3
	mvi	c,CMSG
	call	bdos

	SUB	A
	STA	COUNT	; Set output buffer to zero
	LHLD	START	; Table Pointer
;
DO3:	MOV	A,M	; 1st byte is type of MAP address
	CPI	0
	rz		; -DONE-
	inx	h	; <HL> -> absolute address
;------------------------------------
;	Test for suitable ENTRY POINT
;------------------------------------
	cpi	080H	; 'P' MAP point
	jz	map$p
	cpi	082H	; 'P' MAP point
	jz	map$pl
	cpi	08aH	; 'S' MAP point
	jz	map$s
	cpi	040H	; 'B' MAP point
	jz	map$b
	cpi	048H	; 'W' MAP point
	jz	map$w
	cpi	04aH	; 'T' MAP point
	jz	map$t
nxt03:	inx	h
	inx	h
	jmp	do3

map$pl:	; 'P' MAP point or 'L' MAP point
	push	d
	push	h
	lhld	load	; load address
	xchg
	pop	h
	mov	a,m
	cmp	e
	jnz	not$l
	inx	h
	mov	a,m
	dcx	h
	cmp	d
	jnz	not$l
	pop	d
	jmp	map$l
not$l:	pop	d

map$p:	mvi	a,'P'
	jmp	mapitem

map$l:	; 'L' MAP point
	mvi	a,'L'
	jmp	mapitem


map$s:	; 'S' MAP point
	mvi	a,'S'
	jmp	mapitem


map$b:	; 'B' MAP point
	mvi	a,'B'
	jmp	mapitem

map$w:	; 'W' MAP point
	mvi	a,'W'
	jmp	mapitem

map$t:	; 'T' MAP point
	mvi	a,'T'
	jmp	mapitem

mapitem:
	push	psw
; write address '0000 '
	push	d
	mov	d,h
	mov	e,l
	inx	d
	call	hex	; HIGH byte
	dcx	d
	dcx	d
	call	hex	; LOW byte
	pop	d
	mvi	a,' '
	call	char
; write type '/?/'
	mvi	a,'/'
	call	char
	pop	psw
	call	char
	mvi	a,'/'
	call	char
; terminate line
	mvi	a,cr
	call	char
	mvi	a,lf
	call	char

	jmp	nxt03

;-----------------------------------------
; pass1a Subroutines to decode instructions
;-----------------------------------------
CODE:
	MOV	A,M
	ANI	00EH	; Clear unresolved bit
	ORI	080H	; Mark MAP type type as resolved
	MOV	M,A
	INX	H
	CALL	PROG	; Add OFSET to absolute program address in <DE>
OPER:
	push	h
	lhld	start	; check if >= top of program
	call	insx3	; cf -> <DE> - <HL>
	pop	h
	rnc		; -YES- so stop decoding

	PUSH	H
	LDAX	D
	INX	D	; Read instruction
	LXI	H,SETS
	call	getset	; return <A>,<B> = operand type
	POP	H
	cpi	07h
	cz	moper	; multi byte operand

	mov	a,b
	ANI	0FH	; Check valid instruction
	RZ	 	; Non Z80 instruction
	MOV	A,B
	CPI	01H	; Single byte non branch instruction
	JZ	OPER
	CPI	13H
	JZ	OPER2
	CPI	23H
	JZ	OPER3
	CPI	31H
	JZ	OPER4
	CPI	42H
	JZ	OPER5
	CPI	0c3h	; dual byte of data
	JZ	OPER5b
	CPI	43H
	JZ	OPER6
	CPI	53H
	JZ	OPER7
	CPI	92H	; conditional branch relative
	JZ	OPER2r
	CPI	0a2H	; unconditional branch relative
	JZ	OPER3r
	CPI	83H	; CALL (may be followed by data)
	JZ	OPER1
;
; ERROR in operand type
;
	lxi	d,pass1err
	mvi	c,cmsg
	call	bdos
	jmp	abort
pass1err:
	db	'DISZ80: PASS1 Program error',cr,lf,'$'

;-----------------------------------------
;			MULTI BYTE OPERAND
;-----------------------------------------
moper:	push	h
	lxi	h,tble2
moper0:	mov	a,m
	cmp	c
	jz	moper1
	ora	a
	jz	moper2	; fatal program error
	inx	h
	inx	h
	inx	h
	jmp	moper0
moper1:	inx	h
	mov	a,m
	inx	h
	mov	h,m
	mov	l,a	; <HL> -> second operand process
	call	jphl
	mov	a,b
moper2:	pop	h
	ret

jphl:	pchl

;------------------------------;
; table of multi byte operands ;
;------------------------------;
tble2:	db	0cbh
	dw	m2$cb
	db	0ddh
	dw	m2$dd
	db	0edh
	dw	m2$ed
	db	0fdh
	dw	m2$fd
	db	0	; end of table

m2$cb:
	ldax	d	; 2nd byte of operand
	inx	d	; Read instruction
	lxi	h,set$cb
	call	getset	; return <A> & <B> = op type
	ret

m2$dd:
	ldax	d	; 2nd byte of operand
	inx	d	; Read instruction
	lxi	h,sets$dd	; Table of instruction
	call	cmpset
	mov	a,b
	cpi	07h	; test for multi byte operand
	rnz
	mov	a,c
	cpi	0cbh	; special 2.1 operand
	jz	m3$dd
	mvi	b,0	; ERROR
	ret

m3$dd:			; 0ddh,03bh,disp,?
	inx	d	; skip displacement to 4th byte of instruction
	ldax	d	; Read 3rd byte of operand
	dcx	d
	lxi	h,set$ddcb	; Table of instruction
	call	cmpset	; Low <B> = byte count, <HL> -> mnemonic text
	ret

m2$ed:
	ldax	d	; 2nd byte of operand
	inx	d	; Read instruction
	lxi	h,sets$ed	; Table of instruction
	call	cmpset	; Low <B> = byte count, <HL> -> mnemonic text
	ret

m2$fd:
	ldax	d	; 2nd byte of operand
	inx	d	; Read instruction
	lxi	h,sets$fd	; Table of instruction
	call	cmpset	; Low <B> = byte count, <HL> -> mnemonic text
	mov	a,b
	cpi	07h	; test for multi byte operand
	rnz
	mov	a,c
	cpi	0cbh	; special 2.1 operand
	jz	m3$fd
	mvi	b,0	; ERROR
	ret

m3$fd:			; 0fdh,03bh,disp,?
	inx	d	; skip displacement to 4th byte of instruction
	ldax	d	; Read 3rd byte of operand
	dcx	d
	lxi	h,set$fdcb	; Table of instruction
	call	cmpset	; Low <B> = byte count, <HL> -> mnemonic text
	ret

;-----------------------------------------------;
; search table for op code in <C>		;
; on entry:	<a>     = op code		;
;		<hl>    -> start of table	;
; returns:	<a>,<b> = op type		;
;		<hl>    = address of mnemonics	;
;		<c>	= op code		;
; preserved:	<de>				;
;-----------------------------------------------;

cmpset:	mov	c,a	; save op code to find
cset1:	mov	a,m	; OP CODE
	inx	h
	mov	b,m	; High nibble = type, Low nibble = byte count
	inx	h	; (HL) -> mnemonic text
	cmp	c
	jz	fndset
	ora	a
	rz		; error - not Z80 multi byte instruction
	inx	h
	inx	h	; skip pointer
	jmp	cset1
fndset:	mov	a,m
	inx	h
	mov	h,m
	mov	l,a	; <hl> = mnemonic
	mov	a,b	; <a> & <b> = op code type
	ret

;-----------------------------------------------;
; fetch op type and mnemonic from set table	;
; on entry:	<a>     = op code		;
;		<hl>    -> start of table	;
; returns:	<a>,<b> = op type		;
;		<c>	= op code		;
;		<hl>    = address of mnemonics	;
; preserved:	<de>				;
;-----------------------------------------------;

getset:	mov	c,a
	mvi	b,0
	dad	b
	dad	b
	dad	b	; <hl> -> 3 byte paramter
	mov	b,m	; High nibble = type, Low nibble = byte count
	inx	h	; (HL) -> mnemonic text
	mov	a,m
	inx	h
	mov	h,m
	mov	l,a	; <hl> = mnemonic
	mov	a,b	; <a> & <b> = op code type
	ret

;---------------------------------------
;			CALL INSTRUCTION
;---------------------------------------
OPER1:	MVI	B,01
	CALL	INSX
	mov	a,b
	cpi	007h
	jz	oper1a	; call with data byte
	cpi	009h
	jz	oper1b	; call with data word
	cpi	00bh
	jz	oper1c	; call with data string terminated with 0h
	cpi	086h
	jz	oper1a	; call with data byte
	cpi	088h
	jz	oper1b	; call with data word
	cpi	08ah
	jz	oper1c	; call with data string terminated with 0h
	JMP	OPER

oper1a:			; call with data byte
	mvi	b,04ch
	call	inso	; next byte is data byte
	push	d
	mvi	b,005h
	inx	d
	call	inso	; next byte is code
	pop	d
	ret

oper1b:	; call with data word
	mvi	b,04eh
	call	inso	; next byte is data word
	push	d
	inx	d
	inx	d
	mvi	b,005h
	call	inso	; followed by code
	pop	d
	ret

oper1c:	; call with data string terminated with 0h
	mvi	b,04ch
	call	inso	; next byte is data byte (s)
	push	d
oper1d:	ldax	d
	inx	d
	ora	a	; is it zero
	jnz	oper1d	; -no-
	mvi	b,005h
	call	inso	; followed by code
	pop	d
	ret

;-----------------------------------------
;			CONDITIONAL BRANCH
;-----------------------------------------
OPER2:	MVI	B,01
	CALL	INSX
	JMP	OPER
;--------------------------------------------------
;			CONDITIONAL BRANCH RELATIVE
;--------------------------------------------------
OPER2r:
	MVI	B,01
	CALL	INSR
	JMP	OPER
;----------------------------------------------------
;			UNCONDITIONAL BRANCH RELATIVE
;----------------------------------------------------
OPER3r:
	MVI	B,01
	CALL	INSR
	JMP	OPER4
;-------------------------------------------
;			UNCONDITIONAL BRANCH
;-------------------------------------------
OPER3:	MVI	B,01
	CALL	INSX
;---------------------------------------
;			RETURN (or HALT)
;---------------------------------------
OPER4:	MVI	B,20H
	CALL	INSO
	RET
;---------------------------------------
;			Single BYTE data
;---------------------------------------
OPER5:
;-----	MVI	B,10H
;-----	LDAX	D
	INX	D
;-----	CPI	10
;-----	JC	OPER	; Values less than 10 are printed as numeric
;-----	PUSH	D
;-----	MOV	E,A
;-----	MVI	D,0
;-----	CALL	INSERT
;-----	POP	D
	JMP	OPER
;-------------------------------------
;			Dual BYTE data
;-------------------------------------
OPER5b:	INX	D
	INX	D
	JMP	OPER
;--------------------------------
;			Data word
;--------------------------------
OPER6:	MVI	B,40H
	CALL	INSX
	JMP	OPER
;
PAD:	DS	2
;-----------------------------------
;			Data address
;-----------------------------------
OPER7:
;	JMP	OPER8
	PUSH	H	; Save <HL> & <DE>
	PUSH	D
	LDAX	D
	MOV	L,A
	INX	D
	LDAX	D
	MOV	H,A
	INX	H
	INX	H
	SHLD	PAD
	LXI	D,PAD
	MVI	B,20H
	CALL	INSX	; (<DE> + 2) marked as No reference to location
	POP	D
	POP	H
	jmp	oper8
;-----------------------------------
;		CALL	Data address
;-----------------------------------
OPER7a:
	PUSH	H	; Save <HL> & <DE>
	PUSH	D
	LDAX	D
	MOV	L,A
	INX	D
	LDAX	D
	MOV	H,A
	INX	H
	INX	H
	SHLD	PAD
	LXI	D,PAD
	MVI	B,20H
	CALL	INSX	; (<DE> + 2) marked as No reference to location
	POP	D
	POP	H
;--------check for data following call statement----------------
	jmp	oper8
;----------------------------------------------
;			and (<DE>) marked as DW
;----------------------------------------------
OPER8:	MVI	B,48H
	CALL	INSX
	JMP	OPER
;
POINT:
	MOV	A,M
	ANI	0FBH	; Clear unresolved bit
	ORI	00AH	; Mark MAP as resolved
	MOV	M,A
	INX	H
	CALL	PROG	; Add OFSET to absolute program address in <DE>
	MVI	B,01
	CALL	INSX
	MVI	B,24H
	CALL	INSO
	RET
;
INSX:		; Put address at (<DE>) into table
	PUSH	H
	PUSH	D	; But first check if address within program
	XCHG
	MOV	E,M
	INX	H
	MOV	D,M
	LXI	H,CPM
	CALL	INSX3
	JC	INSX1
	LHLD	FREE	; FREE contains first absolute location after program
	XCHG
	CALL	INSX3
	xchg
	JNC	INSX2
INSX1:	MOV	A,B
	MVI	B,1AH	; Address outside range, thus made an EQU
	CPI	20H	; Unless 020H type, then ignored!
	JZ	INSX5
INSX2:	CALL	INSERT	; insert <DE> into table with mode <B>
INSX5:	POP	D
	INX	D
	INX	D
	POP	H
	RET
;
INSX3:	MOV	A,D	; returns flag set for <DE> - <HL>
	CMP	H
	RNZ
	MOV	A,E
	CMP	L
	RET
;
INSR:		; Put address based on offset at (<DE>) into table
	PUSH	H
	ldax	d	; offset
	inx	d	; move to next OP code
	PUSH	D
	push	psw
	lhld	OFSET	; ofset
	call	sdehl	; <HL> = <DE> - <HL>
;	<HL> now contains absolute program address

	pop	psw
	mov	e,a	; offset
	rlc
	mvi	a,0
	sbb	a	; <A> = 0 for plus, or 0ffh for minus
	mov	d,a
	dad	d
	xchg		; <DE> now contains jump address

	LXI	H,CPM	; But first check if address within program
	CALL	INSX3
	JC	INSR1
	LHLD	FREE	; FREE contains first absolute location after program
	XCHG
	CALL	INSX3
	XCHG
	JNC	INSR2
INSR1:			; outside program range
	MOV	A,B
	MVI	B,1AH	; Address outside range, thus made an EQU
	CPI	20H	; Unless 020H type, then ignored!
	JZ	INSR5
INSR2:	CALL	INSERT	; insert <DE> into table with mode <B>
INSR5:	pop	D
	POP	H
	RET
;
;
INSO:	PUSH	H	; Insert absolute program pointer to table
	lhld	OFSET	; ofset
	PUSH	D
	call	sdehl	; <HL> = <DE> - <HL>
	xchg		;	<DE> now contains absolute program address
	CALL	INSERT
	POP	D
	POP	H
	RET
;
INSERT:
	PUSH	D
	PUSH	H
	PUSH	B
	LHLD	START
IRT1:	MOV	A,M	; Read MAP type
	INX	H
	MOV	C,M
	INX	H
	MOV	B,M	; Read MAP address
	INX	H
	CPI	0	; Test for END of table
	JZ	IRT2
	MOV	A,D	; Test if MAP address = INSERT address
	CMP	B
	JC	IRT2	; INSERT < MAP
	JNZ	IRT1	; INSERT > MAP
	MOV	A,E
	CMP	C
	JZ	IRT3	; INSERT = MAP
	JNC	IRT1	; INSERT > MAP
;
IRT2:	DCX	H	; Copy INSERT entry into current entry of MAP
	MOV	M,D
	DCX	H
	MOV	M,E
	DCX	H
	MOV	D,B
	MOV	E,C	; Previous MAP address moved to <DE>
	POP	B	; Recover INSERT type
	SUB	A
	CMP	B	; Check if END of MAP
	MOV	A,M	; Previous MAP type is saved in A
	MOV	M,B
	JZ	IRT8
	MOV	B,A
	PUSH	B	; then put into stack pointer for B
	INX	H
	INX	H
	INX	H
	INX	H
	MOV	C,M	; Next MAP address is saved in B,C
	INX	H
	MOV	B,M
	INX	H
;	CPI	0
;	CZ	INS9	; Check if new MAP location is not corrupting program
	JMP	IRT2
;
IRT3:	DCX	H	; INSERT = MAP
	DCX	H
	DCX	H
	MOV	A,M	; MAP	type
	POP	B
	MOV	C,A	; MAP type saved in <C>
	CMP	B	; Compared with INSERT type
	JZ	IRT8	; SAME

	ANI	010H	; Test if MAP type or INSERT is EQU
	JNZ	IRT9	; MAP type is EQU

	mov	a,b
	ani	010h
	JNZ	IRT9	; INSERT is EQU

	MOV	A,B
	ANI	020H
	JNZ	IRT8	; INSERT is NO REFERENCE TO LOCATION

	MOV	A,C	; test MAP type
	CPI	020H
	JZ	IRT4
	CPI	024H
	JZ	IRT4
	CPI	02AH
	JZ	IRT10

	CPI	080H
	JZ	IRT5
	CPI	082H
	JZ	IRT5
	CPI	084H	; unlabeled instruction
	JZ	IRT5a
	CPI	086H	; call with data byte
	JZ	IRT5b
	CPI	088H	; call with data word
	JZ	IRT5b
	CPI	08aH	; call with string
	JZ	IRT5b

	mov	a,c
	ani	040h
	jnz	IRT7	; data types

	mov	a,c
	CPI	010H
	JZ	IRT9	; MAP type is byte EQU
	CPI	018H
	JZ	IRT4
	CPI	01AH
	JZ	IRT9

	CPI	008H
	JZ	IRT4

	CPI	001H
	JZ	IRT6
	CPI	003H
	JZ	IRT6
	CPI	005H	; unlabelled instruction continues
	JZ	IRT6a
	CPI	007H	; CALL with Data byte
	JZ	IRT6
	CPI	009H	; CALL with Data word
	JZ	IRT6
	CPI	00bH	; CALL with Data string
	JZ	IRT6

	JMP	ERROR
IRT4:
	MOV	M,B	; Update MAP type to INSERT type
	JMP	IRT8
IRT4a:			; resolve MAP type
	mov	a,b
	ani	00eh
	ori	080h
	mov	m,a
	jmp	IRT8
IRT5:			; MAP type is already RESOLVED
	MOV	A,B
	CPI	001H
	JZ	IRT8
	CPI	003H
	JZ	IRT8
	cpi	005h
	jz	irt8
	CPI	080H
	JZ	IRT8
	CPI	082H
	JZ	IRT4
	CPI	084H
	JZ	IRT8

	CPI	044H	; Perhaps print WARNING !
	JZ	IRT4
	CPI	04AH	; Perhaps print WARNING !
	JZ	IRT4
	MOV	A,B
	ani	40h	; any other data
	jnz	IRT8	; Perhaps print WARNING !

	JMP	ERROR
irt5a:			; resolved unlabeled instruction
	MOV	A,B
	ani	80h	; resolved instruction
	jnz	IRT4

	mov	a,b
	ani	001h	; unresolved instruction
	jnz	IRT4a	; now resolved

	CPI	044H	; Perhaps print WARNING !
	JZ	IRT4
	CPI	04AH	; Perhaps print WARNING !
	JZ	IRT4
	MOV	A,B
	ani	40h	; any other data
	jnz	IRT8	; Perhaps print WARNING !

	JMP	ERROR
irt5b:			; MAP type is already RESOLVED call with data
	mov	a,b
	ani	080h
	jnz	IRT8

	mov	A,B
	ani	001h
	jnz	irt8

	mov	A,B
	ani	040h
	jnz	IRT8	; Perhaps print WARNING !

	JMP	ERROR

IRT6:			; MAP type is unresolved
	mov	a,b
	cpi	001h
	jz	irt8
	cpi	005h
	jz	irt8
	cpi	003h
	jz	irt8

	MOV	A,B
	ani	040h	; test for data type
	jnz	IRT8	; Perhaps print WARNING !

	JMP	ERROR
irt6a:			; MAP type is unresolved & unlabelled
	mov	a,b
	cpi	001h
	jz	irt4
	cpi	003h
	jz	irt8
	cpi	005h
	jz	irt8

	MOV	A,B	; MAP type is unresolved
	ani	040h	; test for data type
	jnz	IRT8	; Perhaps print WARNING !

	JMP	ERROR
IRT7:			; MAP type is DATA entry point
	MOV	A,B
	CPI	048H
	JZ	IRT4	; DW has priority over DB
	CPI	040H
	JZ	IRT8
	CPI	044H
	JZ	IRT4
	CPI	04AH
	JZ	IRT4
	CPI	04cH	; unlabeled data byte
	JZ	IRT8
	CPI	04eH	; unlabeled data word
	JZ	IRT8

	CPI	001H
	JZ	IRT4
	CPI	003H
	JZ	IRT4
	cpi	005h
	jz	irt4
	JMP	ERROR
IRT10:			; MAP type is (2a, 44, 4a) DATA MAP type entry point
	MOV	A,B
	ani	040h	; test for data type
	jnz	IRT8

	MOV	A,B
	CPI	001H
	JZ	IRT4
	CPI	003H
	JZ	IRT4
	cpi	005h
	jz	irt4
	JMP	ERROR
IRT8:
	mov	b,m	; return MAP type in <B>
	POP	H
	POP	D
	RET
;
IRT9:	INX	H	; MAP type is EQU BYTE
	INX	H
	INX	H
	PUSH	B
	JMP	IRT1
;------------------------------------------------------------
;			Subroutine to write MNEMONICS to file
;			B contains length of instruction
; Returns CF = NZ if ok, = Z if error
;------------------------------------------------------------
mnemsp:	dw	0	; save stack pointer for error quit
MNEM:
	PUSH	H	; Subroutine to return length in B of instruction in A
	lxi	h,0
	dad	sp
	shld	mnemsp	; save stack

;------------------------------------------
; entry <A> = op code, (SP) -> next byte
; return <HL> -> MNEMONIC, <B> = byte count
;------------------------------------------
	LXI 	H,SETS	; standard set of op codes
	call	getset	; returns <A> & <B> = op type, <HL> -> mnemonic
	cpi	07h	; test op type
	jnz	mnem1	; single byte operand

;-----------------------------------------
;			MULTI BYTE OPERAND
;-----------------------------------------
	lxi	h,tble3
mnem01:	mov	a,m
	cmp	c
	jz	mnem02
	ora	a
	jz	mnemerr	; fatal program error
	inx	h
	inx	h
	inx	h
	jmp	mnem01
mnem02:	inx	h
	mov	a,m
	inx	h
	mov	h,m
	mov	l,a	; <HL> -> second operand process
	ldax	d	; 2nd byte of operand
	inx	d	; Read instruction
	call	jphl	; pchl

;------------------------------------------
; entry: <HL> -> MNEMONIC, <B> = byte count
;------------------------------------------
MNEM1:
; test for unconditional jump or return or PCHL
	MOV	a,b
	ANI 	20H	; Unconditional jump, return, PCHL, or jr
	STA	DATA
; test for zero length - invalid op code
	MOV	a,b
	ANI	0FH
	jz	mnemerr
	mov	b,a	; <B> = length of instruction (1, 2 or 3 bytes)
; print mnemonic scanning for ? indicating data
	MVI	C,16	; <HL> points to start of MNEMONICS string
MNEM2:	MOV	A,M	; Write up to 16 characters to file
	CPI	0	; Each MNEMONIC ends with a zero
	JZ	mnem4
	CPI	'?'	; ? denotes address or offset goes here
	jz	mnem5
	CPI	'!'	; ! denotes extra instruction byte to be ignored
	jz	mnem11
	CPI	'@'	; @ denotes byte relative address
	jz	mnem12
	CALL	CHAR
mnem3:			; increment to next bytes
	INX	H
	DCR	C	; unless done 16 bytes
	JNZ	MNEM2
mnem4:
	dcr	b	; check <B> reached zero
	jnz	mnemerr	; wrong number of operands
	CALL	CRLF

	POP	H
	ori	-1	; return ok
	RET

;---------------------------------
; found a '?' in the mnemonic text
;---------------------------------
mnem5:	inx	h
	dcr	c
	jz	mnem6
	mov	a,m
	cpi	'?'	; test for pointer
	jz	mnem9	; fill ?? with data
mnem6:	dcx	h
	inr	c

; fill ? with data byte

	DCR	B
	jz	mnemerr	; should be 1
	push	b
mnem7:	LDAX	D	; <DE> -> next byte
	CPI	10	; Check if byte value below 10
	JNC	mnem8
	INX	D	 ; YES - Write as constant
	ADI	'0'
	CALL	CHAR
	JMP	mnem10
mnem8:
;------	CALL	BYTE	; write as data label
	call	value	; write as HEX constant
	jmp	mnem10

;----------------------------------
; found a '??' in the mnemonic text
;----------------------------------
mnem9:			; fill ?? with data word
	DCR	B
	jz	mnemerr	; should be 2
	DCR	B
	jz	mnemerr	; should be 2
	push	b
	CALL	WORD	; where <DE> -> next byte
mnem10:	pop	b
	jmp	mnem3

;---------------------------------
; found a '!' in the mnemonic text
;---------------------------------
mnem11:	DCR	B
	jz	mnemerr	; should be 1
	inx	d	 ; skip next instruction byte
	jmp	mnem3

;---------------------------------
; found a '@' in the mnemonic text
;---------------------------------
mnem12:			; fill @ with calculated data word
	DCR	B
	jz	mnemerr	; should be 1
	push	b

	PUSH	H
	ldax	d	; offset
	inx	d	; move to next OP code
	push	psw
	lhld	OFSET	; ofset
	call	sdehl	; <HL> = <DE> - <HL>
;	<HL> now contains absolute program address

	pop	psw
	mov	c,a	; offset
	rlc
	mvi	a,0
	sbb	a	; <A> = 0 for plus, or 0ffh for minus
	mov	b,a
	dad	b	; <HL> = actual address

	MVI	A,'X'
	CALL	CHAR
	mov	a,h
	call	digit	; HIGH byte first
	mov	a,l
	call	digit	; LOW byte next
	MVI	A,'H'
	CALL	CHAR
	pop	h
	pop	b
	jmp	mnem3

; error in mnemonics
mnemerr:
	lhld	mnemsp	; recover stack on entry
	sphl
	lxi	d,progerr
	mvi	c,cmsg
	call	bdos
	call	QUERY	; Zero byte length
	CALL	CRLF
	POP	H
	xra	a	; return error
	RET


progerr:
	db	cr,lf,'DISZ80 - Program error in mnenomic table','$'

savehl:	dw	0
savede:	dw	0

;-------------------------------------------------
; entry <A> = op code, (SP+2) -> next byte
;       <HL> -> OP TYPE, <DE> -> mnemonic table
; return <HL> -> mnemonic string, <A> = op type
;-------------------------------------------------
;-----lookup:	MVI 	b,0
;-----	MOV	c,A	; <BE> contains index
;-----	DAD	b	; <HL> -> OP code type
;-----	MOV	a,M
;-----
;-----	mov	h,b
;-----	mov	l,c
;-----	dad	h
;-----	dad	h
;-----	dad	h
;-----	dad	h	; *16
;-----	dad	d	; <HL> -> standard mnemonics
;-----
;-----	MOV	b,a
;-----	ret

;------------------------------;
; table of multi byte operands ;
;------------------------------;
tble3:	db	0cbh
	dw	mn2$cb
	db	0ddh
	dw	mn2$dd
	db	0edh
	dw	mn2$ed
	db	0fdh
	dw	mn2$fd
	db	0	; end of table

mn2$cb:
	lxi	h,set$cb
	call	getset	; returns <A> & <B> = op type, <HL> -> mnemonic
	ret

mn2$dd:
	lxi	h,sets$dd	; set of 2nd op codes
	call	cmpset	; Low <B> = byte count, <HL> -> mnemonic text
	mov	a,b
	cpi	7	; test for multi byte
	rnz		; -no-
	mov	a,c	; check 2nd operand
	cpi	0cbh	; special 2.1 operand
	jz	mn3$dd
	xra	a	; -no-
	mov	b,a	; set return to zero
	ret		; ERROR

mn3$dd:			; 0ddh,0cbh,disp,?
	inx	d	; skip displacement to 4th byte of instruction
	ldax	d	; Read 3rd byte of operand
	dcx	d
	lxi	h,set$ddcb	; Table of instruction
	call	cmpset	; Low <B> = byte count, <HL> -> mnemonic text
	ret

mn2$ed:
	lxi	h,sets$ed	; set of 2nd op codes
	call	cmpset	; Low <B> = byte count, <HL> -> mnemonic text
	ret

mn2$fd:
	lxi	h,sets$fd	; set of 2nd op codes
	call	cmpset	; Low <B> = byte count, <HL> -> mnemonic text
	mov	a,b
	cpi	7	; test for multi byte
	rnz		; -no-
	mov	a,c	; check 2nd operand
	cpi	0cbh	; special 2.1 operand
	jz	mn3$fd
	xra	a	; -no-
	mov	b,a	; set return to zero
	ret		; ERROR

mn3$fd:			; 0fdh,0cbh,disp,?
	inx	d	; skip displacement to 4th byte of instruction
	ldax	d	; Read 3rd byte of operand
	dcx	d
	lxi	h,set$fdcb	; Table of instruction
	call	cmpset	; Low <B> = byte count, <HL> -> mnemonic text
	ret

;-----------------
; add word address
;-----------------
WORD:	MVI	A,'X'
	CALL	CHAR
CONST:	INX	D	 ; Write 2nd byte first
	CALL	HEX
	DCX	D
	DCX	D
	CALL	HEX
	MVI	A,'H'
	CALL	CHAR
	INX	D
	RET
;
HALF:	MVI	A,'X'
	CALL	CHAR
	CALL	HEX
	MVI	A,'H'
	CALL	CHAR
	LDAX	D
	INX	D
	CPI	0	; Check not data WORD
	JNZ	ERROR
	RET
;
BYTE:	MVI	A,'X'
	jmp	leadb
VALUE:	MVI	A,'0'
leadb:	CALL	CHAR
	CALL	HEX
	PUSH	PSW
	MVI	A,'H'
	CALL	CHAR
	POP	PSW	; Recover data byte
	CPI	' '	; If byte is >= to a space then it is also written
	RC		; as a character string: ;'.'
	CPI	21H	; Unless it is = 021H
	RZ
	CPI	7FH	; or it is => 07FH
	RNC
	MOV	B,A
	MVI	A,';'
	CALL	CHAR
	MVI	A,''''
	CALL	CHAR
	MOV	A,B
	CALL	CHAR
	MVI	A,''''
	CALL	CHAR
	RET
;			Subroutine to write in HEX contents of (<DE>)
;			and increment <DE>
HEX:	LDAX	D
	INX	D
DIGIT:	PUSH	B
	MOV	B,A
	RAR
	RAR
	RAR
	RAR
	CALL	HEX1
	MOV	A,B
	CALL	HEX1
	MOV	A,B
	POP	B
	RET
HEX1:	ANI	0FH
	CPI	10
	JC	HEX2
	ADI	'A'-10
	JMP	HEX3
HEX2:	ADI	'0'
HEX3:	CALL	CHAR
	RET
;
CRLF:	MVI	A,CR
	CALL	CHAR
	MVI	A,LF
	CALL	CHAR
	RET
;			Write 8 charcters to BUFFER
SPACE:	MVI	A,TAB
	CALL	CHAR
	RET
;
STRING:	MOV	A,M	; Write string of characters
	INX	H	; Terminated by a zero
	ORA	A
	RZ
	CALL	CHAR
	JMP	STRING
;			Write character in A to file BUFFER
CHAR:	PUSH	H
	PUSH	D
	PUSH	B
	PUSH	PSW
	MOV	D,A	; Save character in D
	LDA	COUNT	; Number of characters in BUFFER
	LXI	H,dmabuf
	MOV	C,A
	MVI	B,0
	DAD	B	; <HL> = BUFFER + COUNT
	MOV	M,D	; Write character to BUFFER
	INR	C
	MOV	A,C
	STA	COUNT	; Increment counter and save in COUNT
	CM	WRITE	; If BUFFER filled with 128 characters then write to file
	POP	PSW
	POP	B
	POP	D
	POP	H
	RET

;
;Open output file
;
OPENtmp:
	lxi	h,t$tmp
	LXI	d,dfcb+9	; FCB set to M80 type
	lxi	b,3
	call	copy

create:
	call	zrofcb		; intialise FCB to zero

	LXI	D,dfcb
	MVI	C,FDELETE
	CALL	BDOS	;DELETE FILE

	LXI	D,dfcb
	MVI	C,FCREATE
	CALL	BDOS	;CREATE FILE
	CPI	255
	JNZ	SETDMA

	LXI	D,NOSPACE
	MVI	C,CMSG
	CALL	BDOS
	JMP	RETURN	;FILE NOT FOUND
;
SETDMA:	LXI	D,dmabuf
	MVI	C,FDMA
	CALL	BDOS
	RET
;
WRITE:	LXI	D,dfcb
	MVI	C,FWRITE
	CALL	BDOS
	SUB	A
	STA	COUNT
	RET
;
WARN1:	DB	';WARN',TAB,'DB',CR,LF,0
;
WARN:	PUSH	H
	LXI	H,WARN1
	CALL	STRING
	POP	H
	PUSH	D
	PUSH	H
	PUSH	B
	LXI	D,WARNS
	MVI	C,CMSG
	CALL	BDOS
	POP	B
	POP	H
	POP	D
	RET
WARNS:	DB	'! WARN !',CR,LF,'$'
;
QUER0:	DB	'ERROR',CR,LF,0
;
QUERY:	PUSH	H
	LXI	H,QUER0
	CALL	STRING
	POP	H
QUER1:	PUSH	D
	PUSH	H
	PUSH	B
	LXI	D,QUERYS
	MVI	C,CMSG
	CALL	BDOS
	POP	B
	POP	H
	POP	D
	RET
QUERYS:	DB	'! ERROR !',CR,LF,'$'
;
ERROR:	CALL	QUER1
	LDA	COUNT
	CPI	0
	cnz	DONE
	JMP	ABORT

;----------------------------------------;
zrofcb:	; zeroes RC to CR of default FCB
;----------------------------------------;
	lxi	h,dfcb+12
	lxi	d,dfcb+13
	lxi	b,1+(32-9-3)-1
	mvi	m,0
	jmp	copy		; zero remaining bytes

;---------------------------------------------;
copy:	; copies <BC> bytes from (HL) to (DE)
;---------------------------------------------;
	mov	a,b
	ora	c
	rz
	mov	a,m
	stax	d
	inx	h
	inx	d
	dcx	b
	jmp	copy

;-------------------------------------------;
sdehl:	; <HL> = <DE> - <HL> (destroys <A>)
;-------------------------------------------;
	mov	a,e
	sub	l
	mov	l,a
	mov	a,d
	sbb	h
	mov	h,a
	ret

;-------------------------------;
; load program on page boundary ;
;-------------------------------;

lenprog	equ	($-cpm)
bound	equ	(lenprog + 0ffh) and not 0ffh
	ds	bound - lenprog

	END
