;------------------------------------------------------------
; SCSI_RW.ASM
;
; Stand-alone CP/M 8080 test program for NCR5380 SCSI board.
; ORG 100H, suitable for assembling to a .COM program.
;
; Test performed:
;   1. Fill WRBUF with known 512-byte pattern: 00,01,02,...FF,00,...FF
;   2. Write WRBUF to raw SCSI LBA TESTLBA, default 100 decimal / 0064H
;   3. Clear RDBUF
;   4. Read same LBA into RDBUF
;   5. Compare WRBUF and RDBUF
;   6. Print PASS or first mismatch offset/expected/got
;
; WARNING: This program overwrites raw SCSI LBA 100.
; Use only on a scratch/test disk.
;
; Assumes:
;   NCR5380 board base I/O address = 48H
;   Host SCSI ID = 7
;   Target disk ID = 0
;   Drive uses 512-byte logical blocks
;------------------------------------------------------------

        ORG     0100H

BDOS    EQU     0005H

; CP/M BDOS functions
PRINTC  EQU     2
PRINTS  EQU     9

; NCR5380 ports
NCRBASE EQU     048H
ODR     EQU     NCRBASE+0       ; Output Data Register, write
CSD     EQU     NCRBASE+0       ; Current SCSI Data, read
ICR     EQU     NCRBASE+1       ; Initiator Command Register
MR2     EQU     NCRBASE+2       ; Mode Register 2
TCR     EQU     NCRBASE+3       ; Target Command Register
SER     EQU     NCRBASE+4       ; Select Enable Register
CSB     EQU     NCRBASE+4       ; Current SCSI Bus Status, read
BSR     EQU     NCRBASE+5       ; Bus and Status Register
IDR     EQU     NCRBASE+6       ; Input Data Register
RPI     EQU     NCRBASE+7       ; Reset Parity/Interrupt

; ICR bits
ICR$DBUS EQU    001H
ICR$ATN  EQU    002H
ICR$SEL  EQU    004H
ICR$BSY  EQU    008H
ICR$ACK  EQU    010H
ICR$RST  EQU    080H

; TCR phase values to output
PH$DATAOUT EQU  000H
PH$DATAIN  EQU  001H
PH$COMMAND EQU  002H
PH$STATUS  EQU  003H
PH$MSGOUT  EQU  006H
PH$MSGIN   EQU  007H

; CSB bits / masked phase values
CSB$REQ   EQU    020H
CSB$BSY   EQU    040H
CSB$PHASE EQU    01CH

PHASE$DATAOUT EQU 000H
PHASE$DATAIN  EQU 004H
PHASE$STATUS  EQU 00CH
PHASE$MSGIN   EQU 01CH

OURID   EQU     080H            ; host ID 7 bit mask
TGTID   EQU     001H            ; target ID 0 bit mask

; Test LBA = 100 decimal = 000064H
TESTL0  EQU     064H
TESTL1  EQU     000H
TESTL2  EQU     000H

;------------------------------------------------------------
; Program start
;------------------------------------------------------------
START:
        LXI     SP,STACKTOP

        LXI     D,MSG$HELLO
        CALL    PSTRING

        CALL    SCSI$INIT
        CALL    SCSI$TEST$READY
        ORA     A
        JZ      START$READY
        LXI     D,MSG$NOTREADY
        CALL    PSTRING
        JMP     DONE

START$READY:
        LXI     D,MSG$READY
        CALL    PSTRING

        CALL    FILL$WRBUF
        CALL    CLEAR$RDBUF

        LXI     D,MSG$WRITE
        CALL    PSTRING
        CALL    SCSI$WRITE$TESTLBA
        ORA     A
        JZ      WRITE$OK
        LXI     D,MSG$WRITEERR
        CALL    PSTRING
        JMP     DONE

WRITE$OK:
        LXI     D,MSG$READ
        CALL    PSTRING
        CALL    SCSI$READ$TESTLBA
        ORA     A
        JZ      READ$OK
        LXI     D,MSG$READERR
        CALL    PSTRING
        JMP     DONE

READ$OK:
        CALL    COMPARE$BUFS
        ORA     A
        JZ      PASS

        LXI     D,MSG$BAD
        CALL    PSTRING
        LHLD    BAD$OFF
        CALL    PHEX16
        LXI     D,MSG$EXP
        CALL    PSTRING
        LDA     BAD$EXP
        CALL    PHEX8
        LXI     D,MSG$GOT
        CALL    PSTRING
        LDA     BAD$GOT
        CALL    PHEX8
        CALL    PCRLF
        JMP     DONE

