;==================================================================================================
; Z80 DMA TEST UTILITY - ROMWBW SPECIFIC
;==================================================================================================
;
;==================================================================================================
; PLATFORM CONFIGURATION
;==================================================================================================
;
DMAMODE_NONE	.EQU	0
DMAMODE_ECB	.EQU	1		; ECB-DMA WOLFGANG KABATZKE'S Z80 DMA ECB BOARD
DMAMODE_Z180	.EQU	2		; Z180 INTEGRATED DMA
DMAMODE_Z280	.EQU	3		; Z280 INTEGRATED DMA
DMAMODE_RC	.EQU	4		; RCBUS Z80 DMA
DMAMODE_MBC	.EQU	5		; MBC
DMAMODE_DUO	.EQU	6		; DUO
DMAMODE_VDG	.EQU	7		; VELESOFT DATAGEAR
;
DMAMODE		.EQU	DMAMODE_DUO	; SELECT DMA DEVICE FOR TESTING
;
;==================================================================================================
; SOME DEFAULT PLATFORM CONFIGURATIONS
;==================================================================================================
;
DMABASE		.EQU	$E0		; DMA: DMA BASE ADDRESS
DMALATCH	.EQU	DMABASE+1	; DMA: DMA LATCH ADDRESS
DMAIOTST	.EQU	$68		; AN OUTPUT PORT FOR TESTING - 16C450 SERIAL OUT
;
#IF (DMAMODE==DMAMODE_DUO)
DMABASE		.SET	$40		; DMA: DMA0 BASE ADDRESS
DMALATCH	.SET	$43		; DMA: DMA LATCH ADDRESS
DMAIOTST	.SET	$58		; AN OUTPUT PORT FOR TESTING - 16C450 SERIAL OUT
;DMAIOTST	.SET	$94		; AN ALT OUTPUT PORT FOR TESTING - RTC/SPEAKER/LEDS PORT
#ENDIF
;
;==================================================================================================
; HELPER MACROS AND EQUATES
;==================================================================================================
;
FALSE		.EQU	0
TRUE		.EQU	~FALSE
;
#DEFINE	PRTC(C)	CALL PRTCH \ .DB C	; PRINT CHARACTER C TO CONSOLE - PRTC('X')
#DEFINE	PRTS(S)	CALL PRTSTRD \ .TEXT S	; PRINT STRING S TO CONSOLE - PRTD("HELLO")
#DEFINE	PRTX(X) CALL PRTSTRI \ .DW X	; PRINT STRING AT ADDRESS X TO CONSOLE - PRTI(STR_HELLO)
;
;==================================================================================================
; INTERRUPT TESTING CONFIGURATION
; ASSUMES SYSTEM IS ALREADY CONFIGURED FOR IM2 OPERATION
; INTIDX MUST BE SET TO AN UNUSED INTERRUPT SLOT
;==================================================================================================
;		
INTENABLE	.EQU	TRUE		; ENABLE INT TESTING
INTIDX		.EQU	1		; INT VECTOR INDEX
;
;==================================================================================================
; DMA MODE BYTES - I/O ROUTINES CLEAR b3 AND ONLY PROGRAM LOW ADDRESS
;==================================================================================================
;
DMA_BYTE			.equ 	%10011101	; b7b1b0 = Register		= 1xxxxx01	= Program R4
							; b6b5	 = Transfer Mode	= x00xxxxx	= Byte transfer mode
							; b2	 = start address (low)	= xxxxx1xx	= low address follows (1 byte)
							; b3	 = start address (high) = xxxx1xxx	= high address follows (1 byte)
							; b4	 = int control byte	= xxx1xxxx	= interrupt, pulse or vector byte follow
;
DMA_CONTINUOUS			.equ 	%10111101	; b7b1b0 = Register		= 1xxxxx01	= Program R4
							; b6b5	 = Transfer Mode	= x01xxxxx	= Continuous transfer mode (default)
							; b2	 = start address (low)	= xxxxx1xx	= low address follows (1 byte)
							; b3	 = start address (high) = xxxx1xxx	= high address follows (1 byte)
							; b4	 = int control byte	= xxx1xxxx	= interrupt, pulse or vector byte follow
;
DMA_BURST 			.equ	%11011101	; b7b1b0 = Register		= 1xxxxx01	= Program R4
							; b6b5	 = Transfer Mode	= x10xxxxx	= Burst transfer mode
							; b2	 = start address (low)	= xxxxx1xx	= low address follows (1 byte)
							; b3	 = start address (high) = xxxx1xxx	= high address follows (1 byte)
							; b4	 = int control byte	= xxx1xxxx	= interrupt, pulse or vector byte follow
;
DMA_ICBYTE			.equ	%00001100	; b7	 = Interrupt Cont. Byte	= 0xxxxxxx	= Interrupt Control Byte identifier
							; b6	 = Interrupt on RDY	= x0xxxxxx	= Do not interrupt on RDY
							; b5	 = Status affects vector= xx0xxxxx	= Status does not affect vector
							; b4	 = Interrupt vector byte= xxx0xxxx	= No interrupt vector byte will follow
							; b3	 = Pulse control byte	= xxxx1xxx	= A pulse control byte will follow
							; b2	 = Pulse generation	= xxxxx100	= A pulse will be generated
							; b1	 = Interrupt @ block end= xxxxxx0x	= No interrupt at block end
							; b0	 = Interrupt on match	= xxxxxxx0	= No interrupt on match
