Files
lba1-classic/LIB386/LIB_SAMP/DMA_CODE.ASM
Gwen Gourevich c5f4f6ba25 Initial commit
2021-10-27 10:34:18 +02:00

379 lines
11 KiB
NASM

.DATA
Status EQU 08h ;DMAC status port (read) \ same port
Command EQU 08h ;DMAC command port (write) / (read/write)
Request EQU 09h ;DMAC channel request (write-only)
DMA_Mask EQU 0Ah ;DMAC DMA_Mask (write-only)
Mode EQU 0Bh ;DMAC mode (write)
byte_ptr EQU 0Ch ;byte pointer flip-flop
addr EQU 000h ; per-channel base address
count EQU 001h ; per-channel byte count
read_cmd EQU 058h ; autoinitialising read
write_cmd EQU 054h ; auto write
set_cmd EQU 000h ; DMA_Mask set (enable dma)
reset_cmd EQU 004h ; DMA_Mask reset (disable)
page_table DW 00087h ; channel 0
DW 00083h ; channel 1
DW 00081h ; channel 2
DW 00082h ; channel 3
DW 0FFFFh ; ch 4 (not used)
DW 0008Bh ; ch 5
DW 00089h ; ch 6
DW 0008Ah ; ch 7
dmac2 DB 0 ; Flag set to non-zero when using the 2nd DMA controller
.CODE
MACRO adjust reg ; Adjust register port for 2nd DMA cont
local no_adjust
cmp byte ptr[dmac2], 0
jz no_adjust
shl reg, 1
add reg, 0C0h
no_adjust:
ENDM adjust
;+---------------------------------------------------------------------------+
;| int dma_setup(int Channel, char far *Buffer, unsigned Length) |
;| ------------------------------------------------------------------------- |
;| Channel = 0-7 |
;| Buffer = Address of data to transfer |
;| Length = Length of data to transfer |
;| ------------------------------------------------------------------------- |
;| Returns: 0 if no errors (dma_errno == 0) |
;| -1 if errors occurred (dma_errno set to indicate error.) |
;+---------------------------------------------------------------------------+
dma_setup PROC USES EBX,\
Channel:DWORD, Buffer:DWORD, Len:DWORD
;Convert 20-bit physical address of buffer into page:offset
mov edi, Buffer
mov ecx, edi
shr ecx, 8
; ch:di == The physical buffer base.
; Adjust channel number
mov byte ptr[dmac2], 0
mov bx, Channel
cmp bx, 4
jb OkChannel
and bx, 3
mov byte ptr[dmac2], 1
OkChannel:
; BX contains the adjusted channel number
mov si, read_cmd
add si, bx
mov cl, set_cmd ;allow dma requests
add cl, bl
;si contains READ/WRITE command for DMA controller
;cl contains confirmation command for DMA controller
shl bx, 1
;bx == Port # Channel*2
;-------------------------------------------------------------------------
; Calculations have been done ahead of time to minimize time with
; interrupts disabled.
;
; edi (ch:di) == physical base address (must be on word boundary for 16 bits)
;
; cl == Confirmation command (Unmasks the channel)
;
; bx == I/O port Channel*2 (This is where the address is written)
;
; si == Mode command for DMA
;-------------------------------------------------------------------------
; Now we shift the address and word count right one bit if in 16 bit mode.
cmp [dmac2], 0
jz AddrOk
shr ch, 1
rcr di, 1
shl ch, 1
shr [Len], 1 ;Odd byte lengths are rounded down
AddrOk:
;The "byte pointer" is also known as the LSB/MSB flip flop.
;By writing any value to it, the DMA controller registers are prepared
;to accept the address and length values LSB first.
mov dx, byte_pt ;Reset byte pointer Flip/flop
adjust dx
out dx, al
mov dx, bx ;dx=DMAC Base Address port
adjust dx
mov ax, di ;ax=LSW of 20-bit address
out dx, al ;Store LSB
mov al, ah
out dx, al ;Store MSB
;Write length to port (Channel * 2 + 1)
inc dx ;dx=DMAC Count port
mov eax, Len
out dx, al ;Write LSB of Length
mov al, ah
out dx, al ;Write MSB
;Write page to port page_table[channel]
mov ebx, Channel
shl bx, 1
mov dx, word ptr[OFFSET page_table + bx]
mov al, ch ;al=Page number
out dx, al ;Store the page
;Write Readcmd for channel to Mode port
mov dx, Mode ;dx=DMAC mode register
adjust dx
mov ax, si ;Load pre-calculated mode
out dx, al ;Write it to the DSP
mov dx, DMA_Mask ;dx=DMAX DMA_Mask register
adjust dx
mov al, cl ;al=pre-calulated DMA_Mask value
out dx, al ;Write DMA_Mask (allow dma on this channel)
ret
dma_setup ENDP
;+---------------------------------------------------------------------------+
;| int prevent_dma(int Channel) |
;| ------------------------------------------------------------------------- |
;| Channel = 0-7 |
;| Prevents DMA requests from Channel by masking bit in DMA_C. |
;| ------------------------------------------------------------------------- |
;+---------------------------------------------------------------------------+
prevent_dma PROC ,\
Channel:DWORD
; Check channel number range
mov dx, DMA_Mask
mov eax, Channel
cmp al, 4
jb OkChannel
and al, 3
shl dx, 1
add dx, 0C0h
OkChannel:
add al, reset_cmd ; Add disable DMA requests
out dx, al
ret
prevent_dma ENDP
;+---------------------------------------------------------------------------+
;| int allow_dma(int Channel) |
;| ------------------------------------------------------------------------- |
;| Channel = 0-7 |
;| Unmasks DMA on the specified channel. |
;| ------------------------------------------------------------------------- |
;+---------------------------------------------------------------------------+
allow_dma PROC ,\
Channel:DWORD
; Check channel number range
mov dx, DMA_Mask
mov eax, Channel
cmp al, 4
jb OkChannel
and al, 3
shl dx, 1
add dx, 0C0h
OkChannel:
add al, set_cmd ; Add enable DMA requests
out dx, al
ret
allow_dma ENDP
;+---------------------------------------------------------------------------+
;| int dma_count(Channel) |
;| ------------------------------------------------------------------------- |
;| Channel = 0-7 |
;| ------------------------------------------------------------------------- |
;| Returns: -1 if DMA transaction completed |
;| else returns the number of bytes/words left to transfer |
;+---------------------------------------------------------------------------+
PROC dma_count ,\
Channel:DWORD
mov eax, Channel
mov dx, ax
and dx, 3
shl dx, 1
add dx, count
cmp al, 4
jb OkChannel
shl dx, 1
add dx, 0C0h
OkChannel:
xor eax, eax
cli
in al, dx
mov ah, al
in al, dx
sti
xchg al, ah
ret
dma_count ENDP
;+---------------------------------------------------------------------------+
;| unsigned dma_addr(Channel) |
;| ------------------------------------------------------------------------- |
;| Channel = 0-7 |
;| ------------------------------------------------------------------------- |
;| Returns: Current address word of that channel |
;| Value must be multiplied by 2 for a 16-bit channel. |
;| It is best to start at offset 0, ie on a 64K boundary |
;+---------------------------------------------------------------------------+
dma_addr PROC ,\
Channel:DWORD
mov eax, Channel
mov dx, ax
and dx, 3
shl dx, 1
cmp al, 4
jb OkChannel
shl dx, 1
add dx, 0C0h
OkChannel:
xor eax, eax
cli
in al, dx
mov ah, al
in al, dx
sti
xchg al, ah
ret
dma_addr ENDP
VOID *DMABuffer, *DMAAllocBuffer;
BYTE LockedDMARegion;
#define MAX_DMABUFFERATTEMPTS 10;
;+---------------------------------------------------------------------------+
;| BYTE *AlignOn4KBoundry( BYTE *Buf ) |
;| ------------------------------------------------------------------------- |
;| Align Buf to the nearest 4K page |
;+---------------------------------------------------------------------------+
#define AlignOn4KBoundry(Buf) ((Buf + 0x00000FFF) & 0xFFFFF000)
;+---------------------------------------------------------------------------+
;| BYTE *AllocateDMABuffer( WORD BufferSize ) |
;| ------------------------------------------------------------------------- |
;| BufferSize = 0-64K |
;| ------------------------------------------------------------------------- |
;| Returns: Buffer if successful |
;| NULL if error |
;+---------------------------------------------------------------------------+
BYTE *AllocateDMABuffer( WORD BufferSize )
{
VOID *Tried[ MAX_DMABUFFERATTEMPTS ] ;
VOID *Buf, *DMAAddr ;
int Tries, Error ;
for (Tries = 0; Tries < MAX_DMABUFFERATTEMPTS; Tries++)
Tried[ Tries ] = NULL ;
Tries = 0;
LockedDMARegion = FALSE;
do
{
// printf( "DMA buffer allocation attempt: %d\n", Tries + 1 ) ;
if ((DMAAllocBuffer = (VOID *)DosMalloc( BufferSize + 0xFFF)) != NULL)
{
DMABuffer = AlignOn4KBoundry(DMAAllocBuffer);
if ( ((LONG)DMABuffer & 0xF0000) !=
(((LONG)DMABuffer + BufferSize) & 0xF0000) )
{
Tried[ Tries ] = DMAAllocBuffer ;
DMAAllocBuffer = NULL ;
DMABuffer = NULL ;
}
}
Tries++ ;
DMAAllocBuf = Buf;
DMABuffer = DMAAddr ;
}
while ((DMAAllocBuf == NULL) && (Tries < MAX_DMABUFFERATTEMPTS)) ;
for (wTries = 0; wTries < MAX_DMABUFFERATTEMPTS; wTries++)
{
if (alpTried[ wTries ])
free( alpTried[ wTries ] ) ;
else
break ;
}
return( DMABuffer ) ;
;+---------------------------------------------------------------------------+
;| VOID FreeDMABuffer( VOID ) |
;| ------------------------------------------------------------------------- |
;| Free the DMA buffer |
;+---------------------------------------------------------------------------+
VOID FreeDMABuffer( VOID )
{
if (LockedDMARegion)
UnlockDMARegion( &gDDS, 0 ) ;
if (DMAAllocBuf)
{
free( glpDMAAllocBuf ) ;
DMAAllocBuf = NULL ;
}
}