-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathmain.c
233 lines (176 loc) · 5.99 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/* Copyright (C) 2023 Nikita Burnashev
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted.
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND! */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libretro.h"
#include "core_api.h"
#include "stockfw.h"
#include "debug.h"
static void init_once();
static void full_cache_flush();
static int state_stub(const char *path) {
return 1;
}
#define MAXPATH 255
static char *corefile = NULL;
static char *romfile = NULL;
static char *tmpbuffer = NULL;
#define MIPS_J(pfunc) (2 << 26) | (uint32_t)pfunc >> 2 & ((1 << 26) - 1)
#define MIPS_JAL(pfunc) (3 << 26) | (uint32_t)pfunc >> 2 & ((1 << 26) - 1)
#define PATCH_J(target, hook) *(uint32_t*)(target) = MIPS_J(hook)
#define PATCH_JAL(target, hook) *(uint32_t*)(target) = MIPS_JAL(hook)
bool parse_filename(const char *file_path, const char**corename, const char **filename)
{
char* s = strncpy(tmpbuffer, file_path, MAXPATH);
*corename = s;
char* p = strchr(s, ';');
if (!p) return false;
*(p++) = 0;
char* pp = strrchr(s, '/');
if (!pp) return false;
*(pp++) = 0;
*corename = pp;
*filename = p;
char* p2 = strrchr(p, '.');
if (!p2) return false;
*(p2) = 0;
return true;
}
void load_and_run_core(const char *file_path, int load_state)
{
init_once();
xlog("l: run file=%s\n", file_path);
// the expected template for file_path is - [corename];[rom filename].gba
const char *corename;
const char *filename;
if (!parse_filename(file_path, &corename, &filename)) {
xlog("file not MC stub: calling run_gba\n");
dbg_show_noblock(0x00, "\n STOCK\n\n %s\n\n ", file_path); // black
run_gba(file_path, load_state);
return;
}
// this will show a loading screen when loading a rom.
// it will act as an indicator that a custom core and not a stock emulator is running.
dbg_show_noblock(0x7800,"\n MULTICORE\n\n %s\n\n %s \n\n ", corename, filename); // red
void *core_load_addr = (void*)0x87000000;
FILE *pf;
size_t core_size;
/* wait for the sound thread to exit, replicated in all run_... functions */
g_snd_task_flags = g_snd_task_flags & 0xfffe;
while (g_snd_task_flags != 0) {
dly_tsk(1);
}
/* FIXME! all of it!! */
RAMSIZE = 0x87000000;
snprintf(corefile, MAXPATH, "/mnt/sda1/cores/%s/core_87000000", corename);
snprintf(romfile, MAXPATH, "/mnt/sda1/ROMS/%s/%s", corename, filename);
xlog("corefile=%s\n", corefile);
xlog("romfile=%s\n", romfile);
pf = fopen(corefile, "rb");
if (!pf) {
xlog("Error opening corefile\n");
return;
}
fseeko(pf, 0, SEEK_END);
core_size = ftell(pf);
fseeko(pf, 0, SEEK_SET);
fw_fread(core_load_addr, 1, core_size, pf);
fclose(pf);
xlog("l: core loaded\n");
full_cache_flush();
xlog("l: cache flushed\n");
// address of the core entry function resides at the begining of the loaded core
core_entry_t core_entry = core_load_addr;
// the entry function clears core's .bss and return the core's exported api
struct retro_core_t *core_api = core_entry();
/* TODO */
gfn_state_load = state_stub;
gfn_state_save = state_stub;
core_api->retro_set_video_refresh(retro_video_refresh_cb);
core_api->retro_set_audio_sample_batch(retro_audio_sample_batch_cb);
core_api->retro_set_input_poll(retro_input_poll_cb);
core_api->retro_set_input_state(retro_input_state_cb);
core_api->retro_set_environment(retro_environment_cb);
xlog("l: retro_init\n");
core_api->retro_init();
g_retro_game_info.path = romfile;
g_retro_game_info.data = gp_buf_64m;
g_retro_game_info.size = g_run_file_size;
gfn_retro_get_region = core_api->retro_get_region;
gfn_get_system_av_info = core_api->retro_get_system_av_info;
gfn_retro_load_game = core_api->retro_load_game;
gfn_retro_unload_game = core_api->retro_unload_game;
gfn_retro_run = core_api->retro_run;
xlog("l: run_emulator(%d)\n", load_state);
run_emulator(load_state);
xlog("l: retro_deinit\n");
core_api->retro_deinit();
}
/* FIXME gets repetitive but we really need this $ra (in lib.c, too) */
void hook_sys_watchdog_reboot(void)
{
unsigned ra;
asm volatile ("move %0, $ra" : "=r" (ra));
lcd_bsod("assert at 0x%08x", ra);
}
void hook_exception_handler(unsigned code)
{
unsigned ra;
asm volatile ("move %0, $ra" : "=r" (ra));
lcd_bsod("exception %d at 0x%08x", code, ra);
}
static void clear_bss()
{
extern void *__bss_start;
extern void *_end;
void *start = &__bss_start;
void *end = &_end;
memset(start, 0, end - start);
}
static void restore_stock_gp()
{
// set $gp to the original stock's value like is done at 0x80001274 where $gp is
// initially set by the stock startup code
asm(
"lui $gp, 0x80c1 \n"
"addiu $gp, $gp, 0x14f4 \n"
);
}
static void init_once()
{
static bool first_call = true;
if (!first_call)
return;
first_call = false;
clear_bss();
lcd_init();
#if defined(CLEAR_LOG_ON_BOOT)
xlog_clear();
#endif
corefile = malloc(MAXPATH);
romfile = malloc(MAXPATH);
tmpbuffer = malloc(MAXPATH);
// Before calling "irq_handler", make sure the $gp register points to the original address that
// was initially set by the stock startup code and that all stock code expect it to be.
//
// This solves the freeze that was caused when using gpSP dynarec.
// The dynamically generated code modifies the $gp register for the duration of its execution,
// but if suddenly an interrupt occurs and it needs to access some global vars, then the system will crash
// or freeze because $gp doesn't have right value that the irq/interrupt handlers expect it to be.
PATCH_JAL(0x80049744, restore_stock_gp);
}
static void full_cache_flush()
{
unsigned idx;
// Index_Writeback_Inv_D
for (idx = 0x80000000; idx <= 0x80004000; idx += 16) // all of D-cache
asm volatile("cache 1, 0(%0); cache 1, 0(%0)" : : "r"(idx));
asm volatile("sync 0; nop; nop");
// Index_Invalidate_I
for (idx = 0x80000000; idx <= 0x80004000; idx += 16) // all of I-cache
asm volatile("cache 0, 0(%0); cache 0, 0(%0)" : : "r"(idx));
asm volatile("nop; nop; nop; nop; nop"); // ehb may be nop on this core
}