<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aditya R</title>
    <description>The latest articles on DEV Community by Aditya R (@adityatr64).</description>
    <link>https://dev.to/adityatr64</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4011920%2F0dfe13a8-1e12-405c-8d9f-33348ce0d7d1.png</url>
      <title>DEV Community: Aditya R</title>
      <link>https://dev.to/adityatr64</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adityatr64"/>
    <language>en</language>
    <item>
      <title>CBA A from-scratch ARM7TDMI / Game Boy Advance emulator, written in C++</title>
      <dc:creator>Aditya R</dc:creator>
      <pubDate>Thu, 02 Jul 2026 11:43:39 +0000</pubDate>
      <link>https://dev.to/adityatr64/cba-a-from-scratch-arm7tdmi-game-boy-advance-emulator-written-in-c-42me</link>
      <guid>https://dev.to/adityatr64/cba-a-from-scratch-arm7tdmi-game-boy-advance-emulator-written-in-c-42me</guid>
      <description>&lt;h2&gt;
  
  
  &lt;code&gt;$ CBA&lt;/code&gt; : - an ARM7TDMI / GBA emulator, built from scratch
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; Aditya R&lt;/p&gt;

&lt;h3&gt;
  
  
  Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;From the ground up

&lt;ul&gt;
&lt;li&gt;What's an emulator, actually?&lt;/li&gt;
&lt;li&gt;Meet the ARM7TDMI&lt;/li&gt;
&lt;li&gt;The GBA's memory map&lt;/li&gt;
&lt;li&gt;ARM's party trick: conditional execution&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Genesis&lt;/li&gt;
&lt;li&gt;All about CBA

&lt;ul&gt;
&lt;li&gt;File structure&lt;/li&gt;
&lt;li&gt;Workflow of CBA&lt;/li&gt;
&lt;li&gt;Instructions supported&lt;/li&gt;
&lt;li&gt;Main features&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;How you can run CBA locally&lt;/li&gt;
&lt;li&gt;Where things stand&lt;/li&gt;
&lt;li&gt;Experience&lt;/li&gt;
&lt;li&gt;Fun stuff&lt;/li&gt;
&lt;li&gt;References&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What made me pick this project?
&lt;/h3&gt;

&lt;p&gt;A few months after wrapping up &lt;strong&gt;psh&lt;/strong&gt; a POSIX-ish shell I built with a team over a summer : - I wanted to go one level lower. A shell talks to the kernel. I wanted to know what's underneath the kernel: the chip itself, decoding raw bytes into "move this register into that one" and nothing else. An emulator felt like the natural next rabbit hole.&lt;/p&gt;

&lt;p&gt;I picked the &lt;strong&gt;ARM7TDMI&lt;/strong&gt; specifically because it's small enough for one person to reasonably understand end-to-end : - unlike, say, a modern x86 core  and because it's the CPU inside the &lt;strong&gt;Game Boy Advance&lt;/strong&gt;, which means every decision I make can be checked against real hardware, real games, and an extremely well-documented memory map. &lt;strong&gt;CBA&lt;/strong&gt; is the result: a from-scratch attempt at a hardware-accurate GBA emulator in C++, with no borrowed CPU cores or existing emulator code,  just the ARM architecture reference manual, GBATEK, and a lot of trial and error.&lt;/p&gt;

&lt;p&gt;Unlike psh, this one's solo no teammates, no mentors, just me and a very large PDF.&lt;/p&gt;

&lt;h2&gt;
  
  
  From the ground up
&lt;/h2&gt;

&lt;p&gt;Some background before diving in.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's an emulator, actually?
&lt;/h3&gt;

&lt;p&gt;An emulator is software that pretends to &lt;em&gt;be&lt;/em&gt; a different piece of hardware, closely enough that programs written for the real thing run on it unmodified. A GBA emulator needs to reproduce the GBA's CPU, its memory layout, its graphics and sound hardware, and its timers, closely enough that a real cartridge dump can't tell the difference. CBA is currently focused on the hardest and most foundational part of that: the CPU.&lt;/p&gt;

&lt;h3&gt;
  
  
  Meet the ARM7TDMI
&lt;/h3&gt;

&lt;p&gt;The ARM7TDMI is a 32-bit RISC processor from the early '90s that ended up everywhere  the original GBA, the Nintendo DS (as a co-processor), the classic iPod, and a long list of embedded devices. The name is basically a spec sheet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;T&lt;/strong&gt; : - Thumb, a compressed 16-bit instruction encoding traded for smaller code size&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D&lt;/strong&gt; : - Debug hardware support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;M&lt;/strong&gt; : - a hardware Multiplier&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I&lt;/strong&gt; : - an embedded In-circuit emulator interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an emulator author, the part that matters most is that it's really two instruction sets sharing one CPU: 32-bit ARM instructions and 16-bit Thumb instructions, with the ability to switch between them at runtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  The GBA's memory map
&lt;/h3&gt;

