; ;================================================================================================== ; CH376 SD CARD SUB-DRIVER ;================================================================================================== ; ; Thanks and credit to Alan Cox. Much of this driver is based on ; his code in FUZIX (https://github.com/EtchedPixels/FUZIX). ; ; This file contains the SD Card specific support for the CH37x ; driver. This file is included by the core driver file (ch.asm) as ; needed. Note that only the CH376 actually supports SD Card access. ; ; The SD Card support is implemented using the CH376 file-level ; support. It is *not* possible to access SD Cards using raw ; sector I/O. ; ; TODO: ; - Implement auto-recovery on error status? ; #DEFINE CHSD_IMGFILE "DISK.IMG" ; CHSD_FASTIO .EQU TRUE ; USE INIR/OTIR? ; ; CHUSB DEVICE STATUS ; CHSD_STOK .EQU 0 CHSD_STNOMEDIA .EQU -1 CHSD_STCMDERR .EQU -2 CHSD_STIOERR .EQU -3 CHSD_STTO .EQU -4 CHSD_STNOTSUP .EQU -5 CHSD_STNOFILE .EQU -6 ; ; CHSD DEVICE CONFIGURATION ; CHSD_CFGSIZ .EQU 14 ; SIZE OF USB CFG TBL ENTRIES ; ; CONFIG ENTRY DATA OFFSETS ; ; THE LOCATION OF CHSD_MODE IS SHARED BY ALL SUB-DRIVERS AND THE ; CH_SETMODE FUNCTION IN THE MAIN DRIVER (CH.ASM). IF YOU CHANGE ; IT, YOU MUST SYNC UP THE MAIN DRIVER AND ALL SUB-DRIVERS! ; ; FIRST 3 BYTES SAME AS CH CONFIG CHSD_STAT .EQU 3 ; LAST STATUS (BYTE) CHSD_MEDCAP .EQU 4 ; MEDIA CAPACITY (DWORD) CHSD_LBA .EQU 8 ; CURRENT LBA (DWORD) CHSD_MODE .EQU 12 ; PTR TO MODE BYTE (WORD) ; CHSD_CFGTBL: ; #IF (CHCNT >= 1) CHSD_CFG0: .DB 0 ; DEV NUM, FILLED DYNAMICALLY .DB CHTYP_NONE ; DEV TYPE, FILLED DYNCAMICALLY .DB CH0BASE ; IO BASE ADDRESS .DB 0 ; DEVICE STATUS .DW 0,0 ; DEVICE CAPACITY .DW 0,0 ; CURRENT LBA .DW CH0_MODE ; POINTER TO MODE BYTE ; #IF (CH0SDENABLE) DEVECHO "CHSD: IO=" DEVECHO CH0BASE DEVECHO "\n" #ENDIF #ENDIF ; #IF (CHCNT >= 2) CHSD_CFG1: .DB 0 ; DEV NUM .DB CHTYP_NONE ; DEV TYPE, FILLED DYNCAMICALLY .DB CH1BASE ; IO BASE ADDRESS .DB 0 ; DEVICE STATUS .DW 0,0 ; DEVICE CAPACITY .DW 0,0 ; CURRENT LBA .DW CH1_MODE ; POINTER TO MODE BYTE ; #IF (CH1SDENABLE) DEVECHO "CHSD: IO=" DEVECHO CH1BASE DEVECHO "\n" #ENDIF #ENDIF ; #IF ($ - CHSD_CFGTBL) != (CHCNT * CHSD_CFGSIZ) .ECHO "*** INVALID CHSD CONFIG TABLE ***\n" #ENDIF ; .DB $FF ; END OF TABLE MARKER ; ; ; CHSD_INIT: LD A,(IY+CH_TYPE) ; GET DEVICE TYPE PUSH HL ; COPY INCOMING HL POP IY ; ... TO IY LD (IY+CH_TYPE),A ; SAVE DEVICE TYPE ; ; UPDATE DRIVER RELATIVE UNIT NUMBER IN CONFIG TABLE LD A,(CHSD_DEVNUM) ; GET NEXT UNIT NUM TO ASSIGN LD (IY+CH_DEV),A ; UPDATE IT INC A ; BUMP TO NEXT UNIT NUM TO ASSIGN LD (CHSD_DEVNUM),A ; SAVE IT ; ; ADD UNIT TO GLOBAL DISK UNIT TABLE LD BC,CHSD_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 CHSD_RESET ; RESET & DISCOVER MEDIA #IF (CHSDTRACE <= 1) CALL NZ,CHSD_PRTSTAT #ENDIF RET NZ ; ABORT ON FAILURE ; ; START PRINTING DEVICE INFO CALL CHSD_PRTPREFIX ; PRINT DEVICE PREFIX ; ; PRINT STORAGE CAPACITY (BLOCK COUNT) PRTS(" BLOCKS=0x$") ; PRINT FIELD LABEL LD A,CHSD_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 ; XOR A ; SIGNAL SUCCESS RET ; ; DRIVER FUNCTION TABLE ; CHSD_FNTBL: .DW CHSD_STATUS .DW CHSD_RESET .DW CHSD_SEEK .DW CHSD_READ .DW CHSD_WRITE .DW CHSD_VERIFY .DW CHSD_FORMAT .DW CHSD_DEVICE .DW CHSD_MEDIA .DW CHSD_DEFMED .DW CHSD_CAP .DW CHSD_GEOM #IF (($ - CHSD_FNTBL) != (DIO_FNCNT * 2)) .ECHO "*** INVALID CHSD FUNCTION TABLE ***\n" #ENDIF ; CHSD_VERIFY: CHSD_FORMAT: CHSD_DEFMED: SYSCHKERR(ERR_NOTIMPL) ; NOT IMPLEMENTED RET ; ; ; CHSD_READ: LD A,CH_MODE_SD ; REQUEST SD MODE CALL CH_SETMODE ; DO IT JP NZ,CHSD_CMDERR ; HANDLE ERROR ; CALL HB_DSKREAD ; HOOK HBIOS DISK READ SUPERVISOR LD (CHSD_DSKBUF),HL ; SAVE DISK BUFFER ADDRESS CALL CHSD_RWSTART ; SET LBA OFFSET RET NZ ; ;PRTS("\n\rREAD:$") ; *DEBUG* LD A,CH_CMD_BYTERD ; BYTE READ CALL CH_CMD ; SEND COMMAND CALL CH_NAP LD A,0 ; LSB CALL CH_WR ; SEND IT LD A,2 ; MSB CALL CH_WR ; SEND IT CALL CH_POLL ; GET RESULT ;CALL PC_SPACE ; *DEBUG* ;CALL PRTHEXBYTE ; *DEBUG* CP $1D ; DATA READY TO READ? JP NZ,CHSD_IOERR ; HANDLE I/O ERROR ; LD HL,(CHSD_DSKBUF) CHSD_READ1: CALL CH_CMD_RD ; SEND READ USB DATA CMD CALL CH_RD ; GET DATA LENGTH ;CALL PC_SPACE ; *DEBUG* ;CALL PRTHEXBYTE ; *DEBUG* ; #IF (CHSD_FASTIO) LD B,A ; BYTE COUNT TO READ LD C,(IY+CH_IOBASE) ; BASE PORT EZ80_IO ;!! NOT SUPPORT INIR YET INIR ; DO IT FAST #ELSE LD B,A ; SAVE IT CHSD_READ2: CALL CH_RD ; GET DATA BYTE LD (HL),A ; SAVE IN BUFFER INC HL ; INC BUF PTR DJNZ CHSD_READ2 ; LOOP TILL DONE W/ ALL BYTES #ENDIF ; LD A,CH_CMD_BYTERDGO ; BYTE READ GO COMMAND CALL CH_CMD ; SEND IT CALL CH_NAP CALL CH_POLL ; GET RESULT ;CALL PC_SPACE ; *DEBUG* ;CALL PRTHEXBYTE ; *DEBUG* CP $1D ; MORE? JR Z,CHSD_READ1 ; IF SO, GET MORE CP $14 ; GOOD FINISH? JP NZ,CHSD_IOERR ; HANDLE ERROR ; ; INCREMENT LBA PUSH HL ; SAVE HL LD A,CHSD_LBA ; LBA OFFSET CALL LDHLIYA ; HL := IY + A, REG A TRASHED CALL INC32HL ; INCREMENT THE VALUE POP HL ; RESTORE HL ; XOR A ; SIGNAL SUCCESS RET ; ; ; CHSD_WRITE: LD A,CH_MODE_SD ; REQUEST SD MODE CALL CH_SETMODE ; DO IT JP NZ,CHSD_CMDERR ; HANDLE ERROR ; CALL HB_DSKWRITE ; HOOK HBIOS DISK WRITE SUPERVISOR LD (CHSD_DSKBUF),HL ; SAVE DISK BUFFER ADDRESS CALL CHSD_RWSTART ; SET LBA OFFSET' RET NZ ; ;PRTS("\n\rWRITE:$") ; *DEBUG* LD A,CH_CMD_BYTEWR ; BYTE WRITE CALL CH_CMD ; SEND COMMAND LD A,0 ; LSB CALL CH_WR ; SEND IT LD A,2 ; MSB CALL CH_WR ; SEND IT CALL CH_POLL ; GET RESULT ;CALL PC_SPACE ; *DEBUG* ;CALL PRTHEXBYTE ; *DEBUG* CP $1E ; DATA READY TO GO? JP NZ,CHSD_IOERR ; HANDLE I/O ERROR ; LD HL,(CHSD_DSKBUF) CHSD_WRITE1: LD A,CH_CMD_WRREQDAT ; WRITE REQUESTED DATA CMD CALL CH_CMD ; SEND IT CALL CH_RD ; GET DATA LENGTH ;CALL PC_SPACE ; *DEBUG* ;CALL PRTHEXBYTE ; *DEBUG* ; #IF (CHSD_FASTIO) LD B,A ; BYTE COUNT TO WRITE LD C,(IY+CH_IOBASE) ; BASE PORT EZ80_IO ;!! NOT SUPPORT OTIR YET OTIR ; DO IT FAST #ELSE LD B,A ; SAVE IT CHSD_WRITE2: CALL CH_WR ; WRITE DATA BYTE LD (HL),A ; SAVE IN BUFFER INC HL ; INC BUF PTR DJNZ CHSD_WRITE2 ; LOOP TILL DONE W/ ALL BYTES #ENDIF ; LD A,CH_CMD_BYTEWRGO ; BYTE WRITE GO COMMAND CALL CH_CMD ; SEND IT CALL CH_NAP CALL CH_POLL ; GET RESULT ;CALL PC_SPACE ; *DEBUG* ;CALL PRTHEXBYTE ; *DEBUG* CP $1E ; MORE? JR Z,CHSD_WRITE1 ; IF SO, SEND MORE CP $14 ; GOOD FINISH? JP NZ,CHSD_IOERR ; HANDLE ERROR ; ; INCREMENT LBA PUSH HL ; SAVE HL LD A,CHSD_LBA ; LBA OFFSET CALL LDHLIYA ; HL := IY + A, REG A TRASHED CALL INC32HL ; INCREMENT THE VALUE POP HL ; RESTORE HL ; XOR A ; SIGNAL SUCCESS RET ; ; SEEK TO CURRENT LBA ; CHSD_RWSTART: ;PRTS("\n\rRWST:$") ; *DEBUG* LD A,CH_CMD_BYTE_LOC ; BYTE LOCATE COMMAND (SEEK) CALL CH_CMD ; SEND IT ; ; GET CURRENT LBA OFFSET LD A,CHSD_LBA ; OFFSET TO CAPACITY FIELD CALL LDHLIYA ; HL := IY + A, REG A TRASHED CALL LD32 ; OFFSET = DE:HL ; ; CONVERT OFFSET FROM LBA TO BYTE LD B,9 CHSD_RWSTART1: SLA L RL H RL E RL D DJNZ CHSD_RWSTART1 ;CALL PRTHEX32 ; *DEBUG* ; ; SEND THE BYTE OFFSET (LSB FIRST) LD A,L CALL CH_WR LD A,H CALL CH_WR LD A,E CALL CH_WR LD A,D CALL CH_WR ; CALL CH_POLL ; WAIT FOR RESPONSE ;CALL PC_SPACE ; *DEBUG* ;CALL PRTHEXBYTE ; *DEBUG* CP $14 ; CHECK RESULT JP NZ,CHSD_CMDERR ; HANDLE CMD ERROR ; XOR A RET ; ; ; CHSD_STATUS: ; RETURN UNIT STATUS LD A,(IY+CHSD_STAT) ; GET STATUS OF SELECTED DEVICE OR A ; SET FLAGS RET ; AND RETURN ; ; RESET THE INTERFACE AND REDISCOVER MEDIA ; CHSD_RESET: ;PRTS("\n\rRES SD:$") ; *DEBUG* ; ; ACTIVATE SD MODE LD A,CH_CMD_MODE ; SET MODE COMMAND CALL CH_CMD ; SEND IT LD A,3 ; SD MODE CALL CH_WR ; SEND IT CALL CH_NAP ; SMALL WAIT CALL CH_RD ; GET RESULT ;CALL PC_SPACE ; *DEBUG* ;CALL PRTHEXBYTE ; *DEBUG* CALL CH_NAP ; SMALL WAIT ; LD A,CH_MODE_SD ; WE ARE NOW IN SD MODE LD L,(IY+CHSD_MODE+0) ; GET MODE PTR (LSB) LD H,(IY+CHSD_MODE+1) ; GET MODE PTR (MSB) LD (HL),A ; SAVE IT ; CALL CHSD_DSKMNT ; MOUNT DISK RET NZ ; ; OPEN DISK IMAGE FILE LD DE,CHSD_FNAME CALL CHSD_FOPEN RET NZ ; ; GET FILESIZE CALL CHSD_FILESIZE RET NZ ; ; SET STATUS AND RETURN XOR A ; CLEAR STATUS LD (IY+CHSD_STAT),A ; RECORD STATUS OR A ; SET FLAGS RET ; AND RETURN ; ; ; CHSD_DEVICE: LD D,DIODEV_CHSD ; D := DEVICE TYPE LD E,(IY+CH_DEV) ; E := PHYSICAL DEVICE NUMBER LD C,%00110010 ; SD HARD DISK ATTRIBUTES LD H,(IY+CH_TYPE) ; H := MODE LD L,(IY+CH_IOBASE) ; L := BASE I/O ADDRESS XOR A ; SIGNAL SUCCESS RET ; ; CHSD_GETMED ; CHSD_MEDIA: LD A,E ; GET FLAGS OR A ; SET FLAGS JR Z,CHSD_MEDIA1 ; JUST REPORT CURRENT STATUS AND MEDIA CALL CHSD_RESET ; RESET CHSD INTERFACE ; CHSD_MEDIA1: LD A,(IY+CHSD_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 ; ; ; CHSD_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+CHSD_LBA+0),L ; SAVE NEW LBA LD (IY+CHSD_LBA+1),H ; ... LD (IY+CHSD_LBA+2),E ; ... LD (IY+CHSD_LBA+3),D ; ... XOR A ; SIGNAL SUCCESS RET ; AND RETURN ; ; ; CHSD_CAP: LD A,(IY+CHSD_STAT) ; GET STATUS PUSH AF ; SAVE IT LD A,CHSD_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 ; ; ; CHSD_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 CHSD_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 CHSD_CAP STATUS ; ; CH37X HELPER ROUTINES ; ; ; PERFORM DISK MOUNT ; CHSD_DSKMNT: ;PRTS("\n\rMOUNT:$") ; *DEBUG* LD A,CH_CMD_DSKMNT ; DISK QUERY CALL CH_CMD ; DO IT CALL CH_POLL ; WAIT FOR RESPONSE ;CALL PC_SPACE ; *DEBUG* ;CALL PRTHEXBYTE ; *DEBUG* CP $82 ; NO DISK? JP Z,CHSD_NOMEDIA ; HANDLE NO MEDIA ERROR CP $14 ; SUCCESS? JP NZ,CHSD_CMDERR ; HANDLE ERROR ; #IF FALSE CALL CH_CMD_RD ; SEND READ COMMAND CALL CH_RD ; GET LENGTH ;CALL PC_SPACE ; *DEBUG* ;CALL PRTHEXBYTE ; *DEBUG* LD B,A ; LOOP COUNTER LD HL,HB_WRKBUF ; USE WORK BUFFER FOR DATA CHSD_DSKMNT1: CALL CH_RD ; GET A BYTE LD (HL),A ; SAVE IT INC HL ; BUMP BUF PTR DJNZ CHSD_DSKMNT1 ; LOOP FOR ALL DATA ; ;LD DE,HB_WRKBUF ; *DEBUG* ;CALL DUMP_BUFFER ; *DEBUG* ; CALL CHSD_PRTPREFIX ; PRINT DEVICE PREFIX LD HL,HB_WRKBUF + 8 LD B,28 CHSD_DSKMNT2: LD A,(HL) INC HL CALL COUT DJNZ CHSD_DSKMNT2 #ENDIF ; XOR A RET ; ; SET FILE NAME ; CHSD_SETFNAME: ;PRTS("\n\rSETFNAME:$") ; *DEBUG* LD A,CH_CMD_SET_FN ; SET FILE NAME COMMAND CALL CH_CMD ; SEND IT CALL CH_NAP ;CALL DELAY ; MAY NOT BE NEEDED ;CALL PC_SPACE ; *DEBUG* CHSD_SETFNAME1: ;CALL DELAY LD A,(DE) ; GET NEXT BYTE INC DE ; BUMP POINTER CALL CH_WR ; SEND IT ;CALL COUT ; *DEBUG* OR A ; CHECK FOR NUL (EOS) RET Z ; IF NUL, DONE JR CHSD_SETFNAME1 ; SEND MORE CHARACTERS ; ; OPEN FILE ; CHSD_FOPEN: CALL CHSD_SETFNAME ;PRTS("\n\rFOPEN:$") ; *DEBUG* LD A,CH_CMD_FOPEN ; FILE OPEN COMMAND CALL CH_CMD ; SEND IT CALL CH_POLL ; WAIT FOR RESULT ;CALL PC_SPACE ; *DEBUG* ;CALL PRTHEXBYTE ; *DEBUG* CP $42 ; MISSING FILE? JP Z,CHSD_NOFILE ; HANDLE ERROR CP $14 ; SUCCESS? JP NZ,CHSD_IOERR ; HANDLE ERROR RET ; RETURN WITH ZF SET APPROPRIATELY ; ; GET FILE SIZE ; CHSD_FILESIZE: ;PRTS("\n\rFSIZE:$") LD A,CH_CMD_FILESIZE ; FILE SIZE COMMAND CALL CH_CMD ; SEND IT LD A,$68 ; REQUIRED CMD PARAMETER CALL CH_WR ; SEND IT CALL CH_NAP LD A,CHSD_MEDCAP ; MEDIA CAPACITY OFFSET CALL LDHLIYA ; HL := IY + A, REG A TRASHED PUSH HL ; SAVE ADDRESS CALL CH_RD LD L,A CALL CH_RD LD H,A CALL CH_RD LD E,A CALL CH_RD LD D,A ;CALL PC_SPACE ; *DEBUG* ;CALL PRTHEX32 ; *DEBUG* LD B,9 ; ROTATE 9 BITS FOR DIV 512 CHSD_FILESIZE1: SRL D RR E RR H RR L DJNZ CHSD_FILESIZE1 ; LOOP TILL DONE POP BC ; RECOVER ADDRESS TO BC ;CALL PC_SPACE ; *DEBUG* ;CALL PRTHEX32 ; *DEBUG* CALL ST32 ; STORE IT XOR A ; SIGNAL SUCCESS RET ; AND DONE ; ; ERROR HANDLERS ; ; CHSD_NOFILE: LD A,CHSD_STNOFILE JR CHSD_ERR ; CHSD_NOMEDIA: LD A,CHSD_STNOMEDIA JR CHSD_ERR ; CHSD_CMDERR: LD A,CHSD_STCMDERR JR CHSD_ERR ; CHSD_IOERR: LD A,CHSD_STIOERR JR CHSD_ERR ; CHSD_TO: LD A,CHSD_STTO JR CHSD_ERR ; CHSD_NOTSUP: LD A,CHSD_STNOTSUP JR CHSD_ERR ; CHSD_ERR: LD (IY+CHSD_STAT),A ; SAVE NEW STATUS ; CHSD_ERR2: #IF (CHSDTRACE >= 2) CALL CHSD_PRTSTAT #ENDIF OR A ; SET FLAGS RET ; ; ; CHSD_PRTERR: RET Z ; DONE IF NO ERRORS ; FALL THRU TO CHSD_PRTSTAT ; ; PRINT FULL DEVICE STATUS LINE ; CHSD_PRTSTAT: PUSH AF PUSH DE PUSH HL LD A,(IY+CHSD_STAT) CALL CHSD_PRTPREFIX ; PRINT UNIT PREFIX CALL PC_SPACE ; FORMATTING CALL CHSD_PRTSTATSTR POP HL POP DE POP AF RET ; ; PRINT STATUS STRING ; CHSD_PRTSTATSTR: PUSH AF PUSH DE PUSH HL LD A,(IY+CHSD_STAT) NEG LD HL,CHSD_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 DIAGNONSTIC PREFIX ; CHSD_PRTPREFIX: PUSH AF CALL NEWLINE PRTS("CHSD$") LD A,(IY+CH_DEV) ; GET CURRENT DEVICE NUM CALL PRTDECB CALL PC_COLON POP AF RET ; ; DATA STORAGE ; CHSD_DEVNUM .DB 0 ; TEMP DEVICE NUM USED DURING INIT CHSD_DSKBUF .DW 0 ; CHSD_FNAME .DB "/", CHSD_IMGFILE, 0 ; CHSD_STR_ST_MAP: .DW CHSD_STR_STOK .DW CHSD_STR_STNOMEDIA .DW CHSD_STR_STCMDERR .DW CHSD_STR_STIOERR .DW CHSD_STR_STTO .DW CHSD_STR_STNOTSUP .DW CHSD_STR_STNOFILE ; CHSD_STR_STOK .TEXT "OK$" CHSD_STR_STNOMEDIA .TEXT "NO MEDIA$" CHSD_STR_STCMDERR .TEXT "COMMAND ERROR$" CHSD_STR_STIOERR .TEXT "IO ERROR$" CHSD_STR_STTO .TEXT "TIMEOUT$" CHSD_STR_STNOTSUP .TEXT "NOT SUPPORTED$" CHSD_STR_STNOFILE .TEXT "MISSING " .TEXT CHSD_IMGFILE .TEXT " FILE$" CHSD_STR_STUNK .TEXT "UNKNOWN ERROR$"