-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlibpce.cpp
More file actions
308 lines (270 loc) · 7.95 KB
/
libpce.cpp
File metadata and controls
308 lines (270 loc) · 7.95 KB
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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
#include "corelib.h"
#include "ring.hpp"
#include "Geargrafx/src/geargrafx_core.h"
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define DBG true
#ifndef DBG
#define DBG false
#endif
#if DBG
#include <stdio.h>
#else
void printf(const char* msg, ...) {}
#endif
#define REQUIRE_CORE(val) if (!has_init_) { printf("no core. skipping %s\n", __func__); return val; }
GeargrafxCore core_;
bool has_init_ = false;
uint32_t megabuffer[OVER_WIDTH * OVER_HEIGHT]; // core needs to dump w/ overscan somewhere
uint32_t fbuffer[VIDEO_WIDTH * VIDEO_HEIGHT]; // non-overscan buffer.
int16_t abuffer[SAMPLE_RATE]; // buffer at most 1sec audio
Ring<int16_t, SAMPLE_RATE> ring_;
void (*corelib_puts)(const char* msg);
EXPOSE
void corelib_set_puts(void(*cb)(const char*)) {
corelib_puts = cb;
corelib_puts("corelib_puts initialized");
}
#ifndef ISWASM
#define puts(msg) corelib_puts(msg);
#endif
void log(const char* msg) {
#ifdef DBG
puts(msg);
#endif
}
EXPOSE
void zero() {
printf("zeroing.\n");
}
EXPOSE
void set_key(size_t key, char val) {
GG_Keys gsk;
switch(key) {
case BTN_A: gsk = GG_KEY_I; break;
case BTN_B: gsk = GG_KEY_II; break;
/* Keys III to VI also exist, but
don't exist on the main controller
so are unmapped here.
*/
case BTN_Sel: gsk = GG_KEY_SELECT; break;
case BTN_Start: gsk = GG_KEY_RUN; break;
case BTN_Up: gsk = GG_KEY_UP; break;
case BTN_Down: gsk = GG_KEY_DOWN; break;
case BTN_Left: gsk = GG_KEY_LEFT; break;
case BTN_Right: gsk = GG_KEY_RIGHT; break;
default: return;
}
constexpr GG_Controllers joypad = GG_CONTROLLER_1;
if (val) {
core_.KeyPressed(joypad, gsk);
} else {
core_.KeyReleased(joypad, gsk);
}
}
uint8_t rom_buffer_[10*1024*1024];
// frontend needs a wasm buffer into which it can copy the rom.
EXPOSE
uint8_t *alloc_rom(size_t bytes) {
if (bytes > sizeof(rom_buffer_)) {
printf("rom too large, failed to load\n");
return 0;
}
return rom_buffer_;
}
void unused_input_pump() {}
EXPOSE
void init(const uint8_t* data, size_t len) {
printf("libsms init\n");
core_.Init(
unused_input_pump,
GG_PIXEL_RGBA8888);
if (!core_.LoadHuCardFromBuffer(data, len, "game.pce")) {
puts("ROM load failed.");
return;
}
has_init_ = true;
GG_Runtime_Info runtime_info;
core_.GetRuntimeInfo(runtime_info);
printf("screen width: %d\n", runtime_info.screen_width);
printf("screen height: %d\n", runtime_info.screen_height);
assert(runtime_info.screen_width == VIDEO_WIDTH);
assert(runtime_info.screen_height == VIDEO_HEIGHT);
}
EXPOSE
const uint8_t *framebuffer() {
#ifdef ISWASM
// ensure all pixels have 255 alpha
for (int i = 0; i < VIDEO_WIDTH * VIDEO_HEIGHT; i++) {
fbuffer[i] |= 0xff000000;
}
#endif
return (const uint8_t*)fbuffer;
}
extern "C"
__attribute__((visibility("default")))
size_t framebuffer_bytes() {
return sizeof fbuffer;
}
size_t min(size_t a, size_t b) {
return a < b ? a : b;
}
int16_t last_sample_ = 0;
EXPOSE
long apu_sample_variable(int16_t* output, int32_t samples) {
REQUIRE_CORE(0);
size_t read = ring_.pull(output, samples);
if (read > 0) {
last_sample_ = output[read - 1];
}
// if (read < samples) {
// printf("underflow: want=%u, had=%zu\n", samples, read);
// }
for (int i = read; read < samples; read++) {
output[i] = last_sample_;
}
return read;
}
void copy_from_megabuffer(size_t width, size_t height) {
// Given a frame that was just copied to the megabuffer, copy a smaller
// window into the framebuffer for presentation.
GG_Runtime_Info runtime_info;
core_.GetRuntimeInfo(runtime_info);
// printf("screen width: %d\n", runtime_info.screen_width);
// printf("screen height: %d\n", runtime_info.screen_height);
// assert(runtime_info.screen_width == VIDEO_WIDTH);
// assert(runtime_info.screen_height == VIDEO_HEIGHT);
const int row_stride = runtime_info.screen_width;
for (int y = 0; y < height; y++) {
memcpy(fbuffer + width*y,
megabuffer+row_stride*y,
sizeof(uint32_t) * width);
// for (int x = 0; x < width; x++) {
// uint32_t pixel = megabuffer[row_stride*y + x];
// // rgba -> abgr byte ordering
// // uint32_t out = (r) | (b << 16) | (g << 8) | 0xff000000;
// // fbuffer[y*width + x] = out;
// fbuffer[y*width + x] = 0xffff0000;
// }
}
}
EXPOSE
void frame() {
REQUIRE_CORE();
int samples = 0;
// core_.RunToVBlank((uint8_t*)&megabuffer, abuffer, &samples);
core_.RunToVBlank((uint8_t*)&megabuffer, abuffer, &samples);
copy_from_megabuffer(VIDEO_WIDTH, VIDEO_HEIGHT);
// Core produces stereo, convert to mono
for (int i = 0; i < samples/2; i++) {
abuffer[i] = abuffer[2*i];
}
samples /= 2;
int pushed = ring_.push(abuffer, samples);
if (pushed < samples) {
printf("ring overflow: %d / %d pushed\n", pushed, samples);
}
// auto *video = core_.GetVideo();
// video->Render32bit(video->GetFrameBuffer(), (uint8_t*)fbuffer, GS_PIXEL_RGBA8888, VIDEO_WIDTH*VIDEO_HEIGHT, /*overscan*/false);
}
// Returns bytes saved, and writes to dest.
// Dest may be null to calculate size only. returns < 0 on error.
constexpr size_t MAX_STATE_SIZE = 100'000'000; // 100M
EXPOSE int save_str(uint8_t* dest, int capacity) {
REQUIRE_CORE(-1);
const bool estimate = dest == NULL;
if (estimate) {
// size is unknown apriori, give it a huge allocation
size_t sz = MAX_STATE_SIZE;
dest = (uint8_t*)malloc(sz);
capacity = sz;
}
size_t bytes = capacity;
core_.SaveState(dest, bytes);
printf("Wrote %zu bytes\n", bytes);
assert(bytes <= capacity);
if (estimate) {
free(dest);
}
return bytes;
}
// Loads len bytes from src
EXPOSE void load_str(int len, const uint8_t* src) {
core_.LoadState(src, len);
}
#ifndef __wasm32__
// save&load unsupported for wasm
EXPOSE
void save(int fd) {
REQUIRE_CORE();
// Total size is unknown, make a conservative allocation.
size_t bytes = save_str(NULL, 0);
uint8_t *buffer = (uint8_t*)malloc(bytes);
save_str(buffer, bytes);
uint8_t* const orig_buffer = buffer;
if (save_str(buffer, bytes) < 0) {
puts("Failed to save state.");
return;
} else {
printf("State generated %zu bytes\n", bytes);
}
while (bytes > 0) {
ssize_t written = write(fd, buffer, bytes);
if (written <= 0) {
perror("Save failed: ");
return;
}
bytes -= written;
buffer += written;
}
free(orig_buffer);
}
EXPOSE
void dump_state(const char* filename) {
REQUIRE_CORE();
int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY , 0700);
if (fd == -1) {
perror("failed to open:");
return;
}
printf("saving to %s\n", filename);
save(fd);
}
EXPOSE
void load(int fd) {
REQUIRE_CORE();
ssize_t bytes = lseek(fd, 0, SEEK_END);
if (bytes <= 0) {
perror("Failed to seek while loading: ");
return;
}
const size_t state_size = bytes;
printf("Loading %zu bytes\n", bytes);
lseek(fd, 0, SEEK_SET);
uint8_t *buffer = (uint8_t*)malloc(bytes);
uint8_t *write = buffer;
while (bytes > 0) {
ssize_t read_bytes = read(fd, write, bytes);
if (read_bytes <= 0) {
perror("Read failure during load: ");
return;
}
printf("read returned %zu bytes\n", read_bytes);
write += read_bytes;
bytes -= read_bytes;
}
load_str(state_size, buffer);
free(buffer);
}
EXPOSE
void load_state(const char* filename) {
REQUIRE_CORE();
int fd = open(filename, O_RDONLY , 0700);
if (fd == -1) {
perror("Failed to open: ");
return;
}
load(fd);
}
#endif // ifndef __wasm32__