naomi: f355 multiboard support WIP
This commit is contained in:
parent
4f0d3a958d
commit
7e4572a86a
|
@ -797,6 +797,8 @@ target_sources(${PROJECT_NAME} PRIVATE
|
|||
core/hw/naomi/m1cartridge.h
|
||||
core/hw/naomi/m4cartridge.cpp
|
||||
core/hw/naomi/m4cartridge.h
|
||||
core/hw/naomi/multiboard.h
|
||||
core/hw/naomi/multiboard.cpp
|
||||
core/hw/naomi/naomi_cart.cpp
|
||||
core/hw/naomi/naomi_cart.h
|
||||
core/hw/naomi/naomi_flashrom.cpp
|
||||
|
|
122
core/build.h
122
core/build.h
|
@ -1,120 +1,4 @@
|
|||
/*
|
||||
reicast build options
|
||||
|
||||
Reicast can support a lot of stuff, and this is an attempt
|
||||
to organize the build time options
|
||||
|
||||
Option categories
|
||||
|
||||
BUILD_* - BUILD_COMPILER, etc...
|
||||
definitions about the build machine
|
||||
|
||||
HOST_*
|
||||
definitions about the host machine
|
||||
|
||||
FEAT_*
|
||||
definitions about the features that this build targets
|
||||
This is higly related to HOST_*, but it's for options that might
|
||||
or might not be avaiable depending on the target host, or that
|
||||
features that are irrelevant of the host
|
||||
|
||||
Eg, Alsa, Pulse Audio and OSS might sense as HOST dedinitions
|
||||
but it usually makes more sense to detect them as runtime. In
|
||||
that context, HOST_ALSA makes no sense because the host might
|
||||
or might not have alsa installed/ running
|
||||
|
||||
MMU makes no sense as a HOST definition at all, so it should
|
||||
be FEAT_HAS_MMU
|
||||
|
||||
TARGET_*
|
||||
A preconfigured default. Eg TARGET_WIN86.
|
||||
|
||||
Naming of options, option values, and how to use them
|
||||
|
||||
for options that makes sense to have a list of values
|
||||
{CATEGORY}_{OPTION}
|
||||
{OPTION}_{VALUE}
|
||||
|
||||
eg.
|
||||
BUILD_COMPILER == COMPILER_GCC, HOST_CPU != CPU_X64, ...
|
||||
|
||||
for options that are boolean
|
||||
{CATEGORY}_IS_{OPTION} or {CATEGORY}_HAS_{OPTION}
|
||||
|
||||
Evaluates to 0 or 1
|
||||
|
||||
If an configuration cannot be neatly split into a set of
|
||||
of orthogonal options, then it makes sense to break things
|
||||
to "sets" or have a hierarchy of options.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
In the beggining it made sense to have an audio backend
|
||||
per operating system. It made sense to have it depend
|
||||
on HOST_OS and seleect DirectSound or alsa.
|
||||
|
||||
// no option needed
|
||||
|
||||
Then, as android was introduced, which also uses OS_LINUX
|
||||
atm, the audio could have been made an option. It could be
|
||||
a HOST_* option, or FEAT_* one. I'd prefer FEAT_*.
|
||||
FEAT_* makes more sense as future wise we might want
|
||||
to support multiple backends.
|
||||
|
||||
FEAT_AUDIO_BACKEND
|
||||
AUDIO_BACKEND_NONE
|
||||
AUDIO_BACKEND_DS
|
||||
AUDIO_BACKEND_ALSA
|
||||
AUDIO_BACKEND_ANDROID
|
||||
|
||||
Used like
|
||||
#if FEAT_AUDIO_BACKEND == AUDIO_BACKEND_DS ....
|
||||
|
||||
At some point, we might have multiple audio backends that
|
||||
can be compiled in and autodetected/selected at runtime.
|
||||
In that case, it might make sense to have the options like
|
||||
|
||||
FEAT_HAS_ALSA
|
||||
FEAT_HAS_DIRECTSOUND
|
||||
FEAT_HAS_ANDROID_AUDIO
|
||||
|
||||
or
|
||||
FEAT_HAS_AUDIO_ALSA
|
||||
FEAT_HAS_AUDIO_DS
|
||||
FEAT_HAS_AUDIO_ANDROID
|
||||
|
||||
The none option might or might not make sense. In this
|
||||
case it can be removed, as it should always be avaiable.
|
||||
|
||||
Guidelines
|
||||
----------
|
||||
|
||||
General rule of thumb, don't overcomplicate things. Start
|
||||
with a simple option, and then make it more complicated
|
||||
as new uses apply (see the example above)
|
||||
|
||||
Don't use too long names, don't use too cryptic names.
|
||||
Most team developers should be able to understand or
|
||||
figure out most of the acronyms used.
|
||||
|
||||
Try to be consistent on the acronyms across all definitions
|
||||
|
||||
Code shouldn't depend on build level options whenever possible
|
||||
|
||||
Generally, the file should compile even if the option/module is
|
||||
disabled. This makes makefiles etc much easier to write
|
||||
|
||||
TARGET_* options should generally only be used in this file
|
||||
|
||||
The current source is *not* good example of these guidelines
|
||||
|
||||
We'll try to be smart and figure out some options/defaults on this file
|
||||
but this shouldn't get too complicated
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
//#define STRICT_MODE
|
||||
#ifndef STRICT_MODE
|
||||
#define FAST_MMU
|
||||
|
@ -226,6 +110,10 @@
|
|||
#define USE_GGPO
|
||||
#endif
|
||||
|
||||
#if !defined(__ANDROID__) && !defined(TARGET_IPHONE) && !defined(TARGET_UWP) && !defined(__SWITCH__) && !defined(LIBRETRO)
|
||||
#define NAOMI_MULTIBOARD
|
||||
#endif
|
||||
|
||||
// TARGET PLATFORM
|
||||
|
||||
#define RAM_SIZE_MAX (32*1024*1024)
|
||||
|
|
|
@ -146,6 +146,7 @@ Option<bool> GGPOChat("GGPOChat", true, "network");
|
|||
Option<bool> GGPOChatTimeoutToggle("GGPOChatTimeoutToggle", true, "network");
|
||||
Option<int> GGPOChatTimeout("GGPOChatTimeout", 10, "network");
|
||||
Option<bool> NetworkOutput("NetworkOutput", false, "network");
|
||||
Option<int> MultiboardSlaves("MultiboardSlaves", 1, "network");
|
||||
|
||||
#ifdef SUPPORT_DISPMANX
|
||||
Option<bool> DispmanxMaintainAspect("maintain_aspect", true, "dispmanx");
|
||||
|
|
|
@ -507,6 +507,7 @@ extern Option<bool> GGPOChat;
|
|||
extern Option<bool> GGPOChatTimeoutToggle;
|
||||
extern Option<int> GGPOChatTimeout;
|
||||
extern Option<bool> NetworkOutput;
|
||||
extern Option<int> MultiboardSlaves;
|
||||
|
||||
#ifdef SUPPORT_DISPMANX
|
||||
extern Option<bool> DispmanxMaintainAspect;
|
||||
|
|
|
@ -345,6 +345,11 @@ static void loadSpecialSettings()
|
|||
INFO_LOG(BOOT, "Enabling specific JVS setup for game %s", naomi_game_id);
|
||||
settings.input.JammaSetup = JVS::_18Wheeler;
|
||||
}
|
||||
else if (!strcmp("F355 CHALLENGE JAPAN", naomi_game_id))
|
||||
{
|
||||
INFO_LOG(BOOT, "Enabling specific JVS setup for game %s", naomi_game_id);
|
||||
settings.input.JammaSetup = JVS::F355;
|
||||
}
|
||||
else if (!strcmp("INU NO OSANPO", naomi_game_id)) // Dog Walking
|
||||
{
|
||||
INFO_LOG(BOOT, "Enabling specific JVS setup for game %s", naomi_game_id);
|
||||
|
@ -458,6 +463,10 @@ void Emulator::init()
|
|||
|
||||
int getGamePlatform(const char *path)
|
||||
{
|
||||
if (settings.naomi.slave)
|
||||
// Multiboard slave
|
||||
return DC_PLATFORM_NAOMI;
|
||||
|
||||
if (path == NULL)
|
||||
// Dreamcast BIOS
|
||||
return DC_PLATFORM_DREAMCAST;
|
||||
|
@ -547,13 +556,16 @@ void Emulator::loadGame(const char *path, LoadProgress *progress)
|
|||
// Reload the BIOS in case a game-specific region is set
|
||||
naomi_cart_LoadBios(path);
|
||||
}
|
||||
mcfg_DestroyDevices();
|
||||
mcfg_CreateDevices();
|
||||
if (settings.platform.isNaomi()) {
|
||||
// Must be done after the maple devices are created and EEPROM is accessible
|
||||
naomi_cart_ConfigureEEPROM();
|
||||
// and reload settings so that eeprom-based settings can be overridden
|
||||
loadGameSpecificSettings();
|
||||
if (!settings.naomi.slave)
|
||||
{
|
||||
mcfg_DestroyDevices();
|
||||
mcfg_CreateDevices();
|
||||
if (settings.platform.isNaomi()) {
|
||||
// Must be done after the maple devices are created and EEPROM is accessible
|
||||
naomi_cart_ConfigureEEPROM();
|
||||
// and reload settings so that eeprom-based settings can be overridden
|
||||
loadGameSpecificSettings();
|
||||
}
|
||||
}
|
||||
cheatManager.reset(settings.content.gameId);
|
||||
if (cheatManager.isWidescreen())
|
||||
|
@ -567,7 +579,7 @@ void Emulator::loadGame(const char *path, LoadProgress *progress)
|
|||
{
|
||||
if (config::GGPOEnable)
|
||||
dc_loadstate(-1);
|
||||
else if (config::AutoLoadState && !NaomiNetworkSupported())
|
||||
else if (config::AutoLoadState && !NaomiNetworkSupported() && !settings.naomi.multiboard)
|
||||
dc_loadstate(config::SavestateSlot);
|
||||
}
|
||||
EventManager::event(Event::Start);
|
||||
|
@ -625,9 +637,13 @@ void Emulator::unloadGame()
|
|||
stop();
|
||||
if (state == Loaded || state == Error)
|
||||
{
|
||||
if (state == Loaded && config::AutoSaveState && !settings.content.path.empty())
|
||||
if (state == Loaded && config::AutoSaveState && !settings.content.path.empty() && !settings.naomi.multiboard)
|
||||
dc_savestate(config::SavestateSlot);
|
||||
dc_reset(true);
|
||||
try {
|
||||
dc_reset(true);
|
||||
} catch (const FlycastException& e) {
|
||||
ERROR_LOG(COMMON, "%s", e.what());
|
||||
}
|
||||
|
||||
config::Settings::instance().reset();
|
||||
config::Settings::instance().load(false);
|
||||
|
@ -656,7 +672,8 @@ void Emulator::term()
|
|||
}
|
||||
}
|
||||
|
||||
void Emulator::stop() {
|
||||
void Emulator::stop()
|
||||
{
|
||||
if (state != Running)
|
||||
return;
|
||||
state = Loaded;
|
||||
|
@ -780,7 +797,8 @@ void EventManager::registerEvent(Event event, Callback callback, void *param)
|
|||
callbacks.insert({ event, { std::make_pair(callback, param) } });
|
||||
}
|
||||
|
||||
void EventManager::unregisterEvent(Event event, Callback callback, void *param) {
|
||||
void EventManager::unregisterEvent(Event event, Callback callback, void *param)
|
||||
{
|
||||
auto it = callbacks.find(event);
|
||||
if (it == callbacks.end())
|
||||
return;
|
||||
|
@ -792,7 +810,8 @@ void EventManager::unregisterEvent(Event event, Callback callback, void *param)
|
|||
it->second.erase(it2);
|
||||
}
|
||||
|
||||
void EventManager::broadcastEvent(Event event) {
|
||||
void EventManager::broadcastEvent(Event event)
|
||||
{
|
||||
auto it = callbacks.find(event);
|
||||
if (it == callbacks.end())
|
||||
return;
|
||||
|
|
|
@ -187,10 +187,10 @@ static void fixUpDCFlash()
|
|||
|
||||
static bool nvmem_load()
|
||||
{
|
||||
bool rc;
|
||||
bool rc = true;
|
||||
if (settings.platform.isConsole())
|
||||
rc = sys_nvmem->Load(getRomPrefix(), "%nvmem.bin", "nvram");
|
||||
else
|
||||
else if (!settings.naomi.slave)
|
||||
rc = sys_nvmem->Load(hostfs::getArcadeFlashPath() + ".nvmem");
|
||||
if (!rc)
|
||||
INFO_LOG(FLASHROM, "flash/nvmem is missing, will create new file...");
|
||||
|
@ -228,6 +228,8 @@ bool LoadRomFiles()
|
|||
|
||||
void SaveRomFiles()
|
||||
{
|
||||
if (settings.naomi.slave)
|
||||
return;
|
||||
if (settings.platform.isConsole())
|
||||
sys_nvmem->Save(getRomPrefix(), "nvmem.bin", "nvmem");
|
||||
else
|
||||
|
@ -369,10 +371,7 @@ T DYNACALL ReadMem_area0(u32 paddr)
|
|||
default:
|
||||
// G2 Ext area
|
||||
if (System == DC_PLATFORM_NAOMI || System == DC_PLATFORM_NAOMI2)
|
||||
{
|
||||
INFO_LOG(MEMORY, "Read<%d> from G2 Ext area not implemented @ %08x", sz, addr);
|
||||
return (T)0;
|
||||
}
|
||||
return (T)g2ext_readMem(addr, sz);
|
||||
else if (config::EmulateBBA)
|
||||
return (T)bba_ReadMem(addr, sz);
|
||||
else
|
||||
|
@ -485,7 +484,7 @@ void DYNACALL WriteMem_area0(u32 paddr, T data)
|
|||
default:
|
||||
// G2 Ext area
|
||||
if (System == DC_PLATFORM_NAOMI || System == DC_PLATFORM_NAOMI2)
|
||||
INFO_LOG(MEMORY, "Write<%d> to G2 Ext area not implemented @ %08x: %x", sz, addr, (u32)data);
|
||||
g2ext_writeMem(addr, data, sz);
|
||||
else if (config::EmulateBBA)
|
||||
bba_WriteMem(addr, data, sz);
|
||||
return;
|
||||
|
|
|
@ -457,19 +457,73 @@ protected:
|
|||
}
|
||||
};
|
||||
|
||||
class jvs_837_13844_racing : public jvs_837_13844_motor_board
|
||||
{
|
||||
public:
|
||||
jvs_837_13844_racing(u8 node_id, maple_naomi_jamma *parent, int first_player = 0)
|
||||
: jvs_837_13844_motor_board(node_id, parent, first_player)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
u8 process(u8 in) override
|
||||
{
|
||||
in = ~in;
|
||||
// E0: stop motor
|
||||
// E3: roll right
|
||||
// EB: roll left
|
||||
|
||||
// Dn: set wheel high-order?
|
||||
// Cn: set wheel low-order?
|
||||
// 18 wheeler: ff, fe, 3f, 49, 67
|
||||
// d8, c0, e0, d0
|
||||
// 4b, 4a, 9f, 4b, 4d, 4e, 4f, 45, 45, 4f, a3, 4d, ..., 9f, 4c,
|
||||
u8 out = 0;
|
||||
switch (in)
|
||||
{
|
||||
case 0xf0:
|
||||
testMode = true;
|
||||
break;
|
||||
|
||||
case 0xff:
|
||||
testMode = false;
|
||||
break;
|
||||
|
||||
case 0xf1:
|
||||
out = 0x10;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (testMode)
|
||||
out = in;
|
||||
|
||||
// reverse
|
||||
out = (out & 0xF0) >> 4 | (out & 0x0F) << 4;
|
||||
out = (out & 0xCC) >> 2 | (out & 0x33) << 2;
|
||||
out = (out & 0xAA) >> 1 | (out & 0x55) << 1;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
bool testMode = false; // TODO serialize
|
||||
};
|
||||
|
||||
// 18 Wheeler: fake the drive board and limit the wheel analog value
|
||||
class jvs_837_13844_18wheeler : public jvs_837_13844_motor_board
|
||||
class jvs_837_13844_18wheeler : public jvs_837_13844_racing
|
||||
{
|
||||
public:
|
||||
jvs_837_13844_18wheeler(u8 node_id, maple_naomi_jamma *parent, int first_player = 0)
|
||||
: jvs_837_13844_motor_board(node_id, parent, first_player)
|
||||
: jvs_837_13844_racing(node_id, parent, first_player)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void read_digital_in(const u32 *buttons, u16 *v) override
|
||||
{
|
||||
jvs_837_13844_motor_board::read_digital_in(buttons, v);
|
||||
jvs_837_13844_racing::read_digital_in(buttons, v);
|
||||
if (buttons[0] & NAOMI_BTN2_KEY)
|
||||
{
|
||||
gear = -1;
|
||||
|
@ -507,43 +561,14 @@ protected:
|
|||
|
||||
u16 read_analog_axis(int player_num, int player_axis, bool inverted) override
|
||||
{
|
||||
u16 v = jvs_837_13844_motor_board::read_analog_axis(player_num, player_axis, inverted);
|
||||
u16 v = jvs_837_13844_racing::read_analog_axis(player_num, player_axis, inverted);
|
||||
if (player_axis == 0)
|
||||
return std::min<u16>(0xefff, std::max<u16>(0x1000, v));
|
||||
else
|
||||
return v;
|
||||
}
|
||||
|
||||
u8 process(u8 in) override
|
||||
{
|
||||
in = ~in;
|
||||
switch (in)
|
||||
{
|
||||
case 0xf0:
|
||||
testMode = true;
|
||||
break;
|
||||
|
||||
case 0xff:
|
||||
testMode = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
u8 out = 0;
|
||||
if (testMode)
|
||||
out = in;
|
||||
|
||||
// reverse
|
||||
out = (out & 0xF0) >> 4 | (out & 0x0F) << 4;
|
||||
out = (out & 0xCC) >> 2 | (out & 0x33) << 2;
|
||||
out = (out & 0xAA) >> 1 | (out & 0x55) << 1;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
bool testMode = false; // TODO serialize
|
||||
int gear = 0; // 0: low, 1: high, -1: reverse
|
||||
bool transitionWait = false;
|
||||
};
|
||||
|
@ -757,72 +782,78 @@ private:
|
|||
|
||||
maple_naomi_jamma::maple_naomi_jamma()
|
||||
{
|
||||
switch (settings.input.JammaSetup)
|
||||
if (!settings.naomi.slave)
|
||||
{
|
||||
case JVS::Default:
|
||||
default:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551>(new jvs_837_13551(1, this)));
|
||||
break;
|
||||
case JVS::FourPlayers:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551_4P>(new jvs_837_13551_4P(1, this)));
|
||||
break;
|
||||
case JVS::RotaryEncoders:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13938>(new jvs_837_13938(1, this)));
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551>(new jvs_837_13551(2, this)));
|
||||
break;
|
||||
case JVS::OutTrigger:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13938>(new jvs_837_13938(1, this)));
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551_noanalog>(new jvs_837_13551_noanalog(2, this)));
|
||||
break;
|
||||
case JVS::SegaMarineFishing:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13844>(new jvs_837_13844(1, this)));
|
||||
break;
|
||||
case JVS::DualIOBoards4P:
|
||||
if (!strcmp(naomi_game_id, "VIRTUA ATHLETE"))
|
||||
{
|
||||
// reverse the board order so that P1 is P1
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551>(new jvs_837_13551(1, this, 2)));
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551>(new jvs_837_13551(2, this, 0)));
|
||||
}
|
||||
else
|
||||
switch (settings.input.JammaSetup)
|
||||
{
|
||||
case JVS::Default:
|
||||
default:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551>(new jvs_837_13551(1, this)));
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551>(new jvs_837_13551(2, this, 2)));
|
||||
break;
|
||||
case JVS::FourPlayers:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551_4P>(new jvs_837_13551_4P(1, this)));
|
||||
break;
|
||||
case JVS::RotaryEncoders:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13938>(new jvs_837_13938(1, this)));
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551>(new jvs_837_13551(2, this)));
|
||||
break;
|
||||
case JVS::OutTrigger:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13938>(new jvs_837_13938(1, this)));
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551_noanalog>(new jvs_837_13551_noanalog(2, this)));
|
||||
break;
|
||||
case JVS::SegaMarineFishing:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13844>(new jvs_837_13844(1, this)));
|
||||
break;
|
||||
case JVS::DualIOBoards4P:
|
||||
if (!strcmp(naomi_game_id, "VIRTUA ATHLETE"))
|
||||
{
|
||||
// reverse the board order so that P1 is P1
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551>(new jvs_837_13551(1, this, 2)));
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551>(new jvs_837_13551(2, this, 0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551>(new jvs_837_13551(1, this)));
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551>(new jvs_837_13551(2, this, 2)));
|
||||
}
|
||||
break;
|
||||
case JVS::LightGun:
|
||||
io_boards.push_back(std::unique_ptr<jvs_namco_jyu>(new jvs_namco_jyu(1, this)));
|
||||
break;
|
||||
case JVS::LightGunAsAnalog:
|
||||
// Regular board sending lightgun coords as axis 0/1
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551>(new jvs_837_13551(1, this)));
|
||||
io_boards.back()->lightgun_as_analog = true;
|
||||
break;
|
||||
case JVS::Mazan:
|
||||
io_boards.push_back(std::unique_ptr<jvs_namco_fcb>(new jvs_namco_fcb(1, this)));
|
||||
io_boards.push_back(std::unique_ptr<jvs_namco_fcb>(new jvs_namco_fcb(2, this)));
|
||||
break;
|
||||
case JVS::GunSurvivor:
|
||||
io_boards.push_back(std::unique_ptr<jvs_namco_fca>(new jvs_namco_fca(1, this)));
|
||||
break;
|
||||
case JVS::DogWalking:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13844_encoders>(new jvs_837_13844_encoders(1, this)));
|
||||
break;
|
||||
case JVS::TouchDeUno:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13844_touch>(new jvs_837_13844_touch(1, this)));
|
||||
break;
|
||||
case JVS::WorldKicks:
|
||||
io_boards.push_back(std::unique_ptr<jvs_namco_v226>(new jvs_namco_v226(1, this)));
|
||||
break;
|
||||
case JVS::WorldKicksPCB:
|
||||
io_boards.push_back(std::unique_ptr<jvs_namco_v226_pcb>(new jvs_namco_v226_pcb(1, this)));
|
||||
break;
|
||||
case JVS::WaveRunnerGP:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13844_wrungp>(new jvs_837_13844_wrungp(1, this)));
|
||||
break;
|
||||
case JVS::_18Wheeler:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13844_18wheeler>(new jvs_837_13844_18wheeler(1, this)));
|
||||
break;
|
||||
case JVS::F355:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13844_racing>(new jvs_837_13844_racing(1, this)));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case JVS::LightGun:
|
||||
io_boards.push_back(std::unique_ptr<jvs_namco_jyu>(new jvs_namco_jyu(1, this)));
|
||||
break;
|
||||
case JVS::LightGunAsAnalog:
|
||||
// Regular board sending lightgun coords as axis 0/1
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13551>(new jvs_837_13551(1, this)));
|
||||
io_boards.back()->lightgun_as_analog = true;
|
||||
break;
|
||||
case JVS::Mazan:
|
||||
io_boards.push_back(std::unique_ptr<jvs_namco_fcb>(new jvs_namco_fcb(1, this)));
|
||||
io_boards.push_back(std::unique_ptr<jvs_namco_fcb>(new jvs_namco_fcb(2, this)));
|
||||
break;
|
||||
case JVS::GunSurvivor:
|
||||
io_boards.push_back(std::unique_ptr<jvs_namco_fca>(new jvs_namco_fca(1, this)));
|
||||
break;
|
||||
case JVS::DogWalking:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13844_encoders>(new jvs_837_13844_encoders(1, this)));
|
||||
break;
|
||||
case JVS::TouchDeUno:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13844_touch>(new jvs_837_13844_touch(1, this)));
|
||||
break;
|
||||
case JVS::WorldKicks:
|
||||
io_boards.push_back(std::unique_ptr<jvs_namco_v226>(new jvs_namco_v226(1, this)));
|
||||
break;
|
||||
case JVS::WorldKicksPCB:
|
||||
io_boards.push_back(std::unique_ptr<jvs_namco_v226_pcb>(new jvs_namco_v226_pcb(1, this)));
|
||||
break;
|
||||
case JVS::WaveRunnerGP:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13844_wrungp>(new jvs_837_13844_wrungp(1, this)));
|
||||
break;
|
||||
case JVS::_18Wheeler:
|
||||
io_boards.push_back(std::unique_ptr<jvs_837_13844_18wheeler>(new jvs_837_13844_18wheeler(1, this)));
|
||||
break;
|
||||
}
|
||||
|
||||
std::string eeprom_file = hostfs::getArcadeFlashPath() + ".eeprom";
|
||||
|
|
|
@ -22,11 +22,11 @@
|
|||
|
||||
enum
|
||||
{
|
||||
AW_EPR_OFFSETL_addr = 0x00,
|
||||
AW_EPR_OFFSETH_addr = 0x04,
|
||||
AW_MPR_RECORD_INDEX_addr = 0x0C,
|
||||
AW_MPR_FIRST_FILE_INDEX_addr = 0x10,
|
||||
AW_MPR_FILE_OFFSETL_addr = 0x14,
|
||||
AW_MPR_FILE_OFFSETH_addr = 0x18,
|
||||
AW_PIO_DATA_addr = 0x80,
|
||||
AW_EPR_OFFSETL_addr = 0x5f7000,
|
||||
AW_EPR_OFFSETH_addr = 0x5f7004,
|
||||
AW_MPR_RECORD_INDEX_addr = 0x5f700C,
|
||||
AW_MPR_FIRST_FILE_INDEX_addr = 0x5f7010,
|
||||
AW_MPR_FILE_OFFSETL_addr = 0x5f7014,
|
||||
AW_MPR_FILE_OFFSETH_addr = 0x5f7018,
|
||||
AW_PIO_DATA_addr = 0x5f7080,
|
||||
};
|
||||
|
|
|
@ -166,20 +166,8 @@ ROM board internal layouts:
|
|||
|
||||
u32 AWCartridge::ReadMem(u32 address, u32 size) {
|
||||
verify(size != 1);
|
||||
switch(address & 255)
|
||||
switch (address)
|
||||
{
|
||||
// case AW_EPR_OFFSETH_addr:
|
||||
// break;
|
||||
// case AW_EPR_OFFSETL_addr:
|
||||
// break;
|
||||
// case AW_MPR_RECORD_INDEX_addr:
|
||||
// break;
|
||||
// case AW_MPR_FIRST_FILE_INDEX_addr:
|
||||
// break;
|
||||
// case AW_MPR_FILE_OFFSETH_addr:
|
||||
// break;
|
||||
// case AW_MPR_FILE_OFFSETL_addr:
|
||||
// break;
|
||||
case AW_PIO_DATA_addr:
|
||||
{
|
||||
u32 roffset = epr_offset & 0x3ffffff;
|
||||
|
@ -197,7 +185,7 @@ u32 AWCartridge::ReadMem(u32 address, u32 size) {
|
|||
|
||||
void AWCartridge::WriteMem(u32 address, u32 data, u32 size)
|
||||
{
|
||||
switch(address & 255)
|
||||
switch (address)
|
||||
{
|
||||
case AW_EPR_OFFSETH_addr:
|
||||
epr_offset = (epr_offset & 0x0000ffff) | (data << 16);
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
|
||||
u32 data = NaomiCartridge::ReadMem(address, size);
|
||||
|
||||
if ((address & 0xff) == (NAOMI_ROM_OFFSETH_addr & 0xff))
|
||||
if (address == NAOMI_ROM_OFFSETH_addr)
|
||||
// indicates that security PIC is present
|
||||
data |= 0x2000;
|
||||
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
Copyright 2023 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "multiboard.h"
|
||||
|
||||
static Multiboard *multiboard;
|
||||
|
||||
#ifdef NAOMI_MULTIBOARD
|
||||
#include "cfg/cfg.h"
|
||||
#ifndef _WIN32
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include "oslib/oslib.h"
|
||||
#include "naomi_cart.h"
|
||||
#include "naomi_roms.h"
|
||||
#include "cfg/option.h"
|
||||
#include "hw/sh4/sh4_sched.h"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
constexpr int SyncCycles = 500000;
|
||||
|
||||
static int schedCallback(int tag, int cycles, int jitter)
|
||||
{
|
||||
multiboard->syncWait();
|
||||
return SyncCycles;
|
||||
}
|
||||
|
||||
Multiboard::Multiboard()
|
||||
{
|
||||
sharedMem = nullptr;
|
||||
|
||||
boardId = cfgLoadInt("naomi", "BoardId", 0);
|
||||
#ifdef _WIN32
|
||||
const char *FileName = "Local\\naomi_multiboard_mem";
|
||||
if (isMaster())
|
||||
mapFile = CreateFileMapping(
|
||||
INVALID_HANDLE_VALUE, // use paging file
|
||||
NULL, // default security
|
||||
PAGE_READWRITE, // read/write access
|
||||
0, // max. object size (high-order)
|
||||
sizeof(SharedMemory), // max. object size (low-order)
|
||||
FileName); // name of mapping object
|
||||
else
|
||||
mapFile = OpenFileMapping(
|
||||
FILE_MAP_ALL_ACCESS, // read/write access
|
||||
FALSE, // do not inherit the name
|
||||
FileName); // name of mapping object
|
||||
|
||||
if (mapFile == NULL)
|
||||
{
|
||||
ERROR_LOG(NAOMI, "Could not open/create file mapping (%d)", GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO_LOG(NAOMI, "Created shared memory: \"%s\"", FileName);
|
||||
sharedMem = (SharedMemory *) MapViewOfFile(mapFile, // handle to map object
|
||||
FILE_MAP_ALL_ACCESS, // read/write permission
|
||||
0,
|
||||
0,
|
||||
sizeof(SharedMemory));
|
||||
|
||||
if (sharedMem == nullptr)
|
||||
{
|
||||
ERROR_LOG(NAOMI, "Could not map view of file (%d)", GetLastError());
|
||||
CloseHandle(mapFile);
|
||||
mapFile = NULL;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define SHARED_MEM_FILE "/naomi_multiboard_mem"
|
||||
|
||||
int fd = shm_open(SHARED_MEM_FILE, O_RDWR | (isMaster() ? O_CREAT : 0), 0644);
|
||||
if (fd < 0)
|
||||
ERROR_LOG(NAOMI, "Can't open mapped file. errno %d", errno);
|
||||
else
|
||||
{
|
||||
if (isMaster() && ftruncate(fd, sizeof(SharedMemory)))
|
||||
ERROR_LOG(NAOMI, "Can't ftruncate mapped file. errno %d", errno);
|
||||
sharedMem = (SharedMemory *)mmap(nullptr, sizeof(SharedMemory), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
close(fd);
|
||||
}
|
||||
#endif
|
||||
if (sharedMem == nullptr)
|
||||
throw FlycastException("Cannot initialize Naomi multiboard shared memory");
|
||||
if (isMaster())
|
||||
{
|
||||
new (sharedMem) SharedMemory();
|
||||
sharedMem->boardReady[1] = sharedMem->boardReady[2] = sharedMem->boardReady[3] = true;
|
||||
sharedMem->boardSynced[1] = sharedMem->boardSynced[2] = sharedMem->boardSynced[3] = true;
|
||||
}
|
||||
multiboard = this;
|
||||
schedId = sh4_sched_register(0, schedCallback);
|
||||
sh4_sched_request(schedId, SyncCycles);
|
||||
}
|
||||
|
||||
void Multiboard::startSlave()
|
||||
{
|
||||
if (isSlave() || slaveStarted)
|
||||
return;
|
||||
int slaves;
|
||||
if (!strcmp("f355", CurrentCartridge->game->name))
|
||||
slaves = 3;
|
||||
else if (config::MultiboardSlaves >= 2)
|
||||
slaves = CurrentCartridge->game->multiboard;
|
||||
else
|
||||
slaves = 1;
|
||||
boardCount = slaves + 1;
|
||||
for (int i = 0; i < boardCount; i++)
|
||||
sharedMem->boardReady[i] = false;
|
||||
|
||||
int x = cfgLoadInt("window", "left", (1920 - 640) / 2);
|
||||
int y = cfgLoadInt("window", "top", (1080 - 480) / 2);
|
||||
for (int i = 0; i < slaves; i++)
|
||||
{
|
||||
std::string region = "config:Dreamcast.Region=" + std::to_string(config::Region);
|
||||
std::string board = "naomi:BoardId=" + std::to_string(i + 1);
|
||||
int slaveX = x;
|
||||
int slaveY = y + 480;
|
||||
if (slaves == 2) {
|
||||
slaveX = i == 0 ? x - 640 : x + 640;
|
||||
slaveY = y;
|
||||
}
|
||||
else if (slaves == 3)
|
||||
slaveX = i == 1 ? x - 640 : i == 2 ? x + 640 : x;
|
||||
std::string left = "window:left=" + std::to_string(slaveX);
|
||||
std::string top = "window:top=" + std::to_string(slaveY);
|
||||
const char *args[] = {
|
||||
"-config", board.c_str(),
|
||||
"-config", region.c_str(),
|
||||
"-config", left.c_str(),
|
||||
"-config", top.c_str(),
|
||||
CurrentCartridge->game->bios == nullptr ? "naomi" : CurrentCartridge->game->bios
|
||||
};
|
||||
os_RunInstance(ARRAY_SIZE(args), args);
|
||||
}
|
||||
slaveStarted = true;
|
||||
}
|
||||
|
||||
void Multiboard::syncWait()
|
||||
{
|
||||
if (isMaster() && !slaveStarted)
|
||||
return;
|
||||
|
||||
{
|
||||
std::unique_lock<IpcMutex> lock(sharedMem->mutex);
|
||||
sharedMem->boardReady[boardId] = true;
|
||||
sharedMem->boardSynced[boardId] = false;
|
||||
sharedMem->cond.notify_all();
|
||||
}
|
||||
do {
|
||||
if (isSlave() && sharedMem->exit) {
|
||||
NOTICE_LOG(NAOMI, "Slave exiting");
|
||||
throw FlycastException("Slave exit");
|
||||
}
|
||||
{
|
||||
std::unique_lock<IpcMutex> lock(sharedMem->mutex);
|
||||
bool allReady = true;
|
||||
for (const auto& ready : sharedMem->boardReady)
|
||||
if (!ready) {
|
||||
allReady = false;
|
||||
break;
|
||||
}
|
||||
if (allReady) {
|
||||
sharedMem->boardSynced[boardId] = true;
|
||||
sharedMem->cond.notify_all();
|
||||
break;
|
||||
}
|
||||
if (sharedMem->cond.wait_for(lock, std::chrono::seconds(5)) == std::cv_status::timeout) {
|
||||
ERROR_LOG(NAOMI, "Time out waiting for multiboard vsync. Slave %d", isSlave());
|
||||
return;
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
if (isMaster())
|
||||
{
|
||||
do {
|
||||
{
|
||||
std::unique_lock<IpcMutex> lock(sharedMem->mutex);
|
||||
|
||||
bool allSynced = true;
|
||||
for (const auto& synced : sharedMem->boardSynced)
|
||||
if (!synced) {
|
||||
allSynced = false;
|
||||
break;
|
||||
}
|
||||
if (allSynced)
|
||||
{
|
||||
for (int i = 0; i < boardCount; i++)
|
||||
sharedMem->boardReady[i] = false;
|
||||
break;
|
||||
}
|
||||
if (sharedMem->cond.wait_for(lock, std::chrono::seconds(5)) == std::cv_status::timeout) {
|
||||
ERROR_LOG(NAOMI, "Time out waiting for multiboard vsync");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
|
||||
Multiboard::~Multiboard()
|
||||
{
|
||||
if (schedId != -1)
|
||||
sh4_sched_unregister(schedId);
|
||||
multiboard = nullptr;
|
||||
|
||||
if (sharedMem != nullptr)
|
||||
{
|
||||
sharedMem->exit = true;
|
||||
sharedMem->cond.notify_all();
|
||||
if (isMaster())
|
||||
sharedMem->~SharedMemory();
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (sharedMem != nullptr)
|
||||
UnmapViewOfFile(sharedMem);
|
||||
if (mapFile != NULL)
|
||||
CloseHandle(mapFile);
|
||||
#else
|
||||
if (sharedMem != nullptr)
|
||||
munmap(sharedMem, sizeof(SharedMemory));
|
||||
shm_unlink(SHARED_MEM_FILE);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // NAOMI_MULTIBOARD
|
|
@ -0,0 +1,554 @@
|
|||
/*
|
||||
Copyright 2023 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#ifdef NAOMI_MULTIBOARD
|
||||
#include "naomi_regs.h"
|
||||
#include "hw/holly/sb.h"
|
||||
#include "hw/sh4/sh4_mem.h"
|
||||
#include <atomic>
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
|
||||
class IpcMutex
|
||||
{
|
||||
HANDLE mutex;
|
||||
|
||||
public:
|
||||
using native_handle_type = HANDLE;
|
||||
|
||||
IpcMutex()
|
||||
{
|
||||
SECURITY_ATTRIBUTES secattr{ sizeof(SECURITY_ATTRIBUTES) };
|
||||
secattr.bInheritHandle = TRUE;
|
||||
|
||||
mutex = CreateMutex(&secattr, FALSE, NULL);
|
||||
if (mutex == NULL)
|
||||
throw std::runtime_error("CreateMutex failed");
|
||||
}
|
||||
IpcMutex(const IpcMutex&) = delete;
|
||||
|
||||
~IpcMutex() {
|
||||
CloseHandle(mutex);
|
||||
}
|
||||
|
||||
void lock() {
|
||||
WaitForSingleObject(mutex, INFINITE);
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
ReleaseMutex(mutex);
|
||||
}
|
||||
|
||||
native_handle_type native_handle() {
|
||||
return mutex;
|
||||
}
|
||||
|
||||
IpcMutex& operator=(const IpcMutex&) = delete;
|
||||
};
|
||||
|
||||
class IpcConditionVariable
|
||||
{
|
||||
HANDLE semaphore;
|
||||
IpcMutex mutex;
|
||||
int waiters = 0;
|
||||
|
||||
std::cv_status waitMs(std::unique_lock<IpcMutex>& lock, DWORD msecs)
|
||||
{
|
||||
mutex.lock();
|
||||
waiters++;
|
||||
mutex.unlock();
|
||||
|
||||
// The unlock/wait/lock should be atomical so this implementation isn't compliant
|
||||
lock.unlock();
|
||||
DWORD rc = WaitForSingleObject(semaphore, msecs);
|
||||
lock.lock();
|
||||
if (rc == WAIT_ABANDONED || rc == WAIT_FAILED)
|
||||
throw std::runtime_error("Semaphore wait failure");
|
||||
return rc == WAIT_TIMEOUT ? std::cv_status::timeout : std::cv_status::no_timeout;
|
||||
}
|
||||
|
||||
public:
|
||||
IpcConditionVariable()
|
||||
{
|
||||
SECURITY_ATTRIBUTES secattr{ sizeof(SECURITY_ATTRIBUTES) };
|
||||
secattr.bInheritHandle = TRUE;
|
||||
semaphore = CreateSemaphore(&secattr, 0, std::numeric_limits<LONG>::max(), NULL);
|
||||
if (semaphore == NULL)
|
||||
throw std::runtime_error("Semaphore create failed");
|
||||
}
|
||||
|
||||
~IpcConditionVariable()
|
||||
{
|
||||
CloseHandle(semaphore);
|
||||
}
|
||||
|
||||
void notify_all()
|
||||
{
|
||||
mutex.lock();
|
||||
ReleaseSemaphore(semaphore, waiters, NULL);
|
||||
waiters = 0;
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void wait(std::unique_lock<IpcMutex>& lock)
|
||||
{
|
||||
waitMs(lock, INFINITE);
|
||||
}
|
||||
|
||||
template<class Rep, class Period>
|
||||
std::cv_status wait_for(std::unique_lock<IpcMutex>& lock,
|
||||
const std::chrono::duration<Rep, Period>& rel_time)
|
||||
{
|
||||
return waitMs(lock, std::chrono::duration_cast<std::chrono::milliseconds>(rel_time).count());
|
||||
}
|
||||
|
||||
IpcConditionVariable& operator=(const IpcConditionVariable&) = delete;
|
||||
};
|
||||
|
||||
#else // _!WIN32
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
class IpcMutex
|
||||
{
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
public:
|
||||
using native_handle_type = pthread_mutex_t*;
|
||||
|
||||
IpcMutex()
|
||||
{
|
||||
pthread_mutexattr_t mattr;
|
||||
pthread_mutexattr_init(&mattr);
|
||||
pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
|
||||
pthread_mutex_init(&mutex, &mattr);
|
||||
pthread_mutexattr_destroy(&mattr);
|
||||
}
|
||||
IpcMutex(const IpcMutex&) = delete;
|
||||
|
||||
~IpcMutex() {
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
void lock() {
|
||||
pthread_mutex_lock(&mutex);
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
native_handle_type native_handle() {
|
||||
return &mutex;
|
||||
}
|
||||
|
||||
IpcMutex& operator=(const IpcMutex&) = delete;
|
||||
};
|
||||
|
||||
class IpcConditionVariable
|
||||
{
|
||||
pthread_cond_t cond;
|
||||
|
||||
public:
|
||||
IpcConditionVariable()
|
||||
{
|
||||
pthread_condattr_t cvattr;
|
||||
pthread_condattr_init(&cvattr);
|
||||
pthread_condattr_setpshared(&cvattr, PTHREAD_PROCESS_SHARED);
|
||||
pthread_cond_init(&cond, &cvattr);
|
||||
pthread_condattr_destroy(&cvattr);
|
||||
}
|
||||
IpcConditionVariable(const IpcConditionVariable&) = delete;
|
||||
|
||||
~IpcConditionVariable() {
|
||||
pthread_cond_destroy(&cond);
|
||||
}
|
||||
|
||||
void notify_all() {
|
||||
pthread_cond_broadcast(&cond);
|
||||
}
|
||||
|
||||
void wait(std::unique_lock<IpcMutex>& lock) {
|
||||
pthread_cond_wait(&cond, lock.mutex()->native_handle());
|
||||
}
|
||||
|
||||
template<class Rep, class Period>
|
||||
std::cv_status wait_for(std::unique_lock<IpcMutex>& lock,
|
||||
const std::chrono::duration<Rep, Period>& rel_time)
|
||||
{
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
std::chrono::seconds seconds = std::chrono::duration_cast<std::chrono::seconds>(rel_time);
|
||||
ts.tv_sec += seconds.count();
|
||||
auto nanoTime = rel_time - seconds;
|
||||
ts.tv_nsec += std::chrono::nanoseconds(nanoTime).count();
|
||||
if (ts.tv_nsec >= 1000000000) {
|
||||
ts.tv_sec++;
|
||||
ts.tv_nsec -= 1000000000;
|
||||
}
|
||||
int rc = pthread_cond_timedwait(&cond, lock.mutex()->native_handle(), &ts);
|
||||
return rc == ETIMEDOUT ? std::cv_status::timeout : std::cv_status::no_timeout;
|
||||
}
|
||||
|
||||
IpcConditionVariable& operator=(const IpcConditionVariable&) = delete;
|
||||
};
|
||||
#endif // _!WIN32
|
||||
|
||||
class Multiboard
|
||||
{
|
||||
public:
|
||||
static constexpr u32 G1_BASE = 0x05F7080;
|
||||
static constexpr u32 G2_BASE = 0x1010000;
|
||||
|
||||
Multiboard();
|
||||
~Multiboard();
|
||||
|
||||
u32 readG1(u32 addr, u32 size)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case NAOMI_MBOARD_OFFSET_addr:
|
||||
return offset;
|
||||
|
||||
case NAOMI_MBOARD_DATA_addr:
|
||||
{
|
||||
u32 bank = (sharedMem->status & 1) ? 0 : 0x80000;
|
||||
u32 addr;
|
||||
if (isMaster())
|
||||
{
|
||||
bank = 0x80000 - bank;
|
||||
addr = offset + bank / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = offset + (boardId - 1) * 0x10000 + bank / 2;
|
||||
}
|
||||
u16 data = sharedMem->data[addr & (MEM_SIZE - 1)];
|
||||
DEBUG_LOG(NAOMI, "read NAOMI_COMM_DATA[%x]: %x (pc = %x)", addr, data, p_sh4rcb->cntx.pc);
|
||||
offset++;
|
||||
return data;
|
||||
}
|
||||
|
||||
case 0x5f7074:
|
||||
DEBUG_LOG(NAOMI, "5F7074 read: %d", reg74); // loops from 0 to ff
|
||||
return reg74 & 0xff;
|
||||
|
||||
case 0x5f706C:
|
||||
DEBUG_LOG(NAOMI, "5F706C read");
|
||||
return 0; // written to C4 afterwards & 7 except if == 7
|
||||
|
||||
case G1_BASE + 0x08:
|
||||
DEBUG_LOG(NAOMI, "5F7088 read");
|
||||
return 0x80; // loops until bit 7 is set
|
||||
|
||||
case G1_BASE + 0x10:
|
||||
DEBUG_LOG(NAOMI, "5F7090 read");
|
||||
return 0x60; // ? or 0x61 or 0x62
|
||||
|
||||
case G1_BASE + 0x14:
|
||||
DEBUG_LOG(NAOMI, "5F7094 read");
|
||||
return 0x43; // set to 43 before
|
||||
|
||||
default:
|
||||
DEBUG_LOG(NAOMI, "Unknown G1 register read<%d>: %x (pc = %x)", size, addr, p_sh4rcb->cntx.pc);
|
||||
return 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
void writeG1(u32 addr, u32 size, u32 data)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case NAOMI_MBOARD_OFFSET_addr:
|
||||
DEBUG_LOG(NAOMI, "NAOMI_COMM_OFFSET = %x (pc = %x)", data, p_sh4rcb->cntx.pc);
|
||||
offset = data;
|
||||
break;
|
||||
|
||||
case NAOMI_MBOARD_DATA_addr:
|
||||
{
|
||||
DEBUG_LOG(NAOMI, "NAOMI_COMM_DATA[%x] = %x (pc = °%x)", offset, data, p_sh4rcb->cntx.pc);
|
||||
u32 bank = (sharedMem->status & 1) ? 0 : 0x80000;
|
||||
u32 addr;
|
||||
if (isMaster())
|
||||
{
|
||||
bank = 0x80000 - bank;
|
||||
addr = offset + bank / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = offset + (boardId - 1) * 0x10000 + bank / 2;
|
||||
}
|
||||
sharedMem->data[addr & (MEM_SIZE - 1)] = data;
|
||||
offset++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x5f7070:
|
||||
DEBUG_LOG(NAOMI, "5F7070 written: %d", data);
|
||||
break;
|
||||
|
||||
case 0x5f7074:
|
||||
DEBUG_LOG(NAOMI, "5F7074 written: %d", data);
|
||||
reg74 = data;
|
||||
startSlave();
|
||||
break;
|
||||
|
||||
case 0x5f7058: // Set to 0 before DMA operation from multiboard
|
||||
break;
|
||||
|
||||
case NAOMI_MBOARD_STATUS_addr:
|
||||
// Set to 4 after writing most packets
|
||||
if (isSlave())
|
||||
{
|
||||
if ((data & 4) != 0)
|
||||
sharedMem->status.fetch_or(0x10 << boardId);
|
||||
//else
|
||||
// sharedMem->status.fetch_and(~(0x10 << boardId));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_LOG(NAOMI, "Unknown G1 register written<%d>: %x = %x (pc = %x)", size, addr, data, p_sh4rcb->cntx.pc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u32 readG2Ext(u32 addr, u32 size)
|
||||
{
|
||||
//DEBUG_LOG(NAOMI, "g2ext_readMem<%d> %x (pc = %x)", size, addr, p_sh4rcb->cntx.pc);
|
||||
switch (addr)
|
||||
{
|
||||
case G2_BASE + 0x08:
|
||||
return 0x80; // loops until bit 7 is set
|
||||
|
||||
case G2_BASE + 0x10: // similar to 5F7090
|
||||
return 0x60;
|
||||
|
||||
case G2_BASE + 0x14: // similar to 5F7094
|
||||
return 0x43;
|
||||
|
||||
case G2_BASE + 0x94:
|
||||
return 0; // ?
|
||||
case G2_BASE + 0x98:
|
||||
return 0; // ?
|
||||
|
||||
case G2_BASE + 0x9c: // similar to 5F7074
|
||||
return isMaster() ? reg9c : 0;
|
||||
|
||||
case G2_BASE + 0xc0: // similar to 5F706C. need to match!
|
||||
return 0;
|
||||
|
||||
case 0x1008000: // status reg
|
||||
{
|
||||
verify(size == 2);
|
||||
// seems to determine if acting as master or slave
|
||||
if (isSlave())
|
||||
return 0;
|
||||
u16 v = 0xff00 | sharedMem->status;
|
||||
DEBUG_LOG(NAOMI, "g2ext_readMem status_reg %x", v);
|
||||
return v;
|
||||
}
|
||||
|
||||
default:
|
||||
if ((addr - 0x1020000) < MEM_SIZE * 2)
|
||||
{
|
||||
u32 bank = (sharedMem->status & 1) ? 0x80000 : 0;
|
||||
DEBUG_LOG(NAOMI, "g2ext_readMem<%d> %x -> %x", size, addr, sharedMem->data[(addr - 0x1020000 + bank) / 2]);
|
||||
verify(size >= 2);
|
||||
u32 offset;
|
||||
if (isSlave())
|
||||
{
|
||||
return 0;
|
||||
bank = 0x80000 - bank;
|
||||
if (addr >= 0x1040000) {
|
||||
INFO_LOG(NAOMI, "Read shared mem out of bound for slave: %x", addr);
|
||||
break;
|
||||
}
|
||||
offset = (addr - 0x1020000 + bank + (boardId - 1) * 0x20000) / 2;
|
||||
}
|
||||
else
|
||||
offset = (addr - 0x1020000 + bank) / 2;
|
||||
|
||||
if (size == 2)
|
||||
return sharedMem->data[offset];
|
||||
else
|
||||
return *(u32 *)&sharedMem->data[offset];
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void writeG2Ext(u32 addr, u32 size, u32 data)
|
||||
{
|
||||
//DEBUG_LOG(NAOMI, "g2ext_writeMem<%d> %x = %x", size, addr, data);
|
||||
switch (addr) {
|
||||
case 0x1008000: // status reg
|
||||
verify(size == 2);
|
||||
if (isMaster())
|
||||
sharedMem->status = data;
|
||||
DEBUG_LOG(NAOMI, "g2ext_writeMem status_reg %x", data);
|
||||
break;
|
||||
|
||||
case G2_BASE + 0x9c:
|
||||
reg9c = data;
|
||||
break;
|
||||
|
||||
case G2_BASE + 0xa0: // LEDs
|
||||
DEBUG_LOG(NAOMI, "G2 leds %x", data);
|
||||
break;
|
||||
|
||||
default:
|
||||
if ((addr - 0x1020000) < MEM_SIZE * 2)
|
||||
{
|
||||
DEBUG_LOG(NAOMI, "g2ext_writeMem<%d> %x: %x", size, addr, data);
|
||||
verify(size >= 2);
|
||||
u32 bank = (sharedMem->status & 1) ? 0x80000 : 0;
|
||||
if (isSlave())
|
||||
{
|
||||
break;
|
||||
bank = 0x80000 - bank;
|
||||
if (addr >= 0x1040000) {
|
||||
INFO_LOG(NAOMI, "Write shared mem out of bound for slave: %x", addr);
|
||||
break;
|
||||
}
|
||||
u32 offset = (addr - 0x1020000 + bank + (boardId - 1) * 0x20000) / 2;
|
||||
if (size == 2)
|
||||
sharedMem->data[offset] = data;
|
||||
else
|
||||
*(u32 *)&sharedMem->data[offset] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 offset = (addr - 0x1020000 + bank) / 2;
|
||||
if ((sharedMem->status & 2) != 0 || addr >= 0x1040000)
|
||||
{
|
||||
if (size == 2)
|
||||
sharedMem->data[offset] = data;
|
||||
else
|
||||
*(u32 *)&sharedMem->data[offset] = data;
|
||||
}
|
||||
if (addr < 0x1040000) // FIXME this is weird
|
||||
{
|
||||
if ((sharedMem->status & 4) != 0)
|
||||
{
|
||||
if (size == 2)
|
||||
sharedMem->data[offset + 0x10000] = data;
|
||||
else
|
||||
*(u32 *)&sharedMem->data[offset + 0x10000] = data;
|
||||
}
|
||||
if (sharedMem->status & 8)
|
||||
{
|
||||
if (size == 2)
|
||||
sharedMem->data[offset + 0x20000] = data;
|
||||
else
|
||||
*(u32 *)&sharedMem->data[offset + 0x20000] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool dmaStart()
|
||||
{
|
||||
if (isMaster())
|
||||
return false;
|
||||
|
||||
DEBUG_LOG(NAOMI, "Multiboard DMA start addr %08X len %d", SB_GDSTAR, SB_GDLEN);
|
||||
verify(1 == SB_GDDIR);
|
||||
u32 start = SB_GDSTAR & 0x1FFFFFE0;
|
||||
u32 len = (SB_GDLEN + 31) & ~31;
|
||||
u32 bank = (sharedMem->status & 1) ? 0 : 0x80000;
|
||||
u32 *src = (u32 *)&sharedMem->data[(boardId - 1) * 0x10000 + bank / 2];
|
||||
|
||||
WriteMemBlock_nommu_ptr(start, src, len);
|
||||
SB_GDSTARD = start + len;
|
||||
SB_GDLEND = len;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
if (isSlave())
|
||||
sharedMem->status.fetch_and(~(0x10 << boardId));
|
||||
}
|
||||
|
||||
void syncWait();
|
||||
|
||||
private:
|
||||
bool isMaster() const { return boardId == 0; }
|
||||
bool isSlave() const { return boardId != 0; }
|
||||
|
||||
void startSlave();
|
||||
|
||||
static constexpr size_t MEM_SIZE = 0x100000 / sizeof(u16);
|
||||
struct SharedMemory
|
||||
{
|
||||
std::atomic<u16> status;
|
||||
IpcMutex mutex;
|
||||
IpcConditionVariable cond;
|
||||
std::atomic<bool> boardReady[4];
|
||||
std::atomic<bool> boardSynced[4];
|
||||
std::atomic<bool> exit;
|
||||
u16 data[MEM_SIZE];
|
||||
};
|
||||
int boardId = 0;
|
||||
u32 offset = 0;
|
||||
u16 reg74 = 0;
|
||||
u32 reg9c = 0;
|
||||
SharedMemory *sharedMem;
|
||||
#ifdef _WIN32
|
||||
HANDLE mapFile;
|
||||
#endif
|
||||
int boardCount = 0;
|
||||
bool slaveStarted = false;
|
||||
int schedId;
|
||||
};
|
||||
|
||||
#else // !NAOMI_MULTIBOARD
|
||||
|
||||
class Multiboard
|
||||
{
|
||||
public:
|
||||
u32 readG1(u32 addr, u32 size) {
|
||||
return 0;
|
||||
}
|
||||
void writeG1(u32 addr, u32 size, u32 data) { }
|
||||
|
||||
u32 readG2Ext(u32 addr, u32 size) {
|
||||
return 0;
|
||||
}
|
||||
void writeG2Ext(u32 addr, u32 size, u32 data) { }
|
||||
|
||||
bool dmaStart() {
|
||||
return false;
|
||||
}
|
||||
void reset() { }
|
||||
|
||||
void syncWait() { }
|
||||
};
|
||||
|
||||
#endif // !NAOMI_MULTIBOARD
|
|
@ -18,9 +18,8 @@
|
|||
#include "serialize.h"
|
||||
#include "network/output.h"
|
||||
|
||||
//#define NAOMI_COMM
|
||||
|
||||
static NaomiM3Comm m3comm;
|
||||
Multiboard *multiboard;
|
||||
|
||||
static const u32 BoardID = 0x980055AA;
|
||||
static u32 GSerialBuffer, BSerialBuffer;
|
||||
|
@ -31,12 +30,6 @@ static int BControl, BCmd, BLastCmd;
|
|||
static int GControl, GCmd, GLastCmd;
|
||||
static int SerStep, SerStep2;
|
||||
|
||||
#ifdef NAOMI_COMM
|
||||
u32 CommOffset;
|
||||
u32* CommSharedMem;
|
||||
HANDLE CommMapFile=INVALID_HANDLE_VALUE;
|
||||
#endif
|
||||
|
||||
/*
|
||||
El numero de serie solo puede contener:
|
||||
0-9 (0x30-0x39)
|
||||
|
@ -45,6 +38,7 @@ J-N (0x4A-0x4E)
|
|||
P-Z (0x50-0x5A)
|
||||
*/
|
||||
static u8 BSerial[]="\xB7"/*CRC1*/"\x19"/*CRC2*/"0123234437897584372973927387463782196719782697849162342198671923649";
|
||||
//static u8 BSerial[]="\x09\xa1 0000000000000000\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; // default from mame
|
||||
static u8 GSerial[]="\xB7"/*CRC1*/"\x19"/*CRC2*/"0123234437897584372973927387463782196719782697849162342198671923649";
|
||||
|
||||
static u8 midiTxBuf[4];
|
||||
|
@ -278,11 +272,11 @@ static void NaomiGameIDProcessCmd()
|
|||
|
||||
void NaomiGameIDWrite(const u16 Data)
|
||||
{
|
||||
int Dat=Data&0x01;
|
||||
int Clk=Data&0x02;
|
||||
int Rst=Data&0x04;
|
||||
int Sta=Data&0x08;
|
||||
int Cmd=Data&0x10;
|
||||
int Dat=Data&0x01; // mame: SDA
|
||||
int Clk=Data&0x02; // mame: SCL
|
||||
int Rst=Data&0x04; // mame: CS
|
||||
int Sta=Data&0x08; // mame: RST
|
||||
int Cmd=Data&0x10; // mame: unused...
|
||||
|
||||
if(Rst)
|
||||
{
|
||||
|
@ -395,7 +389,7 @@ void naomi_process(u32 command, u32 offsetl, u32 parameterl, u32 parameterh)
|
|||
|
||||
u32 ReadMem_naomi(u32 address, u32 size)
|
||||
{
|
||||
verify(size != 1);
|
||||
// verify(size != 1);
|
||||
if (unlikely(CurrentCartridge == NULL))
|
||||
{
|
||||
INFO_LOG(NAOMI, "called without cartridge");
|
||||
|
@ -432,7 +426,10 @@ static void Naomi_DmaStart(u32 addr, u32 data)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!m3comm.DmaStart(addr, data) && CurrentCartridge != NULL)
|
||||
if (multiboard != nullptr && multiboard->dmaStart())
|
||||
{
|
||||
}
|
||||
else if (!m3comm.DmaStart(addr, data) && CurrentCartridge != NULL)
|
||||
{
|
||||
DEBUG_LOG(NAOMI, "NAOMI-DMA start addr %08X len %d", SB_GDSTAR, SB_GDLEN);
|
||||
verify(1 == SB_GDDIR);
|
||||
|
@ -477,67 +474,15 @@ static void Naomi_DmaEnable(u32 addr, u32 data)
|
|||
|
||||
void naomi_reg_Init()
|
||||
{
|
||||
#ifdef NAOMI_COMM
|
||||
CommMapFile = CreateFileMapping(
|
||||
INVALID_HANDLE_VALUE, // use paging file
|
||||
NULL, // default security
|
||||
PAGE_READWRITE, // read/write access
|
||||
0, // max. object size
|
||||
0x1000*4, // buffer size
|
||||
L"Global\\nullDC_103_naomi_comm"); // name of mapping object
|
||||
|
||||
if (CommMapFile == NULL || CommMapFile==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
_tprintf(TEXT("Could not create file mapping object (%d).\nTrying to open existing one\n"), GetLastError());
|
||||
|
||||
CommMapFile=OpenFileMapping(
|
||||
FILE_MAP_ALL_ACCESS, // read/write access
|
||||
FALSE, // do not inherit the name
|
||||
L"Global\\nullDC_103_naomi_comm"); // name of mapping object
|
||||
}
|
||||
|
||||
if (CommMapFile == NULL || CommMapFile==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
_tprintf(TEXT("Could not open existing file either\n"), GetLastError());
|
||||
CommMapFile=INVALID_HANDLE_VALUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("NAOMI: Created \"Global\\nullDC_103_naomi_comm\"\n");
|
||||
CommSharedMem = (u32*) MapViewOfFile(CommMapFile, // handle to map object
|
||||
FILE_MAP_ALL_ACCESS, // read/write permission
|
||||
0,
|
||||
0,
|
||||
0x1000*4);
|
||||
|
||||
if (CommSharedMem == NULL)
|
||||
{
|
||||
_tprintf(TEXT("Could not map view of file (%d).\n"),
|
||||
GetLastError());
|
||||
|
||||
CloseHandle(CommMapFile);
|
||||
CommMapFile=INVALID_HANDLE_VALUE;
|
||||
}
|
||||
else
|
||||
printf("NAOMI: Mapped CommSharedMem\n");
|
||||
}
|
||||
#endif
|
||||
NaomiInit();
|
||||
networkOutput.init();
|
||||
}
|
||||
|
||||
void naomi_reg_Term()
|
||||
{
|
||||
#ifdef NAOMI_COMM
|
||||
if (CommSharedMem)
|
||||
{
|
||||
UnmapViewOfFile(CommSharedMem);
|
||||
}
|
||||
if (CommMapFile!=INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(CommMapFile);
|
||||
}
|
||||
#endif
|
||||
if (multiboard != nullptr)
|
||||
delete multiboard;
|
||||
multiboard = nullptr;
|
||||
m3comm.closeNetwork();
|
||||
networkOutput.term();
|
||||
}
|
||||
|
@ -573,7 +518,18 @@ void naomi_reg_Reset(bool hard)
|
|||
reg_dimm_status = 0x11;
|
||||
m3comm.closeNetwork();
|
||||
if (hard)
|
||||
{
|
||||
naomi_cart_Close();
|
||||
if (multiboard != nullptr)
|
||||
{
|
||||
delete multiboard;
|
||||
multiboard = nullptr;
|
||||
}
|
||||
if (settings.naomi.multiboard)
|
||||
multiboard = new Multiboard();
|
||||
}
|
||||
else if (multiboard != nullptr)
|
||||
multiboard->reset();
|
||||
}
|
||||
|
||||
static u8 aw_maple_devs;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include "multiboard.h"
|
||||
|
||||
void naomi_reg_Init();
|
||||
void naomi_reg_Term();
|
||||
|
@ -30,3 +31,21 @@ void initMidiForceFeedback();
|
|||
|
||||
u32 libExtDevice_ReadMem_A0_006(u32 addr, u32 size);
|
||||
void libExtDevice_WriteMem_A0_006(u32 addr, u32 data, u32 size);
|
||||
|
||||
extern Multiboard *multiboard;
|
||||
//Area 0 , 0x01000000- 0x01FFFFFF [G2 Ext. Device]
|
||||
static inline u32 g2ext_readMem(u32 addr, u32 size)
|
||||
{
|
||||
if (multiboard != nullptr)
|
||||
return multiboard->readG2Ext(addr, size);
|
||||
|
||||
INFO_LOG(NAOMI, "Unhandled G2 Ext read<%d> at %x", size, addr);
|
||||
return 0;
|
||||
}
|
||||
static inline void g2ext_writeMem(u32 addr, u32 data, u32 size)
|
||||
{
|
||||
if (multiboard != nullptr)
|
||||
multiboard->writeG2Ext(addr, size, data);
|
||||
else
|
||||
INFO_LOG(NAOMI, "Unhandled G2 Ext write<%d> at %x: %x", size, addr, data);
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ static bool loadBios(const char *filename, Archive *child_archive, Archive *pare
|
|||
return false;
|
||||
}
|
||||
|
||||
struct BIOS_t *bios = &BIOS[biosid];
|
||||
const BIOS_t *bios = &BIOS[biosid];
|
||||
|
||||
std::string arch_name(filename);
|
||||
std::string path = hostfs::findNaomiBios(arch_name + ".zip");
|
||||
|
@ -164,7 +164,7 @@ static bool loadBios(const char *filename, Archive *child_archive, Archive *pare
|
|||
return found_region;
|
||||
}
|
||||
|
||||
static Game *FindGame(const char *filename)
|
||||
static const Game *FindGame(const char *filename)
|
||||
{
|
||||
std::string gameName = get_file_basename(filename);
|
||||
size_t folder_pos = get_last_slash_pos(gameName);
|
||||
|
@ -180,7 +180,14 @@ static Game *FindGame(const char *filename)
|
|||
|
||||
void naomi_cart_LoadBios(const char *filename)
|
||||
{
|
||||
Game *game = FindGame(filename);
|
||||
if (settings.naomi.slave)
|
||||
{
|
||||
if (!loadBios(filename, nullptr, nullptr, config::Region))
|
||||
throw NaomiCartException(std::string("Error: cannot load BIOS ") + filename);
|
||||
bios_loaded = true;
|
||||
return;
|
||||
}
|
||||
const Game *game = FindGame(filename);
|
||||
if (game == nullptr)
|
||||
return;
|
||||
|
||||
|
@ -211,7 +218,7 @@ void naomi_cart_LoadBios(const char *filename)
|
|||
|
||||
static void loadMameRom(const char *filename, LoadProgress *progress)
|
||||
{
|
||||
Game *game = FindGame(filename);
|
||||
const Game *game = FindGame(filename);
|
||||
if (game == NULL)
|
||||
throw NaomiCartException("Unknown game");
|
||||
|
||||
|
@ -268,6 +275,7 @@ static void loadMameRom(const char *filename, LoadProgress *progress)
|
|||
}
|
||||
CurrentCartridge->SetKey(game->key);
|
||||
NaomiGameInputs = game->inputs;
|
||||
CurrentCartridge->game = game;
|
||||
|
||||
MD5Sum md5;
|
||||
|
||||
|
@ -565,6 +573,11 @@ void naomi_cart_LoadRom(const char* file, LoadProgress *progress)
|
|||
{
|
||||
naomi_cart_Close();
|
||||
|
||||
if (settings.naomi.slave)
|
||||
{
|
||||
CurrentCartridge = new NaomiCartridge(0);
|
||||
return;
|
||||
}
|
||||
std::string extension = get_file_extension(file);
|
||||
|
||||
if (extension == "zip" || extension == "7z")
|
||||
|
@ -625,22 +638,30 @@ void naomi_cart_Close()
|
|||
|
||||
int naomi_cart_GetPlatform(const char *path)
|
||||
{
|
||||
Game *game = FindGame(path);
|
||||
if (game == NULL)
|
||||
settings.naomi.multiboard = false;
|
||||
const Game *game = FindGame(path);
|
||||
if (game == nullptr)
|
||||
return DC_PLATFORM_NAOMI;
|
||||
else if (game->cart_type == AW)
|
||||
return DC_PLATFORM_ATOMISWAVE;
|
||||
else if (game->bios != nullptr && !strcmp("naomi2", game->bios))
|
||||
return DC_PLATFORM_NAOMI2;
|
||||
else
|
||||
{
|
||||
#ifdef NAOMI_MULTIBOARD
|
||||
if (game->multiboard > 0)
|
||||
settings.naomi.multiboard = true;
|
||||
#endif
|
||||
return DC_PLATFORM_NAOMI;
|
||||
}
|
||||
}
|
||||
|
||||
Cartridge::Cartridge(u32 size)
|
||||
{
|
||||
RomPtr = (u8 *)malloc(size);
|
||||
RomSize = size;
|
||||
memset(RomPtr, 0xFF, RomSize);
|
||||
if (size != 0)
|
||||
memset(RomPtr, 0xFF, RomSize);
|
||||
}
|
||||
|
||||
Cartridge::~Cartridge()
|
||||
|
@ -719,33 +740,33 @@ void* NaomiCartridge::GetDmaPtr(u32& size)
|
|||
|
||||
u32 NaomiCartridge::ReadMem(u32 address, u32 size)
|
||||
{
|
||||
verify(size!=1);
|
||||
// verify(size != 1); not true anymore with multiboard
|
||||
|
||||
switch(address & 255)
|
||||
switch (address)
|
||||
{
|
||||
case 0x3c: // 5f703c: DIMM COMMAND
|
||||
case NAOMI_DIMM_COMMAND:
|
||||
//DEBUG_LOG(NAOMI, "DIMM COMMAND read<%d>", size);
|
||||
return 0xffff; //reg_dimm_command
|
||||
case 0x40: // 5f7040: DIMM OFFSETL
|
||||
case NAOMI_DIMM_OFFSETL:
|
||||
DEBUG_LOG(NAOMI, "DIMM OFFSETL read<%d>", size);
|
||||
return reg_dimm_offsetl;
|
||||
case 0x44: // 5f7044: DIMM PARAMETERL
|
||||
case NAOMI_DIMM_PARAMETERL:
|
||||
DEBUG_LOG(NAOMI, "DIMM PARAMETERL read<%d>", size);
|
||||
return reg_dimm_parameterl;
|
||||
case 0x48: // 5f7048: DIMM PARAMETERH
|
||||
case NAOMI_DIMM_PARAMETERH:
|
||||
DEBUG_LOG(NAOMI, "DIMM PARAMETERH read<%d>", size);
|
||||
return reg_dimm_parameterh;
|
||||
case 0x4c: // 5f704c: DIMM STATUS
|
||||
case NAOMI_DIMM_STATUS:
|
||||
DEBUG_LOG(NAOMI, "DIMM STATUS read<%d>: %x", size, reg_dimm_status);
|
||||
return reg_dimm_status;
|
||||
|
||||
case NAOMI_ROM_OFFSETH_addr&255:
|
||||
return RomPioOffset>>16 | (RomPioAutoIncrement << 15);
|
||||
case NAOMI_ROM_OFFSETH_addr:
|
||||
return RomPioOffset >> 16 | (RomPioAutoIncrement << 15);
|
||||
|
||||
case NAOMI_ROM_OFFSETL_addr&255:
|
||||
return RomPioOffset&0xFFFF;
|
||||
case NAOMI_ROM_OFFSETL_addr:
|
||||
return RomPioOffset & 0xFFFF;
|
||||
|
||||
case NAOMI_ROM_DATA_addr & 255:
|
||||
case NAOMI_ROM_DATA_addr:
|
||||
{
|
||||
u32 rv = 0;
|
||||
Read(RomPioOffset, 2, &rv);
|
||||
|
@ -755,51 +776,40 @@ u32 NaomiCartridge::ReadMem(u32 address, u32 size)
|
|||
return rv;
|
||||
}
|
||||
|
||||
case NAOMI_DMA_COUNT_addr&255:
|
||||
return (u16) DmaCount;
|
||||
case NAOMI_DMA_COUNT_addr:
|
||||
return (u16)DmaCount;
|
||||
|
||||
case NAOMI_BOARDID_READ_addr&255:
|
||||
return NaomiGameIDRead()?0x8000:0x0000;
|
||||
case NAOMI_BOARDID_READ_addr:
|
||||
return NaomiGameIDRead() ? 0x8000 : 0x0000;
|
||||
|
||||
//What should i do to emulate 'nothing' ?
|
||||
case NAOMI_COMM_OFFSET_addr&255:
|
||||
#ifdef NAOMI_COMM
|
||||
DEBUG_LOG(NAOMI, "naomi COMM offs READ: %X, %d", address, size);
|
||||
return CommOffset;
|
||||
#endif
|
||||
case NAOMI_COMM_DATA_addr&255:
|
||||
#ifdef NAOMI_COMM
|
||||
DEBUG_LOG(NAOMI, "naomi COMM data read: %X, %d", CommOffset, size);
|
||||
if (CommSharedMem)
|
||||
{
|
||||
return CommSharedMem[CommOffset&0xF];
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
case NAOMI_DMA_OFFSETH_addr:
|
||||
return DmaOffset >> 16;
|
||||
case NAOMI_DMA_OFFSETL_addr:
|
||||
return DmaOffset & 0xFFFF;
|
||||
|
||||
|
||||
case NAOMI_DMA_OFFSETH_addr&255:
|
||||
return DmaOffset>>16;
|
||||
case NAOMI_DMA_OFFSETL_addr&255:
|
||||
return DmaOffset&0xFFFF;
|
||||
|
||||
case NAOMI_BOARDID_WRITE_addr&255:
|
||||
case NAOMI_BOARDID_WRITE_addr:
|
||||
DEBUG_LOG(NAOMI, "naomi ReadBoardId: %X, %d", address, size);
|
||||
return 1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
DEBUG_LOG(NAOMI, "naomi?WTF? ReadMem: %X, %d", address, size);
|
||||
if (multiboard != nullptr)
|
||||
return multiboard->readG1(address, size);
|
||||
|
||||
return 0xFFFF;
|
||||
if (address == NAOMI_MBOARD_DATA_addr || address == NAOMI_MBOARD_OFFSET_addr)
|
||||
return 1;
|
||||
else {
|
||||
DEBUG_LOG(NAOMI, "naomiCart::ReadMem<%d> unknown: %08x", size, address);
|
||||
return 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
void NaomiCartridge::WriteMem(u32 address, u32 data, u32 size)
|
||||
{
|
||||
switch(address & 255)
|
||||
switch (address)
|
||||
{
|
||||
case 0x3c: // 5f703c: DIMM COMMAND
|
||||
case NAOMI_DIMM_COMMAND:
|
||||
if (0x1E03 == data)
|
||||
{
|
||||
/*
|
||||
|
@ -811,20 +821,20 @@ void NaomiCartridge::WriteMem(u32 address, u32 data, u32 size)
|
|||
DEBUG_LOG(NAOMI, "DIMM COMMAND Write<%d>: %x", size, data);
|
||||
return;
|
||||
|
||||
case 0x40: // 5f7040: DIMM OFFSETL
|
||||
case NAOMI_DIMM_OFFSETL:
|
||||
reg_dimm_offsetl = data;
|
||||
DEBUG_LOG(NAOMI, "DIMM OFFSETL Write<%d>: %x", size, data);
|
||||
return;
|
||||
case 0x44: // 5f7044: DIMM PARAMETERL
|
||||
case NAOMI_DIMM_PARAMETERL:
|
||||
reg_dimm_parameterl = data;
|
||||
DEBUG_LOG(NAOMI, "DIMM PARAMETERL Write<%d>: %x", size, data);
|
||||
return;
|
||||
case 0x48: // 5f7048: DIMM PARAMETERH
|
||||
case NAOMI_DIMM_PARAMETERH:
|
||||
reg_dimm_parameterh = data;
|
||||
DEBUG_LOG(NAOMI, "DIMM PARAMETERH Write<%d>: %x", size, data);
|
||||
return;
|
||||
|
||||
case 0x4C: // 5f704c: DIMM STATUS
|
||||
case NAOMI_DIMM_STATUS:
|
||||
DEBUG_LOG(NAOMI, "DIMM STATUS Write<%d>: %x", size, data);
|
||||
if (data&0x100)
|
||||
{
|
||||
|
@ -841,76 +851,63 @@ void NaomiCartridge::WriteMem(u32 address, u32 data, u32 size)
|
|||
return;
|
||||
|
||||
//These are known to be valid on normal ROMs and DIMM board
|
||||
case NAOMI_ROM_OFFSETH_addr&255:
|
||||
case NAOMI_ROM_OFFSETH_addr:
|
||||
RomPioAutoIncrement = (data & 0x8000) != 0;
|
||||
RomPioOffset&=0x0000ffff;
|
||||
RomPioOffset|=(data<<16)&0x7fff0000;
|
||||
RomPioOffset &= 0x0000ffff;
|
||||
RomPioOffset |= (data << 16) & 0x7fff0000;
|
||||
PioOffsetChanged(RomPioOffset);
|
||||
return;
|
||||
|
||||
case NAOMI_ROM_OFFSETL_addr&255:
|
||||
RomPioOffset&=0xffff0000;
|
||||
RomPioOffset|=data;
|
||||
case NAOMI_ROM_OFFSETL_addr:
|
||||
RomPioOffset &= 0xffff0000;
|
||||
RomPioOffset |= data;
|
||||
PioOffsetChanged(RomPioOffset);
|
||||
return;
|
||||
|
||||
case NAOMI_ROM_DATA_addr&255:
|
||||
case NAOMI_ROM_DATA_addr:
|
||||
Write(RomPioOffset, size, data);
|
||||
if (RomPioAutoIncrement)
|
||||
RomPioOffset += 2;
|
||||
|
||||
return;
|
||||
|
||||
case NAOMI_DMA_OFFSETH_addr&255:
|
||||
DmaOffset&=0x0000ffff;
|
||||
DmaOffset|=(data&0x7fff)<<16;
|
||||
case NAOMI_DMA_OFFSETH_addr:
|
||||
DmaOffset &= 0x0000ffff;
|
||||
DmaOffset |= (data & 0x7fff) << 16;
|
||||
DmaOffsetChanged(DmaOffset);
|
||||
return;
|
||||
|
||||
case NAOMI_DMA_OFFSETL_addr&255:
|
||||
DmaOffset&=0xffff0000;
|
||||
DmaOffset|=data;
|
||||
case NAOMI_DMA_OFFSETL_addr:
|
||||
DmaOffset &= 0xffff0000;
|
||||
DmaOffset |= data;
|
||||
DmaOffsetChanged(DmaOffset);
|
||||
return;
|
||||
|
||||
case NAOMI_DMA_COUNT_addr&255:
|
||||
{
|
||||
DmaCount=data;
|
||||
}
|
||||
case NAOMI_DMA_COUNT_addr:
|
||||
DmaCount = data;
|
||||
return;
|
||||
case NAOMI_BOARDID_WRITE_addr&255:
|
||||
|
||||
case NAOMI_BOARDID_WRITE_addr:
|
||||
NaomiGameIDWrite((u16)data);
|
||||
return;
|
||||
|
||||
//What should i do to emulate 'nothing' ?
|
||||
case NAOMI_COMM_OFFSET_addr&255:
|
||||
#ifdef NAOMI_COMM
|
||||
DEBUG_LOG(NAOMI, "naomi COMM ofset Write: %X <= %X, %d", address, data, size);
|
||||
CommOffset=data&0xFFFF;
|
||||
#endif
|
||||
return;
|
||||
|
||||
case NAOMI_COMM_DATA_addr&255:
|
||||
#ifdef NAOMI_COMM
|
||||
DEBUG_LOG(NAOMI, "naomi COMM data Write: %X <= %X, %d", CommOffset, data, size);
|
||||
if (CommSharedMem)
|
||||
{
|
||||
CommSharedMem[CommOffset&0xF]=data;
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
|
||||
//This should be valid
|
||||
case NAOMI_BOARDID_READ_addr&255:
|
||||
case NAOMI_BOARDID_READ_addr:
|
||||
DEBUG_LOG(NAOMI, "naomi WriteMem: %X <= %X, %d", address, data, size);
|
||||
return;
|
||||
|
||||
case NAOMI_LED_addr & 0xff:
|
||||
case NAOMI_LED_addr:
|
||||
DEBUG_LOG(NAOMI, "LED %d %d %d %d %d %d %d %d", (data >> 7) & 1, (data >> 6) & 1, (data >> 5) & 1, (data >> 4) & 1,
|
||||
(data >> 3) & 1, (data >> 2) & 1, (data >> 1) & 1, data & 1);
|
||||
return;
|
||||
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
DEBUG_LOG(NAOMI, "naomi?WTF? WriteMem: %X <= %X, %d", address, data, size);
|
||||
if (multiboard != nullptr)
|
||||
multiboard->writeG1(address, size, data);
|
||||
else
|
||||
DEBUG_LOG(NAOMI, "naomiCart::WriteMem<%d>: unknown %08x <= %x", size, address, data);
|
||||
}
|
||||
|
||||
void NaomiCartridge::Serialize(Serializer& ser) const
|
||||
|
|
|
@ -43,6 +43,8 @@ struct RomBootID
|
|||
// Note: this structure is copied to system RAM by the BIOS at location 0c01f400
|
||||
};
|
||||
|
||||
struct Game;
|
||||
|
||||
class Cartridge
|
||||
{
|
||||
public:
|
||||
|
@ -67,6 +69,8 @@ public:
|
|||
virtual void SetKeyData(u8 *key_data) { }
|
||||
virtual bool GetBootId(RomBootID *bootId) = 0;
|
||||
|
||||
const Game *game;
|
||||
|
||||
protected:
|
||||
u8* RomPtr;
|
||||
u32 RomSize;
|
||||
|
|
|
@ -105,17 +105,17 @@ void NaomiM3Comm::sendNetwork()
|
|||
|
||||
u32 NaomiM3Comm::ReadMem(u32 address, u32 size)
|
||||
{
|
||||
switch (address & 255)
|
||||
switch (address)
|
||||
{
|
||||
case NAOMI_COMM2_CTRL_addr & 255:
|
||||
case NAOMI_COMM2_CTRL_addr:
|
||||
//DEBUG_LOG(NAOMI, "NAOMI_COMM2_CTRL read");
|
||||
return comm_ctrl;
|
||||
|
||||
case NAOMI_COMM2_OFFSET_addr & 255:
|
||||
case NAOMI_COMM2_OFFSET_addr:
|
||||
//DEBUG_LOG(NAOMI, "NAOMI_COMM2_OFFSET read");
|
||||
return comm_offset;
|
||||
|
||||
case NAOMI_COMM2_DATA_addr & 255:
|
||||
case NAOMI_COMM2_DATA_addr:
|
||||
{
|
||||
u16 value;
|
||||
if (comm_ctrl & COMM_CTRL_CPU_RAM)
|
||||
|
@ -129,11 +129,11 @@ u32 NaomiM3Comm::ReadMem(u32 address, u32 size)
|
|||
return value;
|
||||
}
|
||||
|
||||
case NAOMI_COMM2_STATUS0_addr & 255:
|
||||
case NAOMI_COMM2_STATUS0_addr:
|
||||
DEBUG_LOG(NAOMI, "NAOMI_COMM2_STATUS0 read %x", comm_status0);
|
||||
return comm_status0;
|
||||
|
||||
case NAOMI_COMM2_STATUS1_addr & 255:
|
||||
case NAOMI_COMM2_STATUS1_addr:
|
||||
DEBUG_LOG(NAOMI, "NAOMI_COMM2_STATUS1 read %x", comm_status1);
|
||||
return comm_status1;
|
||||
|
||||
|
@ -170,9 +170,9 @@ void NaomiM3Comm::connectedState()
|
|||
|
||||
void NaomiM3Comm::WriteMem(u32 address, u32 data, u32 size)
|
||||
{
|
||||
switch (address & 255)
|
||||
switch (address)
|
||||
{
|
||||
case NAOMI_COMM2_CTRL_addr & 255:
|
||||
case NAOMI_COMM2_CTRL_addr:
|
||||
// bit 0: access RAM is 0 - communication RAM / 1 - M68K RAM
|
||||
// bit 1: comm RAM bank (seems R/O for SH4)
|
||||
// bit 5: M68K Reset
|
||||
|
@ -192,12 +192,12 @@ void NaomiM3Comm::WriteMem(u32 address, u32 data, u32 size)
|
|||
DEBUG_LOG(NAOMI, "NAOMI_COMM2_CTRL = %x", comm_ctrl);
|
||||
return;
|
||||
|
||||
case NAOMI_COMM2_OFFSET_addr & 255:
|
||||
case NAOMI_COMM2_OFFSET_addr:
|
||||
comm_offset = (u16)data;
|
||||
//DEBUG_LOG(NAOMI, "NAOMI_COMM2_OFFSET set to %x", comm_offset);
|
||||
return;
|
||||
|
||||
case NAOMI_COMM2_DATA_addr & 255:
|
||||
case NAOMI_COMM2_DATA_addr:
|
||||
DEBUG_LOG(NAOMI, "NAOMI_COMM2_DATA written @ %04x %04x", comm_offset, (u16)data);
|
||||
data = swap16(data);
|
||||
if (comm_ctrl & COMM_CTRL_CPU_RAM)
|
||||
|
@ -207,12 +207,12 @@ void NaomiM3Comm::WriteMem(u32 address, u32 data, u32 size)
|
|||
comm_offset += 2;
|
||||
return;
|
||||
|
||||
case NAOMI_COMM2_STATUS0_addr & 255:
|
||||
case NAOMI_COMM2_STATUS0_addr:
|
||||
comm_status0 = (u16)data;
|
||||
//DEBUG_LOG(NAOMI, "NAOMI_COMM2_STATUS0 set to %x", comm_status0);
|
||||
return;
|
||||
|
||||
case NAOMI_COMM2_STATUS1_addr & 255:
|
||||
case NAOMI_COMM2_STATUS1_addr:
|
||||
comm_status1 = (u16)data;
|
||||
//DEBUG_LOG(NAOMI, "NAOMI_COMM2_STATUS1 set to %x", comm_status1);
|
||||
return;
|
||||
|
|
|
@ -19,9 +19,17 @@ enum
|
|||
NAOMI_COMM2_STATUS0_addr = 0x5F7024,
|
||||
NAOMI_COMM2_STATUS1_addr = 0x5F7028,
|
||||
|
||||
NAOMI_DIMM_COMMAND = 0x5f703c,
|
||||
NAOMI_DIMM_OFFSETL = 0x5f7040,
|
||||
NAOMI_DIMM_PARAMETERL = 0x5f7044,
|
||||
NAOMI_DIMM_PARAMETERH = 0x5f7048,
|
||||
NAOMI_DIMM_STATUS = 0x5f704C,
|
||||
|
||||
NAOMI_LED_addr = 0x5F7068,
|
||||
NAOMI_BOARDID_WRITE_addr = 0x5F7078,
|
||||
NAOMI_BOARDID_READ_addr = 0x5F707C,
|
||||
NAOMI_COMM_OFFSET_addr = 0x5F7050,
|
||||
NAOMI_COMM_DATA_addr = 0x5F7054,
|
||||
|
||||
NAOMI_MBOARD_OFFSET_addr = 0x5F7050,
|
||||
NAOMI_MBOARD_DATA_addr = 0x5F7054,
|
||||
NAOMI_MBOARD_STATUS_addr = 0x5F705C,
|
||||
};
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include "naomi_roms_eeprom.h"
|
||||
#include "naomi_roms_input.h"
|
||||
|
||||
BIOS_t BIOS[] =
|
||||
const BIOS_t BIOS[] =
|
||||
{
|
||||
{
|
||||
"airlbios",
|
||||
|
@ -175,7 +175,7 @@ BIOS_t BIOS[] =
|
|||
}
|
||||
};
|
||||
|
||||
Game Games[] =
|
||||
const Game Games[] =
|
||||
{
|
||||
// Naomi M1 Roms
|
||||
// Giant Gram 2000 (JPN, USA, EXP, KOR, AUS)
|
||||
|
@ -825,7 +825,7 @@ Game Games[] =
|
|||
"Airline Pilots (World, Rev B)",
|
||||
0x0b000000,
|
||||
0x28070e41,
|
||||
"naomi",
|
||||
"airlbios",
|
||||
M2,
|
||||
ROT0,
|
||||
{
|
||||
|
@ -859,7 +859,7 @@ Game Games[] =
|
|||
"Airline Pilots (Japan, Rev A)",
|
||||
0x0b000000,
|
||||
0x28070e41,
|
||||
"naomi",
|
||||
"airlbios",
|
||||
M2,
|
||||
ROT0,
|
||||
{
|
||||
|
@ -1676,8 +1676,10 @@ Game Games[] =
|
|||
|
||||
{ NULL, 0, 0 },
|
||||
},
|
||||
NULL,
|
||||
&f355_inputs
|
||||
nullptr,
|
||||
&f355_inputs,
|
||||
nullptr,
|
||||
3,
|
||||
},
|
||||
// Ferrari F355 Challenge (twin)
|
||||
{
|
||||
|
@ -1713,7 +1715,11 @@ Game Games[] =
|
|||
{ "mpr-22846.ic20s", 0xa000000, 0x800000, 0xd4148f39 },
|
||||
{ "mpr-22847.ic21s", 0xa800000, 0x800000, 0x955ad42e },
|
||||
{ NULL, 0, 0 },
|
||||
}
|
||||
},
|
||||
nullptr,
|
||||
&f355_inputs,
|
||||
nullptr,
|
||||
3,
|
||||
},
|
||||
// Ferrari F355 Challenge (twin/deluxe, preview)
|
||||
{
|
||||
|
@ -1749,7 +1755,11 @@ Game Games[] =
|
|||
{ "rom20.ic20s", 0xa000000, 0x800000, 0xd4148f39 },
|
||||
{ "rom21.ic21s", 0xa800000, 0x800000, 0x955ad42e },
|
||||
{ NULL, 0, 0 },
|
||||
}
|
||||
},
|
||||
nullptr,
|
||||
&f355_inputs,
|
||||
nullptr,
|
||||
3,
|
||||
},
|
||||
// Ferrari F355 Challenge 2 (twin)
|
||||
{
|
||||
|
@ -1786,7 +1796,11 @@ Game Games[] =
|
|||
{ "mpr-23397.ic20s", 0xa000000, 0x800000 },
|
||||
{ "mpr-23398.ic21s", 0xa800000, 0x800000 },
|
||||
{ NULL, 0, 0 },
|
||||
}
|
||||
},
|
||||
nullptr,
|
||||
&f355_inputs,
|
||||
nullptr,
|
||||
3,
|
||||
},
|
||||
// Giant Gram: All Japan Pro Wrestling 2 (JPN, USA, EXP, KOR, AUS)
|
||||
{
|
||||
|
|
|
@ -63,7 +63,7 @@ struct BIOS_t
|
|||
u32 src_offset; // For copy
|
||||
} blobs[MAX_GAME_FILES];
|
||||
};
|
||||
extern BIOS_t BIOS[];
|
||||
extern const BIOS_t BIOS[];
|
||||
|
||||
struct InputDescriptors;
|
||||
|
||||
|
@ -89,5 +89,6 @@ struct Game
|
|||
const char *gdrom_name;
|
||||
InputDescriptors *inputs;
|
||||
u8 *eeprom_dump;
|
||||
int multiboard;
|
||||
};
|
||||
extern Game Games[];
|
||||
extern const Game Games[];
|
||||
|
|
|
@ -361,32 +361,28 @@ static InputDescriptors marine_fishing_inputs = {
|
|||
|
||||
static InputDescriptors f355_inputs = {
|
||||
{
|
||||
{ NAOMI_BTN0_KEY, "ASSIST SC" },
|
||||
{ NAOMI_BTN1_KEY, "ASSIST TC" },
|
||||
{ NAOMI_BTN2_KEY, "ASSIST ABS" },
|
||||
{ NAOMI_BTN3_KEY, "ASSIST IBS", 0, NAOMI_BTN1_KEY },
|
||||
{ NAOMI_BTN4_KEY, "WING SHIFT L", 0, NAOMI_DOWN_KEY },
|
||||
{ NAOMI_BTN5_KEY, "WING SHIFT R", 0, NAOMI_UP_KEY },
|
||||
{ NAOMI_UP_KEY, "ASSIST SC" },
|
||||
{ NAOMI_DOWN_KEY, "ASSIST TC" },
|
||||
{ NAOMI_LEFT_KEY, "ASSIST ABS" },
|
||||
{ NAOMI_RIGHT_KEY, "ASSIST IBS" },
|
||||
|
||||
// Manual gearshift (Deluxe only)
|
||||
// L R
|
||||
// U 1 3 5
|
||||
//
|
||||
// D 2 4 6
|
||||
{ NAOMI_UP_KEY, "SPEED SHIFT UP" },
|
||||
{ NAOMI_DOWN_KEY, "SPEED SHIFT DOWN" },
|
||||
{ NAOMI_LEFT_KEY, "SPEED SHIFT LEFT" },
|
||||
{ NAOMI_RIGHT_KEY, "SPEED SHIFT RIGHT" },
|
||||
{ NAOMI_BTN0_KEY, "WING SHIFT L", 0, NAOMI_BTN1_KEY },
|
||||
{ NAOMI_BTN1_KEY, "WING SHIFT R", 0, NAOMI_BTN0_KEY },
|
||||
|
||||
// manual gear shift on P2 DPad
|
||||
// L R
|
||||
// U 2 1
|
||||
// 4 3
|
||||
// D 6 5
|
||||
|
||||
NAO_START_DESC
|
||||
NAO_BASE_BTN_DESC
|
||||
},
|
||||
{
|
||||
{ "HANDLE", Full, 0 },
|
||||
{ "ACCEL", Half, 4 },
|
||||
{ "BRAKE", Half, 5 },
|
||||
{ "CLUTCH", Full, 2 }, // Deluxe only
|
||||
{ "unused", Full, 4 },
|
||||
{ "HANDLE", Full, 0 },
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ bool GamepadDevice::handleButtonInput(int port, DreamcastKey key, bool pressed)
|
|||
break;
|
||||
case EMU_BTN_FFORWARD:
|
||||
if (pressed && !gui_is_open())
|
||||
settings.input.fastForwardMode = !settings.input.fastForwardMode && !settings.network.online;
|
||||
settings.input.fastForwardMode = !settings.input.fastForwardMode && !settings.network.online && !settings.naomi.multiboard;
|
||||
break;
|
||||
case EMU_BTN_INSERT_CARD:
|
||||
if (pressed && settings.platform.isNaomi())
|
||||
|
|
|
@ -294,6 +294,21 @@ std::vector<std::string> find_system_data_dirs()
|
|||
return dirs;
|
||||
}
|
||||
|
||||
static const char *selfPath;
|
||||
|
||||
void os_RunInstance(int argc, const char *argv[])
|
||||
{
|
||||
if (fork() == 0)
|
||||
{
|
||||
std::vector<char *> localArgs;
|
||||
localArgs.push_back((char *)selfPath);
|
||||
for (int i = 0; i < argc; i++)
|
||||
localArgs.push_back((char *)argv[i]);
|
||||
localArgs.push_back(nullptr);
|
||||
execv(selfPath, &localArgs[0]);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(USE_BREAKPAD)
|
||||
static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded)
|
||||
{
|
||||
|
@ -306,6 +321,7 @@ static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
|
|||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
selfPath = argv[0];
|
||||
#if defined(__SWITCH__)
|
||||
socketInitializeDefault();
|
||||
nxlinkStdio();
|
||||
|
|
|
@ -370,6 +370,16 @@ void SetNaomiNetworkConfig(int node)
|
|||
{
|
||||
configure_maxspeed_flash(node != -1, node == 0);
|
||||
}
|
||||
else if (!strcmp("F355 CHALLENGE JAPAN", naomi_game_id))
|
||||
{
|
||||
// FIXME need default flash
|
||||
write_naomi_flash(0x230, node == -1 ? 0 : node == 0 ? 1 : 2);
|
||||
if (node != -1)
|
||||
// car number (0 to 7)
|
||||
write_naomi_flash(0x231, node);
|
||||
// 0x233: cabinet type (0 deluxe, 1 twin)
|
||||
write_naomi_flash(0x233, config::MultiboardSlaves >= 2 ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool NaomiNetworkSupported()
|
||||
|
@ -378,6 +388,7 @@ bool NaomiNetworkSupported()
|
|||
"ALIEN FRONT", "MOBILE SUIT GUNDAM JAPAN", "MOBILE SUIT GUNDAM DELUXE JAPAN", " BIOHAZARD GUN SURVIVOR2",
|
||||
"HEAVY METAL JAPAN", "OUTTRIGGER JAPAN", "SLASHOUT JAPAN VERSION", "SPAWN JAPAN",
|
||||
"SPIKERS BATTLE JAPAN VERSION", "VIRTUAL-ON ORATORIO TANGRAM", "WAVE RUNNER GP", "WORLD KICKS",
|
||||
"F355 CHALLENGE JAPAN",
|
||||
// Naomi 2
|
||||
"CLUB KART IN JAPAN", "INITIAL D", "INITIAL D Ver.2", "INITIAL D Ver.3", "THE KING OF ROUTE66"
|
||||
};
|
||||
|
|
|
@ -31,6 +31,12 @@ int flycast_init(int argc, char* argv[])
|
|||
{
|
||||
return 69;
|
||||
}
|
||||
if (cfgLoadInt("naomi", "BoardId", 0) != 0)
|
||||
{
|
||||
settings.naomi.multiboard = true;
|
||||
settings.naomi.slave = true;
|
||||
}
|
||||
|
||||
config::Settings::instance().reset();
|
||||
LogManager::Shutdown();
|
||||
if (!cfgOpen())
|
||||
|
|
|
@ -13,6 +13,7 @@ void os_SetupInput();
|
|||
void os_TermInput();
|
||||
void os_InstallFaultHandler();
|
||||
void os_UninstallFaultHandler();
|
||||
void os_RunInstance(int argc, const char *argv[]);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
|
|
|
@ -448,7 +448,7 @@ void gui_plot_render_time(int width, int height)
|
|||
void gui_open_settings()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(guiMutex);
|
||||
if (gui_state == GuiState::Closed)
|
||||
if (gui_state == GuiState::Closed && !settings.naomi.slave)
|
||||
{
|
||||
if (!ggpo::active())
|
||||
{
|
||||
|
@ -515,7 +515,7 @@ static void gui_display_commands()
|
|||
|
||||
ImGui::Begin("##commands", NULL, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize);
|
||||
|
||||
bool loadSaveStateDisabled = settings.content.path.empty() || settings.network.online;
|
||||
bool loadSaveStateDisabled = settings.content.path.empty() || settings.network.online || settings.naomi.multiboard;
|
||||
if (loadSaveStateDisabled)
|
||||
{
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
||||
|
@ -2235,6 +2235,11 @@ static void gui_display_settings()
|
|||
OptionCheckbox("Enable UPnP", config::EnableUPnP, "Automatically configure your network router for netplay");
|
||||
OptionCheckbox("Broadcast Digital Outputs", config::NetworkOutput, "Broadcast digital outputs and force-feedback state on TCP port 8000. "
|
||||
"Compatible with the \"-output network\" MAME option. Arcade games only.");
|
||||
ImGui::Text("Multiboard Screens:");
|
||||
//OptionRadioButton<int>("Disabled", config::MultiboardSlaves, 0, "Multiboard disabled (when optional)");
|
||||
OptionRadioButton<int>("1 (Twin)", config::MultiboardSlaves, 1, "One screen configuration (F355 Twin)");
|
||||
ImGui::SameLine();
|
||||
OptionRadioButton<int>("3 (Deluxe)", config::MultiboardSlaves, 2, "Three screens configuration");
|
||||
}
|
||||
ImGui::Spacing();
|
||||
header("Other");
|
||||
|
@ -2797,7 +2802,7 @@ void gui_display_ui()
|
|||
return;
|
||||
if (gui_state == GuiState::Main)
|
||||
{
|
||||
if (!settings.content.path.empty())
|
||||
if (!settings.content.path.empty() || settings.naomi.slave)
|
||||
{
|
||||
#ifndef __ANDROID__
|
||||
commandLineStart = true;
|
||||
|
|
|
@ -651,13 +651,16 @@ void sdl_window_create()
|
|||
void sdl_window_destroy()
|
||||
{
|
||||
#ifndef __SWITCH__
|
||||
get_window_state();
|
||||
cfgSaveInt("window", "left", windowPos.x);
|
||||
cfgSaveInt("window", "top", windowPos.y);
|
||||
cfgSaveInt("window", "width", windowPos.w);
|
||||
cfgSaveInt("window", "height", windowPos.h);
|
||||
cfgSaveBool("window", "maximized", window_maximized);
|
||||
cfgSaveBool("window", "fullscreen", window_fullscreen);
|
||||
if (!settings.naomi.slave)
|
||||
{
|
||||
get_window_state();
|
||||
cfgSaveInt("window", "left", windowPos.x);
|
||||
cfgSaveInt("window", "top", windowPos.y);
|
||||
cfgSaveInt("window", "width", windowPos.w);
|
||||
cfgSaveInt("window", "height", windowPos.h);
|
||||
cfgSaveBool("window", "maximized", window_maximized);
|
||||
cfgSaveBool("window", "fullscreen", window_fullscreen);
|
||||
}
|
||||
#endif
|
||||
termRenderApi();
|
||||
SDL_DestroyWindow(window);
|
||||
|
|
|
@ -197,6 +197,7 @@ enum class JVS {
|
|||
LightGunAsAnalog,
|
||||
WaveRunnerGP,
|
||||
_18Wheeler,
|
||||
F355
|
||||
};
|
||||
|
||||
enum class RenderType {
|
||||
|
@ -304,6 +305,13 @@ struct settings_t
|
|||
u8 vmu[16];
|
||||
} md5;
|
||||
} network;
|
||||
|
||||
struct
|
||||
{
|
||||
bool multiboard;
|
||||
bool slave;
|
||||
} naomi;
|
||||
|
||||
bool disableRenderer;
|
||||
};
|
||||
|
||||
|
|
|
@ -963,3 +963,31 @@ void os_DoEvents()
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void os_RunInstance(int argc, const char *argv[])
|
||||
{
|
||||
char exePath[MAX_PATH];
|
||||
GetModuleFileNameA(NULL, exePath, sizeof(exePath));
|
||||
|
||||
std::string cmdLine(exePath);
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
cmdLine += ' ';
|
||||
cmdLine += argv[i];
|
||||
}
|
||||
|
||||
STARTUPINFO startupInfo{};
|
||||
startupInfo.cb = sizeof(STARTUPINFO);
|
||||
|
||||
PROCESS_INFORMATION processInfo{};
|
||||
BOOL rc = CreateProcessA(exePath, (char *)cmdLine.c_str(), nullptr, nullptr, true, 0, nullptr, nullptr, &startupInfo, &processInfo);
|
||||
if (rc)
|
||||
{
|
||||
CloseHandle(processInfo.hProcess);
|
||||
CloseHandle(processInfo.hThread);
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(BOOT, "Cannot launch Flycast instance: error %d", GetLastError());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,14 +106,6 @@ extern "C" int SDL_main(int argc, char *argv[])
|
|||
if (!file_exists(config_dir))
|
||||
config_dir = std::string(home) + "/Library/Application Support/Flycast/";
|
||||
|
||||
/* Different config folder for multiple instances */
|
||||
int instanceNumber = (int)[[NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.flyinghead.Flycast"] count];
|
||||
if (instanceNumber > 1)
|
||||
{
|
||||
config_dir += std::to_string(instanceNumber) + "/";
|
||||
[[NSApp dockTile] setBadgeLabel:@(instanceNumber).stringValue];
|
||||
}
|
||||
|
||||
mkdir(config_dir.c_str(), 0755); // create the directory if missing
|
||||
set_user_config_dir(config_dir);
|
||||
add_system_data_dir(config_dir);
|
||||
|
@ -182,3 +174,20 @@ std::string os_Locale(){
|
|||
std::string os_PrecomposedString(std::string string){
|
||||
return [[[NSString stringWithUTF8String:string.c_str()] precomposedStringWithCanonicalMapping] UTF8String];
|
||||
}
|
||||
|
||||
void os_RunInstance(int argc, const char *argv[])
|
||||
{
|
||||
if (fork() == 0)
|
||||
{
|
||||
std::vector<char *> localArgs;
|
||||
NSArray *arguments = [[NSProcessInfo processInfo] arguments];
|
||||
const char *selfPath = [[arguments objectAtIndex:0] UTF8String];
|
||||
localArgs.push_back((char *)selfPath);
|
||||
for (int i = 0; i < argc; i++)
|
||||
localArgs.push_back((char *)argv[i]);
|
||||
localArgs.push_back(nullptr);
|
||||
execv(selfPath, &localArgs[0]);
|
||||
ERROR_LOG(BOOT, "Error %d launching Flycast instance %s", errno, selfPath);
|
||||
die("execv failed");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3243,3 +3243,5 @@ void gui_display_notification(const char *msg, int duration)
|
|||
retromsg.frames = duration / 17;
|
||||
environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &retromsg);
|
||||
}
|
||||
|
||||
void os_RunInstance(int argc, const char *argv[]) { }
|
||||
|
|
|
@ -119,6 +119,7 @@ Option<int> GGPODelay("", 0);
|
|||
Option<bool> NetworkStats("", false);
|
||||
Option<int> GGPOAnalogAxes("", 0);
|
||||
Option<bool> NetworkOutput(CORE_OPTION_NAME "_network_output", false);
|
||||
Option<int> MultiboardSlaves("", 0);
|
||||
|
||||
// Maple
|
||||
|
||||
|
|
Loading…
Reference in New Issue