;	core.s
;	------
;	Central routines for Hu6280 emulation


CPU_Debug		*	0

	GET	hdr.common
	GET	hdr.memory

	[	CPU_Debug > 0
	GET	ARM:hdr.SWINames
	]


	GBLS	core_val
	GBLS	core_val2

	MACRO
	extern	$label
	IMPORT	$label
	DCD	$label
	MEND


	AREA	|cpu$$code|, CODE, READONLY


	IMPORT	Memory_Initialise
;	IMPORT	Memory_Finalise
	IMPORT	Memory_Map
	IMPORT	keyboard

;	IMPORT	VDC_Raster


	[	CPU_Debug > 0
	IMPORT	Display_CPU_State

Debug_Initialise
	stmfd	r13!, {r14}
	swi	OS_WriteI + 22
	swi	OS_WriteI + 14
	swi	OS_RemoveCursors
	ldmfd	r13!, {pc}^

Debug_CLS
	EXPORT	Debug_CLS
	stmfd	r13!, {r14}
	swi	OS_WriteI + 12 ; CLS
	ldmfd	r13!, {pc}^

Debug_Wait
	EXPORT	Debug_Wait
	stmfd	r13!, {r14}
	swi	OS_ReadC
	ldmfd	r13!, {pc}^

Debug_Finalise
	stmfd	r13!, {r14}
	swi	OS_RestoreCursors
	ldmfd	r13!, {pc}^
	]


