diff --git a/AppleWinExpress2019.vcxproj b/AppleWinExpress2019.vcxproj index 6e8a483d..eb8544b9 100644 --- a/AppleWinExpress2019.vcxproj +++ b/AppleWinExpress2019.vcxproj @@ -121,6 +121,7 @@ + @@ -247,6 +248,7 @@ + diff --git a/AppleWinExpress2019.vcxproj.filters b/AppleWinExpress2019.vcxproj.filters index 49c6034d..26c96c5d 100644 --- a/AppleWinExpress2019.vcxproj.filters +++ b/AppleWinExpress2019.vcxproj.filters @@ -241,6 +241,9 @@ Source Files\Emulator + + Source Files\Video + @@ -555,6 +558,9 @@ Source Files\Emulator + + Source Files\Video + diff --git a/resource/Applewin.rc b/resource/Applewin.rc index 968fa3f8..dc7c5687 100644 --- a/resource/Applewin.rc +++ b/resource/Applewin.rc @@ -84,7 +84,7 @@ IDB_DEBUG_FONT_7X8 BITMAP "Debug_Font.bmp" // Dialog // -IDD_PROPPAGE_CONFIG DIALOGEX 0, 0, 210, 209 +IDD_PROPPAGE_CONFIG DIALOGEX 0, 0, 210, 240 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_CAPTION | WS_SYSMENU CAPTION "Configuration" FONT 8, "MS Shell Dlg", 0, 0, 0x0 @@ -92,30 +92,32 @@ BEGIN LTEXT "&Model:",IDC_STATIC,5,7,40,8 COMBOBOX IDC_COMPUTER,45,5,91,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP CONTROL "Confirm reboot",IDC_CHECK_CONFIRM_REBOOT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,142,8,62,10 - GROUPBOX "Video",IDC_STATIC,5,22,200,56 + GROUPBOX "Video",IDC_STATIC,5,22,200,74 LTEXT "Mo&de:",IDC_STATIC,12,33,33,8 COMBOBOX IDC_VIDEOTYPE,33,30,103,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "Monochrome &Color...",IDC_MONOCOLOR,12,46,80,14 CONTROL "50% Scan lines",IDC_CHECK_HALF_SCAN_LINES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,142,33,62,10 CONTROL "Vertical blend",IDC_CHECK_VERTICAL_BLEND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,142,48,62,10 - CONTROL "Full-Screen: Show drive/keyboard status",IDC_CHECK_FS_SHOW_SUBUNIT_STATUS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,64,140,10 - LTEXT "&Serial Port:",IDC_STATIC,5,89,40,8 - COMBOBOX IDC_SERIALPORT,45,87,90,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "&Ethernet Settings...",IDC_ETHERNET,4,105,80,14 - GROUPBOX "Emulation Speed Control",IDC_STATIC,5,130,200,85 + CONTROL "Full-Screen: Show drive/keyboard status",IDC_CHECK_FS_SHOW_SUBUNIT_STATUS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,64,140,10 + CONTROL "VidHD in slot 3",IDC_CHECK_VIDHD_IN_SLOT3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,78,140,10 + LTEXT "&Serial Port:",IDC_STATIC,5,108,40,8 + COMBOBOX IDC_SERIALPORT,45,106,90,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "&Ethernet Settings...",IDC_ETHERNET,4,124,80,14 + GROUPBOX "Emulation Speed Control",IDC_STATIC,5,149,200,85 CONTROL "Use &Authentic Machine Speed",IDC_AUTHENTIC_SPEED, - "Button",BS_AUTORADIOBUTTON,15,141,115,10 - CONTROL "Select C&ustom Speed (in MHz)",IDC_CUSTOM_SPEED,"Button",BS_AUTORADIOBUTTON,15,153,115,10 - CONTROL "Generic2",IDC_SLIDER_CPU_SPEED,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,25,164,160,15 - CTEXT "0.5",IDC_0_5_MHz,23,180,20,10 - CTEXT "1.0",IDC_1_0_MHz,59,180,20,10 - CTEXT "2.0",IDC_2_0_MHz,96,180,20,10 - RTEXT "Fastest",IDC_MAX_MHz,150,180,29,10 - PUSHBUTTON "&Benchmark Emulator",IDC_BENCHMARK,15,194,85,15 - CONTROL "50Hz video",IDC_CHECK_50HZ_VIDEO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,142,141,51,10 + "Button",BS_AUTORADIOBUTTON,15,160,115,10 + CONTROL "Select C&ustom Speed (in MHz)",IDC_CUSTOM_SPEED,"Button",BS_AUTORADIOBUTTON,15,172,115,10 + CONTROL "Generic2",IDC_SLIDER_CPU_SPEED,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,25,183,160,15 + CTEXT "0.5",IDC_0_5_MHz,23,199,20,10 + CTEXT "1.0",IDC_1_0_MHz,59,199,20,10 + CTEXT "2.0",IDC_2_0_MHz,96,199,20,10 + RTEXT "Fastest",IDC_MAX_MHz,150,199,29,10 + PUSHBUTTON "&Benchmark Emulator",IDC_BENCHMARK,15,213,85,15 + CONTROL "50Hz video",IDC_CHECK_50HZ_VIDEO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,142,160,51,10 END -IDD_PROPPAGE_INPUT DIALOGEX 0, 0, 211, 240 +IDD_PROPPAGE_INPUT DIALOGEX 0, 0, 210, 240 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_CAPTION | WS_SYSMENU CAPTION "Input" FONT 8, "MS Shell Dlg", 0, 0, 0x0 @@ -172,7 +174,7 @@ BEGIN CONTROL "No sound cards",IDC_SOUNDCARD_DISABLE,"Button",BS_AUTORADIOBUTTON,10,175,78,10 END -IDD_PROPPAGE_DISK DIALOGEX 0, 0, 211, 188 +IDD_PROPPAGE_DISK DIALOGEX 0, 0, 210, 240 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_CAPTION | WS_SYSMENU CAPTION "Disk" FONT 8, "MS Shell Dlg", 0, 0, 0x0 diff --git a/resource/resource.h b/resource/resource.h index ae5a2158..819b1011 100644 --- a/resource/resource.h +++ b/resource/resource.h @@ -70,7 +70,6 @@ #define IDC_PHASOR_ENABLE 1029 #define IDC_SAM_ENABLE 1030 #define IDC_SOUNDCARD_DISABLE 1031 - #define IDC_TFE_SETTINGS_ENABLE_T 1032 #define IDC_TFE_SETTINGS_ENABLE 1033 #define IDC_TFE_SETTINGS_INTERFACE_T 1034 @@ -99,7 +98,6 @@ #define IDC_PRINTER_FILTER_UNPRINTABLE 1057 #define IDC_PRINTER_APPEND 1058 #define IDC_SPIN_PRINTER_IDLE 1059 - #define IDC_CHECK_HALF_SCAN_LINES 1060 #define IDC_GPL_TEXT 1061 #define IDC_GPL_BORDER 1063 @@ -120,6 +118,7 @@ #define IDC_COMBO_DISK2_SLOT5 1086 #define IDC_FOURPLAY_CONFIG 1087 #define IDC_SNESMAX_CONFIG 1088 +#define IDC_CHECK_VIDHD_IN_SLOT3 1089 #define IDM_EXIT 40001 #define IDM_HELP 40002 #define IDM_ABOUT 40003 diff --git a/source/Card.h b/source/Card.h index 9dffa7f9..f9707a1e 100644 --- a/source/Card.h +++ b/source/Card.h @@ -23,6 +23,7 @@ enum SS_CARDTYPE CT_Saturn128K, // Saturn 128K (but may be populated with less RAM, in multiples of 16K) CT_FourPlay, // 4 port Atari 2600 style digital joystick card CT_SNESMAX, // 2 port Nintendo NES/SNES controller serial interface card + CT_VidHD, }; enum SLOTS { SLOT0=0, SLOT1, SLOT2, SLOT3, SLOT4, SLOT5, SLOT6, SLOT7, NUM_SLOTS, SLOT_AUX }; diff --git a/source/CardManager.cpp b/source/CardManager.cpp index bdff4f14..0361e280 100644 --- a/source/CardManager.cpp +++ b/source/CardManager.cpp @@ -40,6 +40,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "SAM.h" #include "SerialComms.h" #include "SNESMAX.h" +#include "VidHD.h" void CardManager::InsertInternal(UINT slot, SS_CARDTYPE type) { @@ -96,6 +97,9 @@ void CardManager::InsertInternal(UINT slot, SS_CARDTYPE type) case CT_SNESMAX: m_slot[slot] = new SNESMAXCard(slot); break; + case CT_VidHD: + m_slot[slot] = new VidHDCard(slot); + break; case CT_LanguageCard: case CT_Saturn128K: diff --git a/source/CmdLine.cpp b/source/CmdLine.cpp index 2dbccffe..acfdca31 100644 --- a/source/CmdLine.cpp +++ b/source/CmdLine.cpp @@ -169,6 +169,13 @@ bool ProcessCmdLine(LPSTR lpCmdLine) g_cmdLine.bSlotEmpty[slot] = true; if (strcmp(lpCmdLine, "diskii") == 0) g_cmdLine.slotInsert[slot] = CT_Disk2; + if (strcmp(lpCmdLine, "vidhd") == 0) + { + if (slot == SLOT3) + g_cmdLine.slotInsert[slot] = CT_VidHD; + else + LogFileOutput("VidHD currently only supported in slot 3\n"); + } } else if (lpCmdLine[3] == 'd' && (lpCmdLine[4] == '1' || lpCmdLine[4] == '2')) // -s[1..7]d[1|2] { @@ -208,6 +215,19 @@ bool ProcessCmdLine(LPSTR lpCmdLine) { g_cmdLine.setFullScreen = 0; } +#define CMD_FS_WIDTH "-fs-width=" + else if (strncmp(lpCmdLine, CMD_FS_WIDTH, sizeof(CMD_FS_WIDTH)-1) == 0) + { + if (g_cmdLine.setFullScreen < 0) // Not yet been specified on cmd line? + g_cmdLine.setFullScreen = 1; // Implicity set full-screen. NB. Can be overridden by "-no-full-screen" + + LPSTR lpTmp = lpCmdLine + sizeof(CMD_FS_WIDTH)-1; + { + g_cmdLine.userSpecifiedWidth = atoi(lpTmp); + if (!g_cmdLine.userSpecifiedWidth) + LogFileOutput("Invalid cmd-line parameter for -fs-width=x switch\n"); + } + } #define CMD_FS_HEIGHT "-fs-height=" else if (strncmp(lpCmdLine, CMD_FS_HEIGHT, sizeof(CMD_FS_HEIGHT)-1) == 0) { @@ -215,24 +235,16 @@ bool ProcessCmdLine(LPSTR lpCmdLine) g_cmdLine.setFullScreen = 1; // Implicity set full-screen. NB. Can be overridden by "-no-full-screen" LPSTR lpTmp = lpCmdLine + sizeof(CMD_FS_HEIGHT)-1; - bool bRes = false; - UINT bestWidth=0, bestHeight=0; if (strcmp(lpTmp, "best") == 0) { - bRes = GetFrame().GetBestDisplayResolutionForFullScreen(bestWidth, bestHeight); + g_cmdLine.bestFullScreenResolution = true; } else { - UINT userSpecifiedHeight = atoi(lpTmp); - if (userSpecifiedHeight) - bRes = GetFrame().GetBestDisplayResolutionForFullScreen(bestWidth, bestHeight, userSpecifiedHeight); - else + g_cmdLine.userSpecifiedHeight = atoi(lpTmp); + if (!g_cmdLine.userSpecifiedHeight) LogFileOutput("Invalid cmd-line parameter for -fs-height=x switch\n"); } - if (bRes) - LogFileOutput("Best resolution for -fs-height=x switch: Width=%d, Height=%d\n", bestWidth, bestHeight); - else - LogFileOutput("Failed to set parameter for -fs-height=x switch\n"); } else if (strcmp(lpCmdLine, "-no-di") == 0) { diff --git a/source/CmdLine.h b/source/CmdLine.h index 2e1034cd..b76dd0f4 100644 --- a/source/CmdLine.h +++ b/source/CmdLine.h @@ -35,6 +35,9 @@ struct CmdLine rgbCard = RGB_Videocard_e::Apple; rgbCardForegroundColor = 15; rgbCardBackgroundColor = 0; + bestFullScreenResolution = false; + userSpecifiedWidth = 0; + userSpecifiedHeight = 0; for (UINT i = 0; i < NUM_SLOTS; i++) { @@ -74,6 +77,9 @@ struct CmdLine int rgbCardForegroundColor; int rgbCardBackgroundColor; std::string strCurrentDir; + bool bestFullScreenResolution; + UINT userSpecifiedWidth; + UINT userSpecifiedHeight; }; bool ProcessCmdLine(LPSTR lpCmdLine); diff --git a/source/Configuration/PageConfig.cpp b/source/Configuration/PageConfig.cpp index 4db1efa8..996c56fe 100644 --- a/source/Configuration/PageConfig.cpp +++ b/source/Configuration/PageConfig.cpp @@ -89,18 +89,18 @@ INT_PTR CPageConfig::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPA switch (LOWORD(wparam)) { case IDC_AUTHENTIC_SPEED: // Authentic Machine Speed - SendDlgItemMessage(hWnd,IDC_SLIDER_CPU_SPEED,TBM_SETPOS,1,SPEED_NORMAL); - EnableTrackbar(hWnd,0); + SendDlgItemMessage(hWnd, IDC_SLIDER_CPU_SPEED, TBM_SETPOS, 1, SPEED_NORMAL); + EnableTrackbar(hWnd, 0); break; case IDC_CUSTOM_SPEED: // Select Custom Speed - SetFocus(GetDlgItem(hWnd,IDC_SLIDER_CPU_SPEED)); - EnableTrackbar(hWnd,1); + SetFocus(GetDlgItem(hWnd, IDC_SLIDER_CPU_SPEED)); + EnableTrackbar(hWnd, 1); break; case IDC_SLIDER_CPU_SPEED: // CPU speed slider - CheckRadioButton(hWnd,IDC_AUTHENTIC_SPEED,IDC_CUSTOM_SPEED,IDC_CUSTOM_SPEED); - EnableTrackbar(hWnd,1); + CheckRadioButton(hWnd, IDC_AUTHENTIC_SPEED, IDC_CUSTOM_SPEED, IDC_CUSTOM_SPEED); + EnableTrackbar(hWnd, 1); break; case IDC_BENCHMARK: @@ -113,6 +113,7 @@ INT_PTR CPageConfig::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPA case IDC_ETHERNET: ui_tfe_settings_dialog(hWnd); m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3] = m_PageConfigTfe.m_tfe_enabled ? CT_Uthernet : CT_Empty; + InitOptions(hWnd); break; case IDC_MONOCOLOR: @@ -127,6 +128,14 @@ INT_PTR CPageConfig::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPA // Checked in DlgOK() break; + case IDC_CHECK_VIDHD_IN_SLOT3: + { + const UINT newState = IsDlgButtonChecked(hWnd, IDC_CHECK_VIDHD_IN_SLOT3) ? 1 : 0; + m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3] = newState ? CT_VidHD : CT_Empty; + InitOptions(hWnd); + } + break; + case IDC_COMPUTER: if(HIWORD(wparam) == CBN_SELCHANGE) { @@ -374,6 +383,11 @@ void CPageConfig::InitOptions(HWND hWnd) const SS_CARDTYPE slot3 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3]; const BOOL enableUthernetDialog = slot3 == CT_Empty || slot3 == CT_Uthernet; EnableWindow(GetDlgItem(hWnd, IDC_ETHERNET), enableUthernetDialog); + + const bool bIsSlot3VidHD = slot3 == CT_VidHD; + CheckDlgButton(hWnd, IDC_CHECK_VIDHD_IN_SLOT3, bIsSlot3VidHD ? BST_CHECKED : BST_UNCHECKED); + const BOOL enableVidHD = slot3 == CT_Empty || bIsSlot3VidHD; + EnableWindow(GetDlgItem(hWnd, IDC_CHECK_VIDHD_IN_SLOT3), enableVidHD); } // Config->Computer: Menu item to eApple2Type diff --git a/source/Configuration/PropertySheetHelper.cpp b/source/Configuration/PropertySheetHelper.cpp index 217d79d3..426c0597 100644 --- a/source/Configuration/PropertySheetHelper.cpp +++ b/source/Configuration/PropertySheetHelper.cpp @@ -548,6 +548,8 @@ std::string CPropertySheetHelper::GetCardName(const SS_CARDTYPE CardType) return "4Play"; case CT_SNESMAX: return "SNES MAX"; + case CT_VidHD: + return "VidHD"; default: return "Unknown"; } diff --git a/source/Debugger/Debug.cpp b/source/Debugger/Debug.cpp index 3e68efab..6acb242b 100644 --- a/source/Debugger/Debug.cpp +++ b/source/Debugger/Debug.cpp @@ -6545,6 +6545,11 @@ Update_t _ViewOutput( ViewVideoPage_t iPage, int bVideoModeFlags ) { return _ViewOutput( VIEW_PAGE_2, VF_HIRES | VF_DHIRES | VF_80COL ); } +// Super Hi-Res + Update_t CmdViewOutput_SHR(int nArgs) + { + return _ViewOutput( VIEW_PAGE_1, VF_SHR ); + } // Watches ________________________________________________________________________________________ @@ -8744,6 +8749,9 @@ void DebuggerProcessKey( int keycode ) // Normally any key press takes us out of "Viewing Apple Output" g_nAppMode // VK_F# are already processed, so we can't use them to cycle next video g_nAppMode // if ((g_nAppMode != MODE_LOGO) && (g_nAppMode != MODE_DEBUG)) + + GetVideo().ClearSHRResidue(); // Clear the framebuffer to remove any SHR residue in the borders + DebugVideoMode::Instance().Reset(); UpdateDisplay( UPDATE_ALL ); // 1 return; diff --git a/source/Debugger/Debugger_Commands.cpp b/source/Debugger/Debugger_Commands.cpp index ac056080..c1967512 100644 --- a/source/Debugger/Debugger_Commands.cpp +++ b/source/Debugger/Debugger_Commands.cpp @@ -267,6 +267,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {TEXT("DHGR") , CmdViewOutput_DHGRX , CMD_VIEW_DHGRX , "View Double Hi-res (current page)" }, {TEXT("DHGR1") , CmdViewOutput_DHGR1 , CMD_VIEW_DHGR1 , "View Double Hi-res Page 1" }, {TEXT("DHGR2") , CmdViewOutput_DHGR2 , CMD_VIEW_DHGR2 , "View Double Hi-res Page 2" }, + {TEXT("SHR") , CmdViewOutput_SHR , CMD_VIEW_SHR , "View Super Hi-res" }, // Watch {TEXT("W") , CmdWatch , CMD_WATCH , "Alias for WA (Watch Add)" }, {TEXT("WA") , CmdWatchAdd , CMD_WATCH_ADD , "Add/Update address or symbol to watch" }, diff --git a/source/Debugger/Debugger_Display.cpp b/source/Debugger/Debugger_Display.cpp index 70d7a9ec..9e118b45 100644 --- a/source/Debugger/Debugger_Display.cpp +++ b/source/Debugger/Debugger_Display.cpp @@ -639,8 +639,8 @@ void StretchBltMemToFrameDC(void) int nViewportCX, nViewportCY; win32Frame.GetViewportCXCY(nViewportCX, nViewportCY); - int xdest = win32Frame.IsFullScreen() ? win32Frame.GetFullScreenOffsetX() : 0; - int ydest = win32Frame.IsFullScreen() ? win32Frame.GetFullScreenOffsetY() : 0; + int xdest = win32Frame.IsFullScreen() ? win32Frame.GetFullScreenOffsetX() : GetVideo().GetFrameBufferCentringOffsetX() * win32Frame.GetViewportScale(); + int ydest = win32Frame.IsFullScreen() ? win32Frame.GetFullScreenOffsetY() : GetVideo().GetFrameBufferCentringOffsetY() * win32Frame.GetViewportScale(); int wdest = nViewportCX; int hdest = nViewportCY; @@ -738,6 +738,7 @@ static void PrintGlyph( const int xDst, const int yDst, const int glyph ) // Manual print of character. A lot faster than BitBlt, which must be avoided. int index_src = (CONSOLE_FONT_BITMAP_HEIGHT - 1 - ySrc) * CONSOLE_FONT_NUM_CHARS_PER_ROW * CONSOLE_FONT_GRID_X + xSrc; // font bitmap int index_dst = (DISPLAY_HEIGHT - 1 - yDst) * DEBUG_VIRTUAL_TEXT_WIDTH * CONSOLE_FONT_GRID_X + xDst; // debugger bitmap + for (int yy = 0; yy < CONSOLE_FONT_GRID_Y; yy++) { for (int xx = 0; xx < CONSOLE_FONT_GRID_X; xx++) diff --git a/source/Debugger/Debugger_Types.h b/source/Debugger/Debugger_Types.h index 8ac67eb2..63141dbd 100644 --- a/source/Debugger/Debugger_Types.h +++ b/source/Debugger/Debugger_Types.h @@ -509,6 +509,7 @@ , CMD_VIEW_DHGRX , CMD_VIEW_DHGR1 , CMD_VIEW_DHGR2 + , CMD_VIEW_SHR // Watch , CMD_WATCH // TODO: Deprecated ? , CMD_WATCH_ADD @@ -770,6 +771,7 @@ Update_t CmdViewOutput_DHGRX (int nArgs); Update_t CmdViewOutput_DHGR1 (int nArgs); Update_t CmdViewOutput_DHGR2 (int nArgs); + Update_t CmdViewOutput_SHR (int nArgs); // Watch Update_t CmdWatch (int nArgs); Update_t CmdWatchAdd (int nArgs); diff --git a/source/FrameBase.h b/source/FrameBase.h index bcf267ef..1850cc59 100644 --- a/source/FrameBase.h +++ b/source/FrameBase.h @@ -15,7 +15,7 @@ public: BOOL g_bMultiMon; bool g_bFreshReset; - virtual void Initialize(void) = 0; + virtual void Initialize(bool resetVideoState) = 0; virtual void Destroy(void) = 0; virtual void FrameDrawDiskLEDS() = 0; @@ -26,13 +26,14 @@ public: virtual void FrameSetCursorPosByMousePos() = 0; virtual void SetFullScreenShowSubunitStatus(bool bShow) = 0; - virtual bool GetBestDisplayResolutionForFullScreen(UINT& bestWidth, UINT& bestHeight, UINT userSpecifiedHeight = 0) = 0; + virtual bool GetBestDisplayResolutionForFullScreen(UINT& bestWidth, UINT& bestHeight, UINT userSpecifiedWidth=0, UINT userSpecifiedHeight=0) = 0; virtual int SetViewportScale(int nNewScale, bool bForce = false) = 0; virtual void SetAltEnterToggleFullScreen(bool mode) = 0; virtual void SetLoadedSaveStateFlag(const bool bFlag) = 0; virtual void VideoPresentScreen(void) = 0; + virtual void ResizeWindow(void) = 0; // this function has the same interface as MessageBox in windows.h virtual int FrameMessageBox(LPCSTR lpText, LPCSTR lpCaption, UINT uType) = 0; diff --git a/source/Harddisk.cpp b/source/Harddisk.cpp index b46e124c..01651dbb 100644 --- a/source/Harddisk.cpp +++ b/source/Harddisk.cpp @@ -518,6 +518,11 @@ BYTE __stdcall HarddiskInterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BY { memdirty[dstAddr >> 8] = 0xFF; LPBYTE page = memwrite[dstAddr >> 8]; + if (!page) + { + _ASSERT(0); + break; + } // handle both page-aligned & non-page aligned destinations UINT size = PAGE_SIZE - (dstAddr & 0xff); diff --git a/source/Memory.cpp b/source/Memory.cpp index 21ffc2d0..48af597f 100644 --- a/source/Memory.cpp +++ b/source/Memory.cpp @@ -122,6 +122,10 @@ VIDEO SOFT SWITCHES $C00D W 80COLON Turn on 80 column display $C00E W ALTCHARSETOFF Turn off alternate characters $C00F W ALTCHARSETON Turn on alternate characters + $C022 R/W SCREENCOLOR [IIgs] text foreground and background colors (also VidHD) + $C029 R/W NEWVIDEO [IIgs] Select new video modes (also VidHD) + $C034 R/W BORDERCOLOR [IIgs] b3:0 are border color (also VidHD) + $C035 R/W SHADOW [IIgs] auxmem-to-bank-E1 shadowing (also VidHD) $C050 R/W TEXTOFF Select graphics mode $C051 R/W TEXTON Select text mode $C052 R/W MIXEDOFF Use full screen for graphics @@ -470,6 +474,12 @@ static BYTE __stdcall IORead_C02x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG static BYTE __stdcall IOWrite_C02x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles) { + if (GetCardMgr().QuerySlot(SLOT3) == CT_VidHD) + { + if (addr == 0xC022 || addr == 0xC029) + GetVideo().VideoSetMode(pc, addr, bWrite, d, nExecutedCycles); + } + return TapeWrite(pc, addr, bWrite, d, nExecutedCycles); // $C020 TAPEOUT } @@ -482,6 +492,13 @@ static BYTE __stdcall IORead_C03x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG static BYTE __stdcall IOWrite_C03x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles) { + if (GetCardMgr().QuerySlot(SLOT3) == CT_VidHD) + { + // NB. Writes to $C03x addresses will still toggle the speaker, even with a VidHD present + if (addr == 0xC034 || addr == 0xC035) + GetVideo().VideoSetMode(pc, addr, bWrite, d, nExecutedCycles); + } + return SpkrToggle(pc, addr, bWrite, d, nExecutedCycles); } diff --git a/source/NTSC.cpp b/source/NTSC.cpp index 0eaccfeb..a8691a50 100644 --- a/source/NTSC.cpp +++ b/source/NTSC.cpp @@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "Memory.h" // MemGetMainPtr(), MemGetAuxPtr(), MemGetAnnunciator() #include "Interface.h" // GetFrameBuffer() #include "RGBMonitor.h" + #include "VidHD.h" #include "NTSC_CharSet.h" @@ -100,11 +101,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #define VIDEO_SCANNER_HORZ_START 25 // first displayable horz scanner index #define VIDEO_SCANNER_Y_MIXED 160 // num scanlins for mixed graphics + text #define VIDEO_SCANNER_Y_DISPLAY 192 // max displayable scanlines + #define VIDEO_SCANNER_Y_DISPLAY_IIGS 200 - // these are initialized in NTSC_VideoInit + // These 3 vars are initialized in NTSC_VideoInit() static bgra_t* g_pVideoAddress = 0; - static bgra_t *g_pScanLines[VIDEO_SCANNER_Y_DISPLAY*2]; // To maintain the 280x192 aspect ratio for 560px width, we double every scan line -> 560x384 - static UINT g_kFrameBufferWidth; + // To maintain the 280x192 aspect ratio for 560px width, we double every scan line -> 560x384 + // NB. For IIgs SHR, the 320x200 is again doubled (to 640x400), but this gives a ~16:9 ratio, when 4:3 is probably required (ie. stretch height from 200 to 240) + static bgra_t* g_pScanLines[VIDEO_SCANNER_Y_DISPLAY_IIGS * 2]; + static UINT g_kFrameBufferWidth = 0; static unsigned short (*g_pHorzClockOffset)[VIDEO_SCANNER_MAX_HORZ] = 0; @@ -348,6 +352,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA static void updateScreenText80RGB ( long cycles6502 ); static void updateScreenDoubleHires80Simplified(long cycles6502); static void updateScreenDoubleHires80RGB(long cycles6502); + static void updateScreenSHR(long cycles6502); //=========================================================================== static void set_csbits() @@ -712,13 +717,37 @@ inline void updateVideoScannerHorzEOL() } } +inline void updateVideoScannerHorzEOL_SHR() +{ + if (VIDEO_SCANNER_MAX_HORZ == ++g_nVideoClockHorz) + { + g_nVideoClockHorz = 0; + + if (++g_nVideoClockVert == g_videoScannerMaxVert) + { + g_nVideoClockVert = 0; + } + + if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY_IIGS) + { + updateVideoScannerAddress(); + } + } +} + //=========================================================================== inline void updateVideoScannerAddress() { if (g_nVideoMixed && g_nVideoClockVert >= VIDEO_SCANNER_Y_MIXED && GetVideo().GetVideoRefreshRate() == VR_50HZ) // GH#763 g_nColorBurstPixels = 0; // instantaneously kill color-burst! - g_pVideoAddress = g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY ? g_pScanLines[2*g_nVideoClockVert] : g_pScanLines[0]; + if (g_pFuncUpdateGraphicsScreen == updateScreenSHR) + { + g_pVideoAddress = g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY_IIGS ? g_pScanLines[2 * g_nVideoClockVert] : g_pScanLines[0]; + return; + } + + g_pVideoAddress = g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY ? g_pScanLines[2 * g_nVideoClockVert] : g_pScanLines[0]; // Adjust, as these video styles have 2x 14M pixels of pre-render // NB. For VT_COLOR_MONITOR_NTSC, also check color-burst so that TEXT and MIXED(HGR+TEXT) render the TEXT at the same offset (GH#341) @@ -740,6 +769,9 @@ inline void updateVideoScannerAddress() g_pVideoAddress -= 1; } + // Centre the older //e video modes when running with a VidHD + g_pVideoAddress += GetVideo().GetFrameBufferCentringValue(); + g_nColorPhaseNTSC = INITIAL_COLOR_PHASE; g_nLastColumnPixelNTSC = 0; g_nSignalBitsNTSC = 0; @@ -760,7 +792,6 @@ INLINE uint16_t getVideoScannerAddressHGR() APPLE_IIE_HORZ_CLOCK_OFFSET[g_nVideoClockVert/64][g_nVideoClockHorz] + (g_nHiresPage * 0x2000)); } - // Non-Inline _________________________________________________________ // Build the 4 phase chroma lookup table @@ -1709,6 +1740,42 @@ void updateScreenText80RGB(long cycles6502) } } +//=========================================================================== +void updateScreenSHR(long cycles6502) +{ + for (; cycles6502 > 0; --cycles6502) + { + // 2 pixels per byte in 320-pixel mode = 160 bytes/scanline + // 4 pixels per byte in 640-pixel mode = 160 bytes/scanline + const UINT kBytesPerScanline = 160; + const UINT kBytesPerCycle = 4; + uint16_t addr = 0x2000 + kBytesPerScanline * g_nVideoClockVert + kBytesPerCycle * (g_nVideoClockHorz - VIDEO_SCANNER_HORZ_START); + + if (g_nVideoClockVert < VIDEO_SCANNER_Y_DISPLAY_IIGS) + { + if (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_START) + { + uint32_t* pAux = (uint32_t*) MemGetAuxPtr(addr); // 8 pixels (320 mode) / 16 pixels (640 mode) + uint32_t a = pAux[0]; + + uint8_t* pControl = MemGetAuxPtr(0x9D00 + g_nVideoClockVert); // scan-line control byte + uint8_t c = pControl[0]; + + bool is640Mode = c & 0x80; + bool isColorFillMode = c & 0x20; + UINT paletteSelectCode = c & 0xf; + const UINT kColorsPerPalette = 16; + const UINT kColorSize = 2; + uint16_t addrPalette = 0x9E00 + paletteSelectCode * kColorsPerPalette * kColorSize; + + VidHDCard::UpdateSHRCell(is640Mode, isColorFillMode, addrPalette, g_pVideoAddress, a); + g_pVideoAddress += 16; + } + } + updateVideoScannerHorzEOL_SHR(); + } +} + // Functions (Public) _____________________________________________________________________________ //=========================================================================== @@ -1803,6 +1870,19 @@ void NTSC_SetVideoTextMode( int cols ) //=========================================================================== void NTSC_SetVideoMode( uint32_t uVideoModeFlags, bool bDelay/*=false*/ ) { + if (uVideoModeFlags & VF_SHR) + { + g_pFuncUpdateGraphicsScreen = updateScreenSHR; + g_pFuncUpdateTextScreen = updateScreenSHR; + return; + } + + if (g_pFuncUpdateGraphicsScreen == updateScreenSHR && !(uVideoModeFlags & VF_SHR)) + { + // Was SHR mode, so clear the framebuffer to remove any SHR residue in the borders + GetVideo().ClearFrameBuffer(); + } + if (bDelay && !g_bFullSpeed) { // (GH#670) NB. if g_bFullSpeed then NTSC_VideoUpdateCycles() won't be called on the next 6502 opcode. @@ -1812,7 +1892,6 @@ void NTSC_SetVideoMode( uint32_t uVideoModeFlags, bool bDelay/*=false*/ ) return; } - g_nVideoMixed = uVideoModeFlags & VF_MIXED; g_nVideoCharSet = GetVideo().VideoGetSWAltCharSet() ? 1 : 0; @@ -2104,7 +2183,7 @@ void NTSC_VideoInit( uint8_t* pFramebuffer ) // wsVideoInit g_kFrameBufferWidth = GetVideo().GetFrameBufferWidth(); - for (int y = 0; y < (VIDEO_SCANNER_Y_DISPLAY*2); y++) + for (int y = 0; y < (VIDEO_SCANNER_Y_DISPLAY_IIGS*2); y++) { uint32_t offset = sizeof(bgra_t) * GetVideo().GetFrameBufferWidth() * ((GetVideo().GetFrameBufferHeight() - 1) - y - GetVideo().GetFrameBufferBorderHeight()) diff --git a/source/SaveState.cpp b/source/SaveState.cpp index d1bda6d3..0c4b12e5 100644 --- a/source/SaveState.cpp +++ b/source/SaveState.cpp @@ -50,6 +50,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "SNESMAX.h" #include "Speaker.h" #include "Speech.h" +#include "VidHD.h" #include "z80emu.h" #include "Configuration/Config.h" @@ -288,10 +289,6 @@ static void ParseUnitApple2(YamlLoadHelper& yamlLoadHelper, UINT version) SpkrLoadSnapshot(yamlLoadHelper); GetVideo().VideoLoadSnapshot(yamlLoadHelper, version); MemLoadSnapshot(yamlLoadHelper, version); - - // g_Apple2Type may've changed: so redraw frame (title, buttons, leds, etc) - GetVideo().VideoReinitialize(true); // g_CharsetType changed - GetFrame().FrameUpdateApple2Type(); // Calls VideoRedrawScreen() before the aux mem has been loaded (so if DHGR is enabled, then aux mem will be zeros at this stage) } //--- @@ -381,6 +378,10 @@ static void ParseSlots(YamlLoadHelper& yamlLoadHelper, UINT unitVersion) { type = CT_SNESMAX; } + else if (card == VidHDCard::GetSnapshotCardName()) + { + type = CT_VidHD; + } else { throw std::string("Slots: Unknown card: " + card); // todo: don't throw - just ignore & continue @@ -482,6 +483,7 @@ static void Snapshot_LoadState_v2(void) GetPravets().Reset(); KeybReset(); + GetVideo().SetVidHD(false); // Set true later only if VidHDCard is instantiated GetVideo().VideoResetState(); GetVideo().SetVideoRefreshRate(VR_60HZ); // Default to 60Hz as older save-states won't contain refresh rate MB_InitializeForLoadingSnapshot(); // GH#609 @@ -520,6 +522,12 @@ static void Snapshot_LoadState_v2(void) DebugReset(); if (g_nAppMode == MODE_DEBUG) DebugDisplay(TRUE); + + frame.Initialize(false); // don't reset the video state + frame.ResizeWindow(); + + // g_Apple2Type may've changed: so reload button bitmaps & redraw frame (title, buttons, leds, etc) + frame.FrameUpdateApple2Type(); // NB. Calls VideoRedrawScreen() } catch(std::string szMessage) { diff --git a/source/Utilities.cpp b/source/Utilities.cpp index bd2f2566..ff8be7d9 100644 --- a/source/Utilities.cpp +++ b/source/Utilities.cpp @@ -541,6 +541,8 @@ void ResetMachineState() GetCardMgr().GetDisk2CardMgr().Reset(true); if (GetCardMgr().QuerySlot(SLOT7) == CT_GenericHDD) GetCardMgr().GetRef(SLOT7).Reset(true); + if (GetCardMgr().QuerySlot(SLOT3) == CT_VidHD) + GetCardMgr().GetRef(SLOT3).Reset(true); g_bFullSpeed = 0; // Might've hit reset in middle of InternalCpuExecute() - so beep may get (partially) muted MemReset(); // calls CpuInitialize(), CNoSlotClock.Reset() @@ -595,7 +597,9 @@ void CtrlReset() GetPravets().Reset(); GetCardMgr().GetDisk2CardMgr().Reset(); if (GetCardMgr().QuerySlot(SLOT7) == CT_GenericHDD) - GetCardMgr().GetRef(SLOT7).Reset(true); + GetCardMgr().GetRef(SLOT7).Reset(false); + if (GetCardMgr().QuerySlot(SLOT3) == CT_VidHD) + GetCardMgr().GetRef(SLOT3).Reset(false); KeybReset(); if (GetCardMgr().IsSSCInstalled()) GetCardMgr().GetSSC()->CommReset(); diff --git a/source/VidHD.cpp b/source/VidHD.cpp new file mode 100644 index 00000000..59284e05 --- /dev/null +++ b/source/VidHD.cpp @@ -0,0 +1,189 @@ +/* + AppleWin : An Apple //e emulator for Windows + + Copyright (C) 1994-1996, Michael O'Brien + Copyright (C) 1999-2001, Oliver Schmidt + Copyright (C) 2002-2005, Tom Charlesworth + Copyright (C) 2006-2021, Tom Charlesworth, Michael Pohoreski + + AppleWin is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + AppleWin is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with AppleWin; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* + Emulate a VidHD card (Blue Shift Inc) + + Allows any Apple II to support the IIgs' 320x200 and 640x200 256-colour Super High-Res video modes. + Currently only a //e with 64K aux memory supports SHR mode. + + NB. The extended text modes 80x45, 120x67, 240x135 (and setting FG/BG colours) are not supported yet. +*/ + +#include "StdAfx.h" + +#include "Memory.h" +#include "NTSC.h" +#include "Video.h" +#include "VidHD.h" +#include "YamlHelper.h" + +void VidHDCard::Reset(const bool powerCycle) +{ + m_NEWVIDEO = 0; + GetVideo().SetVideoMode(GetVideo().GetVideoMode() & ~VF_SHR); +} + +void VidHDCard::InitializeIO(LPBYTE pCxRomPeripheral) +{ + RegisterIoHandler(m_slot, IO_Null, IO_Null, &VidHDCard::IORead, IO_Null, this, NULL); +} + +BYTE __stdcall VidHDCard::IORead(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles) +{ + // Return magic bytes (from the VidHD firmware) for VidHD detection + switch (addr & 0xff) + { + case 0: return 0x24; + case 1: return 0xEA; + case 2: return 0x4C; + } + return IO_Null(pc, addr, bWrite, value, nExecutedCycles); +} + +// NB. VidHD has no support for reading the IIgs video registers (from an earlier Apple II) +void VidHDCard::VideoIOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles) +{ + switch (addr & 0xff) + { + case 0x22: // SCREENCOLOR + m_SCREENCOLOR = value; + break; + case 0x29: // NEWVIDEO + m_NEWVIDEO = value; + break; + case 0x34: // BORDERCOLOR + m_BORDERCOLOR = value; + break; + case 0x35: // SHADOW + m_SHADOW = value; + break; + default: + _ASSERT(0); + } +} + +//=========================================================================== + +#pragma pack(push) +#pragma pack(1) // Ensure struct is packed +struct Color +{ + USHORT blue : 4; + USHORT green : 4; + USHORT red : 4; + USHORT reserved : 4; +}; +#pragma pack(pop) + +bgra_t ConvertIIgs2RGB(Color color) +{ + bgra_t rgb = { 0 }; + rgb.r = color.red * 16; + rgb.g = color.green * 16; + rgb.b = color.blue * 16; + return rgb; +} + +void VidHDCard::UpdateSHRCell(bool is640Mode, bool isColorFillMode, uint16_t addrPalette, bgra_t* pVideoAddress, uint32_t a) +{ + _ASSERT(!is640Mode); // to do: test this mode + + Color* palette = (Color*) MemGetAuxPtr(addrPalette); + + for (UINT i = 0; i < 4; i++) + { + if (!is640Mode) // 320 mode + { + BYTE pixel1 = (a >> 4) & 0xf; + bgra_t color1 = ConvertIIgs2RGB(palette[pixel1]); + if (isColorFillMode && pixel1 == 0) color1 = *(pVideoAddress - 1); + *pVideoAddress++ = color1; + *pVideoAddress++ = color1; + + BYTE pixel2 = a & 0xf; + bgra_t color2 = ConvertIIgs2RGB(palette[pixel2]); + if (isColorFillMode && pixel2 == 0) color2 = color1; + *pVideoAddress++ = color2; + *pVideoAddress++ = color2; + } + else // 640 mode - see IIgs Hardware Ref, Pg.96, Table4-21 'Color Selection in 640 mode' + { + BYTE pixel1 = (a >> 6) & 0x3; + bgra_t color1 = ConvertIIgs2RGB(palette[0x8 + pixel1]); + *pVideoAddress++ = color1; + + BYTE pixel2 = (a >> 4) & 0x3; + bgra_t color2 = ConvertIIgs2RGB(palette[0xC + pixel2]); + *pVideoAddress++ = color2; + + BYTE pixel3 = (a >> 2) & 0x3; + bgra_t color3 = ConvertIIgs2RGB(palette[0x0 + pixel3]); + *pVideoAddress++ = color3; + + BYTE pixel4 = a & 0x3; + bgra_t color4 = ConvertIIgs2RGB(palette[0x4 + pixel4]); + *pVideoAddress++ = color4; + } + + a >>= 8; + } +} + +//=========================================================================== + +static const UINT kUNIT_VERSION = 1; + +#define SS_YAML_KEY_SCREEN_COLOR "Screen Color" +#define SS_YAML_KEY_NEW_VIDEO "New Video" +#define SS_YAML_KEY_BORDER_COLOR "Border Color" +#define SS_YAML_KEY_SHADOW "Shadow" + +std::string VidHDCard::GetSnapshotCardName(void) +{ + static const std::string name("VidHD"); + return name; +} + +void VidHDCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper) +{ + YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_VERSION); + + YamlSaveHelper::Label unit(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE); + yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SCREEN_COLOR, m_SCREENCOLOR); + yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_NEW_VIDEO, m_NEWVIDEO); + yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_BORDER_COLOR, m_BORDERCOLOR); + yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SHADOW, m_SHADOW); +} + +bool VidHDCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version) +{ + if (version < 1 || version > kUNIT_VERSION) + throw std::string("Card: wrong version"); + + m_SCREENCOLOR = yamlLoadHelper.LoadUint(SS_YAML_KEY_SCREEN_COLOR); + m_NEWVIDEO = yamlLoadHelper.LoadUint(SS_YAML_KEY_NEW_VIDEO); + m_BORDERCOLOR = yamlLoadHelper.LoadUint(SS_YAML_KEY_BORDER_COLOR); + m_SHADOW = yamlLoadHelper.LoadUint(SS_YAML_KEY_SHADOW); + + return true; +} diff --git a/source/VidHD.h b/source/VidHD.h new file mode 100644 index 00000000..8652c278 --- /dev/null +++ b/source/VidHD.h @@ -0,0 +1,44 @@ +#pragma once + +#include "Card.h" +#include "Interface.h" + +class VidHDCard : public Card +{ +public: + VidHDCard(UINT slot) : + Card(CT_VidHD, slot) + { + m_SCREENCOLOR = 0; + m_NEWVIDEO = 0; + m_BORDERCOLOR = 0; + m_SHADOW = 0; + + GetVideo().SetVidHD(true); + } + virtual ~VidHDCard(void) {} + + virtual void Init(void) {} + virtual void Reset(const bool powerCycle); + virtual void Update(const ULONG nExecutedCycles) {} + virtual void InitializeIO(LPBYTE pCxRomPeripheral); + + static BYTE __stdcall IORead(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles); + + void VideoIOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG nExecutedCycles); + + bool IsSHR(void) { return (m_NEWVIDEO & 0xC0) == 0xC0; } // 11000001 = Enable SHR(b7) | Linearize SHR video memory(b6) + bool IsDHGRBlackAndWhite(void) { return (m_NEWVIDEO & (1 << 5)) ? true : false; } + + static void UpdateSHRCell(bool is640Mode, bool isColorFillMode, uint16_t addrPalette, bgra_t* pVideoAddress, uint32_t a); + + static std::string GetSnapshotCardName(void); + virtual void SaveSnapshot(YamlSaveHelper& yamlSaveHelper); + virtual bool LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version); + +private: + BYTE m_SCREENCOLOR; + BYTE m_NEWVIDEO; + BYTE m_BORDERCOLOR; + BYTE m_SHADOW; +}; diff --git a/source/Video.cpp b/source/Video.cpp index f096cd31..cc030d52 100644 --- a/source/Video.cpp +++ b/source/Video.cpp @@ -29,6 +29,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "StdAfx.h" #include "Video.h" +#include "CardManager.h" #include "Core.h" #include "CPU.h" #include "Log.h" @@ -36,6 +37,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "Registry.h" #include "NTSC.h" #include "RGBMonitor.h" +#include "VidHD.h" #include "YamlHelper.h" #define SW_80COL (g_uVideoMode & VF_80COL) @@ -45,6 +47,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #define SW_MIXED (g_uVideoMode & VF_MIXED) #define SW_PAGE2 (g_uVideoMode & VF_PAGE2) #define SW_TEXT (g_uVideoMode & VF_TEXT) +#define SW_SHR (g_uVideoMode & VF_SHR) //------------------------------------- @@ -91,14 +94,12 @@ const char* const Video::g_apVideoModeDesc[NUM_VIDEO_MODES] = UINT Video::GetFrameBufferBorderlessWidth(void) { - static const UINT uFrameBufferBorderlessW = 560; // 560 = Double Hi-Res - return uFrameBufferBorderlessW; + return HasVidHD() ? kVideoWidthIIgs : kVideoWidthII; } UINT Video::GetFrameBufferBorderlessHeight(void) { - static const UINT uFrameBufferBorderlessH = 384; // 384 = Double Scan Line - return uFrameBufferBorderlessH; + return HasVidHD() ? kVideoHeightIIgs : kVideoHeightII; } // NB. These border areas are not visible (... and these border areas are unrelated to the 3D border below) @@ -124,6 +125,29 @@ UINT Video::GetFrameBufferHeight(void) return GetFrameBufferBorderlessHeight() + 2 * GetFrameBufferBorderHeight(); } +UINT Video::GetFrameBufferCentringOffsetX(void) +{ + return HasVidHD() ? ((kVideoWidthIIgs - kVideoWidthII) / 2) : 0; +} + +UINT Video::GetFrameBufferCentringOffsetY(void) +{ + return HasVidHD() ? ((kVideoHeightIIgs - kVideoHeightII) / 2) : 0; +} + +int Video::GetFrameBufferCentringValue(void) +{ + int value = 0; + + if (HasVidHD()) + { + value -= GetFrameBufferCentringOffsetY() * GetFrameBufferWidth(); + value += GetFrameBufferCentringOffsetX(); + } + + return value; +} + //=========================================================================== void Video::VideoReinitialize(bool bInitVideoScannerAddress) @@ -151,12 +175,15 @@ void Video::VideoResetState(void) //=========================================================================== -BYTE Video::VideoSetMode(WORD, WORD address, BYTE write, BYTE, ULONG uExecutedCycles) +BYTE Video::VideoSetMode(WORD pc, WORD address, BYTE write, BYTE d, ULONG uExecutedCycles) { - address &= 0xFF; - const uint32_t oldVideoMode = g_uVideoMode; + VidHDCard* vidHD = NULL; + if (GetCardMgr().QuerySlot(SLOT3) == CT_VidHD) + vidHD = dynamic_cast(GetCardMgr().GetObj(SLOT3)); + + address &= 0xFF; switch (address) { case 0x00: g_uVideoMode &= ~VF_80STORE; break; @@ -165,6 +192,10 @@ BYTE Video::VideoSetMode(WORD, WORD address, BYTE write, BYTE, ULONG uExecutedCy case 0x0D: if (!IS_APPLE2){g_uVideoMode |= VF_80COL; NTSC_SetVideoTextMode(80);}; break; case 0x0E: if (!IS_APPLE2) g_nAltCharSetOffset = 0; break; // Alternate char set off case 0x0F: if (!IS_APPLE2) g_nAltCharSetOffset = 256; break; // Alternate char set on + case 0x22: if (vidHD) vidHD->VideoIOWrite(pc, address, write, d, uExecutedCycles); break; // VidHD IIgs video mode register + case 0x29: if (vidHD) vidHD->VideoIOWrite(pc, address, write, d, uExecutedCycles); break; // VidHD IIgs video mode register + case 0x34: if (vidHD) vidHD->VideoIOWrite(pc, address, write, d, uExecutedCycles); break; // VidHD IIgs video mode register + case 0x35: if (vidHD) vidHD->VideoIOWrite(pc, address, write, d, uExecutedCycles); break; // VidHD IIgs video mode register case 0x50: g_uVideoMode &= ~VF_TEXT; break; case 0x51: g_uVideoMode |= VF_TEXT; break; case 0x52: g_uVideoMode &= ~VF_MIXED; break; @@ -177,6 +208,11 @@ BYTE Video::VideoSetMode(WORD, WORD address, BYTE write, BYTE, ULONG uExecutedCy case 0x5F: if (!IS_APPLE2) g_uVideoMode &= ~VF_DHIRES; break; } + if (vidHD && vidHD->IsSHR()) + g_uVideoMode |= VF_SHR; + else + g_uVideoMode &= ~VF_SHR; + if (!IS_APPLE2) RGB_SetVideoMode(address); @@ -185,7 +221,7 @@ BYTE Video::VideoSetMode(WORD, WORD address, BYTE write, BYTE, ULONG uExecutedCy if ((oldVideoMode ^ g_uVideoMode) & (VF_TEXT|VF_MIXED)) delay = true; - NTSC_SetVideoMode( g_uVideoMode, delay ); + NTSC_SetVideoMode(g_uVideoMode, delay); return MemReadFloatingBus(uExecutedCycles); } @@ -754,16 +790,18 @@ const char* Video::VideoGetAppWindowTitle(void) return apVideoMonitorModeDesc[ GetVideoRefreshRate() == VR_60HZ ? 0 : 1 ]; // NTSC or PAL } - -void Video::Initialize(uint8_t* frameBuffer) +void Video::Initialize(uint8_t* frameBuffer, bool resetState) { SetFrameBuffer(frameBuffer); - // RESET THE VIDEO MODE SWITCHES AND THE CHARACTER SET OFFSET - VideoResetState(); + if (resetState) + { + // RESET THE VIDEO MODE SWITCHES AND THE CHARACTER SET OFFSET + VideoResetState(); + } // DRAW THE SOURCE IMAGE INTO THE SOURCE BIT BUFFER - memset(GetFrameBuffer(), 0, GetFrameBufferWidth() * GetFrameBufferHeight() * sizeof(bgra_t)); + ClearFrameBuffer(); // CREATE THE OFFSET TABLE FOR EACH SCAN LINE IN THE FRAME BUFFER NTSC_VideoInit(GetFrameBuffer()); @@ -796,3 +834,15 @@ void Video::VideoRefreshBuffer(uint32_t uRedrawWholeScreenVideoMode, bool bRedra NTSC_VideoRedrawWholeScreen(); } } + +void Video::ClearFrameBuffer(void) +{ + memset(GetFrameBuffer(), 0, GetFrameBufferWidth() * GetFrameBufferHeight() * sizeof(bgra_t)); +} + +// Called when entering debugger, and after viewing Apple II video screen from debugger +void Video::ClearSHRResidue(void) +{ + ClearFrameBuffer(); + GetFrame().VideoPresentScreen(); +} diff --git a/source/Video.h b/source/Video.h index acde3915..e28da3a3 100644 --- a/source/Video.h +++ b/source/Video.h @@ -55,7 +55,8 @@ enum VideoFlag_e VF_80STORE= 0x00000008, VF_MIXED = 0x00000010, VF_PAGE2 = 0x00000020, - VF_TEXT = 0x00000040 + VF_TEXT = 0x00000040, + VF_SHR = 0x00000080 // For VidHD's support for IIgs SHR video modes }; enum AppleFont_e @@ -193,9 +194,12 @@ public: g_nMonochromeRGB = RGB(0xC0,0xC0,0xC0); g_videoRomSize = 0; g_videoRomRockerSwitch = false; + m_hasVidHD = false; } - void Initialize(uint8_t* frameBuffer); // Do not call directly. Call FrameBase::Initialize() + ~Video(void){} + + void Initialize(uint8_t* frameBuffer, bool resetState); // Do not call directly. Call FrameBase::Initialize() void Destroy(void); // Call FrameBase::Destroy() uint8_t* GetFrameBuffer(void) { return g_pFramebufferbits; } @@ -207,6 +211,9 @@ public: UINT GetFrameBufferBorderHeight(void); UINT GetFrameBufferWidth(void); UINT GetFrameBufferHeight(void); + UINT GetFrameBufferCentringOffsetX(void); + UINT GetFrameBufferCentringOffsetY(void); + int GetFrameBufferCentringValue(void); COLORREF GetMonochromeRGB(void) { return g_nMonochromeRGB; } void SetMonochromeRGB(COLORREF colorRef) { g_nMonochromeRGB = colorRef; } @@ -214,6 +221,8 @@ public: void VideoReinitialize(bool bInitVideoScannerAddress); void VideoResetState(void); void VideoRefreshBuffer(uint32_t uRedrawWholeScreenVideoMode, bool bRedrawWholeScreen); + void ClearFrameBuffer(void); + void ClearSHRResidue(void); enum VideoScanner_e {VS_FullAddr, VS_PartialAddrV, VS_PartialAddrH}; WORD VideoGetScannerAddress(DWORD nCycles, VideoScanner_e videoScannerAddr = VS_FullAddr); @@ -268,6 +277,9 @@ public: const char* VideoGetAppWindowTitle(void); const char* GetVideoChoices(void) { return g_aVideoChoices; } + bool HasVidHD(void) { return m_hasVidHD; } + void SetVidHD(bool hasVidHD) { m_hasVidHD = hasVidHD; } + static const UINT kVideoRomSize2K = 1024*2; static const UINT kVideoRomSize4K = kVideoRomSize2K*2; @@ -275,7 +287,6 @@ protected: uint8_t *g_pFramebufferbits; private: - void SetFrameBuffer(uint8_t* frameBuffer) { g_pFramebufferbits = frameBuffer; } std::string VideoGetSnapshotStructName(void); @@ -285,6 +296,7 @@ private: VideoStyle_e g_eVideoStyle; bool g_bVideoScannerNTSC; // NTSC video scanning (or PAL) COLORREF g_nMonochromeRGB; // saved to Registry + bool m_hasVidHD; WinBmpHeader_t g_tBmpHeader; @@ -309,4 +321,10 @@ private: static const char m_szModeDesc7[]; static const char m_szModeDesc8[]; static const char* const g_apVideoModeDesc[NUM_VIDEO_MODES]; + + static const UINT kVideoHeightII = 192*2; + static const UINT kVideoHeightIIgs = 200*2; + + static const UINT kVideoWidthII = 280*2; + static const UINT kVideoWidthIIgs = 320*2; }; diff --git a/source/Windows/AppleWin.cpp b/source/Windows/AppleWin.cpp index ccc9f913..65f683fd 100644 --- a/source/Windows/AppleWin.cpp +++ b/source/Windows/AppleWin.cpp @@ -667,6 +667,7 @@ static void OneTimeInitialization(HINSTANCE passinstance) // DO INITIALIZATION THAT MUST BE REPEATED FOR A RESTART static void RepeatInitialization(void) { + GetVideo().SetVidHD(false); // Set true later only if VidHDCard is instantiated ResetToLogoMode(); // NB. g_OldAppleWinVersion needed by LoadConfiguration() -> Config_Load_Video() @@ -731,13 +732,6 @@ static void RepeatInitialization(void) JoyInitialize(); LogFileOutput("Main: JoyInitialize()\n"); - GetFrame().Initialize(); // g_pFramebufferinfo been created now & COM init'ed - LogFileOutput("Main: VideoInitialize()\n"); - - LogFileOutput("Main: FrameCreateWindow() - pre\n"); - Win32Frame::GetWin32Frame().FrameCreateWindow(); // GetFrame().g_hFrameWindow is now valid - LogFileOutput("Main: FrameCreateWindow() - post\n"); - // Init palette color VideoSwitchVideocardPalette(RGB_GetVideocard(), GetVideo().GetVideoType()); @@ -753,6 +747,11 @@ static void RepeatInitialization(void) if (g_cmdLine.bSlotEmpty[SLOT6]) GetCardMgr().Remove(SLOT6); + if (g_cmdLine.slotInsert[SLOT3] != CT_Empty && g_cmdLine.slotInsert[SLOT3] == CT_VidHD) // For now just support VidHD in slot 3 + { + GetCardMgr().Insert(SLOT3, g_cmdLine.slotInsert[SLOT3]); + } + if (g_cmdLine.slotInsert[SLOT5] != CT_Empty) { if (GetCardMgr().QuerySlot(SLOT4) == CT_MockingboardC && g_cmdLine.slotInsert[SLOT5] != CT_MockingboardC) // Currently MB occupies slot4+5 when enabled @@ -764,6 +763,35 @@ static void RepeatInitialization(void) GetCardMgr().Insert(SLOT5, g_cmdLine.slotInsert[SLOT5]); } + // Create window after inserting/removing VidHD card (as it affects width & height) + { + Win32Frame::GetWin32Frame().SetViewportScale(Win32Frame::GetWin32Frame().GetViewportScale(), true); + + GetFrame().Initialize(true); // g_pFramebufferinfo been created now & COM init'ed + LogFileOutput("Main: VideoInitialize()\n"); + + LogFileOutput("Main: FrameCreateWindow() - pre\n"); + Win32Frame::GetWin32Frame().FrameCreateWindow(); // GetFrame().g_hFrameWindow is now valid + LogFileOutput("Main: FrameCreateWindow() - post\n"); + } + + // Set best W,H resolution after inserting/removing VidHD card + if (g_cmdLine.bestFullScreenResolution || g_cmdLine.userSpecifiedWidth || g_cmdLine.userSpecifiedHeight) + { + bool res = false; + UINT bestWidth = 0, bestHeight = 0; + + if (g_cmdLine.bestFullScreenResolution) + res = GetFrame().GetBestDisplayResolutionForFullScreen(bestWidth, bestHeight); + else + res = GetFrame().GetBestDisplayResolutionForFullScreen(bestWidth, bestHeight, g_cmdLine.userSpecifiedWidth, g_cmdLine.userSpecifiedHeight); + + if (res) + LogFileOutput("Best resolution for -fs-height/height=x switch(es): Width=%d, Height=%d\n", bestWidth, bestHeight); + else + LogFileOutput("Failed to set parameter for -fs-width/height=x switch(es)\n"); + } + // Pre: may need g_hFrameWindow for MessageBox errors // Post: may enable HDD, required for MemInitialize()->MemInitializeIO() { diff --git a/source/Windows/Win32Frame.cpp b/source/Windows/Win32Frame.cpp index f78ce332..06beb8f0 100644 --- a/source/Windows/Win32Frame.cpp +++ b/source/Windows/Win32Frame.cpp @@ -33,16 +33,12 @@ Win32Frame::Win32Frame() g_bFrameActive = false; g_windowMinimized = false; g_bFullScreen_ShowSubunitStatus = true; - g_win_fullscreen_scale = 1; g_win_fullscreen_offsetx = 0; g_win_fullscreen_offsety = 0; m_bestWidthForFullScreen = 0; m_bestHeightForFullScreen = 0; m_changedDisplaySettings = false; - g_nViewportCX = GetVideo().GetFrameBufferBorderlessWidth() * kDEFAULT_VIEWPORT_SCALE; - g_nViewportCY = GetVideo().GetFrameBufferBorderlessHeight() * kDEFAULT_VIEWPORT_SCALE; - g_nViewportScale = kDEFAULT_VIEWPORT_SCALE; // saved REGSAVE g_nMaxViewportScale = kDEFAULT_VIEWPORT_SCALE; // Max scale in Windowed mode with borders, buttons etc (full-screen may be +1) btnfacebrush = (HBRUSH)0; @@ -52,8 +48,6 @@ Win32Frame::Win32Frame() buttonactive = -1; buttondown = -1; buttonover = -1; - buttonx = BUTTONX; // NB. macro uses g_nViewportCX - buttony = BUTTONY; g_hFrameDC = (HDC)0; memset(&framerect, 0, sizeof(framerect)); @@ -76,9 +70,12 @@ Win32Frame::Win32Frame() g_eStatusDrive1 = DISK_STATUS_OFF; g_eStatusDrive2 = DISK_STATUS_OFF; + + // Set g_nViewportScale, g_nViewportCX, g_nViewportCY & buttonx, buttony + SetViewportScale(kDEFAULT_VIEWPORT_SCALE, true); } -void Win32Frame::videoCreateDIBSection(Video & video) +void Win32Frame::VideoCreateDIBSection(bool resetVideoState) { // CREATE THE DEVICE CONTEXT HWND window = GetDesktopWindow(); @@ -91,7 +88,10 @@ void Win32Frame::videoCreateDIBSection(Video & video) // CREATE THE FRAME BUFFER DIB SECTION if (g_hDeviceBitmap) + { DeleteObject(g_hDeviceBitmap); + GetVideo().Destroy(); + } uint8_t* pFramebufferbits; @@ -102,29 +102,33 @@ void Win32Frame::videoCreateDIBSection(Video & video) (LPVOID*)&pFramebufferbits, 0, 0 ); SelectObject(g_hDeviceDC, g_hDeviceBitmap); - video.Initialize(pFramebufferbits); + GetVideo().Initialize(pFramebufferbits, resetVideoState); } -void Win32Frame::Initialize(void) +void Win32Frame::Initialize(bool resetVideoState) { - // LOAD THE LOGO - g_hLogoBitmap = LoadBitmap(g_hInstance, MAKEINTRESOURCE(IDB_APPLEWIN)); + if (g_hLogoBitmap == NULL) + { + // LOAD THE LOGO + g_hLogoBitmap = LoadBitmap(g_hInstance, MAKEINTRESOURCE(IDB_APPLEWIN)); + } + + if (g_pFramebufferinfo) + delete[] g_pFramebufferinfo; // CREATE A BITMAPINFO STRUCTURE FOR THE FRAME BUFFER g_pFramebufferinfo = (LPBITMAPINFO) new BYTE[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)]; - Video & video = GetVideo(); - memset(g_pFramebufferinfo, 0, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)); g_pFramebufferinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - g_pFramebufferinfo->bmiHeader.biWidth = video.GetFrameBufferWidth(); - g_pFramebufferinfo->bmiHeader.biHeight = video.GetFrameBufferHeight(); + g_pFramebufferinfo->bmiHeader.biWidth = GetVideo().GetFrameBufferWidth(); + g_pFramebufferinfo->bmiHeader.biHeight = GetVideo().GetFrameBufferHeight(); g_pFramebufferinfo->bmiHeader.biPlanes = 1; g_pFramebufferinfo->bmiHeader.biBitCount = 32; g_pFramebufferinfo->bmiHeader.biCompression = BI_RGB; g_pFramebufferinfo->bmiHeader.biClrUsed = 0; - videoCreateDIBSection(video); + VideoCreateDIBSection(resetVideoState); #if 0 DDInit(); // For WaitForVerticalBlank() diff --git a/source/Windows/Win32Frame.h b/source/Windows/Win32Frame.h index cbce057e..75fd8926 100644 --- a/source/Windows/Win32Frame.h +++ b/source/Windows/Win32Frame.h @@ -25,7 +25,8 @@ class Video; class Win32Frame : public FrameBase { public: - Win32Frame(); + Win32Frame(void); + virtual ~Win32Frame(void){} static Win32Frame& GetWin32Frame(); static LRESULT CALLBACK FrameWndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam); @@ -38,15 +39,16 @@ public: virtual void FrameSetCursorPosByMousePos(); virtual void SetFullScreenShowSubunitStatus(bool bShow); - virtual bool GetBestDisplayResolutionForFullScreen(UINT& bestWidth, UINT& bestHeight, UINT userSpecifiedHeight = 0); + virtual bool GetBestDisplayResolutionForFullScreen(UINT& bestWidth, UINT& bestHeight, UINT userSpecifiedWidth=0, UINT userSpecifiedHeight=0); virtual int SetViewportScale(int nNewScale, bool bForce = false); virtual void SetAltEnterToggleFullScreen(bool mode); virtual void SetLoadedSaveStateFlag(const bool bFlag); - virtual void Initialize(void); + virtual void Initialize(bool resetVideoState); virtual void Destroy(void); virtual void VideoPresentScreen(void); + virtual void ResizeWindow(void); virtual int FrameMessageBox(LPCSTR lpText, LPCSTR lpCaption, UINT uType); virtual void GetBitmap(LPCSTR lpBitmapName, LONG cb, LPVOID lpvBits); @@ -64,6 +66,7 @@ public: UINT Get3DBorderHeight(void); int GetViewportScale(void); void GetViewportCXCY(int& nViewportCX, int& nViewportCY); + void SetFullScreenViewportScale(int nNewXScale, int nNewYScale); void ApplyVideoModeChange(void); @@ -76,7 +79,7 @@ private: static BOOL CALLBACK DDEnumProc(LPGUID lpGUID, LPCTSTR lpszDesc, LPCTSTR lpszDrvName, LPVOID lpContext); LRESULT WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam); - void videoCreateDIBSection(Video& video); + void VideoCreateDIBSection(bool resetVideoState); void VideoDrawLogoBitmap(HDC hDstDC, int xoff, int yoff, int srcw, int srch, int scale); bool DDInit(void); void DDUninit(void); @@ -129,7 +132,6 @@ private: bool g_windowMinimized; std::string driveTooltip; bool g_bFullScreen_ShowSubunitStatus; - FULLSCREEN_SCALE_TYPE g_win_fullscreen_scale; int g_win_fullscreen_offsetx; int g_win_fullscreen_offsety; UINT m_bestWidthForFullScreen; diff --git a/source/Windows/WinFrame.cpp b/source/Windows/WinFrame.cpp index 8c3c2a40..e78abbea 100644 --- a/source/Windows/WinFrame.cpp +++ b/source/Windows/WinFrame.cpp @@ -463,11 +463,20 @@ void Win32Frame::DrawFrameWindow (bool bPaintingWindow/*=false*/) DrawButton(dc,iButton); } - if (g_nViewportScale == 2) + if (g_nViewportScale == 2 || GetVideo().HasVidHD()) { - int x = buttonx + 1; - int y = buttony + BUTTONS*BUTTONCY + 36; // 36 = height of StatusArea - RECT rect = {x, y, x+45, y+BUTTONS*BUTTONCY+22}; + const int x = buttonx + 1; + const int y = buttony + BUTTONS * BUTTONCY + 36; // 36 = height of StatusArea + RECT rect = { x, y, x + BUTTONCX, y + BUTTONS * BUTTONCY + 22 }; + + if (GetVideo().HasVidHD()) + { + if (g_nViewportScale == 1) + rect.bottom += 14; + else + rect.bottom += 32; + } + int res = FillRect(dc, &rect, btnfacebrush); } } @@ -1933,6 +1942,7 @@ void Win32Frame::ProcessButtonClick(int button, bool bFromButtonUI /*=false*/) } else // MODE_RUNNING, MODE_LOGO, MODE_PAUSED { + GetVideo().ClearSHRResidue(); // Clear the framebuffer to remove any SHR residue in the borders DebugBegin(); } break; @@ -2149,13 +2159,14 @@ void Win32Frame::SetFullScreenMode(void) scalex = width / GetVideo().GetFrameBufferBorderlessWidth(); scaley = height / GetVideo().GetFrameBufferBorderlessHeight(); - g_win_fullscreen_scale = (scalex <= scaley) ? scalex : scaley; - g_win_fullscreen_offsetx = ((int)width - (int)(g_win_fullscreen_scale * GetVideo().GetFrameBufferBorderlessWidth())) / 2; - g_win_fullscreen_offsety = ((int)height - (int)(g_win_fullscreen_scale * GetVideo().GetFrameBufferBorderlessHeight())) / 2; + // NB. Separate x,y scaling is OK in full-screen mode + // . eg. SHR 640x400 (scalex=2, scaley=3) => 1280x1200, which roughly gives a 4:3 aspect ratio for a resolution of 1600x1200 + g_win_fullscreen_offsetx = ((int)width - (int)(scalex * GetVideo().GetFrameBufferBorderlessWidth())) / 2; + g_win_fullscreen_offsety = ((int)height - (int)(scaley * GetVideo().GetFrameBufferBorderlessHeight())) / 2; SetWindowPos(g_hFrameWindow, NULL, left, top, (int)width, (int)height, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); g_bIsFullScreen = true; - SetViewportScale(g_win_fullscreen_scale, true); + SetFullScreenViewportScale(scalex, scaley); buttonx = GetFullScreenOffsetX() + g_nViewportCX + VIEWPORTX*2; buttony = GetFullScreenOffsetY(); @@ -2187,7 +2198,6 @@ void Win32Frame::SetNormalMode(void) g_win_fullscreen_offsetx = 0; g_win_fullscreen_offsety = 0; - g_win_fullscreen_scale = 1; SetWindowLong(g_hFrameWindow, GWL_STYLE, g_main_window_saved_style); SetWindowLong(g_hFrameWindow, GWL_EXSTYLE, g_main_window_saved_exstyle); SetWindowPos(g_hFrameWindow, NULL, @@ -2249,9 +2259,22 @@ int Win32Frame::SetViewportScale(int nNewScale, bool bForce /*=false*/) g_nViewportCX = g_nViewportScale * GetVideo().GetFrameBufferBorderlessWidth(); g_nViewportCY = g_nViewportScale * GetVideo().GetFrameBufferBorderlessHeight(); + buttonx = BUTTONX; // NB. macro uses g_nViewportCX + buttony = BUTTONY; + return nNewScale; } +void Win32Frame::SetFullScreenViewportScale(int nNewXScale, int nNewYScale) +{ + g_nViewportScale = MIN(nNewXScale, nNewYScale); // Not needed in FS mode + g_nViewportCX = nNewXScale * GetVideo().GetFrameBufferBorderlessWidth(); + g_nViewportCY = nNewYScale * GetVideo().GetFrameBufferBorderlessHeight(); + + buttonx = BUTTONX; // NB. macro uses g_nViewportCX + buttony = BUTTONY; +} + void Win32Frame::SetupTooltipControls(void) { TOOLINFO toolinfo; @@ -2303,6 +2326,12 @@ void Win32Frame::GetWidthHeight(int& nWidth, int& nHeight) #endif } +// Window frame's border size has changed (eg. VidHD added/removed) +void Win32Frame::ResizeWindow(void) +{ + FrameResizeWindow(GetViewportScale()); +} + void Win32Frame::FrameResizeWindow(int nNewScale) { int nOldWidth, nOldHeight; @@ -2314,11 +2343,6 @@ void Win32Frame::FrameResizeWindow(int nNewScale) int nXPos = framerect.left; int nYPos = framerect.top; - // - - buttonx = g_nViewportCX + VIEWPORTX*2; - buttony = 0; - // Invalidate old rect region { RECT irect; @@ -2697,7 +2721,7 @@ void Win32Frame::FrameUpdateApple2Type(void) DrawFrameWindow(); } -bool Win32Frame::GetBestDisplayResolutionForFullScreen(UINT& bestWidth, UINT& bestHeight, UINT userSpecifiedHeight /*= 0*/) +bool Win32Frame::GetBestDisplayResolutionForFullScreen(UINT& bestWidth, UINT& bestHeight, UINT userSpecifiedWidth/*=0*/, UINT userSpecifiedHeight/*=0*/) { m_bestWidthForFullScreen = 0; m_bestHeightForFullScreen = 0; @@ -2734,10 +2758,17 @@ bool Win32Frame::GetBestDisplayResolutionForFullScreen(UINT& bestWidth, UINT& be if (vecDisplayResolutions.size() == 0) return false; - // Pick least width (such that it's wide enough to scale) + // Pick user-specific width if it exists + // Else pick least width (such that it's wide enough to scale) UINT width = (UINT)-1; for (VEC_PAIR::iterator it = vecDisplayResolutions.begin(); it!= vecDisplayResolutions.end(); ++it) { + if (it->first == userSpecifiedWidth) + { + width = userSpecifiedWidth; + break; + } + if (width > it->first) { UINT scaleFactor = it->second / GetVideo().GetFrameBufferBorderlessHeight();