Compare commits

...

8 Commits

Author SHA1 Message Date
TomCh
a88040c3ef
SSI263 - support for core functionality (#175, PR #1301)
. A phoneme will continue playing back infinitely; unless the phoneme is changed or CTL=1.
  . Reset doesn't affect SSI263/SC01 (so phonemes continue to play).
. CTL=1 sets "PD" (Power Down / "standby") mode, also set at power-on.
. CTL=0 brings device out of "PD" mode, the mode will be set to DR1,DR0 and the phoneme P5-P0 will play.
. Setting mode to DR1:0 = %00 just disables A/!R (ie. disables interrupts), but otherwise retains the previous DR1:0 mode.
. RESET is not connected to !PD/!RST pin 18.
 . Support edge-case where RESET can enable ints & assert IRQ.
. Power-on: PD=1 (so D7=0), reg4 (Filter Freq)=0xFF.
. Support SSI263 IRQ and D7 on a Phasor mode change (including Echo+).
. $Csxx I/O mapping (same for Mockingboard and Phasor mode).

Other:
. SSI263 save-state: support SC01 as a sub-unit of the card.
. 6522: Fix reg $F (ORA w/HS) to be identical to reg $1 (ORA).
2024-06-07 21:10:33 +01:00
Andrea
ae7e5a63a9
More portable headers. (PR #1310) 2024-06-02 12:54:40 +01:00
TomCh
06a646f751
Use actual video ROMs (#1308, PR #1311)
* Apple II+ - 7341-0036 - Character Generator Rev7+
* Apple IIe Enhanced - 342-0265-A
* Remove CHARSET4.BMP
2024-06-02 12:47:22 +01:00
xotmatrix
c87a2c90da
Fix for corrupt 't' glyph (#1309) 2024-06-01 18:20:02 +01:00
Andrea
c0bfb0b0fe
linux munmap: fix comment and simplify memory free. (PR #1293) 2024-04-26 21:53:09 +01:00
tomcw
664d7c2d86 Phasor card: in native mode, writes to SSI263 regs 0,1 & 2 all clear the SSI263 IRQ. (#1197) 2024-04-13 13:23:10 +01:00
TomCh
b00c9b0d3f
Fix for alloc of 'memimage': so that two adjacent 64K regions map to same physical 64K region. Fixes #1285. (PR #1286)
Includes platform-specific code for both Windows & Linux.
Co-authored-by: @audetto
2024-04-01 10:20:43 +01:00
tomcw
d3ff855f2d Minor: replace IS_APPLE2 macro with function 2024-03-28 22:36:34 +00:00
16 changed files with 470 additions and 130 deletions

BIN
resource/Apple2_Video.rom Normal file

Binary file not shown.

Binary file not shown.

View File

@ -60,7 +60,6 @@ DEBUG_BUTTON BITMAP "DEBUG.BMP"
DRIVE1_BUTTON BITMAP "DRIVE1.BMP" DRIVE1_BUTTON BITMAP "DRIVE1.BMP"
DRIVE2_BUTTON BITMAP "DRIVE2.BMP" DRIVE2_BUTTON BITMAP "DRIVE2.BMP"
SETUP_BUTTON BITMAP "SETUP.BMP" SETUP_BUTTON BITMAP "SETUP.BMP"
CHARSET40 BITMAP "CHARSET4.BMP"
DISKOFF_BITMAP BITMAP "DISKOFF.BMP" DISKOFF_BITMAP BITMAP "DISKOFF.BMP"
DISKREAD_BITMAP BITMAP "DISKREAD.BMP" DISKREAD_BITMAP BITMAP "DISKREAD.BMP"
DISKWRITE_BITMAP BITMAP "DISKWRIT.BMP" DISKWRITE_BITMAP BITMAP "DISKWRIT.BMP"
@ -348,7 +347,7 @@ IDR_APPLE2_ROM ROM "Apple2.rom"
IDR_APPLE2_PLUS_ROM ROM "Apple2_Plus.rom" IDR_APPLE2_PLUS_ROM ROM "Apple2_Plus.rom"
IDR_APPLE2_JPLUS_ROM ROM "Apple2_JPlus.rom" IDR_APPLE2_JPLUS_ROM ROM "Apple2_JPlus.rom"
IDR_APPLE2E_ROM ROM "Apple2e.rom" IDR_APPLE2E_ROM ROM "Apple2e.rom"
IDR_APPLE2E_ENHANCED_ROM ROM "Apple2e_Enhanced.rom" IDR_APPLE2E_ENHANCED_ROM ROM "Apple2e_Enhanced.rom"
IDR_PRAVETS_82_ROM ROM "Pravets82.rom" IDR_PRAVETS_82_ROM ROM "Pravets82.rom"
IDR_PRAVETS_8M_ROM ROM "Pravets8M.rom" IDR_PRAVETS_8M_ROM ROM "Pravets8M.rom"
IDR_PRAVETS_8C_ROM ROM "Pravets8C.rom" IDR_PRAVETS_8C_ROM ROM "Pravets8C.rom"
@ -361,7 +360,9 @@ IDR_FREEZES_F8_ROM ROM "FREEZES_NON-AUTOSTART_F8_ROM.ro
// VIDEO ROM // VIDEO ROM
// //
IDR_APPLE2_VIDEO_ROM ROM "Apple2_Video.rom"
IDR_APPLE2_JPLUS_VIDEO_ROM ROM "Apple2_JPlus_Video.rom" IDR_APPLE2_JPLUS_VIDEO_ROM ROM "Apple2_JPlus_Video.rom"
IDR_APPLE2E_ENHANCED_VIDEO_ROM ROM "Apple2e_Enhanced_Video.rom"
IDR_BASE64A_VIDEO_ROM ROM "Base64A_German_Video.rom" IDR_BASE64A_VIDEO_ROM ROM "Base64A_German_Video.rom"
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -28,11 +28,13 @@
#define IDR_MENU 125 #define IDR_MENU 125
#define IDR_APPLE2_ROM 126 #define IDR_APPLE2_ROM 126
#define IDR_APPLE2_PLUS_ROM 127 #define IDR_APPLE2_PLUS_ROM 127
#define IDR_APPLE2E_ROM 128 #define IDR_APPLE2_VIDEO_ROM 128
#define IDR_APPLE2E_ENHANCED_ROM 129 #define IDR_APPLE2E_ROM 129
#define IDD_TFE_SETTINGS_DIALOG 131 #define IDR_APPLE2E_ENHANCED_ROM 130
#define IDR_PRINTDRVR_FW 132 #define IDR_APPLE2E_ENHANCED_VIDEO_ROM 131
#define IDD_PROPPAGE_ADVANCED 132 #define IDD_TFE_SETTINGS_DIALOG 132
#define IDR_PRINTDRVR_FW 133
#define IDD_PROPPAGE_ADVANCED 133
#define IDR_SSC_FW 134 #define IDR_SSC_FW 134
#define IDR_MOCKINGBOARD_D_FW 135 #define IDR_MOCKINGBOARD_D_FW 135
#define IDR_MOUSEINTERFACE_FW 136 #define IDR_MOUSEINTERFACE_FW 136

View File

@ -153,6 +153,7 @@ void SY6522::Write(BYTE nReg, BYTE nValue)
m_regs.ORB = nValue & m_regs.DDRB; m_regs.ORB = nValue & m_regs.DDRB;
break; break;
case 0x01: // ORA case 0x01: // ORA
case 0x0f: // ORA_NO_HS
m_regs.ORA = nValue & m_regs.DDRA; m_regs.ORA = nValue & m_regs.DDRA;
break; break;
case 0x02: // DDRB case 0x02: // DDRB
@ -220,8 +221,6 @@ void SY6522::Write(BYTE nReg, BYTE nValue)
m_syncEvent[1]->m_canAssertIRQ = (m_regs.IER & IxR_TIMER2) ? true : false; m_syncEvent[1]->m_canAssertIRQ = (m_regs.IER & IxR_TIMER2) ? true : false;
UpdateIFR(0); UpdateIFR(0);
break; break;
case 0x0f: // ORA_NO_HS
break;
} }
} }
@ -348,6 +347,7 @@ BYTE SY6522::Read(BYTE nReg)
if (m_isMegaAudio) nValue = 0x00; // MegaAudio: IRB just reads as $00 if (m_isMegaAudio) nValue = 0x00; // MegaAudio: IRB just reads as $00
break; break;
case 0x01: // IRA case 0x01: // IRA
case 0x0f: // IRA_NO_HS
nValue = m_regs.ORA | (m_isBusDriven ? 0x00 : (m_regs.DDRA ^ 0xff)); // NB. Inputs bits driven by AY8913 if in PSG READ mode nValue = m_regs.ORA | (m_isBusDriven ? 0x00 : (m_regs.DDRA ^ 0xff)); // NB. Inputs bits driven by AY8913 if in PSG READ mode
if (m_isMegaAudio) nValue = 0x00; // MegaAudio: IRA just reads as $00 if (m_isMegaAudio) nValue = 0x00; // MegaAudio: IRA just reads as $00
break; break;
@ -400,9 +400,6 @@ BYTE SY6522::Read(BYTE nReg)
if (m_isMegaAudio) if (m_isMegaAudio)
nValue &= 0x7F; nValue &= 0x7F;
break; break;
case 0x0f: // ORA_NO_HS
nValue = m_regs.ORA;
break;
} }
return nValue; return nValue;

