QLWA Hard Disk Structure

I am grateful for the help and advice of John Hall and Daniele Terdina in the explanation of the QLWA hard disc format.


A hard disc has the following physical layout :

HeaderGroup MapData                                                            

The following is the header block structure for the first sector on a QDOS hard disc, or a QPC or QXL QXL.WIN file. To avoid confusion, I shall refer to it as a QLWA format hard disc.

MnemonicOffsetSizeDescription
qwa.id $00004 bytes'QLWA' - Identification bytes.
qwa.pflg$01 & 'QWA'4 bytesQWA partition flag. (Any comments on what this means exactly?)
qwa_name$000422 bytesup to 20 characters, space padded name. Word count first then bytes.
qwa_spr0$001A2 bytesspare - set to zero.
qwa_uchk$001Clongupdate check1.
qwa_intl$0020wordinterleave factor (0 = SCSI/QXL.WIN).
qwa_sctg$0022wordsectors per group.
qwa_sctt$0024wordsectors per track (0 = SCSI/QXL.WIN).
qwa_trkc$0026wordtracks per cylinder (number of heads) (0 = SCSI/QXL.WIN).
qwa_cyld$0028wordcylinders per drive.
qwa_ngrp$002Awordnumber of groups.
qwa_fgrp$002Cwordnumber of free groups.
qwa_sctm$002Ewordsectors per map.
qwa_nmap$0030wordnumber of maps.
qwa_free$0032wordfirst free group.
qwa_root$0034wordroot directory number.
qwa_rlen$0036longroot directory length.
qwa_fcyl$003Awordfirst cylinder number (ST506 only = Miracle Hard Disc?). OR
qwa_fsct$003Alongfirst sector for this partition (SCSI/QXL.WIN).
qwa_park$003Ewordpark cylinder.
qwa_gmap$0040wordsgroup map: each entry is one word and is number of next group or zero.

Notes:

1. The update check is incremented by 1 every time a file is created, deleted etc. If you open a file in over mode and then close it again without doing anything, this number will increment, this is because you have (just) overwritten the file!



The following is an initial attempt at an analysis of my QPC win1_ hard disc file.

00000000  51 4c 57 41 00 04 57 49  4e 32 20 20 20 20 20 20  |QLWA..WIN2      |
00000010  20 20 20 20 20 20 20 20  20 20 00 00 ee 8c 00 ce  |          ......|
00000020  00 00 00 04 00 00 00 00  00 00 3c 00 08 71 00 3d  |..........<..q.=|
00000030  00 01 33 8f 00 10 00 00  0a 00 00 00 00 00 00 00  |..3.............|

If we decode the above dump into something a bit more meaningful, we get the following.

MnemonicOffsetValueComments
qwa.id $0000'QLWA'QDOS hard disc identifier
qwa_name$0004DC.W $0004,
DC.B 'WIN2'
Disc label
qwa_spr0$001ADC.W $0000Spare
qwa_uchk$001CDC.L $EE8C00CEupdate check.
qwa_intl$0020DC.W $0000interleave factor.
qwa_sctg$0022DC.W $0004sectors per group.
qwa_sctt$0024DC.W $0000sectors per track.
qwa_trkc$0026DC.W $0000tracks per cylinder (number of heads).
qwa_cyld$0028DC.W $0000cylinders per drive.
qwa_ngrp$002ADC.W $3C00number of groups.
qwa_fgrp$002CDC.W $0871number of free groups.
qwa_sctm$002EDC.W $003Dsectors per map.
qwa_nmap$0030DC.W $0001number of maps.
qwa_free$0032DC.W $338Ffirst free group.
qwa_root$0034DC.W $0010root directory number.
qwa_rlen$0036DC.L $00000A00root directory length.
qwa_fsct$003ADC.L $00000000first sector for this partition.
qwa_park$003EDC.W $0000park cylinder.

A 'STAT' against Win1_ in QPC returns the following :

WIN2 QDOS
8644/61440 sectors

So the disc label for Win1_ is actually 'WIN2' for some reason - I suspect I may have copied it at some point in the past! The 61,440 total sectors works out as the number of groups from the first sector. The 8644 free sectors does not appear in the heading fields, but the number of free groups * sectors per group comes out at exactly 8644 when converted to decimal.

Update: ok, when I formatted the drive I created it as the second drive, hence the WIN2 name. Had it been the third drive, it would be called WIN3 and so on. At present, there isn't a way to name a drive at format time similar to a floppy format

