Class WinDrive
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 Summary
FieldsModifier and TypeFieldDescriptionprotected int
protected FAT
protected String
protected int
protected WinDriver
protected WinFile[]
protected FileLock
protected final inifile.IniFile
protected FileChannel
static final byte[]
Array for converting QL chars into lower case see : util_cv_locas_asmprotected WinDir
static final int
protected int
static final int
static final int
static final int
static final int
static final int
static final int
static final int
The structure of the drive header.static final int
static final int
static final int
static final int
static final int
static final int
static final int
static final int
static final int
static final int
static final int
static final int
static final int
protected RandomAccessFile
protected boolean
protected boolean
protected Warnings
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionallocateClusters
(int clustersNeeded) This allocates a new clusterchain with as many clusters as needed.void
changeParentDir
(WinDir oldDir, int oldEntry, WinDir newDir, int newEntry) Changes the parent dir of a file to the new dir.int
checkSector
(int sector) Checks that the special file doesn't go beyond the end of the allowed sectors.void
"Closes" all files by just forgetting about them.boolean
closeFile
(int fileNbr, MC68000Cpu cpu) Closes a file.static final byte[]
convert2SMSQELowerCase
(byte[] in) This creates a new array of bytes, converting the case of the input array to SMSQE lower case.void
flush()
Writes the FAT + drive header to the drive.void
freeClusters
(int cluster) Frees clusters in the FAT, freeing all clusters in a clusterchain.int
Gets the size, in bytes, of one cluster on the drive.Gets the name of the native file containing this drive.int
getIntFromFAT
(int index) Gets an int from the FAT or header.increaseCapacity
(ByteBuffer oldBuffer, int bytesToAdd, ArrayList<Integer> clusterchain) This "increases" a file's buffer by multiples of the clustersize.boolean
Checks whether the drive is read only.int
openFile
(int devDriverLinkageBlock, int channelDefinitionBlock, int openType, int driveNumber, byte[] filename, byte[] uncased, MC68000Cpu cpu) Opens a file.int
readBytes
(int nbrOfBytes, ByteBuffer buffer, long position) Reads bytes at a certain position in the drive to a buffer.Creates a ByteBuffer (for a file) and reads the file into it.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.void
setRootDirLength
(int fsize) Sets the length of the root directory in the appropriate place in the FAT.void
Marks special file as closed.int
trap3OK
(int driveNumber, int trapKey, int fileNbr, MC68000Cpu cpu) Handles trap#3 calls.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).void
unuse()
Unuses (frees) the locked channel.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.void
writeFile
(ByteBuffer fileBuffer, ArrayList<Integer> clusterchain) This writes an entire file in a byte buffer back to the drive.void
writePartOfFile
(ByteBuffer fileBuffer, ArrayList<Integer> clusterchain, int start, int bytesToWrite) This writes a part of byte buffer back to the drive.
-
Field Details
-
QWA_IDEN
public static final int QWA_IDENThe structure of the drive header. Most of the the words in the header are unsigned values.- See Also:
-
QWA_NAME
public static final int QWA_NAME- See Also:
-
QWA_SPR0
public static final int QWA_SPR0- See Also:
-
QWA_UCHK
public static final int QWA_UCHK- See Also:
-
QWA_INTL
public static final int QWA_INTL- See Also:
-
QWA_SCTG
public static final int QWA_SCTG- See Also:
-
QWA_SCTT
public static final int QWA_SCTT- See Also:
-
QWA_TRKC
public static final int QWA_TRKC- See Also:
-
QWA_CYLD
public static final int QWA_CYLD- See Also:
-
QWA_NGRP
public static final int QWA_NGRP- See Also:
-
QWA_FGRP
public static final int QWA_FGRP- See Also:
-
QWA_SCTM
public static final int QWA_SCTM- See Also:
-
QWA_NMAP
public static final int QWA_NMAP- See Also:
-
QWA_FREE
public static final int QWA_FREE- See Also:
-
QWA_ROOT
public static final int QWA_ROOT- See Also:
-
QWA_RLEN
public static final int QWA_RLEN- See Also:
-
QWA_FCYL
public static final int QWA_FCYL- See Also:
-
QWA_FSCT
public static final int QWA_FSCT- See Also:
-
QWA_PARK
public static final int QWA_PARK- See Also:
-
QWA_GMAP
public static final int QWA_GMAP- See Also:
-
LOWER_CASE
public static final byte[] LOWER_CASEArray for converting QL chars into lower case see : util_cv_locas_asm -
MAX_FILES_OPEN
public static final int MAX_FILES_OPEN- See Also:
-
driveName
-
driver
-
driveNumber
protected int driveNumber -
warnings
-
raFile
-
ioChannel
-
readOnly
protected boolean readOnly -
flock
-
clusterSize
protected int clusterSize -
driveFAT
-
mainDir
-
fileNumber
-
specialFileIsOpen
protected boolean specialFileIsOpen -
nbrOfOpenFiles
protected int nbrOfOpenFiles -
inifile
protected final inifile.IniFile inifile
-
-
Constructor Details
-
WinDrive
DO NOT USE THIS FORM OF CREATING THE OBJECT. (I need this for creating a derived object (MEM drive), else the compiler complains.- Throws:
UnsupportedOperationException
- always!
-
WinDrive
Create object with a CPU and warnings. Use only for memdrive!- Parameters:
cpu
- the cpu to be usedwarn
- the warning object with info about what warnings should be suppressed or not.
-
WinDrive
public WinDrive(String driveName, WinDriver driver, int number, Warnings warn, inifile.IniFile inifile) throws FileNotFoundException, IOException, ArrayIndexOutOfBoundsException, DoNothingException, IncorrectFiletypeException, FileLockException Creates the object.- Parameters:
driveName
- the name of the qxl.win file on the native file system.driver
- the driver having created this object.number
- my driveNbr as ASCII!.warn
- object with warning flags.inifile
- the ".ini" file object.- Throws:
FileNotFoundException
- the qxl.win file isn't foundIOException
- any IO exception reading the FAT / main dirArrayIndexOutOfBoundsException
- a wrong FAT cluster number was givenDoNothingException
- if this is a drive with a wrong FAT and user doesn't want me to (try to) fix it.IncorrectFiletypeException
- if this is not a valid qlwa file or the main dir couldn't be read.FileLockException
- couldn't acquire a file lock.
-
-
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
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
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
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
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 bufferposition
- 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
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
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
Handles trap#3 calls.- Parameters:
driveNumber
- number of drive on which to open the filetrapKey
- 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
Closes a file.- Parameters:
fileNbr
- the number of the file to close.cpu
-- Returns:
true
if closed ok, elsefalse
.
-
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
Changes the parent dir of a file to the new dir.- Parameters:
oldDir
- the old parent diroldEntry
- where in the old dir the file is : this identifies the file to treatnewDir
- the new parent dirnewEntry
- the new entry in that dir
-
closeAllFiles
public void closeAllFiles()"Closes" all files by just forgetting about them. -
setRemovableStatus
(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.
-