-
Notifications
You must be signed in to change notification settings - Fork 1
/
bankswitch.s
344 lines (318 loc) · 9.8 KB
/
bankswitch.s
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
.module bankswitch
.globl _bankswitch_get_current_bank
.globl _bankswitch_get_rom_bank_count
.globl _flashrom_chip_read_bankswitch
.globl _flashrom_chip_write_bankswitch
.globl _flashrom_block_read_bankswitch
.globl _flashrom_block_write_bankswitch
.globl _flashrom_block_verify_bankswitch
.globl _default_mem_bank
.globl _bank_switch_method
.globl _una_entry_vector
.globl _bankswitch_check_irq_flag
.globl _irq_enabled_flag
; RomWBW entry vectors
ROMWBW_OLD_SETBNK .equ 0xFC06 ; prior to v2.6
ROMWBW_OLD_GETBNK .equ 0xFC09 ; prior to v2.6
ROMWBW_SETBNK .equ 0xFFF3 ; v2.6 and later (function vector)
ROMWBW_CURBNK .equ 0xFFE0 ; v2.6 and later (byte variable)
ROMWBW_MEMINFO .equ 0xF8F1 ; v2.6 and later
; UNA BIOS banked memory functions
UNABIOS_ENTRY .equ 0x08 ; entry vector
UNABIOS_BANKEDMEM .equ 0xFB ; C register - function number
UNABIOS_BANK_GET .equ 0x00 ; B register - subfunction number
UNABIOS_BANK_SET .equ 0x01 ; B register - subfunction number
; P112 Z80182 hardware control registers
P112_DCNTL .equ 0x32 ; Device control register
P112_BBR .equ 0x39 ; Bank base register
P112_SCR .equ 0xEF ; System config register
; N8VEM SBC hardware control registers
N8VEM_MPCL_RAM .equ 0x78 ; IO address of RAM memory pager configuration latch
N8VEM_MPCL_ROM .equ 0x7C ; IO address of ROM memory pager configuration latch
.area _CODE
.z180
; load the page in register HL into the banked region (lower 32K)
loadbank:
ld a, (_bank_switch_method)
or a
jr z, loadbank_romwbw_old
dec a
jr z, loadbank_unabios
dec a
jr z, loadbank_p112
dec a
jr z, loadbank_romwbw_26
dec a
jr z, loadbank_n8vem_sbc
; well, this is unexpected
ret
loadbank_n8vem_sbc:
ld a, l
out (N8VEM_MPCL_ROM), a
out (N8VEM_MPCL_RAM), a
ret
loadbank_romwbw_old:
ld a, l
call #ROMWBW_OLD_SETBNK
ret
loadbank_romwbw_26:
ld a, l
call #ROMWBW_SETBNK
ret
loadbank_unabios:
; This is slightly tricky. Normally we'd enter UNA via the RST 8 call,
; but we can't do this as we're going to replace the lower part of memory
; with flash ROM contents, that does not have the RST 8 vector in place.
ld bc, #(UNABIOS_BANK_SET << 8 | UNABIOS_BANKEDMEM)
ex de, hl ; move page number into DE
ld hl, #loadbank_una_return
push hl ; put return address on stack
ld hl, (_una_entry_vector)
jp (hl) ; simulate call to entry vector
loadbank_una_return:
ret
loadbank_p112:
; map in the EEPROM if HL==0, else unmap it
ld a, h
or l
jr nz, unmap_p112_rom
map_p112_rom:
xor a
out0 (P112_BBR), a ; map ROM into banked area
in0 a, (P112_SCR)
and a, #0xf7 ; enable EEPROM
out0 (P112_SCR), a
in0 a, (P112_DCNTL)
or #0xc0 ; set 3 memory wait states
out0 (P112_DCNTL), a
ret
unmap_p112_rom:
out0 (P112_BBR), l
in0 a, (P112_SCR)
or a, #0x08 ; disable EEPROM
out0 (P112_SCR), a
out0 (P112_DCNTL), h
ret
_bankswitch_get_rom_bank_count:
ld a, (_bank_switch_method)
cp #3 ; romwbw 2.6+?
jr nz, retzero ; return 0 if not
ld bc, #ROMWBW_MEMINFO ; SYSGET MEMINFO
rst 8 ; call into RomWBW
or a ; A=0?
jr nz, retzero ; something went wrong
ld h, #0
ld l, d ; return number of ROM banks in HL
ret
; return the currently loaded page number
_bankswitch_get_current_bank:
ld a, (_bank_switch_method)
or a
jr z, getbank_romwbw_old
dec a
jr z, getbank_unabios
dec a
jr z, getbank_p112
dec a
jr z, getbank_romwbw_26
dec a
jr z, getbank_n8vem_sbc
; well, this is unexpected
retzero:
ld hl, #0
ret
getbank_n8vem_sbc:
ld hl, #0x0080 ; we assume that it's the first page of RAM
ret
getbank_romwbw_old:
call #ROMWBW_OLD_GETBNK
; returns page number in A
ld h, #0
ld l, a
ret
getbank_romwbw_26:
ld a, (ROMWBW_CURBNK)
ld h, #0
ld l, a
ret
getbank_unabios:
ld bc, #(UNABIOS_BANK_GET << 8 | UNABIOS_BANKEDMEM)
ld hl, #getbank_una_return
push hl ; put return address on stack
ld hl, (_una_entry_vector)
jp (hl) ; simulate call to entry vector
getbank_una_return:
; returns page number in DE
ex de, hl
ret
getbank_p112:
in0 h, (P112_DCNTL)
in0 l, (P112_BBR)
ret
selectaddr:
; compute bank number, disable interrupts, select it
; 32-bit address is at sp+4 through sp+7
; The code below is limited to 256 x 32KB banks = 8MB
; eg for flash address = 0x654321:
; mem addr: SP+4 SP+5 SP+6 SP+7
; contents: 21 43 65 00
; bank number = address >> 15 = 0xCA -- data in SP+6 (7 bits), SP+5 (1 bit)
; bank offset = address & 0x7FFF = 0x4321 -- data in SP+5 (7 bits), SP+4 (8 bits)
; first compute bank number
ld hl,#6
add hl,sp ; HL is now SP+6
ld a, (hl) ; 7 bits we want are in here
add a, a ; shift left one place
dec hl ; HL is now SP+5
bit 7, (hl) ; test top bit
jr z, bankready
or #1 ; set low bit if required
bankready:
; now A contains desired bank number -- let's select that bank!
push hl ; stash this, we'll need it in a moment
ld h, #0 ; zero high byte
ld l, a ; bank number now in HL
di ; disable interrupts; the vector or ISR may be in banked memory
call loadbank ; switch memory bank
pop hl ; HL is now SP+5 again
; now compute bank offset
ld d, (hl) ; load top byte of the word
res 7, d ; clear out the top bit (already used for the bank number)
dec hl ; HL is now SP+4
ld e, (hl) ; load low byte of the word
ret ; return with bank selected, DE = offset (0--0x7FFF), HL = SP+4
_flashrom_chip_read_bankswitch:
call selectaddr
ld a, (de) ; read from the flash in banked memory
ld l, a
jr putback
_flashrom_chip_write_bankswitch:
call selectaddr
inc hl
inc hl
inc hl
inc hl ; hl is now sp+6 where the value to write resides
ld a, (hl)
ld (de), a ; write to flash memory
jr putback
targetlength:
ex de, hl ; banked flash address -> hl
push ix
ld ix, #0
add ix, sp
ld e, 10(ix) ; buffer address -> de
ld d, 11(ix)
ld c, 12(ix) ; length -> bc
ld b, 13(ix)
pop ix
ret
_flashrom_block_read_bankswitch:
call selectaddr
call targetlength
ldir ; copy copy copy
; fall through to putback
putback:
; restore original memory bank, restore interrupt flag
push hl
; put our memory back in the banked region
ld hl, (_default_mem_bank)
call loadbank
pop hl ; recover value read from flash
ld a, (_irq_enabled_flag)
bit 0, a
ret z ; return now if IRQs were not previously enabled
ei ; re-enable interrupts
ret ; return
_flashrom_block_verify_bankswitch:
call selectaddr
call targetlength
; DE = source address in RAM (pointer into buffer)
; HL = destination address in flash (pointer into banked memory)
; BC = byte counter (# bytes remaining to program)
cmpnext:
ld a, b
or c
jr z, cmpok
ld a, (de)
cp (hl)
jr nz, cmpfail
inc de
inc hl
dec bc
jr cmpnext
cmpok:
ld l, #1 ; return true
jr putback
cmpfail:
ld l, #0 ; return false
jr putback
_flashrom_block_write_bankswitch:
call selectaddr
call targetlength
writenext:
; program a range of bytes in flash;
; DE = source address in RAM (pointer into buffer)
; HL = destination address in flash (pointer into banked memory)
; BC = byte counter (# bytes remaining to program)
; put flash into write mode
ld a, #0xaa
ld (0x5555), a
ld a, #0x55
ld (0x2aaa), a
ld a, #0xa0
ld (0x5555), a
; program the byte
ld a, (de)
ld (hl), a
; partial setup for the next byte while the write occurs
nextbyte:
dec bc
inc de
; check if the next byte is 0xff -- we can skip it (0xff = erased)
ld a, (de)
inc a
jr nz, writewait ; only 0xff + 1 = 0
ld a, b ; but are there any bytes left to write?
or c
jr z, writewait ; if this was the last byte, complete normally
inc hl ; data sheet does not indicate we must read status from the same address
jr nextbyte ; okay, we can safely skip this byte!
; wait for programming to complete
writewait:
ld a, (hl)
cp (hl)
jr nz, writewait
; data sheet advises double checking with two further reads
ld a, (hl)
cp (hl)
jr nz, writewait
; finish setup for the next byte
inc hl ; do not do this earlier, to ensure we stay in the ROM address space
ld a, b
or c
jr nz, writenext
jr putback
; determine if IRQs are enabled -- based on Z80 Family Q&A
; page 3-131 http://z80.info/zip/ZilogProductSpecsDatabook129-143.pdf
; NB this will NOT work if located at addresses 0x0000-0x00FF.
_bankswitch_check_irq_flag:
xor a ; NMOS Z80 bug work around as per CPU manual
push af
pop af ; clear byte on stack below our usage
ld a, i ; during LD A, I instruction the P/V flag is set with the value of IFF2
jp pe, irq_ena ; P/V is now IFF2, if irqs on return is safe
dec sp ; the CPU may have lied due to an erratum
dec sp
pop af ; see if anyone pushed a return address
and a
jr nz, irq_ena ; someone did - IRQs were enabled then
; if we get here, IRQs are disabled
xor a
jr irqdone
irq_ena:
ld a, #0x01
irqdone:
ld (_irq_enabled_flag), a
ret
.area _DATA
_irq_enabled_flag: .ds 1