Congratulations!

[Valid RSS] This is a valid RSS feed.

Recommendations

This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

Source: http://www.pagetable.com/?feed=rss2

  1. <?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
  2. xmlns:content="http://purl.org/rss/1.0/modules/content/"
  3. xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  4. xmlns:dc="http://purl.org/dc/elements/1.1/"
  5. xmlns:atom="http://www.w3.org/2005/Atom"
  6. xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  7. xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  8. >
  9.  
  10. <channel>
  11. <title>pagetable.com</title>
  12. <atom:link href="https://www.pagetable.com/?feed=rss2" rel="self" type="application/rss+xml" />
  13. <link>https://www.pagetable.com</link>
  14. <description>Some Assembly Required</description>
  15. <lastBuildDate>Sun, 21 Apr 2024 12:50:49 +0000</lastBuildDate>
  16. <language>en-US</language>
  17. <sy:updatePeriod>hourly</sy:updatePeriod>
  18. <sy:updateFrequency>1</sy:updateFrequency>
  19. <generator>https://wordpress.org/?v=4.9.25</generator>
  20. <item>
  21. <title>64&#8217;er Magazin 5/84</title>
  22. <link>https://www.pagetable.com/?p=1768</link>
  23. <comments>https://www.pagetable.com/?p=1768#respond</comments>
  24. <pubDate>Sun, 21 Apr 2024 12:50:49 +0000</pubDate>
  25. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  26. <category><![CDATA[6502]]></category>
  27. <category><![CDATA[C64]]></category>
  28. <category><![CDATA[Commodore]]></category>
  29. <category><![CDATA[Literature]]></category>
  30. <category><![CDATA[VIC-20]]></category>
  31.  
  32. <guid isPermaLink="false">https://www.pagetable.com/?p=1768</guid>
  33. <description><![CDATA[Die Ausgabe 5/84 ist nun auf www.64er-magazin.de verfügbar. Vielen Dank an goloMAK, Endurion und Drachen für das Abtippen der Listings! Neu ist auf der Website zudem: Während über das Inhaltsverzeichnis jetzt schon alle Artikel verfügbar sind, werden auf der Startseite die Artikel nach und nach gepostet, genauso wie im RSS. Alle Fehler in Artikeln, die ... <a title="64&#8217;er Magazin 5/84" class="read-more" href="https://www.pagetable.com/?p=1768">Read more<span class="screen-reader-text">64&#8217;er Magazin 5/84</span></a>]]></description>
  34. <content:encoded><![CDATA[<p>Die Ausgabe 5/84 ist nun auf <a href="https://www.64er-magazin.de/">www.64er-magazin.de</a> verfügbar. Vielen Dank an <a href="https://www.forum64.de/wcf/index.php?user/28439-golomak/">goloMAK</a>, <a href="https://www.forum64.de/wcf/index.php?user/1964-endurion/">Endurion</a> und <a href="https://www.forum64.de/wcf/index.php?user/9125-drachen/">Drachen</a> für das Abtippen der Listings!</p>
  35. <p>Neu ist auf der Website zudem:</p>
  36. <ul>
  37. <li>Während über das Inhaltsverzeichnis jetzt schon alle Artikel verfügbar sind, werden auf der Startseite die Artikel nach und nach gepostet, genauso wie im RSS.</li>
  38. <li>Alle Fehler in Artikeln, die in einem &ldquo;Fehlerteufelchen&rdquo; Abschnitt korrigiert werden, sind durch eine rote Unterstreichung markiert, und über einen Klick gelangt man zur Korrektur. (Fehler in Listings sind weiterhin direkt korrigiert.)</li>
  39. </ul>
  40. <p>Zur nächsten Ausgabe sind folgende Features der Website geplant:</p>
  41. <ul>
  42. <li>Bessere Darstellung auf mobilen Endgeräten</li>
  43. <li>Artikel werden auf Mastodon gepostet</li>
  44. </ul>
  45. ]]></content:encoded>
  46. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1768</wfw:commentRss>
  47. <slash:comments>0</slash:comments>
  48. </item>
  49. <item>
  50. <title>64&#8217;er Magazin – mit 40 Jahren Verzögerung jetzt monatlich im Web</title>
  51. <link>https://www.pagetable.com/?p=1764</link>
  52. <comments>https://www.pagetable.com/?p=1764#comments</comments>
  53. <pubDate>Wed, 20 Mar 2024 08:15:35 +0000</pubDate>
  54. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  55. <category><![CDATA[6502]]></category>
  56. <category><![CDATA[C128]]></category>
  57. <category><![CDATA[C64]]></category>
  58. <category><![CDATA[Commodore]]></category>
  59. <category><![CDATA[GitHub]]></category>
  60. <category><![CDATA[Literature]]></category>
  61. <category><![CDATA[VIC-20]]></category>
  62.  
  63. <guid isPermaLink="false">https://www.pagetable.com/?p=1764</guid>
  64. <description><![CDATA[Zum 40jährigen Jubiläum des 64&#8217;er Magazins präsentieren wir das Kunstprojekt www.64er-magazin.de: eine Website, die so tut, als wäre 1984. Exakt 40 Jahre nach der ursprünglichen Veröffentlichung erscheint hier jeden Monat eine neue Ausgabe: 4/84: 20. März 2024 (Erstausgabe) 5/84: 19. April 2024 6/84: 18. Mai 2024 usw. Auf der modernen Homepage gibt es durchsuchbare PDF-Dateien ... <a title="64&#8217;er Magazin – mit 40 Jahren Verzögerung jetzt monatlich im Web" class="read-more" href="https://www.pagetable.com/?p=1764">Read more<span class="screen-reader-text">64&#8217;er Magazin – mit 40 Jahren Verzögerung jetzt monatlich im Web</span></a>]]></description>
  65. <content:encoded><![CDATA[<p>Zum 40jährigen Jubiläum des <em>64&#8217;er Magazins</em> präsentieren wir das Kunstprojekt <a href="https://www.64er-magazin.de">www.64er-magazin.de</a>: eine Website, die so tut, als wäre 1984. Exakt 40 Jahre nach der ursprünglichen Veröffentlichung erscheint hier jeden Monat eine neue Ausgabe:</p>
  66. <ul>
  67. <li>4/84: 20. März 2024 (Erstausgabe)</li>
  68. <li>5/84: 19. April 2024</li>
  69. <li>6/84: 18. Mai 2024</li>
  70. <li>usw.</li>
  71. </ul>
  72. <p>Auf der modernen Homepage gibt es</p>
  73. <ul>
  74. <li>durchsuchbare PDF-Dateien der einzelnen Ausgaben</li>
  75. <li>alle Artikel im Web-Format mit Kommentar-Funktion</li>
  76. <li>alle Listings zum Download statt zum Abtippen</li>
  77. <li>Übersichtsseiten für alle Tests, alle Listings etc. über alle Ausgaben hinweg</li>
  78. <li>eine Suche über den Text aller Artikel</li>
  79. <li>einen RSS-Feed, der ab Veröffentlichung jeden Tag zwei Artikel liefert</li>
  80. <li>die Funktion, einen Artikel auf Mastodon zu teilen</li>
  81. </ul>
  82. <p>Alle Artikel sind mit dem Text im gedruckten Magazin identisch, Schreibfehler und sachliche Fehler sind also unverändert. Errata aus späteren Ausgaben (&ldquo;Fehlerteufelchen&rdquo;) werden den Artikeln allerdings angehängt, und später dokumentierte Fehler in Software sind in den Downloads bereits behoben.</p>
  83. <p>Verbleibende Unterschiede im Text sowie Verbesserungen der Website sind auf <a href="https://github.com/mist64/64er-magazin.de">GitHub</a> willkommen.</p>
  84. <p><img src="docs/64er-magazin/64er-magazin1.png" alt="" /></p>
  85. <p><img src="docs/64er-magazin/64er-magazin2.png" alt="" /></p>
  86. ]]></content:encoded>
  87. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1764</wfw:commentRss>
  88. <slash:comments>10</slash:comments>
  89. </item>
  90. <item>
  91. <title>Silo S01E06: 38911 BYTES FREE</title>
  92. <link>https://www.pagetable.com/?p=1760</link>
  93. <comments>https://www.pagetable.com/?p=1760#respond</comments>
  94. <pubDate>Fri, 21 Jul 2023 20:53:22 +0000</pubDate>
  95. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  96. <category><![CDATA[6502]]></category>
  97. <category><![CDATA[BASIC]]></category>
  98. <category><![CDATA[C64]]></category>
  99. <category><![CDATA[Commodore]]></category>
  100. <category><![CDATA[Trivia]]></category>
  101.  
  102. <guid isPermaLink="false">https://www.pagetable.com/?p=1760</guid>
  103. <description><![CDATA[]]></description>
  104. <content:encoded><![CDATA[<p><img src="docs/silo_38911/silo_38911.png" width="500" height="300"></p>
  105. <p><video width="100%" autoplay loop muted><source src="docs/silo_38911/silo_38911.mp4" type="video/mp4"></video></p>
  106. ]]></content:encoded>
  107. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1760</wfw:commentRss>
  108. <slash:comments>0</slash:comments>
  109. </item>
  110. <item>
  111. <title>[Ankündigung] Vortrag &#8220;Apollo Guidance Computer&#8221; an der Embedded Computing Conference in Winterthur</title>
  112. <link>https://www.pagetable.com/?p=1753</link>
  113. <comments>https://www.pagetable.com/?p=1753#comments</comments>
  114. <pubDate>Tue, 16 May 2023 12:44:05 +0000</pubDate>
  115. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  116. <category><![CDATA[Archeology]]></category>
  117. <category><![CDATA[Presentation]]></category>
  118.  
  119. <guid isPermaLink="false">https://www.pagetable.com/?p=1753</guid>
  120. <description><![CDATA[This post is about an upcoming talk in German. Am Dienstag, den 06. Juni 2023 um 9:00 gibt es auf der Embedded Computing Conference in Winterthur (bei Zürich) meinen Vortrag zum Thema &#8220;Apollo Guidance Computer im Kontext von modernen Embedded Systems und Echtzeit-Betriebssystemen&#8221;. Beim Vortrag handelt es sich um eine abgewandelte und erweiterte Version des ... <a title="[Ankündigung] Vortrag &#8220;Apollo Guidance Computer&#8221; an der Embedded Computing Conference in Winterthur" class="read-more" href="https://www.pagetable.com/?p=1753">Read more<span class="screen-reader-text">[Ankündigung] Vortrag &#8220;Apollo Guidance Computer&#8221; an der Embedded Computing Conference in Winterthur</span></a>]]></description>
  121. <content:encoded><![CDATA[<p><em>This post is about an upcoming talk in German.</em></p>
  122. <p>Am Dienstag, den 06. Juni 2023 um 9:00 gibt es auf der <a href="https://www.swisst.net/swisstevents/eventdetails/?eventid=1&amp;scrollsingleexecution=62#!/?eventid=1&amp;scrollsingleexecution=62">Embedded Computing Conference</a> in Winterthur (bei Zürich) meinen Vortrag zum Thema &ldquo;<em>Apollo Guidance Computer im Kontext von modernen Embedded Systems und Echtzeit-Betriebssystemen</em>&rdquo;.</p>
  123. <p><img src="docs/agc_ecc/agc_ecc.jpg" height="400" width="710" alt="" /></p>
  124. <p>Beim Vortrag handelt es sich um eine abgewandelte und erweiterte Version des <a href="https://www.pagetable.com/?p=922">Ultimate Apollo Guidance Computer Talk</a> von Christian Hessmann und mir. Diese Version vertieft sich in die Software-Architektur und betrachtet den AGC aus dem Blickwinkel von <em>modernen</em> Echtzeitsystemen – schließlich handelt es sich um eine Konferenz über Embedded Systems!</p>
  125. <p>Die eintägige Konferenz besteht aus 30 weiteren Vorträgen auf 3 bis 4 Tracks. Der Eintritt ist <strong><a href="https://www.swisst.net/swisstevents/eventdetails/?eventid=1&amp;scrollsingleexecution=62#!/?eventid=1&amp;scrollsingleexecution=62">nach vorheriger Anmeldung</a></strong> kostenlos.</p>
  126. <p><a href="https://www.swisst.net/swisstevents/eventdetails/?eventid=1&amp;scrollsingleexecution=62#!/?eventid=1&amp;scrollsingleexecution=62"><img src="docs/agc_ecc/ecc2023.png" height="335" width="659" alt="" /></a></p>
  127. ]]></content:encoded>
  128. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1753</wfw:commentRss>
  129. <slash:comments>1</slash:comments>
  130. </item>
  131. <item>
  132. <title>The Easter Egg in the &#8220;Schrott-Tornado&#8221; at the Deutsches Museum</title>
  133. <link>https://www.pagetable.com/?p=1750</link>
  134. <comments>https://www.pagetable.com/?p=1750#comments</comments>
  135. <pubDate>Mon, 26 Dec 2022 19:21:05 +0000</pubDate>
  136. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  137. <category><![CDATA[Archeology]]></category>
  138. <category><![CDATA[Hardware]]></category>
  139. <category><![CDATA[Puzzle]]></category>
  140. <category><![CDATA[Teardown]]></category>
  141.  
  142. <guid isPermaLink="false">https://www.pagetable.com/?p=1750</guid>
  143. <description><![CDATA[The Deutsches Museum in Munich (Germany) has a new art installation as part of the reopened Electronics exhibition: The &#8220;Schrott-Tornado&#8221;, a tornado-shaped sculpture made from scrap electronics. There is (at least) one item in it that is most definitely not trash. Here is a picture of the full sculpture: Let&#8217;s zoom into the interesting part: ... <a title="The Easter Egg in the &#8220;Schrott-Tornado&#8221; at the Deutsches Museum" class="read-more" href="https://www.pagetable.com/?p=1750">Read more<span class="screen-reader-text">The Easter Egg in the &#8220;Schrott-Tornado&#8221; at the Deutsches Museum</span></a>]]></description>
  144. <content:encoded><![CDATA[<p>The Deutsches Museum in Munich (Germany) has a new art installation as part of the reopened Electronics exhibition: The &ldquo;Schrott-Tornado&rdquo;, a tornado-shaped sculpture made from scrap electronics. There is (at least) one item in it that is most definitely not trash.</p>
  145. <p>Here is a picture of the full sculpture:</p>
  146. <p><img src="docs/schrott_tornado/schrott_tornado.jpg" alt="" /></p>
  147. <p>Let&rsquo;s zoom into the interesting part:</p>
  148. <p><video width="100%" autoplay loop><source src="docs/schrott_tornado/schrott_tornado_zoom.mp4" type="video/mp4"></video></p>
  149. <p>And this is a high-res photo of the detail. You might recognize it.</p>
  150. <p><img src="docs/schrott_tornado/schrott_tornado_detail.jpg" alt="" /></p>
  151. <p>And now look closely at what&rsquo;s written on the cartridge port shield.</p>
  152. <p>Related: Here&rsquo;s the Schrott-Tornado Making-Of video:</p>
  153. <p><iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/CaCB8QQ8zjY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
  154. ]]></content:encoded>
  155. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1750</wfw:commentRss>
  156. <slash:comments>4</slash:comments>
  157. </item>
  158. <item>
  159. <title>darmok.com: Memes in the Tamarian Language</title>
  160. <link>https://www.pagetable.com/?p=1747</link>
  161. <comments>https://www.pagetable.com/?p=1747#comments</comments>
  162. <pubDate>Sun, 25 Dec 2022 13:30:43 +0000</pubDate>
  163. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  164. <category><![CDATA[Uncategorized]]></category>
  165.  
  166. <guid isPermaLink="false">https://www.pagetable.com/?p=1747</guid>
  167. <description><![CDATA[I have created darmok.com, a website that lets you share common memes in the Tamarian language. Try it out, and fork at github.com/mist64/darmok!]]></description>
  168. <content:encoded><![CDATA[<p>I have created <a href="http://darmok.com">darmok.com</a>, a website that lets you share common memes in the Tamarian language.</p>
  169. <p>Try it out, and fork at <a href="https://github.com/mist64/darmok">github.com/mist64/darmok</a>!</p>
  170. <p><a href="http://darmok.com"><img src="docs/darmok/darmok_meta.jpg" alt="" /></a></p>
  171. ]]></content:encoded>
  172. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1747</wfw:commentRss>
  173. <slash:comments>4</slash:comments>
  174. </item>
  175. <item>
  176. <title>PostScript Cartridge for HP LaserJet</title>
  177. <link>https://www.pagetable.com/?p=1721</link>
  178. <comments>https://www.pagetable.com/?p=1721#respond</comments>
  179. <pubDate>Sat, 24 Dec 2022 20:00:31 +0000</pubDate>
  180. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  181. <category><![CDATA[Archeology]]></category>
  182. <category><![CDATA[PostScript]]></category>
  183.  
  184. <guid isPermaLink="false">https://www.pagetable.com/?p=1721</guid>
  185. <description><![CDATA[We have recently dissected and dumped the Level 2 &#8220;Plus&#8221; version of HP&#8217;s PostScript cartridge series. This time, we will look at the earlier Level 1 &#8220;PostScript Cartridge&#8221;. Article Series There are two cartridges for the HP LaserJet series that add Adobe PostScript support. They differ in the PostScript Level: Year Name Description 1991 HP ... <a title="PostScript Cartridge for HP LaserJet" class="read-more" href="https://www.pagetable.com/?p=1721">Read more<span class="screen-reader-text">PostScript Cartridge for HP LaserJet</span></a>]]></description>
  186. <content:encoded><![CDATA[<p>We have recently dissected and dumped the <a href="https://www.pagetable.com/?p=1673">Level 2 &ldquo;Plus&rdquo; version</a> of HP&rsquo;s PostScript cartridge series. This time, we will look at the earlier Level 1 &ldquo;PostScript Cartridge&rdquo;.</p>
  187. <p><a href="docs/postscript_cartridge/postscript_cartridge_case.jpg"><img src="docs/postscript_cartridge/postscript_cartridge_case.jpg" height="276" width="302" alt="" /></a></p>
  188. <h2 id="article-series">Article Series</h2>
  189. <p>There are two cartridges for the HP LaserJet series that add Adobe PostScript support. They differ in the PostScript <a href="https://en.wikipedia.org/wiki/PostScript#History">Level</a>:</p>
  190. <table>
  191. <thead>
  192. <tr>
  193. <th> Year </th>
  194. <th> Name </th>
  195. <th> Description </th>
  196. </tr>
  197. </thead>
  198. <tbody>
  199. <tr>
  200. <td> 1991 </td>
  201. <td> <a href="https://www.pagetable.com/?p=1721">HP LaserJet PostScript Cartridge</a> </td>
  202. <td> PostScript Level 1 </td>
  203. </tr>
  204. <tr>
  205. <td> 1991 </td>
  206. <td> <a href="https://www.pagetable.com/?p=1673">HP LaserJet III PostScript Cartridge Plus</a> </td>
  207. <td> PostScript Level 2 </td>
  208. </tr>
  209. </tbody>
  210. </table>
  211. <p>Note that the article on the Level 2 &ldquo;Plus&rdquo; cartridge is the main one. This article only describes the differences of the Level 1 cartridge to the Level 2 cartridge.</p>
  212. <h2 id="cartridge">Cartridge</h2>
  213. <p>The cartridge is about 9&#215;14 cm in size.</p>
  214. <p><a href="docs/postscript_cartridge/postscript_cartridge_case_front.jpg"><img src="docs/postscript_cartridge/postscript_cartridge_case_front.jpg" height="380" width="269" alt="" /></a><a href="docs/postscript_cartridge/postscript_cartridge_case_back.jpg"><img src="docs/postscript_cartridge/postscript_cartridge_case_back.jpg" height="391" width="270" alt="" /></a></p>
  215. <p>The front says</p>
  216. <blockquote>
  217. <p>HEWLETT PACKARD</p>
  218. <p>PostScript* Cartridge</p>
  219. <p>ITC Avant Garde Gothic<em><br />
  220. ITC Bookman</em><br />
  221. Courier<br />
  222. Helvetica<em><br />
  223. Helvetica-Narrow<br />
  224. New Century Schoolbook<br />
  225. Palatino</em><br />
  226. Times<em><br />
  227. ITC Zapf Chancery</em><br />
  228. TIC Zapf Dingbats*<br />
  229. Symbol<br />
  230. 33439Q ©Hewlett-Packard 1989, 1990, 1991</p>
  231. <p>HP<br />
  232. LASERJET<br />
  233. POSTSCRIPT®</p>
  234. </blockquote>
  235. <p>The back says</p>
  236. <blockquote>
  237. <p>Adobe and PostScript are registered trademark of Adobe Systems<br />
  238. Incorporated in the U.S, and other countries. Helvetica, Palatino and<br />
  239. Times Roman are registered trademarks of Linotype AG and/<br />
  240. or its subsidiaries in the U.S. and other countries. IT Avant Garde<br />
  241. Gothic, ITC Bookman, ITC Zapf Chancery and ITC Zapf Dingbats are<br />
  242. registered trademarks of International Typeface Corporation in the<br />
  243. U.S. and other countries.</p>
  244. </blockquote>
  245. <h2 id="board">Board</h2>
  246. <p>This is the very same board as used by the later <a href="https://www.pagetable.com/?p=1673">&ldquo;Plus&rdquo; cartridge</a>, except that it is fitted with only three instead of four ROM chips:</p>
  247. <p><a href="docs/postscript_cartridge/postscript_cartridge_board_front.jpg"><img src="docs/postscript_cartridge/postscript_cartridge_board_front.jpg" height="254" width="388" alt="" /></a></p>
  248. <p>The ROM chips are:</p>
  249. <ul>
  250. <li>two 512 KB Toshiba TC534200P mask ROM chips</li>
  251. <li>one 512 KB Toshiba TC574200D-150 EPROM</li>
  252. </ul>
  253. <p>Both types conform to the 27C400 pinout. The mask ROMs are marked with</p>
  254. <blockquote>
  255. <p>© 1989-90 HP BOISE<br />
  256. © 1984-90 ADOBE<br />
  257. © 1981 LINOTYPE</p>
  258. </blockquote>
  259. <p>and the EPROM is marked with</p>
  260. <blockquote>
  261. <p>&copy; 1986-91 HP BOISE<br />
  262. &copy; 1984-91 Adobe<br />
  263. &copy; 1981 Linotype AG</p>
  264. </blockquote>
  265. <h2 id="rom">ROM</h2>
  266. <p>These are the verbatim dumps (adjacent bytes are swapped):</p>
  267. <ul>
  268. <li><a href="docs/postscript_cartridge/1818-4788.bin">1818-4788</a>, MD5 e2d740f3b15bfdf837b9385490e8dafd</li>
  269. <li><a href="docs/postscript_cartridge/1818-4789.bin">1818-4789</a>, MD5 1fe13fabb47f0719b9a840df8e5844e6</li>
  270. <li><a href="docs/postscript_cartridge/33439-60115.bin">33439-60115</a>, MD5 520d4f2e0ccf7f143fcdbe62f31792a2</li>
  271. </ul>
  272. <p>This is the combined (<a href="docs/postscript_cartridge_plus/byteswap.py">byte-swapped</a>) 1.5 MB ROM image:</p>
  273. <p><a href="docs/postscript_cartridge/33439q.bin">HP PostScript Cartridge 33439Q ROM</a>, MD5 929ba4050a8a48c3ed4761c3f9267837</p>
  274. <p>The ROM image starts with a signature of &ldquo;SYST&rdquo; and the following messages at 0x30:</p>
  275. <blockquote>
  276. <p>C V6.20 PSCRIPT<br />
  277. 06.20<br />
  278. Copyright &copy; Hewlett-Packard Company, 1991. All rights reserved.</p>
  279. </blockquote>
  280. <p>(This part comes from the EEPROM.)</p>
  281. <p>Just like the &ldquo;Plus&rdquo; cartridge, the ROM contains Adobe&rsquo;s PostScript rasterizer (level 1 in this case) compiled for the 68000 CPU, the PostScript base fonts as well as some LaserJet-specific software (messages, errors and settings texts for the 15 char display in several languages).</p>
  282. <h2 id="postscript-files">PostScript Files</h2>
  283. <p>Like in the &ldquo;Plus&rdquo; cartridge, there is some PostScript source code in the ROMs:</p>
  284. <ul>
  285. <li><a href="docs/postscript_cartridge_plus/ps/fontpage.ps">fontpage.ps</a></li>
  286. <li><a href="docs/postscript_cartridge/ps/test_page.ps">test_page.ps</a></li>
  287. <li><a href="docs/postscript_cartridge/ps/startup_page.ps">startup_page.ps</a></li>
  288. </ul>
  289. <p>(The missing <code>%!</code> file header has been added to the downloads.)</p>
  290. <h3 id="fontpage">FONTPAGE</h3>
  291. <p>&ldquo;FONTPAGE&rdquo; is identical to the file in the &ldquo;Plus&rdquo; cartridge, see <a href="https://www.pagetable.com/?p=1673">there</a>.</p>
  292. <h3 id="test-page">TEST PAGE</h3>
  293. <p>&ldquo;TEST PAGE&rdquo; prints various internal printer settings which are unsupported by computer-based PostScript rasterizers, so the file has been patched to work with the the macOS 12.6 PSNormalizer.framework rasterizer (based on Adobe Acrobat Distiller 5.0):</p>
  294. <ul>
  295. <li><a href="docs/postscript_cartridge/ps/test_page_apple.ps">test_page_apple.ps</a></li>
  296. <li><a href="docs/postscript_cartridge/ps/test_page_apple.pdf">test_page_apple.pdf</a></li>
  297. </ul>
  298. <p><img border="1" src="docs/postscript_cartridge/ps/test_page_apple.png" width="319" height="413"/></p>
  299. <h3 id="startup-page">STARTUP PAGE</h3>
  300. <ul>
  301. <li><a href="docs/postscript_cartridge/ps/startup_page.ps">startup_page.ps</a></li>
  302. <li><a href="docs/postscript_cartridge/ps/startup_page.pdf">startup_page.pdf</a></li>
  303. </ul>
  304. <p><img border="1" src="docs/postscript_cartridge/ps/startup_page.png" width="319" height="413"/></p>
  305. <h2 id="manuals-and-extras">Manuals and Extras</h2>
  306. <p><kbd><a href="docs/postscript_cartridge/postscript_cartridge_users_guide.pdf"><img src="docs/postscript_cartridge/postscript_cartridge_users_guide.jpg" height="316" width="208" alt="" /></a></kbd> <kbd><a href="docs/postscript_cartridge/postscript_cartridge_application_notes.pdf"><img src="docs/postscript_cartridge/postscript_cartridge_application_notes.png" height="314" width="208" alt="" /></a></kbd>  <kbd><a href="docs/postscript_cartridge/postscript_cartridge_license.pdf"><img src="docs/postscript_cartridge/postscript_cartridge_license.jpg" height="314" width="202" alt="" /></a></kbd>  <kbd><a href="docs/postscript_cartridge/postscript_cartridge_smart_choice.pdf"><img src="docs/postscript_cartridge/postscript_cartridge_smart_choice.jpg" height="258" width="185" alt="" /></a></kbd> <kbd><a href="docs/postscript_cartridge/postscript_cartridge_stickers.jpg"><img src="docs/postscript_cartridge/postscript_cartridge_stickers.jpg" height="126" width="100" alt="" /></a></kbd></p>
  307. <h2 id="future-work">Future Work</h2>
  308. <p>Are there versions of this cartridge with different EPROM contents?</p>
  309. ]]></content:encoded>
  310. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1721</wfw:commentRss>
  311. <slash:comments>0</slash:comments>
  312. </item>
  313. <item>
  314. <title>Scanntronik Manuals</title>
  315. <link>https://www.pagetable.com/?p=1730</link>
  316. <comments>https://www.pagetable.com/?p=1730#comments</comments>
  317. <pubDate>Fri, 23 Dec 2022 18:00:18 +0000</pubDate>
  318. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  319. <category><![CDATA[C64]]></category>
  320. <category><![CDATA[Commodore]]></category>
  321. <category><![CDATA[Literature]]></category>
  322.  
  323. <guid isPermaLink="false">https://www.pagetable.com/?p=1730</guid>
  324. <description><![CDATA[The German company &#8220;Scanntronik&#8221; offered a lot of high-quality hardware and software for the Commodore 64 series computers, most in the space of graphics and desktop publishing. They are well-known for their Pagefox and Printfox software as well as their Handyscanner 64 hardware. This page offers most of the German-language manuals from across their product ... <a title="Scanntronik Manuals" class="read-more" href="https://www.pagetable.com/?p=1730">Read more<span class="screen-reader-text">Scanntronik Manuals</span></a>]]></description>
  325. <content:encoded><![CDATA[<p>The German company &ldquo;Scanntronik&rdquo; offered a lot of high-quality hardware and software for the Commodore 64 series computers, most in the space of graphics and desktop publishing. They are well-known for their Pagefox and Printfox software as well as their Handyscanner 64 hardware. This page offers most of the German-language manuals from across their product range as searchable PDFs.</p>
  326. <p><img src="docs/scanntronik_manuals/montage.png" height="271" width="302" alt="" /></p>
  327. <p>If you have any additional manuals, or manuals in different languages, please reach out to me and we can get them added.</p>
  328. <h2 id="pagefox">Pagefox</h2>
  329. <div style="display: flex; flex-flow: row wrap;">
  330. <div style="display: inline-block; margin: 4px; width: 210px;"><a href="docs/scanntronik_manuals/pagefox.pdf"><img src="docs/scanntronik_manuals/pagefox.png" width="210" height="296" style="border:1px solid gray;"></a><center>PAGEFOX</center></div>
  331. <div style="display: inline-block; margin: 4px; width: 213px;"><a href="docs/scanntronik_manuals/tipsundtricks_pagefox.pdf"><img src="docs/scanntronik_manuals/tipsundtricks_pagefox.png" width="213" height="295" style="border:1px solid gray;"></a><center>Tips &#038; Tricks für den PAGEFOX</center></div>
  332. </div>
  333. <h2 id="printfox">Printfox</h2>
  334. <div style="display: flex; flex-flow: row wrap;">
  335. <div style="display: inline-block; margin: 4px; width: 211px;"><a href="docs/scanntronik_manuals/printfox.pdf"><img src="docs/scanntronik_manuals/printfox.png" width="211" height="296" style="border:1px solid gray;"></a><center>Printfox</center></div>
  336. <div style="display: inline-block; margin: 4px; margin: 4px;width: 221px;"><a href="docs/scanntronik_manuals/printfox__errata.pdf"><img src="docs/scanntronik_manuals/printfox__errata.png" width="221" height="129" style="border:1px solid gray;"></a><center>Printfox Errata</center></div>
  337. <div style="display: inline-block; margin: 4px; margin: 4px;width: 211px;"><a href="docs/scanntronik_manuals/character_fox.pdf"><img src="docs/scanntronik_manuals/character_fox.png" width="211" height="296" style="border:1px solid gray;"></a><center>Character Fox; Erweiterungsdisk 1 zum Printfox</center></div>
  338. <div style="display: inline-block; margin: 4px; margin: 4px;width: 212px;"><a href="docs/scanntronik_manuals/fox_bibel.pdf"><img src="docs/scanntronik_manuals/fox_bibel.png" width="212" height="296" style="border:1px solid gray;"></a><center>Die Fox-Bibel zum Printfox-Basar</center></div>
  339. </div>
  340. <h2 id="videofox">Videofox</h2>
  341. <div style="display: flex; flex-flow: row wrap;">
  342. <div style="display: inline-block; margin: 4px; width: 208px;"><a href="docs/scanntronik_manuals/videofox.pdf"><img src="docs/scanntronik_manuals/videofox.png" width="208" height="296" style="border:1px solid gray;"></a><center>VIDEOFOX</center></div>
  343. <div style="display: inline-block; margin: 4px; width: 208px;"><a href="docs/scanntronik_manuals/videofox2.pdf"><img src="docs/scanntronik_manuals/videofox2.png" width="208" height="295" style="border:1px solid gray;"></a><center>VIDEOFOX II</center></div>
  344. <div style="display: inline-block; margin: 4px; width: 209px;"><a href="docs/scanntronik_manuals/movies_videofox.pdf"><img src="docs/scanntronik_manuals/movies_videofox.png" width="209" height="297" style="border:1px solid gray;"></a><center>MOVIES FÜR DEN VIDEOFOX</center></div>
  345. <div style="display: inline-block; margin: 4px; width: 211px;"><a href="docs/scanntronik_manuals/colour_movies.pdf"><img src="docs/scanntronik_manuals/colour_movies.png" width="211" height="297" style="border:1px solid gray;"></a><center>COLOUR MOVIES; Eine Kollektion von Farbbildern und Vorspännen für den Videofox II</center></div>
  346. </div>
  347. <h2 id="eddison-&amp;-eddifox">Eddison &amp; Eddifox</h2>
  348. <div style="display: flex; flex-flow: row wrap;">
  349. <div style="display: inline-block; margin: 4px; width: 213px;"><a href="docs/scanntronik_manuals/eddison.pdf"><img src="docs/scanntronik_manuals/eddison.png" width="213" height="297" style="border:1px solid gray;"></a><center>EDDISON</center></div>
  350. <div style="display: inline-block; margin: 4px; width: 209px;"><a href="docs/scanntronik_manuals/eddifox.pdf"><img src="docs/scanntronik_manuals/eddifox.png" width="209" height="295" style="border:1px solid gray;"></a><center>EDDIFOX</center></div>
  351. </div>
  352. <h2 id="cheese">Cheese</h2>
  353. <div style="display: flex; flex-flow: row wrap;">
  354. <div style="display: inline-block; margin: 4px; width: 195px;"><a href="docs/scanntronik_manuals/cheese.pdf"><img src="docs/scanntronik_manuals/cheese.png" width="195" height="251" style="border:1px solid gray;"></a><center>Bedienungsanleidung Malprogramm Cheese</center></div>
  355. <div style="display: inline-block; margin: 4px; width: 208px;"><a href="docs/scanntronik_manuals/cheese_addon.pdf"><img src="docs/scanntronik_manuals/cheese_addon.png" width="208" height="294" style="border:1px solid gray;"></a><center>Cheese Add-on</center></div>
  356. </div>
  357. <h2 id="colourprinter">Colourprinter</h2>
  358. <div style="display: flex; flex-flow: row wrap;">
  359. <div style="display: inline-block; margin: 4px; width: 210px;"><a href="docs/scanntronik_manuals/colourprinter.pdf"><img src="docs/scanntronik_manuals/colourprinter.png" width="210" height="295" style="border:1px solid gray;"></a><center>Colourprinter</center></div>
  360. </div>
  361. <h2 id="handyscanner-64">Handyscanner 64</h2>
  362. <div style="display: flex; flex-flow: row wrap;">
  363. <div style="display: inline-block; margin: 4px; width: 209px;"><a href="docs/scanntronik_manuals/handyscanner64.pdf"><img src="docs/scanntronik_manuals/handyscanner64.png" width="209" height="292" style="border:1px solid gray;"></a><center>Handyscanner 64</center></div>
  364. </div>
  365. <h2 id="catalogs"><em>Catalogs</em></h2>
  366. <div style="display: flex; flex-flow: row wrap;">
  367. <div style="display: inline-block; margin: 4px; width: 210px;"><a href="docs/scanntronik_manuals/katalog_1990-12.pdf"><img src="docs/scanntronik_manuals/katalog_1990-12.png" width="210" height="296" style="border:1px solid gray;"></a><center>Scanntronik-Katalog 12/90</center></div>
  368. <div style="display: inline-block; margin: 4px; width: 142px;"><a href="docs/scanntronik_manuals/katalog.pdf"><img src="docs/scanntronik_manuals/katalog.png" width="142" height="297" style="border:1px solid gray;"></a><center>Scanntronik Katalog [ca. 1993+]</center></div>
  369. <div style="display: inline-block; margin: 4px; width: 296px;"><a href="docs/scanntronik_manuals/katalog_beilage.pdf"><img src="docs/scanntronik_manuals/katalog_beilage.png" width="296" height="133" style="border:1px solid gray;"></a><center>Katalog-Beilage [ca. 1993+]</center></div>
  370. <div style="display: inline-block; margin: 4px; width: 218px;"><a href="docs/scanntronik_manuals/katalog_1992_12.pdf"><img src="docs/scanntronik_manuals/katalog_1992_12.png" width="218" height="307" style="border:1px solid gray;"></a><center>Scanntronik-Katalog 12/92</center></div>
  371. <div style="display: inline-block; margin: 4px; width: 208px;"><a href="docs/scanntronik_manuals/bestellung.pdf"><img src="docs/scanntronik_manuals/bestellung.png" width="208" height="148" style="border:1px solid gray;"></a><center>Scanntronik Bestell-Postkarte</center></div>
  372. </div>
  373. ]]></content:encoded>
  374. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1730</wfw:commentRss>
  375. <slash:comments>3</slash:comments>
  376. </item>
  377. <item>
  378. <title>The Commodore AUTOMODEM (Model 1650)</title>
  379. <link>https://www.pagetable.com/?p=1716</link>
  380. <comments>https://www.pagetable.com/?p=1716#comments</comments>
  381. <pubDate>Thu, 22 Dec 2022 17:06:57 +0000</pubDate>
  382. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  383. <category><![CDATA[Archeology]]></category>
  384. <category><![CDATA[C64]]></category>
  385. <category><![CDATA[Commodore]]></category>
  386. <category><![CDATA[Literature]]></category>
  387. <category><![CDATA[Modem]]></category>
  388. <category><![CDATA[VIC-20]]></category>
  389.  
  390. <guid isPermaLink="false">https://www.pagetable.com/?p=1716</guid>
  391. <description><![CDATA[The Commodore 1650, also known as the “AUTOMODEM”, is Commodore’s first full modem directly connected to the phone line. It supports pulse dialing in software and 300 baud duplex connections. Historical Context Year Name Model Description 1982 VICMODEM 1600 connected to phone&#8217;s handset connector; manual dialing through phone; Motorola MC14412 1982 AUTOMODEM 1650 connected to ... <a title="The Commodore AUTOMODEM (Model 1650)" class="read-more" href="https://www.pagetable.com/?p=1716">Read more<span class="screen-reader-text">The Commodore AUTOMODEM (Model 1650)</span></a>]]></description>
  392. <content:encoded><![CDATA[<p>The Commodore 1650, also known as the “AUTOMODEM”, is Commodore’s first full modem directly connected to the phone line. It supports pulse dialing in software and 300 baud duplex connections.</p>
  393. <h2 id="historical-context">Historical Context</h2>
  394. <table>
  395. <thead>
  396. <tr>
  397. <th> Year </th>
  398. <th> Name </th>
  399. <th> Model </th>
  400. <th> Description </th>
  401. </tr>
  402. </thead>
  403. <tbody>
  404. <tr>
  405. <td> 1982 </td>
  406. <td> <a href="https://www.pagetable.com/?p=1679">VICMODEM</a> </td>
  407. <td> 1600 </td>
  408. <td> connected to phone&rsquo;s handset connector; manual dialing through phone; Motorola MC14412 </td>
  409. </tr>
  410. <tr>
  411. <td> 1982 </td>
  412. <td> <a href="https://www.pagetable.com/?p=1716">AUTOMODEM</a> </td>
  413. <td> 1650 </td>
  414. <td> connected to phone line, pulse dialing in software; Motorola MC14412 </td>
  415. </tr>
  416. <tr>
  417. <td> 1985 </td>
  418. <td> <a href="https://www.pagetable.com/?p=1644">MODEM/300</a> </td>
  419. <td> 1660 </td>
  420. <td>added tone dialing support by feeding SID output into modem; Texas Instruments TMS99532A </td>
  421. </tr>
  422. <tr>
  423. <td> 1987 </td>
  424. <td> <a href="https://www.pagetable.com/?p=1647">MODEM/1200</a> </td>
  425. <td> 1670 </td>
  426. <td> Hayes command set; pulse and tone dialing in hardware; 300/1200 baud support; U.S. Robotics chipset </td>
  427. </tr>
  428. </tbody>
  429. </table>
  430. <h2 id="photos">Photos</h2>
  431. <p><a href="docs/cbm1650modem/cbm1650.jpg"><img src="docs/cbm1650modem/cbm1650.jpg" height="398" width="506" alt="" /></a></p>
  432. <p>On the front, there is the VIC-20/C64 user port connector and an activity LED.</p>
  433. <p>On the left, there is<br />
  434. * the “LINE” jack that is supposed to be connected to the telephone network<br />
  435. * the “PHONE” jack for connecting an existing telephone<br />
  436. * a D/T switch. In D mode, the modem takes over the phone line, in T mode, “LINE” gets passed through to the telephone.<br />
  437. * an A/O switch. When making a modem call, it is to be put into the “O” (originate) position, and when answering a call, it is to be put into the “A” (answer) position.</p>
  438. <p>On the right, there is an H/F switch to select Half Duplex (“H“) or Full Duplex (“F“).</p>
  439. <p><a href="docs/cbm1650modem/front.jpg"><img src="docs/cbm1650modem/front.jpg" height="277" width="348" alt="" /></a><a href="docs/cbm1650modem/back.jpg"><img src="docs/cbm1650modem/back.jpg" height="284" width="356" alt="" /></a></p>
  440. <p>The label on the bottom says:</p>
  441. <blockquote>
  442. <p>FCC ID: B4V8N2AUTOVIC<br />
  443. Commodore Business Machines, Inc<br />
  444. Made in USA</p>
  445. <p>Certified to comply with the limits for a Class B<br />
  446. computing device pursuant to Subpart J of<br />
  447. Part 15 of FCC Rules. See instructions if<br />
  448. interference to radio reception is suspected.</p>
  449. <p>Complies with Part 68, FCC Rules; FCC<br />
  450. Registration Number B4V8N2-70317-<br />
  451. DM-R; Ringer Equivalence 0.1B; Jack<br />
  452. (USOC) RJ11</p>
  453. <p>Model 1650<br />
  454. Serial Number: 018208</p>
  455. </blockquote>
  456. <p><a href="docs/cbm1650modem/board1.jpg"><img src="docs/cbm1650modem/board1.jpg" height="283" width="375" alt="" /></a><a href="docs/cbm1650modem/board2.jpg"><img src="docs/cbm1650modem/board2.jpg" height="281" width="359" alt="" /></a></p>
  457. <p>The only text on the board is &ldquo;00312A&rdquo; on the back. The core component is the Motorola <a href="docs/cbm1600modem/MC14412.pdf">MC14412</a> modem chip at the bottom center.</p>
  458. <p>The design of the AUTOMODEM is basically the same as its <a href="TODO">predecessor&rsquo;s</a>, with the extra circuitry added to allow it work with the phone line directly instead of acting as the handset of an existing telephone.</p>
  459. <h2 id="box">Box</h2>
  460. <p><a href="docs/cbm1650modem/box_front.jpg"><img src="docs/cbm1650modem/box_front.jpg" height="357" width="257" alt="" /></a><a href="docs/cbm1650modem/box_side.jpg"><img src="docs/cbm1650modem/box_side.jpg" height="357" width="59" alt="" /></a><a href="docs/cbm1650modem/box_back.jpg"><img src="docs/cbm1650modem/box_back.jpg" height="357" width="257" alt="" /></a></p>
  461. <h2 id="manual">Manual</h2>
  462. <p><a href="docs/cbm1650modem/cbm1650modem_manual.pdf"><img src="docs/cbm1650modem/cbm1650modem_manual.jpg" height="261" width="189" alt="" /></a></p>
  463. <h2 id="more-box-contents-&amp;-tape">More Box Contents &amp; Tape</h2>
  464. <p>I don&rsquo;t have any of the extras that came with the box. If you have them, please contact me.</p>
  465. ]]></content:encoded>
  466. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1716</wfw:commentRss>
  467. <slash:comments>1</slash:comments>
  468. </item>
  469. <item>
  470. <title>A 1960s Children&#8217;s Book about Computers</title>
  471. <link>https://www.pagetable.com/?p=1709</link>
  472. <comments>https://www.pagetable.com/?p=1709#comments</comments>
  473. <pubDate>Wed, 21 Dec 2022 21:43:22 +0000</pubDate>
  474. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  475. <category><![CDATA[Archeology]]></category>
  476.  
  477. <guid isPermaLink="false">https://www.pagetable.com/?p=1709</guid>
  478. <description><![CDATA[The 1963 book &#8220;Robots and Electronic Brains&#8221; (by Robert Scharff) from the &#8220;How and Why Wonder Books&#8221; series is an early children&#8217;s book about computers. Let&#8217;s look at some of the interesting contents – and how the German translation &#8220;Was ist was: Roboter und Elektronengehirne&#8221; from 1967 changed some details. Historical Context of the Books ... <a title="A 1960s Children&#8217;s Book about Computers" class="read-more" href="https://www.pagetable.com/?p=1709">Read more<span class="screen-reader-text">A 1960s Children&#8217;s Book about Computers</span></a>]]></description>
  479. <content:encoded><![CDATA[<p>The 1963 book <a href="https://www.worldcat.org/title/how-and-why-wonder-book-of-robots-and-electronic-brains/oclc/7839429?referer=br&#038;ht=edition">&#8220;Robots and Electronic Brains&#8221;</a> (by Robert Scharff) from the <a href="https://en.wikipedia.org/wiki/How_and_Why_Wonder_Books">&#8220;How and Why Wonder Books&#8221;</a> series is an early children&#8217;s book about computers. Let&#8217;s look at some of the interesting contents – and how the German translation &#8220;Was ist was: Roboter und Elektronengehirne&#8221; from 1967 changed some details.</p>
  480. <p><img src="docs/robots_and_electronic_brains/titles.jpg" width="670" height="450"></p>
  481. <h2>Historical Context of the Books</h2>
  482. <p>Published in 1963, this might very well be one of the first children&#8217;s books on computers ever. To put this into context, this was:</p>
  483. <ul>
  484. <li>less than 20 years after the first workable computers</li>
  485. <li>four years after the introduction of the IBM 1401, one of the first commercial computers based on transistors (as opposed to tubes), weighing five tons and with programs on punch cards</li>
  486. <li>six years before the <a href="https://www.pagetable.com/?p=922">moon landings</a></li>
  487. </ul>
  488. <p>With this context, the somewhat weird title &#8220;Robots and Electronic Brains&#8221; might be more understandable:</p>
  489. <ul>
  490. <li>Computers are called &#8220;electronic brains&#8221;, since &#8220;computers&#8221; might not have been a common enough term (especially for children!) at the time.</li>
  491. <li>Even though the book is mostly about computers, &#8220;robots&#8221; are mentioned first in the title, because they are a familiar concept. Robots like &#8220;Robby&#8221; (&#8220;Forbidden Planet&#8221;, 1956) were part of the pop culture.</li>
  492. </ul>
  493. <p>While the original German version went with a literal translation, later editions updated the title to &#8220;Computer und Roboter&#8221; – computers and robots. They kept updating it until <a href="https://www.amazon.de/Was-ist-was-Band-037/dp/3788602775">1999</a>, but it&#8217;s now out of print.</p>
  494. <h2>Differences in the German Version</h2>
  495. <p>The German translation was done by Käte und Heinrich Hart. While the book retains the same chapters and the identical layout, the text was slightly adapted. Here are some examples:</p>
  496. <h3>Who invented computers?</h3>
  497. <p>This question has no easy answer, as both versions correctly state. Yet the original version claims it&#8217;s an <i>American</i> invention – this part has been removed from the German version.</p>
  498. <table border="1">
  499. <tr>
  500. <th width="33%">English</th>
  501. <th width="33%">German (<a href="https://www.DeepL.com/Translator">translated back</a>)</th>
  502. <th width="33%">German</th>
  503. </tr>
  504. <tr>
  505. <td style="vertical-align: top;">
  506. <p>The electronic computer, among the foremost <b>American</b> inventions of this century, was not an overnight discovery. It is the fruit of the practical science of mathematics and has its roots far in the past.</p>
  507. </td>
  508. <td style="vertical-align: top;">
  509. <p>Electronic computing systems, one of the most significant technical achievements of this century, were not accidental inventions. They are the result of developed modern technology and applied mathematical science. The origin of mathematics lies far in the past.</p>
  510. </td>
  511. <td style="vertical-align: top;">
  512. <p>Die elektronischen Rechenanlagen, eine der bedeutendsten technischen Leistungen dieses Jahrhunderts, waren keine Zufallserfindungen. Sie sind das Ergebnis der entwickelten modernen Technik und der angewandten mathematischen Wissenschaft. Der Ursprung der Mathematik liegt weit in der Vergangenheit.</p>
  513. </td>
  514. </tr>
  515. </table>
  516. <p>The translation also adds an explicit credit to the German Leibniz for his <a href="https://en.wikipedia.org/wiki/Stepped_reckoner">calculator</a>:</p>
  517. <table border="1">
  518. <tr>
  519. <th width="33%">English</th>
  520. <th width="33%">German (<a href="https://www.DeepL.com/Translator">translated back</a>)</th>
  521. <th width="33%">German</th>
  522. </tr>
  523. <tr>
  524. <td style="vertical-align: top;">
  525. <p>The first adding machine, invented in 1642, was followed by a four-operation arithmetic machine composed of a difference engine that performed calculations, a mechanical tabulator, a punch-paper control system, and a differential analyzer. Although these inventions increased computation speeds, they failed to fulfill the needs of our complex world.</p>
  526. </td>
  527. <td style="vertical-align: top;">
  528. <p>The first simple adding machine was invented in 1642, the first calculating machine for all four basic arithmetic operations around 1672 <b>by the German philosopher and mathematician Leibniz.</b></p>
  529. </td>
  530. <td style="vertical-align: top;">
  531. <p>Die erste einfache Addiermaschine wurde im Jahre 1642 erfunden, die erste Rechenmaschine für alle vier Grundrechnungsarten um 1672 <b>von dem deutschen Philosophen und Mathematiker Leibniz.</b></p>
  532. </td>
  533. </tr>
  534. </table>
  535. <p>The two versions of the book heavily diverge on the topic of the first &#8220;workable&#8221; computer:</p>
  536. <table border="1">
  537. <tr>
  538. <th width="33%">English</th>
  539. <th width="33%">German (<a href="https://www.DeepL.com/Translator">translated back</a>)</th>
  540. <th width="33%">German</th>
  541. </tr>
  542. <tr>
  543. <td style="vertical-align: top;">
  544. <p>In <b>1936</b>, a young <b>Harvard</b> physicist, Professor <b>Howard Aiken</b>, happened upon some of the writings of Dr. Babbage. Like Babbage, Dr. Aiken saw the possibility of a robot that could do the thinking of hundreds of men in a fraction of the time it took any one of them to work out routine mathematical problems. Aiken teamed up with other researchers and, by 1944, they built the first workable computer.</p>
  545. </td>
  546. <td style="vertical-align: top;">
  547. <p>An electromechanical calculator was first built in <b>Germany</b> in <b>1941</b> by <b>K. Zuse</b>. <b>At the same time, similar types were being worked on in North America.</b></p>
  548. </td>
  549. <td style="vertical-align: top;">
  550. <p>Eine elektromechanische Rechenmaschine wurde zuerst im Jahre <b>1941</b> in <b>Deutschland</b> von <b>K. Zuse</b> gebaut. <b>Zur gleichen Zeit wurde in Nordamerika an ähnlichen Typen gearbeitet.</a></p>
  551. </td>
  552. </tr>
  553. </table>
  554. <p>Some historical context from today&#8217;s perspective: There were actually several electronic or electromechanical and <a href="https://en.wikipedia.org/wiki/History_of_computing_hardware#Early_digital_computer_characteristics">more or less general-purpose computers in the 1940s</a>. The original version of the book picked the <a href="https://en.wikipedia.org/wiki/Harvard_Mark_I">Harvard Mark I</a> by the team around Howard Aiken as the first &#8220;workable&#8221; computer. The German adaptation replaced this with the <a href="https://en.wikipedia.org/wiki/Z3_(computer)">Z3</a> of the German Konrad Zuse, which pre-dated the Mark I as the first programmable computer by 3 years.</p>
  555. <p>To be fair to the original book, Zuse&#8217;s work had been largely unknown outside of German-speaking countries until at least the late 1970s. The talk <a href="https://www.youtube.com/watch?v=MWaCpSu9eks">&#8220;The Early Development of Digital Computing In Central Europe&#8221;</a> given by <a href="https://en.wikipedia.org/wiki/Friedrich_L._Bauer">Friedrich L. Bauer</a> at the 1976 <a href="https://computerhistory.org/playlists/international-research-conference-on-the-history-of-computing/">First International Research Conference on the History of Computing</a> in Los Alamos, NM presented Zuse&#8217;s work to an international audience. Zuse himself also had a <a href="https://www.youtube.com/watch?v=UHOU_FlLsHc">talk</a> at the conference.</p>
  556. <p>In fact, even the <a href="https://en.wikipedia.org/wiki/Colossus_computer">Colossus Mark 1</a> at Bletchley Park predated the Harvard Mark I (by 3 months), but the UK&#8217;s codebreakers efforts were still secret at the time the books were written – in fact, they were <a href="https://www.youtube.com/watch?v=WCeLTWPCEFI">revealed</a> at the very same conference in 1976.</a></p>
  557. <p>Here is what the two versions say about ENIAC:</p>
  558. <table border="1">
  559. <tr>
  560. <th width="33%">English</th>
  561. <th width="33%">German (<a href="https://www.DeepL.com/Translator">translated back</a>)</th>
  562. <th width="33%">German</th>
  563. </tr>
  564. <tr>
  565. <td style="vertical-align: top;">
  566. <p>Two years later, the <b>first general-purpose</b>, all-electronic computer, called the ENIAC computer (from Electric Numerical Integrator and Calculator), was built. ENIAC was the grandfather of today’s electronic brains, room-size robots who answer to the unlikely names as UNIVAC, STRETCH, MANIAC, UNICALL, MINIVAC, SEAC, and BIZMAC.</p>
  567. </td>
  568. <td style="vertical-align: top;">
  569. <p>In 1946, there was <b>also</b> the first real electron computer <b>in the USA</b>, named ENIAC (Electronic Numerical Integrator And Computer). ENIAC was, so to speak, the ancestor of today&#8217;s electron brains, the room-sized robots. Some <b>American</b> manufacturers give them names like UNIVAC, MANIAC, UNICALL, MINIVAC and BIZMAC; <b>others, including the German manufacturers, refer to their various computer types only by numbers.</b></p>
  570. </td>
  571. <td style="vertical-align: top;">
  572. <p>1946 gab es <b>auch in den USA</b> den ersten wirklichen Elektronenrechner, ENIAC genannt (Electronic Numerical Integrator And Computer). ENIAC war sozusagen der Ahnherr der heutigen Elektronengehirne, der zimmergroßen Roboter. Einige <b>amerikanische</b> Hersteller geben ihnen Namen wie UNIVAC, MANIAC, UNICALL, MINIVAC und BIZMAC; <b>andere, auch die deutschen Hersteller, bezeichnen ihre verschiedenen Computertypen nur mit Nummern.</b></p>
  573. </td>
  574. </tr>
  575. </table>
  576. <p>The German version downgrades <a href="https://en.wikipedia.org/wiki/ENIAC">ENIAC</a> into an &#8220;also-ran&#8221;. In all fairness, ENIAC should be credited as the first working computer designed to be Turing-complete.</p>
  577. <p>Finally, the German text clarifies that the UNIVAC-style naming scheme does not apply to all computers, especially non-US ones.</p>
  578. <h3>Does an electronic brain ever fail?</h3>
  579. <p>Thankfully (and surprisingly), the German version removes the sexism from the garbage-in/garbage-out chapter:</p>
  580. <table border="1">
  581. <tr>
  582. <th width="33%">English</th>
  583. <th width="33%">German (<a href="https://www.DeepL.com/Translator">translated back</a>)</th>
  584. <th width="33%">German</th>
  585. </tr>
  586. <tr>
  587. <td style="vertical-align: top;">
  588. A computer, of course, gives wrong answers if given wrong information. One experiment with the decision-making ability of computers was a failure. A television quiz program used a computer to select the ideal wife for a contestant. To accomplish this, the programmer fed into the machine all facts known for a perfect marriage – likes and dislikes, interests in various hobbies, movies, music, food, etc. When the computer compared the qualifications of many women with those of the male contestant, it recommended one as ideal. But, when the two got to know each other, they decided they were mismatched and should not marry each other. Whose fault was this? The machine programmer’s? <b>Perhaps it only proves that even a computer cannot understand a woman’s mind.</b>
  589. </td>
  590. <td style="vertical-align: top;">
  591. Of course, if a computer is fed the wrong information, it will give a wrong answer, but other attempts to use a computer&#8217;s capability can also lead to failure. Example: In a television program, a computer was used to find out the ideal wife for a certain man. The programmer gave the electronic computer all the characteristics desired for an ideal marriage – likes and dislikes, interests in various hobbies, in cultural values, and so on. After the computer compared the characteristics of many women with those of the man in question or according to his wishes, it declared a particular woman to be the ideal partner. But when the two got to know each other, it turned out that they did not like each other. Whose fault was that? The programmer&#8217;s? <b>Perhaps the experiment only proves that the human heart and its inclinations are not calculable.</b>
  592. </td>
  593. <td style="vertical-align: top;">
  594. Natürlich gibt ein Computer, wenn er mit falschen Angaben gefüttert wurde, eine falsche Antwort. Aber auch andere Versuche, die Fähigkeit eines Computers zu nutzen, können zum Mißerfolg führen. Ein Beispiel: In einem Fernsehprogramm wurde ein Computer dazu benutzt, jeweils für einen bestimmten Mann die ideale Ehefrau herauszufinden. Der Programmierer gab dem Elektronenrechner alle für eine ideale Ehe gewünschten Eigenschaften auf – Neigungen und Abneigungen, Interessen an verschiedenen Hobbies, an kulturellen Werten usw. Nachdem der Computer die Eigenschaften vieler Frauen mit denen des betreffenden Mannes oder gemäß dessen Wünschen verglichen hatte, erklärte er eine bestimmte Frau zur idealen Partnerin. Aber als die beiden dann einander kennenlernten, stellte sich heraus, daß sie sich nicht sympathisch waren. Wessen Fehler war das? Des Programmierers? <b>Vielleicht beweist der Versuch nur, daß das menschliche Herz und seine Neigung nicht berechenbar ist.</b>
  595. </td>
  596. </tr>
  597. </table>
  598. <h2>Complete Comparison</h2>
  599. <p>Here are both books side by side. If you find any more interesting details (or differences), please add them in the comments of this article!</p>
  600. <p><img src="docs/robots_and_electronic_brains/haw-000.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-000.webp" width="335" height="450"><br />
  601. <img src="docs/robots_and_electronic_brains/haw-001.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-002.webp" width="335" height="450"><br />
  602. <img src="docs/robots_and_electronic_brains/wiw-001.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-002.webp" width="335" height="450"><br />
  603. <img src="docs/robots_and_electronic_brains/haw-003.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-004.webp" width="335" height="450"><br />
  604. <img src="docs/robots_and_electronic_brains/wiw-003.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-004.webp" width="335" height="450"><br />
  605. <img src="docs/robots_and_electronic_brains/haw-005.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-006.webp" width="335" height="450"><br />
  606. <img src="docs/robots_and_electronic_brains/wiw-005.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-006.webp" width="335" height="450"><br />
  607. <img src="docs/robots_and_electronic_brains/haw-007.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-008.webp" width="335" height="450"><br />
  608. <img src="docs/robots_and_electronic_brains/wiw-007.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-008.webp" width="335" height="450"><br />
  609. <img src="docs/robots_and_electronic_brains/haw-009.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-010.webp" width="335" height="450"><br />
  610. <img src="docs/robots_and_electronic_brains/wiw-009.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-010.webp" width="335" height="450"><br />
  611. <img src="docs/robots_and_electronic_brains/haw-011.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-012.webp" width="335" height="450"><br />
  612. <img src="docs/robots_and_electronic_brains/wiw-011.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-012.webp" width="335" height="450"><br />
  613. <img src="docs/robots_and_electronic_brains/haw-013.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-014.webp" width="335" height="450"><br />
  614. <img src="docs/robots_and_electronic_brains/wiw-013.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-014.webp" width="335" height="450"><br />
  615. <img src="docs/robots_and_electronic_brains/haw-015.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-016.webp" width="335" height="450"><br />
  616. <img src="docs/robots_and_electronic_brains/wiw-015.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-016.webp" width="335" height="450"><br />
  617. <img src="docs/robots_and_electronic_brains/haw-017.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-018.webp" width="335" height="450"><br />
  618. <img src="docs/robots_and_electronic_brains/wiw-017.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-018.webp" width="335" height="450"><br />
  619. <img src="docs/robots_and_electronic_brains/haw-019.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-020.webp" width="335" height="450"><br />
  620. <img src="docs/robots_and_electronic_brains/wiw-019.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-020.webp" width="335" height="450"><br />
  621. <img src="docs/robots_and_electronic_brains/haw-021.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-022.webp" width="335" height="450"><br />
  622. <img src="docs/robots_and_electronic_brains/wiw-021.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-022.webp" width="335" height="450"><br />
  623. <img src="docs/robots_and_electronic_brains/haw-023.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-024.webp" width="335" height="450"><br />
  624. <img src="docs/robots_and_electronic_brains/wiw-023.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-024.webp" width="335" height="450"><br />
  625. <img src="docs/robots_and_electronic_brains/haw-025.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-026.webp" width="335" height="450"><br />
  626. <img src="docs/robots_and_electronic_brains/wiw-025.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-026.webp" width="335" height="450"><br />
  627. <img src="docs/robots_and_electronic_brains/haw-027.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-028.webp" width="335" height="450"><br />
  628. <img src="docs/robots_and_electronic_brains/wiw-027.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-028.webp" width="335" height="450"><br />
  629. <img src="docs/robots_and_electronic_brains/haw-029.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-030.webp" width="335" height="450"><br />
  630. <img src="docs/robots_and_electronic_brains/wiw-029.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-030.webp" width="335" height="450"><br />
  631. <img src="docs/robots_and_electronic_brains/haw-031.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-032.webp" width="335" height="450"><br />
  632. <img src="docs/robots_and_electronic_brains/wiw-031.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-032.webp" width="335" height="450"><br />
  633. <img src="docs/robots_and_electronic_brains/haw-033.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-034.webp" width="335" height="450"><br />
  634. <img src="docs/robots_and_electronic_brains/wiw-033.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-034.webp" width="335" height="450"><br />
  635. <img src="docs/robots_and_electronic_brains/haw-035.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-036.webp" width="335" height="450"><br />
  636. <img src="docs/robots_and_electronic_brains/wiw-035.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-036.webp" width="335" height="450"><br />
  637. <img src="docs/robots_and_electronic_brains/haw-037.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-038.webp" width="335" height="450"><br />
  638. <img src="docs/robots_and_electronic_brains/wiw-037.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-038.webp" width="335" height="450"><br />
  639. <img src="docs/robots_and_electronic_brains/haw-039.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-040.webp" width="335" height="450"><br />
  640. <img src="docs/robots_and_electronic_brains/wiw-039.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-040.webp" width="335" height="450"><br />
  641. <img src="docs/robots_and_electronic_brains/haw-041.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-042.webp" width="335" height="450"><br />
  642. <img src="docs/robots_and_electronic_brains/wiw-041.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-042.webp" width="335" height="450"><br />
  643. <img src="docs/robots_and_electronic_brains/haw-043.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-044.webp" width="335" height="450"><br />
  644. <img src="docs/robots_and_electronic_brains/wiw-043.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-044.webp" width="335" height="450"><br />
  645. <img src="docs/robots_and_electronic_brains/haw-045.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/haw-046.webp" width="335" height="450"><br />
  646. <img src="docs/robots_and_electronic_brains/wiw-045.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-046.webp" width="335" height="450"><br />
  647. <img src="docs/robots_and_electronic_brains/haw-047.webp" width="335" height="450"><img src="docs/robots_and_electronic_brains/wiw-047.webp" width="335" height="450"></p>
  648. ]]></content:encoded>
  649. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1709</wfw:commentRss>
  650. <slash:comments>6</slash:comments>
  651. </item>
  652. <item>
  653. <title>Digitizing Analog Video through a Digital Camcorder</title>
  654. <link>https://www.pagetable.com/?p=1697</link>
  655. <comments>https://www.pagetable.com/?p=1697#comments</comments>
  656. <pubDate>Sat, 19 Nov 2022 22:06:04 +0000</pubDate>
  657. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  658. <category><![CDATA[Archeology]]></category>
  659. <category><![CDATA[Digital Video]]></category>
  660.  
  661. <guid isPermaLink="false">https://www.pagetable.com/?p=1697</guid>
  662. <description><![CDATA[This article explains a setup and workflow for digitizing analog video (e.g. VHS, Beta, Video 2000, LaserDisc, &#8230;) using a Mac and digital camcorder – in high quality and with interlacing intact; optimized for archival. We will use a old-school digital camcorder (they are cheap!) to convert the analog signal to a high-quality digital &#8220;DV&#8221; ... <a title="Digitizing Analog Video through a Digital Camcorder" class="read-more" href="https://www.pagetable.com/?p=1697">Read more<span class="screen-reader-text">Digitizing Analog Video through a Digital Camcorder</span></a>]]></description>
  663. <content:encoded><![CDATA[<p>This article explains a setup and workflow for digitizing analog video (e.g. VHS, Beta, Video 2000, LaserDisc, &hellip;) using a Mac and digital camcorder – in high quality and with interlacing intact; optimized for archival. We will use a old-school digital camcorder (they are cheap!) to convert the analog signal to a high-quality digital &ldquo;DV&rdquo; stream and then record the DV stream on a Mac using a FireWire connection.</p>
  664. <p><img src="docs/digitizing_vhs/digitizing_vhs.png" height="238" width="800" alt="" /></p>
  665. <h2 id="the-problem-with-interlaced-video">The Problem with Interlaced Video</h2>
  666. <p>Standard definition analog video is either</p>
  667. <ul>
  668. <li>576i50 (PAL/SECAM): 25 full frames per second of 720&#215;576 pixels, interlaced</li>
  669. <li>480i60 (NTSC): 30 full frames per second of 720&#215;480 pixels, interlaced</li>
  670. </ul>
  671. <p>Interlaced means that every full frame is split into two &ldquo;fields&rdquo;, one with the picture&rsquo;s 288 (PAL; NTSC: 240) odd lines, and one with the 288 (240) even lines. These two fields can represent one moment in time: When combining them, they will give 25 (30) full 720&#215;576 (720&#215;480) frames. Or they can be recorded 1/50 (1/60) second apart, giving motion at a resolution of 50 (60) Hz, but at half the vertical resolution, and with the set of lines alternating every time.</p>
  672. <p>Interlaced video was natural for old CRT displays, but in order to show interlaced video on modern displays or encode them into modern video compression formats, they need to be converted into progressive format, i.e. deinterlaced.</p>
  673. <p>The two fields are always transmitted one after the other, and it is unclear whether every two fields should be combined (&ldquo;comb filter&rdquo;) for a 25 (30) Hz video or whether the 50 (60) fields should be vertically upscaled for a 50 (60) Hz video. Using the wrong method means either <a href="https://www.google.com/search?q=interlaced+combing+artifact">combing artifacts</a> or flickering.</p>
  674. <p>The worst part is when the pairing of fields is inconsistent, like <a href="https://www.pagetable.com/?p=484">in this example of Futurama</a> or whenever two scenes are composited in the original SD version of Star Trek TNG.</p>
  675. <p>So deinterlacing is hard, especially if you want to do it well. When digitizing analog video, it is best to keep the interlacing intact. When playing the file, VLC for instance will already do pretty good deinterlacing by default – and tomorrow&rsquo;s VLC will certainly do a better job. And if you come back to the footage years later to share it or reuse parts in an HD video, you can use the best deinterlacer available then!</p>
  676. <p>So all in all, if you want to <em>archive</em> analog video, you should keep it interlaced. Many simple solutions won&rsquo;t do this, but this solution does.</p>
  677. <h2 id="digital-video-and-dv">Digital Video and DV</h2>
  678. <p>A full, uncompressed digital representation of a PAL signal is 50 times a second a 720&#215;576 image at 24 bits per pixel, which is 25x720x576x24: almost 240 Mbits/sec.</p>
  679. <p>This implies &ldquo;4:4:4&rdquo;, meaning every 2&#215;2 pixels have four brightness values (Y&#8217;) and four values each for the two color components (Cb and Cr). Most digital formats use <a href="https://en.wikipedia.org/wiki/Chroma_subsampling">chroma subsampling</a>, meaning that a 2&#215;2 pixel grid has fewer chroma values. Since the human visual system is less sensitive to color than it is to brightness, 4:2:2 (half horizontal chroma resolution) is practically indistinguishable from 4:4:4.</p>
  680. <p>The 1986 <a href="https://en.wikipedia.org/wiki/D-1_(Sony)">D-1 format</a> uses 4:2:2 chroma subsampling and thus reduces the data rate by a factor of 1.5 to about 160 MBits/sec. Sony&rsquo;s 1993 <a href="https://en.wikipedia.org/wiki/Digital_Betacam">Digital Betacam</a> additionally uses lossy compression to reduce the data rate by a factor of 2.34:1 down to about 70 MBits/sec. Both formats were meant for professional use.</p>
  681. <p>The consumer format for digital SD camcorders is the 1994 <a href="https://en.wikipedia.org/wiki/DV">DV</a> (&ldquo;Digital Video&rdquo;). It uses 4:2:0 chroma subsampling (one color value per 2&#215;2) for PAL and 4:1:1 (one color value per 4&#215;1) for NTSC, which reduces 4:4:4 data by a factor of 2. The resulting data is lossily <a href="https://en.wikipedia.org/wiki/Discrete_cosine_transform">DCT</a>-compressed by a factor of 5, leading to a data rate of 25 MBit/sec.</p>
  682. <p>DV embeds an uncompressed 48 kHz 16 bit stereo PCM audio stream, which adds 1.5 MBit/sec (or alternatively, two 32 kHz 12 bit stereo streams with the same total bitrate).</p>
  683. <p>Unlike today&rsquo;s common compression methods (e.g. MPEG-2, H.264, H.265), DV compresses images individually, so in the stream, there are no dependencies between images. You can imagine DV as a stream of 64 KB JPEG images. This allows frame-exact editing and allows for simpler encoder and decoder hardware, but means that a higher data rate is required for the same quality. A 25 MBit/sec DV stream is roughly equivalent to a 10 MBit/sec MPEG-2 stream and a 3 MBit/sec H.264 stream.</p>
  684. <p>DV is an excellent match as an intermediate digital representation or even as an archival format for pretty much</a> all analog media, it even surpasses LaserDisc and matches DVD in <a href="https://en.wikipedia.org/wiki/Betamax#Comparison_with_other_video_formats">luma and chroma resolution</a> (PAL values; NTSC are similar):</p>
  685. <table>
  686. <thead>
  687. <tr>
  688. <th> Format    </th>
  689. <th> Luma    </th>
  690. <th> Chroma  </th>
  691. </tr>
  692. </thead>
  693. <tbody>
  694. <tr>
  695. <td> VHS       </td>
  696. <td> 320&#215;576 </td>
  697. <td>  40&#215;288 </td>
  698. </tr>
  699. <tr>
  700. <td> Betamax   </td>
  701. <td> 333&#215;576 </td>
  702. <td>  40&#215;288 </td>
  703. </tr>
  704. <tr>
  705. <td> S-VHS     </td>
  706. <td> 560&#215;576 </td>
  707. <td>  40&#215;288 </td>
  708. </tr>
  709. <tr>
  710. <td> Broadcast </td>
  711. <td> 440&#215;576 </td>
  712. <td> 120&#215;288 </td>
  713. </tr>
  714. <tr>
  715. <td> LaserDisc </td>
  716. <td> 560&#215;576 </td>
  717. <td> 120&#215;288 </td>
  718. </tr>
  719. <tr>
  720. <td> DVD       </td>
  721. <td> 720&#215;576 </td>
  722. <td> 360&#215;288 </td>
  723. </tr>
  724. <tr>
  725. <td> <strong>DV</strong>    </td>
  726. <td> <strong>720&#215;576</strong> </td>
  727. <td> <strong>360&#215;288</strong><sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup> </td>
  728. </tr>
  729. </tbody>
  730. </table>
  731. <p>DV&rsquo;s DCT-based compression is lossy but quite gentle, and given the increased resolution of DV compared to all analog media, it will capture virtually all data from the analog media.</p>
  732. <h2 id="digital-sd-camcorders">Digital SD Camcorders</h2>
  733. <p>There are two types of digital standard definition camcorders:</p>
  734. <ul>
  735. <li>MiniDV (1995) was the industry standard. It uses a proprietary cassette format.</li>
  736. <li>Digital8 (1999) by Sony re-uses the same tapes as the analog Hi8 format.</li>
  737. </ul>
  738. <p>Both Digital8 and MiniDV camcorders store a DV stream on tape and all devices come with a 4-pin FireWire/IEEE-1394/i.LINK connector that allows losslessly copying the DV stream from the camcorder to a second device or to a computer, or copying a DV stream <em>to</em> the device.</p>
  739. <p>In addition to an analog video output (for connecting it to a TV), many camcorders also have an analog composite or even S-Video input. Unless the firmware has this feature disabled to avoid the European tax on video recorders (check the manual!), these camcorders can record analog video from an external input, or output a live digitized DV stream over FireWire (&ldquo;DAC&rdquo;).</p>
  740. <p>This is what we will be using, so the tape format of the camcorder does not matter, since we won&rsquo;t be using tape.</p>
  741. <h2 id="setup">Setup</h2>
  742. <p>You need</p>
  743. <ul>
  744. <li>a high quality player for your VHS, Beta, Video 2000, LaserDisc etc. media</li>
  745. <li>a composite or S-Video cable plus audio to connect the player to the camcorder<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup></li>
  746. <li>a Digital8 or miniDV camcorder that supports digitizing external sources (&ldquo;DAC&rdquo; functionality).</li>
  747. <li>an Apple Mac with a FireWire or Thunderbolt port, so:
  748. <ul>
  749. <li>any iMac, Mac mini, Mac Studio, Mac Pro or MacBook Pro</li>
  750. <li>MacBook Air since Mid 2011</li>
  751. <li>MacBook up to (!) Mid 2009, except Aluminum 2008</li>
  752. </ul>
  753. </li>
  754. </ul>
  755. <p>Depending on what kind of Port your Mac has, you need the following cables and adapters:</p>
  756. <table>
  757. <thead>
  758. <tr>
  759. <th> Port on Mac        </th>
  760. <th> Cables </th>
  761. <th> Images </th>
  762. </tr>
  763. </thead>
  764. <tbody>
  765. <tr>
  766. <td> FireWire&nbsp;400<br />(1999-) </td>
  767. <td> 4-pin (DV) to 6-pin (FW 400) </td>
  768. <td> <img src="docs/digitizing_vhs/4-6.jpg" height="75" width="150" alt="" /> </td>
  769. </tr>
  770. <tr>
  771. <td> FireWire&nbsp;800<br />(2009-) </td>
  772. <td> 4-pin (DV) to 9-pin (FW 800) </td>
  773. <td> <img src="docs/digitizing_vhs/4-9.jpg" height="75" width="150" alt="" /> </td>
  774. </tr>
  775. <tr>
  776. <td> Thunderbolt&nbsp;1/2<br />(2011-) </td>
  777. <td> 4-pin (DV) to 9-pin (FW 800)<br /><a href="https://www.apple.com/shop/product/MD464LL/A/apple-thunderbolt-to-firewire-adapter">Thunderbolt to FireWire adapter</a> </td>
  778. <td> <img src="docs/digitizing_vhs/4-9.jpg" height="75" width="150" alt="" /><br /><img src="docs/digitizing_vhs/fw_tb.jpg" height="52" width="256" alt="" /></td>
  779. </tr>
  780. <tr>
  781. <td> Thunderbolt&nbsp;3/4<br />(2016-) </td>
  782. <td> 4-pin (DV) to 9-pin (FW 800)<br /><a href="https://www.apple.com/shop/product/MD464LL/A/apple-thunderbolt-to-firewire-adapter">Thunderbolt to FireWire adapter</a><br /><a href="https://www.apple.com/shop/product/MMEL2AM/A/thunderbolt-3-usb-c-to-thunderbolt-2-adapter">Thunderbolt 3 (USB-C) to Thunderbolt 2 Adapter</a> </td>
  783. <td> <img src="docs/digitizing_vhs/4-9.jpg" height="75" width="150" alt="" /><br /><img src="docs/digitizing_vhs/fw_tb.jpg" height="52" width="256" alt="" /><br /><img src="docs/digitizing_vhs/tb_tb.jpg" height="52" width="256" alt="" /> </td>
  784. </tr>
  785. </tbody>
  786. </table>
  787. <p>The older the Mac, the fewer adapters you will need, but the more hassle it will be installing the necessary software. The sweet spot seem to be late FireWire 800 Macs (model years 2011-2012) or Macs with Thunderbolt 1/2 (model years 2011-2015).</p>
  788. <h2 id="installing-tools">Installing Tools</h2>
  789. <p>macOS supports DV video over FireWire natively, so you can open QuickTime Player, and select &ldquo;File -> New Movie Recording&hellip;&rdquo; to preview what is being transmitted by the camcorder. While QuickTime Player can record, the resulting video will already be deinterlaced using the low-quality &ldquo;blend&rdquo; method.</p>
  790. <p>Apple&rsquo;s iMovie has a dedicated DV/FireWire import function that will save MOV-encapsulated DV video, but has the habit of silently stopping recording when there is an empty area of the source tape.</p>
  791. <p>The open source <code>ffmpeg</code> tool not only allows grabbing the original DV bits from FireWire, it can also convert video in all kinds of formats.</p>
  792. <p>If you are running macOS 11 (Big Sur) or later, install <a href="https://brew.sh">Homebrew</a>, and then install <code>ffmpeg</code> with this Terminal command:</p>
  793. <pre><code>brew install ffmpeg
  794. </code></pre>
  795. <p>Homebrew should also work but is <a href="https://docs.brew.sh/Installation#2">unsupported</a> on 10.11 (El Capitan) through 10.15 (Catalina). If Homebrew does not work on your version of macOS, you may want to find an <a href="https://duckduckgo.com/?q=static+ffmpeg+mac">ffmpeg binary through other means</a> or consider upgrading to a later version of macOS &#8211; maybe even through <a href="https://dortania.github.io/OpenCore-Legacy-Patcher/">OpenCore Legacy Patcher</a>.</p>
  796. <h2 id="digitizing">Digitizing</h2>
  797. <p>Make sure the camcorder&rsquo;s audio is configured to 48 KHz 16 bit mode (as opposed to 32 KHz 12 bit). Connect the camera to the Mac and switch it into &ldquo;PLAY&rdquo; or &ldquo;DAC&rdquo; mode. Then run the following Terminal command to list the available capture devices:</p>
  798. <pre><code>ffmpeg -f avfoundation -list_devices true -i ""
  799. </code></pre>
  800. <p>On my MacBook Pro, this prints:</p>
  801. <pre><code>AVFoundation video devices:  
  802. [0] DCR-TRV520E  
  803. [1] FaceTime HD Camera  
  804. [2] Capture screen 0  
  805. AVFoundation audio devices:  
  806. [0] Speaker Audio Recorder  
  807. [1] MacBook Pro Microphone
  808. </code></pre>
  809. <p>It detected the Sony DCR-TRV520E. We will have to pass this device name whenever we want to read DV data with ffmpeg.</p>
  810. <p>To capture the video tape into a DV stream, enter the following command (replacing the device name) and press PLAY on the VCR immediately after.</p>
  811. <pre><code>ffmpeg -f avfoundation -capture_raw_data true -i "DCR-TRV520E" -c copy -map 0 -f rawvideo video.dv
  812. </code></pre>
  813. <p>Once the tape is finished, press Ctrl+C to stop recording. If you want to automatically stop the recording after a certain time, you can add something like <code>-t 4:10:00</code> (4h 10m) to the command line.</p>
  814. <p>You can monitor the progress by looking at the camcorder&rsquo;s viewfinder or LCD, or by using this command in a different Terminal window:</p>
  815. <pre><code>tail -f video.dv | ffplay -i -
  816. </code></pre>
  817. <h2 id="compressing">Compressing</h2>
  818. <p>While DV is an excellent archival format, it&rsquo;s also big: about 11 GB per hour. MPEG-2 can slash this by a factor of three (4.5 GB per hour). The following line recompresses the DV into DVD-quality MPEG-2, leaving the interlacing intact.</p>
  819. <pre><code>ffmpeg -i video.dv -b:v 10M -flags +ildct+ilme video.vob
  820. </code></pre>
  821. <p>Here is the corresponding line to encode the video in H.264 at 3 MBit/sec, which is about the same quality, and also with interlacing intact. This will be a little more than 1 GB per hour – 10 times smaller than DV.</p>
  822. <pre><code>ffmpeg -i video.dv -b:v 3M -flags +ildct+ilme video.mp4
  823. </code></pre>
  824. <p>While H.265 has some basic support for interlaced video, neither ffmpeg nor VLC support it without tricks, and AV1 does not support interlaced video at all. Consequently, H.264 is effectively the latest compression format that you should use to store interlaced video.</p>
  825. <h2 id="on-the-fly-compression">On-the-fly Compression</h2>
  826. <p>Any Intel or Apple Silicon Mac can also encode MPEG-2 in real-time. The following line skips the intermediate DV file:</p>
  827. <pre><code>ffmpeg -f avfoundation -capture_raw_data true -i "DCR-TRV520E" -c copy -map 0 -f rawvideo pipe:1 | ffmpeg -i - -b:v 10M -flags +ildct+ilme video.vob
  828. </code></pre>
  829. <p>And this is the line to encode straight into H.264. You will need a 2015 or newer Mac for this, otherwise it won&rsquo;t be able to keep up with the incoming data.</p>
  830. <pre><code>ffmpeg -f avfoundation -capture_raw_data true -i "DCR-TRV520E" -c copy -map 0 -f rawvideo pipe:1 | ffmpeg -i - -b:v 3M -flags +ildct+ilme video.mp4
  831. </code></pre>
  832. <h2 id="deinterlacing">Deinterlacing</h2>
  833. <p>If you need the video in progressive format, you can use the following commands to deinterlace the video. You should try the 50/60 Hz command first, which will retain motion smoothness. If the resulting video contains every frame twice (verify by single stepping with the arrow keys in QuickTime), the original material was 25/30 Hz, so you can to re-do the deinterlacing with a frame rate of 25/30.</p>
  834. <p>MPEG-2, 10 MBit, 50/60 frames per second:</p>
  835. <pre><code>ffmpeg -i video.dv -vf yadif=1:-1:0 -b:v 10M video.vob
  836. </code></pre>
  837. <p>MPEG-2, 10 MBit, 25/30 frames per second:</p>
  838. <pre><code>ffmpeg -i video.dv -vf yadif=0:-1:0 -b:v 10M video.vob
  839. </code></pre>
  840. <p>H.264, 3 MBit, 50/60 frames per second:</p>
  841. <pre><code>ffmpeg -i video.dv -vf yadif=1:-1:0 -b:v 3M video.mp4
  842. </code></pre>
  843. <p>H.264, 3 MBit, 25/30 frames per second:</p>
  844. <pre><code>ffmpeg -i video.dv -vf yadif=0:-1:0 -b:v 3M video.mp4
  845. </code></pre>
  846. <p>H.265, 1.5 MBit, 50/60 frames per second:</p>
  847. <pre><code>ffmpeg -i video.dv -vf yadif=1:-1:0 -c:v libx265 -b:v 1.5M -tag:v hvc1 video.mp4
  848. </code></pre>
  849. <p>H.265, 1.5 MBit, 25/30 frames per second:</p>
  850. <pre><code>ffmpeg -i video.dv -vf yadif=0:-1:0 -c:v libx265 -b:v 1.5M -tag:v hvc1 video.mp4
  851. </code></pre>
  852. <p>While the YADIF filter in ffmpeg does a pretty good job, there are now also machine-learning based tool like <a href="https://www.topazlabs.com/topaz-video-ai">Topaz Video AI</a> for deinterlacing.</p>
  853. <p>Remember though that you should archive the original interlaced data, since deinterlacing is lossy.</p>
  854. <h2 id="limitations">Limitations</h2>
  855. <p>This method only captures the 720&#215;576 (720&#215;480) video signal and one stereo audio track of your media. Depending on the media, there may be information that is not captured:</p>
  856. <ul>
  857. <li>PAL broadcast recordings usually contain <a href="https://en.wikipedia.org/wiki/Teletext">teletext</a>, albeit with lots of errors, because of the insufficient bandwidth of tape.</li>
  858. <li>NTSC broadcast recordings and pre-recorded media usually contain <a href="https://en.wikipedia.org/wiki/Closed_captioning">closed captioning</a>.</li>
  859. <li>VHS contains a mono track and an optional HiFi stereo track. VHS players pick the HiFi stereo track if it exists, so this solution will not capture the mono track which, in theory, could contain entirely different audio.</li>
  860. <li>LaserDisc can contain digital audio (PCM, Dolby Digital or DTS). If you want to capture the audio losslessly, you need to record it in parallel using an S/PDIF connection.</li>
  861. </ul>
  862. <p>In general, the recording will only be as good as the player can decode the media. If you don&rsquo;t have access to a good player or if the media has defects, you may want to talk to a company that specializes in digitization services.</p>
  863. <h2 id="simpler-solutions">Simpler Solutions</h2>
  864. <p>If the solution described in this setup seems overkill, there are simpler solutions as well:</p>
  865. <ul>
  866. <li>
  867. <p>A DVD recorder (or a VHS/DVD combo device like the Panasonic DMR-EX98V and DMR-EX99V) can convert analog sources (or VHS directly) into high-quality interlaced MPEG-2 files written to a recordable DVD. The downside is that these devices are usually limited to two (DVD-5), maybe four (DVD-9, i.e. dual-layer) hours of recording at 10 MBit/sec because of the limitied capacity of a DVD.</p>
  868. </li>
  869. <li>
  870. <p>There are devices that connect to the analog signal on one side and to a computer&rsquo;s USB port on one side. The PC/Mac software will usually create a deinterlaced MP4 file.</p>
  871. </li>
  872. <li>
  873. <p><a href="https://www.youtube.com/watch?v=ZC5Zr3NC2PY">Technology Connections</a> describes a solution where you connect the video player to an analog-to-HDMI box, and its output in turn to a device that records HDMI onto an SD card. This will also deinterlace the video.</p>
  874. </li>
  875. </ul>
  876. <h2 id="links">Links</h2>
  877. <ul>
  878. <li><a href="https://forums.macrumors.com/threads/updated-old-video-tapes-conversion-via-thunderbolt-firewire-for-camcorders-video-decks-and-players-that-supported-ieee-1394-firewire-ilink.2321691/">Old Video Tapes Conversion via Thunderbolt / Firewire </a></li>
  879. <li><a href="https://leolabs.org/blog/capture-minidv-on-macos">Capturing and Archiving MiniDV Tapes on macOS</a></li>
  880. <li><a href="https://mipops.github.io/dvrescue/index.html">DVRescue</a></li>
  881. <li><a href="https://www.researchgate.net/publication/224078937_HDTV_subjective_quality_of_H264_vs_MPEG-2_with_and_without_packet_loss">HDTV subjective quality of H.264 vs. MPEG-2, with and without packet loss</a></li>
  882. </ul>
  883. <hr />
  884. <div class="footnotes">
  885. <hr/>
  886. <ol>
  887. <li id="fn:1">
  888. <p>NTSC DV uses 4:1:1 chroma subsampling instead of PAL&rsquo;s 4:2:2, so it has a Chroma resolution of 130&#215;480. This still surpasses all consumer formats – except DVD.<a href="#fnref:1" rev="footnote">&#8617;</a></p>
  889. </li>
  890. <li id="fn:1">
  891. <p>Always use an S-Video connection if the player supports it! If the connector exists, it means the media supports a higher-than-composite color bandwidth, which is only available through S-Video.<a href="#fnref:1" rev="footnote">&#8617;</a></p>
  892. </li>
  893. </ol>
  894. </div>
  895. ]]></content:encoded>
  896. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1697</wfw:commentRss>
  897. <slash:comments>8</slash:comments>
  898. </item>
  899. <item>
  900. <title>Dissecting a Dummy Promo MiniDisc</title>
  901. <link>https://www.pagetable.com/?p=1693</link>
  902. <comments>https://www.pagetable.com/?p=1693#comments</comments>
  903. <pubDate>Thu, 17 Nov 2022 23:08:35 +0000</pubDate>
  904. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  905. <category><![CDATA[Archeology]]></category>
  906. <category><![CDATA[Teardown]]></category>
  907.  
  908. <guid isPermaLink="false">https://www.pagetable.com/?p=1693</guid>
  909. <description><![CDATA[Many pre-recorded MiniDiscs are rare and expensive. An extra rare special case is the dummy promo copy of Michael Jackson&#8217;s &#8220;Dangerous&#8221;, which we will dissect in this article. Just looking at the case, the dummy promo copy looks like the regular retail MiniDisc: Instead of the inner pages with the song lyrics, the booklet only ... <a title="Dissecting a Dummy Promo MiniDisc" class="read-more" href="https://www.pagetable.com/?p=1693">Read more<span class="screen-reader-text">Dissecting a Dummy Promo MiniDisc</span></a>]]></description>
  910. <content:encoded><![CDATA[<p>Many pre-recorded MiniDiscs are rare and expensive. An extra rare special case is the <em>dummy</em> promo copy of Michael Jackson&rsquo;s &ldquo;Dangerous&rdquo;, which we will dissect in this article.</p>
  911. <p><img src="docs/promo_md/dummy.webp" height="379" width="292" alt="" /></p>
  912. <p>Just looking at the case, the dummy promo copy looks like the regular retail MiniDisc:</p>
  913. <p><img src="docs/promo_md/case_front.webp" height="410" width="284" alt="" /><img src="docs/promo_md/case_back.webp" height="410" width="427" alt="" /></p>
  914. <p>Instead of the inner pages with the song lyrics, the booklet only contains a sheet with the MiniDisc logo:</p>
  915. <p><img src="docs/promo_md/booklet_inner.webp" height="410" width="545" alt="" /></p>
  916. <p>The front of the MiniDisc looks inconspicuous:</p>
  917. <p><img src="docs/promo_md/md_front.webp" height="340" width="360" alt="" /></p>
  918. <p>But instead of the track listing, the back only contains a sticker saying &ldquo;NOT PLAYABLE&rdquo;:</p>
  919. <p><img src="docs/promo_md/md_back.webp" height="345" width="360" alt="" /></p>
  920. <p>If you look closely, you can see that the actual disc behind the shutter has &ldquo;DUMMY&rdquo; etched into it. The full text is &ldquo;DUMMY-CDF01-1/100&rdquo;.</p>
  921. <p>When trying to play the MiniDisc, any player will complain that it cannot read the TOC (table of contents). But a closer look reveals that the data area is not in fact empty:</p>
  922. <p><img src="docs/promo_md/surface_dummy.webp" height="363" width="315" alt="" /></p>
  923. <p>Like audio CDs, MiniDiscs are played from the inside to the outside. The innermost area (outside of the readable text) looks uniform, which usually indicates an empty area. Then, there are three areas of what looks like random data, with smaller regular stripes in between – this reminds of tracks on a vinyl record. For comparison, here is a proper pre-recorded MiniDisc:</p>
  924. <p><img src="docs/promo_md/surface_regular.webp" height="363" width="377" alt="" /></p>
  925. <p>The empty area is at the end of the MiniDisc, that is, on the outside. The data is in one large area – individual tracks are in fact not visible.</p>
  926. <p>The random areas do look like they contain some data, so we have to try to read it! But how do you play a MiniDisc without a valid TOC? By hot-swapping a good MiniDisc with this one! I used a toothpick on the door detection switch of a SHARP MD-MT15 so I could open the door without the player noticing, as described <a href="https://www.pagetable.com/?p=1384">in my article about dumping MiniDiscs</a>:</p>
  927. <p><img src="docs/dumping_md/recover1.jpg" height="330" width="440" alt="" /><img src="docs/dumping_md/recover2.jpg" height="330" width="440" alt="" /></p>
  928. <p>No matter which of the 11 tracks (i.e. offsets) of the good MiniDisc I picked, the player always shows a READ ERROR after a few seconds. It seems the &ldquo;data&rdquo; area does not in fact contain any correctly formatted data sectors.</p>
  929. <p><img src="docs/promo_md/read_error.webp" height="350" width="446" alt="" /></p>
  930. <p>Yes, it&rsquo;s disappointing, but I chose to publish this anyway to counter <a href="https://en.wikipedia.org/wiki/Publication_bias">publication bias</a>, and to maybe motivate someone else to come up with a different method to analyze what&rsquo;s on these dummy MiniDiscs!</p>
  931. <p>Here&rsquo;s a question though: What was the point of these dummy promo MiniDiscs? What were they used for?</p>
  932. <h2 id="references">References</h2>
  933. <ul>
  934. <li><a href="https://www.laserdiscplaza.fr/forumld/viewtopic.php?t=6415">Toutes les versions de &ldquo;Dangerous&rdquo; en MD</a> by macaddict77, 2020-08-12</li>
  935. <li><a href="https://www.discogs.com/release/15823037-Michael-Jackson-Dangerous">Discogs: Dangerous (Promo, Not Playable &#8211; Mock-Up)</a></li>
  936. <li><a href="https://www.discogs.com/release/12752332-Michael-Jackson-Dangerous">Discogs: Dangerous (Promo)</a></li>
  937. </ul>
  938. ]]></content:encoded>
  939. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1693</wfw:commentRss>
  940. <slash:comments>8</slash:comments>
  941. </item>
  942. <item>
  943. <title>The Commodore VICMODEM (Model 1600)</title>
  944. <link>https://www.pagetable.com/?p=1679</link>
  945. <comments>https://www.pagetable.com/?p=1679#comments</comments>
  946. <pubDate>Mon, 27 Jun 2022 15:25:28 +0000</pubDate>
  947. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  948. <category><![CDATA[Archeology]]></category>
  949. <category><![CDATA[C64]]></category>
  950. <category><![CDATA[Commodore]]></category>
  951. <category><![CDATA[Literature]]></category>
  952. <category><![CDATA[Modem]]></category>
  953. <category><![CDATA[VIC-20]]></category>
  954.  
  955. <guid isPermaLink="false">https://www.pagetable.com/?p=1679</guid>
  956. <description><![CDATA[The Commodore 1600, also known as the “VICMODEM”, is Commodore’s very first modem (1982): It supports 300 baud duplex connections, and is connected to an existing telephone&#8217;s handset connector instead of the phone line. This kept the price down, but required the user to dial manually through the phone. Historical Context Year Name Model Description ... <a title="The Commodore VICMODEM (Model 1600)" class="read-more" href="https://www.pagetable.com/?p=1679">Read more<span class="screen-reader-text">The Commodore VICMODEM (Model 1600)</span></a>]]></description>
  957. <content:encoded><![CDATA[<p>The Commodore 1600, also known as the “VICMODEM”, is Commodore’s very first modem (1982): It supports 300 baud duplex connections, and is connected to an existing telephone&rsquo;s handset connector instead of the phone line. This kept the price down, but required the user to dial manually through the phone.</p>
  958. <h2 id="historical-context">Historical Context</h2>
  959. <table>
  960. <thead>
  961. <tr>
  962. <th> Year </th>
  963. <th> Name </th>
  964. <th> Model </th>
  965. <th> Description </th>
  966. </tr>
  967. </thead>
  968. <tbody>
  969. <tr>
  970. <td> 1982 </td>
  971. <td> <a href="https://www.pagetable.com/?p=1679">VICMODEM</a> </td>
  972. <td> 1600 </td>
  973. <td> connected to phone&rsquo;s handset connector; manual dialing through phone; Motorola MC14412 </td>
  974. </tr>
  975. <tr>
  976. <td> 1982 </td>
  977. <td> <a href="https://www.pagetable.com/?p=1716">AUTOMODEM</a> </td>
  978. <td> 1650 </td>
  979. <td> connected to phone line, pulse dialing in software; Motorola MC14412 </td>
  980. </tr>
  981. <tr>
  982. <td> 1985 </td>
  983. <td> <a href="https://www.pagetable.com/?p=1644">MODEM/300</a> </td>
  984. <td> 1660 </td>
  985. <td>added tone dialing support by feeding SID output into modem; Texas Instruments TMS99532A </td>
  986. </tr>
  987. <tr>
  988. <td> 1987 </td>
  989. <td> <a href="https://www.pagetable.com/?p=1647">MODEM/1200</a> </td>
  990. <td> 1670 </td>
  991. <td> Hayes command set; pulse and tone dialing in hardware; 300/1200 baud support; U.S. Robotics chipset </td>
  992. </tr>
  993. </tbody>
  994. </table>
  995. <h2 id="photos">Photos</h2>
  996. <p><a href="docs/cbm1600modem/cbm1600_1.jpg"><img src="docs/cbm1600modem/cbm1600_1.jpg" height="383" width="459" alt="" /></a></p>
  997. <p>On the back, there is the RJ11C handset connector. On the side, there is an LED indicating that indicates when the phone is transmitting or receiving, and a switch that can be put into the &ldquo;A&rdquo; (answer) or &ldquo;O&rdquo; (originate) position, depending whether a phone call is supposed to be made or accepted.</p>
  998. <p><a href="docs/cbm1600modem/cbm1600_2.jpg"><img src="docs/cbm1600modem/cbm1600_2.jpg" height="345" width="459" alt="" /></a></p>
  999. <p>On the front, there is the VIC-20/C64 user port connector.</p>
  1000. <p><a href="docs/cbm1600modem/front.jpg"><img src="docs/cbm1600modem/front.jpg" height="480" width="270" alt="" /></a><a href="docs/cbm1600modem/back.jpg"><img src="docs/cbm1600modem/back.jpg" height="468" width="250" alt="" /></a></p>
  1001. <p>The label on the bottom says:</p>
  1002. <blockquote>
  1003. <p>FCC ID: B4V8N2VIC 20<br />
  1004. Commodore Business Machines, Inc.<br />
  1005. Made in USA</p>
  1006. <p>Certified to comply with the limits for a Class B<br />
  1007. computing device pursuant to Subpart J of<br />
  1008. Part 15 of FCC Rules. See instructions if<br />
  1009. interference to radio reception is suspected.</p>
  1010. <p>COMPLIES WITH PART 68, FCC RULES FCC<br />
  1011. REGISTRATION NUMBER B4V8N2-68331-<br />
  1012. KX-N RINGER EQUIVALENCE O.0B JACK<br />
  1013. (USOC) N.A. (KX)</p>
  1014. <p>Model 1600<br />
  1015. Serial Number: 093333</p>
  1016. </blockquote>
  1017. <p><a href="docs/cbm1600modem/board1.jpg"><img src="docs/cbm1600modem/board1.jpg" height="472" width="263" alt="" /></a><a href="docs/cbm1600modem/board2.jpg"><img src="docs/cbm1600modem/board2.jpg" height="465" width="262" alt="" /></a></p>
  1018. <p>The only text on the board is &ldquo;PWB 00201 B&rdquo; on the back. The core component is the Motorola <a href="docs/cbm1600modem/MC14412.pdf">MC14412</a> modem chip (bottom, second from the left).</p>
  1019. <p>The RJ11 connector on the back must be connected to an existing phone, instead of its handset, like this:</p>
  1020. <p><img src="docs/cbm1600modem/connection1.png" height="179" width="474" alt="" /></p>
  1021. <p>The VICMODEM does not talk on the phone line level, but on the handset level, this reusing the hardware in the existing phone. This allowed the modem to be produced for under $33, so it could be the first modem to be sold for under $100<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>.</p>
  1022. <p>The flip side of this design was that the modem could not dial or answer by itself. To dial a number, it had to be keyed into the telephone with the handset connected, and once one would hear that the remote side picked up, switch the cable to the modem. To wait for a call and answer it, the modem has to be connected and the (unconnected) handset has to be in the cradle. Once it rings, the handset has to be picked up and set aside.</p>
  1023. <p><img src="docs/cbm1600modem/connection2.png" height="243" width="389" alt="" /></p>
  1024. <p>This is exactly how one would operate an acoustic coupler. After all, this modem is more of an acoustic coupler with a direct audio connection than a full modem.</p>
  1025. <h2 id="box">Box</h2>
  1026. <p><a href="docs/cbm1600modem/box_top.jpg"><img src="docs/cbm1600modem/box_top.jpg" height="57" width="257" alt="" /></a><br />
  1027. <a href="docs/cbm1600modem/box_front.jpg"><img src="docs/cbm1600modem/box_front.jpg" height="357" width="257" alt="" /></a><a href="docs/cbm1600modem/box_back.jpg"><img src="docs/cbm1600modem/box_back.jpg" height="357" width="257" alt="" /></a> <a href="docs/cbm1600modem/box_side.jpg"><img src="docs/cbm1600modem/box_side.jpg" height="357" width="59" alt="" /></a></p>
  1028. <p>In late 1983, the modem was already sold for under $70.</p>
  1029. <h2 id="manual">Manual</h2>
  1030. <p><a href="docs/cbm1600modem/cbm1600modem_manual.pdf"><img src="docs/cbm1600modem/cbm1600modem_manual.jpg" height="261" width="189" alt="" /></a></p>
  1031. <h2 id="more-box-contents">More Box Contents</h2>
  1032. <p><a href="docs/cbm1600modem/warranty_card.pdf"><img src="docs/cbm1600modem/warranty_card.png" height="262" width="131" alt="" /></a> <a href="docs/cbm1600modem/services.pdf"><img src="docs/cbm1600modem/services.png" height="260" width="172" alt="" /></a><br />
  1033. <a href="docs/cbm1600modem/dowjones1.pdf"><img src="docs/cbm1600modem/dowjones1.jpg" height="176" width="77" alt="" /></a> <a href="docs/cbm1600modem/dowjones2.pdf"><img src="docs/cbm1600modem/dowjones2.jpg" height="208" width="159" alt="" /></a> <a href="docs/cbm1600modem/dowjones_envelope.pdf"><img src="docs/cbm1600modem/dowjones_envelope.png" height="95" width="217" alt="" /></a><br />
  1034. <a href="docs/cbm1600modem/compuserve1.pdf"><img src="docs/cbm1600modem/compuserve1.png" height="187" width="144" alt="" /></a> <a href="docs/cbm1600modem/compuserve2.pdf"><img src="docs/cbm1600modem/compuserve2.png" height="187" width="144" alt="" /></a> <a href="docs/cbm1600modem/compuserve3.pdf"><img src="docs/cbm1600modem/compuserve3.png" height="97" width="147" alt="" /></a></p>
  1035. <p>The CompuServe signup form came with the user name and the password pre-filled. We would have been user 75155,731 with a password of <code>FRILL:DOUBLET</code>.</p>
  1036. <h2 id="tape">Tape</h2>
  1037. <p><a href="docs/cbm1600modem/tape1.jpg"><img src="docs/cbm1600modem/tape1.jpg" height="122" width="190" alt="" /></a> <a href="docs/cbm1600modem/tape2.jpg"><img src="docs/cbm1600modem/tape2.jpg" height="122" width="190" alt="" /></a></p>
  1038. <p><a href="docs/cbm1600modem/64%20term.prg">64 TERM.PRG</a> (2577 bytes)</p>
  1039. <hr />
  1040. <div class="footnotes">
  1041. <hr/>
  1042. <ol>
  1043. <li id="fn:1">
  1044. <p><a href="https://talesfromthecollection.com/2021/10/19/michael-tomczyk-commodore/">Michael Tomczyk: Commodore VIC-20 Developer, Computer Pioneer</a><a href="#fnref:1" rev="footnote">&#8617;</a></p>
  1045. </li>
  1046. </ol>
  1047. </div>
  1048. ]]></content:encoded>
  1049. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1679</wfw:commentRss>
  1050. <slash:comments>5</slash:comments>
  1051. </item>
  1052. <item>
  1053. <title>PostScript Cartridge Plus for HP LaserJet III</title>
  1054. <link>https://www.pagetable.com/?p=1673</link>
  1055. <comments>https://www.pagetable.com/?p=1673#comments</comments>
  1056. <pubDate>Tue, 21 Jun 2022 06:31:49 +0000</pubDate>
  1057. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  1058. <category><![CDATA[Archeology]]></category>
  1059. <category><![CDATA[PostScript]]></category>
  1060.  
  1061. <guid isPermaLink="false">https://www.pagetable.com/?p=1673</guid>
  1062. <description><![CDATA[The HP LaserJet III laser printer from 1990 used the &#8220;Printer Command Language&#8221; PCL 5 by default, but could be upgraded with the &#8220;HP PostScript Cartridge Plus&#8221; cartridge, which contained 2 MB of ROM with Adobe&#8217;s PostScript Level 2 rasterizer. Let&#8217;s look at the ROM contents and some of its hidden gems. Cartridge The cartridge ... <a title="PostScript Cartridge Plus for HP LaserJet III" class="read-more" href="https://www.pagetable.com/?p=1673">Read more<span class="screen-reader-text">PostScript Cartridge Plus for HP LaserJet III</span></a>]]></description>
  1063. <content:encoded><![CDATA[<p>The HP LaserJet III laser printer from 1990 used the &ldquo;Printer Command Language&rdquo; PCL 5 by default, but could be upgraded with the &ldquo;HP PostScript Cartridge Plus&rdquo; cartridge, which contained 2 MB of ROM with Adobe&rsquo;s PostScript Level 2 rasterizer. Let&rsquo;s look at the ROM contents and some of its hidden gems.</p>
  1064. <p><a href="docs/postscript_cartridge_plus/postscript_cartridge_plus_case.jpg"><img src="docs/postscript_cartridge_plus/postscript_cartridge_plus_case.jpg" height="303" width="403" alt="" /></a></p>
  1065. <h2 id="cartridge">Cartridge</h2>
  1066. <p>The cartridge is about 9&#215;14 cm in size.</p>
  1067. <p><a href="docs/postscript_cartridge_plus/postscript_cartridge_plus_case_front.jpg"><img src="docs/postscript_cartridge_plus/postscript_cartridge_plus_case_front.jpg" height="380" width="269" alt="" /></a><a href="docs/postscript_cartridge_plus/postscript_cartridge_plus_case_back.jpg"><img src="docs/postscript_cartridge_plus/postscript_cartridge_plus_case_back.jpg" height="391" width="270" alt="" /></a></p>
  1068. <p><a href="docs/postscript_cartridge_plus/postscript_cartridge_plus_case_side.jpg"><img src="docs/postscript_cartridge_plus/postscript_cartridge_plus_case_side.jpg" height="52" width="270" alt="" /></a></p>
  1069. <p>The front says</p>
  1070. <blockquote>
  1071. <p>HEWLETT PACKARD</p>
  1072. <p>PostScript Cartridge Plus</p>
  1073. <p>ITC Avant Garde Gothic®<br />
  1074. ITC Bookman®<br />
  1075. Courier<br />
  1076. Helvetica®<br />
  1077. Helvetica-Narrow<br />
  1078. New Century Schoolbook<br />
  1079. Palatino®<br />
  1080. Times®<br />
  1081. ITC Zapf Chancery®<br />
  1082. TIC Zapf Dingbats®<br />
  1083. Symbol<br />
  1084. C2089A ©Hewlett-Packard 1989, 1990, 1991</p>
  1085. <p>HP<br />
  1086. LASERJET III<br />
  1087. POSTSCRIPT®</p>
  1088. </blockquote>
  1089. <p>The back says</p>
  1090. <blockquote>
  1091. <p>Adobe and PostScript are registered trademark of Adobe Systems<br />
  1092. Incorporated in the U.S, and other countries. Helvetica, Palatino and<br />
  1093. Times Roman are registered trademarks of Linotype AG and/<br />
  1094. or its subsidiaries in the U.S. and other countries. IT Avant Garde<br />
  1095. Gothic, ITC Bookman, ITC Zapf Chancery and ITC Zapf Dingbats are<br />
  1096. registered trademarks of International Typeface Corporation in the<br />
  1097. U.S, and other countries.</p>
  1098. </blockquote>
  1099. <h2 id="board">Board</h2>
  1100. <p><a href="docs/postscript_cartridge_plus/postscript_cartridge_plus_board_front.jpg"><img src="docs/postscript_cartridge_plus/postscript_cartridge_plus_board_front.jpg" height="247" width="377" alt="" /></a><br />
  1101. <a href="docs/postscript_cartridge_plus/postscript_cartridge_plus_board_back.jpg"><img src="docs/postscript_cartridge_plus/postscript_cartridge_plus_board_back.jpg" height="257" width="381" alt="" /></a></p>
  1102. <p>Here is the front without the components:<br />
  1103. <a href="docs/postscript_cartridge_plus/postscript_cartridge_plus_board_front_empty.jpg"><img src="docs/postscript_cartridge_plus/postscript_cartridge_plus_board_front_empty.jpg" height="247" width="377" alt="" /></a></p>
  1104. <p>The board contains 6 74-series logic chips:</p>
  1105. <ul>
  1106. <li>1x SN74ALS139N: Dual 2-to-4 Decoder/Demultiplexer</li>
  1107. <li>4x SN74ALS244BN: Octal Buffer and Line Driver with 3-State Output</li>
  1108. <li>1x SN74LS32N: Quadruple 2-Input Positive-Or Gates [marked as HP part number 1820-1208]</li>
  1109. </ul>
  1110. <p>and four 512 KB mask ROM chips of the type Fujitsu MB834200B-15 (27C400 pinout). They are all marked with</p>
  1111. <blockquote>
  1112. <p>© 1991 HP-BOISE<br />
  1113. © 1984-90 ADOBE<br />
  1114. © 1981 LINOTYPE AG<br />
  1115. © 1991 FUJITSU</p>
  1116. </blockquote>
  1117. <h2 id="rom">ROM</h2>
  1118. <p>These are the verbatim dumps (adjacent bytes are swapped):</p>
  1119. <ul>
  1120. <li><a href="docs/postscript_cartridge_plus/1818-5336_16a.bin">1818-5336 / 16A AA</a>, MD5 258464faa19a1ff78bbb57270eec8835</li>
  1121. <li><a href="docs/postscript_cartridge_plus/1818-5318_03a.bin">1818-5318 / 03A AA</a>, MD5 da113b6c6c53e21858b30a71c7be017c</li>
  1122. <li><a href="docs/postscript_cartridge_plus/1818-5319_04a.bin">1818-5319 / 04A AA</a>, MD5 f6e368806aa8caf22b4c28d235a2df1d</li>
  1123. <li><a href="docs/postscript_cartridge_plus/1818-5320_05a.bin">1818-5320 / 05A AA</a>, MD5 ed1700895daeac733f80ce20278c4a64</li>
  1124. </ul>
  1125. <p>This is the combined (<a href="docs/postscript_cartridge_plus/byteswap.py">byte-swapped</a>) 2 MB ROM image:</p>
  1126. <p><a href="docs/postscript_cartridge_plus/c2089a.bin">HP PostScript Cartridge Plus C2089A ROM</a>, MD5 8a5d1f66ab1624e7188fc07154f4224d</p>
  1127. <p>The ROM image starts with a signature of &ldquo;SYST&rdquo; and the following messages at 0x30:</p>
  1128. <blockquote>
  1129. <p>V9H-18f PSCRIPT<br />
  1130. 09.H<br />
  1131. Copyright &copy; Hewlett-Packard Company, 1991. All rights reserved.</p>
  1132. </blockquote>
  1133. <p>The ROM contains Adobe&rsquo;s PostScript Level 2 rasterizer compiled for the 68000 CPU, the PostScript base fonts as well as some LaserJet-specific software (messages, errors and settings texts for the 15 char display in several languages).</p>
  1134. <h2 id="postscript-files">PostScript Files</h2>
  1135. <p>There is also some PostScript source code in the ROMs!</p>
  1136. <ul>
  1137. <li><a href="docs/postscript_cartridge_plus/ps/fontpage.ps">fontpage.ps</a></li>
  1138. <li><a href="docs/postscript_cartridge_plus/ps/test_page.ps">test_page.ps</a></li>
  1139. <li><a href="docs/postscript_cartridge_plus/ps/startup_page.ps">startup_page.ps</a></li>
  1140. </ul>
  1141. <p>(The missing <code>%!</code> file header has been added to the downloads.)</p>
  1142. <p>Since this is printer-specific PostScript code, it may not work with computer-based rasterizers, so let&rsquo;s go over them one by one.</p>
  1143. <h3 id="fontpage">FONTPAGE</h3>
  1144. <p>This is &ldquo;FONTPAGE&rdquo; converted to PDF using GPL GhostScript:</p>
  1145. <p><a href="docs/postscript_cartridge_plus/ps/fontpage.pdf">fontpage.pdf</a></p>
  1146. <p><img border="1" src="docs/postscript_cartridge_plus/ps/fontpage.png" width="319" height="413"/></p>
  1147. <p>The PostScript code contains the <code>product</code> operator, which returns the name of the printer, so the second line – &ldquo;GPL Ghostscript printer&rdquo; – would read &ldquo;HP LaserJet III printer&rdquo; on an actual LaserJet.</p>
  1148. <h3 id="test-page">TEST PAGE</h3>
  1149. <p>&ldquo;TEST PAGE&rdquo; prints various internal printer settings which are unsupported by computer-based PostScript rasterizers, so some lines had to be removed for the file to work. These are the files hacked for different rasterizers:</p>
  1150. <table>
  1151. <tr>
  1152. <td>
  1153. Adobe Acrobat Distiller 5.0 (2001)  </p>
  1154. <li><a href="docs/postscript_cartridge_plus/ps/test_page_acrobat.ps">test_page_acrobat.ps</a></li>
  1155. <li><a href="docs/postscript_cartridge_plus/ps/test_page_acrobat.pdf">test_page_acrobat.pdf</a></li>
  1156. <p><img border="1" src="docs/postscript_cartridge_plus/ps/test_page_acrobat.png" width="319" height="413"/>
  1157. </td>
  1158. <td>
  1159. macOS 12.4 PSNormalizer.framework  </p>
  1160. <li><a href="docs/postscript_cartridge_plus/ps/test_page_apple.ps">test_page_apple.ps</a></li>
  1161. <li><a href="docs/postscript_cartridge_plus/ps/test_page_apple.pdf">test_page_apple.pdf</a></li>
  1162. <p><img border="1" src="docs/postscript_cartridge_plus/ps/test_page_apple.png" width="319" height="413"/>
  1163. </td>
  1164. </tr>
  1165. <tr>
  1166. <td>
  1167. GhostScript 9.56.1  </p>
  1168. <li><a href="docs/postscript_cartridge_plus/ps/test_page_gs.ps">test_page_gs.ps</a></li>
  1169. <li><a href="docs/postscript_cartridge_plus/ps/test_page_gs.pdf">test_page_gs.pdf</a></li>
  1170. <p><img border="1" src="docs/postscript_cartridge_plus/ps/test_page_gs.png" width="319" height="413"/>
  1171. </td>
  1172. </tr>
  1173. </table>
  1174. <p>(The almost identical contents of Acrobat Distiller 5.0 and Apple&rsquo;s PS to PDF converter built into macOS (down to the internal version number!) is no coincidence: Apple&rsquo;s converter is in fact a licensed &ldquo;Adobe Normalizer 5.0&rdquo;<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>, the same engine powering Distiller 5.0.)</p>
  1175. <p>There is a second page which only prints if the <code>product</code> is &ldquo;HP LaserJet IIP&rdquo; or &ldquo;HP LaserJet IIIP&rdquo;:</p>
  1176. <ul>
  1177. <li><a href="docs/postscript_cartridge_plus/ps/test_cleaning.ps">test_cleaning.ps</a></li>
  1178. <li><a href="docs/postscript_cartridge_plus/ps/test_cleaning.pdf">test_cleaning.pdf</a></li>
  1179. </ul>
  1180. <p><img border="1" src="docs/postscript_cartridge_plus/ps/test_cleaning.png" width="319" height="413"/></p>
  1181. <h3 id="startup-page">STARTUP PAGE</h3>
  1182. <ul>
  1183. <li><a href="docs/postscript_cartridge_plus/ps/startup_page.ps">startup_page.ps</a></li>
  1184. <li><a href="docs/postscript_cartridge_plus/ps/startup_page.pdf">startup_page.pdf</a></li>
  1185. </ul>
  1186. <p><img border="1" src="docs/postscript_cartridge_plus/ps/startup_page.png" width="319" height="413"/></p>
  1187. <p>There is no such device as a &ldquo;LaserJet IIx&rdquo; – this is what the PostScript code falls back to if the <code>product</code> is none of these:</p>
  1188. <ul>
  1189. <li>&ldquo;HP LaserJet IID&rdquo;</li>
  1190. <li>&ldquo;HP LaserJet IIP&rdquo;</li>
  1191. <li>&ldquo;HP LaserJet III&rdquo;</li>
  1192. <li>&ldquo;HP LaserJet IIID&rdquo;</li>
  1193. <li>&ldquo;HP LaserJet IIIP&rdquo;</li>
  1194. </ul>
  1195. <p>Tests for these can be found across all PostScript code in the ROM. The IIID (&ldquo;duplex&rdquo;) and IIIP (&ldquo;personal&rdquo;) and variants of the LaserJet III. HP never offered PostScript for the LaserJet II series, so it is unknown why these product names show up in the ROM.</p>
  1196. <h2 id="future-work">Future Work</h2>
  1197. <p>There are several open questions that might be interesting:</p>
  1198. <ul>
  1199. <li>What&rsquo;s up with PostScript for the LaserJet II?</li>
  1200. <li>Did the cartridge extend the printer&rsquo;s internal ROM or replace it?</li>
  1201. <li>What other PostScript features are supported that are not official API?</li>
  1202. <li>What is the computer inside the LaserJet III like? Can we emulate it and run this rasterizer on a computer?</li>
  1203. <li>What is the pinout of the cartridge connector?</li>
  1204. </ul>
  1205. <div class="footnotes">
  1206. <hr/>
  1207. <ol>
  1208. <li id="fn:1">
  1209. <p><code>strings /System/Library/PrivateFrameworks/PSNormalizer.framework/Versions/A/Resources/PS.VM  | grep -i Adobe</code><a href="#fnref:1" rev="footnote">&#8617;</a></p>
  1210. </li>
  1211. </ol>
  1212. </div>
  1213. ]]></content:encoded>
  1214. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1673</wfw:commentRss>
  1215. <slash:comments>6</slash:comments>
  1216. </item>
  1217. <item>
  1218. <title>CCGMS Future 0.2</title>
  1219. <link>https://www.pagetable.com/?p=1665</link>
  1220. <comments>https://www.pagetable.com/?p=1665#comments</comments>
  1221. <pubDate>Sun, 20 Mar 2022 17:56:09 +0000</pubDate>
  1222. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  1223. <category><![CDATA[6502]]></category>
  1224. <category><![CDATA[C64]]></category>
  1225. <category><![CDATA[Commodore]]></category>
  1226. <category><![CDATA[GitHub]]></category>
  1227.  
  1228. <guid isPermaLink="false">https://www.pagetable.com/?p=1665</guid>
  1229. <description><![CDATA[CCGMS Future 0.2 was just released. It adds 80 columns support, a true ASCII charset (in 80c mode), and bug fixes. Release on CSDb Release on GitHub]]></description>
  1230. <content:encoded><![CDATA[<p>CCGMS Future 0.2 was just released. It adds 80 columns support, a true ASCII charset (in 80c mode), and bug fixes.</p>
  1231. <p><img src="docs/ccgms_80.png"/></p>
  1232. <ul>
  1233. <li><a href="https://csdb.dk/release/?id=215589">Release on CSDb</a></li>
  1234. <li><a href="https://github.com/mist64/ccgmsterm/releases/tag/v0.2">Release on GitHub</a></li>
  1235. </ul>
  1236. ]]></content:encoded>
  1237. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1665</wfw:commentRss>
  1238. <slash:comments>2</slash:comments>
  1239. </item>
  1240. <item>
  1241. <title>The Punter C1 Protocol</title>
  1242. <link>https://www.pagetable.com/?p=1663</link>
  1243. <comments>https://www.pagetable.com/?p=1663#comments</comments>
  1244. <pubDate>Tue, 15 Mar 2022 01:17:43 +0000</pubDate>
  1245. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  1246. <category><![CDATA[6502]]></category>
  1247. <category><![CDATA[Archeology]]></category>
  1248. <category><![CDATA[C64]]></category>
  1249. <category><![CDATA[Commodore]]></category>
  1250.  
  1251. <guid isPermaLink="false">https://www.pagetable.com/?p=1663</guid>
  1252. <description><![CDATA[The Punter file transfer protocol (&#8220;New Punter&#8221;/&#8220;Punter C1&#8221;) is an alternative to the XMODEM family of protocols, which was and still is very popular on BBSes for Commodore computers. It is notorious for being badly documented. Let&#8217;s fix that. I quickly developed quite a distaste for the badly designed punter protocol. Especially with the awful ... <a title="The Punter C1 Protocol" class="read-more" href="https://www.pagetable.com/?p=1663">Read more<span class="screen-reader-text">The Punter C1 Protocol</span></a>]]></description>
  1253. <content:encoded><![CDATA[<p>The Punter file transfer protocol (&ldquo;New Punter&rdquo;/&ldquo;Punter C1&rdquo;) is an alternative to the XMODEM family of protocols, which was and still is very popular on BBSes for Commodore computers. It is notorious for being badly documented. Let&rsquo;s fix that.</p>
  1254. <blockquote><p>I quickly developed quite a distaste for the badly designed punter protocol. Especially with the awful and rather smug “documentation”.</p>
  1255. <div style="float: right;">&mdash; MagerValp</div>
  1256. </blockquote>
  1257. <h2 id="the-protocol">The Protocol</h2>
  1258. <p>The protocol allows the transmission of one file from the &ldquo;sender&rdquo; to the &ldquo;receiver&rdquo;. No file name, but a 1-byte file type is transmitted (0=<code>PRG</code>, 1=<code>SEQ</code>).</p>
  1259. <p>For handshaking, the three-byte ASCII strings <code>GOO</code>, <code>BAD</code>, <code>ACK</code>, <code>S/B</code>, <code>SYN</code> are used.</p>
  1260. <p>The transmission of a file consists of two almost identical phases:</p>
  1261. <ul>
  1262. <li>Phase A transmits the file type, and consists of a lot of handshaking and overhead to transmit a single block with a single payload byte (the file type).</li>
  1263. <li>Phase B transmits the file data in one or more blocks. &ldquo;B2&rdquo; is repeated for each block.</li>
  1264. </ul>
  1265. <table>
  1266. <tr>
  1267. <td>
  1268. <h3 id="a-file-type">A File Type</h3>
  1269. </td>
  1270. <td>
  1271. <h3 id="b-file-contents">B File Contents</h3>
  1272. </td>
  1273. </tr>
  1274. <td>
  1275. <h4 id="a1-start">A1 Start</h4>
  1276. <table>
  1277. <thead>
  1278. <tr>
  1279. <th></th>
  1280. <th>Sender</th>
  1281. <th>Receiver</th>
  1282. </tr>
  1283. </thead>
  1284. <tbody>
  1285. <tr>
  1286. <td>1</td>
  1287. <td><code>GOO</code></td>
  1288. <td></td>
  1289. </tr>
  1290. <tr>
  1291. <td>2</td>
  1292. <td></td>
  1293. <td><code>GOO</code></td>
  1294. </tr>
  1295. </tbody>
  1296. </table>
  1297. </td>
  1298. <td>
  1299. <h4 id="b1-start">B1 Start</h4>
  1300. <table>
  1301. <thead>
  1302. <tr>
  1303. <th></th>
  1304. <th>Sender</th>
  1305. <th>Receiver</th>
  1306. </tr>
  1307. </thead>
  1308. <tbody>
  1309. <tr>
  1310. <td>17</td>
  1311. <td></td>
  1312. <td><code>GOO</code></td>
  1313. </tr>
  1314. </tbody>
  1315. </table>
  1316. </td>
  1317. </tr>
  1318. <td>
  1319. <h4 id="a2-block">A2 Block</h4>
  1320. <table>
  1321. <thead>
  1322. <tr>
  1323. <th></th>
  1324. <th>Sender</th>
  1325. <th>Receiver</th>
  1326. </tr>
  1327. </thead>
  1328. <tbody>
  1329. <tr>
  1330. <td>3</td>
  1331. <td><code>ACK</code></td>
  1332. <td></td>
  1333. </tr>
  1334. <tr>
  1335. <td>4</td>
  1336. <td></td>
  1337. <td><code>S/B</code></td>
  1338. </tr>
  1339. <tr>
  1340. <td>5</td>
  1341. <td>block data</td>
  1342. <td></td>
  1343. </tr>
  1344. <tr>
  1345. <td>6</td>
  1346. <td></td>
  1347. <td><code>GOO</code>*</td>
  1348. </tr>
  1349. </tbody>
  1350. </table>
  1351. </td>
  1352. <td>
  1353. <h4 id="b2-blocks-(repeated)">B2 Blocks<em>(repeated)</em></h4>
  1354. <table>
  1355. <thead>
  1356. <tr>
  1357. <th></th>
  1358. <th>Sender</th>
  1359. <th>Receiver</th>
  1360. </tr>
  1361. </thead>
  1362. <tbody>
  1363. <tr>
  1364. <td>18</td>
  1365. <td><code>ACK</code></td>
  1366. <td></td>
  1367. </tr>
  1368. <tr>
  1369. <td>19</td>
  1370. <td></td>
  1371. <td><code>S/B</code></td>
  1372. </tr>
  1373. <tr>
  1374. <td>20</td>
  1375. <td>block data</td>
  1376. <td></td>
  1377. </tr>
  1378. <tr>
  1379. <td>21</td>
  1380. <td></td>
  1381. <td><code>GOO</code>*</td>
  1382. </tr>
  1383. </tbody>
  1384. </table>
  1385. </td>
  1386. </tr>
  1387. <td>
  1388. <h4 id="a3-end-off">A3 End-Off</h4>
  1389. <table>
  1390. <thead>
  1391. <tr>
  1392. <th></th>
  1393. <th>Sender</th>
  1394. <th>Receiver</th>
  1395. </tr>
  1396. </thead>
  1397. <tbody>
  1398. <tr>
  1399. <td>7</td>
  1400. <td><code>ACK</code></td>
  1401. <td></td>
  1402. </tr>
  1403. <tr>
  1404. <td>8</td>
  1405. <td></td>
  1406. <td><code>S/B</code></td>
  1407. </tr>
  1408. <tr>
  1409. <td>9</td>
  1410. <td><code>SYN</code></td>
  1411. <td></td>
  1412. </tr>
  1413. <tr>
  1414. <td>10</td>
  1415. <td></td>
  1416. <td><code>SYN</code></td>
  1417. </tr>
  1418. <tr>
  1419. <td>11</td>
  1420. <td><code>S/B</code></td>
  1421. <td></td>
  1422. </tr>
  1423. <tr>
  1424. <td>12</td>
  1425. <td><em>(wait 1s)</em></td>
  1426. <td><em>(ignored)</em></td>
  1427. </tr>
  1428. <tr>
  1429. <td>13</td>
  1430. <td><code>S/B</code></td>
  1431. <td></td>
  1432. </tr>
  1433. <tr>
  1434. <td>14</td>
  1435. <td><em>(wait 1s)</em></td>
  1436. <td><em>(ignored)</em></td>
  1437. </tr>
  1438. <tr>
  1439. <td>15</td>
  1440. <td><code>S/B</code></td>
  1441. <td></td>
  1442. </tr>
  1443. <tr>
  1444. <td>16</td>
  1445. <td><em>(wait 1s)</em></td>
  1446. <td><em>(ignored)</em></td>
  1447. </tr>
  1448. </tbody>
  1449. </table>
  1450. </td>
  1451. <td>
  1452. <h4 id="b3-end-off">B3 End-Off</h4>
  1453. <table>
  1454. <thead>
  1455. <tr>
  1456. <th></th>
  1457. <th>Sender</th>
  1458. <th>Receiver</th>
  1459. </tr>
  1460. </thead>
  1461. <tbody>
  1462. <tr>
  1463. <td>22</td>
  1464. <td><code>ACK</code></td>
  1465. <td></td>
  1466. </tr>
  1467. <tr>
  1468. <td>23</td>
  1469. <td></td>
  1470. <td><code>S/B</code></td>
  1471. </tr>
  1472. <tr>
  1473. <td>24</td>
  1474. <td><code>SYN</code></td>
  1475. <td></td>
  1476. </tr>
  1477. <tr>
  1478. <td>25</td>
  1479. <td></td>
  1480. <td><code>SYN</code></td>
  1481. </tr>
  1482. <tr>
  1483. <td>26</td>
  1484. <td><code>S/B</code></td>
  1485. <td></td>
  1486. </tr>
  1487. <tr>
  1488. <td>27</td>
  1489. <td><em>(wait 1s)</em></td>
  1490. <td><em>(ignored)</em></td>
  1491. </tr>
  1492. <tr>
  1493. <td>28</td>
  1494. <td><code>S/B</code></td>
  1495. <td></td>
  1496. </tr>
  1497. <tr>
  1498. <td>29</td>
  1499. <td><em>(wait 1s)</em></td>
  1500. <td><em>(ignored)</em></td>
  1501. </tr>
  1502. <tr>
  1503. <td>30</td>
  1504. <td><code>S/B</code></td>
  1505. <td></td>
  1506. </tr>
  1507. <tr>
  1508. <td>31</td>
  1509. <td><em>(wait 1s)</em></td>
  1510. <td><em>(ignored)</em></td>
  1511. </tr>
  1512. </tbody>
  1513. </table>
  1514. </td>
  1515. </tr>
  1516. </table>
  1517. <p>*Step 6/21: After receiving the block data, if the checksum is incorrect, the receiver sends <code>BAD</code>, moving the protocol to step 3/18 with the same block.</p>
  1518. <h3 id="blocks">Blocks</h3>
  1519. <p>Every block has a 7 byte header:</p>
  1520. <table>
  1521. <thead>
  1522. <tr>
  1523. <th> offset </th>
  1524. <th> size </th>
  1525. <th> description          </th>
  1526. </tr>
  1527. </thead>
  1528. <tbody>
  1529. <tr>
  1530. <td> 0      </td>
  1531. <td> 2    </td>
  1532. <td> &ldquo;additive&rdquo; checksum  </td>
  1533. </tr>
  1534. <tr>
  1535. <td> 2      </td>
  1536. <td> 2    </td>
  1537. <td> &ldquo;cyclic&rdquo; checksum    </td>
  1538. </tr>
  1539. <tr>
  1540. <td> 4      </td>
  1541. <td> 1    </td>
  1542. <td> size of <em>next</em> block </td>
  1543. </tr>
  1544. <tr>
  1545. <td> 5      </td>
  1546. <td> 2    </td>
  1547. <td> block index          </td>
  1548. </tr>
  1549. </tbody>
  1550. </table>
  1551. <p>The two checksums are calculated over all bytes of the block, starting with index 4.</p>
  1552. <ul>
  1553. <li>The &ldquo;additive&rdquo; checksum is calculated by adding all bytes.</li>
  1554. <li>The &ldquo;cyclic&rdquo; checksum is calculated by XORing all bytes, rotating the 16 bit result left after each byte.</li>
  1555. </ul>
  1556. <p>Block sizes can be freely chosen and can vary from block to block. The maximum size is 255. This includes the 7 header bytes, so the payload is up to 248 bytes. Every block contains the size of the following block. The size of the first block is fixed (per phase). (For the last block in a sequence, this field is unused.)</p>
  1557. <p>The block index starts with 0. The last block (or only block) in a sequence has index -1 ($FFFF), though it is enough to only check the hi byte. This allows file sizes of almost 16 MB.</p>
  1558. <h3 id="file-type-transmission">File Type Transmission</h3>
  1559. <p>File type transmission only consists of a single 8 byte block. Its index is -1, since it is the last block. The single payload byte is the file type.</p>
  1560. <h3 id="file-data-transmission">File Data Transmission</h3>
  1561. <p>File data transmission starts with a 7 byte header block that contains no payload. Its purpose is to communicate the size of the first block that actually contains file data.</p>
  1562. <h2 id="bugs">Bugs</h2>
  1563. <h3 id="checksum">Checksum</h3>
  1564. <p>The two checksums are 32 bits total, yet CRC16 would probably provide better integrity guarantees.</p>
  1565. <h3 id="end-off-sequence">End-Off Sequence</h3>
  1566. <p>Steps 11-16 and 26-31 in the &ldquo;End-Off&rdquo; sequence in both phases are buggy. In the original implementation</p>
  1567. <ul>
  1568. <li>The sender repeats the following three times: transmit <code>S/B</code>, then keep reading a byte from the modem about 5000 times, discarding the data. This takes about one second on a C64.</li>
  1569. <li>The receiver expects just one <code>S/B</code>, then proceeds to the next step. The <code>GOO</code> sent in step 17 will be ignored by the sender.</li>
  1570. </ul>
  1571. <p>Because of the resends in the original implementation, the protocol can recover from this.</p>
  1572. <p>(Lines 5070-5120 in the <a href="https://groups.google.com/g/net.micro.cbm/c/NWEcf_15nkk/m/MKtq7fyetMYJ">original source</a> are responsible for this. The call to <code>accept</code> with an argument of 0 will continue reading bytes from the modem until it times out, because it can&rsquo;t match any of the codes. Maybe this was meant to match one or any specific code at some point.)</p>
  1573. <p>New implementations should expect three <code>S/B</code> and then wait for two seconds before continuing.</p>
  1574. <h2 id="multi-punter">Multi-Punter</h2>
  1575. <p>Multi-Punter is a very simple extension to the protocol that can transmit several files at a time and includes the file names and types. It has no error detection or re-send capability on a file level whatsoever.</p>
  1576. <p>Every file is transmitted like this:</p>
  1577. <ul>
  1578. <li>16× code 0x09 (TAB)</li>
  1579. <li><code>FILENAME,P</code> (<code>PRG</code>) or <code>FILENAME,S</code> (<code>SEQ</code>)</li>
  1580. <li>code 0x0D (CR)</li>
  1581. </ul>
  1582. <p>&hellip;followed by the transfer using the Punter protocol.</p>
  1583. <p>After the last file, the following is sent:</p>
  1584. <ul>
  1585. <li>16× code 0x09 (TAB)</li>
  1586. <li>16× code 0x04 (EOT)</li>
  1587. <li>code 0x0D (CR)</li>
  1588. </ul>
  1589. <h2 id="documentation">Documentation</h2>
  1590. <ul>
  1591. <li><a href="http://www.bbsdocumentary.com/software/COMMODORE/PET/PUNTERNET/punter_c1.txt">punter_c1.txt</a>: The original documentation by Steve Punter with additional comments by Geoffrey Welsh and Matthew Desmond</li>
  1592. <li><a href="https://csdb.dk/forums/?roomid=11&amp;topicid=109007&amp;showallposts=1">Thread on CSDb</a>, mostly by Oliver VieBrooks (Six), about reverse engineering the protocol</li>
  1593. </ul>
  1594. <h2 id="implementations">Implementations</h2>
  1595. <ul>
  1596. <li>The <a href="https://groups.google.com/g/net.micro.cbm/c/NWEcf_15nkk/m/MKtq7fyetMYJ">original 6502 source</a> by Steve Punter was posted to net.micro.cbm in 1985. Almost all software supporting the Punter protocol was based on this implementation.
  1597. <ul>
  1598. <li>The binary and a BASIC program to control it are #1797 and #1798 in the <a href="https://cbmfiles.com/genie/C64TelecomListing.php">GEnie Commodore File Library</a></li>
  1599. </ul>
  1600. </li>
  1601. <li><a href="https://github.com/mist64/ccgmsterm/blob/main/src/punter.s">CCGMS Future</a> contains a cleaned-up and heavily commented version of the original code.</li>
  1602. <li><a href="https://csdb.dk/release/?id=45545">C*Base</a> contains an independent 6502 implementation. (Source is available in the downloads.)</li>
  1603. <li>There is a C implementation as part of <a href="https://github.com/MagerValp/CGTerm/blob/master/punter.c">CGTerm</a> by Per Olofsson (MagerValp). It only supports downloading and interoperates with the C*Base implementation, but not the original implementation. There is a <a href="https://github.com/mist64/ccgmsterm/blob/main/test/punter.c">hacked version</a> in the CCGMS Future repo that works with the original code (and CCGMS).</li>
  1604. <li><a href="https://github.com/sixofdloc/CBMTerm3/blob/master/FileTransferProtocols/Protocols/Punter.cs">CBMTerm</a> by Oliver VieBrooks (Six) contains a C# implementation, including Multi-Punter.</li>
  1605. <li>There is also a Rust implementation called <a href="https://github.com/srwalter/punter-server">punter-server</a> by Steven Walter.</li>
  1606. </ul>
  1607. ]]></content:encoded>
  1608. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1663</wfw:commentRss>
  1609. <slash:comments>2</slash:comments>
  1610. </item>
  1611. <item>
  1612. <title>UP9600: How to Bit-Bang 9600 Baud RS-232 on the C64</title>
  1613. <link>https://www.pagetable.com/?p=1656</link>
  1614. <comments>https://www.pagetable.com/?p=1656#comments</comments>
  1615. <pubDate>Mon, 07 Mar 2022 18:11:14 +0000</pubDate>
  1616. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  1617. <category><![CDATA[C64]]></category>
  1618. <category><![CDATA[Commodore]]></category>
  1619. <category><![CDATA[Hacks]]></category>
  1620. <category><![CDATA[Hardware]]></category>
  1621. <category><![CDATA[Tricks]]></category>
  1622.  
  1623. <guid isPermaLink="false">https://www.pagetable.com/?p=1656</guid>
  1624. <description><![CDATA[The user port of the Commodore 64 exposes a TTL-level RS-232 serial port that supports up to 1200 baud1. In 1997, Daniel Dallmann came up with a very sophisticated trick that allowed sending and receiving at 9600 baud2, using slightly different wiring and a dedicated driver. This &#8220;UP9600&#8221; wiring has become the de-facto standard for ... <a title="UP9600: How to Bit-Bang 9600 Baud RS-232 on the C64" class="read-more" href="https://www.pagetable.com/?p=1656">Read more<span class="screen-reader-text">UP9600: How to Bit-Bang 9600 Baud RS-232 on the C64</span></a>]]></description>
  1625. <content:encoded><![CDATA[<p>The user port of the Commodore 64 exposes a TTL-level RS-232 serial port that supports up to 1200 baud<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>. In 1997, Daniel Dallmann came up with a very sophisticated trick that allowed sending and receiving at 9600 baud<sup id="fnref:2"><a href="#fn:2" rel="footnote">2</a></sup>, using slightly different wiring and a dedicated driver. This &ldquo;UP9600&rdquo; wiring has become the de-facto standard for all modern accessories, like C64 WiFi modems. Let&rsquo;s see how UP9600 works.</p>
  1626. <h2 id="history">History</h2>
  1627. <p>Before diving into the details of RS-232 and the UP9600 solution, let&rsquo;s look at some historical context.</p>
  1628. <h3 id="mos-6551-acia">MOS 6551 ACIA</h3>
  1629. <p>MOS made an RS-232 chip for the 6502: the 6551 ACIA (&ldquo;Asynchronous Communications Interface Adapter&rdquo;), which, per specification, can support up to 19200 baud. Commodore used it in the SuperPET (1981), the CBM-II series (1982) as well as the business-oriented Plus/4 (1984). It is exposed through a KERNAL driver as device #2.</p>
  1630. <h3 id="6551-emulator">6551 Emulator</h3>
  1631. <p>For cost saving reasons, the VIC-20 and its successors, the C64 and C128<sup id="fnref:3"><a href="#fn:3" rel="footnote">3</a></sup>, did not contain a 6551 chip. Instead, Commodore included a bit-banging driver in the KERNAL that emulated the 6551 and exposed it as device #2. This emulator supports up to 2400 baud, but due to DMA from the VIC-II video chip (&ldquo;badlines&rdquo;), only speeds up to 1200 are stable on the C64 (and the C128 in 40 columns mode).</p>
  1632. <h3 id="software-2400-baud">Software 2400 baud</h3>
  1633. <p>In the article &ldquo;Toward 2400&rdquo; in <a href="https://archive.org/details/transactor-magazines-v9-i03">Transactor volume 9, issue 3 (Feburary 1989)</a>, George Hug presented software to achieve 2400 baud reliably on the C64 without any hardware modifications<sup id="fnref:4"><a href="#fn:4" rel="footnote">4</a></sup>.</p>
  1634. <h3 id="swiftlink-232">SwiftLink-232</h3>
  1635. <p>The primary use for RS-232 on the C64 was for modems, and as modems faster than 2400 baud became available, Dr. Evil Laboratories released the $30 <a href="https://www.commodoreserver.com/BlogEntryView.asp?EID=FA5AE758474345A9A0A7208C7F408538">SwiftLink-232 cartridge</a> for the C64 expansion port in 1990. It contained a 6551 chip that could even reach 38400 baud, thanks to doubling the rate of the external oscillator.</p>
  1636. <h3 id="up9600">UP9600</h3>
  1637. <p>In 1997, Daniel Dallmann created UP9600, a solution that allowed 9600 baud on the user port. The idea is to use the hardware shift registers of the two CIA 6526 I/O controllers to do the timing-critical part of the transfer.</p>
  1638. <h2 id="rs-232-pins-on-the-user-port">RS-232 Pins on the User Port</h2>
  1639. <p>Every Commodore 8 bit computer except for the C16/C116 has a user port, and all user ports except for the original PET support TTL-level RS-232 through eight dedicated pins.</p>
  1640. <p><img src="docs/up9600/userport.png" height="143" width="451" alt="" /></p>
  1641. <p>The following table describes the C64 user port. The RS-232 pins are marked in red:</p>
  1642. <table>
  1643. <thead>
  1644. <tr>
  1645. <th> Pin </th>
  1646. <th> Description </th>
  1647. <th> Pin </th>
  1648. <th> Description </th>
  1649. </tr>
  1650. </thead>
  1651. <tbody>
  1652. <tr>
  1653. <td> 1   </td>
  1654. <td> GND         </td>
  1655. <td> A   </td>
  1656. <td> GND         </td>
  1657. </tr>
  1658. <tr>
  1659. <td> 2   </td>
  1660. <td> +5V         </td>
  1661. <td> B   </td>
  1662. <td> /FLAG2      </td>
  1663. </tr>
  1664. <tr>
  1665. <td> 3   </td>
  1666. <td> /RESET      </td>
  1667. <td> C   </td>
  1668. <td> PB0: <font color="red">RS-232 RXD</font> <img src="https://s.w.org/images/core/emoji/11/72x72/2b05.png" alt="⬅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> </td>
  1669. </tr>
  1670. <tr>
  1671. <td> 4   </td>
  1672. <td> CNT1        </td>
  1673. <td> D   </td>
  1674. <td> PB1: <font color="red">RS-232 RTS</font> </td>
  1675. </tr>
  1676. <tr>
  1677. <td> 5   </td>
  1678. <td> SP1         </td>
  1679. <td> E   </td>
  1680. <td> PB2: <font color="red">RS-232 DTR</font> </td>
  1681. </tr>
  1682. <tr>
  1683. <td> 6   </td>
  1684. <td> CNT2        </td>
  1685. <td> F   </td>
  1686. <td> PB3: <font color="red">RS-232 RI</font></td>
  1687. </tr>
  1688. <tr>
  1689. <td> 7   </td>
  1690. <td> SP2         </td>
  1691. <td> H   </td>
  1692. <td> PB4: <font color="red">RS-232 DCD</font> </td>
  1693. </tr>
  1694. <tr>
  1695. <td> 8   </td>
  1696. <td> /PC2        </td>
  1697. <td> J   </td>
  1698. <td> PB5         </td>
  1699. </tr>
  1700. <tr>
  1701. <td> 9   </td>
  1702. <td> SER ATN IN  </td>
  1703. <td> K   </td>
  1704. <td> PB6: <font color="red">RS-232 CTS</font> </td>
  1705. </tr>
  1706. <tr>
  1707. <td> 10  </td>
  1708. <td> 9 VAC       </td>
  1709. <td> L   </td>
  1710. <td> PB7: <font color="red">RS-232 DSR</font> </td>
  1711. </tr>
  1712. <tr>
  1713. <td> 11  </td>
  1714. <td> 9 VAC       </td>
  1715. <td> M   </td>
  1716. <td> PA2: <font color="red">RS-232 TXD</font> <img src="https://s.w.org/images/core/emoji/11/72x72/2b05.png" alt="⬅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> </td>
  1717. </tr>
  1718. <tr>
  1719. <td> 12  </td>
  1720. <td> GND         </td>
  1721. <td> N   </td>
  1722. <td> GND         </td>
  1723. </tr>
  1724. </tbody>
  1725. </table>
  1726. <p>RXD is the receive line, and TXD is the transmit line. The remaining lines deal with flow control, among other things, and are optional.</p>
  1727. <h2 id="rs-232">RS-232</h2>
  1728. <p>Before we can talk about UP9600, we first need some understanding of the RS-232 serial protocol.</p>
  1729. <p>To send serial data from one sender to one receiver, only a single data wire is needed. Beforehand, the two devices need to agree on the baudrate (e.g. 1200 bits/sec) and the format of each unit of data: how many data bits, whether there is an added parity bit, and how many stop bits. The most common parameter setting (and in fact the only one supported by the original UP9600 driver) is 8-N-1, meaning 8 data bits, no parity, and a single stop bit.</p>
  1730. <p>The transmission of one byte in 8-N-1 mode will look like this on the wire:</p>
  1731. <p><img src="docs/up9600/rs232-1.png" height="84" width="483" alt="" /></p>
  1732. <ul>
  1733. <li>&ldquo;1&rdquo; on the wire signals an idle line: the sender has no new data to send.</li>
  1734. <li>The transmission of a byte is indicated by sending a &ldquo;0&rdquo; for the duration of one bit.</li>
  1735. <li>The 8 data bits are transmitted afterwards, LSB first.</li>
  1736. <li>After that, a &ldquo;1&rdquo; is sent for at least the duration of one bit.</li>
  1737. <li>If there is no additional data, the line stays at &ldquo;1&rdquo;.</li>
  1738. </ul>
  1739. <p>Data can be sent back to back if there are multiple bytes available for sending:</p>
  1740. <p><img src="docs/up9600/rs232-2.png" height="57" width="226" alt="" /></p>
  1741. <p>As usual, the 8 data bits are followed by a stop bit (&ldquo;1&rdquo;), then immediately by a start bit (&ldquo;0&rdquo;) and then the next 8 data bits.</p>
  1742. <p>The point in time when the sender sets the wire from &ldquo;1&rdquo; to &ldquo;0&rdquo; for the start bit is the synchronization point for the transmission of the following byte. The receiver will have to sample the line at the right intervals counting from the sync point for the following 8 data bits and the stop bit. The next start bit will re-sync sender and receiver again, so the clocks of sender and receiver only have to be matching well enough for the duration of the transmission of a single byte.</p>
  1743. <p>After the line has been idle, a start bit can be sent at any time, it does not have to fall into the time raster of the bit rate. As just described, time starts anew with each start bit for both sender and receiver.</p>
  1744. <p>If the receiver starts listening while the sender is already in the middle of a transmission, incorrect data will be received. The first &ldquo;0&rdquo; bit it encounters will be understood as the start bit, and the next 8 bits will be the data byte. If the following bit is not a &ldquo;1&rdquo;, the receiver will discard the byte, and wait for the next &ldquo;0&rdquo; bit, which hopefully is the correct start bit this time. In the worst case, it will detect a few garbled bytes, but statistically, the receiver will sync itself over time.</p>
  1745. <h2 id="the-cia-6526-shift-register">The CIA 6526 Shift Register</h2>
  1746. <p>The core idea of UP9600 is to use the two otherwise unused hardware shift registers in the C64, which can automatically send or receive a byte bit-by-bit over a single wire, faster than the CPU would be able to do it.</p>
  1747. <p>The C64 has two CIA 6526 I/O controllers, each of which contains a serial shift register that can transfer bytes using a clock and a data line to a peer that speaks the same protocol, e.g. the CIA in another computer<sup id="fnref:5"><a href="#fn:5" rel="footnote">5</a></sup>. They are unused by a stock C64, but exposed on the user port. Here is the table again, this time with the shift register wires marked in red:</p>
  1748. <table>
  1749. <thead>
  1750. <tr>
  1751. <th> Pin </th>
  1752. <th> Description </th>
  1753. <th> Pin </th>
  1754. <th> Description </th>
  1755. </tr>
  1756. </thead>
  1757. <tbody>
  1758. <tr>
  1759. <td> 1   </td>
  1760. <td> GND         </td>
  1761. <td> A   </td>
  1762. <td> GND         </td>
  1763. </tr>
  1764. <tr>
  1765. <td> 2   </td>
  1766. <td> +5V         </td>
  1767. <td> B   </td>
  1768. <td> /FLAG2      </td>
  1769. </tr>
  1770. <tr>
  1771. <td> 3   </td>
  1772. <td> /RESET      </td>
  1773. <td> C   </td>
  1774. <td> PB0         </td>
  1775. </tr>
  1776. <tr>
  1777. <td> 4   </td>
  1778. <td> <font color="red">CNT1</font></td>
  1779. <td> D   </td>
  1780. <td> PB1         </td>
  1781. </tr>
  1782. <tr>
  1783. <td> 5   </td>
  1784. <td> <font color="red">SP1</font> </td>
  1785. <td> E   </td>
  1786. <td> PB2         </td>
  1787. </tr>
  1788. <tr>
  1789. <td> 6   </td>
  1790. <td> <font color="red">CNT2</font></td>
  1791. <td> F   </td>
  1792. <td> PB3         </td>
  1793. </tr>
  1794. <tr>
  1795. <td> 7   </td>
  1796. <td> <font color="red">SP2</font> </td>
  1797. <td> H   </td>
  1798. <td> PB4         </td>
  1799. </tr>
  1800. <tr>
  1801. <td> 8   </td>
  1802. <td> /PC2        </td>
  1803. <td> J   </td>
  1804. <td> PB5         </td>
  1805. </tr>
  1806. <tr>
  1807. <td> 9   </td>
  1808. <td> SER ATN IN  </td>
  1809. <td> K   </td>
  1810. <td> PB6         </td>
  1811. </tr>
  1812. <tr>
  1813. <td> 10  </td>
  1814. <td> 9 VAC       </td>
  1815. <td> L   </td>
  1816. <td> PB7         </td>
  1817. </tr>
  1818. <tr>
  1819. <td> 11  </td>
  1820. <td> 9 VAC       </td>
  1821. <td> M   </td>
  1822. <td> PA2         </td>
  1823. </tr>
  1824. <tr>
  1825. <td> 12  </td>
  1826. <td> GND         </td>
  1827. <td> N   </td>
  1828. <td> GND         </td>
  1829. </tr>
  1830. </tbody>
  1831. </table>
  1832. <p>CNT1 and SP1 are the clock and data lines of CIA#1, and CNT2 and SP2 are the clock and data lines of CIA#2.</p>
  1833. <p>In order to send a byte, we need to set timer A so it fires at the correct interval between bits<sup id="fnref:6"><a href="#fn:6" rel="footnote">6</a></sup></p>
  1834. <pre><code>    timer = (system_freq) / (2 * bit_output_freq) - 1
  1835. </code></pre>
  1836. <p>and set it to continuous mode, set the serial port to output, and then write the byte to the serial data register. On every timer underflow, the CNT line will be toggled, and on every falling edge, the next bit is put on the SP line – this is why the timer has to fire twice per bit, explaining the extra factor of 2 in the divisor of the formula.</p>
  1837. <p>Once all 8 bits are shifted out, a bit in the interrupt control register is sent, which can optionally trigger an interrupt. The SP line will retain the value of the last bit.</p>
  1838. <p>To send a continuous stream of bytes, you have to write the first two bytes to the serial data register immediately after one another – the CIA will start sending out the first byte and cache the second one – and after each interrupt, a new byte can be written to the shift register, while the previous one is still being shifted out.</p>
  1839. <p>To receive a byte through the shift register, you have to set it to input mode, and it will sample a bit on every rising edge of CNT. Again, after 8 bits, it will set a bit in the interrupt control register and optionally trigger an interrupt. Now, the 8 bits can be read from the serial data register.</p>
  1840. <h2 id="cia#1-and-cia#2">CIA#1 and CIA#2</h2>
  1841. <p>The two 6526 CIA chips in the C64 are identical, but connected differently.</p>
  1842. <ul>
  1843. <li>Most pins of the user port, and all RS-232 pins go to CIA#2.</li>
  1844. <li>The serial ports of both CIAs are exposed on the user port.</li>
  1845. <li>The interrupt output of CIA#1 is connected to the CPU&rsquo;s IRQ line, while the interrupt output of CIA#2 is connected to the CPU&rsquo;s NMI line.</li>
  1846. </ul>
  1847. <p>Receiving data is more timing-critical than sending, so we will use CIA#2 for receiving, since NMIs are useful for tighter timing requirements.</p>
  1848. <h2 id="sending-rs-232-data">Sending RS-232 Data</h2>
  1849. <p>Now how do we use the shift register to send data in the RS-232 transmission format? After all, it was not exactly designed with RS-232 in mind:</p>
  1850. <ol>
  1851. <li>The user port wiring of the serial port is different than what the KERNAL driver uses.</li>
  1852. <li>The order of the bits in a byte is reversed in the CIA compared to RS-232.</li>
  1853. <li>The shift register works on 8 bits at a time, while RS-232 deals with groups of 10 bits.</li>
  1854. </ol>
  1855. <h3 id="txd-wiring">TXD Wiring</h3>
  1856. <p>The data output of the CIA#1 shift register is wired to SP1 (pin 5) on the user port, but the old RS-232 KERNAL driver outputs data on PA2 (pin M). This means that existing user port RS-232 hardware (which uses pin M) wouldn&rsquo;t be compatible. But new hardware that is aware of UP9600 can just bridge pin M to pin 5, and it will be compatible both with the original pinout and with UP9600.</p>
  1857. <p>The additional clock output of the CIA is not a problem: It will appear at pin 4 (CNT1) on the user port, and existing user port RS-232 hardware wouldn&rsquo;t have it connected, and there is no reason to connect it on new hardware.</p>
  1858. <h3 id="bit-order">Bit Order</h3>
  1859. <p>The shift register sends data with the most significant bit first, while RS-232 sends the least significant bit first. The 8 data bits will have to be reversed in order before sending them. The fastest solution is a 256 byte lookup table. The UP9600 code actually uses a 128 byte lookup table and patches the remaining bit into the resulting byte, trading some execution speed for memory.</p>
  1860. <h3 id="sending-10-bits">Sending 10 Bits</h3>
  1861. <p>The trickiest problem in sending is that the shift register works on 8 bits at a time, while for RS-232, each data byte results in a total of 10 bits: one start bit, 8 data bits and one stop bit. If all the data to be sent were known beforehand, one could shift the data bytes into a set of bytes that corresponds to the RS-232 bit stream and continuously write them to the shift register:</p>
  1862. <p><img src="docs/up9600/shift_register_stream.png" height="109" width="458" alt="" /></p>
  1863. <p>Unfortunately, software that wishes to send data often doesn&rsquo;t have all the data available, or data arrives from software in bursts with pauses in between. In a terminal program, text typed by the user arrives one byte at a time, with significant pauses. During file transmission (e.g. XMODEM), a number of bytes will be sent together, but without additional communication, the RS-232 driver wouldn&rsquo;t know at what point it needs to flush the accumulated data. It&rsquo;s possible to do it this way, but it is quite complicated.</p>
  1864. <p>Instead, the UP9600 software sends every data byte as 16 bits, the 10 RS-232 bits (one stop bit, 8 data bits, one stop bit), and 6 filler bits with a value of &ldquo;1&rdquo; – you can see them either as additional stop bits or as a signal that the line is idle.</p>
  1865. <p><img src="docs/up9600/shift_register_2bytes.png" height="100" width="194" alt="" /></p>
  1866. <p>While this only achieves 62.5% of the peak data rate – 600 bytes/sec instead of 960 bytes/sec, assuming 9600 baud – this is completely legal and will be understood by any receiver. It does not look any different than a sender that had to add a little extra pause between bytes.</p>
  1867. <p>So in practice, the UP9600 code always sends one data byte at a time, like this (using CIA#1):</p>
  1868. <ul>
  1869. <li>Set the serial port to output.</li>
  1870. <li>Enable serial port interrupts.</li>
  1871. <li>Set timer A to 1022727 / (2 * 9600) &#8211; 1 = 52 (NTSC) or 985249 / (2 * 9600) &#8211; 1 = 50 (PAL)<sup id="fnref:7"><a href="#fn:7" rel="footnote">7</a></sup>.</li>
  1872. <li>Set the timer to continuous and start it.</li>
  1873. <li>Write a &ldquo;0&rdquo; (start bit) and bits 0 through 6 to the serial data register – note the reversed bit order!</li>
  1874. <li>Write the remaining bit and seven &ldquo;1&rdquo; bits (stop bits) to the serial data register. Again, note the bit order.</li>
  1875. </ul>
  1876. <p>The next byte can be written once two interrupts have been triggered by the CIA.</p>
  1877. <p>(There is a little added complication: Serial timing has to come from the timer A, but the C64 KERNAL has timer A of CIA#1 set up as the 60 Hz interrupt source that deals with updating the TI$ clock, scanning the keyboard and blinking the cursor. We have to migrate the 60 Hz interrupt to timer B if we want to continue using these KERNAL services.)</p>
  1878. <p>Let&rsquo;s look at the timing of this code: We have to make sure that the second byte is written before the first byte is fully transmitted. Otherwise, the last bit of the first byte (i.e. second-to-last data bit) would stay on the line too long, leading to incorrect data transmission. With 9600 baud, we have a window of about 100 clock cycles.</p>
  1879. <ul>
  1880. <li>Every 8 raster lines, the VIC-II takes control from the CPU for 40 cycles (&ldquo;badlines&rdquo;).</li>
  1881. <li>We could be interrupted by an NMI caused by the receiving part of the UP9600 software – see below.</li>
  1882. </ul>
  1883. <p>As long as the receiver NMI code takes less than 60 cycles, we&rsquo;re good at 9600 baud, even when a badline and an NMI occur at the same time. In fact, even higher speeds could be possible by avoiding VIC-II badlines – after all, we control when to start the transmission of a byte. But since the baud rate is the same for sending and receiving, it&rsquo;ll be the receiving part that will limit the maximum bit rate.</p>
  1884. <h2 id="receiving-rs-232-data">Receiving RS-232 Data</h2>
  1885. <p>Receiving is also tricky because:</p>
  1886. <ol>
  1887. <li>Again, the user port wiring of the serial port is different than what the KERNAL driver uses.</li>
  1888. <li>A start bit and thus the transmission of a data byte can happen at any time, and at 9600 baud, the window for a single bit is just 100 cycles.</li>
  1889. <li>The shift register has no way to shift in a bit every <em>n</em> cycles.</li>
  1890. </ol>
  1891. <h3 id="rxd-wiring">RXD Wiring</h3>
  1892. <p>The data input of the CIA#2 shift register is wired to SP2 (pin 7) on the user port, but the old RS-232 KERNAL driver receives data on PB0 (pin C). As with the output wire, new hardware that is aware of UP9600 can just bridge pin C to pin 7 and be compatible with both drivers.</p>
  1893. <h3 id="detecting-the-start-bit">Detecting the Start Bit</h3>
  1894. <p>To detect a start bit, we have the following options:</p>
  1895. <ul>
  1896. <li>Busy waiting. This way, there will be no way to do anything else on the system.</li>
  1897. <li>A timer interrupt that fires every 100 cycles, so we can check the line. This would be a lot of interrupts, slowing down the system significantly.</li>
  1898. <li>Finding a way to have the stop bit cause an interrupt.</li>
  1899. </ul>
  1900. <p>In fact, it is possible to have an external pin on the user port generate an interrupt – a falling edge on FLAG2 (pin B) will make CIA#2 trigger an NMI. So UP9600-aware hardware needs to bridge PB0 (pin C), which normally carries RDX (receive line) to FLAG2 (pin B).</p>
  1901. <p>The UP9600 software thus enables the FLAG interrupt on CIA#2 and hooks the NMI vector. Once the NMI fires, it sets up the CIA#2 to read the next 8 bits into the shift register.</p>
  1902. <h3 id="using-a-cia-timer-for-the-clock">Using a CIA Timer for the Clock</h3>
  1903. <p>The trickiest problem in receiving is the fact that for data input, the shift register has to be clocked from the CNT line, and we don&rsquo;t have such a signal! There is no way to tell the shift register to just sample the data wire every <em>n</em> clock cycles.</p>
  1904. <p>The trick is to have the CIA generate a matching clock signal, output it on the user port, and use a bridge on the UP9600-aware device to feed this signal back into the CIA through the CNT2 pin.</p>
  1905. <p>The CIA has a way to send a pulse every <em>n</em> cycles on pins PB6 and PB7. When enabled, an underflow of timer A will pulse PB6, and an underflow of timer B will pulse PB7. And both these CIA outputs are available on the user port! UP9600 uses timer B for this, so we need a bridge from PB7 (pin L) to CNT2 (pin 6).</p>
  1906. <p>So inside the NMI handler for the start bit, we have to</p>
  1907. <ul>
  1908. <li>Disable FLAG interrupts – we are not listening for a start bit any more.</li>
  1909. <li>Set the serial port to input</li>
  1910. <li>Enable serial port interrupts.</li>
  1911. <li>Set timer B to 1022727 / (9600) &#8211; 1 = 106 (NTSC) or 985249 / (9600) &#8211; 1 = 102 (PAL).</li>
  1912. <li>Set the timer to continuous, set it to pulse on PB7 and start it.</li>
  1913. <li>Change the NMI vector to the second NMI handler.</li>
  1914. </ul>
  1915. <p>The second NMI handler will then</p>
  1916. <ul>
  1917. <li>Read the data byte from the serial data register and reverse the bit order.</li>
  1918. <li>Disable timer B.</li>
  1919. <li>Enable FLAG interrupts for the next start bit.</li>
  1920. <li>Change the NMI vector to the first NMI handler.</li>
  1921. </ul>
  1922. <p>Note that we never read the stop bit. This means that the UP9600 does not plausibility check of the data. If the receiver starts listening while the sender is already in the middle of a transmission, the UP9600 driver will pass several garbled bytes to the receiving software and take longer than necessary to eventually sync itself to the signal.</p>
  1923. <h2 id="up9600-wiring">UP9600 Wiring</h2>
  1924. <p>So for a C64 user port RS-232 device to be UP9600-aware, all it needs to do is bridge the following pins:</p>
  1925. <table>
  1926. <thead>
  1927. <tr>
  1928. <th> Pin 1   </th>
  1929. <th> Pin 2      </th>
  1930. <th> Description                        </th>
  1931. </tr>
  1932. </thead>
  1933. <tbody>
  1934. <tr>
  1935. <td> M (PA2) </td>
  1936. <td> 5 (SP1)    </td>
  1937. <td> RS-232 output to CIA#1 Serial Port </td>
  1938. </tr>
  1939. <tr>
  1940. <td> C (PB0) </td>
  1941. <td> 7 (SP2)    </td>
  1942. <td> RS-232 input to CIA#2 Serial Port  </td>
  1943. </tr>
  1944. <tr>
  1945. <td> C (PB0) </td>
  1946. <td> B (/FLAG2) </td>
  1947. <td> RS-232 input to CIA#2 FLAG, for NMI on start bit </td>
  1948. </tr>
  1949. <tr>
  1950. <td> L (PB7) </td>
  1951. <td> 6 (CNT2)   </td>
  1952. <td> CIA#2 timer B pulse to CIA#2 Serial Port clock </td>
  1953. </tr>
  1954. </tbody>
  1955. </table>
  1956. <p>UP9600 does not work on a C128 in 128 mode, since it uses the CIA#1 shift register for communication with Fast Serial disk drives (1571, 1581, &hellip;). It will even hang on boot if a Fast Serial device is attached. To work around the latter, a jumper can be installed to disconnect M from 5.</p>
  1957. <h2 id="why-is-9600-the-maximum-baudrate?">Why is 9600 the Maximum Baudrate?</h2>
  1958. <p>The highest baudrate that is possible on a C64 with the described algorithm is 9600 baud.</p>
  1959. <p>At this speed, a bit arrives roughly every 100 cycles. It takes about 30 cycles from the moment of the start bit NMI to disable the FLAG NMI, enable the timer and hook the serial port NMI. 50 cycles after the start bit NMI would be the center of the start bit and the optimal time to sample every 100 cycles, so starting the timer 30 cycles into the start bit is fine.</p>
  1960. <p>The interesting part on the C64 are VIC-II badlines, which can stall the CPU for 40 cycles at practically any time. If a badline happens just as the NMI is taken, setting up the CIA to read the 8 data bits will be finished 40 cycles later, i.e. at 70 cycles after the moment of the start bit NMI. This is still fine, because in either case, we will fall into the window where the start bit is valid.</p>
  1961. <p><img src="docs/up9600/sampling_9600.gif" height="256" width="420" alt="" /><br />
  1962. <img src="docs/up9600/sampling_9600.png" height="256" width="420" alt="" /></p>
  1963. <p>The next highest standard baudrate would be 19200, with a bit every 50 cycles. With the same algorithm, the following will happen: If there is no badline, we will set up the CIA at 30 cycles again, and it will sample every 50 cycles, so pretty much in the center of each bit. Great. But if there is a badline, we&rsquo;re already at 70 cycles, which is in the middle of the first data bit. The first bit would have to be read by the serial port immediately, 50 cycles later would be too late.</p>
  1964. <p><img src="docs/up9600/sampling_19200.gif" height="256" width="420" alt="" /></p>
  1965. <p>We can&rsquo;t detect whether a badline just happened, but since the window to read a bit is 50 cycles and the fuzz introduced by badlines is just 40 cycles, we have a chance to time it just right to read the bit within the window.</p>
  1966. <p><img src="docs/up9600/sampling_19200.png" height="256" width="420" alt="" /></p>
  1967. <p>Depending on whether there is a badline, the latency from the moment of the start bit NMI to the point where we can access the CIA is 30 to 70 cycles. The process would be to:</p>
  1968. <ul>
  1969. <li>Wait 25 cycles, so we fall into the 55 to 95 cycle range, in the center of the window of bit #0. These cycles can be used to set up the shift register and disable the FLAG NMI.</li>
  1970. <li>Output a manual pulse on PB7 to sample the first bit into the shift register immediately.</li>
  1971. <li>Set up the timer to pulse PB7 every 50 cycles and start it.</li>
  1972. </ul>
  1973. <p><img src="docs/up9600/sampling_19200-2.png" height="256" width="420" alt="" /></p>
  1974. <p>The implementation of UP19200 is left as an exercise to the reader.</p>
  1975. <h2 id="source-code">Source Code</h2>
  1976. <ul>
  1977. <li>The <a href="docs/up9600/up9600.txt">original UP9600 source code</a> is available as part of an email from Daniel Dallmann to the developer of Novaterm, dated 30 Nov 1997. It is designed as a library that can be used from BASIC or assembly.</li>
  1978. <li>Bo Zimmerman maintains <a href="https://github.com/bozimmerman/Zimodem/blob/master/cbm8bit/src/up9600.asm">a version of UP9600</a> as part of the ZiModem repository that hooks itself into the KERNAL vectors for the Channel I/O calls, so UP9600 can be used from BASIC or assembly using the existing KERNAL interface.</li>
  1979. <li>The <a href="https://github.com/mist64/ccgmsterm">CCGMS</a> terminal program contains a version adapted by <a href="https://1200baud.wordpress.com/">alwyz</a>. It is the only version to also support, in addition to 9600, baudrates of 300, 1200, 2400 and 4800. The current version in the repository contains fixes, cleanpus and comments by myself.</li>
  1980. </ul>
  1981. <h2 id="related">Related</h2>
  1982. <ul>
  1983. <li><a href="http://kahlin.net/daniel/over5/">over5</a> by Daniel Kahlin can do 38400/8N2 with the screen off.</li>
  1984. <li><a href="http://www.pastbytes.com/retroterm/">Retroterm</a> (<a href="https://csdb.dk/release/?id=206995">CSDb</a>) by Jorge Castillo can do 57600/8N1 with the screen off, and – using RTS/CTS flow control – an effective throughput of 1500 (PAL) or 1800 (NTSC) baud equivalent by only receiving in the border area.</li>
  1985. </ul>
  1986. <div class="footnotes">
  1987. <hr/>
  1988. <ol>
  1989. <li id="fn:1">
  1990. <p>It is supposed to support 2400 baud as well, but this speed does not work reliably in practice.  <a href="#fnref:1" rev="footnote">&#8617;</a></p>
  1991. </li>
  1992. <li id="fn:2">
  1993. <p>Sending won&rsquo;t achieve the full data rate though; we&rsquo;ll cover this in the article.  <a href="#fnref:2" rev="footnote">&#8617;</a></p>
  1994. </li>
  1995. <li id="fn:3">
  1996. <p>Development machines of the C128 did contain a 6551.  <a href="#fnref:3" rev="footnote">&#8617;</a></p>
  1997. </li>
  1998. <li id="fn:4">
  1999. <p>A modern version of this software exists in the form of a <a href="https://github.com/nanoflite/c64-up2400-cc65">cc65 driver</a>.  <a href="#fnref:4" rev="footnote">&#8617;</a></p>
  2000. </li>
  2001. <li id="fn:5">
  2002. <p>This is the same shift register that was supported, but broken in the MOS 6522 VIA chip, which is why the <a href="https://www.pagetable.com/?p=1595">1541 disk drive was so slow</a>.  <a href="#fnref:5" rev="footnote">&#8617;</a></p>
  2003. </li>
  2004. <li id="fn:6">
  2005. <p>The timer value has to be one less than the quotient, because in continuous mode, automatic reloading of the timer takes one extra cycle.  <a href="#fnref:6" rev="footnote">&#8617;</a></p>
  2006. </li>
  2007. <li id="fn:7">
  2008. <p>1022727 and 985249 are approximations of the respective system clock rates in Hz. The exact numbers are 4/14 * <strong>315/88</strong> for PAL and 4/18 * <strong>4433618.75</strong> for NTSC. The two constants are the respective <a href="https://en.wikipedia.org/wiki/Colorburst">colorbust frequencies</a>.<a href="#fnref:7" rev="footnote">&#8617;</a></p>
  2009. </li>
  2010. </ol>
  2011. </div>
  2012. ]]></content:encoded>
  2013. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1656</wfw:commentRss>
  2014. <slash:comments>8</slash:comments>
  2015. </item>
  2016. <item>
  2017. <title>The Commodore Modem/1200 (Model 1670)</title>
  2018. <link>https://www.pagetable.com/?p=1647</link>
  2019. <comments>https://www.pagetable.com/?p=1647#comments</comments>
  2020. <pubDate>Sat, 05 Mar 2022 19:23:11 +0000</pubDate>
  2021. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  2022. <category><![CDATA[Archeology]]></category>
  2023. <category><![CDATA[C64]]></category>
  2024. <category><![CDATA[Commodore]]></category>
  2025. <category><![CDATA[Literature]]></category>
  2026. <category><![CDATA[Modem]]></category>
  2027.  
  2028. <guid isPermaLink="false">https://www.pagetable.com/?p=1647</guid>
  2029. <description><![CDATA[The Commodore 1670, also known as the &#8220;Modem/1200&#8221;, is Commodore&#8217;s first Hayes-compatible modem: It connects directly to the phone line and supports pulse and tone dialing for 1200 and 300 baud duplex connections. There were two revisions, the original 1670 and the &#8220;new&#8221; 1670, a.k.a. CR-1670. (The unit in this article is the later revision.) ... <a title="The Commodore Modem/1200 (Model 1670)" class="read-more" href="https://www.pagetable.com/?p=1647">Read more<span class="screen-reader-text">The Commodore Modem/1200 (Model 1670)</span></a>]]></description>
  2030. <content:encoded><![CDATA[<p>The Commodore 1670, also known as the &ldquo;Modem/1200&rdquo;, is Commodore&rsquo;s first Hayes-compatible modem: It connects directly to the phone line and supports pulse and tone dialing for 1200 and 300 baud duplex connections. There were two revisions, the original 1670 and the &ldquo;new&rdquo; 1670, a.k.a. CR-1670. (The unit in this article is the later revision.)</p>
  2031. <h2 id="historical-context">Historical Context</h2>
  2032. <table>
  2033. <thead>
  2034. <tr>
  2035. <th> Year </th>
  2036. <th> Name </th>
  2037. <th> Model </th>
  2038. <th> Description </th>
  2039. </tr>
  2040. </thead>
  2041. <tbody>
  2042. <tr>
  2043. <td> 1982 </td>
  2044. <td> <a href="https://www.pagetable.com/?p=1679">VICMODEM</a> </td>
  2045. <td> 1600 </td>
  2046. <td> connected to phone&rsquo;s handset connector; manual dialing through phone; Motorola MC14412 </td>
  2047. </tr>
  2048. <tr>
  2049. <td> 1982 </td>
  2050. <td> <a href="https://www.pagetable.com/?p=1716">AUTOMODEM</a> </td>
  2051. <td> 1650 </td>
  2052. <td> connected to phone line, pulse dialing in software; Motorola MC14412 </td>
  2053. </tr>
  2054. <tr>
  2055. <td> 1985 </td>
  2056. <td> <a href="https://www.pagetable.com/?p=1644">MODEM/300</a> </td>
  2057. <td> 1660 </td>
  2058. <td>added tone dialing support by feeding SID output into modem; Texas Instruments TMS99532A </td>
  2059. </tr>
  2060. <tr>
  2061. <td> 1987 </td>
  2062. <td> <a href="https://www.pagetable.com/?p=1647">MODEM/1200</a> </td>
  2063. <td> 1670 </td>
  2064. <td> Hayes command set; pulse and tone dialing in hardware; 300/1200 baud support; U.S. Robotics chipset </td>
  2065. </tr>
  2066. </tbody>
  2067. </table>
  2068. <h2 id="photos">Photos</h2>
  2069. <p><a href="docs/cbm1670modem/cbm1670_1.jpg"><img src="docs/cbm1670modem/cbm1670_1.jpg" height="403" width="476" alt="" /></a></p>
  2070. <p>On the front, there is the user port connector.</p>
  2071. <p><a href="docs/cbm1670modem/cbm1670_2.jpg"><img src="docs/cbm1670modem/cbm1670_2.jpg" height="363" width="452" alt="" /></a></p>
  2072. <p>On the back, there are</p>
  2073. <ul>
  2074. <li>two phone line connectors. &ldquo;LINE&rdquo; is connected to the telephone network, and an existing telephone can be connected to the &ldquo;PHONE&rdquo; line.</li>
  2075. <li>four DIP switches (default to down):</li>
  2076. </ul>
  2077. <table>
  2078. <thead>
  2079. <tr>
  2080. <th> DIP </th>
  2081. <th> Description           </th>
  2082. <th> </th>
  2083. </tr>
  2084. </thead>
  2085. <tbody>
  2086. <tr>
  2087. <td> 1   </td>
  2088. <td> Auto Answer Enable    </td>
  2089. <td> down: auto-answer on second ring disabled </td>
  2090. </tr>
  2091. <tr>
  2092. <td> 2   </td>
  2093. <td> Carrier Detect Enable </td>
  2094. <td> down: carrier detect on pin H-K of edge connector (needed for Plus/4) </td>
  2095. </tr>
  2096. <tr>
  2097. <td> 3   </td>
  2098. <td> Speed Indicate Enable </td>
  2099. <td> down: speed indicate on pin J of edge connector </td>
  2100. </tr>
  2101. <tr>
  2102. <td> 4   </td>
  2103. <td> Data Terminal Ready   </td>
  2104. <td> down: DTR always on (instead of computer controlled) </td>
  2105. </tr>
  2106. </tbody>
  2107. </table>
  2108. <p><a href="docs/cbm1670modem/back.jpg"><img src="docs/cbm1670modem/back.jpg" height="463" width="259" alt="" /></a></p>
  2109. <p>The label on the bottom says:</p>
  2110. <table>
  2111. <tr>
  2112. <td align="center">
  2113. C= COMMODORE
  2114. </td>
  2115. <tr>
  2116. <tr>
  2117. <td>
  2118. MODEL NO. 1670
  2119. </td>
  2120. <tr>
  2121. <tr>
  2122. <td>
  2123. SERIAL NO. CA1111714
  2124. </td>
  2125. <tr>
  2126. <tr>
  2127. <td>
  2128. COMPLIES WITH PART 68, FCC RULES; FC<br /> <br />
  2129. REGISTRATION NUMBER BR98YV-19442-MDE<br /> <br />
  2130. RINGER EQUIVALENT 0.4A 0.6B; JACK (USOC) RJ11<br />
  2131. </td>
  2132. <tr>
  2133. <tr>
  2134. <td>
  2135. CERTIFIED TO COMPLY WITH CLASS B LIMITS,<br /> <br />
  2136. PART 15, SUBPART J OF FCC RULES. SEE<br /> <br />
  2137. INSTRUCTIONS IF INTERFERENCE TO RADIO<br /> <br />
  2138. RECEPTION IS SUSPECTED.
  2139. </td>
  2140. <tr>
  2141. <tr>
  2142. <td>
  2143. FCC ID BR98YV1670<br /> <br />
  2144. MADE IN USA <span style="float:right">310476-03</span>
  2145. </td>
  2146. <tr>
  2147. </table>
  2148. <p><a href="docs/cbm1670modem/board1.jpg"><img src="docs/cbm1670modem/board1.jpg" height="242" width="480" alt="" /></a><a href="docs/cbm1670modem/board2.jpg"><img src="docs/cbm1670modem/board2.jpg" height="257" width="480" alt="" /></a></p>
  2149. <p>The board is marked &ldquo;COMMODORE CR-1670 MODEM&rdquo; and &ldquo;A/W 311956 REV 4&rdquo;.</p>
  2150. <p>The 1670 is based on a U.S. Robotics (&ldquo;USR&rdquo;) chipset:</p>
  2151. <ul>
  2152. <li>U2: <code>© USR'85 U2J2 / OKI C49-387 / JAPAN 7X2033</code>: This is an OKI MSM80C49-387RS, a <a href="docs/cbm1670modem/M80C49_OKIelectronic.pdf">80C49 microcontroller</a> with 2KB of mask ROM and 128 bytes of RAM. &ldquo;387&rdquo; indicates which ROM image it contains.</li>
  2153. <li>U3: <code>© USR'85 U3J2 / OKI C49-388 / JAPAN 7Y2013</code>: This is an OKI MSM80C49-388RS, another 80C49 microcontroller, with a different ROM image (&ldquo;388&rdquo;).</li>
  2154. <li>U4: <code>© USR86 / USR101 16-249 / S8749 / 35561</code>: This socketed IC is probably the ROM that holds the bulk of the modem&rsquo;s firmware.</li>
  2155. </ul>
  2156. <p><a href="docs/cbm1670modem/speaker.jpg"><img src="docs/cbm1670modem/speaker.jpg" height="252" width="336" alt="" /></a></p>
  2157. <p>There is a speaker glued to the top shell that allows monitoring the audio data on the line.</p>
  2158. <p><a href="docs/cbm1670modem/phone_cable.jpg"><img src="docs/cbm1670modem/phone_cable.jpg" height="218" width="296" alt="" /></a></p>
  2159. <p>The modem comes with a phone cable.</p>
  2160. <h2 id="box">Box</h2>
  2161. <p><a href="docs/cbm1670modem/box_front.jpg"><img src="docs/cbm1670modem/box_front.jpg" height="460" width="292" alt="" /></a><a href="docs/cbm1670modem/box_back.jpg"><img src="docs/cbm1670modem/box_back.jpg" height="460" width="295" alt="" /></a><br />
  2162. <a href="docs/cbm1670modem/box_side.jpg"><img src="docs/cbm1670modem/box_side.jpg" height="87" width="447" alt="" /></a></p>
  2163. <h2 id="manual">Manual</h2>
  2164. <p><a href="docs/cbm1670modem/cbm1670modem_manual.pdf"><img src="docs/cbm1670modem/cbm1670modem_manual.jpg" height="412" width="284" alt="" /></a></p>
  2165. <h2 id="quantumlink-material">QuantumLink Material</h2>
  2166. <p><a href="docs/cbm1670modem/QuantumLink%20Envelope.pdf"><img src="docs/cbm1670modem/QuantumLink%20Envelope.jpg" height="176" width="216" alt="" /></a><a href="docs/cbm1670modem/QuantumLink%20Letter.pdf"><img src="docs/cbm1670modem/QuantumLink%20Letter.jpg" height="314" width="199" alt="" /></a><a href="docs/cbm1670modem/QuantumLink_Registration_Certificate.pdf"><img src="docs/cbm1670modem/QuantumLink_Registration_Certificate.png" height="142" width="203" alt="" /></a><a href="docs/cbm1670modem/QuantumLink%20Quick%20Connect%20Guide.pdf"><img src="docs/cbm1670modem/QuantumLink%20Quick%20Connect%20Guide.jpg" height="406" width="265" alt="" /></a><a href="docs/cbm1670modem/QuantumLink%20Members%20Guide.pdf"><img src="docs/cbm1670modem/QuantumLink%20Members%20Guide.jpg" height="406" width="265" alt="" /></a><a href="docs/cbm1670modem/QuantumLink%20Local%20Access%20Directory.pdf"><img src="docs/cbm1670modem/QuantumLink%20Local%20Access%20Directory.jpg" height="395" width="266" alt="" /></a></p>
  2167. <h2 id="more-box-contents">More Box Contents</h2>
  2168. <p><a href="docs/cbm1670modem/Warranty%20Card.pdf"><img src="docs/cbm1670modem/Warranty%20Card.png" height="423" width="255" alt="" /></a><a href="docs/cbm1670modem/Sixth%20Sense.pdf"><img src="docs/cbm1670modem/Sixth%20Sense.png" height="358" width="277" alt="" /></a><a href="docs/cbm1670modem/Damaged%20Disk.pdf"><img src="docs/cbm1670modem/Damaged%20Disk.png" height="224" width="180" alt="" /></a></p>
  2169. <h2 id="disk">Disk</h2>
  2170. <p><img src="docs/cbm1670modem/disk_label_a.png" height="124" width="384" alt="" /> <a href="docs/cbm1670modem/cbm1670modem_a.d64"><img src="docs/d64.png" alt="" /></a><br />
  2171. <img src="docs/cbm1670modem/disk_label_b.png" height="124" width="384" alt="" /> <a href="docs/cbm1670modem/cbm1670modem_b.d64"><img src="docs/d64.png" alt="" /></a></p>
  2172. <h2 id="the-hayes/at-interface">The Hayes/AT Interface</h2>
  2173. <ul>
  2174. <li>
  2175. <p>ATI/ATI0 (product code) prints</p>
  2176. <pre><code>  121
  2177.  
  2178.  OK
  2179. </code></pre>
  2180. </li>
  2181. <li>
  2182. <p>ATI1 (ROM checksum) prints</p>
  2183. <pre><code>  1003
  2184.  
  2185.  OK
  2186. </code></pre>
  2187. </li>
  2188. <li>
  2189. <p>The manual defines registers (<code>ATSn=a</code>, <code>ATSn?</code>) 0-8, 10-12 and 16. Except for 16 (self test), they are identical to other U.S. Robotics modems – as late as the 1997 <a href="https://support.usr.com/support/1663/1663-files/1663-unkg-Manual.pdf">Sportster Flash x2</a>!</p>
  2190. </li>
  2191. <li>
  2192. <p>Register 9 is undocumented. Later U.S. Robotics manuals document it as: </p>
  2193. <blockquote><p>Sets the required duration, in tenths of a second, of the remote modem’s carrier signal before recognition by your modem. (default: 6)</p></blockquote>
  2194. <p> On the 1670, register 9 reads back as 6, so it may as well be the same feature.</p>
  2195. </li>
  2196. <li>
  2197. <p>The registers just seem to map to the 128 bytes of RAM of one of the 80C49 microcontrollers, they wrap around at 128, e.g. 130 is the same as 2. Here is a complete dump after power-on; much of the data may be random, but note the current <code>AT</code> command (<code>S19?</code>) starting at register 0x11 (17):</p>
  2198. <pre><code>  00000000  00 00 2b 0d 0a 08 02 1e |..+.....|  
  2199.  00000008  02 06 07 46 32 00 02 00 |...F2...|  
  2200.  00000010  00 53 31 39 3f ff ff 00 |.S19?...|  
  2201.  00000018  12 01 00 00 00 00 02 04 |........|  
  2202.  00000020  20 00 00 04 00 00 20 a4 | ..... .|  
  2203.  00000028  00 80 00 00 00 00 50 01 |......P.|  
  2204.  00000030  00 00 00 00 00 00 04 00 |........|  
  2205.  00000038  20 80 00 00 02 00 00 00 | .......|  
  2206.  00000040  98 02 00 00 00 40 02 80 |.....@..|  
  2207.  00000048  05 00 10 00 00 7f 47 81 |......G.|  
  2208.  00000050  00 00 0a 00 86 10 02 63 |.......c|  
  2209.  00000058  d4 23 30 24 bd 63 6e 63 |.#0$.cnc|  
  2210.  00000060  00 08 00 00 00 00 04 00 |........|  
  2211.  00000068  6b 00 00 00 e5 66 00 08 |k....f..|  
  2212.  00000070  80 60 00 65 01 3b 00 07 |.`.e.;..|  
  2213.  00000078  01 80 65 00 00 0a 01 00 |..e.....|
  2214. </code></pre>
  2215. </li>
  2216. <li>
  2217. <p>Like all Bell 212A-compatible modems, when on a 1200 baud call, the data rate between the modem and the C64 is actually 1219 bits/sec. The manual states that it is necessary to specify this manual bitrate when using the KERNAL&rsquo;s software RS232 implementation: </p>
  2218. <pre>OPEN2,2,2,CHR$(0)+CHR$(0)+CHR$(61)+CHR$(1)</pre>
  2219. <p><a href="https://github.com/mist64/ccgmsterm">Modern versions of CCGMS</a> won&rsquo;t work with the 1670 for this reason.</p>
  2220. </li>
  2221. </ul>
  2222. <h2 id="open-questions">Open Questions</h2>
  2223. <ul>
  2224. <li>How to dump the ROM chip? I had no success reading it as a 2764..27512.</li>
  2225. <li>How to dump the ROM of the 80C49 microcontrollers?</li>
  2226. </ul>
  2227. ]]></content:encoded>
  2228. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1647</wfw:commentRss>
  2229. <slash:comments>2</slash:comments>
  2230. </item>
  2231. <item>
  2232. <title>The Commodore Modem/300 (Model 1660)</title>
  2233. <link>https://www.pagetable.com/?p=1644</link>
  2234. <comments>https://www.pagetable.com/?p=1644#comments</comments>
  2235. <pubDate>Wed, 02 Mar 2022 00:55:00 +0000</pubDate>
  2236. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  2237. <category><![CDATA[Archeology]]></category>
  2238. <category><![CDATA[C64]]></category>
  2239. <category><![CDATA[Commodore]]></category>
  2240. <category><![CDATA[Literature]]></category>
  2241. <category><![CDATA[Modem]]></category>
  2242.  
  2243. <guid isPermaLink="false">https://www.pagetable.com/?p=1644</guid>
  2244. <description><![CDATA[The Commodore 1660, also known as the &#8220;Modem/300&#8221;, is Commodore&#8217;s first full-featured modem: It connects directly to the phone line and supports pulse and tone dialing for 300 baud duplex connections. Historical Context Year Name Model Description 1982 VICMODEM 1600 connected to phone&#8217;s handset connector; manual dialing through phone; Motorola MC14412 1982 AUTOMODEM 1650 connected ... <a title="The Commodore Modem/300 (Model 1660)" class="read-more" href="https://www.pagetable.com/?p=1644">Read more<span class="screen-reader-text">The Commodore Modem/300 (Model 1660)</span></a>]]></description>
  2245. <content:encoded><![CDATA[<p>The Commodore 1660, also known as the &ldquo;Modem/300&rdquo;, is Commodore&rsquo;s first full-featured modem: It connects directly to the phone line and supports pulse and tone dialing for 300 baud duplex connections.</p>
  2246. <h2 id="historical-context">Historical Context</h2>
  2247. <table>
  2248. <thead>
  2249. <tr>
  2250. <th> Year </th>
  2251. <th> Name </th>
  2252. <th> Model </th>
  2253. <th> Description </th>
  2254. </tr>
  2255. </thead>
  2256. <tbody>
  2257. <tr>
  2258. <td> 1982 </td>
  2259. <td> <a href="https://www.pagetable.com/?p=1679">VICMODEM</a> </td>
  2260. <td> 1600 </td>
  2261. <td> connected to phone&rsquo;s handset connector; manual dialing through phone; Motorola MC14412 </td>
  2262. </tr>
  2263. <tr>
  2264. <td> 1982 </td>
  2265. <td> <a href="https://www.pagetable.com/?p=1716">AUTOMODEM</a> </td>
  2266. <td> 1650 </td>
  2267. <td> connected to phone line, pulse dialing in software; Motorola MC14412 </td>
  2268. </tr>
  2269. <tr>
  2270. <td> 1985 </td>
  2271. <td> <a href="https://www.pagetable.com/?p=1644">MODEM/300</a> </td>
  2272. <td> 1660 </td>
  2273. <td>added tone dialing support by feeding SID output into modem; Texas Instruments TMS99532A </td>
  2274. </tr>
  2275. <tr>
  2276. <td> 1987 </td>
  2277. <td> <a href="https://www.pagetable.com/?p=1647">MODEM/1200</a> </td>
  2278. <td> 1670 </td>
  2279. <td> Hayes command set; pulse and tone dialing in hardware; 300/1200 baud support; U.S. Robotics chipset </td>
  2280. </tr>
  2281. </tbody>
  2282. </table>
  2283. <h2 id="photos">Photos</h2>
  2284. <p><a href="docs/cbm1660modem/cbm1660_1.jpg"><img src="docs/cbm1660modem/cbm1660_1.jpg" height="325" width="459" alt="" /></a></p>
  2285. <p>On the front, there is the user port connector. On the left, there is a switch that can be put into the &ldquo;A&rdquo; (answer) or &ldquo;O&rdquo; (originate) position, depending whether a phone call is supposed to be made or accepted.</p>
  2286. <p><a href="docs/cbm1660modem/cbm1660_2.jpg"><img src="docs/cbm1660modem/cbm1660_2.jpg" height="287" width="457" alt="" /></a></p>
  2287. <p>On the back, there are</p>
  2288. <ul>
  2289. <li>two phone line connectors. &ldquo;LINE&rdquo; is connected to the telephone network, and an existing telephone can be connected to the &ldquo;PHONE&rdquo; line.</li>
  2290. <li>an RCA audio connector. The circuitry in the modem only does the 300 baud data transmission part once the telephone connection is established – tone dialing is done by using software to generate the audio using the C64&rsquo;s sound chip, which is looped into this connector! (Pulse dialing is also done in software, by timing on-hook and off-hook events.)</li>
  2291. </ul>
  2292. <p><a href="docs/cbm1660modem/back.jpg"><img src="docs/cbm1660modem/back.jpg" height="454" width="259" alt="" /></a></p>
  2293. <p>The label on the bottom says:</p>
  2294. <table>
  2295. <tr>
  2296. <td align="center">
  2297. C= commodore
  2298. </td>
  2299. <tr>
  2300. <tr>
  2301. <td>
  2302. MODEL NO. 1660
  2303. </td>
  2304. <tr>
  2305. <tr>
  2306. <td>
  2307. SERIAL NO. 158297
  2308. </td>
  2309. <tr>
  2310. <tr>
  2311. <td>
  2312. COMPLIES WITH PART 68, FCC RULES; FC<br /> <br />
  2313. REGISTRATION NUMBER BR 9608-15671-DM-E<br /> <br />
  2314. RINGER EQUIVALENT 0.4A 0.6B; JACK (USOC) RJ11<br />
  2315. </td>
  2316. <tr>
  2317. <tr>
  2318. <td align="center">
  2319. CERTIFIED TO COMPLY WITH CLASS B LIMITS,<br /> <br />
  2320. PART 15, SUBPART J OF FCC RULES. SEE<br /> <br />
  2321. INSTRUCTIONS IF INTERFERENCE TO RADIO<br /> <br />
  2322. RECEPTION IS SUSPECTED.
  2323. </td>
  2324. <tr>
  2325. <tr>
  2326. <td>
  2327. FCC ID BR 9608-1660<br /> <br />
  2328. MADE IN: HONG KONG <span style="float:right">310476-01</span>
  2329. </td>
  2330. <tr>
  2331. </table>
  2332. <p><a href="docs/cbm1660modem/board1.jpg"><img src="docs/cbm1660modem/board1.jpg" height="472" width="257" alt="" /></a><a href="docs/cbm1660modem/board2.jpg"><img src="docs/cbm1660modem/board2.jpg" height="454" width="244" alt="" /></a></p>
  2333. <p>The board is marked &ldquo;MAGIC MODEM TI-1660&rdquo; and &ldquo;ARTWORK NO. 310484 REV 4&rdquo;. The &ldquo;TI&rdquo; might stand for &ldquo;Texas Instruments&rdquo;, the manufacturer of the <a href="docs/cbm1660modem/TMS99532A.pdf">TMS99532A</a> modem chip on the top left of the board, which is the central component of the device.</p>
  2334. <p><a href="docs/cbm1660modem/speaker.jpg"><img src="docs/cbm1660modem/speaker.jpg" height="378" width="504" alt="" /></a></p>
  2335. <p>There is a speaker glued to the top shell that allows monitoring the audio data on the line.</p>
  2336. <p><a href="docs/cbm1660modem/phonecable.jpg"><img src="docs/cbm1660modem/phonecable.jpg" height="148" width="452" alt="" /></a></p>
  2337. <p>The modem comes with one phone cable.</p>
  2338. <p><a href="docs/cbm1660modem/dincable.jpg"><img src="docs/cbm1660modem/dincable.jpg" height="123" width="456" alt="" /></a><br />
  2339. <a href="docs/cbm1660modem/ycable.jpg"><img src="docs/cbm1660modem/ycable.jpg" height="150" width="488" alt="" /></a></p>
  2340. <p>And there are two cables that connect the C64&rsquo;s audio output to the modem:</p>
  2341. <ul>
  2342. <li>The DIN to RCA cable takes the audio signal from the C64/C128 AV connector. It is used if the C64 is connected to a TV through the RF connector, so the AV connector is available.</li>
  2343. <li>The RCA Y-cable takes the audio signal from the monitor cable. This is used if the C64 is connected to a monitor using the AV connector.</li>
  2344. </ul>
  2345. <p>Page 8 in the manual visualizes this:</p>
  2346. <p><img src="docs/cbm1660modem/connection1.png" height="204" width="300" alt="" /><img src="docs/cbm1660modem/connection2.png" height="204" width="300" alt="" /></p>
  2347. <h2 id="box">Box</h2>
  2348. <p><a href="docs/cbm1660modem/box_front.jpg"><img src="docs/cbm1660modem/box_front.jpg" height="453" width="293" alt="" /></a><a href="docs/cbm1660modem/box_back.jpg"><img src="docs/cbm1660modem/box_back.jpg" height="460" width="297" alt="" /></a><br />
  2349. <a href="docs/cbm1660modem/box_side.jpg"><img src="docs/cbm1660modem/box_side.jpg" height="93" width="453" alt="" /></a></p>
  2350. <h2 id="manuals">Manuals</h2>
  2351. <p><a href="docs/cbm1660modem/cbm1660modem_manual.pdf"><img src="docs/cbm1660modem/cbm1660modem_manual.jpg" height="326" width="215" alt="" /></a> <a href="docs/cbm1660modem/quantumlink_manual.pdf"><img src="docs/cbm1660modem/quantumlink_manual.jpg" height="306" width="212" alt="" /></a></p>
  2352. <h2 id="more-box-contents">More Box Contents</h2>
  2353. <p><a href="docs/cbm1660modem/QuantumLink%20Letter.pdf"><img src="docs/cbm1660modem/QuantumLink%20Letter.jpg" height="395" width="303" alt="" /></a> <a href="docs/cbm1660modem/QuantumLink%20Card.pdf"><img src="docs/cbm1660modem/QuantumLink%20Card.jpg" height="254" width="165" alt="" /></a> <a href="docs/cbm1660modem/QuantumLink%20Code.pdf"><img src="docs/cbm1660modem/QuantumLink%20Code.jpg" height="191" width="148" alt="" /></a></p>
  2354. <p><a href="docs/cbm1660modem/Important.pdf"><img src="docs/cbm1660modem/Important.jpg" height="305" width="198" alt="" /></a> <a href="docs/cbm1660modem/Damaged%20Disk.pdf"><img src="docs/cbm1660modem/Damaged%20Disk.jpg" height="222" width="176" alt="" /></a> <a href="docs/cbm1660modem/Warranty%20Card.pdf"><img src="docs/cbm1660modem/Warranty%20Card.png" height="427" width="215" alt="" /></a> <a href="docs/cbm1660modem/Magazine%20Ad.pdf"><img src="docs/cbm1660modem/Magazine%20Ad.jpg" height="307" width="197" alt="" /></a></p>
  2355. <p><a href="docs/cbm1660modem/Keyboard%20Overlay.pdf"><img src="docs/cbm1660modem/Keyboard%20Overlay.png" height="157" width="145" alt="" /></a></p>
  2356. <h2 id="disk">Disk</h2>
  2357. <p><img src="docs/cbm1660modem/disk_label_a.png" height="127" width="393" alt="" /> <a href="docs/cbm1660modem/cbm1660modem_a.d64"><img src="docs/d64.png" alt="" /></a></p>
  2358. <p><img src="docs/cbm1660modem/disk_label_b.png" height="126" width="389" alt="" /> <a href="docs/cbm1660modem/cbm1660modem_b.d64"><img src="docs/d64.png" alt="" /></a></p>
  2359. <h2 id="further-reading">Further Reading</h2>
  2360. <p><a href="https://archive.org/details/byte-magazine-1983-03-rescan/page/n27/mode/2up">Build the ECM-103, an Originate/Answer Modem</a>, Byte Magazine Volume 08 Number 03</p>
  2361. ]]></content:encoded>
  2362. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1644</wfw:commentRss>
  2363. <slash:comments>3</slash:comments>
  2364. </item>
  2365. <item>
  2366. <title>Announcing CCGMS Future 0.1</title>
  2367. <link>https://www.pagetable.com/?p=1641</link>
  2368. <comments>https://www.pagetable.com/?p=1641#comments</comments>
  2369. <pubDate>Fri, 25 Feb 2022 15:32:06 +0000</pubDate>
  2370. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  2371. <category><![CDATA[6502]]></category>
  2372. <category><![CDATA[C64]]></category>
  2373. <category><![CDATA[Commodore]]></category>
  2374. <category><![CDATA[GitHub]]></category>
  2375.  
  2376. <guid isPermaLink="false">https://www.pagetable.com/?p=1641</guid>
  2377. <description><![CDATA[The CCGMS Terminal Program for the Commodore 64 is maintained again, and there is a new version: CCGMS Future 0.1, with bug fixes and new features. History CCGMS has a rich history: It was originally written in 1985-1988 by Craig Smith, then binary patched by many people over the years, and finally maintained again by ... <a title="Announcing CCGMS Future 0.1" class="read-more" href="https://www.pagetable.com/?p=1641">Read more<span class="screen-reader-text">Announcing CCGMS Future 0.1</span></a>]]></description>
  2378. <content:encoded><![CDATA[<p>The CCGMS Terminal Program for the Commodore 64 is maintained again, and there is a new version: CCGMS Future 0.1, with bug fixes and new features.</p>
  2379. <p><img src="docs/ccgms/ccgms.png" height="272" width="384" alt="" /></p>
  2380. <h2 id="history">History</h2>
  2381. <p>CCGMS has a rich history: It was originally written in 1985-1988 by <a href="https://github.com/spathiwa/ccgmsterm">Craig Smith</a>, then <a href="https://commodore.software/downloads/category/59-ccgms">binary patched</a> by many people over the years, and finally maintained again by <a href="https://1200baud.wordpress.com">alwyz</a> from 2016 to 2020, based on the rediscovered source code.</p>
  2382. <h2 id="cleanup">Cleanup</h2>
  2383. <p>As a first step, I cleaned up the source of the last version (v2021), splitting it into multiple files, renaming symbols and adding comments. The resulting source uses cc65/ca65 to build and will generate a byte-for-byte identical v2021 PRG file – you can find this version in a <a href="https://github.com/mist64/ccgmsterm/tree/ccgmsterm2021">branch</a>.</p>
  2384. <h2 id="fixes">Fixes</h2>
  2385. <p>Then I started implementing fixes. In v2021, the standard user port driver was broken for PAL systems because of a bug in the lookup of the timings. Similarly, the UP9600 driver had a timing issue on PAL, but it was minor; but the fix may improve data transfer stability.</p>
  2386. <h2 id="features">Features</h2>
  2387. <p>Finally, I added features to the XMODEM transfer protocol:</p>
  2388. <ul>
  2389. <li>The <strong>XMODEM-1K</strong> protocol has been added. This increases the block size to 1 KB (instead of 128 bytes) and will significantly increase throughput. Both regular checksum and CRC are supported with XMODEM-1K, and since the protocol specifies that the sender decides on the block size, CCGMS will accept 128 bytes and 1 KB blocks on receive, no matter the setting.</li>
  2390. <li>The XMODEM protocol specifies that the receiver decides whether a simple checksum or CRC16 should be used. The original code would only accept its own settings on uploads. For example, if CCGMS was set to regular &ldquo;XMODEM&rdquo; (i.e. no CRC16) and the sender used the XMODEM-CRC protocol, the transfer would fail. This has been changed to always accept the sender&rsquo;s choice.</li>
  2391. </ul>
  2392. <p><img src="docs/ccgms/protocols.gif" height="272" width="384" alt="" /></p>
  2393. <p>Because of the added flexibility, the upload and download prompts are now a little clearer about the current settings:</p>
  2394. <ul>
  2395. <li><strong>XMODEM/XMODEM-CRC Upload</strong>: forces 128 B blocks, will accept checksum or CRC (more compatible)</li>
  2396. <li><strong>XMODEM-1K Upload</strong>: forces 1 KB blocks, will accept checksum or CRC (faster)</li>
  2397. <li><strong>XMODEM Download</strong>: forces checksum, will accept 128 B or 1 KB blocks (more compatible)</li>
  2398. <li><strong>XMODEM-CRC/XMODEM-1K Download</strong>: forces CRC16, will accept 128 B or 1 KB blocks (more reliable)</li>
  2399. </ul>
  2400. <p><img src="docs/ccgms/xmodem_up.gif" height="272" width="384" alt="" /><img src="docs/ccgms/xmodem_down.gif" height="272" width="384" alt="" /></p>
  2401. <h2 id="download">Download</h2>
  2402. <p>The .PRG files for this release are available on <a href="https://github.com/mist64/ccgmsterm/releases/tag/v0.1">GitHub</a>.</p>
  2403. <h2 id="future">Future</h2>
  2404. <p>I set up a <a href="https://github.com/mist64/ccgmsterm">GitHub repository</a> for the project, which is licensed under the terms of the 3-clause BSD license.</p>
  2405. <p>While I am working on further features, I am also more than happy to accept pull requests for features, bug fixes as well as clean up work!</p>
  2406. ]]></content:encoded>
  2407. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1641</wfw:commentRss>
  2408. <slash:comments>2</slash:comments>
  2409. </item>
  2410. <item>
  2411. <title>Free Joystick Extension Cable to Build Your DB9 Competition Pro</title>
  2412. <link>https://www.pagetable.com/?p=1636</link>
  2413. <comments>https://www.pagetable.com/?p=1636#respond</comments>
  2414. <pubDate>Wed, 02 Feb 2022 15:07:32 +0000</pubDate>
  2415. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  2416. <category><![CDATA[Commodore]]></category>
  2417. <category><![CDATA[Hardware]]></category>
  2418.  
  2419. <guid isPermaLink="false">https://www.pagetable.com/?p=1636</guid>
  2420. <description><![CDATA[This article explains how to convert a “Competition Pro Extra USB” (which you can still buy new) to work with a C64, Amiga or Atari. For the conversion, you need a joystick extension cable like this: I have a huge amount of them, with the wire colors described in the article, and I am happy ... <a title="Free Joystick Extension Cable to Build Your DB9 Competition Pro" class="read-more" href="https://www.pagetable.com/?p=1636">Read more<span class="screen-reader-text">Free Joystick Extension Cable to Build Your DB9 Competition Pro</span></a>]]></description>
  2421. <content:encoded><![CDATA[<p><a href="https://www.pagetable.com/?p=1097">This article</a> explains how to convert a “Competition Pro Extra USB” (which you can still buy new) to work with a C64, Amiga or Atari. For the conversion, you need a joystick extension cable like this:</p>
  2422. <p><img src="docs/competition_pro/joyext.jpg" height="312" width="600" alt="" /></p>
  2423. <p>I have a huge amount of them, with the wire colors described in the article, and I am happy to mail one (or more) to you for free within Europe. Reach out to <a href="mailto:mist64@mac.com">mist64@mac.com</a>/<a href="https://twitter.com/pagetable">@pagetable</a>.</p>
  2424. ]]></content:encoded>
  2425. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1636</wfw:commentRss>
  2426. <slash:comments>0</slash:comments>
  2427. </item>
  2428. <item>
  2429. <title>[UPDATE] Converting the “Competition Pro Extra USB” to C64/Amiga/Atari DB9</title>
  2430. <link>https://www.pagetable.com/?p=1612</link>
  2431. <comments>https://www.pagetable.com/?p=1612#respond</comments>
  2432. <pubDate>Thu, 06 Jan 2022 08:53:47 +0000</pubDate>
  2433. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  2434. <category><![CDATA[Commodore]]></category>
  2435. <category><![CDATA[Hardware]]></category>
  2436.  
  2437. <guid isPermaLink="false">https://www.pagetable.com/?p=1612</guid>
  2438. <description><![CDATA[I updated the instructions to a USB Competition Pro to DB9, so you can use it with a C64, Amiga etc. They now include the new “V3” and &#8220;V04T&#8221; pinouts and were updated with the use of a joystick extension cable.]]></description>
  2439. <content:encoded><![CDATA[<p>I updated the <a href="https://www.pagetable.com/?p=1097">instructions to a USB Competition Pro to DB9</a>, so you can use it with a C64, Amiga etc. They now include the new “V3” and &#8220;V04T&#8221; pinouts and were updated with the use of a joystick extension cable.</p>
  2440. <p><img src="/docs/competition_pro/competition_pro.jpg" width="600"></p>
  2441. ]]></content:encoded>
  2442. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1612</wfw:commentRss>
  2443. <slash:comments>0</slash:comments>
  2444. </item>
  2445. <item>
  2446. <title>The Ultimate Commodore 1541 Disk Drive Talk [video]</title>
  2447. <link>https://www.pagetable.com/?p=1595</link>
  2448. <comments>https://www.pagetable.com/?p=1595#comments</comments>
  2449. <pubDate>Thu, 16 Sep 2021 05:57:10 +0000</pubDate>
  2450. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  2451. <category><![CDATA[6502]]></category>
  2452. <category><![CDATA[Archeology]]></category>
  2453. <category><![CDATA[C64]]></category>
  2454. <category><![CDATA[Commodore]]></category>
  2455. <category><![CDATA[Floppy Disks]]></category>
  2456. <category><![CDATA[Presentation]]></category>
  2457.  
  2458. <guid isPermaLink="false">https://www.pagetable.com/?p=1595</guid>
  2459. <description><![CDATA[This is the video recording of “The Ultimate Commodore 1541 Disk Drive Talk” at VCF West 2021. As always, if you think it&#8217;s too fast, try watching it at 0.75x speed! I will post the slides in Apple Keynote format later. If you enjoyed this, you might also like my talks “Ultimate Apollo Guidance Computer ... <a title="The Ultimate Commodore 1541 Disk Drive Talk [video]" class="read-more" href="https://www.pagetable.com/?p=1595">Read more<span class="screen-reader-text">The Ultimate Commodore 1541 Disk Drive Talk [video]</span></a>]]></description>
  2460. <content:encoded><![CDATA[<p>This is the video recording of “The Ultimate Commodore 1541 Disk Drive Talk” at VCF West 2021. As always, if you think it&#8217;s too fast, try watching it at 0.75x speed!</p>
  2461. <p><iframe width="640" height="360" src="https://www.youtube.com/embed/_1jXExwse08?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
  2462. <p>I will post the slides in Apple Keynote format later.</p>
  2463. <p>If you enjoyed this, you might also like my talks</p>
  2464. <ul>
  2465. <li><a href="https://www.pagetable.com/?p=922">“Ultimate Apollo Guidance Computer Talk”</a></li>
  2466. <li><a href="http://www.pagetable.com/?p=877">“Ultimate Game Boy Talk”</a></li>
  2467. <li><a href="http://www.pagetable.com/?p=54">“Ultimate Commodore 64 Talk”</a></li>
  2468. <li><a href="http://www.pagetable.com/?p=517">“Reverse Engineering the MOS 6502 CPU”</a></li>
  2469. </ul>
  2470. ]]></content:encoded>
  2471. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1595</wfw:commentRss>
  2472. <slash:comments>12</slash:comments>
  2473. </item>
  2474. <item>
  2475. <title>[Announcement] The Ultimate Commodore 1541 Disk Drive Talk @ VCFW 2021</title>
  2476. <link>https://www.pagetable.com/?p=1590</link>
  2477. <comments>https://www.pagetable.com/?p=1590#comments</comments>
  2478. <pubDate>Fri, 06 Aug 2021 16:25:38 +0000</pubDate>
  2479. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  2480. <category><![CDATA[6502]]></category>
  2481. <category><![CDATA[C64]]></category>
  2482. <category><![CDATA[Commodore]]></category>
  2483. <category><![CDATA[Floppy Disks]]></category>
  2484. <category><![CDATA[Presentation]]></category>
  2485.  
  2486. <guid isPermaLink="false">https://www.pagetable.com/?p=1590</guid>
  2487. <description><![CDATA[After The Ultimate Commodore 64 Talk (2008) The Ultimate Game Boy Talk (2016) The Ultimate Apollo Guidance Computer Talk (2017) my fourth talk from the “Ultimate” series will take place at the Vintage Computer Festival West 2021 in Mountain View on 2021-08-08 at 12:00. This talk discusses floppy disk drives, with the 5,25” Commodore 1541 ... <a title="[Announcement] The Ultimate Commodore 1541 Disk Drive Talk @ VCFW 2021" class="read-more" href="https://www.pagetable.com/?p=1590">Read more<span class="screen-reader-text">[Announcement] The Ultimate Commodore 1541 Disk Drive Talk @ VCFW 2021</span></a>]]></description>
  2488. <content:encoded><![CDATA[<p>After</p>
  2489. <ul>
  2490. <li>The Ultimate Commodore 64 Talk (2008)</li>
  2491. <li>The Ultimate Game Boy Talk (2016)</li>
  2492. <li>The Ultimate Apollo Guidance Computer Talk (2017)</li>
  2493. </ul>
  2494. <p>my fourth talk from the “Ultimate” series will take place at the <a href="https://vcfed.org/wp/vcf-west-event-schedule/">Vintage Computer Festival West 2021 in Mountain View on 2021-08-08 at 12:00</a>.</p>
  2495. <p>This talk discusses floppy disk drives, with the 5,25” Commodore 1541 as a case study. We  discuss the history of magnetic recording formats, how data is represented on a disk and how it gets from the drive to the computer. We also talk about fast loaders, alternate recording formats, copy protection schemes, and how to preserve disks using modern tools.</p>
  2496. <p><img src="docs/vcfw-1541.png"/></p>
  2497. ]]></content:encoded>
  2498. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1590</wfw:commentRss>
  2499. <slash:comments>10</slash:comments>
  2500. </item>
  2501. <item>
  2502. <title>Commodore&#8217;s Assemblers: Part 5: 6502ASM</title>
  2503. <link>https://www.pagetable.com/?p=1542</link>
  2504. <comments>https://www.pagetable.com/?p=1542#respond</comments>
  2505. <pubDate>Sun, 13 Jun 2021 18:32:44 +0000</pubDate>
  2506. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  2507. <category><![CDATA[6502]]></category>
  2508. <category><![CDATA[Commodore]]></category>
  2509.  
  2510. <guid isPermaLink="false">https://www.pagetable.com/?p=1542</guid>
  2511. <description><![CDATA[In the series about the assemblers Commodore used for developing the ROMs of their 8-bit computers, this article covers the 1989 &#8220;Commodore 6502 Assembler&#8221; (6502ASM), a cross-assembler written in C that ran on VAX and PC. Series Overview Part 0: Overview Part 1: MOS Cross-Assembler Part 2: MOS Resident Assembler Part 3: BSO CY6502 Part 4: HCD65 Part 5: 6502ASM ← ... <a title="Commodore&#8217;s Assemblers: Part 5: 6502ASM" class="read-more" href="https://www.pagetable.com/?p=1542">Read more<span class="screen-reader-text">Commodore&#8217;s Assemblers: Part 5: 6502ASM</span></a>]]></description>
  2512. <content:encoded><![CDATA[<p>In the series about the assemblers Commodore used for developing the ROMs of their 8-bit computers, this article covers the 1989 &ldquo;Commodore 6502 Assembler&rdquo; (6502ASM), a cross-assembler written in C that ran on VAX and PC.</p>
  2513. <p><img src="docs/cbmasm/6502asm.png" alt="" /></p>
  2514. <h2 id="series-overview">Series Overview</h2>
  2515. <ul>
  2516. <li><a href="https://www.pagetable.com/?p=1518">Part 0: Overview</a></li>
  2517. <li><a href="https://www.pagetable.com/?p=1520">Part 1: MOS Cross-Assembler</a></li>
  2518. <li><a href="https://www.pagetable.com/?p=1522">Part 2: MOS Resident Assembler</a></li>
  2519. <li><a href="https://www.pagetable.com/?p=1538">Part 3: BSO CY6502</a></li>
  2520. <li><a href="https://www.pagetable.com/?p=1540">Part 4: HCD65</a></li>
  2521. <li>Part 5: <strong>6502ASM</strong> ← this article</li>
  2522. </ul>
  2523. <p><img src="docs/cbmasm/cbmasm_history.png" alt="" /></p>
  2524. <h2 id="history">History</h2>
  2525. <p>To build the ROMs for their 8-bit computers, Commodore originally used an assembler that ran on their own 6502-based machines (<a href="https://www.pagetable.com/?p=1522">part 2</a> of the series). From 1984 on, they used the &ldquo;Boston Systems Office&rdquo; (BSO) cross-assembler running on VAX/VMS (<a href="https://www.pagetable.com/?p=1538">part 3</a>).</p>
  2526. <p>In 1989, Commodore started working on the C65 project, a much enhanced successor to the C64. The C65 had a 4510 CPU, which supported the extended 65CE02 instruction set.</p>
  2527. <p>In order to be able to use the new 65CE02 instructions, they had several options:</p>
  2528. <ul>
  2529. <li>Write a set of macros that wrapped the new instructions: This worked well for some instructions, but required non-standard syntax for other cases. Dennis Jarvis, who developed the C65 DOS, started out with this approach while developing using the Merlin 128 assembler. Here is an example for the <a href="https://www.pagetable.com/c64ref/6502/?cpu=65ce02&amp;tab=2#INW"><code>INW</code></a> instruction:</li>
  2530. </ul>
  2531. <pre>
  2532.    INW MAC  
  2533.     DFB $E3 ;INW BP  
  2534.     DFB ]1  
  2535.     <<<  
  2536. </pre>
  2537. <ul>
  2538. <li>
  2539. <p>Add 65CE02 support to an existing assembler: Three years earlier, Commodore had written the HCD65 assembler for C128 (<a href="https://www.pagetable.com/?p=1540">part 4</a>), and they did in fact add 65CE02 support to it. Unfortunately, developing with HCD65 was slow and cumbersome.</p>
  2540. </li>
  2541. <li>
  2542. <p>Write a new assembler from scratch: Using modern tools on a modern platform, writing a new assembler would not be too much work.</p>
  2543. </li>
  2544. </ul>
  2545. <p>The &ldquo;Commodore 6502 Assembler&rdquo; (6502ASM") is a 6502/65CE02 cross-assembler written in C, <a href="https://www.amazon.com/Commodore-Final-Years-Brian-Bagnall/dp/0994031033">written by Bob Norby</a> of Commodore Semiconductor Group (CSG, formerly MOS). It aimed at full compatibility with the &ldquo;BSO&rdquo; cross-assembler on VAX (<a href="https://www.pagetable.com/?p=1538">part 3</a>), which Commodore had been using for all their projects before. It was developed on VAX (using VAX C V2.4-026) to allow for easy comparisons of its outputs with BSO&rsquo;s.</p>
  2546. <p>In June 1989, Fred Bowen ported the source to MS-DOS. An Amiga version was mentioned in the 1991 C65 specification, but it is unclear whether the source was ever actually ported to an Amiga C compiler.</p>
  2547. <h2 id="usage">Usage</h2>
  2548. <p>The 6502ASM was closely modeled after the BSO assembler, so it used the same command line interface. The user could pass the arguments either as command line options, or type it into a prompt, if no command line arguments were specified:</p>
  2549. <pre><code>C65&gt;PROG.OBJ,PROG.LST=PROG.SRC
  2550. </code></pre>
  2551. <p><code>C65&gt;</code> is the prompt printed by the assembler. It matches the prompt of the BSO assembler, and symbolizes &ldquo;<strong>C</strong>ross assembler for <strong>65</strong>02&rdquo; – it has nothing to do with the Commodore C65 project.</p>
  2552. <p>This is the help text that is printed if <code>/H</code> is passed:</p>
  2553. <pre><code>the assembler command line consists of the file names and switches
  2554.  
  2555. C65&gt; [object],[listing]=source[,source]...[,source][/switch]...[/switch]
  2556.  
  2557. Items enclosed in brackets [...] are optional.
  2558.  
  2559. The default file extentions are .obj , .lst and .src
  2560.  
  2561. Switches  (either upper or lower case)  
  2562. /A absolute assembly (default)  
  2563. /Cn cpu instruction set /C0 for NMOS 6502   /C1 for CMOS 6502  
  2564.     /C2 for CMOS 6502 w/bit instructions   /C3 for Commodore 4502 (default)  
  2565. /Dpath specify path for intermediate file ( usually RAM disk on PCs).  
  2566. /H help - prints this message  
  2567. /L assume long branches on pass1. (default= assume short branches)  
  2568. /Mnnn maximum macro nesting depth (default=50, limits=2-999).  
  2569. /Pnn maximum number of passes to try (default=15, limits=2-99).  
  2570. /N don't print errors to console during assembly  
  2571. /R relocatable assembly - illegal since this is an absolute assembler only  
  2572. /S narrow list format  
  2573. /T don't print symbol table  
  2574. /V don't print cross reference  
  2575. /X print cross reference (default)
  2576. </code></pre>
  2577. <p>Details on the usage as well as the supported syntax are described in the <a href="https://github.com/mist64/cbm6502asm/blob/main/README.md">unofficial manual</a>.</p>
  2578. <h2 id="differences">Differences</h2>
  2579. <p>6502ASM is very similar to the BSO assembler. It supports the same directives, and uses the same syntax for local symbols, conditional assembly and macros.</p>
  2580. <p>The only real difference seems to be the lack of the <code>.IF</code> directive, just like with HCD65. <code>.IF</code> seems to have been undocumented on the BSO assembler, which would explain its omission from both HCD65 and 6502ASM. That BSO supported it and treated it as a synonym for <code>.IFN</code> can be seen from its use in the <a href="https://github.com/mist64/cbmsrc/blob/master/KERNAL_TED_04/vectors.src#L9">TED KERNAL source</a>.</p>
  2581. <h2 id="versions">Versions</h2>
  2582. <h3 id="6502asm-b0.0-(1989)">6502ASM B0.0 (1989)</h3>
  2583. <p>This is the MS-DOS binary of version B0.0, built with Microsoft C 5.00 for MS-DOS (<a href="https://sites.google.com/site/pcdosretro/mschist">1987</a>):</p>
  2584. <ul>
  2585. <li><a href="docs/cbmasm/6502asm_msdos.zip">6502ASM B0.0</a> (1989-06-14, 67233 bytes, from <a href="http://6502.org/users/sjgray/dj/">DJ</a> 4502-asm-for-pc-7-89.img)</li>
  2586. </ul>
  2587. <p>This version has a few bugs:</p>
  2588. <ul>
  2589. <li>A <code>;</code> character in a string terminates it.</li>
  2590. <li>
  2591. <p>If an operand is 8 bits wide and the instruction does not support the zero-page addressing mode (i.e. <a href="https://www.pagetable.com/c64ref/6502/?cpu=65ce02&amp;tab=2#LDZ"><code>LDZ</code></a> on the 65CE02), the assembler does not support falling back to absolute addressing. The following code would fail:</p>
  2592. <p>  <tt>ldz $80</tt></p>
  2593. <p>  This issue prevents some C65 source code from building.</p>
  2594. </li>
  2595. <li>
  2596. <p>If a macro is used within an <code>.INCLUDE</code>d file, assembly is stopped after the current file. <a href="http://www.zimmers.net/anonftp/pub/cbm/src/drives/serlib.zip">serlib.zip</a> on zimmers.net contains an LST file generated by 6502ASM B0.0 from the original 1581 source that shows this problem. The LST file is incomplete; it ends after the included file &ldquo;mrout&rdquo;, and the remaining LST therefore shows 150 undefined symbols.</p>
  2597. </li>
  2598. <li>Projects like <a href="https://github.com/mist64/cbmsrc/tree/master/RAMDOS">RAMDOS</a> (a solution for exposing a RAM Expansion Units (REU) as a disk), the <a href="https://github.com/mist64/cbmsrc/tree/master/HCD65_3.5">HCD65 assembler</a> and its editor (<a href="https://github.com/mist64/cbmsrc/tree/master/EDT_C128">EDT_C128</a>) as well as <a href="https://github.com/mist64/cbmsrc/tree/master/DOS_SHELL">DOS_SHELL</a> use BSO assembler features extensively. 6502ASM fails to build it for various reasons.</li>
  2599. </ul>
  2600. <p>The source code of 6502ASM B0.0 has been preserved through <a href="http://6502.org/users/sjgray/dj/">DJ</a> 4502-asm-for-pc.img.</p>
  2601. <h3 id="6502asm-v1.0-(1990)">6502ASM V1.0 (1990)</h3>
  2602. <p>Version V1.0 fixes a few bugs, including the <code>;</code> and <code>LDZ</code> problems.</p>
  2603. <p>LST files of the source are included in <a href="http://www.zimmers.net/anonftp/pub/cbm/src/c65/c65_src.tar.gz">c65_src.tar.gz</a>. The reconstructed source is available as part of the <a href="https://github.com/mist64/cbmsrc">cbmsrc repository</a>.</p>
  2604. <h3 id="current-version-(2021)">Current Version (2021)</h3>
  2605. <p>I adapted version V1.0 to current compilers so that it builds and runs on modern Unix systems. It is available on GitHub:</p>
  2606. <p><a href="https://github.com/mist64/cbm6502asm">https://github.com/mist64/cbm6502asm</a></p>
  2607. <p><code>.IF</code> has been added and the bug with macros within <code>.INCLUDE</code>s has been fixed. It can currently compile all known Commodore BSO source code, with the exceptions mentioned above. Contributions are welcome!</p>
  2608. <h2 id="use-at-commodore">Use at Commodore</h2>
  2609. <p>At the beginning of the C65 project, three different assemblers had been in use at Commodore:</p>
  2610. <ul>
  2611. <li>Dennis Jarvis (DOS) originally had used Merlin 128, with added 65CE02 macros.</li>
  2612. <li>Fred Bowen (KERNAL, BASIC) had originally used the BSO assembler, possibly also with added 65CE02 macros.</li>
  2613. <li>The external contractor Walrus Software Inc. (graphics extensions for BASIC) had used HCD65.</li>
  2614. </ul>
  2615. <p>All development was switched to 6502ASM in mid-1990.</p>
  2616. <p>The A2232 7-port serial card for the Amiga also contained a 65CE02 CPU. The project started in 1988, using the BSO assembler. The final version was built with 6502ASM on VAX<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>.</p>
  2617. <hr />
  2618. <p>This marks the end of the 5-part series on the assemblers used by Commodore.</p>
  2619. <div class="footnotes">
  2620. <hr/>
  2621. <ol>
  2622. <li id="fn:1">
  2623. <p>The Amiga makefile in the driver source does not build the 65CE02 source, but only links an already built .OBJ file, which is a strong indication that 6502ASM did not in fact exist on the Amiga, and the source was built on VAX and transferred to the Amiga instead.<a href="#fnref:1" rev="footnote">&#8617;</a></p>
  2624. </li>
  2625. </ol>
  2626. </div>
  2627. ]]></content:encoded>
  2628. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1542</wfw:commentRss>
  2629. <slash:comments>0</slash:comments>
  2630. </item>
  2631. <item>
  2632. <title>Commodore&#8217;s Assemblers: Part 4: HCD65</title>
  2633. <link>https://www.pagetable.com/?p=1540</link>
  2634. <comments>https://www.pagetable.com/?p=1540#comments</comments>
  2635. <pubDate>Sun, 06 Jun 2021 04:31:41 +0000</pubDate>
  2636. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  2637. <category><![CDATA[6502]]></category>
  2638. <category><![CDATA[Commodore]]></category>
  2639.  
  2640. <guid isPermaLink="false">https://www.pagetable.com/?p=1540</guid>
  2641. <description><![CDATA[In the series about the assemblers Commodore used for developing the ROMs of their 8-bit computers, this article covers the 1987 &#8220;HCD65&#8221; assembler that ran on the C128. Series Overview Part 0: Overview Part 1: MOS Cross-Assembler Part 2: MOS Resident Assembler Part 3: BSO CY6502 Part 4: HCD65 ← this article Part 5: 6502ASM History To build the ROMs for ... <a title="Commodore&#8217;s Assemblers: Part 4: HCD65" class="read-more" href="https://www.pagetable.com/?p=1540">Read more<span class="screen-reader-text">Commodore&#8217;s Assemblers: Part 4: HCD65</span></a>]]></description>
  2642. <content:encoded><![CDATA[<p>In the series about the assemblers Commodore used for developing the ROMs of their 8-bit computers, this article covers the 1987 &ldquo;HCD65&rdquo; assembler that ran on the C128.</p>
  2643. <p><img src="docs/cbmasm/hcd65.png" alt="" /></p>
  2644. <h2 id="series-overview">Series Overview</h2>
  2645. <ul>
  2646. <li><a href="https://www.pagetable.com/?p=1518">Part 0: Overview</a></li>
  2647. <li><a href="https://www.pagetable.com/?p=1520">Part 1: MOS Cross-Assembler</a></li>
  2648. <li><a href="https://www.pagetable.com/?p=1522">Part 2: MOS Resident Assembler</a></li>
  2649. <li><a href="https://www.pagetable.com/?p=1538">Part 3: BSO CY6502</a></li>
  2650. <li><strong>Part 4: HCD65</strong> ← this article</li>
  2651. <li><a href="https://www.pagetable.com/?p=1542">Part 5: 6502ASM</a></li>
  2652. </ul>
  2653. <p><img src="docs/cbmasm/cbmasm_history.png" alt="" /></p>
  2654. <h2 id="history">History</h2>
  2655. <p>To build the ROMs for their 8-bit computers, Commodore originally used an assembler that ran on their own PET machines (<a href="https://www.pagetable.com/?p=1522">part 2</a> of the series). From 1984 on, they used the &ldquo;Boston Systems Office&rdquo; (BSO) cross-assembler running on VAX/VMS (<a href="https://www.pagetable.com/?p=1538">part 3</a>).</p>
  2656. <p>In 1986, Commodore started working on a clone of the BSO cross-assembler that was supposed to run on the C128. An internal document (<code>c65.doc</code>) in the <a href="http://www.zimmers.net/anonftp/pub/cbm/src/c128/c128_dev_pack.tar.gz">original C128 source archive</a> states:</p>
  2657. <blockquote>
  2658. <p>HCD65XX is a powerful macro assembler syntactically identical to the assembler used to orginally develop the C128 source code running on a VAX-8600 under VMS. This tool is capable of assembling the same source files on a C128.</p>
  2659. </blockquote>
  2660. <p>They announced the project in a <a href="https://archive.org/download/usenet-net">usenet post to net.micro.cbm</a> from April 1986:</p>
  2661. <blockquote>
  2662. <p>There is a package developed in house that includes a macro assembler and a program editor that both take advantage of the C128 hardware.  [&hellip;] the object was to make it do everything that the VAX based cross assemblers can do, and to allow truely large assemblies.  The editor is similar to the DEC EDT keypad editing mode.</p>
  2663. <p>It is too soon to know when or if this package will be available, but I am sure that it will be publicized on the various networks&hellip;</p>
  2664. </blockquote>
  2665. <p>It was mentioned again in a post from October 1986:</p>
  2666. <blockquote>
  2667. <p>We have a developers package containing a new macro assembler and editor that we hope to have available by year end.  The assembler is compatible with the BSO 6502 Assembler that has been used by Commodore for software development of late.</p>
  2668. </blockquote>
  2669. <p>The new assembler, called &ldquo;HCD65&rdquo; (in the manual) or &ldquo;HCD65XX&rdquo; (in the software), named after its developer Hedley Davis, was released in 1987 as part of the <a href="https://commodore.software/downloads/download/121-general-programming-tools/11956-commodore-128-developer-s-package">Commodore 128 Developer&rsquo;s Package</a> (<a href="ftp://www.zimmers.net/pub/cbm/schematics/computers/c128/servicemanuals/C128_Developers_Package_for_Commodore_6502_Development_%281987_Oct%29.pdf">manual</a>, <a href="docs/cbmasm/hcd65.txt">internal</a>, <a href="docs/cbmasm/asm65ce02.txt">internal (65CE02 version)</a>).</p>
  2670. <h2 id="usage">Usage</h2>
  2671. <p>The assembler consists of two components: A BASIC program to query the arguments from the user, and the non-interactive assembler binary. The BASIC frontend presents the following menu:</p>
  2672. <pre><code>(C)1986 COMMODORE ELECTRONICS, LTD.  
  2673. ALL RIGHTS RESERVED     V3.5  
  2674. ENTER FILE NAME FOR SOURCE = KERNAL.SRC
  2675.  
  2676. PICK A CONFIGURATION  
  2677. 0) NO LISTING         : SOURCE,OBJ ON 8  
  2678. 1) LISTING TO SCREEN  : SOURCE,OBJ ON 8  
  2679. 2) LISTING TO UNIT 4  : SOURCE,OBJ ON 8  
  2680. 3) ERRORS ONLY        : SOURCE     ON 8  
  2681. 4) LIST/XREF TO UNIT 4: SOURCE,OBJ ON 8  
  2682. 5) NO LISTING         : SOURCE,OBJ ON 9  
  2683. 6) LISTING TO SCREEN  : SOURCE,OBJ ON 9  
  2684. 7) PRINT LISTING ON 4 : SOURCE,OBJ ON 9  
  2685. 8) ERRORS ONLY        : SOURCE     ON 9  
  2686. 9) LIST/XREF TO UNIT 4: SOURCE,OBJ ON 9
  2687. </code></pre>
  2688. <p>It allows entering a filename (the extension <code>.SRC</code> is added automatically) and configuration options that control whether to hide/show/print the LST, whether to generate a cross-reference and what drive to read the sources from. Pressing RETURN in the menu will allow the user to configure all options manually.</p>
  2689. <p>It then asks for the date string, since the C128 does not have a real-time clock, and asks the user to verify the settings:</p>
  2690. <pre><code>(C)1986 COMMODORE ELECTRONICS, LTD.  
  2691. ALL RIGHTS RESERVED     V3.5  
  2692. ASSEMBLY OF     KERNAL.SRC  
  2693. SOURCE FILES ON UNIT 9
  2694.  
  2695. LISTING OUTPUT = PRINTER  
  2696.   NO CROSS REFERENCE  
  2697.   DATE STRING ="2021-05-08"
  2698.  
  2699. ERROR OUTPUT = SCREEN  
  2700. OBJECT OUTPUT = KERNAL.OBJ ON UNIT  9
  2701.  
  2702. PRINTER DEVICE NUMBER =  4  
  2703. LIST AND ERROR FILE WIDTH = 80 COLUMNS
  2704.  
  2705.  
  2706. RUN THIS CONFIGURATION ? (Y/N)
  2707. </code></pre>
  2708. <p>Pressing <code>Y</code> will start the assembly.</p>
  2709. <h2 id="differences">Differences</h2>
  2710. <p>HCD65 was designed to be as similar to BSO as possible, but there are a few differences:</p>
  2711. <ul>
  2712. <li>HCD65 accepts both ASCII and PETSCII source files. When using existing BSO source files, it is important that the source files use CR line breaks and that the filenames referenced by <code>.INCLUDE</code> have the right case.</li>
  2713. <li>HCD65 is missing the <code>.IF</code> directive that BSO understands as a synonym to <code>.IFN</code>.</li>
  2714. </ul>
  2715. <h2 id="versions">Versions</h2>
  2716. <p>There are four known versions of the HCD65 assembler:</p>
  2717. <h3 id="hcd65xx-v3.1">HCD65XX V3.1</h3>
  2718. <p>Little is known about this version. The LST files that shipped with the C128 Devpack were built with this version.</p>
  2719. <h3 id="hcd65xx-v3.5">HCD65XX V3.5</h3>
  2720. <p>This is the version that shipped as part of the C128 Devpack.</p>
  2721. <ul>
  2722. <li><a href="docs/cbmasm/hcd65_v3.5.zip">HCD65XX V3.5</a> (BAS: 6613 bytes, BIN: 10498 bytes, from <a href="http://6502.org/users/sjgray/dj/">DJ</a> devpack1of4.d64)</li>
  2723. </ul>
  2724. <h3 id="hcd65xx-65ce02-v0.1">HCD65XX 65CE02 V0.1</h3>
  2725. <p>In 1989, Commodore started writing the ROM for the upcoming C64DX/C65. The CPU of the machine was a 4510, which implemented the extended 65CE02 instruction set, so in order to use the additional CPU features, they needed an assembler that supported them.</p>
  2726. <p>The BSO/VAX cross-assembler that they had been using for all development at the time was written by a third party and Commodore did not have its source, so Fred Bowen added support to the C128 HCD65.</p>
  2727. <ul>
  2728. <li><a href="docs/cbmasm/hcd65_65ce02_v0.1.bin">HCD65XX 65CE02 V0.1</a> (BIN: 11166 bytes, built from the source using HCD65XX V3.5)</li>
  2729. </ul>
  2730. <h3 id="hcd65xx-65ce02-v0.2">HCD65XX 65CE02 V0.2</h3>
  2731. <p>Version 0.2 is changes the AUG/MAP opcode from 65CE02 to the 4510 semantics, and adds an alternative syntax to the BBR/BBS mnemos.</p>
  2732. <ul>
  2733. <li><a href="docs/cbmasm/hcd65_65ce02_v0.2.zip">HCD65XX 65CE02 V0.2</a> (BAS: 6769 bytes, BIN: 11156 bytes, from <a href="http://6502.org/users/sjgray/dj/">DJ</a> asm45.d81)</li>
  2734. </ul>
  2735. <h2 id="source">Source</h2>
  2736. <p>The sources of the V3.5 and 65CE02 0.1 and 0.2 versions are available as part of the Commodore Source collection:</p>
  2737. <p><a href="https://github.com/mist64/cbmsrc">https://github.com/mist64/cbmsrc</a></p>
  2738. <ul>
  2739. <li>HCD65_3.5</li>
  2740. <li>HCD65_65CE02_0.1</li>
  2741. <li>HCD65_65CE02_0.2</li>
  2742. </ul>
  2743. <h2 id="use-at-commodore">Use at Commodore</h2>
  2744. <p>It is unclear why Commodore developed HCD65 in the first place.</p>
  2745. <p>It is unlikely that they developed HCD65 mainly for a commercial release. There were plenty of assemblers on the market, and BSO compatibility is not something users were looking for.</p>
  2746. <p>Another hypothesis would be that they wanted to replace the BSO cross-assembler for in-house use.</p>
  2747. <p>In 1986, they were using BSO on a 12.5 MHz VAX-8600. The original PDP-10 version of BSO&rsquo;s tools were implemented in hand-written machine code, but the later PDP-11/VAX/ULTRIX versions were probably written in a high-level language. There might have been a chance that a new assembler written in well-optimized machine code for the 2 MHz C128 could have matched the speed of the BSO assembler, but the C128&rsquo;s I/O system (even using the new Fast Serial bus) would have certainly ruined that.</p>
  2748. <p>So they wouldn&rsquo;t have replaced BSO for a speed benefit or added convenience. Original LST files that have been preserved show that Commodore used BSO, not HCD65, for all 6502-based projects until 1990 (C64GS).</p>
  2749. <p>One realistic reason for developing HCD65 would be as an insurance policy: to have an in-house alternative to BSO, so they would not be dependent on a third party. The in-house solution could also be altered and extended, especially in the context of the upcoming 4510/65CE02 CPU. The 65CE02 project, an effort to re-design the 6502 in CMOS, <a href="https://www.amazon.com/Commodore-Final-Years-Brian-Bagnall/dp/0994031033">started in late 1985</a>. And in fact, 4510/65CE02 support was added to HCD65 in 1989.</p>
  2750. <p>The graphics extensions to BASIC 10 by the external contractor Walrus Software Inc. were developed using HCD65 65CE02, and so was the C65 DOS ROM for a while, before all development switched to the &ldquo;Commodore 6502ASM&rdquo; cross-assembler in 1990.</p>
  2751. <p>The next article will discuss this new cross-assembler that Commodore developed in 1989.</p>
  2752. ]]></content:encoded>
  2753. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1540</wfw:commentRss>
  2754. <slash:comments>1</slash:comments>
  2755. </item>
  2756. <item>
  2757. <title>Commodore&#8217;s Assemblers: Part 3: BSO CY6502</title>
  2758. <link>https://www.pagetable.com/?p=1538</link>
  2759. <comments>https://www.pagetable.com/?p=1538#comments</comments>
  2760. <pubDate>Sat, 29 May 2021 17:37:59 +0000</pubDate>
  2761. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  2762. <category><![CDATA[6502]]></category>
  2763. <category><![CDATA[Commodore]]></category>
  2764.  
  2765. <guid isPermaLink="false">https://www.pagetable.com/?p=1538</guid>
  2766. <description><![CDATA[In the series about the assemblers Commodore used for developing the ROMs of their 8-bit computers, this article covers the 1984 &#8220;Boston Systems Office&#8221; (BSO) cross-assembler running on VAX/VMS. 6502 relocating cross assembler version 20.53.12 Copyright, The Boston Systems Office, Inc. 1978 (617) 894-7800 TWX: 710-324-0760 Type /H for help Series Overview Part 0: Overview Part ... <a title="Commodore&#8217;s Assemblers: Part 3: BSO CY6502" class="read-more" href="https://www.pagetable.com/?p=1538">Read more<span class="screen-reader-text">Commodore&#8217;s Assemblers: Part 3: BSO CY6502</span></a>]]></description>
  2767. <content:encoded><![CDATA[<p>In the series about the assemblers Commodore used for developing the ROMs of their 8-bit computers, this article covers the 1984 &ldquo;Boston Systems Office&rdquo; (BSO) cross-assembler running on VAX/VMS.</p>
  2768. <pre><code>6502 relocating cross assembler version 20.53.12  
  2769. Copyright, The Boston Systems Office, Inc. 1978  
  2770. (617) 894-7800   TWX: 710-324-0760  
  2771. Type /H for help
  2772. </code></pre>
  2773. <h2 id="series-overview">Series Overview</h2>
  2774. <ul>
  2775. <li><a href="https://www.pagetable.com/?p=1518">Part 0: Overview</a></li>
  2776. <li><a href="https://www.pagetable.com/?p=1520">Part 1: MOS Cross-Assembler</a></li>
  2777. <li><a href="https://www.pagetable.com/?p=1522">Part 2: MOS Resident Assembler</a></li>
  2778. <li><strong>Part 3: BSO CY6502</strong> ← this article</li>
  2779. <li><a href="https://www.pagetable.com/?p=1540">Part 4: HCD65</a></li>
  2780. <li><a href="https://www.pagetable.com/?p=1542">Part 5: 6502ASM</a></li>
  2781. </ul>
  2782. <p><img src="docs/cbmasm/cbmasm_history.png" alt="" /></p>
  2783. <h2 id="history">History</h2>
  2784. <p>Commodore bought the chip-maker MOS in 1976 and with it its development tools: They used the so-called MOS &ldquo;Resident Assembler&rdquo; (<a href="https://www.pagetable.com/?p=1522">part 2</a> of the series) – first on the MDT650 and later on the PET – to develop for machines like the VIC-20 and the C64, and for disk drives like the 8250 and the 1541.</p>
  2785. <p>In 1984, they switched to a cross-assembler by the company &ldquo;Boston Systems Office&rdquo; (BSO).</p>
  2786. <h2 id="boston-systems-office">Boston Systems Office</h2>
  2787. <p>Boston Systems Office had been specializing on cross-development tools since 1975, offering dozens of cross-assemblers for <a href="https://ia803207.us.archive.org/20/items/bitsavers_tymsharetygramIndexRev11May77_1215271/Tymcom-X_User_Program_Index_Rev11_May77_text.pdf">Tymshare</a> systems that were hand-written in PDP-10 assembly for maximum speed, and that aimed at full compatibility with the CPU-makers&#8217; own tools.</p>
  2788. <p>Since 1976, they had been offering a 6502 assembler. The 1979 version of the BSO 6502 cross-assembler, called &ldquo;CA6500&rdquo; (<a href="https://github.com/TYMCOM-X/169273.tape/blob/abc68e373db6be0104efe986d23624462ef691b9/*6news/ca6502.doc">documentation</a>, <a href="https://github.com/TYMCOM-X/169273.tape/blob/abc68e373db6be0104efe986d23624462ef691b9/sys/ca6502.sav">PDP-10 binary</a>) was highly compatible with the original MOS assemblers, and had added a few features.</p>
  2789. <p>According to <a href="https://archive.org/details/AtariOperatingSystemUsersManualAugust1980">internal documentation</a>, Atari had used CA6500 on a PDP-11 for their in-house 6502 development as early as mid-1980.</p>
  2790. <p>In early 1984, BSO <a href="https://archive.org/details/computerworld1816unse/page/46/mode/2up?q=cy65xx">released</a> a version for PDP-11 and VAX/VMS named CY65XX<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>, which was then licensed by Commodore. This version had many additional features added.</p>
  2791. <p>No binary of the assembler seems to be archived anywhere, but we do have the manual of the last version Commodore used:</p>
  2792. <ul>
  2793. <li><a href="docs/cbmasm/cy6502.txt">CY6502 Version 20.53.12 Manual</a> (1985)</li>
  2794. </ul>
  2795. <h2 id="usage">Usage</h2>
  2796. <p>When run, the BSO assembler shows the following prompt:</p>
  2797. <pre><code>6502 relocating cross assembler version 20.53.12  
  2798. Copyright, The Boston Systems Office, Inc. 1978  
  2799. (617) 894-7800   TWX: 710-324-0760  
  2800. Type /H for help  
  2801. C65&gt;
  2802. </code></pre>
  2803. <p>The output file (ABS) file, the LST file and the SRC file would be specified like this:</p>
  2804. <pre><code>C65&gt;PROG.ABS,PROG.LST=PROG.SRC
  2805. </code></pre>
  2806. <p>The file name extensions are optional. If they are omitted, the defaults are used.</p>
  2807. <p>The output file is in BSOs&#8217; own &ldquo;ABS&rdquo; format, and needs another conversion step using the tool OBJCNV:</p>
  2808. <pre><code>Object file converter  Version 3.25  
  2809. Copyright, The Boston Systems Office, Inc. 1978  
  2810. TEL: (617) 894-7800 TWX: 710-324-0760  
  2811. Type /H for help  
  2812. &gt;
  2813. </code></pre>
  2814. <p>To convert the resulting ABS file into the MOS &ldquo;OBJ&rdquo; format, you would type this at the prompt:</p>
  2815. <pre><code>&gt;PROG.OBJ/F:MTK=PROG.ABS/F:BSO
  2816. </code></pre>
  2817. <p><code>MTK</code> stands for MOSTEK, a chipmaker unrelated to MOS Technology, Inc. BSO seems to have had confused the two companies: ABS files also contain the string <code>6502,MOSTEK</code>.</p>
  2818. <p>In practice, one would use a DCL script to invoke both tools with the right arguments.</p>
  2819. <h2 id="differences-to-original-mos-spec">Differences to Original MOS Spec</h2>
  2820. <p>The BSO assembler diverged in two aspects from the original MOS syntax:</p>
  2821. <ul>
  2822. <li>Labels have to start at column 1, so lines with labels cannot have any leading whitespace. But it is still legal for statements to also start on column 1: The assembler can tell them apart, since all mnemos are reserved keywords, which can&rsquo;t be used as labels.</li>
  2823. <li>The <code>.OPT</code> directive is not supported. Several of the options are supported through dedicated directives though. See below.</li>
  2824. </ul>
  2825. <h2 id="differences-to-updated-resident-assembler">Differences to Updated Resident Assembler</h2>
  2826. <p>The syntax of both the Resident Assembler and the BSO cross-assembler are based on the original MOS syntax, but some features that are supported by later versions of both have diverged.</p>
  2827. <h3 id="include-syntax">Include Syntax</h3>
  2828. <p>The Resident Assembler uses the <code>.LIB</code> directive to include source files. The BSO assembler uses the <code>.INCLUDE</code> directive:</p>
  2829. <pre><code>.include disclaimer
  2830. </code></pre>
  2831. <h3 id="macro-syntax">Macro Syntax</h3>
  2832. <p>Macros for the BSO assembler look like this:</p>
  2833. <pre><code>dpinc  .macro arg    ;double precision increment  
  2834.       inc arg  
  2835.       bne 1$  
  2836.       inc arg+1  
  2837. 1$     .endm
  2838. </code></pre>
  2839. <p>The name of the macro is defined as a label. Arguments can be any string of characters after the <code>.MACRO</code> directive, and are blindly searched and replaced in the macro&rsquo;s body. <code>.ENDM</code> marks the end of the body.</p>
  2840. <h3 id="conditional-assembly-syntax">Conditional Assembly Syntax</h3>
  2841. <p>Conditional assembly on the BSO assembler uses an <code>.IF</code>/<code>.ELSE</code>/<code>.ENDIF</code> construct, like this:</p>
  2842. <pre><code>.ifn gdebug  
  2843.       jsr grbpri      ;garbage debug  
  2844. .endif
  2845. </code></pre>
  2846. <h2 id="new-features">New Features</h2>
  2847. <p>There are a number of additional features supported by the BSO assembler compared to the original MOS assemblers.</p>
  2848. <h3 id="text-format">Text Format</h3>
  2849. <p>Source files can now use the complete ASCII character set, as opposed to just uppercase. This means that comments can use mixed case and that TABs can be used for indenting.</p>
  2850. <p>In addition, labels, statements and operands are now case insensitive. In practice, all Commodore BSO source code uses lower case for those.</p>
  2851. <h3 id="local-labels">Local Labels</h3>
  2852. <p>Local labels are in the form of a number from 1 to 255 followed by a <code>$</code> sign, e.g.:</p>
  2853. <pre><code>100$    ; this is legal  
  2854. 001$    ; this is the same as the next  
  2855. 1$      ; this is the same as the previous  
  2856. 999$    ; this is illegal (1-255).
  2857. </code></pre>
  2858. <p>The range of local labels is delimited by global labels as well as the <code>.LOCAL</code> directive.</p>
  2859. <h3 id="additional-directives">Additional Directives</h3>
  2860. <p>The BSO assembler supports a number of additional directives:</p>
  2861. <ul>
  2862. <li><code>.NAME</code>/<code>.TITLE</code>: set name of project</li>
  2863. <li><code>.SUBTTL</code>: set name of section (like <code>.PAGE</code> on MOS)</li>
  2864. <li><code>.SKIP</code>/<code>.SPACE</code>: skip lines in LST</li>
  2865. <li><code>.FORMLN</code>: set number of lines per page</li>
  2866. <li><code>.LIST</code>/<code>.NLIST</code>: enable/disable LST (like <code>.OPT LIST</code>)</li>
  2867. <li><code>.CLIST</code>/<code>.NCLIST</code>: show/hide disabled conditional assembly in LST</li>
  2868. <li><code>.MLIST</code>/<code>.NMLIST</code>/<code>.BLIST</code>: control macro expansions in LST</li>
  2869. <li><code>.GEN</code>/<code>.NOGEN</code>: enable/disable extra lines in LST (like <code>.OPT GENERATE</code>)</li>
  2870. <li><code>.INCLUDE</code>: include file</li>
  2871. <li><code>.MACRO</code>/<code>.ENDM</code>: macro definition (existed on MOS, but different syntax)</li>
  2872. <li><code>.REPT</code>/<code>.ENDR</code>: repeat</li>
  2873. <li><code>.IRP</code>/<code>.IRPC</code>: extended repeat</li>
  2874. <li><code>.IFE</code> conditional assembly if == 0 (existed on MOS, but different syntax)</li>
  2875. <li><code>.IFN</code>/<code>.IF</code> conditional assembly if != 0 (existed on MOS, but different syntax)</li>
  2876. <li><code>.IFGE</code> conditional assembly if is >= 0</li>
  2877. <li><code>.IFGT</code> conditional assembly if is > 0</li>
  2878. <li><code>.IFLT</code> conditional assembly if is &lt; 0</li>
  2879. <li><code>.IFLE</code> conditional assembly if is =&lt; 0</li>
  2880. <li><code>.IFB</code>/<code>.IFNB</code> conditional assembly if (not) blank</li>
  2881. <li><code>.IFIDN</code>/<code>.IFNIDN</code> conditional assembly if strings are (not) identical</li>
  2882. <li><code>.IFDEF</code>/<code>.IFNDEF</code> conditional assembly if symbol is (not) defined</li>
  2883. <li><code>.LOCAL</code>: delimit the range of local labels</li>
  2884. <li><code>.MESSG</code>: print message during pass 2</li>
  2885. <li><code>.RMB</code>: reserve memory byte</li>
  2886. <li><code>.RADIX</code>: change default radix for literals without a prefix</li>
  2887. </ul>
  2888. <p>All these features are also <a href="ftp://www.zimmers.net/pub/cbm/schematics/computers/c128/servicemanuals/C128_Developers_Package_for_Commodore_6502_Development_%281987_Oct%29.pdf">documented</a> in the &ldquo;Commodore 128 Developer&rsquo;s Package&rdquo;, which cloned the BSO assembler for the C128. (More details in part 4 of the series.)</p>
  2889. <h3 id="relocating">Relocating</h3>
  2890. <p>It was possible to create relocatable object files with the BSO assembler. Commodore never made use of this feature. The following directives control this:</p>
  2891. <ul>
  2892. <li><code>.AORG</code>/<code>.RORG</code>/<code>.ZORG</code></li>
  2893. <li><code>.SECT</code>/<code>.ASECT</code>/<code>.RSECT</code>/<code>.ZSECT</code></li>
  2894. <li><code>.EXTERN</code>/<code>.INTERN</code></li>
  2895. <li><code>.LINK</code></li>
  2896. </ul>
  2897. <h3 id="lst-files">LST Files</h3>
  2898. <p>Listing files had an updated format. Here is an example:</p>
  2899. <pre><code>1581 DOS v10  318045-01  (c)1987 CBM    CR6502/11 version 20.53.12  19-Mar-87  20:17:11 Page 26  
  2900. "copy"   COPY.SRC
  2901.  
  2902. Error Addr  Code          Seq   Source statement
  2903.  
  2904.                         1755   ; copy file(s) to one file  
  2905.                         1756  
  2906.     87A2   20 82B9      1757   copy    jsr  lookup     ; look ip all files  
  2907.     87A5   AD 022F      1758           lda  f2cnt      
  2908. </code></pre>
  2909. <p>The header distinguishes between the project name (<code>.NAME</code>, first field of first line), the section name (<code>.SUBTTL</code>, first field of second line) and the filename (second field of second line), and contains the assembler&rsquo;s name and the current date.</p>
  2910. <p>The body lines have a new first column that can contain one or more characters that indicate errors in the line, e.g. <code>U</code> for undefined symbol. This is instead of added lines with error messages.</p>
  2911. <h2 id="use-at-commodore">Use at Commodore</h2>
  2912. <p>From the headers of the Commodore LST that have been preserved, we can see that Commodore was using at least the following versions:</p>
  2913. <ul>
  2914. <li>version 10.36.6 since at least July 1984 (TED KERNAL and Char ROM)</li>
  2915. <li>version 20.51.10 since at least August 1984 (TED BASIC, 1570, original C128 ROM)</li>
  2916. <li>version 20.53.12 since at least December 1985 until July 1990 (later C128 ROM, 1551, later 1541/1541C/1541-II, 1571, 1571CR, 1581, C64GS)</li>
  2917. </ul>
  2918. <p>CY6502 was available for VAX/VMS and PDP-11 systems. Everything points towards Commodore using VAX hardware. The <a href="http://www.zimmers.net/anonftp/pub/cbm/src/c128/c128_dev_pack.tar.gz">original C128 source archive</a> contains a VAX reference dated October 1984 (file <code>monitor.sum</code>). A different file (<code>c65.doc</code>) in the C128 source archive mentions that the C128 ROM was developed on a VAX-8600 (12.5 MHz, 4+ MB RAM; released in October 1984).</p>
  2919. <p>According to a <a href="https://archive.org/download/usenet-net">usenet post to net.micro.cbm</a> from December 1985, Commodore had multiple VAX systems at least as early as December 1985:</p>
  2920. <blockquote>
  2921. <p>[&hellip;] which consists of the cbmvax 11/750 and a host of VMS Vaxen, Sun&rsquo;s and developmental systems at both Commodore and Commodore [Amiga].</p>
  2922. </blockquote>
  2923. <p>(<code>cbmvax</code> was the UUCP-connected machine at Commodore. Usenet identifiers and (later) email addresses of Commodore employees contained this machine name.)</p>
  2924. <p>Another document (<code>howto.doc</code>) in the source archive describes how the result was transferred from the VAX to the Commodore computer: VAX computers were multi-user, and each developer had their own VT100-like terminal connected through RS-232. Each terminal had a second AUX/printer RS-232 connection that was connected to the Commodore computer (at 9600 baud if it had an ACIA chip). The tool &ldquo;DOWNLOAD&rdquo; on the VAX then sent the contents of the OBJ file through the terminal to the Commodore computer, which used a tool named &ldquo;RSX&rdquo; to receive the data.</p>
  2925. <p>The <a href="https://www.pagetable.com/?p=1540">next article</a> will discuss the HDC65 assembler on C128 that Commodore built in 1986 as a clone of the BSO assembler.</p>
  2926. <p><!--  
  2927. --></p>
  2928. <div class="footnotes">
  2929. <hr/>
  2930. <ol>
  2931. <li id="fn:1">
  2932. <p>The BSO 6502 cross-assembler seems to have many names. The 1976 documentation called it CA6500: Back then, the 6502 was part of the 6500-series that consisted of the 6501 and the 6502. The version Commodore used 8 years later was called CY6502 in the documentation, but in LST files, it called itself CR6502/11: The &ldquo;11&rdquo; must have stood for either PDP-11 or VAX-11, or both. The &ldquo;R&rdquo; could stand for &ldquo;relocatable&rdquo;, which seemed to have been a recent feature.<a href="#fnref:1" rev="footnote">&#8617;</a></p>
  2933. </li>
  2934. </ol>
  2935. </div>
  2936. ]]></content:encoded>
  2937. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1538</wfw:commentRss>
  2938. <slash:comments>2</slash:comments>
  2939. </item>
  2940. <item>
  2941. <title>Commodore&#8217;s Assemblers: Part 2: MOS Resident Assembler</title>
  2942. <link>https://www.pagetable.com/?p=1522</link>
  2943. <comments>https://www.pagetable.com/?p=1522#comments</comments>
  2944. <pubDate>Sat, 22 May 2021 06:19:07 +0000</pubDate>
  2945. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  2946. <category><![CDATA[6502]]></category>
  2947. <category><![CDATA[Commodore]]></category>
  2948.  
  2949. <guid isPermaLink="false">https://www.pagetable.com/?p=1522</guid>
  2950. <description><![CDATA[In the series about the assemblers Commodore used for developing the ROMs of their 8-bit computers, this article covers the 1976 &#8220;MOS Resident Assembler&#8221; that ran on a variety of 6502-based computers. Series Overview Part 0: Overview Part 1: MOS Cross-Assembler Part 2: MOS Resident Assembler ← this article Part 3: BSO CY6502 Part 4: HCD65 Part 5: 6502ASM History MOS ... <a title="Commodore&#8217;s Assemblers: Part 2: MOS Resident Assembler" class="read-more" href="https://www.pagetable.com/?p=1522">Read more<span class="screen-reader-text">Commodore&#8217;s Assemblers: Part 2: MOS Resident Assembler</span></a>]]></description>
  2951. <content:encoded><![CDATA[<p>In the series about the assemblers Commodore used for developing the ROMs of their 8-bit computers, this article covers the 1976 &ldquo;MOS Resident Assembler&rdquo; that ran on a variety of 6502-based computers.</p>
  2952. <p><img src="docs/cbmasm/resident_assembler.png" alt="" /></p>
  2953. <h2 id="series-overview">Series Overview</h2>
  2954. <ul>
  2955. <li><a href="https://www.pagetable.com/?p=1518">Part 0: Overview</a></li>
  2956. <li><a href="https://www.pagetable.com/?p=1520">Part 1: MOS Cross-Assembler</a></li>
  2957. <li><strong>Part 2: MOS Resident Assembler</strong> ← this article</li>
  2958. <li><a href="https://www.pagetable.com/?p=1538">Part 3: BSO CY6502</a></li>
  2959. <li><a href="https://www.pagetable.com/?p=1540">Part 4: HCD65</a></li>
  2960. <li><a href="https://www.pagetable.com/?p=1542">Part 5: 6502ASM</a></li>
  2961. </ul>
  2962. <p><img src="docs/cbmasm/cbmasm_history.png" alt="" /></p>
  2963. <h2 id="history">History</h2>
  2964. <p>MOS Technology, Inc. released two assemblers for the newly introduced 6502 architecture: the &ldquo;<strong>Cross-Assembler</strong>&rdquo; (1975), available for various mainframes and minicomputers, and the &ldquo;<strong>Resident Assembler</strong>&rdquo; (1976), running natively on 6502 systems.</p>
  2965. <p>The Resident Assembler was <a href="https://www.facebook.com/groups/commodoreinternationalhistoricalsociety/permalink/2304551053122944/?comment_id=2304702559774460&amp;__tn__=R">written</a> by Michael Corder (of MOS contractor COMPAS Microsystems) by hand-assembling the Cross-Assembler FORTRAN code to native 6502 assembly.</p>
  2966. <p>Consequently, both assemblers were compatible in that they understood the same source format, with the same math features<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup> and the same directives and options. That way, they defined the basic format supported by all future Commodore assemblers.</p>
  2967. <p>This means that the Resident Assembler took uppercase ASCII source files and created ASCII-encoded OBJ output files. It could even create the same LST output, but it was sent to the screen or the printer instead of a file.</p>
  2968. <h2 id="versions-and-platforms">Versions and Platforms</h2>
  2969. <p>The Resident Assembler ran on almost all 8-bit computer systems by MOS and Commodore.</p>
  2970. <h3 id="mdt650">MDT650</h3>
  2971. <p>MOS built two computers for demonstration of and development for their new 6502 CPU: the MDT650 and the KIM-1.</p>
  2972. <p>The very first version of the Resident Assembler targeted the MDT650 (&ldquo;Microcomputer Development Terminal&rdquo;; 1976), a development computer by MOS that contained two 6502 CPUs: One to develop software, and one to test it on. Its ROM came with an editor, the Resident Assembler and a disassembler. Source was saved on tape, and later on 8&#8243; disks.</p>
  2973. <p>According to the <a href="docs/cbmasm/MOS%20MDT.pdf">manual</a>, the feature list of this version of the Resident Assembler was the same as that of the Cross-Assembler, except:</p>
  2974. <ul>
  2975. <li>no multiplication or division</li>
  2976. <li>no error files</li>
  2977. <li>no cross references</li>
  2978. <li>no symbol table sorting</li>
  2979. <li>no error messages; but errors numbers instead (1-23)</li>
  2980. </ul>
  2981. <p>No ROM dumps are known.</p>
  2982. <h3 id="kim-1">KIM-1</h3>
  2983. <p>The KIM-1 (1976) was a single-board computer originally intended as a demonstration board for the 6502, but with additional hardware, it could also be used as a full computer. For this, MOS released <a href="http://retro.hansotten.nl/6502-sbc/kim-1-manuals-and-software/kim-1-manuals-and-software/">hardware</a> like the KIM-2/KIM-3 RAM extension and the KIM-4 motherboard.</p>
  2984. <p>In 1977, Commodore <a href="https://archive.org/details/kilobaudmagazine-1977-07">announced</a> the Resident Assembler for the KIM-1, which was supposed to ship in the form of the KIM-5 ROM board with three 6540 ROM chips (6 KB total) containing the assembler and a simple editor. The same year, they announced that the project was being <a href="http://archive.6502.org/publications/6502notes/6502_user_notes_5.pdf">postponed indefinitely</a>, but it did appear in a <a href="https://worldradiohistory.com/UK/Practical-Computing/70s/Practical-Computing-1979-03-S-OCR.pdf">price list in 1979</a>.</p>
  2985. <p><a href="https://www.facebook.com/groups/commodoreinternationalhistoricalsociety/permalink/2304551053122944/"><img src="docs/cbmasm/KIM-5.png" alt="" /></a></p>
  2986. <p>There is a <a href="docs/cbmasm/MOS_KIM_Assembler_Manual_Preliminary.pdf">1977 manual</a>, which does not mention the <code>.DBYTE</code>, <code>.PAGE</code> and <code>.SKIP</code> directives. These were probably just undocumented, since the smaller AIM-65 version (see below) does support them.</p>
  2987. <p>The assembler could read source from cassette tape or paper tape, but it could not write OBJ files; instead, it would write the binary to RAM.</p>
  2988. <p>No ROM dumps are known.</p>
  2989. <p>(A company named &ldquo;ARESCO&rdquo; offered a 6 KB RAM-based assembler for the KIM-1 that may have been based on the MOS Resident Assembler. According to the <a href="https://www.apple.asimov.net/documentation/programming/6502assembly/Rainbow%20Assembler%20Editor%20manual%20Apple%20II.pdf">documentation of the Apple II version</a>, the supported directives and error codes matched the MOS Resident Assembler, and some sources call it the &ldquo;MOS/ARESCO&rdquo; assembler. Unfortunately, no dumps are known.)</p>
  2990. <h3 id="aim-65">AIM-65</h3>
  2991. <p>The AIM-65 (&ldquo;Advanced Interactive Microcomputer&rdquo;, 1978) was a single-board computer similar to the KIM-1, sold by the 6502 second source Rockwell. A very stripped down and optimized 4 KB version of the MDT650 version of the Resident Assembler was available for this platform.</p>
  2992. <p>The AIM-65 is not a MOS/Commodore device, and it was not used internally at Commodore, but its version of the Resident Assembler is presumably based on the (lost) KIM-1 version.</p>
  2993. <p>Chapter 5 of the <a href="docs/cbmasm/AIM-65_Users_Guide.pdf">AIM-65 User&rsquo;s Guide</a> covers the assembler.</p>
  2994. <ul>
  2995. <li><a href="docs/cbmasm/resident_assembler_r3224_aim65.bin">AIM-65 Resident Assembler R3224</a> (1978)</li>
  2996. </ul>
  2997. <h3 id="pet">PET</h3>
  2998. <p>After the release of the CBM 2040 disk drive for the PET in 1978, John Feagans (of KERNAL fame) ported the MDT650 version of the Resident Assembler to the new platform.</p>
  2999. <p>The oldest known version is undated:</p>
  3000. <ul>
  3001. <li><a href="docs/cbmasm/resident_assembler_Vxxxxxx_pet.prg">PET Resident Assembler</a> (undated, 6696 bytes, from <a href="http://6502.org/users/sjgray/dj/">DJ</a> old-dos-sources.d81); for BASIC 2</li>
  3002. </ul>
  3003. <p>The next known versions are the only ones publicly released:</p>
  3004. <ul>
  3005. <li><a href="docs/cbmasm/resident_assembler_V112779_pet.prg">PET Resident Assembler V112779</a> (1979-11-27, 7426 bytes); for BASIC 2</li>
  3006. <li><a href="docs/cbmasm/resident_assembler_V121579_BASIC4_pet.prg">PET Resident Assembler V121579 BASIC4</a> (1979-12-15, 7546 bytes); for BASIC 4</li>
  3007. </ul>
  3008. <p>These two binaries were published as part of the <a href="http://www.zimmers.net/anonftp/pub/cbm/pet/programming/c=development.d64.gz">Commodore PET Assembler Development System</a> in 1980.</p>
  3009. <p>They add multiplication and division support, as well as textual error messages. So except for the missing cross-reference support, this version basically has feature parity with the MOS Cross-Assembler. The <a href="https://archive.org/details/manualzilla-id-5917290">manual</a> is based on the KIM-1 Assembler Manual, and adds descriptions of the OBJ format as well as the added features. Conditional compilation (<code>.IFN</code>/<code>.IFE</code>) is undocumented, but supported by the BASIC 4 version of the assembler.</p>
  3010. <p>There are more versions, all of which have only been used only internally:</p>
  3011. <ul>
  3012. <li><a href="docs/cbmasm/resident_assembler_V121579_BASIC2_pet.prg">PET Resident Assembler V121579 BASIC2</a> (1979-12-15, 7546 bytes, from <a href="docs/RB_Images.zip">RB</a> 6502ASM/MASTER.D80, UTIL001.D80, UTIL003.D80, UTIL004.D80, UTIL008.D80, UTIL009.D80); for BASIC 2: only direct calls into the PET ROM differ</li>
  3013. <li><a href="docs/cbmasm/resident_assembler_V090580_a_pet.prg">PET Resident Assembler V090580 A</a> (1980-09-05, 7858 bytes, from <a href="http://www.cbmhardware.de/show.php?r=14&amp;id=66/Software">cbmhardware.de</a>); <strong>adds cross-reference support</strong></li>
  3014. <li><a href="docs/cbmasm/resident_assembler_V090580_b1_pet.prg">PET Resident Assembler V090580 B1</a> (1980-09-05, 7938 bytes, from <a href="http://6502.org/users/sjgray/dj/">DJ</a> b128-editor-1of1.d64); later (!) version with optimizations</li>
  3015. <li><a href="docs/cbmasm/resident_assembler_V090580_b2_pet.prg">PET Resident Assembler V090580 B2</a> (1980-09-05, 7822 bytes, from <a href="docs/RB_Images.zip">RB</a> UNKN003.D80, UTIL008.D80, UTIL009.D80); identical to B1, but for 72 lines per page instead of 66</li>
  3016. <li><a href="docs/cbmasm/resident_assembler_V102780_pet.prg">PET Resident Assembler V102780</a> (1980-10-27, 8049 bytes, from <a href="http://6502.org/users/sjgray/dj/">DJ</a> c64kernal.d64); detects BASIC 2/4 at runtime</li>
  3017. </ul>
  3018. <p>These two versions are non-mainline:</p>
  3019. <ul>
  3020. <li><a href="docs/cbmasm/resident_assembler_V050482_pet.prg">PET Resident Assembler V050482</a> (1982-05-04, 7624 bytes, from <a href="docs/RB_Images.zip">RB</a> UTIL008.D80); binary-patched version of V121579 BASIC4, asks &ldquo;PAPER LENGTH (CR/66 OR 72)?&rdquo;</li>
  3021. <li><a href="docs/cbmasm/resident_assembler_V26MAR82RR_pet.prg">CBM 6502 Copyright Assembler V26MAR82RR</a> (1982-03-26, 7982 bytes, from <a href="http://6502.org/users/sjgray/dj/">DJ</a> b128-kernal-2of2.d64); based on V090580 B, calls itself &ldquo;CBM 6502 COPYRIGHT ASSEMBLER&rdquo;; changes to LST format</li>
  3022. </ul>
  3023. <h3 id="c64">C64</h3>
  3024. <p>There is one known version of the Resident Assembler for the C64:</p>
  3025. <ul>
  3026. <li><a href="docs/cbmasm/resident_assembler_V080282_c64.prg">CBM Resident Assembler V080282 (C64)</a> (1982-08-02, 9714 bytes)</li>
  3027. </ul>
  3028. <p>It was released on disk as part of the <a href="http://www.zimmers.net/anonftp/pub/cbm/c64/programming/cbm-assembler.d64.gz">Commodore 64 Macro Assembler Development System</a>. The <a href="https://archive.org/details/C64MacroAssemblerDevelopmentSystemManualC64101">manual</a> is based on the PET version, and adds documentation of the macro (see below) and cross-reference features, but does not document conditional compilation. An earlier version of the manual, which is part of the <a href="https://archive.org/details/commodore64softwaredevelopmentkit">Commodore 64 Development Kit</a> internal document, does document conditional compilation, but not macros.</p>
  3029. <h3 id="ted-series-(c16,-c116,-plus/4)">TED Series (C16, C116, Plus/4)</h3>
  3030. <p>Commodore also ported the assembler to the TED series, but never released it publicly:</p>
  3031. <ul>
  3032. <li><a href="docs/cbmasm/resident_assembler_V112384_ted.prg">Commodore Assembler V2.0 (TED)</a> (1984-11-23, 10550 bytes, from <a href="http://6502.org/users/sjgray/dj/">DJ</a> cbm-assembler.d64)</li>
  3033. </ul>
  3034. <p>This binary was built from the <a href="https://github.com/mist64/cbmsrc/tree/master/ASSEMBLER_TED">original source code</a>. Unlike all earlier and later versions, this one does not interactively ask for its arguments, but relies on a BASIC program to place the arguments in memory before calling the machine code.</p>
  3035. <h3 id="c128">C128</h3>
  3036. <p>And finally, there is a C128 version:</p>
  3037. <ul>
  3038. <li><a href="docs/cbmasm/resident_assembler_V022086_c128.prg">C/128 6502 ASSEMBLER V022086</a> (1986-02-20, 10498 bytes, from <a href="http://6502.org/users/sjgray/dj/">DJ</a> testing-c65-dos.d81)</li>
  3039. </ul>
  3040. <p>It was based on the TED version, but went back to being standalone and asking for the arguments itself.</p>
  3041. <p>This version, too, was not released publicly.</p>
  3042. <h2 id="new-features">New Features</h2>
  3043. <p>While the MDT650 version of the Resident Assembler had no additional features compared to the Cross-Assembler, new features were added to later versions.</p>
  3044. <h3 id="kim-1/aim-65">KIM-1/AIM-65</h3>
  3045. <p>The AIM-65 version (and presumably the KIM-1 version) of the Resident Assembler added one new directive:</p>
  3046. <ul>
  3047. <li><code>.FILE</code>: switch to different source file, do not return</li>
  3048. </ul>
  3049. <p>The size of the source of a more complex program could easily exceed the amount of RAM installed in a KIM-1/AIM-65. For instance, the 4 KB version of the Resident Assembler itself was, without comments, 24 KB in size, much larger than the typical amount of RAM in a KIM-1 or AIM-65.</p>
  3050. <p>While the powerful computers that the <em>Cross-Assembler</em> ran on had text editors that could handle files larger than the available RAM, the KIM-1/AIM-65 editors could not do this.</p>
  3051. <p>Therefore, the assembler allowed splitting the source into multiple files, which were then concatenated during the build.</p>
  3052. <h3 id="pet-v112779-(1979-11-27)">PET V112779 (1979-11-27)</h3>
  3053. <p>Version V112779 of the PET Resident Assembler had the following additional features:</p>
  3054. <ul>
  3055. <li><code>.LIB</code>: switch to a different source file (and unlike <code>.FILE</code>, return to the original source file after the end of the included file)</li>
  3056. <li><code>.OPT</code> <code>SYMBOL</code> &#8211; <code>NOSYMBOL</code>: add symbol table to the end of the LST</li>
  3057. </ul>
  3058. <h3 id="pet-v121579-(1979-12-15)">PET V121579 (1979-12-15)</h3>
  3059. <p>Version V121579 added conditional assembly:</p>
  3060. <ul>
  3061. <li><code>.IFE</code>: conditionally assemble if expression == 0</li>
  3062. <li><code>.IFN</code>: conditionally assemble if expression != 0</li>
  3063. </ul>
  3064. <p>The code guarded by the condition has to be between <code>&lt;</code> and <code>&gt;</code>:</p>
  3065. <pre><code>.IFN GDEBUG &lt;  
  3066.       JSR GRBPRI      ;GARBAGE DEBUG  
  3067. &gt;
  3068. </code></pre>
  3069. <h3 id="c64-v080282-(1982-08-02)">C64 V080282 (1982-08-02)</h3>
  3070. <p>The C64 version adds support for macros. The manual contains the following example:</p>
  3071. <pre><code>       .MAC DPINC    ;DOUBLE PRECISION INCREMENT  
  3072.       INC ?1  
  3073.       BNE ?2  
  3074.       INC ?1+1  
  3075. ?2     .MND
  3076. </code></pre>
  3077. <p>The argument of <code>.MAC</code> is the name of the macro. The macro contents are defined between <code>.MAC</code> and <code>.MND</code>. Arguments are named <code>?1</code> etc., and local variables can be declared from the same namespace.</p>
  3078. <p>No known Commodore source code used this feature.</p>
  3079. <h3 id="ted-(1984-11-23)">TED (1984-11-23)</h3>
  3080. <p>The TED version adds the following <code>.OPT</code> arguments:</p>
  3081. <ul>
  3082. <li><code>LONG</code>/<code>NLONG</code>: allow long labels</li>
  3083. <li><code>MLI</code>/<code>NMLI</code>: enable expanding macros in LST</li>
  3084. </ul>
  3085. <p>Also, expressions can contain <code>&amp;</code> (bitwise and), <code>.</code> (bitwise or) and <code>!</code> (bitwise exclusive or).</p>
  3086. <h3 id="c128-v022086-(1986-02-20)">C128 V022086 (1986-02-20)</h3>
  3087. <p>The C128 version removes support for mnemo statistics (<code>.OPT COUNT</code>/<code>CNT</code>/<code>NOCOUNT</code>).</p>
  3088. <h2 id="source-code">Source Code</h2>
  3089. <p>The source code of the TED and C128 versions was preserved as part of the <a href="http://www.zimmers.net/anonftp/pub/cbm/src/plus4/index.html">TED</a> and <a href="http://www.zimmers.net/anonftp/pub/cbm/src/c128/c128_dev_pack.tar.gz">C128 Source archives</a>. it was added to the Commodore Source collection:</p>
  3090. <p><a href="https://github.com/mist64/cbmsrc">https://github.com/mist64/cbmsrc</a></p>
  3091. <p>The sources of the AIM-65 version, all PET versions as well as the C64 version have been reconstructed from the TED source and are also available in this repository:</p>
  3092. <ul>
  3093. <li>ASSEMBLER_AIM65_REC</li>
  3094. <li>ASSEMBLER_PET_REC</li>
  3095. <li>ASSEMBLER_PET_V112779_REC</li>
  3096. <li>ASSEMBLER_PET_V121579_REC</li>
  3097. <li>ASSEMBLER_PET_V090580_A_REC</li>
  3098. <li>ASSEMBLER_PET_V090580_B_REC</li>
  3099. <li>ASSEMBLER_PET_V102780_REC</li>
  3100. <li>ASSEMBLER_PET_V26MAR82RR_REC</li>
  3101. <li>ASSEMBLER_C64_REC</li>
  3102. <li>ASSEMBLER_TED</li>
  3103. <li>ASSEMBLER_C128</li>
  3104. </ul>
  3105. <h2 id="bugs-and-quirks">Bugs and Quirks</h2>
  3106. <p>The C64 version has at least two known bugs:</p>
  3107. <ul>
  3108. <li><a href="https://archive.org/details/Commodore_Disk_User_Volume_03_Number_10_1990-08_Argus_Specialist_Publications_GB">Commodore Disk User 1990-08</a> contains an article that fixes some problems with macros.</li>
  3109. <li>A <a href="https://comp.sys.cbm.narkive.com/mgfpE1pf/c64-cbm-asm-bug">thread in comp.sys.cbm</a> discusses and fixes a problem with multiplication.</li>
  3110. </ul>
  3111. <p>It remains to be researched whether the TED source and the C128 binary contain these bugs.</p>
  3112. <p>Also, there is a confusing error message:</p>
  3113. <pre><code>**RAN OFF END OF CARD
  3114. </code></pre>
  3115. <p>This means that the assembler was expecting more input, but encountered the end of the line. Back in the mainframe days, lines were also called &ldquo;cards&rdquo;, since one punch card stored a single input line.</p>
  3116. <p>The Cross-Assembler used to call lines &ldquo;cards&rdquo;, as can be seen in the LST headings:</p>
  3117. <pre><code> CARD # LOC     CODE        CARD  
  3118.   10  0009  B1 0E      NEXT    LDA (SAVIL)Y
  3119. </code></pre>
  3120. <p>The Resident Assembler had dropped the &ldquo;card&rdquo; nomenclature on the PET though:</p>
  3121. <pre><code>LINE# LOC   CODE        LINE  
  3122.   10  0009  B1 0E      NEXT    LDA (SAVIL)Y
  3123. </code></pre>
  3124. <p>&hellip;except for the single remaining case of the error message.</p>
  3125. <h2 id="use-at-commodore">Use at Commodore</h2>
  3126. <p>Commodore used the Resident Assembler on the MDT650 (with an 8&#8243; disk drive) to develop the ROM of the original PET and the CBM 2040 (dual 5.25&#8243;) disk drive. At the time, the two MDT650 systems at Commodore were a <a href="https://www.facebook.com/groups/commodoreinternationalhistoricalsociety/permalink/2624023531175693/?comment_id=2624122201165826&amp;__cft__[0]=AZWHV99O07jwUFqpjjSsN9hVaNNizvF4K5GhXzvU_WJJ9bxIyDRnZxmdrVduK35nuorC5tFw-kEpLhOroSx60QvS8hJrXSAEazqROkfRUuC37MhW-MqxHqB9joVP3coemgwJE6bgtiRwQ7wHwfjBOjQx&amp;__tn__=R]-R">scarce resource</a>, so as soon as the CBM 2040 drive was available, the Resident Assembler was ported to the PET, and thus 6502 software development was switched to PETs.</p>
  3127. <p>The software Commodore built on the PET was:</p>
  3128. <ul>
  3129. <li>KERNAL/EDITOR and BASIC of later PETs, the CBM2, VIC-20 and C64</li>
  3130. <li>the DOS ROMs of the 4040, 8061/8062, D9060/D9090, 8050/8250/1001, 2031, 1540 and the original 1541</li>
  3131. <li>the ROMs of the 6502-based printers</li>
  3132. <li>Commodore-developed applications and games, like Gorf and Omega Race</li>
  3133. </ul>
  3134. <p>A large amount of <a href="https://github.com/mist64/cbmsrc">Commodore source</a> since 1980 (VIC-20, 1540, CBM 4040, &hellip;) has been preserved, and all source until 1984 (just before the TED series) is in a format that can be built with the Resident Assembler (V121579 or above).</p>
  3135. <p>The <a href="https://www.pagetable.com/?p=1538">next article</a> will discuss the BSO cross-assembler on VAX that Commodore switched to in 1984.</p>
  3136. <p><!--  
  3137. TODO:  
  3138. * some other stuff during PET times, e.g. Transactor  
  3139.    * https://ksquiggle.neocities.org/ff6502.htm was built with this  
  3140.    * http://archive.6502.org/publications/icpug/icpug_v04_i06_nov_1982.pdf  
  3141. * inexplicably still uses OBJ  
  3142. * use outside of CBM? Rockwell?  
  3143. --></p>
  3144. <div class="footnotes">
  3145. <hr/>
  3146. <ol>
  3147. <li id="fn:1">
  3148. <p>Multiplication and division were missing from the original version.<a href="#fnref:1" rev="footnote">&#8617;</a></p>
  3149. </li>
  3150. </ol>
  3151. </div>
  3152. ]]></content:encoded>
  3153. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1522</wfw:commentRss>
  3154. <slash:comments>1</slash:comments>
  3155. </item>
  3156. <item>
  3157. <title>Commodore&#8217;s Assemblers: Part 1: MOS Cross-Assembler</title>
  3158. <link>https://www.pagetable.com/?p=1520</link>
  3159. <comments>https://www.pagetable.com/?p=1520#comments</comments>
  3160. <pubDate>Sat, 15 May 2021 05:30:19 +0000</pubDate>
  3161. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  3162. <category><![CDATA[6502]]></category>
  3163. <category><![CDATA[Commodore]]></category>
  3164.  
  3165. <guid isPermaLink="false">https://www.pagetable.com/?p=1520</guid>
  3166. <description><![CDATA[In the series about the assemblers Commodore used for developing the ROMs of their 8-bit computers, this article covers the 1975 &#8220;MOS Cross-Assembler&#8221;, which was available for various mainfraimes of the era. Series Overview Part 0: Overview Part 1: MOS Cross-Assembler ← this article Part 2: MOS Resident Assembler Part 3: BSO CY6502 Part 4: HCD65 Part 5: 6502ASM History MOS ... <a title="Commodore&#8217;s Assemblers: Part 1: MOS Cross-Assembler" class="read-more" href="https://www.pagetable.com/?p=1520">Read more<span class="screen-reader-text">Commodore&#8217;s Assemblers: Part 1: MOS Cross-Assembler</span></a>]]></description>
  3167. <content:encoded><![CDATA[<p>In the series about the assemblers Commodore used for developing the ROMs of their 8-bit computers, this article covers the 1975 &ldquo;MOS Cross-Assembler&rdquo;, which was available for various mainfraimes of the era.</p>
  3168. <p><a href="https://archive.org/details/mos-6500-software-support"><img src="docs/cbmasm/cross-assembler.png" alt="" /></a></p>
  3169. <h2 id="series-overview">Series Overview</h2>
  3170. <ul>
  3171. <li><a href="https://www.pagetable.com/?p=1518">Part 0: Overview</a></li>
  3172. <li><strong>Part 1: MOS Cross-Assembler</strong> ← this article</li>
  3173. <li><a href="https://www.pagetable.com/?p=1522">Part 2: MOS Resident Assembler</a></li>
  3174. <li><a href="https://www.pagetable.com/?p=1538">Part 3: BSO CY6502</a></li>
  3175. <li><a href="https://www.pagetable.com/?p=1540">Part 4: HCD65</a></li>
  3176. <li><a href="https://www.pagetable.com/?p=1542">Part 5: 6502ASM</a></li>
  3177. </ul>
  3178. <p><img src="docs/cbmasm/cbmasm_history.png" alt="" /></p>
  3179. <h2 id="history">History</h2>
  3180. <p>MOS Technology, Inc. released two assemblers for the newly introduced 6502 architecture: the &ldquo;<strong>Cross-Assembler</strong>&rdquo;, available for various mainframes and minicomputers, and the &ldquo;<strong>Resident Assembler</strong>&rdquo;, running on natively on 6502 systems.</p>
  3181. <p><a href="https://www.pagetable.com/?p=1520#comment-694762">According to Norm Farrington</a>, MOS contracted with the company COMPAS to write the Cross-Assembler. COMPAS specialized in 6502-related software and hardware and developed some of the official MOS peripherals for the KIM-1.</p>
  3182. <p>The Cross-Assembler was <a href="https://archive.org/details/crossassemblersi886blan">written in FORTRAN</a><sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup> and used a 6-bit (i.e. all uppercase) character encoding. On a CDC Cyber 175 system, it required 120K (!) 60-bit words of memory to run and had &ldquo;generally acceptable&rdquo; response times.</p>
  3183. <p>The Resident Assembler (<a href="https://www.pagetable.com/?p=1522">part 2</a> of the series) was then developed (also by COMPAS) using the Cross-Assembler, and designed to be compatible in that it understood the same source format, with the same math features and the same directives and options. This way, MOS defined the basic format supported by all future Commodore assemblers.</p>
  3184. <h2 id="platforms">Platforms</h2>
  3185. <p>Today, you would expect a cross-assembler to run on Linux, Windows or Mac. But these were different times: When the 6502 was released, <em>micro</em>computers were just appearing – and the 6502 was a part of this shift – and even the first fridge-sized <em>mini</em>computers like the PDP series had only been introduced 10 years earlier. Most companies that used computers at the time used terminals that dialed into mainframes hosted in computing centers. And the market was very fragmented.</p>
  3186. <p>The 6502 <a href="http://archive.6502.org/books/mcs6500_family_programming_manual.pdf">Programming Manual (6500-50A)</a> states:</p>
  3187. <blockquote>
  3188. <p>The Cross Assembler is available on various time share systems or for batch use on the user&rsquo;s system.</p>
  3189. </blockquote>
  3190. <p>According to the 6502 <a href="docs/cbmasm/MCS6500%20Microcomputer%20Family%20Cross%20Assembler%20Manual.pdf">Cross Assembler Manual (6500-60P)</a>, the supported platforms as of August 1975 were:</p>
  3191. <ul>
  3192. <li><a href="https://en.wikipedia.org/wiki/General_Electric">GE</a> Timesharing, running on GE/Honeywell mainframes</li>
  3193. <li><a href="https://en.wikipedia.org/wiki/National_CSS">National CSS</a> time-sharing, running VP/CSS on IBM System/360 and System/370 mainframes</li>
  3194. </ul>
  3195. <p>And the 1975 <a href="https://www.team6502.org/uploads/1/0/6/0/106091831/mos_brochure.pdf">MOS Technology marketing brochure</a>:</p>
  3196. <blockquote>
  3197. <p>Current plans involve having the software available on several of the more popular Time Sharing services.</p>
  3198. <p>In addition, it will be available for deck sales. Batch decks for the CDC, IBM, and PDP-11 class machines are available and we will support several other popular mini and major computer systems in the near future.</p>
  3199. </blockquote>
  3200. <p>Furthermore:</p>
  3201. <ul>
  3202. <li>The 1975 <a href="https://archive.org/details/mos-6500-software-support">MCS6500 Microprocessor Software Support brochure</a> shows the Cross-Assembler running on the United Computing Systems (UCS) time-sharing service.</li>
  3203. <li>A 1976 <a href="https://lib.dr.iastate.edu/cgi/viewcontent.cgi?article=6700&amp;context=rtd">dissertation</a> shows that the Iowa State University ran the Cross-Assembler locally on an IBM 360/370.</li>
  3204. <li>A 1977 <a href="https://archive.org/details/bitsavers_ElectronicignV25N2119771011_176731913">magazine article</a> mentions support for PDP-8, PDP-10 and PDP-11.</li>
  3205. </ul>
  3206. <p><!--  NASA was using Cross-Assembler 5.1: https://ia601203.us.archive.org/23/items/NASA_NTRS_Archive_19800013494/NASA_NTRS_Archive_19800013494.pdf – Not sure what hardware it was on, though... --></p>
  3207. <h2 id="basic-syntax">Basic Syntax</h2>
  3208. <p>Back then, not all computer systems used the ASCII encoding, and some computers didn&rsquo;t even support lower case. The encoding of source files is therefore specific to the platform the Cross-Assembler runs on, and only uppercase characters were allowed.</p>
  3209. <p>Here is an example from the manual:</p>
  3210. <pre><code>;  
  3211. ; 650X CROSS ASSEMBLER SAMPLE PROGRAM.  
  3212. ;
  3213. *=$C000     DEFINE ORIGIN.  
  3214. LDX #$FF    SET UP STACK.  
  3215. TXS         LOAD STACK POINTER.  
  3216. LDA #$F0    LOAD A WITH HEX F0.  
  3217. STA ASAVE   SAVE A IN ASAVE.  
  3218. ;
  3219. ; ALLOCATE SAVE AREA.  
  3220. ;
  3221. *=$0000  
  3222. ASAVE *=*  
  3223. .END
  3224. </code></pre>
  3225. <ul>
  3226. <li>Lines starting with <code>;</code> are comments and will be ignored.</li>
  3227. <li>Labels start at the first column, everything else is indented.</li>
  3228. <li>Comments may follow the operand or the operand-less mnemonic. No <code>;</code> is necessary.</li>
  3229. <li>The Cross-Assembler uses the <code>*=</code> syntax to define the current assembly address.</li>
  3230. </ul>
  3231. <p>The rule about the start columns of labels and assembly statements is actually more relaxed, as this very compressed example from the C64 KERNAL shows:</p>
  3232. <pre><code>;COMMAND SERIAL BUS DEVICE TO LISTEN  
  3233. ;
  3234. LISTN ORA #$20 ;MAKE A LISTEN ADR  
  3235. JSR RSP232 ;PROTECT SELF FROM RS232 NMI'S  
  3236. LIST1 PHA
  3237. </code></pre>
  3238. <p>Since all mnemos and register names are reserved keywords and cannot be used for labels, the assembler does not enforce indenting for assembly statement. The <code>JSR RSP232</code> starting at the first column is legal. In fact, even labels may be indented. This example prepends comments after statements with <code>;</code>, which is legal because the assembler ignores everything after the statement anyway.</p>
  3239. <p>If you want to go for maximum readability (and don&rsquo;t care about the size of the source), you could also indent the above example like this:</p>
  3240. <pre><code>; COMMAND SERIAL BUS DEVICE TO LISTEN  
  3241. ;
  3242. LISTN  ORA #$20       ; MAKE A LISTEN ADR  
  3243.       JSR RSP232     ; PROTECT SELF FROM RS232 NMI'S  
  3244. LIST1  PHA
  3245. </code></pre>
  3246. <p>Labels can be up to 6 characters in length, so one could use 7 character indents for statements so that they always line up. That&rsquo;s also how the LST output is formatted, which will be described later.</p>
  3247. <h2 id="assembly-statements">Assembly Statements</h2>
  3248. <p>The accepted syntax of assembly statements matches the one in the <a href="http://archive.6502.org/books/mcs6500_family_programming_manual.pdf">6502 Programming Manual</a>. This includes the syntax for statements that take the accumulator as the argument:</p>
  3249. <pre><code>ASL A  
  3250. LSR A  
  3251. ROL A  
  3252. ROR A
  3253. </code></pre>
  3254. <p>Modern assemblers usually allow omitting the <code>A</code>; the Cross-Assembler does not.</p>
  3255. <p>One additional feature is an alternative syntax to the indirect, y-indexed addressing mode. In addition to</p>
  3256. <pre><code>LDA (PNT),Y
  3257. </code></pre>
  3258. <p>the following syntax is accepted:</p>
  3259. <pre><code>LDA (PNT)Y
  3260. </code></pre>
  3261. <h2 id="expressions">Expressions</h2>
  3262. <p>Operands of assembly statements can use hexadecimal (<code>$</code>), octal (<code>@</code>), binary (<code>%</code>) and decimal (no prefix) constants. Mathematical expressions using <code>+</code>, <code>-</code>, <code>*</code> and <code>/</code> are possible, but they are always evaluated left-to-right with no operator precedence and no parenthetical grouping. Character/string literals are prefixed with <code>'</code>.</p>
  3263. <h2 id="directives">Directives</h2>
  3264. <p>The Cross-Assembler understands the following directives:</p>
  3265. <ul>
  3266. <li><code>.BYTE</code>: store one or more bytes</li>
  3267. <li><code>.WORD</code>: store one or more words (little endian)</li>
  3268. <li><code>.DBYTE</code>: store one or more words (big endian)</li>
  3269. <li><code>.PAGE</code>: optionally set a section title, and cause an LST page break</li>
  3270. <li><code>.SKIP</code>: insert a number of blank lines into the LST</li>
  3271. <li><code>.END</code>: stop assembly; not required but suggested at end of file</li>
  3272. <li><code>.OPT</code>: set or clear a list of options
  3273. <ul>
  3274. <li><code>XREF</code> &#8211; <code>NOXREF</code>: add a cross-reference to the end of the LST</li>
  3275. <li><code>ERRORS</code> &#8211; <code>NOERRORS</code>: write errors into separate file</li>
  3276. <li><code>COUNT</code>/<code>CNT</code> &#8211; <code>NOCOUNT</code>: add mnemo statistics to the end of the LST</li>
  3277. <li><code>LIST</code> &#8211; <code>NOLIST</code>: enable LST output</li>
  3278. <li><code>MEMORY</code> &#8211; <code>NOMEMORY</code>: enable writing object file</li>
  3279. <li><code>GENERATE</code> &#8211; <code>NOGENERATE</code>: verbose printing of character strings in the LST</li>
  3280. </ul>
  3281. </li>
  3282. </ul>
  3283. <p>Only the first three characters after the period are actually checked.</p>
  3284. <h2 id="lst-file">LST File</h2>
  3285. <p>Like most development tools from the 1970s, the assembler can create a so-called listing file (suggested file extension <code>.LST</code>) during assembly that shows the source and the generated bytes side-by-side and is meant to be printed on paper. Here is a example from the manual:</p>
  3286. <pre><code> CARD # LOC     CODE        CARD  
  3287.    1                   CR=15  
  3288.    2                   LF=12  
  3289.    3                   ; LOW CORE DATA AREAS  
  3290.    4  0000  E7 06      TEMTBL   .WORD G3TEM, G1TEM  
  3291.    5  0002  E7 05  
  3292.    6                   GROUP=B10  
  3293.    7  0004  00         THI     .BYTE 0  
  3294.    8  0005  00         TLO     .BYTE 0  
  3295.    9  0006  00 00 00   3PER    .WORD 0  
  3296. ***** ERROR ** LABEL DOESN'T BEGIN WITH ALPHABETIC CHARACTER - NEAR COLUMN 1  
  3297.   10  0009  B1 0E      NEXT    LDA (SAVIL)Y  
  3298. [...]  
  3299.  269  07C9  C9 3B              CMP #';  
  3300.  270  07CB  00 00              BEQ DONE  
  3301. *****  ERROR ** UNDEFINED SYMBOL - NEAR COLUMN 18  
  3302.  280                           .END
  3303.  
  3304. END OF MOS/TECHNOLOGY 650X ASSEMBLY VERSION 4  
  3305. NUMBER OF ERRORS =    2,   NUMBER OF WARNINGS =    0
  3306. </code></pre>
  3307. <p>The first column (<code>CARD #</code>) is the line number in the source. The <code>LOC</code> field is the memory address, which is followed by the output bytes (<code>CODE</code>) and the source line (<code>CARD</code>), which the assembler re-indented for readability.</p>
  3308. <p>The <code>CARD</code> nomenclature stems from 1960s mainframes, where each line of text was represented by one punch card.</p>
  3309. <p>Error messages are shown as extra lines after the line that caused the error. Note that the assembler will keep working through the file no matter what, and will output placeholder bytes for lines with errors.</p>
  3310. <p>The <a href="http://retro.hansotten.nl/uploads/files/KIM%20ROM%20listings%20user%20manual.pdf">original LST printout of the KIM-1 ROM</a>, which was part of the <a href="http://retro.hansotten.nl/uploads/6502docs/usrman.htm">user manual</a>, is a real-world example of a 1200-line LST file, with the symbol table and the mnemo statistics (<code>.OPT COUNT</code>) at the end.</p>
  3311. <h2 id="obj-file">OBJ File</h2>
  3312. <p>The main output of the assembler is the binary program, which is in the form of a so-called &ldquo;interface file&rdquo; with a suggested file extension of <code>.OBJ</code>.</p>
  3313. <p>The diverse set of platforms that the Cross-Assembler ran on all had different word sizes, and many of them measured memory only in words and did not even have a concept of (8-bit) &ldquo;bytes&rdquo;. Therefore, the assembler could not output a binary file, but instead wrote a portable, hex-encoded text file, like this:</p>
  3314. <pre><code>;18E500A200A0DC60A228A01960B00786D684D3206CE5A6D6A4D3600D8C  
  3315. ;18E51820A0E5A9008D910285CFA9488D8F02A9EB8D9002A90A8D890C62  
  3316. [...]  
  3317. ;06FFFA43FEE2FC48FF0665  
  3318. ;0001200021
  3319. </code></pre>
  3320. <p>Every line consists of the following characters:</p>
  3321. <ul>
  3322. <li><code>;</code>: first character of each record</li>
  3323. <li>2 chars: number of data bytes to follow</li>
  3324. <li>4 chars: load address of first byte of line</li>
  3325. <li>2 chars (repeated): data byte</li>
  3326. <li>4 chars: checksum</li>
  3327. </ul>
  3328. <p>The last line is<br />
  3329. * <code>;00</code>: identifier for last line<br />
  3330. * 4 chars: number of preceding lines<br />
  3331. * 4 chars: checksum</p>
  3332. <p>The checksum is calculated by adding <em>all</em> preceding bytes of the line together.</p>
  3333. <p>This text-only file is platform-independent and can easily be transferred between different computer systems, and e.g. downloaded from a time-sharing system in order to write it to an EPROM.</p>
  3334. <h2 id="use-at-commodore">Use at Commodore</h2>
  3335. <p>The Cross-Assembler was used at MOS to create the very first 6502 code, like the KIM-1 ROM (shown above), or the <a href="http://archive.6502.org/books/mos_tim_terminal_interface_monitor_manual_mar_1976.pdf">TIM</a> ROM (MCS6530-004).</p>
  3336. <p>A large amount of the <a href="https://github.com/mist64/cbmsrc">original Commodore source code</a> has been preserved, and all code before 1984 is in a format very similar to the original MOS definition supported by both the Cross-Assembler and the Resident Assembler. So at first sight, it is not so clear which assembler Commodore used for developing ROMs of the PET, VIC-20 etc. and the disk drives.</p>
  3337. <p>The <a href="https://www.pagetable.com/?p=1522">next article</a> in the series will discuss this topic further.</p>
  3338. <div class="footnotes">
  3339. <hr/>
  3340. <ol>
  3341. <li id="fn:1">
  3342. <p>An earlier version of the article stated that the original MOS Cross-Assembler had been implemented by a grad student at the University of Illinois at Urbana-Champaign. In fact, the linked paper is only <em>about</em> the COMPAS/MOS Cross-Assembler as it was used on the university&rsquo;s CDC computer.<a href="#fnref:1" rev="footnote">&#8617;</a></p>
  3343. </li>
  3344. </ol>
  3345. </div>
  3346. ]]></content:encoded>
  3347. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1520</wfw:commentRss>
  3348. <slash:comments>8</slash:comments>
  3349. </item>
  3350. <item>
  3351. <title>Commodore&#8217;s Assemblers: Overview</title>
  3352. <link>https://www.pagetable.com/?p=1518</link>
  3353. <comments>https://www.pagetable.com/?p=1518#respond</comments>
  3354. <pubDate>Sun, 09 May 2021 11:06:01 +0000</pubDate>
  3355. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  3356. <category><![CDATA[6502]]></category>
  3357. <category><![CDATA[Commodore]]></category>
  3358.  
  3359. <guid isPermaLink="false">https://www.pagetable.com/?p=1518</guid>
  3360. <description><![CDATA[Commodore used 5 different assemblers, most of them in-house tools, to build the ROMs for their Computers like the PET, the C64 and the C128. Nevertheless, all Commodore source files, from 1975 to 1990, share a common format and use the same assembly directives. This series of articles describes each of these assemblers. Year Company ... <a title="Commodore&#8217;s Assemblers: Overview" class="read-more" href="https://www.pagetable.com/?p=1518">Read more<span class="screen-reader-text">Commodore&#8217;s Assemblers: Overview</span></a>]]></description>
  3361. <content:encoded><![CDATA[<p>Commodore used 5 different assemblers, most of them in-house tools, to build the ROMs for their Computers like the PET, the C64 and the C128. Nevertheless, <a href="https://github.com/mist64/cbmsrc">all Commodore source files</a>, from 1975 to 1990, share a common format and use the same assembly directives. This series of articles describes each of these assemblers.</p>
  3362. <table>
  3363. <thead>
  3364. <tr>
  3365. <th> Year  </th>
  3366. <th> Company   </th>
  3367. <th> Assembler            </th>
  3368. <th> Platform              </th>
  3369. <th> Encoding                    </th>
  3370. </tr>
  3371. </thead>
  3372. <tbody>
  3373. <tr>
  3374. <td> 1975  </td>
  3375. <td> MOS       </td>
  3376. <td> 6502 Cross-Assembler </td>
  3377. <td> GE, NCSS time-sharing, &hellip; </td>
  3378. <td> various (upper case)        </td>
  3379. </tr>
  3380. <tr>
  3381. <td> 1976  </td>
  3382. <td> MOS       </td>
  3383. <td> Resident Assembler   </td>
  3384. <td> MDT650, KIM-1, PET, C64, CBM2, TED, C128 </td>
  3385. <td> ASCII (upper case, CR) </td>
  3386. </tr>
  3387. <tr>
  3388. <td> 1984  </td>
  3389. <td> BSO       </td>
  3390. <td> CY6502               </td>
  3391. <td> VAX                   </td>
  3392. <td> ASCII (mixed case, CRLF)    </td>
  3393. </tr>
  3394. <tr>
  3395. <td> 1986  </td>
  3396. <td> Commodore </td>
  3397. <td> HCD65                </td>
  3398. <td> C128                  </td>
  3399. <td> PETSCII (mixed case, CR)    </td>
  3400. </tr>
  3401. <tr>
  3402. <td> 1989  </td>
  3403. <td> Commodore </td>
  3404. <td> 6502ASM              </td>
  3405. <td> VAX, Amiga, PC        </td>
  3406. <td> ASCII (mixed case, LF/CRLF) </td>
  3407. </tr>
  3408. </tbody>
  3409. </table>
  3410. <h2 id="series-overview">Series Overview</h2>
  3411. <ul>
  3412. <li><strong>Part 0: Overview</strong> ← this article</li>
  3413. <li><a href="https://www.pagetable.com/?p=1520">Part 1: MOS Cross-Assembler</a></li>
  3414. <li><a href="https://www.pagetable.com/?p=1522">Part 2: MOS Resident Assembler</a></li>
  3415. <li><a href="https://www.pagetable.com/?p=1538">Part 3: BSO CY6502</a></li>
  3416. <li><a href="https://www.pagetable.com/?p=1540">Part 4: HCD65</a></li>
  3417. <li><a href="https://www.pagetable.com/?p=1542">Part 5: 6502ASM</a></li>
  3418. </ul>
  3419. <p><img src="docs/cbmasm/cbmasm_history.png" alt="" /></p>
  3420. <h2 id="cross-assembler-and-resident-assembler">Cross-Assembler and Resident Assembler</h2>
  3421. <p>In late 1975, MOS Technology, Inc. introduced the 6502 CPU and in 1976, they released the KIM-1, a demonstration/development platform for the 6502. Commodore bought MOS in November 1976, and the 6502 and the KIM-1 became Commodore products.</p>
  3422. <p>MOS also developed two assemblers for the 6502:</p>
  3423. <ul>
  3424. <li>The &ldquo;Cross-Assembler&rdquo; (1975), available for various mainframes and minicomputers.</li>
  3425. <li>The &ldquo;Resident Assembler&rdquo; (1976), running on 6502 systems. It was ported to all Commodore 8-bit computers. The C64 version was sold as the &ldquo;C64 Macro Assembler&rdquo; in 1982.</li>
  3426. </ul>
  3427. <p>Both assemblers were compatible in that they understood the same source format, with the same math features and the same directives and options.</p>
  3428. <p><strong>Read more: <a href="https://www.pagetable.com/?p=1520">Commodore&rsquo;s Assemblers: Part 1: MOS Cross-Assembler</a></strong><br />
  3429. <strong>Read more: <a href="https://www.pagetable.com/?p=1522">Commodore&rsquo;s Assemblers: Part 2: MOS Resident Assembler</a></strong></p>
  3430. <h2 id="bso-cy6502-(vax)">BSO CY6502 (VAX)</h2>
  3431. <p>In mid-1984, Commodore switched to &ldquo;CY6502&rdquo; by the company Boston Systems Office (BSO), a cross-assembler running on VAX/VMS systems that was highly compatible to the MOS assemblers, but more advanced.</p>
  3432. <p><strong>Read more: <a href="https://www.pagetable.com/?p=1538">Commodore&rsquo;s Assemblers: Part 3: BSO</a></strong></p>
  3433. <h2 id="hcd65-(c128)">HCD65 (C128)</h2>
  3434. <p>In 1986, Commodore wrote a new assembler named &ldquo;HCD65&rdquo; for the C128 that aimed at full compatibility with the BSO assembler. They sold it as part of the Commodore 128 Developer&rsquo;s Package in 1987. In 1989, as Commodore worked on the ill-fated C65, they added support for the extended 65CE02/4510 instruction set.</p>
  3435. <p><strong>Read more: <a href="https://www.pagetable.com/?p=1540">Commodore&rsquo;s Assemblers: Part 4: HCD65</a></strong></p>
  3436. <h2 id="6502asm-(vax,-amiga,-pc)">6502ASM (VAX, Amiga, PC)</h2>
  3437. <p>Also in 1989, and also for the C65 project, they wrote a new cross-platform assembler from scratch to replace the BSO one on VAX/VMS. It was supposed to be fully backwards-compatible and support the 65CE02/4510 instruction set from the start.</p>
  3438. <p><strong>Read more: <a href="https://www.pagetable.com/?p=1542">Commodore&rsquo;s Assemblers: Part 5: 6502ASM</a></strong></p>
  3439. <h2 id="others">Others</h2>
  3440. <p>There are two more assemblers that were used to develop the ROMs of Commodore computers that don&rsquo;t really count as in-house tools:</p>
  3441. <h3 id="macro-10-(pdp-10)">MACRO-10 (PDP-10)</h3>
  3442. <p>All Commodore 8-bit computers shipped with a version of Microsoft BASIC. Microsoft had used a PDP-10 mainframe for cross-developing the BASIC interpreter. Instead of writing a cross-assembler from scratch, they reused the MACRO-10 assembler that came with the PDP-10 and defined a set of macros that emitted 6502 opcodes. The article <a href="https://www.pagetable.com/?p=774">Microsoft BASIC for 6502 Original Source Code [1978]</a> has more information.</p>
  3443. <p>For the first two versions of the PET ROM, Microsoft delivered the BASIC binary together with the source to Commodore. After BASIC V2, Commodore adapted it to their own assemblers and built it themselves – so Microsoft&rsquo;s development tools were never used by Commodore.</p>
  3444. <h3 id="merlin-128-(c128)">Merlin 128 (C128)</h3>
  3445. <p>The Merlin 128 Macro Assembler by Glen Bredon was a commercial assembler for the C128. It was used by <a href="http://6502.org/users/sjgray/dj/">Dennis Jarvis</a> while he worked on the DOS of the Commodore 65. Jarvis had used Merlin for his personal projects before, and it had become the tool of his choice.</p>
  3446. <p>He started out with the source of the CBM 8250 disk drive ROM, converted it from Commodore&rsquo;s format to Merlin (PETSCII) format, and developed on top of it. The 65CE02/4510 extensions were used through a set of macros.</p>
  3447. <p>Towards the end of the project, the C65 DOS code was ported from Merlin to the cross-platform Commodore 6502ASM.</p>
  3448. ]]></content:encoded>
  3449. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1518</wfw:commentRss>
  3450. <slash:comments>0</slash:comments>
  3451. </item>
  3452. <item>
  3453. <title>Reverse-Engineered geoWrite 2.1 for C64 Source Code</title>
  3454. <link>https://www.pagetable.com/?p=1512</link>
  3455. <comments>https://www.pagetable.com/?p=1512#comments</comments>
  3456. <pubDate>Mon, 23 Nov 2020 14:26:48 +0000</pubDate>
  3457. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  3458. <category><![CDATA[6502]]></category>
  3459. <category><![CDATA[Archeology]]></category>
  3460. <category><![CDATA[C64]]></category>
  3461. <category><![CDATA[Commodore]]></category>
  3462. <category><![CDATA[GEOS]]></category>
  3463. <category><![CDATA[Operating Systems]]></category>
  3464.  
  3465. <guid isPermaLink="false">https://www.pagetable.com/?p=1512</guid>
  3466. <description><![CDATA[geoWrite is a WYSIWYG rich text editor for the Commodore 64 GEOS operating system. I created a reverse-engineered source version of the geoWrite 2.1 for the C64 (English and German) for the cc65 compiler suite: https://github.com/mist64/geowrite The source compiles into the exact same binaries as the English and German versions of geoWrite 2.1 included with ... <a title="Reverse-Engineered geoWrite 2.1 for C64 Source Code" class="read-more" href="https://www.pagetable.com/?p=1512">Read more<span class="screen-reader-text">Reverse-Engineered geoWrite 2.1 for C64 Source Code</span></a>]]></description>
  3467. <content:encoded><![CDATA[<p>geoWrite is a WYSIWYG rich text editor for the Commodore 64 GEOS operating system. I created a reverse-engineered source version of the geoWrite 2.1 for the C64 (English and German) for the cc65 compiler suite:</p>
  3468. <p><a href="https://github.com/mist64/geowrite">https://github.com/mist64/geowrite</a></p>
  3469. <p>The source compiles into the exact same binaries as the English and German versions of geoWrite 2.1 included with GEOS 2.0.</p>
  3470. <p>Not all code has been commented yet, contributions are welcome.</p>
  3471. <p>The pagetable.com article series on geoWrite internals is based on the results of this reverse-engineering effort. Here is the list of articles in the series again:</p>
  3472. <ol>
  3473. <li><a href="https://www.pagetable.com/?p=1425">The Overlay System</a></li>
  3474. <li><a href="https://www.pagetable.com/?p=1428">Screen Recovery</a></li>
  3475. <li><a href="https://www.pagetable.com/?p=1436">Font Management</a></li>
  3476. <li><a href="https://www.pagetable.com/?p=1442">Zero Page</a></li>
  3477. <li><a href="https://www.pagetable.com/?p=1449">Copy Protection</a></li>
  3478. <li><a href="https://www.pagetable.com/?p=1460">Localization</a></li>
  3479. <li><a href="https://www.pagetable.com/?p=1471">File Format and Pagination</a></li>
  3480. <li><a href="https://www.pagetable.com/?p=1481">Copy &amp; Paste</a></li>
  3481. <li><a href="https://www.pagetable.com/?p=1490">Keyboard Handling</a></li>
  3482. </ol>
  3483. ]]></content:encoded>
  3484. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1512</wfw:commentRss>
  3485. <slash:comments>3</slash:comments>
  3486. </item>
  3487. <item>
  3488. <title>Diskettenlaufwerke am Beispiel der Commodore 1541 [video]</title>
  3489. <link>https://www.pagetable.com/?p=1505</link>
  3490. <comments>https://www.pagetable.com/?p=1505#comments</comments>
  3491. <pubDate>Tue, 20 Oct 2020 16:30:15 +0000</pubDate>
  3492. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  3493. <category><![CDATA[6502]]></category>
  3494. <category><![CDATA[C64]]></category>
  3495. <category><![CDATA[Commodore]]></category>
  3496. <category><![CDATA[Floppy Disks]]></category>
  3497.  
  3498. <guid isPermaLink="false">https://www.pagetable.com/?p=1505</guid>
  3499. <description><![CDATA[Dieser Vortrag wurde am 11. Oktober 2020 auf dem (virtuellen) Vintage Computing Festival Berlin gehalten. Diskettenlaufwerke am Beispiel der Commodore 1541 Aus der Homecomputer- und frühen Personal-Computer-Zeit sind Disketten nicht wegzudenken. Dieser Vortrag beschäftigt sich mit Diskettentechnologie am Beispiel des 5,25-Zoll-Laufwerks &#8220;Commodore 1541&#8221;, bekannt als das Laufwerk zum Commodore C64. Nach einer historischen Einordnung (Bänder, ... <a title="Diskettenlaufwerke am Beispiel der Commodore 1541 [video]" class="read-more" href="https://www.pagetable.com/?p=1505">Read more<span class="screen-reader-text">Diskettenlaufwerke am Beispiel der Commodore 1541 [video]</span></a>]]></description>
  3500. <content:encoded><![CDATA[<p><iframe width="1024" height="576" src="https://media.ccc.de/v/vcfb20_-_149_-_de_-_202010111700_-_diskettenlaufwerke_am_beispiel_der_commodore_1541_-_michael_steil/oembed" frameborder="0" allowfullscreen></iframe></p>
  3501. <p>Dieser Vortrag wurde am 11. Oktober 2020 auf dem (virtuellen) <a href="http://vcfb.de/">Vintage Computing Festival Berlin</a> gehalten.</p>
  3502. <p><b>Diskettenlaufwerke am Beispiel der Commodore 1541</b></p>
  3503. <p>Aus der Homecomputer- und frühen Personal-Computer-Zeit sind Disketten nicht wegzudenken. Dieser Vortrag beschäftigt sich mit Diskettentechnologie am Beispiel des 5,25-Zoll-Laufwerks &#8220;Commodore 1541&#8221;, bekannt als das Laufwerk zum Commodore C64. Nach einer historischen Einordnung (Bänder, Platten, 8-Zoll-Disketten) besprechen wir den Aufbau von Laufwerken und Disketten, sowie das Low-Level-Aufzeichnungsformat (Spuren, Sektoren, SYNC-Marker, GCR-Codierung) und dessen Implementierung in der Laufwerks-Firmware. Danach behandeln wir das Dateisystem-Format und die Datenübertragung zwischen dem Laufwerk und dem C64. Wir thematisieren außerdem Schnelllader, welche die Laufwerks-Firmware durch optimierteren Code zum Lesen und zur Datenübertragung ersetzen, sowie Kopierschutz-Systeme, die Nonstandard-Formate mit verschleierten Leseroutinen verbinden. Schließlich sprechen wir noch über Lösungen zum fehlerfreien Auslesen von antiken Disketten mit moderner Hardware.</p>
  3504. ]]></content:encoded>
  3505. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1505</wfw:commentRss>
  3506. <slash:comments>7</slash:comments>
  3507. </item>
  3508. <item>
  3509. <title>Ankündigung: Vortrag &#8220;Diskettenlaufwerke am Beispiel der Commodore 1541&#8221; am VCFB 2020</title>
  3510. <link>https://www.pagetable.com/?p=1502</link>
  3511. <comments>https://www.pagetable.com/?p=1502#respond</comments>
  3512. <pubDate>Sat, 10 Oct 2020 09:49:27 +0000</pubDate>
  3513. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  3514. <category><![CDATA[6502]]></category>
  3515. <category><![CDATA[C64]]></category>
  3516. <category><![CDATA[Commodore]]></category>
  3517. <category><![CDATA[Floppy Disks]]></category>
  3518. <category><![CDATA[Presentation]]></category>
  3519.  
  3520. <guid isPermaLink="false">https://www.pagetable.com/?p=1502</guid>
  3521. <description><![CDATA[This post is about an upcoming talk in German. Update: Mitschnitt verfügbar! Am Sonntag, den 11. Oktober 2020 um 17:00 gibt es auf dem (virtuellen) Vintage Computing Festival Berlin meinen Vortrag Diskettenlaufwerke am Beispiel der Commodore 1541. Der Vortrag wird live gestreamt; ein Mitschnitt wird danach verfügbar sein. Diskettenlaufwerke am Beispiel der Commodore 1541 Aus ... <a title="Ankündigung: Vortrag &#8220;Diskettenlaufwerke am Beispiel der Commodore 1541&#8221; am VCFB 2020" class="read-more" href="https://www.pagetable.com/?p=1502">Read more<span class="screen-reader-text">Ankündigung: Vortrag &#8220;Diskettenlaufwerke am Beispiel der Commodore 1541&#8221; am VCFB 2020</span></a>]]></description>
  3522. <content:encoded><![CDATA[<p><i>This post is about an upcoming talk in German.</i></p>
  3523. <p><b>Update: <a href="https://www.pagetable.com/?p=1505">Mitschnitt</a> verfügbar!</b></p>
  3524. <p>Am Sonntag, den 11. Oktober 2020 um 17:00 gibt es auf dem (virtuellen) <a href="http://vcfb.de/">Vintage Computing Festival Berlin</a> meinen Vortrag <a href="https://wiki.vcfb.de/2020/vortraege_workshops#diskettenlaufwerke_am_beispiel_der_commodore_1541">Diskettenlaufwerke am Beispiel der Commodore 1541</a>.</p>
  3525. <p>Der Vortrag wird <a href="https://wiki.vcfb.de/2020/stream">live gestreamt</a>; ein Mitschnitt wird danach verfügbar sein.</p>
  3526. <p><img src="docs/vcfb-1541.png" width="800" height="586"></p>
  3527. <p><b>Diskettenlaufwerke am Beispiel der Commodore 1541</b></p>
  3528. <p>Aus der Homecomputer- und frühen Personal-Computer-Zeit sind Disketten nicht wegzudenken. Dieser Vortrag beschäftigt sich mit Diskettentechnologie am Beispiel des 5,25-Zoll-Laufwerks &#8220;Commodore 1541&#8221;, bekannt als das Laufwerk zum Commodore C64. Nach einer historischen Einordnung (Bänder, Platten, 8-Zoll-Disketten) besprechen wir den Aufbau von Laufwerken und Disketten, sowie das Low-Level-Aufzeichnungsformat (Spuren, Sektoren, SYNC-Marker, GCR-Codierung) und dessen Implementierung in der Laufwerks-Firmware. Danach behandeln wir das Dateisystem-Format und die Datenübertragung zwischen dem Laufwerk und dem C64. Wir thematisieren außerdem Schnelllader, welche die Laufwerks-Firmware durch optimierteren Code zum Lesen und zur Datenübertragung ersetzen, sowie Kopierschutz-Systeme, die Nonstandard-Formate mit verschleierten Leseroutinen verbinden. Schließlich sprechen wir noch über Lösungen zum fehlerfreien Auslesen von antiken Disketten mit moderner Hardware.</p>
  3529. ]]></content:encoded>
  3530. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1502</wfw:commentRss>
  3531. <slash:comments>0</slash:comments>
  3532. </item>
  3533. <item>
  3534. <title>Inside geoWrite – 9: Keyboard Handling</title>
  3535. <link>https://www.pagetable.com/?p=1490</link>
  3536. <comments>https://www.pagetable.com/?p=1490#comments</comments>
  3537. <pubDate>Thu, 24 Sep 2020 14:19:10 +0000</pubDate>
  3538. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  3539. <category><![CDATA[6502]]></category>
  3540. <category><![CDATA[Archeology]]></category>
  3541. <category><![CDATA[C64]]></category>
  3542. <category><![CDATA[Commodore]]></category>
  3543. <category><![CDATA[GEOS]]></category>
  3544. <category><![CDATA[Operating Systems]]></category>
  3545.  
  3546. <guid isPermaLink="false">https://www.pagetable.com/?p=1490</guid>
  3547. <description><![CDATA[In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses how the app consolidates keyboard input to keep up with fast typists. Article Series The Overlay System Screen Recovery Font Management Zero Page Copy Protection Localization File Format and Pagination Copy &#38; Paste Keyboard Handling ← this ... <a title="Inside geoWrite – 9: Keyboard Handling" class="read-more" href="https://www.pagetable.com/?p=1490">Read more<span class="screen-reader-text">Inside geoWrite – 9: Keyboard Handling</span></a>]]></description>
  3548. <content:encoded><![CDATA[<p>In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses how the app consolidates keyboard input to keep up with fast typists.</p>
  3549. <p><img src="docs/geowrite/geowrite_9_keyboard.gif" height="400" width="640" alt="" /></p>
  3550. <h2 id="article-series">Article Series</h2>
  3551. <ol>
  3552. <li><a href="https://www.pagetable.com/?p=1425">The Overlay System</a></li>
  3553. <li><a href="https://www.pagetable.com/?p=1428">Screen Recovery</a></li>
  3554. <li><a href="https://www.pagetable.com/?p=1436">Font Management</a></li>
  3555. <li><a href="https://www.pagetable.com/?p=1442">Zero Page</a></li>
  3556. <li><a href="https://www.pagetable.com/?p=1449">Copy Protection</a></li>
  3557. <li><a href="https://www.pagetable.com/?p=1460">Localization</a></li>
  3558. <li><a href="https://www.pagetable.com/?p=1471">File Format and Pagination</a></li>
  3559. <li><a href="https://www.pagetable.com/?p=1481">Copy &amp; Paste</a></li>
  3560. <li><strong>Keyboard Handling</strong> ← this article</li>
  3561. </ol>
  3562. <h2 id="geos-keyboard-basics">GEOS Keyboard Basics</h2>
  3563. <p>GEOS does not use the keyboard driver in the C64&rsquo;s ROM, but has its own implementation that uses ASCII-based 8 bit key codes: Codes $20 through $7F are the regular ASCII printable characters. All control keys (e.g. cursor keys) as well as non-ASCII keys (like &ldquo;£&rdquo;) are mapped into the control code space $00 through $1F:</p>
  3564. <table>
  3565. <thead>
  3566. <tr>
  3567. <th> Code </th>
  3568. <th> Key        </th>
  3569. <th> Comment          </th>
  3570. </tr>
  3571. </thead>
  3572. <tbody>
  3573. <tr>
  3574. <td> $00  </td>
  3575. <td>            </td>
  3576. <td> no key           </td>
  3577. </tr>
  3578. <tr>
  3579. <td> $01  </td>
  3580. <td> F1         </td>
  3581. <td>                  </td>
  3582. </tr>
  3583. <tr>
  3584. <td> $02  </td>
  3585. <td> F2         </td>
  3586. <td>                  </td>
  3587. </tr>
  3588. <tr>
  3589. <td> $03  </td>
  3590. <td> F3         </td>
  3591. <td>                  </td>
  3592. </tr>
  3593. <tr>
  3594. <td> $04  </td>
  3595. <td> F4         </td>
  3596. <td>                  </td>
  3597. </tr>
  3598. <tr>
  3599. <td> $05  </td>
  3600. <td> F5         </td>
  3601. <td>                  </td>
  3602. </tr>
  3603. <tr>
  3604. <td> $06  </td>
  3605. <td> F6         </td>
  3606. <td>                  </td>
  3607. </tr>
  3608. <tr>
  3609. <td> $07  </td>
  3610. <td> NO SCROLL  </td>
  3611. <td> C128 only        </td>
  3612. </tr>
  3613. <tr>
  3614. <td> $08  </td>
  3615. <td> CRSR←      </td>
  3616. <td> C64: SHIFT+CRSR→ </td>
  3617. </tr>
  3618. <tr>
  3619. <td> $09  </td>
  3620. <td> TAB        </td>
  3621. <td> C64: Ctrl+I      </td>
  3622. </tr>
  3623. <tr>
  3624. <td> $0A  </td>
  3625. <td> LF         </td>
  3626. <td> C128 only        </td>
  3627. </tr>
  3628. <tr>
  3629. <td> $0B  </td>
  3630. <td> ENTER      </td>
  3631. <td> C128 only        </td>
  3632. </tr>
  3633. <tr>
  3634. <td> $0C  </td>
  3635. <td>            </td>
  3636. <td> unused           </td>
  3637. </tr>
  3638. <tr>
  3639. <td> $0D  </td>
  3640. <td>            </td>
  3641. <td> unused           </td>
  3642. </tr>
  3643. <tr>
  3644. <td> $0E  </td>
  3645. <td> F7         </td>
  3646. <td>                  </td>
  3647. </tr>
  3648. <tr>
  3649. <td> $0F  </td>
  3650. <td> F8         </td>
  3651. <td>                  </td>
  3652. </tr>
  3653. <tr>
  3654. <td> $10  </td>
  3655. <td> CRSR↑      </td>
  3656. <td> C64: SHIFT+CRSR↓ </td>
  3657. </tr>
  3658. <tr>
  3659. <td> $11  </td>
  3660. <td> CRSR↓      </td>
  3661. <td>                  </td>
  3662. </tr>
  3663. <tr>
  3664. <td> $12  </td>
  3665. <td> HOME       </td>
  3666. <td>                  </td>
  3667. </tr>
  3668. <tr>
  3669. <td> $13  </td>
  3670. <td> SHIFT+HOME </td>
  3671. <td> &ldquo;CLR&rdquo;            </td>
  3672. </tr>
  3673. <tr>
  3674. <td> $14  </td>
  3675. <td> ←          </td>
  3676. <td>                  </td>
  3677. </tr>
  3678. <tr>
  3679. <td> $15  </td>
  3680. <td> UPARROW    </td>
  3681. <td>                  </td>
  3682. </tr>
  3683. <tr>
  3684. <td> $16  </td>
  3685. <td> STOP       </td>
  3686. <td>                  </td>
  3687. </tr>
  3688. <tr>
  3689. <td> $17  </td>
  3690. <td> SHIFT+STOP </td>
  3691. <td> &ldquo;RUN&rdquo;            </td>
  3692. </tr>
  3693. <tr>
  3694. <td> $18  </td>
  3695. <td> £          </td>
  3696. <td>                  </td>
  3697. </tr>
  3698. <tr>
  3699. <td> $19  </td>
  3700. <td> HELP       </td>
  3701. <td> C128 only        </td>
  3702. </tr>
  3703. <tr>
  3704. <td> $1A  </td>
  3705. <td> ALT        </td>
  3706. <td> C128 only        </td>
  3707. </tr>
  3708. <tr>
  3709. <td> $1B  </td>
  3710. <td> ESC        </td>
  3711. <td> C128 only        </td>
  3712. </tr>
  3713. <tr>
  3714. <td> $1C  </td>
  3715. <td> SHIFT+DEL  </td>
  3716. <td> &ldquo;INST&rdquo;           </td>
  3717. </tr>
  3718. <tr>
  3719. <td> $1D  </td>
  3720. <td> DEL        </td>
  3721. <td>                  </td>
  3722. </tr>
  3723. <tr>
  3724. <td> $1E  </td>
  3725. <td> CRSR→      </td>
  3726. <td>                  </td>
  3727. </tr>
  3728. <tr>
  3729. <td> $1F  </td>
  3730. <td>            </td>
  3731. <td> used internally  </td>
  3732. </tr>
  3733. </tbody>
  3734. </table>
  3735. <p>All this defines the codes $00-$7F, which accounts for seven bits. The uppermost bit (value of $80) indicates whether the &ldquo;Commodore&rdquo; modifier key has been pressed with the key. The Commodore key is used in GEOS for keyboard shortcuts, much like the Command key on the Macintosh.</p>
  3736. <h3 id="keyboard-buffer">Keyboard Buffer</h3>
  3737. <p>The keyboard driver runs in the interrupt context, which is triggered 60 times per second by a timer. It scans the keyboard and puts new key codes at the end of a 16 byte queue: This way, no keys will be lost if the application can&rsquo;t get to handling incoming keys immediately – as long as it&rsquo;s not more than 16 keys behind.</p>
  3738. <p>The application can get the next key from the queue by calling <code>GetNextChar</code>. If there is a key in the queue, it will be removed from the queue and returned. Otherwise, a value of 0 will be returned.</p>
  3739. <p>All this is pretty much what&rsquo;s going on in any operating system – including the C64&rsquo;s original ROM in BASIC mode.</p>
  3740. <h3 id="callback">Callback</h3>
  3741. <p>GEOS is an event-driven environment: The system&rsquo;s &ldquo;main loop&rdquo; controls all execution and calls back the app based on events, like mouse clicks, key presses and timers.</p>
  3742. <p>The idiomatic way to handle the keyboard on GEOS is therefore to register for keyboard callbacks. On every main loop iteration, the system will check whether there is at least one key in the queue. If this is the case, it will dequeue the oldest key, store it in the system variable <code>keyData</code> and call the function pointer <code>keyVector</code>, which the app can set. If the vector is 0 (the default), no callback will happen and the key code will be discarded.</p>
  3743. <p>The app can then read the key code from <code>keyData</code>, handle it and return. If there are more keys in the queue, the main loop will call the vector again, with the next key code, in the next iteration.</p>
  3744. <p>Alternatively, the callback function can read more keys from the queue by repeatedly calling <code>GetNextChar</code>.</p>
  3745. <h2 id="geowrite">geoWrite</h2>
  3746. <p>The core function of geoWrite is text input, so it needs to make sure it is responsive, and no key presses get lost.</p>
  3747. <p>And that&rsquo;s tricky: On a 1 MHz CPU, redrawing several lines of proportional fonts can take seconds, so it is quite common that the app falls behind the user&rsquo;s typing. So if the user types a character that leads to a few lines being redrawn, and the user types three characters in the meantime, these three characters may cause three redraws, in which time the user can type another nine characters. The app would fall more and more behind, and eventually drop characters.</p>
  3748. <p>To avoid this, the app has to catch up to the user&rsquo;s typing. The good news is that once the app is behind on keys, it has several key codes to work with, so there are some optimizations that can be applied:</p>
  3749. <h3 id="consolidating-character-inserts">Consolidating Character Inserts</h3>
  3750. <p>When inserting a single character, the text data after the cursor is moved up by one byte and the new character is added. Then, the screen is updated: The part of the current line to the right of the cursor is redrawn, and if the last word of the line overflows to the next line, that one has to be redrawn as well and so on.</p>
  3751. <p>The following animation shows this in action:</p>
  3752. <p><img src="docs/geowrite/geowrite_9_keyboard.gif" height="400" width="640" alt="" /></p>
  3753. <p>Instead of doing this work for every character in the keyboard buffer separately, a lot of work can be saved by doing it for all characters in one go:</p>
  3754. <ul>
  3755. <li>Only move the buffer up once, by the number of characters in the buffer.</li>
  3756. <li>Copy the characters into the buffer.</li>
  3757. <li>Redraw only once.</li>
  3758. </ul>
  3759. <p>This is basically the same as what happens when <a href="https://www.pagetable.com/?p=1481">pasting text from a text scrap</a>.</p>
  3760. <p>You can see the effect of this strategy on the animation above. The &ldquo;a&rdquo; key was held down on the keyboard, generating a fast stream of &ldquo;a&rdquo; key codes.</p>
  3761. <ul>
  3762. <li>The first character is inserted, which triggers a redraw of just the line.</li>
  3763. <li>In the meantime, two more characters came in, which are added in one go. This triggers the redraw of the whole paragraph, in this case, since the word &ldquo;Commodore&rdquo; had to be moved to the next line.</li>
  3764. <li>This redraw was so expensive that seven more characters came in in the meantime. The line gets redrawn, and it does not overflow this time.</li>
  3765. <li>Because this redraw was cheap, only two more characters came in. Again, it causes a redraw of only the current line.</li>
  3766. </ul>
  3767. <p>We see in practice that whenever more text needs to be redrawn, more keypresses are buffered, but the system catches up quickly.</p>
  3768. <p>All printable characters as well as line break and TAB characters can be combined into a single string to be inserted. All other keys have to be special cased.</p>
  3769. <h3 id="del-characters">DEL Characters</h3>
  3770. <p>If the user types a character, and then hits the DEL key, the document will effectively be unchanged, but the text in memory was first moved up by one byte, then moved down by one byte again, and the screen was updated twice.</p>
  3771. <p>So if the keyboard buffer contains a DEL char, geoWrites removes the preceding character from the buffer. The two characters cancel each other out and no more work has to be done.</p>
  3772. <p>If a DEL is encountered while the buffer is empty, no character can be removed, but it&rsquo;s not a character that can be inserted either. geoWrite then increments the count of DEL keys it has seen in the keyboard buffer that remained. There are some examples after the next paragraph.</p>
  3773. <h3 id="control-keys-and-keyboard-shortcuts">Control Keys and Keyboard Shortcuts</h3>
  3774. <p>Non-printable key codes like cursor keys and keyboard shortcuts are also special. If the queue contains &ldquo;abc&rdquo;, followed by C=T (Commodore Key + T; paste text) and &ldquo;def&rdquo;, geoWrite has to insert &ldquo;abc&rdquo;, paste the text in the text scrap, and then insert &ldquo;def&rdquo;. It can not combine the two text strings.</p>
  3775. <p>This is why whenever a control key or shortcut is encountered, geoWrite stops processing the queue. The DEL characters, the string so far and the control key will be evaluated, and the remainder of the keyboard queue will be handled once the <code>keyVector</code> is called the next time.</p>
  3776. <h3 id="examples">Examples</h3>
  3777. <p>Here are some examples of contents of the keyboard queue, the number of DEL characters at the beginning, the string to be inserted, the control key that was detected, and the contents of the keyboard queue after this processing:</p>
  3778. <table>
  3779. <thead>
  3780. <tr>
  3781. <th> Kbd Queue Before            </th>
  3782. <th>  # DEL </th>
  3783. <th> Insert String </th>
  3784. <th> Control Key </th>
  3785. <th> Kbd Queue After </th>
  3786. </tr>
  3787. </thead>
  3788. <tbody>
  3789. <tr>
  3790. <td> <code>abc</code>                       </td>
  3791. <td> 0      </td>
  3792. <td> <code>abc</code>         </td>
  3793. <td>             </td>
  3794. <td>                 </td>
  3795. </tr>
  3796. <tr>
  3797. <td> <code>abc&lt;DEL&gt;</code>                  </td>
  3798. <td> 0      </td>
  3799. <td> <code>ab</code>          </td>
  3800. <td>             </td>
  3801. <td>                 </td>
  3802. </tr>
  3803. <tr>
  3804. <td> <code>abc&lt;DEL&gt;d</code>                 </td>
  3805. <td> 0      </td>
  3806. <td> <code>abd</code>         </td>
  3807. <td>             </td>
  3808. <td>                 </td>
  3809. </tr>
  3810. <tr>
  3811. <td> <code>abc&lt;DEL&gt;&lt;DEL&gt;DEL&gt;</code>         </td>
  3812. <td> 0      </td>
  3813. <td>               </td>
  3814. <td>             </td>
  3815. <td>                 </td>
  3816. </tr>
  3817. <tr>
  3818. <td> <code>abc&lt;DEL&gt;&lt;DEL&gt;DEL&gt;&lt;DEL&gt;</code>    </td>
  3819. <td> 1      </td>
  3820. <td>               </td>
  3821. <td>             </td>
  3822. <td>                 </td>
  3823. </tr>
  3824. <tr>
  3825. <td> <code>&lt;DEL&gt;abc</code>                  </td>
  3826. <td> 1      </td>
  3827. <td> <code>abc</code>         </td>
  3828. <td>             </td>
  3829. <td>                 </td>
  3830. </tr>
  3831. <tr>
  3832. <td> <code>&lt;DEL&gt;&lt;DEL&gt;abc</code>             </td>
  3833. <td> 2      </td>
  3834. <td> <code>abc</code>         </td>
  3835. <td>             </td>
  3836. <td>                 </td>
  3837. </tr>
  3838. <tr>
  3839. <td> <code>a&lt;DEL&gt;&lt;DEL&gt;bc</code>             </td>
  3840. <td> 1      </td>
  3841. <td> <code>bc</code>          </td>
  3842. <td>             </td>
  3843. <td>                 </td>
  3844. </tr>
  3845. <tr>
  3846. <td> <code>a&lt;C=T&gt;bc</code>                  </td>
  3847. <td> 0      </td>
  3848. <td> <code>a</code>           </td>
  3849. <td> <code>C=T</code>       </td>
  3850. <td> <code>bc</code>            </td>
  3851. </tr>
  3852. <tr>
  3853. <td> <code>a&lt;DEL&gt;&lt;DEL&gt;bc&lt;C=T&gt;&lt;DEL&gt;de</code> </td>
  3854. <td> 1      </td>
  3855. <td> <code>bc</code>          </td>
  3856. <td> <code>C=T</code>       </td>
  3857. <td> <code>&lt;DEL&gt;de</code>       </td>
  3858. </tr>
  3859. </tbody>
  3860. </table>
  3861. <p>The examples show that DEL keys that follow a character will effectively remove that key, but if the number of DEL key codes exceeds the number of characters before it, the extra DEL keys will be counted. And if there is a control key or keyboard shortcut, processing of the keyboard queue stops at this point.</p>
  3862. <h3 id="code">Code</h3>
  3863. <p>This is the code that processes the keyboard queue and returns the number of characters to delete, the string to insert, and the detected control key:</p>
  3864. <pre><code>processKbdQueue:  
  3865.        lda     #0  
  3866.        sta     delCount                ; no excess DEL keys so far  
  3867.        sta     kbdStringCnt            ; kbd string empty  
  3868.        sta     curControlKey           ; no control key
  3869.  
  3870.        lda     keyData                 ; first character, as passed into keyVector
  3871.  
  3872. @again: bmi     @ctrl                   ; "C=" shortcut, so stop processing
  3873.  
  3874.        cmp     #KEY_DELETE             ; DEL key?  
  3875.        beq     @del                    ; yes  
  3876.        cmp     #KEY_INSERT             ; SHIFT+DEL?  
  3877.        beq     @del                    ; yes (same as DEL in GEOS)
  3878.  
  3879.        cmp     #CR                     ; return key?  
  3880.        beq     @nctrl                  ; yes, does not count as a control key  
  3881.        cmp     #TAB                    ; TAB key?  
  3882.        beq     @nctrl                  ; yes, does not count as a control key
  3883.  
  3884.        cmp     #$20                    ; below $20, i.e. non-printable  
  3885.        bcc     @ctrl                   ; yes, it's a control key, stop processing
  3886.  
  3887. @nctrl: ldx     kbdStringCnt  
  3888.        sta     kbdString,x             ; add to kbd string  
  3889.        inc     kbdStringCnt  
  3890. @next:  jsr     GetNextChar             ; are there more characters in the kbd queue?  
  3891.        tax  
  3892.        beq     @rts                    ; no, return  
  3893.        bne     @again                  ; yes, repeat
  3894.  
  3895. @del:   ldx     kbdStringCnt            ; are there characters in the kbd string?  
  3896.        beq     @excss                  ; no, no characters to delete
  3897.  
  3898.        dec     kbdStringCnt            ; remove previous character  
  3899.        bra     @next                   ; and continue
  3900.  
  3901. @excss: inc     delCount                ; count excess delete characters  
  3902.        bne     @next                   ; and continue
  3903.  
  3904. @ctrl:  sta     curControlKey           ; save control key  
  3905. @rts:   rts
  3906. </code></pre>
  3907. <p>geoWrite&rsquo;s <code>keyVector</code> handler calls this, and then applies the three outputs (DELs, string, control key) to the document:</p>
  3908. <ol>
  3909. <li>First, if there are excess DELs, it deletes characters at the current cursor position according to the number of DELs. It just moves the remainder of the buffer down by the number of DELs.</li>
  3910. <li>Then, if there is a string, it inserts it in one go by moving the buffer up by the length of the string, and copying the string into the text.</li>
  3911. <li>If there were DELs or a string, the screen is updated.</li>
  3912. <li>If there is a control key, it gets evaluated.</li>
  3913. </ol>
  3914. <p>There are two special cases: If there are more DELs than characters on the current page, the extra DELs have to be handled differently. And if there are DELs <strong>and</strong> a string, there is an optimization performed in the first two steps: There is no need to move the buffer twice. For example</p>
  3915. <ul>
  3916. <li>if there is one DEL and two characters in the string, the buffer has to be moved up by one byte, and the insertion position is one byte before the cursor.</li>
  3917. <li>if there are two DELs and one character in the string, the buffer has to be moved down by one byte, and the insertion position is two bytes before the cursor.</li>
  3918. <li>if there are two DELs and two characters in the string, the buffer does not have to be moved at all, and the insertion position is two bytes before the cursor.</li>
  3919. </ul>
  3920. <h2 id="conclusion">Conclusion</h2>
  3921. <p>When designing software, a slow CPU can be made up for if there is a lot of memory, e.g. by caching data to avoid calculating it. And too little memory can be made up for by a fast CPU. GEOS and geoWrite had neither. They were written for an 8-bit CPU with 64 KB of RAM.</p>
  3922. <p>This scenario makes all design decisions interdependent:</p>
  3923. <ul>
  3924. <li>The memory constraint requires all code to be optimized for size, which makes it slower, and requires geoWrite to be <a href="https://www.pagetable.com/?p=1425">split into 9 parts</a> that are loaded from the slow disk drive.</li>
  3925. <li>The font renderer could have been much faster, had it had the space for fastpath code and caches.</li>
  3926. <li>Slow font rendering requires geoWrite to include lots of extra logic to be able to keep up with fast typists.</li>
  3927. <li>The extra code increases the memory pressure, requiring other code to be written more densely or pushed into different records.</li>
  3928. </ul>
  3929. <p>The GEOS engineers seem to have found a reasonable tradeoff. Yes, geoWrite is slow on an unexpanded C64, but it is a useful and very powerful tool for a 64 KB computer.</p>
  3930. ]]></content:encoded>
  3931. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1490</wfw:commentRss>
  3932. <slash:comments>5</slash:comments>
  3933. </item>
  3934. <item>
  3935. <title>Inside geoWrite – 8: Copy &#038; Paste</title>
  3936. <link>https://www.pagetable.com/?p=1481</link>
  3937. <comments>https://www.pagetable.com/?p=1481#comments</comments>
  3938. <pubDate>Tue, 22 Sep 2020 15:18:45 +0000</pubDate>
  3939. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  3940. <category><![CDATA[6502]]></category>
  3941. <category><![CDATA[Archeology]]></category>
  3942. <category><![CDATA[C64]]></category>
  3943. <category><![CDATA[Commodore]]></category>
  3944. <category><![CDATA[GEOS]]></category>
  3945. <category><![CDATA[Operating Systems]]></category>
  3946.  
  3947. <guid isPermaLink="false">https://www.pagetable.com/?p=1481</guid>
  3948. <description><![CDATA[In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses its efficient cross-application cut/copy/paste implementation. Article Series The Overlay System Screen Recovery Font Management Zero Page Copy Protection Localization File Format and Pagination Copy &#38; Paste ← this article Keyboard Handling GEOS Scrap Architecture Just like modern ... <a title="Inside geoWrite – 8: Copy &#038; Paste" class="read-more" href="https://www.pagetable.com/?p=1481">Read more<span class="screen-reader-text">Inside geoWrite – 8: Copy &#038; Paste</span></a>]]></description>
  3949. <content:encoded><![CDATA[<p>In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses its efficient cross-application cut/copy/paste implementation.</p>
  3950. <p><img src="docs/geowrite/geowrite_8_textscrap.png" height="400" width="640" alt="" /></p>
  3951. <h2 id="article-series">Article Series</h2>
  3952. <ol>
  3953. <li><a href="https://www.pagetable.com/?p=1425">The Overlay System</a></li>
  3954. <li><a href="https://www.pagetable.com/?p=1428">Screen Recovery</a></li>
  3955. <li><a href="https://www.pagetable.com/?p=1436">Font Management</a></li>
  3956. <li><a href="https://www.pagetable.com/?p=1442">Zero Page</a></li>
  3957. <li><a href="https://www.pagetable.com/?p=1449">Copy Protection</a></li>
  3958. <li><a href="https://www.pagetable.com/?p=1460">Localization</a></li>
  3959. <li><a href="https://www.pagetable.com/?p=1471">File Format and Pagination</a></li>
  3960. <li><strong>Copy &amp; Paste</strong> ← this article</li>
  3961. <li><a href="https://www.pagetable.com/?p=1490">Keyboard Handling</a></li>
  3962. </ol>
  3963. <h2 id="geos-scrap-architecture">GEOS Scrap Architecture</h2>
  3964. <p>Just like modern operating systems, GEOS supports cut, copy and paste, within one app as well as between multiple apps. But this is about where the similarities end.</p>
  3965. <p>First of all, there is no single clipboard/pasteboard: There is one for every type of data, and they are called &ldquo;scraps&rdquo;. There can be a &ldquo;text scrap&rdquo; and a &ldquo;photo scrap&rdquo; (image data) at the same time, for example. When the user selects &ldquo;paste&rdquo; in the &ldquo;edit&rdquo; menu, geoWrite asks what type should be pasted:</p>
  3966. <p><img src="docs/geowrite/geowrite_8_paste.png" height="400" width="640" alt="" /></p>
  3967. <p>The GEOS KERNAL has no concept of scraps – they are purely a convention between apps. Text and photo scraps are specified by the <a href="https://archive.org/details/The_Official_GEOS_Programmers_Reference_Guide">GEOS reference manual</a> and apps like geoWrite, geoPaint and geoPublish implement this specification.</p>
  3968. <p>Since scraps can be rather large, they are stored as files on disk. Copying text between two apps basically means that one app writes a file with a defined file name in a standardized format, and the other app reads it. As a side effect, scraps are persistent even across reboots of the operating system. Here is a text scrap on disk:</p>
  3969. <p><img src="docs/geowrite/geowrite_8_textscrap.png" height="400" width="640" alt="" /></p>
  3970. <p>Text and photo scraps are the two types specified by GEOS, but since there is nothing special about scrap files, any application can use its own scrap format: geoCalc uses calc scraps, for example, which allows spreadsheet cells to be copied between documents.</p>
  3971. <p>By convention, scraps are sequential (non-VLIR) files with a name of &ldquo;<code>* Scrap</code>&rdquo; and a type of &ldquo;<code>* Scrap Vn.n</code>&rdquo;. &ldquo;<code>*</code>&rdquo; represents the type of scrap, space-padded to 5 characters:</p>
  3972. <table>
  3973. <thead>
  3974. <tr>
  3975. <th> App          </th>
  3976. <th> Filename      </th>
  3977. <th> Type String        </th>
  3978. </tr>
  3979. </thead>
  3980. <tbody>
  3981. <tr>
  3982. <td> geoWrite 2.1 </td>
  3983. <td> <tt>Text&nbsp;&nbsp;Scrap</tt> </td>
  3984. <td> <tt>Text&nbsp;&nbsp;Scrap&nbsp;V2.0</tt> </td>
  3985. </tr>
  3986. <tr>
  3987. <td> geoPaint 2.0 </td>
  3988. <td> <tt>Photo&nbsp;Scrap</tt> </td>
  3989. <td> <tt>Photo&nbsp;Scrap&nbsp;V1.1</tt> </td>
  3990. </tr>
  3991. <tr>
  3992. <td> geoCalc  1.0 </td>
  3993. <td> <tt>Calc&nbsp;&nbsp;Scrap</tt> </td>
  3994. <td> <tt>Calc&nbsp;&nbsp;Scrap&nbsp;V1.1</tt> </td>
  3995. </tr>
  3996. </tbody>
  3997. </table>
  3998. <p>As you can see from the type string, scraps are versioned, just like apps and documents. Apps should contain conversion code to accept older versions, and refuse to load versions that are newer than supported.</p>
  3999. <p>GEOS comes with the Text Manager and Photo Manager desk accessories, which are simple databases (called &ldquo;albums&rdquo;) for text and photo scraps, respectively. The following screenshot is Text Manager showing a plain-text preview of one text scrap in its album:</p>
  4000. <p><img src="docs/geowrite/geowrite_8_textmanager.png" height="400" width="640" alt="" /></p>
  4001. <h2 id="text-scraps">Text Scraps</h2>
  4002. <h3 id="format">Format</h3>
  4003. <p>A text scrap is a sequential file on disk with a name of &ldquo;<tt>Text&nbsp;&nbsp;Scrap</tt>&rdquo; and a type of &ldquo;<tt>Text&nbsp;&nbsp;Scrap&nbsp;V2.0</tt>&rdquo;. It supports all geoWrite features (fonts, styles, rulers) except embedded images.</p>
  4004. <p>The first two bytes of the file are the size of the data that follows, so the data can be up to 65535 bytes. geoWrite can paste any size, but cannot create scraps larger than a page.</p>
  4005. <p>The remainder is regular geoWrite page data. It must start with a NewCardSet escape sequence to define the font and style of the text that follows. Here is an example:</p>
  4006. <pre><code>00000000  10 00 17 8c 00 40 48 65  6c 6c 6f 20 57 6f 72 6c  |.....@Hello Worl|  
  4007. 00000010  64 21                                             |d!|
  4008. </code></pre>
  4009. <ul>
  4010. <li><code>10 00</code> ($0010) is the scrap&rsquo;s length, 16 bytes.</li>
  4011. <li><code>17</code> is the <code>ESC_NEWCARDSET</code> escape code.</li>
  4012. <li><code>8c 00</code> ($008C) specifies the California font at 12 pt.</li>
  4013. <li><code>40</code> is the style byte, and means bold face.</li>
  4014. <li>The remainder is the ASCII text &ldquo;Hello World!&rdquo;</li>
  4015. </ul>
  4016. <p>The text can contain the following control codes:</p>
  4017. <table>
  4018. <thead>
  4019. <tr>
  4020. <th> Code </th>
  4021. <th> Description       </th>
  4022. </tr>
  4023. </thead>
  4024. <tbody>
  4025. <tr>
  4026. <td> $09  </td>
  4027. <td> Tab               </td>
  4028. </tr>
  4029. <tr>
  4030. <td> $0C  </td>
  4031. <td> Page Break        </td>
  4032. </tr>
  4033. <tr>
  4034. <td> $0D  </td>
  4035. <td> Line Break        </td>
  4036. </tr>
  4037. <tr>
  4038. <td> $11  </td>
  4039. <td> Ruler Escape      </td>
  4040. </tr>
  4041. <tr>
  4042. <td> $17  </td>
  4043. <td> NewCardSet Escape </td>
  4044. </tr>
  4045. </tbody>
  4046. </table>
  4047. <p>See <a href="https://www.pagetable.com/?p=1471">part 7</a> for more information on the geoWrite escape sequences. For the following sections, the background discussed in that article may be generally useful.</p>
  4048. <h3 id="geowrite-implementation">geoWrite Implementation</h3>
  4049. <p>By convention, the text scrap needs to be a file on disk. But disk is slow, so geoWrite uses a 300 byte cache in memory. When copying and pasting within the app, and if the text is small enough to fit in the cache, the scrap is not written to disk until necessary.</p>
  4050. <h4 id="copy">Copy</h4>
  4051. <p>If the user selects some text and then clicks on &ldquo;copy&rdquo; in the &ldquo;edit&rdquo; menu, the current selection gets copied into a text scrap.</p>
  4052. <p>First, geoWrite looks through the selected range of geoWrite data to see whether it contains an embedded image (<code>ESC_GRAPHICS</code>). If this is the case, it shows an error, since text scraps cannot contain images.</p>
  4053. <p><img src="docs/geowrite/geowrite_8_cantcopyimage.png" height="400" width="640" alt="" /></p>
  4054. <p>Then it trims the selection so that it doesn&rsquo;t contain unnecessary data: If the very end of the selection is a ruler data structure, it gets removed, since all its properties (margins, tab stops, alignment and spacing) only apply to the paragraph following it, which is not part of the selection.</p>
  4055. <p>The same would be true for a NewCardSet (font and style) structure at the end of the selection, but the text selection logic has already stripped it from the end.</p>
  4056. <p>All text scraps have to start with the NewCardSet structure to set font and style. geoWrite does not have the space to work on a copy of the selected text and needs to create the scrap in-place: Therefore it saves the four bytes preceding the text range and overwrites them with the NewCardSet structure. It will restore these bytes after saving the text scrap.</p>
  4057. <p>If the resulting scrap data is small enough, it will be copied into the buffer in memory. Otherwise, it will be saved to disk. If there is already a text scrap on disk, it will be overwritten.</p>
  4058. <h4 id="paste">Paste</h4>
  4059. <p>The &ldquo;paste/text&rdquo; function in the &ldquo;edit&rdquo; menu inserts the text scrap at the cursor position. If there is currently text selected, it gets deleted before inserting the scrap, effectively replacing the selection with the scrap.</p>
  4060. <p>To insert text into a page, the text between the cursor position and the end of the buffer is moved up in memory to make space for the data to be inserted. If there is a text scrap in the memory buffer, geoWrite just copies the scrap data verbatim into the page.</p>
  4061. <h5 id="disk">Disk</h5>
  4062. <p>If the text scrap is on disk, it is more complicated. geoWrite cannot just make space in the buffer and read the scrap into it: It might not fit. The app&rsquo;s <a href="https://www.pagetable.com/?p=1471">memory manager</a> generally keeps about one page in the buffer, and pages data from and to disk to work with bigger amounts of data.</p>
  4063. <p>geoWrite therefore reads and inserts the text scrap block by block, every time moving the page data up by a block. If this would overflow the buffer, the memory manager will do a repagination run to move some data to disk and therefore reduce the size of the data in the buffer.</p>
  4064. <p>But there is now another problem: Inserting the text scrap is no longer an atomic operation. geoWrite must be able to abort the insertion process after any block and still have a consistent document at the end. There are several reasons the insertion might be aborted:</p>
  4065. <ul>
  4066. <li>There is an I/O error when reading the scrap.</li>
  4067. <li>The document exceeds 61 pages.</li>
  4068. <li>The disk is too low on space to continue.</li>
  4069. </ul>
  4070. <p>Ruler (27 bytes) and NewCardSet (4 bytes) escape sequences may cross a block boundary, so if insertion is aborted, it could happen that an incomplete sequence is added to the document, which would leave the document in an illegal state.</p>
  4071. <p>Therefore, geoWrite interprets the scrap data and stops before escape sequences that span two blocks. It then only inserts the data before this incomplete escape sequence. Then, when the next block is loaded, it inserts the whole escape sequence in one go.</p>
  4072. <p>With this strategy, if an insertion has to be aborted, the text up to the error will be cleanly added to the document.</p>
  4073. <h5 id="style">Style</h5>
  4074. <p>All text scraps define the font and style at the beginning, and they can contain a ruler definition. By just concatenating the scrap with the document&rsquo;s existing text after the insertion point, these font/style/ruler changes would continue to apply to existing text after the inserted scrap. Therefore, geoWrites inserts after the scrap a copy of the ruler or cardset that was active before the insertion point, in order to keep the style of the original text the same.</p>
  4075. <p>There is no need for this though:</p>
  4076. <ul>
  4077. <li>if there is already a ruler/NewCardSet escape at this position.</li>
  4078. <li>if the paste happens at the end of the document.</li>
  4079. <li>if the paste happens at the end of a page. The next page always starts with explicit paragraph and font/style escapes.</li>
  4080. </ul>
  4081. <h4 id="lazy-logic">Lazy Logic</h4>
  4082. <p>Copying and pasting text that is less than 300 bytes within geoWrite happens without disk access, but for interoperability with other apps, the buffer in memory will have to be written to disk in two cases:</p>
  4083. <ul>
  4084. <li>If geoWrite runs a desk accessory. Desk accessories are like apps, but they are smaller, launched from a regular app and they return to the app when they quit. Examples would be the calculator, note pad and the text and photo managers. These desk accessories may want to access the text scrap.</li>
  4085. <li>It geoWrite quits. If geoWrite is launched again, or any other app is run, this app may want to read the text scrap.</li>
  4086. </ul>
  4087. <h2 id="photo-scraps">Photo Scraps</h2>
  4088. <h3 id="format">Format</h3>
  4089. <p>A photo scrap is a sequential file on disk with a name of &ldquo;<tt>Photo&nbsp;Scrap</tt>&rdquo; and a type of &ldquo;<tt>Photo&nbsp;Scrap&nbsp;V1.1</tt>&rdquo;. It is generally a rectangular monochrome image.</p>
  4090. <p>The first three bytes of the file are the dimensions of the image. The width is one byte and is measured in units of 8 pixels. The next two bytes are the height in pixels. The theoretical maximum dimensions of a photo scrap are therefore 2040 (255*8) * 65535, with the width divisible by 8.</p>
  4091. <p>The bitmap data uses 8 horizontal pixels per byte (the leftmost pixel is bit 7, 1-bits are black). Consecutive bytes describe a pixel line from left to right. These lines are stored row by row starting from the top.</p>
  4092. <p>This bitmap data is stored compressed using a format based on runlength-encoding (RLE). GEOS KERNAL&rsquo;s &ldquo;BitmapUp&rdquo; can decode this format, so applications can just pass the data to the KERNAL&rsquo;s APIs without having to care about the specific encoding.</p>
  4093. <p>BitmapUp-compressed data is a sequence of packets. Every packet starts with a &ldquo;count&rdquo; byte:</p>
  4094. <table>
  4095. <thead>
  4096. <tr>
  4097. <th> <em>count</em> Value </th>
  4098. <th> Description </th>
  4099. </tr>
  4100. </thead>
  4101. <tbody>
  4102. <tr>
  4103. <td> $00         </td>
  4104. <td> Reserved    </td>
  4105. </tr>
  4106. <tr>
  4107. <td> $01-$7F     </td>
  4108. <td> <strong>Repeat</strong>: Repeat the following byte <em>count</em> times </td>
  4109. </tr>
  4110. <tr>
  4111. <td> $80         </td>
  4112. <td> Reserved    </td>
  4113. </tr>
  4114. <tr>
  4115. <td> $81-$DB     </td>
  4116. <td> <strong>Unique</strong>: Use the next <em>count</em> &#8211; $80 bytes literally </td>
  4117. </tr>
  4118. <tr>
  4119. <td> $DC         </td>
  4120. <td> Reserved    </td>
  4121. </tr>
  4122. <tr>
  4123. <td> $DD-$FF     </td>
  4124. <td> <strong>Bigcount</strong>: Followed by a <em>bigcount</em> byte. Repeat the following <em>count</em> &#8211; $DC bytes <em>bigcount</em> times, interpreting the resulting bytes using the <strong>Repeat</strong> and <strong>Unique</strong> rules. </td>
  4125. </tr>
  4126. </tbody>
  4127. </table>
  4128. <p>As an example, let&rsquo;s look at a 16&#215;16 rectangle:</p>
  4129. <pre><code>****************  
  4130. *              *  
  4131. *              *  
  4132. *              *  
  4133. *              *  
  4134. *              *  
  4135. *              *  
  4136. *              *  
  4137. *              *  
  4138. *              *  
  4139. *              *  
  4140. *              *  
  4141. *              *  
  4142. *              *  
  4143. *              *  
  4144. ****************
  4145. </code></pre>
  4146. <p>It can be compressed into 9 bytes:</p>
  4147. <pre><code>.byte   2,%11111111  
  4148. .byte   $DC+3,14  
  4149.    .byte   $80+2,%10000000,%00000001  
  4150. .byte   2,%11111111
  4151. </code></pre>
  4152. <ul>
  4153. <li>The first line instructs the decoder to repeat the bit pattern %11111111 <strong>2</strong> times, producing 16 black pixels – the top line of the rectangle.</li>
  4154. <li>The second line will cause the next <strong>3</strong> bytes to be repeated <strong>14</strong> times, once for every line of the rectangle except the first and the last one.</li>
  4155. <li>These next three bytes are again encoded and tell the decoder to take the next <strong>2</strong> bytes verbatim: %10000000 and %0000001 describe one line of the rectangle with the leftmost and the rightmost pixel set.</li>
  4156. <li>The last line is the same as the first one; it creates the bottom row.</li>
  4157. </ul>
  4158. <p>After the monochrome bitmap data, an image scrap can optionally contain data on how to colorize 8&#215;8 pixel squares. geoWrite does not support color and just ignores this part.</p>
  4159. <h3 id="geowrite-implementation">geoWrite Implementation</h3>
  4160. <p>geoWrite can paste photo scraps with heights up to 144 pixels. Images wider than the page&rsquo;s dimensions will effectively be cropped when rendering.</p>
  4161. <p>In geoWrite documents, image data is not stored inline with the text of the document. Instead, the text contains a 5 byte graphics escape sequence pointing to the image:</p>
  4162. <table>
  4163. <thead>
  4164. <tr>
  4165. <th>Offset</th>
  4166. <th> Type </th>
  4167. <th> Contents          </th>
  4168. <th> Description                            </th>
  4169. </tr>
  4170. </thead>
  4171. <tbody>
  4172. <tr>
  4173. <td> 0    </td>
  4174. <td> Byte </td>
  4175. <td> Escape Code       </td>
  4176. <td> Constant $10 (<code>ESC_GRAPHICS</code>)          </td>
  4177. </tr>
  4178. <tr>
  4179. <td> 1    </td>
  4180. <td> Byte </td>
  4181. <td> Image Width       </td>
  4182. <td> Width of image divided by 8            </td>
  4183. </tr>
  4184. <tr>
  4185. <td> 2-3  </td>
  4186. <td> Word </td>
  4187. <td> Image Height      </td>
  4188. <td> Height of image                        </td>
  4189. </tr>
  4190. <tr>
  4191. <td> 4    </td>
  4192. <td> Byte </td>
  4193. <td> Record Number     </td>
  4194. <td> Number of record containing image data </td>
  4195. </tr>
  4196. </tbody>
  4197. </table>
  4198. <p>The data of each image is stored in a separate <a href="https://www.pagetable.com/?p=1425">VLIR</a> record. The format of image records is that of photo scraps, so when pasting an image, all geoWrite has to do is make a copy of the photo scrap file into a new VLIR record in the document.</p>
  4199. <p>Before this can be done, a few checks have to be made though:</p>
  4200. <ul>
  4201. <li>A photo scrap file must exist.</li>
  4202. <li>The file version must not be too new. (There is a bug in geoWrite 2.1: It checks for a maximum version of V2.1, but the highest version of the photo scrap file format at the time geoWrite was written was V1.1 – and still is today. The version checking code was meant for geoWrite documents and text scraps, whose versioning follows the geoWrite version. It should have been special cased for photo scraps.)</li>
  4203. <li>The height must not be more than 144 pixels.</li>
  4204. <li>There must be space in the document. A geoWrite document can hold up to 64 images.</li>
  4205. </ul>
  4206. <h2 id="nomenclature">Nomenclature</h2>
  4207. <p>One last thought about GEOS and naming things. What I called &ldquo;image&rdquo; throughout this article, GEOS calls:</p>
  4208. <ul>
  4209. <li><strong>graphics</strong> in the KERNAL API.</li>
  4210. <li><strong>picture</strong> in the geoWrite UI.</li>
  4211. <li><strong>photo</strong> in all references to photo scraps.</li>
  4212. </ul>
  4213. <h2 id="references">References</h2>
  4214. <ul>
  4215. <li>Michael Farr: <a href="https://archive.org/details/The_Official_GEOS_Programmers_Reference_Guide">The Official GEOS Programmer&rsquo;s Reference Guide</a></li>
  4216. <li>Berkeley Softworks: <a href="https://archive.org/details/The_Hitchhikers_Guide_to_GEOS">The Hitchhiker’s Guide to GEOS</a></li>
  4217. </ul>
  4218. ]]></content:encoded>
  4219. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1481</wfw:commentRss>
  4220. <slash:comments>2</slash:comments>
  4221. </item>
  4222. <item>
  4223. <title>Inside geoWrite – 7: File Format and Pagination</title>
  4224. <link>https://www.pagetable.com/?p=1471</link>
  4225. <comments>https://www.pagetable.com/?p=1471#respond</comments>
  4226. <pubDate>Wed, 16 Sep 2020 17:34:59 +0000</pubDate>
  4227. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  4228. <category><![CDATA[6502]]></category>
  4229. <category><![CDATA[Archeology]]></category>
  4230. <category><![CDATA[C64]]></category>
  4231. <category><![CDATA[Commodore]]></category>
  4232. <category><![CDATA[GEOS]]></category>
  4233. <category><![CDATA[Operating Systems]]></category>
  4234.  
  4235. <guid isPermaLink="false">https://www.pagetable.com/?p=1471</guid>
  4236. <description><![CDATA[In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses how its file format allows the app to efficiently edit documents hundreds of KB in size. Article Series The Overlay System Screen Recovery Font Management Zero Page Copy Protection Localization File Format and Pagination ← this article ... <a title="Inside geoWrite – 7: File Format and Pagination" class="read-more" href="https://www.pagetable.com/?p=1471">Read more<span class="screen-reader-text">Inside geoWrite – 7: File Format and Pagination</span></a>]]></description>
  4237. <content:encoded><![CDATA[<p>In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses how its file format allows the app to efficiently edit documents hundreds of KB in size.</p>
  4238. <p><img src="docs/geowrite/geowrite_7_pagebreak.png" height="400" width="640" alt="" /></p>
  4239. <h2 id="article-series">Article Series</h2>
  4240. <ol>
  4241. <li><a href="https://www.pagetable.com/?p=1425">The Overlay System</a></li>
  4242. <li><a href="https://www.pagetable.com/?p=1428">Screen Recovery</a></li>
  4243. <li><a href="https://www.pagetable.com/?p=1436">Font Management</a></li>
  4244. <li><a href="https://www.pagetable.com/?p=1442">Zero Page</a></li>
  4245. <li><a href="https://www.pagetable.com/?p=1449">Copy Protection</a></li>
  4246. <li><a href="https://www.pagetable.com/?p=1460">Localization</a></li>
  4247. <li><strong>File Format and Pagination</strong> ← this article</li>
  4248. <li><a href="https://www.pagetable.com/?p=1481">Copy &amp; Paste</a></li>
  4249. <li><a href="https://www.pagetable.com/?p=1490">Keyboard Handling</a></li>
  4250. </ol>
  4251. <h2 id="design">Design</h2>
  4252. <p>Writing a word processor, especially a WYSIWYG one, poses two core challenges:</p>
  4253. <ol>
  4254. <li><strong>Memory Management</strong>: The whole document may not fit into memory. A small text file is easily dozens of KB, and a large one hundreds of KB, not counting inline images. The word processor needs a strategy to only keep the parts in memory that are currently needed.</li>
  4255. <li><strong>Pagination</strong>: WYSIWYG mans that the user will always see on which page, and where on that page the text that is currently being edited will appear when printing. This requires the word processor to layout the whole text up to the current position.</li>
  4256. </ol>
  4257. <h3 id="memory-management">Memory Management</h3>
  4258. <p>The natural approach to memory management is to only read a small part of the document into memory for viewing and editing. Once the user jumps to a different part of the document or writes enough text to fill the buffer, the current part has to be written to disk, and a different part has to be loaded into memory.</p>
  4259. <p>This is tricky with regular (&ldquo;sequential&rdquo;) files: When writing a part of the document back to disk, the new version is generally a different size than the part that it replaces. This means that the remainder of the document has to be moved. In the worst case, this requires making a complete copy of the document and temporarily taking up twice the space on disk.</p>
  4260. <p>GEOS has the concept of a VLIR file, which is a collection of up to 127 &ldquo;sub-files&rdquo;, called records, numbered 0 to 126. The geoWrite application itself consists of several such records for its own code, which are <a href="https://www.pagetable.com/?p=1425">paged in</a> based on which functionality is being used. Similarly, geoWrite documents are VLIR files containing multiple records of no more than 7 KB – this is how much memory is left on a 64 KB RAM system after accounting for the operating system and the geoWrite application.</p>
  4261. <p>In the generic case, the word processor can then just read a single record from disk into memory, have the user edit it, and write the record back to disk. All other records remain unchanged.</p>
  4262. <p>A simple approach for dividing up the document would be to just cut it into 7 KB parts. If text is added to the middle of the document, and the record overflows 7 KB, it will have to be divided into two, and all subsequent records have to be moved up. If two consecutive parts are less than 7 KB together, they can be combined, and subsequent records have to be moved down. Moving records really just means renaming them and is therefore cheap.</p>
  4263. <p>The problem with dividing up the document at fixed limits is that the point where text continues from one record to the next may be anywhere. Therefore, a single sentence on the screen might come from two different records, and moving the mouse across this invisible line will cause slow (and surprising!) disk access. It&rsquo;s even worse when performing an operation on selected text that spans two records, which may cause swapping in and out of parts multiple times.</p>
  4264. <h3 id="pagination">Pagination</h3>
  4265. <p>The other challenge is pagination. There is no information in the document on how to map a page number to a record, so if the user wants to jump to a specific page, the word processor would have to actively find out what part of the document will end up printed on that page. If the desired page is after the current one, all text from the current position on has to be paginated, i.e. put through the page layout logic until the point in the document is found that will be printed on the specified page. If the desired page is before the current one, the same logic would have to be done starting from the first page of the document.</p>
  4266. <p>To avoid redundant &ldquo;re-pagination&rdquo;, the calculated pagination information could be stored as metadata in the file. For every page, this would be the combination of record number and offset within the record to point to the first character of the page. If text is edited anywhere but at the end of the document, the remainder of the document has to be re-paginated, and the table has to be updated – this can be done lazily. Jumping within the document now only requires a table lookup.</p>
  4267. <h3 id="geowrite-strategy">geoWrite Strategy</h3>
  4268. <p>geoWrite uses a combined strategy for memory management and pagination: It maps every page to exactly one record. The app reserves 7000 bytes of RAM for the currently edited page, which corresponds to just about one page fully filled with 9 pt text. Jumping to a different page is as simple as reading a different record – without requiring a separate page-to-record mapping table. And it also solves the other problem from before: Since a whole page is guaranteed to be in RAM, editing text within a page generally does not cause disk access.</p>
  4269. <p>Picking pages as the unit of editing does sound weird at first, because the separation into pages is such a transient property of a text document. After all, the very idea of a word processor (as opposed to a typewriter) is that the user can regard the document as just linear text without worrying about page breaks. When editing text, page boundaries change, and the whole document would have to be changed. This is true, but these re-layouts of the document are necessary when editing, no matter what strategy is used to cut the document into pieces.</p>
  4270. <p>Here is an overview of the properties of the two strategies, with the more desirable ones marked in bold:</p>
  4271. <table>
  4272. <thead>
  4273. <tr>
  4274. <th>                           </th>
  4275. <th> Max Size Records + Metadata </th>
  4276. <th> One Record per Page       </th>
  4277. </tr>
  4278. </thead>
  4279. <tbody>
  4280. <tr>
  4281. <td> Jump to page              </td>
  4282. <td> <strong>lookup, read record</strong>     </td>
  4283. <td> <strong>read record</strong>           </td>
  4284. </tr>
  4285. <tr>
  4286. <td> Add text in the middle    </td>
  4287. <td> <strong>re-pagination</strong>           </td>
  4288. <td> re-pagination &amp; data copy </td>
  4289. </tr>
  4290. <tr>
  4291. <td> Surprising disk access    </td>
  4292. <td> yes                         </td>
  4293. <td> <strong>no</strong>                    </td>
  4294. </tr>
  4295. </tbody>
  4296. </table>
  4297. <ul>
  4298. <li>Both strategies allow navigating the document efficiently.</li>
  4299. <li>Adding text to the middle of the document always requires re-pagination of the following pages at some point. With the &ldquo;one record per page&rdquo; strategy, this also requires going through all following records and re-combining them according to the new page breaks.</li>
  4300. <li>Generally, no edit operation within a single page will cause disk access with the &ldquo;one record per page&rdquo; strategy.</li>
  4301. </ul>
  4302. <p>So it&rsquo;s basically a tradeoff between repagination speed and editing performance, and geoWrite went for the latter.</p>
  4303. <h2 id="file-format">File Format</h2>
  4304. <p>Before we discuss when and how exactly a document is re-paginated with geoWrite, we have to dive into the exact file format.</p>
  4305. <p>geoWrite files are VLIR files. GEOS specifies a VLIR file as consisting of a 256 byte file header and up to 127 records of arbitrary lengths.</p>
  4306. <p>The file header of any GEOS document contains the file&rsquo;s icon, type and creator, a comment, and optionally, type-specific metadata.</p>
  4307. <p>geoWrite stores 9 bytes of metadata at offset $89 for document-global properties:</p>
  4308. <table>
  4309. <thead>
  4310. <tr>
  4311. <th> Offset  </th>
  4312. <th> Size </th>
  4313. <th> Contents          </th>
  4314. <th> Description                        </th>
  4315. </tr>
  4316. </thead>
  4317. <tbody>
  4318. <tr>
  4319. <td> $89-$8A </td>
  4320. <td> Word </td>
  4321. <td> Start Page Number </td>
  4322. <td> Number of first page, usually 1    </td>
  4323. </tr>
  4324. <tr>
  4325. <td> $8B     </td>
  4326. <td> Byte </td>
  4327. <td> Title/NLQ Flags   </td>
  4328. <td> $80: has title page, $40: NLQ mode </td>
  4329. </tr>
  4330. <tr>
  4331. <td> $8C-$8D </td>
  4332. <td> Word </td>
  4333. <td> Header Height     </td>
  4334. <td> Height of header in dots           </td>
  4335. </tr>
  4336. <tr>
  4337. <td> $8E-$8F </td>
  4338. <td> Word </td>
  4339. <td> Footer Height     </td>
  4340. <td> Height of footer in dots           </td>
  4341. </tr>
  4342. <tr>
  4343. <td> $90-$91 </td>
  4344. <td> Word </td>
  4345. <td> Page Height       </td>
  4346. <td> Page height in dots                </td>
  4347. </tr>
  4348. </tbody>
  4349. </table>
  4350. <ul>
  4351. <li>The start page number can be set to numbers other than 1, to allow splitting a project into multiple document files with consistent page numbering.</li>
  4352. <li>If the document has a title page, no header or footer will be printed onto the first page.</li>
  4353. <li>In NLQ mode, the document will be printed by sending ASCII characters to the printer, using the printer&rsquo;s built-in fonts. This changes the metrics calculation.</li>
  4354. <li>Header and footer height are calculated from the header/footer text. These are cached values to allow page layouts without having to measure the height of the header and the footer.</li>
  4355. <li>The page height is generally a property of the printer. The field in the file header specifies what page height was used for paginating the document. If the page height of the current printer is different, the document has to be re-paginated.</li>
  4356. <li>All sizes are specified in dots, which are 1/80 of an inch on paper, and the same as GEOS screen pixels. geoWrite documents are either 480 (6 inches, &ldquo;regular&rdquo;) or 640 (8.2 inches, &ldquo;wide&rdquo;) dots wide. The default height (i.e. if no printer is installed) is 752 dots (9.4 inches).</li>
  4357. </ul>
  4358. <p>These are the contents of the VLIR records of a geoWrite document:</p>
  4359. <table>
  4360. <thead>
  4361. <tr>
  4362. <th> Records </th>
  4363. <th> Contents </th>
  4364. </tr>
  4365. </thead>
  4366. <tbody>
  4367. <tr>
  4368. <td> 0-60    </td>
  4369. <td> Pages    </td>
  4370. </tr>
  4371. <tr>
  4372. <td> 61      </td>
  4373. <td> Header   </td>
  4374. </tr>
  4375. <tr>
  4376. <td> 62      </td>
  4377. <td> Footer   </td>
  4378. </tr>
  4379. <tr>
  4380. <td> 63      </td>
  4381. <td> Reserved </td>
  4382. </tr>
  4383. <tr>
  4384. <td> 64-126  </td>
  4385. <td> Images   </td>
  4386. </tr>
  4387. </tbody>
  4388. </table>
  4389. <ul>
  4390. <li>A document can have up to 61 pages, which are stored in records 0 through 60. Internally, page numbering is zero-based. For the UI, the start page number from the header is added.</li>
  4391. <li>The text for the header and footer are stored in two separate records. They have the same format as pages.</li>
  4392. <li>geoWrite supports up to 63 inline images, each of which is stored in its own record, which is pointed to by the page that contains the image.</li>
  4393. </ul>
  4394. <p>In a properly closed geoWrite document, all page records are consecutive with no empty records in between, all image records are referenced by pages, pagination is consistent with the page height in the header, and the header and footer height values in the header correspond to the text in the header and footer records.</p>
  4395. <h3 id="text-format">Text Format</h3>
  4396. <p>The text is stored in ASCII format, that is, codes $20 through $7F are printable characters, and codes $00 through $1F are control codes. Of these, only the following are defined:</p>
  4397. <table>
  4398. <thead>
  4399. <tr>
  4400. <th> Code </th>
  4401. <th> Description       </th>
  4402. </tr>
  4403. </thead>
  4404. <tbody>
  4405. <tr>
  4406. <td> $00  </td>
  4407. <td> No-Op             </td>
  4408. </tr>
  4409. <tr>
  4410. <td> $09  </td>
  4411. <td> Tab               </td>
  4412. </tr>
  4413. <tr>
  4414. <td> $0C  </td>
  4415. <td> Page Break        </td>
  4416. </tr>
  4417. <tr>
  4418. <td> $0D  </td>
  4419. <td> Line Break        </td>
  4420. </tr>
  4421. <tr>
  4422. <td> $10  </td>
  4423. <td> Graphics Escape   </td>
  4424. </tr>
  4425. <tr>
  4426. <td> $11  </td>
  4427. <td> Ruler Escape      </td>
  4428. </tr>
  4429. <tr>
  4430. <td> $17  </td>
  4431. <td> NewCardSet Escape </td>
  4432. </tr>
  4433. </tbody>
  4434. </table>
  4435. <p>The $00 character code specifies the end of the file. The graphics, ruler and NewCardSet escape codes indicate data structures that need a detailed description.</p>
  4436. <h4 id="newcardset-escape">NewCardSet Escape</h4>
  4437. <p>The NewCardSet structure encodes a change in font and style. It can appear anywhere in the document.</p>
  4438. <table>
  4439. <thead>
  4440. <tr>
  4441. <th>Offset</th>
  4442. <th> Size </th>
  4443. <th> Contents          </th>
  4444. <th> Description                            </th>
  4445. </tr>
  4446. </thead>
  4447. <tbody>
  4448. <tr>
  4449. <td> 0    </td>
  4450. <td> Byte </td>
  4451. <td> Escape Code       </td>
  4452. <td> Constant $17 (<code>ESC_NEWCARDSET</code>)        </td>
  4453. </tr>
  4454. <tr>
  4455. <td> 1-2  </td>
  4456. <td> Word </td>
  4457. <td> Font ID           </td>
  4458. <td> Encoded font and point size identifier </td>
  4459. </tr>
  4460. <tr>
  4461. <td> 3    </td>
  4462. <td> Byte </td>
  4463. <td> Style             </td>
  4464. <td> Text style bitfield                    </td>
  4465. </tr>
  4466. </tbody>
  4467. </table>
  4468. <ul>
  4469. <li><a href="https://www.pagetable.com/?p=1436">GEOS Font IDs</a> are 16 bit values that encode the unique font identifier (0: system font, 1: University, 2: California, 3: Roma, &hellip;) in bits 6-15, and the point size in bits 0-5.</li>
  4470. <li>The style bitfield is defined as follows:</li>
  4471. </ul>
  4472. <table>
  4473. <thead>
  4474. <tr>
  4475. <th>Bit</th>
  4476. <th> Description </th>
  4477. </tr>
  4478. </thead>
  4479. <tbody>
  4480. <tr>
  4481. <td> 7 </td>
  4482. <td> Underline   </td>
  4483. </tr>
  4484. <tr>
  4485. <td> 6 </td>
  4486. <td> Bold        </td>
  4487. </tr>
  4488. <tr>
  4489. <td> 5 </td>
  4490. <td> Reverse     </td>
  4491. </tr>
  4492. <tr>
  4493. <td> 4 </td>
  4494. <td> Italics     </td>
  4495. </tr>
  4496. <tr>
  4497. <td> 3 </td>
  4498. <td> Outline     </td>
  4499. </tr>
  4500. <tr>
  4501. <td> 2 </td>
  4502. <td> Superscript </td>
  4503. </tr>
  4504. <tr>
  4505. <td> 1 </td>
  4506. <td> Subscript   </td>
  4507. </tr>
  4508. <tr>
  4509. <td> 0 </td>
  4510. <td> <em>Reserved</em>  </td>
  4511. </tr>
  4512. </tbody>
  4513. </table>
  4514. <ul>
  4515. <li>All bits can be combined, except subscript with superscript.</li>
  4516. <li>All zero bits indicate plain text.</li>
  4517. </ul>
  4518. <h4 id="ruler-escape">Ruler Escape</h4>
  4519. <p>The ruler structure encodes a paragraph&rsquo;s properties. It can appear only at the beginning of a new paragraph.</p>
  4520. <table>
  4521. <thead>
  4522. <tr>
  4523. <th>Offset </th>
  4524. <th> Type </th>
  4525. <th> Contents          </th>
  4526. <th> Description                            </th>
  4527. </tr>
  4528. </thead>
  4529. <tbody>
  4530. <tr>
  4531. <td> 0     </td>
  4532. <td> Byte </td>
  4533. <td> Escape Code       </td>
  4534. <td> Constant $11 (<code>ESC_RULER</code>)             </td>
  4535. </tr>
  4536. <tr>
  4537. <td> 1-2   </td>
  4538. <td> Word </td>
  4539. <td> Left Margin       </td>
  4540. <td> Left margin                            </td>
  4541. </tr>
  4542. <tr>
  4543. <td> 3-4   </td>
  4544. <td> Word </td>
  4545. <td> Right Margin      </td>
  4546. <td> Right margin                           </td>
  4547. </tr>
  4548. <tr>
  4549. <td> 5-6   </td>
  4550. <td> Word </td>
  4551. <td> Tab Stop 0        </td>
  4552. <td> Position/type of tab stop 0            </td>
  4553. </tr>
  4554. <tr>
  4555. <td> 7-8   </td>
  4556. <td> Word </td>
  4557. <td> Tab Stop 1        </td>
  4558. <td> Position/type of tab stop 1            </td>
  4559. </tr>
  4560. <tr>
  4561. <td> 9-10  </td>
  4562. <td> Word </td>
  4563. <td> Tab Stop 2        </td>
  4564. <td> Position/type of tab stop 2            </td>
  4565. </tr>
  4566. <tr>
  4567. <td> 11-12 </td>
  4568. <td> Word </td>
  4569. <td> Tab Stop 3        </td>
  4570. <td> Position/type of tab stop 3            </td>
  4571. </tr>
  4572. <tr>
  4573. <td> 13-14 </td>
  4574. <td> Word </td>
  4575. <td> Tab Stop 4        </td>
  4576. <td> Position/type of tab stop 4            </td>
  4577. </tr>
  4578. <tr>
  4579. <td> 15-16 </td>
  4580. <td> Word </td>
  4581. <td> Tab Stop 5        </td>
  4582. <td> Position/type of tab stop 5            </td>
  4583. </tr>
  4584. <tr>
  4585. <td> 17-18 </td>
  4586. <td> Word </td>
  4587. <td> Tab Stop 6        </td>
  4588. <td> Position/type of tab stop 6            </td>
  4589. </tr>
  4590. <tr>
  4591. <td> 19-20 </td>
  4592. <td> Word </td>
  4593. <td> Tab Stop 7        </td>
  4594. <td> Position/type of tab stop 7            </td>
  4595. </tr>
  4596. <tr>
  4597. <td> 21-22 </td>
  4598. <td> Word </td>
  4599. <td> Paragraph Margin  </td>
  4600. <td> Left margin of first line of paragraph </td>
  4601. </tr>
  4602. <tr>
  4603. <td> 23    </td>
  4604. <td> Byte </td>
  4605. <td> Spacing/Alignment </td>
  4606. <td> Line spacing and text alignment        </td>
  4607. </tr>
  4608. <tr>
  4609. <td> 24    </td>
  4610. <td> Byte </td>
  4611. <td> Reserved          </td>
  4612. <td> Reserved for text color                </td>
  4613. </tr>
  4614. <tr>
  4615. <td> 25    </td>
  4616. <td> Byte </td>
  4617. <td> Reserved          </td>
  4618. <td> Reserved                               </td>
  4619. </tr>
  4620. <tr>
  4621. <td> 26    </td>
  4622. <td> Byte </td>
  4623. <td> Reserved          </td>
  4624. <td> Reserved                               </td>
  4625. </tr>
  4626. </tbody>
  4627. </table>
  4628. <ul>
  4629. <li>All sizes are in dots.</li>
  4630. <li>The left margin is less than the right margin, and the tab stops are in ascending order.</li>
  4631. <li>The most significant bit of each tab stop indicates whether it is a regular or a decimal tab stop. Decimal tab stops align the <a href="https://www.pagetable.com/?p=1460">decimal separator</a> to the tab stop.</li>
  4632. </ul>
  4633. <table>
  4634. <thead>
  4635. <tr>
  4636. <th>Bit 15</th>
  4637. <th> Description                            </th>
  4638. </tr>
  4639. </thead>
  4640. <tbody>
  4641. <tr>
  4642. <td> 0    </td>
  4643. <td> Regular tab stop                       </td>
  4644. </tr>
  4645. <tr>
  4646. <td> 1    </td>
  4647. <td> Decimal tab stop                       </td>
  4648. </tr>
  4649. </tbody>
  4650. </table>
  4651. <ul>
  4652. <li>Line spacing and alignment are encoded into a single byte:</li>
  4653. </ul>
  4654. <table>
  4655. <thead>
  4656. <tr>
  4657. <th>Bits 0-1</th>
  4658. <th> Description                            </th>
  4659. </tr>
  4660. </thead>
  4661. <tbody>
  4662. <tr>
  4663. <td> 0      </td>
  4664. <td> Left aligned                           </td>
  4665. </tr>
  4666. <tr>
  4667. <td> 1      </td>
  4668. <td> Centered                               </td>
  4669. </tr>
  4670. <tr>
  4671. <td> 2      </td>
  4672. <td> Right aligned                          </td>
  4673. </tr>
  4674. <tr>
  4675. <td> 3      </td>
  4676. <td> Justified                              </td>
  4677. </tr>
  4678. </tbody>
  4679. </table>
  4680. <table>
  4681. <thead>
  4682. <tr>
  4683. <th>Bits 2-3</th>
  4684. <th> Description                            </th>
  4685. </tr>
  4686. </thead>
  4687. <tbody>
  4688. <tr>
  4689. <td> 0      </td>
  4690. <td> 1.0 line spacing                       </td>
  4691. </tr>
  4692. <tr>
  4693. <td> 1      </td>
  4694. <td> 1.5 line spacing                       </td>
  4695. </tr>
  4696. <tr>
  4697. <td> 2      </td>
  4698. <td> 2.0 line spacing                       </td>
  4699. </tr>
  4700. </tbody>
  4701. </table>
  4702. <h4 id="graphics-escape">Graphics Escape</h4>
  4703. <p>The graphics escape is used to embed an image into the text. It can appear anywhere in the document, and is regarded as a paragraph of its own.</p>
  4704. <table>
  4705. <thead>
  4706. <tr>
  4707. <th>Offset</th>
  4708. <th> Type </th>
  4709. <th> Contents          </th>
  4710. <th> Description                            </th>
  4711. </tr>
  4712. </thead>
  4713. <tbody>
  4714. <tr>
  4715. <td> 0    </td>
  4716. <td> Byte </td>
  4717. <td> Escape Code       </td>
  4718. <td> Constant $10 (<code>ESC_GRAPHICS</code>)          </td>
  4719. </tr>
  4720. <tr>
  4721. <td> 1    </td>
  4722. <td> Byte </td>
  4723. <td> Image Width       </td>
  4724. <td> Width of image divided by 8            </td>
  4725. </tr>
  4726. <tr>
  4727. <td> 2-3  </td>
  4728. <td> Word </td>
  4729. <td> Image Height      </td>
  4730. <td> Height of image                        </td>
  4731. </tr>
  4732. <tr>
  4733. <td> 4    </td>
  4734. <td> Byte </td>
  4735. <td> Record Number     </td>
  4736. <td> Number of record containing image data </td>
  4737. </tr>
  4738. </tbody>
  4739. </table>
  4740. <ul>
  4741. <li>All sizes are in dots.</li>
  4742. <li>The width of the image has to be divisible by 8.</li>
  4743. <li>The record number of the image data is in the range of 64 through 126.</li>
  4744. </ul>
  4745. <h3 id="page-format">Page Format</h3>
  4746. <p>To divide the linear text into pages, it is not enough to just cut the file at the (hard or soft) page breaks. When navigating to a page, it would not be clear what the current font and paragraph style of the first character of the page should be. Therefore, every page starts with a header containing this information, repeating the font/style/ruler state from the end of the previous page:</p>
  4747. <table>
  4748. <thead>
  4749. <tr>
  4750. <th>Offset</th>
  4751. <th> Length   </th>
  4752. <th> Contents          </th>
  4753. <th> Description          </th>
  4754. </tr>
  4755. </thead>
  4756. <tbody>
  4757. <tr>
  4758. <td> 0-26 </td>
  4759. <td> 27 Bytes </td>
  4760. <td> Ruler Data        </td>
  4761. <td> Ruler data           </td>
  4762. </tr>
  4763. <tr>
  4764. <td> 27-30</td>
  4765. <td> 4  Bytes </td>
  4766. <td> NewCardSet Data   </td>
  4767. <td> Font/style data      </td>
  4768. </tr>
  4769. <tr>
  4770. <td> 31   </td>
  4771. <td>          </td>
  4772. <td> ASCII Text        </td>
  4773. <td> Text of the document </td>
  4774. </tr>
  4775. </tbody>
  4776. </table>
  4777. <p>The ruler data and NewCardSet data include their respective escape codes (<code>ESC_RULER</code> = $11, <code>ESC_NEWCARDSET</code> = $17), which makes any page by itself legally formatted geoWrite text.</p>
  4778. <h2 id="memory-representation">Memory Representation</h2>
  4779. <p>The strategy of the editor is to basically keep a single page in RAM and editing there. This way, for most editing work, there is no need to access the disk.</p>
  4780. <p>The buffer in RAM is 7000 bytes in size and in the same format as a page on disk: The first bytes are the header (ruler, NewCardSet), and the remainder is the actual text data, which may include NewCardSet, ruler and graphics escapes.</p>
  4781. <p>When the user jumps to a page, the corresponding record is loaded into the buffer. And when a new page is added to the document, an empty page is created in the buffer.</p>
  4782. <p>But the buffer isn&rsquo;t always exactly one page. The text in the buffer starts at a known page boundary in the document, and the start of the buffer is associated with a page number in the document on disk.</p>
  4783. <p>But the amount of text in the buffer may be more than fits on the current page: If the user enters some text in the middle of a page, it will be inserted at the corresponding place in the buffer. The text at the end of the buffer may technically belong to the next page, because when laying out the current page, it wouldn&rsquo;t fit.</p>
  4784. <p>The buffer may also be less than the current page of the document: If the user deletes text from the middle of a page, then the data in the buffer may not fill the current page any more – what should show up at the very bottom of this page is actually stored in the following record.</p>
  4785. <h2 id="streaming">Streaming</h2>
  4786. <p>It is not a problem to have more text than fits the page in the buffer (as long as the data doesn&rsquo;t overflow the available 7000 bytes – we&rsquo;ll talk about that later). But if there is less than a page in the buffer, and the bottom of the page needs to be rendered onto the screen, the missing text needs to be loaded from the next record.</p>
  4787. <p>The whole next record is unlikely to fit into the remainder of the current buffer, so the memory management logic loads data from the following records at a block (256 byte) granularity.</p>
  4788. <p>Let&rsquo;s look at the code that does this. When rendering the page for the screen or for printing, and during re-pagination, all code goes through the <code>getByteFromBuffer</code> function:</p>
  4789. <pre><code>getByteFromBuffer:  
  4790.        CmpW    pageEndPtr, r15         ; end reached?  
  4791.        bcc     @end                    ; yes  
  4792.        ldy     #0  
  4793.        lda     (r15),y                 ; read byte  
  4794.        rts
  4795. </code></pre>
  4796. <p>The virtual register <code>r15</code> points to the next byte, and <code>pageEndPtr</code> points to the end of the data in the buffer. The interesting case here is reaching the end:</p>
  4797. <pre><code>@end:   bit     streamingMode  
  4798.        bpl     @skip
  4799.  
  4800.        [push r0 though r15]  
  4801.        jsr     streamBlock  
  4802.        [pop r0 though r15]
  4803.  
  4804.        bra     getByteFromBuffer       ; try again
  4805.  
  4806. @skip:  lda     #0  
  4807.        rts
  4808. </code></pre>
  4809. <p>If <code>streamingMode</code> is false, the function just returns NULL bytes, indicating the end of the buffer. But in &ldquo;streaming mode&rdquo;, it calls <code>streamBlock</code> (not shown). On its first invocation, this function manually looks up the next record in the filesystem and loads the first block, appending it to the data in the buffer, basically extending the buffer by a single block from the next record. The <code>getByteFromBuffer</code> code now has more data that it can fetch.</p>
  4810. <p>On subsequent invocations, <code>streamBlock</code> will keep reading blocks from the record, and will also skip to the following records. With <code>streamingMode</code> enabled, <code>getByteFromBuffer</code> will effectively read bytes from the whole document linearly.</p>
  4811. <p>The ruler and NewCardSet escapes at the beginning of each record are redundant and not needed when concatenating the pages, so <code>streamBlock</code> skips them. All of this is completely transparent to the caller.</p>
  4812. <p>Let&rsquo;s look at an example in practice: The document has two pages of text. The user is at the very top of the first page and deletes a few lines. Visually, a few lines from the second page should now show up at the bottom of the first page. But the editor does not care at this point, the buffer only contains the reduced data. And since the cursor is still at the top of the page (and vertically, only about one fifth of a page fits onto the screen), the text renderer for the screen won&rsquo;t reach the end of the buffer when reading bytes. But once the user moves the cursor down to the end of the page, the text renderer&rsquo;s calls to <code>getByteFromBuffer</code> will cause one or more blocks of the next record to be loaded into the buffer before the part can be shown that was previously on page 2.</p>
  4813. <p>Reading in blocks from subsequent pages is not just some temporary look-ahead: Even though the read-in blocks still exist on disk as part of the next record, geoWrite now regards the data as part of the buffer in memory and will disregard them when accessing the next record in the future.</p>
  4814. <h2 id="repagination">Repagination</h2>
  4815. <p>When adding text to or deleting text from the middle of a document, the document generally needs re-pagination at some point, that is, the document will be updated so that every record on disk contains exactly the text of the corresponding page. geoWrite does this lazily: As seen before, most editing within a page happens on the buffer in memory. The buffer usually only gets written back to disk when moving away from the current page, at which point the remainder if the document needs to be repaginated.</p>
  4816. <p>The same is true if the buffer overflows the available 7000 bytes: The document has to be repaginated from the current page on. Every record will only be filled with text for exactly one page, so when the record for the current page will be loaded again afterwards, it should be significantly below 7000 bytes.</p>
  4817. <h3 id="triggers">Triggers</h3>
  4818. <p>There are many other actions that trigger a repagination run, like:</p>
  4819. <ul>
  4820. <li>The page height changes because of switching printers or toggling NLQ printing mode.</li>
  4821. <li>The page width is changed from 480 to 640 dots. (geoWrite does not allow switching back.)</li>
  4822. <li>The &ldquo;title page&rdquo; setting is toggled. Since this toggles showing the header and footer on the first page, the height of this page that is usable for text changes.</li>
  4823. <li>The header or footer is edited, potentially changing their heights and changing the usable height of pages.</li>
  4824. <li>The search/replace function changes text on arbitrary pages.</li>
  4825. <li>The function <em>update</em> in the <em>file</em> menu explicitly updates the document on disk into a consistent state, which includes repagination.</li>
  4826. <li>The same is done when closing the document or quitting the app.</li>
  4827. </ul>
  4828. <p>In some cases, like writing back a page to disk or closing a document, only repagination of pages following the current one is necessary.</p>
  4829. <h3 id="basic-algorithm">Basic Algorithm</h3>
  4830. <p>The basic repagination algorithm looks like this:</p>
  4831. <ul>
  4832. <li>Read the first page into the buffer and enable <code>streamingMode</code> for <code>getByteFromBuffer</code>. Using this function, the whole (remaining) document can now be read as if it was one linear file.</li>
  4833. <li>Keep reading from the document until the text fills a page.</li>
  4834. <li>Write the current buffer up to this point (including the header) to the record on disk that corresponds to the current page.</li>
  4835. <li>Move the remaining data in the buffer up to the beginning. This data is the start of the next page.</li>
  4836. <li>Copy the current font, style and ruler state into buffer&rsquo;s page header.</li>
  4837. <li>Repeat all of this until the end of the data is reached.</li>
  4838. </ul>
  4839. <h3 id="measuring-lines">Measuring Lines</h3>
  4840. <p>The core of pagination is the function that measures a line of text. Starting with the current pointer into the buffer, it reads and interprets bytes from the document, and returns the line&rsquo;s width, height, baseline and a buffer pointer that points to the first character of the next line.</p>
  4841. <p>This function is also used for rendering a line on screen or for the printer: Before a line can be rendered, the baseline has to be calculated, so that different fonts on the same line are printed consistently. And the width of the line is necessary to center it, for example.</p>
  4842. <p>The following screenshot shows an example of mixed fonts in a single line, where it is necessary to gather the lowest baseline before starting to draw the text:</p>
  4843. <p><img src="docs/geowrite/geowrite_7_baseline.png" height="400" width="640" alt="" /></p>
  4844. <p>First, this function calculates the available width by subtracting the left margin from the right margin. For the first line of a paragraph, the &ldquo;paragraph margin&rdquo; will be used instead of the left margin. It then reads and interprets the document byte by byte.</p>
  4845. <p>If a graphics escape is encountered, the height of the image is returned as the line height, since images are always in their own paragraphs.</p>
  4846. <p>Otherwise, the function keeps adding up the widths of characters based on the current font, and keeps track of the maximum baseline offset and maximum font height.</p>
  4847. <p>A TAB character in the text requires some additional logic: A TAB will have the cursor jump to the next tab stop. To account for this, the measure line function increases the width of the line to reach the tab stop. In the following example, the two words are separated by a TAB character.</p>
  4848. <pre><code>          tab stop  
  4849.             |
  4850. .............*..............  
  4851. Hello        World!  
  4852.     /\/\/\/\  
  4853.    added width
  4854. </code></pre>
  4855. <p>For decimal tab stops, it calculates the widths of all following text until the next decimal separator, and increases the width up to the tab stop minus the width of this text. In the following example, &ldquo;84&rdquo; is measured, and enough width is added so that the decimal separator is lined up with the tab stop.</p>
  4856. <pre><code>      decimal tab stop  
  4857.             |
  4858. .............*..............  
  4859. Total      84.25 EUR  
  4860.     /\/\/\  
  4861.    added width
  4862. </code></pre>
  4863. <p>If there is a ruler escape in the text, the ruler data gets copied into the app&rsquo;s state. All further calculations will use the new margins and tab stops. The same happens for NewCardSet escapes: All further character size calculations will be based on the new font and style.</p>
  4864. <p>During repagination, the metrics of all fonts used in the particular part of the document need to be known to be able to add up character widths. The geoWrite <a href="https://www.pagetable.com/?p=1436">font manager</a> has a font metrics cache that can hold data for up to 8 fonts, which is more than the font data cache, which can only hold an average of 3 font images, depending on their size. The font files have to be loaded at least once in order to extract the metrics, but the images are not necessary for repagination, and it is enough to keep the metrics data.</p>
  4865. <p>The end of a line is reached once the text overflows the available width. The function will then reset the buffer pointer to after the last SPACE character – this is the first character of the new line.</p>
  4866. <p>The end of a line is also reached if there is either an explicit line break or page break in the text. In this case, the pointer will be set to the character after the break.</p>
  4867. <h3 id="measuring-pages">Measuring Pages</h3>
  4868. <p>The function that measures a page first calculates the usable height by subtracting the header and footer heights from the page height – unless this is the title page, in which the full page height is available.</p>
  4869. <p>It then repeatedly calls the function to measure a line and adds up line heights until the sum overflows the usable page height. The buffer pointer is reset to the beginning of the first line that does not fit onto the page. This is the first character of the next page.</p>
  4870. <p>A special case is the page break character: Page measuring is stopped here, and the pointer to the next character is returned.</p>
  4871. <h2 id="conclusion">Conclusion</h2>
  4872. <p>While geoWrite is extremely powerful for an app on a 1 MHz computer with 64 KB of RAM, it is also very slow. Some of the reasons are true for many GEOS applications:</p>
  4873. <ul>
  4874. <li>The 6502 cannot efficiently handle 16 bit data, so dealing with pointers and dot size values requires large and slow code everywhere.</li>
  4875. <li>Because of memory scarcity, code has to repeatedly be <a href="https://www.pagetable.com/?p=1425">paged in</a> for certain functionality.</li>
  4876. <li>Some of the code is especially inefficient, because it had to be optimized for size rather than for speed.</li>
  4877. <li>Even with the GEOS &ldquo;turboDisk&rdquo; driver, the 1541 disk drive is still very slow, at a maximum of 4 KB/sec of linear reading.</li>
  4878. </ul>
  4879. <p>In this context, geoWrite picked a document model that allows the user to edit a page at a time practically without any disk access, with the tradeoff of slower repagination. So in practice, repaginating a document that contains dozens of pages can take a minute or more, but on the other hand, geoWrite can usually keep up with the fastest typists when rendering even complicated text layouts in real-time.</p>
  4880. <p>P.S.: The image at the beginning of this article shows the error message caused by a record overflowing 7000 bytes. This happens when using a font that is 9 pt or smaller and filling a page completely with characters. geoWrite will insert a page break character and re-run the pagination code.</p>
  4881. ]]></content:encoded>
  4882. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1471</wfw:commentRss>
  4883. <slash:comments>0</slash:comments>
  4884. </item>
  4885. <item>
  4886. <title>Inside geoWrite – 6: Localization</title>
  4887. <link>https://www.pagetable.com/?p=1460</link>
  4888. <comments>https://www.pagetable.com/?p=1460#comments</comments>
  4889. <pubDate>Tue, 08 Sep 2020 22:18:51 +0000</pubDate>
  4890. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  4891. <category><![CDATA[6502]]></category>
  4892. <category><![CDATA[Archeology]]></category>
  4893. <category><![CDATA[C64]]></category>
  4894. <category><![CDATA[Commodore]]></category>
  4895. <category><![CDATA[GEOS]]></category>
  4896. <category><![CDATA[Operating Systems]]></category>
  4897.  
  4898. <guid isPermaLink="false">https://www.pagetable.com/?p=1460</guid>
  4899. <description><![CDATA[In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses what was required for the German localization. Article Series The Overlay System Screen Recovery Font Management Zero Page Copy Protection Localization ← this article File Format and Pagination Copy &#38; Paste Keyboard Handling Overview Localizing an app ... <a title="Inside geoWrite – 6: Localization" class="read-more" href="https://www.pagetable.com/?p=1460">Read more<span class="screen-reader-text">Inside geoWrite – 6: Localization</span></a>]]></description>
  4900. <content:encoded><![CDATA[<p>In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses what was required for the German localization.</p>
  4901. <p><img src="docs/geowrite/geowrite_6_localizaion.gif" height="400" width="640" alt="" /></p>
  4902. <h2 id="article-series">Article Series</h2>
  4903. <ol>
  4904. <li><a href="https://www.pagetable.com/?p=1425">The Overlay System</a></li>
  4905. <li><a href="https://www.pagetable.com/?p=1428">Screen Recovery</a></li>
  4906. <li><a href="https://www.pagetable.com/?p=1436">Font Management</a></li>
  4907. <li><a href="https://www.pagetable.com/?p=1442">Zero Page</a></li>
  4908. <li><a href="https://www.pagetable.com/?p=1449">Copy Protection</a></li>
  4909. <li><strong>Localization</strong> ← this article</li>
  4910. <li><a href="https://www.pagetable.com/?p=1471">File Format and Pagination</a></li>
  4911. <li><a href="https://www.pagetable.com/?p=1481">Copy &amp; Paste</a></li>
  4912. <li><a href="https://www.pagetable.com/?p=1490">Keyboard Handling</a></li>
  4913. </ol>
  4914. <h2 id="overview">Overview</h2>
  4915. <p>Localizing an app doesn&rsquo;t mean just translating all text. Language is just one part of it. Here are all concepts that require changes to geoWrite:</p>
  4916. <ul>
  4917. <li>Language</li>
  4918. <li>Date/time format</li>
  4919. <li>Number format</li>
  4920. <li>Character set</li>
  4921. </ul>
  4922. <p>Let&rsquo;s go through them.</p>
  4923. <h2 id="language">Language</h2>
  4924. <p>Translating the app is not as easy as just translating all strings.</p>
  4925. <ul>
  4926. <li>Some strings must not be translated.</li>
  4927. <li>Not all text is part of the UI.</li>
  4928. <li>Not all text is in string form.</li>
  4929. </ul>
  4930. <h3 id="do-not-translate">Do Not Translate</h3>
  4931. <p>First, let&rsquo;s look at what <strong>must not</strong> be translated:</p>
  4932. <pre><code>fn_textscrap:  
  4933.        .byte   "Text  Scrap",0  
  4934. fn_photoscrap:  
  4935.        .byte   "Photo Scrap",0
  4936. </code></pre>
  4937. <p>These are magic filenames that contain the current clipboard/pasteboard contents. Translating them would break interoperability between apps in different languages.</p>
  4938. <h3 id="keywords">Keywords</h3>
  4939. <p>Then, there are strings where it is up to debate whether they should be translated: geoWrite supports three keywords that get replaced with dynamic contents when used in a page header or footer:</p>
  4940. <ul>
  4941. <li><code>DATE</code>: inserts the current date</li>
  4942. <li><code>TIME</code>: inserts the current time</li>
  4943. <li><code>PAGE</code>: inserts the current page number</li>
  4944. </ul>
  4945. <p>For the German version, these keywords were in fact translated: <code>DATUM</code>, <code>ZEIT</code>, <code>SEITE</code>.</p>
  4946. <h3 id="strings">Strings</h3>
  4947. <p>From the strings that should be translated, let&rsquo;s start with the straightforward ones: Here is a table of strings that is used in menus:</p>
  4948. <p><img src="docs/geowrite/geowrite_6_diff0.png" height="189" width="671" alt="" /></p>
  4949. <p>Care has to be taken that the translated version fits the available space: The translations of &ldquo;edit&rdquo; and &ldquo;options&rdquo; were abbreviated (&ldquo;Edit&rdquo;/&ldquo;Editieren&rdquo;, &ldquo;Opt&rdquo;/&ldquo;Optionen&rdquo;), because the whole menu bar would have been too wide otherwise:</p>
  4950. <p><img src="docs/geowrite/geowrite_6_menubar.png" height="60" width="630" alt="" /></p>
  4951. <p>For submenus, GEOS programmers have to explicitly state the location and size in pixels, so the definition of a submenu has to change as well:</p>
  4952. <p><img src="docs/geowrite/geowrite_6_menu_en.png" height="400" width="640" alt="" /><br />
  4953. <img src="docs/geowrite/geowrite_6_menu_de.png" height="400" width="640" alt="" /></p>
  4954. <p><img src="docs/geowrite/geowrite_6_diff0a.png" height="190" width="697" alt="" /></p>
  4955. <p>The German version is wider, so the value for the right border of the menu was updated.</p>
  4956. <p>Additionally, all menus in the German version are moved up by one pixel. If you look closely, you can see that the English version has a double line between &ldquo;file&rdquo; and &ldquo;close&rdquo;, while the German version has a single line. The symbol <code>MENU_HEIGHT</code> in the previous code block is 15 for the English version and 14 for the German version. It is unknown what the purpose of this is.</p>
  4957. <p>In the case of dialogs, the translated text might not fit into the same number of lines and might require a re-layout:</p>
  4958. <p><img src="docs/geowrite/geowrite_6_dialog1_en.png" height="400" width="640" alt="" /><br />
  4959. <img src="docs/geowrite/geowrite_6_dialog1_de.png" height="400" width="640" alt="" /></p>
  4960. <p>So while the English version just consists of one line of text, the German version adds a <code>GOTOXY</code> control code to move the cursor to the second line:</p>
  4961. <p><img src="docs/geowrite/geowrite_6_diff1.png" height="111" width="669" alt="" /></p>
  4962. <p>Because of word order differences, the startup dialog needed a complete redesign&hellip;</p>
  4963. <p><img src="docs/geowrite/geowrite_6_dialog2_en.png" height="400" width="640" alt="" /><br />
  4964. <img src="docs/geowrite/geowrite_6_dialog2_de.png" height="400" width="640" alt="" /></p>
  4965. <p>&hellip;which required changing the locations of all text and icons in the dialog&rsquo;s definition:</p>
  4966. <p><img src="docs/geowrite/geowrite_6_diff2.png" height="399" width="668" alt="" /></p>
  4967. <h3 id="images">Images</h3>
  4968. <p>The startup dialog contains buttons that say &ldquo;Create&rdquo;, &ldquo;Open&rdquo; and &ldquo;Quit&rdquo;. GEOS only provides a limited set of predefined buttons (&ldquo;OK&rdquo;, &ldquo;Cancel&rdquo;, &ldquo;Open&rdquo;, &hellip;), so the pixel images of &ldquo;Create&rdquo; and &ldquo;Quit&rdquo; are supplied by the app and need to be translated as well.</p>
  4969. <p><img src="docs/geowrite/geowrite_6_buttons.png" height="66" width="226" alt="" /></p>
  4970. <p>The translated words are longer, so the buttons have to be bigger as well.</p>
  4971. <h3 id="screen-recovery-rectangles">Screen Recovery Rectangles</h3>
  4972. <p>As discussed in <a href="https://www.pagetable.com/?p=1428">part 1</a> of this series, GEOS uses a custom system to save and recover screen contents that get overwritten by menus and dialogs. Since the sizes and positions of the menus are different, the rectangles that need to be recovered are changed in their table as well:</p>
  4973. <p><img src="docs/geowrite/geowrite_6_diff3.png" height="514" width="653" alt="" /></p>
  4974. <h2 id="date/time-format">Date/Time Format</h2>
  4975. <p>Different cultures/languages use different conventions for the date and time format. The <code>DATE</code> and <code>TIME</code> keywords stamp the current date and time into a page&rsquo;s header or footer. For the English version, it uses the US format for dates and times:</p>
  4976. <pre><code>December 31, 1999  11:59 PM
  4977. </code></pre>
  4978. <p>For the German version, it uses the German format, with German month names:</p>
  4979. <pre><code>31. Dezember 1999  23:59
  4980. </code></pre>
  4981. <p>This is the core function to create the date string:</p>
  4982. <pre><code>        ; date  
  4983.        LoadW   r0, dateString  
  4984. .if DATE_FORMAT=DATE_FORMAT_US  
  4985.        jsr     getMonthName  
  4986.        jsr     getDay  
  4987. .elseif DATE_FORMAT=DATE_FORMAT_DE  
  4988.        jsr     getDay  
  4989.        jsr     getMonthName  
  4990. .endif  
  4991.        jsr     getYear
  4992. </code></pre>
  4993. <p>The month and day are reversed in the two different formats. The &ldquo;.&rdquo; vs. the &ldquo;,&rdquo; after the day gets handled by the function <code>getDay</code>:</p>
  4994. <pre><code>getDay:  
  4995.        MoveB   day, r3L  
  4996.        LoadB   r3H, 0  
  4997.        jsr     byteToDecimal  
  4998.        ldy     #0  
  4999. .if DATE_FORMAT=DATE_FORMAT_US  
  5000.        lda     #','  
  5001. .elseif DATE_FORMAT=DATE_FORMAT_DE  
  5002.        lda     #'.'  
  5003. .endif  
  5004.        sta     (r0),y  
  5005.        IncW    r0  
  5006.        lda     #' '  
  5007.        sta     (r0),y  
  5008.        IncW    r0  
  5009.        rts
  5010. </code></pre>
  5011. <p>The function to create the time string has some extra logic to convert the hour (0-23) to the range (1-12):</p>
  5012. <pre><code>        lda     hour  
  5013. .if DATE_FORMAT=DATE_FORMAT_US          ; AM/PM  
  5014.        cmp     #12  
  5015.        bcc     :+                      ; &gt;= 12?  
  5016.        sub     #12                     ; then subtract 12  
  5017. :       cmp     #0  
  5018.        bne     :+                      ; == 0  
  5019.        lda     #12                     ; then it's 12  
  5020. :
  5021. .endif  
  5022.        sta     r3L  
  5023.        LoadB   r3H, 0
  5024.  
  5025.        jsr     byteToDecimal           ; hours  
  5026.        ldy     #0  
  5027.        lda     #':'  
  5028.        sta     (r0),y                  ; ':'  
  5029.        IncW    r0
  5030.  
  5031.        lda     minutes  
  5032.        sta     r3L  
  5033.        LoadB   r3H, 0  
  5034.        lda     #1  
  5035.        jsr     byteToDecimal           ; minutes
  5036. </code></pre>
  5037. <p>And at the end, there is extra code in the US version to add &ldquo;AM&rdquo; or &ldquo;PM&rdquo;:</p>
  5038. <pre><code>        ldy     #0  
  5039.        lda     #' '  
  5040.        sta     (r0),y                  ; space  
  5041.        IncW    r0
  5042.  
  5043. .if DATE_FORMAT=DATE_FORMAT_US          ; AM/PM  
  5044.        lda     #'A'  
  5045.        ldx     hour  
  5046.        cpx     #12  
  5047.        bcc     :+  
  5048.        lda     #'P'  
  5049. :       sta     (r0),y  
  5050.        IncW    r0  
  5051.        lda     #'M'  
  5052.        sta     (r0),y  
  5053.        IncW    r0  
  5054. .endif
  5055. </code></pre>
  5056. <h2 id="number-format">Number Format</h2>
  5057. <p>The character used for the decimal separator may differ between languages – &ldquo;3.14&rdquo; in an English text would be &ldquo;3,14&rdquo; in a German text. Since geoWrite supports &ldquo;decimal&rdquo; tab stops that align numbers around the decimal separator, it needs to scan for this character: The English version checks for &ldquo;.&rdquo;, while the German version checks for &ldquo;,&rdquo;.</p>
  5058. <h2 id="character-set-&amp;-encoding">Character Set &amp; Encoding</h2>
  5059. <p>The German language has four extra letters, the umlauts: &ldquo;ä&rdquo;/&ldquo;Ä&rdquo;, &ldquo;ö&rdquo;/&ldquo;Ö&rdquo;, &ldquo;ü&rdquo;/&ldquo;Ü&rdquo; and &ldquo;ß&rdquo;.</p>
  5060. <h3 id="geos-character-encoding">GEOS Character Encoding</h3>
  5061. <p>Until the advent of Unicode, operating systems used different character encodings for different languages or scripts.</p>
  5062. <p>The English version of GEOS uses the 7 bit ASCII encoding, which contains the 26 letters A through Z, but no umlauts. The GEOS KERNAL has no context of a character encoding, it just blindly draws glyphs that are stored at an index in a font – as long as the index is between 32 and 127, the 7-bit ASCII printable range. The only difference between the English and the German operating system in terms of character encoding are the fonts: Just like the regular fonts, the fonts that come with the German version have 96 characters, but some characters have been replaced by the extra umlauts and the &lsquo;§&rsquo; character (important for legal documents). These are the variants of the system font &ldquo;BSW/9&rdquo;:</p>
  5063. <p><img src="docs/geowrite/bsw9.png" height="20" width="954" alt="" /><br />
  5064. <img src="docs/geowrite/bsw9_de.png" height="20" width="960" alt="" /></p>
  5065. <table>
  5066. <thead>
  5067. <tr>
  5068. <th> ASCII </th>
  5069. <th> German GEOS </th>
  5070. </tr>
  5071. </thead>
  5072. <tbody>
  5073. <tr>
  5074. <td> @     </td>
  5075. <td> §           </td>
  5076. </tr>
  5077. <tr>
  5078. <td> [     </td>
  5079. <td> Ä           </td>
  5080. </tr>
  5081. <tr>
  5082. <td> \     </td>
  5083. <td> Ö           </td>
  5084. </tr>
  5085. <tr>
  5086. <td> ]     </td>
  5087. <td> Ü           </td>
  5088. </tr>
  5089. <tr>
  5090. <td> {     </td>
  5091. <td> ä           </td>
  5092. </tr>
  5093. <tr>
  5094. <td> |    </td>
  5095. <td> ö           </td>
  5096. </tr>
  5097. <tr>
  5098. <td> }     </td>
  5099. <td> ü           </td>
  5100. </tr>
  5101. <tr>
  5102. <td> ~     </td>
  5103. <td> ß           </td>
  5104. </tr>
  5105. </tbody>
  5106. </table>
  5107. <p>geoWrite doesn&rsquo;t generally have to care about the encoding either: With the German font set, any version of geoWrite will display German umlauts.</p>
  5108. <p>There are two cases where it does have to care though: searching and printing.</p>
  5109. <h3 id="searching">Searching</h3>
  5110. <p>The function to search text has the option of searching for whole words only. For this, geoWrite needs to know which code points are letters or numbers. In English, that&rsquo;s A through Z and 0 through 9. In German, this must include the umlauts. This is the function that decides on what&rsquo;s an alphanumeric character:</p>
  5111. <pre><code>isAlphanumeric:  
  5112.        cmp     #'0'  
  5113.        bcc     @1  
  5114.        cmp     #'9'+1  
  5115.        bcc     @yes  
  5116. @1:  
  5117. .if CHAR_ENCODING=CHAR_ENCODING_ASCII  
  5118.        cmp     #'A'  
  5119. .elseif CHAR_ENCODING=CHAR_ENCODING_DE  
  5120.        cmp     #'@'  
  5121. .endif  
  5122.        bcc     @2  
  5123. .if CHAR_ENCODING=CHAR_ENCODING_ASCII  
  5124.        cmp     #'Z'+1  
  5125. .elseif CHAR_ENCODING=CHAR_ENCODING_DE  
  5126.        cmp     #']'+1  
  5127. .endif  
  5128.        bcc     @yes  
  5129. @2:     cmp     #'a'  
  5130.        bcc     @3  
  5131. .if CHAR_ENCODING=CHAR_ENCODING_ASCII  
  5132.        cmp     #'z'+1  
  5133. .elseif CHAR_ENCODING=CHAR_ENCODING_DE  
  5134.        cmp     #'~'+1  
  5135. .endif  
  5136.        bcc     @yes  
  5137. @3:     cmp     #'_'  
  5138.        beq     @yes  
  5139.        clc  
  5140.        rts
  5141.  
  5142. @yes:   sec  
  5143.        rts
  5144. </code></pre>
  5145. <p>With the German encoding, it includes the three characters after the uppercase &lsquo;Z&rsquo; and the four characters after the lowercase &lsquo;z&rsquo; (see image of font above). There is a bug in this code though: The German version considers &ldquo;§&rdquo; (&ldquo;@&rdquo; in the code above) an alphanumeric character, which it isn&rsquo;t.</p>
  5146. <h3 id="printing">Printing</h3>
  5147. <p>The default is for geoWrite to print pixel images of the pages of a document. But there is also ASCII mode, which sends the plain text to the printer, so the printer can use its built-in fonts. In this mode, the English GEOS sends ASCII-encoded text, that is, its internal representation without any conversion, to the printer driver. If the printer uses a different encoding, the driver has to do the conversion.</p>
  5148. <p>German GEOS can&rsquo;t just send the codes for &ldquo;§ÄÖÜäöüß&rdquo; – they would print as &ldquo;@[]{|}~&rdquo;. It has to convert them, so that printer drivers can be universal and independent of the system&rsquo;s language.</p>
  5149. <p>This is the code that does the conversion – it is missing from the English version:</p>
  5150. <pre><code>convertToCp437:  
  5151.        ldy     #8  
  5152. @loop:  cmp     @from-1,y  
  5153.        beq     @found  
  5154.        dey  
  5155.        bne     @loop  
  5156.        rts  
  5157. @found: lda     @to-1,y  
  5158.        rts
  5159.  
  5160. @from:  .byte '@','[','\',']','{','|','}','~'   ; source: GEOS_de  
  5161. @to:    .byte $EB,$8E,$99,$9A,$84,$94,$81,$E1   ; target: CP437  
  5162. ;             'δ','Ä','Ö','Ü','ä','ö','ü','ß'
  5163. </code></pre>
  5164. <p>The eight character codes in German GEOS that differ from ASCII (line <code>@from</code>) are converted to eight codes above 127 (line <code>@to</code>).</p>
  5165. <p>The destination encoding is <a href="https://en.wikipedia.org/wiki/Code_page_437">Codepage 437</a>, the standard (and now obsolete) encoding used by the IBM PC and MS-DOS. That is, except for &lsquo;§&rsquo;, whose CP437 equivalent would be $15, which is a non-printable character in ASCII-based encodings.</p>
  5166. <p>The authors of GEOS were free to choose any encoding – it&rsquo;s really just a convention between applications and the printer drivers. But with CP437, drivers for PC printers of the time can just pipe the data through as is.</p>
  5167. <h2 id="discussion">Discussion</h2>
  5168. <p>Modern software usually comes as a single application binary that supports multiple languages, and with support from the operating system can use different conventions for date/time and numbers, and uses Unicode to express and work with any character in any script.</p>
  5169. <p>geoWrite is running on a 64 KB system and doesn&rsquo;t have the luxury of spending code for any of these features – all localization differences are compile-time options. This means that in a multi-lingual environment, there are many limitations:</p>
  5170. <ul>
  5171. <li>The English version of geoWrite on a German version of GEOS will support umlauts, but can&rsquo;t correctly search for words with umlauts or print umlauts in ASCII mode. Besides, some buttons in the UI will be in German.</li>
  5172. <li>The German version of geoWrite on an English version of GEOS will not support umlauts, and the characters &ldquo;@[]{|}~&rdquo; won&rsquo;t print correctly in ASCII mode.</li>
  5173. <li>Writing an English document in the German version of geoWrite will use the German date/time format, use German month names, and can&rsquo;t use decimal tab stops with with a &lsquo;.&rsquo; as the decimal point.</li>
  5174. <li>Writing a German document in the English version of geoWrite (on a German GEOS) has the equivalent problems. In addition, searching for words with umlauts won&rsquo;t work correctly, and neither will printing umlauts.</li>
  5175. <li>Writing a French or Spanish document with any version of geoWrite works, even with accented letters, as long as only fonts are used where the extra letters are added. But the same limitations with date/time, numbers, searching and printing apply.</li>
  5176. <li>Good luck with CJK and RTL!</li>
  5177. <li>Opening a document in a different language version of geoWrite than it was saved in will break either the umlauts or the &ldquo;@[]{|}~&rdquo; characters, as well as data, time and page numbers in headers and footers.</li>
  5178. </ul>
  5179. <p>Then again, it would be possible to architect a version of geoWrite with more flexibility:</p>
  5180. <ul>
  5181. <li>The code for date, time, numbers and the encoding only differs minimally between the localizations, so the app could support all variants, based on a system setting.</li>
  5182. <li>The VLIR architecture of GEOS applications allows dividing code and data into an arbitrary number of records, so every VLIR record of the current geoWrite app could be split into two: one with the code, and one with the strings and UI data structures. Which variant of the UI gets loaded depends on the system language.</li>
  5183. </ul>
  5184. <p>The latter point would of course waste space on disk (regular geoWrite is 35 KB, a 1541 disk holds 165 KB) and increase load times of VLIR records.</p>
  5185. ]]></content:encoded>
  5186. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1460</wfw:commentRss>
  5187. <slash:comments>1</slash:comments>
  5188. </item>
  5189. <item>
  5190. <title>Inside geoWrite – 5: Copy Protection</title>
  5191. <link>https://www.pagetable.com/?p=1449</link>
  5192. <comments>https://www.pagetable.com/?p=1449#comments</comments>
  5193. <pubDate>Sun, 06 Sep 2020 09:48:15 +0000</pubDate>
  5194. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  5195. <category><![CDATA[6502]]></category>
  5196. <category><![CDATA[Archeology]]></category>
  5197. <category><![CDATA[C64]]></category>
  5198. <category><![CDATA[Commodore]]></category>
  5199. <category><![CDATA[Floppy Disks]]></category>
  5200. <category><![CDATA[GEOS]]></category>
  5201. <category><![CDATA[Operating Systems]]></category>
  5202.  
  5203. <guid isPermaLink="false">https://www.pagetable.com/?p=1449</guid>
  5204. <description><![CDATA[In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses the geoWrite copy protection. Article Series The Overlay System Screen Recovery Font Management Zero Page Copy Protection ← this article Localization File Format and Pagination Copy &#38; Paste Keyboard Handling GEOS Copy Protection Strategy GEOS has one ... <a title="Inside geoWrite – 5: Copy Protection" class="read-more" href="https://www.pagetable.com/?p=1449">Read more<span class="screen-reader-text">Inside geoWrite – 5: Copy Protection</span></a>]]></description>
  5205. <content:encoded><![CDATA[<p>In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses the geoWrite copy protection.</p>
  5206. <p><img src="docs/geowrite/geowrite_5_wrong_serial.png" alt="" /></p>
  5207. <h2 id="article-series">Article Series</h2>
  5208. <ol>
  5209. <li><a href="https://www.pagetable.com/?p=1425">The Overlay System</a></li>
  5210. <li><a href="https://www.pagetable.com/?p=1428">Screen Recovery</a></li>
  5211. <li><a href="https://www.pagetable.com/?p=1436">Font Management</a></li>
  5212. <li><a href="https://www.pagetable.com/?p=1442">Zero Page</a></li>
  5213. <li><strong>Copy Protection</strong> ← this article</li>
  5214. <li><a href="https://www.pagetable.com/?p=1460">Localization</a></li>
  5215. <li><a href="https://www.pagetable.com/?p=1471">File Format and Pagination</a></li>
  5216. <li><a href="https://www.pagetable.com/?p=1481">Copy &amp; Paste</a></li>
  5217. <li><a href="https://www.pagetable.com/?p=1490">Keyboard Handling</a></li>
  5218. </ol>
  5219. <h2 id="geos-copy-protection-strategy">GEOS Copy Protection Strategy</h2>
  5220. <p>GEOS has one of the most notorious copy protection systems. The system disk contains bit patterns that are very hard to reproduce on a stock disk drive. These are checked on every boot in obfuscated code that is used to decrypt the core operating system code. This way, copies of the GEOS boot disk will not boot. Therefore, GEOS always has to be booted from the original disk, so these would <a href="https://www.pagetable.com/?p=1118">break frequently</a>, which is why GEOS came with a second boot disk, and there was a program to get broken boot disks replaced once both failed.</p>
  5221. <p>GEOS maker Berkeley Softworks also created several high-profile GEOS applications like geoPublish and geoCalc, which came with a similar copy protection. Even the bundled apps geoWrite, geoSpell and geoMerge have the same protection.</p>
  5222. <p>But with apps, it&rsquo;s more complicated: On a C64 system with a single disk drive, the app needs to be on the same disk as the document that is being worked on, and since it&rsquo;s a non-starter to make the user edit all their documents on the original application disk, it needs to be possible to have a copy of the app working on the user&rsquo;s work disk – and still prevent pirated copies of the app from running.</p>
  5223. <p>The idea is to link the boot disk and the application through a serial number. On the <a href="https://www.pagetable.com/?p=1140#Conclusion">very first boot</a>, the GEOS system picks a random 16 bit serial number (excluding zero) and stores it on the boot disk as well as on the backup disk – this is called &ldquo;installing&rdquo; GEOS. On the first start of a copy-protected application, it verifies that it&rsquo;s running from the original disk, and if yes, it takes the system&rsquo;s serial number and stores it – this is called &ldquo;installing&rdquo; an application. On subsequent boots, it does not check for the original disk any more, but it only runs if its stored serial number matches the system&rsquo;s.</p>
  5224. <p>As long as the GEOS boot disk cannot be copied, two users (who both bought GEOS) will have different serial numbers, and installed apps from one user won&rsquo;t work on a different user&rsquo;s GEOS. And a copy of a not-yet-installed app will refuse to install itself, because it doesn&rsquo;t run from an original disk.</p>
  5225. <h2 id="code">Code</h2>
  5226. <p>Apart from this basic protection concept, geoWrite obfuscates what&rsquo;s going on by encrypting parts of the code, to make it hard to crack the protection.</p>
  5227. <p>Let&rsquo;s walk through the components of the copy protection in the order of what happens on application startup.</p>
  5228. <h3 id="encrypted-record-1">Encrypted Record 1</h3>
  5229. <p>As discussed in <a href="https://www.pagetable.com/?p=1425">part 1</a> of this series, the GEOS &ldquo;VLIR&rdquo; binary consists of 9 so-called records, which are basically individual code files. Record 0 is the main program, and records 1 through 8 get swapped in and out of memory based on what functionality is needed.</p>
  5230. <p>When the application gets started, the first thing the record 0 code of geoWrite does is load record 1, which contains initialization code as well as the copy protection.</p>
  5231. <pre><code>        lda     #BANK_1  
  5232.        jsr     loadCode
  5233. </code></pre>
  5234. <p>All of record 1 is encrypted, so after loading, it decrypts it by XORing every byte with $DE.</p>
  5235. <pre><code>        lda     #$EB  
  5236.        eor     #$35  
  5237.        sta     @2  
  5238.        LoadW   r0, MEM_OVERLAY  
  5239.        LoadW   r1, -4000  
  5240.        ldy     #0  
  5241. @1:     lda     (r0),y  
  5242. @2 = * + 1  
  5243.        eor     #$00  
  5244.        sta     (r0),y  
  5245.        IncW    r0  
  5246.        IncW    r1  
  5247.        bne     @1
  5248. </code></pre>
  5249. <p>The decryption constant of $DE gets constructed using $ED XOR $35, for which there is no good reason other than maybe making it harder to search for the value.</p>
  5250. <p>After decryption, execution is handed to the record 1 code:</p>
  5251. <pre><code>        jmp     MEM_OVERLAY
  5252. </code></pre>
  5253. <h3 id="installation">Installation</h3>
  5254. <p>The first few instructions of record 1 do things in a way more complicated way than necessary, probably to deter any hackers from looking further:</p>
  5255. <pre><code>.,3244  A9 32     LDA #$32  
  5256. .,3246  48        PHA  
  5257. .,3247  A9 54     LDA #$54  
  5258. .,3249  48        PHA  
  5259. .,324A  A9 3B     LDA #$3B  
  5260. .,324C  85 21     STA $21  
  5261. .,324E  A9 6F     LDA #$6F  
  5262. .,3250  85 20     STA $20  
  5263. .,3252  6C 20 00  JMP ($0020)
  5264. </code></pre>
  5265. <p>The source makes it clear what&rsquo;s going on:</p>
  5266. <pre><code>        lda     #&gt;(@continue-1)  
  5267.        pha  
  5268.        lda     #&lt;(@continue-1)  
  5269.        pha  
  5270.        LoadW   r15, initApp  
  5271.        jmp     (r15)  
  5272. @continue:
  5273. </code></pre>
  5274. <p>It pushes the address of the code following it as a return address (i.e. minus one) on the stack and jumps to <code>initApp</code> using a vector. This is just a convoluted way of calling <code>initApp</code> and continuing with the code below.</p>
  5275. <p><code>initApp</code> does some initialization and calls <code>checkSerialOrInstall</code>. This is the first part of it:</p>
  5276. <pre><code>checkSerialOrInstall:  
  5277.        lda     serial  
  5278.        ora     serial+1                ; does app have a serial?  
  5279.        beq     @install                ; no, then install
  5280.  
  5281.        lda     #&lt;GetSerialNumber  
  5282.        ldx     #&gt;GetSerialNumber  
  5283.        jsr     CallRoutine  
  5284.        CmpW    serial, r0              ; does the app serial match the system's?  
  5285.        beq     @rts                    ; yes, return
  5286.  
  5287.        lda     #&lt;txt_serial_mismatch  
  5288.        ldy     #&gt;txt_serial_mismatch  
  5289.        jsr     showError               ; no, tell the user  
  5290.        jsr     swap_userzp  
  5291.        jmp     EnterDeskTop            ; and exit
  5292.  
  5293. @rts:   rts
  5294. </code></pre>
  5295. <p>(For the meaning of <code>swap_userzp</code>, check out <a href="https://www.pagetable.com/?p=1442">part 4</a> of this series.)</p>
  5296. <p><img src="docs/geowrite/geowrite_5_wrong_serial.png" alt="" /></p>
  5297. <p><code>serial</code> is a 16 bit variable that is part of the record 1 code:</p>
  5298. <pre><code>serial:  
  5299.        .word   0                       ; not installed
  5300. </code></pre>
  5301. <p>If it is zero, <code>checkSerialOrInstall</code> jumps to the install logic. Otherwise it gets the system&rsquo;s serial. (It could just call the <code>GetSerialNumber</code> KERNAL API directly, but instead, it calls it by loading its address into registers and calling <code>CallRoutine</code>, so that a reverse engineer has a harder time finding the call.)</p>
  5302. <p>If the system&rsquo;s serial number is the same, the function returns, and the application can start. If no, it shows an error and exits the app.</p>
  5303. <p>Let&rsquo;s look at the installer code. First, it calls <code>executeDiskBlock</code>, which loads a block from disk and runs it:</p>
  5304. <pre><code>@install:  
  5305. protExecTrack = * + 1  
  5306.        lda     #0                      ; protection track (stamped in by build system)  
  5307.        sta     r1L  
  5308. protExecSector = * + 1  
  5309.        lda     #0                      ; protection sector (stamped in by build system)  
  5310.        sta     r1H  
  5311.        jsr     executeDiskBlock  
  5312.        beqx    @ok                     ; no error
  5313. </code></pre>
  5314. <p>This block contains the code to verify that this is an original disk and not a copy. (We will discuss all of this in detail in the next section.) If <code>executeDiskBlock</code> returns with X != 0, this signals a failure, and and geoWrite exits:</p>
  5315. <pre><code>        lda     #&lt;txt_copy_protection   ; installing a non-original disk?  
  5316.        ldy     #&gt;txt_copy_protection   ; then show a non-informative message  
  5317.        bra     showErrorAndExit        ; and exit to deskTop
  5318. </code></pre>
  5319. <p><img src="docs/geowrite/geowrite_5_wrong_disk.png" alt="" /></p>
  5320. <p>Otherwise, the installer now knows that it&rsquo;s an original disk, so it can stamp in the system&rsquo;s serial into its own code. So after fetching the system&rsquo;s serial again, it reads the block from disk that is supposed to contain the app&rsquo;s serial. This block is part of the record 1 file.</p>
  5321. <pre><code>@ok:    lda     #&lt;GetSerialNumber  
  5322.        ldx     #&gt;GetSerialNumber  
  5323.        jsr     CallRoutine             ; get OS serial number to put into app
  5324.  
  5325.        MoveW   r0, serial  
  5326.        LoadW   r4, diskBlkBuf  
  5327. protSerialTrack = * + 1  
  5328.        lda     #0                      ; serial track (stamped in by build system)  
  5329.        sta     r1L  
  5330. protSerialSector = * + 1  
  5331.        lda     #0                      ; serial sector (stamped in by build system)  
  5332.        sta     r1H  
  5333.        jsr     _GetBlock               ; read sector that contains code with serial  
  5334.        bnex    @ierror
  5335. </code></pre>
  5336. <p>Note that the track and sector numbers point to the location of this very code on disk. The geoWrite build system creates a disk image with the app on it and stamps track and sector numbers in.</p>
  5337. <p>The serial has to be put at the correct location within the block and encrypted with the same XOR $DE that is used for decrypting all of record 1:</p>
  5338. <pre><code>@offset = (serial-CODE1) .mod 254 + 2  
  5339.        lda     serial                  ; get serial low  
  5340.        eor     #$DE                    ; "encrypt"  
  5341.        sta     diskBlkBuf+@offset  
  5342.        lda     serial+1                ; get serial high  
  5343.        eor     #$DE                    ; "encrypt"  
  5344.        sta     diskBlkBuf+@offset+1
  5345. </code></pre>
  5346. <p>The offset can be calculated by the assembler: It&rsquo;s the offset of the serial in the current (record 1) code, modulus 254 (because blocks on disk are 254 bytes), plus 2 (number of header bytes at the start of each block).</p>
  5347. <pre><code>        LoadW   r4, diskBlkBuf  
  5348.        jsr     _PutBlock               ; write back block  
  5349.        beqx    installOk
  5350.  
  5351.        cpx     #WR_PR_ON  
  5352.        beq     @wperr  
  5353. @ierror:  
  5354.        lda     #&lt;txt_error_installing  
  5355.        ldy     #&gt;txt_error_installing  
  5356.        bra     showErrorAndExit
  5357.  
  5358. @wperr: lda     #&lt;txt_install_write_protected  
  5359.        ldy     #&gt;txt_install_write_protected
  5360.  
  5361. showErrorAndExit:  
  5362.        jsr     showError  
  5363.        jsr     swap_userzp  
  5364.        jmp     EnterDeskTop
  5365. </code></pre>
  5366. <p>Finally, the block is written back. If there was an error, a dialog is shown, and the app exits.</p>
  5367. <p>If installation was successful, the following code runs:</p>
  5368. <pre><code>installOk:  
  5369.        asl     serial                  ; cycle serial left to obfuscate  
  5370.        rol     serial+1                ; serial = serial[14..0,15]  
  5371.        lda     serial  
  5372.        adc     #0  
  5373.        sta     serial
  5374.  
  5375.        jsr     swap_userzp  
  5376.        jsr     GetDirHead              ; read BAM block  
  5377.        jsr     swap_userzp
  5378.  
  5379.        MoveW   serial, curDirHead+$BE  ; store serial after "GEOS format V1.x"
  5380.  
  5381.        jsr     swap_userzp  
  5382.        jsr     PutDirHead              ; write BAM block  
  5383.        jsr     swap_userzp
  5384. </code></pre>
  5385. <p>This writes a copy of the serial with the bits rotated into two unused bytes of the disk&rsquo;s header block, track 18, sector 0.</p>
  5386. <p>The reason for this most probably had to do with ordering broken replacement disks: Once both the system and the backup disks didn&rsquo;t boot any more, the user was supposed to send in both disks, and would get new disks in return, already installed with the same serial, so that existing apps would continue to function.</p>
  5387. <p>Side B of the system disk contains geoWrite, and side B of the backup disk contains geoMerge, both of which had to be installed – the manual explicitly instructs the user to open each app once. So after this, both boot disks contain the obfuscated but plaintext serial on track 18, sector 0, offset $BE of side B. This could then be used to create proper replacement disks. After all, sides A of both disks were broken.</p>
  5388. <p>Finally, it shows a success dialog and exits.</p>
  5389. <pre><code>        lda     #&lt;txt_installed  
  5390.        ldy     #&gt;txt_installed         ; show success  
  5391.        jsr     showError  
  5392.        jsr     swap_userzp  
  5393.        jmp     EnterDeskTop            ; and exit
  5394. </code></pre>
  5395. <p><img src="docs/geowrite/geowrite_5_installed.png" alt="" /></p>
  5396. <h3 id="disk-signature-check">Disk Signature Check</h3>
  5397. <p>We skipped over <code>executeDiskBlock</code>, which was called by the installer. First, it initializes the disk:</p>
  5398. <pre><code>executeDiskBlock:  
  5399.        PushW   r1  
  5400.        jsr     swap_userzp  
  5401.        jsr     NewDisk  
  5402.        jsr     swap_userzp  
  5403.        PopW    r1  
  5404.        bnex    @rts                    ; I/O error -&gt; fail
  5405. </code></pre>
  5406. <p>Then it reads the block whose track and sector was passed in by the caller. It&rsquo;s the location of the protection check code that was stamped in by the build system.</p>
  5407. <pre><code>        LoadW   r4, diskBlkBuf          ; read block  
  5408.        jsr     _GetBlock  
  5409.        bnex    @rts                    ; I/O error -&gt; fail
  5410. </code></pre>
  5411. <p>The last byte of the block is a checksum which is the lower 8 bits of the sum of all payload bytes of the sector:</p>
  5412. <pre><code>        lda     #0  
  5413.        ldy     #2  
  5414. @loop:  clc  
  5415.        adc     diskBlkBuf,y            ; checksum bytes $02-$FE  
  5416.        iny  
  5417.        cpy     #$FF  
  5418.        bne     @loop  
  5419.        cmp     diskBlkBuf+$FF          ; checksum at offset $FF  
  5420.        beq     @ok
  5421.  
  5422.        ldx     #$FF                    ; fail  
  5423. @rts:   rts
  5424. </code></pre>
  5425. <p>If the checksum matches, the block is run in place in the disk block buffer (<code>diskBlkBuf</code> at $8000):</p>
  5426. <pre><code>@ok:    jsr     swap_userzp  
  5427.        jsr     diskBlkBuf+2            ; execute block  
  5428.        jsr     swap_userzp  
  5429.        rts
  5430. </code></pre>
  5431. <p>The protection check block is not part of the VLIR file and not formally referenced anywhere. The build system just writes it to a random free block when it generates the final disk image. In fact, it writes another 6 unused decoy copies of the block.</p>
  5432. <p>This is the layout of this block:</p>
  5433. <pre><code>00:  00 ff                                             block link pointer
  5434.  
  5435.           4c 7a 80                                    jump at entry point
  5436.  
  5437.                    ad 0f 18  48 29 df 8d 0f 18 20 16  
  5438. 10:  07 68 8d 0f 18 60 ba 86  49 a9 ee 8d 0c 1c a9 07  
  5439. 20:  85 33 a9 f5 85 32 a5 22  8d f5 07 20 10 f5 a0 02  
  5440. 30:  84 00 20 55 07 a2 10 d0  12 a0 00 20 55 07 a0 45  drive code  
  5441. 40:  20 55 07 20 64 07 a0 0a  20 55 07 20 64 07 ca d0  
  5442. 50:  e8 e8 86 00 60 50 fe b8  ad 01 1c 88 d0 f7 60 ad  
  5443. 60:  01 1c b8 60 ac 00 1c 10  f6 50 f9 b8 ad 01 1c c9  
  5444. 70:  55 f0 f1 c9 67 f0 ed 68  68 60
  5445.  
  5446.                                    ad e3 c1 85 03 ad  
  5447. 80:  e2 c1 85 02 ad e1 c1 c9  4c f0 0c a0 00 b1 02 aa  
  5448. 90:  c8 b1 02 85 03 86 02 a2  0a ac 89 84 b9 86 84 29  
  5449. a0:  bf c9 02 90 24 d0 57 a8  88 88 b1 02 c8 c9 20 d0  computer code  
  5450. b0:  f9 b1 02 c8 c9 5c d0 f1  b1 02 c8 c9 c2 d0 e9 b1  
  5451. c0:  02 c8 c9 20 d0 f9 88 d0  11 a0 ff c8 b1 02 c9 85  
  5452. d0:  d0 f9 c8 b1 02 c9 8b d0  f3 c8 a2 00 b1 02 9d f5  
  5453. e0:  80 c8 e8 e0 06 d0 f5 20  14 c2 20 5c c2 a9 05 85  
  5454. f0:  8b a2 07 86 8c 00 00 00  00 00 00 20 5f c2 60
  5455.  
  5456.                                                   d7  checksum
  5457. </code></pre>
  5458. <p>The second half of the block is GEOS code that executes on the computer side, and the first part is code that runs on the disk drive. The drive code does the actual disk authenticity check. The job of the computer part is to get the disk drive to run the drive code, and receive the result.</p>
  5459. <h4 id="running-the-drive-code">Running the Drive Code</h4>
  5460. <p>GEOS comes with driver code for the 1541 and 1571 disk drives, which contains logic to upload code to the drive, execute it, and send commands, status messages and block data back and forth. The protection code reuses the driver to execute custom code and retrieve the result.</p>
  5461. <p>But the disk drivers don&rsquo;t export this functionality as an API. Instead of adding this to the disk drivers as a private API, the authors of the protection chose to do some hacky stuff to get to these functions instead. This has the side effect of making it much harder to understand what is going on – which is a plus for protection code.</p>
  5462. <p>The computer part needs to call the private functions <code>sendExecuteWithTrkSec</code> and <code>getDOSError</code> in the driver. This is some code in the 1541 driver that calls both functions in sequence:</p>
  5463. <pre><code>__NewDisk:  
  5464.        jsr     EnterTurbo  
  5465.        bnex    NewDsk2  
  5466.        jsr     ClearCache  
  5467.        jsr     InitForIO  
  5468.        LoadB   errCount, 0  
  5469. NewDsk0:  
  5470.        lda     #&gt;Drv_NewDisk  
  5471.        sta     $8C  
  5472.        lda     #&lt;Drv_NewDisk  
  5473.        sta     $8B  
  5474.        jsr     SendExecuteWithTrkSec   ; &lt;------  
  5475.        jsr     GetDOSError             ; &lt;------  
  5476.        beq     NewDsk1
  5477. </code></pre>
  5478. <p>The protection code scans the <code>__NewDisk</code> function for the <code>STA $8B</code> and steals the following two instructions.</p>
  5479. <p>It gets the pointer to the the function by looking at the GEOS KERNAL&rsquo;s API jump table entry for the symbol <code>NewDisk</code>:</p>
  5480. <pre><code>start:  lda     NewDisk+2               ; find the code that is pointed  
  5481.        sta     r0H                     ; to by the NewDisk API  
  5482.        lda     NewDisk+1               ; by reading the operand of the  
  5483.        sta     r0L                     ; direct/indirect JMP at the  
  5484.        lda     NewDisk                 ; entry point  
  5485.        cmp     #$4C                    ; direct or indirect JMP?  
  5486.        beq     @direct                 ; direct, then we found the code
  5487. </code></pre>
  5488. <p>If the KERNAL jumps directly to the driver code (opcode $4C), the two bytes after the API&rsquo;s address point to the implementation. If it&rsquo;s an indirect jump, it resolves the indirection:</p>
  5489. <pre><code>        ldy     #0                      ; indirect jump, so  
  5490.        lda     (r0),y                  ; we need to read the vector  
  5491.        tax  
  5492.        iny  
  5493.        lda     (r0),y  
  5494.        sta     r0H                     ; and we have a pointer to the code  
  5495.        stx     r0L
  5496. </code></pre>
  5497. <p>The 1541 and 1571 drivers differ slightly, so it checks which type of driver is running:</p>
  5498. <pre><code>@direct:  
  5499.        ldx     #STRUCT_MISMAT          ; default error code:  
  5500.        ldy     curDrive  
  5501.        lda     driveType-8,y           ; what kind of drive is this?  
  5502.        and     #$BF                    ; ignore the shadow bit (drive cache)  
  5503.        cmp     #$02                    ; 2: 1571  
  5504.        bcc     @is1541                 ; less, then 1541!  
  5505.        bne     @rts                    ; not 1571, then return with error (X != 0)
  5506. </code></pre>
  5507. <p>Let&rsquo;s look at the 1541 code. (The 1571 is be similar.)</p>
  5508. <pre><code>@is1541:  
  5509.        ldy     #$FF  
  5510. @loop5: iny  
  5511. @loop6: lda     (r0),y                  ; search for $85 $8B  
  5512.        cmp     #$85                    ; (STA $8B)  
  5513.        bne     @loop5                  ; in NewDisk code  
  5514.        iny  
  5515.        lda     (r0),y  
  5516.        cmp     #$8B  
  5517.        bne     @loop6
  5518. </code></pre>
  5519. <p>This looks for the <code>STA $8B</code> just before the two calls. It then appends the next two calls to the end of its own code:</p>
  5520. <pre><code>        iny
  5521.  
  5522. @cont:  ldx     #$00  
  5523. @loop7: lda     (r0),y                  ; extract 6 bytes  
  5524.        sta     @code,x                 ; copy into this code  
  5525.        iny  
  5526.        inx  
  5527.        cpx     #6  
  5528.        bne     @loop7
  5529. </code></pre>
  5530. <p>Finally, it enables the disk driver, and calls the two functions it extracted the pointers of to have the driver execute code at <code>checkProtection</code> in its own RAM and retrieve the status.</p>
  5531. <pre><code>        jsr     EnterTurbo  
  5532.        jsr     InitForIO
  5533.  
  5534.        lda     #&lt;checkProtection       ; $0705 ptr in 1541 RAM  
  5535.        sta     $8B                     ; to execute  
  5536.        ldx     #&gt;checkProtection       ; (this sector is at $0700!)  
  5537.        stx     $8C  
  5538. @code:  .byte   0,0,0                   ; jsr SendExecuteWithTrkSec  
  5539.        .byte   0,0,0                   ; jsr GetDOSError  
  5540.        jsr     DoneWithIO  
  5541. @rts:   rts
  5542. </code></pre>
  5543. <p><code>checkProtection</code> is actually $0705, and points into the drive&rsquo;s buffer at $0700-$07FF, which is where the block was read. So the drive code does not have to be uploaded from the computer – it was the last block read by the driver, and it guaranteed to be located at $0700.</p>
  5544. <h4 id="checking-for-the-gap-sequence">Checking for the Gap Sequence</h4>
  5545. <p>Tracks on a 1541-formatted disk contain the following sequence of structures for 17 to 21 sectors, depending on the track.</p>
  5546. <p><img src="docs/geosdiskerrors/1.png" height="64" width="600" alt="" /></p>
  5547. <p>A SYNC mark is followed by a sector header, and after a gap, there is another SYNC mark, followed by the sector&rsquo;s data and another gap. This is repeated for the next sector.</p>
  5548. <p>The GEOS copy protection relies on the fact that the data in the gap, which is irrelevant for normal operation, cannot be reliably written to by stock drives. GEOS boot and application disks contain the following sequence of bytes there:</p>
  5549. <pre><code>55 55 55 67 55 55 55 67
  5550. </code></pre>
  5551. <p>The purpose of the drive code is to test that the gap after both the header and the sector data contain only the values 0x55 and 0x67 for 16 consecutive sectors.</p>
  5552. <p>Before we look at the main program, let&rsquo;s look at its two helpers. This is <code>skipBytes</code>. It just reads a certain number of bytes from disk and ignores them.</p>
  5553. <pre><code>skipBytes:  
  5554.        bvc     skipBytes               ; wait for byte  
  5555.        clv  
  5556.        lda     $1C01                   ; read it  
  5557.        dey  
  5558.        bne     skipBytes               ; y times  
  5559.        rts
  5560. </code></pre>
  5561. <p>And this is <code>checkSignature</code>, which reads all bytes up to the next sync mark and makes sure that they are all either 0x55 or 0x67:</p>
  5562. <pre><code>checkSignature:  
  5563.        ldy     $1C00                   ; if we found the SYNC mark,  
  5564.        bpl     @foundSync              ; the check is ok and we're done
  5565.  
  5566.        bvc     checkSignature          ; wait until byte ready  
  5567.        clv
  5568.  
  5569.        lda     $1C01                   ; get byte  
  5570.        cmp     #$55  
  5571.        beq     checkSignature          ; has to be either signature byte $55  
  5572.        cmp     #$67  
  5573.        beq     checkSignature          ; or signature byte $67
  5574.  
  5575.        pla                             ; magic not found  
  5576.        pla                             ; -&gt; return to main code  
  5577.        rts                             ; (error code remains at "2")
  5578.  
  5579. @foundSync:  
  5580.        lda     $1C01                   ; read value  
  5581.        clv  
  5582.        rts
  5583. </code></pre>
  5584. <p>If this enounters a gap byte other than 0x55 of 0x67, it pops the return address from the stack, which returns to the drive&rsquo;s main code with an error.</p>
  5585. <p>The main code starts out with waiting until the read head passes the header of sector 0 of the current track and reading the header. It calls a function in the DOS ROM ($F510) for this:</p>
  5586. <pre><code>        lda     #&gt;(buffer-$8000+$0700)  
  5587.        sta     $33  
  5588.        lda     #&lt;(buffer-$8000+$0700)  
  5589.        sta     $32                     ; set pointer to track/sector for ROM call  
  5590.        lda     $22                     ; current track number  
  5591.        sta     $07F5                   ; (sector is 0)  
  5592.        jsr     $F510                   ; ROM call: find and read block header
  5593. </code></pre>
  5594. <p>There are two more bytes that are part of the header that haven&rsquo;t been read yet (the &ldquo;OFF&rdquo; bytes), which have to be skipped:</p>
  5595. <pre><code>        ldy     #2  
  5596.        sty     $00                     ; set default error code 2: "READ ERROR"  
  5597.        jsr     skipBytes               ; skip 2 bytes
  5598. </code></pre>
  5599. <p>The next bytes to be read are now the sector header&rsquo;s gap bytes.</p>
  5600. <p>The remainder of the code now iterates over 16 sectors, always skipping all header bytes and data bytes, and checking for 0x55 and 0x67 values in the gaps:</p>
  5601. <pre><code>        ldx     #16  
  5602.        bne     @1                      ; check 16 headers and sectors
  5603.  
  5604. @loop:  ldy     #&lt;$100                  ; skip a total of  
  5605.        jsr     skipBytes               ; 325 GCR bytes  
  5606.        ldy     #$45                    ; = 260 data bytes  
  5607.        jsr     skipBytes               ; = marker + full block + checksum + filler  
  5608.        jsr     checkSignature          ; check signature after data block  
  5609.        ldy     #10  
  5610.        jsr     skipBytes               ; skip full header  
  5611. @1:     jsr     checkSignature          ; check signature after header  
  5612.        dex  
  5613.        bne     @loop                   ; repeat  
  5614.        inx  
  5615.        stx     $00                     ; set error code 1: "OK"  
  5616.        rts
  5617. </code></pre>
  5618. <p>If <code>checkSignature</code> never failed, a status code indicating success will be set, which the computer part of the protection code will fetch.</p>
  5619. <h3 id="encrypted-code-in-record-0">Encrypted Code in Record 0</h3>
  5620. <p>To make cracking the protection harder, there is one more component: Somewhere in the initialization code, record 1 checksums itself, and uses this checksum as a key to decrypt one function in record 0.  So if someone was to crack the protection by changing any of the code in record 1, the checksum would be different, the one function in record 0 would be garbled, and the app would sooner or later crash.</p>
  5621. <p>First, the checksum code:</p>
  5622. <pre><code>decryptR00:  
  5623.        LoadW   r0, MEM_OVERLAY         ; checksum code record #1  
  5624.        LoadW   r1, CODE1_END-CODE1  
  5625.        LoadB   r2L, 0  
  5626.        ldy     #0  
  5627. @loop1: lda     (r0),y  
  5628.        add     r2L  
  5629.        sta     r2L  
  5630.        IncW    r0  
  5631.        ldx     #r1  
  5632.        jsr     Ddec  
  5633.        bne     @loop1
  5634. </code></pre>
  5635. <p>It adds all bytes together and keeps the lowest 8 bits.</p>
  5636. <p>There are several bytes within the record 1 code that must not be part of the checksum though: The 2 bytes containing the serial number will change once the app is installed, and the value will be different on every user&rsquo;s copy. So their values will be subtracted from the checksum again:</p>
  5637. <pre><code>        lda     r2L                     ; remove variable bytes from checksum  
  5638.        sub     serial  
  5639.        sub     serial+1
  5640. </code></pre>
  5641. <p>Furthermore, the stamped-in track/sector pointers of the block with the signature check code and the block with the serial have to be excluded. This is because of the necessary order in the build process, which looks something like this:</p>
  5642. <ol>
  5643. <li>assemble the source of each record as well as the drive code block</li>
  5644. <li>encrypt record 1 with a constant of $DE</li>
  5645. <li>encrypt one function in record 0 with the checksum of the record 1 plaintext</li>
  5646. <li>write the whole geoWrite VLIR file to a disk image</li>
  5647. <li>write the drive code block to a free block on the disk image</li>
  5648. <li>stamp the track and sector of the drive code block into record 1 on the disk image</li>
  5649. <li>stamp the track and sector of the block that contains the serial into record 1 on the disk image</li>
  5650. </ol>
  5651. <p>Both track/sector pointers aren&rsquo;t known until step 6 and 7, but they are part of the checksum in step 3. Therefore, they are also excluded:</p>
  5652. <pre><code>        sub     protExecTrack  
  5653.        sub     protExecSector  
  5654.        sub     protSerialTrack  
  5655.        sub     protSerialSector
  5656. </code></pre>
  5657. <p>Now it can decrypt the one function in record 0:</p>
  5658. <pre><code>        LoadW   r0, r0_encrypted_start ; decrypt some code in record 0  
  5659.        LoadW   r1, r0_encrypted_start-r0_encrypted_end  
  5660.        ldy     #0  
  5661. @loop2: lda     (r0),y  
  5662.        eor     r2L  
  5663.        sta     (r0),y  
  5664.        IncW    r0  
  5665.        IncW    r1  
  5666.        bne     @loop2  
  5667.        rts
  5668. </code></pre>
  5669. <h2 id="discussion">Discussion</h2>
  5670. <p>While the geoWrite copy protection isn&rsquo;t as complicated or quite as <a href="https://www.pagetable.com/?p=865">mean</a> as the one on the GEOS system disks, it is nevertheless effective, and requires quite some effort to be cracked.</p>
  5671. <p>Without disassembling through the geoWrite binary, a cracker could search the whole disk for code that looks like it&rsquo;s checking for the protection. Any code running on the disk and reading bytes from the head manually is a candidate. This is easy to find by looking for <code>LDA $1C01</code>, which would reveal the block with the gap signature check. But it&rsquo;s checksummed, and the cracker wouldn&rsquo;t know the algorithm, the range or the location of the checksum unless they had disassembled record 1. Besides, they might change the wrong block by mistake because of the decoy copies of this block on the disk.</p>
  5672. <p>So any cracking attempt would require disassembling through the geoWrite code. It is quite straightforward to find the code to load and decrypt record 1, and record 1 can either be decrypted with a small script using the key found in the code, or by dumping the memory contents after decryption.</p>
  5673. <p>The first few bytes of record 1 are a simple but clever obfuscation with the chance that the cracker would miss the serial check and installation code. If they do find it, they now know the track and sector of the serial on disk, and the encryption key, so they could change the serial of an installed copy, or deinstall geoWrite on the original disk.</p>
  5674. <p>The holy grail would be a cracked version of geoWrite that didn&rsquo;t care about the serial, so a cracker could just remove the call to <code>checkSerialOrInstall</code>. But this would alter the checksum of record 1, and break the decryption of the one function in record 0, so the app would sooner or later crash. So removing the call to <code>checkSerialOrInstall</code> would also require patching the decryption to take a fixed key instead.</p>
  5675. <h2 id="references">References</h2>
  5676. <ul>
  5677. <li>ZAK256: <a href="https://www.c64-wiki.de/wiki/GEOS-Kopierschutz">GEOS-Kopierschutz</a> (C64 Wiki)</li>
  5678. <li>Michael Steil: <a href="https://www.pagetable.com/?p=865">Copy Protection Traps in GEOS for C64</a></li>
  5679. <li>Michael Steil: <a href="https://www.pagetable.com/?p=1118">Why Do C64 GEOS Boot Disks Break?</a></li>
  5680. <li>Michael Steil: <a href="https://www.pagetable.com/?p=1140">Reconstructing the GEOS 2.0 (de) Master Images from a Pile of Broken Disks</a></li>
  5681. </ul>
  5682. ]]></content:encoded>
  5683. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1449</wfw:commentRss>
  5684. <slash:comments>3</slash:comments>
  5685. </item>
  5686. <item>
  5687. <title>Inside geoWrite – 4: Zero Page</title>
  5688. <link>https://www.pagetable.com/?p=1442</link>
  5689. <comments>https://www.pagetable.com/?p=1442#comments</comments>
  5690. <pubDate>Fri, 04 Sep 2020 20:25:55 +0000</pubDate>
  5691. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  5692. <category><![CDATA[6502]]></category>
  5693. <category><![CDATA[Archeology]]></category>
  5694. <category><![CDATA[C64]]></category>
  5695. <category><![CDATA[Commodore]]></category>
  5696. <category><![CDATA[GEOS]]></category>
  5697. <category><![CDATA[Operating Systems]]></category>
  5698.  
  5699. <guid isPermaLink="false">https://www.pagetable.com/?p=1442</guid>
  5700. <description><![CDATA[In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses how it makes maximum use of the scarce zero page space. Article Series The Overlay System Screen Recovery Font Management Zero Page ← this article Copy Protection Localization File Format and Pagination Copy &#38; Paste Keyboard Handling ... <a title="Inside geoWrite – 4: Zero Page" class="read-more" href="https://www.pagetable.com/?p=1442">Read more<span class="screen-reader-text">Inside geoWrite – 4: Zero Page</span></a>]]></description>
  5701. <content:encoded><![CDATA[<p>In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses how it makes maximum use of the scarce zero page space.</p>
  5702. <h2 id="article-series">Article Series</h2>
  5703. <ol>
  5704. <li><a href="https://www.pagetable.com/?p=1425">The Overlay System</a></li>
  5705. <li><a href="https://www.pagetable.com/?p=1428">Screen Recovery</a></li>
  5706. <li><a href="https://www.pagetable.com/?p=1436">Font Management</a></li>
  5707. <li><strong>Zero Page</strong> ← this article</li>
  5708. <li><a href="https://www.pagetable.com/?p=1449">Copy Protection</a></li>
  5709. <li><a href="https://www.pagetable.com/?p=1460">Localization</a></li>
  5710. <li><a href="https://www.pagetable.com/?p=1471">File Format and Pagination</a></li>
  5711. <li><a href="https://www.pagetable.com/?p=1481">Copy &amp; Paste</a></li>
  5712. <li><a href="https://www.pagetable.com/?p=1490">Keyboard Handling</a></li>
  5713. </ol>
  5714. <h2 id="geos-zero-page">GEOS Zero Page</h2>
  5715. <p>The MOS 6502 CPU has special encodings for addresses that fit in 8 bits: Instructions that read from or write to addresses $0000 to $00FF in memory are encoded in two instead of three bytes:</p>
  5716. <pre><code>a5 28      lda $28  
  5717. ad 28 00   lda $0028              
  5718. </code></pre>
  5719. <p>The two instructions have the same effect, but the first one is one byte shorter, and faster by one clock cycle.</p>
  5720. <p>Zero page space is scare and valuable, so it has to be used wisely. This is the GEOS zero page layout, roughly to scale:</p>
  5721. <pre><code>-----------------------------------------  
  5722. $0000  6510 CPU built-in I/O port  
  5723. -----------------------------------------  
  5724. $0002  Virtual 16 bit registers  
  5725.       r0-r15
  5726.  
  5727. -----------------------------------------  
  5728. $0022  Used by GEOS system
  5729.  
  5730.  
  5731. -----------------------------------------  
  5732. $0040  Reserved for GEOS system
  5733.  
  5734.  
  5735.  
  5736.  
  5737. -----------------------------------------  
  5738. $0070  GEOS app space            &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;  
  5739. -----------------------------------------  
  5740. $0080  Used by GEOS disk driver  
  5741. -----------------------------------------  
  5742. $0090  Used by Commodore KERNAL ROM
  5743.  
  5744.  
  5745.  
  5746.  
  5747.  
  5748.  
  5749.  
  5750.  
  5751.  
  5752.  
  5753.  
  5754.  
  5755. -----------------------------------------  
  5756. $00FB  GEOS app space            &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;  
  5757. -----------------------------------------
  5758. </code></pre>
  5759. <p>GEOS designates only a total of 21 bytes to the application. 30 bytes are used by the GEOS KERNAL itself, and 48 bytes are reserved for future versions of GEOS, so these areas are off-limits. The biggest part, 107 bytes, is used by the Commodore KERNAL ROM.</p>
  5760. <h2 id="kernal-zero-page">KERNAL Zero Page</h2>
  5761. <p>The C64&rsquo;s ROM consists of the 9 KB Microsoft BASIC interpreter and a 7 KB operating system: the <a href="https://www.pagetable.com/?p=926">Commodore KERNAL</a>. When the machine is in BASIC mode, the KERNAL ROM takes care of the keyboard, the screen, RS232, tape, disks and printers.</p>
  5762. <p>For the most part, GEOS does not use the KERNAL ROM at all: It comes with its own keyboard, screen and mouse drivers. With disk drives and printers, it&rsquo;s more complicated.</p>
  5763. <p>Disk drives and printers are daisy-chained on the <a href="https://www.pagetable.com/?p=1135">Commodore Serial Bus</a>. Unfortunately, byte transmission with the original protocol is painfully slow, which is why most applications and games come with their own speeder code which uploads alternative transfer code to the disk drive. GEOS also uses its own disk speeder called diskTurbo.</p>
  5764. <p>diskTurbo only replaces the data transmission protocol though, not the <a href="https://www.pagetable.com/?p=1031">IEEE-488 TALK/LISTEN protocol</a>, which is needed to negotiate which device is talking on the bus at which time. So whenever GEOS switches between disk drives, it calls the original code in KERNAL. And printers don&rsquo;t allow uploading code to replace the bus protocol at all, so GEOS uses the KERNAL for talking to the printer as well.</p>
  5765. <p>To keep the original KERNAL happy, GEOS doesn&rsquo;t touch any of its zero page variables – which is quite generous, since the serial code only touches a small part of the $0090-$00FA area.</p>
  5766. <p>In addition, GEOS reserves 16 more bytes for use by the diskTurbo driver at $0080-$008F. The Commodore 1541 driver uses two bytes in this space, for example.</p>
  5767. <p>So effectively, almost the whole upper half of the zero page ($0080-$00FA) is blocked because the code to access disks and printers uses parts of it.</p>
  5768. <h2 id="geowrite">geoWrite</h2>
  5769. <p>Since the $0080+ area is only used by the system during disk and printer accesses, geoWrite can use it whenever it is not using the disk or the printer, as long as it restores its contents whenever it does need to use them.</p>
  5770. <p>It does this by swapping the 128 bytes in the zero page with a dedicated buffer. The area can now have one of two sets of contents: the geoWrite contents and the diskTurbo/KERNAL contents:</p>
  5771. <pre><code>-----------------------------------------  
  5772. $0000  6510 CPU built-in I/O port  
  5773. -----------------------------------------  
  5774. $0002  Virtual 16 bit registers  
  5775.       r0-r15
  5776.  
  5777. -----------------------------------------  
  5778. $0022  Used by GEOS system
  5779.  
  5780.  
  5781. -----------------------------------------  
  5782. $0040  Reserved for GEOS system
  5783.  
  5784.  
  5785.  
  5786.  
  5787. -----------------------------------------  
  5788. $0070  geoWrite variables        &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;  
  5789. -----------------------------------------    -----------------------------------------  
  5790. $0080  geoWrite variables        &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;    $0080  Used by GEOS disk driver  
  5791.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;    -----------------------------------------  
  5792.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;    $0090  Used by Commodore KERNAL ROM  
  5793.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;  
  5794.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;  
  5795.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;  
  5796.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;  
  5797.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;  
  5798.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; &lt;-swapped-&gt;
  5799.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;  
  5800.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;  
  5801.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;  
  5802.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;  
  5803.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;  
  5804.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;  
  5805.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;    -----------------------------------------  
  5806.                                 &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;    $00FB  GEOS app space (unused)  
  5807. -----------------------------------------    -----------------------------------------
  5808. </code></pre>
  5809. <p>This is the code that swaps the zero page area and the buffer:</p>
  5810. <pre><code>swap_userzp:  
  5811.        php                             ; save all registers and flags  
  5812.        pha  
  5813.        txa  
  5814.        pha  
  5815.        tya  
  5816.        pha  
  5817.        ldx     #$7F                    ; $7F..$00  
  5818. @loop:  ldy     userzp,x                ; load zp byte  
  5819.        lda     userzp_copy,x           ; load buffer byte  
  5820.        sta     userzp,x                ; store zp byte  
  5821.        tya  
  5822.        sta     userzp_copy,x           ; store buffer byte  
  5823.        dex  
  5824.        bpl     @loop  
  5825.        pla                             ; restore all registers and flags  
  5826.        tay  
  5827.        pla  
  5828.        tax  
  5829.        pla  
  5830.        plp  
  5831.        rts
  5832. </code></pre>
  5833. <p>It saves all registers and flags, so it can easily be called from anywhere in the code without messing up any state. Here is an example of using it:</p>
  5834. <pre><code>        LoadW   r0, otherFnBuffer       ; load argument  
  5835.        jsr     swap_userzp             ; **swap**  
  5836.        jsr     OpenRecordFile          ; call KERNAL disk API  
  5837.        jsr     swap_userzp             ; **swap**  
  5838.        lda     #2                      ; load argument  
  5839.        jmp     PointRecord             ; load KERNAL API that does not access disk
  5840. </code></pre>
  5841. <p>The <code>OpenRecordFile</code> API call accesses disk, so it&rsquo;s surrounded by calls to <code>swap_userzp</code>. The geoWrite programmers were very aware of which API calls cause a disk access: The <code>PointRecord</code> API call is about file management as well, but it only updates data structures and does not access disk, so there is no need to swap the zero page.</p>
  5842. <p>For all of this to be correct</p>
  5843. <ul>
  5844. <li><code>swap_userzp</code> has to be called as the very first thing when the application launches.</li>
  5845. <li><code>swap_userzp</code> has to be called before exiting the app.</li>
  5846. <li><code>swap_userzp</code> has to be called for every API that may end up calling the disk driver, as well as all printer APIs.</li>
  5847. <li>calls to <code>swap_userzp</code> always need to be balanced.</li>
  5848. <li>zero page variables at $80+ cannot be accessed between the two <code>swap_userzp</code> invocations.</li>
  5849. </ul>
  5850. <p>Adding the two calls to every disk API call is prone to error, and bloats the code, so there are wrapper functions for the commonly used disk access APIs:</p>
  5851. <pre><code>_ReadFile:  
  5852.        lda     #ReadFile-GetBlock  
  5853.        .byte   $2C                     ; skip next  
  5854. _ReadByte:  
  5855.        lda     #ReadByte-GetBlock  
  5856.        .byte   $2C  
  5857. _CloseRecordFile:  
  5858.        lda     #CloseRecordFile-GetBlock  
  5859.        .byte   $2C  
  5860. _InsertRecord:  
  5861.        lda     #InsertRecord-GetBlock  
  5862.        .byte   $2C  
  5863. _DeleteRecord:  
  5864.        lda     #DeleteRecord-GetBlock  
  5865.        .byte   $2C  
  5866. _AppendRecord:  
  5867.        lda     #AppendRecord-GetBlock  
  5868.        .byte   $2C  
  5869. _UpdateRecordFile:  
  5870.        lda     #UpdateRecordFile-GetBlock  
  5871.        .byte   $2C  
  5872. _OpenDisk:  
  5873.        lda     #OpenDisk-GetBlock  
  5874.        .byte   $2C  
  5875. _FindFile:  
  5876.        lda     #FindFile-GetBlock  
  5877.        .byte   $2C  
  5878. _GetBlock:  
  5879.        lda     #GetBlock-GetBlock  
  5880.        .byte   $2C  
  5881. _PutBlock:  
  5882.        lda     #PutBlock-GetBlock  
  5883.        add     #&lt;GetBlock  
  5884.        sta     @jmp+1  
  5885.        lda     #0  
  5886.        adc     #&gt;GetBlock  
  5887.        sta     @jmp+2  
  5888.        jsr     swap_userzp  
  5889. @jmp:   jsr     GetBlock  
  5890.        jmp     swap_userzp
  5891. </code></pre>
  5892. <p>Each of the wrappers loads the offset of the specific API entry point from the <code>GetBlock</code> entry point, and the common code adds it to the <code>GetBlock</code> address, and uses self-modification to call the API – of course calling <code>swap_userzp</code> before and after.</p>
  5893. <p>These wrappers are in the record 0 code, so that any <a href="https://www.pagetable.com/?p=1425">overlay code</a> can call it as well.</p>
  5894. <h2 id="discussion">Discussion</h2>
  5895. <p>Like most of geoWrite&rsquo;s tricks, this is a tradeoff. It gains 123 zero page locations, and its use speeds up the code by maybe a low two-digit percentage and saves maybe 1 KB of code space. On a slow and memory-constrained system like the C64, this is significant. On the other hand, the disk access code gets a bit more complicated (which is countered by the wrappers), and every back-and-forth swap takes about 6000 cycles. But in the context of a disk access, this is negligible.</p>
  5896. ]]></content:encoded>
  5897. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1442</wfw:commentRss>
  5898. <slash:comments>2</slash:comments>
  5899. </item>
  5900. <item>
  5901. <title>Inside geoWrite – 3: Font Management</title>
  5902. <link>https://www.pagetable.com/?p=1436</link>
  5903. <comments>https://www.pagetable.com/?p=1436#respond</comments>
  5904. <pubDate>Thu, 03 Sep 2020 22:25:55 +0000</pubDate>
  5905. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  5906. <category><![CDATA[6502]]></category>
  5907. <category><![CDATA[Archeology]]></category>
  5908. <category><![CDATA[C64]]></category>
  5909. <category><![CDATA[Commodore]]></category>
  5910. <category><![CDATA[GEOS]]></category>
  5911. <category><![CDATA[Operating Systems]]></category>
  5912. <category><![CDATA[Uncategorized]]></category>
  5913.  
  5914. <guid isPermaLink="false">https://www.pagetable.com/?p=1436</guid>
  5915. <description><![CDATA[In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses the font manager&#8217;s system of caches for pixel fonts. Article Series The Overlay System Screen Recovery Font Management ← this article Zero Page Copy Protection Localization File Format and Pagination Copy &#38; Paste Keyboard Handling GEOS Fonts ... <a title="Inside geoWrite – 3: Font Management" class="read-more" href="https://www.pagetable.com/?p=1436">Read more<span class="screen-reader-text">Inside geoWrite – 3: Font Management</span></a>]]></description>
  5916. <content:encoded><![CDATA[<p>In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses the font manager&rsquo;s system of caches for pixel fonts.</p>
  5917. <p><img src="docs/geowrite/geowrite_3_fonts.gif" alt="" /></p>
  5918. <h2 id="article-series">Article Series</h2>
  5919. <ol>
  5920. <li><a href="https://www.pagetable.com/?p=1425">The Overlay System</a></li>
  5921. <li><a href="https://www.pagetable.com/?p=1428">Screen Recovery</a></li>
  5922. <li><strong>Font Management</strong> ← this article</li>
  5923. <li><a href="https://www.pagetable.com/?p=1442">Zero Page</a></li>
  5924. <li><a href="https://www.pagetable.com/?p=1449">Copy Protection</a></li>
  5925. <li><a href="https://www.pagetable.com/?p=1460">Localization</a></li>
  5926. <li><a href="https://www.pagetable.com/?p=1471">File Format and Pagination</a></li>
  5927. <li><a href="https://www.pagetable.com/?p=1481">Copy &amp; Paste</a></li>
  5928. <li><a href="https://www.pagetable.com/?p=1490">Keyboard Handling</a></li>
  5929. </ol>
  5930. <h2 id="geos-fonts-overview">GEOS Fonts Overview</h2>
  5931. <p>The GEOS operating system contains a rendering library for pixel fonts of up to 63 pt, using its own font file format.</p>
  5932. <p>Like most GEOS files, fonts are <a href="https://www.pagetable.com/?p=1425#vlir-files">VLIR bundles</a> that contain one &ldquo;sub-file&rdquo; for every point size. This is &ldquo;California&rdquo;, which is available at 10, 12, 14 and 18 pt:</p>
  5933. <pre><code>California         size  
  5934. \--- File Header    256  
  5935. \--- 10             892  
  5936. \--- 12            1114  
  5937. \--- 14            1322  
  5938. \--- 18            2110
  5939. </code></pre>
  5940. <p>To use a font, an application has to explicitly load it into its own memory buffer and activate it using the <code>LoadCharSet</code> API:</p>
  5941. <pre><code>    LoadW   r0, otherFnBuffer  
  5942.    jsr     OpenRecordFile          ; open font file  
  5943.    lda     #12  
  5944.    jsr     PointRecord             ; select 12 pt font  
  5945.    LoadW   r7, FONT_BUFFER  
  5946.    LoadW   r2, FONT_BUFFER_SIZE  
  5947.    jsr     ReadRecord              ; read pixel font into memory  
  5948.    jsr     CloseRecordFile         ; close font file  
  5949.    LoadW   r0, FONT_BUFFER  
  5950.    jsr     LoadCharSet             ; activate font
  5951. </code></pre>
  5952. <p>The 9 pt system font (called &ldquo;BSW&rdquo;) is always in memory and can be activated using <code>UseSystemFont</code>:</p>
  5953. <pre><code>jsr     UseSystemFont
  5954. </code></pre>
  5955. <p>As soon as a font is activated, it can be used for drawing text:</p>
  5956. <ul>
  5957. <li><code>Putchar</code> – draw a character</li>
  5958. <li><code>PutString</code> – draw a zero-terminated string of characters</li>
  5959. </ul>
  5960. <p>Font metrics are accessible through:</p>
  5961. <ul>
  5962. <li><code>curHeight</code> (global variable) – font height (in pixels)</li>
  5963. <li><code>baselineOffset</code> (global variable) – baseline offset (in pixels from top)</li>
  5964. <li><code>GetRealSize</code> – query width and height of a given character code</li>
  5965. </ul>
  5966. <p>All this API is very basic. The GEOS KERNAL does not help the application with:</p>
  5967. <ul>
  5968. <li>enumerating available fonts and sizes: the application has to find font files on disk and decode their metadata.</li>
  5969. <li>dynamically caching several fonts in memory: GEOS only knows about a single font at a time.</li>
  5970. <li>getting font metrics without loading the font data: the GEOS API for getting the metrics requires the font to be loaded and active.</li>
  5971. </ul>
  5972. <p>geoWrite implements all this on the application side.</p>
  5973. <h2 id="enumerating-fonts">Enumerating Fonts</h2>
  5974. <p>geoWrite&rsquo;s &ldquo;font&rdquo; menu shows all available fonts and their point sizes:</p>
  5975. <p><img src="docs/geowrite/font_menu.png" alt="" /></p>
  5976. <p>To get this information, applications have to find font files on disk and extract it from their metadata.</p>
  5977. <p>A font file has a file type of <code>FONT</code>, and its file name is also the font&rsquo;s name, so that&rsquo;s what the application will show in the UI.</p>
  5978. <p>The API <code>FindFTypes</code> returns an array of file names matching a filename or type, so this is how geoWrite gets the (file) names of the fonts on disk:</p>
  5979. <pre><code>    LoadW   r6, fontNames  
  5980.    LoadB   r7L, FONT               ; file type  
  5981.    LoadB   r7H, MAX_FONT_FILES  
  5982.    LoadW   r10, 0                  ; no name filter  
  5983.    jsr     FindFTypes              ; get font files  
  5984.    lda     #8  
  5985.    sub     r7H                     ; number of files found  
  5986.    sta     numFontFiles
  5987. </code></pre>
  5988. <p>(All code has been edited for readability.)</p>
  5989. <p>geoWrite also needs to get the available point sizes for each font, as well as the start track and sector of the data on disk and its size. This way, it can later load the data for a particular point size without having to read any extra metadata again.</p>
  5990. <p>In C notation, the data structure that it builds looks like this:</p>
  5991. <pre><code>struct {  
  5992.    uint16_t font_id;  
  5993.    uint16_t record_size;  
  5994.    uint16_t start_ts;  
  5995. } disk_fonts[16][8];
  5996. </code></pre>
  5997. <p>For each of the (up to) 8 font files, there are (up to) 16 point sizes, for each of which geoWrite collects the font ID, the record size and the start track and sector.</p>
  5998. <p>A font ID is a GEOS concept that allows applications to use numbers instead of font name strings. It is a 16 bit value that uniquely identifies the font and point size:</p>
  5999. <ul>
  6000. <li>The upper 10 bits are a unique ID assigned by Berkeley Softworks, e.g. 3 is a synonym &ldquo;California&rdquo;.</li>
  6001. <li>The lower 6 bits are the point size (0-63).</li>
  6002. </ul>
  6003. <p>This is the main function to extract the metadata of a font:</p>
  6004. <pre><code>;---------------------------------------------------------------  
  6005. ; extractFontMetadata  
  6006. ;
  6007. ; Function:  Read point sizes, track/sector pointers and data  
  6008. ;            sizes for a font file from its file header  
  6009. ;
  6010. ; Pass:      a   font index (0-7)  
  6011. ;            r0  font filename  
  6012. ;---------------------------------------------------------------  
  6013. extractFontMetadata:  
  6014.        pha  
  6015.        MoveW   r0, r6  
  6016.        jsr     FindFile                ; get file  
  6017.        LoadW   r9, dirEntryBuf  
  6018.        jsr     GetFHdrInfo             ; read file header  
  6019.        MoveW   dirEntryBuf+OFF_DE_TR_SC, r1  
  6020.        jsr     ldR4DiskBlkBuf  
  6021.        jsr     GetBlock                ; read index block  
  6022.        pla                             ; font index (0-7)  
  6023.        asl     a  
  6024.        asl     a  
  6025.        asl     a  
  6026.        asl     a                       ; * 16  
  6027.        tay  
  6028.        ldx     #0  
  6029. @loop:  jsr     extractFontIdTrackSector  
  6030.        jsr     extractFontRecordSize  
  6031.        iny  
  6032.        iny  
  6033.        inx  
  6034.        inx  
  6035.        cpx     #FONTS_PER_FONTFILE  
  6036.        bne     @loop  
  6037.        rts
  6038. </code></pre>
  6039. <p>It opens each font file and reads its file header and index block. For every point size, it calls <code>extractFontIdTrackSector</code> and <code>extractFontRecordSize</code>.</p>
  6040. <p>Here is <code>extractFontIdTrackSector</code>:</p>
  6041. <pre><code>;---------------------------------------------------------------  
  6042. ; extractFontIdTrackSector  
  6043. ;
  6044. ; Function:  Copy a point size ID and its track/sector pointer  
  6045. ;            from the font file header and the index block  
  6046. ;            into the app's data structures.  
  6047. ;
  6048. ; Pass:      x   font index within font file (0-15)  
  6049. ;            y   fontfile * 16 + fontindex * 2  
  6050. ;---------------------------------------------------------------  
  6051. extractFontIdTrackSector:  
  6052.        lda     fileHeader+OFF_GHPOINT_SIZES,x  
  6053.        sta     diskFontIds,y  
  6054.        and     #FONT_SIZE_MASK  
  6055.        sta     r6L                     ; point size  
  6056.        lda     fileHeader+OFF_GHPOINT_SIZES+1,x  
  6057.        sta     diskFontIds+1,y  
  6058.        ora     diskFontIds,y  
  6059.        beq     @rts                    ; skip empty records  
  6060.        txa  
  6061.        pha  
  6062.        lda     r6L                     ; point size  
  6063.        asl     a  
  6064.        tax  
  6065.        lda     diskBlkBuf+2,x          ; track  
  6066.        sta     diskFontRecordTrackSector,y  
  6067.        lda     diskBlkBuf+3,x          ; sector  
  6068.        sta     diskFontRecordTrackSector+1,y  
  6069.        pla  
  6070.        tax  
  6071. @rts:   rts
  6072. </code></pre>
  6073. <p>A font&rsquo;s file header contains 16 words at offset OFF_GHPOINT_SIZES that contain font IDs of the different point sizes, which this code copies into its internal data structure. It takes the start track and sectors for each point size from the VLIR index sector.</p>
  6074. <p>And this is <code>extractFontRecordSize</code>:</p>
  6075. <pre><code>;---------------------------------------------------------------  
  6076. ; extractFontRecordSize  
  6077. ;
  6078. ; Function:  Copy a font's data size from the font file header  
  6079. ;            into the app's data structures.  
  6080. ;
  6081. ; Pass:      x   font index within file (0-15)  
  6082. ;            y   fontfile * 16 + fontindex * 2  
  6083. ;---------------------------------------------------------------  
  6084. extractFontRecordSize:  
  6085.        lda     fileHeader+OFF_GHSET_LENGTHS,x  
  6086.        sta     diskFontRecordSize,y  
  6087.        sta     r2L  
  6088.        lda     fileHeader+OFF_GHSET_LENGTHS+1,x  
  6089.        sta     diskFontRecordSize+1,y  
  6090.        sta     r2H
  6091.  
  6092.        CmpWI   r2, MEM_SIZE_FONTS      ; data size too big?  
  6093.        bcc     @rts  
  6094.        beq     @rts  
  6095.        lda     #0  
  6096.        sta     diskFontRecordTrackSector,y ; then pretend it doesn't exist
  6097.  
  6098. @rts:   rts
  6099. </code></pre>
  6100. <p>Similarly, it extracts the data size for each point size.</p>
  6101. <h2 id="caching-font-data">Caching Font Data</h2>
  6102. <p>geoWrite can keep up to 8 fonts in memory at the same time and dynamically allocates space for fonts in a 7000 byte buffer.</p>
  6103. <p>Fonts are managed with an LRU strategy, meaning that if a new font is supposed to be loaded that wouldn&rsquo;t fit, the least recently used font will be removed from memory.</p>
  6104. <p>The font buffer contains one font immediately after the other: If a font is removed, fonts at higher addresses are moved down to fill the hole. This way, the free space is always at the end and there is no fragmentation.</p>
  6105. <p>These are the data structures in C notation:</p>
  6106. <pre><code>uint8_t buffer[7000];  
  6107. struct {  
  6108.    uint16_t font_id;  
  6109.    uint16_t data_ptr;  
  6110.    uint16_t data_size;  
  6111.    uint16_t lru;  
  6112. } loaded_fonts[8];
  6113. </code></pre>
  6114. <p>For every loaded font, geoWrite keeps track of its font ID, the pointer to the data in the buffer, the size in the buffer, and its LRU ID.</p>
  6115. <p>The main API of the font library is the call <code>setFontFromFile</code>, which allows the application to ask the library to activate a font given its ID. If it&rsquo;s not already in memory, it will be loaded into the buffer, and if necessary, one or more previously used fonts will be removed from memory.</p>
  6116. <p>This is the first part of the function:</p>
  6117. <pre><code>;---------------------------------------------------------------  
  6118. ; setFontFromFile  
  6119. ;
  6120. ; Function:  Set font. If necessary, load from disk and cache it.  
  6121. ;
  6122. ; Pass:      r1  font ID  
  6123. ;
  6124. ; Return:    c   =0: success  
  6125. ;                =1: fail, system font was loaded instead  
  6126. ;---------------------------------------------------------------  
  6127. setFontFromFile:  
  6128.        CmpW    r1, curFont  
  6129.        bne     @find  
  6130.        clc  
  6131.        rts
  6132.  
  6133. @find:  jsr     findLoadedFont          ; is it already loaded?  
  6134.        bcs     @load                   ; not found  
  6135.        jsr     updateloadedFontLruId   ; mark it as the latest one that was used  
  6136.        lda     loadedFontPtrsHi,x  
  6137.        sta     r0H  
  6138.        lda     loadedFontPtrsLo,x  
  6139.        sta     r0L  
  6140.        jsr     LoadCharSet             ; switch to it  
  6141.        MoveW   r1, curFont  
  6142.        clc  
  6143.        rts
  6144. </code></pre>
  6145. <p>If the requested font is the currently active font, the function does nothing. Otherwise, it checks whether the font is already loaded into memory, and if yes, it just activates it and returns.</p>
  6146. <p>This is the implementation of <code>findLoadedFont</code>:</p>
  6147. <pre><code>;---------------------------------------------------------------  
  6148. ; findLoadedFont  
  6149. ;
  6150. ; Function:  Search for font in font buffer.  
  6151. ;
  6152. ; Pass:      r1  font ID  
  6153. ;
  6154. ; Return:    c   =0: found  
  6155. ;                    x   index  
  6156. ;---------------------------------------------------------------  
  6157. findLoadedFont:  
  6158.        ldx     #0  
  6159. @loop:  cpx     loadedFontsCount  
  6160.        beq     @notfound  
  6161.        lda     loadedFontIdsLo,x  
  6162.        cmp     r1L  
  6163.        bne     @1  
  6164.        lda     loadedFontIdsHi,x  
  6165.        cmp     r1H  
  6166.        beq     @found  
  6167. @1:     inx  
  6168.        bra     @loop
  6169.  
  6170. @notfound:  
  6171.        sec                             ; failure  
  6172.        rts  
  6173. @found:  
  6174.        clc                             ; success  
  6175.        rts
  6176. </code></pre>
  6177. <p>It scans the array of loaded font IDs. If the ID is found, the index to be used with the data structures is returned in X.</p>
  6178. <p>If the font ID is not currently loaded into memory, <code>setFontFromFile</code> will load it:</p>
  6179. <pre><code>;---------------------------------------------------------------  
  6180. ; setFontFromFile  
  6181. ; (continued)  
  6182. ;---------------------------------------------------------------  
  6183. @load:  jsr     findFontIdOnDisk        ; does the font exist on disk?  
  6184.        bcs     useSystemFont           ; no, quietly use system font instead  
  6185.        lda     diskFontRecordTrackSector,x ; does point size exist?  
  6186.        beq     useSystemFont           ; no, quietly use system font instead  
  6187.        txa  
  6188.        pha  
  6189.        lda     diskFontRecordSize,x    ; r3 = size of font data  
  6190.        sta     r3L  
  6191.        lda     diskFontRecordSize+1,x  
  6192.        sta     r3H  
  6193.        jsr     allocateFontBufferSpace ; kick out least recently used font(s) if needed  
  6194.        jsr     updateloadedFontLruId   ; mark it as the latest one that was used  
  6195.        lda     r1L  
  6196.        sta     loadedFontIdsLo,x       ; save the ID in the table so the font  
  6197.        lda     r1H                     ; can be found in RAM again  
  6198.        sta     loadedFontIdsHi,x  
  6199.        lda     loadedFontPtrsHi,x      ; r7 = allocated location in RAM  
  6200.        sta     r7H  
  6201.        lda     loadedFontPtrsLo,x  
  6202.        sta     r7L  
  6203.        pla  
  6204.        tax  
  6205.        PushW   r1                      ; save ID  
  6206.        PushW   r7                      ; save RAM location  
  6207.        lda     diskFontRecordTrackSector,x; location on disk  
  6208.        sta     r1L  
  6209.        lda     diskFontRecordTrackSector+1,x  
  6210.        sta     r1H  
  6211.        LoadW   r2, MEM_SIZE_FONTS      ; maximum file size  
  6212.        jsr     setDevice  
  6213.        jsr     ReadFile                ; load font data into font buffer  
  6214.        PopW    r0                      ; read RAM location into r0  
  6215.        PopW    curFont                 ; read ID into curFont  
  6216.        cpx     #0  
  6217.        bne     useSystemFont           ; read error  
  6218.        jsr     LoadCharSet  
  6219.        clc                             ; success  
  6220.        rts
  6221.  
  6222. useSystemFont:  
  6223.        jsr     UseSystemFont  
  6224.        LoadW   curFont, SYSTEM_FONT_ID  
  6225.        sec                             ; fail: it's not the font we wanted  
  6226.        rts
  6227. </code></pre>
  6228. <p>It calls <code>findFontIdOnDisk</code> (not shown) to check whether the information about the available fonts and point sizes on disk contains the requested font ID.</p>
  6229. <p>If the ID is found, <code>setFontFromFile</code> calls <code>allocateFontBufferSpace</code> with the required data size to make space for the font in the buffer, and loads it using <code>ReadFile</code> and the track and sector pointer. If anything goes wrong, the system font is activated instead.</p>
  6230. <p>This is <code>allocateFontBufferSpace</code>:</p>
  6231. <pre><code>;---------------------------------------------------------------  
  6232. ; allocateFontBufferSpace  
  6233. ;
  6234. ; Function:  Allocate buffer space for a new font.  
  6235. ;
  6236. ; Pass:      r3  size of font data  
  6237. ;
  6238. ; Note:      This function cannot fail: It will remove fonts  
  6239. ;            using an LRU strategy until there is space.  
  6240. ;---------------------------------------------------------------  
  6241. allocateFontBufferSpace:  
  6242.        ldx     loadedFontsCount        ; no fonts loaded?  
  6243.        beq     @first                  ; then load it to start of buffer
  6244.  
  6245.        cpx     #MAX_FONTS_LOADED  
  6246.        beq     @remove                 ; too many fonts loaded, remove one
  6247.  
  6248.        lda     loadedFontPtrsLo-1,x    ; check for r3 bytes of spaces in font buffer  
  6249.        clc                             ; (last font pointer + last font size + required size)  
  6250.        adc     loadedfontDataSizeLo-1,x  
  6251.        tay  
  6252.        lda     loadedFontPtrsHi-1,x  
  6253.        adc     loadedfontDataSizeHi-1,x  
  6254.        tax  
  6255.        tya  
  6256.        add     r3L  
  6257.        tay  
  6258.        txa  
  6259.        adc     r3H  
  6260.        cmp     #&gt;MEM_SCRRECV  
  6261.        bne     :+  
  6262.        cpy     #&lt;MEM_SCRRECV  
  6263. :       bcc     @add                    ; it fits  
  6264.        beq     @add
  6265.  
  6266. @remove:  
  6267.        PushW   r1                      ; does not fit  
  6268.        jsr     unloadLruFont           ; remove one  
  6269.        PopW    r1  
  6270.        bra     allocateFontBufferSpace ; try again
  6271.  
  6272. @first: lda     #&gt;MEM_FONT              ; load first font to start  
  6273.        ldy     #&lt;MEM_FONT              ; of font buffer  
  6274.        bra     @set                    
  6275.  
  6276. @add:   ldx     loadedFontsCount        ; new ptr = last ptr + size  
  6277.        lda     loadedFontPtrsLo-1,x  
  6278.        clc  
  6279.        adc     loadedfontDataSizeLo-1,x  
  6280.        tay  
  6281.        lda     loadedFontPtrsHi-1,x  
  6282.        adc     loadedfontDataSizeHi-1,x  
  6283. @set:   ldx     loadedFontsCount  
  6284.        sta     loadedFontPtrsHi,x      ; store new ptr  
  6285.        tya  
  6286.        sta     loadedFontPtrsLo,x  
  6287.        lda     r3L  
  6288.        sta     loadedfontDataSizeLo,x  ; new size  
  6289.        lda     r3H  
  6290.        sta     loadedfontDataSizeHi,x  
  6291.        inc     loadedFontsCount        ; one font more  
  6292.        rts
  6293. </code></pre>
  6294. <p>If the new font does not fit into the empty space at the end of the buffer, this function keeps calling <code>unloadLruFont</code> until there is enough space. It then fills the data pointer and size fields for the new font and increments the number of currently loaded fonts.</p>
  6295. <p><code>unloadLruFont</code> is used to make space:</p>
  6296. <pre><code>;---------------------------------------------------------------  
  6297. ; unloadLruFont  
  6298. ;
  6299. ; Function:  Unload the least recently used font and compress  
  6300. ;            the font buffer.  
  6301. ;---------------------------------------------------------------  
  6302. unloadLruFont:  
  6303.                                        ; find lowest LRU ID  
  6304.        ldy     #0                      ; candidate for lowest  
  6305.        ldx     #1  
  6306. @loop1: cpx     loadedFontsCount  
  6307.        beq     @end1                   ; done iterating  
  6308.        lda     loadedFontLruIdHi,x  
  6309.        cmp     loadedFontLruIdHi,y  
  6310.        bne     @1  
  6311.        lda     loadedFontLruIdLo,x  
  6312.        cmp     loadedFontLruIdLo,y  
  6313. @1:     bcs     @2  
  6314.        txa                             ; current one is lower  
  6315.        tay                             ; -&gt; update candidate  
  6316. @2:     inx  
  6317.        bra     @loop1
  6318.  
  6319. @end1:  tya  
  6320.        tax                             ; lowest index to X
  6321.  
  6322. @loop2: inx  
  6323.        cpx     loadedFontsCount        ; is it the last one?  
  6324.        beq     @end2                   ; then we're done
  6325.  
  6326.        dex  
  6327.        lda     loadedfontDataSizeHi+1,x; count: size of the one after  
  6328.        sta     r2H  
  6329.        lda     loadedfontDataSizeLo+1,x  
  6330.        sta     r2L  
  6331.        lda     loadedFontPtrsHi+1,x    ; source: address of the one after  
  6332.        sta     r0H  
  6333.        lda     loadedFontPtrsLo+1,x  
  6334.        sta     r0L  
  6335.        lda     loadedFontPtrsHi,x      ; target: address of the current one  
  6336.        sta     r1H  
  6337.        lda     loadedFontPtrsLo,x  
  6338.        sta     r1L  
  6339.        txa  
  6340.        pha  
  6341.        jsr     MoveData                ; move the next font down  
  6342.        pla  
  6343.        tax  
  6344.        lda     loadedFontIdsLo+1,x     ; move loadedFontIds  
  6345.        sta     loadedFontIdsLo,x  
  6346.        lda     loadedFontIdsHi+1,x  
  6347.        sta     loadedFontIdsHi,x  
  6348.        lda     loadedFontLruIdLo+1,x   ; move FontLru  
  6349.        sta     loadedFontLruIdLo,x  
  6350.        lda     loadedFontLruIdHi+1,x  
  6351.        sta     loadedFontLruIdHi,x  
  6352.        lda     loadedfontDataSizeLo+1,x  
  6353.        sta     loadedfontDataSizeLo,x  ; move loadedfontDataSize  
  6354.        clc  
  6355.        adc     loadedFontPtrsLo,x      ; update fontPtrs  
  6356.        sta     loadedFontPtrsLo+1,x  
  6357.        lda     loadedfontDataSizeHi+1,x  
  6358.        sta     loadedfontDataSizeHi,x  
  6359.        adc     loadedFontPtrsHi,x  
  6360.        sta     loadedFontPtrsHi+1,x  
  6361.        inx  
  6362.        bra     @loop2                  ; repeat for all fonts above removed one
  6363.  
  6364. @end2:  dec     loadedFontsCount  
  6365.        rts
  6366. </code></pre>
  6367. <p>It finds the lowest LRU ID, i.e. the least recently used font, moves all fonts at higher addresses (and their pointers) down, and decrements the number of loaded fonts.</p>
  6368. <p>To keep track of which font is least recently used, <code>updateLoadedFontLruId</code> is called on load and on every activation of a font:</p>
  6369. <pre><code>;---------------------------------------------------------------  
  6370. ; updateLoadedFontLruId  
  6371. ;
  6372. ; Function:  Mark a given font as most recently used.  
  6373. ;
  6374. ; Pass:      x   font index  
  6375. ;---------------------------------------------------------------  
  6376. updateLoadedFontLruId:  
  6377.        lda     fontLruCounter  
  6378.        sta     loadedFontLruIdLo,x  
  6379.        lda     fontLruCounter+1  
  6380.        sta     loadedFontLruIdHi,x  
  6381.        IncW    fontLruCounter  
  6382.        bne     @rts
  6383.  
  6384.        ; 16 bit overflow: clear LRU ID for all fonts  
  6385.        ldy     #0  
  6386.        tya  
  6387. @loop:  sta     loadedFontLruIdLo,y  
  6388.        sta     loadedFontLruIdHi,y  
  6389.        iny  
  6390.        cpy     loadedFontsCount  
  6391.        bne     @loop
  6392.  
  6393. @rts:   rts
  6394. </code></pre>
  6395. <p>It keeps assigning the next number of a sequence to the given font, guaranteeing that it will always be the highest number and therefore the last one to be removed.</p>
  6396. <h2 id="caching-metrics">Caching Metrics</h2>
  6397. <p>When a word processor is dealing with fonts, it does not always need the actual image data for it. Sometimes the fonts metrics are enough, i.e. the height of the font, the baseline offset and the width of the different characters. This is true when selecting text, for example, to know where the character boundaries are, or when reflowing a document.</p>
  6398. <p>Caching font data is expensive; the 7000 bytes of geoWrite can hold five 12pt fonts, but only two 24pt fonts. Caching metrics is cheap: The character widths take up only 96 bytes per point size (for the printable ASCII character codes $20-$7F).</p>
  6399. <p>geoWrite therefore has an independent cache for font metrics that holds information about the last 8 loaded fonts.</p>
  6400. <p>The data structure looks like this:</p>
  6401. <pre><code>struct {  
  6402.    uint16_t font_id;  
  6403.    uint8_t height;  
  6404.    uint8_t baseline_offset;  
  6405.    uint8_t widths[96];  
  6406. } metrics[8];
  6407. </code></pre>
  6408. <p>So if the application wants to draw characters, it has to call <code>setFontFromFile</code>, which will make sure the pixel data is in memory and activated, but if it only needs the font for measuring, it should call <code>lookupFontMetrics</code> instead:</p>
  6409. <pre><code>;---------------------------------------------------------------  
  6410. ; lookupFontMetrics  
  6411. ;
  6412. ; Function:  Prepare cached font metrics for use.  
  6413. ;
  6414. ; Pass:      a3  font ID  
  6415. ;---------------------------------------------------------------  
  6416. lookupFontMetrics:  
  6417.        ldx     #0                      ; find font id in metricsIds  
  6418. @loop:  lda     metricsIds,x  
  6419.        tay  
  6420.        ora     metricsIds+8,x  
  6421.        beq     @nfound  
  6422.        cpy     a3L  
  6423.        bne     @no  
  6424.        lda     metricsIds+8,x  
  6425.        cmp     a3H  
  6426.        beq     @found  
  6427. @no:    inx  
  6428.        cpx     #MAX_FONTS_LOADED  
  6429.        bne     @loop
  6430.  
  6431.        jsr     getMod8Index
  6432.  
  6433.        ; not found in metrics cache  
  6434. @nfound:  
  6435.        PushB   r1H  
  6436.        jsr     moveA3R1                ; r1 = font id  
  6437.        txa  
  6438.        pha  
  6439.        jsr     setFontFromFile         ; set font  
  6440.        pla  
  6441.        tax                             ; mod8 index  
  6442.        PopB    r1H  
  6443.        lda     a3L                     ; store font id in metricsIds  
  6444.        sta     metricsIds,x  
  6445.        lda     a3H  
  6446.        sta     metricsIds+8,x  
  6447.        lda     curHeight               ; store height in table  
  6448.        sta     metricsHeights,x  
  6449.        lda     baselineOffset  
  6450.        sta     metricsBaselineOffsets,x
  6451.  
  6452.        jsr     getCachedFontMetrics  
  6453.        jsr     calcCharWidths  
  6454.        rts
  6455.  
  6456. @found: jmp     getCachedFontMetrics
  6457.  
  6458. ; ----------------------------------------------------------------------------  
  6459. getCachedFontMetrics:  
  6460.        [...]  
  6461.        sta     metricsWidths  
  6462.        [...]  
  6463.        sta     metricsWidths+1  
  6464.        lda     metricsHeights,x  
  6465.        sta     curFontHeight  
  6466.        lda     metricsBaselineOffsets,x  
  6467.        sta     curBaselineOffset  
  6468.        rts
  6469. </code></pre>
  6470. <p>If the metrics for the requested font and point size are in the cache, they will be copied into  <code>curFontHeight</code>, <code>curBaselineOffset</code> and the array <code>metricsWidths</code>. Otherwise, the font&rsquo;s pixel data is loaded and the metrics are added to the cache using <code>calcCharWidths</code> (not shown).</p>
  6471. <p>To get the width of a character after metrics have been looked up, the app can now call <code>getCharWidth</code>:</p>
  6472. <pre><code>;---------------------------------------------------------------  
  6473. ; getCharWidth  
  6474. ;
  6475. ; Function:  Get the width of a specified char of the currently  
  6476. ;            active metrics set (-&gt; lookupFontMetrics).  
  6477. ;
  6478. ; Pass:      a   character  
  6479. ;            x   currentMode  
  6480. ;
  6481. ; Return:    a   width  
  6482. ;---------------------------------------------------------------  
  6483. getCharWidth:  
  6484.        sub     #$20                    ; ASCII -&gt; table index  
  6485.        pha  
  6486.        MoveW   metricsWidths, r14  
  6487.        pla  
  6488.        tay  
  6489.        lda     (r14),y                 ; width  
  6490.        sta     metricsTmp  
  6491.        txa                             ; mode  
  6492.        and     #SET_BOLD  
  6493.        beq     :+  
  6494.        inc     metricsTmp              ; add one  
  6495. :       txa  
  6496.        and     #SET_OUTLINE  
  6497.        beq     :+  
  6498.        inc     metricsTmp              ; add 3  
  6499.        inc     metricsTmp  
  6500. :       lda     metricsTmp  
  6501.        rts
  6502. </code></pre>
  6503. <p>This is basically a reimplementation of the GEOS KERNAL&rsquo;s <code>GetRealSize</code> API: If the current font style is bold or outline, the width is increased by one or three pixels, respectively.</p>
  6504. <h2 id="conclusion">Conclusion</h2>
  6505. <p>One goal of a modern operating system and even of many kinds of libraries is to abstract what is going on underneath it. GEOS is a very constrained operating system with only 64 KB of total RAM at its disposal, so it tries to provide as many useful functions as possible (graphics, text rendering, disk access, mouse, printer, &hellip;) that fit into 20 KB of code, but in many parts of the system, it barely abstracts the underlying hardware.</p>
  6506. <p>GEOS applications are seen as as the natural extension of the operating system, and many features that did not fit into the operating system were implemented in the applications, with full awareness of the details of the filesystem or the file formats of system files.</p>
  6507. <p>The GEOS font manager can be regarded as a low-level system library, which deals with the internals of the BAM/VLIR filesystem and the font file format.</p>
  6508. <h2 id="references">References</h2>
  6509. <ul>
  6510. <li>Michael Farr: <a href="https://archive.org/details/The_Official_GEOS_Programmers_Reference_Guide">The Official GEOS Programmer&rsquo;s Reference Guide</a></li>
  6511. <li>Berkeley Softworks: <a href="https://archive.org/details/The_Hitchhikers_Guide_to_GEOS">The Hitchhiker’s Guide to GEOS</a></li>
  6512. <li>Berkeley Softworks: <a href="https://archive.org/details/GEOProgrammer_Users_Manual">geoProgrammer User&rsquo;s Manual</a></li>
  6513. <li>Rebecca G. Bettencourt: <a href="https://github.com/kreativekorp/bitsnpicas/wiki/GEOS-Font-Format">GEOS Font Format</a></li>
  6514. <li>Glenn Holmer: <a href="https://www.lyonlabs.org/commodore/onrequest/geos/geos-fonts.html">GEOS Fonts</a></li>
  6515. <li><a href="https://github.com/mist64/geos">GEOS Source Code</a></li>
  6516. </ul>
  6517. ]]></content:encoded>
  6518. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1436</wfw:commentRss>
  6519. <slash:comments>0</slash:comments>
  6520. </item>
  6521. <item>
  6522. <title>Inside geoWrite – 2: Screen Recovery</title>
  6523. <link>https://www.pagetable.com/?p=1428</link>
  6524. <comments>https://www.pagetable.com/?p=1428#comments</comments>
  6525. <pubDate>Wed, 02 Sep 2020 21:46:43 +0000</pubDate>
  6526. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  6527. <category><![CDATA[6502]]></category>
  6528. <category><![CDATA[Archeology]]></category>
  6529. <category><![CDATA[C64]]></category>
  6530. <category><![CDATA[Commodore]]></category>
  6531. <category><![CDATA[GEOS]]></category>
  6532. <category><![CDATA[Operating Systems]]></category>
  6533.  
  6534. <guid isPermaLink="false">https://www.pagetable.com/?p=1428</guid>
  6535. <description><![CDATA[In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses how the app manages to extend its usable RAM by 5 KB using a custom screen recovery solution. Article Series The Overlay System Screen Recovery ← this article Font Management Zero Page Copy Protection Localization File Format ... <a title="Inside geoWrite – 2: Screen Recovery" class="read-more" href="https://www.pagetable.com/?p=1428">Read more<span class="screen-reader-text">Inside geoWrite – 2: Screen Recovery</span></a>]]></description>
  6536. <content:encoded><![CDATA[<p>In the series about the internals of the geoWrite WYSIWYG text editor for the C64, this article discusses how the app manages to extend its usable RAM by 5 KB using a custom screen recovery solution.</p>
  6537. <p><img src="docs/geowrite/geowrite_2_scrrecovery.gif" alt="" /></p>
  6538. <h2 id="article-series">Article Series</h2>
  6539. <ol>
  6540. <li><a href="https://www.pagetable.com/?p=1425">The Overlay System</a></li>
  6541. <li><strong>Screen Recovery</strong> ← this article</li>
  6542. <li><a href="https://www.pagetable.com/?p=1436">Font Management</a></li>
  6543. <li><a href="https://www.pagetable.com/?p=1442">Zero Page</a></li>
  6544. <li><a href="https://www.pagetable.com/?p=1449">Copy Protection</a></li>
  6545. <li><a href="https://www.pagetable.com/?p=1460">Localization</a></li>
  6546. <li><a href="https://www.pagetable.com/?p=1471">File Format and Pagination</a></li>
  6547. <li><a href="https://www.pagetable.com/?p=1481">Copy &amp; Paste</a></li>
  6548. <li><a href="https://www.pagetable.com/?p=1490">Keyboard Handling</a></li>
  6549. </ol>
  6550. <h2 id="screen-recovery">Screen Recovery</h2>
  6551. <p>Any graphical user interface that allows overlapping items has to deal with the problem of recovery: When dialogs and pull-down menus are shown, they cover parts of the screen, and when they are dismissed, the underlying UI needs to be revealed again.</p>
  6552. <p>There are two basic approaches to this:</p>
  6553. <ol>
  6554. <li>
  6555. <p>When the dialog or menu is dismissed, the system calls the same text or image rendering code again that drew it in the first place.</p>
  6556. </li>
  6557. <li>
  6558. <p>Before the dialog or menu is drawn, the system saves the pixel data that will be overwritten, and after the item is dismissed, the saved pixels get written back onto the screen.</p>
  6559. </li>
  6560. </ol>
  6561. <p>The latter solution requires additional memory, but is a lot faster and doesn&rsquo;t create a potentially jarring redraw.</p>
  6562. <h2 id="screen-recovery-in-geos">Screen Recovery in GEOS</h2>
  6563. <p>GEOS uses the &ldquo;restore the pixel data&rdquo; approach, but with a twist.</p>
  6564. <p>Let&rsquo;s look at the GEOS memory layout again:</p>
  6565. <pre><code>-----------------------------------------  
  6566. $0000  Zero page, stack, system variables  
  6567. -----------------------------------------  
  6568. $0400
  6569.  
  6570.  
  6571.  
  6572.  
  6573.       Application memory
  6574.  
  6575.  
  6576.  
  6577.  
  6578.  
  6579. -----------------------------------------  
  6580. $6000  
  6581.       Background bitmap
  6582.  
  6583. -----------------------------------------  
  6584. $8000  System buffers and variables  
  6585. -----------------------------------------  
  6586. $9000  Disk driver  
  6587. -----------------------------------------  
  6588. $A000  
  6589.       Screen bitmap
  6590.  
  6591. -----------------------------------------  
  6592. $C000  GEOS KERNAL
  6593.  
  6594.  
  6595. -----------------------------------------
  6596. </code></pre>
  6597. <p>The 8 KB of 320&#215;200 monochrome bitmap data resides at $A000-$BF40. In addition, GEOS statically reserves a whole 8 KB just for screen recovery: The &ldquo;background bitmap&rdquo; at $6000-$7F40 allows GEOS to recover the whole screen area.</p>
  6598. <h3 id=" imprint-and-recover"> Imprint and Recover</h3>
  6599. <p>This background bitmap is really just another (invisible) framebuffer with the same layout as the actual screen bitmap. When saving pixels, they get copied from the screen (&ldquo;foreground&rdquo;) bitmap to the same offset in the background bitmap and vice versa.</p>
  6600. <p>These two functions are the core of the API:</p>
  6601. <ul>
  6602. <li><code>ImprintRectangle</code> – copy rectangle from fg bitmap to bg bitmap</li>
  6603. <li><code>RecoverRectangle</code> – copy rectangle from bg bitmap to fg bitmap</li>
  6604. </ul>
  6605. <p>While applications are free to use the API this way, the system-suggested way is actually different.</p>
  6606. <h3 id="display-buffering">Display Buffering</h3>
  6607. <p>When using the core GEOS API, it&rsquo;s not actually necessary to save the pixels (<code>ImprintRectangle</code>) before overwriting them. The background buffer already contains a copy!</p>
  6608. <p>That&rsquo;s because all graphics code can be configured to either draw to the foreground bitmap, the background bitmap, or both.</p>
  6609. <p>To calculate the offset in the bitmap of a y coordinate, all internal drawing code calls <code>GetScanLine</code>. This function usually returns two pointers in virtual registers r5 and r6:</p>
  6610. <ul>
  6611. <li>r5: pointer to the pixel line in the <em>foreground</em> screen</li>
  6612. <li>r6: pointer to the pixel line in the <em>background</em> screen</li>
  6613. </ul>
  6614. <p>Any code in the GEOS drawing library then stores the pixel data in both the locations pointed to by r5 and r6:</p>
  6615. <pre><code>    [...]  
  6616.    sta (r5),y  
  6617.    sta (r6),y
  6618. </code></pre>
  6619. <p>For as little as an extra 6 clock cycles for every byte to be stored (which is 8 pixels), the drawing code stores it into both bitmaps, so there is usually no need to copy data from the foreground to the background bitmap.</p>
  6620. <p>Now when the system draws a menu or a dialog, it only draws it into the foreground buffer. That&rsquo;s because the <code>GetScanLine</code> call can actually return different kinds of pointers depending on the global system variable <code>dispBufferOn</code>:</p>
  6621. <table>
  6622. <thead>
  6623. <tr>
  6624. <th> <code>dispBufferOn</code>        </th>
  6625. <th> r5            </th>
  6626. <th> r6            </th>
  6627. </tr>
  6628. </thead>
  6629. <tbody>
  6630. <tr>
  6631. <td> <code>%11000000</code> (default) </td>
  6632. <td> fg screen ptr </td>
  6633. <td> bg screen ptr </td>
  6634. </tr>
  6635. <tr>
  6636. <td> <code>%10000000</code>           </td>
  6637. <td> fg screen ptr </td>
  6638. <td> fg screen ptr </td>
  6639. </tr>
  6640. <tr>
  6641. <td> <code>%01000000</code>           </td>
  6642. <td> bg screen ptr </td>
  6643. <td> bg screen ptr </td>
  6644. </tr>
  6645. </tbody>
  6646. </table>
  6647. <p>By only setting <em>one</em> of bits 6 and 7, all drawing code can effectively be instructed to only draw to the foreground or the background bitmap. In this case, the two <code>sta</code> instructions will just store the pixel data to the same location twice.</p>
  6648. <p>The system default is to draw everything to both bitmaps – except menus and dialogs, which are only ever drawn to the foreground bitmap. There is therefore no need to call <code>ImprintRectangle</code>: All that the built-in code has to do is call <code>RecoverRectangle</code> on the rectangle that it overwrote.</p>
  6649. <h2 id="screen-recovery-in-geowrite">Screen Recovery in geoWrite</h2>
  6650. <p>geoWrite is a very complex application that is very tight on memory, so it was designed to reclaim as much as possible of the 8 KB normally used for screen recovery. Text rendering is very slow, so redrawing the page as a recovery strategy is out of the question.</p>
  6651. <p>Instead, geoWrite stores the saved pixels more efficiently: The buffer only really needs to be as big as is required for the largest rectangle ever saved while in the app. And for geoWrite, that&rsquo;s dialogs, which are 200&#215;104 pixels. So that&rsquo;s 2600 bytes instead of 8000 bytes.</p>
  6652. <p>The code to save a screen rectangle is called with the following arguments in virtual registers:</p>
  6653. <ul>
  6654. <li><code>r1</code>: the pointer to the recovery buffer</li>
  6655. <li><code>r2L</code>: x ÷ 8</li>
  6656. <li><code>r2H</code>: y</li>
  6657. <li><code>r3L</code>: width ÷ 8</li>
  6658. <li><code>r3H</code>: height</li>
  6659. </ul>
  6660. <p>X coordinates have to be divisible by 8, so that the pixels are at byte boundaries.</p>
  6661. <h3 id="the-code">The Code</h3>
  6662. <p>The following is the main code, slightly edited for readability. It iterates over all lines of the rectangle and, on save, appends the bitmap bytes to the array of saved data. On recover, it does the opposite.</p>
  6663. <pre><code>@loop1: ldx     r2H         ; y coord  
  6664.        jsr     GetScanLine ; r5 := ptr to bitmap  
  6665.        lda     r2L         ; x coord / 8  
  6666.        asl     a  
  6667.        asl     a  
  6668.        asl     a           ; * 8  
  6669.        bcc     :+  
  6670.        inc     r5H  
  6671. :       tay  
  6672.        MoveB   r3L, r4L    ; copy byte count  
  6673. @loop2: bit     r4H         ; save or recover?  
  6674.        bpl     @1  
  6675.        jsr     @recv  
  6676.        bra     @2  
  6677. @1:     jsr     @save  
  6678. @2:     IncW    r1          ; advance buffer pointer  
  6679.        add     #8          ; account for quirky VIC-II memory layout  
  6680.        bcc     :+  
  6681.        inc     r5H  
  6682. :       tay  
  6683.        dec     r4L         ; dec byte counter  
  6684.        bne     @loop2      ; loop for horizontal bytes  
  6685.        inc     r2H  
  6686.        dec     r3H         ; dec line counter  
  6687.        bne     @loop1      ; loop for lines  
  6688.        rts
  6689. </code></pre>
  6690. <p>This is the subroutine for copying a byte from the screen to the buffer&hellip;</p>
  6691. <pre><code>@save:  lda     (r5),y      ; read screen byte  
  6692.        tax  
  6693.        tya  
  6694.        pha                 ; save offset  
  6695.        ldy     #0  
  6696.        txa  
  6697.        sta     (r1),y      ; write into buffer  
  6698.        pla                 ; restore offset  
  6699.        rts
  6700. </code></pre>
  6701. <p>&hellip;and the code for copying a byte from the buffer to the screen:</p>
  6702. <pre><code>@recv:  tya                 ; save offset  
  6703.        pha  
  6704.        ldy     #0  
  6705.        lda     (r1),y      ; read from buffer  
  6706.        tax  
  6707.        pla  
  6708.        tay                 ; restore offset  
  6709.        txa  
  6710.        sta     (r5),y      ; write onto screen  
  6711.        tya  
  6712.        rts
  6713. </code></pre>
  6714. <h3 id="the-tables">The Tables</h3>
  6715. <p>geoWrite uses a table with the buffer pointer, x, y, width and height values for each use case, so only an offset within the table has to be passed:</p>
  6716. <pre><code>;              r1L/r1H  r2L  r2H  r3L  r3H  
  6717. ;                ptr      x    y wdth hght  
  6718. scrrecvtabs:  
  6719. scrrecvtab_geos:  
  6720.        .word   MEM_SCRREST  
  6721.        .byte             0,  15,  10, 128  
  6722. scrrecvtab_file:  
  6723.        .word   MEM_SCRREST  
  6724.        .byte             3,  15,   7, 100  
  6725. [...]
  6726. </code></pre>
  6727. <p>The first item in the table is for saving and restoring the rectangle under the &ldquo;geos&rdquo; menu, and so on.</p>
  6728. <p><code>MEM_SCRREST</code> is the address of the start of the recover buffer. The buffer pointer isn&rsquo;t always <code>MEM_SCRREST</code> though:</p>
  6729. <pre><code>scrrecvtab_font:  
  6730.        .word   MEM_SCRREST  
  6731.        .byte            17,  15,  10, 114  
  6732. scrrecvtab_fontsize:  
  6733.        .word   MEM_SCRREST + 1408  
  6734.        .byte            26,  15,   9, 114
  6735. </code></pre>
  6736. <p>This is because the font menu has a sub-menu for the point size:</p>
  6737. <p><img src="docs/geowrite/font_menu.png" alt="" /></p>
  6738. <p>When the font menu is open, the rectangle covered by it is already saved in the buffer. The rectangle under the point size menu has to be saved in the area following the already occupied data.</p>
  6739. <h3 id="memory-layout">Memory Layout</h3>
  6740. <p>Conveniently, the RAM area for the background bitmap immediately follows the application&rsquo;s memory. Apps are free to just not use the background bitmap at all. By setting <code>dispBufferOn</code> to <code>%10000000</code> globally, the system will never touch the $6000-$7FFF area.</p>
  6741. <p>Here&rsquo;s a mostly to scale overview of the geoWrite memory map compared to the GEOS default:</p>
  6742. <pre><code>       GEOS Default                    geoWrite  
  6743. -------------------------      -------------------------  
  6744. $0400                                              $0400
  6745.  
  6746.                                     Main Code
  6747.  
  6748.  
  6749.       Application memory      -------------------------  
  6750.                                     Overlay Code  $3244  
  6751.                               -------------------------  
  6752.                                                   $4310  
  6753.                                     Page Data
  6754.  
  6755. -------------------------      -------------------------  
  6756. $6000                                Font Heap     $5E68  
  6757.       Background bitmap       _________________________  
  6758.                                     Recovery Data $75D8  
  6759. -------------------------      -------------------------
  6760. </code></pre>
  6761. <p>geoWrite puts the recovery buffer at the topmost 2600 bytes of the $0400-$8000 window that is available to the application if it doesn&rsquo;t use the background screen.</p>
  6762. <p>The reason for this location is because the range $7900-$7F40 is where GEOS requires the printer driver to be loaded once the application wants to print. It overlaps the background screen on purpose: As long as the application is not printing, so no space for the driver is wasted. And once it is printing, it just can&rsquo;t use the background buffer any more. The same is true in geoWrite&rsquo;s model.</p>
  6763. <h3 id="triggers">Triggers</h3>
  6764. <p>Aside from setting <code>dispBufferOn</code> to just the foreground, using one&rsquo;s own save/recover logic requires the app to make sure the save and recover code gets called at the right times.</p>
  6765. <p>For dialogs, this is easy: Apps have to explicitly show them by calling the <code>doDlgBox</code> API, so before calling it, geoWrite saves the respective rectangle, and once the API returns, it recovers it.</p>
  6766. <p>For menus, it&rsquo;s more tricky. The application has to trigger on the open event of a sub-menu.</p>
  6767. <p>Usually, the data structure of a menu for the <code>doMenu</code> API contains pointers to sub-menu data structures, forming the complete menu tree, like this:</p>
  6768. <pre><code>menu_main:  
  6769.        .byte   0, 14, 0, 191 ; pos &amp; size  
  6770.        .byte   HORIZONTAL | UN_CONSTRAINED | 3 ; #items
  6771.  
  6772.        .word   txt_geos  ; string "geos"  
  6773.        .byte   SUB_MENU  ; =below is a sub-menu ptr  
  6774.        .word   menu_geos ; sub-menu data structure
  6775.  
  6776.        .word   txt_file  
  6777.        .byte   SUB_MENU  
  6778.        .word   menu_file
  6779.  
  6780.        .word   txt_edit  
  6781.        .byte   SUB_MENU  
  6782.        .word   menu_edit
  6783. </code></pre>
  6784. <p>This way, an application can describe a complete menu tree with just data structures and no code.</p>
  6785. <p>But instead of a pointer to a sub-menu (code <code>SUB_MENU</code>), the data structure can alternatively contain a pointer to <em>code</em> that returns a sub-menu data structure (code <code>DYN_SUB_MENU</code>):</p>
  6786. <pre><code>        .word   txt_geos     ; string "geos"  
  6787.        .byte   DYN_SUB_MENU ; =below is a code ptr  
  6788.        .word   callbackGeos ; code, called on sub-menu open
  6789. </code></pre>
  6790. <p>In this example, whenever the &ldquo;geos&rdquo; item is clicked, the <code>callbackGeos</code> function is called, which saves the rectangle that will be covered by the &ldquo;geos&rdquo; sub-menu, and returns a pointer to the sub-menu to be presented (edited for clarity):</p>
  6791. <pre><code>callbackGeos:  
  6792.        ldx     #scrrecvtab_geos-scrrecvtabs  
  6793.        stx     a2L  
  6794.        jsr     screenSave  
  6795.        LoadW   r0, menu_geos  
  6796.        rts
  6797. </code></pre>
  6798. <p>For the recovery of the rectangle, there is actually a GEOS system variable supporting this use case: If the application sets the <code>RecoverVector</code> pointer to anything but 0, closing a menu will call that code instead of doing its own <code>RecoverRectangle</code>-based recovery.</p>
  6799. <p>This is where it points in geoWrite (again edited for clarity):</p>
  6800. <pre><code>appRecoverVector:  
  6801.        ldx     a2L  
  6802.        jsr     screenRecover  
  6803.        rts
  6804. </code></pre>
  6805. <p>The function reuses the previously set offset in the screen recovery table (in virtual register a2L) for buffer location, the origin and size of the rectangle.</p>
  6806. <h2 id="conclusion">Conclusion</h2>
  6807. <p>Berkeley Softworks wrote the GEOS operating system as well as many major applications like geoWrite, geoPaint, geoPublish and geoCalc, yet they implemented two different methods for screen recovery: The system way of doing it wastes space, but is very simple to use. And the way they do it in the applications is very much tied to the specific use case, but very optimized.</p>
  6808. ]]></content:encoded>
  6809. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1428</wfw:commentRss>
  6810. <slash:comments>1</slash:comments>
  6811. </item>
  6812. <item>
  6813. <title>Inside geoWrite – 1: The Overlay System</title>
  6814. <link>https://www.pagetable.com/?p=1425</link>
  6815. <comments>https://www.pagetable.com/?p=1425#comments</comments>
  6816. <pubDate>Tue, 01 Sep 2020 20:42:53 +0000</pubDate>
  6817. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  6818. <category><![CDATA[6502]]></category>
  6819. <category><![CDATA[Archeology]]></category>
  6820. <category><![CDATA[C64]]></category>
  6821. <category><![CDATA[Commodore]]></category>
  6822. <category><![CDATA[GEOS]]></category>
  6823. <category><![CDATA[Operating Systems]]></category>
  6824.  
  6825. <guid isPermaLink="false">https://www.pagetable.com/?p=1425</guid>
  6826. <description><![CDATA[geoWrite is a WYSIWYG rich text editor for the Commodore 64 GEOS operating system, which runs with a total of just 64 KB of RAM. In the series about the internals of geoWrite, this article discusses how it manages to fit 52 KB of code into the available 23 KB of application RAM. Introduction GEOS ... <a title="Inside geoWrite – 1: The Overlay System" class="read-more" href="https://www.pagetable.com/?p=1425">Read more<span class="screen-reader-text">Inside geoWrite – 1: The Overlay System</span></a>]]></description>
  6827. <content:encoded><![CDATA[<p>geoWrite is a WYSIWYG rich text editor for the Commodore 64 GEOS operating system, which runs with a total of just 64 KB of RAM. In the series about the internals of geoWrite, this article discusses how it manages to fit 52 KB of code into the available 23 KB of application RAM.</p>
  6828. <p><img src="docs/geowrite/geowrite_1_overlays.png" alt="" /></p>
  6829. <h2 id="introduction">Introduction</h2>
  6830. <p><a href="https://github.com/mist64/geos">GEOS</a> is a disk-based graphical operating system for the Commodore 64 that provides the following features:</p>
  6831. <ul>
  6832. <li>applications, desk accessories</li>
  6833. <li>disk, printer and mouse drivers</li>
  6834. <li>loadable proportional fonts</li>
  6835. <li>menu bars, dialogs, file picker</li>
  6836. <li>multi-fork filesystem API</li>
  6837. <li>misc. library code (math, memory, strings, &hellip;)</li>
  6838. </ul>
  6839. <p>But since the OS kernel (called the &ldquo;GEOS KERNAL&rdquo;) is only 20 KB in size, some of the APIs are very limited, or cumbersome to use, so applications had to do a lot of work one would expect from the OS these days.</p>
  6840. <p>The GEOS authors &ldquo;Berkeley Softworks&rdquo; also wrote several applications – the OS-included deskTop, geoWrite and geoPaint, as well as geoPublish, geoCalc, geoChart, geoFile and geoDex – which all share low-level functionality that is not in fact part of the operating system, but shared code between these apps, like:</p>
  6841. <ul>
  6842. <li>code overlays</li>
  6843. <li>custom screen recovery</li>
  6844. <li>create/open/exit startup dialog</li>
  6845. <li>desk accessory enumeration</li>
  6846. <li>font management</li>
  6847. <li>zero page management</li>
  6848. <li>copy protection</li>
  6849. </ul>
  6850. <p>In this series of articles, we will discuss some of the lower-level features that are implemented on the application side, using the example of geoWrite.</p>
  6851. <ol>
  6852. <li><strong>The Overlay System</strong> ← this article</li>
  6853. <li><a href="https://www.pagetable.com/?p=1428">Screen Recovery</a></li>
  6854. <li><a href="https://www.pagetable.com/?p=1436">Font Management</a></li>
  6855. <li><a href="https://www.pagetable.com/?p=1442">Zero Page</a></li>
  6856. <li><a href="https://www.pagetable.com/?p=1449">Copy Protection</a></li>
  6857. <li><a href="https://www.pagetable.com/?p=1460">Localization</a></li>
  6858. <li><a href="https://www.pagetable.com/?p=1471">File Format and Pagination</a></li>
  6859. <li><a href="https://www.pagetable.com/?p=1481">Copy &amp; Paste</a></li>
  6860. <li><a href="https://www.pagetable.com/?p=1490">Keyboard Handling</a></li>
  6861. </ol>
  6862. <h2 id="geos-memory-map">GEOS Memory Map</h2>
  6863. <p>GEOS and its apps need to fit into the 64 KB of RAM of the C64. Here is a rough overview of the memory map, mostly to scale:</p>
  6864. <pre><code>-----------------------------------------  
  6865. $0000  Zero page, stack, system variables  
  6866. -----------------------------------------  
  6867. $0400
  6868.  
  6869.  
  6870.  
  6871.  
  6872.       Application memory
  6873.  
  6874.  
  6875.  
  6876.  
  6877.  
  6878. -----------------------------------------  
  6879. $6000  
  6880.       Background bitmap
  6881.  
  6882. -----------------------------------------  
  6883. $8000  System buffers and variables  
  6884. -----------------------------------------  
  6885. $9000  Disk driver  
  6886. -----------------------------------------  
  6887. $A000  
  6888.       Screen bitmap
  6889.  
  6890. -----------------------------------------  
  6891. $C000  GEOS KERNAL
  6892.  
  6893.  
  6894. -----------------------------------------
  6895. </code></pre>
  6896. <p>The screen bitmap is an 8 KB RAM area for the 320&#215;200 monochrome screen bitmap. There is a full-size &ldquo;background&rdquo; copy used for recovering the main screen contents after closing a menu or a dialog without needing a slow redraw.</p>
  6897. <p>The application has 23 KB of memory that it can use for code and data. geoWrite is 52 KB of code, and it needs 2 KB for variables, 7 KB for the current page of text and 6 KB for bitmap fonts. That would be 67 KB&hellip;</p>
  6898. <h2 id="vlir-files">VLIR Files</h2>
  6899. <p>In the UNIX world, a file is a mapping of a filename to a sequence of bytes (and some metadata). Some operating systems extend this concept: On classic MacOS, files consist of two of these sequences (the &ldquo;resource fork&rdquo; and the &ldquo;data fork&rdquo;). NextSTEP and MacOS X can present folders with a tree of individual files as a single &ldquo;bundle&rdquo;.</p>
  6900. <p>GEOS extends the UNIX-like Commodore filesystem with &ldquo;VLIR&rdquo; files, which stands for &ldquo;Variable Length Index Record&rdquo;. A VLIR file maps a filename to a 256 byte &ldquo;file header&rdquo; (for the icon and extra metadata) and up to 127 records, numbered 0-126. A record is a variable-length sequence of bytes, much like a traditional UNIX file.</p>
  6901. <p>You can imagine a VLIR file as a folder with several files in it. The following is a visualization of a typical geoWrite document:</p>
  6902. <pre><code>geoWrite Doc  
  6903. \--- File Header  
  6904. \--- 0  
  6905. \--- 1  
  6906. \--- 2  
  6907. \--- 61  
  6908. \--- 62  
  6909. \--- 64
  6910. </code></pre>
  6911. <p>As you can see, record numbers don&rsquo;t have to be contiguous. (geoWrite for example stores pages in records 0-60, the header and footer into records 61 and 62, and image data in records 64-126.)</p>
  6912. <p>The file header contains the icon, the file type, and some other generic as well as application-specific metadata.</p>
  6913. <h2 id="vlir-applications">VLIR Applications</h2>
  6914. <p>GEOS applications can be VLIR files as well. When running an app, the system loads record 0 into memory and executes it. It&rsquo;s entirely up to the application what to do with the other records.</p>
  6915. <p>This is what the geoWrite app looks like:</p>
  6916. <pre><code>GEOWRITE           size  
  6917. \--- File Header    256  
  6918. \--- 0            10335  
  6919. \--- 1             2552  
  6920. \--- 2             3999  
  6921. \--- 3             2328  
  6922. \--- 4             1965  
  6923. \--- 5             3870  
  6924. \--- 6             3998  
  6925. \--- 7             3897  
  6926. \--- 8             1194
  6927. </code></pre>
  6928. <p>The geoWrite main code in record 0 is about 10 KB in size. The operating system loads it to $0400-$2C5E into application RAM.</p>
  6929. <h2 id="code-overlays">Code Overlays</h2>
  6930. <p>This is the memory layout of application RAM for geoWrite:</p>
  6931. <pre><code>-----------------------------------------  
  6932. $0400  Main code (record 0)
  6933.  
  6934.  
  6935. -----------------------------------------  
  6936. $2C5F  Variables  
  6937. -----------------------------------------  
  6938. $3244  Overlay code (records 1-7)
  6939.  
  6940. -----------------------------------------  
  6941. $41E4  Variables, page data, font data
  6942.  
  6943.  
  6944.  
  6945.  
  6946.  
  6947. -----------------------------------------
  6948. </code></pre>
  6949. <p>The record 0 code loaded by the OS always remains in its slot. It contains the core editing functionality, the overlay manager, the font manager and other library code.</p>
  6950. <p>There is a 4 KB slot for &ldquo;overlay&rdquo; code, meaning that the record 0 code can swap in any of the records from 1 through 7.</p>
  6951. <ul>
  6952. <li>[0 library code, core text editing]</li>
  6953. <li>1 initialization, copy protection</li>
  6954. <li>2 core text editing</li>
  6955. <li>3 cut, copy, paste</li>
  6956. <li>4 ruler editing</li>
  6957. <li>5 startup/about, create, open, paste text, run desk accessory</li>
  6958. <li>6 navigation, search/replace, header/footer, reflow</li>
  6959. <li>7 printing</li>
  6960. <li>[8 print settings]</li>
  6961. </ul>
  6962. <p>(Record 8 is handled differently and is discussed at the end of this article.)</p>
  6963. <p>Every record is linked to the same address ($3244) and starts with a jump table, like this one:</p>
  6964. <pre><code>CODE5:  
  6965.    jmp recover            ; 0  
  6966.    jmp showStartupMenu    ; 1  
  6967.    jmp renameDocument     ; 2  
  6968.    jmp openDocument       ; 3  
  6969.    jmp showAboutDialog    ; 4  
  6970.    jmp loadDeskAcc        ; 5  
  6971.    jmp exitToDesktop      ; 6  
  6972.    jmp readReservedRecord ; 7  
  6973.    jmp makeFullPageWide   ; 8
  6974. </code></pre>
  6975. <p>The jump table means that the record 0 code can be assembled independently of the overlays. While the overlays access symbols in the record 0 code, record 0 code only calls through these jump table entries.</p>
  6976. <h2 id="vlir-api">VLIR API</h2>
  6977. <p>The GEOS KERNAL has the following calls for working with VLIR files:</p>
  6978. <ul>
  6979. <li><code>OpenRecordFile</code> – Open an existing VLIR file given its name</li>
  6980. <li><code>UpdateRecordFile</code> – Flush the VLIR&rsquo;s metadata to disk</li>
  6981. <li><code>CloseRecordFile</code> – Flush and close VLIR file</li>
  6982. <li><code>PointRecord</code> – Set current record</li>
  6983. <li><code>PreviousRecord</code> – Move to previous record</li>
  6984. <li><code>NextRecord</code> – Move to next record</li>
  6985. <li><code>ReadRecord</code> – Read complete record into memory</li>
  6986. <li><code>WriteRecord</code> – Write/overwrite complete record from memory image</li>
  6987. <li><code>DeleteRecord</code> – Delete current record</li>
  6988. </ul>
  6989. <p>Reading overlay code should therefore be as simple as this:</p>
  6990. <pre><code>    ; startup  
  6991.    LoadW   r0, fnBuffer  
  6992.    jsr     OpenRecordFile
  6993.  
  6994.    ; load overlay code  
  6995.    lda     #n  
  6996. loadCode:  
  6997.    jsr     PointRecord  
  6998.    LoadW   r7, OVERLAY_ADDRESS  
  6999.    LoadW   r2, OVERLAY_SIZE  
  7000.    jsr     ReadRecord
  7001. </code></pre>
  7002. <p>Unfortunately, GEOS can only have one VLIR file open at a time, and a geoWrite document is also a VLIR file. Opening and closing the two files would cause too much disk activity, which is why geoWrite comes with a simple read-only VLIR implementation on the side.</p>
  7003. <h2 id="loading-overlays-manually">Loading Overlays Manually</h2>
  7004. <p>On disk, a VLIR file&rsquo;s directory entry points to it 256 bytes index table. Here is an example:</p>
  7005. <pre><code>00 FF  06 13  08 10  09 14  0A 01  0A 12  0A 13  0B 00  
  7006. 0C 02  0D 04  00 00  00 00  00 00  00 00  00 00  00 00  
  7007. [...]
  7008. </code></pre>
  7009. <p>The <code>00 FF</code> at the beginning is the Commodore DOS sector header and not part of the data. The remaining pairs of bytes point to the track and sector of the start of each record on disk.</p>
  7010. <p>GEOS has an API for loading a file given a track and a sector (<code>ReadFile</code>), so all geoWrite needs to do is read its own index table on startup, and call <code>ReadFile</code> on items of this table when loading an overlay.</p>
  7011. <p>Here&rsquo;s a shortened version of the code to get a copy of the app&rsquo;s index table:</p>
  7012. <pre><code>    ; find application  
  7013.    LoadW   r6, fnBuffer  
  7014.    lda     #APPLICATION  
  7015.    sta     r7L  
  7016.    lda     #1 ; find max. 1 file  
  7017.    sta     r7H  
  7018.    LoadW   r10, appname  
  7019.    jsr     FindFTypes
  7020.  
  7021.    LoadW   r0, fnBuffer  
  7022.    jsr     OpenRecordFile
  7023.  
  7024.    jsr     i_MoveData ; copy index table  
  7025.    .word   fileHeader+2  
  7026.    .word   appIndexTable  
  7027.    .word   2 * NUM_APP_RECORDS  
  7028.    LoadB   curCodeRecord, $FF  
  7029.    rts
  7030.  
  7031. appname:  
  7032.    .byte   "geoWrite    V2.1",0
  7033. </code></pre>
  7034. <p><code>OpenRecordFile</code> reads the VLIR file&rsquo;s index table info <code>fileHeader</code>. geoWrite then copies <code>NUM_APP_RECORDS</code> into its own table <code>appIndexTable</code>, skipping the first two bytes (<code>00 FF</code>).</p>
  7035. <p>And here is a shortened version of the code to read a record:</p>
  7036. <pre><code>    ; load overlay code  
  7037.    lda     #n  
  7038. loadCode:  
  7039.    cmp     curCodeRecord  
  7040.    beq     @rts ; already loaded  
  7041.    sta     curCodeRecord  
  7042.    asl     a  
  7043.    tay  
  7044.    lda     appIndexTable,y  
  7045.    sta     r1L  
  7046.    lda     appIndexTable+1,y  
  7047.    sta     r1H  
  7048.    LoadW   r7, OVERLAY_ADDRESS  
  7049.    LoadW   r2, OVERLAY_SIZE  
  7050.    jsr     _ReadFile  
  7051. @rts:  
  7052.    rts
  7053. </code></pre>
  7054. <p>You can see that the code keeps track of the currently loaded record, so it does not re-load the same code if it&rsquo;s already in memory.</p>
  7055. <h2 id="managing-overlays">Managing Overlays</h2>
  7056. <p>All overlay functionality is implemented in the record 0 code, because it always needs to be accessible.</p>
  7057. <p>There is a set of functions for loading the different records:</p>
  7058. <pre><code>loadCode1:  
  7059.    lda     #1  
  7060.    .byte   $2C ; skip next  
  7061. loadCode2:  
  7062.    lda     #2  
  7063.    .byte   $2C ; skip next  
  7064. loadCode3:  
  7065.    lda     #3  
  7066.    .byte   $2C ; skip next  
  7067. loadCode4:  
  7068.    lda     #4  
  7069.    .byte   $2C ; skip next  
  7070. loadCode5:  
  7071.    lda     #5  
  7072.    .byte   $2C ; skip next  
  7073. loadCode6:  
  7074.    lda     #6  
  7075.    .byte   $2C ; skip next  
  7076. loadCode7:  
  7077.    lda     #7  
  7078. loadCode:  
  7079.    [...]
  7080. </code></pre>
  7081. <p>The record 0 code can then load an overlay and call a function through its jump table</p>
  7082. <pre><code>    jsr     loadCode5  
  7083.    jsr     J5_showStartupMenu ; OVERLAY_ADDRESS + 3 * 1
  7084. </code></pre>
  7085. <p>Code inside an overlay can&rsquo;t call code from a different overlay this way, because the <code>loadCode</code> call would overwrite the caller. For this case, the record 0 code has functions like this one:</p>
  7086. <pre><code>_showCantAddPages:  
  7087.    ldy     #&lt;J3_showCantAddPages ; OVERLAY_ADDRESS + 3 * 8  
  7088.    .byte   $2C  
  7089. _showTooManyPages:  
  7090.    ldy     #&lt;J3_showTooManyPages ; OVERLAY_ADDRESS + 3 * 7  
  7091.    .byte   $2C  
  7092. _splitTooBigPage:  
  7093.    ldy     #&lt;J3_splitTooBigPage  ; OVERLAY_ADDRESS + 3 * 5  
  7094.    ldx     #BANK_3  
  7095. callRestore:  
  7096.    lda     curCodeRecord  
  7097.    pha  
  7098.    sty     @1  
  7099.    txa  
  7100.    jsr     loadCode  
  7101. @1 = * + 1  
  7102.    jsr     OVERLAY_ADDRESS  
  7103.    pla  
  7104.    jmp     loadCode
  7105. </code></pre>
  7106. <p>The function <code>showCantAddPages</code> is implemented on overlay 3. Code in overlay 2 can call <code>_showCantAddPages</code> in the record 0 code, which will load overlay 3, call the function, load the original overlay 2 again, and return.</p>
  7107. <h2 id="splitting-the-logic">Splitting the Logic</h2>
  7108. <p>With helper functions in the record 0 code, it is possible to arbitrarily split logic into the different records. But since loading an overlay takes about 2-3 seconds on a 1541 disk drive, this should be minimized.</p>
  7109. <h3 id="central-library-code">Central Library Code</h3>
  7110. <p>The overlay code that we have seen above has to live in the record 0 code, so it&rsquo;s directly callable by any record.</p>
  7111. <p>While in theory any other library code could live in any other record, keeping the most-used functionality in the record 0 code will reduce disk accesses. Here are some examples:</p>
  7112. <ul>
  7113. <li>generic dialogs</li>
  7114. <li>error dialogs</li>
  7115. <li>drive switching (app vs. document)</li>
  7116. <li>disk full testing</li>
  7117. <li>screen recovery</li>
  7118. <li>font management</li>
  7119. <li>zero page management</li>
  7120. <li>some common text strings</li>
  7121. </ul>
  7122. <h3 id="one-time-logic">One-time logic</h3>
  7123. <p>Furthermore, there is code that is only ever needed once. On startup, the following is done:</p>
  7124. <ul>
  7125. <li>enumerate fonts and desk accessories on disk</li>
  7126. <li>get the page size from the printer</li>
  7127. <li>initialize the menu bar</li>
  7128. <li>draw the ruler and the page indicator</li>
  7129. <li>prepare a file opened for printing only</li>
  7130. <li>do the copy protection dance</li>
  7131. </ul>
  7132. <p>All this code lives in record 1. It is loaded immediately after the app is started. When it returns, it never gets loaded again.</p>
  7133. <h3 id="main-mode-code">Main Mode Code</h3>
  7134. <p>Then, there is code that is needed when the app is in its main mode, like the text renderer and the handlers for navigating on the page, typing text and deleting text.</p>
  7135. <p>The main mode code lives in the remainder of record 0 as well as in record 2: During normal text editing, geoWrite always keeps record 2 loaded.</p>
  7136. <p>Since functions for menu items, keyboard shortcuts and mouse triggers in main mode are called by the GEOS KERNAL directly, which does not know about banking, at least the entry points of these handlers also have to live in record 0 (or, with restrictions, record 2).</p>
  7137. <h3 id="grouped-functionality">Grouped Functionality</h3>
  7138. <p>The remaining records contain further functionality, grouped by topic, so they can use common code inside the same record. Here is the list again:</p>
  7139. <ul>
  7140. <li>3 cut, copy, paste</li>
  7141. <li>4 ruler editing</li>
  7142. <li>5 startup/about, create, open, paste text, run desk accessory</li>
  7143. <li>6 navigation, search/replace, header/footer, reflow</li>
  7144. <li>7 printing</li>
  7145. </ul>
  7146. <p>On app launch, the record 0 entry code immediately loads initialization code in record 1. After its return, the record 0 code runs the startup UI code in record 5, which creates a new file or opens an existing file and returns to the core text editor in the record 0/2 code.</p>
  7147. <h3 id="duplicate-code">Duplicate Code</h3>
  7148. <p>Common code needed by very different functionality groups usually lives in record 0, so that any record can call it without causing swapping in and out overlays. This is true for most common code, but since space in record 0 is at a premium, a different solution was necessary especially for bigger reusable components.</p>
  7149. <p>The startup code for example needs to talk to the printer driver to query the page size, and the print code needs to talk to the printer for printing. They both need to look up and load the printer driver. This common code is too big to fit into record 0, and having the startup code (#1) call out into the printing record (#7) would increase startup time by at least 5 seconds, swapping in #7 and then swapping in #1 again.</p>
  7150. <p>The solution is to just duplicate the common code into different records, e.g. by using <code>.include</code> statements to reuse the same code in multiple places. As long as the individual records don&rsquo;t overflow their 4 KB maximum, this is a reasonable tradeoff.</p>
  7151. <p>Other examples are the document file version check, the string-to-int conversion code and common &ldquo;text scrap&rdquo; (clipboard) code. Parts of the latter are even included in three different records.</p>
  7152. <h2 id="functionality->-4-kb&#8221;>Functionality > 4 KB</h2>
  7153. <p>The printing logic is about 5 KB in size, so it doesn&rsquo;t fit into a 4 KB overlay record. Swapping in and out records during printing is not an option.</p>
  7154. <p>Since printing is a different mode altogether, 1 KB of record 0 code in RAM is overwritten by the additional printing code from record #8. The code in record 0 was arranged in a way that there is a contiguous 1 KB chunk ($0680-$0B29) that does not need to be called by the printing functionality. This is, among other things, the data structures for drawing the main menu. After printing, the record #7 code re-loads all of record 0.</p>
  7155. <h2 id="conclusion">Conclusion</h2>
  7156. <p>The overlay system has been a feature since the very first version of GEOS and is used by all major applications. It is not without its limits though: An application&rsquo;s main mode should be responsive and do most of its work without swapping overlays, so the core logic (record 0 and one overlay record) has a certain limit in complexity. The individual overlays have a very tight size limit as well.</p>
  7157. <p>For the printing functionality, geoWrite already has to work around the overlay code size, which shows that an app like geoWrite is truly at the limit of what a GEOS application can do on a 64 KB system.</p>
  7158. ]]></content:encoded>
  7159. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1425</wfw:commentRss>
  7160. <slash:comments>6</slash:comments>
  7161. </item>
  7162. <item>
  7163. <title>CMDR-DOS: Commodore DOS on FAT32</title>
  7164. <link>https://www.pagetable.com/?p=1421</link>
  7165. <comments>https://www.pagetable.com/?p=1421#comments</comments>
  7166. <pubDate>Sat, 22 Aug 2020 08:17:36 +0000</pubDate>
  7167. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  7168. <category><![CDATA[6502]]></category>
  7169. <category><![CDATA[Commodore]]></category>
  7170. <category><![CDATA[Commodore Peripheral Bus]]></category>
  7171. <category><![CDATA[DOS]]></category>
  7172. <category><![CDATA[GitHub]]></category>
  7173. <category><![CDATA[KERNAL]]></category>
  7174. <category><![CDATA[Operating Systems]]></category>
  7175. <category><![CDATA[X16]]></category>
  7176.  
  7177. <guid isPermaLink="false">https://www.pagetable.com/?p=1421</guid>
  7178. <description><![CDATA[All disk drives connected to the Serial Bus of a Commodore 64 speak the Commodore DOS protocol, from the popular 1541 5.25&#8243; drive to the modern sd2iec SD card interfaces. CMDR-DOS is a new and open source implementation of the Commodore DOS protocol, using SD cards with the FAT32 filesystem and supporting advances features like ... <a title="CMDR-DOS: Commodore DOS on FAT32" class="read-more" href="https://www.pagetable.com/?p=1421">Read more<span class="screen-reader-text">CMDR-DOS: Commodore DOS on FAT32</span></a>]]></description>
  7179. <content:encoded><![CDATA[<p>All disk drives connected to the Serial Bus of a Commodore 64 speak the <a href="https://www.pagetable.com/?p=1038">Commodore DOS</a> protocol, from the popular 1541 5.25&#8243; drive to the modern <a href="https://sd2iec.de">sd2iec</a> SD card interfaces. <strong>CMDR-DOS</strong> is a new and open source implementation of the Commodore DOS protocol, using SD cards with the FAT32 filesystem and supporting advances features like partitions, subdirectories and timestamps – and running on a 65c02!</p>
  7180. <h2 id="commander-x16">Commander X16</h2>
  7181. <p>It is the built-in DOS of the <a href="https://www.commanderx16.com">Commander X16</a>, and runs on the main CPU, so the KERNAL API (<code>talk</code>, <code>tksa</code>, <code>untlk</code>, <code>listn</code>, <code>secnd</code>, <code>unlsn</code>, <code>acptr</code>, <code>ciout</code>) calls directly into the DOS implementation. This allows <code>LOAD</code> speeds of about 140 KB/sec on an 8 MHz system.</p>
  7182. <p>Demo:</p>
  7183. <p><img src="docs/cmdr-dos.gif" alt="" /></p>
  7184. <p>Transcript:</p>
  7185. <pre style="overflow:scroll; height:200px;"><code>DOS"$=P":REM THERE ARE TWO PARTITIONS ON THIS SD-CARD
  7186.  
  7187. 255 "CMDR-DOS SD CARD"  MBR  
  7188. 1    "PART1"            FAT32  
  7189. 2    "PART2"            FAT32
  7190.  
  7191. READY.  
  7192. DOS"N1:SYSTEM,1616,FAT32":REM FORMAT PARTITION 1
  7193.  
  7194. READY.  
  7195. DOS"N2:DATA,1617,FAT32":REM FORMAT PARTITION 2
  7196.  
  7197. READY.  
  7198. DOS"$=P":REM THE NEW NAMES OF THE TWO PARTITIONS
  7199.  
  7200. 255 "CMDR-DOS SD CARD"  MBR  
  7201. 1    "SYSTEM"           FAT32  
  7202. 2    "DATA"             FAT32
  7203.  
  7204. READY.  
  7205. DOS"CP1":REM SWITCH TO PARTITION 1
  7206.  
  7207. READY.  
  7208. DOS"$":REM SHOW DIRECTORY
  7209.  
  7210. 0 "SYSTEM          " FAT32  
  7211. 99 MB FREE.
  7212.  
  7213. READY.  
  7214. OPEN1,8,2,"HELLO,P,W":PRINT#1,"HELLO WORLD!":CLOSE1:REM CREATE FILE
  7215.  
  7216. READY.  
  7217. DOS"$"
  7218.  
  7219. 0 "SYSTEM          " FAT32  
  7220. 1    "HELLO"            PRG  
  7221. 99 MB FREE.
  7222.  
  7223. READY.  
  7224. DOS"C:WORLD=HELLO":REM DUPLICATE FILE
  7225.  
  7226. READY.  
  7227. DOS"$"
  7228.  
  7229. 0 "SYSTEM          " FAT32  
  7230. 1    "HELLO"            PRG  
  7231. 1    "WORLD"            PRG  
  7232. 99 MB FREE.
  7233.  
  7234. READY.  
  7235. DOS"C:HELLO WORLD=HELLO,WORLD":REM CONCATENATE FILES
  7236.  
  7237. READY.  
  7238. DOS"$"
  7239.  
  7240. 0 "SYSTEM          " FAT32  
  7241. 1    "HELLO"            PRG  
  7242. 1    "WORLD"            PRG  
  7243. 1    "HELLO WORLD"      PRG  
  7244. 99 MB FREE.
  7245.  
  7246. READY.  
  7247. DOS"MD:SECRET":REM CREATE SUBDIRECTORY
  7248.  
  7249. READY.  
  7250. DOS"$"
  7251.  
  7252. 0 "SYSTEM          " FAT32  
  7253. 1    "HELLO"            PRG  
  7254. 1    "WORLD"            PRG  
  7255. 1    "HELLO WORLD"      PRG  
  7256. 0    "SECRET"           DIR  
  7257. 99 MB FREE.
  7258.  
  7259. READY.  
  7260. DOS"$//SECRET/:":REM SHOW SUBDIR CONTENTS
  7261.  
  7262. 0 "SYSTEM          " FAT32  
  7263. 0    "."                DIR  
  7264. 0    ".."               DIR  
  7265. 99 MB FREE.
  7266.  
  7267. READY.  
  7268. DOS"CD:SECRET":REM CHANGE TO SUBDIR
  7269.  
  7270. READY.  
  7271. DOS"$"
  7272.  
  7273. 0 "SYSTEM          " FAT32  
  7274. 0    "."                DIR  
  7275. 0    ".."               DIR  
  7276. 99 MB FREE.
  7277.  
  7278. READY.  
  7279. DOS"C:SECRET HELLO=//:HELLO":REM COPY FILE FROM ROOT TO HERE
  7280.  
  7281. READY.  
  7282. DOS"CD:_":REM CHANGE BACK UP
  7283.  
  7284. READY.  
  7285. DOS"CP2":REM CHANGE TO PARTITION 2
  7286.  
  7287. READY.  
  7288. DOS"$
  7289.  
  7290. 0 "DATA            " FAT32  
  7291. 98 MB FREE.
  7292.  
  7293. READY.  
  7294. DOS"C:DATA FILE=1//SECRET/:SECRET HELLO":REM COPY FILE FROM PARTITION 1
  7295.  
  7296. READY.  
  7297. DOS"$
  7298.  
  7299. 0 "DATA            " FAT32  
  7300. 1    "DATA FILE"        PRG  
  7301. 98 MB FREE.
  7302.  
  7303. READY.  
  7304. DOS"$1:":REM SHOW DIRECTORY OF PARTITION 1
  7305.  
  7306. 1 "SYSTEM          " FAT32  
  7307. 1    "HELLO"            PRG  
  7308. 1    "WORLD"            PRG  
  7309. 1    "HELLO WORLD"      PRG  
  7310. 0    "SECRET"           DIR  
  7311. 99 MB FREE.
  7312.  
  7313. READY.  
  7314. DOS"S1:H*":REM DELETE ALL FILES THERE STARTING WITH H
  7315.  
  7316. READY.  
  7317. DOS:REM THIS WILL SAY THAT "02" FILES WERE DELETED  
  7318. 01, FILES SCRATCHED,02,00
  7319.  
  7320. READY.  
  7321. DOS"CP1":REM CHANGE BACK TO PARTITION 1
  7322.  
  7323. READY.  
  7324. DOS"$
  7325.  
  7326. 0 "SYSTEM          " FAT32  
  7327. 1    "WORLD"            PRG  
  7328. 0    "SECRET"           DIR  
  7329. 99 MB FREE.
  7330.  
  7331. READY.  
  7332. DOS"S:*":REM DELETE ALL REMAINING FILES
  7333.  
  7334. READY.  
  7335. DOS:REM THIS WILL SAY THAT "01" FILE WAS DELETED  
  7336. 01, FILES SCRATCHED,01,00
  7337.  
  7338. READY.  
  7339. DOS"$":REM THE DIRECTORY IS STILL THERE
  7340.  
  7341. 0 "SYSTEM          " FAT32  
  7342. 0    "SECRET"           DIR  
  7343. 99 MB FREE.
  7344.  
  7345. READY.  
  7346. DOS"RD:SECRET":REM DELETE IT
  7347.  
  7348. READY.  
  7349. DOS:REM "00" FILES DELETED, BECAUSE DIR WAS NOT EMPTY  
  7350. 01, FILES SCRATCHED,00,00
  7351.  
  7352. READY.  
  7353. DOS"S//SECRET/:*":REM DELETE ALL FILES INSIDE
  7354.  
  7355. READY.  
  7356. DOS:REM "01" FILE DELETED  
  7357. 01, FILES SCRATCHED,01,00
  7358.  
  7359. READY.  
  7360. DOS"RD:SECRET":REM NOW TRY DELING THE DIR AGAIN
  7361.  
  7362. READY.  
  7363. DOS:REM "01" FILES DELETED, IT WORKED THIS TIME  
  7364. 01, FILES SCRATCHED,01,00
  7365.  
  7366. READY.  
  7367. DOS"$
  7368.  
  7369. 0 "SYSTEM          " FAT32  
  7370. 99 MB FREE.
  7371.  
  7372. READY.  
  7373. REM THAT'S IT. :)
  7374.  
  7375. READY.
  7376. </code></pre>
  7377. <h2 id="source">Source</h2>
  7378. <p>The implementation is part of the Commander X16 ROM and available here:</p>
  7379. <p><a href="https://github.com/commanderx16/x16-rom/tree/master/dos">https://github.com/commanderx16/x16-rom/tree/master/dos</a></p>
  7380. <h2 id="future">Future</h2>
  7381. <p>The codebase is very versatile and could be reused for other kinds of projects:</p>
  7382. <h3 id="other-new-retro-machines">Other New Retro Machines</h3>
  7383. <p>CMDR-DOS could be easily ported to other Commodore-like 65c02+ systems like the <a href="https://mega65.org">MEGA65</a> and the <a href="https://www.c256foenix.com">C256 Foenix</a>, providing a DOS interface to FAT32 on those platforms.</p>
  7384. <h3 id="sd2iec-like-device">sd2iec-like Device</h3>
  7385. <p>Functionality-wise, the CMDR-DOS codebase is also very similar to what sd2iec does – minus the Commodore Serial part. It could be ported a device like the <a href="https://www.forum64.de/index.php?thread/102472-1581replica-gotek1581-1581-pc-drive-adapter/">1581replica</a>, with an SD card attached instead of a disk drive, and one would have a 65c02-based sdi2ec-like device.</p>
  7386. ]]></content:encoded>
  7387. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1421</wfw:commentRss>
  7388. <slash:comments>3</slash:comments>
  7389. </item>
  7390. <item>
  7391. <title>FAT32 Filesystem for the 65c02</title>
  7392. <link>https://www.pagetable.com/?p=1416</link>
  7393. <comments>https://www.pagetable.com/?p=1416#comments</comments>
  7394. <pubDate>Fri, 21 Aug 2020 07:55:25 +0000</pubDate>
  7395. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  7396. <category><![CDATA[6502]]></category>
  7397. <category><![CDATA[Commodore]]></category>
  7398. <category><![CDATA[DOS]]></category>
  7399. <category><![CDATA[Floppy Disks]]></category>
  7400. <category><![CDATA[GitHub]]></category>
  7401. <category><![CDATA[Operating Systems]]></category>
  7402. <category><![CDATA[X16]]></category>
  7403.  
  7404. <guid isPermaLink="false">https://www.pagetable.com/?p=1416</guid>
  7405. <description><![CDATA[We are presenting the (to our knowledge) first full-featured open source library for 65c02 CPUs for accessing FAT32 formatted disks. The library supports filesystems from 32 MB to 2 TB, can read and write long filenames, subdirectories and time stamps, and can even create new filesystems. It decodes a Master Boot Record (MBR) partitioning table ... <a title="FAT32 Filesystem for the 65c02" class="read-more" href="https://www.pagetable.com/?p=1416">Read more<span class="screen-reader-text">FAT32 Filesystem for the 65c02</span></a>]]></description>
  7406. <content:encoded><![CDATA[<p>We are presenting the (to our knowledge) first full-featured open source library for 65c02 CPUs for accessing FAT32 formatted disks.</p>
  7407. <ul>
  7408. <li>The library supports filesystems from 32 MB to 2 TB, can read and write long filenames, subdirectories and time stamps, and can even create new filesystems.</li>
  7409. <li>It decodes a Master Boot Record (MBR) partitioning table and can have multiple partitions mounted at the same time.</li>
  7410. <li>It comes with a driver for the SD card protocol, so you can hook it to your own SD card solution; all you have to do is implement your own byte transmission code. If you want to use a VIA 65c22, you can hook up the <a href="https://bitbucket.org/steckschwein/steckschwein-code/src/master/steckos/libsrc/spi/">65c22 serial port code by the Steckschwein project</a>.</li>
  7411. <li>Converting character encodings and matching names is done using callbacks – you can use the <a href="https://github.com/commanderx16/x16-rom/blob/master/cbdos/match.s">X16 implementation</a> for a template.</li>
  7412. </ul>
  7413. <p>The API looks like this:</p>
  7414. <pre><code>    ; allocate context for filesystem #0  
  7415.    ; (first MBR partition, mount if necessary)  
  7416.    lda #0  
  7417.    jsr fat32_alloc_context  
  7418.    sta context
  7419.  
  7420.    ; open file  
  7421.    lda #&lt;filename  
  7422.    sta fat32_ptr  
  7423.    lda #&gt;filename  
  7424.    sta fat32_ptr + 1  
  7425.    jsr fat32_open
  7426.  
  7427. loop:  
  7428.    ; read and print byte  
  7429.    jsr fat32_read_byte  
  7430.    bcc end  
  7431.    jsr print_character  
  7432.    jmp loop
  7433.  
  7434. end:  
  7435.    jsr fat32_close
  7436.  
  7437.    lda context  
  7438.    jmp fat32_free_context
  7439.  
  7440.    filename:  
  7441.        .byte '/path/to/file.txt', 0
  7442. </code></pre>
  7443. <p>The implementation uses the 65c02 extensions. With the help of <a href="https://github.com/commanderx16/x16-rom/blob/68cec17c700bd9666dc49f801e0853af4e417ebf/cbdos/65c02.inc">65c02.inc</a> and some simple search-and-replace, it can be adapted for the 6502 though.</p>
  7444. <p>The library was written by Frank van den Hoef, with features (LFN, mkfs, &hellip;) added by Michael Steil.</p>
  7445. <p>It is the core of the DOS in the Commodore-like <a href="https://www.commanderx16.com">Commander X16</a> retro computer. The source is currently maintained as part of the X16 ROM:</p>
  7446. <p><a href="https://github.com/commanderx16/x16-rom/tree/master/dos/fat32">https://github.com/commanderx16/x16-rom/tree/master/dos/fat32</a></p>
  7447. <p>Contributions welcome!</p>
  7448. ]]></content:encoded>
  7449. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1416</wfw:commentRss>
  7450. <slash:comments>6</slash:comments>
  7451. </item>
  7452. <item>
  7453. <title>Building the Tynemouth Mini PET</title>
  7454. <link>https://www.pagetable.com/?p=1408</link>
  7455. <comments>https://www.pagetable.com/?p=1408#comments</comments>
  7456. <pubDate>Sun, 05 Jul 2020 10:03:41 +0000</pubDate>
  7457. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  7458. <category><![CDATA[6502]]></category>
  7459. <category><![CDATA[Commodore]]></category>
  7460. <category><![CDATA[Hardware]]></category>
  7461. <category><![CDATA[PET]]></category>
  7462. <category><![CDATA[Teardown]]></category>
  7463.  
  7464. <guid isPermaLink="false">https://www.pagetable.com/?p=1408</guid>
  7465. <description><![CDATA[Tynemouth Mini PET at FTW8b.com]]></description>
  7466. <content:encoded><![CDATA[<p><a href="docs/minipet/minipet.gif"><img width="806" height="400" src="docs/minipet/minipet.gif"></a></p>
  7467. <p><a href="https://www.thefuturewas8bit.com/shop/tynemouth-products/mini-pet.html"> Tynemouth Mini PET at FTW8b.com</a></p>
  7468. ]]></content:encoded>
  7469. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1408</wfw:commentRss>
  7470. <slash:comments>2</slash:comments>
  7471. </item>
  7472. <item>
  7473. <title>Ultimate Commodore Charset / PETSCII / Keyboard Reference</title>
  7474. <link>https://www.pagetable.com/?p=1404</link>
  7475. <comments>https://www.pagetable.com/?p=1404#comments</comments>
  7476. <pubDate>Tue, 09 Jun 2020 09:29:07 +0000</pubDate>
  7477. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  7478. <category><![CDATA[6502]]></category>
  7479. <category><![CDATA[Archeology]]></category>
  7480. <category><![CDATA[C64]]></category>
  7481. <category><![CDATA[Commodore]]></category>
  7482. <category><![CDATA[KERNAL]]></category>
  7483. <category><![CDATA[PET]]></category>
  7484. <category><![CDATA[TED]]></category>
  7485. <category><![CDATA[VIC-20]]></category>
  7486.  
  7487. <guid isPermaLink="false">https://www.pagetable.com/?p=1404</guid>
  7488. <description><![CDATA[Another addition to the The Ultimate C64 Reference: We&#8217;re adding character sets, PETSCII codes and keyboard layouts – supporting eight different Commodore computers. There are three different (related) modes: Character Sets, PETSCII and Keyboard. The controls on the left switch some global settings: Character Set lets you select the Commodore 8&#215;8 charset to be used ... <a title="Ultimate Commodore Charset / PETSCII / Keyboard Reference" class="read-more" href="https://www.pagetable.com/?p=1404">Read more<span class="screen-reader-text">Ultimate Commodore Charset / PETSCII / Keyboard Reference</span></a>]]></description>
  7489. <content:encoded><![CDATA[<p>Another addition to the <em>The Ultimate C64 Reference</em>: We&rsquo;re adding <a href="http://www.pagetable.com/c64ref/charset/">character sets, PETSCII codes and keyboard layouts</a> – supporting eight different Commodore computers.</p>
  7490. <p><a href="docs/c64ref_charset/c64ref_charset-1.png"><img src="docs/c64ref_charset/c64ref_charset-1_small.png" height="479" width="600" alt="" /></a></p>
  7491. <p>There are three different (related) modes: Character Sets, PETSCII and Keyboard. The controls on the left switch some global settings:</p>
  7492. <p><a href="docs/c64ref_charset/c64ref_charset-2.png"><img src="docs/c64ref_charset/c64ref_charset-2.png" height="256" width="391" alt="" /></a></p>
  7493. <ul>
  7494. <li><strong>Character Set</strong> lets you select the Commodore 8&#215;8 charset to be used in all modes. The drop-down list contains 84 ROM-extracted charsets.</li>
  7495. </ul>
  7496. <p><img src="docs/c64ref_charset/charsets.gif" height="296" width="300" alt="" /></p>
  7497. <ul>
  7498. <li><strong>Control Codes</strong> specifies which set of PETSCII control codes will be visualized in the PETSCII table.</li>
  7499. </ul>
  7500. <p><img src="docs/c64ref_charset/petscii.gif" height="296" width="300" alt="" /></p>
  7501. <ul>
  7502. <li><strong>Color Scheme</strong> matches the charset to the color scheme of a specific computer, or shows it in black-on-white.</li>
  7503. </ul>
  7504. <p><img src="docs/c64ref_charset/colorscheme.gif" height="296" width="300" alt="" /></p>
  7505. <ul>
  7506. <li><strong>Aspect Ratio</strong> controls the width-to-height ratio of the character set displayed, showing them either with square pixels, or matching one of the computers.</li>
  7507. </ul>
  7508. <p><img src="docs/c64ref_charset/aspectratio.gif" height="296" width="300" alt="" /></p>
  7509. <ul>
  7510. <li>By clicking one of the radio boxes next to the computer names, the four settings above will be set to match a specific computer.</li>
  7511. </ul>
  7512. <p><img src="docs/c64ref_charset/machine.gif" height="296" width="300" alt="" /></p>
  7513. <ul>
  7514. <li>The checkboxes next to the computer names allow viewing the PETSCII control codes, keyboards and keyboard combinations of multiple computers in all other views.</li>
  7515. </ul>
  7516. <h2 id="character-sets">Character Sets</h2>
  7517. <p>The <strong>Character Sets</strong> tab shows all 128 characters of the currently selected charset as well as its inverted form, sorted by screen code. You can click on a character to view its screen code, PETSCII and Unicode values, as well as the keyboard combinations that produce this key on the different machines.</p>
  7518. <p><img src="docs/c64ref_charset/charset-detail.png" height="379" width="411" alt="" /></p>
  7519. <p>Below, there is a table showing all character sets at the same time. It can be filtered to only show upper case or lower case charsets.</p>
  7520. <p><a href="docs/c64ref_charset/charset-table.png"><img src="docs/c64ref_charset/charset-table.png" height="312" width="658" alt="" /></a></p>
  7521. <p>In addition, there is a function that lets you compare two character sets. The middle line shows the XOR of the two charsets. This example shows that several lower case letters were optimized from the C64 to the TED:</p>
  7522. <p><img src="docs/c64ref_charset/charset-compare.png" height="79" width="472" alt="" /></p>
  7523. <h2 id="petscii">PETSCII</h2>
  7524. <p>The <strong>PETSCII</strong> tab visualizes the 256 PETSCII codes. Codes 0x00 to 0x1F (the first two rows) and codes $80 to $9F (rows 9 and 10) are control codes, the others are printable. Clicking on a code will reveal the PETSCII value, the screen code and the keyboard combinations. For control codes, it shows the functions on the different computers, and for printable characters it shows the Unicode equivalent.</p>
  7525. <p><img src="docs/c64ref_charset/petscii-detail-1.png" height="262" width="273" alt="" /><img src="docs/c64ref_charset/petscii-detail-2.png" height="267" width="273" alt="" /></p>
  7526. <p>The table below shows this information for all codes in one place. Here is a part of it comparing some keyboard combinations and control codes between three different computers:</p>
  7527. <p><a href="docs/c64ref_charset/petscii-table.png"><img src="docs/c64ref_charset/petscii-table.png" height="341" width="650" alt="" /></a></p>
  7528. <h2 id="keyboard">Keyboard</h2>
  7529. <p>The <strong>Keyboard</strong> tab shows the keyboard layouts of the different computers and lets you explore which PETSCII codes and characters are generated by which key combinations.</p>
  7530. <p><a href="docs/c64ref_charset/keyboard-1.png"><img src="docs/c64ref_charset/keyboard-1.png" height="356" width="278" alt="" /></a><a href="docs/c64ref_charset/keyboard-2.png"><img src="docs/c64ref_charset/keyboard-2.png" height="285" width="276" alt="" /></a></p>
  7531. <p>In the screenshot above, you can see the three different keyboards of the computers from the TED series: the C16, the C116 and the Plus/4.</p>
  7532. <h2 id="contributing">Contributing</h2>
  7533. <p>Like all web pages of the Ultimate C64 Reference, these view are generated from independent formatted ASCII files. The C64 keyboard file looks like this, for example:</p>
  7534. <p><img src="docs/c64ref_charset/keyboard-file.png" height="416" width="608" alt="" /></p>
  7535. <p>It contains the ASCII-art of the layout, which is converted into SVG graphics by a Python script, the key caps, information about modifiers as well as the scancode-to-PETSCII tables.</p>
  7536. <p>The Ultimate C64 Reference is being developed as an open source project at <a href="https://github.com/mist64/c64ref">github.com/mist64/c64ref</a> &#8211; contributions in the form of additions, corrections etc. are welcome!</p>
  7537. ]]></content:encoded>
  7538. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1404</wfw:commentRss>
  7539. <slash:comments>7</slash:comments>
  7540. </item>
  7541. <item>
  7542. <title>Ultimate C64 KERNAL API Reference</title>
  7543. <link>https://www.pagetable.com/?p=1401</link>
  7544. <comments>https://www.pagetable.com/?p=1401#comments</comments>
  7545. <pubDate>Wed, 03 Jun 2020 21:35:41 +0000</pubDate>
  7546. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  7547. <category><![CDATA[6502]]></category>
  7548. <category><![CDATA[Archeology]]></category>
  7549. <category><![CDATA[BASIC]]></category>
  7550. <category><![CDATA[C64]]></category>
  7551. <category><![CDATA[Commodore]]></category>
  7552. <category><![CDATA[KERNAL]]></category>
  7553.  
  7554. <guid isPermaLink="false">https://www.pagetable.com/?p=1401</guid>
  7555. <description><![CDATA[The Ultimate C64 Reference is growing again: This time, we&#8217;re adding the KERNAL API reference – as always, in eleven different versions side-by-side. These are the references that have been adapted for this: Commodore 64 Programmer&#8217;s Reference Guide, ISBN 0-672-22056-3 COMPUTE!&#8217;s VIC-20 and Commodore 64 Tool Kit: Kernal by Dan Heeb, ISBN 0942386337 Machine Language ... <a title="Ultimate C64 KERNAL API Reference" class="read-more" href="https://www.pagetable.com/?p=1401">Read more<span class="screen-reader-text">Ultimate C64 KERNAL API Reference</span></a>]]></description>
  7556. <content:encoded><![CDATA[<p>The Ultimate C64 Reference is growing again: This time, we&rsquo;re adding the <a href="http://www.pagetable.com/c64ref/kernal/">KERNAL API reference</a> – as always, in <strong>eleven</strong> different versions side-by-side.</p>
  7557. <p><a href="docs/c64ref_kernal/c64ref_kernal-1.png"><img src="docs/c64ref_kernal/c64ref_kernal-1.png" height="468" width="796" alt="" /></a></p>
  7558. <p>These are the references that have been adapted for this:</p>
  7559. <ul>
  7560. <li><a href="https://github.com/Project-64/reloaded/blob/master/c64/c64prg/C64PRG11.TXT">Commodore 64 Programmer&rsquo;s Reference Guide</a>, ISBN 0-672-22056-3</li>
  7561. <li><a href="https://archive.org/details/COMPUTEs_VIC-20_and_Commodore_64_Tool_Kit_Kernal_1985_COMPUTE_Publications_a">COMPUTE!&rsquo;s VIC-20 and Commodore 64 Tool Kit: Kernal</a> by Dan Heeb, ISBN 0942386337</li>
  7562. <li><a href="https://archive.org/details/Compute_s_Machine_Language_Routines_for_the_Commodore_64_and_128">Machine Language Routines for the Commodore 64 and 128</a> by Todd D Heimarck and Patrick Parrish, ISBN 0874550858</li>
  7563. <li><a href="https://github.com/Project-64/reloaded/blob/master/c64/mapc64/MAPC6412.TXT">Mapping the Commodore 64</a> by Sheldon Leemon, ISBN 0-942386-23-X</li>
  7564. <li><a href="https://www.retrozone.ch/docs/c128/Commodore128Intern.pdf">Commodore 128 intern</a> by Jörg Schieb, Frank Thrun and Heinz Wrobel, ISBN 3-89011-098-3</li>
  7565. <li><a href="https://github.com/Project-64/reloaded/blob/master/c64/firmware/C64LD11.S">The almost completely commented C64 ROM disassembly</a> by Lee Davison</li>
  7566. <li><a href="https://www.atarimagazines.com/compute/issue40/cracking_the_kernal.php">Cracking The Kernal</a> by Peter Marcotty in COMPUTE! #40, September 1983, pp. 268-274</li>
  7567. <li><a href="http://csbruce.com/cbm/hacking/hacking03.txt">Kernal 64 / 128</a> by Craig Taylor in C= Hacking, Volume 1, Issue 3; July 15, 1992</li>
  7568. <li><a href="https://sta.c64.org/cbm64krnfunc.html">Commodore 64 standard KERNAL functions</a> by Joe Forster/STA</li>
  7569. <li><a href="http://www.zimmers.net/anonftp/pub/cbm/c64/programming/documents/c64-kernal.txt">C64 KERNAL jump table</a> by Frank Kontros</li>
  7570. <li><a href="https://www.pagetable.com/?p=1015">Das neue Commodore-64-intern-Buch</a> by Baloui, Brückmann, Englisch, Felt, Gelfand, Gerits and Krsnik, ISBN 3890113079</li>
  7571. </ul>
  7572. <p>You can enable and disable columns by clicking the checkboxes next to the sources, and you can expand/collapse all details with the corresponding button above the table.</p>
  7573. <p>Here are four different expanded explanations of the <code>SCNKEY</code> call ($FF9F):</p>
  7574. <p><a href="docs/c64ref_kernal/c64ref_kernal-2.png"><img src="docs/c64ref_kernal/c64ref_kernal-2.png" height="139" width="747" alt="" /></a></p>
  7575. <p>As you can see, KERNAL API symbols as well as zeropage/variable symbols and addresses are cross-referenced and link to the respective description.</p>
  7576. <p>Like all web pages of the Ultimate C64 Reference, this table is generated from independent formatted ASCII files. In the case of the KERNAL API, these files look like this:</p>
  7577. <p><a href="docs/c64ref_kernal/c64ref_kernal-3.png"><img src="docs/c64ref_kernal/c64ref_kernal-3.png" height="519" width="633" alt="" /></a></p>
  7578. <p>It consists of three columns: the address in hex, the symbol name and the description in MarkDown format.</p>
  7579. <p>The Ultimate C64 Reference is being developed as an open source project at <a href="https://github.com/mist64/c64ref">github.com/mist64/c64ref</a> &#8211; contributions in the form of additions, corrections etc. are welcome!</p>
  7580. ]]></content:encoded>
  7581. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1401</wfw:commentRss>
  7582. <slash:comments>3</slash:comments>
  7583. </item>
  7584. <item>
  7585. <title>Ultimate C64 Memory Map</title>
  7586. <link>https://www.pagetable.com/?p=1397</link>
  7587. <comments>https://www.pagetable.com/?p=1397#comments</comments>
  7588. <pubDate>Fri, 15 May 2020 19:26:41 +0000</pubDate>
  7589. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  7590. <category><![CDATA[6502]]></category>
  7591. <category><![CDATA[Archeology]]></category>
  7592. <category><![CDATA[BASIC]]></category>
  7593. <category><![CDATA[C64]]></category>
  7594. <category><![CDATA[Commodore]]></category>
  7595. <category><![CDATA[GitHub]]></category>
  7596. <category><![CDATA[KERNAL]]></category>
  7597.  
  7598. <guid isPermaLink="false">https://www.pagetable.com/?p=1397</guid>
  7599. <description><![CDATA[The system software of the Commodore 64 has been extensively reverse-engineered. Next to disassemblies of the ROM, several &#8220;memory maps&#8221; have been published: tables that document system variables in the first kilobyte of RAM, and how to tweak the system software with PEEK and POKE. Now, I&#8217;m presenting the Ultimate C64 Memory Map: A C64 ... <a title="Ultimate C64 Memory Map" class="read-more" href="https://www.pagetable.com/?p=1397">Read more<span class="screen-reader-text">Ultimate C64 Memory Map</span></a>]]></description>
  7600. <content:encoded><![CDATA[<p>The system software of the Commodore 64 has been extensively reverse-engineered. Next to <a href="https://www.pagetable.com/c64ref/c64disasm/">disassemblies</a> of the ROM, several &ldquo;memory maps&rdquo; have been published: tables that document system variables in the first kilobyte of RAM, and how to tweak the system software with <code>PEEK</code> and <code>POKE</code>. Now, I&rsquo;m presenting the <a href="https://www.pagetable.com/c64ref/c64mem/">Ultimate C64 Memory Map</a>: A C64 memory reference that shows eight sources side-by-side.</p>
  7601. <p><img src="docs/c64mem/c64mem-1.png" height="385" width="708" alt="" /></p>
  7602. <p>These are the references that have been adapted for this:</p>
  7603. <ul>
  7604. <li>Reference from <a href="http://unusedino.de/ec64/technical/project64/mapping_c64.html">Mapping the Commodore 64</a> by Sheldon Leemon, ISBN 0-942386-23-X.</li>
  7605. <li>German-language reference from <a href="https://archive.org/details/64er_sonderheft_1986_07/page/n6/mode/2up">Memory Map mit Wandervorschlägen</a> by Dr. H. Hauck, in 64&#8217;er Sonderheft 1986/07.</li>
  7606. <li>German-language reference from <a href="https://www.pagetable.com/?p=1015">Das neue Commodore-64-intern-Buch</a> by Data Becker, ISBN 3890113079.</li>
  7607. <li>Reference by <a href="https://sta.c64.org/cbm64mem.html">Joe Forster/STA</a>, with <a href="http://www.awsm.de/mem64/">awsm&rsquo;s</a> changes applied.</li>
  7608. <li>Comments from the <a href="https://github.com/mist64/cbmsrc">original M6502 BASIC source by Microsoft and the original C64 KERNAL source by Commodore</a></li>
  7609. <li>Reference from <a href="http://www.zimmers.net/cbmpics/cbm/c64/c64prg.txt">Commodore 64 Programmer&rsquo;s Reference Guide</a>.</li>
  7610. <li>Reference as found in <a href="http://unusedino.de/ec64/technical/project64/memory_maps.html">Commodore 64 Memory Maps.txt</a> by anonymous.</li>
  7611. <li>Reference by <a href="https://www.atarimagazines.com/compute/issue29/394_1_COMMODORE_64_MEMORY_MAP.php">Jim Butterfield</a> in COMPUTE! #29 (October 1982).</li>
  7612. </ul>
  7613. <p>You can enable and disable columns by clicking the checkboxes next to the sources, and you can expand/collapse all details with the corresponding button above the table. Here are four different expanded explanations of the <code>STATUS</code> byte:</p>
  7614. <p><img src="docs/c64mem/c64mem-2.png" height="213" width="715" alt="" /></p>
  7615. <p>And here is the collapsed version of the range $2B-$48, comparing the comments in the original sources with the Programmer&rsquo;s Reference Manual:</p>
  7616. <p><img src="docs/c64mem/c64mem-3.png" height="132" width="595" alt="" /></p>
  7617. <p>The symbols (second column) are taken from the original sources. Sometimes, a single memory location has several meanings and thus several symbols. Some descriptions have been adapted to describe the different meanings independently:</p>
  7618. <p><img src="docs/c64mem/c64mem-4.png" height="78" width="500" alt="" /></p>
  7619. <p>KERNAL and BASIC ROM addresses link to the respective spots in the disassembly:</p>
  7620. <p><img src="docs/c64mem/c64mem-5.png" height="123" width="559" alt="" /></p>
  7621. <p>And in the disassembly, zero page addresses (like <code>$CC</code>) and symbols (like <code>BLNSW</code>) link back to the memory map:</p>
  7622. <p><img src="docs/c64mem/c64mem-6.png" height="84" width="483" alt="" /></p>
  7623. <p>The memory map table is generated from independent formatted ASCII files that look like this:</p>
  7624. <p><img src="docs/c64mem/c64mem-7.png" height="286" width="534" alt="" /></p>
  7625. <p>It consists of three columns: the address range in hex, the symbol name and the description in MarkDown format.</p>
  7626. <p>The Ultimate C64 Reference is being developed as an open source project at <a href="https://github.com/mist64/c64ref">github.com/mist64/c64ref</a> &#8211; contributions in the form of additions, corrections etc. are welcome!</p>
  7627. ]]></content:encoded>
  7628. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1397</wfw:commentRss>
  7629. <slash:comments>8</slash:comments>
  7630. </item>
  7631. <item>
  7632. <title>Typos in the C64 Programmer&#8217;s Reference Guide: C3PO or C3P0?</title>
  7633. <link>https://www.pagetable.com/?p=1391</link>
  7634. <comments>https://www.pagetable.com/?p=1391#comments</comments>
  7635. <pubDate>Sun, 10 May 2020 19:23:36 +0000</pubDate>
  7636. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  7637. <category><![CDATA[6502]]></category>
  7638. <category><![CDATA[Archeology]]></category>
  7639. <category><![CDATA[C64]]></category>
  7640. <category><![CDATA[Commodore]]></category>
  7641. <category><![CDATA[KERNAL]]></category>
  7642.  
  7643. <guid isPermaLink="false">https://www.pagetable.com/?p=1391</guid>
  7644. <description><![CDATA[The Commodore 64 Programmer&#8217;s Reference Guide contains a memory map with a complete description of the zeropage and system variables used by KERNAL and BASIC, but now that we have the original source, we know there are three typos in this table. C3P0 The first one is the symbol at address $94. Here is the ... <a title="Typos in the C64 Programmer&#8217;s Reference Guide: C3PO or C3P0?" class="read-more" href="https://www.pagetable.com/?p=1391">Read more<span class="screen-reader-text">Typos in the C64 Programmer&#8217;s Reference Guide: C3PO or C3P0?</span></a>]]></description>
  7645. <content:encoded><![CDATA[<p>The <a href="https://archive.org/details/c64-programmer-ref/">Commodore 64 Programmer&rsquo;s Reference Guide</a> contains a memory map with a complete description of the zeropage and system variables used by KERNAL and BASIC, but now that we have the <a href="https://github.com/mist64/cbmsrc">original source</a>, we know there are three typos in this table.</p>
  7646. <h2 id="c3p0">C3P0</h2>
  7647. <p>The first one is the symbol at address $94. Here is the full page from the Programmer&rsquo;s Reference Guide:</p>
  7648. <p><img src="docs/prg_typos/prg-04.png" height="726" width="423" alt="" /></p>
  7649. <p>If you look closely, the symbol at $94 is <code>C3PO</code>, with a capital Oh.</p>
  7650. <p><img src="docs/prg_typos/c3p0-prg.png" height="86" width="180" alt="" /></p>
  7651. <p>But it should end in a zero. Here is the original source:</p>
  7652. <p><img src="docs/prg_typos/c3p0-src.png" height="45" width="366" alt="" /></p>
  7653. <p>This is a <a href="https://www.pagetable.com/docs/C64_KERNAL_03_LST.pdf">printout of the original assembly log from 1983</a>:</p>
  7654. <p><img src="docs/prg_typos/c3p0-lst.png" height="30" width="404" alt="" /></p>
  7655. <p>On the kind of printer Commodore was using, there is no way to tell a <code>0</code> from an <code>O</code>. Here is the whole page for context:</p>
  7656. <p><img src="docs/prg_typos/kernal-001.png" height="566" width="400" alt="" /></p>
  7657. <p>The <a href="https://www.pagetable.com/?p=1135">Commodore Serial Bus</a> library uses <code>C3P0</code> to buffer a byte that is supposed to be sent to the bus &#8211; similar to the zero page location <code>R2D2</code> located at address $A3. Here is <a href="https://www.pagetable.com/c64disasm/#ED16">some code</a> that uses the two:</p>
  7658. <p><img src="docs/prg_typos/c3p0-r2d2.png" height="205" width="337" alt="" /></p>
  7659. <p>So yes, the two symbol names are references to the Star Wars characters C-3PO and R2-D2. It is unknown why the former is spelled with a zero in the source, but the typo in the Programmer&rsquo;s Reference Guide is very forgivable.</p>
  7660. <p>Disappointingly, the <code>R2D2</code> symbol is not mentioned in the Programmer&rsquo;s Reference Guide &#8211; $A3 is just part of a &ldquo;Temp Data Area&rdquo;:</p>
  7661. <p><img src="docs/prg_typos/a3.png" height="17" width="422" alt="" /></p>
  7662. <h2 id="bufpt">BUFPT</h2>
  7663. <p>The next typo is the symbol at address $A6, the tape buffer pointer:</p>
  7664. <p><img src="docs/prg_typos/bufpt-prg.png" height="18" width="423" alt="" /></p>
  7665. <p>The Reference Guide calls it <code>BUFPNT</code>, but it is in fact called <code>BUFPT</code>.</p>
  7666. <p><img src="docs/prg_typos/bufpt-lst.png" height="14" width="562" alt="" /></p>
  7667. <h2 id="lsxp">LSXP</h2>
  7668. <p>And the final typo is the symbol at $C9:</p>
  7669. <p><img src="docs/prg_typos/lsxp-prg.png" height="32" width="420" alt="" /></p>
  7670. <p>The correct spelling is <code>LSXP</code>:</p>
  7671. <p><img src="docs/prg_typos/lsxp-lst.png" height="29" width="484" alt="" /></p>
  7672. <p>In fact, in the source, the two bytes have individual labels, <code>LSXP</code> and <code>LSTP</code>. <code>LSXP</code> is the cursor line, and <code>LSTP</code> the cursor column in the context of inputting text, shadowing the zero page addresses <code>TBLX</code> (line) and <code>PNTR</code> (column). The following table summarizes this:</p>
  7673. <table>
  7674. <thead>
  7675. <tr>
  7676. <th>        </th>
  7677. <th> Generic </th>
  7678. <th> Input  </th>
  7679. </tr>
  7680. </thead>
  7681. <tbody>
  7682. <tr>
  7683. <td> Column </td>
  7684. <td> <code>PNTR</code>  </td>
  7685. <td> <code>LSTP</code> </td>
  7686. </tr>
  7687. <tr>
  7688. <td> Line   </td>
  7689. <td> <code>TBLX</code>  </td>
  7690. <td> <code>LSXP</code> </td>
  7691. </tr>
  7692. </tbody>
  7693. </table>
  7694. <p>It is confusing that the symbols for the Y coordinates each contain an <code>X</code>. It is unclear what these symbol names are supposed to mean in the first place &#8211; if you have any idea, please share them in the comments!</p>
  7695. ]]></content:encoded>
  7696. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1391</wfw:commentRss>
  7697. <slash:comments>3</slash:comments>
  7698. </item>
  7699. <item>
  7700. <title>Dumping MiniDisc Media</title>
  7701. <link>https://www.pagetable.com/?p=1384</link>
  7702. <comments>https://www.pagetable.com/?p=1384#comments</comments>
  7703. <pubDate>Fri, 27 Mar 2020 06:41:56 +0000</pubDate>
  7704. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  7705. <category><![CDATA[Archeology]]></category>
  7706. <category><![CDATA[Floppy Disks]]></category>
  7707. <category><![CDATA[Hacks]]></category>
  7708. <category><![CDATA[Hardware]]></category>
  7709.  
  7710. <guid isPermaLink="false">https://www.pagetable.com/?p=1384</guid>
  7711. <description><![CDATA[Update 2022: Web MiniDisc Pro can access NetMD devices through the browser and can also download protected files! If you have music on a collection of MiniDisc media and want to finally copy the data off onto modern media (or the cloud!), here are simple instructions for some different solutions: MZ-RH1 and NetMDPython MZ-RH1 and ... <a title="Dumping MiniDisc Media" class="read-more" href="https://www.pagetable.com/?p=1384">Read more<span class="screen-reader-text">Dumping MiniDisc Media</span></a>]]></description>
  7712. <content:encoded><![CDATA[<p><big><b><font color="red">Update 2022: <a href="https://web.minidisc.wiki/">Web MiniDisc Pro</a> can access NetMD devices through the browser and can also download protected files!</font></b></big></p>
  7713. <hr/>
  7714. <p>If you have music on a collection of MiniDisc media and want to finally copy the data off onto modern media (or the cloud!), here are simple instructions for some different solutions:</p>
  7715. <ul>
  7716. <li>MZ-RH1 and NetMDPython</li>
  7717. <li>MZ-RH1 and SonicStage</li>
  7718. <li>Analog Copy</li>
  7719. <li>Bonus: Recovering Deleted Tracks</li>
  7720. </ul>
  7721. <h2 id="mz-rh1-and-netmdpython">MZ-RH1 and NetMDPython</h2>
  7722. <p>The best device for digitally dumping MiniDiscs is Sony&rsquo;s last (and best) MiniDisc recorder, the portable MZ-RH1. Unfortunately, this makes it quite pricy on the used market these days – but you can (and should!) always sell it after you&rsquo;re done dumping your media&hellip;</p>
  7723. <h3 id="dumping">Dumping</h3>
  7724. <p>The <a href="https://wiki.physik.fu-berlin.de/linux-minidisc/doku.php?id=netmdpython">NetMDPython</a> set of scripts can copy the original <a href="https://en.wikipedia.org/wiki/Adaptive_Transform_Acoustic_Coding">ATRAC1</a>-encoded bitstreams off MiniDiscs. You can run it on Windows, macOS or Linux. I will describe the Linux steps with Ubuntu – I would advise Windows and Mac users to set up a virtual machine with Ubuntu, since this is the most reliable way.</p>
  7725. <ul>
  7726. <li>
  7727. <p>First, you need to make sure that you have Git, Python 2 (with crypto support) and libusb installed:</p>
  7728. <pre><code>  sudo apt-get install git python2 python-crypto libusb-dev
  7729. </code></pre>
  7730. </li>
  7731. <li>
  7732. <p>Then, get the source from the <em>linux-minidisc</em> project:</p>
  7733. <pre><code>  git clone https://github.com/glaubitz/linux-minidisc.git
  7734. </code></pre>
  7735. </li>
  7736. <li>
  7737. <p>The tools are in <code>linux-minidisc/netmd</code></p>
  7738. <pre><code>  cd linux-minidisc/netmd
  7739. </code></pre>
  7740. </li>
  7741. <li>
  7742. <p>Connect your MZ-RH1 to the (virtual) machine, but do not insert a MiniDisc yet. The Linux <code>usb-storage</code> driver would claim any inserted media, so that the tools wouldn&rsquo;t be able to access it any more. Therefore, you need to remove the <code>usb-storage</code> driver:</p>
  7743. <pre><code>  sudo modprobe -r usb-storage
  7744. </code></pre>
  7745. </li>
  7746. <li>
  7747. <p>Now make sure that the MZ-RH1 shows up:</p>
  7748. <pre><code>  sudo ./lsusb.py
  7749.  
  7750.  [...]  
  7751.  Bus 002 Device 006: ID 054c:0286 Sony Net MD/Hi-MD  
  7752.  [...]
  7753. </code></pre>
  7754. </li>
  7755. <li>
  7756. <p>If your device is detected, you can list the contents of the MiniDisc like this:</p>
  7757. <pre><code>  sudo ./lsmd.py
  7758.  
  7759.  Disk (writable media) We Are The Night  
  7760.  Time used: 01:09:50+032 (87.14%)  
  7761.  14 tracks  
  7762.  000: 00:01:04+027 sp stereo unprotected No Path To Follow  
  7763.  001: 00:06:33+039 sp stereo unprotected We Are The Night  
  7764.  [...]
  7765. </code></pre>
  7766. </li>
  7767. <li>
  7768. <p>You can dump the whole media like this:</p>
  7769. <pre><code>  sudo ./upload.py
  7770.  
  7771.  Storing in We Are The Night  
  7772.  Uploading ./01 - No Path To Follow.aea  
  7773.  Done: 10000/1a51a0 (3.80%)  
  7774.  Done: 20000/1a51a0 (7.60%)  
  7775.  Done: 30000/1a51a0 (11.40%)  
  7776.  [...]
  7777. </code></pre>
  7778. </li>
  7779. </ul>
  7780. <h3 id="converting-atrac1-files">Converting ATRAC1 Files</h3>
  7781. <p>The resulting files are in <code>.aea</code> format, which is the raw data from the MiniDisc, encoded in the ATRAC1 format.</p>
  7782. <p>You can convert them to any other audio format using <em>ffmpeg</em>:</p>
  7783. <pre><code>    ffmpeg -i file.aea file.wav # decompression  
  7784.    ffmpeg -i file.aea file.mp3 # lossy recompression  
  7785.    ffmpeg -i file.aea file.m4a # lossy recompression
  7786. </code></pre>
  7787. <h3 id="limitations">Limitations</h3>
  7788. <p><em>NetMDPython</em> can not dump tracks that are marked &ldquo;protected&rdquo;. This includes all tracks on pressed MiniDiscs, tracks that have been copied from a digital master, and tracks written with the PC software (<em>SonicStage</em>).</p>
  7789. <h2 id="mz-rh1-and-sonicstage">MZ-RH1 and SonicStage</h2>
  7790. <p>Sony&rsquo;s <em>SonicStage</em> software is an iTunes-like application for Windows that can, among many many other things, transfer most tracks from MiniDisc to the PC&rsquo;s hard disk.</p>
  7791. <p>(The last version is 4.3; the screenshots are showing 3.4. I recommend Windows XP; newer versions might work as well.)</p>
  7792. <ul>
  7793. <li>Navigate to the &ldquo;Transfer&rdquo; Tab. Your MZ-RH1 should show up in the pane on the right.</li>
  7794. </ul>
  7795. <p><img src="docs/dumping_md/sonicstage1.png" height="384" width="512" alt="" /></p>
  7796. <ul>
  7797. <li>By default, <em>SonicStage</em> will recompress dumped data as ATRAC3+. To make sure you don&rsquo;t lose any sound quality, press the toolbox icon, select &ldquo;Advanced&hellip;&rdquo; and &ldquo;Import Settings&rdquo;, and set the format for importing non-MDLP tracks to &ldquo;PCM&rdquo;:</li>
  7798. </ul>
  7799. <p><img src="docs/dumping_md/sonicstage2.png" height="380" width="516" alt="" /><br />
  7800. <img src="docs/dumping_md/sonicstage3.png" height="507" width="460" alt="" /></p>
  7801. <ul>
  7802. <li>Now you can drag and drop tracks from the right pane to the left one. Right clicking a track on the left and selecting &ldquo;Properties&hellip;&rdquo; will reveal the location of the file in the filesystem.</li>
  7803. </ul>
  7804. <h3 id="converting-oma-files">Converting OMA Files</h3>
  7805. <p>The resulting files are in OpenMG Audio (<code>.oma</code>) format, which is Sony&rsquo;s container format that can contain audio data encoded with one of a number of different codecs. Since we changed the import settings to PCM, they will basically be the same as umcompressed WAV/AIFF files (44.1/2/16), just in a different container.</p>
  7806. <p><em>ffmpeg</em> can also convert <code>.oma</code> files into any other audio format:</p>
  7807. <pre><code>    ffmpeg -i file.oma file.wav # lossless container conversion  
  7808.    ffmpeg -i file.oma file.mp3 # lossy recompression  
  7809.    ffmpeg -i file.oma file.m4a # lossy recompression
  7810. </code></pre>
  7811. <h3 id="limitations">Limitations</h3>
  7812. <p><em>SonicStage</em> does not allow dumping the raw ATRAC1-encoded data from the MiniDisc, it always decodes it and stores it in a different format. When decoding to PCM, as shown above, there is no quality loss.</p>
  7813. <p>Unlike <em>NetMDPython</em>, <em>SonicStage</em> can dump protected tracks from pressed MiniDiscs, but it also cannot dump tracks that have been copied digitally from a CD, as well as tracks it has written itself – unless it was from the same computer.</p>
  7814. <h2 id="analog-copy">Analog Copy</h2>
  7815. <p>If all else fails, you can always make an analog copy of the audio on a MiniDisc. After all, the kinds of MiniDiscs you care about are probably not digital copies of CDs (you should rather find the original CDs then), but recordings from analog sources anyway – so the extra added noise should be negligible.</p>
  7816. <p>The most basic way to make an anlog copy is to connect any MiniDisc player to a computer and using <a href="Audacity">https://www.audacityteam.org</a> for recording. Since modern computers have many things going on at once, you might get skips while recording, so you need to make sure that there is as little load on the computer as possible.</p>
  7817. <p>If you have an MZ-RH1 and a second MiniDisc player, you could also connect the two and have the MZ-RH1 record the MiniDisc in the second player onto a 1 GB Hi-MD – uncompressed. You can then use <em>SonicStage</em> or the platform-independent <a href="https://wiki.physik.fu-berlin.de/linux-minidisc/doku.php?id=qhimdtransfer">QHiMDTransfer</a> to copy the file(s) over.</p>
  7818. <p>The nicest solution is the <code>dump_md.py</code> script from the <a href="https://wiki.physik.fu-berlin.de/linux-minidisc/doku.php?id=netmdpython">NetMDPython</a> project, which can remote-control any NetMD-compliant MiniDisc player (such as the MZ-RH1) and record the analog output through the sound card. This way, the audio will be copied as individual tracks.</p>
  7819. <h2 id="bonus:-recovering-deleted-tracks">Bonus: Recovering Deleted Tracks</h2>
  7820. <p>MiniDiscs allow arbitrarily deleting tracks, and recording new tracks in their place. This is made possible by the TOC: a global data structure that points to the sections on the media that make up the tracks.</p>
  7821. <p>When deleting a track, or the entire MiniDisc, the audio data is not touched, only the TOC is modified. By hacking the TOC, it is possible to access all raw data on the media.</p>
  7822. <p>Here are the steps: (This is based on the <a href="https://www.minidisc.org/conner/MD-MT15_TOC_cloning.htm">TOC Cloning</a> trick.)</p>
  7823. <ul>
  7824. <li>Locate your recorder&rsquo;s door detection switch. On the SHARP MD-MT15, it is here:</li>
  7825. </ul>
  7826. <p><img src="docs/dumping_md/recover1.jpg" height="330" width="440" alt="" /></p>
  7827. <ul>
  7828. <li>Find a way to keep the switch constantly depressed. In the case of the MD-MT15, you can use part of a toothpick to keep it down.</li>
  7829. </ul>
  7830. <p><img src="docs/dumping_md/recover2.jpg" height="330" width="440" alt="" /></p>
  7831. <ul>
  7832. <li>Take a <em>spare</em> MiniDisc that is at least the same size (60/74/80) as the one you want to recover.</li>
  7833. <li>Erase the whole <em>spare</em> MiniDisc. (Don&rsquo;t erase single tracks!)</li>
  7834. <li>Record silence until the whole disk is full.</li>
  7835. <li>Remove the <em>spare</em> MiniDisc and take take the batteries out of the recorder.</li>
  7836. <li>Insert the <em>spare</em> MiniDisc and put the batteries back in. The media should be detected correctly.</li>
  7837. <li>Now replace the <em>spare</em> MiniDisc with the one you want to recover. The recorder should not notice that the door has been opened and will not re-read the TOC.</li>
  7838. <li>Press play. The whole 60/74/80 minutes of raw audio data should now play.</li>
  7839. </ul>
  7840. ]]></content:encoded>
  7841. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1384</wfw:commentRss>
  7842. <slash:comments>8</slash:comments>
  7843. </item>
  7844. <item>
  7845. <title>The Ultimate Acorn Archimedes Talk [video]</title>
  7846. <link>https://www.pagetable.com/?p=1381</link>
  7847. <comments>https://www.pagetable.com/?p=1381#comments</comments>
  7848. <pubDate>Mon, 30 Dec 2019 00:09:06 +0000</pubDate>
  7849. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  7850. <category><![CDATA[Archeology]]></category>
  7851. <category><![CDATA[Presentation]]></category>
  7852.  
  7853. <guid isPermaLink="false">https://www.pagetable.com/?p=1381</guid>
  7854. <description><![CDATA[Matt Evans presented “The Ultimate Acorn Archimedes Talk”, the 8th talk in the “Ultimate Talk” series, at the 36th Chaos Communication Congress (36C3). Here is the video:]]></description>
  7855. <content:encoded><![CDATA[<p><a href="https://axio.ms">Matt Evans</a> presented “The Ultimate Acorn Archimedes Talk”, the 8th talk in the “Ultimate Talk” series, at the 36th Chaos Communication Congress (36C3).</p>
  7856. <p><img src="docs/ultimate8.png" width="640" height="360"/></p>
  7857. <p>Here is the video:</p>
  7858. <p><iframe width="600" height="338" src="https://www.youtube.com/embed/Hf67JYkUCHQ?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
  7859. ]]></content:encoded>
  7860. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1381</wfw:commentRss>
  7861. <slash:comments>1</slash:comments>
  7862. </item>
  7863. <item>
  7864. <title>Commander X16: Philosophy and Specification</title>
  7865. <link>https://www.pagetable.com/?p=1373</link>
  7866. <comments>https://www.pagetable.com/?p=1373#comments</comments>
  7867. <pubDate>Fri, 18 Oct 2019 11:45:31 +0000</pubDate>
  7868. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  7869. <category><![CDATA[6502]]></category>
  7870. <category><![CDATA[X16]]></category>
  7871.  
  7872. <guid isPermaLink="false">https://www.pagetable.com/?p=1373</guid>
  7873. <description><![CDATA[I recently got involved in the Commander X16 project. I would like to give an overview of the project and the vision behind it from my perspective. Philosophy The Commander X16 is a new 8 bit computer designed by David Murray of the well-known &#8220;The 8-Bit Guy&#8221; online video channel. 8 bit computers are great ... <a title="Commander X16: Philosophy and Specification" class="read-more" href="https://www.pagetable.com/?p=1373">Read more<span class="screen-reader-text">Commander X16: Philosophy and Specification</span></a>]]></description>
  7874. <content:encoded><![CDATA[<p>I recently got involved in the Commander X16 project. I would like to give an overview of the project and the vision behind it from my perspective.</p>
  7875. <h2 id="Philosophy">Philosophy</h2>
  7876. <p>The Commander X16 is a new 8 bit computer designed by David Murray of the well-known <a href="https://www.youtube.com/channel/UC8uT9cgJorJPWu7ITLGo9Ww">&ldquo;The 8-Bit Guy&rdquo; online video channel</a>.</p>
  7877. <p>8 bit computers are great for learning about computer architecture, because they are simple enough that they can be fully understood, and they are great for learning to program, because they boot straight into a programming language. But 8 bit computers also have some annoyances like cheap, non-standard keyboards, TV-out and the reliance on floppy disks (or rather expensive SD card adapters).</p>
  7878. <p>Therefore, David is designing  a new 8 bit computer in the style of Commodore computers like the VIC-20 and the C64, fixing the annoyances of retro computers by having:</p>
  7879. <ul>
  7880. <li>VGA output (in addition to composite)</li>
  7881. <li>support for standard PS/2 keyboards (instead of a cheap built-in keyboard with a non-standard layout)</li>
  7882. <li>SD card for storage (instead of an additional floppy drive)</li>
  7883. <li>RS232 port for efficient cross-developmment</li>
  7884. <li>efficient modern power supply</li>
  7885. </ul>
  7886. <p>Another problem with retro computers nowadays is the high barrier of entry when developing (semi-)professional software: Since retro computers have been researched for almost 40 years, standards for software are very high. Any credible new game release for the C64 for example will have to come with a fastloader to overcome slow load speeds, and a sprite multiplexer to overcome the 8 sprite limit. The X16 ROM supports fast loading from SD card, and its video chip supports plenty of sprites as well as hardware scrolling of bitmaps. The memory map is kept simple and does not need (or support) complex reconfigurations.</p>
  7887. <p>With a rather modest set of hardware features, the X16 targets a lower price point than most comparable projects.</p>
  7888. <p>These are the original videos on the topic:</p>
  7889. <ul>
  7890. <li><a href="https://www.youtube.com/watch?v=ayh0qebfD2g">The 8-Bit Guy: My dream computer &#8211; Part 1</a></li>
  7891. <li><a href="https://www.youtube.com/watch?v=sg-6Cjzzg8s">The 8-Bit Guy: My dream computer &#8211; Part 2</a></li>
  7892. </ul>
  7893. <h2 id="Hardware">Hardware</h2>
  7894. <p>These are the hardware specs:</p>
  7895. <ul>
  7896. <li>65C02 CPU at 8 MHz</li>
  7897. <li>40 KB of main RAM</li>
  7898. <li>512 KB (or 2 MB) of banked RAM</li>
  7899. <li>128 KB of banked ROM</li>
  7900. <li>VERA video controller
  7901. <ul>
  7902. <li>16-bit class</li>
  7903. <li>128 KB of external video RAM</li>
  7904. <li>640&#215;480 (or 320&#215;240) pixels</li>
  7905. <li>256 colors out of 4096</li>
  7906. <li>2 layers supporting tiled and bitmap modes</li>
  7907. <li>128 sprites (limit per line based on memory bandwidth)</li>
  7908. </ul>
  7909. </li>
  7910. <li>sound TBD</li>
  7911. <li>two 6522 VIA I/O controllers</li>
  7912. </ul>
  7913. <p>The computer supports the following connections:</p>
  7914. <ul>
  7915. <li>VGA (480p), or Composite/RGB (480i)</li>
  7916. <li>PS/2 keyboard</li>
  7917. <li>PS/2 mouse</li>
  7918. <li>two NES or SNES controllers</li>
  7919. <li>SD card</li>
  7920. <li>legacy IEC</>
  7921. <li>RS-232</li>
  7922. </ul>
  7923. <p>The X16 is not an FPGA-based solution, but uses real, socketed 65C02 and 6522 chips for the same hackability as a retro computer. The video chip is a new design and comes as an FPGA.</p>
  7924. <h2 id="Software">Software</h2>
  7925. <p>From the software side, the Commander X16 feels like a Commodore computer. Its ROM contains the &ldquo;KERNAL&rdquo; operating system derived from the C64 version, as well as an enhanced version of Commodore/Microsoft BASIC based on V2.</p>
  7926. <p><img src="docs/x16/hello_world.gif" alt="" /></p>
  7927. <p>Consequently, the X16 can be considered a sibling of the computers from the Commodore 8 bit family (PET, VIC-20, C64, CBM2, Plus/4, C128 and C65). It is not meant to be fully compatible with any of these machines, but it is as compatible as a Plus/4 is with a C64: BASIC programs without PEEK and POKE as well as machine code programs that only use the documented KERNAL API (e.g. BSOUT $FFD2) will just work, but existing code that accesses hardware would have to be ported.</p>
  7928. <p>The fact that the X16 breaks compatibility with the C64 is what I find particular interesting. Most retro projects try to recreate a classic computer. Users will start their favorite two games and then get bored. The X16 is a new system, with new tricks to discover – but familiar to people who know the C64.</p>
  7929. <h2 id="Development">Development</h2>
  7930. <p>As of October 2019, only a handful of prototype machines exist. If you don&rsquo;t want to wait for the release hardware, you can use the emulator:</p>
  7931. <p><a href="https://github.com/commanderx16/x16-emulator/">https://github.com/commanderx16/x16-emulator/</a></p>
  7932. <p>There are binary releases for macOS, Windows and Linux on the GitHub releases page, which always include the latest build of the ROM.</p>
  7933. <p>The ROM is being developed as open source:</p>
  7934. <p><a href="https://github.com/commanderx16/x16-rom/">https://github.com/commanderx16/x16-rom/</a></p>
  7935. <p>The reference guide is worked on as an open source project as well:</p>
  7936. <p><a href="https://github.com/commanderx16/x16-docs/">https://github.com/commanderx16/x16-docs/</a></p>
  7937. <p>And here is a collection of demo/example code contributed to by many people:</p>
  7938. <p><a href="https://github.com/commanderx16/x16-demo/">https://github.com/commanderx16/x16-demo/</a></p>
  7939. <p><strike>The <a href="https://www.facebook.com/groups/CommanderX16/">official forum</a> is <a href="https://en.wikipedia.org/wiki/Surveillance_capitalism">unfortunately</a> hosted on Facebook, but there is a lot going on: Users show off programs they have written and discuss programming questions. There is also <a href="https://murray2.com/forums/commander-x16.9/">a forum on David Murray&rsquo;s website</a>.</strike></p>
  7940. <p>The official forum is at <a href="https://www.commanderx16.com/forum/">commanderx16.com</a>, with people discussing programming questions and showing off new programs.</p>
  7941. ]]></content:encoded>
  7942. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1373</wfw:commentRss>
  7943. <slash:comments>24</slash:comments>
  7944. </item>
  7945. <item>
  7946. <title>NES and SNES Controllers on a 6502 (like the C64)</title>
  7947. <link>https://www.pagetable.com/?p=1365</link>
  7948. <comments>https://www.pagetable.com/?p=1365#comments</comments>
  7949. <pubDate>Sat, 10 Aug 2019 22:52:50 +0000</pubDate>
  7950. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  7951. <category><![CDATA[6502]]></category>
  7952. <category><![CDATA[C64]]></category>
  7953. <category><![CDATA[Commodore]]></category>
  7954. <category><![CDATA[GitHub]]></category>
  7955.  
  7956. <guid isPermaLink="false">https://www.pagetable.com/?p=1365</guid>
  7957. <description><![CDATA[NES and SNES controllers support 8 to 12 buttons with only three data pins (plus VCC/GND). Let&#8217;s attach them to a C64 – or any 6502-based system! NES Connector The NES controller needs to be connected to +5V, GND and three GPIOs. ---------- &#124; 5 6 7 \ &#124; 4 3 2 1 &#124; ------------ ... <a title="NES and SNES Controllers on a 6502 (like the C64)" class="read-more" href="https://www.pagetable.com/?p=1365">Read more<span class="screen-reader-text">NES and SNES Controllers on a 6502 (like the C64)</span></a>]]></description>
  7958. <content:encoded><![CDATA[<p>NES and SNES controllers support 8 to 12 buttons with only three data pins (plus VCC/GND). Let&rsquo;s attach them to a C64 – or any 6502-based system!</p>
  7959. <p><a href="docs/nes_snes_controller_6502/nes_snes_controller_6502.jpg"><img src="docs/nes_snes_controller_6502/nes_snes_controller_6502_small.jpg" alt="" /></a></p>
  7960. <h2 id="NES-Connector">NES Connector</h2>
  7961. <p>The NES controller needs to be connected to +5V, GND and three GPIOs.</p>
  7962. <pre><code> ----------  
  7963. | 5  6  7   \  
  7964. | 4  3  2  1 |  
  7965. ------------
  7966. </code></pre>
  7967. <table>
  7968. <thead>
  7969. <tr>
  7970. <th> Pin </th>
  7971. <th> Description </th>
  7972. </tr>
  7973. </thead>
  7974. <tbody>
  7975. <tr>
  7976. <td> 1   </td>
  7977. <td> GND         </td>
  7978. </tr>
  7979. <tr>
  7980. <td> 2   </td>
  7981. <td> CLK         </td>
  7982. </tr>
  7983. <tr>
  7984. <td> 3   </td>
  7985. <td> LATCH       </td>
  7986. </tr>
  7987. <tr>
  7988. <td> 4   </td>
  7989. <td> DATA        </td>
  7990. </tr>
  7991. <tr>
  7992. <td> 5   </td>
  7993. <td> &#8211;           </td>
  7994. </tr>
  7995. <tr>
  7996. <td> 6   </td>
  7997. <td> &#8211;           </td>
  7998. </tr>
  7999. <tr>
  8000. <td> 7   </td>
  8001. <td> +5V         </td>
  8002. </tr>
  8003. </tbody>
  8004. </table>
  8005. <h2 id="SNES-Connector">SNES Connector</h2>
  8006. <p>The SNES controller&rsquo;s pins are just like the NES controller&rsquo;s, but with a different connector.</p>
  8007. <pre><code> /---------------------  
  8008. | 7  6  5 | 4  3  2  1 |  
  8009. \---------------------
  8010. </code></pre>
  8011. <table>
  8012. <thead>
  8013. <tr>
  8014. <th> Pin </th>
  8015. <th> Description </th>
  8016. </tr>
  8017. </thead>
  8018. <tbody>
  8019. <tr>
  8020. <td> 1   </td>
  8021. <td> +5V         </td>
  8022. </tr>
  8023. <tr>
  8024. <td> 2   </td>
  8025. <td> CLK         </td>
  8026. </tr>
  8027. <tr>
  8028. <td> 3   </td>
  8029. <td> LATCH       </td>
  8030. </tr>
  8031. <tr>
  8032. <td> 4   </td>
  8033. <td> DATA        </td>
  8034. </tr>
  8035. <tr>
  8036. <td> 5   </td>
  8037. <td> &#8211;           </td>
  8038. </tr>
  8039. <tr>
  8040. <td> 6   </td>
  8041. <td> &#8211;           </td>
  8042. </tr>
  8043. <tr>
  8044. <td> 7   </td>
  8045. <td> GND         </td>
  8046. </tr>
  8047. </tbody>
  8048. </table>
  8049. <h2 id="User-Port">User Port</h2>
  8050. <p>The C64 User Port exposes, among other lines, +5V, GND and 8 GPIOs (CIA#2 Port B):</p>
  8051. <pre><code> 1 | 2  3  4  5  6  7  8  9  10 | 11 12  
  8052. --- ---------------------------- -------  
  8053. A | B  C  D  E  F  H  J  K  L  | M  N
  8054. </code></pre>
  8055. <p>(viewed towards the C64 edge connector)</p>
  8056. <table>
  8057. <thead>
  8058. <tr>
  8059. <th> Pin </th>
  8060. <th> Description </th>
  8061. </tr>
  8062. </thead>
  8063. <tbody>
  8064. <tr>
  8065. <td> 1   </td>
  8066. <td> GND         </td>
  8067. </tr>
  8068. <tr>
  8069. <td> 2   </td>
  8070. <td> +5V         </td>
  8071. </tr>
  8072. <tr>
  8073. <td> C   </td>
  8074. <td> PB0         </td>
  8075. </tr>
  8076. <tr>
  8077. <td> D   </td>
  8078. <td> PB1         </td>
  8079. </tr>
  8080. <tr>
  8081. <td> E   </td>
  8082. <td> PB2         </td>
  8083. </tr>
  8084. <tr>
  8085. <td> F   </td>
  8086. <td> PB3         </td>
  8087. </tr>
  8088. <tr>
  8089. <td> H   </td>
  8090. <td> PB4         </td>
  8091. </tr>
  8092. <tr>
  8093. <td> J   </td>
  8094. <td> PB5         </td>
  8095. </tr>
  8096. <tr>
  8097. <td> K   </td>
  8098. <td> PB6         </td>
  8099. </tr>
  8100. <tr>
  8101. <td> L   </td>
  8102. <td> PB7         </td>
  8103. </tr>
  8104. </tbody>
  8105. </table>
  8106. <h2 id="Connection">Connection</h2>
  8107. <p>Let&rsquo;s semi-arbitrarily map the signals like this:</p>
  8108. <table>
  8109. <thead>
  8110. <tr>
  8111. <th> GPIO </th>
  8112. <th> Description                  </th>
  8113. </tr>
  8114. </thead>
  8115. <tbody>
  8116. <tr>
  8117. <td> PB3  </td>
  8118. <td> LATCH (for both controllers) </td>
  8119. </tr>
  8120. <tr>
  8121. <td> PB4  </td>
  8122. <td> DATA (controller 1)          </td>
  8123. </tr>
  8124. <tr>
  8125. <td> PB5  </td>
  8126. <td> CLK (for both controllers)   </td>
  8127. </tr>
  8128. <tr>
  8129. <td> PB6  </td>
  8130. <td> DATA (controller 2)          </td>
  8131. </tr>
  8132. </tbody>
  8133. </table>
  8134. <p>The latch and clock outputs go to both controllers. There is a data line for each controller.</p>
  8135. <p>So the connection diagram for two NES controllers looks like this:</p>
  8136. <table>
  8137. <thead>
  8138. <tr>
  8139. <th> Description </th>
  8140. <th> User Port Pin </th>
  8141. <th> NES #1 Pin </th>
  8142. <th> NES #2 Pin </th>
  8143. <th> Color  </th>
  8144. </tr>
  8145. </thead>
  8146. <tbody>
  8147. <tr>
  8148. <td> GND         </td>
  8149. <td> 1             </td>
  8150. <td> 1          </td>
  8151. <td> 1          </td>
  8152. <td> black  </td>
  8153. </tr>
  8154. <tr>
  8155. <td> +5V         </td>
  8156. <td> 2             </td>
  8157. <td> 7          </td>
  8158. <td> 7          </td>
  8159. <td> red    </td>
  8160. </tr>
  8161. <tr>
  8162. <td> LATCH       </td>
  8163. <td> F             </td>
  8164. <td> 3          </td>
  8165. <td> 3          </td>
  8166. <td> blue   </td>
  8167. </tr>
  8168. <tr>
  8169. <td> DATA#1      </td>
  8170. <td> H             </td>
  8171. <td> 4          </td>
  8172. <td> &#8211;          </td>
  8173. <td> green  </td>
  8174. </tr>
  8175. <tr>
  8176. <td> CLK         </td>
  8177. <td> J             </td>
  8178. <td> 2          </td>
  8179. <td> 2          </td>
  8180. <td> white  </td>
  8181. </tr>
  8182. <tr>
  8183. <td> DATA#2      </td>
  8184. <td> K             </td>
  8185. <td> &#8211;          </td>
  8186. <td> 4          </td>
  8187. <td> yellow </td>
  8188. </tr>
  8189. </tbody>
  8190. </table>
  8191. <p>And this is the same diagram for two SNES controllers:</p>
  8192. <table>
  8193. <thead>
  8194. <tr>
  8195. <th> Description </th>
  8196. <th> User Port Pin </th>
  8197. <th> NES #1 Pin </th>
  8198. <th> NES #2 Pin </th>
  8199. <th> Color  </th>
  8200. </tr>
  8201. </thead>
  8202. <tbody>
  8203. <tr>
  8204. <td> GND         </td>
  8205. <td> 1             </td>
  8206. <td> 7          </td>
  8207. <td> 7          </td>
  8208. <td> black  </td>
  8209. </tr>
  8210. <tr>
  8211. <td> +5V         </td>
  8212. <td> 2             </td>
  8213. <td> 1          </td>
  8214. <td> 1          </td>
  8215. <td> red    </td>
  8216. </tr>
  8217. <tr>
  8218. <td> LATCH       </td>
  8219. <td> F             </td>
  8220. <td> 3          </td>
  8221. <td> 3          </td>
  8222. <td> blue   </td>
  8223. </tr>
  8224. <tr>
  8225. <td> DATA#1      </td>
  8226. <td> H             </td>
  8227. <td> 4          </td>
  8228. <td> &#8211;          </td>
  8229. <td> green  </td>
  8230. </tr>
  8231. <tr>
  8232. <td> CLK         </td>
  8233. <td> J             </td>
  8234. <td> 2          </td>
  8235. <td> 2          </td>
  8236. <td> white  </td>
  8237. </tr>
  8238. <tr>
  8239. <td> DATA#2      </td>
  8240. <td> K             </td>
  8241. <td> &#8211;          </td>
  8242. <td> 4          </td>
  8243. <td> yellow </td>
  8244. </tr>
  8245. </tbody>
  8246. </table>
  8247. <p>In fact, you can attach an NES and an SNES controller in parallel for each of the two slots, as long as you ever only connect one controller per slot.</p>
  8248. <p>This is the user port connector with wires attached for two controllers, using the color scheme above:</p>
  8249. <p><a href="docs/nes_snes_controller_6502/userport_connector.jpg"><img src="docs/nes_snes_controller_6502/userport_connector_small.jpg" alt="" /></a></p>
  8250. <p>This is an NES connector attached as the first controller (green data line):</p>
  8251. <p><a href="docs/nes_snes_controller_6502/nes_connector.jpg"><img src="docs/nes_snes_controller_6502/nes_connector_small.jpg" alt="" /></a></p>
  8252. <p>And this is an SNES connector attached as the second controller (yellow data line):</p>
  8253. <p><a href="docs/nes_snes_controller_6502/snes_connector.jpg"><img src="docs/nes_snes_controller_6502/snes_connector_small.jpg" alt="" /></a></p>
  8254. <h2 id="The-Code">The Code</h2>
  8255. <p>The code to read both controllers at a time is pretty simple:</p>
  8256. <pre><code>; C64 CIA#2 PB  
  8257. nes_data = $dd01  
  8258. nes_ddr  = $dd03  
  8259. ;
  8260. bit_latch = $08 ; PB3 (user port pin F): LATCH (both controllers)  
  8261. bit_data1 = $10 ; PB4 (user port pin H): DATA  (controller #1)  
  8262. bit_clk   = $20 ; PB5 (user port pin J): CLK   (both controllers)  
  8263. bit_data2 = $40 ; PB6 (user port pin K): DATA  (controller #2)
  8264.  
  8265. ; zero page  
  8266. controller1 = $e0 ; 3 bytes  
  8267. controller2 = $f0 ; 3 bytes
  8268.  
  8269. query_controllers:  
  8270.    lda #$ff-bit_data1-bit_data2  
  8271.    sta nes_ddr  
  8272.    lda #$00  
  8273.    sta nes_data
  8274.  
  8275.    ; pulse latch  
  8276.    lda #bit_latch  
  8277.    sta nes_data  
  8278.    lda #0  
  8279.    sta nes_data
  8280.  
  8281.    ; read 3x 8 bits  
  8282.    ldx #0  
  8283. l2: ldy #8  
  8284. l1: lda nes_data  
  8285.    cmp #bit_data2  
  8286.    rol controller2,x  
  8287.    and #bit_data1  
  8288.    cmp #bit_data1  
  8289.    rol controller1,x  
  8290.    lda #bit_clk  
  8291.    sta nes_data  
  8292.    lda #0  
  8293.    sta nes_data  
  8294.    dey  
  8295.    bne l1  
  8296.    inx  
  8297.    cpx #3  
  8298.    bne l2  
  8299.    rts
  8300. </code></pre>
  8301. <p>After calling <code>query_controllers</code>, three bytes each at <code>controller1</code> and <code>controller2</code> will contain the state:</p>
  8302. <pre><code>; byte 0:      | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |  
  8303. ;         NES  | A | B |SEL|STA|UP |DN |LT |RT |  
  8304. ;         SNES | B | Y |SEL|STA|UP |DN |LT |RT |  
  8305. ;
  8306. ; byte 1:      | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |  
  8307. ;         NES  | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |  
  8308. ;         SNES | A | X | L | R | 1 | 1 | 1 | 1 |  
  8309. ; byte 2:  
  8310. ;         $00 = controller present  
  8311. ;         $FF = controller not present
  8312. </code></pre>
  8313. <p>A 0 bit means the button is pressed, 1 means it is released.</p>
  8314. <p>The code pulses LATCH once, which makes the controllers sample their button states and transmit the first bit through their respective DATA lines. Pulsing CLK 15 more times will make the controllers send the remaining bits. Since SNES controllers send 16 bits of data, but NES controllers only send 8 bits, the type of controller can be detected through the lowermost nybble of byte 1. Similarly, the presense of a controller is detected by continuing to read bits: If a controller is attached, it will send 0 bits.</p>
  8315. <h2 id="Repository">Repository</h2>
  8316. <p>The driver code, together with demo code for the C64 can be found at <a href="https://github.com/mist64/nes_snes_controller_6502">https://github.com/mist64/nes_snes_controller_6502</a>.</p>
  8317. <p><a href="docs/nes_snes_controller_6502/demo.png"><img src="docs/nes_snes_controller_6502/demo.png" alt="" /></a></p>
  8318. <h2 id="More?">More?</h2>
  8319. <p>For each additional controller, only a single extra GPIO is needed. This way, six controllers would be possible on a single 8 bit I/O port, with only slightly modified code.</p>
  8320. ]]></content:encoded>
  8321. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1365</wfw:commentRss>
  8322. <slash:comments>11</slash:comments>
  8323. </item>
  8324. <item>
  8325. <title>The Apollo Guidance Computer [video + slides]</title>
  8326. <link>https://www.pagetable.com/?p=1362</link>
  8327. <comments>https://www.pagetable.com/?p=1362#respond</comments>
  8328. <pubDate>Sun, 04 Aug 2019 21:28:19 +0000</pubDate>
  8329. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  8330. <category><![CDATA[Archeology]]></category>
  8331. <category><![CDATA[Presentation]]></category>
  8332.  
  8333. <guid isPermaLink="false">https://www.pagetable.com/?p=1362</guid>
  8334. <description><![CDATA[I re-did an updated version of my part of the Ultimate Apollo Guidance Computer Talk at VCF West 2019. It was followed by Frank O&#8217;Brien&#8216;s talk on the role of the AGC in the Apollo missions. Here is the video: And here are my slides in PDF format: AGC_CHM.pdf (43 MB)]]></description>
  8335. <content:encoded><![CDATA[<p>I re-did an updated version of my part of the <a href="https://www.pagetable.com/?p=922">Ultimate Apollo Guidance Computer Talk</a> at VCF West 2019.</p>
  8336. <p>It was followed by <a href="https://www.amazon.com/Apollo-Guidance-Computer-Architecture-Operation/dp/1441908765">Frank O&#8217;Brien</a>&#8216;s talk on the role of the AGC in the Apollo missions.</p>
  8337. <p>Here is the video:</p>
  8338. <p><iframe width="600" height="450" src="https://www.youtube.com/embed/Nidi2p_PAJM?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
  8339. <p>And here are my slides in PDF format:</p>
  8340. <p><a href="docs/agc/AGC_CHM.pdf">AGC_CHM.pdf</a> (43 MB)</p>
  8341. ]]></content:encoded>
  8342. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1362</wfw:commentRss>
  8343. <slash:comments>0</slash:comments>
  8344. </item>
  8345. <item>
  8346. <title>Visualizing Commodore 1541 Disk Contents – Part 2: Errors</title>
  8347. <link>https://www.pagetable.com/?p=1356</link>
  8348. <comments>https://www.pagetable.com/?p=1356#comments</comments>
  8349. <pubDate>Wed, 29 May 2019 07:17:55 +0000</pubDate>
  8350. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  8351. <category><![CDATA[Archeology]]></category>
  8352. <category><![CDATA[Commodore]]></category>
  8353. <category><![CDATA[Floppy Disks]]></category>
  8354.  
  8355. <guid isPermaLink="false">https://www.pagetable.com/?p=1356</guid>
  8356. <description><![CDATA[We have previously visualized the physical layout of C64/1541 disks. In order to understand the encoding and potential read errors, it is more useful to visualize the disks sector-by-sector. This animation shows 12 regular disks. (Click for original size.) Every pack of 17-21 lines is a track, numbered 1-41. Every line within a pack is ... <a title="Visualizing Commodore 1541 Disk Contents – Part 2: Errors" class="read-more" href="https://www.pagetable.com/?p=1356">Read more<span class="screen-reader-text">Visualizing Commodore 1541 Disk Contents – Part 2: Errors</span></a>]]></description>
  8357. <content:encoded><![CDATA[<p>We have previously <a href="https://www.pagetable.com/?p=1070">visualized the physical layout of C64/1541 disks</a>. In order to understand the encoding and potential read errors, it is more useful to visualize the disks sector-by-sector.</p>
  8358. <p><a href="docs/visualize_1541_2/good_disks.gif"><img src="docs/visualize_1541_2/good_disks.gif" alt="" /></a></p>
  8359. <p>This animation shows 12 regular disks. (Click for original size.)</p>
  8360. <ul>
  8361. <li>Every pack of 17-21 lines is a track, numbered 1-41.</li>
  8362. <li>Every line within a pack is one sector.</li>
  8363. <li>The raw sector contents are drawn from left to right.</li>
  8364. <li>The cyan part is the header, the green part the data.</li>
  8365. <li>Black is 0, cyan/green is 1.</li>
  8366. <li>White represents missing header or sector sections.</li>
  8367. </ul>
  8368. <p>The tool to generate these images from <code>.G64</code> files is available at <a href="https://github.com/mist64/visualize_1541">https://github.com/mist64/visualize_1541</a>.</p>
  8369. <h2 id="Header">Header</h2>
  8370. <p>Let us look at the header first. Here is an animation of the header of tracks 23 to 25 of a few disks:</p>
  8371. <p><a href="docs/visualize_1541_2/headers.gif"><img src="docs/visualize_1541_2/headers.gif" height="264" width="512" alt="" /></a><br />
  8372. <img src="docs/visualize_1541_2/header.png" height="52" width="512" alt="" /></p>
  8373. <p>The header contains six data bytes encoded using the <a href="https://www.linusakesson.net/programming/gcr-decoding/">4-to-5 GCR scheme</a>:</p>
  8374. <ul>
  8375. <li><strong>H: Header Code</strong>: Every header starts with 0x08 so it can be distinguished from the sector data (0x07). You can see that it is the same bit pattern on all sectors on all tracks.</li>
  8376. <li><strong>T: Track</strong>: This is the track number (1-35). It is the same for all sectors on the same track.</li>
  8377. <li><strong>S: Sector</strong>: This is the sector number (0-16/17/18/20, depending on the track). You can see the same bit patterns on each of the three tracks shown.</li>
  8378. <li><strong>ID: ID</strong>: The two-byte ID should be unique per disk and is used to detect disk changes. It is the same for all sectors on the same disk.</li>
  8379. <li><strong>C: Checksum</strong>: The checksum is the XOR of C, S, T and ID, so you can see it behaves kind of randomly in the animation above.</li>
  8380. <li><strong>GAP</strong>: The gap does not contain usable data, but separates the header from the sector data.</li>
  8381. </ul>
  8382. <h2 id="Data">Data</h2>
  8383. <p><a href="docs/visualize_1541_2/data.gif"><img src="docs/visualize_1541_2/data.gif" height="264" width="512" alt="" /></a></p>
  8384. <p>The first byte of the data section is the code 0x07 to distinguish it from a header. The rest is the 256 data bytes. The Commodore DOS filesystem uses two link bytes (next track, next sector) at the beginning of every sector, which is why the next two bytes in this visualization look more regular between disks.</p>
  8385. <h2 id="Good-Disks">Good Disks</h2>
  8386. <p>First, let&rsquo;s look at some error-free disks in practice.</p>
  8387. <h3 id="Empty-Disk">Empty Disk</h3>
  8388. <p>An empty disk looks quite uniform: (Click for original size.)</p>
  8389. <p><a href="docs/visualize_1541_2/empty.png"><img src="docs/visualize_1541_2/empty.png" alt="" /></a></p>
  8390. <p>Only sectors 0 and 1 of track 18 look different:</p>
  8391. <p><a href="docs/visualize_1541_2/empty18.png"><img src="docs/visualize_1541_2/empty18.png" height="46" width="600" alt="" /></a></p>
  8392. <p>18/0 contains the block allocation map (BAM) and the name of the disk, and 18/1 contains the first 8 directory entries.</p>
  8393. <h3 id="Illegal-Bits">Illegal Bits</h3>
  8394. <p>The last few bits of the end of the header are also different for sectors 0 and 1. The headers of all sectors directly after formatting the disk end in a pattern of alternating zeros and ones, but as soon as a sector has been written to, this is no longer the case for the last few bits. It gets even more interesting when visualizing these bits across several reads of <strong>the same disk</strong>:</p>
  8395. <p><a href="docs/visualize_1541_2/empty18.gif"><img src="docs/visualize_1541_2/empty18.gif" height="92" width="704" alt="" /></a></p>
  8396. <p>These are unstable illegal bits on disk, bits that sometimes read back as zeros and sometimes as ones.</p>
  8397. <p>When writing a sector, the software in the disk drive waits for the correct sector header to pass by the read head, then waits until the exact end of the header gap area, and starts writing the new data. When switching to write mode, the magnetization written onto the media for the first ~15 µs (4-5 bits) will be analog values between logical 0 and 1. These are technically illegal values, and will be unstable when read.</p>
  8398. <p><a href="docs/visualize_1541_2/illegal_bits.png"><img src="docs/visualize_1541_2/illegal_bits.png" alt="" /></a></p>
  8399. <p>These illegal bits are benign, because they do not encode anything. But it is important to note that two reads of a good disk may not be identical.</p>
  8400. <h2 id="Errors">Errors</h2>
  8401. <p>Now, let&rsquo;s look at what common errors look like:</p>
  8402. <h3 id="Logical-Errors">Logical Errors</h3>
  8403. <p>Some read errors are caused by buggy software writing to disk: (Click for original size.)</p>
  8404. <p><a href="docs/visualize_1541_2/missing_headers.png"><img src="docs/visualize_1541_2/missing_headers.png" alt="" /></a></p>
  8405. <p>This disk is missing some sector headers:</p>
  8406. <p><a href="docs/visualize_1541_2/missing_headers_detail.png"><img src="docs/visualize_1541_2/missing_headers_detail.png" height="46" width="600" alt="" /></a></p>
  8407. <p>This is one of the <a href="https://www.pagetable.com/?p=1128">faulty GEOS boot disks</a>: Some sectors were written with the wrong speed zone setting, so they spilled into the next header, overwriting it. The sector contents are still there, but the missing header makes them unreadable with the original Commodore DOS. (The visualization tool will guess the sector number in case of a sector with a missing header.)</p>
  8408. <h3 id="Dropouts">Dropouts</h3>
  8409. <p> <!-- sh42b.nbz --></p>
  8410. <p>There is an example of a dropout:</p>
  8411. <p><a href="docs/visualize_1541_2/dirt.png"><img src="docs/visualize_1541_2/dirt.png" height="264" width="512" alt="" /></a></p>
  8412. <p>There is one sector that starts reading back as all 0 bits (black) at some point, and the next sector data is completely missing (white), which is because the SYNC mark of the next sector was not readable.</p>
  8413. <p>This can be caused by demagnetization, or more likely, by dirt on the disk&rsquo;s surface. Sometimes, the dirt will scrape off if we just read the disk often enough. The following animation shows the result of subsequent reads of the same disk:</p>
  8414. <p><a href="docs/visualize_1541_2/dirt2.gif"><img src="docs/visualize_1541_2/dirt2.gif" height="264" width="512" alt="" /></a></p>
  8415. <p>Here, you can see how the faulty sector on the third track in the picture starts out all-black, with the next sector missing, over unstable data reads, to finally stable and correct reads. You can also see how the previous two sectors are impacted by the same speck of dirt, just not as much.</p>
  8416. <p>(The fact that it is different sector numbers that are impacted on the different tracks is due to the fact that on 1541 disks, there is no sector alignment between tracks, i.e. whether sector 0 on one track touches sector 0..20 on the next track is practically random.)</p>
  8417. <p>If the dropout persists across retries, it might still be dirt, just of the more resilient kind. Just <a href="https://www.pagetable.com/?p=1352">clean the disk</a> and try again.</p>
  8418. <h3 id="Weak-Bit-Data-Errors">Weak Bit Data Errors</h3>
  8419. <p><!-- sh65b.nbz --></p>
  8420. <p>This is a disk with multiple checksum errors in the data section across several tracks. Cleaning the disk did not help, so these are weak bits:</p>
  8421. <p><a href="docs/visualize_1541_2/error5_detail.gif"><img src="docs/visualize_1541_2/error5_detail.gif" height="183" width="600" alt="" /></a></p>
  8422. <p>Weak bits do not result in flipped bits, but in missing or duplicate bits. As you can see in the animation, sections of the data move back and forth between retries. It is clearer when looking at a single sector. This excerpt contains four weak bits:</p>
  8423. <p><a href="docs/visualize_1541_2/error5_detail2.gif"><img src="docs/visualize_1541_2/error5_detail2.gif" height="6" width="600" alt="" /></a><br />
  8424. <a href="docs/visualize_1541_2/error5_detail2_legend.png"><img src="docs/visualize_1541_2/error5_detail2_legend.png" height="6" width="600" alt="" /></a></p>
  8425. <p>The read logic of Commodore disk drives measures the time between zero-to-one and one-to-zero edges and then decides how many zeros or ones were on disk. Therefore, weak bits lead to duplicate or missing bits.</p>
  8426. <p>A single weak bit can be recovered from by retrying: If it reads back correctly every now and then, it is as easy as retrying until the checksum is correct. But with multiple weak bits in a single sector, this gets exponentially less likely.</p>
  8427. <h2 id="Conclusion">Conclusion</h2>
  8428. <p>All that current tools can do with faulty disks is retry until the checksum is correct. By analyzing the weak bits, it should be possible to create tools for data recovery on a lower level. Multiple reads will reveal where the weak bits are, so a tool could try out different sequences around the weak bits and verify the checksum.</p>
  8429. ]]></content:encoded>
  8430. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1356</wfw:commentRss>
  8431. <slash:comments>5</slash:comments>
  8432. </item>
  8433. <item>
  8434. <title>Commodore &#8220;Video Supergame 64&#8221; Bundle</title>
  8435. <link>https://www.pagetable.com/?p=1354</link>
  8436. <comments>https://www.pagetable.com/?p=1354#comments</comments>
  8437. <pubDate>Tue, 28 May 2019 11:40:14 +0000</pubDate>
  8438. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  8439. <category><![CDATA[C64]]></category>
  8440. <category><![CDATA[Commodore]]></category>
  8441. <category><![CDATA[Teardown]]></category>
  8442.  
  8443. <guid isPermaLink="false">https://www.pagetable.com/?p=1354</guid>
  8444. <description><![CDATA[The &#8220;Video Supergame 64&#8221; is a Commodore 64 bundled with a joystick and three games on a cartridge, sold mainly in Germany in 1988/1989. Here are some pictures. The top and bottom as well as the two sides of the box are identical. They all say: VIDEO SUPERGAME 64 Inhalt: Contents: Commodore 64 Joystick 3 ... <a title="Commodore &#8220;Video Supergame 64&#8221; Bundle" class="read-more" href="https://www.pagetable.com/?p=1354">Read more<span class="screen-reader-text">Commodore &#8220;Video Supergame 64&#8221; Bundle</span></a>]]></description>
  8445. <content:encoded><![CDATA[<p>The &ldquo;Video Supergame 64&rdquo; is a Commodore 64 bundled with a joystick and three games on a cartridge, sold mainly in Germany in 1988/1989. Here are some pictures.</p>
  8446. <p><a href="docs/supergame64/side2.jpg"><img src="docs/supergame64/side2_small.jpg" alt="" /></a><br />
  8447. <a href="docs/supergame64/front.jpg"><img src="docs/supergame64/front_small.jpg" alt="" /></a><br />
  8448. <a href="docs/supergame64/side1.jpg"><img src="docs/supergame64/side1_small.jpg" alt="" /></a></p>
  8449. <p>The top and bottom as well as the two sides of the box are identical. They all say:</p>
  8450. <blockquote>
  8451. <blockquote>
  8452. <p>VIDEO SUPERGAME 64<br />
  8453. Inhalt:<br />
  8454. Contents:<br />
  8455. Commodore 64<br />
  8456. Joystick<br />
  8457. 3 Super Games</p>
  8458. </blockquote>
  8459. </blockquote>
  8460. <p>There is a sticker on one side:</p>
  8461. <blockquote>
  8462. <blockquote>
  8463. <p>software<br />
  8464. by<br />
  8465. Epyx<br />
  8466. CDS SOFTWARE<br />
  8467. COMMODORE</p>
  8468. </blockquote>
  8469. </blockquote>
  8470. <p>There is absolutely no technical information on the box whatsoever.</p>
  8471. <p><a href="docs/supergame64/inside1.jpg"><img src="docs/supergame64/inside1_small.jpg" alt="" /></a></p>
  8472. <p>Inside the cardboard, there is a styrofoam box with the Commodore logo, and another cardboard box.</p>
  8473. <p><a href="docs/supergame64/inside2.jpg"><img src="docs/supergame64/inside2_small.jpg" alt="" /></a></p>
  8474. <p>The C64 and the power supply are inside the styrofoam.</p>
  8475. <p><a href="docs/supergame64/packaging1.jpg"><img src="docs/supergame64/packaging1_small.jpg" alt="" /></a><br />
  8476. <a href="docs/supergame64/packaging2.jpg"><img src="docs/supergame64/packaging2_small.jpg" alt="" /></a></p>
  8477. <p>These are pictures of the storofoam.</p>
  8478. <p><a href="docs/supergame64/c64_front.jpg"><img src="docs/supergame64/c64_front_small.jpg" alt="" /></a></p>
  8479. <p>The C64 has a red power LED and a light keyboard. These properties varied for the &ldquo;Supergame&rdquo; bundle. Note that the packaging shows a brown keyboard.</p>
  8480. <p><a href="docs/supergame64/c64_back.jpg"><img src="docs/supergame64/c64_back_small.jpg" alt="" /></a></p>
  8481. <p>The label on the bottom states that this is a C64G. Two of the screws are covered with warranty stickers.</p>
  8482. <p><a href="docs/supergame64/psu1.jpg"><img src="docs/supergame64/psu1_small.jpg" height="554" width="300" alt="" /></a><a href="docs/supergame64/psu2.jpg"><img src="docs/supergame64/psu2_small.jpg" height="587" width="260" alt="" /></a></p>
  8483. <p>The power supply says &ldquo;FOR C-64 ONLY&rdquo;. It outputs 5V DC at 1.7A and 9V AC at 1A. There is a sicker saying &ldquo;7-88&rdquo;.</p>
  8484. <p><a href="docs/supergame64/accessories.jpg"><img src="docs/supergame64/accessories_small.jpg" alt="" /></a></p>
  8485. <p>The cardboard box contains the UHF video cable, the cartridge, the joystick and the manuals.</p>
  8486. <p><a href="docs/supergame64/joystick.jpg"><img src="docs/supergame64/joystick_small.jpg" alt="" /></a></p>
  8487. <p>The C-1342 joystick was only sold as part of this bundle.</p>
  8488. <p><a href="docs/supergame64/cart_front.jpg"><img src="docs/supergame64/cart_front_small.jpg" height="386" width="300" alt="" /></a><a href="docs/supergame64/cart_back.jpg"><img src="docs/supergame64/cart_back_small.jpg" height="380" width="300" alt="" /></a></p>
  8489. <p>The &ldquo;Super Games&rdquo; cartridge contains Colossus Chess 2.0, Silicon Syborgs (a space-themed &ldquo;Connect Four&rdquo; style game), and International Football. Note that the packaging of the bundle says nothing about the types of games included. The art implies it was about space ships, race cars and soccer.</p>
  8490. <p><a href="docs/supergame64/manual.png"><img src="docs/supergame64/manual_small.png" height="438" width="300" alt="" /></a><a href="docs/supergame64/games_manual.png"><img src="docs/supergame64/games_manual_small.png" height="458" width="300" alt="" /></a></p>
  8491. <p>These are the cover pages of the C64 and &ldquo;Supergames&rdquo; manuals.</p>
  8492. <p><a href="docs/supergame64/warranty.pdf"><img src="docs/supergame64/warranty_small.png" height="428" width="300" alt="" /></a></p>
  8493. <p>The German warranty sheet has a fixed end date of 1989-06-30 and guarantees repair or replacement within 10 days. It also advertizes some periperals:</p>
  8494. <ul>
  8495. <li>Commodore Diskettenlaufwerk (Floppy) 1541 II (für 5 1/4“ Diskette)</li>
  8496. <li>Commodore Diskettenlaufwerk (Floppy) 1581 (für 3 1/2“ Disketten)</li>
  8497. <li>Commodore Datasette 1530</li>
  8498. <li>Commodore Monochrom-Monitor 1900 M</li>
  8499. <li>Commodore Farbmonitor 1802</li>
  8500. <li>Commodore Drucker MPS 1230</li>
  8501. <li>Commodore RAM-Expansion 1764</li>
  8502. <li>Commodore BTX-Decoder 2</li>
  8503. <li>Commodore Maus 1351</li>
  8504. </ul>
  8505. ]]></content:encoded>
  8506. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1354</wfw:commentRss>
  8507. <slash:comments>6</slash:comments>
  8508. </item>
  8509. <item>
  8510. <title>Dumping Commodore 64/1541 Disks with Errors</title>
  8511. <link>https://www.pagetable.com/?p=1352</link>
  8512. <comments>https://www.pagetable.com/?p=1352#comments</comments>
  8513. <pubDate>Mon, 27 May 2019 17:53:53 +0000</pubDate>
  8514. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  8515. <category><![CDATA[C64]]></category>
  8516. <category><![CDATA[Commodore]]></category>
  8517. <category><![CDATA[Floppy Disks]]></category>
  8518.  
  8519. <guid isPermaLink="false">https://www.pagetable.com/?p=1352</guid>
  8520. <description><![CDATA[Many old Commodore 64/1541 disks have read errors, but this doesn&#8217;t mean the data isn&#8217;t recoverable – with the right nibtools settings and some cleaning. ZoomFloppy and nibtools The ZoomFloppy adapter is the de-facto standard for connecting Commodore disk drives to modern computers, using a USB connection. There are two ways to read a disk image: ... <a title="Dumping Commodore 64/1541 Disks with Errors" class="read-more" href="https://www.pagetable.com/?p=1352">Read more<span class="screen-reader-text">Dumping Commodore 64/1541 Disks with Errors</span></a>]]></description>
  8521. <content:encoded><![CDATA[<p>Many old Commodore 64/1541 disks have read errors, but this doesn&rsquo;t mean the data isn&rsquo;t recoverable – with the right <em>nibtools</em> settings and some cleaning.</p>
  8522. <h2 id="ZoomFloppy-and-nibtools">ZoomFloppy and nibtools</h2>
  8523. <p>The <a href="http://store.go4retro.com/zoomfloppy/">ZoomFloppy</a> adapter is the de-facto standard for connecting Commodore disk drives to modern computers, using a USB connection.</p>
  8524. <p>There are two ways to read a disk image:</p>
  8525. <ul>
  8526. <li><em>d64copy</em> (<em>opencbm</em>) will read the contents of a disk sector by sector and create a <code>.d64</code> image file. This works with all Commodore drives through the serial cable.</li>
  8527. <li><em>nibread</em> (<em>nibtools</em>) will read the raw bits of each track without any interpretation and create a <code>.nib</code>/<code>.nbz</code> file (which can be converted into a standard <code>.g64</code> file). This requires a 1541 with a <a href="http://sta.c64.org/parport41c.html">parallel port mod</a> or a 1570/1571 with just a serial connection.</li>
  8528. </ul>
  8529. <p>The main use for <code>.g64</code> files is to preserve copy-protected games, which often use custom on-disk structures that would not be or sometimes cannot even be preserved in a <code>.d64</code> image. In the context of reading disks that may have read errors, &ldquo;nibbling&rdquo; into <code>.nbz</code>/<code>.g64</code> preserves all the information that could be read, which gives us better insight into what happened, and may allow us to recover more data.</p>
  8530. <p>This article covers reading the raw data using <em>nibread</em>, but most of the information translates to reading the decoded data using <em>d64copy</em>.</p>
  8531. <h2 id="Error-free-Dump">Error-free Dump</h2>
  8532. <p>Let&rsquo;s first look at the output of <code>nibread</code> for an error-free disk. The command <code>nibread disk123</code> will create <code>disk123.nbz</code> and print this:</p>
  8533. <pre><code>   1.0: (3) 7818 [CBM OK] (weakgcr:3)  
  8534.   2.0: (3) 7819 [CBM OK] (weakgcr:6)  
  8535.   3.0: (3) 7819 [CBM OK] (weakgcr:4)  
  8536.   4.0: (3) 7819 [CBM OK] (weakgcr:9)  
  8537.   5.0: (3) 7819 [CBM OK] (weakgcr:5)  
  8538.   6.0: (3) 7819 [CBM OK] (weakgcr:4)  
  8539.   7.0: (3) 7819 [CBM OK] (weakgcr:3)  
  8540.   8.0: (3) 7819 [CBM OK] (weakgcr:6)  
  8541.   9.0: (3) 7819 [CBM OK] (weakgcr:1)  
  8542.  10.0: (3) 7819 [CBM OK] (weakgcr:3)  
  8543.  11.0: (3) 7819 [CBM OK] (weakgcr:4)  
  8544.  12.0: (3) 7819 [CBM OK] (weakgcr:5)  
  8545.  13.0: (3) 7819 [CBM OK] (weakgcr:3)  
  8546.  14.0: (3) 7819 [CBM OK] (weakgcr:9)  
  8547.  15.0: (3) 7819 [CBM OK] (weakgcr:5)  
  8548.  16.0: (3) 7819 [CBM OK] (weakgcr:3)  
  8549.  17.0: (3) 7819 [CBM OK] (weakgcr:4)  
  8550.  18.0: (2) 7148 [CBM OK] (weakgcr:2)  
  8551.  19.0: (2) 7148 [CBM OK] (weakgcr:8)  
  8552.  20.0: (2) 7148 [CBM OK] (weakgcr:4)  
  8553.  21.0: (2) 7148 [CBM OK] (weakgcr:5)  
  8554.  22.0: (2) 7148 [CBM OK] (weakgcr:2)  
  8555.  23.0: (2) 7149 [CBM OK] (weakgcr:6)  
  8556.  24.0: (2) 7148 [CBM OK] (weakgcr:1)  
  8557.  25.0: (1) 6673 [CBM OK] (weakgcr:4)  
  8558.  26.0: (1) 6673 [CBM OK] (weakgcr:3)  
  8559.  27.0: (1) 6673 [CBM OK] (weakgcr:1)  
  8560.  28.0: (1) 6673 [CBM OK] (weakgcr:6)  
  8561.  29.0: (1) 6673 [CBM OK] (weakgcr:5)  
  8562.  30.0: (1) 6673 [CBM OK] (weakgcr:4)  
  8563.  31.0: (0) 6255 [CBM OK]  
  8564.  32.0: (0) 6255 [CBM OK]  
  8565.  33.0: (0) 6255 [CBM OK] (weakgcr:2)  
  8566.  34.0: (0) 6255 [CBM OK] (weakgcr:4)  
  8567.  35.0: (0) 6255 [CBM OK] (weakgcr:2)  
  8568.  36.0: (1!=2 NOSYNC!) 0 [Unformatted Track]  
  8569.  37.0: (1!=2 NOSYNC!) 0 [Unformatted Track]  
  8570.  38.0: (1!=2 NOSYNC!) 0 [Unformatted Track]  
  8571.  39.0: (1!=2 NOSYNC!) 0 [Unformatted Track]  
  8572.  40.0: (1!=2 NOSYNC!) 0 [Unformatted Track]  
  8573.  41.0: (1!=2 NOSYNC!) 0 [Unformatted Track]
  8574. </code></pre>
  8575. <p>Tracks 1 to 35 show &ldquo;CBM OK&#8217;, which means that there are no errors. Tracks 36 to 41 are unformatted, which is the usual case for data disks. You can pass <code>-E35</code> to nibread to save a few seconds on each disk if you are certain it doesn&rsquo;t use the extra tracks.</p>
  8576. <p>&ldquo;weakgcr&rdquo; means that there is data on the track that does not consist of legal <a href="https://www.linusakesson.net/programming/gcr-decoding/">GCR-encoded</a> bit combinations. This is normal for the <a href="https://www.pagetable.com/?p=1070">gaps, especially the tail gap</a>, i.e. the unused areas of a track.</p>
  8577. <p>You can then use <code>nibconvert</code> to create a standard <code>.g64</code> image – or, if there are no errors and no copy protection, a <code>.d64</code> image.</p>
  8578. <h2 id="Error-Codes">Error Codes</h2>
  8579. <p>Errors are shown like this:</p>
  8580. <pre><code>  10.0: (3) 7898 [E5S16]
  8581. </code></pre>
  8582. <p><code>E5S16</code> means there was an error 5 when decoding sector 16. Here is the full list of error codes:</p>
  8583. <table>
  8584. <thead>
  8585. <tr>
  8586. <th> Controller Code </th>
  8587. <th> Description          </th>
  8588. <th> DOS Code </th>
  8589. </tr>
  8590. </thead>
  8591. <tbody>
  8592. <tr>
  8593. <td> 2               </td>
  8594. <td> Header not found     </td>
  8595. <td> 20       </td>
  8596. </tr>
  8597. <tr>
  8598. <td> 4               </td>
  8599. <td> Data not found       </td>
  8600. <td> 22       </td>
  8601. </tr>
  8602. <tr>
  8603. <td> 5               </td>
  8604. <td> Data checksum error  </td>
  8605. <td> 23       </td>
  8606. </tr>
  8607. <tr>
  8608. <td> 9               </td>
  8609. <td> Header checkum error </td>
  8610. <td> 27       </td>
  8611. </tr>
  8612. </tbody>
  8613. </table>
  8614. <p>The <em>nibtools</em> error codes are the same as the 1541 &ldquo;controller&rdquo; error codes. The DOS codes are the &ldquo;READ ERROR&rdquo; codes returned by the 1541 status channel.</p>
  8615. <ul>
  8616. <li>The most common error is number 5: About 90% of a track consists of the actual sector data, and any incorrectly read bit will cause an error 5.</li>
  8617. <li>Tracks with more serious read problems often show an error 2 or 4: This usually means that large chunks of the track were unreadable, so that the header/data markers (&ldquo;SYNC&rdquo;) could not be found.</li>
  8618. </ul>
  8619. <p>For a description of the on-disk format of sectors and more information on common errors, have a look at <a href="https://www.pagetable.com/?p=1128">this article</a>.</p>
  8620. <h2 id="Checksums">Checksums</h2>
  8621. <p>The on-disk format of 1541 disks uses 8-bit checksums to protect the integrity of headers and data sections. It is calculcated as an XOR of all data bytes.</p>
  8622. <p><em>nibread</em> will flag an error if any of the checksums is incorrect, and retry reading up to the specified retry count (default 10).</p>
  8623. <p>An 8 bit checksum is not very strong: The probability of an undetected read error is 1:256. In practice, it is good enough that it should be trusted for most cases. But if a disk has dozens of read errors, it is not a solution to set the number of retries to 1000 and leave it running overnight!</p>
  8624. <h2 id="Weak-Sector">Weak Sector</h2>
  8625. <p>The most common case is an error that goes away after a few retries:</p>
  8626. <pre><code>  10.0: (3) 7898 [E5S16]  
  8627.        (3) 7898 [E5S16]  
  8628.        (3) 7898 [CBM OK] (weakgcr:3)
  8629. </code></pre>
  8630. <p>In this example, the second retry was successful. The incorrect read was either caused by dirt that rubbed off easily, or by a weak bit.</p>
  8631. <h2 id="Bad-sector">Bad sector</h2>
  8632. <p>But sometimes an error does not go away. Here is an error 2 that persists after 10 retries, the <em>nibread</em> default:</p>
  8633. <pre><code>   1.0: (3) 7692 [E2S9]  
  8634.        (3) 7691 [E2S9]  
  8635.        (3) 7690 [E2S9]  
  8636.        (3) 7691 [E2S9]  
  8637.        (3) 7691 [E2S9]  
  8638.        (3) 7692 [E2S9]  
  8639.        (3) 7692 [E2S9]  
  8640.        (3) 7691 [E2S9]  
  8641.        (3) 7691 [E2S9]  
  8642.        (3) 7692 [E2S9]  
  8643.        (3) 7691 [E2S9] (weakgcr:34)
  8644. </code></pre>
  8645. <p>As always, the error could be caused by weak bits, or by dirt<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>.</p>
  8646. <p>You could increase the number of retries, which works in some cases, but if the error does not go away after maybe 20 retries, it is best to first <strong>cleaned the disk</strong>.</p>
  8647. <h2 id="Cleaning-the-Disk">Cleaning the Disk</h2>
  8648. <p>The most common reason thirty year old disks have read errors is that some of their surface material became loose and is now a thin layer of dust that interferes with the read head.</p>
  8649. <p>An alcohol-based cleaning wipe is the easiest method to clean the disk. For this, you need to remove the top of the drive&rsquo;s case. It&rsquo;s good practice to keep it unscrewed on the device you are using for dumping disks anyway.</p>
  8650. <p>The read head is on the bottom, and there is a spring-loaded counter-weight on the top, which can be easily lifted, so that the cloth can be put between it and the disk&rsquo;s surface:</p>
  8651. <p><a href="docs/recovering_disks/clean_disk.jpg"><img src="docs/recovering_disks/clean_disk_small.jpg" alt="" /></a></p>
  8652. <p>Note that this always cleans the side of the disk that is opposite of what is expected based on the orientation of the disk on the drive. In other words, to clean side A, you have to put in the disk as if you wanted to read side B.</p>
  8653. <p>A 1571 has a read head on both sides, and the top one cannot be lifted very far, so you have to be careful not to break anything.</p>
  8654. <p>Then, instruct nibread to do one pass across all tracks, ignoring errors:</p>
  8655. <pre><code>  nibread -e0 /tmp/x
  8656. </code></pre>
  8657. <p>You should do this several times, so that the cloth gets rubbed over every track multiple times.</p>
  8658. <p>These cleaning cloths dry out very quickly. Instead of using new cloths every time, you can use an alcohol-based disinfection spray to add moisture to them again.</p>
  8659. <h2 id="Trying-Again">Trying Again</h2>
  8660. <p>Before trying again, you should make sure that the disk&rsquo;s surface has dried. Otherwise, you will get something like this, possibly on a track that was fine before:</p>
  8661. <pre><code>  12.0: (3) 7882 [E5S0]  
  8662.        (3) 7882 [E5S5]  
  8663.        (3) 7880 [E5S8]  
  8664.        (3) 7881 [E5S0][E5S5][E5S8][E5S11]  
  8665.        (3) 7880 [E5S2][E5S5][E5S8][E5S10][E5S12][E5S16]  
  8666.        (3) 7882 [E5S1][E5S2][E2S7][E5S11]  
  8667.        (3) 7882 [E5S1][E5S8][E5S9][E5S10][E5S12][E5S16]  
  8668.        (3) 7882 [E5S2][E5S3][E5S5][E5S10][E5S11][E5S13][E5S15]  
  8669.        (3) 7881 [E5S2][E5S10][E5S11][E5S13][E5S14][E2S18]  
  8670.        (3) 7882 [E5S4][E5S5][E5S9][E5S10][E5S11][E5S12][E5S13][E5S14][E5S16]  
  8671.        (3) 7881 [E5S1][E5S2][E5S4][E5S5][E5S8][E5S10][E5S11][E5S12][E5S13][E5S14][E5S16] (weakgcr:11)
  8672. </code></pre>
  8673. <p>On every read, there were different errors, which indicates that the errors aren&rsquo;t caused by the contents of the disk. Let it dry, then try again. (It could also indicate a dirty read head – see below.)</p>
  8674. <p>If you are lucky, the track will read without any errors after cleaning:</p>
  8675. <pre><code>   1.0: (3) 7691 [CBM OK] (weakgcr:37)
  8676. </code></pre>
  8677. <p>Or maybe it just reads a little better, because it was a combination of dirt and weak bits.</p>
  8678. <p>In any case, now is the time that you can increase the number of retries with the <code>-e</code> argument. Numbers up to 100 are reasonable; in practice, there is rarely a good read after 30 retries.</p>
  8679. <p>And here is an example of a track with multiple errors that, after cleaning, read correctly after 26 retries:</p>
  8680. <pre><code>  28.0: (1) 6638 [E5S5][E2S6]  
  8681.        (1) 6639 [E5S5][E2S6]  
  8682.        (1) 6638 [E5S5][E2S6]  
  8683.        (1) 6639 [E5S5][E2S6]  
  8684.        (1) 6638 [E5S5][E9S6]  
  8685.        (1) 6638 [E5S5][E9S6]  
  8686.        (1) 6638 [E2S6]  
  8687.        (1) 6638 [E5S5][E9S6]  
  8688.        (1) 6638 [E2S6]  
  8689.        (1) 6638 [E5S5][E2S6]  
  8690.        (1) 6638 [E5S5][E2S6]  
  8691.        (1) 6638 [E5S5][E2S6]  
  8692.        (1) 6638 [E2S6]  
  8693.        (1) 6638 [E5S5][E2S6]  
  8694.        (1) 6638 [E5S5][E2S6]  
  8695.        (1) 6638 [E5S5][E2S6]  
  8696.        (1) 6638 [E5S5][E2S6]  
  8697.        (1) 6638 [E5S5][E2S6]  
  8698.        (1) 6638 [E5S5][E2S6]  
  8699.        (1) 6638 [E5S5][E2S6]  
  8700.        (1) 6638 [E2S6]  
  8701.        (1) 6638 [E2S6]  
  8702.        (1) 6638 [E5S5][E2S6]  
  8703.        (1) 6638 [E5S5][E2S6]  
  8704.        (1) 6638 [E5S5][E2S6]  
  8705.        (1) 6639 [E5S5][E2S6]  
  8706.        (1) 6638 [CBM OK] (weakgcr:5)
  8707. </code></pre>
  8708. <h2 id="Dirty-Read-Head">Dirty Read Head</h2>
  8709. <p>The dirt on a disk can stick to the read head, which will interfere with all reads. Here is an example of a disk dump with a dirty head:</p>
  8710. <pre><code>   1.0: (3) 7801 [CBM OK] (weakgcr:21)  
  8711.   2.0: (3) 7882 [CBM OK] (weakgcr:2)  
  8712.   3.0: (3) 7882 [CBM OK] (weakgcr:5)  
  8713.   4.0: (3) 7775 [CBM OK] (weakgcr:21)  
  8714.   5.0: (3) 7694 [CBM OK] (weakgcr:29)  
  8715.   6.0: (3) 7693 [CBM OK] (weakgcr:27)  
  8716.   7.0: (3) 7693 [CBM OK] (weakgcr:27)  
  8717.   8.0: (3) 7695 [E5S2][E9S13]  
  8718.        (3) 7694 [CBM OK] (weakgcr:23)  
  8719.   9.0: (3) 7696 [E5S3][E9S5][E9S7][E2S10][E5S18][E2S20]  
  8720.        (3) 7696 [E9S7][E2S10]  
  8721.        (3) 7696 [E9S6][E9S7][E5S8][E2S10][E2S15][E2S20]  
  8722.        (3) 7695 [E5S0][E2S2][E9S4][E9S6][E9S7][E9S8][E9S9][E5S10][E9S20]  
  8723.        (3) 7696 [E5S2][E5S4][E9S6][E2S7][E9S8][E9S9][E2S10][E2S11][E2S12][E9S18][E5S19][E9S20]  
  8724.        (3) 7695 [E9S0][E9S1][E9S2][E9S3][E9S4][E9S5][E9S6][E9S7][E2S8][E9S9][E2S10][E2S11][E5S13][E5S14][E2S15][E2S16][E9S17][E2S18][E5S19][E9S20]  
  8725.        (3) 7693 [E9S0][E9S2][E5S3][E2S4][E2S5][E5S6][E9S7][E9S8][E2S9][E2S10][E2S11][E5S12][E9S13][E2S15][E2S16][E2S17][E9S18][E5S19][E9S20]  
  8726.        (3) 7694 [E5S1][E9S2][E2S3][E9S4][E2S5][E9S6][E2S7][E2S8][E2S9][E2S10][E9S11][E2S12][E9S14][E5S15][E5S16][E5S17][E5S18][E9S19][E2S20]  
  8727.        (3) 7691 [E5S0][E2S1][E2S2][E9S3][E2S4][E2S5][E9S6][E2S7][E2S8][E2S9][E2S10][E2S11][E2S12][E5S13][E9S15][E2S16][E2S17][E9S18][E2S20]  
  8728.        (3) 7690 [NDOS]  (weakgcr:29)  
  8729.  [...]
  8730. </code></pre>
  8731. <p>Again, if the errors are radically different between retries, it cannot be an issue of the disk&rsquo;s surface.</p>
  8732. <p>A clean read head looks like this:</p>
  8733. <p><a href="docs/recovering_disks/head_clean.jpg"><img src="docs/recovering_disks/head_clean_small.jpg" alt="" /></a></p>
  8734. <p>And a dirty one like this:</p>
  8735. <p><a href="docs/recovering_disks/head_dirty.jpg"><img src="docs/recovering_disks/head_dirty_small.jpg" alt="" /></a></p>
  8736. <p>You can use the same alcohol-based wipes to clean the read head.</p>
  8737. <h2 id="Conclusion">Conclusion</h2>
  8738. <p>In my experience, more than half of old disks with read errors can be read correctly again after cleaning them.</p>
  8739. <p>If the errors don&rsquo;t go away, and it is a commercial disk, there is of course usually the possibility of buying another copy, and even if that one has errors as well, the data can be spliced together.</p>
  8740. <p>Otherwise, the tool <a href="https://github.com/markusC64/g64conv">g64conv</a> can help: It converts <code>.g64</code> files into a textual representation, which allows you to inspect what exactly went wrong, and may give you the opportunity to extract some information from sections with errors.</p>
  8741. <div class="footnotes">
  8742. <hr/>
  8743. <ol>
  8744. <li id="fn:1">
  8745. <p>Errors can also be cause by <a href="https://www.pagetable.com/?p=1128">incorrectly written on-disk data structures</a>, e.g. because of a buggy driver, or an error when duplicating the disk. We will ignore these kinds of errors in this article.<a href="#fnref:1" rev="footnote">&#8617;</a></p>
  8746. </li>
  8747. </ol>
  8748. </div>
  8749. ]]></content:encoded>
  8750. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1352</wfw:commentRss>
  8751. <slash:comments>5</slash:comments>
  8752. </item>
  8753. <item>
  8754. <title>Commodore &#8220;Magic Voice&#8221; Cartridge for C64</title>
  8755. <link>https://www.pagetable.com/?p=1348</link>
  8756. <comments>https://www.pagetable.com/?p=1348#comments</comments>
  8757. <pubDate>Sun, 26 May 2019 10:02:50 +0000</pubDate>
  8758. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  8759. <category><![CDATA[C64]]></category>
  8760. <category><![CDATA[Commodore]]></category>
  8761. <category><![CDATA[Teardown]]></category>
  8762.  
  8763. <guid isPermaLink="false">https://www.pagetable.com/?p=1348</guid>
  8764. <description><![CDATA[&#8220;Magic Voice&#8221; is an expansion cartridge for the Commodore 64 that can speak 235 predefined words. Here are some pictures. Magic Voice does not combine words from phonemes, but contains 235 prerecorded words in a 16 KB ROM. It was meant to be used with tape, disk or cartridge based software that made use of ... <a title="Commodore &#8220;Magic Voice&#8221; Cartridge for C64" class="read-more" href="https://www.pagetable.com/?p=1348">Read more<span class="screen-reader-text">Commodore &#8220;Magic Voice&#8221; Cartridge for C64</span></a>]]></description>
  8765. <content:encoded><![CDATA[<p>&ldquo;Magic Voice&rdquo; is an expansion cartridge for the Commodore 64 that can speak 235 predefined words. Here are some pictures.</p>
  8766. <p><a href="docs/magic_voice/magic_voice.jpg"><img src="docs/magic_voice/magic_voice_small.jpg" alt="" /></a></p>
  8767. <p>Magic Voice does not combine words from phonemes, but contains 235 prerecorded words in a 16 KB ROM. It was meant to be used with tape, disk or cartridge based software that made use of the extension, but few titles were released.</p>
  8768. <p>Without any extra software, the speech features are accessible through added BASIC commands.</p>
  8769. <p>The canceled <a href="http://www.zimmers.net/cbmpics/cv364.html">Commodore V364</a> was supposed to ship with the same speech Toshiba T6721A chip and a similar driver ROM.</p>
  8770. <p><a href="docs/magic_voice/top.jpg"><img src="docs/magic_voice/top_small.jpg" alt="" /></a></p>
  8771. <p>The case has a passthrough connector on top, which allows using the speech feature in cartridge-based applications. The label says &ldquo;Commodore Magic Voice&rdquo;.</p>
  8772. <p><a href="docs/magic_voice/bottom.jpg"><img src="docs/magic_voice/bottom_small.jpg" alt="" /></a></p>
  8773. <p>There is no label in the designated area on the bottom. A small sticker says &ldquo;MADE IN HONG HONG&rdquo;. It also contains the names of the two connectors on the side: &ldquo;OUT&rdquo; and &ldquo;IN&rdquo;.</p>
  8774. <p><a href="docs/magic_voice/side.jpg"><img src="docs/magic_voice/side_small.jpg" alt="" /></a></p>
  8775. <p>The Audio In (red) and Audio Out (black) RCA connectors are on the side.</p>
  8776. <p><a href="docs/magic_voice/board_front.jpg"><img src="docs/magic_voice/board_front_small.jpg" alt="" /></a></p>
  8777. <p>The board contains</p>
  8778. <ul>
  8779. <li>a MOS 6525A TPI I/O controller (also used in the <a href="https://www.pagetable.com/?p=1303">IEEE-488 cartridge</a>, the <a href="https://www.pagetable.com/?p=1331">1551</a> disk drive, as well as other devices)</li>
  8780. <li>a 16 KB EPROM: This contains the compressed speech data and the C64 code.</li>
  8781. <li>a gate array (address decoding, parallel-serial-conversion)</li>
  8782. <li>the Toshiba <a href="http://www.stefan-uhlmann.de/cbm/MVM/Hardware/T6721A.pdf">T6721A</a> speech chip</li>
  8783. </ul>
  8784. <p><a href="docs/magic_voice/board_back.jpg"><img src="docs/magic_voice/board_back_small.jpg" alt="" /></a></p>
  8785. <h2 id="Emulation">Emulation</h2>
  8786. <p>VICE supports the Magic Voice cartridge by giving it the ROM from <a href="http://www.zimmers.net/anonftp/pub/cbm/schematics/cartridges/c64/magic-voice/index.html">zimmers.net</a>:</p>
  8787. <pre><code>  x64 -magicvoiceimage 251476.bin
  8788. </code></pre>
  8789. <p>Then you can make it speak all words with</p>
  8790. <pre><code>  FOR I = 0 TO 255: SAY I: PRINT I: NEXT
  8791. </code></pre>
  8792. <h2 id="References">References</h2>
  8793. <ul>
  8794. <li><a href="http://www.stefan-uhlmann.de/cbm/MVM/index.html">http://www.stefan-uhlmann.de/cbm/MVM/index.html</a></li>
  8795. <li><a href="http://www.stefan-uhlmann.de/cbm/MVM/Repair/index.html">http://www.stefan-uhlmann.de/cbm/MVM/Repair/index.html</a></li>
  8796. <li><a href="https://binarium.de/commodore_magic_voice_speech_module_c64">https://binarium.de/commodore_magic_voice_speech_module_c64</a></li>
  8797. <li><a href="http://www.floodgap.com/retrobits/ckb/secret/operiph.html">http://www.floodgap.com/retrobits/ckb/secret/operiph.html</a></li>
  8798. <li><a href="http://www.6502.org/users/sjgray/computer/magicvoice/index.html">http://www.6502.org/users/sjgray/computer/magicvoice/index.html</a></li>
  8799. <li><a href="https://www.c64-wiki.de/wiki/Magic_Voice">https://www.c64-wiki.de/wiki/Magic_Voice</a></li>
  8800. <li><a href="http://www.zimmers.net/anonftp/pub/cbm/schematics/cartridges/c64/magic-voice/index.html">http://www.zimmers.net/anonftp/pub/cbm/schematics/cartridges/c64/magic-voice/index.html</a></li>
  8801. <li><a href="http://cbm-hackers.2304266.n4.nabble.com/Magic-Voice-Schematics-td4067465.html">http://cbm-hackers.2304266.n4.nabble.com/Magic-Voice-Schematics-td4067465.html</a></li>
  8802. </ul>
  8803. ]]></content:encoded>
  8804. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1348</wfw:commentRss>
  8805. <slash:comments>1</slash:comments>
  8806. </item>
  8807. <item>
  8808. <title>Falk Rehwagen, TopDesk and GEOS</title>
  8809. <link>https://www.pagetable.com/?p=1344</link>
  8810. <comments>https://www.pagetable.com/?p=1344#respond</comments>
  8811. <pubDate>Sat, 25 May 2019 13:22:09 +0000</pubDate>
  8812. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  8813. <category><![CDATA[C128]]></category>
  8814. <category><![CDATA[C64]]></category>
  8815. <category><![CDATA[Commodore]]></category>
  8816. <category><![CDATA[GEOS]]></category>
  8817.  
  8818. <guid isPermaLink="false">https://www.pagetable.com/?p=1344</guid>
  8819. <description><![CDATA[Here are some of Falk Rehwagen&#8217;s thoughts on his involvement with GEOS and TopDesk. It&#8217;s not so easy to remember the details after more than 25 years :-) GEOS Back then, I was fascinated by what one could get out of this outdated hardware, many years after its release. As for GEOS, I found it ... <a title="Falk Rehwagen, TopDesk and GEOS" class="read-more" href="https://www.pagetable.com/?p=1344">Read more<span class="screen-reader-text">Falk Rehwagen, TopDesk and GEOS</span></a>]]></description>
  8820. <content:encoded><![CDATA[<p>Here are some of Falk Rehwagen&rsquo;s thoughts on his involvement with <a href="https://www.pagetable.com/?p=869">GEOS</a> and <a href="https://www.pagetable.com/?p=1341">TopDesk</a>.</p>
  8821. <blockquote>
  8822. <p>It&rsquo;s not so easy to remember the details after more than 25 years :-)</p>
  8823. </blockquote>
  8824. <h2 id="GEOS">GEOS</h2>
  8825. <blockquote>
  8826. <p>Back then, I was fascinated by what one could get out of this outdated hardware, many years after its release. As for GEOS, I found it exciting that it was possible to develop a real commercial operating system on such a small machine, with a variety of powerful applications, tools, drivers, and so on. Naturally, I bought GEOS as soon as it was possible, paid with my family&rsquo;s <a href="https://en.wikipedia.org/wiki/Begr%C3%BC%C3%9Fungsgeld">welcome money</a> in Berlin, 1990. :-)</p>
  8827. </blockquote>
  8828. <h2 id="Programming-&amp;-GEOS-User-Club">Programming &amp; GEOS User Club</h2>
  8829. <blockquote>
  8830. <p>That&rsquo;s when I got very involved with GEOS (64) and its possibilities on my C128, and it quickly became clear that I wanted to develop applications myself. I a found good and fast entry with the MegaAssembler, which paved the way deep into GEOS development. As part of this learning, I created a diverse set of applications, which, compiled as a &ldquo;best of&rdquo; disk, we also offered for sale. In this context, in 1991/92, I got into contact with the Geos User Club, the association of all GEOS users and fans in Germany. The &ldquo;GUC-Regio-Sachsen&rdquo; was founded, and in 1992, I participated in the GUC Annual General Meeting for the first time. If I remember correctly, this was also the time when TopDesk was almost ready as a the modern replacement for the &ldquo;deskTop&rdquo; built into GEOS. It was absolutely fascinating, because the windowing technology once again showed what can be done with GEOS and the hardware and brought the system closer to the more modern competitors. TopDesk was delayed, but eventually I held it in my hands and used it as my exclusively GEOS control center.</p>
  8831. </blockquote>
  8832. <h2 id="Patch-System-and-GeoCOM">Patch System and GeoCOM</h2>
  8833. <blockquote>
  8834. <p>I was keen to make GEOS better and more flexible. From this thought arose the &ldquo;Patch System&rdquo;, which allowed defining and distributing small improvements and extensions in a consisteny way. In order to give more users and developers the opportunity to develop new applications for GEOS, a powerful programming environment called GeoCom was created, based on ECOM from the 64&#8217;er Magazine. With an extensive manual, created in co-operation with Denis Döhler, the system was offered commercially as a alternative to programming in assemnly.</p>
  8835. </blockquote>
  8836. <h2 id="TopDesk-Maintainer">TopDesk Maintainer</h2>
  8837. <blockquote>
  8838. <p>Around the same time, the market around PCs and operating systems developed rapidly, many GEOS fans following the technological development towards PC/GEOS or other available alternatives. I think sometime in 1993/94, the GUC had completely switched its focus on PC/GEOS and was looking for maintainers for its GEOS-64-related technologies and products. With my spectrum of projects and experience, I was probably a good candidate for this: After all, I remained faithful to the C64/128 and GEOS 64 as a developer. I don&rsquo;t remember when and where exactly, but I was given TopDesk maintainership &#8211; and the sources. :-) It was convenient that TopDesk had been written by the same people as MegaAssembler, so I was already well-equipped for the development of TopDesk.</p>
  8839. </blockquote>
  8840. <h2 id="Improving-GEOS-and-TopDesk">Improving GEOS and TopDesk</h2>
  8841. <blockquote>
  8842. <p>After &ldquo;Patch System&rdquo; and GeoCom, I wanted to develop GEOS in a more profound way, to combine it with the extended TopDesk, and to make the whole system more open to the many hardware enhancements that were available on the market. For this purpose, I completely reverse-engineered the GEOS KERNAL (with all-GEOS tools like GEODISASSEMBLER), so it could be assembled again with MegaAssembler. The idea for GEOS 3.0 was born. Development became much faster after I had gotten a loaner Flash 8 acceleration cartridge (8 MHz) – for which I adapted GEOS. TopDesk got color support, and the GEOS KERNAL was further developed to have a flexible driver model. An early version of the project was presented in Berlin at the GUC Annual General Meeting 1994.</p>
  8843. </blockquote>
  8844. <h2 id="After-GEOS-64">After GEOS 64</h2>
  8845. <blockquote>
  8846. <p>I think I had already decided to take the step towards the PC at that time, to intensively work my way into the PC/GEOS SDK, which had just been published. (However, various sources seem to state that PC/GEOS wasn&rsquo;t released until April 1995&hellip;) At least that&rsquo;s when I decided to turn my back on GEOS 64 (initially) and to take on new challenges as part of my university studies. For this reason, the current state of development of TopDesk and GEOS 3.0 was cleanly returned to the GUC – to Wolfgang Grimm, I think. This soon resulted in version 3.0 of TopDesk, which was developed further as part of the MegaPatch 3.</p>
  8847. </blockquote>
  8848. ]]></content:encoded>
  8849. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1344</wfw:commentRss>
  8850. <slash:comments>0</slash:comments>
  8851. </item>
  8852. <item>
  8853. <title>TopDesk 1.3 GEOS64/128 Original Source</title>
  8854. <link>https://www.pagetable.com/?p=1341</link>
  8855. <comments>https://www.pagetable.com/?p=1341#comments</comments>
  8856. <pubDate>Fri, 24 May 2019 10:26:18 +0000</pubDate>
  8857. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  8858. <category><![CDATA[C128]]></category>
  8859. <category><![CDATA[C64]]></category>
  8860. <category><![CDATA[Commodore]]></category>
  8861. <category><![CDATA[GEOS]]></category>
  8862.  
  8863. <guid isPermaLink="false">https://www.pagetable.com/?p=1341</guid>
  8864. <description><![CDATA[Thanks to Falk Rehwagen and Jürgen Heinisch, the original source of the TopDesk file manager for the GEOS operating system of C64/C128 is available. https://github.com/mist64/TopDesk The source has been converted from GeoWrite format to plain text. These are the original source disks: td64_13_en.d71 td64_13_de.d71 td128_13_de.d71 td128_13_en.d71 Most of the code of the different variants is ... <a title="TopDesk 1.3 GEOS64/128 Original Source" class="read-more" href="https://www.pagetable.com/?p=1341">Read more<span class="screen-reader-text">TopDesk 1.3 GEOS64/128 Original Source</span></a>]]></description>
  8865. <content:encoded><![CDATA[<p>Thanks to Falk Rehwagen and Jürgen Heinisch, the original source of the TopDesk file manager for the GEOS operating system of C64/C128 is available.</p>
  8866. <p><a href="https://github.com/mist64/TopDesk">https://github.com/mist64/TopDesk</a></p>
  8867. <p><img src="docs/topdesk/topdesk_1.png" alt="" /></p>
  8868. <p>The source has been converted from GeoWrite format to plain text.</p>
  8869. <p>These are the original source disks:</p>
  8870. <ul>
  8871. <li><a href="docs/topdesk/td64_13_en.d71">td64_13_en.d71</a></li>
  8872. <li><a href="docs/topdesk/td64_13_de.d71">td64_13_de.d71</a></li>
  8873. <li><a href="docs/topdesk/td128_13_de.d71">td128_13_de.d71</a></li>
  8874. <li><a href="docs/topdesk/td128_13_en.d71">td128_13_en.d71</a></li>
  8875. </ul>
  8876. <p>Most of the code of the different variants is identical.</p>
  8877. <p>These disks use TopDesk-style subdirectories. Here is a listing of the disk contents:</p>
  8878. <pre><code>  NAME                 TYPE   SIZE  
  8879.  --------------------------------  
  8880.  Main/               &lt;DIR&gt;  
  8881.    DeskWindows.akt    WRI    40K  
  8882.    DeskTop.main       WRI    52K  
  8883.    DeskTop.sub        WRI    4.7K  
  8884.    DeskTop.sub2       WRI    4.2K  
  8885.    DeskTop.sub3       WRI    8.6K  
  8886.    DeskTop.sub4       WRI    6.2K  
  8887.    DeskTop.sub5       WRI    7.3K  
  8888.    DeskTop.sub6       WRI    10K  
  8889.    DeskTop.sub7       WRI    12K  
  8890.    DeskTop.sub8       WRI    1.8K  
  8891.    DeskTop.sub9       WRI    7.8K  
  8892.    DeskTop.sub10      WRI    7.8K  
  8893.    Ende.s             WRI    808B  
  8894.    Dos.lnk            WRI    1.0K  
  8895.  Include/            &lt;DIR&gt;  
  8896.    DeskMain2          WRI    20K  
  8897.    SubDir.src         WRI    21K  
  8898.    University 6       FNT    1.0K  
  8899.    Symbol/           &lt;DIR&gt;  
  8900.      TopSym           WRI    8.7K  
  8901.      TopMac           WRI    3.7K  
  8902.      Sym128.erg       WRI    1.1K  
  8903.      CiMac            WRI    1.2K  
  8904.      CiSym            WRI    1.1K  
  8905.  DeskInclude/        &lt;DIR&gt;  
  8906.    Validate+Undelet   WRI    9.0K  
  8907.    SizeRectangle      WRI    2.3K  
  8908.    CopyFile           WRI    11K  
  8909.    DiskCopy           WRI    11K  
  8910.    SearchDisk         WRI    4.0K  
  8911.    EditText           WRI    8.7K  
  8912.    DosFormat.s        WRI    3.0K  
  8913.  WinInclude/         &lt;DIR&gt;  
  8914.    InvFrame           WRI    2.7K  
  8915.    SpeedFrame         WRI    2.2K  
  8916.    InvFrame.old       WRI    2.6K  
  8917.  GetDrivers.dir/     &lt;DIR&gt;  
  8918.    GetDrivers.s       WRI    2.8K  
  8919.    GetDrivers         APP    1.3K  
  8920.    SaveDriver.s       WRI    1.6K  
  8921.  Protect/            &lt;DIR&gt;  
  8922.    Desksub0.s         WRI    1.2K  
  8923.    SetBAM             APP    962B  
  8924.    ProtectDisk        APP    1.3K  
  8925.    Sources/          &lt;DIR&gt;  
  8926.      SetBAM.s         WRI    2.8K  
  8927.      ProtectDisk.s    WRI    2.8K  
  8928.      SetProtection.s  WRI    2.4K  
  8929.      RemProtection.s  WRI    2.4K  
  8930.      RemProt.mod.s    WRI    2.6K  
  8931.      SetProt.mod      APP    762B  
  8932.      RemProt.mod      APP    762B  
  8933.    TopMac(a)          WRI    4.9K  
  8934.    SetProt.mod.s      WRI    2.4K  
  8935.    Desksub0           APP    9.6K
  8936. </code></pre>
  8937. <p>There is a <a href="https://www.pagetable.com/?p=1344">follow-up article with with some history by Falk Rehwagen on the &ldquo;Patch System&rdquo;, TopDesk, and GEOS 3.0</a>.</p>
  8938. ]]></content:encoded>
  8939. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1341</wfw:commentRss>
  8940. <slash:comments>3</slash:comments>
  8941. </item>
  8942. <item>
  8943. <title>Tynemouth Minstrel ZX80 Clone Kit</title>
  8944. <link>https://www.pagetable.com/?p=1337</link>
  8945. <comments>https://www.pagetable.com/?p=1337#comments</comments>
  8946. <pubDate>Thu, 23 May 2019 09:19:33 +0000</pubDate>
  8947. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  8948. <category><![CDATA[Hardware]]></category>
  8949. <category><![CDATA[Teardown]]></category>
  8950.  
  8951. <guid isPermaLink="false">https://www.pagetable.com/?p=1337</guid>
  8952. <description><![CDATA[I have previously shown an unassembled Sinclair ZX81 Kit that could be bought in the early 80s. Today, it is possible to buy a clone of the ZX80 from Tynemouth Software. Here are some pictures. The PCB. The ZX80 and the ZX81 are very similar. The basic difference is that the ZX81 had the 74 ... <a title="Tynemouth Minstrel ZX80 Clone Kit" class="read-more" href="https://www.pagetable.com/?p=1337">Read more<span class="screen-reader-text">Tynemouth Minstrel ZX80 Clone Kit</span></a>]]></description>
  8953. <content:encoded><![CDATA[<p>I have previously shown an unassembled <a href="https://www.pagetable.com/?p=1308">Sinclair ZX81 Kit</a> that could be bought in the early 80s. Today, it is possible to buy a <a href="https://www.tindie.com/products/tynemouthsw/minstrel-zx80-clone/">clone of the ZX80 from Tynemouth Software</a>. Here are some pictures.</p>
  8954. <p><a href="docs/zx80/board_front.jpg"><img src="docs/zx80/board_front_small.jpg" alt="" /></a><br />
  8955. <a href="docs/zx80/board_back.jpg"><img src="docs/zx80/board_back_small.jpg" alt="" /></a></p>
  8956. <p>The PCB. The ZX80 and the ZX81 are very similar. The basic difference is that the ZX81 had the 74 chips combined into the ULA chip. This clone uses off-the-shelf components, and therefore the old ZX80 design with the individual 74s.</p>
  8957. <p><a href="docs/zx80/chips.jpg"><img src="docs/zx80/chips_small.jpg" height="376" width="300" alt="" /></a><a href="docs/zx80/sockets.jpg"><img src="docs/zx80/sockets_small.jpg" height="329" width="300" alt="" /></a></p>
  8958. <p>The ICs and the sockets. ROM, RAM, CPU, and lots of 74s.</p>
  8959. <p><a href="docs/zx80/board_front_sockets.jpg"><img src="docs/zx80/board_front_sockets_small.jpg" alt="" /></a></p>
  8960. <p>They board comes with the sockets already placed.</p>
  8961. <p><a href="docs/zx80/components.jpg"><img src="docs/zx80/components_small.jpg" height="343" width="300" alt="" /></a><a href="docs/zx80/feet.jpg"><img src="docs/zx80/feet_small.jpg" height="374" width="300" alt="" /></a></p>
  8962. <p>The other components, mounting pillars and feet.</p>
  8963. <p><a href="docs/zx80/plate1.jpg"><img src="docs/zx80/plate1_small.jpg" height="323" width="300" alt="" /></a><a href="docs/zx80/plate2.jpg"><img src="docs/zx80/plate2_small.jpg" height="320" width="300" alt="" /></a></p>
  8964. <p>It doesn&rsquo;t come with a case, but it can be mounted onto this baseplate instead.</p>
  8965. <p><a href="docs/zx80/keyboard.jpg"><img src="docs/zx80/keyboard_small.jpg" alt="" /></a></p>
  8966. <p>The ZX81-style membrane keyboard. (There is also an option for a keyboard with switches.)</p>
  8967. <p><a href="docs/zx80/keyboard2.jpg"><img src="docs/zx80/keyboard2_small.jpg" alt="" /></a><br />
  8968. <a href="docs/zx80/keyboard3.jpg"><img src="docs/zx80/keyboard3_small.jpg" alt="" /></a></p>
  8969. <p>And ZX80 style keyboard overlays.</p>
  8970. <p><a href="docs/zx80/psu.jpg"><img src="docs/zx80/psu_small.jpg" height="222" width="300" alt="" /></a><a href="docs/zx80/psu3.jpg"><img src="docs/zx80/psu3_small.jpg" height="475" width="300" alt="" /></a><a href="docs/zx80/cables.jpg"><img src="docs/zx80/cables_small.jpg" height="470" width="300" alt="" /></a></p>
  8971. <p>The power supply and the cables.</p>
  8972. <p><a href="Tynemouth%20Software%20Minstrel%20ZX80%20Clone%20Kit.pdf"><img src="docs/zx80/Tynemouth%20Software%20Minstrel%20ZX80%20Clone%20Kit.png" height="212" width="300" alt="" /></a></p>
  8973. <p>Assembly instructions. Includes schematics. (0.6 MB)</p>
  8974. ]]></content:encoded>
  8975. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1337</wfw:commentRss>
  8976. <slash:comments>2</slash:comments>
  8977. </item>
  8978. <item>
  8979. <title>Converting a  Commodore 1541 or 1570 Drive into a 1551</title>
  8980. <link>https://www.pagetable.com/?p=1331</link>
  8981. <comments>https://www.pagetable.com/?p=1331#comments</comments>
  8982. <pubDate>Wed, 22 May 2019 09:09:25 +0000</pubDate>
  8983. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  8984. <category><![CDATA[Commodore]]></category>
  8985. <category><![CDATA[Floppy Disks]]></category>
  8986. <category><![CDATA[Hardware]]></category>
  8987. <category><![CDATA[TED]]></category>
  8988.  
  8989. <guid isPermaLink="false">https://www.pagetable.com/?p=1331</guid>
  8990. <description><![CDATA[The 1551 is the weird outlier in the family of Commodore disk drives: It is the only drive using the &#8220;TCBM&#8221; bus instead of Serial or IEEE-488. And what&#8217;s even weirder? 1541 and 1570 drives converted into 1551s! The first Commodore disk drives for the PET were using the industry-standard parallel IEEE-488 bus. For cost ... <a title="Converting a  Commodore 1541 or 1570 Drive into a 1551" class="read-more" href="https://www.pagetable.com/?p=1331">Read more<span class="screen-reader-text">Converting a  Commodore 1541 or 1570 Drive into a 1551</span></a>]]></description>
  8991. <content:encoded><![CDATA[<p>The 1551 is the weird outlier in the family of Commodore disk drives: It is the only drive using the &ldquo;TCBM&rdquo; bus instead of Serial or IEEE-488. And what&rsquo;s even weirder? 1541 and 1570 drives converted into 1551s!</p>
  8992. <p><a href="docs/1551_mod/1541-1570.jpg"><img src="docs/1551_mod/1541-1570.jpg" alt="" /></a></p>
  8993. <p>The first Commodore disk drives for the PET were using the industry-standard parallel <a href="https://www.pagetable.com/?p=1023">IEEE-488</a> bus. For cost reasons, Commodore switched to a custom <a href="https://www.pagetable.com/?p=1135">serial</a> version of the bus with the VIC-20 and the C64. But serial was slow, and IEEE-488 was expensive. <a href="https://www.pagetable.com/?p=1324">TCBM</a> was supposed to be the compromise. There was no connector on the drive side, and the computer side was a cartridge (&ldquo;paddle&rdquo;) with the I/O chip – which was shipped with the drive instead of the computer, to keep the cost of the computer low.</p>
  8994. <h2 id="Pictures">Pictures</h2>
  8995. <p><a href="docs/1551_mod/1541_front.jpg"><img src="docs/1551_mod/1541_front_small.jpg" alt="" /></a></p>
  8996. <p>The modded 1541 looks just like a 1541 from the front, except for the device 8/9 switch – but many 1541 drives had that added.</p>
  8997. <p><a href="docs/1551_mod/1570_front.jpg"><img src="docs/1551_mod/1570_front_small.jpg" alt="" /></a></p>
  8998. <p>The 1570 has the power LED converted into a reset button with a power LED, and on the right, there is an added device 8/9 toggle button with an LED indicating the current device number.</p>
  8999. <p><a href="docs/1551_mod/1541_back.jpg"><img src="docs/1551_mod/1541_back_small.jpg" alt="" /></a></p>
  9000. <p>On the back, the 1541 has two holes where the serial ports were, and the added 1551 paddle cable.</p>
  9001. <p><a href="docs/1551_mod/1570_back.jpg"><img src="docs/1551_mod/1570_back_small.jpg" alt="" /></a></p>
  9002. <p>The 1570 also has empty holes, but it uses a DB15 connector for the paddle.</p>
  9003. <p><a href="docs/1551_mod/1541_inside.jpg"><img src="docs/1551_mod/1541_inside_small.jpg" alt="" /></a></p>
  9004. <p>The mechanics of the 1541 are unchanged, but the board has been replaced with a 1551 board.</p>
  9005. <p><a href="docs/1551_mod/1570_inside.jpg"><img src="docs/1551_mod/1570_inside_small.jpg" alt="" /></a></p>
  9006. <p>Same on the 1570.</p>
  9007. <p><a href="docs/1551_mod/1541_board.jpg"><img src="docs/1551_mod/1541_board_small.jpg" alt="" /></a></p>
  9008. <p>Here is a closeup of the 1551 board in the 1541. It contains a MOS 6510T CPU (2 MHz version of the C64&rsquo;s 6510 CPU) and a MOS 6525 TPI I/O controller.</p>
  9009. <p>The top left connector is power. The four white connectors at the bottom and the black connector at the top lead to the drive mechanics and are just compatible between the 1541 and the 1551. The black cable on the left goes to the paddle.</p>
  9010. <p>A standard 1551 does not have a switch to change the device number. Cutting the trace at JP1 would switch it from 8 to 9. Here, the trace is cut, and the yellow/white wires lead to the switch at the front.</p>
  9011. <p><a href="docs/1551_mod/1570_board.jpg"><img src="docs/1551_mod/1570_board_small.jpg" alt="" /></a></p>
  9012. <p>Things look similar in the 1570. JP1 is cut and connected here as well (blue/green) and goes to the toggle switch on the right of the front panel. But there are two more connectors: the orange/red one at the top right (drive number LED), and the white/gray one at the bottom center (reset button).</p>
  9013. <p><a href="docs/1551_mod/gw1.jpg"><img src="docs/1551_mod/gw1_small.jpg" height="199" width="300" alt="" /></a><a href="docs/1551_mod/gw2.jpg"><img src="docs/1551_mod/gw2_small.jpg" height="199" width="300" alt="" /></a></p>
  9014. <p>These pictures compare the unmodified board with the modified one. The white/gray wires move the capacitor C13 and add a header that leads to the button on the left of the front panel. Pressing the button causes a reset.</p>
  9015. <p><a href="docs/1551_mod/ro1.jpg"><img src="docs/1551_mod/ro1_small.jpg" height="204" width="300" alt="" /></a><a href="docs/1551_mod/ro2.jpg"><img src="docs/1551_mod/ro2_small.jpg" height="204" width="300" alt="" /></a></p>
  9016. <p>The newly added orange/red wires connect to the LED on the right of the front panel. The LED will show whether the device number is 8 or 9.</p>
  9017. <p><a href="docs/1551_mod/paddle_board_front.jpg"><img src="docs/1551_mod/paddle_board_front_small.jpg" alt="" /></a></p>
  9018. <p>The modded 1541 uses a regular 1551 paddle. It contains a 251641-03 PLA and a 6523T TPI I/O controller. The 6323T TPI is a chip that was only ever used in the paddle. It is the DIP-28 version of the DIP-40 6525 TPI (used in the drive), i.e. the same die in a smaller package. The TPI has three times 8 GPIOs (PA, PB, PC), but on the 6523T version, only PA, PB0/1 and PC6/7 are exposed, which is just enough for the 12 wire TCBM protocol. In case the 6523T ever breaks, <a href="http://www.zimmers.net/anonftp/pub/cbm/documents/projects/drives/1551-tia.gif">a full 6525 can be used in its place</a>.</p>
  9019. <p>The PLA is the address decoder to map the TIA to either $FEC0 or $FEE0 in the computer&rsquo;s address space. In case the PLA breaks, it can be replaced by a <a href="https://www.polyplay.xyz/PLAdvanced-PLA-Replacement_1">PLAdvanced</a> or a <a href="https://icomp.de/shop-icomp/en/shop/product/superpla-v4.html">SuperPLA V4</a>.</p>
  9020. <p><a href="docs/1551_mod/paddle_board_back.jpg"><img src="docs/1551_mod/paddle_board_back_small.jpg" alt="" /></a></p>
  9021. <p>This is the back of the paddle.</p>
  9022. <p><a href="docs/1551_mod/board_front.jpg"><img src="docs/1551_mod/board_front_small.jpg" alt="" /></a></p>
  9023. <p>The modded 1570 came with a hand-built paddle. It uses an original 251641-03 PLA with a date code of 2190. 1990 is very late for a chip that was only used in disk drives dicontinued not long after 1985. Commodore may have been producing it as a replacement part for quite a while<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>.</p>
  9024. <p>The TPI is the DIP-40 6525 described earlier. The extra pins are just not connected. The 6523T was probably not available as a replacement part from Commodore.</p>
  9025. <p><a href="docs/1551_mod/board_back.jpg"><img src="docs/1551_mod/board_back_small.jpg" alt="" /></a></p>
  9026. <p>And here is the back of the hand-built paddle. The schematics are pretty simple. The <a href="http://www.zimmers.net/anonftp/pub/cbm/schematics/drives/new/1551/paddle-251925.png">original schematics</a> exist, as well as a <a href="http://www.zimmers.net/anonftp/pub/cbm/schematics/drives/new/1551/1551crt.gif">newer reverse-engineered version</a>.</p>
  9027. <p><a href="docs/1551_mod/connector.jpg"><img src="docs/1551_mod/connector_small.jpg" alt="" /></a></p>
  9028. <p>This is the DB15 connector used to connect the paddle to the drive.</p>
  9029. <h2 id="Conclusion">Conclusion</h2>
  9030. <p>The modded 1541 was most likely created by combining a 1551 that had dead mechanics with a 1541 that possibly had a dead board.</p>
  9031. <p>The 1570 was more likely modded from scratch. The 1551 board might have been available as a replacement part that could be ordered from Commodore. The paddle either wasn&rsquo;t available as a replacement part, or was too expensive, so it was hand-built using Commodore replacement ICs.</p>
  9032. <p>The mechanics and the cases of both the 1541 and the 1570 are compatible with the 1551 electronics, so it is possible to revive a 1551 with dead mechanics, or even build a 1551 from a 1541/1570 and a 1551 board and some extra parts.</p>
  9033. <h2 id="Things-To-Do">Things To Do</h2>
  9034. <p>The 1570&rsquo;s reset switch in the power LED and the 8/9 switch with an LED indicator are great modifications. It would be interesting to understand how they work using the schematics, and write instructions for modding other 1551-like devices like this.</p>
  9035. <div class="footnotes">
  9036. <hr/>
  9037. <ol>
  9038. <li id="fn:1">
  9039. <p>PLA stands for &ldquo;Programmable Logic Array&rdquo;, so Commodore could easily create more of these chips at any time.<a href="#fnref:1" rev="footnote">&#8617;</a></p>
  9040. </li>
  9041. </ol>
  9042. </div>
  9043. ]]></content:encoded>
  9044. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1331</wfw:commentRss>
  9045. <slash:comments>4</slash:comments>
  9046. </item>
  9047. <item>
  9048. <title>More Original Commodore Source Code</title>
  9049. <link>https://www.pagetable.com/?p=1333</link>
  9050. <comments>https://www.pagetable.com/?p=1333#respond</comments>
  9051. <pubDate>Tue, 21 May 2019 10:56:26 +0000</pubDate>
  9052. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  9053. <category><![CDATA[6502]]></category>
  9054. <category><![CDATA[Archeology]]></category>
  9055. <category><![CDATA[Commodore]]></category>
  9056. <category><![CDATA[Commodore Peripheral Bus]]></category>
  9057. <category><![CDATA[Floppy Disks]]></category>
  9058. <category><![CDATA[GitHub]]></category>
  9059.  
  9060. <guid isPermaLink="false">https://www.pagetable.com/?p=1333</guid>
  9061. <description><![CDATA[There have been a few new interesting additions to the Commodore Source Code repository, including: RAMDOS: An RAM disk implementation of Commodore DOS that runs on the computer side and stores the data in a REU. It supports most of the Commodore DOS API including relative files. 1570: Commodore sold a single-side stripped down 1571-derivative ... <a title="More Original Commodore Source Code" class="read-more" href="https://www.pagetable.com/?p=1333">Read more<span class="screen-reader-text">More Original Commodore Source Code</span></a>]]></description>
  9062. <content:encoded><![CDATA[<p>There have been a few new interesting additions to the <a href="https://github.com/mist64/cbmsrc">Commodore Source Code</a> repository, including:</p>
  9063. <ul>
  9064. <li>RAMDOS: An RAM disk implementation of <a href="https://www.pagetable.com/?p=1038">Commodore DOS</a> that runs on the computer side and stores the data in a REU. It supports most of the Commodore DOS API including relative files.</li>
  9065. <li>1570: Commodore sold a single-side stripped down 1571-derivative for a while.</li>
  9066. <li>1571CR: The cost-reduced 1571 with the integrated 5710 I/O and FDC controller.</li>
  9067. <li>1551: The odd one, with the one-off <a href="https://www.pagetable.com/?p=1324">TCBM</a> bus. The source is odd too, because all filenames have been renamed to three characters.</li>
  9068. <li>1541: Three original versions of the 1541 DOS source. (This amends my previous reconstructions from the 1540 and 1571 sources.)</li>
  9069. </ul>
  9070. <p>I am planning to add more source. I&rsquo;m happy for any pointers.</p>
  9071. ]]></content:encoded>
  9072. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1333</wfw:commentRss>
  9073. <slash:comments>0</slash:comments>
  9074. </item>
  9075. <item>
  9076. <title>Commodore Peripheral Bus: Part 5: TCBM</title>
  9077. <link>https://www.pagetable.com/?p=1324</link>
  9078. <comments>https://www.pagetable.com/?p=1324#comments</comments>
  9079. <pubDate>Mon, 20 May 2019 15:00:17 +0000</pubDate>
  9080. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  9081. <category><![CDATA[Commodore]]></category>
  9082. <category><![CDATA[Commodore Peripheral Bus]]></category>
  9083. <category><![CDATA[Floppy Disks]]></category>
  9084. <category><![CDATA[TED]]></category>
  9085.  
  9086. <guid isPermaLink="false">https://www.pagetable.com/?p=1324</guid>
  9087. <description><![CDATA[In the series about the variants of the Commodore Peripheral Bus family, this article covers the lowest two layers (electrical and byte transfer) of the &#8220;TCBM&#8221; bus as found on the TED series computers: the C16, C116 and the Plus/4. NOTE: I am releasing one part every week, at which time links will be added ... <a title="Commodore Peripheral Bus: Part 5: TCBM" class="read-more" href="https://www.pagetable.com/?p=1324">Read more<span class="screen-reader-text">Commodore Peripheral Bus: Part 5: TCBM</span></a>]]></description>
  9088. <content:encoded><![CDATA[<p>In the <a href="https://www.pagetable.com/?p=1018">series about the variants of the Commodore Peripheral Bus family</a>, this article covers the lowest two layers (electrical and byte transfer) of the &ldquo;TCBM&rdquo; bus as found on the TED series computers: the C16, C116 and the Plus/4.</p>
  9089. <p><img src="docs/cbmbus/tcbm_layers.png" height="241" width="371" alt="" /></p>
  9090. <hr/>
  9091. <blockquote>
  9092. <p><strong><em>NOTE:</em></strong>  I am releasing one part every week, at which time links will be added to the bullet points below. The articles will also be announced on my Twitter account <a href="https://twitter.com/pagetable">@pagetable</a> and my Mastodon account <a href="https://mastodon.social/@pagetable">@pagetable&#64;mastodon.social</a>.</p>
  9093. </blockquote>
  9094. <hr/>
  9095. <ul>
  9096. <li><a href="https://www.pagetable.com/?p=1018">Part 0: Overview and Introduction</a></li>
  9097. <li><a href="https://www.pagetable.com/?p=1023">Part 1: IEEE-488</a> [PET/CBM Series; 1977]</li>
  9098. <li><a href="https://www.pagetable.com/?p=1031">Part 2: The TALK/LISTEN Layer</a></li>
  9099. <li><a href="https://www.pagetable.com/?p=1038">Part 3: The Commodore DOS Layer</a></li>
  9100. <li><a href="https://www.pagetable.com/?p=1135">Part 4: Standard Serial (IEC)</a> [VIC-20, C64; 1981]</li>
  9101. <li><strong>Part 5: TCBM [C16, C116, Plus/4; 1984]</strong> ← <em>this article</em></li>
  9102. <li>Part 6: JiffyDOS [1985] <em>(coming soon)</em></li>
  9103. <li>Part 7: Fast Serial [C128; 1986] <em>(coming soon)</em></li>
  9104. <li>Part 8: CBDOS [C65; 1991] <em>(coming soon)</em></li>
  9105. </ul>
  9106. <h2 id="Naming">Naming</h2>
  9107. <p>The only computers with a TCBM bus are the Commodore C16, C116 and Plus/4. Internally, Commodore called these the TED series, named after the core IC of the system<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>. This name can be seen in multiple places in the <a href="https://github.com/mist64/cbmsrc">TED KERNAL and BASIC sources</a> as well as <a href="https://www.pagetable.com/?p=541">internal documentation</a>. Even though Commodore never marketed these computers under the name TED, many products around them, like software cartridges, had product codes prefixed with &ldquo;T&rdquo;<sup id="fnref:2"><a href="#fn:2" rel="footnote">2</a></sup>.</p>
  9108. <p>The naming for the new peripheral bus of the TED is not completely consistent:</p>
  9109. <ul>
  9110. <li>Comments in the <a href="https://github.com/mist64/cbmsrc/tree/master/KERNAL_TED_05">KERNAL driver code</a> call it the <strong>Kennedy</strong> or <strong>KDY</strong> interface – a reference to <a href="https://en.wikipedia.org/wiki/Ted_Kennedy">the politician</a>. Some places call it <strong>TEDISK</strong>.</li>
  9111. <li>The 1551 power-on message is &ldquo;CBM DOS 2.6 <strong>TDISK</strong>&rdquo;, which seems a variant of &ldquo;TEDISK&rdquo;.</li>
  9112. <li>The <a href="http://www.zimmers.net/anonftp/pub/cbm/manuals/drives/1551_Disk_Drive_Users_Guide.pdf">users manual</a> and the <a href="http://www.zimmers.net/anonftp/pub/cbm/schematics/drives/new/1551/251860.gif">schematics</a> of the 1551 disk drive call it <strong>TCBM</strong>. &ldquo;CBM&rdquo; is the common abbreviation of &ldquo;Commodore Business Machines&rdquo;, and the &ldquo;T&rdquo;, as always, stands for &ldquo;TED&rdquo;.</li>
  9113. </ul>
  9114. <h2 id="History-and-Development">History and Development</h2>
  9115. <p>The Commodore PET (1977) was using the industry-standard 8-bit parallel <a href="https://www.pagetable.com/?p=1023">IEEE-488 bus</a> for disk drives and printers. For the VIC-20 (1981), Commodore changed layers 1 and 2 of the protocol stack (electrical and byte transfer) into a cheaper <a href="https://www.pagetable.com/?p=1135">serial bus</a>, which turned out to have severe speed problems<sup id="fnref:3"><a href="#fn:3" rel="footnote">3</a></sup>.</p>
  9116. <p>For the TED series (1984), Commodore decided to create another variant of the protocol stack, replacing layers 1 and 2 again. The new bus was supposed to combine the speed of the IEEE-488 bus with the low cost of the serial bus.</p>
  9117. <p>While the switch from parallel IEEE-488 to serial allowed the protocol stack to retain all key properties of the original design, TCBM drops some of these features:</p>
  9118. <table>
  9119. <thead>
  9120. <tr>
  9121. <th> Feature </th>
  9122. <th> IEEE-488 </th>
  9123. <th> Serial </th>
  9124. <th> TCBM </th>
  9125. </tr>
  9126. </thead>
  9127. <tbody>
  9128. <tr>
  9129. <td> All participants are <strong>daisy-chained</strong>. </td>
  9130. <td> Yes </td>
  9131. <td> Yes </td>
  9132. <td> <strong>No</strong> </td>
  9133. </tr>
  9134. <tr>
  9135. <td> <strong>One dedicated controller</strong> (the computer) does bus arbitration of <strong>up to 31 devices</strong>. </td>
  9136. <td> Yes </td>
  9137. <td> Yes </td>
  9138. <td> <strong>No</strong> </td>
  9139. </tr>
  9140. <tr>
  9141. <td> <strong>One-to-many</strong>: Any participant can send data to any set of participants. </td>
  9142. <td> Yes </td>
  9143. <td> Yes </td>
  9144. <td> <strong>No</strong> </td>
  9145. </tr>
  9146. <tr>
  9147. <td> A device has <strong>multiple channels</strong> for different functions. </td>
  9148. <td> Yes </td>
  9149. <td> Yes </td>
  9150. <td> Yes </td>
  9151. </tr>
  9152. <tr>
  9153. <td> Data transmission is <strong>byte stream</strong> based. </td>
  9154. <td> Yes </td>
  9155. <td> Yes </td>
  9156. <td> Yes </td>
  9157. </tr>
  9158. </tbody>
  9159. </table>
  9160. <p>A device still has multiple channels, and all data transmission is still byte stream based, because these are properties of the layers 3 and 4, which are retained in TCBM.</p>
  9161. <p>The key difference is that the TCBM bus is point-to-point: The bus is between one controller (the computer) and one device. For connecting multiple devices to one computer, the computer needs one dedicated bus per device.</p>
  9162. <p>With the introduction of Fast Serial with the C128, Commodore gave up the TCBM bus, which had only shipped with the TED series.</p>
  9163. <h2 id="Layer-1:-Electrical">Layer 1: Electrical</h2>
  9164. <h3 id="Connectors-and-Pinout">Connectors and Pinout</h3>
  9165. <p>There are no standardized connectors. Both on the computer and the device side, the 17 wires are connected directly to the board through pin headers. This is the computer side:</p>
  9166. <p><a href="docs/cbmbus/tcbm_connector.jpg"><img src="docs/cbmbus/tcbm_connector_small.jpg" height="311" width="600" alt="" /></a></p>
  9167. <p>And the device side:</p>
  9168. <p><a href="docs/cbmbus/tcbm_connector2.jpg"><img src="docs/cbmbus/tcbm_connector2_small.jpg" height="311" width="600" alt="" /></a></p>
  9169. <p>This is the pinout:</p>
  9170. <table>
  9171. <thead>
  9172. <tr>
  9173. <th> Pin </th>
  9174. <th> Signal  </th>
  9175. <th> Description </th>
  9176. </tr>
  9177. </thead>
  9178. <tbody>
  9179. <tr>
  9180. <td>  1  </td>
  9181. <td> GND     </td>
  9182. <td> Ground      </td>
  9183. </tr>
  9184. <tr>
  9185. <td>  2  </td>
  9186. <td> DEV     </td>
  9187. <td> Device 0/1  </td>
  9188. </tr>
  9189. <tr>
  9190. <td>  3  </td>
  9191. <td> DIO1    </td>
  9192. <td> Data I/O    </td>
  9193. </tr>
  9194. <tr>
  9195. <td>  4  </td>
  9196. <td> DIO2    </td>
  9197. <td> Data I/O    </td>
  9198. </tr>
  9199. <tr>
  9200. <td>  5  </td>
  9201. <td> DIO3    </td>
  9202. <td> Data I/O    </td>
  9203. </tr>
  9204. <tr>
  9205. <td>  6  </td>
  9206. <td> DIO4    </td>
  9207. <td> Data I/O    </td>
  9208. </tr>
  9209. <tr>
  9210. <td>  7  </td>
  9211. <td> DIO5    </td>
  9212. <td> Data I/O    </td>
  9213. </tr>
  9214. <tr>
  9215. <td>  8  </td>
  9216. <td> DIO6    </td>
  9217. <td> Data I/O    </td>
  9218. </tr>
  9219. <tr>
  9220. <td>  9  </td>
  9221. <td> DIO7    </td>
  9222. <td> Data I/O    </td>
  9223. </tr>
  9224. <tr>
  9225. <td> 10  </td>
  9226. <td> DIO8    </td>
  9227. <td> Data I/O    </td>
  9228. </tr>
  9229. <tr>
  9230. <td> 11  </td>
  9231. <td> DAV     </td>
  9232. <td> Data Valid  </td>
  9233. </tr>
  9234. <tr>
  9235. <td> 12  </td>
  9236. <td> STATUS0 </td>
  9237. <td> Status 0    </td>
  9238. </tr>
  9239. <tr>
  9240. <td> 13  </td>
  9241. <td> ACK     </td>
  9242. <td> Acknowledge </td>
  9243. </tr>
  9244. <tr>
  9245. <td> 14  </td>
  9246. <td> STATUS1 </td>
  9247. <td> Status 1    </td>
  9248. </tr>
  9249. <tr>
  9250. <td> 15  </td>
  9251. <td> RESET   </td>
  9252. <td> Reset       </td>
  9253. </tr>
  9254. <tr>
  9255. <td> 16  </td>
  9256. <td> GND     </td>
  9257. <td> Ground      </td>
  9258. </tr>
  9259. <tr>
  9260. <td> 17  </td>
  9261. <td> GND     </td>
  9262. <td> Ground      </td>
  9263. </tr>
  9264. </tbody>
  9265. </table>
  9266. <ul>
  9267. <li>There are 8 data lines, DIO1-8<sup id="fnref:4"><a href="#fn:4" rel="footnote">4</a></sup>.</li>
  9268. <li>The two STATUS lines are used by the device to signal errors.</li>
  9269. <li>The DAV and ACK lines are used for handshaking.</li>
  9270. <li>The RESET line resets the device.</li>
  9271. <li>The DEV line tells the paddle whether to create bus #0 or #1 (see below).</li>
  9272. </ul>
  9273. <h3 id="Open-Collector-Logic">Open Collector Logic</h3>
  9274. <p><!--- this is *similiar* to the text in part 1 --></p>
  9275. <p>The DIO lines are TTL open collector, which means:</p>
  9276. <ul>
  9277. <li>Both participants of the bus can not only read, but also write to the line.</li>
  9278. <li>When <strong>both</strong> participants write 0, the line will read back 0, but if either device writes 1, the bus will read back as 1.</li>
  9279. <li>The logic is inverted: 5V is 0 (false), and 0V is 1 (true).</li>
  9280. </ul>
  9281. <p>In other words: If the line is <em>released</em> by both bus participants, it will be 5V (logically 0), and either participant can <em>pull</em> it to 0V (logically 1).</p>
  9282. <p>This can be visualized with two hands that can pull the line to 1, and a spring that pushes it to 0:</p>
  9283. <p><img src="docs/cbmbus/open_collector.gif" height="162" width="302" alt="" /></p>
  9284. <p>So when a line reads as 0, it is known that it is currently released by all participants, and if a line reads as 1, one or more participants are pulling it, but it is impossible to know who or even how many.</p>
  9285. <h3 id="Paddle">Paddle</h3>
  9286. <p>The TED series spans from the super-low-cost<sup id="fnref:5"><a href="#fn:5" rel="footnote">5</a></sup> C116 (rubber keyboard, 16 KB) to the high-end Plus/4 (pro keyboard, 64 KB, RS232, built-in productivity software). The <a href="https://www.pagetable.com/?p=1135">Standard Serial</a> port only requires 3 GPIO lines and was natively supported by all TED machines. A parallel bus would have required adding an additional I/O controller.</p>
  9287. <p>To save on costs, the I/O controller did not come with the computer. Instead, it shipped with the disk drive, where the costs of the chip were eclipsed by the total cost of the drive (USD 269).</p>
  9288. <p>The 1551 disk drive, the only TCBM device made, came with a fixed cable that ended in the so-called &ldquo;Paddle&rdquo;, a cartridge for the TED expansion port. The expansion port on Commodore computers exposes the complete internal bus, allowing the I/O controller in the paddle to map itself into the computer&rsquo;s address space.</p>
  9289. <p>Each paddle has a pass-through connector to allow using any other cartridge at the same time – or to connect a another paddle.</p>
  9290. <p>A second 1551 required its own paddle on top of the first one. In this setup, there is one I/O controller in each paddle, which is mapped to one of two locations in the computer&rsquo;s address space. A switch in the disk drive tells the paddle through the DEV line which location and thus which device number the drive should have<sup id="fnref:6"><a href="#fn:6" rel="footnote">6</a></sup>.</p>
  9291. <p><a href="docs/cbmbus/tcbm_paddle.jpg"><img src="docs/cbmbus/tcbm_paddle_small.jpg" height="494" width="300" alt="" /></a><a href="docs/cbmbus/tcbm_paddle_board.jpg"><img src="docs/cbmbus/tcbm_paddle_board_small.jpg" height="408" width="300" alt="" /></a><a href="docs/cbmbus/tcbm_paddle_connected.jpg"><img src="docs/cbmbus/tcbm_paddle_connected_small.jpg" height="247" width="300" alt="" /></a></p>
  9292. <h4 id="Multiple-Busses">Multiple Busses</h4>
  9293. <p>The TED computers support the Standard Serial bus and send all traffic to devices 4-30 to this bus. If a paddle with DEV = 0 is detected, it will direct traffic to drive 8 to this TCBM bus. And if there is a paddle with DEV = 1, all traffic to drive 9 will go to that TCBM bus. This allows a TED to have up to three separate Commodore Peripheral busses. All participants of the Standard Serial bus can talk to each other, but the TCBM busses are point-to-point<sup id="fnref:7"><a href="#fn:7" rel="footnote">7</a></sup>.</p>
  9294. <p>The presence of a TCBM device is detected<sup id="fnref:8"><a href="#fn:8" rel="footnote">8</a></sup> whenever a communication channel is initiated (<a href="https://www.pagetable.com/?p=1031">layer 3</a> TALK or LISTEN) for devices 8 or 9.</p>
  9295. <h2 id="Layer-2:-Byte-Transfer">Layer 2: Byte Transfer</h2>
  9296. <p>The byte transfer layer of TCBM defines the roles of the controller (which is the computer) and the device.</p>
  9297. <p>The controller and the device each own one handshaking line: DAV and ACK, respectively. The names stand for &ldquo;Data Valid&rdquo; and &ldquo;Acklowledge&rdquo;, but in practice, they are just general handshaking lines with different meanings across the protocol.</p>
  9298. <p>The device also owns the two STATUS lines to communicacte error codes to the controller. The eight DIO lines are shared and used to send data in either direction. The protocol defines at what point which participant owns it.</p>
  9299. <p>All communication is initiated by the controller by telling the device whether data is sent to it or received from it, or whether a layer 3 command is sent to the device.</p>
  9300. <h3 id="Sending-Bytes">Sending Bytes</h3>
  9301. <p>When the controller sends data to the device, it owns the eight DIO lines during the whole process. The basic idea is that the controller sends a TCBM code of 0x83 to indicate a byte send, then sends the byte, and the device returns a two-bit status code.</p>
  9302. <p><img src="docs/cbmbus/tcbm-send.gif" height="577" width="601" alt="" /></p>
  9303. <p>Let&rsquo;s go through it step by step:</p>
  9304. <h4 id="0:-Initial-State">0: Initial State</h4>
  9305. <p><img src="docs/cbmbus/tcbm-01.png" height="261" width="601" alt="" /><br />
  9306. In the initial state, the controller is holding the DAV line to indicate that it it is not ready for the next transmission. The device is holding the ACK line, meaning it is not ready either. The DIO and ST lines are all released.</p>
  9307. <h4 id="1:-Controller-puts-code-0x83-on-the-bus">1: Controller puts code 0x83 on the bus</h4>
  9308. <p><img src="docs/cbmbus/tcbm-02.png" height="261" width="601" alt="" /><br />
  9309. All communication is initiated by the controller by putting a TCBM code byte onto DIO. 0x83 means that the controller intends to send a byte to the device.</p>
  9310. <h4 id="2:-Device-has-accepted-the-code">2: Device has accepted the code</h4>
  9311. <p><img src="docs/cbmbus/tcbm-03.png" height="261" width="601" alt="" /><br />
  9312. At some point the device is done handling the previous byte it may have received, so it detects that the most significant bit of DIO is set, reads the TCBM code, and signals that it has received the code by releasing ACK.</p>
  9313. <h4 id="3:-Controller-puts-data-on-the-bus">3: Controller puts data on the bus</h4>
  9314. <p><img src="docs/cbmbus/tcbm-04.png" height="261" width="601" alt="" /><br />
  9315. Triggered by ACK being 0, the controller now puts the byte value onto DIO.</p>
  9316. <h4 id="4:-Data-on-bus-is-now-valid">4: Data on bus is now valid</h4>
  9317. <p><img src="docs/cbmbus/tcbm-05.png" height="261" width="601" alt="" /><br />
  9318. After that, the controller releases DAV, signaling that the data in DIO is valid.</p>
  9319. <h4 id="5:-Device-puts-status-on-the-bus">5: Device puts status on the bus</h4>
  9320. <p><img src="docs/cbmbus/tcbm-06.png" height="261" width="601" alt="" /><br />
  9321. Triggered by DAV being 0, the device reads the data from DIO and puts the two status bits onto ST.</p>
  9322. <h4 id="6:-Device-has-accepted-the-data,-status-is-valid">6: Device has accepted the data, status is valid</h4>
  9323. <p><img src="docs/cbmbus/tcbm-07.png" height="261" width="601" alt="" /><br />
  9324. It then pulls ACK, signaling that it has accepted the data and that the status is valid.</p>
  9325. <h4 id="7:-Controller-clears-data-on-the-bus">7: Controller clears data on the bus</h4>
  9326. <p><img src="docs/cbmbus/tcbm-08.png" height="261" width="601" alt="" /><br />
  9327. Triggered by ACK being 1, the controller reads the status, and sets the DIO lines back to 0, so they can&rsquo;t be interpreted as a TCBM code in the next cycle.</p>
  9328. <h4 id="8:-Controller-resets-data-valid">8: Controller resets data valid</h4>
  9329. <p><img src="docs/cbmbus/tcbm-09.png" height="261" width="601" alt="" /><br />
  9330. In order to return to the initial state, the sender then pulls DAV.</p>
  9331. <h4 id="9:-Device-resets-status">9: Device resets status</h4>
  9332. <p><img src="docs/cbmbus/tcbm-10.png" height="261" width="601" alt="" /><br />
  9333. Triggered by DAV being 1 (the controller has read the status), the device clears the status. All wires are now in the initial state again. All steps are repeated as long as there is more data to be sent.</p>
  9334. <p><img src="docs/cbmbus/tcbm-send.png" height="275" width="601" alt="" /></p>
  9335. <h3 id="Receiving-Bytes">Receiving Bytes</h3>
  9336. <p>When the controller wants to receive data from the device, ownership of the eight DIO lines is passed to the device and back. The basic idea is that the controller sends a TCBM code of 0x84 to indicate a byte receive, passes DIO to the device, the device returns the byte and a two-bit status code, and DIO is passed back to the controller.</p>
  9337. <p><img src="docs/cbmbus/tcbm-receive.gif" height="577" width="601" alt="" /></p>
  9338. <p>Here it is step by step:</p>
  9339. <h4 id="0:-Initial-State">0: Initial State</h4>
  9340. <p><img src="docs/cbmbus/tcbm-11.png" height="261" width="601" alt="" /><br />
  9341. The initial state between bytes when receiving is the same as when sending: The controller is holding the DAV line to indicate that it it is not ready for the next transmission and the device is holding the ACK line, meaning it is not ready either. The DIO and ST lines are all released.</p>
  9342. <h4 id="1:-Controller-puts-code-0x84-on-the-bus">1: Controller puts code 0x84 on the bus</h4>
  9343. <p><img src="docs/cbmbus/tcbm-12.png" height="261" width="601" alt="" /><br />
  9344. For receiving, the controller puts the TCBM code of 0x84 into DIO, indicating that it is now ready to receive a byte.</p>
  9345. <h4 id="2:-Device-has-accepted-the-code">2: Device has accepted the code</h4>
  9346. <p><img src="docs/cbmbus/tcbm-13.png" height="261" width="601" alt="" /><br />
  9347. As soon as the device is done handling the last transmission, it detects that the most significant bit of DIO is set, reads the TCBM code, and signals that it has received the code by releasing ACK.</p>
  9348. <h4 id="3:-Controller-clears-data-on-the-bus">3: Controller clears data on the bus</h4>
  9349. <p><img src="docs/cbmbus/tcbm-14.png" height="261" width="601" alt="" /><br />
  9350. For the transmission of the data, the DIO lines will be operated by the device, so triggered by ACK being 0 (the device has received the code), the controller releases all DIO lines.</p>
  9351. <h4 id="4:-Controller-signals-DIO-belongs-to-device">4: Controller signals DIO belongs to device</h4>
  9352. <p><img src="docs/cbmbus/tcbm-15.png" height="261" width="601" alt="" /><br />
  9353. To signal that the DIO lines now belong to the device, the controller then releases DAV.</p>
  9354. <h4 id="5:-Device-puts-data-on-the-bus">5: Device puts data on the bus</h4>
  9355. <p><img src="docs/cbmbus/tcbm-16.png" height="261" width="601" alt="" /><br />
  9356. Triggered by DAV being 0, the device puts the data onto DIO.</p>
  9357. <h4 id="6:-Device-puts-status-on-the-bus">6: Device puts status on the bus</h4>
  9358. <p><img src="docs/cbmbus/tcbm-17.png" height="261" width="601" alt="" /><br />
  9359. It also puts the status bits onto ST.</p>
  9360. <h4 id="7:-Data-on-bus-is-now-valid">7: Data on bus is now valid</h4>
  9361. <p><img src="docs/cbmbus/tcbm-18.png" height="261" width="601" alt="" /><br />
  9362. After that, the device pulls ACK, signaling that the data in DIO is valid.</p>
  9363. <h4 id="8:-Controller-has-accepted-the-data">8: Controller has accepted the data</h4>
  9364. <p><img src="docs/cbmbus/tcbm-19.png" height="261" width="601" alt="" /><br />
  9365. Triggered by ACK being 1, the controller reads the data and the status from DIO and pulls DAV, indicating that it has accepted the data.</p>
  9366. <h4 id="9:-Device-clears-data-on-the-bus">9: Device clears data on the bus</h4>
  9367. <p><img src="docs/cbmbus/tcbm-20.png" height="261" width="601" alt="" /><br />
  9368. Triggered by DAV being 1, the device clears the data from DIO, in order to return ownership of these lines to the controller.</p>
  9369. <h4 id="10:-Device-clears-status">10: Device clears status</h4>
  9370. <p><img src="docs/cbmbus/tcbm-21.png" height="261" width="601" alt="" /><br />
  9371. It then resets the status lines, so they are in the initial state again.</p>
  9372. <h4 id="11:-Device-signals-it-is-done-with-DIO">11: Device signals it is done with DIO</h4>
  9373. <p><img src="docs/cbmbus/tcbm-22.png" height="261" width="601" alt="" /><br />
  9374. Finally, it releases ACK to indicate that it is no longer using DIO.</p>
  9375. <h4 id="12:-Controller-signals-it-owns-DIO">12: Controller signals it owns DIO</h4>
  9376. <p><img src="docs/cbmbus/tcbm-23.png" height="261" width="601" alt="" /><br />
  9377. Triggered by ACK being 0, the controller releases DAV, meaning it now owns the DIO lines again.</p>
  9378. <h4 id="13:-Device-returns-to-initial-state">13: Device returns to initial state</h4>
  9379. <p><img src="docs/cbmbus/tcbm-24.png" height="261" width="601" alt="" /><br />
  9380. Triggered by DAV being 0, the device pulls ACK, which is the initial state.</p>
  9381. <h4 id="14:-Controller-returns-to-initial-state">14: Controller returns to initial state</h4>
  9382. <p><img src="docs/cbmbus/tcbm-25.png" height="261" width="601" alt="" /><br />
  9383. Triggered by ACK being 1, the controller pulls DAV. All wires are now in the initial state again. All steps are repeated as long as there is more data to be received.</p>
  9384. <p>Note that the protocol only specifies the triggers: For example, the controller is to read the data from DIO once ACK = 1, so it would be just as legal for the the device to put the status on the bus before the data, or put both the status and the data on the bus and pull ACK at the same time.</p>
  9385. <p><img src="docs/cbmbus/tcbm-receive.png" height="275" width="601" alt="" /></p>
  9386. <h3 id="Sending-Commands">Sending Commands</h3>
  9387. <p><a href="https://www.pagetable.com/">Layer 3</a> of the protocol stack requires the controller to send commands (&ldquo;TALK&rdquo;/&ldquo;LISTEN&rdquo;) to devices. Layer 2 therefore has a mode for the transmission of command byte streams.</p>
  9388. <p>As shown before, for the transmission of each byte, the controller first sends a TCBM code to the device indicating whether a byte is supposed to be sent (0x83) or received (0x84) by the controller.</p>
  9389. <p>Commands are sent just like data bytes, but with a TCBM code of 0x81 or 0x82.</p>
  9390. <p>Whether the TCBM cde is 0x81 or 0x82 depends on the type of layer 3 command: The commands 0x20/0x3F/0x40/0x5F (LISTEN, UNLISTEN, TALK, UNTALK) are sent with the TCBM code 0x81, and the commands 0x60/0xE0/0xF0 (SECOND, CLOSE, OPEN) are sent with the TCBM code 0x82.</p>
  9391. <p>While the other variants of the Commodore Peripheral Bus protocol family strictly separate the layers, this is one case where details from a higher layer leak into a lower layer.</p>
  9392. <table>
  9393. <thead>
  9394. <tr>
  9395. <th> TCBM code </th>
  9396. <th> Description                                       </th>
  9397. </tr>
  9398. </thead>
  9399. <tbody>
  9400. <tr>
  9401. <td> 0x81      </td>
  9402. <td> Controller sends command byte (state change)      </td>
  9403. </tr>
  9404. <tr>
  9405. <td> 0x82      </td>
  9406. <td> Controller sends command byte (secondary address) </td>
  9407. </tr>
  9408. <tr>
  9409. <td> 0x83      </td>
  9410. <td> Controller sends data byte                        </td>
  9411. </tr>
  9412. <tr>
  9413. <td> 0x84      </td>
  9414. <td> Controller receives data byte                     </td>
  9415. </tr>
  9416. </tbody>
  9417. </table>
  9418. <h3 id="Status-Codes">Status Codes</h3>
  9419. <p>With the transmission of every byte in either direction, the device sends a two-bit status code to the controller:</p>
  9420. <table>
  9421. <thead>
  9422. <tr>
  9423. <th> Status </th>
  9424. <th> Description   </th>
  9425. </tr>
  9426. </thead>
  9427. <tbody>
  9428. <tr>
  9429. <td> 00     </td>
  9430. <td> OK            </td>
  9431. </tr>
  9432. <tr>
  9433. <td> 01     </td>
  9434. <td> receive error </td>
  9435. </tr>
  9436. <tr>
  9437. <td> 10     </td>
  9438. <td> send error    </td>
  9439. </tr>
  9440. <tr>
  9441. <td> 11     </td>
  9442. <td> EOI           </td>
  9443. </tr>
  9444. </tbody>
  9445. </table>
  9446. <p>In the general case, the device sends a status of 00, which means that everything is okay.</p>
  9447. <p>A receive error is returned by the device if the controller was trying to receive a byte from the device, but the device did not have any data. This corresponds to the IEEE-488 &ldquo;sender timeout&rdquo; and the Standard Serial &ldquo;empty stream&rdquo; case. It signals a <a href="https://www.pagetable.com/?p=1038">Commodore DOS (layer 4)</a> &ldquo;FILE NOT FOUND&rdquo; condition.</p>
  9448. <p>A send error is returned by the device if the controller was trying to send a byte to the device, but the device decided not to accept it.</p>
  9449. <p>The &ldquo;EOI&rdquo; (&ldquo;End or Identify&rdquo;) status is returned if the byte currently received by the controller is the last byte of the stream.</p>
  9450. <h3 id="Timing">Timing</h3>
  9451. <p>The timing of TCBM is completely flexible, there are no timeouts. Both the controller and the device can stall any step in the protocol as long as they wish<sup id="fnref:9"><a href="#fn:9" rel="footnote">9</a></sup>.</p>
  9452. <h3 id="Discussion">Discussion</h3>
  9453. <p>A lot can be criticized about the TCBM bus:</p>
  9454. <h4 id="Paddle">Paddle</h4>
  9455. <p>The paddles are of course awkward and ugly<sup id="fnref:10"><a href="#fn:10" rel="footnote">10</a></sup>, and in the case of two paddles connected at the same time, even to a comical extent.</p>
  9456. <p>If there was no way around the requirement of not shipping the computer with the necessary I/O chip, there could instead have been a cartridge with the chip – like the <a href="https://www.pagetable.com/?p=1312">IEEE-488 cartridge for the C64</a> – supporting any number of daisy-chained devices with cheap DB-25 connectors.</p>
  9457. <h4 id="Protocol-Stack-Issues">Protocol Stack Issues</h4>
  9458. <p>There are two details that divert from the clean original design of the IEEE-488 stack:</p>
  9459. <p>There is no strict separation of layers 2 and 3. The TCBM codes 0x81 and 0x82 on layer 2 depend on the type of command on layer 3. Layer 2 should not have any knowledge of layer 3: It should be possible to add layer 3 commands or even replace all of layer 3 completely, with all layer 2 variants still compatible. This would be true for IEEE-488 and Standard Serial, but not for TCBM.</p>
  9460. <p>Also, with TCBM the communication features are not symmetric any more: While a device can signal the EOI condition by setting the status wires to &ldquo;11&rdquo;, there is no way for the controller to signal EOI to the device. For Commodore DOS disk drives, this is not necessary: EOI from the device indicates that the end of a file has been reached, which is necessary and actionable for the controller, but EOI from the controller is not necessary: If the end of a stream sent for writing has been reached, there is nothing actionable for the drive yet. The controller, who knows about the EOI condition, can then send a layer 3 UNTALK or CLOSE command.</p>
  9461. <h4 id="Speed">Speed</h4>
  9462. <p>There are several issues with the protocol, which lead to a much lower speed than what would be expected from a parallel connection:</p>
  9463. <h5 id="Unnecessary-Steps">Unnecessary Steps</h5>
  9464. <p>When the controller receives a byte, releasing DAV in step 4 (&ldquo;Controller signals DIO belongs to device&rdquo;) is not necessary. The device can already detect that the TCBM code has been removed from DIO in step 3 (MSB is 0) and use it as a trigger for step 5. In a modified protocol with step 4 removed, DAV would be inverted from this point on. So in step 12, the controller would pull DAV instead of releasing it, which is the initial state, so the additional handshake in step 14 would be unnecessary.</p>
  9465. <h5 id="TCBM-Codes">TCBM Codes</h5>
  9466. <p>The design of the TCBM code being sent with every byte almost halves the trasmission speed because of its two handshakes.</p>
  9467. <p>The complex protocol to hand the DIO wires to the device and pass it back with its extra handshakes will also make receiving slower than sending. But receiving is the common case: Much more data is ever read from disk than written to it.</p>
  9468. <p>Most data transmissions between a computer and a disk drive are the LOAD and SAVE operations, where a full file is transfered in one go. Optimized stream transfer protocols could have been added for these cases – the Fast Serial protocol of the C128 (&ldquo;Burst&rdquo;) as well as JiffyDOS do this – doubling the SAVE speed and tripling the LOAD speed.</p>
  9469. <p>A more generic protocol optimization could replace the TCBM code before every byte with an extra wire owned by the controller indicating whether the next byte will be transmitted in the same mode. The common case of transfering multiple bytes would also be sped up by at least a factor of two this way, and even more in the receive case, which would no longer require the DIO handover between bytes.</p>
  9470. <p>This would not even require adding an extra line: It is enough to have one status line going from the device to the computer. &ldquo;0&rdquo; would mean OK, and in the rare case of &ldquo;1&rdquo;, the controller would transfer DIO to the device, so it can return an 8 bit status code.</p>
  9471. <h5 id="Other-Solutions">Other Solutions</h5>
  9472. <p>It is puzzling why they created a completely custom layer 2 protocol that ended up having so many issues, because they could have started with one of the existing layer 2 protocols and easily adapted it:</p>
  9473. <p>They could have used the exact IEEE-488 protocol on layer 2, even in point-to-point mode with just a single device. IEEE-488 only requires one more data line (13 instead of 12), but if that had been an issue, they could have removed EOI and used a timing sidechannel instead, as done by Standard Serial.</p>
  9474. <p>Another option would have been to just reuse the Standard Serial protocol and add another 8 parallel wires for the transfer of the actual data, for a total of 11 wires (one fewer than TCBM). That&rsquo;s what third party hardware extensions for the C64/1541 like Speed DOS and Professional DOS did.</p>
  9475. <p>Yet another option would have been to finally implement the original hardware-based serial protocol that was intended for the VIC-20, but scrapped because of hardware bugs. 8 data lines and one handshaking line can be replaced by one data line and a handshaking line, with a dedicated hardware shift register pushing a bit every few cycles. With hardware that has a working shift register, there is nothing wrong with such a protocol.</p>
  9476. <p>A shift register based serial protocol is exactly what replaced TCBM in the C128 as the &ldquo;Fast Serial&rdquo; protocol.</p>
  9477. <h4 id="Conclusion">Conclusion</h4>
  9478. <p>While TCBM is faster than Standard Serial, it is a huge compromise and certainly not a clean design. And with more than half the bandwidth eaten up by the TCBM codes, it is still slower than IEEE-488.</p>
  9479. <h3 id="Next-Up">Next Up</h3>
  9480. <p>Part 6 of the series of articles on the Commodore Peripheral Bus family will cover the JiffyDOS protocol on layer 2, which shipped as a third party ROM patch for computers and drives, replacing the byte transmission protocol of Standard Serial by using the clock and data lines in a more efficient way.</p>
  9481. <blockquote>
  9482. <p>This article series is an Open Source project. Corrections, clarifications and additions are <strong>highly</strong> appreciated. I will regularly update the articles from the repository at <a href="https://github.com/mist64/cbmbus_doc">https://github.com/mist64/cbmbus_doc</a>.</p>
  9483. </blockquote>
  9484. <h1 id="References">References</h1>
  9485. <ul>
  9486. <li><a href="http://www.zimmers.net/anonftp/pub/cbm/src/plus4/plus4_rom_disassembly.txt">The Complete ROM dissasembly</a> by Mike Dailly</li>
  9487. <li><a href="http://www.cbmhardware.de/show.php?r=7&amp;id=21">The Complete Commodore 1551 ROM disassembly</a> by Attila Grósz</li>
  9488. <li><a href="https://www.github.com/mist64/cbmsrc">Original source code of various Commodore computers and peripherals</a></li>
  9489. <li><a href="http://www.zimmers.net/anonftp/pub/cbm/schematics/drives/new/1551/index.html">Commodore 1551 schematics</a></li>
  9490. <li><a href="http://www.zimmers.net/anonftp/pub/cbm/documents/projects/drives/1551-tia.gif">Floppy 1551 reparieren – so geht&rsquo;s</a></li>
  9491. <li><a href="http://www.softwolves.com/arkiv/cbm-hackers/15/15949.html">The strange design of the 1551 floppy drive</a></li>
  9492. <li><a href="https://www.forum64.de/index.php?thread/58413-tcbm-bus-analyse/">TCBM-Bus Analyse</a></li>
  9493. <li><a href="http://www.cbmhardware.de/show.php?r=10&amp;id=15">1551USB</a></li>
  9494. <li><a href="https://hup.hu/node/121506">Retró rovat IV/B: Az 1551-II projekt folytatása</a></li>
  9495. </ul>
  9496. <div class="footnotes">
  9497. <hr/>
  9498. <ol>
  9499. <li id="fn:1">
  9500. <p>The VIC-20 was named after the VIC (&ldquo;Video Interface Controller&rdquo;), the video chip of the system.<a href="#fnref:1" rev="footnote">&#8617;</a></p>
  9501. </li>
  9502. <li id="fn:2">
  9503. <p>These computers are also often referenced as the &ldquo;264 series&rdquo;, since three machines with the names C232, C264 and C364 were originally planned.<a href="#fnref:2" rev="footnote">&#8617;</a></p>
  9504. </li>
  9505. <li id="fn:3">
  9506. <p>A hardware defect in the VIC-20 required a significant slowdown of the bus timing. More information in the <a href="https://www.pagetable.com/?p=1135">article about the serial bus</a>.<a href="#fnref:3" rev="footnote">&#8617;</a></p>
  9507. </li>
  9508. <li id="fn:4">
  9509. <p>The 1551 schematics call these pins PA0-PA7, after port A of the MOS 6523 I/O controller it is connected to.<a href="#fnref:4" rev="footnote">&#8617;</a></p>
  9510. </li>
  9511. <li id="fn:5">
  9512. <p>The C116 was supposed to compete with the Sinclair ZX81 and had a target price of $49. If ended up being sold only in Europe, for 100 DM or 99 GBP (which was about 75 USD).<a href="#fnref:5" rev="footnote">&#8617;</a></p>
  9513. </li>
  9514. <li id="fn:6">
  9515. <p>Support for two paddles was added very late in the design process. Early <a href="http://www.zimmers.net/anonftp/pub/cbm/firmware/computers/plus4/264/index.html">TED</a> <a href="http://www.zimmers.net/anonftp/pub/cbm/firmware/computers/plus4/364/index.html">prototype</a> <a href="http://www.zimmers.net/anonftp/pub/cbm/firmware/computers/plus4/PI9/index.html">ROMs</a> only support a single TCBM bus. When support for multiple busses was added, the byte send and receive code had to overflow into the patch area, which can be seen in the release ROM versions. The late addition of DEV can also be seen in the pinout of the cable: Without it, there would be two GND wires at each end.<a href="#fnref:6" rev="footnote">&#8617;</a></p>
  9516. </li>
  9517. <li id="fn:7">
  9518. <p>Here is a party trick: On a system with IEEE-488 or Standard Serial, the bus is blocked for all communication while one drive is busy with a long-running task, like formatting a disk. On a TED with one 1541 connected to Serial, and two 1551 drives connected to two separate TCBM busses, it is possible for all three drives to format disks at the same time.<a href="#fnref:7" rev="footnote">&#8617;</a></p>
  9519. </li>
  9520. <li id="fn:8">
  9521. <p>This is done by first testing for the presence of the I/O controller by testing whether the DIO port can retain a test value, and then by checking for STATUS0 being pulled to 0, which a device does when present and idle. (Label <code>tstkdy</code> in the source.)<a href="#fnref:8" rev="footnote">&#8617;</a></p>
  9522. </li>
  9523. <li id="fn:9">
  9524. <p>This is in contrast to Standard Serial, where the tight timing requirements of the original specification (for the VIC-20) could not be met by the C64, so the specification had to be changed.<a href="#fnref:9" rev="footnote">&#8617;</a></p>
  9525. </li>
  9526. <li id="fn:10">
  9527. <p>In addition to the I/O chip, the paddles contain a PLA for address decoding, since unlike the C64&rsquo;s PLA, the TED&rsquo;s PLA does not generate chip select signals for external devices.<a href="#fnref:10" rev="footnote">&#8617;</a></p>
  9528. </li>
  9529. </ol>
  9530. </div>
  9531. ]]></content:encoded>
  9532. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1324</wfw:commentRss>
  9533. <slash:comments>3</slash:comments>
  9534. </item>
  9535. <item>
  9536. <title>Run CP/M on your C64 &#8211; using Emulation!</title>
  9537. <link>https://www.pagetable.com/?p=1315</link>
  9538. <comments>https://www.pagetable.com/?p=1315#comments</comments>
  9539. <pubDate>Sun, 19 May 2019 09:24:29 +0000</pubDate>
  9540. <dc:creator><![CDATA[Michael Steil]]></dc:creator>
  9541. <category><![CDATA[Archeology]]></category>
  9542. <category><![CDATA[C64]]></category>
  9543. <category><![CDATA[Commodore]]></category>
  9544. <category><![CDATA[Operating Systems]]></category>
  9545.  
  9546. <guid isPermaLink="false">https://www.pagetable.com/?p=1315</guid>
  9547. <description><![CDATA[In this episode of computer archeology, we deconstruct a very interesting case of borrowing code from multiple places &#8211; but first try this D64 disk image with any Commodore 64 emulator or a real C64: Commodore 64 CP/M Emulator The CP/M operating system was the first home computer OS that ran on machines from many ... <a title="Run CP/M on your C64 &#8211; using Emulation!" class="read-more" href="https://www.pagetable.com/?p=1315">Read more<span class="screen-reader-text">Run CP/M on your C64 &#8211; using Emulation!</span></a>]]></description>
  9548. <content:encoded><![CDATA[<p>In this episode of computer archeology, we deconstruct a very interesting case of borrowing code from multiple places &#8211; but first try this D64 disk image with any Commodore 64 emulator or a real C64:</p>
  9549. <p><a href="docs/cpm_emu/cpm_emu.d64"><img src="docs/d64.png" height="64" width="64" alt="" /><br />
  9550. Commodore 64 CP/M Emulator</a></p>
  9551. <p>The CP/M operating system was the first home computer OS that ran on machines from many different vendors, as long as they had an Intel 8080 compatible CPU &#8211; which the 8 bit machines from Commodore, Apple, Atari and BBC didn&rsquo;t: They used the MOS 6502 CPU.</p>
  9552. <h3 id="The-Commodore-64-CPM-Cartridge">The Commodore 64 CPM Cartridge</h3>
  9553. <p>In 1983, Commodore released the the &ldquo;<a href="https://www.pagetable.com/?p=1312">CP/M Cartridge</a>&rdquo; for the Commodore 64, which contained a Z80 CPU and came with CP/M 2.2: The OS and its apps would run on the Z80, which would switch over to the computer&rsquo;s 6502 to call code in the original ROM for screen, keyboard and disk accesses. It wasn&rsquo;t very good, didn&rsquo;t sell well, and was quickly discontinued.</p>
  9554. <h3 id="Roßmöller's-CP/M-Emulator">Roßmöller&rsquo;s CP/M Emulator</h3>
  9555. <p>But it was also possible to run CP/M on a C64 without a hardware extension: All that is necessary is to emulate the Intel 8080 instruction set on the MOS 6502. And the German company Roßmöller did exactly that. In 1988, a little while after releasing their 4 MHz speeder cartridge &ldquo;<a href="http://www.retroport.de/Hardware_T-U.html">Turbo Process</a>&rdquo; for the C64, they started selling</p>
  9556. <blockquote>
  9557. <blockquote>
  9558. <p>CP/M Emulator &#8211; CP/M 2.2-kompatibler C64 ohne zusätzl. Hardware</p>
  9559. </blockquote>
  9560. </blockquote>
  9561. <p>for 10 DM.</p>
  9562. <p>The product is directly based on Commodore&rsquo;s adaption of CP/M for the C64 CP/M cartridge. Commodore&rsquo;s disk contains a small bootloader that loads BIOS and the Z80 bootstrap from the first sectors on disk (as specified by CP/M) &#8211; the bootloader of the CP/M Emulator directly contains slightly patched versions of BIOS and the Z80 bootstrap, as well as the 8080 emulator:</p>
  9563. <table border="1">
  9564. <tbody>
  9565. <tr>
  9566. <th>CP/M Cartridge</th>
  9567. <th>CP/M Emulator</th>
  9568. </tr>
  9569. <tr>
  9570. <td>
  9571. <pre>0 "CP/M DISK       " 65 2A  
  9572. 1     "CPM             "  PRG  
  9573. 0 BLOCKS FREE.  
  9574. </pre>
  9575. </td>
  9576. <td>
  9577. <pre>0 "CP/M EMULATOR   " 32 0B  
  9578. 12    "CP/M            "  PRG  
  9579. 0 BLOCKS FREE.  
  9580. </pre>
  9581. </td>
  9582. </tr>
  9583. </tbody>
  9584. </table>
  9585. <p>The first sectors on disk still contain the original versions of BIOS and the Z80 bootstrap &#8211; and curiously, all references on disk to &ldquo;Digital Research&rdquo;, the CP/M license holder, have been replaced with &ldquo;Roßmöller&rdquo;, hinting towards a very questionable licensing status &#8211; not inconsistent with Roßmöller&rsquo;s reputation at the time.</p>
  9586. <table border="1">
  9587. <tbody>
  9588. <tr>
  9589. <th>CP/M Cartridge</th>
  9590. <th>CP/M Emulator</th>
  9591. </tr>
  9592. <tr>
  9593. <td>
  9594. <pre>
  9595.    COMMODORE 64   44k CP/M vers 2.2    
  9596.  
  9597.  Copyright (C) 1979, Digital Research  
  9598.     Copyright (C) 1982, Commodore      
  9599.  
  9600. A>  
  9601. </pre>
  9602. </td>
  9603. <td>
  9604. <pre>     R 03 07                            
  9605.  
  9606.    COMMODORE 64  44k CP/M vers 2.2    
  9607.  
  9608.    ===============================    
  9609.  
  9610.    Copyright (C) 1988, ROSSMOELLER    
  9611.  
  9612. A>  
  9613. </pre>
  9614. </td>
  9615. </tr>
  9616. </tbody>
  9617. </table>
  9618. <h3 id="Emulator-Performance">Emulator Performance</h3>
  9619. <p>The Roßmöller 8080 emulator contained in the boot program is less than 1.4 KB in size &#8211; and it&rsquo;s super slow: Executing a single 8080 instruction takes between 300 and 800 cycles (485 on average). This makes the CP/M emulator about 100 times slower than a similarly spec&#8217;ed 8080-based machine. Even with the 8 MHz speeder, the emulator was still 12 times slower. It was in fact so slow that it failed to register normal keypresses on stock C64 systems unless you held the keys for a second.</p>
  9620. <h3 id="8080-Simulator-for-the-6502">8080 Simulator for the 6502</h3>
  9621. <p>There is a simple reason the emulator is this slow: It wasn&rsquo;t written for speed. It wasn&rsquo;t even written as an emulator. As it turns out, the Roßmöller 8080 emulator is a binary-patched version of the <a href="https://www.pagetable.com/?p=824"><em>8080 Simulator for the 6502, KIM-1 Version</em></a> &ldquo;<em>8080 simulator-debug package</em>&rdquo; by Dann McCreary, written in 1978.</p>
  9622. <p><em>8080 Simulator for the 6502</em> is an interactive 8080 simulator primarily aimed at teaching 8080 assembly &#8211; and optimized for size, since it had to to fit into the 2 KB of RAM of the KIM-1.</p>
  9623. <p>In the following implementation of the memory access instructions STAX/LDAX, you can see that the Roßmöller version just relocated everything by $CA pages:</p>
  9624. <table border="1">
  9625. <tbody>
  9626. <tr>
  9627. <th>8080 Simulator for the 6502</th>
  9628. <th>Roßmöller CP/M Emulator</th>
  9629. </tr>
  9630. <tr>
  9631. <td>
  9632. <pre>0249 DIRECT  PHA         ; TEMP SAVE  
  9633. 024A         JSR PC/PNT  ; PC TO POINTER  
  9634. 024D         JSR DBLINC  
  9635. 0250         LDXIM (SCR)  
  9636. 0252         JSR MEM/RP  
  9637. 0255         LDXIM (PNT)  
  9638. 0257         JSR SRC/RP  
  9639. 025A         PLA         ; RECOVER TEMP  
  9640. 025B         LSRA        ; LOAD?  
  9641. 025C         BCS STORE   ; NO, STORE  
  9642. 025E         ASLA        ; MAKE INDEX  
  9643. 025F         TAX  
  9644. 0260         BEQ LDAD    ; LOAD A DIRECT?  
  9645. 0262         JMP MEM/RP  ; NO, LOAD HL DIRECT  
  9646. </pre>
  9647. </td>
  9648. <td>
  9649. <pre>.,CC49 48       PHA  
  9650. .,CC4A 20 5D CD JSR $CD5D  
  9651. .,CC4D 20 80 CD JSR $CD80  
  9652. .,CC50 A2 19    LDX #$19  
  9653. .,CC52 20 A0 CD JSR $CDA0  
  9654. .,CC55 A2 17    LDX #$17  
  9655. .,CC57 20 70 CD JSR $CD70  
  9656. .,CC5A 68       PLA  
  9657. .,CC5B 4A       LSR A  
  9658. .,CC5C B0 0E    BCS $CC6C  
  9659. .,CC5E 0A       ASL A  
  9660. .,CC5F AA       TAX  
  9661. .,CC60 F0 03    BEQ $CC65  
  9662. .,CC62 4C A0 CD JMP $CDA0  
  9663. </pre>
  9664. </td>
  9665. </tr>
  9666. </tbody>
  9667. </table>
  9668. <h3 id="Roßmöller-Changes">Roßmöller Changes</h3>
  9669. <p>But there are also sections in the emulator that had to be adapted for use in the CP/M context:</p>
  9670. <ul>
  9671. <li>The memory model of the C64 CP/M cartridge made the 8080 CPU see the machine&rsquo;s RAM moved up by $1000 &#8211; so the 8080 RST area and the 6502 zero page would not be at the same spot. The emulator also uses this layout, so every memory access has to go through an expensive 16 bit addidion. (Avoiding this addition would have been the better strategy &#8211; and ironically, Dann&rsquo;s original design document explains why he decided <em>not</em> to shift memory!)</li>
  9672. <li>The interpreter was extended to special case &ldquo;LD ($CE00),A&rdquo; (which switches back to the 6502) and to support the &ldquo;LDIR&rdquo; instruction (a Z80 extension used by CP/M), but it seems the people patching the emulator didn&rsquo;t understand the code well enough to work &ldquo;LDIR&rdquo; into the instruction decoder or put the address check into the &ldquo;LD (), A&rdquo; handler &#8211; instead, the interpreter loop checks for these instruction before the normal decode. (Ironically, the emulator already contains a special opcode to switch to 6502 mode, so it would have been easier to patch the code that switches modes.)</li>
  9673. <li>
  9674. <p>The following code increments the 8080 program counter in 45 cycles, while it could also have been done is as little as 6 cycles:</p>
  9675. <pre><code>    ldx #pc - registers  
  9676.    ldy #const_one - registers  
  9677.    clc  
  9678. :   lda registers,x  
  9679.    adc registers,y  
  9680.    sta registers,x  
  9681.    iny  
  9682.    inx  
  9683.    tya  
  9684.    and #1  
  9685.    bne :- ; do it twice
  9686. </code></pre>
  9687. </li>
  9688. </ul>
  9689. <p>Roßmöller did not make these modifications in source, but binary patched the original KIM-1 version: Changing the exact page alignment of the emulator would have required an extensive rework of the extremely optimized instruction decoder.</p>
  9690. <p>But it doesn&rsquo;t mean the patches are done in an elegant way: They are very wasteful, weird&hellip; and just horrible. The following example stores a 16 bit register in memory at the address pointed to by PNT:</p>
  9691. <table border="1">
  9692. <tbody>
  9693. <tr>
  9694. <th>8080 Simulator for the 6502</th>
  9695. <th>Roßmöller CP/M Emulator</th>
  9696. </tr>
  9697. <tr>
  9698. <td valign="top">
  9699. <pre>017D RP/MEM  LDYIM #$01    ; CLEAR INDEX  
  9700. 017F RPLP    LDAZX REGS+1  ; GET NEXT RP DATA  
  9701. 0181         STAIY PNT     ; STORE IN MEMORY  
  9702. 0183         DEX  
  9703. 0184         DEY  
  9704. 0185         BEQ RPLP  
  9705. 0187         RTS  
  9706. </pre>
  9707. </td>
  9708. <td>
  9709. <pre>.,CB7D A0 01    LDY #$01  
  9710. <b>.,CB7F 20 44 CE JSR $CE44  
  9711. .,CB82 EA       NOP</b>  
  9712. .,CB83 CA       DEX  
  9713. .,CB84 88       DEY  
  9714. .,CB85 F0 F8    BEQ $CB7F  
  9715. .,CB87 60       RTS
  9716.  
  9717. <b>.,CE44 20 C0 CE JSR $CEC0  
  9718. .,CE47 B5 36    LDA $36,X  
  9719. .,CE49 91 C1    STA ($C1),Y  
  9720. .,CE4B 60       RTS
  9721.  
  9722. .,CEC0 08       PHP  
  9723. .,CEC1 20 20 CE JSR $CE20  
  9724. .,CEC4 28       PLP  
  9725. .,CEC5 60       RTS
  9726.  
  9727. .,CE20 A5 4C    LDA $4C  
  9728. .,CE22 85 C1    STA $C1  
  9729. .,CE24 A5 4D    LDA $4D  
  9730. .,CE26 18       CLC  
  9731. .,CE27 69 10    ADC #$10  
  9732. .,CE29 85 C2    STA $C2  
  9733. .,CE2B 60       RTS</b>  
  9734. </pre>
  9735. </td>
  9736. </tr>
  9737. </tbody>
  9738. </table>
  9739. <p>Since for CP/M, memory is shifted by $1000, the Roßmöller version has to copy PNT into a temporary zero page location, add $1000 and use this one instead of PNT. The patch at $CE44 does the extra work. It calls the common routine $CEC0, which is supposed to make the copy + $1000 &#8211; and this function itself is a wrapper around a library function that saves the status bits for no reason. A more optimized version would have replaced all callers to $CB7D by code that does all this in one go. There was no reason to reuse logic; there are kilobytes of unused space!</p>
  9740. <h3 id="Was-the-Emulator-Licensed?">Was the Emulator Licensed?</h3>
  9741. <p>Dann McCreary confirmed that Roßmöller never licensed the emulator from him:</p>
  9742. <blockquote>
  9743. <blockquote>
  9744. <p>I don&rsquo;t have all the old records, but it seems likely that they bought a KIM-1 version directly from me at some point, or got it from one of my customers.</p>
  9745. </blockquote>
  9746. </blockquote>
  9747. <p>After I commented that he was unlikely to sell any more copies of the KIM-1 version around the time the Roßmöller emulator was written, Dann wrote:</p>
  9748. <blockquote>
  9749. <blockquote>
  9750. <p>I made random sales all around the globe&hellip; but you&rsquo;re probably right, 1987 sounds pretty late&hellip; Perhaps one of the Rossmuller principals had bought from me years earlier?</p>
  9751. </blockquote>
  9752. </blockquote>
  9753. <p>Given that all Digital Research copyright messages were also completely removed from the product, it seems unlikely that the CP/M part was licensed.</p>
  9754. <h3 id="Conclusion">Conclusion</h3>
  9755. <p>Dann McCreary:</p>
  9756. <blockquote>
  9757. <blockquote>
  9758. <p>Back in the day, I was asked repeatedly whether the simulator was suitable for running CP/M, and my response invariably was that (in my opinion) it would run unacceptably slow. That is primarily why I never attempted the task myself. Of course, my code was completely optimized for space and not for speed&hellip; Those were the days of a 1.2K RAM space (KIM-1), or if you were lucky, 16K (Apple ][).</p>
  9759. </blockquote>
  9760. </blockquote>
  9761. ]]></content:encoded>
  9762. <wfw:commentRss>https://www.pagetable.com/?feed=rss2&#038;p=1315</wfw:commentRss>
  9763. <slash:comments>1</slash:comments>
  9764. </item>
  9765. </channel>
  9766. </rss>
  9767.  

If you would like to create a banner that links to this page (i.e. this validation result), do the following:

  1. Download the "valid RSS" banner.

  2. Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)

  3. Add this HTML to your page (change the image src attribute if necessary):

If you would like to create a text link instead, here is the URL you can use:

http://www.feedvalidator.org/check.cgi?url=http%3A//www.pagetable.com/%3Ffeed%3Drss2

Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda