CoCo Disk BASIC disk structure – part 2

See also: part 1 or part 2.

In the first installment, we began exploring the anatomy of a Disk BASIC disk and how it is made up of tracks and sectors. We also took a peek at the file allocation table (FAT) and a simple program was shared that would count the number of empty granules on the disk. It did this by reading the file allocations able sector (track 17, sector 2) and looking at the first 68 bytes (which represent the status of the 68 available granules). If a byte was 255, the granule is free.

But what if it isn’t free?

File allocation table (FAT) revisited

In that case, the number will represent one of two things:

  1. If the granule is used, but the file is larger and continues on to another granule, the value will be the granule number for the next granule of the file. It’s a linked list!
  2. If the granule is used, but the file does not continue to another granule, the top two bits will be set (11xxxxxx, hex value &HC0 or decimal 192) and the remaining five bits will indicate how many sectors of that granule are part of the file.

If each granule is 2304 (and it is), and a file is 6000 bytes, it is going to need three granules (2304 *3 = 6912) to store that file. That would be two full granules (4608 bytes) and then the remaining 1292 bytes (6000-4608=1392) in the third granule. 1392 bytes needs 6 sectors (6*256 = 1536) to fit the remaining data.

But, since files are not always exactly multiples of 256-bytes, there is one more bit of information that tells how many bytes in the final sector are used by the file. That value is part of the directory entry in bytes 14-15:

Since the full file size is not part of the directory entry, we will need to scan the File Allocation Table and do some calculations. Here are what the 68 bytes of the FAT can be:

RS-DOS FAT

To calculate the size of a file, we need to do these steps:

  1. Get the file’s directory entry (32 bytes), specifically byte 13 (the number of the first granule in the file) and bytes 14-15 (the number of bytes used in the last sector of the file).
  2. Read the FAT byte that corresponds to the start granule of the file, then…
    • If the value is 0-67, that is the number of the next granule used by the file. Add the size of the granule (2304 bytes) and get the next granule value.
    • If the value has high two bits set (11000000), the remaining value will be how many sectors of that granule are used by the file. Since this is the last sector, add the “number of bytes used in the last sector” from the directory entry (bytes 14-15) then the number of sectors minus 1 multiplied by the size of a sector (256).

“And it’s just that easy!”

So let’s try it… Here is a more “complete” DIR program, though this time instead of doing less, it does more by showing the size of the file in bytes, rather than how many granules it takes up on disk, and by showing file types in a more verbose/descriptive way.

To speed it up (even though it is still slow), it will load the FAT entries in to an array, along with the directory entries. This makes calculating the size easier since everything is now a variable in an array rather than having to read and parse bytes from a disk sector.

