summaryrefslogtreecommitdiff
path: root/interrupts.s.m4
blob: 5a47d149bc141772e51310a0cf327761d2a0c90a (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
||| interrupt handling code, in -*- gas -*-

.data
	|| 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 the emulated device doesn't want interrupts.
	|| 1 if interrupts are turned on.
int_enabled:	dc.b	1


	|| 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.
int0_opcode:	dc.b	0
		dc.b	0xc3		| JP immed.w
int0_return:	dc.w	0		| the destination address


.text
	|| This is the interrupt routine.  It can come at any point
	|| during an instruction, though routines that use a5 (e.g. by
	|| calling C subroutines) will have to turn off or hold
	|| interrupts.
	||
	|| Routines that call into TIOS will have to remove this
	|| interrupt handler.
int_handler:
	sub.l	#INT_OFFSET,a5
	rte


int_nevermind:
	rts
do_interrupt:
	add.l	#INT_OFFSET,a5		| clear the interrupt flag
	pea	0(a5,d0.w)		| allows us to rts properly

	tst.b	int_enabled		| 4 cycles
	beq.b	int_nevermind		| 8 cycles not taken
	|| Common case: interrupts enabled, fall through

	|| Since this is an instruction all its own, we have D0, D1,
	|| and D2 available.

	|| Interrupts are most often in mode 1, then mode 2, and
	|| almost never in mode 0.
	move.b	int_mode,d0
	cmpi.b	#1,d0
	beq	int_do_mode2
	cmpi.b	#2,d0
	beq	int_do_mode1
	cmpi.b	#1,d0
	beq	int_do_mode0
	rts

	|| 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 int0_opcode and set epc (or int_jump) to point
	|| there.
int_do_mode0:
	move	epc,int0_opcode
	rts

	|| This routine emulates a mode 1 interrupt.

	|| IM 1: RST 38 is executed on every interrupt.  This is what
	|| the TI-83+ uses almost all the time.
int_do_mode1:
	jmp	emu_op_ff


	|| 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