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@…, 12 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.