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
|
; Fenders "3-sector" loader disassembly, 20070526 bkw
; Double-density version
; Note: the double-density loader doesn't actually fit in 3 sectors.
; It uses sectors 1-3 and 720.
; First 3 sectors of a DD disk are still only 128 bytes/sector.
; At boot, the OS boot code loads the first 3 sectors and jumps to
; the loaded code... which then loads sector 720 (a proper 256-byte DD
; sector), which contains the rest of the code.
; I haven't done a very thorough job of reverse-engineering the DD
; version of the loader. Its structure is similar to that of the SD
; loader (fenders.dasm).
processor 6502
;;; Equates:
;; OS ROM entry points
SIOV .equ $e459
COLDSV .equ $e477
KEYBDV .equ $e420 ; K: handler device table
keyb_get_lo .equ KEYBDV+4 ; pointer to "get byte" routine, minus 1
keyb_get_hi .equ KEYBDV+5 ; (used by get_key)
;; OS zero page
BOOTQ .equ $09
SAVMSC .equ $58
ZROFRE .equ $80
;; OS and FMS page 2 RAM variables
COLDST .equ $0244
SDMCTL .equ $022f
SDLSTL .equ $0230
SDLSTH .equ $0231
RUNAD .equ $02e0
INITAD .equ $02e2
;; DCB, used for sector I/O parameters by SIOV (called by read_sector)
DDEVIC .equ $0300
DUNIT .equ $0301
DCOMND .equ $0302
DSTATS .equ $0303
DBUFLO .equ $0304
DBUFHI .equ $0305
DTIMLO .equ $0306
DBYTLO .equ $0308
DBYTHI .equ $0309
DAUX1 .equ $030a
DAUX2 .equ $030b
;; Hardware registers
COLPF1 .equ $d017
VCOUNT .equ $d40b
;; Local variables (zero page)
end_address .equ $45
save_pos .equ $49
menu_counter .equ $b0
dir_sector_lo .equ $b1
tmp_dlistl .equ $b2
tmp_dlisth .equ $b3
menu_ptr_lo .equ $b4
menu_ptr_hi .equ $b5
dest_ptr .equ $43
start_sector_lo_tbl .equ $c0
start_sector_hi_tbl .equ $e0
;; Local variables (non zero page)
buffer .equ $0b10
sector_link_hi .equ $0c0d ; buffer + $fd
sector_link_lo .equ $0c0e ; buffer + $fe
sector_byte_count .equ $0c0f ; buffer + $ff
;;; Bootable disk image starts here:
.org $0700
;;; Standard Atari boot disk header (6 bytes)
boot_record:
.byte $00 ; ignored
.byte $03 ; number of sectors to read
.word boot_record ; load address
.word COLDSV ; init address, don't think this gets used
;;; Actual code starts here:
boot_continuation:
OFFSET_COLDST_1_DD .equ *-boot_record+1
OFFSET_COLDST_2_DD .equ *-boot_record+5
LDY #$00 ; 0 .
STY COLDST
INY
STY BOOTQ
STY DUNIT
DEC DTIMLO
set_dbl_density:
LDA #$4e ; 78 N
STA DCOMND
LDA #$40 ; 64 @
STA DSTATS
LDA #$0c ; 12 .
STA DBYTLO
LDA #$00 ; 0 .
STA DBYTHI
LDA #<buffer
STA DBUFLO
LDA #>buffer
STA DBUFHI
JSR SIOV
BMI set_dbl_density
LDA #$04 ; 4 .
STA buffer+5
LDA #$01 ; 1 .
STA buffer+6
LDA #$00 ; 0 .
STA buffer+7
LDA #$4f ; 79 O
STA DCOMND
LDA #$80 ; 128 .
STA DSTATS
JSR SIOV
BMI set_dbl_density
; The bootloader code is 640 bytes long. First 384 bytes were loaded
; from the 3 boot sectors already; the rest lives in sector 720, which
; we have to load before running it:
read_sec_720:
LDA #$52 ; 82 R
STA DCOMND
LDA #$40 ; 64 @
STA DSTATS
LDA #$80 ; 128 .
STA DBUFLO
LDA #$08 ; 8 .
STA DBUFHI
LDA #$00 ; 0 .
STA DBYTLO
LDA #$01 ; 1 .
STA DBYTHI
LDA #$d0 ; 208 .
STA DAUX1
LDA #$02 ; 2 .
STA DAUX2
JSR SIOV
BMI read_sec_720
; setup display list
LDA SDLSTL
STA tmp_dlistl
LDA SDLSTH
STA tmp_dlisth
LDA #$00 ; 0 .
STA SDMCTL
LDA #<display_list ; 58 :
STA SDLSTL
LDA #>display_list ; 9 .
STA SDLSTH
; init menu
LDA #$6c ; 108 l
STA menu_ptr_lo
LDA #$09 ; 9 .
STA menu_ptr_hi
LDA #$69 ; 105 i
STA dir_sector_lo
read_dir_sector:
LDA dir_sector_lo
STA DAUX1
LDA #$01 ; 1 .
STA DAUX2
JSR read_sector
INC dir_sector_lo
DEX
do_dirent:
LDA buffer,X
BEQ dir_done
BMI next_dirent
AND #$01 ; 1 .
BNE next_dirent
INC menu_counter
LDY menu_counter
LDA buffer+3,X
STA start_sector_lo_tbl,Y
LDA buffer+4,X
STA start_sector_hi_tbl,Y
TYA
CLC
ADC #$a0 ; 160 .
LDY #$03 ; 3 .
STA (menu_ptr_lo),Y
INY
LDA #$8e ; 142 .
STA (menu_ptr_lo),Y
INY
next_char:
INY
LDA buffer+5,X
INX
SEC
SBC #$20 ; 32
STA (menu_ptr_lo),Y
CPY #$10 ; 16 .
BNE next_char
CLC
LDA menu_ptr_lo
ADC #$14 ; 20 .
STA menu_ptr_lo
BCC skip_ptr_hi
INC menu_ptr_hi
skip_ptr_hi:
LDA menu_counter
CMP #$14 ; 20 .
BEQ dir_done
next_dirent:
TXA
AND #$f0 ; 240 .
CLC
ADC #$10 ; 16 .
TAX
ASL
BCC do_dirent
BCS read_dir_sector
dir_done:
LDA #$22 ; 34 "
STA SDMCTL
wait_vcount_0:
LDA VCOUNT
BNE wait_vcount_0
wait_for_input:
JSR get_key
SEC
SBC #$40 ; 64 @
CMP menu_counter
BEQ load_file
BCS wait_for_input
load_file:
TAX
LDA start_sector_lo_tbl,X
STA DAUX1
LDA start_sector_hi_tbl,X
STA DAUX2
LDA #$68 ; 104 h
STA menu_ptr_lo
LDA #$09 ; 9 .
STA menu_ptr_hi
L0834:
DEX
BEQ print_loading_msg
CLC
LDA menu_ptr_lo
ADC #$14 ; 20 .
STA menu_ptr_lo
BCC L0834
INC menu_ptr_hi
BNE L0834
print_loading_msg:
LDY #$00 ; 0 .
next_msg_byte:
LDA loading_msg,Y
STA (SAVMSC),Y
INY
CPY #$09 ; 9 .
BNE next_msg_byte
print_filename:
LDA (menu_ptr_lo),Y
STA (SAVMSC),Y
INY
CPY #$15 ; 21 .
BNE print_filename
LDA #$00 ; 0 .
STA SDMCTL
LDA tmp_dlistl
STA SDLSTL
LDA tmp_dlisth
STA SDLSTH
LDA #$22 ; 34 "
STA SDMCTL
wait_vcount_again:
LDA VCOUNT
BNE wait_vcount_again
LDY #$00 ; 0 .
TYA
clear_zp:
STA ZROFRE,Y
INY
BPL clear_zp
; the "JSR try_read" below MUST be located at $087f.
; The JSR opcode is the last byte loaded in the 3-sector boot loader,
; and its operand is the first 2 bytes loaded from sector 720!
.if *<>$087f
.echo "Code offsets have changed, fix me (", *, "should be $087f)"
.err
.endif
JSR try_read ; cut here!
DEX
read_segment:
JSR get_next_byte
STA dest_ptr
JSR get_next_byte
STA dest_ptr+1
AND dest_ptr
CMP #$ff ; 255 .
BEQ read_segment
JSR get_next_byte
STA end_address
JSR get_next_byte
STA end_address+1
load_byte:
JSR get_next_byte
STA (dest_ptr),Y
INC dest_ptr
BNE check_seg_done
INC dest_ptr+1
BEQ check_for_init
check_seg_done:
LDA end_address
CMP dest_ptr
LDA end_address+1
SBC dest_ptr+1
BCS load_byte
check_for_init:
LDA INITAD
ORA INITAD+1
BEQ read_segment
STX save_pos
JSR do_init
LDX save_pos
LDY #$00 ; 0 .
STY INITAD
STY INITAD+1
BEQ read_segment
do_init:
JMP (INITAD)
get_next_byte:
; self-modifying code changes immediate CPX operand
CPX #$fd ; 253 .
BNE return_next_byte
LDA DAUX1
ORA DAUX2
BNE try_read
OFFSET_SCREENOFF_DD .equ *-boot_record
LDA SDMCTL
JMP (RUNAD)
read_sector:
LDA #$31 ; 49 1
STA DDEVIC
LDA #$52 ; 82 R
STA DCOMND
LDA #<buffer ; 16 .
STA DBUFLO
LDA #>buffer ; 11 .
STA DBUFHI
LDA #$00 ; 0 .
STA DBYTLO
LDA #$01 ; 1 .
STA DBYTHI
try_read:
LDA #$40 ; 64 @
STA DSTATS
JSR SIOV
BMI try_read
LDA sector_link_hi
AND #$03 ; 3 .
STA DAUX2
LDA sector_link_lo
STA DAUX1
OFFSET_ROTCOLOR_DD .equ *-boot_record
LDA COLPF1
LDA sector_byte_count
STA get_next_byte+1
LDY #$00 ; 0 .
LDX #$00 ; 0 .
return_next_byte:
LDA buffer,X
INX
RTS
get_key:
LDA keyb_get_hi
PHA
LDA keyb_get_lo
PHA
RTS
loading_msg:
.byte $00,$00,$2c,$6f,$61,$64,$69,$6e ; "..,oadin"
.byte $67,$00,$00 ; "g.."
display_list:
.byte $70,$70,$70,$47 ; "pppG"
.byte <screen,>screen
.byte $70,$06,$06,$06,$06,$06,$06 ; "p......"
.byte $06,$06,$06,$06,$06,$06,$06,$06 ; "........"
.byte $06,$06,$06,$06,$06,$06,$41 ; "......A"
.byte <display_list,>display_list
OFFSET_TITLE_DD .equ *-boot_record
screen:
.byte $00,$00,$00,$00,$61,$74,$61 ; ".....ata"
.byte $72,$69,$00,$61,$72,$63,$61,$64 ; "ri.arcad"
.byte $65,$00,$00,$00,$00,$00,$00,$00 ; "e......."
.byte $00,$00,$00,$00,$00,$00,$00,$00 ; "........"
.byte $00,$00,$00,$00,$00,$00,$00,$00 ; "........"
.byte $00 ; "."
|