Derived Information

From the above we can derive the following information :

Total Size in MB = ((qwa_ngrp * qwa_sctg) * 512) / 1,048,576
Free Space in MB = ((qwa_fgrp * qwa_sctg) * 512) / 1,048,576

Following on from the 'disc' header, we find the map at address $0040. The map is a series of words (16 bit) which define whether a 'group' is in use or free. When the drive is formatted initially, the words are all set to point to the next free group. This means that we have a linked list where group zero points at group one which points to group two and so on. The end of all the groups is a word of zero.

A 'group' as described above is a number of sectors, a sector being 512 bytes, as defined by the value in qwa_sctg - number of sectors per group. In the example I'm using, this is 4 giving me a group size of 4 * 512 or 2,048 bytes. This implies that the smallest files that physically exists on a disc of this kind are at least 2,048 bytes in size.

I presume here that the group size is defined by the maximum number of groups that we have in the map - limited to 65,536 by the word sized entries - and/or the size that we format the disc to. My example is a 30 MB file and has a group size of 2,048 bytes. With 65,536 groups allowed in total, this implies a maximum size of 128 MB.

Formatting a disc to 130 MB works - so obviously something is happening internally - and in this case, the group size is changed from 4 sectors, 2,048 bytes to 5 sectors or 2,560 bytes.

The smallest disc I have formatted is a 1 MB file, and it has a group size of 4 sectors as well, so it appears that 4 sectors is the minimum size of a file on a 'qlwa' formatted hard disc.

Qwa_ngrp defines the number of groups that physically exist on this medium. It is possible that there may be additional entries in the group map above this number, but these cannot be accessed within the QLWA disc itself.

There are not always 65,536 entries in the map - it can be smaller according to the size of the QLWA disc.

Each entry in the group has two purposes. The first is to determine where the data is located on disc and the second is to determine if there are any more groups of sectors containing data for this file.

The entry's index number is used to determine where on the disc the data starts :

  index_number * qwa_sgrp * 512

The actual entry itself determines if more data exists for this file. If the word in the group map is zero, then the current entry is the last one for the file.

The following is the first few entries from my example group map :

00000040  00 01 00 02 00 03 00 04  00 05 00 06 00 07 00 08  |................|
00000050  00 09 00 0a 00 0b 00 0c  00 0d 00 0e 00 0f 00 00  |................|
00000060  00 5c 01 4e 0b ff 00 00  06 93 04 58 08 50 08 e5  |.\.N.......X.P..|
00000070  09 93 00 00 00 00 00 00  09 f3 00 00 00 00 00 00  |................|
00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000090  00 00 00 00 00 00 00 00  00 00 00 00 13 06 00 00  |................|

Starting at address $40, we see that the first entry (entry zero) has the word $0001 in it. So whichever file begins at entry zero on the map has a linked list of 'groups' that extends up to the zero word at address $5e. These groups of sectors belong to the map itself. The map is always the first group of entries in itself (recursive or what?) and the first real file after the map is always the root directory.

The data sectors contain the date that makes up user files plus the root directory and any sub-directories created by the user.

To find out where on the physical disc a file begins is actually quite simple :

  • Take the File Id and multiply by the number of sectors per group. This gives the

sector number.

  • Multiply the sector number by 512 to get the physical address.

So that's the first bit, we have the first 'n' sectors of the file, where are the next 'n' sectors?

  • Take the File Id again, and multiply by two to get the group map offset.
  • Add this offset to $40 to get the map address.
  • The word at this address is the 'File Id' of the next chunk of this file.
  • If this new File Id is zero, there is no next chunk of file.
  • If the (new) File Id is non-zero then use it to access the disc as per the first chunk above.
  • Repeat until we hit a word of zero in the group map.

Where does the File Id come from in the first place? It comes from the directory entry for the file we are interested in. This is discussed below.

Root Directory

The root directory on QDOS floppy discs was always file zero. This allowed its place on the physical medium to be easily determined. With the QLWA format, this is not so easy. The map can take up more or less of the disc and the root directory cannot be found as simply the first file on disc, so we need to have a pointer to the beginning of the root directory in the disc header. This pointer is the qwa_root field and simply holds the file id, or group map index, for the root directory's first group of 'n' sectors.

