/
am9511a_isr.asm
188 lines (144 loc) · 7.17 KB
/
am9511a_isr.asm
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
;==================================================================================
;
; INTERRUPT SECTION
;
; Interrupt Service Routine for the Am9511A-1
;
; Initially called once the required operand pointers and commands are loaded
; Following calls generated by END signal whenever a single APU command is completed
; Sends a new command (with operands if needed) to the APU
;
; On interrupt exit APUStatus contains either
; __IO_APU_STATUS_BUSY = 1, and rest of APUStatus bits are invalid
; __IO_APU_STATUS_BUSY = 0, idle, and the status bits resulting from the final COMMAND
INCLUDE "config_private.inc"
SECTION code_driver
PUBLIC asm_am9511a_isr
EXTERN APUCMDOutPtr, APUPTROutPtr
EXTERN APUCMDBufUsed, APUPTRBufUsed, APUStatus, APUError
asm_am9511a_isr:
push af ; store AF, etc, so we don't clobber them
push bc
push de
push hl
am9511a_isr_entry:
ld a,(APUCMDBufUsed) ; check whether we have a command to do
or a ; zero?
jr Z,am9511a_isr_end ; if so then clean up and END
ld bc,__IO_APU_STATUS ; the address of the APU status port in BC
in a,(c) ; read the APU
and __IO_APU_STATUS_ERROR ; any errors?
call NZ,am9511a_isr_error ; then capture the error in APUError
ld hl,(APUCMDOutPtr) ; get the pointer to place where we pop the COMMAND
ld a,(hl) ; get the COMMAND byte
ld (APUStatus),a ; save the COMMAND (in APUStatus byte)
inc l ; move the COMMAND pointer low byte along, 0xFF rollover
ld (APUCMDOutPtr),hl ; write where the next byte should be popped
ld hl,APUCMDBufUsed
dec (hl) ; atomically decrement COMMAND count remaining
and $F0 ; mask only most significant nibble of COMMAND
cp __IO_APU_OP_REM ; check whether it is OPERAND removal COMMAND
jr Z,am9511a_isr_op_rem ; remove an OPERAND
cp __IO_APU_OP_ENT ; check whether it is OPERAND entry COMMAND
jr Z,am9511a_isr_op_ent ; load an OPERAND
xor a ; set PHI = crystal x 1/2 = 9.216MHz
out0 (CMR),a ; CPU Clock Multiplier Reg (CMR)
out0 (CCR),a ; CPU Control Reg (CCR)
; Am9511A-1 needs TWCS 30ns. This provides 41.7ns.
ld a,(APUStatus) ; recover the COMMAND from status byte
ld bc,__IO_APU_CONTROL ; the address of the APU control port in BC
out (c),a ; load the COMMAND, and do it
ld a,CMR_X2 ; set PHI = crystal x 2 = 36.864MHz
out0 (CMR),a ; CPU Clock Multiplier Reg (CMR)
out0 (CCR),a ; CPU Control Reg (CCR) CCR_XTAL_X2 = CMR_X2
ld hl,APUStatus ; set APUStatus to busy
ld (hl),__IO_APU_STATUS_BUSY
am9511a_isr_exit:
pop hl ; recover HL, etc
pop de
pop bc
pop af
ei ; interrupts were enabled, or we wouldn't have been here
ret ; no Z80 interrupt chaining
am9511a_isr_end: ; we've finished a COMMAND sentence
ld bc,__IO_APU_STATUS ; the address of the APU status port in BC
in a,(c) ; read the APU
tst __IO_APU_STATUS_BUSY; test the STATUS byte is valid (i.e. we're not busy)
jr NZ,am9511a_isr_end
ld (APUStatus),a ; update status byte
jr am9511a_isr_exit ; we're done here
am9511a_isr_op_ent:
ld hl,(APUPTROutPtr) ; get the pointer to where we pop OPERAND PTR
ld e,(hl) ; read the OPERAND PTR low byte from the APUPTROutPtr
inc l ; move the POINTER low byte along, 0xFF rollover
ld d,(hl) ; read the OPERAND PTR high byte from the APUPTROutPtr
inc l
ld b,(hl) ; read the BBR of OPERAND PTR to the APUPTRInPtr
inc l
ld (APUPTROutPtr),hl ; write where the next POINTER should be read
ld hl,APUPTRBufUsed ; decrement of POINTER count remaining
dec (hl)
dec (hl)
dec (hl)
ex de,hl ; move the base address of the OPERAND to HL
in0 e,(BBR) ; keep current BBR in E
out0 (BBR),b ; make the bank swap to B
xor a ; set PHI = crystal x 1/2 = 9.216MHz
out0 (CMR),a ; CPU Clock Multiplier Reg (CMR)
out0 (CCR),a ; CPU Control Reg (CCR)
; Am9511A-1 needs TWCS 30ns. This provides 41.7ns.
ld bc,__IO_APU_DATA+$0300 ; the address of the APU data port in BC
outi ; output 16 bit OPERAND to APU
ex (sp),hl ; delay for 38 cycles (5us) TWI 1.280us
ex (sp),hl
outi ; output 16 bit OPERAND to APU
ld a,(APUStatus) ; recover the COMMAND (stored in APUStatus byte)
cp __IO_APU_OP_ENT16 ; is it a 2 byte OPERAND
jr Z,am9511a_isr_op_ent16 ; yes, then skip over 32bit stuff
ex (sp),hl ; delay for 38 cycles (5us) TWI 1.280us
ex (sp),hl
outi ; output last two bytes of 32 bit OPERAND
ex (sp),hl ; delay for 38 cycles (5us) TWI 1.280us
ex (sp),hl
outi
am9511a_isr_op_ent16:
ld a,CMR_X2 ; set PHI = crystal x 2 = 36.864MHz
out0 (CMR),a ; CPU Clock Multiplier Reg (CMR)
out0 (CCR),a ; CPU Control Reg (CCR) CCR_XTAL_X2 = CMR_X2
out0 (BBR),e ; make the bank swap back
jp am9511a_isr_entry ; go back to get another COMMAND
am9511a_isr_op_rem: ; REMINDER operands removed BIG ENDIAN !!!
ld hl,(APUPTROutPtr) ; get the pointer to where we pop OPERAND PTR
ld e,(hl) ; read the OPERAND PTR low byte from the APUPTROutPtr
inc l ; move the POINTER low byte along, 0xFF rollover
ld d,(hl) ; read the OPERAND PTR high byte from the APUPTROutPtr
inc l
ld b,(hl) ; read the BBR of OPERAND PTR to the APUPTRInPtr
inc l
ld (APUPTROutPtr),hl ; write where the next POINTER should be read
ld hl,APUPTRBufUsed ; decrement of OPERAND POINTER count remaining
dec (hl)
dec (hl)
dec (hl)
ex de,hl ; move the base address of the OPERAND to HL
in0 e,(BBR) ; keep current BBR in E
out0 (BBR),b ; make the bank swap to B
ld bc,__IO_APU_DATA+$0300 ; the address of the APU data port in BC
inc hl ; reverse the OPERAND bytes to load
ld a,(APUStatus) ; recover the COMMAND (stored in APUStatus byte)
cp __IO_APU_OP_REM16 ; is it a 2 byte OPERAND
jr Z,am9511a_isr_op_rem16 ; yes, then skip over 32bit stuff
inc hl ; increment two more bytes for 32bit OPERAND
inc hl
ind ; get the higher two bytes of 32bit OPERAND
ind
am9511a_isr_op_rem16:
ind ; get 16 bit OPERAND
ind
out0 (BBR),e ; make the bank swap back
jp am9511a_isr_entry ; go back to get another COMMAND
am9511a_isr_error: ; we've an error to notify in A
ld hl,APUError ; collect any previous errors
or (hl) ; and we add any new error types
ld (hl),a ; set the APUError status
ret