;
DMA_LOAD			.equ	$cf 		; %11001111
DMA_ENABLE			.equ	$87		; %10000111
DMA_FORCE_READY 		.equ 	$b3
DMA_DISABLE			.equ	$83
DMA_START_READ_SEQUENCE		.equ	$a7
DMA_READ_STATUS_BYTE		.equ	$bf
DMA_READ_MASK_FOLLOWS		.equ	$bb
DMA_RESET			.equ	$c3
;DMA_RESET_PORT_A_TIMING	.equ	$c7
;DMA_RESET_PORT_B_TIMING	.equ	$cb
;DMA_CONTINUE			.equ	$d3
;DMA_DISABLE_INTERUPTS		.equ	$af
DMA_ENABLE_INTERUPTS		.equ	$ab
;DMA_RESET_DISABLE_INTERUPTS	.equ 	$a3
;DMA_ENABLE_AFTER_RETI		.equ	$b7
DMA_REINIT_STATUS_BYTE		.equ	$8b
;
DMA_RDY				.EQU	%00001000
DMA_FORCE			.EQU	0
DMA_XMODE			.equ	1		; Byte = 0, Continuous = 1, Burst = 2
;
;==================================================================================================
; ROMWBW HBIOS DEFINITIONS
;==================================================================================================
;
bf_sysint			.equ	$FC	; INT function
bf_sysget			.equ	$F8	; GET function
;		
bf_sysintinfo			.equ	$00	; INT INFO subfunction
bf_sysintget			.equ	$10	; INT GET subfunction
bf_sysintset			.equ	$20	; INT SET subfunction
bf_sysgetcpuspd			.equ	$F3	; GET CPUSPD subfunction
;
;==================================================================================================
; MAIN DMA MONITOR ROUTINE
;==================================================================================================
;
	.ORG	$0100
;
MAIN:
	LD	(SAVSTK),SP		; SETUP LOCAL
	LD	SP,STACK		; STACK
;
	call	PRTSTRD			; WELCOME
	.db	"\n\rDMA Monitor V3.1\n\r$"
;
#IF (INTENABLE)
;
	; Install interrupt handler in upper mem
	ld	hl,reladr
	ld	de,$A000
	ld	bc,hsiz
	ldir
	ld	a,(dmaport)
	ld	(int_dmaport),a
;
	; Install interrupt vector (RomWBW specific!!!)
	ld	hl,int		; pointer to my interrupt handler
	ld	b,bf_sysint
	ld	c,bf_sysintset	; set new vector
	ld	e,INTIDX	; vector idx
	di
	rst	08		; do it
	ld	(orgvec),hl	; save the original vector
	ei			; interrupts back on
;
#ENDIF
;
MENULP:	CALL	DISPM			; DISPLAY MENU
	CALL	CINU			; GET SELECTION
	CALL	COUT
;
MENULP1:
	CALL	NEWLINE
	CP	'C'
	JP	Z,DMACFG_C		; CONFIGURE XFER MODE
	CP	'D'
	JP	Z,DMATST_D		; DUMP REGISTERS
	CP	'I'
	JP	Z,DMATST_I		; INITIALIZE
#IF (INTENABLE)
	CP	'T'
	JP	Z,DMATST_T		; TOGGLE INT USAGE
#ENDIF
	CP	'M'
	JP	Z,DMATST_M		; MEMORY COPY
	CP	'N'
	JP	Z,DMATST_N		; MEMORY COPY ITER
	CP	'0'
	JP	Z,DMATST_0		; PULSE DMA PORT
	CP	'1'
	JP	Z,DMATST_1		; PULSE LATCH PORT
	CP	'O'
	JP	Z,DMATST_O
	CP	'R'
	JP	Z,DMATST_R		; TOGGLE RESET
	CP	'Y'
	JP	Z,DMATST_Y
	cp	'L'
	jp	z,DMACFG_L		; SET LATCH PORT
	cp	'S'
	jp	z,DMACFG_S		; SET PORT
	cp	'V'
	jp	z,DMACFG_V		; TOGGLE VERBOSE
	CP	'X'
	JP	Z,DMABYE		; EXIT
;
	JR	MENULP
;
DMABYE:
#IF (INTENABLE)
	; Deinstall interrupt vector
	ld	hl,(orgvec)		; original vector
	ld	b,bf_sysint
	ld	c,bf_sysintset		; set new vector
	ld	e,INTIDX		; vector idx
	di
	rst	08			; do it
	ei				; interrupts back on
#ENDIF
;
	LD	SP,(SAVSTK)		; RESTORE CP/M STACK
	RET
;
;==================================================================================================
; CONFIGURE PORT
;==================================================================================================
;
DMACFG_S:
	call	PRTSTRD
	.db	"\n\rSet DMA port address\n\rPort:$"
	call	HEXIN
	ld	hl,dmaport
	ld	(hl),a
#IF (INTENABLE)
	ld	(int_dmaport),a
#ENDIF
	jp	MENULP
;
DMACFG_L:
	call	PRTSTRD
	.db	"\n\rSet Latch port address\n\rPort:$"
	call	HEXIN
	ld	hl,dmalach
	ld	(hl),a
	jp	MENULP
;
DMATST_I:
	call	PRTSTRD
	.db	"\n\rStart Initialization\n\r$"
	CALL	DMA_INIT
	JP	MENULP
;
#IF (INTENABLE)
;
DMATST_T:
	LD	A,(USEINT)
	XOR	$FF
	LD	(USEINT),A
	JP	MENULP
;
#ENDIF
;
DMATST_M:
	call	PRTSTRD
	.db	"\n\rPerforming Memory-Memory Copy Test\n\r$"
	CALL	DMAMemTest
	JP	MENULP
