mirror of
https://github.com/TomHarte/CLK.git
synced 2025-10-26 17:17:58 +00:00
Compare commits
22 Commits
2020-09-16
...
2020-10-02
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcf8cb14e2 | ||
|
|
38912859e1 | ||
|
|
f4a23af5d6 | ||
|
|
ce0536cdfa | ||
|
|
669d8e64ab | ||
|
|
9447aa38be | ||
|
|
a781c3eb4d | ||
|
|
c0b1308dfd | ||
|
|
2d9dd6704a | ||
|
|
94dba70bbe | ||
|
|
022ec20e75 | ||
|
|
41f69405d8 | ||
|
|
5741e22e29 | ||
|
|
8e242eea54 | ||
|
|
703065a0a5 | ||
|
|
291aa42fe1 | ||
|
|
8fc3496cc9 | ||
|
|
e807a462a1 | ||
|
|
18790a90ae | ||
|
|
21afc70261 | ||
|
|
7bb74af478 | ||
|
|
894269aa06 |
@@ -128,13 +128,14 @@ template <class T> class MOS6522: public MOS6522Storage {
|
|||||||
|
|
||||||
void access(int address);
|
void access(int address);
|
||||||
|
|
||||||
uint8_t get_port_input(Port port, uint8_t output_mask, uint8_t output);
|
uint8_t get_port_input(Port port, uint8_t output_mask, uint8_t output, uint8_t timer_mask);
|
||||||
inline void reevaluate_interrupts();
|
inline void reevaluate_interrupts();
|
||||||
|
|
||||||
/// Sets the current intended output value for the port and line;
|
/// Sets the current intended output value for the port and line;
|
||||||
/// if this affects the visible output, it will be passed to the handler.
|
/// if this affects the visible output, it will be passed to the handler.
|
||||||
void set_control_line_output(Port port, Line line, LineState value);
|
void set_control_line_output(Port port, Line line, LineState value);
|
||||||
void evaluate_cb2_output();
|
void evaluate_cb2_output();
|
||||||
|
void evaluate_port_b_output();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,18 +38,18 @@ 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;
|
||||||
|
|
||||||
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||||
bus_handler_.set_port_output(Port::B, value, registers_.data_direction[1]);
|
evaluate_port_b_output();
|
||||||
|
|
||||||
registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | ((registers_.peripheral_control&0x20) ? 0 : InterruptFlag::CB2ActiveEdge));
|
registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | ((registers_.peripheral_control&0x20) ? 0 : InterruptFlag::CB2ActiveEdge));
|
||||||
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,44 @@ 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').
|
||||||
registers_.next_timer[0] = registers_.timer_latch[0];
|
// Fill latch.
|
||||||
timer_is_running_[0] = true;
|
registers_.timer_latch[0] = (registers_.timer_latch[0]&0x00ff) | uint16_t(value << 8);
|
||||||
|
|
||||||
|
// Restart timer.
|
||||||
|
registers_.next_timer[0] = registers_.timer_latch[0];
|
||||||
|
timer_is_running_[0] = true;
|
||||||
|
|
||||||
|
// If PB7 output mode is active, set it low.
|
||||||
|
if(timer1_is_controlling_pb7()) {
|
||||||
|
registers_.timer_port_b_output &= 0x7f;
|
||||||
|
evaluate_port_b_output();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 +108,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 +116,18 @@ 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();
|
||||||
|
|
||||||
|
// This is a bit of a guess: reset the timer-based PB7 output to its default high level
|
||||||
|
// any timer that timer-linked PB7 output is disabled.
|
||||||
|
if(!timer1_is_controlling_pb7()) {
|
||||||
|
registers_.timer_port_b_output |= 0x80;
|
||||||
|
}
|
||||||
|
evaluate_port_b_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 +168,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,54 +186,55 @@ 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], registers_.auxiliary_control & 0x80);
|
||||||
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], 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> uint8_t MOS6522<T>::get_port_input(Port port, uint8_t output_mask, uint8_t output) {
|
template <typename T> uint8_t MOS6522<T>::get_port_input(Port port, uint8_t output_mask, uint8_t output, uint8_t timer_mask) {
|
||||||
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||||
const uint8_t input = bus_handler_.get_port_input(port);
|
const uint8_t input = bus_handler_.get_port_input(port);
|
||||||
|
output = (output & ~timer_mask) | (registers_.timer_port_b_output & timer_mask);
|
||||||
return (input & ~output_mask) | (output & output_mask);
|
return (input & ~output_mask) | (output & output_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,10 +304,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).
|
||||||
|
registers_.timer[1] -= timer2_clock_decrement();
|
||||||
|
|
||||||
|
// TODO: can eliminate conditional branches here.
|
||||||
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;
|
||||||
@@ -330,20 +361,29 @@ template <typename T> void MOS6522<T>::do_phase1() {
|
|||||||
reevaluate_interrupts();
|
reevaluate_interrupts();
|
||||||
|
|
||||||
// Determine whether to reload.
|
// Determine whether to reload.
|
||||||
if(registers_.auxiliary_control&0x40)
|
if(timer1_is_continuous())
|
||||||
registers_.timer_needs_reload = true;
|
registers_.timer_needs_reload = true;
|
||||||
else
|
else
|
||||||
timer_is_running_[0] = false;
|
timer_is_running_[0] = false;
|
||||||
|
|
||||||
// Determine whether to toggle PB7.
|
// Determine whether to toggle PB7.
|
||||||
if(registers_.auxiliary_control&0x80) {
|
if(timer1_is_controlling_pb7()) {
|
||||||
registers_.output[1] ^= 0x80;
|
registers_.timer_port_b_output ^= 0x80;
|
||||||
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||||
bus_handler_.set_port_output(Port::B, registers_.output[1], registers_.data_direction[1]);
|
evaluate_port_b_output();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> void MOS6522<T>::evaluate_port_b_output() {
|
||||||
|
// Apply current timer-linked PB7 output if any atop the stated output.
|
||||||
|
const uint8_t timer_control_bit = registers_.auxiliary_control & 0x80;
|
||||||
|
bus_handler_.set_port_output(
|
||||||
|
Port::B,
|
||||||
|
(registers_.output[1] & (0xff ^ timer_control_bit)) | timer_control_bit,
|
||||||
|
registers_.data_direction[1] | timer_control_bit);
|
||||||
|
}
|
||||||
|
|
||||||
/*! Runs for a specified number of half cycles. */
|
/*! Runs for a specified number of half cycles. */
|
||||||
template <typename T> void MOS6522<T>::run_for(const HalfCycles half_cycles) {
|
template <typename T> void MOS6522<T>::run_for(const HalfCycles half_cycles) {
|
||||||
auto number_of_half_cycles = half_cycles.as_integral();
|
auto number_of_half_cycles = half_cycles.as_integral();
|
||||||
@@ -438,10 +478,11 @@ template <typename T> void MOS6522<T>::shift_in() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> void MOS6522<T>::shift_out() {
|
template <typename T> void MOS6522<T>::shift_out() {
|
||||||
// When shifting out, the shift register rotates rather than strictly shifts.
|
const bool is_free_running = shift_mode() == ShiftMode::OutUnderT2FreeRunning;
|
||||||
// TODO: is that true for all modes?
|
if(is_free_running || shift_bits_remaining_) {
|
||||||
if(shift_mode() == ShiftMode::OutUnderT2FreeRunning || shift_bits_remaining_) {
|
// Recirculate bits only if in free-running mode (?)
|
||||||
registers_.shift = uint8_t((registers_.shift << 1) | (registers_.shift >> 7));
|
const uint8_t incoming_bit = (registers_.shift >> 7) * is_free_running;
|
||||||
|
registers_.shift = uint8_t(registers_.shift << 1) | incoming_bit;
|
||||||
evaluate_cb2_output();
|
evaluate_cb2_output();
|
||||||
|
|
||||||
--shift_bits_remaining_;
|
--shift_bits_remaining_;
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ class MOS6522Storage {
|
|||||||
uint8_t peripheral_control = 0;
|
uint8_t peripheral_control = 0;
|
||||||
uint8_t interrupt_flags = 0;
|
uint8_t interrupt_flags = 0;
|
||||||
uint8_t interrupt_enable = 0;
|
uint8_t interrupt_enable = 0;
|
||||||
|
|
||||||
bool timer_needs_reload = false;
|
bool timer_needs_reload = false;
|
||||||
|
uint8_t timer_port_b_output = 0xff;
|
||||||
} registers_;
|
} registers_;
|
||||||
|
|
||||||
// Control state.
|
// Control state.
|
||||||
@@ -79,12 +81,30 @@ class MOS6522Storage {
|
|||||||
OutUnderPhase2 = 6,
|
OutUnderPhase2 = 6,
|
||||||
OutUnderCB1 = 7
|
OutUnderCB1 = 7
|
||||||
};
|
};
|
||||||
ShiftMode shift_mode() const {
|
bool timer1_is_controlling_pb7() const {
|
||||||
return ShiftMode((registers_.auxiliary_control >> 2) & 7);
|
return registers_.auxiliary_control & 0x80;
|
||||||
|
}
|
||||||
|
bool timer1_is_continuous() const {
|
||||||
|
return registers_.auxiliary_control & 0x40;
|
||||||
}
|
}
|
||||||
bool is_shifting_out() const {
|
bool is_shifting_out() const {
|
||||||
return registers_.auxiliary_control & 0x10;
|
return registers_.auxiliary_control & 0x10;
|
||||||
}
|
}
|
||||||
|
int timer2_clock_decrement() const {
|
||||||
|
return 1 ^ ((registers_.auxiliary_control >> 5)&1);
|
||||||
|
}
|
||||||
|
int timer2_pb6_decrement() const {
|
||||||
|
return (registers_.auxiliary_control >> 5)&1;
|
||||||
|
}
|
||||||
|
ShiftMode shift_mode() const {
|
||||||
|
return ShiftMode((registers_.auxiliary_control >> 2) & 7);
|
||||||
|
}
|
||||||
|
bool portb_is_latched() const {
|
||||||
|
return registers_.auxiliary_control & 0x02;
|
||||||
|
}
|
||||||
|
bool port1_is_latched() const {
|
||||||
|
return registers_.auxiliary_control & 0x01;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -860,6 +860,7 @@
|
|||||||
4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */; };
|
4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */; };
|
||||||
4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; };
|
4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; };
|
||||||
4BF437EF209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; };
|
4BF437EF209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; };
|
||||||
|
4BF8D4C82516E27A00BBE21B /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BB8617024E22F4900A00E03 /* Accelerate.framework */; };
|
||||||
4BFCA1241ECBDCB400AC40C1 /* AllRAMProcessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */; };
|
4BFCA1241ECBDCB400AC40C1 /* AllRAMProcessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */; };
|
||||||
4BFCA1271ECBE33200AC40C1 /* TestMachineZ80.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1261ECBE33200AC40C1 /* TestMachineZ80.mm */; };
|
4BFCA1271ECBE33200AC40C1 /* TestMachineZ80.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1261ECBE33200AC40C1 /* TestMachineZ80.mm */; };
|
||||||
4BFCA1291ECBE7A700AC40C1 /* zexall.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BFCA1281ECBE7A700AC40C1 /* zexall.com */; };
|
4BFCA1291ECBE7A700AC40C1 /* zexall.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BFCA1281ECBE7A700AC40C1 /* zexall.com */; };
|
||||||
@@ -1830,6 +1831,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
4B9F11CA2272433900701480 /* libz.tbd in Frameworks */,
|
4B9F11CA2272433900701480 /* libz.tbd in Frameworks */,
|
||||||
|
4BF8D4C82516E27A00BBE21B /* Accelerate.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
</Testables>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
enableASanStackUseAfterReturn = "YES"
|
enableASanStackUseAfterReturn = "YES"
|
||||||
|
|||||||
@@ -278,8 +278,9 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
|||||||
size_t _chromaKernelSize;
|
size_t _chromaKernelSize;
|
||||||
std::atomic<bool> _isUsingSupersampling;
|
std::atomic<bool> _isUsingSupersampling;
|
||||||
|
|
||||||
// The output view.
|
// The output view and its aspect ratio.
|
||||||
__weak MTKView *_view;
|
__weak MTKView *_view;
|
||||||
|
CGFloat _viewAspectRatio; // To avoid accessing .bounds away from the main thread.
|
||||||
}
|
}
|
||||||
|
|
||||||
- (nonnull instancetype)initWithView:(nonnull MTKView *)view {
|
- (nonnull instancetype)initWithView:(nonnull MTKView *)view {
|
||||||
@@ -357,6 +358,7 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
|||||||
@param size New drawable size in pixels
|
@param size New drawable size in pixels
|
||||||
*/
|
*/
|
||||||
- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size {
|
- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size {
|
||||||
|
_viewAspectRatio = size.width / size.height;
|
||||||
[self setAspectRatio];
|
[self setAspectRatio];
|
||||||
|
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
@@ -522,7 +524,6 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
|||||||
|
|
||||||
- (void)setAspectRatio {
|
- (void)setAspectRatio {
|
||||||
const auto modals = _scanTarget.modals();
|
const auto modals = _scanTarget.modals();
|
||||||
const auto viewAspectRatio = (_view.bounds.size.width / _view.bounds.size.height);
|
|
||||||
simd::float3x3 sourceToDisplay{1.0f};
|
simd::float3x3 sourceToDisplay{1.0f};
|
||||||
|
|
||||||
// The starting coordinate space is [0, 1].
|
// The starting coordinate space is [0, 1].
|
||||||
@@ -550,7 +551,7 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
|||||||
// Determine the correct zoom level. This is a combination of (i) the necessary horizontal stretch to produce a proper
|
// Determine the correct zoom level. This is a combination of (i) the necessary horizontal stretch to produce a proper
|
||||||
// aspect ratio; and (ii) the necessary zoom from there to either fit the visible area width or height as per a decision
|
// aspect ratio; and (ii) the necessary zoom from there to either fit the visible area width or height as per a decision
|
||||||
// on letterboxing or pillarboxing.
|
// on letterboxing or pillarboxing.
|
||||||
const float aspectRatioStretch = float(modals.aspect_ratio / viewAspectRatio);
|
const float aspectRatioStretch = float(modals.aspect_ratio / _viewAspectRatio);
|
||||||
const float fitWidthZoom = 1.0f / (float(modals.visible_area.size.width) * aspectRatioStretch);
|
const float fitWidthZoom = 1.0f / (float(modals.visible_area.size.width) * aspectRatioStretch);
|
||||||
const float fitHeightZoom = 1.0f / float(modals.visible_area.size.height);
|
const float fitHeightZoom = 1.0f / float(modals.visible_area.size.height);
|
||||||
const float zoom = std::min(fitWidthZoom, fitHeightZoom);
|
const float zoom = std::min(fitWidthZoom, fitHeightZoom);
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
func runTest(_ code: [UInt8], expectedRunLength: UInt32) {
|
func runTest(_ code: [UInt8], expectedRunLength: UInt32) {
|
||||||
machine.trapHandler = self
|
machine.trapHandler = self
|
||||||
|
|
||||||
let immediateCode = Data(bytes: UnsafePointer<UInt8>(code), count: code.count)
|
let immediateCode = Data(code)
|
||||||
machine.setData(immediateCode, atAddress: 0x200)
|
machine.setData(immediateCode, atAddress: 0x200)
|
||||||
machine.addTrapAddress(UInt16(0x200 + code.count))
|
machine.addTrapAddress(UInt16(0x200 + code.count))
|
||||||
machine.setValue(0x00, forAddress: 0x0000)
|
machine.setValue(0x00, forAddress: 0x0000)
|
||||||
|
|||||||
@@ -101,6 +101,116 @@ class MOS6522Tests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: PB7 timer 1 tests
|
||||||
|
// These follow the same logic and check for the same results as the VICE VIC-20 via_pb7 tests.
|
||||||
|
|
||||||
|
// Perfoms:
|
||||||
|
//
|
||||||
|
// (1) establish initial ACR and port B output value, and grab port B input value.
|
||||||
|
// (2) start timer 1, grab port B input value.
|
||||||
|
// (3) set final ACR, grab port B input value.
|
||||||
|
// (4) allow timer 1 to expire, grab port B input value.
|
||||||
|
private func runTest(startACR: UInt8, endACR: UInt8, portBOutput: UInt8) -> [UInt8] {
|
||||||
|
var result: [UInt8] = []
|
||||||
|
|
||||||
|
// Clear all register values.
|
||||||
|
for n: UInt in 0...15 {
|
||||||
|
m6522.setValue(0, forRegister: n)
|
||||||
|
}
|
||||||
|
m6522.run(forHalfCycles: 2)
|
||||||
|
|
||||||
|
// Set data direction and current port B value.
|
||||||
|
m6522.setValue(0xff, forRegister: 2)
|
||||||
|
m6522.run(forHalfCycles: 2)
|
||||||
|
m6522.setValue(portBOutput, forRegister: 0)
|
||||||
|
m6522.run(forHalfCycles: 2)
|
||||||
|
|
||||||
|
// Set initial ACR and grab the current port B value.
|
||||||
|
m6522.setValue(startACR, forRegister: 0xb)
|
||||||
|
m6522.run(forHalfCycles: 2)
|
||||||
|
result.append(m6522.value(forRegister: 0))
|
||||||
|
m6522.run(forHalfCycles: 2)
|
||||||
|
|
||||||
|
// Start timer 1 and grab the value.
|
||||||
|
m6522.setValue(1, forRegister: 0x5)
|
||||||
|
m6522.run(forHalfCycles: 2)
|
||||||
|
result.append(m6522.value(forRegister: 0))
|
||||||
|
m6522.run(forHalfCycles: 2)
|
||||||
|
|
||||||
|
// Set the final ACR value and grab value.
|
||||||
|
m6522.setValue(endACR, forRegister: 0xb)
|
||||||
|
m6522.run(forHalfCycles: 2)
|
||||||
|
result.append(m6522.value(forRegister: 0))
|
||||||
|
m6522.run(forHalfCycles: 2)
|
||||||
|
|
||||||
|
// Make sure timer 1 has expired.
|
||||||
|
m6522.run(forHalfCycles: 512)
|
||||||
|
|
||||||
|
// Grab the final value.
|
||||||
|
result.append(m6522.value(forRegister: 0))
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTimer1PB7() {
|
||||||
|
// Original top row. [original Vic-20 screen output in comments on the right]
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x00, portBOutput: 0x00), [0x00, 0x00, 0x00, 0x00]) // @@@@ (i.e. 0, 0, 0, 0)
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x40, portBOutput: 0x00), [0x00, 0x00, 0x00, 0x00]) // @@@@ (i.e. 0, 0, 0, 0)
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x80, portBOutput: 0x00), [0x00, 0x00, 0x80, 0x00]) // @@b@ (i.e. 0, 0, 1, 0)
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x00, endACR: 0xc0, portBOutput: 0x00), [0x00, 0x00, 0x80, 0x00]) // @@b@ (i.e. 0, 0, 1, 0)
|
||||||
|
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x00, portBOutput: 0xff), [0xff, 0xff, 0xff, 0xff]) // cccc (i.e. 1, 1, 1, 1)
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x40, portBOutput: 0xff), [0xff, 0xff, 0xff, 0xff]) // cccc (i.e. 1, 1, 1, 1)
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x80, portBOutput: 0xff), [0xff, 0xff, 0xff, 0x7f]) // ccca (i.e. 1, 1, 1, 0)
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x00, endACR: 0xc0, portBOutput: 0xff), [0xff, 0xff, 0xff, 0x7f]) // ccca (i.e. 1, 1, 1, 0)
|
||||||
|
|
||||||
|
// Second row. [same output as first row]
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x00, portBOutput: 0x00), [0x00, 0x00, 0x00, 0x00]) // @@@@
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x40, portBOutput: 0x00), [0x00, 0x00, 0x00, 0x00]) // @@@@
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x80, portBOutput: 0x00), [0x00, 0x00, 0x80, 0x00]) // @@b@
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x40, endACR: 0xc0, portBOutput: 0x00), [0x00, 0x00, 0x80, 0x00]) // @@b@
|
||||||
|
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x00, portBOutput: 0xff), [0xff, 0xff, 0xff, 0xff]) // cccc
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x40, portBOutput: 0xff), [0xff, 0xff, 0xff, 0xff]) // cccc
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x80, portBOutput: 0xff), [0xff, 0xff, 0xff, 0x7f]) // ccca
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x40, endACR: 0xc0, portBOutput: 0xff), [0xff, 0xff, 0xff, 0x7f]) // ccca
|
||||||
|
|
||||||
|
// Third row.
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x00, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x00]) // b@@@ (i.e. 1, 0, 0, 0)
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x40, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x00]) // b@@@ (i.e. 1, 0, 0, 0)
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x80, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x80]) // b@@b (i.e. 1, 0, 0, 1)
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x80, endACR: 0xc0, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x80]) // b@@b (i.e. 1, 0, 0, 1)
|
||||||
|
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x00, portBOutput: 0xff), [0xff, 0x7f, 0xff, 0xff]) // cacc (i.e. 1, 0, 1, 1)
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x40, portBOutput: 0xff), [0xff, 0x7f, 0xff, 0xff]) // cacc (i.e. 1, 0, 1, 1)
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x80, portBOutput: 0xff), [0xff, 0x7f, 0x7f, 0xff]) // caac (i.e. 1, 0, 0, 1)
|
||||||
|
XCTAssertEqual(runTest(startACR: 0x80, endACR: 0xc0, portBOutput: 0xff), [0xff, 0x7f, 0x7f, 0xff]) // caac (i.e. 1, 0, 0, 1)
|
||||||
|
|
||||||
|
// Final row. [same output as third row]
|
||||||
|
XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x00, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x00]) // b@@@
|
||||||
|
XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x40, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x00]) // b@@@
|
||||||
|
XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x80, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x80]) // b@@b
|
||||||
|
XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0xc0, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x80]) // b@@b
|
||||||
|
|
||||||
|
XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x00, portBOutput: 0xff), [0xff, 0x7f, 0xff, 0xff]) // cacc
|
||||||
|
XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x40, portBOutput: 0xff), [0xff, 0x7f, 0xff, 0xff]) // cacc
|
||||||
|
XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x80, portBOutput: 0xff), [0xff, 0x7f, 0x7f, 0xff]) // caac
|
||||||
|
XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0xc0, portBOutput: 0xff), [0xff, 0x7f, 0x7f, 0xff]) // caac
|
||||||
|
|
||||||
|
// Conclusions:
|
||||||
|
//
|
||||||
|
// after inital ACR and port B value: [original data if not in PB7 output mode, otherwise 1]
|
||||||
|
// after starting timer 1: [original data if not in PB7 output mode, otherwise 0]
|
||||||
|
// after final ACR value: [original data if not in PB7 output mode, 1 if has transitioned to PB7 mode, 0 if was already in PB7 mode]
|
||||||
|
// after timer 1 expiry: [original data if not in PB7 mode, 1 if timer has expired while in PB7 mode]
|
||||||
|
//
|
||||||
|
// i.e.
|
||||||
|
// (1) there is separate storage for the programmer-set PB7 and the timer output;
|
||||||
|
// (2) the timer output is reset upon a timer write only if PB7 output is enabled;
|
||||||
|
// (3) expiry toggles the output.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Data direction tests
|
// MARK: Data direction tests
|
||||||
func testDataDirection() {
|
func testDataDirection() {
|
||||||
// set low four bits of register B as output, the top four as input
|
// set low four bits of register B as output, the top four as input
|
||||||
|
|||||||
@@ -70,12 +70,9 @@
|
|||||||
|
|
||||||
- (void)testSeekToSecondBit {
|
- (void)testSeekToSecondBit {
|
||||||
Storage::Disk::PCMSegmentEventSource segmentSource = self.segmentSource;
|
Storage::Disk::PCMSegmentEventSource segmentSource = self.segmentSource;
|
||||||
Storage::Time target_time(1, 10);
|
|
||||||
|
|
||||||
Storage::Time found_time = segmentSource.seek_to(target_time);
|
const float found_time = segmentSource.seek_to(1.0f / 10.0f);
|
||||||
found_time.simplify();
|
XCTAssertTrue(fabsf(found_time - 1.0f / 20.0f) < 0.01f, @"A request to seek to 1/10th should have seeked to 1/20th");
|
||||||
|
|
||||||
XCTAssertTrue(found_time.length == 1 && found_time.clock_rate == 20, @"A request to seek to 1/10th should have seeked to 1/20th");
|
|
||||||
|
|
||||||
Storage::Disk::Track::Event next_event = segmentSource.get_next_event();
|
Storage::Disk::Track::Event next_event = segmentSource.get_next_event();
|
||||||
next_event.length.simplify();
|
next_event.length.simplify();
|
||||||
@@ -85,12 +82,9 @@
|
|||||||
|
|
||||||
- (void)testSeekBeyondFinalBit {
|
- (void)testSeekBeyondFinalBit {
|
||||||
Storage::Disk::PCMSegmentEventSource segmentSource = self.segmentSource;
|
Storage::Disk::PCMSegmentEventSource segmentSource = self.segmentSource;
|
||||||
Storage::Time target_time(24, 10);
|
const float found_time = segmentSource.seek_to(2.4f);
|
||||||
|
|
||||||
Storage::Time found_time = segmentSource.seek_to(target_time);
|
XCTAssertTrue(fabsf(found_time - 47.0f / 20.0f) < 0.01f, @"A request to seek to 24/10ths should have seeked to 47/20ths");
|
||||||
found_time.simplify();
|
|
||||||
|
|
||||||
XCTAssertTrue(found_time.length == 47 && found_time.clock_rate == 20, @"A request to seek to 24/10ths should have seeked to 47/20ths");
|
|
||||||
|
|
||||||
Storage::Disk::Track::Event next_event = segmentSource.get_next_event();
|
Storage::Disk::Track::Event next_event = segmentSource.get_next_event();
|
||||||
next_event.length.simplify();
|
next_event.length.simplify();
|
||||||
|
|||||||
@@ -73,16 +73,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
Storage::Disk::PCMTrack track(segments);
|
Storage::Disk::PCMTrack track(segments);
|
||||||
Storage::Time late_time(967445, 2045454);
|
const float late_time = 967445.0f / 2045454.0f;
|
||||||
const auto offset = track.seek_to(late_time);
|
const auto offset = track.seek_to(late_time);
|
||||||
XCTAssert(offset <= late_time, "Found location should be at or before sought time");
|
XCTAssert(offset <= late_time, "Found location should be at or before sought time");
|
||||||
|
|
||||||
const auto difference = late_time - offset;
|
const auto difference = late_time - offset;
|
||||||
const double difference_duration = difference.get<double>();
|
XCTAssert(difference >= 0.0 && difference < 0.005, "Next event should occur soon");
|
||||||
XCTAssert(difference_duration >= 0.0 && difference_duration < 0.005, "Next event should occur soon");
|
|
||||||
|
|
||||||
const double offset_duration = offset.get<double>();
|
XCTAssert(offset >= 0.0 && offset < 0.5, "Next event should occur soon");
|
||||||
XCTAssert(offset_duration >= 0.0 && offset_duration < 0.5, "Next event should occur soon");
|
|
||||||
|
|
||||||
auto next_event = track.get_next_event();
|
auto next_event = track.get_next_event();
|
||||||
double next_event_duration = next_event.length.get<double>();
|
double next_event_duration = next_event.length.get<double>();
|
||||||
|
|||||||
@@ -812,7 +812,7 @@ void MainWindow::setWindowTitle() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mouseIsCaptured) title += " (press control+escape to release mouse)";
|
if(mouseIsCaptured) title += " (press control+escape or F8+F12 to release mouse)";
|
||||||
|
|
||||||
QMainWindow::setWindowTitle(title);
|
QMainWindow::setWindowTitle(title);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,12 +142,25 @@ void ScanTargetWidget::setMouseDelegate(MouseDelegate *delegate) {
|
|||||||
setMouseTracking(delegate);
|
setMouseTracking(delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScanTargetWidget::keyReleaseEvent(QKeyEvent *event) {
|
||||||
|
// Releasing F8 or F12 needs to be tracked but doesn't actively do anything,
|
||||||
|
// so I'm counting that as a Qt ignore.
|
||||||
|
if(event->key() == Qt::Key_F8) f8State = false;
|
||||||
|
if(event->key() == Qt::Key_F12) f12State = false;
|
||||||
|
event->ignore();
|
||||||
|
}
|
||||||
|
|
||||||
void ScanTargetWidget::keyPressEvent(QKeyEvent *event) {
|
void ScanTargetWidget::keyPressEvent(QKeyEvent *event) {
|
||||||
// Use CTRL+Escape to end mouse captured mode, if currently captured; otherwise ignore the event.
|
// Use either CTRL+Escape or F8+F12 to end mouse captured mode, if currently captured;
|
||||||
// Empirical note: control actually appears to mean command on the Mac. I have no idea what the
|
// otherwise ignore the event.
|
||||||
// Mac's command key would actually be as a modifier. Fingers crossed control means control
|
|
||||||
// elsewhere (?).
|
if(event->key() == Qt::Key_F8) f8State = true;
|
||||||
if(mouseIsCaptured && event->key() == Qt::Key_Escape && event->modifiers()&Qt::ControlModifier) {
|
if(event->key() == Qt::Key_F12) f12State = true;
|
||||||
|
|
||||||
|
if(mouseIsCaptured && (
|
||||||
|
(event->key() == Qt::Key_Escape && event->modifiers()&Qt::ControlModifier) ||
|
||||||
|
(f8State && f12State)
|
||||||
|
)) {
|
||||||
releaseMouse();
|
releaseMouse();
|
||||||
|
|
||||||
QCursor cursor;
|
QCursor cursor;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class ScanTargetWidget : public QOpenGLWidget {
|
|||||||
void mouseReleaseEvent(QMouseEvent *) override;
|
void mouseReleaseEvent(QMouseEvent *) override;
|
||||||
void mouseMoveEvent(QMouseEvent *) override;
|
void mouseMoveEvent(QMouseEvent *) override;
|
||||||
void keyPressEvent(QKeyEvent *) override;
|
void keyPressEvent(QKeyEvent *) override;
|
||||||
|
void keyReleaseEvent(QKeyEvent *) override;
|
||||||
|
|
||||||
void releaseMouse();
|
void releaseMouse();
|
||||||
void setMouseButtonPressed(Qt::MouseButton, bool);
|
void setMouseButtonPressed(Qt::MouseButton, bool);
|
||||||
@@ -66,6 +67,7 @@ class ScanTargetWidget : public QOpenGLWidget {
|
|||||||
|
|
||||||
MouseDelegate *mouseDelegate = nullptr;
|
MouseDelegate *mouseDelegate = nullptr;
|
||||||
bool mouseIsCaptured = false;
|
bool mouseIsCaptured = false;
|
||||||
|
bool f8State = false, f12State = false; // To support F8+F12 as a mouse release combination.
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void vsync();
|
void vsync();
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ void CRT::set_new_timing(int cycles_per_line, int height_of_display, Outputs::Di
|
|||||||
// 7 microseconds for horizontal retrace and 500 to 750 microseconds for vertical retrace
|
// 7 microseconds for horizontal retrace and 500 to 750 microseconds for vertical retrace
|
||||||
// in NTSC and PAL TV."
|
// in NTSC and PAL TV."
|
||||||
|
|
||||||
time_multiplier_ = 65535 / cycles_per_line;
|
time_multiplier_ = 63487 / cycles_per_line; // 63475 = 65535 * 31/32, i.e. the same 1/32 error as below is permitted.
|
||||||
phase_denominator_ = int64_t(cycles_per_line) * int64_t(colour_cycle_denominator) * int64_t(time_multiplier_);
|
phase_denominator_ = int64_t(cycles_per_line) * int64_t(colour_cycle_denominator) * int64_t(time_multiplier_);
|
||||||
phase_numerator_ = 0;
|
phase_numerator_ = 0;
|
||||||
colour_cycle_numerator_ = int64_t(colour_cycle_numerator);
|
colour_cycle_numerator_ = int64_t(colour_cycle_numerator);
|
||||||
|
|||||||
Reference in New Issue
Block a user