summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--Makefile24
-rw-r--r--checksum.c121
-rw-r--r--hello.c29
-rw-r--r--rom.ld20
-rw-r--r--rom_header.S37
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;
+}
diff --git a/hello.c b/hello.c
new file mode 100644
index 0000000..cc26bf3
--- /dev/null
+++ b/hello.c
@@ -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
+}
diff --git a/rom.ld b/rom.ld
new file mode 100644
index 0000000..403a83d
--- /dev/null
+++ b/rom.ld
@@ -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