diff --git a/AppleWinExpress2019.vcxproj b/AppleWinExpress2019.vcxproj index e99f20a7..0e7fe97a 100644 --- a/AppleWinExpress2019.vcxproj +++ b/AppleWinExpress2019.vcxproj @@ -79,6 +79,7 @@ + @@ -102,6 +103,7 @@ + @@ -160,6 +162,7 @@ + @@ -196,6 +199,7 @@ + @@ -280,9 +284,12 @@ + + + @@ -313,6 +320,7 @@ + diff --git a/AppleWinExpress2019.vcxproj.filters b/AppleWinExpress2019.vcxproj.filters index 5b45ee53..9a43d02c 100644 --- a/AppleWinExpress2019.vcxproj.filters +++ b/AppleWinExpress2019.vcxproj.filters @@ -232,6 +232,12 @@ Source Files\Windows + + Source Files\Emulator + + + Source Files\Emulator + @@ -546,6 +552,12 @@ Source Files\Windows + + Source Files\Emulator + + + Source Files\Emulator + @@ -656,6 +668,9 @@ Resource Files + + Resource Files + @@ -721,6 +736,15 @@ Resource Files + + Resource Files + + + Resource Files + + + Resource Files + diff --git a/help/CommandLine.html b/help/CommandLine.html index fd3f5edb..9945171e 100644 --- a/help/CommandLine.html +++ b/help/CommandLine.html @@ -171,7 +171,9 @@ Support 60Hz(NTSC) video refresh rate and NTSC 1.020MHz base CPU clock (default).

-power-on
Force a power-on.
- Use to auto power-on when not using -d1, -h1 or -load-state.
+ Use to auto power-on when not using -d1, -h1 or -load-state.

+ -snes-max-alt-joy1 or -snes-max-alt-joy2
+ Use alternate button mappings for the SNES MAX card. See Input Settings.

Debug arguments: diff --git a/help/acknowledgements.html b/help/acknowledgements.html index ce09755e..4884f11d 100644 --- a/help/acknowledgements.html +++ b/help/acknowledgements.html @@ -25,5 +25,6 @@

Iván Izaguirre: Taiwanese Copam Base64A Apple II clone

Arnaud C: debugger suggestions and help with 6502/6522/video timing issues

Cyril Lambin: RGB card/monitor rendering, debugger improvements

+

Alex Lukacz: 4Play & SNES MAX card support

diff --git a/help/cfg-advanced.html b/help/cfg-advanced.html index 12627596..28886e0d 100644 --- a/help/cfg-advanced.html +++ b/help/cfg-advanced.html @@ -3,7 +3,7 @@ Advanced Settings

Advanced Settings

-
Advanced settings +
Advanced settings

Save State File Name:
This is the file name to use for save-state files. The default directory is the same as where your AppleWin.exe program is stored.

diff --git a/help/cfg-config.html b/help/cfg-config.html index c0317cf7..df364682 100644 --- a/help/cfg-config.html +++ b/help/cfg-config.html @@ -8,7 +8,7 @@ link="#008000" vlink="#008000">

Configuration Settings


- Configuration settings Model:
diff --git a/help/cfg-disk.html b/help/cfg-disk.html index 0cb4ca85..1d244155 100644 --- a/help/cfg-disk.html +++ b/help/cfg-disk.html @@ -6,7 +6,7 @@

Disk Settings

-
Disk settings +
Disk settings

Floppy Controller Settings:

Enhanced disk access speed:
diff --git a/help/cfg-input.html b/help/cfg-input.html index 4ff5b8a6..fa12e146 100644 --- a/help/cfg-input.html +++ b/help/cfg-input.html @@ -13,7 +13,7 @@

Input Settings