View File

@ -124,7 +124,7 @@ char FormatChar4Font(const BYTE b, bool* pWasHi_, bool* pWasLo_)
// Disassembly // Disassembly
/* /*
// Thought about moving MouseText to another location, say high bit, 'A' + 0x80 // Thought about moving MouseText to another location, say high bit, 'A' + 0x80
// But would like to keep compatibility with existing CHARSET40 // But would like to keep compatibility with existing CHARSET40 - UPDATE: we now use original video ROMs (GH#1308)
// Since we should be able to display all apple chars 0x00 .. 0xFF with minimal processing // Since we should be able to display all apple chars 0x00 .. 0xFF with minimal processing
// Use CONSOLE_COLOR_ESCAPE_CHAR to shift to mouse text // Use CONSOLE_COLOR_ESCAPE_CHAR to shift to mouse text
* Apple Font * Apple Font

View File

@ -56,9 +56,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "YamlHelper.h" #include "YamlHelper.h"
// In this file allocate the 64KB of RAM with aligned memory allocations (0x10000) // In this file allocate the 64KB of RAM with aligned memory allocations (0x10000)
// to ease mapping between Apple ][ and host memory space (while debugging). // to ease mapping between Apple ][ and host memory space (while debugging) & also to fix GH#1285.
// This is not available in Windows CRT:
// this is not available in Visual Studio
// https://en.cppreference.com/w/c/memory/aligned_alloc // https://en.cppreference.com/w/c/memory/aligned_alloc
#ifdef _MSC_VER #ifdef _MSC_VER
@ -66,6 +65,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#define ALIGNED_ALLOC(size) (LPBYTE)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE) #define ALIGNED_ALLOC(size) (LPBYTE)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)
#define ALIGNED_FREE(ptr) VirtualFree(ptr, 0, MEM_RELEASE) #define ALIGNED_FREE(ptr) VirtualFree(ptr, 0, MEM_RELEASE)
#else #else
#include <unistd.h>
#include <sys/mman.h>
// use plain "new" in gcc (where debugging needs are less important) // use plain "new" in gcc (where debugging needs are less important)
#define ALIGNED_ALLOC(size) new BYTE[size] #define ALIGNED_ALLOC(size) new BYTE[size]
#define ALIGNED_FREE(ptr) delete [] ptr #define ALIGNED_FREE(ptr) delete [] ptr
@ -242,7 +243,15 @@ static LPBYTE RWpages[kMaxExMemoryBanks]; // pointers to RW memory banks
static const UINT kNumAnnunciators = 4; static const UINT kNumAnnunciators = 4;
static bool g_Annunciator[kNumAnnunciators] = {}; static bool g_Annunciator[kNumAnnunciators] = {};
static const UINT num64KPages = 2; // number of 64K pages used to create hardware circular buffer
#ifdef _MSC_VER
static HANDLE g_hMemImage = NULL; // NB. When not initialised, this handle is NULL (not INVALID_HANDLE_VALUE)
#else
static FILE * g_hMemTempFile = NULL;
#endif
BYTE __stdcall IO_Annunciator(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles); BYTE __stdcall IO_Annunciator(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles);
static void FreeMemImage(void);
//============================================================================= //=============================================================================
@ -1289,7 +1298,7 @@ void MemDestroy()
{ {
ALIGNED_FREE(memaux); ALIGNED_FREE(memaux);
ALIGNED_FREE(memmain); ALIGNED_FREE(memmain);
ALIGNED_FREE(memimage); FreeMemImage();
delete [] memdirty; delete [] memdirty;
delete [] memrom; delete [] memrom;
@ -1509,12 +1518,153 @@ bool MemIsAddrCodeMemory(const USHORT addr)
//=========================================================================== //===========================================================================
static void FreeMemImage(void)
{
#ifdef _MSC_VER
if (g_hMemImage)
{
for (UINT i = 0; i < num64KPages; i++)
UnmapViewOfFile(memimage + i * _6502_MEM_LEN);
CloseHandle(g_hMemImage);
g_hMemImage = NULL;
}
else
{
ALIGNED_FREE(memimage);
}
#else
if (g_hMemTempFile)
{
// unmap the whole region, everything inside will be unmapped too
munmap(memimage, num64KPages * _6502_MEM_LEN);
fclose(g_hMemTempFile);
g_hMemTempFile = NULL;
}
else
{
ALIGNED_FREE(memimage);
}
#endif
}
static LPBYTE AllocMemImage(void)
{
#ifdef _MSC_VER
LPBYTE baseAddr = NULL;
// Allocate memory for 'memimage' (and the alias 'mem')
// . Setup so we have 2 consecutive virtual 64K regions pointing to the same physical 64K region.
// . This is a fix (and optimisation) for 6502 opcodes that do a 16-bit read at 6502 address $FFFF. (GH#1285)
SYSTEM_INFO info;
GetSystemInfo(&info);
bool res = (info.dwAllocationGranularity == _6502_MEM_LEN);
if (res)
{
UINT retry = 10;
do
{
res = false;
const SIZE_T totalVirtualSize = _6502_MEM_LEN * num64KPages;
baseAddr = (LPBYTE)VirtualAlloc(0, totalVirtualSize, MEM_RESERVE, PAGE_NOACCESS);
if (baseAddr == NULL)
break;
VirtualFree(baseAddr, 0, MEM_RELEASE);
// Create a file mapping object of [64K] size that is backed by the system paging file.
g_hMemImage = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, _6502_MEM_LEN, NULL);
// NB. Returns NULL on failure (not INVALID_HANDLE_VALUE)
if (g_hMemImage == NULL)
break;
UINT count = 0;
while (count < num64KPages)
{
// MSDN: "To specify a suggested base address for the view, use the MapViewOfFileEx function. However, this practice is not recommended."
// The OS (ie. another process) may've beaten us to this suggested baseAddr. This is why we retry multiple times.
if (!MapViewOfFileEx(g_hMemImage, FILE_MAP_ALL_ACCESS, 0, 0, _6502_MEM_LEN, baseAddr + count * _6502_MEM_LEN))
break;
count++;
}
res = (count == num64KPages);
if (res)
break;
// Failed this time, so clean-up and retry...
FreeMemImage();
}
while (retry--);
#if 1
if (res) // test
{
baseAddr[0x0000] = 0x11;
baseAddr[0xffff] = 0x22;
USHORT value = *((USHORT*)(baseAddr + 0xffff));
_ASSERT(value == 0x1122);
}
#endif
}
else
{
LogFileOutput("MemInitialize: SYSETEM_INFO.wAllocationGranularity = 0x%08X.\n", info.dwAllocationGranularity);
}
if (!res)
{
LogFileOutput("MemInitialize: Failed to map 2 adjacent virtual 64K pages (reverting to old method).\n");
baseAddr = ALIGNED_ALLOC(_6502_MEM_LEN);
}
return baseAddr;
#else
g_hMemTempFile = tmpfile();
if (g_hMemTempFile)
{
const int fd = fileno(g_hMemTempFile);
if (!ftruncate(fd, _6502_MEM_LEN))
{
LPBYTE baseAddr = static_cast<LPBYTE>(mmap(NULL, num64KPages * _6502_MEM_LEN, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
if (baseAddr)
{
bool ok = true;
for (UINT i = 0; i < num64KPages; i++)
{
void * target = baseAddr + i * _6502_MEM_LEN;
void * addr = mmap(target, _6502_MEM_LEN, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
ok = ok && (target == addr);
}
// we could fclose the file here
// but we keep it as a reminder of how to free the memory later
if (ok)
{
return baseAddr;
}
// revert to ALIGNED_ALLOC
munmap(baseAddr, num64KPages * _6502_MEM_LEN);
}
}
fclose(g_hMemTempFile);
g_hMemTempFile = NULL;
}
LogFileOutput("MemInitialize: Failed to map 2 adjacent virtual 64K pages (reverting to old method).\n");
return ALIGNED_ALLOC(_6502_MEM_LEN);
#endif
}
//===========================================================================
void MemInitialize() void MemInitialize()
{ {
// ALLOCATE MEMORY FOR THE APPLE MEMORY IMAGE AND ASSOCIATED DATA STRUCTURES // ALLOCATE MEMORY FOR THE APPLE MEMORY IMAGE AND ASSOCIATED DATA STRUCTURES
memaux = ALIGNED_ALLOC(_6502_MEM_LEN); // NB. alloc even if model is Apple II/II+, since it's used by VidHD card memaux = ALIGNED_ALLOC(_6502_MEM_LEN); // NB. alloc even if model is Apple II/II+, since it's used by VidHD card
memmain = ALIGNED_ALLOC(_6502_MEM_LEN); memmain = ALIGNED_ALLOC(_6502_MEM_LEN);
memimage = ALIGNED_ALLOC(_6502_MEM_LEN); memimage = AllocMemImage();
memdirty = new BYTE[0x100]; memdirty = new BYTE[0x100];
memrom = new BYTE[0x3000 * MaxRomPages]; memrom = new BYTE[0x3000 * MaxRomPages];

View File

@ -405,9 +405,9 @@ bool MockingboardCard::Is6522IRQ(void)
irq |= m_MBSubUnit[i].sy6522.GetReg(SY6522::rIFR) & 0x80 ? true : false; irq |= m_MBSubUnit[i].sy6522.GetReg(SY6522::rIFR) & 0x80 ? true : false;
// NB. Mockingboard generates IRQ on both 6522s: // NB. Mockingboard generates IRQ on both 6522s:
// . SSI263's IRQ (A/!R) is routed via the 2nd 6522 (at $Cn80) and must generate a 6502 IRQ (not NMI) // . SSI263's IRQ (A/!R) is routed via the 2nd 6522's CA1 input (at $Cn80) and must generate a 6502 IRQ (not NMI)
// - NB. 2nd SSI263's IRQ is routed via the 1st 6522 (at $Cn00) and again generates a 6502 IRQ // - NB. 2nd SSI263's IRQ is routed via the 1st 6522's CA1 input (at $Cn00) and again generates a 6502 IRQ
// . SC-01's IRQ (A/!R) is routed via the 6522 at $Cn00 (NB. Only the Mockingboard "Sound/Speech I" card supports the SC-01) // . SC-01's IRQ (!A/R) is routed via the 6522 at $Cn00 (NB. Only the Mockingboard "Sound/Speech I" card supports the SC-01)
// Phasor's SSI263 IRQ (A/!R) line is *also* wired directly to the 6502's IRQ (as well as the 6522's CA1) // Phasor's SSI263 IRQ (A/!R) line is *also* wired directly to the 6502's IRQ (as well as the 6522's CA1)
return irq; return irq;
@ -576,7 +576,7 @@ void MockingboardCard::Reset(const bool powerCycle) // CTRL+RESET or power-cycle
m_MBSubUnit[subunit].Reset(QueryType()); m_MBSubUnit[subunit].Reset(QueryType());
m_MBSubUnit[subunit].ssi263.SetCardMode(PH_Mockingboard); // Revert to PH_Mockingboard mode m_MBSubUnit[subunit].ssi263.SetCardMode(PH_Mockingboard); // Revert to PH_Mockingboard mode
m_MBSubUnit[subunit].ssi263.Reset(); m_MBSubUnit[subunit].ssi263.Reset(powerCycle, m_phasorEnable);
} }
// Reset state // Reset state
@ -668,7 +668,7 @@ BYTE MockingboardCard::IOReadInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE nVa
return MemReadFloatingBus(nExecutedCycles); return MemReadFloatingBus(nExecutedCycles);
#endif #endif
// NB. Mockingboard: SSI263.bit7 not readable (TODO: check this with real h/w) // NB. Mockingboard: SSI263.bit7 not readable
const BYTE subunit = QueryType() == CT_SDMusic ? SY6522_DEVICE_A : !(nAddr & 0x80) ? SY6522_DEVICE_A : SY6522_DEVICE_B; const BYTE subunit = QueryType() == CT_SDMusic ? SY6522_DEVICE_A : !(nAddr & 0x80) ? SY6522_DEVICE_A : SY6522_DEVICE_B;
const BYTE reg = nAddr & 0xf; const BYTE reg = nAddr & 0xf;
return m_MBSubUnit[subunit].sy6522.Read(reg); return m_MBSubUnit[subunit].sy6522.Read(reg);
@ -751,14 +751,13 @@ BYTE MockingboardCard::IOWriteInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE nV
WriteToORB(SY6522_DEVICE_B); WriteToORB(SY6522_DEVICE_B);
} }
bool CS_SSI263_A = (m_phasorMode == PH_Phasor) ? !(nAddr & 0x80) && (nAddr & 0x40) // SSI263 at $Cn4x, $Cn6x
: nAddr & 0x40; // SSI263 at $Cn4x-Cn7x, $CnCx-CnFx
bool CS_SSI263_B = (m_phasorMode == PH_Phasor) ? !(nAddr & 0x80) && (nAddr & 0x20) // SSI263 at $Cn2x, $Cn6x
: nAddr & 0x20; // SSI263 at $Cn2x-Cn3x, $Cn6x-Cn7x, $CnAx-CnBx, $CnEx-CnFx
if (m_phasorMode == PH_Mockingboard || m_phasorMode == PH_Phasor) // No SSI263 for Echo+ if (m_phasorMode == PH_Mockingboard || m_phasorMode == PH_Phasor) // No SSI263 for Echo+
{ {
// Confirmed that Phasor has no extra logic to map SSI263 (it's the same as Mockingboard's)
bool CS_SSI263_A = nAddr & 0x40; // SSI263 at $Cn4x-Cn7x, $CnCx-CnFx
bool CS_SSI263_B = nAddr & 0x20; // SSI263 at $Cn2x-Cn3x, $Cn6x-Cn7x, $CnAx-CnBx, $CnEx-CnFx
// NB. Mockingboard mode: writes to $Cn4x/SSI263 also get written to 1st 6522 (have confirmed on real Phasor h/w) // NB. Mockingboard mode: writes to $Cn4x/SSI263 also get written to 1st 6522 (have confirmed on real Phasor h/w)
if (CS_SSI263_A) // Primary SSI263 if (CS_SSI263_A) // Primary SSI263
m_MBSubUnit[1].ssi263.Write(nAddr&0x7, nValue); // 2nd 6522 is used for 1st speech chip m_MBSubUnit[1].ssi263.Write(nAddr&0x7, nValue); // 2nd 6522 is used for 1st speech chip
@ -839,6 +838,8 @@ BYTE MockingboardCard::PhasorIOInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE n
m_phasorClockScaleFactor = 1; m_phasorClockScaleFactor = 1;
else if (m_phasorMode == PH_Phasor) else if (m_phasorMode == PH_Phasor)
m_phasorClockScaleFactor = 2; m_phasorClockScaleFactor = 2;
else // undefined mode
m_phasorClockScaleFactor = 1; // TODO: Check for undefined Phasor mode
if (m_phasorMode == PH_Mockingboard) if (m_phasorMode == PH_Mockingboard)
{ {
@ -849,7 +850,7 @@ BYTE MockingboardCard::PhasorIOInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE n
AY8910_InitClock((int)(Get6502BaseClock() * m_phasorClockScaleFactor)); AY8910_InitClock((int)(Get6502BaseClock() * m_phasorClockScaleFactor));
for (UINT i = 0; i < NUM_SSI263; i++) for (UINT i = 0; i < NUM_SSI263; i++)
m_MBSubUnit[i].ssi263.SetCardMode(m_phasorMode); m_MBSubUnit[i].ssi263.SetCardMode(m_phasorMode); // TODO: Check for undefined Phasor mode
#if DBG_SUPPORT_ECHOPLUS #if DBG_SUPPORT_ECHOPLUS
if (m_phasorMode == PH_EchoPlus && (nAddr & 0xf) == 0) if (m_phasorMode == PH_EchoPlus && (nAddr & 0xf) == 0)
@ -1155,7 +1156,9 @@ UINT MockingboardCard::AY8910_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, BYTE
// "Reg Address Latch Valid A" + "Reg Address Latch Valid B" // "Reg Address Latch Valid A" + "Reg Address Latch Valid B"
// Changed at AppleWin 1.30.14 // Changed at AppleWin 1.30.14
//11: Added: "Bus Driven by AY" //11: Added: "Bus Driven by AY"
const UINT kUNIT_VERSION = 11; //12: Added: SSI263: SC01 phoneme & active
// Current Mode changed (added bit5 = enableInts)
const UINT kUNIT_VERSION = 12;
#define SS_YAML_KEY_MB_UNIT "Unit" #define SS_YAML_KEY_MB_UNIT "Unit"
#define SS_YAML_KEY_AY_CURR_REG "AY Current Register" #define SS_YAML_KEY_AY_CURR_REG "AY Current Register"
@ -1231,6 +1234,8 @@ void MockingboardCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
pMB->sy6522.SaveSnapshot(yamlSaveHelper); pMB->sy6522.SaveSnapshot(yamlSaveHelper);
AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_A, std::string("")); AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_A, std::string(""));
pMB->ssi263.SaveSnapshot(yamlSaveHelper); pMB->ssi263.SaveSnapshot(yamlSaveHelper);
if (subunit == 0) // has SC01
pMB->ssi263.SC01_SaveSnapshot(yamlSaveHelper);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE, pMB->state[0]); yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE, pMB->state[0]);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_AY_CURR_REG, pMB->nAYCurrentRegister[0]); // save all 8 bits (even though top 4 bits should be 0) yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_AY_CURR_REG, pMB->nAYCurrentRegister[0]); // save all 8 bits (even though top 4 bits should be 0)
@ -1271,6 +1276,8 @@ bool MockingboardCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version
UpdateIFRandIRQ(pMB, 0, pMB->sy6522.GetReg(SY6522::rIFR)); // Assert any pending IRQs (GH#677) UpdateIFRandIRQ(pMB, 0, pMB->sy6522.GetReg(SY6522::rIFR)); // Assert any pending IRQs (GH#677)
AY8910_LoadSnapshot(yamlLoadHelper, subunit, AY8913_DEVICE_A, std::string("")); AY8910_LoadSnapshot(yamlLoadHelper, subunit, AY8913_DEVICE_A, std::string(""));
pMB->ssi263.LoadSnapshot(yamlLoadHelper, PH_Mockingboard, version); // Pre: SetVotraxPhoneme() pMB->ssi263.LoadSnapshot(yamlLoadHelper, PH_Mockingboard, version); // Pre: SetVotraxPhoneme()
if (subunit == 0) // has SC01
pMB->ssi263.SC01_LoadSnapshot(yamlLoadHelper, version);
pMB->nAYCurrentRegister[0] = yamlLoadHelper.LoadUint(SS_YAML_KEY_AY_CURR_REG); pMB->nAYCurrentRegister[0] = yamlLoadHelper.LoadUint(SS_YAML_KEY_AY_CURR_REG);
@ -1337,6 +1344,8 @@ void MockingboardCard::Phasor_SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_A, std::string("-A")); AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_A, std::string("-A"));
AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_B, std::string("-B")); AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_B, std::string("-B"));
pMB->ssi263.SaveSnapshot(yamlSaveHelper); pMB->ssi263.SaveSnapshot(yamlSaveHelper);
if (subunit == 0) // has SC01
pMB->ssi263.SC01_SaveSnapshot(yamlSaveHelper);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE, pMB->state[0]); yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE, pMB->state[0]);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE_B, pMB->state[1]); yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE_B, pMB->state[1]);
@ -1405,6 +1414,8 @@ bool MockingboardCard::Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT
AY8910_LoadSnapshot(yamlLoadHelper, subunit, AY8913_DEVICE_B, std::string("-B")); AY8910_LoadSnapshot(yamlLoadHelper, subunit, AY8913_DEVICE_B, std::string("-B"));
} }
pMB->ssi263.LoadSnapshot(yamlLoadHelper, m_phasorMode, version); // Pre: SetVotraxPhoneme() pMB->ssi263.LoadSnapshot(yamlLoadHelper, m_phasorMode, version); // Pre: SetVotraxPhoneme()
if (subunit == 0) // has SC01
pMB->ssi263.SC01_LoadSnapshot(yamlLoadHelper, version);
pMB->nAYCurrentRegister[0] = yamlLoadHelper.LoadUint(SS_YAML_KEY_AY_CURR_REG); pMB->nAYCurrentRegister[0] = yamlLoadHelper.LoadUint(SS_YAML_KEY_AY_CURR_REG);
if (version >= 10) if (version >= 10)

View File

@ -246,6 +246,15 @@ static void userVideoRomForIIPlus(void)
//------------------------------------- //-------------------------------------
static void VideoRomForIIandIIPlus(void)
{
BYTE* pVideoRom = GetFrame().GetResource(IDR_APPLE2_VIDEO_ROM, "ROM", Video::kVideoRomSize2K);
if (pVideoRom == NULL)
return;
userVideoRom2K(&csbits_a2[0], pVideoRom);
}
static void VideoRomForIIJPlus(void) static void VideoRomForIIJPlus(void)
{ {
BYTE* pVideoRom = GetFrame().GetResource(IDR_APPLE2_JPLUS_VIDEO_ROM, "ROM", Video::kVideoRomSize2K); BYTE* pVideoRom = GetFrame().GetResource(IDR_APPLE2_JPLUS_VIDEO_ROM, "ROM", Video::kVideoRomSize2K);
@ -266,19 +275,27 @@ static void VideoRomForBase64A(void)
userVideoRom2K(&csbits_base64a[1], pVideoRom + Video::kVideoRomSize2K, A2TYPE_BASE64A, 0); userVideoRom2K(&csbits_base64a[1], pVideoRom + Video::kVideoRomSize2K, A2TYPE_BASE64A, 0);
} }
static void VideoRomForIIeEnhanced(void)
{
BYTE* pVideoRom = GetFrame().GetResource(IDR_APPLE2E_ENHANCED_VIDEO_ROM, "ROM", Video::kVideoRomSize4K);
if (pVideoRom == NULL)
return;
userVideoRom4K(&csbits_enhanced2e[0], pVideoRom);
}
//------------------------------------- //-------------------------------------
void make_csbits(void) void make_csbits(void)
{ {
get_csbits(&csbits_enhanced2e[0], TEXT("CHARSET40"), 0); // Enhanced //e: Alt char set off
get_csbits(&csbits_enhanced2e[1], TEXT("CHARSET40"), 16); // Enhanced //e: Alt char set on (mousetext)
get_csbits(&csbits_a2[0], TEXT("CHARSET40"), 32); // Apple ][, ][+
get_csbits(&csbits_pravets82[0], TEXT("CHARSET82"), 0); // Pravets 82 get_csbits(&csbits_pravets82[0], TEXT("CHARSET82"), 0); // Pravets 82
get_csbits(&csbits_pravets8M[0], TEXT("CHARSET8M"), 0); // Pravets 8M get_csbits(&csbits_pravets8M[0], TEXT("CHARSET8M"), 0); // Pravets 8M
get_csbits(&csbits_pravets8C[0], TEXT("CHARSET8C"), 0); // Pravets 8A / 8C: Alt char set off get_csbits(&csbits_pravets8C[0], TEXT("CHARSET8C"), 0); // Pravets 8A / 8C: Alt char set off
get_csbits(&csbits_pravets8C[1], TEXT("CHARSET8C"), 16); // Pravets 8A / 8C: Alt char set on get_csbits(&csbits_pravets8C[1], TEXT("CHARSET8C"), 16); // Pravets 8A / 8C: Alt char set on
VideoRomForIIandIIPlus(); // GH#1308
VideoRomForIIeEnhanced(); // GH#1308
// Original //e is just Enhanced //e with the 32 mousetext chars [0x40..0x5F] replaced by the non-alt charset chars [0x40..0x5F] // Original //e is just Enhanced //e with the 32 mousetext chars [0x40..0x5F] replaced by the non-alt charset chars [0x40..0x5F]
memcpy(csbits_2e, csbits_enhanced2e, sizeof(csbits_enhanced2e)); memcpy(csbits_2e, csbits_enhanced2e, sizeof(csbits_enhanced2e));
memcpy(&csbits_2e[1][64], &csbits_2e[0][64], 32*8); memcpy(&csbits_2e[1][64], &csbits_2e[0][64], 32*8);

View File

@ -4,7 +4,7 @@ AppleWin : An Apple //e emulator for Windows
Copyright (C) 1994-1996, Michael O'Brien Copyright (C) 1994-1996, Michael O'Brien
Copyright (C) 1999-2001, Oliver Schmidt Copyright (C) 1999-2001, Oliver Schmidt
Copyright (C) 2002-2005, Tom Charlesworth Copyright (C) 2002-2005, Tom Charlesworth
Copyright (C) 2006-2021, Tom Charlesworth, Michael Pohoreski, Nick Westgate Copyright (C) 2006-2024, Tom Charlesworth, Michael Pohoreski, Nick Westgate
AppleWin is free software; you can redistribute it and/or modify AppleWin is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -23,7 +23,24 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/* Description: SSI263 emulation /* Description: SSI263 emulation
* *
* Author: Various * Extra "spec" that's not obvious from the datasheet: (GH#175)
* . Writes to regs 0,1,2 (and reg3.CTL=1) all de-assert the IRQ (and writes to reg3.CTL=0 and regs 4..7 don't) (GH#1197)
* . A phoneme will continue playing back infinitely; unless the phoneme is changed or CTL=1.
* . NB. if silenced (Amplitude=0) it's still playing.
* . The IRQ is set at the end of the phoneme.
* . If IRQ is then cleared, a new IRQ will occur when the phoneme completes again (but need to clear IRQ with a write to reg0, 1 or 2, even for Mockingboard-C).
* . CTL=1 sets "PD" (Power Down / "standby") mode, also set at power-on.
* . Registers can still be changed in this mode.
* . IRQ de-asserted & D7=0.
* . CTL=0 brings device out of "PD" mode, the mode will be set to DR1,DR0 and the phoneme P5-P0 will play.
* . Setting mode to DR1:0 = %00 just disables A/!R (ie. disables interrupts), but otherwise retains the previous DR1:0 mode.
* . If an IRQ was previously asserted then to set DR1:0=%00, you must go via CTL=1, which de-asserts the IRQ.
* . Mockingboard-C: CTRL+RESET is not connected to !PD/!RST pin 18.
* . Phasor: TODO: check with a 'scope.
* . Phasor: with SSI263 ints disabled & reg0's DR1:0 != %00, then CTRL+RESET will cause SSI263 to enable ints & assert IRQ.
* . it's as if the SSI263 does a CTL H->L to pick-up the new DR1:0. (Bug in SSI263? Assume it should remain in PD mode.)
* . but if CTL=1, then CTRL+RESET has no effect.
* . Power-on: PD=1 (so D7=0), reg4 (Filter Freq)=0xFF (other regs are seemingly random?).
*/ */
#include "StdAfx.h" #include "StdAfx.h"
@ -54,26 +71,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
const DWORD SAMPLE_RATE_SSI263 = 22050; const DWORD SAMPLE_RATE_SSI263 = 22050;
// Duration/Phonome
const BYTE DURATION_MODE_MASK = 0xC0;
const BYTE DURATION_SHIFT = 6;
const BYTE PHONEME_MASK = 0x3F;
const BYTE MODE_PHONEME_TRANSITIONED_INFLECTION = 0xC0; // IRQ active
const BYTE MODE_PHONEME_IMMEDIATE_INFLECTION = 0x80; // IRQ active
const BYTE MODE_FRAME_IMMEDIATE_INFLECTION = 0x40; // IRQ active
const BYTE MODE_IRQ_DISABLED = 0x00;
// Rate/Inflection
const BYTE RATE_MASK = 0xF0;
const BYTE INFLECTION_MASK_H = 0x08; // I11
const BYTE INFLECTION_MASK_L = 0x07; // I2..I0
// Ctrl/Art/Amp
const BYTE CONTROL_MASK = 0x80;
const BYTE ARTICULATION_MASK = 0x70;
const BYTE AMPLITUDE_MASK = 0x0F;
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#if LOG_SSI263B #if LOG_SSI263B
@ -127,8 +124,9 @@ BYTE SSI263::Read(ULONG nExecutedCycles)
{ {
// Regardless of register, just return inverted A/!R in bit7 // Regardless of register, just return inverted A/!R in bit7
// . inverted "A/!R" is high for REQ (ie. Request, as phoneme nearly complete) // . inverted "A/!R" is high for REQ (ie. Request, as phoneme nearly complete)
// NB. this doesn't clear the IRQ
return MemReadFloatingBus(m_currentMode & 1, nExecutedCycles); return MemReadFloatingBus(m_currentMode.D7, nExecutedCycles);
} }
void SSI263::Write(BYTE nReg, BYTE nValue) void SSI263::Write(BYTE nReg, BYTE nValue)
@ -140,56 +138,48 @@ void SSI263::Write(BYTE nReg, BYTE nValue)
ssiRegs[nReg] = nValue; ssiRegs[nReg] = nValue;
#endif #endif
// SSI263 datasheet is not clear, but a write to DURPHON de-asserts the IRQ and clears D7.
// . Empirically writes to regs 0,1,2 (and reg3.CTL=1) all de-assert the IRQ (and writes to reg3.CTL=0 and regs 4..7 don't) (GH#1197)
// NB. The same for Mockingboard as there's no automatic handshake from the 6522 (CA2 isn't connected to the SSI263). So writes to regs 0, 1 or 2 complete the "handshake".
if (nReg <= SSI_RATEINF)
{
CpuIrqDeassert(IS_SPEECH);
m_currentMode.D7 = 0;
}
switch(nReg) switch(nReg)
{ {
case SSI_DURPHON: case SSI_DURPHON:
#if LOG_SSI263 #if LOG_SSI263
if(g_fh) fprintf(g_fh, "DUR = 0x%02X, PHON = 0x%02X\n\n", nValue>>6, nValue&PHONEME_MASK); 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); LogOutput("DUR = %d, PHON = 0x%02X\n", nValue>>6, nValue&PHONEME_MASK);
#endif #endif
#if LOG_SSI263B #if LOG_SSI263B
SSI_Output(); SSI_Output();
#endif #endif
// 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 (m_cardMode == PH_Phasor)
{
CpuIrqDeassert(IS_SPEECH);
}
m_currentMode &= ~1; // Clear SSI263's D7 pin
m_durationPhoneme = nValue; m_durationPhoneme = nValue;
m_isVotraxPhoneme = false;
Play(nValue & PHONEME_MASK); if ((m_ctrlArtAmp & CONTROL_MASK) == 0)
Play(m_durationPhoneme & PHONEME_MASK); // Play phoneme when *not* in power-down / standby mode
break; break;
case SSI_INFLECT: case SSI_INFLECT:
#if LOG_SSI263 #if LOG_SSI263
if(g_fh) fprintf(g_fh, "INF = 0x%02X\n", nValue); if (g_fh) fprintf(g_fh, "INF = 0x%02X\n", nValue);
#endif #endif
m_inflection = nValue; m_inflection = nValue;
break; break;
case SSI_RATEINF: case SSI_RATEINF:
#if LOG_SSI263 #if LOG_SSI263
if(g_fh) fprintf(g_fh, "RATE = 0x%02X, INF = 0x%02X\n", nValue>>4, nValue&0x0F); if (g_fh) fprintf(g_fh, "RATE = 0x%02X, INF = 0x%02X\n", nValue>>4, nValue&0x0F);
#endif #endif
m_rateInflection = nValue; m_rateInflection = nValue;
break; break;
case SSI_CTTRAMP: case SSI_CTTRAMP:
#if LOG_SSI263 #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); 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 = (m_ctrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK); bool H2L = (m_ctrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK);
@ -201,23 +191,25 @@ void SSI263::Write(BYTE nReg, BYTE nValue)
if ( ((m_ctrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK)) || ((nValue&0xF) == 0x0) ) // H->L or amp=0 if ( ((m_ctrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK)) || ((nValue&0xF) == 0x0) ) // H->L or amp=0
SSI_Output(); SSI_Output();
#endif #endif
if((m_ctrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK)) // H->L if ((m_ctrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK)) // H->L
{ {
m_currentMode = m_durationPhoneme & DURATION_MODE_MASK; // NB. Just changed from CTL=1 (power-down) - where IRQ was already de-asserted & D7=0
if (m_currentMode == MODE_IRQ_DISABLED) // . So CTL H->L never affects IRQ or D7
{ SetDeviceModeAndInts();
// "Disables A/!R output only; does not change previous A/!R response" (SSI263 datasheet)
// CpuIrqDeassert(IS_SPEECH); // Device out of power down / "standby" mode, so play phoneme
} m_isVotraxPhoneme = false;
Play(m_durationPhoneme & PHONEME_MASK);
} }
m_ctrlArtAmp = nValue; m_ctrlArtAmp = nValue;
// "Setting the Control bit (CTL) to a logic one puts the device into Power Down mode..." (SSI263 datasheet) // "Setting the Control bit (CTL) to a logic one puts the device into Power Down mode..." (SSI263 datasheet)
// . this silences the phoneme - actually "turns off the excitation sources and analog circuits"
if (m_ctrlArtAmp & CONTROL_MASK) if (m_ctrlArtAmp & CONTROL_MASK)
{ {
// CpuIrqDeassert(IS_SPEECH); CpuIrqDeassert(IS_SPEECH);
// m_currentMode &= ~1; // Clear SSI263's D7 pin m_currentMode.D7 = 0;
} }
break; break;
case SSI_FILFREQ: // RegAddr.b2=1 (b1 & b0 are: don't care) case SSI_FILFREQ: // RegAddr.b2=1 (b1 & b0 are: don't care)
@ -230,6 +222,20 @@ void SSI263::Write(BYTE nReg, BYTE nValue)
} }
} }
void SSI263::SetDeviceModeAndInts(void)
{
if ((m_durationPhoneme & DURATION_MODE_MASK) != MODE_IRQ_DISABLED)
{
m_currentMode.function = (m_durationPhoneme & DURATION_MODE_MASK) >> DURATION_MODE_SHIFT;
m_currentMode.enableInts = 1;
}
else
{
// "Disables A/!R output only; does not change previous A/!R response" (SSI263 datasheet)
m_currentMode.enableInts = 0;
}
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
const BYTE SSI263::m_Votrax2SSI263[/*64*/] = const BYTE SSI263::m_Votrax2SSI263[/*64*/] =
@ -309,6 +315,7 @@ void SSI263::Votrax_Write(BYTE value)
LogOutput("SC01: %02X (= SSI263: %02X)\n", value, m_Votrax2SSI263[value & PHONEME_MASK]); LogOutput("SC01: %02X (= SSI263: %02X)\n", value, m_Votrax2SSI263[value & PHONEME_MASK]);
#endif #endif
m_isVotraxPhoneme = true; m_isVotraxPhoneme = true;
m_votraxPhoneme = value & PHONEME_MASK;
// !A/R: Acknowledge receipt of phoneme data (signal goes from high to low) // !A/R: Acknowledge receipt of phoneme data (signal goes from high to low)
UpdateIFR(m_device, SY6522::IxR_VOTRAX, 0); UpdateIFR(m_device, SY6522::IxR_VOTRAX, 0);
@ -316,7 +323,7 @@ void SSI263::Votrax_Write(BYTE value)
// NB. Don't set reg0.DUR, as SC01's phoneme duration doesn't change with pitch (empirically determined from MAME's SC01 emulation) // NB. Don't set reg0.DUR, as SC01's phoneme duration doesn't change with pitch (empirically determined from MAME's SC01 emulation)
//m_durationPhoneme = value; // Set reg0.DUR = I1:0 (inflection or pitch) //m_durationPhoneme = value; // Set reg0.DUR = I1:0 (inflection or pitch)
m_durationPhoneme = 0; m_durationPhoneme = 0;
Play(m_Votrax2SSI263[value & PHONEME_MASK]); Play(m_Votrax2SSI263[m_votraxPhoneme]);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -365,6 +372,7 @@ void SSI263::Play(unsigned int nPhoneme)
m_phonemeAccurateLengthRemaining = m_phonemeLengthRemaining; m_phonemeAccurateLengthRemaining = m_phonemeLengthRemaining;
m_phonemePlaybackAndDebugger = (g_nAppMode == MODE_STEPPING || g_nAppMode == MODE_DEBUG); m_phonemePlaybackAndDebugger = (g_nAppMode == MODE_STEPPING || g_nAppMode == MODE_DEBUG);
m_phonemeCompleteByFullSpeed = false; m_phonemeCompleteByFullSpeed = false;
m_phonemeLeadoutLength = m_phonemeLengthRemaining / 10; // Arbitrary! (TODO: determine a more accurate factor)
if (bPause) if (bPause)
{ {
@ -580,18 +588,22 @@ void SSI263::Update(void)
//------------- //-------------
const double amplitude = !m_isVotraxPhoneme ? (double)(m_ctrlArtAmp & AMPLITUDE_MASK) / (double)AMPLITUDE_MASK : 1.0; const double amplitude = m_isVotraxPhoneme ? 1.0
: m_ctrlArtAmp & CONTROL_MASK ? 0.0 // Power-down / standby
: m_filterFreq == FILTER_FREQ_SILENCE ? 0.0
: (double)(m_ctrlArtAmp & AMPLITUDE_MASK) / (double)AMPLITUDE_MASK;
bool bSpeechIRQ = false; bool bSpeechIRQ = false;
{ {
const BYTE DUR = m_durationPhoneme >> DURATION_SHIFT; const BYTE DUR = (m_currentMode.function == (MODE_FRAME_IMMEDIATE_INFLECTION >> DURATION_MODE_SHIFT)) ? 3 // Frame timing mode
: m_durationPhoneme >> DURATION_MODE_SHIFT; // Phoneme timing mode
const BYTE numSamplesToAvg = (DUR <= 1) ? 1 : const BYTE numSamplesToAvg = (DUR <= 1) ? 1 :
(DUR == 2) ? 2 : (DUR == 2) ? 2 :
4; 4;
short* pMixBuffer = &m_mixBufferSSI263[0]; short* pMixBuffer = &m_mixBufferSSI263[0];
int zeroSize = nNumSamples; UINT zeroSize = nNumSamples;
if (m_phonemeLengthRemaining && !prefillBufferOnInit) if (m_phonemeLengthRemaining && !prefillBufferOnInit)
{ {
@ -632,7 +644,12 @@ void SSI263::Update(void)
} }
if (zeroSize) if (zeroSize)
{
memset(pMixBuffer, 0, zeroSize * sizeof(short)); memset(pMixBuffer, 0, zeroSize * sizeof(short));
if (!prefillBufferOnInit)
m_phonemeLeadoutLength -= (m_phonemeLeadoutLength > zeroSize) ? zeroSize : m_phonemeLeadoutLength;
}
} }
// //
@ -665,8 +682,24 @@ void SSI263::Update(void)
{ {
// NB. if m_phonemePlaybackAndDebugger==true, then "m_phonemeAccurateLengthRemaining!=0" must be true. // NB. if m_phonemePlaybackAndDebugger==true, then "m_phonemeAccurateLengthRemaining!=0" must be true.
// Since in UpdateAccurateLength(), (when m_phonemePlaybackAndDebugger==true) then m_phonemeAccurateLengthRemaining decs to zero. // Since in UpdateAccurateLength(), (when m_phonemePlaybackAndDebugger==true) then m_phonemeAccurateLengthRemaining decs to zero.
#if _DEBUG
if (m_phonemePlaybackAndDebugger)
{
_ASSERT(m_phonemeAccurateLengthRemaining); // Check this!
}
#endif
if (!m_phonemePlaybackAndDebugger /*|| m_phonemeAccurateLengthRemaining*/) // superfluous, so commented out (see above) if (!m_phonemePlaybackAndDebugger /*|| m_phonemeAccurateLengthRemaining*/) // superfluous, so commented out (see above)
{
UpdateIRQ(); UpdateIRQ();
}
}
if (m_phonemeLeadoutLength == 0)
{
if (!m_isVotraxPhoneme)
Play(m_durationPhoneme & PHONEME_MASK); // Repeat this phoneme again
else
Play(m_Votrax2SSI263[m_votraxPhoneme]); // Votrax phoneme repeats too (tested in MAME 0.262)
} }
} }
@ -688,7 +721,7 @@ void SSI263::UpdateAccurateLength(void)
const double nIrqFreq = g_fCurrentCLK6502 / updateInterval + 0.5; // Round-up const double nIrqFreq = g_fCurrentCLK6502 / updateInterval + 0.5; // Round-up
const int nNumSamplesPerPeriod = (int)((double)(SAMPLE_RATE_SSI263) / nIrqFreq); // Eg. For 60Hz this is 367 const int nNumSamplesPerPeriod = (int)((double)(SAMPLE_RATE_SSI263) / nIrqFreq); // Eg. For 60Hz this is 367
const BYTE DUR = m_durationPhoneme >> DURATION_SHIFT; const BYTE DUR = m_durationPhoneme >> DURATION_MODE_SHIFT;
const UINT numSamples = nNumSamplesPerPeriod * (DUR+1); const UINT numSamples = nNumSamplesPerPeriod * (DUR+1);
if (m_phonemeAccurateLengthRemaining > numSamples) if (m_phonemeAccurateLengthRemaining > numSamples)
@ -732,32 +765,37 @@ void SSI263::UpdateIRQ(void)
// Pre: m_isVotraxPhoneme, m_cardMode, m_device // Pre: m_isVotraxPhoneme, m_cardMode, m_device
void SSI263::SetSpeechIRQ(void) void SSI263::SetSpeechIRQ(void)
{ {
if (!m_isVotraxPhoneme) if (!m_isVotraxPhoneme && (m_ctrlArtAmp & CONTROL_MASK) == 0)
{ {
// Always set SSI263's D7 pin regardless of SSI263 mode (DR1:0), including MODE_IRQ_DISABLED if (m_currentMode.enableInts)
m_currentMode |= 1; // Set SSI263's D7 pin
if ((m_currentMode & DURATION_MODE_MASK) != MODE_IRQ_DISABLED)
{ {
if (m_cardMode == PH_Mockingboard) if (m_cardMode == PH_Mockingboard)
{ {
if ((GetPCR(m_device) & 1) == 0) // CA1 Latch/Input = 0 (Negative active edge) if (m_currentMode.D7 == 0)
UpdateIFR(m_device, 0, SY6522::IxR_SSI263); {
if (GetPCR(m_device) == 0x0C) // CA2 Control = b#110 (Low output) // 6522's PCR = 0x0C (all SSI263 speech routine use this value, but 0x00 will do equally as well!)
m_currentMode &= ~1; // Clear SSI263's D7 pin (cleared by 6522's PCR CA1/CA2 handshake) // . b3:1 CA2 Control = b#110 (Low output) - not connected
// . b0 CA1 Latch/Input = 0 (Negative active edge) - input from SSI263's A/!R
// NB. Don't set CTL=1, as Mockingboard(SMS) speech doesn't work (it sets MODE_IRQ_DISABLED mode during ISR) if ((GetPCR(m_device) & 1) == 0) // Level change from SSI263's A/!R, latch this as an interrupt
//pMB->SpeechChip.CtrlArtAmp |= CONTROL_MASK; // 6522's CA2 sets Power Down mode (pin 18), which sets Control bit UpdateIFR(m_device, 0, SY6522::IxR_SSI263);
}
} }
else if (m_cardMode == 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) else if (m_cardMode == PH_Phasor)
{ {
// Phasor (in native mode): SSI263 IRQ (A/!R) pin is connected directly to the 6502's IRQ
// . And Mockingboard mode: A/!R is connected to the 6522's CA1
CpuIrqAssert(IS_SPEECH); CpuIrqAssert(IS_SPEECH);
} }
else else
{ {
_ASSERT(0); _ASSERT(m_cardMode == PH_EchoPlus);
// SSI263 not visible from Echo+ mode, but still continues to operate
} }
} }
// Always set SSI263's D7 pin regardless of SSI263 mode (DR1:0), including when SSI263 ints are disabled (via MODE_IRQ_DISABLED)
// NB. Don't set D7 when in power-down / standby mode.
m_currentMode.D7 = 1;
} }
// //
@ -765,15 +803,34 @@ void SSI263::SetSpeechIRQ(void)
if (m_isVotraxPhoneme && GetPCR(m_device) == 0xB0) if (m_isVotraxPhoneme && GetPCR(m_device) == 0xB0)
{ {
// !A/R: Time-out of old phoneme (signal goes from low to high) // !A/R: Time-out of old phoneme (signal goes from low to high)
UpdateIFR(m_device, 0, SY6522::IxR_VOTRAX); UpdateIFR(m_device, 0, SY6522::IxR_VOTRAX);
m_isVotraxPhoneme = false;
} }
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void SSI263::SetCardMode(PHASOR_MODE mode)
{
const PHASOR_MODE oldCardMode = m_cardMode;
m_cardMode = mode;
if (oldCardMode == m_cardMode)
return;
// mode change
if (m_currentMode.D7 == 1)
{
m_currentMode.D7 = 0; // So that \PH_Mockingboard\ path sets IFR. Post: D7=1
SetSpeechIRQ();
}
if (m_cardMode != PH_Phasor)
CpuIrqDeassert(IS_SPEECH);
}
//-----------------------------------------------------------------------------
bool SSI263::DSInit(void) bool SSI263::DSInit(void)
{ {
// //
@ -810,10 +867,27 @@ void SSI263::DSUninit(void)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void SSI263::Reset(void) // SSI263 phoneme continues to play after CTRL+RESET (tested on real h/w)
// Votrax phoneme continues to play after CTRL+RESET (tested on MAME 0.262)
void SSI263::Reset(const bool powerCycle, const bool isPhasorCard)
{ {
if (!powerCycle)
{
if (isPhasorCard)
{
// Empirically observed it does CTL H->L to enable ints (and set the device mode?) (GH#175)
// NB. CTRL+RESET doesn't clear m_ctrlArtAmp.CTL (ie. if the device is in power-down/standby mode then ignore RST)
// Speculate that there's a bug in the SSI263 and that RST should put the device into power-down/standby mode (ie. silence the device)
// TODO: Stick a 'scope on !PD/!RST pin 18 to see what the Phasor h/w does.
if ((m_ctrlArtAmp & CONTROL_MASK) == 0)
SetDeviceModeAndInts();
}
return;
}
Stop(); Stop();
ResetState(); ResetState(powerCycle);
CpuIrqDeassert(IS_SPEECH); CpuIrqDeassert(IS_SPEECH);
} }
@ -883,7 +957,7 @@ void SSI263::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_REG_RATE_INF, m_rateInflection); yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_REG_RATE_INF, m_rateInflection);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_REG_CTRL_ART_AMP, m_ctrlArtAmp); yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_REG_CTRL_ART_AMP, m_ctrlArtAmp);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_REG_FILTER_FREQ, m_filterFreq); yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_REG_FILTER_FREQ, m_filterFreq);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_CURRENT_MODE, m_currentMode); yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_CURRENT_MODE, m_currentMode.mode);
yamlSaveHelper.SaveBool(SS_YAML_KEY_SSI263_ACTIVE_PHONEME, IsPhonemeActive()); yamlSaveHelper.SaveBool(SS_YAML_KEY_SSI263_ACTIVE_PHONEME, IsPhonemeActive());
} }
@ -897,10 +971,23 @@ void SSI263::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT
m_rateInflection = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_REG_RATE_INF); m_rateInflection = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_REG_RATE_INF);
m_ctrlArtAmp = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_REG_CTRL_ART_AMP); m_ctrlArtAmp = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_REG_CTRL_ART_AMP);
m_filterFreq = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_REG_FILTER_FREQ); m_filterFreq = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_REG_FILTER_FREQ);
m_currentMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_CURRENT_MODE); m_currentMode.mode = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_CURRENT_MODE);
bool activePhoneme = (version >= 7) ? yamlLoadHelper.LoadBool(SS_YAML_KEY_SSI263_ACTIVE_PHONEME) : false; bool activePhoneme = (version >= 7) ? yamlLoadHelper.LoadBool(SS_YAML_KEY_SSI263_ACTIVE_PHONEME) : false;
m_currentActivePhoneme = !activePhoneme ? -1 : 0x00; // Not important which phoneme, since UpdateIRQ() resets this m_currentActivePhoneme = !activePhoneme ? -1 : 0x00; // Not important which phoneme, since UpdateIRQ() resets this
if (version < 12)
{
if (m_currentMode.function == 0) // invalid function (but in older versions this was accepted)
{
m_currentMode.function = MODE_PHONEME_TRANSITIONED_INFLECTION >> DURATION_MODE_SHIFT; // Typically this is used
m_currentMode.enableInts = 0;
}
else
{
m_currentMode.enableInts = 1;
}
}
yamlLoadHelper.PopMap(); yamlLoadHelper.PopMap();
// //
@ -909,7 +996,7 @@ void SSI263::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT
SetCardMode(mode); SetCardMode(mode);
// Only need to directly assert IRQ for Phasor mode (for Mockingboard mode it's done via UpdateIFR() in parent) // Only need to directly assert IRQ for Phasor mode (for Mockingboard mode it's done via UpdateIFR() in parent)
if (m_cardMode == PH_Phasor && (m_currentMode & DURATION_MODE_MASK) != MODE_IRQ_DISABLED && (m_currentMode & 1)) if (m_cardMode == PH_Phasor && (m_ctrlArtAmp & CONTROL_MASK) == 0 && m_currentMode.enableInts && m_currentMode.D7 == 1)
CpuIrqAssert(IS_SPEECH); CpuIrqAssert(IS_SPEECH);
if (IsPhonemeActive()) if (IsPhonemeActive())
@ -917,3 +1004,30 @@ void SSI263::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT
m_lastUpdateCycle = GetLastCumulativeCycles(); m_lastUpdateCycle = GetLastCumulativeCycles();
} }
//=============================================================================
#define SS_YAML_KEY_SC01 "SC01"
// NB. No version - this is determined by the parent "Mockingboard C" or "Phasor" unit
#define SS_YAML_KEY_SC01_PHONEME "SC01 Phoneme"
#define SS_YAML_KEY_SC01_ACTIVE_PHONEME "SC01 Active Phoneme"
void SSI263::SC01_SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
{
YamlSaveHelper::Label label(yamlSaveHelper, "%s:\n", SS_YAML_KEY_SC01);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SC01_PHONEME, m_votraxPhoneme);
yamlSaveHelper.SaveBool(SS_YAML_KEY_SC01_ACTIVE_PHONEME, m_isVotraxPhoneme);
}
void SSI263::SC01_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
{
if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_SC01))
throw std::runtime_error("Card: Expected key: " SS_YAML_KEY_SC01);
m_votraxPhoneme = yamlLoadHelper.LoadUint(SS_YAML_KEY_SC01_PHONEME);
m_isVotraxPhoneme = yamlLoadHelper.LoadBool(SS_YAML_KEY_SC01_ACTIVE_PHONEME);
yamlLoadHelper.PopMap();
}