&lt;p&gt;Real hardware doesn't just have "RAM" : - it has several distinct memory regions, each with its own size, speed, and purpose, all mapped into one 32-bit address space:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0x00000000 – 0x00003FFF   BIOS              16 KB
0x02000000 – 0x0203FFFF   WRAM              256 KB  (on-board work RAM)
0x03000000 – 0x03007FFF   IWRAM             32 KB   (on-chip, fast work RAM)
0x04000000 – 0x040003FE   I/O registers
0x05000000 – 0x050003FF   Palette RAM       1 KB
0x06000000 – 0x06017FFF   VRAM              96 KB
0x07000000 – 0x070003FF   OAM               1 KB    (sprite attributes)
0x08000000 – 0x09FFFFFF   Game Pak ROM / FlashROM
0x0E000000 – 0x0E00FFFF   Game Pak SRAM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every load/store instruction has to figure out which of these regions an address falls into and route it accordingly, which is most of what CBA's &lt;code&gt;Memory&lt;/code&gt; class does.&lt;/p&gt;

&lt;h3&gt;
  
  
  ARM's party trick: conditional execution
&lt;/h3&gt;

&lt;p&gt;Most instruction sets only let &lt;em&gt;branches&lt;/em&gt; be conditional. ARM lets almost every instruction carry a 4-bit condition code, so "add these two registers, but only if the last comparison was equal" is a single instruction, no branch required. It's a neat trick for avoiding pipeline-stalling branches on simple if-statements and it means the first thing CBA's decoder has to do, before it even figures out &lt;em&gt;what&lt;/em&gt; instruction it's looking at, is check &lt;em&gt;whether&lt;/em&gt; to run it at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Genesis
&lt;/h2&gt;

&lt;p&gt;CBA started about as small as a project can start: a &lt;code&gt;Registers&lt;/code&gt; struct 16 general-purpose registers, plus the current and saved status registers (CPSR/SPSR) and a &lt;code&gt;Memory&lt;/code&gt; class that could barely do more than load a raw binary into a buffer. Then a fetch → decode → execute loop that, for a good while, decoded nothing at all, it just walked the ROM printing hex and hoping.&lt;/p&gt;

&lt;p&gt;Everything since has been filling in that decode step, one instruction category at a time, and checking the results against what the reference manual says should happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  All about CBA
&lt;/h2&gt;

&lt;h3&gt;
  
  
  File structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CBA
├── CMakeLists.txt
├── LICENSE
├── README.md
├── link.ld
├── makeGBA.sh
├── makeGBA.ps1
├── include
│   ├── arm.hpp
│   ├── cpu.hpp
│   └── memory.hpp
└── src
    ├── arm.cpp
    ├── cpu.cpp
    ├── emulator.cpp
    ├── kernel.s
    ├── memory.cpp
    └── thumb.cpp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Workflow of CBA
&lt;/h3&gt;

&lt;p&gt;On boot, CBA loads a raw &lt;code&gt;.gba&lt;/code&gt; binary straight into ROM, peeks at the very first instruction to guess whether it's ARM or Thumb code, and sets the CPU's starting mode accordingly. From there it's a straightforward loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fetch&lt;/strong&gt; : - read 4 bytes (ARM mode) or 2 bytes (Thumb mode) at the program counter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decode&lt;/strong&gt; : - for ARM, walk a table of &lt;code&gt;{mask, pattern, handler}&lt;/code&gt; entries and find the first one whose pattern matches the masked instruction bits; for Thumb, switch on the top opcode bits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execute&lt;/strong&gt; : - call the matching handler, which reads and writes registers and memory as needed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The mask-and-pattern dispatch table, instead of one giant &lt;code&gt;switch&lt;/code&gt;, mirrors how real ARM decoders are usually built ARM's 32-bit encoding is dense enough that a table lookup stays a lot more maintainable than a wall of nested conditionals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Instructions supported
&lt;/h3&gt;

&lt;p&gt;ARM-mode decoding currently covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BX&lt;/strong&gt; : - branch and exchange, the actual ARM ⟷ Thumb mode switch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MRS / MSR&lt;/strong&gt; : - read and write the status registers, both immediate and register forms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SWP&lt;/strong&gt; : - atomic register/memory swap&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MUL / MLA&lt;/strong&gt; : - multiply and multiply-accumulate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Halfword / signed data transfer&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Branch / Branch-with-Link&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load/Store&lt;/strong&gt; : - word and byte, base register + immediate offset&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SWI&lt;/strong&gt; : - software interrupt (today it logs the interrupt and halts, this is the eventual home for BIOS syscalls)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data processing&lt;/strong&gt; : - the ALU ops (MOV, ADD, CMP, and friends), including flag updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Multiply-long and block data transfer (LDM/STM) are stubbed but not wired up yet, and Thumb mode currently understands a handful of opcodes  immediate MOV, register ADD, conditional branch, literal LDR rather than the full set.&lt;/p&gt;

&lt;h3&gt;
  
  
  Main features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Full condition-code evaluation : - all 16 ARM conditions, on almost every instruction, not just branches&lt;/li&gt;
