From 8e414a3efc33d1f1180d2612b873ad3a499e16f3 Mon Sep 17 00:00:00 2001 From: Wayne Warthen Date: Mon, 14 Mar 2022 10:46:11 -0700 Subject: [PATCH] DMAMON Enhancements - Added interrupt testing to DMAMON - Updated FIND to latest --- Source/Apps/Test/DMAmon/Readme.txt | 7 + Source/Apps/Test/DMAmon/dmamon.asm | 351 ++++++++++++++++++++++++----- Source/Apps/Test/DMAmon/dmamon.sh | 3 - Source/Images/Common/All/FIND.COM | Bin 10662 -> 10280 bytes desktop.ini | 4 + 5 files changed, 311 insertions(+), 54 deletions(-) delete mode 100644 Source/Apps/Test/DMAmon/dmamon.sh create mode 100644 desktop.ini diff --git a/Source/Apps/Test/DMAmon/Readme.txt b/Source/Apps/Test/DMAmon/Readme.txt index 1d6ab543..24ca0401 100644 --- a/Source/Apps/Test/DMAmon/Readme.txt +++ b/Source/Apps/Test/DMAmon/Readme.txt @@ -1 +1,8 @@ DMAmon is a program to verify operation of the Z80 MBC DMA board + +Version 2 has been hacked to include testing for interrupts. This +requires running the application under RomWBW using IM2. There +is an equate in the source file to disable interrupt testing +if needed. + +--WBW 10:36 AM 3/14/2022 \ No newline at end of file diff --git a/Source/Apps/Test/DMAmon/dmamon.asm b/Source/Apps/Test/DMAmon/dmamon.asm index 5baa6be3..9cfc0441 100644 --- a/Source/Apps/Test/DMAmon/dmamon.asm +++ b/Source/Apps/Test/DMAmon/dmamon.asm @@ -16,6 +16,14 @@ TRUE .EQU ~FALSE SPD_FIXED .EQU 0 ; PLATFORM SPEED FIXED AND CANNOT CHANGE SPEEDS SPD_HILO .EQU 1 ; PLATFORM CAN CHANGE BETWEEN TWO SPEEDS ; +; INTERRUPT TESTING CONFIGURATION +; N.B., INTERRUPT TESTING REQUIRES ROMWBW!!! +; 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 +; ; SYSTEM SPEED CHARACTERISTICS ; SPD_UNSUP .EQU 0 ; PLATFORM CAN CHANGE SPEEDS BUT IS UNSUPPORTED @@ -56,14 +64,20 @@ DMA_RESET .equ $c3 ;DMA_RESET_PORT_B_TIMING .equ $cb ;DMA_CONTINUE .equ $d3 ;DMA_DISABLE_INTERUPTS .equ $af -;DMA_ENABLE_INTERUPTS .equ $ab +DMA_ENABLE_INTERUPTS .equ $ab ;DMA_RESET_DISABLE_INTERUPTS .equ $a3 ;DMA_ENABLE_AFTER_RETI .equ $b7 -;DMA_REINIT_STATUS_BYTE .equ $8b +DMA_REINIT_STATUS_BYTE .equ $8b ; DMA_RDY .EQU %00001000 DMA_FORCE .EQU 0 - +; +bf_sysint .equ $FC ; INT function +; +bf_sysintinfo .equ $00 ; INT INFO subfunction +bf_sysintget .equ $10 ; INT GET subfunction +bf_sysintset .equ $20 ; INT SET subfunction +; #IF (DMA_USEHS & (DMAMODE=DMAMODE_MBC)) #IF (CPUSPDDEF=SPD_HIGH) #DEFINE DMAIOSLO LD A,(HB_RTCVAL) \ AND %11110111 \ OUT (RTCIO),A @@ -100,66 +114,128 @@ MAIN: LD SP,STACK ; STACK ; call PRTSTRD ; WELCOME - .db "DMA MONITOR\n\r$" + .db "\n\rDMA Monitor V2\n\r$" +; +#IF (INTENABLE) +; + ; Install interrupt handler in upper mem + ld hl,reladr + ld de,$A000 + ld bc,hsiz + ldir +; + ; 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 CIN ; GET SELECTION -; + ; Force upper case + CP 'a' ; < 'a' + JR C,MENULP1 ; IF SO, JUST CONTINUE + CP 'z'+1 ; > 'z' + JR NC,MENULP1 ; IS SO, JUST CONTINUE + SUB 'a'-'A' ; CONVERT TO UPPER +; +MENULP1: + CALL NEWLINE 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 MOVE + JP Z,DMATST_M ; MEMORY COPY + CP 'N' + JP Z,DMATST_N ; MEMORY COPY ITER CP '0' JP Z,DMATST_01 CP '1' - JR Z,DMATST_01 + JP Z,DMATST_01 CP 'R' JP Z,DMATST_R ; TOGGLE RESET CP 'Y' JP Z,DMATST_Y ; TOGGLE READY CP 'X' - JR Z,DMABYE ; EXIT + JP Z,DMABYE ; EXIT ; JR MENULP ; -DMABYE: LD SP,(SAVSTK) ; RESTORE CP/M STACK +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 ; DMATST_I: call PRTSTRD - .db "\n\rSTART DMA_INIT\n\r$" + .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\rSTART DMAMemMove\n\r$" - CALL DMAMemMove + .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_01: call PRTSTRD - .db "\n\rTOGGLE PORT\n\r$" + .db "\n\rPerforming Port Selection Test\n\r$" CALL DMA_Port01 JP MENULP ; DMATST_D: call PRTSTRD - .db "\n\rSTART DMARegDump\n\r$" + .db "\n\rDump Registers\n\r$" CALL DMARegDump JP MENULP ; DMATST_Y: call PRTSTRD - .db "\n\rTEST READY\n\r$" + .db "\n\rPerforming Ready Bit Test\n\r$" CALL DMA_ReadyT JP MENULP ; DMATST_R: call PRTSTRD - .db "\n\rRESET\n\r$" + .db "\n\rPerforming Reset\n\r$" ; CALL JP MENULP ;================================================================================================== @@ -167,19 +243,39 @@ DMATST_R: ;================================================================================================== ; DISPM: call PRTSTRD - .db "\n\rDMA DEVICE: $" + .db "\n\rDMA Device: $" LD C,DMAMODE ; DISPLAY LD A,00000111B ; TARGET LD DE,DMA_DEV_STR ; DEVICE CALL PRTIDXMSK - CALL NEWLINE ; call PRTSTRD - .db "DMA PORT: $" + .db ", Port=0x$" LD A,DMABASE ; DISPLAY CALL PRTHEXBYTE ; DMA PORT - CALL NEWLINE ; +#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 +; + call NEWLINE LD HL,MENU_OPT ; DISPLAY CALL PRTSTR ; MENU OPTIONS ; @@ -206,7 +302,7 @@ DMA_INIT: jr nz,DMA_NOTFOUND ; call PRTSTRD - .db " DMA FOUND\n\r$" + .db " DMA Found\n\r$" ; ld hl,DMACode ; program the ld b,DMACode_Len ; dma command @@ -224,7 +320,7 @@ DMA_EXIT: DMA_NOTFOUND: push af call PRTSTRD - .db " NOT PRESENT$" + .db " NOT Present$" pop af jr DMA_EXIT ; @@ -242,38 +338,41 @@ DMA_DEV_STR: MENU_OPT: .TEXT "\n\r" .TEXT "I) Initialize DMA\n\r" - .TEXT "M) Memory to Memory test\n\r" - .TEXT "0) DMA Port select test\n\r" - .TEXT "1) DMA Latch Port select test\n\r" - .TEXT "Y) Ready bit test\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 "0) Test DMA Port Selection\n\r" + .TEXT "1) Test DMA Latch Port Selection\n\r" + .TEXT "Y) Test Ready Bit\n\r" .TEXT "X) Exit\n\r" .TEXT ">$" ; ;================================================================================================== -; TOGGLE A PORT ON AND OFF +; PULSE PORT ;================================================================================================== ; DMA_Port01: + call PRTSTRD + .db "\r\nPulsing port 0x$" sub '0' ; Calculate add a,DMABASE ; Port to + call PRTHEXBYTE + call NEWLINE ld c,a ; toggle - ld b,0 + ld b,$20 ; loop counter portlp: push bc - call PRTSTRD - .db "\n\rON ...$" - call PRTHEXWORD + call PC_PERIOD push bc ld b,0 ld a,0 portlp1:out (c),a djnz portlp1 pop bc - call PRTSTRD - .db " OFF$" call delay pop bc djnz portlp + call NEWLINE JP MENULP ; delay: push bc @@ -290,18 +389,23 @@ dlylp: dec bc ;================================================================================================== ; DMA_ReadyT: + call NEWLINE ld c,DMABASE+1 ; toggle - ld b,0 + ld b,$20 ; loop counter portlp2:push bc + ld a,b + call PRTDECB call PRTSTRD - .db "\n\rON ...$" - call PRTHEXWORD + .db ": ON$" + call delay ld a,$FF ld c,DMABASE+1 out (c),a call PRTSTRD - .db " OFF$" + .db " -> OFF$" call delay + call PRTSTRD + .db "\r \r$" ld c,DMABASE+1 ld a,0 out (c),a @@ -332,8 +436,16 @@ DMAMemMove: LD HL,PROEND ; DMA COPY LD DE,$8000 LD BC,4096-1 - CALL DMALDIR - + 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 @@ -345,15 +457,57 @@ DMAMemMove: NXTCMP: CPI JP PO,CMPOK JR Z,NXTCMP - - call PRTHEXWORD + 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 " TEST MEMORY MOVE FAILED\n\r$" - RET - -CMPOK: call PRTSTRD - .db "TEST MEMORY MOVE SUCCEEDED\n\r$" - RET + .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 + call PRTSTRD + .db "\n\rPerforming $" + ld a,b + call PRTDECB + call PRTSTRD + .db " iterations, '.'=OK, '*'=Fail\n\r$" +DMAMemTestIterLoop: + push bc ; save loop control + call DMAMemMove ; do an iteration + jr z,DMAMemTestIterOK + call PC_ASTERISK ; signal failure + jr DMAMemTestIterCont ; continue +; +DMAMemTestIterOK: + call PC_PERIOD ; signal pass +; +DMAMemTestIterCont: + pop bc + djnz DMAMemTestIterLoop + call NEWLINE + ret ; ;================================================================================================== ; DMA PROBE - WRITE TO ADDRESS REGISTER AND READ BACK @@ -455,6 +609,62 @@ DMADest .dw 0 ; R4-Port B, Destination address 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,DMACopyInt ; program the + ld b,DMACopyInt_Len ; dma command + ld c,DMABASE ; block +; + DMAIOSLO + di + otir ; load and execute dma + ei +; + ld a,DMA_READ_STATUS_BYTE ; check status + out (DMABASE),a ; of transfer + in a,(DMABASE) ; set non-zero + and %00111011 ; if failed + sub %00011011 + DMAIONOR +; +#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 + .db DMA_CONTINUOUS ; R4-Continuous 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 ;================================================================================================== ; @@ -552,8 +762,6 @@ DMAIn_Len .equ $-DMAInCode ; DEBUG - READ START, DESTINATION AND COUNT REGISTERS ;================================================================================================== ; -;#IF (0) -; DMARegDump: ld a,DMA_READ_MASK_FOLLOWS out (DMABASE),a @@ -586,7 +794,6 @@ DMARegDump: ; call NEWLINE ret -;#ENDIF ; CIO_CONSOLE .EQU $80 ; CONSOLE UNIT TO C BF_CIOOUT .EQU $01 ; HBIOS FUNC: OUTPUT CHAR @@ -662,10 +869,52 @@ CST: POP DE POP BC RET - +; +USEINT .DB FALSE ; USE INTERRUPTS FLAG +; 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: + ; 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,DMA_DISABLE + out (DMABASE),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 (DMABASE),a +; + ld hl,(counter) + inc hl + ld (counter),hl +; + or $ff ; signal int handled + ret +; +counter .dw 0 +; +hsiz .equ $ - $A000 ; size of handler to relocate +; + .org reladr + hsiz +; PROEND: .EQU $ ; .END diff --git a/Source/Apps/Test/DMAmon/dmamon.sh b/Source/Apps/Test/DMAmon/dmamon.sh deleted file mode 100644 index 2d738d47..00000000 --- a/Source/Apps/Test/DMAmon/dmamon.sh +++ /dev/null @@ -1,3 +0,0 @@ -~/RomWBW-dev/Tools/unix/uz80as/uz80as -t z80 dmamon.asm dmamon.bin -#srec_cat dmamon.bin -binary -offset 0x0100 --address-length=2 -o dmamon.hex -Intel -cat dmamon.bin > dmamon.com \ No newline at end of file diff --git a/Source/Images/Common/All/FIND.COM b/Source/Images/Common/All/FIND.COM index 2ed5c52be9bf878283c8502d04525f4cc37f6356..163dcc9123bc09bb9e4752edf90697ecf96d88dd 100644 GIT binary patch literal 10280 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 z