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 -*- asm -*-
.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 $c3 ; 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
|