N64 Programming
Architecture
- CPU: MIPS III R4300I @ 93 MHz, access RAM must be done through through RCP. Has 32 and 64 bit instructions and a FPU. 5 stage pipeline and a cache of 16KB for instructions and 8KB for data.
- Reality Control Processor (RCP): Contains the memory controller, GPU
(RSP and RDP) and other things.
- Reality Signal Processor (RSP): MIPS co-processor with 128-bit SIMD. Fully programmable. Used for vertex data and to process audio data.
- Reality Display Processor (RDP): Rasterizer.
- 4MB of RDRAM - UMA w/ 520 MB/sec bandwidth. Can be expanded with an extra 4MB of RAM via expansion pack.
Video Features
- Hardware texture mapping
- Hardware anti-aliasing
- Hardware z-buffer
- Tri-linear filtering MIP mapping
- Perspective correction
- Environment mapping
- 16/32bit RGBA framebuffer
Video
The framebuffer (FB) is part of the DRAM so it can be writtent to in many ways:
- Directly from the CPU.
- RDP issue commands to the RSP, which writes to the FB.
- Write a programm (microcode) for the RSP that writes to the framebuffer, since the RSP is fully programmable.
- Use the DMA to move data to the FB.
If we want to use the RDP, the usual process is as follows:
- Transfer gfx data from ROM to RDRAM (CPU – N64 OS)
- Create display list in RDRAM from img data (CPU – Game application)
- Transfer display list and graphics microcode to the RSP (CPU – N64 OS)
- Conversion process (RSP – GFX microcode)
- Transfer converted data to RDP (RSP – GFX microcode)
- Manipulation process (RDP)
- Transfer manipulated data to framebuffer (RDP)
- Transfer manipulated data to the video interface (CPU – N64 OS)
- Trasfer digital data to DAC (Video Interface)
- 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:
- Find a copy of the proprietary Windows development development kit
to extract the
libultrafiles that we need to link to access the N64 “OS” functionality. This is the approach used by most commercial games. - 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 withlibdragon.
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:
- Download and compile
gcc,binutlisandnewlibfor amips32-elftarget with avr4300CPU. I followed this wiki from the N64brew community. - 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 $@