Patron
14.02.2011, 16:10
После завершения серии тестов (http://bk0010.org/forum/?id=3799) встроенного таймера процессора 1801ВМ1 ( но же ВЕ-таймер ) мною написан алгоритм максимально точной эмуляции его работы.
Предлагаю всем заинтересованным участникам проверить корректность алгоритма:
typedef unsigned long long QWORD;
typedef unsigned long DWORD;
#define WORD(a) (*(unsigned short *)&aMemory[a])
PowerON()
{
WORD(0177706) = wCPU_ID;
wVE_Timer_Counter = 0177777;
WORD(0177710) = wVE_Timer_Counter;
WORD(0177712) = 0177400;
}
Reset()
{
WORD(0177712) = 0;
Last_IO_Mode = IO_WRITE;
wLast_IO_Addr = 0177712;
Make_IO();
}
Make_IO()
{
switch( wLast_IO_Addr )
{
// 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 qwUsedCPU_Clocks = qwCurrentCPU_Clocks - qwVE_TimerStart_CPU_Clocks;
qwUsedCPU_Clocks += qwVE_TimerStart_CPU_Clocks%128;
QWORD qwCurrentVE_Timer_Clocks = qwUsedCPU_Clocks/nVE_CPU_Clock_Divider;
if( !dwInitialVE_CounterVal ) { dwInitialVE_CounterVal = 0x10000; }
DWORD dwTimerClocksHi = qwCurrentVE_Timer_Clocks / dwInitialVE_CounterVal;
DWORD dwTimerClocksLow = qwCurrentVE_Timer_Clocks % dwInitialVE_CounterVal;
if( dwTimerClocksHi )
{
bVE_Expiry = true;
if( bVE_NoReload )
{
dwTimerClocksLow = ( qwCurrentVE_Timer_Clocks - dwInitialVE_CounterVal ) % 0xFFFF;
dwInitialVE_CounterVal = 0xFFFF;
wVE_Timer_Counter = dwInitialVE_CounterVal - dwTimerClocksLow;
}
else if( bVE_NoRestart )
{
wVE_Timer_Counter = 0;
}
else
{
wVE_Timer_Counter = dwInitialVE_CounterVal - dwTimerClocksLow;
if( !dwTimerClocksLow || dwInitialVE_CounterVal == 1 )
{
if( qwCurrentCPU_Clocks%128 < 5 )
{
wVE_Timer_Counter = 0;
}
}
}
}
else
{ // dwTimerClocksHi == 0
wVE_Timer_Counter = dwInitialVE_CounterVal - dwTimerClocksLow;
}
WORD(0177710) = wVE_Timer_Counter;
}
}
}
break;
// VE-Timer CSR
case 0177712:
{
word wVE_Timer_CSR = WORD(0177712);
if( Last_IO_Mode == IO_READ )
{
wLast_IO_Addr = 0177710;
Make_IO();
wLast_IO_Addr = 0177712;
if( bVE_Expiry )
{
if( bVE_Monitor )
{
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
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;
}
QWORD qwCurrentCPU_RunTimeNS = qwCPU_TotalRunTimeNS;
qwCurrentCPU_RunTimeNS += dwCurrentStepsLatencyNS;
QWORD qwCurrentCPU_Clocks = qwCurrentCPU_RunTimeNS/uCPU_CycleNS;
if( bVE_Go )
{
qwVE_TimerStart_CPU_Clocks = qwCurrentCPU_Clocks;
}
dwInitialVE_CounterVal = WORD(0177706);
wVE_Timer_Counter = WORD(0177706);
WORD(0177710) = wVE_Timer_Counter;
WORD(0177712) = wVE_Timer_CSR|0xFF00;
}
}
break;
}
}
...
Добавлен файл VM1T8.zip, содержащий "1801VM1 Interrupts Test #8".
Добавлен файл VM1VE8.zip, содержащий "1801VM1 VE-Timer Test #8".
Добавлен файл VM1VE9.zip, содержащий "1801VM1 VE-Timer Test #9".
Добавлен файл VM1E10.zip, содержащий "1801VM1 VE-Timer Test #10".
Добавлен файл VM1E11.zip, содержащий "1801VM1 VE-Timer Test #11".
Предлагаю всем заинтересованным участникам проверить корректность алгоритма:
typedef unsigned long long QWORD;
typedef unsigned long DWORD;
#define WORD(a) (*(unsigned short *)&aMemory[a])
PowerON()
{
WORD(0177706) = wCPU_ID;
wVE_Timer_Counter = 0177777;
WORD(0177710) = wVE_Timer_Counter;
WORD(0177712) = 0177400;
}
Reset()
{
WORD(0177712) = 0;
Last_IO_Mode = IO_WRITE;
wLast_IO_Addr = 0177712;
Make_IO();
}
Make_IO()
{
switch( wLast_IO_Addr )
{
// 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 qwUsedCPU_Clocks = qwCurrentCPU_Clocks - qwVE_TimerStart_CPU_Clocks;
qwUsedCPU_Clocks += qwVE_TimerStart_CPU_Clocks%128;
QWORD qwCurrentVE_Timer_Clocks = qwUsedCPU_Clocks/nVE_CPU_Clock_Divider;
if( !dwInitialVE_CounterVal ) { dwInitialVE_CounterVal = 0x10000; }
DWORD dwTimerClocksHi = qwCurrentVE_Timer_Clocks / dwInitialVE_CounterVal;
DWORD dwTimerClocksLow = qwCurrentVE_Timer_Clocks % dwInitialVE_CounterVal;
if( dwTimerClocksHi )
{
bVE_Expiry = true;
if( bVE_NoReload )
{
dwTimerClocksLow = ( qwCurrentVE_Timer_Clocks - dwInitialVE_CounterVal ) % 0xFFFF;
dwInitialVE_CounterVal = 0xFFFF;
wVE_Timer_Counter = dwInitialVE_CounterVal - dwTimerClocksLow;
}
else if( bVE_NoRestart )
{
wVE_Timer_Counter = 0;
}
else
{
wVE_Timer_Counter = dwInitialVE_CounterVal - dwTimerClocksLow;
if( !dwTimerClocksLow || dwInitialVE_CounterVal == 1 )
{
if( qwCurrentCPU_Clocks%128 < 5 )
{
wVE_Timer_Counter = 0;
}
}
}
}
else
{ // dwTimerClocksHi == 0
wVE_Timer_Counter = dwInitialVE_CounterVal - dwTimerClocksLow;
}
WORD(0177710) = wVE_Timer_Counter;
}
}
}
break;
// VE-Timer CSR
case 0177712:
{
word wVE_Timer_CSR = WORD(0177712);
if( Last_IO_Mode == IO_READ )
{
wLast_IO_Addr = 0177710;
Make_IO();
wLast_IO_Addr = 0177712;
if( bVE_Expiry )
{
if( bVE_Monitor )
{
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
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;
}
QWORD qwCurrentCPU_RunTimeNS = qwCPU_TotalRunTimeNS;
qwCurrentCPU_RunTimeNS += dwCurrentStepsLatencyNS;
QWORD qwCurrentCPU_Clocks = qwCurrentCPU_RunTimeNS/uCPU_CycleNS;
if( bVE_Go )
{
qwVE_TimerStart_CPU_Clocks = qwCurrentCPU_Clocks;
}
dwInitialVE_CounterVal = WORD(0177706);
wVE_Timer_Counter = WORD(0177706);
WORD(0177710) = wVE_Timer_Counter;
WORD(0177712) = wVE_Timer_CSR|0xFF00;
}
}
break;
}
}
...
Добавлен файл VM1T8.zip, содержащий "1801VM1 Interrupts Test #8".
Добавлен файл VM1VE8.zip, содержащий "1801VM1 VE-Timer Test #8".
Добавлен файл VM1VE9.zip, содержащий "1801VM1 VE-Timer Test #9".
Добавлен файл VM1E10.zip, содержащий "1801VM1 VE-Timer Test #10".
Добавлен файл VM1E11.zip, содержащий "1801VM1 VE-Timer Test #11".