-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathflashio.6502
421 lines (384 loc) · 9.05 KB
/
flashio.6502
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
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
\ Common routines for programming and erasing the SST39SF010 flash IC in a
\ MultiROM cartridge. Should work with any SST39SF010-based hardware that has
\ A0-A13 connected 'normally' and A14 connected to the low bit of ROMSEL.
\ ZP locations:
\ romsav (1 byte) - saved ROM number
\ romptr (2 bytes) - pointer into ROM
\ Before including this file, set these 'ENABLE_<routinename>' variables to TRUE
\ for routines that you wish to call.
ENABLE_ERASE_CHIP =? FALSE
ENABLE_ERASE_ROM =? FALSE
ENABLE_ERASE_SECTOR =? ENABLE_ERASE_ROM
ENABLE_PRINT_ROMS =? FALSE
ENABLE_PRINT_ROMSTR =? ENABLE_PRINT_ROMS
ENABLE_READ_BYTE =? ENABLE_PRINT_ROMSTR
ENABLE_READ_ID =? ENABLE_PRINT_ROMS
ENABLE_ROM_VALID =? ENABLE_PRINT_ROMS
ENABLE_WRITE_BYTE =? FALSE
DEBUG_ID = FALSE
IF ENABLE_READ_ID
\ Read ROM ID bytes into A and Y
\ Arguments: X - ROM to select
\ Returns: A - manufacturer ID
\ Y - product ID
\
\ Uses the (I think) invalid manufacturer ID &00 to indicate an error:
\ Product ID &00 - ROM did not respond to query command
\ Product ID &01 - 'ROM' is actually Sideways RAM
.read_id
{
JSR prologue
TXA
JSR select_rom
\ Check for sideways RAM before we try writing!
LDA &8000
EOR #&FF
STA &8000
CMP &8000
BEQ ram
LDA #&90:JSR command \ Enter ID mode
LDA &8000 \ Read manufacturer ID byte
LDY &8001 \ Read product ID byte
PHA
LDA #&F0:JSR command \ Exit ID mode
PLA
\ Check if the first two bytes of the ROM match the ID bytes we just read
CMP &8000
BNE got_id
CPY &8001
BNE got_id
\... if so, we're almost certainly not looking at a valid flash IC. Return an
\ invalid ID of 00,00
IF DEBUG_ID = FALSE
LDA #$00
LDY #$00
ENDIF
.got_id
JMP epilogue
.ram
\ Oops, it was actually RAM! Restore the byte we clobbered and return an
\ invalid ID of 00,01
EOR #&FF
STA &8000
LDA #$00
LDY #$01
JMP epilogue
}
ENDIF
IF ENABLE_ERASE_SECTOR
\ Erase a single 4K sector, pointed to by romptr
\ Arguments: X - ROM to select
\ romptr (zero page) - address of sector to erase
\ Destroys: A, Y
.erase_sector
{
JSR prologue
TXA
JSR select_rom
LDA #&80:JSR command
JSR magic
TXA
JSR select_rom
LDA #&30
LDY #0
STA (romptr), Y
JMP wait_complete
}
ENDIF
IF ENABLE_ERASE_ROM
\ Erase an entire ROM
\ Arguments: X - ROM to select
\ Destroys: A, Y, romptr
.erase_rom
{
LDA #&00
STA romptr
LDA #&80
STA romptr+1
JSR erase_sector
CLC:LDA romptr+1:ADC #&10:STA romptr+1
JSR erase_sector
CLC:LDA romptr+1:ADC #&10:STA romptr+1
JSR erase_sector
CLC:LDA romptr+1:ADC #&10:STA romptr+1
JMP erase_sector
}
ENDIF
IF ENABLE_ERASE_CHIP
\ Erase the whole chip
\ Arguments: X - ROM to select
\ Destroys: A, X
.erase_chip
{
JSR prologue
TXA
JSR select_rom
LDA #&80:JSR command
LDA #&10:JSR command
JMP wait_complete
}
ENDIF
IF ENABLE_WRITE_BYTE
\ Write a byte to (romptr),y
\ Arguments: A - byte to write
\ X - ROM to select
\ (romptr), Y - address to write
\ Destroys: A
\ Preserves: X, Y
.write_byte
{
PHA
JSR prologue
TXA
JSR select_rom
LDA #&A0
JSR command
TXA
JSR select_rom
PLA
STA (romptr),Y
\ Fall through to wait_complete
}
ENDIF
IF ENABLE_ERASE_CHIP OR ENABLE_ERASE_ROM OR ENABLE_ERASE_SECTOR OR ENABLE_WRITE_BYTE
\ SST39SF010 indicates an operation in progress by toggling bit 6 on every
\ subsequent read. Repeatedly read from ROM until we get the same value twice.
\ Preserves: X, Y
\ Destroys: A
.wait_complete
{
.loop
LDA &8000
CMP &8000
BNE loop
JMP epilogue
}
ENDIF
IF ENABLE_READ_BYTE
\ Read a byte from (romptr),y
\ Arguments: X - ROM to select
\ (romptr), Y - address to write
\ Returns: A - byte from ROM
\ Preserves: X, Y
.read_byte
{
PHA
JSR prologue
TXA
JSR select_rom
PLA
LDA (romptr),Y
JMP epilogue
}
ENDIF
IF ENABLE_PRINT_ROMS
\ Print list of ROMs, formatted as a table
\ Destroys: A, X, Y, romptr
.print_roms
{
LDA #&00:STA romptr
LDA #&80:STA romptr+1
\ Loop through all ROMs and print ROM #, type, rom name, version string (if present), and version byte
LDX #&0F
.loop
TXA:PHA
JSR print_hex \ Print ROM #
JSR print_space
JSR read_id \ Read ROM ID bytes
IF DEBUG_ID
PHA
JSR print_byte
JSR print_space
TYA
JSR print_byte
JSR print_space
PLA
ENDIF
CMP #0:BNE checkflash \ mfg ID nonzero, check if we're a flash chip
CPY #1:BEQ ram \ mfg ID 0, product ID 1 = writeable memory
PLA:TAX:PHA
JSR rom_valid:BNE empty \ Otherwise, we're rom, check if the ROM is valid
LDA #'R':JMP print_type
.checkflash
CMP #&BF:BNE unknown
CPY #&B5:BNE unknown
LDA #'F':JMP print_type \ We only consider SST39SF010 (ID byte &BF &BE) to be Flash
.ram
LDA #'W':JMP print_type
.unknown
JSR rom_valid:BNE empty \ Check for a rom signature; a bogus ID could be
LDA #'?':JMP print_type \ just noise from an undriven bus
.empty
LDA #'-'
.print_type
JSR osasci
PLA:TAX
JSR rom_valid:BNE nextrom \ No point printing name string for invalid ROMs
\ Side-effect of rom_valid call saves offset of copyright string into zeropage
JSR print_space
LDY #&08
JSR read_byte
JSR print_byte \ Byte at offset &08 is a binary version number
JSR print_space
LDY #&09
JSR print_romstr \ String at offset &09 is ROM name
CPY copyright_offset \ Check if we have reached the offset of the copyright string yet
BEQ nextrom \...If yes, we're done
INY \...If not, there's a second string with version info
JSR print_space
JSR print_romstr
.nextrom
JSR osnewl
DEX
BPL loop
RTS
}
\ Print a space
\ Preserves: X, Y
\ Destroys: A
.print_space
LDA #' ':JMP osasci
\ Print a byte as hex
\ Arguments: A - byte to print
\ Preserves: X, Y
\ Destroys: A
.print_byte
PHA:LSR A:LSR A:LSR A:LSR A:JSR print_hex
PLA
.print_hex
{
AND #&0F
ORA #'0'
CMP #':'
BCC out
ADC #$06
.out
JMP osasci
}
ENDIF
IF ENABLE_PRINT_ROMSTR
\ Print a string from ROM, stopping at the first control character
\ Arguments: (romptr), Y - start address of string
\ X - ROM number
\ Returns: Y incremented to end of string
\ Preserves: X, romptr
\ Destroys: A
.print_romstr
{
JSR read_byte
CMP #&20:BMI done \ Stop as soon as we hit a control char
CMP #&7F:BPL skip_char \ Skip chars &7F and above
JSR osasci
.skip_char
INY
BNE print_romstr
.done
RTS
}
ENDIF
IF ENABLE_ROM_VALID
\ Check for a valid ROM (existence of copyright string)
\ Arguments: X - ROM number
\ romptr must be &8000
\ Returns: A=0 if valid, nonzero if invalid
\ copyright_offset = contents of offset &07
\ Preserves: X, romptr
\ Destroys: Y
.rom_valid
{
LDY #&07:JSR read_byte \ Offset &07 = start of copyright string
STA copyright_offset
TAY:JSR read_byte:CMP #0 :BNE done \ Copyright string must start with NUL + '(C)'
INY:JSR read_byte:CMP #'(':BNE done
INY:JSR read_byte:CMP #'C':BNE done
INY:JSR read_byte:CMP #')':BNE done
.done
RTS
}
ENDIF
\*******************************************************************************
\ Private routines used within this source file.
\ Disable interrupts and save current ROM
\ Preserves: A, X, Y
.prologue
{
PHA
SEI
LDA romzp
STA romsav
PLA
RTS
}
\ Re-enable interrupts and page original ROM back in
\ Preserves: A, X, Y
.epilogue
{
PHA
LDA romsav
JSR select_rom
CLI
PLA
RTS
}
\ Page in a ROM
\ Arguments: A - rom number to page in
\ Preserves: A, X, Y
.select_rom
{
\ Workaround for Electron's odd handling of ROM selection
\ To correctly select ROMs < 8, we must first select a ROM between &C and &F.
\ For explanation see https://beebwiki.mdfs.net/Paged_ROM#Electron and
\ https://stardot.org.uk/forums/viewtopic.php?p=6681#6681
CMP #&08:BCS do_select
PHA
LDA #&0F:STA romzp:STA romsel
PLA
.do_select
STA romzp
.*romsel_sta \ export this label so that we can patch the address of ROMSEL on Electron
STA romsel
RTS
}
\ Select low ROM
\ Preserves: X, Y
\ Destroys: A
.low_rom
{
LDA romzp
AND #&FE
JSR select_rom
RTS
}
\ Select high ROM
\ Preserves: X, Y
\ Destroys: A
.high_rom
{
LDA romzp
ORA #&01
JSR select_rom
RTS
}
\ Send 'magic write' sequence to the currently paged-in ROM
\ Preserves: X, Y
\ Destroys: A
.magic
{
JSR high_rom
LDA #&AA:STA &9555 \ Write &AA to &5555
JSR low_rom
LDA #&55:STA &AAAA \ Write &55 to &2AAA
RTS
}
\ Send a command to the currently paged-in ROM
\ Arguments: A - command byte to send
\ Preserves: A, X, Y
.command
{
PHA
JSR magic \ Write &AA to &5555, &55 to &2AAA
JSR high_rom
PLA
STA &9555 \ Write command byte to &5555
RTS
}