<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://byteofmelon.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://byteofmelon.com/" rel="alternate" type="text/html" /><updated>2026-01-23T08:32:35+00:00</updated><id>https://byteofmelon.com/feed.xml</id><title type="html">Byte of Melon</title><subtitle>The official site of Michael Webb, who runs Byte of Melon, a tech and video game analysis show with a slight focus on comedy; among other various projects.</subtitle><entry><title type="html">Learning Low-Level Computing and C++ by Making a Game Boy Emulator - The Making of GameByte</title><link href="https://byteofmelon.com/blog/2026/making-of-gamebyte" rel="alternate" type="text/html" title="Learning Low-Level Computing and C++ by Making a Game Boy Emulator - The Making of GameByte" /><published>2026-01-21T00:00:00+00:00</published><updated>2026-01-21T00:00:00+00:00</updated><id>https://byteofmelon.com/blog/2026/making-of-gamebyte</id><content type="html" xml:base="https://byteofmelon.com/blog/2026/making-of-gamebyte"><![CDATA[<p>I’ve decided to split this blog post into four main sections - <a href="#the-why">“The Why”</a>, <a href="#the-how">“The How”</a>, <a href="#whats-missing">“What’s Missing”</a> and <a href="#whats-next">“What’s Next”</a>. If you just want to see the source code and get your hands dirty with GameByte, click <a href="https://github.com/ByteOfMelon/GameByte">here</a> to go to the GitHub repo.</p>

<h2 id="the-why">The Why</h2>
<p>I have always been fascinated by the vast amounts of open-source projects that dive deep into the nitty-gritty of hardware and software. Things like the <a href="https://github.com/isledecomp/isle">LEGO Island decompilation project</a> and especially the <a href="https://github.com/grimdoomer/Xbox360BadUpdate">Bad Update exploit for Xbox 360</a> and its associated further efforts to open the (sort of) softmodding floodgates for the Xbox 360 (shoutout to my homie <a href="https://invoxiplaygames.uk/">InvoxiPlayGames</a>!). The passion and drive behind these projects is something I’ve always admired, and I’ve always wanted to be a part of it in some way, shape or form. Problem is, I had a shoddy background on how low-level computing works or how to use lower-level languages like C and C++, despite working on various pieces of code for years at this point using languages like C#, Golang and JavaScript/TypeScript. Therefore, I set out to learn through what is, in my opinion, the best method: actually making a small project that needs these skills.</p>

<p>In this vein, I decided that an emulator would be a cool end result for a project and a goal that would keep me motivated, while also allowing me to learn some low-level computing skills AND begin learning how to use C++ at the same time. This seemed like a win-win to me! I specifically decided on the original Game Boy as the piece of hardware to emulate because not only is it extremely well documented, especially compared to many other game consoles, but it is also a piece of hardware that is very easy to emulate in comparison to other consoles as well.</p>

<h2 id="the-how">The How</h2>
<h3 id="the-barely-beginnings">The (barely) beginnings</h3>
<p>I initially started this project back in June 2025. I did some initial research mainly using <a href="https://gbdev.io/pandocs/About.html">Pan Docs</a> as my guide on the Game Boy’s hardware, alongside a <a href="https://cturt.github.io/cinoop.html">great blog post by CTurt</a> where he describes the creation of his very own C-based minimal Game Boy emulator. I started with only a very basic CMakeLists.txt, and started a small amount of boilerplating, such as adding variables to represent the Game Boy CPU’s registers and basic functions to handle the Game Boy’s 16-bit register pairs, which are made up of a pairing of two 8-bit registers.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// A small sampling of the initial cpu.cpp file.</span>
<span class="k">class</span> <span class="nc">CPU</span> <span class="p">{</span>
    <span class="nl">public:</span>
        <span class="c1">// 8-bit general purpose registers</span>
        <span class="kt">uint8_t</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">l</span><span class="p">;</span>

        <span class="c1">// Flag register</span>
        <span class="kt">uint8_t</span> <span class="n">f</span><span class="p">;</span>

        <span class="c1">// 16-bit registers</span>
        <span class="kt">uint16_t</span> <span class="n">sp</span><span class="p">;</span> <span class="c1">// Stack pointer</span>
        <span class="kt">uint16_t</span> <span class="n">pc</span><span class="p">;</span> <span class="c1">// Program counter</span>

        <span class="cm">/**
         * Getter/setter methods for 16-bit register pairs
         */</span>

        <span class="c1">// AF register pair</span>
        <span class="kt">uint16_t</span> <span class="n">get_af</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> 
            <span class="k">return</span> <span class="p">(</span><span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">uint16_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">|</span> <span class="n">f</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="kt">void</span> <span class="n">set_af</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="n">value</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">a</span> <span class="o">=</span> <span class="p">(</span><span class="n">value</span> <span class="o">&gt;&gt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">;</span> <span class="n">f</span> <span class="o">=</span> <span class="n">value</span> <span class="o">&amp;</span> <span class="mh">0xF0</span><span class="p">;</span> <span class="c1">// Lower 4 bits of F are always 0</span>
        <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>

<p>And then it sat for almost <strong>6 months</strong>!</p>

<p>Yeah, I’m really not the best at keeping myself motivated, even when I try to be. I quickly realized how much of a commitment this was going to be (at least, I thought, anyway) and decided to abandon it. Then, one random day just under six months later, I stumbled across the repository for GameByte and decided that I really wanted to tackle it, as I felt more motivated than ever to complete the project in order to achieve the goals I described above. This is when things truly began.</p>

<h3 id="the-real-beginnings---header-files-basic-cpummu-and-the-pain-of-mbcs">The (real) beginnings - Header files, basic CPU/MMU and the pain of MBCs</h3>
<p>On January 6, 2026, I began seriously tackling the project. I still utilized things like Pan Docs and CTurt’s blog post as great guides to decide on some basic implementation things, alongside consulting a couple friends with much better general knowledge on low-level programming such as the previously mentioned <a href="https://invoxiplaygames.uk/">InvoxiPlayGames</a>.</p>

<p>I began by properly seperating the CPU/MMU classes into proper header and source files, as is proper C++ convention. At first, it sort of annoyed me, as I was used to being able to contain my variables, structs, and functions all in one class file, but I actually grew to like this seperation as the CPU class in particular grew to be a behemoth with all of the opcode implementations that were needed. It was a lot easier to find specific variables and things like that with this system.</p>

<p>I then began to implement basic communication between the CPU and MMU classes so that the CPU could read and write to the emulated RAM, alongside having the MMU class allocate sections of CPU address space to specific parts as outlined in the Pan Docs, such as cartridge ROM space, video RAM (VRAM), and specific I/O registers and sprite attribute memory for input and the PPU respectively.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">MMU</span> <span class="p">{</span>
    <span class="nl">public:</span>
        <span class="kt">uint8_t</span> <span class="n">read_byte</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="n">address</span><span class="p">);</span>
        <span class="kt">void</span> <span class="n">write_byte</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="n">address</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="n">value</span><span class="p">);</span>

        <span class="kt">uint16_t</span> <span class="n">read_word</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="n">address</span><span class="p">);</span>
        <span class="kt">void</span> <span class="n">write_word</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="n">address</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">value</span><span class="p">);</span>
        
        <span class="kt">bool</span> <span class="n">load_game</span><span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">size</span><span class="p">);</span>
    <span class="nl">private:</span>
        <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">cart</span><span class="p">[</span><span class="mh">0x8000</span><span class="p">];</span> <span class="c1">// 32 KB total cartridge ROM space</span>
        <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">vram</span><span class="p">[</span><span class="mh">0x2000</span><span class="p">];</span> <span class="c1">// 8 KB of video RAM (VRAM)</span>
        <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">eram</span><span class="p">[</span><span class="mh">0x2000</span><span class="p">];</span> <span class="c1">// 8 KB of external RAM (cartridge battery-backed RAM)</span>
        <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">wram</span><span class="p">[</span><span class="mh">0x2000</span><span class="p">];</span> <span class="c1">// 8 KB of work RAM (WRAM). In CGB mode, this is switchable banks 1-7</span>
        <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">oam</span><span class="p">[</span><span class="mh">0xA0</span><span class="p">];</span>    <span class="c1">// 160 bytes for sprite attribute memory (OAM)</span>
        <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">io</span><span class="p">[</span><span class="mh">0x80</span><span class="p">];</span>     <span class="c1">// 128 bytes for I/O registers</span>
        <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">hram</span><span class="p">[</span><span class="mh">0x7F</span><span class="p">];</span>   <span class="c1">// 127 bytes for high RAM</span>
        <span class="kt">uint8_t</span> <span class="n">ie</span><span class="p">;</span>                 <span class="c1">// Interrupt Enable register (IE) at 0xFFFF</span>