;
DMATST_N:
	call	PRTSTRD
	.db	"\n\rPerforming Iterative Memory-Memory Copy Test\n\r$"
	CALL	DMAMemTestIter
	JP	MENULP
;
DMATST_0:
	call	PRTSTRD
	.db	"\n\rPerforming DMA Port Selection Test\n\r$"
	CALL	DMA_Port0
	ret

DMATST_1:
	call	PRTSTRD
	.db	"\n\rPerforming Latch Port Selection Test\n\r$"
	CALL	DMA_Port1
	ret
;
DMATST_O:
	call	PRTSTRD
	.db	"\n\rTest output to I/O device\n\r$"
	CALL	DMA_ReadyO
	JP	MENULP
;
DMATST_D:
	call	PRTSTRD
	.db	"\n\rRegister dump:\n\r$"
	CALL	DMARegDump
	JP	MENULP
;
DMATST_Y:
	call	PRTSTRD
	.db	"\n\rPerforming Ready Bit Test\n\r$"
	CALL	DMA_ReadyY
	JP	MENULP
;
DMATST_R:
	call	PRTSTRD
	.db	"\n\rPerforming Reset\n\r$"
;	CALL	
	JP	MENULP

DMACFG_C:
	CALL	DMA_XferM
	call	PRTSTRD
	.db	"\n\rTransfer mode change to: $"
	LD	a,(dmaxfer)
	LD	DE,DMA_XFRMODE
	CALL	PRTIDXDEA
	CALL	NEWLINE
	JP	MENULP
;
;==================================================================================================
; DISPLAY MENU
;==================================================================================================
;
DISPM:	call	PRTSTRD
	.db	"\n\rDMA Device: $"
	LD	C,DMAMODE		; DISPLAY
	LD	A,00000111B		; TARGET
	LD	DE,DMA_DEV_STR		; DEVICE
	CALL	PRTIDXMSK
;
	call	PRTSTRD
	.db	", Port=0x$"
	LD	A,(dmaport)		; DISPLAY
	CALL	PRTHEXBYTE		; DMA PORT
	call	PRTSTRD
	.db	", Latch Port=0x$"
	ld	A,(dmalach)
	CALL	PRTHEXBYTE		; DMA PORT
;
#IF (INTENABLE)
;
	call	PRTSTRD
	.db	"\n\rInterrupts=$"
	LD	A,(USEINT)
	OR	A
	LD	A,'Y'
	JR	NZ,DISPM_INT
	LD	A,'N'
	JR	DISPM_INT
;
DISPM_INT:
	CALL	COUT
;
	call	PRTSTRD
	.db	", Interrupt Count=$"
	ld	hl,(counter)
	call	PRTDEC
;
#ENDIF
;
#IF (DMAMODE==DMAMODE_VDG)
	call	PRTSTRD
	.db	"\n\rReset\\Ready Latch unsupported.$"
#ENDIF
	call	PRTSTRD			; DISPLAY SPEED
	.db	"\n\rCPU at $"
	LD	B,bf_sysget
	LD	C,bf_sysgetcpuspd	; GET CURRENT 
	RST	08			; SPEED SETTING
	OR	A
	LD	A,L
	JR	Z,SPDDISP
	LD	A,3
SPDDISP:LD	DE,DMA_SPD_STR
	CALL	PRTIDXDEA
;
	call	PRTSTRD
	.db	"\n\rTransfer Mode: $"	; DIPLAY TRANSFER
	LD	a,(dmaxfer)		; MODE
	LD	DE,DMA_XFRMODE
	CALL	PRTIDXDEA
	CALL	NEWLINE
;
	LD	HL,MENU_OPT		; DISPLAY
	CALL	PRTSTR			; MENU OPTIONS
	RET
;
#INCLUDE "util.asm"
;
;==================================================================================================
; DMA INITIALIZATION CODE
;==================================================================================================
;
DMA_INIT:
	CALL	NEWLINE
	PRTS("DMA: IO=0x$")		; announce
	LD	A,(dmaport)
	CALL	PRTHEXBYTE
;
#IF !(DMAMODE==DMAMODE_VDG)
	ld	a,(dmalach)
	ld	c,a
	LD	A,DMA_FORCE
	out	(c),a			; force ready off
#ENDIF
;
	call	DMAProbe		; do we have a dma?
	jr	nz,DMA_NOTFOUND
;
	call	PRTSTRD
	.db	" DMA Found\n\r$"
;
	ld	hl,DMAInitMode		; setup the 
	call	SETXFER			; transfer mode
	set	3,(hl)			; upper and lower address
;
	ld	hl,DMACode		; program the
	ld	b,DMACode_Len		; dma command
	ld	a,(dmaport)
	ld	c,a		; block
;
	di
	otir				; load dma
	ei
	xor	a			; set status
;
DMA_EXIT:
	ret
;
DMA_NOTFOUND:
	push	af
	call	PRTSTRD
	.db	" NOT Present$"
	pop	af
	jr	DMA_EXIT
;
DMA_FAIL_FLAG:
	.db	0
;
DMACode		;.db	DMA_DISABLE	; R6-Command Disable DMA
		.db	%01111101 	; R0-Transfer mode, A -> B, start address, block length follow
		.dw	0		; R0-Port A, Start address
		.dw	0 		; R0-Block length
		.db	%00010100	; R1-No timing bytes follow, address increments, is memory
		.db	%00010000 	; R2-No timing bytes follow, address increments, is memory
		.db	%10000000 	; R3-DMA, interrupt, stop on match disabled
