Tvùrce webu je i pro tebe! Postav tøeba web. Bez grafika. Bez kodéra. Hned.
wz

DMA.ASM

DMA channel


; =============================================================================
;
;                               Litos - DMA channel
;
; =============================================================================
; NOTES:
; - DMA0-3 is 8-bit controller 1, max. 64 KB, must not cross 64K boundary
; - DMA4 channel is used as cascade (cannot be used)
; - DMA5-7 is 16=bit controller 2, max. 128 KB, must not cross 128K boundary
; - transfers are limited to lower 16 MB of physical memory (use DMAMemGet)
;
; Prepare DMA transfer:
; - Allocate memory block using DMAMemGet (max. 64 KB).
; - Allocate DMA channel using DMAAlloc or DMAInstall. Before it you can verify
;   channel number with DMAGetBus function and check if with DMACheck.
;
; Start transfer using DMAStart or with following steps:
; - Disable interrupts and lock DMA controller using DMALOCK.
; - Set DMA mode (transfer direction) using DMASetMode with modes DMAMODE_READ
;   or DMAMODE_WRITE.
; - Set transfer address using DMASetAddr (align it to word).
; - Set transfer size using DMASetSize (align it to word).
; - Start DMA transfer with DMAEnable.
; - Unlock DMA controller using DMAUNLOCK and return interrupt flag.
;
; Test transfer using DMATest or with following steps:
; - Disable interrupts and lock DMA controller using DMALOCK.
; - Check if transfer have finished using DMARemain.
; - Unlock DMA controller using DMAUNLOCK and return interrupt flag.
;
; Stop transfer using DMAStop or with following steps:
; - After DMA finish transfer, disable interrupts and lock contr. with DMALOCK.
; - Disable channel with DMADisable.
; - Unlock DMA controller using DMAUNLOCK and return interrupt flag.
;
; Default DMA assignments on PC/AT:
;	DMA 2: floppy disk controller
;	DMA 3: ECP printer port (LPT1)
;	DMA 4: cascade to slave DMA controller
; =============================================================================

		CODE_SECTION	32

DMA_CHANNELS	EQU	8		; number of DMA channels

; ------------- DMA modes

DMAMODE_VERIFY	EQU	B6		; DMA verify
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

; -----------------------------------------------------------------------------
;                              Lock/unlock DMA
; -----------------------------------------------------------------------------
; NOTES:	Use macro DMALOCK to lock, DMAUNLOCK to unlock.
; -----------------------------------------------------------------------------

; ------------- DMA lock function

%ifdef	SMP
		LOCK_LockFnc DMALock,Lock8237 ; declare DMA lock function
%endif

; ------------- Macro - call DMA lock function

%macro		DMALOCK 0
%ifdef	SMP
		call	DMALock		; call DMA lock function
%endif
%endmacro

; ------------- Macro - DMA unlock

%macro		DMAUNLOCK 0
%ifdef	SMP
		LOCK_Unlock Lock8237	; unlock 8237
%endif
%endmacro

; -----------------------------------------------------------------------------
;      Get DMA channel bus width (and check DMA channel number validity)
; -----------------------------------------------------------------------------
; INPUT:	AL = DMA channel number (0 to 7)
; OUTPUT:	CY = invalid channel number (EAX = 0)
;		EAX = width of DMA channel bus (8 or 16, 0 on CY)
; -----------------------------------------------------------------------------

; ------------- Check for DMA channel number validity

DMAGetBus:	cmp	al,DMA_CHANNELS ; check DMA channel number
		jb	DMAGetBus2	; DMA channel number is valid
		xor	eax,eax		; EAX <- 0, invalid channel number
		stc			; set error flag
		ret

; ------------- Get DMA channel number width

DMAGetBus2:	cmp	al,4		; is it 16-bit DMA bus?
		mov	al,8		; 8-bit DMA bus
		jb	DMAGetBus4	; it is 8-bit DMA bus
		mov	al,16		; 16-bit DMA bus
DMAGetBus4:	movzx	eax,al		; EAX <- DMA channel bus width
		clc			; clear error flag
		ret

; -----------------------------------------------------------------------------
;                             Allocate DMA channel
; -----------------------------------------------------------------------------
; INPUT:	AL = DMA channel number (0 to 7)
; OUTPUT:	CY = DMA channel is already in use
; NOTES:	AL not checked for validity and DMA lock not locked.
; -----------------------------------------------------------------------------

DMAAlloc:	push	eax		; push EAX
		movzx	eax,al		; EAX <- DMA number
		bts	[DMAMap],eax	; test and set usage bit
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                              Install DMA handler
; -----------------------------------------------------------------------------
; INPUT:	AL = recommended best (or last) DMA channel number
;		EDX = mask of usable DMA channels (1=enabled)
; OUTPUT:	EAX = DMA channel number (0 to 7 or -1 on error)
;		CY = cannot install DMA handler (no free DMA entry, try later)
; -----------------------------------------------------------------------------

; ------------- Try to allocate recommended best DMA channel

DMAInstall:	movzx	eax,al		; EAX <- recommended DMA channel
		cmp	al,DMA_CHANNELS	; check channel number
		jae	DMAInstall2	; invalid channel number
		bts	[DMAMap],eax	; test and set usage bit
		jnc	DMAInstall9	; recommended DMA channel is OK

; ------------- Try to allocate some other DMA channel

DMAInstall2:	xor	eax,eax		; EAX <- 0, first DMA channel
DMAInstall4:	bt	edx,eax		; is this channel allowed?
		jnc	DMAInstall6	; channel is not allowed
		bts	[DMAMap],eax	; test and set usage bit
		jnc	DMAInstall9	; DMA channel is OK
DMAInstall6:	inc	eax		; increase DMA channel
		cmp	al,DMA_CHANNELS	; is next channel valid?
		jb	DMAInstall4	; check next channel

; ------------- Error, no suitable DMA channel found

		xor	eax,eax		; EAX <- 0
		dec	eax		; EAX <- -1
		stc			; set error flag
DMAInstall9:	ret

; -----------------------------------------------------------------------------
;                              Free DMA channel
; -----------------------------------------------------------------------------
; INPUT:	AL = DMA channel number (0 to 7)
; NOTES:	AL not checked for validity and DMA lock not locked.
; -----------------------------------------------------------------------------

DMAFree:	push	eax		; push EAX
		movzx	eax,al		; EAX <- DMA number
		btr	[DMAMap],eax	; reset usage bit
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                         Check if DMA channel is used
; -----------------------------------------------------------------------------
; INPUT:	AL = DMA channel number (0 to 7)
; OUTPUT:	CY = DMA channel is used
; NOTES:	AL not checked for validity and DMA lock not locked.
; -----------------------------------------------------------------------------

DMACheck:	push	eax		; push EAX
		movzx	eax,al		; EAX <- DMA number
		btc	[DMAMap],eax	; test usage bit
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                            Enabled DMA channel
; -----------------------------------------------------------------------------
; INPUT:	AL = DMA channel number (0 to 7)
; NOTES:	AL not checked for validity and DMA lock not locked.
; -----------------------------------------------------------------------------

; ------------- Check if it is controller 2

DMAEnable:	cmp	al,4		; is it controller 2?
		jae	DMAEnable2	; it is controller 2

; ------------- Enable DMA channel on controller 1

		out	0ah,al		; enable DMA channel on controller 1
		ret

; ------------- Enable DMA channel on controller 2

DMAEnable2:	sub	al,4		; Al <- relative DMA channel number
		out	0d4h,al		; enable DMA channel on controller 2
		add	al,4		; AL <- return DMA channel number
		ret

; -----------------------------------------------------------------------------
;                           Disable DMA channel
; -----------------------------------------------------------------------------
; INPUT:	AL = DMA channel number (0 to 7)
; NOTES:	AL not checked for validity and DMA lock not locked.
; -----------------------------------------------------------------------------

