mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-18 01:07:58 +00:00
Wrote test case for what appears to be correct timer behaviour if those were acting in isolation. Ensured implementation matches test case.
This commit is contained in:
parent
394902f409
commit
5d26cd85a3
@ -150,28 +150,39 @@ template <class T> class MOS6522 {
|
||||
{
|
||||
if(_is_phase2)
|
||||
{
|
||||
_registers.timer[0] --;
|
||||
_registers.timer[1] --;
|
||||
_registers.last_timer[0] = _registers.timer[0];
|
||||
_registers.last_timer[1] = _registers.timer[1];
|
||||
|
||||
if(!_registers.timer[1] && _timer_is_running[1])
|
||||
if(_registers.timer_needs_reload)
|
||||
{
|
||||
_registers.timer_needs_reload = false;
|
||||
_registers.timer[0] = _registers.timer_latch[0];
|
||||
}
|
||||
else
|
||||
_registers.timer[0] --;
|
||||
|
||||
_registers.timer[1] --;
|
||||
}
|
||||
else
|
||||
{
|
||||
// IRQ is raised on the half cycle after overflow
|
||||
if((_registers.timer[1] == 0xffff) && !_registers.last_timer[1] && _timer_is_running[1])
|
||||
{
|
||||
_timer_is_running[1] = false;
|
||||
_registers.interrupt_flags |= InterruptFlag::Timer2;
|
||||
reevaluate_interrupts();
|
||||
}
|
||||
|
||||
if(!_registers.timer[0] && _timer_is_running[0])
|
||||
if((_registers.timer[0] == 0xffff) && !_registers.last_timer[0] && _timer_is_running[0])
|
||||
{
|
||||
_registers.interrupt_flags |= InterruptFlag::Timer1;
|
||||
reevaluate_interrupts();
|
||||
|
||||
// TODO: reload shouldn't occur for a further 1.5 cycles
|
||||
if(_registers.auxiliary_control&0x40)
|
||||
_registers.timer[0] = _registers.timer_latch[0];
|
||||
_registers.timer_needs_reload = true;
|
||||
else
|
||||
_timer_is_running[0] = false;
|
||||
}
|
||||
// TODO: lots of other status effects
|
||||
}
|
||||
|
||||
_is_phase2 ^= true;
|
||||
@ -214,16 +225,18 @@ template <class T> class MOS6522 {
|
||||
// The registers
|
||||
struct Registers {
|
||||
uint8_t output[2], input[2], data_direction[2];
|
||||
uint16_t timer[2], timer_latch[2];
|
||||
uint16_t timer[2], timer_latch[2], last_timer[2];
|
||||
uint8_t shift;
|
||||
uint8_t auxiliary_control, peripheral_control;
|
||||
uint8_t interrupt_flags, interrupt_enable;
|
||||
bool timer_needs_reload;
|
||||
|
||||
// "A low reset (RES) input clears all R6522 internal registers to logic 0"
|
||||
Registers() :
|
||||
output{0, 0}, input{0, 0}, data_direction{0, 0},
|
||||
auxiliary_control(0), peripheral_control(0),
|
||||
interrupt_flags(0), interrupt_enable(0) {}
|
||||
interrupt_flags(0), interrupt_enable(0),
|
||||
last_timer{0, 0}, timer_needs_reload(false) {}
|
||||
} _registers;
|
||||
|
||||
// Internal state other than the registers
|
||||
|
@ -18,13 +18,51 @@ class MOS6522Tests: XCTestCase {
|
||||
|
||||
func testTimerCount() {
|
||||
with6522 {
|
||||
// set timer 1 to a value of $000a
|
||||
$0.setValue(10, forRegister: 4)
|
||||
$0.setValue(0, forRegister: 5)
|
||||
|
||||
// run for 5 cycles
|
||||
$0.runForHalfCycles(10)
|
||||
|
||||
// check that the timer has gone down by 5
|
||||
XCTAssert($0.valueForRegister(4) == 5, "Low order byte of timer should be 5; was \($0.valueForRegister(4))")
|
||||
XCTAssert($0.valueForRegister(5) == 0, "High order byte of timer should be 5; was \($0.valueForRegister(5))")
|
||||
XCTAssert($0.valueForRegister(5) == 0, "High order byte of timer should be 0; was \($0.valueForRegister(5))")
|
||||
}
|
||||
}
|
||||
|
||||
func testTimerReload() {
|
||||
with6522 {
|
||||
// set timer 1 to a value of $0010, enable repeating mode
|
||||
$0.setValue(16, forRegister: 4)
|
||||
$0.setValue(0, forRegister: 5)
|
||||
$0.setValue(0x40, forRegister: 11)
|
||||
$0.setValue(0x40 | 0x80, forRegister: 14)
|
||||
|
||||
// run for 16 cycles
|
||||
$0.runForHalfCycles(32)
|
||||
|
||||
// check that the timer has gone down to 0 but not yet triggered an interrupt
|
||||
XCTAssert($0.valueForRegister(4) == 0, "Low order byte of timer should be 0; was \($0.valueForRegister(4))")
|
||||
XCTAssert($0.valueForRegister(5) == 0, "High order byte of timer should be 0; was \($0.valueForRegister(5))")
|
||||
XCTAssert(!$0.irqLine, "IRQ should not yet be active")
|
||||
|
||||
// check that two half-cycles later the timer is $ffff but IRQ still hasn't triggered
|
||||
$0.runForHalfCycles(2)
|
||||
XCTAssert($0.valueForRegister(4) == 0xff, "Low order byte of timer should be 0xff; was \($0.valueForRegister(4))")
|
||||
XCTAssert($0.valueForRegister(5) == 0xff, "High order byte of timer should be 0xff; was \($0.valueForRegister(5))")
|
||||
XCTAssert(!$0.irqLine, "IRQ should not yet be active")
|
||||
|
||||
// check that one half-cycle later the timer is still $ffff and IRQ has triggered
|
||||
$0.runForHalfCycles(1)
|
||||
XCTAssert($0.irqLine, "IRQ should be active")
|
||||
XCTAssert($0.valueForRegister(4) == 0xff, "Low order byte of timer should be 0xff; was \($0.valueForRegister(4))")
|
||||
XCTAssert($0.valueForRegister(5) == 0xff, "High order byte of timer should be 0xff; was \($0.valueForRegister(5))")
|
||||
|
||||
// check that one half-cycles later the timer has reloaded
|
||||
$0.runForHalfCycles(1)
|
||||
XCTAssert($0.valueForRegister(4) == 0x10, "Low order byte of timer should be 0x10; was \($0.valueForRegister(4))")
|
||||
XCTAssert($0.valueForRegister(5) == 0x00, "High order byte of timer should be 0x00; was \($0.valueForRegister(5))")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,12 @@ class VanillaVIA: public MOS::MOS6522<VanillaVIA> {
|
||||
|
||||
- (void)runForHalfCycles:(NSUInteger)numberOfHalfCycles
|
||||
{
|
||||
_via.run_for_half_cycles(numberOfHalfCycles);
|
||||
_via.run_for_half_cycles((int)numberOfHalfCycles);
|
||||
}
|
||||
|
||||
- (BOOL)irqLine
|
||||
{
|
||||
return _via.irq_line;
|
||||
}
|
||||
|
||||
@end
|
||||
|
Loading…
Reference in New Issue
Block a user