-
Input settings +
Input settings Joystick Control:
These options allow you to configure up to two joysticks attached to @@ -38,6 +38,24 @@ then you should leave these values at 0.
  • Keyboard auto-centering: When keys used for joystick emulation are released then the joystick will return to the central position.
  • +4Play Joystick card:
    +On real hardware this card allows up to 4 Atari 9-pin joysticks to be connected.
    +Under emulation, the first 2 Windows-detected controllers will be used, and then for joysticks 3 and 4, use keys: ESDF+ZX and IJKL+NM. Note these keys will also be readable from the keyboard.
    +
  • The card can be configured in slots 3, 4 or 5. +
  • Since it only uses the slot's DEVICE SELECT space ($C0Bx for slot 3) then it can co-exist with 80-column cards in the Apple //e's AUX slot. NB. For a real PAL Apple //e, then a slot riser card is required for it to fit.
    +See Lukazi's 4Play card and 4Play card software blogs for more information.
    +
    + +SNES MAX card:
    +On real hardware this card allows up to 2 SNES controllers to be connected and all 12 buttons can be read.
    +Under emulation, the first 2 Windows-detected controllers will be used, ideally with 12 (or more) buttons eg. Logitech F310, PlayStation Dualshock 4, DualSense. Note that for some controllers (eg. 8BitDo NES30 Pro) the buttons need remapping, so use the command line switches -snes-max-alt-joy1 or -snes-max-alt-joy2 to remap.
    +
  • The card can be configured in slots 3, 4 or 5. +
  • Since it only uses the slot's DEVICE SELECT space ($C0Bx for slot 3) then it can co-exist with 80-column cards in the Apple //e's AUX slot. NB. This card is small, so no slot riser card is required.
    +See Lukazi's SNES MAX blog for more information.
    +
    +
    + +
    Scroll Lock acts as toggle for full-speed CPU:
      @@ -50,13 +68,13 @@ then you should leave these values at 0.
      • Disables joystick emulation with mouse.
      • Disables Mockingboard/Phasor in slot 4.
      • -
      • Show crosshairs in window's frame:
      • +
      • Show cross-hairs in window's frame:
        • -
        • Configure whether you want crosshairs or not
        • +
        • Configure whether you want cross-hairs or not
      • Restrict mouse to Apple window:
        • -
        • Resticting is useful for paint applications
        • +
        • Restricting is useful for paint applications
        • When unrestricted, the emulated mouse is fully integrated with the Window desktop: moving in and out of the AppleWin window will switch between Windows' and the Apple's mouse cursor.
        • NB. Even when unrestricted, you won't be able to move the mouse outside the Apple window for GEOS. This is not a bug.
        • diff --git a/help/cfg-sound.html b/help/cfg-sound.html index 806d391f..d83d71fb 100644 --- a/help/cfg-sound.html +++ b/help/cfg-sound.html @@ -13,7 +13,7 @@

          Sound Settings

          -
          Sound settingsSound:
          +
          Sound settingsSound:
          This option allows you to choose how sound is output for the system. Your choices are:
          diff --git a/help/img/input.png b/help/img/input.png index 79a38d4a..d3d6b1a1 100644 Binary files a/help/img/input.png and b/help/img/input.png differ diff --git a/resource/Applewin.rc b/resource/Applewin.rc index 705bee9d..968fa3f8 100644 --- a/resource/Applewin.rc +++ b/resource/Applewin.rc @@ -115,12 +115,12 @@ BEGIN CONTROL "50Hz video",IDC_CHECK_50HZ_VIDEO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,142,141,51,10 END -IDD_PROPPAGE_INPUT DIALOGEX 0, 0, 210, 215 +IDD_PROPPAGE_INPUT DIALOGEX 0, 0, 211, 240 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_CAPTION | WS_SYSMENU CAPTION "Input" FONT 8, "MS Shell Dlg", 0, 0, 0x0 BEGIN - GROUPBOX "Joystick Control",IDC_STATIC,5,7,200,101 + GROUPBOX "Joystick Control",IDC_STATIC,5,7,200,135 LTEXT "Joystick &1:",IDC_STATIC,12,20,40,8 COMBOBOX IDC_JOYSTICK0,52,18,110,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "Joystick &2:",IDC_STATIC,12,35,40,8 @@ -136,17 +136,21 @@ BEGIN CONTROL "Swap 0/1",IDC_SWAPBUTTONS0AND1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,162,75,41,10 CONTROL "Auto-fire (all 3 buttons)",IDC_AUTOFIRE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,90,89,10 CONTROL "Keyboard auto-centering",IDC_CENTERINGCONTROL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,90,96,10 + LTEXT "4Play Joystick card:",IDC_STATIC,8,108,84,10 + COMBOBOX IDC_FOURPLAY_CONFIG,93,106,55,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "SNES MAX card:",IDC_STATIC,8,123,82,10 + COMBOBOX IDC_SNESMAX_CONFIG,93,122,55,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP CONTROL "&Scroll Lock acts as toggle for full-speed CPU",IDC_SCROLLLOCK_TOGGLE, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,113,166,10 - CONTROL "&Mouse interface in slot 4",IDC_MOUSE_IN_SLOT4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,127,106,10 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,147,166,10 + CONTROL "&Mouse interface in slot 4",IDC_MOUSE_IN_SLOT4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,161,106,10 CONTROL "Show &crosshairs in window's frame",IDC_MOUSE_CROSSHAIR, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,141,159,10 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,175,159,10 CONTROL "&Restrict mouse to Apple window",IDC_MOUSE_RESTRICT_TO_WINDOW, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,155,121,10 - LTEXT "Microsoft CP/M SoftCard:",IDC_STATIC,8,170,122,10 - COMBOBOX IDC_CPM_CONFIG,93,168,55,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Paste &From Clipboard",IDC_PASTE_FROM_CLIPBOARD,8,192,81,14 - LTEXT "(Shift+Insert during emulation)",IDC_STATIC,93,195,111,8 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,189,121,10 + LTEXT "Microsoft CP/M SoftCard:",IDC_STATIC,8,204,83,10 + COMBOBOX IDC_CPM_CONFIG,93,202,55,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Paste &From Clipboard",IDC_PASTE_FROM_CLIPBOARD,8,219,81,14 + LTEXT "(Shift+Insert during emulation)",IDC_STATIC,93,222,111,8 END IDD_PROPPAGE_SOUND DIALOGEX 0, 0, 210, 191 @@ -391,7 +395,7 @@ GUIDELINES DESIGNINFO BEGIN IDD_PROPPAGE_INPUT, DIALOG BEGIN - BOTTOMMARGIN, 182 + BOTTOMMARGIN, 231 END IDD_PROPPAGE_DISK, DIALOG diff --git a/resource/resource.h b/resource/resource.h index bf651586..ae5a2158 100644 --- a/resource/resource.h +++ b/resource/resource.h @@ -118,6 +118,8 @@ #define IDC_CHECK_50HZ_VIDEO 1084 #define IDC_COMBO_DISK1_SLOT5 1085 #define IDC_COMBO_DISK2_SLOT5 1086 +#define IDC_FOURPLAY_CONFIG 1087 +#define IDC_SNESMAX_CONFIG 1088 #define IDM_EXIT 40001 #define IDM_HELP 40002 #define IDM_ABOUT 40003 diff --git a/source/Card.h b/source/Card.h index 64170098..e5a6c4a7 100644 --- a/source/Card.h +++ b/source/Card.h @@ -21,6 +21,8 @@ enum SS_CARDTYPE CT_LanguageCard, // Apple][ or ][+ in slot-0 CT_LanguageCardIIe, // Apple//e LC instance (not a card) CT_Saturn128K, // Saturn 128K (but may be populated with less RAM, in multiples of 16K) + CT_FourPlay, // 4 port Atari 2600 style digital joystick card + CT_SNESMAX, // 2 port Nintendo NES/SNES controller serial interface card }; enum SLOTS { SLOT0=0, SLOT1, SLOT2, SLOT3, SLOT4, SLOT5, SLOT6, SLOT7, NUM_SLOTS }; diff --git a/source/CardManager.cpp b/source/CardManager.cpp index a3181d7f..1d4ababa 100644 --- a/source/CardManager.cpp +++ b/source/CardManager.cpp @@ -33,8 +33,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "Core.h" #include "Disk.h" +#include "FourPlay.h" #include "MouseInterface.h" #include "SerialComms.h" +#include "SNESMAX.h" void CardManager::Insert(UINT slot, SS_CARDTYPE type) { @@ -85,6 +87,12 @@ void CardManager::Insert(UINT slot, SS_CARDTYPE type) case CT_Uthernet: m_slot[slot] = new DummyCard(type); break; + case CT_FourPlay: + m_slot[slot] = new FourPlayCard(slot); + break; + case CT_SNESMAX: + m_slot[slot] = new SNESMAXCard(slot); + break; case CT_LanguageCard: case CT_Saturn128K: diff --git a/source/CardManager.h b/source/CardManager.h index e152f645..19d2243b 100644 --- a/source/CardManager.h +++ b/source/CardManager.h @@ -33,10 +33,10 @@ public: SS_CARDTYPE QuerySlot(UINT slot) { _ASSERT(slotQueryType(); } Card& GetRef(UINT slot) { - SS_CARDTYPE t=QuerySlot(slot); _ASSERT((t==CT_SSC || t==CT_MouseInterface || t==CT_Disk2) && m_slot[slot]); + SS_CARDTYPE t=QuerySlot(slot); _ASSERT((t==CT_SSC || t==CT_MouseInterface || t==CT_Disk2 || t == CT_FourPlay || t == CT_SNESMAX) && m_slot[slot]); return *m_slot[slot]; } - Card* GetObj(UINT slot) { SS_CARDTYPE t=QuerySlot(slot); _ASSERT(t==CT_SSC || t==CT_MouseInterface || t==CT_Disk2); return m_slot[slot]; } + Card* GetObj(UINT slot) { SS_CARDTYPE t=QuerySlot(slot); _ASSERT(t==CT_SSC || t==CT_MouseInterface || t==CT_Disk2 || t == CT_FourPlay || t == CT_SNESMAX); return m_slot[slot]; } void InsertAux(SS_CARDTYPE type); void RemoveAux(void); diff --git a/source/CmdLine.cpp b/source/CmdLine.cpp index 3f183527..4c9cc36f 100644 --- a/source/CmdLine.cpp +++ b/source/CmdLine.cpp @@ -510,6 +510,14 @@ bool ProcessCmdLine(LPSTR lpCmdLine) { g_cmdLine.bRemoveNoSlotClock = true; } + else if (strcmp(lpCmdLine, "-snes-max-alt-joy1") == 0) + { + g_cmdLine.snesMaxAltControllerType[0] = true; + } + else if (strcmp(lpCmdLine, "-snes-max-alt-joy2") == 0) + { + g_cmdLine.snesMaxAltControllerType[1] = true; + } else // unsupported { LogFileOutput("Unsupported arg: %s\n", lpCmdLine); diff --git a/source/CmdLine.h b/source/CmdLine.h index 7c974550..6fe0c632 100644 --- a/source/CmdLine.h +++ b/source/CmdLine.h @@ -18,6 +18,8 @@ struct CmdLine bSlot7EmptyOnExit = false; bSwapButtons0and1 = false; bRemoveNoSlotClock = false; + snesMaxAltControllerType[0] = false; + snesMaxAltControllerType[1] = false; szImageName_harddisk[HARDDISK_1] = NULL; szImageName_harddisk[HARDDISK_2] = NULL; szSnapshotName = NULL; @@ -53,6 +55,7 @@ struct CmdLine bool bSlot7EmptyOnExit; bool bSwapButtons0and1; bool bRemoveNoSlotClock; + bool snesMaxAltControllerType[2]; SS_CARDTYPE slotInsert[NUM_SLOTS]; LPCSTR szImageName_drive[NUM_SLOTS][NUM_DRIVES]; bool driveConnected[NUM_SLOTS][NUM_DRIVES]; diff --git a/source/Common.h b/source/Common.h index e93eb6cf..dce710b7 100644 --- a/source/Common.h +++ b/source/Common.h @@ -107,7 +107,7 @@ enum AppMode_e #define REGVALUE_CUSTOM_SPEED "Custom Speed" #define REGVALUE_EMULATION_SPEED "Emulation Speed" #define REGVALUE_WINDOW_SCALE "Window Scale" -#define REGVALUE_UTHERNET_ACTIVE "Uthernet Active" +#define REGVALUE_UTHERNET_ACTIVE "Uthernet Active" // GH#977: Deprecated from 1.30.4 #define REGVALUE_UTHERNET_INTERFACE "Uthernet Interface" #define REGVALUE_SLOT4 "Slot 4" // GH#977: Deprecated from 1.30.4 #define REGVALUE_SLOT5 "Slot 5" // GH#977: Deprecated from 1.30.4 diff --git a/source/Configuration/Config.h b/source/Configuration/Config.h index 659b12c7..55b3bd87 100644 --- a/source/Configuration/Config.h +++ b/source/Configuration/Config.h @@ -25,7 +25,6 @@ public: m_Slot[SLOT5] = GetCardMgr().QuerySlot(SLOT5); m_Slot[SLOT7] = GetCardMgr().QuerySlot(SLOT7); - m_tfeEnabled = get_tfe_enabled(); m_tfeInterface = get_tfe_interface(); } @@ -35,7 +34,6 @@ public: m_CpuType = other.m_CpuType; memcpy(m_Slot, other.m_Slot, sizeof(m_Slot)); m_bEnableHDD = other.m_bEnableHDD; - m_tfeEnabled = other.m_tfeEnabled; m_tfeInterface = other.m_tfeInterface; m_bEnableTheFreezesF8Rom = other.m_bEnableTheFreezesF8Rom; m_uSaveLoadStateMsg = other.m_uSaveLoadStateMsg; @@ -49,7 +47,6 @@ public: m_CpuType == other.m_CpuType && memcmp(m_Slot, other.m_Slot, sizeof(m_Slot)) == 0 && m_bEnableHDD == other.m_bEnableHDD && - m_tfeEnabled == other.m_tfeEnabled && m_tfeInterface == other.m_tfeInterface && m_bEnableTheFreezesF8Rom == other.m_bEnableTheFreezesF8Rom && m_uSaveLoadStateMsg == other.m_uSaveLoadStateMsg && @@ -66,7 +63,6 @@ public: SS_CARDTYPE m_Slot[NUM_SLOTS]; // 0..7 SS_CARDTYPE m_SlotAux; bool m_bEnableHDD; - int m_tfeEnabled; std::string m_tfeInterface; UINT m_bEnableTheFreezesF8Rom; UINT m_uSaveLoadStateMsg; diff --git a/source/Configuration/PageConfig.cpp b/source/Configuration/PageConfig.cpp index 8c1c2c80..eaeb34f4 100644 --- a/source/Configuration/PageConfig.cpp +++ b/source/Configuration/PageConfig.cpp @@ -112,6 +112,7 @@ INT_PTR CPageConfig::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPA case IDC_ETHERNET: ui_tfe_settings_dialog(hWnd); + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3] = m_PageConfigTfe.m_tfe_enabled ? CT_Uthernet : CT_Empty; break; case IDC_MONOCOLOR: @@ -315,7 +316,6 @@ void CPageConfig::DlgOK(HWND hWnd) m_PropertySheetHelper.GetConfigNew().m_videoRefreshRate = isNewVideoRate50Hz ? VR_50HZ : VR_60HZ; } - m_PropertySheetHelper.GetConfigNew().m_tfeEnabled = m_PageConfigTfe.m_tfe_enabled; m_PropertySheetHelper.GetConfigNew().m_tfeInterface = m_PageConfigTfe.m_tfe_interface_name; if (bVideoReinit) @@ -376,8 +376,9 @@ void CPageConfig::DlgOK(HWND hWnd) void CPageConfig::InitOptions(HWND hWnd) { - // Nothing to do: - // - no changes made on any other pages affect this page + const SS_CARDTYPE slot3 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3]; + const BOOL enableUthernetDialog = slot3 == CT_Empty || slot3 == CT_Uthernet; + EnableWindow(GetDlgItem(hWnd, IDC_ETHERNET), enableUthernetDialog); } // Config->Computer: Menu item to eApple2Type diff --git a/source/Configuration/PageConfigTfe.cpp b/source/Configuration/PageConfigTfe.cpp index fbba5842..7241c64f 100644 --- a/source/Configuration/PageConfigTfe.cpp +++ b/source/Configuration/PageConfigTfe.cpp @@ -144,10 +144,7 @@ int CPageConfigTfe::gray_ungray_items(HWND hwnd) int enable; int number; - //resources_get_value("ETHERNET_DISABLED", (void *)&disabled); - DWORD dwDisabled; - REGLOAD_DEFAULT(TEXT("Uthernet Disabled"), &dwDisabled, 0); - int disabled = dwDisabled ? 1 : 0; + int disabled = 0; get_disabled_state(&disabled); if (disabled) @@ -274,7 +271,7 @@ void CPageConfigTfe::save_tfe_dialog(HWND hwnd) { m_tfe_interface_name = buffer; active_value = SendMessage(GetDlgItem(hwnd, IDC_TFE_SETTINGS_ENABLE), CB_GETCURSEL, 0, 0); - m_tfe_enabled = active_value >= 1 ? 1 : 0; + m_tfe_enabled = active_value > 0 ? 1 : 0; } else { diff --git a/source/Configuration/PageInput.cpp b/source/Configuration/PageInput.cpp index 84ffd6b0..ea286e05 100644 --- a/source/Configuration/PageInput.cpp +++ b/source/Configuration/PageInput.cpp @@ -63,6 +63,17 @@ const TCHAR CPageInput::m_szCPMSlotChoice_Slot5[] = TEXT("Slot 5\0"); const TCHAR CPageInput::m_szCPMSlotChoice_Unplugged[] = TEXT("Unplugged\0"); const TCHAR CPageInput::m_szCPMSlotChoice_Unavailable[] = TEXT("Unavailable\0"); +const TCHAR CPageInput::m_szFourPlaySlotChoice_Slot3[] = TEXT("Slot 3\0"); +const TCHAR CPageInput::m_szFourPlaySlotChoice_Slot4[] = TEXT("Slot 4\0"); +const TCHAR CPageInput::m_szFourPlaySlotChoice_Slot5[] = TEXT("Slot 5\0"); +const TCHAR CPageInput::m_szFourPlaySlotChoice_Unplugged[] = TEXT("Unplugged\0"); +const TCHAR CPageInput::m_szFourPlaySlotChoice_Unavailable[] = TEXT("Unavailable\0"); + +const TCHAR CPageInput::m_szSNESMAXSlotChoice_Slot3[] = TEXT("Slot 3\0"); +const TCHAR CPageInput::m_szSNESMAXSlotChoice_Slot4[] = TEXT("Slot 4\0"); +const TCHAR CPageInput::m_szSNESMAXSlotChoice_Slot5[] = TEXT("Slot 5\0"); +const TCHAR CPageInput::m_szSNESMAXSlotChoice_Unplugged[] = TEXT("Unplugged\0"); +const TCHAR CPageInput::m_szSNESMAXSlotChoice_Unavailable[] = TEXT("Unavailable\0"); INT_PTR CALLBACK CPageInput::DlgProc(HWND hWnd, UINT message, WPARAM wparam, LPARAM lparam) { @@ -124,20 +135,20 @@ INT_PTR CPageInput::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPAR switch (LOWORD(wparam)) { case IDC_JOYSTICK0: - if(HIWORD(wparam) == CBN_SELCHANGE) + if (HIWORD(wparam) == CBN_SELCHANGE) { DWORD dwNewJoyType = (DWORD)SendDlgItemMessage(hWnd, IDC_JOYSTICK0, CB_GETCURSEL, 0, 0); - const bool bIsSlot4Mouse = m_PropertySheetHelper.GetConfigNew().m_Slot[4] == CT_MouseInterface; + const bool bIsSlot4Mouse = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4] == CT_MouseInterface; JoySetEmulationType(hWnd, m_nJoy0ChoiceTranlationTbl[dwNewJoyType], JN_JOYSTICK0, bIsSlot4Mouse); InitOptions(hWnd); } break; case IDC_JOYSTICK1: - if(HIWORD(wparam) == CBN_SELCHANGE) + if (HIWORD(wparam) == CBN_SELCHANGE) { DWORD dwNewJoyType = (DWORD)SendDlgItemMessage(hWnd, IDC_JOYSTICK1, CB_GETCURSEL, 0, 0); - const bool bIsSlot4Mouse = m_PropertySheetHelper.GetConfigNew().m_Slot[4] == CT_MouseInterface; + const bool bIsSlot4Mouse = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4] == CT_MouseInterface; JoySetEmulationType(hWnd, m_nJoy1ChoiceTranlationTbl[dwNewJoyType], JN_JOYSTICK1, bIsSlot4Mouse); InitOptions(hWnd); } @@ -150,14 +161,14 @@ INT_PTR CPageInput::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPAR case IDC_MOUSE_IN_SLOT4: { const UINT uNewState = IsDlgButtonChecked(hWnd, IDC_MOUSE_IN_SLOT4) ? 1 : 0; - m_PropertySheetHelper.GetConfigNew().m_Slot[4] = uNewState ? CT_MouseInterface : CT_Empty; + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4] = uNewState ? CT_MouseInterface : CT_Empty; InitOptions(hWnd); // re-init } break; case IDC_CPM_CONFIG: - if(HIWORD(wparam) == CBN_SELCHANGE) + if (HIWORD(wparam) == CBN_SELCHANGE) { const DWORD NewCPMChoiceItem = (DWORD) SendDlgItemMessage(hWnd, IDC_CPM_CONFIG, CB_GETCURSEL, 0, 0); const CPMCHOICE NewCPMChoice = m_CPMComboItemToChoice[NewCPMChoiceItem]; @@ -165,18 +176,80 @@ INT_PTR CPageInput::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPAR break; // Whatever has changed, the old slot will now be empty - const SS_CARDTYPE Slot4 = m_PropertySheetHelper.GetConfigNew().m_Slot[4]; - const SS_CARDTYPE Slot5 = m_PropertySheetHelper.GetConfigNew().m_Slot[5]; - if (Slot4 == CT_Z80) - m_PropertySheetHelper.GetConfigNew().m_Slot[4] = CT_Empty; - else if (Slot5 == CT_Z80) - m_PropertySheetHelper.GetConfigNew().m_Slot[5] = CT_Empty; + const SS_CARDTYPE slot4 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4]; + const SS_CARDTYPE slot5 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT5]; + if (slot4 == CT_Z80) + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4] = CT_Empty; + else if (slot5 == CT_Z80) + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT5] = CT_Empty; // Insert CP/M card into new slot (or leave slot empty) if (NewCPMChoice == CPM_SLOT4) - m_PropertySheetHelper.GetConfigNew().m_Slot[4] = CT_Z80; + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4] = CT_Z80; else if (NewCPMChoice == CPM_SLOT5) - m_PropertySheetHelper.GetConfigNew().m_Slot[5] = CT_Z80; + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT5] = CT_Z80; + + InitOptions(hWnd); // re-init + } + break; + + case IDC_FOURPLAY_CONFIG: + if (HIWORD(wparam) == CBN_SELCHANGE) + { + const DWORD NewFourPlayChoiceItem = (DWORD) SendDlgItemMessage(hWnd, IDC_FOURPLAY_CONFIG, CB_GETCURSEL, 0, 0); + const FOURPLAYCHOICE NewFourPlayChoice = m_FourPlayComboItemToChoice[NewFourPlayChoiceItem]; + if (NewFourPlayChoice == m_FourPlayChoice) + break; + + // Whatever has changed, the old slot will now be empty + const SS_CARDTYPE slot3 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3]; + const SS_CARDTYPE slot4 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4]; + const SS_CARDTYPE slot5 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT5]; + if (slot3 == CT_FourPlay) + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3] = CT_Empty; + else if (slot4 == CT_FourPlay) + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4] = CT_Empty; + else if (slot5 == CT_FourPlay) + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT5] = CT_Empty; + + // Insert 4Play card into new slot (or leave slot empty) + if (NewFourPlayChoice == FOURPLAY_SLOT3) + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3] = CT_FourPlay; + else if (NewFourPlayChoice == FOURPLAY_SLOT4) + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4] = CT_FourPlay; + else if (NewFourPlayChoice == FOURPLAY_SLOT5) + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT5] = CT_FourPlay; + + InitOptions(hWnd); // re-init + } + break; + + case IDC_SNESMAX_CONFIG: + if (HIWORD(wparam) == CBN_SELCHANGE) + { + const DWORD NewSNESMAXChoiceItem = (DWORD) SendDlgItemMessage(hWnd, IDC_SNESMAX_CONFIG, CB_GETCURSEL, 0, 0); + const SNESMAXCHOICE NewSNESMAXChoice = m_SNESMAXComboItemToChoice[NewSNESMAXChoiceItem]; + if (NewSNESMAXChoice == m_SNESMAXChoice) + break; + + // Whatever has changed, the old slot will now be empty + const SS_CARDTYPE slot3 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3]; + const SS_CARDTYPE slot4 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4]; + const SS_CARDTYPE slot5 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT5]; + if (slot3 == CT_SNESMAX) + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3] = CT_Empty; + else if (slot4 == CT_SNESMAX) + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4] = CT_Empty; + else if (slot5 == CT_SNESMAX) + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT5] = CT_Empty; + + // Insert SNES MAX card into new slot (or leave slot empty) + if (NewSNESMAXChoice == SNESMAX_SLOT3) + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3] = CT_SNESMAX; + else if (NewSNESMAXChoice == SNESMAX_SLOT4) + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4] = CT_SNESMAX; + else if (NewSNESMAXChoice == SNESMAX_SLOT5) + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT5] = CT_SNESMAX; InitOptions(hWnd); // re-init } @@ -268,7 +341,7 @@ void CPageInput::InitJoystickChoices(HWND hWnd, int nJoyNum, int nIdcValue) TCHAR** ppszJoyChoices; int nOtherJoyNum = nJoyNum == JN_JOYSTICK0 ? JN_JOYSTICK1 : JN_JOYSTICK0; - if(nJoyNum == JN_JOYSTICK0) + if (nJoyNum == JN_JOYSTICK0) { pnzJoystickChoices = m_joystick0choices; pnJoyTranslationTbl = m_nJoy0ChoiceTranlationTbl; @@ -347,19 +420,21 @@ void CPageInput::InitJoystickChoices(HWND hWnd, int nJoyNum, int nIdcValue) void CPageInput::InitSlotOptions(HWND hWnd) { - const SS_CARDTYPE Slot4 = m_PropertySheetHelper.GetConfigNew().m_Slot[4]; + const SS_CARDTYPE slot4 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4]; - const bool bIsSlot4Mouse = Slot4 == CT_MouseInterface; + const bool bIsSlot4Mouse = slot4 == CT_MouseInterface; CheckDlgButton(hWnd, IDC_MOUSE_IN_SLOT4, bIsSlot4Mouse ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hWnd, IDC_MOUSE_CROSSHAIR, m_uMouseShowCrosshair ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hWnd, IDC_MOUSE_RESTRICT_TO_WINDOW, m_uMouseRestrictToWindow ? BST_CHECKED : BST_UNCHECKED); - const bool bIsSlot4Empty = Slot4 == CT_Empty; + const bool bIsSlot4Empty = slot4 == CT_Empty; EnableWindow(GetDlgItem(hWnd, IDC_MOUSE_IN_SLOT4), ((bIsSlot4Mouse || bIsSlot4Empty) && !JoyUsingMouse()) ? TRUE : FALSE); EnableWindow(GetDlgItem(hWnd, IDC_MOUSE_CROSSHAIR), bIsSlot4Mouse ? TRUE : FALSE); EnableWindow(GetDlgItem(hWnd, IDC_MOUSE_RESTRICT_TO_WINDOW), bIsSlot4Mouse ? TRUE : FALSE); InitCPMChoices(hWnd); + InitFourPlayChoices(hWnd); + InitSNESMAXChoices(hWnd); InitJoystickChoices(hWnd, JN_JOYSTICK0, IDC_JOYSTICK0); InitJoystickChoices(hWnd, JN_JOYSTICK1, IDC_JOYSTICK1); @@ -370,11 +445,11 @@ void CPageInput::InitSlotOptions(HWND hWnd) void CPageInput::InitCPMChoices(HWND hWnd) { - const SS_CARDTYPE Slot4 = m_PropertySheetHelper.GetConfigNew().m_Slot[4]; - const SS_CARDTYPE Slot5 = m_PropertySheetHelper.GetConfigNew().m_Slot[5]; + const SS_CARDTYPE slot4 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4]; + const SS_CARDTYPE slot5 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT5]; - if (Slot4 == CT_Z80) m_CPMChoice = CPM_SLOT4; - else if (Slot5 == CT_Z80) m_CPMChoice = CPM_SLOT5; + if (slot4 == CT_Z80) m_CPMChoice = CPM_SLOT4; + else if (slot5 == CT_Z80) m_CPMChoice = CPM_SLOT5; else m_CPMChoice = CPM_UNPLUGGED; for (UINT i=0; i<_CPM_MAX_CHOICES; i++) @@ -383,10 +458,10 @@ void CPageInput::InitCPMChoices(HWND hWnd) UINT uStringOffset = 0; UINT uComboItemIdx = 0; - const bool bIsSlot4Empty = Slot4 == CT_Empty; - const bool bIsSlot4CPM = Slot4 == CT_Z80; - const bool bIsSlot5Empty = Slot5 == CT_Empty; - const bool bIsSlot5CPM = Slot5 == CT_Z80; + const bool bIsSlot4Empty = slot4 == CT_Empty; + const bool bIsSlot4CPM = slot4 == CT_Z80; + const bool bIsSlot5Empty = slot5 == CT_Empty; + const bool bIsSlot5CPM = slot5 == CT_Z80; if (bIsSlot4Empty || bIsSlot4CPM) { @@ -440,3 +515,175 @@ void CPageInput::InitCPMChoices(HWND hWnd) m_PropertySheetHelper.FillComboBox(hWnd, IDC_CPM_CONFIG, m_szCPMSlotChoices, uCurrentChoice); } + +void CPageInput::InitFourPlayChoices(HWND hWnd) +{ + const SS_CARDTYPE slot3 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3]; + const SS_CARDTYPE slot4 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4]; + const SS_CARDTYPE slot5 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT5]; + + if (slot3 == CT_FourPlay) m_FourPlayChoice = FOURPLAY_SLOT3; + else if (slot4 == CT_FourPlay) m_FourPlayChoice = FOURPLAY_SLOT4; + else if (slot5 == CT_FourPlay) m_FourPlayChoice = FOURPLAY_SLOT5; + else m_FourPlayChoice = FOURPLAY_UNPLUGGED; + + for (UINT i=0; i<_FOURPLAY_MAX_CHOICES; i++) + m_FourPlayComboItemToChoice[i] = FOURPLAY_UNAVAILABLE; + + UINT uStringOffset = 0; + UINT uComboItemIdx = 0; + + const bool bIsSlot3Empty = slot3 == CT_Empty; + const bool bIsSlot3FourPlay = slot3 == CT_FourPlay; + const bool bIsSlot4Empty = slot4 == CT_Empty; + const bool bIsSlot4FourPlay = slot4 == CT_FourPlay; + const bool bIsSlot5Empty = slot5 == CT_Empty; + const bool bIsSlot5FourPlay = slot5 == CT_FourPlay; + + if (bIsSlot3Empty || bIsSlot3FourPlay) + { + const UINT uStrLen = strlen(m_szFourPlaySlotChoice_Slot3) + 1; + memcpy(&m_szFourPlaySlotChoices[uStringOffset], m_szFourPlaySlotChoice_Slot3, uStrLen); + uStringOffset += uStrLen; + + m_FourPlayComboItemToChoice[uComboItemIdx++] = FOURPLAY_SLOT3; + } + + if (bIsSlot4Empty || bIsSlot4FourPlay) + { + const UINT uStrLen = strlen(m_szFourPlaySlotChoice_Slot4)+1; + memcpy(&m_szFourPlaySlotChoices[uStringOffset], m_szFourPlaySlotChoice_Slot4, uStrLen); + uStringOffset += uStrLen; + + m_FourPlayComboItemToChoice[uComboItemIdx++] = FOURPLAY_SLOT4; + } + + if (bIsSlot5Empty || bIsSlot5FourPlay) + { + const UINT uStrLen = strlen(m_szFourPlaySlotChoice_Slot5)+1; + memcpy(&m_szFourPlaySlotChoices[uStringOffset], m_szFourPlaySlotChoice_Slot5, uStrLen); + uStringOffset += uStrLen; + + m_FourPlayComboItemToChoice[uComboItemIdx++] = FOURPLAY_SLOT5; + } + + if (uStringOffset) + { + const UINT uStrLen = strlen(m_szFourPlaySlotChoice_Unplugged)+1; + memcpy(&m_szFourPlaySlotChoices[uStringOffset], m_szFourPlaySlotChoice_Unplugged, uStrLen); + uStringOffset += uStrLen; + + m_FourPlayComboItemToChoice[uComboItemIdx] = FOURPLAY_UNPLUGGED; + } + else + { + const UINT uStrLen = strlen(m_szFourPlaySlotChoice_Unavailable)+1; + memcpy(&m_szFourPlaySlotChoices[uStringOffset], m_szFourPlaySlotChoice_Unavailable, uStrLen); + uStringOffset += uStrLen; + + m_FourPlayChoice = FOURPLAY_UNAVAILABLE; // Force this + m_FourPlayComboItemToChoice[uComboItemIdx] = FOURPLAY_UNAVAILABLE; + } + + m_szFourPlaySlotChoices[uStringOffset] = 0; // Doubly null terminated + + // + + UINT uCurrentChoice = uComboItemIdx; // Default to last item (either UNPLUGGED or UNAVAILABLE) + for (UINT i=0; i<=uComboItemIdx; i++) + { + if (m_FourPlayComboItemToChoice[i] == m_FourPlayChoice) + { + uCurrentChoice = i; + break; + } + } + + m_PropertySheetHelper.FillComboBox(hWnd, IDC_FOURPLAY_CONFIG, m_szFourPlaySlotChoices, uCurrentChoice); +} + +void CPageInput::InitSNESMAXChoices(HWND hWnd) +{ + const SS_CARDTYPE slot3 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3]; + const SS_CARDTYPE slot4 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT4]; + const SS_CARDTYPE slot5 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT5]; + + if (slot3 == CT_SNESMAX) m_SNESMAXChoice = SNESMAX_SLOT3; + else if (slot4 == CT_SNESMAX) m_SNESMAXChoice = SNESMAX_SLOT4; + else if (slot5 == CT_SNESMAX) m_SNESMAXChoice = SNESMAX_SLOT5; + else m_SNESMAXChoice = SNESMAX_UNPLUGGED; + + for (UINT i=0; i<_SNESMAX_MAX_CHOICES; i++) + m_SNESMAXComboItemToChoice[i] = SNESMAX_UNAVAILABLE; + + UINT uStringOffset = 0; + UINT uComboItemIdx = 0; + + const bool bIsSlot3Empty = slot3 == CT_Empty; + const bool bIsSlot3SNESMAX = slot3 == CT_SNESMAX; + const bool bIsSlot4Empty = slot4 == CT_Empty; + const bool bIsSlot4SNESMAX = slot4 == CT_SNESMAX; + const bool bIsSlot5Empty = slot5 == CT_Empty; + const bool bIsSlot5SNESMAX = slot5 == CT_SNESMAX; + + if (bIsSlot3Empty || bIsSlot3SNESMAX) + { + const UINT uStrLen = strlen(m_szSNESMAXSlotChoice_Slot3) + 1; + memcpy(&m_szSNESMAXSlotChoices[uStringOffset], m_szSNESMAXSlotChoice_Slot3, uStrLen); + uStringOffset += uStrLen; + + m_SNESMAXComboItemToChoice[uComboItemIdx++] = SNESMAX_SLOT3; + } + + if (bIsSlot4Empty || bIsSlot4SNESMAX) + { + const UINT uStrLen = strlen(m_szSNESMAXSlotChoice_Slot4)+1; + memcpy(&m_szSNESMAXSlotChoices[uStringOffset], m_szSNESMAXSlotChoice_Slot4, uStrLen); + uStringOffset += uStrLen; + + m_SNESMAXComboItemToChoice[uComboItemIdx++] = SNESMAX_SLOT4; + } + + if (bIsSlot5Empty || bIsSlot5SNESMAX) + { + const UINT uStrLen = strlen(m_szSNESMAXSlotChoice_Slot5)+1; + memcpy(&m_szSNESMAXSlotChoices[uStringOffset], m_szSNESMAXSlotChoice_Slot5, uStrLen); + uStringOffset += uStrLen; + + m_SNESMAXComboItemToChoice[uComboItemIdx++] = SNESMAX_SLOT5; + } + + if (uStringOffset) + { + const UINT uStrLen = strlen(m_szSNESMAXSlotChoice_Unplugged)+1; + memcpy(&m_szSNESMAXSlotChoices[uStringOffset], m_szSNESMAXSlotChoice_Unplugged, uStrLen); + uStringOffset += uStrLen; + + m_SNESMAXComboItemToChoice[uComboItemIdx] = SNESMAX_UNPLUGGED; + } + else + { + const UINT uStrLen = strlen(m_szSNESMAXSlotChoice_Unavailable)+1; + memcpy(&m_szSNESMAXSlotChoices[uStringOffset], m_szSNESMAXSlotChoice_Unavailable, uStrLen); + uStringOffset += uStrLen; + + m_SNESMAXChoice = SNESMAX_UNAVAILABLE; // Force this + m_SNESMAXComboItemToChoice[uComboItemIdx] = SNESMAX_UNAVAILABLE; + } + + m_szSNESMAXSlotChoices[uStringOffset] = 0; // Doubly null terminated + + // + + UINT uCurrentChoice = uComboItemIdx; // Default to last item (either UNPLUGGED or UNAVAILABLE) + for (UINT i=0; i<=uComboItemIdx; i++) + { + if (m_SNESMAXComboItemToChoice[i] == m_SNESMAXChoice) + { + uCurrentChoice = i; + break; + } + } + + m_PropertySheetHelper.FillComboBox(hWnd, IDC_SNESMAX_CONFIG, m_szSNESMAXSlotChoices, uCurrentChoice); +} diff --git a/source/Configuration/PageInput.h b/source/Configuration/PageInput.h index d527609b..60311b8e 100644 --- a/source/Configuration/PageInput.h +++ b/source/Configuration/PageInput.h @@ -19,7 +19,9 @@ public: m_bSwapButtons0and1(false), m_uMouseShowCrosshair(0), m_uMouseRestrictToWindow(0), - m_CPMChoice(CPM_UNPLUGGED) + m_CPMChoice(CPM_UNPLUGGED), + m_FourPlayChoice(FOURPLAY_UNPLUGGED), + m_SNESMAXChoice(SNESMAX_UNPLUGGED) { CPageInput::ms_this = this; } @@ -53,6 +55,8 @@ private: void InitJoystickChoices(HWND hWnd, int nJoyNum, int nIdcValue); void InitSlotOptions(HWND hWnd); void InitCPMChoices(HWND hWnd); + void InitFourPlayChoices(HWND hWnd); + void InitSNESMAXChoices(HWND hWnd); static CPageInput* ms_this; static const UINT MaxMenuChoiceLen = 40; @@ -72,6 +76,18 @@ private: static const TCHAR m_szCPMSlotChoice_Unplugged[]; static const TCHAR m_szCPMSlotChoice_Unavailable[]; + static const TCHAR m_szFourPlaySlotChoice_Slot3[]; + static const TCHAR m_szFourPlaySlotChoice_Slot4[]; + static const TCHAR m_szFourPlaySlotChoice_Slot5[]; + static const TCHAR m_szFourPlaySlotChoice_Unplugged[]; + static const TCHAR m_szFourPlaySlotChoice_Unavailable[]; + + static const TCHAR m_szSNESMAXSlotChoice_Slot3[]; + static const TCHAR m_szSNESMAXSlotChoice_Slot4[]; + static const TCHAR m_szSNESMAXSlotChoice_Slot5[]; + static const TCHAR m_szSNESMAXSlotChoice_Unplugged[]; + static const TCHAR m_szSNESMAXSlotChoice_Unavailable[]; + int m_nJoy0ChoiceTranlationTbl[J0C_MAX]; TCHAR m_joystick0choices[J0C_MAX * MaxMenuChoiceLen]; int m_nJoy1ChoiceTranlationTbl[J1C_MAX]; @@ -92,4 +108,14 @@ private: TCHAR m_szCPMSlotChoices[_CPM_MAX_CHOICES * MaxMenuChoiceLen]; CPMCHOICE m_CPMChoice; CPMCHOICE m_CPMComboItemToChoice[_CPM_MAX_CHOICES]; + + enum FOURPLAYCHOICE {FOURPLAY_SLOT3=0, FOURPLAY_SLOT4, FOURPLAY_SLOT5, FOURPLAY_UNPLUGGED, FOURPLAY_UNAVAILABLE, _FOURPLAY_MAX_CHOICES}; + TCHAR m_szFourPlaySlotChoices[_FOURPLAY_MAX_CHOICES * MaxMenuChoiceLen]; + FOURPLAYCHOICE m_FourPlayChoice; + FOURPLAYCHOICE m_FourPlayComboItemToChoice[_FOURPLAY_MAX_CHOICES]; + + enum SNESMAXCHOICE {SNESMAX_SLOT3=0, SNESMAX_SLOT4, SNESMAX_SLOT5, SNESMAX_UNPLUGGED, SNESMAX_UNAVAILABLE, _SNESMAX_MAX_CHOICES}; + TCHAR m_szSNESMAXSlotChoices[_SNESMAX_MAX_CHOICES * MaxMenuChoiceLen]; + SNESMAXCHOICE m_SNESMAXChoice; + SNESMAXCHOICE m_SNESMAXComboItemToChoice[_SNESMAX_MAX_CHOICES]; }; diff --git a/source/Configuration/PropertySheetHelper.cpp b/source/Configuration/PropertySheetHelper.cpp index 476176ea..016e1a8c 100644 --- a/source/Configuration/PropertySheetHelper.cpp +++ b/source/Configuration/PropertySheetHelper.cpp @@ -336,21 +336,33 @@ void CPropertySheetHelper::ApplyNewConfig(const CConfigNeedingRestart& ConfigNew SaveCpuType(ConfigNew.m_CpuType); } - UINT slot = 4; + UINT slot = SLOT3; + if (CONFIG_CHANGED_LOCAL(m_Slot[slot])) + { + SetSlot(slot, ConfigNew.m_Slot[slot]); + + if (ConfigNew.m_Slot[slot] == CT_Uthernet) // TODO: move this to UthernetCard object + { + std::string& regSection = RegGetConfigSlotSection(slot); + RegSaveString(regSection.c_str(), REGVALUE_UTHERNET_INTERFACE, 1, ConfigNew.m_tfeInterface); + } + } + + slot = SLOT4; if (CONFIG_CHANGED_LOCAL(m_Slot[slot])) SetSlot(slot, ConfigNew.m_Slot[slot]); - slot = 5; + slot = SLOT5; if (CONFIG_CHANGED_LOCAL(m_Slot[slot])) SetSlot(slot, ConfigNew.m_Slot[slot]); -// slot = 7; +// slot = SLOT7; // if (CONFIG_CHANGED_LOCAL(m_Slot[slot])) // SetSlot(slot, ConfigNew.m_Slot[slot]); if (CONFIG_CHANGED_LOCAL(m_bEnableHDD)) { - REGSAVE(TEXT(REGVALUE_HDD_ENABLED), ConfigNew.m_bEnableHDD ? 1 : 0); // TODO: Change to REGVALUE_SLOT7 + REGSAVE(TEXT(REGVALUE_HDD_ENABLED), ConfigNew.m_bEnableHDD ? 1 : 0); } if (CONFIG_CHANGED_LOCAL(m_bEnableTheFreezesF8Rom)) @@ -362,17 +374,6 @@ void CPropertySheetHelper::ApplyNewConfig(const CConfigNeedingRestart& ConfigNew { REGSAVE(TEXT(REGVALUE_VIDEO_REFRESH_RATE), ConfigNew.m_videoRefreshRate); } - - if (CONFIG_CHANGED_LOCAL(m_tfeEnabled)) - { - REGSAVE(TEXT(REGVALUE_UTHERNET_ACTIVE), ConfigNew.m_tfeEnabled); - } - - if (CONFIG_CHANGED_LOCAL(m_tfeInterface)) - { - RegSaveString(TEXT(REG_CONFIG), TEXT(REGVALUE_UTHERNET_INTERFACE), 1, ConfigNew.m_tfeInterface); - } - } void CPropertySheetHelper::ApplyNewConfig(void) @@ -385,13 +386,13 @@ void CPropertySheetHelper::SaveCurrentConfig(void) // NB. clone-type is encoded in g_Apple2Type m_ConfigOld.m_Apple2Type = GetApple2Type(); m_ConfigOld.m_CpuType = GetMainCpu(); + m_ConfigOld.m_Slot[SLOT3] = GetCardMgr().QuerySlot(SLOT3); m_ConfigOld.m_Slot[SLOT4] = GetCardMgr().QuerySlot(SLOT4); m_ConfigOld.m_Slot[SLOT5] = GetCardMgr().QuerySlot(SLOT5); m_ConfigOld.m_Slot[SLOT6] = GetCardMgr().QuerySlot(SLOT6); // CPageDisk::HandleFloppyDriveCombo() needs this to be CT_Disk2 (temp, as will replace with PR #955) m_ConfigOld.m_bEnableHDD = HD_CardIsEnabled(); m_ConfigOld.m_bEnableTheFreezesF8Rom = GetPropertySheet().GetTheFreezesF8Rom(); m_ConfigOld.m_videoRefreshRate = GetVideo().GetVideoRefreshRate(); - m_ConfigOld.m_tfeEnabled = get_tfe_enabled(); m_ConfigOld.m_tfeInterface = get_tfe_interface(); // Reset flags each time: @@ -407,6 +408,7 @@ void CPropertySheetHelper::RestoreCurrentConfig(void) // NB. clone-type is encoded in g_Apple2Type SetApple2Type(m_ConfigOld.m_Apple2Type); SetMainCpu(m_ConfigOld.m_CpuType); + SetSlot(SLOT3, m_ConfigOld.m_Slot[SLOT3]); SetSlot(SLOT4, m_ConfigOld.m_Slot[SLOT4]); SetSlot(SLOT5, m_ConfigOld.m_Slot[SLOT5]); HD_SetEnabled(m_ConfigOld.m_bEnableHDD); @@ -465,20 +467,20 @@ bool CPropertySheetHelper::HardwareConfigChanged(HWND hWnd) if (CONFIG_CHANGED(m_videoRefreshRate)) strMsgMain += ". Video refresh rate has changed\n"; - if (CONFIG_CHANGED(m_Slot[4])) - strMsgMain += GetSlot(4); + if (CONFIG_CHANGED(m_Slot[SLOT3])) + strMsgMain += GetSlot(SLOT3); - if (CONFIG_CHANGED(m_Slot[5])) - strMsgMain += GetSlot(5); + if (CONFIG_CHANGED(m_Slot[SLOT4])) + strMsgMain += GetSlot(SLOT4); + + if (CONFIG_CHANGED(m_Slot[SLOT5])) + strMsgMain += GetSlot(SLOT5); if (CONFIG_CHANGED(m_bEnableHDD)) strMsgMain += ". Harddisk(s) have been plugged/unplugged\n"; if (CONFIG_CHANGED(m_bEnableTheFreezesF8Rom)) strMsgMain += ". F8 ROM changed (The Freeze's F8 Rom)\n"; - - if (CONFIG_CHANGED(m_tfeEnabled) || CONFIG_CHANGED(m_tfeInterface)) - strMsgMain += ". Ethernet (TFE) Options\n"; } std::string strMsgPost("\n"); @@ -559,6 +561,12 @@ std::string CPropertySheetHelper::GetCardName(const SS_CARDTYPE CardType) return "Echo"; case CT_SAM: // Soundcard: Software Automated Mouth return "SAM"; + case CT_Uthernet: + return "Uthernet"; + case CT_FourPlay: + return "4Play"; + case CT_SNESMAX: + return "SNES MAX"; default: return "Unknown"; } diff --git a/source/FourPlay.cpp b/source/FourPlay.cpp new file mode 100644 index 00000000..f0341d27 --- /dev/null +++ b/source/FourPlay.cpp @@ -0,0 +1,155 @@ +/* + AppleWin : An Apple //e emulator for Windows + + Copyright (C) 1994-1996, Michael O'Brien + Copyright (C) 1999-2001, Oliver Schmidt + Copyright (C) 2002-2005, Tom Charlesworth + Copyright (C) 2006-2007, Tom Charlesworth, Michael Pohoreski + + AppleWin 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. + + AppleWin 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 AppleWin; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* + FourPlay.CPP + + Emulate a 4Play Joystick card + + 4 ports (one for each of the four possible joysticks) + + Each port + Bit 0 = Up, Active High + Bit 1 = Down, Active High + Bit 2 = Left, Active High + Bit 3 = Right, Active High + Bit 4 = Trigger 2, Active High + Bit 5 = Not Used, Always High + Bit 6 = Trigger 2, Active High + Bit 7 = Trigger 1, Active High + + Address = C0nx + n = 8 + slot number + x = 0 - Joystick 1 + 1 - Joystick 2 + 2 - Joystick 3 + 3 - Joystick 4 + + Alex Lukacz Aug 2021 +*/ +#include "StdAfx.h" + +#include "FourPlay.h" +#include "Memory.h" +#include "YamlHelper.h" + +BYTE __stdcall FourPlayCard::IORead(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles) +{ + BYTE nOutput = MemReadFloatingBus(nExecutedCycles); + BOOL up = 0; + BOOL down = 0; + BOOL left = 0; + BOOL right = 0; + BOOL trigger1 = 0; + BOOL trigger2 = 0; + BOOL trigger3 = 0; + BOOL alwaysHigh = 1; + UINT xAxis = 0; + UINT yAxis = 0; + + JOYINFOEX infoEx; + MMRESULT result = 0; + infoEx.dwSize = sizeof(infoEx); + infoEx.dwFlags = JOY_RETURNPOV | JOY_RETURNBUTTONS; + + switch (addr & 0xF) + { + case 0: // Joystick 1 + result = joyGetPosEx(JOYSTICKID1, &infoEx); + if (result == JOYERR_NOERROR) + { + xAxis = (infoEx.dwXpos >> 8) & 0xFF; + yAxis = (infoEx.dwYpos >> 8) & 0xFF; + trigger1 = infoEx.dwButtons & 0x01; + trigger2 = (infoEx.dwButtons & 0x02) >> 1; + up = yAxis < 103 || infoEx.dwPOV == 0 || infoEx.dwPOV == 4500 || infoEx.dwPOV == 31500; + down = yAxis > 153 || (infoEx.dwPOV >= 13500 && infoEx.dwPOV <= 22500); + left = xAxis < 103 || (infoEx.dwPOV >= 22500 && infoEx.dwPOV <= 31500); + right = xAxis > 153 || (infoEx.dwPOV >= 4500 && infoEx.dwPOV <= 13500); + } + nOutput = up | (down << 1) | (left << 2) | (right << 3) | (alwaysHigh << 5) | (trigger2 << 6) | (trigger1 << 7); + break; + case 1: // Joystick 2 + result = joyGetPosEx(JOYSTICKID2, &infoEx); + if (result == JOYERR_NOERROR) + { + xAxis = (infoEx.dwXpos >> 8) & 0xFF; + yAxis = (infoEx.dwYpos >> 8) & 0xFF; + trigger1 = infoEx.dwButtons & 0x01; + trigger2 = (infoEx.dwButtons & 0x02) >> 1; + up = yAxis < 103 || infoEx.dwPOV == 0 || infoEx.dwPOV == 4500 || infoEx.dwPOV == 31500; + down = yAxis > 153 || (infoEx.dwPOV >= 13500 && infoEx.dwPOV <= 22500); + left = xAxis < 103 || (infoEx.dwPOV >= 22500 && infoEx.dwPOV <= 31500); + right = xAxis > 153 || (infoEx.dwPOV >= 4500 && infoEx.dwPOV <= 13500); + } + nOutput = up | (down << 1) | (left << 2) | (right << 3) | (alwaysHigh << 5) | (trigger2 << 6) | (trigger1 << 7); + break; + case 2: // Joystick 3 + nOutput = FourPlayCard::JOYSTICKSTATIONARY; // esdf - direction buttons, zx - trigger buttons + nOutput = nOutput | (MyGetAsyncKeyState('E') | (MyGetAsyncKeyState('D') << 1) | (MyGetAsyncKeyState('S') << 2) | (MyGetAsyncKeyState('F') << 3) | (MyGetAsyncKeyState('X') << 6) | (MyGetAsyncKeyState('Z') << 7)); + break; + case 3: // Joystick 4 + nOutput = FourPlayCard::JOYSTICKSTATIONARY; // ijkl - direction buttons, nm - trigger buttons + nOutput = nOutput | (MyGetAsyncKeyState('I') | (MyGetAsyncKeyState('K') << 1) | (MyGetAsyncKeyState('J') << 2) | (MyGetAsyncKeyState('L') << 3) | (MyGetAsyncKeyState('M') << 6) | (MyGetAsyncKeyState('N') << 7)); + break; + default: + break; + } + + return nOutput; +} + +BYTE FourPlayCard::MyGetAsyncKeyState(int vKey) +{ + return GetAsyncKeyState(vKey) < 0 ? 1 : 0; +} + +void FourPlayCard::InitializeIO(LPBYTE pCxRomPeripheral, UINT slot) +{ + RegisterIoHandler(slot, &FourPlayCard::IORead, IO_Null, IO_Null, IO_Null, this, NULL); +} + +//=========================================================================== + +static const UINT kUNIT_VERSION = 1; + +std::string FourPlayCard::GetSnapshotCardName(void) +{ + static const std::string name("4Play"); + return name; +} + +void FourPlayCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper) +{ + YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_VERSION); + + YamlSaveHelper::Label unit(yamlSaveHelper, "%s: null\n", SS_YAML_KEY_STATE); + // NB. No state for this card +} + +bool FourPlayCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version) +{ + if (version < 1 || version > kUNIT_VERSION) + throw std::string("Card: wrong version"); + + return true; +} diff --git a/source/FourPlay.h b/source/FourPlay.h new file mode 100644 index 00000000..f236bc08 --- /dev/null +++ b/source/FourPlay.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Card.h" + +class FourPlayCard : public Card +{ +public: + FourPlayCard(UINT slot) : + Card(CT_FourPlay), + m_slot(slot) + { + } + virtual ~FourPlayCard(void) {} + + virtual void Init(void) {}; + virtual void Reset(const bool powerCycle) {}; + + void InitializeIO(LPBYTE pCxRomPeripheral, UINT slot); + + static BYTE __stdcall IORead(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles); + + static std::string GetSnapshotCardName(void); + void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper); + bool LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version); + + static const UINT JOYSTICKSTATIONARY = 0x20; + +private: + static BYTE MyGetAsyncKeyState(int vKey); + + UINT m_slot; +}; diff --git a/source/Memory.cpp b/source/Memory.cpp index a521737b..3defaa3e 100644 --- a/source/Memory.cpp +++ b/source/Memory.cpp @@ -49,6 +49,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "ParallelPrinter.h" #include "Registry.h" #include "SAM.h" +#include "FourPlay.h" +#include "SNESMAX.h" #include "SerialComms.h" #include "Speaker.h" #include "Tape.h" @@ -1727,6 +1729,14 @@ void MemInitializeIO(void) // . Uthernet card has no ROM and only IO mapped at $C0Bx // NB. I/O handlers setup via tfe_init() & update_tfe_interface() } + else if (GetCardMgr().QuerySlot(SLOT3) == CT_FourPlay) + { + dynamic_cast(GetCardMgr().GetRef(SLOT3)).InitializeIO(pCxRomPeripheral, SLOT3); + } + else if (GetCardMgr().QuerySlot(SLOT3) == CT_SNESMAX) + { + dynamic_cast(GetCardMgr().GetRef(SLOT3)).InitializeIO(pCxRomPeripheral, SLOT3); + } // Apple//e: Auxiliary slot contains Extended 80 Column card or RamWorksIII card @@ -1746,6 +1756,14 @@ void MemInitializeIO(void) // { // LoadRom_Clock_Generic(pCxRomPeripheral, SLOT4); // } + else if (GetCardMgr().QuerySlot(SLOT4) == CT_FourPlay) + { + dynamic_cast(GetCardMgr().GetRef(SLOT4)).InitializeIO(pCxRomPeripheral, SLOT4); + } + else if (GetCardMgr().QuerySlot(SLOT4) == CT_SNESMAX) + { + dynamic_cast(GetCardMgr().GetRef(SLOT4)).InitializeIO(pCxRomPeripheral, SLOT4); + } if (GetCardMgr().QuerySlot(SLOT5) == CT_Z80) { @@ -1753,7 +1771,15 @@ void MemInitializeIO(void) } else if (GetCardMgr().QuerySlot(SLOT5) == CT_SAM) { - ConfigureSAM(pCxRomPeripheral, SLOT5); // $C500 : Z80 card + ConfigureSAM(pCxRomPeripheral, SLOT5); // $C500 : SAM card + } + else if (GetCardMgr().QuerySlot(SLOT5) == CT_FourPlay) + { + dynamic_cast(GetCardMgr().GetRef(SLOT5)).InitializeIO(pCxRomPeripheral, SLOT5); + } + else if (GetCardMgr().QuerySlot(SLOT5) == CT_SNESMAX) + { + dynamic_cast(GetCardMgr().GetRef(SLOT5)).InitializeIO(pCxRomPeripheral, SLOT5); } else if (GetCardMgr().QuerySlot(SLOT5) == CT_Disk2) { diff --git a/source/Registry.cpp b/source/Registry.cpp index d252babf..a3e51702 100644 --- a/source/Registry.cpp +++ b/source/Registry.cpp @@ -195,7 +195,8 @@ void RegDeleteConfigSlotSection(UINT slot) if (status == ERROR_SUCCESS) { std::string& keySlot = RegGetSlotSection(slot); - if (RegDeleteKey(keyhandle, keySlot.c_str()) != ERROR_SUCCESS) + LSTATUS status2 = RegDeleteKey(keyhandle, keySlot.c_str()); + if (status2 != ERROR_SUCCESS && status2 != ERROR_FILE_NOT_FOUND) _ASSERT(0); } diff --git a/source/SNESMAX.cpp b/source/SNESMAX.cpp new file mode 100644 index 00000000..acb7110c --- /dev/null +++ b/source/SNESMAX.cpp @@ -0,0 +1,238 @@ +/* + AppleWin : An Apple //e emulator for Windows + + Copyright (C) 1994-1996, Michael O'Brien + Copyright (C) 1999-2001, Oliver Schmidt + Copyright (C) 2002-2005, Tom Charlesworth + Copyright (C) 2006-2007, Tom Charlesworth, Michael Pohoreski + + AppleWin 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. + + AppleWin 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 AppleWin; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* + SNESMAX.CPP + + Emulate a SNES MAX controller serial interface card + + 2 ports (one for each of the two possible joysticks) + + Latch = write any data to C0n0 + Clock = write any data to C0n1 + Data = read from C0n0 + + n = 8 + slot number + + Data + Bit 0 = Not Used + Bit 1 = Not Used + Bit 2 = Not Used + Bit 3 = Not Used + Bit 4 = Not Used + Bit 5 = Not Used + Bit 6 = Controller 2, Active Low + Bit 7 = Controller 1, Active Low + + Once data is read, button presses (for each controller) should be stored in the following structure + Byte 0: B:Y:Sl:St:U:D:L:R + Byte 1: A:X:Fl:Fr:x:x:x:x + + The variable controllerXButtons will stored in the reverse order. + + Alex Lukacz Aug 2021 +*/ +#include "StdAfx.h" + +#include "SNESMAX.h" +#include "Memory.h" +#include "YamlHelper.h" + +BYTE __stdcall SNESMAXCard::IORead(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles) +{ + const UINT slot = ((addr & 0xff) >> 4) - 8; + SNESMAXCard* pCard = (SNESMAXCard*)MemGetSlotParameters(slot); + + BYTE output = MemReadFloatingBus(nExecutedCycles); + + switch (addr & 0xF) + { + case 0: + output = (output & 0x3F) | ((pCard->controller2Buttons & 0x1) << 6) | ((pCard->controller1Buttons & 0x1) << 7); + break; + default: + break; + } + return output; +} + +BYTE __stdcall SNESMAXCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles) +{ + const UINT slot = ((addr & 0xff) >> 4) - 8; + SNESMAXCard* pCard = (SNESMAXCard*)MemGetSlotParameters(slot); + + UINT xAxis = 0; + UINT yAxis = 0; + + JOYINFOEX infoEx; + MMRESULT result = 0; + infoEx.dwSize = sizeof(infoEx); + infoEx.dwFlags = JOY_RETURNPOV | JOY_RETURNBUTTONS; + + UINT& controller1Buttons = pCard->controller1Buttons; // ref to object's controller1Buttons + UINT& controller2Buttons = pCard->controller2Buttons; // ref to object's controller2Buttons + + switch (addr & 0xF) + { + case 0: // Latch + pCard->buttonIndex = 0; + controller1Buttons = 0; + controller2Buttons = 0; + + result = joyGetPosEx(JOYSTICKID1, &infoEx); + if (result == JOYERR_NOERROR) + { + xAxis = (infoEx.dwXpos >> 8) & 0xFF; + yAxis = (infoEx.dwYpos >> 8) & 0xFF; + controller1Buttons = controller1Buttons | ((yAxis < 103 || infoEx.dwPOV == 0 || infoEx.dwPOV == 4500 || infoEx.dwPOV == 31500) << 4); // U Button + controller1Buttons = controller1Buttons | ((yAxis > 153 || (infoEx.dwPOV >= 13500 && infoEx.dwPOV <= 22500)) << 5); // D Button + controller1Buttons = controller1Buttons | ((xAxis < 103 || (infoEx.dwPOV >= 22500 && infoEx.dwPOV <= 31500)) << 6); // L Button + controller1Buttons = controller1Buttons | ((xAxis > 153 || (infoEx.dwPOV >= 4500 && infoEx.dwPOV <= 13500)) << 7); // R Button +// controller1Buttons = controller1Buttons | 0 * 0x1000; // spare Button +// controller1Buttons = controller1Buttons | 0 * 0x2000; // spare Button +// controller1Buttons = controller1Buttons | 0 * 0x4000; // spare Button +// controller1Buttons = controller1Buttons | 0 * 0x8000; // spare Button + + if (pCard->m_altControllerType[0]) + { + // 8BitDo NES30 PRO + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0002) >> 1); // B Button + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0010) >> 3); // Y Button + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0400) >> 8); // Sl Button + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0800) >> 8); // St Button + + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0001) << 8); // A Button + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0008) << 6); // X Button + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0100) << 2) | ((infoEx.dwButtons & 0x0040) << 4); // Fl Button + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0200) << 2) | ((infoEx.dwButtons & 0x0080) << 4); // Fr Button + } + else + { + // Logitech F310, Dualshock 4 + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0002) >> 1); // B Button + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0001) << 1); // Y Button + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0100) >> 6); // Sl Button + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0200) >> 6); // St Button + + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0004) << 6); // A Button + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0008) << 6); // X Button + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0010) << 6) | ((infoEx.dwButtons & 0x0040) << 4); // Fl Button + controller1Buttons = controller1Buttons | ((infoEx.dwButtons & 0x0020) << 6) | ((infoEx.dwButtons & 0x0080) << 4); // Fr Button + } + controller1Buttons = controller1Buttons | 0x10000; // Controller plugged in status. + } + controller1Buttons = ~controller1Buttons; + + result = joyGetPosEx(JOYSTICKID2, &infoEx); + if (result == JOYERR_NOERROR) + { + xAxis = (infoEx.dwXpos >> 8) & 0xFF; + yAxis = (infoEx.dwYpos >> 8) & 0xFF; + controller2Buttons = controller2Buttons | ((yAxis < 103 || infoEx.dwPOV == 0 || infoEx.dwPOV == 4500 || infoEx.dwPOV == 31500) << 4); // U Button + controller2Buttons = controller2Buttons | ((yAxis > 153 || (infoEx.dwPOV >= 13500 && infoEx.dwPOV <= 22500)) << 5); // D Button + controller2Buttons = controller2Buttons | ((xAxis < 103 || (infoEx.dwPOV >= 22500 && infoEx.dwPOV <= 31500)) << 6); // L Button + controller2Buttons = controller2Buttons | ((xAxis > 153 || (infoEx.dwPOV >= 4500 && infoEx.dwPOV <= 13500)) << 7); // R Button +// controller2Buttons = controller2Buttons | 0 * 0x1000; // spare Button +// controller2Buttons = controller2Buttons | 0 * 0x2000; // spare Button +// controller2Buttons = controller2Buttons | 0 * 0x4000; // spare Button +// controller2Buttons = controller2Buttons | 0 * 0x8000; // spare Button + + if (pCard->m_altControllerType[1]) + { + // 8BitDo NES30 PRO + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0002) >> 1); // B Button + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0010) >> 3); // Y Button + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0400) >> 8); // Sl Button + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0800) >> 8); // St Button + + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0001) << 8); // A Button + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0008) << 6); // X Button + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0100) << 2) | ((infoEx.dwButtons & 0x0040) << 4); // Fl Button + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0200) << 2) | ((infoEx.dwButtons & 0x0080) << 4); // Fr Button + } + else + { + // Logitech F310, Dualshock 4 + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0002) >> 1); // B Button + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0001) << 1); // Y Button + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0100) >> 6); // Sl Button + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0200) >> 6); // St Button + + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0004) << 6); // A Button + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0008) << 6); // X Button + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0010) << 6) | ((infoEx.dwButtons & 0x0040) << 4); // Fl Button + controller2Buttons = controller2Buttons | ((infoEx.dwButtons & 0x0020) << 6) | ((infoEx.dwButtons & 0x0080) << 4); // Fr Button + } + controller2Buttons = controller2Buttons | 0x10000; // Controller plugged in status. + } + controller2Buttons = ~controller2Buttons; + + break; + case 1: // Clock + if (pCard->buttonIndex <= 16) + { + pCard->buttonIndex++; + controller1Buttons = controller1Buttons >> 1; + controller2Buttons = controller2Buttons >> 1; + } + break; + default: + break; + } + + return 0; +} + +void SNESMAXCard::InitializeIO(LPBYTE pCxRomPeripheral, UINT slot) +{ + RegisterIoHandler(slot, &SNESMAXCard::IORead, &SNESMAXCard::IOWrite, IO_Null, IO_Null, this, NULL); +} + +//=========================================================================== + +static const UINT kUNIT_VERSION = 1; + +#define SS_YAML_KEY_BUTTON_INDEX "Button Index" + +std::string SNESMAXCard::GetSnapshotCardName(void) +{ + static const std::string name("SNES MAX"); + return name; +} + +void SNESMAXCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper) +{ + YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_VERSION); + + YamlSaveHelper::Label unit(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE); + yamlSaveHelper.SaveUint(SS_YAML_KEY_BUTTON_INDEX, buttonIndex); +} + +bool SNESMAXCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version) +{ + if (version < 1 || version > kUNIT_VERSION) + throw std::string("Card: wrong version"); + + buttonIndex = yamlLoadHelper.LoadUint(SS_YAML_KEY_BUTTON_INDEX); + + return true; +} diff --git a/source/SNESMAX.h b/source/SNESMAX.h new file mode 100644 index 00000000..7e179b42 --- /dev/null +++ b/source/SNESMAX.h @@ -0,0 +1,42 @@ +#pragma once + +#include "Card.h" +#include "CmdLine.h" + +class SNESMAXCard : public Card +{ +public: + SNESMAXCard(UINT slot) : + Card(CT_SNESMAX), + m_slot(slot) + { + buttonIndex = 0; + controller1Buttons = 0; + controller2Buttons = 0; + + m_altControllerType[0] = g_cmdLine.snesMaxAltControllerType[0]; + m_altControllerType[1] = g_cmdLine.snesMaxAltControllerType[1]; + } + virtual ~SNESMAXCard(void) {} + + virtual void Init(void) {}; + virtual void Reset(const bool powerCycle) {}; + + void InitializeIO(LPBYTE pCxRomPeripheral, UINT slot); + + static BYTE __stdcall IORead(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles); + static BYTE __stdcall IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles); + + static std::string GetSnapshotCardName(void); + void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper); + bool LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version); + +private: + UINT m_slot; + + UINT buttonIndex; + UINT controller1Buttons; + UINT controller2Buttons; + + bool m_altControllerType[2]; +}; diff --git a/source/SaveState.cpp b/source/SaveState.cpp index caf58958..5f264df4 100644 --- a/source/SaveState.cpp +++ b/source/SaveState.cpp @@ -36,6 +36,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "CPU.h" #include "Debug.h" #include "Disk.h" +#include "FourPlay.h" #include "Joystick.h" #include "Keyboard.h" #include "LanguageCard.h" @@ -45,6 +46,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "ParallelPrinter.h" #include "Pravets.h" #include "SerialComms.h" +#include "SNESMAX.h" #include "Speaker.h" #include "Speech.h" #include "z80emu.h" @@ -314,7 +316,7 @@ static void ParseSlots(YamlLoadHelper& yamlLoadHelper, UINT unitVersion) std::string card = yamlLoadHelper.LoadString(SS_YAML_KEY_CARD); UINT cardVersion = yamlLoadHelper.LoadUint(SS_YAML_KEY_VERSION); - if (!yamlLoadHelper.GetSubMap(std::string(SS_YAML_KEY_STATE))) + if (!yamlLoadHelper.GetSubMap(std::string(SS_YAML_KEY_STATE), true)) // NB. For some cards, State can be null throw std::string(SS_YAML_KEY_UNIT ": Expected sub-map name: " SS_YAML_KEY_STATE); SS_CARDTYPE type = CT_Empty; @@ -378,6 +380,18 @@ static void ParseSlots(YamlLoadHelper& yamlLoadHelper, UINT unitVersion) CreateLanguageCard(); bRes = GetLanguageCard()->LoadSnapshot(yamlLoadHelper, slot, cardVersion); } + else if (card == FourPlayCard::GetSnapshotCardName()) + { + type = CT_FourPlay; + GetCardMgr().Insert(slot, type); + bRes = dynamic_cast(GetCardMgr().GetRef(slot)).LoadSnapshot(yamlLoadHelper, slot, cardVersion); + } + else if (card == SNESMAXCard::GetSnapshotCardName()) + { + type = CT_SNESMAX; + GetCardMgr().Insert(slot, type); + bRes = dynamic_cast(GetCardMgr().GetRef(slot)).LoadSnapshot(yamlLoadHelper, slot, cardVersion); + } else { throw std::string("Slots: Unknown card: " + card); // todo: don't throw - just ignore & continue @@ -635,6 +649,14 @@ void Snapshot_SaveState(void) if (GetCardMgr().QuerySlot(SLOT7) == CT_GenericHDD) HD_SaveSnapshot(yamlSaveHelper); + + for (UINT slot = SLOT3; slot <= SLOT5; slot++) + { + if (GetCardMgr().QuerySlot(slot) == CT_FourPlay) + dynamic_cast(GetCardMgr().GetRef(slot)).SaveSnapshot(yamlSaveHelper); + else if (GetCardMgr().QuerySlot(slot) == CT_SNESMAX) + dynamic_cast(GetCardMgr().GetRef(slot)).SaveSnapshot(yamlSaveHelper); + } } // Miscellaneous diff --git a/source/Tfe/tfe.cpp b/source/Tfe/tfe.cpp index cbe137a7..25ec9e09 100644 --- a/source/Tfe/tfe.cpp +++ b/source/Tfe/tfe.cpp @@ -1500,9 +1500,7 @@ return ret; void get_disabled_state(int * param) { - -*param = tfe_cannot_use; - + *param = tfe_cannot_use; } int update_tfe_interface(const std::string & name) diff --git a/source/Utilities.cpp b/source/Utilities.cpp index 9b59cbe5..ddbc0bee 100644 --- a/source/Utilities.cpp +++ b/source/Utilities.cpp @@ -256,6 +256,12 @@ void LoadConfiguration(void) if(REGLOAD(TEXT(REGVALUE_MOUSE_RESTRICT_TO_WINDOW), &dwTmp)) GetPropertySheet().SetMouseRestrictToWindow(dwTmp); + // + + TCHAR szFilename[MAX_PATH]; + + // + for (UINT slot = SLOT0; slot <= SLOT7; slot++) { std::string& regSection = RegGetConfigSlotSection(slot); @@ -263,10 +269,38 @@ void LoadConfiguration(void) if (RegLoadValue(regSection.c_str(), REGVALUE_CARD_TYPE, TRUE, &dwTmp)) { GetCardMgr().Insert(slot, (SS_CARDTYPE)dwTmp); + + if (slot == SLOT3) + { + if ((SS_CARDTYPE)dwTmp == CT_Uthernet) // TODO: move this to when UthernetCard object is instantiated + { + tfe_enabled = 1; + + std::string& regSection = RegGetConfigSlotSection(slot); + if (RegLoadString(regSection.c_str(), REGVALUE_UTHERNET_INTERFACE, TRUE, szFilename, MAX_PATH, TEXT(""))) + update_tfe_interface(szFilename); + } + else + { + tfe_enabled = 0; + } + } } else // legacy (AppleWin 1.30.3 or earlier) { - if (slot == SLOT4 && REGLOAD(TEXT(REGVALUE_SLOT4), &dwTmp)) + if (slot == SLOT3) + { + DWORD tfeEnabled; + REGLOAD_DEFAULT(TEXT(REGVALUE_UTHERNET_ACTIVE), &tfeEnabled, 0); + tfe_enabled = tfeEnabled ? 1 : 0; + + GetCardMgr().Insert(SLOT3, get_tfe_enabled() ? CT_Uthernet : CT_Empty); + + // TODO: move this to when UthernetCard object is instantiated + RegLoadString(TEXT(REG_CONFIG), TEXT(REGVALUE_UTHERNET_INTERFACE), 1, szFilename, MAX_PATH, TEXT("")); + update_tfe_interface(szFilename); + } + else if (slot == SLOT4 && REGLOAD(TEXT(REGVALUE_SLOT4), &dwTmp)) GetCardMgr().Insert(SLOT4, (SS_CARDTYPE)dwTmp); else if (slot == SLOT5 && REGLOAD(TEXT(REGVALUE_SLOT5), &dwTmp)) GetCardMgr().Insert(SLOT5, (SS_CARDTYPE)dwTmp); @@ -277,8 +311,6 @@ void LoadConfiguration(void) // - TCHAR szFilename[MAX_PATH]; - // Load save-state pathname *before* inserting any harddisk/disk images (for both init & reinit cases) // NB. inserting harddisk/disk can change snapshot pathname RegLoadString(TEXT(REG_CONFIG), TEXT(REGVALUE_SAVESTATE_FILENAME), 1, szFilename, MAX_PATH, TEXT("")); // Can be pathname or just filename @@ -306,15 +338,6 @@ void LoadConfiguration(void) // - DWORD dwTfeEnabled; - REGLOAD_DEFAULT(TEXT(REGVALUE_UTHERNET_ACTIVE), &dwTfeEnabled, 0); - tfe_enabled = dwTfeEnabled ? 1 : 0; - - RegLoadString(TEXT(REG_CONFIG), TEXT(REGVALUE_UTHERNET_INTERFACE), 1, szFilename, MAX_PATH, TEXT("")); - update_tfe_interface(szFilename); - - // - RegLoadString(TEXT(REG_CONFIG), TEXT(REGVALUE_PRINTER_FILENAME), 1, szFilename, MAX_PATH, TEXT("")); Printer_SetFilename(szFilename); // If not in Registry than default will be used diff --git a/source/YamlHelper.cpp b/source/YamlHelper.cpp index f6a4c366..8bfb1737 100644 --- a/source/YamlHelper.cpp +++ b/source/YamlHelper.cpp @@ -192,10 +192,10 @@ std::string YamlHelper::GetMapValue(MapYaml& mapYaml, const std::string& key, bo return value; } -bool YamlHelper::GetSubMap(MapYaml** mapYaml, const std::string& key) +bool YamlHelper::GetSubMap(MapYaml** mapYaml, const std::string& key, const bool canBeNull = false) { MapYaml::const_iterator iter = (*mapYaml)->find(key); - if (iter == (*mapYaml)->end() || iter->second.subMap == NULL) + if (iter == (*mapYaml)->end() || (!canBeNull && iter->second.subMap == NULL)) { return false; // not found } diff --git a/source/YamlHelper.h b/source/YamlHelper.h index 69a38d5e..da18d608 100644 --- a/source/YamlHelper.h +++ b/source/YamlHelper.h @@ -50,7 +50,7 @@ private: int ParseMap(MapYaml& mapYaml); std::string GetMapValue(MapYaml& mapYaml, const std::string &key, bool& bFound); UINT LoadMemory(MapYaml& mapYaml, const LPBYTE pMemBase, const size_t kAddrSpaceSize); - bool GetSubMap(MapYaml** mapYaml, const std::string &key); + bool GetSubMap(MapYaml** mapYaml, const std::string &key, const bool canBeNull /*= false*/); void GetMapRemainder(std::string& mapName, MapYaml& mapYaml); void MakeAsciiToHexTable(void); @@ -103,11 +103,11 @@ public: void LoadMemory(const LPBYTE pMemBase, const size_t size); void LoadMemory(std::vector& memory, const size_t size); - bool GetSubMap(const std::string & key) + bool GetSubMap(const std::string & key, const bool canBeNull=false) { YamlStackItem item = {m_pMapYaml, m_currentMapName}; m_stackMap.push(item); - bool res = m_yamlHelper.GetSubMap(&m_pMapYaml, key); + bool res = m_yamlHelper.GetSubMap(&m_pMapYaml, key, canBeNull); if (!res) m_stackMap.pop(); else