diff --git a/AppleWinExpress2019.vcxproj b/AppleWinExpress2019.vcxproj index 6e8a483d4..eb8544b90 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 49c6034d5..26c96c5dd 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 968fa3f81..dc7c5687a 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 ae5a2158b..819b10115 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/CMakeLists.txt b/source/CMakeLists.txt index 34bed4c64..c1c35200f 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -47,6 +47,7 @@ set(SOURCE_FILES Debugger/Debugger_Commands.cpp Debugger/Util_MemoryTextFile.cpp + VidHD.cpp SSI263.cpp Speaker.cpp SoundCore.cpp @@ -115,6 +116,7 @@ set(SOURCE_FILES ) set(HEADER_FILES + VidHD.cpp SSI263.h SSI263Phonemes.h Speaker.h diff --git a/source/Card.h b/source/Card.h index aae57cf0a..225ba8047 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, CT_Uthernet2, }; diff --git a/source/CardManager.cpp b/source/CardManager.cpp index f250f1fe2..65c9f3257 100644 --- a/source/CardManager.cpp +++ b/source/CardManager.cpp @@ -40,6 +40,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "SAM.h" #include "SerialComms.h" #include "SNESMAX.h" +#include "VidHD.h" +#include "linux/network/uthernet2.h" void CardManager::InsertInternal(UINT slot, SS_CARDTYPE type) { @@ -91,7 +93,7 @@ void CardManager::InsertInternal(UINT slot, SS_CARDTYPE type) m_slot[slot] = new DummyCard(type, slot); break; case CT_Uthernet2: - m_slot[slot] = new DummyCard(type, slot); + m_slot[slot] = new Uthernet2(slot); break; case CT_FourPlay: m_slot[slot] = new FourPlayCard(slot); @@ -99,6 +101,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 2dbccffe0..acfdca31c 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 2e1034cd8..b76dd0f4a 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 4db1efa83..996c56fe0 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 217d79d3e..426c05973 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 3e68efabc..6acb242b9 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 ac056080f..c19675129 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 70d7a9ec3..9e118b459 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 8ac67eb2f..63141dbd9 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 bcf267ef3..1850cc592 100644 --- a/source/FrameBase.h +++ b/source/FrameBase.h @@ -15,7 +15,7 @@ class FrameBase 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 @@ class FrameBase 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 b46e124cc..01651dbb6 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 21ffc2d00..48af597f3 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 0eaccfeba..a8691a50c 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 d1bda6d32..0c4b12e50 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 ff1bcbd26..ffd6ad225 100644 --- a/source/Utilities.cpp +++ b/source/Utilities.cpp @@ -543,6 +543,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() @@ -597,7 +599,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 000000000..88a27c38c --- /dev/null +++ b/source/VidHD.cpp @@ -0,0 +1,190 @@ +/* + 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; + rgb.a = ALPHA; + 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 000000000..8652c2787 --- /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 f096cd31e..569afe176 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,16 @@ void Video::VideoRefreshBuffer(uint32_t uRedrawWholeScreenVideoMode, bool bRedra NTSC_VideoRedrawWholeScreen(); } } + +void Video::ClearFrameBuffer(void) +{ + UINT32* frameBuffer = (UINT32*)GetFrameBuffer(); + std::fill(frameBuffer, frameBuffer + GetFrameBufferWidth() * GetFrameBufferHeight(), OPAQUE_BLACK); +} + +// 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 acde39156..e28da3a31 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 @@ class Video 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 @@ class Video 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 @@ class Video 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 @@ class Video 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 @@ class Video uint8_t *g_pFramebufferbits; private: - void SetFrameBuffer(uint8_t* frameBuffer) { g_pFramebufferbits = frameBuffer; } std::string VideoGetSnapshotStructName(void); @@ -285,6 +296,7 @@ class Video 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 @@ class Video 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 ccc9f913b..65f683fdb 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 f78ce3322..06beb8f0b 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 cbce057ed..75fd89265 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 @@ class Win32Frame : public FrameBase 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 @@ class Win32Frame : public FrameBase 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 @@ class Win32Frame : public FrameBase 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 @@ class Win32Frame : public FrameBase 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 8c3c2a40a..e78abbea7 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(); diff --git a/source/frontends/common2/commonframe.cpp b/source/frontends/common2/commonframe.cpp index 3917a7246..94e42ceec 100644 --- a/source/frontends/common2/commonframe.cpp +++ b/source/frontends/common2/commonframe.cpp @@ -2,6 +2,7 @@ #include "frontends/common2/commonframe.h" #include "frontends/common2/utils.h" #include "linux/resources.h" +#include "linux/context.h" #include #include @@ -77,25 +78,6 @@ namespace common2 g_sProgramDir = getResourcePath("/bin/"); } - void CommonFrame::Initialize() - { - InitialiseEmulator(); - LinuxFrame::Initialize(); - } - - void CommonFrame::Destroy() - { - LinuxFrame::Destroy(); - myResource.clear(); - DestroyEmulator(); - } - - void CommonFrame::Restart() - { - Destroy(); - Initialize(); - } - BYTE* CommonFrame::GetResource(WORD id, LPCSTR lpType, DWORD expectedSize) { myResource.clear(); diff --git a/source/frontends/common2/commonframe.h b/source/frontends/common2/commonframe.h index 102391df7..44a0f785f 100644 --- a/source/frontends/common2/commonframe.h +++ b/source/frontends/common2/commonframe.h @@ -12,10 +12,6 @@ namespace common2 public: CommonFrame(); - void Initialize() override; - void Destroy() override; - void Restart() override; - BYTE* GetResource(WORD id, LPCSTR lpType, DWORD expectedSize) override; protected: diff --git a/source/frontends/common2/utils.cpp b/source/frontends/common2/utils.cpp index 7c7d62c4b..2f2fc7f91 100644 --- a/source/frontends/common2/utils.cpp +++ b/source/frontends/common2/utils.cpp @@ -1,35 +1,8 @@ #include "StdAfx.h" -#include "frontends/common2/utils.h" #include "frontends/common2/programoptions.h" -#include "linux/network/uthernet2.h" - -#include "SaveState.h" - -#include "Common.h" -#include "CardManager.h" -#include "Core.h" -#include "Disk.h" -#include "Mockingboard.h" -#include "SoundCore.h" -#include "Harddisk.h" -#include "Speaker.h" -#include "Log.h" -#include "CPU.h" -#include "Memory.h" -#include "LanguageCard.h" -#include "MouseInterface.h" -#include "ParallelPrinter.h" -#include "Video.h" -#include "NTSC.h" #include "SaveState.h" -#include "RGBMonitor.h" -#include "Riff.h" #include "Registry.h" -#include "Utilities.h" -#include "Interface.h" -#include "Debugger/Debug.h" -#include "Tfe/tfe.h" #include #include @@ -62,90 +35,6 @@ namespace common2 } } - void LoadUthernet2() - { - CardManager & cardManager = GetCardMgr(); - for (UINT slot = SLOT1; slot < NUM_SLOTS; slot++) - { - if (cardManager.QuerySlot(slot) == CT_Uthernet2) - { - // AppleWin does not know anything about this - registerUthernet2(slot); - break; - } - } - } - - void InitialiseEmulator() - { -#ifdef RIFF_SPKR - RiffInitWriteFile("/tmp/Spkr.wav", SPKR_SAMPLE_RATE, 1); -#endif -#ifdef RIFF_MB - RiffInitWriteFile("/tmp/Mockingboard.wav", 44100, 2); -#endif - - g_nAppMode = MODE_RUNNING; - LogFileOutput("Initialisation\n"); - - g_bFullSpeed = false; - - LoadConfiguration(); - LoadUthernet2(); - SetCurrentCLK6502(); - GetAppleWindowTitle(); - GetFrame().FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES | DRAW_DISK_STATUS); - - DSInit(); - MB_Initialize(); - SpkrInitialize(); - - MemInitialize(); - - CardManager & cardManager = GetCardMgr(); - cardManager.GetDisk2CardMgr().Reset(); - if (cardManager.QuerySlot(SLOT7) == CT_GenericHDD) - { - dynamic_cast(cardManager.GetRef(SLOT7)).Reset(true); - } - - Snapshot_Startup(); - - DebugInitialize(); - } - - void DestroyEmulator() - { - CardManager & cardManager = GetCardMgr(); - - Snapshot_Shutdown(); - CMouseInterface* pMouseCard = cardManager.GetMouseCard(); - if (pMouseCard) - { - pMouseCard->Reset(); - } - MemDestroy(); - - SpkrDestroy(); - MB_Destroy(); - DSUninit(); - - unRegisterUthernet2(); - tfe_shutdown(); - - if (cardManager.QuerySlot(SLOT7) == CT_GenericHDD) - { - dynamic_cast(cardManager.GetRef(SLOT7)).Destroy(); - } - - PrintDestroy(); - CpuDestroy(); - DebugDestroy(); - - GetCardMgr().GetDisk2CardMgr().Destroy(); - RiffFinishWriteFile(); - } - void loadGeometryFromRegistry(const std::string §ion, Geometry & geometry) { if (geometry.empty) // otherwise it was user provided diff --git a/source/frontends/common2/utils.h b/source/frontends/common2/utils.h index 5986d9d9d..40b840940 100644 --- a/source/frontends/common2/utils.h +++ b/source/frontends/common2/utils.h @@ -8,10 +8,6 @@ namespace common2 void setSnapshotFilename(const std::string & filename, const bool load); - // Do not call directly. Used in CommonFrame - void InitialiseEmulator(); - void DestroyEmulator(); - void loadGeometryFromRegistry(const std::string §ion, Geometry & geometry); void saveGeometryToRegistry(const std::string §ion, const Geometry & geometry); } diff --git a/source/frontends/libretro/game.cpp b/source/frontends/libretro/game.cpp index a5fc8445b..bff837b95 100644 --- a/source/frontends/libretro/game.cpp +++ b/source/frontends/libretro/game.cpp @@ -36,7 +36,7 @@ namespace ra2 myFrame.reset(new ra2::RetroFrame()); SetFrame(myFrame); - myFrame->Initialize(); + myFrame->Begin(); Video & video = GetVideo(); // should the user be allowed to tweak 0.75 @@ -46,7 +46,7 @@ namespace ra2 Game::~Game() { - myFrame->Destroy(); + myFrame->End(); myFrame.reset(); SetFrame(myFrame); } diff --git a/source/frontends/libretro/retroframe.cpp b/source/frontends/libretro/retroframe.cpp index 3b857387a..9c9c4c3c2 100644 --- a/source/frontends/libretro/retroframe.cpp +++ b/source/frontends/libretro/retroframe.cpp @@ -106,9 +106,9 @@ namespace ra2 video_cb(myVideoBuffer.data() + myOffset, myBorderlessWidth, myBorderlessHeight, myPitch); } - void RetroFrame::Initialize() + void RetroFrame::Initialize(bool resetVideoState) { - CommonFrame::Initialize(); + CommonFrame::Initialize(resetVideoState); FrameRefreshStatus(DRAW_TITLE); Video & video = GetVideo(); diff --git a/source/frontends/libretro/retroframe.h b/source/frontends/libretro/retroframe.h index 376dd34b0..7df2f3a81 100644 --- a/source/frontends/libretro/retroframe.h +++ b/source/frontends/libretro/retroframe.h @@ -15,7 +15,7 @@ namespace ra2 void VideoPresentScreen() override; void FrameRefreshStatus(int drawflags) override; - void Initialize() override; + void Initialize(bool resetVideoState) override; void Destroy() override; int FrameMessageBox(LPCSTR lpText, LPCSTR lpCaption, UINT uType) override; void GetBitmap(LPCSTR lpBitmapName, LONG cb, LPVOID lpvBits) override; diff --git a/source/frontends/ncurses/main.cpp b/source/frontends/ncurses/main.cpp index 56e078e1d..51b0b6774 100644 --- a/source/frontends/ncurses/main.cpp +++ b/source/frontends/ncurses/main.cpp @@ -152,7 +152,7 @@ namespace const Initialisation init(frame, paddle); applyOptions(options); - frame->Initialize(); + frame->Begin(); na2::SetCtrlCHandler(options.headless); @@ -165,6 +165,7 @@ namespace { EnterMessageLoop(options, frame); } + frame->End(); return 0; } diff --git a/source/frontends/ncurses/nframe.cpp b/source/frontends/ncurses/nframe.cpp index c2231a9f9..b7417b2a8 100644 --- a/source/frontends/ncurses/nframe.cpp +++ b/source/frontends/ncurses/nframe.cpp @@ -47,9 +47,9 @@ namespace na2 // so we can run headless } - void NFrame::Initialize() + void NFrame::Initialize(bool resetVideoState) { - CommonFrame::Initialize(); + CommonFrame::Initialize(resetVideoState); myTextFlashCounter = 0; myTextFlashState = 0; myAsciiArt.reset(new ASCIIArt()); diff --git a/source/frontends/ncurses/nframe.h b/source/frontends/ncurses/nframe.h index fdfb912cb..533fd3078 100644 --- a/source/frontends/ncurses/nframe.h +++ b/source/frontends/ncurses/nframe.h @@ -21,7 +21,7 @@ namespace na2 WINDOW * GetWindow(); WINDOW * GetStatus(); - void Initialize() override; + void Initialize(bool resetVideoState) override; void Destroy() override; void VideoPresentScreen() override; int FrameMessageBox(LPCSTR lpText, LPCSTR lpCaption, UINT uType) override; diff --git a/source/frontends/qt/qapple.cpp b/source/frontends/qt/qapple.cpp index 557e00ace..9de150572 100644 --- a/source/frontends/qt/qapple.cpp +++ b/source/frontends/qt/qapple.cpp @@ -10,19 +10,13 @@ #include "Harddisk.h" #include "Log.h" #include "CPU.h" -#include "Memory.h" #include "LanguageCard.h" -#include "Mockingboard.h" -#include "MouseInterface.h" -#include "ParallelPrinter.h" #include "Video.h" -#include "SoundCore.h" #include "NTSC.h" #include "SaveState.h" #include "Speaker.h" #include "Riff.h" #include "RGBMonitor.h" -#include "Utilities.h" #include "linux/benchmark.h" #include "linux/version.h" @@ -76,72 +70,6 @@ namespace * */ - void loadEmulator(const std::shared_ptr & frame, const GlobalOptions & options) - { - LoadConfiguration(); - - // ResetDefaultMachineMemTypes(); - - switch (options.slot0Card) { - case 1: // Language Card - SetExpansionMemType(CT_LanguageCard); - break; - case 2: // Saturn 64 - SetSaturnMemorySize(Saturn128K::kMaxSaturnBanks / 2); - SetExpansionMemType(CT_Saturn128K); - break; - case 3: // Saturn 128 - SetSaturnMemorySize(Saturn128K::kMaxSaturnBanks); - SetExpansionMemType(CT_Saturn128K); - break; - case 4: // RamWorks - SetRamWorksMemorySize(options.ramWorksMemorySize); - SetExpansionMemType(CT_RamWorksIII); - break; - } - - DSInit(); - MB_Initialize(); - SpkrInitialize(); - MemInitialize(); - frame->Initialize(); - - CardManager & cardManager = GetCardMgr(); - cardManager.GetDisk2CardMgr().Reset(); - if (cardManager.QuerySlot(SLOT7) == CT_GenericHDD) - { - dynamic_cast(cardManager.GetRef(SLOT7)).Reset(true); - } - } - - void unloadEmulator(const std::shared_ptr & frame) - { - CardManager & cardManager = GetCardMgr(); - - CMouseInterface* pMouseCard = cardManager.GetMouseCard(); - if (pMouseCard) - { - pMouseCard->Reset(); - } - if (cardManager.QuerySlot(SLOT7) == CT_GenericHDD) - { - dynamic_cast(cardManager.GetRef(SLOT7)).Destroy(); - } - PrintDestroy(); - MemDestroy(); - SpkrDestroy(); - frame->Destroy(); - MB_Destroy(); - DSUninit(); - CpuDestroy(); - - cardManager.GetDisk2CardMgr().Destroy(); - LogDone(); - RiffFinishWriteFile(); - - QDirectSound::stop(); - } - qint64 emulatorTimeInMS() { const double timeInSeconds = g_nCumulativeCycles / g_fCurrentCLK6502; @@ -206,7 +134,7 @@ QApple::QApple(QWidget *parent) : on_actionPause_triggered(); initialiseEmulator(); - loadEmulator(myFrame, myOptions); + myFrame->Begin(); setAcceptDrops(true); } @@ -219,7 +147,8 @@ QApple::~QApple() void QApple::closeEvent(QCloseEvent * event) { stopTimer(); - unloadEmulator(myFrame); + myFrame->End(); + QDirectSound::stop(); QSettings settings; settings.setValue("QApple/window/geometry", saveGeometry().toBase64()); @@ -367,8 +296,7 @@ void QApple::on_actionReboot_triggered() emit endEmulator(); mySaveStateLabel->clear(); - unloadEmulator(myFrame); - loadEmulator(myFrame, myOptions); + myFrame->Restart(); myFrame->VideoPresentScreen(); } diff --git a/source/frontends/qt/qtframe.cpp b/source/frontends/qt/qtframe.cpp index 305de1b9c..f6d8fec86 100644 --- a/source/frontends/qt/qtframe.cpp +++ b/source/frontends/qt/qtframe.cpp @@ -36,9 +36,9 @@ void QtFrame::FrameRefreshStatus(int drawflags) } } -void QtFrame::Initialize() +void QtFrame::Initialize(bool resetVideoState) { - LinuxFrame::Initialize(); + LinuxFrame::Initialize(resetVideoState); FrameRefreshStatus(DRAW_TITLE); myEmulator->loadVideoSettings(); myEmulator->displayLogo(); diff --git a/source/frontends/qt/qtframe.h b/source/frontends/qt/qtframe.h index d0c5cf1a7..2a0aa163b 100644 --- a/source/frontends/qt/qtframe.h +++ b/source/frontends/qt/qtframe.h @@ -16,7 +16,7 @@ class QtFrame : public LinuxFrame void VideoPresentScreen() override; void FrameRefreshStatus(int drawflags) override; - void Initialize() override; + void Initialize(bool resetVideoState) override; void Destroy() override; int FrameMessageBox(LPCSTR lpText, LPCSTR lpCaption, UINT uType) override; diff --git a/source/frontends/sdl/imgui/sdlimguiframe.cpp b/source/frontends/sdl/imgui/sdlimguiframe.cpp index d0462ab2a..ec0827a52 100644 --- a/source/frontends/sdl/imgui/sdlimguiframe.cpp +++ b/source/frontends/sdl/imgui/sdlimguiframe.cpp @@ -96,9 +96,25 @@ namespace sa2 ImGui::StyleColorsDark(); ImGui_ImplSDL2_InitForOpenGL(myWindow.get(), myGLContext); - ImGui_ImplOpenGL3_Init(); + myDeadTopZone = 0; + myTexture = 0; + } + + SDLImGuiFrame::~SDLImGuiFrame() + { + glDeleteTextures(1, &myTexture); + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); + SDL_GL_DeleteContext(myGLContext); + } + + void SDLImGuiFrame::Initialize(bool resetVideoState) + { + SDLFrame::Initialize(resetVideoState); + glDeleteTextures(1, &myTexture); glGenTextures(1, &myTexture); Video & video = GetVideo(); @@ -114,17 +130,6 @@ namespace sa2 myOffset = (width * borderHeight + borderWidth) * sizeof(bgra_t); allocateTexture(myTexture, myBorderlessWidth, myBorderlessHeight); - - myDeadTopZone = 0; - } - - SDLImGuiFrame::~SDLImGuiFrame() - { - glDeleteTextures(1, &myTexture); - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplSDL2_Shutdown(); - ImGui::DestroyContext(); - SDL_GL_DeleteContext(myGLContext); } void SDLImGuiFrame::UpdateTexture() diff --git a/source/frontends/sdl/imgui/sdlimguiframe.h b/source/frontends/sdl/imgui/sdlimguiframe.h index b18472f59..41bc6a920 100644 --- a/source/frontends/sdl/imgui/sdlimguiframe.h +++ b/source/frontends/sdl/imgui/sdlimguiframe.h @@ -21,6 +21,7 @@ namespace sa2 void VideoPresentScreen() override; void ResetSpeed() override; + void Initialize(bool resetVideoState) override; protected: diff --git a/source/frontends/sdl/imgui/sdlsettings.cpp b/source/frontends/sdl/imgui/sdlsettings.cpp index 131ecefa9..824244648 100644 --- a/source/frontends/sdl/imgui/sdlsettings.cpp +++ b/source/frontends/sdl/imgui/sdlsettings.cpp @@ -306,7 +306,7 @@ namespace sa2 const bool isSelected = card == current; if (ImGui::Selectable(getCardName(card).c_str(), isSelected)) { - insertCard(slot, card); + insertCard(slot, card, frame); } if (isSelected) { @@ -330,7 +330,7 @@ namespace sa2 const bool isSelected = card == expansion; if (ImGui::Selectable(getCardName(card).c_str(), isSelected)) { - SetExpansionMemType(card); + setExpansionCard(card); } if (isSelected) { diff --git a/source/frontends/sdl/imgui/settingshelper.cpp b/source/frontends/sdl/imgui/settingshelper.cpp index df42e7333..e6e688fd5 100644 --- a/source/frontends/sdl/imgui/settingshelper.cpp +++ b/source/frontends/sdl/imgui/settingshelper.cpp @@ -3,6 +3,8 @@ #include "Registry.h" #include "Harddisk.h" #include "Core.h" +#include "Memory.h" +#include "Interface.h" #include "Debugger/Debug.h" #include "Tfe/tfe.h" @@ -16,60 +18,61 @@ namespace { const std::map cards = { - {CT_Empty, "CT_Empty"}, - {CT_Disk2, "CT_Disk2"}, - {CT_SSC, "CT_SSC"}, - {CT_MockingboardC, "CT_MockingboardC"}, - {CT_GenericPrinter, "CT_GenericPrinter"}, - {CT_GenericHDD, "CT_GenericHDD"}, - {CT_GenericClock, "CT_GenericClock"}, - {CT_MouseInterface, "CT_MouseInterface"}, - {CT_Z80, "CT_Z80"}, - {CT_Phasor, "CT_Phasor"}, - {CT_Echo, "CT_Echo"}, - {CT_SAM, "CT_SAM"}, - {CT_80Col, "CT_80Col"}, - {CT_Extended80Col, "CT_Extended80Col"}, - {CT_RamWorksIII, "CT_RamWorksIII"}, - {CT_Uthernet, "CT_Uthernet"}, - {CT_LanguageCard, "CT_LanguageCard"}, - {CT_LanguageCardIIe, "CT_LanguageCardIIe"}, - {CT_Saturn128K, "CT_Saturn128K"}, - {CT_FourPlay, "CT_FourPlay"}, - {CT_SNESMAX, "CT_SNESMAX"}, - {CT_Uthernet2, "CT_Uthernet2"}, + {CT_Empty, "Empty"}, + {CT_Disk2, "Disk2"}, + {CT_SSC, "SSC"}, + {CT_MockingboardC, "MockingboardC"}, + {CT_GenericPrinter, "GenericPrinter"}, + {CT_GenericHDD, "GenericHDD"}, + {CT_GenericClock, "GenericClock"}, + {CT_MouseInterface, "MouseInterface"}, + {CT_Z80, "Z80"}, + {CT_Phasor, "Phasor"}, + {CT_Echo, "Echo"}, + {CT_SAM, "SAM"}, + {CT_80Col, "80Col"}, + {CT_Extended80Col, "Extended80Col"}, + {CT_RamWorksIII, "RamWorksIII"}, + {CT_Uthernet, "Uthernet"}, + {CT_LanguageCard, "LanguageCard"}, + {CT_LanguageCardIIe, "LanguageCardIIe"}, + {CT_Saturn128K, "Saturn128K"}, + {CT_FourPlay, "FourPlay"}, + {CT_SNESMAX, "SNESMAX"}, + {CT_VidHD, "VidHD"}, + {CT_Uthernet2, "Uthernet2"}, }; const std::map apple2Types = { - {A2TYPE_APPLE2, "A2TYPE_APPLE2"}, - {A2TYPE_APPLE2PLUS, "A2TYPE_APPLE2PLUS"}, - {A2TYPE_APPLE2JPLUS, "A2TYPE_APPLE2JPLUS"}, - {A2TYPE_APPLE2E, "A2TYPE_APPLE2E"}, - {A2TYPE_APPLE2EENHANCED, "A2TYPE_APPLE2EENHANCED"}, - {A2TYPE_APPLE2C, "A2TYPE_APPLE2C"}, - {A2TYPE_PRAVETS8M, "A2TYPE_PRAVETS8M"}, - {A2TYPE_PRAVETS82, "A2TYPE_PRAVETS82"}, - {A2TYPE_BASE64A, "A2TYPE_BASE64A"}, - {A2TYPE_PRAVETS8A, "A2TYPE_PRAVETS8A"}, - {A2TYPE_TK30002E, "A2TYPE_TK30002E"}, + {A2TYPE_APPLE2, "APPLE2"}, + {A2TYPE_APPLE2PLUS, "APPLE2PLUS"}, + {A2TYPE_APPLE2JPLUS, "APPLE2JPLUS"}, + {A2TYPE_APPLE2E, "APPLE2E"}, + {A2TYPE_APPLE2EENHANCED, "APPLE2EENHANCED"}, + {A2TYPE_APPLE2C, "APPLE2C"}, + {A2TYPE_PRAVETS8M, "PRAVETS8M"}, + {A2TYPE_PRAVETS82, "PRAVETS82"}, + {A2TYPE_BASE64A, "BASE64A"}, + {A2TYPE_PRAVETS8A, "PRAVETS8A"}, + {A2TYPE_TK30002E, "TK30002E"}, }; const std::map cpuTypes = { - {CPU_6502, "CPU_6502"}, - {CPU_65C02, "CPU_65C02"}, - {CPU_Z80, "CPU_Z80"}, + {CPU_6502, "6502"}, + {CPU_65C02, "65C02"}, + {CPU_Z80, "Z80"}, }; const std::map appModes = { - {MODE_LOGO, "MODE_LOGO"}, - {MODE_PAUSED, "MODE_PAUSED"}, - {MODE_RUNNING, "MODE_RUNNING"}, - {MODE_DEBUG, "MODE_DEBUG"}, - {MODE_STEPPING, "MODE_STEPPING"}, - {MODE_BENCHMARK, "MODE_BENCHMARCK"}, + {MODE_LOGO, "LOGO"}, + {MODE_PAUSED, "PAUSED"}, + {MODE_RUNNING, "RUNNING"}, + {MODE_DEBUG, "DEBUG"}, + {MODE_STEPPING, "STEPPING"}, + {MODE_BENCHMARK, "BENCHMARCK"}, }; const std::map statuses = @@ -98,11 +101,11 @@ namespace {0, {CT_Empty, CT_LanguageCard, CT_Saturn128K}}, {1, {CT_Empty, CT_GenericPrinter, CT_Uthernet2}}, {2, {CT_Empty, CT_SSC, CT_Uthernet2}}, - {3, {CT_Empty, CT_Uthernet, CT_Uthernet2}}, + {3, {CT_Empty, CT_Uthernet, CT_Uthernet2, CT_VidHD}}, {4, {CT_Empty, CT_MockingboardC, CT_MouseInterface, CT_Phasor, CT_Uthernet2}}, {5, {CT_Empty, CT_MockingboardC, CT_Z80, CT_SAM, CT_Disk2, CT_FourPlay, CT_SNESMAX, CT_Uthernet2}}, - {6, {CT_Empty, CT_Disk2}}, - {7, {CT_Empty, CT_GenericHDD}}, + {6, {CT_Empty, CT_Disk2, CT_Uthernet2}}, + {7, {CT_Empty, CT_GenericHDD, CT_Uthernet2}}, }; const std::vector expansionCards = @@ -158,11 +161,23 @@ namespace sa2 return statuses.at(status); } - void insertCard(size_t slot, SS_CARDTYPE card) + void insertCard(size_t slot, SS_CARDTYPE card, FrameBase * frame) { CardManager & cardManager = GetCardMgr(); + Video & video = GetVideo(); + const bool oldHasVid = video.HasVidHD(); switch (slot) { + case 3: + { + if (cardManager.QuerySlot(slot) == CT_VidHD) + { + // the old card was a VidHD, which will be removed + // reset it + video.SetVidHD(false); + } + break; + } case 4: case 5: { @@ -172,7 +187,6 @@ namespace sa2 } else { - CardManager & cardManager = GetCardMgr(); if (cardManager.QuerySlot(slot) == CT_MockingboardC) { cardManager.Insert(9 - slot, CT_Empty); // the other @@ -182,19 +196,24 @@ namespace sa2 } }; - if (card == CT_Uthernet2) + cardManager.Insert(slot, card); + + // keep everything consistent + // a bit of a heavy call, but nothing simpler is available now + MemInitializeIO(); + + if (oldHasVid != video.HasVidHD()) { - // only 1 Uthernet2 allowed - for (size_t s = SLOT1; s < NUM_SLOTS; ++s) - { - if (cardManager.QuerySlot(s) == card) - { - cardManager.Insert(s, CT_Empty); - } - } + frame->Destroy(); + frame->Initialize(true); } + } - cardManager.Insert(slot, card); + void setExpansionCard(SS_CARDTYPE card) + { + SetExpansionMemType(card); + CreateLanguageCard(); + MemInitializeIO(); } void setVideoStyle(Video & video, const VideoStyle_e style, const bool enabled) diff --git a/source/frontends/sdl/imgui/settingshelper.h b/source/frontends/sdl/imgui/settingshelper.h index 694d5c393..646d4a4f6 100644 --- a/source/frontends/sdl/imgui/settingshelper.h +++ b/source/frontends/sdl/imgui/settingshelper.h @@ -10,6 +10,8 @@ #include #include +class FrameBase; + namespace sa2 { @@ -24,8 +26,8 @@ namespace sa2 const std::vector & getExpansionCards(); const std::map & getAapple2Types(); - void insertCard(size_t slot, SS_CARDTYPE card); - + void insertCard(size_t slot, SS_CARDTYPE card, FrameBase * frame); + void setExpansionCard(SS_CARDTYPE card); void setVideoStyle(Video & video, const VideoStyle_e style, const bool enabled); void saveTFEEnabled(const int enabled); diff --git a/source/frontends/sdl/main.cpp b/source/frontends/sdl/main.cpp index 516190842..d7f3f0ff6 100644 --- a/source/frontends/sdl/main.cpp +++ b/source/frontends/sdl/main.cpp @@ -7,7 +7,6 @@ #include "StdAfx.h" #include "linux/benchmark.h" #include "linux/context.h" -#include "linux/network/uthernet2.h" #include "frontends/common2/fileregistry.h" #include "frontends/common2/utils.h" @@ -95,7 +94,7 @@ void run_sdl(int argc, const char * argv []) std::shared_ptr paddle(new sa2::Gamepad(0)); const Initialisation init(frame, paddle); applyOptions(options); - frame->Initialize(); + frame->Begin(); std::cerr << "Default GL swap interval: " << SDL_GL_GetSwapInterval() << std::endl; @@ -143,7 +142,6 @@ void run_sdl(int argc, const char * argv []) eventTimer.tic(); sa2::writeAudio(); - processEventsUthernet2(5); frame->ProcessEvents(quit); eventTimer.toc(); @@ -174,6 +172,7 @@ void run_sdl(int argc, const char * argv []) std::cerr << "Actual clock: " << actualClock << " Hz, " << timeInSeconds << " s" << std::endl; sa2::stopAudio(); } + frame->End(); #endif } diff --git a/source/frontends/sdl/renderer/sdlrendererframe.cpp b/source/frontends/sdl/renderer/sdlrendererframe.cpp index cc87c2d91..be0c8fb49 100644 --- a/source/frontends/sdl/renderer/sdlrendererframe.cpp +++ b/source/frontends/sdl/renderer/sdlrendererframe.cpp @@ -30,9 +30,12 @@ namespace sa2 throw std::runtime_error(SDL_GetError()); } - const Uint32 format = SDL_PIXELFORMAT_ARGB8888; - printRendererInfo(std::cerr, myRenderer, format, options.sdlDriver); + printRendererInfo(std::cerr, myRenderer, ourFormat, options.sdlDriver); + } + void SDLRendererFrame::Initialize(bool resetVideoState) + { + SDLFrame::Initialize(resetVideoState); Video & video = GetVideo(); const int width = video.GetFrameBufferWidth(); @@ -40,7 +43,7 @@ namespace sa2 const int sw = video.GetFrameBufferBorderlessWidth(); const int sh = video.GetFrameBufferBorderlessHeight(); - myTexture.reset(SDL_CreateTexture(myRenderer.get(), format, SDL_TEXTUREACCESS_STATIC, width, height), SDL_DestroyTexture); + myTexture.reset(SDL_CreateTexture(myRenderer.get(), ourFormat, SDL_TEXTUREACCESS_STATIC, width, height), SDL_DestroyTexture); myRect.x = video.GetFrameBufferBorderWidth(); myRect.y = video.GetFrameBufferBorderHeight(); diff --git a/source/frontends/sdl/renderer/sdlrendererframe.h b/source/frontends/sdl/renderer/sdlrendererframe.h index c3a41c8a1..775e5fd60 100644 --- a/source/frontends/sdl/renderer/sdlrendererframe.h +++ b/source/frontends/sdl/renderer/sdlrendererframe.h @@ -16,11 +16,15 @@ namespace sa2 SDLRendererFrame(const common2::EmulatorOptions & options); void VideoPresentScreen() override; + void Initialize(bool resetVideoState) override; protected: void GetRelativeMousePosition(const SDL_MouseMotionEvent & motion, double & x, double & y) const override; private: + + static constexpr Uint32 ourFormat = SDL_PIXELFORMAT_ARGB8888; + SDL_Rect myRect; int myPitch; diff --git a/source/frontends/sdl/sdlframe.cpp b/source/frontends/sdl/sdlframe.cpp index 376a81aea..27fd5d52b 100644 --- a/source/frontends/sdl/sdlframe.cpp +++ b/source/frontends/sdl/sdlframe.cpp @@ -136,8 +136,9 @@ namespace sa2 { } - void SDLFrame::Destroy() + void SDLFrame::End() { + CommonFrame::End(); if (!myFullscreen) { common2::Geometry geometry; @@ -145,7 +146,6 @@ namespace sa2 SDL_GetWindowSize(myWindow.get(), &geometry.width, &geometry.height); saveGeometryToRegistry("sa2", geometry); } - common2::CommonFrame::Destroy(); } void SDLFrame::setGLSwapInterval(const int interval) @@ -159,9 +159,9 @@ namespace sa2 } } - void SDLFrame::Initialize() + void SDLFrame::Begin() { - CommonFrame::Initialize(); + CommonFrame::Begin(); mySpeed.reset(); setGLSwapInterval(myTargetGLSwap); ResetHardware(); diff --git a/source/frontends/sdl/sdlframe.h b/source/frontends/sdl/sdlframe.h index 7995419c1..e6304ea38 100644 --- a/source/frontends/sdl/sdlframe.h +++ b/source/frontends/sdl/sdlframe.h @@ -19,8 +19,8 @@ namespace sa2 public: SDLFrame(const common2::EmulatorOptions & options); - void Initialize() override; - void Destroy() override; + void Begin() override; + void End() override; void FrameRefreshStatus(int drawflags) override; int FrameMessageBox(LPCSTR lpText, LPCSTR lpCaption, UINT uType) override; diff --git a/source/linux/context.cpp b/source/linux/context.cpp index 24873f593..dcf7baf0e 100644 --- a/source/linux/context.cpp +++ b/source/linux/context.cpp @@ -8,6 +8,18 @@ #include "Interface.h" #include "Log.h" +#include "Utilities.h" +#include "SoundCore.h" +#include "CPU.h" +#include "ParallelPrinter.h" +#include "Riff.h" +#include "SaveState.h" +#include "Memory.h" +#include "Speaker.h" +#include "MouseInterface.h" +#include "Debugger/Debug.h" +#include "Mockingboard.h" + namespace { @@ -80,3 +92,72 @@ RegistryContext::~RegistryContext() { Registry::instance.reset(); } + +void InitialiseEmulator() +{ +#ifdef RIFF_SPKR + RiffInitWriteFile("/tmp/Spkr.wav", SPKR_SAMPLE_RATE, 1); +#endif +#ifdef RIFF_MB + RiffInitWriteFile("/tmp/Mockingboard.wav", 44100, 2); +#endif + + g_nAppMode = MODE_RUNNING; + LogFileOutput("Initialisation\n"); + + g_bFullSpeed = false; + + GetVideo().SetVidHD(false); + LoadConfiguration(); + SetCurrentCLK6502(); + GetAppleWindowTitle(); + GetFrame().FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES | DRAW_DISK_STATUS); + + DSInit(); + MB_Initialize(); + SpkrInitialize(); + + MemInitialize(); + + CardManager & cardManager = GetCardMgr(); + cardManager.GetDisk2CardMgr().Reset(); + if (cardManager.QuerySlot(SLOT7) == CT_GenericHDD) + { + dynamic_cast(cardManager.GetRef(SLOT7)).Reset(true); + } + + Snapshot_Startup(); + + DebugInitialize(); +} + +void DestroyEmulator() +{ + CardManager & cardManager = GetCardMgr(); + + Snapshot_Shutdown(); + CMouseInterface* pMouseCard = cardManager.GetMouseCard(); + if (pMouseCard) + { + pMouseCard->Reset(); + } + MemDestroy(); + + SpkrDestroy(); + MB_Destroy(); + DSUninit(); + + tfe_shutdown(); + + if (cardManager.QuerySlot(SLOT7) == CT_GenericHDD) + { + dynamic_cast(cardManager.GetRef(SLOT7)).Destroy(); + } + + PrintDestroy(); + CpuDestroy(); + DebugDestroy(); + + GetCardMgr().GetDisk2CardMgr().Destroy(); + RiffFinishWriteFile(); +} diff --git a/source/linux/context.h b/source/linux/context.h index 6099d4b89..f1c713f19 100644 --- a/source/linux/context.h +++ b/source/linux/context.h @@ -8,6 +8,9 @@ class Registry; void SetFrame(const std::shared_ptr & frame); +void InitialiseEmulator(); +void DestroyEmulator(); + // RAII around Frame Registry and Paddle class Initialisation { diff --git a/source/linux/linuxframe.cpp b/source/linux/linuxframe.cpp index cdbc9cf7b..ad238efbe 100644 --- a/source/linux/linuxframe.cpp +++ b/source/linux/linuxframe.cpp @@ -1,5 +1,6 @@ #include "StdAfx.h" #include "linux/linuxframe.h" +#include "linux/context.h" #include "Interface.h" #include "Log.h" #include "Core.h" @@ -28,7 +29,7 @@ void LinuxFrame::SetFullScreenShowSubunitStatus(bool /* bShow */) { } -bool LinuxFrame::GetBestDisplayResolutionForFullScreen(UINT& /* bestWidth */, UINT& /* bestHeight */ , UINT /* userSpecifiedHeight */) +bool LinuxFrame::GetBestDisplayResolutionForFullScreen(UINT& /* bestWidth */, UINT& /* bestHeight */, UINT /* userSpecifiedWidth */, UINT /* userSpecifiedHeight */) { return false; } @@ -46,15 +47,21 @@ void LinuxFrame::SetLoadedSaveStateFlag(const bool /* bFlag */) { } -void LinuxFrame::Initialize() +void LinuxFrame::ResizeWindow() +{ +} + +void LinuxFrame::Initialize(bool resetVideoState) { - static_assert(sizeof(bgra_t) == 4, "Invalid size of bgra_t"); Video & video = GetVideo(); const size_t numberOfPixels = video.GetFrameBufferWidth() * video.GetFrameBufferHeight(); + + static_assert(sizeof(bgra_t) == 4, "Invalid size of bgra_t"); const size_t numberOfBytes = sizeof(bgra_t) * numberOfPixels; + myFramebuffer.resize(numberOfBytes); - video.Initialize(myFramebuffer.data()); + video.Initialize(myFramebuffer.data(), resetVideoState); LogFileTimeUntilFirstKeyReadReset(); } @@ -97,17 +104,30 @@ void LinuxFrame::Cycle50ScanLines() ApplyVideoModeChange(); } -void LinuxFrame::Restart() -{ - LogFileOutput("Restart: not implemented\n"); -} - void LinuxFrame::GetBitmap(LPCSTR lpBitmapName, LONG cb, LPVOID lpvBits) { LogFileOutput("LoadBitmap: could not load resource %s\n", lpBitmapName); memset(lpvBits, 0, cb); } +void LinuxFrame::Begin() +{ + InitialiseEmulator(); + Initialize(true); +} + +void LinuxFrame::End() +{ + Destroy(); + DestroyEmulator(); +} + +void LinuxFrame::Restart() +{ + End(); + Begin(); +} + int MessageBox(HWND, LPCSTR lpText, LPCSTR lpCaption, UINT uType) { return GetFrame().FrameMessageBox(lpText, lpCaption, uType); diff --git a/source/linux/linuxframe.h b/source/linux/linuxframe.h index eb1650cf4..97cea1e55 100644 --- a/source/linux/linuxframe.h +++ b/source/linux/linuxframe.h @@ -7,7 +7,7 @@ class LinuxFrame : public FrameBase { public: - void Initialize() override; + void Initialize(bool resetVideoState) override; void Destroy() override; void FrameDrawDiskLEDS() override; @@ -15,15 +15,16 @@ class LinuxFrame : public FrameBase void FrameRefreshStatus(int drawflags) override; void FrameUpdateApple2Type() override; void FrameSetCursorPosByMousePos() override; + void ResizeWindow() override; void SetFullScreenShowSubunitStatus(bool bShow) override; - bool GetBestDisplayResolutionForFullScreen(UINT& bestWidth, UINT& bestHeight, UINT userSpecifiedHeight = 0) override; + bool GetBestDisplayResolutionForFullScreen(UINT& bestWidth, UINT& bestHeight, UINT userSpecifiedWidth = 0, UINT userSpecifiedHeight = 0) override; int SetViewportScale(int nNewScale, bool bForce = false) override; void SetAltEnterToggleFullScreen(bool mode) override; void SetLoadedSaveStateFlag(const bool bFlag) override; - void Restart() override; + void Restart() override; // calls End() - Begin() void GetBitmap(LPCSTR lpBitmapName, LONG cb, LPVOID lpvBits) override; void CycleVideoType(); @@ -31,6 +32,12 @@ class LinuxFrame : public FrameBase void ApplyVideoModeChange(); + // these are wrappers around Initialize / Destroy that take care of initialising the emulator components + // FrameBase::Initialize and ::Destroy only deal with the video part of the Frame, not the emulator + // in AppleWin this happens in AppleWin.cpp, but it is useful to share it + virtual void Begin(); + virtual void End(); + protected: std::vector myFramebuffer; }; diff --git a/source/linux/network/uthernet2.cpp b/source/linux/network/uthernet2.cpp index d768b9b6c..31d98c6db 100644 --- a/source/linux/network/uthernet2.cpp +++ b/source/linux/network/uthernet2.cpp @@ -1,5 +1,7 @@ #include #include "linux/network/uthernet2.h" +#include "linux/network/slirp2.h" +#include "linux/network/registers.h" #include #include @@ -23,11 +25,6 @@ // #define U2_LOG_STATE // #define U2_LOG_UNKNOWN -#include "linux/network/uthernet2.h" -#include "linux/network/registers.h" - -#include "linux/network/slirp2.h" - #ifndef U2_USE_SLIRP #include "linux/network/tfe2.h" #include "Tfe/tfe.h" @@ -39,86 +36,6 @@ namespace { - struct Socket - { - uint16_t transmitBase; - uint16_t transmitSize; - uint16_t receiveBase; - uint16_t receiveSize; - uint16_t registers; - - uint16_t sn_rx_wr; - uint16_t sn_rx_rsr; - - uint8_t sn_sr = SN_SR_CLOSED; - - int myFD = -1; - int myErrno = 0; - - void clearFD(); - void setFD(const int fd, const int status); - void process(); - - ~Socket(); - }; - - void Socket::clearFD() - { - if (myFD != -1) - { - close(myFD); - } - myFD = -1; - sn_sr = SN_SR_CLOSED; - } - - void Socket::setFD(const int fd, const int status) - { - clearFD(); - myFD = fd; - myErrno = 0; - sn_sr = status; - } - - Socket::~Socket() - { - clearFD(); - } - - void Socket::process() - { - if (myFD != -1 && sn_sr == SN_SR_SOCK_INIT && myErrno == EINPROGRESS) - { - pollfd pfd = {.fd = myFD, .events = POLLOUT}; - if (poll(&pfd, 1, 0) > 0) - { - int err = 0; - socklen_t elen = sizeof err; - getsockopt(myFD, SOL_SOCKET, SO_ERROR, &err, &elen); - - if (err == 0) - { - myErrno = 0; - sn_sr = SN_SR_ESTABLISHED; -#ifdef U2_LOG_STATE - LogFileOutput("U2: TCP[]: CONNECTED\n"); -#endif - } - } - } - } - - std::vector memory; - std::vector sockets; - uint8_t modeRegister = 0; - uint16_t dataAddress = 0; - -#ifdef U2_USE_SLIRP - std::shared_ptr slirp; -#endif - - void initialise(); - uint16_t readNetworkWord(const uint8_t * address) { const uint16_t network = *reinterpret_cast(address); @@ -126,32 +43,13 @@ namespace return host; } - bool isThereRoomFor(const size_t i, const size_t len, const size_t header) - { - const Socket & socket = sockets[i]; - const uint16_t rsr = socket.sn_rx_rsr; // already present - const uint16_t size = socket.receiveSize; // total size - - return rsr + len + header < size; // "not =": we do not want to fill the buffer. - } - - uint16_t getFreeRoom(const size_t i) - { - const Socket & socket = sockets[i]; - const uint16_t rsr = socket.sn_rx_rsr; // already present - const uint16_t size = socket.receiveSize; // total size - - return size - rsr; - } - uint8_t getIByte(const uint16_t value, const size_t shift) { return (value >> shift) & 0xFF; } - void write8(const size_t i, const uint8_t value) + void write8(Socket & socket, std::vector & memory, const uint8_t value) { - Socket & socket = sockets[i]; const uint16_t base = socket.receiveBase; const uint16_t address = base + socket.sn_rx_wr; memory[address] = value; @@ -160,903 +58,997 @@ namespace } // reverses the byte order - void write16(const size_t i, const uint16_t value) + void write16(Socket & socket, std::vector & memory, const uint16_t value) { - write8(i, getIByte(value, 8)); // high - write8(i, getIByte(value, 0)); // low + write8(socket, memory, getIByte(value, 8)); // high + write8(socket, memory, getIByte(value, 0)); // low } - void writeData(const size_t i, const uint8_t * data, const size_t len) + void writeData(Socket & socket, std::vector & memory, const uint8_t * data, const size_t len) { for (size_t c = 0; c < len; ++c) { - write8(i, data[c]); + write8(socket, memory, data[c]); } } // no byte reversal template - void writeAny(const size_t i, const T & t) + void writeAny(Socket & socket, std::vector & memory, const T & t) { const uint8_t * data = reinterpret_cast(&t); const uint16_t len = sizeof(T); - writeData(i, data, len); + writeData(socket, memory, data, len); } - void writeDataMacRaw(const size_t i, const uint8_t * data, const size_t len) + void writeDataMacRaw(Socket & socket, std::vector & memory, const uint8_t * data, const size_t len) { // size includes sizeof(size) const uint16_t size = len + sizeof(uint16_t); - write16(i, size); - writeData(i, data, len); + write16(socket, memory, size); + writeData(socket, memory, data, len); } - void writeDataForProtocol(const size_t i, const uint8_t * data, const size_t len, const sockaddr_in & source) + void writeDataForProtocol(Socket & socket, std::vector & memory, const uint8_t * data, const size_t len, const sockaddr_in & source) { - const Socket & socket = sockets[i]; - if (socket.sn_sr == SN_SR_SOCK_UDP) { // these are already in network order - writeAny(i, source.sin_addr); - writeAny(i, source.sin_port); + writeAny(socket, memory, source.sin_addr); + writeAny(socket, memory, source.sin_port); // size does not include sizeof(size) - write16(i, len); + write16(socket, memory, len); } // no header for TCP - writeData(i, data, len); + writeData(socket, memory, data, len); } - void setSocketModeRegister(const size_t i, const uint16_t address, const uint8_t value) +} + +Socket::Socket() + : sn_sr(SN_SR_CLOSED) + , myFD(-1) + , myErrno(0) +{ + +} + +void Socket::clearFD() +{ + if (myFD != -1) { - memory[address] = value; - const uint8_t protocol = value & SN_MR_PROTO_MASK; - switch (protocol) + close(myFD); + } + myFD = -1; + sn_sr = SN_SR_CLOSED; +} + +void Socket::setFD(const int fd, const int status) +{ + clearFD(); + myFD = fd; + myErrno = 0; + sn_sr = status; +} + +Socket::~Socket() +{ + clearFD(); +} + +void Socket::process() +{ + if (myFD != -1 && sn_sr == SN_SR_SOCK_INIT && myErrno == EINPROGRESS) + { + pollfd pfd = {.fd = myFD, .events = POLLOUT}; + if (poll(&pfd, 1, 0) > 0) { - case SN_MR_CLOSED: + int err = 0; + socklen_t elen = sizeof err; + getsockopt(myFD, SOL_SOCKET, SO_ERROR, &err, &elen); + + if (err == 0) + { + myErrno = 0; + sn_sr = SN_SR_ESTABLISHED; #ifdef U2_LOG_STATE - LogFileOutput("U2: Mode[%d]: CLOSED\n", i); + LogFileOutput("U2: TCP[]: CONNECTED\n"); #endif - break; - case SN_MR_TCP: + } + } + } +} + +bool Socket::isThereRoomFor(const size_t len, const size_t header) const +{ + const uint16_t rsr = sn_rx_rsr; // already present + const uint16_t size = receiveSize; // total size + +return rsr + len + header < size; // "not =": we do not want to fill the buffer. +} + +uint16_t Socket::getFreeRoom() const +{ + const uint16_t rsr = sn_rx_rsr; // already present + const uint16_t size = receiveSize; // total size + + return size - rsr; +} + +Uthernet2::Uthernet2(UINT slot) : Card(CT_Uthernet2, slot) +{ + #ifdef U2_USE_SLIRP + mySlirp = std::make_shared(); +#else + const int check = tfe_enabled; + tfe_init(true); + if (tfe_enabled != check) + { + // tfe initialisation failed + return; + } +#endif + Reset(true); +} + +void Uthernet2::setSocketModeRegister(const size_t i, const uint16_t address, const uint8_t value) +{ + myMemory[address] = value; + const uint8_t protocol = value & SN_MR_PROTO_MASK; + switch (protocol) + { + case SN_MR_CLOSED: #ifdef U2_LOG_STATE - LogFileOutput("U2: Mode[%d]: TCP\n", i); + LogFileOutput("U2: Mode[%d]: CLOSED\n", i); #endif - break; - case SN_MR_UDP: + break; + case SN_MR_TCP: #ifdef U2_LOG_STATE - LogFileOutput("U2: Mode[%d]: UDP\n", i); + LogFileOutput("U2: Mode[%d]: TCP\n", i); #endif - break; - case SN_MR_IPRAW: + break; + case SN_MR_UDP: #ifdef U2_LOG_STATE - LogFileOutput("U2: Mode[%d]: IPRAW\n", i); + LogFileOutput("U2: Mode[%d]: UDP\n", i); #endif - break; - case SN_MR_MACRAW: + break; + case SN_MR_IPRAW: #ifdef U2_LOG_STATE - LogFileOutput("U2: Mode[%d]: MACRAW\n", i); + LogFileOutput("U2: Mode[%d]: IPRAW\n", i); #endif - break; + break; + case SN_MR_MACRAW: +#ifdef U2_LOG_STATE + LogFileOutput("U2: Mode[%d]: MACRAW\n", i); +#endif + break; #ifdef U2_LOG_UNKNOWN - default: - LogFileOutput("U2: Unknown protocol: %02x\n", protocol); + default: + LogFileOutput("U2: Unknown protocol: %02x\n", protocol); #endif - } } +} - void setTXSizes(const uint16_t address, uint8_t value) +void Uthernet2::setTXSizes(const uint16_t address, uint8_t value) +{ + myMemory[address] = value; + uint16_t base = TX_BASE; + const uint16_t end = RX_BASE; + for (Socket & socket : mySockets) { - memory[address] = value; - uint16_t base = TX_BASE; - const uint16_t end = RX_BASE; - for (Socket & socket : sockets) - { - socket.transmitBase = base; + socket.transmitBase = base; - const uint8_t bits = value & 0x03; - value >>= 2; + const uint8_t bits = value & 0x03; + value >>= 2; - const uint16_t size = 1 << (10 + bits); - base += size; + const uint16_t size = 1 << (10 + bits); + base += size; - if (base > end) - { - base = end; - } - socket.transmitSize = base - socket.transmitBase; + if (base > end) + { + base = end; } + socket.transmitSize = base - socket.transmitBase; } +} - void setRXSizes(const uint16_t address, uint8_t value) +void Uthernet2::setRXSizes(const uint16_t address, uint8_t value) +{ + myMemory[address] = value; + uint16_t base = RX_BASE; + const uint16_t end = MEM_SIZE; + for (Socket & socket : mySockets) { - memory[address] = value; - uint16_t base = RX_BASE; - const uint16_t end = MEM_SIZE; - for (Socket & socket : sockets) - { - socket.receiveBase = base; + socket.receiveBase = base; - const uint8_t bits = value & 0x03; - value >>= 2; + const uint8_t bits = value & 0x03; + value >>= 2; - const uint16_t size = 1 << (10 + bits); - base += size; + const uint16_t size = 1 << (10 + bits); + base += size; - if (base > end) - { - base = end; - } - socket.receiveSize = base - socket.receiveBase; + if (base > end) + { + base = end; } + socket.receiveSize = base - socket.receiveBase; } +} - uint16_t getTXDataSize(const size_t i) - { - const Socket & socket = sockets[i]; - const uint16_t size = socket.transmitSize; - const uint16_t mask = size - 1; - - const int sn_tx_rd = readNetworkWord(memory.data() + socket.registers + SN_TX_RD0) & mask; - const int sn_tx_wr = readNetworkWord(memory.data() + socket.registers + SN_TX_WR0) & mask; +uint16_t Uthernet2::getTXDataSize(const size_t i) const +{ + const Socket & socket = mySockets[i]; + const uint16_t size = socket.transmitSize; + const uint16_t mask = size - 1; - int dataPresent = sn_tx_wr - sn_tx_rd; - if (dataPresent < 0) - { - dataPresent += size; - } - return dataPresent; - } + const int sn_tx_rd = readNetworkWord(myMemory.data() + socket.registers + SN_TX_RD0) & mask; + const int sn_tx_wr = readNetworkWord(myMemory.data() + socket.registers + SN_TX_WR0) & mask; - uint8_t getTXFreeSizeRegister(const size_t i, const size_t shift) + int dataPresent = sn_tx_wr - sn_tx_rd; + if (dataPresent < 0) { - const int size = sockets[i].transmitSize; - const uint16_t present = getTXDataSize(i); - const uint16_t free = size - present; - const uint8_t reg = getIByte(free, shift); - return reg; + dataPresent += size; } + return dataPresent; +} - uint8_t getRXDataSizeRegister(const size_t i, const size_t shift) - { - const uint16_t rsr = sockets[i].sn_rx_rsr; - const uint8_t reg = getIByte(rsr, shift); - return reg; - } +uint8_t Uthernet2::getTXFreeSizeRegister(const size_t i, const size_t shift) const +{ + const int size = mySockets[i].transmitSize; + const uint16_t present = getTXDataSize(i); + const uint16_t free = size - present; + const uint8_t reg = getIByte(free, shift); + return reg; +} - void updateRSR(const size_t i) - { - Socket & socket = sockets[i]; +uint8_t Uthernet2::getRXDataSizeRegister(const size_t i, const size_t shift) const +{ + const uint16_t rsr = mySockets[i].sn_rx_rsr; + const uint8_t reg = getIByte(rsr, shift); + return reg; +} + +void Uthernet2::updateRSR(const size_t i) +{ + Socket & socket = mySockets[i]; - const int size = socket.receiveSize; - const uint16_t mask = size - 1; + const int size = socket.receiveSize; + const uint16_t mask = size - 1; - const int sn_rx_rd = readNetworkWord(memory.data() + socket.registers + SN_RX_RD0) & mask; - const int sn_rx_wr = socket.sn_rx_wr & mask; - int dataPresent = sn_rx_wr - sn_rx_rd; - if (dataPresent < 0) - { - dataPresent += size; - } - // is this logic correct? - // here we are re-synchronising the size with the pointers - // elsewhere I have seen people updating this value - // by the amount of how much 0x28 has moved forward - // but then we need to keep track of where it was - // the final result should be the same + const int sn_rx_rd = readNetworkWord(myMemory.data() + socket.registers + SN_RX_RD0) & mask; + const int sn_rx_wr = socket.sn_rx_wr & mask; + int dataPresent = sn_rx_wr - sn_rx_rd; + if (dataPresent < 0) + { + dataPresent += size; + } + // is this logic correct? + // here we are re-synchronising the size with the pointers + // elsewhere I have seen people updating this value + // by the amount of how much 0x28 has moved forward + // but then we need to keep track of where it was + // the final result should be the same #ifdef U2_LOG_TRAFFIC - if (socket.sn_rx_rsr != dataPresent) - { - LogFileOutput("U2: RECV[%d]: %d -> %d bytes\n", i, socket.sn_rx_rsr, dataPresent); - } -#endif - socket.sn_rx_rsr = dataPresent; + if (socket.sn_rx_rsr != dataPresent) + { + LogFileOutput("U2: RECV[%d]: %d -> %d bytes\n", i, socket.sn_rx_rsr, dataPresent); } +#endif + socket.sn_rx_rsr = dataPresent; +} - void receiveOnePacketMacRaw(const size_t i) - { - const Socket & socket = sockets[i]; - const uint16_t rsr = socket.sn_rx_rsr; +void Uthernet2::receiveOnePacketMacRaw(const size_t i) +{ + Socket & socket = mySockets[i]; + const uint16_t rsr = socket.sn_rx_rsr; #ifdef U2_USE_SLIRP + { + std::queue> & queue = mySlirp->getQueue(); + while (!queue.empty()) { - std::queue> & queue = slirp->getQueue(); - while (!queue.empty()) + const std::vector & packet = queue.front(); + if (socket.isThereRoomFor(packet.size(), sizeof(uint16_t))) { - const std::vector & packet = queue.front(); - if (isThereRoomFor(i, packet.size(), sizeof(uint16_t))) - { - writeDataMacRaw(i, packet.data(), packet.size()); - queue.pop(); + writeDataMacRaw(socket, myMemory, packet.data(), packet.size()); + queue.pop(); #ifdef U2_LOG_TRAFFIC - LogFileOutput("U2: READ MACRAW[%d]: +%d -> %d bytes (%04x)\n", i, packet.size(), socket.sn_rx_rsr, socket.sn_rx_wr); + LogFileOutput("U2: READ MACRAW[%d]: +%d -> %d bytes (%04x)\n", i, packet.size(), socket.sn_rx_rsr, socket.sn_rx_wr); #endif - } - else - { - break; - } + } + else + { + break; } } + } #else + { + BYTE buffer[MAX_RXLENGTH]; + int len; + if (tfeReceiveOnePacket(memory.data() + SHAR0, sizeof(buffer), buffer, len)) { - BYTE buffer[MAX_RXLENGTH]; - int len; - if (tfeReceiveOnePacket(memory.data() + SHAR0, sizeof(buffer), buffer, len)) + if (isThereRoomFor(i, len, sizeof(uint16_t))) { - if (isThereRoomFor(i, len, sizeof(uint16_t))) - { - writeDataMacRaw(i, buffer, len); + writeDataMacRaw(i, buffer, len); #ifdef U2_LOG_TRAFFIC - LogFileOutput("U2: READ MACRAW[%d]: +%d -> %d bytes\n", i, len, socket.sn_rx_rsr); + LogFileOutput("U2: READ MACRAW[%d]: +%d -> %d bytes\n", i, len, socket.sn_rx_rsr); #endif - } - else - { - // drop it + } + else + { + // drop it #ifdef U2_LOG_TRAFFIC - LogFileOutput("U2: SKIP MACRAW[%d]: %d bytes\n", i, len); + LogFileOutput("U2: SKIP MACRAW[%d]: %d bytes\n", i, len); #endif - } } } -#endif } +#endif +} - // UDP & TCP - void receiveOnePacketFromSocket(const size_t i) +// UDP & TCP +void Uthernet2::receiveOnePacketFromSocket(const size_t i) +{ + Socket & socket = mySockets[i]; + if (socket.myFD != -1) { - Socket & socket = sockets[i]; - if (socket.myFD != -1) + const uint16_t freeRoom = socket.getFreeRoom(); + if (freeRoom > 32) // avoid meaningless reads { - const uint16_t freeRoom = getFreeRoom(i); - if (freeRoom > 32) // avoid meaningless reads - { - std::vector buffer(freeRoom - 1); // do not fill the buffer completely - sockaddr_in source = {0}; - socklen_t len = sizeof(sockaddr_in); - const ssize_t data = recvfrom(socket.myFD, buffer.data(), buffer.size(), 0, (struct sockaddr *) &source, &len); + std::vector buffer(freeRoom - 1); // do not fill the buffer completely + sockaddr_in source = {0}; + socklen_t len = sizeof(sockaddr_in); + const ssize_t data = recvfrom(socket.myFD, buffer.data(), buffer.size(), 0, (struct sockaddr *) &source, &len); #ifdef U2_LOG_TRAFFIC - const char * proto = socket.sn_sr == SN_SR_SOCK_UDP ? "UDP" : "TCP"; + const char * proto = socket.sn_sr == SN_SR_SOCK_UDP ? "UDP" : "TCP"; #endif - if (data > 0) - { - writeDataForProtocol(i, buffer.data(), data, source); + if (data > 0) + { + writeDataForProtocol(socket, myMemory, buffer.data(), data, source); #ifdef U2_LOG_TRAFFIC - LogFileOutput("U2: READ %s[%d]: +%d -> %d bytes\n", proto, i, data, socket.sn_rx_rsr); + LogFileOutput("U2: READ %s[%d]: +%d -> %d bytes\n", proto, i, data, socket.sn_rx_rsr); #endif - } - else if (data == 0) - { - // gracefull termination - socket.clearFD(); - } - else // data < 0; + } + else if (data == 0) + { + // gracefull termination + socket.clearFD(); + } + else // data < 0; + { + const int error = errno; + if (error != EAGAIN && error != EWOULDBLOCK) { - const int error = errno; - if (error != EAGAIN && error != EWOULDBLOCK) - { #ifdef U2_LOG_TRAFFIC - LogFileOutput("U2: ERROR %s[%d]: %d\n", proto, i, error); + LogFileOutput("U2: ERROR %s[%d]: %d\n", proto, i, error); #endif - socket.clearFD(); - } + socket.clearFD(); } } } } +} - void receiveOnePacket(const size_t i) +void Uthernet2::receiveOnePacket(const size_t i) +{ + const Socket & socket = mySockets[i]; + switch (socket.sn_sr) { - const Socket & socket = sockets[i]; - switch (socket.sn_sr) - { - case SN_SR_SOCK_MACRAW: - receiveOnePacketMacRaw(i); - break; - case SN_SR_ESTABLISHED: - case SN_SR_SOCK_UDP: - receiveOnePacketFromSocket(i); - break; - case SN_SR_CLOSED: - break; // nothing to do + case SN_SR_SOCK_MACRAW: + receiveOnePacketMacRaw(i); + break; + case SN_SR_ESTABLISHED: + case SN_SR_SOCK_UDP: + receiveOnePacketFromSocket(i); + break; + case SN_SR_CLOSED: + break; // nothing to do #ifdef U2_LOG_UNKNOWN - default: - LogFileOutput("U2: READ[%d]: unknown mode: %02x\n", i, socket.sn_sr); + default: + LogFileOutput("U2: READ[%d]: unknown mode: %02x\n", i, socket.sn_sr); #endif - }; - } + }; +} - void sendDataMacRaw(const size_t i, const std::vector & data) - { +void Uthernet2::sendDataMacRaw(const size_t i, const std::vector & data) const +{ #ifdef U2_LOG_TRAFFIC - LogFileOutput("U2: SEND MACRAW[%d]: %d bytes\n", i, data.size()); + LogFileOutput("U2: SEND MACRAW[%d]: %d bytes\n", i, data.size()); #endif #ifdef U2_USE_SLIRP - slirp->sendFromGuest(data.data(), data.size()); + mySlirp->sendFromGuest(data.data(), data.size()); #else - tfeTransmitOnePacket(data.data(), data.size()); + tfeTransmitOnePacket(data.data(), data.size()); #endif - } +} - void sendDataToSocket(const size_t i, std::vector & data) +void Uthernet2::sendDataToSocket(const size_t i, std::vector & data) +{ + Socket & socket = mySockets[i]; + if (socket.myFD != -1) { - Socket & socket = sockets[i]; - if (socket.myFD != -1) - { - sockaddr_in destination = {}; - destination.sin_family = AF_INET; + sockaddr_in destination = {}; + destination.sin_family = AF_INET; - // already in network order - // this seems to be ignored for TCP, and so we reuse the same code - const uint8_t * dest = memory.data() + socket.registers + SN_DIPR0; - destination.sin_addr.s_addr = *reinterpret_cast(dest); - destination.sin_port = *reinterpret_cast(memory.data() + socket.registers + SN_DPORT0); + // already in network order + // this seems to be ignored for TCP, and so we reuse the same code + const uint8_t * dest = myMemory.data() + socket.registers + SN_DIPR0; + destination.sin_addr.s_addr = *reinterpret_cast(dest); + destination.sin_port = *reinterpret_cast(myMemory.data() + socket.registers + SN_DPORT0); - const ssize_t res = sendto(socket.myFD, data.data(), data.size(), 0, (const struct sockaddr *) &destination, sizeof(destination)); + const ssize_t res = sendto(socket.myFD, data.data(), data.size(), 0, (const struct sockaddr *) &destination, sizeof(destination)); #ifdef U2_LOG_TRAFFIC - const char * proto = socket.sn_sr == SN_SR_SOCK_UDP ? "UDP" : "TCP"; - LogFileOutput("U2: SEND %s[%d]: %d of %d bytes\n", proto, i, res, data.size()); + const char * proto = socket.sn_sr == SN_SR_SOCK_UDP ? "UDP" : "TCP"; + LogFileOutput("U2: SEND %s[%d]: %d of %d bytes\n", proto, i, res, data.size()); #endif - if (res < 0) + if (res < 0) + { + const int error = errno; + if (error != EAGAIN && error != EWOULDBLOCK) { - const int error = errno; - if (error != EAGAIN && error != EWOULDBLOCK) - { #ifdef U2_LOG_TRAFFIC - LogFileOutput("U2: ERROR %s[%d]: %d\n", proto, i, error); + LogFileOutput("U2: ERROR %s[%d]: %d\n", proto, i, error); #endif - socket.clearFD(); - } + socket.clearFD(); } } } +} - void sendData(const size_t i) - { - const Socket & socket = sockets[i]; - const uint16_t size = socket.transmitSize; - const uint16_t mask = size - 1; +void Uthernet2::sendData(const size_t i) +{ + const Socket & socket = mySockets[i]; + const uint16_t size = socket.transmitSize; + const uint16_t mask = size - 1; - const int sn_tx_rr = readNetworkWord(memory.data() + socket.registers + SN_TX_RD0) & mask; - const int sn_tx_wr = readNetworkWord(memory.data() + socket.registers + SN_TX_WR0) & mask; + const int sn_tx_rr = readNetworkWord(myMemory.data() + socket.registers + SN_TX_RD0) & mask; + const int sn_tx_wr = readNetworkWord(myMemory.data() + socket.registers + SN_TX_WR0) & mask; - const uint16_t base = socket.transmitBase; - const uint16_t rr_address = base + sn_tx_rr; - const uint16_t wr_address = base + sn_tx_wr; + const uint16_t base = socket.transmitBase; + const uint16_t rr_address = base + sn_tx_rr; + const uint16_t wr_address = base + sn_tx_wr; - std::vector data; - if (rr_address < wr_address) - { - data.assign(memory.begin() + rr_address, memory.begin() + wr_address); - } - else - { - const uint16_t end = base + size; - data.assign(memory.begin() + rr_address, memory.begin() + end); - data.insert(data.end(), memory.begin() + base, memory.begin() + wr_address); - } + std::vector data; + if (rr_address < wr_address) + { + data.assign(myMemory.begin() + rr_address, myMemory.begin() + wr_address); + } + else + { + const uint16_t end = base + size; + data.assign(myMemory.begin() + rr_address, myMemory.begin() + end); + data.insert(data.end(), myMemory.begin() + base, myMemory.begin() + wr_address); + } - // move read pointer to writer - memory[socket.registers + SN_TX_RD0] = getIByte(sn_tx_wr, 8); - memory[socket.registers + SN_TX_RD1] = getIByte(sn_tx_wr, 0); + // move read pointer to writer + myMemory[socket.registers + SN_TX_RD0] = getIByte(sn_tx_wr, 8); + myMemory[socket.registers + SN_TX_RD1] = getIByte(sn_tx_wr, 0); - switch (socket.sn_sr) - { - case SN_SR_SOCK_MACRAW: - sendDataMacRaw(i, data); - break; - case SN_SR_ESTABLISHED: - case SN_SR_SOCK_UDP: - sendDataToSocket(i, data); - break; + switch (socket.sn_sr) + { + case SN_SR_SOCK_MACRAW: + sendDataMacRaw(i, data); + break; + case SN_SR_ESTABLISHED: + case SN_SR_SOCK_UDP: + sendDataToSocket(i, data); + break; #ifdef U2_LOG_UNKNOWN - default: - LogFileOutput("U2: SEND[%d]: unknown mode: %02x\n", i, socket.sn_sr); + default: + LogFileOutput("U2: SEND[%d]: unknown mode: %02x\n", i, socket.sn_sr); #endif - } } +} - void resetRXTXBuffers(const size_t i) - { - Socket & socket = sockets[i]; - socket.sn_rx_wr = 0x00; - socket.sn_rx_rsr = 0x00; - memory[socket.registers + SN_TX_RD0] = 0x00; - memory[socket.registers + SN_TX_RD1] = 0x00; - memory[socket.registers + SN_TX_WR0] = 0x00; - memory[socket.registers + SN_TX_WR1] = 0x00; - memory[socket.registers + SN_RX_RD0] = 0x00; - memory[socket.registers + SN_RX_RD1] = 0x00; - } +void Uthernet2::resetRXTXBuffers(const size_t i) +{ + Socket & socket = mySockets[i]; + socket.sn_rx_wr = 0x00; + socket.sn_rx_rsr = 0x00; + myMemory[socket.registers + SN_TX_RD0] = 0x00; + myMemory[socket.registers + SN_TX_RD1] = 0x00; + myMemory[socket.registers + SN_TX_WR0] = 0x00; + myMemory[socket.registers + SN_TX_WR1] = 0x00; + myMemory[socket.registers + SN_RX_RD0] = 0x00; + myMemory[socket.registers + SN_RX_RD1] = 0x00; +} - void openSystemSocket(const size_t i, const int type, const int protocol, const int state) +void Uthernet2::openSystemSocket(const size_t i, const int type, const int protocol, const int state) +{ + Socket & s = mySockets[i]; + const int fd = socket(AF_INET, type | SOCK_NONBLOCK, protocol); + if (fd < -1) { - Socket & s = sockets[i]; - const int fd = socket(AF_INET, type | SOCK_NONBLOCK, protocol); - if (fd < -1) - { #ifdef U2_LOG_STATE - const char * proto = state == SN_SR_SOCK_UDP ? "UDP" : "TCP"; - LogFileOutput("U2: %s[%d]: %s\n", proto, i, strerror(errno)); + const char * proto = state == SN_SR_SOCK_UDP ? "UDP" : "TCP"; + LogFileOutput("U2: %s[%d]: %s\n", proto, i, strerror(errno)); #endif - s.clearFD(); - } - else - { - s.setFD(fd, state); - } + s.clearFD(); + } + else + { + s.setFD(fd, state); } +} - void openSocket(const size_t i) +void Uthernet2::openSocket(const size_t i) +{ + Socket & socket = mySockets[i]; + const uint8_t mr = myMemory[socket.registers + SN_MR]; + const uint8_t protocol = mr & SN_MR_PROTO_MASK; + uint8_t & sr = socket.sn_sr; + switch (protocol) { - Socket & socket = sockets[i]; - const uint8_t mr = memory[socket.registers + SN_MR]; - const uint8_t protocol = mr & SN_MR_PROTO_MASK; - uint8_t & sr = socket.sn_sr; - switch (protocol) - { - case SN_MR_IPRAW: - sr = SN_SR_SOCK_IPRAW; - break; - case SN_MR_MACRAW: - sr = SN_SR_SOCK_MACRAW; - break; - case SN_MR_TCP: - openSystemSocket(i, SOCK_STREAM, IPPROTO_TCP, SN_SR_SOCK_INIT); - break; - case SN_MR_UDP: - openSystemSocket(i, SOCK_DGRAM, IPPROTO_UDP, SN_SR_SOCK_UDP); - break; + case SN_MR_IPRAW: + sr = SN_SR_SOCK_IPRAW; + break; + case SN_MR_MACRAW: + sr = SN_SR_SOCK_MACRAW; + break; + case SN_MR_TCP: + openSystemSocket(i, SOCK_STREAM, IPPROTO_TCP, SN_SR_SOCK_INIT); + break; + case SN_MR_UDP: + openSystemSocket(i, SOCK_DGRAM, IPPROTO_UDP, SN_SR_SOCK_UDP); + break; #ifdef U2_LOG_UNKNOWN - default: - LogFileOutput("U2: OPEN[%d]: unknown mode: %02x\n", i, mr); + default: + LogFileOutput("U2: OPEN[%d]: unknown mode: %02x\n", i, mr); #endif - } - resetRXTXBuffers(i); // needed? + } + resetRXTXBuffers(i); // needed? #ifdef U2_LOG_STATE - LogFileOutput("U2: OPEN[%d]: %02x\n", i, sr); + LogFileOutput("U2: OPEN[%d]: %02x\n", i, sr); #endif - } +} - void closeSocket(const size_t i) - { - Socket & socket = sockets[i]; - socket.clearFD(); +void Uthernet2::closeSocket(const size_t i) +{ + Socket & socket = mySockets[i]; + socket.clearFD(); #ifdef U2_LOG_STATE - LogFileOutput("U2: CLOSE[%d]\n", i); + LogFileOutput("U2: CLOSE[%d]\n", i); #endif - } +} - void connectSocket(const size_t i) - { - Socket & socket = sockets[i]; - const uint8_t * dest = memory.data() + socket.registers + SN_DIPR0; +void Uthernet2::connectSocket(const size_t i) +{ + Socket & socket = mySockets[i]; + const uint8_t * dest = myMemory.data() + socket.registers + SN_DIPR0; - sockaddr_in destination = {}; - destination.sin_family = AF_INET; + sockaddr_in destination = {}; + destination.sin_family = AF_INET; - // already in network order - destination.sin_port = *reinterpret_cast(memory.data() + socket.registers + SN_DPORT0); - destination.sin_addr.s_addr = *reinterpret_cast(dest); + // already in network order + destination.sin_port = *reinterpret_cast(myMemory.data() + socket.registers + SN_DPORT0); + destination.sin_addr.s_addr = *reinterpret_cast(dest); - const int res = connect(socket.myFD, (struct sockaddr *)&destination, sizeof(destination)); + const int res = connect(socket.myFD, (struct sockaddr *)&destination, sizeof(destination)); - if (res == 0) - { - socket.sn_sr = SN_SR_ESTABLISHED; - socket.myErrno = 0; + if (res == 0) + { + socket.sn_sr = SN_SR_ESTABLISHED; + socket.myErrno = 0; #ifdef U2_LOG_STATE - const uint16_t port = readNetworkWord(memory.data() + socket.registers + SN_DPORT0); - LogFileOutput("U2: TCP[%d]: CONNECT to %d.%d.%d.%d:%d\n", i, dest[0], dest[1], dest[2], dest[3], port); + const uint16_t port = readNetworkWord(memory.data() + socket.registers + SN_DPORT0); + LogFileOutput("U2: TCP[%d]: CONNECT to %d.%d.%d.%d:%d\n", i, dest[0], dest[1], dest[2], dest[3], port); #endif - } - else + } + else + { + const int error = errno; + if (error == EINPROGRESS) { - const int error = errno; - if (error == EINPROGRESS) - { - socket.myErrno = error; - } + socket.myErrno = error; + } #ifdef U2_LOG_STATE - LogFileOutput("U2: TCP[%d]: connect: %s\n", i, strerror(error)); + LogFileOutput("U2: TCP[%d]: connect: %s\n", i, strerror(error)); #endif - } } +} - void setCommandRegister(const size_t i, const uint8_t value) +void Uthernet2::setCommandRegister(const size_t i, const uint8_t value) +{ + switch (value) { - switch (value) - { - case SN_CR_OPEN: - openSocket(i); - break; - case SN_CR_CONNECT: - connectSocket(i); - break; - case SN_CR_CLOSE: - case SN_CR_DISCON: - closeSocket(i); - break; - case SN_CR_SEND: - sendData(i); - break; - case SN_CR_RECV: - updateRSR(i); - break; + case SN_CR_OPEN: + openSocket(i); + break; + case SN_CR_CONNECT: + connectSocket(i); + break; + case SN_CR_CLOSE: + case SN_CR_DISCON: + closeSocket(i); + break; + case SN_CR_SEND: + sendData(i); + break; + case SN_CR_RECV: + updateRSR(i); + break; #ifdef U2_LOG_UNKNOWN - default: - LogFileOutput("U2: Unknown command[%d]: %02x\n", i, value); + default: + LogFileOutput("U2: Unknown command[%d]: %02x\n", i, value); #endif - } } +} - uint8_t readSocketRegister(const uint16_t address) +uint8_t Uthernet2::readSocketRegister(const uint16_t address) +{ + const uint16_t i = (address >> 8) - 0x04; + const uint16_t loc = address & 0xFF; + uint8_t value; + switch (loc) { - const uint16_t i = (address >> 8) - 0x04; - const uint16_t loc = address & 0xFF; - uint8_t value; - switch (loc) - { - case SN_MR: - case SN_CR: - value = memory[address]; - break; - case SN_SR: - value = sockets[i].sn_sr; - break; - case SN_TX_FSR0: - value = getTXFreeSizeRegister(i, 8); - break; - case SN_TX_FSR1: - value = getTXFreeSizeRegister(i, 0); - break; - case SN_TX_RD0: - case SN_TX_RD1: - value = memory[address]; - break; - case SN_TX_WR0: - case SN_TX_WR1: - value = memory[address]; - break; - case SN_RX_RSR0: - receiveOnePacket(i); - value = getRXDataSizeRegister(i, 8); - break; - case SN_RX_RSR1: - receiveOnePacket(i); - value = getRXDataSizeRegister(i, 0); - break; - case SN_RX_RD0: - case SN_RX_RD1: - value = memory[address]; - break; - default: + case SN_MR: + case SN_CR: + value = myMemory[address]; + break; + case SN_SR: + value = mySockets[i].sn_sr; + break; + case SN_TX_FSR0: + value = getTXFreeSizeRegister(i, 8); + break; + case SN_TX_FSR1: + value = getTXFreeSizeRegister(i, 0); + break; + case SN_TX_RD0: + case SN_TX_RD1: + value = myMemory[address]; + break; + case SN_TX_WR0: + case SN_TX_WR1: + value = myMemory[address]; + break; + case SN_RX_RSR0: + receiveOnePacket(i); + value = getRXDataSizeRegister(i, 8); + break; + case SN_RX_RSR1: + receiveOnePacket(i); + value = getRXDataSizeRegister(i, 0); + break; + case SN_RX_RD0: + case SN_RX_RD1: + value = myMemory[address]; + break; + default: #ifdef U2_LOG_UNKNOWN - LogFileOutput("U2: Get unknown socket register[%d]: %04x\n", i, address); + LogFileOutput("U2: Get unknown socket register[%d]: %04x\n", i, address); #endif - value = memory[address]; - break; - } - return value; + value = myMemory[address]; + break; } + return value; +} - uint8_t readValueAt(const uint16_t address) +uint8_t Uthernet2::readValueAt(const uint16_t address) +{ + uint8_t value; + switch (address) { - uint8_t value; - switch (address) - { - case MR: - value = modeRegister; - break; - case GAR0 ... UPORT1: - value = memory[address]; - break; - case S0_BASE ... S3_MAX: - value = readSocketRegister(address); - break; - case TX_BASE ... MEM_MAX: - value = memory[address]; - break; - default: + case MR: + value = myModeRegister; + break; + case GAR0 ... UPORT1: + value = myMemory[address]; + break; + case S0_BASE ... S3_MAX: + value = readSocketRegister(address); + break; + case TX_BASE ... MEM_MAX: + value = myMemory[address]; + break; + default: #ifdef U2_LOG_UNKNOWN - LogFileOutput("U2: Read unknown location: %04x\n", address); + LogFileOutput("U2: Read unknown location: %04x\n", address); #endif - // this might not be 100% correct if address >= 0x8000 - // see top of page 13 Uthernet II - value = memory[address & MEM_MAX]; - break; - } - return value; + // this might not be 100% correct if address >= 0x8000 + // see top of page 13 Uthernet II + value = myMemory[address & MEM_MAX]; + break; } + return value; +} - void autoIncrement() +void Uthernet2::autoIncrement() +{ + if (myModeRegister & MR_AI) { - if (modeRegister & MR_AI) + ++myDataAddress; + // Read bottom of Uthernet II page 12 + // Setting the address to values >= 0x8000 is not really supported + switch (myDataAddress) { - ++dataAddress; - // Read bottom of Uthernet II page 12 - // Setting the address to values >= 0x8000 is not really supported - switch (dataAddress) - { - case RX_BASE: - case MEM_SIZE: - dataAddress -= 0x2000; - break; - } + case RX_BASE: + case MEM_SIZE: + myDataAddress -= 0x2000; + break; } } +} - uint8_t readValue() - { - const uint8_t value = readValueAt(dataAddress); - autoIncrement(); - return value; - } +uint8_t Uthernet2::readValue() +{ + const uint8_t value = readValueAt(myDataAddress); + autoIncrement(); + return value; +} - void setIPProtocol(const size_t i, const uint16_t address, const uint8_t value) - { - memory[address] = value; +void Uthernet2::setIPProtocol(const size_t i, const uint16_t address, const uint8_t value) +{ + myMemory[address] = value; #ifdef U2_LOG_STATE - LogFileOutput("U2: IP PROTO[%d] = %d\n", i, value); + LogFileOutput("U2: IP PROTO[%d] = %d\n", i, value); #endif - } +} - void setIPTypeOfService(const size_t i, const uint16_t address, const uint8_t value) - { - memory[address] = value; +void Uthernet2::setIPTypeOfService(const size_t i, const uint16_t address, const uint8_t value) +{ + myMemory[address] = value; #ifdef U2_LOG_STATE - LogFileOutput("U2: IP TOS[%d] = %d\n", i, value); + LogFileOutput("U2: IP TOS[%d] = %d\n", i, value); #endif - } +} - void setIPTTL(const size_t i, const uint16_t address, const uint8_t value) - { - memory[address] = value; +void Uthernet2::setIPTTL(const size_t i, const uint16_t address, const uint8_t value) +{ + myMemory[address] = value; #ifdef U2_LOG_STATE - LogFileOutput("U2: IP TTL[%d] = %d\n", i, value); + LogFileOutput("U2: IP TTL[%d] = %d\n", i, value); #endif - } +} - void writeSocketRegister(const uint16_t address, const uint8_t value) +void Uthernet2::writeSocketRegister(const uint16_t address, const uint8_t value) +{ + const uint16_t i = (address >> 8) - 0x04; + const uint16_t loc = address & 0xFF; + switch (loc) { - const uint16_t i = (address >> 8) - 0x04; - const uint16_t loc = address & 0xFF; - switch (loc) - { - case SN_MR: - setSocketModeRegister(i, address, value); - break; - case SN_CR: - setCommandRegister(i, value); - break; - case SN_PORT0: - case SN_PORT1: - case SN_DPORT0: - case SN_DPORT1: - memory[address] = value; - break; - case SN_DIPR0 ... SN_DIPR3: - memory[address] = value; - break; - case SN_PROTO: - setIPProtocol(i, address, value); - break; - case SN_TOS: - setIPTypeOfService(i, address, value); - break; - case SN_TTL: - setIPTTL(i, address, value); - break; - case SN_TX_WR0: - memory[address] = value; - break; - case SN_TX_WR1: - memory[address] = value; - break; - case SN_RX_RD0: - memory[address] = value; - break; - case SN_RX_RD1: - memory[address] = value; - break; + case SN_MR: + setSocketModeRegister(i, address, value); + break; + case SN_CR: + setCommandRegister(i, value); + break; + case SN_PORT0: + case SN_PORT1: + case SN_DPORT0: + case SN_DPORT1: + myMemory[address] = value; + break; + case SN_DIPR0 ... SN_DIPR3: + myMemory[address] = value; + break; + case SN_PROTO: + setIPProtocol(i, address, value); + break; + case SN_TOS: + setIPTypeOfService(i, address, value); + break; + case SN_TTL: + setIPTTL(i, address, value); + break; + case SN_TX_WR0: + myMemory[address] = value; + break; + case SN_TX_WR1: + myMemory[address] = value; + break; + case SN_RX_RD0: + myMemory[address] = value; + break; + case SN_RX_RD1: + myMemory[address] = value; + break; #ifdef U2_LOG_UNKNOWN - default: - LogFileOutput("U2: Set unknown socket register[%d]: %04x\n", i, address); - break; + default: + LogFileOutput("U2: Set unknown socket register[%d]: %04x\n", i, address); + break; #endif - }; - } + }; +} - void setModeRegister(const uint16_t address, const uint8_t value) +void Uthernet2::setModeRegister(const uint16_t address, const uint8_t value) +{ + if (value & MR_RST) { - if (value & MR_RST) - { - initialise(); - } - else - { - modeRegister = value; - } + Reset(false); + } + else + { + myModeRegister = value; } +} - void writeCommonRegister(const uint16_t address, const uint8_t value) +void Uthernet2::writeCommonRegister(const uint16_t address, const uint8_t value) +{ + switch (address) { - switch (address) - { - case MR: - setModeRegister(address, value); - break; - case GAR0 ... GAR3: - case SUBR0 ... SUBR3: - case SHAR0 ... SHAR5: - case SIPR0 ... SIPR3: - memory[address] = value; - break; - case RMSR: - setRXSizes(address, value); - break; - case TMSR: - setTXSizes(address, value); - break; + case MR: + setModeRegister(address, value); + break; + case GAR0 ... GAR3: + case SUBR0 ... SUBR3: + case SHAR0 ... SHAR5: + case SIPR0 ... SIPR3: + myMemory[address] = value; + break; + case RMSR: + setRXSizes(address, value); + break; + case TMSR: + setTXSizes(address, value); + break; #ifdef U2_LOG_UNKNOWN - default: - LogFileOutput("U2: Set unknown common register: %04x\n", address); - break; + default: + LogFileOutput("U2: Set unknown common register: %04x\n", address); + break; #endif - }; - } + }; +} - void writeValueAt(const uint16_t address, const uint8_t value) +void Uthernet2::writeValueAt(const uint16_t address, const uint8_t value) +{ + switch (address) { - switch (address) - { - case MR ... UPORT1: - writeCommonRegister(address, value); - break; - case S0_BASE ... S3_MAX: - writeSocketRegister(address, value); - break; - case TX_BASE ... MEM_MAX: - memory[address] = value; - break; + case MR ... UPORT1: + writeCommonRegister(address, value); + break; + case S0_BASE ... S3_MAX: + writeSocketRegister(address, value); + break; + case TX_BASE ... MEM_MAX: + myMemory[address] = value; + break; #ifdef U2_LOG_UNKNOWN - default: - LogFileOutput("U2: Write to unknown location: %02x to %04x\n", value, address); - break; + default: + LogFileOutput("U2: Write to unknown location: %02x to %04x\n", value, address); + break; #endif - } } +} - void writeValue(const uint8_t value) - { - writeValueAt(dataAddress, value); - autoIncrement(); - } +void Uthernet2::writeValue(const uint8_t value) +{ + writeValueAt(myDataAddress, value); + autoIncrement(); +} + +void Uthernet2::Reset(const bool powerCycle) +{ + LogFileOutput("U2: Uthernet 2 initialisation\n"); + myModeRegister = 0; - void initialise() + if (powerCycle) { - LogFileOutput("U2: Uthernet 2 initialisation\n"); - modeRegister = 0; // dataAddress is NOT reset, see page 10 of Uthernet II - sockets.resize(4); - memory.clear(); - memory.resize(MEM_SIZE, 0); + myDataAddress = 0; + } - for (size_t i = 0; i < sockets.size(); ++i) - { - sockets[i].clearFD(); - sockets[i].registers = S0_BASE + (i << 8); - } + mySockets.resize(4); + myMemory.clear(); + myMemory.resize(MEM_SIZE, 0); - // initial values - memory[RTR0] = 0x07; - memory[RTR1] = 0xD0; - setRXSizes(RMSR, 0x55); - setTXSizes(TMSR, 0x55); + for (size_t i = 0; i < mySockets.size(); ++i) + { + mySockets[i].clearFD(); + mySockets[i].registers = S0_BASE + (i << 8); + } + + // initial values + myMemory[RTR0] = 0x07; + myMemory[RTR1] = 0xD0; + setRXSizes(RMSR, 0x55); + setTXSizes(TMSR, 0x55); #ifdef U2_USE_SLIRP - slirp->clearQueue(); + mySlirp->clearQueue(); #endif - } +} - BYTE u2_C0(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles) - { - BYTE res = write ? 0 : MemReadFloatingBus(nCycles); +BYTE Uthernet2::IO_C0(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles) +{ + BYTE res = write ? 0 : MemReadFloatingBus(nCycles); #ifdef U2_LOG_VERBOSE - const uint16_t oldAddress = dataAddress; + const uint16_t oldAddress = dataAddress; #endif - const uint8_t loc = address & 0x0F; + const uint8_t loc = address & 0x0F; - if (write) + if (write) + { + switch (loc) { - switch (loc) - { - case C0X_MODE_REGISTER: - setModeRegister(MR, value); - break; - case C0X_ADDRESS_HIGH: - dataAddress = (value << 8) | (dataAddress & 0x00FF); - break; - case C0X_ADDRESS_LOW: - dataAddress = (value << 0) | (dataAddress & 0xFF00); - break; - case C0X_DATA_PORT: - writeValue(value); - break; - } + case C0X_MODE_REGISTER: + setModeRegister(MR, value); + break; + case C0X_ADDRESS_HIGH: + myDataAddress = (value << 8) | (myDataAddress & 0x00FF); + break; + case C0X_ADDRESS_LOW: + myDataAddress = (value << 0) | (myDataAddress & 0xFF00); + break; + case C0X_DATA_PORT: + writeValue(value); + break; } - else + } + else + { + switch (loc) { - switch (loc) - { - case C0X_MODE_REGISTER: - res = modeRegister; - break; - case C0X_ADDRESS_HIGH: - res = getIByte(dataAddress, 8); - break; - case C0X_ADDRESS_LOW: - res = getIByte(dataAddress, 0); - break; - case C0X_DATA_PORT: - res = readValue(); - break; - } + case C0X_MODE_REGISTER: + res = myModeRegister; + break; + case C0X_ADDRESS_HIGH: + res = getIByte(myDataAddress, 8); + break; + case C0X_ADDRESS_LOW: + res = getIByte(myDataAddress, 0); + break; + case C0X_DATA_PORT: + res = readValue(); + break; } + } #ifdef U2_LOG_VERBOSE - const char * mode = write ? "WRITE " : "READ "; - const char c = std::isprint(res) ? res : '.'; - LogFileOutput("U2: %04x: %s %04x[%04x] %02x = %02x, %c [%d = %d]\n", programcounter, mode, address, oldAddress, value, res, c, value, res); + const char * mode = write ? "WRITE " : "READ "; + const char c = std::isprint(res) ? res : '.'; + LogFileOutput("U2: %04x: %s %04x[%04x] %02x = %02x, %c [%d = %d]\n", programcounter, mode, address, oldAddress, value, res, c, value, res); #endif - return res; - } - + return res; } -void registerUthernet2(UINT uSlot) +BYTE u2_C0(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles) { - #ifdef U2_USE_SLIRP - slirp.reset(); - slirp = std::make_shared(); -#else - const int check = tfe_enabled; - tfe_init(true); - if (tfe_enabled != check) - { - // tfe initialisation failed - return; - } -#endif - dataAddress = 0; - initialise(); - RegisterIoHandler(uSlot, u2_C0, u2_C0, nullptr, nullptr, nullptr, nullptr); -} - -void unRegisterUthernet2() -{ - #ifdef U2_USE_SLIRP - slirp.reset(); -#endif - memory.clear(); - sockets.clear(); + UINT uSlot = ((address & 0xff) >> 4) - 8; + Uthernet2* pCard = (Uthernet2*) MemGetSlotParameters(uSlot); + return pCard->IO_C0(programcounter, address, write, value, nCycles); } -void processEventsUthernet2(uint32_t timeout) +void Uthernet2::processEvents(uint32_t timeout) { #ifdef U2_USE_SLIRP - if (slirp) + if (mySlirp) { - slirp->process(timeout); + mySlirp->process(timeout); } #endif - for (Socket & socket : sockets) + for (Socket & socket : mySockets) { socket.process(); } } + +void Uthernet2::InitializeIO(LPBYTE pCxRomPeripheral) +{ + RegisterIoHandler(m_slot, u2_C0, u2_C0, nullptr, nullptr, this, nullptr); +} + +void Uthernet2::Init() +{ + +} + +void Uthernet2::Update(const ULONG nExecutedCycles) +{ + processEvents(0); +} + +void Uthernet2::SaveSnapshot(YamlSaveHelper& yamlSaveHelper) +{ + +} + +bool Uthernet2::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version) +{ + return true; +} diff --git a/source/linux/network/uthernet2.h b/source/linux/network/uthernet2.h index 20548774e..e0a952c18 100644 --- a/source/linux/network/uthernet2.h +++ b/source/linux/network/uthernet2.h @@ -1,7 +1,100 @@ #pragma once -// this register the IOCalls for Uthernet 2 on slot 3 -// if TFE is not enabled (or available) all operations will timeout -void registerUthernet2(UINT uSlot); -void unRegisterUthernet2(); -void processEventsUthernet2(uint32_t timeout); +#include "Card.h" + +#include + +class SlirpNet; + +struct Socket +{ + uint16_t transmitBase; + uint16_t transmitSize; + uint16_t receiveBase; + uint16_t receiveSize; + uint16_t registers; + + uint16_t sn_rx_wr; + uint16_t sn_rx_rsr; + + uint8_t sn_sr; + + int myFD; + int myErrno; + + void clearFD(); + void setFD(const int fd, const int status); + void process(); + + bool isThereRoomFor(const size_t len, const size_t header) const; + uint16_t getFreeRoom() const; + + Socket(); + + ~Socket(); +}; + +class Uthernet2 : public Card +{ +public: + Uthernet2(UINT slot); + + virtual void InitializeIO(LPBYTE pCxRomPeripheral); + virtual void Init(); + virtual void Reset(const bool powerCycle); + virtual void Update(const ULONG nExecutedCycles); + virtual void SaveSnapshot(YamlSaveHelper& yamlSaveHelper); + virtual bool LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version); + + BYTE IO_C0(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles); + +private: + std::vector myMemory; + std::vector mySockets; + uint8_t myModeRegister; + uint16_t myDataAddress; + std::shared_ptr mySlirp; + + void setSocketModeRegister(const size_t i, const uint16_t address, const uint8_t value); + void setTXSizes(const uint16_t address, uint8_t value); + void setRXSizes(const uint16_t address, uint8_t value); + uint16_t getTXDataSize(const size_t i) const; + uint8_t getTXFreeSizeRegister(const size_t i, const size_t shift) const; + uint8_t getRXDataSizeRegister(const size_t i, const size_t shift) const; + + void receiveOnePacketMacRaw(const size_t i); + void receiveOnePacketFromSocket(const size_t i); + void receiveOnePacket(const size_t i); + + void sendDataMacRaw(const size_t i, const std::vector & data) const; + void sendDataToSocket(const size_t i, std::vector & data); + void sendData(const size_t i); + + void resetRXTXBuffers(const size_t i); + void updateRSR(const size_t i); + + void openSystemSocket(const size_t i, const int type, const int protocol, const int state); + void openSocket(const size_t i); + void closeSocket(const size_t i); + void connectSocket(const size_t i); + + void setCommandRegister(const size_t i, const uint8_t value); + + uint8_t readSocketRegister(const uint16_t address); + uint8_t readValueAt(const uint16_t address); + + void autoIncrement(); + uint8_t readValue(); + + void setIPProtocol(const size_t i, const uint16_t address, const uint8_t value); + void setIPTypeOfService(const size_t i, const uint16_t address, const uint8_t value); + void setIPTTL(const size_t i, const uint16_t address, const uint8_t value); + void writeSocketRegister(const uint16_t address, const uint8_t value); + + void setModeRegister(const uint16_t address, const uint8_t value); + void writeCommonRegister(const uint16_t address, const uint8_t value); + void writeValueAt(const uint16_t address, const uint8_t value); + void writeValue(const uint8_t value); + + void processEvents(uint32_t timeout); +};