Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Andrea Odetti 2020-05-24 19:18:49 +01:00
commit 0706697b13
20 changed files with 494 additions and 146 deletions

View file

@ -121,11 +121,14 @@ C017 RDC3ROM
C018 RD80STORE
C019 RDVBLBAR
C01A RDTEXT
C01B RDMIXED
C01C RDPAGE2
C01D RDHIRES
C01E ALTCHARSET
C01F RD80COL
C020 TAPEOUT
C030 SPKR
C040 STROBE
C050 TXTCLR
C051 TXTSET
C052 MIXCLR
@ -145,18 +148,48 @@ C05F CLRAN3
C060 TAPEIN
C061 BUTNO
C062 BUTN1
C063 BUTN2
C064 PADDL0
C065 PADDL1
C066 PADDL2
C067 PADDL3
C070 PTRIG
C080 STEPPER
C081 ROMIN
; Slot-0: Language Card
C080 LCRAMIN2
C081 ROMIN2
C082 LCROMIN2
C083 LCBANK2
C08A ENABLE1
C08B LCBANK1/ENABLE2
C08C DATASTROBE
C08D LOADLATCH
C08E SETREADMODE
C08E WRITEPROT
C08F SETWRITEMODE
C084 LCRAMIN2_
C085 ROMIN2_
C086 LCROMIN2_
C087 LCBANK2_
C088 LCRAMIN1
C089 ROMIN1
C08A LCROMIN1
C08B LCBANK1
C08C LCRAMIN1_
C08D ROMIN1_
C08E LCROMIN1_
C08F LCBANK1_
; Slot-6: DiskII interface
C0E0 DRV_P0_OFF
C0E1 DRV_P0_ON
C0E2 DRV_P1_OFF
C0E3 DRV_P1_ON
C0E4 DRV_P2_OFF
C0E5 DRV_P2_ON
C0E6 DRV_P3_OFF
C0E7 DRV_P3_ON
C0E8 DRV_OFF
C0E9 DRV_ON
C0EA DRV_SEL1
C0EB DRV_SEL2
C0EC DRV_SHIFT
C0ED DRV_LOAD
C0EE DRV_READ
C0EF DRV_WRITE
; Firmware
; Renamed due to ROM name collision

View file

