source: xtideuniversalbios/trunk/XTIDE_Universal_BIOS/Src/Device/IDE/IdeDmaTransfer.asm@ 478

Last change on this file since 478 was 478, checked in by aitotat@…, 13 years ago

Changes to XTIDE Universal BIOS:

  • Fixes to DMA transfers but it still does not work.
File size: 19.0 KB
Line 
1; Project name : XTIDE Universal BIOS
2; Description : IDE Device DMA transfer functions.
3
4;
5; XTIDE Universal BIOS and Associated Tools
6; Copyright (C) 2009-2010 by Tomi Tilli, 2011-2012 by XTIDE Universal BIOS Team.
7;
8; This program is free software; you can redistribute it and/or modify
9; it under the terms of the GNU General Public License as published by
10; the Free Software Foundation; either version 2 of the License, or
11; (at your option) any later version.
12;
13; This program is distributed in the hope that it will be useful,
14; but WITHOUT ANY WARRANTY; without even the implied warranty of
15; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16; GNU General Public License for more details.
17; Visit http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
18;
19
20; Structure containing variables for DMA transfer functions.
21; This struct must not be larger than IDEPACK without INTPACK.
22struc DMAVARS ; Must not be larger than 9 bytes! See IDEPACK in RamVars.inc.
23 .wTotalWordsXferred resb 2 ; 0-1,
24 .wBytesLeftToXferLessOne resb 2 ; 2-3,
25 .bbbPhysicalAddress resb 3 ; 4-6,
26 resb 1 ; 7, IDEPACK.bDeviceControl
27endstruc
28
29
30; Section containing code
31SECTION .text
32
33;--------------------------------------------------------------------
34; IdeDmaTransfer_StartWithCommandInAL
35; Parameters:
36; AL: IDE command that was used to start the transfer
37; (all PIO read and write commands including Identify Device)
38; ES:SI: Ptr to data buffer (not normalized)
39; DS:DI: Ptr to DPT (in RAMVARS segment)
40; SS:BP: Ptr to IDEPACK
41; Returns:
42; AH: INT 13h Error Code
43; CX: Number of successfully transferred sectors
44; CF: Cleared if success, Set if error
45; Corrupts registers:
46; AL, BX, DX
47;--------------------------------------------------------------------
48ALIGN JUMP_ALIGN
49IdeDmaTransfer_StartWithCommandInAL:
50 ; Initialize DMAVARS
51 xor cx, cx
52 mov [bp+DMAVARS.wTotalWordsXferred], cx
53 mov ch, [bp+IDEPACK.bSectorCount] ; CX = WORDs to transfer
54 shl cx, 1 ; WORDs to BYTEs, 0 = 65536
55 dec cx
56 mov [bp+DMAVARS.wBytesLeftToXferLessOne], cx
57
58 ; Convert Segment:Offset type pointer to physical address
59 xor bx, bx
60 mov cx, es
61%rep 4
62 shl cx, 1
63 rcl bx, 1
64%endrep
65 add cx, si
66 adc bl, bh
67 mov [bp+DMAVARS.bbbPhysicalAddress], cx
68 mov [bp+DMAVARS.bbbPhysicalAddress+2], bl
69
70 ; Calculate bytes for first page - 1
71 neg cx ; Max number of bytes for first page, 0 = 65536
72 dec cx
73 MIN_U cx, [bp+DMAVARS.wBytesLeftToXferLessOne]
74
75 ; Are we reading or writing?
76 test al, 16 ; Bit 4 is cleared on all the read commands but set on 3 of the 4 write commands
77 jnz SHORT WriteBlockToXTCF
78 cmp al, COMMAND_WRITE_MULTIPLE
79 je SHORT WriteBlockToXTCF
80 ; Fall to ReadBlockFromXTCF
81
82
83;--------------------------------------------------------------------
84; ReadBlockFromXTCF
85; Parameters:
86; CX: Bytes in first page - 1
87; DS:DI: Ptr to DPT (in RAMVARS segment)
88; SS:BP: Ptr to DMAVARS
89; Returns:
90; DS:DI: Ptr to DPT (in RAMVARS segment)
91; AH: BIOS Error code
92; CX: Number of successfully transferred sectors
93; CF: 0 if transfer successful
94; 1 if any error
95; Corrupts registers:
96; AL, BX, DX
97;--------------------------------------------------------------------
98ReadBlockFromXTCF:
99 ; 8-bit DMA transfers must be done withing 64k physical page.
100 ; We support maximum of 128 sectors (65536 bytes) per one INT 13h call
101 ; so we might need to separate transfer to 2 separate DMA operations.
102
103 ; Transfer first DMA page
104 mov ah, CHANNEL_3 | WRITE | AUTOINIT_DISABLE | ADDRESS_INCREMENT | DEMAND_MODE
105 call StartDMAtransferForXTCFwithDmaModeInAH
106 call UpdateVariablesForSecondPageIfRequired
107 jc SHORT ReturnNumberOfSectorsXferred ; Second page not needed
108
109 ; Transfer second DMA page if necessary (always less than 64k)
110 mov ah, CHANNEL_3 | WRITE | AUTOINIT_DISABLE | ADDRESS_INCREMENT | DEMAND_MODE
111 call StartDMAtransferForXTCFwithDmaModeInAH
112 ; Fall to BothDmaPagesTransferred
113
114BothDmaPagesTransferred:
115 inc cx ; Never overflows since second page always less than 64k
116 shr cx, 1 ; BYTEs to WORDs
117 add [bp+DMAVARS.wTotalWordsXferred], cx
118ReturnNumberOfSectorsXferred:
119 mov bx, TIMEOUT_AND_STATUS_TO_WAIT(TIMEOUT_DRQ, FLG_STATUS_BSY)
120 call IdeWait_PollStatusFlagInBLwithTimeoutInBH
121 jc SHORT .ErrorInTransfer
122 mov cx, [bp+DMAVARS.wTotalWordsXferred]
123 xchg cl, ch ; WORDs to sectors
124 ret
125
126.ErrorInTransfer:
127 mov cx, 0 ; No way to know how many bytes got xferred
128 ret
129
130
131;--------------------------------------------------------------------
132; WriteBlockToXTCF
133; Parameters:
134; CX: Bytes in first page - 1
135; DS:DI: Ptr to DPT (in RAMVARS segment)
136; SS:BP: Ptr to DMAVARS
137; Returns:
138; DS:DI: Ptr to DPT (in RAMVARS segment)
139; AH: BIOS Error code
140; CX: Number of successfully transferred sectors
141; CF: 0 if transfer successful
142; 1 if any error
143; Corrupts registers:
144; AL, BX, DX
145;--------------------------------------------------------------------
146ALIGN JUMP_ALIGN
147WriteBlockToXTCF:
148 ; Transfer first DMA page
149 mov ah, CHANNEL_3 | READ | AUTOINIT_DISABLE | ADDRESS_INCREMENT | DEMAND_MODE
150 call StartDMAtransferForXTCFwithDmaModeInAH
151 call UpdateVariablesForSecondPageIfRequired
152 jc SHORT ReturnNumberOfSectorsXferred ; Second page not needed
153
154 ; Transfer second DMA page if necessary (always less than 64k)
155 mov ah, CHANNEL_3 | READ | AUTOINIT_DISABLE | ADDRESS_INCREMENT | DEMAND_MODE
156 call StartDMAtransferForXTCFwithDmaModeInAH
157 jmp SHORT BothDmaPagesTransferred
158
159
160;--------------------------------------------------------------------
161; StartDMAtransferForXTCFwithDmaModeInAH
162; Parameters:
163; AH: Byte for DMA Mode Register
164; CX: Number of BYTEs to transfer - 1
165; DS:DI: Ptr to DPT (in RAMVARS segment)
166; Returns:
167; Nothing
168; Corrupts registers:
169; AL, DX
170;--------------------------------------------------------------------
171ALIGN JUMP_ALIGN
172StartDMAtransferForXTCFwithDmaModeInAH:
173 push cx
174
175 ; Program 8-bit DMA Controller
176
177 ; Disable Interrupts and DMA Channel 3 during DMA setup
178 cli ; Disable interrupts
179 mov al, SET_CH3_MASK_BIT
180 out MASK_REGISTER_DMA8_out, al ; Disable DMA Channel 3
181
182 ; Set DMA Mode (read or write using channel 3)
183 mov al, ah
184 out MODE_REGISTER_DMA8_out, al
185
186 ; Set address to DMA controller
187 out CLEAR_FLIPFLOP_DMA8_out, al ; Reset flip-flop to low byte
188 mov ax, [bp+DMAVARS.bbbPhysicalAddress]
189 out BASE_AND_CURRENT_ADDRESS_REGISTER_DMA8_CH3_out, al ; Low byte
190 mov al, ah
191 out BASE_AND_CURRENT_ADDRESS_REGISTER_DMA8_CH3_out, al ; High byte
192 mov al, [bp+DMAVARS.bbbPhysicalAddress+2]
193 out PAGE_DMA8_CH_3, al
194
195 ; Set number of bytes to transfer (DMA controller must be programmed number of bytes - 1)
196 out CLEAR_FLIPFLOP_DMA8_out, al ; Reset flip-flop to low byte
197 mov ax, cx
198 out BASE_AND_CURRENT_COUNT_REGISTER_DMA8_CH3_out, al ; Low byte
199 mov al, ah
200 out BASE_AND_CURRENT_COUNT_REGISTER_DMA8_CH3_out, al ; High byte
201
202 ; Enable DMA Channel 3
203 mov al, CLEAR_CH3_MASK_BIT
204 out MASK_REGISTER_DMA8_out, al ; Enable DMA Channel 3
205 sti ; Enable interrupts
206
207
208 ; XT-CFv2 will present data in 16-byte blocks, but byte count may not
209 ; be divisable by 16 due to boundary crossing. So catch any < 16-byte
210 ; block by adding 15, then dividing bytes (in CX) by 16 to get the
211 ; total block requests. The 8237 is programmed with the actual byte
212 ; count and will end the transfer by asserting TC when done.
213 add cx, BYTE 1 + 15 ; Number of BYTEs to xfer + 15 (bit 16 in CF)
214 rcr cx, 1
215 eSHR_IM cx, 3 ; CX = Number of 16 byte blocks
216 mov dx, [di+DPT.wBasePort]
217 add dl, XTCF_CONTROL_REGISTER
218
219.MoreToDo: ; at this point, cx must be >0
220 mov al, 40h ; 0x40 = Raise DRQ and clear XT-CFv2 transfer counter
221.NextDemandBlock:
222 out dx, al ; get up to 16 bytes from XT-CF card
223 loop .NextDemandBlock ; decrement CX and loop if <> 0
224 ; (Loop provides a wait-state between 16-byte blocks; do not unroll)
225
226.CleanUp:
227 ; check the transfer is actually done - in case another DMA operation messed things up
228 inc cx ; set up CX, in case we need to do an extra iteration
229 in al, STATUS_REGISTER_DMA8_in ; get DMA status register
230 test al, FLG_CH3_HAS_REACHED_TERMINAL_COUNT ; test DMA ch.3 TC bit
231 jz SHORT .MoreToDo ; it wasn't set so get more bytes
232
233.EndDMA:
234 mov al, 10h ;
235 out dx, al ; set back to DMA enabled status
236 pop cx
237 ret
238
239
240;--------------------------------------------------------------------
241; UpdateVariablesForSecondPageIfRequired
242; Parameters:
243; CX: Number of BYTEs in first page - 1
244; SS:BP: Ptr to DMAVARS
245; Returns:
246; CX: Bytes left to transfer - 1 (if CF = 0)
247; CF: 0 if second DMA transfer required
248; 1 if one DMA transfer was enough
249; Corrupts registers:
250; AX, (CX)
251;--------------------------------------------------------------------
252ALIGN JUMP_ALIGN
253UpdateVariablesForSecondPageIfRequired:
254 inc cx ; BYTEs in first page
255 jcxz .FullPageXferred ; 0 = 65536
256
257 ; Store total WORDs transferred so far
258 mov ax, cx
259 shr ax, 1 ; BYTEs to WORDs
260 mov [bp+DMAVARS.wTotalWordsXferred], ax
261
262 ; Get bytes left to transfer for second DMA page
263 mov ax, [bp+DMAVARS.wBytesLeftToXferLessOne]
264 sub ax, cx
265 jb SHORT .OnePageWasEnough
266
267 ; Increment address
268 add [bp+DMAVARS.bbbPhysicalAddress], cx
269 adc BYTE [bp+DMAVARS.bbbPhysicalAddress+2], 0 ; Never sets CF
270 xchg cx, ax
271 ret
272
273.FullPageXferred:
274 mov WORD [bp+DMAVARS.wTotalWordsXferred], 65536 / 2
275 stc
276.OnePageWasEnough:
277 ret
278
279
280
281
282%if 0
283;--------------------------------------------------------------------
284ALIGN JUMP_ALIGN
285ReadBlockFromXTCF:
286; Parameters:
287; CX: Block size in 512 byte sectors (1..64)
288; DX: IDE Data port address
289; ES:DI: Normalized ptr to buffer to receive data
290; Returns:
291; Nothing
292; Corrupts registers:
293; AX, BX, CX
294
295 ; use config register to see what we're doing:
296 ; < A0 = DMA via ch.3
297 ; > A0 = mem-mapped IO (word-length)
298 ;
299
300 or dl, 0x1f ; XT-CF board control register address (base + 1fh)
301 in al, dx ; get control register
302 cmp al, 0xA0 ; test al against 0xA0:
303 jae .MemMapIO ; - use memory-mapped IO if >=A0h
304 or al, al ; test al against 0 (quicker than cmp):
305 jz .PortIO ; - use port-based IO (the default) if it was zero
306 ; otherwise, 0 < al < A0h, so fall to DMA mode
307
308.DMAIO:
309 ; work out how much we're transferring. We can't cross a physical 64KB boundary
310 ; but since the max transfer size is 64KB, we only ever need to do one or two DMA operations
311
312 ; first, normalise the pointer - we need ES to be a physical page address
313
314 mov ax, es
315 mov ch, cl ; max sectors is 128; also sectors << 8 = words, and we need cl...
316 mov cl, 4 ; ... for the rotate parameter
317 rol ax, cl ; ax = es rol 4
318 mov bx, ax ; bx = ax = es rol 4
319 and al, 0xf0 ; ax (15..4) now has es (11..0)
320 and bx, 0x000f ; bx (3..0) now has es (15..12)
321 add di, ax ; add offset portion of es (in ax) to di...
322 adc bl, 0 ; ... and if it overflowed, increment bl
323 mov es, bx ; es now has physical segment address >> 12
324
325 ; now check how much we can transfer without crossing a physical page boundary
326 mov bx, di ;
327 not bx ; 65535 - di = number of bytes we could transfer -1 now in bx
328 xor cl, cl ; zero cl; cx now has transfer size in words
329 shl cx, 1 ; words to bytes; CX has total byte count
330 dec cx ; calling DMA with 0 implies 1 byte transferred (so max is 64k exactly)
331 cmp bx, cx ; can we do it in one hit?
332 jae .LastDMA
333
334 ; at this point, the (bytes-1) for this transfer are in bx, total byte count -1 in cx
335 ; and we need to do 2 DMA operations as the buffer straddles a physical 64k boundary
336
337 sub cx, bx ; cx has bytes for 2nd transfer (as (x-1)-(y-1) = x-y)
338 dec cx ; cx has bytes-1 for 2nd transfer
339 xchg bx, cx ; bx = bytes-1 for 2nd transfer; cx = bytes-1 for this transfer
340 mov ah, 0x07 ; request a read DMA transfer
341 call DemandBasedTransferWithBytesInCX
342
343 ; DMA 1 of 2 done - set up registers for second transfer
344 mov cx, bx ; bytes-1 for the 2nd transfer back in cx
345 ; 1st transfer is done, now for the second
346ALIGN JUMP_ALIGN
347.LastDMA:
348 ; at this point, (bytes-1) for this transfer are in CX
349 mov ah, 0x07 ; request a read DMA transfer
350 call DemandBasedTransferWithBytesInCX
351
352 ; transfer is done, set ES back to a physical segment address
353 mov ax, es ;
354 mov cl, 4 ;
355 ror ax, cl ; ax = es ror 4. Since ES was >> 12 (for DMA controller) so
356 mov es, ax ; now it's back to normal format
357
358 ; pointer format restored - we're done
359 and dl, 0xE0 ; restore register values
360 ret
361
362
363;--------------------------------------------------------------------
364ALIGN JUMP_ALIGN
365WriteBlockToXTCF:
366 ; use config register to see what we're doing:
367 ; 0 = not set; use byte-length port-IO
368 ; < A0 = DMA via ch.3
369 ; > A0 = mem-mapped IO (word-length)
370 ;
371
372 or dl, 0x1f ; XT-CF board control register address (base + 1fh)
373 in al, dx ; get control register
374 cmp al, 0xA0 ; test al against 0xA0:
375 jae .MemMapIO ; - use memory-mapped IO if >=A0h
376 or al, al ; test al against 0 (quicker than cmp):
377 jz .PortIO ; - use port-based IO (the default) if it was zero
378 ; otherwise, 0 < al < A0h, so fall to DMA mode
379
380.DMAIO:
381 ; work out how much we're transferring. We can't cross a physical 64KB boundary
382 ; but since the max transfer size is 64KB, we only ever need to do one or two DMA operations
383
384 push di ; save di...
385 mov di, si ; ...and move si to di (for DMA routine)
386
387 ; first, normalise the pointer - we need ES to be a physical page address
388
389 mov ax, es
390 mov ch, cl ; max sectors is 64; also sectors << 8 = words, and we need cl...
391 mov cl, 4 ; ... for the rotate parameter
392 rol ax, cl ; ax = es rol 4
393 mov bx, ax ; bx = ax = es rol 4
394 and al, 0xf0 ; ax (15..4) now has es (11..0)
395 and bx, 0x000f ; bx (3..0) now has es (15..12)
396 add di, ax ; add offset portion of es (in ax) to si...
397 adc bl, 0 ; ... and if it overflowed, increment bl
398 mov es, bx ; es now has physical segment address >> 12
399
400 ; now check how much we can transfer without crossing a physical page boundary
401 mov bx, di ;
402 not bx ; 65535 - di = number of bytes we could transfer -1 now in bx
403 xor cl, cl ; zero cl; cx now has transfer size in words
404 shl cx, 1 ; words to bytes; CX has total byte count
405 dec cx ; calling DMA with 0 implies 1 byte transferred (so max is 64k exactly)
406 cmp bx, cx ; can we do it in one hit?
407 jae .LastDMA
408
409 ; at this point, the (bytes-1) for this transfer are in bx, total byte count -1 in cx
410 ; and we need to do 2 DMA operations as the buffer straddles a physical 64k boundary
411
412 sub cx, bx ; cx has bytes for 2nd transfer (as (x-1)-(y-1) = x-y)
413 dec cx ; cx has bytes-1 for 2nd transfer
414 xchg bx, cx ; bx = bytes-1 for 2nd transfer; cx = bytes-1 for this transfer
415 mov ah, 0x0b ; request a write DMA transfer
416 call DemandBasedTransferWithBytesInCX
417
418 ; DMA 1 of 2 done - set up registers for second transfer
419 mov cx, bx ; bytes-1 for the 2nd transfer back in cx
420 ; 1st transfer is done, now for the second
421ALIGN JUMP_ALIGN
422.LastDMA:
423 ; at this point, (bytes-1) for this transfer are in CX
424 mov ah, 0x0b ; request a write DMA transfer
425 call DemandBasedTransferWithBytesInCX
426
427 ; transfer is done, set ES back to a physical segment address
428 mov ax, es ;
429 mov cl, 4 ;
430 ror ax, cl ; ax = es ror 4. Since ES was >> 12 (for DMA controller) so
431 mov es, ax ; now it's back to normal format
432
433 mov si, di ; move di (used by DMA routine) back to si
434
435 ; pointers updated - we're done
436 pop di ;
437 and dl, 0xE0 ; restore register values
438 ret
439
440
441; -------------------------------------------------------------------------------------------------------------
442;
443; DemandBasedTransferWithBytesInCX
444; ================================
445;
446; DMA Transfer function:
447; - AH = 0x07 for read from media - demand/inc/non-auto-init/write(to memory)/ch3
448; - AH = 0x0b for write to media - demand/inc/non-auto-init/read(from memory)/ch3
449; - byte count -1 in CX
450; - XT-CF control register in DX
451; - physical segment address in ES *but* high four bits in low four bits (i.e. shr 12)
452; - buffer offset (from physical segment) in DI
453;
454; Note - cannot cross a physical segment boundary, but ES will be updated (maintaining the >>12
455; format) if after the last byte has been transferred the pointer needs to be updated to
456; the next physical segment.
457;
458; Preserves: BX, DX
459; Corrupts: AX, CX
460; Updates: ES, DI
461;
462; -------------------------------------------------------------------------------------------------------------
463
464ALIGN JUMP_ALIGN
465DemandBasedTransferWithBytesInCX:
466 cli ; clear interrupts while we set up the actual DMA transfer
467 mov al, 0x07 ; mask (4) + channel (3)
468 out 0x0a, al ; send to DMA mask register
469 mov al, ah ; retrieve the transfer mode passed in...
470 out 0x0b, al ; and send mode to DMA mode register
471 out 0x0c, al ; clear DMA byte-order flip-flop (write any value)
472 mov ax, di ; di has buffer offset
473 out 0x06, al ;
474 mov al, ah ;
475 out 0x06, al ; send offset to DMA controller address port for ch.3
476 mov ax, es ; es has physical segment address >> 12
477 out 0x82, al ; send the relavent 4-bits to DMA controller page for ch.3
478 out 0x0c, al ; clear DMA byte-order flip-flop (write any value)
479 mov ax, cx ; byte count to AX
480 out 0x07, al ; send low-byte of transfer size in bytes...
481 mov al, ah ; mov high byte to low byte...
482 out 0x07, al ; ...and high byte to DMA controller count port for ch.3
483 mov al, 0x03 ; clear bit mask (0) + channel (3)
484 out 0x0a, al ; send to DMA mask register - enable the DMA!
485 sti ; enable interrutps; let the CPU see to anything outstanding
486
487 mov ax, es ; update ES:DI (values have been loaded into DMA controller)
488 inc cx ; CX back to actual bytes
489 add di, cx ; add bytes to DI...
490 adc ax, 0 ; ...and if it overflowed, increment...
491 mov es, ax ; ...ES
492
493 add cx, 15 ; XT-CFv2 will present data in 16-byte blocks, but byte count may not
494 shr cx, 1 ; be divisable by 16 due to boundary crossing. So catch any < 16-byte
495 shr cx, 1 ; block by adding 15, then dividing bytes (in CX) by 16 to get the
496 shr cx, 1 ; total block requests. The 8237 is programmed with the actual byte
497 shr cx, 1 ; count and will end the transfer by asserting TC when done.
498
499.MoreToDo: ; at this point, cx must be >0
500 mov al, 0x40 ; 0x40 = Raise DRQ and clear XT-CFv2 transfer counter
501.NextDemandBlock:
502 out dx, al ; get up to 16 bytes from XT-CF card
503 loop .NextDemandBlock ; decrement CX and loop if <> 0
504 ; (Loop provides a wait-state between 16-byte blocks; do not unroll)
505.CleanUp:
506 ; check the transfer is actually done - in case another DMA operation messed things up
507 inc cx ; set up CX, in case we need to do an extra iteration
508 in al, 0x08 ; get DMA status register
509 and al, 0x08 ; test DMA ch.3 TC bit
510 jz .MoreToDo ; it wasn't set so get more bytes
511.EndDMA:
512 mov al, 0x10 ;
513 out dx, al ; set back to DMA enabled status
514 ret
515
516%endif ; 0
517
518
519
Note: See TracBrowser for help on using the repository browser.