Emulating other CPUs

Using, learning, programming and modding the Gigatron and anything related.
Forum rules
Be nice. No drama.
Post Reply
monsonite
Posts: 101
Joined: 17 May 2018, 07:17

Emulating other CPUs

Post by monsonite »

Hi All,

This a topic that has been simmering on the back-burner, ever since Marcel astounded us all with the v6502 and the Apple and Microchess emulation.

As a Z80 enthusiast, it would be great to emulate the Z80, but for it's day, the Z80 is a surprisingly complex cpu, with 158 instruction "types", but this rapidly expands to several hundred individual instructions when you include all the bit SET, RES and BIT test instructions.

However, a simpler task is to only implement the 78 instruction types, which the Z80 has in common with the Intel 8080. After all, the Digital Research CP/M operating system originally targeted the 8080, before the Z80 was commercially released.

Having trawled the web, I have found that there is a generic approach to writing an emulator. This involves breaking down the target instruction into three separate phases, which considerably reduces the task of writing the emulator:

1. FETCH phase
2. OPERATE phase
3. STORE phase

First we have to create a model of the target CPU, using whatever resources we have available. To do this on the Gigatron, we would use the zero-page RAM pseudo-registers. In the case of an 8080 target, we would allocate zero page RAM locations to the various registers named in the 8080 model :- i.e. A, B, C, D, E, H, L and M (M is (HL) ). We also need a mechanism to process the register pairs BC, DE, HL and the stack pointer SP.

There are 20 different operations that make up the FETCH repertoire. This include fetches from registers, memory eg FETCH_MHL (the memory address pointed to by HL) and 8-bit or 16-bit immediate data. FETCH_DIR8 and FETCH_DIR16

It should be noted that none of this is processor specific, the same approach could be applied to 6800, 6502, 6809 and RCA 1802. Or even to some experimental CPU model that doesn't exist yet in hardware.

Code: Select all

; ------------ Fetch phase stuff -----------------

.equ FETCH_NOP = (0<<0)
.equ FETCH_A	= (1<<0)
.equ FETCH_B	= (2<<0)
.equ FETCH_C	= (3<<0)
.equ FETCH_D	= (4<<0)
.equ FETCH_E	= (5<<0)
.equ FETCH_H	= (6<<0)
.equ FETCH_L	= (7<<0)
.equ FETCH_AF	= (8<<0)
.equ FETCH_BC	= (9<<0)
.equ FETCH_DE	= (10<<0)
.equ FETCH_HL	= (11<<0)
.equ FETCH_SP	= (12<<0)
.equ FETCH_MBC = (13<<0)
.equ FETCH_MDE = (14<<0)
.equ FETCH_MHL = (15<<0)
.equ FETCH_MSP = (16<<0)
.equ FETCH_DIR8 = (17<<0)
.equ FETCH_DIR16 = (18<<0)
.equ FETCH_RST = (19<<0)
The FETCH operation number (0 to 19) is expressed as a 5-bit binary number, located in bits [4:0] of a 16-bit pseudo instruction word.

Having extracted this FETCH number, we use it to index into a jump table, so we can find the start address of the action routine which is going to implement the FETCH operation:

Code: Select all

;Jump table for fetch routines. Make sure to keep this in sync with the .equs!
fetchjumps:
.dw do_fetch_nop
.dw do_fetch_a
.dw do_fetch_b
.dw do_fetch_c
.dw do_fetch_d
.dw do_fetch_e
.dw do_fetch_h
.dw do_fetch_l
.dw do_fetch_af
.dw do_fetch_bc
.dw do_fetch_de
.dw do_fetch_hl
.dw do_fetch_sp
.dw do_fetch_mbc
.dw do_fetch_mde
.dw do_fetch_mhl
.dw do_fetch_msp
.dw do_fetch_dir8
.dw do_fetch_dir16
.dw do_fetch_rst
And here is the code for the first few FETCH operations:

Code: Select all

do_fetch_nop:
	ret

do_fetch_a:
	mov opl,z_a
	ret

do_fetch_b:
	mov opl,z_b
	ret

do_fetch_c:
	mov opl,z_c
	ret

do_fetch_d:
	mov opl,z_d
	ret
At this point, I must point out that the above is written in AVR assembly language. z_a, z_b, z_c, z_d are pseudonyms for the AVR registers, as is opl (low byte operand, there is also oph which is storage for the high byte operand).

Code: Select all

do_fetch_bc:
	mov opl,z_c
	mov oph,z_b
	ret

do_fetch_de:
	mov opl,z_e
	mov oph,z_d
	ret
And also code to read from memory, where 16-bit register pairs are providing the address:

Code: Select all

do_fetch_mde:
	mov adrh,z_d
	mov adrl,z_e
	rcall memReadByte
	mov opl,temp
	ret

do_fetch_mhl:
	mov adrh,z_h
	mov adrl,z_l
	rcall memReadByte
	mov opl,temp
	ret
Finally for 8-bit or 16-bit immediate data:

Code: Select all

do_fetch_dir8:
	mov adrl,z_pcl
	mov adrh,z_pch
	rcall memReadByte
	adiw z_pcl,1
	mov opl,temp
	ret

