Enhance! Replacing GGSound with Sabre

Strap in, this is a big one! This is a step-by-step guide on how to replace GGSound, the sound engine NESMaker uses by default, with CutterCross’ Sabre, an even lighter-weight sound driver with more features, into your NESMaker project.

…Why?

There are quite a few advantages towards using Sabre instead of GGSound. On the upside, Sabre…

  • …uses less ROM space
  • …needs less allocated RAM variables
  • …supports more effects (like C00, D00, Fxx and Zxx)
  • …has support for linear counter trill on the triangle channel
  • …allows sound effects to use up to four channels, instead of two
  • …makes it easier to spread music over multiple banks (but that’s not covered in this post)

On the downside though, it’ll take some time and work to implement it in your project. But no worries! This post will guide you through the process.

Before I start, I’d suggest you use a fresh NESMaker folder, because you’ll need to alter some of the base scripts. Also, note that this tutorial is written for NESMaker version 4.5.6 and newer. Furthermore, this guide presumes that you are already familiar with FamiTracker text files. Lastly, this guide is written with unmodified base assembly scripts in mind; if you have edited your base scripts before, the filenames and/or line numbers may be off.

Alas, without further ado, here’s the step-by-step guide!

Glossary

  1. Download Sabre
  2. Download and install Python
  3. Add Sabre files to your project
  4. Add/update references to Sabre
  5. Remove GGSound variables
  6. Convert, copy and add your sound files
  7. Assign songs and sound effects

1) Download Sabre

First, if you haven’t done it yet, you’ll need to download the Sabre sound driver from GitHub.
You can get the repository by cloning it to your development environment:
git clone https://github.com/CutterCross/Sabre.git

If you’re not comfortable, or have no experience with using Git, you can also download the repository from https://github.com/CutterCross/Sabre by clicking <> Code, then clicking Download ZIP. Extract the ZIP file to a folder of choice.

2) Download and install Python

To convert your Famitracker text file to Sabre-compatible assembly, there’s a Python script available in the repository. You’ll need Python 3.7 or above to be able to run that script though, so go get that as well if you haven’t yet.

3) Add Sabre files to your project

Once you’ve downloaded everything you need, you can copy the Sabre files you need from the (cloned or downloaded) Sabre folder into your NESMaker folder. To do so, go to the GameEngineData folder and create a folder called Sabre. Now copy the following files into this folder from the original Sabre folder:

  • Sabre/Sabre_asm6/SabreFiles/sabre.asm
  • Sabre/Sabre_asm6/SabreFiles/sabre_Misc_RAM.asm
  • Sabre/Sabre_asm6/SabreFiles/sabre_ZP_RAM.asm
  • Sabre/Sabre_asm6/SabreFiles/sabre_includes.asm

4) Add/update references to Sabre

Now we’ve got the files in place, let’s add them to the NESMaker assembly files. The following files, which can be found in the /GameEngineData/Routines/BASE_4_5/ folder, need to be edited.

4a. Open the file Game/Initialization.asm, and replace lines 106-124 (the part where currently GGSound is being initialized) with the following lines:

    LDA #REGION_NTSC ;; replace with PAL/TENDY if applicable
    STA soundRegion
    JSR sabre_initAPU

4b. Open the file System/BankData/Bank1B.asm and find where the subroutine doHandleUpdateMusic is located (probably high up at line 3). Replace its contents with this file. We’ll come back to this file later to add the actual sound track as well.

4c. Open the file System/Base.asm. Find the line that says .include "Sound\ggsound.inc" (line 8) and replace that with .include Sabre\sabre_includes.asm. Also, just before the vectors are included (line 25), add the following two lines:

.include "Sabre\soundfile_static.asm"
.include "Sabre\soundfile_dpcm.asm"

4d. Open the file System/MemoryMap.asm. Find the line that says .include GameData/ZP_RAM.asm and right underneath, add this line: .include "Sabre/sabre_ZP_RAM.asm". Also, replace line 25 (which says .include ROOT\System\ggsound_ram.asm) with: .include "Sabre/sabre_Misc_RAM.asm".

4e. Finally, in the file System/NMI.asm, replace the line that says JSR doSoundEngineUpdate (line 201) with: JSR sabre_soundUpdate.

5) Remove GGSound variables

We have just replaced GGSound’s source code with Sabre, but there’s still a few GGSound ornaments to get rid of. By default, NESMaker has some GGSound-related variables in its project settings.  To clear up the much-needed RAM space, open up your Project Settings, click on the Zero Page RAM tab, and remove the following variables:

  • sound_region
  • sound_disable_update
  • sound_local_byte_0, sound_local_byte_1, sound_local_byte_2
  • sound_local_word_0, sound_local_word_1, sound_local_word_2
  • sound_param_byte_0, sound_param_byte_1, sound_param_byte_2
  • sound_param_word_0, sound_param_word_1, sound_param_word_2, sound_param_wrod_3 [sic]
  • base_address_instruments
  • base_address_note_table_lo, base_address_note_table_hi
  • apu_data_ready
  • apu_square_1_old, apu_square_2_old
  • song_list_address
  • sfx_list_address
  • song_address
  • apu_register_sets

Removing these variables will free up 57 bytes (or about 22%) of Zero Page RAM, so it is definitely worth the hassle.

6) Convert, copy and add your sound files

Right now, everything in your project is set up to use Sabre! Now it’s time to get the music itself into your game. First, we need to convert the exported FamiTracker .txt file. The easiest way to do this is by locating the .txt file in an explorer window, and then opening the original Sabre repository folder in another window. In this folder, you’ll find a file called sabre_ft_txt.py. This is the script that converts your text file. Now simply drag your .txt file and drop it on the .py file. If everything goes right and there are no incompatibilities or errors in your .txt file, there will be two or three new files alongside the .txt file:

  • soundfile_static.asm
  • soundfile_default.asm
  • soundfile_dpcm.asm – this file will only show up if you’re using DPCM samples.

…where soundfile is the name of your Famitracker .txt file.
Copy these .asm files and paste them in the NESMaker Sabre folder, /GameEngineData/Sabre.

We’re almost there! We only need to change the filename in the base files. First, open System/BankData/Bank1B.asm and scroll all the way to the bottom. On the last line, replace soundfile_default.asm with the correct _default.asm filename. Then, open System/Base.asm and go to the line which says .include "Sabre\soundfile_static.asm" (line 27). Replace the filename with the actual name here as well. Finally, if you don’t use DPCM samples, you can remove the line underneath (.include "Sabre\soundfile_dpcm.asm", line 28). If you do use DPCM, you’ll need to replace this filename with the correct one as well.

7) Assign songs and sound effects

Assigning songs: In the NESMaker UI, right-click Sound in the project workspace (the left column), select Import FamiTracker .txt, and import the FamiTracker .txt file, just like you would have before replacing the sound engine. This will not actually import the .txt file anymore, since we’ve gutted that from the source code in step 4. However, this does allow you to assign songs to screens in the NESMaker UI the way you would’ve before.

Assigning sfx: By default, if you want a sound effect to play, you would use something like this:
PlaySound #sfx_theName

With Sabre, the only difference is that the sound effect name must be preceded by an extra underscore, i.e.:
PlaySound #_sfx_theName

That’s it!

You’ve done it! Grab yourself a coffee, beer or whatever beverage you prefer, take a quick little breather, and have fun adding songs and sounds with more functionality and less bytes!