DMAInitMode:	.db	DMA_CONTINUOUS	; R4-Transfer mode, destination address, interrupt and control byte follow
		.dw	0 		; R4-Port B, Destination address
		.db	DMA_ICBYTE	; R4-Pulse byte follows, Pulse generated
		.db	0		; R4-Pulse offset
		.db	%10010010+DMA_RDY; R5-Stop on end of block, ce/wait multiplexed, READY active config
		.db	DMA_LOAD 	; R6-Command Load
;		.db	DMA_FORCE_READY	; R6-Command Force ready
;		.db	DMA_ENABLE 	; R6-Command Enable DMA
DMACode_Len 	.equ	$-DMACode
;
;==================================================================================================
; STRINGS
;==================================================================================================
;	
DMA_DEV_STR:
	.TEXT	"NONE$"
	.TEXT	"ECB$"
	.TEXT	"Z180$"
	.TEXT	"Z280$"
	.TEXT	"RCBUS$"
	.TEXT	"MBC$"
	.TEXT	"DUODYNE$"
	.TEXT	"DATAGEAR$"
;
DMA_SPD_STR:
	.TEXT	"half speed.$"
	.TEXT	"full speed.$"
	.TEXT	"double speed.$"
	.TEXT	"unknown speed.$"
;
DMA_XFRMODE:
	.TEXT	"Byte.$"
	.TEXT	"Continuous.$"
	.TEXT	"Burst.$"
;
MENU_OPT:
	.TEXT	"\n\r"
	.TEXT	"C) Change transfer mode\n\r"
	.TEXT	"D) Dump DMA registers\n\r"
	.TEXT	"I) Initialize DMA\n\r"
	.TEXT	"T) Toggle Interrupt Usage\n\r"
	.TEXT	"M) Test Memory-Memory Copy\n\r"
	.TEXT	"N) Test Memory-Memory Copy Iteratively\n\r"
	.TEXT	"O) Memory to I/O Test\n\r"
	.TEXT	"0) Test DMA Port Selection\n\r"
#IF !(DMAMODE==DMAMODE_VDG)
	.TEXT	"1) Test DMA Latch Port Selection\n\r"
	.TEXT	"Y) Test Ready Bit\n\r"
#ENDIF
	.TEXT	"S) Set DMA port\n\r"
	.TEXT	"L) Set Latch port\n\r"
	.TEXT	"V) Verbose status toggle\n\r"
	.TEXT	"X) Exit\n\r"

	.TEXT	">$"
;
;==================================================================================================
; TOGGLE TRANSFER MODE
;==================================================================================================
;
DMA_XferM:				; Set next transfer mode
	ld	a,(dmaxfer)
	inc	a
	cp	3
	jr	nz,NextX
	ld	a,0
NextX:	ld	(dmaxfer),a
	ret
;
;==================================================================================================
; TOGGLE VERBOSE MODE
;==================================================================================================
;
DMACFG_V:
	ld	a,(dmavbs)
	cpl
	ld	(dmavbs),a
	jp	MENULP
;
;==================================================================================================
; OUTPUT A BUFFER OF TEXT TO AN IOPORT
;==================================================================================================
;
DMABUF	.TEXT	"0123456789abcdef"
;DMABUF	.DB	$04,$00,$04,$00,$04,$00,$04,$00,$04,$00,$04,$00,$04,$00,$04,$00,$00,$00,$00 ; SPEAKER
;
DMA_ReadyO:
	call	PRTSTRD
	.db	"\r\nOutputing string to port 0x$"
	ld	a,(tstport)
	call	PRTHEXBYTE
	call	NEWLINE
;
	ld	b,16
IOLoop:	push	bc
	call	NEWLINE
	ld	hl,DMABUF
	ld	a,(tstport)
	ld	bc,16
;
	call	DMAOTIR
;
	pop	bc
	djnz	IOLoop
	call	NEWLINE
	ret
;
;==================================================================================================
; PULSE PORT
;==================================================================================================
;
DMA_Port0:
	ld	a,(dmaport)
	jr	DMA_Port
DMA_Port1:
	ld	a,(dmalach)
DMA_Port:
	call	PRTSTRD
	.db	"\r\nPulsing port 0x$"
	call	PRTHEXBYTE
	call	NEWLINE
	ld	c,a			; toggle
	ld	b,$20			; loop counter
portlp:	push	bc
	call	PC_PERIOD
	push	bc
	ld	b,0
	ld	a,0
portlp1:out	(c),a
	djnz	portlp1
	pop	bc
	call	delay	
	pop	bc
	djnz	portlp
	call	NEWLINE
	JP	MENULP
;
delay:	push	bc
	ld	bc,0
dlylp:	dec	bc
	ld	a,b
	or	c
	jr	nz,dlylp
	pop	bc
	ret
;
;==================================================================================================
; TOGGLE READY BIT
;==================================================================================================
;
DMA_ReadyY:
	call	NEWLINE
	ld	a,(dmalach)
	ld	c,a			; toggle
	ld	b,$20			; loop counter
portlp2:push	bc
	ld	a,b
	call	PRTDECB
	call	PRTSTRD
	.db	": ON$"
	call	delay
	ld	a,$FF
	out	(c),a
	call	PRTSTRD
	.db	" -> OFF$"
	call	delay
	call	PRTSTRD
	.db	"\r               \r$"
	ld	a,0
	out	(c),a
	pop	bc
	djnz	portlp2
	ret
;
;==================================================================================================
; DMA MEMORY MOVE
;==================================================================================================
;
DMAMemMove:
	LD	HL,$8000	; PREFILL DESTINATION WITH $55
	LD	A,$55
	LD	(HL),A
	LD	DE,$8001
	LD	BC,4096-1
	LDIR
;
	LD	HL,PROEND	; FILL SOURCE WITH $AA
	LD	A,$AA
	LD	(HL),A
	LD	DE,PROEND+1
	LD	BC,4096-1
	LDIR
;
	LD	HL,PROEND	; DMA COPY
	LD	DE,$8000
	LD	BC,4096-1
	LD	A,(USEINT)	; USE INTS?
	OR	A		; TEST VALUE
	JR	NZ,DMAMemMove1	; IF SO, DO SO
	CALL	DMALDIR		; ELSE NORMAL DMA
	JR	DMAMemMove2
;
DMAMemMove1:
	CALL	DMALDIRINT	; DMA W/ INTERRUPTS
;
DMAMemMove2:
;
	;LD	HL,$8400	; PLANT
	;LD	A,$00		; BAD
	;LD	(HL),A		; SEED
;
	LD	A,$AA		; CHECK COPY SUCCESSFULL
	LD	HL,$8000
	LD	BC,4096
NXTCMP:	CPI
	JP	PO,CMPOK
	JR	Z,NXTCMP

	DEC	HL
	CALL	PRTHEXWORDHL
	LD	A,' '
	CALL	COUT
	LD	A,(HL)
	CALL	PRTHEXBYTE
	
	RET			; RET W/ ZF CLEAR
;
CMPOK:
	RET			; RET W/ ZF SET
;
;==================================================================================================
; DMA MEMORY TEST
;==================================================================================================
;
DMAMemTest:
	call	DMAMemMove	; do a single memory copy
	jr	z,DMAMemTestOK
	jr	DMAMemTestFail
;
DMAMemTestOK:
	call	PRTSTRD
	.db	"\n\rMemory-Memory Test Passed\n\r$"
	ret
;
DMAMemTestFail:
	call	PRTSTRD
	.db	"\n\rMemory-Memory Test Failed\n\r$"
	ret
;
;==================================================================================================
; DMA MEMORY MOVE ITERATIVE
;==================================================================================================
;
DMAMemTestIter:
	ld	b,$20			; loop counter
	ld	a,b
	call	PRTDECB
	call	PRTSTRD
	.db	" iterations:\n\r$"
DMAMemTestIterLoop:
	push	bc			; save loop control
	call	DMAMemMove		; do an iteration
	jr	z,DMAMemTestIterOK
	call	PRTSTRD
	.db	" Mismatch\n\r$"
	jr	DMAMemTestIterCont	; continue
;
DMAMemTestIterOK:
	call	PRTSTRD
	.db	" Match\n\r$"
;
DMAMemTestIterCont:
	pop	bc
	djnz	DMAMemTestIterLoop
	call	NEWLINE
	ret
;
;==================================================================================================
; DMA PROBE - WRITE TO ADDRESS REGISTER AND READ BACK
;==================================================================================================
;
DMAProbe:
	ld	a,(dmaport)
	ld	c,a
	ld	a,DMA_RESET	
	out	(c),a
	ld	a,%01111101 		; R0-Transfer mode, A -> B, start address follows
	out	(c),a
	ld	a,$cc
	out	(c),a
	ld	a,$dd
	out	(c),a
	ld	a,$e5
	out	(c),a
	ld	a,$1a
	out	(c),a
	ld	a,DMA_LOAD
	out	(c),a
;
	ld	a,DMA_READ_MASK_FOLLOWS	; set up
	out	(c),a			; for 
	ld	a,%00011000		; register
	out	(c),a			; read
	ld	a,DMA_START_READ_SEQUENCE
	out	(c),a
;
	in	a,(c)			; read in 
	ld	e,a			; address
	in	a,(c)
	ld	d,a
;
	xor	a			; is it
	ld	hl,$ddcc		; a match
	sbc	hl,de			; return with
	ret	z			; status
	cpl
	ret
;
;==================================================================================================
; DMA COPY BLOCK CODE -  ASSUMES DMA PREINITIALIZED
;==================================================================================================
;
DMALDIR:
	ld	(DMASource),hl		; populate the dma
	ld	(DMADest),de		; register template
	ld	(DMALength),bc
;
	ld	hl,DMACopyMode
	call	SETXFER
	set	3,(hl)			; upper and lower address
;
	ld	hl,DMACopy		; program the
	ld	b,DMACopy_Len		; dma command
	ld	a,(dmaport)		; block
	ld	c,a
;
	di
	otir				; load and execute dma
	ei
;
	call	DMASTATUS
	ret
;
DMACopy 	;.db	DMA_DISABLE	; R6-Command Disable DMA
		.db	%01111101 	; R0-Transfer mode, A -> B, start address, block length follow
DMASource	.dw	0 		; R0-Port A, Start address
DMALength	.dw	0 		; R0-Block length
		.db	%00010100	; R1-No timing bytes follow, address increments, is memory
		.db	%00010000 	; R2-No timing bytes follow, address increments, is memory
		.db	%10000000 	; R3-DMA, interrupt, stop on match disabled
DMACopyMode:	.db	DMA_CONTINUOUS	; R4-Transfer mode. Destination address, interrupt and control byte follow
DMADest		.dw	0 		; R4-Port B, Destination address
		.db	DMA_ICBYTE	; R4-Pulse byte follows, Pulse generated
		.db	0		; R4-Pulse offset
;		.db	%10010010+DMA_RDY;R5-Stop on end of block, ce/wait multiplexed, READY active config
		.db	DMA_LOAD 	; R6-Command Load
		.db	DMA_FORCE_READY	; R6-Command Force ready
		.db	DMA_ENABLE 	; R6-Command Enable DMA