@ -9,6 +9,18 @@ https://github.com/AppleWin/AppleWin/issues/new
Tom Charlesworth
1.29.12.0 - 26 Apr 2020
-----------------------
. [PR #757] Allow use of an INI-file for configuration instead of the Registry (fixes #709).
- new command line switch: -conf <INI-file>
. [Change #773] Added Apple II J-Plus support.
. [Bug #778] Fixed for when Joystick(s) are disabled.
. [Bug #777] Fixed Phasor speech (SSI263) to match Mockingboard (when in Mockingboard mode).
- fixes: #698 (Rescue Raiders), #753 (Bejeweled).
- also improvements to Phasor card's native Phasor mode.
. [PR #775] Debugger: Fixed so that hitting "=" in the debugger sets PC to the current cursor address.
1.29.11.0 - 27 Mar 2020
-----------------------
. [Bug #771] Added new command line switch to load custom ROM: -rom <file>.

View file

@ -1,4 +1,11 @@
/*
2.9.1.0 Added: Bookmarks now have their own indicator (a number with a box around it) and replace the ":" seperator. Updated Debug_Font.bmp
.18 Fixed: Resetting bookmarks wasn't setting the total bookmarks back to zero.
.17 Fixed: If all bookmarks were used then setting a new one wouldn't update an existing one to the new address.
.16 Fixed: Replacing an existing bookmark incorrectly increased the total bookmark count.
.15 Cleanup: HELP CALC examples and See also.
.14 Fixed: HELP JSR wasn't color-coding syntax.
.13 Added: PROFILE LIST now shows how many clock cycles were executed.

View file

@ -11,7 +11,7 @@
<p>The team&nbsp;would like to thank the following people for their contributions:</p>
<p style="MARGIN-LEFT: 40px">Brian Broker: This HTML / CHM help file</p>
<p style="MARGIN-LEFT: 40px">Thomas Stahl: TV emulation mode (up to v1.25.0.4)</p>
<p style="MARGIN-LEFT: 40px">Chris Foxwell: SSI263 phoneme samples</p>
<p style="MARGIN-LEFT: 40px">Greg Hedger: SSI263 phoneme samples</p>
<p style="MARGIN-LEFT: 40px">Robert Hoem: Hard disk card (source module &amp; f/w)</p>
<p style="MARGIN-LEFT: 40px">VICE team: TFE, Z80, MC6821 PIA emulation modules (<a href="http://vice-emu.sourceforge.net/index.html#developers">http://vice-emu.sourceforge.net/index.html#developers</a>)<br>
- In particular, Spiro Trikaliotis for TFE, whose code Glenn Jones adapted for Uthernet support</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View file

@ -1,4 +1,4 @@
#define APPLEWIN_VERSION 1,29,11,0
#define APPLEWIN_VERSION 1,29,12,0
#define xstr(a) str(a)
#define str(a) #a

View file

@ -123,11 +123,51 @@ CSpeech g_Speech;
//===========================================================================
#ifdef LOG_PERF_TIMINGS
static UINT64 g_timeTotal = 0;
UINT64 g_timeCpu = 0;
UINT64 g_timeVideo = 0; // part of timeCpu
UINT64 g_timeMB_Timer = 0; // part of timeCpu
UINT64 g_timeMB_NoTimer = 0;
UINT64 g_timeSpeaker = 0;
static UINT64 g_timeVideoRefresh = 0;
void LogPerfTimings(void)
{
if (g_timeTotal)
{
UINT64 cpu = g_timeCpu - g_timeVideo - g_timeMB_Timer;
UINT64 video = g_timeVideo + g_timeVideoRefresh;
UINT64 spkr = g_timeSpeaker;
UINT64 mb = g_timeMB_Timer + g_timeMB_NoTimer;
UINT64 audio = spkr + mb;
UINT64 other = g_timeTotal - g_timeCpu - g_timeSpeaker - g_timeMB_NoTimer - g_timeVideoRefresh;
LogOutput("Perf breakdown:\n");
LogOutput(". CPU %% = %2f.2\n", (double)cpu / (double)g_timeTotal * 100.0);
LogOutput(". Video %% = %2f.2\n", (double)video / (double)g_timeTotal * 100.0);
LogOutput("... NTSC %% = %2f.2\n", (double)g_timeVideo / (double)g_timeTotal * 100.0);
LogOutput("... refresh %% = %2f.2\n", (double)g_timeVideoRefresh / (double)g_timeTotal * 100.0);
LogOutput(". Audio %% = %2f.2\n", (double)audio / (double)g_timeTotal * 100.0);
LogOutput("... Speaker %% = %2f.2\n", (double)spkr / (double)g_timeTotal * 100.0);
LogOutput("... MB %% = %2f.2\n", (double)mb / (double)g_timeTotal * 100.0);
LogOutput(". Other %% = %2f.2\n", (double)other / (double)g_timeTotal * 100.0);
LogOutput(". TOTAL %% = %2f.2\n", (double)(cpu+video+audio+other) / (double)g_timeTotal * 100.0);
}
}
#endif
//===========================================================================
static DWORD dwLogKeyReadTickStart;
static bool bLogKeyReadDone = false;
void LogFileTimeUntilFirstKeyReadReset(void)
{
#ifdef LOG_PERF_TIMINGS
LogPerfTimings();
#endif
if (!g_fh)
return;
@ -234,6 +274,10 @@ static bool g_uModeStepping_LastGetKey_ScrollLock = false;
static void ContinueExecution(void)
{
#ifdef LOG_PERF_TIMINGS
PerfMarker* pPerfMarkerTotal = new PerfMarker(g_timeTotal);
#endif
_ASSERT(g_nAppMode == MODE_RUNNING || g_nAppMode == MODE_STEPPING);
const double fUsecPerSec = 1.e6;
@ -352,6 +396,9 @@ static void ContinueExecution(void)
const UINT dwClksPerFrame = NTSC_GetCyclesPerFrame();
if (g_dwCyclesThisFrame >= dwClksPerFrame && !VideoGetVblBarEx(g_dwCyclesThisFrame))
{
#ifdef LOG_PERF_TIMINGS
PerfMarker perfMarkerVideoRefresh(g_timeVideoRefresh);
#endif
g_dwCyclesThisFrame -= dwClksPerFrame;
if (g_bFullSpeed)
@ -360,6 +407,10 @@ static void ContinueExecution(void)
VideoRefreshScreen(); // Just copy the output of our Apple framebuffer to the system Back Buffer
}
#ifdef LOG_PERF_TIMINGS
delete pPerfMarkerTotal; // Explicitly call dtor *before* SysClk_WaitTimer()
#endif
if ((g_nAppMode == MODE_RUNNING && !g_bFullSpeed) || bModeStepping_WaitTimer)
{
SysClk_WaitTimer();

View file

@ -63,3 +63,27 @@ extern CSpeech g_Speech;
#endif
extern __interface IPropertySheet& sg_PropertySheet;
//
//#define LOG_PERF_TIMINGS
#ifdef LOG_PERF_TIMINGS
class PerfMarker
{
public:
PerfMarker(UINT64& globalCounter)
: counter(globalCounter)
{
QueryPerformanceCounter(&timeStart);
}
~PerfMarker()
{
QueryPerformanceCounter(&timeEnd);
counter += (UINT64)timeEnd.QuadPart - (UINT64)timeStart.QuadPart;
}
private:
UINT64& counter;
LARGE_INTEGER timeStart;
LARGE_INTEGER timeEnd;
};
#endif

View file

@ -98,6 +98,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#endif
#include "Video.h"
#include "NTSC.h"
#include "Log.h"
#include "z80emu.h"
#include "Z80VICE/z80.h"
@ -105,6 +106,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "YamlHelper.h"
#define LOG_IRQ_TAKEN_AND_RTI 0
// 6502 Accumulator Bit Flags
#define AF_SIGN 0x80
#define AF_OVERFLOW 0x40
@ -243,6 +246,10 @@ static __forceinline void DoIrqProfiling(DWORD uCycles)
if(regs.ps & AF_INTERRUPT)
return; // Still in Apple's ROM
#if LOG_IRQ_TAKEN_AND_RTI
LogOutput("ISR-end\n\n");
#endif
g_nCycleIrqEnd = g_nCumulativeCycles + uCycles;
g_nCycleIrqTime = (UINT) (g_nCycleIrqEnd - g_nCycleIrqStart);
@ -442,6 +449,9 @@ static __forceinline void IRQ(ULONG& uExecutedCycles, BOOL& flagc, BOOL& flagn,
regs.pc = * (WORD*) (mem+0xFFFE);
UINT uExtraCycles = 0; // Needed for CYC(a) macro
CYC(7)
#if defined(_DEBUG) && LOG_IRQ_TAKEN_AND_RTI
LogOutput("IRQ\n");
#endif
}
g_irqOnLastOpcodeCycle = false;
@ -554,6 +564,11 @@ ULONG CpuGetCyclesThisVideoFrame(const ULONG nExecutedCycles)
DWORD CpuExecute(const DWORD uCycles, const bool bVideoUpdate)
{
#ifdef LOG_PERF_TIMINGS
extern UINT64 g_timeCpu;
PerfMarker perfMarker(g_timeCpu);
#endif
g_nCyclesExecuted = 0;
#ifdef _DEBUG

View file

@ -50,7 +50,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#define ALLOW_INPUT_LOWERCASE 1
// See /docs/Debugger_Changelog.txt for full details
const int DEBUGGER_VERSION = MAKE_VERSION(2,9,0,15);
const int DEBUGGER_VERSION = MAKE_VERSION(2,9,1,0);
// Public _________________________________________________________________________________________
@ -484,13 +484,17 @@ bool _Bookmark_Del( const WORD nAddress )
{
// g_aBookmarks.at( iBookmark ) = NO_6502_TARGET;
g_aBookmarks[ iBookmark ].bSet = false;
g_nBookmarks--;
bDeleted = true;
}
}
return bDeleted;
}
bool Bookmark_Find( const WORD nAddress )
// Returns:
// 0 if address does not have a bookmark set
// N+1 if there is an existing bookmark that has this address
int Bookmark_Find( const WORD nAddress )
{
// Ugh, linear search
// int nSize = g_aBookmarks.size();
@ -500,10 +504,10 @@ bool Bookmark_Find( const WORD nAddress )
if (g_aBookmarks[ iBookmark ].nAddress == nAddress)
{
if (g_aBookmarks[ iBookmark ].bSet)
return true;
return iBookmark + 1;
}
}
return false;
return 0;
}
@ -534,6 +538,8 @@ void _Bookmark_Reset()
{
g_aBookmarks[ iBookmark ].bSet = false;
}
g_nBookmarks = 0;
}
@ -598,15 +604,26 @@ Update_t CmdBookmarkAdd (int nArgs )
ConsoleDisplayPush( sText );
return ConsoleUpdate();
}
if ((iBookmark < MAX_BOOKMARKS) && (g_nBookmarks < MAX_BOOKMARKS))
// 2.9.0.16 Fixed: Replacing an existing bookmark incorrectly increased the total bookmark count.
int nOldBookmark = Bookmark_Find( nAddress );
if (nOldBookmark)
{
g_aBookmarks[iBookmark].bSet = true;
g_aBookmarks[iBookmark].nAddress = nAddress;
bAdded = true;
g_nBookmarks++;
iBookmark++;
_Bookmark_Del( nAddress );
}
// 2.9.0.17 Fixed: If all bookmarks were used then setting a new one wouldn't update an existing one to the new address.
if (g_aBookmarks[ iBookmark ].bSet)
{
g_aBookmarks[ iBookmark ].nAddress = nAddress;
bAdded = true;
}
else
if (g_nBookmarks < MAX_BOOKMARKS)
{
bAdded = _Bookmark_Add( iBookmark, nAddress );
iBookmark++;
}
}
if (!bAdded)
@ -632,11 +649,7 @@ Update_t CmdBookmarkClear (int nArgs)
{
if (! _tcscmp(g_aArgs[nArgs].sArg, g_aParameters[ PARAM_WILDSTAR ].m_sName))
{
for (iBookmark = 0; iBookmark < MAX_BOOKMARKS; iBookmark++ )
{
if (g_aBookmarks[ iBookmark ].bSet)
g_aBookmarks[ iBookmark ].bSet = false;
}
_Bookmark_Reset();
break;
}
@ -6896,11 +6909,16 @@ Update_t CmdCyclesInfo(int nArgs)
else
{
if (strcmp(g_aArgs[1].sArg, "abs") == 0)
g_videoScannerDisplayInfo.isAbsCycle = true;
g_videoScannerDisplayInfo.cycleMode = VideoScannerDisplayInfo::abs;
else if (strcmp(g_aArgs[1].sArg, "rel") == 0)
g_videoScannerDisplayInfo.isAbsCycle = false;
g_videoScannerDisplayInfo.cycleMode = VideoScannerDisplayInfo::rel;
else if (strcmp(g_aArgs[1].sArg, "part") == 0)
g_videoScannerDisplayInfo.cycleMode = VideoScannerDisplayInfo::part;
else
return Help_Arg_1(CMD_CYCLES_INFO);
if (g_videoScannerDisplayInfo.cycleMode == VideoScannerDisplayInfo::part)
CmdCyclesReset(0);
}
TCHAR sText[CONSOLE_WIDTH];
@ -6910,6 +6928,12 @@ Update_t CmdCyclesInfo(int nArgs)
return UPDATE_ALL;
}
Update_t CmdCyclesReset(int /*nArgs*/)
{
g_videoScannerDisplayInfo.savedCumulativeCycles = g_nCumulativeCycles;
return UPDATE_ALL;
}
// View ___________________________________________________________________________________________
// See: CmdWindowViewOutput (int nArgs)

View file

@ -135,7 +135,7 @@
// Prototypes _______________________________________________________________
// Bookmarks
bool Bookmark_Find( const WORD nAddress );
int Bookmark_Find( const WORD nAddress );
// Breakpoints
int CheckBreakpointsIO ();

View file

@ -123,6 +123,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
{TEXT("PAGEDOWN4K" ) , CmdCursorPageDown4K , CMD_CURSOR_PAGE_DOWN_4K , "Scroll down 4096 bytes" }, // Ctrl
// Cycles info
{TEXT("CYCLES") , CmdCyclesInfo , CMD_CYCLES_INFO, "Cycles display configuration" },
{TEXT("RCC") , CmdCyclesReset , CMD_CYCLES_RESET, "Reset cycles counter" },
// Disassembler Data
{TEXT("Z") , CmdDisasmDataDefByte1 , CMD_DISASM_DATA , "Treat byte [range] as data" },
{TEXT("X") , CmdDisasmDataDefCode , CMD_DISASM_CODE , "Treat byte [range] as code" },

View file

@ -622,7 +622,7 @@ void DebuggerSetColorBG( COLORREF nRGB, bool bTransparent )
// @param glyph Specifies a native glyph from the 16x16 chars Apple Font Texture.
//===========================================================================
void PrintGlyph( const int x, const int y, const char glyph )
void PrintGlyph( const int x, const int y, const int glyph )
{
HDC hDstDC = GetDebuggerMemDC();
@ -1935,7 +1935,7 @@ WORD DrawDisassemblyLine ( int iLine, const WORD nBaseAddress )
bool bBreakpointEnable;
GetBreakpointInfo( nBaseAddress, bBreakpointActive, bBreakpointEnable );
bool bAddressAtPC = (nBaseAddress == regs.pc);
bool bAddressIsBookmark = Bookmark_Find( nBaseAddress );
int bAddressIsBookmark = Bookmark_Find( nBaseAddress );
DebugColors_e iBackground = BG_DISASM_1;
DebugColors_e iForeground = FG_DISASM_MNEMONIC; // FG_DISASM_TEXT;
@ -2005,16 +2005,8 @@ WORD DrawDisassemblyLine ( int iLine, const WORD nBaseAddress )
}
}
if (bAddressIsBookmark)
{
DebuggerSetColorBG( DebuggerGetColor( BG_DISASM_BOOKMARK ) );
DebuggerSetColorFG( DebuggerGetColor( FG_DISASM_BOOKMARK ) );
}
else
{
DebuggerSetColorBG( DebuggerGetColor( iBackground ) );
DebuggerSetColorFG( DebuggerGetColor( iForeground ) );
}
DebuggerSetColorBG( DebuggerGetColor( iBackground ) );
DebuggerSetColorFG( DebuggerGetColor( iForeground ) );
// Address
if (! bCursorLine)
@ -2030,18 +2022,31 @@ WORD DrawDisassemblyLine ( int iLine, const WORD nBaseAddress )
PrintTextCursorX( (LPCTSTR) line.sAddress, linerect );
}
if (bAddressIsBookmark)
{
DebuggerSetColorBG( DebuggerGetColor( iBackground ) );
DebuggerSetColorFG( DebuggerGetColor( iForeground ) );
}
// Address Seperator
if (! bCursorLine)
DebuggerSetColorFG( DebuggerGetColor( FG_DISASM_OPERATOR ) );
if (g_bConfigDisasmAddressColon)
PrintTextCursorX( ":", linerect );
{
if (bAddressIsBookmark)
{
DebuggerSetColorBG( DebuggerGetColor( BG_DISASM_BOOKMARK ) );
DebuggerSetColorFG( DebuggerGetColor( FG_DISASM_BOOKMARK ) );
// Can't use PrintTextCursorX() as that clamps chars > 0x7F to Mouse Text
// char bookmark_text[2] = { 0x7F + bAddressIsBookmark, 0 };
// PrintTextCursorX( bookmark_text, linerect );
FillRect( GetDebuggerMemDC(), &linerect, g_hConsoleBrushBG );
PrintGlyph( linerect.left, linerect.top, 0x7F + bAddressIsBookmark ); // Glyphs 0x80 .. 0x89 = Unicode U+24EA, U+2460 .. U+2468
linerect.left += g_aFontConfig[ FONT_DISASM_DEFAULT ]._nFontWidthAvg;
DebuggerSetColorBG( DebuggerGetColor( iBackground ) );
DebuggerSetColorFG( DebuggerGetColor( iForeground ) );
}
else
PrintTextCursorX( ":", linerect );
}
else
PrintTextCursorX( " ", linerect ); // bugfix, not showing "addr:" doesn't alternate color lines
@ -3784,8 +3789,15 @@ void DrawVideoScannerInfo (int line)
PrintText("cycles:", rect);
rect.left += nameWidth * nFontWidth;
UINT cycles = 0;
if (g_videoScannerDisplayInfo.cycleMode == VideoScannerDisplayInfo::abs)
cycles = (UINT)g_nCumulativeCycles;
else if (g_videoScannerDisplayInfo.cycleMode == VideoScannerDisplayInfo::rel)
cycles = g_videoScannerDisplayInfo.cycleDelta;
else // "part"
cycles = (UINT)g_videoScannerDisplayInfo.lastCumulativeCycles - (UINT)g_videoScannerDisplayInfo.savedCumulativeCycles;
char sValue[10];
const UINT cycles = g_videoScannerDisplayInfo.isAbsCycle ? (UINT)g_nCumulativeCycles : g_videoScannerDisplayInfo.cycleDelta;
sprintf_s(sValue, sizeof(sValue), "%08X", cycles);
PrintText(sValue, rect);
}

View file

@ -102,15 +102,17 @@
class VideoScannerDisplayInfo
{
public:
VideoScannerDisplayInfo(void) : isDecimal(false), isHorzReal(false), isAbsCycle(false),
VideoScannerDisplayInfo(void) : isDecimal(false), isHorzReal(false), cycleMode(rel),
lastCumulativeCycles(0), cycleDelta(0) {}
void Reset(void) { lastCumulativeCycles = g_nCumulativeCycles; cycleDelta = 0; }
void Reset(void) { lastCumulativeCycles = savedCumulativeCycles = g_nCumulativeCycles; cycleDelta = 0; }
bool isDecimal;
bool isHorzReal;
bool isAbsCycle;
enum CYCLE_MODE {abs=0, rel, part};
CYCLE_MODE cycleMode;
unsigned __int64 lastCumulativeCycles;
unsigned __int64 savedCumulativeCycles;
UINT cycleDelta;
};

View file

@ -1408,11 +1408,17 @@ Update_t CmdHelpSpecific (int nArgs)
break;
// Cycles
case CMD_CYCLES_INFO:
ConsoleColorizePrint(sText, " Usage: <abs|rel>");
ConsoleColorizePrint(sText, " Usage: <abs|rel|part>");
ConsoleBufferPush(" Where:");
ConsoleBufferPush(" <abs|rel> changes cycle output to absolute/relative");
ConsoleBufferPush(" abs = absolute number of cycles since power-on");
ConsoleBufferPush(" rel = number of cycles since last step or breakpoint");
ConsoleBufferPush(" part= number of cycles relative to current instruction");
break;
case CMD_CYCLES_RESET:
ConsoleBufferPush(" Use in conjunctioned with 'cycles part' to reset to current instruction");
break;
// Video-Scanner
case CMD_VIDEO_SCANNER_INFO:
ConsoleColorizePrint(sText, " Usage: <dec|hex|real|apple>");
ConsoleBufferPush(" Where:");

View file

@ -377,6 +377,7 @@
, CMD_CURSOR_PAGE_DOWN_4K // Down to nearest 4K boundary
// Cycles info
, CMD_CYCLES_INFO
, CMD_CYCLES_RESET
// Disassembler Data
, CMD_DISASM_DATA
, CMD_DISASM_CODE
@ -666,7 +667,8 @@
Update_t CmdCursorPageUp4K (int nArgs);
// Cycles info
Update_t CmdCyclesInfo (int nArgs);
Update_t CmdCyclesInfo (int nArgs);
Update_t CmdCyclesReset (int nArgs);
// Disk
Update_t CmdDisk (int nArgs);

View file

@ -650,6 +650,10 @@ BYTE __stdcall JoyReadPosition(WORD programcounter, WORD address, BYTE, BYTE, UL
BOOL nPdlCntrActive = g_nCumulativeCycles <= (g_nJoyCntrResetCycle + (unsigned __int64) ((double)nPdlPos * PDL_CNTR_INTERVAL));
// If no joystick connected, then this is always active (GH#778)
if (joyinfo[joytype[nJoyNum]] == DEVICE_NONE)
nPdlCntrActive = TRUE;
return MemReadFloatingBus(nPdlCntrActive, nExecutedCycles);
}

View file

@ -92,6 +92,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "SSI263Phonemes.h"
#define LOG_SSI263 0
#define LOG_SSI263B 0 // Alternate SSI263 logging (use in conjunction with CPU.cpp's LOG_IRQ_TAKEN_AND_RTI)
#define SY6522_DEVICE_A 0
@ -187,7 +188,8 @@ static bool g_bMBAvailable = false;
static SS_CARDTYPE g_SoundcardType = CT_Empty; // Use CT_Empty to mean: no soundcard
static bool g_bPhasorEnable = false;
static BYTE g_nPhasorMode = 0; // 0=Mockingboard emulation, 1=Phasor native
enum PHASOR_MODE {PH_Mockingboard=0, PH_UNDEF1, PH_UNDEF2, PH_UNDEF3, PH_UNDEF4, PH_Phasor/*=5*/, PH_UNDEF6, PH_EchoPlus/*=7*/};
static PHASOR_MODE g_phasorMode = PH_Mockingboard;
static UINT g_PhasorClockScaleFactor = 1; // for save-state only
//-------------------------------------
@ -363,8 +365,7 @@ static void UpdateIFR(SY6522_AY8910* pMB, BYTE clr_ifr, BYTE set_ifr=0)
// NB. Mockingboard generates IRQ on both 6522s:
// . SSI263's IRQ (A/!R) is routed via the 2nd 6522 (at $Cx80) and must generate a 6502 IRQ (not NMI)
// . SC-01's IRQ (A/!R) is also routed via a (2nd?) 6522
// Phasor's SSI263 appears to be wired directly to the 6502's IRQ (ie. not via a 6522)
// . I assume Phasor's 6522s just generate 6502 IRQs (not NMIs)
// Phasor's SSI263 IRQ (A/!R) line is *also* wired directly to the 6502's IRQ (as well as the 6522's CA1)
if (bIRQ)
CpuIrqAssert(IS_6522);
@ -394,7 +395,7 @@ static void SY6522_Write(BYTE nDevice, BYTE nReg, BYTE nValue)
if(g_bPhasorEnable)
{
int nAY_CS = (g_nPhasorMode & 1) ? (~(nValue >> 3) & 3) : 1;
int nAY_CS = (g_phasorMode == PH_Phasor) ? (~(nValue >> 3) & 3) : 1;
if(nAY_CS & 1)
AY8910_Write(nDevice, nReg, nValue, 0);
@ -590,31 +591,68 @@ const BYTE CONTROL_MASK = 0x80;
const BYTE ARTICULATION_MASK = 0x70;
const BYTE AMPLITUDE_MASK = 0x0F;
static BYTE SSI263_Read(BYTE nDevice, BYTE nReg)
#if LOG_SSI263B
static int ssiRegs[5]={-1,-1,-1,-1,-1};
void SSI_Output(void)
{
LogOutput("SSI: ");
for (int i=0; i<=4; i++)
{
char r[3]="--";
if (ssiRegs[i]>=0) sprintf(r,"%02X",ssiRegs[i]);
LogOutput("%s ", r);
ssiRegs[i] = -1;
}
LogOutput("\n");
}
#endif
static BYTE SSI263_Read(BYTE nDevice, ULONG nExecutedCycles)
{
SY6522_AY8910* pMB = &g_MB[nDevice];
// Regardless of register, just return inverted A/!R in bit7
// . A/!R is low for IRQ
return pMB->SpeechChip.CurrentMode << 7;
return MemReadFloatingBus(pMB->SpeechChip.CurrentMode & 1, nExecutedCycles);
}
static void SSI263_Write(BYTE nDevice, BYTE nReg, BYTE nValue)
{
SY6522_AY8910* pMB = &g_MB[nDevice];
#if LOG_SSI263B
_ASSERT(nReg < 5);
if (nReg>4) nReg=4;
if (ssiRegs[nReg]>=0) SSI_Output(); // overwriting a reg
ssiRegs[nReg] = nValue;
#endif
switch(nReg)
{
case SSI_DURPHON:
#if LOG_SSI263
if(g_fh) fprintf(g_fh, "DUR = 0x%02X, PHON = 0x%02X\n\n", nValue>>6, nValue&PHONEME_MASK);
LogOutput("DUR = %d, PHON = 0x%02X\n", nValue>>6, nValue&PHONEME_MASK);
#endif
#if LOG_SSI263B
SSI_Output();
#endif
// Datasheet is not clear, but a write to DURPHON must clear the IRQ
if (pMB->sy6522.PCR == 0x0C)
UpdateIFR(pMB, IxR_PERIPHERAL);
else // Phasor's SSI263.IRQ line appears to be wired directly to IRQ (Bypassing the 6522)
// Notes:
// . Phasor's text-to-speech playback has no CTL H->L
// - ISR just writes CTL=0 (and new ART+AMP values), and writes DUR=x (and new PHON)
// - since no CTL H->L, then DUR value doesn't take affect (so continue using previous)
// - so the write to DURPHON must clear the IRQ
// . Does a write of CTL=0 clear IRQ? (ie. CTL 0->0)
// . Does a write of CTL=1 clear IRQ? (ie. CTL 0->1)
// - SSI263 datasheet says: "Setting the Control bit (CTL) to a logic one puts the device into Power Down mode..."
// . Does phoneme output only happen when CTL=0? (Otherwise device is in PD mode)
// SSI263 datasheet is not clear, but a write to DURPHON must clear the IRQ.
// NB. For Mockingboard, A/!R is ack'ed by 6522's PCR handshake.
if (g_bPhasorEnable && g_phasorMode == PH_Phasor)
CpuIrqDeassert(IS_SPEECH);
pMB->SpeechChip.CurrentMode &= ~1; // Clear SSI263's D7 pin
@ -623,7 +661,6 @@ static void SSI263_Write(BYTE nDevice, BYTE nReg, BYTE nValue)
g_nSSI263Device = nDevice;
// Phoneme output not dependent on CONTROL bit
SSI263_Play(nValue & PHONEME_MASK);
break;
case SSI_INFLECT:
@ -632,6 +669,7 @@ static void SSI263_Write(BYTE nDevice, BYTE nReg, BYTE nValue)
#endif
pMB->SpeechChip.Inflection = nValue;
break;
case SSI_RATEINF:
#if LOG_SSI263
if(g_fh) fprintf(g_fh, "RATE = 0x%02X, INF = 0x%02X\n", nValue>>4, nValue&0x0F);
@ -641,19 +679,44 @@ static void SSI263_Write(BYTE nDevice, BYTE nReg, BYTE nValue)
case SSI_CTTRAMP:
#if LOG_SSI263
if(g_fh) fprintf(g_fh, "CTRL = %d, ART = 0x%02X, AMP=0x%02X\n", nValue>>7, (nValue&ARTICULATION_MASK)>>4, nValue&AMPLITUDE_MASK);
//
{
bool H2L = (pMB->SpeechChip.CtrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK);
char newMode[20];
sprintf_s(newMode, sizeof(newMode), "(new mode=%d)", pMB->SpeechChip.DurationPhoneme>>6);
LogOutput("CTRL = %d->%d, ART = 0x%02X, AMP=0x%02X %s\n", pMB->SpeechChip.CtrlArtAmp>>7, nValue>>7, (nValue&ARTICULATION_MASK)>>4, nValue&AMPLITUDE_MASK, H2L?newMode:"");
}
#endif
#if LOG_SSI263B
if ( ((pMB->SpeechChip.CtrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK)) || ((nValue&0xF) == 0x0) ) // H->L or amp=0
SSI_Output();
#endif
if((pMB->SpeechChip.CtrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK)) // H->L
{
pMB->SpeechChip.CurrentMode = pMB->SpeechChip.DurationPhoneme & DURATION_MODE_MASK;
if (pMB->SpeechChip.CurrentMode == MODE_IRQ_DISABLED)
{
// "Disables A/!R output only; does not change previous A/!R response" (SSI263 datasheet)
// CpuIrqDeassert(IS_SPEECH);
}
}
pMB->SpeechChip.CtrlArtAmp = nValue;
// "Setting the Control bit (CTL) to a logic one puts the device into Power Down mode..." (SSI263 datasheet)
if (pMB->SpeechChip.CtrlArtAmp & CONTROL_MASK)
{
// CpuIrqDeassert(IS_SPEECH);
// pMB->SpeechChip.CurrentMode &= ~1; // Clear SSI263's D7 pin
}
break;
case SSI_FILFREQ:
case SSI_FILFREQ: // RegAddr.b2=1 (b1 & b0 are: don't care)
default:
#if LOG_SSI263
if(g_fh) fprintf(g_fh, "FFREQ = 0x%02X\n", nValue);
#endif
pMB->SpeechChip.FilterFreq = nValue;
break;
default:
break;
}
}
@ -751,7 +814,7 @@ static UINT64 g_uLastMBUpdateCycle = 0;
// Called by:
// . MB_UpdateCycles() - when g_nMBTimerDevice == {0,1,2,3}
// . MB_PeriodicUpdate() - when g_nMBTimerDevice == kTIMERDEVICE_INVALID
static void MB_Update(void)
static void MB_UpdateInt(void)
{
if (!MockingboardVoice.bActive)
return;
@ -958,8 +1021,59 @@ static void MB_Update(void)
#endif
}
static void MB_Update(void)
{
#ifdef LOG_PERF_TIMINGS
extern UINT64 g_timeMB_NoTimer;
extern UINT64 g_timeMB_Timer;
PerfMarker perfMarker(g_nMBTimerDevice == kTIMERDEVICE_INVALID ? g_timeMB_NoTimer : g_timeMB_Timer);
#endif
MB_UpdateInt();
}
//-----------------------------------------------------------------------------
// Called by SSI263Thread(), MB_LoadSnapshot & Phasor_LoadSnapshot
// Pre: g_bVotraxPhoneme, g_bPhasorEnable, g_phasorMode
static void SetSpeechIRQ(SY6522_AY8910* pMB)
{
if (!g_bVotraxPhoneme)
{
// Always set SSI263's D7 pin regardless of SSI263 mode (DR1:0), including MODE_IRQ_DISABLED
pMB->SpeechChip.CurrentMode |= 1; // Set SSI263's D7 pin
if ((pMB->SpeechChip.CurrentMode & DURATION_MODE_MASK) != MODE_IRQ_DISABLED)
{
if (!g_bPhasorEnable || (g_bPhasorEnable && g_phasorMode == PH_Mockingboard))
{
if ((pMB->sy6522.PCR & 1) == 0) // CA1 Latch/Input = 0 (Negative active edge)
UpdateIFR(pMB, 0, IxR_PERIPHERAL);
if (pMB->sy6522.PCR == 0x0C) // CA2 Control = b#110 (Low output)
pMB->SpeechChip.CurrentMode &= ~1; // Clear SSI263's D7 pin (cleared by 6522's PCR CA1/CA2 handshake)
// NB. Don't set CTL=1, as Mockingboard(SMS) speech doesn't work (it sets MODE_IRQ_DISABLED mode during ISR)
//pMB->SpeechChip.CtrlArtAmp |= CONTROL_MASK; // 6522's CA2 sets Power Down mode (pin 18), which sets Control bit
}
else if (g_bPhasorEnable && g_phasorMode == PH_Phasor) // Phasor's SSI263 IRQ (A/!R) line is *also* wired directly to the 6502's IRQ (as well as the 6522's CA1)
{
CpuIrqAssert(IS_SPEECH);
}
}
}
//
if (g_bVotraxPhoneme && pMB->sy6522.PCR == 0xB0)
{
// !A/R: Time-out of old phoneme (signal goes from low to high)
UpdateIFR(pMB, 0, IxR_VOTRAX);
g_bVotraxPhoneme = false;
}
}
static DWORD WINAPI SSI263Thread(LPVOID lpParameter)
{
while(1)
@ -990,32 +1104,15 @@ static DWORD WINAPI SSI263Thread(LPVOID lpParameter)
//if(g_fh) fprintf(g_fh, "IRQ: Phoneme complete (0x%02X)\n\n", g_nCurrentActivePhoneme);
#endif
if (g_nCurrentActivePhoneme < 0)
continue; // On CTRL+RESET or power-cycle (during phoneme playback): ResetState() is called, which set g_nCurrentActivePhoneme=-1
SSI263Voice[g_nCurrentActivePhoneme].bActive = false;
g_nCurrentActivePhoneme = -1;
// Phoneme complete, so generate IRQ if necessary
SY6522_AY8910* pMB = &g_MB[g_nSSI263Device];
if (!g_bVotraxPhoneme && pMB->SpeechChip.CurrentMode != MODE_IRQ_DISABLED)
{
pMB->SpeechChip.CurrentMode |= 1; // Set SSI263's D7 pin
if (pMB->sy6522.PCR == 0x0C)
UpdateIFR(pMB, 0, IxR_PERIPHERAL);
else // Phasor's SSI263.IRQ line appears to be wired directly to IRQ (Bypassing the 6522)
CpuIrqAssert(IS_SPEECH);
}
//
if (g_bVotraxPhoneme && pMB->sy6522.PCR == 0xB0)
{
// !A/R: Time-out of old phoneme (signal goes from low to high)
UpdateIFR(pMB, 0, IxR_VOTRAX);
g_bVotraxPhoneme = false;
}
SetSpeechIRQ(pMB);
}
return 0;
@ -1470,7 +1567,7 @@ static void ResetState()
g_bMB_RegAccessedFlag = false;
g_bMB_Active = false;
g_nPhasorMode = 0;
g_phasorMode = PH_Mockingboard;
g_PhasorClockScaleFactor = 1;
g_uLastMBUpdateCycle = 0;
@ -1499,6 +1596,8 @@ void MB_Reset() // CTRL+RESET or power-cycle
//-----------------------------------------------------------------------------
// Echo+ mode - Phasor's 2nd 6522 is mapped to every 16-byte offset in $Cnxx (Echo+ has a single 6522 controlling two AY-3-8913's)
static BYTE __stdcall MB_Read(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles)
{
if (g_bFullSpeed)
@ -1526,11 +1625,13 @@ static BYTE __stdcall MB_Read(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULO
if(nMB != 0) // Slot4 only
return MemReadFloatingBus(nExecutedCycles);
int CS;
if(g_nPhasorMode & 1)
CS = ( ( nAddr & 0x80 ) >> 6 ) | ( ( nAddr & 0x10 ) >> 4 ); // 0, 1, 2 or 3
else // Mockingboard Mode
int CS = 0;
if (g_phasorMode == PH_Mockingboard)
CS = ( ( nAddr & 0x80 ) >> 7 ) + 1; // 1 or 2
else if (g_phasorMode == PH_Phasor)
CS = ( ( nAddr & 0x80 ) >> 6 ) | ( ( nAddr & 0x10 ) >> 4 ); // 0, 1, 2 or 3
else if (g_phasorMode == PH_EchoPlus)
CS = 2;
BYTE nRes = 0;
@ -1542,23 +1643,21 @@ static BYTE __stdcall MB_Read(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULO
bool bAccessedDevice = (CS & 3) ? true : false;
if((nOffset >= SSI263_Offset) && (nOffset <= (SSI263_Offset+0x05)))
if ((g_phasorMode == PH_Phasor) && ((nAddr & 0xD0) == 0x40)) // $Cn4x and $Cn6x (Mockingboard mode: SSI263.bit7 not readable)
{
nRes |= SSI263_Read(nMB, nAddr&0xf);
_ASSERT(!bAccessedDevice);
nRes = SSI263_Read(nMB*2+1, nExecutedCycles); // SSI263 only drives bit7
bAccessedDevice = true;
}
return bAccessedDevice ? nRes : MemReadFloatingBus(nExecutedCycles);
}
if(nOffset <= (SY6522A_Offset+0x0F))
// NB. Mockingboard: SSI263.bit7 not readable (TODO: check this with real h/w)
if (nOffset < SY6522B_Offset)
return SY6522_Read(nMB*NUM_DEVS_PER_MB + SY6522_DEVICE_A, nAddr&0xf);
else if((nOffset >= SY6522B_Offset) && (nOffset <= (SY6522B_Offset+0x0F)))
return SY6522_Read(nMB*NUM_DEVS_PER_MB + SY6522_DEVICE_B, nAddr&0xf);
else if((nOffset >= SSI263_Offset) && (nOffset <= (SSI263_Offset+0x05)))
return SSI263_Read(nMB, nAddr&0xf);
else
return MemReadFloatingBus(nExecutedCycles);
return SY6522_Read(nMB*NUM_DEVS_PER_MB + SY6522_DEVICE_B, nAddr&0xf);
}
//-----------------------------------------------------------------------------
@ -1592,10 +1691,12 @@ static BYTE __stdcall MB_Write(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, UL
int CS;
if(g_nPhasorMode & 1)
CS = ( ( nAddr & 0x80 ) >> 6 ) | ( ( nAddr & 0x10 ) >> 4 ); // 0, 1, 2 or 3
else // Mockingboard Mode
if (g_phasorMode == PH_Mockingboard)
CS = ( ( nAddr & 0x80 ) >> 7 ) + 1; // 1 or 2
else if (g_phasorMode == PH_Phasor)
CS = ( ( nAddr & 0x80 ) >> 6 ) | ( ( nAddr & 0x10 ) >> 4 ); // 0, 1, 2 or 3
else if (g_phasorMode == PH_EchoPlus)
CS = 2;
if(CS & 1)
SY6522_Write(nMB*NUM_DEVS_PER_MB + SY6522_DEVICE_A, nAddr&0xf, nValue);
@ -1603,33 +1704,64 @@ static BYTE __stdcall MB_Write(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, UL
if(CS & 2)
SY6522_Write(nMB*NUM_DEVS_PER_MB + SY6522_DEVICE_B, nAddr&0xf, nValue);
if((nOffset >= SSI263_Offset) && (nOffset <= (SSI263_Offset+0x05)))
SSI263_Write(nMB*2+1, nAddr&0xf, nValue); // Second 6522 is used for speech chip
int CS_SSI263 = (g_phasorMode == PH_Mockingboard) ? (nAddr & 0xE0) == 0x40 // Mockingboard: $Cn4x
: (g_phasorMode == PH_Phasor) ? (nAddr & 0xC0) == 0x40 // Phasor: $Cn4x and $Cn6x
: 0; // Echo+
if (CS_SSI263)
{
// NB. Mockingboard mode: writes to $Cn4x/SSI263 also get written to 1st 6522 (have confirmed on real Phasor h/w)
_ASSERT( (g_phasorMode == PH_Mockingboard && (CS==0 || CS==1)) || (g_phasorMode == PH_Phasor && (CS==0)) );
SSI263_Write(nMB*2+1, nAddr&0x7, nValue); // Second 6522 is used for speech chip
}
return 0;
}
if(nOffset <= (SY6522A_Offset+0x0F))
if (nOffset < SY6522B_Offset)
SY6522_Write(nMB*NUM_DEVS_PER_MB + SY6522_DEVICE_A, nAddr&0xf, nValue);
else if((nOffset >= SY6522B_Offset) && (nOffset <= (SY6522B_Offset+0x0F)))
else
SY6522_Write(nMB*NUM_DEVS_PER_MB + SY6522_DEVICE_B, nAddr&0xf, nValue);
else if((nOffset >= SSI263_Offset) && (nOffset <= (SSI263_Offset+0x05)))
SSI263_Write(nMB*2+1, nAddr&0xf, nValue); // Second 6522 is used for speech chip
if ((nOffset >= SSI263_Offset) && (nOffset <= (SSI263_Offset+0x07)))
SSI263_Write(nMB*2+1, nAddr&0x7, nValue); // Second 6522 is used for speech chip -- TODO confirm with real MB h/w that writes go to 1st 6522
return 0;
}
//-----------------------------------------------------------------------------
// Phasor's DEVICE SELECT' logic:
// . if addr.[b3]==1, then clear the card's mode bits b2:b0
// . if any of addr.[b2:b0] are a logic 1, then set these bits in the card's mode
//
// Example DEVICE SELECT' accesses for Phasor in slot-4: (from empirical observations on real Phasor h/w)
// 1)
// . RESET -> Mockingboard mode (b#000)
// . $C0C5 -> Phasor mode (b#101)
// 2)
// . RESET -> Mockingboard mode (b#000)
// . $C0C1, then $C0C4 (or $C0C4, then $C0C1) -> Phasor mode (b#101)
// . $C0C2 -> Echo+ mode (b#111)
// . $C0C5 -> remaing in Echo+ mode (b#111)
// So $C0C5 seemingly results in 2 different modes.
//
static BYTE __stdcall PhasorIO(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, ULONG nExecutedCycles)
{
if(!g_bPhasorEnable)
if (!g_bPhasorEnable)
return MemReadFloatingBus(nExecutedCycles);
if(g_nPhasorMode < 2)
g_nPhasorMode = nAddr & 1;
UINT bits = (UINT) g_phasorMode;
if (nAddr & 8)
bits = 0;
bits |= (nAddr & 7);
g_phasorMode = (PHASOR_MODE) bits;
g_PhasorClockScaleFactor = (nAddr & 4) ? 2 : 1;
if (g_phasorMode == PH_Mockingboard || g_phasorMode == PH_EchoPlus)
g_PhasorClockScaleFactor = 1;
else if (g_phasorMode == PH_Phasor)
g_PhasorClockScaleFactor = 2;
AY8910_InitClock((int)(Get6502BaseClock() * g_PhasorClockScaleFactor));
@ -1972,7 +2104,10 @@ void MB_GetSnapshot_v1(SS_CARD_MOCKINGBOARD_v1* const pSS, const DWORD dwSlot)
// 3: Added: Unit state - GH#320
// 4: Added: 6522 timerIrqDelay - GH#652
// 5: Added: Unit state-B (Phasor only) - GH#659
const UINT kUNIT_VERSION = 5;
// 6: Changed SS_YAML_KEY_PHASOR_MODE from (0,1) to (0,5,7)
// Added SS_YAML_KEY_VOTRAX_PHONEME
// Removed: redundant SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR
const UINT kUNIT_VERSION = 6;
const UINT NUM_MB_UNITS = 2;
const UINT NUM_PHASOR_UNITS = 2;
@ -2011,9 +2146,11 @@ const UINT NUM_PHASOR_UNITS = 2;
#define SS_YAML_KEY_SY6522_TIMER2_IRQ_DELAY "Timer2 IRQ Delay"
#define SS_YAML_KEY_PHASOR_UNIT "Unit"
#define SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR "Clock Scale Factor"
#define SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR "Clock Scale Factor" // Redundant from v6
#define SS_YAML_KEY_PHASOR_MODE "Mode"
#define SS_YAML_KEY_VOTRAX_PHONEME "Votrax Phoneme"
std::string MB_GetSnapshotCardName(void)
{
static const std::string name("Mockingboard C");
@ -2070,6 +2207,8 @@ void MB_SaveSnapshot(YamlSaveHelper& yamlSaveHelper, const UINT uSlot)
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
yamlSaveHelper.SaveBool(SS_YAML_KEY_VOTRAX_PHONEME, g_bVotraxPhoneme);
for(UINT i=0; i<NUM_MB_UNITS; i++)
{
YamlSaveHelper::Label unit(yamlSaveHelper, "%s%d:\n", SS_YAML_KEY_MB_UNIT, i);
@ -2145,6 +2284,8 @@ bool MB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version)
if (version < 1 || version > kUNIT_VERSION)
throw std::string("Card: wrong version");
g_bVotraxPhoneme = (version >= 6) ? yamlLoadHelper.LoadBool(SS_YAML_KEY_VOTRAX_PHONEME) : false;
AY8910UpdateSetCycles();
const UINT nMbCardNum = slot - SLOT4;
@ -2196,19 +2337,15 @@ bool MB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version)
StartTimer1(pMB); // Attempt to start timer
}
// Crude - currently only support a single speech chip
// FIX THIS:
// . Speech chip could be Votrax instead
// . Is this IRQ compatible with Phasor?
if(pMB->SpeechChip.DurationPhoneme)
// FIXME: currently only support a single speech chip
// NB. g_bVotraxPhoneme is never true, as the phoneme playback completes in SSI263Thread() before this point in the save-state.
// NB. SpeechChip.DurationPhoneme will mostly be non-zero during speech playback, as this is the SSI263 register, not whether the phonene is active.
// FIXME: So possible race-condition between saving-state & SSI263Thread()
if (pMB->SpeechChip.DurationPhoneme || g_bVotraxPhoneme)
{
g_nSSI263Device = nDeviceNum;
if((pMB->SpeechChip.CurrentMode != MODE_IRQ_DISABLED) && (pMB->sy6522.PCR == 0x0C) && (pMB->sy6522.IER & IxR_PERIPHERAL))
{
UpdateIFR(pMB, 0, IxR_PERIPHERAL);
pMB->SpeechChip.CurrentMode |= 1; // Set SSI263's D7 pin
}
g_bPhasorEnable = false;
SetSpeechIRQ(pMB);
}
nDeviceNum++;
@ -2234,8 +2371,8 @@ void Phasor_SaveSnapshot(YamlSaveHelper& yamlSaveHelper, const UINT uSlot)
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
yamlSaveHelper.SaveUint(SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR, g_PhasorClockScaleFactor);
yamlSaveHelper.SaveUint(SS_YAML_KEY_PHASOR_MODE, g_nPhasorMode);
yamlSaveHelper.SaveUint(SS_YAML_KEY_PHASOR_MODE, g_phasorMode);
yamlSaveHelper.SaveBool(SS_YAML_KEY_VOTRAX_PHONEME, g_bVotraxPhoneme);
for(UINT i=0; i<NUM_PHASOR_UNITS; i++)
{
@ -2268,8 +2405,21 @@ bool Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version
if (version < 1 || version > kUNIT_VERSION)
throw std::string("Card: wrong version");
g_PhasorClockScaleFactor = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR);
g_nPhasorMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASOR_MODE);
if (version < 6)
yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR); // Consume redundant data
UINT phasorMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASOR_MODE);
if (version < 6)
{
if (phasorMode == 0)
phasorMode = PH_Mockingboard;
else
phasorMode = PH_Phasor;
}
g_phasorMode = (PHASOR_MODE) phasorMode;
g_PhasorClockScaleFactor = (g_phasorMode == PH_Phasor) ? 2 : 1;
g_bVotraxPhoneme = (version >= 6) ? yamlLoadHelper.LoadBool(SS_YAML_KEY_VOTRAX_PHONEME) : false;
AY8910UpdateSetCycles();
@ -2324,19 +2474,12 @@ bool Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version
StartTimer1(pMB); // Attempt to start timer
}
// Crude - currently only support a single speech chip
// FIX THIS:
// . Speech chip could be Votrax instead
// . Is this IRQ compatible with Phasor?
if(pMB->SpeechChip.DurationPhoneme)
// FIXME: currently only support a single speech chip
if (pMB->SpeechChip.DurationPhoneme || g_bVotraxPhoneme)
{
g_nSSI263Device = nDeviceNum;
if((pMB->SpeechChip.CurrentMode != MODE_IRQ_DISABLED) && (pMB->sy6522.PCR == 0x0C) && (pMB->sy6522.IER & IxR_PERIPHERAL))
{
UpdateIFR(pMB, 0, IxR_PERIPHERAL);
pMB->SpeechChip.CurrentMode |= 1; // Set SSI263's D7 pin
}
g_bPhasorEnable = true;
SetSpeechIRQ(pMB);
}
nDeviceNum += 2;