; ------------- Check if it is controller 2

DMADisable:	cmp	al,4		; is it controller 2?
		jae	DMADisable2	; it is controller 2

; ------------- Disable DMA channel on controller 1

		add	al,B2		; set disable flag
		out	0ah,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)

DMADisable2:	out	0d4h,al		; disable DMA channel on controller 2
		ret

; -----------------------------------------------------------------------------
;                        Clear DMA channel flip-flop
; -----------------------------------------------------------------------------
; INPUT:	AL = DMA channel number (0 to 7)
; NOTES:	AL not checked for validity and DMA lock not locked.
; -----------------------------------------------------------------------------

; ------------- Check if it is controller 2

DMAClearFF:	cmp	al,4		; is it controller 2?
		jae	DMAClearFF2	; controller 2

; ------------- Clear DMA channel flip-flop on controller 1

		out	0ch,al		; clear DMA channel flip-flop on ctrl 1
		ret

; ------------- Clear DMA channel flip-flop on controller 2

DMAClearFF2:	out	0d8h,al		; clear DMA channel flip-flop on ctrl 2
		ret

; -----------------------------------------------------------------------------
;                          Set DMA channel mode
; -----------------------------------------------------------------------------
; INPUT:	AL = 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:	AL not checked for validity and DMA lock not locked.
; -----------------------------------------------------------------------------

; ------------- Check if it is controller 2

DMASetMode:	push	eax		; push EAX
		cmp	al,4		; is it controller 2?
		jae	DMASetMode2	; controller 2

; ------------- Set DMA channel mode on controller 1

		or	al,bl		; AL <- channel + mode
		out	0bh,al		; set DMA mode on controller 1
		pop	eax		; pop EAX
		ret

; ------------- Set DMA channel mode on controller 2

DMASetMode2:	sub	al,4		; AL <- relative DMA channel number
		or	al,bl		; AL <- channel + mode
		out	0d6h,al		; set DMA mode on controller 2
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                            Set DMA channel page
; -----------------------------------------------------------------------------
; INPUT:	AL = DMA channel number (0 to 7)
;		EDX = DMA address (physical address)
; NOTES:	AL not checked for validity and DMA lock not locked.
;		Only address bits 16-23 (DMA0-3) or 17-23 (DMA4-7) are used.
; -----------------------------------------------------------------------------

; ------------- Push registers

DMASetPage:	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	DMASetPage2	; it is controller 1
		and	dl,~B0		; clear bit 16 for controller 2

; ------------- Prepare port address

DMASetPage2:	movzx	eax,al		; EAX <- DMA channel
		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

; -----------------------------------------------------------------------------
;                    Set DMA transfer address (offset and page)
; -----------------------------------------------------------------------------
; INPUT:	AL = DMA channel number (0 to 7)
;		EDX = DMA address (physical address, only bits 0-23 are used)
; NOTES:	AL not checked for validity and DMA lock not locked.
;		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.
; -----------------------------------------------------------------------------

; ------------- Set DMA channel page

DMASetAddr:	call	DMASetPage	; set DMA channel page

; ------------- Clear DMA channel flip-flop

		call	DMAClearFF	; clear DMA channel flip-flop

; ------------- Push registers

		push	eax		; push EAX
		push	edx		; push EDX

; ------------- Prepare port for controller 1 (00h, 02h, 04h, 06h)

		movzx	eax,al		; EAX <- DMA channel number
		shl	eax,1		; AX <- channel number*2
		cmp	al,4*2		; is it DMA0 to DMA3?
		jb	DMASetAddr2	; it is DMA0 to DMA3

; ------------- Prepare port for controller 2 (0C0h, 0C4h, 0C8h, 0CCh)

		shl	eax,1		; AX <- channel number*4
                add	al,0c0h-4*4	; AL <- port
		shr	edx,1		; convert address to words

