N64 Programming


Architecture

Video Features

Video

The framebuffer (FB) is part of the DRAM so it can be writtent to in many ways:

If we want to use the RDP, the usual process is as follows:

  1. Transfer gfx data from ROM to RDRAM (CPU – N64 OS)
  2. Create display list in RDRAM from img data (CPU – Game application)
  3. Transfer display list and graphics microcode to the RSP (CPU – N64 OS)
  4. Conversion process (RSP – GFX microcode)
  5. Transfer converted data to RDP (RSP – GFX microcode)
  6. Manipulation process (RDP)
  7. Transfer manipulated data to framebuffer (RDP)
  8. Transfer manipulated data to the video interface (CPU – N64 OS)
  9. Trasfer digital data to DAC (Video Interface)
  10. Video signal is displayed.

In theory is possible to write to the FB using both the RDP and direct CPU writing, but my initial experiments have been unsuccessful, maybe due to cache or other things. Since I can’t easily test this in real hardware I also don’t know if some of these issues are caused by incorrect emulation.

Tools

Programming for the N64 can be a bit of a pain. On the one hand, testing in hardware is limited to inaccessible development kits and expensive Everdrive64 devices. At this point I’m not ready to spend a chunk of money for small N64 hobby projects, so I mostly test my programs with emulators.

If you want to develop for the N64, you currently have two realistic options:

  1. Find a copy of the proprietary Windows development development kit to extract the libultra files that we need to link to access the N64 “OS” functionality. This is the approach used by most commercial games.
  2. Use libdragon, an open source Nintendo SDK. It has a much modern toolset and probably much easier to develop for. Unfortunately, not all emulators support programs developed with libdragon.

In all likelihood you want to use this modern n64sdk, but before I discovered this I had already found the official windows SDK and setup my dev environment. Here is what I did:

  1. Download and compile gcc, binutlis and newlib for a mips32-elf target with a vr4300 CPU. I followed this wiki from the N64brew community.
  2. Download and compile spicy and makemask, which I actually had to modify to make it work properly. These are necessary to create bootable n64 roms, although ideally I would get rid of them with some custom code to handle this process and avoid dependencies altogether.

This is the Makefile I use for my initial experiments. It supports out of source builds so that I can clean up the build artifacts easier and keep my source tree organized:

.POSIX:
.SUFFIXES:
.PHONY: main run clean

# Paths for SDK.
SDK_BASE     := /opt/n64sdk
SDK_BIN      := $(SDK_BASE)/bin
LIBULTRA_DIR := $(SDK_BASE)/libultra
LIBULTRA_INC := $(LIBULTRA_DIR)/usr/include
LIBULTRA     := $(LIBULTRA_DIR)/usr/lib/libgultra.a

# Source code location and files to watch for changes.
SRC_DIR     := src
BUILD_DIR   := build
SRC_MAIN    := $(SRC_DIR)/main.c
SRC_OBJ     :=
OBJECTS     := $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(SRC_OBJ))

WATCH_SRC   := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h")
INC_DIRS    := $(shell find $(SRC_DIR) -type d)
INC_DIRS    += $(LIBULTRA_INC)
INC_FLAGS   := $(addprefix -I,$(INC_DIRS))

# Output names and executables.
TARGET := uxn64
ELF    := $(BUILD_DIR)/$(TARGET).elf
BIN    := $(BUILD_DIR)/$(TARGET).n64

# Main compilation tool paths.
CC       := $(SDK_BIN)/mips32-elf-gcc
LD       := $(SDK_BIN)/mips32-elf-ld
AS       := $(SDK_BIN)/mips32-elf-as
OBJDUMP  := $(SDK_BIN)/mips32-elf-objdump
SPICY    := $(SDK_BIN)/spicy
MAKEMASK := $(SDK_BIN)/makemask

# Compiler and linker configuration.
CFLAGS         := -Wall -Wextra -pedantic
CFLAGS         += -mabi=32 -mfix4300
CFLAGS         += -ffreestanding -G 0
CFLAGS         += $(INC_FLAGS)
CFLAGS         += -DF3DEX_GBI_2 -nostdlib -r
LDFLAGS        := -nostdlib -r
LDLIBS         := $(LIBULTRA) $(SDK_BASE)/lib/gcc/mips32-elf/13.0.0/libgcc.a
RELEASE_CFLAGS := -O2 -DNDEBUG -D_FINALROM
DEBUG_CFLAGS   := -O0 -DDEBUG -D_FINALROM

# Setup debug/release builds.
#     make clean && make <target> DEBUG=0
#     make clean && make <target> DEBUG=1
DEBUG ?= 0
ifeq ($(DEBUG), 1)
    CFLAGS += $(DEBUG_CFLAGS)
else
    CFLAGS += $(RELEASE_CFLAGS)
endif

main: $(BUILD_DIR) $(BIN)

$(ELF): $(SRC_MAIN) $(WATCH_SRC)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $(ELF) $(SRC_MAIN) $(LDLIBS)

$(BIN): $(ELF) $(OBJECTS) $(WATCH_SRC)
    $(SPICY) -r $@ $(SRC_DIR)/spec \
        --as_command="$(SDK_BIN)/mips32-elf-as" \
        --cpp_command="$(SDK_BIN)/mips32-elf-gcc" \
        --ld_command="$(SDK_BIN)/mips32-elf-ld" \
        --objcopy_command="$(SDK_BIN)/mips32-elf-objcopy"
    rm a.out
    $(MAKEMASK) $(BIN)

# Test the output .n64 in an emulator.
run: $(BUILD_DIR) $(BIN)
    mupen64plus $(BIN)

# Remove build directory.
clean:
    rm -rf $(BUILD_DIR)

# Create the build directory.
$(BUILD_DIR):
    mkdir -p $(BUILD_DIR)

# Inference rules for C files.
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
    $(CC) $(CFLAGS) $< -o $@

If you prefer a simpler version, I got the demos from the CD to compile with:

SDK_BASE := /opt/n64sdk/
LIBULTRA := $(SDK_BASE)/libultra

CFLAGS := -mabi=32 -ffreestanding -mfix4300 -G 0 -I$(LIBULTRA)/usr/include
LIBS := $(LIBULTRA)/usr/lib/libgultra.a
LIBS += $(SDK_BASE)/lib/gcc/mips32-elf/13.0.0/libgcc.a
TARGET := onetri.n64

CC := $(SDK_BASE)/bin/mips32-elf-gcc
LD := $(SDK_BASE)/bin/mips32-elf-ld

CFLAGS += -DF3DEX_GBI_2
CFLAGS += -O2 -DNDEBUG -D_FINALROM
N64LIB = ultra_rom

codefiles = onetri.c dram_stack.c rdp_output.c
codeobjects = $(codefiles:.c=.o)
datafiles = static.c cfb.c rsp_cfb.c
dataobjects = $(datafiles:.c=.o)

$(TARGET): spec codesegment.o $(dataobjects)
    $(SDK_BASE)/bin/spicy -r $@ spec \
        --as_command="$(SDK_BASE)/bin/mips32-elf-as" \
        --cpp_command="$(SDK_BASE)/bin/mips32-elf-gcc" \
        --ld_command="$(SDK_BASE)/bin/mips32-elf-ld" \
        --objcopy_command="$(SDK_BASE)/bin/mips32-elf-objcopy"
    $(SDK_BASE)/bin/makemask $(TARGET)

codesegment.o: $(codeobjects)
    $(LD) -nostdlib -r -o $@ $^ $(LIBS)

run: $(TARGET)
    mupen64plus $(TARGET)

clean:
    rm -f $(codeobjects) $(dataobjects) codesegment.o $(TARGET)
.PHONY: clean

Using linker scripts

To compile and link a valid N64 roms the official N64SDK relied on two executables mild.exe and makemask.exe and we can use the open source equivalents spicy and makemask. However I’ve found these tools to not work right off the bat. Luckily, spicy can be easily replaced with linker scripts and a dash of assembly. Details on the process can be found here.

Here is an example of a working makefile for this purpose:

.POSIX:
.SUFFIXES:
.PHONY: main run clean

# Paths for SDK.
SDK_BASE     := /opt/n64sdk
SDK_BIN      := $(SDK_BASE)/bin
LIBULTRA_DIR := $(SDK_BASE)/libultra
LIBULTRA_INC := $(LIBULTRA_DIR)/usr/include
LIBULTRA     := $(LIBULTRA_DIR)/usr/lib/libgultra.a

