;------------------------------------------------------------
; SCSITEST_8080.ASM
; Intel 8080 conversion of the supplied SCSITEST.Z80 source using Chat GPT.
;------------------------------------------------------------
;
; 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:
        LXI     SP,STACK

        LXI     H,DEFAULT_TRK
        SHLD    CURTRK
        LXI     H,DEFAULT_SEC
        SHLD    CURSEC

        CALL    CRLF
        LXI     H,MSG_TITLE
        CALL    PSTRING

MAIN_MENU:
        CALL    SHOW_POSITION
        LXI     H,MSG_MENU
        CALL    PSTRING

        CALL    CONSOLE_INPUT_UPPER
        CALL    ECHO_CHAR
        CALL    CRLF

        CPI     'R'
        JZ      DO_READ
        CPI     'W'
        JZ      DO_WRITE
        CPI     'T'
        JZ      SET_TRACK
        CPI     'S'
        JZ      SET_SECTOR
        CPI     'D'
        JZ      DO_DUMP
        CPI     'Q'
        JZ      EXIT_PROGRAM

        LXI     H,MSG_BAD
        CALL    PSTRING
        JMP     MAIN_MENU

EXIT_PROGRAM:
        LXI     H,MSG_EXIT
        CALL    PSTRING
        RET

DO_READ:
        CALL    SCSI_TEST_READY
        ORA     A
        JNZ     SCSI_ERROR

        CALL    MAKE_LBA
        CALL    SCSI_READ512
        ORA     A
        JNZ     SCSI_ERROR
        CALL    COPY_FROM_SECBUF
        LXI     H,MSG_READOK
        CALL    PSTRING
        CALL    DUMP_DMABUF
        JMP     MAIN_MENU

DO_WRITE:
        LXI     H,MSG_WARN1
        CALL    PSTRING
        CALL    CONSOLE_INPUT_UPPER
        CALL    ECHO_CHAR
        CALL    CRLF
        CPI     'Y'
        JNZ     MAIN_MENU

        CALL    SCSI_TEST_READY
        ORA     A
        JNZ     SCSI_ERROR

        CALL    MAKE_LBA
        CALL    MAKE_PATTERN
        CALL    COPY_TO_SECBUF_RMW
        ORA     A
        JNZ     SCSI_ERROR
        CALL    SCSI_WRITE512
        ORA     A
        JNZ     SCSI_ERROR

        LXI     H,MSG_WRITEOK
        CALL    PSTRING
        CALL    SCSI_READ512
        ORA     A
        JNZ     SCSI_ERROR
        CALL    COPY_FROM_SECBUF
        CALL    DUMP_DMABUF
        JMP     MAIN_MENU

SET_TRACK:
        LXI     H,MSG_TRACK
        CALL    PSTRING
        CALL    READ_HEX4
        SHLD    CURTRK
        CALL    CRLF
        JMP     MAIN_MENU

SET_SECTOR:
        LXI     H,MSG_SECTOR
        CALL    PSTRING
        CALL    READ_HEX2
        MOV     L,A
        MVI     H,0
        SHLD    CURSEC
        CALL    CRLF
        JMP     MAIN_MENU

DO_DUMP:
        LXI     H,MSG_DUMP
        CALL    PSTRING
        CALL    DUMP_DMABUF
        JMP     MAIN_MENU

SCSI_ERROR:
        LXI     H,MSG_SCSIERR
        CALL    PSTRING
        JMP     MAIN_MENU

MAKE_LBA:
        LHLD    CURSEC
        DCX     H
        MOV     A,L
        ANI     03H
        CPI     00H
        JNZ     MO1
        XRA     A
        STA     SECOFFL
        STA     SECOFFH
        JMP     MOX
MO1:
        CPI     01H
        JNZ     MO2
        MVI     A,080H
        STA     SECOFFL
        XRA     A
        STA     SECOFFH
        JMP     MOX
MO2:
        CPI     02H
        JNZ     MO3
        XRA     A
        STA     SECOFFL
        MVI     A,01H
        STA     SECOFFH
        JMP     MOX
MO3:
        MVI     A,080H
        STA     SECOFFL
        MVI     A,01H
        STA     SECOFFH
MOX:
        MOV     A,L
        ORA     A       ; clear CY before logical shift right
        RAR
        ORA     A       ; clear CY before logical shift right
        RAR
        ANI     0FH
        MOV     E,A
        MVI     D,0
        LHLD    CURTRK
        DAD     H
        DAD     H
        DAD     H
        DAD     H
        DAD     D
        MOV     A,L
        STA     LBA0
        MOV     A,H
        STA     LBA1
        XRA     A
        STA     LBA2
        RET