10 ' FILEINFO.BAS
20 '
30 ' 0.0 2023-01-25 ALLENH
40 ' 0.1 2023-01-26 ADD DR
50 ' 0.2 2023-01-27 MORE COMMENTS
60 '
70 ' E$(0-1) - SECTOR HALVES
80 ' FT$ - FILE TYPE STRINGS
90 '
100 CLEAR 1500:DIM E$(1),FT$(3)
110 FT$(0)="BPRG":FT$(1)="BDAT":FT$(2)="M/L ":FT$(3)="TEXT "
120 '
130 ' DIR HOLDS UP TO 72 ENTRIES
140 '
150 ' NM$ - NAME
160 ' EX$ - EXTENSION
170 ' FT - FILE TYPE (0-3)
180 ' AF - ASCII FLAG (0/255)
190 ' FG - FIRST GRANULE #
200 ' BU - BYTES USED IN LAST SECTOR
210 ' SZ - FILE SIZE
220 '
230 DIM NM$(71),EX$(71),FT(71),AF(71),FG(71),BU(71),SZ(71)
240 '
250 INPUT "DRIVE";DR
260 '
270 ' FILE ALLOCATION TABLE
280 ' 68 GRANULE ENTRIES
290 '
300 DIM FA(67)
310 DSKI$ DR,17,2,G$,Z$:Z$=""
320 FOR G=0 TO 67
330 FA(G)=ASC(MID$(G$,G+1,1))
340 NEXT
350 '
360 ' READ DIRECTORY
370 '
380 DE=0
390 FOR S=3 TO 11
400 DSKI$ DR,17,S,E$(0),E$(1)
410 '
420 ' PART OF SECTOR
430 '
440 FOR P=0 TO 1
450 '
460 ' ENTRY WITHIN SECTOR PART
470 '
480 FOR E=0 TO 3
490 '
500 ' DIR ENTRY IS 32 BYTES
510 '
520 E$=MID$(E$(P),E*32+1,32)
530 '
540 ' NAME IS FIRST 8 BYTES
550 '
560 NM$(DE)=LEFT$(E$,8)
570 '
580 ' EXTENSION IS BYTES 9-11
590 '
600 EX$(DE)=MID$(E$,9,3)
610 '
620 ' FILE TYPE IS BYTE 12
630 '
640 FT(DE)=ASC(MID$(E$,12,1))
650 '
660 ' ASCII FLAG IS BYTE 13
670 '
680 AF(DE)=ASC(MID$(E$,13,1))
690 '
700 ' FIRST GRANUAL IS BYTE 14
710 '
720 FG(DE)=ASC(MID$(E$,14,1))
730 '
740 ' BYTES USED IN LAST SECTOR
750 ' ARE IN BYTES 15-16
760 '
770 BU(DE)=ASC(MID$(E$,15,1))*256+ASC(MID$(E$,16,1))
780 '
790 ' IF FIRST BYTE IS 255, END
800 ' OF USED DIR ENTRIES
810 '
820 IF LEFT$(NM$(DE),1)=CHR$(255) THEN 1390
830 '
840 ' IF FIRST BYTE IS 0, FILE
850 ' WAS DELETED
860 '
870 IF LEFT$(NM$(DE),1)=CHR$(0) THEN 1370
880 '
890 ' SHOW DIRECTORY ENTRY
900 '
910 PRINT NM$(DE);TAB(9);EX$(DE);"  ";FT$(FT(DE));" ";
920 IF AF(DE)=0 THEN PRINT"ASC"; ELSE PRINT "BIN";
930 '
940 ' CALCULATE FILE SIZE
950 ' SZ - TEMP SIZE
960 ' GN - TEMP GRANULE NUM
970 ' SG - SECTORS IN LAST GRAN
980 '
990 SZ=0:GN=FG(DE):SG=0
1000 '
1010 ' GET GRANULE VALUE
1020 ' GV - GRAN VALUE
1030 '
1040 GV=FA(GN)
1050 '
1060 ' IF TOP TWO BITS SET (C0
1070 ' OR GREATER), IT IS THE
1080 ' LAST GRANULE OF THE FILE
1090 ' SG - SECTORS IN GRANULE
1100 '
1110 IF GV>=&HC0 THEN SG=(GV AND &H1F):GOTO 1280
1120 '
1130 ' ELSE, MORE GRANS
1140 ' ADD GRANULE SIZE
1150 '
1160 SZ=SZ+2304
1170 '
1180 ' MOVE ON TO NEXT GRANULE
1190 '
1200 GN=GV
1210 GOTO 1040
1220 '
1230 ' DONE WITH GRANS
1240 ' CALCULATE SIZE
1250 '
1260 ' FOR EMPTY FILES
1270 '
1280 IF SG>0 THEN SG=SG-1
1290 '
1300 ' FILE SIZE IS SZ PLUS
1310 ' 256 BYTES PER SECTOR
1320 ' IN LAST GRAN PLUS
1330 ' NUM BYTES IN LAST SECT
1340 '
1350 SZ(DE)=SZ+(SG*256)+BU(DE)
1360 PRINT " ";SZ(DE)
1370 DE=DE+1
1380 NEXT:NEXT:NEXT
1390 END
1400 ' SUBETHASOFTWARE.COM

It looks like this:

And that, I suppose, is about as much disk talk as I can handle for the moment. Let me know in the comments if I missed anything else important.

Until then…

2 thoughts on “CoCo Disk BASIC disk structure – part 2

  1. David McNally

    I loved this article. A couple of questions. Where is the extension separator stored if not using /. Some disks used . Or dash? Also what would be the correct process to save a file properly using a manual method?

    Reply
    1. Allen Huffman Post author

      The separator is not stored – 8 characters then 3 characters in the entry. “A.BAS” would be “A———BAS” with the – representing a space.

      Reply

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.