optimize register usage for write, add patch system

This commit is contained in:
Matthew Laux
2026-02-03 18:29:48 -06:00
parent d43868a279
commit 1eb1209097
8 changed files with 127 additions and 26 deletions
+17 -23
View File
@@ -29,26 +29,26 @@ void compile_slow_dmg_write(struct code_block *block, uint8_t val_reg)
static void compile_inline_dmg_write(struct code_block *block, uint8_t val_reg)
{
// Fast path: check write page table
// move.w d1, d0 ; 2 bytes [0-1]
emit_move_w_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_SCRATCH_0);
// lsr.w #8, d0 ; 2 bytes [2-3]
emit_lsr_w_imm_dn(block, 8, REG_68K_D_SCRATCH_0);
// lsl.w #2, d0 ; 2 bytes [4-5]
emit_lsl_w_imm_dn(block, 2, REG_68K_D_SCRATCH_0);
// movea.l (a6,d0.w), a0 ; 4 bytes [6-9]
emit_movea_l_idx_an_an(block, 0, REG_68K_A_WRITE_PAGE, REG_68K_D_SCRATCH_0, REG_68K_A_SCRATCH_1);
// move.w d1, d3 ; 2 bytes [0-1]
emit_move_w_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_NEXT_PC);
// lsr.w #8, d3 ; 2 bytes [2-3]
emit_lsr_w_imm_dn(block, 8, REG_68K_D_NEXT_PC);
// lsl.w #2, d3 ; 2 bytes [4-5]
emit_lsl_w_imm_dn(block, 2, REG_68K_D_NEXT_PC);
// movea.l (a6,d3.w), a0 ; 4 bytes [6-9]
emit_movea_l_idx_an_an(block, 0, REG_68K_A_WRITE_PAGE, REG_68K_D_NEXT_PC, REG_68K_A_SCRATCH_1);
// cmpa.w #0, a0 ; 4 bytes [10-13]
emit_cmpa_w_imm_an(block, 0, REG_68K_A_SCRATCH_1);
// beq.s slow_path (+12) ; 2 bytes [14-15] -> offset 28
emit_beq_b(block, 12);
// Page hit (offset 16):
// move.w d1, d0 ; 2 bytes [16-17]
emit_move_w_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_SCRATCH_0);
// andi.w #$ff, d0 ; 4 bytes [18-21]
emit_andi_w_dn(block, REG_68K_D_SCRATCH_0, 0x00ff);
// move.b val_reg, (a0,d0.w) ; 4 bytes [22-25]
emit_move_b_dn_idx_an(block, val_reg, REG_68K_A_SCRATCH_1, REG_68K_D_SCRATCH_0);
// move.w d1, d3 ; 2 bytes [16-17]
emit_move_w_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_NEXT_PC);
// andi.w #$ff, d3 ; 4 bytes [18-21]
emit_andi_w_dn(block, REG_68K_D_NEXT_PC, 0x00ff);
// move.b val_reg, (a0,d3.w) ; 4 bytes [22-25]
emit_move_b_dn_idx_an(block, val_reg, REG_68K_A_SCRATCH_1, REG_68K_D_NEXT_PC);
// bra.s done (+24) ; 2 bytes [26-27] -> offset 52
emit_bra_b(block, 24);
@@ -67,20 +67,14 @@ void compile_call_dmg_write_a(struct code_block *block)
// Call dmg_write(dmg, addr, val) - addr in D1, val is immediate
void compile_call_dmg_write_imm(struct code_block *block, uint8_t val)
{
emit_move_b_dn(block, 3, val);
compile_inline_dmg_write(block, 3);
// emit_move_b_dn(block, 0, val);
// compile_slow_dmg_write(block, 0);
emit_move_b_dn(block, 0, val);
compile_inline_dmg_write(block, 0);
}
// Call dmg_write(dmg, addr, val) - addr in D1, val in D0
void compile_call_dmg_write_d0(struct code_block *block)
{
// uses d0 as scratch so need to move to d3, but it's so long that
// what's one more instruction...
emit_move_b_dn_dn(block, 0, 3);
compile_inline_dmg_write(block, 3);
// compile_slow_dmg_write(block, 0);
compile_inline_dmg_write(block, 0);
}
// Emit slow path call to dmg_read - expects address in D1, returns in D0
+11 -3
View File
@@ -58,6 +58,7 @@ void dmg_init_pages(struct dmg *dmg)
for (k = 0x40; k <= 0x7f; k++) {
dmg->read_page[k] = &dmg->rom->data[k << 8];
}
dmg->current_rom_bank = 1;
// video RAM: 0x8000-0x9fff (pages 0x80-0x9f)
// Uses bank 0 initially, cgb_update_vram_bank() can switch for CGB
@@ -106,7 +107,14 @@ void dmg_init_pages(struct dmg *dmg)
void dmg_update_rom_bank(struct dmg *dmg, int bank)
{
int k;
u8 *bank_base = &dmg->rom->data[bank * 0x4000];
u8 *bank_base;
if (dmg->current_rom_bank == bank) {
return;
}
dmg->current_rom_bank = bank;
bank_base = &dmg->rom->data[bank * 0x4000];
for (k = 0x40; k <= 0x7f; k++) {
dmg->read_page[k] = &bank_base[(k - 0x40) << 8];
}
@@ -363,9 +371,9 @@ void dmg_write(void *_dmg, u16 address, u8 data)
u8 *page = dmg->write_page[address >> 8];
if (page) {
page[address & 0xff] = data;
return;
} else {
dmg_write_slow(dmg, address, data);
}
dmg_write_slow(dmg, address, data);
}
u16 dmg_read16(void *_dmg, u16 address)
+3
View File
@@ -68,6 +68,9 @@ struct dmg {
// for TIMA timer
u32 timer_cycles;
// current ROM bank (to avoid redundant page table updates)
int current_rom_bank;
};
void dmg_new(struct dmg *dmg, struct rom *rom, struct lcd *lcd);
+56
View File
@@ -0,0 +1,56 @@
#include <string.h>
#include "patches.h"
// Pokemon Red/Blue: fix terrible frame rate in menus
// make HandleMenuInput and WaitForTextScrollButtonPress halt instead of spin
static const u8 pokemon_red_patch0[] = { 0xd5 }; /* jr $3ad9 -> jr $3ad6 */
static const u8 pokemon_red_patch1[] = { 0xd7, 0x3d }; /* call $3e6d -> call $3dd7 */
static const struct rom_patch pokemon_red_patches[] = {
{ 0x3b00, 1, NULL, pokemon_red_patch0 },
{ 0x3889, 2, NULL, pokemon_red_patch1 },
};
static const struct rom_patch_list all_patches[] = {
{ "POKEMON RED", pokemon_red_patches, 2 },
{ "POKEMON BLUE", pokemon_red_patches, 2 },
};
#define PATCH_LIST_COUNT (sizeof(all_patches) / sizeof(all_patches[0]))
const struct rom_patch_list *patches_find(const char *title)
{
int k;
for (k = 0; k < PATCH_LIST_COUNT; k++) {
if (strcmp(title, all_patches[k].game_title) == 0) {
return &all_patches[k];
}
}
return NULL;
}
int patches_apply(u8 *rom_data, u32 rom_length, const struct rom_patch_list *list)
{
int k, applied = 0;
const struct rom_patch *p;
for (k = 0; k < list->patch_count; k++) {
p = &list->patches[k];
if (p->address + p->length > rom_length) {
continue;
}
if (p->original != NULL) {
if (memcmp(rom_data + p->address, p->original, p->length) != 0) {
continue;
}
}
memcpy(rom_data + p->address, p->replacement, p->length);
applied++;
}
return applied;
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef _PATCHES_H
#define _PATCHES_H
#include "types.h"
struct rom_patch {
u16 address;
u16 length;
const u8 *original; /* NULL = don't verify, just apply */
const u8 *replacement;
};
struct rom_patch_list {
const char *game_title;
const struct rom_patch *patches;
int patch_count;
};
/* Find patches for a game by title. Returns NULL if no patches found. */
const struct rom_patch_list *patches_find(const char *title);
/* Apply all patches from a patch list to ROM data. Returns number of patches applied. */
int patches_apply(u8 *rom_data, u32 rom_length, const struct rom_patch_list *list);
#endif
+1
View File
@@ -14,6 +14,7 @@ add_application("Gray Brick"
../src/cgb.c
../src/audio.c
../src/rom.c
../src/patches.c
../src/mbc.c
../compiler/compiler.c
../compiler/emitters.c
+12
View File
@@ -28,6 +28,7 @@
#include "mbc.h"
#include "audio.h"
#include "cgb.h"
#include "patches.h"
#include "debug.h"
#include "dialogs.h"
@@ -526,6 +527,17 @@ int LoadRom(Str63 fileName, short vRefNum)
FSRead(fileNo, &amtRead, rom.data);
FSClose(fileNo);
{
char title[17];
const struct rom_patch_list *patch_list;
rom_get_title(&rom, title);
patch_list = patches_find(title);
if (patch_list) {
patches_apply(rom.data, rom.length, patch_list);
}
}
// Read CGB flag from ROM header byte 0x143
rom.cgb_flag = rom.data[0x143];
+2
View File
@@ -332,6 +332,8 @@ int jit_run(struct dmg *dmg)
}
// sync hardware with cycles accumulated by compiled code
// sprintf(buf, "%lu %lu", dmg->frame_cycles, jit_regs.d2);
// set_status_bar(buf);
dmg_sync_hw(dmg, jit_regs.d2);
if (dmg->interrupt_enable) {
check_interrupts(dmg);