emu48-mirror/sources/Emu48/ENGINE.C

422 lines
11 KiB
C
Raw Normal View History

2024-03-19 22:24:30 +01:00
/*
* engine.c
*
* This file is part of Emu48
*
* Copyright (C) 1995 Sebastien Carlier
*
*/
#include "pch.h"
#include "Emu48.h"
2024-03-19 22:25:45 +01:00
#include "Opcodes.h"
#include "io.h" // 24.10.99 cg, renamed from Serial.h
#include "debugger.h"
2024-03-19 22:24:30 +01:00
2024-03-19 22:25:45 +01:00
#define SAMPLE 16384 // speed adjust sample frequency
2024-03-19 22:24:30 +01:00
2024-03-19 22:25:45 +01:00
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;
2024-03-19 22:24:30 +01:00
2024-03-19 22:25:45 +01:00
char szSerialWire[16]; // evicename for wire port
char szSerialIr[16]; // devicename for IR port
2024-03-19 22:24:30 +01:00
2024-03-19 22:25:45 +01:00
DWORD dwSXCycles = 82; // SX cpu cycles in interval
DWORD dwGXCycles = 123; // GX cpu cycles in interval
2024-03-19 22:24:30 +01:00
2024-03-19 22:25:45 +01:00
static BOOL bCommInit = FALSE; // COM port not open
static BOOL bCpuSlow = FALSE; // 11.12.99 cg, renamed, enable/disable real speed
2024-03-19 22:24:30 +01:00
2024-03-19 22:25:45 +01:00
static DWORD dwOldCyc; // cpu cycles at last event
static DWORD dwSpeedRef; // timer value at last event
static DWORD dwTickRef; // sample timer ticks
2024-03-19 22:24:30 +01:00
#include "Ops.h"
2024-03-19 22:25:45 +01:00
static __inline VOID CheckDisp(BOOL bSync)
{
if (disp == 0) return; // no display update need
2024-03-19 22:24:30 +01:00
2024-03-19 22:25:45 +01:00
// update display when drawing top line or display is off
if (bSync && GetLineCounter() != 0x3F && (Chipset.IORam[0x00]&8))
return;
2024-03-19 22:24:30 +01:00
2024-03-19 22:25:45 +01:00
_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;
}
2024-03-19 22:24:30 +01:00
2024-03-19 22:25:45 +01:00
static __inline VOID CheckSerial(VOID)
2024-03-19 22:24:30 +01:00
{
if (ioc_acc) // IOC changed
{
ioc_acc = FALSE;
// COM port closed and serial on
2024-03-19 22:25:45 +01:00
if (bCommInit == FALSE && (Chipset.IORam[IOC] & SON) != 0)
2024-03-19 22:24:30 +01:00
{
CommOpen(szSerialWire,szSerialIr); // open COM ports
bCommInit = CommConnect() != PORT_CLOSE;
}
// COM port opend and serial off
2024-03-19 22:25:45 +01:00
if (bCommInit == TRUE && (Chipset.IORam[IOC] & SON) == 0)
2024-03-19 22:24:30 +01:00
{
CommClose(); // close COM port
bCommInit = FALSE;
}
// Interrupt on recv buffer full and receive buffer full
2024-03-19 22:25:45 +01:00
if ((Chipset.IORam[IOC] & ERBF) && (Chipset.IORam[RCS] & RBF))
2024-03-19 22:24:30 +01:00
{
Chipset.Shutdn = FALSE;
INTERRUPT;
}
}
}
2024-03-19 22:25:45 +01:00
static __inline VOID AdjustSpeed(VOID) // adjust emulation speed
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
if (bCpuSlow || bKeySlow) // 11.12.99 cg, changed, emulation slow down
2024-03-19 22:24:30 +01:00
{
// cycles elapsed for next check
2024-03-19 22:25:45 +01:00
// 22.11.99 cg, changed, DWORD casting of Chipset.cycles
if ((DWORD) (Chipset.cycles & 0xFFFFFFFF)-dwOldCyc >= (DWORD) T2CYCLES)
2024-03-19 22:24:30 +01:00
{
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;
}
2024-03-19 22:25:45 +01:00
VOID AdjKeySpeed(VOID) // slow down key repeat
2024-03-19 22:24:30 +01:00
{
WORD i;
BOOL bKey;
2024-03-19 22:25:45 +01:00
if (bCpuSlow) return; // 11.12.99 cg, changed, no need to slow down
2024-03-19 22:24:30 +01:00
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
2024-03-19 22:25:45 +01:00
// save reference cycles
// 22.11.99 cg, changed, DWORD casting
dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF);
2024-03-19 22:24:30 +01:00
QueryPerformanceCounter(&lTime); // get timer ticks
dwSpeedRef = lTime.LowPart; // save reference time
}
bKeySlow = bKey; // save new state
return;
}
2024-03-19 22:25:45 +01:00
VOID SetSpeed(BOOL bAdjust) // set emulation speed
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
if (bAdjust) // switch to real speed
2024-03-19 22:24:30 +01:00
{
LARGE_INTEGER lTime; // sample timer ticks
2024-03-19 22:25:45 +01:00
// save reference cycles
// 22.11.99 cg, changed, DWORD casting
dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF);
2024-03-19 22:24:30 +01:00
QueryPerformanceCounter(&lTime); // get timer ticks
dwSpeedRef = lTime.LowPart; // save reference time
}
2024-03-19 22:25:45 +01:00
bCpuSlow = bAdjust; // 11.12.99 cg, changed, save emulation speed
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:25:45 +01:00
VOID UpdateKdnBit(VOID) // update KDN bit
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
// 22.11.99 cg, changed, DWORD casting of Chipset.cycles
if (Chipset.intk && (DWORD) (Chipset.cycles & 0xFFFFFFFFF) - Chipset.dwKdnCycles > (DWORD) T2CYCLES * 16)
2024-03-19 22:24:30 +01:00
IOBit(0x19,8,Chipset.in != 0);
}
2024-03-19 22:25:45 +01:00
BOOL WaitForSleepState(VOID) // wait for cpu SHUTDN then sleep state
2024-03-19 22:24:30 +01:00
{
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
2024-03-19 22:25:45 +01:00
if (hDlgDebug) // debugger running
{
DestroyWindow(hDlgDebug); // then close debugger to renter emulation
hDlgDebug = NULL;
}
2024-03-19 22:24:30 +01:00
switch (nNewState)
{
case 1: // -> Invalid
nNextState = 1;
if (Chipset.Shutdn)
2024-03-19 22:25:45 +01:00
SetEvent(hEventShutdn);
2024-03-19 22:24:30 +01:00
else
bInterrupt = TRUE;
while (nState!=nNextState) Sleep(0);
UpdateWindowStatus();
break;
case 2: // -> Return
nNextState = 1;
if (Chipset.Shutdn)
2024-03-19 22:25:45 +01:00
SetEvent(hEventShutdn);
2024-03-19 22:24:30 +01:00
else
bInterrupt = TRUE;
while (nState!=nNextState) Sleep(0);
nNextState = 2;
2024-03-19 22:25:45 +01:00
SetEvent(hEventShutdn);
2024-03-19 22:24:30 +01:00
while (nState!=nNextState) Sleep(0);
UpdateWindowStatus();
break;
case 3: // -> Sleep
nNextState = 3;
if (Chipset.Shutdn)
2024-03-19 22:25:45 +01:00
SetEvent(hEventShutdn);
2024-03-19 22:24:30 +01:00
else
bInterrupt = TRUE;
while (nState!=nNextState) Sleep(0);
break;
}
break;
case 1: // Invalid
switch (nNewState)
{
case 0: // -> Run
nNextState = 0;
bInterrupt = FALSE;
2024-03-19 22:25:45 +01:00
SetEvent(hEventShutdn);
2024-03-19 22:24:30 +01:00
while (nState!=nNextState) Sleep(0);
UpdateWindowStatus();
break;
case 2: // -> Return
nNextState = 2;
2024-03-19 22:25:45 +01:00
SetEvent(hEventShutdn);
2024-03-19 22:24:30 +01:00
while (nState!=nNextState) Sleep(0);
break;
case 3: // -> Sleep
nNextState = 3;
2024-03-19 22:25:45 +01:00
SetEvent(hEventShutdn);
2024-03-19 22:24:30 +01:00
while (nState!=nNextState) Sleep(0);
UpdateWindowStatus();
break;
}
break;
case 3: // Sleep
switch (nNewState)
{
case 0: // -> Run
nNextState = 0;
if (Chipset.Shutdn) bInterrupt=TRUE;
2024-03-19 22:25:45 +01:00
SetEvent(hEventShutdn);
2024-03-19 22:24:30 +01:00
break;
case 1: // -> Invalid
nNextState = 1;
2024-03-19 22:25:45 +01:00
SetEvent(hEventShutdn);
2024-03-19 22:24:30 +01:00
while (nState!=nNextState) Sleep(0);
UpdateWindowStatus();
break;
case 2: // -> Return
nNextState = 1;
2024-03-19 22:25:45 +01:00
SetEvent(hEventShutdn);
2024-03-19 22:24:30 +01:00
while (nState!=nNextState) Sleep(0);
nNextState = 2;
2024-03-19 22:25:45 +01:00
SetEvent(hEventShutdn);
2024-03-19 22:24:30 +01:00
while (nState!=nNextState) Sleep(0);
UpdateWindowStatus();
break;
}
break;
}
return nOldState;
}
UINT WorkerThread(LPVOID pParam)
{
2024-03-19 22:25:45 +01:00
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
2024-03-19 22:24:30 +01:00
loop:
while (nNextState == 1) // go into invalid state
{
2024-03-19 22:25:45 +01:00
CommClose(); // close COM port
bCommInit = FALSE; // COM port not open
2024-03-19 22:24:30 +01:00
nState = 1; // in invalid state
2024-03-19 22:25:45 +01:00
WaitForSingleObject(hEventShutdn,INFINITE);
2024-03-19 22:24:30 +01:00
if (nNextState == 2) // go into return state
{
nState = 2; // in return state
return 0; // kill thread
}
2024-03-19 22:25:45 +01:00
ioc_acc = TRUE; // test if UART on
2024-03-19 22:24:30 +01:00
}
while (nNextState == 0)
{
if (nState!=0)
{
nState = 0;
2024-03-19 22:25:45 +01:00
// clear port2 status bits
Chipset.cards_status &= ~(PORT2_PRESENT | PORT2_WRITE);
if (pbyPort2 || Chipset.Port2) // card plugged in port2
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
Chipset.cards_status |= PORT2_PRESENT;
if (bPort2Writeable) // is card writeable
Chipset.cards_status |= PORT2_WRITE;
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:25:45 +01:00
RomSwitch(Chipset.Bank_FF); // select HP49G ROM bank and update memory mapping
2024-03-19 22:24:30 +01:00
UpdateDisplayPointers();
UpdateMainDisplay();
UpdateMenuDisplay();
UpdateAnnunciators();
2024-03-19 22:25:45 +01:00
// init speed reference
// 22.11.99 cg, changed, DWORD casting
dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF);
2024-03-19 22:24:30 +01:00
QueryPerformanceCounter(&lDummyInt);
dwSpeedRef = lDummyInt.LowPart;
2024-03-19 22:25:45 +01:00
SetHP48Time(); // update HP48 time & date
2024-03-19 22:24:30 +01:00
StartTimers();
}
PCHANGED;
while (!bInterrupt)
{
2024-03-19 22:25:45 +01:00
LPBYTE I = FASTPTR(Chipset.pc);
if (bDbgEnable) // debugger active
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
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;
}
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:25:45 +01:00
EvalOpcode(I);
CheckDisp(!Chipset.Shutdn); // check for display update
CheckSerial(); // serial support
AdjustSpeed(); // adjust emulation speed
2024-03-19 22:24:30 +01:00
}
2024-03-19 22:25:45 +01:00
bInterrupt = FALSE; // be sure to reenter opcode loop
// enter SHUTDN handler only in RUN mode
if (Chipset.Shutdn && !(nDbgState == DBG_STEPINTO || nDbgState == DBG_STEPOVER))
2024-03-19 22:24:30 +01:00
{
2024-03-19 22:25:45 +01:00
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;
}
2024-03-19 22:24:30 +01:00
}
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
2024-03-19 22:25:45 +01:00
WaitForSingleObject(hEventShutdn,INFINITE);
2024-03-19 22:24:30 +01:00
}
goto loop;
UNREFERENCED_PARAMETER(pParam);
}