View File

@ -11,17 +11,18 @@ public:
m_cardMode = PH_Mockingboard; m_cardMode = PH_Mockingboard;
m_pPhonemeData00 = NULL; m_pPhonemeData00 = NULL;
ResetState(); ResetState(true);
} }
~SSI263(void) ~SSI263(void)
{ {
delete [] m_pPhonemeData00; delete [] m_pPhonemeData00;
} }
void ResetState(void) void ResetState(const bool powerCycle)
{ {
m_currentActivePhoneme = -1; m_currentActivePhoneme = -1;
m_isVotraxPhoneme = false; m_isVotraxPhoneme = false;
m_votraxPhoneme = 0;
m_cyclesThisAudioFrame = 0; m_cyclesThisAudioFrame = 0;
// //
@ -34,6 +35,7 @@ public:
m_phonemeAccurateLengthRemaining = 0; m_phonemeAccurateLengthRemaining = 0;
m_phonemePlaybackAndDebugger = false; m_phonemePlaybackAndDebugger = false;
m_phonemeCompleteByFullSpeed = false; m_phonemeCompleteByFullSpeed = false;
m_phonemeLeadoutLength = 0;
// //
@ -45,13 +47,19 @@ public:
// //
m_durationPhoneme = 0; // After a chip power-on, if the first thing done is CTL=0, then empirically it can be observed that:
// . enableInts = 1 (mostly an SSI263 interrupt occurs, but not always)
// . DR1:0 != b#00 (since enableInts is set to 1, except when no interrupt => DR1:0 == b#00)
m_durationPhoneme = MODE_PHONEME_TRANSITIONED_INFLECTION; // Typical function & phoneme=$00
m_inflection = 0; m_inflection = 0;
m_rateInflection = 0; m_rateInflection = 0;
m_ctrlArtAmp = 0; m_ctrlArtAmp = powerCycle ? CONTROL_MASK : 0; // Chip power-on, so CTL=1 (power-down / standby)
m_filterFreq = 0; m_filterFreq = powerCycle ? FILTER_FREQ_SILENCE : 0; // Empirically observed at chip power-on (GH#1302)
m_currentMode = 0; m_currentMode.mode = 0;
m_currentMode.function = 0; // Set at runtime when CTL=0
m_currentMode.enableInts = 0; // Set at runtime when CTL=0
m_currentMode.D7 = 0; // Since in power-down mode (as per SSI263 datasheet)
// //
@ -60,12 +68,12 @@ public:
} }
void SetDevice(UINT device) { m_device = device; } void SetDevice(UINT device) { m_device = device; }
void SetCardMode(PHASOR_MODE mode) { m_cardMode = mode; } void SetCardMode(PHASOR_MODE mode);
bool DSInit(void); bool DSInit(void);
void DSUninit(void); void DSUninit(void);
void Reset(void); void Reset(const bool powerCycle, const bool isPhasorCard);
bool IsPhonemeActive(void) { return m_currentActivePhoneme >= 0; } bool IsPhonemeActive(void) { return m_currentActivePhoneme >= 0; }
BYTE Read(ULONG nExecutedCycles); BYTE Read(ULONG nExecutedCycles);
@ -85,12 +93,15 @@ public:
void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper); void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper);
void LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT version); void LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT version);
void SC01_SaveSnapshot(YamlSaveHelper& yamlSaveHelper);
void SC01_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version);
private: private:
void Play(unsigned int nPhoneme); void Play(unsigned int nPhoneme);
void Stop(void); void Stop(void);
void UpdateIRQ(void); void UpdateIRQ(void);
void UpdateAccurateLength(void); void UpdateAccurateLength(void);
void SetDeviceModeAndInts(void);
UINT64 GetLastCumulativeCycles(void); UINT64 GetLastCumulativeCycles(void);
void UpdateIFR(BYTE nDevice, BYTE clr_mask, BYTE set_mask); void UpdateIFR(BYTE nDevice, BYTE clr_mask, BYTE set_mask);
@ -105,13 +116,37 @@ private:
// //
// Duration/Phonome
static const BYTE DURATION_MODE_MASK = 0xC0;
static const BYTE DURATION_MODE_SHIFT = 6;
static const BYTE PHONEME_MASK = 0x3F;
static const BYTE MODE_PHONEME_TRANSITIONED_INFLECTION = 0xC0;
static const BYTE MODE_PHONEME_IMMEDIATE_INFLECTION = 0x80;
static const BYTE MODE_FRAME_IMMEDIATE_INFLECTION = 0x40;
static const BYTE MODE_IRQ_DISABLED = 0x00; // disable interrupts, but retains one of the 3 modes
// Rate/Inflection
static const BYTE RATE_MASK = 0xF0;
static const BYTE INFLECTION_MASK_H = 0x08; // I11
static const BYTE INFLECTION_MASK_L = 0x07; // I2..I0
// Ctrl/Art/Amp
static const BYTE ARTICULATION_MASK = 0x70;
static const BYTE AMPLITUDE_MASK = 0x0F;
static const BYTE CONTROL_MASK = 0x80;
// Filter frequency range
static const BYTE FILTER_FREQ_SILENCE = 0xFF;
UINT m_slot; UINT m_slot;
BYTE m_device; // SSI263 device# which is generating phoneme-complete IRQ (and only required whilst Mockingboard isn't a class) BYTE m_device; // SSI263 device# which is generating phoneme-complete IRQ (and only required whilst Mockingboard isn't a class)
PHASOR_MODE m_cardMode; PHASOR_MODE m_cardMode;
short* m_pPhonemeData00; short* m_pPhonemeData00;
int m_currentActivePhoneme; int m_currentActivePhoneme; // -1 (if none) or SSI263 or SC01 phoneme
bool m_isVotraxPhoneme; bool m_isVotraxPhoneme;
BYTE m_votraxPhoneme;
UINT m_cyclesThisAudioFrame; UINT m_cyclesThisAudioFrame;
// //
@ -124,6 +159,7 @@ private:
UINT m_phonemeAccurateLengthRemaining; // length in samples, decremented by cycles executed UINT m_phonemeAccurateLengthRemaining; // length in samples, decremented by cycles executed
bool m_phonemePlaybackAndDebugger; bool m_phonemePlaybackAndDebugger;
bool m_phonemeCompleteByFullSpeed; bool m_phonemeCompleteByFullSpeed;
UINT m_phonemeLeadoutLength; // length in samples, decremented after \m_phonemeLengthRemaining\ goes to zero. Delay until phoneme repeats
// //
@ -140,7 +176,17 @@ private:
BYTE m_ctrlArtAmp; BYTE m_ctrlArtAmp;
BYTE m_filterFreq; BYTE m_filterFreq;
BYTE m_currentMode; // b7:6=Mode; b0=D7 pin (for IRQ) union
{
struct
{
BYTE D7 : 1; // b0=D7 pin (for IRQ)
BYTE reserved : 4;
BYTE enableInts : 1; // b5 = enable A/!R (ie. interrupts)
BYTE function : 2; // b7:6 = function
};
BYTE mode;
} m_currentMode;
// Debug // Debug
bool m_dbgFirst; bool m_dbgFirst;

