This commit is contained in:
violette 2025-08-21 22:24:02 -04:00
parent d8f573c678
commit 044d31fa47
8 changed files with 1111 additions and 62 deletions

169
Makefile
View file

@ -1,7 +1,166 @@
CFLAGS = -lm
# SPDX-License-Identifier: CC0-1.0
#
# SPDX-FileContributor: Antonio Niño Díaz, 2022
build:
cc ./main.c $(CFLAGS) -DVBUF -o ./cube
# User config
# ===========
run: build
./cube
# ROM config
# ----------
NAME := vi
GAME_TITLE := "VIOLET'S CV"
GAME_CODE := "00"
# Defines passed to all files
# ---------------------------
DEFINES :=
# Libraries
# ---------
LIBS := -lc
LIBDIRS := # A list of paths
# Include paths
# -------------
INCLUDES := # A list of paths
# Tools
# -----
PREFIX := arm-none-eabi-
CC := $(PREFIX)gcc
CXX := $(PREFIX)g++
OBJDUMP := $(PREFIX)objdump
OBJCOPY := $(PREFIX)objcopy
MKDIR := mkdir
RM := rm -rf
# Verbose flag
# ------------
# `make V=` builds the binary in verbose build mode
V := @
# Directories
# -----------
SOURCEDIR := source
BUILDDIR := build
# Build artfacts
# --------------
ELF := $(NAME).elf
DUMP := $(NAME).dump
ROM := $(NAME).gba
MAP := $(NAME).map
GBAFIX := gbafix/gbafix
# Source files
# ------------
SOURCES_S := $(wildcard $(SOURCEDIR)/*.s $(SOURCEDIR)/**/*.s)
SOURCES_C := $(wildcard $(SOURCEDIR)/*.c $(SOURCEDIR)/**/*.c)
SOURCES_CPP := $(wildcard $(SOURCEDIR)/*.cpp $(SOURCEDIR)/**/*.cpp)
# Compiler and linker flags
# -------------------------
DEFINES += -D__GBA__
ARCH := -mcpu=arm7tdmi -mtune=arm7tdmi
WARNFLAGS := -Wall
INCLUDEFLAGS := $(foreach path,$(INCLUDES),-I$(path)) \
$(foreach path,$(LIBDIRS),-I$(path)/include)
LIBDIRSFLAGS := $(foreach path,$(LIBDIRS),-L$(path)/lib)
ASFLAGS += -x assembler-with-cpp $(DEFINES) $(ARCH) \
-mthumb -mthumb-interwork $(INCLUDEFLAGS) \
-ffunction-sections -fdata-sections -lm
CFLAGS += -std=gnu11 $(WARNFLAGS) $(DEFINES) $(ARCH) \
-mthumb -mthumb-interwork $(INCLUDEFLAGS) -O2 \
-ffunction-sections -fdata-sections -lm -g
CXXFLAGS += -std=gnu++14 $(WARNFLAGS) $(DEFINES) $(ARCH) \
-mthumb -mthumb-interwork $(INCLUDEFLAGS) -O2 \
-ffunction-sections -fdata-sections \
-fno-exceptions -fno-rtti
LDFLAGS := -mthumb -mthumb-interwork $(LIBDIRSFLAGS) \
-Wl,-Map,$(MAP) -Wl,--gc-sections \
-specs=nano.specs -T source/sys/gba_cart.ld \
-Wl,--start-group $(LIBS) -Wl,--end-group -lm
# Intermediate build files
# ------------------------
OBJS := \
$(patsubst $(SOURCEDIR)/%.s,$(BUILDDIR)/%.s.o,$(SOURCES_S)) \
$(patsubst $(SOURCEDIR)/%.c,$(BUILDDIR)/%.c.o,$(SOURCES_C)) \
$(patsubst $(SOURCEDIR)/%.cpp,$(BUILDDIR)/%.cpp.o,$(SOURCES_CPP))
DEPS := $(OBJS:.o=.d)
# Rules
# -----
$(BUILDDIR)/%.s.o : $(SOURCEDIR)/%.s
@echo " AS $<"
@$(MKDIR) -p $(@D) # Build target's directory if it doesn't exist
$(V)$(CC) $(ASFLAGS) -MMD -MP -c -o $@ $<
$(BUILDDIR)/%.c.o : $(SOURCEDIR)/%.c
@echo " CC $<"
@$(MKDIR) -p $(@D) # Build target's directory if it doesn't exist
$(V)$(CC) $(CFLAGS) -MMD -MP -c -o $@ $<
$(BUILDDIR)/%.cpp.o : $(SOURCEDIR)/%.cpp
@echo " CXX $<"
@$(MKDIR) -p $(@D) # Build target's directory if it doesn't exist
$(V)$(CXX) $(CXXFLAGS) -MMD -MP -c -o $@ $<
# Targets
# -------
.PHONY: all clean dump
all: $(ROM)
$(GBAFIX):
$(V)cd gbafix && make
$(ELF): $(OBJS)
@echo " LD $@"
$(V)$(CC) -o $@ $(OBJS) $(LDFLAGS)
$(ROM): $(ELF) $(GBAFIX)
@echo " OBJCOPY $<"
$(V)$(OBJCOPY) -O binary $< $@
@echo " GBAFIX $@"
$(V)$(GBAFIX) $@ -t$(GAME_TITLE) -c$(GAME_CODE)
$(DUMP): $(ELF)
@echo " OBJDUMP $@"
$(V)$(OBJDUMP) -h -C -S $< > $@
dump: $(DUMP)
clean:
@echo " CLEAN"
$(V)$(RM) $(ROM) $(ELF) $(DUMP) $(MAP) $(BUILDDIR)
$(V)cd gbafix && make clean
# Include dependency files if they exist
# --------------------------------------
-include $(DEPS)

