summaryrefslogtreecommitdiff
path: root/disk.asm
blob: 2e001f8abbf041e4630cf85457f29d682a889e95 (plain) (blame)
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
%ifndef _DISK_ASM
%define _DISK_ASM

%include "stdio.asm"
SECTORS_PER_TRACK equ 12h

disk_info:
	push bp
	mov bp, sp
	pusha

	mov ax, 0
	mov es, ax
	mov di, 0

	mov ah, 08h
	mov si, disk_info_fmt
	mov dx, [bp + 4]	; load drive number
				; {diskette=00h..03h, hdd=80h-81h}
	cmp dx, 80h
	jge .is_disk

	.is_diskette:
	mov si, diskette_info_fmt

	.is_disk:
	int 13h
	jc .error

	and word [bp + 4], 07fh	; mask off bit 7

	push cx
	push dx
	push ax
	push word [bp + 4]	; drive number
	push si			; format string
	call printf
	add sp, 2 * 5

	jmp .return

	.error:
		stc
	.return:
		popa
		mov sp, bp
		pop bp
		ret

disk_info_ext:
	push bp
	mov bp, sp
	pusha

	sub sp, 2		; bit-field temp storage

	xor dx, dx
	mov ah, 41h
	mov bx, 55aah
	mov dl, [bp + 4]	; drive number {80h..ffh}
	int 13h
	jc .return		; carry is set on failure
	mov word [bp - 2], cx	; store bit-field

	and dx, 07fh		; mask off bit 7

	xchg ah, al		; extension version
	and ax, 00ffh		; mask off high byte

	push cx
	push ax
	push dx
	push disk_info_ext_fmt
	call printf
	add sp, 2 * 4

	mov dx, word [bp - 2]	; load bit-field
	mov cx, 0000h		; set counter

	.do_flag:
		mov bx, dx	; load BX with bit-field
		shr bx, cl	; shift bit-field by count
		and bx, 01h	; Is the bit set?
		je .no_flag	; non-zero = YES

		push cx		; push count
		call disk_info_ext_print_flag	; display flag
		add sp, 2	; clean up
	.no_flag:
		inc cx		; next bit
		cmp cx, 0fh	; 16 bits in bit field
		jne .do_flag
.return:
	add sp, 2
	popa
	mov sp, bp
	pop bp
	ret


disk_info_ext_print_flag:
	push bp
	mov bp, sp
	pusha

	mov cx, [bp + 4]

	imul cx, cx, 2			; compute WORD offset
	lea bx, [disk_info_ext_table]	; load array address
	add bx, cx			; compute string pointer offset

	push word [bx]			; load address of string pointer
	push .fmt			; load format string
	call printf
	add sp, 2 * 2
.return:
	popa
	mov sp, bp
	pop bp
	ret
	.fmt db "           - %s", ASCII_LF, 0


disk_last_status:
	push bp
	mov bp, sp

	clc				; clear carry flag
					; DL = drive number
	mov ah, 01h			; BIOS get status of last drive operation
	int 13h				; BIOS disk service
					; carry set on error
	mov sp, bp
	pop bp
	ret


disk_convert_status:
	push bp
	mov bp, sp
	pusha

	cmp ah, 00h			; Avoid writing "success" message
	je .return

	xor cx, cx
	mov cx, ax			; AL contains the sector count
	and cx, 00ffh			; store in CX
	shr ax, 8			; shift error code into AL

					; This block handles AL when not 00h..11h
					; The extended error values have no real order
	cmp al, 20h
	je .status_20

	cmp al, 40h
	je .status_40

	cmp al, 80h
	je .status_80

	cmp al, 0AAh
	je .status_AA

	cmp al, 0BBh
	je .status_BB

	cmp al, 0CCh
	je .status_CC

	cmp al, 0E0h
	je .status_E0

	cmp al, 0FFh
	je .status_FF

	jmp .convert				; No match, convert AL as-is

						; Adjust error code so that it can be indexed
						; easily by error_msg_disk_table
	.status_20:
		mov al, 12h
		jmp .convert

	.status_40:
		mov al, 13h
		jmp .convert

	.status_80:
		mov al, 14h
		jmp .convert

	.status_AA:
		mov al, 15h
		jmp .convert

	.status_BB:
		mov al, 16h
		jmp .convert

	.status_CC:
		mov al, 17h
		jmp .convert

	.status_E0:
		mov al, 18h
		jmp .convert

	.status_FF:
		mov al, 19h
		jmp .convert

	.convert:
		mov bx, ax			; Get array index
		imul bx, bx, 2			; Calculate index offset
						; in human speak: idx * (idx * WORD)

		lea si, [error_msg_disk_table]	; Load address of table into source index
		add si, bx			; Add calculated index offset

	and dx, 00ffh				; get drive number

	push word [si]				; error message
	push dx					; drive number
	push ax					; error code
	push error_msg_disk_status_fmt
	call printf				; Print error message
	add sp, 2 * 4				; Clean up stack