DMACopy_Len 	.equ	$-DMACopy
;
;==================================================================================================
; DMA COPY BLOCK CODE -  ASSUMES DMA PREINITIALIZED
; INTERRUPT VERSION!
;==================================================================================================
;
DMALDIRINT:
;
#IF (INTENABLE)
;
	ld	(DMASourceInt),hl	; populate the dma
	ld	(DMADestInt),de		; register template
	ld	(DMALengthInt),bc
;
	ld	hl,DMAICopyMode
	call	SETXFER
	set	3,(hl)			; upper and lower address
;
	ld	hl,DMACopyInt		; program the
	ld	b,DMACopyInt_Len	; dma command
	ld	a,(dmaport)		; block
	ld	c,a
;
	di
	otir				; load and execute dma
	ei
;
	call	DMASTATUS
#ENDIF
;
	ret
;
#IF (INTENABLE)
;
DMACopyInt 	;.db	DMA_DISABLE	; R6-Command Disable DMA
		.db	%01111101 	; R0-Transfer mode, A -> B, start address, block length follow
DMASourceInt	.dw	0 		; R0-Port A, Start address
DMALengthInt	.dw	0 		; R0-Block length
		.db	%00010100	; R1-No timing bytes follow, address increments, is memory
		.db	%00010000 	; R2-No timing bytes follow, address increments, is memory
		.db	%10100000 	; R3-DMA, interrupt, stop on match disabled
DMAICopyMode:	.db	DMA_CONTINUOUS	; R4-Transfer mode, destination address, interrupt and control byte follow
DMADestInt	.dw	0 		; R4-Port B, Destination address
		.db	%00011110	; R4-Interrupt control byte: Pulse byte follows, Pulse generated
		.db	0		; R4-Pulse control byte
		.db	INTIDX*2	; R4-Interrupt vector
;		.db	%10010010+DMA_RDY;R5-Stop on end of block, ce/wait multiplexed, READY active config
		.db	%10011010
		.db	DMA_LOAD 	; R6-Command Load
		.db	DMA_FORCE_READY	; R6-Command Force ready
		.db	DMA_ENABLE 	; R6-Command Enable DMA
DMACopyInt_Len 	.equ	$-DMACopyInt
;
#ENDIF
;
;==================================================================================================
; DMA I/O OUT BLOCK CODE - ADDRESS TO I/O PORT
;==================================================================================================
;
DMAOTIR:
	ld	(DMAOutSource),hl	; populate the dma
	ld	(DMAOutDest),a		; register template
	ld	(DMAOutLength),bc	
;
	ld	hl,DMAOutMode
	call	SETXFER
	res	3,(hl)			; no upper address
;
	ld	hl,DMAOutCode		; program the
	ld	b,DMAOut_Len		; dma command
	ld	a,(dmaport)		; block
	ld	c,a
;
	di
	otir				; load and execute dma
	ei
;
	call	DMASTATUS
	ret
;
DMAOutCode  	;.db	DMA_DISABLE	; R6-Command Disable DMA
		.db	%01111001 	; R0-Transfer mode, B -> A (temp), start address, block length follow	
DMAOutSource	.dw	0 		; R0-Port A, Start address
DMAOutLength	.dw	0 		; R0-Block length

		.db	%00010100	; R1-No timing bytes follow, fixed incrementing address, is memory	
		.db	%00101000 	; R2-No timing bytes follow, address static, is i/o			
		.db	%10000000 	; R3-DMA, interrupt, stop on match disabled

DMAOutMode:	.db	DMA_CONTINUOUS	; R4-Transfer Mode, destination port, interrupt and control byte follow
DMAOutDest	.db	0 		; R4-Port B, Destination port
		.db	DMA_ICBYTE	; R4-Pulse byte follows, Pulse generated
		.db	0		; R4-Pulse offset

		.db	%10010010+DMA_RDY;R5-Stop on end of block, ce/wait multiplexed, READY active config	
		.db	DMA_LOAD 	; R6-Command Load							
		.db	%00000101	; R0-Port A is Source							
		.db	DMA_LOAD 	; R6-Command Load							
		.db	DMA_FORCE_READY	; R6-Command Force ready						
		.db	DMA_ENABLE 	; R6-Command Enable DMA							
DMAOut_Len 	.equ	$-DMAOutCode
;
;==================================================================================================
; DMA I/O INPUT BLOCK CODE - I/O PORT TO ADDRESS
;==================================================================================================
;
DMAINIR:
	ld	(DMAInDest),hl		; populate the dma
	ld	(DMAInSource),a		; register template
	ld	(DMAInLength),bc	
;
	ld	hl,DMAOutMode
	call	SETXFER
	res	3,(hl)			; no upper address

	ld	hl,DMAInCode		; program the
	ld	b,DMAIn_Len		; dma command
	ld	a,(dmaport)		; block
	ld	c,a
;
	di
	otir				; load and execute dma
	ei
;
	call	DMASTATUS
	ret
;
DMAInCode 	;.db	DMA_DISABLE	; R6-Command Disable DMA
		.db	%01111001 	; R0-Transfer mode, B -> A, start address, block length follow		
DMAInDest	.dw	0 		; R0-Port A, Start address
DMAInLength	.dw	0 		; R0-Block length
		.db	%00010100	; R1-No timing bytes follow, address increments, is memory		
		.db	%00111000 	; R2-No timing bytes follow, address static, is i/o			
		.db	%10000000 	; R3-DMA, interrupt, stop on match disabled
