[comp.sources.amiga] v90i081: MSH 1.5 - Messydos File System Handler , Part03/06

Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator) (02/21/90)

Submitted-by: Olaf 'Rhialto' Seibert <U211344%HNYKUN11.BITNET@CUNYVM.CUNY.EDU>
Posting-number: Volume 90, Issue 081
Archive-name: devices/msh-1.5/part03

#!/bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 6)."
# Contents:  src/device.c src/hansec.c
# Wrapped by tadguy@xanth on Tue Feb 20 20:57:10 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/device.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/device.c'\"
else
echo shar: Extracting \"'src/device.c'\" \(18725 characters\)
sed "s/^X//" >'src/device.c' <<'END_OF_FILE'
X/*-
X * $Id: device.c,v 1.5 90/01/27 20:34:43 Rhialto Exp $
X * $Log:	device.c,v $
X * Revision 1.5  90/01/27  20:34:43  Rhialto
X * Commented out #undef DEBUG
X * 
X * Revision 1.4  90/01/23  00:24:50  Rhialto
X * io_Error=0 for immediate commands
X *
X * Revision 1.3  89/12/17  21:29:37  Rhialto
X * Revision 1.1  89/12/17  20:03:55  Rhialto
X *
X * DEVICE.C
X *
X * The messydisk.device code that makes it a real Exec .device.
X * Mostly based on the 1.1 RKM example and Matt Dillon's library code.
X *
X * This code is (C) Copyright 1989 by Olaf Seibert. All rights reserved. May
X * not be used or copied without a licence.
X-*/
X
X#include "dev.h"
X#include "device.h"
X
X/*#undef DEBUG			/**/
X#ifdef DEBUG
X#   define	debug(x)  dbprintf x
X#else
X#   define	debug(x)
X#endif
X
X/*
X * The first executable location. This should return an error in case
X * someone tried to run you as a program (instead of loading you as a
X * device)
X */
X/* INDENT OFF */
X#asm
X	moveq.l #20,d0
X	rts
X#endasm
X/* INDENT ON */
X
X/*
X * A romtag structure. Both "exec" and "ramlib" look for this structure to
X * discover magic constants about you (such as where to start running you
X * from...).
X */
X/* INDENT OFF */
X#asm
X	public	__H0_end
X_EndCode equ	__H0_end
X	public	_RomTag
X_RomTag:
X	dc.w	$4AFC		; RTC_MATCHWORD
X	dc.l	_RomTag 	; rt_MatchTag
X	dc.l	__H0_end	; rt_EndSkip
X	dc.b	0		; rt_Flags (no RTF_AUTOINIT)
X	dc.b	VERSION 	; rt_Version
X	dc.b	3		; rt_Type  NT_DEVICE
X	dc.b	RTPRI		; rt_Pri
X	dc.l	_DevName	; rt_Name
X	dc.l	_idString	; rt_IdString
X	dc.l	_Init		; rt_Init
X#endasm
X/* INDENT ON */
X
Xchar		DevName[] = "messydisk.device";
Xchar		idString[] = "messydisk.device $Revision: 1.5 $ $Date: 90/01/27 20:34:43 $\r\n";
X
X/*
X * -30-6*X  Library vectors:
X */
X
Xvoid		(*LibVectors[]) () =
X{
X    _DevOpen, _DevClose, _DevExpunge, _LibNull,
X
X    _DevBeginIO,
X    _DevAbortIO,
X    (void (*) ()) -1
X};
X
X/*
X * Device commands:
X */
X
Xvoid		(*funcTable[]) () = {
X    CMD_Invalid, CMD_Reset, CMD_Read, CMD_Write, CMD_Update, CMD_Clear,
X    CMD_Stop, CMD_Start, CMD_Flush, TD_Motor, TD_Seek, TD_Format,
X    TD_Remove, TD_Changenum, TD_Changestate, TD_Protstatus, TD_Rawread,
X    TD_Rawwrite, TD_Getdrivetype, TD_Getnumtracks, TD_Addchangeint,
X    TD_Remchangeint,
X};
X
X/*
X * Here begin the system interface commands. When the user calls
X * OpenDevice/CloseDevice/RemDevice, this eventually gets trahslated into
X * a call to the following routines (Open/Close/Expunge).  Exec has
X * already put our device pointer in A6 for us.  Exec has turned off task
X * switching while in these routines (via Forbid/Permit), so we should not
X * take too long in them.
X */
X/* INDENT OFF */
X#asm
X	public	_Init
X_Init:				;a0=segment list
X	movem.l D2-D3/A0/A6,-(sp)
X	jsr	_CInit
X	movem.l (sp)+,D2-D3/A0/A6
X	rts
X
X	public	__DevOpen
X__DevOpen:			;d0=unitnum,d1=flags,a1=ioreq,a6=device
X	movem.l D0-D3/A1/A6,-(sp)
X	jsr	_DevOpen
X	movem.l (sp)+,D0-D3/A1/A6
X	rts
X
X	public	__DevClose
X__DevClose:			;a1=ioreq,a6=device
X	movem.l D2-D3/A1/A6,-(sp)
X	jsr	_DevClose
X	movem.l (sp)+,D2-D3/A1/A6
X	rts
X
X	public	__DevExpunge
X__DevExpunge:			;a6=device
X	movem.l D2-D3/A6,-(sp)
X	jsr	_DevExpunge
X	movem.l (sp)+,D2-D3/A6
X	rts
X
X	public	__LibNull
X__LibNull:
X	clr.l	d0
X	rts
X
X	public	__DevBeginIO
X__DevBeginIO:			;a1=ioreq,a6=device
X	movem.l D2-D3/A1/A6,-(sp)
X	jsr	_DevBeginIO
X	movem.l (sp)+,D2-D3/A1/A6
X	rts
X
X	public	__DevAbortIO
X__DevAbortIO:			;a1=ioreq,a6=device
X	movem.l D2-D3/A1/A6,-(sp)
X	jsr	_DevAbortIO
X	movem.l (sp)+,D2-D3/A1/A6
X	rts
X#endasm
X
X#ifdef HANDLE_IO_QUICK
X#asm
X;;;;
X;
X;   C interface to the atomic set bit and test old value instruction.
X;
X;   Called as	BSET_ACTIVE(byte *address).
X;
X;   Old value of the bit returned all over d0.w
X
X_BSET_ACTIVE:
X	move.l	4(sp),a0
X	bset	#0,(a0)         ; UNITB_ACTIVE
X	sne	d0
X	rts
X
X#endasm
X#endif
X/* INDENT ON */
X
Xlong		SysBase;	/* Argh! A global variable! */
X
X/*
X * The Initialization routine is given only a seglist pointer.	Since we
X * are NOT AUTOINIT we must construct and add the device ourselves and
X * return either NULL or the device pointer.  Exec has Forbid() for us
X * during the call.
X *
X * If you have an extended device structure you must specify the size of the
X * extended structure in MakeLibrary().
X */
X
XDEV	       *
XCInit(D2, D3, segment)
Xulong		D2,
X		D3;
Xlong		segment;
X{
X    DEV 	   *dev;
X
X    SysBase = *(long *) 4;
X#ifdef DEBUG
X    dbinit();
X#endif
X    dev = MakeLibrary(LibVectors, NULL, NULL, (long) sizeof (DEV), NULL);
X    if (DevInit(dev)) {
X	dev->dev_Node.ln_Type = NT_DEVICE;
X	dev->dev_Node.ln_Name = DevName;
X	dev->dev_Flags = LIBF_CHANGED | LIBF_SUMUSED;
X	dev->dev_Version = VERSION;
X	dev->dev_Revision = REVISION;
X	dev->dev_IdString = (APTR) idString;
X	dev->md_Seglist = segment;
X	AddDevice(dev);
X	return (dev);
X    }
X    FreeMem((char *) dev - dev->dev_NegSize, dev->dev_NegSize + dev->dev_PosSize);
X    return NULL;
X}
X
X/*
X * Open is given the device pointer, unitno and flags.	Either return the
X * device pointer or NULL.  Remove the DELAYED-EXPUNGE flag. Exec has
X * Forbid() for us during the call.
X */
X
Xvoid
XDevOpen(unitno, flags, D2, D3, ioreq, dev)
Xulong		unitno;
Xulong		flags;
Xulong		D2,
X		D3;
Xstruct IOStdReq *ioreq;
XDEV	       *dev;
X{
X    UNIT	   *unit;
X
X    debug(("OpenDevice unit %ld, flags %lx\n", unitno, flags));
X    if (unitno >= MD_NUMUNITS)
X	goto error;
X
X    if ((unit = dev->md_Unit[unitno]) == NULL) {
X	if ((unit = UnitInit(dev, unitno)) == NULL)
X	    goto error;
X	dev->md_Unit[unitno] = unit;
X    }
X    ioreq->io_Unit = (struct Unit *) unit;
X
X    ++unit->mu_OpenCnt;
X    ++dev->dev_OpenCnt;
X    dev->dev_Flags &= ~LIBF_DELEXP;
X
X    return;
X
Xerror:
X    ioreq->io_Error = IOERR_OPENFAIL;
X}
X
X/*
X * Close is given the device pointer and the io request.  Be sure not to
X * decrement the open count if already zero.	If the open count is or
X * becomes zero AND there is a LIBF_DELEXP, we expunge the device and
X * return the seglist.	Otherwise we return NULL.
X *
X * Note that this routine never sets LIBF_DELEXP on its own.
X *
X * Exec has Forbid() for us during the call.
X */
X
Xlong
XDevClose(D2, D3, ioreq, dev)
Xulong		D2,
X		D3;
Xstruct IOStdReq *ioreq;
XDEV	       *dev;
X{
X    UNIT	   *unit;
X
X    unit = (UNIT *) ioreq->io_Unit;
X    debug(("CloseDevice io %08lx unit %08lx\n", ioreq, unit));
X
X    /*
X     * See if the unit is still in use. If not, close it down. This may
X     * need to do an update, which requires the ioreq.
X     */
X
X    if (unit->mu_OpenCnt && --unit->mu_OpenCnt == 0) {
X	dev->md_Unit[unit->mu_UnitNr] = NULL;
X	UnitCloseDown(ioreq, dev, unit);
X    }
X    /*
X     * Make sure the ioreq is not used again.
X     */
X    ioreq->io_Unit = (void *) -1;
X    ioreq->io_Device = (void *) -1;
X
X    if (dev->dev_OpenCnt && --dev->dev_OpenCnt)
X	return (NULL);
X    if (dev->dev_Flags & LIBF_DELEXP)
X	return (DevExpunge(D2, D3, dev));
X    return (NULL);
X}
X
X/*
X * We expunge the device and return the Seglist ONLY if the open count is
X * zero. If the open count is not zero we set the DELAYED-EXPUNGE
X * flag and return NULL.
X *
X * Exec has Forbid() for us during the call.  NOTE ALSO that Expunge might be
X * called from the memory allocator and thus we CANNOT DO A Wait() or
X * otherwise take a long time to complete (straight from RKM).
X *
X * Apparently RemLibrary(lib) calls our expunge routine and would therefore
X * freeze if we called it ourselves.  As far as I can tell from RKM,
X * DevExpunge(lib) must remove the device itself as shown below.
X */
X
Xlong
XDevExpunge(D2, D3, dev)
Xulong		D2,
X		D3;
XDEV	       *dev;
X{
X    long	    Seglist;
X
X    if (dev->dev_OpenCnt) {
X	dev->dev_Flags |= LIBF_DELEXP;
X	return (NULL);
X    }
X    Remove(dev);
X    DevCloseDown(dev);          /* Should be quick! */
X#ifdef DEBUG
X    dbuninit();
X#endif
X    Seglist = dev->md_Seglist;
X    FreeMem((char *) dev - dev->dev_NegSize,
X	    (long) dev->dev_NegSize + dev->dev_PosSize);
X    return (Seglist);
X}
X
X/*
X * BeginIO entry point. We don't handle any QUICK requests, we just send
X * the request to the proper unit to handle.
X */
X
Xvoid
XDevBeginIO(D2, D3, ioreq, dev)
Xulong		D2,
X		D3;
Xregister struct IOStdReq *ioreq;
XDEV	       *dev;
X{
X    UNIT	   *unit;
X
X    /*
X     * Bookkeeping.
X     */
X    unit = (UNIT *) ioreq->io_Unit;
X    debug(("BeginIO: io %08lx dev %08lx u %08lx\n", ioreq, dev, unit));
X
X    /*
X     * See if the io command is within range.
X     */
X    if (STRIP(ioreq->io_Command) > TD_LASTCOMM)
X	goto NoCmd;
X
X#ifdef HANDLE_IO_QUICK
X    Forbid();                   /* Disable(); is a bit too strong for us. */
X#endif
X
X    /*
X     * Process all immediate commands no matter what. Don't even require
X     * an exclusive lock on the unit.
X     */
X    if (IMMEDIATE & (1L << STRIP(ioreq->io_Command)))
X	goto Immediate;
X
X    /*
X     * We don't handle any QUICK I/O since that only gives trouble with
X     * message ports and so. Other devices normally would include the code
X     * below.
X     */
X#ifdef HANDLE_IO_QUICK
X    /*
X     * See if the user does not request QUICK IO. If not, it is likely to
X     * be async and therefore we don't do it sync.
X     */
X    if (!(ioreq->io_Flags & IOF_QUICK))
X	goto NoQuickRequested;
X
X    /*
X     * See if the unit is STOPPED. If so, queue the msg.
X     */
X    if (unit->mu_Flags & UNITF_STOPPED)
X	goto QueueMsg;
X
X    /*
X     * This is not an immediate command. See if the device is busy. If
X     * not, process the action in this (the caller's) context.
X     */
X    if (!BSET_ACTIVE(&unit->mu_Flags))
X	goto Immediate;
X#endif
X
X    /*
X     * We need to queue the device. Clear the QUICK flag.
X     */
XQueueMsg:
X    ioreq->io_Flags &= ~IOF_QUICK;
XNoQuickRequested:
X#ifdef HANDLE_IO_QUICK
X    Permit();                   /* Enable(); is a bit too strong for us. */
X#endif
X    PutMsg(&unit->mu_Port, ioreq);
X
X    return;
X
XImmediate:
X#ifdef HANDLE_IO_QUICK
X    Permit();                   /* Enable(); is a bit too strong for us. */
X#endif
X    debug(("BeginIO: Immediate\n"));
X    ioreq->io_Error = TDERR_NoError;
X    PerformIO(ioreq, unit);
X    return;
X
XNoCmd:
X    ioreq->io_Error = IOERR_NOCMD;
X    TermIO(ioreq);
X    return;
X
X}
X
X/*
X * Terminate an io request. Called (normally) for every BeginIO. 'Funny'
X * commands that don't call TermIO, or call it multiple times, may not be
X * properly handled unless you are careful. TD_ADDCHANGEINT and
X * TD_REMCHANGEINT are obvious examples.
X */
X
Xvoid
XTermIO(ioreq)
Xregister struct IOStdReq *ioreq;
X{
X    register UNIT  *unit;
X
X    unit = (UNIT *) ioreq->io_Unit;
X    debug(("TermIO: io %08lx u %08lx %ld %d\n", ioreq, unit,
X	   ioreq->io_Actual, ioreq->io_Error));
X
X#ifdef HANDLE_IO_QUICK
X    /*
X     * Since immediate commands don't even require an exclusive lock on
X     * the unit, don't unlock it.
X     */
X    if (IMMEDIATE & (1L << STRIP(ioreq->io_Command)))
X	goto Immediate;
X
X    /*
X     * We may need to turn the active (lock) bit off, but not if we are
X     * within the task.
X     */
X    if (unit->mu_Flags & UNITF_INTASK)
X	goto Immediate;
X
X    unit->mu_Flags &= ~UNITF_ACTIVE;
X
X    /*
X     * The task may have work to do that came in while we were processing
X     * in the caller's context.
X     */
X    if (unit->mu_Flags & UNITF_WAKETASK) {
X	unit->mu_Flags &= ~UNITF_WAKETASK;
X	WakePort(&unit->mu_Port);
X    }
X#endif
X
XImmediate:
X    /*
X     * If the quick bit is still set then wen don't need to reply the msg
X     * -- just return to the user.
X     */
X
X    if (!(ioreq->io_Flags & IOF_QUICK))
X	ReplyMsg(&ioreq->io_Message);
X
X    return;
X}
X
X/*
X * AbortIO entry point. We don't abort IO here.
X */
X
Xlong
XDevAbortIO(D2, D3, ioreq, dev)
Xulong		D2,
X		D3;
XDEV	       *dev;
Xstruct IOStdReq *ioreq;
X{
X    return 1;
X}
X
Xvoid
XWakePort(port)
Xregister struct MsgPort *port;
X{
X    Signal(port->mp_SigTask, 1L << port->mp_SigBit);
X}
X
X/*
X * This is the main loop of the Unit tasks. It must be very careful with
X * global data.
X */
X
Xvoid
XUnitTask()
X{
X    /* DEV *dev; */
X    UNIT	   *unit;
X    long	    waitmask;
X    struct IOExtTD *ioreq;
X
X    {
X	struct Task    *task,
X		       *FindTask();
X
X	task = FindTask(NULL);
X	unit = (UNIT *) task->tc_UserData;
X	/* dev = unit->mu_Dev; */
X	task->tc_UserData = NULL;
X    }
X
X    /*
X     * Now finish initializing the message ports and other signal things
X     */
X
X    {
X	byte		sigbit;
X
X	unit->mu_DiskReplyPort.mp_SigBit = AllocSignal(-1L);
X	unit->mu_DiskReplyPort.mp_Flags = PA_SIGNAL;
X
X	sigbit = AllocSignal(-1L);
X	unit->mu_Port.mp_SigBit = sigbit;
X	unit->mu_Port.mp_Flags = PA_SIGNAL;
X	waitmask = 1L << sigbit;
X
X	unit->mu_DmaSignal = AllocSignal(-1L);
X    }
X
X    for (;;) {
X	debug(("Task: Waiting... "));
X	Wait(waitmask);
X
X	/*
X	 * See if we are stopped.
X	 */
X	if (unit->mu_Flags & UNITF_STOPPED)
X	    continue;
X
X#ifdef HANDLE_IO_QUICK
X	/*
X	 * Lock the device. If it fails, we have set a flag such that the
X	 * TermIO wakes us again.
X	 */
X	unit->mu_Flags |= UNITF_WAKETASK;
X	if (BSET_ACTIVE(&unit->mu_Flags))
X	    continue;
X
X	unit->mu_Flags |= UNITF_INTASK;
X#endif
X
X	while (ioreq = (struct IOExtTD *) GetMsg(&unit->mu_Port)) {
X	    debug(("Task: io %08lx %x\n", ioreq, ioreq->iotd_Req.io_Command));
X	    ioreq->iotd_Req.io_Error = 0;
X	    PerformIO((&ioreq->iotd_Req), unit);
X	}
X
X#ifdef HANDLE_IO_QUICK
X	unit->mu_Flags &= ~(UNITF_ACTIVE | UNITF_INTASK | UNITF_WAKETASK);
X#endif
X    }
X}
X
Xvoid
XCMD_Invalid(ioreq, unit)
Xstruct IOStdReq *ioreq;
XUNIT	       *unit;
X{
X    ioreq->io_Error = IOERR_NOCMD;
X    TermIO(ioreq);
X}
X
Xvoid
XCMD_Stop(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    unit->mu_Flags |= UNITF_STOPPED;
X    TermIO(ioreq);
X}
X
Xvoid
XCMD_Start(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    unit->mu_Flags &= ~UNITF_STOPPED;
X    WakePort(&unit->mu_Port);
X    TermIO(ioreq);
X}
X
Xvoid
XCMD_Flush(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    register struct IOStdReq *req;
X
X    /* Flush our own command queue */
X    Forbid();
X    while (req = (struct IOStdReq *) GetMsg(unit->mu_Port)) {
X	req->io_Error = IOERR_ABORTED;
X	ReplyMsg(&req->io_Message);
X    }
X    Permit();
X
X    WakePort(&unit->mu_Port);
X    TermIO(ioreq);
X}
X
Xvoid
XTrackdiskGateway(ioreq, unit)
Xregister struct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    register struct IOExtTD *tdioreq;
X
X    debug(("Trackdisk: %x ", ioreq->iotd_Req.io_Command));
X    tdioreq = unit->mu_DiskIOReq;
X
X    /*
X     * Clone almost the entire io request to relay to the
X     * trackdisk.device.
X     */
X
X    tdioreq->iotd_Req.io_Command = ioreq->iotd_Req.io_Command;
X    tdioreq->iotd_Req.io_Flags = ioreq->iotd_Req.io_Flags | IOF_QUICK;
X    tdioreq->iotd_Req.io_Length = ioreq->iotd_Req.io_Length;
X    tdioreq->iotd_Req.io_Data = ioreq->iotd_Req.io_Data;
X    tdioreq->iotd_Req.io_Offset = ioreq->iotd_Req.io_Offset;
X    if (ioreq->iotd_Req.io_Command & TDF_EXTCOM) {
X	tdioreq->iotd_Count = ioreq->iotd_Count;
X	tdioreq->iotd_SecLabel = ioreq->iotd_SecLabel;
X    }
X    BeginIO(tdioreq);
X    WaitIO(tdioreq);
X
X    ioreq->iotd_Req.io_Error = tdioreq->iotd_Req.io_Error;
X    ioreq->iotd_Req.io_Actual = tdioreq->iotd_Req.io_Actual;
X
X    TermIO(ioreq);
X}
X
X#ifdef DEBUG
X/* DEBUGGING			    */
Xstruct MsgPort *Dbport; 	/* owned by the debug process	    */
Xstruct MsgPort *Dback;		/* owned by the DOS device driver  */
Xshort		DBEnable;
Xstruct SignalSemaphore PortUse;
X
X#define CTOB(x) (void *)(((long)(x))>>2)        /* BCPL conversion */
X
X/*
X * DEBUGGING CODE.	You cannot make DOS library calls that access
X * other devices from within a device driver because the caller may not be
X * a process.  If you need to make such calls you must create a port and
X * construct the DOS messages yourself.  I do not do this.  To get
X * debugging info out another PROCESS is created to which debugging
X * messages can be sent. The replyport gets a new SigTask for every
X * dbprintf call, therefore the semaphore.
X */
X
Xextern void	debugproc();
Xstruct Library *DOSBase,
X	       *OpenLibrary();
X
Xdbinit()
X{
X    struct Task    *task = FindTask(NULL);
X
X    DOSBase = OpenLibrary("dos.library", 0L);
X    Dback = CreatePort("Dback", -1L);
X    FreeSignal((long) Dback->mp_SigBit);
X    Dback->mp_SigBit = 2;
X    InitSemaphore(&PortUse);
X    CreateProc("messydisk_DB", (long) TASKPRI + 1, CTOB(debugproc), 2000L);
X    WaitPort(Dback);            /* handshake startup    */
X    GetMsg(Dback);              /* remove dummy msg     */
X    DBEnable = 1;
X    dbprintf("Debugger running V1.11\n");
X}
X
Xdbuninit()
X{
X    struct Message  killmsg;
X
X    if (Dbport) {
X	killmsg.mn_Length = 0;	/* 0 means die	    */
X	ObtainSemaphore(&PortUse);
X	Dback->mp_SigTask = FindTask(NULL);
X	PutMsg(Dbport, &killmsg);
X	WaitPort(Dback);        /* He's dead jim!      */
X	GetMsg(Dback);
X	ReleaseSemaphore(&PortUse);
X	Dback->mp_SigBit = -1;
X	DeletePort(Dback);
X
X	/*
X	 * Since the debug process is running at a greater priority, I am
X	 * pretty sure that it is guarenteed to be completely removed
X	 * before this task gets control again.
X	 */
X    }
X    CloseLibrary(DOSBase);
X}
X
Xdbprintf(a, b, c, d, e, f, g, h, i, j)
Xlong		a, b, c, d, e, f, g, h, i, j;
X{
X    struct {
X	struct Message	msg;
X	char		buf[256];
X    }		    msgbuf;
X    register struct Message *msg = &msgbuf.msg;
X    register long   len;
X
X    if (Dbport && DBEnable) {
X	ObtainSemaphore(&PortUse);      /* sprintf is not re-entrant */
X	sprintf(msgbuf.buf, a, b, c, d, e, f, g, h, i, j);
X	len = strlen(msgbuf.buf) + 1;
X	msg->mn_Length = len;	/* Length NEVER 0	 */
X	Dback->mp_SigTask = FindTask(NULL);
X	PutMsg(Dbport, msg);
X	WaitPort(Dback);
X	GetMsg(Dback);
X	ReleaseSemaphore(&PortUse);
X    }
X}
X
X/*
X * BTW, the DOS library used by debugmain() was actually opened by the
X * opener of the device driver.
X */
X
Xdebugmain()
X{
X    register struct Message *msg;
X    register long   len;
X    register void  *fh,
X		   *Open();
X    void	   *fh2;
X    struct Message  DummyMsg;
X
X    Dbport = CreatePort("Dbport", -1L);
X    fh = Open("CON:0/20/640/101/Device debug", MODE_NEWFILE);
X    fh2 = Open("PAR:", MODE_OLDFILE);
X    PutMsg(Dback, &DummyMsg);
X    for (;;) {
X	WaitPort(Dbport);
X	msg = GetMsg(Dbport);
X	len = msg->mn_Length;
X	if (len == 0)
X	    break;
X	--len;			/* Fix length up	 */
X	if (DBEnable & 1)
X	    Write(fh, msg + 1, len);
X	if (DBEnable & 2)
X	    Write(fh2, msg + 1, len);
X	PutMsg(Dback, msg);
X    }
X    Close(fh);
X    Close(fh2);
X    DeletePort(Dbport);
X    PutMsg(Dback, msg);         /* Kill handshake  */
X}
X
X/*
X * The assembly tag for the DOS process:  CNOP causes alignment problems
X * with the Aztec assembler for some reason.  I assume then, that the
X * alignment is unknown.  Since the BCPL conversion basically zero's the
X * lower two bits of the address the actual code may start anywhere within
X * 8 bytes of address (remember the first longword is a segment pointer
X * and skipped).  Sigh....  (see CreateProc() above).
X */
X/* INDENT OFF */
X#asm
X	public	_debugproc
X	public	_debugmain
X
X	cseg
X_debugproc:
X	nop
X	nop
X	nop
X	nop
X	nop
X	movem.l D2-D7/A2-A6,-(sp)
X	jsr	_debugmain
X	movem.l (sp)+,D2-D7/A2-A6
X	rts
X#endasm
X
X#endif				/* DEBUG */
END_OF_FILE
if test 18725 -ne `wc -c <'src/device.c'`; then
    echo shar: \"'src/device.c'\" unpacked with wrong size!
