diff --git a/AppleWinExpress2008.vcproj b/AppleWinExpress2008.vcproj index 0fdb2a79..28abd76a 100644 --- a/AppleWinExpress2008.vcproj +++ b/AppleWinExpress2008.vcproj @@ -1091,6 +1091,14 @@ RelativePath=".\resource\Apple2_Plus.rom" > + + + + diff --git a/resource/Apple2_JPlus.rom b/resource/Apple2_JPlus.rom new file mode 100644 index 00000000..8db7de21 Binary files /dev/null and b/resource/Apple2_JPlus.rom differ diff --git a/resource/Apple2_JPlus_Video.rom b/resource/Apple2_JPlus_Video.rom new file mode 100644 index 00000000..cd419afe Binary files /dev/null and b/resource/Apple2_JPlus_Video.rom differ diff --git a/resource/Applewin.rc b/resource/Applewin.rc index 8c3c0e25..8422331a 100644 --- a/resource/Applewin.rc +++ b/resource/Applewin.rc @@ -325,6 +325,7 @@ IDR_TKCLOCK_FW FIRMWARE "TKClock.rom" IDR_APPLE2_ROM ROM "Apple2.rom" IDR_APPLE2_PLUS_ROM ROM "Apple2_Plus.rom" +IDR_APPLE2_JPLUS_ROM ROM "Apple2_JPlus.rom" IDR_APPLE2E_ROM ROM "Apple2e.rom" IDR_APPLE2E_ENHANCED_ROM ROM "Apple2e_Enhanced.rom" IDR_PRAVETS_82_ROM ROM "Pravets82.rom" @@ -333,6 +334,13 @@ IDR_PRAVETS_8C_ROM ROM "Pravets8C.rom" IDR_TK3000_2E_ROM ROM "TK3000e.rom" IDR_FREEZES_F8_ROM ROM "FREEZES_NON-AUTOSTART_F8_ROM.rom" +///////////////////////////////////////////////////////////////////////////// +// +// VIDEO ROM +// + +IDR_APPLE2_JPLUS_VIDEO_ROM ROM "Apple2_JPlus_Video.rom" + ///////////////////////////////////////////////////////////////////////////// // // Menu diff --git a/resource/resource.h b/resource/resource.h index ce0bec36..932ac25d 100644 --- a/resource/resource.h +++ b/resource/resource.h @@ -49,6 +49,8 @@ #define IDR_TKCLOCK_FW 148 #define IDR_DISK2_13SECTOR_FW 149 #define IDR_DISK2_16SECTOR_FW 150 +#define IDR_APPLE2_JPLUS_ROM 151 +#define IDR_APPLE2_JPLUS_VIDEO_ROM 152 #define IDC_KEYB_BUFFER_ENABLE 1005 #define IDC_SAVESTATE 1006 #define IDC_SAVESTATE_ON_EXIT 1007 diff --git a/source/Applewin.cpp b/source/Applewin.cpp index 9863081e..74598476 100644 --- a/source/Applewin.cpp +++ b/source/Applewin.cpp @@ -1692,6 +1692,8 @@ static bool ProcessCmdLine(LPSTR lpCmdLine) g_cmdLine.model = A2TYPE_APPLE2; else if (strcmp(lpCmdLine, "apple2p") == 0) g_cmdLine.model = A2TYPE_APPLE2PLUS; + else if (strcmp(lpCmdLine, "apple2jp") == 0) + g_cmdLine.model = A2TYPE_APPLE2JPLUS; else if (strcmp(lpCmdLine, "apple2e") == 0) g_cmdLine.model = A2TYPE_APPLE2E; else if (strcmp(lpCmdLine, "apple2ee") == 0) diff --git a/source/Common.h b/source/Common.h index dc6947e9..6a9b5cc6 100644 --- a/source/Common.h +++ b/source/Common.h @@ -48,6 +48,7 @@ enum AppMode_e // TODO: Move to StringTable.h #define TITLE_APPLE_2 TEXT("Apple ][ Emulator") #define TITLE_APPLE_2_PLUS TEXT("Apple ][+ Emulator") +#define TITLE_APPLE_2_JPLUS TEXT("Apple ][ J-Plus Emulator") #define TITLE_APPLE_2E TEXT("Apple //e Emulator") #define TITLE_APPLE_2E_ENHANCED TEXT("Enhanced Apple //e Emulator") #define TITLE_APPLE_2C TEXT("Apple //e Emulator") @@ -168,6 +169,7 @@ enum eIRQSRC {IS_6522=0, IS_SPEECH, IS_SSC, IS_MOUSE}; enum eApple2Type { A2TYPE_APPLE2=0, A2TYPE_APPLE2PLUS, + A2TYPE_APPLE2JPLUS, A2TYPE_APPLE2E=APPLE2E_MASK, A2TYPE_APPLE2EENHANCED, A2TYPE_UNDEFINED, @@ -196,17 +198,22 @@ inline bool IsApple2Original(eApple2Type type) // Apple ][ return type == A2TYPE_APPLE2; } -inline bool IsApple2Plus(eApple2Type type) // Apple ][,][+ +inline bool IsApple2Plus(eApple2Type type) // Apple ][,][+,][J-Plus { return ((type & (APPLE2E_MASK|APPLE2C_MASK)) == 0) && !(type & APPLECLONE_MASK); } +inline bool IsApple2JPlus(eApple2Type type) // Apple ][J-Plus +{ + return type == A2TYPE_APPLE2JPLUS; +} + inline bool IsClone(eApple2Type type) { return (type & APPLECLONE_MASK) != 0; } -inline bool IsApple2PlusOrClone(eApple2Type type) // Apple ][,][+ or clone ][,][+ +inline bool IsApple2PlusOrClone(eApple2Type type) // Apple ][,][+,][J-Plus or clone ][,][+ { return (type & (APPLE2E_MASK|APPLE2C_MASK)) == 0; } diff --git a/source/Configuration/PageConfig.cpp b/source/Configuration/PageConfig.cpp index ffe3846e..ddba9ce3 100644 --- a/source/Configuration/PageConfig.cpp +++ b/source/Configuration/PageConfig.cpp @@ -34,10 +34,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA CPageConfig* CPageConfig::ms_this = 0; // reinit'd in ctor -enum APPLEIICHOICE {MENUITEM_IIORIGINAL, MENUITEM_IIPLUS, MENUITEM_IIE, MENUITEM_ENHANCEDIIE, MENUITEM_CLONE}; +enum APPLEIICHOICE {MENUITEM_IIORIGINAL, MENUITEM_IIPLUS, MENUITEM_IIJPLUS, MENUITEM_IIE, MENUITEM_ENHANCEDIIE, MENUITEM_CLONE}; const TCHAR CPageConfig::m_ComputerChoices[] = TEXT("Apple ][ (Original)\0") TEXT("Apple ][+\0") + TEXT("Apple ][ J-Plus\0") TEXT("Apple //e\0") TEXT("Enhanced Apple //e\0") TEXT("Clone\0"); @@ -182,6 +183,7 @@ BOOL CPageConfig::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPARAM { case A2TYPE_APPLE2: nCurrentChoice = MENUITEM_IIORIGINAL; break; case A2TYPE_APPLE2PLUS: nCurrentChoice = MENUITEM_IIPLUS; break; + case A2TYPE_APPLE2JPLUS: nCurrentChoice = MENUITEM_IIJPLUS; break; case A2TYPE_APPLE2E: nCurrentChoice = MENUITEM_IIE; break; case A2TYPE_APPLE2EENHANCED:nCurrentChoice = MENUITEM_ENHANCEDIIE; break; case A2TYPE_PRAVETS82: nCurrentChoice = MENUITEM_CLONE; break; @@ -380,6 +382,7 @@ eApple2Type CPageConfig::GetApple2Type(DWORD NewMenuItem) { case MENUITEM_IIORIGINAL: return A2TYPE_APPLE2; case MENUITEM_IIPLUS: return A2TYPE_APPLE2PLUS; + case MENUITEM_IIJPLUS: return A2TYPE_APPLE2JPLUS; case MENUITEM_IIE: return A2TYPE_APPLE2E; case MENUITEM_ENHANCEDIIE: return A2TYPE_APPLE2EENHANCED; case MENUITEM_CLONE: return A2TYPE_CLONE; diff --git a/source/Frame.cpp b/source/Frame.cpp index b8e9caf6..40fdd55c 100644 --- a/source/Frame.cpp +++ b/source/Frame.cpp @@ -256,6 +256,7 @@ static void GetAppleWindowTitle() default: case A2TYPE_APPLE2: g_pAppTitle = TITLE_APPLE_2 ; break; case A2TYPE_APPLE2PLUS: g_pAppTitle = TITLE_APPLE_2_PLUS ; break; + case A2TYPE_APPLE2JPLUS: g_pAppTitle = TITLE_APPLE_2_JPLUS ; break; case A2TYPE_APPLE2E: g_pAppTitle = TITLE_APPLE_2E ; break; case A2TYPE_APPLE2EENHANCED: g_pAppTitle = TITLE_APPLE_2E_ENHANCED; break; case A2TYPE_PRAVETS82: g_pAppTitle = TITLE_PRAVETS_82 ; break; diff --git a/source/Memory.cpp b/source/Memory.cpp index 49b545b7..b65dd0e8 100644 --- a/source/Memory.cpp +++ b/source/Memory.cpp @@ -485,8 +485,11 @@ static BYTE __stdcall IORead_C05x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG case 0xC: return IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); case 0xD: return IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); case 0xE: // fall through... - case 0xF: return (!SW_IOUDIS) ? VideoSetMode(pc, addr, bWrite, d, nExecutedCycles) - : IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); + case 0xF: if (IsApple2PlusOrClone(GetApple2Type())) + IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); + else + return (!SW_IOUDIS) ? VideoSetMode(pc, addr, bWrite, d, nExecutedCycles) + : IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); } return 0; @@ -511,8 +514,11 @@ static BYTE __stdcall IOWrite_C05x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULON case 0xC: return IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); case 0xD: return IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); case 0xE: // fall through... - case 0xF: return (!SW_IOUDIS) ? VideoSetMode(pc, addr, bWrite, d, nExecutedCycles) - : IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); + case 0xF: if (IsApple2PlusOrClone(GetApple2Type())) + IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); + else + return (!SW_IOUDIS) ? VideoSetMode(pc, addr, bWrite, d, nExecutedCycles) + : IO_Annunciator(pc, addr, bWrite, d, nExecutedCycles); } return 0; @@ -690,6 +696,9 @@ BYTE __stdcall IO_Annunciator(WORD programcounter, WORD address, BYTE write, BYT JoyportControl(address & 0x3); // AN0 and AN1 control } + if (address >= 0xC05C && address <= 0xC05D && IsApple2JPlus(GetApple2Type())) + NTSC_VideoInitAppleType(); // AN2 switches between Katakana & ASCII video rom chars (GH#773) + if (!write) return MemReadFloatingBus(nExecutedCycles); else @@ -1506,6 +1515,7 @@ void MemInitializeROM(void) { case A2TYPE_APPLE2: hResInfo = FindResource(NULL, MAKEINTRESOURCE(IDR_APPLE2_ROM ), "ROM"); ROM_SIZE = Apple2RomSize ; break; case A2TYPE_APPLE2PLUS: hResInfo = FindResource(NULL, MAKEINTRESOURCE(IDR_APPLE2_PLUS_ROM ), "ROM"); ROM_SIZE = Apple2RomSize ; break; + case A2TYPE_APPLE2JPLUS: hResInfo = FindResource(NULL, MAKEINTRESOURCE(IDR_APPLE2_JPLUS_ROM ), "ROM"); ROM_SIZE = Apple2RomSize ; break; case A2TYPE_APPLE2E: hResInfo = FindResource(NULL, MAKEINTRESOURCE(IDR_APPLE2E_ROM ), "ROM"); ROM_SIZE = Apple2eRomSize; break; case A2TYPE_APPLE2EENHANCED:hResInfo = FindResource(NULL, MAKEINTRESOURCE(IDR_APPLE2E_ENHANCED_ROM), "ROM"); ROM_SIZE = Apple2eRomSize; break; case A2TYPE_PRAVETS82: hResInfo = FindResource(NULL, MAKEINTRESOURCE(IDR_PRAVETS_82_ROM ), "ROM"); ROM_SIZE = Apple2RomSize ; break; @@ -1521,6 +1531,7 @@ void MemInitializeROM(void) { case A2TYPE_APPLE2: _tcscpy(sRomFileName, TEXT("APPLE2.ROM" )); break; case A2TYPE_APPLE2PLUS: _tcscpy(sRomFileName, TEXT("APPLE2_PLUS.ROM" )); break; + case A2TYPE_APPLE2JPLUS: _tcscpy(sRomFileName, TEXT("APPLE2_JPLUS.ROM" )); break; case A2TYPE_APPLE2E: _tcscpy(sRomFileName, TEXT("APPLE2E.ROM" )); break; case A2TYPE_APPLE2EENHANCED:_tcscpy(sRomFileName, TEXT("APPLE2E_ENHANCED.ROM")); break; case A2TYPE_PRAVETS82: _tcscpy(sRomFileName, TEXT("PRAVETS82.ROM" )); break; diff --git a/source/NTSC.cpp b/source/NTSC.cpp index 4c49426d..fb5e077a 100644 --- a/source/NTSC.cpp +++ b/source/NTSC.cpp @@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "Applewin.h" #include "CPU.h" // CpuGetCyclesThisVideoFrame() #include "Frame.h" - #include "Memory.h" // MemGetMainPtr() MemGetAuxPtr() + #include "Memory.h" // MemGetMainPtr(), MemGetAuxPtr(), MemGetAnnunciator() #include "Video.h" // g_pFramebufferbits #include "RGBMonitor.h" @@ -450,13 +450,14 @@ static void set_csbits() { case A2TYPE_APPLE2: csbits = &csbits_a2[0]; g_nVideoCharSet = 0; break; case A2TYPE_APPLE2PLUS: csbits = &csbits_a2[0]; g_nVideoCharSet = 0; break; + case A2TYPE_APPLE2JPLUS: csbits = &csbits_a2j[MemGetAnnunciator(2) ? 1 : 0]; g_nVideoCharSet = 0; break; case A2TYPE_APPLE2E: csbits = Get2e_csbits(); break; case A2TYPE_APPLE2EENHANCED:csbits = Get2e_csbits(); break; case A2TYPE_PRAVETS82: csbits = &csbits_pravets82[0]; g_nVideoCharSet = 0; break; // Apple ][ clone case A2TYPE_PRAVETS8M: csbits = &csbits_pravets8M[0]; g_nVideoCharSet = 0; break; // Apple ][ clone case A2TYPE_PRAVETS8A: csbits = &csbits_pravets8C[0]; break; // Apple //e clone case A2TYPE_TK30002E: csbits = &csbits_enhanced2e[0]; break; // Enhanced Apple //e clone - default: csbits = &csbits_enhanced2e[0]; break; + default: _ASSERT(0); csbits = &csbits_enhanced2e[0]; break; } } diff --git a/source/NTSC_CharSet.cpp b/source/NTSC_CharSet.cpp index 628f0596..ac3051be 100644 --- a/source/NTSC_CharSet.cpp +++ b/source/NTSC_CharSet.cpp @@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "StdAfx.h" #include "Applewin.h" +#include "../resource/resource.h" #include "Video.h" #include "NTSC_CharSet.h" @@ -29,6 +30,7 @@ unsigned char csbits_enhanced2e[2][256][8]; // Enhanced //e (2732 4K video ROM) static unsigned char csbits_2e_pal[2][256][8]; // PAL Original or Enhanced //e (2764 8K video ROM - low 4K) via rocker switch under keyboard unsigned char csbits_2e[2][256][8]; // Original //e (no mousetext) unsigned char csbits_a2[1][256][8]; // ][ and ][+ +unsigned char csbits_a2j[2][256][8]; // ][J-Plus unsigned char csbits_pravets82[1][256][8]; // Pravets 82 unsigned char csbits_pravets8M[1][256][8]; // Pravets 8M unsigned char csbits_pravets8C[2][256][8]; // Pravets 8A & 8C @@ -105,7 +107,7 @@ static void get_csbits(csbits_t csbits, const char* resourceName, const UINT cy0 // FLASH toggles every 16 VBLs, so alternates between selecting NORMAL control/special and INVERSE control/special // -void userVideoRom4K(csbits_t csbits, const BYTE* pVideoRom) +static void userVideoRom4K(csbits_t csbits, const BYTE* pVideoRom) { int RA = 0; // rom address int i = 0; @@ -154,7 +156,7 @@ void userVideoRom4K(csbits_t csbits, const BYTE* pVideoRom) } } -void userVideoRomForIIe(void) +static void userVideoRomForIIe(void) { const BYTE* pVideoRom; UINT size = GetVideoRom(pVideoRom); // 2K or 4K or 8K @@ -177,20 +179,36 @@ void userVideoRomForIIe(void) //------------------------------------- -void userVideoRom2K(csbits_t csbits, const BYTE* pVideoRom) -{ - int RA = 0; // rom address +static void userVideoRom2K(csbits_t csbits, const BYTE* pVideoRom, const int AN2=0); - for (int i=0; i<256; i++, RA+=8) +static void userVideoRom2K(csbits_t csbits, const BYTE* pVideoRom, const int AN2/*=0*/) +{ + const bool isApple2JPlus = IsApple2JPlus(GetApple2Type()); // GH#773 + + for (int i=0; i<256; i++) { + int RA = i*8; // rom address + + if (isApple2JPlus) + { + // AN2=0: $00-3F, $00-3F; $80-BF, $80-BF => KKAA (Repeat Katakana) + // AN2=1: $40-7F, $40-7F; $C0-FF, $C0-FF => AAAA (Repeat ASCII) + RA &= ~(1<<(6+3)); + RA |= (AN2<<(6+3)); // AN2 controls A9 (UTAII 8-12, Fig 8.7) + } + for (int y=0; y<8; y++) { BYTE n = pVideoRom[RA+y]; // UTAII:8-30 "Bit 7 of your EPROM fonts will control flashing in the lower 1024 bytes of the EPROM" // UTAII:8-31 "If you leave O7 (EPROM Output7) reset in these patterns, the resulting characters will be inversions..." - if (!(n & 0x80) && RA < 1024) - n = n ^ 0x7f; + // Apple II J-Plus: simplest logic is just invert if reading low 1K of video ROM + if (RA < 1024) + { + if (!(n & 0x80) || isApple2JPlus) + n = n ^ 0x7f; + } // UTAII:8-30 "TEXT ROM pattern is ... reversed" BYTE d = 0; @@ -202,7 +220,7 @@ void userVideoRom2K(csbits_t csbits, const BYTE* pVideoRom) } } -void userVideoRomForIIPlus(void) +static void userVideoRomForIIPlus(void) { const BYTE* pVideoRom; UINT size = GetVideoRom(pVideoRom); // 2K or 4K or 8K @@ -214,6 +232,30 @@ void userVideoRomForIIPlus(void) //------------------------------------- +static void VideoRomForIIJPlus(void) +{ + HRSRC hResInfo = FindResource(NULL, MAKEINTRESOURCE(IDR_APPLE2_JPLUS_VIDEO_ROM), "ROM"); + if (hResInfo == NULL) + return; + + DWORD dwResSize = SizeofResource(NULL, hResInfo); + if(dwResSize != kVideoRomSize2K) + return; + + HGLOBAL hResData = LoadResource(NULL, hResInfo); + if(hResData == NULL) + return; + + BYTE* pVideoRom = (BYTE*) LockResource(hResData); // NB. Don't need to unlock resource + if (pVideoRom == NULL) + return; + + userVideoRom2K(&csbits_a2j[0], pVideoRom, 0); + userVideoRom2K(&csbits_a2j[1], pVideoRom, 1); +} + +//------------------------------------- + void make_csbits(void) { get_csbits(&csbits_enhanced2e[0], TEXT("CHARSET40"), 0); // Enhanced //e: Alt char set off @@ -228,6 +270,8 @@ void make_csbits(void) memcpy(csbits_2e, csbits_enhanced2e, sizeof(csbits_enhanced2e)); memcpy(&csbits_2e[1][64], &csbits_2e[0][64], 32*8); + VideoRomForIIJPlus(); + // Try to use any user-provided video ROM for Original/Enhanced //e userVideoRomForIIe(); diff --git a/source/NTSC_CharSet.h b/source/NTSC_CharSet.h index 6a5c86cc..e3d1d7fd 100644 --- a/source/NTSC_CharSet.h +++ b/source/NTSC_CharSet.h @@ -4,6 +4,7 @@ typedef unsigned char (*csbits_t)[256][8]; extern unsigned char csbits_enhanced2e[2][256][8]; // Enhanced //e (2732 4K video ROM) extern unsigned char csbits_a2[1][256][8]; // ][ and ][+ +extern unsigned char csbits_a2j[2][256][8]; // ][J-Plus extern unsigned char csbits_pravets82[1][256][8]; // Pravets 82 extern unsigned char csbits_pravets8M[1][256][8]; // Pravets 8M extern unsigned char csbits_pravets8C[2][256][8]; // Pravets 8A & 8C diff --git a/source/SaveState.cpp b/source/SaveState.cpp index 33c57d2c..7d1de8a2 100644 --- a/source/SaveState.cpp +++ b/source/SaveState.cpp @@ -138,6 +138,7 @@ static std::string GetSnapshotUnitSlotsName(void) #define SS_YAML_VALUE_APPLE2 "Apple][" #define SS_YAML_VALUE_APPLE2PLUS "Apple][+" +#define SS_YAML_VALUE_APPLE2JPLUS "Apple][ J-Plus" #define SS_YAML_VALUE_APPLE2E "Apple//e" #define SS_YAML_VALUE_APPLE2EENHANCED "Enhanced Apple//e" #define SS_YAML_VALUE_APPLE2C "Apple2c" @@ -150,6 +151,7 @@ static eApple2Type ParseApple2Type(std::string type) { if (type == SS_YAML_VALUE_APPLE2) return A2TYPE_APPLE2; else if (type == SS_YAML_VALUE_APPLE2PLUS) return A2TYPE_APPLE2PLUS; + else if (type == SS_YAML_VALUE_APPLE2JPLUS) return A2TYPE_APPLE2JPLUS; else if (type == SS_YAML_VALUE_APPLE2E) return A2TYPE_APPLE2E; else if (type == SS_YAML_VALUE_APPLE2EENHANCED) return A2TYPE_APPLE2EENHANCED; else if (type == SS_YAML_VALUE_APPLE2C) return A2TYPE_APPLE2C; @@ -167,6 +169,7 @@ static std::string GetApple2TypeAsString(void) { case A2TYPE_APPLE2: return SS_YAML_VALUE_APPLE2; case A2TYPE_APPLE2PLUS: return SS_YAML_VALUE_APPLE2PLUS; + case A2TYPE_APPLE2JPLUS: return SS_YAML_VALUE_APPLE2JPLUS; case A2TYPE_APPLE2E: return SS_YAML_VALUE_APPLE2E; case A2TYPE_APPLE2EENHANCED:return SS_YAML_VALUE_APPLE2EENHANCED; case A2TYPE_APPLE2C: return SS_YAML_VALUE_APPLE2C;