.return:
	popa
	mov sp, bp
	pop bp
	ret



disk_reset:
	push bp
	mov bp, sp
	pusha

	mov ah, 00h		; reset disk
	mov dl, byte [bp + 4]	; disk number
	int 13h			; BIOS disk service
	jnc .success
	stc
.success:
	popa
	mov sp, bp
	pop bp
	ret


disk_lba_chs:
	push bp
	mov bp, sp
	push ax
	push bx
	push dx
	sub sp, 2 * 3		; -2 = HEAD
				; -4 = TRACK
				; -6 = SECTOR

	mov si, [bp + 4] ; Linear Block Address

	; compute HEAD
	mov cx, SECTORS_PER_TRACK
	shl cx, 1			; SPT * 2
	xor dx, dx			; initialize quotient
	mov ax, si			; LBA %
	mov bx, cx			; (SPT * 2)
	div bx

	mov ax, dx			; use quotient as next dividend
	xor dx, dx
	mov bx, SECTORS_PER_TRACK	; / SPT
	div bx
	mov [bp - 2], ax		; store HEAD (remainder)

	; compute TRACK
	xor dx, dx   			; initialize quotient
	mov ax, si			; LBA %
	mov bx, cx			; (SPT * 2)
	div bx
	mov [bp - 4], ax		; store TRACK (remainder)

	; compute SECTOR
	xor dx, dx
	mov ax, si
	mov bx, SECTORS_PER_TRACK
	div bx
	add dx, 1
	mov [bp - 6], dx		; store SECTOR (quotient)

	pop dx
	pop bx
	pop ax

	; assemble return values
	mov dh, byte [bp - 2]		; load HEAD
	mov ch, byte [bp - 4]		; load TRACK
	mov cl, byte [bp - 6]		; load SECTOR

	add sp, 2 * 3
	mov sp, bp
	pop bp
	ret


disk_read:
	push bp
	mov bp, sp

	mov si, [bp + 4]	; LBA

	push si
	call disk_lba_chs		; convert LBA to CHS
	add sp, 2

	mov ah, 02h
	mov dl, byte [bp + 6]	; drive number
	mov al, byte [bp + 8]	; number of sectors to read
	mov bx, [bp + 10]	; data destination address [es:bx]
	int 13h
	jnc .success

	call disk_convert_status

	push dx
	call disk_reset
	add sp, 2
	stc
.success:
	mov sp, bp
	pop bp
	ret


disk_write:
	push bp
	mov bp, sp

	mov si, [bp + 4]	; LBA

	push si
	call disk_lba_chs		; convert LBA to CHS
	add sp, 2

	mov ah, 03h
	mov dl, byte [bp + 6]	; drive number
	mov al, byte [bp + 8]	; number of sectors to read
	mov bx, [bp + 10]	; data destination address [es:bx]
	int 13h
	jnc .success

	call disk_convert_status

	push dx
	call disk_reset
	add sp, 2
	stc
.success:
	mov sp, bp
	pop bp
	ret


; data
align 2
drive0: dw 0

msg_disk_reset: db "Drive reset successful.", ASCII_LF, 0
msg_disk_read: db "Sector read successful.", ASCII_LF, 0

