Bus fixes: Reading of CPU MMIO registers does not update MDR
[lsnes.git] / src / emulation / bsnes-legacy / core.cpp
blobd93ee608cd28a1990ff22d6917807643e846a350
1 /*************************************************************************
2 * Copyright (C) 2011-2013 by Ilari Liusvaara *
3 * *
4 * This program is free software: you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation, either version 3 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
16 *************************************************************************/
17 #include "lsnes.hpp"
18 #include <sstream>
19 #include <map>
20 #include <string>
21 #include <cctype>
22 #include <vector>
23 #include <fstream>
24 #include <climits>
25 #include "core/audioapi.hpp"
26 #include "core/misc.hpp"
27 #include "core/command.hpp"
28 #include "core/controllerframe.hpp"
29 #include "core/dispatch.hpp"
30 #include "core/framebuffer.hpp"
31 #include "core/instance.hpp"
32 #include "core/settings.hpp"
33 #include "core/messages.hpp"
34 #include "interface/cover.hpp"
35 #include "interface/romtype.hpp"
36 #include "interface/setting.hpp"
37 #include "interface/callbacks.hpp"
38 #include "library/framebuffer-pixfmt-lrgb.hpp"
39 #include "library/hex.hpp"
40 #include "library/string.hpp"
41 #include "library/portctrl-data.hpp"
42 #include "library/memoryspace.hpp"
43 #include "library/framebuffer.hpp"
44 #include "library/lua-base.hpp"
45 #include "lua/internal.hpp"
46 #ifdef BSNES_HAS_DEBUGGER
47 #define DEBUGGER
48 #endif
49 #include <snes/snes.hpp>
50 #include <gameboy/gameboy.hpp>
51 #include LIBSNES_INCLUDE_FILE
53 #define DURATION_NTSC_FRAME 357366
54 #define DURATION_NTSC_FIELD 357368
55 #define DURATION_PAL_FRAME 425568
56 #define DURATION_PAL_FIELD 425568
57 #define ROM_TYPE_NONE 0
58 #define ROM_TYPE_SNES 1
59 #define ROM_TYPE_BSX 2
60 #define ROM_TYPE_BSXSLOTTED 3
61 #define ROM_TYPE_SUFAMITURBO 4
62 #define ROM_TYPE_SGB 5
64 #define ADDR_KIND_ALL -1
65 #define ADDR_KIND_NONE -2
67 namespace
69 bool p1disable = false;
70 bool do_hreset_flag = false;
71 long do_reset_flag = -1;
72 bool support_hreset = false;
73 bool support_dreset = false;
74 bool save_every_frame = false;
75 bool have_saved_this_frame = false;
76 int16_t blanksound[1070] = {0};
77 int16_t soundbuf[8192] = {0};
78 size_t soundbuf_fill = 0;
79 bool last_hires = false;
80 bool last_interlace = false;
81 bool last_PAL = false;
82 bool disable_breakpoints = false;
83 uint64_t trace_counter;
84 bool trace_cpu_enable;
85 bool trace_smp_enable;
86 bool trace_sa1_enable;
87 SNES::Interface* old;
88 bool stepping_into_save;
89 bool video_refresh_done;
90 bool forced_hook = false;
91 std::map<int16_t, std::pair<uint64_t, uint64_t>> ptrmap;
92 std::vector<uint8_t> init_savestate;
93 uint32_t cover_fbmem[512 * 448];
94 //Delay reset.
95 unsigned long long delayreset_cycles_run;
96 unsigned long long delayreset_cycles_target;
98 //Framebuffer.
99 struct framebuffer::info cover_fbinfo = {
100 &framebuffer::pixfmt_lrgb, //Format.
101 (char*)cover_fbmem, //Memory.
102 512, 448, 2048, //Physical size.
103 512, 448, 2048, //Logical size.
104 0, 0 //Offset.
107 struct interface_device_reg snes_registers[] = {
108 {"pbpc", []() -> uint64_t { return SNES::cpu.regs.pc; }, [](uint64_t v) { SNES::cpu.regs.pc = v; }},
109 {"pb", []() -> uint64_t { return SNES::cpu.regs.pc >> 16; },
110 [](uint64_t v) { SNES::cpu.regs.pc = (v << 16) | (SNES::cpu.regs.pc & 0xFFFF); }},
111 {"pc", []() -> uint64_t { return SNES::cpu.regs.pc & 0xFFFF; },
112 [](uint64_t v) { SNES::cpu.regs.pc = (v & 0xFFFF) | (SNES::cpu.regs.pc & ~0xFFFF); }},
113 {"r0", []() -> uint64_t { return SNES::cpu.regs.r[0]; }, [](uint64_t v) { SNES::cpu.regs.r[0] = v; }},
114 {"r1", []() -> uint64_t { return SNES::cpu.regs.r[1]; }, [](uint64_t v) { SNES::cpu.regs.r[1] = v; }},
115 {"r2", []() -> uint64_t { return SNES::cpu.regs.r[2]; }, [](uint64_t v) { SNES::cpu.regs.r[2] = v; }},
116 {"r3", []() -> uint64_t { return SNES::cpu.regs.r[3]; }, [](uint64_t v) { SNES::cpu.regs.r[3] = v; }},
117 {"r4", []() -> uint64_t { return SNES::cpu.regs.r[4]; }, [](uint64_t v) { SNES::cpu.regs.r[4] = v; }},
118 {"r5", []() -> uint64_t { return SNES::cpu.regs.r[5]; }, [](uint64_t v) { SNES::cpu.regs.r[5] = v; }},
119 {"a", []() -> uint64_t { return SNES::cpu.regs.a; }, [](uint64_t v) { SNES::cpu.regs.a = v; }},
120 {"x", []() -> uint64_t { return SNES::cpu.regs.x; }, [](uint64_t v) { SNES::cpu.regs.x = v; }},
121 {"y", []() -> uint64_t { return SNES::cpu.regs.y; }, [](uint64_t v) { SNES::cpu.regs.y = v; }},
122 {"z", []() -> uint64_t { return SNES::cpu.regs.z; }, [](uint64_t v) { SNES::cpu.regs.z = v; }},
123 {"s", []() -> uint64_t { return SNES::cpu.regs.s; }, [](uint64_t v) { SNES::cpu.regs.s = v; }},
124 {"d", []() -> uint64_t { return SNES::cpu.regs.d; }, [](uint64_t v) { SNES::cpu.regs.d = v; }},
125 {"db", []() -> uint64_t { return SNES::cpu.regs.db; }, [](uint64_t v) { SNES::cpu.regs.db = v; }},
126 {"p", []() -> uint64_t { return SNES::cpu.regs.p; }, [](uint64_t v) { SNES::cpu.regs.p = v; }},
127 {"e", []() -> uint64_t { return SNES::cpu.regs.e; }, [](uint64_t v) { SNES::cpu.regs.e = v; }},
128 {"irq", []() -> uint64_t { return SNES::cpu.regs.irq; }, [](uint64_t v) { SNES::cpu.regs.irq = v; }},
129 {"wai", []() -> uint64_t { return SNES::cpu.regs.wai; }, [](uint64_t v) { SNES::cpu.regs.wai = v; }},
130 {"mdr", []() -> uint64_t { return SNES::cpu.regs.mdr; }, [](uint64_t v) { SNES::cpu.regs.mdr = v; }},
131 {"vector", []() -> uint64_t { return SNES::cpu.regs.vector; },
132 [](uint64_t v) { SNES::cpu.regs.vector = v; }},
133 {"aa", []() -> uint64_t { return SNES::cpu.aa; }, [](uint64_t v) { SNES::cpu.aa = v; }},
134 {"rd", []() -> uint64_t { return SNES::cpu.rd; }, [](uint64_t v) { SNES::cpu.rd = v; }},
135 {"sp", []() -> uint64_t { return SNES::cpu.sp; }, [](uint64_t v) { SNES::cpu.sp = v; }},
136 {"dp", []() -> uint64_t { return SNES::cpu.dp; }, [](uint64_t v) { SNES::cpu.dp = v; }},
137 {"p_n", []() -> uint64_t { return SNES::cpu.regs.p.n; }, [](uint64_t v) { SNES::cpu.regs.p.n = v; },
138 true},
139 {"p_v", []() -> uint64_t { return SNES::cpu.regs.p.v; }, [](uint64_t v) { SNES::cpu.regs.p.v = v; },
140 true},
141 {"p_m", []() -> uint64_t { return SNES::cpu.regs.p.m; }, [](uint64_t v) { SNES::cpu.regs.p.m = v; },
142 true},
143 {"p_x", []() -> uint64_t { return SNES::cpu.regs.p.x; }, [](uint64_t v) { SNES::cpu.regs.p.x = v; },
144 true},
145 {"p_d", []() -> uint64_t { return SNES::cpu.regs.p.d; }, [](uint64_t v) { SNES::cpu.regs.p.d = v; },
146 true},
147 {"p_i", []() -> uint64_t { return SNES::cpu.regs.p.i; }, [](uint64_t v) { SNES::cpu.regs.p.i = v; },
148 true},
149 {"p_z", []() -> uint64_t { return SNES::cpu.regs.p.z; }, [](uint64_t v) { SNES::cpu.regs.p.z = v; },
150 true},
151 {"p_c", []() -> uint64_t { return SNES::cpu.regs.p.c; }, [](uint64_t v) { SNES::cpu.regs.p.c = v; },
152 true},
153 {"hcounter", []() -> uint64_t { return SNES::cpu.hcounter(); }, [](uint64_t v) {}, false},
154 {"vcounter", []() -> uint64_t { return SNES::cpu.vcounter(); }, [](uint64_t v) {}, false},
155 {"sa1_pbpc", []() -> uint64_t { return SNES::sa1.regs.pc; },
156 [](uint64_t v) { SNES::sa1.regs.pc = v; }},
157 {"sa1_pb", []() -> uint64_t { return SNES::sa1.regs.pc >> 16; },
158 [](uint64_t v) { SNES::sa1.regs.pc = (v << 16) | (SNES::sa1.regs.pc & 0xFFFF); }},
159 {"sa1_pc", []() -> uint64_t { return SNES::sa1.regs.pc & 0xFFFF; },
160 [](uint64_t v) { SNES::sa1.regs.pc = (v & 0xFFFF) | (SNES::sa1.regs.pc & ~0xFFFF); }},
161 {"sa1_r0", []() -> uint64_t { return SNES::sa1.regs.r[0]; },
162 [](uint64_t v) { SNES::sa1.regs.r[0] = v; }},
163 {"sa1_r1", []() -> uint64_t { return SNES::sa1.regs.r[1]; },
164 [](uint64_t v) { SNES::sa1.regs.r[1] = v; }},
165 {"sa1_r2", []() -> uint64_t { return SNES::sa1.regs.r[2]; },
166 [](uint64_t v) { SNES::sa1.regs.r[2] = v; }},
167 {"sa1_r3", []() -> uint64_t { return SNES::sa1.regs.r[3]; },
168 [](uint64_t v) { SNES::sa1.regs.r[3] = v; }},
169 {"sa1_r4", []() -> uint64_t { return SNES::sa1.regs.r[4]; },
170 [](uint64_t v) { SNES::sa1.regs.r[4] = v; }},
171 {"sa1_r5", []() -> uint64_t { return SNES::sa1.regs.r[5]; },
172 [](uint64_t v) { SNES::sa1.regs.r[5] = v; }},
173 {"sa1_a", []() -> uint64_t { return SNES::sa1.regs.a; }, [](uint64_t v) { SNES::sa1.regs.a = v; }},
174 {"sa1_x", []() -> uint64_t { return SNES::sa1.regs.x; }, [](uint64_t v) { SNES::sa1.regs.x = v; }},
175 {"sa1_y", []() -> uint64_t { return SNES::sa1.regs.y; }, [](uint64_t v) { SNES::sa1.regs.y = v; }},
176 {"sa1_z", []() -> uint64_t { return SNES::sa1.regs.z; }, [](uint64_t v) { SNES::sa1.regs.z = v; }},
177 {"sa1_s", []() -> uint64_t { return SNES::sa1.regs.s; }, [](uint64_t v) { SNES::sa1.regs.s = v; }},
178 {"sa1_d", []() -> uint64_t { return SNES::sa1.regs.d; }, [](uint64_t v) { SNES::sa1.regs.d = v; }},
179 {"sa1_db", []() -> uint64_t { return SNES::sa1.regs.db; }, [](uint64_t v) { SNES::sa1.regs.db = v; }},
180 {"sa1_p", []() -> uint64_t { return SNES::sa1.regs.p; }, [](uint64_t v) { SNES::sa1.regs.p = v; }},
181 {"sa1_e", []() -> uint64_t { return SNES::sa1.regs.e; }, [](uint64_t v) { SNES::sa1.regs.e = v; }},
182 {"sa1_irq", []() -> uint64_t { return SNES::sa1.regs.irq; },
183 [](uint64_t v) { SNES::sa1.regs.irq = v; }},
184 {"sa1_wai", []() -> uint64_t { return SNES::sa1.regs.wai; },
185 [](uint64_t v) { SNES::sa1.regs.wai = v; }},
186 {"sa1_mdr", []() -> uint64_t { return SNES::sa1.regs.mdr; },
187 [](uint64_t v) { SNES::sa1.regs.mdr = v; }},
188 {"sa1_vector", []() -> uint64_t { return SNES::sa1.regs.vector; },
189 [](uint64_t v) { SNES::sa1.regs.vector = v; }},
190 {"sa1_aa", []() -> uint64_t { return SNES::sa1.aa; }, [](uint64_t v) { SNES::sa1.aa = v; }},
191 {"sa1_rd", []() -> uint64_t { return SNES::sa1.rd; }, [](uint64_t v) { SNES::sa1.rd = v; }},
192 {"sa1_sp", []() -> uint64_t { return SNES::sa1.sp; }, [](uint64_t v) { SNES::sa1.sp = v; }},
193 {"sa1_dp", []() -> uint64_t { return SNES::sa1.dp; }, [](uint64_t v) { SNES::sa1.dp = v; }},
194 {"sa1_p_n", []() -> uint64_t { return SNES::sa1.regs.p.n; },
195 [](uint64_t v) { SNES::sa1.regs.p.n = v; }, true},
196 {"sa1_p_v", []() -> uint64_t { return SNES::sa1.regs.p.v; },
197 [](uint64_t v) { SNES::sa1.regs.p.v = v; }, true},
198 {"sa1_p_m", []() -> uint64_t { return SNES::sa1.regs.p.m; },
199 [](uint64_t v) { SNES::sa1.regs.p.m = v; }, true},
200 {"sa1_p_x", []() -> uint64_t { return SNES::sa1.regs.p.x; },
201 [](uint64_t v) { SNES::sa1.regs.p.x = v; }, true},
202 {"sa1_p_d", []() -> uint64_t { return SNES::sa1.regs.p.d; },
203 [](uint64_t v) { SNES::sa1.regs.p.d = v; }, true},
204 {"sa1_p_i", []() -> uint64_t { return SNES::sa1.regs.p.i; },
205 [](uint64_t v) { SNES::sa1.regs.p.i = v; }, true},
206 {"sa1_p_z", []() -> uint64_t { return SNES::sa1.regs.p.z; },
207 [](uint64_t v) { SNES::sa1.regs.p.z = v; }, true},
208 {"sa1_p_c", []() -> uint64_t { return SNES::sa1.regs.p.c; },
209 [](uint64_t v) { SNES::sa1.regs.p.c = v; }, true},
210 {"hcounter", []() -> uint64_t { return SNES::cpu.hcounter(); }, [](uint64_t v) {}},
211 {"vcounter", []() -> uint64_t { return SNES::cpu.vcounter(); }, [](uint64_t v) {}},
212 #ifdef BSNES_IS_COMPAT
213 {"ppu_display_disabled", []() -> uint64_t { return SNES::ppu.regs.display_disabled; },
214 [](uint64_t v) { SNES::ppu.regs.display_disabled = v; }, true},
215 {"ppu_oam_priority", []() -> uint64_t { return SNES::ppu.regs.oam_priority; },
216 [](uint64_t v) { SNES::ppu.regs.oam_priority = v; }, true},
217 {"ppu_bg_tilesize[0]", []() -> uint64_t { return SNES::ppu.regs.bg_tilesize[0]; },
218 [](uint64_t v) { SNES::ppu.regs.bg_tilesize[0] = v; }, true},
219 {"ppu_bg_tilesize[1]", []() -> uint64_t { return SNES::ppu.regs.bg_tilesize[1]; },
220 [](uint64_t v) { SNES::ppu.regs.bg_tilesize[1] = v; }, true},
221 {"ppu_bg_tilesize[2]", []() -> uint64_t { return SNES::ppu.regs.bg_tilesize[2]; },
222 [](uint64_t v) { SNES::ppu.regs.bg_tilesize[2] = v; }, true},
223 {"ppu_bg_tilesize[3]", []() -> uint64_t { return SNES::ppu.regs.bg_tilesize[3]; },
224 [](uint64_t v) { SNES::ppu.regs.bg_tilesize[3] = v; }, true},
225 {"ppu_bg3_priority", []() -> uint64_t { return SNES::ppu.regs.bg3_priority; },
226 [](uint64_t v) { SNES::ppu.regs.bg3_priority = v; }, true},
227 {"ppu_mosaic_enabled[0]", []() -> uint64_t { return SNES::ppu.regs.mosaic_enabled[0]; },
228 [](uint64_t v) { SNES::ppu.regs.mosaic_enabled[0] = v; }, true},
229 {"ppu_mosaic_enabled[1]", []() -> uint64_t { return SNES::ppu.regs.mosaic_enabled[1]; },
230 [](uint64_t v) { SNES::ppu.regs.mosaic_enabled[1] = v; }, true},
231 {"ppu_mosaic_enabled[2]", []() -> uint64_t { return SNES::ppu.regs.mosaic_enabled[2]; },
232 [](uint64_t v) { SNES::ppu.regs.mosaic_enabled[2] = v; }, true},
233 {"ppu_mosaic_enabled[3]", []() -> uint64_t { return SNES::ppu.regs.mosaic_enabled[3]; },
234 [](uint64_t v) { SNES::ppu.regs.mosaic_enabled[3] = v; }, true},
235 {"ppu_vram_incmode", []() -> uint64_t { return SNES::ppu.regs.vram_incmode; },
236 [](uint64_t v) { SNES::ppu.regs.vram_incmode = v; }, true},
237 {"ppu_mode7_vflip", []() -> uint64_t { return SNES::ppu.regs.mode7_vflip; },
238 [](uint64_t v) { SNES::ppu.regs.mode7_vflip = v; }, true},
239 {"ppu_mode7_hflip", []() -> uint64_t { return SNES::ppu.regs.mode7_hflip; },
240 [](uint64_t v) { SNES::ppu.regs.mode7_hflip = v; }, true},
241 {"ppu_window1_enabled[0]", []() -> uint64_t { return SNES::ppu.regs.window1_enabled[0]; },
242 [](uint64_t v) { SNES::ppu.regs.window1_enabled[0] = v; }, true},
243 {"ppu_window1_enabled[1]", []() -> uint64_t { return SNES::ppu.regs.window1_enabled[1]; },
244 [](uint64_t v) { SNES::ppu.regs.window1_enabled[1] = v; }, true},
245 {"ppu_window1_enabled[2]", []() -> uint64_t { return SNES::ppu.regs.window1_enabled[2]; },
246 [](uint64_t v) { SNES::ppu.regs.window1_enabled[2] = v; }, true},
247 {"ppu_window1_enabled[3]", []() -> uint64_t { return SNES::ppu.regs.window1_enabled[3]; },
248 [](uint64_t v) { SNES::ppu.regs.window1_enabled[3] = v; }, true},
249 {"ppu_window1_enabled[4]", []() -> uint64_t { return SNES::ppu.regs.window1_enabled[4]; },
250 [](uint64_t v) { SNES::ppu.regs.window1_enabled[4] = v; }, true},
251 {"ppu_window1_enabled[5]", []() -> uint64_t { return SNES::ppu.regs.window1_enabled[5]; },
252 [](uint64_t v) { SNES::ppu.regs.window1_enabled[5] = v; }, true},
253 {"ppu_window1_invert[0]", []() -> uint64_t { return SNES::ppu.regs.window1_invert[0]; },
254 [](uint64_t v) { SNES::ppu.regs.window1_invert[0] = v; }, true},
255 {"ppu_window1_invert[1]", []() -> uint64_t { return SNES::ppu.regs.window1_invert[1]; },
256 [](uint64_t v) { SNES::ppu.regs.window1_invert[1] = v; }, true},
257 {"ppu_window1_invert[2]", []() -> uint64_t { return SNES::ppu.regs.window1_invert[2]; },
258 [](uint64_t v) { SNES::ppu.regs.window1_invert[2] = v; }, true},
259 {"ppu_window1_invert[3]", []() -> uint64_t { return SNES::ppu.regs.window1_invert[3]; },
260 [](uint64_t v) { SNES::ppu.regs.window1_invert[3] = v; }, true},
261 {"ppu_window1_invert[4]", []() -> uint64_t { return SNES::ppu.regs.window1_invert[4]; },
262 [](uint64_t v) { SNES::ppu.regs.window1_invert[4] = v; }, true},
263 {"ppu_window1_invert[5]", []() -> uint64_t { return SNES::ppu.regs.window1_invert[5]; },
264 [](uint64_t v) { SNES::ppu.regs.window1_invert[5] = v; }, true},
265 {"ppu_window2_enabled[0]", []() -> uint64_t { return SNES::ppu.regs.window2_enabled[0]; },
266 [](uint64_t v) { SNES::ppu.regs.window2_enabled[0] = v; }, true},
267 {"ppu_window2_enabled[1]", []() -> uint64_t { return SNES::ppu.regs.window2_enabled[1]; },
268 [](uint64_t v) { SNES::ppu.regs.window2_enabled[1] = v; }, true},
269 {"ppu_window2_enabled[2]", []() -> uint64_t { return SNES::ppu.regs.window2_enabled[2]; },
270 [](uint64_t v) { SNES::ppu.regs.window2_enabled[2] = v; }, true},
271 {"ppu_window2_enabled[3]", []() -> uint64_t { return SNES::ppu.regs.window2_enabled[3]; },
272 [](uint64_t v) { SNES::ppu.regs.window2_enabled[3] = v; }, true},
273 {"ppu_window2_enabled[4]", []() -> uint64_t { return SNES::ppu.regs.window2_enabled[4]; },
274 [](uint64_t v) { SNES::ppu.regs.window2_enabled[4] = v; }, true},
275 {"ppu_window2_enabled[5]", []() -> uint64_t { return SNES::ppu.regs.window2_enabled[5]; },
276 [](uint64_t v) { SNES::ppu.regs.window2_enabled[5] = v; }, true},
277 {"ppu_window2_invert[0]", []() -> uint64_t { return SNES::ppu.regs.window2_invert[0]; },
278 [](uint64_t v) { SNES::ppu.regs.window2_invert[0] = v; }, true},
279 {"ppu_window2_invert[1]", []() -> uint64_t { return SNES::ppu.regs.window2_invert[1]; },
280 [](uint64_t v) { SNES::ppu.regs.window2_invert[1] = v; }, true},
281 {"ppu_window2_invert[2]", []() -> uint64_t { return SNES::ppu.regs.window2_invert[2]; },
282 [](uint64_t v) { SNES::ppu.regs.window2_invert[2] = v; }, true},
283 {"ppu_window2_invert[3]", []() -> uint64_t { return SNES::ppu.regs.window2_invert[3]; },
284 [](uint64_t v) { SNES::ppu.regs.window2_invert[3] = v; }, true},
285 {"ppu_window2_invert[4]", []() -> uint64_t { return SNES::ppu.regs.window2_invert[4]; },
286 [](uint64_t v) { SNES::ppu.regs.window2_invert[4] = v; }, true},
287 {"ppu_window2_invert[5]", []() -> uint64_t { return SNES::ppu.regs.window2_invert[5]; },
288 [](uint64_t v) { SNES::ppu.regs.window2_invert[5] = v; }, true},
289 {"ppu_bg_enabled[0]", []() -> uint64_t { return SNES::ppu.regs.bg_enabled[0]; },
290 [](uint64_t v) { SNES::ppu.regs.bg_enabled[0] = v; }, true},
291 {"ppu_bg_enabled[1]", []() -> uint64_t { return SNES::ppu.regs.bg_enabled[1]; },
292 [](uint64_t v) { SNES::ppu.regs.bg_enabled[1] = v; }, true},
293 {"ppu_bg_enabled[2]", []() -> uint64_t { return SNES::ppu.regs.bg_enabled[2]; },
294 [](uint64_t v) { SNES::ppu.regs.bg_enabled[2] = v; }, true},
295 {"ppu_bg_enabled[3]", []() -> uint64_t { return SNES::ppu.regs.bg_enabled[3]; },
296 [](uint64_t v) { SNES::ppu.regs.bg_enabled[3] = v; }, true},
297 {"ppu_bg_enabled[4]", []() -> uint64_t { return SNES::ppu.regs.bg_enabled[4]; },
298 [](uint64_t v) { SNES::ppu.regs.bg_enabled[4] = v; }, true},
299 {"ppu_bgsub_enabled[0]", []() -> uint64_t { return SNES::ppu.regs.bgsub_enabled[0]; },
300 [](uint64_t v) { SNES::ppu.regs.bgsub_enabled[0] = v; }, true},
301 {"ppu_bgsub_enabled[1]", []() -> uint64_t { return SNES::ppu.regs.bgsub_enabled[1]; },
302 [](uint64_t v) { SNES::ppu.regs.bgsub_enabled[1] = v; }, true},
303 {"ppu_bgsub_enabled[2]", []() -> uint64_t { return SNES::ppu.regs.bgsub_enabled[2]; },
304 [](uint64_t v) { SNES::ppu.regs.bgsub_enabled[2] = v; }, true},
305 {"ppu_bgsub_enabled[3]", []() -> uint64_t { return SNES::ppu.regs.bgsub_enabled[3]; },
306 [](uint64_t v) { SNES::ppu.regs.bgsub_enabled[3] = v; }, true},
307 {"ppu_bgsub_enabled[4]", []() -> uint64_t { return SNES::ppu.regs.bgsub_enabled[4]; },
308 [](uint64_t v) { SNES::ppu.regs.bgsub_enabled[4] = v; }, true},
309 {"ppu_window_enabled[0]", []() -> uint64_t { return SNES::ppu.regs.window_enabled[0]; },
310 [](uint64_t v) { SNES::ppu.regs.window_enabled[0] = v; }, true},
311 {"ppu_window_enabled[1]", []() -> uint64_t { return SNES::ppu.regs.window_enabled[1]; },
312 [](uint64_t v) { SNES::ppu.regs.window_enabled[1] = v; }, true},
313 {"ppu_window_enabled[2]", []() -> uint64_t { return SNES::ppu.regs.window_enabled[2]; },
314 [](uint64_t v) { SNES::ppu.regs.window_enabled[2] = v; }, true},
315 {"ppu_window_enabled[3]", []() -> uint64_t { return SNES::ppu.regs.window_enabled[3]; },
316 [](uint64_t v) { SNES::ppu.regs.window_enabled[3] = v; }, true},
317 {"ppu_window_enabled[4]", []() -> uint64_t { return SNES::ppu.regs.window_enabled[4]; },
318 [](uint64_t v) { SNES::ppu.regs.window_enabled[4] = v; }, true},
319 {"ppu_sub_window_enabled[0]", []() -> uint64_t { return SNES::ppu.regs.sub_window_enabled[0]; },
320 [](uint64_t v) { SNES::ppu.regs.sub_window_enabled[0] = v; }, true},
321 {"ppu_sub_window_enabled[1]", []() -> uint64_t { return SNES::ppu.regs.sub_window_enabled[1]; },
322 [](uint64_t v) { SNES::ppu.regs.sub_window_enabled[1] = v; }, true},
323 {"ppu_sub_window_enabled[2]", []() -> uint64_t { return SNES::ppu.regs.sub_window_enabled[2]; },
324 [](uint64_t v) { SNES::ppu.regs.sub_window_enabled[2] = v; }, true},
325 {"ppu_sub_window_enabled[3]", []() -> uint64_t { return SNES::ppu.regs.sub_window_enabled[3]; },
326 [](uint64_t v) { SNES::ppu.regs.sub_window_enabled[3] = v; }, true},
327 {"ppu_sub_window_enabled[4]", []() -> uint64_t { return SNES::ppu.regs.sub_window_enabled[4]; },
328 [](uint64_t v) { SNES::ppu.regs.sub_window_enabled[4] = v; }, true},
329 {"ppu_addsub_mode", []() -> uint64_t { return SNES::ppu.regs.addsub_mode; },
330 [](uint64_t v) { SNES::ppu.regs.addsub_mode = v; }, true},
331 {"ppu_direct_color", []() -> uint64_t { return SNES::ppu.regs.direct_color; },
332 [](uint64_t v) { SNES::ppu.regs.direct_color = v; }, true},
333 {"ppu_color_mode", []() -> uint64_t { return SNES::ppu.regs.color_mode; },
334 [](uint64_t v) { SNES::ppu.regs.color_mode = v; }, true},
335 {"ppu_color_halve", []() -> uint64_t { return SNES::ppu.regs.color_halve; },
336 [](uint64_t v) { SNES::ppu.regs.color_halve = v; }, true},
337 {"ppu_color_enabled[0]", []() -> uint64_t { return SNES::ppu.regs.color_enabled[0]; },
338 [](uint64_t v) { SNES::ppu.regs.color_enabled[0] = v; }, true},
339 {"ppu_color_enabled[1]", []() -> uint64_t { return SNES::ppu.regs.color_enabled[1]; },
340 [](uint64_t v) { SNES::ppu.regs.color_enabled[1] = v; }, true},
341 {"ppu_color_enabled[2]", []() -> uint64_t { return SNES::ppu.regs.color_enabled[2]; },
342 [](uint64_t v) { SNES::ppu.regs.color_enabled[2] = v; }, true},
343 {"ppu_color_enabled[3]", []() -> uint64_t { return SNES::ppu.regs.color_enabled[3]; },
344 [](uint64_t v) { SNES::ppu.regs.color_enabled[3] = v; }, true},
345 {"ppu_color_enabled[4]", []() -> uint64_t { return SNES::ppu.regs.color_enabled[4]; },
346 [](uint64_t v) { SNES::ppu.regs.color_enabled[4] = v; }, true},
347 {"ppu_color_enabled[5]", []() -> uint64_t { return SNES::ppu.regs.color_enabled[5]; },
348 [](uint64_t v) { SNES::ppu.regs.color_enabled[5] = v; }, true},
349 {"ppu_mode7_extbg", []() -> uint64_t { return SNES::ppu.regs.mode7_extbg; },
350 [](uint64_t v) { SNES::ppu.regs.mode7_extbg = v; }, true},
351 {"ppu_pseudo_hires", []() -> uint64_t { return SNES::ppu.regs.pseudo_hires; },
352 [](uint64_t v) { SNES::ppu.regs.pseudo_hires = v; }, true},
353 {"ppu_overscan", []() -> uint64_t { return SNES::ppu.regs.overscan; },
354 [](uint64_t v) { SNES::ppu.regs.overscan = v; }, true},
355 {"ppu_oam_interlace", []() -> uint64_t { return SNES::ppu.regs.oam_interlace; },
356 [](uint64_t v) { SNES::ppu.regs.oam_interlace = v; }, true},
357 {"ppu_interlace", []() -> uint64_t { return SNES::ppu.regs.interlace; },
358 [](uint64_t v) { SNES::ppu.regs.interlace = v; }, true},
359 {"ppu_latch_hcounter", []() -> uint64_t { return SNES::ppu.regs.latch_hcounter; },
360 [](uint64_t v) { SNES::ppu.regs.latch_hcounter = v; }, true},
361 {"ppu_latch_vcounter", []() -> uint64_t { return SNES::ppu.regs.latch_vcounter; },
362 [](uint64_t v) { SNES::ppu.regs.latch_vcounter = v; }, true},
363 {"ppu_counters_latched", []() -> uint64_t { return SNES::ppu.regs.counters_latched; },
364 [](uint64_t v) { SNES::ppu.regs.counters_latched = v; }, true},
365 {"ppu_time_over", []() -> uint64_t { return SNES::ppu.regs.time_over; },
366 [](uint64_t v) { SNES::ppu.regs.time_over = v; }, true},
367 {"ppu_range_over", []() -> uint64_t { return SNES::ppu.regs.range_over; },
368 [](uint64_t v) { SNES::ppu.regs.range_over = v; }, true},
369 {"ppu_ppu1_mdr", []() -> uint64_t { return SNES::ppu.regs.ppu1_mdr; },
370 [](uint64_t v) { SNES::ppu.regs.ppu1_mdr = v; }},
371 {"ppu_ppu2_mdr", []() -> uint64_t { return SNES::ppu.regs.ppu2_mdr; },
372 [](uint64_t v) { SNES::ppu.regs.ppu2_mdr = v; }},
373 {"ppu_bg_y[0]", []() -> uint64_t { return SNES::ppu.regs.bg_y[0]; },
374 [](uint64_t v) { SNES::ppu.regs.bg_y[0] = v; }},
375 {"ppu_bg_y[1]", []() -> uint64_t { return SNES::ppu.regs.bg_y[1]; },
376 [](uint64_t v) { SNES::ppu.regs.bg_y[1] = v; }},
377 {"ppu_bg_y[2]", []() -> uint64_t { return SNES::ppu.regs.bg_y[2]; },
378 [](uint64_t v) { SNES::ppu.regs.bg_y[2] = v; }},
379 {"ppu_bg_y[3]", []() -> uint64_t { return SNES::ppu.regs.bg_y[3]; },
380 [](uint64_t v) { SNES::ppu.regs.bg_y[3] = v; }},
381 {"ppu_ioamaddr", []() -> uint64_t { return SNES::ppu.regs.ioamaddr; },
382 [](uint64_t v) { SNES::ppu.regs.ioamaddr = v; }},
383 {"ppu_icgramaddr", []() -> uint64_t { return SNES::ppu.regs.icgramaddr; },
384 [](uint64_t v) { SNES::ppu.regs.icgramaddr = v; }},
385 {"ppu_display_brightness", []() -> uint64_t { return SNES::ppu.regs.display_brightness; },
386 [](uint64_t v) { SNES::ppu.regs.display_brightness = v; }},
387 {"ppu_oam_basesize", []() -> uint64_t { return SNES::ppu.regs.oam_basesize; },
388 [](uint64_t v) { SNES::ppu.regs.oam_basesize = v; }},
389 {"ppu_oam_nameselect", []() -> uint64_t { return SNES::ppu.regs.oam_nameselect; },
390 [](uint64_t v) { SNES::ppu.regs.oam_nameselect = v; }},
391 {"ppu_oam_tdaddr", []() -> uint64_t { return SNES::ppu.regs.oam_tdaddr; },
392 [](uint64_t v) { SNES::ppu.regs.oam_tdaddr = v; }},
393 {"ppu_oam_baseaddr", []() -> uint64_t { return SNES::ppu.regs.oam_baseaddr; },
394 [](uint64_t v) { SNES::ppu.regs.oam_baseaddr = v; }},
395 {"ppu_oam_addr", []() -> uint64_t { return SNES::ppu.regs.oam_addr; },
396 [](uint64_t v) { SNES::ppu.regs.oam_addr = v; }},
397 {"ppu_oam_firstsprite", []() -> uint64_t { return SNES::ppu.regs.oam_firstsprite; },
398 [](uint64_t v) { SNES::ppu.regs.oam_firstsprite = v; }},
399 {"ppu_oam_latchdata", []() -> uint64_t { return SNES::ppu.regs.oam_latchdata; },
400 [](uint64_t v) { SNES::ppu.regs.oam_latchdata = v; }},
401 {"ppu_bg_mode", []() -> uint64_t { return SNES::ppu.regs.bg_mode; },
402 [](uint64_t v) { SNES::ppu.regs.bg_mode = v; }},
403 {"ppu_mosaic_size", []() -> uint64_t { return SNES::ppu.regs.mosaic_size; },
404 [](uint64_t v) { SNES::ppu.regs.mosaic_size = v; }},
405 {"ppu_mosaic_countdown", []() -> uint64_t { return SNES::ppu.regs.mosaic_countdown; },
406 [](uint64_t v) { SNES::ppu.regs.mosaic_countdown = v; }},
407 {"ppu_bg_scaddr[0]", []() -> uint64_t { return SNES::ppu.regs.bg_scaddr[0]; },
408 [](uint64_t v) { SNES::ppu.regs.bg_scaddr[0] = v; }},
409 {"ppu_bg_scaddr[1]", []() -> uint64_t { return SNES::ppu.regs.bg_scaddr[1]; },
410 [](uint64_t v) { SNES::ppu.regs.bg_scaddr[1] = v; }},
411 {"ppu_bg_scaddr[2]", []() -> uint64_t { return SNES::ppu.regs.bg_scaddr[2]; },
412 [](uint64_t v) { SNES::ppu.regs.bg_scaddr[2] = v; }},
413 {"ppu_bg_scaddr[3]", []() -> uint64_t { return SNES::ppu.regs.bg_scaddr[3]; },
414 [](uint64_t v) { SNES::ppu.regs.bg_scaddr[3] = v; }},
415 {"ppu_bg_scsize[0]", []() -> uint64_t { return SNES::ppu.regs.bg_scsize[0]; },
416 [](uint64_t v) { SNES::ppu.regs.bg_scsize[0] = v; }},
417 {"ppu_bg_scsize[1]", []() -> uint64_t { return SNES::ppu.regs.bg_scsize[1]; },
418 [](uint64_t v) { SNES::ppu.regs.bg_scsize[1] = v; }},
419 {"ppu_bg_scsize[2]", []() -> uint64_t { return SNES::ppu.regs.bg_scsize[2]; },
420 [](uint64_t v) { SNES::ppu.regs.bg_scsize[2] = v; }},
421 {"ppu_bg_scsize[3]", []() -> uint64_t { return SNES::ppu.regs.bg_scsize[3]; },
422 [](uint64_t v) { SNES::ppu.regs.bg_scsize[3] = v; }},
423 {"ppu_bg_tdaddr[0]", []() -> uint64_t { return SNES::ppu.regs.bg_tdaddr[0]; },
424 [](uint64_t v) { SNES::ppu.regs.bg_tdaddr[0] = v; }},
425 {"ppu_bg_tdaddr[1]", []() -> uint64_t { return SNES::ppu.regs.bg_tdaddr[1]; },
426 [](uint64_t v) { SNES::ppu.regs.bg_tdaddr[1] = v; }},
427 {"ppu_bg_tdaddr[2]", []() -> uint64_t { return SNES::ppu.regs.bg_tdaddr[2]; },
428 [](uint64_t v) { SNES::ppu.regs.bg_tdaddr[2] = v; }},
429 {"ppu_bg_tdaddr[3]", []() -> uint64_t { return SNES::ppu.regs.bg_tdaddr[3]; },
430 [](uint64_t v) { SNES::ppu.regs.bg_tdaddr[3] = v; }},
431 {"ppu_bg_ofslatch", []() -> uint64_t { return SNES::ppu.regs.bg_ofslatch; },
432 [](uint64_t v) { SNES::ppu.regs.bg_ofslatch = v; }},
433 {"ppu_m7_hofs", []() -> uint64_t { return SNES::ppu.regs.m7_hofs; },
434 [](uint64_t v) { SNES::ppu.regs.m7_hofs = v; }},
435 {"ppu_m7_vofs", []() -> uint64_t { return SNES::ppu.regs.m7_vofs; },
436 [](uint64_t v) { SNES::ppu.regs.m7_vofs = v; }},
437 {"ppu_bg_hofs[0]", []() -> uint64_t { return SNES::ppu.regs.bg_hofs[0]; },
438 [](uint64_t v) { SNES::ppu.regs.bg_hofs[0] = v; }},
439 {"ppu_bg_hofs[1]", []() -> uint64_t { return SNES::ppu.regs.bg_hofs[1]; },
440 [](uint64_t v) { SNES::ppu.regs.bg_hofs[1] = v; }},
441 {"ppu_bg_hofs[2]", []() -> uint64_t { return SNES::ppu.regs.bg_hofs[2]; },
442 [](uint64_t v) { SNES::ppu.regs.bg_hofs[2] = v; }},
443 {"ppu_bg_hofs[3]", []() -> uint64_t { return SNES::ppu.regs.bg_hofs[3]; },
444 [](uint64_t v) { SNES::ppu.regs.bg_hofs[3] = v; }},
445 {"ppu_bg_vofs[0]", []() -> uint64_t { return SNES::ppu.regs.bg_vofs[0]; },
446 [](uint64_t v) { SNES::ppu.regs.bg_vofs[0] = v; }},
447 {"ppu_bg_vofs[1]", []() -> uint64_t { return SNES::ppu.regs.bg_vofs[1]; },
448 [](uint64_t v) { SNES::ppu.regs.bg_vofs[1] = v; }},
449 {"ppu_bg_vofs[2]", []() -> uint64_t { return SNES::ppu.regs.bg_vofs[2]; },
450 [](uint64_t v) { SNES::ppu.regs.bg_vofs[2] = v; }},
451 {"ppu_bg_vofs[3]", []() -> uint64_t { return SNES::ppu.regs.bg_vofs[3]; },
452 [](uint64_t v) { SNES::ppu.regs.bg_vofs[3] = v; }},
453 {"ppu_vram_mapping", []() -> uint64_t { return SNES::ppu.regs.vram_mapping; },
454 [](uint64_t v) { SNES::ppu.regs.vram_mapping = v; }},
455 {"ppu_vram_incsize", []() -> uint64_t { return SNES::ppu.regs.vram_incsize; },
456 [](uint64_t v) { SNES::ppu.regs.vram_incsize = v; }},
457 {"ppu_vram_addr", []() -> uint64_t { return SNES::ppu.regs.vram_addr; },
458 [](uint64_t v) { SNES::ppu.regs.vram_addr = v; }},
459 {"ppu_mode7_repeat", []() -> uint64_t { return SNES::ppu.regs.mode7_repeat; },
460 [](uint64_t v) { SNES::ppu.regs.mode7_repeat = v; }},
461 {"ppu_m7_latch", []() -> uint64_t { return SNES::ppu.regs.m7_latch; },
462 [](uint64_t v) { SNES::ppu.regs.m7_latch = v; }},
463 {"ppu_m7a", []() -> uint64_t { return SNES::ppu.regs.m7a; },
464 [](uint64_t v) { SNES::ppu.regs.m7a = v; }},
465 {"ppu_m7b", []() -> uint64_t { return SNES::ppu.regs.m7b; },
466 [](uint64_t v) { SNES::ppu.regs.m7b = v; }},
467 {"ppu_m7c", []() -> uint64_t { return SNES::ppu.regs.m7c; },
468 [](uint64_t v) { SNES::ppu.regs.m7c = v; }},
469 {"ppu_m7d", []() -> uint64_t { return SNES::ppu.regs.m7d; },
470 [](uint64_t v) { SNES::ppu.regs.m7d = v; }},
471 {"ppu_m7x", []() -> uint64_t { return SNES::ppu.regs.m7x; },
472 [](uint64_t v) { SNES::ppu.regs.m7x = v; }},
473 {"ppu_m7y", []() -> uint64_t { return SNES::ppu.regs.m7y; },
474 [](uint64_t v) { SNES::ppu.regs.m7y = v; }},
475 {"ppu_cgram_addr", []() -> uint64_t { return SNES::ppu.regs.cgram_addr; },
476 [](uint64_t v) { SNES::ppu.regs.cgram_addr = v; }},
477 {"ppu_cgram_latchdata", []() -> uint64_t { return SNES::ppu.regs.cgram_latchdata; },
478 [](uint64_t v) { SNES::ppu.regs.cgram_latchdata = v; }},
479 {"ppu_window1_left", []() -> uint64_t { return SNES::ppu.regs.window1_left; },
480 [](uint64_t v) { SNES::ppu.regs.window1_left = v; }},
481 {"ppu_window1_right", []() -> uint64_t { return SNES::ppu.regs.window1_right; },
482 [](uint64_t v) { SNES::ppu.regs.window1_right = v; }},
483 {"ppu_window2_left", []() -> uint64_t { return SNES::ppu.regs.window2_left; },
484 [](uint64_t v) { SNES::ppu.regs.window2_left = v; }},
485 {"ppu_window2_right", []() -> uint64_t { return SNES::ppu.regs.window2_right; },
486 [](uint64_t v) { SNES::ppu.regs.window2_right = v; }},
487 {"ppu_window_mask[0]", []() -> uint64_t { return SNES::ppu.regs.window_mask[0]; },
488 [](uint64_t v) { SNES::ppu.regs.window_mask[0] = v; }},
489 {"ppu_window_mask[1]", []() -> uint64_t { return SNES::ppu.regs.window_mask[1]; },
490 [](uint64_t v) { SNES::ppu.regs.window_mask[1] = v; }},
491 {"ppu_window_mask[2]", []() -> uint64_t { return SNES::ppu.regs.window_mask[2]; },
492 [](uint64_t v) { SNES::ppu.regs.window_mask[2] = v; }},
493 {"ppu_window_mask[3]", []() -> uint64_t { return SNES::ppu.regs.window_mask[3]; },
494 [](uint64_t v) { SNES::ppu.regs.window_mask[3] = v; }},
495 {"ppu_window_mask[4]", []() -> uint64_t { return SNES::ppu.regs.window_mask[4]; },
496 [](uint64_t v) { SNES::ppu.regs.window_mask[4] = v; }},
497 {"ppu_window_mask[5]", []() -> uint64_t { return SNES::ppu.regs.window_mask[5]; },
498 [](uint64_t v) { SNES::ppu.regs.window_mask[5] = v; }},
499 {"ppu_color_mask", []() -> uint64_t { return SNES::ppu.regs.color_mask; },
500 [](uint64_t v) { SNES::ppu.regs.color_mask = v; }},
501 {"ppu_colorsub_mask", []() -> uint64_t { return SNES::ppu.regs.colorsub_mask; },
502 [](uint64_t v) { SNES::ppu.regs.colorsub_mask = v; }},
503 {"ppu_color_r", []() -> uint64_t { return SNES::ppu.regs.color_r; },
504 [](uint64_t v) { SNES::ppu.regs.color_r = v; }},
505 {"ppu_color_g", []() -> uint64_t { return SNES::ppu.regs.color_g; },
506 [](uint64_t v) { SNES::ppu.regs.color_g = v; }},
507 {"ppu_color_b", []() -> uint64_t { return SNES::ppu.regs.color_b; },
508 [](uint64_t v) { SNES::ppu.regs.color_b = v; }},
509 {"ppu_color_rgb", []() -> uint64_t { return SNES::ppu.regs.color_rgb; },
510 [](uint64_t v) { SNES::ppu.regs.color_rgb = v; }},
511 {"ppu_scanlines", []() -> uint64_t { return SNES::ppu.regs.scanlines; },
512 [](uint64_t v) { SNES::ppu.regs.scanlines = v; }},
513 {"ppu_hcounter", []() -> uint64_t { return SNES::ppu.regs.hcounter; },
514 [](uint64_t v) { SNES::ppu.regs.hcounter = v; }},
515 {"ppu_vcounter", []() -> uint64_t { return SNES::ppu.regs.vcounter; },
516 [](uint64_t v) { SNES::ppu.regs.vcounter = v; }},
517 {"ppu_vram_readbuffer", []() -> uint64_t { return SNES::ppu.regs.vram_readbuffer; },
518 [](uint64_t v) { SNES::ppu.regs.vram_readbuffer = v; }},
519 {"ppu_oam_itemcount", []() -> uint64_t { return SNES::ppu.regs.oam_itemcount; },
520 [](uint64_t v) { SNES::ppu.regs.oam_itemcount = v; }},
521 {"ppu_oam_tilecount", []() -> uint64_t { return SNES::ppu.regs.oam_tilecount; },
522 [](uint64_t v) { SNES::ppu.regs.oam_tilecount = v; }},
523 #endif
524 //TODO: SMP registers, DSP registers, chip registers.
525 {NULL, NULL, NULL}
528 #include "ports.inc"
530 core_region region_auto{{"autodetect", "Autodetect", 1, 0, true, {178683, 10738636}, {0,1,2}}};
531 core_region region_pal{{"pal", "PAL", 0, 2, false, {6448, 322445}, {2}}};
532 core_region region_ntsc{{"ntsc", "NTSC", 0, 1, false, {178683, 10738636}, {1}}};
534 std::vector<core_setting_value_param> boolean_values = {{"0", "False", 0}, {"1", "True", 1}};
535 core_setting_group bsnes_settings = {
536 {"port1", "Port 1 Type", "gamepad", {
537 {"none", "None", 0},
538 {"gamepad", "Gamepad", 1},
539 {"gamepad16", "Gamepad (16-button)", 2},
540 {"ygamepad16", "Y-cabled gamepad (16-button)", 9},
541 {"multitap", "Multitap", 3},
542 {"multitap16", "Multitap (16-button)", 4},
543 {"mouse", "Mouse", 5}
545 {"port2", "Port 2 Type", "none", {
546 {"none", "None", 0},
547 {"gamepad", "Gamepad", 1},
548 {"gamepad16", "Gamepad (16-button)", 2},
549 {"ygamepad16", "Y-cabled gamepad (16-button)", 9},
550 {"multitap", "Multitap", 3},
551 {"multitap16", "Multitap (16-button)", 4},
552 {"mouse", "Mouse", 5},
553 {"superscope", "Super Scope", 8},
554 {"justifier", "Justifier", 6},
555 {"justifiers", "2 Justifiers", 7}
557 {"hardreset", "Support hard resets", "0", boolean_values},
558 {"saveevery", "Emulate saving each frame", "0", boolean_values},
559 {"radominit", "Random initial state", "0", boolean_values},
560 {"compact", "Don't support delayed resets", "0", boolean_values},
561 #ifdef BSNES_SUPPORTS_ALT_TIMINGS
562 {"alttimings", "Alternate poll timings", "0", boolean_values},
563 #endif
564 #ifdef BSNES_SUPPORTS_BUS_FIXES
565 {"busfixes", "System bus fixes", "0", boolean_values},
566 #endif
567 #ifdef BSNES_SUPPORTS_MOUSE_SPEED_FIX
568 {"mousespeed", "Support mouse speeds", "0", boolean_values},
569 #endif
572 ////////////////// PORTS COMMON ///////////////////
573 portctrl::type* index_to_ptype[] = {
574 &none, &gamepad, &gamepad16, &multitap, &multitap16, &mouse, &justifier, &justifiers, &superscope,
575 &ygamepad16
577 unsigned index_to_bsnes_type[] = {
578 SNES_DEVICE_NONE, SNES_DEVICE_JOYPAD, SNES_DEVICE_JOYPAD, SNES_DEVICE_MULTITAP, SNES_DEVICE_MULTITAP,
579 SNES_DEVICE_MOUSE, SNES_DEVICE_JUSTIFIER, SNES_DEVICE_JUSTIFIERS, SNES_DEVICE_SUPER_SCOPE,
580 SNES_DEVICE_JOYPAD
583 bool port_is_ycable[2];
585 void snesdbg_on_break();
586 void snesdbg_on_trace();
587 std::pair<int, uint64_t> recognize_address(uint64_t addr);
589 class my_interfaced : public SNES::Interface
591 string path(SNES::Cartridge::Slot slot, const string &hint)
593 return "./";
597 void basic_init()
599 static bool done = false;
600 if(done)
601 return;
602 done = true;
603 static my_interfaced i;
604 SNES::interface = &i;
607 core_type* internal_rom = NULL;
609 template<bool(*T)(const char*,const unsigned char*, unsigned)>
610 bool load_rom_X1(core_romimage* img)
612 return T(img[0].markup, img[0].data, img[0].size);
615 template<bool(*T)(const char*,const unsigned char*, unsigned, const char*,const unsigned char*, unsigned)>
616 bool load_rom_X2(core_romimage* img)
618 return T(img[0].markup, img[0].data, img[0].size,
619 img[1].markup, img[1].data, img[1].size);
622 template<bool(*T)(const char*,const unsigned char*, unsigned, const char*,const unsigned char*, unsigned,
623 const char*,const unsigned char*, unsigned)>
624 bool load_rom_X3(core_romimage* img)
626 return T(img[0].markup, img[0].data, img[0].size,
627 img[1].markup, img[1].data, img[1].size,
628 img[2].markup, img[2].data, img[2].size);
632 int load_rom(core_type* ctype, core_romimage* img, std::map<std::string, std::string>& settings,
633 uint64_t secs, uint64_t subsecs, bool(*fun)(core_romimage*))
635 std::map<std::string, std::string> _settings = settings;
636 bsnes_settings.fill_defaults(_settings);
637 signed type1 = bsnes_settings.ivalue_to_index(_settings, "port1");
638 signed type2 = bsnes_settings.ivalue_to_index(_settings, "port2");
639 signed hreset = bsnes_settings.ivalue_to_index(_settings, "hardreset");
640 signed compact = bsnes_settings.ivalue_to_index(_settings, "compact");
641 signed esave = bsnes_settings.ivalue_to_index(_settings, "saveevery");
642 signed irandom = bsnes_settings.ivalue_to_index(_settings, "radominit");
643 #ifdef BSNES_SUPPORTS_ALT_TIMINGS
644 signed ialttimings = bsnes_settings.ivalue_to_index(_settings, "alttimings");
645 #endif
646 #ifdef BSNES_SUPPORTS_BUS_FIXES
647 signed ibusfixes = bsnes_settings.ivalue_to_index(_settings, "busfixes");
648 #endif
649 #ifdef BSNES_SUPPORTS_MOUSE_SPEED_FIX
650 signed ispeedfix = bsnes_settings.ivalue_to_index(_settings, "mousespeed");
651 #endif
653 basic_init();
654 snes_term();
655 snes_unload_cartridge();
656 SNES::config.random = (irandom != 0);
657 save_every_frame = (esave != 0);
658 support_hreset = (hreset != 0 || compact != 0);
659 support_dreset = (compact == 0);
660 SNES::config.expansion_port = SNES::System::ExpansionPortDevice::None;
661 #ifdef BSNES_SUPPORTS_ALT_TIMINGS
662 SNES::config.cpu.alt_poll_timings = (ialttimings != 0);
663 #endif
664 #ifdef BSNES_SUPPORTS_BUS_FIXES
665 SNES::config.cpu.bus_fixes = (ibusfixes != 0);
666 #endif
667 #ifdef BSNES_SUPPORTS_MOUSE_SPEED_FIX
668 SNES::config.mouse_speed_fix = (ispeedfix != 0);
669 #endif
670 bool r = fun(img);
671 if(r) {
672 internal_rom = ctype;
673 snes_set_controller_port_device(false, index_to_bsnes_type[type1]);
674 snes_set_controller_port_device(true, index_to_bsnes_type[type2]);
675 port_is_ycable[0] = (type1 == 9);
676 port_is_ycable[1] = (type2 == 9);
677 have_saved_this_frame = false;
678 do_reset_flag = -1;
679 if(ecore_callbacks)
680 ecore_callbacks->action_state_updated();
681 //Save initial state, so we can restore it later.
682 serializer s = SNES::system.serialize();
683 init_savestate.resize(s.size());
684 memcpy(&init_savestate[0], s.data(), s.size());
686 return r ? 0 : -1;
689 controller_set bsnes_controllerconfig(std::map<std::string, std::string>& settings)
691 std::map<std::string, std::string> _settings = settings;
692 bsnes_settings.fill_defaults(_settings);
693 signed type1 = bsnes_settings.ivalue_to_index(_settings, "port1");
694 signed type2 = bsnes_settings.ivalue_to_index(_settings, "port2");
695 signed hreset = bsnes_settings.ivalue_to_index(_settings, "hardreset");
696 signed compact = bsnes_settings.ivalue_to_index(_settings, "compact");
697 controller_set r;
698 if(compact)
699 r.ports.push_back(&psystem_compact);
700 else if(hreset)
701 r.ports.push_back(&psystem_hreset);
702 else
703 r.ports.push_back(&psystem);
704 r.ports.push_back(index_to_ptype[type1]);
705 r.ports.push_back(index_to_ptype[type2]);
706 unsigned p1controllers = r.ports[1]->controller_info->controllers.size();
707 unsigned p2controllers = r.ports[2]->controller_info->controllers.size();
708 r.logical_map.resize(p1controllers + p2controllers);
709 if(p1controllers == 4) {
710 r.logical_map[0] = std::make_pair(1, 0);
711 for(size_t j = 0; j < p2controllers; j++)
712 r.logical_map[j + 1] = std::make_pair(2U, j);
713 for(size_t j = 1; j < p1controllers; j++)
714 r.logical_map[j + p2controllers] = std::make_pair(1U, j);
715 } else {
716 for(size_t j = 0; j < p1controllers; j++)
717 r.logical_map[j] = std::make_pair(1, j);
718 for(size_t j = 0; j < p2controllers; j++)
719 r.logical_map[j + p1controllers] = std::make_pair(2U, j);
721 return r;
724 class my_interface : public SNES::Interface
726 string path(SNES::Cartridge::Slot slot, const string &hint)
728 const char* _hint = hint;
729 std::string _hint2 = _hint;
730 std::string fwp = ecore_callbacks->get_firmware_path();
731 regex_results r;
732 std::string msubase = ecore_callbacks->get_base_path();
733 if(regex_match(".*\\.sfc", msubase))
734 msubase = msubase.substr(0, msubase.length() - 4);
736 if(_hint2 == "msu1.rom" || _hint2 == ".msu") {
737 //MSU-1 main ROM.
738 std::string x = msubase + ".msu";
739 messages << "MSU main data file: " << x << std::endl;
740 return x.c_str();
742 if(r = regex("(track)?(-([0-9]+)\\.pcm)", _hint2)) {
743 //MSU track.
744 std::string x = msubase + r[2];
745 messages << "MSU track " << r[3] << "': " << x << std::endl;
746 return x.c_str();
748 std::string finalpath = fwp + "/" + _hint2;
749 return finalpath.c_str();
752 time_t currentTime()
754 return ecore_callbacks->get_time();
757 time_t randomSeed()
759 return ecore_callbacks->get_randomseed();
762 void notifyLatched()
764 std::list<std::string> dummy;
765 ecore_callbacks->notify_latch(dummy);
768 void videoRefresh(const uint32_t* data, bool hires, bool interlace, bool overscan);
770 void audioSample(int16_t l_sample, int16_t r_sample)
772 uint16_t _l = l_sample;
773 uint16_t _r = r_sample;
774 //Don't overflow buffers if bsnes goes bonkers.
775 if(soundbuf_fill >= sizeof(soundbuf) / sizeof(soundbuf[0])) return;
776 soundbuf[soundbuf_fill++] = l_sample;
777 soundbuf[soundbuf_fill++] = r_sample;
778 //The SMP emits a sample every 768 ticks of its clock. Use this in order to keep track of
779 //time.
780 ecore_callbacks->timer_tick(768, SNES::system.apu_frequency());
783 int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id)
785 if(port_is_ycable[port ? 1 : 0]) {
786 int16_t bit0 = ecore_callbacks->get_input(port ? 2 : 1, 0, id);
787 int16_t bit1 = ecore_callbacks->get_input(port ? 2 : 1, 1, id);
788 return bit0 + 2 * bit1;
790 int16_t offset = 0;
791 //The superscope/justifier handling is nuts.
792 if(port && SNES::input.port2) {
793 SNES::SuperScope* ss = dynamic_cast<SNES::SuperScope*>(SNES::input.port2);
794 SNES::Justifier* js = dynamic_cast<SNES::Justifier*>(SNES::input.port2);
795 if(ss && index == 0) {
796 if(id == 0)
797 offset = ss->x;
798 if(id == 1)
799 offset = ss->y;
801 if(js && index == 0) {
802 if(id == 0)
803 offset = js->player1.x;
804 if(id == 1)
805 offset = js->player1.y;
807 if(js && js->chained && index == 1) {
808 if(id == 0)
809 offset = js->player2.x;
810 if(id == 1)
811 offset = js->player2.y;
814 return ecore_callbacks->get_input(port ? 2 : 1, index, id) - offset;
816 } my_interface_obj;
818 bool trace_fn()
820 #ifdef BSNES_HAS_DEBUGGER
821 if(trace_counter && !--trace_counter) {
822 //Trace counter did transition 1->0. Call the hook.
823 snesdbg_on_trace();
825 if(trace_cpu_enable) {
826 char buffer[1024];
827 SNES::cpu.disassemble_opcode(buffer, SNES::cpu.regs.pc);
828 ecore_callbacks->memory_trace(0, buffer, true);
830 return false;
831 #endif
833 bool smp_trace_fn()
835 #ifdef BSNES_HAS_DEBUGGER
836 if(trace_smp_enable) {
837 nall::string _disasm = SNES::smp.disassemble_opcode(SNES::smp.regs.pc);
838 std::string disasm(_disasm, _disasm.length());
839 ecore_callbacks->memory_trace(1, disasm.c_str(), true);
841 return false;
842 #endif
844 void sa1_trace_fn()
846 #ifdef BSNES_HAS_DEBUGGER
847 if(trace_sa1_enable) {
848 char buffer[1024];
849 SNES::sa1.disassemble_opcode(buffer, SNES::sa1.regs.pc);
850 ecore_callbacks->memory_trace(2, buffer, true);
852 #endif
854 void cpu_dma_fn(const char* buf)
856 if(trace_cpu_enable) {
857 ecore_callbacks->memory_trace(0, buf, false);
860 bool delayreset_fn()
862 trace_fn(); //Call this also.
863 if(delayreset_cycles_run == delayreset_cycles_target || video_refresh_done)
864 return true;
865 delayreset_cycles_run++;
866 return false;
870 bool trace_enabled()
872 return (trace_counter || !!trace_cpu_enable);
875 void update_trace_hook_state()
877 if(forced_hook)
878 return;
879 #ifdef BSNES_HAS_DEBUGGER
880 if(!trace_enabled())
881 SNES::cpu.step_event = nall::function<bool()>();
882 else
883 SNES::cpu.step_event = trace_fn;
884 if(!trace_smp_enable)
885 SNES::smp.step_event = nall::function<bool()>();
886 else
887 SNES::smp.step_event = smp_trace_fn;
888 #ifdef BSNES_SUPPORTS_TRACE_SA1
889 if(!trace_sa1_enable)
890 SNES::sa1.step_event = nall::function<void()>();
891 else
892 SNES::sa1.step_event = sa1_trace_fn;
893 SNES::sa1.trace_enabled = trace_sa1_enable;
894 #endif
895 #ifdef BSNES_SUPPORTS_DMA_TRACE
896 if(!trace_enabled())
897 SNES::cpu.dma_trace_fn = nall::function<void(const char*)>();
898 else
899 SNES::cpu.dma_trace_fn = cpu_dma_fn;
900 #endif
901 #endif
904 std::string sram_name(const nall::string& _id, SNES::Cartridge::Slot slotname)
906 std::string id(_id, _id.length());
907 //Fixup name change by bsnes v087...
908 if(id == "bsx.ram")
909 id = ".bss";
910 if(id == "bsx.psram")
911 id = ".bsp";
912 if(id == "program.rtc")
913 id = ".rtc";
914 if(id == "upd96050.ram")
915 id = ".dsp";
916 if(id == "program.ram")
917 id = ".srm";
918 if(slotname == SNES::Cartridge::Slot::SufamiTurboA)
919 return "slota." + id.substr(1);
920 if(slotname == SNES::Cartridge::Slot::SufamiTurboB)
921 return "slotb." + id.substr(1);
922 return id.substr(1);
925 uint8_t snes_bus_iospace_read(uint64_t offset)
927 disable_breakpoints = true;
928 #ifdef BSNES_SUPPORTS_ADV_BREAKPOINTS
929 uint8_t val = SNES::bus.read(offset, false);
930 #else
931 uint8_t val = SNES::bus.read(offset);
932 #endif
933 disable_breakpoints = false;
934 return val;
937 void snes_bus_iospace_write(uint64_t offset, uint8_t data)
939 disable_breakpoints = true;
940 SNES::bus.write(offset, data);
941 disable_breakpoints = false;
944 uint8_t ptrtable_iospace_read(uint64_t offset)
946 uint16_t entry = offset >> 4;
947 if(!ptrmap.count(entry))
948 return 0;
949 uint64_t val = ((offset & 15) < 8) ? ptrmap[entry].first : ptrmap[entry].second;
950 uint8_t byte = offset & 7;
951 //These things are always little-endian.
952 return (val >> (8 * byte));
955 void create_region(std::list<core_vma_info>& inf, const std::string& name, uint64_t base, uint64_t size,
956 uint8_t (*readfn)(uint64_t offset), void (*writefn)(uint64_t offset, uint8_t data))
957 throw(std::bad_alloc)
959 if(size == 0)
960 return;
961 core_vma_info i;
962 i.name = name;
963 i.base = base;
964 i.size = size;
965 i.endian = -1;
966 i.special = true;
967 i.readonly = (writefn == NULL);
968 i.read = readfn;
969 i.write = writefn;
970 inf.push_back(i);
973 void create_region(std::list<core_vma_info>& inf, const std::string& name, uint64_t base, uint8_t* memory,
974 uint64_t size, bool readonly, bool native_endian = false) throw(std::bad_alloc)
976 if(size == 0)
977 return;
978 core_vma_info i;
979 i.name = name;
980 i.base = base;
981 i.size = size;
982 i.backing_ram = memory;
983 i.readonly = readonly;
984 i.endian = native_endian ? 0 : -1;
985 i.volatile_flag = true;
986 //SRAMs aren't volatile.
987 for(unsigned j = 0; j < SNES::cartridge.nvram.size(); j++) {
988 SNES::Cartridge::NonVolatileRAM& r = SNES::cartridge.nvram[j];
989 if(r.data == memory)
990 i.volatile_flag = false;
992 inf.push_back(i);
995 void create_region(std::list<core_vma_info>& inf, const std::string& name, uint64_t base,
996 SNES::MappedRAM& memory, bool readonly, bool native_endian = false) throw(std::bad_alloc)
998 create_region(inf, name, base, memory.data(), memory.size(), readonly, native_endian);
1001 void map_internal(std::list<core_vma_info>& inf, const std::string& name, uint16_t index, void* memory,
1002 size_t memsize)
1004 ptrmap[index] = std::make_pair(reinterpret_cast<uint64_t>(memory), static_cast<uint64_t>(memsize));
1005 create_region(inf, name, 0x101000000 + index * 0x1000000, reinterpret_cast<uint8_t*>(memory),
1006 memsize, true, true);
1009 std::list<core_vma_info> get_VMAlist();
1010 std::set<std::string> bsnes_srams()
1012 std::set<std::string> r;
1013 if(!internal_rom)
1014 return r;
1015 for(unsigned i = 0; i < SNES::cartridge.nvram.size(); i++) {
1016 SNES::Cartridge::NonVolatileRAM& s = SNES::cartridge.nvram[i];
1017 r.insert(sram_name(s.id, s.slot));
1019 return r;
1022 uint64_t translate_class_address(uint8_t clazz, unsigned offset)
1024 switch(clazz) {
1025 case 1: //ROM.
1026 return 0x80000000 + offset;
1027 case 2: //SRAM.
1028 return 0x10000000 + offset;
1029 case 3: //WRAM
1030 return 0x007E0000 + offset;
1031 case 6: //SA1IRAM
1032 return 0x00040000 + offset;
1033 case 8: //SufamiTurboA ROM.
1034 return 0x90000000 + offset;
1035 case 9: //SufamiTurboB ROM.
1036 return 0xA0000000 + offset;
1037 case 10: //SufamiTurboA RAM.
1038 return 0x20000000 + offset;
1039 case 11: //SufamiTurboB RAM.
1040 return 0x30000000 + offset;
1041 case 12: //BSX flash.
1042 return 0x90000000 + offset;
1043 #ifdef BSNES_SUPPORTS_ADV_BREAKPOINTS_PPU
1044 #ifdef BSNES_IS_COMPAT
1045 case 13: //VRAM.
1046 return 0x00010000 + offset;
1047 case 14: //OAM.
1048 return 0x00020000 + offset;
1049 case 15: //GCRAM.
1050 return 0x00021000 + offset;
1051 #endif
1052 case 16: //APURAM
1053 return 0x00000000 + offset;
1054 #endif
1055 default: //Other, including bus.
1056 return 0xFFFFFFFFFFFFFFFFULL;
1060 void bsnes_debug_read(uint8_t clazz, unsigned offset, unsigned addr, uint8_t val, bool exec)
1062 if(disable_breakpoints) return;
1063 uint64_t _addr = translate_class_address(clazz, offset);
1064 if(_addr != 0xFFFFFFFFFFFFFFFFULL) {
1065 if(exec)
1066 ecore_callbacks->memory_execute(_addr, 0);
1067 else
1068 ecore_callbacks->memory_read(_addr, val);
1070 if(exec)
1071 ecore_callbacks->memory_execute(0x1000000 + addr, 0);
1072 else
1073 ecore_callbacks->memory_read(0x1000000 + addr, val);
1076 void bsnes_debug_read2(uint8_t clazz, unsigned offset, uint8_t val, bool exec)
1078 if(disable_breakpoints) return;
1079 uint64_t _addr = translate_class_address(clazz, offset);
1080 if(_addr != 0xFFFFFFFFFFFFFFFFULL) {
1081 //SMP uses this, so CPU#1.
1082 if(exec)
1083 ecore_callbacks->memory_execute(_addr, 1);
1084 else
1085 ecore_callbacks->memory_read(_addr, val);
1089 void bsnes_debug_read3(uint8_t clazz, unsigned offset, uint8_t val)
1091 if(disable_breakpoints) return;
1092 uint64_t _addr = translate_class_address(clazz, offset);
1093 if(_addr != 0xFFFFFFFFFFFFFFFFULL) {
1094 ecore_callbacks->memory_read(_addr, val);
1098 void bsnes_debug_write(uint8_t clazz, unsigned offset, unsigned addr, uint8_t val)
1100 if(disable_breakpoints) return;
1101 uint64_t _addr = translate_class_address(clazz, offset);
1102 if(_addr != 0xFFFFFFFFFFFFFFFFULL)
1103 ecore_callbacks->memory_write(_addr, val);
1104 ecore_callbacks->memory_write(0x1000000 + addr, val);
1107 void bsnes_debug_write2(uint8_t clazz, unsigned offset, uint8_t val)
1109 if(disable_breakpoints) return;
1110 uint64_t _addr = translate_class_address(clazz, offset);
1111 if(_addr != 0xFFFFFFFFFFFFFFFFULL)
1112 ecore_callbacks->memory_write(_addr, val);
1115 void redraw_cover_fbinfo();
1117 struct _bsnes_core : public core_core
1119 _bsnes_core() : core_core({&gamepad, &gamepad16, &justifier, &justifiers, &mouse, &multitap,
1120 &multitap16, &none, &superscope, &psystem, &psystem_hreset, &psystem_compact}, {
1121 {0, "Soft reset", "reset", {}},
1122 {1, "Hard reset", "hardreset", {}},
1123 #ifdef BSNES_HAS_DEBUGGER
1124 {2, "Delayed soft reset", "delayreset", {
1125 {"Delay","int:0,99999999"}
1127 {3, "Delayed hard reset", "delayhardreset", {
1128 {"Delay","int:0,99999999"}
1130 #endif
1131 #ifdef BSNES_IS_COMPAT
1132 {4, "Layers‣BG1 Priority 0", "bg1pri0", {{"", "toggle"}}},
1133 {5, "Layers‣BG1 Priority 1", "bg1pri1", {{"", "toggle"}}},
1134 {8, "Layers‣BG2 Priority 0", "bg2pri0", {{"", "toggle"}}},
1135 {9, "Layers‣BG2 Priority 1", "bg2pri1", {{"", "toggle"}}},
1136 {12, "Layers‣BG3 Priority 0", "bg3pri0", {{"", "toggle"}}},
1137 {13, "Layers‣BG3 Priority 1", "bg3pri1", {{"", "toggle"}}},
1138 {16, "Layers‣BG4 Priority 0", "bg4pri0", {{"", "toggle"}}},
1139 {17, "Layers‣BG4 Priority 1", "bg4pri1", {{"", "toggle"}}},
1140 {20, "Layers‣Sprite Priority 0", "oampri0", {{"", "toggle"}}},
1141 {21, "Layers‣Sprite Priority 1", "oampri1", {{"", "toggle"}}},
1142 {22, "Layers‣Sprite Priority 2", "oampri2", {{"", "toggle"}}},
1143 {23, "Layers‣Sprite Priority 3", "oampri3", {{"", "toggle"}}},
1144 #endif
1145 }) {}
1147 std::string c_core_identifier() const {
1148 return (stringfmt() << snes_library_id() << " (" << SNES::Info::Profile << " core)").str();
1150 bool c_set_region(core_region& region) {
1151 if(&region == &region_auto)
1152 SNES::config.region = SNES::System::Region::Autodetect;
1153 else if(&region == &region_ntsc)
1154 SNES::config.region = SNES::System::Region::NTSC;
1155 else if(&region == &region_pal)
1156 SNES::config.region = SNES::System::Region::PAL;
1157 else
1158 return false;
1159 return true;
1161 std::pair<uint32_t, uint32_t> c_video_rate() {
1162 if(!internal_rom)
1163 return std::make_pair(60, 1);
1164 uint32_t div;
1165 if(SNES::system.region() == SNES::System::Region::PAL)
1166 div = last_interlace ? DURATION_PAL_FIELD : DURATION_PAL_FRAME;
1167 else
1168 div = last_interlace ? DURATION_NTSC_FIELD : DURATION_NTSC_FRAME;
1169 return std::make_pair(SNES::system.cpu_frequency(), div);
1171 double c_get_PAR() {
1172 double base = (SNES::system.region() == SNES::System::Region::PAL) ? 1.25 : 1.146;
1173 return base;
1175 std::pair<uint32_t, uint32_t> c_audio_rate() {
1176 if(!internal_rom)
1177 return std::make_pair(64081, 2);
1178 return std::make_pair(SNES::system.apu_frequency(), static_cast<uint32_t>(768));
1180 std::map<std::string, std::vector<char>> c_save_sram() throw(std::bad_alloc) {
1181 std::map<std::string, std::vector<char>> out;
1182 if(!internal_rom)
1183 return out;
1184 for(unsigned i = 0; i < SNES::cartridge.nvram.size(); i++) {
1185 SNES::Cartridge::NonVolatileRAM& r = SNES::cartridge.nvram[i];
1186 std::string savename = sram_name(r.id, r.slot);
1187 std::vector<char> x;
1188 x.resize(r.size);
1189 memcpy(&x[0], r.data, r.size);
1190 out[savename] = x;
1192 return out;
1194 void c_load_sram(std::map<std::string, std::vector<char>>& sram) throw(std::bad_alloc) {
1195 std::set<std::string> used;
1196 if(!internal_rom) {
1197 for(auto i : sram)
1198 messages << "WARNING: SRAM '" << i.first << ": Not found on cartridge."
1199 << std::endl;
1200 return;
1202 if(sram.empty())
1203 return;
1204 for(unsigned i = 0; i < SNES::cartridge.nvram.size(); i++) {
1205 SNES::Cartridge::NonVolatileRAM& r = SNES::cartridge.nvram[i];
1206 std::string savename = sram_name(r.id, r.slot);
1207 if(sram.count(savename)) {
1208 std::vector<char>& x = sram[savename];
1209 if(r.size != x.size())
1210 messages << "WARNING: SRAM '" << savename << "': Loaded " << x.size()
1211 << " bytes, but the SRAM is " << r.size << "." << std::endl;
1212 memcpy(r.data, &x[0], (r.size < x.size()) ? r.size : x.size());
1213 used.insert(savename);
1214 } else
1215 messages << "WARNING: SRAM '" << savename << ": No data." << std::endl;
1217 for(auto i : sram)
1218 if(!used.count(i.first))
1219 messages << "WARNING: SRAM '" << i.first << ": Not found on cartridge."
1220 << std::endl;
1222 void c_serialize(std::vector<char>& out) {
1223 if(!internal_rom)
1224 throw std::runtime_error("No ROM loaded");
1225 serializer s = SNES::system.serialize();
1226 out.resize(s.size());
1227 memcpy(&out[0], s.data(), s.size());
1229 void c_unserialize(const char* in, size_t insize) {
1230 if(!internal_rom)
1231 throw std::runtime_error("No ROM loaded");
1232 serializer s(reinterpret_cast<const uint8_t*>(in), insize);
1233 if(!SNES::system.unserialize(s))
1234 throw std::runtime_error("SNES core rejected savestate");
1235 have_saved_this_frame = true;
1236 do_reset_flag = -1;
1238 core_region& c_get_region() {
1239 return (SNES::system.region() == SNES::System::Region::PAL) ? region_pal : region_ntsc;
1241 void c_power() {
1242 if(internal_rom) snes_power();
1244 void c_unload_cartridge() {
1245 if(!internal_rom) return;
1246 snes_term();
1247 snes_unload_cartridge();
1248 internal_rom = NULL;
1250 std::pair<uint32_t, uint32_t> c_get_scale_factors(uint32_t width, uint32_t height) {
1251 return std::make_pair((width < 400) ? 2 : 1, (height < 400) ? 2 : 1);
1253 void c_install_handler() {
1254 #ifdef BSNES_SUPPORTS_ADV_BREAKPOINTS
1255 SNES::bus.debug_read = bsnes_debug_read;
1256 SNES::bus.debug_write = bsnes_debug_write;
1257 #endif
1258 #ifdef BSNES_SUPPORTS_ADV_BREAKPOINTS_PPU
1259 #ifdef BSNES_IS_COMPAT
1260 SNES::ppu.debug_read = bsnes_debug_read3;
1261 SNES::ppu.debug_write = bsnes_debug_write2;
1262 #endif
1263 SNES::smp.debug_read = bsnes_debug_read2;
1264 SNES::smp.debug_write = bsnes_debug_write2;
1265 #endif
1266 basic_init();
1267 old = SNES::interface;
1268 SNES::interface = &my_interface_obj;
1269 SNES::system.init();
1270 magic_flags |= 1;
1272 void c_uninstall_handler() { SNES::interface = old; }
1273 void c_emulate() {
1274 if(!internal_rom)
1275 return;
1276 bool was_delay_reset = false;
1277 int16_t reset = ecore_callbacks->get_input(0, 0, 1);
1278 int16_t hreset = 0;
1279 if(support_hreset)
1280 hreset = ecore_callbacks->get_input(0, 0, 4);
1281 if(reset) {
1282 long hi = ecore_callbacks->get_input(0, 0, 2);
1283 long lo = ecore_callbacks->get_input(0, 0, 3);
1284 long delay = 10000 * hi + lo;
1285 if(delay > 0) {
1286 was_delay_reset = true;
1287 #ifdef BSNES_HAS_DEBUGGER
1288 messages << "Executing delayed reset... This can take some time!"
1289 << std::endl;
1290 video_refresh_done = false;
1291 delayreset_cycles_run = 0;
1292 delayreset_cycles_target = delay;
1293 forced_hook = true;
1294 SNES::cpu.step_event = delayreset_fn;
1295 again:
1296 SNES::system.run();
1297 if(SNES::scheduler.exit_reason() == SNES::Scheduler::ExitReason::DebuggerEvent
1298 && SNES::debugger.break_event ==
1299 SNES::Debugger::BreakEvent::BreakpointHit) {
1300 snesdbg_on_break();
1301 goto again;
1303 SNES::cpu.step_event = nall::function<bool()>();
1304 forced_hook = false;
1305 update_trace_hook_state();
1306 if(video_refresh_done) {
1307 //Force the reset here.
1308 do_reset_flag = -1;
1309 messages << "SNES reset (forced at " << delayreset_cycles_run << ")"
1310 << std::endl;
1311 if(hreset)
1312 SNES::system.power();
1313 else
1314 SNES::system.reset();
1315 return;
1317 if(hreset)
1318 SNES::system.power();
1319 else
1320 SNES::system.reset();
1321 messages << "SNES reset (delayed " << delayreset_cycles_run << ")"
1322 << std::endl;
1323 #else
1324 messages << "Delayresets not supported on this bsnes version "
1325 "(needs v084 or v085)" << std::endl;
1326 if(hreset)
1327 SNES::system.power();
1328 else
1329 SNES::system.reset();
1330 #endif
1331 } else if(delay == 0) {
1332 if(hreset)
1333 SNES::system.power();
1334 else
1335 SNES::system.reset();
1336 messages << "SNES reset" << std::endl;
1339 do_reset_flag = -1;
1341 if(!have_saved_this_frame && save_every_frame && !was_delay_reset)
1342 SNES::system.runtosave();
1343 #ifdef BSNES_HAS_DEBUGGER
1344 if(trace_enabled())
1345 SNES::cpu.step_event = trace_fn;
1346 #endif
1347 again2:
1348 SNES::system.run();
1349 #ifdef BSNES_HAS_DEBUGGER
1350 if(SNES::scheduler.exit_reason() == SNES::Scheduler::ExitReason::DebuggerEvent &&
1351 SNES::debugger.break_event == SNES::Debugger::BreakEvent::BreakpointHit) {
1352 snesdbg_on_break();
1353 goto again2;
1355 SNES::cpu.step_event = nall::function<bool()>();
1356 #endif
1357 have_saved_this_frame = false;
1359 void c_runtosave() {
1360 if(!internal_rom)
1361 return;
1362 stepping_into_save = true;
1363 SNES::system.runtosave();
1364 have_saved_this_frame = true;
1365 stepping_into_save = false;
1367 bool c_get_pflag() { return SNES::cpu.controller_flag; }
1368 void c_set_pflag(bool pflag) { SNES::cpu.controller_flag = pflag; }
1369 framebuffer::raw& c_draw_cover() {
1370 static framebuffer::raw x(cover_fbinfo);
1371 redraw_cover_fbinfo();
1372 return x;
1374 std::string c_get_core_shortname() const
1376 #ifdef BSNES_IS_COMPAT
1377 return (stringfmt() << "bsnes" << BSNES_VERSION << "c").str();
1378 #else
1379 return (stringfmt() << "bsnes" << BSNES_VERSION << "a").str();
1380 #endif
1382 void c_pre_emulate_frame(portctrl::frame& cf)
1384 cf.axis3(0, 0, 1, (do_reset_flag >= 0) ? 1 : 0);
1385 if(support_hreset)
1386 cf.axis3(0, 0, 4, do_hreset_flag ? 1 : 0);
1387 if(do_reset_flag >= 0) {
1388 cf.axis3(0, 0, 2, do_reset_flag / 10000);
1389 cf.axis3(0, 0, 3, do_reset_flag % 10000);
1390 } else {
1391 cf.axis3(0, 0, 2, 0);
1392 cf.axis3(0, 0, 3, 0);
1395 void c_execute_action(unsigned id, const std::vector<interface_action_paramval>& p)
1397 switch(id) {
1398 case 0: //Soft reset.
1399 do_reset_flag = 0;
1400 do_hreset_flag = false;
1401 break;
1402 case 1: //Hard reset.
1403 do_reset_flag = 0;
1404 do_hreset_flag = true;
1405 break;
1406 case 2: //Delayed soft reset.
1407 do_reset_flag = p[0].i;
1408 do_hreset_flag = false;
1409 break;
1410 case 3: //Delayed hard reset.
1411 do_reset_flag = p[0].i;
1412 do_hreset_flag = true;
1413 break;
1415 #ifdef BSNES_IS_COMPAT
1416 if(id >= 4 && id <= 23) {
1417 unsigned y = (id - 4) / 4;
1418 SNES::ppu.layer_enabled[y][id % 4] = !SNES::ppu.layer_enabled[y][id % 4];
1419 ecore_callbacks->action_state_updated();
1421 #endif
1423 const interface_device_reg* c_get_registers() { return snes_registers; }
1424 unsigned c_action_flags(unsigned id)
1426 if((id == 2 || id == 3) && !support_dreset)
1427 return 0;
1428 if(id == 0 || id == 2)
1429 return 1;
1430 if(id == 1 || id == 3)
1431 return support_hreset ? 1 : 0;
1432 #ifdef BSNES_IS_COMPAT
1433 if(id >= 4 && id <= 23) {
1434 unsigned y = (id - 4) / 4;
1435 return SNES::ppu.layer_enabled[y][id % 4] ? 3 : 1;
1437 #endif
1438 return 0; //WTF?
1440 int c_reset_action(bool hard)
1442 return hard ? (support_hreset ? 1 : -1) : 0;
1444 std::pair<uint64_t, uint64_t> c_get_bus_map()
1446 return std::make_pair(0x1000000, 0x1000000);
1448 std::list<core_vma_info> c_vma_list() { return get_VMAlist(); }
1449 std::set<std::string> c_srams() { return bsnes_srams(); }
1450 std::pair<unsigned, unsigned> c_lightgun_scale() {
1451 return std::make_pair(256, last_PAL ? 239 : 224);
1453 void c_set_debug_flags(uint64_t addr, unsigned int sflags, unsigned int cflags)
1455 if(addr == 0) {
1456 if(sflags & 8) trace_cpu_enable = true;
1457 if(cflags & 8) trace_cpu_enable = false;
1458 update_trace_hook_state();
1460 if(addr == 1) {
1461 if(sflags & 8) trace_smp_enable = true;
1462 if(cflags & 8) trace_smp_enable = false;
1463 update_trace_hook_state();
1465 if(addr == 2) {
1466 if(sflags & 8) trace_sa1_enable = true;
1467 if(cflags & 8) trace_sa1_enable = false;
1468 update_trace_hook_state();
1470 #ifdef BSNES_SUPPORTS_ADV_BREAKPOINTS
1471 auto _addr = recognize_address(addr);
1472 if(_addr.first == ADDR_KIND_ALL)
1473 SNES::bus.debugFlags(sflags & 7, cflags & 7);
1474 #ifdef BSNES_SUPPORTS_ADV_BREAKPOINTS_PPU
1475 #ifdef BSNES_IS_COMPAT
1476 else if(_addr.first == 13) //VRAM.
1477 SNES::ppu.vram_debugflags[_addr.second] =
1478 SNES::ppu.vram_debugflags[_addr.second] & ~(cflags & 7) | (sflags & 7);
1479 else if(_addr.first == 14) //OAM.
1480 SNES::ppu.oam_debugflags[_addr.second] =
1481 SNES::ppu.oam_debugflags[_addr.second] & ~(cflags & 7) | (sflags & 7);
1482 else if(_addr.first == 15) //CGRAM.
1483 SNES::ppu.cgram_debugflags[_addr.second] =
1484 SNES::ppu.cgram_debugflags[_addr.second] & ~(cflags & 7) | (sflags & 7);
1485 #endif
1486 else if(_addr.first == 16) //APURAM.
1487 SNES::smp.debugflags[_addr.second] =
1488 SNES::smp.debugflags[_addr.second] & ~(cflags & 7) | (sflags & 7);
1489 #endif
1490 else if(_addr.first != ADDR_KIND_NONE && ((sflags | cflags) & 7))
1491 SNES::bus.debugFlags(sflags & 7, cflags & 7, _addr.first, _addr.second);
1492 #endif
1494 void c_set_cheat(uint64_t addr, uint64_t value, bool set)
1496 #ifdef BSNES_SUPPORTS_ADV_BREAKPOINTS
1497 bool s = false;
1498 auto _addr = recognize_address(addr);
1499 //13-16 are VRAM, OAM, CGRAM and APURAM, can't cheat on those (yet).
1500 if(_addr.first == ADDR_KIND_NONE || _addr.first == ADDR_KIND_ALL ||
1501 (_addr.first >= 13 && _addr.first <= 16))
1502 return;
1503 unsigned x = 0;
1504 while(x < 0x1000000) {
1505 x = SNES::bus.enumerateMirrors(_addr.first, _addr.second, x);
1506 if(x < 0x1000000) {
1507 if(set) {
1508 for(size_t i = 0; i < SNES::cheat.size(); i++) {
1509 if(SNES::cheat[i].addr == x) {
1510 SNES::cheat[i].data = value;
1511 s = true;
1512 break;
1515 if(!s) SNES::cheat.append({x, (uint8_t)value, true});
1516 } else
1517 for(size_t i = 0; i < SNES::cheat.size(); i++) {
1518 if(SNES::cheat[i].addr == x) {
1519 SNES::cheat.remove(i);
1520 break;
1524 x++;
1526 SNES::cheat.synchronize();
1527 #endif
1529 void c_debug_reset()
1531 #ifdef BSNES_SUPPORTS_ADV_BREAKPOINTS
1532 SNES::bus.clearDebugFlags();
1533 SNES::cheat.reset();
1534 #ifdef BSNES_SUPPORTS_ADV_BREAKPOINTS_PPU
1535 #ifdef BSNES_IS_COMPAT
1536 memset(SNES::ppu.vram_debugflags, 0, sizeof(SNES::ppu.vram_debugflags));
1537 memset(SNES::ppu.oam_debugflags, 0, sizeof(SNES::ppu.oam_debugflags));
1538 memset(SNES::ppu.cgram_debugflags, 0, sizeof(SNES::ppu.cgram_debugflags));
1539 #endif
1540 memset(SNES::smp.debugflags, 0, sizeof(SNES::smp.debugflags));
1541 #endif
1542 #endif
1543 trace_cpu_enable = false;
1544 trace_smp_enable = false;
1545 update_trace_hook_state();
1547 std::vector<std::string> c_get_trace_cpus()
1549 std::vector<std::string> r;
1550 r.push_back("cpu");
1551 r.push_back("smp");
1552 #ifdef BSNES_SUPPORTS_TRACE_SA1
1553 r.push_back("sa1");
1554 #endif
1555 //TODO: Trace various chips.
1556 return r;
1558 void c_reset_to_load()
1560 serializer s(&init_savestate[0], init_savestate.size());
1561 if(!SNES::system.unserialize(s))
1562 throw std::runtime_error("SNES core rejected initial state?");
1563 have_saved_this_frame = false;
1564 do_reset_flag = -1;
1565 if(ecore_callbacks)
1566 ecore_callbacks->action_state_updated();
1568 } bsnes_core;
1570 struct _type_snes : public core_type
1572 _type_snes()
1573 : core_type({{
1574 .iname = "snes",
1575 .hname = "SNES",
1576 .id = 0,
1577 .sysname = "SNES",
1578 .bios = NULL,
1579 .regions = {&region_auto, &region_ntsc, &region_pal},
1580 .images = {{"rom", "Cartridge ROM", 1, 0, 512,
1581 "sfc;smc;swc;fig;ufo;sf2;gd3;gd7;dx2;mgd;mgh"}},
1582 .settings = bsnes_settings,
1583 .core = &bsnes_core,
1584 }}) {}
1586 int t_load_rom(core_romimage* img, std::map<std::string, std::string>& settings,
1587 uint64_t secs, uint64_t subsecs)
1589 return load_rom(this, img, settings, secs, subsecs,
1590 load_rom_X1<snes_load_cartridge_normal>);
1592 controller_set t_controllerconfig(std::map<std::string, std::string>& settings)
1594 return bsnes_controllerconfig(settings);
1596 } type_snes;
1597 core_sysregion snes_pal("snes_pal", type_snes, region_pal);
1598 core_sysregion snes_ntsc("snes_ntsc", type_snes, region_ntsc);
1600 struct _type_bsx : public core_type, public core_sysregion
1602 _type_bsx()
1603 : core_type({{
1604 .iname = "bsx",
1605 .hname = "BS-X (non-slotted)",
1606 .id = 1,
1607 .sysname = "BS-X",
1608 .bios = "bsx.sfc",
1609 .regions = {&region_ntsc},
1610 .images = {{"rom", "BS-X BIOS", 1, 0, 512,
1611 "sfc;smc;swc;fig;ufo;sf2;gd3;gd7;dx2;mgd;mgh"},
1612 {"bsx", "BS-X Flash", 2, 0, 512, "bs"}},
1613 .settings = bsnes_settings,
1614 .core = &bsnes_core,
1615 }}),
1616 core_sysregion("bsx", *this, region_ntsc) {}
1618 int t_load_rom(core_romimage* img, std::map<std::string, std::string>& settings,
1619 uint64_t secs, uint64_t subsecs)
1621 return load_rom(this, img, settings, secs, subsecs,
1622 load_rom_X2<snes_load_cartridge_bsx>);
1624 controller_set t_controllerconfig(std::map<std::string, std::string>& settings)
1626 return bsnes_controllerconfig(settings);
1628 } type_bsx;
1630 struct _type_bsxslotted : public core_type, public core_sysregion
1632 _type_bsxslotted()
1633 : core_type({{
1634 .iname = "bsxslotted",
1635 .hname = "BS-X (slotted)",
1636 .id = 2,
1637 .sysname = "BS-X",
1638 .bios = "bsxslotted.sfc",
1639 .regions = {&region_ntsc},
1640 .images = {{"rom", "BS-X BIOS", 1, 0, 512,
1641 "sfc;smc;swc;fig;ufo;sf2;gd3;gd7;dx2;mgd;mgh"},
1642 {"bsx", "BS-X Flash", 2, 0, 512, "bss"}},
1643 .settings = bsnes_settings,
1644 .core = &bsnes_core,
1645 }}),
1646 core_sysregion("bsxslotted", *this, region_ntsc) {}
1647 int t_load_rom(core_romimage* img, std::map<std::string, std::string>& settings,
1648 uint64_t secs, uint64_t subsecs)
1650 return load_rom(this, img, settings, secs, subsecs,
1651 load_rom_X2<snes_load_cartridge_bsx_slotted>);
1653 controller_set t_controllerconfig(std::map<std::string, std::string>& settings)
1655 return bsnes_controllerconfig(settings);
1657 } type_bsxslotted;
1659 struct _type_sufamiturbo : public core_type, public core_sysregion
1661 _type_sufamiturbo()
1662 : core_type({{
1663 .iname = "sufamiturbo",
1664 .hname = "Sufami Turbo",
1665 .id = 3,
1666 .sysname = "SufamiTurbo",
1667 .bios = "sufamiturbo.sfc",
1668 .regions = {&region_ntsc},
1669 .images = {
1670 {"rom", "ST BIOS", 1, 0, 512, "sfc;smc;swc;fig;ufo;sf2;gd3;gd7;dx2;mgd;mgh"},
1671 {"slot-a", "ST SLOT A ROM", 2, 0, 512, "st"},
1672 {"slot-b", "ST SLOT B ROM", 2, 0, 512, "st"}
1674 .settings = bsnes_settings,
1675 .core = &bsnes_core,
1676 }}),
1677 core_sysregion("sufamiturbo", *this, region_ntsc) {}
1678 int t_load_rom(core_romimage* img, std::map<std::string, std::string>& settings,
1679 uint64_t secs, uint64_t subsecs)
1681 return load_rom(this, img, settings, secs, subsecs,
1682 load_rom_X3<snes_load_cartridge_sufami_turbo>);
1684 controller_set t_controllerconfig(std::map<std::string, std::string>& settings)
1686 return bsnes_controllerconfig(settings);
1688 } type_sufamiturbo;
1690 struct _type_sgb : public core_type
1692 _type_sgb()
1693 : core_type({{
1694 .iname = "sgb",
1695 .hname = "Super Game Boy",
1696 .id = 4,
1697 .sysname = "SGB",
1698 .bios = "sgb.sfc",
1699 .regions = {&region_auto, &region_ntsc, &region_pal},
1700 .images = {{"rom", "SGB BIOS", 1, 0, 512,
1701 "sfc;smc;swc;fig;ufo;sf2;gd3;gd7;dx2;mgd;mgh"},
1702 {"dmg", "DMG ROM", 2, 0, 512, "gb;dmg;sgb"}},
1703 .settings = bsnes_settings,
1704 .core = &bsnes_core,
1705 }}) {}
1706 int t_load_rom(core_romimage* img, std::map<std::string, std::string>& settings,
1707 uint64_t secs, uint64_t subsecs)
1709 return load_rom(this, img, settings, secs, subsecs,
1710 load_rom_X2<snes_load_cartridge_super_game_boy>);
1712 controller_set t_controllerconfig(std::map<std::string, std::string>& settings)
1714 return bsnes_controllerconfig(settings);
1716 } type_sgb;
1717 core_sysregion sgb_pal("sgb_pal", type_sgb, region_pal);
1718 core_sysregion sgb_ntsc("sgb_ntsc", type_sgb, region_ntsc);
1720 void redraw_cover_fbinfo()
1722 for(size_t i = 0; i < sizeof(cover_fbmem) / sizeof(cover_fbmem[0]); i++)
1723 cover_fbmem[i] = 0;
1724 std::string ident = bsnes_core.get_core_identifier();
1725 cover_render_string(cover_fbmem, 0, 0, ident, 0x7FFFF, 0x00000, 512, 448, 2048, 4);
1726 std::ostringstream name;
1727 name << "Internal ROM name: ";
1728 disable_breakpoints = true;
1729 for(unsigned i = 0; i < 21; i++) {
1730 unsigned busaddr = 0x00FFC0 + i;
1731 #ifdef BSNES_SUPPORTS_ADV_BREAKPOINTS
1732 unsigned char ch = SNES::bus.read(busaddr, false);
1733 #else
1734 unsigned char ch = SNES::bus.read(busaddr);
1735 #endif
1736 if(ch < 32 || ch > 126)
1737 name << "<" << hex::to8(ch) << ">";
1738 else
1739 name << ch;
1741 disable_breakpoints = false;
1742 cover_render_string(cover_fbmem, 0, 16, name.str(), 0x7FFFF, 0x00000, 512, 448, 2048, 4);
1743 unsigned y = 32;
1744 for(auto i : cover_information()) {
1745 cover_render_string(cover_fbmem, 0, y, i, 0x7FFFF, 0x00000, 512, 448, 2048, 4);
1746 y += 16;
1748 #ifdef BSNES_SUPPORTS_ALT_TIMINGS
1749 if(SNES::config.cpu.alt_poll_timings) {
1750 cover_render_string(cover_fbmem, 0, y, "Alternate timings enabled.", 0x7FFFF, 0x00000,
1751 512, 448, 2048, 4);
1752 y += 16;
1754 #endif
1755 #ifdef BSNES_SUPPORTS_BUS_FIXES
1756 if(SNES::config.cpu.bus_fixes) {
1757 cover_render_string(cover_fbmem, 0, y, "Bus fixes enabled.", 0x7FFFF, 0x00000,
1758 512, 448, 2048, 4);
1759 y += 16;
1761 #endif
1762 #ifdef BSNES_SUPPORTS_MOUSE_SPEED_FIX
1763 if(SNES::config.mouse_speed_fix) {
1764 cover_render_string(cover_fbmem, 0, y, "Mouse speed support enabled.", 0x7FFFF, 0x00000,
1765 512, 448, 2048, 4);
1766 y += 16;
1768 #endif
1771 void my_interface::videoRefresh(const uint32_t* data, bool hires, bool interlace, bool overscan)
1773 last_hires = hires;
1774 last_interlace = interlace;
1775 bool region = (SNES::system.region() == SNES::System::Region::PAL);
1776 last_PAL = region;
1777 if(stepping_into_save)
1778 messages << "Got video refresh in runtosave, expect desyncs!" << std::endl;
1779 video_refresh_done = true;
1780 uint32_t fps_n, fps_d;
1781 auto fps = bsnes_core.get_video_rate();
1782 fps_n = fps.first;
1783 fps_d = fps.second;
1784 uint32_t g = gcd(fps_n, fps_d);
1785 fps_n /= g;
1786 fps_d /= g;
1788 framebuffer::info inf;
1789 inf.type = &framebuffer::pixfmt_lrgb;
1790 inf.mem = const_cast<char*>(reinterpret_cast<const char*>(data));
1791 inf.physwidth = 512;
1792 inf.physheight = 512;
1793 inf.physstride = 2048;
1794 inf.width = hires ? 512 : 256;
1795 inf.height = (region ? 239 : 224) * (interlace ? 2 : 1);
1796 inf.stride = interlace ? 2048 : 4096;
1797 inf.offset_x = 0;
1798 inf.offset_y = (region ? (overscan ? 9 : 1) : (overscan ? 16 : 9)) * 2;
1799 framebuffer::raw ls(inf);
1801 ecore_callbacks->output_frame(ls, fps_n, fps_d);
1802 if(soundbuf_fill > 0) {
1803 auto freq = SNES::system.apu_frequency();
1804 CORE().audio->submit_buffer(soundbuf, soundbuf_fill / 2, true, freq / 768.0);
1805 soundbuf_fill = 0;
1809 std::list<core_vma_info> get_VMAlist()
1811 std::list<core_vma_info> ret;
1812 if(!internal_rom)
1813 return ret;
1814 create_region(ret, "WRAM", 0x007E0000, SNES::cpu.wram, 131072, false);
1815 create_region(ret, "APURAM", 0x00000000, SNES::smp.apuram, 65536, false);
1816 create_region(ret, "VRAM", 0x00010000, SNES::ppu.vram, 65536, false);
1817 create_region(ret, "OAM", 0x00020000, SNES::ppu.oam, 544, false);
1818 create_region(ret, "CGRAM", 0x00021000, SNES::ppu.cgram, 512, false);
1819 if(SNES::cartridge.has_srtc()) create_region(ret, "RTC", 0x00022000, SNES::srtc.rtc, 20, false);
1820 if(SNES::cartridge.has_spc7110rtc()) create_region(ret, "RTC", 0x00022000, SNES::spc7110.rtc, 20,
1821 false);
1822 if(SNES::cartridge.has_necdsp()) {
1823 create_region(ret, "DSPRAM", 0x00023000, reinterpret_cast<uint8_t*>(SNES::necdsp.dataRAM),
1824 4096, false, true);
1825 create_region(ret, "DSPPROM", 0xF0000000, reinterpret_cast<uint8_t*>(SNES::necdsp.programROM),
1826 65536, true, true);
1827 create_region(ret, "DSPDROM", 0xF0010000, reinterpret_cast<uint8_t*>(SNES::necdsp.dataROM),
1828 4096, true, true);
1830 if(SNES::cartridge.has_sa1())
1831 create_region(ret, "SA1IRAM", 0x00040000, SNES::sa1.iram.data(), SNES::sa1.iram.size(),
1832 false);
1833 create_region(ret, "SRAM", 0x10000000, SNES::cartridge.ram, false);
1834 create_region(ret, "ROM", 0x80000000, SNES::cartridge.rom, true);
1835 create_region(ret, "BUS", 0x1000000, 0x1000000, snes_bus_iospace_read, snes_bus_iospace_write);
1836 create_region(ret, "PTRTABLE", 0x100000000, 0x100000, ptrtable_iospace_read, NULL);
1837 map_internal(ret, "CPU_STATE", 0, &SNES::cpu, sizeof(SNES::cpu));
1838 map_internal(ret, "PPU_STATE", 1, &SNES::ppu, sizeof(SNES::ppu));
1839 map_internal(ret, "SMP_STATE", 2, &SNES::smp, sizeof(SNES::smp));
1840 map_internal(ret, "DSP_STATE", 3, &SNES::dsp, sizeof(SNES::dsp));
1841 if(internal_rom == &type_bsx || internal_rom == &type_bsxslotted) {
1842 create_region(ret, "BSXFLASH", 0x90000000, SNES::bsxflash.memory, true);
1843 create_region(ret, "BSX_RAM", 0x20000000, SNES::bsxcartridge.sram, false);
1844 create_region(ret, "BSX_PRAM", 0x30000000, SNES::bsxcartridge.psram, false);
1846 if(internal_rom == &type_sufamiturbo) {
1847 create_region(ret, "SLOTA_ROM", 0x90000000, SNES::sufamiturbo.slotA.rom, true);
1848 create_region(ret, "SLOTB_ROM", 0xA0000000, SNES::sufamiturbo.slotB.rom, true);
1849 create_region(ret, "SLOTA_RAM", 0x20000000, SNES::sufamiturbo.slotA.ram, false);
1850 create_region(ret, "SLOTB_RAM", 0x30000000, SNES::sufamiturbo.slotB.ram, false);
1852 if(internal_rom == &type_sgb) {
1853 map_internal(ret, "GBCPU_STATE", 4, &GameBoy::cpu, sizeof(GameBoy::cpu));
1854 create_region(ret, "GBROM", 0x90000000, GameBoy::cartridge.romdata,
1855 GameBoy::cartridge.romsize, true);
1856 create_region(ret, "GBRAM", 0x20000000, GameBoy::cartridge.ramdata,
1857 GameBoy::cartridge.ramsize, false);
1858 create_region(ret, "GBWRAM", 0x00030000, GameBoy::cpu.wram, 32768, false);
1859 create_region(ret, "GBHRAM", 0x00038000, GameBoy::cpu.hram, 128, true);
1861 return ret;
1864 std::pair<int, uint64_t> recognize_address(uint64_t addr)
1866 if(addr == 0xFFFFFFFFFFFFFFFFULL)
1867 return std::make_pair(ADDR_KIND_ALL, 0);
1868 if(addr >= 0x80000000 && addr <= 0x8FFFFFFF) //Rom.
1869 return std::make_pair(1, addr - 0x80000000);
1870 if(addr >= 0x10000000 && addr <= 0x1FFFFFFF) //SRAM.
1871 return std::make_pair(2, addr - 0x10000000);
1872 if(addr >= 0x007E0000 && addr <= 0x007FFFFF) //WRAM.
1873 return std::make_pair(3, addr - 0x007E0000);
1874 if(addr >= 0x00040000 && addr <= 0x000407FF) //SA1IRAM.
1875 return std::make_pair(6, addr - 0x00040000);
1876 if(addr >= 0x00010000 && addr <= 0x00020000) //VRAM.
1877 return std::make_pair(13, addr - 0x00010000);
1878 if(addr >= 0x00020000 && addr <= 0x0002021F) //OAM.
1879 return std::make_pair(14, addr - 0x00020000);
1880 if(addr >= 0x00021000 && addr <= 0x000211FF) //CGRAM.
1881 return std::make_pair(15, addr - 0x00021000);
1882 if(addr >= 0x00000000 && addr <= 0x0000FFFF) //APURAM.
1883 return std::make_pair(16, addr - 0x00000000);
1884 if(internal_rom == &type_sufamiturbo) {
1885 if(addr >= 0x90000000 && addr <= 0x9FFFFFFF) //SufamiTurboA Rom.
1886 return std::make_pair(8, addr - 0x90000000);
1887 if(addr >= 0xA0000000 && addr <= 0xAFFFFFFF) //SufamiTurboB Rom.
1888 return std::make_pair(9, addr - 0x90000000);
1889 if(addr >= 0x20000000 && addr <= 0x2FFFFFFF) //SufamiTurboA Ram.
1890 return std::make_pair(10, addr - 0x20000000);
1891 if(addr >= 0x20000000 && addr <= 0x3FFFFFFF) //SufamiTurboB Ram.
1892 return std::make_pair(11, addr - 0x30000000);
1894 if(internal_rom == &type_bsx || internal_rom == &type_bsxslotted) {
1895 if(addr >= 0x90000000 && addr <= 0x9FFFFFFF) //BSX flash.
1896 return std::make_pair(12, addr - 0x90000000);
1898 if(addr >= 0x01000000 && addr <= 0x01FFFFFF) //BUS.
1899 return std::make_pair(255, addr - 0x01000000);
1900 return std::make_pair(ADDR_KIND_NONE, 0);
1903 command::fnptr<command::arg_filename> dump_core(lsnes_cmds, "dump-core", "No description available",
1904 "No description available\n",
1905 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
1906 std::vector<char> out;
1907 bsnes_core.serialize(out);
1908 std::ofstream x(args, std::ios_base::out | std::ios_base::binary);
1909 x.write(&out[0], out.size());
1912 #ifdef BSNES_HAS_DEBUGGER
1913 lua::state* snes_debug_cb_keys[SNES::Debugger::Breakpoints];
1914 lua::state* snes_debug_cb_trace;
1916 void snesdbg_execute_callback(lua::state*& cb, signed r)
1918 auto& core = CORE();
1919 if(!cb)
1920 return;
1921 cb->pushlightuserdata(&cb);
1922 cb->gettable(LUA_REGISTRYINDEX);
1923 cb->pushnumber(r);
1924 if(cb->type(-2) == LUA_TFUNCTION) {
1925 int s = cb->pcall(1, 0, 0);
1926 if(s)
1927 cb->pop(1);
1928 } else {
1929 messages << "Can't execute debug callback" << std::endl;
1930 cb->pop(2);
1932 if(core.lua2->requests_repaint) {
1933 core.lua2->requests_repaint = false;
1934 core.command->invoke("repaint");
1938 void snesdbg_on_break()
1940 signed r = SNES::debugger.breakpoint_hit;
1941 snesdbg_execute_callback(snes_debug_cb_keys[r], r);
1944 void snesdbg_on_trace()
1946 snesdbg_execute_callback(snes_debug_cb_trace, -1);
1949 void snesdbg_set_callback(lua::state& L, lua::state*& cb)
1951 cb = &L.get_master();
1952 L.pushlightuserdata(&cb);
1953 L.pushvalue(-2);
1954 L.settable(LUA_REGISTRYINDEX);
1957 bool snesdbg_get_bp_enabled(lua::state& L)
1959 bool r;
1960 L.getfield(-1, "addr");
1961 r = (L.type(-1) == LUA_TNUMBER);
1962 L.pop(1);
1963 return r;
1966 uint32_t snesdbg_get_bp_addr(lua::state& L)
1968 uint32_t r = 0;
1969 L.getfield(-1, "addr");
1970 if(L.type(-1) == LUA_TNUMBER)
1971 r = static_cast<uint32_t>(L.tointeger(-1));
1972 L.pop(1);
1973 return r;
1976 uint32_t snesdbg_get_bp_data(lua::state& L)
1978 signed r = -1;
1979 L.getfield(-1, "data");
1980 if(L.type(-1) == LUA_TNUMBER)
1981 r = static_cast<signed>(L.tointeger(-1));
1982 L.pop(1);
1983 return r;
1986 SNES::Debugger::Breakpoint::Mode snesdbg_get_bp_mode(lua::state& L)
1988 SNES::Debugger::Breakpoint::Mode r = SNES::Debugger::Breakpoint::Mode::Exec;
1989 L.getfield(-1, "mode");
1990 if(L.type(-1) == LUA_TSTRING && !strcmp(L.tostring(-1), "e"))
1991 r = SNES::Debugger::Breakpoint::Mode::Exec;
1992 if(L.type(-1) == LUA_TSTRING && !strcmp(L.tostring(-1), "x"))
1993 r = SNES::Debugger::Breakpoint::Mode::Exec;
1994 if(L.type(-1) == LUA_TSTRING && !strcmp(L.tostring(-1), "exec"))
1995 r = SNES::Debugger::Breakpoint::Mode::Exec;
1996 if(L.type(-1) == LUA_TSTRING && !strcmp(L.tostring(-1), "r"))
1997 r = SNES::Debugger::Breakpoint::Mode::Read;
1998 if(L.type(-1) == LUA_TSTRING && !strcmp(L.tostring(-1), "read"))
1999 r = SNES::Debugger::Breakpoint::Mode::Read;
2000 if(L.type(-1) == LUA_TSTRING && !strcmp(L.tostring(-1), "w"))
2001 r = SNES::Debugger::Breakpoint::Mode::Write;
2002 if(L.type(-1) == LUA_TSTRING && !strcmp(L.tostring(-1), "write"))
2003 r = SNES::Debugger::Breakpoint::Mode::Write;
2004 L.pop(1);
2005 return r;
2008 SNES::Debugger::Breakpoint::Source snesdbg_get_bp_source(lua::state& L)
2010 SNES::Debugger::Breakpoint::Source r = SNES::Debugger::Breakpoint::Source::CPUBus;
2011 L.getfield(-1, "source");
2012 if(L.type(-1) == LUA_TSTRING && !strcmp(L.tostring(-1), "cpubus"))
2013 r = SNES::Debugger::Breakpoint::Source::CPUBus;
2014 if(L.type(-1) == LUA_TSTRING && !strcmp(L.tostring(-1), "apuram"))
2015 r = SNES::Debugger::Breakpoint::Source::APURAM;
2016 if(L.type(-1) == LUA_TSTRING && !strcmp(L.tostring(-1), "vram"))
2017 r = SNES::Debugger::Breakpoint::Source::VRAM;
2018 if(L.type(-1) == LUA_TSTRING && !strcmp(L.tostring(-1), "oam"))
2019 r = SNES::Debugger::Breakpoint::Source::OAM;
2020 if(L.type(-1) == LUA_TSTRING && !strcmp(L.tostring(-1), "cgram"))
2021 r = SNES::Debugger::Breakpoint::Source::CGRAM;
2022 L.pop(1);
2023 return r;
2026 void snesdbg_get_bp_callback(lua::state& L)
2028 L.getfield(-1, "callback");
2031 int setdebug(lua::state& L, lua::parameters& P)
2033 unsigned r;
2034 int ltbl;
2036 P(r);
2038 if(r >= SNES::Debugger::Breakpoints)
2039 throw std::runtime_error("Bad breakpoint number");
2040 if(P.is_novalue()) {
2041 //Clear breakpoint.
2042 SNES::debugger.breakpoint[r].enabled = false;
2043 return 0;
2044 } else if(P.is_table()) {
2045 P(P.table(ltbl));
2046 L.pushvalue(ltbl);
2047 auto& x = SNES::debugger.breakpoint[r];
2048 x.enabled = snesdbg_get_bp_enabled(L);
2049 x.addr = snesdbg_get_bp_addr(L);
2050 x.data = snesdbg_get_bp_data(L);
2051 x.mode = snesdbg_get_bp_mode(L);
2052 x.source = snesdbg_get_bp_source(L);
2053 snesdbg_get_bp_callback(L);
2054 snesdbg_set_callback(L, snes_debug_cb_keys[r]);
2055 L.pop(2);
2056 return 0;
2057 } else
2058 P.expected("table or nil");
2059 return 0; //NOTREACHED.
2062 int setstep(lua::state& L, lua::parameters& P)
2064 uint64_t r;
2065 int lfn = 2;
2067 P(r);
2068 if(P.is_function() || P.is_novalue()) lfn = P.skip();
2070 L.pushvalue(lfn);
2071 snesdbg_set_callback(L, snes_debug_cb_trace);
2072 trace_counter = r;
2073 update_trace_hook_state();
2074 L.pop(1);
2075 return 0;
2078 int settrace(lua::state& L, lua::parameters& P)
2080 std::string r;
2082 P(r);
2084 CORE().command->invoke("tracelog cpu " + r);
2085 return 0;
2088 command::fnptr<const std::string&> start_trace(lsnes_cmds, "set-trace", "No description available",
2089 "No description available\n",
2090 [](const std::string& r) throw(std::bad_alloc, std::runtime_error) {
2091 CORE().command->invoke("tracelog cpu " + r);
2094 #ifdef BSNES_IS_COMPAT
2095 int enablelayer(lua::state& L, lua::parameters& P)
2097 unsigned layer, priority;
2098 bool enabled;
2100 P(layer, priority, enabled);
2102 SNES::ppu.layer_enable(layer, priority, enabled);
2103 return 0;
2105 #endif
2107 int smpdisasm(lua::state& L, lua::parameters& P)
2109 uint64_t addr;
2111 P(addr);
2113 nall::string _disasm = SNES::smp.disassemble_opcode(addr);
2114 std::string disasm(_disasm, _disasm.length());
2115 L.pushlstring(disasm);
2116 return 1;
2119 lua::functions debug_fns_snes(lua_func_misc, "bsnes", {
2120 #ifdef BSNES_IS_COMPAT
2121 {"enablelayer", enablelayer},
2122 #endif
2123 {"smpdisasm", smpdisasm},
2126 lua::functions debug_fns_memory(lua_func_misc, "memory", {
2127 {"setdebug", setdebug},
2128 {"setstep", setstep},
2129 {"settrace", settrace},
2131 #else
2132 void snesdbg_on_break() {}
2133 void snesdbg_on_trace() {}
2134 #endif
2136 struct oninit {
2137 oninit()
2139 register_sysregion_mapping("snes_pal", "SNES");
2140 register_sysregion_mapping("snes_ntsc", "SNES");
2141 register_sysregion_mapping("bsx", "SNES");
2142 register_sysregion_mapping("bsxslotted", "SNES");
2143 register_sysregion_mapping("sufamiturbo", "SNES");
2144 register_sysregion_mapping("sgb_ntsc", "SGB");
2145 register_sysregion_mapping("sgb_pal", "SGB");
2147 } _oninit;