1 /***************************************************************************
2 * Copyright (C) 2012-2013 by Ilari Liusvaara *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License version 2 as *
6 * published by the Free Software Foundation. *
8 * This program is distributed in the hope that it will be useful, *
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
11 * GNU General Public License version 2 for more details. *
13 * You should have received a copy of the GNU General Public License *
14 * version 2 along with this program; if not, write to the *
15 * Free Software Foundation, Inc., *
16 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
17 ***************************************************************************/
24 #include "core/audioapi.hpp"
25 #include "core/misc.hpp"
26 #include "core/command.hpp"
27 #include "core/controllerframe.hpp"
28 #include "core/dispatch.hpp"
29 #include "core/settings.hpp"
30 #include "core/framebuffer.hpp"
31 #include "core/window.hpp"
32 #include "interface/callbacks.hpp"
33 #include "interface/cover.hpp"
34 #include "interface/romtype.hpp"
35 #include "library/pixfmt-rgb32.hpp"
36 #include "library/string.hpp"
37 #include "library/controller-data.hpp"
38 #include "library/serialization.hpp"
39 #include "library/minmax.hpp"
40 #include "library/framebuffer.hpp"
42 #include "libgambatte/include/gambatte.h"
44 #define SAMPLES_PER_FRAME 35112
48 setting_var
<setting_var_model_bool
<setting_yes_no
>> output_native(lsnes_vset
, "gambatte-native-sound",
49 "Gambatteā£Sound Output at native rate", false);
51 bool do_reset_flag
= false;
52 core_type
* internal_rom
= NULL
;
55 gambatte::GB
* instance
;
56 unsigned frame_overflow
= 0;
57 std::vector
<unsigned char> romdata
;
58 uint32_t cover_fbmem
[480 * 432];
59 uint32_t primary_framebuffer
[160*144];
60 uint32_t accumulator_l
= 0;
61 uint32_t accumulator_r
= 0;
62 unsigned accumulator_s
= 0;
66 struct interface_device_reg gb_registers
[] = {
67 {"wrambank", []() -> uint64_t { return instance
? instance
->getIoRam().first
[0x170] & 0x07 : 0; },
69 {"cyclecount", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_CYCLECOUNTER
); },
71 {"pc", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_PC
); },
72 [](uint64_t v
) { instance
->set_cpureg(gambatte::GB::REG_PC
, v
); }},
73 {"sp", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_SP
); },
74 [](uint64_t v
) { instance
->set_cpureg(gambatte::GB::REG_SP
, v
); }},
75 {"hf1", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_HF1
); },
76 [](uint64_t v
) { instance
->set_cpureg(gambatte::GB::REG_HF1
, v
); }},
77 {"hf2", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_HF2
); },
78 [](uint64_t v
) { instance
->set_cpureg(gambatte::GB::REG_HF2
, v
); }},
79 {"zf", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_ZF
); },
80 [](uint64_t v
) { instance
->set_cpureg(gambatte::GB::REG_ZF
, v
); }},
81 {"cf", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_CF
); },
82 [](uint64_t v
) { instance
->set_cpureg(gambatte::GB::REG_CF
, v
); }},
83 {"a", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_A
); },
84 [](uint64_t v
) { instance
->set_cpureg(gambatte::GB::REG_A
, v
); }},
85 {"b", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_B
); },
86 [](uint64_t v
) { instance
->set_cpureg(gambatte::GB::REG_B
, v
); }},
87 {"c", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_C
); },
88 [](uint64_t v
) { instance
->set_cpureg(gambatte::GB::REG_C
, v
); }},
89 {"d", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_D
); },
90 [](uint64_t v
) { instance
->set_cpureg(gambatte::GB::REG_D
, v
); }},
91 {"e", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_E
); },
92 [](uint64_t v
) { instance
->set_cpureg(gambatte::GB::REG_E
, v
); }},
93 {"f", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_F
); },
94 [](uint64_t v
) { instance
->set_cpureg(gambatte::GB::REG_F
, v
); }},
95 {"h", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_H
); },
96 [](uint64_t v
) { instance
->set_cpureg(gambatte::GB::REG_H
, v
); }},
97 {"l", []() -> uint64_t { return instance
->get_cpureg(gambatte::GB::REG_L
); },
98 [](uint64_t v
) { instance
->set_cpureg(gambatte::GB::REG_L
, v
); }},
103 struct framebuffer_info cover_fbinfo
= {
104 &_pixel_format_rgb32
, //Format.
105 (char*)cover_fbmem
, //Memory.
106 480, 432, 1920, //Physical size.
107 480, 432, 1920, //Logical size.
116 return rtc_fixed_val
;
118 return ecore_callbacks
->get_time();
123 class myinput
: public gambatte::InputGetter
126 unsigned operator()()
129 for(unsigned i
= 0; i
< 8; i
++) {
130 if(ecore_callbacks
->get_input(0, 1, i
))
140 static bool done
= false;
144 instance
= new gambatte::GB
;
145 instance
->setInputGetter(&getinput
);
146 instance
->set_walltime_fn(walltime_fn
);
149 int load_rom_common(core_romimage
* img
, unsigned flags
, uint64_t rtc_sec
, uint64_t rtc_subsec
,
153 const char* markup
= img
[0].markup
;
156 flags2
= atoi(markup
);
160 const unsigned char* data
= img
[0].data
;
161 size_t size
= img
[0].size
;
165 memset(instance
, 0, sizeof(gambatte::GB
));
166 new(instance
) gambatte::GB
;
167 instance
->setInputGetter(&getinput
);
168 instance
->set_walltime_fn(walltime_fn
);
169 memset(primary_framebuffer
, 0, sizeof(primary_framebuffer
));
173 rtc_fixed_val
= rtc_sec
;
174 instance
->load(data
, size
, flags
);
176 romdata
.resize(size
);
177 memcpy(&romdata
[0], data
, size
);
178 internal_rom
= inttype
;
179 do_reset_flag
= false;
183 controller_set
gambatte_controllerconfig(std::map
<std::string
, std::string
>& settings
)
185 std::map
<std::string
, std::string
> _settings
= settings
;
187 r
.ports
.push_back(&psystem
);
188 r
.logical_map
.push_back(std::make_pair(0, 1));
192 std::list
<core_vma_info
> get_VMAlist()
194 std::list
<core_vma_info
> vmas
;
200 core_vma_info ioamhram
;
203 auto g
= instance
->getSaveRam();
206 sram
.size
= g
.second
;
207 sram
.backing_ram
= g
.first
;
210 auto g2
= instance
->getWorkRam();
213 wram
.size
= g2
.second
;
214 wram
.backing_ram
= g2
.first
;
217 auto g3
= instance
->getVideoRam();
220 vram
.size
= g3
.second
;
221 vram
.backing_ram
= g3
.first
;
224 auto g4
= instance
->getIoRam();
225 ioamhram
.name
= "IOAMHRAM";
226 ioamhram
.base
= 0x18000;
227 ioamhram
.size
= g4
.second
;
228 ioamhram
.backing_ram
= g4
.first
;
229 ioamhram
.endian
= -1;
232 rom
.base
= 0x80000000;
233 rom
.size
= romdata
.size();
234 rom
.backing_ram
= (void*)&romdata
[0];
239 vmas
.push_back(sram
);
240 vmas
.push_back(wram
);
242 vmas
.push_back(vram
);
243 vmas
.push_back(ioamhram
);
247 std::set
<std::string
> gambatte_srams()
249 std::set
<std::string
> s
;
252 auto g
= instance
->getSaveRam();
259 std::string
get_cartridge_name()
261 std::ostringstream name
;
262 if(romdata
.size() < 0x200)
264 for(unsigned i
= 0; i
< 16; i
++) {
265 if(romdata
[0x134 + i
])
266 name
<< (char)romdata
[0x134 + i
];
273 void redraw_cover_fbinfo();
275 struct _gambatte_core
: public core_core
, public core_region
278 : core_core({&psystem
}, {
279 {0, "Soft reset", "reset", {}},
280 {1, "Change BG palette", "bgpalette", {
281 {"Color 0","string:[0-9A-Fa-f]{6}"},
282 {"Color 1","string:[0-9A-Fa-f]{6}"},
283 {"Color 2","string:[0-9A-Fa-f]{6}"},
284 {"Color 3","string:[0-9A-Fa-f]{6}"}
285 }},{2, "Change SP1 palette", "sp1palette", {
286 {"Color 0","string:[0-9A-Fa-f]{6}"},
287 {"Color 1","string:[0-9A-Fa-f]{6}"},
288 {"Color 2","string:[0-9A-Fa-f]{6}"},
289 {"Color 3","string:[0-9A-Fa-f]{6}"}
290 }}, {3, "Change SP2 palette", "sp2palette", {
291 {"Color 0","string:[0-9A-Fa-f]{6}"},
292 {"Color 1","string:[0-9A-Fa-f]{6}"},
293 {"Color 2","string:[0-9A-Fa-f]{6}"},
294 {"Color 3","string:[0-9A-Fa-f]{6}"}
297 core_region({{"world", "World", 0, 0, false, {4389, 262144}, {0}}}) {}
299 std::string
c_core_identifier() { return "libgambatte "+gambatte::GB::version(); }
300 bool c_set_region(core_region
& region
) { return (®ion
== this); }
301 std::pair
<uint32_t, uint32_t> c_video_rate() { return std::make_pair(262144, 4389); }
302 double c_get_PAR() { return 1.0; }
303 std::pair
<uint32_t, uint32_t> c_audio_rate() {
305 return std::make_pair(2097152, 1);
307 return std::make_pair(32768, 1);
309 std::map
<std::string
, std::vector
<char>> c_save_sram() throw(std::bad_alloc
) {
310 std::map
<std::string
, std::vector
<char>> s
;
313 auto g
= instance
->getSaveRam();
314 s
["main"].resize(g
.second
);
315 memcpy(&s
["main"][0], g
.first
, g
.second
);
317 time_t timebase
= instance
->getRtcBase();
318 for(size_t i
= 0; i
< 8; i
++)
319 s
["rtc"][i
] = ((unsigned long long)timebase
>> (8 * i
));
322 void c_load_sram(std::map
<std::string
, std::vector
<char>>& sram
) throw(std::bad_alloc
) {
325 std::vector
<char> x
= sram
.count("main") ? sram
["main"] : std::vector
<char>();
326 std::vector
<char> x2
= sram
.count("rtc") ? sram
["rtc"] : std::vector
<char>();
327 auto g
= instance
->getSaveRam();
329 if(x
.size() != g
.second
)
330 messages
<< "WARNING: SRAM 'main': Loaded " << x
.size()
331 << " bytes, but the SRAM is " << g
.second
<< "." << std::endl
;
332 memcpy(g
.first
, &x
[0], min(x
.size(), g
.second
));
336 for(size_t i
= 0; i
< 8 && i
< x2
.size(); i
++)
337 timebase
|= (unsigned long long)(unsigned char)x2
[i
] << (8 * i
);
338 instance
->setRtcBase(timebase
);
341 void c_serialize(std::vector
<char>& out
) {
343 throw std::runtime_error("Can't save without ROM");
344 instance
->saveState(out
);
345 size_t osize
= out
.size();
346 out
.resize(osize
+ 4 * sizeof(primary_framebuffer
) / sizeof(primary_framebuffer
[0]));
347 for(size_t i
= 0; i
< sizeof(primary_framebuffer
) / sizeof(primary_framebuffer
[0]); i
++)
348 write32ube(&out
[osize
+ 4 * i
], primary_framebuffer
[i
]);
349 out
.push_back(frame_overflow
>> 8);
350 out
.push_back(frame_overflow
);
352 void c_unserialize(const char* in
, size_t insize
) {
354 throw std::runtime_error("Can't load without ROM");
355 size_t foffset
= insize
- 2 - 4 * sizeof(primary_framebuffer
) /
356 sizeof(primary_framebuffer
[0]);
357 std::vector
<char> tmp
;
359 memcpy(&tmp
[0], in
, foffset
);
360 instance
->loadState(tmp
);
361 for(size_t i
= 0; i
< sizeof(primary_framebuffer
) / sizeof(primary_framebuffer
[0]); i
++)
362 primary_framebuffer
[i
] = read32ube(&in
[foffset
+ 4 * i
]);
364 unsigned x1
= (unsigned char)in
[insize
- 2];
365 unsigned x2
= (unsigned char)in
[insize
- 1];
366 frame_overflow
= x1
* 256 + x2
;
367 do_reset_flag
= false;
369 core_region
& c_get_region() { return *this; }
371 void c_unload_cartridge() {}
372 std::pair
<uint32_t, uint32_t> c_get_scale_factors(uint32_t width
, uint32_t height
) {
373 return std::make_pair(max(512 / width
, (uint32_t)1), max(448 / height
, (uint32_t)1));
375 void c_install_handler() { magic_flags
|= 2; }
376 void c_uninstall_handler() {}
380 bool native_rate
= output_native
;
381 int16_t reset
= ecore_callbacks
->get_input(0, 0, 1);
384 messages
<< "GB(C) reset" << std::endl
;
386 do_reset_flag
= false;
388 uint32_t samplebuffer
[SAMPLES_PER_FRAME
+ 2064];
389 int16_t soundbuf
[2 * (SAMPLES_PER_FRAME
+ 2064)];
392 unsigned samples_emitted
= SAMPLES_PER_FRAME
- frame_overflow
;
393 long ret
= instance
->runFor(primary_framebuffer
, 160, samplebuffer
, samples_emitted
);
395 for(unsigned i
= 0; i
< samples_emitted
; i
++) {
396 soundbuf
[emitted
++] = (int16_t)(samplebuffer
[i
]);
397 soundbuf
[emitted
++] = (int16_t)(samplebuffer
[i
] >> 16);
400 for(unsigned i
= 0; i
< samples_emitted
; i
++) {
401 uint32_t l
= (int32_t)(int16_t)(samplebuffer
[i
]) + 32768;
402 uint32_t r
= (int32_t)(int16_t)(samplebuffer
[i
] >> 16) + 32768;
406 if((accumulator_s
& 63) == 0) {
407 int16_t l2
= (accumulator_l
>> 6) - 32768;
408 int16_t r2
= (accumulator_r
>> 6) - 32768;
409 soundbuf
[emitted
++] = l2
;
410 soundbuf
[emitted
++] = r2
;
411 accumulator_l
= accumulator_r
= 0;
415 ecore_callbacks
->timer_tick(samples_emitted
, 2097152);
416 frame_overflow
+= samples_emitted
;
417 if(frame_overflow
>= SAMPLES_PER_FRAME
) {
418 frame_overflow
-= SAMPLES_PER_FRAME
;
422 framebuffer_info inf
;
423 inf
.type
= &_pixel_format_rgb32
;
424 inf
.mem
= const_cast<char*>(reinterpret_cast<const char*>(primary_framebuffer
));
426 inf
.physheight
= 144;
427 inf
.physstride
= 640;
434 framebuffer_raw
ls(inf
);
435 ecore_callbacks
->output_frame(ls
, 262144, 4389);
436 audioapi_submit_buffer(soundbuf
, emitted
/ 2, true, native_rate
? 2097152 : 32768);
438 void c_runtosave() {}
439 bool c_get_pflag() { return pflag
; }
440 void c_set_pflag(bool _pflag
) { pflag
= _pflag
; }
441 framebuffer_raw
& c_draw_cover() {
442 static framebuffer_raw
x(cover_fbinfo
);
443 redraw_cover_fbinfo();
446 std::string
c_get_core_shortname() { return "gambatte"+gambatte::GB::version(); }
447 void c_pre_emulate_frame(controller_frame
& cf
) {
448 cf
.axis3(0, 0, 1, do_reset_flag
? 1 : 0);
450 void c_execute_action(unsigned id
, const std::vector
<interface_action_paramval
>& p
)
454 case 0: //Soft reset.
455 do_reset_flag
= true;
457 case 1: //Change DMG BG palette.
458 case 2: //Change DMG SP1 palette.
459 case 3: //Change DMG SP2 palette.
460 a
= strtoul(p
[0].s
.c_str(), NULL
, 16);
461 b
= strtoul(p
[1].s
.c_str(), NULL
, 16);
462 c
= strtoul(p
[2].s
.c_str(), NULL
, 16);
463 d
= strtoul(p
[3].s
.c_str(), NULL
, 16);
465 instance
->setDmgPaletteColor(id
- 1, 0, a
);
466 instance
->setDmgPaletteColor(id
- 1, 1, b
);
467 instance
->setDmgPaletteColor(id
- 1, 2, c
);
468 instance
->setDmgPaletteColor(id
- 1, 3, d
);
472 const interface_device_reg
* c_get_registers() { return gb_registers
; }
473 unsigned c_action_flags(unsigned id
) { return (id
< 4) ? 1 : 0; }
474 int c_reset_action(bool hard
) { return hard
? -1 : 0; }
475 std::pair
<uint64_t, uint64_t> c_get_bus_map()
477 return std::make_pair(0, 0);
479 std::list
<core_vma_info
> c_vma_list() { return get_VMAlist(); }
480 std::set
<std::string
> c_srams() { return gambatte_srams(); }
483 struct _type_dmg
: public core_type
, public core_sysregion
490 .sysname
= "Gameboy",
492 .regions
= {&gambatte_core
},
493 .images
= {{"rom", "Cartridge ROM", 1, 0, 0, "gb;dmg"}},
495 .core
= &gambatte_core
,
497 core_sysregion("gdmg", *this, gambatte_core
) {}
499 int t_load_rom(core_romimage
* img
, std::map
<std::string
, std::string
>& settings
,
500 uint64_t secs
, uint64_t subsecs
)
502 return load_rom_common(img
, gambatte::GB::FORCE_DMG
, secs
, subsecs
, this);
504 controller_set
t_controllerconfig(std::map
<std::string
, std::string
>& settings
)
506 return gambatte_controllerconfig(settings
);
510 struct _type_gbc
: public core_type
, public core_sysregion
515 .hname
= "Game Boy Color",
517 .sysname
= "Gameboy",
519 .regions
= {&gambatte_core
},
520 .images
= {{"rom", "Cartridge ROM", 1, 0, 0, "gbc;cgb"}},
522 .core
= &gambatte_core
,
524 core_sysregion("ggbc", *this, gambatte_core
) {}
526 int t_load_rom(core_romimage
* img
, std::map
<std::string
, std::string
>& settings
,
527 uint64_t secs
, uint64_t subsecs
)
529 return load_rom_common(img
, 0, secs
, subsecs
, this);
531 controller_set
t_controllerconfig(std::map
<std::string
, std::string
>& settings
)
533 return gambatte_controllerconfig(settings
);
537 struct _type_gbca
: public core_type
, public core_sysregion
542 .hname
= "Game Boy Color (GBA)",
544 .sysname
= "Gameboy",
546 .regions
= {&gambatte_core
},
547 .images
= {{"rom", "Cartridge ROM", 1, 0, 0, ""}},
549 .core
= &gambatte_core
,
551 core_sysregion("ggbca", *this, gambatte_core
) {}
553 int t_load_rom(core_romimage
* img
, std::map
<std::string
, std::string
>& settings
,
554 uint64_t secs
, uint64_t subsecs
)
556 return load_rom_common(img
, gambatte::GB::GBA_CGB
, secs
, subsecs
, this);
558 controller_set
t_controllerconfig(std::map
<std::string
, std::string
>& settings
)
560 return gambatte_controllerconfig(settings
);
564 void redraw_cover_fbinfo()
566 for(size_t i
= 0; i
< sizeof(cover_fbmem
) / sizeof(cover_fbmem
[0]); i
++)
567 cover_fbmem
[i
] = 0x00000000;
568 std::string ident
= gambatte_core
.get_core_identifier();
569 cover_render_string(cover_fbmem
, 0, 0, ident
, 0xFFFFFF, 0x00000, 480, 432, 1920, 4);
570 cover_render_string(cover_fbmem
, 0, 16, "Internal ROM name: " + get_cartridge_name(),
571 0xFFFFFF, 0x00000, 480, 432, 1920, 4);
573 for(auto i
: cover_information()) {
574 cover_render_string(cover_fbmem
, 0, y
, i
, 0xFFFFFF, 0x000000, 480, 432, 1920, 4);
579 std::vector
<char> cmp_save
;
581 function_ptr_command
<> cmp_save1(lsnes_cmd
, "set-cmp-save", "", "\n", []() throw(std::bad_alloc
,
582 std::runtime_error
) {
585 instance
->saveState(cmp_save
);
588 function_ptr_command
<> cmp_save2(lsnes_cmd
, "do-cmp-save", "", "\n", []() throw(std::bad_alloc
,
589 std::runtime_error
) {
593 instance
->saveState(x
, cmp_save
);
599 register_sysregion_mapping("gdmg", "GB");
600 register_sysregion_mapping("ggbc", "GBC");
601 register_sysregion_mapping("ggbca", "GBC");