<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Gba on A blog by Dennis Hilhorst</title><link>https://blog.dennishilhorst.nl/categories/gba/</link><description>Recent content in Gba on A blog by Dennis Hilhorst</description><generator>Hugo -- gohugo.io</generator><language>nl-nl</language><lastBuildDate>Sat, 23 Mar 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.dennishilhorst.nl/categories/gba/index.xml" rel="self" type="application/rss+xml"/><item><title>GBA Audio Data</title><link>https://blog.dennishilhorst.nl/post/gba-audio-data/</link><pubDate>Sat, 23 Mar 2024 00:00:00 +0000</pubDate><guid>https://blog.dennishilhorst.nl/post/gba-audio-data/</guid><description>&lt;img src="https://blog.dennishilhorst.nl/post/gba-audio-data/images/extractor-zelda-8.webp" alt="Featured image of post GBA Audio Data" /&gt;&lt;p&gt;[latexpage]&lt;/p&gt;
&lt;p&gt;About a year ago, I was working on &lt;a class="link" href="https://github.com/densinh/pret-port" target="_blank" rel="noopener"
 &gt;porting Pokemon Ruby&lt;/a&gt; from the GBA to the PC (including some &lt;a class="link" href="https://github.com/Kurausukun/pokeemerald/blob/54e55cf040e8ef4b11632a0af14b9f512827d92a/src/sound_mixer.c" target="_blank" rel="noopener"
 &gt;extra m4a engine decompilation work by Karaukasan&lt;/a&gt;, most notably the &lt;a class="link" href="https://github.com/Kurausukun/pokeemerald/blob/pc_port/src/music_player.c" target="_blank" rel="noopener"
 &gt;music_player.c&lt;/a&gt; file). After getting it working, with emulated (threaded) graphics and audio, I realized the game was not running any faster than &lt;a class="link" href="https://github.com/DenSinH/DSHBA" target="_blank" rel="noopener"
 &gt;my GBA emulator DSHBA&lt;/a&gt;, which I found surprising, considering a native game should probably run faster (right?). After profiling the ported game, I saw that audio emulation was taking up most of the CPU resources (by far!). I figured that, considering I have all the decompiled code available, there should be a way to run the audio engine natively (and threaded!) as well.&lt;/p&gt;
&lt;p&gt;This is why I decided to research into the m4a engine, and, particularly, how the GBA stores / plays audio. I mean, the mp3 songs you might listen to take up multiple megabytes even for one or two minutes of play time, while a GBA ROM can only store 2MB of data &lt;em&gt;total&lt;/em&gt;. Of course, a highly efficient way of storing song data was used, similar to the MIDI format. Programs to export sound already exist (like &lt;a class="link" href="https://github.com/ipatix/agbplay" target="_blank" rel="noopener"
 &gt;agbplay&lt;/a&gt; or the &lt;a class="link" href="https://github.com/zeldaret/tmc/tree/master/tools/src/agb2mid" target="_blank" rel="noopener"
 &gt;agb2mid&lt;/a&gt; tool in the zelda tmc decomp, or many pret decompilation projects). I decided to write &lt;a class="link" href="https://github.com/DenSinH/gba-audio-extractor" target="_blank" rel="noopener"
 &gt;my own GBA audio extractor&lt;/a&gt; for the learning experience.&lt;/p&gt;
&lt;h2 id="the-hardware"&gt;The Hardware
&lt;/h2&gt;&lt;p&gt;As an emulator developer, I am aware of the GBA audio hardware to a certain extent, and know how the GBA is supposed to play audio when IO registers are written to (see &lt;a class="link" href="https://problemkaputt.de/gbatek-gba-sound-controller.htm" target="_blank" rel="noopener"
 &gt;GBATek&lt;/a&gt; or even the &lt;a class="link" href="https://gbdev.io/pandocs/Audio.html" target="_blank" rel="noopener"
 &gt;GB Pandocs&lt;/a&gt;, considering the GBA uses mostly the same audio registers, besides direct audio). The GBA has 4 audio channels: 2 square wave channels, a wave channel with 32 (or 64) programmable samples, a noise channel and direct 8 bit audio. Games write to IO registers to control the channels, but how does the audio engine do this to make a song?&lt;/p&gt;
