diff options
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | Makefile | 24 | ||||
-rw-r--r-- | checksum.c | 121 | ||||
-rw-r--r-- | hello.c | 29 | ||||
-rw-r--r-- | rom.ld | 20 | ||||
-rw-r--r-- | rom_header.S | 37 |
6 files changed, 236 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8e00519 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.o +*.elf +*.bin +*.map +checksum diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1018d2f --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +_CFLAGS=-march=armv4t $(CFLAGS) +_ASFLAGS=-march=armv4t $(ASFLAGS) +_LDFLAGS=$(LDFLAGS) + +OBJECTS=rom_header.o hello.o + +rom.bin: rom.elf checksum Makefile + arm-none-eabi-objcopy -O binary $< $@ + ./checksum $@ + +rom.elf: $(OBJECTS) rom.ld Makefile + arm-none-eabi-ld $(_LDFLAGS) -T rom.ld -Map=rom.map -o $@ $(OBJECTS) + +%.o: %.c Makefile + arm-none-eabi-gcc $(_CFLAGS) -c -o $@ $< + +%.o: %.S Makefile + arm-none-eabi-as $(_ASFLAGS) -o $@ $< + +checksum: checksum.c + +.PHONY: claen +clean: + rm -rf $(OBJECTS) rom.bin rom.elf checksum diff --git a/checksum.c b/checksum.c new file mode 100644 index 0000000..c728887 --- /dev/null +++ b/checksum.c @@ -0,0 +1,121 @@ +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <stdlib.h> + +#define DATA_START_OFFSET (0xa0) +#define DATA_LEN (29) +#define CHECKSUM_OFFSET (0xbd) + +static void print_usage(FILE *const stream, const char *const argv0) +{ + fprintf(stream, "This program calculates Game Boy Advance ROM "); + fprintf(stream, "checksum and places it into the header.\n"); + fprintf(stream, "Usage: %s rom.bin\n", argv0); +} + +static int fseek_set_wrapper(FILE *const stream, const long offset) +{ + const int ret = fseek(stream, offset, SEEK_SET); + if (ret) { + const int err = errno; + if (feof(stream)) { + fprintf(stderr, "fseek(%zu): EOF, file is too small, exiting\n", offset); + } else if (ferror(stream)) { + fprintf(stderr, "fseek(%zu): Error (%d): \"%s\"\n", offset, err, strerror(err)); + } else { + assert(0); + } + } + return ret; +} + +static size_t fread_wrapper(void *const ptr, const size_t size, FILE *const stream) +{ + const size_t ret = fread(ptr, 1, size, stream); + if (ret >= size) { + assert(ret == size); + } else { + const int err = errno; + if (feof(stream)) { + fprintf(stderr, "fread(%zu): EOF, file is too small, exiting\n", size); + } else if (ferror(stream)) { + fprintf(stderr, "fread(%zu): Error (%d): \"%s\"\n", size, err, strerror(err)); + } else { + assert(0); + } + } + return ret; +} + +static size_t fwrite_wrapper(void *const ptr, const size_t size, FILE *const stream) +{ + const size_t ret = fwrite(ptr, 1, size, stream); + if (ret >= size) { + assert(ret == size); + } else { + const int err = errno; + if (feof(stream)) { + fprintf(stderr, "fwrite(%zu): EOF, file is too small, exiting\n", size); + } else if (ferror(stream)) { + fprintf(stderr, "fwrite(%zu): Error (%d): \"%s\"\n", size, err, strerror(err)); + } else { + assert(0); + } + } + return ret; +} + +static int run_checksum(FILE *const stream) +{ + if (fseek_set_wrapper(stream, DATA_START_OFFSET)) { + return EXIT_FAILURE; + } + uint8_t data_raw[DATA_LEN]; + if (sizeof(data_raw) > fread_wrapper(&data_raw, sizeof(data_raw), stream)) { + return EXIT_FAILURE; + } + uint8_t checksum; + if (sizeof(checksum) > fread_wrapper(&checksum, sizeof(checksum), stream)) { + return EXIT_FAILURE; + } + if (fseek_set_wrapper(stream, DATA_START_OFFSET)) { + return EXIT_FAILURE; + } + uint8_t checksum_calculated = 0; + // http://www.problemkaputt.de/gbatek-gba-cartridge-header.htm + // chk=0:for i=0A0h to 0BCh:chk=chk-[i]:next:chk=(chk-19h) and 0FFh + for (size_t i = 0; i < DATA_LEN; i++) { + const uint8_t val = data_raw[i]; + checksum_calculated -= val; + } + checksum_calculated -= 0x19; + printf("checksum = 0x%02x, checksum_calculated = 0x%02x\n", checksum, checksum_calculated); + if (fseek_set_wrapper(stream, CHECKSUM_OFFSET)) { + return EXIT_FAILURE; + } + if (sizeof(checksum) > fwrite_wrapper(&checksum, sizeof(checksum), stream)) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +int main(const int argc, char *argv[]) +{ + if (argc < 2) { + print_usage(stderr, argv[0]); + return EXIT_FAILURE; + } + const char* input_file_name = argv[1]; + FILE *const input_stream = fopen(input_file_name, "rb+"); + if (input_stream == NULL) { + const int err = errno; + fprintf(stderr, "main: fopen(\"%s\", \"r\"): Error (%d): \"%s\"\n", input_file_name, err, strerror(err)); + return EXIT_FAILURE; + } + const int ret = run_checksum(input_stream); + fclose(input_stream); + return ret; +} @@ -0,0 +1,29 @@ +/* http://www.loirak.com/gameboy/gbatutor.php + * hello.c - Gameboy Advance Tutorial - Loirak Development + */ +#define RGB16(r,g,b) ((r)+(g<<5)+(b<<10)) + +void start(void) +{ + char x,y; + unsigned short* Screen = (unsigned short*)0x6000000; + *(unsigned long*)0x4000000 = 0x403; // mode3, bg2 on + + // clear screen, and draw a blue back ground + for(x = 0; x<240;x++) //loop through all x + { + for(y = 0; y<160; y++) //loop through all y + { + Screen[x+y*240] = RGB16(0,0,31); + } + } + + // draw a white HI on the background + for(x = 20; x<=60; x+=15) + for(y = 30; y<50; y++) + Screen[x+y*240] = RGB16(31,31,31); + for (x = 20; x < 35; x++) + Screen[x+40*240] = RGB16(31,31,31); + + while(1){} //loop forever +} @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: Unlicense + */ + +MEMORY { + ROM(rx) : ORIGIN = 0x08000000, LENGTH = 4M +} + +SECTIONS { + . = ORIGIN(ROM); + .text : { + KEEP(*(.rom_header)) + KEEP(*(.text)) + . = ALIGN(4); + *(.text*) + . = ALIGN(4); + KEEP(*(.rodata)) + *(.rodata*) + . = ALIGN(4); + } >ROM +} diff --git a/rom_header.S b/rom_header.S new file mode 100644 index 0000000..c97de02 --- /dev/null +++ b/rom_header.S @@ -0,0 +1,37 @@ +# See http://www.problemkaputt.de/gbatek-gba-cartridge-header.htm +# +# This blob is mostly borrowed from "Legend of Zelda - Minish Cap (U)" ROM. +# sha1sum b4bd50e4131b027c334547b4524e2dbbd4227130 Legend of Zelda - Minish Cap (U) [!].gba +# md5sum a104896da0047abe8bee2a6e3f4c7290 Legend of Zelda - Minish Cap (U) [!].gba + .globl start + .globl __rom_header + .section .rom_header +__rom_header: + b start + .byte 0x24, 0xff, 0xae, 0x51 + .byte 0x69, 0x9a, 0xa2, 0x21, 0x3d, 0x84, 0x82, 0x0a + .byte 0x84, 0xe4, 0x09, 0xad, 0x11, 0x24, 0x8b, 0x98 + .byte 0xc0, 0x81, 0x7f, 0x21, 0xa3, 0x52, 0xbe, 0x19 + .byte 0x93, 0x09, 0xce, 0x20, 0x10, 0x46, 0x4a, 0x4a + .byte 0xf8, 0x27, 0x31, 0xec, 0x58, 0xc7, 0xe8, 0x33 + .byte 0x82, 0xe3, 0xce, 0xbf, 0x85, 0xf4, 0xdf, 0x94 + .byte 0xce, 0x4b, 0x09, 0xc1, 0x94, 0x56, 0x8a, 0xc0 + .byte 0x13, 0x72, 0xa7, 0xfc, 0x9f, 0x84, 0x4d, 0x73 + .byte 0xa3, 0xca, 0x9a, 0x61, 0x58, 0x97, 0xa3, 0x27 + .byte 0xfc, 0x03, 0x98, 0x76, 0x23, 0x1d, 0xc7, 0x61 + .byte 0x03, 0x04, 0xae, 0x56, 0xbf, 0x38, 0x84, 0x00 + .byte 0x40, 0xa7, 0x0e, 0xfd, 0xff, 0x52, 0xfe, 0x03 + .byte 0x6f, 0x95, 0x30, 0xf1, 0x97, 0xfb, 0xc0, 0x85 + .byte 0x60, 0xd6, 0x80, 0x25, 0xa9, 0x63, 0xbe, 0x03 + .byte 0x01, 0x4e, 0x38, 0xe2, 0xf9, 0xa2, 0x34, 0xff + .byte 0xbb, 0x3e, 0x03, 0x44, 0x78, 0x00, 0x90, 0xcb + .byte 0x88, 0x11, 0x3a, 0x94, 0x65, 0xc0, 0x7c, 0x63 + .byte 0x87, 0xf0, 0x3c, 0xaf, 0xd6, 0x25, 0xe4, 0x8b + .byte 0x38, 0x0a, 0xac, 0x72, 0x21, 0xd4, 0xf8, 0x07 + .ascii "TEST ROM BZME" + .ascii "FF" + .byte 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +# checksum byte +# chk=0:for i=0A0h to 0BCh:chk=chk-[i]:next:chk=(chk-19h) and 0FFh + .byte 0xc9 + .byte 0x00, 0x00 |