-
Notifications
You must be signed in to change notification settings - Fork 0
/
ata.inc
403 lines (394 loc) · 10.4 KB
/
ata.inc
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
;---------------------------------------------------------------:
; L-ATA Fixed Disk BIOS - ATA I/O routines :
; Written by John 'Lameguy' Wilbert Villamor :
; 2022 Meido-Tek Productions :
; :
; Main file: latabios.asm :
;---------------------------------------------------------------:
;---------------------------------------------------------------:
; =GetIO :
; Obtain the base address of the ATA channel selected :
; by ES:PORT_OFF. :
; :
; Assumed: DS=CS(ROM), ES:PORT_OFF=<drive numer> :
; :
; Returns: :
; DX I/O address :
;---------------------------------------------------------------:
GetIO:
SUB DH,DH ; Zero out upper byte of DX
MOV DL,ES:PORT_OFF ; Get current disk number
AND DL,06H ; Mask off master/slave bit
PUSH SI ; Load base I/O address
MOV SI,ATA_BASE
ADD SI,DX
MOV DX,[CS:SI]
POP SI
RET
;---------------------------------------------------------------:
; =ResetATA :
; Resets the specified ATA bus by toggling the SRST bit. :
; :
; Arguments: :
; DX Base I/O address of ATA channel :
; :
; Assumes: None :
; :
; Destroys: None :
;---------------------------------------------------------------:
ResetATA:
.baseIO EQU -2
PUSH BP
MOV BP,SP
SUB SP,2
PUSH AX
PUSH DX
PUSH CX
;----- Perform software reset
AND DH,0FH
MOV [BP+.baseIO],DX ; Store base address for later recalls
ADD DX,ATA_CT_DCTRL ; Adjust to control port
MOV AL,DC_SRST ; Set software reset bit
OUT DX,AL
MOV DX,[BP+.baseIO] ; Get alternate status register
ADD DX,ATA_CT_ASTAT
MOV CX,15 ; Delay 400ns by reading port 15 times
.resetwait:
IN AL,DX
LOOP .resetwait
MOV DX,[BP+.baseIO] ; Get control port
ADD DX,ATA_CT_DCTRL
SUB AL,AL ; Clear SRST bit
OUT DX,AL
;----- Post reset
MOV AH,086H ; Delay 256 just in case
MOV CX,001H
SUB DX,DX
INT 015H
POP CX
POP DX
POP AX
ADD SP,2
POP BP
RET
;---------------------------------------------------------------:
; =SetCHS :
; Applies CHS parameters to the hard drive specified by :
; global variable ES:PORT_OFF and additionally negotiates :
; drive selection. :
; :
; The arguments are arranged such that untranslated CHS :
; parameters from INT 13h calls can be rearranged with :
; few instructions possible: :
; :
; AH = Function number (unneeded) :
; AL = Number of sectors :
; CH = Low eight bits of cylinder :
; CL = Bits 0-5: Sector number :
; Bits 6-7: Upper two bits of cylinder :
; DH = Head number :
; :
; MOV AH,CL ; Use AH as sector number :
; AND AH,03FH ; Mask off upper bits for cylinder :
; SHR CL,6 ; Shift upper 2 bits of cylinder down :
; XCHG CH,CL ; Exchange to correct byte order :
; :
; Arguments: :
; AL Number of sectors (1-255, 0=256) :
; AH Sector number (1-63) :
; CX Cylinder number (0-65535) :
; DH Head number (0-15) :
; :
; Assumes: DS=CS(ROM), ES=BDA_SEG, ES:PORT_OFF=<drive number> :
; :
; Destroys: None :
; :
; Returns: :
; DX Base I/O address of ATA channel :
;---------------------------------------------------------------:
SetCHS:
.head EQU -1
.bplen EQU 2
PUSH BP
MOV BP,SP
SUB SP,.bplen
PUSH AX
MOV [BP+.head],DH ; Save head number
CALL GetIO ; Get I/O address
AND DH,00FH ; Mask off IRQ number
;----- Set drive/head register
PUSH AX
ADD DX,ATA_DHEAD ; Adjust to drive/head register
MOV AL,[BP+.head] ; Get head value
AND AL,0FH ; Mask off upper 4-bits just in case
OR AL,0A0H
MOV AH,ES:PORT_OFF ; Get current disk number
AND AH,01H ; Mask out channel number
SHL AH,4 ; Shift lone bit to drive select
OR AL,AH
OUT DX,AL ; Set new drive/head select value
MOV AH,AL ; Save register value
;----- Drive select delay
ADD DX,ATA_STAT-ATA_DHEAD
PUSH CX
MOV CX,15 ; Loop 15 times
.dseldelay:
IN AL,DX ; Read alternate status register
LOOP .dseldelay ; ... as a crude delay
POP CX
ADD DX,ATA_DHEAD-ATA_STAT
MOV AL,AH ; Restore register value
OUT DX,AL ; Set register twice just to make sure
.skipdelay:
;----- Set sector count, number and cylinders
ADD DX,ATA_FEAT-ATA_DHEAD
MOV AL,0FFH ; Set WPCOMP (just FF for none)
OUT DX,AL
POP AX ; Set sector count register
INC DX
OUT DX,AL
INC DX ; Set sector number register
MOV AL,AH
OUT DX,AL
INC DX ; Set cylinder low
MOV AL,CL
OUT DX,AL
INC DX ; Set cylinder high
MOV AL,CH
OUT DX,AL
SUB DX,ATA_CYLH
POP AX
ADD SP,.bplen
POP BP
RET
;---------------------------------------------------------------:
; =SendCMD :
; Send a command to the ATA device selected by :
; ES:PORT_OFF. For master/slave drives to be selected :
; properly, SetCHS must be called prior :
; :
; Arguments: :
; AL Command :
; :
; Assumes: ES=BDA_SEG, ES:PORT_OFF=<drive number> :
; :
; Destroys: None :
; :
; Returns: :
; DX I/O port of ATA channel :
;---------------------------------------------------------------:
SendCMD:
PUSH AX
;----- Get base address of ATA channel
SUB DH,DH
MOV DL,[ES:PORT_OFF] ; Get port offset
AND DL,06H ; Mask off master/slave bit
;----- Set flag on ATA channel
PUSH CX
SHR DL,1
MOV CL,DL
MOV AL,1
SHL AL,CL
OR ES:ATA_STATUS,AL
POP CX
;----- Issue command
CALL GetIO ; Get IO port
AND DH,00FH ; Mask off IRQ number
ADD DX,ATA_CMD ; Adjust address to command register
POP AX
OUT DX,AL ; Write command
SUB DX,ATA_CMD
RET
;---------------------------------------------------------------:
; =ReadBLK :
; Read a data block from the hard drive using PIO. :
; :
; Arguments: :
; DX Base I/O address (ie. 01F0H) :
; ES:DI Pointer to store read block (DI updated) :
; :
; Destroys: AX,CX :
;---------------------------------------------------------------:
ReadBLK:
CLD
ADD DX,ATA_STAT ; Adjust to status register
IN AL,DX
.waitdrq: ; Wait for DRQ to become set
IN AL,DX
TEST AL,STAT_DRQ
JZ .waitdrq
SUB DX,ATA_STAT ; Adjust to data port
MOV CX,0100H ; 256 words, 512 bytes
REP INSW ; Fetch the block
RET
;---------------------------------------------------------------:
; =WriteBLK :
; Write a data block to the hard drive using PIO. :
; :
; Arguments: :
; DX Base I/O address (ie. 01F0H) :
; DS:SI Source pointer for data block (SI updated) :
; :
; Destroys: AX,CX :
;---------------------------------------------------------------:
WriteBLK:
CLD
ADD DX,ATA_STAT ; Adjust returned port to status reg.
.drqwait: ; Wait for data request
IN AL,DX
TEST AL,STAT_DRQ
JZ .drqwait
SUB DX,ATA_STAT ; Adjust back to data port
MOV CX,0100H ; 256 words, 512 bytes
REP OUTSW ; Write the block
RET
;---------------------------------------------------------------:
; =WaitRDY :
; Wait for a drive on channel to become ready. :
; :
; Arguments: :
; BX Timeout (seconds) :
; :
; Returns: :
; Sets ES:DISK_STATUS to TIME_OUT when timeout surpassed. :
;---------------------------------------------------------------:
WaitRDY:
PUSH AX
;----- Post a device busy
PUSH BX
SUB BH,BH ; Standard timeout
MOV BL,010H
CLC ; Device busy interrupt
MOV AX,09000H
INT 015H
STI ; Just in case IRQs got cleared by OS
POP BX
;----- Calculate timeout value
PUSH CX
MOV AX,18
MUL BX
MOV BX,AX
MOV CX,DX
ADD BX,ES:TIMER_LOW
ADC CX,ES:TIMER_LOW+2
;----- Get status register
CALL GetIO
AND DH,0FH
ADD DX,ATA_CT_ASTAT
IN AL,DX
.bsywait:
CMP ES:TIMER_LOW+2,CX
JB .cont
CMP ES:TIMER_LOW,BX
JB .cont
MOV BYTE [ES:DISK_STATUS],TIME_OUT ; Set timeout condition
JMP .done
.cont:
IN AL,DX
TEST AL,STAT_BUSY
JNZ .bsywait
.done:
SUB DX,ATA_CT_ASTAT
POP CX
POP AX
RET
;---------------------------------------------------------------:
; =WaitINT :
; Wait for disk interrupt to occur. Sets DISK_STATUS :
; to TIME_OUT if no IRQ occurred during the last disk :
; operation. Normally used for device detection. :
; :
; The hard drive is not automatically acknowledged by :
; this function when an interrupt occurs. :
; :
; Arguments: :
; BL Timeout value. :
; :
; Assumes: ES=CS,ES=BDA_SEG :
; :
; Destroys: BX :
; :
; Returns: :
; DX Data port of selected drive :
; ES:DISK_STATUS Updated to TIME_OUT on IRQ timeout :
;---------------------------------------------------------------:
WaitINT:
PUSH AX
PUSH CX
;----- Obtain channel number
MOV AL,[ES:PORT_OFF] ; Get current drive
AND AL,06H
SHR AL,1
MOV CL,AL
MOV AH,1 ; Set pending bit
SHL AH,CL
;----- Wait until IRQ occurs
PUSH AX
MOV AX,18
MUL BX
MOV CX,DX ; Move upper word to DX
ADD AX,ES:TIMER_LOW ; Load BDA counter
ADC CX,ES:TIMER_LOW+2
MOV BX,AX
POP AX
CALL GetIO ; Load I/O port
AND DH,0FH ; Mask out IRQ bit
ADD DX,ATA_CT_ASTAT ; Adjust to alternate status register
.wait:
TEST ES:ATA_STATUS,AH ; Has interrupt occurred?
JZ .waitdone
CMP ES:TIMER_LOW,BX ; Compare ticks to test timeout
JB .wait
CMP ES:TIMER_LOW+2,CX
JB .wait
MOV BYTE ES:DISK_STATUS,TIME_OUT ; Set timeout condition
.waitdone:
SUB DX,ATA_CT_ASTAT ; Revert back to base address
POP CX
POP AX
RET
;---------------------------------------------------------------:
; =IntHandler :
; Hard disk interrupt handler. The hard disk posts a :
; hardware interrupt when a disk operation has completed. :
; :
; The handler additionally posts a busy done call on :
; INT 15h for multi-tasking operating systems. :
;---------------------------------------------------------------:
IntHandler:
PUSH AX
PUSH DS
PUSH ES
;----- Setup segments
MOV AX,CS ; Setup ROM data segment
MOV DS,AX
MOV AX,WRK_SEG ; Setup BDA segment
MOV ES,AX
;----- Turn off IRQ mask of drive
CLI
SUB AH,AH ; Get drive number of current operation
MOV AL,[ES:PORT_OFF]
AND AL,06H ; Mask off master/slave bit
;----- Clear IRQ pending bit in status
PUSH AX
PUSH CX
SHR AL,1
MOV CL,AL
MOV AL,1
SHL AL,CL
XOR AL,0FFH
AND ES:ATA_STATUS,AL
POP CX
POP AX
;----- Issue EOI
MOV AL,EOI
OUT INTB00,AL ; Slave 8259
OUT INTA00,AL ; Then master 8259
;----- Post interrupt then return
POP ES
POP DS
STI
MOV AX,09100H ; Device post interrupt
INT 015H
POP AX
IRET