Package drivers.win

Class WinDrive

java.lang.Object
drivers.win.WinDrive

public class WinDrive extends Object
This class handles one "QXL.WIN" drive.

A windrive is just a file in the native file system, upon which a certain structure is superimposed. It mimics a physical hard drive, so there is a "physical" structure and a "logical" structure.

The physical structure is as follows:

The basic subdivision of a drive is a sector. Each sector is 512 bytes long. Sectors are numbered from 0 upwards. Sector 0 is at the very beginning of the drive, sector 1 is the following sector and so on.

Sectors are nominally grouped into tracks - each track may contain a certain number of sectors. The number of sectors in a track is supposed to remain constant across the entire drive. Tracks are grouped in cylinders (normally platters or read/write heads). A drive may have several cylinders. Since a qxl.win drive is NOT a real hard disk, these physical details may be neglected, except for sector size.

The logical structure is as follows:

Sectors are grouped together in clusters, or "groups" as the SMSQ/E documentation calls them. A cluster is a grouping of consecutive and contiguous sectors and is the minimum size any file may have. The cluster size is not necessarily the same for all drives, it may vary from drive to drive - but does not vary within one drive. A cluster may be composed of 2,4,8 etc sectors. The cluster size is given in the header of the drive.

Each drive has a header which is 64 bytes long and is located at the very beginning of the drive, i.e. in sector 0. The header contains information about the structure of the disk. The exact content of the header is described below in the data structures. Most of the the words in the header are unsigned values.

The header of the drive is immediately followed by the file allocation table (FAT) or "map" as the SMSQ/E documentation calls it. A physical drive might have several FATs, each FAT corresponding to a partition, but a qxl.win drive does not contain several partitions.

The FAT is composed of unsigned words (2 bytes). There can thus only be 65536 entries in the FAT. Each entry in the FAT corresponds to a cluster and thus to a specific location within the drive. If the cluster size is 4 sectors, then entry 0 in the FAT corresponds to sectors 0,1,2 and 3 of the drive, entry 1 in the FAT corresponds to sectors 4,5,6 and 7 etc... If the cluster size is 2 sectors, then entry 0 in the FAT corresponds to sectors 0 and 1 of the drive, entry 1 in the FAT corresponds to sectors 2 and 3 etc... So the physical location of a cluster in a file is determined by cluster number * cluster size.

Since the size of one entry in the FAT is a word, and there can thus only be a maximum of 65536 entries in the FAT, the size of a drive is this directly proportional to the number of sectors per cluster.

Each FAT entry points to the next cluster FOR THE SAME FILE, or is 0 to mark that this cluster is the of the end of the file. The first entries in the FAT are for the FAT itself. After that, there is the first entry for the root directory of the drive.

Any actual reading/writing from/to the drive is done through THIS object.

Implementation details:

SMSQ/E doesn't open "files". It opens channels to files. this mens that a ('physical') file on the drive may have several channels open to it, provided they are all read only, or one single read/write channel. This object maintains a Hashmap of WinFiles. When a channel is opened an entry for the corresponding file is created. Each file that is opened on a drive is given a file number.

There is a maximum of WinDrive.MAX_FILES_OPEN files that may be open at any one time.

The drive is responsible for opening, closing and deleting files. The files themselves are responsible for the actual I/O INTO THEIR BUFFER. However any actual reading/writing from/to the drive is done through THIS object. Files, dirs, even the FAT, have a clusterchain : just an arraylist with all of the cluster numbers of the file/dir/FAT.

  • There is one ByteBuffer holding the entire header + the FAT of the drive
  • Other directories are also kept in ByteBuffers and put into a hashmap as and when they are read.

Each file has a WinDriver.HEADER_LENGTH bytes long header.

Each (sub)directory contains the entire name of the file (stripped of the device part, though).

Note : As far as SMSQE is concerned, each access to a file on the drive will be atomic - smsqe is suspended until the trzp rturns.

On a QXL.Win drive the fileheaders of files themselves contain rubbish. The fileheader is maintained in the directory and, when SMSQE tries to read the header, this is copied from the directory.

