home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
assemblr
/
library
/
sound
/
sb_dma
/
dma_code.asm
next >
Wrap
Assembly Source File
|
1992-03-12
|
8KB
|
238 lines
IDEAL
MODEL small
;---------------------------------------------------------------------------
; IBM-PC(tm) compatible programmer's DMA library
; Version 1.0
;---------------------------------------------------------------------------
; Copyright (C) 1992, Heath I Hunnicutt
; Portions Copyright (C) 1986, Carnegie Mellon University
;---------------------------------------------------------------------------
; Thanks to: Gary Nemirovsky
;---------------------------------------------------------------------------
; This document is for free public distribution. It is unlawful to
; sell this document, or any work based substantially upon it.
;---------------------------------------------------------------------------
; This assembly code defines 3 functions that are intended for use
; by C programmers in code that requires access to the DMA system.
;
; DMA transfers occur asynchronously to the CPU's activity, so they
; are quite efficient.
;
; The general sequence for using the DMA is:
; int channel=1; /* DO NOT use channel 0 */
; dma_reset(channel);
; dma_setup(channel,(char far *)My_Buffer,sizeof(My_Buffer),1);
; /* Insert "foreground" code here. */
; while (dma_done!=-1)
; ;
;---------------------------------------------------------------------------
; Caution: There is some weird bug in dma_setup that causes crashes
; if the length is greater than approx. 32000.
; There doesn't seem to be any 'magic-number' like 32768
; that relates to this.
;---------------------------------------------------------------------------
; Send suggestions, questions, comments, knoweledge to:
; heathh@cco.caltech.edu (also @tybalt.cco.caltech.edu)
; (or hihunn@through.ugcs.caltech.edu -- not preferred)
;---------------------------------------------------------------------------
; PUBLIC FUNCTIONS
; void far dma_reset(int Channel)
; void far dma_setup(int Channel,char far *Buffer,int Length,int Dir)
; int far dma_done(int Channel)
;---------------------------------------------------------------------------
; How to assemble this code:
; You'll need Turbo Assembler(tm) from Borland(tm) Internationl
; TASM /mx /m2 dma_code
;---------------------------------------------------------------------------
byte_ptr EQU 00ch ; byte pointer flip-flop
mode EQU 00bh ; dma controller mode register
dma_mask EQU 00ah ; dma controller mask register
addr EQU 000h ; per-channel base address
count EQU 001h ; per-channel byte count
read_cmd EQU 048h ; read mode
write_cmd EQU 044h ; write mode
set_cmd EQU 000h ; mask set
reset_cmd EQU 004h ; mask reset
; dma controller page register table
; this table maps from channel number to the i/o port number of the
; page register for that channel
DATASEG
page_table DW 00087h ; channel 0
DW 00083h ; channel 1
DW 00081h ; channel 2
DW 00082h ; channel 3
CODESEG
MACRO zero reg
xor reg,reg
ENDM zero
PUBLIC _dma_setup,_dma_reset,_dma_done
; --------------------------------------------------------------------------
; void far dma_setup(int Channel,char far *Buffer,int Length,int Dir)
;--------------------------------------------------------------------------
; Channel = 0-3 !Channel 0 is often reserved for memory refresh!
; Buffer = Address of data to play
; Length = Length of data to play
; NOTE: For some reason, certain combinations of Buffer and
; Length cause the system to lock up. As a rule,
; keep the Length < 30000 (dec), and DO NOT try to play
; any region of memory that might be simultaneously
; accessed, such as shared memory, code segments
; including the ROM and TSRs that hook interrupts, etc.,
; Dir = Direction to move bytes. 1 == Out to the BUS (TO the card)
; 0 == In from the BUS and cards.
;---------------------------------------------------------------------------
PROC _dma_setup FAR
ARG Channel:WORD,Buffer:DWORD,Len:WORD,Dir:WORD
push bp
mov bp,sp
push ax bx cx dx si di
pushf
;Convert seg:ofs Buffer to 20-bit physical address
;Assumes operating in 8086/real-mode
mov bx,[WORD PTR Buffer]
mov ax,[WORD PTR Buffer+2]
mov cl,4
rol ax,cl
mov ch,al
and al,0F0h
add ax,bx
adc ch,0
and ch,0Fh
mov di,ax
; (ch << 16) + di == The physical buffer base.
;Calculate the port to receive this address
mov bx,[Channel]
shl bx,1
;bx == Port # Channel*2
;Determine which command byte will be written later
cmp [WORD PTR Dir],0
jnz SHORT @@Do_Read
mov al,write_cmd
jmp SHORT @@Do_Mode
@@Do_Read:
mov al,read_cmd
@@Do_Mode:
mov cx,[Channel]
add al,cl
zero ah
mov si,ax
mov ax,set_cmd
add al,cl
mov cl,al
;si contains READ/WRITE command for DMA controller
;cl contains confirmation command for DMA controller
;-------------------------------------------------------------------------
; Calculations have been done ahead of time to minimize time with
; interrupts disabled.
;
; ch:di == physical base address
;
; cl == Confirmation command (Tells DMA we're done bothering it.)
;
; bx == I/O port Channel*2 (This is where the address is written)
;
; si == Mode command for DMA
;-------------------------------------------------------------------------
cli ;Disable interrupts while mucking with DMA
;I have no idea what purpose these next three instructions serve.
;Without them, the procedure crashes and burns.
mov al,0
mov dx,byte_ptr ; reset the byte pointer to be safe
out dx,al
mov ax,di ;ax=LSW of 20-bit address
mov dx,bx
out dx,al ;Store LSB
mov al,ah
out dx,al ;Store next byte
mov al,ch
mov dx,[bx + OFFSET page_table] ;Port is the "Page index"
out dx,al ;Store the page
;Write length to port Channel*2 + 1
mov ax,[Len]
mov dx,bx
add dx,count
out dx,al ;Write LSB
mov al,ah
out dx,al ;Write MSB
mov ax,si ;Load pre-calculated command
mov dx,mode
out dx,al ;Write it to the DSP
mov dx,dma_mask
mov al,cl
out dx,al
popf
pop di si dx cx bx ax
pop bp
ret
ENDP _dma_setup
;---------------------------------------------------------------------------
; void far dma_reset(int Channel)
;---------------------------------------------------------------------------
; Channel = 0-4
; Resets the specified channel.
; NOTE: It is a very bad idea to reset channel 0.
;---------------------------------------------------------------------------
PROC _dma_reset FAR
ARG Channel:Word
push bp
mov bp,sp
push ax dx
mov dx,dma_mask
mov ax,reset_cmd
add ax,[Channel]
out dx,al
pop dx ax
pop bp
ret
ENDP _dma_reset
;---------------------------------------------------------------------------
; int far dma_done(Channel)
;---------------------------------------------------------------------------
; Channel = 0-4
;---------------------------------------------------------------------------
; Returns: -1 if DMA transaction completed
; (Maybe it returns the number of bytes left to transfer?)
;---------------------------------------------------------------------------
PROC _dma_done FAR
ARG Channel:Word
push bp
mov bp,sp
pushf
push dx
mov dx,[Channel]
shl dx,1
add dx,count
cli
in al,dx
mov ah,al
in al,dx
xchg al,ah
pop dx
popf
pop bp
ret
ENDP _dma_done
END