*Size 20
*Include CSMDEF
*Include ZEXTDEF

; RTDECODE.GEN : Root instruction decoder - machine code section

;			ZEM  Z80 Emulator and Monitor
;			 Version 1.03   22 June 1986
;
;			(C) 1986 by P. G. Kenny
;				    27 Colwyn Road
;			 	    Northampton
;
;	Permission is hereby granted for all non-commercial
;	reproduction and distribution of this material provided
;	this notice is included.


; This file contains the function for instruction decoding.

; Global function :

; 	decode(addr)		- fetch and decode instruction


; -------------------------------- Directory -------------------------------

	dirent	decode DECOD E
	enddir

; ----------------------------  MODULES  FOLLOW  ------------------------------

; ------------------------------ Module DECODE --------------------------------

; decode(addr)				/* decode instruction at address */
; address addr;				/* return 0 if bad instruction */
; {
; #define	GETB1	(sb1 = sget(addr), ib1 = mget(addr))
; #define	GETB2	(sb2 = sget(addr+1), ib2 = mget(addr+1))
; #define	GETB3	(sb3 = sget(addr+2), ib3 = mget(addr+2))
; #define	GETB4	(sb4 = sget(addr+3), ib4 = mget(addr+3))
; #define	ILEN	(gtimlen_tab[igroup] & 7)
;
; 	byte igroup,			/* instruction group number */
; 	     gval;			/* value in group table */

	module	decode
	needf	decode mcget MCGE T ; external function needed
	body	decode

;  ***	fetch address

	push	bc		; save bc
	ld	hl, 4		; offset to first arg on stack
	add	hl, sp		; make pointer
	ld	e, (hl)		; load into de
	inc	hl
	ld	d, (hl)		; de = addr
	ld	(dec_addr), de	; save it
	rel	decode 1

;  ***	look up group - first try 8080 class

; 	if (igroup = gr8080[GETB1]) {	/* 8080 class */
;	    iselb = ib1;
; 	    switch (ILEN) {		/* these have unique first byte */
; 	    case 3 : idb2 = GETB3;	/* get 2nd and 3rd bytes if used */
; 	    case 2 : idb1 = GETB2;	/* never 4 bytes for this class */
;	    }
;	}

	call	dec_gb1		; a = ib1 = first byte (also gets sb1)
	rel	decode 2
	xaddr	de, gr8080	; de ^ table for class 8080
	ld	l, a		; hl = select byte = ib1
	ld	h, 0
	add	hl, de		; hl ^ gr8080[ib1]
	ld	c, (hl)		; c = gr8080[ib1]
	inc	c		; test c
	dec	c
	jr	z, dec_ext	; if not gr8080 then try other classes
				; * c = gr8080[ib1], a = ib1 *

;  ***	fetch other bytes (including sb bytes), setting iselb, idb values

	xput	iselb, a	; iselb = ib1
	call	dec_len		; a = length (b & hl altered)
	rel	decode 3
	dec	a		; a = length - 1
	jr	z, dec_vlook	; skip to look-up if length = 1
	ld	b, a		; b = length - 1
	call	dec_gb2		; fetch ib2 = a = 2nd byte (also gets sb2)
	rel	decode 4
	xput	idb1, a		; idb1 = ib2
	dec	b		; b = length - 2
	jr	z, dec_vlook	; skip to look-up if length = 2
	call	dec_gb3		; fetch ib3 = a = 3rd byte (also gets sb3)
	rel	decode 5
	xput	idb2, a		; idb2 = ib3
	jr	dec_vlook	; skip to look-up
				; * c = group *

;  ***	look up group for non-8080 classes

;  ***	Extended class

; 	else
; 	    switch (ib1) {		/* depending on context of first byte */
; 	    case 0xed : igroup = grext[iselb = GETB2];	/* extended class */
; 			if (ILEN == 4) {	/* always 2 or 4 bytes */
; 			    idb1 = GETB3;	/* fetch if needed */
; 			    idb2 = GETB4;
; 			}
; 			break;

dec_ext:			; * a = ib1 *

	cp	0edh		; extended class instructions ?
	jr	nz, dec_shi	; if not then try shift class
	call	dec_gb2		; ib2 = a = second byte (also gets sb2)
	rel	decode 6
	xput	iselb, a	; iselb = ib2
	xaddr	de, grext	; de ^ grext
	ld	l, a		; hl = select byte = ib2
	ld	h, 0
	add	hl, de		; hl ^ grext[ib2]
	ld	c, (hl)		; c = grext[ib2]
	inc	c		; test c
	dec	c
	jr	z, dec_bad	; illegal instruction
				; * c = group, a = ib2 *

	call	dec_len		; a = length (b & hl altered)
	rel	decode 7
	cp	2		; length = 2 or 4 for this class
	jr	z, dec_look	; skip to look-up if length = 2
	call	dec_gb3		; fetch ib3 = a = 3rd byte (also gets sb3)
	rel	decode 8
	xput	idb1, a		; idb1 = ib3
	call	dec_gb4		; fetch ib4 = a = 4th byte (also gets sb4)
	rel	decode 9
	xput	idb2, a		; idb2 = ib4
dec_vlook:			; vector to dec-look used for long rel jump
	jr	dec_look	; skip to look-up
				; * c = group *

;  ***	shift class

; 	    case 0xcb : igroup = grshift[iselb=GETB2];	/* shift-rot-bit class */
; 			break;			/* always 2 bytes long */

dec_shi:			; * a = ib1 *

	cp	0cbh		; shift class instructions ?
	jr	nz, dec_ind	; if not then try indexed class
	call	dec_gb2		; ib2 = a = second byte (also gets sb2)
	rel	decode 10
	xput	iselb, a	; iselb = ib2
	xaddr	de, grshift	; de ^ table for shift class
	ld	l, a		; hl = select byte = ib2
	ld	h, 0
	add	hl, de		; hl ^ grshift[ib2]
	ld	c, (hl)		; c = grshift[ib2]
	jr	dec_look	; skip to look-up
				; * c = group, a = ib2 *

;  ***	indexed classes

; 	    case 0xdd :
; 	    case 0xfd :	if (igroup = grindex[iselb=GETB2]) /* indexed class */
; 			    switch (ILEN) {	/* fetch extra bytes */
; 			    case 4 : idb2 = GETB4;
; 			    case 3 : idb1 = GETB3;
; 			    }

dec_ind:			; * a = ib1 *

	cp	0ddh		; should be 0ddh or 0fdh to reach here
	jr	z, dec_ind2	; but check anyway
	cp	0fdh
	jr	nz, dec_bad	; assume bad instruction if no match

dec_ind2: call	dec_gb2		; ib2 = a = second byte (also gets sb2)
	rel	decode 11
	xput	iselb, a	; iselb = ib2
	xaddr	de, grindex	; de ^ table for indexed class
	ld	l, a		; hl = select byte = ib2
	ld	h, 0
	add	hl, de		; hl ^ grindex[ib2]
	ld	c, (hl)		; c = grindex[ib2]
	inc	c		; test c
	dec	c
	jr	z, dec_shid	; failing this try indexed shift class
				; * c = group, a = ib2 *

	call	dec_len		; a = length (b & hl altered)
	rel	decode 12
	sub	2		; a = length - 2
	jr	z, dec_look	; skip to look-up if length = 2
	ld	b, a		; b = length - 2
	call	dec_gb3		; fetch ib3 = a = 3rd byte (also gets sb3)
	rel	decode 13
	xput	idb1, a		; idb1 = ib3
	dec	b		; b = length - 3
	jr	z, dec_look	; skip to look-up if length = 3
	call	dec_gb4		; fetch ib4 = a = 4th byte (also gets sb4)
	rel	decode 14
	xput	idb2, a		; idb2 = ib4
	jr	dec_look	; skip to look-up
				; * c = group *

;  ***	indexed shift class

; 			else if (ib2 == 0xcb) { /* indexed shift class */
; 			    igroup = grindsh[iselb = GETB4];
; 			    GETB3;		/* 4 bytes long */
; 			}
; 			break;
; 	    }
; 	}

