- 14
- Posts
- 12
- Years
- Seen Oct 4, 2020
Hello. I'm working on a ROM hack of Pokemon Emerald and I'm trying to add a few custom battle types. I'm actually a lot more comfortable writing code in C than ASM, so that's what I've been doing. I'm using this GitHub repository as a template (can't post links yet, sorry: https://github.com/EternalCode/Empty-Template), obviously replacing BPRE.ld with a BPEE.ld composed of various references I've been able to find and dig up around the Internet.
The problem is as described in the title. The application here is that I have an array of structs stored in a fixed part of the ROM. I want to be able to pass an index to a C/ASM function so that the game knows which element in the array to load data from. It seems to work fine when I use a literal (array[0];) or a const variable (const uint8_t index = 0; array[index];). But non-const variables access the wrong part of memory.
Consider the following self-contained example:
example.h
example.c
example_script.rbc
Now here's what the script looks like in-game. Using literals, function1 performs as expected. But function2 returns garbage values, even though we've confirmed that 0x8004 is correctly being set to 0. The visual proof is shown in the attachments. It does the same if manual pointer arithmetic as well (ie &numbers + var_8004 is just as wrong).
This is obviously really problematic. As the GBA has really piss-poor memory management, it's really easy to read into memory you're not supposed to. In more complicated examples, simply accessing an array as lead to things like the game force-resetting to the emulator crashing, unable to recognize which command to execute next.
Never in my years of programming C have I ever encountered a system where pointer arithmetic was only valid with constants and literals. This is one of the most basic and fundamental aspects of programming in general so I doubt this is just how the system is "supposed" to behave. There has to be something stupid I'm not seeing.
For your convenience, I'm also attaching the ASM gcc generates for function1 and function2. They seem fine and valid to me, but my C is way better than my ASM so maybe there's a problem you can spot that I haven't.
Thanks in advance!
The problem is as described in the title. The application here is that I have an array of structs stored in a fixed part of the ROM. I want to be able to pass an index to a C/ASM function so that the game knows which element in the array to load data from. It seems to work fine when I use a literal (array[0];) or a const variable (const uint8_t index = 0; array[index];). But non-const variables access the wrong part of memory.
Consider the following self-contained example:
example.h
Spoiler:
Code:
#pragma once
#include <stdint.h>
// my variables
const extern uint16_t numbers[0xFF];
// game vars
extern uint16_t var_8000;
extern uint16_t var_8001;
extern uint16_t var_8002;
extern uint16_t var_8003;
extern uint16_t var_8004;
extern uint16_t var_8005;
extern uint16_t var_8006;
extern uint16_t var_8007;
extern uint16_t var_800D_lastresult;
example.c
Spoiler:
Code:
#include "example.h"
// the other elements should be 0
const uint16_t numbers[0xFF] = { 100, 200 };
// expected values: 100, 200
void function1() {
var_8000 = numbers[0];
var_8001 = numbers[1];
}
// expected values if 0x8004 is 0: 100, 200
void function2() {
var_8000 = var_8004;
var_8001 = numbers[var_8004];
var_8002 = numbers[var_8004 + 1];
}
example_script.rbc
Spoiler:
Code:
#dynamic 0xE3CF64
#freespace 0xFF
#org @start
lock
faceplayer
callasm 0x8E61E91 'function1
buffernumber 0x0 0x8000
buffernumber 0x1 0x8001
msgbox @txt1 0x6
waitkeypress
setvar 0x8004 0x0
callasm 0x8E61EA9 'function2
buffernumber 0x0 0x8000
buffernumber 0x1 0x8001
buffernumber 0x2 0x8002
msgbox @txt2 0x6
release
end
#org @txt1
= [buffer1] and [buffer2]!
#org @txt2
= [buffer1], [buffer2], and [buffer3]!
Now here's what the script looks like in-game. Using literals, function1 performs as expected. But function2 returns garbage values, even though we've confirmed that 0x8004 is correctly being set to 0. The visual proof is shown in the attachments. It does the same if manual pointer arithmetic as well (ie &numbers + var_8004 is just as wrong).
This is obviously really problematic. As the GBA has really piss-poor memory management, it's really easy to read into memory you're not supposed to. In more complicated examples, simply accessing an array as lead to things like the game force-resetting to the emulator crashing, unable to recognize which command to execute next.
Never in my years of programming C have I ever encountered a system where pointer arithmetic was only valid with constants and literals. This is one of the most basic and fundamental aspects of programming in general so I doubt this is just how the system is "supposed" to behave. There has to be something stupid I'm not seeing.
For your convenience, I'm also attaching the ASM gcc generates for function1 and function2. They seem fine and valid to me, but my C is way better than my ASM so maybe there's a problem you can spot that I haven't.
Spoiler:
Code:
numbers:
.short 100
.short 200
.space 506
.text
.align 2
.global function1
.arch armv4t
.syntax unified
.arm
.fpu softvfp
.type function1, %function
function1:
@ Function supports interworking.
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 1, uses_anonymous_args = 0
@ link register save eliminated.
str fp, [sp, #-4]!
add fp, sp, #0
mov r2, #100
ldr r3, .L2
strh r2, [r3] @ movhi
mov r2, #200
ldr r3, .L2+4
strh r2, [r3] @ movhi
nop
add sp, fp, #0
@ sp needed
ldr fp, [sp], #4
bx lr
.L3:
.align 2
.L2:
.word var_8000
.word var_8001
.size function1, .-function1
.align 2
.global function2
.syntax unified
.arm
.fpu softvfp
.type function2, %function
function2:
@ Function supports interworking.
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 1, uses_anonymous_args = 0
@ link register save eliminated.
str fp, [sp, #-4]!
add fp, sp, #0
ldr r3, .L5
ldrh r3, [r3]
ldr r2, .L5+4
strh r3, [r2] @ movhi
ldr r3, .L5
ldrh r3, [r3]
ldr r2, .L5+8
lsl r3, r3, #1
add r3, r2, r3
ldrh r3, [r3]
ldr r2, .L5+12
strh r3, [r2] @ movhi
ldr r3, .L5
ldrh r3, [r3]
add r3, r3, #1
ldr r2, .L5+8
lsl r3, r3, #1
add r3, r2, r3
ldrh r3, [r3]
ldr r2, .L5+16
strh r3, [r2] @ movhi
nop
add sp, fp, #0
@ sp needed
ldr fp, [sp], #4
bx lr
.L6:
.align 2
.L5:
.word var_8004
.word var_8000
.word numbers
.word var_8001
.word var_8002
.size function2, .-function2
.ident "GCC: (devkitARM release 54) 10.1.0"
Thanks in advance!