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.
 
 
 
 
 
 

1567 lines
37 KiB

;
;=============================================================================
; IMM DISK DRIVER
;=============================================================================
;
; PARALLEL PORT INTERFACE FOR SCSI DISK DEVICES USING A PARALLEL PORT
; ADAPTER. PRIMARILY TARGETS PARALLEL PORT IOMEGA ZIP DRIVES.
;
; INTENDED TO CO-EXIST WITH LPT DRIVER.
;
; CREATED BY WAYNE WARTHEN FOR ROMWBW HBIOS.
; MUCH OF THE CODE IS DERIVED FROM LINUX AND FUZIX (ALAN COX).
; - https://github.com/EtchedPixels/FUZIX
; - https://github.com/torvalds/linux
;
; 05/23/2023 WBW - INITIAL RELEASE
; 05/26/2023 WBW - CLEAN UP, LED ACTIVITY
; 05/27/2023 WBW - ADDED SPP MODE
; 06/06/2023 WBW - OPTIMIZE BLOCK READ AND WRITE
;
;=============================================================================
;
; IBM PC STANDARD PARALLEL PORT (SPP):
; - NHYODYNE PRINT MODULE
;
; PORT 0 (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
; +-------+-------+-------+-------+-------+-------+-------+-------+
; | /BUSY | /ACK | POUT | SEL | /ERR | 0 | 0 | 0 |
; +-------+-------+-------+-------+-------+-------+-------+-------+
;
; PORT 2 (OUTPUT):
;
; D7 D6 D5 D4 D3 D2 D1 D0
; +-------+-------+-------+-------+-------+-------+-------+-------+
; | STAT1 | STAT0 | ENBL | PINT | SEL | RES | LF | STB |
; +-------+-------+-------+-------+-------+-------+-------+-------+
;
;=============================================================================
;
; MG014 STYLE INTERFACE:
; - RCBUS MG014 MODULE
;
; PORT 0 (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 (OUTPUT):
;
; D7 D6 D5 D4 D3 D2 D1 D0
; +-------+-------+-------+-------+-------+-------+-------+-------+
; | LED | | | | /SEL | /RES | /LF | /STB |
; +-------+-------+-------+-------+-------+-------+-------+-------+
;
;=============================================================================
;
; TODO:
;
; NOTES:
;
; - THIS DRIVER IS FOR THE ZIP DRIVE IMM 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 PPA OR IMM. SIGH.
;
; - THIS DRIVER OPERATES USES NIBBLE READ MODE. ALTHOUGH THE 8255
; (MG014) CAN READ OR WRITE TO PORT A (DATA), IT "GLITCHES" WHEN
; THE MODE IS CHANGED CAUSING THE CONTROL LINES TO CHANGE AND
; BREAKS THE PROTOCOL. I SUSPECT THE MBC SPP CAN SUPPORT 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.
;
; - SOME OF THE DATA TRANSFERS HAVE NO BUFFER OVERRUN CHECKS. IT IS
; ASSUMED SCSI DEVICES WILL SEND/REQUEST THE EXPECTED NUMBER OF BYTES.
;
; 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
;
; IMM DEVICE STATUS
;
IMM_STOK .EQU 0
IMM_STNOMEDIA .EQU -1
IMM_STCMDERR .EQU -2
IMM_STIOERR .EQU -3
IMM_STTO .EQU -4
IMM_STNOTSUP .EQU -5
;
; 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)
;
; MACROS
;
#DEFINE IMM_WCTL(VAL) LD A,VAL \ CALL IMM_WRITECTRL
#DEFINE IMM_WDATA(VAL) LD A,VAL \ CALL IMM_WRITEDATA
;
; INCLUDE MG014 NIBBLE MAP FOR MG014 MODE
;
#IF (IMMMODE == IMMMODE_MG014)
#DEFINE MG014_MAP
#ENDIF
;
;=============================================================================
; 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 HL,IMM_STR_MODE_MAP
ADD A,A
CALL ADDHLA
LD E,(HL)
INC HL
LD D,(HL)
CALL WRITESTR
;
; 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
#IF (IMMTRACE <= 1)
CALL NZ,IMM_PRTSTAT
#ENDIF
JR NZ,IMM_INIT6
;
; START PRINTING DEVICE INFO
CALL IMM_PRTPREFIX ; PRINT DEVICE PREFIX
;
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:
#IF (IMMTRACE >= 3)
PRTS("\r\nDETECT:$")
#ENDIF
;
#IF (IMMMODE == IMMMODE_MG014)
; 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 ; BRIEF DELAY FOR GOOD MEASURE
#ENDIF
;
; ATTEMPT TO ESTABLISH A CONNECTION TO THE IMM DEVICE AND
; ISSUE A SCSI BUS RESET. WE DON'T KNOW IF DEVICE IS THERE
; YET. THIS IS DONE BLIND ASSUMING IT IS THERE.
CALL IMM_DISCONNECT
CALL IMM_CONNECT
CALL IMM_RESETPULSE ; ISSUE A SCSI BUS RESET
LD DE,62 ; WAIT A BIT
CALL VDELAY
;
; USE AN ABBREVIATED VERSION OF SELECT PROCESSING TO
; CHECK IF DEVICE EXISTS.
IMM_WCTL($0C)
CALL IMM_READSTATUS
;
#IF (IMMTRACE >= 3)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
;
; READY FOR SELECT?
AND $08
CP $00
JR NZ,IMM_DETECT1
;
IMM_WCTL($04)
LD A,$80 | (1 << IMM_TGT)
CALL IMM_WRITEDATA
IMM_WCTL($0C)
IMM_WCTL($0D)
CALL DELAY
CALL IMM_READSTATUS
;
#IF (IMMTRACE >= 3)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
;
; DID SELECT SUCCEED?
AND $08
CP $08
;
IMM_DETECT1:
PUSH AF
CALL IMM_DISCONNECT
POP AF
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 ; HOOK DISK READ CONTROLLER
LD A,SCSI_CMD_READ ; SETUP SCSI READ
LD (IMM_CMD_RW),A ; AND SAVE IT IN SCSI CMD
JP IMM_IO ; DO THE I/O
;
;
;
IMM_WRITE:
CALL HB_DSKWRITE ; HOOK DISK WRITE CONTROLLER
LD A,SCSI_CMD_WRITE ; SETUP SCSI WRITE
LD (IMM_CMD_RW),A ; AND SAVE IT IN SCSI CMD
JP IMM_IO ; DO THE I/O
;
;
;
IMM_IO:
PUSH HL
CALL IMM_CHKERR ; CHECK FOR ERR STATUS AND RESET IF SO
POP HL
JR NZ,IMM_IO3 ; BAIL OUT ON ERROR
;
LD (IMM_DSKBUF),HL ; SAVE DISK BUFFER ADDRESS
;
#IF (DSKYENABLE)
#IF (DSKYDSKACT)
LD A,IMM_LBA
CALL LDHLIYA
CALL HB_DSKACT ; SHOW ACTIVITY
#ENDIF
#ENDIF
;
; 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) ; DISK BUFFER TO DE
LD A,1 ; BLOCK I/O, ONE SECTOR
LD HL,IMM_CMD_RW ; POINT TO READ/WRITE CMD TEMPLATE
CALL IMM_RUNCMD ; RUN THE SCSI ENGINE
CALL Z,IMM_CHKCMD ; IF EXIT OK, CHECK SCSI RESULTS
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,IMM_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 ; JUST (RE)INIT DEVICE
;
;
;
IMM_DEVICE:
LD D,DIODEV_IMM ; D := DEVICE TYPE
LD E,(IY+IMM_DEV) ; E := PHYSICAL DEVICE NUMBER
LD C,%01111001 ; 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 INCLUDES MEDIA CHECK
;
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
;=============================================================================
;
; OUTPUT BYTE IN A TO THE DATA PORT
;
IMM_WRITEDATA:
LD C,(IY+IMM_IOBASE) ; DATA PORT IS AT IOBASE
OUT (C),A ; WRITE THE BYTE
;CALL DELAY ; IS THIS NEEDED???
RET ; DONE
;
;
;
IMM_WRITECTRL:
; IBM PC INVERTS ALL BUT C2 ON THE BUS, MG014 DOES NOT.
; BELOW TRANSLATES FROM IBM -> MG014. IT ALSO INVERTS THE
; MG014 LED SIMPLY TO MAKE IT EASY TO KEEP LED ON DURING
; ALL ACTIVITY.
;
#IF (IMMMODE == IMMMODE_MG014
XOR $0B | $80 ; HIGH BIT IS MG014 LED
#ENDIF
;#IF (IMMMODE == IMMMODE_SPP
; AND %00001111
; OR %11000000
;#ENDIF
LD C,(IY+IMM_IOBASE) ; GET BASE IO ADDRESS
INC C ; BUMP TO CONTROL PORT
INC C
OUT (C),A ; WRITE TO CONTROL PORT
;CALL DELAY ; IS THIS NEEDED?
RET ; DONE
;
; READ THE PARALLEL PORT INPUT LINES (STATUS) AND MAP SIGNALS FROM
; MG014 TO IBM STANDARD. NOTE POLARITY CHANGE REQUIRED FOR BUSY.
;
; MG014 IBM PC (SPP)
; -------- --------
; 0: /ACK 6: /ACK
; 1: BUSY 7: /BUSY
; 2: POUT 5: POUT
; 3: SEL 4: SEL
; 4: /ERR 3: /ERR
;
IMM_READSTATUS:
LD C,(IY+IMM_IOBASE) ; IOBASE TO C
INC C ; BUMP TO STATUS PORT
IN A,(C) ; READ IT
;
#IF (IMMMODE == IMMMODE_MG014)
;
; SHUFFLE BITS ON MG014
LD C,0 ; INIT RESULT
BIT 0,A ; 0: /ACK
JR Z,IMM_READSTATUS1
SET 6,C ; 6: /ACK
IMM_READSTATUS1:
BIT 1,A ; 1: BUSY
JR NZ,IMM_READSTATUS2 ; POLARITY CHANGE!
SET 7,C ; 7: /BUSY
IMM_READSTATUS2:
BIT 2,A ; 2: POUT
JR Z,IMM_READSTATUS3
SET 5,C ; 5: POUT
IMM_READSTATUS3:
BIT 3,A ; 3: SEL
JR Z,IMM_READSTATUS4
SET 4,C ; 4: SEL
IMM_READSTATUS4:
BIT 4,A ; 4: /ERR
JR Z,IMM_READSTATUS5
SET 3,C ; 3: /ERR
IMM_READSTATUS5:
LD A,C ; RESULT TO A
;
#ENDIF
;
RET
;
; SIGNAL SEQUENCE TO CONNECT/DISCONNECT
; VALUE IN A IS WRITTEN TO DATA PORT DURING SEQUENCE
;
IMM_CPP:
PUSH AF
IMM_WCTL($0C)
IMM_WDATA($AA)
IMM_WDATA($55)
IMM_WDATA($00)
IMM_WDATA($FF)
CALL IMM_READSTATUS
AND $B8
LD (IMM_S1),A
IMM_WDATA($87)
CALL IMM_READSTATUS
AND $B8
LD (IMM_S2),A
IMM_WDATA($78)
CALL IMM_READSTATUS
AND $38
LD (IMM_S3),A
POP AF
CALL IMM_WRITEDATA
IMM_WCTL($0C)
IMM_WCTL($0D)
IMM_WCTL($0C)
IMM_WDATA($FF)
;
; CONNECT: S1=$B8 S2=$18 S3=$30
; DISCONNECT: S1=$B8 S2=$18 S3=$38
;
#IF (IMMTRACE >= 3)
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
;
; SEQUENCE TO CONNECT TO DEVICE ON PARALLEL PORT BUS.
;
IMM_CONNECT:
LD A,$E0
CALL IMM_CPP
LD A,$30
CALL IMM_CPP
LD A,$E0
CALL IMM_CPP
RET
;
; SEQUENCE TO DISCONNECT FROM DEVICE ON PARALLEL PORT BUS.
; THE FINAL IMM_WRITECTRL IS ONLY TO TURN OFF THE MG014 STATUS LED.
;
IMM_DISCONNECT:
LD A,$30
CALL IMM_CPP
;
; TURNS OFF MG014 LED
IMM_WCTL($8C)
;
RET
;
; INITIATE A SCSI BUS RESET.
;
IMM_RESETPULSE:
IMM_WCTL($04)
IMM_WDATA($40)
CALL DELAY ; 16 US, IDEALLY, 1 US
IMM_WCTL($0C)
IMM_WCTL($0D)
CALL DELAY ; 48 US, IDEALLY, 50 US
CALL DELAY
CALL DELAY
IMM_WCTL($0C)
IMM_WCTL($04)
RET
;
; SCSI SELECT PROCESS
;
IMM_SELECT:
#IF (IMMTRACE >= 3)
PRTS("\r\nSELECT: $")
#ENDIF
IMM_WCTL($0C)
;
LD B,0 ; TIMEOUT COUNTER
IMM_SELECT1:
CALL IMM_READSTATUS
#IF (IMMTRACE >= 3)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
AND $08
JR Z,IMM_SELECT2 ; IF CLEAR, MOVE ON
DJNZ IMM_SELECT1
JP IMM_CMD_TIMEOUT ; TIMEOUT
;
IMM_SELECT2:
IMM_WCTL($04)
; PLACE HOST AND TARGET BIT ON DATA BUS
LD A,$80 | (1 << IMM_TGT)
CALL IMM_WRITEDATA
IMM_WCTL($0C)
IMM_WCTL($0D)
;
LD B,0 ; TIMEOUT COUNTER
IMM_SELECT3:
CALL IMM_READSTATUS
#IF (IMMTRACE >= 3)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
AND $08
JR NZ,IMM_SELECT4 ; IF SET, MOVE ON
DJNZ IMM_SELECT3
JP IMM_CMD_TIMEOUT ; TIMEOUT
;
IMM_SELECT4:
IMM_WCTL($0C)
;
XOR A
RET
;
; SEND SCSI CMD BYTE STRING. AT ENTRY, HL POINTS TO START OF
; COMMAND BYTES. THE LENGTH OF THE COMMAND STRING MUST PRECEED
; THE COMMAND BYTES (HL - 1).
;
; NOTE THAT DATA IS SENT AS BYTE PAIRS! EACH LOOP SENDS 2 BYTES.
; DATA OUTPOUT IS BURSTED (NO CHECK FOR BUSY). SEEMS TO WORK FINE.
;
IMM_SENDCMD:
;
#IF (IMMTRACE >= 3)
PRTS("\r\nSENDCMD:$")
#ENDIF
;
DEC HL ; BACKUP TO LENGTH BYTE
LD B,(HL) ; PUT IN B FOR LOOP COUNTER
;
#IF (IMMTRACE >= 3)
LD A,B
CALL PC_SPACE
CALL PRTHEXBYTE
PRTS(" BYTES$")
#ENDIF
;
INC HL ; BACK TO FIRST CMD BYTE
IMM_SENDCMD1:
IMM_WCTL($04)
LD A,(HL) ; LOAD CMD BYTE
;
#IF (IMMTRACE >= 3)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
;
CALL IMM_WRITEDATA ; PUT IT ON THE BUS
INC HL ; BUMP TO NEXT BYTE
DEC B ; DEC LOOP COUNTER
IMM_WCTL($05)
LD A,(HL) ; LOAD CMD BYTE
;
#IF (IMMTRACE >= 3)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
;
CALL IMM_WRITEDATA ; PUT IT ON THE BUS
INC HL ; BUMP TO NEXT BYTE
IMM_WCTL($00)
DJNZ IMM_SENDCMD1 ; LOOP TILL DONE
;
IMM_SENDCMD2:
IMM_WCTL($04)
;
RET
;
; WAIT FOR SCSI BUS TO BECOME READY WITH A TIMEOUT.
;
IMM_WAITLOOP:
CALL IMM_READSTATUS
BIT 7,A
RET NZ ; DONE, STATUS IN A
DEC HL
LD A,H
OR L
RET Z ; TIMEOUT
JR IMM_WAITLOOP
;
IMM_WAIT:
LD HL,500 ; GOOD VALUE???
IMM_WCTL($0C)
CALL IMM_WAITLOOP
JP Z,IMM_CMD_TIMEOUT ; HANDLE TIMEOUT
PUSH AF
IMM_WCTL($04)
POP AF
AND $B8
RET ; RETURN W/ RESULT IN A
;
; MAX OBSERVED WAITLOOP ITERATIONS IS $0116B3 @ 7.3728 MHZ ON MG014
; MAX OBSERVED WAITLOOP ITERATIONS IS $028EFE @ 8.000 MHZ ON MBC SPP
;
IMM_LONGWAIT:
LD A,(CB_CPUMHZ) ; LOAD CPU SPEED IN MHZ
SRL A ; DIVIDE BY 2, GOOD ENOUGH
LD B,A ; USE FOR OUTER LOOP COUNT
IMM_WCTL($0C)
IMM_LONGWAIT1:
LD HL,0
CALL IMM_WAITLOOP
JR NZ,IMM_LONGWAIT2 ; HANDLE SUCCESS
DJNZ IMM_LONGWAIT1 ; LOOP TILL COUNTER EXHAUSTED
JP IMM_CMD_TIMEOUT ; HANDLE TIMEOUT
;
IMM_LONGWAIT2:
PUSH AF
IMM_WCTL($04)
;
#IF 0
CALL PC_GT
LD A,B
CALL PRTHEXBYTE
CALL PC_COLON
CALL PRTHEXWORDHL
#ENDIF
;
POP AF
AND $B8
RET ; RETURN W/ RESULT IN A
;
; PEROFRM SCSI BUS NEGOTIATION. REQURIED PRIOR TO DATA READS.
;
IMM_NEGOTIATE:
#IF (IMMTRACE >= 3)
PRTS("\r\nNEGO: $")
#ENDIF
IMM_WCTL($04)
CALL DELAY ; 16 US, IDEALLY 5 US
IMM_WDATA($00)
LD DE,7 ; 112 US, IDEALLY 100 US
CALL VDELAY
IMM_WCTL($06)
CALL DELAY ; 16 US, IDEALLY 5 US
CALL IMM_READSTATUS
PUSH AF ; SAVE RESULT
CALL DELAY ; 16 US, IDEALLY 5 US
IMM_WCTL($07)
CALL DELAY ; 16 US, IDEALLY 5 US
IMM_WCTL($06)
;
POP AF
;
#IF (IMMTRACE >= 3)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
;
AND $20
;
#IF (IMMTRACE >= 3)
CALL PC_SPACE
CALL PC_GT
CALL PRTHEXBYTE
#ENDIF
;
CP $20 ; $20 MEANS DATA READY
JP NZ,IMM_CMD_IOERR
RET
;
; GET A BYTE OF DATA FROM THE SCSI DEVICE. THIS IS A NIBBLE READ.
; BYTE RETURNED IN A.
;
IMM_GETBYTE:
CALL IMM_WAIT
IMM_WCTL($06)
CALL IMM_READSTATUS
AND $F0
RRCA
RRCA
RRCA
RRCA
PUSH AF
IMM_WCTL($05)
CALL IMM_READSTATUS
AND $F0
POP HL
OR H
PUSH AF
IMM_WCTL($04)
POP AF
RET
;
; GET A CHUNK OF DATA FROM SCSI BUS. THIS IS SPECIFICALLY FOR
; READ PHASE. IF TRANSFER MODE IS NON-ZERO, THEN A BLOCK (512 BYTES)
; OF DATA WILL BE READ. OTHERWISE, DATA IS WRITTEN AS
; LONG AS SCSI DEVICE WANTS TO CONTINUE RECEIVING (NO OVERRUN
; CHECK IN THIS CASE).
;
; THIS IS A NIBBLE READ.
;
; DE=BUFFER
; A=TRANSFER MODE (0=VARIABLE, 1=BLOCK)
;
IMM_GETDATA:
; BRANCH TO CORRECT ROUTINE
OR A
JR NZ,IMM_GETBLOCK ; DO BLOCK READ
;
#IF (IMMTRACE >= 3)
PRTS("\r\nGETDATA:$")
#ENDIF
;
IMM_GETDATA1:
PUSH HL ; SAVE BYTE COUNTER
CALL IMM_WAIT ; WAIT FOR BUS READY
POP HL ; RESTORE BYTE COUNTER
CP $98 ; CHECK FOR READ PHASE
JR NZ,IMM_GETDATA2 ; IF NOT, ASSUME WE ARE DONE
IMM_WCTL($06)
CALL IMM_READSTATUS ; GET FIRST NIBBLE
AND $F0 ; ISOLATE BITS
RRCA ; AND SHIFT TO LOW NIBBLE
RRCA
RRCA
RRCA
PUSH AF ; SAVE WORKING VALUE
IMM_WCTL($05)
CALL IMM_READSTATUS ; GET SECOND NIBBLE
AND $F0 ; ISOLATE BITS
POP BC ; RECOVER LOW NIBBLE
OR B ; COMBINE
LD (DE),A ; AND SAVE THE FULL BYTE VALUE
INC DE ; NEXT BUFFER POS
INC HL ; INCREMENT BYTES COUNTER
IMM_WCTL($04)
JR IMM_GETDATA1 ; LOOP TILL DONE
;
IMM_GETDATA2:
;
#IF (IMMTRACE >= 3)
CALL PC_SPACE
CALL PRTHEXWORDHL
PRTS(" BYTES$")
#ENDIF
;
IMM_WCTL($0C)
RET
;
IMM_GETBLOCK:
;
#IF (IMMTRACE >= 3)
PRTS("\r\nGETBLK:$")
#ENDIF
;
IMM_WCTL($04)
LD B,0 ; LOOP COUNTER
EXX ; SWITCH TO ALT REGS
EX AF,AF' ; SWITCH TO ALT AF
; SAVE ALT REGS
PUSH AF
PUSH BC
PUSH DE
PUSH HL
; C: PORT C
LD A,(IY+IMM_IOBASE) ; BASE PORT
INC A ; STATUS PORT
LD (IMM_GETBLOCK_A),A ; FILL IN
LD (IMM_GETBLOCK_B),A ; ... DYNAMIC BITS OF CODE
INC A ; CONTROL PORT
LD C,A ; ... TO C
#IF (IMMMODE == IMMMODE_MG014)
; DE: CLOCK VALUES
LD D,$06 ^ ($0B | $80)
LD E,$05 ^ ($0B | $80)
; HL: STATMAP
LD H,MG014_STATMAPLO >> 8
#ENDIF
#IF (IMMMODE == IMMMODE_SPP)
; DE: CLOCK VALUES
LD D,$06
LD E,$05
#ENDIF
EXX ; SWITCH TO PRI REGS
EX AF,AF' ; SWITCH TO PRI AF
CALL IMM_GETBLOCK1 ; LOOP TWICE
CALL IMM_GETBLOCK1 ; ... FOR 512 BYTES
; RESTORE ALT REGS
EXX ; SWITCH TO ALT REGS
EX AF,AF' ; SWITCH TO ALT AF
POP HL
POP DE
POP BC
POP AF
EXX ; SWITCH TO PRI REGS
EX AF,AF' ; SWITCH TO PRI AF
IMM_WCTL($0C)
RET
;
IMM_GETBLOCK1:
EXX ; ALT REGS
OUT (C),D ; SEND FIRST CLOCK
IMM_GETBLOCK_A .EQU $+1
IN A,($FF) ; GET LOW NIBBLE
#IF (IMMMODE == IMMMODE_MG014)
AND $0F ; RELEVANT BITS ONLY
ADD A,MG014_STATMAPLO & $FF ; LOW BYTE OF MAP PTR
LD L,A ; PUT IN L
LD A,(HL) ; LOOKUP LOW NIBBLE VALUE
EX AF,AF' ; ALT AF, SAVE NIBBLE
#ENDIF
#IF (IMMMODE == IMMMODE_SPP)
AND $F0 ; RELEVANT BITS ONLY
RLCA ; MOVE TO LOW NIBBLE
RLCA ; MOVE TO LOW NIBBLE
RLCA ; MOVE TO LOW NIBBLE
RLCA ; MOVE TO LOW NIBBLE
LD L,A ; SAVE NIBBLE IN L
#ENDIF
OUT (C),E ; SEND SECOND CLOCK
IMM_GETBLOCK_B .EQU $+1
IN A,($FF) ; GET HIGH NIBBLE
#IF (IMMMODE == IMMMODE_MG014)
AND $0F ; RELEVANT BITS ONLY
ADD A,MG014_STATMAPHI & $FF ; HIGH BYTE OF MAP PTR
LD L,A ; PUT IN L
EX AF,AF' ; PRI AF, RECOVER LOW NIBBLE VALUE
OR (HL) ; COMBINE WITH HIGH NIB VALUE
#ENDIF
#IF (IMMMODE == IMMMODE_SPP)
AND $F0 ; RELEVANT BITS ONLY
OR L ; COMBINE WITH HIGH NIB VALUE
#ENDIF
EXX ; SWITCH TO PRI REGS
LD (DE),A ; SAVE BYTE
INC DE ; BUMP BUF PTR
DJNZ IMM_GETBLOCK1 ; LOOP
RET ; DONE
;
; PUT A CHUNK OF DATA TO THE SCSI BUS. THIS IS SPECIFICALLY FOR
; WRITE PHASE. IF TRANSFER MODE IS NON-ZERO, THEN A BLOCK (512 BYTES)
; OF DATA WILL BE WRITTEN. OTHERWISE, DATA IS WRITTEN AS
; LONG AS SCSI DEVICE WANTS TO CONTINUE RECEIVING (NO OVERRUN
; CHECK IN THIS CASE).
;
; DE=BUFFER
; A=TRANSFER MODE (0=VARIABLE, 1=BLOCK)
;
IMM_PUTDATA:
; BRANCH TO CORRECT ROUTINE
OR A
JR NZ,IMM_PUTBLOCK ; DO BLOCK WRITE
;
#IF (IMMTRACE >= 3)
PRTS("\r\nPUTDATA:$")
#ENDIF
;
IMM_PUTDATA1:
PUSH HL ; SAVE BYTE COUNTER
CALL IMM_WAIT ; WAIT FOR BUS READY
POP HL ; RESTORE BYTE COUNTER
CP $88 ; CHECK FOR WRITE PHASE
JR NZ,IMM_PUTDATA2 ; IF NOT, ASSUME WE ARE DONE
;IMM_WCTL($04)
LD A,(DE) ; GET NEXT BYTE TO WRITE (FIRST OF PAIR)
CALL IMM_WRITEDATA ; PUT ON BUS
INC DE ; BUMP TO NEXT BUF POS
INC HL ; INCREMENT COUNTER
IMM_WCTL($05)
LD A,(DE) ; GET NEXT BYTE TO WRITE (SECOND OF PAIR)
CALL IMM_WRITEDATA ; PUT ON BUS
INC DE ; BUMP TO NEXT BUF POS
INC HL ; INCREMENT COUNTER
IMM_WCTL($00)
JR IMM_PUTDATA1 ; LOOP TILL DONE
;
IMM_PUTDATA2:
IMM_WCTL($04)
;
#IF (IMMTRACE >= 3)
CALL PC_SPACE
CALL PRTHEXWORDHL
PRTS(" BYTES$")
#ENDIF
;
RET
;
IMM_PUTBLOCK:
;
#IF (IMMTRACE >= 3)
PRTS("\r\nPUTBLK:$")
#ENDIF
;
IMM_WCTL($04)
LD B,0 ; LOOP COUNTER
LD A,(IY+IMM_IOBASE) ; GET BASE IO ADR
LD (IMM_PUTBLOCK_A),A ; FILL IN
LD (IMM_PUTBLOCK_B),A ; ... DYNAMIC BITS OF CODE
INC A ; STATUS PORT
INC A ; CONTROL PORT
LD C,A ; ... TO C
; HL: CLOCK VALUES
#IF (IMMMODE == IMMMODE_MG014)
LD H,$05 ^ ($0B | $80)
LD L,$00 ^ ($0B | $80)
#ENDIF
#IF (IMMMODE == IMMMODE_SPP)
LD H,$05
LD L,$00
#ENDIF
CALL IMM_PUTBLOCK1 ; ONE LOOP CUZ BYTE PAIRS
IMM_WCTL($04)
RET
;
IMM_PUTBLOCK1:
LD A,(DE) ; GET NEXT BYTE
IMM_PUTBLOCK_A .EQU $+1
OUT ($FF),A ; PUT ON BUS
INC DE ; INCREMENT BUF POS
OUT (C),H ; FIRST CLOCK
LD A,(DE) ; GET NEXT BYTE
IMM_PUTBLOCK_B .EQU $+1
OUT ($FF),A ; PUT ON BUS
INC DE ; INCREMENT BUF POS
OUT (C),L ; SECOND CLOCK
DJNZ IMM_PUTBLOCK1 ; LOOP
RET ; DONE
;
; READ SCSI COMMAND STATUS
;
IMM_GETSTATUS:
;
#IF (IMMTRACE >= 3)
PRTS("\r\nSTATUS:$")
#ENDIF
;
CALL IMM_GETBYTE ; GET ONE BYTE
LD (IMM_CMDSTAT),A ; SAVE AS FIRST STATUS BYTE
;
#IF (IMMTRACE >= 3)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
;
CALL IMM_WAIT ; CHECK FOR OPTIONAL SECOND BYTE
CP $B8 ; STILL IN STATUS PHASE?
RET NZ ; IF NOT, DONE
CALL IMM_GETBYTE ; ELSE, GET THE SECOND BYTE
LD (IMM_CMDSTAT+1),A ; AND SAVE IT
;
#IF (IMMTRACE >= 3)
CALL PC_SPACE
CALL PRTHEXBYTE
#ENDIF
;
RET
;
; TERMINATE A BULK READ OPERATION
;
IMM_ENDREAD:
IMM_WCTL($04)
IMM_WCTL($0C)
IMM_WCTL($0E)
IMM_WCTL($04)
RET
;
; THIS IS THE MAIN SCSI ENGINE. BASICALLY, IT SELECTS THE DEVICE
; ON THE BUS, SENDS THE COMMAND, THEN PROCESSES THE RESULT.
;
; HL: COMMAND BUFFER
; DE: TRANSFER BUFFER
; A: TRANSFER MODE (0=VARIABLE, 1=BLOCK)
;
IMM_RUNCMD:
; THERE ARE MANY PLACES NESTED WITHIN THE ROUTINES THAT
; ARE CALLED HERE. HERE WE SAVE THE STACK SO THAT WE CAN
; EASILY AND QUICKLY ABORT OUT OF ANY NESTED ROUTINE.
; SEE IMM_CMD_ERR BELOW.
LD (IMM_CMDSTK),SP ; FOR ERROR ABORTS
LD (IMM_DSKBUF),DE ; SAVE BUF PTR
LD (IMM_XFRMODE),A ; SAVE XFER LEN
PUSH HL
CALL IMM_CONNECT ; PARALLEL PORT BUS CONNECT
CALL IMM_SELECT ; SELECT TARGET DEVICE
CALL IMM_WAIT ; WAIT TILL READY
POP HL
CALL IMM_SENDCMD ; SEND THE COMMAND
;
IMM_RUNCMD_PHASE:
; WAIT FOR THE BUS TO BE READY. WE USE AN EXTRA LONG WAIT
; TIMEOUT HERE BECAUSE THIS IS WHERE WE WILL WAIT FOR LONG
; OPERATIONS TO COMPLETE. IT CAN TAKE SOME TIME IF THE
; DEVICE HAS GONE TO SLEEP BECAUSE IT WILL NEED TO WAKE UP
; AND SPIN UP BEFORE PROCESSING AN I/O COMMAND.
CALL IMM_LONGWAIT ; WAIT TILL READY
;
#IF (IMMTRACE >= 3)
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) ; XFER BUFFER
LD A,(IMM_XFRMODE) ; XFER MODE
CALL IMM_PUTDATA ; SEND DATA NOW
JR IMM_RUNCMD_PHASE ; BACK TO DISPATCH
;
IMM_RUNCMD_READ:
CALL IMM_NEGOTIATE ; NEGOTIATE FOR READ
CALL IMM_WAIT ; WAIT TILL READY
; CHECK FOR STATUS $98???
LD DE,(IMM_DSKBUF) ; XFER BUFFER
LD A,(IMM_XFRMODE) ; XFER MODE
CALL IMM_GETDATA ; GET THE DATA NOW
CALL IMM_ENDREAD ; TERMINATE THE READ
JR IMM_RUNCMD_PHASE ; BACK TO DISPATCH
;
IMM_RUNCMD_END:
CALL IMM_NEGOTIATE ; NEGOTIATE FOR READ (STATUS)
CALL IMM_WAIT ; WAIT TILL READY
; CHECK FOR STATUS $B8???
CALL IMM_GETSTATUS ; READ STATUS BYTES
CALL IMM_ENDREAD ; TERMINATE THE READ
CALL IMM_DISCONNECT ; PARALLEL PORT BUS DISCONNECT
XOR A ; SIGNAL SUCCESS
RET
;
IMM_CMD_IOERR:
LD A,IMM_STIOERR ; ERROR VALUE TO A
JR IMM_CMD_ERR ; CONTINUE
;
IMM_CMD_TIMEOUT:
LD A,IMM_STTO ; ERROR VALUE TO A
JR IMM_CMD_ERR ; CONTINUE
;
IMM_CMD_ERR:
LD SP,(IMM_CMDSTK) ; UNWIND STACK
PUSH AF ; SAVE STATUS
;CALL IMM_RESETPULSE ; CLEAN UP THE MESS???
LD DE,62 ; DELAY AFTER RESET PULSE
CALL VDELAY
CALL IMM_DISCONNECT ; PARALLEL PORT BUS DISCONNECT
LD DE,62 ; DELAY AFTER DISCONNECT
CALL VDELAY
POP AF ; RECOVER STATUS
JP IMM_ERR ; NOW DO STANDARD ERR PROCESSING
;
; ERRORS SHOULD GENERALLY NOT CAUSE SCSI PROCESSING TO FAIL. IF A
; DEVICE ERROR (I.E., READ ERROR) OCCURS, THEN THE SCSI PROTOCOL WILL
; PROVIDE ERROR INFORMATION. THE STATUS RESULT OF THE SCSI COMMAND
; WILL INDICATE IF AN ERROR OCCURRED. ADDITIONALLY, IF THE ERROR IS
; A CHECK CONDITION ERROR, THEN IT IS MANDATORY TO ISSUE A SENSE
; REQUEST SCSI COMMAND TO CLEAR THE ERROR AND RETRIEVE DETAILED ERROR
; INFO.
;
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 A,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 >= 3)
PRTS("\r\nSENSE:$")
LD A,$19
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
; TODO: WE NEED TO CHECK THE SENSE KEY FIRST!!!
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:
;
#IF (IMMMODE == IMMMODE_MG014)
; 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 ; SHORT DELAY FOR BUS SETTLE
#ENDIF
;
CALL IMM_DISCONNECT ; DISCONNECT FIRST JUST IN CASE
CALL IMM_CONNECT ; NOW CONNECT TO BUS
CALL IMM_RESETPULSE ; ISSUE A SCSI BUS RESET
LD DE,62 ; WAIT A BIT
CALL VDELAY
CALL IMM_DISCONNECT ; AND DISCONNECT FROM BUS
LD DE,62 ; WAIT A BIT MORE
CALL VDELAY
;
; INITIALLY, THE DEVICE MAY REQUIRE MULTIPLE REQUEST SENSE
; COMMANDS BEFORE IT WILL ACCEPT I/O COMMANDS. THIS IS DUE
; TO THINGS LIKE BUS RESET NOTIFICATION, MEDIA CHANGE, ETC.
; HERE, WE RUN A FEW REQUEST SENSE COMMANDS. AS SOON AS ONE
; INDICATES NO ERRORS, WE CAN CONTINUE.
LD B,4 ; TRY UP TO 4 TIMES
IMM_INITDEV1:
PUSH BC ; SAVE LOOP COUNTER
;
; REQUEST SENSE COMMAND
LD DE,HB_WRKBUF ; BUFFER FOR SENSE DATA
LD A,0 ; READ WHATEVER IS SENT
LD HL,IMM_CMD_SENSE ; POINT TO CMD BUFFER
CALL IMM_RUNCMD ; RUN THE SCSI ENGINE
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 ; IF ERROR, LOOP
;
#IF (IMMTRACE >= 3)
PRTS("\r\nSENSE:$")
LD A,$19
LD DE,HB_WRKBUF
CALL PRTHEXBUF
#ENDIF
;
; CHECK SENSE KEY
LD A,(HB_WRKBUF + 2) ; GET SENSE KEY
OR A ; SET FLAGS
;
IMM_INITDEV2:
POP BC ; RESTORE LOOP COUNTER
JR Z,IMM_INITDEV3 ; IF NO ERROR, MOVE ON
DJNZ IMM_INITDEV1 ; TRY UNTIL COUNTER EXHAUSTED
JP IMM_IOERR ; BAIL OUT WITH ERROR
;
IMM_INITDEV3:
; READ & RECORD DEVICE CAPACITY
LD DE,HB_WRKBUF ; BUFFER TO CAPACITY RESPONSE
LD A,0 ; READ WHATEVER IS SENT
LD HL,IMM_CMD_RDCAP ; POINT TO READ CAPACITY CMD
CALL IMM_RUNCMD ; RUN THE SCSI ENGINE
CALL Z,IMM_CHKCMD ; CHECK AND RECORD ANY ERRORS
RET NZ ; BAIL OUT ON ERROR
;
#IF (IMMTRACE >= 3)
PRTS("\r\nRDCAP:$")
LD A,8
LD DE,HB_WRKBUF
CALL PRTHEXBUF
#ENDIF
;
; CAPACITY IS RETURNED IN A 4 BYTE, BIG ENDIAN FIELD AND
; INDICATES THE LAST LBA VALUE. WE NEED TO CONVERT THIS TO
; LITTLE ENDIAN AND INCREMENT THE VALUE TO MAKE IT A CAPACITY
; COUNT INSTEAD OF A LAST LBA VALUE.
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
;
;=============================================================================
; ERROR HANDLING AND DIAGNOSTICS
;=============================================================================
;
; ERROR HANDLERS
;
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)
CALL IMM_PRTPREFIX ; PRINT UNIT PREFIX
CALL PC_SPACE ; FORMATTING
CALL IMM_PRTSTATSTR
POP HL
POP DE
POP AF
RET
;
; PRINT STATUS STRING
;
IMM_PRTSTATSTR:
PUSH AF
PUSH DE
PUSH HL
LD A,(IY+IMM_STAT)
NEG
LD HL,IMM_STR_ST_MAP
ADD A,A
CALL ADDHLA
LD E,(HL)
INC HL
LD D,(HL)
CALL WRITESTR
POP HL
POP DE
POP AF
RET
;
; PRINT DEVICE/UNIT PREFIX
;
IMM_PRTPREFIX:
PUSH AF
CALL NEWLINE
PRTS("IMM$")
LD A,(IY+IMM_DEV) ; GET CURRENT DEVICE NUM
CALL PRTDECB
CALL PC_COLON
POP AF
RET
;
;=============================================================================
; STRING DATA
;=============================================================================
;
IMM_STR_ST_MAP:
.DW IMM_STR_ST_OK
.DW IMM_STR_ST_NOMEDIA
.DW IMM_STR_ST_CMDERR
.DW IMM_STR_ST_IOERR
.DW IMM_STR_ST_TO
.DW IMM_STR_ST_NOTSUP
;
IMM_STR_ST_OK .TEXT "OK$"
IMM_STR_ST_NOMEDIA .TEXT "NO MEDIA$"
IMM_STR_ST_CMDERR .TEXT "COMMAND ERROR$"
IMM_STR_ST_IOERR .TEXT "IO ERROR$"
IMM_STR_ST_TO .TEXT "TIMEOUT$"
IMM_STR_ST_NOTSUP .TEXT "NOT SUPPORTED$"
IMM_STR_ST_UNK .TEXT "UNKNOWN ERROR$"
;
IMM_STR_MODE_MAP:
.DW IMM_STR_MODE_NONE
.DW IMM_STR_MODE_SPP
.DW IMM_STR_MODE_MG014
;
IMM_STR_MODE_NONE .TEXT "NONE$"
IMM_STR_MODE_SPP .TEXT "SPP$"
IMM_STR_MODE_MG014 .TEXT "MG014$"
;
IMM_STR_NOHW .TEXT "NOT PRESENT$"
;
;=============================================================================
; 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_XFRMODE .DB 0 ; 0=VARIABLE, 1=BLOCK (512 BYTES)
IMM_CMDSTAT .DB 0, 0 ; CMD RESULT STATUS
;
; SCSI COMMAND TEMPLATES (LENGTH PREFIXED)
;
.DB 6
IMM_CMD_RW .DB $00, $00, $00, $00, $01, $00 ; READ/WRITE SECTOR
.DB 6
IMM_CMD_SENSE .DB $03, $00, $00, $00, $FF, $00 ; REQUEST SENSE DATA
.DB 10
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 IMMMODE ; DRIVER DEVICE MODE
.DB 0 ; DEVICE STATUS
.DB IMM0BASE ; IO BASE ADDRESS
.DW 0,0 ; DEVICE CAPACITY
.DW 0,0 ; CURRENT LBA
;
DEVECHO "IMM: MODE="
#IF (IMMMODE == IMMMODE_SPP)
DEVECHO "SPP"
#ENDIF
#IF (IMMMODE == IMMMODE_MG014)
DEVECHO "MG014"
#ENDIF
DEVECHO ", IO="
DEVECHO IMM0BASE
DEVECHO "\n"
#ENDIF
;
#IF (IMMCNT >= 2)
;
IMM1_CFG: ; DEVICE 1
.DB 0 ; DRIVER DEVICE NUMBER (FILLED DYNAMICALLY)
.DB IMMMODE ; DRIVER DEVICE MODE
.DB 0 ; DEVICE STATUS
.DB IMM1BASE ; IO BASE ADDRESS
.DW 0,0 ; DEVICE CAPACITY
.DW 0,0 ; CURRENT LBA
;
DEVECHO "IMM: MODE="
#IF (IMMMODE == IMMMODE_SPP)
DEVECHO "SPP"
#ENDIF
#IF (IMMMODE == IMMMODE_MG014)
DEVECHO "MG014"
#ENDIF
DEVECHO ", IO="
DEVECHO IMM1BASE
DEVECHO "\n"
#ENDIF
;
#IF ($ - IMM_CFG) != (IMMCNT * IMM_CFGSIZ)
.ECHO "*** INVALID IMM CONFIG TABLE ***\n"
#ENDIF
;
.DB $FF ; END MARKER