;	void CPU_Initialise(int rom_size)
CPU_Initialise
	EXPORT	CPU_Initialise
	stmfd	r13!, {r4-r12, r14}
	[	CPU_Debug > 0
	bl	Debug_Initialise
	]
	ldr	data, adrof_cpu_emulation_data
	ldr	r2, [data, #rom_address]
	add	wmemory, data, #wmemory_map
	mov	r_p, #Z_FLAG
	orr	r_p, r_p, #0x80 << 16
	orr	r_p, r_p, #0x80 << 24
	; inverted IRQ mask = 2_000 (mask out all IRQ's)
	; IRQ status = 2_000 (no IRQ's high)
	; inverted IRQ enabled (I_FLAG is clear)
	; bit 31 set to allow (0x40 << 16) to set/clear N flag
	add	r1, r2, #0x1f00
	ldrb	r_pc, [r1, #0xfe]
	ldrb	r1, [r1, #0xff]
	mov	r_pc, r_pc, lsl #16
	add	r_pc, r_pc, r1, lsl #24
	mov	r_a, #0x00 << 24
	mov	r_x, #0x00 << 24
	mov	r_y, #0x00 << 24
	mov	r_s, #0xff << 24
	mov	timer, #456
	add	r0, data, #cpu_registers
	orr	timer, timer, timer, lsl #16
	stmia	r0, {r_p, r_pc, r_s, r_a, r_x, r_y, timer}
	bl	Memory_Initialise
	mov	r0, #0xff
	mov	r1, #0
	bl	Memory_Map
	mov	r0, #0xf8
	mov	r1, #1
	bl	Memory_Map
	mov	r0, #0x00
	mov	r1, #2
CPU_Initialise_MMap
	bl	Memory_Map
	add	r1, r1, #1
	cmp	r1, #7
	ble	CPU_Initialise_MMap

	adr	r0, opcode_timings
	adr	r1, opcode_table
	add	r2, data, #opcode_data
	mov	r3, #256
	adr	r4, Opcode_Jump_Instruction
	add	r4, r4, #8
CPU_Initialise_Opcodes
	ldr	r5, [r1], #4
	ldrb	r6, [r0], #1
	sub	r5, r5, r4
	orr	r5, r6, r5, lsl #8
	str	r5, [r2], #4
	subs	r3, r3, #1
	bgt	CPU_Initialise_Opcodes
	ldmfd	r13!, {r4-r12, pc}^

CPU_Finalise
	EXPORT	CPU_Finalise
	stmfd	r13!, {r4-r12, r14}
	[	CPU_Debug > 0
	bl	Debug_Finalise
	]
;	ldr	memory, adrof_pce_memory
;	ldr	data, adrof_cpu_emulation_data
;	ldr	memory, [memory, #0]
;	bl	Memory_Finalise
	ldmfd	r13!, {r4-r12, pc}^


Run_CPU_Core
	EXPORT	Run_CPU_Core
	ldr	r0, adrof_cpu_stack_end
	stmfd	r0!, {r4-r14}
	ldr	data, adrof_cpu_emulation_data
	mov	r13, r0
	add	r0, data, #cpu_registers
	add	wmemory, data, #wmemory_map
	ldmia	r0, {r_p, r_pc, r_s, r_a, r_x, r_y, timer}
Next_Opcode
	EXPORT	Next_Opcode
	ands	r0, r_p, r_p, lsl #8 + 1
	bmi	Finished_T_Opcode
	bhi	Handle_Interrupt
	cmp	timer, #0
	ble	Timer_Expired
Handle_Opcode
	EXPORT	Handle_Opcode
;	NEW2	core_val, 0
;	read_byte_fast $core_val
;	DELETE	core_val
	mov	r0, r_pc, lsr #29
	ldr	r0, [rmemory, r0, lsl #2]
	add	r1, data, #opcode_data
	ldrb	r0, [r0, r_pc, lsr #16]			; get opcode
	add	r_pc, r_pc, #1 << 16
	ldr	r2, [r1, r0, lsl #2]			; get data for opcode
	tst	r_p, #T_FLAG
	mov	r1, r2, lsl #24
	sub	timer, timer, r1, lsr #24 - 16
Opcode_Jump_Instruction
	addeq	pc, pc, r2, asr #8
	ldr	r1, [data, #zp_address]
	orr	r_p, r_p, #0x40 << 16			; set attention grabbing bit
	str	r_a, [data, #tflag_real_a]
	ldrb	r_a, [r1, r_x, lsr #24]!
	str	r1, [data, #tflag_address]
	mov	r_a, r_a, lsl #24
	sub	r2, r2, #((. + 4) - Opcode_Jump_Instruction) :SHL: 8
	add	pc, pc, r2, asr #8
Finished_T_Opcode
	ldr	r0, [data, #tflag_address]
	mov	r_a, r_a, lsr #24
	bic	r_p, r_p, #T_FLAG
	strb	r_a, [r0, #0]
	bic	r_p, r_p, #0x40 << 16			; clear attention grabbing bit
	ands	r0, r_p, r_p, lsl #8 + 1		; and re-check for interrupt
	ldr	r_a, [data, #tflag_real_a]
	bhi	Handle_Interrupt			; if interrupt, do something about it!
	cmp	timer, #0				; timer expired?
	bgt	Handle_Opcode				; if not, handle the next opcode
Timer_Expired
	add	timer, timer, timer, lsl #16		; reload the timer

;	bl	VDC_Raster				; ask the VDC to draw the next line
	IMPORT	Increment_Raster
	bl	Increment_Raster

	ldr	r0, =keyboard
	ldr	r0, [r0, #0]
	cmp	r0, #0
	swine	0x11 ; OS_Exit

	; This timer code is a *HACK* to save emulating a 6.9KHz timer alongside the 7.16MHz
	; system clock.  It could be made more accurate by calculating the approximate number
	; of lines that would be drawn every 6.9KHz rather than just assuming (as the following
	; does) that two lines are drawn every 6.9KHz.  Having said that, the effect is not
	; particularly noticable...
	ldr	r1, adrof_timer_status
	ldr	r1, [r1, #0]
	tst	r1, #1
	ldrne	r1, [data, #toggling_bit]
	beq	Timer_Expired_Check_IRQs		; (VDC might generate an IRQ, so check for it)
	eors	r1, r1, #1
	ldrne	r0, adrof_timer_value
	str	r1, [data, #toggling_bit]
	ldrne	r1, [r0, #0]
	beq	Timer_Expired_Check_IRQs		; (VDC might generate an IRQ, so check for it)
	cmp	r1, #0
	ldrls	r1, adrof_timer_load
	subhi	r1, r1, #1
	ldrls	r1, [r1, #0]
	str	r1, [r0, #0]
	orrls	r_p, r_p, #TIQ << 16			; set TIQD
Timer_Expired_Check_IRQs
	ands	r0, r_p, r_p, lsl #8 + 1		; interrupt?
	bls	Handle_Opcode				; no (if HI, drop through to Handle_Interrupt)
	; Makes the assumption that (0x40 << 16) is not set which will be always be the case
	; at this point, unless it is used for something else besides mirroring T_FLAG

Handle_Interrupt
	; r0 = (irq_status AND NOT irq_mask) << 25
	mov	r0, r0, lsr #24 + 1
	bic	r0, r0, r0, lsr #1
	bic	r0, r0, r0, lsr #2			; r0 = most significant bit originally set in r0

;	ldr	r1, adrof_frame_counter
;	str	r0, [r1, #0]

	bic	r_p, r_p, r0, lsl #16			; update irq status
	sub	r_pc, r_pc, #1 << 16
	NEW2	core_val, 0
	write_word_sp r_pc
	write_byte_sp r_p
	DELETE	core_val
	ldr	r1, [rmemory, #7 * 4]
	bic	r2, r0, #1
	add	r1, r1, #0xff00
	add	r1, r1, #0x00f6
	ldrb	r_pc, [r1, r2]!
	ldrb	r1, [r1, #1]
	bic	r_p, r_p, #D_FLAG
	orr	r_p, r_p, #I_FLAG			; disable further interrupts
	bic	r_p, r_p, #0x80 << 16			; (and hence, clear inverted I_FLAG)
	mov	r_pc, r_pc, lsl #16
	add	r_pc, r_pc, r1, lsl #24
	b	Handle_Opcode

;adrof_frame_counter
;	IMPORT	frame_counter
;	DCD	frame_counter


adrof_cpu_emulation_data
	DCD	cpu_emulation_data
adrof_cpu_stack_end
	DCD	cpu_stack_end

adrof_timer_value
	IMPORT	timer_value
	DCD	timer_value
adrof_timer_load
	IMPORT	timer_load
	DCD	timer_load
adrof_timer_status
	IMPORT	timer_status
	DCD	timer_status

opcode_timings
;		0 1 2  3 4 5 6 7 8 9 A B C D E F
	=	8,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,6 ; 0
	=	2,7,7, 4,6,4,6,7,2,5,2,7,7,5,7,6 ; 1
	=	7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,6 ; 2
	=	2,7,7, 8,4,4,6,7,2,5,2,7,5,5,7,6 ; 3
	=	7,7,4, 4,8,4,6,7,3,2,2,2,4,5,7,6 ; 4
	=	2,7,2, 5,2,4,6,7,2,5,3,7,5,5,7,6 ; 5
	=	7,7,2, 8,4,4,6,7,4,2,2,2,7,5,7,6 ; 6
	=	2,7,7,17,4,4,6,7,2,9,4,7,7,5,7,6 ; 7
	=	4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6 ; 8
	=	2,7,7, 8,4,4,4,7,2,5,2,5,5,5,5,6 ; 9
	=	2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6 ; A
	=	2,7,7, 8,4,4,4,7,2,5,2,5,5,5,5,6 ; B
	=	2,7,2,17,4,4,6,7,2,2,2,2,5,5,6,6 ; C
	=	2,7,7,17,2,4,6,7,2,5,3,7,5,5,7,6 ; D
	=	2,7,2,17,4,4,6,7,2,2,2,2,5,4,7,6 ; E
	=	2,7,7,17,3,4,6,7,2,4,4,7,5,5,7,6 ; F
opcode_table
	extern	brk
	extern	ora_ix
	extern	sxy
	extern	st0
	extern	tsb_zp
	extern	ora_zp
	extern	asl_zp
	extern	rmbi0_zp
	extern	php
	extern	ora_im
	extern	asl_rg
	DCD	Next_Opcode
	extern	tsb_ab
	extern	ora_ab
	extern	asl_ab
	extern	bbr0_zp
	extern	bpl_rg
	extern	ora_iy
	extern	ora_in
	extern	st1
	extern	trb_zp
	extern	ora_zx
	extern	asl_zx
	extern	rmbi1_zp
	extern	clc
	extern	ora_ay
	extern	inc_rg
	DCD	Next_Opcode
	extern	trb_ab
	extern	ora_ax
	extern	asl_ax
	extern	bbr1_zp
	extern	jsr
	extern	and_ix
	extern	sax
	extern	st2
	extern	bit_zp
	extern	and_zp
	extern	rol_zp
	extern	rmbi2_zp
	extern	plp
	extern	and_im
	extern	rol_rg
	DCD	Next_Opcode
	extern	bit_ab
	extern	and_ab
	extern	rol_ab
	extern	bbr2_zp
	extern	bmi_rg
	extern	and_iy
	extern	and_in
	DCD	Next_Opcode
	extern	bit_zx
	extern	and_zx
	extern	rol_zx
	extern	rmbi3_zp
	extern	sec
	extern	and_ay
	extern	dec_rg
	DCD	Next_Opcode
	extern	bit_ax
	extern	and_ax
	extern	rol_ax
	extern	bbr3_zp
	extern	rti
	extern	eor_ix
	extern	say
	extern	tmai
	extern	bsr
	extern	eor_zp
	extern	lsr_zp
	extern	rmbi4_zp
	extern	pha
	extern	eor_im
	extern	lsr_rg
	DCD	Next_Opcode
	extern	jmp_ab
	extern	eor_ab
	extern	lsr_ab
	extern	bbr4_zp
	extern	bvc_rg
	extern	eor_iy
	extern	eor_in
	extern	tami
	extern	csl
	extern	eor_zx
	extern	lsr_zx
	extern	rmbi5_zp
	extern	cli
	extern	eor_ay
	extern	phy
	DCD	Next_Opcode
	DCD	Next_Opcode
	extern	eor_ax
	extern	lsr_ax
	extern	bbr5_zp
	extern	rts
	extern	adc_ix
	extern	cla
	DCD	Next_Opcode
	extern	stz_zp
	extern	adc_zp
	extern	ror_zp
	extern	rmbi6_zp
	extern	pla
	extern	adc_im
	extern	ror_rg
	DCD	Next_Opcode
	extern	jmp_in
	extern	adc_ab
	extern	ror_ab
	extern	bbr6_zp
	extern	bvs_rg
	extern	adc_iy
	extern	adc_in
	extern	tii
	extern	stz_zx
	extern	adc_zx
	extern	ror_zx
	extern	rmbi7_zp
	extern	sei
	extern	adc_ay
	extern	ply
	DCD	Next_Opcode
	extern	jmp_ix
	extern	adc_ax
	extern	ror_ax
	extern	bbr7_zp
	extern	bra_rg
	extern	sta_ix
	extern	clx
	extern	tst_zp
	extern	sty_zp
	extern	sta_zp
	extern	stx_zp
	extern	smbi0_zp
	extern	dey_rg
	extern	bit_im
	extern	txa
	DCD	Next_Opcode
	extern	sty_ab
	extern	sta_ab
	extern	stx_ab
	extern	bbs0_zp
	extern	bcc_rg
	extern	sta_iy
	extern	sta_in
	extern	tst_ab
	extern	sty_zx
	extern	sta_zx
	extern	stx_zy
	extern	smbi1_zp
	extern	tya
	extern	sta_ay
	extern	txs
	DCD	Next_Opcode
	extern	stz_ab
	extern	sta_ax
	extern	stz_ax
	extern	bbs1_zp
	extern	ldy_im
	extern	lda_ix
	extern	ldx_im
	extern	tst_zx
	extern	ldy_zp
	extern	lda_zp
	extern	ldx_zp
	extern	smbi2_zp
	extern	tay
	extern	lda_im
	extern	tax
	DCD	Next_Opcode
	extern	ldy_ab
	extern	lda_ab
	extern	ldx_ab
	extern	bbs2_zp
	extern	bcs_rg
	extern	lda_iy
	extern	lda_in
	extern	tst_ax
	extern	ldy_zx
	extern	lda_zx
	extern	ldx_zy
	extern	smbi3_zp
	extern	clv
	extern	lda_ay
	extern	tsx
	DCD	Next_Opcode
	extern	ldy_ax
	extern	lda_ax
	extern	ldx_ay
	extern	bbs3_zp
	extern	cpy_im
	extern	cmp_ix
	extern	cly
	extern	tdd
	extern	cpy_zp
	extern	cmp_zp
	extern	dec_zp
	extern	smbi4_zp
	extern	iny_rg
	extern	cmp_im
	extern	dex_rg
	DCD	Next_Opcode
	extern	cpy_ab
	extern	cmp_ab
	extern	dec_ab
	extern	bbs4_zp
	extern	bne_rg
	extern	cmp_iy
	extern	cmp_in
	extern	tin
	extern	csh
	extern	cmp_zx
	extern	dec_zx
	extern	smbi5_zp
	extern	cld
	extern	cmp_ay
	extern	phx
	DCD	Next_Opcode
	DCD	Next_Opcode
	extern	cmp_ax
	extern	dec_ax
	extern	bbs5_zp
	extern	cpx_im
	extern	sbc_ix
	DCD	Next_Opcode
	extern	tia
	extern	cpx_zp
	extern	sbc_zp
	extern	inc_zp
	extern	smbi6_zp
	extern	inx_rg
	extern	sbc_im
	extern	nop
	DCD	Next_Opcode
	extern	cpx_ab
	extern	sbc_ab
	extern	inc_ab
	extern	bbs6_zp
	extern	beq_rg
	extern	sbc_iy
	extern	sbc_in
	extern	tai
	extern	set
	extern	sbc_zx
	extern	inc_zx
	extern	smbi7_zp
	extern	sed
	extern	sbc_ay
	extern	plx
	DCD	Next_Opcode
	DCD	Next_Opcode
	extern	sbc_ax
	extern	inc_ax
	extern	bbs7_zp


	AREA	|cpu$$zidata|, NOINIT


cpu_emulation_data
	EXPORT	cpu_emulation_data
	%	data_size

	%	1024
cpu_stack_end
	EXPORT	cpu_stack_end


	END
