diff --git a/.gitignore b/.gitignore index e50e45d..ca1ffe7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ __pycache__ *.gch -*.o \ No newline at end of file +*.o +*.d +*.elf +*.bin +*.nds \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a8fc0b2 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +all: main.nds + +MAIN_OBJ = \ + header.o \ + start.o \ + main.o + +main.elf: $(MAIN_OBJ) + +include arm9.mk +include common.mk diff --git a/addresses_arm9.lds b/addresses_arm9.lds new file mode 100644 index 0000000..b95a94c --- /dev/null +++ b/addresses_arm9.lds @@ -0,0 +1,5 @@ +graphics_engine_a = 0x04000000; +graphics_engine_b = 0x04001000; +pram = 0x05000000; +vram = 0x06000000; +oam = 0x07000000; diff --git a/arm9.lds b/arm9.lds new file mode 100644 index 0000000..b61d403 --- /dev/null +++ b/arm9.lds @@ -0,0 +1,70 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +MEMORY +{ + rom : ORIGIN = 0x00000000, LENGTH = 4M + ram : ORIGIN = 0x02000000, LENGTH = 4M +} +SECTIONS +{ + . = ORIGIN(rom); + + .text.header ALIGN(4) : SUBALIGN(4) + { + KEEP(*(.text.header)) + } > rom + + . = ORIGIN(ram); + + .text.arm9 ALIGN(4) : SUBALIGN(4) + { + KEEP(*(.text.9start)) + *(.text) + *(.text.*) + *(.glue_7t) + *(.glue_7) + *(.vfp11_veneer) + *(.v4_bx) + } > ram AT> rom + + .text ALIGN(4) : SUBALIGN(4) + { + *(.text.*) + *(.text) + } > ram AT> rom + + .data ALIGN(4) : + { + *(.data) + *(.data.*) + } > ram AT> rom + + .rodata ALIGN(4) : + { + *(.rodata) + *(.rodata.*) + } > ram AT> rom + + .ctors ALIGN(4) : SUBALIGN(4) + { + KEEP(*(.ctors)) + KEEP(*(.ctors.*)) + } > ram AT> rom + + .bss ALIGN(4) (NOLOAD) : SUBALIGN(4) + { + *(.bss) + *(.bss.*) + *(COMMON) + } > ram AT> rom + + .text.arm7 ALIGN(4) : SUBALIGN(4) + { + KEEP(*(.text.7start)) + } > ram AT> rom + + INCLUDE "debug.lds" +} + +INCLUDE "symbols.lds" +INCLUDE "addresses_arm9.lds" diff --git a/arm9.mk b/arm9.mk new file mode 100644 index 0000000..1160206 --- /dev/null +++ b/arm9.mk @@ -0,0 +1,5 @@ +TARGET = arm-none-eabi- +AARCH = -march=armv5te+nofp -mlittle-endian +CARCH = -march=armv5te+nofp -mtune=arm946e-s -mlittle-endian -mno-thumb-interwork +OBJARCH = -O elf32-littlearm -B armv5te +LDSCRIPT = arm9.lds diff --git a/common.mk b/common.mk new file mode 100644 index 0000000..fd67be6 --- /dev/null +++ b/common.mk @@ -0,0 +1,91 @@ +OPT ?= -Og +DEBUG = -g -gdwarf-4 + +AFLAGS += --fatal-warnings + +CFLAGS += -falign-functions=4 -ffunction-sections -fdata-sections -fshort-enums -ffreestanding -nostdlib -fno-builtin +#-finline-stringops +CFLAGS += -Wall -Werror -Wfatal-errors +CFLAGS += -Wno-array-bounds +#CFLAGS += -Wno-error=narrowing -Wno-error=unused-variable -Wno-error=array-bounds= +CFLAGS += -Wno-error=maybe-uninitialized +CFLAGS += -Wno-error=unused-but-set-variable +CFLAGS += -Wno-error=unused-variable + +CXXFLAGS += -fno-exceptions -fno-non-call-exceptions -fno-rtti -fno-threadsafe-statics + +# --print-gc-sections +LDFLAGS += --gc-sections --no-warn-rwx-segment --print-memory-usage --entry=_start --orphan-handling=error + +DEPFLAGS = -MMD -MP + +CC = $(TARGET)gcc +CXX = $(TARGET)g++ +AS = $(TARGET)as +LD = $(TARGET)ld +OBJCOPY = $(TARGET)objcopy +OBJDUMP = $(TARGET)objdump + +LIBGCC = $(shell $(CC) -print-file-name=libgcc.a) + +define BUILD_BINARY_O + $(OBJCOPY) \ + -I binary $(OBJARCH) \ + --rename-section .data=.data.$(basename $@) \ + $< $@ +endef + +as_obj_binary = _binary_$(subst .,_,$(subst /,_,$(1))) + +define BUILD_BINARY_H + @echo gen $@ + @echo '#pragma once' > $@ + @echo '#include ' >> $@ + @echo 'extern uint32_t $(call as_obj_binary,$<)_start __asm("$(call as_obj_binary,$<)_start");' >> $@ + @echo 'extern uint32_t $(call as_obj_binary,$<)_end __asm("$(call as_obj_binary,$<)_end");' >> $@ + @echo 'extern uint32_t $(call as_obj_binary,$<)_size __asm("$(call as_obj_binary,$<)_size");' >> $@ +endef + +%.bin.o: %.bin + $(BUILD_BINARY_O) + +%.pcm.o: %.pcm + $(BUILD_BINARY_O) + +%.data.o: %.data + $(BUILD_BINARY_O) + +%.data.pal.o: %.data.pal + $(BUILD_BINARY_O) + +%.o: %.s + $(AS) $(AARCH) $(AFLAGS) $(DEBUG) $< -o $@ + +%.o: %.c + $(CC) $(CARCH) $(CFLAGS) $(OPT) $(DEBUG) $(DEPFLAGS) -MF ${<}.d -c $< -o $@ + +%.o: %.cpp + $(CXX) $(CARCH) $(CFLAGS) $(CXXFLAGS) $(OPT) $(DEBUG) $(DEPFLAGS) -MF ${<}.d -c $< -o $@ + +%.elf: + $(LD) $(LDFLAGS) -T $(LDSCRIPT) $^ -o $@ + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + du -b $@ + +%.nds: %.bin + python patch.py $< $@ + +-include $(shell find -type f -name '*.d') + +.SUFFIXES: +.INTERMEDIATE: +.SECONDARY: +.PHONY: all clean + +%: RCS/%,v +%: RCS/% +%: %,v +%: s.% +%: SCCS/s.% diff --git a/debug.lds b/debug.lds new file mode 100644 index 0000000..d7d682a --- /dev/null +++ b/debug.lds @@ -0,0 +1,94 @@ + .rel.dyn : + { + *(.rel.init) + *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) + *(.rel.fini) + *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) + *(.rel.data.rel.ro .rel.data.rel.ro.* .rel.gnu.linkonce.d.rel.ro.*) + *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) + *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) + *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) + *(.rel.ctors) + *(.rel.dtors) + *(.rel.got) + *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) + PROVIDE_HIDDEN (__rel_iplt_start = .); + *(.rel.iplt) + PROVIDE_HIDDEN (__rel_iplt_end = .); + } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + .rel.plt : + { + *(.rel.plt) + } + .rela.plt : + { + *(.rela.plt) + } + .plt : { *(.plt) } + .iplt : { *(.iplt) } + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1. */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions. */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2. */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2. */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions. */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3. */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF 5. */ + .debug_addr 0 : { *(.debug_addr) } + .debug_line_str 0 : { *(.debug_line_str) } + .debug_loclists 0 : { *(.debug_loclists) } + .debug_macro 0 : { *(.debug_macro) } + .debug_names 0 : { *(.debug_names) } + .debug_rnglists 0 : { *(.debug_rnglists) } + .debug_str_offsets 0 : { *(.debug_str_offsets) } + .debug_sup 0 : { *(.debug_sup) } + .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) KEEP (*(.gnu.attributes)) } + .got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) *(.rela.*) } diff --git a/header.s b/header.s new file mode 100644 index 0000000..b8ebb76 --- /dev/null +++ b/header.s @@ -0,0 +1,53 @@ + .section .text.header + + .fill 12,1,0x0 /* Game Title */ + .ascii "CODE" /* Game Code */ + .ascii "00" /* Maker Code */ + .byte 0x0 /* Unit Code */ + .byte 0x0 /* Encryption Seed Select */ + .byte 0x8 /* Device capacity */ + .fill 7,1,0x0 /* Reserved */ + .byte 0x0 /* Reserved */ + .byte 0x0 /* NDS region */ + .byte 0x0 /* ROM version */ + .byte 0x0 /* Autostart */ + .long _arm9_offset /* ARM9 rom offset */ + .long _arm9_entry /* ARM9 entry address */ + .long _arm9_addr /* ARM9 ram address */ + .long _arm9_size /* ARM9 size */ + .long _arm7_offset /* ARM7 rom offset */ + .long _arm7_entry /* ARM7 entry address */ + .long _arm7_addr /* ARM7 ram address */ + .long _arm7_size /* ARM7 size */ + .long 0x00000000 /* File name table offset */ + .long 0x00000000 /* File name table size */ + .long 0x00000000 /* File allocation table offset */ + .long 0x00000000 /* file allocation table size */ + .long 0x00000000 /* ARM9 overlay offset */ + .long 0x00000000 /* ARM9 overlay size */ + .long 0x00000000 /* ARM7 overlay offset */ + .long 0x00000000 /* ARM7 overlay size */ + .long 0x00586000 /* 040001a4 setting for normal commands */ + .long 0x001808F8 /* 040001a4 setting for KEY1 commands */ + .long 0x00000000 /* Icon/Title offset */ + .short 0x8e6e /* Secure Area Checksum */ + .short 0x0d7e /* Secure Area Delay */ + .long 0x02000a58 /* ARM9 auto load list hook ram address */ + .long 0x02380158 /* ARM7 auto load list hook ram address */ + .fill 8,1,0x0 /* Secure Area Disable */ + .long _rom_size /* Total used rom size */ + .long _header_size /* ROM header size */ + .fill 4,1,0x0 /* Reserved */ + .fill 8,1,0x0 /* Reserved */ + .short 0x0000 /* NAND end of ROM area */ + .short 0x0000 /* NAND start of RW area */ + .fill 24,1,0x0 /* Reserved */ + .fill 16,1,0x0 /* Reserved */ + .include "logo.s" /* Nintendo logo */ + .short 0xcf56 /* Nintendo logo checksum */ + .short 0xffff /* Header checksum */ + .long 0x00000000 /* Debug rom offset */ + .long 0x00000000 /* Debug rom size */ + .long 0x00000000 /* Debug ram address */ + .fill 4,1,0x0 /* Reserved */ + .balign 0x8000,0x0 /* Padding */ diff --git a/logo.s b/logo.s new file mode 100644 index 0000000..8875450 --- /dev/null +++ b/logo.s @@ -0,0 +1,20 @@ + .byte 0x24, 0xff, 0xae, 0x51, 0x69, 0x9a, 0xa2, 0x21 + .byte 0x3d, 0x84, 0x82, 0x0a, 0x84, 0xe4, 0x09, 0xad + .byte 0x11, 0x24, 0x8b, 0x98, 0xc0, 0x81, 0x7f, 0x21 + .byte 0xa3, 0x52, 0xbe, 0x19, 0x93, 0x09, 0xce, 0x20 + .byte 0x10, 0x46, 0x4a, 0x4a, 0xf8, 0x27, 0x31, 0xec + .byte 0x58, 0xc7, 0xe8, 0x33, 0x82, 0xe3, 0xce, 0xbf + .byte 0x85, 0xf4, 0xdf, 0x94, 0xce, 0x4b, 0x09, 0xc1 + .byte 0x94, 0x56, 0x8a, 0xc0, 0x13, 0x72, 0xa7, 0xfc + .byte 0x9f, 0x84, 0x4d, 0x73, 0xa3, 0xca, 0x9a, 0x61 + .byte 0x58, 0x97, 0xa3, 0x27, 0xfc, 0x03, 0x98, 0x76 + .byte 0x23, 0x1d, 0xc7, 0x61, 0x03, 0x04, 0xae, 0x56 + .byte 0xbf, 0x38, 0x84, 0x00, 0x40, 0xa7, 0x0e, 0xfd + .byte 0xff, 0x52, 0xfe, 0x03, 0x6f, 0x95, 0x30, 0xf1 + .byte 0x97, 0xfb, 0xc0, 0x85, 0x60, 0xd6, 0x80, 0x25 + .byte 0xa9, 0x63, 0xbe, 0x03, 0x01, 0x4e, 0x38, 0xe2 + .byte 0xf9, 0xa2, 0x34, 0xff, 0xbb, 0x3e, 0x03, 0x44 + .byte 0x78, 0x00, 0x90, 0xcb, 0x88, 0x11, 0x3a, 0x94 + .byte 0x65, 0xc0, 0x7c, 0x63, 0x87, 0xf0, 0x3c, 0xaf + .byte 0xd6, 0x25, 0xe4, 0x8b, 0x38, 0x0a, 0xac, 0x72 + .byte 0x21, 0xd4, 0xf8, 0x07 diff --git a/main.c b/main.c new file mode 100644 index 0000000..dd74991 --- /dev/null +++ b/main.c @@ -0,0 +1,9 @@ +#include "registers/graphics_engine_a.h" + +void main() +{ + graphics_engine_a.DISPCNT = (1 << 16); // DISPLAY_ON + + // palette ram + *((volatile uint16_t *)(0x05000000)) = (7 << 10) | (31 << 5) | (19 << 0); +} diff --git a/patch.py b/patch.py new file mode 100644 index 0000000..33819f4 --- /dev/null +++ b/patch.py @@ -0,0 +1,59 @@ +def crc16_modbus(b: bytes) -> int: + crc = 0xFFFF + for n in range(len(b)): + crc ^= b[n] + for i in range(8): + if crc & 1: + crc >>= 1 + crc ^= 0xA001 + else: + crc >>= 1 + return crc + +logo = [ + 0x24, 0xff, 0xae, 0x51, 0x69, 0x9a, 0xa2, 0x21, + 0x3d, 0x84, 0x82, 0x0a, 0x84, 0xe4, 0x09, 0xad, + 0x11, 0x24, 0x8b, 0x98, 0xc0, 0x81, 0x7f, 0x21, + 0xa3, 0x52, 0xbe, 0x19, 0x93, 0x09, 0xce, 0x20, + 0x10, 0x46, 0x4a, 0x4a, 0xf8, 0x27, 0x31, 0xec, + 0x58, 0xc7, 0xe8, 0x33, 0x82, 0xe3, 0xce, 0xbf, + 0x85, 0xf4, 0xdf, 0x94, 0xce, 0x4b, 0x09, 0xc1, + 0x94, 0x56, 0x8a, 0xc0, 0x13, 0x72, 0xa7, 0xfc, + 0x9f, 0x84, 0x4d, 0x73, 0xa3, 0xca, 0x9a, 0x61, + 0x58, 0x97, 0xa3, 0x27, 0xfc, 0x03, 0x98, 0x76, + 0x23, 0x1d, 0xc7, 0x61, 0x03, 0x04, 0xae, 0x56, + 0xbf, 0x38, 0x84, 0x00, 0x40, 0xa7, 0x0e, 0xfd, + 0xff, 0x52, 0xfe, 0x03, 0x6f, 0x95, 0x30, 0xf1, + 0x97, 0xfb, 0xc0, 0x85, 0x60, 0xd6, 0x80, 0x25, + 0xa9, 0x63, 0xbe, 0x03, 0x01, 0x4e, 0x38, 0xe2, + 0xf9, 0xa2, 0x34, 0xff, 0xbb, 0x3e, 0x03, 0x44, + 0x78, 0x00, 0x90, 0xcb, 0x88, 0x11, 0x3a, 0x94, + 0x65, 0xc0, 0x7c, 0x63, 0x87, 0xf0, 0x3c, 0xaf, + 0xd6, 0x25, 0xe4, 0x8b, 0x38, 0x0a, 0xac, 0x72, + 0x21, 0xd4, 0xf8, 0x07, +] +logo_b = bytes(logo) +assert crc16_modbus(logo_b) == 0xcf56 + +import sys +with open(sys.argv[1], 'rb') as f: + buf = bytearray(f.read()) + +assert buf[0x15c] == 0x56 +assert buf[0x15d] == 0xcf + +logo_crc = crc16_modbus(buf[0x0c0:0xc0 + 0x9c]) +print(hex(logo_crc)) +assert logo_crc == 0xcf56 + +header_crc = crc16_modbus(buf[0:0x15e]) +print(hex(header_crc)) + +import struct +header_crc_b = struct.pack('') print('#include ') -print('#include ') +print('#define static_assert _Static_assert') print('') print(f'struct {struct_name} {{') for line in c_source(registers): diff --git a/registers/graphics_engine_a.h b/registers/graphics_engine_a.h index 56818a3..b721dec 100644 --- a/registers/graphics_engine_a.h +++ b/registers/graphics_engine_a.h @@ -1,6 +1,6 @@ #include #include -#include +#define static_assert _Static_assert struct graphics_engine_a { volatile uint32_t DISPCNT; diff --git a/registers/graphics_engine_b.h b/registers/graphics_engine_b.h index e2720c7..1369205 100644 --- a/registers/graphics_engine_b.h +++ b/registers/graphics_engine_b.h @@ -1,6 +1,6 @@ #include #include -#include +#define static_assert _Static_assert struct graphics_engine_b { volatile uint32_t DISPCNT; diff --git a/start.s b/start.s new file mode 100644 index 0000000..d0783ae --- /dev/null +++ b/start.s @@ -0,0 +1,65 @@ + .macro FILL_ZERO_32_BYTE_ALIGNED + cmp r1, r10 + beq _fill_break.\@ + .irp i, 2, 3, 4, 5, 6, 7, 8, 9 + mov r\i, #0 + .endr +_fill_loop.\@: + stmia r1!, {r2 - r9} + cmp r1, r10 + blt _fill_loop.\@ +_fill_break.\@: + .endm + + .macro COPY_32_BYTE_ALIGNED + cmp r1, r10 + beq _fill_break.\@ +_fill_loop.\@: + ldmia r0!, {r2 - r9} + stmia r1!, {r2 - r9} + cmp r1, r10 + bne _fill_loop.\@ +_fill_break.\@: + .endm + + .section .text.9start + .global _start +_start: + /* +_link_text: + ldr r0, =__text_load_start + ldr r1, =__text_link_start + ldr r10, =__text_link_end + COPY_32_BYTE_ALIGNED + +_link_data: + ldr r0, =__data_load_start + ldr r1, =__data_link_start + ldr r10, =__data_link_end + COPY_32_BYTE_ALIGNED + +_link_rodata: + ldr r0, =__rodata_load_start + ldr r1, =__rodata_link_start + ldr r10, =__rodata_link_end + COPY_32_BYTE_ALIGNED + */ + +_link_bss: + ldr r1, =__bss_link_start + ldr r10, =__bss_link_end + FILL_ZERO_32_BYTE_ALIGNED + +_c_runtime: + /* set stack pointer */ + ldr sp, =0x23ffffc + + /* jump to main */ + ldr r0, =main + bx r0 + + .section .text.7start + .global _7start +_7start: + mov r1, #0x7 + b _7start diff --git a/symbols.lds b/symbols.lds new file mode 100644 index 0000000..0f700e6 --- /dev/null +++ b/symbols.lds @@ -0,0 +1,33 @@ +__stack_end = ORIGIN(ram) + LENGTH(ram) - 4; + +__text_link_start = ADDR(.text); +__text_link_end = ADDR(.text) + SIZEOF(.text); +__text_load_start = LOADADDR(.text); + +__data_link_start = ADDR(.data); +__data_link_end = ADDR(.data) + SIZEOF(.data); +__data_load_start = LOADADDR(.data); + +__rodata_link_start = ADDR(.rodata); +__rodata_link_end = ADDR(.rodata) + SIZEOF(.rodata); +__rodata_load_start = LOADADDR(.rodata); + +__ctors_link_start = ADDR(.ctors); +__ctors_link_end = ADDR(.ctors) + SIZEOF(.ctors); + +__bss_link_start = ADDR(.bss); +__bss_link_end = ADDR(.bss) + SIZEOF(.bss); + +/* header symbols */ +_arm9_offset = LOADADDR(.text.arm9); +_arm9_entry = ADDR(.text.arm9); +_arm9_addr = ADDR(.text.arm9); +_arm9_size = SIZEOF(.text.arm9); + +_arm7_offset = LOADADDR(.text.arm7); +_arm7_entry = ADDR(.text.arm7); +_arm7_addr = ADDR(.text.arm7); +_arm7_size = SIZEOF(.text.arm7); + +_rom_size = SIZEOF(.text.header) + SIZEOF(.text.arm9) + SIZEOF(.text.arm7); +_header_size = SIZEOF(.text.header);