&lt;h2 id="gba-sound-format"&gt;GBA Sound Format
&lt;/h2&gt;&lt;p&gt;A lot of work has been done (see for example &lt;a class="link" href="https://github.com/Kurausukun/pokeemerald/blob/pc_port/src/m4a.c" target="_blank" rel="noopener"
 &gt;an attempted pc port of pokemon emerald&lt;/a&gt; or &lt;a class="link" href="https://github.com/ipatix/agbplay" target="_blank" rel="noopener"
 &gt;agbplay&lt;/a&gt;), but documentation is sparse and incomplete. I will be using code from the &lt;a class="link" href="https://github.com/zeldaret/tmc" target="_blank" rel="noopener"
 &gt;Zelda: the Minish Cap decompilation project&lt;/a&gt;, as well as my own code to explain how everything works. The process works a bit like the following. There are 3 main m4a engine control functions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m4aSoundInit&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// initializes the engine and the hardware, called on program startup
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m4aSoundMain&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// mixes audio samples and sends them to the direct audio controller, called regularly (every VBlank or HBlank?)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m4aSoundVSync&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// triggers an audio DMA to transfer data from a buffer to the audio channels
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then there are some more controller functions, starting or stopping songs or sounds, controlling the master volume or other effects.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m4aSongNumStart&lt;/span&gt;(u16 n);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m4aSongNumStartOrContinue&lt;/span&gt;(u16 n);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m4aSongNumStop&lt;/span&gt;(u16 n);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m4aMPlayAllStop&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m4aMPlayImmInit&lt;/span&gt;(MusicPlayerInfo&lt;span style="color:#f92672"&gt;*&lt;/span&gt; mplayInfo);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m4aMPlayTempoControl&lt;/span&gt;(MusicPlayerInfo&lt;span style="color:#f92672"&gt;*&lt;/span&gt; mplayInfo, u16 tempo);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m4aMPlayVolumeControl&lt;/span&gt;(MusicPlayerInfo&lt;span style="color:#f92672"&gt;*&lt;/span&gt; mplayInfo, u16 trackBits, u16 volume);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m4aSoundVSyncOn&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m4aSoundVSyncOff&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These controller functions update &lt;code&gt;MusicPlayer&lt;/code&gt; structs, which contain the state of a single song / sound that may be played. Some comments have been added to certain fields, which will be clear later.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; MusicPlayerInfo {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; SongHeader&lt;span style="color:#f92672"&gt;*&lt;/span&gt; songHeader; &lt;span style="color:#75715e"&gt;// songheader pointing to tracks / voice group
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u32 status;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 trackCount; &lt;span style="color:#75715e"&gt;// number of tracks
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; MusicPlayerTrack&lt;span style="color:#f92672"&gt;*&lt;/span&gt; tracks; &lt;span style="color:#75715e"&gt;// track list
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ToneData&lt;span style="color:#f92672"&gt;*&lt;/span&gt; tone; &lt;span style="color:#75715e"&gt;// voice
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u32 ident; &lt;span style="color:#75715e"&gt;// lock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; MPlayMainFunc func;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u32&lt;span style="color:#f92672"&gt;*&lt;/span&gt; intp;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; MusicPlayerTrack {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 flags; &lt;span style="color:#75715e"&gt;// state
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 wait; &lt;span style="color:#75715e"&gt;// counter
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 patternLevel; &lt;span style="color:#75715e"&gt;// nested pattern depth
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 repN;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 gateTime; &lt;span style="color:#75715e"&gt;// time to play a note for
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 key; &lt;span style="color:#75715e"&gt;// key of the current playing note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 velocity; &lt;span style="color:#75715e"&gt;// velocity of the current playing note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 runningStatus; &lt;span style="color:#75715e"&gt;// last repeatable event (cmd &amp;gt;= 0xbd)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// keyShift, pitch, bend, volume, pan, mod, lfo, echo, etc. stuff
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SoundChannel&lt;span style="color:#f92672"&gt;*&lt;/span&gt; chan; &lt;span style="color:#75715e"&gt;// soundchannel to play sound on
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ToneData tone; &lt;span style="color:#75715e"&gt;// voice to play sound with
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8&lt;span style="color:#f92672"&gt;*&lt;/span&gt; cmdPtr; &lt;span style="color:#75715e"&gt;// current position in command stream
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8&lt;span style="color:#f92672"&gt;*&lt;/span&gt; patternStack[&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;]; &lt;span style="color:#75715e"&gt;// PEND return addresses
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;typedef&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; MusicPlayer {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; MusicPlayerInfo&lt;span style="color:#f92672"&gt;*&lt;/span&gt; info;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; MusicPlayerTrack&lt;span style="color:#f92672"&gt;*&lt;/span&gt; tracks;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 nTracks;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u16 unk_A;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;} MusicPlayer;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Most notably, the music player holds info on the song (a song consists of multiple &lt;em&gt;tracks&lt;/em&gt; with audio data, and a pointer to an array of &lt;code&gt;ToneData&lt;/code&gt; structs, which is a table containing sound samples which the tracks may use, which we refer to as &lt;em&gt;voices&lt;/em&gt;). Then there is some more random struct fields which are used to track the status of certain audio effects like panning, echo, pitch shifting etc.&lt;/p&gt;
&lt;p&gt;This already gives some insight into how the data is stored: as &lt;em&gt;songs&lt;/em&gt;, consisting of &lt;em&gt;tracks&lt;/em&gt;, using certain &lt;em&gt;voices&lt;/em&gt; to play tones with different sounds. A game usually contains a table of songs, called the &lt;em&gt;songtable&lt;/em&gt; (commonly named &lt;code&gt;gSongTable&lt;/code&gt; in decompilation projects. Songs contain an ID for a music player, which is then triggered to stop playing the song it was previously playing, and start playing the requested song. This means that the m4a engine can play only a limited amount of songs (and sound effects!) at the same time (commonly 32, so quite a few).&lt;/p&gt;
&lt;p&gt;Songs consist of &lt;em&gt;tracks&lt;/em&gt; and a pointer to a &lt;em&gt;voice group&lt;/em&gt;. The tracks contain the parts of the song that is played, like in a real orchestra! A track may contain music for multiple different instruments, but only one instrument (or &lt;em&gt;voice!&lt;/em&gt;) is playing at once. Though a game may have many &lt;em&gt;voice groups&lt;/em&gt; stored in it&amp;rsquo;s data, only one can be selected per song, and only one voice within this group can be active at a time, per track. This means that if a song contains 7 tracks, we may hear 7 different instruments at the same time! A &lt;em&gt;voice group&lt;/em&gt; is an array of &lt;em&gt;voices&lt;/em&gt;. A &lt;em&gt;voice&lt;/em&gt; contains data that describes the sound of an instrument. This may be in the form of CGB audio channel controls, wave data or a direct sound sample.&lt;/p&gt;
&lt;p&gt;So how is all the data for a song stored? Well, a track consists of a stream of &lt;em&gt;events&lt;/em&gt;, which may be either a &lt;em&gt;note&lt;/em&gt;, a &lt;em&gt;controller event&lt;/em&gt;, or a &lt;em&gt;wait event&lt;/em&gt;. Music players have an internal, regularly ticked counter, ticking down until the next events should be played, or until the counter is incremented again by a &lt;em&gt;wait&lt;/em&gt; event. Let us briefly look at my own implementation for the data in a song. The data defined as below. Note that this does &lt;em&gt;not&lt;/em&gt; contain any information for actually &lt;em&gt;playing&lt;/em&gt; a song, we will look into that later.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Event&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;enum&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Type&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Meta &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Goto, Tempo, VoiceChange, Controller, Note, Fine,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Type type;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; i32 tick;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;union&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Track&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Event&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; events{};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u32 length;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ..
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Song&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 reverb;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; do_reverb;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; VoiceGroup voicegroup;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Track&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; tracks;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The songtable (&lt;code&gt;gSongTable&lt;/code&gt;) contains pointers to &lt;em&gt;song headers&lt;/em&gt;, which look like this&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;typedef&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; SongHeader {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 trackCount;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 blockCount;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 priority;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 reverb;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ToneData&lt;span style="color:#f92672"&gt;*&lt;/span&gt; tone;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8&lt;span style="color:#f92672"&gt;*&lt;/span&gt; part[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;} SongHeader;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Where the &lt;code&gt;ToneData&lt;/code&gt; points to the voice group, and the &lt;code&gt;part&lt;/code&gt; points to the tracks. So we extract a song like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#define HEADER_NUM_TRACKS_OFFSET 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#define HEADER_REVERB_OFFSET 3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#define HEADER_VOICEGROUP_PTR_OFFSET 4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#define HEADER_TRACK_PTR_OFFSET 8
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Song Song&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Extract(&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; u8&lt;span style="color:#f92672"&gt;*&lt;/span&gt; header) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; u8 num_tracks &lt;span style="color:#f92672"&gt;=&lt;/span&gt; header[HEADER_NUM_TRACKS_OFFSET];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; u8 reverb &lt;span style="color:#f92672"&gt;=&lt;/span&gt; header[HEADER_REVERB_OFFSET];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; u32 voicegroup_ptr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; util&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Read&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;u32&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;header[HEADER_VOICEGROUP_PTR_OFFSET]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Song song &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Song(num_tracks, reverb, util&lt;span style="color:#f92672"&gt;::&lt;/span&gt;GetPointer(voicegroup_ptr));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; num_tracks; i&lt;span style="color:#f92672"&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt; track_ptr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; util&lt;span style="color:#f92672"&gt;::&lt;/span&gt;GetPointer(util&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Read&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;u32&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;header[HEADER_TRACK_PTR_OFFSET &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt;(u32) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; i]));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; song.AddTrack(track_ptr);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; song;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="events"&gt;Events
&lt;/h3&gt;&lt;p&gt;A track is then simply a long stream of events (or commands). These events are encoded in one or multiple bytes. A byte with a value of at least &lt;code&gt;0x80&lt;/code&gt; indicates a &lt;em&gt;command&lt;/em&gt;, and a byte with a value lower than &lt;code&gt;0x80&lt;/code&gt; is a data byte, which is either a parameter of a previous command byte, or indicates a &lt;em&gt;repeat command&lt;/em&gt;, repeating the previous repeatable command, with that byte as first parameter. Events happen at certain &lt;em&gt;ticks&lt;/em&gt;, forming a kind of &amp;ldquo;timeline&amp;rdquo;, like you can see in the preview image of this post (of &lt;a class="link" href="https://github.com/DenSinH/gba-audio-extractor" target="_blank" rel="noopener"
 &gt;my gba audio extractor&lt;/a&gt;). The duration of one &lt;em&gt;tick&lt;/em&gt; depends on the &lt;em&gt;tempo&lt;/em&gt; of the song, which should be indicated by one of the first events of the track, happening at tick 0. During the song, the tempo of a track might change. Using the &lt;a class="link" href="https://loveemu.github.io/vgmdocs/Summary_of_GBA_Standard_Sound_Driver_MusicPlayer2000.html" target="_blank" rel="noopener"
 &gt;very useful table found here&lt;/a&gt;, the &lt;a class="link" href="https://github.com/zeldaret/tmc/tree/master/tools/src/agb2mid" target="_blank" rel="noopener"
 &gt;agb2mid tool&lt;/a&gt;, the &lt;a class="link" href="https://github.com/Kurausukun/pokeemerald/blob/pc_port/src/music_player.c" target="_blank" rel="noopener"
 &gt;already decompiled audio engine by Kurausukun&lt;/a&gt; and &lt;a class="link" href="https://github.com/pret/pokeruby/blob/master/sound/songs/mus_vs_kyogre_groudon.s" target="_blank" rel="noopener"
 &gt;extracted audio from pokemon ruby&lt;/a&gt;, we can formulate a table of audio commands, their parameters and their meaning. Some commands are missing, but I have not encountered these in the ROMS that I have tested, nor are they in agb2mid.&lt;/p&gt;
&lt;figure&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Command&lt;/th&gt;
					&lt;th&gt;Symbol&lt;/th&gt;
					&lt;th&gt;Parameters&lt;/th&gt;
					&lt;th&gt;Repeatable&lt;/th&gt;
					&lt;th&gt;Description&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0x00-0x7F&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;n/a&lt;/td&gt;
					&lt;td&gt;n/a&lt;/td&gt;
					&lt;td&gt;Yes&lt;/td&gt;
					&lt;td&gt;Data byte OR repeat command, repeating the previous repeatable command using the command byte as first argument&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0x80-0xB0&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;W00-W96&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;No&lt;/td&gt;
					&lt;td&gt;Wait&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xB1&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;FINE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;No&lt;/td&gt;
					&lt;td&gt;Track end&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xB2&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;GOTO&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u32 dest&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;No&lt;/td&gt;
					&lt;td&gt;Jump to specified address (absolute hardware address like 0x8XXXXXX)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xB3&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;PATT&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u32 dest&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;No&lt;/td&gt;
					&lt;td&gt;Pattern start (subroutine jump, cannot be nested)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xB4&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;PEND&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;No&lt;/td&gt;
					&lt;td&gt;Pattern end&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xBA&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;PRIO&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u8&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;No&lt;/td&gt;
					&lt;td&gt;Priority of track (0-255, the higher the value, the higher priority)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xBB&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;TEMPO&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u8&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;No&lt;/td&gt;
					&lt;td&gt;Tempo, specifies half the value of BPM&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xBC&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;KEYSH&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;i8&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;No&lt;/td&gt;
					&lt;td&gt;Transpose (-128-127, per-track transpose)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xBD&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;VOICE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u8&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Yes&lt;/td&gt;
					&lt;td&gt;Instrument&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xBE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;VOL&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u8&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Yes&lt;/td&gt;
					&lt;td&gt;Volume&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xBF&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;PAN&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u8&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Yes&lt;/td&gt;
					&lt;td&gt;Pan (0: left, 64: center, 127: right)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xC0&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;BEND&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u8&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Yes&lt;/td&gt;
					&lt;td&gt;Pitch bend (0-127, center is 64)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xC1&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;BENDR&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u8&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Yes&lt;/td&gt;
					&lt;td&gt;Pitch bend range (specified in semitones, default is 2)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xC2&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;LFOS&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u8&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;No&lt;/td&gt;
					&lt;td&gt;LFO speed (higher values are faster, actual speed fluctuates with tempo)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xC3&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;LFODL&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u8&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;No&lt;/td&gt;
					&lt;td&gt;LFO delay (specified in ticks)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xC4&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;MOD&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u8&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Yes&lt;/td&gt;
					&lt;td&gt;LFO depth&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xC5&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;MODT&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u8&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;No&lt;/td&gt;
					&lt;td&gt;LFO type (0: pitch (default), 1: volume, 2: pan)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xC8&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;TUNE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u8&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Yes&lt;/td&gt;
					&lt;td&gt;Micro-tuning (0: one semitone lower, 64: normal: 127: one semitone higher）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xCD&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;XCMD&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;u8 cmd [, u8 param]&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Yes&lt;/td&gt;
					&lt;td&gt;Extended commands&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xCE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;EOT&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;[u8 key]&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Yes&lt;/td&gt;
					&lt;td&gt;Tie end / Note off&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xCF&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;TIE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;[u8 key [, u8 velo]]&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Yes&lt;/td&gt;
					&lt;td&gt;Tie / Note on (note length is undeterminated until the &lt;code&gt;0xCE&lt;/code&gt; command appears)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;0xD0-0xFF&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;N01-N96&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;[u8 key [, u8 velo [, u8 length]]]&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Yes&lt;/td&gt;
					&lt;td&gt;Note (the third argument is for fine adjustment of gate time, specified in ticks)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption&gt;
&lt;p&gt;Command table adapted from &lt;a class="link" href="https://loveemu.github.io/vgmdocs/Summary_of_GBA_Standard_Sound_Driver_MusicPlayer2000.html" target="_blank" rel="noopener"
 &gt;loveemu.github.io&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;For the full code used to parse a track, see &lt;a class="link" href="https://github.com/DenSinH/gba-audio-extractor/blob/master/src/extractor/track.cpp" target="_blank" rel="noopener"
 &gt;track.cpp&lt;/a&gt;. The very basic idea is that we keep a counter for the tick that an event is on, which we increment on wait commands (&lt;code&gt;W00-W96&lt;/code&gt;), and store in any other command. Note that a &lt;em&gt;note&lt;/em&gt; command does &lt;em&gt;not&lt;/em&gt; increment the current tick. In order to play two consecutive 32-tick length notes, one needs commands&lt;/p&gt;
&lt;p&gt;&lt;code&gt;N32, W32, N32&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The available lengths for both wait and note commands are the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;array&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;i32, &lt;span style="color:#ae81ff"&gt;49&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; LengthTable &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;8&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;9&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;12&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;14&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;15&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;16&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;18&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;19&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;21&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;22&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;23&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;24&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;28&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;32&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;36&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;40&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;42&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;44&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;48&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;52&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;54&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;56&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;60&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;64&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;66&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;68&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;72&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;76&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;78&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;84&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;88&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;90&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;92&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;96&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="wait-commands-and-timing"&gt;Wait commands and timing
&lt;/h3&gt;&lt;p&gt;As mentioned, event timing is determined in terms of &lt;em&gt;ticks&lt;/em&gt; and wait commands which dictate the delay for the following events. How long a &lt;em&gt;tick&lt;/em&gt; takes is determined by the &lt;code&gt;TEMPO&lt;/code&gt; command. The parameter passed to the command indicates &lt;em&gt;half of the BPM&lt;/em&gt;. One &lt;em&gt;beat&lt;/em&gt; in this case is 24 ticks, so a 4-beat &lt;em&gt;measure&lt;/em&gt; in a song is made up of 96 ticks, allowing for an easy way of defining beat divisions (eighth = 12 ticks, sixteenth = 6 ticks, 32th notes = 3 ticks, triplets = 8 ticks etc.). We compute the &lt;em&gt;real time per tick&lt;/em&gt; as&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;inline&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TimePerTick&lt;/span&gt;(u32 bpm &lt;span style="color:#75715e"&gt;/* == 2 * the TEMPO command parameter */&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 96 ticks per 4 beats
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;60.0&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; bpm) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;24.0&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// seconds
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Indeed, this means that a bpm of 150 (so a value of 75 as parameter for &lt;code&gt;TEMPO&lt;/code&gt;) has precisely 1 tick per frame.&lt;/p&gt;
&lt;h3 id="notes"&gt;Notes
&lt;/h3&gt;&lt;p&gt;Besides wait commands, which dictate the timing of all other events, the simplest events are &lt;em&gt;notes&lt;/em&gt;. The command byte dictates the length of the note, as one of the options from the array above. Then there are some (optional) parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;u8 key&lt;/code&gt;. This parameter indicates the key of the note. If this parameter is not passed, the key of the previous note in the track is used. Of course, this means that the very first note of a track MUST indicate a key. The key value is the midi key (yes, the midi key from the well-known MidiKeyToFreq BIOS routine used to dump the GBA BIOS) that indicates the frequency of a note. How this key is used to play a sound at the correct frequency depends on the type of voice that is currently active on the track. The note corresponding to the MIDI key value can be read in the following table:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;array&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;char&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;128&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; NoteNames &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;CnM2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;CsM2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;DnM2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;DsM2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;EnM2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;FnM2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;FsM2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;GnM2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;GsM2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;AnM2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;AsM2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;BnM2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;CnM&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;CsM&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;DnM&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;DsM&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;EnM&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;FnM&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;FsM&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;GnM&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;GsM&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;AnM&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;AsM&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;BnM&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Cn0&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Cs0&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Dn0&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Ds0&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;En0&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fn0&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fs0&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gn0&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gs0&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;An0&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;As0&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bn0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Cn1&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Cs1&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Dn1&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Ds1&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;En1&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fn1&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fs1&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gn1&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gs1&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;An1&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;As1&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bn1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Cn2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Cs2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Dn2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Ds2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;En2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fn2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fs2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gn2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gs2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;An2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;As2&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bn2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Cn3&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Cs3&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Dn3&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Ds3&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;En3&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fn3&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fs3&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gn3&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gs3&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;An3&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;As3&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bn3&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Cn4&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Cs4&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Dn4&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Ds4&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;En4&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fn4&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fs4&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gn4&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gs4&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;An4&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;As4&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bn4&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Cn5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Cs5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Dn5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Ds5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;En5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fn5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fs5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gn5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gs5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;An5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;As5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bn5&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Cn6&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Cs6&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Dn6&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Ds6&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;En6&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fn6&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fs6&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gn6&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gs6&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;An6&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;As6&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bn6&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Cn7&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Cs7&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Dn7&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Ds7&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;En7&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fn7&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fs7&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gn7&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gs7&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;An7&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;As7&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bn7&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Cn8&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Cs8&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Dn8&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Ds8&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;En8&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fn8&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Fs8&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Gn8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;u8 velocity&lt;/code&gt;. This parameter indicates the &lt;em&gt;velocity&lt;/em&gt; of a note. In this case, velocity does not indicate any real speed-related quantity, but rather &amp;ldquo;how hard the piano key was pressed&amp;rdquo;. That is to say, how loud the note is. The amplitude of the sound wave is linearly related to the velocity parameter. If this parameter is not passed, the velocity of the previous note in the track is used. Again, the first note in the track MUST specify a velocity. This parameter can only be passed if the &lt;code&gt;key&lt;/code&gt; parameter is also passed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;u8 length&lt;/code&gt;. This parameter can be used to increment the length of the note to get more exotic note lengths, other than that from the command byte, without using a &lt;code&gt;TIE&lt;/code&gt; command.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ties"&gt;Ties
&lt;/h3&gt;&lt;p&gt;There is one other way to play a note, besides using a note command with a predefined length: with the &lt;code&gt;TIE&lt;/code&gt;/&lt;code&gt;EOT&lt;/code&gt; commands. The &lt;code&gt;TIE&lt;/code&gt; command starts a note at the given key, which is ended on an &lt;code&gt;EOT&lt;/code&gt; command with the same key. This means that, within a track, multiple ties may be playing at once, but only one tie can be playing per note (after all, what would it mean for two ties to be playing at the same note&amp;hellip;). The parameters in the &lt;code&gt;TIE&lt;/code&gt; command mean the same thing as for the note command, where the default value is determined by the previously played note (&lt;em&gt;this includes tie commands, as does it for the note and EOT commands&lt;/em&gt;). The &lt;code&gt;TIE&lt;/code&gt; command is commonly used for playing long notes, taking more than 96 ticks.&lt;/p&gt;
&lt;h3 id="voices"&gt;Voices
&lt;/h3&gt;&lt;p&gt;We know about notes, but how do we know how the note should sound? Should it be a piano or a guitar? A square wave or a sine wave? This is dictated by the voice that is active in the track. These can be controlled by the &lt;code&gt;VOICE&lt;/code&gt; command, providing an index into the voice group for the voice that is used. There is only one voice group per song, though a voice group may be used by multiple songs. There are a few different types of voices, but all of them have some things in common: volume envelope. This term may be familiar to those who have emulated a GBA or GB before, though the actual idea may not be very clear.&lt;/p&gt;
&lt;h4 id="volume-envelope"&gt;Volume envelope
&lt;/h4&gt;&lt;p&gt;A brief intermezzo: the &lt;em&gt;volume envelope&lt;/em&gt; in a voice is a way of simulating an instrument by modulating the volume of the waveform. Basically, by defining &lt;em&gt;attack&lt;/em&gt;, &lt;em&gt;decay&lt;/em&gt;, &lt;em&gt;sustain&lt;/em&gt; and &lt;em&gt;release&lt;/em&gt; parameters, we can create the following volume progression over time:&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img class="gallery-image" data-flex-basis="341px" data-flex-grow="142" height="679" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://blog.dennishilhorst.nl/post/gba-audio-data/images/image.png" srcset="https://blog.dennishilhorst.nl/post/gba-audio-data/images/image_hu_6cecc363c21cfe84.png 800w, https://blog.dennishilhorst.nl/post/gba-audio-data/images/image.png 967w" width="967"&gt;&lt;/p&gt;
&lt;figcaption&gt;
&lt;p&gt;&lt;a class="link" href="https://whrstudios.com/properties-of-sound-envelopes/" target="_blank" rel="noopener"
 &gt;source&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Basically, we can simulate the volume progression of a piano key being struck (or a string being struck, or a voice singing a tone, or a brass instrument playing a tone&amp;hellip;). When a brass player starts playing a tone, the volume rises quickly, then decays until it hits the volume that the tone is sustained at for the duration of the note, and it might echo or decay very quickly after it has been released. The source of the image gives another very good example: the sound of a person yawning has a very long attack time. The sound volume rises pretty slowly, even when the action of the yawning has begun. Compare this to someone hitting a snare drum: the sound explodes immediately, so the attack time is very short!&lt;/p&gt;
&lt;p&gt;This gives rise to the following data in my generic &lt;code&gt;Voice&lt;/code&gt; struct:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;struct Voice {
 enum Type : u8 {

 ...
 };

 u8 type; // value depends on subtype
 u8 base;
 u8 pan;
 u8 attack;
 u8 decay;
 u8 sustain;
 u8 release;

protected:
 ...

 double attack_time;
 double decay_time;
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The public &lt;code&gt;u8&lt;/code&gt; type parameters are all in the data in the ROM, while the last 2 &lt;code&gt;protected&lt;/code&gt; values are computed once on instantiation. For the attack, decay, sustain and release parameters, the values mean the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Attack:&lt;/strong&gt; The attack volume is incremented by the &lt;code&gt;attack&lt;/code&gt; value every frame, giving us a linear attack increment lasting &lt;code&gt;attack_time = FrameTime * (255.0 / attack)&lt;/code&gt;, meaning that an attack value of 255 (which is very common) means we reach maximum volume in a single frame.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Decay:&lt;/strong&gt; The decay is exponential, multiplying the volume by &lt;code&gt;decay / 255.0&lt;/code&gt; every frame until the volume reaches the sustain level. A value of 0 means we reach the sustain volume immediately.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sustain:&lt;/strong&gt; This parameter indicates a level, sustaining the volume at level &lt;code&gt;sustain / 255.0&lt;/code&gt; until the note ends.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; This parameter again defines an exponential decay, reducing the volume by a factor of &lt;code&gt;release / 255.0&lt;/code&gt; every frame after the note has ended.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The other parameters in the &lt;code&gt;Voice&lt;/code&gt; struct will be explained later, but they are related to audio effects. Let us now go over the different types of voices.&lt;/p&gt;
&lt;h4 id="types-of-voices"&gt;Types of voices
&lt;/h4&gt;&lt;p&gt;There are a couple different types of voices, all encoded differently. Every voice starts with a single byte indicating the type of voice. Every voice consists of the following (see for example &lt;a class="link" href="https://github.com/pret/pokeemerald/blob/f19747d6cc340cad5689e526ec29af30bc89b82e/asm/macros/music_voice.inc" target="_blank" rel="noopener"
 &gt;music_voice.inc from pokeemerald&lt;/a&gt;).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;1 byte indicating the voice type
1 byte indicating the voices &amp;#39;base key&amp;#39;, dictating how a specific midi key frequency is generated
2 bytes containing pan / sweep data depending on the voice type (A, B)
4 bytes of voice type dependent data (C)
1 byte with the attack value
1 byte with the decay value
1 byte with the sustain level
1 byte with the release level
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The voice types are the following:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Voice Type&lt;/th&gt;
					&lt;th&gt;Byte&lt;/th&gt;
					&lt;th&gt;A&lt;/th&gt;
					&lt;th&gt;B&lt;/th&gt;
					&lt;th&gt;C&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;DirectSound&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;0x00&lt;/code&gt; or &lt;code&gt;0x10&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;SBZ&lt;/td&gt;
					&lt;td&gt;pan&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;WaveData&lt;/code&gt; pointer&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;DirectSound without resampling&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;0x08&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;SBZ&lt;/td&gt;
					&lt;td&gt;pan&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;WaveData&lt;/code&gt; pointer&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Square &amp;amp; sweep (CGB channel 1)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;0x01&lt;/code&gt; or &lt;code&gt;0x09&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;pan&lt;/td&gt;
					&lt;td&gt;sweep&lt;/td&gt;
					&lt;td&gt;1 byte duty cycle type 3 bytes SBZ&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Square 2 (CGB channel 2)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;0x02&lt;/code&gt; or &lt;code&gt;0x0a&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;pan&lt;/td&gt;
					&lt;td&gt;n/a&lt;/td&gt;
					&lt;td&gt;1 byte duty cycle type 3 bytes SBZ&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Waveform (CGB channel 3)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;0x03&lt;/code&gt; or &lt;code&gt;0x0b&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;pan&lt;/td&gt;
					&lt;td&gt;SBZ&lt;/td&gt;
					&lt;td&gt;Wave RAM data pointer&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Noise (CGB channel 4)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;0x04&lt;/code&gt; or &lt;code&gt;0x0c&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;pan&lt;/td&gt;
					&lt;td&gt;SBZ&lt;/td&gt;
					&lt;td&gt;1 byte period type 3 bytes SBZ&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Keysplit&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;0x40&lt;/code&gt; or &lt;code&gt;0x80&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;SBZ&lt;/td&gt;
					&lt;td&gt;SBZ&lt;/td&gt;
					&lt;td&gt;Voicegroup pointer&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The precise meaning of the alternative encodings is unclear to me, for audio extracting it did not seem to matter much.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Squarewave voices&lt;/strong&gt; These are perhaps the simplest voices. They generate sound using the corresponding CGB wave channels. The duty cycle parameter means the same as the &lt;a class="link" href="https://problemkaputt.de/gbatek-gba-sound-channel-1-tone-sweep.htm" target="_blank" rel="noopener"
 &gt;value in the corresponding IO register&lt;/a&gt;, meaning that the values 0, 1, 2 and 3 correspond with a wave value of 1 for 12.5%, 25%, 50% or 75% of the time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Waveform voices&lt;/strong&gt; These use CGB channel 3, and the wave RAM data pointer, containing 16 bytes (&lt;em&gt;always&lt;/em&gt; 16 bytes, so 32 4-bit samples) of audio data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Noise voices&lt;/strong&gt; These are often used for percussion instruments, and use CGB channel 4. The period type parameter is the same as that in &lt;a class="link" href="https://problemkaputt.de/gbatek-gba-sound-channel-4-noise.htm" target="_blank" rel="noopener"
 &gt;the corresponding IO register&lt;/a&gt;, meaning that a 0 indicates a 15 bit counter step width, and a 1 a 7 bit counter step width.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DirectSound voices&lt;/strong&gt; These use DMA audio. The &lt;code&gt;WaveData&lt;/code&gt; pointer points to a struct defined as below. I ignore the &lt;code&gt;type&lt;/code&gt;. The &lt;code&gt;data&lt;/code&gt; pointer points to an array of &lt;code&gt;size&lt;/code&gt; samples. The &lt;code&gt;freq&lt;/code&gt; parameter indicates the frequency at which the sample was recorded, dictating at what rate the waveform has to be resampled to produce the right sound (using the MidiKeyToFreq function!). The &lt;code&gt;loopStart&lt;/code&gt; parameter indicates the index at which a possible loop in the data might start. Of course, we can&amp;rsquo;t store a waveform for an arbitrarily long tone, so we indicate the index into the wave data to which the engine has to loop back when it reaches the end of the data. The presence of such a loop is indicated by either of the &lt;code&gt;0xc000&lt;/code&gt; bits in the &lt;code&gt;flags&lt;/code&gt; parameter.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; WaveData
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u16 type;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u16 flags;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u32 freq; &lt;span style="color:#75715e"&gt;// sample rate * 1024
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u32 loopStart; &lt;span style="color:#75715e"&gt;// index into samples at which loop starts
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u32 size; &lt;span style="color:#75715e"&gt;// number of samples
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; i8&lt;span style="color:#f92672"&gt;*&lt;/span&gt; data; &lt;span style="color:#75715e"&gt;// samples
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Keysplit voices&lt;/strong&gt; These are special voices. Remember when I said there can only be one voicegroup per song? That was a little bit of a lie, but not entirely. Basically, keysplit voices point to &lt;em&gt;another&lt;/em&gt; voice group. The midi key in a note now doesn&amp;rsquo;t indicate what tone should be played, but instead what index into the nested voice group should be used. The frequency is then determined either by the &lt;code&gt;base&lt;/code&gt; key, or by the index itself (depending on the type byte of the keysplit voice type.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A precise parsing of voice data can be found in &lt;a class="link" href="https://github.com/DenSinH/gba-audio-extractor/blob/master/src/extractor/voice.cpp" target="_blank" rel="noopener"
 &gt;voices.cpp&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="control-flow-events"&gt;Control Flow Events
&lt;/h3&gt;&lt;p&gt;There are three types of control flow events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;GOTO&lt;/code&gt;: This event takes a pointer as parameter. When this event happens, the audio engine jumps to the pointer, and starts processing commands from there. Note that this is an &lt;em&gt;unconditional&lt;/em&gt; jump, so it is only really useful to create a loop in a song, by placing a &lt;code&gt;GOTO&lt;/code&gt; at the &amp;ldquo;end&amp;rdquo;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;PATT&lt;/code&gt; / &lt;code&gt;PEND&lt;/code&gt;: These commands define a &amp;ldquo;pattern&amp;rdquo;. The engine jumps to the location indicated in the parameter of the &lt;code&gt;PATT&lt;/code&gt; command, and starts reading from there, returning when it finds a &lt;code&gt;PEND&lt;/code&gt; command, defining a sort of &amp;ldquo;subroutine&amp;rdquo; in the song. A song may nest patterns up to three layers deep.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;FINE&lt;/code&gt;: This indicates the end of the track, telling the engine that there are no more commands to be read and that the song is over.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="generating-audio"&gt;Generating audio
&lt;/h3&gt;&lt;p&gt;Before we go over to the last couple of events, I want to briefly discuss my idea behind generating audio based on the parsed data (tracks and voice types), in a GBA ROM. What I do is the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;When parsing a track, I &amp;ldquo;unfold&amp;rdquo; all patterns and turn ties into notes with very high lengths. Basically, this simplifies a track a lot. Instead of having to jump to memory addresses, I simply have a long vector of notes and simple control events, and possibly a GOTO, which jumps to a specific time tick.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I have a &lt;code&gt;TrackStatus&lt;/code&gt; struct, which I use to keep track of the current status of a track.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TrackStatus&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// external control (enable / disable track from UI)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ExternalControl&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; enabled &lt;span style="color:#f92672"&gt;=&lt;/span&gt; true;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } external;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// control variables
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; i32 keyshift &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; vol &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; i32 pan &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; i32 bend &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; i32 bend_range &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// default
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u32 lfo_speed &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;22&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// default
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u32 lfo_delay &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u32 mod &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u32 modt &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; i32 tune &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// internal state
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; i32 tick &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// current tick may vary between tracks because of GOTO events
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; track_ended &lt;span style="color:#f92672"&gt;=&lt;/span&gt; false;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; Event&lt;span style="color:#f92672"&gt;*&lt;/span&gt; current_event &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nullptr&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// points into track-&amp;gt;events
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; Voice&lt;span style="color:#f92672"&gt;*&lt;/span&gt; voice &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nullptr&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// current voice
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;list&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;PlayingNote&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; current_notes{}; &lt;span style="color:#75715e"&gt;// current playing notes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Instead of counting &lt;em&gt;down&lt;/em&gt; a timer, I count &lt;em&gt;up&lt;/em&gt; a timer, which I reset on GOTO commands, and I simply handle all the events that happen at a specific tick.
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If this is a controller event, it happens immediately (usually only some parameter changes, or I set the current tick value if it is a GOTO event)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If this is a note, I add the note to the track&amp;rsquo;s &lt;code&gt;current_notes&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;PlayingNote&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; time_started; &lt;span style="color:#75715e"&gt;// real time (seconds)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; i32 tick_started; &lt;span style="color:#75715e"&gt;// tick
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; time_released; &lt;span style="color:#75715e"&gt;// real time (seconds)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; Note&lt;span style="color:#f92672"&gt;*&lt;/span&gt; note; &lt;span style="color:#75715e"&gt;// note data from track-&amp;gt;events
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; VoiceState voice; &lt;span style="color:#75715e"&gt;// state of voice, used to track &amp;#34;integrated time&amp;#34; for sampling waveform
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Whenever I request a sample, I increment the time by 1 / 44200 (the time per requested sample) and the following happens:
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If enough time has passed, a tick happens (depending on the &amp;ldquo;time per tick&amp;rdquo;, dictated by &lt;code&gt;TEMPO&lt;/code&gt; events). If this is the case, I do the following for every track:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I check all the events that should happen. I set any controller event parameters in the track state and add any playing notes to the track&amp;rsquo;s current playing notes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I tick the voice state of any playing notes, meaning that I keep track of the time properly, as to get the waveform at the correct frequency (as explained below).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I remove any notes that finish playing from the track&amp;rsquo;s current playing notes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I compute a sample for the track, based on the current track&amp;rsquo;s control parameters, any time-dependent audio effects and the current playing notes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I blend all the track&amp;rsquo;s current samples together and return the result.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This can be seen in &lt;a class="link" href="https://github.com/DenSinH/gba-audio-extractor/blob/master/src/extractor/player.cpp" target="_blank" rel="noopener"
 &gt;player.cpp&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="controller-events"&gt;Controller events
&lt;/h3&gt;&lt;p&gt;There are some events that manage certain &amp;ldquo;audio effects&amp;rdquo; (panning, pitch shifting etc.), or the general volume of a track. Some of these involve the LFO (Low Frequency Oscillator), which is controlled by the &lt;code&gt;LFO-&lt;/code&gt; commands, setting the LFO &lt;code&gt;speed&lt;/code&gt;, &lt;code&gt;delay&lt;/code&gt;, &lt;code&gt;depth&lt;/code&gt; and the modulation type &lt;code&gt;modt&lt;/code&gt;. Each track has its own LFO, allowing for per-track volume / pitch control effects.&lt;/p&gt;
&lt;h4 id="the-lfo"&gt;The LFO
&lt;/h4&gt;&lt;p&gt;The &lt;code&gt;MODT&lt;/code&gt; command sets the mode of the LFO to either pitch (allowing for a &amp;ldquo;vibrato&amp;rdquo; effect), volume (also allowing for some sort of vibrato effect) or pan (allowing for a &amp;ldquo;panning&amp;rdquo; effect, making it sound like the sound origin is slowly moving from left to right). In the GBA, the LFO values are updated every frame, but by carefully studying its definitions in &lt;a class="link" href="https://github.com/Kurausukun/pokeemerald/blob/pc_port/src/music_player.c" target="_blank" rel="noopener"
 &gt;music_player.c by Karaukasan&lt;/a&gt;, we can define a direct formula for it:&lt;/p&gt;
&lt;p&gt;\[ \texttt{modM}(t) = \begin{cases} 0 &amp;amp; \textrm{ if } t &amp;lt; \texttt{delay} \ast\texttt{FrameTime} \\ \texttt{depth} \ast\sin\left(2\pi\ast\frac{\texttt{speed}}{255}\ast\left(t - \texttt{delay}\ast\texttt{FrameTime}\right)\right) &amp;amp; \textrm{ if } t \geq \texttt{delay} \ast \texttt{FrameTime} \end{cases} \]&lt;/p&gt;
&lt;p&gt;Commonly, the value of &lt;code&gt;depth&lt;/code&gt; is 1, 2 or 3, causing these to be very slight adjustments.&lt;/p&gt;
&lt;h4 id="volume-controller-events"&gt;Volume controller events
&lt;/h4&gt;&lt;p&gt;Some controller events affect the volume or the volume pan.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;VOL&lt;/code&gt; sets the master volume of the track to a value between 0 and 255. We call the current track volume &lt;code&gt;vol&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;PAN&lt;/code&gt; defines a volume pan in the track, which gradually oscillates the volume on the left and right channel, creating a &amp;ldquo;panning effect&amp;rdquo;, making it sound like the sound origin is moving from left to right. We call the current track pan value &lt;code&gt;pan&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The resulting (normalized) volume of the left and right channels is then computed as&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PanVolume Player&lt;span style="color:#f92672"&gt;::&lt;/span&gt;TrackStatus&lt;span style="color:#f92672"&gt;::&lt;/span&gt;GetPannedVolume(&lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; dt) &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; main_vol &lt;span style="color:#f92672"&gt;=&lt;/span&gt; vol;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (modt &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; main_vol &lt;span style="color:#f92672"&gt;*=&lt;/span&gt; (modM(dt) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;128.0&lt;/span&gt;) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;128.0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; pan_vol &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (pan &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x40&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (modt &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pan_vol &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; modM(dt);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pan_vol &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;clamp(pan_vol, &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;128.0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;127.0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .left &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;127.0&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; pan_vol) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; main_vol &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;256.0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .right &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (pan_vol &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;128.0&lt;/span&gt;) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; main_vol &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;256.0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="pitch-controller-events"&gt;Pitch controller events
&lt;/h4&gt;&lt;p&gt;Besides the volume, the pitch of a note can be adjusted as well. Since this does not affect the &amp;ldquo;actual waveform&amp;rdquo; of a voice, but only the frequency at which it plays, I continually compute &amp;ldquo;integrated time&amp;rdquo; in the current state of a voice, and then sample the voice&amp;rsquo;s waveform using the integrated time. That is, in my music player, I track &amp;ldquo;voice states&amp;rdquo;, and every time I request a sample (at a 44.2kHz rate), I add a very small time &lt;code&gt;dt = 1.0 / 44200&lt;/code&gt;, which I then correct by the current frequency of the playing note (which may change through the controller events) to compute the &amp;ldquo;integrated time&amp;rdquo; to sample the waveform at. Volume envelope is not affected by the frequency of the played tone, meaning we also have to track &amp;ldquo;actual time&amp;rdquo;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; VoiceState&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Tick(i32 midi_key, &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; pitch_adjust, &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; dt) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (base_key &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) { &lt;span style="color:#75715e"&gt;// only for keysplit voices
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// keep fine adjust
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; midi_key &lt;span style="color:#f92672"&gt;=&lt;/span&gt; base_key;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; freq &lt;span style="color:#f92672"&gt;=&lt;/span&gt; voice&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;GetFrequency(midi_key &lt;span style="color:#f92672"&gt;+&lt;/span&gt; pitch_adjust);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; integrated_time &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; dt &lt;span style="color:#f92672"&gt;*&lt;/span&gt; freq;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; actual_time &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; dt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; VoiceState&lt;span style="color:#f92672"&gt;::&lt;/span&gt;GetSample(&lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; time_since_release) &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; wave_sample &lt;span style="color:#f92672"&gt;=&lt;/span&gt; voice&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;WaveForm(integrated_time);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; envelope_volume &lt;span style="color:#f92672"&gt;=&lt;/span&gt; voice&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;GetEnvelopeVolume(actual_time, time_since_release);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; wave_sample &lt;span style="color:#f92672"&gt;*&lt;/span&gt; envelope_volume;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;pitch_adjust&lt;/code&gt; is affected by the controller events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;KEYSH&lt;/code&gt; sets the track&amp;rsquo;s (signed) &lt;code&gt;keyshift&lt;/code&gt; value, adjusting the midi key by full keys. This is not affected by the LFO in the pitch adjusting mode.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;TUNE&lt;/code&gt; command provides a constant &amp;ldquo;fine adjust&amp;rdquo; (fractional midi key) in the pitch. It is on a scale of -64 to 64, normalized to a range of -1 to 1 midi key.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;BENDR&lt;/code&gt; sets the track&amp;rsquo;s current bend range, providing further fine adjust. The &amp;ldquo;bend&amp;rdquo; is like a variable &amp;ldquo;fine adjust&amp;rdquo; which is controlled manually.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;BEND&lt;/code&gt; command sets the current track&amp;rsquo;s bend amount. This is how we manually adjust the &amp;ldquo;fine adjust&amp;rdquo;. The total &amp;ldquo;fine adjust&amp;rdquo; from the bending effect takes the bend value and multiplies it by the bend range.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the end, a note&amp;rsquo;s midi key is determined as follows&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; Player&lt;span style="color:#f92672"&gt;::&lt;/span&gt;TrackStatus&lt;span style="color:#f92672"&gt;::&lt;/span&gt;GetPitchAdjust(&lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; dt) &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; adjust &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (tune &lt;span style="color:#f92672"&gt;+&lt;/span&gt; bend &lt;span style="color:#f92672"&gt;*&lt;/span&gt; bend_range);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (modt &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// pitch adjust: &amp;#34;vibrato effect&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; adjust &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; modM(dt);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; keyshift &lt;span style="color:#f92672"&gt;+&lt;/span&gt; (adjust &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;256.0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And for all the currently playing notes in a track, we get a new sample by ticking the voice state&amp;rsquo;s integrated time as&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; pitch_adjust &lt;span style="color:#f92672"&gt;=&lt;/span&gt; status.GetPitchAdjust(global_time &lt;span style="color:#f92672"&gt;-&lt;/span&gt; note.time_started);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; i32 keyshift &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;floor(pitch_adjust);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; fine_adjust &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pitch_adjust &lt;span style="color:#f92672"&gt;-&lt;/span&gt; keyshift;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; i32 key &lt;span style="color:#f92672"&gt;=&lt;/span&gt; note.note&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;key &lt;span style="color:#f92672"&gt;+&lt;/span&gt; keyshift;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; note.voice.Tick(key, fine_adjust, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; SampleRate);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, the last step is how we determine the actual frequency from the midi key, the keyshift and the fine adjust. As seen before, the actual played midi key is determined by simply adding the midi key and the fine adjust caused by the keyshift and the tune / bend effects. The way we then determine the voice&amp;rsquo;s frequency depends on the voice type. Since keysplit voices act like any other voice type, depending on the selected voice through the played midi key (of course &lt;em&gt;not&lt;/em&gt; adjusted by any pitch shifting mechanisms), we only have to consider the &amp;ldquo;CGB type voices&amp;rdquo; or the &amp;ldquo;directsound voices&amp;rdquo;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CGB type voices (except noise)&lt;/strong&gt; use the &lt;code&gt;CgbMidiKeyToFreq&lt;/code&gt; function (which is not available in the BIOS). It is defined as below. The value 69 is the midi key for a neutral A (concert pitch at 440Hz), 12 means there are 12 midi keys (semitones) in an octave, meaning that an octave doubles the frequency (as expected). In the actual GBA, this value is then corrected by the right amount to be written to the corresponding CGB channel&amp;rsquo;s IO register.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;inline&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;CgbMidiKeyToFreq&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; midi_key) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; corrected_key &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (midi_key &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;69.0&lt;/span&gt;) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;12.0&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// An3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;pow(&lt;span style="color:#ae81ff"&gt;2.0&lt;/span&gt;, corrected_key) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;440&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Noise voices&lt;/strong&gt; use a table, indexed by the midi key:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;array&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;double&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;256&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; NoiseFreqTable &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;5.33333333&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;6.40000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;8.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;9.14285714&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;10.66666667&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;12.80000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;16.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;18.28571429&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;21.33333333&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;25.60000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;32.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;36.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;42.66666667&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;51.20000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;64.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;73.14285714&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;85.33333333&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;102.40000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;128.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;146.28571429&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;170.66666667&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;204.80000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;256.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;292.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;341.33333333&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;409.60000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;512.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;585.14285714&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;682.66666667&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;819.20000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1024.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1170.28571429&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1365.33333333&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1638.40000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2048.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2340.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2730.66666667&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;3276.80000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4096.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4681.14285714&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;5461.33333333&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;6553.60000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;8192.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;9362.28571429&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;10922.66666667&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;13107.20000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;16384.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;18724.57142857&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;21845.33333333&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;26214.40000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;32768.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;37449.14285714&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;43690.66666667&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;52428.80000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;65536.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;87381.33333333&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;131072.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;262144.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;524288.00000000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Direct sound voices&lt;/strong&gt; use the MidiKeyToFreq function, defined as below. Here, &lt;code&gt;base&lt;/code&gt; is the sampled frequency for the sound sample in the voice (which is the sample rate times 1024). The default tuning in this case is to a neutral C, again an octave (consisting of 12 semitones) means we double the frequency, as expected.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;inline&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MidiKeyToFreq&lt;/span&gt;(u32 base, &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; midi_key) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; corrected_key &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (midi_key &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;60.0&lt;/span&gt;) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;12.0&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// Cn3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;pow(&lt;span style="color:#ae81ff"&gt;2.0&lt;/span&gt;, corrected_key) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (base &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1024.0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;p&gt;I want to quickly give a summary of how the GBA plays sound:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Data&lt;/strong&gt; is stored in terms of tracks and voices.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tracks&lt;/strong&gt; are streams of events (wait events, notes, voice changes, tempos, control flow, volume / pitch control etc.). They each have their own sets of parameters describing what voice to use, what the pitch / volume adjustments are, and what audio effects should be used.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Voices&lt;/strong&gt; contain data describing how to generate audio, using either CGB or direct audio channels.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The song table&lt;/strong&gt; contains a list of the song headers that are used in the game, making it easy to find all the songs in a ROM.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Audio is generated&lt;/strong&gt; by computing the needed samples every frame. This happens by&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Handling events&lt;/strong&gt; by tracking currently played notes (adding new ones or removing old ones) and setting control parameters.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Computing each track&amp;rsquo;s active notes&amp;rsquo; waveforms&lt;/strong&gt; by handling pitch shifting controls (possibly using the track&amp;rsquo;s LTO) and properly transforming the computed midi key and fine adjustment into a frequency at which the waveform should be sampled.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Computing each track&amp;rsquo;s volume&lt;/strong&gt; by handling volume changing controls (possibly using the track&amp;rsquo;s LTO) and mixing the tracks together.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="applications"&gt;Applications
&lt;/h2&gt;&lt;p&gt;So what are some other interesting purposes for all this information? Well, one that comes to mind is possible audio HLE in GBA emulation. This may be a significant performance boost, as my own emulator spends most of its time emulating audio. Since most games use this engine, it may be possible to find the main audio management functions through a byte pattern (take a look at &lt;a class="link" href="https://github.com/loveemu/saptapper/blob/2da643b718ec6308c4064f5aae769c035c496368/src/saptapper/mp2k_driver.cpp#L165" target="_blank" rel="noopener"
 &gt;saptapper&lt;/a&gt; for example, using byte patterns to find a game&amp;rsquo;s songtable and main audio functions) or some other methods, and then triggering a HLE function handling the engine&amp;rsquo;s capabilities. Most significantly, this saves a GBA game a lot of computation every VBlank for mixing audio samples from the tracks in a song.&lt;/p&gt;
&lt;p&gt;Another application is of course audio extraction. As mentioned above, a game&amp;rsquo;s &lt;em&gt;songtable&lt;/em&gt; (the main array of songheader pointers) may be found with a byte pattern, and used to extract data like I did in &lt;a class="link" href="https://github.com/DenSinH/gba-audio-extractor" target="_blank" rel="noopener"
 &gt;my GBA audio extractor&lt;/a&gt;, see &lt;a class="link" href="https://github.com/DenSinH/gba-audio-extractor/blob/master/src/extractor/mp2k_driver.cpp" target="_blank" rel="noopener"
 &gt;mp2k_driver.cpp&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img class="gallery-image" data-flex-basis="409px" data-flex-grow="170" height="940" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://blog.dennishilhorst.nl/post/gba-audio-data/images/extractor-zelda-8.webp" srcset="https://blog.dennishilhorst.nl/post/gba-audio-data/images/extractor-zelda-8_hu_4eb7eb9de366a83e.webp 800w, https://blog.dennishilhorst.nl/post/gba-audio-data/images/extractor-zelda-8_hu_d9d700cf34ede8c0.webp 1600w, https://blog.dennishilhorst.nl/post/gba-audio-data/images/extractor-zelda-8.webp 1603w" width="1603"&gt;&lt;/p&gt;
&lt;h2 id="final-note"&gt;Final note
&lt;/h2&gt;&lt;p&gt;As a final note, I strongly encourage anyone to take a look at &lt;a class="link" href="https://github.com/Kurausukun/pokeemerald/blob/pc_port/src/music_player.c" target="_blank" rel="noopener"
 &gt;Karausukun&amp;rsquo;s music_player.c&lt;/a&gt;, just to see how much computation the GBA has to do to properly mix sound samples. It is good to keep in mind that all of this actually happens in the background (triggered by VBlank interrupts), and is mostly it&amp;rsquo;s own thing, running apart from the game. It should be very possible to HLE audio completely for a large library of games, since most games use this audio engine provided by Nintendo.&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources
&lt;/h2&gt;&lt;p&gt;Important sources include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;GBA decompilation projects like &lt;a class="link" href="https://github.com/zeldaret/tmc" target="_blank" rel="noopener"
 &gt;zeldaret/tmc&lt;/a&gt;, &lt;a class="link" href="https://github.com/pret/pokeruby" target="_blank" rel="noopener"
 &gt;pret/pokeruby&lt;/a&gt; or &lt;a class="link" href="https://github.com/pret/pokeemerald" target="_blank" rel="noopener"
 &gt;pret/pokeemerald&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/Kurausukun/pokeemerald/blob/pc_port/src/music_player.c" target="_blank" rel="noopener"
 &gt;Karausukun&amp;rsquo;s work on decompiling / porting the m4a audio engine.&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/ipatix/agbplay/tree/master" target="_blank" rel="noopener"
 &gt;ipatix&amp;rsquo;s AGBPlay&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/loveemu/saptapper" target="_blank" rel="noopener"
 &gt;Saptapper&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://loveemu.github.io/vgmdocs/Summary_of_GBA_Standard_Sound_Driver_MusicPlayer2000.html" target="_blank" rel="noopener"
 &gt;Saptapper&amp;rsquo;s documentation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>EEPROM Save Type</title><link>https://blog.dennishilhorst.nl/post/eeprom-save-type/</link><pubDate>Mon, 01 Feb 2021 00:00:00 +0000</pubDate><guid>https://blog.dennishilhorst.nl/post/eeprom-save-type/</guid><description>&lt;p&gt;The GBA has games with different sorts of backup memory. There are 3 types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;SRAM, straightforward RAM on the cartridge&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Flash, flash storage on the cartridge (explained &lt;a class="link" href="https://dillonbeliveau.com/2020/06/05/GBA-FLASH.html" target="_blank" rel="noopener"
 &gt;here&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;EEPROM, which I will attempt to explain in this document.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can see what save type a cartridge uses most accurately by using a game database. If you don’t have one of those available, you can check for certain strings in the cartridge (also explained &lt;a class="link" href="https://dillonbeliveau.com/2020/06/05/GBA-FLASH.html" target="_blank" rel="noopener"
 &gt;here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://problemkaputt.de/gbatek.htm#gbacartbackupeeprom" target="_blank" rel="noopener"
 &gt;GBATek&lt;/a&gt; also explains the EEPROM save type, but I found it quite brief, so I will try to add on to this documentation.&lt;/p&gt;
&lt;h3 id="eeprom-types"&gt;EEPROM types
&lt;/h3&gt;&lt;p&gt;There are 2 different types of EEPROM cartridges:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;512 bytes / 4Kbit EEPROM&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;8KB / 64Kbit EEPROM&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Which of these 2 a cartridge has is impossible to figure out just by looking at the cartridge. Your only options are a game database, or a trick where you check the first access made to it, which I will explain later.&lt;/p&gt;
&lt;h3 id="addressing-and-waitstates"&gt;Addressing and waitstates
&lt;/h3&gt;
 &lt;blockquote&gt;
 &lt;p&gt;The eeprom is connected to Bit0 of the data bus, and to the upper 1 bit (or upper 17 bits in case of large 32MB ROM) of the cartridge ROM address bus, communication with the chip takes place serially.&lt;/p&gt;
&lt;p&gt;GBATek&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;This means that data transferred to or from the EEPROM chip is always 1 bit at a time. Any transfer made to EEPROM will be “masked” to only the bottom bit, and any read will just be 1 or 0.&lt;/p&gt;
&lt;p&gt;On a large ROM (of greater than 16MB in size), ROM is restricted to &lt;code&gt;0x0800'000h-0x09ff'feff&lt;/code&gt;. So, EEPROM can be accessed between &lt;code&gt;0x09ff'ff00&lt;/code&gt; and &lt;code&gt;0x09ff'ffff&lt;/code&gt;. This is also mirrored to the higher waitstate cartridge regions. Judging from the source code of certain emulators, it can really onlly be accessed in the &lt;code&gt;0x0dxx'xxxx&lt;/code&gt; region of ROM (second waitstate), despite what GBATek says. On smaller ROMs, it can also be accessed between &lt;code&gt;0x0d00'0000-0x0dff'ffff.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The actual address that is accessed for the EEPROM access does not matter, as the “internal address” has to be sent first, and then data can be written or read.&lt;/p&gt;
&lt;p&gt;Data can be read or written. The initial pattern for the access is similar: the mode has to be sent, and the address as well.&lt;/p&gt;
&lt;p&gt;This is also where the different EEPROM sizes come into play. The EEPROM can only transfer data in units of 64 bits. Addressing also works in units of 64 bits. This means, that while for a 512 byte EEPROM, you have 0x200 bytes to address, there are only 0x40 blocks of 64 bits. The address will only be in the range of &lt;code&gt;0 - 0x3f&lt;/code&gt;. The bus width for a 512 byte EEPROM is 6, and the address that will be sent will also be 6 bits long.&lt;/p&gt;
&lt;p&gt;For an 8KB EEPROM, there are 0x2000 bytes, but only 0x400 blocks of 64 bits. The address will thus only be in the range &lt;code&gt;0 - 0x3ff&lt;/code&gt;. The bus width for a 8KB EEPROM is 14 bits, but the address only 10. The address that gets sent will be 14 bits long, but the first 4 bits should be zero, as they don’t correspond with any blocks.&lt;/p&gt;
&lt;p&gt;It is important to actually have the addressing happen in blocks of 8 bytes / 64 bits. I did this wrong in my emulator at first, and it caused some sneaky corrupted saves.&lt;/p&gt;
&lt;h3 id="reading-data"&gt;Reading data
&lt;/h3&gt;&lt;p&gt;When you want to read data from the EEPROM, you have to send the following sequence of bits:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;2 bits &amp;#34;11&amp;#34; (Read Request)
n bits eeprom address (MSB first, 6 or 14 bits, depending on EEPROM)
1 bit &amp;#34;0&amp;#34;
(GBATek)
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="eeprom-size-detection"&gt;EEPROM size detection
&lt;/h4&gt;&lt;p&gt;This &lt;code&gt;n&lt;/code&gt; is what you can deduce the EEPROM size from. A trick to detect the EEPROM size is to keep it ambiguous until the first (read) request is made. Requests have to be done by DMA, since normal transfers via LDRH/STRH are too slow, and don’t keep the right bits set during the transfer.&lt;/p&gt;
&lt;p&gt;Since DMA channel 3 is the only DMA channel that can access ROM, you could, on the first (read) access, check the transfer length of DMA channel 3. If it’s of length 9, a 6 bit address will be sent, and the EEPROM is (likely) a 512 byte EEPROM. If it’s of length 17, a 14 bit address will be sent, and the EEPROM is (likely) an 8KB EEPROM.&lt;/p&gt;
&lt;p&gt;This method is not perfect though. Some games, like the NES classic series, try to trick you into thinking it’s the wrong EEPROM size, by doing a transfer of the “wrong” length. Your best bet will be a game database, or some sort of hybrid approach.&lt;/p&gt;
&lt;h4 id="the-transfer"&gt;The transfer
&lt;/h4&gt;&lt;p&gt;Since it’s annoying to have to place individual bits at a (half)word interval in memory to then transfer data, a common approach for games/programs is to “shift” them into memory. Basically, if I wanted to send a read request to an 8KB EEPROM to block &lt;code&gt;0x123&lt;/code&gt;, I would need to transfer: &lt;code&gt;(0b11 &amp;lt;&amp;lt; 15) | (0x123 &amp;lt;&amp;lt; 1) | 0 = 0x18246&lt;/code&gt;. Suppose &lt;code&gt;r1&lt;/code&gt; holds a pointer to a the end of a 17 halfword buffer where we want to store our bits, and &lt;code&gt;r0&lt;/code&gt; holds &lt;code&gt;0x18246&lt;/code&gt;. One could simply do&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;strh r0, [r1], #-2
lsr r0, #1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;17 times, and the buffer will then be filled with&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;0x0001
...
0x3048
0x6091
0xc123
0x8246
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;such that the 0th bit of each halfword exactly reads out &lt;code&gt;0x18246&lt;/code&gt; (MSB first). I can then transfer this buffer to EEPROM. Since only bit 0 is connected to the data bus, it does not matter that there is other data in the other 15 bits of each halfword.&lt;/p&gt;
&lt;p&gt;This is how the address is transferred for a read access. After the address is transferred, we can read back the data. This again has to happen by DMA. There are 68 bytes to be read back with DMA:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;4 bits - ignore these
64 bits - data (conventionally MSB first)
(GBATek)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These accesses have to be made in the same region as the address has to be written to, but just read instead of written. After this, it is up to the game how it handles the individual bits returned by the DMA.&lt;/p&gt;
&lt;h3 id="writing-data"&gt;Writing data
&lt;/h3&gt;&lt;p&gt;The written data immediately follows the address that is written. I have never encountered a game doing a write access before a read access, but it’s possible, so it might be good to check if this is the case when trying to detect the EEPROM’s size.&lt;/p&gt;
&lt;p&gt;The data that has to be written is:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;2 bits &amp;#34;10&amp;#34; (Write Request)
n bits eeprom address (MSB first, 6 or 14 bits, depending on EEPROM)
64 bits data (conventionally MSB first)
1 bit &amp;#34;0&amp;#34;
(GBATek)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So the start is similar to the read request, except a different “code”. Then the address, again different sizes depending on the EEPROM’s size. Then 64 bits / 8 bytes of data, and one bit to end the transfer.&lt;/p&gt;
&lt;p&gt;These transfers might also be made in a similar way to the read access, where the data is first “shifted” to a buffer, and then transferred by DMA.&lt;/p&gt;
&lt;p&gt;After a write transfer, games likely check if the transfer is complete. They do this by reading from the EEPROM and waiting until it returns 1. Martin Korth describes in GBATek how it’s important to set a timeout if the EEPROM does not respond, but some games might just hang if you don’t return 1 on reads after a write access.&lt;/p&gt;</description></item><item><title>Writing a cached interpreter</title><link>https://blog.dennishilhorst.nl/post/writing-a-cached-interpreter/</link><pubDate>Sun, 31 Jan 2021 00:00:00 +0000</pubDate><guid>https://blog.dennishilhorst.nl/post/writing-a-cached-interpreter/</guid><description>&lt;p&gt;A while ago, I rewrote my GBA emulator (GBAC-) in C++ (DSHBA) to make it faster. I wanted to add a hardware renderer, and really focus on optimizing it all the way. After I was “done”, and had reached framerates higher than I had ever hoped before, I recently came back to it, and wanted to try to write a cached interpreter. I have heard a lot of talks about JITs and cached interpreters for performance gain, and since I had reached pretty insane framerates already, I wanted to go even further.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note: this is not a general guide for writing a cached interpreter, and this approach might not be viable on every system. In this post I will explain why that is, and focus on my thought process and the implementation for my cached interpreter.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="why-not-a-jit"&gt;Why not a JIT?
&lt;/h3&gt;&lt;p&gt;Something you might ask is: “why did you write a cached interpreter and not a JIT”. The short answer to this is this: accuracy. The GBA is a fairly old system. Usually for older systems, timings are important. Where for modern systems, you don’t have to worry about cycles for the most part, other than being in the right ballpark (this is mainly because for more modern systems, the programmer himself can also rely less on specific timings due to pipelining, OoO execution and other advanced techniques), for the GBA you still sort of have to. A lot of tests, even in the official AGS Aging Cartridge test from Nintendo, some tests require accuracy within 16 cycles (in a frame of ~1200 cycles, or 1 scanline), or some even perfect cycle accuracy. Games may also rely on specific timings.&lt;/p&gt;
&lt;p&gt;My old GBA emulator and my new one had the same performance in terms of accuracy. I passed most AGS tests, a lot of Endrift’s tests, but not any of the crazy perfect cycle accuracy ones. This was good enough for me, but I wouldn’t want to reduce accuracy for the sake of speed. These timings are easiest to emulate in an interpreter. In a JIT, you want to mostly just keep track of your GPRs (General Purpose Registers), and not of PPU events happening, or audio samples being requested, and most importantly: IRQs firing at the right times. These things, will often require me to check the scheduler, or break blocks early at “random” moments. These things combined led me to write a cached interpreter and not a JIT.&lt;/p&gt;
&lt;h3 id="how-does-a-cached-interpreter-work"&gt;How does a cached interpreter work?
&lt;/h3&gt;&lt;p&gt;First of all, this is how my interpreter loop looked before my cached interpreter:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;check scheduler:
 if there are events to handle: do events 
 otherwise: step once
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then let’s first look at the classic interpreter. The idea for this is very simple:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;fetch opcode from memory
decode opcode 
run instruction handler
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Alright, simple enough. This seems very straight forward, but there is a step here that we can remove. If we decode an instruction, and later on we get to the same address, we wouldn’t have to decode it again if we store the information on the instruction. This is “caching” the instruction. We can make it even better than this though, we can make entire blocks of instructions at once, so that we dont have to look up the cached instruction every time, but just look up a block and run for multiple instructions at once!&lt;/p&gt;
&lt;p&gt;Now, the GBA memory map has some mirroring, and code cannot be ran from all regions. So I decided to split things up a bit. There are 3 regions I allow caching in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;the BIOS: this region cannot be written to, so once we make a cached block, we never have to delete it again!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ROM: this region is also not writeable, so we can save the cache blocks forever!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iWRAM: code often gets run from here because it takes very few cycles to fetch data from here. We have to handle this region slightly differently though, because code can get overwritten.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cache “blocks” are blocks of instructions following each other that can be executed after each other. These blocks have to end somewhere, and the most logical places are branches or on certain boundaries. I set these boundaries at every 256 bytes. This number is rather arbitrary, but setting this number allows you to only delete certain blocks when writes to iWRAM happen.&lt;/p&gt;
&lt;p&gt;The idea for the main loop of the emulator becomes slightly different:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;check if current address has a cached block 
if it doesn&amp;#39;t have one, and none can be created: 
 run interpreter normally, until it can 
if it doesn&amp;#39;t have one, and one can be created:
 make a cache block and run interpreter normally*, but record the instructions
if it has one:
 run the cache block**
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now this is not the entire story. As I mentioned before, there is still a scheduler, and IRQs might happen that interrupt a block. Now what I could naively do (and did at first) is just break whenever we are interrupted by the scheduler. I don’t need to always break though. Some events, like adding a sample to the audio buffer, shouldn’t need to interrupt the CPU at all, since they don’t have anything to do with it!&lt;/p&gt;
&lt;p&gt;I changed up my scheduler slightly, and made every event return a boolean, whether they directly affected the CPU state or not. Events that do are events like IRQs, or DMAs where an IRQ happened inbetween. The extra steps will be:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;(*): check if the scheduler interrupted the CPU, and if so, return from making the block

(**): check if the scheduler interrupted the CPU, and if so, return from running the block
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So in the end, the general idea will be this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;while true:
 check current address
 if we are not in a cacheable region (not in ROM/BIOS/iWRAM), do:
 while we are not in a cacheable region:
 check scheduler and run events 
 step, and check if we are in a cacheable region now.
 if we are in a cacheable region, but no cache exists:
 make a cache 
 while true:
 check the scheduler and run events
 if the scheduler affected our CPU: break the block and destroy it 
 fetch 
 decode and store in cache 
 run 
 if the block should end (branch): break 
 if we are in a cacheable region, and a cache exists:
 get the cache 
 for every instruction in the cache:
 check the scheduler and run events
 if the scheduler affected our CPU: break
 run the instruction (in ARM mode: only if condition is true)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This might seem a bit abstract, so here is some actual code. Before I added the cached interpreter, the main loop essentially looked like this (some details like pipeline stuff are left out):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (true) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;Scheduler.ShouldDoEvents()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CPU.Step();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Scheduler.DoEvents();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;with&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ARM7TDMI::Step() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u32 instruction;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (ARMMode){ 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; instruction = Mem-&amp;gt;Read&amp;lt;u32&amp;gt;(pc - &lt;span style="color:#ae81ff"&gt;8&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (CheckCondition(instruction &amp;gt;&amp;gt; &lt;span style="color:#ae81ff"&gt;28&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;-&amp;gt;*ARMInstructions[ARMHash(instruction)])(instruction);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; instruction = Mem-&amp;gt;Read&amp;lt;u16&amp;gt;(pc - &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;-&amp;gt;*THUMBInstructions[THUMBHash(instruction)])(instruction);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A classic interpreter loop. Now we want to add something to store our cache blocks in.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// a simple struct holding the instruction and the handler
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;CachedInstruction&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; u32 Instruction;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__fastcall&lt;/span&gt; (ARM7TDMI&lt;span style="color:#f92672"&gt;::*&lt;/span&gt;Pointer)(u32 instruction);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// a struct holding a block of instructions
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;InstructionCache&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; ARM;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; Deletable; &lt;span style="color:#75715e"&gt;// true in iWRAM
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; u16 AccessTime;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;CachedInstruction&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; Instructions;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And we want some lookup tables for the cache blocks in our CPU.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;array&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_ptr&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;InstructionCache&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;, (Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;BIOSSize &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; BIOSCache &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;array&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_ptr&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;InstructionCache&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;, ((Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;iWRAMSize &lt;span style="color:#f92672"&gt;-&lt;/span&gt; Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;StackSize) &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; iWRAMCache &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;array&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_ptr&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;InstructionCache&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;, (Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;ROMSize &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; ROMCache &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You might notice some things here. First of all, the &lt;code&gt;unique_ptr&lt;/code&gt;s. I know that dynamic allocation is slow in general. I rewrote it to not use dynamic allocation, and I did not notice a difference. Main reason for this might be that blocks will never be deleted in ROM and in the BIOS, and it might be rare for blocks to be deleted in iWRAM. Another benefit this has is that I don’t have to worry about writing a bump allocator (a big region of memory that I push instructions to until it overflows, and then reset the entire thing), the &lt;code&gt;unique_ptr&lt;/code&gt;s will handle memory allocation for me!&lt;/p&gt;
&lt;p&gt;Another thing to note is that I shift the size of the respective regions by 1. This is because all instructions must be aligned by 4 in ARM mode, and 2 in THUMB mode.&lt;/p&gt;
&lt;p&gt;Last thing, for the iWRAM cache I subtract &lt;code&gt;Mem::StackSize&lt;/code&gt;. This is an arbitrary number (that I think I put at &lt;code&gt;0x400&lt;/code&gt;). I did this, because when writes to iWRAM happen, I have to check out which blocks have to be deleted. Since the stack is also in iWRAM, a lot of pushes will happen, and I don’t want to check which blocks to delete on every push/pop.&lt;/p&gt;
&lt;p&gt;One last thing then. We need to keep track of our current cache.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_ptr&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;InstructionCache&lt;span style="color:#f92672"&gt;&amp;gt;*&lt;/span&gt; CurrentCache &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nullptr&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The way this works is very convenient. This might seem like 2 layers of indirection, but while running the cache we can just look at the object that is finally pointed at. In general this is what we will be doing anyway. This is convenient, because we can use &lt;code&gt;nullptr&lt;/code&gt; to encode different states:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;CurrentCache == nullptr: no cache present, no cache can be made
CurrentCache == nullptr*: no cache present, cache can be made
otherwise: cache present!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We also need a way to update the current cache (simplified to remove some range checks etc.):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_ptr&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;InstructionCache&lt;span style="color:#f92672"&gt;&amp;gt;*&lt;/span&gt; ARM7TDMI&lt;span style="color:#f92672"&gt;::&lt;/span&gt;GetCache(&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; u32 address) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;static_cast&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;MemoryRegion&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(address &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;24&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; MemoryRegion&lt;span style="color:#f92672"&gt;::&lt;/span&gt;BIOS: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; u32 index &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (address &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; (Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;BIOSSize &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)) &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// check if cache exists
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (BIOSCache[index]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;BIOSCache[index];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// show that no cache exists, but can be created
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;BIOSCache[index];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; MemoryRegion&lt;span style="color:#f92672"&gt;::&lt;/span&gt;iWRAM: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; u32 index &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (address &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; (Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;iWRAMSize &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)) &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ((address &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; (Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;iWRAMSize &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)) &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; (Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;iWRAMSize &lt;span style="color:#f92672"&gt;-&lt;/span&gt; Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;StackSize)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (iWRAMCache[index]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;iWRAMCache[index];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// mark index as filled for faster page clearing
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; iWRAMCacheFilled[(address &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; (Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;iWRAMSize &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;InstructionCacheBlockSizeBytes].push_back(index);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;iWRAMCache[index];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nullptr&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; MemoryRegion&lt;span style="color:#f92672"&gt;::&lt;/span&gt;ROM: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; u32 index &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (address &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; (Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;ROMSize &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)) &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (ROMCache[index]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;ROMCache[index];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;ROMCache[index];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nullptr&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ignore the &lt;code&gt;iWRAMCacheFilled&lt;/code&gt; line for now. Alright, so now we have everything set up to make caches! First of all, we need different run loops:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ARM7TDMI&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Run();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ARM7TDMI&lt;span style="color:#f92672"&gt;::&lt;/span&gt;RunMakeCache();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ARM7TDMI&lt;span style="color:#f92672"&gt;::&lt;/span&gt;RunCache();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running the cache is the simplest method:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; ARM7TDMI&lt;span style="color:#f92672"&gt;::&lt;/span&gt;RunCache() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// run created cache
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; InstructionCache&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; cache &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;**&lt;/span&gt;CurrentCache;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (cache.ARM) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ARM mode, we need to check the condition now too
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; instr : cache.Instructions) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (unlikely(Scheduler&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;ShouldDoEvents())) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (unlikely(Scheduler&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;DoEvents())) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// CPU state affected
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;timer &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; cache.AccessTime; &lt;span style="color:#75715e"&gt;// add time we would otherwise spend fetching the opcode from memory
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (CheckCondition(instr.Instruction &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;28&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&amp;gt;*&lt;/span&gt;instr.Pointer)(instr.Instruction);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pc &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// block was destroyed (very unlikely)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (cache.Deletable &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; unlikely(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;CurrentCache)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// THUMB mode, no need to check condition
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * the code here is essentially the same as above
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Making the block looks very similar to the old loop, but then with a twist. I templated my basic &lt;code&gt;ARM7TDMI::Step()&lt;/code&gt; to take a boolean &lt;code&gt;MakeCache&lt;/code&gt;. If we are not making a cache, I made the step function return a boolean saying whether we are in a cacheable region or not. That means that &lt;code&gt;Step&amp;lt;false&amp;gt;&lt;/code&gt; is the same as the original, basic interpreter step function, except it returns&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;InCacheRegion(corrected_pc); &lt;span style="color:#75715e"&gt;// corrected_pc = pc - (ARMMode ? 8 : 4)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we are making a cache, we need to record the instructions we find. Then it looks a bit more complex:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;template&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; MakeCache&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; ARM7TDMI&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Step&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;true&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u32 instruction;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; block_end &lt;span style="color:#f92672"&gt;=&lt;/span&gt; false;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (ARMMode) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; instruction &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Memory&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;Read&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;u32, true&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(pc &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; instr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; CachedInstruction(instruction, ARMInstructions[ARMHash(instruction)]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#f92672"&gt;*&lt;/span&gt;CurrentCache)&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;Instructions.push_back(instr);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// check if instruction is branch or an operation with Rd == pc (does not detect multiplies with pc as destination)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; block_end &lt;span style="color:#f92672"&gt;=&lt;/span&gt; IsARMBranch[ARMHash(instruction)]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; ((instruction &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x0000f000&lt;/span&gt;) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x0000f000&lt;/span&gt;) &lt;span style="color:#75715e"&gt;// any operation with PC as destination
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; ((instruction &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x0e108000&lt;/span&gt;) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x08108000&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// ldm r, { .. pc }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (CheckCondition(instruction &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;28&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&amp;gt;*&lt;/span&gt;ARMInstructions[ARMHash(instruction)])(instruction);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pc &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// THUMB mode
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; instruction &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Memory&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;Read&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;u16, true&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(pc &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; instr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; CachedInstruction(instruction, THUMBInstructions[THUMBHash((u16)instruction)]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#f92672"&gt;*&lt;/span&gt;CurrentCache)&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;Instructions.push_back(instr);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// also check for hi-reg-ops with PC as destination
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; block_end &lt;span style="color:#f92672"&gt;=&lt;/span&gt; IsTHUMBBranch[THUMBHash((u16)instruction)] &lt;span style="color:#f92672"&gt;||&lt;/span&gt; ((instruction &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0xfc87&lt;/span&gt;) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x4487&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&amp;gt;*&lt;/span&gt;THUMBInstructions[THUMBHash((u16)instruction)])(instruction);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pc &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (block_end &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#f92672"&gt;!&lt;/span&gt;(corrected_pc &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; (Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;InstructionCacheBlockSizeBytes &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;))) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// branch or block alignment
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; true;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; false;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Basically, if &lt;code&gt;MakeCache&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;Step&lt;/code&gt; returns whether the block has ended or not. If &lt;code&gt;MakeCache&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;, &lt;code&gt;Step()&lt;/code&gt; returns whether a block can be made, or might be available. The &lt;code&gt;RunMakeCache()&lt;/code&gt; method will use &lt;code&gt;Step&amp;lt;true&amp;gt;&lt;/code&gt;, and the basic &lt;code&gt;Run()&lt;/code&gt; method will use &lt;code&gt;Step&amp;lt;false&amp;gt;&lt;/code&gt;, while we are not in a region where caches can be made. The &lt;code&gt;RunMakeCache()&lt;/code&gt; looks like&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; ARM7TDMI&lt;span style="color:#f92672"&gt;::&lt;/span&gt;RunMakeCache() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// run the interpreter &amp;#34;normally&amp;#34;, but record the instructions we execute in the current cache
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (true) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (unlikely(Scheduler&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;ShouldDoEvents())) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (unlikely(Scheduler&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;DoEvents())) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// CPU state affected
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// just destroy the cache, we want the caches to be as big as possible
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;CurrentCache &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nullptr&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (unlikely(Step&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;true&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;())) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// cache done
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (unlikely((&lt;span style="color:#f92672"&gt;*&lt;/span&gt;CurrentCache)&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;Instructions.empty())) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// empty caches will hang the emulator
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// they might happen when branches close to pc get written (self modifying code)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;CurrentCache &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nullptr&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (unlikely(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;CurrentCache)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// cache destroyed by near write
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You’ll notice that I destroy the cache if we get interrupted by the scheduler. This is because I never recreate any caches in ROM and BIOS, so I want them to be as long as possible. Worst case scenario, a lot of events happen in a row, and we might make 10 blocks of 1 instruction each, while we could have easily made 1 block of 10 instructions, and saving a lot more cache block lookup time.&lt;/p&gt;
&lt;p&gt;If we end the block because &lt;code&gt;Step&lt;/code&gt; returned &lt;code&gt;true&lt;/code&gt;, we save the block unless there are no instructions in it. Normally, you’d say this cannot happen. But since the ARM7TDMI has a pipeline that can hold instructions that might be overwritten in memory, it can! I might have started a block with instructions in the pipeline and ended it before it was empty. We don’t want wrong instructions in our cache block, so I cannot add those pipelined instructions, and the block will be invalid.&lt;/p&gt;
&lt;p&gt;And then there is the normal running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (true) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (unlikely(Scheduler&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;ShouldDoEvents())) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Scheduler&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;DoEvents();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// if Step returns true, we are in a region where we can generate a cache
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (Step&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;false&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Basically the same as the classic interpreter loop. The entire run function becomes this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ARM7TDMI&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Run() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt;(true) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CurrentCache &lt;span style="color:#f92672"&gt;=&lt;/span&gt; GetCache(corrected_pc);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (unlikely(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;CurrentCache)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// nullptr: no cache (not iWRAM / ROM / BIOS)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// run interpreter normally, without recording cache
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (true) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (unlikely(Scheduler&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;ShouldDoEvents())) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Scheduler&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;DoEvents();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// if Step returns true, we are in a region where we can generate a cache
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (Step&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;false&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;if&lt;/span&gt; (unlikely(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;CurrentCache))) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// possible cache, but none present
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// make new one
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (ARMMode) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; access_time &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Memory&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;GetAccessTime&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;u32&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;static_cast&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;MemoryRegion&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(pc &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;24&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;CurrentCache &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;make_unique&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;InstructionCache&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(access_time, true, &lt;span style="color:#66d9ef"&gt;static_cast&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;MemoryRegion&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(pc &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;24&lt;/span&gt;) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; MemoryRegion&lt;span style="color:#f92672"&gt;::&lt;/span&gt;iWRAM);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; access_time &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Memory&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;GetAccessTime&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;u16&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;static_cast&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;MemoryRegion&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(pc &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;24&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;CurrentCache &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;make_unique&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;InstructionCache&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(access_time, false, &lt;span style="color:#66d9ef"&gt;static_cast&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;MemoryRegion&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(pc &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;24&lt;/span&gt;) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; MemoryRegion&lt;span style="color:#f92672"&gt;::&lt;/span&gt;iWRAM);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; RunMakeCache();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// cache possible and present
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; RunCache();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That is essentially it for making and running caches. There is one last, important thing we need to account for though: writes to iWRAM. When a write to iWRAM happens, it’s possible that it’s overwriting code that was there before. When we jump to an address, we don’t want to be running code from our caches that is not there anymore! So, when a write to iWRAM happens, we have to clear a section of our caches in iWRAM. We could do this naively, and loop over all 128 entries in the block of the write, check if there is a cache and destroy it. This means a lot of checking! Something else we can do, is whenever we make a cache block, store the index of that cache block to a cache block page table. Then when a write happens to iWRAM, we just have to look up what page this is in (only a shift and a mask), and then destroy the blocks at the indices that are in the page table. This is what the&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;iWRAMCacheFilled[(address &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; (Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;iWRAMSize &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;InstructionCacheBlockSizeBytes].push_back(index);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;line meant in an earlier code block. Writing to iWRAM then has the following callback:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; ARM7TDMI&lt;span style="color:#f92672"&gt;::&lt;/span&gt;iWRAMWrite(u32 address) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// clear all instruction caches in a CacheBlockSizeBytes sized region
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; u32 cache_page_index &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (address &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0x7fff&lt;/span&gt;) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;InstructionCacheBlockSizeBytes;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (u32 index : iWRAMCacheFilled[cache_page_index]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; iWRAMCache[index] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nullptr&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; iWRAMCacheFilled[cache_page_index].clear();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ((corrected_pc &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#f92672"&gt;~&lt;/span&gt;(Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;InstructionCacheBlockSizeBytes &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; (address &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#f92672"&gt;~&lt;/span&gt;(Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;InstructionCacheBlockSizeBytes &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;))) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// current block destroyed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (CurrentCache) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;CurrentCache &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nullptr&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CurrentCache &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nullptr&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, we look up the cache page (the integer division will be optimized out to a single shift, since &lt;code&gt;Mem::InstructionCacheBlockSizeBytes&lt;/code&gt; is 256 (256 bytes per cache block). We then look up in this table which indices are filled in that page. This will usually be at most 1 or 2 blocks, but will probably be 0 blocks in most cases. That’s a lot fewer loops than 128!&lt;/p&gt;
&lt;p&gt;Then we also need to check if the current block is overwritten. You might recall that we check this in the new run methods. If the address is in the same page as &lt;code&gt;pc&lt;/code&gt;, we destroy the current block, and show that there is no longer a block there by setting &lt;code&gt;CurrentCache = nullptr&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="the-pros-and-the-cons"&gt;The pros and the cons
&lt;/h3&gt;&lt;p&gt;I’ve tried it out on a few games, and got between 10-20% performance increase in most games, but some ROMs that run a lot of code from ROM (like AGS) I gained about 50% more frames per second. I said this before, but I also tried rewriting it to not dynamically allocate blocks with &lt;code&gt;unique_ptr&lt;/code&gt;, but besides being a lot more annoying in having to deal with a bump allocator, it also didn’t gain me any more speed over dynamic allocation. And on top of that, this way with dynamic allocation uses a minimal amount of memory.&lt;/p&gt;
&lt;p&gt;The reason that this dynamic allocation probably doesn’t matter performance wise is because a lot of blocks don’t have to be recreated. In the very large ROM region, blocks will always be the same, and only have to be allocated once. Only in the iWRAM region will blocks have to be recreated and allocated. The big read only code region is a major reason why this might not be the case for other systems. In a lot of systems, the entire memory range is writeable, and you would spend a lot more time allocating and deallocating memory for the blocks.&lt;/p&gt;
&lt;h3 id="bump-allocator"&gt;Bump allocator
&lt;/h3&gt;&lt;p&gt;I’ve mentioned this term before, and said that I did not use it, but I will briefly explain an approach to using a bump allocator instead. A bump allocator basically means you have a large array holding cached instructions, and every time you make a new block you push instructions to that instead of to an allocated &lt;code&gt;InstructionCache&lt;/code&gt;. The caches themselves would look a bit different as well. When I wrote one to see performance differences, I did something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;InstructionCache&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; ARM;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u8 Length;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CachedInstruction&lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; Pointer;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, the &lt;code&gt;InstructionCache&lt;/code&gt; now no longer holds the instructions, but rather a pointer to the instructions in the pre-allocated memory, and a constant of how long the block is. We would no longer have an array of &lt;code&gt;unique_ptr&amp;lt;InstructionCache&amp;gt;&lt;/code&gt;, but rather a straight up array of &lt;code&gt;InstructionCache&lt;/code&gt;s. Our &lt;code&gt;CurrentCache&lt;/code&gt; would be a &lt;code&gt;InstructionCache*&lt;/code&gt;, and to check if the cache it points to is empty, we would check if the &lt;code&gt;Length&lt;/code&gt; field is 0. You would then have something like a&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;array&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;CachedInstruction, &lt;span style="color:#ae81ff"&gt;0x0100&amp;#39;0000&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; Cache;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;u32 CacheEnd; &lt;span style="color:#75715e"&gt;// points to the first non-filled space in Cache
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;to hold the instructions. The length of this &lt;code&gt;Cache&lt;/code&gt; is a bit arbitrary, and probably something you’ll have to play around with. You want it to be big enough so that you are not constantly invalidating your cache because it’s overflowing, but small enough that your memory usage is not through the roof. &lt;code&gt;InstructionCache&lt;/code&gt; would be pointing into this &lt;code&gt;Cache&lt;/code&gt; buffer of instructions. When making a new cache, I did:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;InstructionCache ARM7TDMI&lt;span style="color:#f92672"&gt;::&lt;/span&gt;NewCache() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (CacheEnd &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; (Cache.size() &lt;span style="color:#f92672"&gt;-&lt;/span&gt; (Mem&lt;span style="color:#f92672"&gt;::&lt;/span&gt;InstructionCacheBlockSizeBytes &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// invalidate entire cache, so go over the array(s) of `InstructionCache`s and set their length to 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CacheEnd &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;InstructionCache&lt;/span&gt;(ARMMode, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;Cache[CacheEnd]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The initial check is to see if we can fit an entire new cache in the unused space of the &lt;code&gt;Cache&lt;/code&gt; array. If this is not the case, we will have to destroy all the cache blocks and just start again. And when adding new instructions, instead of pushing them back on the &lt;code&gt;InstructionCache&lt;/code&gt;, I would do&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; ARM7TDMI&lt;span style="color:#f92672"&gt;::&lt;/span&gt;BumpAlloc(CachedInstruction&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; instr) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Cache[CacheEnd&lt;span style="color:#f92672"&gt;++&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; instr;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CurrentCache&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;Length&lt;span style="color:#f92672"&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This method can be very advantagious for systems where the entire address space is writeable, as you would have to invalidate blocks anyway. For a system like the GBA, where you have a lot of read-only memory, I found that it not as good of a fit, as we wouldn’t want to be destroying blocks that will not change anyway. There might be better ways to write a bump allocator, but this is just the idea I had for it, and how I wrote it to see the difference.&lt;/p&gt;
&lt;p&gt;I could maybe have taken a hybrid approach, the dynamically allocated blocks for ROM/BIOS and a bump allocator for iWRAM, but it really won’t win much.&lt;/p&gt;
&lt;p&gt;All in all, it was not only a quest for more performance, but also a fun learning experience for trying out the idea I had for the cached interpreter. Thank you for reading!&lt;/p&gt;</description></item><item><title>AGS Aging Cartridge Test Decompilation</title><link>https://blog.dennishilhorst.nl/post/ags-aging-cartridge-test-decompilation/</link><pubDate>Mon, 11 Jan 2021 00:00:00 +0000</pubDate><guid>https://blog.dennishilhorst.nl/post/ags-aging-cartridge-test-decompilation/</guid><description>&lt;img src="https://blog.dennishilhorst.nl/post/ags-aging-cartridge-test-decompilation/images/AGSAging.webp" alt="Featured image of post AGS Aging Cartridge Test Decompilation" /&gt;&lt;p&gt;AGS Aging Cartridge is a test used by Nintendo to test GBA systems. I have used it myself mostly to see how well my emulator performed in it while developing it, but I never knew exactly what the tests did. There wasn&amp;rsquo;t much documentation on the tests other than what Normmatt had already decompiled, but this led me mostly to the names and entry points (this was especially useful!) of the tests. Therefore, I decided to decompile the tests myself, and put them in a central place to help future emulator developers!&lt;/p&gt;
&lt;p&gt;You might recognize it from the following screenshot:&lt;/p&gt;
&lt;figure&gt;
&lt;p&gt;&lt;img class="gallery-image" data-flex-basis="360px" data-flex-grow="150" height="160" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://blog.dennishilhorst.nl/post/ags-aging-cartridge-test-decompilation/images/AGSAging.webp" width="240"&gt;&lt;/p&gt;
&lt;figcaption&gt;
&lt;p&gt;AGS aging cartridge&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The code was quite clean, and most of it wasn&amp;rsquo;t too hard to decompile. The hardest test to decompile was the DMA address control test, since it was so large, it used weird inlined functions it seemed and GHidra kind of messed up the stack on that one.&lt;/p&gt;
&lt;p&gt;All tests in the suite return flags for which parts of the test the system failed, but these are never seen on the screen. In my decompilation, besides the actual code for the test, I also documented where in the ROM the test functions start, where they return, and what the return flags mean. I also added a python script to patch the ROM to write the test results to address 0004 (in the BIOS, so it won&amp;rsquo;t do anything, you can catch these writes and show print them to the console to see what the test returned).&lt;/p&gt;
&lt;p&gt;After finishing decompiling the tests that are enabled in the AGS ROM with md5 sum &lt;code&gt;9f74b2ad1d33e08e8a570ffe4564cbc3&lt;/code&gt;, I noticed that there were some tests that were unused. I also wrote a script to patch the same ROM to use the unused tests instead of some of the default tests. Some of them will freeze up a system though (I think), so they might be disabled for this reason.&lt;/p&gt;
&lt;p&gt;That patch script can also change it so all tests are disabled, or so that you can always get the &amp;ldquo;push start to aging&amp;rdquo; prompt, and the following animation, regardless of whether you pass all tests or not. Usually you would get a prompt saying &amp;ldquo;error occurred!!&amp;rdquo; if you failed any and it would freeze up.&lt;/p&gt;
&lt;p&gt;I just want to stress that it is not a full decompilation of the ROM, they are only the tests, and it was meant mostly as info for people interested in them. AGS was always kind of a black box to me, but it doesn&amp;rsquo;t have to be anymore now!&lt;/p&gt;
&lt;p&gt;The repository is &lt;a class="link" href="https://github.com/DenSinH/AGSTests" target="_blank" rel="noopener"
 &gt;here&lt;/a&gt;, feel free to check it out. All the info on the test functions is in the .h files, the .md files and of course the tests.c files for the tests in each category, corresponding to the respective folder.&lt;/p&gt;</description></item><item><title>GBA Emulator with Hardware Renderer</title><link>https://blog.dennishilhorst.nl/post/gba-emulator-with-hardware-renderer/</link><pubDate>Sun, 29 Nov 2020 00:00:00 +0000</pubDate><guid>https://blog.dennishilhorst.nl/post/gba-emulator-with-hardware-renderer/</guid><description>&lt;img src="https://blog.dennishilhorst.nl/post/gba-emulator-with-hardware-renderer/images/DSHBA_unlocked.png" alt="Featured image of post GBA Emulator with Hardware Renderer" /&gt;&lt;p&gt;After having a lot of fun writing a GBA emulator in C# a while ago, I decided it was too slow for my liking. I took the time to rewrite it in C++ with an added challenge: I wanted to do &lt;em&gt;all&lt;/em&gt; rendering on the GPU. I do some minimal parsing of video memory (mostly in OAM, but also some IO registers) to optimize GPU usage a bit, but other than that, the PPU is entirely emulated in shaders!&lt;/p&gt;
&lt;p&gt;It runs pretty fast (on my system, i7 2600 and GTX 670 I have reached framerates of over 5 to 10k fps in some menus, generally in game it reaches framerates of 800fps). It can frameskip, although that causes some graphical glitches sometimes because I might not be syncing correctly. The frameskip feature is mainly there to save GPU usage (less data transfer). I noticed this more on lower end hardware (on my laptop with intel HD graphics for example).&lt;/p&gt;
&lt;p&gt;As for accuracy: it&amp;rsquo;s fairly accurate, it passes all &amp;ldquo;sane&amp;rdquo; AGS tests (so not the prefetch buffer ones, or the ones that require cycle accuracy). It also passes a lot of endrift&amp;rsquo;s test in her mGBA suite.&lt;/p&gt;
&lt;p&gt;I spent a lot of time profiling it, checking out the assembly it generated and optimizing based on that. Some major optimizations I made are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Templated function handlers&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Faking the pipeline (saving a lot of time spent reading memory on branches and such). I &amp;ldquo;reflush&amp;rdquo; the pipeline whenever a write close to PC happens.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Faster DMAs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A better scheduler (based on a heap, and it resets its own timer so that I could use a 32 bit int clock instead of a 64 bit one. This might not sound like a big improvement, but it really did save a lot of time)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pagetables for memory reads (software fastmem)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Compiler intrinsics&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And on the graphics side, to save resources (and time spent memcpy&amp;rsquo;ing to buffer video memory), I did some stuff as well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Batching up scanlines to whether or not VRAM is &amp;ldquo;dirty&amp;rdquo; (writes happened), and only buffering the &amp;ldquo;dirty&amp;rdquo; regions of VRAM.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Object batching/palette batching (basically the same thing but for OAM or palette RAM)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To run the emulator, you need an opengl 3.3 compatible graphics card. It should be cross platform, though I haven&amp;rsquo;t tried that myself, but it works on windows for me.&lt;/p&gt;
&lt;p&gt;Anyway, &lt;a class="link" href="https://github.com/DenSinH/DSHBA" target="_blank" rel="noopener"
 &gt;here is the link to the repository&lt;/a&gt;, feel free to check it out and let me know what you think!&lt;/p&gt;</description></item></channel></rss>