1
gbafix/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
gbafix

25
gbafix/Makefile Normal file
View file

@ -0,0 +1,25 @@
# SPDX-License-Identifier: CC0-1.0
#
# SPDX-FileContributor: Antonio Niño Díaz, 2022
NAME := gbafix
CC := gcc
RM := rm -rf
# `make V=` builds the binary in verbose build mode
V := @
CFLAGS := -Wall -O3
.PHONY: all clean
all: $(NAME)
$(NAME): gbafix.c
@echo " HOSTCC $<"
$(V)$(CC) $(CFLAGS) -o $@ $<
clean:
@echo " CLEAN"
$(V)$(RM) $(NAME)

285
gbafix/gbafix.c Normal file
View file

@ -0,0 +1,285 @@
/*
"$Id: gbafix.c,v 1.2 2008-07-30 17:12:51 wntrmute Exp $"
DevkitPro GBA ROM fix utility
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
Please report all bugs and problems through the bug tracker at
"http://sourceforge.net/tracker/?group_id=114505&atid=668551".
"$Header: /lvm/shared/ds/ds/cvs/devkitpro-cvsbackup/tools/gba/gbatools/gbafix.c,v 1.2 2008-07-30 17:12:51 wntrmute Exp $"
*/
//---------------------------------------------------------------------------------
// gbafix.c
//---------------------------------------------------------------------------------
/*
Gameboy Advance ROM fixer (by Dark Fader / BlackThunder / WinterMute / Diegoisawesome)
Validates header of GBA roms.
History
-------
v1.05 - added debug offset argument, (Diegoisawesome)
v1.04 - converted to plain C, (WinterMute)
v1.03 - header.fixed, header.device_type
v1.02 - redefined the options (rgbfix style), checksum=0
v1.01 - fix in parameters
v1.00 - logo, complement
*/
#pragma pack(1)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define VER "1.05"
#define ARGV argv[arg]
#define VALUE (ARGV+2)
#define NUMBER strtoul(VALUE, NULL, 0)
typedef struct
{
uint32_t start_code; // B instruction
uint8_t logo[0xA0-0x04]; // logo data
uint8_t title[0xC]; // game title name
uint32_t game_code; //
uint16_t maker_code; //
uint8_t fixed; // 0x96
uint8_t unit_code; // 0x00
uint8_t device_type; // 0x00
uint8_t unused[7]; //
uint8_t game_version; // 0x00
uint8_t complement; // 800000A0..800000BC
uint16_t checksum; // 0x0000
} Header;
Header header;
unsigned short checksum_without_header = 0;
const Header good_header =
{
// start_code
0xEA00002E,
// 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 } ,
// title
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
// game code
0x00000000,
// maker code
0x3130,
// fixed
0x96,
// unit_code
0x00,
// device type
0x00,
// unused
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
// game version
0x00,
// complement
0x00,
// checksum
0x0000
};
//---------------------------------------------------------------------------------
char HeaderComplement(void)
/*---------------------------------------------------------------------------------
Calculate Header complement check
---------------------------------------------------------------------------------*/
{
int n;
char c = 0;
char *p = (char *)&header + 0xA0;
for (n=0; n<0xBD-0xA0; n++)
{
c += *p++;
}
return -(0x19+c);
}
//---------------------------------------------------------------------------------
int main(int argc, char *argv[])
//---------------------------------------------------------------------------------
{
int arg;
char *argfile = 0;
FILE *infile;
int size,bit;
// show syntax
if (argc <= 1)
{
printf("GBA ROM fixer v"VER" by Dark Fader / BlackThunder / WinterMute / Diegoisawesome \n");
printf("Syntax: gbafix <rom.gba> [-p] [-t[title]] [-c<game_code>] [-m<maker_code>] [-r<version>] [-d<debug>]\n");
printf("\n");
printf("parameters:\n");
printf(" -p Pad to next exact power of 2. No minimum size!\n");
printf(" -t[<title>] Patch title. Stripped filename if none given.\n");
printf(" -c<game_code> Patch game code (four characters)\n");
printf(" -m<maker_code> Patch maker code (two characters)\n");
printf(" -r<version> Patch game version (number)\n");
printf(" -d<debug> Enable debugging handler and set debug entry point (0 or 1)\n");
return -1;
}
// get filename
for (arg=1; arg<argc; arg++)
{
if ((ARGV[0] != '-')) { argfile=ARGV; break; }
}
// check filename
if (!argfile)
{
printf("Filename needed!\n");
return -1;
}
// read file
infile = fopen(argfile, "r+b");
if (!infile) { printf("Error opening input file!\n"); return -1; }
fseek(infile, 0, SEEK_SET);
if (fread(&header, sizeof(header), 1, infile) != 1)
{
printf("Error reading header of input file!\n");
return -1;
}
// fix some data
memcpy(header.logo, good_header.logo, sizeof(header.logo));
memcpy(&header.fixed, &good_header.fixed, sizeof(header.fixed));
memcpy(&header.device_type, &good_header.device_type, sizeof(header.device_type));
// parse command line
for (arg=1; arg<argc; arg++)
{
if ((ARGV[0] == '-'))
{
switch (ARGV[1])
{
case 'p': // pad
{
fseek(infile, 0, SEEK_END);
size = ftell(infile);
for (bit=31; bit>=0; bit--) if (size & (1<<bit)) break;
if (size != (1<<bit))
{
int todo = (1<<(bit+1)) - size;
while (todo--) fputc(0xFF, infile);
}
fseek(infile, 0, SEEK_SET);
break;
}
case 't': // title
{
char title[256];
memset(title, 0, sizeof(title));
if (VALUE[0])
{
strncpy(title, VALUE, sizeof(header.title));
}
else
{
// use filename
char s[256], *begin=s, *t; strcpy(s, argfile);
t = strrchr(s, '\\'); if (t) begin = t+1;
t = strrchr(s, '/'); if (t) begin = t+1;
t = strrchr(s, '.'); if (t) *t = 0;
snprintf(title, sizeof(header.title), "%s", begin);
printf("%s\n",begin);
}
memcpy(header.title, title, sizeof(header.title)); // copy
break;
}
case 'c': // game code
{
//if (!VALUE[0]) { printf("Need value for %s\n", ARGV); break; }
//header.game_code = NUMBER;
header.game_code = VALUE[0] | VALUE[1]<<8 | VALUE[2]<<16 | VALUE[3]<<24;
break;
}
case 'm': // maker code
{
//if (!VALUE[0]) { printf("Need value for %s\n", ARGV); break; }
//header.maker_code = (unsigned short)NUMBER;
header.maker_code = VALUE[0] | VALUE[1]<<8;
break;
}
case 'v': // ignored, compatability with other gbafix
{
break;
}
case 'r': // version
{
if (!VALUE[0]) { printf("Need value for %s\n", ARGV); break; }
header.game_version = (unsigned char)NUMBER;
break;
}
case 'd': // debug
{
if (!VALUE[0]) { printf("Need value for %s\n", ARGV); break; }
header.logo[0x9C-0x04] = 0xA5; // debug enable
header.device_type = (unsigned char)((NUMBER & 1) << 7); // debug handler entry point
break;
}
default:
{
printf("Invalid option: %s\n", ARGV);
}
}
}
}
// update complement check & total checksum
header.complement = 0;
header.checksum = 0; // must be 0
header.complement = HeaderComplement();
//header.checksum = checksum_without_header + HeaderChecksum();
fseek(infile, 0, SEEK_SET);
fwrite(&header, sizeof(header), 1, infile);
fclose(infile);
printf("ROM fixed!\n");
return 0;
}

