Obsah / Ovladače / DMA / Interní funkce ovladače řadiče DMA
Zdrojový kód:
DRIVERS\SYSTEM\DMA.ASM
Interní
funkce ovladače řadiče DMA
Definice konstant
řadiče DMA:
DMA_CHANNELS EQU 8 ; number of DMA channels
DMA_CASCADE EQU 4 ; channel 4 is reserved (cascade)
; ------------- DMA modes
DMAMODE_VERIFY EQU B6 ; DMA verify (verify,increment,single)
DMAMODE_READ EQU B2+B6 ; DMA read (read, increment, single)
DMAMODE_WRITE EQU B3+B6 ; DMA write (write, increment, single)
DMAMODE_CASCADE EQU B6+B7 ; DMA cascade
DMAMODE_INIT EQU B4 ; DMA autoinit
; ------------- Ports - controller 1
DMA1_BASE EQU 0 ; base port of controller 1 (8-bit)
DMA1_CMD EQU DMA1_BASE+8 ; command register WRITE (controller 1)
DMA1_STAT EQU DMA1_BASE+8 ; status register READ (controller 1)
DMA1_REQ EQU DMA1_BASE+9 ; request register WRITE (controller 1)
DMA1_MASK EQU DMA1_BASE+10 ; single-channel mask WRITE (ctrl 1)
DMA1_MODE EQU DMA1_BASE+11 ; mode register WRITE (controller 1)
DMA1_CLRFF EQU DMA1_BASE+12 ; clear flip-flop WRITE (controller 1)
DMA1_RESET EQU DMA1_BASE+13 ; master clear WRITE (controller 1)
DMA1_TEMP EQU DMA1_BASE+13 ; temporary register READ (ctrl 1)
DMA1_CMASK EQU DMA1_BASE+14 ; clear mask (controller 1)
DMA1_AMASK EQU DMA1_BASE+15 ; all-channels mask WRITE (ctrl 1)
; ------------- Ports - controller 2
DMA2_BASE EQU 0c0h ; base port of controller 2 (16-bit)
DMA2_CMD EQU DMA2_BASE+2*8 ; command register WRITE (controller 2)
DMA2_STAT EQU DMA2_BASE+2*8 ; status register READ (controller 2)
DMA2_REQ EQU DMA2_BASE+2*9 ; request register WRITE (controller 2)
DMA2_MASK EQU DMA2_BASE+2*10 ; single-channel mask WRITE (ctrl 2)
DMA2_MODE EQU DMA2_BASE+2*11 ; mode register WRITE (controller 2)
DMA2_CLRFF EQU DMA2_BASE+2*12 ; clear flip-flop WRITE (controller 2)
DMA2_RESET EQU DMA2_BASE+2*13 ; master clear WRITE (controller 2)
DMA2_TEMP EQU DMA2_BASE+2*13 ; temporary register READ (ctrl 2)
DMA2_CMASK EQU DMA2_BASE+2*14 ; clear mask (controller 2)
DMA2_AMASK EQU DMA2_BASE+2*15 ; all-channels mask WRITE (ctrl 2)
; ------------- Ports - address registers
DMA_ADDR0 EQU DMA1_BASE+0 ; channel 0 address
DMA_ADDR1 EQU DMA1_BASE+2 ; channel 1 address
DMA_ADDR2 EQU DMA1_BASE+4 ; channel 2 address
DMA_ADDR3 EQU DMA1_BASE+6 ; channel 3 address
DMA_ADDR4 EQU DMA2_BASE+2*0 ; channel 4 address
DMA_ADDR5 EQU DMA2_BASE+2*2 ; channel 5 address
DMA_ADDR6 EQU DMA2_BASE+2*4 ; channel 6 address
DMA_ADDR7 EQU DMA2_BASE+2*6 ; channel 7 address
; ------------- Ports - address registers
DMA_COUNT0 EQU DMA1_BASE+1 ; channel 0 count
DMA_COUNT1 EQU DMA1_BASE+3 ; channel 1 count
DMA_COUNT2 EQU DMA1_BASE+5 ; channel 2 count
DMA_COUNT3 EQU DMA1_BASE+7 ; channel 3 count
DMA_COUNT4 EQU DMA2_BASE+2*1 ; channel 4 count
DMA_COUNT5 EQU DMA2_BASE+2*3 ; channel 5 count
DMA_COUNT6 EQU DMA2_BASE+2*5 ; channel 6 count
DMA_COUNT7 EQU DMA2_BASE+2*7 ; channel 7 count
; ------------- Ports - page registers
DMA_PAGE_BASE EQU 80h ; base port of page registers
DMA_PAGE0 EQU DMA_PAGE_BASE+7 ; page 0
DMA_PAGE1 EQU DMA_PAGE_BASE+3 ; page 1
DMA_PAGE2 EQU DMA_PAGE_BASE+1 ; page 2
DMA_PAGE3 EQU DMA_PAGE_BASE+2 ; page 3
DMA_PAGE4 EQU DMA_PAGE_BASE+15; page 4
DMA_PAGE5 EQU DMA_PAGE_BASE+11; page 5
DMA_PAGE6 EQU DMA_PAGE_BASE+9 ; page 6
DMA_PAGE7 EQU DMA_PAGE_BASE+10; page 7
|
DMA_CHANNELS udává
počet kanálů řadiče DMA. Pro jednoduchost se započítává
i DMA4 (kaskáda), ale je označen jako použitý. DMA_CASCADE je
číslo DMA pro kaskádové připojení prvního řadiče k
druhému.
Konstanty
DMAMODE_VERIFY, DMAMODE_READ, DMAMODE_WRITE, DMAMODE_CASCADE a
DMAMODE_INIT jsou řídicí slova pro nastavení módu práce
řadiče DMA.
Ve výše uvedeném
seznamu dále následují adresy portů prvního a druhého
řadiče. DMA1_BASE je bázový port prvního řadiče a
DMA2_BASE je bázový port druhého řadiče. DMA_ADDR0..7 jsou
porty pro nastavení adresy přenosu jednotlivých kanálů.
DMA_COUNT0..7 jsou porty čítačů dat k přenosu. DMA_PAGE_BASE
je bázová adresa obvodu stránkových registrů. DMA_PAGE0..7
jsou adresy stránkových registrů pro jednotlivé kanály.
; -----------------------------------------------------------------------------
; Internal function: Enable DMA channel
; -----------------------------------------------------------------------------
; INPUT: EAX = DMA channel number (0 to 7)
; NOTES: EAX not checked for validity.
; -----------------------------------------------------------------------------
; ------------- Check if it is controller 2
DMADevEnable: cmp al,4 ; is it controller 2?
jae DMADevEnable2 ; it is controller 2
; ------------- Enable DMA channel on controller 1
out DMA1_MASK,al ; enable DMA channel on controller 1
ret
; ------------- Enable DMA channel on controller 2
DMADevEnable2: sub al,4 ; Al <- relative DMA channel number
out DMA2_MASK,al ; enable DMA channel on controller 2
add al,4 ; AL <- return DMA channel number
ret
|
DMADevEnable
je interní funkce řadiče DMA. Povolí DMA kanál, jehož
číslo obsahuje na vstupu funkce registr EAX. Hodnota registru
EAX není kontrolovaná na platnost a musí být v rozsahu 0 až
7.
; -----------------------------------------------------------------------------
; Internal function: Disable DMA channel
; -----------------------------------------------------------------------------
; INPUT: EAX = DMA channel number (0 to 7)
; NOTES: EAX not checked for validity.
; -----------------------------------------------------------------------------
; ------------- Check if it is controller 2
DMADevDisable: cmp al,4 ; is it controller 2?
jae DMADevDisable2 ; it is controller 2
; ------------- Disable DMA channel on controller 1
add al,B2 ; set disable flag
out DMA1_MASK,al ; disable DMA channel on controller 1
sub al,B2 ; return DMA channel number
ret
; ------------- Disable DMA channel on controller 2 (bit 2 is set)
DMADevDisable2: out DMA2_MASK,al ; disable DMA channel on controller 2
ret
|
DMADevDisable
je interní funkce řadiče DMA. Zakáže DMA kanál, jehož
číslo obsahuje na vstupu funkce registr EAX. Hodnota registru
EAX není kontrolovaná na platnost a musí být v rozsahu 0 až
7.
; -----------------------------------------------------------------------------
; Internal function: Clear DMA channel flip-flop
; -----------------------------------------------------------------------------
; INPUT: EAX = DMA channel number (0 to 7)
; NOTES: EAX not checked for validity.
; -----------------------------------------------------------------------------
; ------------- Check if it is controller 2
DMADevClearFF: cmp al,4 ; is it controller 2?
jae DMADevClearFF2 ; controller 2
; ------------- Clear DMA channel flip-flop on controller 1
out DMA1_CLRFF,al ; clear DMA channel flip-flop on ctrl 1
ret
; ------------- Clear DMA channel flip-flop on controller 2
DMADevClearFF2: out DMA2_CLRFF,al ; clear DMA channel flip-flop on ctrl 2
ret
|
DMADevClearFF
je interní funkce řadiče DMA. Nuluje flip-flop příznak
řadiče DMA v závislosti na čisle DMA kanálu obsaženém na
vstupu funkce v registru EAX. Hodnota registru EAX není
kontrolovaná na platnost a musí být v rozsahu 0 až 7.
; -----------------------------------------------------------------------------
; Internal function: Set DMA channel mode
; -----------------------------------------------------------------------------
; INPUT: EAX = DMA channel number (0 to 7)
; BL = DMA channel mode
; B0-B1: must be 0
; B2-B3: transfer mode
; 00=verify
; 01=read from device (write to memory)
; 10=write to device (read from memory)
; B4: 1=autoinitialisation enabled
; B5: 0=increment address, 1=decrement address
; B6-B7: 00=demand mode
; 01=single mode
; 10=block transfer
; 11=cascade mode
; preset modes: DMAMODE_VERIFY - verify
; DMAMODE_READ - DMA read from device
; DMAMODE_WRITE - DMA write to device
; DMAMODE_CASCADE - DMA cascade
; DMAMODE_INIT - DMA autoinit
; NOTES: EAX not checked for validity.
; -----------------------------------------------------------------------------
; ------------- Check if it is controller 2
DMADevSetMode: push eax ; push EAX
cmp al,4 ; is it controller 2?
jae DMADevSetMode2 ; controller 2
; ------------- Set DMA channel mode on controller 1
or al,bl ; AL <- channel + mode
out DMA1_MODE,al ; set DMA mode on controller 1
pop eax ; pop EAX
ret
; ------------- Set DMA channel mode on controller 2
DMADevSetMode2: sub al,4 ; AL <- relative DMA channel number
or al,bl ; AL <- channel + mode
out DMA2_MODE,al ; set DMA mode on controller 2
pop eax ; pop EAX
ret
|
DMADevSetMode
je interní funkce řadiče DMA. Nastaví mód DMA kanálu,
jehož číslo obsahuje na vstupu funkce registr EAX. Hodnota
registru EAX není kontrolovaná na platnost a musí být v
rozsahu 0 až 7. Registr BL obsahuje řídicí slovo pro
nastavení módu kanálu - lze použít předdefinované
konstanty DMAMODE_VERIFY .. DMAMODE_INIT.
; -----------------------------------------------------------------------------
; Internal function: Set DMA channel page
; -----------------------------------------------------------------------------
; INPUT: EAX = DMA channel number (0 to 7)
; EDX = DMA address (physical address)
; NOTES: EAX not checked for validity.
; Only address bits 16-23 (DMA0-3) or 17-23 (DMA4-7) are used.
; -----------------------------------------------------------------------------
; ------------- Push registers
DMADevSetPage: push eax ; push EAX
push edx ; push EDX
; ------------- Prepare DMA address
shr edx,16 ; DL <- page number
cmp al,4 ; is it controller 1?
jb DMADevSetPage2 ; it is controller 1
and dl,~B0 ; clear bit 16 for controller 2
; ------------- Prepare port address
DMADevSetPage2: mov al,[eax+DMAPagePort] ; AL <- address (here AH = 0)
; ------------- Set DMA channel page
xchg eax,edx ; DX <- port, AL <- page
out dx,al ; set DMA channel page
; ------------- Pop registers
pop edx ; pop EDX
pop eax ; pop EAX
ret
; ------------- DMA page ports
DMAPagePort: db DMA_PAGE0 ; DMA 0
db DMA_PAGE1 ; DMA 1
db DMA_PAGE2 ; DMA 2
db DMA_PAGE3 ; DMA 3
db DMA_PAGE4 ; DMA 4
db DMA_PAGE5 ; DMA 5
db DMA_PAGE6 ; DMA 6
db DMA_PAGE7 ; DMA 7
|
DMADevSetPage
je interní funkce řadiče DMA. Nastaví stránkový registr DMA
kanálu, jehož číslo obsahuje na vstupu funkce registr EAX.
Hodnota registru EAX není kontrolovaná na platnost a musí být
v rozsahu 0 až 7. Registr EDX obsahuje adresu ve fyzické
paměti - jsou použity pouze bity 16..23 (pro DMA0..3) nebo
17..23 (pro DMA4..7), ostatní adresové bity jsou ignorovány.
; -----------------------------------------------------------------------------
; Internal function: Set DMA transfer address (offset and page)
; -----------------------------------------------------------------------------
; INPUT: EAX = DMA channel number (0 to 7)
; EDX = DMA address (physical address, only bits 0-23 are used)
; NOTES: EAX not checked for validity.
; DMA transfer cannot cross 64K (or 128K for DMA4-7) boundary.
; DMA5 to DMA7 address must be word aligned.
; Transfers are limited to lower 16 MB of physical memory.
; DMADevClearFF should be caled first.
; -----------------------------------------------------------------------------
; ------------- Set DMA channel page
DMADevSetAddr: call DMADevSetPage ; set DMA channel page
; ------------- Push registers
push eax ; push EAX
push edx ; push EDX
; ------------- Prepare port for controller 1 (00h, 02h, 04h, 06h)
shl eax,1 ; AX <- channel number*2
cmp al,DMA1_BASE+4*2 ; is it DMA0..DMA3?
jb DMADevSetAddr2 ; it is DMA0..DMA3
; ------------- Prepare port for controller 2 (0C0h, 0C4h, 0C8h, 0CCh)
shl eax,1 ; AX <- channel number*4
add al,DMA2_BASE-DMA1_BASE-4*4 ; AL <- port
shr edx,1 ; convert address to words
; ------------- Set transfer address
DMADevSetAddr2: xchg eax,edx ; DX <- port, EAX <- address
out dx,al ; set transfer address LOW
mov al,ah ; AL <- transfer address HIGH
jmp short DMADevSetAddr4 ; short delay
DMADevSetAddr4: out dx,al ; set transfer address HIGH
; ------------- Pop registers
pop edx ; pop EDX
pop eax ; pop EAX
ret
|
DMADevSetAddr
je interní funkce řadiče DMA. Nastaví adresu k přenosu dat
DMA kanálu, jehož číslo obsahuje na vstupu funkce registr
EAX. Hodnota registru EAX není kontrolovaná na platnost a musí
být v rozsahu 0 až 7. Registr EDX obsahuje adresu ve fyzické
paměti - jsou použity pouze bity 0..23, ostatní adresové bity
jsou ignorovány. Před voláním funkce by měl být resetován
flip-flop příznak pomocí funkce DMADevClearFF.
; -----------------------------------------------------------------------------
; Internal function: Set DMA transfer size
; -----------------------------------------------------------------------------
; INPUT: EAX = DMA channel number (0 to 7)
; ECX = DMA transfer size (cannot be 0; B0 is ignored for DMA4-7)
; NOTES: EAX not checked for validity.
; DMA transfer cannot cross 64K (or 128K for DMA4-7) boundary.
; Maximal transfer size is 64K for DMA0-3 and 128K for DMA4-7.
; DMADevClearFF should be caled first.
; -----------------------------------------------------------------------------
; ------------- Push registers
DMADevSetSize: push eax ; push EAX
push ecx ; push ECX
push edx ; push EDX
; ------------- Prepare port for controller 1 (01h, 03h, 05h, 07h)
shl eax,1 ; AX <- channel number*2
inc eax ; EAX <- channel number*2 + 1
cmp al,DMA1_BASE+4*2+1 ; is it DMA0..DMA3?
jb DMADevSetSize2 ; it is DMA0..DMA3
; ------------- Prepare port for controller 2 (0C2h, 0C6h, 0CAh, 0CEh)
shl eax,1 ; AX <- channel number*4 + 2
add al,DMA2_BASE-DMA1_BASE-4*4 ; AL <- port
shr ecx,1 ; convert size to words
; ------------- Set transfer size
DMADevSetSize2: dec ecx ; correct size (=last transfered item)
xchg eax,edx ; DX <- port
xchg eax,ecx ; AX <- size
out dx,al ; set transfer size LOW
mov al,ah ; AL <- transfer size HIGH
jmp short DMADevSetSize4 ; short delay
DMADevSetSize4: out dx,al ; set transfer size HIGH
; ------------- Pop registers
pop edx ; pop EDX
pop ecx ; pop ECX
pop eax ; pop EAX
ret
|
DMADevSetSize
je interní funkce řadiče DMA. Nastaví velikost
přenášených dat DMA kanálu, jehož číslo obsahuje na
vstupu funkce registr EAX. Hodnota registru EAX není
kontrolovaná na platnost a musí být v rozsahu 0 až 7. Registr
ECX obsahuje počet bajtů dat k přenosu (nesmí být hodnota
0). Před voláním funkce by měl být resetován flip-flop
příznak pomocí funkce DMADevClearFF.
; -----------------------------------------------------------------------------
; Internal function: Get DMA remaining transfer size
; -----------------------------------------------------------------------------
; INPUT: EAX = DMA channel number (0 to 7)
; OUTPUT: ECX = remaining bytes to transfer (0 if transfer finished)
; NOTES: EAX not checked for validity.
; If called before the channel has been used, it may return 1
; (or 2 on DMA4..DMA7).
; DMADevClearFF should be caled first.
; -----------------------------------------------------------------------------
; ------------- Push registers
DMADevGetSize: push eax ; push EAX
push edx ; push EDX
; ------------- Prepare port for controller 1 (01h, 03h, 05h, 07h)
shl eax,1 ; EAX <- channel number*2
inc eax ; EAX <- channel number*2 + 1
cmp al,DMA1_BASE+4*2+1 ; is it DMA0..DMA3?
jb DMADevGetSize2 ; it is DMA0..DMA3
; ------------- Prepare port for controller 2 (0C2h, 0C6h, 0CAh, 0CEh)
shl eax,1 ; EAX <- channel number*4 + 2
add al,DMA2_BASE-DMA1_BASE-4*4 ; AL <- port
; ------------- Get transfer size
DMADevGetSize2: xchg eax,edx ; DX <- port
xor eax,eax ; EAX <- 0
in al,dx ; get transfer size LOW
xchg eax,ecx ; ECX <- transfer size LOW
jmp short DMADevGetSize4 ; short delay
DMADevGetSize4: in al,dx ; get transfer size HIGH
mov ch,al ; CH <- transfer size HIGH
inc ecx ; correction
; ------------- Pop registers
pop edx ; pop EDX
pop eax ; pop EAX
; ------------- Convert to words for DMA4 - DMA7
cmp al,4 ; is it DMA4..DMA7?
jb DMADevGetSize8 ; it is DMA0..DMA3
shl ecx,1 ; convert to words
DMADevGetSize8: ret
|
DMADevGetSize
je interní funkce řadiče DMA. Slouží ke zjištění
zbývajících dat k přenosu DMA kanálu, jehož číslo
obsahuje na vstupu funkce registr EAX. Hodnota registru EAX není
kontrolovaná na platnost a musí být v rozsahu 0 až 7. Na
výstupu funkce je v registru ECX navrácen zbývající počet
bajtů dat k přenosu. Hodnota 0 indikuje ukončení přenosu.
Při prvním volání (pokud kanál nebyl dosud inicializován)
může být navrácena hodnota 1 (nebo 2 u DMA4..DMA7). Před
voláním funkce by měl být resetován flip-flop příznak
pomocí funkce DMADevClearFF.
; -----------------------------------------------------------------------------
; Internal function: Check if transfer is still running
; -----------------------------------------------------------------------------
; INPUT: EAX = DMA channel number (0 to 7)
; OUTPUT: CY = transfer is not running
; NOTES: EAX not checked for validity.
; If called before the channel has been used, it may return NC.
; It may be faster than DMADevGetSize.
; -----------------------------------------------------------------------------
; ------------- Push registers
DMADevRunning: push eax ; push EAX
push edx ; push EDX
; ------------- Prepare port for controller 1 (01h, 03h, 05h, 07h)
shl eax,1 ; EAX <- channel number*2
inc eax ; EAX <- channel number*2 + 1
cmp al,DMA1_BASE+4*2+1 ; is it DMA0..DMA3?
jb DMADevRunning2 ; it is DMA0..DMA3
; ------------- Prepare port for controller 2 (0C2h, 0C6h, 0CAh, 0CEh)
shl eax,1 ; EAX <- channel number*4 + 2
add al,DMA2_BASE-DMA1_BASE-4*4 ; AL <- port
; ------------- Get transfer size
DMADevRunning2: xchg eax,edx ; DX <- port
in al,dx ; get transfer size 1
cmp al,0ffh ; is transfer still running?
jne DMADevRunning6 ; transfer is still running
jmp short DMADevRunning4 ; short delay
DMADevRunning4: in al,dx ; get transfer size 2
cmp al,0ffh ; is transfer still running?
DMADevRunning6: cmc ; NC = transfer is still running
; ------------- Pop registers
pop edx ; pop EDX
pop eax ; pop EAX
ret
|
DMADevRunning
je interní funkce řadiče DMA. Slouží ke zjištění, zda
dosud probíhá přenos dat na DMA kanálu, jehož číslo
obsahuje na vstupu funkce registr EAX. Hodnota registru EAX není
kontrolovaná na platnost a musí být v rozsahu 0 až 7. Na
výstupu funkce je navrácen příznak CY, pokud přenos dat
neprobíhá. Při prvním volání (pokud kanál nebyl dosud
inicializován) může být navrácen příznak probíhajícího
přenosu (NC). Funkce se používá jako rychlejší varianta k
testu přenosu než DMADevGetSize.
Obsah / Ovladače / DMA / Interní funkce ovladače řadiče DMA