From 7e4572a86aab2b5197cde3a71abf75b29ba6474c Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Wed, 11 Jan 2023 22:31:36 +0100 Subject: [PATCH] naomi: f355 multiboard support WIP --- CMakeLists.txt | 2 + core/build.h | 122 +--- core/cfg/option.cpp | 1 + core/cfg/option.h | 1 + core/emulator.cpp | 45 +- core/hw/holly/sb_mem.cpp | 13 +- core/hw/maple/maple_jvs.cpp | 221 ++++--- core/hw/naomi/awave_regs.h | 14 +- core/hw/naomi/awcartridge.cpp | 16 +- core/hw/naomi/m4cartridge.h | 2 +- core/hw/naomi/multiboard.cpp | 245 ++++++++ core/hw/naomi/multiboard.h | 554 ++++++++++++++++++ core/hw/naomi/naomi.cpp | 96 +-- core/hw/naomi/naomi.h | 19 + core/hw/naomi/naomi_cart.cpp | 183 +++--- core/hw/naomi/naomi_cart.h | 4 + core/hw/naomi/naomi_m3comm.cpp | 24 +- core/hw/naomi/naomi_regs.h | 12 +- core/hw/naomi/naomi_roms.cpp | 32 +- core/hw/naomi/naomi_roms.h | 5 +- core/hw/naomi/naomi_roms_input.h | 30 +- core/input/gamepad_device.cpp | 2 +- core/linux-dist/main.cpp | 16 + core/network/naomi_network.cpp | 11 + core/nullDC.cpp | 6 + core/oslib/oslib.h | 1 + core/rend/gui.cpp | 11 +- core/sdl/sdl.cpp | 17 +- core/types.h | 8 + core/windows/winmain.cpp | 28 + .../emulator-osx/emulator-osx/osx-main.mm | 25 +- shell/libretro/libretro.cpp | 2 + shell/libretro/option.cpp | 1 + 33 files changed, 1291 insertions(+), 478 deletions(-) create mode 100644 core/hw/naomi/multiboard.cpp create mode 100644 core/hw/naomi/multiboard.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e58227b0..be0fa3800 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/core/build.h b/core/build.h index 20fa9e132..37c4d9dba 100755 --- a/core/build.h +++ b/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) diff --git a/core/cfg/option.cpp b/core/cfg/option.cpp index 4edafbb5d..771e64384 100644 --- a/core/cfg/option.cpp +++ b/core/cfg/option.cpp @@ -146,6 +146,7 @@ Option GGPOChat("GGPOChat", true, "network"); Option GGPOChatTimeoutToggle("GGPOChatTimeoutToggle", true, "network"); Option GGPOChatTimeout("GGPOChatTimeout", 10, "network"); Option NetworkOutput("NetworkOutput", false, "network"); +Option MultiboardSlaves("MultiboardSlaves", 1, "network"); #ifdef SUPPORT_DISPMANX Option DispmanxMaintainAspect("maintain_aspect", true, "dispmanx"); diff --git a/core/cfg/option.h b/core/cfg/option.h index a02ed806b..51d7582bf 100644 --- a/core/cfg/option.h +++ b/core/cfg/option.h @@ -507,6 +507,7 @@ extern Option GGPOChat; extern Option GGPOChatTimeoutToggle; extern Option GGPOChatTimeout; extern Option NetworkOutput; +extern Option MultiboardSlaves; #ifdef SUPPORT_DISPMANX extern Option DispmanxMaintainAspect; diff --git a/core/emulator.cpp b/core/emulator.cpp index 3338d4523..70f8f3493 100644 --- a/core/emulator.cpp +++ b/core/emulator.cpp @@ -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; diff --git a/core/hw/holly/sb_mem.cpp b/core/hw/holly/sb_mem.cpp index 619095f4e..8b9995e46 100644 --- a/core/hw/holly/sb_mem.cpp +++ b/core/hw/holly/sb_mem.cpp @@ -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; diff --git a/core/hw/maple/maple_jvs.cpp b/core/hw/maple/maple_jvs.cpp index 73843aa2e..f643c9df1 100644 --- a/core/hw/maple/maple_jvs.cpp +++ b/core/hw/maple/maple_jvs.cpp @@ -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(0xefff, std::max(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(new jvs_837_13551(1, this))); - break; - case JVS::FourPlayers: - io_boards.push_back(std::unique_ptr(new jvs_837_13551_4P(1, this))); - break; - case JVS::RotaryEncoders: - io_boards.push_back(std::unique_ptr(new jvs_837_13938(1, this))); - io_boards.push_back(std::unique_ptr(new jvs_837_13551(2, this))); - break; - case JVS::OutTrigger: - io_boards.push_back(std::unique_ptr(new jvs_837_13938(1, this))); - io_boards.push_back(std::unique_ptr(new jvs_837_13551_noanalog(2, this))); - break; - case JVS::SegaMarineFishing: - io_boards.push_back(std::unique_ptr(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(new jvs_837_13551(1, this, 2))); - io_boards.push_back(std::unique_ptr(new jvs_837_13551(2, this, 0))); - } - else + switch (settings.input.JammaSetup) { + case JVS::Default: + default: io_boards.push_back(std::unique_ptr(new jvs_837_13551(1, this))); - io_boards.push_back(std::unique_ptr(new jvs_837_13551(2, this, 2))); + break; + case JVS::FourPlayers: + io_boards.push_back(std::unique_ptr(new jvs_837_13551_4P(1, this))); + break; + case JVS::RotaryEncoders: + io_boards.push_back(std::unique_ptr(new jvs_837_13938(1, this))); + io_boards.push_back(std::unique_ptr(new jvs_837_13551(2, this))); + break; + case JVS::OutTrigger: + io_boards.push_back(std::unique_ptr(new jvs_837_13938(1, this))); + io_boards.push_back(std::unique_ptr(new jvs_837_13551_noanalog(2, this))); + break; + case JVS::SegaMarineFishing: + io_boards.push_back(std::unique_ptr(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(new jvs_837_13551(1, this, 2))); + io_boards.push_back(std::unique_ptr(new jvs_837_13551(2, this, 0))); + } + else + { + io_boards.push_back(std::unique_ptr(new jvs_837_13551(1, this))); + io_boards.push_back(std::unique_ptr(new jvs_837_13551(2, this, 2))); + } + break; + case JVS::LightGun: + io_boards.push_back(std::unique_ptr(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(new jvs_837_13551(1, this))); + io_boards.back()->lightgun_as_analog = true; + break; + case JVS::Mazan: + io_boards.push_back(std::unique_ptr(new jvs_namco_fcb(1, this))); + io_boards.push_back(std::unique_ptr(new jvs_namco_fcb(2, this))); + break; + case JVS::GunSurvivor: + io_boards.push_back(std::unique_ptr(new jvs_namco_fca(1, this))); + break; + case JVS::DogWalking: + io_boards.push_back(std::unique_ptr(new jvs_837_13844_encoders(1, this))); + break; + case JVS::TouchDeUno: + io_boards.push_back(std::unique_ptr(new jvs_837_13844_touch(1, this))); + break; + case JVS::WorldKicks: + io_boards.push_back(std::unique_ptr(new jvs_namco_v226(1, this))); + break; + case JVS::WorldKicksPCB: + io_boards.push_back(std::unique_ptr(new jvs_namco_v226_pcb(1, this))); + break; + case JVS::WaveRunnerGP: + io_boards.push_back(std::unique_ptr(new jvs_837_13844_wrungp(1, this))); + break; + case JVS::_18Wheeler: + io_boards.push_back(std::unique_ptr(new jvs_837_13844_18wheeler(1, this))); + break; + case JVS::F355: + io_boards.push_back(std::unique_ptr(new jvs_837_13844_racing(1, this))); + break; } - break; - case JVS::LightGun: - io_boards.push_back(std::unique_ptr(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(new jvs_837_13551(1, this))); - io_boards.back()->lightgun_as_analog = true; - break; - case JVS::Mazan: - io_boards.push_back(std::unique_ptr(new jvs_namco_fcb(1, this))); - io_boards.push_back(std::unique_ptr(new jvs_namco_fcb(2, this))); - break; - case JVS::GunSurvivor: - io_boards.push_back(std::unique_ptr(new jvs_namco_fca(1, this))); - break; - case JVS::DogWalking: - io_boards.push_back(std::unique_ptr(new jvs_837_13844_encoders(1, this))); - break; - case JVS::TouchDeUno: - io_boards.push_back(std::unique_ptr(new jvs_837_13844_touch(1, this))); - break; - case JVS::WorldKicks: - io_boards.push_back(std::unique_ptr(new jvs_namco_v226(1, this))); - break; - case JVS::WorldKicksPCB: - io_boards.push_back(std::unique_ptr(new jvs_namco_v226_pcb(1, this))); - break; - case JVS::WaveRunnerGP: - io_boards.push_back(std::unique_ptr(new jvs_837_13844_wrungp(1, this))); - break; - case JVS::_18Wheeler: - io_boards.push_back(std::unique_ptr(new jvs_837_13844_18wheeler(1, this))); - break; } std::string eeprom_file = hostfs::getArcadeFlashPath() + ".eeprom"; diff --git a/core/hw/naomi/awave_regs.h b/core/hw/naomi/awave_regs.h index 0b9f1ce3c..d958089e6 100644 --- a/core/hw/naomi/awave_regs.h +++ b/core/hw/naomi/awave_regs.h @@ -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, }; diff --git a/core/hw/naomi/awcartridge.cpp b/core/hw/naomi/awcartridge.cpp index aa3d6c8f4..8679d1fd9 100644 --- a/core/hw/naomi/awcartridge.cpp +++ b/core/hw/naomi/awcartridge.cpp @@ -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); diff --git a/core/hw/naomi/m4cartridge.h b/core/hw/naomi/m4cartridge.h index 72be56786..a37127d40 100644 --- a/core/hw/naomi/m4cartridge.h +++ b/core/hw/naomi/m4cartridge.h @@ -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; diff --git a/core/hw/naomi/multiboard.cpp b/core/hw/naomi/multiboard.cpp new file mode 100644 index 000000000..54abc1a96 --- /dev/null +++ b/core/hw/naomi/multiboard.cpp @@ -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 . + */ +#include "multiboard.h" + +static Multiboard *multiboard; + +#ifdef NAOMI_MULTIBOARD +#include "cfg/cfg.h" +#ifndef _WIN32 +#include +#include +#include +#include +#endif +#include "oslib/oslib.h" +#include "naomi_cart.h" +#include "naomi_roms.h" +#include "cfg/option.h" +#include "hw/sh4/sh4_sched.h" +#include +#include + +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 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 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 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 diff --git a/core/hw/naomi/multiboard.h b/core/hw/naomi/multiboard.h new file mode 100644 index 000000000..208d044b0 --- /dev/null +++ b/core/hw/naomi/multiboard.h @@ -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 . + */ +#pragma once +#include "types.h" +#ifdef NAOMI_MULTIBOARD +#include "naomi_regs.h" +#include "hw/holly/sb.h" +#include "hw/sh4/sh4_mem.h" +#include + + +#ifdef _WIN32 +#include +#include + +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& 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::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& lock) + { + waitMs(lock, INFINITE); + } + + template + std::cv_status wait_for(std::unique_lock& lock, + const std::chrono::duration& rel_time) + { + return waitMs(lock, std::chrono::duration_cast(rel_time).count()); + } + + IpcConditionVariable& operator=(const IpcConditionVariable&) = delete; +}; + +#else // _!WIN32 + +#include + +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& lock) { + pthread_cond_wait(&cond, lock.mutex()->native_handle()); + } + + template + std::cv_status wait_for(std::unique_lock& lock, + const std::chrono::duration& rel_time) + { + timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + std::chrono::seconds seconds = std::chrono::duration_cast(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 status; + IpcMutex mutex; + IpcConditionVariable cond; + std::atomic boardReady[4]; + std::atomic boardSynced[4]; + std::atomic 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 diff --git a/core/hw/naomi/naomi.cpp b/core/hw/naomi/naomi.cpp index a203de6e7..db672e864 100644 --- a/core/hw/naomi/naomi.cpp +++ b/core/hw/naomi/naomi.cpp @@ -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; diff --git a/core/hw/naomi/naomi.h b/core/hw/naomi/naomi.h index 3c3b3f0c4..6d988e023 100644 --- a/core/hw/naomi/naomi.h +++ b/core/hw/naomi/naomi.h @@ -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); +} diff --git a/core/hw/naomi/naomi_cart.cpp b/core/hw/naomi/naomi_cart.cpp index f74f13186..9ac93b228 100644 --- a/core/hw/naomi/naomi_cart.cpp +++ b/core/hw/naomi/naomi_cart.cpp @@ -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 diff --git a/core/hw/naomi/naomi_cart.h b/core/hw/naomi/naomi_cart.h index 9f0f6fa8b..d05f57af4 100644 --- a/core/hw/naomi/naomi_cart.h +++ b/core/hw/naomi/naomi_cart.h @@ -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; diff --git a/core/hw/naomi/naomi_m3comm.cpp b/core/hw/naomi/naomi_m3comm.cpp index aa6cc897d..6d2ccaa42 100644 --- a/core/hw/naomi/naomi_m3comm.cpp +++ b/core/hw/naomi/naomi_m3comm.cpp @@ -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; diff --git a/core/hw/naomi/naomi_regs.h b/core/hw/naomi/naomi_regs.h index 555d7205c..910dc61cf 100644 --- a/core/hw/naomi/naomi_regs.h +++ b/core/hw/naomi/naomi_regs.h @@ -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, }; diff --git a/core/hw/naomi/naomi_roms.cpp b/core/hw/naomi/naomi_roms.cpp index abbbd69b7..aedcd781f 100644 --- a/core/hw/naomi/naomi_roms.cpp +++ b/core/hw/naomi/naomi_roms.cpp @@ -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) { diff --git a/core/hw/naomi/naomi_roms.h b/core/hw/naomi/naomi_roms.h index 82a6db60c..16d17d61f 100644 --- a/core/hw/naomi/naomi_roms.h +++ b/core/hw/naomi/naomi_roms.h @@ -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[]; diff --git a/core/hw/naomi/naomi_roms_input.h b/core/hw/naomi/naomi_roms_input.h index b739a1ed7..a254fb188 100644 --- a/core/hw/naomi/naomi_roms_input.h +++ b/core/hw/naomi/naomi_roms_input.h @@ -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 }, }, }; diff --git a/core/input/gamepad_device.cpp b/core/input/gamepad_device.cpp index 6944c7574..33324ea70 100644 --- a/core/input/gamepad_device.cpp +++ b/core/input/gamepad_device.cpp @@ -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()) diff --git a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp index 79ec2d938..a5f4e04f7 100644 --- a/core/linux-dist/main.cpp +++ b/core/linux-dist/main.cpp @@ -294,6 +294,21 @@ std::vector find_system_data_dirs() return dirs; } +static const char *selfPath; + +void os_RunInstance(int argc, const char *argv[]) +{ + if (fork() == 0) + { + std::vector 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(); diff --git a/core/network/naomi_network.cpp b/core/network/naomi_network.cpp index 500202ca0..096866960 100644 --- a/core/network/naomi_network.cpp +++ b/core/network/naomi_network.cpp @@ -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" }; diff --git a/core/nullDC.cpp b/core/nullDC.cpp index bdd1cc493..84a02e2af 100644 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -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()) diff --git a/core/oslib/oslib.h b/core/oslib/oslib.h index 124c639be..962c11077 100644 --- a/core/oslib/oslib.h +++ b/core/oslib/oslib.h @@ -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 diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 52022d787..193d801e3 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -448,7 +448,7 @@ void gui_plot_render_time(int width, int height) void gui_open_settings() { std::lock_guard 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("Disabled", config::MultiboardSlaves, 0, "Multiboard disabled (when optional)"); + OptionRadioButton("1 (Twin)", config::MultiboardSlaves, 1, "One screen configuration (F355 Twin)"); + ImGui::SameLine(); + OptionRadioButton("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; diff --git a/core/sdl/sdl.cpp b/core/sdl/sdl.cpp index 89f8edea9..d2caa7578 100644 --- a/core/sdl/sdl.cpp +++ b/core/sdl/sdl.cpp @@ -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); diff --git a/core/types.h b/core/types.h index a67debcb2..9e0a50522 100644 --- a/core/types.h +++ b/core/types.h @@ -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; }; diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index aa751509e..00dee3e37 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -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()); + } +} diff --git a/shell/apple/emulator-osx/emulator-osx/osx-main.mm b/shell/apple/emulator-osx/emulator-osx/osx-main.mm index e7a45c8db..ad6ad6629 100644 --- a/shell/apple/emulator-osx/emulator-osx/osx-main.mm +++ b/shell/apple/emulator-osx/emulator-osx/osx-main.mm @@ -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 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"); + } +} diff --git a/shell/libretro/libretro.cpp b/shell/libretro/libretro.cpp index 97125e8c8..5ff8f274f 100644 --- a/shell/libretro/libretro.cpp +++ b/shell/libretro/libretro.cpp @@ -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[]) { } diff --git a/shell/libretro/option.cpp b/shell/libretro/option.cpp index 221e8b388..2b7605b38 100644 --- a/shell/libretro/option.cpp +++ b/shell/libretro/option.cpp @@ -119,6 +119,7 @@ Option GGPODelay("", 0); Option NetworkStats("", false); Option GGPOAnalogAxes("", 0); Option NetworkOutput(CORE_OPTION_NAME "_network_output", false); +Option MultiboardSlaves("", 0); // Maple