View File

@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#ifndef _MSC_VER #ifndef _MSC_VER
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h> #include <netdb.h>
#endif #endif

View File

@ -69,11 +69,12 @@ typedef int socklen_t;
#define SOCK_EWOULDBLOCK EWOULDBLOCK #define SOCK_EWOULDBLOCK EWOULDBLOCK
#define SOCK_EINPROGRESS EINPROGRESS #define SOCK_EINPROGRESS EINPROGRESS
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netinet/in.h>
#include <poll.h> #include <poll.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/types.h>
#include <errno.h> #include <errno.h>
#endif #endif

View File

@ -491,7 +491,7 @@ void GetAppleWindowTitle()
if (g_hCustomRomF8 != INVALID_HANDLE_VALUE) if (g_hCustomRomF8 != INVALID_HANDLE_VALUE)
g_pAppTitle += TEXT(" (custom rom)"); g_pAppTitle += TEXT(" (custom rom)");
else if (GetPropertySheet().GetTheFreezesF8Rom() && IS_APPLE2) else if (GetPropertySheet().GetTheFreezesF8Rom() && IsApple2PlusOrClone(GetApple2Type()))
g_pAppTitle += TEXT(" (The Freeze's non-autostart F8 rom)"); g_pAppTitle += TEXT(" (The Freeze's non-autostart F8 rom)");
switch (g_nAppMode) switch (g_nAppMode)
@ -546,7 +546,7 @@ void ResetMachineState()
// todo: consolidate CtrlReset() and ResetMachineState() // todo: consolidate CtrlReset() and ResetMachineState()
void CtrlReset() void CtrlReset()
{ {
if (!IS_APPLE2) if (IsAppleIIeOrAbove(GetApple2Type()))
{ {
// For A][ & A][+, reset doesn't reset the LC switches (UTAII:5-29) // For A][ & A][+, reset doesn't reset the LC switches (UTAII:5-29)
// TODO: What about Saturn cards? Presumably the same as the A][ & A][+ slot0 LC? // TODO: What about Saturn cards? Presumably the same as the A][ & A][+ slot0 LC?