/* * engine.c * * This file is part of Emu48 * * Copyright (C) 1995 Sebastien Carlier * */ #include "pch.h" #include "Emu48.h" #include "Opcodes.h" #include "io.h" // 24.10.99 cg, renamed from Serial.h #include "debugger.h" #define SAMPLE 16384 // speed adjust sample frequency BOOL bInterrupt = FALSE; UINT nState = 1; UINT nNextState = 0; BOOL bRealSpeed = FALSE; // 11.12.99 cg, moved from Emu48.c BOOL bKeySlow = FALSE; // slow down for key emulation CHIPSET Chipset; char szSerialWire[16]; // evicename for wire port char szSerialIr[16]; // devicename for IR port DWORD dwSXCycles = 82; // SX cpu cycles in interval DWORD dwGXCycles = 123; // GX cpu cycles in interval static BOOL bCommInit = FALSE; // COM port not open static BOOL bCpuSlow = FALSE; // 11.12.99 cg, renamed, enable/disable real speed static DWORD dwOldCyc; // cpu cycles at last event static DWORD dwSpeedRef; // timer value at last event static DWORD dwTickRef; // sample timer ticks #include "Ops.h" static __inline VOID CheckDisp(BOOL bSync) { if (disp == 0) return; // no display update need // update display when drawing top line or display is off if (bSync && GetLineCounter() != 0x3F && (Chipset.IORam[0x00]&8)) return; _ASSERT((disp & DISP_POINTER) == 0); // display pointer already updated if (disp & DISP_MAIN) UpdateMainDisplay(); if (disp & DISP_MENUE) UpdateMenuDisplay(); _ASSERT((disp & DISP_ANNUN) == 0); // annunciators already updated disp = 0; // display updated return; } static __inline VOID CheckSerial(VOID) { if (ioc_acc) // IOC changed { ioc_acc = FALSE; // COM port closed and serial on if (bCommInit == FALSE && (Chipset.IORam[IOC] & SON) != 0) { CommOpen(szSerialWire,szSerialIr); // open COM ports bCommInit = CommConnect() != PORT_CLOSE; } // COM port opend and serial off if (bCommInit == TRUE && (Chipset.IORam[IOC] & SON) == 0) { CommClose(); // close COM port bCommInit = FALSE; } // Interrupt on recv buffer full and receive buffer full if ((Chipset.IORam[IOC] & ERBF) && (Chipset.IORam[RCS] & RBF)) { Chipset.Shutdn = FALSE; INTERRUPT; } } } static __inline VOID AdjustSpeed(VOID) // adjust emulation speed { if (bCpuSlow || bKeySlow) // 11.12.99 cg, changed, emulation slow down { // cycles elapsed for next check // 22.11.99 cg, changed, DWORD casting of Chipset.cycles if ((DWORD) (Chipset.cycles & 0xFFFFFFFF)-dwOldCyc >= (DWORD) T2CYCLES) { LARGE_INTEGER lAct; do { BOOL bErr = QueryPerformanceCounter(&lAct); _ASSERT(bErr); // no high-resolution performance counter } while(lAct.LowPart-dwSpeedRef <= dwTickRef); dwOldCyc += T2CYCLES; // adjust cycles reference dwSpeedRef += dwTickRef; // adjust reference time } } return; } VOID AdjKeySpeed(VOID) // slow down key repeat { WORD i; BOOL bKey; if (bCpuSlow) return; // 11.12.99 cg, changed, no need to slow down bKey = FALSE; // search for a pressed key for (i = 0;i < sizeof(Chipset.Keyboard_Row) / sizeof(Chipset.Keyboard_Row[0]) && !bKey;++i) bKey = (Chipset.Keyboard_Row[i] != 0); if (!bKeySlow && bKey) // key pressed, init variables { LARGE_INTEGER lTime; // sample timer ticks // save reference cycles // 22.11.99 cg, changed, DWORD casting dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); QueryPerformanceCounter(&lTime); // get timer ticks dwSpeedRef = lTime.LowPart; // save reference time } bKeySlow = bKey; // save new state return; } VOID SetSpeed(BOOL bAdjust) // set emulation speed { if (bAdjust) // switch to real speed { LARGE_INTEGER lTime; // sample timer ticks // save reference cycles // 22.11.99 cg, changed, DWORD casting dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); QueryPerformanceCounter(&lTime); // get timer ticks dwSpeedRef = lTime.LowPart; // save reference time } bCpuSlow = bAdjust; // 11.12.99 cg, changed, save emulation speed } VOID UpdateKdnBit(VOID) // update KDN bit { // 22.11.99 cg, changed, DWORD casting of Chipset.cycles if (Chipset.intk && (DWORD) (Chipset.cycles & 0xFFFFFFFFF) - Chipset.dwKdnCycles > (DWORD) T2CYCLES * 16) IOBit(0x19,8,Chipset.in != 0); } BOOL WaitForSleepState(VOID) // wait for cpu SHUTDN then sleep state { DWORD dwRefTime = timeGetTime(); // wait for the SHUTDN command with 1.5 sec timeout while (timeGetTime() - dwRefTime < 1500L && !Chipset.Shutdn) Sleep(0); if (Chipset.Shutdn) // not timeout, cpu is down SwitchToState(3); // go to sleep state return 3 != nNextState; // state not changed, emulator was busy } UINT SwitchToState(UINT nNewState) { UINT nOldState = nState; if (nState == nNewState) return nOldState; switch (nState) { case 0: // Run if (hDlgDebug) // debugger running { DestroyWindow(hDlgDebug); // then close debugger to renter emulation hDlgDebug = NULL; } switch (nNewState) { case 1: // -> Invalid nNextState = 1; if (Chipset.Shutdn) SetEvent(hEventShutdn); else bInterrupt = TRUE; while (nState!=nNextState) Sleep(0); UpdateWindowStatus(); break; case 2: // -> Return nNextState = 1; if (Chipset.Shutdn) SetEvent(hEventShutdn); else bInterrupt = TRUE; while (nState!=nNextState) Sleep(0); nNextState = 2; SetEvent(hEventShutdn); while (nState!=nNextState) Sleep(0); UpdateWindowStatus(); break; case 3: // -> Sleep nNextState = 3; if (Chipset.Shutdn) SetEvent(hEventShutdn); else bInterrupt = TRUE; while (nState!=nNextState) Sleep(0); break; } break; case 1: // Invalid switch (nNewState) { case 0: // -> Run nNextState = 0; bInterrupt = FALSE; SetEvent(hEventShutdn); while (nState!=nNextState) Sleep(0); UpdateWindowStatus(); break; case 2: // -> Return nNextState = 2; SetEvent(hEventShutdn); while (nState!=nNextState) Sleep(0); break; case 3: // -> Sleep nNextState = 3; SetEvent(hEventShutdn); while (nState!=nNextState) Sleep(0); UpdateWindowStatus(); break; } break; case 3: // Sleep switch (nNewState) { case 0: // -> Run nNextState = 0; if (Chipset.Shutdn) bInterrupt=TRUE; SetEvent(hEventShutdn); break; case 1: // -> Invalid nNextState = 1; SetEvent(hEventShutdn); while (nState!=nNextState) Sleep(0); UpdateWindowStatus(); break; case 2: // -> Return nNextState = 1; SetEvent(hEventShutdn); while (nState!=nNextState) Sleep(0); nNextState = 2; SetEvent(hEventShutdn); while (nState!=nNextState) Sleep(0); UpdateWindowStatus(); break; } break; } return nOldState; } UINT WorkerThread(LPVOID pParam) { LARGE_INTEGER lDummyInt; // sample timer ticks QueryPerformanceFrequency(&lDummyInt); // init timer ticks lDummyInt.QuadPart /= SAMPLE; // calculate sample ticks dwTickRef = lDummyInt.LowPart; // sample timer ticks _ASSERT(dwTickRef); // tick resolution error loop: while (nNextState == 1) // go into invalid state { CommClose(); // close COM port bCommInit = FALSE; // COM port not open nState = 1; // in invalid state WaitForSingleObject(hEventShutdn,INFINITE); if (nNextState == 2) // go into return state { nState = 2; // in return state return 0; // kill thread } ioc_acc = TRUE; // test if UART on } while (nNextState == 0) { if (nState!=0) { nState = 0; // clear port2 status bits Chipset.cards_status &= ~(PORT2_PRESENT | PORT2_WRITE); if (pbyPort2 || Chipset.Port2) // card plugged in port2 { Chipset.cards_status |= PORT2_PRESENT; if (bPort2Writeable) // is card writeable Chipset.cards_status |= PORT2_WRITE; } RomSwitch(Chipset.Bank_FF); // select HP49G ROM bank and update memory mapping UpdateDisplayPointers(); UpdateMainDisplay(); UpdateMenuDisplay(); UpdateAnnunciators(); // init speed reference // 22.11.99 cg, changed, DWORD casting dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); QueryPerformanceCounter(&lDummyInt); dwSpeedRef = lDummyInt.LowPart; SetHP48Time(); // update HP48 time & date StartTimers(); } PCHANGED; while (!bInterrupt) { LPBYTE I = FASTPTR(Chipset.pc); if (bDbgEnable) // debugger active { BOOL bStopEmulation; // 13.11.99 cg, changed, check for step into bStopEmulation = (nDbgState == DBG_STEPINTO); // check for step over bStopEmulation |= (nDbgState == DBG_STEPOVER) && dwDbgRstkp == Chipset.rstkp; // 13.11.99 cg, new, check for step out, something was popped from hardware stack if (nDbgState == DBG_STEPOUT && dwDbgRstkp == Chipset.rstkp) { _ASSERT(bStopEmulation == FALSE); if ((bStopEmulation = (Chipset.pc == dwDbgRstk)) == FALSE) { // it was C=RSTK, check for next object popped from hardware stack dwDbgRstkp = (Chipset.rstkp-1)&7; dwDbgRstk = Chipset.rstk[dwDbgRstkp]; } } // 13.11.99 cg, end of step out implementation // check for breakpoints bStopEmulation |= CheckBreakpoint(Chipset.pc); // NOP3, opcode #420 (GOC) if (bDbgNOP3 && I[0] == 0x4 && I[1] == 0x2 && I[2] == 0x0) bStopEmulation = TRUE; if (bStopEmulation) // stop condition { StopTimers(); // hold timer values when emulator is stopped OutputDebugString("Emulator stopped...\n"); NotifyDebugger(); // 10.11.99 cg, changed, update registers WaitForSingleObject(hEventDebug,INFINITE); OutputDebugString("Emulator running...\n"); // @todo add timer emulation StartTimers(); // continue timers Chipset.Shutdn = FALSE; Chipset.bShutdnWake = FALSE; // init slow down part // 22.11.99 cg, changed, DWORD casting dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); QueryPerformanceCounter(&lDummyInt); dwSpeedRef = lDummyInt.LowPart; } } EvalOpcode(I); CheckDisp(!Chipset.Shutdn); // check for display update CheckSerial(); // serial support AdjustSpeed(); // adjust emulation speed } bInterrupt = FALSE; // be sure to reenter opcode loop // enter SHUTDN handler only in RUN mode if (Chipset.Shutdn && !(nDbgState == DBG_STEPINTO || nDbgState == DBG_STEPOVER)) { WaitForSingleObject(hEventShutdn,INFINITE); if (Chipset.bShutdnWake) // waked up by timer, keyboard or serial { Chipset.bShutdnWake = FALSE; Chipset.Shutdn = FALSE; // init speed reference // 22.11.99 cg, changed, DWORD casting dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); QueryPerformanceCounter(&lDummyInt); dwSpeedRef = lDummyInt.LowPart; } } if (Chipset.SoftInt) { Chipset.SoftInt = FALSE; if (Chipset.inte) { Chipset.inte = FALSE; rstkpush(Chipset.pc); Chipset.pc = 0xf; } } if (nNextState != 0) { StopTimers(); Chipset.cards_status &= (Chipset.type=='S')?0x5:0xA; } } while (nNextState == 3) // go into sleep state { nState = 3; // in sleep state WaitForSingleObject(hEventShutdn,INFINITE); } goto loop; UNREFERENCED_PARAMETER(pParam); }