error_msg_disk_reset: db "Drive reset failed!", ASCII_LF, 0
error_msg_disk_read: db "Drive read failed!", ASCII_LF, 0

diskette_info_fmt: db 'FDD(%x): status=%x head_max=%x cyl_max=%x', ASCII_LF, 0

disk_info_fmt: db 'HDD(%x): status=%x head_max=%x cyl_max=%x', ASCII_LF, 0
disk_info_ext_fmt: db 'HDD(%x): IBM/MS INT 13 Extensions v%x [%x]', ASCII_LF, 0

disk_info_ext_flags:
	.bit_0: db "Extended disk access support", 0
	.bit_1: db "Removable drive controller support", 0
	.bit_2: db "Enhanced disk drive support", 0
	.reserved: db "Reserved", 0

disk_info_ext_table:
	dw disk_info_ext_flags.bit_0
	dw disk_info_ext_flags.bit_1
	dw disk_info_ext_flags.bit_2
	.reserved: times(15-3) dw disk_info_ext_flags.reserved
	dw 0000h	; END of disk_info_ext_table

error_msg_disk_status_fmt: db 'ERROR %x: Drive %x: %s', ASCII_LF, 0
error_msg_disk:
	eds_success: db "Success", 0					; 00h
	eds_invalid_command: db "Invalid command", 0
	eds_address_mark: db "Cannot find address mark", 0
	eds_write_protected: db "Attempted write on write protected disk", 0
	eds_sector_not_found: db "Sector not found.", 0
	eds_reset_failed: db "Reset failed.", 0
	eds_disk_change_line_active: db "Disk change line active", 0
	eds_drive_parameter_activity_failed: db "Drive parameter activity failed", 0
	eds_dma_overrun: db "DMA overrun", 0
	eds_dma_boundary_64: db "DMA over 64kb boundary", 0
	eds_bad_sector: db "Bad sector detected", 0
	eds_bad_cylinder: db "Bad cylinder detected", 0
	eds_media_type_not_found: db "Media type not found", 0
	eds_invalid_number_of_sectors: db "Invalid number of sectors", 0
	eds_control_data_address_mark: db "Control data address mark detected", 0
	eds_dma_out_of_range: db "DMA out of range", 0
	eds_crc_ecc_data_error: db "CRC/ECC data error", 0	 	; 10h
	eds_ecc_corrected_data_error: db "ECC corrected data error", 0	; 11h
	; Everything was normal until... this...
	eds_controller_failure: db "Controller failure", 0		; 20h
	eds_seek_failure: db "Seek failure", 0				; 40h
	eds_timeout: db "Drive timed out (not ready?)", 0		; 80h
	eds_not_ready: db "Drive not ready", 0				; AAh
	eds_undefined_error: db "Undefined error", 0			; BBh
	eds_write_fault: db "Write fault", 0				; CCh
	eds_status_error: db "Status error", 0				; E0h
	eds_sense_operation_failed: db "Sense operation failed", 0	; FFh

error_msg_disk_table:				; is an array of pointers for each error message
	dw eds_success
	dw eds_invalid_command
	dw eds_address_mark
	dw eds_write_protected
	dw eds_sector_not_found
	dw eds_reset_failed
	dw eds_disk_change_line_active
	dw eds_drive_parameter_activity_failed
	dw eds_dma_overrun
	dw eds_dma_boundary_64
	dw eds_bad_sector
	dw eds_bad_cylinder
	dw eds_media_type_not_found
	dw eds_invalid_number_of_sectors
	dw eds_control_data_address_mark
	dw eds_dma_out_of_range
	dw eds_crc_ecc_data_error
	dw eds_ecc_corrected_data_error
	dw eds_controller_failure
	dw eds_seek_failure
	dw eds_timeout
	dw eds_not_ready
	dw eds_undefined_error
	dw eds_write_fault
	dw eds_status_error
	dw eds_sense_operation_failed
	dw 0000h				; END of error_msg_disk_table

%endif ; _DISK_ASM