mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-23 03:32:32 +00:00
Merge pull request #836 from TomHarte/Vic20Tests
Corrects a couple of minor VIA timer issues
This commit is contained in:
commit
8fc3496cc9
@ -8,6 +8,10 @@
|
|||||||
|
|
||||||
#include "../../../Outputs/Log.hpp"
|
#include "../../../Outputs/Log.hpp"
|
||||||
|
|
||||||
|
// As-yet unimplemented (incomplete list):
|
||||||
|
//
|
||||||
|
// PB6 count-down mode for timer 2.
|
||||||
|
|
||||||
namespace MOS {
|
namespace MOS {
|
||||||
namespace MOS6522 {
|
namespace MOS6522 {
|
||||||
|
|
||||||
@ -34,7 +38,7 @@ template <typename T> void MOS6522<T>::write(int address, uint8_t value) {
|
|||||||
address &= 0xf;
|
address &= 0xf;
|
||||||
access(address);
|
access(address);
|
||||||
switch(address) {
|
switch(address) {
|
||||||
case 0x0: // Write Port B.
|
case 0x0: // Write Port B. ('ORB')
|
||||||
// Store locally and communicate outwards.
|
// Store locally and communicate outwards.
|
||||||
registers_.output[1] = value;
|
registers_.output[1] = value;
|
||||||
|
|
||||||
@ -45,7 +49,7 @@ template <typename T> void MOS6522<T>::write(int address, uint8_t value) {
|
|||||||
reevaluate_interrupts();
|
reevaluate_interrupts();
|
||||||
break;
|
break;
|
||||||
case 0xf:
|
case 0xf:
|
||||||
case 0x1: // Write Port A.
|
case 0x1: // Write Port A. ('ORA')
|
||||||
registers_.output[0] = value;
|
registers_.output[0] = value;
|
||||||
|
|
||||||
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||||
@ -59,28 +63,38 @@ template <typename T> void MOS6522<T>::write(int address, uint8_t value) {
|
|||||||
reevaluate_interrupts();
|
reevaluate_interrupts();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x2: // Port B direction.
|
case 0x2: // Port B direction ('DDRB').
|
||||||
registers_.data_direction[1] = value;
|
registers_.data_direction[1] = value;
|
||||||
break;
|
break;
|
||||||
case 0x3: // Port A direction.
|
case 0x3: // Port A direction ('DDRA').
|
||||||
registers_.data_direction[0] = value;
|
registers_.data_direction[0] = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Timer 1
|
// Timer 1
|
||||||
case 0x6: case 0x4: registers_.timer_latch[0] = (registers_.timer_latch[0]&0xff00) | value; break;
|
case 0x6: case 0x4: // ('T1L-L' and 'T1C-L')
|
||||||
case 0x5: case 0x7:
|
registers_.timer_latch[0] = (registers_.timer_latch[0]&0xff00) | value;
|
||||||
|
break;
|
||||||
|
case 0x7: // Timer 1 latch, high ('T1L-H').
|
||||||
registers_.timer_latch[0] = (registers_.timer_latch[0]&0x00ff) | uint16_t(value << 8);
|
registers_.timer_latch[0] = (registers_.timer_latch[0]&0x00ff) | uint16_t(value << 8);
|
||||||
registers_.interrupt_flags &= ~InterruptFlag::Timer1;
|
break;
|
||||||
if(address == 0x05) {
|
case 0x5: // Timer 1 counter, high ('T1C-H').
|
||||||
|
// Fill latch.
|
||||||
|
registers_.timer_latch[0] = (registers_.timer_latch[0]&0x00ff) | uint16_t(value << 8);
|
||||||
|
|
||||||
|
// Restart timer.
|
||||||
registers_.next_timer[0] = registers_.timer_latch[0];
|
registers_.next_timer[0] = registers_.timer_latch[0];
|
||||||
timer_is_running_[0] = true;
|
timer_is_running_[0] = true;
|
||||||
}
|
|
||||||
|
// Clear existing interrupt flag.
|
||||||
|
registers_.interrupt_flags &= ~InterruptFlag::Timer1;
|
||||||
reevaluate_interrupts();
|
reevaluate_interrupts();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Timer 2
|
// Timer 2
|
||||||
case 0x8: registers_.timer_latch[1] = value; break;
|
case 0x8: // ('T2C-L')
|
||||||
case 0x9:
|
registers_.timer_latch[1] = value;
|
||||||
|
break;
|
||||||
|
case 0x9: // ('T2C-H')
|
||||||
registers_.interrupt_flags &= ~InterruptFlag::Timer2;
|
registers_.interrupt_flags &= ~InterruptFlag::Timer2;
|
||||||
registers_.next_timer[1] = registers_.timer_latch[1] | uint16_t(value << 8);
|
registers_.next_timer[1] = registers_.timer_latch[1] | uint16_t(value << 8);
|
||||||
timer_is_running_[1] = true;
|
timer_is_running_[1] = true;
|
||||||
@ -88,7 +102,7 @@ template <typename T> void MOS6522<T>::write(int address, uint8_t value) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
// Shift
|
// Shift
|
||||||
case 0xa:
|
case 0xa: // ('SR')
|
||||||
registers_.shift = value;
|
registers_.shift = value;
|
||||||
shift_bits_remaining_ = 8;
|
shift_bits_remaining_ = 8;
|
||||||
registers_.interrupt_flags &= ~InterruptFlag::ShiftRegister;
|
registers_.interrupt_flags &= ~InterruptFlag::ShiftRegister;
|
||||||
@ -96,11 +110,11 @@ template <typename T> void MOS6522<T>::write(int address, uint8_t value) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
// Control
|
// Control
|
||||||
case 0xb:
|
case 0xb: // Auxiliary control ('ACR').
|
||||||
registers_.auxiliary_control = value;
|
registers_.auxiliary_control = value;
|
||||||
evaluate_cb2_output();
|
evaluate_cb2_output();
|
||||||
break;
|
break;
|
||||||
case 0xc: {
|
case 0xc: { // Peripheral control ('PCR').
|
||||||
// const auto old_peripheral_control = registers_.peripheral_control;
|
// const auto old_peripheral_control = registers_.peripheral_control;
|
||||||
registers_.peripheral_control = value;
|
registers_.peripheral_control = value;
|
||||||
|
|
||||||
@ -141,11 +155,11 @@ template <typename T> void MOS6522<T>::write(int address, uint8_t value) {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
// Interrupt control
|
// Interrupt control
|
||||||
case 0xd:
|
case 0xd: // Interrupt flag regiser ('IFR').
|
||||||
registers_.interrupt_flags &= ~value;
|
registers_.interrupt_flags &= ~value;
|
||||||
reevaluate_interrupts();
|
reevaluate_interrupts();
|
||||||
break;
|
break;
|
||||||
case 0xe:
|
case 0xe: // Interrupt enable register ('IER').
|
||||||
if(value&0x80)
|
if(value&0x80)
|
||||||
registers_.interrupt_enable |= value;
|
registers_.interrupt_enable |= value;
|
||||||
else
|
else
|
||||||
@ -159,46 +173,46 @@ template <typename T> uint8_t MOS6522<T>::read(int address) {
|
|||||||
address &= 0xf;
|
address &= 0xf;
|
||||||
access(address);
|
access(address);
|
||||||
switch(address) {
|
switch(address) {
|
||||||
case 0x0:
|
case 0x0: // Read Port B ('IRB').
|
||||||
registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | InterruptFlag::CB2ActiveEdge);
|
registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | InterruptFlag::CB2ActiveEdge);
|
||||||
reevaluate_interrupts();
|
reevaluate_interrupts();
|
||||||
return get_port_input(Port::B, registers_.data_direction[1], registers_.output[1]);
|
return get_port_input(Port::B, registers_.data_direction[1], registers_.output[1]);
|
||||||
case 0xf:
|
case 0xf:
|
||||||
case 0x1:
|
case 0x1: // Read Port A ('IRA').
|
||||||
registers_.interrupt_flags &= ~(InterruptFlag::CA1ActiveEdge | InterruptFlag::CA2ActiveEdge);
|
registers_.interrupt_flags &= ~(InterruptFlag::CA1ActiveEdge | InterruptFlag::CA2ActiveEdge);
|
||||||
reevaluate_interrupts();
|
reevaluate_interrupts();
|
||||||
return get_port_input(Port::A, registers_.data_direction[0], registers_.output[0]);
|
return get_port_input(Port::A, registers_.data_direction[0], registers_.output[0]);
|
||||||
|
|
||||||
case 0x2: return registers_.data_direction[1];
|
case 0x2: return registers_.data_direction[1]; // Port B direction ('DDRB').
|
||||||
case 0x3: return registers_.data_direction[0];
|
case 0x3: return registers_.data_direction[0]; // Port A direction ('DDRA').
|
||||||
|
|
||||||
// Timer 1
|
// Timer 1
|
||||||
case 0x4:
|
case 0x4: // Timer 1 low-order latches ('T1L-L').
|
||||||
registers_.interrupt_flags &= ~InterruptFlag::Timer1;
|
registers_.interrupt_flags &= ~InterruptFlag::Timer1;
|
||||||
reevaluate_interrupts();
|
reevaluate_interrupts();
|
||||||
return registers_.timer[0] & 0x00ff;
|
return registers_.timer[0] & 0x00ff;
|
||||||
case 0x5: return registers_.timer[0] >> 8;
|
case 0x5: return registers_.timer[0] >> 8; // Timer 1 high-order counter ('T1C-H')
|
||||||
case 0x6: return registers_.timer_latch[0] & 0x00ff;
|
case 0x6: return registers_.timer_latch[0] & 0x00ff; // Timer 1 low-order latches ('T1L-L').
|
||||||
case 0x7: return registers_.timer_latch[0] >> 8;
|
case 0x7: return registers_.timer_latch[0] >> 8; // Timer 1 high-order latches ('T1L-H').
|
||||||
|
|
||||||
// Timer 2
|
// Timer 2
|
||||||
case 0x8:
|
case 0x8: // Timer 2 low-order counter ('T2C-L').
|
||||||
registers_.interrupt_flags &= ~InterruptFlag::Timer2;
|
registers_.interrupt_flags &= ~InterruptFlag::Timer2;
|
||||||
reevaluate_interrupts();
|
reevaluate_interrupts();
|
||||||
return registers_.timer[1] & 0x00ff;
|
return registers_.timer[1] & 0x00ff;
|
||||||
case 0x9: return registers_.timer[1] >> 8;
|
case 0x9: return registers_.timer[1] >> 8; // Timer 2 high-order counter ('T2C-H').
|
||||||
|
|
||||||
case 0xa:
|
case 0xa: // Shift register ('SR').
|
||||||
shift_bits_remaining_ = 8;
|
shift_bits_remaining_ = 8;
|
||||||
registers_.interrupt_flags &= ~InterruptFlag::ShiftRegister;
|
registers_.interrupt_flags &= ~InterruptFlag::ShiftRegister;
|
||||||
reevaluate_interrupts();
|
reevaluate_interrupts();
|
||||||
return registers_.shift;
|
return registers_.shift;
|
||||||
|
|
||||||
case 0xb: return registers_.auxiliary_control;
|
case 0xb: return registers_.auxiliary_control; // Auxiliary control ('ACR').
|
||||||
case 0xc: return registers_.peripheral_control;
|
case 0xc: return registers_.peripheral_control; // Peripheral control ('PCR').
|
||||||
|
|
||||||
case 0xd: return registers_.interrupt_flags | (get_interrupt_line() ? 0x80 : 0x00);
|
case 0xd: return registers_.interrupt_flags | (get_interrupt_line() ? 0x80 : 0x00); // Interrupt flag register ('IFR').
|
||||||
case 0xe: return registers_.interrupt_enable | 0x80;
|
case 0xe: return registers_.interrupt_enable | 0x80; // Interrupt enable register ('IER').
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0xff;
|
return 0xff;
|
||||||
@ -276,10 +290,13 @@ template <typename T> void MOS6522<T>::do_phase2() {
|
|||||||
registers_.timer_needs_reload = false;
|
registers_.timer_needs_reload = false;
|
||||||
registers_.timer[0] = registers_.timer_latch[0];
|
registers_.timer[0] = registers_.timer_latch[0];
|
||||||
} else {
|
} else {
|
||||||
registers_.timer[0] --;
|
-- registers_.timer[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
registers_.timer[1] --;
|
// Count down timer 2 if it is in timed interrupt mode (i.e. auxiliary control bit 5 is clear).
|
||||||
|
// TODO: implement count down on PB6 if this bit isn't set.
|
||||||
|
registers_.timer[1] -= 1 ^ ((registers_.auxiliary_control >> 5)&1);
|
||||||
|
|
||||||
if(registers_.next_timer[0] >= 0) {
|
if(registers_.next_timer[0] >= 0) {
|
||||||
registers_.timer[0] = uint16_t(registers_.next_timer[0]);
|
registers_.timer[0] = uint16_t(registers_.next_timer[0]);
|
||||||
registers_.next_timer[0] = -1;
|
registers_.next_timer[0] = -1;
|
||||||
|
Loading…
Reference in New Issue
Block a user