When a file is created, it is allocated one cluster. New clusters are not allocated as the file grows, only when the file is closed/flushed. The drawback of this is that a file may grow enormously without error and, when it is written back, it can't be saved any more.

  • Field Details

  • Constructor Details

  • Method Details

    • openFile

      public int openFile(int devDriverLinkageBlock, int channelDefinitionBlock, int openType, int driveNumber, byte[] filename, byte[] uncased, MC68000Cpu cpu)
      Opens a file.
      Parameters:
      devDriverLinkageBlock - pointer to the device driver linkage block (a3).
      channelDefinitionBlock - pointer to the channel definition block (a0).
      openType - which kind of open is requested? (0 - old exclusive, 1 old shared,2 new exclusive,3 new overwrite,4 open dir).
      driveNumber - which drive number the file is to be opened on.
      filename - the (SMSQE) name of the file to open.
      uncased - the lower cased (SMSQE) name of the file to open.
      cpu - the CPU currently used.
      Returns:
      true if open went ok, false if not.
      See Also:
    • setRootDirLength

      public void setRootDirLength(int fsize)
      Sets the length of the root directory in the appropriate place in the FAT. It then writes the FAT to the disk.
      Parameters:
      fsize - the length of the root directory, in bytes.
    • flush

      public void flush()
      Writes the FAT + drive header to the drive.
    • readFile

      public ByteBuffer readFile(int fileLength, int cluster, ArrayList<Integer> clusterchain)
      Creates a ByteBuffer (for a file) and reads the file into it. This also sets the clusters in the clusterchain
      Parameters:
      fileLength - the length of the file (should include the length of the file header).
      cluster - the first cluster of the file (index into the FAT).
      clusterchain - - the empty cluster chain, will be filled in here.
      Returns:
      the newly created buffer with the content of the file, or null if problem. Attention : the size of the buffer will most likely be bigger than the size of the file : the size of the buffer is increased to be a multiple of the clusterSize. This should correspond to the number of clusters occupied by the file.
    • writeFile

      public void writeFile(ByteBuffer fileBuffer, ArrayList<Integer> clusterchain) throws IOException
      This writes an entire file in a byte buffer back to the drive.
      Parameters:
      fileBuffer - the ByteBuffer containing the file. The capacity of the buffer may be bigger than the true filesize.
      clusterchain - the first cluster of the file.
      Throws:
      IOException - any i/o exception from the java i/o operations.
    • writePartOfFile

      public void writePartOfFile(ByteBuffer fileBuffer, ArrayList<Integer> clusterchain, int start, int bytesToWrite) throws IOException
      This writes a part of byte buffer back to the drive.
      Parameters:
      fileBuffer - the ByteBuffer containing the file. The capacity of the buffer may be bigger than the true filesize.
      clusterchain - the first cluster of the file.
      start - where in the buffer to start writing.
      bytesToWrite - how many bytes must be written.
      Throws:
      IOException - any i/o exception from the java i/o operations.
    • increaseCapacity

      public ByteBuffer increaseCapacity(ByteBuffer oldBuffer, int bytesToAdd, ArrayList<Integer> clusterchain)
      This "increases" a file's buffer by multiples of the clustersize. It actually just creates a new, bigger buffer and copies the contents of the old buffer over. This also increases the clusterchain.
      Parameters:
      oldBuffer - the buffer to increase. The limit and position of the buffer MUST have been put at the correct values before calling this. (position should be 0, limit set to the fileSize incl the header.
      bytesToAdd - the number of bytes to add to the file.
      clusterchain - the clusterchain for this file. New clusters will be added at the end.
      Returns:
      the new buffer or NULL if no more space on the drive.
    • truncateFile

      public ArrayList<Integer> truncateFile(int oldsize, int newsize, ArrayList<Integer> currentClusterchain)
      If a file is truncated : delete the clusterchain it occupied and make a new one with the lesser size, unless it would still occupy the same number of clusters (in which case nothing is done).
      Parameters:
      oldsize - old size of the file (=current file size).
      newsize - the file size once the file is truncated.
      currentClusterchain - the file's cluster chain.
      Returns:
      the file's new clusterchain.
    • readBytes

      public int readBytes(int nbrOfBytes, ByteBuffer buffer, long position)
      Reads bytes at a certain position in the drive to a buffer. Used by the special file.
      Parameters:
      nbrOfBytes - how many bytes to read.
      buffer - where to read the bytes to - this buffer must be at least nbrOfBytes long. The bytes are read to the start of the buffer.
      position - where to read from in the drive.
      Returns:
      the number of bytes read, -1 if error.
    • writeBytes

      public int writeBytes(int nbrOfBytes, ByteBuffer buffer, long position, int startInBuffer)
      Writes bytes from a certain position in a buffer to a certain position in the drive.
      Parameters:
      nbrOfBytes - how many bytes to write.
      buffer - where to read the bytes to - this buffer must be at least nbrOfBytes long. The bytes are taken from the start of the buffer
      position - where to write to in the drive.
      startInBuffer - where in the buffer to start taking the bytes to wirite from.
      Returns:
      the number of bytes read, -1 if error.
    • allocateClusters

      public ArrayList<Integer> allocateClusters(int clustersNeeded)
      This allocates a new clusterchain with as many clusters as needed. The newly occupied clusters are marked as occupied in the FAT, but the FAT is not written to the disk.
      Parameters:
      clustersNeeded - the number of clusters needed
      Returns:
      the new clusterchain, or null if there weren't enough free clusters.
    • freeClusters

      public void freeClusters(int cluster)
      Frees clusters in the FAT, freeing all clusters in a clusterchain. The new number of free clusters IS set in the FAT. The newly freed clusters are added to the free clusters list. The modified FAT is not written back to the drive here.
      Parameters:
      cluster - the first cluster to be freed, all other clusters this cluster refers to will also be freed.
    • getIntFromFAT

      public int getIntFromFAT(int index)
      Gets an int from the FAT or header.
      Parameters:
      index - where to read in the map.
      Returns:
      the int
    • convert2SMSQELowerCase

      public static final byte[] convert2SMSQELowerCase(byte[] in)
      This creates a new array of bytes, converting the case of the input array to SMSQE lower case.
      Parameters:
      in - the array with the bytes to convert.
      Returns:
      the new array with lower cased bytes.
    • unuse

      public void unuse()
      Unuses (frees) the locked channel.
    • getDrivename

      public String getDrivename()
      Gets the name of the native file containing this drive.
      Returns:
      the name of the native file containing this drive.
    • isReadOnly

      public boolean isReadOnly()
      Checks whether the drive is read only.
      Returns:
      true if drive is read only, else false.
    • trap3OK

      public int trap3OK(int driveNumber, int trapKey, int fileNbr, MC68000Cpu cpu)
      Handles trap#3 calls.
      Parameters:
      driveNumber - number of drive on which to open the file
      trapKey - what kind of trap #3 is it?
      fileNbr - the file number given by the ddd when file was opened (A0+0x1e)
      cpu -
      Returns:
      SMSQE error/success code NOTE there are many premature exits here!
    • closeFile

      public boolean closeFile(int fileNbr, MC68000Cpu cpu)
      Closes a file.
      Parameters:
      fileNbr - the number of the file to close.
      cpu -
      Returns:
      true if closed ok, else false.
    • specialFileClosed

      public void specialFileClosed()
      Marks special file as closed.
    • checkSector

      public int checkSector(int sector)
      Checks that the special file doesn't go beyond the end of the allowed sectors.
      Parameters:
      sector - sector to check for EOF.
      Returns:
      either this sector, or last allowed sector.
    • getClusterSize

      public int getClusterSize()
      Gets the size, in bytes, of one cluster on the drive.
      Returns:
      the size, in bytes, of one cluster on the drive.
    • changeParentDir

      public void changeParentDir(WinDir oldDir, int oldEntry, WinDir newDir, int newEntry)
      Changes the parent dir of a file to the new dir.
      Parameters:
      oldDir - the old parent dir
      oldEntry - where in the old dir the file is : this identifies the file to treat
      newDir - the new parent dir
      newEntry - the new entry in that dir
    • closeAllFiles

      public void closeAllFiles()
      "Closes" all files by just forgetting about them.
    • setRemovableStatus

      public int setRemovableStatus(boolean newState, MC68000Cpu cpu)
      (Tries to) Set the removable status for the drive.If the new state to set is that the drive should be removable, this will only succeed if there are no files open on the drive. This does not return an error per se, but sets the SMSQ/E error (in D0 - 0 = no error).
      Parameters:
      newState - the new state wished : true → drive is to become removable.
      cpu - the current cpu used
      Returns:
      SMSQE error/success code NOTE: PREMATURE EXITS IF ERROR.