{"id":2261,"date":"2018-11-11T11:12:02","date_gmt":"2018-11-11T11:12:02","guid":{"rendered":"http:\/\/www.sydneysmith.com\/wordpress\/?p=2261"},"modified":"2018-11-11T11:17:15","modified_gmt":"2018-11-11T11:17:15","slug":"what-is-in-a-cp-m-file-control-block","status":"publish","type":"post","link":"https:\/\/www.sydneysmith.com\/wordpress\/2261\/what-is-in-a-cp-m-file-control-block\/","title":{"rendered":"What is in a CP\/M File Control Block?"},"content":{"rendered":"<p>This seems straight forward and already well documented; but it isn&#8217;t. There are some of the underlying &#8220;technical details&#8221; that aren&#8217;t mentioned but which have a big effect at the higher level. <!--more--><\/p>\n<p>The <a href=\"http:\/\/www.cpm.z80.de\/manuals\/cpm22-m.pdf\" target=\"_blank\">CP\/M 2.2 Manual<\/a> says, on page 5-8, &#8220;Internally, all files are divided into 16K byte segments called logical extents, so that counters are easily maintained as 8-bit values. The division into extents is discussed in the paragraphs that follow: however, they are not particularly significant for the programmer, &#8230;&#8221;.<\/p>\n<p>Whilst the statement is true, as &#8220;counters are &#8230; 8-bit values&#8221; and &#8220;extents &#8230; are not <em>particularly<\/em> significant&#8221;; it does suggest that extents contain 8-bit values and that programmers needn&#8217;t worry about extents. Both of those conclusions are wrong.<\/p>\n<h2>One-byte Block Numbers<\/h2>\n<p>In CP\/M 1, a File Control Block (FCB) was a 32 byte copy in memory of 32 bytes in the disk directory. Block numbers were one-byte (8-bit) numbers and the FCB map contained up to 16 block numbers, with each block being 1K bytes in size. That&#8217;s where the 16K byte segments come from, CP\/M 1. Everything in a CP\/M 1 FCB is either text (the filename) or an 8-bit value. Every extent is 16K bytes long.<\/p>\n<p>CP\/M 2.X supports larger disks. That is one of its features. You&#8217;ll see more about that in section 6.10 &#8220;Disk Parameter Tables&#8221; of the manual. If you use a disk larger than 256K bytes, you have to use 2K blocks. You can&#8217;t get block numbers larger than 255 into a one-byte (8-bit) value so they increased the size of the block. That worked for disk sizes up to 512 KB; but it also introduced a problem.<\/p>\n<p>The problem is &#8220;files are divided into 16K byte segments called logical extents&#8221; yet we now have 16 slots and 2K blocks. Is an extent 16K or is it 32K?<\/p>\n<p>There seems two answers and vendors of CP\/M-compatible operating systems could have chosen either, with half being right and half being wrong. However, there is a third answer that makes all of them wrong. Digital Research, the manufacturer of CP\/M, chose the third answer. What&#8217;s the third answer? Both.<\/p>\n<p>According to Digital Research, an extent is 16KB. So, in memory, the extent counter &#8220;ex&#8221; ticks over every 16K. But, on disk, each FCB contains 32KB and there are gaps in &#8220;ex&#8221; for a file, on disk. You see things for ex like &#8220;01&#8221;, &#8220;03&#8221;, &#8220;05&#8221;, etc. Here&#8217;s some real examples:<code><\/p>\n<pre>\r\n64k CP\/M version 2.2L\r\n\r\nA>c:\r\nC>dir\r\nNO FILE\r\nC>r 104kb.txt\r\n\r\nREAD V-2.23 (05-Jul-18) Z80SIM Interface V1\r\nRead from \"104KB.TXT\" and write to \"104KB.TXT\".\r\n103.625kB written.\r\n\r\nC>dir\r\nC: 104KB    TXT\r\nC> (Ctrl-F10 to exit simulator)\r\nC:\\Test\\run-cpm22L>cpmfs drivec.dsk dir2\r\n\r\nBoot Sector\r\n3E 01 D3 40 21 00 E4 DD 21 EE 00 DD 36 00 02 11 >..@!...!...6...\r\n7F 31 01 0F 02 7B D3 04 CB 4B 28 15 7A D3 34 79 .1...{...K(.z.4y\r\nD3 30 97 3D 20 FD DB 34 1F 30 FB DB 30 E6 98 20 .0.= ..4.0..0..\r\nCF 78 D3 32 7A F6 80 D3 34 0E 33 3E 9C D3 30 DB .x.2z...4.3>..0.\r\n34 1F 38 04 ED A2 18 F7 DB 30 CB 67 28 B2 DD 35 4.8......0.g(..5\r\n00 CA 00 FA 3A FC 00 FE 44 20 02 CB F2 06 01 3A ....:...D .....:\r\nFA 00 FE 44 20 04 7B EE 02 5F 0E 5F 18 A7 FF FF ...D .{.._._....\r\nFF FF FF FF FF FF FF FF 4C 47 44 53 53 44 E5 E5 ........LGDSSD..\r\n\r\nDirectory\r\n00 31 30 34 4B 42 20 20 20 54 58 54 01 00 00 80 .104KB   TXT....\r\n02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 ................\r\n00 31 30 34 4B 42 20 20 20 54 58 54 03 00 00 80 .104KB   TXT....\r\n12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 .............. !\r\n00 31 30 34 4B 42 20 20 20 54 58 54 05 00 00 80 .104KB   TXT....\r\n22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 \"#$%&'()*+,-.\/01\r\n00 31 30 34 4B 42 20 20 20 54 58 54 06 00 00 3D .104KB   TXT...=\r\n32 33 34 35 00 00 00 00 00 00 00 00 00 00 00 00 2345............\r\n\r\n\r\nC:\\Test\\run-cpm22L>\r\n<\/pre>\n<p><\/code><br \/>\nThis is a large (8&#8243; \/ LG) DSSD disk. It uses 2KB blocks and one-byte block numbers.<\/p>\n<p>The &#8220;ex&#8221; value is just after the file type (&#8220;TXT&#8221;). You see 01, 03, 05 and 06. Under CP\/M 1 you&#8217;d see 00, 01, 02, 03, 04, 05, 06.<\/p>\n<p>You can see 16 block numbers in most of the extents (eg for ex=01, 02 03 04 &#8230; 10 11).<\/p>\n<p>An extent, on disk, holds 32K bytes. They are numbered 01, 03, 05, etc.<\/p>\n<p>Here&#8217;s the same file on a CDOS 2K one-byte block number disk:<code><\/p>\n<pre>\r\n\r\nCDOS version 02.58\r\nCromemco Disk Operating System\r\nCopyright (C) 1977, 1983 Cromemco, Inc.\r\n\r\nA.b:\r\n\r\nB.dir\r\n*** 0 Files, 0 Entries, 0 K Displayed, 490 K Left ***\r\nB.r 104kb.txt\r\n\r\nREAD V-2.23 (05-Jul-18) Z80SIM Interface V1\r\nRead from \"104KB.TXT\" and write to \"104KB.TXT\".\r\n103.625kB written.\r\n\r\nB.dir\r\n104KB     TXT   104K\r\n*** 1 Files, 7 Entries, 104 K Displayed, 386 K Left ***\r\nB. (Ctrl-F10 to exit the simulator)\r\nC:\\Test\\run-cdos0258-3>cpmfs driveb.dsk dir2\r\n\r\nBoot Sector\r\nE5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................\r\nE5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................\r\nE5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................\r\nE5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................\r\nE5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................\r\nE5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................\r\nE5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................\r\nE5 E5 E5 E5 E5 E5 E5 E5 4C 47 44 53 53 44 E5 E5 ........LGDSSD..\r\n\r\nCDOS disk label: Userdisk\r\nDate on disk   : 2000-00-00\r\nCluster size   : 2K\r\nDirectory type : Normal\r\nDirectory size : 128 entries\r\n\r\nDirectory\r\n81 55 73 65 72 64 69 73 6B 00 00 00 10 00 00 20 .Userdisk......\r\n00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................\r\n00 31 30 34 4B 42 20 20 20 54 58 54 00 00 00 80 .104KB   TXT....\r\n02 03 04 05 06 07 08 09 00 00 00 00 00 00 00 00 ................\r\n00 31 30 34 4B 42 20 20 20 54 58 54 01 00 00 80 .104KB   TXT....\r\n0A 0B 0C 0D 0E 0F 10 11 00 00 00 00 00 00 00 00 ................\r\n00 31 30 34 4B 42 20 20 20 54 58 54 02 00 00 80 .104KB   TXT....\r\n12 13 14 15 16 17 18 19 00 00 00 00 00 00 00 00 ................\r\n00 31 30 34 4B 42 20 20 20 54 58 54 03 00 00 80 .104KB   TXT....\r\n1A 1B 1C 1D 1E 1F 20 21 00 00 00 00 00 00 00 00 ...... !........\r\n00 31 30 34 4B 42 20 20 20 54 58 54 04 00 00 80 .104KB   TXT....\r\n22 23 24 25 26 27 28 29 00 00 00 00 00 00 00 00 \"#$%&'()........\r\n00 31 30 34 4B 42 20 20 20 54 58 54 05 00 00 80 .104KB   TXT....\r\n2A 2B 2C 2D 2E 2F 30 31 00 00 00 00 00 00 00 00 *+,-.\/01........\r\n00 31 30 34 4B 42 20 20 20 54 58 54 06 00 00 3D .104KB   TXT...=\r\n32 33 34 35 00 00 00 00 00 00 00 00 00 00 00 00 2345............\r\n\r\n\r\nC:\\Test\\run-cdos0258-3>\r\n<\/pre>\n<p><\/code><br \/>\nIt is the same disk type (LGDSSD). This one has a CDOS label to allow it to access the extra space and the label matches what I said for the CP\/M one before this: 2K blocks and one-byte block numbers.<\/p>\n<p>This time there are 7 extents on the disk. These are all labelled sequentially: 00, 01, 02, 03, 04, 05 and 06.<\/p>\n<p>There are one-byte block numbers; but there are only 8 in each of the 16K extents. eg (for ex=01, 0A 0B 0C 0D 0E 0F 10 11). This is the last half of the CP\/M ex=01 disk FCB for the same file. The CP\/M disk FCB for ex 01 contains the ex 00 half at the front, and the ex 01 part at the end.<\/p>\n<p>CDOS contains a mechanism that allows us to look more directly at the directory. In case you&#8217;re thinking perhaps cpmfs is displaying it wrong, here&#8217;s what CDOS has to say for itself:<code><\/p>\n<pre>\r\nB.dir\r\n104KB     TXT   104K\r\n*** 1 Files, 7 Entries, 104 K Displayed, 386 K Left ***\r\nB.debug sys.dir\r\nDEBUG version 00.20\r\nNEXT  = 1100\r\nNEXTM = 1100\r\n-d100\r\n0100   81 55 73 65  72 64 69 73  6B 00 00 00  10 00 00 20  .Userdisk......\r\n0110   00 01 00 00  00 00 00 00  00 00 00 00  00 00 00 00  ................\r\n0120   00 31 30 34  4B 42 20 20  20 54 58 54  00 00 00 80  .104KB   TXT....\r\n0130   02 03 04 05  06 07 08 09  00 00 00 00  00 00 00 00  ................\r\n0140   00 31 30 34  4B 42 20 20  20 54 58 54  01 00 00 80  .104KB   TXT....\r\n0150   0A 0B 0C 0D  0E 0F 10 11  00 00 00 00  00 00 00 00  ................\r\n0160   00 31 30 34  4B 42 20 20  20 54 58 54  02 00 00 80  .104KB   TXT....\r\n0170   12 13 14 15  16 17 18 19  00 00 00 00  00 00 00 00  ................\r\n-d\r\n0180   00 31 30 34  4B 42 20 20  20 54 58 54  03 00 00 80  .104KB   TXT....\r\n0190   1A 1B 1C 1D  1E 1F 20 21  00 00 00 00  00 00 00 00  ...... !........\r\n01A0   00 31 30 34  4B 42 20 20  20 54 58 54  04 00 00 80  .104KB   TXT....\r\n01B0   22 23 24 25  26 27 28 29  00 00 00 00  00 00 00 00  \"#$%&'()........\r\n01C0   00 31 30 34  4B 42 20 20  20 54 58 54  05 00 00 80  .104KB   TXT....\r\n01D0   2A 2B 2C 2D  2E 2F 30 31  00 00 00 00  00 00 00 00  *+,-.\/01........\r\n01E0   00 31 30 34  4B 42 20 20  20 54 58 54  06 00 00 3D  .104KB   TXT...=\r\n01F0   32 33 34 35  00 00 00 00  00 00 00 00  00 00 00 00  2345............\r\n-\r\n<\/pre>\n<p><\/code><br \/>\nDespite SYS.DIR not being present as a file on any disk, you can load it successfully into memory with DEBUG.COM (the CDOS equivalent of ZSID.COM). You can also do edits and write the non-existent &#8220;file&#8221; back over the directory so it&#8217;s not generally mentioned to users.<\/p>\n<p>You can see the disk label and the same items in the on-disk FCBs. It&#8217;s not just cpmfs.<\/p>\n<p>It&#8217;s a little harder to do for the CP\/M disk (image) but you can use a hex editor or viewer to see what&#8217;s in the directory. It&#8217;s a match to what cpmfs is saying.<\/p>\n<p>If you have a disk image from the era, that was created on a system from another manufacturer, there&#8217;s a chance you&#8217;ll also see 16x2K blocks in an on-disk FCB and with the &#8220;ex&#8221; values being sequential. That implies they guessed that Digital Research would come out with 32K byte extents for larger disks. (They would have also had to cope with the &#8220;rc&#8221; field holding a record count of 0-100H, so probably they&#8217;d use the &#8220;s2&#8221; byte as a high-order rc value (guess only).<\/p>\n<p>Digital Research did their high-order rc value by using the low bit\/s of the &#8220;ex&#8221; value. The LHS of ex is the equivalent 32K byte extent number (01 03 05 06 becomes 00 01 02 03 &#8211; a nice sequence) and the RHS bit\/s go to the front of the &#8220;rc&#8221; byte (sort of). You get an &#8220;rc&#8221; of 180H 180H 180H 03DH from the CP\/M ex&#038;exm,rc bytes. 180H should be read as 1x80H+80H and 03DH as 0x80H+3DH. There are 100H records in each of the first three on-disk extents and 03DH records in the last one.<\/p>\n<h2>Does it Matter?<\/h2>\n<p>Here&#8217;s what happens if you take the two disks (CDOS 2K 8&#215;1 and CP\/M 2K 16&#215;1) and swap them: <code><\/p>\n<pre>\r\nB.type 104kb.txt\r\nFile not found\r\nB.dir\r\n104KB     TXT   104K\r\n*** 1 Files, 4 Entries, 104 K Displayed, 200 K Left ***\r\nB.\r\n<\/pre>\n<p><\/code><br \/>\nIn CDOS, the CP\/M disk doesn&#8217;t have a disk label so it thinks the blocks are 1K in size (run CDOS STAT to see this). The &#8220;200 K Left&#8221; statement is 254 blocks (maximum with 1-byte blocks in CDOS) &#8211; the 52 blocks used by &#8220;104KB.TXT&#8221; (which required 52 blocks of, then, 2KB) &#8211; 2 directory blocks. There are 200 unused blocks and CDOS thinks they are 1KB each.<\/p>\n<p>The file shows up in the DIR listing because it has extents in the directory. However, you can&#8217;t TYPE it because extent 0 (the start) is missing. It&#8217;s a mess.<\/p>\n<p>Here&#8217;s the other one: <code><\/p>\n<pre>\r\nC>dir\r\nC: 104KB    TXT : 104KB    TXT\r\nC>type 104kb.txt\r\n0001 The quick brown fox jumped over the lazy dogs.\r\n0002 The quick brown fox jumped over the lazy dogs.\r\n...\r\n0306 The quick brown fox jumped over the lazy dogs.\r\n0307 The quick brown fox jumped over the lazy dogs.\r\n0308 The quick brown fox jumped over the lazy dogs.\r\n0309 The quick brown fox jumped over the lazy dogs.\r\n0310 Th\r\nC>\r\n<\/pre>\n<p><\/code><br \/>\nCP\/M has 2K blocks in its DPB in its BIOS so it treats the blocks as 2K.<\/p>\n<p>It successfully types all of the text up to block 09 which is the last block in the 8&#215;1 byte CDOS FCB.<br \/>\nThen it stops because it doesn&#8217;t think there are any more records (ex=00 and rc=80H so there&#8217;s 128 records not a full 256 records in this on-disk extent).<\/p>\n<p>The one that stands out though, is the two entries in the DIR for the same file. If it were a CP\/M 2K 1-byte FCB, it would only have ex=01, 03, 05, &#8230; or an even ex number at the end. DIR sees the ex=00 and thinks that is the file so it lists it. Then it sees ex=01 and that&#8217;s a valid first extent too so it lists that too. They just happen, in this case, to be for the same file. It&#8217;s a mess.<\/p>\n<p>Clearly, it does matter &#8211; a lot. It is hardly surprising that we had to resort to serial lines and null modems to copy files between computers rather just putting a disk from one, in the other.<\/p>\n<p>Knowing that the extent numbers (ex) on disk aren&#8217;t necessarily sequential any more would have helped (and &#8220;been particularly significant for the programmer&#8221; if they were writing a directory lister or a disk utility program). <\/p>\n<h2>Two-Byte Block Numbers<\/h2>\n<p>2K byte blocks and one-byte block numbers will only get you so far (512 KB). For disks larger than that, CP\/M allows you to use 4K blocks thus deferring the problem to 1024 KB disks, or to use two-byte block numbers instead.<\/p>\n<p>In most cases, two-byte block numbers are a much better solution because they allow better granularity and a much higher limit on disk space. With 2K blocks and two-byte block numbers, the upper limit goes to 128 MB per floppy disk. The map area of the FCB goes from 16x 1-byte values pointing to 1KB each, to 8x 2-byte values pointing to 2KB each. File extents (on disk or in memory) refer to &#8220;16K byte segments&#8221; again and all of the counter values go back to what they were. It&#8217;s a lot neater.<\/p>\n<p>Unfortunately, for all of CP\/M&#8217;s flexibility, you can&#8217;t tell it to use two-byte block numbers. It&#8217;s a choice it makes for itself.<\/p>\n<p>If the disk size is more than 256 blocks, CP\/M will switch to two-byte block numbers. It will only do so if the block size is at least 2K. This means it will never use two byte block numbers on disks smaller than 512 KB. If you cheat and tell it the disk is bigger, you are likely to get &#8220;WRITE ERROR&#8221; or &#8220;SEEK ERROR&#8221; messages instead of &#8220;DISK FULL&#8221; ones.<\/p>\n<p>So:<\/p>\n<ul>\n<li>CP\/M Ver 1 FCBs (up to 256 one-byte blocks of 1KB each) for disks up to 256 KB.<\/li>\n<li>CP\/M one-byte 2K, odd extents on disk FCBs for 256KB < disk size <= 512KB.<\/li>\n<li>CP\/M two-byte 2K FCBs for 512KB < disk size <= 128 MB.<\/li>\n<\/ul>\n<p>CDOS can also use two byte block numbers and its behaviour seems to exactly match CP\/M for these ones.<\/p>\n<p>Two-byte block number FCBs look like this: <code><\/p>\n<pre>\r\n00 31 30 34  4B 42 20 20  20 54 58 54  00 00 00 80  .104KB   TXT....\r\n04 00 05 00  06 00 07 00  08 00 09 00  0A 00 0B 00  ................\r\n00 31 30 34  4B 42 20 20  20 54 58 54  01 00 00 80  .104KB   TXT....\r\n0C 00 0D 00  0E 00 0F 00  10 00 11 00  12 00 13 00  ................\r\n00 31 30 34  4B 42 20 20  20 54 58 54  02 00 00 80  .104KB   TXT....\r\n14 00 15 00  16 00 17 00  18 00 19 00  1A 00 1B 00  ................\r\n00 31 30 34  4B 42 20 20  20 54 58 54  03 00 00 80  .104KB   TXT....\r\n1C 00 1D 00  1E 00 1F 00  20 00 21 00  22 00 23 00  ........ .!.\".#.\r\n00 31 30 34  4B 42 20 20  20 54 58 54  04 00 00 80  .104KB   TXT....\r\n24 00 25 00  26 00 27 00  28 00 29 00  2A 00 2B 00  $.%.&.'.(.).*.+.\r\n00 31 30 34  4B 42 20 20  20 54 58 54  05 00 00 80  .104KB   TXT....\r\n2C 00 2D 00  2E 00 2F 00  30 00 31 00  32 00 33 00  ,.-...\/.0.1.2.3.\r\n00 31 30 34  4B 42 20 20  20 54 58 54  06 00 00 3D  .104KB   TXT...=\r\n34 00 35 00  36 00 37 00  00 00 00 00  00 00 00 00  4.5.6.7.........\r\n<\/pre>\n<p><\/code><br \/>\nYou can see &#8220;ex&#8221; going from 00 to 06. &#8220;rc&#8221; is 80 for each full extent. You can even see the two-byte block numbers in the map area (eg for ex=00: 04 00, 05 00, 06 00, &#8230;, 0B 00).<\/p>\n<h2>Summary<\/h2>\n<p>There are at least four different FCB types: the original (CP\/M 1) type, the CP\/M 2 2K one-byte style, the CDOS 2K one-byte style, and a two-byte version.<\/p>\n<h2>More Information<\/h2>\n<p>This is part of the <a href=\"http:\/\/www.sydneysmith.com\/wordpress\/cpm-programs\/\">CP\/M topic<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This seems straight forward and already well documented; but it isn&#8217;t. There are some of the underlying &#8220;technical details&#8221; that aren&#8217;t mentioned but which have a big effect at the higher level.<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[16,59],"tags":[],"_links":{"self":[{"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/posts\/2261"}],"collection":[{"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/comments?post=2261"}],"version-history":[{"count":6,"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/posts\/2261\/revisions"}],"predecessor-version":[{"id":2267,"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/posts\/2261\/revisions\/2267"}],"wp:attachment":[{"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/media?parent=2261"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/categories?post=2261"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/tags?post=2261"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}