DMAInMode:	.db	DMA_CONTINUOUS	; R4-Transfer mode, destination port, no interrupt, control byte.	
DMAInSource	.db	0 		; R4-Port B, Destination port
		.db	DMA_ICBYTE	; R4-Pulse byte follows, Pulse generated
		.db	0		; R4-Pulse offset
		.db	%10010010+DMA_RDY;R5-Stop on end of block, ce/wait multiplexed, READY active config	
		.db	DMA_LOAD 	; R6-Command Load							
		.db	DMA_FORCE_READY	; R6-Command Force ready						
		.db	DMA_ENABLE 	; R6-Command Enable DMA							

DMAIn_Len 	.equ	$-DMAInCode
;
;==================================================================================================
; SET TRANSFER MODE
;==================================================================================================
;
SETXFER:
	ld	a,(dmaxfer)		; setup the 
	cp	0			; transfer mode
	jr	nz,DMAX1
	ld	a,DMA_BYTE
	jr	DMAX3
DMAX1:	cp	1
	jr	nz,DMAX2
	ld	a,DMA_CONTINUOUS
	jr	DMAX3
DMAX2:	cp	2
	ret	nz
	ld	a,DMA_BURST
DMAX3:	ld	(hl),a
	ret
;
;==================================================================================================
; GET STATUS
;==================================================================================================
;
DMASTATUS:
	ld	a,(dmaxfer)		; if byte mode
	cp	0			; give some time to finish
	jr	nz,DMASTS1

;	ld	b,1
;DMASTS2:call	delay
;	djnz	DMASTS2
;
DMASTS1:ld	a,DMA_READ_STATUS_BYTE	; check status
	out	(c),a			; of transfer
	in	a,(c)
	and	%00111011
;
;	push	af
;	ld	a,DMA_REINIT_STATUS_BYTE
;	out	(c),a
;	pop	af
;
	call	PRTSTRD
	.db	"\n\rReturn Status: $"
	call	PRTHEXBYTE
	call	NEWLINE
;
	ld	c,a
	ld	a,(dmavbs)
	or	a
	jr	z,DMSSTS2
	ld	a,c
;
	ld	a,%00000001
	ld	de,DMASTSBIT0
	call	PRTIDXMSK
;
	ld	a,%00000010
	ld	de,DMASTSBIT1
	call	PRTIDXMSK
;
	ld	a,%00001000
	ld	de,DMASTSBIT3
	call	PRTIDXMSK
;
	ld	a,%00010000
	ld	de,DMASTSBIT4
	call	PRTIDXMSK
;
	ld	a,%00100000
	ld	de,DMASTSBIT5
	call	PRTIDXMSK
;
DMSSTS2:ld	a,c
	ret

DMASTSBIT0:
	.TEXT	"DMA Bus request did not occur after LOAD command\n\r$"
	.TEXT	"DMA Bus request occurred after the LOAD command\n\r$"
DMASTSBIT1:
	.TEXT	"Ready line inactive\n\r$"
	.TEXT	"Ready line active\n\r$"
DMASTSBIT3:
	.TEXT	"Interrupt pending\n\r$"
	.TEXT	"No interrupt pending\n\r$"
DMASTSBIT4:
	.TEXT	"Match found\n\r$"
	.TEXT	"No match found\n\r$"
DMASTSBIT5:
	.TEXT	"End of block reached\n\r$"
	.TEXT	"End of block not reached\n\r$"
;
;==================================================================================================
; DEBUG - READ START, DESTINATION AND COUNT REGISTERS
;==================================================================================================
;
DMARegDump:
	ld	a,(dmaport)
	ld	c,a
	ld	a,DMA_READ_MASK_FOLLOWS
	out	(c),a
	ld	a,%01111110
	out	(c),a
	ld	a,DMA_START_READ_SEQUENCE
	out	(c),a
;
	in	a,(c)
	ld	l,a
	in	a,(c)
	ld	h,a
	call	PRTHEXWORDHL
	ld	a,':'
	call	COUT
;
	in	a,(c)
	ld	l,a
	in	a,(c)
	ld	h,a
	call	PRTHEXWORDHL
	ld	a,':'
	call	COUT
;
	in	a,(c)
	ld	l,a
	in	a,(c)
	ld	h,a
	call	PRTHEXWORDHL
;
	call	NEWLINE
	ret
;
;==================================================================================================
; CONSOLE I/O ROUTINES
;==================================================================================================
;
CIO_CONSOLE	.EQU	$80	; HBIOS DEFAULT CONSOLE
BF_CIOOUT	.EQU	$01	; HBIOS FUNC: OUTPUT CHAR
BF_CIOIN	.EQU	$00	; HBIOS FUNC: INPUT CHAR
BF_CIOIST	.EQU	$02	; HBIOS FUNC: INPUT CHAR STATUS
;
;__CINU_______________________________________________________________________
;
;	INPUT AN UPPERCASE CHARACTER
;_____________________________________________________________________________
;
CINU:	CALL	CIN
	; Force upper case
	CP	'a'			; < 'a'
	RET	C			; IF SO, JUST CONTINUE
	CP	'z'+1			; > 'z'
	RET	NC			; IS SO, JUST CONTINUE
	SUB	'a'-'A'			; CONVERT TO UPPER
	RET
;
;__HEXIN_______________________________________________________________________
;
;	INPUT A HEX BYTE, RETURN VALUE IN A
;_____________________________________________________________________________
;
HEXIN:	CALL	CINU		; GET 1 CHAR INPUT
	push	af
	CALL	ISHEX		; CHECK FOR VALID CHARACTER
	JR	C,HEXIN
	ADD	A,A
	ADD	A,A	
	ADD	A,A
	ADD	A,A
	LD	C,A		; Save top nibble
	pop	af		; Retreive letter
	call	COUT		; and display it
