Files
RomWBW/Source/HBIOS/imm.asm
Wayne Warthen f8c800e527 Create DSKY Driver Framework
Added a new driver category for DSKY (Display/Keypad) devices.  Existing DSKY devices were converted into drivers ICM and PKD.  These devices were previously DSKY and DSKYNG.

This removes substantial code duplication and recovers significant space in romldr and dbgmon.
2023-06-28 15:06:53 -07:00

1546 lines
36 KiB
NASM

;
;=============================================================================
; 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
#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
#ENDIF
;
#IF ($ - IMM_CFG) != (IMMCNT * IMM_CFGSIZ)
.ECHO "*** INVALID IMM CONFIG TABLE ***\n"
#ENDIF
;
.DB $FF ; END MARKER