home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 354.lha / MSH_v1.5 / src / hansec.c < prev    next >
C/C++ Source or Header  |  1990-03-12  |  19KB  |  870 lines

  1. /*-
  2.  * $Id: hansec.c,v 1.4 90/02/10 21:30:54 Rhialto Exp $
  3.  * $Log:    hansec.c,v $
  4.  * Revision 1.4  90/02/10  21:30:54  Rhialto
  5.  * Tuned cache a bit.
  6.  * 
  7.  * Revision 1.3  90/01/27  20:20:16  Rhialto
  8.  * Sorted sectors when flushing cache
  9.  *
  10.  * Revision 1.2  90/01/23  02:31:50  Rhialto
  11.  * Add 16-bit FAT support.
  12.  *
  13.  * Revision 1.1  89/12/17  20:02:49  Rhialto
  14.  * Initial revision
  15.  *
  16.  * HANSEC.C
  17.  *
  18.  * The code for the messydos file system handler.
  19.  *
  20.  * Sector-level stuff: read, write, cache, unit conversion.
  21.  * Other interactions (via MyDoIO) with messydisk.device.
  22.  *
  23.  * This code is (C) Copyright 1989,1990 by Olaf Seibert. All rights reserved.
  24.  * May not be used or copied without a licence.
  25. -*/
  26.  
  27. #include "han.h"
  28. #include "dos.h"
  29.  
  30. /*#undef DEBUG                /**/
  31. #ifdef DEBUG
  32. #   define    debug(x)  dbprintf x
  33. #else
  34. #   define    debug(x)
  35. #endif
  36.  
  37. struct MsgPort *DiskReplyPort;
  38. struct IOExtTD *DiskIOReq;
  39. struct IOStdReq *DiskChangeReq;
  40.  
  41. struct DiskParam Disk;
  42. byte           *Fat;
  43. short        FatDirty;    /* Fat must be written to disk */
  44.  
  45. short        error;        /* To put the error value; for Result2 */
  46. long        IDDiskState;    /* InfoData.id_DiskState */
  47. long        IDDiskType;    /* InfoData.id_DiskType */
  48. struct timerequest *TimeIOReq;    /* For motor-off delay */
  49. struct MinList    CacheList;    /* Sector cache */
  50. int        CurrentCache;    /* How many cached buffers do we have */
  51. int        MaxCache = 5;    /* Maximum amount of cached buffers */
  52. long        CacheBlockSize; /* Size of disk block + overhead */
  53. ulong        BufMemType;
  54. int        DelayState;
  55.  
  56. byte           *Word8086;
  57.  
  58. word
  59. Get8086Word(offset)
  60. register int    offset;
  61. {
  62.     return Word8086[offset] | Word8086[offset + 1] << 8;
  63. }
  64.  
  65. word
  66. OtherEndianWord(oew)
  67. word        oew;
  68. {
  69. /* INDENT OFF */
  70. #asm
  71.     move.w    8(a5),d0
  72.     rol.w    #8,d0
  73. #endasm
  74.     /* INDENT ON */
  75.     /*
  76.      * return (oew << 8) | ((oew >> 8) & 0xff);
  77.      */
  78. }
  79.  
  80. ulong
  81. OtherEndianLong(oel)
  82. ulong        oel;
  83. {
  84. /* INDENT OFF */
  85. #asm
  86.     move.l    8(a5),d0
  87.     rol.w    #8,d0
  88.     swap    d0
  89.     rol.w    #8,d0
  90. #endasm
  91.     /* INDENT ON */
  92.     /*
  93.      * return ((oel &       0xff) << 24) | ((oel &     0xff00) <<  8) |
  94.      * ((oel &   0xff0000) >>  8) | ((oel & 0xff000000) >> 24);
  95.      */
  96. }
  97.  
  98. void
  99. OtherEndianMsd(msd)
  100. register struct MsDirEntry *msd;
  101. {
  102.     msd->msd_Date = OtherEndianWord(msd->msd_Date);
  103.     msd->msd_Time = OtherEndianWord(msd->msd_Time);
  104.     msd->msd_Cluster = OtherEndianWord(msd->msd_Cluster);
  105.     msd->msd_Filesize = OtherEndianLong(msd->msd_Filesize);
  106. }
  107.  
  108. word
  109. ClusterToSector(cluster)
  110. register word    cluster;
  111. {
  112.     return cluster ? Disk.start + cluster * Disk.spc
  113.     : 0;
  114. }
  115.  
  116. word
  117. ClusterOffsetToSector(cluster, offset)
  118. register word    cluster;
  119. register word    offset;
  120. {
  121.     return cluster ? Disk.start + cluster * Disk.spc + offset / Disk.bps
  122.     : 0;
  123. }
  124.  
  125. word
  126. DirClusterToSector(cluster)
  127. register word    cluster;
  128. {
  129.     return cluster ? Disk.start + cluster * Disk.spc
  130.     : Disk.rootdir;
  131. }
  132.  
  133. word
  134. SectorToCluster(sector)
  135. register word    sector;
  136. {
  137.     return sector ? (sector - Disk.start) / Disk.spc
  138.     : 0;
  139. }
  140.  
  141. /*
  142.  * Get the next cluster in a chain. Sort-of checks for special entries.
  143.  */
  144.  
  145. word
  146. NextCluster(cluster)
  147. word cluster;
  148. {
  149.     register word entry;
  150.  
  151.     return (entry = GetFatEntry(cluster)) >= 0xFFF0 ? FAT_EOF : entry;
  152. }
  153.  
  154. word
  155. NextClusteredSector(sector)
  156. word        sector;
  157. {
  158.     word        next = (sector + 1 - Disk.start) % Disk.spc;
  159.  
  160.     if (next == 0) {
  161.     next = NextCluster(SectorToCluster(sector));
  162.     return next != FAT_EOF ? ClusterToSector(next)
  163.         : SEC_EOF;
  164.     } else
  165.     return sector + 1;
  166. }
  167.  
  168. #ifndef READONLY
  169.  
  170. word
  171. FindFreeSector(prev)
  172. word        prev;
  173. {
  174.     word freecluster = FindFreeCluster(SectorToCluster(prev));
  175.  
  176.     return freecluster == FAT_EOF ? SEC_EOF : ClusterToSector(freecluster);
  177. }
  178.  
  179. #endif
  180.  
  181. /*
  182.  * Find a specific sector. The cache list is a Least Recently Used stack:
  183.  * Put it on the head of the cache list. So if it is not used anymore in a
  184.  * long time, it bubbles to the end of the list, getting a higher chance
  185.  * of being trashed for re-use.
  186.  */
  187.  
  188. struct CacheSec *
  189. FindSecByNumber(number)
  190. register int    number;
  191. {
  192.     register struct CacheSec *sec;
  193.     register struct CacheSec *nextsec;
  194.  
  195.     debug(("FindSecByNumber %d", number));
  196.  
  197.     for (sec = (void *) CacheList.mlh_Head;
  198.      nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
  199.     if (sec->sec_Number == number) {
  200.         debug((" (%x) %lx\n", sec->sec_Refcount, sec));
  201.         Remove(sec);
  202.         AddHead(&CacheList, &sec->sec_Node);
  203.         return sec;
  204.     }
  205.     }
  206.  
  207.     debug(("; "));
  208.     return NULL;
  209. }
  210.  
  211. struct CacheSec *
  212. FindSecByBuffer(buffer)
  213. byte           *buffer;
  214. {
  215.     return (struct CacheSec *) (buffer - OFFSETOF(CacheSec, sec_Data));
  216. }
  217.  
  218. /*
  219.  * Get a fresh cache buffer. If we are allowed more cache, we just
  220.  * allocate memory. Otherwise, we try to find a currently unused buffer.
  221.  * We start looking at the end of the list, which is the bottom of the LRU
  222.  * stack. If that fails, allocate more memory anyway. Not that is likely
  223.  * anyway, since we currently lock only one sector at a time.
  224.  */
  225.  
  226. struct CacheSec *
  227. NewCacheSector()
  228. {
  229.     register struct CacheSec *sec;
  230.     register struct CacheSec *nextsec;
  231.  
  232.     debug(("NewCacheSector\n"));
  233.  
  234.     if (CurrentCache < MaxCache) {
  235.     if (sec = AllocMem(CacheBlockSize, BufMemType)) {
  236.         goto add;
  237.     }
  238.     }
  239.     for (sec = (void *) CacheList.mlh_TailPred;
  240.      nextsec = (void *) sec->sec_Node.mln_Pred; sec = nextsec) {
  241.     if ((CurrentCache >= MaxCache) && (sec->sec_Refcount == SEC_DIRTY)) {
  242.         FreeCacheSector(sec);       /* Also writes it to disk */
  243.         continue;
  244.     }
  245.     if (sec->sec_Refcount == 0)     /* Implies not SEC_DIRTY */
  246.         return sec;
  247.     }
  248.  
  249.     sec = AllocMem(CacheBlockSize, BufMemType);
  250.  
  251.     if (sec) {
  252. add:
  253.     CurrentCache++;
  254.     AddHead(&CacheList, &sec->sec_Node);
  255.     } else
  256.     error = ERROR_NO_FREE_STORE;
  257.  
  258.     return sec;
  259. }
  260.  
  261. /*
  262.  * Dispose a cached sector, even if it has a non-zero refcount. If it is
  263.  * dirty, write it out.
  264.  */
  265.  
  266. void
  267. FreeCacheSector(sec)
  268. register struct CacheSec *sec;
  269. {
  270.     debug(("FreeCacheSector %d\n", sec->sec_Number));
  271.     Remove(sec);
  272. #ifndef READONLY
  273.     if (sec->sec_Refcount & SEC_DIRTY) {
  274.     PutSec(sec->sec_Number, sec->sec_Data);
  275.     }
  276. #endif
  277.     FreeMem(sec, CacheBlockSize);
  278.     CurrentCache--;
  279. }
  280.  
  281. /*
  282.  * Create an empty cache list
  283.  */
  284.  
  285. void
  286. InitCacheList()
  287. {
  288.     extern struct CacheSec *sec;    /* Of course this does not exist... */
  289.  
  290.     NewList(&CacheList);
  291.     CurrentCache = 0;
  292.     CacheBlockSize = Disk.bps + sizeof (*sec) - sizeof (sec->sec_Data);
  293. }
  294.  
  295. /*
  296.  * Dispose all cached sectors, possibly writing them to disk.
  297.  */
  298.  
  299. void
  300. FreeCacheList()
  301. {
  302.     register struct CacheSec *sec;
  303.  
  304.     debug(("FreeCacheList, %d\n", CurrentCache));
  305.     while (sec = GetHead(&CacheList)) {
  306.     FreeCacheSector(sec);
  307.     }
  308. }
  309.  
  310. /*
  311.  * Do an insertion sort on tosort in the CacheList. Since it changes the
  312.  * location in the list, you must fetch it before calling this routine.
  313.  * The list will become ascending.
  314.  */
  315.  
  316. void
  317. SortSec(tosort)
  318. register struct CacheSec *tosort;
  319. {
  320.     register struct CacheSec *sec;
  321.     struct CacheSec *nextsec;
  322.     register word   secno;
  323.  
  324.     secno = tosort->sec_Number;
  325.     debug(("SortSec %d: ", secno));
  326.  
  327.     for (sec = (void *) CacheList.mlh_Head;
  328.      nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
  329.     debug(("%d, ", sec->sec_Number));
  330.     if (sec == tosort) {
  331.         debug(("\n"));
  332.         return;            /* No need to move it away */
  333.     }
  334.     if (sec->sec_Number > secno)
  335.         break;
  336.     }
  337.     /* Insert before sec */
  338.     Remove(tosort);
  339.     Insert(&CacheList, tosort, sec->sec_Node.mln_Pred);
  340.     debug(("\n"));
  341. }
  342.  
  343. /*
  344.  * Write all dirty cache buffers to disk. They are written from highest to
  345.  * lowest, and then the FAT is written out.
  346.  */
  347.  
  348. void
  349. MSUpdate(immediate)
  350. int        immediate;
  351. {
  352.     register struct CacheSec *sec;
  353.     register struct CacheSec *nextsec;
  354.  
  355.     debug(("MSUpdate\n"));
  356.  
  357. #ifndef READONLY
  358.     if (DelayState & DELAY_DIRTY) {
  359.     /*
  360.      * First sort all dirty sectors on block number
  361.      */
  362.     for (sec = (void *) CacheList.mlh_Head;
  363.          nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
  364.         if (sec->sec_Refcount & SEC_DIRTY) {
  365.         SortSec(sec);
  366.         }
  367.     }
  368.     /*
  369.      * Then do a second (backward) scan to write them out.
  370.      */
  371.     for (sec = (void *) CacheList.mlh_TailPred;
  372.          nextsec = (void *) sec->sec_Node.mln_Pred; sec = nextsec) {
  373.         if (sec->sec_Refcount & SEC_DIRTY) {
  374.         PutSec(sec->sec_Number, sec->sec_Data);
  375.         sec->sec_Refcount &= ~SEC_DIRTY;
  376.         }
  377.     }
  378.     DelayState &= ~DELAY_DIRTY;
  379.     }
  380.     if (FatDirty) {
  381.     WriteFat();
  382.     }
  383. #endif
  384.  
  385.     if (immediate)
  386.     DelayState = DELAY_RUNNING1;
  387.  
  388.     if (DelayState & DELAY_RUNNING2) {
  389.     StartTimer();
  390.     DelayState &= ~DELAY_RUNNING2;
  391.     } else {            /* DELAY_RUNNING1 */
  392. #ifndef READONLY
  393.     while (TDUpdate() != 0 && RetryRwError(DiskIOReq))
  394.         ;
  395. #endif
  396.     TDMotorOff();
  397.     DelayState = DELAY_OFF;
  398.     }
  399. }
  400.  
  401. /*
  402.  * Start the timer which triggers cache writing and stopping the disk
  403.  * motor.
  404.  */
  405.  
  406. void
  407. StartTimer()
  408. {
  409.     DelayState |= DELAY_RUNNING1 | DELAY_RUNNING2;
  410.  
  411.     if (CheckIO(TimeIOReq)) {
  412.     WaitIO(TimeIOReq);
  413.     TimeIOReq->tr_node.io_Command = TR_ADDREQUEST;
  414.     TimeIOReq->tr_time.tv_secs = 3;
  415.     TimeIOReq->tr_time.tv_micro = 0;
  416.     SendIO(TimeIOReq);
  417.     }
  418. }
  419.  
  420. /*
  421.  * Get a pointer to a logical sector { 0, ..., MS_SECTCNT - 1}. We
  422.  * allocate a buffer and copy the data in, and lock the buffer until
  423.  * FreeSec() is called.
  424.  */
  425.  
  426. byte           *
  427. GetSec(sector)
  428. int        sector;
  429. {
  430.     struct CacheSec *sec;
  431.  
  432.     if (sec = FindSecByNumber(sector)) {
  433.     sec->sec_Refcount++;
  434.  
  435.     return sec->sec_Data;
  436.     }
  437.     if (sec = NewCacheSector()) {
  438.     register struct IOExtTD *req;
  439.  
  440.     sec->sec_Number = sector;
  441.     sec->sec_Refcount = 1;
  442.  
  443.     debug(("GetSec %d\n", sector));
  444.  
  445.     req = DiskIOReq;
  446.     do {
  447.         req->iotd_Req.io_Command = ETD_READ;
  448.         req->iotd_Req.io_Data = (APTR)sec->sec_Data;
  449.         req->iotd_Req.io_Offset = Disk.lowcyl + (long) sector * Disk.bps;
  450.         req->iotd_Req.io_Length = Disk.bps;
  451.         MyDoIO(req);
  452.     } while (req->iotd_Req.io_Error != 0 && RetryRwError(req));
  453.  
  454.     StartTimer();
  455.  
  456.     if (req->iotd_Req.io_Error == 0) {
  457.         return sec->sec_Data;
  458.     }
  459.     error = ERROR_NOT_A_DOS_DISK;
  460.     FreeCacheSector(sec);
  461.     }
  462.     return NULL;
  463. }
  464.  
  465. #ifndef READONLY
  466.  
  467. byte           *
  468. EmptySec(sector)
  469. int        sector;
  470. {
  471.     byte       *buffer;
  472.     register struct CacheSec *sec;
  473.  
  474.     if (sec = FindSecByNumber(sector)) {
  475.     sec->sec_Refcount++;
  476.  
  477.     return sec->sec_Data;
  478.     }
  479.     if (sec = NewCacheSector()) {
  480.     sec->sec_Number = sector;
  481.     sec->sec_Refcount = 1;
  482.  
  483.     return sec->sec_Data;
  484.     }
  485.  
  486.     return NULL;
  487. }
  488.  
  489. void
  490. PutSec(sector, data)
  491. int        sector;
  492. byte           *data;
  493. {
  494.     register struct IOExtTD *req;
  495.  
  496.     debug(("PutSec %d\n", sector));
  497.  
  498.     req = DiskIOReq;
  499.     do {
  500.     req->iotd_Req.io_Command = ETD_WRITE;
  501.     req->iotd_Req.io_Data = (APTR) data;
  502.     req->iotd_Req.io_Offset = Disk.lowcyl + (long) sector * Disk.bps;
  503.     req->iotd_Req.io_Length = Disk.bps;
  504.     MyDoIO(req);
  505.     } while (req->iotd_Req.io_Error != 0 && RetryRwError(req));
  506.  
  507.     StartTimer();
  508. }
  509.  
  510. #endif
  511.  
  512. /*
  513.  * Unlock a cached sector. When the usage count drops to zero, which
  514.  * implies it is not dirty, and we are over our cache quota, the sector is
  515.  * freed. Otherwise we keep it for re-use.
  516.  */
  517.  
  518. void
  519. FreeSec(buffer)
  520. byte           *buffer;
  521. {
  522.     register struct CacheSec *sec;
  523.  
  524.     if (sec = FindSecByBuffer(buffer)) {
  525.     if (--sec->sec_Refcount == 0) { /* Implies not SEC_DIRTY */
  526.         if (CurrentCache > MaxCache) {
  527.         FreeCacheSector(sec);
  528.         }
  529.     }
  530.     }
  531. }
  532.  
  533. #ifndef READONLY
  534.  
  535. void
  536. MarkSecDirty(buffer)
  537. byte           *buffer;
  538. {
  539.     register struct CacheSec *sec;
  540.  
  541.     if (sec = FindSecByBuffer(buffer)) {
  542.     sec->sec_Refcount |= SEC_DIRTY;
  543.     DelayState |= DELAY_DIRTY;
  544.     StartTimer();
  545.     }
  546. }
  547.  
  548. /*
  549.  * Write out the FAT. Called from MSUpdate(), so don't call it again from
  550.  * here. Don't use precious cache space for it; you could say it has its
  551.  * own private cache already.
  552.  */
  553.  
  554. void
  555. WriteFat()
  556. {
  557.     register int    fat,
  558.             sec;
  559.     int         disksec = Disk.res;      /* First FAT, first sector */
  560.  
  561.     /* Write all FATs */
  562.     for (fat = 0; fat < Disk.nfats; fat++) {
  563.     for (sec = 0; sec < Disk.spf; sec++) {
  564.         PutSec(disksec++, Fat + sec * Disk.bps);
  565.         /* return;           /* Fat STILL dirty! */
  566.     }
  567.     }
  568.     FatDirty = FALSE;
  569. }
  570.  
  571. #endif
  572.  
  573. int
  574. ReadBootBlock()
  575. {
  576.     int protstatus;
  577.  
  578.     debug(("ReadBootBlock\n"));
  579.     FreeFat();                  /* before disk parameters change */
  580.     TDClear();
  581.  
  582.     if ((protstatus = TDProtStatus()) >= 0) {
  583.     TDChangeNum();
  584.     debug(("Changenumber = %ld\n", DiskIOReq->iotd_Count));
  585.     if (Word8086 = GetSec(0)) {
  586.         word bps;
  587.  
  588.         /* 8086 ml for a jump */
  589.         if (Word8086[0] != 0xE9 && Word8086[0] != 0xEB) {
  590.         goto nodisk;
  591.         }
  592.         bps = Get8086Word(0x0b);
  593.         Disk.spc = Word8086[0x0d];
  594.         Disk.res = Get8086Word(0x0e);
  595.         Disk.nfats = Word8086[0x10];
  596.         Disk.ndirs = Get8086Word(0x11);
  597.         Disk.nsects = Get8086Word(0x13);
  598.         Disk.media = Word8086[0x15];
  599.         Disk.spf = Get8086Word(0x16);
  600.         Disk.spt = Get8086Word(0x18);
  601.         Disk.nsides = Get8086Word(0x1a);
  602.         Disk.nhid = Get8086Word(0x1c);
  603.         FreeSec(Word8086);
  604.  
  605.         /*
  606.          *    Maybe the sector size just changed. Who knows?
  607.          */
  608.         if (Disk.bps != bps) {
  609.         FreeCacheList();
  610.         Disk.bps = bps;
  611.         InitCacheList();
  612.         }
  613.  
  614.         Disk.ndirsects = (Disk.ndirs * MS_DIRENTSIZE) / Disk.bps;
  615.         Disk.rootdir = Disk.res + Disk.spf * Disk.nfats;
  616.         Disk.datablock = Disk.rootdir + Disk.ndirsects;
  617.         Disk.start = Disk.datablock - MS_FIRSTCLUST * Disk.spc;
  618.         /* Available clusters are 2..maxclust in secs start..nsects-1 */
  619.         Disk.maxclst = (Disk.nsects - Disk.start) / Disk.spc - 1;
  620.         Disk.bpc = Disk.bps * Disk.spc;
  621.         Disk.vollabel = FakeRootDirEntry;
  622. /*        Disk.fat16bits = Disk.nsects > 20740;   /* DOS3.2 magic value */
  623.         Disk.fat16bits = Disk.maxclst > 0xFF6;  /* DOS3.0 magic value */
  624.  
  625.         debug(("%x\tbytes per sector\n", Disk.bps));
  626.         debug(("%x\tsectors per cluster\n", Disk.spc));
  627.         debug(("%x\treserved blocks\n", Disk.res));
  628.         debug(("%x\tfats\n", Disk.nfats));
  629.         debug(("%x\tdirectory entries\n", Disk.ndirs));
  630.         debug(("%x\tsectors\n", Disk.nsects));
  631.         debug(("%x\tmedia byte\n", Disk.media));
  632.         debug(("%x\tsectors per FAT\n", Disk.spf));
  633.         debug(("%x\tsectors per track\n", Disk.spt));
  634.         debug(("%x\tsides\n", Disk.nsides));
  635.         debug(("%x\thidden sectors\n", Disk.nhid));
  636.  
  637.         debug(("%x\tdirectory sectors\n", Disk.ndirsects));
  638.         debug(("%x\troot dir block\n", Disk.rootdir));
  639.         debug(("%x\tblock for (imaginary) cluster 0\n", Disk.start));
  640.         debug(("%x\tfirst data block\n", Disk.datablock));
  641.         debug(("%x\tclusters total\n", Disk.maxclst));
  642.         debug(("%x\tbytes per cluster\n", Disk.bpc));
  643.         debug(("%x\t16-bits FAT?\n", Disk.fat16bits));
  644.  
  645.         IDDiskType = ID_DOS_DISK;
  646. #ifdef READONLY
  647.         IDDiskState = ID_WRITE_PROTECTED;
  648. #else
  649.         if (protstatus > 0)
  650.         IDDiskState = ID_WRITE_PROTECTED;
  651.         else
  652.         IDDiskState = ID_VALIDATED;
  653. #endif
  654.  
  655.         if (Disk.nsects / (MS_SPT * Disk.nsides) <= 40)
  656.         DiskIOReq->iotd_Req.io_Flags |= IOMDF_40TRACKS;
  657.         else
  658.         DiskIOReq->iotd_Req.io_Flags &= ~IOMDF_40TRACKS;
  659.  
  660.         GetFat();
  661.     } else {
  662.         debug(("Can't read %d.\n", DiskIOReq->iotd_Req.io_Error));
  663.     nodisk:
  664.         FreeCacheList();
  665.         error = ERROR_NO_DISK;
  666.         IDDiskType = ID_UNREADABLE_DISK;
  667.         IDDiskState = ID_WRITE_PROTECTED;
  668.     }
  669.     }
  670. #ifdef DEBUG
  671.     else debug(("No disk inserted %d.\n", DiskIOReq->iotd_Req.io_Error));
  672. #endif
  673.     return 1;
  674. }
  675.  
  676. /*
  677.  * We try to identify the disk currently in the drive, trying to find the
  678.  * volume label in the first directory block.
  679.  */
  680.  
  681. int
  682. IdentifyDisk(name, date)
  683. char           *name;        /* Should be at least 32 characters */
  684. struct DateStamp *date;
  685. {
  686.     debug(("IdentifyDisk\n"));
  687.     ReadBootBlock();            /* Also sets default vollabel */
  688.  
  689.     if (IDDiskType == ID_DOS_DISK) {
  690.     byte           *dirblock;
  691.     register struct MsDirEntry *dirent;
  692.  
  693.     if (dirblock = GetSec(Disk.rootdir)) {
  694.         dirent = (struct MsDirEntry *) dirblock;
  695.  
  696.         while ((byte *) dirent < &dirblock[Disk.bps]) {
  697.         if (dirent->msd_Attributes & ATTR_VOLUMELABEL) {
  698.             Disk.vollabel.de_Msd = *dirent;
  699.             Disk.vollabel.de_Sector = Disk.rootdir;
  700.             Disk.vollabel.de_Offset = (byte *) dirent - dirblock;
  701.             OtherEndianMsd(&Disk.vollabel.de_Msd);
  702.             Disk.vollabel.de_Msd.msd_Cluster = 0;    /* to be sure */
  703.             break;
  704.         }
  705.         dirent++;
  706.         }
  707.         strncpy(name, Disk.vollabel.de_Msd.msd_Name, 8 + 3);
  708.         name[8 + 3] = '\0';
  709.         ZapSpaces(name, name + 8 + 3);
  710.         ToDateStamp(date, Disk.vollabel.de_Msd.msd_Date,
  711.             Disk.vollabel.de_Msd.msd_Time);
  712.         debug(("Disk is called '%s'\n", name));
  713.  
  714.         FreeSec(dirblock);
  715.  
  716.         return 0;
  717.     }
  718.     }
  719.     return 1;
  720. }
  721.  
  722. /*
  723.  * Remove the disk change SoftInt. The V1.2 / V1.3 version is broken, so
  724.  * we use a workaround. The correct thing to do is shown but not used.
  725.  */
  726.  
  727. void
  728. TDRemChangeInt()
  729. {
  730.     if (DiskChangeReq) {
  731.     register struct IOExtTD *req = DiskIOReq;
  732.  
  733. #if 0                /* V1.2 and V1.3 have a broken
  734.                  * TD_REMCHANGEINT */
  735.     req->iotd_Req.io_Command = TD_REMCHANGEINT;
  736.     req->iotd_Req.io_Data = (void *) DiskChangeReq;
  737.     MyDoIO(req);
  738.     WaitIO(DiskChangeReq);
  739. #else
  740.     Forbid();
  741.     Remove(DiskChangeReq);
  742.     Permit();
  743. #endif
  744.     DeleteExtIO(DiskChangeReq);
  745.     DiskChangeReq = NULL;
  746.     }
  747. }
  748.  
  749. /*
  750.  * Set the disk change SoftInt. Return nonzero on failure.
  751.  */
  752.  
  753. int
  754. TDAddChangeInt(interrupt)
  755. struct Interrupt *interrupt;
  756. {
  757.     register struct IOExtTD *req = DiskIOReq;
  758.  
  759.     if (DiskChangeReq) {
  760.     TDRemChangeInt();
  761.     }
  762.     DiskChangeReq = (void *)CreateExtIO(DiskReplyPort,
  763.                      (long) sizeof (*DiskChangeReq));
  764.     if (DiskChangeReq) {
  765.     /* Clone IO request part */
  766.     DiskChangeReq->io_Device = req->iotd_Req.io_Device;
  767.     DiskChangeReq->io_Unit = req->iotd_Req.io_Unit;
  768.     DiskChangeReq->io_Command = TD_ADDCHANGEINT;
  769.     DiskChangeReq->io_Data = (void *) interrupt;
  770.     SendIO(DiskChangeReq);
  771.  
  772.     return 0;
  773.     }
  774.     return 1;
  775. }
  776.  
  777. /*
  778.  * Get the current disk change number. Necessary for ETD_ commands. Makes
  779.  * absolutely sure nobody can change the disk without us noticing it.
  780.  */
  781.  
  782. int
  783. TDChangeNum()
  784. {
  785.     register struct IOExtTD *req = DiskIOReq;
  786.  
  787.     req->iotd_Req.io_Command = TD_CHANGENUM;
  788.     MyDoIO(req);
  789.     req->iotd_Count = req->iotd_Req.io_Actual;
  790.  
  791.     return req->iotd_Req.io_Actual;
  792. }
  793.  
  794. /*
  795.  * Get the current write protection state.
  796.  *
  797.  * Zero means writable, one means write protected, minus one means
  798.  * no disk in drive.
  799.  */
  800.  
  801. int
  802. TDProtStatus()
  803. {
  804.     register struct IOExtTD *req = DiskIOReq;
  805.  
  806.     req->iotd_Req.io_Command = TD_PROTSTATUS;
  807.     MyDoIO(req);
  808.  
  809.     if (req->iotd_Req.io_Error)
  810.     return -1;
  811.  
  812.     return req->iotd_Req.io_Actual != 0;
  813. }
  814.  
  815. /*
  816.  * Switch the drive motor off. Return previous state. Don't use this when
  817.  * you have allocated the disk via GetDrive().
  818.  */
  819.  
  820. int
  821. TDMotorOff()
  822. {
  823.     register struct IOExtTD *req = DiskIOReq;
  824.  
  825.     req->iotd_Req.io_Command = TD_MOTOR;
  826.     req->iotd_Req.io_Length = 0;
  827.     MyDoIO(req);
  828.  
  829.     return req->iotd_Req.io_Actual;
  830. }
  831.  
  832. /*
  833.  * Clear all internal messydisk buffers.
  834.  */
  835.  
  836. int
  837. TDClear()
  838. {
  839.     register struct IOExtTD *req = DiskIOReq;
  840.  
  841.     req->iotd_Req.io_Command = CMD_CLEAR;
  842.  
  843.     return MyDoIO(req);
  844. }
  845.  
  846. #ifndef READONLY
  847. /*
  848.  * Write out all internal messydisk buffers to the disk.
  849.  */
  850.  
  851. int
  852. TDUpdate()
  853. {
  854.     register struct IOExtTD *req = DiskIOReq;
  855.  
  856.     req->iotd_Req.io_Command = ETD_UPDATE;
  857.  
  858.     return MyDoIO(req);
  859. }
  860. #endif
  861.  
  862. int
  863. MyDoIO(ioreq)
  864. register struct IOStdReq *ioreq;
  865. {
  866.     ioreq->io_Flags |= IOF_QUICK;    /* Preserve IOMDF_40TRACKS */
  867.     BeginIO(ioreq);
  868.     return WaitIO(ioreq);
  869. }
  870.