# Game Boy ASM style guide

Version 1.0.

This style guide aims to formalize a style that most Game Boy ASM programmers agree on, and provide a good baseline for new programmers just starting in this field. (If that's you, welcome! 😄)

To quote the Linux kernel style guide (opens new window):

Coding style is very personal, and I won't force my views on anybody, but this is what goes for anything that I have to be able to maintain, and I'd prefer it for most other things too. Please at least consider the points made here.

Many people follow alternate style guides, and that's fine; but if you're starting to code in ASM, a clean style goes a long way to keep your code organized. Again: you don't have to do everything listed here, but please at least consider the reasons behind each bullet point.

Oh, by the way, you're free to contribute to this document (opens new window) and/or chat with us about it (opens new window)!

# Naming

RGBASM accepts a lot of symbol names (opens new window):

Symbol names can contain letters, numbers, underscores ‘_’, hashes ‘#’ and at signs ‘@’. However, they must begin with either a letter, or an underscore.

However, naming conventions make code easier to read, since they help convey the different semantics between each symbol's name.

  • Labels use PascalCase: DrawNPCs, GetOffsetFromCamera.

  • Labels in RAM (VRAM, SRAM, WRAM, HRAM; you shouldn't be using Echo RAM or OAM) use the same convention but are prefixed with the initial of the RAM they're in, in lowercase: wCameraOffsetBuffer, hVBlankFlag, vTilesetTiles, sSaveFileChecksum. Rationale: to know in which memory type the label is; this is important because VRAM and SRAM have special access precautions and HRAM can (should (must)) be accessed using the ldh instruction.

  • Local labels use camelCase, regardless of memory type: .waitVRAM, wPlayer.xCoord.

  • Macro names use snake_case: wait_vram, end_struct.


    Exception: constants that are used like labels should follow the label naming conventions. For example, see hardware.inc (opens new window)'s rXXX constants.

# Best practices

  • Avoid hardcoding things. This means:

  • Allocate space for your variables using labels (opens new window) + ds & co (opens new window) instead of equ (opens new window). This has several benefits:

    • Removing, adding, or changing the size of a variable that isn't the last one doesn't require updating every variable after it.
    • The size of each variable is obvious (ds 4) instead of having to be calculated from the addresses.
    • equ allocation is equivalent to hardcoding section addresses (see above), whereas labels are placed automatically by RGBLINK.
    • Labels support BANK() (opens new window) and many cool other features!
    • Labels are output in map and sym (opens new window) files.
  • If a file gets too big, you should split it. Files too large are harder to read and navigate. However, the splitting should stay coherent and consistent; having to jump around files constantly is equally as hard to read and navigate.

  • Unless you're making a 32k ROM (opens new window), put things in ROMX (opens new window) by default. ROM0 space is precious, and can deplete quickly; and when you run out, it's difficult to move things to ROMX.

    However, if you have code in ROM bank A refer to code or data in ROM bank B, then either should probably be moved to ROM0, or both be placed in the same bank (options for that are mentioned further above). farcall (opens new window) is a good way to make your code really spaghetti (opens new window).

  • Don't clear RAM at init! Good debugging emulators will warn you when you're reading uninitialized RAM (BGB (opens new window) has one in the option's Exceptions tab, for example), which will let you know that you forgot to initialize a variable. Clearing RAM does not fix most of these bugs, but silences the helpful warnings.

    Also, a lot of the time, variables need to get initialized to values other than 0, so clearing RAM is actually counter-productive in these cases.

# Recommendations

The difference between these and the "best practices" above is that these are more subjective, but they're still worth talking about here.

  • Historically, RGBDS has required label definitions to begin at "column 1" (i.e. no whitespace before them on their line). However, later versions (with full support added in 0.5.0) allow indenting labels (opens new window), for example to make loops stand out like in higher-level languages. However, a lot of people don't do this (opens new window), so it's up to you.

  • Please use the .asm (or .s) file extensions, not .z80. The GB CPU isn't a Z80, so syntax highlighters get it mostly right, but not quite. And it helps spreading the false idea that the GB CPU is a Z80. 😢

  • Compressing data is useful for several reasons; however, it's not necessary in a lot of cases nowadays, so you may want to only look at it after more high-priority aspects.

  • Avoid abusing macros. Macros tend to make code opaque and hard to read for people trying to help you, in addition to having side effects and sometimes leading to very inefficient code.

  • Never let the hardware draw a corrupted frame even if it's just one frame. If it's noticeable by squinting a bit, it must go.

  • Makefiles are bae (opens new window); they speed up build time by not re-processing what hasn't changed, and they can automate a lot of tedium. Writing a good Makefile can be quite daunting, but gb-boilerplate (opens new window) and gb-starter-kit (opens new window) can help you get started faster.

Game Boy is a registered trademark. This project is not endorsed by Nintendo.
© 2015-2021 gbdev contributors.