;
HEXIN1:	CALL	CINU		; GET 1 CHAR INPUT
	push	af
	CALL	ISHEX		; CHECK FOR VALID CHARACTER
	JR	C,HEXIN1
;
	or	c
	ld	c,a
	pop	af		; Retreive letter
	call	COUT		; and display it
;
	ld	a,c
	RET
	
;	CF SET MEANS CHARACTER 0-9,A-F

ISHEX:	CP	'0'			; < '0'?
	JR	C,ISHEX1		; YES, NOT 0-9, CHECK A-F
	CP	'9' + 1			; > '9'
	jr	nc,ISHEX1
	sub	'0'			; MUST BE 0-9, RETURN
	ret
ISHEX1:	CP	'A'			; < 'A'?
	ret	c			; YES, NOT A-F, FAIL
	cp	'F' + 1			; > 'F'
	jr	nc,ISHEX2
	sub	'A' - 10
	RET				; MUST BE A-F, RETURN
ISHEX2: SCF
	RET	
;
;__COUT_______________________________________________________________________
;
;	OUTPUT CHARACTER FROM A
;_____________________________________________________________________________
;
COUT:
	; SAVE ALL INCOMING REGISTERS
	PUSH	AF
	PUSH	BC
	PUSH	DE
	PUSH	HL
;
	; OUTPUT CHARACTER TO CONSOLE VIA HBIOS
	LD	E,A			; OUTPUT CHAR TO E
	LD	C,CIO_CONSOLE		; CONSOLE UNIT TO C
	LD	B,BF_CIOOUT		; HBIOS FUNC: OUTPUT CHAR
	CALL	$FFF0			; HBIOS OUTPUTS CHARACTER
;
	; RESTORE ALL REGISTERS
	POP	HL
	POP	DE
	POP	BC
	POP	AF
	RET
;
;__CIN________________________________________________________________________
;
;	INPUT CHARACTER TO A
;_____________________________________________________________________________
;
CIN:
	; SAVE INCOMING REGISTERS (AF IS OUTPUT)
	PUSH	BC
	PUSH	DE
	PUSH	HL
;
	; INPUT CHARACTER FROM CONSOLE VIA HBIOS
	LD	C,CIO_CONSOLE		; CONSOLE UNIT TO C
	LD	B,BF_CIOIN		; HBIOS FUNC: INPUT CHAR
	CALL	$FFF0			; HBIOS READS CHARACTER
	LD	A,E			; MOVE CHARACTER TO A FOR RETURN
;
	; RESTORE REGISTERS (AF IS OUTPUT)
	POP	HL
	POP	DE
	POP	BC
	RET
;
;__CST________________________________________________________________________
;
;	RETURN INPUT STATUS IN A (0 = NO CHAR, !=0 CHAR WAITING)
;_____________________________________________________________________________
;
CST:
	; SAVE INCOMING REGISTERS (AF IS OUTPUT)
	PUSH	BC
	PUSH	DE
	PUSH	HL
;
	; GET CONSOLE INPUT STATUS VIA HBIOS
	LD	C,CIO_CONSOLE		; CONSOLE UNIT TO C
	LD	B,BF_CIOIST		; HBIOS FUNC: INPUT STATUS
	CALL	$FFF0			; HBIOS RETURNS STATUS IN A
;
	; RESTORE REGISTERS (AF IS OUTPUT)
	POP	HL
	POP	DE
	POP	BC
	RET
;
USEINT	.DB	FALSE		; USE INTERRUPTS FLAG
dmaport	.db	DMABASE
dmalach	.db	DMALATCH
dmaxfer	.db	DMA_XMODE
tstport	.db	DMAIOTST
dmavbs	.db	0
SAVSTK:	.DW	2
	.FILL	64
STACK:	.EQU	$
;
orgvec	.dw	0		; saved interrupt vector
;
;===============================================================================
; Interrupt Handler
;===============================================================================
;
reladr	.equ	$		; relocation start adr
;
	.org	$A000		; code will run here
;
int:
	;LD	E,'.'			; OUTPUT CHAR TO E
	;LD	C,CIO_CONSOLE		; CONSOLE UNIT TO C
	;LD	B,BF_CIOOUT		; HBIOS FUNC: OUTPUT CHAR
	;CALL	$FFF0			; HBIOS OUTPUTS CHARACTER

	; According to the DMA doc, you must issue
	; a DMA_DISABLE command prior to a
	; DMA_REINIT_STATUS_BYTE command to avoid a
	; potential race condition.
	ld	a,(int_dmaport)
	ld	c,a
	ld	a,DMA_DISABLE
	out	(c),a
;	
	; The doc confuses me, but apparently it is
	; necessary to reinitialize the status byte
	; when an end-of-block interrupt occurs.  Otherwise,
	; the end-of-block condition remains set and
	; causes the interrupt to fire continuously.
	ld	a,DMA_REINIT_STATUS_BYTE
	out	(c),a
;
	ld	hl,(counter)
	inc	hl
	ld	(counter),hl
;
	or	$ff		; signal int handled
	ret
;
; data referred to in handler must reside in high mem
;
int_dmaport	.db	0	; hi mem copy of dmaport
counter		.dw	0	; interrupt counter
;
hsiz	.equ	$ - $A000	; size of handler to relocate
;
	.org	reladr + hsiz
;
PROEND:	.EQU	$
;
	.END