; ------------- Set transfer address

DMASetAddr2:	xchg	eax,edx		; DX <- port, EAX <- address
		out	dx,al		; set transfer address LOW
		mov	al,ah		; AL <- transfer address HIGH
		out	dx,al		; set transfer address HIGH

; ------------- Pop registers

		pop	edx		; pop EDX
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                             Set DMA transfer size
; -----------------------------------------------------------------------------
; INPUT:	AL = DMA channel number (0 to 7)
;		ECX = DMA transfer size (cannot be 0; B0 is ignored for DMA4-7)
; NOTES:	AL not checked for validity and DMA lock not locked.
;		DMA transfer cannot cross 64K (or 128K for DMA4-7) boundary.
;		Maximal transfer size is 64K for DMA0-3 and 128K for DMA4-7.
; -----------------------------------------------------------------------------

; ------------- Clear DMA channel flip-flop

DMASetSize:	call	DMAClearFF	; clear DMA channel flip-flop

; ------------- Push registers

		push	eax		; push EAX
		push	ecx		; push ECX
		push	edx		; push EDX

; ------------- Prepare port for controller 1 (01h, 03h, 05h, 07h)

		movzx	eax,al		; EAX <- DMA channel number
		shl	eax,1		; AX <- channel number*2
		inc	eax		; EAX <- channel number*2 + 1
		cmp	al,4*2+1	; is it DMA0 to DMA3?
		jb	DMASetSize2	; it is DMA0 to DMA3

; ------------- Prepare port for controller 2 (0C2h, 0C6h, 0CAh, 0CEh)

		shl	eax,1		; AX <- channel number*4 + 2
                add	al,0c0h-4*4	; AL <- port
		shr	ecx,1		; convert size to words

; ------------- Set transfer size

DMASetSize2:	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
		out	dx,al		; set transfer size HIGH

; ------------- Pop registers

		pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	eax		; pop EAX
		ret

; -----------------------------------------------------------------------------
;                         Get DMA remaining transfer size
; -----------------------------------------------------------------------------
; INPUT:	AL = DMA channel number (0 to 7)
; OUTPUT:	ECX = remaining bytes to transfer (0 if transfer finished)
; NOTES:	AL not checked for validity and DMA lock not locked.
;		If called before the channel has been used, it may return 1.
; -----------------------------------------------------------------------------

; ------------- Clear DMA channel flip-flop

DMARemain:	call	DMAClearFF	; clear DMA channel flip-flop

; ------------- Push registers

		push	eax		; push EAX
		push	edx		; push EDX

; ------------- Prepare port for controller 1 (01h, 03h, 05h, 07h)

		movzx	eax,al		; EAX <- DMA channel number
		shl	eax,1		; AX <- channel number*2
		inc	eax		; EAX <- channel number*2 + 1
		cmp	al,4*2+1	; is it DMA0 to DMA3?
		jb	DMARemain2	; it is DMA0 to DMA3

; ------------- Prepare port for controller 2 (0C2h, 0C6h, 0CAh, 0CEh)

		shl	eax,1		; AX <- channel number*4 + 2
                add	al,0c0h-4*4	; AL <- port

; ------------- Get transfer size

DMARemain2:	xchg	eax,edx		; DX <- port
		xor	eax,eax		; EAX <- 0
		in	al,dx		; get transfer size LOW
		xchg	eax,ecx		; ECX <- transfer size LOW
		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 to DMA7?
		jb	DMARemain4	; it is DMA0 to DMA3
		shl	ecx,1		; convert to words
DMARemain4:	ret

; -----------------------------------------------------------------------------
;                             Start DMA transfer
; -----------------------------------------------------------------------------
; INPUT:	AL = DMA channel number (0 to 7)
;		BL = DMA mode: 0=read, 1=write, 2=verify
;		ECX = DMA transfer size (cannot be 0; B0 is ignored for DMA4-7)
;		EDX = DMA address (physical address, only bits 0-23 are used)
; NOTES:	AL 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.
;		DMA5 to DMA7 address must be word aligned.
;		Transfers are limited to lower 16 MB of physical memory.
;		It locks and unlocks DMA lock.
; -----------------------------------------------------------------------------