do_fetch_dir16:
	mov adrl,z_pcl
	mov adrh,z_pch
	rcall memReadByte
	mov opl,temp
	adiw z_pcl,1
	mov adrl,z_pcl
	mov adrh,z_pch
	rcall memReadByte
	adiw z_pcl,1
	mov oph,temp
	ret
Although the AVR mnemonics might be unfamiliar, the principles of what is happening is easy to grasp, and thus translate to Gigatron code.

Next we look at the OPERATE phase - here a specific function is performed on the data that we have just fetched. There are 40 individual operations that we might need to do on the fetched data. These are given a number 0 to 39 and this is coded into bits 10 to 15 of the pseudo-instruction word.

Code: Select all

; ------------ Operation phase stuff -----------------


.equ OP_NOP		= (0<<10)
.equ OP_INC		= (1<<10)
.equ OP_DEC		= (2<<10)
.equ OP_INC16		= (3<<10)
.equ OP_DEC16		= (4<<10)
.equ OP_RLC 		= (5<<10)
.equ OP_RRC 		= (6<<10)
.equ OP_RR	 	= (7<<10)
.equ OP_RL		= (8<<10)
.equ OP_ADDA		= (9<<10)
.equ OP_ADCA		= (10<<10)
.equ OP_SUBFA		= (11<<10)
.equ OP_SBCFA		= (12<<10)
.equ OP_ANDA		= (13<<10)
.equ OP_ORA		= (14<<10)
.equ OP_XORA		= (15<<10)
.equ OP_ADDHL		= (16<<10)
.equ OP_STHL		= (17<<10) ;store HL in fetched address
.equ OP_RMEM16	= (18<<10) ;read mem at fetched address
.equ OP_RMEM8	= (19<<10) ;read mem at fetched address
.equ OP_DA		= (20<<10)
.equ OP_SCF		= (21<<10)
.equ OP_CPL		= (22<<10)
.equ OP_CCF		= (23<<10)
.equ OP_POP16		= (24<<10)
.equ OP_PUSH16	= (25<<10)
.equ OP_IFNZ		= (26<<10)
.equ OP_IFZ		= (27<<10)
.equ OP_IFNC		= (28<<10)
.equ OP_IFC		= (29<<10)
.equ OP_IFPO		= (30<<10)
.equ OP_IFPE		= (31<<10)
.equ OP_IFP		= (32<<10)
.equ OP_IFM		= (33<<10)
.equ OP_OUTA		= (34<<10)
.equ OP_IN		= (35<<10)
.equ OP_EXHL		= (36<<10)
.equ OP_DI		= (37<<10)
.equ OP_EI		= (38<<10)
.equ OP_INV		= (39<<10)

opjumps:
.dw do_op_nop
.dw do_op_inc
.dw do_op_dec
.dw do_op_inc16
.dw do_op_dec16
.dw do_op_rlc
.dw do_op_rrc
.dw do_op_rr
.dw do_op_rl
.dw do_op_adda
.dw do_op_adca
.dw do_op_subfa
.dw do_op_sbcfa
.dw do_op_anda
.dw do_op_ora
.dw do_op_xora
.dw do_op_addhl
.dw do_op_sthl
.dw do_op_rmem16
.dw do_op_rmem8
.dw do_op_da
.dw do_op_scf
.dw do_op_cpl
.dw do_op_ccf
.dw do_op_pop16
.dw do_op_push16
.dw do_op_ifnz
.dw do_op_ifz
.dw do_op_ifnc
.dw do_op_ifc
.dw do_op_ifpo
.dw do_op_ifpe
.dw do_op_ifp
.dw do_op_ifm
.dw do_op_outa
.dw do_op_in
.dw do_op_exhl
.dw do_op_di
.dw do_op_ei
.dw do_op_inv
And of course there are functions to handle all of these operation - for example:

Code: Select all

do_op_inc:
	andi z_flags,1
	ldi temp,1
	add opl,temp
	in temp,sreg
	mov parityb,opl
	bst temp,AVR_Z
	bld z_flags,ZFL_Z
	sbrc opl,7
	 ori z_flags,(1<<ZFL_S)
	bst temp,AVR_H
	bld z_flags,ZFL_H
	ret

do_op_dec:
	andi z_flags,1
	ori z_flags,(1<<ZFL_N)
	ldi temp,1
	sub opl,temp
	in temp,sreg
	mov parityb,opl
	bst temp,AVR_Z
	bld z_flags,ZFL_Z
	bst temp,AVR_S
	bld z_flags,ZFL_S
	bst temp,AVR_H
	bld z_flags,ZFL_H
	ret

do_op_inc16:
	ldi temp,1
	ldi temp2,0
	add opl,temp
	adc oph,temp2
	ret

do_op_dec16:
	ldi temp,1
	ldi temp2,0
	sub opl,temp
	sbc oph,temp2
	ret

do_op_rlc:
	;Rotate Left Cyclical. All bits move 1 to the 
	;left, the msb becomes c and lsb.
	andi z_flags,0b11101100
	lsl opl
	brcc do_op_rlc_noc
	ori opl,1
	ori z_flags,(1<<ZFL_C)
	