COPY_FROM_SECBUF:
        LXI     H,SECBUF
        LDA     SECOFFL
        MOV     E,A
        LDA     SECOFFH
        MOV     D,A
        DAD     D
        LXI     D,DMABUF
        LXI     B,128
        CALL    MOVE_BC
        RET

COPY_TO_SECBUF_RMW:
        CALL    SCSI_READ512
        ORA     A
        RNZ
        LXI     H,DMABUF
        LXI     D,SECBUF
        LDA     SECOFFL
        MOV     C,A
        LDA     SECOFFH
        MOV     B,A
        XCHG
        DAD     B
        XCHG
        LXI     B,128
        CALL    MOVE_BC
        XRA     A
        RET

MAKE_PATTERN:
        LXI     H,DMABUF
        MVI     B,128
        MVI     A,00H
MP1:
        MOV     M,A
        INR     A
        INX     H
        DCR     B
        JNZ     MP1
        LXI     H,DMABUF
        MVI     M,'S'
        INX     H
        MVI     M,'C'
        INX     H
        MVI     M,'S'
        INX     H
        MVI     M,'I'
        INX     H
        MVI     M,'-'
        INX     H
        MVI     M,'T'
        INX     H
        MVI     M,'E'
        INX     H
        MVI     M,'S'
        INX     H
        MVI     M,'T'
        RET

SCSI_READ512:
        CALL    BUILD_READ_CDB
        CALL    SCSI_SELECT
        RNZ
                                           
        LXI     H,CDB
        MVI     B,6
        CALL    SCSI_SEND_CMD
        RNZ
                                           
;       Do not assume DATA IN.             
;       First see what phase the drive is asking for.
                                           
        CALL    SCSI_WAIT_REQ_PHASE
        RNZ
                                           
        LDA     LASTPHASE
        CPI     PHASE_DATAIN
        JZ      SRD_DATAIN
                                           
        CPI     PHASE_STATUS
        JZ      SRD_STATUS_ONLY
                                           
;       Unexpected phase                   
        MVI     A,'P'
        CALL    ZCO
        LDA     LASTPHASE
        CALL    HEXOUT
        MVI     A,1
        RET
SCSI_WRITE512:
        CALL    BUILD_WRITE_CDB
        CALL    SCSI_SELECT
        RNZ
        LXI     H,CDB
        MVI     B,6
        CALL    SCSI_SEND_CMD
        RNZ
        LXI     H,SECBUF
        LXI     D,512
        CALL    SCSI_SEND_DATA
        RNZ
        CALL    SCSI_GET_STATUS
        RET

SRD_DATAIN:
        LXI     H,SECBUF
        LXI     D,512
        CALL    SCSI_RECV_DATA
        RNZ
                                         
        CALL    SCSI_GET_STATUS
        RET
                                           
SRD_STATUS_ONLY:
;       Drive gave status instead of data, probably CHECK CONDITION
        CALL    SCSI_GET_STATUS
        MVI     A,1
        RET




BUILD_READ_CDB:
        LXI     H,CDB
        MVI     M,08H
        INX     H
        LDA     LBA2
        ANI     1FH
        MOV     M,A
        INX     H
        LDA     LBA1
        MOV     M,A
        INX     H
        LDA     LBA0
        MOV     M,A
        INX     H
        MVI     M,01H
        INX     H
        MVI     M,00H
        RET

BUILD_WRITE_CDB:
        LXI     H,CDB
        MVI     M,0AH
        INX     H
        LDA     LBA2
        ANI     1FH
        MOV     M,A
        INX     H
        LDA     LBA1
        MOV     M,A
        INX     H
        LDA     LBA0
        MOV     M,A
        INX     H
        MVI     M,01H
        INX     H
        MVI     M,00H
        RET

SCSI_SELECT:
        XRA     A
        OUT     ICR
        OUT     TCR
        OUT     MR2
SELWAIT:
        IN      CSB
        ANI     CSB_BSY
        JNZ     SELWAIT
        
        MVI     A,OURID+TGTID
        OUT     ODR
        MVI     A,ICR_DBUS+ICR_SEL
        OUT     ICR
        CALL    DELAY
SELBUSY:
        IN      CSB
        ANI     CSB_BSY
        JZ      SELBUSY
        XRA     A
        OUT     ICR
        XRA     A
        RET

SCSI_SEND_CMD:
        MVI     A,PH_COMMAND
        OUT     TCR