# Source code location and files to watch for changes.
SRC_DIR     := src
BUILD_DIR   := build
SRC_MAIN    := $(SRC_DIR)/main.c $(SRC_DIR)/entry.s
SRC_LN      := $(SRC_DIR)/linker.ld
SRC_OBJ     :=
OBJECTS     := $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(SRC_OBJ))
WATCH_SRC   := $(shell find $(SRC_DIR) \
               -name "*.c" -or \
               -name "*.s" -or \
               -name "*.h" -or \
               -name "**.ld")
INC_DIRS    := $(shell find $(SRC_DIR) -type d)
INC_DIRS    += $(LIBULTRA_INC)
INC_FLAGS   := $(addprefix -I,$(INC_DIRS))

# Output names and executables.
TARGET := uxn64
ELF    := $(BUILD_DIR)/$(TARGET).elf
BIN    := $(BUILD_DIR)/$(TARGET).n64

# Main compilation tool paths.
CC       := $(SDK_BIN)/mips32-elf-gcc
LD       := $(SDK_BIN)/mips32-elf-ld
AS       := $(SDK_BIN)/mips32-elf-as
OBJDUMP  := $(SDK_BIN)/mips32-elf-objdump
OBJCOPY  := $(SDK_BIN)/mips32-elf-objcopy
MAKEMASK := $(SDK_BIN)/makemask

# Compiler and linker configuration.
CFLAGS         := -Wall -Wextra -pedantic
CFLAGS         += -mabi=32 -mfix4300
CFLAGS         += -ffreestanding -G 0
CFLAGS         += $(INC_FLAGS)
CFLAGS         += -DF3DEX_GBI_2
LDFLAGS        := -nostdlib
LDLIBS         := $(LIBULTRA) $(SDK_BASE)/lib/gcc/mips32-elf/13.0.0/libgcc.a
RELEASE_CFLAGS := -O2 -DNDEBUG -D_FINALROM
DEBUG_CFLAGS   := -O0 -DDEBUG -D_FINALROM

# Setup debug/release builds.
#     make clean && make <target> DEBUG=0
#     make clean && make <target> DEBUG=1
DEBUG ?= 0
ifeq ($(DEBUG), 1)
    CFLAGS += $(DEBUG_CFLAGS)
else
    CFLAGS += $(RELEASE_CFLAGS)
endif

main: $(BIN)

$(ELF): $(SRC_MAIN) $(WATCH_SRC) | $(BUILD_DIR)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $(ELF) -T $(SRC_LN) $(SRC_MAIN) $(LDLIBS)

$(BIN): $(ELF) $(OBJECTS) $(WATCH_SRC) | $(BUILD_DIR)
    $(OBJCOPY) -O binary $< $@
    $(MAKEMASK) $(BIN)

# Test the output .n64 in an emulator.
run: $(BIN)
    mupen64plus $(BIN)

# Remove build directory.
clean:
    rm -rf $(BUILD_DIR)

# Create the build directory.
$(BUILD_DIR):
    mkdir -p $(BUILD_DIR)

# Inference rules for C files.
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR)
    $(CC) $(CFLAGS) $< -o $@

# Inference rules for assembly files.
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.s | $(BUILD_DIR)
    $(AS) $(AFLAGS) -o $@ -c $<

The linker script sets the right memory regions, rom header, stacks and elf sections:

ENTRY(_start)

STACK_SIZE = 8K;

MEMORY {
    rom (R) : ORIGIN = 0, LENGTH = 64M
    ram (RWX) : ORIGIN = 0x80000400, LENGTH = 4M - 0x400
}

SECTIONS {
    .header : {
        LONG(0x80371240)
        LONG(0x0000000f)
        LONG(0x80000400)
        LONG(0x0000144c)
        . = 0x1000;
    } >rom

    .text : {
        _text_start = .;
        *(.text.entry)
        *(.text .text.*)
        *(.rodata .rodata.*)
        *(.data .data.*)
        _text_end = .;
    } >ram AT>rom

    .bss (NOLOAD) : ALIGN(16) {
        _bss_start = .;
        *(.bss .bss.*)
        *(COMMON)
        *(.scommon .scommon.*)
        _bss_end = .;
    } >ram

    .stack (NOLOAD) : ALIGN(16) {
        . += STACK_SIZE;
        . = ALIGN(8);
        _boot_stack = .;
        _main_thread_stack = .;

        . += STACK_SIZE;
        . = ALIGN(8);
        _idle_thread_stack = .;
    } >ram

    /DISCARD/ : {
        *(*)
    }
}

And this bit of assembly handles the primary initialization and jumping to the initial function in our C code:

# Entry point for the ROM image.
        .section .text.entry
        .global _start
_start:
        # Set up the stack
        la      $sp, _boot_stack

        # Zero BSS.
        la      $a0, _bss_start
        la      $a1, _bss_end
        subu    $a1, $a1, $a0
        jal     bzero

        # Jump to C entry point.
        j       boot

Creating a valid rom

As mentioned before, to create a valid N64 rom, we need to use makemask to inject the boot data code and a checksum of our program in file offsets 0x40 and 0x10 respectively. To avoid mixing up multiple codebases, I wrote a minimal alternative to this tool in C:

