home *** CD-ROM | disk | FTP | other *** search
- Xref: sparky comp.sys.ibm.pc.programmer:594 alt.msdos.programmer:2785
- Path: sparky!uunet!cs.utexas.edu!wupost!sdd.hp.com!sgiblab!public!heard
- From: heard@public.BTR.COM (Charles M. Heard heard@btr.com)
- Newsgroups: comp.sys.ibm.pc.programmer,alt.msdos.programmer
- Subject: Re: DMA-- *HOW?* (long)
- Summary: Discussion of two-cycle vs fly-by plus sample working code
- Keywords: DMA
- Message-ID: <8332@public.BTR.COM>
- Date: 19 Nov 92 04:54:42 GMT
- References: <im14u2c.721884439@camelot> <1992Nov17.084050.9545@cs.joensuu.fi> <1992Nov17.162107.15828@athena.cs.uga.edu>
- Followup-To: comp.sys.ibm.pc.programmer
- Organization: BTR Public Access UNIX, MtnView CA. For info contact: info@BTR.COM
- Lines: 507
-
- In article <im14u2c.721730126@camelot> im14u2c@camelot.bradley.edu
- (Joseph Zbiciak) writes:
-
- > I would like to know HOW to program the DMA controller to do such
- > aweful tasks as moving a screen-size hunk of memory out to the
- > display, or repeatedly "sampling" an input port to a bank of
- > memory.
-
- This posting is relatively long owing to the number of requests for this
- information. It discusses the following topics:
-
- 1.) The difference between fly-by and two-cycle transfers, and why it is
- usually a waste of time to use a DMA for memory-to-memory moves.
-
- 2.) Sample DMA routines that were actually used to download program overlays
- and upload measurement results between a PC/XT compatible computer board and
- an expansion card with an on-board processor doing data acquisition.
-
- ----------------------------------------------------------------------------
-
- 1.) Fly-by and two-cycle transfers --
-
- In the PC/XT/AT the 8237 DMA controller is ordinarily set up to transfer data
- between an I/O mapped peripheral and main memory. The PC/AT actually has two
- DMA controllers, one of which does eight-bit transfers and one of which does
- sixteen-bit transfers. These transfers are called single-cycle or fly-by
- transfers because a memory read and an I/O write (for a READ TRANSFER
- operation) or an I/O read and a memory write are both performed in the same
- bus cycle and the data is not stored within the DMA but rather "flies by" on
- the computer bus. This is accomplished in the following way. The peripheral
- indicates that it is ready to send or receive a byte (or word) of data by
- asserting a DMA request line which is attached to the DREQ input pin of one
- of the DMA channels. The DMA chip then asserts the CPU's hold request (HREQ)
- line. At the next opportunity (typically when it has finished whatever bus
- operation that is in progress) the CPU asserts hold acknowledge (HACK) and
- tri-states its bus control lines (i.e. relinquishes control of the bus). The
- DMA, upon sensing this, asserts the DMA acknowledge (DACK) line of the highest
- priority unmasked channel, and after a predictable amount of delay will place
- a memory address on the bus (determined by the contents of the channel's
- address register) and will assert -MEMRD (memory read) and -IOWR (I/O write)
- (for a read transfer) or -IORD (I/O read) and -MEMWR simultaneously. It does
- not drive the data bus -- that is up to the memory or peripheral. In effect
- the DMA is just a traffic cop. After the operation is complete, the address
- register mey be incremented, decremented, or held, and the count register is
- decremented. When the latter expires, the channel is masked and no further
- transfers take place until it is reprogrammed.
-
- Note that the DACK signal is used in place of an I/O address to select the
- peripheral device which will participate in the transfer. One can see that
- this will not work if the peripheral device is the video memory, for example,
- because one MUST use an address to select the byte or word to be accessed,
- and the DMA cannot put two addresses on the bus simultaneously (and get
- sensible results, anyway!). For this reason memory-to-memory transfers
- are usually implemented as two-cycle transfers. In the 8237 this is
- accomplished by linking channels 0 and 1 together, and using one channel
- to perform a memory read and store the data in a temporary register; the
- other channel writes the data from the temporary register to a different
- memory address during a subsequent bus cycle. Since memories usually
- don't have any way to assert a DREQ line, the transfer is typically
- initiated by the CPU setting a request bit in one of the DMA controller's
- internal registers. Such a transfer will run until completion (only one
- DMA channel's count register is actually used), and the CPU will be in a
- bus hold state for the duration. Unless the CPU has a cache (as do all
- 80486 PC's and some 80386 PC's) it will just idle while this is happening.
- The 80X86 family block move instruction, however, will generally do a
- memory-to-memory move just as fast as the DMA can -- after all, this is
- limited mainly by memory speed, and the same number of bus cycles are run
- (i.e., one memory read and one memory write). And this is a LOT easier
- to program than fiddling with the 8237's linked channel programming. Even
- the more advanced DMA devices (such as the Intel 82380 integrated peripheral,
- which mates with the 80386-DX) that can do a two-cycle transfer with a
- single channel are typically not any faster than the CPU at doing block
- moves and are much more troublesome to program.
-
- LESSON 1: UNLESS YOU HAVE A CACHE, YOU MIGHT AS WELL USE A BLOCK MOVE
- INSTRUCTION FOR SUCH TASKS AS MOVING A SCREEN-SIZED CHUNK OF
- DATA FROM MAIN MEMORY TO VIDEO DISPLAY ADAPTER MEMORY.
- ---------------------------------------------------------------------------
-
- 2.) Sample DMA routines that were actually used to download program overlays
- and upload measurement results between a PC/XT compatible computer board and
- an expansion card with an on-board processor doing data acquisition -- this
- is an application for fly-by transfers where the DMA is actually helpful.
- The slave board in the example below has a Zilog Z8038 Z-FIO fifo with which
- it transfers data to or from the PC/XT host, and it produces or consumes data
- at a rather slow rate relative to the bus speed. Rather than interrupting the
- CPU whenever a byte is needed (or available) it pays to let the DMA steal a
- bus cycle every now and then rather than enduring the interrupt overhead;
- and anyway it's a fly-by transfer so it's faster than programmed block I/O
- instructions would be. The DREQ and DACK lines are hooked up to the FIO,
- and we set up the DMA for single transfers (i.e., it transfers one byte
- then gives up the bus again).
-
- In order to make the program a little more comprehensible, here are the
- bit definitions for the 8237 command and mode registers. I/O address
- equates are in the source (author's note: these equates are little more
- than documentation as they were added to the source ex post facto -- the
- routines use literals, unfortunately).
-
-
- DMA_CMD_REG record DACK_SENSE:1, ; active high or low
- & DREQ_SENSE:1, ; " "
- & WRT_SEL:1, ;normal or late write
- & PRIO_SEL:1, ;rotating or fixed
- & TIMING_SEL:1, ;compressed or normal
- & DEV_ENA:1, ;disable/enable HREQ to CPU
- & CH0_ADR_HOLD:1, ;address hold (memory-to-memory xfr)
- & MM_ENA:1 ;use CH0/CH1 for memory-to-memory xfr
-
- DMA_MODE_REG record MODE_SEL:2, ;demand, single, block, or cascade
- & ADDR_INCR:1, ;increment or decrement address reg.
- & AUTO_INIT:1, ;restart or mask chnl when cnt expires
- & XFER_TYPE:2, ;read, write, or verify xfr
- & CHNL_SEL:2 ;0, 1, 2, or 3 (binary encoded)
-
- Note that the commands to mask single channels put the channel number in the
- two LSB's (bits 0 & 1) as in the mode register and the mask bit in bit 2.
- See the code below for more details. It is also STRONGLY recommended that
- you get the 8237 data sheet from Intel or from Harris Semiconductor.
-
-
- PAGE ,132
- NAME DMA_XFER
- TITLE DMA Channel 1 Init'zation Routines
- ;***************************************************************
- ;* DOS SEGMENT ORDER *
- ;***************************************************************
- DOSSEG
- ;***************************************************************
- ;* MEMORY MODEL *
- ;***************************************************************
- .MODEL small
- ;***************************************************************
- ;* DATA SEGMENT *
- ;***************************************************************
- .DATA
-
- STATS_SIZE EQU 640+32 ; 16*20*2+32
- ALARM_SIZE EQU 576+32 ; 16*18*2+32
- XFR_SIZE EQU STATS_SIZE+ALARM_SIZE+2
-
- EVEN
- _DMA_BUFFER DB 1A00H DUP (?)
- DMA_BUF_SEG DW ?
- DMA_BUF_START DW ?
- DMA_BUF_END DW ?
- OV_BUF1_PTR DW ?
- OV_BUF2_PTR DW ?
- EXPORT_DMA_PTR DW ?
-
- ; ---------- equates related to 8237 DMA
-
- DMA equ 00H ;base for DMA channel address & count registers
- DMA_STATUS_PORT equ 08H ;read status of DMA channels
- DMA_CMD_PORT equ 08H ;write global init'zation commands
- DMA_RQST_PORT equ 09H ;software request port -- not used
- DMA_MASK_PORT equ 0AH ;write single mask bit
- DMA_MODE_PORT equ 0BH ;write channel mode registers
- DMA_CLFL_PORT equ 0CH ;clear first/last flip-flop
- DMA_MCLR_PORT equ 0DH ;master clear (equiv to hdw reset)
- DMA_MSK_ALL_CH equ 0FH ;write all mask register bits
-
- DMA_PAGE equ 80H ;DMA page registers base address
- ; ---------- record definitions for 8237 internal registers
-
- DMA_CMD_REG record DACK_SENSE:1,DREQ_SENSE:1,WRT_SEL:1,PRIO_SEL:1,TIMING_SEL:1,DEV_ENA:1,CH0_ADR_HOLD:1,MM_ENA:1
- DMA_MODE_REG record MODE_SEL:2,ADDR_INCR:1,AUTO_INIT:1,XFER_TYPE:2,CHNL_SEL:2
-
- PAGE
- ;***************************************************************
- ;* CODE SEGMENT *
- ;***************************************************************
- .CODE
-
- ;***************************************************************
- ;* *
- ;* PUBLICS *
- ;* *
- ;***************************************************************
-
- PUBLIC _SETUP_DMA_FOR_STATS
- PUBLIC _SETUP_DMA_FOR_OVLOAD
- PUBLIC _START_DMA_FIFO_TXFER
- PUBLIC _SETUP_EXPORT_DMA
- PUBLIC _START_EXPORT_DMA
- PUBLIC _SHUT_OFF_DMA
-
- PAGE
- ;***************************************************************
- ;* *
- ;* NAME: _SETUP_DMA_FOR_STATS *
- ;* *
- ;* FUNCTION: To setup DMA channel 1 for transfer of stats *
- ;* blocks from TMS FIFO into a circular buffer. *
- ;* *
- ;* INPUT: None *
- ;* *
- ;* OUTPUT: None *
- ;* *
- ;***************************************************************
-
- _SETUP_DMA_FOR_STATS PROC NEAR
-
- ; partition dma transfer buffer so that we get at least 4 256-byte blocks
- ; that don't cross 64K boundary
-
- PUSH DI
- PUSH BP
- MOV BP,SP
-
- CALL _SHUT_OFF_DMA
-
- MOV AX,DS ;convert DMA buffer address to 20-bit value
- SUB DX,DX
- MOV DL,AH
- MOV CL,4
- SHR DX,CL
- SHL AX,CL
- ADD AX,OFFSET DGROUP:_DMA_BUFFER
- ADC DX,0 ;DX:AX=20-bit address of DMA buffer
- MOV DMA_BUF_SEG,DX ;save segment part (in "compressed" format )
- MOV DMA_BUF_START,AX; and provisionally save offset part
- NEG AX ;AX=amount of memory left in this 64K segment
- CMP AX, XFR_SIZE ;less than size of stats array ?
- JA SETUP_DMA_S2 ;jump if not
- MOV DMA_BUF_START,0 ;start at offset 0
- INC DMA_BUF_SEG ; in next 64K segment
- PAGE
- SETUP_DMA_S2:
- MOV AX,DMA_BUF_START
- ADD AX, XFR_SIZE
- MOV DMA_BUF_END,AX ;save end address of dma buffer
-
- MOV AX,DMA_BUF_SEG ;fill buffer with all 0's
- MOV CL,4
- ROR AX,CL
- MOV ES,AX
- MOV DI,DMA_BUF_START
- CLD
- MOV AX,0H
- MOV CX, XFR_SIZE ; Number of bytes
- SHR CX, 1 ; Number of words
- REP STOSW
-
- ; set up DMA to continuously transfer data out of TMS FIFO to the DMA buffer
- CLI ;interrupts off
- MOV AL,55H ;set mode: single,auto-inc,I/O -> mem on Ch. 1
- OUT DMA+11,AL
- OUT DMA+12,AL ;set first/last F/F
- MOV AX,DMA_BUF_START
- OUT DMA+2,AL ;output start address (low byte)
- MOV AL,AH
- OUT DMA+2,AL ;and high byte
- MOV AX,DMA_BUF_SEG
- AND AX,0FH
- OUT DMA_PAGE+3,AL ;output high 4 bits of 20-bit addr to page reg
- MOV AX, XFR_SIZE ;count output to DMA should be ONE LESS than
- ; ACTUAL COUNT !!!!!!
- DEC AX
- OUT DMA+3,AL ;output byte count (low byte)
- MOV AL,AH
- OUT DMA+3,AL ;and high byte
- MOV AL,1 ;unmask DMA channel 1
- OUT DMA+10,AL
- STI
- MOV SP,BP
- POP BP
- POP DI
- RET
- _SETUP_DMA_FOR_STATS ENDP
- PAGE
- ;***************************************************************
- ;* *
- ;* NAME: _SHUT_OFF_DMA *
- ;* *
- ;* FUNCTION: To shut off DMA channel 1 *
- ;* *
- ;* INPUT: None *
- ;* *
- ;* OUTPUT: None *
- ;* *
- ;***************************************************************
- _SHUT_OFF_DMA PROC NEAR
- CLI
- MOV AL,5 ;mask DMA channel 1
- OUT DMA+10,AL
- STI
- RET
- _SHUT_OFF_DMA ENDP
- PAGE
- ;***************************************************************
- ;* *
- ;* NAME: _SETUP_DMA_FOR_OVLOAD *
- ;* *
- ;* FUNCTION: To setup DMA channel 1 for loading overlay *
- ;* to the TMS through the FIFO. *
- ;* *
- ;* INPUT: None *
- ;* *
- ;* OUTPUT: None *
- ;* *
- ;* NOTE: We use the same transfer buffer that is used *
- ;* for statistics blocks transfer. This makes stats *
- ;* collection and overlay load mutually exclusive activi- *
- ;* ties. *
- ;***************************************************************
-
- _SETUP_DMA_FOR_OVLOAD PROC NEAR
-
- ; partition dma transfer buffer so that we get at least 2 128-byte blocks
- ; that don't cross 64K boundary
-
- PUSH DI
- PUSH BP
- MOV BP,SP
-
- CALL _SHUT_OFF_DMA
-
- MOV AX,DS ;convert DMA buffer address to 20-bit value
- SUB DX,DX
- MOV DL,AH
- MOV CL,4
- SHR DX,CL
- SHL AX,CL
- ADD AX,OFFSET DGROUP:_DMA_BUFFER
- ADC DX,0 ;DX:AX=20-bit address of DMA buffer
- MOV DMA_BUF_SEG,DX ;save segment part (in "compressed" format )
- MOV DMA_BUF_START,AX; and provisionally save offset part
- NEG AX ;AX=amount of memory left in this 64K segment
- CMP AX,100H ;less than 256 bytes (2 128-byte blocks) ?
- JA SETUP_DMA_O2 ;jump if not
- MOV DMA_BUF_START,0 ;start at offset 0
- INC DMA_BUF_SEG ; in next 64K segment
- SETUP_DMA_O2:
- MOV AX,DMA_BUF_START
- MOV OV_BUF1_PTR,AX ;save start pointer as overlay buffer 1 ptr
- ADD AX,80H
- MOV OV_BUF2_PTR,AX ;save overlay buffer 2 ptr
- PAGE
- MOV AX,DMA_BUF_SEG ;fill buffer with all 0's
- MOV CL,4
- ROR AX,CL
- MOV ES,AX
- MOV DI,DMA_BUF_START
- CLD
- MOV AX,0
- MOV CX,100H/2
- REP STOSW
-
- ; set up DMA for transfer data out of DMA buffer to the TMS FIFO
- CLI ;interrupts off
- MOV AL,49H ;set mode: single,no auto,inc,mem->I/O on Ch. 1
- OUT DMA+11,AL
- MOV AX,DMA_BUF_SEG
- AND AX,0FH
- OUT DMA_PAGE+3,AL ;output high 4 bits of 20-bit addr to page reg
- STI
- MOV SP,BP
- POP BP
- POP DI
- RET
- _SETUP_DMA_FOR_OVLOAD ENDP
- PAGE
- ;***************************************************************
- ;* *
- ;* NAME: _START_DMA_FIFO_TXFER *
- ;* *
- ;* FUNCTION: To start DMA-FIFO transfer. *
- ;* *
- ;* INPUT: [BP+4] = Buffer # (0 or 1) *
- ;* *
- ;* OUTPUT: None *
- ;* *
- ;* NOTE: DMA should have been previously setup in *
- ;* _SETUP_DMA_FOR_OVLOAD. FIFO should be held CLEAR *
- ;* while DMA is being reprogrammed. *
- ;***************************************************************
-
- _START_DMA_FIFO_TXFER PROC NEAR
-
- PUSH BP
- MOV BP,SP
-
- CALL _SHUT_OFF_DMA
-
- CLI ;interrupts off
- MOV AX,OV_BUF1_PTR ;decide which overlay buffer to use
- CMP WORD PTR [BP+4],0
- JE START_DMA2
- MOV AX,OV_BUF2_PTR
- START_DMA2:
- OUT DMA+12,AL ;set first/last F/F
- OUT DMA+2,AL ;output start address (low byte)
- MOV AL,AH
- OUT DMA+2,AL ;and high byte
- MOV AX,80H-1 ;count output to DMA should be ONE LESS than
- ; ACTUAL COUNT !!!!!!
- OUT DMA+3,AL ;output byte count (low byte)
- MOV AL,AH
- OUT DMA+3,AL ;and high byte
- MOV AL,1 ;unmask DMA channel 1
- OUT DMA+10,AL
- STI
- MOV SP,BP
- POP BP
- RET
- _START_DMA_FIFO_TXFER ENDP
- PAGE
- ;***************************************************************
- ;* *
- ;* NAME: _SETUP_EXPORT_DMA *
- ;* *
- ;* FUNCTION: To setup DMA channel 1 for export to 186 *
- ;* through the FIFO. *
- ;* *
- ;* INPUT: None *
- ;* *
- ;* OUTPUT: None *
- ;* *
- ;* NOTE: We use the same transfer buffer that is used *
- ;* for statistics blocks transfer. This makes stats *
- ;* collection and DMA export mutually exclusive activi- *
- ;* ties. *
- ;***************************************************************
-
- _SETUP_EXPORT_DMA PROC NEAR
-
- ; partition dma transfer buffer so that we get at least 2 128-byte blocks
- ; that don't cross 64K boundary
-
- PUSH DI
- PUSH BP
- MOV BP,SP
-
- CALL _SHUT_OFF_DMA
-
- MOV AX,DS ;convert DMA buffer address to 20-bit value
- SUB DX,DX
- MOV DL,AH
- MOV CL,4
- SHR DX,CL
- SHL AX,CL
- ADD AX,OFFSET DGROUP:_DMA_BUFFER
- ADC DX,0 ;DX:AX=20-bit address of DMA buffer
- MOV DMA_BUF_SEG,DX ;save segment part (in "compressed" format )
- MOV DMA_BUF_START,AX; and provisionally save offset part
- MOV EXPORT_DMA_PTR,AX ;save start pointer
- ;
- ; set up DMA for transfer data out of DMA buffer to the 186 FIFO
- ;
- CLI ;interrupts off
- MOV AL,49H ;set mode: single,no auto,inc,mem->I/O on Ch. 1
- OUT DMA+11,AL
- MOV AX,DMA_BUF_SEG
- AND AX,0FH
- OUT DMA_PAGE+3,AL ;output high 4 bits of 20-bit addr to page reg
- STI
- MOV SP,BP
- POP BP
- POP DI
- RET
-
- _SETUP_EXPORT_DMA ENDP
- PAGE
- ;***************************************************************
- ;* *
- ;* NAME: _START_EXPORT_DMA *
- ;* *
- ;* FUNCTION: To start Export DMA-FIFO transfer. *
- ;* *
- ;* INPUT: [BP+4] = Number of bytes *
- ;* *
- ;* OUTPUT: None *
- ;* *
- ;* NOTE: DMA should have been previously setup in *
- ;* _SETUP_EXPORT_DMA. FIFO should be held CLEAR *
- ;* while DMA is being reprogrammed. *
- ;***************************************************************
-
- _START_EXPORT_DMA PROC NEAR
-
- PUSH BP
- MOV BP,SP
-
- CALL _SHUT_OFF_DMA
-
- CLI ;interrupts off
- MOV AX,EXPORT_DMA_PTR ;Get Export pointer buffer to use
- OUT DMA+12,AL ;set first/last F/F
- OUT DMA+2,AL ;output start address (low byte)
- MOV AL,AH
- OUT DMA+2,AL ;and high byte
- MOV AX, WORD PTR [BP+4] ;count output to DMA should be ONE LESS than
- DEC AX ; ACTUAL COUNT !!!!!!
- OUT DMA+3,AL ;output byte count (low byte)
- MOV AL,AH
- OUT DMA+3,AL ;and high byte
- MOV AL,1 ;unmask DMA channel 1
- OUT DMA+10,AL
- STI
- MOV SP,BP
- POP BP
- RET
-
- _START_EXPORT_DMA ENDP
-
- END
-