dec_shid:			; * a = ib2 *

	cp	0cbh		; indexed shift class instructions ?
	jr	nz, dec_bad	; if not then illegal opcode
	call	dec_gb3		; ib3 = a = third byte (also gets sb3)
	rel	decode 15
	call	dec_gb4		; ib4 = a = fourth byte (also gets sb4)
	rel	decode 16	; (all these instructions 4 bytes long)
	xput	iselb, a	; iselb = ib4
	xaddr	de, grindsh	; de ^ table for indexed shift class
	ld	l, a		; hl = select byte = ib4
	ld	h, 0
	add	hl, de		; hl ^ grindsh[ib4]
	ld	c, (hl)		; c = grindsh[ib4]
				; * c = group, a = ib4 *

;  ***	Look up other details for group identified

; 	if (igroup == 0) {			/* force load of all 4 bytes */
; 	    GETB3; GETB4;			/* if bad instruction */
;	}
; 	ifunc = gfunc_tab[igroup];		/* processing function code */
; 	ilen = (gval = gtimlen_tab[igroup]) & 7; /* length in bytes */
; 	itime = gval >> 3;			/* time taken */
; 	imode1 = (gval = gop1_tab[igroup]) & 31; /* address mode of 1st op */
; 	iload1 = gval & 128;			/* pre-load flag for op1 */
; 	istore1 = gval & 64;			/* post-store flag for op1 */
; 	imode2 = (gval = gop2_tab[igroup]) & 31; /* address mode of 2nd op */
; 	iload2 = gval & 128;			/* pre-load flag for op2 */
; 	istore2 = gval & 64;			/* post-store flag for op2 */
; 	imnem = gmnem_tab[igroup];		/* mnemonic code number */