// src/bootdata.c
const u8 bootdata[] = {
    0x40, 0x80, 0x68, 0x00, 0x40, 0x80, 0x48, 0x00,
    0x40, 0x80, 0x58, 0x00, 0x3c, 0x08, 0xa4, 0x70,
    0x25, 0x08, 0x00, 0x00, 0x8d, 0x09, 0x00, 0x0c,
    0x15, 0x20, 0x00, 0xed, 0x00, 0x00, 0x00, 0x00,
    0x27, 0xbd, 0xff, 0xe8, 0xaf, 0xb3, 0x00, 0x00,
    0xaf, 0xb4, 0x00, 0x04, 0xaf, 0xb5, 0x00, 0x08,
    0xaf, 0xb6, 0x00, 0x0c, 0xaf, 0xb7, 0x00, 0x10,
    0x3c, 0x08, 0xa4, 0x70, 0x25, 0x08, 0x00, 0x00,
    0x3c, 0x0a, 0xa3, 0xf8, 0x3c, 0x0b, 0xa3, 0xf0,
    0x3c, 0x0c, 0xa4, 0x30, 0x25, 0x8c, 0x00, 0x00,
    0x34, 0x09, 0x00, 0x40, 0xad, 0x09, 0x00, 0x04,
    0x24, 0x11, 0x1f, 0x40, 0x00, 0x00, 0x00, 0x00,
    0x22, 0x31, 0xff, 0xff, 0x16, 0x20, 0xff, 0xfd,
    0x00, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, 0x08,
    0x34, 0x09, 0x00, 0x14, 0xad, 0x09, 0x00, 0x0c,
    0xad, 0x00, 0x00, 0x00, 0x24, 0x11, 0x00, 0x04,
    0x00, 0x00, 0x00, 0x00, 0x22, 0x31, 0xff, 0xff,
    0x16, 0x20, 0xff, 0xfd, 0x00, 0x00, 0x00, 0x00,
    0x34, 0x09, 0x00, 0x0e, 0xad, 0x09, 0x00, 0x00,
    0x24, 0x11, 0x00, 0x20, 0x22, 0x31, 0xff, 0xff,
    0x16, 0x20, 0xff, 0xfe, 0x34, 0x09, 0x01, 0x0f,
    0xad, 0x89, 0x00, 0x00, 0x3c, 0x09, 0x18, 0x08,
    0x35, 0x29, 0x28, 0x38, 0xad, 0x49, 0x00, 0x08,
    0xad, 0x40, 0x00, 0x14, 0x3c, 0x09, 0x80, 0x00,
    0xad, 0x49, 0x00, 0x04, 0x00, 0x00, 0x68, 0x25,
    0x00, 0x00, 0x70, 0x25, 0x3c, 0x0f, 0xa3, 0xf0,
    0x00, 0x00, 0xc0, 0x25, 0x3c, 0x19, 0xa3, 0xf0,
    0x3c, 0x16, 0xa0, 0x00, 0x00, 0x00, 0xb8, 0x25,
    0x3c, 0x06, 0xa3, 0xf0, 0x3c, 0x07, 0xa0, 0x00,
    0x00, 0x00, 0x90, 0x25, 0x3c, 0x14, 0xa0, 0x00,
    0x27, 0xbd, 0xff, 0xb8, 0x03, 0xa0, 0xf0, 0x25,
    0x3c, 0x10, 0xa4, 0x30, 0x8e, 0x10, 0x00, 0x04,
    0x3c, 0x11, 0x01, 0x01, 0x26, 0x31, 0x01, 0x01,
    0x16, 0x11, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
    0x24, 0x10, 0x02, 0x00, 0x35, 0x71, 0x40, 0x00,
    0x10, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
    0x24, 0x10, 0x04, 0x00, 0x35, 0x71, 0x80, 0x00,
    0xae, 0x2e, 0x00, 0x04, 0x25, 0xf5, 0x00, 0x0c,
    0x0d, 0x00, 0x01, 0xde, 0x00, 0x00, 0x00, 0x00,
    0x10, 0x40, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00,
    0xaf, 0xa2, 0x00, 0x00, 0x24, 0x09, 0x20, 0x00,
    0xad, 0x89, 0x00, 0x00, 0x8d, 0xeb, 0x00, 0x00,
    0x3c, 0x08, 0xf0, 0xff, 0x01, 0x68, 0x58, 0x24,
    0xaf, 0xab, 0x00, 0x04, 0x23, 0xbd, 0x00, 0x08,
    0x24, 0x09, 0x10, 0x00, 0xad, 0x89, 0x00, 0x00,
    0x3c, 0x08, 0xb0, 0x19, 0x15, 0x68, 0x00, 0x0c,
    0x00, 0x00, 0x00, 0x00, 0x3c, 0x08, 0x08, 0x00,
    0x03, 0x08, 0xc0, 0x20, 0x03, 0x30, 0xc8, 0x20,
    0x03, 0x30, 0xc8, 0x20, 0x3c, 0x08, 0x00, 0x20,
    0x02, 0xc8, 0xb0, 0x20, 0x02, 0x88, 0xa0, 0x20,
    0x00, 0x12, 0x90, 0x40, 0x22, 0x52, 0x00, 0x01,
    0x10, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
    0x3c, 0x08, 0x00, 0x10, 0x02, 0x88, 0xa0, 0x20,
    0x24, 0x08, 0x20, 0x00, 0xad, 0x88, 0x00, 0x00,
    0x8d, 0xe9, 0x00, 0x24, 0x8d, 0xfa, 0x00, 0x00,
    0x24, 0x08, 0x10, 0x00, 0xad, 0x88, 0x00, 0x00,
    0x31, 0x29, 0xff, 0xff, 0x24, 0x08, 0x05, 0x00,
    0x15, 0x28, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00,
    0x3c, 0x1b, 0x01, 0x00, 0x03, 0x5b, 0xd0, 0x24,
    0x17, 0x40, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
    0x3c, 0x08, 0x10, 0x1c, 0x35, 0x08, 0x0a, 0x04,
    0xad, 0xe8, 0x00, 0x18, 0x10, 0x00, 0x00, 0x03,
    0x3c, 0x08, 0x08, 0x0c, 0x35, 0x08, 0x12, 0x04,
    0xad, 0xe8, 0x00, 0x18, 0x3c, 0x08, 0x08, 0x00,
    0x01, 0xc8, 0x70, 0x20, 0x01, 0xf0, 0x78, 0x20,
    0x01, 0xf0, 0x78, 0x20, 0x25, 0xad, 0x00, 0x01,
    0x2d, 0xa8, 0x00, 0x08, 0x15, 0x00, 0xff, 0xc4,
    0x00, 0x00, 0x00, 0x00, 0x3c, 0x08, 0xc4, 0x00,
    0xad, 0x48, 0x00, 0x0c, 0x3c, 0x08, 0x80, 0x00,
    0xad, 0x48, 0x00, 0x04, 0x03, 0xc0, 0xe8, 0x25,
    0x00, 0x00, 0x18, 0x25, 0x8f, 0xa9, 0x00, 0x04,
    0x3c, 0x08, 0xb0, 0x09, 0x15, 0x28, 0x00, 0x16,
    0x00, 0x00, 0x00, 0x00, 0xae, 0x38, 0x00, 0x04,
    0x27, 0x35, 0x00, 0x0c, 0x8f, 0xa4, 0x00, 0x00,
    0x23, 0xbd, 0x00, 0x08, 0x24, 0x05, 0x00, 0x01,
    0x0d, 0x00, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00,
    0x8e, 0xc8, 0x00, 0x00, 0x3c, 0x08, 0x00, 0x08,
    0x01, 0x16, 0x40, 0x20, 0x8d, 0x09, 0x00, 0x00,
    0x8e, 0xc8, 0x00, 0x00, 0x3c, 0x08, 0x00, 0x08,
    0x01, 0x16, 0x40, 0x20, 0x8d, 0x09, 0x00, 0x00,
    0x3c, 0x08, 0x04, 0x00, 0x01, 0xc8, 0x70, 0x20,
    0x03, 0x30, 0xc8, 0x20, 0x3c, 0x08, 0x00, 0x10,
    0x02, 0xc8, 0xb0, 0x20, 0x10, 0x00, 0x00, 0x21,
    0xae, 0x37, 0x00, 0x04, 0x24, 0xd5, 0x00, 0x0c,
    0x8f, 0xa4, 0x00, 0x00, 0x23, 0xbd, 0x00, 0x08,
    0x24, 0x05, 0x00, 0x01, 0x0d, 0x00, 0x02, 0x90,
    0x00, 0x00, 0x00, 0x00, 0x8c, 0xe8, 0x00, 0x00,
    0x3c, 0x08, 0x00, 0x08, 0x01, 0x07, 0x40, 0x20,
    0x8d, 0x09, 0x00, 0x00, 0x3c, 0x08, 0x00, 0x10,
    0x01, 0x07, 0x40, 0x20, 0x8d, 0x09, 0x00, 0x00,
    0x3c, 0x08, 0x00, 0x18, 0x01, 0x07, 0x40, 0x20,
    0x8d, 0x09, 0x00, 0x00, 0x8c, 0xe8, 0x00, 0x00,
    0x3c, 0x08, 0x00, 0x08, 0x01, 0x07, 0x40, 0x20,
    0x8d, 0x09, 0x00, 0x00, 0x3c, 0x08, 0x00, 0x10,
    0x01, 0x07, 0x40, 0x20, 0x8d, 0x09, 0x00, 0x00,
    0x3c, 0x08, 0x00, 0x18, 0x01, 0x07, 0x40, 0x20,
    0x8d, 0x09, 0x00, 0x00, 0x3c, 0x08, 0x08, 0x00,
    0x02, 0xe8, 0xb8, 0x20, 0x00, 0xd0, 0x30, 0x20,
    0x00, 0xd0, 0x30, 0x20, 0x3c, 0x08, 0x00, 0x20,
    0x00, 0xe8, 0x38, 0x20, 0x24, 0x63, 0x00, 0x01,
    0x00, 0x6d, 0x40, 0x2a, 0x15, 0x00, 0xff, 0xc3,
    0x00, 0x00, 0x00, 0x00, 0x3c, 0x0a, 0xa4, 0x70,
    0x00, 0x12, 0x94, 0xc0, 0x3c, 0x09, 0x00, 0x06,
    0x35, 0x29, 0x36, 0x34, 0x01, 0x32, 0x48, 0x25,
    0xad, 0x49, 0x00, 0x10, 0x8d, 0x49, 0x00, 0x10,
    0x3c, 0x08, 0xa0, 0x00, 0x35, 0x08, 0x03, 0x00,
    0x3c, 0x09, 0x0f, 0xff, 0x35, 0x29, 0xff, 0xff,
    0x02, 0xc9, 0xb0, 0x24, 0xad, 0x16, 0x00, 0x18,
    0x03, 0xc0, 0xe8, 0x25, 0x27, 0xbd, 0x00, 0x48,
    0x8f, 0xb3, 0x00, 0x00, 0x8f, 0xb4, 0x00, 0x04,
    0x8f, 0xb5, 0x00, 0x08, 0x8f, 0xb6, 0x00, 0x0c,
    0x8f, 0xb7, 0x00, 0x10, 0x27, 0xbd, 0x00, 0x18,
    0x3c, 0x08, 0x80, 0x00, 0x25, 0x08, 0x00, 0x00,
    0x25, 0x09, 0x40, 0x00, 0x25, 0x29, 0xff, 0xe0,
    0x40, 0x80, 0xe0, 0x00, 0x40, 0x80, 0xe8, 0x00,
    0xbd, 0x08, 0x00, 0x00, 0x01, 0x09, 0x08, 0x2b,
    0x14, 0x20, 0xff, 0xfd, 0x25, 0x08, 0x00, 0x20,
    0x3c, 0x08, 0x80, 0x00, 0x25, 0x08, 0x00, 0x00,
    0x25, 0x09, 0x20, 0x00, 0x25, 0x29, 0xff, 0xf0,
    0xbd, 0x09, 0x00, 0x00, 0x01, 0x09, 0x08, 0x2b,
    0x14, 0x20, 0xff, 0xfd, 0x25, 0x08, 0x00, 0x10,
    0x10, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00,
    0x3c, 0x08, 0x80, 0x00, 0x25, 0x08, 0x00, 0x00,
    0x25, 0x09, 0x40, 0x00, 0x25, 0x29, 0xff, 0xe0,
    0x40, 0x80, 0xe0, 0x00, 0x40, 0x80, 0xe8, 0x00,
    0xbd, 0x08, 0x00, 0x00, 0x01, 0x09, 0x08, 0x2b,
    0x14, 0x20, 0xff, 0xfd, 0x25, 0x08, 0x00, 0x20,
    0x3c, 0x08, 0x80, 0x00, 0x25, 0x08, 0x00, 0x00,
    0x25, 0x09, 0x20, 0x00, 0x25, 0x29, 0xff, 0xf0,
    0xbd, 0x01, 0x00, 0x00, 0x01, 0x09, 0x08, 0x2b,
    0x14, 0x20, 0xff, 0xfd, 0x25, 0x08, 0x00, 0x10,
    0x3c, 0x0a, 0xa4, 0x00, 0x25, 0x4a, 0x00, 0x00,
    0x3c, 0x0b, 0xff, 0xf0, 0x3c, 0x09, 0x00, 0x10,
    0x01, 0x4b, 0x50, 0x24, 0x3c, 0x08, 0xa4, 0x00,
    0x25, 0x29, 0xff, 0xff, 0x3c, 0x0b, 0xa4, 0x00,
    0x25, 0x08, 0x04, 0xc0, 0x25, 0x6b, 0x07, 0x74,
    0x01, 0x09, 0x40, 0x24, 0x01, 0x69, 0x58, 0x24,
    0x3c, 0x09, 0xa0, 0x00, 0x01, 0x0a, 0x40, 0x25,
    0x01, 0x6a, 0x58, 0x25, 0x25, 0x29, 0x00, 0x00,
    0x8d, 0x0d, 0x00, 0x00, 0x25, 0x08, 0x00, 0x04,
    0x01, 0x0b, 0x08, 0x2b, 0x25, 0x29, 0x00, 0x04,
    0x14, 0x20, 0xff, 0xfb, 0xad, 0x2d, 0xff, 0xfc,
    0x3c, 0x0c, 0x80, 0x00, 0x25, 0x8c, 0x00, 0x00,
    0x01, 0x80, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
    0x3c, 0x0b, 0xb0, 0x00, 0x8d, 0x69, 0x00, 0x08,
    0x3c, 0x0a, 0x1f, 0xff, 0x35, 0x4a, 0xff, 0xff,
    0x3c, 0x01, 0xa4, 0x60, 0x01, 0x2a, 0x48, 0x24,
    0xac, 0x29, 0x00, 0x00, 0x3c, 0x08, 0xa4, 0x60,
    0x8d, 0x08, 0x00, 0x10, 0x31, 0x08, 0x00, 0x02,
    0x55, 0x00, 0xff, 0xfd, 0x3c, 0x08, 0xa4, 0x60,
    0x24, 0x08, 0x10, 0x00, 0x01, 0x0b, 0x40, 0x20,
    0x01, 0x0a, 0x40, 0x24, 0x3c, 0x01, 0xa4, 0x60,
    0xac, 0x28, 0x00, 0x04, 0x3c, 0x0a, 0x00, 0x10,
    0x25, 0x4a, 0xff, 0xff, 0x3c, 0x01, 0xa4, 0x60,
    0xac, 0x2a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x3c, 0x0b, 0xa4, 0x60,
    0x8d, 0x6b, 0x00, 0x10, 0x31, 0x6b, 0x00, 0x01,
    0x15, 0x60, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00,
    0x3c, 0x0b, 0xb0, 0x00, 0x8d, 0x64, 0x00, 0x08,
    0x02, 0xc0, 0x28, 0x25, 0x3c, 0x01, 0x5d, 0x58,
    0x34, 0x21, 0x8b, 0x65, 0x00, 0xa1, 0x00, 0x19,
    0x27, 0xbd, 0xff, 0xe0, 0xaf, 0xbf, 0x00, 0x1c,
    0xaf, 0xb0, 0x00, 0x14, 0x3c, 0x1f, 0x00, 0x10,
    0x00, 0x00, 0x18, 0x25, 0x00, 0x00, 0x40, 0x25,
    0x00, 0x80, 0x48, 0x25, 0x24, 0x0d, 0x00, 0x20,
    0x00, 0x00, 0x10, 0x12, 0x24, 0x42, 0x00, 0x01,
    0x00, 0x40, 0x38, 0x25, 0x00, 0x40, 0x50, 0x25,
    0x00, 0x40, 0x58, 0x25, 0x00, 0x40, 0x80, 0x25,
    0x00, 0x40, 0x30, 0x25, 0x00, 0x40, 0x60, 0x25,
    0x8d, 0x22, 0x00, 0x00, 0x00, 0xe2, 0x18, 0x21,
    0x00, 0x67, 0x08, 0x2b, 0x10, 0x20, 0x00, 0x02,
    0x00, 0x60, 0x28, 0x25, 0x25, 0x4a, 0x00, 0x01,
    0x30, 0x43, 0x00, 0x1f, 0x01, 0xa3, 0x78, 0x23,
    0x01, 0xe2, 0xc0, 0x06, 0x00, 0x62, 0x70, 0x04,
    0x01, 0xd8, 0x20, 0x25, 0x00, 0xc2, 0x08, 0x2b,
    0x00, 0xa0, 0x38, 0x25, 0x01, 0x62, 0x58, 0x26,
    0x10, 0x20, 0x00, 0x04, 0x02, 0x04, 0x80, 0x21,
    0x00, 0xe2, 0xc8, 0x26, 0x10, 0x00, 0x00, 0x02,
    0x03, 0x26, 0x30, 0x26, 0x00, 0xc4, 0x30, 0x26,
    0x25, 0x08, 0x00, 0x04, 0x00, 0x50, 0x78, 0x26,
    0x25, 0x29, 0x00, 0x04, 0x15, 0x1f, 0xff, 0xe8,
    0x01, 0xec, 0x60, 0x21, 0x00, 0xea, 0x70, 0x26,
    0x01, 0xcb, 0x38, 0x26, 0x02, 0x06, 0xc0, 0x26,
    0x03, 0x0c, 0x80, 0x26, 0x3c, 0x0b, 0xb0, 0x00,
    0x8d, 0x68, 0x00, 0x10, 0x14, 0xe8, 0x00, 0x06,
    0x00, 0x00, 0x00, 0x00, 0x8d, 0x68, 0x00, 0x14,
    0x16, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
    0x04, 0x11, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
    0x04, 0x11, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
    0x3c, 0x09, 0xa4, 0x08, 0x8d, 0x29, 0x00, 0x00,
    0x8f, 0xb0, 0x00, 0x14, 0x8f, 0xbf, 0x00, 0x1c,
    0x11, 0x20, 0x00, 0x06, 0x27, 0xbd, 0x00, 0x20,
    0x24, 0x0a, 0x00, 0x41, 0x3c, 0x01, 0xa4, 0x04,
    0xac, 0x2a, 0x00, 0x10, 0x3c, 0x01, 0xa4, 0x08,
    0xac, 0x20, 0x00, 0x00, 0x3c, 0x0b, 0x00, 0xaa,
    0x35, 0x6b, 0xaa, 0xae, 0x3c, 0x01, 0xa4, 0x04,
    0xac, 0x2b, 0x00, 0x10, 0x3c, 0x01, 0xa4, 0x30,
    0x24, 0x08, 0x05, 0x55, 0xac, 0x28, 0x00, 0x0c,
    0x3c, 0x01, 0xa4, 0x80, 0xac, 0x20, 0x00, 0x18,
    0x3c, 0x01, 0xa4, 0x50, 0xac, 0x20, 0x00, 0x0c,
    0x3c, 0x01, 0xa4, 0x30, 0x24, 0x09, 0x08, 0x00,
    0xac, 0x29, 0x00, 0x00, 0x24, 0x09, 0x00, 0x02,
    0x3c, 0x01, 0xa4, 0x60, 0x3c, 0x08, 0xa0, 0x00,
    0x35, 0x08, 0x03, 0x00, 0xac, 0x29, 0x00, 0x10,
    0xad, 0x17, 0x00, 0x14, 0xad, 0x15, 0x00, 0x0c,
    0xad, 0x13, 0x00, 0x04, 0x12, 0x60, 0x00, 0x04,
    0xad, 0x14, 0x00, 0x00, 0x3c, 0x09, 0xa6, 0x00,
    0x10, 0x00, 0x00, 0x03, 0x25, 0x29, 0x00, 0x00,
    0x3c, 0x09, 0xb0, 0x00, 0x25, 0x29, 0x00, 0x00,
    0xad, 0x09, 0x00, 0x08, 0x3c, 0x08, 0xa4, 0x00,
    0x25, 0x08, 0x00, 0x00, 0x21, 0x09, 0x10, 0x00,
    0x25, 0x08, 0x00, 0x04, 0x15, 0x09, 0xff, 0xfe,
    0xad, 0x00, 0xff, 0xfc, 0x3c, 0x08, 0xa4, 0x00,
    0x25, 0x08, 0x10, 0x00, 0x21, 0x09, 0x10, 0x00,
    0x25, 0x08, 0x00, 0x04, 0x15, 0x09, 0xff, 0xfe,
    0xad, 0x00, 0xff, 0xfc, 0x3c, 0x0b, 0xb0, 0x00,
    0x8d, 0x69, 0x00, 0x08, 0x01, 0x20, 0x00, 0x08,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x27, 0xbd, 0xff, 0x60, 0xaf, 0xb0, 0x00, 0x40,
    0xaf, 0xb1, 0x00, 0x44, 0x00, 0x00, 0x88, 0x25,
    0x00, 0x00, 0x80, 0x25, 0xaf, 0xa2, 0x00, 0x00,
    0xaf, 0xa3, 0x00, 0x04, 0xaf, 0xa4, 0x00, 0x08,
    0xaf, 0xa5, 0x00, 0x0c, 0xaf, 0xa6, 0x00, 0x10,
    0xaf, 0xa7, 0x00, 0x14, 0xaf, 0xa8, 0x00, 0x18,
    0xaf, 0xa9, 0x00, 0x1c, 0xaf, 0xaa, 0x00, 0x20,
    0xaf, 0xab, 0x00, 0x24, 0xaf, 0xac, 0x00, 0x28,
    0xaf, 0xad, 0x00, 0x2c, 0xaf, 0xae, 0x00, 0x30,
    0xaf, 0xaf, 0x00, 0x34, 0xaf, 0xb8, 0x00, 0x38,
    0xaf, 0xb9, 0x00, 0x3c, 0xaf, 0xb2, 0x00, 0x48,
    0xaf, 0xb3, 0x00, 0x4c, 0xaf, 0xb4, 0x00, 0x50,
    0xaf, 0xb5, 0x00, 0x54, 0xaf, 0xb6, 0x00, 0x58,
    0xaf, 0xb7, 0x00, 0x5c, 0xaf, 0xbe, 0x00, 0x60,
    0xaf, 0xbf, 0x00, 0x64, 0x0d, 0x00, 0x02, 0x20,
    0x00, 0x00, 0x00, 0x00, 0x26, 0x10, 0x00, 0x01,
    0x2a, 0x09, 0x00, 0x04, 0x15, 0x20, 0xff, 0xfb,
    0x02, 0x22, 0x88, 0x21, 0x00, 0x11, 0x20, 0x82,
    0x0d, 0x00, 0x02, 0x90, 0x24, 0x05, 0x00, 0x01,
    0x8f, 0xbf, 0x00, 0x64, 0x00, 0x11, 0x10, 0x82,
    0x8f, 0xb1, 0x00, 0x44, 0x8f, 0xa3, 0x00, 0x04,
    0x8f, 0xa4, 0x00, 0x08, 0x8f, 0xa5, 0x00, 0x0c,
    0x8f, 0xa6, 0x00, 0x10, 0x8f, 0xa7, 0x00, 0x14,
    0x8f, 0xa8, 0x00, 0x18, 0x8f, 0xa9, 0x00, 0x1c,
    0x8f, 0xaa, 0x00, 0x20, 0x8f, 0xab, 0x00, 0x24,
    0x8f, 0xac, 0x00, 0x28, 0x8f, 0xad, 0x00, 0x2c,
    0x8f, 0xae, 0x00, 0x30, 0x8f, 0xaf, 0x00, 0x34,
    0x8f, 0xb8, 0x00, 0x38, 0x8f, 0xb9, 0x00, 0x3c,
    0x8f, 0xb0, 0x00, 0x40, 0x8f, 0xb2, 0x00, 0x48,
    0x8f, 0xb3, 0x00, 0x4c, 0x8f, 0xb4, 0x00, 0x50,
    0x8f, 0xb5, 0x00, 0x54, 0x8f, 0xb6, 0x00, 0x58,
    0x8f, 0xb7, 0x00, 0x5c, 0x8f, 0xbe, 0x00, 0x60,
    0x03, 0xe0, 0x00, 0x08, 0x27, 0xbd, 0x00, 0xa0,
    0x27, 0xbd, 0xff, 0xe0, 0xaf, 0xbf, 0x00, 0x1c,
    0x00, 0x00, 0x48, 0x25, 0x00, 0x00, 0x58, 0x25,
    0x00, 0x00, 0x60, 0x25, 0x29, 0x9a, 0x00, 0x40,
    0x53, 0x40, 0x00, 0x18, 0x00, 0x00, 0x10, 0x25,
    0x0d, 0x00, 0x02, 0x43, 0x01, 0x80, 0x20, 0x25,
    0x58, 0x40, 0x00, 0x08, 0x29, 0x3a, 0x00, 0x50,
    0x00, 0x49, 0xd0, 0x23, 0x03, 0x4c, 0x00, 0x19,
    0x00, 0x40, 0x48, 0x25, 0x00, 0x00, 0xd0, 0x12,
    0x01, 0x7a, 0x58, 0x21, 0x00, 0x00, 0x00, 0x00,
    0x29, 0x3a, 0x00, 0x50, 0x17, 0x40, 0xff, 0xf1,
    0x25, 0x8c, 0x00, 0x01, 0x00, 0x0b, 0x20, 0x80,
    0x00, 0x8b, 0x20, 0x23, 0x00, 0x04, 0x20, 0x80,
    0x00, 0x8b, 0x20, 0x23, 0x00, 0x04, 0x20, 0x40,
    0x0d, 0x00, 0x02, 0x60, 0x24, 0x84, 0xfc, 0x90,
    0x10, 0x00, 0x00, 0x03, 0x8f, 0xbf, 0x00, 0x1c,
    0x00, 0x00, 0x10, 0x25, 0x8f, 0xbf, 0x00, 0x1c,
    0x27, 0xbd, 0x00, 0x20, 0x03, 0xe0, 0x00, 0x08,
    0x00, 0x00, 0x00, 0x00, 0x27, 0xbd, 0xff, 0xd8,
    0xaf, 0xbf, 0x00, 0x1c, 0x00, 0x00, 0x10, 0x25,
    0x0d, 0x00, 0x02, 0x90, 0x24, 0x05, 0x00, 0x02,
    0x00, 0x00, 0xf0, 0x25, 0x24, 0x1a, 0xff, 0xff,
    0xae, 0x9a, 0x00, 0x04, 0x8e, 0x83, 0x00, 0x04,
    0xae, 0x9a, 0x00, 0x00, 0xae, 0x9a, 0x00, 0x00,
    0x00, 0x00, 0xe0, 0x25, 0x00, 0x03, 0x1c, 0x02,
    0x30, 0x7a, 0x00, 0x01, 0x53, 0x40, 0x00, 0x03,
    0x27, 0x9c, 0x00, 0x01, 0x24, 0x42, 0x00, 0x01,
    0x27, 0x9c, 0x00, 0x01, 0x2b, 0x9a, 0x00, 0x08,
    0x17, 0x40, 0xff, 0xf9, 0x00, 0x03, 0x18, 0x42,
    0x27, 0xde, 0x00, 0x01, 0x2b, 0xda, 0x00, 0x0a,
    0x57, 0x40, 0xff, 0xef, 0x24, 0x1a, 0xff, 0xff,
    0x8f, 0xbf, 0x00, 0x1c, 0x27, 0xbd, 0x00, 0x28,
    0x03, 0xe0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
    0x27, 0xbd, 0xff, 0xd8, 0xaf, 0xbf, 0x00, 0x1c,
    0xaf, 0xa4, 0x00, 0x20, 0xa3, 0xa0, 0x00, 0x27,
    0x00, 0x00, 0x40, 0x25, 0x00, 0x00, 0x50, 0x25,
    0x34, 0x0d, 0xc8, 0x00, 0x00, 0x00, 0x70, 0x25,
    0x29, 0xda, 0x00, 0x40, 0x57, 0x40, 0x00, 0x04,
    0x01, 0xc0, 0x20, 0x25, 0x10, 0x00, 0x00, 0x20,
    0x00, 0x00, 0x10, 0x25, 0x01, 0xc0, 0x20, 0x25,
    0x0d, 0x00, 0x02, 0x90, 0x24, 0x05, 0x00, 0x01,
    0x0d, 0x00, 0x02, 0xb4, 0x27, 0xa4, 0x00, 0x27,
    0x0d, 0x00, 0x02, 0xb4, 0x27, 0xa4, 0x00, 0x27,
    0x93, 0xba, 0x00, 0x27, 0x24, 0x1b, 0x03, 0x20,
    0x8f, 0xa4, 0x00, 0x20, 0x03, 0x5b, 0x00, 0x19,
    0x00, 0x00, 0x40, 0x12, 0x01, 0x04, 0xd0, 0x23,
    0x07, 0x43, 0x00, 0x03, 0x03, 0x4d, 0xd8, 0x2a,
    0x00, 0x88, 0xd0, 0x23, 0x03, 0x4d, 0xd8, 0x2a,
    0x53, 0x60, 0x00, 0x04, 0x8f, 0xa4, 0x00, 0x20,
    0x03, 0x40, 0x68, 0x25, 0x01, 0xc0, 0x50, 0x25,
    0x8f, 0xa4, 0x00, 0x20, 0x01, 0x04, 0xd8, 0x2a,
    0x53, 0x60, 0x00, 0x06, 0x01, 0x4e, 0x10, 0x21,
    0x25, 0xce, 0x00, 0x01, 0x29, 0xdb, 0x00, 0x41,
    0x57, 0x60, 0xff, 0xe0, 0x29, 0xda, 0x00, 0x40,
    0x01, 0x4e, 0x10, 0x21, 0x00, 0x02, 0x10, 0x42,
    0x8f, 0xbf, 0x00, 0x1c, 0x27, 0xbd, 0x00, 0x28,
    0x03, 0xe0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
    0x27, 0xbd, 0xff, 0xd8, 0x30, 0x84, 0x00, 0xff,
    0x24, 0x1b, 0x00, 0x01, 0x38, 0x84, 0x00, 0x3f,
    0xaf, 0xbf, 0x00, 0x1c, 0x14, 0xbb, 0x00, 0x03,
    0x3c, 0x0f, 0x46, 0x00, 0x3c, 0x1a, 0x80, 0x00,
    0x01, 0xfa, 0x78, 0x25, 0x30, 0x9a, 0x00, 0x01,
    0x00, 0x1a, 0xd1, 0x80, 0x01, 0xfa, 0x78, 0x25,
    0x30, 0x9a, 0x00, 0x02, 0x00, 0x1a, 0xd3, 0x40,
    0x01, 0xfa, 0x78, 0x25, 0x30, 0x9a, 0x00, 0x04,
    0x00, 0x1a, 0xd5, 0x00, 0x01, 0xfa, 0x78, 0x25,
    0x30, 0x9a, 0x00, 0x08, 0x00, 0x1a, 0xd1, 0x00,
    0x01, 0xfa, 0x78, 0x25, 0x30, 0x9a, 0x00, 0x10,
    0x00, 0x1a, 0xd2, 0xc0, 0x01, 0xfa, 0x78, 0x25,
    0x30, 0x9a, 0x00, 0x20, 0x00, 0x1a, 0xd4, 0x80,
    0x01, 0xfa, 0x78, 0x25, 0x24, 0x1b, 0x00, 0x01,
    0x14, 0xbb, 0x00, 0x03, 0xae, 0xaf, 0x00, 0x00,
    0x3c, 0x1a, 0xa4, 0x30, 0xaf, 0x40, 0x00, 0x00,
    0x8f, 0xbf, 0x00, 0x1c, 0x27, 0xbd, 0x00, 0x28,
    0x03, 0xe0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
    0x27, 0xbd, 0xff, 0xd8, 0xaf, 0xbf, 0x00, 0x1c,
    0x24, 0x1a, 0x20, 0x00, 0x3c, 0x1b, 0xa4, 0x30,
    0xaf, 0x7a, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x25,
    0x8e, 0xbe, 0x00, 0x00, 0x24, 0x1a, 0x10, 0x00,
    0xaf, 0x7a, 0x00, 0x00, 0x24, 0x1b, 0x00, 0x40,
    0x03, 0x7e, 0xd8, 0x24, 0x00, 0x1b, 0xd9, 0x82,
    0x00, 0x00, 0xd0, 0x25, 0x03, 0x5b, 0xd0, 0x25,
    0x24, 0x1b, 0x40, 0x00, 0x03, 0x7e, 0xd8, 0x24,
    0x00, 0x1b, 0xdb, 0x42, 0x03, 0x5b, 0xd0, 0x25,
    0x3c, 0x1b, 0x00, 0x40, 0x03, 0x7e, 0xd8, 0x24,
    0x00, 0x1b, 0xdd, 0x02, 0x03, 0x5b, 0xd0, 0x25,
    0x24, 0x1b, 0x00, 0x80, 0x03, 0x7e, 0xd8, 0x24,
    0x00, 0x1b, 0xd9, 0x02, 0x03, 0x5b, 0xd0, 0x25,
    0x34, 0x1b, 0x80, 0x00, 0x03, 0x7e, 0xd8, 0x24,
    0x00, 0x1b, 0xda, 0xc2, 0x03, 0x5b, 0xd0, 0x25,
    0x3c, 0x1b, 0x00, 0x80, 0x03, 0x7e, 0xd8, 0x24,
    0x00, 0x1b, 0xdc, 0x82, 0x03, 0x5b, 0xd0, 0x25,
    0xa0, 0x9a, 0x00, 0x00, 0x8f, 0xbf, 0x00, 0x1c,
    0x27, 0xbd, 0x00, 0x28, 0x03, 0xe0, 0x00, 0x08,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x02, 0x00, 0x10, 0x01, 0x40, 0x0a, 0x00, 0x88,
    0x04, 0x40, 0x22, 0x02, 0x08, 0x10, 0x41, 0xff,
    0x08, 0x08, 0x40, 0x44, 0x01, 0x20, 0x08, 0x7f,
    0x02, 0x04, 0x10, 0x10, 0x80, 0x84, 0x04, 0x20,
    0x41, 0xfe, 0x08, 0x08, 0x40, 0x22, 0x01, 0x10,
    0x08, 0x80, 0x44, 0x04, 0x3f, 0xc0, 0x07, 0x00,
    0xc6, 0x08, 0x08, 0x80, 0x24, 0x01, 0x40, 0x02,
    0x00, 0x10, 0x00, 0x80, 0x02, 0x00, 0x90, 0x04,
    0x40, 0x41, 0x8c, 0x03, 0x80, 0x7e, 0x02, 0x0c,
    0x10, 0x10, 0x80, 0x44, 0x02, 0x20, 0x09, 0x00,
    0x48, 0x02, 0x40, 0x12, 0x01, 0x10, 0x08, 0x80,
    0x84, 0x18, 0x3f, 0x00, 0x7f, 0xe2, 0x00, 0x10,
    0x00, 0x80, 0x04, 0x00, 0x20, 0x01, 0xff, 0x08,
    0x00, 0x40, 0x02, 0x00, 0x10, 0x00, 0x80, 0x04,
    0x00, 0x3f, 0xf0, 0x7f, 0xe2, 0x00, 0x10, 0x00,
    0x80, 0x04, 0x00, 0x20, 0x01, 0xff, 0x08, 0x00,
    0x40, 0x02, 0x00, 0x10, 0x00, 0x80, 0x04, 0x00,
    0x20, 0x00, 0x07, 0x00, 0xc6, 0x08, 0x08, 0x80,
    0x24, 0x01, 0x40, 0x02, 0x00, 0x10, 0x00, 0x83,
    0xf2, 0x00, 0x90, 0x04, 0x40, 0x61, 0x8d, 0x03,
    0x88, 0x40, 0x12, 0x00, 0x90, 0x04, 0x80, 0x24,
    0x01, 0x20, 0x09, 0xff, 0xc8, 0x02, 0x40, 0x12,
    0x00, 0x90, 0x04, 0x80, 0x24, 0x01, 0x20, 0x08,
    0x07, 0x00, 0x10, 0x00, 0x80, 0x04, 0x00, 0x20,
    0x01, 0x00, 0x08, 0x00, 0x40, 0x02, 0x00, 0x10,
    0x00, 0x80, 0x04, 0x00, 0x20, 0x03, 0x80, 0x00,
    0x40, 0x02, 0x00, 0x10, 0x00, 0x80, 0x04, 0x00,
    0x20, 0x01, 0x00, 0x08, 0x00, 0x42, 0x02, 0x10,
    0x10, 0x80, 0x82, 0x08, 0x0f, 0x80, 0x40, 0x22,
    0x02, 0x10, 0x20, 0x82, 0x04, 0x20, 0x22, 0x01,
    0x20, 0x0a, 0x80, 0x62, 0x02, 0x08, 0x10, 0x20,
    0x80, 0x84, 0x02, 0x20, 0x08, 0x40, 0x02, 0x00,
    0x10, 0x00, 0x80, 0x04, 0x00, 0x20, 0x01, 0x00,
    0x08, 0x00, 0x40, 0x02, 0x00, 0x10, 0x00, 0x80,
    0x04, 0x00, 0x3f, 0xf0, 0x80, 0x0c, 0x00, 0x70,
    0x07, 0x40, 0x5a, 0x02, 0xc8, 0x26, 0x41, 0x31,
    0x11, 0x88, 0x8c, 0x28, 0x61, 0x43, 0x04, 0x18,
    0x20, 0xc0, 0x04, 0x40, 0x23, 0x01, 0x14, 0x08,
    0xa0, 0x44, 0x82, 0x22, 0x11, 0x10, 0x88, 0x44,
    0x42, 0x22, 0x09, 0x10, 0x28, 0x81, 0x44, 0x06,
    0x20, 0x10, 0x0f, 0x01, 0x86, 0x10, 0x08, 0x80,
    0x48, 0x01, 0x40, 0x0a, 0x00, 0x50, 0x02, 0x80,
    0x14, 0x00, 0x90, 0x08, 0x80, 0x43, 0x0c, 0x07,
    0x80, 0x7f, 0x82, 0x02, 0x10, 0x08, 0x80, 0x44,
    0x02, 0x20, 0x11, 0x01, 0x0f, 0xf0, 0x40, 0x02,
    0x00, 0x10, 0x00, 0x80, 0x04, 0x00, 0x20, 0x00,
    0x0f, 0x01, 0x86, 0x10, 0x08, 0x80, 0x48, 0x01,
    0x40, 0x0a, 0x00, 0x50, 0x02, 0x80, 0x14, 0x10,
    0x90, 0x48, 0x81, 0x43, 0x0c, 0x07, 0x90, 0x7f,
    0x82, 0x02, 0x10, 0x08, 0x80, 0x44, 0x02, 0x20,
    0x21, 0xfe, 0x08, 0x20, 0x40, 0x82, 0x04, 0x10,
    0x10, 0x80, 0x84, 0x02, 0x20, 0x10, 0x1f, 0x81,
    0x02, 0x10, 0x08, 0x80, 0x44, 0x00, 0x10, 0x00,
    0x70, 0x00, 0x70, 0x00, 0x40, 0x01, 0x10, 0x08,
    0x80, 0x42, 0x04, 0x0f, 0xc0, 0x7f, 0xf0, 0x10,
    0x00, 0x80, 0x04, 0x00, 0x20, 0x01, 0x00, 0x08,
    0x00, 0x40, 0x02, 0x00, 0x10, 0x00, 0x80, 0x04,
    0x00, 0x20, 0x01, 0x00, 0x40, 0x22, 0x01, 0x10,
    0x08, 0x80, 0x44, 0x02, 0x20, 0x11, 0x00, 0x88,
    0x04, 0x40, 0x22, 0x01, 0x08, 0x10, 0x40, 0x81,
    0x08, 0x07, 0x80, 0x40, 0x12, 0x00, 0x88, 0x08,
    0x40, 0x42, 0x02, 0x08, 0x20, 0x41, 0x01, 0x10,
    0x08, 0x80, 0x44, 0x01, 0x40, 0x0a, 0x00, 0x20,
    0x01, 0x00, 0x82, 0x0c, 0x10, 0x60, 0x82, 0x8a,
    0x24, 0x51, 0x22, 0x89, 0x14, 0x45, 0x14, 0x28,
    0xa1, 0x45, 0x0a, 0x28, 0x20, 0x81, 0x04, 0x08,
    0x20, 0x40, 0x11, 0x01, 0x04, 0x10, 0x20, 0x80,
    0x88, 0x02, 0x80, 0x08, 0x00, 0x40, 0x05, 0x00,
    0x44, 0x04, 0x10, 0x20, 0x82, 0x02, 0x20, 0x08,
    0x40, 0x11, 0x01, 0x08, 0x08, 0x20, 0x80, 0x88,
    0x04, 0x40, 0x14, 0x00, 0x40, 0x02, 0x00, 0x10,
    0x00, 0x80, 0x04, 0x00, 0x20, 0x01, 0x00, 0x7f,
    0xe0, 0x01, 0x00, 0x10, 0x01, 0x00, 0x08, 0x00,
    0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x04,
    0x00, 0x40, 0x04, 0x00, 0x3f, 0xf0, 0x0f, 0x80,
    0x82, 0x08, 0x08, 0x40, 0x42, 0x02, 0x10, 0x10,
    0x80, 0x84, 0x04, 0x20, 0x21, 0x01, 0x08, 0x08,
    0x40, 0x41, 0x04, 0x07, 0xc0, 0x02, 0x00, 0x30,
    0x02, 0x80, 0x04, 0x00, 0x20, 0x01, 0x00, 0x08,
    0x00, 0x40, 0x02, 0x00, 0x10, 0x00, 0x80, 0x04,
    0x00, 0x20, 0x01, 0x00, 0x0f, 0x00, 0x84, 0x08,
    0x10, 0x40, 0x80, 0x04, 0x00, 0x20, 0x02, 0x00,
    0x20, 0x02, 0x00, 0x20, 0x02, 0x00, 0x20, 0x02,
    0x00, 0x1f, 0xe0, 0x0f, 0x00, 0x84, 0x08, 0x10,
    0x40, 0x80, 0x04, 0x00, 0x40, 0x1c, 0x00, 0x10,
    0x00, 0x40, 0x02, 0x08, 0x10, 0x40, 0x81, 0x08,
    0x07, 0x80, 0x01, 0x00, 0x18, 0x00, 0xc0, 0x0a,
    0x00, 0x90, 0x04, 0x80, 0x44, 0x04, 0x20, 0x21,
    0x02, 0x08, 0x1f, 0xf8, 0x02, 0x00, 0x10, 0x00,
    0x80, 0x1f, 0x81, 0x00, 0x08, 0x00, 0x40, 0x02,
    0x00, 0x17, 0x80, 0xc2, 0x04, 0x08, 0x00, 0x40,
    0x02, 0x08, 0x10, 0x40, 0x81, 0x08, 0x07, 0x80,
    0x0f, 0x00, 0x84, 0x08, 0x10, 0x40, 0x82, 0x00,
    0x10, 0x00, 0xbc, 0x06, 0x10, 0x20, 0x41, 0x02,
    0x08, 0x10, 0x40, 0x81, 0x08, 0x07, 0x80, 0x3f,
    0xc0, 0x02, 0x00, 0x20, 0x01, 0x00, 0x10, 0x00,
    0x80, 0x04, 0x00, 0x40, 0x02, 0x00, 0x10, 0x01,
    0x00, 0x08, 0x00, 0x40, 0x02, 0x00, 0x0f, 0x00,
    0x84, 0x08, 0x10, 0x40, 0x82, 0x04, 0x08, 0x40,
    0x3c, 0x02, 0x10, 0x20, 0x41, 0x02, 0x08, 0x10,
    0x40, 0x81, 0x08, 0x07, 0x80, 0x0f, 0x00, 0x84,
    0x08, 0x10, 0x40, 0x82, 0x04, 0x10, 0x20, 0x43,
    0x01, 0xe8, 0x00, 0x40, 0x02, 0x08, 0x10, 0x40,
    0x81, 0x08, 0x07, 0x80, 0x02, 0x00, 0x10, 0x00,
    0x80, 0x04, 0x00, 0x20, 0x01, 0x00, 0x08, 0x00,
    0x40, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
    0x20, 0x01, 0x00, 0xd8, 0x06, 0xc0, 0x12, 0x01,
    0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x04, 0x40, 0x22, 0x01, 0x10, 0x08,
    0x87, 0xff, 0x04, 0x40, 0x22, 0x01, 0x10, 0x08,
    0x83, 0xff, 0x84, 0x40, 0x22, 0x01, 0x10, 0x08,
    0x80, 0xc0, 0x06, 0x00, 0x10, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x80, 0x44, 0x41, 0x24,
    0x05, 0x40, 0x1c, 0x00, 0x40, 0x07, 0x00, 0x54,
    0x04, 0x90, 0x44, 0x40, 0x20, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x20, 0x01,
    0x00, 0x08, 0x0f, 0xfe, 0x02, 0x00, 0x10, 0x00,
    0x80, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01,
    0x80, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x0f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x80, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x04,
    0x00, 0x40, 0x04, 0x00, 0x40, 0x04, 0x00, 0x40,
    0x04, 0x00, 0x40, 0x04, 0x00, 0x40, 0x04, 0x00,
    0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c,
    0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x3f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x03,
    0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x07, 0x00, 0x44, 0x04, 0x10, 0x20, 0x80, 0x04,
    0x00, 0x40, 0x04, 0x00, 0x40, 0x02, 0x00, 0x10,
    0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x07,
    0x00, 0xc6, 0x08, 0x08, 0x80, 0x24, 0x31, 0x42,
    0x4a, 0x22, 0x51, 0x22, 0x89, 0x22, 0x36, 0x10,
    0x02, 0x40, 0x21, 0x86, 0x03, 0xc0, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

// src/main.c

#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#include "types.h"
#include "bootdata.c"

#define CHECKSUM_START  0x00001000
#define CHECKSUM_LENGTH 0x00100000

#define MAX(A,B) (A) < (B) ? (B) : (A)

typedef struct Bytes {
    u8 *data;
    size_t len;
} Bytes;

typedef struct Checksum {
    union {
        u64 crc;
        u8 data[8];
    };
} Checksum;

Bytes
read_file(FILE *file) {
    Bytes bytes = {0};

    // Get file length.
    fseek(file, 0, SEEK_END);
    bytes.len = ftell(file);
    bytes.len = MAX(bytes.len, CHECKSUM_START + CHECKSUM_LENGTH);

    // Allocate memory, ensuring we have a minimum size for checksum purposes.
    bytes.data = calloc(1, bytes.len);

    // Copy file contents to memory.
    fseek(file, 0, SEEK_SET);
    fread(bytes.data, 1, bytes.len, file);
    return bytes;
}

u32
read_big_endian(u8 *data) {
    return (data[3] <<  0) |
           (data[2] <<  8) |
           (data[1] << 16) |
           (data[0] << 24);
}

u32
rol(u32 x, u32 n) {
    return (x << n) | (x >> (32 - n));
}

Checksum
calculate_crc(Bytes bytes) {
    Checksum checksum = {0};
    u32 t1 = 0xF8CA4DDC;
    u32 t2 = 0xF8CA4DDC;
    u32 t3 = 0xF8CA4DDC;
    u32 t4 = 0xF8CA4DDC;
    u32 t5 = 0xF8CA4DDC;
    u32 t6 = 0xF8CA4DDC;
    for (size_t i = CHECKSUM_START; i < (CHECKSUM_START + CHECKSUM_LENGTH); i += 4) {
        u32 num = read_big_endian(&bytes.data[i]);
        if ((t6 + num) < t6) {
            t4++;
        }
        t6 += num;
        t3 ^= num;
        u32 r = rol(num, (num & 0x1F));
        t5 += r;
        if (t2 > num) {
            t2 ^= r;
        } else {
            t2 ^= t6 ^ num;
        }
        t1 += t5 ^ num;
    }
    u64 upper = t5 ^ t2 ^ t1;
    u64 lower = t6 ^ t4 ^ t3;
    upper = read_big_endian((u8*)&upper);
    lower = read_big_endian((u8*)&lower);
    checksum.crc = upper << 32 | lower;
    return checksum;
}

void
print_usage(void) {
    printf("Usage: %s [options] <filename>\n", BIN_NAME);
    printf("\n");
    printf("\t-o <out_file.c> Path to the output file. If blank, stdout will be used.\n");
    printf("\t-v              Verbose.\n");
    printf("\t-h              Prints usage.\n");
    printf("\n");
}

int main(int argc, char *argv[]) {
    FILE *in_file  = stdin;
    FILE *out_file = stdout;
    char *in_file_path  = NULL;
    char *out_file_path = NULL;
    bool verbose = false;

    // Handle arguments and flags.
    int option;
    while ((option = getopt(argc, argv, "hvo:")) != -1) {
        switch (option) {
            case 'h': {
                print_usage();
                return EXIT_SUCCESS;
            } break;
            case 'o': {
                out_file_path = optarg;
            } break;
            case 'v': {
                verbose = true;
            } break;
            default: {
                print_usage();
                return EXIT_FAILURE;
            } break;
        }
    }
    in_file_path = argv[optind];

    // Open source file.
    if (in_file_path == NULL) {
        in_file = stdin;
    } else {
        in_file = fopen(in_file_path, "rb");
        if (in_file == NULL) {
            fprintf(stderr, "%s: can't open input file: %s\n",
                    BIN_NAME, in_file_path);
            return EXIT_FAILURE;
        }
    }

    // Open destination file.
    if (out_file_path == NULL) {
        out_file = stdout;
    } else {
        out_file = fopen(out_file_path, "wb");
        if (out_file == NULL) {
            fprintf(stderr, "%s: can't open output file: %s\n",
                    BIN_NAME, out_file_path);
            return EXIT_FAILURE;
        }
    }

    // Read source file into memory.
    Bytes bytes = read_file(in_file);

    // Write bootdata at offset 0x40 of dst file.
    memcpy(bytes.data + 0x40, bootdata, sizeof(bootdata));

    // Calculate CRC after bootdata was added on the dst file.
    Checksum checksum = calculate_crc(bytes);
    if (verbose) {
        printf("Checksum: 0x%02X%02X%02X%02X%02X%02X%02X%02X\n",
                checksum.data[0],
                checksum.data[1],
                checksum.data[2],
                checksum.data[3],
                checksum.data[4],
                checksum.data[5],
                checksum.data[6],
                checksum.data[7]);
    }

    // Write crc bytes at 0x10 offset on the dst file.
    memcpy(bytes.data + 0x10, checksum.data, 8 * sizeof(u8));

    // Save the output file.
    fwrite(bytes.data, 1, bytes.len, out_file);

    // Cleanup.
    if (in_file != stdin) {
        fclose(in_file);
    }
    if (out_file != stdout) {
        fclose(out_file);
    }
    return EXIT_SUCCESS;
}

// Makefile
.POSIX:
.SUFFIXES:
.PHONY: main run clean

# Source code location and files to watch for changes.
SRC_DIR     := src
BUILD_DIR   := build
SRC_MAIN    := $(SRC_DIR)/main.c
SRC_OBJ     :=
OBJECTS     := $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(SRC_OBJ))
WATCH_SRC   := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h")
INC_DIRS    := $(shell find $(SRC_DIR) -type d)
INC_FLAGS   := $(addprefix -I,$(INC_DIRS))

