; ; Testing Z80 DMA memory-to-I/O transfer ; ; Note: For proper functionality: ; 1. SIO/2 W/!RDYB output (#30) must be connected to DMA input RDY (#25). I use RC2014 #40 bus line ; 2. Bits 5, 6, 7 of SIO/2 register WR1 must be configured to !W/!RDY acts as !RDY for TX and enable it ; 3. DMA must be programmed to memory-to-I/O transfer with fixed I/O address and WR5 bit 3 = 0 to set Ready active Low ; ; See: http://www.z80.info/zip/um0081.pdf Page 121 - Fixed-Address Destination Ports ; .ORG 100H ; CP/M programs start address JP START ; Go to program start ; ; Variable storage space ; ; https://www.asciiart.eu/animals/cats CAT1 .DB ",_ _", 13, 10 .DB " |\\_,-~/", 13, 10 .DB " / _ _ | ,--.", 13, 10 .DB "( @ @ ) / ,-", 39, 13, 10 .DB " \\ _T_/-._( (", 13, 10 .DB " / `. \\ ", 13, 10 .DB "| _ \\ |", 13, 10 .DB " \\ \\ , / |", 13, 10 .DB " || |-_\\___ /", 13, 10 .DB " ((_/` (____,-", 39, 13, 10, 13, 10, 13, 10, 0 CAT2 .DB " _._ _,- ", 34, 34, "`-._ ", 13, 10 .DB "(,-.`._,/( |\\`-/|", 13, 10 .DB " `-.- \\ )-`( , @ @ ) ", 13, 10 .DB " `- \\`_ ", 34, "-", 13, 10, 13, 10, 13, 10, 0 ; Art by Hayley Jane Wakenshaw CAT3 .DB " ,-", 34, 34, 34, 34, 34, 34, "-.", 13, 10 .DB " /\\j__/\\ ( \\`--.", 13, 10 .DB "hjw \\`@_@\\ / _) >--.`.", 13, 10 .DB " _{.:Y:_}_{{_,/ ) ) ", 13, 10 .DB " {_}`-^{_} ``` (_)", 13, 10, 13, 10, 13, 10, 0 CAT4 .DB " /)", 13, 10 .DB " /\\___/\\ ((", 13, 10 .DB " \\`@_@", 39, "/ ))", 13, 10 .DB " {_:Y:.}_//", 13, 10 .DB "hjw ----------{_}^-", 39, "{_}----------", 13, 10, 13, 10, 13, 10, 0 CAT5 .DB " _..---...,", 34, 34, "-._ ,/}/)", 13, 10 .DB " .'' , ``..", 39, "(/-<", 13, 10 .DB " / _ { ) \\ ", 13, 10 .DB " ; _ `. `. < a(", 13, 10 .DB " ,", 39, " ( \\ ) `. \\ __.._ .: y", 13, 10 .DB "( <\\_-) )", 39, "-.____...\\ `._ //-", 39, 13, 10 .DB " `. `-", 39, " /-._))) `-._)))", 13, 10 .DB " `...", 39, " hjw", 13, 10, 13, 10, 13, 10, 0 CAT6 .DB " ,-. _,---._ __ / \\ ", 13, 10 .DB " / ) .-/ `./ / \\ ", 13, 10 .DB "( ( ,/ `/ /|", 13, 10 .DB " \\ `- \\ \\ / | ", 13, 10 .DB " `. , \\ \\ / | ", 13, 10 .DB " /`. / -`----Y | ", 13, 10 .DB " ( ; | ", 39, 13, 10 .DB " | ,-. ,-/ | /", 13, 10 .DB " | | ( | RC2014 | /", 13, 10 .DB " ) | \\ `.___________|/ hjw", 13, 10 .DB " `--/ `--", 34, 13, 10, 13, 10, 13, 10, 0 CATX .DB 0 MSG_START .DB "Starting DMA transfer", 13, 10, 0 MSG_FINISH .DB "Finished DMA transfer", 13, 10, 0 Stack1 .DW 0 ; Place to save old stack Sbot .DS 64 ; Temp stack for us to use ; ; Constants ; STOP .EQU $-1 ; Top of our stack BDOS .EQU 5 ; Address of BDOS entry ; ; Peripheral I/O adresses ; LEDS .EQU 00H ; Digital I/O board PIO1_A_D .EQU 068H ; Z80 PIO #1 PIO1_A_C .EQU 06AH PIO1_B_D .EQU 069H PIO1_B_C .EQU 06BH PIO2_A_D .EQU 06CH ; Z80 PIO #2 PIO2_A_C .EQU 06EH PIO2_B_D .EQU 06DH PIO2_B_C .EQU 06FH PIO3_A_D .EQU 0F0H ; Z80 PIO #3 PIO3_A_C .EQU 0F2H PIO3_B_D .EQU 0F1H PIO3_B_C .EQU 0F3H PIO4_A_D .EQU 0F4H ; Z80 PIO #4 PIO4_A_C .EQU 0F6H PIO4_B_D .EQU 0F5H PIO4_B_C .EQU 0F7H DMA .EQU 08CH ; Z80 DMA SIOA_D .EQU 081H ; Z80 SIO SIOA_C .EQU 080H SIOB_D .EQU 083H SIOB_C .EQU 082H RTS_HIGH .EQU 0E8H RTS_LOW .EQU 0EAH ; ; Storage for DMA Read Registers ; RR0 .DB 00H ; Status Byte RR1 .DB 00H ; Byte Counter (low byte) RR2 .DB 00H ; Byte Counter (high byte) RR3 .DB 00H ; Port A Address Counter (lOW byte) RR4 .DB 00H ; Port A Address Counter (high byte) RR5 .DB 00H ; Port B Address Counter (low byte) RR6 .DB 00H ; Port B Address Counter (high byte) ; ; CP/M BIOS stuff ; conout .EQU 0E60CH ; 4 Console OUTput punch .EQU 0E612H ; 6 Punch OUTput ; ; Start of code segment ; START: ; ------------------------------------------------------------ ; Starting sequence for CP/M programs LD HL, 0000H ; HL = 0 ADD HL, SP ; HL = SP LD (Stack1), HL ; Save original stack LD HL, STOP ; HL = address of new stack LD SP, HL ; Stack pointer = our stack ; ------------------------------------------------------------ ; Initialize Z80 PIO's LD A, 0FH ; Control word for PIO, set output mode OUT (PIO1_A_C), A ; PIO #1 port A OUT (PIO1_B_C), A ; PIO #1 port B OUT (PIO2_A_C), A ; PIO #2 port A OUT (PIO2_B_C), A ; PIO #2 port B OUT (PIO3_A_C), A ; PIO #3 port A OUT (PIO3_B_C), A ; PIO #3 port B OUT (PIO4_A_C), A ; PIO #4 port A OUT (PIO4_B_C), A ; PIO #4 port B ; Turn all LED's OFF XOR A ; LD A, 0 -> XOR A OUT (LEDS), A OUT (PIO1_A_D), A OUT (PIO1_B_D), A OUT (PIO2_A_D), A OUT (PIO2_B_D), A OUT (PIO3_A_D), A OUT (PIO3_B_D), A OUT (PIO4_A_D), A OUT (PIO4_B_D), A ; ------------------------------------------------------------ ; Print MSG_START and SOURCE to console LD HL, MSG_START ; HL = address of string MSG_START CALL SEND_STR_CONPUN ; Send string in (HL) to console and punch ; ------------------------------------------------------------ ; Initialize Z80 SIO ch. B - set WR1 bit 6 to 1 LD A, $00 ; Write into WR0: select WR0 OUT (SIOB_C), A LD A, $18 ; Write into WR0: 00011000b = channel reset OUT (SIOB_C), A LD A, $04 ; Write into WR0: select WR4 OUT (SIOB_C), A LD A, $C4 ; Write into WR4: xC=xD x64, 8bit sync. 1 stop bit, no parity OUT (SIOB_C), A LD A, 01H ; Write into WR0: select WR1 OUT (SIOB_C), A LD A, 0D8H ; Write into WR1: INT on all Rx chars, (parity does no affect) ... OUT (SIOB_C), A ; ... W/RDY acts as RDY - it's important for cooperation with DMA ! OUT (LEDS), A ; Confirm it on I/O board LD A, $03 ; Write into WR0: select WR3 OUT (SIOB_C), A LD A, $E1 ; Write into WR3: Rx 8bits, Auto, Rx Enable OUT (SIOB_C), A LD A, $05 ; Write into WR0: select WR5 OUT (SIOB_C), A LD A, RTS_LOW ; Write into WR5: DTR active, TX 8bit, BREAK off, TX on, RTS inactive OUT (SIOB_C), A REPEAT: ; ------------------------------------------------------------ ; Cat 1 LD A, 00000001B OUT (PIO4_A_D), A LD HL, CAT1 ; CAT1 HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console LD HL, CAT1 LD (SOURCE), HL LD HL, CAT2 LD DE, CAT1 AND A ; Clear Carry Flag, so SBC works as a SUB SBC HL, DE ; Compute Cat length DEC HL ; See: UM008101-0601 page 95 LD (LENGTH), HL CALL GO_DMA ; ------------------------------------------------------------ ; Cat 2 LD A, 00000010B OUT (PIO4_A_D), A LD HL, CAT2 ; CAT2 HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console LD HL, CAT2 LD (SOURCE), HL LD HL, CAT3 LD DE, CAT2 AND A ; Clear Carry Flag, so SBC works as a SUB SBC HL, DE ; Compute Cat length DEC HL ; See: UM008101-0601 page 95 LD (LENGTH), HL CALL GO_DMA ; ------------------------------------------------------------ ; Cat 3 LD A, 00000100B OUT (PIO4_A_D), A LD HL, CAT3 ; CAT3 HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console LD HL, CAT3 LD (SOURCE), HL LD HL, CAT4 LD DE, CAT3 AND A ; Clear Carry Flag, so SBC works as a SUB SBC HL, DE ; Compute Cat length DEC HL ; See: UM008101-0601 page 95 LD (LENGTH), HL CALL GO_DMA ; ------------------------------------------------------------ ; Cat 4 LD A, 00001000B OUT (PIO4_A_D), A LD HL, CAT4 ; CAT4 HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console LD HL, CAT4 LD (SOURCE), HL LD HL, CAT5 LD DE, CAT4 AND A ; Clear Carry Flag, so SBC works as a SUB SBC HL, DE ; Compute Cat length DEC HL ; See: UM008101-0601 page 95 LD (LENGTH), HL CALL GO_DMA ; ------------------------------------------------------------ ; Cat 5 LD A, 00010000B OUT (PIO4_A_D), A LD HL, CAT5 ; CAT5 HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console LD HL, CAT5 LD (SOURCE), HL LD HL, CAT6 LD DE, CAT5 AND A ; Clear Carry Flag, so SBC works as a SUB SBC HL, DE ; Compute Cat length DEC HL ; See: UM008101-0601 page 95 LD (LENGTH), HL CALL GO_DMA ; ------------------------------------------------------------ ; Cat 6 LD A, 00100000B OUT (PIO4_A_D), A LD HL, CAT6 ; CAT6 HL = address of string CALL SEND_STR_CON ; Send string in (HL) to console LD HL, CAT6 LD (SOURCE), HL LD HL, CATX LD DE, CAT6 AND A ; Clear Carry Flag, so SBC works as a SUB SBC HL, DE ; Compute Cat length DEC HL ; See: UM008101-0601 page 95 LD (LENGTH), HL CALL GO_DMA JP REPEAT EXIT: LD HL, MSG_FINISH ; HL = address of string MSG_FINISH CALL SEND_STR_CONPUN ; Send string in (HL) to console and punch ; ------------------------------------------------------------ ; Finish and return to CP/M LD HL, (Stack1) ; HL = entry stack address LD SP, HL ; SP = value on entry RET ; Return control back to CP/M ;-------------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------------- ; ; Send Cat to SIO ch. B using DMA ; ; ------------------------------------------------------------ ; Z80 DMA sequence: It copies memory from SOURCE to Z80 SIO port B DMA_PROG: .DB 0C3H ; 1100 0011 WR6: RESET - disable interrupt, bus-request logic, reset interrupt, reset wait, ; ... reset auto repeat, reset port A and port B timing ; Write Port A (variable source) starting address to WR0 .DB 79H ; 0111 1001 WR0: Transfer from B to A, Port A Starting address follows, Block length follows SOURCE: .DW 0000H ; Port A starting address LENGTH: .DW 0000H ; Block length, See: UM008101-0601 page 95 .DB 14H ; 0001 0100 WR1: Port A is memory with incrementing address, Timing NOT follows .DB 28H ; 0010 1000 WR2: Port B is I/O with fixed address, Timing NOT follows ; Write Port B (fixed destination) address to WR4 .DB 0C5H ; 1100 0101 WR4: Burst Mode, Port B Starting address follows, Interrupt control NOT follows .DB SIOB_D ; Target address .DB 82H ; 1000 0010 WR5: Stop on end of block, CE only, Ready active Low ; Load Port B address with the LOAD command .DB 0CFH ; 1100 1111 WR6: LOAD, Unforce a FORCE READY condition ; Declare Port A as source in WR0 (bit 2 = 1) .DB 05H ; 0000 0101 WR0: Transfer from A to B ; Load Port A address with the LOAD command .DB 0CFH ; 1100 1111 WR6: LOAD, Unforce a FORCE READY condition ; Enable DMA with the ENABLE DMA command .DB 87H ; 1000 0111 WR6: Enables DMA to start operation DMA_PROG_LEN .EQU $-DMA_PROG ; ------------------------------------------------------------ ; Send DMA_PROG to Z80 DMA GO_DMA: ;CALL OUT_TO_LEDS IN A, (LEDS) ; Read buttons on I/O board OUT (LEDS), A ; Send A to Digital I/O board OR A ; Set Zero Flag JR Z, NOEXIT ; No button pressed -> NOEXIT POP HL ; Recover stack because we RETurning with JR JR EXIT NOEXIT: LD HL, DMA_PROG ; Location of the DMA program LD B, DMA_PROG_LEN ; The length of the program LD C, DMA ; The port of the DMA chip OTIR ; Transfer the program to the DMA and start it CALL DELAY CALL DMA_READ_REGS RET ;-------------------------------------------------------------------------------------- ; ; Read DMA Read Registers (RR0-RR6) and Send it to LED's ; DMA_READ_REGS: LD A, 0BBH ; WR6-0: A read mask byte follows OUT (DMA), A LD A, 07FH ; WR6-1: A read mask byte OUT (DMA), A LD A, 0A7H ; WR6: Initiate read sequence OUT (DMA), A LD HL, RR0 LD B, 7 INIR ; Read RR0 - RR6 LD A, (RR0) ; DMA Status byte OUT (LEDS), A ; Send to Digital I/O board LD A, (RR1) ; Byte counter - Low byte CALL REVERSE ; Because PIO #3 ch. B has LED's order 0 -> 7 OUT (PIO3_B_D), A LD A, (RR2) ; Byte counter - High byte CALL REVERSE ; Because PIO #3 ch. A has LED's order 0 -> 7 OUT (PIO3_A_D), A LD A, (RR3) ; Port A addres counter - Low byte OUT (PIO1_A_D), A LD A, (RR4) ; Port A addres counter - High byte OUT (PIO1_B_D), A LD A, (RR5) ; Port B addres counter - Low byte OUT (PIO2_A_D), A LD A, (RR6) ; Port B addres counter - High byte OUT (PIO2_B_D), A CALL DELAY ; Let user to read LED's status RET ;-------------------------------------------------------------------------------------- ; DELAY: ; Routine to add a delay ; PUSH BC PUSH DE LD C, 24 DELAY0: LD D, 0FFH DELAY1: LD B, 0FFH DELAY2: NOP DJNZ DELAY2 DEC D JR NZ, DELAY1 DEC C JR NZ, DELAY0 POP DE POP BC RET ;-------------------------------------------------------------------------------------- ; ; Send HL and DE to PIO's ; OUT_TO_LEDS: PUSH AF LD A, H OUT (PIO1_A_D), A ; Send B to the PIO #1 port A LD A, L OUT (PIO1_B_D), A ; Send C to the PIO #1 port B LD A, D OUT (PIO2_A_D), A ; Send D to the PIO #2 port A LD A, E OUT (PIO2_B_D), A ; Send E to the PIO #2 port B POP AF RET ;-------------------------------------------------------------------------------------- ; ; Reverse bits in A ; 8 bytes / 206 cycles ; REVERSE: LD B, 8 LD L, A REVLOOP: RL L RRA DJNZ REVLOOP RET ;-------------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------------- ; ; Send zero-terminated string in (HL) to console ; SEND_STR_CON: LD A, (HL) ; Read string char OR A ; Set CPU flags RET Z ; If char = 0 done LD C, A CALL conout ; Send char in C to console INC HL ; Point to next char JR SEND_STR_CON ; Do next char ; ; Send zero-terminated string in (HL) to console and punch ; SEND_STR_CONPUN: LD A, (HL) ; Read string char OR A ; Set CPU flags RET Z ; If char = 0 done LD C, A CALL conout ; Send char in C to console CALL punch ; Send char in C to punch (SIO channel B) INC HL ; Point to next char JR SEND_STR_CONPUN ; Do next char ;-------------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------------- .END