diff --git a/help/CommandLine.html b/help/CommandLine.html index 1c09c7b2..0d6870aa 100644 --- a/help/CommandLine.html +++ b/help/CommandLine.html @@ -109,7 +109,8 @@ Use Right Alt (AltGr) & Right Control for Open Apple & Solid Apple keys respectively.
Caveat: Right Control + F2 will do the //e self test (as Right Control is now both Ctrl and Solid Apple!). A workaround is just to use the Left Control key.

-swap-buttons
- Swap the Windows keys used for Open Apple & Solid Apple keys.

+ Swap buttons 0 and 1 from all input devices.
+ EG. the Windows keys used for Open Apple & Solid Apple keys, and the current device being used to emulate a joystick (keyboard, real joystick or mouse)

-use-real-printer
Enables Advanced configuration control to allow dumping to a real printer

diff --git a/help/cfg-input.html b/help/cfg-input.html index 854c811d..93ff7f64 100644 --- a/help/cfg-input.html +++ b/help/cfg-input.html @@ -33,7 +33,7 @@ then you should leave these values at 0.
  • When cursor keys are used for joystick emulation and are allowed to be read from the keyboard, then some games won't work correctly (eg. Lode Runner).
  • When cursor keys are blocked from being read from the keyboard, then simple command-line cursor editing in AppleSoft won't work.
  • -
  • Swap 0/1: Swap buttons 0 and 1.
  • +
  • Swap 0/1: Swap buttons 0 and 1 from all input devices.
  • Auto-fire (all 3 buttons): For each button pressed, the button's state will be toggled when read.
  • Keyboard auto-centering: When keys used for joystick emulation are released then the joystick will return to the central position.
  • diff --git a/source/Disk.cpp b/source/Disk.cpp index 7de67282..df476444 100644 --- a/source/Disk.cpp +++ b/source/Disk.cpp @@ -54,6 +54,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // . if false && I/O ReadWrite($C0EC) && drive is spinning, then advance the track buffer's nibble index (to simulate spinning). // Also m_enhanceDisk is persisted to the save-state, so it's an attribute of the DiskII interface card. +// NB. Non-standard 4&4, with Vol=0x00 and Chk=0x00 (only a few match, eg. Wasteland, Legacy of the Ancients, Planetfall, Border Zone & Wizardry). [*1] +const BYTE Disk2InterfaceCard::m_T00S00Pattern[] = {0xD5,0xAA,0x96,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xDE}; + Disk2InterfaceCard::Disk2InterfaceCard(UINT slot) : Card(CT_Disk2), m_slot(slot) @@ -1043,6 +1046,9 @@ void Disk2InterfaceCard::ResetLogicStateSequencer(void) m_resetSequencer = true; m_writeStarted = false; m_dbgLatchDelayedCnt = 0; + + m_T00S00PatternIdx = 0; + m_foundT00S00Pattern = false; } UINT Disk2InterfaceCard::GetBitCellDelta(const ULONG uExecutedCycles) @@ -1118,6 +1124,48 @@ __forceinline void Disk2InterfaceCard::IncBitStream(FloppyDisk& floppy) } } +void Disk2InterfaceCard::PreJitterCheck(int phase, BYTE latch) +{ + if (phase != 0 || (latch & 0x80) == 0) + return; + + if (latch == m_T00S00Pattern[m_T00S00PatternIdx]) + { + m_T00S00PatternIdx++; + if (m_T00S00PatternIdx == sizeof(m_T00S00Pattern)) + m_foundT00S00Pattern = true; // 6502 code has just read latch nibbles for T$00,S$00 address prologue + } + else + { + m_T00S00PatternIdx = 0; + } +} + +// GH#930: After T$00,S$00 randomly skip 1 bit-cell. +// . PreJitterCheck() condition met && skipped a big number of bit-cells. +// . Fix is just for 'Wasteland' and 'Legacy of the Ancients' (but shouldn't interfere with any other woz images). +// . NB. This is likely to be the transition from DiskII firmware ($C6xx) to user-code ($801), +// so skipping 1 bit-cell here shouldn't matter. +// . And (see comment [*1]) the T00S00 pattern only matches a handful of titles. +void Disk2InterfaceCard::AddJitter(int phase, FloppyDisk& floppy) +{ + if (phase == 0 && m_foundT00S00Pattern) + { + if (rand() < RAND_THRESHOLD(1, 10)) + { + LogOutput("Disk: T$00 jitter - slip 1 bitcell (PC=%04X)\n", regs.pc); + IncBitStream(floppy); + } + else + { + LogOutput("Disk: T$00 jitter - *** SKIP *** (PC=%04X)\n", regs.pc); + } + } + + m_T00S00PatternIdx = 0; + m_foundT00S00Pattern = false; +} + void __stdcall Disk2InterfaceCard::DataLatchReadWriteWOZ(WORD pc, WORD addr, BYTE bWrite, ULONG uExecutedCycles) { _ASSERT(m_seqFunc.function != dataShiftWrite); @@ -1161,6 +1209,8 @@ void __stdcall Disk2InterfaceCard::DataLatchReadWriteWOZ(WORD pc, WORD addr, BYT m_latchDelay = 0; drive.m_headWindow = 0; + + AddJitter(drive.m_phase, floppy); // Only call when skipping a big number of bit-cells (ie. >significantBitCells) } if (!bWrite) @@ -1173,6 +1223,8 @@ void __stdcall Disk2InterfaceCard::DataLatchReadWriteWOZ(WORD pc, WORD addr, BYT } DataLatchReadWOZ(pc, addr, bitCellRemainder); + + PreJitterCheck(drive.m_phase, m_floppyLatch); // Pre: m_floppyLatch just updated } else { @@ -1198,7 +1250,6 @@ void Disk2InterfaceCard::DataLatchReadWOZ(WORD pc, WORD addr, UINT bitCellRemain #if _DEBUG static int dbgWOZ = 0; - if (dbgWOZ) { dbgWOZ = 0; @@ -1206,9 +1257,6 @@ void Disk2InterfaceCard::DataLatchReadWOZ(WORD pc, WORD addr, UINT bitCellRemain } #endif - // Only extraCycles of 2 & 3 can hold the latch for another bitCell period, eg. m_latchDelay: 3->5 or 7->9 - UINT extraLatchDelay = ((UINT)floppy.m_extraCycles >= 2) ? 2 : 0; // GH#733 (0,1->0; 2,3->2) - for (UINT i = 0; i < bitCellRemainder; i++) { BYTE n = floppy.m_trackimage[floppy.m_byte]; @@ -1233,10 +1281,6 @@ void Disk2InterfaceCard::DataLatchReadWOZ(WORD pc, WORD addr, UINT bitCellRemain if (m_latchDelay) { - if (i == bitCellRemainder-1) // On last bitCell - m_latchDelay += extraLatchDelay; // +0 or +2 - extraLatchDelay = 0; // and always clear (even when not last bitCell) - m_latchDelay -= 4; if (m_latchDelay < 0) m_latchDelay = 0; diff --git a/source/Disk.h b/source/Disk.h index baaf5421..24612c19 100644 --- a/source/Disk.h +++ b/source/Disk.h @@ -207,6 +207,9 @@ private: void InitFirmware(LPBYTE pCxRomPeripheral); void UpdateLatchForEmptyDrive(FloppyDrive* pDrive); + void PreJitterCheck(int phase, BYTE latch); + void AddJitter(int phase, FloppyDisk& floppy); + void SaveSnapshotFloppy(YamlSaveHelper& yamlSaveHelper, UINT unit); void SaveSnapshotDriveUnit(YamlSaveHelper& yamlSaveHelper, UINT unit); bool LoadSnapshotFloppy(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version, std::vector& track); @@ -274,6 +277,11 @@ private: SEQUENCER_FUNCTION m_seqFunc; UINT m_dbgLatchDelayedCnt; + // Jitter (GH#930) + static const BYTE m_T00S00Pattern[]; + UINT m_T00S00PatternIdx; + bool m_foundT00S00Pattern; + // Debug: #if LOG_DISK_NIBBLES_USE_RUNTIME_VAR bool m_bLogDisk_NibblesRW; // From VS Debugger, change this to true/false during runtime for precise nibble logging diff --git a/source/Joystick.cpp b/source/Joystick.cpp index 192ef4fa..69cb5247 100644 --- a/source/Joystick.cpp +++ b/source/Joystick.cpp @@ -345,14 +345,13 @@ BOOL JoyProcessKey(int virtkey, bool extended, bool down, bool autorep) BOOL keychange = 0; bool bIsCursorKey = false; - const bool swapButtons0and1 = GetPropertySheet().GetButtonsSwapState(); - if (virtKeyWithExtended == g_buttonVirtKey[!swapButtons0and1 ? 0 : 1]) + if (virtKeyWithExtended == g_buttonVirtKey[0]) { keychange = 1; keydown[JK_OPENAPPLE] = down; } - else if (virtKeyWithExtended == g_buttonVirtKey[!swapButtons0and1 ? 1 : 0]) + else if (virtKeyWithExtended == g_buttonVirtKey[1]) { keychange = 1; keydown[JK_CLOSEDAPPLE] = down; @@ -367,7 +366,7 @@ BOOL JoyProcessKey(int virtkey, bool extended, bool down, bool autorep) { keydown[virtkey-VK_NUMPAD1] = down; } - else // NumLock off + else // NumLock off (except for '0' and '.') { switch (virtkey) { @@ -380,8 +379,10 @@ BOOL JoyProcessKey(int virtkey, bool extended, bool down, bool autorep) case VK_HOME: keydown[JK_UPLEFT] = down; break; case VK_UP: keydown[JK_UP] = down; break; case VK_PRIOR: keydown[JK_UPRIGHT] = down; break; - case VK_NUMPAD0: keydown[JK_BUTTON0] = down; break; - case VK_DECIMAL: keydown[JK_BUTTON1] = down; break; + case VK_INSERT: // fall through... (NB. extended=0 for NumPad's Insert) + case VK_NUMPAD0: keydown[JK_BUTTON0] = down; break; // NumLock on + case VK_DELETE: // fall through... (NB. extended=0 for NumPad's Delete) + case VK_DECIMAL: keydown[JK_BUTTON1] = down; break; // NumLock on default: keychange = 0; break; } } @@ -502,8 +503,8 @@ BOOL JoyProcessKey(int virtkey, bool extended, bool down, bool autorep) static void DoAutofire(UINT uButton, BOOL& pressed) { - static BOOL toggle[3] = {0}; - static BOOL lastPressed[3] = {0}; + static BOOL toggle[3] = {0,0,0}; + static BOOL lastPressed[3] = {0,0,0}; BOOL nowPressed = pressed; if (GetPropertySheet().GetAutofire(uButton) && pressed) @@ -565,6 +566,32 @@ BYTE __stdcall JoyportReadButton(WORD address, ULONG nExecutedCycles) return MemReadFloatingBus(pressed, nExecutedCycles); } +static BOOL CheckButton0Pressed(void) +{ + BOOL pressed = buttonlatch[0] || + joybutton[0] || + setbutton[0] || + keydown[JK_OPENAPPLE]; + + if (joyinfo[joytype[1]] != DEVICE_KEYBOARD) // NB. always joytype[1] regardless if button is 0 or 1 + pressed = pressed || keydown[JK_BUTTON0]; + + return pressed; +} + +static BOOL CheckButton1Pressed(void) +{ + BOOL pressed = buttonlatch[1] || + joybutton[1] || + setbutton[1] || + keydown[JK_CLOSEDAPPLE]; + + if (joyinfo[joytype[1]] != DEVICE_KEYBOARD) // NB. always joytype[1] regardless if button is 0 or 1 + pressed = pressed || keydown[JK_BUTTON1]; + + return pressed; +} + BYTE __stdcall JoyReadButton(WORD pc, WORD address, BYTE, BYTE, ULONG nExecutedCycles) { address &= 0xFF; @@ -581,23 +608,27 @@ BYTE __stdcall JoyReadButton(WORD pc, WORD address, BYTE, BYTE, ULONG nExecutedC return JoyportReadButton(address, nExecutedCycles); } - BOOL pressed = 0; + const bool swapButtons0and1 = GetPropertySheet().GetButtonsSwapState(); + + BOOL pressed = FALSE; switch (address) { case 0x61: - pressed = (buttonlatch[0] || joybutton[0] || setbutton[0] || keydown[JK_OPENAPPLE]); - if(joyinfo[joytype[1]] != DEVICE_KEYBOARD) // BUG? joytype[1] should be [0] ? - pressed = (pressed || keydown[JK_BUTTON0]); - buttonlatch[0] = 0; - DoAutofire(0, pressed); + { + pressed = !swapButtons0and1 ? CheckButton0Pressed() : CheckButton1Pressed(); + const UINT button0 = !swapButtons0and1 ? 0 : 1; + buttonlatch[button0] = 0; + DoAutofire(button0, pressed); + } break; case 0x62: - pressed = (buttonlatch[1] || joybutton[1] || setbutton[1] || keydown[JK_CLOSEDAPPLE]); - if(joyinfo[joytype[1]] != DEVICE_KEYBOARD) - pressed = (pressed || keydown[JK_BUTTON1]); - buttonlatch[1] = 0; - DoAutofire(1, pressed); + { + pressed = !swapButtons0and1 ? CheckButton1Pressed() : CheckButton0Pressed(); + const UINT button1 = !swapButtons0and1 ? 1 : 0; + buttonlatch[button1] = 0; + DoAutofire(button1, pressed); + } break; case 0x63: diff --git a/source/Mockingboard.cpp b/source/Mockingboard.cpp index 361cdf9c..b99c692d 100644 --- a/source/Mockingboard.cpp +++ b/source/Mockingboard.cpp @@ -195,9 +195,6 @@ static const SHORT nWaveDataMax = (SHORT)0x7FFF; static short g_nMixBuffer[g_dwDSBufferSize / sizeof(short)]; static VOICE MockingboardVoice; -static bool g_bCritSectionValid = false; // Deleting CritialSection when not valid causes crash on Win98 -static CRITICAL_SECTION g_CriticalSection; // To guard 6522's IFR - static UINT g_cyclesThisAudioFrame = 0; //--------------------------------------------------------------------------- @@ -577,19 +574,13 @@ static USHORT SetTimerSyncEvent(UINT id, BYTE reg, USHORT timerLatch) static void UpdateIFR(SY6522_AY8910* pMB, BYTE clr_ifr, BYTE set_ifr=0) { - // Need critical section to avoid data-race: main thread & SSI263Thread can both access IFR -- no longer a SSI263Thread - // . NB. Loading a save-state just directly writes into 6522.IFR (which is fine) - if (g_bCritSectionValid) EnterCriticalSection(&g_CriticalSection); - { - pMB->sy6522.IFR &= ~clr_ifr; - pMB->sy6522.IFR |= set_ifr; + pMB->sy6522.IFR &= ~clr_ifr; + pMB->sy6522.IFR |= set_ifr; - if (pMB->sy6522.IFR & pMB->sy6522.IER & 0x7F) - pMB->sy6522.IFR |= 0x80; - else - pMB->sy6522.IFR &= 0x7F; - } - if (g_bCritSectionValid) LeaveCriticalSection(&g_CriticalSection); + if (pMB->sy6522.IFR & pMB->sy6522.IER & 0x7F) + pMB->sy6522.IFR |= 0x80; + else + pMB->sy6522.IFR &= 0x7F; // Now update the IRQ signal from all 6522s // . OR-sum of all active TIMER1, TIMER2 & SPEECH sources (from all 6522s) @@ -1140,9 +1131,6 @@ void MB_Initialize() LogFileOutput("MB_Initialize: MB_Reset()\n"); } - InitializeCriticalSection(&g_CriticalSection); - g_bCritSectionValid = true; - for (int id=0; idm_active)