fi
# end of 'src/device.c'
fi
if test -f 'src/hansec.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/hansec.c'\"
else
echo shar: Extracting \"'src/hansec.c'\" \(19261 characters\)
sed "s/^X//" >'src/hansec.c' <<'END_OF_FILE'
X/*-
X * $Id: hansec.c,v 1.4 90/02/10 21:30:54 Rhialto Exp $
X * $Log:	hansec.c,v $
X * Revision 1.4  90/02/10  21:30:54  Rhialto
X * Tuned cache a bit.
X * 
X * Revision 1.3  90/01/27  20:20:16  Rhialto
X * Sorted sectors when flushing cache
X *
X * Revision 1.2  90/01/23  02:31:50  Rhialto
X * Add 16-bit FAT support.
X *
X * Revision 1.1  89/12/17  20:02:49  Rhialto
X * Initial revision
X *
X * HANSEC.C
X *
X * The code for the messydos file system handler.
X *
X * Sector-level stuff: read, write, cache, unit conversion.
X * Other interactions (via MyDoIO) with messydisk.device.
X *
X * This code is (C) Copyright 1989,1990 by Olaf Seibert. All rights reserved.
X * May not be used or copied without a licence.
X-*/
X
X#include "han.h"
X#include "dos.h"
X
X/*#undef DEBUG				/**/
X#ifdef DEBUG
X#   define	debug(x)  dbprintf x
X#else
X#   define	debug(x)
X#endif
X
Xstruct MsgPort *DiskReplyPort;
Xstruct IOExtTD *DiskIOReq;
Xstruct IOStdReq *DiskChangeReq;
X
Xstruct DiskParam Disk;
Xbyte	       *Fat;
Xshort		FatDirty;	/* Fat must be written to disk */
X
Xshort		error;		/* To put the error value; for Result2 */
Xlong		IDDiskState;	/* InfoData.id_DiskState */
Xlong		IDDiskType;	/* InfoData.id_DiskType */
Xstruct timerequest *TimeIOReq;	/* For motor-off delay */
Xstruct MinList	CacheList;	/* Sector cache */
Xint		CurrentCache;	/* How many cached buffers do we have */
Xint		MaxCache = 5;	/* Maximum amount of cached buffers */
Xlong		CacheBlockSize; /* Size of disk block + overhead */
Xulong		BufMemType;
Xint		DelayState;
X
Xbyte	       *Word8086;
X
Xword
XGet8086Word(offset)
Xregister int	offset;
X{
X    return Word8086[offset] | Word8086[offset + 1] << 8;
X}
X
Xword
XOtherEndianWord(oew)
Xword		oew;
X{
X/* INDENT OFF */
X#asm
X	move.w	8(a5),d0
X	rol.w	#8,d0
X#endasm
X    /* INDENT ON */
X    /*
X     * return (oew << 8) | ((oew >> 8) & 0xff);
X     */
X}
X
Xulong
XOtherEndianLong(oel)
Xulong		oel;
X{
X/* INDENT OFF */
X#asm
X	move.l	8(a5),d0
X	rol.w	#8,d0
X	swap	d0
X	rol.w	#8,d0
X#endasm
X    /* INDENT ON */
X    /*
X     * return ((oel &       0xff) << 24) | ((oel &     0xff00) <<  8) |
X     * ((oel &   0xff0000) >>  8) | ((oel & 0xff000000) >> 24);
X     */
X}
X
Xvoid
XOtherEndianMsd(msd)
Xregister struct MsDirEntry *msd;
X{
X    msd->msd_Date = OtherEndianWord(msd->msd_Date);
X    msd->msd_Time = OtherEndianWord(msd->msd_Time);
X    msd->msd_Cluster = OtherEndianWord(msd->msd_Cluster);
X    msd->msd_Filesize = OtherEndianLong(msd->msd_Filesize);
X}
X
Xword
XClusterToSector(cluster)
Xregister word	cluster;
X{
X    return cluster ? Disk.start + cluster * Disk.spc
X	: 0;
X}
X
Xword
XClusterOffsetToSector(cluster, offset)
Xregister word	cluster;
Xregister word	offset;
X{
X    return cluster ? Disk.start + cluster * Disk.spc + offset / Disk.bps
X	: 0;
X}
X
Xword
XDirClusterToSector(cluster)
Xregister word	cluster;
X{
X    return cluster ? Disk.start + cluster * Disk.spc
X	: Disk.rootdir;
X}
X
Xword
XSectorToCluster(sector)
Xregister word	sector;
X{
X    return sector ? (sector - Disk.start) / Disk.spc
X	: 0;
X}
X
X/*
X * Get the next cluster in a chain. Sort-of checks for special entries.
X */
X
Xword
XNextCluster(cluster)
Xword cluster;
X{
X    register word entry;
X
X    return (entry = GetFatEntry(cluster)) >= 0xFFF0 ? FAT_EOF : entry;
X}
X
Xword
XNextClusteredSector(sector)
Xword		sector;
X{
X    word	    next = (sector + 1 - Disk.start) % Disk.spc;
X
X    if (next == 0) {
X	next = NextCluster(SectorToCluster(sector));
X	return next != FAT_EOF ? ClusterToSector(next)
X	    : SEC_EOF;
X    } else
X	return sector + 1;
X}
X
X#ifndef READONLY
X
Xword
XFindFreeSector(prev)
Xword		prev;
X{
X    word freecluster = FindFreeCluster(SectorToCluster(prev));
X
X    return freecluster == FAT_EOF ? SEC_EOF : ClusterToSector(freecluster);
X}
X
X#endif
X
X/*
X * Find a specific sector. The cache list is a Least Recently Used stack:
X * Put it on the head of the cache list. So if it is not used anymore in a
X * long time, it bubbles to the end of the list, getting a higher chance
X * of being trashed for re-use.
X */
X
Xstruct CacheSec *
XFindSecByNumber(number)
Xregister int	number;
X{
X    register struct CacheSec *sec;
X    register struct CacheSec *nextsec;
X
X    debug(("FindSecByNumber %d", number));
X
X    for (sec = (void *) CacheList.mlh_Head;
X	 nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
X	if (sec->sec_Number == number) {
X	    debug((" (%x) %lx\n", sec->sec_Refcount, sec));
X	    Remove(sec);
X	    AddHead(&CacheList, &sec->sec_Node);
X	    return sec;
X	}
X    }
X
X    debug(("; "));
X    return NULL;
X}
X
Xstruct CacheSec *
XFindSecByBuffer(buffer)
Xbyte	       *buffer;
X{
X    return (struct CacheSec *) (buffer - OFFSETOF(CacheSec, sec_Data));
X}
X
X/*
X * Get a fresh cache buffer. If we are allowed more cache, we just
X * allocate memory. Otherwise, we try to find a currently unused buffer.
X * We start looking at the end of the list, which is the bottom of the LRU
X * stack. If that fails, allocate more memory anyway. Not that is likely
X * anyway, since we currently lock only one sector at a time.
X */
X
Xstruct CacheSec *
XNewCacheSector()
X{
X    register struct CacheSec *sec;
X    register struct CacheSec *nextsec;
X
X    debug(("NewCacheSector\n"));
X
X    if (CurrentCache < MaxCache) {
X	if (sec = AllocMem(CacheBlockSize, BufMemType)) {
X	    goto add;
X	}
X    }
X    for (sec = (void *) CacheList.mlh_TailPred;
X	 nextsec = (void *) sec->sec_Node.mln_Pred; sec = nextsec) {
X	if ((CurrentCache >= MaxCache) && (sec->sec_Refcount == SEC_DIRTY)) {
X	    FreeCacheSector(sec);       /* Also writes it to disk */
X	    continue;
X	}
X	if (sec->sec_Refcount == 0)     /* Implies not SEC_DIRTY */
X	    return sec;
X    }
X
X    sec = AllocMem(CacheBlockSize, BufMemType);
X
X    if (sec) {
Xadd:
X	CurrentCache++;
X	AddHead(&CacheList, &sec->sec_Node);
X    } else
X	error = ERROR_NO_FREE_STORE;
X
X    return sec;
X}
X
X/*
X * Dispose a cached sector, even if it has a non-zero refcount. If it is
X * dirty, write it out.
X */
X
Xvoid
XFreeCacheSector(sec)
Xregister struct CacheSec *sec;
X{
X    debug(("FreeCacheSector %d\n", sec->sec_Number));
X    Remove(sec);
X#ifndef READONLY
X    if (sec->sec_Refcount & SEC_DIRTY) {
X	PutSec(sec->sec_Number, sec->sec_Data);
X    }
X#endif
X    FreeMem(sec, CacheBlockSize);
X    CurrentCache--;
X}
X
X/*
X * Create an empty cache list
X */
X
Xvoid
XInitCacheList()
X{
X    extern struct CacheSec *sec;    /* Of course this does not exist... */
X
X    NewList(&CacheList);
X    CurrentCache = 0;
X    CacheBlockSize = Disk.bps + sizeof (*sec) - sizeof (sec->sec_Data);
X}
X
X/*
X * Dispose all cached sectors, possibly writing them to disk.
X */
X
Xvoid
XFreeCacheList()
X{
X    register struct CacheSec *sec;
X
X    debug(("FreeCacheList, %d\n", CurrentCache));
X    while (sec = GetHead(&CacheList)) {
X	FreeCacheSector(sec);
X    }
X}
X
X/*
X * Do an insertion sort on tosort in the CacheList. Since it changes the
X * location in the list, you must fetch it before calling this routine.
X * The list will become ascending.
X */
X
Xvoid
XSortSec(tosort)
Xregister struct CacheSec *tosort;
X{
X    register struct CacheSec *sec;
X    struct CacheSec *nextsec;
X    register word   secno;
X
X    secno = tosort->sec_Number;
X    debug(("SortSec %d: ", secno));
X
X    for (sec = (void *) CacheList.mlh_Head;
X	 nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
X	debug(("%d, ", sec->sec_Number));
X	if (sec == tosort) {
X	    debug(("\n"));
X	    return;			/* No need to move it away */
X	}
X	if (sec->sec_Number > secno)
X	    break;
X    }
X    /* Insert before sec */
X    Remove(tosort);
X    Insert(&CacheList, tosort, sec->sec_Node.mln_Pred);
X    debug(("\n"));
X}
X
X/*
X * Write all dirty cache buffers to disk. They are written from highest to
X * lowest, and then the FAT is written out.
X */
X
Xvoid
XMSUpdate(immediate)
Xint		immediate;
X{
X    register struct CacheSec *sec;
X    register struct CacheSec *nextsec;
X
X    debug(("MSUpdate\n"));
X
X#ifndef READONLY
X    if (DelayState & DELAY_DIRTY) {
X	/*
X	 * First sort all dirty sectors on block number
X	 */
X	for (sec = (void *) CacheList.mlh_Head;
X	     nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
X	    if (sec->sec_Refcount & SEC_DIRTY) {
X		SortSec(sec);
X	    }
X	}
X	/*
X	 * Then do a second (backward) scan to write them out.
X	 */
X	for (sec = (void *) CacheList.mlh_TailPred;
X	     nextsec = (void *) sec->sec_Node.mln_Pred; sec = nextsec) {
X	    if (sec->sec_Refcount & SEC_DIRTY) {
X		PutSec(sec->sec_Number, sec->sec_Data);
X		sec->sec_Refcount &= ~SEC_DIRTY;
X	    }
X	}
X	DelayState &= ~DELAY_DIRTY;
X    }
X    if (FatDirty) {
X	WriteFat();
X    }
X#endif
X
X    if (immediate)
X	DelayState = DELAY_RUNNING1;
X
X    if (DelayState & DELAY_RUNNING2) {
X	StartTimer();
X	DelayState &= ~DELAY_RUNNING2;
X    } else {			/* DELAY_RUNNING1 */
X#ifndef READONLY
X	while (TDUpdate() != 0 && RetryRwError(DiskIOReq))
X	    ;
X#endif
X	TDMotorOff();
X	DelayState = DELAY_OFF;
X    }
X}
X
X/*
X * Start the timer which triggers cache writing and stopping the disk
X * motor.
X */
X
Xvoid
XStartTimer()
X{
X    DelayState |= DELAY_RUNNING1 | DELAY_RUNNING2;
X
X    if (CheckIO(TimeIOReq)) {
X	WaitIO(TimeIOReq);
X	TimeIOReq->tr_node.io_Command = TR_ADDREQUEST;
X	TimeIOReq->tr_time.tv_secs = 3;
X	TimeIOReq->tr_time.tv_micro = 0;
X	SendIO(TimeIOReq);
X    }
X}
X
X/*
X * Get a pointer to a logical sector { 0, ..., MS_SECTCNT - 1}. We
X * allocate a buffer and copy the data in, and lock the buffer until
X * FreeSec() is called.
X */
X
Xbyte	       *
XGetSec(sector)
Xint		sector;
X{
X    struct CacheSec *sec;
X
X    if (sec = FindSecByNumber(sector)) {
X	sec->sec_Refcount++;
X
X	return sec->sec_Data;
X    }
X    if (sec = NewCacheSector()) {
X	register struct IOExtTD *req;
X
X	sec->sec_Number = sector;
X	sec->sec_Refcount = 1;
X
X	debug(("GetSec %d\n", sector));
X
X	req = DiskIOReq;
X	do {
X	    req->iotd_Req.io_Command = ETD_READ;
X	    req->iotd_Req.io_Data = (APTR)sec->sec_Data;
X	    req->iotd_Req.io_Offset = Disk.lowcyl + (long) sector * Disk.bps;
X	    req->iotd_Req.io_Length = Disk.bps;
X	    MyDoIO(req);
X	} while (req->iotd_Req.io_Error != 0 && RetryRwError(req));
X
X	StartTimer();
X
X	if (req->iotd_Req.io_Error == 0) {
X	    return sec->sec_Data;
X	}
X	error = ERROR_NOT_A_DOS_DISK;
X	FreeCacheSector(sec);
X    }
X    return NULL;
X}
X
X#ifndef READONLY
X
Xbyte	       *
XEmptySec(sector)
Xint		sector;
X{
X    byte	   *buffer;
X    register struct CacheSec *sec;
X
X    if (sec = FindSecByNumber(sector)) {
X	sec->sec_Refcount++;
X
X	return sec->sec_Data;
X    }
X    if (sec = NewCacheSector()) {
X	sec->sec_Number = sector;
X	sec->sec_Refcount = 1;
X
X	return sec->sec_Data;
X    }
X
X    return NULL;
X}
X
Xvoid
XPutSec(sector, data)
Xint		sector;
Xbyte	       *data;
X{
X    register struct IOExtTD *req;
X
X    debug(("PutSec %d\n", sector));
X
X    req = DiskIOReq;
X    do {
X	req->iotd_Req.io_Command = ETD_WRITE;
X	req->iotd_Req.io_Data = (APTR) data;
X	req->iotd_Req.io_Offset = Disk.lowcyl + (long) sector * Disk.bps;
X	req->iotd_Req.io_Length = Disk.bps;
X	MyDoIO(req);
X    } while (req->iotd_Req.io_Error != 0 && RetryRwError(req));
X
X    StartTimer();
X}
X
X#endif
X
X/*
X * Unlock a cached sector. When the usage count drops to zero, which
X * implies it is not dirty, and we are over our cache quota, the sector is
X * freed. Otherwise we keep it for re-use.
X */
X
Xvoid
XFreeSec(buffer)
Xbyte	       *buffer;
X{
X    register struct CacheSec *sec;
X
X    if (sec = FindSecByBuffer(buffer)) {
X	if (--sec->sec_Refcount == 0) { /* Implies not SEC_DIRTY */
X	    if (CurrentCache > MaxCache) {
X		FreeCacheSector(sec);
X	    }
X	}
X    }
X}
X
X#ifndef READONLY
X
Xvoid
XMarkSecDirty(buffer)
Xbyte	       *buffer;
X{
X    register struct CacheSec *sec;
X
X    if (sec = FindSecByBuffer(buffer)) {
X	sec->sec_Refcount |= SEC_DIRTY;
X	DelayState |= DELAY_DIRTY;
X	StartTimer();
X    }
X}
X
X/*
X * Write out the FAT. Called from MSUpdate(), so don't call it again from
X * here. Don't use precious cache space for it; you could say it has its
X * own private cache already.
X */
X
Xvoid
XWriteFat()
X{
X    register int    fat,
X		    sec;
X    int 	    disksec = Disk.res;      /* First FAT, first sector */
X
X    /* Write all FATs */
X    for (fat = 0; fat < Disk.nfats; fat++) {
X	for (sec = 0; sec < Disk.spf; sec++) {
X	    PutSec(disksec++, Fat + sec * Disk.bps);
X	    /* return;	       /* Fat STILL dirty! */
X	}
X    }
X    FatDirty = FALSE;
X}
X
X#endif
X
Xint
XReadBootBlock()
X{
X    int protstatus;
X
X    debug(("ReadBootBlock\n"));
X    FreeFat();                  /* before disk parameters change */
X    TDClear();
X
X    if ((protstatus = TDProtStatus()) >= 0) {
X	TDChangeNum();
X	debug(("Changenumber = %ld\n", DiskIOReq->iotd_Count));
X	if (Word8086 = GetSec(0)) {
X	    word bps;
X
X	    /* 8086 ml for a jump */
X	    if (Word8086[0] != 0xE9 && Word8086[0] != 0xEB) {
X		goto nodisk;
X	    }
X	    bps = Get8086Word(0x0b);
X	    Disk.spc = Word8086[0x0d];
X	    Disk.res = Get8086Word(0x0e);
X	    Disk.nfats = Word8086[0x10];
X	    Disk.ndirs = Get8086Word(0x11);
X	    Disk.nsects = Get8086Word(0x13);
X	    Disk.media = Word8086[0x15];
X	    Disk.spf = Get8086Word(0x16);
X	    Disk.spt = Get8086Word(0x18);
X	    Disk.nsides = Get8086Word(0x1a);
X	    Disk.nhid = Get8086Word(0x1c);
X	    FreeSec(Word8086);
X
X	    /*
X	     *	Maybe the sector size just changed. Who knows?
X	     */
X	    if (Disk.bps != bps) {
X		FreeCacheList();
X		Disk.bps = bps;
X		InitCacheList();
X	    }
X
X	    Disk.ndirsects = (Disk.ndirs * MS_DIRENTSIZE) / Disk.bps;
X	    Disk.rootdir = Disk.res + Disk.spf * Disk.nfats;
X	    Disk.datablock = Disk.rootdir + Disk.ndirsects;
X	    Disk.start = Disk.datablock - MS_FIRSTCLUST * Disk.spc;
X	    /* Available clusters are 2..maxclust in secs start..nsects-1 */
X	    Disk.maxclst = (Disk.nsects - Disk.start) / Disk.spc - 1;
X	    Disk.bpc = Disk.bps * Disk.spc;
X	    Disk.vollabel = FakeRootDirEntry;
X/*	    Disk.fat16bits = Disk.nsects > 20740;   /* DOS3.2 magic value */
X	    Disk.fat16bits = Disk.maxclst > 0xFF6;  /* DOS3.0 magic value */
X
X	    debug(("%x\tbytes per sector\n", Disk.bps));
X	    debug(("%x\tsectors per cluster\n", Disk.spc));
X	    debug(("%x\treserved blocks\n", Disk.res));
X	    debug(("%x\tfats\n", Disk.nfats));
X	    debug(("%x\tdirectory entries\n", Disk.ndirs));
X	    debug(("%x\tsectors\n", Disk.nsects));
X	    debug(("%x\tmedia byte\n", Disk.media));
X	    debug(("%x\tsectors per FAT\n", Disk.spf));
X	    debug(("%x\tsectors per track\n", Disk.spt));
X	    debug(("%x\tsides\n", Disk.nsides));
X	    debug(("%x\thidden sectors\n", Disk.nhid));
X
X	    debug(("%x\tdirectory sectors\n", Disk.ndirsects));
X	    debug(("%x\troot dir block\n", Disk.rootdir));
X	    debug(("%x\tblock for (imaginary) cluster 0\n", Disk.start));
X	    debug(("%x\tfirst data block\n", Disk.datablock));
X	    debug(("%x\tclusters total\n", Disk.maxclst));
X	    debug(("%x\tbytes per cluster\n", Disk.bpc));
X	    debug(("%x\t16-bits FAT?\n", Disk.fat16bits));
X
X	    IDDiskType = ID_DOS_DISK;
X#ifdef READONLY
X	    IDDiskState = ID_WRITE_PROTECTED;
X#else
X	    if (protstatus > 0)
X		IDDiskState = ID_WRITE_PROTECTED;
X	    else
X		IDDiskState = ID_VALIDATED;
X#endif
X
X	    if (Disk.nsects / (MS_SPT * Disk.nsides) <= 40)
X		DiskIOReq->iotd_Req.io_Flags |= IOMDF_40TRACKS;
X	    else
X		DiskIOReq->iotd_Req.io_Flags &= ~IOMDF_40TRACKS;
X
X	    GetFat();
X	} else {
X	    debug(("Can't read %d.\n", DiskIOReq->iotd_Req.io_Error));
X	nodisk:
X	    FreeCacheList();
X	    error = ERROR_NO_DISK;
X	    IDDiskType = ID_UNREADABLE_DISK;
X	    IDDiskState = ID_WRITE_PROTECTED;
X	}
X    }
X#ifdef DEBUG
X    else debug(("No disk inserted %d.\n", DiskIOReq->iotd_Req.io_Error));
X#endif
X    return 1;
X}
X
X/*
X * We try to identify the disk currently in the drive, trying to find the
X * volume label in the first directory block.
X */
X
Xint
XIdentifyDisk(name, date)
Xchar	       *name;		/* Should be at least 32 characters */
Xstruct DateStamp *date;
X{
X    debug(("IdentifyDisk\n"));
X    ReadBootBlock();            /* Also sets default vollabel */
X
X    if (IDDiskType == ID_DOS_DISK) {
X	byte	       *dirblock;
X	register struct MsDirEntry *dirent;
X
X	if (dirblock = GetSec(Disk.rootdir)) {
X	    dirent = (struct MsDirEntry *) dirblock;
X
X	    while ((byte *) dirent < &dirblock[Disk.bps]) {
X		if (dirent->msd_Attributes & ATTR_VOLUMELABEL) {
X		    Disk.vollabel.de_Msd = *dirent;
X		    Disk.vollabel.de_Sector = Disk.rootdir;
X		    Disk.vollabel.de_Offset = (byte *) dirent - dirblock;
X		    OtherEndianMsd(&Disk.vollabel.de_Msd);
X		    Disk.vollabel.de_Msd.msd_Cluster = 0;	/* to be sure */
X		    break;
X		}
X		dirent++;
X	    }
X	    strncpy(name, Disk.vollabel.de_Msd.msd_Name, 8 + 3);
X	    name[8 + 3] = '\0';
X	    ZapSpaces(name, name + 8 + 3);
X	    ToDateStamp(date, Disk.vollabel.de_Msd.msd_Date,
X			Disk.vollabel.de_Msd.msd_Time);
X	    debug(("Disk is called '%s'\n", name));
X
X	    FreeSec(dirblock);
X
X	    return 0;
X	}
X    }
X    return 1;
X}
X
X/*
X * Remove the disk change SoftInt. The V1.2 / V1.3 version is broken, so
X * we use a workaround. The correct thing to do is shown but not used.
X */
X
Xvoid
XTDRemChangeInt()
X{
X    if (DiskChangeReq) {
X	register struct IOExtTD *req = DiskIOReq;
X
X#if 0				/* V1.2 and V1.3 have a broken
X				 * TD_REMCHANGEINT */
X	req->iotd_Req.io_Command = TD_REMCHANGEINT;
X	req->iotd_Req.io_Data = (void *) DiskChangeReq;
X	MyDoIO(req);
X	WaitIO(DiskChangeReq);
X#else
X	Forbid();
X	Remove(DiskChangeReq);
X	Permit();
X#endif
X	DeleteExtIO(DiskChangeReq);
X	DiskChangeReq = NULL;
X    }
X}
X
X/*
X * Set the disk change SoftInt. Return nonzero on failure.
X */
X
Xint
XTDAddChangeInt(interrupt)
Xstruct Interrupt *interrupt;
X{
X    register struct IOExtTD *req = DiskIOReq;
X
X    if (DiskChangeReq) {
X	TDRemChangeInt();
X    }
X    DiskChangeReq = (void *)CreateExtIO(DiskReplyPort,
X					 (long) sizeof (*DiskChangeReq));
X    if (DiskChangeReq) {
X	/* Clone IO request part */
X	DiskChangeReq->io_Device = req->iotd_Req.io_Device;
X	DiskChangeReq->io_Unit = req->iotd_Req.io_Unit;
X	DiskChangeReq->io_Command = TD_ADDCHANGEINT;
X	DiskChangeReq->io_Data = (void *) interrupt;
X	SendIO(DiskChangeReq);
X
X	return 0;
X    }
X    return 1;
X}
X
X/*
X * Get the current disk change number. Necessary for ETD_ commands. Makes
X * absolutely sure nobody can change the disk without us noticing it.
X */
X
Xint
XTDChangeNum()
X{
X    register struct IOExtTD *req = DiskIOReq;
X
X    req->iotd_Req.io_Command = TD_CHANGENUM;
X    MyDoIO(req);
X    req->iotd_Count = req->iotd_Req.io_Actual;
X
X    return req->iotd_Req.io_Actual;
X}
X
X/*
X * Get the current write protection state.
X *
X * Zero means writable, one means write protected, minus one means
X * no disk in drive.
X */
X
Xint
XTDProtStatus()
X{
X    register struct IOExtTD *req = DiskIOReq;
X
X    req->iotd_Req.io_Command = TD_PROTSTATUS;
X    MyDoIO(req);
X
X    if (req->iotd_Req.io_Error)
X	return -1;
X
X    return req->iotd_Req.io_Actual != 0;
X}
X
X/*
X * Switch the drive motor off. Return previous state. Don't use this when
X * you have allocated the disk via GetDrive().
X */
X
Xint
XTDMotorOff()
X{
X    register struct IOExtTD *req = DiskIOReq;
X
X    req->iotd_Req.io_Command = TD_MOTOR;
X    req->iotd_Req.io_Length = 0;
X    MyDoIO(req);
X
X    return req->iotd_Req.io_Actual;
X}
X
X/*
X * Clear all internal messydisk buffers.
X */
X
Xint
XTDClear()
X{
X    register struct IOExtTD *req = DiskIOReq;
X
X    req->iotd_Req.io_Command = CMD_CLEAR;
X
X    return MyDoIO(req);
X}
X
X#ifndef READONLY
X/*
X * Write out all internal messydisk buffers to the disk.
X */
X
Xint
XTDUpdate()
X{
X    register struct IOExtTD *req = DiskIOReq;
X
X    req->iotd_Req.io_Command = ETD_UPDATE;
X
X    return MyDoIO(req);
X}
X#endif
X
Xint
XMyDoIO(ioreq)
Xregister struct IOStdReq *ioreq;
X{
X    ioreq->io_Flags |= IOF_QUICK;	/* Preserve IOMDF_40TRACKS */
X    BeginIO(ioreq);
X    return WaitIO(ioreq);
X}
END_OF_FILE
if test 19261 -ne `wc -c <'src/hansec.c'`; then
    echo shar: \"'src/hansec.c'\" unpacked with wrong size!
fi
# end of 'src/hansec.c'
fi
echo shar: End of archive 3 \(of 6\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 6 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mail submissions (sources or binaries) to <amiga@cs.odu.edu>.
Mail comments to the moderator at <amiga-request@cs.odu.edu>.
Post requests for sources, and general discussion to comp.sys.amiga.