# Output names and executables.
TARGET := makerom
BIN    := $(BUILD_DIR)/$(TARGET)

# Main compilation tool paths.
CC       := gcc
LD       := ld
AS       := as
OBJDUMP  := objdump

# Compiler and linker configuration.
CFLAGS         := -Wall -Wextra -pedantic
CFLAGS         += -DBIN_NAME=\"$(TARGET)\"
CFLAGS         += $(INC_FLAGS)
LDFLAGS        :=
LDLIBS         :=
RELEASE_CFLAGS := -O2 -DNDEBUG
DEBUG_CFLAGS   := -O0 -DDEBUG -g

# Setup debug/release builds.
DEBUG ?= 0
ifeq ($(DEBUG), 1)
    CFLAGS += $(DEBUG_CFLAGS)
else
    CFLAGS += $(RELEASE_CFLAGS)
endif

main: $(BIN)

# Compile and link everything in one go.
$(BIN): $(SRC_MAIN) $(OBJECTS) $(WATCH_SRC) | $(BUILD_DIR)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $(BIN) $(SRC_MAIN) $(OBJECTS) $(LDLIBS)

# Run the program
run: $(BIN)
    ./$(BIN)

# Remove build directory.
clean:
    rm -rf $(BUILD_DIR)

# Create the build directory.
$(BUILD_DIR):
    mkdir -p $(BUILD_DIR)

# Inference rules for C files.
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR)
    $(CC) $(CFLAGS) $< -o $@

Resources