После завершения серии тестов встроенного таймера процессора 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".




Ответить с цитированием
Размещение рекламы на форуме способствует его дальнейшему развитию 
