BizHawk/waterbox/vb/vb.cpp

830 lines
18 KiB
C++

/******************************************************************************/
/* Mednafen Virtual Boy Emulation Module */
/******************************************************************************/
/* vb.cpp:
** Copyright (C) 2010-2017 Mednafen Team
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation, Inc.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "vb.h"
#include "../emulibc/emulibc.h"
#include "../emulibc/waterboxcore.h"
#define EXPORT extern "C" ECL_EXPORT
namespace MDFN_IEN_VB
{
struct NativeSyncSettings
{
int InstantReadHack;
int DisableParallax;
};
struct NativeSettings
{
int ThreeDeeMode;
int SwapViews;
int AnaglyphPreset;
int AnaglyphCustomLeftColor;
int AnaglyphCustomRightColor;
int NonAnaglyphColor;
int LedOnScale;
int InterlacePrescale;
int SideBySideSeparation;
};
static void (*input_callback)();
static bool lagged;
enum
{
ANAGLYPH_PRESET_DISABLED = 0,
ANAGLYPH_PRESET_RED_BLUE,
ANAGLYPH_PRESET_RED_CYAN,
ANAGLYPH_PRESET_RED_ELECTRICCYAN,
ANAGLYPH_PRESET_RED_GREEN,
ANAGLYPH_PRESET_GREEN_MAGENTA,
ANAGLYPH_PRESET_YELLOW_BLUE,
};
static const uint32 AnaglyphPreset_Colors[][2] =
{
{0, 0},
{0xFF0000, 0x0000FF},
{0xFF0000, 0x00B7EB},
{0xFF0000, 0x00FFFF},
{0xFF0000, 0x00FF00},
{0x00FF00, 0xFF00FF},
{0xFFFF00, 0x0000FF},
};
static uint32 VB3DMode;
static uint8 *WRAM = NULL;
static uint8 *GPRAM = NULL;
static const uint32 GPRAM_Mask = 0xFFFF;
static uint8 *GPROM = NULL;
static uint32 GPROM_Mask;
V810 *VB_V810 = NULL;
VSU *VB_VSU = NULL;
static uint32 VSU_CycleFix;
static uint8 WCR;
static int32 next_vip_ts, next_timer_ts, next_input_ts;
static uint32 IRQ_Asserted;
static INLINE void RecalcIntLevel(void)
{
int ilevel = -1;
for (int i = 4; i >= 0; i--)
{
if (IRQ_Asserted & (1 << i))
{
ilevel = i;
break;
}
}
VB_V810->SetInt(ilevel);
}
void VBIRQ_Assert(int source, bool assert)
{
assert(source >= 0 && source <= 4);
IRQ_Asserted &= ~(1 << source);
if (assert)
IRQ_Asserted |= 1 << source;
RecalcIntLevel();
}
static MDFN_FASTCALL uint8 HWCTRL_Read(v810_timestamp_t &timestamp, uint32 A)
{
uint8 ret = 0;
if (A & 0x3)
{
//puts("HWCtrl Bogus Read?");
return (ret);
}
switch (A & 0xFF)
{
default: //printf("Unknown HWCTRL Read: %08x\n", A);
break;
case 0x18:
case 0x1C:
case 0x20:
ret = TIMER_Read(timestamp, A);
break;
case 0x24:
ret = WCR | 0xFC;
break;
case 0x10:
case 0x14:
case 0x28:
lagged = false;
if (input_callback)
input_callback();
ret = VBINPUT_Read(timestamp, A);
break;
}
return (ret);
}
static MDFN_FASTCALL void HWCTRL_Write(v810_timestamp_t &timestamp, uint32 A, uint8 V)
{
if (A & 0x3)
{
puts("HWCtrl Bogus Write?");
return;
}
switch (A & 0xFF)
{
default: //printf("Unknown HWCTRL Write: %08x %02x\n", A, V);
break;
case 0x18:
case 0x1C:
case 0x20:
TIMER_Write(timestamp, A, V);
break;
case 0x24:
WCR = V & 0x3;
break;
case 0x10:
case 0x14:
case 0x28:
VBINPUT_Write(timestamp, A, V);
break;
}
}
uint8 MDFN_FASTCALL MemRead8(v810_timestamp_t &timestamp, uint32 A)
{
uint8 ret = 0;
A &= (1 << 27) - 1;
//if((A >> 24) <= 2)
// printf("Read8: %d %08x\n", timestamp, A);
switch (A >> 24)
{
case 0:
ret = VIP_Read8(timestamp, A);
break;
case 1:
break;
case 2:
ret = HWCTRL_Read(timestamp, A);
break;
case 3:
break;
case 4:
break;
case 5:
ret = WRAM[A & 0xFFFF];
break;
case 6:
if (GPRAM)
ret = GPRAM[A & GPRAM_Mask];
break;
case 7:
ret = GPROM[A & GPROM_Mask];
break;
}
return (ret);
}
uint16 MDFN_FASTCALL MemRead16(v810_timestamp_t &timestamp, uint32 A)
{
uint16 ret = 0;
A &= (1 << 27) - 1;
//if((A >> 24) <= 2)
// printf("Read16: %d %08x\n", timestamp, A);
switch (A >> 24)
{
case 0:
ret = VIP_Read16(timestamp, A);
break;
case 1:
break;
case 2:
ret = HWCTRL_Read(timestamp, A);
break;
case 3:
break;
case 4:
break;
case 5:
ret = MDFN_de16lsb<true>(&WRAM[A & 0xFFFF]);
break;
case 6:
if (GPRAM)
ret = MDFN_de16lsb<true>(&GPRAM[A & GPRAM_Mask]);
break;
case 7:
ret = MDFN_de16lsb<true>(&GPROM[A & GPROM_Mask]);
break;
}
return ret;
}
void MDFN_FASTCALL MemWrite8(v810_timestamp_t &timestamp, uint32 A, uint8 V)
{
A &= (1 << 27) - 1;
//if((A >> 24) <= 2)
// printf("Write8: %d %08x %02x\n", timestamp, A, V);
switch (A >> 24)
{
case 0:
VIP_Write8(timestamp, A, V);
break;
case 1:
VB_VSU->Write((timestamp + VSU_CycleFix) >> 2, A, V);
break;
case 2:
HWCTRL_Write(timestamp, A, V);
break;
case 3:
break;
case 4:
break;
case 5:
WRAM[A & 0xFFFF] = V;
break;
case 6:
if (GPRAM)
GPRAM[A & GPRAM_Mask] = V;
break;
case 7: // ROM, no writing allowed!
break;
}
}
void MDFN_FASTCALL MemWrite16(v810_timestamp_t &timestamp, uint32 A, uint16 V)
{
A &= (1 << 27) - 1;
//if((A >> 24) <= 2)
// printf("Write16: %d %08x %04x\n", timestamp, A, V);
switch (A >> 24)
{
case 0:
VIP_Write16(timestamp, A, V);
break;
case 1:
VB_VSU->Write((timestamp + VSU_CycleFix) >> 2, A, V);
break;
case 2:
HWCTRL_Write(timestamp, A, V);
break;
case 3:
break;
case 4:
break;
case 5:
MDFN_en16lsb<true>(&WRAM[A & 0xFFFF], V);
break;
case 6:
if (GPRAM)
MDFN_en16lsb<true>(&GPRAM[A & GPRAM_Mask], V);
break;
case 7: // ROM, no writing allowed!
break;
}
}
static void FixNonEvents(void)
{
if (next_vip_ts & 0x40000000)
next_vip_ts = VB_EVENT_NONONO;
if (next_timer_ts & 0x40000000)
next_timer_ts = VB_EVENT_NONONO;
if (next_input_ts & 0x40000000)
next_input_ts = VB_EVENT_NONONO;
}
static void EventReset(void)
{
next_vip_ts = VB_EVENT_NONONO;
next_timer_ts = VB_EVENT_NONONO;
next_input_ts = VB_EVENT_NONONO;
}
static INLINE int32 CalcNextTS(void)
{
int32 next_timestamp = next_vip_ts;
if (next_timestamp > next_timer_ts)
next_timestamp = next_timer_ts;
if (next_timestamp > next_input_ts)
next_timestamp = next_input_ts;
return (next_timestamp);
}
static void RebaseTS(const v810_timestamp_t timestamp)
{
//printf("Rebase: %08x %08x %08x\n", timestamp, next_vip_ts, next_timer_ts);
assert(next_vip_ts > timestamp);
assert(next_timer_ts > timestamp);
assert(next_input_ts > timestamp);
next_vip_ts -= timestamp;
next_timer_ts -= timestamp;
next_input_ts -= timestamp;
}
void VB_SetEvent(const int type, const v810_timestamp_t next_timestamp)
{
//assert(next_timestamp > VB_V810->v810_timestamp);
if (type == VB_EVENT_VIP)
next_vip_ts = next_timestamp;
else if (type == VB_EVENT_TIMER)
next_timer_ts = next_timestamp;
else if (type == VB_EVENT_INPUT)
next_input_ts = next_timestamp;
if (next_timestamp < VB_V810->GetEventNT())
VB_V810->SetEventNT(next_timestamp);
}
static int32 MDFN_FASTCALL EventHandler(const v810_timestamp_t timestamp)
{
if (timestamp >= next_vip_ts)
next_vip_ts = VIP_Update(timestamp);
if (timestamp >= next_timer_ts)
next_timer_ts = TIMER_Update(timestamp);
if (timestamp >= next_input_ts)
next_input_ts = VBINPUT_Update(timestamp);
return (CalcNextTS());
}
// Called externally from debug.cpp in some cases.
void ForceEventUpdates(const v810_timestamp_t timestamp)
{
next_vip_ts = VIP_Update(timestamp);
next_timer_ts = TIMER_Update(timestamp);
next_input_ts = VBINPUT_Update(timestamp);
VB_V810->SetEventNT(CalcNextTS());
//printf("FEU: %d %d %d\n", next_vip_ts, next_timer_ts, next_input_ts);
}
static void VB_Power(void)
{
memset(WRAM, 0, 65536);
VIP_Power();
VB_VSU->Power();
TIMER_Power();
VBINPUT_Power();
EventReset();
IRQ_Asserted = 0;
RecalcIntLevel();
VB_V810->Reset();
VSU_CycleFix = 0;
WCR = 0;
ForceEventUpdates(0); //VB_V810->v810_timestamp);
}
/*struct VB_HeaderInfo
{
char game_title[256];
uint32 game_code;
uint16 manf_code;
uint8 version;
};*/
/*static void ReadHeader(const uint8 *const rom_data, const uint64 rom_size, VB_HeaderInfo *hi)
{
iconv_t sjis_ict = iconv_open("UTF-8", "shift_jis");
if (sjis_ict != (iconv_t)-1)
{
char *in_ptr, *out_ptr;
size_t ibl, obl;
ibl = 20;
obl = sizeof(hi->game_title) - 1;
in_ptr = (char *)rom_data + (0xFFFFFDE0 & (rom_size - 1));
out_ptr = hi->game_title;
iconv(sjis_ict, (ICONV_CONST char **)&in_ptr, &ibl, &out_ptr, &obl);
iconv_close(sjis_ict);
*out_ptr = 0;
MDFN_zapctrlchars(hi->game_title);
MDFN_trim(hi->game_title);
}
else
hi->game_title[0] = 0;
hi->game_code = MDFN_de32lsb(rom_data + (0xFFFFFDFB & (rom_size - 1)));
hi->manf_code = MDFN_de16lsb(rom_data + (0xFFFFFDF9 & (rom_size - 1)));
hi->version = rom_data[0xFFFFFDFF & (rom_size - 1)];
}*/
void VB_ExitLoop(void)
{
VB_V810->Exit();
}
/*MDFNGI EmulatedVB =
{
PortInfo,
Load,
TestMagic,
NULL,
NULL,
CloseGame,
SetLayerEnableMask,
NULL, // Layer names, null-delimited
NULL,
NULL,
VIP_CPInfo,
1 << 0,
CheatInfo_Empty,
false,
StateAction,
Emulate,
NULL,
VBINPUT_SetInput,
NULL,
DoSimpleCommand,
NULL,
VBSettings,
MDFN_MASTERCLOCK_FIXED(VB_MASTER_CLOCK),
0,
false, // Multires possible?
0, // lcm_width
0, // lcm_height
NULL, // Dummy
384, // Nominal width
224, // Nominal height
384, // Framebuffer width
256, // Framebuffer height
2, // Number of output sound channels
};*/
}
using namespace MDFN_IEN_VB;
EXPORT int Load(const uint8 *rom, int length, const NativeSyncSettings *syncSettings)
{
const uint64 rom_size = length;
V810_Emu_Mode cpu_mode = V810_EMU_MODE_ACCURATE;
if (rom_size != round_up_pow2(rom_size))
{
return 0;
// throw MDFN_Error(0, _("VB ROM image size is not a power of 2."));
}
if (rom_size < 256)
{
return 0;
//throw MDFN_Error(0, _("VB ROM image size is too small."));
}
if (rom_size > (1 << 24))
{
return 0;
//throw MDFN_Error(0, _("VB ROM image size is too large."));
}
VB_V810 = new V810();
VB_V810->Init(cpu_mode, true);
VB_V810->SetMemReadHandlers(MemRead8, MemRead16, NULL);
VB_V810->SetMemWriteHandlers(MemWrite8, MemWrite16, NULL);
VB_V810->SetIOReadHandlers(MemRead8, MemRead16, NULL);
VB_V810->SetIOWriteHandlers(MemWrite8, MemWrite16, NULL);
for (int i = 0; i < 256; i++)
{
VB_V810->SetMemReadBus32(i, false);
VB_V810->SetMemWriteBus32(i, false);
}
std::vector<uint32> Map_Addresses;
for (uint64 A = 0; A < 1ULL << 32; A += (1 << 27))
{
for (uint64 sub_A = 5 << 24; sub_A < (6 << 24); sub_A += 65536)
{
Map_Addresses.push_back(A + sub_A);
}
}
WRAM = VB_V810->SetFastMap(alloc_plain, &Map_Addresses[0], 65536, Map_Addresses.size(), "WRAM");
Map_Addresses.clear();
// Round up the ROM size to 65536(we mirror it a little later)
GPROM_Mask = (rom_size < 65536) ? (65536 - 1) : (rom_size - 1);
for (uint64 A = 0; A < 1ULL << 32; A += (1 << 27))
{
for (uint64 sub_A = 7 << 24; sub_A < (8 << 24); sub_A += GPROM_Mask + 1)
{
Map_Addresses.push_back(A + sub_A);
//printf("%08x\n", (uint32)(A + sub_A));
}
}
GPROM = VB_V810->SetFastMap(alloc_sealed, &Map_Addresses[0], GPROM_Mask + 1, Map_Addresses.size(), "Cart ROM");
Map_Addresses.clear();
memcpy(GPROM, rom, rom_size);
// Mirror ROM images < 64KiB to 64KiB
for (uint64 i = rom_size; i < 65536; i += rom_size)
{
memcpy(GPROM + i, GPROM, rom_size);
}
/*VB_HeaderInfo hinfo;
ReadHeader(GPROM, rom_size, &hinfo);
MDFN_printf(_("Title: %s\n"), hinfo.game_title);
MDFN_printf(_("Game ID Code: %u\n"), hinfo.game_code);
MDFN_printf(_("Manufacturer Code: %d\n"), hinfo.manf_code);
MDFN_printf(_("Version: %u\n"), hinfo.version);
MDFN_printf(_("ROM: %uKiB\n"), (unsigned)(rom_size / 1024));
MDFN_printf(_("ROM MD5: 0x%s\n"), md5_context::asciistr(MDFNGameInfo->MD5, 0).c_str());*/
/*MDFN_printf("\n");
MDFN_printf(_("V810 Emulation Mode: %s\n"), (cpu_mode == V810_EMU_MODE_ACCURATE) ? _("Accurate") : _("Fast"));*/
for (uint64 A = 0; A < 1ULL << 32; A += (1 << 27))
{
for (uint64 sub_A = 6 << 24; sub_A < (7 << 24); sub_A += GPRAM_Mask + 1)
{
//printf("GPRAM: %08x\n", A + sub_A);
Map_Addresses.push_back(A + sub_A);
}
}
GPRAM = VB_V810->SetFastMap(alloc_plain, &Map_Addresses[0], GPRAM_Mask + 1, Map_Addresses.size(), "Cart RAM");
Map_Addresses.clear();
memset(GPRAM, 0, GPRAM_Mask + 1);
VIP_Init();
VB_VSU = new VSU();
VBINPUT_Init();
VB3DMode = 0;
uint32 prescale = 1;
uint32 sbs_separation = 0;
bool reverse = false;
VIP_Set3DMode(VB3DMode, reverse, prescale, sbs_separation);
VIP_SetParallaxDisable(syncSettings->DisableParallax);
{
auto presetColor = 1;
uint32 lcolor = 0xff0000;
uint32 rcolor = 0x00ff00;
if (presetColor != ANAGLYPH_PRESET_DISABLED)
{
lcolor = AnaglyphPreset_Colors[presetColor][0];
rcolor = AnaglyphPreset_Colors[presetColor][1];
}
VIP_SetAnaglyphColors(lcolor, rcolor);
VIP_SetDefaultColor(0xffffff);
}
VBINPUT_SetInstantReadHack(syncSettings->InstantReadHack);
VIP_SetLEDOnScale(1750 / 1000.0);
VB_Power();
/*switch (VB3DMode)
{
default:
break;
case VB3DMODE_VLI:
MDFNGameInfo->nominal_width = 768 * prescale;
MDFNGameInfo->nominal_height = 224;
MDFNGameInfo->fb_width = 768 * prescale;
MDFNGameInfo->fb_height = 224;
break;
case VB3DMODE_HLI:
MDFNGameInfo->nominal_width = 384;
MDFNGameInfo->nominal_height = 448 * prescale;
MDFNGameInfo->fb_width = 384;
MDFNGameInfo->fb_height = 448 * prescale;
break;
case VB3DMODE_CSCOPE:
MDFNGameInfo->nominal_width = 512;
MDFNGameInfo->nominal_height = 384;
MDFNGameInfo->fb_width = 512;
MDFNGameInfo->fb_height = 384;
break;
case VB3DMODE_SIDEBYSIDE:
MDFNGameInfo->nominal_width = 384 * 2 + sbs_separation;
MDFNGameInfo->nominal_height = 224;
MDFNGameInfo->fb_width = 384 * 2 + sbs_separation;
MDFNGameInfo->fb_height = 224;
break;
}
MDFNGameInfo->lcm_width = MDFNGameInfo->fb_width;
MDFNGameInfo->lcm_height = MDFNGameInfo->fb_height;*/
VB_VSU->SetSoundRate(44100);
return 1;
}
EXPORT void SetSettings(const NativeSettings *settings)
{
VB3DMode = settings->ThreeDeeMode;
uint32 prescale = settings->InterlacePrescale;
uint32 sbs_separation = settings->SideBySideSeparation;
bool reverse = settings->SwapViews;
VIP_Set3DMode(VB3DMode, reverse, prescale, sbs_separation);
{
auto presetColor = settings->AnaglyphPreset;
uint32 lcolor = settings->AnaglyphCustomLeftColor;
uint32 rcolor = settings->AnaglyphCustomRightColor;
if (presetColor != ANAGLYPH_PRESET_DISABLED)
{
lcolor = AnaglyphPreset_Colors[presetColor][0];
rcolor = AnaglyphPreset_Colors[presetColor][1];
}
VIP_SetAnaglyphColors(lcolor, rcolor);
VIP_SetDefaultColor(settings->NonAnaglyphColor);
}
VIP_SetLEDOnScale(settings->LedOnScale / 1000.0);
}
EXPORT void GetMemoryAreas(MemoryArea *m)
{
m[0].Data = WRAM;
m[0].Name = "WRAM";
m[0].Size = 65536;
m[0].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_PRIMARY | MEMORYAREA_FLAGS_WORDSIZE4;
m[1].Data = GPRAM;
m[1].Name = "CARTRAM";
m[1].Size = GPRAM_Mask + 1;
m[1].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_SAVERAMMABLE | MEMORYAREA_FLAGS_WORDSIZE4;
m[2].Data = GPROM;
m[2].Name = "ROM";
m[2].Size = GPROM_Mask + 1;
m[2].Flags = MEMORYAREA_FLAGS_WORDSIZE4;
}
EXPORT void FrameAdvance(MyFrameInfo *frame)
{
v810_timestamp_t v810_timestamp;
lagged = true;
VBINPUT_Frame(&frame->Buttons);
VIP_StartFrame(frame);
v810_timestamp = VB_V810->Run(EventHandler);
FixNonEvents();
ForceEventUpdates(v810_timestamp);
frame->Samples = VB_VSU->EndFrame((v810_timestamp + VSU_CycleFix) >> 2, frame->SoundBuffer, 8192);
VSU_CycleFix = (v810_timestamp + VSU_CycleFix) & 3;
frame->Cycles = v810_timestamp;
frame->Lagged = lagged;
TIMER_ResetTS();
VBINPUT_ResetTS();
VIP_ResetTS();
RebaseTS(v810_timestamp);
VB_V810->ResetTS(0);
}
EXPORT void PredictFrameSize(MyFrameInfo *frame)
{
VIP_CalcFrameSize(frame);
}
EXPORT void HardReset()
{
VB_Power();
}
EXPORT void SetInputCallback(void (*callback)())
{
input_callback = callback;
}
int main()
{
return 0;
}