<span class="p">};</span>
</code></pre></div></div>

<p>I also added a ROM class that could load basic Game Boy ROMs utilizing standard C++ functions like <code class="language-plaintext highlighter-rouge">fseek</code> and <code class="language-plaintext highlighter-rouge">fread</code>. At this time, I only implemented basic cartridge ROMs. Basically, many Game Boy games utilize <a href="https://gbdev.io/pandocs/MBCs.html">Memory Bank Controllers (MBCs for short)</a> that expand the available address space for games by using <a href="https://en.wikipedia.org/wiki/Bank_switching">bank switching</a>. Not only is this logic much more complicated to write into an emulator, but there are also at least 27 different known mappers across the original Game Boy library, making full compatibility extremely difficult. Plus, the only game that matters, Tetris, does not use any MBCs or other hardware inside its cartridge, so this worked out perfectly.</p>

<p>Next, I wrote a lot of the basic emulation loop code into <code class="language-plaintext highlighter-rouge">main.cpp</code>, handling things like the specific cycles per frame (70,244) for timing. The basic CPU loop went as follows:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// main.cpp</span>
<span class="c1">// Run CPU for one frame</span>
<span class="k">while</span> <span class="p">(</span><span class="n">cycles_this_frame</span> <span class="o">&lt;</span> <span class="n">CYCLES_PER_FRAME</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">try</span> <span class="p">{</span>
        <span class="kt">int</span> <span class="n">cycles</span> <span class="o">=</span> <span class="n">cpu</span><span class="p">.</span><span class="n">step</span><span class="p">();</span>
        <span class="n">cycles_this_frame</span> <span class="o">+=</span> <span class="n">cycles</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">exception</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">"[GameByte] Emulation error about to occur. Total cycles we got through: "</span> <span class="o">&lt;&lt;</span> <span class="n">cpu</span><span class="p">.</span><span class="n">total_cycles</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="n">e</span><span class="p">.</span><span class="n">what</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="n">running</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span> <span class="c1">// Stop on error</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// cpu.cpp</span>
<span class="kt">uint8_t</span> <span class="n">CPU</span><span class="o">::</span><span class="n">step</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">mmu</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">"[CPU] MMU was not connected to CPU before execution"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">uint8_t</span> <span class="n">opcode</span> <span class="o">=</span> <span class="n">mmu</span><span class="o">-&gt;</span><span class="n">read_byte</span><span class="p">(</span><span class="n">pc</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"[CPU] DEBUG: Executing opcode 0x%02X (instruction %s) at address 0x%04X</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">opcode</span><span class="p">,</span> <span class="n">instructions</span><span class="p">[</span><span class="n">opcode</span><span class="p">].</span><span class="n">name</span><span class="p">,</span> <span class="n">pc</span><span class="p">);</span>
    <span class="n">pc</span><span class="o">++</span><span class="p">;</span>

    <span class="kt">uint8_t</span> <span class="n">cycles</span> <span class="o">=</span> <span class="p">(</span><span class="k">this</span><span class="o">-&gt;*</span><span class="n">instructions</span><span class="p">[</span><span class="n">opcode</span><span class="p">].</span><span class="n">operate</span><span class="p">)();</span>

    <span class="n">total_cycles</span> <span class="o">+=</span> <span class="n">cycles</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">cycles</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">CPU</span><span class="o">::</span><span class="n">init_instructions</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">instructions</span><span class="p">.</span><span class="n">assign</span><span class="p">(</span><span class="mi">256</span><span class="p">,</span> <span class="p">{</span> <span class="s">"XXX"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">XXX</span> <span class="p">});</span>
    <span class="n">instructions</span><span class="p">[</span><span class="mh">0x00</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"NOP"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">NOP</span> <span class="p">};</span>
    <span class="n">instructions</span><span class="p">[</span><span class="mh">0xC3</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"JP a16"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">JP_a16</span> <span class="p">};</span>
    <span class="n">instructions</span><span class="p">[</span><span class="mh">0xAF</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"XOR A, A"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">XOR_a</span> <span class="p">};</span>
    <span class="n">instructions</span><span class="p">[</span><span class="mh">0x21</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"LD HL, n16"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">LD_HL_n16</span> <span class="p">};</span>
    <span class="n">instructions</span><span class="p">[</span><span class="mh">0x0E</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"LD C, n8"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">LD_C_n8</span> <span class="p">};</span>
    <span class="n">instructions</span><span class="p">[</span><span class="mh">0x06</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"LD B, n8"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">LD_B_n8</span> <span class="p">};</span>
    <span class="n">instructions</span><span class="p">[</span><span class="mh">0x31</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"LD SP, n16"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">LD_SP_n16</span> <span class="p">};</span>
    <span class="n">instructions</span><span class="p">[</span><span class="mh">0x32</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"LD (HL-), A"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">LD_HLmA_dec</span> <span class="p">};</span>
    <span class="n">instructions</span><span class="p">[</span><span class="mh">0x05</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"DEC B"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">DEC_B</span> <span class="p">};</span>
    <span class="n">instructions</span><span class="p">[</span><span class="mh">0x0D</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"DEC C"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">DEC_C</span> <span class="p">};</span>
    <span class="n">instructions</span><span class="p">[</span><span class="mh">0x20</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"JR NZ, e8"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">JR_NZ_e8</span> <span class="p">};</span>
    <span class="n">instructions</span><span class="p">[</span><span class="mh">0x3E</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"LD A, n8"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">LD_A_n8</span> <span class="p">};</span>
    <span class="n">instructions</span><span class="p">[</span><span class="mh">0xF3</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"DI"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">DI</span> <span class="p">};</span>
    <span class="n">instructions</span><span class="p">[</span><span class="mh">0xFB</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"EI"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">CPU</span><span class="o">::</span><span class="n">EI</span> <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I decided to go with this system as it is similar to what Cinoop and a couple of other Game Boy emulators I had looked at on GitHub had done and it was easiest for me to understand what was happening. There are definitely far more efficient ways of doing this looking back, but I think it also makes it a better research emulator that way as the code is more digestible.</p>

<p>To do initial time synchronization, I utilzied SDL’s GetTicks() functionality and had the emulator sleep if the time elapsed was less than it would take on the actual system to run the opcode.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Timing synchronization</span>
<span class="kt">uint64_t</span> <span class="n">end_time</span> <span class="o">=</span> <span class="n">SDL_GetTicks</span><span class="p">();</span>
<span class="kt">double</span> <span class="n">elapsed_ms</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span><span class="p">(</span><span class="n">end_time</span> <span class="o">-</span> <span class="n">start_time</span><span class="p">);</span>

<span class="k">if</span> <span class="p">(</span><span class="n">elapsed_ms</span> <span class="o">&lt;</span> <span class="n">FRAME_TIME_MS</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Sleep for the remaining time</span>
    <span class="n">SDL_Delay</span><span class="p">(</span><span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">uint32_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">FRAME_TIME_MS</span> <span class="o">-</span> <span class="n">elapsed_ms</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I was seeing some life out of the emulator at this point, albeit just an unimplemented opcode error as I had very few implemented at this stage:</p>

<p><img src="/img/blog/making-of-gamebyte/initial-life.png" alt="The GameByte emulator running in a terminal window. At the bottom it says &quot;[CPU] Illegal/unimplemented opcode 0xC3 at 0x101" /></p>

<h3 id="opcode-hell">Opcode hell</h3>
<p>Now, it was really time to dig into the nitty-gritty of implementing the CPU: implementing at least enough of the Game Boy’s opcodes to get Tetris to load properly. This would not have been possible without GB Dev’s <a href="https://gbdev.io/gb-opcodes//optables/dark">awesome optables</a> that convert the raw instruction byte to the proper assembly, alongside showing the duration of the instruction in t-states for a proper emulation, and showing what flags, if any, are changed upon execution.</p>

<p>Most of the opcode implementations are quite basic and not worth covering here, but for items like <code class="language-plaintext highlighter-rouge">HALT</code> (opcode 0x76), I had to add a <code class="language-plaintext highlighter-rouge">halted</code> variable to the CPU, alongside some logic in <code class="language-plaintext highlighter-rouge">main.cpp</code>:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Initalize cycle count and check for halting</span>
<span class="kt">int</span> <span class="n">cycles</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">cpu</span><span class="p">.</span><span class="n">halted</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">cycles</span> <span class="o">=</span> <span class="n">cpu</span><span class="p">.</span><span class="n">step</span><span class="p">();</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">cycles</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="what-the-hell-is-a-prefix">What the hell is a PREFIX?</h3>
<p>The “opcode” <code class="language-plaintext highlighter-rouge">0xCB</code> is not really an opcode itself, but is instead something called a prefix. The PREFIX allows access to the Game Boy CPU’s “extra” opcodes that handle things like bit manipulations, rotations and shifts. The <a href="https://gbdev.io/gb-opcodes//optables/dark#prefixed">GB Dev optables</a> show exactly what each of the prefixed opcodes do as well, which is incredibly useful.</p>

<p>I got myself quite stuck on how to implement this system for quite a while. I mean, I already had a ton of basic opcodes to implement. Would I really have to write out these 256 opcodes in addition to the base 256 opcodes? Well, luckily, as it turns out, these instructions can be condensed quite easily as there are just a few basic “categories” where each opcode changes just a slight detail:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Extended opcode implementation </span>
<span class="kt">uint8_t</span> <span class="n">CPU</span><span class="o">::</span><span class="n">execute_cb_instruction</span><span class="p">(</span><span class="kt">uint8_t</span> <span class="n">opcode</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Determine register target based on bottom 3 bits</span>
    <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">registers</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="o">&amp;</span><span class="n">b</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">c</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">d</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">e</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">h</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">l</span><span class="p">,</span> <span class="nb">nullptr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">a</span> <span class="p">};</span>
    <span class="kt">uint8_t</span> <span class="n">target_idx</span> <span class="o">=</span> <span class="n">opcode</span> <span class="o">&amp;</span> <span class="mh">0x07</span><span class="p">;</span>
    
    <span class="c1">// Most CB instructions take 8 cycles, but [HL] operations take 16</span>
    <span class="kt">uint8_t</span> <span class="n">cycles</span> <span class="o">=</span> <span class="p">(</span><span class="n">target_idx</span> <span class="o">==</span> <span class="mi">6</span><span class="p">)</span> <span class="o">?</span> <span class="mi">16</span> <span class="o">:</span> <span class="mi">8</span><span class="p">;</span>

    <span class="kt">uint8_t</span> <span class="n">value</span><span class="p">;</span>
    <span class="c1">// Check if target is memory ([HL]) or register</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">target_idx</span> <span class="o">==</span> <span class="mi">6</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">value</span> <span class="o">=</span> <span class="n">mmu</span><span class="o">-&gt;</span><span class="n">read_byte</span><span class="p">(</span><span class="n">get_hl</span><span class="p">());</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="n">value</span> <span class="o">=</span> <span class="o">*</span><span class="n">registers</span><span class="p">[</span><span class="n">target_idx</span><span class="p">];</span>
    <span class="p">}</span>

    <span class="c1">// Decode the top two bits for the category</span>
    <span class="k">switch</span> <span class="p">(</span><span class="n">opcode</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// Shifts and Rotates (0x00 - 0x3F)</span>
        <span class="k">case</span> <span class="mh">0x00</span><span class="p">:</span>
            <span class="n">value</span> <span class="o">=</span> <span class="n">handle_cb_shift_rotate</span><span class="p">(</span><span class="n">opcode</span><span class="p">,</span> <span class="n">value</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>

        <span class="c1">// BIT (0x40 - 0x7F)</span>
        <span class="k">case</span> <span class="mh">0x01</span><span class="p">:</span>
            <span class="p">{</span>
                <span class="kt">uint8_t</span> <span class="n">bit</span> <span class="o">=</span> <span class="p">(</span><span class="n">opcode</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x07</span><span class="p">;</span>
                <span class="n">set_flag_z</span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">value</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">bit</span><span class="p">)));</span>
                <span class="n">set_flag_n</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
                <span class="n">set_flag_h</span><span class="p">(</span><span class="nb">true</span><span class="p">);</span>

                <span class="c1">// BIT doesn't write back</span>
                <span class="k">return</span> <span class="n">cycles</span><span class="p">;</span>
            <span class="p">}</span>

        <span class="c1">// RES (0x80 - 0xBF)</span>
        <span class="k">case</span> <span class="mh">0x02</span><span class="p">:</span>
            <span class="p">{</span>
                <span class="kt">uint8_t</span> <span class="n">bit</span> <span class="o">=</span> <span class="p">(</span><span class="n">opcode</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x07</span><span class="p">;</span>
                <span class="n">value</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">bit</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">break</span><span class="p">;</span>

        <span class="c1">// SET (0xC0 - 0xFF)</span>
        <span class="k">case</span> <span class="mh">0x03</span><span class="p">:</span>
            <span class="p">{</span>
                <span class="kt">uint8_t</span> <span class="n">bit</span> <span class="o">=</span> <span class="p">(</span><span class="n">opcode</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x07</span><span class="p">;</span>
                <span class="n">value</span> <span class="o">|=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">bit</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Write the result back</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">target_idx</span> <span class="o">==</span> <span class="mi">6</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">mmu</span><span class="o">-&gt;</span><span class="n">write_byte</span><span class="p">(</span><span class="n">get_hl</span><span class="p">(),</span> <span class="n">value</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="o">*</span><span class="n">registers</span><span class="p">[</span><span class="n">target_idx</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">cycles</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">uint8_t</span> <span class="n">CPU</span><span class="o">::</span><span class="n">handle_cb_shift_rotate</span><span class="p">(</span><span class="kt">uint8_t</span> <span class="n">opcode</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="n">value</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">uint8_t</span> <span class="n">sub_op</span> <span class="o">=</span> <span class="p">(</span><span class="n">opcode</span> <span class="o">&gt;&gt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x07</span><span class="p">;</span>
    <span class="kt">bool</span> <span class="n">old_carry</span> <span class="o">=</span> <span class="n">get_flag_c</span><span class="p">();</span>

    <span class="k">switch</span> <span class="p">(</span><span class="n">sub_op</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// RLC (Rotate Left)</span>
        <span class="k">case</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">set_flag_c</span><span class="p">(</span><span class="n">value</span> <span class="o">&amp;</span> <span class="mh">0x80</span><span class="p">);</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">value</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">value</span> <span class="o">&gt;&gt;</span> <span class="mi">7</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>

        <span class="c1">// RRC (Rotate Right)</span>
        <span class="k">case</span> <span class="mi">1</span><span class="p">:</span>
            <span class="n">set_flag_c</span><span class="p">(</span><span class="n">value</span> <span class="o">&amp;</span> <span class="mh">0x01</span><span class="p">);</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">value</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">value</span> <span class="o">&lt;&lt;</span> <span class="mi">7</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>
        
        <span class="c1">// RL (Rotate Left through Carry)</span>
        <span class="k">case</span> <span class="mi">2</span><span class="p">:</span>
            <span class="n">set_flag_c</span><span class="p">(</span><span class="n">value</span> <span class="o">&amp;</span> <span class="mh">0x80</span><span class="p">);</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">value</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">old_carry</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>

        <span class="c1">// RR (Rotate Right through Carry)</span>
        <span class="k">case</span> <span class="mi">3</span><span class="p">:</span>
            <span class="n">set_flag_c</span><span class="p">(</span><span class="n">value</span> <span class="o">&amp;</span> <span class="mh">0x01</span><span class="p">);</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">value</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">old_carry</span> <span class="o">?</span> <span class="mh">0x80</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>

        <span class="c1">// SLA (Shift Left Arithmetic)</span>
        <span class="k">case</span> <span class="mi">4</span><span class="p">:</span>
            <span class="n">set_flag_c</span><span class="p">(</span><span class="n">value</span> <span class="o">&amp;</span> <span class="mh">0x80</span><span class="p">);</span>
            <span class="n">value</span> <span class="o">&lt;&lt;=</span> <span class="mi">1</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>

        <span class="c1">// SRA (Shift Right Arithmetic - preserve bit 7)</span>
        <span class="k">case</span> <span class="mi">5</span><span class="p">:</span>
            <span class="n">set_flag_c</span><span class="p">(</span><span class="n">value</span> <span class="o">&amp;</span> <span class="mh">0x01</span><span class="p">);</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">int8_t</span><span class="o">&gt;</span><span class="p">(</span><span class="n">value</span><span class="p">))</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
        
        <span class="c1">// SWAP (Swap nibbles)</span>
        <span class="k">case</span> <span class="mi">6</span><span class="p">:</span>
            <span class="n">set_flag_c</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
            <span class="n">value</span> <span class="o">=</span> <span class="p">((</span><span class="n">value</span> <span class="o">&amp;</span> <span class="mh">0x0F</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">)</span> <span class="o">|</span> <span class="p">((</span><span class="n">value</span> <span class="o">&amp;</span> <span class="mh">0xF0</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="mi">4</span><span class="p">);</span>
            <span class="k">break</span><span class="p">;</span>

        <span class="c1">// SRL (Shift Right Logical)</span>
        <span class="k">case</span> <span class="mi">7</span><span class="p">:</span>
            <span class="n">set_flag_c</span><span class="p">(</span><span class="n">value</span> <span class="o">&amp;</span> <span class="mh">0x01</span><span class="p">);</span>
            <span class="n">value</span> <span class="o">&gt;&gt;=</span> <span class="mi">1</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">set_flag_z</span><span class="p">(</span><span class="n">value</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
    <span class="n">set_flag_n</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
    <span class="n">set_flag_h</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">value</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>While this was not fun to figure out whatsoever, and took tons of research into how the Game Boy’s CPU (and the Z80 that it is based on) works with this prefix system, at least it has a nice, clean solution in the end.</p>

<h3 id="the-ppu">The PPU</h3>
<p>As this post is already extremely long, I won’t go into insane detail here, as you can read the <a href="https://github.com/ByteOfMelon/GameByte/blob/master/src/core/ppu.cpp">full source code</a> on your own, in addition to the <a href="https://gbdev.io/pandocs/Graphics.html">Pan Docs</a> and other useful documentation describing the PPU in great detail.</p>

<p>In short, though, the PPU connects to the MMU (where its VRAM is stored), then initializes SDL’s video component, and creates a window, a renderer, and a texture that holds the current framebuffer data. Then, the PPU also ticks to move between its 4 states - OAM search, pixel transfer, H-blank, and V-blank. These timings are necessary to get proper data into the framebuffer.</p>

<p>To draw a scanline, the <code class="language-plaintext highlighter-rouge">draw_scanline()</code> function gets the current scroll position (the X/Y coordinates of where the frame is meant to render out of the total 256x256 map), reads the tile map, the tile indices, and then extracts the pixels needed to be placed on the scanline to then go into the framebuffer, in this case, an SDL texture.</p>

<p>Eventually, I got this to render!</p>

<p><img src="/img/blog/making-of-gamebyte/garbled-graphics.png" alt="The GameByte emulator window. The graphics are rendering, but are extremely garbled." /></p>

<p>Okay, I mean this isn’t usable whatsoever, but hey, I actually got something to render! Truly incredible stuff in my view when I didn’t really know what I was doing properly. There was a lot to tackle from here. For instance, something that made me pull my hair out for the longest time was something really basic that I screwed up and didn’t realize.</p>

<p>This code looks very innocent:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint8_t</span> <span class="n">ppu_cycles</span><span class="p">;</span>
</code></pre></div></div>

<p>However, you may have realized that this is not a large enough integer to store the amount of PPU cycles needed to complete, well, a full PPU frame. Basically, you need to get up to 456 cycles before the PPU will reach the v-blank interval step. Problem is, you can only go up to 255 with an 8-bit integer. Whoops!</p>

<p>Changing that to a 16-bit integer got me a proper v-blank:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint16_t</span> <span class="n">ppu_cycles</span><span class="p">;</span>
</code></pre></div></div>

<p><img class="no-resize" src="/img/blog/making-of-gamebyte/somewhat-rendering-copyright.png" alt="The GameByte emulator window. The graphics are attempting to render, but only a quotation mark and about half of the (C) 1989 BULLET-PROOF text renders on the screen." /></p>

<p>Well, somewhat. This took much, MUCH more work to figure out, and was a multi-faceted fix, but the most crucial piece of the puzzle was ensuring that the PPU was properly set upon execution beginning. As I was leaving everything uninitalized, random garbage was floating around the VRAM/PPU I/O register section of RAM, therefore, things were mega screwed up.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PPU</span><span class="o">::</span><span class="n">PPU</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// Initialize registers to Post-Boot ROM defaults</span>
    <span class="n">lcdc</span> <span class="o">=</span> <span class="mh">0x91</span><span class="p">;</span> <span class="c1">// LCD enabled, Window enabled, BG window/tile Data @ $8000</span>
    <span class="n">stat</span> <span class="o">=</span> <span class="mh">0x85</span><span class="p">;</span>
    <span class="n">scy</span> <span class="o">=</span> <span class="mh">0x00</span><span class="p">;</span>
    <span class="n">scx</span> <span class="o">=</span> <span class="mh">0x00</span><span class="p">;</span>
    <span class="n">lyc</span> <span class="o">=</span> <span class="mh">0x00</span><span class="p">;</span>
    <span class="n">bgp</span> <span class="o">=</span> <span class="mh">0xFC</span><span class="p">;</span>
    
    <span class="n">current_ly</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">ppu_cycles</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">mode</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// Default - OAM search</span>

    <span class="c1">// Clear framebuffer</span>
    <span class="n">memset</span><span class="p">(</span><span class="n">framebuffer</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">framebuffer</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I intentionally skipped including the boot ROM or replicating it in the emulator for two main reasons: A) Nintendo is extremely litigious and would find any reason possible to sue an emulator developer as seen with the <a href="https://www.polygon.com/24090351/nintendo-2-4-million-yuzu-switch-emulator-settlement-lawsuit/">Switch emulator debacle</a>; and B) this was a much easier method generally.</p>

<p>After all of this work, I finally got this glorious sight to appear!</p>

<p><img class="no-resize" src="/img/blog/making-of-gamebyte/proper-copyright-blurry.png" alt="The GameByte emulator window. The graphics are finally properly rendering, showing Tetris's full copyright screen." /></p>

<p>I added a line to the PPU’s initialization to make the 2x scale I was doing much more crisp utilizing SDL once again:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SDL_SetTextureScaleMode</span><span class="p">(</span><span class="n">texture</span><span class="p">,</span> <span class="n">SDL_SCALEMODE_NEAREST</span><span class="p">);</span>
</code></pre></div></div>

<p><img class="no-resize" src="/img/blog/making-of-gamebyte/proper-copyright-proper.png" alt="The GameByte emulator window. The graphics are finally properly rendering, showing Tetris's full copyright screen, but this time, with a nice, crisp, pixel rendering instead of being blurry while upscaling." /></p>

<p>Much better!</p>

<h3 id="handling-input-and-getting-tetris-up-and-running">Handling Input (and getting Tetris up and running)</h3>
<p>Input handling, especially with SDL on my side, was honestly one of the simplest parts of the project. The Game Boy has a pretty simple scheme for reading the joypad’s current staste utilizing a specific I/O register called JOYP. The only weird thing about it really is that it is active-low, meaning that a byte set to OFF (or <code class="language-plaintext highlighter-rouge">0</code>) actually means the button is being <em>pressed</em>.</p>

<p>Other than that, all I had to do was add a handler to the Joypad class that handles the SDL event and converts keyboard presses into the proper bit layout to send to the JOYP register. You can see this implementation in full <a href="https://github.com/ByteOfMelon/GameByte/blob/master/src/core/ppu.cpp">here</a>.</p>

<p>Finally, after this (and implementing more opcodes I was missing), I was able to play Tetris in all its glory!</p>

<p><img src="/img/blog/making-of-gamebyte/tetris-in-game.png" alt="The GameByte emulator window. Inside the window, Tetris for Game Boy is fully playing, showing the proper Tetris layout, the score, level and lines cleared." /></p>

<p>This moment felt like such a massive accomplishment. Yes, this has already been done a million times. Yes, I didn’t have to do any of the underlying hardware research that the pioneers of Game Boy emulation would have had to do. However, this was something I had created myself that is still pretty complex, and that fact made me incredibly proud of myself.</p>

<h3 id="utilizing-test-roms-to-improve-accuracy">Utilizing Test ROMs to Improve Accuracy</h3>
<p>Since Tetris is a very basic game, the emulator did not need to be insanely accurate to play it, and it most certainly wasn’t after I implemented SDL’s open system file dialog functionality and began being able to use test ROMs properly. <a href="https://github.com/c-sp/game-boy-test-roms">c-sp’s Game Boy Test Roms repository</a> contains various awesome test ROMs that help emulator devs more easily uncover problems with their accuracy.</p>

<p>I utilized various different test ROMs to improve GameByte’s accuracy, but I’ll briefly discuss one that was helpful to fixing up my PPU - <a href="https://github.com/mattcurrie/dmg-acid2"><code class="language-plaintext highlighter-rouge">dmg-acid2</code> by Matt Currie</a>. This test ROM helps debug emulation problems specifically with PPU emulation, as specific timings and functionality gets quite complex.</p>

<p>When I opened this ROM on GameByte initially, it looked like this:</p>

<p><img class="no-resize" src="/img/blog/making-of-gamebyte/dmg-acid2-initial.png" alt="The GameByte emulator window. Inside the window, the dmg-acid2 test ROM is rendering, but wildly incorrectly. The exclamation mark in 'HELLO WORLD!' is missing, the eyes are not rendering properly, and the mouth and right side of the chin on the smiley face are extremely garbled." /></p>

<p>The README file for dmg-acid2 gives you a lot of common failure examples and the specific inaccuracy that the failure tests for. For example, the eye whites’ left half being garbled (as shown above) are a result of the Object to Background Priority bit (bit 7) not being accounted for properly, as it should utilize this bit to replace the white portion with a dark gray object.</p>

<p>I fixed up this and all the other errors and finally got it rendering perfectly:</p>

<p><img class="no-resize" src="/img/blog/making-of-gamebyte/dmg-acid2-fixed.png" alt="The GameByte emulator window. Inside the window, the dmg-acid2 test ROM is rendering correctly, with a proper pixelated and rounded smiling face with full eye whites, a nose, and a mouth." /></p>

<h3 id="fine-lets-add-one-mbc-type">Fine, let’s add one MBC type</h3>
<p>When I realized that Super Mario Land, also one of my favorite original Game Boy games, did not work on my emulator due to missing the implementation of MBC1 (the first and most basic type of MBC, luckily), I finally decided to tackle that specific one. I also decided to implement it fully, including the battery backed RAM portion, so that games like The Legend of Zelda: Link’s Awakening can properly save.</p>

<p>MBC1 is a memory bank controller that supports anywhere from 512 KiB to 2 MiB of ROM and anywhere from 8 KiB to 32 KiB of extra RAM. There are specific memory addresses that if written to, will interface with the MBC on the cartridge to toggle the current ROM and RAM banks in use.</p>

<p>The MMU handles almost all of the MBC1 logic, starting with the battery backed RAM functionality:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">bool</span> <span class="n">MMU</span><span class="o">::</span><span class="n">load_save</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">filename</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">std</span><span class="o">::</span><span class="n">ifstream</span> <span class="n">file</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ios</span><span class="o">::</span><span class="n">binary</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">file</span><span class="p">)</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>

    <span class="n">file</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">char</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">eram</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">eram</span><span class="p">));</span>
    <span class="n">file</span><span class="p">.</span><span class="n">close</span><span class="p">();</span>
    
    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"[MMU] Loaded battery backup RAM from "</span> <span class="o">&lt;&lt;</span> <span class="n">filename</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">bool</span> <span class="n">MMU</span><span class="o">::</span><span class="n">save_game</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">filename</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">std</span><span class="o">::</span><span class="n">ofstream</span> <span class="n">file</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ios</span><span class="o">::</span><span class="n">binary</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">file</span><span class="p">)</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>

    <span class="n">file</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="k">const</span> <span class="kt">char</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">eram</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">eram</span><span class="p">));</span>
    <span class="n">file</span><span class="p">.</span><span class="n">close</span><span class="p">();</span>

    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"[MMU] Saved battery backup RAM to "</span> <span class="o">&lt;&lt;</span> <span class="n">filename</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Then, there is the actual logic to read the ROM banking data as shown from this excerpt of <code class="language-plaintext highlighter-rouge">MMU::read_byte()</code>:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="n">address</span> <span class="o">&lt;=</span> <span class="mh">0x7FFF</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Cartridge ROM</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">rom</span> <span class="o">&amp;&amp;</span> <span class="n">rom</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">uint8_t</span> <span class="n">type</span> <span class="o">=</span> <span class="n">rom</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="n">ROM</span><span class="o">::</span><span class="n">OFFSET_TYPE</span><span class="p">];</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">type</span> <span class="o">==</span> <span class="n">ROM</span><span class="o">::</span><span class="n">ROM_MBC1</span> <span class="o">||</span> <span class="n">type</span> <span class="o">==</span> <span class="n">ROM</span><span class="o">::</span><span class="n">ROM_MBC1_RAM</span> <span class="o">||</span> <span class="n">type</span> <span class="o">==</span> <span class="n">ROM</span><span class="o">::</span><span class="n">ROM_MBC1_RAM_BATT</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">address</span> <span class="o">&lt;=</span> <span class="mh">0x3FFF</span><span class="p">)</span> <span class="p">{</span>
                <span class="c1">// Bank 0 unless mode 1 selected</span>
                <span class="kt">uint8_t</span> <span class="n">bank</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">mbc1_banking_mode</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">bank</span> <span class="o">=</span> <span class="p">(</span><span class="n">mbc1_ram_bank</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="kt">size_t</span> <span class="n">offset</span> <span class="o">=</span> <span class="p">(</span><span class="n">bank</span> <span class="o">*</span> <span class="mh">0x4000</span><span class="p">)</span> <span class="o">+</span> <span class="n">address</span><span class="p">;</span>
                <span class="k">return</span> <span class="n">rom</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="n">offset</span> <span class="o">%</span> <span class="n">rom</span><span class="o">-&gt;</span><span class="n">size</span><span class="p">];</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="c1">// Bank 1-7F (switchable)</span>
                <span class="kt">uint8_t</span> <span class="n">bank</span> <span class="o">=</span> <span class="n">mbc1_rom_bank</span><span class="p">;</span> <span class="c1">// Lower 5 bits</span>
                <span class="c1">// If Mode 0, include upper 2 bits from ram_bank</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">mbc1_banking_mode</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">bank</span> <span class="o">|=</span> <span class="p">(</span><span class="n">mbc1_ram_bank</span> <span class="o">&lt;&lt;</span> <span class="mi">5</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="kt">size_t</span> <span class="n">offset</span> <span class="o">=</span> <span class="p">(</span><span class="n">bank</span> <span class="o">*</span> <span class="mh">0x4000</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">address</span> <span class="o">-</span> <span class="mh">0x4000</span><span class="p">);</span>
                <span class="k">return</span> <span class="n">rom</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="n">offset</span> <span class="o">%</span> <span class="n">rom</span><span class="o">-&gt;</span><span class="n">size</span><span class="p">];</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="c1">// For non-MBC1 roms, just read directly</span>
        <span class="k">return</span> <span class="n">rom</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="n">address</span> <span class="o">%</span> <span class="n">rom</span><span class="o">-&gt;</span><span class="n">size</span><span class="p">];</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">cart</span><span class="p">[</span><span class="n">address</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<p>After implementing this and a couple of other parts, Super Mario Land (and therefore many other games) began working, and Link’s Awakening can even save and load properly as a real cartridge would!</p>

<p><img class="no-resize" src="/img/blog/making-of-gamebyte/links-awakening-save.png" alt="The GameByte emulator window. Inside the window, the game 'The Legend of Zelda - Link's Awakening' is playing, showing the PLAYER SELECT screen with a save file properly loaded." /></p>

<h2 id="whats-missing">What’s Missing</h2>
<p>There are three main components still missing from this emulator that keeps it from being a full-featured Game Boy emulator:</p>

<ul>
  <li>Only MBC1-based cartridges are supported. There are several other pieces of cartridge hardware that needs to be emulated to get all games to work properly on GameByte.</li>
  <li>There is absolutely no sound.</li>
  <li>The emulator is still not fully accurate, failing a few <a href="https://github.com/Gekkio/mooneye-test-suite/tree/main">Mooneye test suite</a> tests related to the PPU.</li>
</ul>

<h2 id="whats-next">What’s Next</h2>
<p>Well, in terms of GameByte, probably not much unless there’s a lot of demand for it for whatever reason. I made this project as a cool way to build some basic skills in low-level computing and C++, and developing GameByte to the point it is at has more than satisfied that goal at this point. Plus, it plays the Game Boy games I most care about, after all, even if they lack sound.</p>

<p>Plus, emulators like <a href="https://sameboy.github.io/">SameBoy</a> exist that can emulate Game Boy and even Game Boy Color games in a far more accurate manner, alongside full debugging features and a nice user interface.</p>

<p>However, my future adventures beyond GameByte are still to be determined. I’d love to continue working on lower-level projects, whether it be an emulator, or potentially homebrew development for a game console like the OG Xbox or the Wii. If you have any ideas for a project I could do, or you run a project like that that you’d be interested in having me participate in, please let me know on <a href="https://bsky.app/profile/byteofmelon.com">Bluesky</a>, <a href="https://discord.gg/bhVkwgCDtZ">Discord</a>, or <a href="https://x.com/byteofmelon">X/Twitter</a>.</p>

<p>I hope you enjoyed this nerdy deep dive into the story behind GameByte! Please let me know what you thought of this article on the above social links, and check out and/or star the GameByte repo <a href="https://github.com/ByteOfMelon/GameByte">here</a>!</p>]]></content><author><name>Michael Webb</name></author><category term="side-projects" /><summary type="html"><![CDATA[I went on a quest to learn more about low-level computing and using C++ to make a cross-platform Game Boy emulator. Here's my story and how I did it.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://byteofmelon.com/img/blog/making-of-gamebyte.webp" /><media:content medium="image" url="https://byteofmelon.com/img/blog/making-of-gamebyte.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">The Future of TheRandomMelon</title><link href="https://byteofmelon.com/blog/2025/the-future" rel="alternate" type="text/html" title="The Future of TheRandomMelon" /><published>2025-05-12T00:00:00+00:00</published><updated>2025-05-12T00:00:00+00:00</updated><id>https://byteofmelon.com/blog/2025/the-future</id><content type="html" xml:base="https://byteofmelon.com/blog/2025/the-future"><![CDATA[<h4 id="current-state-of-affairs">Current state of affairs</h4>
<p>The “TheRandomMelon” YouTube channel has been dead for quite a while now, only being slightly reanimated when I have a random video I want to do. For instance, the last video I uploaded to the channel was <a href="https://www.youtube.com/watch?v=F-7FxYBHoC8">#PersonaAlert 2</a>, on April 1, 2024, to commemorate a really awful video I did years ago called <a href="https://www.youtube.com/watch?v=6SBFmbvWCkA">#PersonaAlert</a>.</p>

<p><br />
The last actual video that was uploaded was even earlier than that, on September 14, 2023, titled <a href="https://www.youtube.com/watch?v=Wp3QauHGrVo">Madden 24 SUCKS! - Gameplay Proving How EXPLOITATIVE Ultimate Team Is (feat. SonicHack)</a>. While I like this video still, and find it pretty enjoyable to look back on, it’s not the kind of content I want my (main) channel to be. This is a fun thing to do as a second channel, though, but I’m getting ahead of myself…</p>

<h4 id="a-new-idea">A new idea</h4>
<p>Recently, I had a bit of an epiphany while on one of my half-hour long drives home from college. As you might imagine, I have plenty of time to think about things during these daily bits of driving, and the Nintendo Switch 2 Direct was just a few days ago at this point in time.</p>

<p><br />
Since there has been a lot of misinformation spreading on the Nintendo Switch 2, I wanted to make a video titled something like “In Defense of the Nintendo Switch 2 (and things I can’t defend!)”, as I wanted to offer my perspective also on things that are true that I don’t like about the new console. I’ve been giving this idea some thoughts on and off, alongside some other potential video topics, and I eventually came to the realization that I wanted a format change:</p>

<h4 id="the-format-change">The format change</h4>
<p>The best way I can immediately describe my concept for my new content format is to describe it as “a mix of Scott the Woz and Arlo”. I want to dive pretty deeply into various different technology and gaming concepts, and specific bits of tech and games as well, while having a comedic element strung throughout, and more on-camera appearances in a similar vein to my <a href="https://www.youtube.com/watch?v=MqLFMITB2s4&amp;t=55s">review of the M1 Mac mini</a>. I, of course, will be aiming to put my own spin on things so that it doesn’t just feel like I’m ripping off other YouTube channels.</p>

<p><br />
However, I quickly came to a realization after thinking up this new format idea:</p>

<h4 id="its-time-to-retire-the-therandommelon-brand">It’s time to retire the “TheRandomMelon” brand</h4>
<p>At this point, the “TheRandomMelon” brand has become pretty iconic and intrinsically linked to me. However, it is an outdated and antiquated username that I made for myself back in 2014, when I wasn’t even 10 yet (I wouldn’t turn 10 until November of that year). The “Random” part in this name comes from my even older channel, named “RandomStuff”, which was an even worse channel name. However, I also like the “melon” branding; I feel it is fairly unique and, with the right name and branding, could still work. Therefore:</p>

<h4 id="introducing-byte-of-melon">Introducing: Byte of Melon!</h4>
<p>After many iterations and discussions with various friends of mine, I have finally landed on a new channel name, <strong>Byte of Melon</strong>! Not only is Byte of Melon a pun (you can bite a melon, and a “byte” is a group of binary digits, making it a pun), but it also describes what I want to do on this channel quite perfectly.</p>

<h4 id="what-does-this-hold-for-the-future">What does this hold for the future?</h4>
<p>I will begin sunsetting the name “TheRandomMelon” in almost all contexts and areas. Some TRM pages will simply be archived and left as is, such as the YouTube channel, others will be used for some other purposes (like my X account already is used for), and some pages will be renamed to Byte of Melon, such as my Twitch channel and Discord server.</p>

<p><br />
Additionally, my second channel, TRM Extras, will simply be renamed to “Bit of Melon” (another pun, as 1 byte = 8 bits). Some of the oldest content on that channel will be unlisted, but still findable in a public playlist on the channel page.</p>

<p><br />
These renames and archivals will begin taking place soon after this post is published to this website. Speaking of this website, I have also purchased <a href="https://byteofmelon.com">byteofmelon.com</a>, however, it only redirects you to this website for now. Eventually, this site will be properly ported over to that domain as well.</p>

<h4 id="how-can-i-follow-the-new-byte-of-melon-stuff">How can I follow the new Byte of Melon stuff?</h4>
<p>Multiple accounts have already been set up for just that. I’d greatly appreciate everyone following these new accounts on these platforms!</p>

<ul>
  <li><a href="https://youtube.com/@byteofmelon">YouTube</a></li>
  <li><a href="https://x.com/byteofmelon">X/Twitter</a></li>
  <li><a href="https://instagram.com/byteofmelon">Instagram</a></li>
  <li><a href="https://tiktok.com/@byteofmelon">TikTok</a></li>
  <li><a href="https://bsky.app/profile/byteofmelon.com">Bluesky</a></li>
  <li><a href="https://discord.gg/eTBwhX9sQf">Discord</a></li>
  <li><a href="https://vanillo.tv/u/byteofmelon">Vanillo</a></li>
  <li><a href="https://byteofmelon.com">Website</a></li>
</ul>

<p>P.S. A logo will be announced and then placed on these accounts shortly - stay tuned!</p>]]></content><author><name>Michael Webb</name></author><category term="therandommelon" /><category term="trm" /><category term="update" /><summary type="html"><![CDATA[A major announcement regarding the future of TheRandomMelon, plus a new frontier.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://byteofmelon.com/img/blog/trm_banner.png" /><media:content medium="image" url="https://byteofmelon.com/img/blog/trm_banner.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>