dec_look:			; * c = group *

	xor	a		; a = 0
	cp	c		; test group number
	jr	nz, dec_l2	; skip over next code if not zero

dec_bad:			; entry point for group = 0

	call	dec_gb3		; fetch bytes ib3 and ib4 (sb3 and sb4 also)
	rel	decode 17
	call	dec_gb4
	rel	decode 18
	xor	a		; set a = c = 0
	ld	c, a		; drop through into look-up code

				; * in the folowing code BC contains the group
				; number and is used as an index for the various
				; look-up tables. DE points at the corresponding
				; variables in the instruction descriptors
				; (ilen, itime ... etc) *

dec_l2:				; * c = group, a = 0 *

	ld	b, a		; clear b (bc = group)
	xaddr	de, ifunc	; de ^ ifunc
	xaddr	hl, gfunc_ta	; look up gfunc_tab[group]
	add	hl, bc
	ld	a, (hl)		; ifunc = gfunc_tab[group]
	ld	(de), a

	inc	de		; de ^ ilen
	xaddr	hl, gtimlen_	; look up gtimlen_tab[group]
	add	hl, bc
	ld	a, (hl)		; extract length info
	and	7
	ld	(de), a		; ilen = gtimlen_tab[group] & 7
	inc	de		; de ^ itime
	xor	(hl)		; extract time info (reset LS 3 bits)
	rrca			; shift right 3 bits
	rrca
	rrca
	ld	(de), a		; itime = gtimlen_tab[group] >> 3

	xaddr	hl, gop1_tab	; look up gop1_tab[group]
	add	hl, bc
	ld	a, (hl)		; save in a
	xaddr	hl, gop2_tab	; look up gop2_tab[group]
	add	hl, bc
	ld	l, (hl)		; l = gop2_tab[group]
	ld	h, a		; h = a = gop1_tab[group]

	inc	de		; de ^ imode1
	and	31		; extract address mode data
	ld	(de), a		; imode1 = gop1_tab[group] & 31
	inc	de		; de ^ imode2
	ld	a, l
	and	31
	ld	(de), a		; imode2 = gop2_tab[group] & 31

	inc	de		; de ^ iload1
	ld	a, h		; extract pre-load flags
	and	128
	ld	(de), a		; iload1 = gop1_tab[group] & 128
	inc	de		; de ^ iload2
	ld	a, l
	and	128
	ld	(de), a		; iload2 = gop2_tab[group] & 128

	inc	de		; de ^ istore1
	ld	a, h		; extract post-store flags
	and	64
	ld	(de), a		; istore1 = gop1_tab[group] & 64
	inc	de		; de ^ istore2
	ld	a, l
	and	64
	ld	(de), a		; istore2 = gop2_tab[group] & 64

	inc	de		; de ^ imnem
	xaddr	hl, gmnem_ta	; look up mnemonic string number
	add	hl, bc
	ld	a, (hl)
	ld	(de), a		; imnem = gmnem_tab[group]
				; * bc = group *

;  ***	exit leaving group number in HL

; 	return(igroup);				/* return group number */

	ld	l, c		; hl = group
	ld	h, b
	pop	bc		; restore old bc
	ret			; exit

;  ***	SUBROUTINES

;  ***	fetch instr byte and status using stored addr+offset in a (de & hl alt)

dec_gb:	ld	hl, (dec_addr)	; fetch address
	rel	decode s1
	ld	e, a		; put offset into de
	ld	d, 0
	add	hl, de		; hl = address
	push	hl		; push it onto the stack
	xcall	decode mcget s2	; hl = mcget(addr)
	pop	de		; throw away address
	ld	a, h		; put status into a
	ret			; return with byte in l and status in a

;  ***	fetch ib1..4 and sb1..4 (equiv to macros GETB1..GETB4) de & hl altered

dec_gb1: xor	a		; a = offset = 0
	call	dec_gb		; fetch byte into a
	rel	decode s3
	xput	sb1, a		; store status
	ld	a, l
	xput	ib1, a		; store byte
	ret			; return with byte in a

dec_gb2: ld	a, 1		; a = offset = 1
	call	dec_gb		; fetch byte into a
	rel	decode s4
	xput	sb2, a		; store status
	ld	a, l
	xput	ib2, a		; store byte
	ret			; return with byte in a

dec_gb3: ld	a, 2		; a = offset = 2
	call	dec_gb		; fetch byte into a
	rel	decode s5
	xput	sb3, a		; store status
	ld	a, l
	xput	ib3, a		; store byte
	ret			; return with byte in a

dec_gb4: ld	a, 3		; a = offset = 3
	call	dec_gb		; fetch byte into a
	rel	decode s6
	xput	sb4, a		; store status
	ld	a, l
	xput	ib4, a		; store byte
	ret			; return with byte in a

;  ***	find length of group in reg c (a, b & hl altered)

dec_len: xaddr	hl, gtimlen_	; hl ^ gtimlen_tab
	ld	b, 0		; bc = group
	add	hl, bc		; hl ^ gtimlen_tab[group]
	ld	a, (hl)
	and	7		; a = length
	ret			; return with length in reg a

;  ***	local variables

dec_addr: defs	2		; decode address parameter

	rparam	decode
	rent	decode 1
	rent	decode 2
	rent	decode 3
	rent	decode 4
	rent	decode 5
	rent	decode 6
	rent	decode 7
	rent	decode 8
	rent	decode 9
	rent	decode 10
	rent	decode 11
	rent	decode 12
	rent	decode 13
	rent	decode 14
	rent	decode 15
	rent	decode 16
	rent	decode 17
	rent	decode 18
	rent	decode s1
	rent	decode s2
	rent	decode s3
	rent	decode s4
	rent	decode s5
	rent	decode s6

	endmod	decode

	endcode
	end

; ------------------------ END OF RTDECODE.GEN -----------------------------
decode s6

	endmod	decode

	endcode
	end

; ------------------------ END OF RTDECODE.GEN 