You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1469 lines
29 KiB

;
;=============================================================================
; IMM DISK DRIVER
;=============================================================================
;
; PARALLEL PORT INTERFACE FOR SCSI DISK DEVICES USING A PARALLEL PORT
; ADAPTER. PRIMARILY TARGETS PARALLEL PORT IOMEGA ZIP DRIVES.
;
; CURRENTLY CODED SPECIFICALLY FOR RCBUS MG014 PARALLEL PORT HARDWARE.
; INTENDED TO CO-EXIST WITH LPT DRIVER.
;
; CREATED BY WAYNE WARTHEN FOR ROMWBW HBIOS.
; MUCH OF THE CODE IS DERIVED FROM FUZIX (ALAN COX).
;
; 5/23/2023 WBW - INITIAL RELEASE
;
;=============================================================================
;
; MG014 STYLE INTERFACE (USED BY RCBUS MG014 MODULE):
;
; PORT 0 (INPUT/OUTPUT):
;
; D7 D6 D5 D4 D3 D2 D1 D0
; +-------+-------+-------+-------+-------+-------+-------+-------+
; | PD7 | PD6 | PD5 | PD4 | PD3 | PD2 | PD1 | PD0 |
; +-------+-------+-------+-------+-------+-------+-------+-------+
;
; PORT 1 (INPUT):
;
; D7 D6 D5 D4 D3 D2 D1 D0
; +-------+-------+-------+-------+-------+-------+-------+-------+
; | | | | /ERR | SEL | POUT | BUSY | /ACK |
; +-------+-------+-------+-------+-------+-------+-------+-------+
;
; PORT 2 (INPUT/OUTPUT):
;
; D7 D6 D5 D4 D3 D2 D1 D0
; +-------+-------+-------+-------+-------+-------+-------+-------+
; | LED | | | | /SEL | /RES | /LF | /STB |
; +-------+-------+-------+-------+-------+-------+-------+-------+
;
;=============================================================================
;
; TODO:
;
; - TIMEOUT IS INFINITE!!!
;
; - ASSIGN DRIVE LETTERS EVEN WHEN NO MEDIA AT BOOT
;
; - OPTIMIZE READ/WRITE LOOPS
;
; - COMMENT MORE OF THE CODE
;
; - THERE ARE CURRENTLY NO BUFFER OVERRUN CHECKS. WE ASSUME SCSI
; DEVICES WILL SEND/REQUEST THE EXPECTED NUMBER OF BYTES.
;
; - MAKE THE MG014 STATUS LED DO SOMETHING USEFUL?
;
; NOTES:
;
; - THIS DRIVER IS FOR THE ZIP DRIVE EMM INTERFACE. IT WILL SIMPLY
; FAIL TO EVEN RECOGNIZE A ZIP DRIVE WITH THE OLDER PPA INTERFACE.
; THERE DOES NOT SEEM TO BE A WAY TO VISUALLY DETERMINE IF A ZIP
; DRIVE IS EMM OR PPA. SIGH.
;
; - THERE ARE SOME HARD CODED TIMEOUT LOOPS IN THE CODE. THEY ARE
; WORKING OK ON A 7 MHZ Z80. THEY ARE LIKELY TO NEED TWEAKING ON
; FASTER CPUS.
;
; - THIS DRIVER OPERATES PURELY IN NIBBLE MODE. I SUSPECT IT IS
; POSSIBLE TO USE FULL BYTE MODE (PS2 STYLE), BUT I HAVE NOT
; ATTEMPTED IT.
;
; - RELATIVE TO ABOVE, THIS BEAST IS SLOW. IN ADDITION TO THE
; NIBBLE MODE READS, THE MG014 ASSIGNS SIGNALS DIFFERENTLY THAN
; THE STANDARD IBM PARALLEL PORT WHICH NECESSITATES A BUNCH OF EXTRA
; BIT FIDDLING ON EVERY READ.
;
; IMM PORT OFFSETS
;
IMM_IODATA .EQU 0 ; PORT A, DATA, OUT
IMM_IOSTAT .EQU 1 ; PORT B, STATUS, IN
IMM_IOCTRL .EQU 2 ; PORT C, CTRL, OUT
IMM_IOSETUP .EQU 3 ; PPI SETUP
;
; SCSI UNIT IDS
;
IMM_SELF .EQU 7
IMM_TGT .EQU 6
;
; SCSI COMMAND CODES
;
SCSI_CMD_READ .EQU $08
SCSI_CMD_INQ .EQU $12
SCSI_CMD_TEST .EQU $00
SCSI_CMD_START .EQU $1B
SCSI_CMD_SENSE .EQU $03
SCSI_CMD_WRITE .EQU $0A
SCSI_CMD_RDCAP .EQU $25
;
; IMM DEVICE STATUS
;
IMM_STOK .EQU 0
IMM_STINVUNIT .EQU -1
IMM_STNOMEDIA .EQU -2
IMM_STCMDERR .EQU -3
IMM_STIOERR .EQU -4
IMM_STTO .EQU -5
IMM_STNOTSUP .EQU -6
;
; IMM DEVICE CONFIGURATION
;
IMM_CFGSIZ .EQU 12 ; SIZE OF CFG TBL ENTRIES
;
; PER DEVICE DATA OFFSETS IN CONFIG TABLE ENTRIES
;
IMM_DEV .EQU 0 ; OFFSET OF DEVICE NUMBER (BYTE)
IMM_MODE .EQU 1 ; OPERATION MODE: IMM MODE (BYTE)
IMM_STAT .EQU 2 ; LAST STATUS (BYTE)
IMM_IOBASE .EQU 3 ; IO BASE ADDRESS (BYTE)
IMM_MEDCAP .EQU 4 ; MEDIA CAPACITY (DWORD)
IMM_LBA .EQU 8 ; OFFSET OF LBA (DWORD)
;
;=============================================================================
; INITIALIZATION ENTRY POINT
;=============================================================================
;
IMM_INIT:
LD IY,IMM_CFG ; POINT TO START OF CONFIG TABLE
;
IMM_INIT1:
LD A,(IY) ; LOAD FIRST BYTE TO CHECK FOR END
CP $FF ; CHECK FOR END OF TABLE VALUE
JR NZ,IMM_INIT2 ; IF NOT END OF TABLE, CONTINUE
XOR A ; SIGNAL SUCCESS
RET ; AND RETURN
;
IMM_INIT2:
CALL NEWLINE ; FORMATTING
PRTS("IMM:$") ; DRIVER LABEL
;
PRTS(" IO=0x$") ; LABEL FOR IO ADDRESS
LD A,(IY+IMM_IOBASE) ; GET IO BASE ADDRES
CALL PRTHEXBYTE ; DISPLAY IT
;
PRTS(" MODE=$") ; LABEL FOR MODE
LD A,(IY+IMM_MODE) ; GET MODE BITS
LD DE,IMM_STR_MODE_NONE ; MODE LABEL
CP IMMMODE_NONE ; TEST FOR MODE
JR Z,IMM_INIT3 ; IF SO, DISPLAY IT
LD DE,IMM_STR_MODE_MG014 ; MODE LABEL
CP IMMMODE_MG014 ; TEST FOR MODE
JR Z,IMM_INIT3 ; IF SO, DISPLAY IT
LD DE,IMM_STR_MODE_UNK ; MODE LABEL
IMM_INIT3:
CALL WRITESTR ; DISPLAY MODE
;
; CHECK FOR HARDWARE PRESENCE
CALL IMM_DETECT ; PROBE FOR INTERFACE
JR Z,IMM_INIT4 ; IF FOUND, CONTINUE
CALL PC_SPACE ; FORMATTING
LD DE,IMM_STR_NOHW ; NO IMM MESSAGE
CALL WRITESTR ; DISPLAY IT
JR IMM_INIT6 ; SKIP CFG ENTRY
;
IMM_INIT4:
; UPDATE DRIVER RELATIVE UNIT NUMBER IN CONFIG TABLE
LD A,(IMM_DEVNUM) ; GET NEXT UNIT NUM TO ASSIGN
LD (IY+IMM_DEV),A ; UPDATE IT
INC A ; BUMP TO NEXT UNIT NUM TO ASSIGN
LD (IMM_DEVNUM),A ; SAVE IT
;
; ADD UNIT TO GLOBAL DISK UNIT TABLE
LD BC,IMM_FNTBL ; BC := FUNC TABLE ADR
PUSH IY ; CFG ENTRY POINTER
POP DE ; COPY TO DE
CALL DIO_ADDENT ; ADD ENTRY TO GLOBAL DISK DEV TABLE
;
CALL IMM_RESET ; RESET/INIT THE INTERFACE
;
; START PRINTING DEVICE INFO
CALL IMM_PRTPREFIX ; PRINT DEVICE PREFIX
;
; CHECK FOR BAD STATUS
LD A,(IY+IMM_STAT) ; GET STATUS
OR A ; SET FLAGS
JP Z,IMM_INIT5 ; CONTINUE
CALL PC_SPACE ; FORMATTING
CALL IMM_PRTSTATSTR ; PRINT STATUS STRING
JR IMM_INIT6 ; LOOP TILL DONE
;
IMM_INIT5:
; PRINT STORAGE CAPACITY (BLOCK COUNT)
PRTS(" BLOCKS=0x$") ; PRINT FIELD LABEL
LD A,IMM_MEDCAP ; OFFSET TO CAPACITY FIELD
CALL LDHLIYA ; HL := IY + A, REG A TRASHED
CALL LD32 ; GET THE CAPACITY VALUE
CALL PRTHEX32 ; PRINT HEX VALUE
;
; PRINT STORAGE SIZE IN MB
PRTS(" SIZE=$") ; PRINT FIELD LABEL
LD B,11 ; 11 BIT SHIFT TO CONVERT BLOCKS --> MB
CALL SRL32 ; RIGHT SHIFT
CALL PRTDEC32 ; PRINT DWORD IN DECIMAL
PRTS("MB$") ; PRINT SUFFIX
;
IMM_INIT6:
LD DE,IMM_CFGSIZ ; SIZE OF CFG TABLE ENTRY
ADD IY,DE ; BUMP POINTER
JP IMM_INIT1 ; AND LOOP
;
;----------------------------------------------------------------------
; PROBE FOR IMM HARDWARE
;----------------------------------------------------------------------
;
; ON RETURN, ZF SET INDICATES HARDWARE FOUND
;
IMM_DETECT:
; INITIALIZE 8255
LD A,(IY+IMM_IOBASE) ; BASE PORT
ADD A,IMM_IOSETUP ; BUMP TO SETUP PORT
LD C,A ; MOVE TO C FOR I/O
LD A,$82 ; CONFIG A OUT, B IN, C OUT
OUT (C),A ; DO IT
CALL DELAY
;
CALL IMM_DISCONNECT
CALL IMM_CONNECT
CALL IMM_DISCONNECT
;
; EXPECTING S1=$B8, S2=$18, S3=$38
LD A,(IMM_S1)
CP $B8
RET NZ
LD A,(IMM_S2)
CP $18
RET NZ
LD A,(IMM_S3)
CP $38
RET NZ
;
XOR A
RET
;
;=============================================================================
; DRIVER FUNCTION TABLE
;=============================================================================
;
IMM_FNTBL:
.DW IMM_STATUS
.DW IMM_RESET
.DW IMM_SEEK
.DW IMM_READ
.DW IMM_WRITE
.DW IMM_VERIFY
.DW IMM_FORMAT
.DW IMM_DEVICE
.DW IMM_MEDIA
.DW IMM_DEFMED
.DW IMM_CAP
.DW IMM_GEOM
#IF (($ - IMM_FNTBL) != (DIO_FNCNT * 2))
.ECHO "*** INVALID IMM FUNCTION TABLE ***\n"
#ENDIF
;
IMM_VERIFY:
IMM_FORMAT:
IMM_DEFMED:
SYSCHKERR(ERR_NOTIMPL) ; NOT IMPLEMENTED
RET
;
;
;
IMM_READ:
CALL HB_DSKREAD
LD A,SCSI_CMD_READ
LD (IMM_CMD_RW),A
JP IMM_IO
;
;
;
IMM_WRITE:
CALL HB_DSKREAD
LD A,SCSI_CMD_WRITE
LD (IMM_CMD_RW),A
JP IMM_IO
;
;
;
IMM_IO:
LD (IMM_DSKBUF),HL ; SAVE DISK BUFFER ADDRESS
CALL IMM_CHKERR ; CHECK FOR ERR STATUS AND RESET IF SO
JR NZ,IMM_IO3 ; BAIL OUT ON ERROR
;
; SETUP LBA
; 3 BYTES, LITTLE ENDIAN -> BIG ENDIAN
LD HL,IMM_CMD_RW+1 ; START OF LBA FIELD IN CDB (MSB)
LD A,(IY+IMM_LBA+2) ; THIRD BYTE OF LBA FIELD IN CFG (MSB)
LD (HL),A
INC HL
LD A,(IY+IMM_LBA+1)
LD (HL),A
INC HL
LD A,(IY+IMM_LBA+0)
LD (HL),A
INC HL
;
; DO SCSI IO
LD DE,(IMM_DSKBUF)
LD BC,512
LD HL,IMM_CMD_RW
CALL IMM_RUNCMD
CALL Z,IMM_CHKCMD
JR NZ,IMM_IO2 ; IF ERROR, SKIP INCREMENT
; INCREMENT LBA
LD A,IMM_LBA ; LBA OFFSET
CALL LDHLIYA ; HL := IY + A, REG A TRASHED
CALL INC32HL ; INCREMENT THE VALUE
; INCREMENT DMA
LD HL,IDE_DSKBUF+1 ; POINT TO MSB OF BUFFER ADR
INC (HL) ; BUMP DMA BY
INC (HL) ; ... 512 BYTES
XOR A ; SIGNAL SUCCESS
;
IMM_IO2:
IMM_IO3:
LD HL,(IMM_DSKBUF) ; CURRENT DMA TO HL
OR A ; SET FLAGS BASED ON RETURN CODE
RET Z ; RETURN IF SUCCESS
LD A,ERR_IO ; SIGNAL IO ERROR
OR A ; SET FLAGS
RET ; AND DONE
;
;
;
IMM_STATUS:
; RETURN UNIT STATUS
LD A,(IY+IMM_STAT) ; GET STATUS OF SELECTED DEVICE
OR A ; SET FLAGS
RET ; AND RETURN
;
;
;
IMM_RESET:
JP IMM_INITDEV
;
;
;
IMM_DEVICE:
LD D,DIODEV_IMM ; D := DEVICE TYPE
LD E,(IY+IMM_DEV) ; E := PHYSICAL DEVICE NUMBER
LD C,%01000000 ; C := REMOVABLE HARD DISK
LD H,(IY+IMM_MODE) ; H := MODE
LD L,(IY+IMM_IOBASE) ; L := BASE I/O ADDRESS
XOR A ; SIGNAL SUCCESS
RET
;
; IMM_GETMED
;
IMM_MEDIA:
LD A,E ; GET FLAGS
OR A ; SET FLAGS
JR Z,IMM_MEDIA1 ; JUST REPORT CURRENT STATUS AND MEDIA
;
CALL IMM_RESET ; RESET IMM INTERFACE
;
IMM_MEDIA1:
LD A,(IY+IMM_STAT) ; GET STATUS
OR A ; SET FLAGS
LD D,0 ; NO MEDIA CHANGE DETECTED
LD E,MID_HD ; ASSUME WE ARE OK
RET Z ; RETURN IF GOOD INIT
LD E,MID_NONE ; SIGNAL NO MEDIA
LD A,ERR_NOMEDIA ; NO MEDIA ERROR
OR A ; SET FLAGS
RET ; AND RETURN
;
;
;
IMM_SEEK:
BIT 7,D ; CHECK FOR LBA FLAG
CALL Z,HB_CHS2LBA ; CLEAR MEANS CHS, CONVERT TO LBA
RES 7,D ; CLEAR FLAG REGARDLESS (DOES NO HARM IF ALREADY LBA)
LD (IY+IMM_LBA+0),L ; SAVE NEW LBA
LD (IY+IMM_LBA+1),H ; ...
LD (IY+IMM_LBA+2),E ; ...
LD (IY+IMM_LBA+3),D ; ...
XOR A ; SIGNAL SUCCESS
RET ; AND RETURN
;
;
;
IMM_CAP:
LD A,(IY+IMM_STAT) ; GET STATUS
PUSH AF ; SAVE IT
LD A,IMM_MEDCAP ; OFFSET TO CAPACITY FIELD
CALL LDHLIYA ; HL := IY + A, REG A TRASHED
CALL LD32 ; GET THE CURRENT CAPACITY INTO DE:HL
LD BC,512 ; 512 BYTES PER BLOCK
POP AF ; RECOVER STATUS
OR A ; SET FLAGS
RET
;
;
;
IMM_GEOM:
; FOR LBA, WE SIMULATE CHS ACCESS USING 16 HEADS AND 16 SECTORS
; RETURN HS:CC -> DE:HL, SET HIGH BIT OF D TO INDICATE LBA CAPABLE
CALL IMM_CAP ; GET TOTAL BLOCKS IN DE:HL, BLOCK SIZE TO BC
LD L,H ; DIVIDE BY 256 FOR # TRACKS
LD H,E ; ... HIGH BYTE DISCARDED, RESULT IN HL
LD D,16 | $80 ; HEADS / CYL = 16, SET LBA CAPABILITY BIT
LD E,16 ; SECTORS / TRACK = 16
RET ; DONE, A STILL HAS IMM_CAP STATUS
;
;=============================================================================
; FUNCTION SUPPORT ROUTINES
;=============================================================================
;
IMM_WRITEDATA:
LD C,(IY+IMM_IOBASE)
OUT (C),A
CALL DELAY
RET
;
;
;
IMM_WRITECTRL:
XOR $0B
LD C,(IY+IMM_IOBASE)
INC C
INC C
OUT (C),A
CALL DELAY
RET
;
;
;
IMM_READSTATUS:
LD C,(IY+IMM_IOBASE)
INC C
IN A,(C)
LD C,0 ; INIT RESULT
;
BIT 0,A ; ACK = $01
JR Z,IMM_READSTATUS1
SET 6,C ; $40
;
IMM_READSTATUS1:
;
BIT 1,A ; BUSY = $02
JR NZ,IMM_READSTATUS2
SET 7,C ; $80
;
IMM_READSTATUS2:
;
BIT 2,A ; PAPER = $04
JR Z,IMM_READSTATUS3
SET 5,C ; $20
;
IMM_READSTATUS3:
;
BIT 3,A ; SELECT = $08
JR Z,IMM_READSTATUS4
SET 4,C ; $10
;
IMM_READSTATUS4:
;
BIT 4,A ; FAULT = $10
JR Z,IMM_READSTATUS5
SET 3,C ; $08
;
IMM_READSTATUS5:
LD A,C
RET
;
;
;
IMM_CPP:
PUSH AF
LD A,$0C
CALL IMM_WRITECTRL
LD A,$AA
CALL IMM_WRITEDATA
LD A,$55
CALL IMM_WRITEDATA
LD A,$00
CALL IMM_WRITEDATA
LD A,$FF
CALL IMM_WRITEDATA
CALL IMM_READSTATUS
AND $B8
LD (IMM_S1),A
LD A,$87
CALL IMM_WRITEDATA
CALL IMM_READSTATUS
AND $B8
LD (IMM_S2),A
LD A,$78
CALL IMM_WRITEDATA
CALL IMM_READSTATUS
AND $38
LD (IMM_S3),A
POP AF
CALL IMM_WRITEDATA
LD A,$0C
CALL IMM_WRITECTRL
LD A,$0D
CALL IMM_WRITECTRL
LD A,$0C
CALL IMM_WRITECTRL
LD A,$FF
CALL IMM_WRITEDATA
;
; CONNECT: S1=$B8 S2=$18 S3=$30
; DISCONNECT: S1=$B8 S2=$18 S3=$38
;
#IF (IMMTRACE >= 2)
PRTS("\r\nCPP: S1=$")
LD A,(IMM_S1)
CALL PRTHEXBYTE
PRTS(" S2=$")
LD A,(IMM_S2)
CALL PRTHEXBYTE
PRTS(" S3=$")
LD A,(IMM_S3)
CALL PRTHEXBYTE
#ENDIF
;
XOR A ; ASSUME SUCCESS FOR NOW
RET
;
IMM_S1 .DB 0
IMM_S2 .DB 0
IMM_S3 .DB 0
;
;
;
IMM_CONNECT:
LD A,$E0
CALL IMM_CPP
LD A,$30
CALL IMM_CPP
LD A,$E0
CALL IMM_CPP
RET
;
;
;
IMM_DISCONNECT:
LD A,$30
CALL IMM_CPP
RET
;
;
;
IMM_RESETPULSE:
LD A,$04
CALL IMM_WRITECTRL
LD A,$40
CALL IMM_WRITEDATA
CALL DELAY ; 16 US, IDEALLY, 1 US
LD A,$0C
CALL IMM_WRITECTRL
LD A,$0D
CALL IMM_WRITECTRL
CALL DELAY ; 48 US, IDEALLY, 50 US
CALL DELAY
CALL DELAY
LD A,$0C
CALL IMM_WRITECTRL
LD A,$04
CALL IMM_WRITECTRL
RET
;
;
;
IMM_SELECT:
#IF IMMTRACE >= 2)
PRTS("\r\nSELECT: $")
#ENDIF
LD A,$0C
CALL IMM_WRITECTRL
;
LD HL,500 ; TIMEOUT COUNTER
;
IMM_SELECT1:
CALL IMM_READSTATUS
AND $08
JR Z,IMM_SELECT2 ; IF CLEAR, MOVE ON
DEC HL
LD A,H
OR L
JP Z,IMM_CMD_TIMEOUT ; TIMEOUT
JR IMM_SELECT1
;
IMM_SELECT2:
LD A,$04
CALL IMM_WRITECTRL
LD A,$80 | (1 << IMM_TGT)
CALL IMM_WRITEDATA
CALL DELAY
LD A,$0C
CALL IMM_WRITECTRL
;
#IF IMMTRACE >= 2)
CALL IMM_READSTATUS
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
;
LD A,$0D
CALL IMM_WRITECTRL
;
#IF IMMTRACE >= 2)
CALL IMM_READSTATUS
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
;
LD HL,500 ; TIMEOUT COUNTER
;
IMM_SELECT3:
CALL IMM_READSTATUS
#IF IMMTRACE >= 2)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
AND $08
JR NZ,IMM_SELECT4 ; IF CLEAR, MOVE ON
DEC HL
LD A,H
OR L
JP Z,IMM_CMD_TIMEOUT ; TIMEOUT
JR IMM_SELECT3
;
IMM_SELECT4:
LD A,$0C
CALL IMM_WRITECTRL
;
XOR A
RET
;
;
;
IMM_SENDCMD:
;
#IF IMMTRACE >= 2)
PRTS("\r\nSENDCMD:$")
#ENDIF
;
LD HL,0
IMM_SENDCMD1:
PUSH HL
CALL IMM_WAITDONE
POP HL
CP $A8
JR NZ,IMM_SENDCMD2
LD A,$04
CALL IMM_WRITECTRL
LD A,(DE)
;
#IF IMMTRACE >= 2)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
;
CALL IMM_WRITEDATA
INC DE
INC HL
LD A,$05
CALL IMM_WRITECTRL
LD A,(DE)
;
#IF IMMTRACE >= 2)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
;
CALL IMM_WRITEDATA
INC DE
INC HL
LD A,$00
CALL IMM_WRITECTRL
JR IMM_SENDCMD1
;
IMM_SENDCMD2:
LD A,$04
CALL IMM_WRITECTRL
;
#IF IMMTRACE >= 2)
CALL PC_SPACE
CALL PRTHEXWORDHL
PRTS(" BYTES$")
#ENDIF
;
RET
;
;
;
IMM_WAITBUSY:
LD HL,500 ; NORMAL TIMEOUT COUNTER
;
IMM_WAITBUSY_HL: ; VARIABLE TIMEOUT ENTRY
LD A,$0C
CALL IMM_WRITECTRL
;
IMM_WAITBUSY2:
CALL IMM_READSTATUS
BIT 7,A
RET NZ ; IF SET, MOVE ON
DEC HL
LD A,H
OR L
; TODO: IMPLEMENT TIMEOUT!!!
;JP Z,IMM_CMD_TIMEOUT ; TIMEOUT
JR IMM_WAITBUSY2
;
;
;
IMM_WAITDONE:
LD HL,500 ; NORMAL TIMEOUT COUNTER
;
IMM_WAITDONE_HL: ; VARIABLE TIMEOUT ENTRY
CALL IMM_WAITBUSY_HL
AND $B8
PUSH AF
LD A,$04
CALL IMM_WRITECTRL
POP AF
RET
;
;
;
IMM_NEGOTIATE:
#IF IMMTRACE >= 2)
PRTS("\r\nNEGO: $")
#ENDIF
LD A,$04
CALL IMM_WRITECTRL
CALL DELAY ; 16 US, IDEALLY 5 US
LD A,$00
CALL IMM_WRITEDATA
LD DE,7 ; 112 US, IDEALLY 100 US
CALL VDELAY
LD A,$06
CALL IMM_WRITECTRL
CALL DELAY ; 16 US, IDEALLY 5 US
CALL IMM_READSTATUS
PUSH AF ; SAVE RESULT
CALL DELAY ; 16 US, IDEALLY 5 US
LD A,$07
CALL IMM_WRITECTRL
CALL DELAY ; 16 US, IDEALLY 5 US
LD A,$06
CALL IMM_WRITECTRL
;
POP AF
;
#IF IMMTRACE >= 2)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
;
AND $20
;
#IF IMMTRACE >= 2)
CALL PC_SPACE
CALL PC_GT
CALL PRTHEXBYTE
#ENDIF
;
CP $20
JP NZ,IMM_CMD_IOERR
RET
;
;
;
IMM_GETBYTE:
CALL IMM_WAITBUSY
LD A,$04
CALL IMM_WRITECTRL
LD A,$06
CALL IMM_WRITECTRL
CALL IMM_READSTATUS
AND $F0
RRCA
RRCA
RRCA
RRCA
PUSH AF
LD A,$05
CALL IMM_WRITECTRL
CALL IMM_READSTATUS
AND $F0
POP HL
OR H
PUSH AF
LD A,$04
CALL IMM_WRITECTRL
POP AF
RET
;
; DE=BUFFER
; HL=LENGTH (0 FOR VARIABLE)
;
IMM_GETDATA:
LD A,H
OR L
JR NZ,IMM_GETDATALEN
;
#IF IMMTRACE >= 2)
PRTS("\r\nGETDATA:$")
#ENDIF
;
IMM_GETDATA1:
PUSH HL
CALL IMM_WAITDONE
POP HL
CP $98
JR NZ,IMM_GETDATA2
LD A,$04
CALL IMM_WRITECTRL
LD A,$06
CALL IMM_WRITECTRL
CALL IMM_READSTATUS
AND $F0
RRCA
RRCA
RRCA
RRCA
PUSH AF
LD A,$05
CALL IMM_WRITECTRL
CALL IMM_READSTATUS
AND $F0
POP BC
OR B
LD (DE),A
INC DE
INC HL
LD A,$04
CALL IMM_WRITECTRL
LD A,$0C
CALL IMM_WRITECTRL
JR IMM_GETDATA1
;
IMM_GETDATA2:
;
#IF IMMTRACE >= 2)
CALL PC_SPACE
CALL PRTHEXWORDHL
PRTS(" BYTES$")
#ENDIF
;
RET
;
IMM_GETDATALEN:
;
#IF IMMTRACE >= 2)
PRTS("\r\nGETDLEN:$")
CALL PC_SPACE
CALL PRTHEXWORDHL
PRTS(" BYTES$")
#ENDIF
;
LD A,$04
CALL IMM_WRITECTRL
IMM_GETDATALEN1:
LD A,$06
CALL IMM_WRITECTRL
CALL IMM_READSTATUS
AND $F0
RRCA
RRCA
RRCA
RRCA
PUSH AF
LD A,$05
CALL IMM_WRITECTRL
CALL IMM_READSTATUS
AND $F0
POP BC
OR B
LD (DE),A
INC DE
DEC HL
LD A,$04
CALL IMM_WRITECTRL
LD A,H
OR L
JR NZ,IMM_GETDATALEN1
LD A,$0C
CALL IMM_WRITECTRL
RET
;
; DE=BUFFER
; HL=LENGTH (0 FOR VARIABLE)
;
IMM_PUTDATA:
LD A,H
OR L
JR NZ,IMM_PUTDATALEN
;
#IF IMMTRACE >= 2)
PRTS("\r\nPUTDATA:$")
#ENDIF
;
IMM_PUTDATA1:
PUSH HL
CALL IMM_WAITDONE
POP HL
CP $88
JR NZ,IMM_PUTDATA2
LD A,$04
CALL IMM_WRITECTRL
LD A,(DE)
CALL IMM_WRITEDATA
INC DE
INC HL
LD A,$05
CALL IMM_WRITECTRL
LD A,(DE)
CALL IMM_WRITEDATA
INC DE
INC HL
LD A,$00
CALL IMM_WRITECTRL
JR IMM_PUTDATA1
;
IMM_PUTDATA2:
LD A,$04
CALL IMM_WRITECTRL
;
#IF IMMTRACE >= 2)
CALL PC_SPACE
CALL PRTHEXWORDHL
PRTS(" BYTES$")
#ENDIF
;
RET
;
IMM_PUTDATALEN:
;
#IF IMMTRACE >= 2)
PRTS("\r\nPUTDLEN:$")
CALL PC_SPACE
CALL PRTHEXWORDHL
PRTS(" BYTES$")
#ENDIF
;
LD A,$04
CALL IMM_WRITECTRL
IMM_PUTDATALEN1:
LD A,(DE)
CALL IMM_WRITEDATA
INC DE
DEC HL
LD A,$05
CALL IMM_WRITECTRL
LD A,(DE)
CALL IMM_WRITEDATA
INC DE
DEC HL
LD A,$00
CALL IMM_WRITECTRL
LD A,H
OR L
JR NZ,IMM_PUTDATALEN1
LD A,$04
CALL IMM_WRITECTRL
RET
;
;
;
IMM_GETSTATUS:
;
#IF IMMTRACE >= 2)
PRTS("\r\nSTATUS:$")
#ENDIF
;
CALL IMM_GETBYTE
LD (IMM_CMDSTAT),A
;
#IF IMMTRACE >= 2)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
;
CALL IMM_WAITDONE
CP $B8
RET NZ
CALL IMM_GETBYTE
LD (IMM_CMDSTAT+1),A
;
#IF IMMTRACE >= 2)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
;
RET
;
;
;
IMM_ENDREAD:
LD A,$04
CALL IMM_WRITECTRL
LD A,$0C
CALL IMM_WRITECTRL
LD A,$0E
CALL IMM_WRITECTRL
LD A,$04
CALL IMM_WRITECTRL
RET
;
; HL: COMMAND BUFFER
; DE: TRANSFER BUFFER
; BC: TRANSFER LENGTH (0=VARIABLE)
;
IMM_RUNCMD:
LD (IMM_CMDSTK),SP ; FOR ERROR ABORTS
LD (IMM_DSKBUF),DE ; SAVE BUF PTR
LD (IMM_XFRLEN),BC ; SAVE XFER LEN
PUSH HL
CALL IMM_CONNECT
CALL IMM_SELECT
CALL IMM_WAITBUSY
POP DE
CALL IMM_SENDCMD
;
IMM_RUNCMD_PHASE:
LD HL,50000 ; LONG TIMEOUT HERE
CALL IMM_WAITDONE_HL
;
#IF IMMTRACE >= 2)
PRTS("\r\nPHASE: $")
CALL PRTHEXBYTE
#ENDIF
;
CP $88 ; DEVICE WANTS TO RCV DATA
JR Z,IMM_RUNCMD_WRITE
CP $98 ; DEVICE WANTS TO SEND DATA
JR Z,IMM_RUNCMD_READ
CP $B8 ; DEVICE WANTS TO BE DONE
JR Z,IMM_RUNCMD_END
JR IMM_CMD_IOERR
;
IMM_RUNCMD_WRITE:
LD DE,(IMM_DSKBUF)
LD HL,(IMM_XFRLEN) ; XFER LENGTH
CALL IMM_PUTDATA ; SEND DATA NOW
JR IMM_RUNCMD_PHASE
;
IMM_RUNCMD_READ:
CALL IMM_NEGOTIATE
CALL IMM_WAITDONE
; CHECK FOR STATUS $98???
LD DE,(IMM_DSKBUF)
LD HL,(IMM_XFRLEN) ; XFER LENGTH
CALL IMM_GETDATA ; GET THE DATA NOW
CALL IMM_ENDREAD
JR IMM_RUNCMD_PHASE
;
IMM_RUNCMD_END:
CALL IMM_NEGOTIATE
CALL IMM_WAITDONE
; CHECK FOR STATUS $B8???
CALL IMM_GETSTATUS
CALL IMM_ENDREAD
CALL IMM_DISCONNECT
XOR A ; SIGNAL SUCCESS
RET
;
IMM_CMD_IOERR:
LD A,IMM_STIOERR
JR IMM_CMD_ERR
;
IMM_CMD_TIMEOUT:
LD A,IMM_STTO
JR IMM_CMD_ERR
;
IMM_CMD_ERR:
LD SP,(IMM_CMDSTK) ; UNWIND STACK
PUSH AF ; SAVE STATUS
;CALL IMM_RESETPULSE ; CLEAN UP THE MESS???
LD DE,62
CALL VDELAY
CALL IMM_DISCONNECT
LD DE,62
CALL VDELAY
POP AF ; RECOVER STATUS
JP IMM_ERR ; ERROR EXIT
;
;
;
IMM_CHKCMD:
; SCSI COMMAND COMPLETED, CHECK SCSI CMD STATUS
LD A,(IMM_CMDSTAT) ; GET STATUS BYTE
OR A ; SET FLAGS
RET Z ; IF ZERO, ALL GOOD, DONE
;
; DO WE HAVE A CHECK CONDITION?
CP 2 ; CHECK CONDITION RESULT?
JR Z,IMM_CHKCMD1 ; IF SO, REQUEST SENSE
JP IMM_IOERR ; ELSE, GENERAL I/O ERROR
;
IMM_CHKCMD1:
; USE REQUEST SENSE CMD TO GET ERROR DETAILS
LD DE,HB_WRKBUF ; PUT DATA IN WORK BUF
LD BC,0 ; VARIABLE LENGTH REQUEST
LD HL,IMM_CMD_SENSE ; REQUEST SENSE CMD
CALL IMM_RUNCMD ; DO IT
JP NZ,IMM_IOERR ; BAIL IF ERROR IN CMD
;
; REQ SENSE CMD COMPLETED
#IF IMMTRACE >= 2)
LD A,16
LD DE,HB_WRKBUF
CALL Z,PRTHEXBUF
#ENDIF
;
; CHECK SCSI CMD STATUS
LD A,(IMM_CMDSTAT) ; GET STATUS BYTE
OR A ; SET FLAGS
JP NZ,IMM_IOERR ; IF FAILED, GENERAL I/O ERROR
;
; RETURN RESULT BASED ON REQ SENSE DATA
LD A,(HB_WRKBUF+12) ; GET ADDITIONAL SENSE CODE
CP $3A ; NO MEDIA?
JP Z,IMM_NOMEDIA ; IF SO, RETURN NO MEDIA ERR
JP IMM_IOERR ; ELSE GENERAL I/O ERR
;
; CHECK CURRENT DEVICE FOR ERROR STATUS AND ATTEMPT TO RECOVER
; VIA RESET IF DEVICE IS IN ERROR.
;
IMM_CHKERR:
LD A,(IY+IMM_STAT) ; GET STATUS
OR A ; SET FLAGS
CALL NZ,IMM_RESET ; IF ERROR STATUS, RESET BUS
RET
;
; (RE)INITIALIZE DEVICE
;
IMM_INITDEV:
; INITIALIZE 8255
LD A,(IY+IMM_IOBASE) ; BASE PORT
ADD A,IMM_IOSETUP ; BUMP TO SETUP PORT
LD C,A ; MOVE TO C FOR I/O
LD A,$82 ; CONFIG A OUT, B IN, C OUT
OUT (C),A ; DO IT
CALL DELAY
;
CALL IMM_DISCONNECT
CALL IMM_CONNECT
CALL IMM_RESETPULSE
LD DE,62
CALL VDELAY
CALL IMM_DISCONNECT
LD DE,62
CALL VDELAY
;
LD B,4
IMM_INITDEV1:
PUSH BC
;
; REQUEST SENSE COMMAND
LD DE,HB_WRKBUF
LD BC,0
LD HL,IMM_CMD_SENSE
CALL IMM_RUNCMD
JR NZ,IMM_INITDEV2 ; CMD PROC ERROR
;
; CHECK CMD STATUS
LD A,(IMM_CMDSTAT) ; GET STATUS BYTE
OR A ; SET FLAGS
JR NZ,IMM_INITDEV2 ; BAD CMD STATUS
;
#IF IMMTRACE >= 2)
LD A,16
LD DE,HB_WRKBUF
CALL PRTHEXBUF
#ENDIF
;
; CHECK SENSE KEY
LD A,(HB_WRKBUF + 2)
OR A
;
IMM_INITDEV2:
POP BC
JR Z,IMM_INITDEV3 ; IF NO ERROR, MOVE ON
DJNZ IMM_INITDEV1 ; TRY UNTIL COUNTER EXHAUSTED
JP IMM_IOERR ; HANDLE ERROR
;
IMM_INITDEV3:
; READ & RECORD DEVICE CAPACITY
LD DE,HB_WRKBUF
LD BC,0
LD HL,IMM_CMD_RDCAP
CALL IMM_RUNCMD
CALL Z,IMM_CHKCMD
RET NZ ; MEDIA PROBLEM
;
#IF IMMTRACE >= 2)
LD A,8
LD DE,HB_WRKBUF
CALL PRTHEXBUF
#ENDIF
;
; INCREMENT, BIG->LITTLE ENDIAN, SAVE
LD A,IMM_MEDCAP ; OFFSET IN CFG FOR CAPACITY
CALL LDHLIYA ; POINTER TO HL
PUSH HL ; SAVE IT
LD HL,HB_WRKBUF ; POINT TO VALUE IN CMD RESULT
CALL LD32 ; LOAD IT TO DE:HL
LD A,L ; FLIP BYTES
LD L,D ; ... BIG ENDIAN
LD D,A ; ... TO LITTLE ENDIAN
LD A,H
LD H,E
LD E,A
CALL INC32 ; INCREMENT TO FINAL VALUE
POP BC ; RECOVER SAVE LOCATION
CALL ST32 ; STORE VALUE
;
XOR A ; SIGNAL SUCCESS
LD (IY+IMM_STAT),A ; RECORD IT
RET
;;;;
;;;; *** DEBUG ***
;;;;
;;; LD DE,HB_WRKBUF
;;; LD BC,512
;;; LD HL,IMM_CMD_READ
;;; CALL IMM_RUNCMD
;;; CALL Z,IMM_CHKCMD
;;; LD DE,HB_WRKBUF
;;; CALL Z,DUMP_BUFFER
;;;;
;;; LD DE,HB_WRKBUF
;;; LD BC,512
;;; LD HL,IMM_CMD_READ
;;; CALL IMM_RUNCMD
;;; CALL Z,IMM_CHKCMD
;;; LD DE,HB_WRKBUF
;;; CALL Z,DUMP_BUFFER
;;;;
;;; LD DE,HB_WRKBUF
;;; LD BC,0
;;; LD HL,IMM_CMD_INQ
;;; CALL IMM_RUNCMD
;;; CALL Z,IMM_CHKCMD
;;; LD DE,HB_WRKBUF
;;; CALL Z,DUMP_BUFFER
;;;;
;;; LD DE,HB_WRKBUF
;;; LD BC,512
;;; LD HL,IMM_CMD_WRITE
;;; CALL IMM_RUNCMD
;;; CALL Z,IMM_CHKCMD
;;;;
;;; LD DE,HB_WRKBUF
;;; LD BC,512
;;; LD HL,IMM_CMD_READ2
;;; CALL IMM_RUNCMD
;;; CALL Z,IMM_CHKCMD
;;; LD DE,HB_WRKBUF
;;; CALL Z,DUMP_BUFFER
;;;;
;;; RET
;;;;
;;;IMM_CMD .DB $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 ; TEMPLATE
;;;IMM_CMD_READ .DB $08, $00, $00, $00, $01, $00 ; READ SECTOR $0000
;;;IMM_CMD_INQ .DB $12, $00, $00, $00, $FF, $00 ; INQUIRY
;;;IMM_CMD_TEST .DB $00, $00, $00, $00, $00, $00 ; TEST UNIT READY
;;;IMM_CMD_START .DB $1B, $00, $00, $00, $01, $00 ; START UNIT
;;;IMM_CMD_WRITE .DB $0A, $00, $F0, $00, $01, $00 ; READ SECTOR $F000
;;;IMM_CMD_READ2 .DB $08, $00, $F0, $00, $01, $00 ; READ SECTOR $F000
;
;=============================================================================
; ERROR HANDLING AND DIAGNOSTICS
;=============================================================================
;
; ERROR HANDLERS
;
IMM_INVUNIT:
LD A,IMM_STINVUNIT
JR IMM_ERR2 ; SPECIAL CASE FOR INVALID UNIT
;
IMM_NOMEDIA:
LD A,IMM_STNOMEDIA
JR IMM_ERR
;
IMM_CMDERR:
LD A,IMM_STCMDERR
JR IMM_ERR
;
IMM_IOERR:
LD A,IMM_STIOERR
JR IMM_ERR
;
IMM_TO:
LD A,IMM_STTO
JR IMM_ERR
;
IMM_NOTSUP:
LD A,IMM_STNOTSUP
JR IMM_ERR
;
IMM_ERR:
LD (IY+IMM_STAT),A ; SAVE NEW STATUS
;
IMM_ERR2:
#IF (IMMTRACE >= 2)
CALL IMM_PRTSTAT
#ENDIF
OR A ; SET FLAGS
RET
;
;
;
IMM_PRTERR:
RET Z ; DONE IF NO ERRORS
; FALL THRU TO IMM_PRTSTAT
;
; PRINT FULL DEVICE STATUS LINE
;
IMM_PRTSTAT:
PUSH AF
PUSH DE
PUSH HL
LD A,(IY+IMM_STAT)
CP IMM_STINVUNIT
JR Z,IMM_PRTSTAT2 ; INVALID UNIT IS SPECIAL CASE
CALL IMM_PRTPREFIX ; PRINT UNIT PREFIX
JR IMM_PRTSTAT3
IMM_PRTSTAT2:
CALL NEWLINE
PRTS("IMM:$") ; NO UNIT NUM IN PREFIX FOR INVALID UNIT
IMM_PRTSTAT3:
CALL PC_SPACE ; FORMATTING
CALL IMM_PRTSTATSTR
POP HL
POP DE
POP AF
RET
;
; PRINT STATUS STRING
;
IMM_PRTSTATSTR:
PUSH AF
PUSH DE
LD A,(IY+IMM_STAT)
OR A
LD DE,IMM_STR_STOK
JR Z,IMM_PRTSTATSTR1
INC A
LD DE,IMM_STR_STINVUNIT
JR Z,IMM_PRTSTATSTR1
INC A
LD DE,IMM_STR_STNOMEDIA
JR Z,IMM_PRTSTATSTR1
INC A
LD DE,IMM_STR_STCMDERR
JR Z,IMM_PRTSTATSTR1
INC A
LD DE,IMM_STR_STIOERR
JR Z,IMM_PRTSTATSTR1
INC A
LD DE,IMM_STR_STTO
JR Z,IMM_PRTSTATSTR1
INC A
LD DE,IMM_STR_STNOTSUP
JR Z,IMM_PRTSTATSTR1
LD DE,IMM_STR_STUNK
IMM_PRTSTATSTR1:
CALL WRITESTR
POP DE
POP AF
RET
;
; PRINT DIAGNONSTIC PREFIX
;
IMM_PRTPREFIX:
PUSH AF
CALL NEWLINE
PRTS("IMM$")
LD A,(IY+IMM_DEV) ; GET CURRENT DEVICE NUM
CP $FE ; NOT YET ASSIGNED?
JR Z,IMM_PRTPREFIX1 ; SKIP DEV NUM IF SO
CALL PRTDECB
IMM_PRTPREFIX1:
CALL PC_COLON
POP AF
RET
;
;=============================================================================
; STRING DATA
;=============================================================================
;
IMM_STR_STOK .TEXT "OK$"
IMM_STR_STINVUNIT .TEXT "INVALID UNIT$"
IMM_STR_STNOMEDIA .TEXT "NO MEDIA$"
IMM_STR_STCMDERR .TEXT "COMMAND ERROR$"
IMM_STR_STIOERR .TEXT "IO ERROR$"
IMM_STR_STTO .TEXT "TIMEOUT$"
IMM_STR_STNOTSUP .TEXT "NOT SUPPORTED$"
IMM_STR_STUNK .TEXT "UNKNOWN ERROR$"
;
IMM_STR_NOHW .TEXT "NOT PRESENT$"
;
IMM_STR_MODE_NONE .TEXT "NONE$"
IMM_STR_MODE_MG014 .TEXT "MG014$"
IMM_STR_MODE_UNK .TEXT "???$"
;
;=============================================================================
; DATA STORAGE
;=============================================================================
;
IMM_DEVNUM .DB 0 ; TEMP DEVICE NUM USED DURING INIT
IMM_CMDSTK .DW 0 ; STACK PTR FOR CMD ABORTING
IMM_DSKBUF .DW 0 ; WORKING DISK BUFFER POINTER
IMM_XFRLEN .DW 0 ; WORKING TRANSFER LENGTH
IMM_CMDSTAT .DB 0, 0 ; CMD RESULT STATUS
;
IMM_TYPE_MAP:
.DW IMM_STR_NONE
.DW IMM_STR_MG014
;
IMM_STR_NONE .DB "<NOT PRESENT>$"
IMM_STR_MG014 .DB "MG014$"
;
; SCSI COMMAND TEMPLATES
;
IMM_CMD_RW .DB $00, $00, $00, $00, $01, $00 ; READ/WRITE SECTOR
IMM_CMD_SENSE .DB $03, $00, $00, $00, $FF, $00 ; REQUEST SENSE DATA
IMM_CMD_RDCAP .DB $25, $00, $00, $00, $00, $00, $00, $00, $00, $00 ; READ CAPACITY
;
; IMM DEVICE CONFIGURATION TABLE
;
IMM_CFG:
;
#IF (IMMCNT >= 1)
;
IMM0_CFG: ; DEVICE 0
.DB 0 ; DRIVER DEVICE NUMBER (FILLED DYNAMICALLY)
.DB IMM0MODE ; DRIVER DEVICE MODE
.DB 0 ; DEVICE STATUS
.DB IMM0BASE ; IO BASE ADDRESS
.DW 0,0 ; DEVICE CAPACITY
.DW 0,0 ; CURRENT LBA
#ENDIF
;
#IF (IMMCNT >= 2)
;
IMM1_CFG: ; DEVICE 1
.DB 0 ; DRIVER DEVICE NUMBER (FILLED DYNAMICALLY)
.DB IMM1MODE ; DRIVER DEVICE MODE
.DB 0 ; DEVICE STATUS
.DB IMM1BASE ; IO BASE ADDRESS
.DW 0,0 ; DEVICE CAPACITY
.DW 0,0 ; CURRENT LBA
#ENDIF
;
#IF ($ - IMM_CFG) != (IMMCNT * IMM_CFGSIZ)
.ECHO "*** INVALID IMM CONFIG TABLE ***\n"
#ENDIF
;
.DB $FF ; END MARKER