diff --git a/AppleWin-VS2022.vcxproj b/AppleWin-VS2022.vcxproj index a853a5c8..6e9860c8 100644 --- a/AppleWin-VS2022.vcxproj +++ b/AppleWin-VS2022.vcxproj @@ -303,6 +303,8 @@ + + @@ -367,6 +369,7 @@ + diff --git a/AppleWin-VS2022.vcxproj.filters b/AppleWin-VS2022.vcxproj.filters index ec1d8cbc..1b76dbbc 100644 --- a/AppleWin-VS2022.vcxproj.filters +++ b/AppleWin-VS2022.vcxproj.filters @@ -734,6 +734,9 @@ Resource Files + + Resource Files + @@ -799,6 +802,12 @@ Resource Files + + Resource Files + + + Resource Files + Resource Files diff --git a/resource/Applewin.rc b/resource/Applewin.rc index fe6b4009..08ce3748 100644 --- a/resource/Applewin.rc +++ b/resource/Applewin.rc @@ -56,6 +56,7 @@ RUN_BUTTON BITMAP "RUN.BMP" RUNP_BUTTON BITMAP "RUNP.BMP" RUN3000E_BUTTON BITMAP "RUN3000E.BMP" RUNBASE64A_BUTTON BITMAP "RUNBASE64A.BMP" +RUNIMC2001_BUTTON BITMAP "RUNIMC2001.BMP" DEBUG_BUTTON BITMAP "DEBUG.BMP" DRIVE1_BUTTON BITMAP "DRIVE1.BMP" DRIVE2_BUTTON BITMAP "DRIVE2.BMP" @@ -354,6 +355,7 @@ IDR_PRAVETS_8M_ROM ROM "Pravets8M.rom" IDR_PRAVETS_8C_ROM ROM "Pravets8C.rom" IDR_TK3000_2E_ROM ROM "TK3000e.rom" IDR_BASE_64A_ROM ROM "Base64A.rom" +IDR_IMC2001_ROM ROM "IMC2001.rom" IDR_FREEZES_F8_ROM ROM "FREEZES_NON-AUTOSTART_F8_ROM.rom" ///////////////////////////////////////////////////////////////////////////// @@ -365,6 +367,7 @@ IDR_APPLE2_VIDEO_ROM ROM "Apple2_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_IMC2001_VIDEO_ROM ROM "IMC2001-Video.rom" ///////////////////////////////////////////////////////////////////////////// // diff --git a/resource/IMC2001-Video.rom b/resource/IMC2001-Video.rom new file mode 100644 index 00000000..6c6fed23 Binary files /dev/null and b/resource/IMC2001-Video.rom differ diff --git a/resource/IMC2001.rom b/resource/IMC2001.rom new file mode 100644 index 00000000..ca1e1696 Binary files /dev/null and b/resource/IMC2001.rom differ diff --git a/resource/RUNIMC2001.BMP b/resource/RUNIMC2001.BMP new file mode 100644 index 00000000..a4e3e949 Binary files /dev/null and b/resource/RUNIMC2001.BMP differ diff --git a/resource/resource.h b/resource/resource.h index a6ef6589..536ecdab 100644 --- a/resource/resource.h +++ b/resource/resource.h @@ -56,6 +56,8 @@ #define IDR_BASE64A_VIDEO_ROM 154 #define IDR_HDDRVR_V2_FW 155 #define IDR_HDC_SMARTPORT_FW 156 +#define IDR_IMC2001_ROM 157 +#define IDR_IMC2001_VIDEO_ROM 158 #define IDC_KEYB_BUFFER_ENABLE 1005 #define IDC_SAVESTATE 1006 #define IDC_SAVESTATE_ON_EXIT 1007 diff --git a/source/Common.h b/source/Common.h index 1e3b6fba..0e67d9e6 100644 --- a/source/Common.h +++ b/source/Common.h @@ -57,6 +57,7 @@ enum AppMode_e #define TITLE_PRAVETS_8A TEXT("Pravets 8A Emulator") #define TITLE_TK3000_2E TEXT("TK3000 //e Emulator") #define TITLE_BASE64A TEXT("Base64A Emulator") +#define TITLE_IMC2001 TEXT("IMC-2001 Emulator") #define TITLE_PAUSED TEXT("* PAUSED *") #define TITLE_STEPPING TEXT("Stepping") @@ -181,6 +182,7 @@ enum eApple2Type { A2TYPE_PRAVETS8M, // Apple ][ clone A2TYPE_PRAVETS82, // Apple ][ clone A2TYPE_BASE64A, // Apple ][ clone + A2TYPE_IMC2001, // Apple ][ clone // (Gap for more Apple ][ clones) A2TYPE_CLONE_A2_MAX, @@ -250,6 +252,11 @@ inline bool IsPravets(eApple2Type type) return type == A2TYPE_PRAVETS8M || type == A2TYPE_PRAVETS82 || type == A2TYPE_PRAVETS8A; } +inline bool IsIMC2001(eApple2Type type) +{ + return type == A2TYPE_IMC2001; +} + enum eBUTTON {BUTTON0=0, BUTTON1}; enum eBUTTONSTATE {BUTTON_UP=0, BUTTON_DOWN}; diff --git a/source/Configuration/PageAdvanced.cpp b/source/Configuration/PageAdvanced.cpp index 5a0c3372..01a33d48 100644 --- a/source/Configuration/PageAdvanced.cpp +++ b/source/Configuration/PageAdvanced.cpp @@ -36,13 +36,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA CPageAdvanced* CPageAdvanced::ms_this = 0; // reinit'd in ctor -enum CLONECHOICE {MENUITEM_CLONEMIN, MENUITEM_PRAVETS82=MENUITEM_CLONEMIN, MENUITEM_PRAVETS8M, MENUITEM_PRAVETS8A, MENUITEM_TK30002E, MENUITEM_BASE64A, MENUITEM_CLONEMAX}; +enum CLONECHOICE {MENUITEM_CLONEMIN, MENUITEM_PRAVETS82=MENUITEM_CLONEMIN, MENUITEM_PRAVETS8M, MENUITEM_PRAVETS8A, MENUITEM_TK30002E, MENUITEM_BASE64A, MENUITEM_IMC2001, MENUITEM_CLONEMAX}; const TCHAR CPageAdvanced::m_CloneChoices[] = TEXT("Pravets 82\0") // Bulgarian TEXT("Pravets 8M\0") // Bulgarian TEXT("Pravets 8A\0") // Bulgarian TEXT("TK3000 //e\0") // Brazilian - TEXT("Base 64A\0"); // Taiwanese + TEXT("Base 64A\0") // Taiwanese + TEXT("IMC 2001\0"); // Taiwanese const TCHAR CPageAdvanced::m_gameIOConnectorChoices[] = "Empty\0" @@ -251,6 +252,7 @@ eApple2Type CPageAdvanced::GetCloneType(DWORD NewMenuItem) case MENUITEM_PRAVETS8A: return A2TYPE_PRAVETS8A; case MENUITEM_TK30002E: return A2TYPE_TK30002E; case MENUITEM_BASE64A: return A2TYPE_BASE64A; + case MENUITEM_IMC2001: return A2TYPE_IMC2001; default: return A2TYPE_PRAVETS82; } } @@ -279,6 +281,7 @@ int CPageAdvanced::GetCloneMenuItem(void) case A2TYPE_PRAVETS8A: nMenuItem = MENUITEM_PRAVETS8A; break; case A2TYPE_TK30002E: nMenuItem = MENUITEM_TK30002E; break; case A2TYPE_BASE64A: nMenuItem = MENUITEM_BASE64A; break; + case A2TYPE_IMC2001: nMenuItem = MENUITEM_IMC2001; break; default: // New clone needs adding here? _ASSERT(0); } diff --git a/source/Configuration/PageConfig.cpp b/source/Configuration/PageConfig.cpp index e5add898..0b8a692e 100644 --- a/source/Configuration/PageConfig.cpp +++ b/source/Configuration/PageConfig.cpp @@ -207,6 +207,7 @@ INT_PTR CPageConfig::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPA case A2TYPE_PRAVETS8A: nCurrentChoice = MENUITEM_CLONE; break; case A2TYPE_TK30002E: nCurrentChoice = MENUITEM_CLONE; break; case A2TYPE_BASE64A: nCurrentChoice = MENUITEM_CLONE; break; + case A2TYPE_IMC2001: nCurrentChoice = MENUITEM_CLONE; break; default: _ASSERT(0); break; } diff --git a/source/Keyboard.cpp b/source/Keyboard.cpp index 5250866e..8ae8378f 100644 --- a/source/Keyboard.cpp +++ b/source/Keyboard.cpp @@ -134,7 +134,7 @@ void KeybQueueKeypress (WPARAM key, Keystroke_e bASCII) return; // Initially default to non-clone behaviour: - if (IsAppleIIeOrAbove(GetApple2Type())) + if (IsAppleIIeOrAbove(GetApple2Type()) || IsIMC2001(GetApple2Type())) { if (!IS_CLONE() && key > 0x7F) // accented chars, eg. AltGr+A return; @@ -223,6 +223,14 @@ void KeybQueueKeypress (WPARAM key, Keystroke_e bASCII) if (key >= VK_LEFT && key <= VK_DELETE) { UINT model = IsCopamBase64A(GetApple2Type()) ? 2 : IS_APPLE2 ? 0 : 1; + if (IsIMC2001(GetApple2Type())) + { + // TODO: the IMC-2001 actually has black LEFT and RIGTH arrow keys which behave as + // on an Apple IIe, but it also has light arrow keys UP, DOWN, LEFT, RIGHT, which + // actually position the cursor on the screen, not sure how to emulate this. + // for now just doing the black LEFT and RIGHT keys + model = 1; + } BYTE n = asciicode[model][key - VK_LEFT]; // Convert to Apple arrow keycode if (!n) return; @@ -450,7 +458,7 @@ BYTE KeybReadFlag (void) //=========================================================================== void KeybToggleCapsLock () { - if (!IS_APPLE2) + if (!IS_APPLE2 ||IsIMC2001(g_Apple2Type)) { g_bCapsLock = (GetKeyState(VK_CAPITAL) & 1); GetFrame().FrameRefreshStatus(DRAW_LEDS | DRAW_DISK_STATUS); diff --git a/source/Memory.cpp b/source/Memory.cpp index a9c15f10..5938f504 100644 --- a/source/Memory.cpp +++ b/source/Memory.cpp @@ -1731,6 +1731,7 @@ void MemInitializeROM(void) case A2TYPE_PRAVETS8A: resourceId = IDR_PRAVETS_8C_ROM ; ROM_SIZE = Apple2eRomSize; break; case A2TYPE_TK30002E: resourceId = IDR_TK3000_2E_ROM ; ROM_SIZE = Apple2eRomSize; break; case A2TYPE_BASE64A: resourceId = IDR_BASE_64A_ROM ; ROM_SIZE = Base64ARomSize; break; + case A2TYPE_IMC2001: resourceId = IDR_IMC2001_ROM ; ROM_SIZE = Apple2RomSize; break; } BYTE* pData = NULL; @@ -1754,6 +1755,7 @@ void MemInitializeROM(void) case A2TYPE_PRAVETS8A: _tcscpy(sRomFileName, TEXT("PRAVETS8C.ROM" )); break; case A2TYPE_TK30002E: _tcscpy(sRomFileName, TEXT("TK3000e.ROM" )); break; case A2TYPE_BASE64A: _tcscpy(sRomFileName, TEXT("BASE64A.ROM" )); break; + case A2TYPE_IMC2001: _tcscpy(sRomFileName, TEXT("IMC2001.ROM" )); break; default: { _tcscpy(sRomFileName, TEXT("Unknown type!")); diff --git a/source/NTSC.cpp b/source/NTSC.cpp index de24f0b4..23c40daa 100644 --- a/source/NTSC.cpp +++ b/source/NTSC.cpp @@ -367,6 +367,7 @@ static void set_csbits() case A2TYPE_PRAVETS8A: csbits = &csbits_pravets8C[0]; break; // Apple //e clone case A2TYPE_TK30002E: csbits = &csbits_enhanced2e[0]; break; // Enhanced Apple //e clone case A2TYPE_BASE64A: csbits = &csbits_base64a[GetVideo().GetVideoRomRockerSwitch() ? 0 : 1]; g_nVideoCharSet = 0; break; // Apple ][ clone + case A2TYPE_IMC2001: csbits = &csbits_imc2001[0]; g_nVideoCharSet = 0; break; default: _ASSERT(0); csbits = &csbits_enhanced2e[0]; break; } } diff --git a/source/NTSC_CharSet.cpp b/source/NTSC_CharSet.cpp index 0a3998cb..9e1a5436 100644 --- a/source/NTSC_CharSet.cpp +++ b/source/NTSC_CharSet.cpp @@ -35,7 +35,7 @@ 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 unsigned char csbits_base64a[2][256][8]; // Base 64A - +unsigned char csbits_imc2001[1][256][8]; // IMC-2001 // @@ -198,6 +198,12 @@ static void userVideoRom2K(csbits_t csbits, const BYTE* pVideoRom, const eApple2 { BYTE n = pVideoRom[RA+y]; + if (type == A2TYPE_IMC2001) + { + // characters are shifted by one position in the ROM + n = ( n >> 1 ); + } + // 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..." // Apple II J-Plus: simplest logic is just invert if reading low 1K of video ROM @@ -209,6 +215,12 @@ static void userVideoRom2K(csbits_t csbits, const BYTE* pVideoRom, const eApple2 if (!(n & 0x01)) n = n ^ 0xfe; } + else if (type == A2TYPE_IMC2001) + { + // for now just unconditionally negate the pattern for inverse (does + // this work for small letters too?) + n = n ^ 0xfe; + } else { if (!(n & 0x80) || (type == A2TYPE_APPLE2JPLUS)) @@ -222,6 +234,11 @@ static void userVideoRom2K(csbits_t csbits, const BYTE* pVideoRom, const eApple2 // On the Base 64A bits are ordered 1345672. d = (n >> 2) | ((n & 2) >> 1) | ((n & 4) << 4); } + else if (type == A2TYPE_IMC2001) + { + // bits are reversed and shifted + d = (n >> 1); + } else { // UTAII:8-30 "TEXT ROM pattern is ... reversed" @@ -255,6 +272,15 @@ static void VideoRomForIIandIIPlus(void) userVideoRom2K(&csbits_a2[0], pVideoRom); } +static void VideoRomForImc2001() +{ + BYTE* pVideoRom = GetFrame().GetResource(IDR_IMC2001_VIDEO_ROM, "ROM", Video::kVideoRomSize2K); + if (pVideoRom == NULL) + return; + + userVideoRom2K(&csbits_imc2001[0], pVideoRom, A2TYPE_IMC2001, 0); +} + static void VideoRomForIIJPlus(void) { BYTE* pVideoRom = GetFrame().GetResource(IDR_APPLE2_JPLUS_VIDEO_ROM, "ROM", Video::kVideoRomSize2K); @@ -303,6 +329,8 @@ void make_csbits(void) VideoRomForIIJPlus(); // GH#773 VideoRomForBase64A(); // GH#806 + VideoRomForImc2001(); + // 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 f8ce9370..47984a34 100644 --- a/source/NTSC_CharSet.h +++ b/source/NTSC_CharSet.h @@ -9,6 +9,7 @@ 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 extern unsigned char csbits_base64a[2][256][8]; // Base64A +extern unsigned char csbits_imc2001[1][256][8]; // IMC-2001 void make_csbits(void); diff --git a/source/SaveState.cpp b/source/SaveState.cpp index e912a0ef..7e631f19 100644 --- a/source/SaveState.cpp +++ b/source/SaveState.cpp @@ -208,6 +208,7 @@ static const std::string& GetSnapshotUnitMiscName(void) #define SS_YAML_VALUE_PRAVETS8A "Pravets8A" #define SS_YAML_VALUE_TK30002E "TK3000//e" #define SS_YAML_VALUE_BASE64A "Base 64A" +#define SS_YAML_VALUE_IMC2001 "IMC 2001" static eApple2Type ParseApple2Type(std::string type) { @@ -222,6 +223,7 @@ static eApple2Type ParseApple2Type(std::string type) else if (type == SS_YAML_VALUE_PRAVETS8A) return A2TYPE_PRAVETS8A; else if (type == SS_YAML_VALUE_TK30002E) return A2TYPE_TK30002E; else if (type == SS_YAML_VALUE_BASE64A) return A2TYPE_BASE64A; + else if (type == SS_YAML_VALUE_IMC2001) return A2TYPE_IMC2001; throw std::runtime_error("Load: Unknown Apple2 type"); } @@ -241,6 +243,7 @@ static std::string GetApple2TypeAsString(void) case A2TYPE_PRAVETS8A: return SS_YAML_VALUE_PRAVETS8A; case A2TYPE_TK30002E: return SS_YAML_VALUE_TK30002E; case A2TYPE_BASE64A: return SS_YAML_VALUE_BASE64A; + case A2TYPE_IMC2001: return SS_YAML_VALUE_IMC2001; default: throw std::runtime_error("Save: Unknown Apple2 type"); } diff --git a/source/Utilities.cpp b/source/Utilities.cpp index 04904caa..e05aa25c 100644 --- a/source/Utilities.cpp +++ b/source/Utilities.cpp @@ -477,6 +477,7 @@ void GetAppleWindowTitle() case A2TYPE_PRAVETS8A: g_pAppTitle = TITLE_PRAVETS_8A; break; case A2TYPE_TK30002E: g_pAppTitle = TITLE_TK3000_2E; break; case A2TYPE_BASE64A: g_pAppTitle = TITLE_BASE64A; break; + case A2TYPE_IMC2001: g_pAppTitle = TITLE_IMC2001; break; } #if _DEBUG diff --git a/source/Windows/WinFrame.cpp b/source/Windows/WinFrame.cpp index 7e003377..b9d5b3ba 100644 --- a/source/Windows/WinFrame.cpp +++ b/source/Windows/WinFrame.cpp @@ -196,6 +196,9 @@ void Win32Frame::CreateGdiObjects(void) case A2TYPE_BASE64A: buttonbitmap[BTN_RUN] = (HBITMAP)LOADBUTTONBITMAP(TEXT("RUNBASE64A_BUTTON")); break; + case A2TYPE_IMC2001: + buttonbitmap[BTN_RUN] = (HBITMAP)LOADBUTTONBITMAP(TEXT("RUNIMC2001_BUTTON")); + break; default: buttonbitmap[BTN_RUN] = (HBITMAP)LOADBUTTONBITMAP(TEXT("RUN_BUTTON")); break;