RSS About
A screenshot of colorized Game Boy assembly code taken from the rgbasm manual and displayed using the doom-one theme in the Chroma playground.

Color coded

As someone who writes a lot of code, for work or for fun, syntax highlighting is something I can hardly do without nowadays. Possibly because the first lines of code I ever wrote looked somewhat like this:

A screenshot of Amstrad CPC Basic code in bright yellow characters on a dark blue background.
I had no idea what an editor — or even a file — was back then.

Yes, I’m old.

Anyway!

Highlighting arguably makes code easier to read and understand. So, of course, when I started writing about code itself, I really wanted it to be colored. The WordPress installation I used back then had some plugin for that, but now it’s even easier since Markdown has a handy shorthand for highlighting code:

A screenshot of vscode showing a section of highlighted Go code in a Markdown file.
It really helps that my code editor also knows how to highlight code in triple backticks.

It doesn’t quite work natively, though, there needs to be some tool to highlight that code. The one Hugo uses is called Chroma, and it supports a couple hundred different programming languages.

Obviously, I have mostly been quoting Go code on this blog, and thanks to Chroma, it looks pretty nice1!

// Tick advances the PPU state one step. Each `case` block will eventually
// contain code that will take various amounts of ticks to complete before
// updating the PPU state.
func (p *PPU) Tick() {
    switch p.state {
    case OAMSearch:
        // TODO: collect sprite data here.
        p.state = PixelTransfer

    case PixelTransfer:
        // TODO: push pixel data to display.
        p.state = HBlank

    case HBlank:
        // TODO: wait, then go back to sprite search for next line, or vblank.
        if p.LY == 144 {
            p.state = VBlank
        } else {
            p.state = OAMSearch
        }

    case VBlank:
        // TODO: wait, then go back to sprite search for the top line.
        p.state = OAMSearch
    }
}

One of the numerous Go code excerpts used throughout this blog, borrowed from an old article.

I also showcased a little assembly code, back when we were coming up with an emulated CPU. Game Boy assembly is close enough to Z80 assembly, which is natively handled by Chroma, so I used that wherever I posted assembly. It wasn’t perfect, but it worked well enough and there was relatively little code to highlight anyway.

However, after some of my latest articles, I felt that I needed something nicer-looking. But that meant manually coloring my assembly code and somehow pasting the resulting HTML directly in my article.

Or, more elegantly, I could contribute to Chroma and add whatever code it needed to understand Game Boy assembly! It mostly involved crafting clever enough regular expressions, and I’m pretty okay at these2.

Now, assembly is somewhat annoying in that you have several flavors of it, mostly depending on what CPU you are writing code for, and which tool you use to assemble it into machine instructions. As far as I can tell, there is no “official” assembly syntax for the Game Boy CPU, and I have found at least two assemblers that can generate code for it.

So in the end, I specifically targeted the RGBDS toolchain and its assembler, which defines its own set of keywords and functions in addition to the Game Boy CPU opcodes.

It took a while because rgbasm has a lot of features. I’m really pleased with the result, though!

This is what assembly code looked like before on this blog:

INCLUDE "include/header.asm"

; RGBDS supports defining internal variables, for readability.
DEF WAIT_FRAMES EQU 60

SECTION "default", ROM0
main:
    ; Initialize frame counter here. We'll wait 60 frames each time.
    LD C, WAIT_FRAMES

    ; Subscribe to vblank interrupt by setting bit zero of register IE ($ffff)
    ; to 1.
    LD A, $01
    LDH ($ff00+$ff), A

    ; Enable interrupts so that the Game Boy CPU will jump to the `vblank` label
    ; whenever a frame is done.
    EI

lock:
    ; Stop execution here, all the work should be done from the vblank interrupt.
    HALT
    JR lock

An excerpt of Game Boy assembly code from the “Playing with Game Boy palettes” article. The code is only partially highlighted because it was interpreted as Z80 assembly, which is subtly different from the rgbasm syntax.

And this is what it looks like now:

INCLUDE "include/header.asm"

; RGBDS supports defining internal variables, for readability.
DEF WAIT_FRAMES EQU 60

SECTION "default", ROM0
main:
    ; Initialize frame counter here. We'll wait 60 frames each time.
    LD C, WAIT_FRAMES

    ; Subscribe to vblank interrupt by setting bit zero of register IE ($ffff)
    ; to 1.
    LD A, $01
    LDH [$ff00+$ff], A

    ; Enable interrupts so that the Game Boy CPU will jump to the `vblank` label
    ; whenever a frame is done.
    EI

lock:
    ; Stop execution here, all the work should be done from the vblank interrupt.
    HALT
    JR lock

This is what the same excerpt looks like when properly interpreted as rgbasm syntax, and I like it a lot better.

Thanks to the magic of open-source, I could simply fork Chroma, add support for rgbasm, rebuild it (and Hugo) locally, and then use that to generate these pages. In fact, that’s exactly the setup I’m using at the moment.

But hey, unlikely as it is, you never know, someone else might want to highlight rgbasm syntax someday! So I submitted that change to Chroma, and it has now been merged. With a bit of luck, in a near future, those changes will be integrated in Hugo and I will have rgbasm syntax highlighting built in my site generator.

I mean, I would have done it anyway for the sheer fun of it, but it also felt nice to contribute, even such a tiny bit!


  1. To me anyway. Color schemes are a pretty personal matter. ↩︎

  2. Also I have hacked together syntax highlighting for weird languages before. ↩︎