add trivial example program

This commit is contained in:
Zack Buhman 2024-08-30 05:46:22 -05:00
parent b2e8b3b1ea
commit 977213d712
16 changed files with 523 additions and 4 deletions

6
.gitignore vendored
View File

@ -1,3 +1,7 @@
__pycache__ __pycache__
*.gch *.gch
*.o *.o
*.d
*.elf
*.bin
*.nds

11
Makefile Normal file
View File

@ -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

5
addresses_arm9.lds Normal file
View File

@ -0,0 +1,5 @@
graphics_engine_a = 0x04000000;
graphics_engine_b = 0x04001000;
pram = 0x05000000;
vram = 0x06000000;
oam = 0x07000000;

70
arm9.lds Normal file
View File

@ -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"

5
arm9.mk Normal file
View File

@ -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

91
common.mk Normal file
View File

@ -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 <cstdint>' >> $@
@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.%

94
debug.lds Normal file
View File

@ -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.*) }

53
header.s Normal file
View File

@ -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 */

20
logo.s Normal file
View File

@ -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

9
main.c Normal file
View File

@ -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);
}

59
patch.py Normal file
View File

@ -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('<H', header_crc)
buf[0x15e] = header_crc_b[0]
buf[0x15f] = header_crc_b[1]
with open(sys.argv[2], 'wb') as f:
f.write(buf)

View File

@ -42,7 +42,7 @@ def c_asserts(registers):
print('#include <stdint.h>') print('#include <stdint.h>')
print('#include <stddef.h>') print('#include <stddef.h>')
print('#include <assert.h>') print('#define static_assert _Static_assert')
print('') print('')
print(f'struct {struct_name} {{') print(f'struct {struct_name} {{')
for line in c_source(registers): for line in c_source(registers):

View File

@ -1,6 +1,6 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <assert.h> #define static_assert _Static_assert
struct graphics_engine_a { struct graphics_engine_a {
volatile uint32_t DISPCNT; volatile uint32_t DISPCNT;

View File

@ -1,6 +1,6 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <assert.h> #define static_assert _Static_assert
struct graphics_engine_b { struct graphics_engine_b {
volatile uint32_t DISPCNT; volatile uint32_t DISPCNT;

65
start.s Normal file
View File

@ -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

33
symbols.lds Normal file
View File

@ -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);