{"id":1128,"date":"2019-04-27T22:03:36","date_gmt":"2019-04-28T05:03:36","guid":{"rendered":"https:\/\/www.pagetable.com\/?p=1128"},"modified":"2019-04-27T22:03:36","modified_gmt":"2019-04-28T05:03:36","slug":"a-geos-speed-zone-bug-why-do-c64-geos-boot-disks-break-part-2","status":"publish","type":"post","link":"https:\/\/www.pagetable.com\/?p=1128","title":{"rendered":"A GEOS Speed Zone Bug? (Why Do C64 GEOS Boot Disks Break, Part 2)"},"content":{"rendered":"<p>I happened to come across 50 original German GEOS 2.0 disks that were broken and sent in for replacement. In the <a href=\"https:\/\/www.pagetable.com\/?p=1118\">first part<\/a>, I covered the disks that were broken probably due to user error. Now let&rsquo;s look at the read errors on the remaining disks. As it turns out, there might be a bug in GEOS that caused the boot disks to break!<\/p>\n<h2 id=\"Error-Types\">Error Types<\/h2>\n<p>On a 1541 disk, every track consists of sectors that are encoded like this:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"docs\/geosdiskerrors\/1.png\" height=\"64\" width=\"600\" alt=\"\" \/><\/p>\n<p>The SYNC marks are used to find the start of a header and the start of data. The header contains the number of the sector. Before and after the sector data, there are gaps to account for uncertainty in timing when overwriting a sector.<\/p>\n<p>On the GEOS boot disks, SYNC marks are 3 bytes long, and gaps are 8 bytes<sup id=\"fnref:1\"><a href=\"#fn:1\" rel=\"footnote\">1<\/a><\/sup>. Headers are always 10 bytes, and sector contents are 325<sup id=\"fnref:2\"><a href=\"#fn:2\" rel=\"footnote\">2<\/a><\/sup> bytes. About 2% of the end of each track are unused (tail gap).<\/p>\n<p>About 90% of a track is actual sector data. 3% is header data, and 1.5% are SYNC marks. So if a disk degrades through wear and tear, physical damage or demagnetizing, we expect mostly data checksum errors, and a maybe a few header checksum errors and missing SYNC marks. But this is the actual statistic:<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align:center;\"> Missing Header SYNC <\/th>\n<th style=\"text-align:center;\"> Missing Data SYNC <\/th>\n<th style=\"text-align:center;\"> Header Checksum Error <\/th>\n<th style=\"text-align:center;\"> Data Checksum Error <\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align:center;\"> 213                 <\/td>\n<td style=\"text-align:center;\"> 0                 <\/td>\n<td style=\"text-align:center;\"> 1               <\/td>\n<td style=\"text-align:center;\"> 205           <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The ratio between missing data sync and the checksum errors is about what we expect \u2013 but what about the <strong>huge<\/strong> number of missing header SYNCs?<\/p>\n<p>In addition, three of the disks that don&rsquo;t boot fail because of the very same issue:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"docs\/geosdiskerrors\/errort01s09.png\" height=\"440\" width=\"606\" alt=\"\" \/><\/p>\n<p>Three of the disks fail in DeskTop when trying to read track 1, sector 9 \u2013 because of a missing header SYNC.<\/p>\n<h2 id=\"Overwritten-Headers\">Overwritten Headers<\/h2>\n<p>A missing header can have different causes:<\/p>\n<ul>\n<li>The SYNCs that mark the header might have degraded. Commodore DOS writes 40 bit SYNC marks, but on GEOS boot disks, they are only 24 bits. But this would apply to header SYNCs and data SYNCs evenly, and we are not seeing any missing data blocks.<\/li>\n<li>The byte after the SYNC, which distinguishes the header from the sector, might have degraded. This is very specific and quite unlikely.<\/li>\n<li>The SYNC and maybe more parts of the header may have been overwritten.<\/li>\n<\/ul>\n<p>Checking several dozen cases showed exactly the same picture:<\/p>\n<ul>\n<li>The preceding sector was written by the user. (A <a href=\"https:\/\/www.pagetable.com\/?p=1118\">side effect of the copy protection<\/a> allows detecting which sectors are unchanged from the original disk contents and which sectors got overwritten.)<\/li>\n<li>The preceding sector data is intact.<\/li>\n<li>The preceding sector was written 7-8% more slowly \u2013 too much for the gap to cover. Its last bytes overwrote the next sector header, but not the next sector data.<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"docs\/geosdiskerrors\/5.png\" height=\"110\" width=\"600\" alt=\"\" \/><\/p>\n<h2 id=\"Track-1-Sector-9\">Track 1 Sector 9<\/h2>\n<p>The reason why track 1 sector 9 is broken on many disks is simple: The preceding sector on disk is track 1 sector 8, which happens to be the first free block of the GEOS boot disk according to the GEOS filesystem logic. Whenever GEOS searches for a free block for writing on the boot disk, it would pick track 1 sector 8. And this happens a lot: Whenever the user starts a desk accessory, like the alarm clock or preferences, the part of memory required by the desk accessory gets written to disk into a &ldquo;Swap File&rdquo; and later loaded back in. So every start of a desk accessory while the boot disk is in the drive will write track 1 sector 8. And sometimes, writing a sector will break the next one.<\/p>\n<p>Track 1 sector 9 is the info sector (the sector that contains the icon and some other metadata) of the joystick driver, which is on the first page of the disk and will be shown by DeskTop after booting \u2013 unless the user switched to e.g. using mouse for input, in which case a different input driver would be shown on the first page.<\/p>\n<h2 id=\"Speed-Zone\">Speed Zone<\/h2>\n<p>So we have established that sometimes sectors are written too slowly, so they write over the next sector header, making the next sector unreadable. A problem that is not too uncommon with 1541-style disk drives is that the motor speed might be off: Because of the sector gaps, the on-disk format can tolerate a motor that is about 2.5% too slow, and we&rsquo;re seeing a case of 7-8% here. But let&rsquo;s first gather more data before jumping to conclusions.<\/p>\n<p>This graph shows that the overwritten headers only ever happen on tracks 1 to 17:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"docs\/geosdiskerrors\/6.png\" height=\"217\" width=\"600\" alt=\"\" \/><\/p>\n<p>Tracks 1 to 17 have one thing in common: They are speed zone 3. A 1541 disk has three speed zones to account for the different lengths of the tracks:<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align:center;\"> Track   <\/th>\n<th style=\"text-align:center;\"> # Sectors <\/th>\n<th style=\"text-align:center;\"> Speed Zone <\/th>\n<th style=\"text-align:center;\"> \u00b5s\/Byte <\/th>\n<th style=\"text-align:center;\"> Raw Kbit\/Track <\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align:center;\">  1 &#8211; 17 <\/td>\n<td style=\"text-align:center;\"> 21        <\/td>\n<td style=\"text-align:center;\"> 3          <\/td>\n<td style=\"text-align:center;\"> 26      <\/td>\n<td style=\"text-align:center;\"> 60.0           <\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:center;\"> 18 &#8211; 24 <\/td>\n<td style=\"text-align:center;\"> 19        <\/td>\n<td style=\"text-align:center;\"> 2          <\/td>\n<td style=\"text-align:center;\"> 28      <\/td>\n<td style=\"text-align:center;\"> 55.8           <\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:center;\"> 25 &#8211; 30 <\/td>\n<td style=\"text-align:center;\"> 18        <\/td>\n<td style=\"text-align:center;\"> 1          <\/td>\n<td style=\"text-align:center;\"> 30      <\/td>\n<td style=\"text-align:center;\"> 52.1           <\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:center;\"> 31 &#8211; 35 <\/td>\n<td style=\"text-align:center;\"> 17        <\/td>\n<td style=\"text-align:center;\"> 0          <\/td>\n<td style=\"text-align:center;\"> 32      <\/td>\n<td style=\"text-align:center;\"> 48.8           <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In speed zone 3, one byte will be written every 26 microseconds. If we write a sector with the speed zone incorrectly set to 2 on a track that should be speed zone 3, one byte will be written every 28 microseconds instead of every 26, which is 7.7% slower. The 330 bytes written with the speed zone 2 setting will cover a length of 355 bytes of the speed zone 3 equivalent \u2013 so it will overwrite about 25 bytes at the end. This will overshoot the 8 byte gap and completely destroy the next header.<\/p>\n<p>The fact that the 28\/26 ratio matches the measured data perfectly and that the errors only ever happen in speed zone 3 are very strong indications that sometimes, sectors in speed zone 3 get incorrectly written with speed 2.<\/p>\n<h2 id=\"GEOS-Specific\">GEOS-Specific<\/h2>\n<p>First, it is important to find out whether this has anything to do with GEOS. Maybe it happens on all 1541 disks, but since no other tasks on a C64 are as disk-bound as running GEOS, the error might not show itself much anywhere else. This is a distribution of missing sector headers errors across about 300 random disks from my collection:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"docs\/geosdiskerrors\/7.png\" height=\"216\" width=\"600\" alt=\"\" \/><\/p>\n<p>The error is spread evenly, except for the lowest tracks, which are written with a <a href=\"https:\/\/www.pagetable.com\/?p=1107\">lower density<\/a>, and track 18, which holds the file directory and therefore usually mostly empty sectors.<\/p>\n<p>So it is not an inherent property of the 1541 hardware or its firmware. It is GEOS-specific.<\/p>\n<p>In order to find out whether it is specific to GEOS boot disks, I would need a <em>large<\/em> number of GEOS work disks. After all, the boot disks in my collection have been hand-picked to have many errors. I don&rsquo;t have access to such a collection, but I don&rsquo;t see why boot disks would be any special, so I am assuming this happens to all disks used with GEOS.<\/p>\n<h2 id=\"The-GEOS-1541-Driver\">The GEOS 1541 Driver<\/h2>\n<p>This makes the GEOS 1541 driver the suspect. The driver takes control of the drive and its firmware and uses its own sector read\/write and bus communication code. It does reuse some ROM code though for tasks like GCR encoding\/decoding.<\/p>\n<p>This is the reverse-engineered GEOS driver code:<\/p>\n<p><a href=\"https:\/\/github.com\/mist64\/geos\/blob\/master\/drv\/drv1541.s\">https:\/\/github.com\/mist64\/geos\/blob\/master\/drv\/drv1541.s<\/a><\/p>\n<p>The speed setting is stored in bits 5 and 6 of VIA #2 port B (register at <code>$1C00<\/code>). The regular speed setup code is this:<\/p>\n<pre><code>Drv_NewDisk_6:\n        jsr $f24b\n        sta $43\nDrv_NewDisk_7:\n        lda $1c00\n        and #$9f\n        ora DTrackTab,x\nDrv_NewDisk_8:\n        sta $1c00\n        rts\n\nDTrackTab:\n    .byte $00, $20, $40, $60\n<\/code><\/pre>\n<p>The ROM code at <code>$F24B<\/code> looks up the number of sectors for the track number in <code>A<\/code>, and as a side effect, returns the speed zone (0-3) in <code>X<\/code>. The code there is identical on all versions of the 1541, the 1571 and all common <a href=\"https:\/\/www.pagetable.com\/?p=1048\">clones<\/a>.<\/p>\n<p>There is one other place that writes to <code>$1C00<\/code>, the code to move the head (direction in <code>X<\/code>, number of steps in <code>A<\/code>):<\/p>\n<pre><code>D_DUNK6:\n        stx $4a\n        asl\n        tay\n        lda $1c00\n        and #$fe\n        sta $70\n        lda #$1e\n        sta $71\nD_DUNK6_1:\n        lda $70\n        add $4a\n        eor $70\n        and #%00000011\n        eor $70\n        sta $70\n        sta $1c00\n<\/code><\/pre>\n<p>Bits 0 and 1 of <code>$1C00<\/code> control movement of the head. The remaining bits are saved in <code>$70<\/code>. I don&rsquo;t see how this code would be able to change bits 5 and 6.<\/p>\n<h2 id=\"Conclusion\">Conclusion<\/h2>\n<p>This is what we know:<\/p>\n<ul>\n<li>Most GEOS boot disks fail because a sector of speed zone 3 (tracks 1 to 17) has been written with an incorrect speed setting of 2, overwriting the following sector header and thus making that sector inaccessible.<\/li>\n<li>This pattern cannot be found on disks used outside of GEOS. It could not be shown that it can be found on users&#8217; <em>data<\/em> disks used inside GEOS, but I don&rsquo;t see where boot and data disks would be different in this regard.<\/li>\n<li>No bug in the GEOS 1541 disk driver could be found.<\/li>\n<\/ul>\n<p>This is an unresolved mystery. I would be very grateful for:<\/p>\n<ul>\n<li>collections of data\/work disks (<code>.G64<\/code> format or physical disks) to confirm this is not specific to boot disks<\/li>\n<li>a way to reproduce this<\/li>\n<li>the solution why this is happening!<\/li>\n<\/ul>\n<div class=\"footnotes\">\n<hr\/>\n<ol>\n<li id=\"fn:1\">\n<p>This is kind of non-standard: Commodore DOS uses 5 byte SYNCs and 9 byte header and 8 byte sector gaps. It is compatible though.<a href=\"#fnref:1\" rev=\"footnote\">&#8617;<\/a><\/p>\n<\/li>\n<li id=\"fn:2\">\n<p>after 4-to-5 GCR encoding, which ensures no more than two consecutive 0-bits and no more than nine consecutive 1-bits.<a href=\"#fnref:2\" rev=\"footnote\">&#8617;<\/a><\/p>\n<\/li>\n<\/ol>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>I happened to come across 50 original German GEOS 2.0 disks that were broken and sent in for replacement. In the first part, I covered the disks that were broken probably due to user error. Now let&rsquo;s look at the read errors on the remaining disks. As it turns out, there might be a bug &#8230; <a title=\"A GEOS Speed Zone Bug? (Why Do C64 GEOS Boot Disks Break, Part 2)\" class=\"read-more\" href=\"https:\/\/www.pagetable.com\/?p=1128\" aria-label=\"Read more about A GEOS Speed Zone Bug? (Why Do C64 GEOS Boot Disks Break, Part 2)\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,41,8,13,15],"tags":[],"class_list":["post-1128","post","type-post","status-publish","format-standard","hentry","category-archeology","category-c64","category-commodore","category-floppy-disks","category-geos"],"_links":{"self":[{"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/posts\/1128","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1128"}],"version-history":[{"count":0,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/posts\/1128\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.pagetable.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1128"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1128"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1128"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}