diff --git a/resource/Applewin.rc b/resource/Applewin.rc index 3a4d3378..3262496d 100644 --- a/resource/Applewin.rc +++ b/resource/Applewin.rc @@ -252,8 +252,8 @@ DISK_ICON ICON "DISK.ICO" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,27,10,0 - PRODUCTVERSION 1,27,10,0 + FILEVERSION 1,27,11,0 + PRODUCTVERSION 1,27,11,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -271,12 +271,12 @@ BEGIN VALUE "Comments", "https://github.com/AppleWin" VALUE "CompanyName", "AppleWin" VALUE "FileDescription", "Apple //e Emulator for Windows" - VALUE "FileVersion", "1, 27, 10, 0" + VALUE "FileVersion", "1, 27, 11, 0" VALUE "InternalName", "APPLEWIN" VALUE "LegalCopyright", " 1994-2018 Michael O'Brien, Oliver Schmidt, Tom Charlesworth, Michael Pohoreski, Nick Westgate, Linards Ticmanis" VALUE "OriginalFilename", "APPLEWIN.EXE" VALUE "ProductName", "Apple //e Emulator" - VALUE "ProductVersion", "1, 27, 10, 0" + VALUE "ProductVersion", "1, 27, 11, 0" END END BLOCK "VarFileInfo" diff --git a/source/Applewin.cpp b/source/Applewin.cpp index f0e787b8..9d2fb161 100644 --- a/source/Applewin.cpp +++ b/source/Applewin.cpp @@ -1289,6 +1289,22 @@ int APIENTRY WinMain(HINSTANCE passinstance, HINSTANCE, LPSTR lpCmdLine, int) if ((g_hCustomRomF8 == INVALID_HANDLE_VALUE) || (GetFileSize(g_hCustomRomF8, NULL) != 0x800)) g_bCustomRomF8Failed = true; } + else if (strcmp(lpCmdLine, "-videorom") == 0) // Use 4K,8K or 16K video ROM for Enhanced //e + { + lpCmdLine = GetCurrArg(lpNextArg); + lpNextArg = GetNextArg(lpNextArg); + + if (!ReadVideoRomFile(lpCmdLine)) + { + std::string msg = "Failed to load video rom (not found or not exactly 4/8/16KiB)"; + LogFileOutput("%s", msg.c_str()); + MessageBox(g_hFrameWindow, msg.c_str(), TEXT("AppleWin Error"), MB_OK); + } + else + { + SetVideoRomRockerSwitch(true); // Use PAL char set + } + } else if (strcmp(lpCmdLine, "-printscreen") == 0) // Turn on display of the last filename print screen was saved to { g_bDisplayPrintScreenFileName = true; @@ -1553,7 +1569,9 @@ int APIENTRY WinMain(HINSTANCE passinstance, HINSTANCE, LPSTR lpCmdLine, int) if (g_bCustomRomF8Failed) { - MessageBox(g_hFrameWindow, "Failed to load custom F8 rom (not found or not exactly 2KB)", TEXT("AppleWin Error"), MB_OK); + std::string msg = "Failed to load custom F8 rom (not found or not exactly 2KiB)"; + LogFileOutput("%s", msg.c_str()); + MessageBox(g_hFrameWindow, msg.c_str(), TEXT("AppleWin Error"), MB_OK); bShutdown = true; } diff --git a/source/Debugger/Debugger_Display.cpp b/source/Debugger/Debugger_Display.cpp index 3a171829..b9a8a62a 100644 --- a/source/Debugger/Debugger_Display.cpp +++ b/source/Debugger/Debugger_Display.cpp @@ -3089,7 +3089,7 @@ void DrawSoftSwitches( int iSoftSwitch ) _DrawSoftSwitch( rect, 0xC00C, bSet, "Col", "40", "80", NULL, bgMemory ); // C00E = off, C00F = on - bSet = VideoGetSWAltCharSet(); + bSet = !VideoGetSWAltCharSet(); _DrawSoftSwitch( rect, 0xC00E, bSet, NULL, "ASC", "MOUS", NULL, bgMemory ); // ASCII/MouseText #if SOFTSWITCH_LANGCARD diff --git a/source/Frame.cpp b/source/Frame.cpp index 26841a44..de139893 100644 --- a/source/Frame.cpp +++ b/source/Frame.cpp @@ -1316,14 +1316,19 @@ LRESULT CALLBACK FrameWndProc ( } else if (wparam == VK_F10) { - if (g_Apple2Type == A2TYPE_PRAVETS8A && !KeybGetCtrlStatus()) - { - KeybToggleP8ACapsLock (); // F10: Toggles P8 Capslock - } - else + if (KeybGetCtrlStatus()) { SetUsingCursor(FALSE); // Ctrl+F10 } + else if (g_Apple2Type == A2TYPE_APPLE2E || g_Apple2Type == A2TYPE_APPLE2EENHANCED) + { + SetVideoRomRockerSwitch( !GetVideoRomRockerSwitch() ); // F10: toggle rocker switch + NTSC_VideoInitAppleType(); + } + else if (g_Apple2Type == A2TYPE_PRAVETS8A) + { + KeybToggleP8ACapsLock (); // F10: Toggles Pravets8A Capslock + } } else if (wparam == VK_F11 && !KeybGetCtrlStatus()) // Save state (F11) { @@ -1758,7 +1763,6 @@ LRESULT CALLBACK FrameWndProc ( KeybUpdateCtrlShiftStatus(); // http://msdn.microsoft.com/en-us/library/windows/desktop/gg153546(v=vs.85).aspx - // v1.25.0: Alt-Return Alt-Enter toggle fullscreen if (g_bAltEnter_ToggleFullScreen && KeybGetAltStatus() && (wparam == VK_RETURN)) // NB. VK_RETURN = 0x0D; Normally WM_CHAR will be 0x0A but ALT key triggers as WM_SYSKEYDOWN and VK_MENU return 0; // NOP -- eat key @@ -1772,7 +1776,10 @@ LRESULT CALLBACK FrameWndProc ( case WM_SYSKEYUP: KeybUpdateCtrlShiftStatus(); - // v1.25.0: Alt-Return Alt-Enter toggle fullscreen + // F10: no WM_KEYUP handler for VK_F10. Don't allow WM_KEYUP to pass to default handler which will show the app window's "menu" (and lose focus) + if (wparam == VK_F10) + return 0; + if (g_bAltEnter_ToggleFullScreen && KeybGetAltStatus() && (wparam == VK_RETURN)) // NB. VK_RETURN = 0x0D; Normally WM_CHAR will be 0x0A but ALT key triggers as WM_SYSKEYDOWN and VK_MENU ScreenWindowResize(false); else diff --git a/source/NTSC.cpp b/source/NTSC.cpp index ae0b1503..088626dc 100644 --- a/source/NTSC.cpp +++ b/source/NTSC.cpp @@ -471,7 +471,7 @@ 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_APPLE2E: csbits = &csbits_2e[0]; break; - case A2TYPE_APPLE2EENHANCED:csbits = &csbits_enhanced2e[0]; break; + case A2TYPE_APPLE2EENHANCED:csbits = GetEnhanced2e_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 diff --git a/source/NTSC_CharSet.cpp b/source/NTSC_CharSet.cpp index 4fa37822..88d1250f 100644 --- a/source/NTSC_CharSet.cpp +++ b/source/NTSC_CharSet.cpp @@ -21,10 +21,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "StdAfx.h" #include "Applewin.h" +#include "Video.h" #include "NTSC_CharSet.h" -unsigned char csbits_enhanced2e[2][256][8]; // Enhanced //e +unsigned char csbits_enhanced2e[2][256][8]; // Enhanced //e (2732 4K video ROM) +static unsigned char csbits_enhanced2e_pal[2][256][8]; // PAL Enhanced //e (2764 8K video ROM - top 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_pravets82[1][256][8]; // Pravets 82 @@ -86,6 +88,91 @@ static void get_csbits(csbits_t csbits, const char* resourceName, const UINT cy0 delete [] pBuffer; } +//------------------------------------- + +// ROM address (RA): +// ----------------- +// . RA10,..,RA3;SEGC,SEGB,SEGA => [2^8][2^3] => 256 chars of 8 lines (total = 2KiB) +// . VID7,..,VID0 is the 8-bit video character (eg. from TEXT/$400 memory) +// +// UTAIIe:8-13, Table 8.2: +// +// ALTCHRSET | RA10 | RA9 +//------------------------------------------ +// 0 | VID7 + VID6.FLASH | VID6.VID7 +// 1 | VID7 | VID6 +// +// FLASH toggles every 16 VBLs, so alternates between selecting NORMAL control/special and INVERSE control/special +// + +void userVideoRom4K(csbits_t csbits, const BYTE* pVideoRom) +{ + int RA = 0; // rom address + int i = 0; + + // regular char set + + for (; i<64; i++, RA+=8) // [00..3F] INVERSE / [40..7F] FLASH + { + for (int y=0; y<8; y++) + { + csbits[0][i][y] = pVideoRom[RA+y] ^ 0xff; // UTAIIe:8-11 "dot patterns in the video ROM are inverted..." + csbits[0][i+64][y] = pVideoRom[RA+y] ^ 0xff; // UTAIIe:8-14 (Table 8.3) we use FLASH=0, so RA=00ccccccsss + } + } + + RA = (1<<10 | 0<<9); // UTAIIe:8-14 (Table 8.3) + + for (i=128; i<256; i++, RA+=8) // [80..BF] NORMAL + { + for (int y=0; y<8; y++) + { + csbits[0][i][y] = pVideoRom[RA+y] ^ 0xff; // UTAIIe:8-11 "dot patterns in the video ROM are inverted..." + } + } + + RA = (1<<10 | 1<<9); // UTAIIe:8-14 (Table 8.3) + + for (i=192; i<256; i++, RA+=8) // [C0..FF] NORMAL + { + for (int y=0; y<8; y++) + { + csbits[0][i][y] = pVideoRom[RA+y] ^ 0xff; // UTAIIe:8-11 "dot patterns in the video ROM are inverted..." + } + } + + // alt char set + + RA = 0; + + for (i=0; i<256; i++, RA+=8) // [00..7F] INVERSE / [80..FF] NORMAL + { + for (int y=0; y<8; y++) + { + csbits[1][i][y] = pVideoRom[RA+y] ^ 0xff; // UTAIIe:8-11 "dot patterns in the video ROM are inverted..." + } + } +} + +void userVideoRom(void) +{ + const BYTE* pVideoRom; + UINT size = GetVideoRom(pVideoRom); // 4K or 8K + if (!size) + return; + + if (size == kVideoRomSize4K) + { + userVideoRom4K(&csbits_enhanced2e[0], pVideoRom); + return; + } + + userVideoRom4K(&csbits_enhanced2e_pal[0], pVideoRom); + userVideoRom4K(&csbits_enhanced2e[0], &pVideoRom[4*1024]); +} + +//------------------------------------- + void make_csbits(void) { get_csbits(&csbits_enhanced2e[0], TEXT("CHARSET40"), 0); // Enhanced //e: Alt char set off @@ -99,4 +186,15 @@ void make_csbits(void) // 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[1][64], &csbits_2e[0][64], 32*8); + + // Try to use any user-provided video ROM for Enhanced //e + userVideoRom(); +} + +csbits_t GetEnhanced2e_csbits(void) +{ + if (IsVideoRom4K()) + return csbits_enhanced2e; + + return GetVideoRomRockerSwitch() == false ? csbits_enhanced2e : csbits_enhanced2e_pal; } diff --git a/source/NTSC_CharSet.h b/source/NTSC_CharSet.h index 3f549059..465a2b51 100644 --- a/source/NTSC_CharSet.h +++ b/source/NTSC_CharSet.h @@ -2,7 +2,7 @@ typedef unsigned char (*csbits_t)[256][8]; -extern unsigned char csbits_enhanced2e[2][256][8]; // Enhanced //e +extern unsigned char csbits_enhanced2e[2][256][8]; // Enhanced //e (2732 4K video ROM) extern unsigned char csbits_2e[2][256][8]; // Original //e (no mousetext) extern unsigned char csbits_a2[1][256][8]; // ][ and ][+ extern unsigned char csbits_pravets82[1][256][8]; // Pravets 82 @@ -10,3 +10,4 @@ extern unsigned char csbits_pravets8M[1][256][8]; // Pravets 8M extern unsigned char csbits_pravets8C[2][256][8]; // Pravets 8A & 8C void make_csbits(void); +csbits_t GetEnhanced2e_csbits(void); diff --git a/source/Video.cpp b/source/Video.cpp index d5663f3b..3d593218 100644 --- a/source/Video.cpp +++ b/source/Video.cpp @@ -647,7 +647,7 @@ void VideoResetState () //=========================================================================== -BYTE VideoSetMode (WORD, WORD address, BYTE write, BYTE, ULONG uExecutedCycles) +BYTE VideoSetMode(WORD, WORD address, BYTE write, BYTE, ULONG uExecutedCycles) { address &= 0xFF; @@ -1141,6 +1141,65 @@ static void Video_SaveScreenShot( const char *pScreenShotFileName, const VideoSc } } + +//=========================================================================== + +static const UINT kVideoRomSize8K = kVideoRomSize4K*2; +static const UINT kVideoRomSize16K = kVideoRomSize8K*2; +static const UINT kVideoRomSizeMax = kVideoRomSize16K; +static BYTE g_videoRom[kVideoRomSizeMax]; +static UINT g_videoRomSize = 0; +static bool g_videoRomRockerSwitch = false; + +bool ReadVideoRomFile(const char* pRomFile) +{ + g_videoRomSize = 0; + + HANDLE h = CreateFile(pRomFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + if (h == INVALID_HANDLE_VALUE) + return false; + + const ULONG size = GetFileSize(h, NULL); + if (size == kVideoRomSize4K || size == kVideoRomSize8K || size == kVideoRomSize16K) + { + DWORD bytesRead; + if (ReadFile(h, g_videoRom, size, &bytesRead, NULL) && bytesRead == size) + g_videoRomSize = size; + } + + if (g_videoRomSize == kVideoRomSize16K) + { + // Use top 8K (assume bottom 8K is all 0xFF's) + memcpy(&g_videoRom[0], &g_videoRom[kVideoRomSize8K], kVideoRomSize8K); + g_videoRomSize = kVideoRomSize8K; + } + + CloseHandle(h); + + return g_videoRomSize != 0; +} + +UINT GetVideoRom(const BYTE*& pVideoRom) +{ + pVideoRom = &g_videoRom[0]; + return g_videoRomSize; +} + +bool GetVideoRomRockerSwitch(void) +{ + return g_videoRomRockerSwitch; +} + +void SetVideoRomRockerSwitch(bool state) +{ + g_videoRomRockerSwitch = state; +} + +bool IsVideoRom4K(void) +{ + return g_videoRomSize == 0 || g_videoRomSize == kVideoRomSize4K; +} + //=========================================================================== void Config_Load_Video() @@ -1160,8 +1219,6 @@ void Config_Save_Video() REGSAVE(TEXT(REGVALUE_VIDEO_MONO_COLOR ),g_nMonochromeRGB); } -// ____________________________________________________________________ - //=========================================================================== static void videoCreateDIBSection() { diff --git a/source/Video.h b/source/Video.h index bd064502..301b503b 100644 --- a/source/Video.h +++ b/source/Video.h @@ -198,7 +198,14 @@ enum VideoScreenShot_e void Video_TakeScreenShot( VideoScreenShot_e iScreenShotType ); void Video_SetBitmapHeader( WinBmpHeader_t *pBmp, int nWidth, int nHeight, int nBitsPerPixel ); -BYTE VideoSetMode (WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG uExecutedCycles); +BYTE VideoSetMode(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG uExecutedCycles); + +const UINT kVideoRomSize4K = 4*1024; +bool ReadVideoRomFile(const char* pRomFile); +UINT GetVideoRom(const BYTE*& pVideoRom); +bool GetVideoRomRockerSwitch(void); +void SetVideoRomRockerSwitch(bool state); +bool IsVideoRom4K(void); void Config_Load_Video(void); void Config_Save_Video(void);