<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on theRookieCoder</title><link>https://therookiecoder.is-a.dev/posts/</link><description>Recent content in Posts on theRookieCoder</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sun, 23 Mar 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://therookiecoder.is-a.dev/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Creating this Website</title><link>https://therookiecoder.is-a.dev/posts/website/</link><pubDate>Sun, 23 Mar 2025 00:00:00 +0000</pubDate><guid>https://therookiecoder.is-a.dev/posts/website/</guid><description>&lt;p>As a developer and person on the internet, I think a website is an important part of your image &amp;mdash; it is a blank canvas for you to showcase yourself and your thoughts. I had been looking to make a personal website for a while, but the inspiration never struck. But I finally got around to it last month, and I want to talk about how I made it.&lt;/p>
&lt;h1 id="philosophy">Philosophy&lt;/h1>
&lt;p>Websites are an integral part of the internet, and as the internet grew, websites did too. This has led to an explosion in the scale of websites. Browsers nowadays are like mini operating systems, and the technologies available to create websites are numerous and complex.&lt;/p></description><content>&lt;p>As a developer and person on the internet, I think a website is an important part of your image &amp;mdash; it is a blank canvas for you to showcase yourself and your thoughts. I had been looking to make a personal website for a while, but the inspiration never struck. But I finally got around to it last month, and I want to talk about how I made it.&lt;/p>
&lt;h1 id="philosophy">Philosophy&lt;/h1>
&lt;p>Websites are an integral part of the internet, and as the internet grew, websites did too. This has led to an explosion in the scale of websites. Browsers nowadays are like mini operating systems, and the technologies available to create websites are numerous and complex.&lt;/p>
&lt;p>But when creating my website, I wanted to something simple to share my thoughts. I had to take a step back, and realise that it doesn&amp;rsquo;t take much to make a good looking website; some simple CSS can go a long way. I came across &lt;a href="https://motherfuckingwebsite.com" target="_blank" rel="noopener" >https://motherfuckingwebsite.com&lt;/a>, and the following lines really resounded with me.&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;&amp;hellip; all the problems we have with websites are ones we create ourselves. Websites aren&amp;rsquo;t broken by default, they are functional, high-performing, and accessible. You break them.&amp;rdquo;&lt;/p>&lt;/blockquote>
&lt;blockquote>
&lt;p>&amp;ldquo;Good design is as little design as possible&amp;rdquo;&lt;/p>
&lt;p>― &lt;a href="https://www.vitsoe.com/rw/about/good-design#id-good-design-is-as-little-design-as-possible" target="_blank" rel="noopener" >Dieter Rams&lt;/a>&lt;/p>&lt;/blockquote>
&lt;h1 id="technology">Technology&lt;/h1>
&lt;p>Choosing a minimalist philosophy, I decided to create a simple static website. No UI frameworks, no JavaScript, no server-side rendering, just plain old HTML and CSS. But creating a website from scratch is quite painful, so I searched for static site generators that fit my use-case.&lt;/p>
&lt;p>One of my requirements was using Markdown &amp;mdash; it really is one of my favourite aspects of computers. I love its simplicity and versatility, and I think it&amp;rsquo;s the perfect tool for writing content on the web.&lt;/p>
&lt;p>I discovered &lt;a href="https://gohugo.io" target="_blank" rel="noopener" >Hugo&lt;/a>, its idea is simple; you select a theme, write content in Markdown, and with one command Hugo will build your website in milliseconds. It even supports hot reloading! I explored the recommended themes, and found the &lt;a href="https://themes.gohugo.io/themes/hugo-theme-terminal" target="_blank" rel="noopener" >Terminal&lt;/a> theme to be good looking, and appropriate for me too! But of course, I had to customise it to my heart&amp;rsquo;s content.&lt;/p>
&lt;h1 id="theming">Theming&lt;/h1>
&lt;p>The most important part of theming is colours. I like using the &lt;a href="https://catppuccin.com" target="_blank" rel="noopener" >Catppuccin&lt;/a> Macchiato theme, so I decided to use it here. Fonts are also really important, most of what you see here is text after all. I decided to use 3 of my favourite fonts &amp;mdash;
&lt;span style='font-family: "Space Grotesk" !important;'>&lt;a href="https://fonts.floriankarsten.com/space-grotesk" target="_blank" rel="noopener" >Space Grotesk&lt;/a> in the header, footer, and headings;&lt;/span>
&lt;span style='font-family: "JetBrains Mono" !important; font-size: 0.9em;'>&lt;a href="https://www.jetbrains.com/lp/mono" target="_blank" rel="noopener" >JetBrains Mono&lt;/a> for code; and&lt;/span>
&lt;span style='font-family: "Atkinson Hyperlegible Next" !important;'>&lt;a href="https://www.brailleinstitute.org/freefont" target="_blank" rel="noopener" >Atkinson Hyperlegible&lt;/a> for other elements and body text.&lt;/span>&lt;/p>
&lt;h1 id="customisation">Customisation&lt;/h1>
&lt;p>At first, I used the Terminal theme directly. But I wanted to make some modifications to it, so I copied over the styles and layouts to my project. I removed components of the theme that I didn&amp;rsquo;t use &amp;mdash; such as the comments, the language selector, Twitter embeds, custom syntax highlighting (Hugo&amp;rsquo;s built-in version worked well for me), and more.&lt;/p>
&lt;p>I ended up making a ton of changes, so I only listed some notable ones here. You can see all the changes on the repository&amp;rsquo;s &lt;a href="https://github.com/theRookieCoder/theRookieCoder.github.io/commits" target="_blank" rel="noopener" >commit history&lt;/a>.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Open all external links in a new tab. I personally hate it when I&amp;rsquo;m reading something on a website and the link replaces the tab I was reading on.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Round the logo in the header, and pad it better.
&lt;img src="header-logo.avif" alt="&amp;ldquo;theRookieCoder&amp;rdquo; in a filled magenta rectangle, with slight rounding in the left corners">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Don&amp;rsquo;t link to the current page in the header and menu, instead italicise the text.
&lt;img src="header-current-page.avif" alt="The website&amp;rsquo;s menu in the setup page, with links to &amp;ldquo;Home&amp;rdquo; and &amp;ldquo;About Me&amp;rdquo;, but no link to &amp;ldquo;My Setup&amp;rdquo;">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Improve OpenGraph metadata tags, and dynamically generate &lt;code>og:image&lt;/code>s &amp;mdash; the link previews that shows up on social media &amp;mdash; for every post.
&lt;img src="og-image.png" alt="The OpenGraph image for this post, with the title &amp;ldquo;Creating this Website&amp;rdquo; and date &amp;ldquo;2025-03-23&amp;rdquo;">&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Make the header sticky, i.e. it stays at the top of the screen even after you scroll.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>To account for the sticky header, I added the &lt;code>scroll-margin&lt;/code> property to headings so that when opening anchor links (links that automatically scroll to a heading) the browser won&amp;rsquo;t hide the heading underneath the header. I&amp;rsquo;ve seen a lot of website that have this issue.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Combine all CSS styles into a single file. I noticed on &lt;a href="https://pagespeed.web.dev" target="_blank" rel="noopener" >PageSpeed Insights&lt;/a> that a significant portion of network time was spent on fetching the numerous stylesheets.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Make the table of contents collapsible.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h1 id="conclusion">Conclusion&lt;/h1>
&lt;p>Well after more than 50 commits tweaking various aspects of the website, am I happy? I am! One small nitpick I have is that there is a &lt;a href="https://github.com/theRookieCoder/theRookieCoder.github.io/blob/main/assets/js/menu.js" target="_blank" rel="noopener" >tiny amount of JavaScript&lt;/a> for controlling the menu, but to my knowledge it is not possible to make a clickable hover dropdown menu in pure HTML and CSS. I&amp;rsquo;m still really satisfied with how my website turned out, and I love that I finally have a platform to share my thoughts on. I hope you have fun reading my content here!&lt;/p></content></item><item><title>Writing a CHIP-8 emulator</title><link>https://therookiecoder.is-a.dev/posts/chip8/</link><pubDate>Sat, 18 Jan 2025 00:00:00 +0000</pubDate><guid>https://therookiecoder.is-a.dev/posts/chip8/</guid><description>&lt;img src="meme.avif" alt="CHIP-8 Meme" class="left" />
&lt;p>I first heard of CHIP-8 when I was beginning to learn Rust. A Redditor suggested it as a reasonably-sized but simple project to get started with a new programming language. I tried to write it in Rust, but was unable to even get the obligatory &lt;em>IBM logo&lt;/em> program to run; likely due to a lack of Rust knowledge, inexperience with programming, or both. Anyways, I bookmarked the tutorial with the intent of trying it out some other day, and moved on to other projects.&lt;/p></description><content>
&lt;img src="meme.avif" alt="CHIP-8 Meme" class="left" />
&lt;p>I first heard of CHIP-8 when I was beginning to learn Rust. A Redditor suggested it as a reasonably-sized but simple project to get started with a new programming language. I tried to write it in Rust, but was unable to even get the obligatory &lt;em>IBM logo&lt;/em> program to run; likely due to a lack of Rust knowledge, inexperience with programming, or both. Anyways, I bookmarked the tutorial with the intent of trying it out some other day, and moved on to other projects.&lt;/p>
&lt;p>Wellll, 3 years have passed since then, and I have become much better at Rust and programming in general. About a year ago, I started learning C in Harvard&amp;rsquo;s &lt;a href="https://cs50.harvard.edu/x" target="_blank" rel="noopener" >CS50x&lt;/a> course, and I really liked its simplicity and closeness to the bare metal. After completing the course&amp;rsquo;s C assignments, and creating a &lt;a href="https://github.com/theRookieCoder/ardudraw" target="_blank" rel="noopener" >small Arduino project&lt;/a>, I yearned for more C (as one does).&lt;/p>
&lt;h1 id="creating-the-emulator">Creating the Emulator&lt;/h1>
&lt;h2 id="resources-used">Resources Used&lt;/h2>
&lt;p>I followed &lt;a href="https://tobiasvl.github.io/blog/write-a-chip-8-emulator" target="_blank" rel="noopener" >Tobias Langhoff&amp;rsquo;s guide&lt;/a>, which points out implementation details to watch out for, and common pitfalls; it is an excellent resource for implementing features iteratively.&lt;/p>
&lt;p>Later on, I discovered &lt;a href="https://github.com/Timendus/chip8-test-suite" target="_blank" rel="noopener" >Timendus&amp;rsquo; test suite&lt;/a>, which I wish I had known about earlier. It is invaluable for checking the implementation of the more nuanced behaviour of the CHIP-8 and its derivatives, such as carry flag behaviour, and implementation-specific quirks that have emerged over the years of various CHIP-8 emulators being developed for different platforms.&lt;/p>
&lt;p>If you are making your own emulator, I highly recommend joining the &lt;a href="https://discord.gg/rcgsvtk7Xv" target="_blank" rel="noopener" >Emulator Development&lt;/a> Discord server, and making use of the &lt;a href="https://discord.com/channels/465585922579103744/465586212804100106" target="_blank" rel="noopener" >#chip-8&lt;/a> and &lt;a href="https://discord.com/channels/465585922579103744/482208284032499713" target="_blank" rel="noopener" >#resources-systems&lt;/a> channels. There are active and experienced people there who can help you with issues in your emulator. Use the websites linked in the server if you would like to find more resources and documentation about the CHIP-8.&lt;/p>
&lt;p>I do not recommend using a search engine to find resources, because they often surface incorrect/outdated websites. Use the links in the EmuDev server, and ask in the #chip-8 channel if you are really struggling to find out about something.&lt;/p>
&lt;h2 id="initial-implementation">Initial Implementation&lt;/h2>
&lt;p>The first step I took was defining variables for the most crucial components of the system; &lt;abbr title="Random Access Memory">RAM&lt;/abbr>, the display buffer, and the program counter (&lt;code>PC&lt;/code>), index (&lt;code>I&lt;/code>), and variable (&lt;code>V0&lt;/code>-&lt;code>VF&lt;/code>) registers. I dumped these into global scope for the moment; I decided to worry about separating the frontend and core later.&lt;/p>
&lt;p>I copied the font from Tobias&amp;rsquo; guide into a constant, and loaded the font into RAM beginning at address &lt;code>0x050&lt;/code>. Then, I loaded the ROM file specified in the command-line argument into RAM, beginning at address &lt;code>0x200&lt;/code>.&lt;/p>
&lt;p>The execution loop of the CHIP-8 is quite simple.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Fetch 2 bytes from RAM at the address in the program counter, and store it in a 16-bit &lt;code>instruction&lt;/code> variable. Keep in mind that the CHIP-8 is big-endian, i.e. the first byte (the one with the lesser address) is the more significant byte.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Increment the program counter by 2, since we just loaded 2 bytes.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&amp;ldquo;Decode&amp;rdquo; the instruction. CHIP-8 instructions pack the opcode and operands into a single 16-bit number. So, we need to mask out parts of it to determine the operands.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Mask&lt;/th>
&lt;th>Desc­rip­tion&lt;/th>
&lt;th>Usage&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>_x__&lt;/code>&lt;/td>
&lt;td>2nd nibble&lt;/td>
&lt;td>&lt;code>x&lt;/code>th variable register, written as &lt;code>Vx&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>__y_&lt;/code>&lt;/td>
&lt;td>3rd nibble&lt;/td>
&lt;td>&lt;code>y&lt;/code>th variable register, written as &lt;code>Vy&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>___n&lt;/code>&lt;/td>
&lt;td>4th nibble&lt;/td>
&lt;td>4-bit number (nibble)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>__nn&lt;/code>&lt;/td>
&lt;td>3rd &amp;amp; 4th nibbles&lt;/td>
&lt;td>8-bit number (byte)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>_nnn&lt;/code>&lt;/td>
&lt;td>2nd, 3rd, and 4th nibbles&lt;/td>
&lt;td>12-bit number&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The purpose of the last 3 operands depends on the opcode.&lt;br>
I extracted the operands by masking out the required nibble(s) using an &lt;code>AND&lt;/code> operation (&lt;code>&amp;amp;&lt;/code> in C), and bit-shifting them to the units place. I used &lt;code>#define&lt;/code> for these instead of dedicated variables, but remember to add an extra pair of parentheses around the entire expression when using &lt;code>#define&lt;/code>!&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Determine the opcode to execute. The first nibble, and sometimes &lt;code>n&lt;/code> or &lt;code>nn&lt;/code>, is used to specify the opcode. I used nested switch statements for this, which is a terrible idea in hindsight. I kept forgetting to add &lt;code>break&lt;/code>, and I initially used multiple &lt;code>default&lt;/code> blocks to catch illegal instructions. A language with more powerful pattern matching would fare better here.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>The instructions you should start off with are.&lt;/p>
&lt;ul>
&lt;li>&lt;code>00E0&lt;/code>: Clear the display (no operands)&lt;/li>
&lt;li>&lt;code>1nnn&lt;/code>: Jump to address &lt;code>nnn&lt;/code>, i.e. set &lt;code>PC&lt;/code> to &lt;code>nnn&lt;/code>&lt;/li>
&lt;li>&lt;code>6xnn&lt;/code>: Set &lt;code>Vx&lt;/code> to &lt;code>nn&lt;/code>&lt;/li>
&lt;li>&lt;code>7xnn&lt;/code>: Add &lt;code>nn&lt;/code> to &lt;code>Vx&lt;/code>&lt;/li>
&lt;li>&lt;code>Annn&lt;/code>: Set &lt;code>I&lt;/code> to &lt;code>nnn&lt;/code>&lt;/li>
&lt;li>&lt;code>Dxyn&lt;/code>: Draw&lt;/li>
&lt;/ul>
&lt;p>Of these, the draw instruction is the most complex to implement. In fact, I would go as far as to say it is the most complex instruction the CHIP-8 has, and I made a few mistakes when implementing it. Despite Tobias mentioning in a yellow box to use &lt;code>Vx&lt;/code> and &lt;code>Vy&lt;/code> instead of &lt;code>x&lt;/code> and &lt;code>y&lt;/code> directly, I still made that mistake. Another mistake I made is that I forgot to reset the x-coordinate for every row, i.e. each time the y-coordinate is incremented. The other 5 instructions should be trivial to implement.&lt;/p>
&lt;p>At first, I used the terminal as a display, using pipes to demarcate the pixels and the Unicode block character to represent a pixel that is on. With this, it should be possible to run &lt;a href="https://github.com/Timendus/chip8-test-suite#chip-8-splash-screen" target="_blank" rel="noopener" >Timendus&amp;rsquo; CHIP-8 splash screen&lt;/a> and the venerable &lt;em>IBM Logo&lt;/em> programs. Here is a recording of the first time my program successfully ran and displayed the IBM logo.&lt;/p>
&lt;script src="https://asciinema.org/a/698540.js" id="asciicast-698540" async="true">&lt;/script>
&lt;h2 id="completing-the-instruction-set">Completing the Instruction Set&lt;/h2>
&lt;p>After that, I began implementing more instructions, going in the order of Tobias&amp;rsquo; guide. As expected, I made many mistakes. Here they are in no particular order.&lt;/p>
&lt;ol>
&lt;li>For the call subroutine instruction &lt;code>2nnn&lt;/code>, I made a silly mistake and assigned &lt;code>nnn&lt;/code> to the program counter before pushing &lt;code>PC&lt;/code> onto the stack.&lt;/li>
&lt;li>I completely forgot to implement &lt;code>8XYE&lt;/code>.&lt;/li>
&lt;li>The font instruction assigned the wrong address to &lt;code>I&lt;/code>. I did not consider that each character takes up 5 bytes, and you have to multiply &lt;code>Vx&lt;/code> by 5 when calculating the address offset.&lt;/li>
&lt;/ol>
&lt;p>I also had tons of bugs with flag calculations. &lt;a href="https://github.com/Timendus/chip8-test-suite#flags-test" target="_blank" rel="noopener" >Timendus&amp;rsquo; flags test&lt;/a> was really helpful for checking this.&lt;/p>
&lt;ol start="4">
&lt;li>You should locally buffer either &lt;code>Vx&lt;/code> and &lt;code>Vy&lt;/code>, or the carry/overflow flag. You do not want the assigned value to affect the flag calculation, or vice versa.&lt;br>
For example, consider the instruction &lt;code>8xy4&lt;/code> (add with carry). If either &lt;code>x&lt;/code> or &lt;code>y&lt;/code> are &lt;code>F&lt;/code>, such as with &lt;code>8FA4&lt;/code>, it becomes tricky because the flag variable itself is being used as an operand. If you do not buffer the variable, you are going to
&lt;ul>
&lt;li>use the result of the flag calculation (&lt;code>0&lt;/code> or &lt;code>1&lt;/code>) when doing the addition, instead of using the value that was originally in &lt;code>VF&lt;/code> or,&lt;/li>
&lt;li>miscalculate the flag since you already overrode &lt;code>Vx&lt;/code> with the result.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>I did not realise that &lt;code>VF&lt;/code> should &lt;em>always&lt;/em> be overridden, even if it is used as an operand in the instruction.&lt;br>
Again with &lt;code>8FA4&lt;/code>, you should first fetch the values of &lt;code>VF&lt;/code> and &lt;code>VA&lt;/code>, add and assign them to &lt;code>VF&lt;/code>, then override &lt;code>VF&lt;/code> with &lt;code>0&lt;/code> or &lt;code>1&lt;/code> depending on whether the addition overflowed. This means that the value of the addition is not actually used when &lt;code>x&lt;/code> is &lt;code>F&lt;/code>.&lt;br>
I reckon this behaviour is a side effect of the CHIP-8&amp;rsquo;s lack of a dedicated carry flag.&lt;/li>
&lt;li>The overflow flag for subtraction should be set even if the result is &lt;code>0&lt;/code>, i.e. &lt;code>Vx&lt;/code> and &lt;code>Vy&lt;/code> are equal. In other words, the check for the overflow flag should be a slack inequality. This was not documented in Tobias&amp;rsquo; guide, and a website I was referring to misleadingly specified strict inequality. I have read online that this behaviour from the original COSMAC VIP is often overlooked due to its insignificance.&lt;/li>
&lt;/ol>
&lt;h2 id="integrating-a-windowed-front-end">Integrating a Windowed Front End&lt;/h2>
&lt;p>I decided to implement a desktop frontend first, and then port the code to the Arduboy, since it would be much easier to debug on a computer. I chose &lt;a href="https://wiki.libsdl.org/SDL3/FrontPage" target="_blank" rel="noopener" >SDL3&lt;/a> as the media interface library. I did not set up any fancy build system since I wanted to keep it simple; I have a &lt;a href="https://just.systems/man/en" target="_blank" rel="noopener" >&lt;code>justfile&lt;/code>&lt;/a> that uses &lt;code>clang&lt;/code> to compile, and &lt;code>pkg-config&lt;/code> to fetch the &lt;code>sdl3&lt;/code> headers. The disadvantage with this setup is that it is not cross-platform, but it should work on any &lt;abbr title="UNIX like">*NIX&lt;/abbr> platform as long as the necessary programs and libraries are installed.&lt;/p>
&lt;p>I used global variables for the frontend too, and I chose to use &lt;a href="https://wiki.libsdl.org/SDL3/README/main-functions#main-callbacks-in-sdl3" target="_blank" rel="noopener" >main callbacks&lt;/a>, a new feature in SDL3. Since rendering the pixels as-is would result in a &lt;em>tiny&lt;/em> window, I set the actual window size to a default of &lt;code>1280x640&lt;/code>, and set the &lt;a href="https://wiki.libsdl.org/SDL3/SDL_SetRenderLogicalPresentation" target="_blank" rel="noopener" >logical presentation&lt;/a> to &lt;code>64x32&lt;/code>. This would allow me to treat the window in my code as if its resolution was &lt;code>64x32&lt;/code>, and let SDL automatically scale and stretch/letterbox the content to the actual window&amp;rsquo;s resolution. I found SDL&amp;rsquo;s API super easy to use; excluding setup, I was able to paint the display buffer to the screen with just 7 lines of code.&lt;/p>
&lt;p>In between developing other features, I made some minor improvements to the SDL frontend.&lt;/p>
&lt;ul>
&lt;li>Change the colour scheme of the display to be more pleasing, &lt;del>stolen from&lt;/del> inspired by &lt;a href="https://github.com/massung/CHIP-8" target="_blank" rel="noopener" >J. Massung&amp;rsquo;s emulator&lt;/a>.&lt;/li>
&lt;li>Use the plus and minus keys to change the emulation frequency.&lt;/li>
&lt;li>Use the space bar to pause execution.&lt;/li>
&lt;li>Simplify input handling to use an array and for loops rather than massive switch statements.&lt;/li>
&lt;li>Switch to using nanoseconds instead of milliseconds to time the emulation more precisely.&lt;/li>
&lt;li>Buffer inputs for 1 frame (16.67 ms).&lt;/li>
&lt;li>Make some tweaks to pass &lt;a href="https://github.com/timendus/chip8-test-suite#quirks-test" target="_blank" rel="noopener" >Timendus&amp;rsquo; quirks test&lt;/a> for the original CHIP-8.&lt;/li>
&lt;li>Use the SUPER-CHIP&amp;rsquo;s better-looking font.&lt;/li>
&lt;li>Redraw the display whenever any window event occurs.&lt;/li>
&lt;/ul>
&lt;h2 id="separating-the-core">Separating the Core&lt;/h2>
&lt;p>I had been playtesting various games, and they seemed to work well. So, I decided it was time to refactor the core into a separate file. I started by moving the machine state variables into a struct, &lt;code>MachineState&lt;/code>. Since dynamic allocation was not necessary, I initialised the machine state on the stack using &lt;code>static&lt;/code>. I added separate functions for executing an instruction and ticking the timers.&lt;/p>
&lt;h2 id="further-refinements">Further Refinements&lt;/h2>
&lt;p>I posted about my emulator, the Arduboy port, and a draft of this article in the EmuDev server&amp;rsquo;s #chip-8 channel, and they provided some very helpful feedback about my implementation. I made more general improvements to my emulator, and some changes based on these suggestions.&lt;/p>
&lt;ul>
&lt;li>For the aforementioned flag instructions, it is recommended to buffer the overflow/carry flag rather than the registers.&lt;/li>
&lt;li>For instructions &lt;code>Ex9E&lt;/code> and &lt;code>ExA1&lt;/code>, only the least significant nibble of the &lt;code>Vx&lt;/code> register should be used when checking for key input.&lt;/li>
&lt;li>The &amp;ldquo;Amiga specific behaviour&amp;rdquo; Tobias talked about in his guide is a myth, and should not be implemented.&lt;/li>
&lt;li>I implemented key release detection for &lt;code>Fx0A&lt;/code> within the core.&lt;/li>
&lt;li>I added an illegal instruction handler to the core so that embedded systems without an easily accessible console can alert the user.&lt;/li>
&lt;/ul>
&lt;h1 id="arduboy-port">Arduboy Port&lt;/h1>
&lt;p>The &lt;a href="https://arduboy.com" target="_blank" rel="noopener" >Arduboy&lt;/a> is a credit-card sized, GameBoy-inspired device that uses the same chip and design as the &lt;a href="https://docs.arduino.cc/hardware/leonardo" target="_blank" rel="noopener" >Arduino Leonardo&lt;/a> internally. It is an embedded system, and if I want to port the emulator to it, I need to make the core more flexible and configurable.&lt;/p>
&lt;h2 id="difficulties">Difficulties&lt;/h2>
&lt;p>The most limiting factor with the Arduboy is its measly 2.5 &lt;abbr title="Kibibyte, 1024 bytes">KiB&lt;/abbr> of RAM, less than what the original COSMAC VIP from 1977 had! There is also the fact that it is much harder to debug; there are no sanitisers to catch illegal memory access (or an OS to catch &lt;code>segfault&lt;/code>s), no &lt;code>gdb&lt;/code>/&lt;code>lldb&lt;/code> to debug the program, no &lt;code>valgrind&lt;/code> to assess memory safety, etc. This was my first non-trivial project in a non memory-safe language and on an embedded system, and it was quite an adventure.&lt;/p>
&lt;p>I struggled to figure out how to instantiate the machine state struct, and I had so much trouble implementing a function that redirected &lt;code>printf&lt;/code> to the serial interface because I did not understand how to use C&amp;rsquo;s variable arguments API.&lt;/p>
&lt;p>&lt;a href="https://github.com/tiberiusbrown" target="_blank" rel="noopener" >Peter Brown&lt;/a>&amp;rsquo;s Arduboy simulator &lt;a href="https://tiberiusbrown.github.io/Ardens" target="_blank" rel="noopener" >Ardens&lt;/a> was quite helpful since it pauses at illegal memory access, stack overflows, etc. Even though I do not understand AVR assembly, I could use the surrounding function calls to roughly figure out where in the program the fault occurred. Make sure you use the &lt;code>.elf&lt;/code> file so that you have debug symbols in the simulator.&lt;/p>
&lt;h2 id="adapting-the-core">Adapting the Core&lt;/h2>
&lt;p>Anyways, back to the emulator. Key input handling, pixel querying and toggling, and clearing the display were made into configurable function pointers, and I removed the display buffer from the machine state since it took up too much RAM. I made the size of the emulated RAM configurable at compile time using the &lt;code>CORE_RAM_SIZE&lt;/code> macro, with a default of 4096 or &lt;code>0x1000&lt;/code>. Many CHIP-8 programs can run on less than 4 KiB of RAM.&lt;/p>
&lt;h2 id="implementing-the-arduboy-front-end">Implementing the Arduboy Front End&lt;/h2>
&lt;p>After a few days of frustrating debugging, I eventually got an initial working implementation. Key input was implemented using a per-program configurable keymap, which maps to the Arduboy&amp;rsquo;s 4-button D-pad and &lt;kbd>A&lt;/kbd> &amp;amp; &lt;kbd>B&lt;/kbd> buttons. Support for sound was next, which is a simple 440 Hz square wave tone.&lt;/p>
&lt;p>I added a program selection menu at startup, and included 4 programs with the emulator. I also added an option to upload a program to RAM from a computer over serial. You need to select &amp;ldquo;Load from Computer&amp;rdquo; in the Arduboy&amp;rsquo;s startup menu, run &lt;a href="https://github.com/theRookieCoder/ChipBoy8/blob/main/chipboy8.py" target="_blank" rel="noopener" >the Python script&lt;/a> with the path to the ROM file, type in the keys to map the Arduboy&amp;rsquo;s buttons to, and wait for the program to transfer. This feature was particularly frustrating to implement because it is hard to keep two computers in-sync and waiting for each other. This was exacerbated by the design of the Arduino &lt;a href="https://docs.arduino.cc/language-reference/en/functions/communication/serial" target="_blank" rel="noopener" >&lt;code>Serial&lt;/code>&lt;/a> class, which does not have a blocking method for reading.&lt;/p>
&lt;h2 id="optimising-the-memory-layout">Optimising the Memory Layout&lt;/h2>
&lt;p>In the CHIP-8&amp;rsquo;s memory map, addresses &lt;code>0x000&lt;/code>&amp;ndash;&lt;code>0x200&lt;/code> are unused (apart from the font, which is 80 bytes) since that is where the COSMAC VIP stored its interpreter. This amounts to 432 unused bytes, which is around 17% of the Arduboy&amp;rsquo;s total RAM. We cannot let these precious bytes go to waste, so I devised a solution to modify the memory map of the emulator.&lt;/p>
&lt;p>The font can be placed anywhere in RAM, so I placed it just before where the program starts. Since the program starts at &lt;code>0x200&lt;/code> and the font takes up &lt;code>0x50&lt;/code> bytes, I placed it at &lt;code>0x1B0&lt;/code>. Now, whenever the program uses addresses past the configured &lt;code>CORE_RAM_SIZE&lt;/code>, we can make it wrap around to the beginning of the emulated RAM like a ring buffer. This effectively increases our usable program memory from &lt;code>CORE_RAM_SIZE - 0x200&lt;/code> to &lt;code>CORE_RAM_SIZE - 0x50&lt;/code>; that is 432 more bytes of usable program memory!&lt;/p>
&lt;figure class="left" >
&lt;img src="memory-layout.svg" alt="ChipBoy8 Optimised Memory Layout" />
&lt;/figure>
&lt;p>This required some modification of the code. The core itself was easy to modify, but for program loading I had to replace &lt;code>Serial#readBytesUntil&lt;/code> with a custom for loop, and I had to split the &lt;code>memcpy_P&lt;/code> into 2 parts based on whether the program wraps around RAM buffer.&lt;/p>
&lt;p>With this, the emulator is pretty much complete! It passes all the tests as expected, and plays games quite well.&lt;/p>
&lt;h1 id="super-chip-emulator-in-rust">SUPER-CHIP Emulator in Rust&lt;/h1>
&lt;p>In 1991, Erik Bryntse introduced SUPER-CHIP (&lt;a href="https://groups.google.com/g/comp.sys.handhelds/c/RuzVNccds2Q/m/TcvLEWuY9scJ" target="_blank" rel="noopener" >v1.0&lt;/a> and &lt;a href="https://groups.google.com/g/comp.sys.handhelds/c/sDY9zFb6KUo/m/JcYBK2_yerMJ" target="_blank" rel="noopener" >v1.1&lt;/a>), a CHIP-8 emulator for the &lt;a href="https://www.hpcc.org/calculators/hp48.html" target="_blank" rel="noopener" >HP-48 series&lt;/a> of graphing calculators with additional features. It included a new 128x64 high resolution mode, support for 16x16 sprites in the draw instruction, a larger font, persistent storage and retrieval of upto 8 of the variable registers, display scrolling, and a proper exit instruction. I decided to write an emulator in Rust that supported both the CHIP-8 and SUPER-CHIP.&lt;/p>
&lt;h2 id="rust-port">Rust Port&lt;/h2>
&lt;p>I started off with the core of the emulator by roughly translating the C implementation. I was able to improve instruction decoding using pattern matching, but Rust requires explicit casts (&lt;code>val as T&lt;/code>) to convert into smaller types, which made the code messier. For the frontend I used SDL3 again, but without main callbacks since the Rust library did not have all the new features yet.&lt;/p>
&lt;p>This was when I discovered a bug with my timing. I was playing Tetris, and I noticed the pieces would glitch out when I rotated them.&lt;/p>
&lt;video controls preload="auto" width="100%" playsinline class="html-video">
&lt;source src="https://therookiecoder.is-a.dev/posts/chip8/glitched-tetris-pieces.webm" type="video/webm">
&lt;span>Your browser doesn't support embedded videos, but don't worry, you can &lt;a href="https://therookiecoder.is-a.dev/posts/chip8/glitched-tetris-pieces.webm">download it&lt;/a> and watch it with your favorite video player!&lt;/span>
&lt;/video>
&lt;h2 id="timing-bug">Timing Bug&lt;/h2>
&lt;p>I implemented the execution loop by creating two separate timers, one for the 60 Hz delay and sound timers, and another for the instruction timer. This approach should not be used since it is possible for a different amount of instructions to run between two timer ticks. For example, at 600 instruction per second, there is supposed to be a constant 10 instructions per timer tick (or &amp;ldquo;frame&amp;rdquo;). With the method I was using, it is possible for the timers to drift, and 9 or 11 instructions may execute between timer ticks. If the instruction frequency is not a multiple of 60, it can also lead to &amp;ldquo;leap&amp;rdquo; instructions. This leads to bugs with certain timing sensitive games like Tetris.&lt;/p>
&lt;p>The recommended approach, which I learned from the EmuDev server, is to run at a fixed 60 Hz. In each frame, you should decrement the timers, and execute a constant amount of instructions at once (&lt;abbr title="Instructions per Frame">IPF&lt;/abbr>). This also resolves the 100% single-core usage issue, since the CPU can sleep for the rest of the frame and busy-wait for less time.&lt;/p>
&lt;p>I implemented the new approach in the Rust code, and it fixed the glitches. I ported the new timing to the Arduboy emulator too, since it was suffering from the same issue, although less frequently. With this bug fix, the Rust port&amp;rsquo;s CHIP-8 implementation was complete.&lt;/p>
&lt;h2 id="super-chip-features">SUPER-CHIP Features&lt;/h2>
&lt;p>The first order of business was to increase the display buffer to &lt;code>128x64&lt;/code>. In low resolution mode, which is backwards compatible with the CHIP-8, the emulator must scale the &amp;ldquo;legacy&amp;rdquo; display coordinates by 2 and toggle pixels in 2x2 blocks. I took the opportunity to improve the drawing code as well.&lt;/p>
&lt;p>Despite claiming backwards compatibility, certain instructions worked differently in the SUPER-CHIP emulator compared to the original COSMAC VIP&amp;rsquo;s CHIP-8 emulator. These are called &amp;ldquo;quirks&amp;rdquo;, and the SUPER-CHIP&amp;rsquo;s are the following.&lt;/p>
&lt;ul>
&lt;li>&lt;code>8xy1&lt;/code>, &lt;code>8xy2&lt;/code>, and &lt;code>8xy3&lt;/code> do not reset &lt;code>VF&lt;/code>&lt;/li>
&lt;li>&lt;code>8xy6&lt;/code> and &lt;code>8xyE&lt;/code> use &lt;code>Vx&lt;/code> instead of &lt;code>Vy&lt;/code>&lt;/li>
&lt;li>&lt;code>Bnnn&lt;/code> jumps to &lt;code>nnn + Vx&lt;/code> instead of &lt;code>nnn + V0&lt;/code>&lt;/li>
&lt;li>&lt;code>Fx55&lt;/code> and &lt;code>Fx66&lt;/code> do not increment &lt;code>I&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>I implemented these, and chose which mode to use based on whether the ROM file had the extension &lt;code>ch8&lt;/code> (CHIP-8) or &lt;code>sc8&lt;/code> (SCHIP). I ran &lt;a href="https://github.com/timendus/chip8-test-suite#quirks-test" target="_blank" rel="noopener" >Timendus&amp;rsquo; quirks test&lt;/a> for both the original CHIP-8 and the modern SUPER-CHIP, and the only test that did not pass was the original CHIP-8&amp;rsquo;s display wait behaviour.&lt;/p>
&lt;p>I implemented the new SUPER-CHIP instructions as well. However, for the moment I left out the persistent flag storage instructions, &lt;code>Fx75&lt;/code> and &lt;code>Fx85&lt;/code>. When implementing the scrolling instructions, I felt that Rust&amp;rsquo;s slice methods and slice indexing made it particularly convenient to implement. Again, the most complex instruction was the draw instruction, which had 3 &amp;ldquo;modes&amp;rdquo; now; low-resolution, high-resolution, and 16-pixel sprite.&lt;/p>
&lt;h1 id="super-chip-port-to-c">SUPER-CHIP Port to C++&lt;/h1>
&lt;p>After experiencing the limitations of C, I was interested in learning C++. I decided to port just the SUPER-CHIP part of the Rust emulator to C++, and use SDL3 again. I chose to use a build system this time, which was CMake.&lt;/p>
&lt;p>My favourite improvements over C are namespaces and classes. I created the &lt;code>core&lt;/code> namespace, with the machine state and constants like the display dimensions. The core methods are namespaced within the class too. I used classes to abstract away the SDL code, making use of the class destructor to implicitly clean up SDL at the end of the scope.&lt;/p>
&lt;p>References meant that there were no longer any pointers to deal with, which I was glad about since I found pointers confusing at times, coming from Rust and its references. &lt;code>std::array&lt;/code>s did not degrade into pointers like C-style arrays, so I could avoid pointer arithmetic. STL&amp;rsquo;s functional features like iterators were a breath of fresh air compared to C, albeit confusing and unintuitive at times, and safe abstractions over functions like &lt;code>memcpy&lt;/code> and &lt;code>memmove&lt;/code> are welcome additions. Like Rust&amp;rsquo;s slice methods, STL&amp;rsquo;s move and fill methods made display scrolling easy to implement.&lt;/p>
&lt;p>One major frustration I had was with CMake, which was horrible to learn and use. The official documentation is unhelpful, tutorials that use modern features are sparse, and there is a confuddling number of ways to achieve the same thing.&lt;/p>
&lt;p>C++ was confusing at times too. One behaviour that tripped me up was that it copies values by default, in contrast to Rust&amp;rsquo;s move by default. This really cemented in me that I should be using references wherever possible, especially in range-based for loops. Coming from Rust, I dislike the implementation of &lt;code>auto&lt;/code>, although I understand that with the looser typing and polymorphism, it would be very difficult to make it work properly.&lt;/p>
&lt;p>I thought that IDE support for C++ in VS Code was poor. C++ is a complex language with many features, and I felt that VS Code and its extensions did not provide as much help as rust-analyzer for Rust would, for example. I was able to use &lt;a href="https://www.jetbrains.com/clion" target="_blank" rel="noopener" >JetBrains CLion&lt;/a> after my GitHub &lt;a href="https://education.github.com/pack" target="_blank" rel="noopener" >Student Developer Pack&lt;/a> was approved, and I find that it is much more helpful, with a significantly better experience out of the box. Unfortunately it is a paid product, unless you are a student.&lt;/p>
&lt;h1 id="potential-features-and-further-optimisations">Potential Features and Further Optimisations&lt;/h1>
&lt;ol>
&lt;li>I have yet to implement sound for any of the desktop versions.&lt;/li>
&lt;li>The Arduboy&amp;rsquo;s ATmega32u4 has 32 KiB of flash memory, of which 15 KiB is still free. We can pack even more games into this space, but they would need to be less than 1 KiB and playable with a D-pad and 2 buttons. Contact me on Discord at &lt;code>therookiecoder&lt;/code> if you have game suggestions.&lt;/li>
&lt;li>The &lt;a href="https://docs.arduino.cc/libraries/arduboy2" target="_blank" rel="noopener" >&lt;code>Arduboy2&lt;/code>&lt;/a> library has an internal screen buffer for the Arduboy&amp;rsquo;s&lt;code>128x64&lt;/code> display that takes up 1 KiB when packed into bytes. Since the CHIP-8 only has a &lt;code>64x32&lt;/code> display, it should be possible to replace it with a display buffer that only takes up 256 bytes.&lt;/li>
&lt;li>Implement the CHIP-8&amp;rsquo;s display wait behaviour.&lt;/li>
&lt;li>Port the SUPER-CHIP emulator to the Arduboy.&lt;/li>
&lt;li>Add support for the XO-CHIP, although it would be near impossible to port this to the Arduboy without significant compromises.&lt;/li>
&lt;/ol></content></item></channel></rss>