Дополнительные тесты позволили выявить несколько ошибок и неточностей в первой версии алгоритма.
Вот новая версия алгоритма, ещё более точно (как я полагаю) эмулирующая работу встроенного таймера процессора 1801ВМ1:
Код:
typedef unsigned long long QWORD;
typedef unsigned long DWORD;
typedef unsigned short word;
#define WORD(a) (*(unsigned short *)&aMemory[a])
PowerON()
{
WORD(0177706) = wCPU_ID;
wVE_Timer_Counter = 0177777;
WORD(0177710) = wVE_Timer_Counter;
WORD(0177712) = 0177400;
nVE_MonitoredExpiryCount = 0;
bVE_Expiry = false;
bVE_Go = false;
}
Reset()
{
WORD(0177712) = 0;
Last_IO_Mode = IO_WRITE;
wLast_IO_Addr = 0177712;
Make_IO();
}
Make_IO()
{
switch( wLast_IO_Addr )
{
// VE-Timer Counter Initial Value
case 0177706:
{
if( Last_IO_Mode == IO_WRITE )
{
Last_IO_Mode = IO_READ;
wLast_IO_Addr = 0177710;
Make_IO();
Last_IO_Mode = IO_WRITE;
wLast_IO_Addr = 0177706;
if( bVE_Go && !bVE_NoReload && !bVE_NoRestart )
{
QWORD qwCurrentCPU_RunTimeNS = qwCPU_TotalRunTimeNS;
qwCurrentCPU_RunTimeNS += dwCurrentStepsLatencyNS;
QWORD qwCurrentCPU_Clocks = qwCurrentCPU_RunTimeNS/uCPU_CycleNS;
if( qwCurrentCPU_Clocks >= qwNextVE_TimerStart_CPU_Clocks )
{
qwPrevVE_TimerStart_CPU_Clocks = qwNextVE_TimerStart_CPU_Clocks;
dwPrevInitialVE_CounterVal = dwNextInitialVE_CounterVal;
QWORD qwUsedCPU_Clocks = qwCurrentCPU_Clocks - qwPrevVE_TimerStart_CPU_Clocks;
QWORD qwCurrentVE_Timer_Clocks = qwUsedCPU_Clocks/nVE_CPU_Clock_Divider;
DWORD dwTimerClocksLow = qwCurrentVE_Timer_Clocks % dwPrevInitialVE_CounterVal;
qwNextVE_TimerStart_CPU_Clocks = qwPrevVE_TimerStart_CPU_Clocks;
qwNextVE_TimerStart_CPU_Clocks += ( dwPrevInitialVE_CounterVal - dwTimerClocksLow ) * nVE_CPU_Clock_Divider;
}
dwNextInitialVE_CounterVal = WORD(0177706);
if( !dwNextInitialVE_CounterVal ) { dwNextInitialVE_CounterVal = 0x10000; }
}
}
}
break;
// VE-Timer Counter
case 0177710:
{
if( Last_IO_Mode == IO_WRITE )
{
WORD(0177710) = wVE_Timer_Counter;
}
else
{
if( bVE_Go )
{
QWORD qwCurrentCPU_RunTimeNS = qwCPU_TotalRunTimeNS;
qwCurrentCPU_RunTimeNS += dwCurrentStepsLatencyNS;
QWORD qwCurrentCPU_Clocks = qwCurrentCPU_RunTimeNS/uCPU_CycleNS;
QWORD qwVE_TimerStart_CPU_Clocks = qwPrevVE_TimerStart_CPU_Clocks;
DWORD dwInitialVE_CounterVal = dwPrevInitialVE_CounterVal;
if( qwCurrentCPU_Clocks >= qwNextVE_TimerStart_CPU_Clocks )
{
bVE_Expiry = true;
if( !bVE_NoRestart )
{
qwVE_TimerStart_CPU_Clocks = qwNextVE_TimerStart_CPU_Clocks;
dwInitialVE_CounterVal = dwNextInitialVE_CounterVal;
}
}
QWORD qwUsedCPU_Clocks = qwCurrentCPU_Clocks - qwVE_TimerStart_CPU_Clocks;
QWORD qwCurrentVE_Timer_Clocks = qwUsedCPU_Clocks/nVE_CPU_Clock_Divider;
DWORD dwTimerClocksHi = qwCurrentVE_Timer_Clocks / dwInitialVE_CounterVal;
DWORD dwTimerClocksLow = qwCurrentVE_Timer_Clocks % dwInitialVE_CounterVal;
if( bVE_Expiry )
{
if( bVE_Monitor && (dwTimerClocksHi > nVE_MonitoredExpiryCount) )
{
nVE_MonitoredExpiryCount = dwTimerClocksHi;
}
if( bVE_NoReload )
{
wVE_Timer_Counter = dwInitialVE_CounterVal - dwTimerClocksLow;
}
else if( bVE_NoRestart )
{
bVE_Go = false;
wVE_Timer_Counter = WORD(0177706);
if( dwTimerClocksHi == 1 && !dwTimerClocksLow )
{
if( qwCurrentCPU_Clocks%128 < 5 )
{
wVE_Timer_Counter = 0;
}
}
}
else
{
wVE_Timer_Counter = dwInitialVE_CounterVal - dwTimerClocksLow;
if( !dwTimerClocksLow )
{
if( qwCurrentCPU_Clocks%128 < 5 )
{
wVE_Timer_Counter = 0;
}
}
}
}
else
{ // !bVE_Expiry
wVE_Timer_Counter = dwInitialVE_CounterVal - dwTimerClocksLow;
}
WORD(0177710) = wVE_Timer_Counter;
}
}
}
break;
// VE-Timer CSR
case 0177712:
{
IO_ModeType IO_Mode = Last_IO_Mode;
Last_IO_Mode = IO_READ;
wLast_IO_Addr = 0177710;
Make_IO();
wLast_IO_Addr = 0177712;
Last_IO_Mode = IO_Mode;
word wVE_Timer_CSR = WORD(0177712);
if( Last_IO_Mode == IO_READ )
{
if( bVE_Expiry )
{
if( bVE_Monitor && nVE_MonitoredExpiryCount > 1 )
{
wVE_Timer_CSR |= BIT_7;
}
if( bVE_NoRestart && !bVE_NoReload )
{
wVE_Timer_CSR &= ~BIT_4;
}
}
WORD(0177712) = wVE_Timer_CSR|0xFF00;
}
else
{ // Last_IO_Mode == IO_WRITE
if( nVE_MonitoredExpiryCount )
{
nVE_MonitoredExpiryCount = 2;
}
bVE_Expiry = false;
bVE_Go = wVE_Timer_CSR & BIT_4;
if( wVE_Timer_CSR & BIT_0 )
{
bVE_Go = false;
}
bVE_NoReload = wVE_Timer_CSR & BIT_1;
bVE_Monitor = wVE_Timer_CSR & BIT_2;
bVE_NoRestart = wVE_Timer_CSR & BIT_3;
nVE_CPU_Clock_Divider = 128;
if( wVE_Timer_CSR & BIT_5 )
{
nVE_CPU_Clock_Divider *= 16;
}
if( wVE_Timer_CSR & BIT_6 )
{
nVE_CPU_Clock_Divider *= 4;
}
if( bVE_Go )
{
QWORD qwCurrentCPU_RunTimeNS = qwCPU_TotalRunTimeNS;
qwCurrentCPU_RunTimeNS += dwCurrentStepsLatencyNS;
QWORD qwCurrentCPU_Clocks = qwCurrentCPU_RunTimeNS/uCPU_CycleNS;
QWORD qwVE_TimerStart_CPU_Clocks = qwCurrentCPU_Clocks;
qwVE_TimerStart_CPU_Clocks -= qwVE_TimerStart_CPU_Clocks%128;
QWORD dwInitialVE_CounterVal = WORD(0177706);
if( !dwInitialVE_CounterVal ) { dwInitialVE_CounterVal = 0x10000; }
dwPrevInitialVE_CounterVal = dwInitialVE_CounterVal;
dwNextInitialVE_CounterVal = dwInitialVE_CounterVal;
if( bVE_NoReload )
{
dwNextInitialVE_CounterVal = 0x10000;
}
qwPrevVE_TimerStart_CPU_Clocks = qwVE_TimerStart_CPU_Clocks;
qwNextVE_TimerStart_CPU_Clocks = qwVE_TimerStart_CPU_Clocks;
qwNextVE_TimerStart_CPU_Clocks += dwInitialVE_CounterVal * nVE_CPU_Clock_Divider;
}
wVE_Timer_Counter = WORD(0177706);
WORD(0177710) = wVE_Timer_Counter;
WORD(0177712) = wVE_Timer_CSR|0xFF00;
}
}
break;
}
}