Jim Harper's DDJ SCSI redirector code was online for a while:
1) Google's cache message
2) CDROM.C by Jim Harper
3) ST01.C by Jim Harper
Rod Pemberton
This is Google's cache of http://www.ddj.com/ftp/1993/1993.03/cdrom.txt as
retrieved on Aug 11, 2005 04:35:36 GMT. Google's cache is the snapshot that
we took of the page as we crawled the web. The page may have changed since
that time. Click here for the current page without highlighting. This
cached page may reference images which are no longer available. Click here
for the cached text only. To link to or bookmark this page, use the
following url:
http://www.google.com/search?q=cache:HqpSYfzkDWgJ:www.ddj.com/ftp/
1993/1993.03/cdrom.txt+%22CD-ROM+redirector+for+High+Sierra+and+ISO+9660+dis
ks%22&hl=en
/***************************************************************************
***
* 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 */
}
/***************************************************************************
***
* 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++)
;
}
}