SCMD1:
        MOV     A,M
        CALL    SCSI_SEND_BYTE
        RNZ
        INX     H
        DCR     B
        JNZ     SCMD1
        XRA     A
        RET

SCSI_SEND_DATA:
        MVI     A,PH_DATAOUT
        OUT     TCR
SDO1:
        MOV     A,D
        ORA     E
        JZ      SDO_DONE
        MOV     A,M
        CALL    SCSI_SEND_BYTE
        RNZ
        INX     H
        DCX     D
        JMP     SDO1
SDO_DONE:
        XRA     A
        RET

SCSI_RECV_DATA:
        MVI     A,PH_DATAIN
        OUT     TCR
SDI1:
        MOV     A,D
        ORA     E
        JZ      SDI_DONE
        CALL    SCSI_RECV_BYTE
        MOV     M,A
        INX     H
        DCX     D
        JMP     SDI1
SDI_DONE:
        XRA     A
        RET

SCSI_SEND_BYTE:
        OUT     ODR
WSREQ1:
        IN      CSB
        ANI     CSB_REQ
        JZ      WSREQ1
        MVI     A,ICR_DBUS
        OUT     ICR
        MVI     A,ICR_DBUS+ICR_ACK
        OUT     ICR
WSREQ0:
        IN      CSB
        ANI     CSB_REQ
        JNZ     WSREQ0
        XRA     A
        OUT     ICR
        XRA     A
        RET

SCSI_RECV_BYTE:
WRREQ1:
        IN      CSB
        ANI     CSB_REQ
        JZ      WRREQ1
        IN      CSD
        PUSH    PSW
        MVI     A,ICR_ACK
        OUT     ICR
WRREQ0:
        IN      CSB
        ANI     CSB_REQ
        JNZ     WRREQ0
        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
                                             
SWRP1:
        IN      CSB
        ANI     CSB_REQ
        JNZ     SWRP2

        DCX     H
        MOV     A,H
        ORA     L
        JNZ     SWRP1
                                             
        MVI     A,'!'
        CALL    ZCO
        MVI     A,1
        RET

SWRP2:
        IN      CSB
        ANI     CSB_PHASE
        STA     LASTPHASE
        XRA     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:
        MVI     A,40
        STA     TURTRIES

TUR_LOOP:
        CALL    BUILD_TUR_CDB
        CALL    SCSI_SELECT
        JNZ     TUR_FAIL_ONE

        LXI     H,CDB
        MVI     B,6
        CALL    SCSI_SEND_CMD
        JNZ     TUR_FAIL_ONE

        CALL    SCSI_WAIT_REQ_PHASE
        JNZ     TUR_FAIL_ONE

        LDA     LASTPHASE
        CPI     PHASE_STATUS
        JNZ     TUR_FAIL_ONE

        CALL    SCSI_GET_STATUS

        LDA     SCSISTAT
        ORA     A
        JZ      TUR_READY

;       Not ready or unit attention. Read sense to clear it.
        CALL    SCSI_REQUEST_SENSE

TUR_FAIL_ONE:
        CALL    BIG_DELAY
                                              
        LXI     H,TURTRIES
        DCR     M
        JNZ     TUR_LOOP
                                             
        MVI     A,1
        RET

TUR_READY:
        XRA     A
        RET


BUILD_TUR_CDB:
        LXI     H,CDB
        XRA     A
        MOV     M,A ; 00H = TEST UNIT READY
        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


BUILD_SENSE_CDB:
        LXI     H,CDB
        MVI     M,03H ; REQUEST SENSE  
        INX     H
        XRA     A
        MOV     M,A
        INX     H
        MOV     M,A
        INX     H
        MOV     M,A
        INX     H
        MVI     M,18 ; request 18 bytes
        INX     H
        MOV     M,A
        RET

                                              
SCSI_REQUEST_SENSE:
        CALL    BUILD_SENSE_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      SRS_DATA
                                              
        CPI     PHASE_STATUS
        JZ      SRS_STATUS

        MVI     A,1
        RET

SRS_DATA:
        LXI     H,SENSEBUF
        LXI     D,18
        CALL    SCSI_RECV_DATA
        RNZ

SRS_STATUS:
        CALL    SCSI_GET_STATUS
        XRA     A
        RET


BIG_DELAY:
        MVI     B,20
BD1:
        PUSH    B
        CALL    DELAY
        POP     B
        DCR     B
        JNZ     BD1
        RET