do_op_rlc_noc:
	ret
Finally we come to the STORE phase. Here the results of the operation phase are written back to registers, the flags register or memory. It follows a very similar structure to the other 2 phases. There are 21 store operations which are encoded into bits[9:5] of the pseudo-instruction word.:

Code: Select all

; ------------ Store phase stuff -----------------

.equ STORE_NOP	= (0<<5)
.equ STORE_A	= (1<<5)
.equ STORE_B	= (2<<5)
.equ STORE_C	= (3<<5)
.equ STORE_D	= (4<<5)
.equ STORE_E	= (5<<5)
.equ STORE_H	= (6<<5)
.equ STORE_L	= (7<<5)
.equ STORE_AF	= (8<<5)
.equ STORE_BC	= (9<<5)
.equ STORE_DE	= (10<<5)
.equ STORE_HL	= (11<<5)
.equ STORE_SP	= (12<<5)
.equ STORE_PC	= (13<<5)
.equ STORE_MBC = (14<<5)
.equ STORE_MDE = (15<<5)
.equ STORE_MHL = (16<<5)
.equ STORE_MSP = (17<<5)
.equ STORE_RET = (18<<5)
.equ STORE_CALL = (19<<5)
.equ STORE_AM	= (20<<5)

;Jump table for store routines. Make sure to keep this in sync with the .equs!
storejumps:
.dw do_store_nop
.dw do_store_a
.dw do_store_b
.dw do_store_c
.dw do_store_d
.dw do_store_e
.dw do_store_h
.dw do_store_l
.dw do_store_af
.dw do_store_bc
.dw do_store_de
.dw do_store_hl
.dw do_store_sp
.dw do_store_pc
.dw do_store_mbc
.dw do_store_mde
.dw do_store_mhl
.dw do_store_msp
.dw do_store_ret
.dw do_store_call
.dw do_store_am


do_store_nop:
	ret

do_store_a:
	mov z_a,opl
	ret

do_store_b:
	mov z_b,opl
	ret

do_store_c:
	mov z_c,opl
	ret
So by using this technique, any instruction set can be analysed into the three sequential operations of FETCH, OPERATE and STORE.

So here is how the 8080 instruction set is analysed and how the 16-bit pseudo instruction is built up by ORing together the three Fetch, Operate and Store terms:

Code: Select all

; ----------------------- Opcode decoding -------------------------

; Lookup table for Z80 opcodes. Translates the first byte of the instruction word into three
; operations: fetch, do something, store.
; The table is made of 256 words. These 16-bit words consist of 
; the fetch operation (bit 0-4), the processing operation (bit 10-15) and the store 
; operation (bit 5-9).

inst_table:

.dw (FETCH_NOP| OP_NOP	| STORE_NOP)	 		; 00		NOP
.dw (FETCH_DIR16| OP_NOP	| STORE_BC )	 		; 01 nn nn	LD BC,nn
.dw (FETCH_A	| OP_NOP	| STORE_MBC  )	 			; 02		LD (BC),A
.dw (FETCH_BC	| OP_INC16	| STORE_BC )			; 03		INC BC
.dw (FETCH_B	| OP_INC	| STORE_B  )				; 04		INC B
.dw (FETCH_B	| OP_DEC	| STORE_B  )	 			; 05		DEC B
.dw (FETCH_DIR8	| OP_NOP	| STORE_B  )	 		; 06 nn	LD B,n
.dw (FETCH_A	| OP_RLC	| STORE_A  )	 			; 07		RLCA
.dw (FETCH_NOP	| OP_INV	| STORE_NOP)			; 08		EX AF,AF'	(Z80)
.dw (FETCH_BC	| OP_ADDHL	| STORE_HL )	 		; 09		ADD HL,BC
.dw (FETCH_MBC	| OP_NOP	| STORE_A  )	 		; 0A		LD A,(BC)
.dw (FETCH_BC	| OP_DEC16	| STORE_BC )	 		; 0B		DEC BC
.dw (FETCH_C	| OP_INC	| STORE_C  )	 			; 0C		INC C
.dw (FETCH_C	| OP_DEC	| STORE_C  )	 			; 0D		DEC C
.dw (FETCH_DIR8	| OP_NOP	| STORE_C  )	 		; 0E nn	LD C,n
.dw (FETCH_A	| OP_RRC	| STORE_A  )	 			; 0F		RRCA
.dw (FETCH_NOP	| OP_INV	| STORE_NOP)			; 10 oo	DJNZ o		(Z80)
.dw (FETCH_DIR16| OP_NOP	| STORE_DE )	 		; 11 nn nn	LD DE,nn
.dw (FETCH_A	| OP_NOP	| STORE_MDE)	 			; 12		LD (DE),A
.dw (FETCH_DE	| OP_INC16  | STORE_DE )	 			; 13		INC DE
.dw (FETCH_D	| OP_INC	| STORE_D  )	 			; 14		INC D
.dw (FETCH_D	| OP_DEC	| STORE_D  )	 			; 15		DEC D
.dw (FETCH_DIR8	| OP_NOP	| STORE_D  )	 		; 16 nn	LD D,n
.dw (FETCH_A	| OP_RL		| STORE_A  )	 		; 17		RLA
.dw (FETCH_NOP	| OP_INV	| STORE_NOP)	 		; 18 oo	JR o		(Z80)
.dw (FETCH_DE	| OP_ADDHL	| STORE_HL )	 		; 19		ADD HL,DE
.dw (FETCH_MDE	| OP_NOP	| STORE_A  )	 		; 1A		LD A,(DE)
.dw (FETCH_DE	| OP_DEC16	| STORE_DE )	 		; 1B		DEC DE
.dw (FETCH_E	| OP_INC	| STORE_E  )	 			; 1C		INC E
.dw (FETCH_E	| OP_DEC	| STORE_E  )	 			; 1D		DEC E
.dw (FETCH_DIR8	| OP_NOP	| STORE_E  )	 		; 1E nn	LD E,n
.dw (FETCH_A	| OP_RR		| STORE_A  )	 		; 1F		RRA
.dw (FETCH_NOP	| OP_INV	| STORE_NOP)			; 20 oo	JR NZ,o		(Z80)
.dw (FETCH_DIR16| OP_NOP	| STORE_HL )	 		; 21 nn nn	LD HL,nn
.dw (FETCH_DIR16| OP_STHL	| STORE_NOP)	 		; 22 nn nn	LD (nn),HL
.dw (FETCH_HL	| OP_INC16	| STORE_HL )	 		; 23		INC HL
.dw (FETCH_H	| OP_INC	| STORE_H  )	 			; 24		INC H
.dw (FETCH_H	| OP_DEC	| STORE_H  )	 			; 25		DEC H
.dw (FETCH_DIR8	| OP_NOP	| STORE_H  )	 		; 26 nn	LD H,n
.dw (FETCH_A	| OP_DA		| STORE_A  )			; 27		DAA
.dw (FETCH_NOP	| OP_INV	| STORE_NOP)	 		; 28 oo	JR Z,o		(Z80)
.dw (FETCH_HL	| OP_ADDHL	| STORE_HL )	 		; 29		ADD HL,HL
.dw (FETCH_DIR16| OP_RMEM16	| STORE_HL )		; 2A nn nn	LD HL,(nn)
.dw (FETCH_HL	| OP_DEC16	| STORE_HL )	 		; 2B		DEC HL
.dw (FETCH_L	| OP_INC	| STORE_L  )	 			; 2C		INC L
.dw (FETCH_L	| OP_DEC	| STORE_L  )	 			; 2D		DEC L
.dw (FETCH_DIR8	| OP_NOP	| STORE_L  )	 		; 2E nn	LD L,n
.dw (FETCH_A	| OP_CPL	| STORE_A  )	 			; 2F		CPL
.dw (FETCH_NOP	| OP_INV	| STORE_NOP)	 		; 30 oo	JR NC,o		(Z80)
.dw (FETCH_DIR16| OP_NOP	| STORE_SP )	 		; 31 nn nn	LD SP,nn
.dw (FETCH_DIR16| OP_NOP	| STORE_AM )	 		; 32 nn nn	LD (nn),A
.dw (FETCH_SP	| OP_INC16	| STORE_SP )	 		; 33		INC SP
.dw (FETCH_MHL	| OP_INC	| STORE_MHL)	 		; 34		INC (HL)
.dw (FETCH_MHL	| OP_DEC	| STORE_MHL)	 		; 35		DEC (HL)
.dw (FETCH_DIR8	| OP_NOP	| STORE_MHL)	 		; 36 nn	LD (HL),n
.dw (FETCH_NOP	| OP_SCF	| STORE_NOP)	 		; 37		SCF
.dw (FETCH_NOP	| OP_INV	| STORE_NOP)	 		; 38 oo	JR C,o		(Z80)
.dw (FETCH_SP	| OP_ADDHL	| STORE_HL )	 		; 39		ADD HL,SP
.dw (FETCH_DIR16| OP_RMEM8	| STORE_A  )	 		; 3A nn nn	LD A,(nn)
.dw (FETCH_SP	| OP_DEC16	| STORE_SP )	 		; 3B		DEC SP
.dw (FETCH_A	| OP_INC	| STORE_A  )	 			; 3C		INC A
.dw (FETCH_A	| OP_DEC	| STORE_A  )	 			; 3D		DEC A
.dw (FETCH_DIR8	| OP_NOP	| STORE_A  )	 		; 3E nn	LD A,n
.dw (FETCH_NOP	| OP_CCF	| STORE_NOP)	 		; 3F		CCF (Complement Carry Flag, gvd)
.dw (FETCH_B	| OP_NOP	| STORE_B  )	 			; 40		LD B,r
.dw (FETCH_C	| OP_NOP	| STORE_B  )	 			; 41		LD B,r
.dw (FETCH_D	| OP_NOP	| STORE_B  )				; 42		LD B,r
.dw (FETCH_E	| OP_NOP	| STORE_B  )				 ; 43		LD B,r
.dw (FETCH_H	| OP_NOP	| STORE_B  )	 			; 44		LD B,r
.dw (FETCH_L	| OP_NOP	| STORE_B  )	 			; 45		LD B,r
.dw (FETCH_MHL	| OP_NOP	| STORE_B  )	 		; 46		LD B,r
.dw (FETCH_A	| OP_NOP	| STORE_B  )				; 47		LD B,r
.dw (FETCH_B	| OP_NOP	| STORE_C  )	 			; 48		LD C,r
.dw (FETCH_C	| OP_NOP	| STORE_C  )				; 49		LD C,r
.dw (FETCH_D	| OP_NOP	| STORE_C  )	 			; 4A		LD C,r
.dw (FETCH_E	| OP_NOP	| STORE_C  )	 			; 4B		LD C,r
.dw (FETCH_H	| OP_NOP	| STORE_C  )	 			; 4C		LD C,r
.dw (FETCH_L	| OP_NOP	| STORE_C  )	 			; 4D		LD C,r
.dw (FETCH_MHL	| OP_NOP	| STORE_C  )	 		; 4E		LD C,r
.dw (FETCH_A	| OP_NOP	| STORE_C  )	 			; 4F		LD C,r
.dw (FETCH_B	| OP_NOP	| STORE_D  )	 			; 50		LD D,r
.dw (FETCH_C	| OP_NOP	| STORE_D  )	 			; 51		LD D,r
.dw (FETCH_D	| OP_NOP	| STORE_D  )	 			; 52		LD D,r
.dw (FETCH_E	| OP_NOP	| STORE_D  )	 			; 53		LD D,r
.dw (FETCH_H	| OP_NOP	| STORE_D  )	 			; 54		LD D,r
.dw (FETCH_L	| OP_NOP	| STORE_D  )				; 55		LD D,r
.dw (FETCH_MHL	| OP_NOP	| STORE_D  )			; 56		LD D,r
.dw (FETCH_A	| OP_NOP	| STORE_D  )	 			; 57		LD D,r
.dw (FETCH_B	| OP_NOP	| STORE_E  )	 			; 58		LD E,r
.dw (FETCH_C	| OP_NOP	| STORE_E  )				 ; 59		LD E,r
.dw (FETCH_D	| OP_NOP	| STORE_E  )	 			; 5A		LD E,r
.dw (FETCH_E	| OP_NOP	| STORE_E  )	 			; 5B		LD E,r
.dw (FETCH_H	| OP_NOP	| STORE_E  )	 			; 5C		LD E,r
.dw (FETCH_L	| OP_NOP	| STORE_E  )	 			; 5D		LD E,r
.dw (FETCH_MHL	| OP_NOP	| STORE_E  )	 		; 5E		LD E,r
.dw (FETCH_A	| OP_NOP	| STORE_E  )	 			; 5F		LD E,r
.dw (FETCH_B	| OP_NOP	| STORE_H  )	 			; 60		LD H,r
.dw (FETCH_C	| OP_NOP	| STORE_H  )	 			; 61		LD H,r
.dw (FETCH_D	| OP_NOP	| STORE_H  )	 			; 62		LD H,r
.dw (FETCH_E	| OP_NOP	| STORE_H  )	 			; 63		LD H,r
.dw (FETCH_H	| OP_NOP	| STORE_H  )	 			; 64		LD H,r
.dw (FETCH_L	| OP_NOP	| STORE_H  )	 			; 65		LD H,r
.dw (FETCH_MHL	| OP_NOP	| STORE_H  )	 		; 66		LD H,r
.dw (FETCH_A	| OP_NOP	| STORE_H  )	 			; 67		LD H,r
.dw (FETCH_B	| OP_NOP	| STORE_L  )	 			; 68		LD L,r
.dw (FETCH_C	| OP_NOP	| STORE_L  )	 			; 69		LD L,r
.dw (FETCH_D	| OP_NOP	| STORE_L  )	 			; 6A		LD L,r
.dw (FETCH_E	| OP_NOP	| STORE_L  )	 			; 6B		LD L,r
.dw (FETCH_H	| OP_NOP	| STORE_L  )	 			; 6C		LD L,r
.dw (FETCH_L	| OP_NOP	| STORE_L  )	 			; 6D		LD L,r
.dw (FETCH_MHL	| OP_NOP	| STORE_L  )	 		; 6E		LD L,r
.dw (FETCH_A	| OP_NOP	| STORE_L  )	 			; 6F		LD L,r
.dw (FETCH_B	| OP_NOP	| STORE_MHL)	 			; 70		LD (HL),r
.dw (FETCH_C	| OP_NOP	| STORE_MHL)	 			; 71		LD (HL),r
.dw (FETCH_D	| OP_NOP	| STORE_MHL)	 			; 72		LD (HL),r
.dw (FETCH_E	| OP_NOP	| STORE_MHL)	 			; 73		LD (HL),r
.dw (FETCH_H	| OP_NOP	| STORE_MHL)	 			; 74		LD (HL),r
.dw (FETCH_L	| OP_NOP	| STORE_MHL)	 			; 75		LD (HL),r
.dw (FETCH_NOP	| OP_NOP	| STORE_NOP)	 		; 76		HALT
.dw (FETCH_A	| OP_NOP	| STORE_MHL)	 			; 77		LD (HL),r
.dw (FETCH_B	| OP_NOP	| STORE_A  )	 			; 78		LD A,r
.dw (FETCH_C	| OP_NOP	| STORE_A  )	 			; 79		LD A,r
.dw (FETCH_D	| OP_NOP	| STORE_A  )	 			; 7A		LD A,r
.dw (FETCH_E	| OP_NOP	| STORE_A  )	 			; 7B		LD A,r
.dw (FETCH_H	| OP_NOP	| STORE_A  )	 			; 7C		LD A,r
.dw (FETCH_L	| OP_NOP	| STORE_A  )	 			; 7D		LD A,r
.dw (FETCH_MHL	| OP_NOP	| STORE_A  )	 		; 7E		LD A,r
.dw (FETCH_A	| OP_NOP	| STORE_A  )	 			; 7F		LD A,r
.dw (FETCH_B	| OP_ADDA	| STORE_A  )	 		; 80		ADD A,r
.dw (FETCH_C	| OP_ADDA	| STORE_A  )	 		; 81		ADD A,r
.dw (FETCH_D	| OP_ADDA	| STORE_A  )	 		; 82		ADD A,r
.dw (FETCH_E	| OP_ADDA	| STORE_A  )	 		; 83		ADD A,r
.dw (FETCH_H	| OP_ADDA	| STORE_A  )	 		; 84		ADD A,r
.dw (FETCH_L	| OP_ADDA	| STORE_A  )	 		; 85		ADD A,r
.dw (FETCH_MHL	| OP_ADDA	| STORE_A  )	 	; 86		ADD A,r
.dw (FETCH_A	| OP_ADDA	| STORE_A  )	 		; 87		ADD A,r
.dw (FETCH_B	| OP_ADCA	| STORE_A  )	 		; 88		ADC A,r
.dw (FETCH_C	| OP_ADCA	| STORE_A  )	 		; 89		ADC A,r
.dw (FETCH_D	| OP_ADCA	| STORE_A  )			 ; 8A		ADC A,r
.dw (FETCH_E	| OP_ADCA	| STORE_A  )	 		; 8B		ADC A,r
.dw (FETCH_H	| OP_ADCA	| STORE_A  )	 		; 8C		ADC A,r
.dw (FETCH_L	| OP_ADCA	| STORE_A  )	 		; 8D		ADC A,r
.dw (FETCH_MHL	| OP_ADCA	| STORE_A  )	 	; 8E		ADC A,r
.dw (FETCH_A	| OP_ADCA	| STORE_A  )	 		; 8F		ADC A,r
.dw (FETCH_B	| OP_SUBFA	| STORE_A  )	 		; 90		SUB A,r
.dw (FETCH_C	| OP_SUBFA	| STORE_A  )	 		; 91		SUB A,r
.dw (FETCH_D	| OP_SUBFA	| STORE_A  )	 		; 92		SUB A,r
.dw (FETCH_E	| OP_SUBFA	| STORE_A  )	 		; 93		SUB A,r
.dw (FETCH_H	| OP_SUBFA	| STORE_A  )	 		; 94		SUB A,r
.dw (FETCH_L	| OP_SUBFA	| STORE_A  )	 		; 95		SUB A,r
.dw (FETCH_MHL	| OP_SUBFA	| STORE_A  )	 	; 96		SUB A,r
.dw (FETCH_A	| OP_SUBFA	| STORE_A  )	 		; 97		SUB A,r
.dw (FETCH_B	| OP_SBCFA	| STORE_A  )	 		; 98		SBC A,r
.dw (FETCH_C	| OP_SBCFA	| STORE_A  )	 		; 99		SBC A,r
.dw (FETCH_D	| OP_SBCFA	| STORE_A  )	 		; 9A		SBC A,r
.dw (FETCH_E	| OP_SBCFA	| STORE_A  )	 		; 9B		SBC A,r
.dw (FETCH_H	| OP_SBCFA	| STORE_A  )	 		; 9C		SBC A,r
.dw (FETCH_L	| OP_SBCFA	| STORE_A  )	 		; 9D		SBC A,r
.dw (FETCH_MHL	| OP_SBCFA	| STORE_A  )	 	; 9E		SBC A,r
.dw (FETCH_A	| OP_SBCFA	| STORE_A  )	 		; 9F		SBC A,r
.dw (FETCH_B	| OP_ANDA	| STORE_A  )	 		; A0		AND A,r
.dw (FETCH_C	| OP_ANDA	| STORE_A  )	 		; A1		AND A,r
.dw (FETCH_D	| OP_ANDA	| STORE_A  )	 		; A2		AND A,r
.dw (FETCH_E	| OP_ANDA	| STORE_A  )	 		; A3		AND A,r
.dw (FETCH_H	| OP_ANDA	| STORE_A  )	 		; A4		AND A,r
.dw (FETCH_L	| OP_ANDA	| STORE_A  )	 		; A5		AND A,r
.dw (FETCH_MHL	| OP_ANDA	| STORE_A  )	 	; A6		AND A,r
.dw (FETCH_A	| OP_ANDA	| STORE_A  )	 		; A7		AND A,r
.dw (FETCH_B	| OP_XORA	| STORE_A  )	 		; A8		XOR A,r
.dw (FETCH_C	| OP_XORA	| STORE_A  )	 		; A9		XOR A,r
.dw (FETCH_D	| OP_XORA	| STORE_A  )	 		; AA		XOR A,r
.dw (FETCH_E	| OP_XORA	| STORE_A  )	 		; AB		XOR A,r
.dw (FETCH_H	| OP_XORA	| STORE_A  )	 		; AC		XOR A,r
.dw (FETCH_L	| OP_XORA	| STORE_A  )	 		; AD		XOR A,r
.dw (FETCH_MHL	| OP_XORA	| STORE_A  )	 	; AE		XOR A,r
.dw (FETCH_A	| OP_XORA	| STORE_A  )	 		; AF		XOR A,r
.dw (FETCH_B	| OP_ORA	| STORE_A  )	 		; B0		OR A,r
.dw (FETCH_C	| OP_ORA	| STORE_A  )			; B1		OR A,r
.dw (FETCH_D	| OP_ORA	| STORE_A  )	 		; B2		OR A,r
.dw (FETCH_E	| OP_ORA	| STORE_A  )	 		; B3		OR A,r
.dw (FETCH_H	| OP_ORA	| STORE_A  )	 		; B4		OR A,r
.dw (FETCH_L	| OP_ORA	| STORE_A  )	 		; B5		OR A,r
.dw (FETCH_MHL	| OP_ORA	| STORE_A  )	 	; B6		OR A,r
.dw (FETCH_A	| OP_ORA	| STORE_A  )	 		; B7		OR A,r
.dw (FETCH_B	| OP_SUBFA	| STORE_NOP)	 	; B8		CP A,r
.dw (FETCH_C	| OP_SUBFA	| STORE_NOP)	 	; B9		CP A,r
.dw (FETCH_D	| OP_SUBFA	| STORE_NOP)	 	; BA		CP A,r
.dw (FETCH_E	| OP_SUBFA	| STORE_NOP)	 	; BB		CP A,r
.dw (FETCH_H	| OP_SUBFA	| STORE_NOP)	 	; BC		CP A,r
.dw (FETCH_L	| OP_SUBFA	| STORE_NOP)	 	; BD		CP A,r
.dw (FETCH_MHL	| OP_SUBFA	| STORE_NOP)	; BE		CP A,r
.dw (FETCH_A	| OP_SUBFA	| STORE_NOP)	 	; BF	 	CP A,r
.dw (FETCH_NOP  | OP_IFNZ	| STORE_RET)	 	; C0		RET NZ
.dw (FETCH_NOP  | OP_POP16	| STORE_BC )	 	; C1		POP BC
.dw (FETCH_DIR16| OP_IFNZ	| STORE_PC )	 	; C2 nn nn	JP NZ,nn
.dw (FETCH_DIR16| OP_NOP	| STORE_PC )	 	; C3 nn nn	JP nn
.dw (FETCH_DIR16| OP_IFNZ	| STORE_CALL)	 	; C4 nn nn	CALL NZ,nn
.dw (FETCH_BC	| OP_PUSH16	| STORE_NOP)	 	; C5		PUSH BC
.dw (FETCH_DIR8	| OP_ADDA	| STORE_A  )	; C6 nn	ADD A,n
.dw (FETCH_RST	| OP_NOP	| STORE_CALL)	 	; C7		RST 0
.dw (FETCH_NOP	| OP_IFZ	| STORE_RET)	 	; C8		RET Z
.dw (FETCH_NOP	| OP_NOP	| STORE_RET)	 	; C9		RET
.dw (FETCH_DIR16| OP_IFZ	| STORE_PC )	 	; CA nn nn	JP Z,nn
.dw (FETCH_NOP	| OP_INV	| STORE_NOP)	 	; CB 		(Z80 specific)
.dw (FETCH_DIR16| OP_IFZ	| STORE_CALL)	 	; CC nn nn	CALL Z,nn
.dw (FETCH_DIR16| OP_NOP	| STORE_CALL)	 	; CD nn nn	CALL nn
.dw (FETCH_DIR8	| OP_ADCA	| STORE_A  )	; CE nn	ADC A,n
.dw (FETCH_RST	| OP_NOP	| STORE_CALL)	 	; CF		RST 8H
.dw (FETCH_NOP	| OP_IFNC	| STORE_RET)	 	; D0		RET NC
.dw (FETCH_NOP  | OP_POP16	| STORE_DE )	 	; D1		POP DE
.dw (FETCH_DIR16| OP_IFNC	| STORE_PC )	 	; D2 nn nn	JP NC,nn
.dw (FETCH_DIR8	| OP_OUTA	| STORE_NOP)	; D3 nn	OUT (n),A
.dw (FETCH_DIR16| OP_IFNC	| STORE_CALL)	 	; D4 nn nn	CALL NC,nn
.dw (FETCH_DE	| OP_PUSH16	| STORE_NOP)	 	; D5		PUSH DE
.dw (FETCH_DIR8	| OP_SUBFA	| STORE_A  )	; D6 nn	SUB n
.dw (FETCH_RST	| OP_NOP	| STORE_CALL)	 	; D7		RST 10H
.dw (FETCH_NOP	| OP_IFC	| STORE_RET)	 	; D8		RET C
.dw (FETCH_NOP	| OP_INV	| STORE_NOP)	 	; D9		EXX			(Z80)
.dw (FETCH_DIR16| OP_IFC	| STORE_PC )	 	; DA nn nn	JP C,nn
.dw (FETCH_DIR8	| OP_IN 	| STORE_A  )	 	; DB nn	IN A,(n)
.dw (FETCH_DIR16| OP_IFC	| STORE_CALL)	 	; DC nn nn	CALL C,nn
.dw (FETCH_NOP	| OP_INV	| STORE_NOP)	 	; DD 		(Z80)
.dw (FETCH_DIR8	| OP_SBCFA	| STORE_A  )	; DE nn	SBC A,n
.dw (FETCH_RST	| OP_NOP	| STORE_CALL)	 	; DF		RST 18H
.dw (FETCH_NOP	| OP_IFPO	| STORE_RET)	 	; E0		RET PO
.dw (FETCH_NOP	| OP_POP16	| STORE_HL )	; E1		POP HL
.dw (FETCH_DIR16| OP_IFPO	| STORE_PC )	 	; E2 nn nn	JP PO,nn
.dw (FETCH_MSP	| OP_EXHL	| STORE_MSP)	; E3		EX (SP),HL
.dw (FETCH_DIR16| OP_IFPO	| STORE_CALL)	 	; E4 nn nn	CALL PO,nn
.dw (FETCH_HL	| OP_PUSH16	| STORE_NOP)	 	; E5		PUSH HL
.dw (FETCH_DIR8	| OP_ANDA	| STORE_A  )	; E6 nn	AND n
.dw (FETCH_RST	| OP_NOP	| STORE_CALL)	 	; E7		RST 20H
.dw (FETCH_NOP	| OP_IFPE	| STORE_RET)	 	; E8		RET PE
.dw (FETCH_HL	| OP_NOP	| STORE_PC )	 		; E9		JP (HL)
.dw (FETCH_DIR16| OP_IFPE	| STORE_PC )	 	; EA nn nn	JP PE,nn
.dw (FETCH_DE	| OP_EXHL	| STORE_DE )	 	; EB		EX DE,HL
.dw (FETCH_DIR16| OP_IFPE	| STORE_CALL)	 	; EC nn nn	CALL PE,nn
.dw (FETCH_NOP	| OP_INV	| STORE_NOP)	 	; ED		(Z80 specific)
.dw (FETCH_DIR8	| OP_XORA	| STORE_A  )	; EE nn	XOR n
.dw (FETCH_RST	| OP_NOP	| STORE_CALL)	 	; EF		RST 28H
.dw (FETCH_NOP	| OP_IFP	| STORE_RET)	 	; F0		RET P
.dw (FETCH_NOP	| OP_POP16	| STORE_AF )	; F1		POP AF
.dw (FETCH_DIR16| OP_IFP	| STORE_PC )	 	; F2 nn nn	JP P,nn
.dw (FETCH_NOP	| OP_DI		| STORE_NOP)	; F3		DI
.dw (FETCH_DIR16| OP_IFP	| STORE_CALL)	 	; F4 nn nn	CALL P,nn
.dw (FETCH_AF	| OP_PUSH16	| STORE_NOP)	 	; F5		PUSH AF
.dw (FETCH_DIR8	| OP_ORA	| STORE_A  )	 	; F6 nn	OR n
.dw (FETCH_RST	| OP_NOP	| STORE_CALL)	 	; F7		RST 30H
.dw (FETCH_NOP	| OP_IFM	| STORE_RET)	 	; F8		RET M
.dw (FETCH_HL	| OP_NOP	| STORE_SP )	 		; F9		LD SP,HL
.dw (FETCH_DIR16| OP_IFM	| STORE_PC )	 	; FA nn nn	JP M,nn
.dw (FETCH_NOP	| OP_EI 	| STORE_NOP)	 	; FB		EI
.dw (FETCH_DIR16| OP_IFM	| STORE_CALL)	 	; FC nn nn	CALL M,nn
.dw (FETCH_NOP	| OP_INV	| STORE_NOP)	 	; FD 		(Z80 specific)
.dw (FETCH_DIR8	| OP_SUBFA	| STORE_NOP)	; FE nn	CP n
.dw (FETCH_RST	| OP_NOP	| STORE_CALL)	 	; FF		RST 38H

So this is a very quick introduction to a generic technique to implementing an emulator. It could be translated to Gigatron assembly, vCPU or in C code The 8080 example implemented on an AVR is just 2252 lines of AVR assembly code (about 4500 bytes).

Thanks to Sprite_Mods for making this code available - you can find the full project listing on my Github repository here:

https://github.com/monsonite/AVR_8080_e ... /tree/main

For a write up of the AVR implementation - see this link:

https://spritesmods.com/?art=avrcpm
Post Reply