-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathide.s
529 lines (491 loc) · 13 KB
/
ide.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
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
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
;
; IDE interface driver
;
; Steve Maddison, 06/03/2007
;
; The IDE interface makes use of three I/O ports; two for the 16-bit IDE
; data bus and one for the remaining address and CS signals (referred to
; here as the "control" port).
ide_data_lsb: equ 0x40
ide_data_msb: equ 0x41
ide_control: equ 0x42
ide_dev_name_master: defm "hd0\0"
ide_dev_name_slave: defm "hd1\0"
ide_driver: defw ide_sector_read
defw ide_sector_write
defw ide_get_size
defw ide_get_buffer
; IDE Regsiters (values for the control port)
; Bit 0-2 = Addr0-2, Bit 3 = /CS0, Bit 4 = /CS1
ide_reg_data: equ 0x10 ; 0: Data port
ide_reg_error: equ 0x11 ; 1: Error code is read-only
ide_reg_sectors: equ 0x12 ; 2: Sectors to transfer
; LBA sector address is a 28-bit value made up of the following 3 bytes
; and bits 0-3 of the fourth.
ide_reg_sector_0: equ 0x13 ; 3: low byte
ide_reg_sector_1: equ 0x14 ; 4: lower-middle byte
ide_reg_sector_2: equ 0x15 ; 5: upper-middle byte
ide_reg_sector_3: equ 0x16 ; 6: bits 0-3 high nibble
ide_reg_config: equ ide_reg_sector_3
; Following register has two purposes
ide_reg_status: equ 0x17 ; When read
ide_reg_command: equ ide_reg_status ; When written
; IDE error codes.
ide_error_none: equ 0x00
ide_error_none_str: defm "No error (timeout?)\0"
ide_error_dam: equ 0x01
ide_error_dam_str: defm "DAM not found\0"
ide_error_track: equ 0x02
ide_error_track_str: defm "Track 000 not found\0"
ide_error_abort: equ 0x04
ide_error_abort_str: defm "Command aborted\0"
ide_error_id: equ 0x10
ide_error_id_str: defm "ID not found\0"
ide_error_ecc: equ 0x40
ide_error_ecc_str: defm "Unrecoverable ECC error\0"
ide_error_block: equ 0x80
ide_error_block_str: defm "Bad block detected\0"
ide_error_bogus_str: defm "Bogus error code\0"
; Table of error strings, indexed by bit set in error code.
ide_error_str_table: defw ide_error_none_str ; 0
defw ide_error_dam_str ; 1
defw ide_error_track_str ; 2
defw ide_error_bogus_str ; 3 (reserved)
defw ide_error_id_str ; 4
defw ide_error_bogus_str ; 5 (reserved)
defw ide_error_ecc_str ; 6
defw ide_error_ecc_str ; 7
; Masks for remaining bits (4-8) of register 6
ide_config_base: equ 0xa0 ; "Always on" bits
ide_config_master: equ 0x00 ; Access master device
ide_config_slave: equ 0x10 ; Access slave device
ide_config_lba: equ 0x40 ; Enable LBA
; Masks for bits in status register
ide_status_error: equ 0x01 ; Last command resulted in error
ide_status_drq: equ 0x08 ; Data Request Ready (sector buffer ready)
ide_status_df: equ 0x20 ; Write fault
ide_status_rdy: equ 0x40 ; Ready for command
ide_status_busy: equ 0x80 ; Executing command
; IDE commands
ide_cmd_read: equ 0x20 ; Read sectors with retry
ide_cmd_write: equ 0x30 ; Write sectors with retry
ide_cmd_id: equ 0xec ; Identify device
; Offsets into IDE device identification info
ide_info_name: equ 0x36
ide_info_name_len: equ 40
ide_info_size: equ 0x72 ; Two 16-bit words, least significant first
; Initialisation messages
ide_init_header: defm "IDE devices\n\0";
ide_init_master: defm " Master: \0"
ide_init_slave: defm " Slave : \0"
ide_init_none: defm "none\0"
; Timeout values used when checking device is ready.
ide_timeout_count: equ 0xffff ; Number of times to loop (16-bit)
ide_timeout_res: equ 20 ; Time to wait each loop (8-bit)
; Name: ide_block_read
; Desc: Read one block (256 words) from IDE device.
; In: HL = start of input buffer.
ide_block_read:
ld a,ide_reg_data
out (ide_control),a
push hl
pop ix
ld b,0 ; loop 256 times
ide_block_read_loop:
in a,(ide_data_lsb)
ld (ix+1),a
in a,(ide_data_msb)
ld (ix+0),a
inc ix
inc ix
djnz ide_block_read_loop
ret
; Name: ide_block_write
; Desc: Write one block (256 words) to IDE device.
; In: HL = start of output buffer.
ide_block_write:
ld a,ide_reg_data
out (ide_control),a
ld b,0 ; loop 256 times
ide_block_write_loop:
ld a,(hl)
out (ide_data_msb),a
inc hl
ld a,(hl)
out (ide_data_lsb),a
inc hl
djnz ide_block_write_loop
ret
; Name: ide_check_error
; Desc: Check for error condition.
; In: none
; Out: ZF = 1 on success, otherwise 1
; A = IDE error code.
ide_check_error:
; Fetch the status and check for error flag
call ide_get_status
and ide_status_error
jp z,ide_check_error_end
; Fetch the error code
ld a,ide_reg_error
out (ide_control),a
in a,(ide_data_lsb)
ide_check_error_end:
ret
; Name: ide_config_check
; Desc: Sanitise saved configuration parameters.
ide_config_check:
ld a,(ide_config) ; Load value for sanitising
and 0xf0 ; Clear lower nibble
or ide_config_base | ide_config_lba
ld (ide_config),a ; Save result
ret
; Name: ide_error_str
; Desc: Get error string for an IDE error code (if multiple bits are
; set, the error string corresponding to the least significant
; bit is returned).
; In: A = IDE error code
; Out: HL = Corresponding error string
ide_error_str:
push bc
ld hl,ide_error_str_table
cp 0 ; No error
jp z,ide_error_str_end
ld b,8 ; There are 8 bits to check
ide_error_str_loop:
inc hl ; Point to next error string
inc hl
cp 0x01 ; LSB set?
jp z,ide_error_str_end
rra
djnz ide_error_str_loop
ide_error_str_end:
pop bc
ret
; Name: ide_get_buffer
; Desc: Return address of internal buffer
; Out: HL = buffer address;
ide_get_buffer:
ld hl,ide_internal_buffer
ret
; Name: ide_get_id
; Desc: Fetch device identifier.
; In: HL = address of input buffer (0 for internal buffer)
; Out: ZF = 1 on success,
; CF = 1 if timed out,
; A = IDE error code,
; HL = address of input buffer
ide_get_id:
call ide_set_buffer
push hl ; Remember value
call ide_ready
jp nz,ide_get_id_end ; Timeout
call ide_config_check
ld a,ide_reg_config ; Set config
out (ide_control),a
ld a,(ide_config)
out (ide_data_lsb),a
ld a,ide_cmd_id ; Send command
call ide_send_command
call ide_ready_data ; Wait for DRQ
jp nz,ide_get_id_end ; Timeout
call ide_check_error ; Check for error
jp nz,ide_get_id_end ; Bail out - A = error code
call ide_block_read ; Read buffer
ide_get_id_end:
pop hl ; Restore value
ret
; Name: ide_get_name
; Desc: Return a trimmed, null-terminated version of the device
; name as found in the identifier info.
; In: none
; Out: ZF = 1 on success,
; A = IDE error code,
; HL = address of string
ide_get_name:
push bc
ld hl,0 ; Use internal buffer
call ide_get_id
jp nz,ide_get_name_end
jp c,ide_get_name_end
; Write a 0 after the end of the field, just in case all
; characters are in use.
ld hl,ide_info_name + ide_info_name_len + ide_internal_buffer
ld (hl),0
ld b,ide_info_name_len ; Keep count, in case the field
; is nothing but spaces!
ide_get_name_trim:
dec hl
ld a,(hl)
cp 0x20
jp nz,ide_get_name_ok
ld (hl),0
djnz ide_get_name_trim
ide_get_name_ok:
xor a ; set zero flag
; Point HL to start of string
ld hl,ide_info_name + ide_internal_buffer
ide_get_name_end:
pop bc
ret
; Name: ide_get_size
; Desc: Fetch size of device in blocks
; In: A = IDE device ID
; Out: BCDE = Number of blocks,
; ZF = 1 on success,
; A = IDE error code.
ide_get_size:
ld (ide_config),a
ld hl,0 ; Use internal buffer
call ide_get_id
jp nz,ide_get_size_end
jp c,ide_get_size_end
ld a,(ide_info_size + ide_internal_buffer)
ld d,a
ld a,(ide_info_size + ide_internal_buffer + 1)
ld e,a
ld a,(ide_info_size + ide_internal_buffer + 2)
ld b,a
ld a,(ide_info_size + ide_internal_buffer + 3)
ld c,a
xor a
ide_get_size_end:
ret
; Name: ide_get_status
; Desc: Fetch contents of the status register
; In: none
; Out: A = contents of IDE status register
ide_get_status:
ld a,ide_reg_status
out (ide_control),a
in a,(ide_data_lsb)
ret
; Name: ide_init
; Desc: Initialise and list devices
ide_init:
; Output header text
ld hl,ide_init_header
call console_outs
; Info for master
ld hl,ide_init_master
ld a,ide_config_master
call ide_init_dev
; Info for slave
ld hl,ide_init_slave
ld a,ide_config_slave
call ide_init_dev
ret
; Name: ide_init_dev
; Desc: Helper for ide_init - scans for device, then initialises it and
; prints an info line as necessary.
; In: A = IDE config parameters
; HL = Address of string constant
; IY = Device name
ide_init_dev:
push bc
push de
call console_outs
ld (ide_config),a
call ide_config_check
ld b,10 ; Try ten times
ide_init_dev_get_name:
call ide_get_name
jp z,ide_init_dev_found
djnz ide_init_dev_get_name
jp nc,ide_init_dev_error
ld hl,ide_init_none
jp ide_init_dev_print
ide_init_dev_error:
call ide_error_str
jp ide_init_dev_print
ide_init_dev_found:
push hl
; Add a new device
ld a,(ide_config)
and ide_config_slave
jp nz,ide_init_dev_slave
ld hl,ide_dev_name_master
jp ide_init_dev_add
ide_init_dev_slave:
ld hl,ide_dev_name_slave
ide_init_dev_add:
ld a,(ide_config) ; ID
ld b,dev_flag_block ; Flags
ld c,0x02 ; Block size (512 >> 8)
ld de,ide_driver ; Driver
call dev_add
pop hl
ide_init_dev_print:
call console_outs
ld a,'\n'
call console_outb
pop de
pop bc
ret
; Name: ide_ready
; Desc: Wait until device is ready.
; Out: ZF = 1 on success, 0 if timed out.
; CF = 1 on timeout
ide_ready:
push bc
; Check BUSY=0 and RDY=1
ld c,ide_status_busy & ~ide_status_rdy
call ide_wait
pop bc
ret
; Name: ide_ready_data
; Desc: Wait until device is ready to send/receive data.
; Out: ZF = 1 on success, 0 if timed out.
; CF = 1 on timeout
ide_ready_data:
push bc
; Check BUSY=0 and DRQ=1
ld c,ide_status_busy & ~ide_status_drq
call ide_wait
pop bc
ret
; Name: ide_sector_count
; Desc: Set IDE sector count register (currently always
; to "1", although this could be parameterised).
ide_sector_count:
ld a,ide_reg_sectors
out (ide_control),a
ld a,1
out (ide_data_lsb),a
ret
; Name: ide_sector_read
; Desc: Read one sector from device.
; In: A = IDE device ID
; BCDE = Sector index as per ide_sector_select,
; HL = address of input buffer (0 for internal buffer)
; Out: ZF = 1 on success,
; CF = 1 if timed out,
; A = IDE error code,
; HL = address of input buffer
ide_sector_read:
ld (ide_config),a
call ide_set_buffer
push hl ; Remember values
call ide_ready ; Wait until device is ready
jp nz,ide_sector_read_error ; Timeout
call ide_sector_select ; Program sector index
call ide_sector_count ; Set sector count
ld a,ide_cmd_read ; Send read command
call ide_send_command
call ide_ready_data ; Wait until data is ready
jp nz,ide_sector_read_error ; Timeout
call ide_check_error
jp nz,ide_sector_read_error
pop hl
push hl
call ide_block_read ; Copy the data to our buffer
ide_sector_read_error:
pop hl ; Restore value
ret
; Name: ide_sector_select
; Desc: Send LBA 28-bit sector address to the appropriate IDE registers.
; In: BCDE = sector address, E being least significant. Only the least
; significant nibble of B is used.
ide_sector_select:
ld a,ide_reg_sector_0
out (ide_control),a
ld a,e
out (ide_data_lsb),a
ld a,ide_reg_sector_1
out (ide_control),a
ld a,d
out (ide_data_lsb),a
ld a,ide_reg_sector_2
out (ide_control),a
ld a,c
out (ide_data_lsb),a
ld a,ide_reg_sector_3
out (ide_control),a
ld a,b
and 0x0f ; clear top nibble
ld b,a
call ide_config_check
or b
out (ide_data_lsb),a
ret
; Name: ide_sector_write
; Desc: Write one sector to the device.
; In: A = IDE device ID
; BCDE = Sector index as per ide_sector_select,
; HL = address of output buffer.
; Out: ZF = 1 on success,
; CF = 1 if timed out,
; A = IDE error code.
ide_sector_write:
ld (ide_config),a
push hl ; Remember values
call ide_ready ; Wait until device is ready
jp nz,ide_sector_write_error ; Timeout
call ide_sector_select ; Program sector index
call ide_sector_count ; Set sector count
ld a,ide_cmd_write ; Send write command
call ide_send_command
call ide_ready_data ; Wait until device is ready for data
jp nz,ide_sector_write_error ; Timeout
pop hl
push hl
call ide_block_write ; Copy our data to the device
call ide_ready ; Wait until device is ready
jp nz,ide_sector_write_error ; Timeout
call ide_check_error
ide_sector_write_error:
pop hl ; Restore value
ret
; Name: ide_send_command
; Desc: Send a command to the device
; In: A = IDE command code
ide_send_command:
push af
ld a,ide_reg_command
out (ide_control),a
pop af
out (ide_data_lsb),a
ret
; Name: ide_set_buffer
; Desc: Check for magic buffer address (0)
; In: HL = proposed buffer address
; Out: HL = actual buffer address
ide_set_buffer:
ld a,h
or l
jp nz,ide_set_buffer_end
ld hl,ide_internal_buffer
ide_set_buffer_end:
ret
; Name: ide_wait
; Desc: Wait for given value of status register, with timeout.
; In: C = mask to AND with register, such that a result of zero
; indicates success.
; Out: ZF = 1 on success, 0 if timed out,
; CF = 1 if timed out
ide_wait:
push hl
push de
and a
ld de,ide_timeout_count
ide_wait_loop:
call ide_get_status
ld h,a
and c
jp z,ide_wait_end
ld b,ide_timeout_res
ide_wait_pause:
djnz ide_wait_pause
dec de
ld a,d
or e
jp nz,ide_wait_loop
ld a,h
; Timeout - clear zero flag, set carry flag
ld a,1
and a
scf
ide_wait_end:
pop de
pop hl
ret