summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xMakefile22
-rwxr-xr-xasm/ewram.s4
-rwxr-xr-xasm/macros.inc1
-rwxr-xr-xasm/macros/function.inc29
-rwxr-xr-xasm/main.s6
-rwxr-xr-xasm/rom.s2
-rwxr-xr-xcharmap.txt1
-rwxr-xr-xinclude/gba/defines.h71
-rwxr-xr-xinclude/gba/flash_internal.h77
-rwxr-xr-xinclude/gba/gba.h12
-rwxr-xr-xinclude/gba/io_reg.h765
-rwxr-xr-xinclude/gba/isagbprint.h50
-rwxr-xr-xinclude/gba/m4a_internal.h467
-rwxr-xr-xinclude/gba/macro.h195
-rwxr-xr-xinclude/gba/multiboot.h55
-rwxr-xr-xinclude/gba/syscall.h48
-rwxr-xr-xinclude/gba/types.h140
-rwxr-xr-xinclude/global.h59
-rwxr-xr-xld_script.ld2
-rwxr-xr-xsrc/main.c16
-rwxr-xr-xtools/preproc/.gitignore1
-rwxr-xr-xtools/preproc/LICENSE19
-rwxr-xr-xtools/preproc/Makefile17
-rwxr-xr-xtools/preproc/asm_file.cpp529
-rwxr-xr-xtools/preproc/asm_file.h72
-rwxr-xr-xtools/preproc/c_file.cpp429
-rwxr-xr-xtools/preproc/c_file.h58
-rwxr-xr-xtools/preproc/char_util.h71
-rwxr-xr-xtools/preproc/charmap.cpp408
-rwxr-xr-xtools/preproc/charmap.h64
-rwxr-xr-xtools/preproc/preproc.cpp156
-rwxr-xr-xtools/preproc/preproc.h54
-rwxr-xr-xtools/preproc/string_parser.cpp355
-rwxr-xr-xtools/preproc/string_parser.h55
-rwxr-xr-xtools/preproc/utf8.cpp92
-rwxr-xr-xtools/preproc/utf8.h34
36 files changed, 4433 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index 48bafa3..0d71ca6 100755
--- a/Makefile
+++ b/Makefile
@@ -21,9 +21,11 @@ OBJ_DIR := build/pokepinballrs
ELF = $(ROM:.gba=.elf)
MAP = $(ROM:.gba=.map)
+C_SUBDIR = src
ASM_SUBDIR = asm
DATA_ASM_SUBDIR = data
+C_BUILDDIR = $(OBJ_DIR)/$(C_SUBDIR)
ASM_BUILDDIR = $(OBJ_DIR)/$(ASM_SUBDIR)
DATA_ASM_BUILDDIR = $(OBJ_DIR)/$(DATA_ASM_SUBDIR)
@@ -38,6 +40,7 @@ LDFLAGS = -Map ../../$(MAP)
SHA1 := $(shell { command -v sha1sum || command -v shasum; } 2>/dev/null) -c
SCANINC := tools/scaninc/scaninc$(EXE)
+PREPROC := tools/preproc/preproc$(EXE)
FIX := tools/gbafix/gbafix$(EXE)
# Clear the default suffixes
@@ -52,7 +55,10 @@ FIX := tools/gbafix/gbafix$(EXE)
.PHONY: rom clean compare tidy
-$(shell mkdir -p $(ASM_BUILDDIR) $(DATA_ASM_BUILDDIR))
+$(shell mkdir -p $(C_BUILDDIR) $(ASM_BUILDDIR) $(DATA_ASM_BUILDDIR))
+
+C_SRCS := $(wildcard $(C_SUBDIR)/*.c)
+C_OBJS := $(patsubst $(C_SUBDIR)/%.c,$(C_BUILDDIR)/%.o,$(C_SRCS))
ASM_SRCS := $(wildcard $(ASM_SUBDIR)/*.s)
ASM_OBJS := $(patsubst $(ASM_SUBDIR)/%.s,$(ASM_BUILDDIR)/%.o,$(ASM_SRCS))
@@ -60,7 +66,7 @@ ASM_OBJS := $(patsubst $(ASM_SUBDIR)/%.s,$(ASM_BUILDDIR)/%.o,$(ASM_SRCS))
DATA_ASM_SRCS := $(wildcard $(DATA_ASM_SUBDIR)/*.s)
DATA_ASM_OBJS := $(patsubst $(DATA_ASM_SUBDIR)/%.s,$(DATA_ASM_BUILDDIR)/%.o,$(DATA_ASM_SRCS))
-OBJS := $(ASM_OBJS) $(DATA_ASM_OBJS)
+OBJS := $(C_OBJS) $(ASM_OBJS) $(DATA_ASM_OBJS)
OBJS_REL := $(patsubst $(OBJ_DIR)/%,%,$(OBJS))
rom: $(ROM)
@@ -90,6 +96,18 @@ tidy:
%.rl: % ; $(GFX) $< $@
ifeq ($(NODEP),)
+$(C_BUILDDIR)/%.o: c_dep = $(shell $(SCANINC) -I include $(C_SUBDIR)/$*.c)
+else
+$(C_BUILDDIR)/%.o: c_dep :=
+endif
+
+$(C_BUILDDIR)/%.o : $(C_SUBDIR)/%.c $$(c_dep)
+ @$(CPP) $(CPPFLAGS) $< -o $(C_BUILDDIR)/$*.i
+ @$(PREPROC) $(C_BUILDDIR)/$*.i charmap.txt | $(CC1) $(CFLAGS) -o $(C_BUILDDIR)/$*.s
+ @echo -e ".text\n\t.align\t2, 0\n" >> $(C_BUILDDIR)/$*.s
+ $(AS) $(ASFLAGS) -o $@ $(C_BUILDDIR)/$*.s
+
+ifeq ($(NODEP),)
$(ASM_BUILDDIR)/%.o: asm_dep = $(shell $(SCANINC) $(ASM_SUBDIR)/$*.s)
else
$(ASM_BUILDDIR)/%.o: asm_dep :=
diff --git a/asm/ewram.s b/asm/ewram.s
index 0857a08..7c53cf0 100755
--- a/asm/ewram.s
+++ b/asm/ewram.s
@@ -5,3 +5,7 @@
.space 0x3060
ds gIntrTable @ 0x2003060
+
+.space 0x8060
+
+ds gUnknown_0200B0C0 @ 0x200B0C0
diff --git a/asm/macros.inc b/asm/macros.inc
index 75ebd12..40605d2 100755
--- a/asm/macros.inc
+++ b/asm/macros.inc
@@ -1 +1,2 @@
+ .include "asm/macros/function.inc"
.include "asm/macros/label.inc"
diff --git a/asm/macros/function.inc b/asm/macros/function.inc
new file mode 100755
index 0000000..b109595
--- /dev/null
+++ b/asm/macros/function.inc
@@ -0,0 +1,29 @@
+ .macro arm_func_start name
+ .align 2, 0
+ .global \name
+ .arm
+ .type \name, %function
+ .endm
+
+ .macro arm_func_end name
+ .size \name, .-\name
+ .endm
+
+ .macro thumb_func_start name
+ .align 2, 0
+ .global \name
+ .thumb
+ .thumb_func
+ .type \name, %function
+ .endm
+
+ .macro non_word_aligned_thumb_func_start name
+ .global \name
+ .thumb
+ .thumb_func
+ .type \name, %function
+ .endm
+
+ .macro thumb_func_end name
+ .size \name, .-\name
+ .endm
diff --git a/asm/main.s b/asm/main.s
new file mode 100755
index 0000000..54f4dd6
--- /dev/null
+++ b/asm/main.s
@@ -0,0 +1,6 @@
+ .include "asm/macros.inc"
+
+ .syntax unified
+
+ .text
+
diff --git a/asm/rom.s b/asm/rom.s
index cac6dd8..84078e0 100755
--- a/asm/rom.s
+++ b/asm/rom.s
@@ -1,3 +1,3 @@
.text
-.incbin "baserom.gba", 0x23C, 0x7FFDC4
+.incbin "baserom.gba", 0x24C, 0x7FFDB4
diff --git a/charmap.txt b/charmap.txt
new file mode 100755
index 0000000..93a85c2
--- /dev/null
+++ b/charmap.txt
@@ -0,0 +1 @@
+' ' = 00
diff --git a/include/gba/defines.h b/include/gba/defines.h
new file mode 100755
index 0000000..16f1315
--- /dev/null
+++ b/include/gba/defines.h
@@ -0,0 +1,71 @@
+#ifndef GUARD_GBA_DEFINES
+#define GUARD_GBA_DEFINES
+
+#include <stddef.h>
+
+#define TRUE 1
+#define FALSE 0
+
+#define IWRAM_DATA __attribute__((section("iwram_data")))
+#define EWRAM_DATA __attribute__((section("ewram_data")))
+
+#define ALIGNED(n) __attribute__((aligned(n)))
+
+#define SOUND_INFO_PTR (*(struct SoundInfo **)0x3007FF0)
+#define INTR_CHECK (*(u16 *)0x3007FF8)
+#define INTR_VECTOR (*(void **)0x3007FFC)
+
+#define EWRAM_START 0x02000000
+#define EWRAM_END (EWRAM_START + 0x40000)
+#define IWRAM_START 0x03000000
+#define IWRAM_END (IWRAM_START + 0x8000)
+
+#define PLTT 0x5000000
+#define PLTT_SIZE 0x400
+
+#define BG_PLTT PLTT
+#define BG_PLTT_SIZE 0x200
+
+#define OBJ_PLTT (PLTT + 0x200)
+#define OBJ_PLTT_SIZE 0x200
+
+#define VRAM 0x6000000
+#define VRAM_SIZE 0x18000
+
+#define BG_VRAM VRAM
+#define BG_VRAM_SIZE 0x10000
+#define BG_CHAR_SIZE 0x4000
+#define BG_SCREEN_SIZE 0x800
+#define BG_CHAR_ADDR(n) (BG_VRAM + (BG_CHAR_SIZE * (n)))
+#define BG_SCREEN_ADDR(n) (BG_VRAM + (BG_SCREEN_SIZE * (n)))
+
+#define BG_TILE_H_FLIP(n) (0x400 + (n))
+#define BG_TILE_V_FLIP(n) (0x800 + (n))
+
+// text-mode BG
+#define OBJ_VRAM0 (VRAM + 0x10000)
+#define OBJ_VRAM0_SIZE 0x8000
+
+// bitmap-mode BG
+#define OBJ_VRAM1 (VRAM + 0x14000)
+#define OBJ_VRAM1_SIZE 0x4000
+
+#define OAM 0x7000000
+#define OAM_SIZE 0x400
+
+#define ROM_HEADER_SIZE 0xC0
+
+#define DISPLAY_WIDTH 240
+#define DISPLAY_HEIGHT 160
+
+#define TILE_SIZE_4BPP 32
+#define TILE_SIZE_8BPP 64
+
+#define TILE_OFFSET_4BPP(n) ((n) * TILE_SIZE_4BPP)
+#define TILE_OFFSET_8BPP(n) ((n) * TILE_SIZE_8BPP)
+
+#define TOTAL_OBJ_TILE_COUNT 1024
+
+#define WIN_RANGE(a, b) (((a) << 8) | (b))
+
+#endif // GUARD_GBA_DEFINES
diff --git a/include/gba/flash_internal.h b/include/gba/flash_internal.h
new file mode 100755
index 0000000..ba84546
--- /dev/null
+++ b/include/gba/flash_internal.h
@@ -0,0 +1,77 @@
+#ifndef GUARD_GBA_FLASH_INTERNAL_H
+#define GUARD_GBA_FLASH_INTERNAL_H
+
+#define FLASH_BASE ((u8 *)0xE000000)
+
+#define FLASH_WRITE(addr, data) ((*(vu8 *)(FLASH_BASE + (addr))) = (data))
+
+#define FLASH_ROM_SIZE_1M 131072 // 1 megabit ROM
+
+#define SECTORS_PER_BANK 16
+
+struct FlashSector
+{
+ u32 size;
+ u8 shift;
+ u16 count;
+ u16 top;
+};
+
+struct FlashType {
+ u32 romSize;
+ struct FlashSector sector;
+ u16 wait[2]; // game pak bus read/write wait
+
+ // TODO: add support for anonymous unions/structs if possible
+ union {
+ struct {
+ u8 makerId;
+ u8 deviceId;
+ } separate;
+ u16 joined;
+ } ids;
+};
+
+struct FlashSetupInfo
+{
+ u16 (*programFlashByte)(u16, u32, u8);
+ u16 (*programFlashSector)(u16, u8 *);
+ u16 (*eraseFlashChip)(void);
+ u16 (*eraseFlashSector)(u16);
+ u16 (*WaitForFlashWrite)(u8, u8 *, u8);
+ const u16 *maxTime;
+ struct FlashType type;
+};
+
+extern u16 gFlashNumRemainingBytes;
+
+extern u16 (*ProgramFlashByte)(u16, u32, u8);
+extern u16 (*ProgramFlashSector)(u16, u8 *);
+extern u16 (*EraseFlashChip)(void);
+extern u16 (*EraseFlashSector)(u16);
+extern u16 (*WaitForFlashWrite)(u8, u8 *, u8);
+extern const u16 *gFlashMaxTime;
+extern const struct FlashType *gFlash;
+
+extern u8 (*PollFlashStatus)(u8 *);
+extern u8 gFlashTimeoutFlag;
+
+extern const struct FlashSetupInfo MX29L010;
+extern const struct FlashSetupInfo LE26FV10N1TS;
+extern const struct FlashSetupInfo DefaultFlash;
+
+void SwitchFlashBank(u8 bankNum);
+u16 ReadFlashId(void);
+void StartFlashTimer(u8 phase);
+void SetReadFlash1(u16 *dest);
+void StopFlashTimer(void);
+void ReadFlash(u16 sectorNum, u32 offset, u8 *dest, u32 size);
+
+u16 WaitForFlashWrite_Common(u8 phase, u8 *addr, u8 lastData);
+
+u16 EraseFlashChip_MX(void);
+u16 EraseFlashSector_MX(u16 sectorNum);
+u16 ProgramFlashByte_MX(u16 sectorNum, u32 offset, u8 data);
+u16 ProgramFlashSector_MX(u16 sectorNum, u8 *src);
+
+#endif // GUARD_GBA_FLASH_INTERNAL_H
diff --git a/include/gba/gba.h b/include/gba/gba.h
new file mode 100755
index 0000000..3493440
--- /dev/null
+++ b/include/gba/gba.h
@@ -0,0 +1,12 @@
+#ifndef GUARD_GBA_GBA_H
+#define GUARD_GBA_GBA_H
+
+#include "gba/defines.h"
+#include "gba/io_reg.h"
+#include "gba/types.h"
+#include "gba/multiboot.h"
+#include "gba/syscall.h"
+#include "gba/macro.h"
+#include "gba/isagbprint.h"
+
+#endif // GUARD_GBA_GBA_H
diff --git a/include/gba/io_reg.h b/include/gba/io_reg.h
new file mode 100755
index 0000000..03faa1a
--- /dev/null
+++ b/include/gba/io_reg.h
@@ -0,0 +1,765 @@
+#ifndef GUARD_GBA_IO_REG_H
+#define GUARD_GBA_IO_REG_H
+
+#define REG_BASE 0x4000000 // I/O register base address
+
+// I/O register offsets
+
+#define REG_OFFSET_DISPCNT 0x0
+#define REG_OFFSET_DISPSTAT 0x4
+#define REG_OFFSET_VCOUNT 0x6
+#define REG_OFFSET_BG0CNT 0x8
+#define REG_OFFSET_BG1CNT 0xa
+#define REG_OFFSET_BG2CNT 0xc
+#define REG_OFFSET_BG3CNT 0xe
+#define REG_OFFSET_BG0HOFS 0x10
+#define REG_OFFSET_BG0VOFS 0x12
+#define REG_OFFSET_BG1HOFS 0x14
+#define REG_OFFSET_BG1VOFS 0x16
+#define REG_OFFSET_BG2HOFS 0x18
+#define REG_OFFSET_BG2VOFS 0x1a
+#define REG_OFFSET_BG3HOFS 0x1c
+#define REG_OFFSET_BG3VOFS 0x1e
+#define REG_OFFSET_BG2PA 0x20
+#define REG_OFFSET_BG2PB 0x22
+#define REG_OFFSET_BG2PC 0x24
+#define REG_OFFSET_BG2PD 0x26
+#define REG_OFFSET_BG2X 0x28
+#define REG_OFFSET_BG2X_L 0x28
+#define REG_OFFSET_BG2X_H 0x2a
+#define REG_OFFSET_BG2Y 0x2c
+#define REG_OFFSET_BG2Y_L 0x2c
+#define REG_OFFSET_BG2Y_H 0x2e
+#define REG_OFFSET_BG3PA 0x30
+#define REG_OFFSET_BG3PB 0x32
+#define REG_OFFSET_BG3PC 0x34
+#define REG_OFFSET_BG3PD 0x36
+#define REG_OFFSET_BG3X 0x38
+#define REG_OFFSET_BG3X_L 0x38
+#define REG_OFFSET_BG3X_H 0x3a
+#define REG_OFFSET_BG3Y 0x3c
+#define REG_OFFSET_BG3Y_L 0x3c
+#define REG_OFFSET_BG3Y_H 0x3e
+#define REG_OFFSET_WIN0H 0x40
+#define REG_OFFSET_WIN1H 0x42
+#define REG_OFFSET_WIN0V 0x44
+#define REG_OFFSET_WIN1V 0x46
+#define REG_OFFSET_WININ 0x48
+#define REG_OFFSET_WINOUT 0x4a
+#define REG_OFFSET_MOSAIC 0x4c
+#define REG_OFFSET_BLDCNT 0x50
+#define REG_OFFSET_BLDALPHA 0x52
+#define REG_OFFSET_BLDY 0x54
+
+#define REG_OFFSET_SOUND1CNT_L 0x60
+#define REG_OFFSET_NR10 0x60
+#define REG_OFFSET_SOUND1CNT_H 0x62
+#define REG_OFFSET_NR11 0x62
+#define REG_OFFSET_NR12 0x63
+#define REG_OFFSET_SOUND1CNT_X 0x64
+#define REG_OFFSET_NR13 0x64
+#define REG_OFFSET_NR14 0x65
+#define REG_OFFSET_SOUND2CNT_L 0x68
+#define REG_OFFSET_NR21 0x68
+#define REG_OFFSET_NR22 0x69
+#define REG_OFFSET_SOUND2CNT_H 0x6c
+#define REG_OFFSET_NR23 0x6c
+#define REG_OFFSET_NR24 0x6d
+#define REG_OFFSET_SOUND3CNT_L 0x70
+#define REG_OFFSET_NR30 0x70
+#define REG_OFFSET_SOUND3CNT_H 0x72
+#define REG_OFFSET_NR31 0x72
+#define REG_OFFSET_NR32 0x73
+#define REG_OFFSET_SOUND3CNT_X 0x74
+#define REG_OFFSET_NR33 0x74
+#define REG_OFFSET_NR34 0x75
+#define REG_OFFSET_SOUND4CNT_L 0x78
+#define REG_OFFSET_NR41 0x78
+#define REG_OFFSET_NR42 0x79
+#define REG_OFFSET_SOUND4CNT_H 0x7c
+#define REG_OFFSET_NR43 0x7c
+#define REG_OFFSET_NR44 0x7d
+#define REG_OFFSET_SOUNDCNT_L 0x80
+#define REG_OFFSET_NR50 0x80
+#define REG_OFFSET_NR51 0x81
+#define REG_OFFSET_SOUNDCNT_H 0x82
+#define REG_OFFSET_SOUNDCNT_X 0x84
+#define REG_OFFSET_NR52 0x84
+#define REG_OFFSET_SOUNDBIAS 0x88
+#define REG_OFFSET_SOUNDBIAS_L 0x88
+#define REG_OFFSET_SOUNDBIAS_H 0x89
+#define REG_OFFSET_WAVE_RAM0 0x90
+#define REG_OFFSET_WAVE_RAM1 0x94
+#define REG_OFFSET_WAVE_RAM2 0x98
+#define REG_OFFSET_WAVE_RAM3 0x9c
+#define REG_OFFSET_FIFO_A 0xa0
+#define REG_OFFSET_FIFO_B 0xa4
+
+#define REG_OFFSET_DMA0 0xb0
+#define REG_OFFSET_DMA0SAD 0xb0
+#define REG_OFFSET_DMA0SAD_L 0xb0
+#define REG_OFFSET_DMA0SAD_H 0xb2
+#define REG_OFFSET_DMA0DAD 0xb4
+#define REG_OFFSET_DMA0DAD_L 0xb4
+#define REG_OFFSET_DMA0DAD_H 0xb6
+#define REG_OFFSET_DMA0CNT 0xb8
+#define REG_OFFSET_DMA0CNT_L 0xb8
+#define REG_OFFSET_DMA0CNT_H 0xba
+#define REG_OFFSET_DMA1 0xbc
+#define REG_OFFSET_DMA1SAD 0xbc
+#define REG_OFFSET_DMA1SAD_L 0xbc
+#define REG_OFFSET_DMA1SAD_H 0xbe
+#define REG_OFFSET_DMA1DAD 0xc0
+#define REG_OFFSET_DMA1DAD_L 0xc0
+#define REG_OFFSET_DMA1DAD_H 0xc2
+#define REG_OFFSET_DMA1CNT 0xc4
+#define REG_OFFSET_DMA1CNT_L 0xc4
+#define REG_OFFSET_DMA1CNT_H 0xc6
+#define REG_OFFSET_DMA2 0xc8
+#define REG_OFFSET_DMA2SAD 0xc8
+#define REG_OFFSET_DMA2SAD_L 0xc8
+#define REG_OFFSET_DMA2SAD_H 0xca
+#define REG_OFFSET_DMA2DAD 0xcc
+#define REG_OFFSET_DMA2DAD_L 0xcc
+#define REG_OFFSET_DMA2DAD_H 0xce
+#define REG_OFFSET_DMA2CNT 0xd0
+#define REG_OFFSET_DMA2CNT_L 0xd0
+#define REG_OFFSET_DMA2CNT_H 0xd2
+#define REG_OFFSET_DMA3 0xd4
+#define REG_OFFSET_DMA3SAD 0xd4
+#define REG_OFFSET_DMA3SAD_L 0xd4
+#define REG_OFFSET_DMA3SAD_H 0xd6
+#define REG_OFFSET_DMA3DAD 0xd8
+#define REG_OFFSET_DMA3DAD_L 0xd8
+#define REG_OFFSET_DMA3DAD_H 0xda
+#define REG_OFFSET_DMA3CNT 0xdc
+#define REG_OFFSET_DMA3CNT_L 0xdc
+#define REG_OFFSET_DMA3CNT_H 0xde
+
+#define REG_OFFSET_TMCNT 0x100
+#define REG_OFFSET_TMCNT_L 0x100
+#define REG_OFFSET_TMCNT_H 0x102
+#define REG_OFFSET_TM0CNT 0x100
+#define REG_OFFSET_TM0CNT_L 0x100
+#define REG_OFFSET_TM0CNT_H 0x102
+#define REG_OFFSET_TM1CNT 0x104
+#define REG_OFFSET_TM1CNT_L 0x104
+#define REG_OFFSET_TM1CNT_H 0x106
+#define REG_OFFSET_TM2CNT 0x108
+#define REG_OFFSET_TM2CNT_L 0x108
+#define REG_OFFSET_TM2CNT_H 0x10a
+#define REG_OFFSET_TM3CNT 0x10c
+#define REG_OFFSET_TM3CNT_L 0x10c
+#define REG_OFFSET_TM3CNT_H 0x10e
+
+#define REG_OFFSET_SIOCNT 0x128
+#define REG_OFFSET_SIODATA8 0x12a
+#define REG_OFFSET_SIODATA32 0x120
+#define REG_OFFSET_SIOMLT_SEND 0x12a
+#define REG_OFFSET_SIOMLT_RECV 0x120
+#define REG_OFFSET_SIOMULTI0 0x120
+#define REG_OFFSET_SIOMULTI1 0x122
+#define REG_OFFSET_SIOMULTI2 0x124
+#define REG_OFFSET_SIOMULTI3 0x126
+
+#define REG_OFFSET_KEYINPUT 0x130
+#define REG_OFFSET_KEYCNT 0x132
+
+#define REG_OFFSET_RCNT 0x134
+
+#define REG_OFFSET_JOYCNT 0x140
+#define REG_OFFSET_JOYSTAT 0x158
+#define REG_OFFSET_JOY_RECV 0x150
+#define REG_OFFSET_JOY_RECV_L 0x150
+#define REG_OFFSET_JOY_RECV_H 0x152
+#define REG_OFFSET_JOY_TRANS 0x154
+#define REG_OFFSET_JOY_TRANS_L 0x154
+#define REG_OFFSET_JOY_TRANS_H 0x156
+
+#define REG_OFFSET_IME 0x208
+#define REG_OFFSET_IE 0x200
+#define REG_OFFSET_IF 0x202
+
+#define REG_OFFSET_WAITCNT 0x204
+
+// I/O register addresses
+
+#define REG_ADDR_DISPCNT (REG_BASE + REG_OFFSET_DISPCNT)
+#define REG_ADDR_DISPSTAT (REG_BASE + REG_OFFSET_DISPSTAT)
+#define REG_ADDR_VCOUNT (REG_BASE + REG_OFFSET_VCOUNT)
+#define REG_ADDR_BG0CNT (REG_BASE + REG_OFFSET_BG0CNT)
+#define REG_ADDR_BG1CNT (REG_BASE + REG_OFFSET_BG1CNT)
+#define REG_ADDR_BG2CNT (REG_BASE + REG_OFFSET_BG2CNT)
+#define REG_ADDR_BG3CNT (REG_BASE + REG_OFFSET_BG3CNT)
+#define REG_ADDR_BG0HOFS (REG_BASE + REG_OFFSET_BG0HOFS)
+#define REG_ADDR_BG0VOFS (REG_BASE + REG_OFFSET_BG0VOFS)
+#define REG_ADDR_BG1HOFS (REG_BASE + REG_OFFSET_BG1HOFS)
+#define REG_ADDR_BG1VOFS (REG_BASE + REG_OFFSET_BG1VOFS)
+#define REG_ADDR_BG2HOFS (REG_BASE + REG_OFFSET_BG2HOFS)
+#define REG_ADDR_BG2VOFS (REG_BASE + REG_OFFSET_BG2VOFS)
+#define REG_ADDR_BG3HOFS (REG_BASE + REG_OFFSET_BG3HOFS)
+#define REG_ADDR_BG3VOFS (REG_BASE + REG_OFFSET_BG3VOFS)
+#define REG_ADDR_BG2PA (REG_BASE + REG_OFFSET_BG2PA)
+#define REG_ADDR_BG2PB (REG_BASE + REG_OFFSET_BG2PB)
+#define REG_ADDR_BG2PC (REG_BASE + REG_OFFSET_BG2PC)
+#define REG_ADDR_BG2PD (REG_BASE + REG_OFFSET_BG2PD)
+#define REG_ADDR_BG2X (REG_BASE + REG_OFFSET_BG2X)
+#define REG_ADDR_BG2X_L (REG_BASE + REG_OFFSET_BG2X_L)
+#define REG_ADDR_BG2X_H (REG_BASE + REG_OFFSET_BG2X_H)
+#define REG_ADDR_BG2Y (REG_BASE + REG_OFFSET_BG2Y)
+#define REG_ADDR_BG2Y_L (REG_BASE + REG_OFFSET_BG2Y_L)
+#define REG_ADDR_BG2Y_H (REG_BASE + REG_OFFSET_BG2Y_H)
+#define REG_ADDR_BG3PA (REG_BASE + REG_OFFSET_BG3PA)
+#define REG_ADDR_BG3PB (REG_BASE + REG_OFFSET_BG3PB)
+#define REG_ADDR_BG3PC (REG_BASE + REG_OFFSET_BG3PC)
+#define REG_ADDR_BG3PD (REG_BASE + REG_OFFSET_BG3PD)
+#define REG_ADDR_BG3X (REG_BASE + REG_OFFSET_BG3X)
+#define REG_ADDR_BG3X_L (REG_BASE + REG_OFFSET_BG3X_L)
+#define REG_ADDR_BG3X_H (REG_BASE + REG_OFFSET_BG3X_H)
+#define REG_ADDR_BG3Y (REG_BASE + REG_OFFSET_BG3Y)
+#define REG_ADDR_BG3Y_L (REG_BASE + REG_OFFSET_BG3Y_L)
+#define REG_ADDR_BG3Y_H (REG_BASE + REG_OFFSET_BG3Y_H)
+#define REG_ADDR_WIN0H (REG_BASE + REG_OFFSET_WIN0H)
+#define REG_ADDR_WIN1H (REG_BASE + REG_OFFSET_WIN1H)
+#define REG_ADDR_WIN0V (REG_BASE + REG_OFFSET_WIN0V)
+#define REG_ADDR_WIN1V (REG_BASE + REG_OFFSET_WIN1V)
+#define REG_ADDR_WININ (REG_BASE + REG_OFFSET_WININ)
+#define REG_ADDR_WINOUT (REG_BASE + REG_OFFSET_WINOUT)
+#define REG_ADDR_MOSAIC (REG_BASE + REG_OFFSET_MOSAIC)
+#define REG_ADDR_BLDCNT (REG_BASE + REG_OFFSET_BLDCNT)
+#define REG_ADDR_BLDALPHA (REG_BASE + REG_OFFSET_BLDALPHA)
+#define REG_ADDR_BLDY (REG_BASE + REG_OFFSET_BLDY)
+
+#define REG_ADDR_SOUND1CNT_L (REG_BASE + REG_OFFSET_SOUND1CNT_L)
+#define REG_ADDR_NR10 (REG_BASE + REG_OFFSET_NR10)
+#define REG_ADDR_SOUND1CNT_H (REG_BASE + REG_OFFSET_SOUND1CNT_H)
+#define REG_ADDR_NR11 (REG_BASE + REG_OFFSET_NR11)
+#define REG_ADDR_NR12 (REG_BASE + REG_OFFSET_NR12)
+#define REG_ADDR_SOUND1CNT_X (REG_BASE + REG_OFFSET_SOUND1CNT_X)
+#define REG_ADDR_NR13 (REG_BASE + REG_OFFSET_NR13)
+#define REG_ADDR_NR14 (REG_BASE + REG_OFFSET_NR14)
+#define REG_ADDR_SOUND2CNT_L (REG_BASE + REG_OFFSET_SOUND2CNT_L)
+#define REG_ADDR_NR21 (REG_BASE + REG_OFFSET_NR21)
+#define REG_ADDR_NR22 (REG_BASE + REG_OFFSET_NR22)
+#define REG_ADDR_SOUND2CNT_H (REG_BASE + REG_OFFSET_SOUND2CNT_H)
+#define REG_ADDR_NR23 (REG_BASE + REG_OFFSET_NR23)
+#define REG_ADDR_NR24 (REG_BASE + REG_OFFSET_NR24)
+#define REG_ADDR_SOUND3CNT_L (REG_BASE + REG_OFFSET_SOUND3CNT_L)
+#define REG_ADDR_NR30 (REG_BASE + REG_OFFSET_NR30)
+#define REG_ADDR_SOUND3CNT_H (REG_BASE + REG_OFFSET_SOUND3CNT_H)
+#define REG_ADDR_NR31 (REG_BASE + REG_OFFSET_NR31)
+#define REG_ADDR_NR32 (REG_BASE + REG_OFFSET_NR32)
+#define REG_ADDR_SOUND3CNT_X (REG_BASE + REG_OFFSET_SOUND3CNT_X)
+#define REG_ADDR_NR33 (REG_BASE + REG_OFFSET_NR33)
+#define REG_ADDR_NR34 (REG_BASE + REG_OFFSET_NR34)
+#define REG_ADDR_SOUND4CNT_L (REG_BASE + REG_OFFSET_SOUND4CNT_L)
+#define REG_ADDR_NR41 (REG_BASE + REG_OFFSET_NR41)
+#define REG_ADDR_NR42 (REG_BASE + REG_OFFSET_NR42)
+#define REG_ADDR_SOUND4CNT_H (REG_BASE + REG_OFFSET_SOUND4CNT_H)
+#define REG_ADDR_NR43 (REG_BASE + REG_OFFSET_NR43)
+#define REG_ADDR_NR44 (REG_BASE + REG_OFFSET_NR44)
+#define REG_ADDR_SOUNDCNT_L (REG_BASE + REG_OFFSET_SOUNDCNT_L)
+#define REG_ADDR_NR50 (REG_BASE + REG_OFFSET_NR50)
+#define REG_ADDR_NR51 (REG_BASE + REG_OFFSET_NR51)
+#define REG_ADDR_SOUNDCNT_H (REG_BASE + REG_OFFSET_SOUNDCNT_H)
+#define REG_ADDR_SOUNDCNT_X (REG_BASE + REG_OFFSET_SOUNDCNT_X)
+#define REG_ADDR_NR52 (REG_BASE + REG_OFFSET_NR52)
+#define REG_ADDR_SOUNDBIAS (REG_BASE + REG_OFFSET_SOUNDBIAS)
+#define REG_ADDR_SOUNDBIAS_L (REG_BASE + REG_OFFSET_SOUNDBIAS_L)
+#define REG_ADDR_SOUNDBIAS_H (REG_BASE + REG_OFFSET_SOUNDBIAS_H)
+#define REG_ADDR_WAVE_RAM0 (REG_BASE + REG_OFFSET_WAVE_RAM0)
+#define REG_ADDR_WAVE_RAM1 (REG_BASE + REG_OFFSET_WAVE_RAM1)
+#define REG_ADDR_WAVE_RAM2 (REG_BASE + REG_OFFSET_WAVE_RAM2)
+#define REG_ADDR_WAVE_RAM3 (REG_BASE + REG_OFFSET_WAVE_RAM3)
+#define REG_ADDR_FIFO_A (REG_BASE + REG_OFFSET_FIFO_A)
+#define REG_ADDR_FIFO_B (REG_BASE + REG_OFFSET_FIFO_B)
+
+#define REG_ADDR_DMA0 (REG_BASE + REG_OFFSET_DMA0)
+#define REG_ADDR_DMA0SAD (REG_BASE + REG_OFFSET_DMA0SAD)
+#define REG_ADDR_DMA0DAD (REG_BASE + REG_OFFSET_DMA0DAD)
+#define REG_ADDR_DMA0CNT (REG_BASE + REG_OFFSET_DMA0CNT)
+#define REG_ADDR_DMA0CNT_L (REG_BASE + REG_OFFSET_DMA0CNT_L)
+#define REG_ADDR_DMA0CNT_H (REG_BASE + REG_OFFSET_DMA0CNT_H)
+#define REG_ADDR_DMA1 (REG_BASE + REG_OFFSET_DMA1)
+#define REG_ADDR_DMA1SAD (REG_BASE + REG_OFFSET_DMA1SAD)
+#define REG_ADDR_DMA1DAD (REG_BASE + REG_OFFSET_DMA1DAD)
+#define REG_ADDR_DMA1CNT (REG_BASE + REG_OFFSET_DMA1CNT)
+#define REG_ADDR_DMA1CNT_L (REG_BASE + REG_OFFSET_DMA1CNT_L)
+#define REG_ADDR_DMA1CNT_H (REG_BASE + REG_OFFSET_DMA1CNT_H)
+#define REG_ADDR_DMA2 (REG_BASE + REG_OFFSET_DMA2)
+#define REG_ADDR_DMA2SAD (REG_BASE + REG_OFFSET_DMA2SAD)
+#define REG_ADDR_DMA2DAD (REG_BASE + REG_OFFSET_DMA2DAD)
+#define REG_ADDR_DMA2CNT (REG_BASE + REG_OFFSET_DMA2CNT)
+#define REG_ADDR_DMA2CNT_L (REG_BASE + REG_OFFSET_DMA2CNT_L)
+#define REG_ADDR_DMA2CNT_H (REG_BASE + REG_OFFSET_DMA2CNT_H)
+#define REG_ADDR_DMA3 (REG_BASE + REG_OFFSET_DMA3)
+#define REG_ADDR_DMA3SAD (REG_BASE + REG_OFFSET_DMA3SAD)
+#define REG_ADDR_DMA3DAD (REG_BASE + REG_OFFSET_DMA3DAD)
+#define REG_ADDR_DMA3CNT (REG_BASE + REG_OFFSET_DMA3CNT)
+#define REG_ADDR_DMA3CNT_L (REG_BASE + REG_OFFSET_DMA3CNT_L)
+#define REG_ADDR_DMA3CNT_H (REG_BASE + REG_OFFSET_DMA3CNT_H)
+
+#define REG_ADDR_TMCNT (REG_BASE + REG_OFFSET_TMCNT)
+#define REG_ADDR_TMCNT_L (REG_BASE + REG_OFFSET_TMCNT_L)
+#define REG_ADDR_TMCNT_H (REG_BASE + REG_OFFSET_TMCNT_H)
+#define REG_ADDR_TM0CNT (REG_BASE + REG_OFFSET_TM0CNT)
+#define REG_ADDR_TM0CNT_L (REG_BASE + REG_OFFSET_TM0CNT_L)
+#define REG_ADDR_TM0CNT_H (REG_BASE + REG_OFFSET_TM0CNT_H)
+#define REG_ADDR_TM1CNT (REG_BASE + REG_OFFSET_TM1CNT)
+#define REG_ADDR_TM1CNT_L (REG_BASE + REG_OFFSET_TM1CNT_L)
+#define REG_ADDR_TM1CNT_H (REG_BASE + REG_OFFSET_TM1CNT_H)
+#define REG_ADDR_TM2CNT (REG_BASE + REG_OFFSET_TM2CNT)
+#define REG_ADDR_TM2CNT_L (REG_BASE + REG_OFFSET_TM2CNT_L)
+#define REG_ADDR_TM2CNT_H (REG_BASE + REG_OFFSET_TM2CNT_H)
+#define REG_ADDR_TM3CNT (REG_BASE + REG_OFFSET_TM3CNT)
+#define REG_ADDR_TM3CNT_L (REG_BASE + REG_OFFSET_TM3CNT_L)
+#define REG_ADDR_TM3CNT_H (REG_BASE + REG_OFFSET_TM3CNT_H)
+
+#define REG_ADDR_SIOCNT (REG_BASE + REG_OFFSET_SIOCNT)
+#define REG_ADDR_SIODATA8 (REG_BASE + REG_OFFSET_SIODATA8)
+#define REG_ADDR_SIODATA32 (REG_BASE + REG_OFFSET_SIODATA32)
+#define REG_ADDR_SIOMLT_SEND (REG_BASE + REG_OFFSET_SIOMLT_SEND)
+#define REG_ADDR_SIOMLT_RECV (REG_BASE + REG_OFFSET_SIOMLT_RECV)
+#define REG_ADDR_SIOMULTI0 (REG_BASE + REG_OFFSET_SIOMULTI0)
+#define REG_ADDR_SIOMULTI1 (REG_BASE + REG_OFFSET_SIOMULTI1)
+#define REG_ADDR_SIOMULTI2 (REG_BASE + REG_OFFSET_SIOMULTI2)
+#define REG_ADDR_SIOMULTI3 (REG_BASE + REG_OFFSET_SIOMULTI3)
+
+#define REG_ADDR_KEYINPUT (REG_BASE + REG_OFFSET_KEYINPUT)
+#define REG_ADDR_KEYCNT (REG_BASE + REG_OFFSET_KEYCNT)
+
+#define REG_ADDR_RCNT (REG_BASE + REG_OFFSET_RCNT)
+
+#define REG_ADDR_JOYCNT (REG_BASE + REG_OFFSET_JOYCNT)
+#define REG_ADDR_JOYSTAT (REG_BASE + REG_OFFSET_JOYSTAT)
+#define REG_ADDR_JOY_RECV (REG_BASE + REG_OFFSET_JOY_RECV)
+#define REG_ADDR_JOY_RECV_L (REG_BASE + REG_OFFSET_JOY_RECV_L)
+#define REG_ADDR_JOY_RECV_H (REG_BASE + REG_OFFSET_JOY_RECV_H)
+#define REG_ADDR_JOY_TRANS (REG_BASE + REG_OFFSET_JOY_TRANS)
+#define REG_ADDR_JOY_TRANS_L (REG_BASE + REG_OFFSET_JOY_TRANS_L)
+#define REG_ADDR_JOY_TRANS_H (REG_BASE + REG_OFFSET_JOY_TRANS_H)
+
+#define REG_ADDR_IME (REG_BASE + REG_OFFSET_IME)
+#define REG_ADDR_IE (REG_BASE + REG_OFFSET_IE)
+#define REG_ADDR_IF (REG_BASE + REG_OFFSET_IF)
+
+#define REG_ADDR_WAITCNT (REG_BASE + REG_OFFSET_WAITCNT)
+
+// I/O registers
+
+#define REG_DISPCNT (*(vu16 *)REG_ADDR_DISPCNT)
+#define REG_DISPSTAT (*(vu16 *)REG_ADDR_DISPSTAT)
+#define REG_VCOUNT (*(vu16 *)REG_ADDR_VCOUNT)
+#define REG_BG0CNT (*(vu16 *)REG_ADDR_BG0CNT)
+#define REG_BG1CNT (*(vu16 *)REG_ADDR_BG1CNT)
+#define REG_BG2CNT (*(vu16 *)REG_ADDR_BG2CNT)
+#define REG_BG3CNT (*(vu16 *)REG_ADDR_BG3CNT)
+#define REG_BG0HOFS (*(vu16 *)REG_ADDR_BG0HOFS)
+#define REG_BG0VOFS (*(vu16 *)REG_ADDR_BG0VOFS)
+#define REG_BG1HOFS (*(vu16 *)REG_ADDR_BG1HOFS)
+#define REG_BG1VOFS (*(vu16 *)REG_ADDR_BG1VOFS)
+#define REG_BG2HOFS (*(vu16 *)REG_ADDR_BG2HOFS)
+#define REG_BG2VOFS (*(vu16 *)REG_ADDR_BG2VOFS)
+#define REG_BG3HOFS (*(vu16 *)REG_ADDR_BG3HOFS)
+#define REG_BG3VOFS (*(vu16 *)REG_ADDR_BG3VOFS)
+#define REG_BG2PA (*(vu16 *)REG_ADDR_BG2PA)
+#define REG_BG2PB (*(vu16 *)REG_ADDR_BG2PB)
+#define REG_BG2PC (*(vu16 *)REG_ADDR_BG2PC)
+#define REG_BG2PD (*(vu16 *)REG_ADDR_BG2PD)
+#define REG_BG2X (*(vu32 *)REG_ADDR_BG2X)
+#define REG_BG2X_L (*(vu16 *)REG_ADDR_BG2X_L)
+#define REG_BG2X_H (*(vu16 *)REG_ADDR_BG2X_H)
+#define REG_BG2Y (*(vu32 *)REG_ADDR_BG2Y)
+#define REG_BG2Y_L (*(vu16 *)REG_ADDR_BG2Y_L)
+#define REG_BG2Y_H (*(vu16 *)REG_ADDR_BG2Y_H)
+#define REG_BG3PA (*(vu16 *)REG_ADDR_BG3PA)
+#define REG_BG3PB (*(vu16 *)REG_ADDR_BG3PB)
+#define REG_BG3PC (*(vu16 *)REG_ADDR_BG3PC)
+#define REG_BG3PD (*(vu16 *)REG_ADDR_BG3PD)
+#define REG_BG3X (*(vu32 *)REG_ADDR_BG3X)
+#define REG_BG3X_L (*(vu16 *)REG_ADDR_BG3X_L)
+#define REG_BG3X_H (*(vu16 *)REG_ADDR_BG3X_H)
+#define REG_BG3Y (*(vu32 *)REG_ADDR_BG3Y)
+#define REG_BG3Y_L (*(vu16 *)REG_ADDR_BG3Y_L)
+#define REG_BG3Y_H (*(vu16 *)REG_ADDR_BG3Y_H)
+#define REG_WIN0H (*(vu16 *)REG_ADDR_WIN0H)
+#define REG_WIN1H (*(vu16 *)REG_ADDR_WIN1H)
+#define REG_WIN0V (*(vu16 *)REG_ADDR_WIN0V)
+#define REG_WIN1V (*(vu16 *)REG_ADDR_WIN1V)
+#define REG_WININ (*(vu16 *)REG_ADDR_WININ)
+#define REG_WINOUT (*(vu16 *)REG_ADDR_WINOUT)
+#define REG_MOSAIC (*(vu16 *)REG_ADDR_MOSAIC)
+#define REG_BLDCNT (*(vu16 *)REG_ADDR_BLDCNT)
+#define REG_BLDALPHA (*(vu16 *)REG_ADDR_BLDALPHA)
+#define REG_BLDY (*(vu16 *)REG_ADDR_BLDY)
+
+#define REG_SOUND1CNT_L (*(vu16 *)REG_ADDR_SOUND1CNT_L)
+#define REG_NR10 (*(vu8 *)REG_ADDR_NR10)
+#define REG_SOUND1CNT_H (*(vu16 *)REG_ADDR_SOUND1CNT_H)
+#define REG_NR11 (*(vu8 *)REG_ADDR_NR11)
+#define REG_NR12 (*(vu8 *)REG_ADDR_NR12)
+#define REG_SOUND1CNT_X (*(vu16 *)REG_ADDR_SOUND1CNT_X)
+#define REG_NR13 (*(vu8 *)REG_ADDR_NR13)
+#define REG_NR14 (*(vu8 *)REG_ADDR_NR14)
+#define REG_SOUND2CNT_L (*(vu16 *)REG_ADDR_SOUND2CNT_L)
+#define REG_NR21 (*(vu8 *)REG_ADDR_NR21)
+#define REG_NR22 (*(vu8 *)REG_ADDR_NR22)
+#define REG_SOUND2CNT_H (*(vu16 *)REG_ADDR_SOUND2CNT_H)
+#define REG_NR23 (*(vu8 *)REG_ADDR_NR23)
+#define REG_NR24 (*(vu8 *)REG_ADDR_NR24)
+#define REG_SOUND3CNT_L (*(vu16 *)REG_ADDR_SOUND3CNT_L)
+#define REG_NR30 (*(vu8 *)REG_ADDR_NR30)
+#define REG_SOUND3CNT_H (*(vu16 *)REG_ADDR_SOUND3CNT_H)
+#define REG_NR31 (*(vu8 *)REG_ADDR_NR31)
+#define REG_NR32 (*(vu8 *)REG_ADDR_NR32)
+#define REG_SOUND3CNT_X (*(vu16 *)REG_ADDR_SOUND3CNT_X)
+#define REG_NR33 (*(vu8 *)REG_ADDR_NR33)
+#define REG_NR34 (*(vu8 *)REG_ADDR_NR34)
+#define REG_SOUND4CNT_L (*(vu16 *)REG_ADDR_SOUND4CNT_L)
+#define REG_NR41 (*(vu8 *)REG_ADDR_NR41)
+#define REG_NR42 (*(vu8 *)REG_ADDR_NR42)
+#define REG_SOUND4CNT_H (*(vu16 *)REG_ADDR_SOUND4CNT_H)
+#define REG_NR43 (*(vu8 *)REG_ADDR_NR43)
+#define REG_NR44 (*(vu8 *)REG_ADDR_NR44)
+#define REG_SOUNDCNT_L (*(vu16 *)REG_ADDR_SOUNDCNT_L)
+#define REG_NR50 (*(vu8 *)REG_ADDR_NR50)
+#define REG_NR51 (*(vu8 *)REG_ADDR_NR51)
+#define REG_SOUNDCNT_H (*(vu16 *)REG_ADDR_SOUNDCNT_H)
+#define REG_SOUNDCNT_X (*(vu16 *)REG_ADDR_SOUNDCNT_X)
+#define REG_NR52 (*(vu8 *)REG_ADDR_NR52)
+#define REG_SOUNDBIAS (*(vu16 *)REG_ADDR_SOUNDBIAS)
+#define REG_SOUNDBIAS_L (*(vu8 *)REG_ADDR_SOUNDBIAS_L)
+#define REG_SOUNDBIAS_H (*(vu8 *)REG_ADDR_SOUNDBIAS_H)
+#define REG_WAVE_RAM0 (*(vu32 *)REG_ADDR_WAVE_RAM0)
+#define REG_WAVE_RAM1 (*(vu32 *)REG_ADDR_WAVE_RAM1)
+#define REG_WAVE_RAM2 (*(vu32 *)REG_ADDR_WAVE_RAM2)
+#define REG_WAVE_RAM3 (*(vu32 *)REG_ADDR_WAVE_RAM3)
+#define REG_FIFO_A (*(vu32 *)REG_ADDR_FIFO_A)
+#define REG_FIFO_B (*(vu32 *)REG_ADDR_FIFO_B)
+
+#define REG_DMA0SAD (*(vu32 *)REG_ADDR_DMA0SAD)
+#define REG_DMA0DAD (*(vu32 *)REG_ADDR_DMA0DAD)
+#define REG_DMA0CNT (*(vu32 *)REG_ADDR_DMA0CNT)
+#define REG_DMA0CNT_L (*(vu16 *)REG_ADDR_DMA0CNT_L)
+#define REG_DMA0CNT_H (*(vu16 *)REG_ADDR_DMA0CNT_H)
+
+#define REG_DMA1SAD (*(vu32 *)REG_ADDR_DMA1SAD)
+#define REG_DMA1DAD (*(vu32 *)REG_ADDR_DMA1DAD)
+#define REG_DMA1CNT (*(vu32 *)REG_ADDR_DMA1CNT)
+#define REG_DMA1CNT_L (*(vu16 *)REG_ADDR_DMA1CNT_L)
+#define REG_DMA1CNT_H (*(vu16 *)REG_ADDR_DMA1CNT_H)
+
+#define REG_DMA2SAD (*(vu32 *)REG_ADDR_DMA2SAD)
+#define REG_DMA2DAD (*(vu32 *)REG_ADDR_DMA2DAD)
+#define REG_DMA2CNT (*(vu32 *)REG_ADDR_DMA2CNT)
+#define REG_DMA2CNT_L (*(vu16 *)REG_ADDR_DMA2CNT_L)
+#define REG_DMA2CNT_H (*(vu16 *)REG_ADDR_DMA2CNT_H)
+
+#define REG_DMA3SAD (*(vu32 *)REG_ADDR_DMA3SAD)
+#define REG_DMA3DAD (*(vu32 *)REG_ADDR_DMA3DAD)
+#define REG_DMA3CNT (*(vu32 *)REG_ADDR_DMA3CNT)
+#define REG_DMA3CNT_L (*(vu16 *)REG_ADDR_DMA3CNT_L)
+#define REG_DMA3CNT_H (*(vu16 *)REG_ADDR_DMA3CNT_H)
+
+#define REG_TMCNT(n) (*(vu16 *)(REG_ADDR_TMCNT + ((n) * 4)))
+#define REG_TMCNT_L(n) (*(vu16 *)(REG_ADDR_TMCNT_L + ((n) * 4)))
+#define REG_TMCNT_H(n) (*(vu16 *)(REG_ADDR_TMCNT_H + ((n) * 4)))
+#define REG_TM0CNT (*(vu32 *)REG_ADDR_TM0CNT)
+#define REG_TM0CNT_L (*(vu16 *)REG_ADDR_TM0CNT_L)
+#define REG_TM0CNT_H (*(vu16 *)REG_ADDR_TM0CNT_H)
+#define REG_TM1CNT (*(vu32 *)REG_ADDR_TM1CNT)
+#define REG_TM1CNT_L (*(vu16 *)REG_ADDR_TM1CNT_L)
+#define REG_TM1CNT_H (*(vu16 *)REG_ADDR_TM1CNT_H)
+#define REG_TM2CNT (*(vu32 *)REG_ADDR_TM2CNT)
+#define REG_TM2CNT_L (*(vu16 *)REG_ADDR_TM2CNT_L)
+#define REG_TM2CNT_H (*(vu16 *)REG_ADDR_TM2CNT_H)
+#define REG_TM3CNT (*(vu32 *)REG_ADDR_TM3CNT)
+#define REG_TM3CNT_L (*(vu16 *)REG_ADDR_TM3CNT_L)
+#define REG_TM3CNT_H (*(vu16 *)REG_ADDR_TM3CNT_H)
+
+#define REG_SIOCNT (*(vu16 *)REG_ADDR_SIOCNT)
+#define REG_SIODATA8 (*(vu16 *)REG_ADDR_SIODATA8)
+#define REG_SIODATA32 (*(vu32 *)REG_ADDR_SIODATA32)
+#define REG_SIOMLT_SEND (*(vu16 *)REG_ADDR_SIOMLT_SEND)
+#define REG_SIOMLT_RECV (*(vu64 *)REG_ADDR_SIOMLT_RECV)
+#define REG_SIOMULTI0 (*(vu16 *)REG_ADDR_SIOMULTI0)
+#define REG_SIOMULTI1 (*(vu16 *)REG_ADDR_SIOMULTI1)
+#define REG_SIOMULTI2 (*(vu16 *)REG_ADDR_SIOMULTI2)
+#define REG_SIOMULTI3 (*(vu16 *)REG_ADDR_SIOMULTI3)
+
+#define REG_KEYINPUT (*(vu16 *)REG_ADDR_KEYINPUT)
+#define REG_KEYCNT (*(vu16 *)REG_ADDR_KEYCNT)
+
+#define REG_RCNT (*(vu16 *)REG_ADDR_RCNT)
+
+#define REG_IME (*(vu16 *)REG_ADDR_IME)
+#define REG_IE (*(vu16 *)REG_ADDR_IE)
+#define REG_IF (*(vu16 *)REG_ADDR_IF)
+
+#define REG_WAITCNT (*(vu16 *)REG_ADDR_WAITCNT)
+
+// I/O register fields
+
+// DISPCNT
+#define DISPCNT_MODE_0 0x0000 // BG0: text, BG1: text, BG2: text, BG3: text
+#define DISPCNT_MODE_1 0x0001 // BG0: text, BG1: text, BG2: affine, BG3: off
+#define DISPCNT_MODE_2 0x0002 // BG0: off, BG1: off, BG2: affine, BG3: affine
+#define DISPCNT_MODE_3 0x0003 // Bitmap mode, 240x160, BGR555 color
+#define DISPCNT_MODE_4 0x0004 // Bitmap mode, 240x160, 256 color palette
+#define DISPCNT_MODE_5 0x0005 // Bitmap mode, 160x128, BGR555 color
+#define DISPCNT_HBLANK_INTERVAL 0x0020 // Allow access to OAM during H-Blank
+#define DISPCNT_OBJ_1D_MAP 0x0040
+#define DISPCNT_FORCED_BLANK 0x0080
+#define DISPCNT_BG0_ON 0x0100
+#define DISPCNT_BG1_ON 0x0200
+#define DISPCNT_BG2_ON 0x0400
+#define DISPCNT_BG3_ON 0x0800
+#define DISPCNT_BG_ALL_ON 0x0F00
+#define DISPCNT_OBJ_ON 0x1000
+#define DISPCNT_WIN0_ON 0x2000
+#define DISPCNT_WIN1_ON 0x4000
+#define DISPCNT_OBJWIN_ON 0x8000
+
+// DISPSTAT
+#define DISPSTAT_VBLANK 0x0001 // in V-Blank
+#define DISPSTAT_HBLANK 0x0002 // in H-Blank
+#define DISPSTAT_VCOUNT 0x0004 // V-Count match
+#define DISPSTAT_VBLANK_INTR 0x0008 // V-Blank interrupt enabled
+#define DISPSTAT_HBLANK_INTR 0x0010 // H-Blank interrupt enabled
+#define DISPSTAT_VCOUNT_INTR 0x0020 // V-Count interrupt enabled
+
+// BGCNT
+#define BGCNT_PRIORITY(n) (n) // Values 0 - 3. Lower priority BGs will be drawn on top of higher priority BGs.
+#define BGCNT_CHARBASE(n) ((n) << 2) // Values 0 - 3. Base block for tile pixel data.
+#define BGCNT_MOSAIC 0x0040
+#define BGCNT_16COLOR 0x0000 // 4 bits per pixel
+#define BGCNT_256COLOR 0x0080 // 8 bits per pixel
+#define BGCNT_SCREENBASE(n) ((n) << 8) // Values 0 - 31. Base block for tile map.
+#define BGCNT_WRAP 0x2000 // Only affects affine BGs. Text BGs wrap by default.
+#define BGCNT_TXT256x256 0x0000 // Internal screen size size of text mode BG in pixels.
+#define BGCNT_TXT512x256 0x4000
+#define BGCNT_TXT256x512 0x8000
+#define BGCNT_TXT512x512 0xC000
+#define BGCNT_AFF128x128 0x0000 // Internal screen size size of affine mode BG in pixels.
+#define BGCNT_AFF256x256 0x4000
+#define BGCNT_AFF512x512 0x8000
+#define BGCNT_AFF1024x1024 0xC000
+
+// WININ/OUT
+#define WININ_WIN0_BG0 (1 << 0)
+#define WININ_WIN0_BG1 (1 << 1)
+#define WININ_WIN0_BG2 (1 << 2)
+#define WININ_WIN0_BG3 (1 << 3)
+#define WININ_WIN0_BG_ALL (WININ_WIN0_BG0 | WININ_WIN0_BG1 | WININ_WIN0_BG2 | WININ_WIN0_BG3)
+#define WININ_WIN0_OBJ (1 << 4)
+#define WININ_WIN0_CLR (1 << 5)
+#define WININ_WIN1_BG0 (1 << 8)
+#define WININ_WIN1_BG1 (1 << 9)
+#define WININ_WIN1_BG2 (1 << 10)
+#define WININ_WIN1_BG3 (1 << 11)
+#define WININ_WIN1_BG_ALL (WININ_WIN1_BG0 | WININ_WIN1_BG1 | WININ_WIN1_BG2 | WININ_WIN1_BG3)
+#define WININ_WIN1_OBJ (1 << 12)
+#define WININ_WIN1_CLR (1 << 13)
+
+#define WINOUT_WIN01_BG0 (1 << 0)
+#define WINOUT_WIN01_BG1 (1 << 1)
+#define WINOUT_WIN01_BG2 (1 << 2)
+#define WINOUT_WIN01_BG3 (1 << 3)
+#define WINOUT_WIN01_OBJ (1 << 4)
+#define WINOUT_WIN01_CLR (1 << 5)
+#define WINOUT_WINOBJ_BG0 (1 << 8)
+#define WINOUT_WINOBJ_BG1 (1 << 9)
+#define WINOUT_WINOBJ_BG2 (1 << 10)
+#define WINOUT_WINOBJ_BG3 (1 << 11)
+#define WINOUT_WINOBJ_OBJ (1 << 12)
+#define WINOUT_WINOBJ_CLR (1 << 13)
+
+// BLDCNT
+// Bits 0-5 select layers for the 1st target
+#define BLDCNT_TGT1_BG0 (1 << 0)
+#define BLDCNT_TGT1_BG1 (1 << 1)
+#define BLDCNT_TGT1_BG2 (1 << 2)
+#define BLDCNT_TGT1_BG3 (1 << 3)
+#define BLDCNT_TGT1_OBJ (1 << 4)
+#define BLDCNT_TGT1_BD (1 << 5)
+#define BLDCNT_TGT1_ALL (BLDCNT_TGT1_BG0 | BLDCNT_TGT1_BG1 | BLDCNT_TGT1_BG2 | BLDCNT_TGT1_BG3 | BLDCNT_TGT1_OBJ | BLDCNT_TGT1_BD)
+// Bits 6-7 select the special effect
+#define BLDCNT_EFFECT_NONE (0 << 6) // no special effect
+#define BLDCNT_EFFECT_BLEND (1 << 6) // 1st+2nd targets mixed (controlled by BLDALPHA)
+#define BLDCNT_EFFECT_LIGHTEN (2 << 6) // 1st target becomes whiter (controlled by BLDY)
+#define BLDCNT_EFFECT_DARKEN (3 << 6) // 1st target becomes blacker (controlled by BLDY)
+// Bits 8-13 select layers for the 2nd target
+#define BLDCNT_TGT2_BG0 (1 << 8)
+#define BLDCNT_TGT2_BG1 (1 << 9)
+#define BLDCNT_TGT2_BG2 (1 << 10)
+#define BLDCNT_TGT2_BG3 (1 << 11)
+#define BLDCNT_TGT2_OBJ (1 << 12)
+#define BLDCNT_TGT2_BD (1 << 13)
+#define BLDCNT_TGT2_ALL (BLDCNT_TGT2_BG0 | BLDCNT_TGT2_BG1 | BLDCNT_TGT2_BG2 | BLDCNT_TGT2_BG3 | BLDCNT_TGT2_OBJ | BLDCNT_TGT2_BD)
+
+// BLDALPHA
+#define BLDALPHA_BLEND(target1, target2) (((target2) << 8) | (target1))
+
+// SOUNDCNT_H
+#define SOUND_CGB_MIX_QUARTER 0x0000
+#define SOUND_CGB_MIX_HALF 0x0001
+#define SOUND_CGB_MIX_FULL 0x0002
+#define SOUND_A_MIX_HALF 0x0000
+#define SOUND_A_MIX_FULL 0x0004
+#define SOUND_B_MIX_HALF 0x0000
+#define SOUND_B_MIX_FULL 0x0008
+#define SOUND_ALL_MIX_FULL 0x000E
+#define SOUND_A_RIGHT_OUTPUT 0x0100
+#define SOUND_A_LEFT_OUTPUT 0x0200
+#define SOUND_A_TIMER_0 0x0000
+#define SOUND_A_TIMER_1 0x0400
+#define SOUND_A_FIFO_RESET 0x0800
+#define SOUND_B_RIGHT_OUTPUT 0x1000
+#define SOUND_B_LEFT_OUTPUT 0x2000
+#define SOUND_B_TIMER_0 0x0000
+#define SOUND_B_TIMER_1 0x4000
+#define SOUND_B_FIFO_RESET 0x8000
+
+// SOUNDCNT_X
+#define SOUND_1_ON 0x0001
+#define SOUND_2_ON 0x0002
+#define SOUND_3_ON 0x0004
+#define SOUND_4_ON 0x0008
+#define SOUND_MASTER_ENABLE 0x0080
+
+// DMA
+#define DMA_DEST_INC 0x0000
+#define DMA_DEST_DEC 0x0020
+#define DMA_DEST_FIXED 0x0040
+#define DMA_DEST_RELOAD 0x0060
+#define DMA_SRC_INC 0x0000
+#define DMA_SRC_DEC 0x0080
+#define DMA_SRC_FIXED 0x0100
+#define DMA_REPEAT 0x0200
+#define DMA_16BIT 0x0000
+#define DMA_32BIT 0x0400
+#define DMA_DREQ_ON 0x0800
+#define DMA_START_NOW 0x0000
+#define DMA_START_VBLANK 0x1000
+#define DMA_START_HBLANK 0x2000
+#define DMA_START_SPECIAL 0x3000
+#define DMA_START_MASK 0x3000
+#define DMA_INTR_ENABLE 0x4000
+#define DMA_ENABLE 0x8000
+
+// timer
+#define TIMER_1CLK 0x00
+#define TIMER_64CLK 0x01
+#define TIMER_256CLK 0x02
+#define TIMER_1024CLK 0x03
+#define TIMER_INTR_ENABLE 0x40
+#define TIMER_ENABLE 0x80
+
+// serial
+#define SIO_ID 0x0030 // Communication ID
+
+#define SIO_8BIT_MODE 0x0000 // Normal 8-bit communication mode
+#define SIO_32BIT_MODE 0x1000 // Normal 32-bit communication mode
+#define SIO_MULTI_MODE 0x2000 // Multi-player communication mode
+#define SIO_UART_MODE 0x3000 // UART communication mode
+
+#define SIO_9600_BPS 0x0000 // baud rate 9600 bps
+#define SIO_38400_BPS 0x0001 // 38400 bps
+#define SIO_57600_BPS 0x0002 // 57600 bps
+#define SIO_115200_BPS 0x0003 // 115200 bps
+
+#define SIO_MULTI_SI 0x0004 // Multi-player communication SI terminal
+#define SIO_MULTI_SD 0x0008 // SD terminal
+#define SIO_MULTI_BUSY 0x0080
+
+#define SIO_ERROR 0x0040 // Detect error
+#define SIO_START 0x0080 // Start transfer
+#define SIO_ENABLE 0x0080 // Enable SIO
+
+#define SIO_INTR_ENABLE 0x4000
+
+#define SIO_MULTI_SI_SHIFT 2
+#define SIO_MULTI_SI_MASK 0x1
+#define SIO_MULTI_DI_SHIFT 3
+#define SIO_MULTI_DI_MASK 0x1
+
+// keys
+#define A_BUTTON 0x0001
+#define B_BUTTON 0x0002
+#define SELECT_BUTTON 0x0004
+#define START_BUTTON 0x0008
+#define DPAD_RIGHT 0x0010
+#define DPAD_LEFT 0x0020
+#define DPAD_UP 0x0040
+#define DPAD_DOWN 0x0080
+#define R_BUTTON 0x0100
+#define L_BUTTON 0x0200
+#define KEYS_MASK 0x03FF
+#define KEY_INTR_ENABLE 0x0400
+#define KEY_OR_INTR 0x0000
+#define KEY_AND_INTR 0x8000
+#define DPAD_ANY ((DPAD_RIGHT | DPAD_LEFT | DPAD_UP | DPAD_DOWN))
+#define JOY_EXCL_DPAD 0x030F
+
+// interrupt flags
+#define INTR_FLAG_VBLANK (1 << 0)
+#define INTR_FLAG_HBLANK (1 << 1)
+#define INTR_FLAG_VCOUNT (1 << 2)
+#define INTR_FLAG_TIMER0 (1 << 3)
+#define INTR_FLAG_TIMER1 (1 << 4)
+#define INTR_FLAG_TIMER2 (1 << 5)
+#define INTR_FLAG_TIMER3 (1 << 6)
+#define INTR_FLAG_SERIAL (1 << 7)
+#define INTR_FLAG_DMA0 (1 << 8)
+#define INTR_FLAG_DMA1 (1 << 9)
+#define INTR_FLAG_DMA2 (1 << 10)
+#define INTR_FLAG_DMA3 (1 << 11)
+#define INTR_FLAG_KEYPAD (1 << 12)
+#define INTR_FLAG_GAMEPAK (1 << 13)
+
+// WAITCNT
+#define WAITCNT_SRAM_4 (0 << 0)
+#define WAITCNT_SRAM_3 (1 << 0)
+#define WAITCNT_SRAM_2 (2 << 0)
+#define WAITCNT_SRAM_8 (3 << 0)
+#define WAITCNT_SRAM_MASK (3 << 0)
+
+#define WAITCNT_WS0_N_4 (0 << 2)
+#define WAITCNT_WS0_N_3 (1 << 2)
+#define WAITCNT_WS0_N_2 (2 << 2)
+#define WAITCNT_WS0_N_8 (3 << 2)
+#define WAITCNT_WS0_N_MASK (3 << 2)
+
+#define WAITCNT_WS0_S_2 (0 << 4)
+#define WAITCNT_WS0_S_1 (1 << 4)
+
+#define WAITCNT_WS1_N_4 (0 << 5)
+#define WAITCNT_WS1_N_3 (1 << 5)
+#define WAITCNT_WS1_N_2 (2 << 5)
+#define WAITCNT_WS1_N_8 (3 << 5)
+#define WAITCNT_WS1_N_MASK (3 << 5)
+
+#define WAITCNT_WS1_S_4 (0 << 7)
+#define WAITCNT_WS1_S_1 (1 << 7)
+
+#define WAITCNT_WS2_N_4 (0 << 8)
+#define WAITCNT_WS2_N_3 (1 << 8)
+#define WAITCNT_WS2_N_2 (2 << 8)
+#define WAITCNT_WS2_N_8 (3 << 8)
+#define WAITCNT_WS2_N_MASK (3 << 8)
+
+#define WAITCNT_WS2_S_8 (0 << 10)
+#define WAITCNT_WS2_S_1 (1 << 10)
+
+#define WAITCNT_PHI_OUT_NONE (0 << 11)
+#define WAITCNT_PHI_OUT_4MHZ (1 << 11)
+#define WAITCNT_PHI_OUT_8MHZ (2 << 11)
+#define WAITCNT_PHI_OUT_16MHZ (3 << 11)
+#define WAITCNT_PHI_OUT_MASK (3 << 11)
+
+#define WAITCNT_PREFETCH_ENABLE (1 << 14)
+
+#define WAITCNT_AGB (0 << 15)
+#define WAITCNT_CGB (1 << 15)
+
+#endif // GUARD_GBA_IO_REG_H
diff --git a/include/gba/isagbprint.h b/include/gba/isagbprint.h
new file mode 100755
index 0000000..c5eb456
--- /dev/null
+++ b/include/gba/isagbprint.h
@@ -0,0 +1,50 @@
+#ifndef GUARD_GBA_ISAGBPRINT_H
+#define GUARD_GBA_ISAGBPRINT_H
+
+#ifdef NDEBUG
+#define AGBPrintInit()
+#define AGBPutc(cChr)
+#define AGBPrint(pBuf)
+#define AGBPrintf(pBuf, ...)
+#define AGBPrintFlush1Block()
+#define AGBPrintFlush()
+#define AGBAssert(pFile, nLine, pExpression, nStopProgram)
+#else
+void AGBPrintInit(void);
+void AGBPutc(const char cChr);
+void AGBPrint(const char *pBuf);
+void AGBPrintf(const char *pBuf, ...);
+void AGBPrintFlush1Block(void);
+void AGBPrintFlush(void);
+void AGBAssert(const char *pFile, int nLine, const char *pExpression, int nStopProgram);
+#endif
+
+#undef AGB_ASSERT
+#ifdef NDEBUG
+#define AGB_ASSERT(exp)
+#else
+#define AGB_ASSERT(exp) (exp) ? ((void*)0) : AGBAssert(__FILE__, __LINE__, #exp, 1);
+#endif
+
+#undef AGB_WARNING
+#ifdef NDEBUG
+#define AGB_WARNING(exp)
+#else
+#define AGB_WARNING(exp) (exp) ? ((void*)0) : AGBAssert(__FILE__, __LINE__, #exp, 0);
+#endif
+
+// for matching purposes
+
+#ifdef NDEBUG
+#define AGB_ASSERT_EX(exp, file, line)
+#else
+#define AGB_ASSERT_EX(exp, file, line) (exp) ? ((void*)0) : AGBAssert(file, line, #exp, 1);
+#endif
+
+#ifdef NDEBUG
+#define AGB_WARNING_EX(exp, file, line)
+#else
+#define AGB_WARNING_EX(exp, file, line) (exp) ? ((void*)0) : AGBAssert(file, line, #exp, 0);
+#endif
+
+#endif // GUARD_GBA_ISAGBPRINT_H
diff --git a/include/gba/m4a_internal.h b/include/gba/m4a_internal.h
new file mode 100755
index 0000000..339a077
--- /dev/null
+++ b/include/gba/m4a_internal.h
@@ -0,0 +1,467 @@
+#ifndef GUARD_GBA_M4A_INTERNAL_H
+#define GUARD_GBA_M4A_INTERNAL_H
+
+#include "gba/gba.h"
+
+// ASCII encoding of 'Smsh' in reverse
+// This is presumably short for SMASH, the developer of MKS4AGB.
+#define ID_NUMBER 0x68736D53
+
+#define C_V 0x40 // center value for PAN, BEND, and TUNE
+
+#define SOUND_MODE_REVERB_VAL 0x0000007F
+#define SOUND_MODE_REVERB_SET 0x00000080
+#define SOUND_MODE_MAXCHN 0x00000F00
+#define SOUND_MODE_MAXCHN_SHIFT 8
+#define SOUND_MODE_MASVOL 0x0000F000
+#define SOUND_MODE_MASVOL_SHIFT 12
+#define SOUND_MODE_FREQ_05734 0x00010000
+#define SOUND_MODE_FREQ_07884 0x00020000
+#define SOUND_MODE_FREQ_10512 0x00030000
+#define SOUND_MODE_FREQ_13379 0x00040000
+#define SOUND_MODE_FREQ_15768 0x00050000
+#define SOUND_MODE_FREQ_18157 0x00060000
+#define SOUND_MODE_FREQ_21024 0x00070000
+#define SOUND_MODE_FREQ_26758 0x00080000
+#define SOUND_MODE_FREQ_31536 0x00090000
+#define SOUND_MODE_FREQ_36314 0x000A0000
+#define SOUND_MODE_FREQ_40137 0x000B0000
+#define SOUND_MODE_FREQ_42048 0x000C0000
+#define SOUND_MODE_FREQ 0x000F0000
+#define SOUND_MODE_FREQ_SHIFT 16
+#define SOUND_MODE_DA_BIT_9 0x00800000
+#define SOUND_MODE_DA_BIT_8 0x00900000
+#define SOUND_MODE_DA_BIT_7 0x00A00000
+#define SOUND_MODE_DA_BIT_6 0x00B00000
+#define SOUND_MODE_DA_BIT 0x00B00000
+#define SOUND_MODE_DA_BIT_SHIFT 20
+
+struct WaveData
+{
+ u16 type;
+ u16 status;
+ u32 freq;
+ u32 loopStart;
+ u32 size; // number of samples
+ s8 data[1]; // samples
+};
+
+#define TONEDATA_TYPE_CGB 0x07
+#define TONEDATA_TYPE_FIX 0x08
+#define TONEDATA_TYPE_SPL 0x40 // key split
+#define TONEDATA_TYPE_RHY 0x80 // rhythm
+
+#define TONEDATA_P_S_PAN 0xc0
+#define TONEDATA_P_S_PAM TONEDATA_P_S_PAN
+
+struct ToneData
+{
+ u8 type;
+ u8 key;
+ u8 length; // sound length (compatible sound)
+ u8 pan_sweep; // pan or sweep (compatible sound ch. 1)
+ struct WaveData *wav;
+ u8 attack;
+ u8 decay;
+ u8 sustain;
+ u8 release;
+};
+
+struct CgbChannel
+{
+ u8 sf;
+ u8 ty;
+ u8 rightVolume;
+ u8 leftVolume;
+ u8 at;
+ u8 de;
+ u8 su;
+ u8 re;
+ u8 ky;
+ u8 ev;
+ u8 eg;
+ u8 ec;
+ u8 echoVolume;
+ u8 echoLength;
+ u8 d1;
+ u8 d2;
+ u8 gt;
+ u8 mk;
+ u8 ve;
+ u8 pr;
+ u8 rp;
+ u8 d3[3];
+ u8 d5;
+ u8 sg;
+ u8 n4;
+ u8 pan;
+ u8 panMask;
+ u8 mo;
+ u8 le;
+ u8 sw;
+ u32 fr;
+ u32 wp;
+ u32 cp;
+ u32 tp;
+ u32 pp;
+ u32 np;
+ u8 d4[8];
+};
+
+struct MusicPlayerTrack;
+
+struct SoundChannel
+{
+ u8 status;
+ u8 type;
+ u8 rightVolume;
+ u8 leftVolume;
+ u8 attack;
+ u8 decay;
+ u8 sustain;
+ u8 release;
+ u8 ky;
+ u8 ev;
+ u8 er;
+ u8 el;
+ u8 echoVolume;
+ u8 echoLength;
+ u8 d1;
+ u8 d2;
+ u8 gt;
+ u8 mk;
+ u8 ve;
+ u8 pr;
+ u8 rp;
+ u8 d3[3];
+ u32 ct;
+ u32 fw;
+ u32 freq;
+ struct WaveData *wav;
+ u32 cp;
+ struct MusicPlayerTrack *track;
+ u32 pp;
+ u32 np;
+ u32 d4;
+ u16 xpi;
+ u16 xpc;
+};
+
+#define MAX_DIRECTSOUND_CHANNELS 12
+
+#define PCM_DMA_BUF_SIZE 1584 // size of Direct Sound buffer
+
+struct SoundInfo
+{
+ // This field is normally equal to ID_NUMBER but it is set to other
+ // values during sensitive operations for locking purposes.
+ // This field should be volatile but isn't. This could potentially cause
+ // race conditions.
+ u32 ident;
+
+ vu8 pcmDmaCounter;
+
+ // Direct Sound
+ u8 reverb;
+ u8 maxChans;
+ u8 masterVolume;
+ u8 freq;
+
+ u8 mode;
+ u8 c15;
+ u8 pcmDmaPeriod; // number of V-blanks per PCM DMA
+ u8 maxLines;
+ u8 gap[3];
+ s32 pcmSamplesPerVBlank;
+ s32 pcmFreq;
+ s32 divFreq;
+ struct CgbChannel *cgbChans;
+ u32 func;
+ u32 intp;
+ void (*CgbSound)(void);
+ void (*CgbOscOff)(u8);
+ u32 (*MidiKeyToCgbFreq)(u8, u8, u8);
+ u32 MPlayJumpTable;
+ u32 plynote;
+ u32 ExtVolPit;
+ u8 gap2[16];
+ struct SoundChannel chans[MAX_DIRECTSOUND_CHANNELS];
+ s8 pcmBuffer[PCM_DMA_BUF_SIZE * 2];
+};
+
+struct SongHeader
+{
+ u8 trackCount;
+ u8 blockCount;
+ u8 priority;
+ u8 reverb;
+ struct ToneData *tone;
+ u8 *part[1];
+};
+
+struct PokemonCrySong
+{
+ u8 trackCount;
+ u8 blockCount;
+ u8 priority;
+ u8 reverb;
+ struct ToneData *tone;
+ u8 *part[2];
+ u8 gap;
+ u8 part0; // 0x11
+ u8 tuneValue; // 0x12
+ u8 gotoCmd; // 0x13
+ u32 gotoTarget; // 0x14
+ u8 part1; // 0x18
+ u8 tuneValue2; // 0x19
+ u8 cont[2]; // 0x1A
+ u8 volCmd; // 0x1C
+ u8 volumeValue; // 0x1D
+ u8 unkCmd0D[2]; // 0x1E
+ u32 unkCmd0DParam; // 0x20
+ u8 xreleCmd[2]; // 0x24
+ u8 releaseValue; // 0x26
+ u8 panCmd;
+ u8 panValue; // 0x28
+ u8 tieCmd; // 0x29
+ u8 tieKeyValue; // 0x2A
+ u8 tieVelocityValue; // 0x2B
+ u8 unkCmd0C[2]; // 0x2C
+ u16 unkCmd0CParam; // 0x2E
+ u8 end[2]; // 0x30
+};
+
+#define MPT_FLG_VOLSET 0x01
+#define MPT_FLG_VOLCHG 0x03
+#define MPT_FLG_PITSET 0x04
+#define MPT_FLG_PITCHG 0x0C
+#define MPT_FLG_START 0x40
+#define MPT_FLG_EXIST 0x80
+
+struct MusicPlayerTrack
+{
+ u8 flags;
+ u8 wait;
+ u8 patternLevel;
+ u8 repN;
+ u8 gateTime;
+ u8 key;
+ u8 velocity;
+ u8 runningStatus;
+ u8 keyM;
+ u8 pitM;
+ s8 keyShift;
+ s8 keyShiftX;
+ s8 tune;
+ u8 pitX;
+ s8 bend;
+ u8 bendRange;
+ u8 volMR;
+ u8 volML;
+ u8 vol;
+ u8 volX;
+ s8 pan;
+ s8 panX;
+ s8 modM;
+ u8 mod;
+ u8 modT;
+ u8 lfoSpeed;
+ u8 lfoSpeedC;
+ u8 lfoDelay;
+ u8 lfoDelayC;
+ u8 priority;
+ u8 echoVolume;
+ u8 echoLength;
+ struct SoundChannel *chan;
+ struct ToneData tone;
+ u8 gap[10];
+ u16 unk_3A;
+ u32 unk_3C;
+ u8 *cmdPtr;
+ u8 *patternStack[3];
+};
+
+#define MUSICPLAYER_STATUS_TRACK 0x0000ffff
+#define MUSICPLAYER_STATUS_PAUSE 0x80000000
+
+#define MAX_MUSICPLAYER_TRACKS 16
+
+#define TEMPORARY_FADE 0x0001
+#define FADE_IN 0x0002
+#define FADE_VOL_MAX 64
+#define FADE_VOL_SHIFT 2
+
+struct MusicPlayerInfo
+{
+ struct SongHeader *songHeader;
+ u32 status;
+ u8 trackCount;
+ u8 priority;
+ u8 cmd;
+ u8 unk_B;
+ u32 clock;
+ u8 gap[8];
+ u8 *memAccArea;
+ u16 tempoD;
+ u16 tempoU;
+ u16 tempoI;
+ u16 tempoC;
+ u16 fadeOI;
+ u16 fadeOC;
+ u16 fadeOV;
+ struct MusicPlayerTrack *tracks;
+ struct ToneData *tone;
+ u32 ident;
+ u32 func;
+ u32 intp;
+};
+
+struct MusicPlayer
+{
+ struct MusicPlayerInfo *info;
+ struct MusicPlayerTrack *track;
+ u8 unk_8;
+ u16 unk_A;
+};
+
+struct Song
+{
+ struct SongHeader *header;
+ u16 ms;
+ u16 me;
+};
+
+extern const struct MusicPlayer gMPlayTable[];
+extern const struct Song gSongTable[];
+
+
+
+extern u8 gMPlayMemAccArea[];
+
+//u8 gPokemonCrySong[52];
+//u8 gPokemonCrySongs[52 * MAX_POKEMON_CRIES];
+
+#define MAX_POKEMON_CRIES 2
+
+extern struct PokemonCrySong gPokemonCrySong;
+extern struct PokemonCrySong gPokemonCrySongs[];
+
+extern struct MusicPlayerInfo gPokemonCryMusicPlayers[];
+extern struct MusicPlayerTrack gPokemonCryTracks[];
+
+extern char SoundMainRAM[];
+
+extern void *gMPlayJumpTable[];
+
+typedef void (*XcmdFunc)(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+extern const XcmdFunc gXcmdTable[];
+
+extern struct CgbChannel gCgbChans[];
+
+extern const u8 gScaleTable[];
+extern const u32 gFreqTable[];
+extern const u16 gPcmSamplesPerVBlankTable[];
+
+extern const u8 gCgbScaleTable[];
+extern const s16 gCgbFreqTable[];
+extern const u8 gNoiseTable[];
+
+extern const struct PokemonCrySong gPokemonCrySongTemplate;
+
+extern const struct ToneData voicegroup000;
+
+extern char gNumMusicPlayers[];
+extern char gMaxLines[];
+
+#define NUM_MUSIC_PLAYERS ((u16)gNumMusicPlayers)
+#define MAX_LINES ((u32)gMaxLines)
+
+u32 umul3232H32(u32 multiplier, u32 multiplicand);
+void SoundMain(void);
+void SoundMainBTM(void);
+void TrackStop(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track);
+void MPlayMain(void);
+void RealClearChain(void *x);
+
+void MPlayContinue(struct MusicPlayerInfo *mplayInfo);
+void MPlayStart(struct MusicPlayerInfo *mplayInfo, struct SongHeader *songHeader);
+void m4aMPlayStop(struct MusicPlayerInfo *mplayInfo);
+void FadeOutBody(struct MusicPlayerInfo *mplayInfo);
+void TrkVolPitSet(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track);
+void MPlayFadeOut(struct MusicPlayerInfo *mplayInfo, u16 speed);
+void ClearChain(void *x);
+void Clear64byte(void *addr);
+void SoundInit(struct SoundInfo *soundInfo);
+void MPlayExtender(struct CgbChannel *cgbChans);
+void m4aSoundMode(u32 mode);
+void MPlayOpen(struct MusicPlayerInfo *mplayInfo, struct MusicPlayerTrack *track, u8 a3);
+void CgbSound(void);
+void CgbOscOff(u8);
+u32 MidiKeyToCgbFreq(u8, u8, u8);
+void DummyFunc(void);
+void MPlayJumpTableCopy(void **mplayJumpTable);
+void SampleFreqSet(u32 freq);
+void m4aSoundVSyncOn(void);
+void m4aSoundVSyncOff(void);
+
+void m4aMPlayTempoControl(struct MusicPlayerInfo *mplayInfo, u16 tempo);
+void m4aMPlayVolumeControl(struct MusicPlayerInfo *mplayInfo, u16 trackBits, u16 volume);
+void m4aMPlayPitchControl(struct MusicPlayerInfo *mplayInfo, u16 trackBits, s16 pitch);
+void m4aMPlayPanpotControl(struct MusicPlayerInfo *mplayInfo, u16 trackBits, s8 pan);
+void ClearModM(struct MusicPlayerTrack *track);
+void m4aMPlayModDepthSet(struct MusicPlayerInfo *mplayInfo, u16 trackBits, u8 modDepth);
+void m4aMPlayLFOSpeedSet(struct MusicPlayerInfo *mplayInfo, u16 trackBits, u8 lfoSpeed);
+
+struct MusicPlayerInfo *SetPokemonCryTone(struct ToneData *tone);
+void SetPokemonCryVolume(u8 val);
+void SetPokemonCryPanpot(s8 val);
+void SetPokemonCryPitch(s16 val);
+void SetPokemonCryLength(u16 val);
+void SetPokemonCryRelease(u8 val);
+void SetPokemonCryProgress(u32 val);
+int IsPokemonCryPlaying(struct MusicPlayerInfo *mplayInfo);
+void SetPokemonCryChorus(s8 val);
+void SetPokemonCryStereo(u32 val);
+void SetPokemonCryPriority(u8 val);
+
+// sound command handler functions
+void ply_fine(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_goto(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_patt(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_pend(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_rept(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_memacc(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_prio(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_tempo(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_keysh(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_voice(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_vol(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_pan(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_bend(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_bendr(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_lfos(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_lfodl(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_mod(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_modt(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_tune(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_port(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_xcmd(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_endtie(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_note(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+
+// extended sound command handler functions
+void ply_xxx(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_xwave(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_xtype(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_xatta(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_xdeca(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_xsust(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_xrele(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_xiecv(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_xiecl(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_xleng(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_xswee(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_xcmd_0C(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+void ply_xcmd_0D(struct MusicPlayerInfo *, struct MusicPlayerTrack *);
+
+#endif // GUARD_GBA_M4A_INTERNAL_H
diff --git a/include/gba/macro.h b/include/gba/macro.h
new file mode 100755
index 0000000..3b35a19
--- /dev/null
+++ b/include/gba/macro.h
@@ -0,0 +1,195 @@
+#ifndef GUARD_GBA_MACRO_H
+#define GUARD_GBA_MACRO_H
+
+#define CPU_FILL(value, dest, size, bit) \
+{ \
+ vu##bit tmp = (vu##bit)(value); \
+ CpuSet((void *)&tmp, \
+ dest, \
+ CPU_SET_##bit##BIT | CPU_SET_SRC_FIXED | ((size)/(bit/8) & 0x1FFFFF)); \
+}
+
+#define CpuFill16(value, dest, size) CPU_FILL(value, dest, size, 16)
+#define CpuFill32(value, dest, size) CPU_FILL(value, dest, size, 32)
+
+#define CPU_COPY(src, dest, size, bit) CpuSet(src, dest, CPU_SET_##bit##BIT | ((size)/(bit/8) & 0x1FFFFF))
+
+#define CpuCopy16(src, dest, size) CPU_COPY(src, dest, size, 16)
+#define CpuCopy32(src, dest, size) CPU_COPY(src, dest, size, 32)
+
+#define CpuFastFill(value, dest, size) \
+{ \
+ vu32 tmp = (vu32)(value); \
+ CpuFastSet((void *)&tmp, \
+ dest, \
+ CPU_FAST_SET_SRC_FIXED | ((size)/(32/8) & 0x1FFFFF)); \
+}
+
+#define CpuFastFill16(value, dest, size) CpuFastFill(((value) << 16) | (value), (dest), (size))
+
+#define CpuFastFill8(value, dest, size) CpuFastFill(((value) << 24) | ((value) << 16) | ((value) << 8) | (value), (dest), (size))
+
+#define CpuFastCopy(src, dest, size) CpuFastSet(src, dest, ((size)/(32/8) & 0x1FFFFF))
+
+#define DmaSet(dmaNum, src, dest, control) \
+{ \
+ vu32 *dmaRegs = (vu32 *)REG_ADDR_DMA##dmaNum; \
+ dmaRegs[0] = (vu32)(src); \
+ dmaRegs[1] = (vu32)(dest); \
+ dmaRegs[2] = (vu32)(control); \
+ dmaRegs[2]; \
+}
+
+#define DMA_FILL(dmaNum, value, dest, size, bit) \
+{ \
+ vu##bit tmp = (vu##bit)(value); \
+ DmaSet(dmaNum, \
+ &tmp, \
+ dest, \
+ (DMA_ENABLE | DMA_START_NOW | DMA_##bit##BIT | DMA_SRC_FIXED | DMA_DEST_INC) << 16 \
+ | ((size)/(bit/8))); \
+}
+
+#define DmaFill16(dmaNum, value, dest, size) DMA_FILL(dmaNum, value, dest, size, 16)
+#define DmaFill32(dmaNum, value, dest, size) DMA_FILL(dmaNum, value, dest, size, 32)
+
+// Note that the DMA clear macros cause the DMA control value to be calculated
+// at runtime rather than compile time. The size is divided by the DMA transfer
+// unit size (2 or 4 bytes) and then combined with the DMA control flags using a
+// bitwise OR operation.
+
+#define DMA_CLEAR(dmaNum, dest, size, bit) \
+{ \
+ vu##bit *_dest = (vu##bit *)(dest); \
+ u32 _size = size; \
+ DmaFill##bit(dmaNum, 0, _dest, _size); \
+}
+
+#define DmaClear16(dmaNum, dest, size) DMA_CLEAR(dmaNum, dest, size, 16)
+#define DmaClear32(dmaNum, dest, size) DMA_CLEAR(dmaNum, dest, size, 32)
+
+#define DMA_COPY(dmaNum, src, dest, size, bit) \
+ DmaSet(dmaNum, \
+ src, \
+ dest, \
+ (DMA_ENABLE | DMA_START_NOW | DMA_##bit##BIT | DMA_SRC_INC | DMA_DEST_INC) << 16 \
+ | ((size)/(bit/8)))
+
+#define DmaCopy16(dmaNum, src, dest, size) DMA_COPY(dmaNum, src, dest, size, 16)
+#define DmaCopy32(dmaNum, src, dest, size) DMA_COPY(dmaNum, src, dest, size, 32)
+
+#define DmaCopyLarge(dmaNum, src, dest, size, block, bit) \
+{ \
+ const void *_src = src; \
+ void *_dest = dest; \
+ u32 _size = size; \
+ while (1) \
+ { \
+ DmaCopy##bit(dmaNum, _src, _dest, (block)); \
+ _src += (block); \
+ _dest += (block); \
+ _size -= (block); \
+ if (_size <= (block)) \
+ { \
+ DmaCopy##bit(dmaNum, _src, _dest, _size); \
+ break; \
+ } \
+ } \
+}
+
+#define DmaCopyLarge16(dmaNum, src, dest, size, block) DmaCopyLarge(dmaNum, src, dest, size, block, 16)
+
+#define DmaCopyLarge32(dmaNum, src, dest, size, block) DmaCopyLarge(dmaNum, src, dest, size, block, 32)
+
+#define DmaFillLarge(dmaNum, value, dest, size, block, bit) \
+{ \
+ void *_dest = dest; \
+ u32 _size = size; \
+ while (1) \
+ { \
+ DmaFill##bit(dmaNum, value, _dest, (block)); \
+ _dest += (block); \
+ _size -= (block); \
+ if (_size <= (block)) \
+ { \
+ DmaFill##bit(dmaNum, value, _dest, _size); \
+ break; \
+ } \
+ } \
+}
+
+#define DmaFillLarge16(dmaNum, value, dest, size, block) DmaFillLarge(dmaNum, value, dest, size, block, 16)
+
+#define DmaFillLarge32(dmaNum, value, dest, size, block) DmaFillLarge(dmaNum, value, dest, size, block, 32)
+
+#define DmaClearLarge(dmaNum, dest, size, block, bit) \
+{ \
+ void *_dest = dest; \
+ u32 _size = size; \
+ while (1) \
+ { \
+ DmaFill##bit(dmaNum, 0, _dest, (block)); \
+ _dest += (block); \
+ _size -= (block); \
+ if (_size <= (block)) \
+ { \
+ DmaFill##bit(dmaNum, 0, _dest, _size); \
+ break; \
+ } \
+ } \
+}
+
+#define DmaClearLarge16(dmaNum, dest, size, block) DmaClearLarge(dmaNum, dest, size, block, 16)
+
+#define DmaClearLarge32(dmaNum, dest, size, block) DmaClearLarge(dmaNum, dest, size, block, 32)
+
+#define DmaCopyDefvars(dmaNum, src, dest, size, bit) \
+{ \
+ const void *_src = src; \
+ void *_dest = dest; \
+ u32 _size = size; \
+ DmaCopy##bit(dmaNum, _src, _dest, _size); \
+}
+
+#define DmaCopy16Defvars(dmaNum, src, dest, size) DmaCopyDefvars(dmaNum, src, dest, size, 16)
+#define DmaCopy32Defvars(dmaNum, src, dest, size) DmaCopyDefvars(dmaNum, src, dest, size, 32)
+
+#define DmaFillDefvars(dmaNum, value, dest, size, bit) \
+{ \
+ void *_dest = dest; \
+ u32 _size = size; \
+ DmaFill##bit(dmaNum, value, _dest, _size); \
+}
+
+#define DmaFill16Defvars(dmaNum, value, dest, size) DmaFillDefvars(dmaNum, value, dest, size, 16)
+#define DmaFill32Defvars(dmaNum, value, dest, size) DmaFillDefvars(dmaNum, value, dest, size, 32)
+
+#define DmaClearDefvars(dmaNum, dest, size, bit) \
+{ \
+ void *_dest = dest; \
+ u32 _size = size; \
+ DmaClear##bit(dmaNum, _dest, _size); \
+}
+
+#define DmaClear16Defvars(dmaNum, dest, size) DmaClearDefvars(dmaNum, dest, size, 16)
+#define DmaClear32Defvars(dmaNum, dest, size) DmaClearDefvars(dmaNum, dest, size, 32)
+
+#define DmaStop(dmaNum) \
+{ \
+ vu16 *dmaRegs = (vu16 *)REG_ADDR_DMA##dmaNum; \
+ dmaRegs[5] &= ~(DMA_START_MASK | DMA_DREQ_ON | DMA_REPEAT); \
+ dmaRegs[5] &= ~DMA_ENABLE; \
+ dmaRegs[5]; \
+}
+
+#define IntrEnable(flags) \
+{ \
+ u16 imeTemp; \
+ \
+ imeTemp = REG_IME; \
+ REG_IME = 0; \
+ REG_IE |= flags; \
+ REG_IME = imeTemp; \
+} \
+
+#endif // GUARD_GBA_MACRO_H
diff --git a/include/gba/multiboot.h b/include/gba/multiboot.h
new file mode 100755
index 0000000..14b6594
--- /dev/null
+++ b/include/gba/multiboot.h
@@ -0,0 +1,55 @@
+#ifndef GUARD_GBA_MULTIBOOT_H
+#define GUARD_GBA_MULTIBOOT_H
+
+#define MULTIBOOT_NCHILD 3 // Maximum number of slaves
+#define MULTIBOOT_HEADER_SIZE 0xc0 // Header size
+#define MULTIBOOT_SEND_SIZE_MIN 0x100 // Minimum transmission size
+#define MULTIBOOT_SEND_SIZE_MAX 0x40000 // Maximum transmission size
+
+struct MultiBootParam
+{
+ u32 system_work[5]; // 00
+ u8 handshake_data; // 14
+ u8 padding; // 15
+ u16 handshake_timeout; // 16
+ u8 probe_count; // 18
+ u8 client_data[MULTIBOOT_NCHILD]; // 19
+ u8 palette_data; // 1c
+ u8 response_bit; // 1d
+ u8 client_bit; // 1e
+ u8 reserved1; // 1f
+ const u8 *boot_srcp; // 20
+ const u8 *boot_endp; // 24
+ const u8 *masterp;
+ u8 *reserved2[MULTIBOOT_NCHILD];
+ u32 system_work2[4];
+ u8 sendflag;
+ u8 probe_target_bit;
+ u8 check_wait;
+ u8 server_type;
+};
+
+#define MULTIBOOT_ERROR_04 0x04
+#define MULTIBOOT_ERROR_08 0x08
+#define MULTIBOOT_ERROR_0c 0x0c
+#define MULTIBOOT_ERROR_40 0x40
+#define MULTIBOOT_ERROR_44 0x44
+#define MULTIBOOT_ERROR_48 0x48
+#define MULTIBOOT_ERROR_4c 0x4c
+#define MULTIBOOT_ERROR_80 0x80
+#define MULTIBOOT_ERROR_84 0x84
+#define MULTIBOOT_ERROR_88 0x88
+#define MULTIBOOT_ERROR_8c 0x8c
+#define MULTIBOOT_ERROR_NO_PROBE_TARGET 0x50
+#define MULTIBOOT_ERROR_NO_DLREADY 0x60
+#define MULTIBOOT_ERROR_BOOT_FAILURE 0x70
+#define MULTIBOOT_ERROR_HANDSHAKE_FAILURE 0x71
+
+#define MULTIBOOT_CONNECTION_CHECK_WAIT 15
+
+#define MULTIBOOT_SERVER_TYPE_NORMAL 0
+#define MULTIBOOT_SERVER_TYPE_QUICK 1
+
+#define MULTIBOOT_HANDSHAKE_TIMEOUT 400
+
+#endif // GUARD_GBA_MULTIBOOT_H
diff --git a/include/gba/syscall.h b/include/gba/syscall.h
new file mode 100755
index 0000000..deddec5
--- /dev/null
+++ b/include/gba/syscall.h
@@ -0,0 +1,48 @@
+#ifndef GUARD_GBA_SYSCALL_H
+#define GUARD_GBA_SYSCALL_H
+
+#define RESET_EWRAM 0x01
+#define RESET_IWRAM 0x02
+#define RESET_PALETTE 0x04
+#define RESET_VRAM 0x08
+#define RESET_OAM 0x10
+#define RESET_SIO_REGS 0x20
+#define RESET_SOUND_REGS 0x40
+#define RESET_REGS 0x80
+#define RESET_ALL 0xFF
+
+void SoftReset(u32 resetFlags);
+
+void RegisterRamReset(u32 resetFlags);
+
+void VBlankIntrWait(void);
+
+u16 Sqrt(u32 num);
+
+u16 ArcTan2(s16 x, s16 y);
+
+#define CPU_SET_SRC_FIXED 0x01000000
+#define CPU_SET_16BIT 0x00000000
+#define CPU_SET_32BIT 0x04000000
+
+void CpuSet(const void *src, void *dest, u32 control);
+
+#define CPU_FAST_SET_SRC_FIXED 0x01000000
+
+void CpuFastSet(const void *src, void *dest, u32 control);
+
+void BgAffineSet(struct BgAffineSrcData *src, struct BgAffineDstData *dest, s32 count);
+
+void ObjAffineSet(struct ObjAffineSrcData *src, void *dest, s32 count, s32 offset);
+
+void LZ77UnCompWram(const void *src, void *dest);
+
+void LZ77UnCompVram(const void *src, void *dest);
+
+void RLUnCompWram(const void *src, void *dest);
+
+void RLUnCompVram(const void *src, void *dest);
+
+int MultiBoot(struct MultiBootParam *mp);
+
+#endif // GUARD_GBA_SYSCALL_H
diff --git a/include/gba/types.h b/include/gba/types.h
new file mode 100755
index 0000000..a7dbf3e
--- /dev/null
+++ b/include/gba/types.h
@@ -0,0 +1,140 @@
+#ifndef GUARD_GBA_TYPES_H
+#define GUARD_GBA_TYPES_H
+
+#include <stdint.h>
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef int8_t s8;
+typedef int16_t s16;
+typedef int32_t s32;
+typedef int64_t s64;
+
+typedef volatile u8 vu8;
+typedef volatile u16 vu16;
+typedef volatile u32 vu32;
+typedef volatile u64 vu64;
+typedef volatile s8 vs8;
+typedef volatile s16 vs16;
+typedef volatile s32 vs32;
+typedef volatile s64 vs64;
+
+typedef float f32;
+typedef double f64;
+
+typedef u8 bool8;
+typedef u16 bool16;
+typedef u32 bool32;
+
+struct BgCnt
+{
+ u16 priority:2;
+ u16 charBaseBlock:2;
+ u16 dummy:4;
+ u16 screenBaseBlock:5;
+ u16 areaOverflowMode:1;
+ u16 screenSize:2;
+};
+typedef volatile struct BgCnt vBgCnt;
+
+struct PlttData
+{
+ u16 r:5; // red
+ u16 g:5; // green
+ u16 b:5; // blue
+ u16 unused_15:1;
+};
+
+struct OamData
+{
+ /*0x00*/ u32 y:8;
+ /*0x01*/ u32 affineMode:2; // 0x1, 0x2 -> 0x4
+ u32 objMode:2; // 0x4, 0x8 -> 0xC
+ u32 mosaic:1; // 0x10
+ u32 bpp:1; // 0x20
+ u32 shape:2; // 0x40, 0x80 -> 0xC0
+
+ /*0x02*/ u32 x:9;
+ u32 matrixNum:5; // bits 3/4 are h-flip/v-flip if not in affine mode
+ u32 size:2;
+
+ /*0x04*/ u16 tileNum:10; // 0x3FF
+ u16 priority:2; // 0x400, 0x800 -> 0xC00
+ u16 paletteNum:4;
+ /*0x06*/ u16 affineParam;
+};
+
+#define ST_OAM_OBJ_NORMAL 0
+#define ST_OAM_OBJ_BLEND 1
+#define ST_OAM_OBJ_WINDOW 2
+
+#define ST_OAM_AFFINE_OFF 0
+#define ST_OAM_AFFINE_NORMAL 1
+#define ST_OAM_AFFINE_ERASE 2
+#define ST_OAM_AFFINE_DOUBLE 3
+
+#define ST_OAM_AFFINE_ON_MASK 1
+#define ST_OAM_AFFINE_DOUBLE_MASK 2
+
+#define ST_OAM_4BPP 0
+#define ST_OAM_8BPP 1
+
+#define ST_OAM_SQUARE 0
+#define ST_OAM_H_RECTANGLE 1
+#define ST_OAM_V_RECTANGLE 2
+
+struct BgAffineSrcData
+{
+ s32 texX;
+ s32 texY;
+ s16 scrX;
+ s16 scrY;
+ s16 sx;
+ s16 sy;
+ u16 alpha;
+};
+
+struct BgAffineDstData
+{
+ s16 pa;
+ s16 pb;
+ s16 pc;
+ s16 pd;
+ s32 dx;
+ s32 dy;
+};
+
+struct ObjAffineSrcData
+{
+ s16 xScale;
+ s16 yScale;
+ u16 rotation;
+};
+
+// Multi-player SIO Control Structure
+struct SioMultiCnt
+{
+ u16 baudRate:2; // baud rate
+ u16 si:1; // SI terminal
+ u16 sd:1; // SD terminal
+ u16 id:2; // ID
+ u16 error:1; // error flag
+ u16 enable:1; // SIO enable
+ u16 unused_11_8:4;
+ u16 mode:2; // communication mode (should equal 2)
+ u16 intrEnable:1; // IRQ enable
+ u16 unused_15:1;
+ u16 data; // data
+};
+
+#define ST_SIO_MULTI_MODE 2 // Multi-player communication mode
+
+// baud rate
+#define ST_SIO_9600_BPS 0 // 9600 bps
+#define ST_SIO_38400_BPS 1 // 38400 bps
+#define ST_SIO_57600_BPS 2 // 57600 bps
+#define ST_SIO_115200_BPS 3 // 115200 bps
+
+#endif // GUARD_GBA_TYPES_H
diff --git a/include/global.h b/include/global.h
new file mode 100755
index 0000000..e7132a4
--- /dev/null
+++ b/include/global.h
@@ -0,0 +1,59 @@
+#ifndef GUARD_GLOBAL_H
+#define GUARD_GLOBAL_H
+
+#include <string.h>
+#include "gba/gba.h"
+
+// Prevent cross-jump optimization.
+#define BLOCK_CROSS_JUMP asm("");
+
+// to help in decompiling
+#define asm_comment(x) asm volatile("@ -- " x " -- ")
+#define asm_unified(x) asm(".syntax unified\n" x "\n.syntax divided")
+#define NAKED __attribute__((naked))
+
+// IDE support
+#if defined (__APPLE__) || defined (__CYGWIN__) || defined (_MSC_VER)
+#define _(x) x
+#define __(x) x
+#define INCBIN_U8 {0}
+#define INCBIN_U16 {0}
+#define INCBIN_U32 {0}
+#define INCBIN_S8 {0}
+#define INCBIN_S16 {0}
+#define INCBIN_S32 {0}
+#endif // IDE support
+
+#define ARRAY_COUNT(array) (size_t)(sizeof(array) / sizeof((array)[0]))
+
+#define SWAP(a, b, temp) \
+{ \
+ temp = a; \
+ a = b; \
+ b = temp; \
+}
+
+// useful math macros
+
+// Converts a number to Q8.8 fixed-point format
+#define Q_8_8(n) ((s16)((n) * 256))
+
+// Converts a number to Q4.12 fixed-point format
+#define Q_4_12(n) ((s16)((n) * 4096))
+
+// Converts a number to Q24.8 fixed-point format
+#define Q_24_8(n) ((s32)((n) * 256))
+
+// Converts a Q8.8 fixed-point format number to a regular integer
+#define Q_8_8_TO_INT(n) ((int)((n) / 256))
+
+// Converts a Q4.12 fixed-point format number to a regular integer
+#define Q_4_12_TO_INT(n) ((int)((n) / 4096))
+
+// Converts a Q24.8 fixed-point format number to a regular integer
+#define Q_24_8_TO_INT(n) ((int)((n) >> 8))
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define max(a, b) ((a) >= (b) ? (a) : (b))
+
+#endif // GUARD_GLOBAL_H
diff --git a/ld_script.ld b/ld_script.ld
index 639b678..ec9609c 100755
--- a/ld_script.ld
+++ b/ld_script.ld
@@ -23,6 +23,8 @@ SECTIONS
.text :
{
asm/start.o(.text);
+ src/main.o(.text);
+ asm/main.o(.text);
asm/rom.o(.text);
}=0xFF
}
diff --git a/src/main.c b/src/main.c
new file mode 100755
index 0000000..5855e35
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,16 @@
+#include "global.h"
+
+struct UnkStruct_200B0C0
+{
+ u8 filler0[0x2];
+ u8 unk2;
+ u8 unk3;
+};
+
+extern struct UnkStruct_200B0C0 gUnknown_0200B0C0;
+
+void sub_23C(u8 arg0)
+{
+ gUnknown_0200B0C0.unk2 = arg0;
+ gUnknown_0200B0C0.unk3 = 0;
+}
diff --git a/tools/preproc/.gitignore b/tools/preproc/.gitignore
new file mode 100755
index 0000000..eb34708
--- /dev/null
+++ b/tools/preproc/.gitignore
@@ -0,0 +1 @@
+preproc
diff --git a/tools/preproc/LICENSE b/tools/preproc/LICENSE
new file mode 100755
index 0000000..534d153
--- /dev/null
+++ b/tools/preproc/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2016 YamaArashi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/tools/preproc/Makefile b/tools/preproc/Makefile
new file mode 100755
index 0000000..3d32758
--- /dev/null
+++ b/tools/preproc/Makefile
@@ -0,0 +1,17 @@
+CXX := g++
+
+CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror
+
+SRCS := asm_file.cpp c_file.cpp charmap.cpp preproc.cpp string_parser.cpp \
+ utf8.cpp
+
+HEADERS := asm_file.h c_file.h char_util.h charmap.h preproc.h string_parser.h \
+ utf8.h
+
+.PHONY: clean
+
+preproc: $(SRCS) $(HEADERS)
+ $(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
+
+clean:
+ $(RM) preproc preproc.exe
diff --git a/tools/preproc/asm_file.cpp b/tools/preproc/asm_file.cpp
new file mode 100755
index 0000000..383010a
--- /dev/null
+++ b/tools/preproc/asm_file.cpp
@@ -0,0 +1,529 @@
+// Copyright(c) 2016 YamaArashi
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <cstdio>
+#include <cstdarg>
+#include "preproc.h"
+#include "asm_file.h"
+#include "char_util.h"
+#include "utf8.h"
+#include "string_parser.h"
+
+AsmFile::AsmFile(std::string filename) : m_filename(filename)
+{
+ FILE *fp = std::fopen(filename.c_str(), "rb");
+
+ if (fp == NULL)
+ FATAL_ERROR("Failed to open \"%s\" for reading.\n", filename.c_str());
+
+ std::fseek(fp, 0, SEEK_END);
+
+ m_size = std::ftell(fp);
+
+ if (m_size < 0)
+ FATAL_ERROR("File size of \"%s\" is less than zero.\n", filename.c_str());
+
+ m_buffer = new char[m_size + 1];
+
+ std::rewind(fp);
+
+ if (std::fread(m_buffer, m_size, 1, fp) != 1)
+ FATAL_ERROR("Failed to read \"%s\".\n", filename.c_str());
+
+ m_buffer[m_size] = 0;
+
+ std::fclose(fp);
+
+ m_pos = 0;
+ m_lineNum = 1;
+ m_lineStart = 0;
+
+ RemoveComments();
+}
+
+AsmFile::AsmFile(AsmFile&& other) : m_filename(std::move(other.m_filename))
+{
+ m_buffer = other.m_buffer;
+ m_pos = other.m_pos;
+ m_size = other.m_size;
+ m_lineNum = other.m_lineNum;
+ m_lineStart = other.m_lineStart;
+
+ other.m_buffer = nullptr;
+}
+
+AsmFile::~AsmFile()
+{
+ delete[] m_buffer;
+}
+
+// Removes comments to simplify further processing.
+// It stops upon encountering a null character,
+// which may or may not be the end of file marker.
+// If it's not, the error will be caught later.
+void AsmFile::RemoveComments()
+{
+ long pos = 0;
+ char stringChar = 0;
+
+ for (;;)
+ {
+ if (m_buffer[pos] == 0)
+ return;
+
+ if (stringChar != 0)
+ {
+ if (m_buffer[pos] == '\\' && m_buffer[pos + 1] == stringChar)
+ {
+ pos += 2;
+ }
+ else
+ {
+ if (m_buffer[pos] == stringChar)
+ stringChar = 0;
+ pos++;
+ }
+ }
+ else if (m_buffer[pos] == '@' && (pos == 0 || m_buffer[pos - 1] != '\\'))
+ {
+ while (m_buffer[pos] != '\n' && m_buffer[pos] != 0)
+ m_buffer[pos++] = ' ';
+ }
+ else if (m_buffer[pos] == '/' && m_buffer[pos + 1] == '*')
+ {
+ m_buffer[pos++] = ' ';
+ m_buffer[pos++] = ' ';
+
+ for (;;)
+ {
+ if (m_buffer[pos] == 0)
+ return;
+
+ if (m_buffer[pos] == '*' && m_buffer[pos + 1] == '/')
+ {
+ m_buffer[pos++] = ' ';
+ m_buffer[pos++] = ' ';
+ break;
+ }
+ else
+ {
+ if (m_buffer[pos] != '\n')
+ m_buffer[pos] = ' ';
+ pos++;
+ }
+ }
+ }
+ else
+ {
+ if (m_buffer[pos] == '"' || m_buffer[pos] == '\'')
+ stringChar = m_buffer[pos];
+ pos++;
+ }
+ }
+}
+
+// Checks if we're at a particular directive and if so, consumes it.
+// Returns whether the directive was found.
+bool AsmFile::CheckForDirective(std::string name)
+{
+ long i;
+ long length = static_cast<long>(name.length());
+
+ for (i = 0; i < length && m_pos + i < m_size; i++)
+ if (name[i] != m_buffer[m_pos + i])
+ return false;
+
+ if (i < length)
+ return false;
+
+ m_pos += length;
+
+ return true;
+}
+
+// Checks if we're at a known directive and if so, consumes it.
+// Returns which directive was found.
+Directive AsmFile::GetDirective()
+{
+ SkipWhitespace();
+
+ if (CheckForDirective(".include"))
+ return Directive::Include;
+ else if (CheckForDirective(".string"))
+ return Directive::String;
+ else if (CheckForDirective(".braille"))
+ return Directive::Braille;
+ else
+ return Directive::Unknown;
+}
+
+// Checks if we're at label that ends with '::'.
+// Returns the name if so and an empty string if not.
+std::string AsmFile::GetGlobalLabel()
+{
+ long start = m_pos;
+ long pos = m_pos;
+
+ if (IsIdentifierStartingChar(m_buffer[pos]))
+ {
+ pos++;
+
+ while (IsIdentifierChar(m_buffer[pos]))
+ pos++;
+ }
+
+ if (m_buffer[pos] == ':' && m_buffer[pos + 1] == ':')
+ {
+ m_pos = pos + 2;
+ ExpectEmptyRestOfLine();
+ return std::string(&m_buffer[start], pos - start);
+ }
+
+ return std::string();
+}
+
+// Skips tabs and spaces.
+void AsmFile::SkipWhitespace()
+{
+ while (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ')
+ m_pos++;
+}
+
+// Reads include path.
+std::string AsmFile::ReadPath()
+{
+ SkipWhitespace();
+
+ if (m_buffer[m_pos] != '"')
+ RaiseError("expected file path");
+
+ m_pos++;
+
+ int length = 0;
+ long startPos = m_pos;
+
+ while (m_buffer[m_pos] != '"')
+ {
+ unsigned char c = m_buffer[m_pos++];
+
+ if (c == 0)
+ {
+ if (m_pos >= m_size)
+ RaiseError("unexpected EOF in include string");
+ else
+ RaiseError("unexpected null character in include string");
+ }
+
+ if (!IsAsciiPrintable(c))
+ RaiseError("unexpected character '\\x%02X' in include string", c);
+
+ // Don't bother allowing any escape sequences.
+ if (c == '\\')
+ {
+ c = m_buffer[m_pos];
+ RaiseError("unexpected escape '\\%c' in include string", c);
+ }
+
+ length++;
+
+ if (length > kMaxPath)
+ RaiseError("path is too long");
+ }
+
+ m_pos++; // Go past the right quote.
+
+ ExpectEmptyRestOfLine();
+
+ return std::string(&m_buffer[startPos], length);
+}
+
+// Reads a charmap string.
+int AsmFile::ReadString(unsigned char* s)
+{
+ SkipWhitespace();
+
+ int length;
+ StringParser stringParser(m_buffer, m_size);
+
+ try
+ {
+ m_pos += stringParser.ParseString(m_pos, s, length);
+ }
+ catch (std::runtime_error& e)
+ {
+ RaiseError(e.what());
+ }
+
+ SkipWhitespace();
+
+ if (ConsumeComma())
+ {
+ SkipWhitespace();
+ int padLength = ReadPadLength();
+
+ while (length < padLength)
+ {
+ s[length++] = 0;
+ }
+ }
+
+ ExpectEmptyRestOfLine();
+
+ return length;
+}
+
+int AsmFile::ReadBraille(unsigned char* s)
+{
+ static std::map<char, unsigned char> encoding =
+ {
+ { 'A', 0x01 },
+ { 'B', 0x05 },
+ { 'C', 0x03 },
+ { 'D', 0x0B },
+ { 'E', 0x09 },
+ { 'F', 0x07 },
+ { 'G', 0x0F },
+ { 'H', 0x0D },
+ { 'I', 0x06 },
+ { 'J', 0x0E },
+ { 'K', 0x11 },
+ { 'L', 0x15 },
+ { 'M', 0x13 },
+ { 'N', 0x1B },
+ { 'O', 0x19 },
+ { 'P', 0x17 },
+ { 'Q', 0x1F },
+ { 'R', 0x1D },
+ { 'S', 0x16 },
+ { 'T', 0x1E },
+ { 'U', 0x31 },
+ { 'V', 0x35 },
+ { 'W', 0x2E },
+ { 'X', 0x33 },
+ { 'Y', 0x3B },
+ { 'Z', 0x39 },
+ { ' ', 0x00 },
+ { ',', 0x04 },
+ { '.', 0x2C },
+ { '$', 0xFF },
+ };
+
+ SkipWhitespace();
+
+ int length = 0;
+
+ if (m_buffer[m_pos] != '"')
+ RaiseError("expected braille string literal");
+
+ m_pos++;
+
+ while (m_buffer[m_pos] != '"')
+ {
+ if (length == kMaxStringLength)
+ RaiseError("mapped string longer than %d bytes", kMaxStringLength);
+
+ if (m_buffer[m_pos] == '\\' && m_buffer[m_pos + 1] == 'n')
+ {
+ s[length++] = 0xFE;
+ m_pos += 2;
+ }
+ else
+ {
+ char c = m_buffer[m_pos];
+
+ if (encoding.count(c) == 0)
+ {
+ if (IsAsciiPrintable(c))
+ RaiseError("character '%c' not valid in braille string", m_buffer[m_pos]);
+ else
+ RaiseError("character '\\x%02X' not valid in braille string", m_buffer[m_pos]);
+ }
+
+ s[length++] = encoding[c];
+ m_pos++;
+ }
+ }
+
+ m_pos++; // Go past the right quote.
+
+ ExpectEmptyRestOfLine();
+
+ return length;
+}
+
+// If we're at a comma, consumes it.
+// Returns whether a comma was found.
+bool AsmFile::ConsumeComma()
+{
+ if (m_buffer[m_pos] == ',')
+ {
+ m_pos++;
+ return true;
+ }
+
+ return false;
+}
+
+// Converts digit character to numerical value.
+static int ConvertDigit(char c, int radix)
+{
+ int digit;
+
+ if (c >= '0' && c <= '9')
+ digit = c - '0';
+ else if (c >= 'A' && c <= 'F')
+ digit = 10 + c - 'A';
+ else if (c >= 'a' && c <= 'f')
+ digit = 10 + c - 'a';
+ else
+ return -1;
+
+ return (digit < radix) ? digit : -1;
+}
+
+// Reads an integer. If the integer is greater than maxValue, it returns -1.
+int AsmFile::ReadPadLength()
+{
+ if (!IsAsciiDigit(m_buffer[m_pos]))
+ RaiseError("expected integer");
+
+ int radix = 10;
+
+ if (m_buffer[m_pos] == '0' && m_buffer[m_pos + 1] == 'x')
+ {
+ radix = 16;
+ m_pos += 2;
+ }
+
+ unsigned n = 0;
+ int digit;
+
+ while ((digit = ConvertDigit(m_buffer[m_pos], radix)) != -1)
+ {
+ n = n * radix + digit;
+
+ if (n > kMaxStringLength)
+ RaiseError("pad length greater than maximum length (%d)", kMaxStringLength);
+
+ m_pos++;
+ }
+
+ return n;
+}
+
+// Outputs the current line and moves to the next one.
+void AsmFile::OutputLine()
+{
+ while (m_buffer[m_pos] != '\n' && m_buffer[m_pos] != 0)
+ m_pos++;
+
+ if (m_buffer[m_pos] == 0)
+ {
+ if (m_pos >= m_size)
+ {
+ RaiseWarning("file doesn't end with newline");
+ puts(&m_buffer[m_lineStart]);
+ }
+ else
+ {
+ RaiseError("unexpected null character");
+ }
+ }
+ else
+ {
+ m_buffer[m_pos] = 0;
+ puts(&m_buffer[m_lineStart]);
+ m_buffer[m_pos] = '\n';
+ m_pos++;
+ m_lineStart = m_pos;
+ m_lineNum++;
+ }
+}
+
+// Asserts that the rest of the line is empty and moves to the next one.
+void AsmFile::ExpectEmptyRestOfLine()
+{
+ SkipWhitespace();
+
+ if (m_buffer[m_pos] == 0)
+ {
+ if (m_pos >= m_size)
+ RaiseWarning("file doesn't end with newline");
+ else
+ RaiseError("unexpected null character");
+ }
+ else if (m_buffer[m_pos] == '\n')
+ {
+ m_pos++;
+ m_lineStart = m_pos;
+ m_lineNum++;
+ }
+ else if (m_buffer[m_pos] == '\r')
+ {
+ RaiseError("only Unix-style LF newlines are supported");
+ }
+ else
+ {
+ RaiseError("junk at end of line");
+ }
+}
+
+// Checks if we're at the end of the file.
+bool AsmFile::IsAtEnd()
+{
+ return (m_pos >= m_size);
+}
+
+// Output the current location to set gas's logical file and line numbers.
+void AsmFile::OutputLocation()
+{
+ std::printf("# %ld \"%s\"\n", m_lineNum, m_filename.c_str());
+}
+
+// Reports a diagnostic message.
+void AsmFile::ReportDiagnostic(const char* type, const char* format, std::va_list args)
+{
+ const int bufferSize = 1024;
+ char buffer[bufferSize];
+ std::vsnprintf(buffer, bufferSize, format, args);
+ std::fprintf(stderr, "%s:%ld: %s: %s\n", m_filename.c_str(), m_lineNum, type, buffer);
+}
+
+#define DO_REPORT(type) \
+do \
+{ \
+ std::va_list args; \
+ va_start(args, format); \
+ ReportDiagnostic(type, format, args); \
+ va_end(args); \
+} while (0)
+
+// Reports an error diagnostic and terminates the program.
+void AsmFile::RaiseError(const char* format, ...)
+{
+ DO_REPORT("error");
+ std::exit(1);
+}
+
+// Reports a warning diagnostic.
+void AsmFile::RaiseWarning(const char* format, ...)
+{
+ DO_REPORT("warning");
+}
diff --git a/tools/preproc/asm_file.h b/tools/preproc/asm_file.h
new file mode 100755
index 0000000..d73b36e
--- /dev/null
+++ b/tools/preproc/asm_file.h
@@ -0,0 +1,72 @@
+// Copyright(c) 2016 YamaArashi
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef ASM_FILE_H
+#define ASM_FILE_H
+
+#include <cstdarg>
+#include <cstdint>
+#include <string>
+#include "preproc.h"
+
+enum class Directive
+{
+ Include,
+ String,
+ Braille,
+ Unknown
+};
+
+class AsmFile
+{
+public:
+ AsmFile(std::string filename);
+ AsmFile(AsmFile&& other);
+ AsmFile(const AsmFile&) = delete;
+ ~AsmFile();
+ Directive GetDirective();
+ std::string GetGlobalLabel();
+ std::string ReadPath();
+ int ReadString(unsigned char* s);
+ int ReadBraille(unsigned char* s);
+ bool IsAtEnd();
+ void OutputLine();
+ void OutputLocation();
+
+private:
+ char* m_buffer;
+ long m_pos;
+ long m_size;
+ long m_lineNum;
+ long m_lineStart;
+ std::string m_filename;
+
+ bool ConsumeComma();
+ int ReadPadLength();
+ void RemoveComments();
+ bool CheckForDirective(std::string name);
+ void SkipWhitespace();
+ void ExpectEmptyRestOfLine();
+ void ReportDiagnostic(const char* type, const char* format, std::va_list args);
+ void RaiseError(const char* format, ...);
+ void RaiseWarning(const char* format, ...);
+};
+
+#endif // ASM_FILE_H
diff --git a/tools/preproc/c_file.cpp b/tools/preproc/c_file.cpp
new file mode 100755
index 0000000..229f568
--- /dev/null
+++ b/tools/preproc/c_file.cpp
@@ -0,0 +1,429 @@
+// Copyright(c) 2016 YamaArashi
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <cstdio>
+#include <cstdarg>
+#include <string>
+#include <memory>
+#include "preproc.h"
+#include "c_file.h"
+#include "char_util.h"
+#include "utf8.h"
+#include "string_parser.h"
+
+CFile::CFile(std::string filename) : m_filename(filename)
+{
+ FILE *fp = std::fopen(filename.c_str(), "rb");
+
+ if (fp == NULL)
+ FATAL_ERROR("Failed to open \"%s\" for reading.\n", filename.c_str());
+
+ std::fseek(fp, 0, SEEK_END);
+
+ m_size = std::ftell(fp);
+
+ if (m_size < 0)
+ FATAL_ERROR("File size of \"%s\" is less than zero.\n", filename.c_str());
+
+ m_buffer = new char[m_size + 1];
+
+ std::rewind(fp);
+
+ if (std::fread(m_buffer, m_size, 1, fp) != 1)
+ FATAL_ERROR("Failed to read \"%s\".\n", filename.c_str());
+
+ m_buffer[m_size] = 0;
+
+ std::fclose(fp);
+
+ m_pos = 0;
+ m_lineNum = 1;
+}
+
+CFile::CFile(CFile&& other) : m_filename(std::move(other.m_filename))
+{
+ m_buffer = other.m_buffer;
+ m_pos = other.m_pos;
+ m_size = other.m_size;
+ m_lineNum = other.m_lineNum;
+
+ other.m_buffer = nullptr;
+}
+
+CFile::~CFile()
+{
+ delete[] m_buffer;
+}
+
+void CFile::Preproc()
+{
+ char stringChar = 0;
+
+ while (m_pos < m_size)
+ {
+ if (stringChar)
+ {
+ if (m_buffer[m_pos] == stringChar)
+ {
+ std::putchar(stringChar);
+ m_pos++;
+ stringChar = 0;
+ }
+ else if (m_buffer[m_pos] == '\\' && m_buffer[m_pos + 1] == stringChar)
+ {
+ std::putchar('\\');
+ std::putchar(stringChar);
+ m_pos += 2;
+ }
+ else
+ {
+ if (m_buffer[m_pos] == '\n')
+ m_lineNum++;
+ std::putchar(m_buffer[m_pos]);
+ m_pos++;
+ }
+ }
+ else
+ {
+ TryConvertString();
+ TryConvertIncbin();
+
+ if (m_pos >= m_size)
+ break;
+
+ char c = m_buffer[m_pos++];
+
+ std::putchar(c);
+
+ if (c == '\n')
+ m_lineNum++;
+ else if (c == '"')
+ stringChar = '"';
+ else if (c == '\'')
+ stringChar = '\'';
+ }
+ }
+}
+
+bool CFile::ConsumeHorizontalWhitespace()
+{
+ if (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ')
+ {
+ m_pos++;
+ return true;
+ }
+
+ return false;
+}
+
+bool CFile::ConsumeNewline()
+{
+ if (m_buffer[m_pos] == '\r' && m_buffer[m_pos + 1] == '\n')
+ {
+ m_pos += 2;
+ m_lineNum++;
+ std::putchar('\n');
+ return true;
+ }
+
+ if (m_buffer[m_pos] == '\n')
+ {
+ m_pos++;
+ m_lineNum++;
+ std::putchar('\n');
+ return true;
+ }
+
+ return false;
+}
+
+void CFile::SkipWhitespace()
+{
+ while (ConsumeHorizontalWhitespace() || ConsumeNewline())
+ ;
+}
+
+void CFile::TryConvertString()
+{
+ long oldPos = m_pos;
+ long oldLineNum = m_lineNum;
+ bool noTerminator = false;
+
+ if (m_buffer[m_pos] != '_' || (m_pos > 0 && IsIdentifierChar(m_buffer[m_pos - 1])))
+ return;
+
+ m_pos++;
+
+ if (m_buffer[m_pos] == '_')
+ {
+ noTerminator = true;
+ m_pos++;
+ }
+
+ SkipWhitespace();
+
+ if (m_buffer[m_pos] != '(')
+ {
+ m_pos = oldPos;
+ m_lineNum = oldLineNum;
+ return;
+ }
+
+ m_pos++;
+
+ SkipWhitespace();
+
+ std::printf("{ ");
+
+ while (1)
+ {
+ SkipWhitespace();
+
+ if (m_buffer[m_pos] == '"')
+ {
+ unsigned char s[kMaxStringLength];
+ int length;
+ StringParser stringParser(m_buffer, m_size);
+
+ try
+ {
+ m_pos += stringParser.ParseString(m_pos, s, length);
+ }
+ catch (std::runtime_error& e)
+ {
+ RaiseError(e.what());
+ }
+
+ for (int i = 0; i < length; i++)
+ printf("0x%02X, ", s[i]);
+ }
+ else if (m_buffer[m_pos] == ')')
+ {
+ m_pos++;
+ break;
+ }
+ else
+ {
+ if (m_pos >= m_size)
+ RaiseError("unexpected EOF");
+ if (IsAsciiPrintable(m_buffer[m_pos]))
+ RaiseError("unexpected character '%c'", m_buffer[m_pos]);
+ else
+ RaiseError("unexpected character '\\x%02X'", m_buffer[m_pos]);
+ }
+ }
+
+ if (noTerminator)
+ std::printf(" }");
+ else
+ std::printf("0xFF }");
+}
+
+bool CFile::CheckIdentifier(const std::string& ident)
+{
+ unsigned int i;
+
+ for (i = 0; i < ident.length() && m_pos + i < (unsigned)m_size; i++)
+ if (ident[i] != m_buffer[m_pos + i])
+ return false;
+
+ return (i == ident.length());
+}
+
+std::unique_ptr<unsigned char[]> CFile::ReadWholeFile(const std::string& path, int& size)
+{
+ FILE* fp = std::fopen(path.c_str(), "rb");
+
+ if (fp == nullptr)
+ RaiseError("Failed to open \"%s\" for reading.\n", path.c_str());
+
+ std::fseek(fp, 0, SEEK_END);
+
+ size = std::ftell(fp);
+
+ std::unique_ptr<unsigned char[]> buffer = std::unique_ptr<unsigned char[]>(new unsigned char[size]);
+
+ std::rewind(fp);
+
+ if (std::fread(buffer.get(), size, 1, fp) != 1)
+ RaiseError("Failed to read \"%s\".\n", path.c_str());
+
+ std::fclose(fp);
+
+ return buffer;
+}
+
+int ExtractData(const std::unique_ptr<unsigned char[]>& buffer, int offset, int size)
+{
+ switch (size)
+ {
+ case 1:
+ return buffer[offset];
+ case 2:
+ return (buffer[offset + 1] << 8)
+ | buffer[offset];
+ case 4:
+ return (buffer[offset + 3] << 24)
+ | (buffer[offset + 2] << 16)
+ | (buffer[offset + 1] << 8)
+ | buffer[offset];
+ default:
+ FATAL_ERROR("Invalid size passed to ExtractData.\n");
+ }
+}
+
+void CFile::TryConvertIncbin()
+{
+ std::string idents[6] = { "INCBIN_S8", "INCBIN_U8", "INCBIN_S16", "INCBIN_U16", "INCBIN_S32", "INCBIN_U32" };
+ int incbinType = -1;
+
+ for (int i = 0; i < 6; i++)
+ {
+ if (CheckIdentifier(idents[i]))
+ {
+ incbinType = i;
+ break;
+ }
+ }
+
+ if (incbinType == -1)
+ return;
+
+ int size = 1 << (incbinType / 2);
+ bool isSigned = ((incbinType % 2) == 0);
+
+ long oldPos = m_pos;
+ long oldLineNum = m_lineNum;
+
+ m_pos += idents[incbinType].length();
+
+ SkipWhitespace();
+
+ if (m_buffer[m_pos] != '(')
+ {
+ m_pos = oldPos;
+ m_lineNum = oldLineNum;
+ return;
+ }
+
+ m_pos++;
+
+ std::printf("{");
+
+ while (true)
+ {
+ SkipWhitespace();
+
+ if (m_buffer[m_pos] != '"')
+ RaiseError("expected double quote");
+
+ m_pos++;
+
+ int startPos = m_pos;
+
+ while (m_buffer[m_pos] != '"')
+ {
+ if (m_buffer[m_pos] == 0)
+ {
+ if (m_pos >= m_size)
+ RaiseError("unexpected EOF in path string");
+ else
+ RaiseError("unexpected null character in path string");
+ }
+
+ if (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n')
+ RaiseError("unexpected end of line character in path string");
+
+ if (m_buffer[m_pos] == '\\')
+ RaiseError("unexpected escape in path string");
+
+ m_pos++;
+ }
+
+ std::string path(&m_buffer[startPos], m_pos - startPos);
+
+ m_pos++;
+
+ int fileSize;
+ std::unique_ptr<unsigned char[]> buffer = ReadWholeFile(path, fileSize);
+
+ if ((fileSize % size) != 0)
+ RaiseError("Size %d doesn't evenly divide file size %d.\n", size, fileSize);
+
+ int count = fileSize / size;
+ int offset = 0;
+
+ for (int i = 0; i < count; i++)
+ {
+ int data = ExtractData(buffer, offset, size);
+ offset += size;
+
+ if (isSigned)
+ std::printf("%d,", data);
+ else
+ std::printf("%uu,", data);
+ }
+
+ SkipWhitespace();
+
+ if (m_buffer[m_pos] != ',')
+ break;
+
+ m_pos++;
+ }
+
+ if (m_buffer[m_pos] != ')')
+ RaiseError("expected ')'");
+
+ m_pos++;
+
+ std::printf("}");
+}
+
+// Reports a diagnostic message.
+void CFile::ReportDiagnostic(const char* type, const char* format, std::va_list args)
+{
+ const int bufferSize = 1024;
+ char buffer[bufferSize];
+ std::vsnprintf(buffer, bufferSize, format, args);
+ std::fprintf(stderr, "%s:%ld: %s: %s\n", m_filename.c_str(), m_lineNum, type, buffer);
+}
+
+#define DO_REPORT(type) \
+do \
+{ \
+ std::va_list args; \
+ va_start(args, format); \
+ ReportDiagnostic(type, format, args); \
+ va_end(args); \
+} while (0)
+
+// Reports an error diagnostic and terminates the program.
+void CFile::RaiseError(const char* format, ...)
+{
+ DO_REPORT("error");
+ std::exit(1);
+}
+
+// Reports a warning diagnostic.
+void CFile::RaiseWarning(const char* format, ...)
+{
+ DO_REPORT("warning");
+}
diff --git a/tools/preproc/c_file.h b/tools/preproc/c_file.h
new file mode 100755
index 0000000..7369aba
--- /dev/null
+++ b/tools/preproc/c_file.h
@@ -0,0 +1,58 @@
+// Copyright(c) 2016 YamaArashi
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef C_FILE_H
+#define C_FILE_H
+
+#include <cstdarg>
+#include <cstdint>
+#include <string>
+#include <memory>
+#include "preproc.h"
+
+class CFile
+{
+public:
+ CFile(std::string filename);
+ CFile(CFile&& other);
+ CFile(const CFile&) = delete;
+ ~CFile();
+ void Preproc();
+
+private:
+ char* m_buffer;
+ long m_pos;
+ long m_size;
+ long m_lineNum;
+ std::string m_filename;
+
+ bool ConsumeHorizontalWhitespace();
+ bool ConsumeNewline();
+ void SkipWhitespace();
+ void TryConvertString();
+ std::unique_ptr<unsigned char[]> ReadWholeFile(const std::string& path, int& size);
+ bool CheckIdentifier(const std::string& ident);
+ void TryConvertIncbin();
+ void ReportDiagnostic(const char* type, const char* format, std::va_list args);
+ void RaiseError(const char* format, ...);
+ void RaiseWarning(const char* format, ...);
+};
+
+#endif // C_FILE_H
diff --git a/tools/preproc/char_util.h b/tools/preproc/char_util.h
new file mode 100755
index 0000000..02a6e1c
--- /dev/null
+++ b/tools/preproc/char_util.h
@@ -0,0 +1,71 @@
+// Copyright(c) 2016 YamaArashi
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef CHAR_UTIL_H
+#define CHAR_UTIL_H
+
+#include <cstdint>
+#include <cassert>
+
+inline bool IsAscii(unsigned char c)
+{
+ return (c < 128);
+}
+
+inline bool IsAsciiAlpha(unsigned char c)
+{
+ return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
+}
+
+inline bool IsAsciiDigit(unsigned char c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+inline bool IsAsciiHexDigit(unsigned char c)
+{
+ return ((c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'f')
+ || (c >= 'A' && c <= 'F'));
+}
+
+inline bool IsAsciiAlphanum(unsigned char c)
+{
+ return (IsAsciiAlpha(c) || IsAsciiDigit(c));
+}
+
+inline bool IsAsciiPrintable(unsigned char c)
+{
+ return (c >= ' ' && c <= '~');
+}
+
+// Returns whether the character can start a C identifier or the identifier of a "{FOO}" constant in strings.
+inline bool IsIdentifierStartingChar(unsigned char c)
+{
+ return IsAsciiAlpha(c) || c == '_';
+}
+
+// Returns whether the character can be used in a C identifier or the identifier of a "{FOO}" constant in strings.
+inline bool IsIdentifierChar(unsigned char c)
+{
+ return IsAsciiAlphanum(c) || c == '_';
+}
+
+#endif // CHAR_UTIL_H
diff --git a/tools/preproc/charmap.cpp b/tools/preproc/charmap.cpp
new file mode 100755
index 0000000..a7bedfe
--- /dev/null
+++ b/tools/preproc/charmap.cpp
@@ -0,0 +1,408 @@
+// Copyright(c) 2016 YamaArashi
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <cstdio>
+#include <cstdint>
+#include <cstdarg>
+#include "preproc.h"
+#include "charmap.h"
+#include "char_util.h"
+#include "utf8.h"
+
+enum LhsType
+{
+ Char,
+ Escape,
+ Constant,
+ None
+};
+
+struct Lhs
+{
+ LhsType type;
+ std::string name;
+ std::int32_t code;
+};
+
+class CharmapReader
+{
+public:
+ CharmapReader(std::string filename);
+ CharmapReader(const CharmapReader&) = delete;
+ ~CharmapReader();
+ Lhs ReadLhs();
+ void ExpectEqualsSign();
+ std::string ReadSequence();
+ void ExpectEmptyRestOfLine();
+ void RaiseError(const char* format, ...);
+
+private:
+ char* m_buffer;
+ long m_pos;
+ long m_size;
+ long m_lineNum;
+ std::string m_filename;
+
+ void RemoveComments();
+ std::string ReadConstant();
+ void SkipWhitespace();
+};
+
+CharmapReader::CharmapReader(std::string filename) : m_filename(filename)
+{
+ FILE *fp = std::fopen(filename.c_str(), "rb");
+
+ if (fp == NULL)
+ FATAL_ERROR("Failed to open \"%s\" for reading.\n", filename.c_str());
+
+ std::fseek(fp, 0, SEEK_END);
+
+ m_size = std::ftell(fp);
+
+ if (m_size < 0)
+ FATAL_ERROR("File size of \"%s\" is less than zero.\n", filename.c_str());
+
+ m_buffer = new char[m_size + 1];
+
+ std::rewind(fp);
+
+ if (std::fread(m_buffer, m_size, 1, fp) != 1)
+ FATAL_ERROR("Failed to read \"%s\".\n", filename.c_str());
+
+ m_buffer[m_size] = 0;
+
+ std::fclose(fp);
+
+ m_pos = 0;
+ m_lineNum = 1;
+
+ RemoveComments();
+}
+
+CharmapReader::~CharmapReader()
+{
+ delete[] m_buffer;
+}
+
+Lhs CharmapReader::ReadLhs()
+{
+ Lhs lhs;
+
+ for (;;)
+ {
+ SkipWhitespace();
+
+ if (m_buffer[m_pos] == '\n')
+ {
+ m_pos++;
+ m_lineNum++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (m_buffer[m_pos] == '\'')
+ {
+ m_pos++;
+
+ bool isEscape = (m_buffer[m_pos] == '\\');
+
+ if (isEscape)
+ {
+ m_pos++;
+ }
+
+ unsigned char c = m_buffer[m_pos];
+
+ if (c == 0)
+ {
+ if (m_pos >= m_size)
+ RaiseError("unexpected EOF in UTF-8 character literal");
+ else
+ RaiseError("unexpected null character in UTF-8 character literal");
+ }
+
+ if (IsAscii(c) && !IsAsciiPrintable(c))
+ RaiseError("unexpected character U+%X in UTF-8 character literal", c);
+
+ UnicodeChar unicodeChar = DecodeUtf8(&m_buffer[m_pos]);
+ std::int32_t code = unicodeChar.code;
+
+ if (code == -1)
+ RaiseError("invalid encoding in UTF-8 character literal");
+
+ m_pos += unicodeChar.encodingLength;
+
+ if (m_buffer[m_pos] != '\'')
+ RaiseError("unterminated character literal");
+
+ m_pos++;
+
+ lhs.code = code;
+
+ if (isEscape)
+ {
+ if (code >= 128)
+ RaiseError("escapes using non-ASCII characters are invalid");
+
+ switch (code)
+ {
+ case '\'':
+ lhs.type = LhsType::Char;
+ break;
+ case '\\':
+ lhs.type = LhsType::Char;
+ case '"':
+ RaiseError("cannot escape double quote");
+ break;
+ default:
+ lhs.type = LhsType::Escape;
+ }
+ }
+ else
+ {
+ if (code == '\'')
+ RaiseError("empty character literal");
+
+ lhs.type = LhsType::Char;
+ }
+ }
+ else if (IsIdentifierStartingChar(m_buffer[m_pos]))
+ {
+ lhs.type = LhsType::Constant;
+ lhs.name = ReadConstant();
+ }
+ else if (m_buffer[m_pos] == '\r')
+ {
+ RaiseError("only Unix-style LF newlines are supported");
+ }
+ else if (m_buffer[m_pos] == 0)
+ {
+ if (m_pos < m_size)
+ RaiseError("unexpected null character");
+ lhs.type = LhsType::None;
+ }
+ else
+ {
+ RaiseError("junk at start of line");
+ }
+
+ return lhs;
+}
+
+void CharmapReader::ExpectEqualsSign()
+{
+ SkipWhitespace();
+
+ if (m_buffer[m_pos] != '=')
+ RaiseError("expected equals sign");
+
+ m_pos++;
+}
+
+static unsigned int ConvertHexDigit(char c)
+{
+ unsigned int digit = 0;
+
+ if (c >= '0' && c <= '9')
+ digit = c - '0';
+ else if (c >= 'A' && c <= 'F')
+ digit = 10 + c - 'A';
+ else if (c >= 'a' && c <= 'f')
+ digit = 10 + c - 'a';
+
+ return digit;
+}
+
+std::string CharmapReader::ReadSequence()
+{
+ SkipWhitespace();
+
+ long startPos = m_pos;
+
+ unsigned int length = 0;
+
+ while (IsAsciiHexDigit(m_buffer[m_pos]) && IsAsciiHexDigit(m_buffer[m_pos + 1]))
+ {
+ m_pos += 2;
+ length++;
+
+ if (length > kMaxCharmapSequenceLength)
+ RaiseError("byte sequence too long (max is %lu bytes)", kMaxCharmapSequenceLength);
+
+ SkipWhitespace();
+ }
+
+ if (IsAsciiHexDigit(m_buffer[m_pos]))
+ RaiseError("each byte must have 2 hex digits");
+
+ if (length == 0)
+ RaiseError("expected byte sequence");
+
+ std::string sequence;
+ sequence.reserve(length);
+
+ m_pos = startPos;
+
+ for (unsigned int i = 0; i < length; i++)
+ {
+ unsigned int digit1 = ConvertHexDigit(m_buffer[m_pos]);
+ unsigned int digit2 = ConvertHexDigit(m_buffer[m_pos + 1]);
+ unsigned char byte = digit1 * 16 + digit2;
+ sequence += byte;
+
+ m_pos += 2;
+ SkipWhitespace();
+ }
+
+ return sequence;
+}
+
+void CharmapReader::ExpectEmptyRestOfLine()
+{
+ SkipWhitespace();
+
+ if (m_buffer[m_pos] == 0)
+ {
+ if (m_pos < m_size)
+ RaiseError("unexpected null character");
+ }
+ else if (m_buffer[m_pos] == '\n')
+ {
+ m_pos++;
+ m_lineNum++;
+ }
+ else if (m_buffer[m_pos] == '\r')
+ {
+ RaiseError("only Unix-style LF newlines are supported");
+ }
+ else
+ {
+ RaiseError("junk at end of line");
+ }
+}
+
+void CharmapReader::RaiseError(const char* format, ...)
+{
+ const int bufferSize = 1024;
+ char buffer[bufferSize];
+
+ std::va_list args;
+ va_start(args, format);
+ std::vsnprintf(buffer, bufferSize, format, args);
+ va_end(args);
+
+ std::fprintf(stderr, "%s:%ld: error: %s\n", m_filename.c_str(), m_lineNum, buffer);
+
+ std::exit(1);
+}
+
+void CharmapReader::RemoveComments()
+{
+ long pos = 0;
+ bool inString = false;
+
+ for (;;)
+ {
+ if (m_buffer[pos] == 0)
+ return;
+
+ if (inString)
+ {
+ if (m_buffer[pos] == '\\' && m_buffer[pos + 1] == '\'')
+ {
+ pos += 2;
+ }
+ else
+ {
+ if (m_buffer[pos] == '\'')
+ inString = false;
+ pos++;
+ }
+ }
+ else if (m_buffer[pos] == '@')
+ {
+ while (m_buffer[pos] != '\n' && m_buffer[pos] != 0)
+ m_buffer[pos++] = ' ';
+ }
+ else
+ {
+ if (m_buffer[pos] == '\'')
+ inString = true;
+ pos++;
+ }
+ }
+}
+
+std::string CharmapReader::ReadConstant()
+{
+ long startPos = m_pos;
+
+ while (IsIdentifierChar(m_buffer[m_pos]))
+ m_pos++;
+
+ return std::string(&m_buffer[startPos], m_pos - startPos);
+}
+
+void CharmapReader::SkipWhitespace()
+{
+ while (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ')
+ m_pos++;
+}
+
+Charmap::Charmap(std::string filename)
+{
+ CharmapReader reader(filename);
+
+ for (;;)
+ {
+ Lhs lhs = reader.ReadLhs();
+
+ if (lhs.type == LhsType::None)
+ return;
+
+ reader.ExpectEqualsSign();
+
+ std::string sequence = reader.ReadSequence();
+
+ switch (lhs.type)
+ {
+ case LhsType::Char:
+ if (m_chars.find(lhs.code) != m_chars.end())
+ reader.RaiseError("redefining char");
+ m_chars[lhs.code] = sequence;
+ break;
+ case LhsType::Escape:
+ if (m_escapes[lhs.code].length() != 0)
+ reader.RaiseError("redefining escape");
+ m_escapes[lhs.code] = sequence;
+ break;
+ case LhsType::Constant:
+ if (m_constants.find(lhs.name) != m_constants.end())
+ reader.RaiseError("redefining constant");
+ m_constants[lhs.name] = sequence;
+ break;
+ }
+
+ reader.ExpectEmptyRestOfLine();
+ }
+}
diff --git a/tools/preproc/charmap.h b/tools/preproc/charmap.h
new file mode 100755
index 0000000..0d752ac
--- /dev/null
+++ b/tools/preproc/charmap.h
@@ -0,0 +1,64 @@
+// Copyright(c) 2016 YamaArashi
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef CHARMAP_H
+#define CHARMAP_H
+
+#include <cstdint>
+#include <string>
+#include <map>
+#include <vector>
+
+class Charmap
+{
+public:
+ Charmap(std::string filename);
+
+ std::string Char(std::int32_t code)
+ {
+ auto it = m_chars.find(code);
+
+ if (it == m_chars.end())
+ return std::string();
+
+ return it->second;
+ }
+
+ std::string Escape(unsigned char code)
+ {
+ return m_escapes[code];
+ }
+
+ std::string Constant(std::string identifier)
+ {
+ auto it = m_constants.find(identifier);
+
+ if (it == m_constants.end())
+ return std::string();
+
+ return it->second;
+ }
+private:
+ std::map<std::int32_t, std::string> m_chars;
+ std::string m_escapes[128];
+ std::map<std::string, std::string> m_constants;
+};
+
+#endif // CHARMAP_H
diff --git a/tools/preproc/preproc.cpp b/tools/preproc/preproc.cpp
new file mode 100755
index 0000000..c9c6042
--- /dev/null
+++ b/tools/preproc/preproc.cpp
@@ -0,0 +1,156 @@
+// Copyright(c) 2016 YamaArashi
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <string>
+#include <stack>
+#include "preproc.h"
+#include "asm_file.h"
+#include "c_file.h"
+#include "charmap.h"
+
+Charmap* g_charmap;
+
+void PrintAsmBytes(unsigned char *s, int length)
+{
+ if (length > 0)
+ {
+ std::printf("\t.byte ");
+ for (int i = 0; i < length; i++)
+ {
+ std::printf("0x%02X", s[i]);
+
+ if (i < length - 1)
+ std::printf(", ");
+ }
+ std::putchar('\n');
+ }
+}
+
+void PreprocAsmFile(std::string filename)
+{
+ std::stack<AsmFile> stack;
+
+ stack.push(AsmFile(filename));
+
+ for (;;)
+ {
+ while (stack.top().IsAtEnd())
+ {
+ stack.pop();
+
+ if (stack.empty())
+ return;
+ else
+ stack.top().OutputLocation();
+ }
+
+ Directive directive = stack.top().GetDirective();
+
+ switch (directive)
+ {
+ case Directive::Include:
+ stack.push(AsmFile(stack.top().ReadPath()));
+ stack.top().OutputLocation();
+ break;
+ case Directive::String:
+ {
+ unsigned char s[kMaxStringLength];
+ int length = stack.top().ReadString(s);
+ PrintAsmBytes(s, length);
+ break;
+ }
+ case Directive::Braille:
+ {
+ unsigned char s[kMaxStringLength];
+ int length = stack.top().ReadBraille(s);
+ PrintAsmBytes(s, length);
+ break;
+ }
+ case Directive::Unknown:
+ {
+ std::string globalLabel = stack.top().GetGlobalLabel();
+
+ if (globalLabel.length() != 0)
+ {
+ const char *s = globalLabel.c_str();
+ std::printf("%s: ; .global %s\n", s, s);
+ }
+ else
+ {
+ stack.top().OutputLine();
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+void PreprocCFile(std::string filename)
+{
+ CFile cFile(filename);
+ cFile.Preproc();
+}
+
+char* GetFileExtension(char* filename)
+{
+ char* extension = filename;
+
+ while (*extension != 0)
+ extension++;
+
+ while (extension > filename && *extension != '.')
+ extension--;
+
+ if (extension == filename)
+ return nullptr;
+
+ extension++;
+
+ if (*extension == 0)
+ return nullptr;
+
+ return extension;
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 3)
+ {
+ std::fprintf(stderr, "Usage: %s SRC_FILE CHARMAP_FILE", argv[0]);
+ return 1;
+ }
+
+ g_charmap = new Charmap(argv[2]);
+
+ char* extension = GetFileExtension(argv[1]);
+
+ if (!extension)
+ FATAL_ERROR("\"%s\" has no file extension.\n", argv[1]);
+
+ if ((extension[0] == 's') && extension[1] == 0)
+ PreprocAsmFile(argv[1]);
+ else if ((extension[0] == 'c' || extension[0] == 'i') && extension[1] == 0)
+ PreprocCFile(argv[1]);
+ else
+ FATAL_ERROR("\"%s\" has an unknown file extension of \"%s\".\n", argv[1], extension);
+
+ return 0;
+}
diff --git a/tools/preproc/preproc.h b/tools/preproc/preproc.h
new file mode 100755
index 0000000..515f64e
--- /dev/null
+++ b/tools/preproc/preproc.h
@@ -0,0 +1,54 @@
+// Copyright(c) 2016 YamaArashi
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef PREPROC_H
+#define PREPROC_H
+
+#include <cstdio>
+#include <cstdlib>
+#include "charmap.h"
+
+#ifdef _MSC_VER
+
+#define FATAL_ERROR(format, ...) \
+do \
+{ \
+ std::fprintf(stderr, format, __VA_ARGS__); \
+ std::exit(1); \
+} while (0)
+
+#else
+
+#define FATAL_ERROR(format, ...) \
+do \
+{ \
+ std::fprintf(stderr, format, ##__VA_ARGS__); \
+ std::exit(1); \
+} while (0)
+
+#endif // _MSC_VER
+
+const int kMaxPath = 256;
+const int kMaxStringLength = 1024;
+const unsigned long kMaxCharmapSequenceLength = 16;
+
+extern Charmap* g_charmap;
+
+#endif // PREPROC_H
diff --git a/tools/preproc/string_parser.cpp b/tools/preproc/string_parser.cpp
new file mode 100755
index 0000000..dd5196a
--- /dev/null
+++ b/tools/preproc/string_parser.cpp
@@ -0,0 +1,355 @@
+// Copyright(c) 2016 YamaArashi
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <cstdio>
+#include <cstdarg>
+#include <stdexcept>
+#include "preproc.h"
+#include "string_parser.h"
+#include "char_util.h"
+#include "utf8.h"
+
+// Reads a charmap char or escape sequence.
+std::string StringParser::ReadCharOrEscape()
+{
+ std::string sequence;
+
+ bool isEscape = (m_buffer[m_pos] == '\\');
+
+ if (isEscape)
+ {
+ m_pos++;
+
+ if (m_buffer[m_pos] == '"')
+ {
+ sequence = g_charmap->Char('"');
+
+ if (sequence.length() == 0)
+ RaiseError("no mapping exists for double quote");
+
+ return sequence;
+ }
+ else if (m_buffer[m_pos] == '\\')
+ {
+ sequence = g_charmap->Char('\\');
+
+ if (sequence.length() == 0)
+ RaiseError("no mapping exists for backslash");
+
+ return sequence;
+ }
+ }
+
+ unsigned char c = m_buffer[m_pos];
+
+ if (c == 0)
+ {
+ if (m_pos >= m_size)
+ RaiseError("unexpected EOF in UTF-8 string");
+ else
+ RaiseError("unexpected null character in UTF-8 string");
+ }
+
+ if (IsAscii(c) && !IsAsciiPrintable(c))
+ RaiseError("unexpected character U+%X in UTF-8 string", c);
+
+ UnicodeChar unicodeChar = DecodeUtf8(&m_buffer[m_pos]);
+ m_pos += unicodeChar.encodingLength;
+ std::int32_t code = unicodeChar.code;
+
+ if (code == -1)
+ RaiseError("invalid encoding in UTF-8 string");
+
+ if (isEscape && code >= 128)
+ RaiseError("escapes using non-ASCII characters are invalid");
+
+ sequence = isEscape ? g_charmap->Escape(code) : g_charmap->Char(code);
+
+ if (sequence.length() == 0)
+ {
+ if (isEscape)
+ RaiseError("unknown escape '\\%c'", code);
+ else
+ RaiseError("unknown character U+%X", code);
+ }
+
+ return sequence;
+}
+
+// Reads a charmap constant, i.e. "{FOO}".
+std::string StringParser::ReadBracketedConstants()
+{
+ std::string totalSequence;
+
+ m_pos++; // Assume we're on the left curly bracket.
+
+ while (m_buffer[m_pos] != '}')
+ {
+ SkipWhitespace();
+
+ if (IsIdentifierStartingChar(m_buffer[m_pos]))
+ {
+ long startPos = m_pos;
+
+ m_pos++;
+
+ while (IsIdentifierChar(m_buffer[m_pos]))
+ m_pos++;
+
+ std::string sequence = g_charmap->Constant(std::string(&m_buffer[startPos], m_pos - startPos));
+
+ if (sequence.length() == 0)
+ {
+ m_buffer[m_pos] = 0;
+ RaiseError("unknown constant '%s'", &m_buffer[startPos]);
+ }
+
+ totalSequence += sequence;
+ }
+ else if (IsAsciiDigit(m_buffer[m_pos]))
+ {
+ Integer integer = ReadInteger();
+
+ switch (integer.size)
+ {
+ case 1:
+ totalSequence += (unsigned char)integer.value;
+ break;
+ case 2:
+ totalSequence += (unsigned char)integer.value;
+ totalSequence += (unsigned char)(integer.value >> 8);
+ break;
+ case 4:
+ totalSequence += (unsigned char)integer.value;
+ totalSequence += (unsigned char)(integer.value >> 8);
+ totalSequence += (unsigned char)(integer.value >> 16);
+ totalSequence += (unsigned char)(integer.value >> 24);
+ break;
+ }
+ }
+ else if (m_buffer[m_pos] == 0)
+ {
+ if (m_pos >= m_size)
+ RaiseError("unexpected EOF after left curly bracket");
+ else
+ RaiseError("unexpected null character within curly brackets");
+ }
+ else
+ {
+ if (IsAsciiPrintable(m_buffer[m_pos]))
+ RaiseError("unexpected character '%c' within curly brackets", m_buffer[m_pos]);
+ else
+ RaiseError("unexpected character '\\x%02X' within curly brackets", m_buffer[m_pos]);
+ }
+ }
+
+ m_pos++; // Go past the right curly bracket.
+
+ return totalSequence;
+}
+
+// Reads a charmap string.
+int StringParser::ParseString(long srcPos, unsigned char* dest, int& destLength)
+{
+ m_pos = srcPos;
+
+ if (m_buffer[m_pos] != '"')
+ RaiseError("expected UTF-8 string literal");
+
+ long start = m_pos;
+
+ m_pos++;
+
+ destLength = 0;
+
+ while (m_buffer[m_pos] != '"')
+ {
+ std::string sequence = (m_buffer[m_pos] == '{') ? ReadBracketedConstants() : ReadCharOrEscape();
+
+ for (const char& c : sequence)
+ {
+ if (destLength == kMaxStringLength)
+ RaiseError("mapped string longer than %d bytes", kMaxStringLength);
+
+ dest[destLength++] = c;
+ }
+ }
+
+ m_pos++; // Go past the right quote.
+
+ return m_pos - start;
+}
+
+void StringParser::RaiseError(const char* format, ...)
+{
+ const int bufferSize = 1024;
+ char buffer[bufferSize];
+
+ std::va_list args;
+ va_start(args, format);
+ std::vsnprintf(buffer, bufferSize, format, args);
+ va_end(args);
+
+ throw std::runtime_error(buffer);
+}
+
+// Converts digit character to numerical value.
+static int ConvertDigit(char c, int radix)
+{
+ int digit;
+
+ if (c >= '0' && c <= '9')
+ digit = c - '0';
+ else if (c >= 'A' && c <= 'F')
+ digit = 10 + c - 'A';
+ else if (c >= 'a' && c <= 'f')
+ digit = 10 + c - 'a';
+ else
+ return -1;
+
+ return (digit < radix) ? digit : -1;
+}
+
+void StringParser::SkipRestOfInteger(int radix)
+{
+ while (ConvertDigit(m_buffer[m_pos], radix) != -1)
+ m_pos++;
+}
+
+StringParser::Integer StringParser::ReadDecimal()
+{
+ const int radix = 10;
+ std::uint64_t n = 0;
+ int digit;
+ std::uint64_t max = UINT32_MAX;
+ long startPos = m_pos;
+
+ while ((digit = ConvertDigit(m_buffer[m_pos], radix)) != -1)
+ {
+ n = n * radix + digit;
+
+ if (n >= max)
+ {
+ SkipRestOfInteger(radix);
+
+ std::string intLiteral(m_buffer + startPos, m_pos - startPos);
+ RaiseError("integer literal \"%s\" is too large", intLiteral.c_str());
+ }
+
+ m_pos++;
+ }
+
+ int size;
+
+ if (m_buffer[m_pos] == 'H')
+ {
+ if (n >= 0x10000)
+ {
+ RaiseError("%lu is too large to be a halfword", (unsigned long)n);
+ }
+
+ size = 2;
+ m_pos++;
+ }
+ else if (m_buffer[m_pos] == 'W')
+ {
+ size = 4;
+ m_pos++;
+ }
+ else
+ {
+ if (n >= 0x10000)
+ size = 4;
+ else if (n >= 0x100)
+ size = 2;
+ else
+ size = 1;
+ }
+
+ return{ static_cast<std::uint32_t>(n), size };
+}
+
+StringParser::Integer StringParser::ReadHex()
+{
+ const int radix = 16;
+ std::uint64_t n = 0;
+ int digit;
+ std::uint64_t max = UINT32_MAX;
+ long startPos = m_pos;
+
+ while ((digit = ConvertDigit(m_buffer[m_pos], radix)) != -1)
+ {
+ n = n * radix + digit;
+
+ if (n >= max)
+ {
+ SkipRestOfInteger(radix);
+
+ std::string intLiteral(m_buffer + startPos, m_pos - startPos);
+ RaiseError("integer literal \"%s\" is too large", intLiteral.c_str());
+ }
+
+ m_pos++;
+ }
+
+ int length = m_pos - startPos;
+ int size = 0;
+
+ switch (length)
+ {
+ case 2:
+ size = 1;
+ break;
+ case 4:
+ size = 2;
+ break;
+ case 8:
+ size = 4;
+ break;
+ default:
+ {
+ std::string intLiteral(m_buffer + startPos, m_pos - startPos);
+ RaiseError("hex integer literal \"0x%s\" doesn't have length of 2, 4, or 8 digits", intLiteral.c_str());
+ }
+ }
+
+ return{ static_cast<std::uint32_t>(n), size };
+}
+
+StringParser::Integer StringParser::ReadInteger()
+{
+ if (!IsAsciiDigit(m_buffer[m_pos]))
+ RaiseError("expected integer");
+
+ if (m_buffer[m_pos] == '0' && m_buffer[m_pos + 1] == 'x')
+ {
+ m_pos += 2;
+ return ReadHex();
+ }
+
+ return ReadDecimal();
+}
+
+// Skips tabs and spaces.
+void StringParser::SkipWhitespace()
+{
+ while (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ')
+ m_pos++;
+}
diff --git a/tools/preproc/string_parser.h b/tools/preproc/string_parser.h
new file mode 100755
index 0000000..abd2bfe
--- /dev/null
+++ b/tools/preproc/string_parser.h
@@ -0,0 +1,55 @@
+// Copyright(c) 2016 YamaArashi
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef STRING_PARSER_H
+#define STRING_PARSER_H
+
+#include <cstdint>
+#include <string>
+#include "preproc.h"
+
+class StringParser
+{
+public:
+ StringParser(char* buffer, long size) : m_buffer(buffer), m_size(size), m_pos(0) {}
+ int ParseString(long srcPos, unsigned char* dest, int &destLength);
+
+private:
+ struct Integer
+ {
+ std::uint32_t value;
+ int size;
+ };
+
+ char* m_buffer;
+ long m_size;
+ long m_pos;
+
+ Integer ReadInteger();
+ Integer ReadDecimal();
+ Integer ReadHex();
+ std::string ReadCharOrEscape();
+ std::string ReadBracketedConstants();
+ void SkipWhitespace();
+ void SkipRestOfInteger(int radix);
+ void RaiseError(const char* format, ...);
+};
+
+#endif // STRING_PARSER_H
diff --git a/tools/preproc/utf8.cpp b/tools/preproc/utf8.cpp
new file mode 100755
index 0000000..7facfd4
--- /dev/null
+++ b/tools/preproc/utf8.cpp
@@ -0,0 +1,92 @@
+// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
+//
+// Copyright(c) 2016 YamaArashi
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <cstdint>
+#include "utf8.h"
+
+static const unsigned char s_byteTypeTable[] =
+{
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
+ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
+ 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
+ 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
+};
+
+const unsigned char s0 = 0 * 12;
+const unsigned char s1 = 1 * 12;
+const unsigned char s2 = 2 * 12;
+const unsigned char s3 = 3 * 12;
+const unsigned char s4 = 4 * 12;
+const unsigned char s5 = 5 * 12;
+const unsigned char s6 = 6 * 12;
+const unsigned char s7 = 7 * 12;
+const unsigned char s8 = 8 * 12;
+
+static const unsigned char s_transitionTable[] =
+{
+ s0,s1,s2,s3,s5,s8,s7,s1,s1,s1,s4,s6, // s0
+ s1,s1,s1,s1,s1,s1,s1,s1,s1,s1,s1,s1, // s1
+ s1,s0,s1,s1,s1,s1,s1,s0,s1,s0,s1,s1, // s2
+ s1,s2,s1,s1,s1,s1,s1,s2,s1,s2,s1,s1, // s3
+ s1,s1,s1,s1,s1,s1,s1,s2,s1,s1,s1,s1, // s4
+ s1,s2,s1,s1,s1,s1,s1,s1,s1,s2,s1,s1, // s5
+ s1,s1,s1,s1,s1,s1,s1,s3,s1,s3,s1,s1, // s6
+ s1,s3,s1,s1,s1,s1,s1,s3,s1,s3,s1,s1, // s7
+ s1,s3,s1,s1,s1,s1,s1,s1,s1,s1,s1,s1, // s8
+};
+
+// Decodes UTF-8 encoded Unicode code point at "s".
+UnicodeChar DecodeUtf8(const char* s)
+{
+ UnicodeChar unicodeChar;
+ int state = s0;
+ auto start = s;
+
+ do
+ {
+ unsigned char byte = *s++;
+ int type = s_byteTypeTable[byte];
+
+ if (state == s0)
+ unicodeChar.code = (0xFF >> type) & byte;
+ else
+ unicodeChar.code = (unicodeChar.code << 6) | (byte & 0x3F);
+
+ state = s_transitionTable[state + type];
+
+ if (state == s1)
+ {
+ unicodeChar.code = -1;
+ return unicodeChar;
+ }
+ } while (state != s0);
+
+ unicodeChar.encodingLength = s - start;
+
+ return unicodeChar;
+}
diff --git a/tools/preproc/utf8.h b/tools/preproc/utf8.h
new file mode 100755
index 0000000..259de67
--- /dev/null
+++ b/tools/preproc/utf8.h
@@ -0,0 +1,34 @@
+// Copyright(c) 2016 YamaArashi
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#ifndef UTF8_H
+#define UTF8_H
+
+#include <cstdint>
+
+struct UnicodeChar
+{
+ std::int32_t code;
+ int encodingLength;
+};
+
+UnicodeChar DecodeUtf8(const char* s);
+
+#endif // UTF8_H