<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/default.xsl"?>
<fr:tree xmlns:fr="http://www.forester-notes.org" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:xml="http://www.w3.org/XML/1998/namespace" root="false" base-url="/">
  <fr:frontmatter>
    <fr:authors>
      <fr:author>
        <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
      </fr:author>
    </fr:authors>
    <fr:date>
      <fr:year>2026</fr:year>
      <fr:month>3</fr:month>
      <fr:day>19</fr:day>
    </fr:date>
    <fr:uri>http://localhost/diamond7/</fr:uri>
    <fr:display-uri>diamond7</fr:display-uri>
    <fr:route>/diamond7/</fr:route>
    <fr:title text="Reverse Engineering Floppy Disk Filesystems">Reverse Engineering Floppy Disk Filesystems</fr:title>
  </fr:frontmatter>
  <fr:mainmatter>
    <html:p>
      <html:em>see the repository <fr:link href="https://github.com/STaylorPi/Diamond7Extractor" type="external">here</fr:link></html:em>
    </html:p>
    <html:p>As part of the CST Part IB group project over the last few months, our group was allocated clients from the Cambridge University Library and Churchill College archives.</html:p>
    <html:p>We were presented with two collections of floppy disk images, both from old word-processing systems, with the task of recovering all the files from them.</html:p>
    <html:p>I was responsible for a set of disks from the Diamond 7 system, made in the early-80s by Data Recall Ltd <fr:link href="https://en.wikipedia.org/wiki/Data_Recall_Diamond" type="external">wiki</fr:link>. There is no documentation online for this system. These disks were originally from the office of Neil Kinnock, where the word-processing system was in use.</html:p>
    <html:p>After initial inspection of the disks, no file-table data structure was located, neither were the disks in the CP/M 2.2 format, which was mentioned on the disk.</html:p>
    <html:p>Interestingly, most of the disks in the collection were designed to be bootable, the general structure was as follows:</html:p>
    <html:p>Bootloader, with a sled of <html:code>RST 08h</html:code> instructions (Z80 assembly):</html:p>
    <html:pre><![CDATA[00000000: cd4f ff01 0200 21c0 fdcd ebfe 2ac2 fde5  .O....!.....*...
00000010: 3ac4 fdb7 2833 fe18 302f 5f2a c0fd 0ccd  :...(3..0/_*....
00000020: ebfe 241d 20f8 3e0c d38a c916 0cc5 e5cd  ..$. .>.........
00000030: 0aff e1c1 c815 2811 7a1f d44f ffc5 7ae6  ......(.z..O..z.
00000040: 0306 14cc 40ff c118 e4c7 223a ffcd 40ff  ....@.....":..@.
00000050: c079 d38e 2132 ff46 230e 90ed b33e 88cd  .y..!2.F#....>..
00000060: 51ff 473e bfd3 90db 90e6 21fe 01c0 78e6  Q.G>......!...x.
00000070: 9cc9 0d6d 8fff 002c 108d 0000 8acf 8b87  ...m...,........
00000080: db8d b8c8 78d3 8f3e 1ccd 51ff e698 c93e  ....x..>..Q....>
00000090: 00d3 8c3e 0b3d 20fd db8c b7f8 1f38 f917  ...>.= ......8..
000000a0: c942 2331 2e4b 3a23 34cf cfcf cfcf cfcf  .B#1.K:#4.......
000000b0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000000c0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000000d0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000000e0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000000f0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000100: 004a 004a 0500 0000 0000 0000 0000 0000  .J.J............

... NULLS ...

00000200: 3148 4fcd 074a e93e 0032 004f cdd5 4dcd  1HO..J.>.2.O..M.
00000210: d94c 3e01 3200 4fcd d94a 3e02 3200 4f3e  .L>.2.O..J>.2.O>
.
.
.
00000580: 1428 858f 8acf 05cf 8b87 cd26 4dd0 7932  .(.........&M.y2
00000590: 024f fe01 3f30 02fe 1b3e 03d4 7e4a 79d3  .O..?0...>..~Jy.
000005a0: 8edb 88e6 feb2 d388 5623 7ed3 9015 20f9  ........V#~... .
000005b0: 78e6 0187 83cd fb4c 3ebf d390 db90 e621  x......L>......!
000005c0: ee01 3e0b c0db 8ce6 943e 0cc0 db88 e602  ..>......>......
000005d0: fe02 3e0d c93e 18d3 853e 18d3 853e 18d3  ..>..>...>...>..
000005e0: 873e 18d3 873e 18d3 973e 18d3 973e 18d3  .>...>...>...>..
000005f0: 953e 18d3 95c9 db88 e6ef d388 c9db 88e6  .>..............
00000600: bfd3 8818 f8cf cfcf cfcf cfcf cfcf cfcf  ................
00000610: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000620: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000630: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000640: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000650: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000660: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000670: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000680: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000690: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000006a0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000006b0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000006c0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000006d0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000006e0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000006f0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................]]></html:pre>
    <html:p>The most interesting part of the disk follows: system version information followed by some semi-structured binary data:</html:p>
    <html:pre><![CDATA[00001920: 4469 616d 6f6e 6420 496e 666f 5465 7874  Diamond InfoText
00001930: 2037 2e31 2e39 2028 6529 ffff 4d41 4eff   7.1.9 (e)..MAN.
NULL
00001a00: 6564 6974 206f 6620 0000 3020 0000 0000  edit of ..0 ....
00001a10: 6564 6974 206f 6620 0000 3020 0000 0000  edit of ..0 ....
00001a20: 6564 6974 206f 6620 0000 3220 0000 0000  edit of ..2 ....
00001a30: 6564 6974 206f 6620 0034 3920 0000 0000  edit of .49 ....
00001a40: 6564 6974 206f 6620 0000 3020 0000 0000  edit of ..0 ....
00001a50: 6564 6974 206f 6620 0000 3120 0000 0000  edit of ..1 ....
00001a60: 6564 6974 206f 6620 0035 3220 0000 0000  edit of .52 ....
00001a70: 6564 6974 206f 6620 0000 3020 0000 0000  edit of ..0 ....
00001a80: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00001a90: 2020 2d2d 2020 2020 2020 2020 0000 0000    --        ....
NULL
00001b00: 397d 3a3d 5d3e 4245 3c4e 4849 5558 594c  9}:=]>BE<NHIUXYL
00001b10: 4d53 5c57 5b5f 6162 6364 6566 6769 714b  MS\W[_abcdefgiqK
00001b20: 6a6b 6d73 6e3b 6f70 7875 7779 7f7e 8d8a  jkmsn;opxuwy.~..
00001b30: 8081 838b 7a85 8200 0000 0000 0000 0000  ....z...........
00001b40: 0000 0000 0000 0000 0000 0000 007c 0000  .............|..
NULL
00001bf0: 020b 2e31 3334 3537 3800 0000 0000 0000  ...134578.......
NULL
00001ce0: 6564 6974 206f 6620 3020 2020 0000 0000  edit of 0   ....
00001cf0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00001d00: 0001 0304 0506 0708 090a 0a0c 0d0e 0f10  ................
00001d10: 1112 1314 1516 1718 191a 1b1c 1d1e 1f20  ............... 
00001d20: 2122 2324 2526 2728 292a 2b2c 2d2d 2f30  !"#$%&'()*+,--/0
00001d30: 3032 3233 3436 3637 3839 3a3b 403d 3f3f  0223466789:;@=??
00001d40: 4141 4344 4446 4747 4a49 5168 4c54 4f50  AACDDFGGJIQhLTOP
00001d50: 5052 5256 5455 565a 5859 5a60 5e5d 5e5f  PRRVTUVZXYZ`^]^_
00001d60: 6061 6263 6465 6667 6869 6c6b 6c6d 746f  `abcdefghilklmto
00001d70: 7072 7273 7675 767b 7879 7a7b 7c7d 7e7f  prrsvuv{xyz{|}~.
NULL
00001f00: 397d 3a3d 5d3e 4245 3c4e 4849 5558 594c  9}:=]>BE<NHIUXYL
00001f10: 4d53 5c57 5b5f 6162 6364 6566 6769 714b  MS\W[_abcdefgiqK
00001f20: 6a6b 6d73 6e3b 6f70 7875 7779 7f7e 8d8a  jkmsn;opxuwy.~..
00001f30: 8081 838b 7a85 8200 0000 0000 0000 0000  ....z...........
NULL
00001ff0: 020b 2e31 3334 3537 3800 0000 0000 0000  ...134578.......
NULL
00002100: 0001 0304 0506 0708 090a 0a0c 0d0e 0f10  ................
00002110: 1112 1314 1516 1718 191a 1b1c 1d1e 1f20  ............... 
00002120: 2122 2324 2526 2728 292a 2b2c 2d2d 2f30  !"#$%&'()*+,--/0
00002130: 3032 3233 3436 3637 3839 3a3b 403d 3f3f  0223466789:;@=??
00002140: 4141 4344 4446 4747 4a49 5168 4c54 4f50  AACDDFGGJIQhLTOP
00002150: 5052 5256 5455 565a 5859 5a60 5e5d 5e5f  PRRVTUVZXYZ`^]^_
00002160: 6061 6263 6465 6667 6869 6c6b 6c6d 746f  `abcdefghilklmto
00002170: 7072 7273 7675 767b 7879 7a7b 7c7d 7e7f  prrsvuv{xyz{|}~.
.
.
.]]></html:pre>
    <html:p>Using <html:code>diff</html:code> to perform a comparison between disks of the same software version allowed me to distinguish between software data and disk-metadata (since only the disk metadata varies between these disks). The data above was determined to be disk metadata.</html:p>
    <html:p>The remaining two sections of the disk were a larger section of software (presumably the word processing software itself), followed by the actual text content of Kinnock's files, split into 256-byte sectors.</html:p>
    <html:p>To summarise, the general layout is as follows:</html:p>
    <html:ol><html:li>Bootloader: from <html:code>0x000</html:code> to <html:code>0x700</html:code></html:li>
<html:li>Metadata: from <html:code>0x1900</html:code> to <html:code>0x3400</html:code></html:li>
<html:li>Software: from <html:code>0x3400</html:code> to ~<html:code>0x3a400</html:code></html:li>
<html:li>Text data: rest of disk</html:li></html:ol>
    <html:p>The next phase was to attempt to order the text sectors on the disk into full files using the metadata. Disassembling the software to help with this was an initial idea. However, this was not viable since the Z80 instruction set covers the whole 8-bit space, and the software spans a space larger than 16-bits can address, making the task of aliging jumps non-trivial.</html:p>
    <html:p>Therefore, the only recourse was to try to reconcile the contents of the metadata with the ordering of the text sectors. The lack of a file table on any of these disks meant I had to be creative. In the end, best-effort recovery was all that I could manage, but here is what I tried:</html:p>
    <html:ul><html:li>Comparing binary information with sequences of sectors that seemed to be
		continuous file data on the disk</html:li>
	<html:li>The Diamond 7 was an ancestor of the Amstrad system, so I tried using
		cluster numberings used there to map from the binary data to the sectors</html:li>
	<html:li>Looking for structure corresponding to a FAT12 layout</html:li>
	<html:li>Comparing the position of the filenames within the binary section to locations
		on the disk itself</html:li></html:ul>
    <html:p>These all proved fruitless. However, the file headers and footers on the disk had some discernible structure, and furthermore many of the files were stored roughly in sequence on the disk. Using the fact that multiple versions of the files were present in most cases allowed the files to be conservatively pieced together by scanning down the sectors from the top of a file, stopping either when the end is reached or when a sector unification would contradict another sector elsewhere:</html:p>
    <html:p>Try to unify: <html:em>...Mr Churchill’s government tak</html:em> with <html:em>Yours sincerely, ...</html:em>
Fails in the presence of the sector containing: <html:em>...’s government takes no
responsibility for the downfall of...</html:em></html:p>
    <html:p>This led to good results, with only files that made sense being output. Since there is the occasional missed text sector (either it is control information, it is a sector from another version of a file already used, or it has been genuinely missed), the program also presents these in a file, along with a file of file names that have been recovered.</html:p>
  </fr:mainmatter>
  <fr:backmatter>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="References">References</fr:title>
      </fr:frontmatter>
      <fr:mainmatter />
    </fr:tree>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="Context">Context</fr:title>
      </fr:frontmatter>
      <fr:mainmatter>
        <fr:tree show-metadata="true" expanded="false" toc="false" numbered="false">
          <fr:frontmatter>
            <fr:authors>
              <fr:contributor>
                <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
              </fr:contributor>
            </fr:authors>
            <fr:date>
              <fr:year>2025</fr:year>
              <fr:month>11</fr:month>
              <fr:day>2</fr:day>
            </fr:date>
            <fr:uri>http://localhost/0002/</fr:uri>
            <fr:display-uri>0002</fr:display-uri>
            <fr:route>/0002/</fr:route>
            <fr:title text="Technical Notes">Technical Notes</fr:title>
          </fr:frontmatter>
          <fr:mainmatter>
            <html:p>These notes include both exposition of work as well as documentation/scratch work.</html:p>
            <fr:tree show-metadata="false" expanded="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2026</fr:year>
                  <fr:month>3</fr:month>
                  <fr:day>19</fr:day>
                </fr:date>
                <fr:uri>http://localhost/diamond7/</fr:uri>
                <fr:display-uri>diamond7</fr:display-uri>
                <fr:route>/diamond7/</fr:route>
                <fr:title text="Reverse Engineering Floppy Disk Filesystems">Reverse Engineering Floppy Disk Filesystems</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>
                  <html:em>see the repository <fr:link href="https://github.com/STaylorPi/Diamond7Extractor" type="external">here</fr:link></html:em>
                </html:p>
                <html:p>As part of the CST Part IB group project over the last few months, our group was allocated clients from the Cambridge University Library and Churchill College archives.</html:p>
                <html:p>We were presented with two collections of floppy disk images, both from old word-processing systems, with the task of recovering all the files from them.</html:p>
                <html:p>I was responsible for a set of disks from the Diamond 7 system, made in the early-80s by Data Recall Ltd <fr:link href="https://en.wikipedia.org/wiki/Data_Recall_Diamond" type="external">wiki</fr:link>. There is no documentation online for this system. These disks were originally from the office of Neil Kinnock, where the word-processing system was in use.</html:p>
                <html:p>After initial inspection of the disks, no file-table data structure was located, neither were the disks in the CP/M 2.2 format, which was mentioned on the disk.</html:p>
                <html:p>Interestingly, most of the disks in the collection were designed to be bootable, the general structure was as follows:</html:p>
                <html:p>Bootloader, with a sled of <html:code>RST 08h</html:code> instructions (Z80 assembly):</html:p>
                <html:pre><![CDATA[00000000: cd4f ff01 0200 21c0 fdcd ebfe 2ac2 fde5  .O....!.....*...
00000010: 3ac4 fdb7 2833 fe18 302f 5f2a c0fd 0ccd  :...(3..0/_*....
00000020: ebfe 241d 20f8 3e0c d38a c916 0cc5 e5cd  ..$. .>.........
00000030: 0aff e1c1 c815 2811 7a1f d44f ffc5 7ae6  ......(.z..O..z.
00000040: 0306 14cc 40ff c118 e4c7 223a ffcd 40ff  ....@.....":..@.
00000050: c079 d38e 2132 ff46 230e 90ed b33e 88cd  .y..!2.F#....>..
00000060: 51ff 473e bfd3 90db 90e6 21fe 01c0 78e6  Q.G>......!...x.
00000070: 9cc9 0d6d 8fff 002c 108d 0000 8acf 8b87  ...m...,........
00000080: db8d b8c8 78d3 8f3e 1ccd 51ff e698 c93e  ....x..>..Q....>
00000090: 00d3 8c3e 0b3d 20fd db8c b7f8 1f38 f917  ...>.= ......8..
000000a0: c942 2331 2e4b 3a23 34cf cfcf cfcf cfcf  .B#1.K:#4.......
000000b0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000000c0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000000d0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000000e0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000000f0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000100: 004a 004a 0500 0000 0000 0000 0000 0000  .J.J............

... NULLS ...

00000200: 3148 4fcd 074a e93e 0032 004f cdd5 4dcd  1HO..J.>.2.O..M.
00000210: d94c 3e01 3200 4fcd d94a 3e02 3200 4f3e  .L>.2.O..J>.2.O>
.
.
.
00000580: 1428 858f 8acf 05cf 8b87 cd26 4dd0 7932  .(.........&M.y2
00000590: 024f fe01 3f30 02fe 1b3e 03d4 7e4a 79d3  .O..?0...>..~Jy.
000005a0: 8edb 88e6 feb2 d388 5623 7ed3 9015 20f9  ........V#~... .
000005b0: 78e6 0187 83cd fb4c 3ebf d390 db90 e621  x......L>......!
000005c0: ee01 3e0b c0db 8ce6 943e 0cc0 db88 e602  ..>......>......
000005d0: fe02 3e0d c93e 18d3 853e 18d3 853e 18d3  ..>..>...>...>..
000005e0: 873e 18d3 873e 18d3 973e 18d3 973e 18d3  .>...>...>...>..
000005f0: 953e 18d3 95c9 db88 e6ef d388 c9db 88e6  .>..............
00000600: bfd3 8818 f8cf cfcf cfcf cfcf cfcf cfcf  ................
00000610: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000620: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000630: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000640: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000650: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000660: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000670: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000680: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
00000690: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000006a0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000006b0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000006c0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000006d0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000006e0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................
000006f0: cfcf cfcf cfcf cfcf cfcf cfcf cfcf cfcf  ................]]></html:pre>
                <html:p>The most interesting part of the disk follows: system version information followed by some semi-structured binary data:</html:p>
                <html:pre><![CDATA[00001920: 4469 616d 6f6e 6420 496e 666f 5465 7874  Diamond InfoText
00001930: 2037 2e31 2e39 2028 6529 ffff 4d41 4eff   7.1.9 (e)..MAN.
NULL
00001a00: 6564 6974 206f 6620 0000 3020 0000 0000  edit of ..0 ....
00001a10: 6564 6974 206f 6620 0000 3020 0000 0000  edit of ..0 ....
00001a20: 6564 6974 206f 6620 0000 3220 0000 0000  edit of ..2 ....
00001a30: 6564 6974 206f 6620 0034 3920 0000 0000  edit of .49 ....
00001a40: 6564 6974 206f 6620 0000 3020 0000 0000  edit of ..0 ....
00001a50: 6564 6974 206f 6620 0000 3120 0000 0000  edit of ..1 ....
00001a60: 6564 6974 206f 6620 0035 3220 0000 0000  edit of .52 ....
00001a70: 6564 6974 206f 6620 0000 3020 0000 0000  edit of ..0 ....
00001a80: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00001a90: 2020 2d2d 2020 2020 2020 2020 0000 0000    --        ....
NULL
00001b00: 397d 3a3d 5d3e 4245 3c4e 4849 5558 594c  9}:=]>BE<NHIUXYL
00001b10: 4d53 5c57 5b5f 6162 6364 6566 6769 714b  MS\W[_abcdefgiqK
00001b20: 6a6b 6d73 6e3b 6f70 7875 7779 7f7e 8d8a  jkmsn;opxuwy.~..
00001b30: 8081 838b 7a85 8200 0000 0000 0000 0000  ....z...........
00001b40: 0000 0000 0000 0000 0000 0000 007c 0000  .............|..
NULL
00001bf0: 020b 2e31 3334 3537 3800 0000 0000 0000  ...134578.......
NULL
00001ce0: 6564 6974 206f 6620 3020 2020 0000 0000  edit of 0   ....
00001cf0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00001d00: 0001 0304 0506 0708 090a 0a0c 0d0e 0f10  ................
00001d10: 1112 1314 1516 1718 191a 1b1c 1d1e 1f20  ............... 
00001d20: 2122 2324 2526 2728 292a 2b2c 2d2d 2f30  !"#$%&'()*+,--/0
00001d30: 3032 3233 3436 3637 3839 3a3b 403d 3f3f  0223466789:;@=??
00001d40: 4141 4344 4446 4747 4a49 5168 4c54 4f50  AACDDFGGJIQhLTOP
00001d50: 5052 5256 5455 565a 5859 5a60 5e5d 5e5f  PRRVTUVZXYZ`^]^_
00001d60: 6061 6263 6465 6667 6869 6c6b 6c6d 746f  `abcdefghilklmto
00001d70: 7072 7273 7675 767b 7879 7a7b 7c7d 7e7f  prrsvuv{xyz{|}~.
NULL
00001f00: 397d 3a3d 5d3e 4245 3c4e 4849 5558 594c  9}:=]>BE<NHIUXYL
00001f10: 4d53 5c57 5b5f 6162 6364 6566 6769 714b  MS\W[_abcdefgiqK
00001f20: 6a6b 6d73 6e3b 6f70 7875 7779 7f7e 8d8a  jkmsn;opxuwy.~..
00001f30: 8081 838b 7a85 8200 0000 0000 0000 0000  ....z...........
NULL
00001ff0: 020b 2e31 3334 3537 3800 0000 0000 0000  ...134578.......
NULL
00002100: 0001 0304 0506 0708 090a 0a0c 0d0e 0f10  ................
00002110: 1112 1314 1516 1718 191a 1b1c 1d1e 1f20  ............... 
00002120: 2122 2324 2526 2728 292a 2b2c 2d2d 2f30  !"#$%&'()*+,--/0
00002130: 3032 3233 3436 3637 3839 3a3b 403d 3f3f  0223466789:;@=??
00002140: 4141 4344 4446 4747 4a49 5168 4c54 4f50  AACDDFGGJIQhLTOP
00002150: 5052 5256 5455 565a 5859 5a60 5e5d 5e5f  PRRVTUVZXYZ`^]^_
00002160: 6061 6263 6465 6667 6869 6c6b 6c6d 746f  `abcdefghilklmto
00002170: 7072 7273 7675 767b 7879 7a7b 7c7d 7e7f  prrsvuv{xyz{|}~.
.
.
.]]></html:pre>
                <html:p>Using <html:code>diff</html:code> to perform a comparison between disks of the same software version allowed me to distinguish between software data and disk-metadata (since only the disk metadata varies between these disks). The data above was determined to be disk metadata.</html:p>
                <html:p>The remaining two sections of the disk were a larger section of software (presumably the word processing software itself), followed by the actual text content of Kinnock's files, split into 256-byte sectors.</html:p>
                <html:p>To summarise, the general layout is as follows:</html:p>
                <html:ol><html:li>Bootloader: from <html:code>0x000</html:code> to <html:code>0x700</html:code></html:li>
<html:li>Metadata: from <html:code>0x1900</html:code> to <html:code>0x3400</html:code></html:li>
<html:li>Software: from <html:code>0x3400</html:code> to ~<html:code>0x3a400</html:code></html:li>
<html:li>Text data: rest of disk</html:li></html:ol>
                <html:p>The next phase was to attempt to order the text sectors on the disk into full files using the metadata. Disassembling the software to help with this was an initial idea. However, this was not viable since the Z80 instruction set covers the whole 8-bit space, and the software spans a space larger than 16-bits can address, making the task of aliging jumps non-trivial.</html:p>
                <html:p>Therefore, the only recourse was to try to reconcile the contents of the metadata with the ordering of the text sectors. The lack of a file table on any of these disks meant I had to be creative. In the end, best-effort recovery was all that I could manage, but here is what I tried:</html:p>
                <html:ul><html:li>Comparing binary information with sequences of sectors that seemed to be
		continuous file data on the disk</html:li>
	<html:li>The Diamond 7 was an ancestor of the Amstrad system, so I tried using
		cluster numberings used there to map from the binary data to the sectors</html:li>
	<html:li>Looking for structure corresponding to a FAT12 layout</html:li>
	<html:li>Comparing the position of the filenames within the binary section to locations
		on the disk itself</html:li></html:ul>
                <html:p>These all proved fruitless. However, the file headers and footers on the disk had some discernible structure, and furthermore many of the files were stored roughly in sequence on the disk. Using the fact that multiple versions of the files were present in most cases allowed the files to be conservatively pieced together by scanning down the sectors from the top of a file, stopping either when the end is reached or when a sector unification would contradict another sector elsewhere:</html:p>
                <html:p>Try to unify: <html:em>...Mr Churchill’s government tak</html:em> with <html:em>Yours sincerely, ...</html:em>
Fails in the presence of the sector containing: <html:em>...’s government takes no
responsibility for the downfall of...</html:em></html:p>
                <html:p>This led to good results, with only files that made sense being output. Since there is the occasional missed text sector (either it is control information, it is a sector from another version of a file already used, or it has been genuinely missed), the program also presents these in a file, along with a file of file names that have been recovered.</html:p>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="false" expanded="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2025</fr:year>
                  <fr:month>11</fr:month>
                  <fr:day>2</fr:day>
                </fr:date>
                <fr:uri>http://localhost/0001/</fr:uri>
                <fr:display-uri>0001</fr:display-uri>
                <fr:route>/0001/</fr:route>
                <fr:title text="On Writing an Operating System">On Writing an Operating System</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>I set out in summer 2025 to write a toy operating system on x86. The repository is <fr:link href="https://www.github.com/STaylorPi/VGA-Driver" type="external">here</fr:link>. This has been a fun project so far, with the development cycle consisting mostly of reading the manual for some hardware feature, then implementing and debugging interface code. It's taught me a great deal about how multi-faceted old platforms like x86 become, especially after having to read <fr:link href="https://pdos.csail.mit.edu/6.828/2005/readings/hardware/8259A.pdf" type="external">datasheets from the 80s</fr:link>.</html:p>
                <html:p>The <fr:link href="https://wiki.osdev.org/" type="external">OSDev Wiki</fr:link> page has been indispensible, and my main reference for the project</html:p>
                <html:p>I have written some documentation for the project so far: here it is file-by-file</html:p>
                <fr:tree show-metadata="false" expanded="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2025</fr:year>
                      <fr:month>11</fr:month>
                      <fr:day>2</fr:day>
                    </fr:date>
                    <fr:uri>http://localhost/0006/</fr:uri>
                    <fr:display-uri>0006</fr:display-uri>
                    <fr:route>/0006/</fr:route>
                    <fr:title text="gdt">gdt</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>Documentation for files <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/gdt.h" type="external">gdt.h</fr:link>, <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/gdt.asm" type="external">gdt.asm</fr:link> and <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/gdt.c" type="external">gdt.c</fr:link></html:p>
                    <html:code>void place_gdt_entry(const struct GDT_entry* data, void* addr)</html:code>
                    <html:p>This function lays out the data field in the proper way for a Global	Descriptor Table entry at the place <html:code>addr</html:code></html:p>
                    <html:code>void load_basic_gdt(void* addr)</html:code>
                    <html:p>This function lays a bare-bones GDT at <html:code>addr</html:code>, featuring kernel code, kernel data, user code, user data segments and one task state segment</html:p>
                    <html:code>uint16_t get_gdtr_limit()</html:code>
                    <html:p>Extracts the limit member for the GDT from the currently loaded GDT in the CPU</html:p>
                    <html:code>uint32_t get_gdtr_base()</html:code>
                    <html:p>Extracts the address of the start of the currently loaded GDT</html:p>
                    <html:code>void load_gdt(const struct GDT_descriptor* desc)</html:code>
                    <html:p>Points the CPU GDTR to the GDT described by <html:code>desc</html:code></html:p>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" expanded="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2025</fr:year>
                      <fr:month>11</fr:month>
                      <fr:day>2</fr:day>
                    </fr:date>
                    <fr:uri>http://localhost/0007/</fr:uri>
                    <fr:display-uri>0007</fr:display-uri>
                    <fr:route>/0007/</fr:route>
                    <fr:title text="graphics">graphics</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>A rudimentary graphics library designed to write to the VGA buffer. Documentation for files <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/graphics.h" type="external">graphics.h</fr:link>, <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/graphics.c" type="external">graphics.c</fr:link></html:p>
                    <html:code>WinFormat</html:code>
                    <html:p>Struct describing memory layout for the VGA buffer. Note that double buffering is employed, with the VGA memory being at <html:code>ren_buf</html:code> and back-buffer draw memory being at <html:code>draw_buf</html:code></html:p>
                    <html:code>void set_layout(const struct WinFormat* fmt)</html:code>
                    <html:p>Sets the (private) static state of the display configuration <html:code>fmt</html:code></html:p>
                    <html:code>void clear()</html:code>
                    <html:p>Clears the <html:code>draw_buf</html:code></html:p>
                    <html:code>void swap()</html:code>
                    <html:p>Swaps the buffers, leaving <html:code>draw_buf</html:code> in-tact</html:p>
                    <html:code>void set_pixel(uint16_t x, uint16_t, pixel_t colour)</html:code>
                    <html:p>Draws a single pixel at the specified location with VGA colour <html:code>colour</html:code>. Don't use for drawing bigger shapes for performance reasons</html:p>
                    <html:code>void draw_rect(uint16_t xtl, uint16_t ytl, uint16_t w, uint16_t h, pixel_t colour)</html:code>
                    <html:p>Filled rectangle</html:p>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" expanded="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2025</fr:year>
                      <fr:month>11</fr:month>
                      <fr:day>2</fr:day>
                    </fr:date>
                    <fr:uri>http://localhost/0008/</fr:uri>
                    <fr:display-uri>0008</fr:display-uri>
                    <fr:route>/0008/</fr:route>
                    <fr:title text="idt">idt</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>Documentation for files <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/idt.h" type="external">idt.h</fr:link>, <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/idt.asm" type="external">idt.asm</fr:link>, <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/idt.c" type="external">idt.c</fr:link></html:p>
                    <html:code>void null_handler()</html:code>
                    <html:p>This interrupt handler does nothing</html:p>
                    <html:code>void asm_handler()</html:code>
                    <html:p>Currently a test handler wrapping the <html:code>cool_handler</html:code> ISR</html:p>
                    <html:code>void load_idt(void* desc)</html:code>
                    <html:p>Similar to <html:code>load_gdt</html:code>, this function loads our descriptor table at <html:code>desc</html:code> into the CPU's IDTR register</html:p>
                    <html:code>void write_null_idt_entry(void* loc, uint16_t num)</html:code>
                    <html:p>This code loads <html:code>num</html:code> null IDT entries (calling the <html:code>null_handler</html:code>) to address <html:code>loc</html:code></html:p>
                    <html:code>void load_idt_entry(void* loc, const struct IDT_entry* entry)</html:code>
                    <html:p>Lays out the <html:code>entry</html:code> according the proper descriptor layout at <html:code>loc</html:code></html:p>
                    <html:code>void cool_handler()</html:code>
                    <html:p>Do not call directly, since this function is not wrapped properly with <html:code>iret</html:code>. Currently prints the results of keypresses in VGA text mode <html:code>0x03</html:code></html:p>
                    <html:code>void setup_idt()</html:code>
                    <html:p>Loads the current test IDT to location <html:code>0x1100</html:code></html:p>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" expanded="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2025</fr:year>
                      <fr:month>11</fr:month>
                      <fr:day>2</fr:day>
                    </fr:date>
                    <fr:uri>http://localhost/0009/</fr:uri>
                    <fr:display-uri>0009</fr:display-uri>
                    <fr:route>/0009/</fr:route>
                    <fr:title text="kernel">kernel</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>File contains rudimentary VGA mode <html:code>0x03</html:code> terminal output. Documentation for file <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/kernel.c" type="external">kernel.c</fr:link></html:p>
                    <html:code>void terminal_initialise()</html:code>
                    <html:p />
                    <html:code>void terminal_setcolour(uint8_t colour)</html:code>
                    <html:p>Sets colour for next characters, <html:code>colour</html:code> is an offset into the currently loaded colour palette in the VGA</html:p>
                    <html:code>void terminal_putentryat(char c, uint8_t colour, size_t x, size_t y)</html:code>
                    <html:p />
                    <html:code>void terminal_scroll()</html:code>
                    <html:p />
                    <html:code>void terminal_putchar(char c)</html:code>
                    <html:p>Supports newlines, tabs and backspaces</html:p>
                    <html:code>void terminal_write(const char* data, size_t size)</html:code>
                    <html:p />
                    <html:code>void terminal_writestring(const char* data)</html:code>
                    <html:p />
                    <html:code>void println()</html:code>
                    <html:p />
                    <html:code>void print_hex<fr:link href="http://localhost/uint%5B32%7C16%7C8%5D_t%20in/" type="external">32|16|8</fr:link></html:code>
                    <html:p>Fixed-width hex literal printing</html:p>
                    <html:code>void print_vga_registers(const struct vga_regs_t* data)</html:code>
                    <html:p>Text annotated output of current VGA state</html:p>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" expanded="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2025</fr:year>
                      <fr:month>11</fr:month>
                      <fr:day>2</fr:day>
                    </fr:date>
                    <fr:uri>http://localhost/000A/</fr:uri>
                    <fr:display-uri>000A</fr:display-uri>
                    <fr:route>/000A/</fr:route>
                    <fr:title text="keyboard">keyboard</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>Documentation for files <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/keyboard.h" type="external">keyboard.h</fr:link>, <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/keyboard.c" type="external">keyboard.c</fr:link></html:p>
                    <html:code>struct KeyEvent to_ascii(uint8_t scan_code)</html:code>
                    <html:p>Converts the scancode coming off the interrupt into an ascii value, plus key event state</html:p>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" expanded="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2025</fr:year>
                      <fr:month>11</fr:month>
                      <fr:day>3</fr:day>
                    </fr:date>
                    <fr:uri>http://localhost/000B/</fr:uri>
                    <fr:display-uri>000B</fr:display-uri>
                    <fr:route>/000B/</fr:route>
                    <fr:title text="pic_8259">pic_8259</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>Documentation for files <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/pic_8259.h" type="external">pic_8259.h</fr:link>, <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/pic_8259.c" type="external">pic_8259.c</fr:link>. This unit is a Programmable Interrupt Controller of which there are two in modern CPUs. Each can issue 8 distinct hardware interrupts, mapped to some offset in the 256-entry IDT. This is a fascinating little device that has been around as far as I can tell unchanged since the 1980s.</html:p>
                    <html:code>void pic8259_unmask()</html:code>
                    <html:p>This code removes sets all mask bits for both PICs to zero</html:p>
                    <html:code>void pic8259_disable()</html:code>
                    <html:p>Sets all mask bits. Must be used if we wish to use the APIC.</html:p>
                    <html:code>void pic8259_mask(uint16_t mask)</html:code>
                    <html:p>Writes out a new mask. Low byte goes to PIC1, high byte to PIC2.</html:p>
                    <html:code>void pic8259_init(uint8_t off_master, uint8_t off_slave)</html:code>
                    <html:p>Initialises both PICs, mapping them to the given offsets in the IDT</html:p>
                    <html:code>void pic8259_end_of_interrupt(uint8_t irq)</html:code>
                    <html:p>Instructs the PIC that interrupt <html:code>irq</html:code> has been handled</html:p>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" expanded="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2025</fr:year>
                      <fr:month>11</fr:month>
                      <fr:day>3</fr:day>
                    </fr:date>
                    <fr:uri>http://localhost/000C/</fr:uri>
                    <fr:display-uri>000C</fr:display-uri>
                    <fr:route>/000C/</fr:route>
                    <fr:title text="utils">utils</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>Documentation for files <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/utils.h" type="external">utils.h</fr:link>, <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/utils.c" type="external">utils.c</fr:link></html:p>
                    <html:code>void* memset(void* dest, int ch, size_t count)</html:code>
                    <html:p>Standard LibC style memset function</html:p>
                    <html:code>void* memcpy(void* dest, const void* src, size_t n)</html:code>
                    <html:p>Standard LibC style memcpy function</html:p>
                    <html:code>size_t strlen(const char* str)</html:code>
                    <html:p>Standard LibC style strlen function</html:p>
                  </fr:mainmatter>
                </fr:tree>
                <fr:tree show-metadata="false" expanded="false">
                  <fr:frontmatter>
                    <fr:authors>
                      <fr:author>
                        <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
                      </fr:author>
                    </fr:authors>
                    <fr:date>
                      <fr:year>2025</fr:year>
                      <fr:month>11</fr:month>
                      <fr:day>3</fr:day>
                    </fr:date>
                    <fr:uri>http://localhost/000D/</fr:uri>
                    <fr:display-uri>000D</fr:display-uri>
                    <fr:route>/000D/</fr:route>
                    <fr:title text="utils">utils</fr:title>
                  </fr:frontmatter>
                  <fr:mainmatter>
                    <html:p>Documentation for files <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/vga.h" type="external">vga.h</fr:link>, <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/vga.c" type="external">vga.c</fr:link>. See <fr:link href="/0005/" title="basic_io" uri="http://localhost/0005/" display-uri="0005" type="local">basic_io</fr:link> for <fr:link href="https://github.com/STaylorPi/VGA-Driver/blob/master/vga.asm" type="external">vga.asm</fr:link></html:p>
                    <html:code>static inline uint8_t vga_entry_colour(enum vga_colour gf, enum vga_colour bg)</html:code>
                    <html:p>Merges VGA 0x03 mode colour enums into a writable byte</html:p>
                    <html:code>static inline uint16_t vga_entry(unsigned char uc, uint8_t colour)</html:code>
                    <html:p>Merges a character and a colour into an unsigned short writable to the VGA memory buffer</html:p>
                    <html:code>struct vga_regs_t read_vga_registers()</html:code>
                    <html:p>Reads the important registers from the VGA</html:p>
                    <html:code>void write_vga_registers(const struct vga_regs_t* data)</html:code>
                    <html:p>Unchecked write of the new register map <html:code>data</html:code> to the VGA</html:p>
                    <html:code>void vga_disable_display()</html:code>
                    <html:p>Blanks the display</html:p>
                    <html:code>void vga_enable_display()</html:code>
                    <html:p>Unblanks the display</html:p>
                    <html:code>uint8_t vga_read_attrib_reg(uint8_t idx)</html:code>
                    <html:p>The Attribute registers in the VGA have a special reading/writing protocol making use of the same register for both index select and data output. Function reads the attribute register at index <html:code>idx</html:code>.</html:p>
                    <html:code>void vga_write_attrib_reg(uint8_t idx, uint8_t data)</html:code>
                    <html:p>See above. Function writes a byte to the specified attribute register in the VGA</html:p>
                    <html:code>void vga_unlock_crtc()</html:code>
                    <html:p>Allows writes to the CRT controller registers on the VGA</html:p>
                    <html:code>void vga_lock_crtc()</html:code>
                    <html:p>Blocks writes to the CRT controller registers on the VGA</html:p>
                    <html:code>void set_vga_mode_0x13()</html:code>
                    <html:p>Sets the graphical display mode to 0x13, 320x200 pixels</html:p>
                    <html:code>void vga_draw_cool_pattern()</html:code>
                    <html:p>&gt;:D</html:p>
                    <html:code>struct vga_colour_t vga_read_palette(uint8_t entry)</html:code>
                    <html:p>Reads the DAC's palette on the VGA at the given index</html:p>
                    <html:code>void vga_write_palette(uint8_t entry, struct vga_colour_t new_col)</html:code>
                    <html:p>Write the DAC's palette with the given colour. Note that only the lower 6 bits are used in each colour channel.</html:p>
                    <html:code>void vga_write_palette256()</html:code>
                    <html:p>Writes the standard 0x13 256-colour palette to the VGA</html:p>
                  </fr:mainmatter>
                </fr:tree>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="false" expanded="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2025</fr:year>
                  <fr:month>11</fr:month>
                  <fr:day>9</fr:day>
                </fr:date>
                <fr:uri>http://localhost/000E/</fr:uri>
                <fr:display-uri>000E</fr:display-uri>
                <fr:route>/000E/</fr:route>
                <fr:title text="MPC Double Pendulum Controller">MPC Double Pendulum Controller</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>This project was a crude attempt at writing a controller for a reasonably complex system. The repository is <fr:link href="https://github.com/STaylorPi/DoublePendulum" type="external">here</fr:link>. The controller can sucessfully stabilise the system around all three unstable equilibriums.</html:p>
                <html:p>The project also included handwritten code for a rotary encoder and motor driver, with a view to put this into action on real hardware. If I were to do this again, I'd change the control scheme (a less expensive controller like an LQR would work well around the equilibriums, and there are stability results that I could reasonably rely on.).</html:p>
                <html:p>A robust implementation of the system would have to include a statistical model and characterisation of the sensors, including noise. Cost constraints meant that building a high-enough performance rig here was unfortunately not feasible.</html:p>
                <html:figure><html:img width="300px" src="/bafkrmic6d3dtz5io7zik7f6dlgeh4i6cgiwp2wt2o4gp7citwuipo7otiy.jpg" />
	<html:img width="300px" src="/bafkrmiea7litolxk2vrjwplfqwahs6vz6oqjyqlr4rpvo4jt3372wsgi3q.jpg" />
	<html:img width="600px" src="/bafkrmif3vuutkhqiytlni72f2yos4zljghhr7t2r3hprmh277isbmdhjtu.jpeg" />
	<html:figcaption>The gantry assembly.</html:figcaption></html:figure>
                <html:figure><html:video width="300px" type="&quot;video/mp4&quot;" src="/bafkrmidn2kwh22qouom5bi7i6lm4eqqftpnsqczhwvvpbmpdtlcmaghlxi.mp4" />
	<html:figcaption>The gantry in action.</html:figcaption></html:figure>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="false" expanded="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2025</fr:year>
                  <fr:month>11</fr:month>
                  <fr:day>9</fr:day>
                </fr:date>
                <fr:uri>http://localhost/000F/</fr:uri>
                <fr:display-uri>000F</fr:display-uri>
                <fr:route>/000F/</fr:route>
                <fr:title text="The Wave Function Collapse Algorithm">The Wave Function Collapse Algorithm</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>
                  <html:em>This algorithm is a very neat constrained procedural terrain generation routine.</html:em>
                </html:p>
                <html:p>
                  <html:strong>The Problem.</html:strong>
                </html:p>
                <html:p>We are given a grid of cells (cast over any number of dimensions) (<html:em>the Reference <fr:tex display="inline"><![CDATA[R]]></fr:tex></html:em>), each with a number label <fr:tex display="inline"><![CDATA[x]]></fr:tex>.</html:p>
                <html:p>We are tasked with returning a new grid of cells (with arbitrary dimensions), such that the distribution of neighbour numbers for each number is as close to the Reference as possible (Cells are neighbours if they share an edge (2D) or a face (3D)).</html:p>
                <html:p>This is a surprisingly effective way to emulate the small-scale features of terrain over a large map.</html:p>
                <html:p>
                  <html:strong>The Algorithm.</html:strong>
                </html:p>
                <html:p>In broad terms:</html:p>
                <html:ol><html:li>For each unique number label <fr:tex display="inline"><![CDATA[x]]></fr:tex> in the Reference, compute <fr:tex display="inline"><![CDATA[\mathbb {P}_{\mathbf {d}, x}(N=x_n)]]></fr:tex>, the probability of the neighbour <fr:tex display="inline"><![CDATA[N]]></fr:tex> on the edge <fr:tex display="inline"><![CDATA[\mathbf {d}]]></fr:tex> taking on the value <fr:tex display="inline"><![CDATA[x_n]]></fr:tex>. For example, in 2D, <fr:tex display="inline"><![CDATA[\mathbf {d}]]></fr:tex> can be (0, 1), (0, -1), (1, 0) or (-1, 0).</html:li>

<html:li>Initialise each cell in the output grid into a <html:em>superposition</html:em> of states. A superposition is a probability distribution, giving the chance of the cell "collapsing" to a given value.</html:li>

<html:li>If there is no cell that is fully collapsed, choose a random one and generate its collapsed state from its distribution.</html:li>

<html:li>Recursively propagate distributions from any collapsed cells, asking what their distributions are given the contents of their neighbours.</html:li></html:ol>
                <html:p>Some cells may end up in a state with a zero distribution. These cells fail to generate.</html:p>
                <html:p>
                  <html:strong>Notes on the Implementation.</html:strong>
                </html:p>
                <html:p>There are two basic building blocks, a <html:code>Pattern</html:code>, representing the neighbour distributions of a single type (dirt, stone or sky for example). In our generation map, there are a series of <html:code>Cell</html:code>s, each having their own distribution.</html:p>
                <html:p>The challenge here is that the number of unique tile types in the reference is effectively dynamic. This suggests a memory layout:</html:p>
                <html:p>
                  <html:code>struct Cell { std::vector&lt;double&gt; dist; // ... };</html:code>
                </html:p>
                <html:p>
                  <html:code>template &lt;size_t D&gt; struct Pattern

{ std::array&lt;std::vector&lt;double&gt;, D*2&gt; neighbour_dists; };</html:code>
                </html:p>
                <html:p>Where <html:code>D</html:code> is the dimension. We then have a <html:code>vector</html:code> of <html:code>Cell</html:code>s which we update according to the algorithm.</html:p>
                <html:p>While this approach will certainly work, each cell having a pointer indirect to somewhere else on the heap is a rather silly memory layout. We can make all the cell data contiguous at the small cost of having to specify a maximum number of texture types <html:code>N</html:code> at compile time:</html:p>
                <html:p>
                  <html:code>template&lt;size_t N&gt; struct Cell { std::array&lt;double, N&gt; dist; // ... };</html:code>
                </html:p>
                <html:p>
                  <html:code>template &lt;size_t D, size_t N&gt; struct Pattern

{ std::array&lt;std::array&lt;double, N&gt;, D*2&gt; neighbour_dists; };</html:code>
                </html:p>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="false" expanded="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2026</fr:year>
                  <fr:month>1</fr:month>
                  <fr:day>6</fr:day>
                </fr:date>
                <fr:uri>http://localhost/4GXX/</fr:uri>
                <fr:display-uri>4GXX</fr:display-uri>
                <fr:route>/4GXX/</fr:route>
                <fr:title text="My NEAT Implementation (2022)">My NEAT Implementation (2022)</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>The NEAT (NeuroEvolution of Augmenting Topologies) algorithm is an effective genetic neuro-evolution algorithm from MIT. The original paper is <fr:link href="https://nn.cs.utexas.edu/downloads/papers/stanley.ec02.pdf" type="external">here</fr:link>. It's approach is to split the population into different "species", so that topological improvements in the neural nets (which will initially reduce fitness) are allowed to evolve in their own niche.</html:p>
                <html:p>I wrote an implementation of the algorithm (<fr:link href="https://github.com/STaylorPi/NEAT" type="external">here</fr:link>) and managed to tune it to fairly robustly balance a pole:</html:p>
                <html:figure><html:video controls="" width="600px" type="&quot;video/mp4&quot;" src="/bafkrmihm7luwe3te7y4jvvjr57lz63iz7z5ho24sjtinwob5g6vomrw2r4.mp4" />
	<html:figcaption>the network swings up and balances a pole</html:figcaption></html:figure>
              </fr:mainmatter>
            </fr:tree>
            <fr:tree show-metadata="false" expanded="false">
              <fr:frontmatter>
                <fr:authors>
                  <fr:author>
                    <fr:link href="/sebastianttaylor/" title="Sebastian Taylor" uri="http://localhost/sebastianttaylor/" display-uri="sebastianttaylor" type="local">Sebastian Taylor</fr:link>
                  </fr:author>
                </fr:authors>
                <fr:date>
                  <fr:year>2026</fr:year>
                  <fr:month>1</fr:month>
                  <fr:day>6</fr:day>
                </fr:date>
                <fr:uri>http://localhost/X5PF/</fr:uri>
                <fr:display-uri>X5PF</fr:display-uri>
                <fr:route>/X5PF/</fr:route>
                <fr:title text="CREST Silver and Gold Awards (2021)">CREST Silver and Gold Awards (2021)</fr:title>
              </fr:frontmatter>
              <fr:mainmatter>
                <html:p>These two awards were given to me in 2021 simultaneously for the project of "Building, Programming and Controlling a Quadcopter". I turned in a <fr:link href="http://sttaylor.net/crest.pdf" type="external">37-page report</fr:link> on the project.</html:p>
                <html:p>This was a naive 14/15 year old attempt at an integrated control system, and I refined the project after the award increasing the motor DAC resolution to 12 bits and the Python to C++.</html:p>
              </fr:mainmatter>
            </fr:tree>
          </fr:mainmatter>
        </fr:tree>
      </fr:mainmatter>
    </fr:tree>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="Backlinks">Backlinks</fr:title>
      </fr:frontmatter>
      <fr:mainmatter />
    </fr:tree>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="Related">Related</fr:title>
      </fr:frontmatter>
      <fr:mainmatter />
    </fr:tree>
    <fr:tree show-metadata="false" hidden-when-empty="true">
      <fr:frontmatter>
        <fr:authors />
        <fr:title text="Contributions">Contributions</fr:title>
      </fr:frontmatter>
      <fr:mainmatter />
    </fr:tree>
  </fr:backmatter>
</fr:tree>