Looking back at the header details above, we see that the root directory begins in block $10. To find where our first chunk of the root directory is located we use the following :

  • File Id = $10.
  • Multiply by sectors in group = $10 * $04 = $40.
  • Multiply by sector size = $40 * $200 = $8000.

The First 'n' sectors of the root directory begins on disc at address $8000. Looking there in my hex dump shows the following :

00008000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00008010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00008020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00008030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

Hmm, not much of interest there, but just after this we see the following:

00008040  00 00 12 80 00 ff 00 00  00 00 00 00 00 00 00 03  |................|
00008050  70 65 6b 00 00 00 00 00  00 00 00 00 00 00 00 00  |pek.............|
00008060  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00008070  00 00 00 00 00 00 00 00  00 01 00 11 00 00 00 00  |................|
00008080  00 00 0d c0 00 ff 00 00  00 00 00 00 00 00 00 03  |................|
00008090  63 36 38 00 00 00 00 00  00 00 00 00 00 00 00 00  |c68.............|
000080a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000080b0  00 00 00 00 00 00 00 00  00 01 00 12 00 00 00 00  |................|
000080c0  00 00 07 c0 00 ff 00 00  00 00 00 00 00 00 00 06  |................|
000080d0  73 6f 75 72 63 65 00 00  00 00 00 00 00 00 00 00  |source..........|
000080e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000080f0  00 00 00 00 00 00 00 00  00 01 00 1d 00 00 00 00  |................|
00008100  00 00 01 80 00 ff 00 00  00 00 00 00 00 00 00 05  |................|

This looks much better. It appears that the root directory's own entry in itself, is all zeros. Then following on from that we have the actual entries of the other files and sub- directories in the normal 64 byte file header format.

So we have found the first chunk of the root directory but where are the rest of it, assuming more exists? The Group Map tells us this.

Our File Id was originally $10 and that told us where the first chunk of directory was on disc. Looking at the group map again we can see :

Block $10 is at offset $40 + ($10 * 2) or address $60.

Looking into our map at that address we see the word $005c - this is the file id of the second chunk of the root directory. We can calculate the start address of the second chunk of the directory in the same manner as above where we found where the beginning of the root directory was on disc :

  • File Id = $5c.
  • Multiply by sectors in group = $5c * $04 = $0170.
  • Multiply by sector size = $0170 * $200 = $2e000.

This means that the second 2,048 bytes of the root directory should be at the above address. Looking at the file again, we see the following there :

0002e000  00 00 02 c0 00 ff 00 00  00 00 00 00 00 00 00 05  |................|
0002e010  71 6d 6f 6e 32 00 00 00  00 00 00 00 00 00 00 00  |qmon2...........|
0002e020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
0002e030  00 00 00 00 00 00 00 00  00 01 00 5d 00 00 00 00  |...........]....|
0002e040  00 00 01 c0 00 ff 00 00  00 00 00 00 00 00 00 04  |................|
0002e050  74 65 73 74 00 00 00 00  00 00 00 00 00 00 00 00  |test............|
0002e060  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
0002e070  00 00 00 00 00 00 00 00  00 01 00 5e 00 00 00 00  |...........^....|
0002e080  00 00 06 c0 00 ff 00 00  00 00 00 00 00 00 00 09  |................|
0002e090  7a 69 70 5f 66 69 6c 65  73 00 00 00 00 00 00 00  |zip_files.......|

And indeed, this is the second part of my root directory. Is there any more?

Back in the group map at offset $40 + (File Id * 2) and using this for our File Id of $5c for the root directory, we find an address of $f8. The word there points to the third group of 'n' sectors for this file. We look at the word in that location and find a word of zero. This means that there is no third chunk of root directory. We have read it all in two chunks of 2,048 bytes.

000000f0  00 00 00 00 00 00 2b aa  00 00 00 00 00 00 00 00  |......+.........|
                                   ^^^^^

Directory Entry Format

MnemonicOffsetSizeDescription
hdr_flen$00longFile length1
hdr_accs$04byteaccess control byte
hdr_type$05bytefile type
hdr_data$06longprogram dataspace
hdr_xtra$0alongextra info
hdr_name$0e file name (up to 36 characters long)
Word length2 then bytes of name. Space filled.
hdr_date$34longupdate date
hdr_vers$38wordversion number
hdr_flid$3awordFile id
hdr_bkup$3clongbackup date

Notes

