_A DOS REDIRECTOR FOR SCSI CD-ROM_ by Jim Harper [LISTING ONE] /****************************************************************************** * CDROM.C -- by Jim Harper (EXCERPTED LISTING) * A CD-ROM redirector for High Sierra and ISO 9660 disks. *****************************************************************************/ /*...#include directives removed...*/ #define SetC(X) (X) |= 0x01 #define ClrC(X) (X) &= ~0x01 extern unsigned _psp, /* Runtime gives us these variables */ end; char *IOBuf; /* I/O Buffer ptr */ /* Table of saved open SystemFileTable's (SFT's) for DoCloseAll() */ struct SFT _far *CloseTab[MAXCLOSEALL]; unsigned StkSeg, DataSeg, DriveNo, DriveFlags, TsrStkSeg, TsrStkPtr, AppStkSeg, AppStkPtr, CDType, FIDoff, Nameoff, Dateoff, Flagsoff, Blkoff, Sizeoff, BlkSize, ChainFlag, MyStack[STACKSIZE]; unsigned _AX,_BX,_CX,_DX,_DS,_ES,_DI,_FLAGS; struct IntRegs { unsigned ES; unsigned DS; unsigned DI; unsigned SI; unsigned BP; unsigned SP; unsigned BX; unsigned DX; unsigned CX; unsigned AX; unsigned IP; unsigned CS; unsigned FLAGS; }; int Active = 0; struct isoVolDesc *isoVolDescp; struct isoDirRec *isoDp; struct hsVolDesc *hsVolDescp; struct hsDirRec *hsDp; struct Cmd Cmd; struct DirEnt RootEnt, DirCache[CACHESIZE]; /* Important pointers */ struct SDB _far *SDBp; /* Ptr to Dos Search Data Blk */ struct FDB _far *FDBp; /* Ptr to Dos Found Data Blk */ struct LOL _far *LOLp; /* Ptr to list of lists */ struct CDS _far *CDSp; /* Ptr to cur dir tab entry */ /* These pointers are set according to DOS 3.xx or 4.xx */ char _far *SWAPp, /* Ptr to Dos swap area */ _far *FN1p, /* Ptr to Dos resolved name */ _far * _far *DTApp, /* Ptr to Ptr to Current DTA */ _far * _far *SFTpp, /* Ptr to Ptr to Current SFT */ _far *DosDp, /* Ptr to dir ent for file */ _far *Sattrp, /* Ptr to search attr */ _far *OpenModep; /* Ptr to open mode */ unsigned _far *PSPp; /* Ptr to current PSP */ char *HiSierra = "HISIERRA ", *Iso9660 = "ISO9660 "; /*...function prototypes removed...*/ /***********************************************************************/ main(int argc, char *argv[]) { union REGS regs; struct SREGS sregs; unsigned _far *EnvBlkp; int i, Junk, CdsLen, ProgSize; DriveNo = 999; if (argc > 1) { if (!strcmp(argv[1],"-u") || ! strcmp(argv[1],"-U")) { regs.h.ah = 0x11; regs.h.al = DEINSTALL; int86x(INT2F,®s,®s,&sregs); exit(0); } if (argv[1][0] >= 'A' && argv[1][0] <= 'Z' && argv[1][1] == ':' && argv[1][2] == '\0') { DriveNo = argv[1][0] - 'A'; } if (argv[1][0] >= 'a' && argv[1][0] <= 'z' && argv[1][1] == ':' && argv[1][2] == '\0') { DriveNo = argv[1][0] - 'a'; } } if (DriveNo > 26) { MsgOut("usage: cdrom [A-Z]:\r\n"); exit(1); } segread(&sregs); /* Get our stack and data segments */ StkSeg = sregs.ss; DataSeg = sregs.ds; regs.h.ah = FUNCID; /* Check if SCSI TSR is present */ regs.h.al = INSTALLCHK; int86x(INT2F,®s,®s,&sregs); if (regs.h.al != 0xff) { MsgOut("Scsi tsr not found!\r\n"); exit(1); } /* Check if there's a High Sierra or ISO9660 disk in the drive. */ if (ScsiRead(0x10L)) { MsgOut("IO error.\r\n"); exit(1); } hsVolDescp = (struct hsVolDesc *) IOBuf; isoVolDescp = (struct isoVolDesc *) IOBuf; CDType = UNKNOWN; Blkoff = 2; Sizeoff = 10; Dateoff = 18; FIDoff = 32; Nameoff = 33; strcpy(RootEnt.FName,"ROOT-CDROM "); if (strncmp(hsVolDescp->ID,"CDROM",5) == 0) { /* it's High Sierra */ CDType = HIGHSIERRA; Flagsoff = 24; hsDp = (struct hsDirRec *)hsVolDescp->DirRec; RootEnt.Fattr = _A_SUBDIR; RootEnt.FTime = ToDosTime(hsDp->Date); RootEnt.FDate = ToDosDate(hsDp->Date); RootEnt.BlkNo = hsDp->ExtLocLSB; RootEnt.FSize = hsDp->DataLenLSB; RootEnt.ParentBlk = hsDp->ExtLocLSB; BlkSize = hsVolDescp->BlkSizeLSB; MsgOut("High Sierra disk...\r\n"); } if (strncmp(isoVolDescp->ID,"CD001",5) == 0) { /* it's ISO 9660 */ CDType = ISO9660; Flagsoff = 25; isoDp = (struct isoDirRec *)isoVolDescp->DirRec; RootEnt.Fattr = _A_SUBDIR; RootEnt.FTime = ToDosTime(isoDp->Date); RootEnt.FDate = ToDosDate(isoDp->Date); RootEnt.BlkNo = isoDp->ExtLocLSB; RootEnt.FSize = isoDp->DataLenLSB; RootEnt.ParentBlk = isoDp->ExtLocLSB; BlkSize = isoVolDescp->BlkSizeLSB; MsgOut("ISO 9660 disk...\r\n"); } if (CDType == UNKNOWN) { MsgOut("Unknown disk type..\r\n"); exit(1); } regs.h.ah = 0x52; /* Get Address of List of Lists */ int86x(0x21,®s,®s,&sregs); FP_SEG(LOLp) = sregs.es; FP_OFF(LOLp) = regs.x.bx; regs.x.ax = 0x5d06; /* Get address of Dos Swap area */ int86x(0x21,®s,®s,&sregs); FP_SEG(SWAPp) = sregs.ds; FP_OFF(SWAPp) = regs.x.si; if (DriveNo > LOLp->LastDrive) { MsgOut("Drive # to high.\r\n"); exit(1); } MsgOut("DOS version "); ToHex(_osmajor); MsgOut("."); ToHex(_osminor); /* Now set the offsets within Dos according to 3.3x, 4.xx or 5.xx */ if ( _osmajor == 3 && _osminor >= 30) { CdsLen = 0x51; PSPp = (unsigned _far *)(SWAPp + 0x10U); FN1p = SWAPp + 0x0092U; Sattrp = SWAPp + 0x023aU; DosDp = SWAPp + 0x01a7U; SDBp = (struct SDB _far *) (SWAPp + 0x0192U); DTApp = (char _far * _far *)(SWAPp + 0x000cU); SFTpp = (char _far * _far *)(SWAPp + 0x0268U); } else if (_osmajor == 4 || _osmajor == 5) { CdsLen = 0x58; PSPp = (unsigned _far *)(SWAPp + 0x10U); FN1p = SWAPp + 0x009eU; Sattrp = SWAPp + 0x024dU; DosDp = SWAPp + 0x01b3U; SDBp = (struct SDB _far *) (SWAPp + 0x019eU); DTApp = (char _far * _far *)(SWAPp + 0x000cU); SFTpp = (char _far * _far *)(SWAPp + 0x027eU); } else { MsgOut("Not DOS 3.3x, 4.xx or 5.xx\r\n"); exit(1); } /* Cast ptr to table entry pointer */ CDSp = (struct CDS _far *) (LOLp->CDS + DriveNo * CdsLen); DriveFlags = CDSp->Flags; /* Turn on network & physical bits */ CDSp->Flags = 0xC000; CDSp->RootOff = 2; CDSp->CurDir[0] = 'A' + DriveNo; /* Set to root */ CDSp->CurDir[1] = ':'; CDSp->CurDir[2] = '\\'; CDSp->CurDir[3] = '\0'; for (i = 0; i < STACKSIZE; i++) /* Initialize our stack */ MyStack[i] = 0x4141; TsrStkSeg = DataSeg; /* Initialize stack and bottom of program. */ TsrStkPtr = (unsigned)&MyStack[STACKSIZE]; if (TsrStkPtr & 0x1U) /* Make sure stack is on a word boundry */ TsrStkPtr--; /* Program size in paragraphs w/o a heap */ ProgSize = (StkSeg + (((unsigned)&end) >> 4)) - _psp + 1; for (i = 1; i < CACHESIZE - 1; i++) { /* Initialize cache */ DirCache[i].Forw = &DirCache[i+1]; DirCache[i].Back = &DirCache[i-1]; } DirCache[0].Forw = &DirCache[1]; DirCache[0].Back = &RootEnt; DirCache[CACHESIZE-1].Forw = &RootEnt; DirCache[CACHESIZE-1].Back = &DirCache[CACHESIZE-2]; /* Root dirent provides anchor into the cache */ RootEnt.Forw = &DirCache[0]; RootEnt.Back = &DirCache[CACHESIZE-1]; /* Close files */ _dos_close(0); /* stdin */ _dos_close(1); /* stdout */ _dos_close(2); /* stderr */ Old2F = _dos_getvect(INT2F); /* Grab multiplex interrupt */ _dos_setvect(INT2F,New2F); FP_SEG(EnvBlkp) = _psp; /* Free the environment */ FP_OFF(EnvBlkp) = 0x2c; _dos_freemem(*EnvBlkp); _dos_setblock(ProgSize,_psp,&Junk); /* Shrink our program size */ _dos_keep(0,ProgSize); /* TSR ourself */ } /***** New2F(struct IntRegs IntRegs) -- our interrupt 2F handler. *****/ void _interrupt _far New2F(struct IntRegs IntRegs) { /* See if we handle this function */ if ((IntRegs.AX >> 8U) != 0x11 || Active) _chain_intr(Old2F); if ((IntRegs.AX & 0xff) == INSTALLCHK) { /* Install check?? */ IntRegs.AX = 0x00ff; return; } Active++; /* Set flag saying we're active */ ChainFlag = 0; /* Don't chain out by default */ /* Save needed regs from stack */ _AX = IntRegs.AX; _BX = IntRegs.BX; _CX = IntRegs.CX; _DX = IntRegs.DX; _DS = IntRegs.DS; _ES = IntRegs.ES; _DI = IntRegs.DI; _FLAGS = IntRegs.FLAGS; _asm /* Switch to own stack */ { cli ; Interrupts off mov WORD PTR AppStkPtr,sp ; Save app stack mov WORD PTR AppStkSeg,ss mov sp,WORD PTR TsrStkPtr ; Load new stack mov ss,WORD PTR TsrStkSeg sti ; Interrupts on } switch(_AX & 0xff) /* handle the command */ { case DEINSTALL: DeInstall(); break; case CHDIR: DoChDir(); break; case CLOSE: DoClose(); break; case READ: DoRead(); break; case GETSPACE: DoGetSpace(); break; case GETATTR: DoGetAttr(); break; case OPEN: DoOpen(); break; case FINDFIRST: DoFindFirst(); break; case FINDNEXT: DoFindNext(); break; case SEEK: DoSeek(); break; case CLOSEALL: DoCloseAll(); break; case PATHNAME: Spoof(); /* hack */ case 0x25: default: ChainFlag = 1; break; } _asm /* Switch back to app stack */ { cli ; Interrupts off mov sp,WORD PTR AppStkPtr ; Load app stack mov ss,WORD PTR AppStkSeg sti ; Interrupts on } if (ChainFlag) { /* If anyone set the chain flag, chain out */ Active = 0; _chain_intr(Old2F); } /* Restore (possibly modifed) registers */ IntRegs.AX = _AX; IntRegs.BX = _BX; IntRegs.CX = _CX; IntRegs.DX = _DX; IntRegs.FLAGS = _FLAGS; Active = 0; /* Clear Active Flag */ } [LISTING TWO] /****************************************************************************** * ST01.C -- by Jim Harper. (EXCERPTED LISTING) -- A simple SCSI transport TSR * that communicates with CDROM.C module via INT2F. The DoScsi() routine below * handles the actual work of of transferring data to/from the SCSI device. *****************************************************************************/ void DoScsi(void) { struct Cmd _far *Cmdp; unsigned Phase, NumBytes, Byte = 0, i; FP_SEG(Cmdp) = _DS; FP_OFF(Cmdp) = _DX; FP_SEG(Datap) = Cmdp->DSeg; FP_OFF(Datap) = Cmdp->DOff; NumBytes = 512; Endp = Datap; Cmdp->Count = 0L; /* Clear control reg */ *RegPort = 0x00; /* Bus has gotta be free */ if ((*RegPort & BUSYBIT) != 0x00) { Cmdp->Stat = 0x80; return; } /* Clear control reg */ *RegPort = 0x00; /* Assert HBA's address */ *DataPort = 0x80; /* Set the arbitration bit */ *RegPort = (ARBITSTART | PENABLE); /* Wait for arbitration to complete */ for (Timer1 = HZ * 3; (*RegPort & ARBITDONE) == 0x00;) if (!Timer1) { Cmdp->Stat = 0x81; return; } /* OR the target & our ID bits into the data reg */ *DataPort = 0x80 | (0x01 << (unsigned)Cmdp->ID); /* Assert SELect, bus enable, deassert arbitration */ *RegPort = (SEL | PENABLE | BUSENABLE); for (Timer1 = 2; Timer1;) ; /* Wait for BUSY */ for (Timer1 = HZ * 3; (*RegPort & BUSYBIT) == 0x00;) if (!Timer1) { *RegPort = 0x00; Cmdp->Stat = 0x82; return; } /* Drop Select */ *RegPort = (PENABLE | BUSENABLE); /* Wait for command phase */ for (Timer1 = HZ * 3; ((Phase = (*RegPort & PHASEMASK)) != COMMAND);) if (!Timer1) { *RegPort = 0x00; Cmdp->Stat = 0x83; return; } Cmdp->Stat = 0x00; for (Timer1 = HZ * 10;;) { /* Cmd must complete in 10s */ if (!Timer1) { Cmdp->Stat = 0x84; return; } Phase = *RegPort & PHASEMASK; switch(Phase) { case COMMAND: while ((*RegPort & PHASEMASK) == COMMAND) *DataPort = Cmdp->CDB[Byte++]; break; case DATAIN: _asm { push es push ds mov cx,NumBytes les di,Datap lds si,DataPort cld repeat1: movsb dec si loop repeat1 pop ds pop es } Datap += NumBytes; break; case DATAOUT: _asm { push es push ds mov cx,NumBytes les di,DataPort lds si,Datap cld repeat2: movsb dec di loop repeat2 pop ds pop es } break; case STATUS: Cmdp->Stat = *DataPort; break; case MSGIN: Cmdp->Sense = *DataPort; break; case BUSFREE: *RegPort = 0x00; Cmdp->Count += Datap - Endp; return; } /* Delay long enough */ for (i = 0; (*RegPort & REQ) && i < 5; i++) ; } }