summaryrefslogtreecommitdiff
path: root/interrupts.asm
blob: 0a3fcfa7ce3bb5f37ccd1c1241ad3e40a1f3e99f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
;;; interrupt handling code

	;; Current interrupt mode.  IM 1 and friends will modify
	;; this, it can be any of 0, 1, 2.
int_mode:	dc.b	0

	;; 0 if interrupts are turned on.
	;; 1 if they are being held.
int_held:	dc.b	0

	;; 0 if no interrupt is pending.
	;; 1 if an interrupt occurred while interrupts were being
	;; held.
	;; 3 (==1|2) if an NMI is pending.
int_waiting:	dc.b	0

	;; The value of epc as a result of a held interrupt.  This is
	;; stored as a derefenced value (pointer into host memory).

	;; I store it as a native pointer because Z80 interrupts can
	;; be very strange.  Interrupt mode 0 in particular requires
	;; a shim.
int_jump:	dc.l	0

	;; In interrupt mode 0, an interrupt will force a byte onto
	;; the data bus for the processor to execute.  To handle
	;; those, the emulator will store the bus byte in int_opcode,
	;; which is followed by an absolute jump to the "next
	;; instruction". The value of int_jump will then be
	;; &int_opcode.
	;;
	;; This differs slightly from what I understand to be actual
	;; handling.  The hardware will fetch an immediate argument to
	;; the interrupting instruction from the next location in
	;; memory.
	;;
	;; This emulator, on the other hand, will fetch the immediate
	;; argument from the JP instruction in the shim, and then
	;; dance off into la-la land.
int_opcode:	dc.b	0
		dc.b	$c3		; JP immed.w
int_return:	dc.w	0		; the destination address


	;; This is a macro to hold interrupts.

	;; When interrupts are "disabled" (held), host interrupts will
	;; still fire.  The ISR will see that they are held and update
	;; the above fields with the interrupt type and location.
	;; Then the EI macro to enable them will cause it to fire.
HOLD_INTS	MACRO
	moveq.b	#1,int_held	; 4 cycles
	ENDM

	;; This is a macro to release a held interrupt.
CONTINUE_INTS	MACRO
	bsr	ints_continue	; 18 cycles
	ENDM

ints_continue:
	tst.b	int_waiting	; 4 cycles
	bne.b	ints_continue_pending ; 8 cycles not taken
	;; Common case: no interrupt pending
	moveq.b	#0,int_held	; 4 cycles
	rts			; 16 cycles
	;; typical case: 4+18+4+8+4+16 = 54 cycles
	
	;; I can go faster (24 cycles typical case) by using 68k
	;; hardware interrupt disable/reenable.
ints_continue_pending:
	subq.b	#3,int_waiting
	beq	int_do_nmi
	move.b	int_mode,



	;; This routine emulates a mode 0 interrupt.

	;; IM 0: A byte is placed on the bus and executed as if it
	;; were inline in the program.  This emulator will put that
	;; byte into int_opcode and set epc (or int_jump) to point
	;; there.
int_do_mode0:
	rts

	;; This routine emulates a mode 1 interrupt.

	;; IM 1: RST 38 is executed on every interrupt.
int_do_mode1:
	rts

	;; This routine emulates a mode 2 interrupt.

	;; IM 2: Vectored, the address jumped to is as follows:
	;;
	;; (I << 8) | (byte & 0xfe)
	;;
	;; where I is the I register, and byte is the byte that was
	;; found on the bus.
int_do_mode2:
	rts

	;; This routine emulates a non-maskable interrupt.
int_do_nmi:
	rts




	;; This routine is used by the emulated DI instruction, which
	;; turns off emulator interrupts.
ints_stop:
	rts

	;; This routine is used by the emulated EI instruction, which
	;; turns on emulator interrupts.
ints_start:
	rts