PASS:
        LXI     D,MSG$PASS
        CALL    PSTRING

DONE:
        RET                     ; back to CP/M

;------------------------------------------------------------
; Fill / clear / compare buffers
;------------------------------------------------------------
FILL$WRBUF:
        LXI     H,WRBUF
        LXI     B,0200H
        XRA     A               ; pattern byte starts at 00H
FILL$1:
        MOV     M,A
        INR     A
        INX     H
        DCX     B
        PUSH    PSW
        MOV     A,B
        ORA     C
        POP     PSW
        JNZ     FILL$1
        RET

CLEAR$RDBUF:
        LXI     H,RDBUF
        LXI     B,0200H
        XRA     A
CLR$1:
        MOV     M,A
        INX     H
        DCX     B
        MOV     A,B
        ORA     C
        JNZ     CLR$1
        RET

COMPARE$BUFS:
        LXI     H,0200H
        SHLD    COUNT
        LXI     H,WRBUF
        LXI     D,RDBUF
        XRA     A
        STA     OFFLO
        STA     OFFHI
CMP$1:
        MOV     A,M             ; expected
        STA     TEMP$EXP
        LDAX    D               ; got
        MOV     C,A             ; save got in C temporarily
        LDA     TEMP$EXP
        CMP     C
        JNZ     CMP$BAD

        INX     H
        INX     D
        CALL    INC$OFF

        ; Need loop count.  Use COUNT as separate 16-bit countdown.
        PUSH    H
        PUSH    D
        LHLD    COUNT
        DCX     H
        SHLD    COUNT
        MOV     A,H
        ORA     L
        POP     D
        POP     H
        JNZ     CMP$1
        XRA     A               ; success A=0
        RET

CMP$BAD:
        STA     BAD$EXP
        MOV     A,C
        STA     BAD$GOT
        LHLD    OFFLO           ; OFFLO/OFFHI are adjacent little-endian
        SHLD    BAD$OFF
        MVI     A,1
        RET

; This subroutine increments 16-bit OFFHI:OFFLO
INC$OFF:
        LDA     OFFLO
        INR     A
        STA     OFFLO
        RNZ
        LDA     OFFHI
        INR     A
        STA     OFFHI
        RET

;------------------------------------------------------------
; SCSI top-level test read/write LBA 100
;------------------------------------------------------------
SCSI$WRITE$TESTLBA:
        CALL    BUILD$WRITE$CDB
        CALL    SCSI$SELECT
        RNZ
        LXI     H,CDB
        MVI     B,6
        CALL    SCSI$SEND$CMD
        RNZ

        CALL    SCSI$WAIT$REQ$PHASE
        RNZ
        LDA     LASTPHASE
        CPI     PHASE$DATAOUT
        JNZ     SCSI$ERR

        LXI     H,WRBUF
        LXI     D,0200H
        CALL    SCSI$SEND$DATA
        RNZ

        CALL    SCSI$WAIT$REQ$PHASE
        RNZ
        LDA     LASTPHASE
        CPI     PHASE$STATUS
        JNZ     SCSI$ERR

        CALL    SCSI$GET$STATUS
        RET

SCSI$READ$TESTLBA:
        CALL    BUILD$READ$CDB
        CALL    SCSI$SELECT
        RNZ
        LXI     H,CDB
        MVI     B,6
        CALL    SCSI$SEND$CMD
        RNZ

        CALL    SCSI$WAIT$REQ$PHASE
        RNZ
        LDA     LASTPHASE
        CPI     PHASE$DATAIN
        JZ      SRT$DATA
        CPI     PHASE$STATUS
        JZ      SRT$STATONLY
        JMP     SCSI$ERR

SRT$DATA:
        LXI     H,RDBUF
        LXI     D,0200H
        CALL    SCSI$RECV$DATA
        RNZ
        CALL    SCSI$GET$STATUS
        RET

SRT$STATONLY:
        CALL    SCSI$GET$STATUS
        JMP     SCSI$ERR

SCSI$ERR:
        MVI     A,1
        RET

BUILD$READ$CDB:
        LXI     H,CDB
        MVI     M,008H          ; READ(6)
        INX     H
        MVI     M,000H
        INX     H
        MVI     M,TESTL1
        INX     H
        MVI     M,TESTL0
        INX     H
        MVI     M,001H          ; one 512-byte block
        INX     H
        MVI     M,000H
        RET

BUILD$WRITE$CDB:
        LXI     H,CDB
        MVI     M,00AH          ; WRITE(6)
        INX     H
        MVI     M,000H
        INX     H
        MVI     M,TESTL1
        INX     H
        MVI     M,TESTL0
        INX     H
        MVI     M,001H          ; one 512-byte block
        INX     H
        MVI     M,000H
        RET

;------------------------------------------------------------
; NCR5380/SCSI support routines
;------------------------------------------------------------
SCSI$INIT:
        XRA     A
        OUT     ICR
        OUT     TCR
        OUT     MR2
        OUT     SER
        RET

SCSI$SELECT:
        XRA     A
        OUT     ICR
        OUT     TCR
        OUT     MR2
SEL$WAIT:
        IN      CSB
        ANI     CSB$BSY
        JNZ     SEL$WAIT
        MVI     A,OURID+TGTID
        OUT     ODR
        MVI     A,ICR$DBUS+ICR$SEL
        OUT     ICR
        CALL    DELAY
SEL$BUSY:
        IN      CSB
        ANI     CSB$BSY
        JZ      SEL$BUSY
        XRA     A
        OUT     ICR
        RET

SCSI$SEND$CMD:
        MVI     A,PH$COMMAND
        OUT     TCR
SCMD$1:
        MOV     A,M
        CALL    SCSI$SEND$BYTE
        RNZ
        INX     H
        DCR     B
        JNZ     SCMD$1
        XRA     A
        RET

SCSI$SEND$DATA:
        MVI     A,PH$DATAOUT
        OUT     TCR
SDO$1:
        MOV     A,D
        ORA     E
        JZ      SDO$DONE
        MOV     A,M
        CALL    SCSI$SEND$BYTE
        RNZ
        INX     H
        DCX     D
        JMP     SDO$1
SDO$DONE:
        XRA     A
        RET

SCSI$RECV$DATA:
        MVI     A,PH$DATAIN
        OUT     TCR
SDI$1:
        MOV     A,D
        ORA     E
        JZ      SDI$DONE
        CALL    SCSI$RECV$BYTE
        MOV     M,A
        INX     H
        DCX     D
        JMP     SDI$1
SDI$DONE:
        XRA     A
        RET

; Data-out byte handshake, current experimental version.
SCSI$SEND$BYTE:
        OUT     ODR
        MVI     A,ICR$DBUS
        OUT     ICR
WSREQ$1:
        IN      CSB
        ANI     CSB$REQ
        JZ      WSREQ$1
        MVI     A,ICR$DBUS+ICR$ACK
        OUT     ICR
WSREQ$0:
        IN      CSB
        ANI     CSB$REQ
        JNZ     WSREQ$0
        XRA     A
        OUT     ICR
        RET

; Data-in byte handshake, using CSD and setup/hold NOPs.
SCSI$RECV$BYTE:
WRREQ$1:
        IN      CSB
        ANI     CSB$REQ
        JZ      WRREQ$1

        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP

        IN      CSD
        PUSH    PSW

        MVI     A,ICR$ACK
        OUT     ICR
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
WRREQ$0:
        IN      CSB
        ANI     CSB$REQ
        JNZ     WRREQ$0
        XRA     A
        OUT     ICR
        POP     PSW
        RET

SCSI$GET$STATUS:
        MVI     A,PH$STATUS
        OUT     TCR
        CALL    SCSI$RECV$BYTE
        STA     SCSISTAT
        MVI     A,PH$MSGIN
        OUT     TCR
        CALL    SCSI$RECV$BYTE
        STA     SCSIMSG
        LDA     SCSISTAT
        ORA     A
        JZ      SGOK
        MVI     A,1
        RET
SGOK:
        XRA     A
        RET

SCSI$WAIT$REQ$PHASE:
        LXI     H,0FFFFH
SWRP$1:
        IN      CSB
        ANI     CSB$REQ
        JNZ     SWRP$2
        DCX     H
        MOV     A,H
        ORA     L
        JNZ     SWRP$1
        MVI     A,1
        RET
SWRP$2:
        IN      CSB
        ANI     CSB$PHASE
        STA     LASTPHASE
        XRA     A
        RET

; TEST UNIT READY
SCSI$TEST$READY:
        MVI     A,20
        STA     TURTRIES
TUR$LOOP:
        CALL    BUILD$TUR$CDB
        CALL    SCSI$SELECT
        JNZ     TUR$FAIL
        LXI     H,CDB
        MVI     B,6
        CALL    SCSI$SEND$CMD
        JNZ     TUR$FAIL
        CALL    SCSI$WAIT$REQ$PHASE
        JNZ     TUR$FAIL
        LDA     LASTPHASE
        CPI     PHASE$STATUS
        JNZ     TUR$FAIL
        CALL    SCSI$GET$STATUS
        LDA     SCSISTAT
        ORA     A
        JZ      TUR$READY
TUR$FAIL:
        CALL    BIG$DELAY
        LDA     TURTRIES
        DCR     A
        STA     TURTRIES
        JNZ     TUR$LOOP
        MVI     A,1
        RET
TUR$READY:
        XRA     A
        RET

BUILD$TUR$CDB:
        LXI     H,CDB
        XRA     A
        MOV     M,A
        INX     H
        MOV     M,A
        INX     H
        MOV     M,A
        INX     H
        MOV     M,A
        INX     H
        MOV     M,A
        INX     H
        MOV     M,A
        RET

;------------------------------------------------------------
; Console output helpers
;------------------------------------------------------------
PSTRING:
        MVI     C,PRINTS
        CALL    BDOS
        RET

PCHAR:
        MOV     E,A
        MVI     C,PRINTC
        CALL    BDOS
        RET

PCRLF:
        MVI     A,0DH
        CALL    PCHAR
        MVI     A,0AH
        CALL    PCHAR
        RET

PHEX16:
        ; HL = word to print high byte then low byte
        MOV     A,H
        CALL    PHEX8
        MOV     A,L
        CALL    PHEX8
        RET

PHEX8:
        PUSH    PSW
        RRC
        RRC
        RRC
        RRC
        ANI     00FH
        CALL    PHEXNIB
        POP     PSW
        ANI     00FH
        CALL    PHEXNIB
        RET

PHEXNIB:
        CPI     10
        JC      PHN$1
        ADI     7
PHN$1:
        ADI     030H
        CALL    PCHAR
        RET

;------------------------------------------------------------
; Delays
;------------------------------------------------------------
DELAY:
        LXI     B,2000H
DLY$1:
        DCX     B
        MOV     A,B
        ORA     C
        JNZ     DLY$1
        RET

BIG$DELAY:
        MVI     B,20
BD$1:
        PUSH    B
        CALL    DELAY
        POP     B
        DCR     B
        JNZ     BD$1
        RET

;------------------------------------------------------------
; Messages and storage
;------------------------------------------------------------
MSG$HELLO:    DB 0DH,0AH,'NCR5380 RAW SCSI RW TEST, LBA 100',0DH,0AH,'$'
MSG$READY:    DB 'TEST UNIT READY OK',0DH,0AH,'$'
MSG$NOTREADY: DB 'TEST UNIT READY FAILED',0DH,0AH,'$'
MSG$WRITE:    DB 'WRITING 512 BYTE PATTERN...',0DH,0AH,'$'
MSG$READ:     DB 'READING BACK...',0DH,0AH,'$'
MSG$WRITEERR: DB 'WRITE COMMAND ERROR',0DH,0AH,'$'
MSG$READERR:  DB 'READ COMMAND ERROR',0DH,0AH,'$'
MSG$PASS:     DB 'PASS - 512 BYTES COMPARE OK',0DH,0AH,'$'
MSG$BAD:      DB 'VERIFY ERROR AT OFFSET $'
MSG$EXP:      DB ' EXPECTED $'
MSG$GOT:      DB ' GOT $'

; Variables
CDB:        DS  6
SCSISTAT:   DB  0
SCSIMSG:    DB  0
LASTPHASE:  DB  0
TURTRIES:   DB  0
TEMP$EXP:   DB  0
BAD$EXP:    DB  0
BAD$GOT:    DB  0
BAD$OFF:    DW  0
OFFLO:      DB  0
OFFHI:      DB  0
COUNT:      DW  0200H

; Buffers
WRBUF:      DS  512
RDBUF:      DS  512

STACK:      DS  80
STACKTOP:

        END     START