&lt;li&gt;A mask-and-pattern dispatch table for ARM decoding, instead of a wall of &lt;code&gt;if&lt;/code&gt;s&lt;/li&gt;
&lt;li&gt;Automatic ARM/Thumb mode detection on boot, by inspecting the first instruction in ROM&lt;/li&gt;
&lt;li&gt;A real, GBA-accurate memory map (BIOS / WRAM / IWRAM / I/O / Palette / VRAM / OAM / ROM / SRAM), not one flat byte array&lt;/li&gt;
&lt;li&gt;Hand-written ARM assembly test kernels, assembled and linked with a custom linker script, to check instruction behaviour against the reference manual&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In progress:&lt;/strong&gt; full Thumb mode, block data transfer, multiply-long, and a real BIOS/syscall layer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Up next:&lt;/strong&gt; an SDL front-end for real-time framebuffer output, and tightening the fetch-decode-execute loop toward cycle-accurate timing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How you can run CBA locally
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/adityatr64/CBA
&lt;span class="nb"&gt;cd &lt;/span&gt;CBA
cmake &lt;span class="nt"&gt;-B&lt;/span&gt; build
cmake &lt;span class="nt"&gt;--build&lt;/span&gt; build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To assemble a test ROM you'll need the &lt;code&gt;arm-none-eabi&lt;/code&gt; GCC toolchain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; bin
./makeGBA.sh      &lt;span class="c"&gt;# Linux / macOS&lt;/span&gt;
./makeGBA.ps1     &lt;span class="c"&gt;# Windows (PowerShell)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This assembles &lt;code&gt;src/kernel.s&lt;/code&gt; against &lt;code&gt;link.ld&lt;/code&gt;, drops a &lt;code&gt;bin/kernel.gba&lt;/code&gt;, and hex-dumps the first few lines so you can eyeball the header. Then run the emulator which, courtesy of &lt;code&gt;CMakeLists.txt&lt;/code&gt;, is quietly named &lt;code&gt;PlusBoy&lt;/code&gt; behind CBA's back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./PlusBoy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where things stand
&lt;/h2&gt;

&lt;p&gt;No formal benchmarks yet that's further down the roadmap, once there's a display to measure frame timing against. For now, correctness gets checked the old-fashioned way: a small hand-assembled ARM program runs through &lt;code&gt;MOV&lt;/code&gt;, &lt;code&gt;CMP&lt;/code&gt;, &lt;code&gt;ADDS&lt;/code&gt;, &lt;code&gt;MRS&lt;/code&gt;, and &lt;code&gt;MSR&lt;/code&gt;, and after &lt;em&gt;every single instruction&lt;/em&gt; the CPU dumps registers r0–r6 and all four condition flags to the terminal, so I can check them by hand against what the manual says should happen. It's not elegant. It's a lot of scrolling. It works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Experience
&lt;/h2&gt;

&lt;p&gt;Doing this solo after a team project like psh is a genuinely different kind of hard, there's no one to rubber-duck a bug with, and no code review to catch a forgotten sign-extension before it costs you an evening. Most of the bugs here aren't the segfault-and-gdb kind; they're the quieter, more annoying kind, where the code compiles fine, runs fine, and just quietly computes the wrong number a flag off by one bit, an offset that needed sign-extending and didn't, a condition checked backwards. The only real fix is going back to the datasheet, instruction by instruction, and being willing to be wrong a lot before being right.&lt;/p&gt;

&lt;p&gt;It's also given me a much deeper respect for how much a "simple" CPU actually does per instruction. Rereading the same page of the manual for the tenth time and finally noticing the one detail you'd missed is a very specific kind of satisfying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fun stuff
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The compiled binary is secretly named &lt;code&gt;PlusBoy&lt;/code&gt; &lt;code&gt;CMakeLists.txt&lt;/code&gt;'s project name has never matched the repo name, and at this point it's staying that way on purpose.&lt;/li&gt;
&lt;li&gt;The first program CBA ever ran "successfully" was 15 lines of hand-written ARM assembly that zeroes a register, compares it to zero, flips a couple of CPSR flags around, and then loops forever doing absolutely nothing. Watching those flags flip correctly in the terminal was, unreasonably, one of the best feelings of the project so far.&lt;/li&gt;
&lt;li&gt;Every single cycle currently prints a full register-and-flag dump to the terminal, so running anything longer than a few dozen instructions turns your terminal into a wall of hex. Future-me's problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://problemkaputt.de/gbatek.htm" rel="noopener noreferrer"&gt;GBATEK&lt;/a&gt; : - Martin Korth's GBA hardware reference; CBA's memory map is credited straight from here&lt;/li&gt;
&lt;li&gt;ARM7TDMI Technical Reference Manual&lt;/li&gt;
&lt;li&gt;ARM Architecture Reference Manual (ARMv4T)&lt;/li&gt;
&lt;li&gt;Stack Overflow&lt;/li&gt;
&lt;li&gt;the gbadev community/wiki&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built solo, with reference material pulled from the sources above. Source: &lt;a href="https://github.com/adityatr64/CBA" rel="noopener noreferrer"&gt;github.com/adityatr64/CBA&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>cpp</category>
      <category>gamedev</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
