Compare commits
2 commits
Author | SHA1 | Date | |
---|---|---|---|
044d31fa47 | |||
d8f573c678 |
8 changed files with 1570 additions and 5 deletions
169
Makefile
169
Makefile
|
@ -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
1
gbafix/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
gbafix
|
25
gbafix/Makefile
Normal file
25
gbafix/Makefile
Normal 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
285
gbafix/gbafix.c
Normal 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;
|
||||
}
|
543
source/main.c
Normal file
543
source/main.c
Normal file
|
@ -0,0 +1,543 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
//
|
||||
// SPDX-FileContributor: Antonio Niño Díaz, 2022
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define GBA_SCREEN_W 240
|
||||
#define GBA_SCREEN_H 160
|
||||
|
||||
#define REG_DISPCNT *((volatile uint16_t *)0x04000000)
|
||||
|
||||
#define REG_KEYINPUT *((volatile uint16_t *)0x04000130)
|
||||
#define DEFAULT_REG_KEYINPUT *((volatile uint16_t *)0x04000130)
|
||||
#define KEY_DOWN_NOW(key) (~(REG_KEYINPUT) & key)
|
||||
|
||||
#define KEY_A 1
|
||||
#define KEY_B 2
|
||||
#define KEY_SELECT 3
|
||||
#define KEY_START 4
|
||||
#define KEY_DPAD_RIGHT 5
|
||||
#define KEY_DPAD_LEFT 6
|
||||
#define KEY_DPAD_UP 7
|
||||
#define KEY_DPAD_DOWN 8
|
||||
#define KEY_TRIGGER_LEFT 9
|
||||
#define KEY_TRIGGER_RIGHT 10
|
||||
|
||||
#define DISPCNT_BG_MODE_MASK (0x7)
|
||||
#define DISPCNT_BG_MODE(n) ((n) & DISPCNT_BG_MODE_MASK) // 0 to 5
|
||||
|
||||
#define DISPCNT_BG2_ENABLE (1 << 10)
|
||||
|
||||
#define MEM_VRAM_MODE3_FB ((uint16_t *)0x06000000)
|
||||
|
||||
#define FIXED_POINT int32_t
|
||||
#define fp 12
|
||||
#define SHIFT_THRESHOLD 0.05
|
||||
#define SHIFT_THRESHOLD_FP ((1 << fp) * SHIFT_THRESHOLD)
|
||||
|
||||
#define FLOAT2FIXED(value) (int)((value) * (1 << fp))
|
||||
#define FIXED2FLOAT(value) ((value) / (float)(1 << fp))
|
||||
|
||||
static inline uint16_t
|
||||
RGB15(uint16_t r, uint16_t g, uint16_t b)
|
||||
{
|
||||
return (r & 0x1F) | ((g & 0x1F) << 5) | ((b & 0x1F) << 10);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <float.h>
|
||||
|
||||
#define VWIDTH 50
|
||||
#define VHEIGHT 50
|
||||
#define CUBE_WIDTH 10
|
||||
#define CUBE_WIDTH_FP ((1 << fp) * CUBE_WIDTH)
|
||||
|
||||
enum faces {
|
||||
FACE_FRONT = 0,
|
||||
FACE_LEFT,
|
||||
FACE_RIGHT,
|
||||
FACE_BOTTOM,
|
||||
FACE_TOP,
|
||||
FACE_BACK,
|
||||
NUM_FACES,
|
||||
};
|
||||
|
||||
#define STEP 5
|
||||
#define STEP_FP ((1 << fp) * STEP)
|
||||
|
||||
#define ACTION_STEP 0.1
|
||||
#define ACTION_STEP_FP ((1 << fp) * ACTION_STEP)
|
||||
|
||||
#define PITCH_STEP 0.05
|
||||
#define ROLL_STEP 0.05
|
||||
#define YAW_STEP 0.05
|
||||
|
||||
volatile FIXED_POINT K1 = 60;
|
||||
volatile FIXED_POINT K2 = (2 * CUBE_WIDTH) + 20;
|
||||
|
||||
|
||||
#define MULT_FP(a,b) ((a * b) >> fp)
|
||||
|
||||
#define SQ(n) (n * n)
|
||||
#define SQ_FP(n) (MULT_FP(n, n))
|
||||
|
||||
#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)) ; \
|
||||
struct Quaternions q = {}; q.w = FLOAT2FIXED(cos(_a * .5)); \
|
||||
q.x = FLOAT2FIXED(sin(_a * .5)); q; })
|
||||
#define GET_ROTATE_Y_Q(a) ({ float _a = (FIXED2FLOAT(a)) ; \
|
||||
struct Quaternions q = {}; q.w = FLOAT2FIXED(cos(_a * .5)); \
|
||||
q.y = FLOAT2FIXED(sin(_a * .5)); q; })
|
||||
#define GET_ROTATE_Z_Q(a) ({ float _a = (FIXED2FLOAT(a)) ; \
|
||||
struct Quaternions q = {}; q.w = FLOAT2FIXED(cos(_a * .5)); \
|
||||
q.z = FLOAT2FIXED(sin(_a * .5)); q; })
|
||||
|
||||
//TODO Idle animations
|
||||
#define IS_IDLE (Idle.x || Idle.y || Idle.z)
|
||||
#define RESET_IDLE {Idle.x = 0; Idle.y = 0; Idle.z = 0;}
|
||||
|
||||
struct {
|
||||
char x;
|
||||
char y;
|
||||
char z;
|
||||
} Idle;
|
||||
|
||||
|
||||
struct Quaternions {
|
||||
FIXED_POINT w;
|
||||
FIXED_POINT x;
|
||||
FIXED_POINT y;
|
||||
FIXED_POINT z;
|
||||
} Target, Current;
|
||||
|
||||
FIXED_POINT interpolationStep = 0;
|
||||
FIXED_POINT zBuffer[VHEIGHT * VWIDTH];
|
||||
char output[VHEIGHT * VWIDTH];
|
||||
|
||||
static volatile char shouldBreak = 1;
|
||||
static volatile char currentlyMoving = 0;
|
||||
static volatile char currentCountR = 0;
|
||||
static volatile char frontFacingFace = FACE_FRONT;
|
||||
|
||||
void
|
||||
normalize(struct Quaternions *q)
|
||||
{
|
||||
float n = sqrt(FIXED2FLOAT(SQ_FP(q->w) + SQ_FP(q->x) +
|
||||
SQ_FP(q->y) + SQ_FP(q->z)));
|
||||
if (n == 0)
|
||||
return;
|
||||
q->w = FLOAT2FIXED(FIXED2FLOAT(q->w) / n);
|
||||
q->x = FLOAT2FIXED(FIXED2FLOAT(q->x) / n);
|
||||
q->y = FLOAT2FIXED(FIXED2FLOAT(q->y) / n);
|
||||
q->z = FLOAT2FIXED(FIXED2FLOAT(q->z) / n);
|
||||
}
|
||||
|
||||
struct Quaternions
|
||||
mult(struct Quaternions q, FIXED_POINT x, FIXED_POINT y, FIXED_POINT z)
|
||||
{
|
||||
//p = q * p * qbar
|
||||
struct Quaternions res;
|
||||
|
||||
res.w = 0;
|
||||
res.x = MULT_FP(x, (SQ_FP(q.w) + SQ_FP(q.x) - SQ_FP(q.y) - SQ_FP(q.z)))
|
||||
+ (MULT_FP(y, (MULT_FP(q.x, q.y) - MULT_FP(q.w, q.z))) * 2)
|
||||
+ (MULT_FP(z, (MULT_FP(q.x, q.z) + MULT_FP(q.w, q.y))) * 2);
|
||||
|
||||
res.y = (MULT_FP(x, (MULT_FP(q.x, q.y) + MULT_FP(q.w,q.z))) * 2)
|
||||
+ (MULT_FP(y, (SQ_FP(q.w) - SQ_FP(q.x) + SQ_FP(q.y) - SQ_FP(q.z))))
|
||||
+ (MULT_FP(z, (MULT_FP(q.y, q.z) - MULT_FP(q.w, q.x))) << 2);
|
||||
|
||||
res.z = (MULT_FP(x, (MULT_FP(q.x, q.z) - MULT_FP(q.w, q.y)))* 2)
|
||||
+ (MULT_FP(y, (MULT_FP(q.y, q.z) + MULT_FP(q.w, q.x))) * 2)
|
||||
+ MULT_FP(z, (SQ_FP(q.w) - SQ_FP(q.x) - SQ_FP(q.y) + SQ_FP(q.z)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
struct Quaternions
|
||||
multQ(struct Quaternions p, struct Quaternions q)
|
||||
{
|
||||
if (p.x <= SHIFT_THRESHOLD_FP && p.x >= -SHIFT_THRESHOLD_FP
|
||||
&& p.y <= SHIFT_THRESHOLD_FP && p.y >= -SHIFT_THRESHOLD_FP
|
||||
&& p.z <= SHIFT_THRESHOLD_FP && p.z >= -SHIFT_THRESHOLD_FP)
|
||||
return q;
|
||||
|
||||
if (q.x <= SHIFT_THRESHOLD_FP && q.x >= -SHIFT_THRESHOLD_FP
|
||||
&& q.y <= SHIFT_THRESHOLD_FP && q.y >= -SHIFT_THRESHOLD_FP
|
||||
&& q.z <= SHIFT_THRESHOLD_FP && q.z >= -SHIFT_THRESHOLD_FP)
|
||||
return p;
|
||||
|
||||
struct Quaternions res = {
|
||||
.w = MULT_FP(p.w, q.w) - MULT_FP(p.x, q.x) -
|
||||
MULT_FP(p.y, q.y) - MULT_FP(p.z, q.z),
|
||||
.x = MULT_FP(p.w, q.x) + MULT_FP(p.x, q.w) +
|
||||
MULT_FP(p.y, q.z) - MULT_FP(p.z, q.y),
|
||||
.y = MULT_FP(p.w, q.y) - MULT_FP(p.x, q.z) +
|
||||
MULT_FP(p.y, q.w) + MULT_FP(p.z, q.x),
|
||||
.z = MULT_FP(p.w, q.z) + MULT_FP(p.x, q.y) -
|
||||
MULT_FP(p.y, q.x) + MULT_FP(p.z, q.w),
|
||||
};
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
uint16_t
|
||||
chooseColor(char c)
|
||||
{
|
||||
switch (c) {
|
||||
case FACE_FRONT:
|
||||
return RGB15(31, 0, 0);
|
||||
case FACE_BACK:
|
||||
return RGB15(31, 15, 31);
|
||||
case FACE_BOTTOM:
|
||||
return RGB15(31, 0, 31);
|
||||
case FACE_LEFT:
|
||||
return RGB15(0, 0, 31);
|
||||
case FACE_RIGHT:
|
||||
return RGB15(0, 31, 31);
|
||||
case FACE_TOP:
|
||||
return RGB15(0, 31, 0);
|
||||
default:
|
||||
// BG
|
||||
return RGB15(31, 31, 31);
|
||||
}
|
||||
}
|
||||
|
||||
char
|
||||
chooseMainFace()
|
||||
{
|
||||
int total = 0;
|
||||
int faces[NUM_FACES] = {0};
|
||||
|
||||
for (int k = 0; k < VWIDTH * VHEIGHT; ++k)
|
||||
if (output[k] >= 0 && output[k] < NUM_FACES) {
|
||||
faces[output[k]]++;
|
||||
++total;
|
||||
}
|
||||
|
||||
int max = 0, idx = 0;
|
||||
for (int k = 0; k < NUM_FACES; ++k)
|
||||
if (faces[k] > max) {
|
||||
max = faces[k];
|
||||
idx = k;
|
||||
}
|
||||
|
||||
frontFacingFace = max > total * 0.9 ? idx : -1;
|
||||
return frontFacingFace;
|
||||
}
|
||||
|
||||
char
|
||||
isInQuad(char curr[2], char top[2], char left[2],
|
||||
char right[2], char bot[2])
|
||||
{
|
||||
char *points[4] = {top, left, bot, right};
|
||||
|
||||
char pos = 0, neg = 0;
|
||||
char x = curr[0];
|
||||
char y = curr[1];;
|
||||
int d;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
detect_and_fill_quads()
|
||||
{
|
||||
for (int current_face = 0 ; current_face < NUM_FACES ; ++current_face) {
|
||||
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 <= left[0]) {
|
||||
left[0] = x;
|
||||
left[1] = y;
|
||||
}
|
||||
if (y <= top[1]) {
|
||||
top[0] = x;
|
||||
top[1] = y;
|
||||
}
|
||||
if (x >= right[0]) {
|
||||
right[0] = x;
|
||||
right[1] = y;
|
||||
}
|
||||
if (y >= bot[1]) {
|
||||
bot[0] = x;
|
||||
bot[1] = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
fill_quads(current_face, top, left, right, bot);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
printAscii()
|
||||
{
|
||||
// TODO scale up
|
||||
MEM_VRAM_MODE3_FB[120 + 80 * GBA_SCREEN_W] = RGB15(currentCountR, 31 - currentCountR, 0);
|
||||
MEM_VRAM_MODE3_FB[136 + 80 * GBA_SCREEN_W] = RGB15(currentCountR, 31 - currentCountR, 0);
|
||||
MEM_VRAM_MODE3_FB[120 + 96 * GBA_SCREEN_W] = RGB15(currentCountR, 31 - currentCountR, 0);
|
||||
currentCountR = currentCountR == 31 ? 0 : 31;
|
||||
|
||||
detect_and_fill_quads();
|
||||
|
||||
for (int i = 0; i < VHEIGHT; ++i) {
|
||||
for (int j = 0; j < VWIDTH; ++j) {
|
||||
char prevc = 0;
|
||||
char *c = output + (i * VWIDTH + j);
|
||||
MEM_VRAM_MODE3_FB[(i + 50) * GBA_SCREEN_W + j + 50] = chooseColor(*c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rotateCube(FIXED_POINT cubeX, FIXED_POINT cubeY, FIXED_POINT cubeZ, char ch)
|
||||
{
|
||||
struct Quaternions q = mult(Current, cubeX, cubeY, cubeZ);
|
||||
|
||||
int x = q.x >> fp;
|
||||
int y = q.y >> fp;
|
||||
|
||||
// not fixed point yet!!
|
||||
float invZ = (1 << fp) / (float)(q.z + K2 * (1 << fp));
|
||||
|
||||
int screenX = (int)(VWIDTH * 0.5) + (int)((x) * K1) * invZ;
|
||||
int screenY = (int)(VHEIGHT * 0.5) + (int)((y) * K1) * invZ;
|
||||
//TODO luminescence
|
||||
|
||||
if (screenX > VWIDTH || screenX < 0) return;
|
||||
|
||||
int idx = screenY * VWIDTH + screenX;
|
||||
if (idx >= 0 && idx < VWIDTH * VHEIGHT) {
|
||||
invZ = FLOAT2FIXED(invZ);
|
||||
if (zBuffer[idx] < invZ) {
|
||||
zBuffer[idx] = invZ;
|
||||
output[idx] = ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Quaternions
|
||||
interpolate(struct Quaternions qa, struct Quaternions qb)
|
||||
{
|
||||
frontFacingFace = -1;
|
||||
struct Quaternions res;
|
||||
float cosHalfTheta =
|
||||
FIXED2FLOAT(MULT_FP(qa.w, qb.w) +
|
||||
MULT_FP(qa.x, qb.x) +
|
||||
MULT_FP(qa.y, qb.y) +
|
||||
MULT_FP(qa.z, qb.z));
|
||||
//if qa = qb or qa = -qb then theta = 0 and we can return qa
|
||||
if (cosHalfTheta >= 1.0 || cosHalfTheta <= -1.0) {
|
||||
res.w = qa.w;
|
||||
res.x = qa.x;
|
||||
res.y = qa.y;
|
||||
res.z = qa.z;
|
||||
goto exit;
|
||||
}
|
||||
if (cosHalfTheta < 0) {
|
||||
qb.w = -qb.w;
|
||||
qb.x = -qb.x;
|
||||
qb.y = -qb.y;
|
||||
qb.z = qb.z;
|
||||
cosHalfTheta = -cosHalfTheta;
|
||||
}
|
||||
|
||||
float halfTheta = acos(cosHalfTheta);
|
||||
float sinHalfTheta = sqrt(1.0 - cosHalfTheta * cosHalfTheta);
|
||||
//if theta = 180 degrees then result is not fully defined
|
||||
// we could rotate around any axis normal to qa or qb
|
||||
if (sinHalfTheta < 0.001 && sinHalfTheta > -0.001) {
|
||||
res.w = ((qa.w >> 1) + (qb.w >> 1));
|
||||
res.x = ((qa.x >> 1) + (qb.x >> 1));
|
||||
res.y = ((qa.y >> 1) + (qb.y >> 1));
|
||||
res.z = ((qa.z >> 1) + (qb.z >> 1));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
|
||||
FIXED_POINT ratioA = FLOAT2FIXED(sin((1 - FIXED2FLOAT(interpolationStep)) * halfTheta) / sinHalfTheta);
|
||||
FIXED_POINT ratioB = FLOAT2FIXED(sin(FIXED2FLOAT(interpolationStep) * halfTheta) / sinHalfTheta);
|
||||
|
||||
res.w = (MULT_FP(qa.w, ratioA) + MULT_FP(qb.w, ratioB));
|
||||
res.x = (MULT_FP(qa.x, ratioA) + MULT_FP(qb.x, ratioB));
|
||||
res.y = (MULT_FP(qa.y, ratioA) + MULT_FP(qb.y, ratioB));
|
||||
res.z = (MULT_FP(qa.z, ratioA) + MULT_FP(qb.z, ratioB));
|
||||
|
||||
exit:
|
||||
interpolationStep += ACTION_STEP_FP;
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
handleAngle(char input)
|
||||
{
|
||||
// TODO
|
||||
if (currentlyMoving == 0) {
|
||||
currentlyMoving = input;
|
||||
switch (input) {
|
||||
case 'w':
|
||||
case 'W':
|
||||
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)), Current);
|
||||
break;
|
||||
case 's':
|
||||
case 'S':
|
||||
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)), Current);
|
||||
break;
|
||||
case 'q':
|
||||
case 'Q':
|
||||
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)), Current);
|
||||
break;
|
||||
default:
|
||||
currentlyMoving = 0;
|
||||
//TODO idle movement
|
||||
}
|
||||
normalize(&Target);
|
||||
} else {
|
||||
if (interpolationStep < (1 << fp) - ACTION_STEP_FP * 2) {
|
||||
Current = interpolate(Current, Target);
|
||||
normalize(&Current);
|
||||
}
|
||||
else {
|
||||
Current = Target;
|
||||
interpolationStep = 0;
|
||||
currentlyMoving = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char
|
||||
getInput()
|
||||
{
|
||||
// TODO
|
||||
char c = 'd';
|
||||
handleAngle(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
REG_DISPCNT = DISPCNT_BG_MODE(3) | DISPCNT_BG2_ENABLE;
|
||||
|
||||
Current = GET_ROTATE_Z_Q(0);
|
||||
|
||||
while (1) {
|
||||
memset(output, NUM_FACES, VWIDTH * VHEIGHT);
|
||||
memset(zBuffer, 0xffffffff, VWIDTH * VHEIGHT * sizeof(FIXED_POINT));
|
||||
|
||||
for (FIXED_POINT cubeX = -CUBE_WIDTH_FP + STEP_FP ;
|
||||
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 (FACE_FRONT) {
|
||||
case FACE_FRONT:
|
||||
rotateCube(cubeX, cubeY, -CUBE_WIDTH_FP, FACE_FRONT);
|
||||
break;
|
||||
case FACE_LEFT:
|
||||
rotateCube(-CUBE_WIDTH_FP, cubeX, cubeY, FACE_LEFT);
|
||||
break;
|
||||
case FACE_RIGHT:
|
||||
rotateCube(CUBE_WIDTH_FP, cubeX, cubeY, FACE_RIGHT);
|
||||
break;
|
||||
case FACE_BOTTOM:
|
||||
rotateCube(cubeX, -CUBE_WIDTH_FP, cubeY, FACE_BOTTOM);
|
||||
break;
|
||||
case FACE_TOP:
|
||||
rotateCube(cubeX, CUBE_WIDTH_FP, cubeY, FACE_TOP);
|
||||
break;
|
||||
case FACE_BACK:
|
||||
rotateCube(cubeX, cubeY, CUBE_WIDTH_FP, FACE_BACK);
|
||||
break;
|
||||
default: // idk render all
|
||||
rotateCube(cubeX, cubeY, -CUBE_WIDTH_FP, FACE_FRONT);
|
||||
rotateCube(-CUBE_WIDTH_FP, cubeX, cubeY, FACE_LEFT);
|
||||
rotateCube(CUBE_WIDTH_FP, cubeX, cubeY, FACE_RIGHT);
|
||||
rotateCube(cubeX, -CUBE_WIDTH_FP, cubeY, FACE_TOP);
|
||||
rotateCube(cubeX, CUBE_WIDTH_FP, cubeY, FACE_BOTTOM);
|
||||
rotateCube(cubeX, cubeY, CUBE_WIDTH_FP, FACE_BACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printAscii();
|
||||
getInput();
|
||||
}
|
||||
}
|
233
source/sys/gba_cart.ld
Normal file
233
source/sys/gba_cart.ld
Normal 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
142
source/sys/gba_crt0.s
Normal 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
177
source/sys/syscalls.c
Normal 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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue