;------------------------------------------------------------ ; SCSITEST.Z80 is a test program for the S100 Computers SCSI Board ; This program utilizes the Non-DMA IO approach for the NRC53890 SCSI controller. ; ; It is a standalone Z80 NCR5380 SCSI read/write sector test ; No CP/M BDOS calls required for console I/O. ; The bulk of the program was generated by Chat GPT. ; Console: ; CONSOLE_STATUS = 00H ; CONSOLE_DATA = 01H ; ; NCR5380: ; Base I/O address = 40H ; ; Menu driven: ; R = read selected CP/M 128-byte sector ; W = write test pattern to selected CP/M sector, then read back ; T = set track number, 4 hex digits ; S = set sector number, 2 hex digits, 01..40H normally ; D = dump last read sector buffer ; Q = quit / return ; ; Disk geometry: ; CP/M logical sector size = 128 bytes ; SCSI physical sector = 512 bytes ; CP/M sectors per track = 64 ; 4 CP/M sectors per SCSI sector ; ; LBA = TRACK * 16 + ((SECTOR - 1) / 4) ; ; WARNING: ; The W command writes a 128-byte test pattern into the selected ; CP/M sector using read/modify/write of the containing 512-byte ; SCSI physical sector. ;------------------------------------------------------------ CONSOLE_STATUS EQU 00H CONSOLE_DATA EQU 01H DEFAULT_TRK EQU 0000H DEFAULT_SEC EQU 0001H NCRBASE EQU 48H ;Base port for all ports on board ODR EQU NCRBASE+0 CSD EQU NCRBASE+0 ICR EQU NCRBASE+1 MR2 EQU NCRBASE+2 TCR EQU NCRBASE+3 SER EQU NCRBASE+4 CSB EQU NCRBASE+4 BSR EQU NCRBASE+5 IDR EQU NCRBASE+6 RPI EQU NCRBASE+7 ICR_DBUS EQU 01H ICR_ATN EQU 02H ICR_SEL EQU 04H ICR_BSY EQU 08H ICR_ACK EQU 10H ICR_RST EQU 80H PH_DATAOUT EQU 00H PH_DATAIN EQU 01H PH_COMMAND EQU 02H PH_STATUS EQU 03H PH_MSGOUT EQU 06H PH_MSGIN EQU 07H CSB_REQ EQU 20H CSB_BSY EQU 40H CSB_IO EQU 04H CSB_CD EQU 08H CSB_MSG EQU 10H CSB_PHASE EQU 1CH PHASE_DATAIN EQU 04H PHASE_STATUS EQU 0CH PHASE_MSGIN EQU 1CH CSB_REQ EQU 20H CSB_BSY EQU 40H OURID EQU 80H ;Controller SCSI ID (ID 7) TGTID EQU 01H ;Assumes SCSI drive ID is 0 ORG 0100H ;Load into RAM using the Master Z80 Monitor 'X' command START: LD SP,STACK LD HL,DEFAULT_TRK LD (CURTRK),HL LD HL,DEFAULT_SEC LD (CURSEC),HL CALL CRLF LD HL,MSG_TITLE CALL PSTRING MAIN_MENU: CALL SHOW_POSITION LD HL,MSG_MENU CALL PSTRING CALL CONSOLE_INPUT_UPPER CALL ECHO_CHAR CALL CRLF CP 'R' JP Z,DO_READ CP 'W' JP Z,DO_WRITE CP 'T' JP Z,SET_TRACK CP 'S' JP Z,SET_SECTOR CP 'D' JP Z,DO_DUMP CP 'Q' JP Z,EXIT_PROGRAM LD HL,MSG_BAD CALL PSTRING JP MAIN_MENU EXIT_PROGRAM: LD HL,MSG_EXIT CALL PSTRING RET DO_READ: CALL SCSI_TEST_READY OR A JP NZ,SCSI_ERROR CALL MAKE_LBA CALL SCSI_READ512 OR A JP NZ,SCSI_ERROR CALL COPY_FROM_SECBUF LD HL,MSG_READOK CALL PSTRING CALL DUMP_DMABUF JP MAIN_MENU DO_WRITE: LD HL,MSG_WARN1 CALL PSTRING CALL CONSOLE_INPUT_UPPER CALL ECHO_CHAR CALL CRLF CP 'Y' JP NZ,MAIN_MENU CALL SCSI_TEST_READY OR A JP NZ,SCSI_ERROR CALL MAKE_LBA CALL MAKE_PATTERN CALL COPY_TO_SECBUF_RMW OR A JP NZ,SCSI_ERROR CALL SCSI_WRITE512 OR A JP NZ,SCSI_ERROR LD HL,MSG_WRITEOK CALL PSTRING CALL SCSI_READ512 OR A JP NZ,SCSI_ERROR CALL COPY_FROM_SECBUF CALL DUMP_DMABUF JP MAIN_MENU SET_TRACK: LD HL,MSG_TRACK CALL PSTRING CALL READ_HEX4 LD (CURTRK),HL CALL CRLF JP MAIN_MENU SET_SECTOR: LD HL,MSG_SECTOR CALL PSTRING CALL READ_HEX2 LD L,A LD H,0 LD (CURSEC),HL CALL CRLF JP MAIN_MENU DO_DUMP: LD HL,MSG_DUMP CALL PSTRING CALL DUMP_DMABUF JP MAIN_MENU SCSI_ERROR: LD HL,MSG_SCSIERR CALL PSTRING JP MAIN_MENU MAKE_LBA: LD HL,(CURSEC) DEC HL LD A,L AND 03H CP 00H JR NZ,MO1 XOR A LD (SECOFFL),A LD (SECOFFH),A JR MOX MO1: CP 01H JR NZ,MO2 LD A,080H LD (SECOFFL),A XOR A LD (SECOFFH),A JR MOX MO2: CP 02H JR NZ,MO3 XOR A LD (SECOFFL),A LD A,01H LD (SECOFFH),A JR MOX MO3: LD A,080H LD (SECOFFL),A LD A,01H LD (SECOFFH),A MOX: LD A,L SRL A SRL A AND 0FH LD E,A LD D,0 LD HL,(CURTRK) ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,DE LD A,L LD (LBA0),A LD A,H LD (LBA1),A XOR A LD (LBA2),A RET COPY_FROM_SECBUF: LD HL,SECBUF LD A,(SECOFFL) LD E,A LD A,(SECOFFH) LD D,A ADD HL,DE LD DE,DMABUF LD BC,128 LDIR RET COPY_TO_SECBUF_RMW: CALL SCSI_READ512 OR A RET NZ LD HL,DMABUF LD DE,SECBUF LD A,(SECOFFL) LD C,A LD A,(SECOFFH) LD B,A EX DE,HL ADD HL,BC EX DE,HL LD BC,128 LDIR XOR A RET MAKE_PATTERN: LD HL,DMABUF LD B,128 LD A,00H MP1: LD (HL),A INC A INC HL DJNZ MP1 LD HL,DMABUF LD (HL),'S' INC HL LD (HL),'C' INC HL LD (HL),'S' INC HL LD (HL),'I' INC HL LD (HL),'-' INC HL LD (HL),'T' INC HL LD (HL),'E' INC HL LD (HL),'S' INC HL LD (HL),'T' RET SCSI_READ512: CALL BUILD_READ_CDB CALL SCSI_SELECT RET NZ LD HL,CDB LD B,6 CALL SCSI_SEND_CMD RET NZ ; Do not assume DATA IN. ; First see what phase the drive is asking for. CALL SCSI_WAIT_REQ_PHASE RET NZ LD A,(LASTPHASE) CP PHASE_DATAIN JR Z,SRD_DATAIN CP PHASE_STATUS JR Z,SRD_STATUS_ONLY ; Unexpected phase LD A,'P' CALL ZCO LD A,(LASTPHASE) CALL HEXOUT LD A,1 RET SCSI_WRITE512: CALL BUILD_WRITE_CDB CALL SCSI_SELECT RET NZ LD HL,CDB LD B,6 CALL SCSI_SEND_CMD RET NZ LD HL,SECBUF LD DE,512 CALL SCSI_SEND_DATA RET NZ CALL SCSI_GET_STATUS RET SRD_DATAIN: LD HL,SECBUF LD DE,512 CALL SCSI_RECV_DATA RET NZ CALL SCSI_GET_STATUS RET SRD_STATUS_ONLY: ; Drive gave status instead of data, probably CHECK CONDITION CALL SCSI_GET_STATUS LD A,1 RET BUILD_READ_CDB: LD HL,CDB LD (HL),08H INC HL LD A,(LBA2) AND 1FH LD (HL),A INC HL LD A,(LBA1) LD (HL),A INC HL LD A,(LBA0) LD (HL),A INC HL LD (HL),01H INC HL LD (HL),00H RET BUILD_WRITE_CDB: LD HL,CDB LD (HL),0AH INC HL LD A,(LBA2) AND 1FH LD (HL),A INC HL LD A,(LBA1) LD (HL),A INC HL LD A,(LBA0) LD (HL),A INC HL LD (HL),01H INC HL LD (HL),00H RET SCSI_SELECT: XOR A OUT (ICR),A OUT (TCR),A OUT (MR2),A SELWAIT: IN A,(CSB) AND CSB_BSY JR NZ,SELWAIT LD A,OURID+TGTID OUT (ODR),A LD A,ICR_DBUS+ICR_SEL OUT (ICR),A CALL DELAY SELBUSY: IN A,(CSB) AND CSB_BSY JR Z,SELBUSY XOR A OUT (ICR),A XOR A RET SCSI_SEND_CMD: LD A,PH_COMMAND OUT (TCR),A SCMD1: LD A,(HL) CALL SCSI_SEND_BYTE RET NZ INC HL DJNZ SCMD1 XOR A RET SCSI_SEND_DATA: LD A,PH_DATAOUT OUT (TCR),A SDO1: LD A,D OR E JR Z,SDO_DONE LD A,(HL) CALL SCSI_SEND_BYTE RET NZ INC HL DEC DE JP SDO1 SDO_DONE: XOR A RET SCSI_RECV_DATA: LD A,PH_DATAIN OUT (TCR),A SDI1: LD A,D OR E JR Z,SDI_DONE CALL SCSI_RECV_BYTE LD (HL),A INC HL DEC DE JP SDI1 SDI_DONE: XOR A RET SCSI_SEND_BYTE: OUT (ODR),A WSREQ1: IN A,(CSB) AND CSB_REQ JR Z,WSREQ1 LD A,ICR_DBUS OUT (ICR),A LD A,ICR_DBUS+ICR_ACK OUT (ICR),A WSREQ0: IN A,(CSB) AND CSB_REQ JR NZ,WSREQ0 XOR A OUT (ICR),A XOR A RET SCSI_RECV_BYTE: WRREQ1: IN A,(CSB) AND CSB_REQ JR Z,WRREQ1 IN A,(CSD) PUSH AF LD A,ICR_ACK OUT (ICR),A WRREQ0: IN A,(CSB) AND CSB_REQ JR NZ,WRREQ0 XOR A OUT (ICR),A POP AF RET SCSI_GET_STATUS: LD A,PH_STATUS OUT (TCR),A CALL SCSI_RECV_BYTE LD (SCSISTAT),A LD A,PH_MSGIN OUT (TCR),A CALL SCSI_RECV_BYTE LD (SCSIMSG),A LD A,(SCSISTAT) OR A JR Z,SGOK LD A,1 RET SGOK: XOR A RET SCSI_WAIT_REQ_PHASE: LD HL,0FFFFH SWRP1: IN A,(CSB) AND CSB_REQ JR NZ,SWRP2 DEC HL LD A,H OR L JR NZ,SWRP1 LD A,'!' CALL ZCO LD A,1 RET SWRP2: IN A,(CSB) AND CSB_PHASE LD (LASTPHASE),A XOR A RET ;------------------------------------------------------------ ; TEST UNIT READY loop. ; This clears the usual cold-start/unit-attention condition. ; Returns A=0 if ready, A=1 if not ready after retries. ;------------------------------------------------------------ SCSI_TEST_READY: LD A,40 LD (TURTRIES),A TUR_LOOP: CALL BUILD_TUR_CDB CALL SCSI_SELECT JR NZ,TUR_FAIL_ONE LD HL,CDB LD B,6 CALL SCSI_SEND_CMD JR NZ,TUR_FAIL_ONE CALL SCSI_WAIT_REQ_PHASE JR NZ,TUR_FAIL_ONE LD A,(LASTPHASE) CP PHASE_STATUS JR NZ,TUR_FAIL_ONE CALL SCSI_GET_STATUS LD A,(SCSISTAT) OR A JR Z,TUR_READY ; Not ready or unit attention. Read sense to clear it. CALL SCSI_REQUEST_SENSE TUR_FAIL_ONE: CALL BIG_DELAY LD HL,TURTRIES DEC (HL) JR NZ,TUR_LOOP LD A,1 RET TUR_READY: XOR A RET BUILD_TUR_CDB: LD HL,CDB XOR A LD (HL),A ; 00H = TEST UNIT READY INC HL LD (HL),A INC HL LD (HL),A INC HL LD (HL),A INC HL LD (HL),A INC HL LD (HL),A RET BUILD_SENSE_CDB: LD HL,CDB LD (HL),03H ; REQUEST SENSE INC HL XOR A LD (HL),A INC HL LD (HL),A INC HL LD (HL),A INC HL LD (HL),18 ; request 18 bytes INC HL LD (HL),A RET SCSI_REQUEST_SENSE: CALL BUILD_SENSE_CDB CALL SCSI_SELECT RET NZ LD HL,CDB LD B,6 CALL SCSI_SEND_CMD RET NZ CALL SCSI_WAIT_REQ_PHASE RET NZ LD A,(LASTPHASE) CP PHASE_DATAIN JR Z,SRS_DATA CP PHASE_STATUS JR Z,SRS_STATUS LD A,1 RET SRS_DATA: LD HL,SENSEBUF LD DE,18 CALL SCSI_RECV_DATA RET NZ SRS_STATUS: CALL SCSI_GET_STATUS XOR A RET BIG_DELAY: LD B,20 BD1: PUSH BC CALL DELAY POP BC DJNZ BD1 RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SUPPORT ROUTINES ;;;;;;;;;;;;;;;;;;;;;;;;;;; ZCI: IN A,(CONSOLE_STATUS) AND 02H JR Z,ZCI IN A,(CONSOLE_DATA) AND 7FH RET ZCO: PUSH AF ;Note this is different from typical Monitor ZCO ZCO1: IN A,(CONSOLE_STATUS) ;where the character in in [C] AND 04H JR Z,ZCO1 POP AF OUT (CONSOLE_DATA),A RET ZCSTS: IN A,(CONSOLE_STATUS) AND 02H JP Z,NOCHAR GOT_CHAR: XOR A DEC A RET NOCHAR: XOR A RET CONSOLE_INPUT_UPPER: CALL ZCI CP 'a' RET C CP 'z'+1 RET NC SUB 20H RET ECHO_CHAR: CALL ZCO RET PSTRING: LD A,(HL) CP '$' RET Z CALL ZCO INC HL JR PSTRING CRLF: PUSH AF LD A,13 CALL ZCO LD A,10 CALL ZCO POP AF RET SPACE: LD A,' ' CALL ZCO RET READ_HEX4: LD HL,0 LD B,4 RH4_1: CALL READ_HEX_NIBBLE ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL LD E,A LD D,0 ADD HL,DE DJNZ RH4_1 RET READ_HEX2: CALL READ_HEX_NIBBLE RLCA RLCA RLCA RLCA LD B,A CALL READ_HEX_NIBBLE OR B RET READ_HEX_NIBBLE: CALL CONSOLE_INPUT_UPPER CALL ECHO_CHAR CP '0' JR C,READ_HEX_NIBBLE CP '9'+1 JR C,RHN_DIGIT CP 'A' JR C,READ_HEX_NIBBLE CP 'F'+1 JR NC,READ_HEX_NIBBLE SUB 'A'-10 RET RHN_DIGIT: SUB '0' RET HEXOUT: PUSH AF RRCA RRCA RRCA RRCA CALL HEXNIB POP AF CALL HEXNIB RET HEXNIB: AND 0FH CP 10 JR C,HEXDIG ADD A,7 HEXDIG: ADD A,'0' CALL ZCO RET PRINT_HL_HEX: LD A,H CALL HEXOUT LD A,L CALL HEXOUT RET PRINT_A_HEX: CALL HEXOUT RET SHOW_POSITION: CALL CRLF LD HL,MSG_POS1 CALL PSTRING LD HL,(CURTRK) CALL PRINT_HL_HEX LD HL,MSG_POS2 CALL PSTRING LD HL,(CURSEC) LD A,L CALL PRINT_A_HEX CALL CRLF RET DUMP_DMABUF: LD HL,DMABUF LD B,8 DUMPLINE: PUSH BC LD B,16 DUMPBYTE: LD A,(HL) CALL HEXOUT CALL SPACE INC HL DJNZ DUMPBYTE CALL CRLF POP BC DJNZ DUMPLINE RET DELAY: LD BC,2000H DLY1: DEC BC LD A,B OR C JR NZ,DLY1 RET MSG_TITLE: DB 'NCR5380 SCSI SECTOR TEST (Using non-DMA IO mode)'13,10 DB 'and the Propeller Board for Console IO (V1.0) ',13,10 DB 'John Monahan S100 Computers.com 5/21/2026',13,10,'$' MSG_MENU: DB 13,10 DB 'R = Read selected sector',13,10 DB 'W = Write test pattern to selected sector',13,10 DB 'T = Set track, 4 hex digits',13,10 DB 'S = Set sector, 2 hex digits',13,10 DB 'D = Dump last buffer',13,10 DB 'Q = Quit',13,10 DB 'Choice: $' MSG_POS1: DB 'Current Track=$' MSG_POS2: DB ' Sector=$' MSG_TRACK: DB 'Enter track as 4 hex digits: $' MSG_SECTOR: DB 'Enter sector as 2 hex digits, 01-40H: $' MSG_BAD: DB 'Unknown command',13,10,'$' MSG_WARN1: DB 'WRITE WILL MODIFY THE DISK. Type Y to continue: $' MSG_READOK: DB 'READ OK - 128 byte sector dump:',13,10,'$' MSG_WRITEOK: DB 'WRITE OK - readback follows:',13,10,'$' MSG_DUMP: DB 'Last 128 byte buffer:',13,10,'$' MSG_SCSIERR: DB 'SCSI ERROR',13,10,'$' MSG_EXIT: DB 'Exiting.',13,10,'$' CURTRK: DW 0 CURSEC: DW 1 LBA0: DB 0 LBA1: DB 0 LBA2: DB 0 SECOFFL: DB 0 SECOFFH: DB 0 CDB: DS 6 SCSISTAT: DB 0 ;Chat GPT SCSIMSG: DB 0 LASTPHASE: DB 0 TURTRIES: DB 0 SENSEBUF: DS 18 DMABUF: DS 128 SECBUF: DS 512 DS 256 STACK: END START