View file

@ -2091,6 +2091,11 @@ static void VideoUpdateCycles( int cyclesLeftToUpdate )
//===========================================================================
void NTSC_VideoUpdateCycles( UINT cycles6502 )
{
#ifdef LOG_PERF_TIMINGS
extern UINT64 g_timeVideo;
PerfMarker perfMarker(g_timeVideo);
#endif
_ASSERT(cycles6502 && cycles6502 < g_videoScanner6502Cycles); // Use NTSC_VideoRedrawWholeScreen() instead
if (g_bDelayVideoMode)

View file

@ -399,7 +399,9 @@ BYTE __stdcall SpkrToggle (WORD, WORD, BYTE, BYTE, ULONG nExecutedCycles)
if (g_bQuieterSpeaker) // quieten the speaker if 8 bit DAC in use
speakerDriveLevel /= 4; // NB. Don't shift -ve number right: undefined behaviour (MSDN says: implementation-dependent)
ResetDCFilter();
// When full-speed: Don't ResetDCFilter(), otherwise get occasional clicks when speaker toggled
if (!g_bFullSpeed)
ResetDCFilter();
if (g_nSpeakerData == speakerDriveLevel)
g_nSpeakerData = ~speakerDriveLevel;
@ -415,6 +417,11 @@ BYTE __stdcall SpkrToggle (WORD, WORD, BYTE, BYTE, ULONG nExecutedCycles)
// Called by ContinueExecution()
void SpkrUpdate (DWORD totalcycles)
{
#ifdef LOG_PERF_TIMINGS
extern UINT64 g_timeSpeaker;
PerfMarker perfMarker(g_timeSpeaker);
#endif
if(!g_bSpkrToggleFlag)
{
if(!g_nSpkrQuietCycleCount)