View file

@ -88,7 +88,7 @@ volatile FIXED_POINT K2 = (2 * CUBE_WIDTH) + 20;
#define SQ(n) (n * n)
#define SQ_FP(n) (MULT_FP(n, n))
#define COORD2INDEX(x, y) (x * VWIDTH + y)
#define COORD2INDEX(x, y) (y * VWIDTH + x)
#define COUPLE2INDEX(x) (COORD2INDEX(x[0], x[1]))
#define GET_ROTATE_X_Q(a) ({ float _a = (FIXED2FLOAT(a)) ; \
@ -117,7 +117,7 @@ struct Quaternions {
FIXED_POINT x;
FIXED_POINT y;
FIXED_POINT z;
} Target, Current, Last;
} Target, Current;
FIXED_POINT interpolationStep = 0;
FIXED_POINT zBuffer[VHEIGHT * VWIDTH];
@ -235,38 +235,62 @@ chooseMainFace()
return frontFacingFace;
}
void
fill_quads(char current_face, char top_left[2], char top_right[2],
char bot_left[2], char bot_right[2])
char
isInQuad(char curr[2], char top[2], char left[2],
char right[2], char bot[2])
{
// calc slope foreach side
float slope_top = 0;
float slope_bot = 0;
float slope_left = INFINITY;
float slope_right = -INFINITY;
char *points[4] = {top, left, bot, right};
if (top_left[0] != top_right[0])
slope_top = (float)(top_right[1] - top_left[1]) /
(float)(top_right[0] - top_left[0]);
if (bot_left[0] == bot_right[0])
slope_bot = (float)(bot_right[1] - bot_left[1]) /
(float)(bot_right[0] - bot_left[0]);
if (top_left[0] == bot_left[0])
slope_left = (float)(bot_left[1] - top_left[1]) /
(float)(bot_left[0] - top_left[0]);
if (top_right[0] == bot_right[0])
slope_right = (float)(bot_right[1] - top_right[1]) /
(float)(bot_right[0] - top_right[0]);
char pos = 0, neg = 0;
char x = curr[0];
char y = curr[1];;
int d;
int top = top_right[1] > top_left[1] ? top_right[1]: top_left[1];
int bot = bot_right[1] > bot_left[1] ? bot_right[1]: bot_left[1];
int left = top_left[0] > bot_left[0] ? top_left[0]: bot_left[0];
int right = top_right[0] > bot_right[0] ? top_right[0]: bot_right[0];
for (int y = top ; y <= bot ; ++y) {
for (int x = left ; x <= right ; ++x) {
if (slope_top * x + top <= y)
// TODO side check
output[COORD2INDEX(x, y)] = current_face;
for (char i = 0; i < 4; ++i) {
if (points[i][0] == curr[0] && points[i][1] == curr[1])
return 1;
//Form a segment between the i'th point
char x1 = points[i][0];
char y1 = points[i][1];
//And the i+1'th, or if i is the last, with the first point
char i2 = (i + 1) % 4;
char x2 = points[i2][0];
char y2 = points[i2][1];
//Compute the cross product
d = (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1);
if (d > 0) ++pos;
if (d < 0) ++neg;
//If the sign changes, then point is outside
if (pos > 0 && neg > 0)
return 0;
}
return 1;
}
void
fill_quads(char current_face, char top[2], char left[2],
char right[2], char bot[2])
{
if (current_face != 0) return;
output[COUPLE2INDEX(top)] = RGB15(0, 0, 15);
output[COUPLE2INDEX(left)] = RGB15(0, 0, 15);
output[COUPLE2INDEX(right)] = RGB15(0, 0, 15);
for (int y = top[1] ; y < bot[1] ; ++y) {
for (int x = left[0] ; x < right[0] ; ++x) {
char curr[2] = {x, y};
if (isInQuad(curr, top, left, right, bot))
//zbuffer issue
{}
//output[COORD2INDEX(x, y)] = current_face;
}
}
}
@ -275,33 +299,37 @@ void
detect_and_fill_quads()
{
for (int current_face = 0 ; current_face < NUM_FACES ; ++current_face) {
char top_left [2] = {VWIDTH, VHEIGHT};
char top_right[2] = {0, VHEIGHT};
char bot_left [2] = {VWIDTH, 0};
char bot_right[2] = {0, 0};
char last_top [2] = {VWIDTH, VHEIGHT};
char last_left[2] = {VWIDTH, 0};
char last_right [2] = {0, 0};
char last_bot[2] = {0, 0};
char top [2] = {VWIDTH, VHEIGHT};
char left[2] = {VWIDTH, 0};
char right [2] = {0, 0};
char bot[2] = {0, 0};
for (char y = 0; y < VHEIGHT; ++y) {
for (char x = 0; x < VWIDTH; ++x) {
if (output[COORD2INDEX(x, y)] != current_face)
continue;
if (x <= top_left[0] && y <= top_left[1]){
top_left[0] = x;
top_left[1] = y;
if (x <= left[0]) {
left[0] = x;
left[1] = y;
}
if (x >= top_right[0] && y <= top_right[1]){
top_right[0] = x;
top_right[1] = y;
if (y <= top[1]) {
top[0] = x;
top[1] = y;
}
if (x <= bot_left[0] && y >= bot_left[1]){
bot_left[0] = x;
bot_left[1] = y;
if (x >= right[0]) {
right[0] = x;
right[1] = y;
}
if (x >= bot_right[0] && y >= bot_right[1]){
bot_right[0] = x;
bot_right[1] = y;
if (y >= bot[1]) {
bot[0] = x;
bot[1] = y;
}
}
}
fill_quads(current_face, top_left, top_right, bot_left, bot_right);
fill_quads(current_face, top, left, right, bot);
}
}
@ -315,7 +343,7 @@ printAscii()
MEM_VRAM_MODE3_FB[120 + 96 * GBA_SCREEN_W] = RGB15(currentCountR, 31 - currentCountR, 0);
currentCountR = currentCountR == 31 ? 0 : 31;
//detect_and_fill_quads();
detect_and_fill_quads();
for (int i = 0; i < VHEIGHT; ++i) {
for (int j = 0; j < VWIDTH; ++j) {
@ -411,31 +439,30 @@ handleAngle(char input)
// TODO
if (currentlyMoving == 0) {
currentlyMoving = input;
Last = Current;
switch (input) {
case 'w':
case 'W':
Target = multQ(GET_ROTATE_X_Q(FLOAT2FIXED(M_PI_2)), Target);
Target = multQ(GET_ROTATE_X_Q(FLOAT2FIXED(M_PI_2)), Current);
break;
case 'a':
case 'A':
Target = multQ(GET_ROTATE_Y_Q(-FLOAT2FIXED(M_PI_2)), Target);
Target = multQ(GET_ROTATE_Y_Q(-FLOAT2FIXED(M_PI_2)), Current);
break;
case 's':
case 'S':
Target = multQ(GET_ROTATE_X_Q(-FLOAT2FIXED(M_PI_2)), Target);
Target = multQ(GET_ROTATE_X_Q(-FLOAT2FIXED(M_PI_2)), Current);
break;
case 'd':
case 'D':
Target = multQ(GET_ROTATE_Y_Q(FLOAT2FIXED(M_PI_2)), Target);
Target = multQ(GET_ROTATE_Y_Q(FLOAT2FIXED(M_PI_2)), Current);
break;
case 'q':
case 'Q':
Target = multQ(GET_ROTATE_Z_Q(-FLOAT2FIXED(M_PI_2)), Target);
Target = multQ(GET_ROTATE_Z_Q(-FLOAT2FIXED(M_PI_2)), Current);
break;
case 'e':
case 'E':
Target = multQ(GET_ROTATE_Z_Q(FLOAT2FIXED(M_PI_2)), Target);
Target = multQ(GET_ROTATE_Z_Q(FLOAT2FIXED(M_PI_2)), Current);
break;
default:
currentlyMoving = 0;
@ -480,7 +507,7 @@ main()
cubeX <= CUBE_WIDTH_FP - STEP_FP; cubeX += STEP_FP) {
for (FIXED_POINT cubeY = -CUBE_WIDTH_FP + STEP_FP;
cubeY <= CUBE_WIDTH_FP - STEP_FP; cubeY += STEP_FP) {
switch (frontFacingFace) {
switch (FACE_FRONT) {
case FACE_FRONT:
rotateCube(cubeX, cubeY, -CUBE_WIDTH_FP, FACE_FRONT);
break;

233
source/sys/gba_cart.ld Normal file
View file

@ -0,0 +1,233 @@
/*
* SPDX-License-Identifier: CC0-1.0
*
* SPDX-FileContributor: Antonio Niño Díaz, 2022
*/
/*
* Some links with information about linker scripts:
*
* - https://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/specialsections.html
*
* - https://blog.thea.codes/the-most-thoroughly-commented-linker-script/
*
* - http://beefchunk.com/documentation/sys-programming/binary_formats/elf/elf_from_the_programmers_perspective/node4.html
*/
OUTPUT_FORMAT("elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(entrypoint)
MEMORY {
ROM : ORIGIN = 0x08000000, LENGTH = 32M
IWRAM : ORIGIN = 0x03000000, LENGTH = 32K
EWRAM : ORIGIN = 0x02000000, LENGTH = 256K
}
/* Set stack pointers the same way as the BIOS */
__STACK_SVC_SIZE__ = 0x40;
__STACK_IRQ_SIZE__ = 0xA0;
__STACK_USR_SIZE_MIN__ = 0x200; /* Minimum size, the real size may be bigger */
__STACK_END__ = ORIGIN(IWRAM) + LENGTH(IWRAM) - 0x20; /* Used by BIOS */
__STACK_SVC_END__ = __STACK_END__;
__STACK_SVC_START__ = __STACK_SVC_END__ - __STACK_SVC_SIZE__;
__STACK_IRQ_END__ = __STACK_SVC_START__;
__STACK_IRQ_START__ = __STACK_IRQ_END__ - __STACK_IRQ_SIZE__;
__STACK_USR_END__ = __STACK_IRQ_START__;
__STACK_USR_START__ = __STACK_USR_END__ - __STACK_USR_SIZE_MIN__;
__STACK_START__ = __STACK_USR_START__;
SECTIONS
{
/*
* ROM sections
* ============
*/
/* Header and crt0 */
.gba_crt0 : ALIGN(4)
{
KEEP (*(.gba_crt0))
} > ROM
/* Code */
.text : ALIGN(4)
{
*(.text)
*(.text*)
*(.gnu.linkonce.t.*) /* Used for vague linking */
/* ARM/Thumb interworking code */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
/* Array of functions to be called at the start of the program */
KEEP(*(.init))
/* Array of functions to be called at the end of the program */
KEEP(*(.fini))
} > ROM
/* Read-only data */
.rodata : ALIGN(4)
{
*(.rodata)
*(.rodata*)
*(.gnu.linkonce.r.*) /* Used for vague linking */
} > ROM
/*
* Required for C++ and for C programs that try to examine backtraces. Each
* function that can throw exceptions has entries in exidx and extab.
*
* - exidx is used to contain index entries for stack unwinding.
* - extab names sections containing exception unwinding information.
*/
.ARM.extab : ALIGN(4)
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > ROM
.ARM.exidx : ALIGN(4)
{
PROVIDE_HIDDEN(__exidx_start = .);
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
PROVIDE_HIDDEN(__exidx_end = .);
} > ROM
/* Array of functions to be called during initialization of the program */
.preinit_array : ALIGN(4)
{
__preinit_array_start = .;
KEEP(*(.preinit_array*))
__preinit_array_end = .;
} > ROM
/*
* Array of functions to be called during initialization of the program.
* They are called after calling everything in .preinit_array
*/
.init_array : ALIGN(4)
{
__init_array_start = .;
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array*))
__init_array_end = .;
} > ROM
/* Array of functions called when the program ends. */
.fini_array : ALIGN(4)
{
__fini_array_start = .;
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array*))
__fini_array_end = .;
} > ROM
/*
* IWRAM sections
* ==============
*/
/* Uninitialized variables */
.bss (NOLOAD) : ALIGN(4)
{
*(.bss)
*(.bss*)
*(.gnu.linkonce.b.*) /* Used for vague linking */
*(COMMON) /* In case -fcommon is used in gcc (not used by default) */
. = ALIGN(4);
} > IWRAM
__IWRAM_BSS_START__ = ADDR(.bss);
__IWRAM_BSS_SIZE__ = SIZEOF(.bss);
__IWRAM_BSS_END__ = __IWRAM_BSS_START__ + __IWRAM_BSS_SIZE__;
/* Read-write data goes into IWRAM */
.data : ALIGN(4)
{
__DATA_START__ = .;
*(.data)
*(.data*)
*(.gnu.linkonce.d.*) /* Used for vague linking */
. = ALIGN(4);
__DATA_END__ = .;
} > IWRAM AT> ROM
__DATA_SIZE__ = __DATA_END__ - __DATA_START__;
__DATA_LMA__ = LOADADDR(.data);
/* Sections that the user requests to add to IWRAM */
.iwram : ALIGN(4)
{
__IWRAM_START__ = .;
*(.iwram)
*(.iwram*)
*iwram.*(.text*)
*iwram.*(.data*)
. = ALIGN(4);
__IWRAM_END__ = .;
} > IWRAM AT> ROM
__IWRAM_SIZE__ = __IWRAM_END__ - __IWRAM_START__;
__IWRAM_LMA__ = LOADADDR(.iwram);
/* The stack goes afterwards, check that there aren't overflows */
ASSERT(__IWRAM_END__ <= __STACK_START__,
"Not enough free IWRAM for stack")
/* Calculate real size of the user stack */
__STACK_USR_SIZE__ = __STACK_USR_END__ - __IWRAM_END__;
/*
* EWRAM sections
* ==============
*/
/*
* Uninitialized data explicitly placed in EWRAM. This section must be
* called ".sbss" for compatibility with devkitARM.
*/
.sbss (NOLOAD) : ALIGN(4)
{
*(.sbss)
*(.sbss*)
. = ALIGN(4);
} > EWRAM
/* Initialized data explicitly placed in EWRAM */
.ewram : ALIGN(4)
{
__EWRAM_START__ = .;
*(.ewram)
*(.ewram*)
*ewram.*(.text*)
*ewram.*(.data*)
. = ALIGN(4);
__EWRAM_END__ = .;
} > EWRAM AT > ROM
__EWRAM_SIZE__ = __EWRAM_END__ - __EWRAM_START__;
__EWRAM_LMA__ = LOADADDR(.ewram);
/* The heap information should be after the last EWRAM section */
__HEAP_START__ = __EWRAM_END__;
__HEAP_END__ = ORIGIN(EWRAM) + LENGTH(EWRAM);
__HEAP_SIZE__ = __HEAP_END__ - __HEAP_START__;
}

142
source/sys/gba_crt0.s Normal file
View file

@ -0,0 +1,142 @@
// SPDX-License-Identifier: CC0-1.0
//
// SPDX-FileContributor: Antonio Niño Díaz, 2022
.section .gba_crt0, "ax"
.global entrypoint
.cpu arm7tdmi
.arm
entrypoint:
b header_end
.fill 156, 1, 0 // Nintendo Logo
.fill 12, 1, 0 // Game Title
.fill 4, 1, 0 // Game Code
.byte 0x30, 0x30 // Maker Code ("00")
.byte 0x96 // Fixed Value (must be 0x96)
.byte 0x00 // Main unit code
.byte 0x00 // Device Type
.fill 7, 1, 0 // Reserved Area
.byte 0x00 // Software version
.byte 0x00 // Complement check (header checksum)
.byte 0x00, 0x00 // Reserved Area
header_end:
b start_vector
// Multiboot Header Entries
.byte 0 // Boot mode
.byte 0 // Slave ID Number
.fill 26, 1, 0 // Not used
.word 0 // JOYBUS entrypoint
.align
start_vector:
// Disable interrupts
mov r0, #0x4000000
mov r1, #0
str r1, [r0, #0x208] // IME
// Setup IRQ mode stack
mov r0, #0x12
msr cpsr, r0
ldr sp, =__STACK_IRQ_END__
// Setup system mode stack
mov r0, #0x1F
msr cpsr, r0
ldr sp, =__STACK_USR_END__
// Switch to Thumb mode
add r0, pc, #1
bx r0
.thumb
// Clear IWRAM
ldr r0, =#0x3000000
ldr r1, =#(32 * 1024)
bl mem_zero
// Copy data section from ROM to RAM
ldr r0, =__DATA_LMA__
ldr r1, =__DATA_START__
ldr r2, =__DATA_SIZE__
bl mem_copy
// Copy IWRAM data from ROM to RAM
ldr r0, =__IWRAM_LMA__
ldr r1, =__IWRAM_START__
ldr r2, =__IWRAM_SIZE__
bl mem_copy
// Clear EWRAM
ldr r0, =#0x2000000
ldr r1, =#(256 * 1024)
bl mem_zero
// Copy EWRAM data from ROM to RAM
ldr r0, =__EWRAM_LMA__
ldr r1, =__EWRAM_START__
ldr r2, =__EWRAM_SIZE__
bl mem_copy
// Global constructors
ldr r2, =__libc_init_array
bl blx_r2_trampoline
// Call main()
mov r0, #0 // int argc
mov r1, #0 // char *argv[]
ldr r2, =main
bl blx_r2_trampoline
// Global destructors
ldr r2, =__libc_fini_array
bl blx_r2_trampoline
// If main() returns, reboot the GBA using SoftReset
swi #0x00
// r0 = Base address
// r1 = Size
mem_zero:
and r1, r1
beq 2f // Return if size is 0
mov r2, #0
1:
stmia r0!, {r2}
sub r1, #4
bne 1b
2:
bx lr
// r0 = Source address
// r1 = Destination address
// r2 = Size
mem_copy:
and r2, r2
beq 2f // Return if size is 0
1:
ldmia r0!, {r3}
stmia r1!, {r3}
sub r2, #4
bne 1b
2:
bx lr
// r2 = Address to jump to
blx_r2_trampoline:
bx r2
.align
.pool
.end

177
source/sys/syscalls.c Normal file
View file

@ -0,0 +1,177 @@
// SPDX-License-Identifier: CC0-1.0
//
// SPDX-FileContributor: Antonio Niño Díaz, 2022
#include <errno.h>
#include <sys/stat.h>
#include <sys/times.h>
#include <time.h>
// This file implements stubs for system calls. For more information about it,
// check the documentation of newlib:
//
// https://sourceware.org/newlib/libc.html#Syscalls
#undef errno
extern int errno;
char *__env[1] = { 0 };
char **environ = __env;
int _getpid(void)
{
return 1;
}
int _kill(int pid, int sig)
{
(void)pid;
(void)sig;
errno = EINVAL;
return -1;
}
void _exit(int status)
{
_kill(status, -1);
// Hang, there is nowhere to go
while (1);
}
__attribute__((weak)) int _read(int file, char *ptr, int len)
{
(void)file;
(void)ptr;
return len;
}
__attribute__((weak)) int _write(int file, char *ptr, int len)
{
(void)file;
(void)ptr;
return len;
}
int _close(int file)
{
(void)file;
return -1;
}
int _fstat(int file, struct stat *st)
{
(void)file;
st->st_mode = S_IFCHR;
return 0;
}
int _isatty(int file)
{
(void)file;
return 1;
}
int _lseek(int file, int ptr, int dir)
{
(void)file;
(void)ptr;
(void)dir;
return 0;
}
int _open(char *path, int flags, ...)
{
(void)path;
(void)flags;
return -1;
}
int _wait(int *status)
{
(void)status;
errno = ECHILD;
return -1;
}
int _unlink(char *name)
{
(void)name;
errno = ENOENT;
return -1;
}
int _times(struct tms *buf)
{
(void)buf;
return -1;
}
int _stat(char *file, struct stat *st)
{
(void)file;
st->st_mode = S_IFCHR;
return 0;
}
int _link(char *old, char *new)
{
(void)old;
(void)new;
errno = EMLINK;
return -1;
}
int _fork(void)
{
errno = EAGAIN;
return -1;
}
int _execve(char *name, char **argv, char **env)
{
(void)name;
(void)argv;
(void)env;
errno = ENOMEM;
return -1;
}
void *_sbrk(int incr)
{
// Symbols defined by the linker
extern char __HEAP_START__[];
extern char __HEAP_END__[];
const uintptr_t HEAP_START = (uintptr_t) __HEAP_START__;
const uintptr_t HEAP_END = (uintptr_t) __HEAP_END__;
// Pointer to the current end of the heap
static uintptr_t heap_end = HEAP_START;
if (heap_end + incr > HEAP_END)
{
errno = ENOMEM;
return (void *)-1;
}
uintptr_t prev_heap_end = heap_end;
heap_end += incr;
return (void *)prev_heap_end;
}