;------------------------------------------------------------
; MOVE_BC
; 8080 replacement for Z80 LDIR.
; Copies BC bytes from (HL) to (DE).
; On entry: HL=source, DE=destination, BC=count.
;------------------------------------------------------------
MOVE_BC:
        MOV     A,B
        ORA     C
        RZ
MOVE1:
        MOV     A,M
        STAX    D
        INX     H
        INX     D
        DCX     B
        MOV     A,B
        ORA     C
        JNZ     MOVE1
        RET


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SUPPORT ROUTINES ;;;;;;;;;;;;;;;;;;;;;;;;;;;
ZCI:
        IN      CONSOLE_STATUS
        ANI     02H
        JZ      ZCI
        IN      CONSOLE_DATA
        ANI     7FH
        RET

ZCO:
        PUSH    PSW
ZCO1:
        IN      CONSOLE_STATUS
        ANI     04H
        JZ      ZCO1
        POP     PSW
        OUT     CONSOLE_DATA
        RET


ZCSTS:
        IN      CONSOLE_STATUS
        ANI     02H
        JZ      NOCHAR
GOT_CHAR:
        XRA     A
        DCR     A
        RET
NOCHAR:
        XRA     A
        RET

CONSOLE_INPUT_UPPER:
        CALL    ZCI
        CPI     'a'
        RC
        CPI     'z'+1
        RNC
        SUI     20H
        RET

ECHO_CHAR:
        CALL    ZCO
        RET

PSTRING:
        MOV     A,M
        CPI     '$'
        RZ
        CALL    ZCO
        INX     H
        JMP     PSTRING

CRLF:
        PUSH    PSW
        MVI     A,13
        CALL    ZCO
        MVI     A,10
        CALL    ZCO
        POP     PSW
        RET

SPACE:
        MVI     A,' '
        CALL    ZCO
        RET

READ_HEX4:
        LXI     H,0
        MVI     B,4
RH4_1:
        CALL    READ_HEX_NIBBLE
        DAD     H
        DAD     H
        DAD     H
        DAD     H
        MOV     E,A
        MVI     D,0
        DAD     D
        DCR     B
        JNZ     RH4_1
        RET

READ_HEX2:
        CALL    READ_HEX_NIBBLE
        RLC
        RLC
        RLC
        RLC
        MOV     B,A
        CALL    READ_HEX_NIBBLE
        ORA     B
        RET

READ_HEX_NIBBLE:
        CALL    CONSOLE_INPUT_UPPER
        CALL    ECHO_CHAR
        CPI     '0'
        JC      READ_HEX_NIBBLE
        CPI     '9'+1
        JC      RHN_DIGIT
        CPI     'A'
        JC      READ_HEX_NIBBLE
        CPI     'F'+1
        JNC     READ_HEX_NIBBLE
        SUI     'A'-10
        RET
RHN_DIGIT:
        SUI     '0'
        RET

HEXOUT:
        PUSH    PSW
        RRC
        RRC
        RRC
        RRC
        CALL    HEXNIB
        POP     PSW
        CALL    HEXNIB
        RET

HEXNIB:
        ANI     0FH
        CPI     10
        JC      HEXDIG
        ADI     7
HEXDIG:
        ADI     '0'
        CALL    ZCO
        RET

PRINT_HL_HEX:
        MOV     A,H
        CALL    HEXOUT
        MOV     A,L
        CALL    HEXOUT
        RET

PRINT_A_HEX:
        CALL    HEXOUT
        RET

SHOW_POSITION:
        CALL    CRLF
        LXI     H,MSG_POS1
        CALL    PSTRING
        LHLD    CURTRK
        CALL    PRINT_HL_HEX
        LXI     H,MSG_POS2
        CALL    PSTRING
        LHLD    CURSEC
        MOV     A,L
        CALL    PRINT_A_HEX
        CALL    CRLF
        RET

DUMP_DMABUF:
        LXI     H,DMABUF
        MVI     B,8
DUMPLINE:
        PUSH    B
        MVI     B,16
DUMPBYTE:
        MOV     A,M
        CALL    HEXOUT
        CALL    SPACE
        INX     H
        DCR     B
        JNZ     DUMPBYTE
        CALL    CRLF
        POP     B
        DCR     B
        JNZ     DUMPLINE
        RET

DELAY:
        LXI     B,2000H
DLY1:
        DCX     B
        MOV     A,B
        ORA     C
        JNZ     DLY1
        RET

MSG_TITLE:
        DB      'NCR5380 SCSI SECTOR TEST (Using non-DMA IO mode,8080 Code)',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