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

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