1. The hdr_flen field includes an extra 64 bytes for the copy of the directory header at the start of the file in storage. This is not shown when you wstat the drive. Also, I created a single byte file and the directory entry showed 66 bytes in size. wstat showed the file as only two bytes. This rounding to even is not consistent as I have a file with 107 bytes in it! (And no, there is not a linefeed at the end!)

2. If the length of the file name is zero, this is not a valid directory entry. It may be one for a file that has been deleted.



Sub-directories

Sub-directories are exactly the same as the root directory. They have an entry in their parent directory which includes a file id number. This is used in exactly the same manner as for the root directory to find the location on disk.

As with the root directory, all sub-directories hold a blank entry (all zeros) as the very first one in the directory.

User Files

User files are identical to directories in as much as finding where they are on disc and so on, so the above calculations apply.

There is a 64 byte copy of the directory entry for the file, stored at the beginning of each file in the actual data sectors on disc. Application programs do not see these 64 bytes. The following example shows this dummy 64 bytes.

This is the beginning of my boot file on my QPC hard disc :

1000 TK2_EXT
1005 WTV 4
1010 PROG_USE 'win1_'
1015 DATA_USE 'win1_'
1020 :
1025 REMark LRESPR 'qpac2_ptr_gen'
1030 REMark LRESPR 'qpac2_wman'
1035 REMark LRESPR 'qpac2_hot_rext'
1040 LRESPR 'qpac2_qpac2'
1045 LRESPR 'qmenu_menu_rext'
1050 LRESPR 'source_djtk_DJToolkit_bin'
1052 LRESPR 'turbo415_turbo_tk_code'

The directory entry for this file is as follows, I have highlighted the File Id in the directory entry.

0002e0c0  00 00 04 6b 00 00 00 00  00 00 00 00 00 00 00 04  |...k............|
0002e0d0  62 6f 6f 74 00 00 00 00  00 00 00 00 00 00 00 00  |boot............|
0002e0e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
0002e0f0  00 00 00 00 4e 4e a6 e6  00 01 00 60 57 c5 9f 16  |....NN.....`W...|
                                         ^^^^^ 

Using the calculations to locate the start of the file on the sectors of the disc, we have :

File Id = $60
Sectors In Group = $04
Sector Size = $200

So, $60 * $04 * $200 = $30000

Looking at the disc file at that location, we see the following :

00030000  65 78 74 20 3d 3d 20 6c  69 66 6f 6e 6f 64 65 29  |ext == lifonode)|
00030010  0a 20 20 20 20 20 20 20  20 7b 0a 20 20 20 20 20  |.        {.     |
00030020  20 20 20 20 20 20 20 62  72 65 61 6b 3b 0a 20 20  |       break;.  |
00030030  20 20 20 20 20 20 7d 0a  20 20 20 20 7d 0a 20 20  |      }.    }.  |

The 4 rows above make up the dummy 64 byte file header. It looks like a load of garbage to me and is probably left over from whatever file was using this sector in the past, before my boot file was created. Following on from the above, we see this :

00030040  31 30 30 30 20 54 4b 32  5f 45 58 54 0a 31 30 30  |1000 TK2_EXT.100|
00030050  35 20 57 54 56 20 34 0a  31 30 31 30 20 50 52 4f  |5 WTV 4.1010 PRO|
00030060  47 5f 55 53 45 20 27 77  69 6e 31 5f 27 0a 31 30  |G_USE 'win1_'.10|
00030070  31 35 20 44 41 54 41 5f  55 53 45 20 27 77 69 6e  |15 DATA_USE 'win|
00030080  31 5f 27 0a 31 30 32 30  20 3a 0a 31 30 32 35 20  |1_'.1020 :.1025 |
00030090  52 45 4d 61 72 6b 20 4c  52 45 53 50 52 20 27 71  |REMark LRESPR 'q|
000300a0  70 61 63 32 5f 70 74 72  5f 67 65 6e 27 0a 31 30  |pac2_ptr_gen'.10|
000300b0  33 30 20 52 45 4d 61 72  6b 20 4c 52 45 53 50 52  |30 REMark LRESPR|
000300c0  20 27 71 70 61 63 32 5f  77 6d 61 6e 27 0a 31 30  | 'qpac2_wman'.10|
000300d0  33 35 20 52 45 4d 61 72  6b 20 4c 52 45 53 50 52  |35 REMark LRESPR|
000300e0  20 27 71 70 61 63 32 5f  68 6f 74 5f 72 65 78 74  | 'qpac2_hot_rext|
000300f0  27 0a 31 30 34 30 20 4c  52 45 53 50 52 20 27 71  |'.1040 LRESPR 'q|
00030100  70 61 63 32 5f 71 70 61  63 32 27 0a 31 30 34 35  |pac2_qpac2'.1045|
00030110  20 4c 52 45 53 50 52 20  27 71 6d 65 6e 75 5f 6d  | LRESPR 'qmenu_m|
00030120  65 6e 75 5f 72 65 78 74  27 0a 31 30 35 30 20 4c  |enu_rext'.1050 L|
00030130  52 45 53 50 52 20 27 73  6f 75 72 63 65 5f 64 6a  |RESPR 'source_dj|
00030140  74 6b 5f 44 4a 54 6f 6f  6c 6b 69 74 5f 62 69 6e  |tk_DJToolkit_bin|
00030150  27 0a 31 30 35 32 20 4c  52 45 53 50 52 20 27 74  |'.1052 LRESPR 't|
00030160  75 72 62 6f 34 31 35 5f  74 75 72 62 6f 5f 74 6b  |urbo415_turbo_tk|
00030170  5f 63 6f 64 65 27 0a 31  30 35 35 20 3a 0a 31 30  |_code'.1055 :.10|

We can see from the above that the first 64 bytes of each file are total garbage. The so called copy of the directory header is not reliable in the slightest and cannot be used for any purpose what-so-ever by applications running on QDOSMSQ. However, applications running on Linux, Windows or Macs might be able to make use of these additional 64 bytes, if they really needed to.

Create A Directory

Creating a directory is easy. The length of the directory is always 64 bytes.

  • Use the qwa_free value from the header as the FileId.
  • Fetch the entry in the map for this FileId, and store it back in qwa_free as the new first free group.
  • Put a word of zero into the map for the FileId we are using.
  • Increment by one, the qwa_uchk field in the header.
  • In the parent directory, create a 64 byte entry for the newly created directory. Remember, the length is initially set to 64 bytes.

Delete A Directory

Normally you are not allowed to delete a directory unless it is empty. However, if you are deleting an empty directory, the process is exactly as described below for deleting a file.

I suppose if we wanted to delete a directory that was full, we should do something like the following :

Procedure DeleteDirectoryTree(FileId, FileType)
  # Entry is with a FileId & FileType representing the root or the directory tree we wish to delete.

  If FileType is not a directory then
    return
  End If

  For each entry in FileId's contents
    Find the FileId and FileType for the (next) entry in the directory
    If FileType is not a directory then
       DeleteFile(FileId)
    Else
       DeleteDirectoryTree(FileId, FileType)
    End If
  End For
  Return
End.

Create A File

Creating a file is easy too.

  • Use the qwa_free value from the header as the FileId.
  • Fetch the entry in the map for this FileId, and store it back in qwa_free as the new first free group.
  • Put a word of zero into the map for the FileId we are using.
  • Increment by one, the qwa_uchk field in the header.
  • In the parent directory, create a 64 byte entry for the newly created file. The length will be 64 because although we ignore the first 64 bytes in all files, we still have to account for it in the directory.

Extend A File

Delete A File

The process to delete a file is quite simple, assuming the FileId you are attempting to delete belongs to a file and not to a directory. If it is a directory, you'll have to go recursive and delete all the files in the directory first. I leave that as an exercise for the reader! LOL. Normally, however, you are not permitted to delete a directory that has any contents, so a recursive delete might not be needed.

Assuming that the FileId is indeed for a file, we can delete it as follows :

  • Given the FileId from whichever directory the file lives in, go to that Map entry.
  • Walk the list of chained map entries until you reach the final one counting entries as you go.
  • Replace the zero word with the current FirstFreeGroup entry from the disc header.
  • Update the header with the FileId as the new FirstFreeGroup.
  • Add the number of groups the file used to need to the number of free groups in the disc header.
  • Increment the header's update counter by 1.
  • The Directory entry for the file has to have the file length and the file's name length set to zero.
  • The actual sectors on disc are not touched in anyway.

So given this situation for a file called Win1_testfile :

FileId$0003
FileLength$0000507C bytes
FileNameLength$0008
FirstFreeGroup$0009
Map Entries for FileId $0003$0004,$0005,$0006,$0007,$0008,$0000
FreeList Map Entries (start at group $0009)$000A,$000B,$000C,….
FreeGroupsInMap$03FD
UpdateCounter$085E0002

Deleting the file with Id 3 frees up the space on the disc corresponding to the linked list of Map Entries from entry 3 through to entry 8 - the file is a 'big' one of 6 groups of 4 (in this example) sectors. After the deletion, the situation is this :

FileId$0003
FileLength$00000000 bytes
FileNameLength$0000
FirstFreeGroup$0003
FreeList Map Entries (start at group $0003 now)$0004,$0005,$0006,$0007,$0008,$0009,$000A,$000B,$000C,….
FreeGroupsInMap$0403
UpdateCounter$085E0003

So the 6 groups of sectors that used to make up FileId 3 are now tagged onto the front of the free list and the header of the disc has been updated to reflect an additional 6 groups of free space. The update counter has been incremented as well.

It appears then that an undelete utility for QLWA format discs would not be of very much use as the space used by the file just deleted will be the first space used when any file is created or extended. Basically, if you delete a file, you need to undelete it almost immediately and certainly before updating or creating any files - including directories!

The before and after directory entries are as follows :

00001040 : 00 00 50 7C 00 00 00 00  00 00 00 00 00 00 00 08    [..P|............]
00001050 : 54 45 53 54 46 49 4C 45  00 00 00 00 00 00 00 00    [TESTFILE........]
00001060 : 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00    [................]
00001070 : 00 00 00 00 59 19 7E 15  00 01 00 03 00 00 00 00    [....Y.~.........]
00001040 : 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00    [................]
           ^^^^^^^^^^^                                ^^^^^
00001050 : 54 45 53 54 46 49 4C 45  00 00 00 00 00 00 00 00    [TESTFILE........]
00001060 : 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00    [................]
00001070 : 00 00 00 00 59 19 7E 15  00 01 00 03 00 00 00 00    [....Y.~.........]

Formatting a QXL.WIN file as a new hard disc is quite a simple process. From a few tests of various sizes of hard disc, I have determined the following process. Each and every sector group in the formatted disc has an entry in the map.

  • The size of the disc is used to determine the number of sectors in a group. The minimum appears to be 4 (for a 1 MB disc) and increases by 1 each time the file gets too big to fit into the maximum of 65,536 map entries.
  • The number of sectors in each map entry is calculated as the smallest number that fits into a maximum of 65,536 map entries. Roughly it is the larger of 4 or ((SizeInMb / 32) rounded up to the next whole number if not an exact multiple).
  • The map is created in memory as a linked list of words, all pointing at the next word. The last one is set to zero.
  • The header is initialised.
  • The update check field high word is a copy of the system random number (SV_RAND), the low word is set to $0001.
  • Each sector is written with 512 bytes of zeros. ($00 and not the character '0'.)

On a 32 bit system, the maximum size of a single disc file is limited to 4 Gb which is 4,096 Mb. A qxl.win file of this size, would use a group size of 128 sectors or 64 Kb.

Format Example

Formatting a file, for use as WIN2, to 30Mb would create a header as follows :

MnemonicOffsetValueComments
qwa.id $0000'QLWA'Identifier.
qwa_name$0004DC.W $0004,
DC.B 'WIN2'
Disc name.
qwa_spr0$001ADC.W $0000Spare.
qwa_uchk$001CDC.L $xxxx0001Update check. The high word is set to the value in the SV_RAND system variable.
qwa_intl$0020DC.W $0000Interleave factor.
qwa_sctg$0022DC.W $0004Sectors per group.
qwa_sctt$0024DC.W $0000Sectors per track.
qwa_trkc$0026DC.W $0000Tracks per cylinder (number of heads).
qwa_cyld$0028DC.W $0000Cylinders per drive.
qwa_ngrp$002ADC.W $3C00Number of groups.
qwa_fgrp$002CDC.W $3bf0Number of free groups. (qwa_ngrp - qwa_root).
qwa_sctm$002EDC.W $003DSectors per map.
qwa_nmap$0030DC.W $0001Number of maps.
qwa_free$0032DC.W $0011First free group. (qwa_root + 1).
qwa_root$0034DC.W $0010Root directory number. (qwa_sctm / qwa_sctg) + 1.
qwa_rlen$0036DC.L $00000040Root directory length.
qwa_fsct$003ADC.L $00000000First sector for this partition.
qwa_park$003EDC.W $0000Park cylinder.
  • Qwa_sctg is calculated as explained above, the size in Mb is divided by 32 and rounded up. If this is less than 4, then round up to 4. For our 30 Mb disc we get a group size of 4 sectors.
  • The total number of sectors is simply 30Mb / 512 which is 61,440. This allows qwa_ngrp to be set to (61,440 / qwa_sctg) which gives us 15,360 or $3C00.
  • Now we calculate ((qwa_ngrp * $0002) + $0040) / 512 rounded up to the next full sector size for our number of sectors to hold the map. (qwa_sctm). This works out at $003D.
  • Qwa_sctm is the number of sectors the group map requires, so that equates to the first (qwa_sctm/qwa_sctg) entries in the map. This works out at $003D/$0004 = $000F so the first 15 entries in the map belong to the map itself. This means that the root directory will get the following entry, $0010, and this is used as the root directory's file number in qwa_root.
  • The root directory length is preset to $0040 - this is an empty file in other words. Remember, every file's length in the directory is 64 bytes bigger than it really is as there is a waste of 64 bytes at the start of the first sector of every file. Directories are no exception.
  • Qwa_free, the first free group in the map, is qwa_root + 1.
  • Qwa_fgrp, the number of free groups, is set to (qwa_ngrp - qwa_root)

The header takes up the first $0040 bytes of the first sector of the qxl.win disc.

Following the header is the map. The map starts with the groups required for the map itself. In a 30Mb file, as calculated above, the map requires $003D sectors (from qwa_sctm) which means 15 groups of qwa_sctg (4) sectors. Then we have the zero word (indicated below) for the first, and only, sector group for the root directory.

00000040 : 00 01 00 02 00 03 00 04  00 05 00 06 00 07 00 08    [................]
00000050 : 00 09 00 0A 00 0B 00 0C  00 0D 00 0E 00 0F 00 00    [................]
                                                      ^^^^^

Following the entries for the map itself and the root directory, every other entry in the map is a free group.

00000060 : 00 11 00 12 00 13 00 14  00 15 00 16 00 17 00 18    [................]
...
00007830 : 3B F9 3B FA 3B FB 3B FC  3B FD 3B FE 3B FF 00 00    [................]

The remainder of the file is simply filled with CHR$(0).

Example Map Group Sizes

The table below shows a list of all the group sizes (in sectors) and the maximum size of a qxl.win file with that group size.

In order to reduce the length of the page, I've created the table with 4 pairs of columns rather than one. This means that there are not 124 lines, only 32. LOL Read across then down.

Group Size Disc size (Mb) Group Size Disc size (Mb) Group Size Disc size (Mb) Group Size Disc size (Mb)
4 128 5 160 6 192 7 224
8 256 9 288 10 320 11 352
12 384 13 416 14 448 15 480
16 512 17 544 18 576 19 608
20 640 21 672 22 704 23 736
24 768 25 800 26 832 27 864
28 896 29 928 30 960 31 992
32 1024 33 1056 34 1088 35 1120
36 1152 37 1184 38 1216 39 1248
40 1280 41 1312 42 1344 43 1376
44 1408 45 1440 46 1472 47 1504
48 1536 49 1568 50 1600 51 1632
52 1664 53 1696 54 1728 55 1760
56 1792 57 1824 58 1856 59 1888
60 1920 61 1952 62 1984 63 2016
64 2048 65 2080 66 2112 67 2144
68 2176 69 2208 70 2240 71 2272
72 2304 73 2336 74 2368 75 2400
76 2432 77 2464 78 2496 79 2528
80 2560 81 2592 82 2624 83 2656
84 2688 85 2720 86 2752 87 2784
88 2816 89 2848 90 2880 91 2912
92 2944 93 2976 94 3008 95 3040
96 3072 97 3104 98 3136 99 3168
100 3200 101 3232 102 3264 103 3296
104 3328 105 3360 106 3392 107 3424
108 3456 109 3488 110 3520 111 3552
112 3584 113 3616 114 3648 115 3680
116 3712 117 3744 118 3776 119 3808
120 3840 121 3872 122 3904 123 3936
124 3968 125 4000 126 4032 127 4064
128 4096
  • qdosmsq/fs/qlwa.txt
  • Last modified: 2018/05/06 22:33
  • by superuser