/* This file is part of BKBTL. BKBTL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. BKBTL 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with BKBTL. If not, see . */ // Emulator.cpp #include "stdafx.h" #include #include #include "Main.h" #include "Emulator.h" #include "Views.h" #include "Dialogs.h" #include "emubase/Emubase.h" #include "SoundGen.h" #include "Joystick.h" ////////////////////////////////////////////////////////////////////// CMotherboard* g_pBoard = nullptr; BKConfiguration g_nEmulatorConfiguration; // Current configuration bool g_okEmulatorRunning = false; int m_wEmulatorCPUBpsCount = 0; uint16_t m_EmulatorCPUBps[MAX_BREAKPOINTCOUNT + 1]; uint16_t m_wEmulatorTempCPUBreakpoint = 0177777; int m_wEmulatorWatchesCount = 0; uint16_t m_EmulatorWatches[MAX_BREAKPOINTCOUNT + 1]; bool m_okEmulatorSound = false; uint16_t m_wEmulatorSoundSpeed = 100; bool m_okEmulatorCovox = false; bool m_okEmulatorSoundAY = false; int m_nEmulatorSoundChanges = 0; long m_nFrameCount = 0; uint32_t m_dwTickCount = 0; uint32_t m_dwEmulatorUptime = 0; // Machine uptime, seconds, from turn on or reset, increments every 25 frames long m_nUptimeFrameCount = 0; uint8_t* g_pEmulatorRam = nullptr; // RAM values - for change tracking uint8_t* g_pEmulatorChangedRam = nullptr; // RAM change flags uint16_t g_wEmulatorCpuPC = 0177777; // Current PC value uint16_t g_wEmulatorPrevCpuPC = 0177777; // Previous PC value void Emulator_FakeTape_ReadFile(); void Emulator_FakeTape_WriteFile(); void CALLBACK Emulator_SoundGenCallback(unsigned short L, unsigned short R); void CALLBACK Emulator_TeletypeCallback(uint8_t symbol); enum { TAPEMODE_STOPPED = 0, TAPEMODE_STARTED = 1, TAPEMODE_READING = 2, TAPEMODE_FINISHED = -1 } m_EmulatorTapeMode; int m_EmulatorTapeCount = 0; //Прототип функции преобразования экрана // Input: // pVideoBuffer Исходные данные, биты экрана БК // okSmallScreen Признак "малого" экрана // pPalette Палитра // scroll Текущее значение скроллинга // pImageBits Результат, 32-битный цвет, размер для каждой функции свой typedef void (CALLBACK* PREPARE_SCREEN_CALLBACK)(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits); void CALLBACK Emulator_PrepareScreenBW512x256(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits); void CALLBACK Emulator_PrepareScreenColor512x256(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits); void CALLBACK Emulator_PrepareScreenBW512x384(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits); void CALLBACK Emulator_PrepareScreenColor512x384(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits); void CALLBACK Emulator_PrepareScreenBW896x512(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits); void CALLBACK Emulator_PrepareScreenColor896x512(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits); void CALLBACK Emulator_PrepareScreenBW1024x768(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits); void CALLBACK Emulator_PrepareScreenColor1024x768(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits); void CALLBACK Emulator_PrepareScreenBW1536x1024(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits); void CALLBACK Emulator_PrepareScreenColor1536x1024(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits); struct ScreenModeStruct { int width; int height; PREPARE_SCREEN_CALLBACK callback; } static ScreenModeReference[] = { { 512, 256, Emulator_PrepareScreenBW512x256 }, { 512, 256, Emulator_PrepareScreenColor512x256 }, { 512, 384, Emulator_PrepareScreenBW512x384 }, { 512, 384, Emulator_PrepareScreenColor512x384 }, { 896, 512, Emulator_PrepareScreenBW896x512 }, { 896, 512, Emulator_PrepareScreenColor896x512 }, { 1024, 768, Emulator_PrepareScreenBW1024x768 }, { 1024, 768, Emulator_PrepareScreenColor1024x768 }, { 1536, 1024, Emulator_PrepareScreenBW1536x1024 }, { 1536, 1024, Emulator_PrepareScreenColor1536x1024 }, }; ////////////////////////////////////////////////////////////////////// const LPCTSTR FILENAME_BKROM_MONIT10 = _T("monit10.rom"); const LPCTSTR FILENAME_BKROM_FOCAL = _T("focal.rom"); const LPCTSTR FILENAME_BKROM_TESTS = _T("tests.rom"); const LPCTSTR FILENAME_BKROM_BASIC10_1 = _T("basic10_1.rom"); const LPCTSTR FILENAME_BKROM_BASIC10_2 = _T("basic10_2.rom"); const LPCTSTR FILENAME_BKROM_BASIC10_3 = _T("basic10_3.rom"); const LPCTSTR FILENAME_BKROM_DISK_326 = _T("disk_326.rom"); const LPCTSTR FILENAME_BKROM_BK11M_BOS = _T("b11m_bos.rom"); const LPCTSTR FILENAME_BKROM_BK11M_EXT = _T("b11m_ext.rom"); const LPCTSTR FILENAME_BKROM_BASIC11M_0 = _T("basic11m_0.rom"); const LPCTSTR FILENAME_BKROM_BASIC11M_1 = _T("basic11m_1.rom"); const LPCTSTR FILENAME_BKROM_BK11M_MSTD = _T("b11m_mstd.rom"); ////////////////////////////////////////////////////////////////////// // Colors const uint32_t ScreenView_BWPalette[4] = { 0x000000, 0xFFFFFF, 0x000000, 0xFFFFFF }; const uint32_t ScreenView_ColorPalette[4] = { 0x000000, 0x0000FF, 0x00FF00, 0xFF0000 }; const uint32_t ScreenView_ColorPalettes[16][4] = { // Palette# 01 10 11 0x000000, 0x0000FF, 0x00FF00, 0xFF0000, // 00 синий | зеленый | красный 0x000000, 0xFFFF00, 0xFF00FF, 0xFF0000, // 01 желтый | сиреневый | красный 0x000000, 0x00FFFF, 0x0000FF, 0xFF00FF, // 02 голубой | синий | сиреневый 0x000000, 0x00FF00, 0x00FFFF, 0xFFFF00, // 03 зеленый | голубой | желтый 0x000000, 0xFF00FF, 0x00FFFF, 0xFFFFFF, // 04 сиреневый | голубой | белый 0x000000, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, // 05 белый | белый | белый 0x000000, 0xC00000, 0x8E0000, 0xFF0000, // 06 темн-красн| красн-корич| красный 0x000000, 0xC0FF00, 0x8EFF00, 0xFFFF00, // 07 салатовый | светл-зелен| желтый 0x000000, 0xC000FF, 0x8E00FF, 0xFF00FF, // 08 фиолетовый| фиол-синий | сиреневый 0x000000, 0x8EFF00, 0x8E00FF, 0x8E0000, // 09 светл-зелен| фиол-синий |красн-корич 0x000000, 0xC0FF00, 0xC000FF, 0xC00000, // 10 салатовый | фиолетовый |темн-красный 0x000000, 0x00FFFF, 0xFFFF00, 0xFF0000, // 11 голубой | желтый | красный 0x000000, 0xFF0000, 0x00FF00, 0x00FFFF, // 12 красный | зеленый | голубой 0x000000, 0x00FFFF, 0xFFFF00, 0xFFFFFF, // 13 голубой | желтый | белый 0x000000, 0xFFFF00, 0x00FF00, 0xFFFFFF, // 14 желтый | зеленый | белый 0x000000, 0x00FFFF, 0x00FF00, 0xFFFFFF, // 15 голубой | зеленый | белый }; ////////////////////////////////////////////////////////////////////// bool Emulator_LoadRomFile(LPCTSTR strFileName, uint8_t* buffer, uint32_t fileOffset, uint32_t bytesToRead) { FILE* fpRomFile = ::_tfsopen(strFileName, _T("rb"), _SH_DENYWR); if (fpRomFile == nullptr) return false; ASSERT(bytesToRead <= 8192); ::memset(buffer, 0, 8192); if (fileOffset > 0) { ::fseek(fpRomFile, fileOffset, SEEK_SET); } size_t dwBytesRead = ::fread(buffer, 1, bytesToRead, fpRomFile); if (dwBytesRead != bytesToRead) { ::fclose(fpRomFile); return false; } ::fclose(fpRomFile); return true; } bool Emulator_Init() { ASSERT(g_pBoard == nullptr); CProcessor::Init(); m_wEmulatorCPUBpsCount = 0; for (int i = 0; i <= MAX_BREAKPOINTCOUNT; i++) { uint16_t address = Settings_GetDebugBreakpoint(i); m_EmulatorCPUBps[i] = address; if (address != 0177777) m_wEmulatorCPUBpsCount = i + 1; } m_wEmulatorWatchesCount = 0; for (int i = 0; i <= MAX_WATCHESCOUNT; i++) { m_EmulatorWatches[i] = 0177777; } g_pBoard = new CMotherboard(); // Allocate memory for old RAM values g_pEmulatorRam = (uint8_t*) ::calloc(65536, 1); g_pEmulatorChangedRam = (uint8_t*) ::calloc(65536, 1); g_pBoard->Reset(); if (m_okEmulatorSound) { SoundGen_Initialize(Settings_GetSoundVolume()); g_pBoard->SetSoundGenCallback(Emulator_SoundGenCallback); } g_pBoard->SetTeletypeCallback(Emulator_TeletypeCallback); m_EmulatorTapeMode = TAPEMODE_STOPPED; return true; } void Emulator_Done() { ASSERT(g_pBoard != nullptr); // Save breakpoints for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++) Settings_SetDebugBreakpoint(i, i < m_wEmulatorCPUBpsCount ? m_EmulatorCPUBps[i] : 0177777); CProcessor::Done(); g_pBoard->SetSoundGenCallback(nullptr); SoundGen_Finalize(); delete g_pBoard; g_pBoard = nullptr; // Free memory used for old RAM values ::free(g_pEmulatorRam); ::free(g_pEmulatorChangedRam); } BKConfiguration Emulator_GetConfiguration() { return (BKConfiguration)g_pBoard->GetConfiguration(); } LPCTSTR Emulator_GetConfigurationName() { BKConfiguration configuration = Emulator_GetConfiguration(); switch (configuration) { case BK_CONF_BK0010_BASIC: return _T("BK-0010.01 BASIC"); case BK_CONF_BK0010_FOCAL: return _T("BK-0010.01 FOCAL"); case BK_CONF_BK0010_FDD: return _T("BK-0010.01 FDD"); case BK_CONF_BK0011: return _T("BK 0011M"); case BK_CONF_BK0011_FDD: return _T("BK 0011M FDD"); default: return _T("UNKNOWN"); } } bool Emulator_InitConfiguration(BKConfiguration configuration) { g_pBoard->SetConfiguration((uint16_t)configuration); uint8_t buffer[8192]; if ((configuration & BK_COPT_BK0011) == 0) { // Load Monitor ROM file if (!Emulator_LoadRomFile(FILENAME_BKROM_MONIT10, buffer, 0, 8192)) { AlertWarning(_T("Failed to load Monitor ROM file.")); return false; } g_pBoard->LoadROM(0, buffer); } if ((configuration & BK_COPT_BK0011) == 0 && (configuration & BK_COPT_ROM_BASIC) != 0 || (configuration & BK_COPT_BK0011) == 0 && (configuration & BK_COPT_FDD) != 0) // BK 0010 BASIC ROM 1-2 { // Load BASIC ROM 1 file if (!Emulator_LoadRomFile(FILENAME_BKROM_BASIC10_1, buffer, 0, 8192)) { AlertWarning(_T("Failed to load BASIC ROM 1 file.")); return false; } g_pBoard->LoadROM(1, buffer); // Load BASIC ROM 2 file if (!Emulator_LoadRomFile(FILENAME_BKROM_BASIC10_2, buffer, 0, 8192)) { AlertWarning(_T("Failed to load BASIC ROM 2 file.")); return false; } g_pBoard->LoadROM(2, buffer); } if ((configuration & BK_COPT_BK0011) == 0 && (configuration & BK_COPT_ROM_BASIC) != 0) // BK 0010 BASIC ROM 3 { // Load BASIC ROM 3 file if (!Emulator_LoadRomFile(FILENAME_BKROM_BASIC10_3, buffer, 0, 8064)) { AlertWarning(_T("Failed to load BASIC ROM 3 file.")); return false; } g_pBoard->LoadROM(3, buffer); } if ((configuration & BK_COPT_BK0011) == 0 && (configuration & BK_COPT_ROM_FOCAL) != 0) // BK 0010 FOCAL { // Load Focal ROM file if (!Emulator_LoadRomFile(FILENAME_BKROM_FOCAL, buffer, 0, 8192)) { AlertWarning(_T("Failed to load Focal ROM file.")); return false; } g_pBoard->LoadROM(1, buffer); // Unused 8KB ::memset(buffer, 0, 8192); g_pBoard->LoadROM(2, buffer); // Load Tests ROM file if (!Emulator_LoadRomFile(FILENAME_BKROM_TESTS, buffer, 0, 8064)) { AlertWarning(_T("Failed to load Tests ROM file.")); return false; } g_pBoard->LoadROM(3, buffer); } if (configuration & BK_COPT_BK0011) { // Load BK0011M BASIC 0, part 1 if (!Emulator_LoadRomFile(FILENAME_BKROM_BASIC11M_0, buffer, 0, 8192)) { AlertWarning(_T("Failed to load BK11M BASIC 0 ROM file.")); return false; } g_pBoard->LoadROM(0, buffer); // Load BK0011M BASIC 0, part 2 if (!Emulator_LoadRomFile(FILENAME_BKROM_BASIC11M_0, buffer, 8192, 8192)) { AlertWarning(_T("Failed to load BK11M BASIC 0 ROM file.")); return false; } g_pBoard->LoadROM(1, buffer); // Load BK0011M BASIC 1 if (!Emulator_LoadRomFile(FILENAME_BKROM_BASIC11M_1, buffer, 0, 8192)) { AlertWarning(_T("Failed to load BK11M BASIC 1 ROM file.")); return false; } g_pBoard->LoadROM(2, buffer); // Load BK0011M EXT if (!Emulator_LoadRomFile(FILENAME_BKROM_BK11M_EXT, buffer, 0, 8192)) { AlertWarning(_T("Failed to load BK11M EXT ROM file.")); return false; } g_pBoard->LoadROM(3, buffer); // Load BK0011M BOS if (!Emulator_LoadRomFile(FILENAME_BKROM_BK11M_BOS, buffer, 0, 8192)) { AlertWarning(_T("Failed to load BK11M BOS ROM file.")); return false; } g_pBoard->LoadROM(4, buffer); } if (configuration & BK_COPT_FDD) { // Load disk driver ROM file ::memset(buffer, 0, 8192); if (!Emulator_LoadRomFile(FILENAME_BKROM_DISK_326, buffer, 0, 4096)) { AlertWarning(_T("Failed to load DISK ROM file.")); return false; } g_pBoard->LoadROM((configuration & BK_COPT_BK0011) ? 5 : 3, buffer); } if ((configuration & BK_COPT_BK0011) && (configuration & BK_COPT_FDD) == 0) { // Load BK0011M MSTD if (!Emulator_LoadRomFile(FILENAME_BKROM_BK11M_MSTD, buffer, 0, 8192)) { AlertWarning(_T("Failed to load BK11M MSTD ROM file.")); return false; } g_pBoard->LoadROM(5, buffer); } g_nEmulatorConfiguration = configuration; g_pBoard->Reset(); #if 0 //DEBUG: CPU and memory tests //Emulator_LoadRomFile(_T("791401"), buffer, 8192); //g_pBoard->LoadRAM(0, buffer, 8192); //Emulator_LoadRomFile(_T("791404"), buffer, 6144); //g_pBoard->LoadRAM(0, buffer, 6144); Emulator_LoadRomFile(_T("791323"), buffer, 4096); g_pBoard->LoadRAM(0, buffer, 4096); g_pBoard->GetCPU()->SetPC(0200); //DEBUG g_pBoard->GetCPU()->SetPSW(0000); //DEBUG #endif m_nUptimeFrameCount = 0; m_dwEmulatorUptime = 0; return true; } void Emulator_Start() { g_okEmulatorRunning = true; // Set title bar text MainWindow_UpdateWindowTitle(); MainWindow_UpdateMenu(); m_nFrameCount = 0; m_dwTickCount = GetTickCount(); // For proper breakpoint processing if (m_wEmulatorCPUBpsCount != 0) { g_pBoard->GetCPU()->ClearInternalTick(); } } void Emulator_Stop() { g_okEmulatorRunning = false; Emulator_SetTempCPUBreakpoint(0177777); // Reset title bar message MainWindow_UpdateWindowTitle(); MainWindow_UpdateMenu(); // Reset FPS indicator MainWindow_SetStatusbarText(StatusbarPartFPS, nullptr); MainWindow_UpdateAllViews(); } void Emulator_Reset() { ASSERT(g_pBoard != nullptr); g_pBoard->Reset(); m_nUptimeFrameCount = 0; m_dwEmulatorUptime = 0; m_EmulatorTapeMode = TAPEMODE_STOPPED; MainWindow_UpdateAllViews(); } bool Emulator_AddCPUBreakpoint(uint16_t address) { if (m_wEmulatorCPUBpsCount == MAX_BREAKPOINTCOUNT - 1 || address == 0177777) return false; for (int i = 0; i < m_wEmulatorCPUBpsCount; i++) // Check if the BP exists { if (m_EmulatorCPUBps[i] == address) return false; // Already in the list } for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++) // Put in the first empty cell { if (m_EmulatorCPUBps[i] > address) // found the place { memcpy(m_EmulatorCPUBps + i + 1, m_EmulatorCPUBps + i, sizeof(uint16_t) * (m_wEmulatorCPUBpsCount - i)); m_EmulatorCPUBps[i] = address; break; } if (m_EmulatorCPUBps[i] == 0177777) // found empty place { m_EmulatorCPUBps[i] = address; break; } } m_wEmulatorCPUBpsCount++; return true; } bool Emulator_RemoveCPUBreakpoint(uint16_t address) { if (m_wEmulatorCPUBpsCount == 0 || address == 0177777) return false; for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++) { if (m_EmulatorCPUBps[i] == address) { m_EmulatorCPUBps[i] = 0177777; m_wEmulatorCPUBpsCount--; if (m_wEmulatorCPUBpsCount > i) // fill the hole { memcpy(m_EmulatorCPUBps + i, m_EmulatorCPUBps + i + 1, sizeof(uint16_t) * (m_wEmulatorCPUBpsCount - i)); m_EmulatorCPUBps[m_wEmulatorCPUBpsCount] = 0177777; } return true; } } return false; } void Emulator_SetTempCPUBreakpoint(uint16_t address) { if (m_wEmulatorTempCPUBreakpoint != 0177777) Emulator_RemoveCPUBreakpoint(m_wEmulatorTempCPUBreakpoint); if (address == 0177777) { m_wEmulatorTempCPUBreakpoint = 0177777; return; } for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++) { if (m_EmulatorCPUBps[i] == address) return; // We have regular breakpoint with the same address } m_wEmulatorTempCPUBreakpoint = address; m_EmulatorCPUBps[m_wEmulatorCPUBpsCount] = address; m_wEmulatorCPUBpsCount++; } const uint16_t* Emulator_GetCPUBreakpointList() { return m_EmulatorCPUBps; } bool Emulator_IsBreakpoint() { uint16_t address = g_pBoard->GetCPU()->GetPC(); if (m_wEmulatorCPUBpsCount > 0) { for (int i = 0; i < m_wEmulatorCPUBpsCount; i++) { if (address == m_EmulatorCPUBps[i]) return true; } } return false; } bool Emulator_IsBreakpoint(uint16_t address) { if (m_wEmulatorCPUBpsCount == 0) return false; for (int i = 0; i < m_wEmulatorCPUBpsCount; i++) { if (address == m_EmulatorCPUBps[i]) return true; } return false; } void Emulator_RemoveAllBreakpoints() { for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++) m_EmulatorCPUBps[i] = 0177777; m_wEmulatorCPUBpsCount = 0; } bool Emulator_AddWatch(uint16_t address) { if (m_wEmulatorWatchesCount == MAX_WATCHESCOUNT - 1 || address == 0177777) return false; for (int i = 0; i < m_wEmulatorWatchesCount; i++) // Check if the BP exists { if (m_EmulatorWatches[i] == address) return false; // Already in the list } for (int i = 0; i < MAX_BREAKPOINTCOUNT; i++) // Put in the first empty cell { if (m_EmulatorWatches[i] == 0177777) { m_EmulatorWatches[i] = address; break; } } m_wEmulatorWatchesCount++; return true; } const uint16_t* Emulator_GetWatchList() { return m_EmulatorWatches; } bool Emulator_RemoveWatch(uint16_t address) { if (m_wEmulatorWatchesCount == 0 || address == 0177777) return false; for (int i = 0; i < MAX_WATCHESCOUNT; i++) { if (m_EmulatorWatches[i] == address) { m_EmulatorWatches[i] = 0177777; m_wEmulatorWatchesCount--; if (m_wEmulatorWatchesCount > i) // fill the hole { m_EmulatorWatches[i] = m_EmulatorWatches[m_wEmulatorWatchesCount]; m_EmulatorWatches[m_wEmulatorWatchesCount] = 0177777; } return true; } } return false; } void Emulator_RemoveAllWatches() { for (int i = 0; i < MAX_WATCHESCOUNT; i++) m_EmulatorWatches[i] = 0177777; m_wEmulatorWatchesCount = 0; } void Emulator_SetSpeed(uint16_t realspeed) { uint16_t speedpercent = 100; switch (realspeed) { case 0: speedpercent = 500; break; case 1: speedpercent = 100; break; case 2: speedpercent = 200; break; case 3: speedpercent = 400; break; case 0x7fff: speedpercent = 50; break; case 0x7ffe: speedpercent = 25; break; case 0x7ffd: speedpercent = 10; break; default: speedpercent = 100; break; } m_wEmulatorSoundSpeed = speedpercent; if (m_okEmulatorSound) SoundGen_SetSpeed(m_wEmulatorSoundSpeed); } void Emulator_SetSound(bool soundOnOff) { if (m_okEmulatorSound != soundOnOff) { if (soundOnOff) { SoundGen_Initialize(Settings_GetSoundVolume()); SoundGen_SetSpeed(m_wEmulatorSoundSpeed); g_pBoard->SetSoundGenCallback(Emulator_SoundGenCallback); } else { g_pBoard->SetSoundGenCallback(nullptr); SoundGen_Finalize(); } } m_okEmulatorSound = soundOnOff; } void Emulator_SetCovox(bool covoxOnOff) { m_okEmulatorCovox = covoxOnOff; } void Emulator_SetSoundAY(bool onoff) { m_okEmulatorSoundAY = onoff; g_pBoard->SetSoundAY(onoff); } bool Emulator_SystemFrame() { SoundGen_SetVolume(Settings_GetSoundVolume()); g_pBoard->SetCPUBreakpoints(m_wEmulatorCPUBpsCount > 0 ? m_EmulatorCPUBps : nullptr); ScreenView_ScanKeyboard(); ScreenView_ProcessKeyboard(); Emulator_ProcessJoystick(); if (!g_pBoard->SystemFrame()) { uint16_t pc = g_pBoard->GetCPU()->GetPC(); if (pc != m_wEmulatorTempCPUBreakpoint) DebugPrintFormat(_T("Breakpoint hit at %06ho\r\n"), pc); return false; } // Calculate frames per second m_nFrameCount++; uint32_t dwCurrentTicks = GetTickCount(); long nTicksElapsed = dwCurrentTicks - m_dwTickCount; if (nTicksElapsed >= 1200) { double dFramesPerSecond = m_nFrameCount * 1000.0 / nTicksElapsed; double dSpeed = dFramesPerSecond / 25.0 * 100; TCHAR buffer[16]; _sntprintf(buffer, sizeof(buffer) / sizeof(TCHAR) - 1, _T("%03.f%%"), dSpeed); MainWindow_SetStatusbarText(StatusbarPartFPS, buffer); bool floppyEngine = g_pBoard->IsFloppyEngineOn(); MainWindow_SetStatusbarText(StatusbarPartFloppyEngine, floppyEngine ? _T("Motor") : nullptr); m_nFrameCount = 0; m_dwTickCount = dwCurrentTicks; } // Calculate emulator uptime (25 frames per second) m_nUptimeFrameCount++; if (m_nUptimeFrameCount >= 25) { m_dwEmulatorUptime++; m_nUptimeFrameCount = 0; int seconds = (int) (m_dwEmulatorUptime % 60); int minutes = (int) (m_dwEmulatorUptime / 60 % 60); int hours = (int) (m_dwEmulatorUptime / 3600 % 60); TCHAR buffer[20]; _sntprintf(buffer, sizeof(buffer) / sizeof(TCHAR) - 1, _T("Uptime: %02d:%02d:%02d"), hours, minutes, seconds); MainWindow_SetStatusbarText(StatusbarPartUptime, buffer); } // Update "Sound" indicator every 5 frames m_nEmulatorSoundChanges += g_pBoard->GetSoundChanges(); if (m_nUptimeFrameCount % 5 == 0) { bool soundOn = m_nEmulatorSoundChanges > 0; MainWindow_SetStatusbarText(StatusbarPartSound, soundOn ? _T("Sound") : nullptr); m_nEmulatorSoundChanges = 0; } bool okTapeMotor = g_pBoard->IsTapeMotorOn(); if (Settings_GetTape()) { m_EmulatorTapeMode = okTapeMotor ? TAPEMODE_FINISHED : TAPEMODE_STOPPED; } else // Fake tape mode { switch (m_EmulatorTapeMode) { case TAPEMODE_STOPPED: if (okTapeMotor) { m_EmulatorTapeMode = TAPEMODE_STARTED; m_EmulatorTapeCount = 10; // wait 2/5 sec } break; case TAPEMODE_STARTED: if (!okTapeMotor) m_EmulatorTapeMode = TAPEMODE_STOPPED; else { m_EmulatorTapeCount--; if (m_EmulatorTapeCount <= 0) { uint16_t pc = g_pBoard->GetCPU()->GetPC(); // Check if BK-0010 and PC=116722,116724 for tape reading if ((g_nEmulatorConfiguration & 1) == BK_COPT_BK0010 && (pc == 0116722 || pc == 0116724)) { Emulator_FakeTape_ReadFile(); m_EmulatorTapeMode = TAPEMODE_FINISHED; } // Check if BK-0011 and PC=155676,155700 for tape reading else if ((g_nEmulatorConfiguration & 1) == BK_COPT_BK0011 && (pc == 0155676 || pc == 0155700)) { Emulator_FakeTape_ReadFile(); m_EmulatorTapeMode = TAPEMODE_FINISHED; } // Check for tape save start on BK-0010 else if ((g_nEmulatorConfiguration & 1) == BK_COPT_BK0010 && (pc == 0116414 || pc == 0116426)) { Emulator_FakeTape_WriteFile(); m_EmulatorTapeMode = TAPEMODE_FINISHED; } } } break; case TAPEMODE_FINISHED: if (!okTapeMotor) m_EmulatorTapeMode = TAPEMODE_STOPPED; break; } } return true; } void Emulator_GetEmt36FileName(TCHAR* filename) { uint16_t nameaddr = 0326; //g_pBoard->GetRAMWord(0306) + 6; for (uint16_t i = 0; i < 16; i++) { uint8_t ch = g_pBoard->GetRAMByte(nameaddr + i); filename[i] = (ch < 32) ? 0 : Translate_BK_Unicode(ch); } filename[16] = 0; // Trim trailing spaces for (int i = 15; i >= 0 && filename[i] == _T(' '); i--) filename[i] = 0; TCHAR* pdot = NULL; if (*filename != 0) { // Check if we have filename extension pdot = _tcsrchr(filename, _T('.')); if (pdot == NULL) // Have no dot so append default '.BIN' extension _tcsncat(filename, _T(".BIN"), 4); else { // We have dot in string so cut off spaces before the dot if (pdot != filename) { TCHAR* pspace = pdot; while (pspace > filename && *(pspace - 1) == _T(' ')) pspace--; if (pspace < pdot) _tcscpy(pspace, pdot); } } } } void Emulator_FakeTape_ReadFile() { TCHAR filename[24]; Emulator_GetEmt36FileName(filename); FILE* fpFile = nullptr; // First, if the filename specified, try to find it if (*filename != 0) fpFile = ::_tfsopen(filename, _T("rb"), _SH_DENYWR); // If file not found then ask user for the file if (fpFile == nullptr) { TCHAR title[36]; _sntprintf(title, 36, _T("Reading tape %s"), filename); TCHAR filter[36]; TCHAR* pdot = _tcsrchr(filename, _T('.')); // Find the extension if (pdot == NULL) memcpy(filter, _T("*.BIN\0*.BIN"), 12 * sizeof(TCHAR)); else { //TODO: Now we assume extension always 4-char filter[0] = _T('*'); _tcsncpy(filter + 1, pdot, 4); filter[5] = 0; filter[6] = _T('*'); _tcsncpy(filter + 7, pdot, 4); filter[11] = 0; } memcpy(filter + 12, _T("*.*\0*.*\0"), 9 * sizeof(TCHAR)); TCHAR filepath[MAX_PATH]; if (ShowOpenDialog(g_hwnd, title, filter, filepath)) { fpFile = ::_tfsopen(filepath, _T("rb"), _SH_DENYWR); } } uint8_t result = 2; // EMT36 result = checksum error uint8_t* pData = nullptr; if (fpFile != nullptr) { for (;;) // For breaks only { // Read the file header uint16_t header[2]; if (::fread(header, 1, 4, fpFile) != 4) break; // Reading error uint16_t filestart = header[0]; uint16_t filesize = header[1]; g_pBoard->SetRAMWord(0350, filesize); g_pBoard->SetRAMWord(0346, filestart); //TODO: Copy 16-char file name from 0326..0345 to 0352..0371 if (filesize == 0) break; // Wrong Length // Read the file pData = (uint8_t*)calloc(filesize, 1); if (pData == nullptr) break; if (::fread(pData, 1, filesize, fpFile) != filesize) break; // Reading error // Copy to memory uint16_t start = g_pBoard->GetRAMWord(0322); if (start == 0) start = filestart; for (uint16_t i = 0; i < filesize; i++) { g_pBoard->SetRAMByte(start + i, pData[i]); } result = 0; // EMT36 result = OK break; } fclose(fpFile); } if (pData != nullptr) free(pData); // Report EMT36 result g_pBoard->SetRAMByte(0321, result); // Execute RTS twice -- return from EMT36 CProcessor* pCPU = g_pBoard->GetCPU(); pCPU->SetPC(g_pBoard->GetRAMWord(pCPU->GetSP())); pCPU->SetSP(pCPU->GetSP() + 2); pCPU->SetPC(g_pBoard->GetRAMWord(pCPU->GetSP())); pCPU->SetSP(pCPU->GetSP() + 2); //TODO: Set flags } void Emulator_FakeTape_WriteFile() { TCHAR filename[24]; Emulator_GetEmt36FileName(filename); FILE* fpFile = nullptr; // First, if the filename specified, try to open if if (*filename != 0) fpFile = ::_tfsopen(filename, _T("wb"), _SH_DENYWR); //TODO: If failed, ask user for file name uint8_t result = 2; // EMT36 result = checksum error uint8_t* pData = nullptr; if (fpFile != nullptr) { for (;;) // For breaks only { uint16_t filesize = g_pBoard->GetRAMWord(0324); uint16_t filestart = g_pBoard->GetRAMWord(0322); pData = (uint8_t*)calloc(filesize, 1); if (pData == nullptr) break; // Copy from memory for (uint16_t i = 0; i < filesize; i++) { pData[i] = g_pBoard->GetRAMByte(filestart + i); } uint16_t header[2]; header[0] = filestart; header[1] = filesize; if (::fwrite(header, 1, 4, fpFile) != 4) break; // Writing error if (::fwrite(pData, 1, filesize, fpFile) != filesize) break; // Writing error result = 0; // EMT36 result = OK break; } fclose(fpFile); } if (pData != nullptr) free(pData); // Report EMT36 result g_pBoard->SetRAMByte(0321, result); // Execute RTS twice -- return from EMT36 CProcessor* pCPU = g_pBoard->GetCPU(); pCPU->SetPC(g_pBoard->GetRAMWord(pCPU->GetSP())); pCPU->SetSP(pCPU->GetSP() + 2); pCPU->SetPC(g_pBoard->GetRAMWord(pCPU->GetSP())); pCPU->SetSP(pCPU->GetSP() + 2); //TODO: Set flags } void Emulator_ProcessJoystick() { if (Settings_GetJoystick() == 0) return; // NumPad joystick processing is inside ScreenView_ScanKeyboard() function UINT joystate = Joystick_GetJoystickState(); g_pBoard->SetPrinterInPort((uint8_t)joystate); } void CALLBACK Emulator_SoundGenCallback(unsigned short L, unsigned short R) { if (m_okEmulatorCovox) { // Get lower byte from printer port output register unsigned short data = g_pBoard->GetPrinterOutPort() & 0xff; // Merge with channel data L += (data << 7); R += (data << 7); } SoundGen_FeedDAC(L, R); } // Update cached values after Run or Step void Emulator_OnUpdate() { // Update stored PC value g_wEmulatorPrevCpuPC = g_wEmulatorCpuPC; g_wEmulatorCpuPC = g_pBoard->GetCPU()->GetPC(); // Update memory change flags { uint8_t* pOld = g_pEmulatorRam; uint8_t* pChanged = g_pEmulatorChangedRam; uint16_t addr = 0; do { uint8_t newvalue = g_pBoard->GetRAMByte(addr); uint8_t oldvalue = *pOld; *pChanged = (newvalue != oldvalue) ? 255 : 0; *pOld = newvalue; addr++; pOld++; pChanged++; } while (addr < 65535); } } // Get RAM change flag // addrtype - address mode - see ADDRTYPE_XXX constants uint16_t Emulator_GetChangeRamStatus(uint16_t address) { return *((uint16_t*)(g_pEmulatorChangedRam + address)); } void CALLBACK Emulator_TeletypeCallback(uint8_t symbol) { if (g_hwndTeletype != (HWND) INVALID_HANDLE_VALUE) { if (symbol >= 32 || symbol == 13 || symbol == 10) { TeletypeView_OutputSymbol((TCHAR)symbol); } else { TCHAR buffer[32]; _sntprintf(buffer, sizeof(buffer) / sizeof(TCHAR) - 1, _T("<%02x>"), symbol); TeletypeView_Output(buffer); } } } void Emulator_GetScreenSize(int scrmode, int* pwid, int* phei) { if (scrmode < 0 || scrmode >= sizeof(ScreenModeReference) / sizeof(ScreenModeStruct)) return; ScreenModeStruct* pinfo = ScreenModeReference + scrmode; *pwid = pinfo->width; *phei = pinfo->height; } const uint32_t * Emulator_GetPalette(int screenMode) { if ((screenMode & 1) == 0) return (const uint32_t *)ScreenView_BWPalette; if ((g_nEmulatorConfiguration & BK_COPT_BK0011) == 0) return (const uint32_t *)ScreenView_ColorPalette; else return (const uint32_t *)ScreenView_ColorPalettes[g_pBoard->GetPalette()]; } void Emulator_PrepareScreenRGB32(void* pImageBits, int screenMode) { if (pImageBits == NULL) return; // Get scroll value uint16_t scroll = g_pBoard->GetPortView(0177664); bool okSmallScreen = ((scroll & 01000) == 0); scroll &= 0377; scroll = (scroll >= 0330) ? scroll - 0330 : 050 + scroll; const uint32_t * pPalette = Emulator_GetPalette(screenMode); const uint8_t* pVideoBuffer = g_pBoard->GetVideoBuffer(); ASSERT(pVideoBuffer != nullptr); // Render to bitmap PREPARE_SCREEN_CALLBACK callback = ScreenModeReference[screenMode].callback; callback(pVideoBuffer, okSmallScreen, pPalette, scroll, pImageBits); } #define AVERAGERGB(a, b) ( (((a) & 0xfefefeffUL) + ((b) & 0xfefefeffUL)) >> 1 ) void CALLBACK Emulator_PrepareScreenBW512x256(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* /*pPalette*/, int scroll, void* pImageBits) { int linesToShow = okSmallScreen ? 64 : 256; for (int y = 0; y < linesToShow; y++) { int yy = (y + scroll) & 0377; const uint16_t* pVideo = (uint16_t*)(pVideoBuffer + yy * 0100); uint32_t* pBits = (uint32_t*)pImageBits + (255 - y) * 512; for (int x = 0; x < 512 / 16; x++) { uint16_t src = *pVideo; for (int bit = 0; bit < 16; bit++) { uint32_t color = (src & 1) ? 0x0ffffff : 0; *pBits = color; pBits++; src = src >> 1; } pVideo++; } } if (okSmallScreen) { memset((uint32_t*)pImageBits, 0, (256 - 64) * 512 * sizeof(uint32_t)); } } void CALLBACK Emulator_PrepareScreenColor512x256(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits) { int linesToShow = okSmallScreen ? 64 : 256; for (int y = 0; y < linesToShow; y++) { int yy = (y + scroll) & 0377; const uint16_t* pVideo = (uint16_t*)(pVideoBuffer + yy * 0100); uint32_t* pBits = (uint32_t*)pImageBits + (255 - y) * 512; for (int x = 0; x < 512 / 16; x++) { uint16_t src = *pVideo; for (int bit = 0; bit < 16; bit += 2) { uint32_t color = pPalette[src & 3]; *pBits = color; pBits++; *pBits = color; pBits++; src = src >> 2; } pVideo++; } } if (okSmallScreen) { memset((uint32_t*)pImageBits, 0, (256 - 64) * 512 * sizeof(uint32_t)); } } void CALLBACK Emulator_PrepareScreenBW512x384(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* /*pPalette*/, int scroll, void* pImageBits) { int linesToShow = okSmallScreen ? 64 : 256; int bky = 0; for (int y = 0; y < 384; y++) { uint32_t* pBits = (uint32_t*)pImageBits + (383 - y) * 512; if (y % 3 == 1) continue; // Skip, fill later int yy = (bky + scroll) & 0377; const uint16_t* pVideo = (uint16_t*)(pVideoBuffer + yy * 0100); for (int x = 0; x < 512 / 16; x++) { uint16_t src = *pVideo; for (int bit = 0; bit < 16; bit++) { uint32_t color = (src & 1) ? 0x0ffffff : 0; *pBits = color; pBits++; src = src >> 1; } pVideo++; } if (y % 3 == 2) // Fill skipped line { uint8_t* pBits2 = (uint8_t*)((uint32_t*)pImageBits + (383 - y + 0) * 512); uint8_t* pBits1 = (uint8_t*)((uint32_t*)pImageBits + (383 - y + 1) * 512); uint8_t* pBits0 = (uint8_t*)((uint32_t*)pImageBits + (383 - y + 2) * 512); for (int x = 0; x < 512 * 4; x++) { *pBits1 = (uint8_t)((((uint16_t) * pBits0) + ((uint16_t) * pBits2)) / 2); pBits2++; pBits1++; pBits0++; } } bky++; if (bky >= linesToShow) break; } if (okSmallScreen) { memset((uint32_t*)pImageBits, 0, (384 - 86) * 512 * sizeof(uint32_t)); //TODO } } void CALLBACK Emulator_PrepareScreenColor512x384(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits) { int linesToShow = okSmallScreen ? 64 : 256; int bky = 0; for (int y = 0; y < 384; y++) { uint32_t* pBits = (uint32_t*)pImageBits + (383 - y) * 512; if (y % 3 == 1) continue; // Skip, fill later int yy = (bky + scroll) & 0377; const uint16_t* pVideo = (uint16_t*)(pVideoBuffer + yy * 0100); for (int x = 0; x < 512 / 16; x++) { uint16_t src = *pVideo; for (int bit = 0; bit < 16; bit += 2) { uint32_t color = pPalette[src & 3]; *pBits = color; pBits++; *pBits = color; pBits++; src = src >> 2; } pVideo++; } if (y % 3 == 2) // Fill skipped line { uint8_t* pBits2 = (uint8_t*)((uint32_t*)pImageBits + (383 - y + 0) * 512); uint8_t* pBits1 = (uint8_t*)((uint32_t*)pImageBits + (383 - y + 1) * 512); uint8_t* pBits0 = (uint8_t*)((uint32_t*)pImageBits + (383 - y + 2) * 512); for (int x = 0; x < 512 * 4; x++) { *pBits1 = (uint8_t)((((uint16_t) * pBits0) + ((uint16_t) * pBits2)) / 2); pBits2++; pBits1++; pBits0++; } } bky++; if (bky >= linesToShow) break; } if (okSmallScreen) { memset((uint32_t*)pImageBits, 0, (384 - 86) * 512 * sizeof(uint32_t)); //TODO } } void CALLBACK Emulator_PrepareScreenBW896x512(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* /*pPalette*/, int scroll, void* pImageBits) { int linesToShow = okSmallScreen ? 64 : 256; for (int y = 0; y < linesToShow; y++) { int yy = (y + scroll) & 0377; const uint16_t* pVideo = (uint16_t*)(pVideoBuffer + yy * 0100); uint32_t* pBits = (uint32_t*)pImageBits + (255 - y) * 896 * 2; uint32_t* pBits2 = pBits + 896; for (int x = 0; x < 512 / 16; x++) { uint16_t src = *pVideo; for (int bit = 0; bit < 16; bit += 4) { uint32_t c1 = (src & 1) ? 0x0ffffff : 0; uint32_t c2 = (src & 2) ? 0x0ffffff : 0; uint32_t c3 = (src & 4) ? 0x0ffffff : 0; uint32_t c4 = (src & 8) ? 0x0ffffff : 0; *(pBits++) = *(pBits2++) = c1; *(pBits++) = *(pBits2++) = AVERAGERGB(c1, c2); *(pBits++) = *(pBits2++) = c2; *(pBits++) = *(pBits2++) = AVERAGERGB(c2, c3); *(pBits++) = *(pBits2++) = c3; *(pBits++) = *(pBits2++) = AVERAGERGB(c3, c4); *(pBits++) = *(pBits2++) = c4; src = src >> 4; } pVideo++; } } if (okSmallScreen) { memset((uint32_t*)pImageBits, 0, (256 - 64) * 896 * 2 * sizeof(uint32_t)); } } void CALLBACK Emulator_PrepareScreenColor896x512(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits) { int linesToShow = okSmallScreen ? 64 : 256; for (int y = 0; y < linesToShow; y++) { int yy = (y + scroll) & 0377; const uint16_t* pVideo = (uint16_t*)(pVideoBuffer + yy * 0100); uint32_t* pBits = (uint32_t*)pImageBits + (255 - y) * 896 * 2; uint32_t* pBits2 = pBits + 896; for (int x = 0; x < 512 / 16; x++) { uint16_t src = *pVideo; for (int bit = 0; bit < 16; bit += 8) { uint32_t c1 = pPalette[src & 3]; src = src >> 2; uint32_t c2 = pPalette[src & 3]; src = src >> 2; uint32_t c3 = pPalette[src & 3]; src = src >> 2; uint32_t c4 = pPalette[src & 3]; src = src >> 2; uint32_t c12 = AVERAGERGB(c1, c2); uint32_t c23 = AVERAGERGB(c2, c3); uint32_t c34 = AVERAGERGB(c3, c4); *(pBits++) = *(pBits2++) = c1; *(pBits++) = *(pBits2++) = c1; *(pBits++) = *(pBits2++) = c12; *(pBits++) = *(pBits2++) = c12; *(pBits++) = *(pBits2++) = c2; *(pBits++) = *(pBits2++) = c2; *(pBits++) = *(pBits2++) = c23; *(pBits++) = *(pBits2++) = c23; *(pBits++) = *(pBits2++) = c3; *(pBits++) = *(pBits2++) = c3; *(pBits++) = *(pBits2++) = c34; *(pBits++) = *(pBits2++) = c34; *(pBits++) = *(pBits2++) = c4; *(pBits++) = *(pBits2++) = c4; } pVideo++; } } if (okSmallScreen) { memset((uint32_t*)pImageBits, 0, (256 - 64) * 896 * 2 * sizeof(uint32_t)); } } void CALLBACK Emulator_PrepareScreenBW1024x768(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* /*pPalette*/, int scroll, void* pImageBits) { int linesToShow = okSmallScreen ? 64 : 256; for (int y = 0; y < linesToShow; y++) { int yy = (y + scroll) & 0377; const uint16_t* pVideo = (uint16_t*)(pVideoBuffer + yy * 0100); uint32_t* pBits = (uint32_t*)pImageBits + (255 - y) * 1024 * 3; uint32_t* pBits2 = pBits + 1024; for (int x = 0; x < 512 / 16; x++) { uint16_t src = *pVideo; for (int bit = 0; bit < 16; bit++) { uint32_t color = (src & 1) ? 0x0ffffff : 0; *pBits = *pBits2 = color; pBits++; pBits2++; *pBits = *pBits2 = color; pBits++; pBits2++; src = src >> 1; } pVideo++; } } if (okSmallScreen) { memset((uint32_t*)pImageBits, 0, (256 - 64) * 1024 * 3 * sizeof(uint32_t)); } } void CALLBACK Emulator_PrepareScreenColor1024x768(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits) { int linesToShow = okSmallScreen ? 64 : 256; for (int y = 0; y < linesToShow; y++) { int yy = (y + scroll) & 0377; const uint16_t* pVideo = (uint16_t*)(pVideoBuffer + yy * 0100); uint32_t* pBits = (uint32_t*)pImageBits + (255 - y) * 1024 * 3; uint32_t* pBits2 = pBits + 1024; for (int x = 0; x < 512 / 16; x++) { uint16_t src = *pVideo; for (int bit = 0; bit < 16; bit += 2) { uint32_t color = pPalette[src & 3]; *pBits = *pBits2 = color; pBits++; pBits2++; *pBits = *pBits2 = color; pBits++; pBits2++; *pBits = *pBits2 = color; pBits++; pBits2++; *pBits = *pBits2 = color; pBits++; pBits2++; src = src >> 2; } pVideo++; } } if (okSmallScreen) { memset((uint32_t*)pImageBits, 0, (256 - 64) * 1024 * 3 * sizeof(uint32_t)); } } void CALLBACK Emulator_PrepareScreenBW1536x1024(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* /*pPalette*/, int scroll, void* pImageBits) { int linesToShow = okSmallScreen ? 64 : 256; for (int y = 0; y < linesToShow; y++) { int yy = (y + scroll) & 0377; const uint16_t* pVideo = (uint16_t*)(pVideoBuffer + yy * 0100); uint32_t* pBits = (uint32_t*)pImageBits + (255 - y) * 1536 * 4; uint32_t* pBits1 = pBits + 1536; uint32_t* pBits2 = pBits + 1536; for (int x = 0; x < 512 / 16; x++) { uint16_t src = *pVideo; for (int bit = 0; bit < 16; bit++) { uint32_t color = (src & 1) ? 0x0ffffff : 0; *pBits++ = *pBits1++ = *pBits2++ = color; *pBits++ = *pBits1++ = *pBits2++ = color; *pBits++ = *pBits1++ = *pBits2++ = color; src = src >> 1; } pVideo++; } } if (okSmallScreen) { memset((uint32_t*)pImageBits, 0, (256 - 64) * 1536 * 4 * sizeof(uint32_t)); } } void CALLBACK Emulator_PrepareScreenColor1536x1024(const uint8_t* pVideoBuffer, int okSmallScreen, const uint32_t* pPalette, int scroll, void* pImageBits) { int linesToShow = okSmallScreen ? 64 : 256; for (int y = 0; y < linesToShow; y++) { int yy = (y + scroll) & 0377; const uint16_t* pVideo = (uint16_t*)(pVideoBuffer + yy * 0100); uint32_t* pBits = (uint32_t*)pImageBits + (255 - y) * 1536 * 4; uint32_t* pBits1 = pBits + 1536; uint32_t* pBits2 = pBits + 1536; for (int x = 0; x < 512 / 16; x++) { uint16_t src = *pVideo; for (int bit = 0; bit < 16; bit += 2) { uint32_t color = pPalette[src & 3]; *pBits++ = *pBits1++ = *pBits2++ = color; *pBits++ = *pBits1++ = *pBits2++ = color; *pBits++ = *pBits1++ = *pBits2++ = color; *pBits++ = *pBits1++ = *pBits2++ = color; *pBits++ = *pBits1++ = *pBits2++ = color; *pBits++ = *pBits1++ = *pBits2++ = color; src = src >> 2; } pVideo++; } } if (okSmallScreen) { memset((uint32_t*)pImageBits, 0, (256 - 64) * 1536 * 4 * sizeof(uint32_t)); } } ////////////////////////////////////////////////////////////////////// // // Emulator image format - see CMotherboard::SaveToImage() // Image header format (32 bytes): // 4 bytes BK_IMAGE_HEADER1 // 4 bytes BK_IMAGE_HEADER2 // 4 bytes BK_IMAGE_VERSION // 4 bytes BK_IMAGE_SIZE // 4 bytes BK uptime // 12 bytes Not used //TODO: 256 bytes * 4 - Floppy 1..4 path bool Emulator_SaveImage(LPCTSTR sFilePath) { // Create file HANDLE hFile = CreateFile(sFilePath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return false; // Allocate memory uint8_t* pImage = static_cast(::calloc(BKIMAGE_SIZE, 1)); if (pImage == nullptr) { ::CloseHandle(hFile); return false; } ::memset(pImage, 0, BKIMAGE_SIZE); // Prepare header uint32_t* pHeader = (uint32_t*) pImage; *pHeader++ = BKIMAGE_HEADER1; *pHeader++ = BKIMAGE_HEADER2; *pHeader++ = BKIMAGE_VERSION; *pHeader++ = BKIMAGE_SIZE; // Store emulator state to the image g_pBoard->SaveToImage(pImage); *(uint32_t*)(pImage + 16) = m_dwEmulatorUptime; // Save image to the file DWORD dwBytesWritten = 0; WriteFile(hFile, pImage, BKIMAGE_SIZE, &dwBytesWritten, NULL); ::free(pImage); CloseHandle(hFile); if (dwBytesWritten != BKIMAGE_SIZE) return false; return true; } bool Emulator_LoadImage(LPCTSTR sFilePath) { Emulator_Stop(); // Open file HANDLE hFile = CreateFile(sFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return false; // Read header uint32_t bufHeader[BKIMAGE_HEADER_SIZE / sizeof(uint32_t)]; DWORD dwBytesRead = 0; ReadFile(hFile, bufHeader, BKIMAGE_HEADER_SIZE, &dwBytesRead, NULL); if (dwBytesRead != BKIMAGE_HEADER_SIZE) { CloseHandle(hFile); return false; } //TODO: Check version and size // Allocate memory uint8_t* pImage = static_cast(::calloc(BKIMAGE_SIZE, 1)); if (pImage == nullptr) { CloseHandle(hFile); return false; } // Read image SetFilePointer(hFile, 0, NULL, FILE_BEGIN); dwBytesRead = 0; ReadFile(hFile, pImage, BKIMAGE_SIZE, &dwBytesRead, NULL); if (dwBytesRead != BKIMAGE_SIZE) { CloseHandle(hFile); return false; } // Restore emulator state from the image g_pBoard->LoadFromImage(pImage); m_dwEmulatorUptime = *(uint32_t*)(pImage + 16); g_wEmulatorCpuPC = g_pBoard->GetCPU()->GetPC(); // Free memory, close file ::free(pImage); CloseHandle(hFile); g_okEmulatorRunning = false; MainWindow_UpdateAllViews(); return true; } //////////////////////////////////////////////////////////////////////