; ------------- Disable interrupts

DMAStart:	pushf			; push flags
		cli			; disable interrupts

; ------------- Lock DMA lock

		DMALOCK			; lock DMA controller

; ------------- Set DMA mode

		push	ebx		; push EBX
		cmp	bl,1		; write mode?
		mov	bl,DMAMODE_READ	; BL <- DMA read mode
		jb	DMAStart2	; read mode
		mov	bl,DMAMODE_WRITE; BL <- DMA write mode
		je	DMAStart2	; write mode
		mov	bl,DMAMODE_VERIFY ; BL <- DMA verify mode
DMAStart2:	call	DMASetMode	; set DMA mode
		pop	ebx		; pop EBX

; ------------- Set transfer address

		call	DMASetAddr	; set DMA address

; ------------- Set transfer size

		call	DMASetSize	; set DMA size

; ------------- Start DMA transfer

		call	DMAEnable	; start DMA transfer

; ------------- Unlock DMA lock

		DMAUNLOCK		; unlock DMA controller

; ------------- Pop flags (to enable interrupts)

		popf			; pop flags
		ret

; -----------------------------------------------------------------------------
;                     Test if DMA transfer is still running
; -----------------------------------------------------------------------------
; INPUT:	AL = DMA channel number (0 to 7)
; OUTPUT:	CY = DMA transfer is still running
; NOTES:	AL not checked for validity.
;		It locks and unlocks DMA lock.
; -----------------------------------------------------------------------------

; ------------- Push registers and disable interrupts

DMATest:	push	ecx		; push ECX
		pushf			; push flags
		cli			; disable interrupts

; ------------- Lock DMA lock

		DMALOCK			; lock DMA controller

; ------------- Get remaining transfer size (-> ECX)

		call	DMARemain	; get remaining transfer size

; ------------- Unlock DMA lock

		DMAUNLOCK		; unlock DMA controller

; ------------- Pop flags (to enable interrupts) and pop registers

		popf			; pop flags
		cmp	ecx,byte 1	; check if transfer finished
		cmc			; CY = transfer not finished yet
		pop	ecx		; pop ECX
		ret

; -----------------------------------------------------------------------------
;                             Stop DMA transfer
; -----------------------------------------------------------------------------
; INPUT:	AL = DMA channel number (0 to 7)
; NOTES:	AL not checked for validity.
;		It locks and unlocks DMA lock.
;		It saves flags.
; -----------------------------------------------------------------------------

; ------------- Disable interrupts

DMAStop:	pushf			; push flags
		cli			; disable interrupts

; ------------- Lock DMA lock

		DMALOCK			; lock DMA controller

; ------------- Stop DMA transfer

		call	DMADisable	; stop DMA transfer

; ------------- Unlock DMA lock

		DMAUNLOCK		; unlock DMA controller

; ------------- Pop flags (to enable interrupts)

		popf			; pop flags
		ret

; -----------------------------------------------------------------------------
;                                   Data
; -----------------------------------------------------------------------------

		DATA_SECTION

; ------------- 8237 DMA controller lock
%ifdef	SMP
		align	4, db  0
Lock8237:	SPINLOCK		; 8237 DMA controller lock
%endif
; ------------- DMA page ports

DMAPagePort:	db	87h		; DMA 0
		db	83h		; DMA 1
		db	81h		; DMA 2
		db	82h		; DMA 3
		db	80h		; DMA 4
		db	8bh		; DMA 5
		db	89h		; DMA 6
		db	8ah		; DMA 7

; ------------- Bitmap of engaged DMA channels (1=channel is used)

		align	4, db 0
DMAMap:		dd	B4		; DMA 4 reserved for cascade

Back to source browser