1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 01:31:42 +00:00

Merge pull request #81 from TomHarte/Underscores

Switches hopefully all C++ classes to postfix underscores
This commit is contained in:
Thomas Harte 2016-12-03 14:19:47 -05:00 committed by GitHub
commit 875e6619cc
51 changed files with 2505 additions and 2412 deletions

View File

@ -57,64 +57,64 @@ template <class T> class MOS6522 {
switch(address)
{
case 0x0:
_registers.output[1] = value;
static_cast<T *>(this)->set_port_output(Port::B, value, _registers.data_direction[1]); // TODO: handshake
registers_.output[1] = value;
static_cast<T *>(this)->set_port_output(Port::B, value, registers_.data_direction[1]); // TODO: handshake
_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();
break;
case 0xf:
case 0x1:
_registers.output[0] = value;
static_cast<T *>(this)->set_port_output(Port::A, value, _registers.data_direction[0]); // TODO: handshake
registers_.output[0] = value;
static_cast<T *>(this)->set_port_output(Port::A, value, registers_.data_direction[0]); // TODO: handshake
_registers.interrupt_flags &= ~(InterruptFlag::CA1ActiveEdge | ((_registers.peripheral_control&0x02) ? 0 : InterruptFlag::CB2ActiveEdge));
registers_.interrupt_flags &= ~(InterruptFlag::CA1ActiveEdge | ((registers_.peripheral_control&0x02) ? 0 : InterruptFlag::CB2ActiveEdge));
reevaluate_interrupts();
break;
// // No handshake, so write directly
// _registers.output[0] = value;
// registers_.output[0] = value;
// static_cast<T *>(this)->set_port_output(0, value);
// break;
case 0x2:
_registers.data_direction[1] = value;
registers_.data_direction[1] = value;
break;
case 0x3:
_registers.data_direction[0] = value;
registers_.data_direction[0] = value;
break;
// Timer 1
case 0x6: case 0x4: _registers.timer_latch[0] = (_registers.timer_latch[0]&0xff00) | value; break;
case 0x6: case 0x4: registers_.timer_latch[0] = (registers_.timer_latch[0]&0xff00) | value; break;
case 0x5: case 0x7:
_registers.timer_latch[0] = (_registers.timer_latch[0]&0x00ff) | (uint16_t)(value << 8);
_registers.interrupt_flags &= ~InterruptFlag::Timer1;
registers_.timer_latch[0] = (registers_.timer_latch[0]&0x00ff) | (uint16_t)(value << 8);
registers_.interrupt_flags &= ~InterruptFlag::Timer1;
if(address == 0x05)
{
_registers.next_timer[0] = _registers.timer_latch[0];
_timer_is_running[0] = true;
registers_.next_timer[0] = registers_.timer_latch[0];
timer_is_running_[0] = true;
}
reevaluate_interrupts();
break;
// Timer 2
case 0x8: _registers.timer_latch[1] = value; break;
case 0x8: registers_.timer_latch[1] = value; break;
case 0x9:
_registers.interrupt_flags &= ~InterruptFlag::Timer2;
_registers.next_timer[1] = _registers.timer_latch[1] | (uint16_t)(value << 8);
_timer_is_running[1] = true;
registers_.interrupt_flags &= ~InterruptFlag::Timer2;
registers_.next_timer[1] = registers_.timer_latch[1] | (uint16_t)(value << 8);
timer_is_running_[1] = true;
reevaluate_interrupts();
break;
// Shift
case 0xa: _registers.shift = value; break;
case 0xa: registers_.shift = value; break;
// Control
case 0xb:
_registers.auxiliary_control = value;
registers_.auxiliary_control = value;
break;
case 0xc:
// printf("Peripheral control %02x\n", value);
_registers.peripheral_control = value;
registers_.peripheral_control = value;
// TODO: simplify below; trying to avoid improper logging of unimplemented warnings in input mode
if(value & 0x08)
@ -139,14 +139,14 @@ template <class T> class MOS6522 {
// Interrupt control
case 0xd:
_registers.interrupt_flags &= ~value;
registers_.interrupt_flags &= ~value;
reevaluate_interrupts();
break;
case 0xe:
if(value&0x80)
_registers.interrupt_enable |= value;
registers_.interrupt_enable |= value;
else
_registers.interrupt_enable &= ~value;
registers_.interrupt_enable &= ~value;
reevaluate_interrupts();
break;
}
@ -160,41 +160,41 @@ template <class T> class MOS6522 {
switch(address)
{
case 0x0:
_registers.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | InterruptFlag::CB2ActiveEdge);
registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | InterruptFlag::CB2ActiveEdge);
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: // TODO: handshake, latching
case 0x1:
_registers.interrupt_flags &= ~(InterruptFlag::CA1ActiveEdge | InterruptFlag::CA2ActiveEdge);
registers_.interrupt_flags &= ~(InterruptFlag::CA1ActiveEdge | InterruptFlag::CA2ActiveEdge);
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 0x3: return _registers.data_direction[0];
case 0x2: return registers_.data_direction[1];
case 0x3: return registers_.data_direction[0];
// Timer 1
case 0x4:
_registers.interrupt_flags &= ~InterruptFlag::Timer1;
registers_.interrupt_flags &= ~InterruptFlag::Timer1;
reevaluate_interrupts();
return _registers.timer[0] & 0x00ff;
case 0x5: return _registers.timer[0] >> 8;
case 0x6: return _registers.timer_latch[0] & 0x00ff;
case 0x7: return _registers.timer_latch[0] >> 8;
return registers_.timer[0] & 0x00ff;
case 0x5: return registers_.timer[0] >> 8;
case 0x6: return registers_.timer_latch[0] & 0x00ff;
case 0x7: return registers_.timer_latch[0] >> 8;
// Timer 2
case 0x8:
_registers.interrupt_flags &= ~InterruptFlag::Timer2;
registers_.interrupt_flags &= ~InterruptFlag::Timer2;
reevaluate_interrupts();
return _registers.timer[1] & 0x00ff;
case 0x9: return _registers.timer[1] >> 8;
return registers_.timer[1] & 0x00ff;
case 0x9: return registers_.timer[1] >> 8;
case 0xa: return _registers.shift;
case 0xa: return registers_.shift;
case 0xb: return _registers.auxiliary_control;
case 0xc: return _registers.peripheral_control;
case 0xb: return registers_.auxiliary_control;
case 0xc: return registers_.peripheral_control;
case 0xd: return _registers.interrupt_flags | (get_interrupt_line() ? 0x80 : 0x00);
case 0xe: return _registers.interrupt_enable | 0x80;
case 0xd: return registers_.interrupt_flags | (get_interrupt_line() ? 0x80 : 0x00);
case 0xe: return registers_.interrupt_enable | 0x80;
}
return 0xff;
@ -205,65 +205,65 @@ template <class T> class MOS6522 {
switch(line)
{
case Line::One:
if( value != _control_inputs[port].line_one &&
value == !!(_registers.peripheral_control & (port ? 0x10 : 0x01))
if( value != control_inputs_[port].line_one &&
value == !!(registers_.peripheral_control & (port ? 0x10 : 0x01))
)
{
_registers.interrupt_flags |= port ? InterruptFlag::CB1ActiveEdge : InterruptFlag::CA1ActiveEdge;
registers_.interrupt_flags |= port ? InterruptFlag::CB1ActiveEdge : InterruptFlag::CA1ActiveEdge;
reevaluate_interrupts();
}
_control_inputs[port].line_one = value;
control_inputs_[port].line_one = value;
break;
case Line::Two:
// TODO: output modes, but probably elsewhere?
if( value != _control_inputs[port].line_two && // i.e. value has changed ...
!(_registers.peripheral_control & (port ? 0x80 : 0x08)) && // ... and line is input ...
value == !!(_registers.peripheral_control & (port ? 0x40 : 0x04)) // ... and it's either high or low, as required
if( value != control_inputs_[port].line_two && // i.e. value has changed ...
!(registers_.peripheral_control & (port ? 0x80 : 0x08)) && // ... and line is input ...
value == !!(registers_.peripheral_control & (port ? 0x40 : 0x04)) // ... and it's either high or low, as required
)
{
_registers.interrupt_flags |= port ? InterruptFlag::CB2ActiveEdge : InterruptFlag::CA2ActiveEdge;
registers_.interrupt_flags |= port ? InterruptFlag::CB2ActiveEdge : InterruptFlag::CA2ActiveEdge;
reevaluate_interrupts();
}
_control_inputs[port].line_two = value;
control_inputs_[port].line_two = value;
break;
}
}
#define phase2() \
_registers.last_timer[0] = _registers.timer[0];\
_registers.last_timer[1] = _registers.timer[1];\
registers_.last_timer[0] = registers_.timer[0];\
registers_.last_timer[1] = registers_.timer[1];\
\
if(_registers.timer_needs_reload)\
if(registers_.timer_needs_reload)\
{\
_registers.timer_needs_reload = false;\
_registers.timer[0] = _registers.timer_latch[0];\
registers_.timer_needs_reload = false;\
registers_.timer[0] = registers_.timer_latch[0];\
}\
else\
_registers.timer[0] --;\
registers_.timer[0] --;\
\
_registers.timer[1] --; \
if(_registers.next_timer[0] >= 0) { _registers.timer[0] = (uint16_t)_registers.next_timer[0]; _registers.next_timer[0] = -1; }\
if(_registers.next_timer[1] >= 0) { _registers.timer[1] = (uint16_t)_registers.next_timer[1]; _registers.next_timer[1] = -1; }\
registers_.timer[1] --; \
if(registers_.next_timer[0] >= 0) { registers_.timer[0] = (uint16_t)registers_.next_timer[0]; registers_.next_timer[0] = -1; }\
if(registers_.next_timer[1] >= 0) { registers_.timer[1] = (uint16_t)registers_.next_timer[1]; registers_.next_timer[1] = -1; }\
// IRQ is raised on the half cycle after overflow
#define phase1() \
if((_registers.timer[1] == 0xffff) && !_registers.last_timer[1] && _timer_is_running[1])\
if((registers_.timer[1] == 0xffff) && !registers_.last_timer[1] && timer_is_running_[1])\
{\
_timer_is_running[1] = false;\
_registers.interrupt_flags |= InterruptFlag::Timer2;\
timer_is_running_[1] = false;\
registers_.interrupt_flags |= InterruptFlag::Timer2;\
reevaluate_interrupts();\
}\
\
if((_registers.timer[0] == 0xffff) && !_registers.last_timer[0] && _timer_is_running[0])\
if((registers_.timer[0] == 0xffff) && !registers_.last_timer[0] && timer_is_running_[0])\
{\
_registers.interrupt_flags |= InterruptFlag::Timer1;\
registers_.interrupt_flags |= InterruptFlag::Timer1;\
reevaluate_interrupts();\
\
if(_registers.auxiliary_control&0x40)\
_registers.timer_needs_reload = true;\
if(registers_.auxiliary_control&0x40)\
registers_.timer_needs_reload = true;\
else\
_timer_is_running[0] = false;\
timer_is_running_[0] = false;\
}
/*!
@ -281,7 +281,7 @@ template <class T> class MOS6522 {
*/
inline void run_for_half_cycles(unsigned int number_of_cycles)
{
if(_is_phase2)
if(is_phase2_)
{
phase2();
number_of_cycles--;
@ -297,11 +297,11 @@ template <class T> class MOS6522 {
if(number_of_cycles)
{
phase1();
_is_phase2 = true;
is_phase2_ = true;
}
else
{
_is_phase2 = false;
is_phase2_ = false;
}
}
@ -326,14 +326,14 @@ template <class T> class MOS6522 {
/*! @returns @c true if the IRQ line is currently active; @c false otherwise. */
inline bool get_interrupt_line()
{
uint8_t interrupt_status = _registers.interrupt_flags & _registers.interrupt_enable & 0x7f;
uint8_t interrupt_status = registers_.interrupt_flags & registers_.interrupt_enable & 0x7f;
return !!interrupt_status;
}
MOS6522() :
_timer_is_running{false, false},
_last_posted_interrupt_status(false),
_is_phase2(false)
timer_is_running_{false, false},
last_posted_interrupt_status_(false),
is_phase2_(false)
{}
private:
@ -351,16 +351,16 @@ template <class T> class MOS6522 {
}
// Phase toggle
bool _is_phase2;
bool is_phase2_;
// Delegate and communications
bool _last_posted_interrupt_status;
bool last_posted_interrupt_status_;
inline void reevaluate_interrupts()
{
bool new_interrupt_status = get_interrupt_line();
if(new_interrupt_status != _last_posted_interrupt_status)
if(new_interrupt_status != last_posted_interrupt_status_)
{
_last_posted_interrupt_status = new_interrupt_status;
last_posted_interrupt_status_ = new_interrupt_status;
static_cast<T *>(this)->set_interrupt_status(new_interrupt_status);
}
}
@ -382,15 +382,15 @@ template <class T> class MOS6522 {
interrupt_flags(0), interrupt_enable(0),
last_timer{0, 0}, timer_needs_reload(false),
next_timer{-1, -1} {}
} _registers;
} registers_;
// control state
struct {
bool line_one, line_two;
} _control_inputs[2];
} control_inputs_[2];
// Internal state other than the registers
bool _timer_is_running[2];
bool timer_is_running_[2];
};
/*!
@ -404,18 +404,18 @@ class MOS6522IRQDelegate {
virtual void mos6522_did_change_interrupt_status(void *mos6522) = 0;
};
void set_interrupt_delegate(Delegate *delegate)
inline void set_interrupt_delegate(Delegate *delegate)
{
_delegate = delegate;
delegate_ = delegate;
}
void set_interrupt_status(bool new_status)
inline void set_interrupt_status(bool new_status)
{
if(_delegate) _delegate->mos6522_did_change_interrupt_status(this);
if(delegate_) delegate_->mos6522_did_change_interrupt_status(this);
}
private:
Delegate *_delegate;
Delegate *delegate_;
};
}

View File

@ -27,8 +27,8 @@ namespace MOS {
*/
template <class T> class MOS6532 {
public:
inline void set_ram(uint16_t address, uint8_t value) { _ram[address&0x7f] = value; }
inline uint8_t get_ram(uint16_t address) { return _ram[address & 0x7f]; }
inline void set_ram(uint16_t address, uint8_t value) { ram_[address&0x7f] = value; }
inline uint8_t get_ram(uint16_t address) { return ram_[address & 0x7f]; }
inline void set_register(int address, uint8_t value)
{
@ -36,13 +36,13 @@ template <class T> class MOS6532 {
switch(decodedAddress) {
// Port output
case 0x00: case 0x02:
_port[decodedAddress / 2].output = value;
static_cast<T *>(this)->set_port_output(decodedAddress / 2, _port[decodedAddress/2].output, _port[decodedAddress / 2].output_mask);
port_[decodedAddress / 2].output = value;
static_cast<T *>(this)->set_port_output(decodedAddress / 2, port_[decodedAddress/2].output, port_[decodedAddress / 2].output_mask);
set_port_did_change(decodedAddress / 2);
break;
case 0x01: case 0x03:
_port[decodedAddress / 2].output_mask = value;
static_cast<T *>(this)->set_port_output(decodedAddress / 2, _port[decodedAddress/2].output, _port[decodedAddress / 2].output_mask);
port_[decodedAddress / 2].output_mask = value;
static_cast<T *>(this)->set_port_output(decodedAddress / 2, port_[decodedAddress/2].output, port_[decodedAddress / 2].output_mask);
set_port_did_change(decodedAddress / 2);
break;
@ -50,16 +50,16 @@ template <class T> class MOS6532 {
case 0x04: case 0x05: case 0x06: case 0x07:
if(address & 0x10)
{
_timer.writtenShift = _timer.activeShift = (decodedAddress - 0x04) * 3 + (decodedAddress / 0x07); // i.e. 0, 3, 6, 10
_timer.value = ((unsigned int)(value) << _timer.activeShift) | ((1 << _timer.activeShift)-1);
_timer.interrupt_enabled = !!(address&0x08);
_interrupt_status &= ~InterruptFlag::Timer;
timer_.writtenShift = timer_.activeShift = (decodedAddress - 0x04) * 3 + (decodedAddress / 0x07); // i.e. 0, 3, 6, 10
timer_.value = ((unsigned int)(value) << timer_.activeShift) | ((1 << timer_.activeShift)-1);
timer_.interrupt_enabled = !!(address&0x08);
interrupt_status_ &= ~InterruptFlag::Timer;
evaluate_interrupts();
}
else
{
_a7_interrupt.enabled = !!(address&0x2);
_a7_interrupt.active_on_positive = !!(address & 0x01);
a7_interrupt_.enabled = !!(address&0x2);
a7_interrupt_.active_on_positive = !!(address & 0x01);
}
break;
}
@ -74,25 +74,25 @@ template <class T> class MOS6532 {
{
const int port = decodedAddress / 2;
uint8_t input = static_cast<T *>(this)->get_port_input(port);
return (input & ~_port[port].output_mask) | (_port[port].output & _port[port].output_mask);
return (input & ~port_[port].output_mask) | (port_[port].output & port_[port].output_mask);
}
break;
case 0x01: case 0x03:
return _port[decodedAddress / 2].output_mask;
return port_[decodedAddress / 2].output_mask;
break;
// Timer and interrupt control
case 0x04: case 0x06:
{
uint8_t value = (uint8_t)(_timer.value >> _timer.activeShift);
_timer.interrupt_enabled = !!(address&0x08);
_interrupt_status &= ~InterruptFlag::Timer;
uint8_t value = (uint8_t)(timer_.value >> timer_.activeShift);
timer_.interrupt_enabled = !!(address&0x08);
interrupt_status_ &= ~InterruptFlag::Timer;
evaluate_interrupts();
if(_timer.activeShift != _timer.writtenShift) {
unsigned int shift = _timer.writtenShift - _timer.activeShift;
_timer.value = (_timer.value << shift) | ((1 << shift) - 1);
_timer.activeShift = _timer.writtenShift;
if(timer_.activeShift != timer_.writtenShift) {
unsigned int shift = timer_.writtenShift - timer_.activeShift;
timer_.value = (timer_.value << shift) | ((1 << shift) - 1);
timer_.activeShift = timer_.writtenShift;
}
return value;
@ -101,8 +101,8 @@ template <class T> class MOS6532 {
case 0x05: case 0x07:
{
uint8_t value = _interrupt_status;
_interrupt_status &= ~InterruptFlag::PA7;
uint8_t value = interrupt_status_;
interrupt_status_ &= ~InterruptFlag::PA7;
evaluate_interrupts();
return value;
}
@ -115,39 +115,39 @@ template <class T> class MOS6532 {
inline void run_for_cycles(unsigned int number_of_cycles)
{
// permit counting _to_ zero; counting _through_ zero initiates the other behaviour
if(_timer.value >= number_of_cycles) {
_timer.value -= number_of_cycles;
if(timer_.value >= number_of_cycles) {
timer_.value -= number_of_cycles;
} else {
number_of_cycles -= _timer.value;
_timer.value = 0x100 - number_of_cycles;
_timer.activeShift = 0;
_interrupt_status |= InterruptFlag::Timer;
number_of_cycles -= timer_.value;
timer_.value = 0x100 - number_of_cycles;
timer_.activeShift = 0;
interrupt_status_ |= InterruptFlag::Timer;
evaluate_interrupts();
}
}
MOS6532() :
_interrupt_status(0),
_port{{.output_mask = 0, .output = 0}, {.output_mask = 0, .output = 0}},
_a7_interrupt({.last_port_value = 0, .enabled = false}),
_interrupt_line(false)
interrupt_status_(0),
port_{{.output_mask = 0, .output = 0}, {.output_mask = 0, .output = 0}},
a7_interrupt_({.last_port_value = 0, .enabled = false}),
interrupt_line_(false)
{}
inline void set_port_did_change(int port)
{
if(!port)
{
uint8_t new_port_a_value = (get_port_input(0) & ~_port[0].output_mask) | (_port[0].output & _port[0].output_mask);
uint8_t difference = new_port_a_value ^ _a7_interrupt.last_port_value;
_a7_interrupt.last_port_value = new_port_a_value;
uint8_t new_port_a_value = (get_port_input(0) & ~port_[0].output_mask) | (port_[0].output & port_[0].output_mask);
uint8_t difference = new_port_a_value ^ a7_interrupt_.last_port_value;
a7_interrupt_.last_port_value = new_port_a_value;
if(difference&0x80)
{
if(
((new_port_a_value&0x80) && _a7_interrupt.active_on_positive) ||
(!(new_port_a_value&0x80) && !_a7_interrupt.active_on_positive)
((new_port_a_value&0x80) && a7_interrupt_.active_on_positive) ||
(!(new_port_a_value&0x80) && !a7_interrupt_.active_on_positive)
)
{
_interrupt_status |= InterruptFlag::PA7;
interrupt_status_ |= InterruptFlag::PA7;
evaluate_interrupts();
}
}
@ -156,34 +156,34 @@ template <class T> class MOS6532 {
inline bool get_inerrupt_line()
{
return _interrupt_line;
return interrupt_line_;
}
private:
uint8_t _ram[128];
uint8_t ram_[128];
struct {
unsigned int value;
unsigned int activeShift, writtenShift;
bool interrupt_enabled;
} _timer;
} timer_;
struct {
bool enabled;
bool active_on_positive;
uint8_t last_port_value;
} _a7_interrupt;
} a7_interrupt_;
struct {
uint8_t output_mask, output;
} _port[2];
} port_[2];
uint8_t _interrupt_status;
uint8_t interrupt_status_;
enum InterruptFlag: uint8_t {
Timer = 0x80,
PA7 = 0x40
};
bool _interrupt_line;
bool interrupt_line_;
// expected to be overridden
uint8_t get_port_input(int port) { return 0xff; }
@ -192,10 +192,10 @@ template <class T> class MOS6532 {
inline void evaluate_interrupts()
{
_interrupt_line =
((_interrupt_status&InterruptFlag::Timer) && _timer.interrupt_enabled) ||
((_interrupt_status&InterruptFlag::PA7) && _a7_interrupt.enabled);
set_irq_line(_interrupt_line);
interrupt_line_ =
((interrupt_status_&InterruptFlag::Timer) && timer_.interrupt_enabled) ||
((interrupt_status_&InterruptFlag::PA7) && a7_interrupt_.enabled);
set_irq_line(interrupt_line_);
}
};

View File

@ -11,23 +11,23 @@
using namespace MOS;
Speaker::Speaker() :
_volume(0),
_control_registers{0, 0, 0, 0},
_shift_registers{0, 0, 0, 0},
_counters{2, 1, 0, 0} // create a slight phase offset for the three channels
volume_(0),
control_registers_{0, 0, 0, 0},
shift_registers_{0, 0, 0, 0},
counters_{2, 1, 0, 0} // create a slight phase offset for the three channels
{}
void Speaker::set_volume(uint8_t volume)
{
enqueue([=]() {
_volume = volume;
volume_ = volume;
});
}
void Speaker::set_control(int channel, uint8_t value)
{
enqueue([=]() {
_control_registers[channel] = value;
control_registers_[channel] = value;
});
}
@ -99,9 +99,9 @@ static uint8_t noise_pattern[] = {
0xf0, 0xe1, 0xe0, 0x78, 0x70, 0x38, 0x3c, 0x3e, 0x1e, 0x3c, 0x1e, 0x1c, 0x70, 0x3c, 0x38, 0x3f,
};
#define shift(r) _shift_registers[r] = (_shift_registers[r] << 1) | (((_shift_registers[r]^0x80)&_control_registers[r]) >> 7)
#define increment(r) _shift_registers[r] = (_shift_registers[r]+1)%8191
#define update(r, m, up) _counters[r]++; if((_counters[r] >> m) == 0x80) { up(r); _counters[r] = (unsigned int)(_control_registers[r]&0x7f) << m; }
#define shift(r) shift_registers_[r] = (shift_registers_[r] << 1) | (((shift_registers_[r]^0x80)&control_registers_[r]) >> 7)
#define increment(r) shift_registers_[r] = (shift_registers_[r]+1)%8191
#define update(r, m, up) counters_[r]++; if((counters_[r] >> m) == 0x80) { up(r); counters_[r] = (unsigned int)(control_registers_[r]&0x7f) << m; }
// Note on slightly askew test: as far as I can make out, if the value in the register is 0x7f then what's supposed to happen
// is that the 0x7f is loaded, on the next clocked cycle the Vic spots a 0x7f, pumps the output, reloads, etc. No increment
// ever occurs. It's conditional. I don't really want two conditionals if I can avoid it so I'm incrementing regardless and
@ -120,11 +120,11 @@ void Speaker::get_samples(unsigned int number_of_samples, int16_t *target)
// this sums the output of all three sounds channels plus a DC offset for volume;
// TODO: what's the real ratio of this stuff?
target[c] = (
(_shift_registers[0]&1) +
(_shift_registers[1]&1) +
(_shift_registers[2]&1) +
((noise_pattern[_shift_registers[3] >> 3] >> (_shift_registers[3]&7))&(_control_registers[3] >> 7)&1)
) * _volume * 700 + _volume * 44;
(shift_registers_[0]&1) +
(shift_registers_[1]&1) +
(shift_registers_[2]&1) +
((noise_pattern[shift_registers_[3] >> 3] >> (shift_registers_[3]&7))&(control_registers_[3] >> 7)&1)
) * volume_ * 700 + volume_ * 44;
}
}

View File

@ -26,10 +26,10 @@ class Speaker: public ::Outputs::Filter<Speaker> {
void skip_samples(unsigned int number_of_samples);
private:
unsigned int _counters[4];
unsigned int _shift_registers[4];
uint8_t _control_registers[4];
uint8_t _volume;
unsigned int counters_[4];
unsigned int shift_registers_[4];
uint8_t control_registers_[4];
uint8_t volume_;
};
/*!
@ -43,15 +43,15 @@ class Speaker: public ::Outputs::Filter<Speaker> {
template <class T> class MOS6560 {
public:
MOS6560() :
_crt(new Outputs::CRT::CRT(65*4, 4, Outputs::CRT::NTSC60, 1)),
_speaker(new Speaker),
_horizontal_counter(0),
_vertical_counter(0),
_cycles_since_speaker_update(0),
_is_odd_frame(false),
_is_odd_line(false)
crt_(new Outputs::CRT::CRT(65*4, 4, Outputs::CRT::NTSC60, 1)),
speaker_(new Speaker),
horizontal_counter_(0),
vertical_counter_(0),
cycles_since_speaker_update_(0),
is_odd_frame_(false),
is_odd_line_(false)
{
_crt->set_composite_sampling_function(
crt_->set_composite_sampling_function(
"float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
"{"
"uint c = texture(texID, coordinate).r;"
@ -69,11 +69,11 @@ template <class T> class MOS6560 {
void set_clock_rate(double clock_rate)
{
_speaker->set_input_rate((float)(clock_rate / 4.0));
speaker_->set_input_rate((float)(clock_rate / 4.0));
}
std::shared_ptr<Outputs::CRT::CRT> get_crt() { return _crt; }
std::shared_ptr<Outputs::Speaker> get_speaker() { return _speaker; }
std::shared_ptr<Outputs::CRT::CRT> get_crt() { return crt_; }
std::shared_ptr<Outputs::Speaker> get_speaker() { return speaker_; }
enum OutputMode {
PAL, NTSC
@ -84,7 +84,7 @@ template <class T> class MOS6560 {
*/
void set_output_mode(OutputMode output_mode)
{
_output_mode = output_mode;
output_mode_ = output_mode;
uint8_t luminances[16] = { // range is 04
0, 4, 1, 3, 2, 2, 1, 3,
2, 1, 2, 1, 2, 3, 2, 3
@ -105,38 +105,38 @@ template <class T> class MOS6560 {
case OutputMode::PAL:
chrominances = pal_chrominances;
display_type = Outputs::CRT::PAL50;
_timing.cycles_per_line = 71;
_timing.line_counter_increment_offset = 0;
_timing.lines_per_progressive_field = 312;
_timing.supports_interlacing = false;
timing_.cycles_per_line = 71;
timing_.line_counter_increment_offset = 0;
timing_.lines_per_progressive_field = 312;
timing_.supports_interlacing = false;
break;
case OutputMode::NTSC:
chrominances = ntsc_chrominances;
display_type = Outputs::CRT::NTSC60;
_timing.cycles_per_line = 65;
_timing.line_counter_increment_offset = 65 - 33; // TODO: this is a bit of a hack; separate vertical and horizontal counting
_timing.lines_per_progressive_field = 261;
_timing.supports_interlacing = true;
timing_.cycles_per_line = 65;
timing_.line_counter_increment_offset = 65 - 33; // TODO: this is a bit of a hack; separate vertical and horizontal counting
timing_.lines_per_progressive_field = 261;
timing_.supports_interlacing = true;
break;
}
_crt->set_new_display_type((unsigned int)(_timing.cycles_per_line*4), display_type);
// _crt->set_visible_area(Outputs::CRT::Rect(0.1f, 0.1f, 0.8f, 0.8f));
crt_->set_new_display_type((unsigned int)(timing_.cycles_per_line*4), display_type);
// crt_->set_visible_area(Outputs::CRT::Rect(0.1f, 0.1f, 0.8f, 0.8f));
// switch(output_mode)
// {
// case OutputMode::PAL:
// _crt->set_visible_area(_crt->get_rect_for_area(16, 237, 15*4, 55*4, 4.0f / 3.0f));
// crt_->set_visible_area(crt_->get_rect_for_area(16, 237, 15*4, 55*4, 4.0f / 3.0f));
// break;
// case OutputMode::NTSC:
// _crt->set_visible_area(_crt->get_rect_for_area(16, 237, 11*4, 55*4, 4.0f / 3.0f));
// crt_->set_visible_area(crt_->get_rect_for_area(16, 237, 11*4, 55*4, 4.0f / 3.0f));
// break;
// }
for(int c = 0; c < 16; c++)
{
_colours[c] = (uint8_t)((luminances[c] << 4) | chrominances[c]);
colours_[c] = (uint8_t)((luminances[c] << 4) | chrominances[c]);
}
}
@ -146,88 +146,88 @@ template <class T> class MOS6560 {
inline void run_for_cycles(unsigned int number_of_cycles)
{
// keep track of the amount of time since the speaker was updated; lazy updates are applied
_cycles_since_speaker_update += number_of_cycles;
cycles_since_speaker_update_ += number_of_cycles;
while(number_of_cycles--)
{
// keep an old copy of the vertical count because that test is a cycle later than the actual changes
int previous_vertical_counter = _vertical_counter;
int previous_vertical_counter = vertical_counter_;
// keep track of internal time relative to this scanline
_horizontal_counter++;
_full_frame_counter++;
if(_horizontal_counter == _timing.cycles_per_line)
horizontal_counter_++;
full_frame_counter_++;
if(horizontal_counter_ == timing_.cycles_per_line)
{
if(_horizontal_drawing_latch)
if(horizontal_drawing_latch_)
{
_current_character_row++;
current_character_row_++;
if(
(_current_character_row == 16) ||
(_current_character_row == 8 && !_registers.tall_characters)
(current_character_row_ == 16) ||
(current_character_row_ == 8 && !registers_.tall_characters)
) {
_current_character_row = 0;
_current_row++;
current_character_row_ = 0;
current_row_++;
}
_pixel_line_cycle = -1;
_columns_this_line = -1;
_column_counter = -1;
pixel_line_cycle_ = -1;
columns_this_line_ = -1;
column_counter_ = -1;
}
_horizontal_counter = 0;
if(_output_mode == OutputMode::PAL) _is_odd_line ^= true;
_horizontal_drawing_latch = false;
horizontal_counter_ = 0;
if(output_mode_ == OutputMode::PAL) is_odd_line_ ^= true;
horizontal_drawing_latch_ = false;
_vertical_counter ++;
if(_vertical_counter == (_registers.interlaced ? (_is_odd_frame ? 262 : 263) : _timing.lines_per_progressive_field))
vertical_counter_ ++;
if(vertical_counter_ == (registers_.interlaced ? (is_odd_frame_ ? 262 : 263) : timing_.lines_per_progressive_field))
{
_vertical_counter = 0;
_full_frame_counter = 0;
vertical_counter_ = 0;
full_frame_counter_ = 0;
if(_output_mode == OutputMode::NTSC) _is_odd_frame ^= true;
_current_row = 0;
_rows_this_field = -1;
_vertical_drawing_latch = false;
_base_video_matrix_address_counter = 0;
_current_character_row = 0;
if(output_mode_ == OutputMode::NTSC) is_odd_frame_ ^= true;
current_row_ = 0;
rows_this_field_ = -1;
vertical_drawing_latch_ = false;
base_video_matrix_address_counter_ = 0;
current_character_row_ = 0;
}
}
// check for vertical starting events
_vertical_drawing_latch |= _registers.first_row_location == (previous_vertical_counter >> 1);
_horizontal_drawing_latch |= _vertical_drawing_latch && (_horizontal_counter == _registers.first_column_location);
vertical_drawing_latch_ |= registers_.first_row_location == (previous_vertical_counter >> 1);
horizontal_drawing_latch_ |= vertical_drawing_latch_ && (horizontal_counter_ == registers_.first_column_location);
if(_pixel_line_cycle >= 0) _pixel_line_cycle++;
switch(_pixel_line_cycle)
if(pixel_line_cycle_ >= 0) pixel_line_cycle_++;
switch(pixel_line_cycle_)
{
case -1:
if(_horizontal_drawing_latch)
if(horizontal_drawing_latch_)
{
_pixel_line_cycle = 0;
_video_matrix_address_counter = _base_video_matrix_address_counter;
pixel_line_cycle_ = 0;
video_matrix_address_counter_ = base_video_matrix_address_counter_;
}
break;
case 1: _columns_this_line = _registers.number_of_columns; break;
case 2: if(_rows_this_field < 0) _rows_this_field = _registers.number_of_rows; break;
case 3: if(_current_row < _rows_this_field) _column_counter = 0; break;
case 1: columns_this_line_ = registers_.number_of_columns; break;
case 2: if(rows_this_field_ < 0) rows_this_field_ = registers_.number_of_rows; break;
case 3: if(current_row_ < rows_this_field_) column_counter_ = 0; break;
}
uint16_t fetch_address = 0x1c;
if(_column_counter >= 0 && _column_counter < _columns_this_line*2)
if(column_counter_ >= 0 && column_counter_ < columns_this_line_*2)
{
if(_column_counter&1)
if(column_counter_&1)
{
fetch_address = _registers.character_cell_start_address + (_character_code*(_registers.tall_characters ? 16 : 8)) + _current_character_row;
fetch_address = registers_.character_cell_start_address + (character_code_*(registers_.tall_characters ? 16 : 8)) + current_character_row_;
}
else
{
fetch_address = (uint16_t)(_registers.video_matrix_start_address + _video_matrix_address_counter);
_video_matrix_address_counter++;
fetch_address = (uint16_t)(registers_.video_matrix_start_address + video_matrix_address_counter_);
video_matrix_address_counter_++;
if(
(_current_character_row == 15) ||
(_current_character_row == 7 && !_registers.tall_characters)
(current_character_row_ == 15) ||
(current_character_row_ == 7 && !registers_.tall_characters)
) {
_base_video_matrix_address_counter = _video_matrix_address_counter;
base_video_matrix_address_counter_ = video_matrix_address_counter_;
}
}
}
@ -242,99 +242,99 @@ template <class T> class MOS6560 {
// divide the byte it is set for 3:1 and then continue as usual.
// determine output state; colour burst and sync timing are currently a guess
if(_horizontal_counter > _timing.cycles_per_line-4) _this_state = State::ColourBurst;
else if(_horizontal_counter > _timing.cycles_per_line-7) _this_state = State::Sync;
if(horizontal_counter_ > timing_.cycles_per_line-4) this_state_ = State::ColourBurst;
else if(horizontal_counter_ > timing_.cycles_per_line-7) this_state_ = State::Sync;
else
{
_this_state = (_column_counter >= 0 && _column_counter < _columns_this_line*2) ? State::Pixels : State::Border;
this_state_ = (column_counter_ >= 0 && column_counter_ < columns_this_line_*2) ? State::Pixels : State::Border;
}
// apply vertical sync
if(
(_vertical_counter < 3 && (_is_odd_frame || !_registers.interlaced)) ||
(_registers.interlaced &&
(vertical_counter_ < 3 && (is_odd_frame_ || !registers_.interlaced)) ||
(registers_.interlaced &&
(
(_vertical_counter == 0 && _horizontal_counter > 32) ||
(_vertical_counter == 1) || (_vertical_counter == 2) ||
(_vertical_counter == 3 && _horizontal_counter <= 32)
(vertical_counter_ == 0 && horizontal_counter_ > 32) ||
(vertical_counter_ == 1) || (vertical_counter_ == 2) ||
(vertical_counter_ == 3 && horizontal_counter_ <= 32)
)
))
_this_state = State::Sync;
this_state_ = State::Sync;
// update the CRT
if(_this_state != _output_state)
if(this_state_ != output_state_)
{
switch(_output_state)
switch(output_state_)
{
case State::Sync: _crt->output_sync(_cycles_in_state * 4); break;
case State::ColourBurst: _crt->output_colour_burst(_cycles_in_state * 4, (_is_odd_frame || _is_odd_line) ? 128 : 0, 0); break;
case State::Border: output_border(_cycles_in_state * 4); break;
case State::Pixels: _crt->output_data(_cycles_in_state * 4, 1); break;
case State::Sync: crt_->output_sync(cycles_in_state_ * 4); break;
case State::ColourBurst: crt_->output_colour_burst(cycles_in_state_ * 4, (is_odd_frame_ || is_odd_line_) ? 128 : 0, 0); break;
case State::Border: output_border(cycles_in_state_ * 4); break;
case State::Pixels: crt_->output_data(cycles_in_state_ * 4, 1); break;
}
_output_state = _this_state;
_cycles_in_state = 0;
output_state_ = this_state_;
cycles_in_state_ = 0;
pixel_pointer = nullptr;
if(_output_state == State::Pixels)
if(output_state_ == State::Pixels)
{
pixel_pointer = _crt->allocate_write_area(260);
pixel_pointer = crt_->allocate_write_area(260);
}
}
_cycles_in_state++;
cycles_in_state_++;
if(_this_state == State::Pixels)
if(this_state_ == State::Pixels)
{
if(_column_counter&1)
if(column_counter_&1)
{
_character_value = pixel_data;
character_value_ = pixel_data;
if(pixel_pointer)
{
uint8_t cell_colour = _colours[_character_colour & 0x7];
if(!(_character_colour&0x8))
uint8_t cell_colour = colours_[character_colour_ & 0x7];
if(!(character_colour_&0x8))
{
uint8_t colours[2];
if(_registers.invertedCells)
if(registers_.invertedCells)
{
colours[0] = cell_colour;
colours[1] = _registers.backgroundColour;
colours[1] = registers_.backgroundColour;
}
else
{
colours[0] = _registers.backgroundColour;
colours[0] = registers_.backgroundColour;
colours[1] = cell_colour;
}
pixel_pointer[0] = colours[(_character_value >> 7)&1];
pixel_pointer[1] = colours[(_character_value >> 6)&1];
pixel_pointer[2] = colours[(_character_value >> 5)&1];
pixel_pointer[3] = colours[(_character_value >> 4)&1];
pixel_pointer[4] = colours[(_character_value >> 3)&1];
pixel_pointer[5] = colours[(_character_value >> 2)&1];
pixel_pointer[6] = colours[(_character_value >> 1)&1];
pixel_pointer[7] = colours[(_character_value >> 0)&1];
pixel_pointer[0] = colours[(character_value_ >> 7)&1];
pixel_pointer[1] = colours[(character_value_ >> 6)&1];
pixel_pointer[2] = colours[(character_value_ >> 5)&1];
pixel_pointer[3] = colours[(character_value_ >> 4)&1];
pixel_pointer[4] = colours[(character_value_ >> 3)&1];
pixel_pointer[5] = colours[(character_value_ >> 2)&1];
pixel_pointer[6] = colours[(character_value_ >> 1)&1];
pixel_pointer[7] = colours[(character_value_ >> 0)&1];
}
else
{
uint8_t colours[4] = {_registers.backgroundColour, _registers.borderColour, cell_colour, _registers.auxiliary_colour};
uint8_t colours[4] = {registers_.backgroundColour, registers_.borderColour, cell_colour, registers_.auxiliary_colour};
pixel_pointer[0] =
pixel_pointer[1] = colours[(_character_value >> 6)&3];
pixel_pointer[1] = colours[(character_value_ >> 6)&3];
pixel_pointer[2] =
pixel_pointer[3] = colours[(_character_value >> 4)&3];
pixel_pointer[3] = colours[(character_value_ >> 4)&3];
pixel_pointer[4] =
pixel_pointer[5] = colours[(_character_value >> 2)&3];
pixel_pointer[5] = colours[(character_value_ >> 2)&3];
pixel_pointer[6] =
pixel_pointer[7] = colours[(_character_value >> 0)&3];
pixel_pointer[7] = colours[(character_value_ >> 0)&3];
}
pixel_pointer += 8;
}
}
else
{
_character_code = pixel_data;
_character_colour = colour_data;
character_code_ = pixel_data;
character_colour_ = colour_data;
}
_column_counter++;
column_counter_++;
}
}
}
@ -350,31 +350,31 @@ template <class T> class MOS6560 {
void set_register(int address, uint8_t value)
{
address &= 0xf;
_registers.direct_values[address] = value;
registers_.direct_values[address] = value;
switch(address)
{
case 0x0:
_registers.interlaced = !!(value&0x80) && _timing.supports_interlacing;
_registers.first_column_location = value & 0x7f;
registers_.interlaced = !!(value&0x80) && timing_.supports_interlacing;
registers_.first_column_location = value & 0x7f;
break;
case 0x1:
_registers.first_row_location = value;
registers_.first_row_location = value;
break;
case 0x2:
_registers.number_of_columns = value & 0x7f;
_registers.video_matrix_start_address = (uint16_t)((_registers.video_matrix_start_address & 0x3c00) | ((value & 0x80) << 2));
registers_.number_of_columns = value & 0x7f;
registers_.video_matrix_start_address = (uint16_t)((registers_.video_matrix_start_address & 0x3c00) | ((value & 0x80) << 2));
break;
case 0x3:
_registers.number_of_rows = (value >> 1)&0x3f;
_registers.tall_characters = !!(value&0x01);
registers_.number_of_rows = (value >> 1)&0x3f;
registers_.tall_characters = !!(value&0x01);
break;
case 0x5:
_registers.character_cell_start_address = (uint16_t)((value & 0x0f) << 10);
_registers.video_matrix_start_address = (uint16_t)((_registers.video_matrix_start_address & 0x0200) | ((value & 0xf0) << 6));
registers_.character_cell_start_address = (uint16_t)((value & 0x0f) << 10);
registers_.video_matrix_start_address = (uint16_t)((registers_.video_matrix_start_address & 0x0200) | ((value & 0xf0) << 6));
break;
case 0xa:
@ -382,26 +382,26 @@ template <class T> class MOS6560 {
case 0xc:
case 0xd:
update_audio();
_speaker->set_control(address - 0xa, value);
speaker_->set_control(address - 0xa, value);
break;
case 0xe:
update_audio();
_registers.auxiliary_colour = _colours[value >> 4];
_speaker->set_volume(value & 0xf);
registers_.auxiliary_colour = colours_[value >> 4];
speaker_->set_volume(value & 0xf);
break;
case 0xf:
{
uint8_t new_border_colour = _colours[value & 0x07];
if(_this_state == State::Border && new_border_colour != _registers.borderColour)
uint8_t new_border_colour = colours_[value & 0x07];
if(this_state_ == State::Border && new_border_colour != registers_.borderColour)
{
output_border(_cycles_in_state * 4);
_cycles_in_state = 0;
output_border(cycles_in_state_ * 4);
cycles_in_state_ = 0;
}
_registers.invertedCells = !((value >> 3)&1);
_registers.borderColour = new_border_colour;
_registers.backgroundColour = _colours[value >> 4];
registers_.invertedCells = !((value >> 3)&1);
registers_.borderColour = new_border_colour;
registers_.backgroundColour = colours_[value >> 4];
}
break;
@ -418,24 +418,24 @@ template <class T> class MOS6560 {
uint8_t get_register(int address)
{
address &= 0xf;
int current_line = (_full_frame_counter + _timing.line_counter_increment_offset) / _timing.cycles_per_line;
int current_line = (full_frame_counter_ + timing_.line_counter_increment_offset) / timing_.cycles_per_line;
switch(address)
{
default: return _registers.direct_values[address];
case 0x03: return (uint8_t)(current_line << 7) | (_registers.direct_values[3] & 0x7f);
default: return registers_.direct_values[address];
case 0x03: return (uint8_t)(current_line << 7) | (registers_.direct_values[3] & 0x7f);
case 0x04: return (current_line >> 1) & 0xff;
}
}
private:
std::shared_ptr<Outputs::CRT::CRT> _crt;
std::shared_ptr<Outputs::CRT::CRT> crt_;
std::shared_ptr<Speaker> _speaker;
unsigned int _cycles_since_speaker_update;
std::shared_ptr<Speaker> speaker_;
unsigned int cycles_since_speaker_update_;
void update_audio()
{
_speaker->run_for_cycles(_cycles_since_speaker_update >> 2);
_cycles_since_speaker_update &= 3;
speaker_->run_for_cycles(cycles_since_speaker_update_ >> 2);
cycles_since_speaker_update_ &= 3;
}
// register state
@ -448,41 +448,41 @@ template <class T> class MOS6560 {
bool invertedCells;
uint8_t direct_values[16];
} _registers;
} registers_;
// output state
enum State {
Sync, ColourBurst, Border, Pixels
} _this_state, _output_state;
unsigned int _cycles_in_state;
} this_state_, output_state_;
unsigned int cycles_in_state_;
// counters that cover an entire field
int _horizontal_counter, _vertical_counter, _full_frame_counter;
int horizontal_counter_, vertical_counter_, full_frame_counter_;
// latches dictating start and length of drawing
bool _vertical_drawing_latch, _horizontal_drawing_latch;
int _rows_this_field, _columns_this_line;
bool vertical_drawing_latch_, horizontal_drawing_latch_;
int rows_this_field_, columns_this_line_;
// current drawing position counter
int _pixel_line_cycle, _column_counter;
int _current_row;
uint16_t _current_character_row;
uint16_t _video_matrix_address_counter, _base_video_matrix_address_counter;
int pixel_line_cycle_, column_counter_;
int current_row_;
uint16_t current_character_row_;
uint16_t video_matrix_address_counter_, base_video_matrix_address_counter_;
// data latched from the bus
uint8_t _character_code, _character_colour, _character_value;
uint8_t character_code_, character_colour_, character_value_;
bool _is_odd_frame, _is_odd_line;
bool is_odd_frame_, is_odd_line_;
// lookup table from 6560 colour index to appropriate PAL/NTSC value
uint8_t _colours[16];
uint8_t colours_[16];
uint8_t *pixel_pointer;
void output_border(unsigned int number_of_cycles)
{
uint8_t *colour_pointer = _crt->allocate_write_area(1);
if(colour_pointer) *colour_pointer = _registers.borderColour;
_crt->output_level(number_of_cycles);
uint8_t *colour_pointer = crt_->allocate_write_area(1);
if(colour_pointer) *colour_pointer = registers_.borderColour;
crt_->output_level(number_of_cycles);
}
struct {
@ -490,8 +490,8 @@ template <class T> class MOS6560 {
int line_counter_increment_offset;
int lines_per_progressive_field;
bool supports_interlacing;
} _timing;
OutputMode _output_mode;
} timing_;
OutputMode output_mode_;
};
}

View File

@ -11,14 +11,14 @@
using namespace GI;
AY38910::AY38910() :
_selected_register(0),
_tone_counters{0, 0, 0}, _tone_periods{0, 0, 0}, _tone_outputs{0, 0, 0},
_noise_shift_register(0xffff), _noise_period(0), _noise_counter(0), _noise_output(0),
_envelope_divider(0), _envelope_period(0), _envelope_position(0),
_master_divider(0),
_output_registers{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
selected_register_(0),
tone_counters_{0, 0, 0}, tone_periods_{0, 0, 0}, tone_outputs_{0, 0, 0},
noise_shift_register_(0xffff), noise_period_(0), noise_counter_(0), noise_output_(0),
envelope_divider_(0), envelope_period_(0), envelope_position_(0),
master_divider_(0),
output_registers_{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
{
_output_registers[8] = _output_registers[9] = _output_registers[10] = 0;
output_registers_[8] = output_registers_[9] = output_registers_[10] = 0;
// set up envelope lookup tables
for(int c = 0; c < 16; c++)
@ -28,39 +28,39 @@ AY38910::AY38910() :
switch(c)
{
case 0: case 1: case 2: case 3: case 9:
_envelope_shapes[c][p] = (p < 16) ? (p^0xf) : 0;
_envelope_overflow_masks[c] = 0x1f;
envelope_shapes_[c][p] = (p < 16) ? (p^0xf) : 0;
envelope_overflow_masks_[c] = 0x1f;
break;
case 4: case 5: case 6: case 7: case 15:
_envelope_shapes[c][p] = (p < 16) ? p : 0;
_envelope_overflow_masks[c] = 0x1f;
envelope_shapes_[c][p] = (p < 16) ? p : 0;
envelope_overflow_masks_[c] = 0x1f;
break;
case 8:
_envelope_shapes[c][p] = (p & 0xf) ^ 0xf;
_envelope_overflow_masks[c] = 0x00;
envelope_shapes_[c][p] = (p & 0xf) ^ 0xf;
envelope_overflow_masks_[c] = 0x00;
break;
case 12:
_envelope_shapes[c][p] = (p & 0xf);
_envelope_overflow_masks[c] = 0x00;
envelope_shapes_[c][p] = (p & 0xf);
envelope_overflow_masks_[c] = 0x00;
break;
case 10:
_envelope_shapes[c][p] = (p & 0xf) ^ ((p < 16) ? 0xf : 0x0);
_envelope_overflow_masks[c] = 0x00;
envelope_shapes_[c][p] = (p & 0xf) ^ ((p < 16) ? 0xf : 0x0);
envelope_overflow_masks_[c] = 0x00;
break;
case 14:
_envelope_shapes[c][p] = (p & 0xf) ^ ((p < 16) ? 0x0 : 0xf);
_envelope_overflow_masks[c] = 0x00;
envelope_shapes_[c][p] = (p & 0xf) ^ ((p < 16) ? 0x0 : 0xf);
envelope_overflow_masks_[c] = 0x00;
break;
case 11:
_envelope_shapes[c][p] = (p < 16) ? (p^0xf) : 0xf;
_envelope_overflow_masks[c] = 0x1f;
envelope_shapes_[c][p] = (p < 16) ? (p^0xf) : 0xf;
envelope_overflow_masks_[c] = 0x1f;
break;
case 13:
_envelope_shapes[c][p] = (p < 16) ? p : 0xf;
_envelope_overflow_masks[c] = 0x1f;
envelope_shapes_[c][p] = (p < 16) ? p : 0xf;
envelope_overflow_masks_[c] = 0x1f;
break;
}
}
@ -71,9 +71,9 @@ AY38910::AY38910() :
float root_two = sqrtf(2.0f);
for(int v = 0; v < 16; v++)
{
_volumes[v] = (int)(max_volume / powf(root_two, (float)(v ^ 0xf)));
volumes_[v] = (int)(max_volume / powf(root_two, (float)(v ^ 0xf)));
}
_volumes[0] = 0;
volumes_[0] = 0;
}
void AY38910::set_clock_rate(double clock_rate)
@ -84,21 +84,21 @@ void AY38910::set_clock_rate(double clock_rate)
void AY38910::get_samples(unsigned int number_of_samples, int16_t *target)
{
int c = 0;
while((_master_divider&15) && c < number_of_samples)
while((master_divider_&15) && c < number_of_samples)
{
target[c] = _output_volume;
_master_divider++;
target[c] = output_volume_;
master_divider_++;
c++;
}
while(c < number_of_samples)
{
#define step_channel(c) \
if(_tone_counters[c]) _tone_counters[c]--;\
if(tone_counters_[c]) tone_counters_[c]--;\
else\
{\
_tone_outputs[c] ^= 1;\
_tone_counters[c] = _tone_periods[c];\
tone_outputs_[c] ^= 1;\
tone_counters_[c] = tone_periods_[c];\
}
// update the tone channels
@ -110,48 +110,48 @@ void AY38910::get_samples(unsigned int number_of_samples, int16_t *target)
// ... the noise generator. This recomputes the new bit repeatedly but harmlessly, only shifting
// it into the official 17 upon divider underflow.
if(_noise_counter) _noise_counter--;
if(noise_counter_) noise_counter_--;
else
{
_noise_counter = _noise_period;
_noise_output ^= _noise_shift_register&1;
_noise_shift_register |= ((_noise_shift_register ^ (_noise_shift_register >> 3))&1) << 17;
_noise_shift_register >>= 1;
noise_counter_ = noise_period_;
noise_output_ ^= noise_shift_register_&1;
noise_shift_register_ |= ((noise_shift_register_ ^ (noise_shift_register_ >> 3))&1) << 17;
noise_shift_register_ >>= 1;
}
// ... and the envelope generator. Table based for pattern lookup, with a 'refill' step — a way of
// implementing non-repeating patterns by locking them to table position 0x1f.
if(_envelope_divider) _envelope_divider--;
if(envelope_divider_) envelope_divider_--;
else
{
_envelope_divider = _envelope_period;
_envelope_position ++;
if(_envelope_position == 32) _envelope_position = _envelope_overflow_masks[_output_registers[13]];
envelope_divider_ = envelope_period_;
envelope_position_ ++;
if(envelope_position_ == 32) envelope_position_ = envelope_overflow_masks_[output_registers_[13]];
}
evaluate_output_volume();
for(int ic = 0; ic < 16 && c < number_of_samples; ic++)
{
target[c] = _output_volume;
target[c] = output_volume_;
c++;
_master_divider++;
master_divider_++;
}
}
_master_divider &= 15;
master_divider_ &= 15;
}
void AY38910::evaluate_output_volume()
{
int envelope_volume = _envelope_shapes[_output_registers[13]][_envelope_position];
int envelope_volume = envelope_shapes_[output_registers_[13]][envelope_position_];
// The output level for a channel is:
// 1 if neither tone nor noise is enabled;
// 0 if either tone or noise is enabled and its value is low.
// The tone/noise enable bits use inverse logic — 0 = on, 1 = off — permitting the OR logic below.
#define tone_level(c, tone_bit) (_tone_outputs[c] | (_output_registers[7] >> tone_bit))
#define noise_level(c, noise_bit) (_noise_output | (_output_registers[7] >> noise_bit))
#define tone_level(c, tone_bit) (tone_outputs_[c] | (output_registers_[7] >> tone_bit))
#define noise_level(c, noise_bit) (noise_output_ | (output_registers_[7] >> noise_bit))
#define level(c, tone_bit, noise_bit) tone_level(c, tone_bit) & noise_level(c, noise_bit) & 1
const int channel_levels[3] = {
@ -163,7 +163,7 @@ void AY38910::evaluate_output_volume()
// Channel volume is a simple selection: if the bit at 0x10 is set, use the envelope volume; otherwise use the lower four bits
#define channel_volume(c) \
((_output_registers[c] >> 4)&1) * envelope_volume + (((_output_registers[c] >> 4)&1)^1) * (_output_registers[c]&0xf)
((output_registers_[c] >> 4)&1) * envelope_volume + (((output_registers_[c] >> 4)&1)^1) * (output_registers_[c]&0xf)
const int volumes[3] = {
channel_volume(8),
@ -173,24 +173,24 @@ void AY38910::evaluate_output_volume()
#undef channel_volume
// Mix additively.
_output_volume = (int16_t)(
_volumes[volumes[0]] * channel_levels[0] +
_volumes[volumes[1]] * channel_levels[1] +
_volumes[volumes[2]] * channel_levels[2]
output_volume_ = (int16_t)(
volumes_[volumes[0]] * channel_levels[0] +
volumes_[volumes[1]] * channel_levels[1] +
volumes_[volumes[2]] * channel_levels[2]
);
}
void AY38910::select_register(uint8_t r)
{
_selected_register = r & 0xf;
selected_register_ = r & 0xf;
}
void AY38910::set_register_value(uint8_t value)
{
_registers[_selected_register] = value;
if(_selected_register < 14)
registers_[selected_register_] = value;
if(selected_register_ < 14)
{
int selected_register = _selected_register;
int selected_register = selected_register_;
enqueue([=] () {
uint8_t masked_value = value;
switch(selected_register)
@ -201,34 +201,34 @@ void AY38910::set_register_value(uint8_t value)
int channel = selected_register >> 1;
if(selected_register & 1)
_tone_periods[channel] = (_tone_periods[channel] & 0xff) | (uint16_t)((value&0xf) << 8);
tone_periods_[channel] = (tone_periods_[channel] & 0xff) | (uint16_t)((value&0xf) << 8);
else
_tone_periods[channel] = (_tone_periods[channel] & ~0xff) | value;
_tone_counters[channel] = _tone_periods[channel];
tone_periods_[channel] = (tone_periods_[channel] & ~0xff) | value;
tone_counters_[channel] = tone_periods_[channel];
}
break;
case 6:
_noise_period = value & 0x1f;
_noise_counter = _noise_period;
noise_period_ = value & 0x1f;
noise_counter_ = noise_period_;
break;
case 11:
_envelope_period = (_envelope_period & ~0xff) | value;
_envelope_divider = _envelope_period;
envelope_period_ = (envelope_period_ & ~0xff) | value;
envelope_divider_ = envelope_period_;
break;
case 12:
_envelope_period = (_envelope_period & 0xff) | (int)(value << 8);
_envelope_divider = _envelope_period;
envelope_period_ = (envelope_period_ & 0xff) | (int)(value << 8);
envelope_divider_ = envelope_period_;
break;
case 13:
masked_value &= 0xf;
_envelope_position = 0;
envelope_position_ = 0;
break;
}
_output_registers[selected_register] = masked_value;
output_registers_[selected_register] = masked_value;
evaluate_output_volume();
});
}
@ -244,22 +244,22 @@ uint8_t AY38910::get_register_value()
0xe0, 0xe0, 0xe0, 0x00, 0x00, 0xf0, 0x00, 0x00
};
return _registers[_selected_register] | register_masks[_selected_register];
return registers_[selected_register_] | register_masks[selected_register_];
}
uint8_t AY38910::get_port_output(bool port_b)
{
return _registers[port_b ? 15 : 14];
return registers_[port_b ? 15 : 14];
}
void AY38910::set_data_input(uint8_t r)
{
_data_input = r;
data_input_ = r;
}
uint8_t AY38910::get_data_output()
{
return _data_output;
return data_output_;
}
void AY38910::set_control_lines(ControlLines control_lines)
@ -277,15 +277,15 @@ void AY38910::set_control_lines(ControlLines control_lines)
case (int)(BCDIR | BC2): new_state = Write; break;
}
if(new_state != _control_state)
if(new_state != control_state_)
{
_control_state = new_state;
control_state_ = new_state;
switch(new_state)
{
default: break;
case LatchAddress: select_register(_data_input); break;
case Write: set_register_value(_data_input); break;
case Read: _data_output = get_register_value(); break;
case LatchAddress: select_register(data_input_); break;
case Write: set_register_value(data_input_); break;
case Read: data_output_ = get_register_value(); break;
}
}
}

View File

@ -51,42 +51,42 @@ class AY38910: public ::Outputs::Filter<AY38910> {
void get_samples(unsigned int number_of_samples, int16_t *target);
private:
int _selected_register;
uint8_t _registers[16], _output_registers[16];
int selected_register_;
uint8_t registers_[16], output_registers_[16];
int _master_divider;
int master_divider_;
int _tone_periods[3];
int _tone_counters[3];
int _tone_outputs[3];
int tone_periods_[3];
int tone_counters_[3];
int tone_outputs_[3];
int _noise_period;
int _noise_counter;
int _noise_shift_register;
int _noise_output;
int noise_period_;
int noise_counter_;
int noise_shift_register_;
int noise_output_;
int _envelope_period;
int _envelope_divider;
int _envelope_position;
int _envelope_shapes[16][32];
int _envelope_overflow_masks[16];
int envelope_period_;
int envelope_divider_;
int envelope_position_;
int envelope_shapes_[16][32];
int envelope_overflow_masks_[16];
int _volumes[16];
int volumes_[16];
enum ControlState {
Inactive,
LatchAddress,
Read,
Write
} _control_state;
} control_state_;
void select_register(uint8_t r);
void set_register_value(uint8_t value);
uint8_t get_register_value();
uint8_t _data_input, _data_output;
uint8_t data_input_, data_output_;
int16_t _output_volume;
int16_t output_volume_;
inline void evaluate_output_volume();
};

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,9 @@
#include <stdint.h>
#include "../../Processors/6502/CPU6502.hpp"
#include "../../Components/6532/6532.hpp"
#include "../CRTMachine.hpp"
#include "PIA.hpp"
#include "Speaker.hpp"
#include "../ConfigurationTarget.hpp"
#include "Atari2600Inputs.h"
@ -23,56 +24,6 @@ namespace Atari2600 {
const unsigned int number_of_upcoming_events = 6;
const unsigned int number_of_recorded_counters = 7;
class Speaker: public ::Outputs::Filter<Speaker> {
public:
Speaker();
~Speaker();
void set_volume(int channel, uint8_t volume);
void set_divider(int channel, uint8_t divider);
void set_control(int channel, uint8_t control);
void get_samples(unsigned int number_of_samples, int16_t *target);
private:
uint8_t _volume[2];
uint8_t _divider[2];
uint8_t _control[2];
int _poly4_counter[2];
int _poly5_counter[2];
int _poly9_counter[2];
int _output_state[2];
int _divider_counter[2];
int _pattern_periods[16];
int _patterns[16][512];
};
class PIA: public MOS::MOS6532<PIA> {
public:
inline uint8_t get_port_input(int port)
{
return _portValues[port];
}
inline void update_port_input(int port, uint8_t mask, bool set)
{
if(set) _portValues[port] &= ~mask; else _portValues[port] |= mask;
set_port_did_change(port);
}
PIA() :
_portValues{0xff, 0xff}
{}
private:
uint8_t _portValues[2];
};
class Machine:
public CPU6502::Processor<Machine>,
public CRTMachine::Machine,
@ -95,26 +46,26 @@ class Machine:
// to satisfy CRTMachine::Machine
virtual void setup_output(float aspect_ratio);
virtual void close_output();
virtual std::shared_ptr<Outputs::CRT::CRT> get_crt() { return _crt; }
virtual std::shared_ptr<Outputs::Speaker> get_speaker() { return _speaker; }
virtual std::shared_ptr<Outputs::CRT::CRT> get_crt() { return crt_; }
virtual std::shared_ptr<Outputs::Speaker> get_speaker() { return speaker_; }
virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); }
// TODO: different rate for PAL
private:
uint8_t *_rom, *_romPages[4];
size_t _rom_size;
uint8_t *rom_, *rom_pages_[4];
size_t rom_size_;
// the RIOT
PIA _mos6532;
PIA mos6532_;
// playfield registers
uint8_t _playfieldControl;
uint8_t _playfieldColour;
uint8_t _backgroundColour;
uint8_t _playfield[41];
uint8_t playfield_control_;
uint8_t playfield_colour_;
uint8_t background_colour_;
uint8_t playfield_[41];
// ... and derivatives
int _ballSize, _missileSize[2];
int ball_size_, missile_size_[2];
// delayed clock events
enum OutputState {
@ -136,12 +87,12 @@ class Machine:
int updates;
OutputState state;
uint8_t playfieldPixel;
uint8_t playfield_pixel;
int counter;
Event() : updates(0), playfieldPixel(0) {}
} _upcomingEvents[number_of_upcoming_events];
unsigned int _upcomingEventsPointer;
Event() : updates(0), playfield_pixel(0) {}
} upcoming_events_[number_of_upcoming_events];
unsigned int upcoming_events_pointer_;
// object counters
struct ObjectCounter {
@ -150,74 +101,73 @@ class Machine:
int broad_pixel; // for sprite objects, a count of cycles since the last counter reset; otherwise unused
ObjectCounter() : count(0), pixel(0), broad_pixel(0) {}
} _objectCounter[number_of_recorded_counters][5];
unsigned int _objectCounterPointer;
} object_counter_[number_of_recorded_counters][5];
unsigned int object_counter_pointer_;
// the latched playfield output
uint8_t _playfieldOutput, _nextPlayfieldOutput;
uint8_t playfield_output_, next_playfield_output_;
// player registers
uint8_t _playerColour[2];
uint8_t _playerReflectionMask[2];
uint8_t _playerGraphics[2][2];
uint8_t _playerGraphicsSelector[2];
bool _playerStart[2];
uint8_t player_colour_[2];
uint8_t player_reflection_mask_[2];
uint8_t player_graphics_[2][2];
uint8_t player_graphics_selector_[2];
// object flags
bool _hasSecondCopy[2];
bool _hasThirdCopy[2];
bool _hasFourthCopy[2];
uint8_t _objectMotion[5]; // the value stored to this counter's motion register
bool has_second_copy_[2];
bool has_third_copy_[2];
bool has_fourth_copy_[2];
uint8_t object_motions_[5]; // the value stored to this counter's motion register
// player + missile registers
uint8_t _playerAndMissileSize[2];
uint8_t player_and_missile_size_[2];
// missile registers
uint8_t _missileGraphicsEnable[2];
bool _missileGraphicsReset[2];
uint8_t missile_graphics_enable_[2];
bool missile_graphics_reset_[2];
// ball registers
uint8_t _ballGraphicsEnable[2];
uint8_t _ballGraphicsSelector;
uint8_t ball_graphics_enable_[2];
uint8_t ball_graphics_selector_;
// graphics output
unsigned int _horizontalTimer;
bool _vSyncEnabled, _vBlankEnabled;
unsigned int horizontal_timer_;
bool vsync_enabled_, vblank_enabled_;
// horizontal motion control
uint8_t _hMoveCounter;
uint8_t _hMoveFlags;
uint8_t hmove_counter_;
uint8_t hmove_flags_;
// joystick state
uint8_t _tiaInputValue[2];
uint8_t tia_input_value_[2];
// collisions
uint8_t _collisions[8];
uint8_t collisions_[8];
void output_pixels(unsigned int count);
uint8_t get_output_pixel();
void update_timers(int mask);
// outputs
std::shared_ptr<Outputs::CRT::CRT> _crt;
std::shared_ptr<Speaker> _speaker;
std::shared_ptr<Outputs::CRT::CRT> crt_;
std::shared_ptr<Speaker> speaker_;
// current mode
bool _is_pal_region;
bool is_pal_region_;
// speaker backlog accumlation counter
unsigned int _cycles_since_speaker_update;
unsigned int cycles_since_speaker_update_;
void update_audio();
// latched output state
unsigned int _lastOutputStateDuration;
OutputState _stateByExtendTime[2][57];
OutputState *_stateByTime;
OutputState _lastOutputState;
uint8_t *_outputBuffer;
unsigned int last_output_state_duration_;
OutputState state_by_extend_time_[2][57];
OutputState *state_by_time_;
OutputState last_output_state_;
uint8_t *output_buffer_;
// lookup table for collision reporting
uint8_t _reportedCollisions[64][8];
uint8_t reported_collisions_[64][8];
void setup_reported_collisions();
};

View File

@ -0,0 +1,40 @@
//
// PIA.h
// Clock Signal
//
// Created by Thomas Harte on 03/12/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_PIA_h
#define Atari2600_PIA_h
#include "../../Components/6532/6532.hpp"
namespace Atari2600 {
class PIA: public MOS::MOS6532<PIA> {
public:
inline uint8_t get_port_input(int port)
{
return port_values_[port];
}
inline void update_port_input(int port, uint8_t mask, bool set)
{
if(set) port_values_[port] &= ~mask; else port_values_[port] |= mask;
set_port_did_change(port);
}
PIA() :
port_values_{0xff, 0xff}
{}
private:
uint8_t port_values_[2];
};
}
#endif /* PIA_h */

View File

@ -0,0 +1,137 @@
//
// Speaker.cpp
// Clock Signal
//
// Created by Thomas Harte on 03/12/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Speaker.hpp"
using namespace Atari2600;
Atari2600::Speaker::Speaker() :
poly4_counter_{0x00f, 0x00f},
poly5_counter_{0x01f, 0x01f},
poly9_counter_{0x1ff, 0x1ff}
{}
void Atari2600::Speaker::set_volume(int channel, uint8_t volume)
{
enqueue([=]() {
volume_[channel] = volume & 0xf;
});
}
void Atari2600::Speaker::set_divider(int channel, uint8_t divider)
{
enqueue([=]() {
divider_[channel] = divider & 0x1f;
divider_counter_[channel] = 0;
});
}
void Atari2600::Speaker::set_control(int channel, uint8_t control)
{
enqueue([=]() {
control_[channel] = control & 0xf;
});
}
#define advance_poly4(c) poly4_counter_[channel] = (poly4_counter_[channel] >> 1) | (((poly4_counter_[channel] << 3) ^ (poly4_counter_[channel] << 2))&0x008)
#define advance_poly5(c) poly5_counter_[channel] = (poly5_counter_[channel] >> 1) | (((poly5_counter_[channel] << 4) ^ (poly5_counter_[channel] << 2))&0x010)
#define advance_poly9(c) poly9_counter_[channel] = (poly9_counter_[channel] >> 1) | (((poly9_counter_[channel] << 4) ^ (poly9_counter_[channel] << 8))&0x100)
void Atari2600::Speaker::get_samples(unsigned int number_of_samples, int16_t *target)
{
for(unsigned int c = 0; c < number_of_samples; c++)
{
target[c] = 0;
for(int channel = 0; channel < 2; channel++)
{
divider_counter_[channel] ++;
int level = 0;
switch(control_[channel])
{
case 0x0: case 0xb: // constant 1
level = 1;
break;
case 0x4: case 0x5: // div2 tone
level = (divider_counter_[channel] / (divider_[channel]+1))&1;
break;
case 0xc: case 0xd: // div6 tone
level = (divider_counter_[channel] / ((divider_[channel]+1)*3))&1;
break;
case 0x6: case 0xa: // div31 tone
level = (divider_counter_[channel] / (divider_[channel]+1))%30 <= 18;
break;
case 0xe: // div93 tone
level = (divider_counter_[channel] / ((divider_[channel]+1)*3))%30 <= 18;
break;
case 0x1: // 4-bit poly
level = poly4_counter_[channel]&1;
if(divider_counter_[channel] == divider_[channel]+1)
{
divider_counter_[channel] = 0;
advance_poly4(channel);
}
break;
case 0x2: // 4-bit poly div31
level = poly4_counter_[channel]&1;
if(divider_counter_[channel]%(30*(divider_[channel]+1)) == 18)
{
advance_poly4(channel);
}
break;
case 0x3: // 5/4-bit poly
level = output_state_[channel];
if(divider_counter_[channel] == divider_[channel]+1)
{
if(poly5_counter_[channel]&1)
{
output_state_[channel] = poly4_counter_[channel]&1;
advance_poly4(channel);
}
advance_poly5(channel);
}
break;
case 0x7: case 0x9: // 5-bit poly
level = poly5_counter_[channel]&1;
if(divider_counter_[channel] == divider_[channel]+1)
{
divider_counter_[channel] = 0;
advance_poly5(channel);
}
break;
case 0xf: // 5-bit poly div6
level = poly5_counter_[channel]&1;
if(divider_counter_[channel] == (divider_[channel]+1)*3)
{
divider_counter_[channel] = 0;
advance_poly5(channel);
}
break;
case 0x8: // 9-bit poly
level = poly9_counter_[channel]&1;
if(divider_counter_[channel] == divider_[channel]+1)
{
divider_counter_[channel] = 0;
advance_poly9(channel);
}
break;
}
target[c] += volume_[channel] * 1024 * level;
}
}
}

View File

@ -0,0 +1,41 @@
//
// Speaker.hpp
// Clock Signal
//
// Created by Thomas Harte on 03/12/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Atari2600_Speaker_hpp
#define Atari2600_Speaker_hpp
#include "../../Outputs/Speaker.hpp"
namespace Atari2600 {
class Speaker: public ::Outputs::Filter<Speaker> {
public:
Speaker();
void set_volume(int channel, uint8_t volume);
void set_divider(int channel, uint8_t divider);
void set_control(int channel, uint8_t control);
void get_samples(unsigned int number_of_samples, int16_t *target);
private:
uint8_t volume_[2];
uint8_t divider_[2];
uint8_t control_[2];
int poly4_counter_[2];
int poly5_counter_[2];
int poly9_counter_[2];
int output_state_[2];
int divider_counter_[2];
};
}
#endif /* Speaker_hpp */

View File

@ -13,21 +13,19 @@
using namespace Commodore::C1540;
Machine::Machine() :
_shift_register(0),
Storage::Disk::Controller(1000000, 4, 300)
shift_register_(0),
Storage::Disk::Controller(1000000, 4, 300),
serial_port_(new SerialPort),
serial_port_VIA_(new SerialPortVIA)
{
// create a serial port and a VIA to run it
_serialPortVIA.reset(new SerialPortVIA);
_serialPort.reset(new SerialPort);
// attach the serial port to its VIA and vice versa
_serialPort->set_serial_port_via(_serialPortVIA);
_serialPortVIA->set_serial_port(_serialPort);
serial_port_->set_serial_port_via(serial_port_VIA_);
serial_port_VIA_->set_serial_port(serial_port_);
// set this instance as the delegate to receive interrupt requests from both VIAs
_serialPortVIA->set_interrupt_delegate(this);
_driveVIA.set_interrupt_delegate(this);
_driveVIA.set_delegate(this);
serial_port_VIA_->set_interrupt_delegate(this);
drive_VIA_.set_interrupt_delegate(this);
drive_VIA_.set_delegate(this);
// set a bit rate
set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(3));
@ -35,27 +33,11 @@ Machine::Machine() :
void Machine::set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bus)
{
Commodore::Serial::AttachPortAndBus(_serialPort, serial_bus);
Commodore::Serial::AttachPortAndBus(serial_port_, serial_bus);
}
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
{
// static bool log = false;
// if(operation == CPU6502::BusOperation::ReadOpcode && (address == 0xF3C0)) log = true;
// if(operation == CPU6502::BusOperation::ReadOpcode && log) printf("%04x\n", address);
// if(operation == CPU6502::BusOperation::ReadOpcode) printf("%04x\n", address);
// if(operation == CPU6502::BusOperation::ReadOpcode && (address >= 0xF510 && address <= 0xF553)) printf("%04x\n", address);
// if(operation == CPU6502::BusOperation::ReadOpcode && (address == 0xE887)) printf("A: %02x\n", get_value_of_register(CPU6502::Register::A));
/* static bool log = false;
if(operation == CPU6502::BusOperation::ReadOpcode)
{
log = (address >= 0xE85B && address <= 0xE907) || (address >= 0xE9C9 && address <= 0xEA2D);
if(log) printf("\n%04x: ", address);
}
if(log) printf("[%c %04x] ", isReadOperation(operation) ? 'r' : 'w', address);*/
/*
Memory map (given that I'm unsure yet on any potential mirroring):
@ -67,39 +49,39 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
if(address < 0x800)
{
if(isReadOperation(operation))
*value = _ram[address];
*value = ram_[address];
else
_ram[address] = *value;
ram_[address] = *value;
}
else if(address >= 0xc000)
{
if(isReadOperation(operation))
*value = _rom[address & 0x3fff];
*value = rom_[address & 0x3fff];
}
else if(address >= 0x1800 && address <= 0x180f)
{
if(isReadOperation(operation))
*value = _serialPortVIA->get_register(address);
*value = serial_port_VIA_->get_register(address);
else
_serialPortVIA->set_register(address, *value);
serial_port_VIA_->set_register(address, *value);
}
else if(address >= 0x1c00 && address <= 0x1c0f)
{
if(isReadOperation(operation))
*value = _driveVIA.get_register(address);
*value = drive_VIA_.get_register(address);
else
_driveVIA.set_register(address, *value);
drive_VIA_.set_register(address, *value);
}
_serialPortVIA->run_for_cycles(1);
_driveVIA.run_for_cycles(1);
serial_port_VIA_->run_for_cycles(1);
drive_VIA_.run_for_cycles(1);
return 1;
}
void Machine::set_rom(const uint8_t *rom)
{
memcpy(_rom, rom, sizeof(_rom));
memcpy(rom_, rom, sizeof(rom_));
}
void Machine::set_disk(std::shared_ptr<Storage::Disk::Disk> disk)
@ -112,8 +94,8 @@ void Machine::set_disk(std::shared_ptr<Storage::Disk::Disk> disk)
void Machine::run_for_cycles(int number_of_cycles)
{
CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles);
set_motor_on(_driveVIA.get_motor_enabled());
if(_driveVIA.get_motor_enabled()) // TODO: motor speed up/down
set_motor_on(drive_VIA_.get_motor_enabled());
if(drive_VIA_.get_motor_enabled()) // TODO: motor speed up/down
Storage::Disk::Controller::run_for_cycles(number_of_cycles);
}
@ -122,29 +104,29 @@ void Machine::run_for_cycles(int number_of_cycles)
void Machine::mos6522_did_change_interrupt_status(void *mos6522)
{
// both VIAs are connected to the IRQ line
set_irq_line(_serialPortVIA->get_interrupt_line() || _driveVIA.get_interrupt_line());
set_irq_line(serial_port_VIA_->get_interrupt_line() || drive_VIA_.get_interrupt_line());
}
#pragma mark - Disk drive
void Machine::process_input_bit(int value, unsigned int cycles_since_index_hole)
{
_shift_register = (_shift_register << 1) | value;
if((_shift_register & 0x3ff) == 0x3ff)
shift_register_ = (shift_register_ << 1) | value;
if((shift_register_ & 0x3ff) == 0x3ff)
{
_driveVIA.set_sync_detected(true);
_bit_window_offset = -1; // i.e. this bit isn't the first within a data window, but the next might be
drive_VIA_.set_sync_detected(true);
bit_window_offset_ = -1; // i.e. this bit isn't the first within a data window, but the next might be
}
else
{
_driveVIA.set_sync_detected(false);
drive_VIA_.set_sync_detected(false);
}
_bit_window_offset++;
if(_bit_window_offset == 8)
bit_window_offset_++;
if(bit_window_offset_ == 8)
{
_driveVIA.set_data_input((uint8_t)_shift_register);
_bit_window_offset = 0;
if(_driveVIA.get_should_set_overflow())
drive_VIA_.set_data_input((uint8_t)shift_register_);
bit_window_offset_ = 0;
if(drive_VIA_.get_should_set_overflow())
{
set_overflow_line(true);
}
@ -171,12 +153,12 @@ void Machine::drive_via_did_set_data_density(void *driveVIA, int density)
#pragma mark - SerialPortVIA
SerialPortVIA::SerialPortVIA() :
_portB(0x00), _attention_acknowledge_level(false), _attention_level_input(true), _data_level_output(false)
port_b_(0x00), attention_acknowledge_level_(false), attention_level_input_(true), data_level_output_(false)
{}
uint8_t SerialPortVIA::get_port_input(Port port)
{
if(port) return _portB;
if(port) return port_b_;
return 0xff;
}
@ -184,10 +166,10 @@ void SerialPortVIA::set_port_output(Port port, uint8_t value, uint8_t mask)
{
if(port)
{
std::shared_ptr<::Commodore::Serial::Port> serialPort = _serialPort.lock();
std::shared_ptr<::Commodore::Serial::Port> serialPort = serial_port_.lock();
if(serialPort) {
_attention_acknowledge_level = !(value&0x10);
_data_level_output = (value&0x02);
attention_acknowledge_level_ = !(value&0x10);
data_level_output_ = (value&0x02);
serialPort->set_output(::Commodore::Serial::Line::Clock, (::Commodore::Serial::LineLevel)!(value&0x08));
update_data_line();
@ -200,31 +182,30 @@ void SerialPortVIA::set_serial_line_state(::Commodore::Serial::Line line, bool v
switch(line)
{
default: break;
case ::Commodore::Serial::Line::Data: _portB = (_portB & ~0x01) | (value ? 0x00 : 0x01); break;
case ::Commodore::Serial::Line::Clock: _portB = (_portB & ~0x04) | (value ? 0x00 : 0x04); break;
case ::Commodore::Serial::Line::Data: port_b_ = (port_b_ & ~0x01) | (value ? 0x00 : 0x01); break;
case ::Commodore::Serial::Line::Clock: port_b_ = (port_b_ & ~0x04) | (value ? 0x00 : 0x04); break;
case ::Commodore::Serial::Line::Attention:
_attention_level_input = !value;
_portB = (_portB & ~0x80) | (value ? 0x00 : 0x80);
attention_level_input_ = !value;
port_b_ = (port_b_ & ~0x80) | (value ? 0x00 : 0x80);
set_control_line_input(Port::A, Line::One, !value);
update_data_line();
break;
}
}
void SerialPortVIA::set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort)
void SerialPortVIA::set_serial_port(const std::shared_ptr<::Commodore::Serial::Port> &serialPort)
{
_serialPort = serialPort;
serial_port_ = serialPort;
}
void SerialPortVIA::update_data_line()
{
std::shared_ptr<::Commodore::Serial::Port> serialPort = _serialPort.lock();
std::shared_ptr<::Commodore::Serial::Port> serialPort = serial_port_.lock();
if(serialPort)
{
// "ATN (Attention) is an input on pin 3 of P2 and P3 that is sensed at PB7 and CA1 of UC3 after being inverted by UA1"
serialPort->set_output(::Commodore::Serial::Line::Data,
(::Commodore::Serial::LineLevel)(!_data_level_output
&& (_attention_level_input != _attention_acknowledge_level)));
(::Commodore::Serial::LineLevel)(!data_level_output_ && (attention_level_input_ != attention_acknowledge_level_)));
}
}
@ -232,35 +213,35 @@ void SerialPortVIA::update_data_line()
void DriveVIA::set_delegate(Delegate *delegate)
{
_delegate = delegate;
delegate_ = delegate;
}
// write protect tab uncovered
DriveVIA::DriveVIA() : _port_b(0xff), _port_a(0xff), _delegate(nullptr) {}
DriveVIA::DriveVIA() : port_b_(0xff), port_a_(0xff), delegate_(nullptr) {}
uint8_t DriveVIA::get_port_input(Port port) {
return port ? _port_b : _port_a;
return port ? port_b_ : port_a_;
}
void DriveVIA::set_sync_detected(bool sync_detected) {
_port_b = (_port_b & 0x7f) | (sync_detected ? 0x00 : 0x80);
port_b_ = (port_b_ & 0x7f) | (sync_detected ? 0x00 : 0x80);
}
void DriveVIA::set_data_input(uint8_t value) {
_port_a = value;
port_a_ = value;
}
bool DriveVIA::get_should_set_overflow() {
return _should_set_overflow;
return should_set_overflow_;
}
bool DriveVIA::get_motor_enabled() {
return _drive_motor;
return drive_motor_;
}
void DriveVIA::set_control_line_output(Port port, Line line, bool value) {
if(port == Port::A && line == Line::Two) {
_should_set_overflow = value;
should_set_overflow_ = value;
}
}
@ -268,36 +249,36 @@ void DriveVIA::set_port_output(Port port, uint8_t value, uint8_t direction_mask)
if(port)
{
// record drive motor state
_drive_motor = !!(value&4);
drive_motor_ = !!(value&4);
// check for a head step
int step_difference = ((value&3) - (_previous_port_b_output&3))&3;
int step_difference = ((value&3) - (previous_port_b_output_&3))&3;
if(step_difference)
{
if(_delegate) _delegate->drive_via_did_step_head(this, (step_difference == 1) ? 1 : -1);
if(delegate_) delegate_->drive_via_did_step_head(this, (step_difference == 1) ? 1 : -1);
}
// check for a change in density
int density_difference = (_previous_port_b_output^value) & (3 << 5);
if(density_difference && _delegate)
int density_difference = (previous_port_b_output_^value) & (3 << 5);
if(density_difference && delegate_)
{
_delegate->drive_via_did_set_data_density(this, (value >> 5)&3);
delegate_->drive_via_did_set_data_density(this, (value >> 5)&3);
}
// TODO: something with the drive LED
// printf("LED: %s\n", value&8 ? "On" : "Off");
_previous_port_b_output = value;
previous_port_b_output_ = value;
}
}
#pragma mark - SerialPort
void SerialPort::set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level) {
std::shared_ptr<SerialPortVIA> serialPortVIA = _serialPortVIA.lock();
std::shared_ptr<SerialPortVIA> serialPortVIA = serial_port_VIA_.lock();
if(serialPortVIA) serialPortVIA->set_serial_line_state(line, (bool)level);
}
void SerialPort::set_serial_port_via(std::shared_ptr<SerialPortVIA> serialPortVIA) {
_serialPortVIA = serialPortVIA;
void SerialPort::set_serial_port_via(const std::shared_ptr<SerialPortVIA> &serialPortVIA) {
serial_port_VIA_ = serialPortVIA;
}

View File

@ -41,17 +41,17 @@ class SerialPortVIA: public MOS::MOS6522<SerialPortVIA>, public MOS::MOS6522IRQD
SerialPortVIA();
uint8_t get_port_input(Port port);
uint8_t get_port_input(Port);
void set_port_output(Port port, uint8_t value, uint8_t mask);
void set_serial_line_state(::Commodore::Serial::Line line, bool value);
void set_port_output(Port, uint8_t value, uint8_t mask);
void set_serial_line_state(::Commodore::Serial::Line, bool);
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort);
void set_serial_port(const std::shared_ptr<::Commodore::Serial::Port> &);
private:
uint8_t _portB;
std::weak_ptr<::Commodore::Serial::Port> _serialPort;
bool _attention_acknowledge_level, _attention_level_input, _data_level_output;
uint8_t port_b_;
std::weak_ptr<::Commodore::Serial::Port> serial_port_;
bool attention_acknowledge_level_, attention_level_input_, data_level_output_;
void update_data_line();
};
@ -79,7 +79,7 @@ class DriveVIA: public MOS::MOS6522<DriveVIA>, public MOS::MOS6522IRQDelegate {
virtual void drive_via_did_step_head(void *driveVIA, int direction) = 0;
virtual void drive_via_did_set_data_density(void *driveVIA, int density) = 0;
};
void set_delegate(Delegate *delegate);
void set_delegate(Delegate *);
using MOS6522IRQDelegate::set_interrupt_status;
@ -87,21 +87,21 @@ class DriveVIA: public MOS::MOS6522<DriveVIA>, public MOS::MOS6522IRQDelegate {
uint8_t get_port_input(Port port);
void set_sync_detected(bool sync_detected);
void set_data_input(uint8_t value);
void set_sync_detected(bool);
void set_data_input(uint8_t);
bool get_should_set_overflow();
bool get_motor_enabled();
void set_control_line_output(Port port, Line line, bool value);
void set_control_line_output(Port, Line, bool value);
void set_port_output(Port port, uint8_t value, uint8_t direction_mask);
void set_port_output(Port, uint8_t value, uint8_t direction_mask);
private:
uint8_t _port_b, _port_a;
bool _should_set_overflow;
bool _drive_motor;
uint8_t _previous_port_b_output;
Delegate *_delegate;
uint8_t port_b_, port_a_;
bool should_set_overflow_;
bool drive_motor_;
uint8_t previous_port_b_output_;
Delegate *delegate_;
};
/*!
@ -109,11 +109,11 @@ class DriveVIA: public MOS::MOS6522<DriveVIA>, public MOS::MOS6522IRQDelegate {
*/
class SerialPort : public ::Commodore::Serial::Port {
public:
void set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level);
void set_serial_port_via(std::shared_ptr<SerialPortVIA> serialPortVIA);
void set_input(::Commodore::Serial::Line, ::Commodore::Serial::LineLevel);
void set_serial_port_via(const std::shared_ptr<SerialPortVIA> &);
private:
std::weak_ptr<SerialPortVIA> _serialPortVIA;
std::weak_ptr<SerialPortVIA> serial_port_VIA_;
};
/*!
@ -152,16 +152,14 @@ class Machine:
void drive_via_did_set_data_density(void *driveVIA, int density);
private:
uint8_t _ram[0x800];
uint8_t _rom[0x4000];
uint8_t ram_[0x800];
uint8_t rom_[0x4000];
std::shared_ptr<SerialPortVIA> _serialPortVIA;
std::shared_ptr<SerialPort> _serialPort;
DriveVIA _driveVIA;
std::shared_ptr<SerialPortVIA> serial_port_VIA_;
std::shared_ptr<SerialPort> serial_port_;
DriveVIA drive_VIA_;
std::shared_ptr<Storage::Disk::Disk> _disk;
int _shift_register, _bit_window_offset;
int shift_register_, bit_window_offset_;
virtual void process_input_bit(int value, unsigned int cycles_since_index_hole);
virtual void process_index_hole();
};

View File

@ -30,14 +30,14 @@ void ::Commodore::Serial::AttachPortAndBus(std::shared_ptr<Port> port, std::shar
void Bus::add_port(std::shared_ptr<Port> port)
{
_ports.push_back(port);
ports_.push_back(port);
for(int line = (int)ServiceRequest; line <= (int)Reset; line++)
{
// the addition of a new device may change the line output...
set_line_output_did_change((Line)line);
// ... but the new device will need to be told the current state regardless
port->set_input((Line)line, _line_levels[line]);
port->set_input((Line)line, line_levels_[line]);
}
}
@ -45,7 +45,7 @@ void Bus::set_line_output_did_change(Line line)
{
// i.e. I believe these lines to be open collector
LineLevel new_line_level = High;
for(std::weak_ptr<Port> port : _ports)
for(std::weak_ptr<Port> port : ports_)
{
std::shared_ptr<Port> locked_port = port.lock();
if(locked_port)
@ -55,11 +55,11 @@ void Bus::set_line_output_did_change(Line line)
}
// post an update only if one occurred
if(new_line_level != _line_levels[line])
if(new_line_level != line_levels_[line])
{
_line_levels[line] = new_line_level;
line_levels_[line] = new_line_level;
for(std::weak_ptr<Port> port : _ports)
for(std::weak_ptr<Port> port : ports_)
{
std::shared_ptr<Port> locked_port = port.lock();
if(locked_port)
@ -74,20 +74,20 @@ void Bus::set_line_output_did_change(Line line)
void DebugPort::set_input(Line line, LineLevel value)
{
_input_levels[line] = value;
input_levels_[line] = value;
printf("[Bus] %s is %s\n", StringForLine(line), value ? "high" : "low");
if(!_incoming_count)
if(!incoming_count_)
{
_incoming_count = (!_input_levels[Line::Clock] && !_input_levels[Line::Data]) ? 8 : 0;
incoming_count_ = (!input_levels_[Line::Clock] && !input_levels_[Line::Data]) ? 8 : 0;
}
else
{
if(line == Line::Clock && value)
{
_incoming_byte = (_incoming_byte >> 1) | (_input_levels[Line::Data] ? 0x80 : 0x00);
incoming_byte_ = (incoming_byte_ >> 1) | (input_levels_[Line::Data] ? 0x80 : 0x00);
}
_incoming_count--;
if(_incoming_count == 0) printf("[Bus] Observed %02x\n", _incoming_byte);
incoming_count_--;
if(incoming_count_ == 0) printf("[Bus] Observed %02x\n", incoming_byte_);
}
}

View File

@ -48,7 +48,7 @@ namespace Serial {
*/
class Bus {
public:
Bus() : _line_levels{High, High, High, High, High} {}
Bus() : line_levels_{High, High, High, High, High} {}
/*!
Adds the supplied port to the bus.
@ -62,8 +62,8 @@ namespace Serial {
void set_line_output_did_change(Line line);
private:
LineLevel _line_levels[5];
std::vector<std::weak_ptr<Port>> _ports;
LineLevel line_levels_[5];
std::vector<std::weak_ptr<Port>> ports_;
};
/*!
@ -72,16 +72,16 @@ namespace Serial {
*/
class Port {
public:
Port() : _line_levels{High, High, High, High, High} {}
Port() : line_levels_{High, High, High, High, High} {}
/*!
Sets the current level of an output line on this serial port.
*/
void set_output(Line line, LineLevel level) {
if(_line_levels[line] != level)
if(line_levels_[line] != level)
{
_line_levels[line] = level;
std::shared_ptr<Bus> bus = _serial_bus.lock();
line_levels_[line] = level;
std::shared_ptr<Bus> bus = serial_bus_.lock();
if(bus) bus->set_line_output_did_change(line);
}
}
@ -90,7 +90,7 @@ namespace Serial {
Gets the previously set level of an output line.
*/
LineLevel get_output(Line line) {
return _line_levels[line];
return line_levels_[line];
}
/*!
@ -102,12 +102,12 @@ namespace Serial {
Sets the supplied serial bus as that to which line levels will be communicated.
*/
inline void set_serial_bus(std::shared_ptr<Bus> serial_bus) {
_serial_bus = serial_bus;
serial_bus_ = serial_bus;
}
private:
std::weak_ptr<Bus> _serial_bus;
LineLevel _line_levels[5];
std::weak_ptr<Bus> serial_bus_;
LineLevel line_levels_[5];
};
/*!
@ -117,12 +117,12 @@ namespace Serial {
public:
void set_input(Line line, LineLevel value);
DebugPort() : _incoming_count(0) {}
DebugPort() : incoming_count_(0) {}
private:
uint8_t _incoming_byte;
int _incoming_count;
LineLevel _input_levels[5];
uint8_t incoming_byte_;
int incoming_count_;
LineLevel input_levels_[5];
};
}

View File

@ -15,28 +15,28 @@
using namespace Commodore::Vic20;
Machine::Machine() :
_rom(nullptr),
_is_running_at_zero_cost(false),
_tape(1022727)
rom_(nullptr),
is_running_at_zero_cost_(false),
tape_(1022727)
{
// create 6522s, serial port and bus
_userPortVIA.reset(new UserPortVIA);
_keyboardVIA.reset(new KeyboardVIA);
_serialPort.reset(new SerialPort);
_serialBus.reset(new ::Commodore::Serial::Bus);
user_port_via_.reset(new UserPortVIA);
keyboard_via_.reset(new KeyboardVIA);
serial_port_.reset(new SerialPort);
serial_bus_.reset(new ::Commodore::Serial::Bus);
// wire up the serial bus and serial port
Commodore::Serial::AttachPortAndBus(_serialPort, _serialBus);
Commodore::Serial::AttachPortAndBus(serial_port_, serial_bus_);
// wire up 6522s and serial port
_userPortVIA->set_serial_port(_serialPort);
_keyboardVIA->set_serial_port(_serialPort);
_serialPort->set_user_port_via(_userPortVIA);
user_port_via_->set_serial_port(serial_port_);
keyboard_via_->set_serial_port(serial_port_);
serial_port_->set_user_port_via(user_port_via_);
// wire up the 6522s, tape and machine
_userPortVIA->set_interrupt_delegate(this);
_keyboardVIA->set_interrupt_delegate(this);
_tape.set_delegate(this);
user_port_via_->set_interrupt_delegate(this);
keyboard_via_->set_interrupt_delegate(this);
tape_.set_delegate(this);
// establish the memory maps
set_memory_size(MemorySize::Default);
@ -44,44 +44,44 @@ Machine::Machine() :
// set the NTSC clock rate
set_region(NTSC);
// _debugPort.reset(new ::Commodore::Serial::DebugPort);
// _debugPort->set_serial_bus(_serialBus);
// _serialBus->add_port(_debugPort);
// _debugPort->set_serial_bus(serial_bus_);
// serial_bus_->add_port(_debugPort);
}
void Machine::set_memory_size(MemorySize size)
{
memset(_processorReadMemoryMap, 0, sizeof(_processorReadMemoryMap));
memset(_processorWriteMemoryMap, 0, sizeof(_processorWriteMemoryMap));
memset(processor_read_memory_map_, 0, sizeof(processor_read_memory_map_));
memset(processor_write_memory_map_, 0, sizeof(processor_write_memory_map_));
switch(size)
{
default: break;
case ThreeKB:
write_to_map(_processorReadMemoryMap, _expansionRAM, 0x0000, 0x1000);
write_to_map(_processorWriteMemoryMap, _expansionRAM, 0x0000, 0x1000);
write_to_map(processor_read_memory_map_, expansion_ram_, 0x0000, 0x1000);
write_to_map(processor_write_memory_map_, expansion_ram_, 0x0000, 0x1000);
break;
case ThirtyTwoKB:
write_to_map(_processorReadMemoryMap, _expansionRAM, 0x0000, 0x8000);
write_to_map(_processorWriteMemoryMap, _expansionRAM, 0x0000, 0x8000);
write_to_map(processor_read_memory_map_, expansion_ram_, 0x0000, 0x8000);
write_to_map(processor_write_memory_map_, expansion_ram_, 0x0000, 0x8000);
break;
}
// install the system ROMs and VIC-visible memory
write_to_map(_processorReadMemoryMap, _userBASICMemory, 0x0000, sizeof(_userBASICMemory));
write_to_map(_processorReadMemoryMap, _screenMemory, 0x1000, sizeof(_screenMemory));
write_to_map(_processorReadMemoryMap, _colorMemory, 0x9400, sizeof(_colorMemory));
write_to_map(_processorReadMemoryMap, _characterROM, 0x8000, sizeof(_characterROM));
write_to_map(_processorReadMemoryMap, _basicROM, 0xc000, sizeof(_basicROM));
write_to_map(_processorReadMemoryMap, _kernelROM, 0xe000, sizeof(_kernelROM));
write_to_map(processor_read_memory_map_, user_basic_memory_, 0x0000, sizeof(user_basic_memory_));
write_to_map(processor_read_memory_map_, screen_memory_, 0x1000, sizeof(screen_memory_));
write_to_map(processor_read_memory_map_, colour_memory_, 0x9400, sizeof(colour_memory_));
write_to_map(processor_read_memory_map_, character_rom_, 0x8000, sizeof(character_rom_));
write_to_map(processor_read_memory_map_, basic_rom_, 0xc000, sizeof(basic_rom_));
write_to_map(processor_read_memory_map_, kernel_rom_, 0xe000, sizeof(kernel_rom_));
write_to_map(_processorWriteMemoryMap, _userBASICMemory, 0x0000, sizeof(_userBASICMemory));
write_to_map(_processorWriteMemoryMap, _screenMemory, 0x1000, sizeof(_screenMemory));
write_to_map(_processorWriteMemoryMap, _colorMemory, 0x9400, sizeof(_colorMemory));
write_to_map(processor_write_memory_map_, user_basic_memory_, 0x0000, sizeof(user_basic_memory_));
write_to_map(processor_write_memory_map_, screen_memory_, 0x1000, sizeof(screen_memory_));
write_to_map(processor_write_memory_map_, colour_memory_, 0x9400, sizeof(colour_memory_));
// install the inserted ROM if there is one
if(_rom)
if(rom_)
{
write_to_map(_processorReadMemoryMap, _rom, _rom_address, _rom_length);
write_to_map(processor_read_memory_map_, rom_, rom_address_, rom_length_);
}
}
@ -99,7 +99,7 @@ void Machine::write_to_map(uint8_t **map, uint8_t *area, uint16_t address, uint1
Machine::~Machine()
{
delete[] _rom;
delete[] rom_;
}
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
@ -117,17 +117,17 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
// }
// run the phase-1 part of this cycle, in which the VIC accesses memory
if(!_is_running_at_zero_cost) _mos6560->run_for_cycles(1);
if(!is_running_at_zero_cost_) mos6560_->run_for_cycles(1);
// run the phase-2 part of the cycle, which is whatever the 6502 said it should be
if(isReadOperation(operation))
{
uint8_t result = _processorReadMemoryMap[address >> 10] ? _processorReadMemoryMap[address >> 10][address & 0x3ff] : 0xff;
uint8_t result = processor_read_memory_map_[address >> 10] ? processor_read_memory_map_[address >> 10][address & 0x3ff] : 0xff;
if((address&0xfc00) == 0x9000)
{
if((address&0xff00) == 0x9000) result &= _mos6560->get_register(address);
if((address&0xfc10) == 0x9010) result &= _userPortVIA->get_register(address);
if((address&0xfc20) == 0x9020) result &= _keyboardVIA->get_register(address);
if((address&0xff00) == 0x9000) result &= mos6560_->get_register(address);
if((address&0xfc10) == 0x9010) result &= user_port_via_->get_register(address);
if((address&0xfc20) == 0x9020) result &= keyboard_via_->get_register(address);
}
*value = result;
@ -135,40 +135,40 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
// PC hits the start of the loop that just waits for an interesting tape interrupt to have
// occurred then skip both 6522s and the tape ahead to the next interrupt without any further
// CPU or 6560 costs.
if(_use_fast_tape_hack && _tape.has_tape() && address == 0xf92f && operation == CPU6502::BusOperation::ReadOpcode)
if(use_fast_tape_hack_ && tape_.has_tape() && address == 0xf92f && operation == CPU6502::BusOperation::ReadOpcode)
{
while(!_userPortVIA->get_interrupt_line() && !_keyboardVIA->get_interrupt_line() && !_tape.get_tape()->is_at_end())
while(!user_port_via_->get_interrupt_line() && !keyboard_via_->get_interrupt_line() && !tape_.get_tape()->is_at_end())
{
_userPortVIA->run_for_cycles(1);
_keyboardVIA->run_for_cycles(1);
_tape.run_for_cycles(1);
user_port_via_->run_for_cycles(1);
keyboard_via_->run_for_cycles(1);
tape_.run_for_cycles(1);
}
}
}
else
{
uint8_t *ram = _processorWriteMemoryMap[address >> 10];
uint8_t *ram = processor_write_memory_map_[address >> 10];
if(ram) ram[address & 0x3ff] = *value;
if((address&0xfc00) == 0x9000)
{
if((address&0xff00) == 0x9000) _mos6560->set_register(address, *value);
if((address&0xfc10) == 0x9010) _userPortVIA->set_register(address, *value);
if((address&0xfc20) == 0x9020) _keyboardVIA->set_register(address, *value);
if((address&0xff00) == 0x9000) mos6560_->set_register(address, *value);
if((address&0xfc10) == 0x9010) user_port_via_->set_register(address, *value);
if((address&0xfc20) == 0x9020) keyboard_via_->set_register(address, *value);
}
}
_userPortVIA->run_for_cycles(1);
_keyboardVIA->run_for_cycles(1);
if(_typer && operation == CPU6502::BusOperation::ReadOpcode && address == 0xEB1E)
user_port_via_->run_for_cycles(1);
keyboard_via_->run_for_cycles(1);
if(typer_ && operation == CPU6502::BusOperation::ReadOpcode && address == 0xEB1E)
{
if(!_typer->type_next_character())
if(!typer_->type_next_character())
{
clear_all_keys();
_typer.reset();
typer_.reset();
}
}
_tape.run_for_cycles(1);
if(_c1540) _c1540->run_for_cycles(1);
tape_.run_for_cycles(1);
if(c1540_) c1540_->run_for_cycles(1);
// If using fast tape then:
// if the PC hits 0xf98e, the ROM's tape loading routine, then begin zero cost processing;
@ -181,19 +181,19 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
// Note the additional test above for PC hitting 0xf92f, which is a loop in the ROM that waits
// for an interesting interrupt. Up there the fast tape hack goes even further in also cutting
// the CPU out of the action.
if(_use_fast_tape_hack && _tape.has_tape())
if(use_fast_tape_hack_ && tape_.has_tape())
{
if(address == 0xf98e && operation == CPU6502::BusOperation::ReadOpcode)
{
_is_running_at_zero_cost = true;
is_running_at_zero_cost_ = true;
set_clock_is_unlimited(true);
}
if(
(address < 0xe000 && operation == CPU6502::BusOperation::ReadOpcode) ||
_tape.get_tape()->is_at_end()
tape_.get_tape()->is_at_end()
)
{
_is_running_at_zero_cost = false;
is_running_at_zero_cost_ = false;
set_clock_is_unlimited(false);
}
}
@ -205,31 +205,31 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
void Machine::mos6522_did_change_interrupt_status(void *mos6522)
{
set_nmi_line(_userPortVIA->get_interrupt_line());
set_irq_line(_keyboardVIA->get_interrupt_line());
set_nmi_line(user_port_via_->get_interrupt_line());
set_irq_line(keyboard_via_->get_interrupt_line());
}
#pragma mark - Setup
void Machine::set_region(Commodore::Vic20::Region region)
{
_region = region;
region_ = region;
switch(region)
{
case PAL:
set_clock_rate(1108404);
if(_mos6560)
if(mos6560_)
{
_mos6560->set_output_mode(MOS::MOS6560<Commodore::Vic20::Vic6560>::OutputMode::PAL);
_mos6560->set_clock_rate(1108404);
mos6560_->set_output_mode(MOS::MOS6560<Commodore::Vic20::Vic6560>::OutputMode::PAL);
mos6560_->set_clock_rate(1108404);
}
break;
case NTSC:
set_clock_rate(1022727);
if(_mos6560)
if(mos6560_)
{
_mos6560->set_output_mode(MOS::MOS6560<Commodore::Vic20::Vic6560>::OutputMode::NTSC);
_mos6560->set_clock_rate(1022727);
mos6560_->set_output_mode(MOS::MOS6560<Commodore::Vic20::Vic6560>::OutputMode::NTSC);
mos6560_->set_clock_rate(1022727);
}
break;
}
@ -237,20 +237,20 @@ void Machine::set_region(Commodore::Vic20::Region region)
void Machine::setup_output(float aspect_ratio)
{
_mos6560.reset(new Vic6560());
_mos6560->get_speaker()->set_high_frequency_cut_off(1600); // There is a 1.6Khz low-pass filter in the Vic-20.
set_region(_region);
mos6560_.reset(new Vic6560());
mos6560_->get_speaker()->set_high_frequency_cut_off(1600); // There is a 1.6Khz low-pass filter in the Vic-20.
set_region(region_);
memset(_mos6560->_videoMemoryMap, 0, sizeof(_mos6560->_videoMemoryMap));
write_to_map(_mos6560->_videoMemoryMap, _characterROM, 0x0000, sizeof(_characterROM));
write_to_map(_mos6560->_videoMemoryMap, _userBASICMemory, 0x2000, sizeof(_userBASICMemory));
write_to_map(_mos6560->_videoMemoryMap, _screenMemory, 0x3000, sizeof(_screenMemory));
_mos6560->_colorMemory = _colorMemory;
memset(mos6560_->video_memory_map, 0, sizeof(mos6560_->video_memory_map));
write_to_map(mos6560_->video_memory_map, character_rom_, 0x0000, sizeof(character_rom_));
write_to_map(mos6560_->video_memory_map, user_basic_memory_, 0x2000, sizeof(user_basic_memory_));
write_to_map(mos6560_->video_memory_map, screen_memory_, 0x3000, sizeof(screen_memory_));
mos6560_->colour_memory = colour_memory_;
}
void Machine::close_output()
{
_mos6560 = nullptr;
mos6560_ = nullptr;
}
void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data)
@ -259,12 +259,12 @@ void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data)
size_t max_length = 0x2000;
switch(slot)
{
case Kernel: target = _kernelROM; break;
case Characters: target = _characterROM; max_length = 0x1000; break;
case BASIC: target = _basicROM; break;
case Kernel: target = kernel_rom_; break;
case Characters: target = character_rom_; max_length = 0x1000; break;
case BASIC: target = basic_rom_; break;
case Drive:
_driveROM.reset(new uint8_t[length]);
memcpy(_driveROM.get(), data, length);
drive_rom_.reset(new uint8_t[length]);
memcpy(drive_rom_.get(), data, length);
install_disk_rom();
return;
}
@ -288,7 +288,7 @@ void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data)
// {
// _rom = new uint8_t[0x2000];
// memcpy(_rom, &data[2], length - 2);
// write_to_map(_processorReadMemoryMap, _rom, _rom_address, 0x2000);
// write_to_map(processor_read_memory_map_, _rom, _rom_address, 0x2000);
// }
// else
// {
@ -303,19 +303,19 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target)
{
if(target.tapes.size())
{
_tape.set_tape(target.tapes.front());
tape_.set_tape(target.tapes.front());
}
if(target.disks.size())
{
// construct the 1540
_c1540.reset(new ::Commodore::C1540::Machine);
c1540_.reset(new ::Commodore::C1540::Machine);
// attach it to the serial bus
_c1540->set_serial_bus(_serialBus);
c1540_->set_serial_bus(serial_bus_);
// hand it the disk
_c1540->set_disk(target.disks.front());
c1540_->set_disk(target.disks.front());
// install the ROM if it was previously set
install_disk_rom();
@ -323,16 +323,16 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target)
if(target.cartridges.size())
{
_rom_address = 0xa000;
rom_address_ = 0xa000;
std::vector<uint8_t> rom_image = target.cartridges.front()->get_segments().front().data;
_rom_length = (uint16_t)(rom_image.size());
rom_length_ = (uint16_t)(rom_image.size());
_rom = new uint8_t[0x2000];
memcpy(_rom, rom_image.data(), rom_image.size());
write_to_map(_processorReadMemoryMap, _rom, _rom_address, 0x2000);
rom_ = new uint8_t[0x2000];
memcpy(rom_, rom_image.data(), rom_image.size());
write_to_map(processor_read_memory_map_, rom_, rom_address_, 0x2000);
}
if(_should_automatically_load_media)
if(should_automatically_load_media_)
{
if(target.loadingCommand.length()) // TODO: and automatic loading option enabled
{
@ -356,18 +356,18 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target)
void Machine::tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape)
{
_keyboardVIA->set_control_line_input(KeyboardVIA::Port::A, KeyboardVIA::Line::One, tape->get_input());
keyboard_via_->set_control_line_input(KeyboardVIA::Port::A, KeyboardVIA::Line::One, tape->get_input());
}
#pragma mark - Disc
void Machine::install_disk_rom()
{
if(_driveROM && _c1540)
if(drive_rom_ && c1540_)
{
_c1540->set_rom(_driveROM.get());
_c1540->run_for_cycles(2000000);
_driveROM.reset();
c1540_->set_rom(drive_rom_.get());
c1540_->run_for_cycles(2000000);
drive_rom_.reset();
}
}
@ -377,7 +377,7 @@ uint8_t UserPortVIA::get_port_input(Port port)
{
if(!port)
{
return _portA; // TODO: bit 6 should be high if there is no tape, low otherwise
return port_a_; // TODO: bit 6 should be high if there is no tape, low otherwise
}
return 0xff;
}
@ -394,8 +394,8 @@ void UserPortVIA::set_serial_line_state(::Commodore::Serial::Line line, bool val
switch(line)
{
default: break;
case ::Commodore::Serial::Line::Data: _portA = (_portA & ~0x02) | (value ? 0x02 : 0x00); break;
case ::Commodore::Serial::Line::Clock: _portA = (_portA & ~0x01) | (value ? 0x01 : 0x00); break;
case ::Commodore::Serial::Line::Data: port_a_ = (port_a_ & ~0x02) | (value ? 0x02 : 0x00); break;
case ::Commodore::Serial::Line::Clock: port_a_ = (port_a_ & ~0x01) | (value ? 0x01 : 0x00); break;
}
}
@ -403,7 +403,7 @@ void UserPortVIA::set_joystick_state(JoystickInput input, bool value)
{
if(input != JoystickInput::Right)
{
_portA = (_portA & ~input) | (value ? 0 : input);
port_a_ = (port_a_ & ~input) | (value ? 0 : input);
}
}
@ -412,22 +412,22 @@ void UserPortVIA::set_port_output(Port port, uint8_t value, uint8_t mask)
// Line 7 of port A is inverted and output as serial ATN
if(!port)
{
std::shared_ptr<::Commodore::Serial::Port> serialPort = _serialPort.lock();
std::shared_ptr<::Commodore::Serial::Port> serialPort = serial_port_.lock();
if(serialPort)
serialPort->set_output(::Commodore::Serial::Line::Attention, (::Commodore::Serial::LineLevel)!(value&0x80));
}
}
UserPortVIA::UserPortVIA() : _portA(0xbf) {}
UserPortVIA::UserPortVIA() : port_a_(0xbf) {}
void UserPortVIA::set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort)
{
_serialPort = serialPort;
serial_port_ = serialPort;
}
#pragma mark - KeyboardVIA
KeyboardVIA::KeyboardVIA() : _portB(0xff)
KeyboardVIA::KeyboardVIA() : port_b_(0xff)
{
clear_all_keys();
}
@ -435,14 +435,14 @@ KeyboardVIA::KeyboardVIA() : _portB(0xff)
void KeyboardVIA::set_key_state(uint16_t key, bool isPressed)
{
if(isPressed)
_columns[key & 7] &= ~(key >> 3);
columns_[key & 7] &= ~(key >> 3);
else
_columns[key & 7] |= (key >> 3);
columns_[key & 7] |= (key >> 3);
}
void KeyboardVIA::clear_all_keys()
{
memset(_columns, 0xff, sizeof(_columns));
memset(columns_, 0xff, sizeof(columns_));
}
uint8_t KeyboardVIA::get_port_input(Port port)
@ -452,26 +452,26 @@ uint8_t KeyboardVIA::get_port_input(Port port)
uint8_t result = 0xff;
for(int c = 0; c < 8; c++)
{
if(!(_activation_mask&(1 << c)))
result &= _columns[c];
if(!(activation_mask_&(1 << c)))
result &= columns_[c];
}
return result;
}
return _portB;
return port_b_;
}
void KeyboardVIA::set_port_output(Port port, uint8_t value, uint8_t mask)
{
if(port)
_activation_mask = (value & mask) | (~mask);
activation_mask_ = (value & mask) | (~mask);
}
void KeyboardVIA::set_control_line_output(Port port, Line line, bool value)
{
if(line == Line::Two)
{
std::shared_ptr<::Commodore::Serial::Port> serialPort = _serialPort.lock();
std::shared_ptr<::Commodore::Serial::Port> serialPort = serial_port_.lock();
if(serialPort)
{
// CB2 is inverted to become serial data; CA2 is inverted to become serial clock
@ -487,24 +487,24 @@ void KeyboardVIA::set_joystick_state(JoystickInput input, bool value)
{
if(input == JoystickInput::Right)
{
_portB = (_portB & ~input) | (value ? 0 : input);
port_b_ = (port_b_ & ~input) | (value ? 0 : input);
}
}
void KeyboardVIA::set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort)
{
_serialPort = serialPort;
serial_port_ = serialPort;
}
#pragma mark - SerialPort
void SerialPort::set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level)
{
std::shared_ptr<UserPortVIA> userPortVIA = _userPortVIA.lock();
std::shared_ptr<UserPortVIA> userPortVIA = user_port_via_.lock();
if(userPortVIA) userPortVIA->set_serial_line_state(line, (bool)level);
}
void SerialPort::set_user_port_via(std::shared_ptr<UserPortVIA> userPortVIA)
{
_userPortVIA = userPortVIA;
user_port_via_ = userPortVIA;
}

View File

@ -89,8 +89,8 @@ class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDeleg
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort);
private:
uint8_t _portA;
std::weak_ptr<::Commodore::Serial::Port> _serialPort;
uint8_t port_a_;
std::weak_ptr<::Commodore::Serial::Port> serial_port_;
};
class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDelegate {
@ -112,10 +112,10 @@ class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDeleg
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort);
private:
uint8_t _portB;
uint8_t _columns[8];
uint8_t _activation_mask;
std::weak_ptr<::Commodore::Serial::Port> _serialPort;
uint8_t port_b_;
uint8_t columns_[8];
uint8_t activation_mask_;
std::weak_ptr<::Commodore::Serial::Port> serial_port_;
};
class SerialPort : public ::Commodore::Serial::Port {
@ -124,19 +124,19 @@ class SerialPort : public ::Commodore::Serial::Port {
void set_user_port_via(std::shared_ptr<UserPortVIA> userPortVIA);
private:
std::weak_ptr<UserPortVIA> _userPortVIA;
std::weak_ptr<UserPortVIA> user_port_via_;
};
class Vic6560: public MOS::MOS6560<Vic6560> {
public:
inline void perform_read(uint16_t address, uint8_t *pixel_data, uint8_t *colour_data)
{
*pixel_data = _videoMemoryMap[address >> 10] ? _videoMemoryMap[address >> 10][address & 0x3ff] : 0xff; // TODO
*colour_data = _colorMemory[address & 0x03ff];
*pixel_data = video_memory_map[address >> 10] ? video_memory_map[address >> 10][address & 0x3ff] : 0xff; // TODO
*colour_data = colour_memory[address & 0x03ff];
}
uint8_t *_videoMemoryMap[16];
uint8_t *_colorMemory;
uint8_t *video_memory_map[16];
uint8_t *colour_memory;
};
class Machine:
@ -153,34 +153,30 @@ class Machine:
void set_rom(ROMSlot slot, size_t length, const uint8_t *data);
void configure_as_target(const StaticAnalyser::Target &target);
// void set_prg(const char *file_name, size_t length, const uint8_t *data);
// void set_tape(std::shared_ptr<Storage::Tape::Tape> tape);
// void set_disk(std::shared_ptr<Storage::Disk::Disk> disk);
void set_key_state(uint16_t key, bool isPressed) { _keyboardVIA->set_key_state(key, isPressed); }
void clear_all_keys() { _keyboardVIA->clear_all_keys(); }
void set_key_state(uint16_t key, bool isPressed) { keyboard_via_->set_key_state(key, isPressed); }
void clear_all_keys() { keyboard_via_->clear_all_keys(); }
void set_joystick_state(JoystickInput input, bool isPressed) {
_userPortVIA->set_joystick_state(input, isPressed);
_keyboardVIA->set_joystick_state(input, isPressed);
user_port_via_->set_joystick_state(input, isPressed);
keyboard_via_->set_joystick_state(input, isPressed);
}
void set_memory_size(MemorySize size);
void set_region(Region region);
inline void set_use_fast_tape_hack(bool activate) { _use_fast_tape_hack = activate; }
inline void set_should_automatically_load_media(bool activate) { _should_automatically_load_media = activate; }
inline void set_use_fast_tape_hack(bool activate) { use_fast_tape_hack_ = activate; }
inline void set_should_automatically_load_media(bool activate) { should_automatically_load_media_ = activate; }
// to satisfy CPU6502::Processor
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);
void synchronise() { _mos6560->synchronise(); }
void synchronise() { mos6560_->synchronise(); }
// to satisfy CRTMachine::Machine
virtual void setup_output(float aspect_ratio);
virtual void close_output();
virtual std::shared_ptr<Outputs::CRT::CRT> get_crt() { return _mos6560->get_crt(); }
virtual std::shared_ptr<Outputs::Speaker> get_speaker() { return _mos6560->get_speaker(); }
virtual std::shared_ptr<Outputs::CRT::CRT> get_crt() { return mos6560_->get_crt(); }
virtual std::shared_ptr<Outputs::Speaker> get_speaker() { return mos6560_->get_speaker(); }
virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); }
// TODO: or 1108405 for PAL; see http://www.antimon.org/dl/c64/code/stable.txt
// to satisfy MOS::MOS6522::Delegate
virtual void mos6522_did_change_interrupt_status(void *mos6522);
@ -192,40 +188,38 @@ class Machine:
virtual void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape);
private:
uint8_t _characterROM[0x1000];
uint8_t _basicROM[0x2000];
uint8_t _kernelROM[0x2000];
uint8_t _expansionRAM[0x8000];
uint8_t character_rom_[0x1000];
uint8_t basic_rom_[0x2000];
uint8_t kernel_rom_[0x2000];
uint8_t expansion_ram_[0x8000];
uint8_t *_rom;
uint16_t _rom_address, _rom_length;
uint8_t *rom_;
uint16_t rom_address_, rom_length_;
uint8_t _userBASICMemory[0x0400];
uint8_t _screenMemory[0x1000];
uint8_t _colorMemory[0x0400];
uint8_t _junkMemory[0x0400];
std::unique_ptr<uint8_t> _driveROM;
uint8_t user_basic_memory_[0x0400];
uint8_t screen_memory_[0x1000];
uint8_t colour_memory_[0x0400];
std::unique_ptr<uint8_t> drive_rom_;
uint8_t *_processorReadMemoryMap[64];
uint8_t *_processorWriteMemoryMap[64];
uint8_t *processor_read_memory_map_[64];
uint8_t *processor_write_memory_map_[64];
void write_to_map(uint8_t **map, uint8_t *area, uint16_t address, uint16_t length);
Region _region;
Region region_;
std::unique_ptr<Vic6560> _mos6560;
std::shared_ptr<UserPortVIA> _userPortVIA;
std::shared_ptr<KeyboardVIA> _keyboardVIA;
std::shared_ptr<SerialPort> _serialPort;
std::shared_ptr<::Commodore::Serial::Bus> _serialBus;
// std::shared_ptr<::Commodore::Serial::DebugPort> _debugPort;
std::unique_ptr<Vic6560> mos6560_;
std::shared_ptr<UserPortVIA> user_port_via_;
std::shared_ptr<KeyboardVIA> keyboard_via_;
std::shared_ptr<SerialPort> serial_port_;
std::shared_ptr<::Commodore::Serial::Bus> serial_bus_;
// Tape
Storage::Tape::BinaryTapePlayer _tape;
bool _use_fast_tape_hack, _should_automatically_load_media;
bool _is_running_at_zero_cost;
Storage::Tape::BinaryTapePlayer tape_;
bool use_fast_tape_hack_, should_automatically_load_media_;
bool is_running_at_zero_cost_;
// Disk
std::shared_ptr<::Commodore::C1540::Machine> _c1540;
std::shared_ptr<::Commodore::C1540::Machine> c1540_;
void install_disk_rom();
// Autoload string

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,9 @@
#include "../CRTMachine.hpp"
#include "../Typer.hpp"
#include "Plus3.hpp"
#include "Speaker.hpp"
#include "Tape.hpp"
#include "Interrupts.hpp"
#include <cstdint>
#include <vector>
@ -35,15 +38,6 @@ enum ROMSlot: uint8_t {
ROMSlotOS, ROMSlotDFS, ROMSlotADFS
};
enum Interrupt: uint8_t {
PowerOnReset = 0x02,
DisplayEnd = 0x04,
RealTimeClock = 0x08,
ReceiveDataFull = 0x10,
TransmitDataEmpty = 0x20,
HighToneDetect = 0x40
};
enum Key: uint16_t {
KeySpace = 0x0000 | 0x08, KeyCopy = 0x0000 | 0x02, KeyRight = 0x0000 | 0x01,
KeyDelete = 0x0010 | 0x08, KeyReturn = 0x0010 | 0x04, KeyDown = 0x0010 | 0x02, KeyLeft = 0x0010 | 0x01,
@ -65,72 +59,6 @@ enum Key: uint16_t {
TerminateSequence = 0xffff, NotMapped = 0xfffe,
};
class Tape: public Storage::Tape::TapePlayer {
public:
Tape();
inline void run_for_cycles(unsigned int number_of_cycles);
inline uint8_t get_data_register();
inline void set_data_register(uint8_t value);
inline void set_counter(uint8_t value);
inline uint8_t get_interrupt_status() { return _interrupt_status; }
inline void clear_interrupts(uint8_t interrupts);
class Delegate {
public:
virtual void tape_did_change_interrupt_status(Tape *tape) = 0;
};
inline void set_delegate(Delegate *delegate) { _delegate = delegate; }
inline void set_is_running(bool is_running) { _is_running = is_running; }
inline void set_is_enabled(bool is_enabled) { _is_enabled = is_enabled; }
inline void set_is_in_input_mode(bool is_in_input_mode);
private:
void process_input_pulse(Storage::Tape::Tape::Pulse pulse);
inline void push_tape_bit(uint16_t bit);
inline void get_next_tape_pulse();
struct {
int minimum_bits_until_full;
} _input;
struct {
unsigned int cycles_into_pulse;
unsigned int bits_remaining_until_empty;
} _output;
bool _is_running;
bool _is_enabled;
bool _is_in_input_mode;
inline void evaluate_interrupts();
uint16_t _data_register;
uint8_t _interrupt_status, _last_posted_interrupt_status;
Delegate *_delegate;
enum {
Long, Short, Unrecognised, Recognised
} _crossings[4];
};
class Speaker: public ::Outputs::Filter<Speaker> {
public:
void set_divider(uint8_t divider);
void set_is_enabled(bool is_enabled);
void get_samples(unsigned int number_of_samples, int16_t *target);
void skip_samples(unsigned int number_of_samples);
private:
unsigned int _counter;
unsigned int _divider;
bool _is_enabled;
};
/*!
@abstract Represents an Acorn Electron.
@ -152,7 +80,7 @@ class Machine:
void set_key_state(uint16_t key, bool isPressed);
void clear_all_keys();
inline void set_use_fast_tape_hack(bool activate) { _use_fast_tape_hack = activate; }
inline void set_use_fast_tape_hack(bool activate) { use_fast_tape_hack_ = activate; }
// to satisfy ConfigurationTarget::Machine
void configure_as_target(const StaticAnalyser::Target &target);
@ -164,8 +92,8 @@ class Machine:
// to satisfy CRTMachine::Machine
virtual void setup_output(float aspect_ratio);
virtual void close_output();
virtual std::shared_ptr<Outputs::CRT::CRT> get_crt() { return _crt; }
virtual std::shared_ptr<Outputs::Speaker> get_speaker() { return _speaker; }
virtual std::shared_ptr<Outputs::CRT::CRT> get_crt() { return crt_; }
virtual std::shared_ptr<Outputs::Speaker> get_speaker() { return speaker_; }
virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); }
// to satisfy Tape::Delegate
@ -189,25 +117,25 @@ class Machine:
inline void evaluate_interrupts();
// Things that directly constitute the memory map.
uint8_t _roms[16][16384];
bool _rom_write_masks[16];
uint8_t _os[16384], _ram[32768];
std::vector<uint8_t> _dfs, _adfs;
uint8_t roms_[16][16384];
bool rom_write_masks_[16];
uint8_t os_[16384], ram_[32768];
std::vector<uint8_t> dfs_, adfs_;
// Things affected by registers, explicitly or otherwise.
uint8_t _interrupt_status, _interrupt_control;
uint8_t _palette[16];
uint8_t _key_states[14];
ROMSlot _active_rom;
bool _keyboard_is_active, _basic_is_active;
uint8_t _screen_mode;
uint16_t _screenModeBaseAddress;
uint16_t _startScreenAddress;
uint8_t interrupt_status_, interrupt_control_;
uint8_t palette_[16];
uint8_t key_states_[14];
ROMSlot active_rom_;
bool keyboard_is_active_, basic_is_active_;
uint8_t screen_mode_;
uint16_t screen_mode_base_address_;
uint16_t start_screen_address_;
// Counters related to simultaneous subsystems
unsigned int _frameCycles, _displayOutputPosition;
unsigned int _audioOutputPosition, _audioOutputPositionError;
uint8_t _phase;
unsigned int frame_cycles_, display_output_position_;
unsigned int audio_output_position_, audio_output_position_error_;
uint8_t phase_;
struct {
uint16_t forty1bpp[256];
@ -215,30 +143,30 @@ class Machine:
uint32_t eighty1bpp[256];
uint16_t eighty2bpp[256];
uint8_t eighty4bpp[256];
} _paletteTables;
} palette_tables_;
// Display generation.
uint16_t _startLineAddress, _currentScreenAddress;
int _current_pixel_line, _current_pixel_column, _current_character_row;
uint8_t _last_pixel_byte;
bool _isBlankLine;
uint16_t start_line_address_, current_screen_address_;
int current_pixel_line_, current_pixel_column_, current_character_row_;
uint8_t last_pixel_byte_;
bool is_blank_line_;
// CRT output
uint8_t *_current_output_target, *_initial_output_target;
unsigned int _current_output_divider;
uint8_t *current_output_target_, *initial_output_target_;
unsigned int current_output_divider_;
// Tape
Tape _tape;
bool _use_fast_tape_hack;
bool _fast_load_is_in_data;
Tape tape_;
bool use_fast_tape_hack_;
bool fast_load_is_in_data_;
// Disk
std::unique_ptr<Plus3> _plus3;
std::unique_ptr<Plus3> plus3_;
bool is_holding_shift_;
// Outputs
std::shared_ptr<Outputs::CRT::CRT> _crt;
std::shared_ptr<Speaker> _speaker;
std::shared_ptr<Outputs::CRT::CRT> crt_;
std::shared_ptr<Speaker> speaker_;
bool speaker_is_enabled_;
};

View File

@ -0,0 +1,27 @@
//
// Interrupts.hpp
// Clock Signal
//
// Created by Thomas Harte on 03/12/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Interrupts_h
#define Interrupts_h
#include <cstdint>
namespace Electron {
enum Interrupt: uint8_t {
PowerOnReset = 0x02,
DisplayEnd = 0x04,
RealTimeClock = 0x08,
ReceiveDataFull = 0x10,
TransmitDataEmpty = 0x20,
HighToneDetect = 0x40
};
}
#endif /* Interrupts_h */

View File

@ -0,0 +1,48 @@
//
// Speaker.cpp
// Clock Signal
//
// Created by Thomas Harte on 03/12/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Speaker.hpp"
using namespace Electron;
void Speaker::get_samples(unsigned int number_of_samples, int16_t *target)
{
if(is_enabled_)
{
while(number_of_samples--)
{
*target = (int16_t)((counter_ / (divider_+1)) * 8192);
target++;
counter_ = (counter_ + 1) % ((divider_+1) * 2);
}
}
else
{
memset(target, 0, sizeof(int16_t) * number_of_samples);
}
}
void Speaker::skip_samples(unsigned int number_of_samples)
{
counter_ = (counter_ + number_of_samples) % ((divider_+1) * 2);
}
void Speaker::set_divider(uint8_t divider)
{
enqueue([=]() {
divider_ = divider * 32 / clock_rate_divider;
});
}
void Speaker::set_is_enabled(bool is_enabled)
{
enqueue([=]() {
is_enabled_ = is_enabled;
counter_ = 0;
});
}

View File

@ -0,0 +1,35 @@
//
// Speaker.hpp
// Clock Signal
//
// Created by Thomas Harte on 03/12/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Electron_Speaker_hpp
#define Electron_Speaker_hpp
#include "../../Outputs/Speaker.hpp"
namespace Electron {
class Speaker: public ::Outputs::Filter<Speaker> {
public:
void set_divider(uint8_t divider);
void set_is_enabled(bool is_enabled);
void get_samples(unsigned int number_of_samples, int16_t *target);
void skip_samples(unsigned int number_of_samples);
static const unsigned int clock_rate_divider = 8;
private:
unsigned int counter_;
unsigned int divider_;
bool is_enabled_;
};
}
#endif /* Speaker_hpp */

135
Machines/Electron/Tape.cpp Normal file
View File

@ -0,0 +1,135 @@
//
// Tape.cpp
// Clock Signal
//
// Created by Thomas Harte on 03/12/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Tape.hpp"
using namespace Electron;
Tape::Tape() :
TapePlayer(2000000),
is_running_(false),
data_register_(0),
delegate_(nullptr),
output_({.bits_remaining_until_empty = 0, .cycles_into_pulse = 0}),
last_posted_interrupt_status_(0),
interrupt_status_(0)
{}
void Tape::push_tape_bit(uint16_t bit)
{
data_register_ = (uint16_t)((data_register_ >> 1) | (bit << 10));
if(input_.minimum_bits_until_full) input_.minimum_bits_until_full--;
if(input_.minimum_bits_until_full == 8) interrupt_status_ &= ~Interrupt::ReceiveDataFull;
if(!input_.minimum_bits_until_full)
{
if((data_register_&0x3) == 0x1)
{
interrupt_status_ |= Interrupt::ReceiveDataFull;
if(is_in_input_mode_) input_.minimum_bits_until_full = 9;
}
}
if(output_.bits_remaining_until_empty) output_.bits_remaining_until_empty--;
if(!output_.bits_remaining_until_empty) interrupt_status_ |= Interrupt::TransmitDataEmpty;
if(data_register_ == 0x3ff) interrupt_status_ |= Interrupt::HighToneDetect;
else interrupt_status_ &= ~Interrupt::HighToneDetect;
evaluate_interrupts();
}
void Tape::evaluate_interrupts()
{
if(last_posted_interrupt_status_ != interrupt_status_)
{
last_posted_interrupt_status_ = interrupt_status_;
if(delegate_) delegate_->tape_did_change_interrupt_status(this);
}
}
void Tape::clear_interrupts(uint8_t interrupts)
{
interrupt_status_ &= ~interrupts;
evaluate_interrupts();
}
void Tape::set_is_in_input_mode(bool is_in_input_mode)
{
is_in_input_mode_ = is_in_input_mode;
}
void Tape::set_counter(uint8_t value)
{
output_.cycles_into_pulse = 0;
output_.bits_remaining_until_empty = 0;
}
void Tape::set_data_register(uint8_t value)
{
data_register_ = (uint16_t)((value << 2) | 1);
output_.bits_remaining_until_empty = 9;
}
uint8_t Tape::get_data_register()
{
return (uint8_t)(data_register_ >> 2);
}
void Tape::process_input_pulse(Storage::Tape::Tape::Pulse pulse)
{
crossings_[0] = crossings_[1];
crossings_[1] = crossings_[2];
crossings_[2] = crossings_[3];
crossings_[3] = Tape::Unrecognised;
if(pulse.type != Storage::Tape::Tape::Pulse::Zero)
{
float pulse_length = (float)pulse.length.length / (float)pulse.length.clock_rate;
if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 2400.0) crossings_[3] = Tape::Short;
if(pulse_length >= 0.35 / 1200.0 && pulse_length < 0.7 / 1200.0) crossings_[3] = Tape::Long;
}
if(crossings_[0] == Tape::Long && crossings_[1] == Tape::Long)
{
push_tape_bit(0);
crossings_[0] = crossings_[1] = Tape::Recognised;
}
else
{
if(crossings_[0] == Tape::Short && crossings_[1] == Tape::Short && crossings_[2] == Tape::Short && crossings_[3] == Tape::Short)
{
push_tape_bit(1);
crossings_[0] = crossings_[1] =
crossings_[2] = crossings_[3] = Tape::Recognised;
}
}
}
void Tape::run_for_cycles(unsigned int number_of_cycles)
{
if(is_enabled_)
{
if(is_in_input_mode_)
{
if(is_running_)
{
TapePlayer::run_for_cycles((int)number_of_cycles);
}
}
else
{
output_.cycles_into_pulse += number_of_cycles;
while(output_.cycles_into_pulse > 1664) // 1664 = the closest you can get to 1200 baud if you're looking for something
{ // that divides the 125,000Hz clock that the sound divider runs off.
output_.cycles_into_pulse -= 1664;
push_tape_bit(1);
}
}
}
}

View File

@ -0,0 +1,72 @@
//
// Tape.hpp
// Clock Signal
//
// Created by Thomas Harte on 03/12/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Electron_Tape_h
#define Electron_Tape_h
#include "../../Storage/Tape/Tape.hpp"
#include "Interrupts.hpp"
#include <cstdint>
namespace Electron {
class Tape: public Storage::Tape::TapePlayer {
public:
Tape();
void run_for_cycles(unsigned int number_of_cycles);
uint8_t get_data_register();
void set_data_register(uint8_t value);
void set_counter(uint8_t value);
inline uint8_t get_interrupt_status() { return interrupt_status_; }
void clear_interrupts(uint8_t interrupts);
class Delegate {
public:
virtual void tape_did_change_interrupt_status(Tape *tape) = 0;
};
inline void set_delegate(Delegate *delegate) { delegate_ = delegate; }
inline void set_is_running(bool is_running) { is_running_ = is_running; }
inline void set_is_enabled(bool is_enabled) { is_enabled_ = is_enabled; }
void set_is_in_input_mode(bool is_in_input_mode);
private:
void process_input_pulse(Storage::Tape::Tape::Pulse pulse);
inline void push_tape_bit(uint16_t bit);
inline void get_next_tape_pulse();
struct {
int minimum_bits_until_full;
} input_;
struct {
unsigned int cycles_into_pulse;
unsigned int bits_remaining_until_empty;
} output_;
bool is_running_;
bool is_enabled_;
bool is_in_input_mode_;
inline void evaluate_interrupts();
uint16_t data_register_;
uint8_t interrupt_status_, last_posted_interrupt_status_;
Delegate *delegate_;
enum {
Long, Short, Unrecognised, Recognised
} crossings_[4];
};
}
#endif /* Electron_Tape_h */

View File

@ -140,15 +140,15 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
}
}
if(_typer && address == scan_keyboard_address_ && operation == CPU6502::BusOperation::ReadOpcode)
if(typer_ && address == scan_keyboard_address_ && operation == CPU6502::BusOperation::ReadOpcode)
{
// the Oric 1 misses any key pressed on the very first entry into the read keyboard routine, so don't
// do anything until at least the second, regardless of machine
if(!keyboard_read_count_) keyboard_read_count_++;
else if(!_typer->type_next_character())
else if(!typer_->type_next_character())
{
clear_all_keys();
_typer.reset();
typer_.reset();
}
}

View File

@ -12,32 +12,32 @@
using namespace Utility;
Typer::Typer(const char *string, int delay, int frequency, Delegate *delegate) :
_counter(-delay), _frequency(frequency), _string_pointer(0), _delegate(delegate), _phase(0)
counter_(-delay), frequency_(frequency), string_pointer_(0), delegate_(delegate), phase_(0)
{
size_t string_size = strlen(string) + 3;
_string = (char *)malloc(string_size);
snprintf(_string, strlen(string) + 3, "%c%s%c", Typer::BeginString, string, Typer::EndString);
string_ = (char *)malloc(string_size);
snprintf(string_, strlen(string) + 3, "%c%s%c", Typer::BeginString, string, Typer::EndString);
}
void Typer::update(int duration)
{
if(_string)
if(string_)
{
if(_counter < 0 && _counter + duration >= 0)
if(counter_ < 0 && counter_ + duration >= 0)
{
if(!type_next_character())
{
_delegate->typer_reset(this);
delegate_->typer_reset(this);
}
}
_counter += duration;
while(_string && _counter > _frequency)
counter_ += duration;
while(string_ && counter_ > frequency_)
{
_counter -= _frequency;
counter_ -= frequency_;
if(!type_next_character())
{
_delegate->typer_reset(this);
delegate_->typer_reset(this);
}
}
}
@ -45,23 +45,23 @@ void Typer::update(int duration)
bool Typer::type_next_character()
{
if(_string == nullptr) return false;
if(string_ == nullptr) return false;
if(_delegate->typer_set_next_character(this, _string[_string_pointer], _phase))
if(delegate_->typer_set_next_character(this, string_[string_pointer_], phase_))
{
_phase = 0;
if(!_string[_string_pointer])
phase_ = 0;
if(!string_[string_pointer_])
{
free(_string);
_string = nullptr;
free(string_);
string_ = nullptr;
return false;
}
_string_pointer++;
string_pointer_++;
}
else
{
_phase++;
phase_++;
}
return true;
@ -69,7 +69,7 @@ bool Typer::type_next_character()
Typer::~Typer()
{
free(_string);
free(string_);
}
#pragma mark - Delegate

View File

@ -35,31 +35,31 @@ class Typer {
const char EndString = 0x03; // i.e. ASCII end of text
private:
char *_string;
int _frequency;
int _counter;
int _phase;
Delegate *_delegate;
size_t _string_pointer;
char *string_;
int frequency_;
int counter_;
int phase_;
Delegate *delegate_;
size_t string_pointer_;
};
class TypeRecipient: public Typer::Delegate {
public:
void set_typer_for_string(const char *string)
{
_typer.reset(new Typer(string, get_typer_delay(), get_typer_frequency(), this));
typer_.reset(new Typer(string, get_typer_delay(), get_typer_frequency(), this));
}
void typer_reset(Typer *typer)
{
clear_all_keys();
_typer.reset();
typer_.reset();
}
protected:
virtual int get_typer_delay() { return 0; }
virtual int get_typer_frequency() { return 0; }
std::unique_ptr<Typer> _typer;
std::unique_ptr<Typer> typer_;
};
}

View File

@ -383,6 +383,9 @@
4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; };
4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD69F921D98760000243FE1 /* AcornADF.cpp */; };
4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE77A2C1D84ADFB00BC3827 /* File.cpp */; };
4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA525D1DF33323007E74F2 /* Tape.cpp */; };
4BEA52631DF339D7007E74F2 /* Speaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA52611DF339D7007E74F2 /* Speaker.cpp */; };
4BEA52661DF3472B007E74F2 /* Speaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA52641DF3472B007E74F2 /* Speaker.cpp */; };
4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; };
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; };
4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; };
@ -892,6 +895,14 @@
4BD69F931D98760000243FE1 /* AcornADF.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AcornADF.hpp; sourceTree = "<group>"; };
4BE77A2C1D84ADFB00BC3827 /* File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = File.cpp; path = ../../StaticAnalyser/Commodore/File.cpp; sourceTree = "<group>"; };
4BE77A2D1D84ADFB00BC3827 /* File.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = File.hpp; path = ../../StaticAnalyser/Commodore/File.hpp; sourceTree = "<group>"; };
4BEA525D1DF33323007E74F2 /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = Electron/Tape.cpp; sourceTree = "<group>"; };
4BEA525F1DF333D8007E74F2 /* Tape.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = Electron/Tape.hpp; sourceTree = "<group>"; };
4BEA52601DF3343A007E74F2 /* Interrupts.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Interrupts.hpp; path = Electron/Interrupts.hpp; sourceTree = "<group>"; };
4BEA52611DF339D7007E74F2 /* Speaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Speaker.cpp; path = Electron/Speaker.cpp; sourceTree = "<group>"; };
4BEA52621DF339D7007E74F2 /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Speaker.hpp; path = Electron/Speaker.hpp; sourceTree = "<group>"; };
4BEA52641DF3472B007E74F2 /* Speaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Speaker.cpp; sourceTree = "<group>"; };
4BEA52651DF3472B007E74F2 /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Speaker.hpp; sourceTree = "<group>"; };
4BEA52671DF34909007E74F2 /* PIA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PIA.hpp; sourceTree = "<group>"; };
4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = "<group>"; };
4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; };
4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = "<group>"; };
@ -1042,8 +1053,11 @@
isa = PBXGroup;
children = (
4B2E2D971C3A06EC00138695 /* Atari2600.cpp */,
4B2E2D981C3A06EC00138695 /* Atari2600.hpp */,
4BEA52641DF3472B007E74F2 /* Speaker.cpp */,
4B2E2D991C3A06EC00138695 /* Atari2600Inputs.h */,
4B2E2D981C3A06EC00138695 /* Atari2600.hpp */,
4BEA52651DF3472B007E74F2 /* Speaker.hpp */,
4BEA52671DF34909007E74F2 /* PIA.hpp */,
);
path = Atari2600;
sourceTree = "<group>";
@ -1052,10 +1066,15 @@
isa = PBXGroup;
children = (
4B2E2D9B1C3A070400138695 /* Electron.cpp */,
4B2E2D9C1C3A070400138695 /* Electron.hpp */,
4B30512E1D98ACC600B4FED8 /* Plus3.cpp */,
4B30512F1D98ACC600B4FED8 /* Plus3.hpp */,
4BEA52611DF339D7007E74F2 /* Speaker.cpp */,
4BEA525D1DF33323007E74F2 /* Tape.cpp */,
4BC8A62B1DCE60E000DAC693 /* Typer.cpp */,
4B2E2D9C1C3A070400138695 /* Electron.hpp */,
4BEA52601DF3343A007E74F2 /* Interrupts.hpp */,
4B30512F1D98ACC600B4FED8 /* Plus3.hpp */,
4BEA52621DF339D7007E74F2 /* Speaker.hpp */,
4BEA525F1DF333D8007E74F2 /* Tape.hpp */,
);
name = Electron;
sourceTree = "<group>";
@ -2314,6 +2333,7 @@
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */,
4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */,
4BC8A62D1DCE60E000DAC693 /* Typer.cpp in Sources */,
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */,
4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */,
@ -2335,6 +2355,7 @@
4B2A332F1DB86869002876E3 /* OricOptionsPanel.swift in Sources */,
4B2A53A11D117D36003C6002 /* CSAtari2600.mm in Sources */,
4BF829661D8F732B001BAE39 /* Disk.cpp in Sources */,
4BEA52631DF339D7007E74F2 /* Speaker.cpp in Sources */,
4BC5E4921D7ED365008CF980 /* StaticAnalyser.cpp in Sources */,
4BC830D11D6E7C690000A26F /* Tape.cpp in Sources */,
4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */,
@ -2378,6 +2399,7 @@
4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */,
4BCA6CC81D9DD9F000C2D7B2 /* CommodoreROM.cpp in Sources */,
4BA22B071D8817CE0008C640 /* Disk.cpp in Sources */,
4BEA52661DF3472B007E74F2 /* Speaker.cpp in Sources */,
4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */,
4B4C83701D4F623200CD541F /* D64.cpp in Sources */,
4B5073071DDD3B9400C48FBD /* ArrayBuilder.cpp in Sources */,

View File

@ -40,13 +40,13 @@ class Speaker {
float get_ideal_clock_rate_in_range(float minimum, float maximum)
{
// return twice the cut off, if applicable
if(_high_frequency_cut_off > 0.0f && _input_cycles_per_second >= _high_frequency_cut_off * 3.0f && _input_cycles_per_second <= _high_frequency_cut_off * 3.0f) return _high_frequency_cut_off * 3.0f;
if(high_frequency_cut_off_ > 0.0f && input_cycles_per_second_ >= high_frequency_cut_off_ * 3.0f && input_cycles_per_second_ <= high_frequency_cut_off_ * 3.0f) return high_frequency_cut_off_ * 3.0f;
// return exactly the input rate if possible
if(_input_cycles_per_second >= minimum && _input_cycles_per_second <= maximum) return _input_cycles_per_second;
if(input_cycles_per_second_ >= minimum && input_cycles_per_second_ <= maximum) return input_cycles_per_second_;
// if the input rate is lower, return the minimum
if(_input_cycles_per_second < minimum) return minimum;
if(input_cycles_per_second_ < minimum) return minimum;
// otherwise, return the maximum
return maximum;
@ -54,29 +54,29 @@ class Speaker {
void set_output_rate(float cycles_per_second, int buffer_size)
{
_output_cycles_per_second = cycles_per_second;
if(_buffer_size != buffer_size)
output_cycles_per_second_ = cycles_per_second;
if(buffer_size_ != buffer_size)
{
_buffer_in_progress.reset(new int16_t[buffer_size]);
_buffer_size = buffer_size;
buffer_in_progress_.reset(new int16_t[buffer_size]);
buffer_size_ = buffer_size;
}
set_needs_updated_filter_coefficients();
}
void set_output_quality(int number_of_taps)
{
_requested_number_of_taps = number_of_taps;
requested_number_of_taps_ = number_of_taps;
set_needs_updated_filter_coefficients();
}
void set_delegate(Delegate *delegate)
{
_delegate = delegate;
delegate_ = delegate;
}
void set_input_rate(float cycles_per_second)
{
_input_cycles_per_second = cycles_per_second;
input_cycles_per_second_ = cycles_per_second;
set_needs_updated_filter_coefficients();
}
@ -85,19 +85,19 @@ class Speaker {
*/
void set_high_frequency_cut_off(float high_frequency)
{
_high_frequency_cut_off = high_frequency;
high_frequency_cut_off_ = high_frequency;
set_needs_updated_filter_coefficients();
}
Speaker() : _buffer_in_progress_pointer(0), _requested_number_of_taps(0), _high_frequency_cut_off(-1.0), _queue(new Concurrency::AsyncTaskQueue) {}
Speaker() : buffer_in_progress_pointer_(0), requested_number_of_taps_(0), high_frequency_cut_off_(-1.0), _queue(new Concurrency::AsyncTaskQueue) {}
/*!
Ensures any deferred processing occurs now.
*/
void flush()
{
std::shared_ptr<std::list<std::function<void(void)>>> queued_functions = _queued_functions;
_queued_functions.reset();
std::shared_ptr<std::list<std::function<void(void)>>> queued_functions = queued_functions_;
queued_functions_.reset();
_queue->enqueue([queued_functions] {
for(auto function : *queued_functions)
{
@ -109,24 +109,24 @@ class Speaker {
protected:
void enqueue(std::function<void(void)> function)
{
if(!_queued_functions) _queued_functions.reset(new std::list<std::function<void(void)>>);
_queued_functions->push_back(function);
if(!queued_functions_) queued_functions_.reset(new std::list<std::function<void(void)>>);
queued_functions_->push_back(function);
}
std::shared_ptr<std::list<std::function<void(void)>>> _queued_functions;
std::shared_ptr<std::list<std::function<void(void)>>> queued_functions_;
std::unique_ptr<int16_t> _buffer_in_progress;
float _high_frequency_cut_off;
int _buffer_size;
int _buffer_in_progress_pointer;
int _number_of_taps, _requested_number_of_taps;
bool _coefficients_are_dirty;
Delegate *_delegate;
std::unique_ptr<int16_t> buffer_in_progress_;
float high_frequency_cut_off_;
int buffer_size_;
int buffer_in_progress_pointer_;
int number_of_taps_, requested_number_of_taps_;
bool coefficients_are_dirty_;
Delegate *delegate_;
float _input_cycles_per_second, _output_cycles_per_second;
float input_cycles_per_second_, output_cycles_per_second_;
void set_needs_updated_filter_coefficients()
{
_coefficients_are_dirty = true;
coefficients_are_dirty_ = true;
}
void get_samples(unsigned int quantity, int16_t *target) {}
@ -160,26 +160,26 @@ template <class T> class Filter: public Speaker {
{
enqueue([=]() {
unsigned int cycles_remaining = input_cycles;
if(_coefficients_are_dirty) update_filter_coefficients();
if(coefficients_are_dirty_) update_filter_coefficients();
// if input and output rates exactly match, just accumulate results and pass on
if(_input_cycles_per_second == _output_cycles_per_second && _high_frequency_cut_off < 0.0)
if(input_cycles_per_second_ == output_cycles_per_second_ && high_frequency_cut_off_ < 0.0)
{
while(cycles_remaining)
{
unsigned int cycles_to_read = (unsigned int)(_buffer_size - _buffer_in_progress_pointer);
unsigned int cycles_to_read = (unsigned int)(buffer_size_ - buffer_in_progress_pointer_);
if(cycles_to_read > cycles_remaining) cycles_to_read = cycles_remaining;
static_cast<T *>(this)->get_samples(cycles_to_read, &_buffer_in_progress.get()[_buffer_in_progress_pointer]);
_buffer_in_progress_pointer += cycles_to_read;
static_cast<T *>(this)->get_samples(cycles_to_read, &buffer_in_progress_.get()[buffer_in_progress_pointer_]);
buffer_in_progress_pointer_ += cycles_to_read;
// announce to delegate if full
if(_buffer_in_progress_pointer == _buffer_size)
if(buffer_in_progress_pointer_ == buffer_size_)
{
_buffer_in_progress_pointer = 0;
if(_delegate)
buffer_in_progress_pointer_ = 0;
if(delegate_)
{
_delegate->speaker_did_complete_samples(this, _buffer_in_progress.get(), _buffer_size);
delegate_->speaker_did_complete_samples(this, buffer_in_progress_.get(), buffer_size_);
}
}
@ -190,45 +190,45 @@ template <class T> class Filter: public Speaker {
}
// if the output rate is less than the input rate, use the filter
if(_input_cycles_per_second > _output_cycles_per_second)
if(input_cycles_per_second_ > output_cycles_per_second_)
{
while(cycles_remaining)
{
unsigned int cycles_to_read = (unsigned int)std::min((int)cycles_remaining, _number_of_taps - _input_buffer_depth);
static_cast<T *>(this)->get_samples(cycles_to_read, &_input_buffer.get()[_input_buffer_depth]);
unsigned int cycles_to_read = (unsigned int)std::min((int)cycles_remaining, number_of_taps_ - input_buffer_depth_);
static_cast<T *>(this)->get_samples(cycles_to_read, &input_buffer_.get()[input_buffer_depth_]);
cycles_remaining -= cycles_to_read;
_input_buffer_depth += cycles_to_read;
input_buffer_depth_ += cycles_to_read;
if(_input_buffer_depth == _number_of_taps)
if(input_buffer_depth_ == number_of_taps_)
{
_buffer_in_progress.get()[_buffer_in_progress_pointer] = _filter->apply(_input_buffer.get());
_buffer_in_progress_pointer++;
buffer_in_progress_.get()[buffer_in_progress_pointer_] = filter_->apply(input_buffer_.get());
buffer_in_progress_pointer_++;
// announce to delegate if full
if(_buffer_in_progress_pointer == _buffer_size)
if(buffer_in_progress_pointer_ == buffer_size_)
{
_buffer_in_progress_pointer = 0;
if(_delegate)
buffer_in_progress_pointer_ = 0;
if(delegate_)
{
_delegate->speaker_did_complete_samples(this, _buffer_in_progress.get(), _buffer_size);
delegate_->speaker_did_complete_samples(this, buffer_in_progress_.get(), buffer_size_);
}
}
// If the next loop around is going to reuse some of the samples just collected, use a memmove to
// preserve them in the correct locations (TODO: use a longer buffer to fix that) and don't skip
// anything. Otherwise skip as required to get to the next sample batch and don't expect to reuse.
uint64_t steps = _stepper->step();
if(steps < _number_of_taps)
uint64_t steps = stepper_->step();
if(steps < number_of_taps_)
{
int16_t *input_buffer = _input_buffer.get();
memmove(input_buffer, &input_buffer[steps], sizeof(int16_t) * ((size_t)_number_of_taps - (size_t)steps));
_input_buffer_depth -= steps;
int16_t *input_buffer = input_buffer_.get();
memmove(input_buffer, &input_buffer[steps], sizeof(int16_t) * ((size_t)number_of_taps_ - (size_t)steps));
input_buffer_depth_ -= steps;
}
else
{
if(steps > _number_of_taps)
static_cast<T *>(this)->skip_samples((unsigned int)steps - (unsigned int)_number_of_taps);
_input_buffer_depth = 0;
if(steps > number_of_taps_)
static_cast<T *>(this)->skip_samples((unsigned int)steps - (unsigned int)number_of_taps_);
input_buffer_depth_ = 0;
}
}
}
@ -241,44 +241,44 @@ template <class T> class Filter: public Speaker {
}
private:
std::unique_ptr<SignalProcessing::Stepper> _stepper;
std::unique_ptr<SignalProcessing::FIRFilter> _filter;
std::unique_ptr<SignalProcessing::Stepper> stepper_;
std::unique_ptr<SignalProcessing::FIRFilter> filter_;
std::unique_ptr<int16_t> _input_buffer;
int _input_buffer_depth;
std::unique_ptr<int16_t> input_buffer_;
int input_buffer_depth_;
void update_filter_coefficients()
{
// make a guess at a good number of taps if this hasn't been provided explicitly
if(_requested_number_of_taps)
if(requested_number_of_taps_)
{
_number_of_taps = _requested_number_of_taps;
number_of_taps_ = requested_number_of_taps_;
}
else
{
_number_of_taps = (int)ceilf((_input_cycles_per_second + _output_cycles_per_second) / _output_cycles_per_second);
_number_of_taps *= 2;
_number_of_taps |= 1;
number_of_taps_ = (int)ceilf((input_cycles_per_second_ + output_cycles_per_second_) / output_cycles_per_second_);
number_of_taps_ *= 2;
number_of_taps_ |= 1;
}
_coefficients_are_dirty = false;
_buffer_in_progress_pointer = 0;
coefficients_are_dirty_ = false;
buffer_in_progress_pointer_ = 0;
_stepper.reset(new SignalProcessing::Stepper((uint64_t)_input_cycles_per_second, (uint64_t)_output_cycles_per_second));
stepper_.reset(new SignalProcessing::Stepper((uint64_t)input_cycles_per_second_, (uint64_t)output_cycles_per_second_));
float high_pass_frequency;
if(_high_frequency_cut_off > 0.0)
if(high_frequency_cut_off_ > 0.0)
{
high_pass_frequency = std::min((float)_output_cycles_per_second / 2.0f, _high_frequency_cut_off);
high_pass_frequency = std::min((float)output_cycles_per_second_ / 2.0f, high_frequency_cut_off_);
}
else
{
high_pass_frequency = (float)_output_cycles_per_second / 2.0f;
high_pass_frequency = (float)output_cycles_per_second_ / 2.0f;
}
_filter.reset(new SignalProcessing::FIRFilter((unsigned int)_number_of_taps, (float)_input_cycles_per_second, 0.0, high_pass_frequency, SignalProcessing::FIRFilter::DefaultAttenuation));
filter_.reset(new SignalProcessing::FIRFilter((unsigned int)number_of_taps_, (float)input_cycles_per_second_, 0.0, high_pass_frequency, SignalProcessing::FIRFilter::DefaultAttenuation));
_input_buffer.reset(new int16_t[_number_of_taps]);
_input_buffer_depth = 0;
input_buffer_.reset(new int16_t[number_of_taps_]);
input_buffer_depth_ = 0;
}
};

File diff suppressed because it is too large Load Diff

View File

@ -51,10 +51,10 @@ class Cartridge {
std::vector<uint8_t> data;
};
const std::list<Segment> &get_segments() { return _segments; }
const std::list<Segment> &get_segments() { return segments_; }
protected:
std::list<Segment> _segments;
std::list<Segment> segments_;
};
}

View File

@ -28,7 +28,7 @@ BinaryDump::BinaryDump(const char *file_name)
fclose(file);
// enshrine
_segments.emplace_back(
segments_.emplace_back(
::Storage::Cartridge::Cartridge::Segment::UnknownAddress,
::Storage::Cartridge::Cartridge::Segment::UnknownAddress,
std::move(contents));

View File

@ -46,5 +46,5 @@ PRG::PRG(const char *file_name)
if(!Storage::Cartridge::Encodings::CommodoreROM::isROM(contents))
throw ErrorNotROM;
_segments.emplace_back(0xa000, 0xa000 + data_length, std::move(contents));
segments_.emplace_back(0xa000, 0xa000 + data_length, std::move(contents));
}

View File

@ -13,58 +13,58 @@
using namespace Storage;
DigitalPhaseLockedLoop::DigitalPhaseLockedLoop(int clocks_per_bit, int tolerance, size_t length_of_history) :
_clocks_per_bit(clocks_per_bit),
_tolerance(tolerance),
clocks_per_bit_(clocks_per_bit),
tolerance_(tolerance),
_phase(0),
_window_length(clocks_per_bit),
phase_(0),
window_length_(clocks_per_bit),
_phase_error_pointer(0)
phase_error_pointer_(0)
{
_phase_error_history.reset(new std::vector<int>(length_of_history, 0));
phase_error_history_.reset(new std::vector<int>(length_of_history, 0));
}
void DigitalPhaseLockedLoop::run_for_cycles(int number_of_cycles)
{
_phase += number_of_cycles;
if(_phase >= _window_length)
phase_ += number_of_cycles;
if(phase_ >= window_length_)
{
int windows_crossed = _phase / _window_length;
int windows_crossed = phase_ / window_length_;
// check whether this triggers any 0s, if anybody cares
if(_delegate)
if(delegate_)
{
if(_window_was_filled) windows_crossed--;
if(window_was_filled_) windows_crossed--;
for(int c = 0; c < windows_crossed; c++)
_delegate->digital_phase_locked_loop_output_bit(0);
delegate_->digital_phase_locked_loop_output_bit(0);
}
_window_was_filled = false;
_phase %= _window_length;
window_was_filled_ = false;
phase_ %= window_length_;
}
}
void DigitalPhaseLockedLoop::add_pulse()
{
if(!_window_was_filled)
if(!window_was_filled_)
{
if(_delegate) _delegate->digital_phase_locked_loop_output_bit(1);
_window_was_filled = true;
post_phase_error(_phase - (_window_length >> 1));
if(delegate_) delegate_->digital_phase_locked_loop_output_bit(1);
window_was_filled_ = true;
post_phase_error(phase_ - (window_length_ >> 1));
}
}
void DigitalPhaseLockedLoop::post_phase_error(int error)
{
// use a simple spring mechanism as a lowpass filter for phase
_phase -= (error + 1) >> 1;
phase_ -= (error + 1) >> 1;
// use the average of the last few errors to affect frequency
std::vector<int> *phase_error_history = _phase_error_history.get();
std::vector<int> *phase_error_history = phase_error_history_.get();
size_t phase_error_history_size = phase_error_history->size();
(*phase_error_history)[_phase_error_pointer] = error;
_phase_error_pointer = (_phase_error_pointer + 1)%phase_error_history_size;
(*phase_error_history)[phase_error_pointer_] = error;
phase_error_pointer_ = (phase_error_pointer_ + 1)%phase_error_history_size;
int total_error = 0;
for(size_t c = 0; c < phase_error_history_size; c++)
@ -72,6 +72,6 @@ void DigitalPhaseLockedLoop::post_phase_error(int error)
total_error += (*phase_error_history)[c];
}
int denominator = (int)(phase_error_history_size * 4);
_window_length += (total_error + (denominator >> 1)) / denominator;
_window_length = std::max(std::min(_window_length, _clocks_per_bit + _tolerance), _clocks_per_bit - _tolerance);
window_length_ += (total_error + (denominator >> 1)) / denominator;
window_length_ = std::max(std::min(window_length_, clocks_per_bit_ + tolerance_), clocks_per_bit_ - tolerance_);
}

View File

@ -46,22 +46,22 @@ class DigitalPhaseLockedLoop {
};
void set_delegate(Delegate *delegate)
{
_delegate = delegate;
delegate_ = delegate;
}
private:
Delegate *_delegate;
Delegate *delegate_;
void post_phase_error(int error);
std::unique_ptr<std::vector<int>> _phase_error_history;
size_t _phase_error_pointer;
std::unique_ptr<std::vector<int>> phase_error_history_;
size_t phase_error_pointer_;
int _phase;
int _window_length;
bool _window_was_filled;
int phase_;
int window_length_;
bool window_was_filled_;
int _clocks_per_bit;
int _tolerance;
int clocks_per_bit_;
int tolerance_;
};
}

View File

@ -11,14 +11,14 @@
using namespace Storage::Disk;
Controller::Controller(unsigned int clock_rate, unsigned int clock_rate_multiplier, unsigned int revolutions_per_minute) :
_clock_rate(clock_rate * clock_rate_multiplier),
_clock_rate_multiplier(clock_rate_multiplier),
clock_rate_(clock_rate * clock_rate_multiplier),
clock_rate_multiplier_(clock_rate_multiplier),
TimedEventLoop(clock_rate * clock_rate_multiplier)
{
_rotational_multiplier.length = 60;
_rotational_multiplier.clock_rate = revolutions_per_minute;
_rotational_multiplier.simplify();
rotational_multiplier_.length = 60;
rotational_multiplier_.clock_rate = revolutions_per_minute;
rotational_multiplier_.simplify();
// seed this class with a PLL, any PLL, so that it's safe to assume non-nullptr later
Time one;
@ -27,38 +27,38 @@ Controller::Controller(unsigned int clock_rate, unsigned int clock_rate_multipli
void Controller::setup_track()
{
_track = _drive->get_track();
track_ = drive_->get_track();
Time offset;
if(_track && _time_into_track.length > 0)
if(track_ && time_into_track_.length > 0)
{
Time time_found = _track->seek_to(_time_into_track).simplify();
offset = (_time_into_track - time_found).simplify();
_time_into_track = time_found;
Time time_found = track_->seek_to(time_into_track_).simplify();
offset = (time_into_track_ - time_found).simplify();
time_into_track_ = time_found;
}
else
{
offset = _time_into_track;
_time_into_track.set_zero();
offset = time_into_track_;
time_into_track_.set_zero();
}
reset_timer_to_offset(offset * _rotational_multiplier);
reset_timer_to_offset(offset * rotational_multiplier_);
get_next_event();
}
void Controller::run_for_cycles(int number_of_cycles)
{
if(_drive && _drive->has_disk() && _motor_is_on)
if(drive_ && drive_->has_disk() && motor_is_on_)
{
if(!_track) setup_track();
number_of_cycles *= _clock_rate_multiplier;
if(!track_) setup_track();
number_of_cycles *= clock_rate_multiplier_;
while(number_of_cycles)
{
int cycles_until_next_event = (int)get_cycles_until_next_event();
int cycles_to_run_for = std::min(cycles_until_next_event, number_of_cycles);
_cycles_since_index_hole += (unsigned int)cycles_to_run_for;
cycles_since_index_hole_ += (unsigned int)cycles_to_run_for;
number_of_cycles -= cycles_to_run_for;
_pll->run_for_cycles(cycles_to_run_for);
pll_->run_for_cycles(cycles_to_run_for);
TimedEventLoop::run_for_cycles(cycles_to_run_for);
}
}
@ -68,31 +68,31 @@ void Controller::run_for_cycles(int number_of_cycles)
void Controller::get_next_event()
{
if(_track)
_current_event = _track->get_next_event();
if(track_)
current_event_ = track_->get_next_event();
else
{
_current_event.length.length = 1;
_current_event.length.clock_rate = 1;
_current_event.type = Track::Event::IndexHole;
current_event_.length.length = 1;
current_event_.length.clock_rate = 1;
current_event_.type = Track::Event::IndexHole;
}
// divide interval, which is in terms of a rotation of the disk, by rotation speed, and
// convert it into revolutions per second
set_next_event_time_interval(_current_event.length * _rotational_multiplier);
set_next_event_time_interval(current_event_.length * rotational_multiplier_);
}
void Controller::process_next_event()
{
switch(_current_event.type)
switch(current_event_.type)
{
case Track::Event::FluxTransition:
_pll->add_pulse();
_time_into_track += _current_event.length;
pll_->add_pulse();
time_into_track_ += current_event_.length;
break;
case Track::Event::IndexHole:
_cycles_since_index_hole = 0;
_time_into_track.set_zero();
cycles_since_index_hole_ = 0;
time_into_track_.set_zero();
process_index_hole();
break;
}
@ -103,57 +103,57 @@ void Controller::process_next_event()
void Controller::set_expected_bit_length(Time bit_length)
{
_bit_length = bit_length;
bit_length_ = bit_length;
// this conversion doesn't need to be exact because there's a lot of variation to be taken
// account of in rotation speed, air turbulence, etc, so a direct conversion will do
int clocks_per_bit = (int)((bit_length.length * _clock_rate) / bit_length.clock_rate);
_pll.reset(new DigitalPhaseLockedLoop(clocks_per_bit, clocks_per_bit / 5, 3));
_pll->set_delegate(this);
int clocks_per_bit = (int)((bit_length.length * clock_rate_) / bit_length.clock_rate);
pll_.reset(new DigitalPhaseLockedLoop(clocks_per_bit, clocks_per_bit / 5, 3));
pll_->set_delegate(this);
}
void Controller::digital_phase_locked_loop_output_bit(int value)
{
process_input_bit(value, _cycles_since_index_hole);
process_input_bit(value, cycles_since_index_hole_);
}
#pragma mark - Drive actions
bool Controller::get_is_track_zero()
{
if(!_drive) return false;
return _drive->get_is_track_zero();
if(!drive_) return false;
return drive_->get_is_track_zero();
}
bool Controller::get_drive_is_ready()
{
if(!_drive) return false;
return _drive->has_disk();
if(!drive_) return false;
return drive_->has_disk();
}
void Controller::step(int direction)
{
if(_drive) _drive->step(direction);
if(drive_) drive_->step(direction);
invalidate_track();
}
void Controller::set_motor_on(bool motor_on)
{
_motor_is_on = motor_on;
motor_is_on_ = motor_on;
}
bool Controller::get_motor_on()
{
return _motor_is_on;
return motor_is_on_;
}
void Controller::set_drive(std::shared_ptr<Drive> drive)
{
_drive = drive;
drive_ = drive;
invalidate_track();
}
void Controller::invalidate_track()
{
_track = nullptr;
track_ = nullptr;
}

View File

@ -81,20 +81,20 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop
virtual bool get_drive_is_ready();
private:
Time _bit_length;
unsigned int _clock_rate;
unsigned int _clock_rate_multiplier;
Time _rotational_multiplier;
Time bit_length_;
unsigned int clock_rate_;
unsigned int clock_rate_multiplier_;
Time rotational_multiplier_;
std::shared_ptr<DigitalPhaseLockedLoop> _pll;
std::shared_ptr<Drive> _drive;
std::shared_ptr<Track> _track;
unsigned int _cycles_since_index_hole;
std::shared_ptr<DigitalPhaseLockedLoop> pll_;
std::shared_ptr<Drive> drive_;
std::shared_ptr<Track> track_;
unsigned int cycles_since_index_hole_;
inline void get_next_event();
Track::Event _current_event;
Time _time_into_track;
bool _motor_is_on;
Track::Event current_event_;
Time time_into_track_;
bool motor_is_on_;
void setup_track();
};

View File

@ -12,35 +12,35 @@
using namespace Storage::Disk;
Drive::Drive()
: _head_position(0), _head(0) {}
: head_position_(0), head_(0) {}
void Drive::set_disk(std::shared_ptr<Disk> disk)
{
_disk = disk;
disk_ = disk;
}
bool Drive::has_disk()
{
return (bool)_disk;
return (bool)disk_;
}
bool Drive::get_is_track_zero()
{
return _head_position == 0;
return head_position_ == 0;
}
void Drive::step(int direction)
{
_head_position = std::max(_head_position + direction, 0);
head_position_ = std::max(head_position_ + direction, 0);
}
void Drive::set_head(unsigned int head)
{
_head = head;
head_ = head;
}
std::shared_ptr<Track> Drive::get_track()
{
if(_disk) return _disk->get_track_at_position(_head, (unsigned int)_head_position);
if(disk_) return disk_->get_track_at_position(head_, (unsigned int)head_position_);
return nullptr;
}

View File

@ -47,9 +47,9 @@ class Drive {
std::shared_ptr<Track> get_track();
private:
std::shared_ptr<Disk> _disk;
int _head_position;
unsigned int _head;
std::shared_ptr<Disk> disk_;
int head_position_;
unsigned int head_;
};

View File

@ -13,7 +13,7 @@ using namespace Storage::Disk;
PCMTrack::PCMTrack(std::vector<PCMSegment> segments)
{
_segments = std::move(segments);
segments_ = std::move(segments);
fix_length();
}
@ -21,7 +21,7 @@ PCMTrack::PCMTrack(PCMSegment segment)
{
segment.length_of_a_bit.length = 1;
segment.length_of_a_bit.clock_rate = 1;
_segments.push_back(std::move(segment));
segments_.push_back(std::move(segment));
fix_length();
}
@ -29,54 +29,54 @@ PCMTrack::Event PCMTrack::get_next_event()
{
// find the next 1 in the input stream, keeping count of length as we go, and assuming it's going
// to be a flux transition
_next_event.type = Track::Event::FluxTransition;
_next_event.length.length = 0;
while(_segment_pointer < _segments.size())
next_event_.type = Track::Event::FluxTransition;
next_event_.length.length = 0;
while(segment_pointer_ < segments_.size())
{
unsigned int clock_multiplier = _track_clock_rate / _segments[_segment_pointer].length_of_a_bit.clock_rate;
unsigned int bit_length = clock_multiplier * _segments[_segment_pointer].length_of_a_bit.length;
unsigned int clock_multiplier = track_clock_rate_ / segments_[segment_pointer_].length_of_a_bit.clock_rate;
unsigned int bit_length = clock_multiplier * segments_[segment_pointer_].length_of_a_bit.length;
const uint8_t *segment_data = &_segments[_segment_pointer].data[0];
while(_bit_pointer < _segments[_segment_pointer].number_of_bits)
const uint8_t *segment_data = &segments_[segment_pointer_].data[0];
while(bit_pointer_ < segments_[segment_pointer_].number_of_bits)
{
// for timing simplicity, bits are modelled as happening at the end of their window
// TODO: should I account for the converse bit ordering? Or can I assume MSB first?
int bit = segment_data[_bit_pointer >> 3] & (0x80 >> (_bit_pointer&7));
_bit_pointer++;
_next_event.length.length += bit_length;
int bit = segment_data[bit_pointer_ >> 3] & (0x80 >> (bit_pointer_&7));
bit_pointer_++;
next_event_.length.length += bit_length;
if(bit) return _next_event;
if(bit) return next_event_;
}
_bit_pointer = 0;
_segment_pointer++;
bit_pointer_ = 0;
segment_pointer_++;
}
// check whether we actually reached the index hole
if(_segment_pointer == _segments.size())
if(segment_pointer_ == segments_.size())
{
_segment_pointer = 0;
_next_event.type = Track::Event::IndexHole;
segment_pointer_ = 0;
next_event_.type = Track::Event::IndexHole;
}
return _next_event;
return next_event_;
}
Storage::Time PCMTrack::seek_to(Time time_since_index_hole)
{
_segment_pointer = 0;
segment_pointer_ = 0;
// pick a common clock rate for counting time on this track and multiply up the time being sought appropriately
Time time_so_far;
time_so_far.clock_rate = NumberTheory::least_common_multiple(_next_event.length.clock_rate, time_since_index_hole.clock_rate);
time_so_far.clock_rate = NumberTheory::least_common_multiple(next_event_.length.clock_rate, time_since_index_hole.clock_rate);
time_since_index_hole.length *= time_so_far.clock_rate / time_since_index_hole.clock_rate;
time_since_index_hole.clock_rate = time_so_far.clock_rate;
while(_segment_pointer < _segments.size())
while(segment_pointer_ < segments_.size())
{
// determine how long this segment is in terms of the master clock
unsigned int clock_multiplier = time_so_far.clock_rate / _next_event.length.clock_rate;
unsigned int bit_length = ((clock_multiplier / _track_clock_rate) / _segments[_segment_pointer].length_of_a_bit.clock_rate) * _segments[_segment_pointer].length_of_a_bit.length;
unsigned int time_in_this_segment = bit_length * _segments[_segment_pointer].number_of_bits;
unsigned int clock_multiplier = time_so_far.clock_rate / next_event_.length.clock_rate;
unsigned int bit_length = ((clock_multiplier / track_clock_rate_) / segments_[segment_pointer_].length_of_a_bit.clock_rate) * segments_[segment_pointer_].length_of_a_bit.length;
unsigned int time_in_this_segment = bit_length * segments_[segment_pointer_].number_of_bits;
// if this segment goes on longer than the time being sought, end here
unsigned int time_remaining = time_since_index_hole.length - time_so_far.length;
@ -86,7 +86,7 @@ Storage::Time PCMTrack::seek_to(Time time_since_index_hole)
unsigned int time_found = time_remaining - (time_remaining % bit_length);
// resolve that into the stateful bit count
_bit_pointer = 1 + (time_remaining / bit_length);
bit_pointer_ = 1 + (time_remaining / bit_length);
// update and return the time sought to
time_so_far.length += time_found;
@ -95,7 +95,7 @@ Storage::Time PCMTrack::seek_to(Time time_since_index_hole)
// otherwise, accumulate time and keep moving
time_so_far.length += time_in_this_segment;
_segment_pointer++;
segment_pointer_++;
}
return time_since_index_hole;
}
@ -103,19 +103,19 @@ Storage::Time PCMTrack::seek_to(Time time_since_index_hole)
void PCMTrack::fix_length()
{
// find the least common multiple of all segment clock rates
_track_clock_rate = _segments[0].length_of_a_bit.clock_rate;
for(size_t c = 1; c < _segments.size(); c++)
track_clock_rate_ = segments_[0].length_of_a_bit.clock_rate;
for(size_t c = 1; c < segments_.size(); c++)
{
_track_clock_rate = NumberTheory::least_common_multiple(_track_clock_rate, _segments[c].length_of_a_bit.clock_rate);
track_clock_rate_ = NumberTheory::least_common_multiple(track_clock_rate_, segments_[c].length_of_a_bit.clock_rate);
}
// thereby determine the total length, storing it to next_event as the track-total divisor
_next_event.length.clock_rate = 0;
for(size_t c = 0; c < _segments.size(); c++)
next_event_.length.clock_rate = 0;
for(size_t c = 0; c < segments_.size(); c++)
{
unsigned int multiplier = _track_clock_rate / _segments[c].length_of_a_bit.clock_rate;
_next_event.length.clock_rate += _segments[c].length_of_a_bit.length * _segments[c].number_of_bits * multiplier;
unsigned int multiplier = track_clock_rate_ / segments_[c].length_of_a_bit.clock_rate;
next_event_.length.clock_rate += segments_[c].length_of_a_bit.length * segments_[c].number_of_bits * multiplier;
}
_segment_pointer = _bit_pointer = 0;
segment_pointer_ = bit_pointer_ = 0;
}

View File

@ -52,21 +52,21 @@ class PCMTrack: public Track {
private:
// storage for the segments that describe this track
std::vector<PCMSegment> _segments;
std::vector<PCMSegment> segments_;
// a helper to determine the overall track clock rate and it's length
void fix_length();
// the event perpetually returned; impliedly contains the length of the entire track
// as its clock rate, per the need for everything on a Track to sum to a length of 1
PCMTrack::Event _next_event;
PCMTrack::Event next_event_;
// contains the master clock rate
unsigned int _track_clock_rate;
unsigned int track_clock_rate_;
// a pointer to the first bit to consider as the next event
size_t _segment_pointer;
size_t _bit_pointer;
size_t segment_pointer_;
size_t bit_pointer_;
};
}

View File

@ -12,7 +12,7 @@ using namespace Storage::Tape::Acorn;
Parser::Parser() :
::Storage::Tape::Parser<WaveType, SymbolType>(),
_crc(0x1021, 0x0000) {}
crc_(0x1021, 0x0000) {}
int Parser::get_next_bit(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
@ -38,7 +38,7 @@ int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape)
set_error_flag();
return -1;
}
_crc.add((uint8_t)value);
crc_.add((uint8_t)value);
return value;
}
@ -56,8 +56,8 @@ int Parser::get_next_word(const std::shared_ptr<Storage::Tape::Tape> &tape)
return result;
}
void Parser::reset_crc() { _crc.reset(); }
uint16_t Parser::get_crc() { return _crc.get_value(); }
void Parser::reset_crc() { crc_.reset(); }
uint16_t Parser::get_crc() { return crc_.get_value(); }
void Parser::process_pulse(Storage::Tape::Tape::Pulse pulse)
{

View File

@ -38,7 +38,7 @@ class Parser: public Storage::Tape::Parser<WaveType, SymbolType> {
private:
void process_pulse(Storage::Tape::Tape::Pulse pulse);
void inspect_waves(const std::vector<WaveType> &waves);
NumberTheory::CRC16 _crc;
NumberTheory::CRC16 crc_;
};
}

View File

@ -13,9 +13,9 @@ using namespace Storage::Tape::Commodore;
Parser::Parser() :
Storage::Tape::Parser<WaveType, SymbolType>(),
_wave_period(0.0f),
_previous_was_high(false),
_parity_byte(0) {}
wave_period_(0.0f),
previous_was_high_(false),
parity_byte_(0) {}
/*!
Advances to the next block on the tape, treating it as a header, then consumes, parses, and returns it.
@ -193,9 +193,9 @@ void Parser::expect_byte(const std::shared_ptr<Storage::Tape::Tape> &tape, uint8
if(next_byte != value) set_error_flag();
}
void Parser::reset_parity_byte() { _parity_byte = 0; }
uint8_t Parser::get_parity_byte() { return _parity_byte; }
void Parser::add_parity_byte(uint8_t byte) { _parity_byte ^= byte; }
void Parser::reset_parity_byte() { parity_byte_ = 0; }
uint8_t Parser::get_parity_byte() { return parity_byte_; }
void Parser::add_parity_byte(uint8_t byte) { parity_byte_ ^= byte; }
/*!
Proceeds to the next word marker then returns the result of @c get_next_byte_contents.
@ -255,19 +255,19 @@ void Parser::process_pulse(Storage::Tape::Tape::Pulse pulse)
// medium: 262µs => 0.000524s cycle
// long: 342µs => 0.000684s cycle
bool is_high = pulse.type == Storage::Tape::Tape::Pulse::High;
if(!is_high && _previous_was_high)
if(!is_high && previous_was_high_)
{
if(_wave_period >= 0.000764) push_wave(WaveType::Unrecognised);
else if(_wave_period >= 0.000604) push_wave(WaveType::Long);
else if(_wave_period >= 0.000444) push_wave(WaveType::Medium);
else if(_wave_period >= 0.000284) push_wave(WaveType::Short);
if(wave_period_ >= 0.000764) push_wave(WaveType::Unrecognised);
else if(wave_period_ >= 0.000604) push_wave(WaveType::Long);
else if(wave_period_ >= 0.000444) push_wave(WaveType::Medium);
else if(wave_period_ >= 0.000284) push_wave(WaveType::Short);
else push_wave(WaveType::Unrecognised);
_wave_period = 0.0f;
wave_period_ = 0.0f;
}
_wave_period += pulse.length.get_float();
_previous_was_high = is_high;
wave_period_ += pulse.length.get_float();
previous_was_high_ = is_high;
}
/*!

View File

@ -94,7 +94,7 @@ class Parser: public Storage::Tape::Parser<WaveType, SymbolType> {
*/
void expect_byte(const std::shared_ptr<Storage::Tape::Tape> &tape, uint8_t value);
uint8_t _parity_byte;
uint8_t parity_byte_;
void reset_parity_byte();
uint8_t get_parity_byte();
void add_parity_byte(uint8_t byte);
@ -122,8 +122,8 @@ class Parser: public Storage::Tape::Parser<WaveType, SymbolType> {
a long, medium, short or unrecognised wave period.
*/
void process_pulse(Storage::Tape::Tape::Pulse pulse);
bool _previous_was_high;
float _wave_period;
bool previous_was_high_;
float wave_period_;
/*!
Per the contract with StaticAnalyser::TapeParser; produces any of a word marker, an end-of-block marker,

View File

@ -12,8 +12,8 @@ using namespace Storage::Tape::Oric;
int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape, bool use_fast_encoding)
{
_detection_mode = use_fast_encoding ? FastZero : SlowZero;
_cycle_length = 0.0f;
detection_mode_ = use_fast_encoding ? FastZero : SlowZero;
cycle_length_ = 0.0f;
int result = 0;
int bit_count = 0;
@ -21,7 +21,7 @@ int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape, bool
{
SymbolType symbol = get_next_symbol(tape);
if(!bit_count && symbol != SymbolType::Zero) continue;
_detection_mode = use_fast_encoding ? FastData : SlowData;
detection_mode_ = use_fast_encoding ? FastData : SlowData;
result |= ((symbol == SymbolType::One) ? 1 : 0) << bit_count;
bit_count++;
}
@ -31,7 +31,7 @@ int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape, bool
bool Parser::sync_and_get_encoding_speed(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
_detection_mode = Sync;
detection_mode_ = Sync;
while(!tape->is_at_end())
{
SymbolType symbol = get_next_symbol(tape);
@ -52,22 +52,22 @@ void Parser::process_pulse(Storage::Tape::Tape::Pulse pulse)
const float maximum_long_length = 0.001456f;
bool wave_is_high = pulse.type == Storage::Tape::Tape::Pulse::High;
if(!_wave_was_high && wave_is_high != _wave_was_high)
if(!wave_was_high_ && wave_is_high != wave_was_high_)
{
if(_cycle_length < maximum_short_length) push_wave(WaveType::Short);
else if(_cycle_length < maximum_medium_length) push_wave(WaveType::Medium);
else if(_cycle_length < maximum_long_length) push_wave(WaveType::Long);
if(cycle_length_ < maximum_short_length) push_wave(WaveType::Short);
else if(cycle_length_ < maximum_medium_length) push_wave(WaveType::Medium);
else if(cycle_length_ < maximum_long_length) push_wave(WaveType::Long);
else push_wave(WaveType::Unrecognised);
_cycle_length = 0.0f;
cycle_length_ = 0.0f;
}
_wave_was_high = wave_is_high;
_cycle_length += pulse.length.get_float();
wave_was_high_ = wave_is_high;
cycle_length_ += pulse.length.get_float();
}
void Parser::inspect_waves(const std::vector<WaveType> &waves)
{
switch(_detection_mode)
switch(detection_mode_)
{
case FastZero:
if(waves.empty()) return;

View File

@ -41,9 +41,9 @@ class Parser: public Storage::Tape::Parser<WaveType, SymbolType> {
FastZero,
SlowZero,
Sync
} _detection_mode;
bool _wave_was_high;
float _cycle_length;
} detection_mode_;
bool wave_was_high_;
float cycle_length_;
struct Pattern
{

View File

@ -21,23 +21,23 @@ TapePlayer::TapePlayer(unsigned int input_clock_rate) :
void Storage::Tape::Tape::seek(Time &seek_time)
{
_current_time.set_zero();
_next_time.set_zero();
while(_next_time < seek_time) get_next_pulse();
current_time_.set_zero();
next_time_.set_zero();
while(next_time_ < seek_time) get_next_pulse();
}
void Storage::Tape::Tape::reset()
{
_current_time.set_zero();
_next_time.set_zero();
current_time_.set_zero();
next_time_.set_zero();
virtual_reset();
}
Tape::Pulse Tape::get_next_pulse()
{
Tape::Pulse pulse = virtual_get_next_pulse();
_current_time = _next_time;
_next_time += pulse.length;
current_time_ = next_time_;
next_time_ += pulse.length;
return pulse;
}
@ -45,34 +45,34 @@ Tape::Pulse Tape::get_next_pulse()
void TapePlayer::set_tape(std::shared_ptr<Storage::Tape::Tape> tape)
{
_tape = tape;
tape_ = tape;
reset_timer();
get_next_pulse();
}
std::shared_ptr<Storage::Tape::Tape> TapePlayer::get_tape()
{
return _tape;
return tape_;
}
bool TapePlayer::has_tape()
{
return (bool)_tape;
return (bool)tape_;
}
void TapePlayer::get_next_pulse()
{
// get the new pulse
if(_tape)
_current_pulse = _tape->get_next_pulse();
if(tape_)
current_pulse_ = tape_->get_next_pulse();
else
{
_current_pulse.length.length = 1;
_current_pulse.length.clock_rate = 1;
_current_pulse.type = Tape::Pulse::Zero;
current_pulse_.length.length = 1;
current_pulse_.length.clock_rate = 1;
current_pulse_.type = Tape::Pulse::Zero;
}
set_next_event_time_interval(_current_pulse.length);
set_next_event_time_interval(current_pulse_.length);
}
void TapePlayer::run_for_cycles(int number_of_cycles)
@ -90,19 +90,19 @@ void TapePlayer::run_for_input_pulse()
void TapePlayer::process_next_event()
{
process_input_pulse(_current_pulse);
process_input_pulse(current_pulse_);
get_next_pulse();
}
#pragma mark - Binary Player
BinaryTapePlayer::BinaryTapePlayer(unsigned int input_clock_rate) :
TapePlayer(input_clock_rate), _motor_is_running(false)
TapePlayer(input_clock_rate), motor_is_running_(false)
{}
void BinaryTapePlayer::set_motor_control(bool enabled)
{
_motor_is_running = enabled;
motor_is_running_ = enabled;
}
void BinaryTapePlayer::set_tape_output(bool set)
@ -112,26 +112,26 @@ void BinaryTapePlayer::set_tape_output(bool set)
bool BinaryTapePlayer::get_input()
{
return _input_level;
return input_level_;
}
void BinaryTapePlayer::run_for_cycles(int number_of_cycles)
{
if(_motor_is_running) TapePlayer::run_for_cycles(number_of_cycles);
if(motor_is_running_) TapePlayer::run_for_cycles(number_of_cycles);
}
void BinaryTapePlayer::set_delegate(Delegate *delegate)
{
_delegate = delegate;
delegate_ = delegate;
}
void BinaryTapePlayer::process_input_pulse(Storage::Tape::Tape::Pulse pulse)
{
bool new_input_level = pulse.type == Tape::Pulse::Low;
if(_input_level != new_input_level)
if(input_level_ != new_input_level)
{
_input_level = new_input_level;
if(_delegate) _delegate->tape_did_change_input(this);
input_level_ = new_input_level;
if(delegate_) delegate_->tape_did_change_input(this);
}
}

View File

@ -53,13 +53,13 @@ class Tape {
virtual bool is_at_end() = 0;
/// @returns the amount of time preceeding the most recently-returned pulse.
virtual Time get_current_time() { return _current_time; }
virtual Time get_current_time() { return current_time_; }
/// Advances or reverses the tape to the last time before or at @c time from which a pulse starts.
virtual void seek(Time &time);
private:
Time _current_time, _next_time;
Time current_time_, next_time_;
virtual Pulse virtual_get_next_pulse() = 0;
virtual void virtual_reset() = 0;
@ -90,8 +90,8 @@ class TapePlayer: public TimedEventLoop {
private:
inline void get_next_pulse();
std::shared_ptr<Storage::Tape::Tape> _tape;
Tape::Pulse _current_pulse;
std::shared_ptr<Storage::Tape::Tape> tape_;
Tape::Pulse current_pulse_;
};
/*!
@ -118,10 +118,10 @@ class BinaryTapePlayer: public TapePlayer {
void set_delegate(Delegate *delegate);
protected:
Delegate *_delegate;
Delegate *delegate_;
virtual void process_input_pulse(Storage::Tape::Tape::Pulse pulse);
bool _input_level;
bool _motor_is_running;
bool input_level_;
bool motor_is_running_;
};
}

View File

@ -13,12 +13,12 @@
using namespace Storage;
TimedEventLoop::TimedEventLoop(unsigned int input_clock_rate) :
_input_clock_rate(input_clock_rate) {}
input_clock_rate_(input_clock_rate) {}
void TimedEventLoop::run_for_cycles(int number_of_cycles)
{
_cycles_until_event -= number_of_cycles;
while(_cycles_until_event <= 0)
cycles_until_event_ -= number_of_cycles;
while(cycles_until_event_ <= 0)
{
process_next_event();
}
@ -26,13 +26,13 @@ void TimedEventLoop::run_for_cycles(int number_of_cycles)
unsigned int TimedEventLoop::get_cycles_until_next_event()
{
return (unsigned int)std::max(_cycles_until_event, 0);
return (unsigned int)std::max(cycles_until_event_, 0);
}
void TimedEventLoop::reset_timer()
{
_subcycles_until_event.set_zero();
_cycles_until_event = 0;
subcycles_until_event_.set_zero();
cycles_until_event_ = 0;
}
void TimedEventLoop::reset_timer_to_offset(Time offset)
@ -49,10 +49,10 @@ void TimedEventLoop::jump_to_next_event()
void TimedEventLoop::set_next_event_time_interval(Time interval)
{
// Calculate [interval]*[input clock rate] + [subcycles until this event].
int64_t denominator = (int64_t)interval.clock_rate * (int64_t)_subcycles_until_event.clock_rate;
int64_t denominator = (int64_t)interval.clock_rate * (int64_t)subcycles_until_event_.clock_rate;
int64_t numerator =
(int64_t)_subcycles_until_event.clock_rate * (int64_t)_input_clock_rate * (int64_t)interval.length +
(int64_t)interval.clock_rate * (int64_t)_subcycles_until_event.length;
(int64_t)subcycles_until_event_.clock_rate * (int64_t)input_clock_rate_ * (int64_t)interval.length +
(int64_t)interval.clock_rate * (int64_t)subcycles_until_event_.length;
// Simplify now, to prepare for stuffing into possibly 32-bit quantities
int64_t common_divisor = NumberTheory::greatest_common_divisor(numerator % denominator, denominator);
@ -61,9 +61,9 @@ void TimedEventLoop::set_next_event_time_interval(Time interval)
// So this event will fire in the integral number of cycles from now, putting us at the remainder
// number of subcycles
_cycles_until_event = (int)(numerator / denominator);
_subcycles_until_event.length = (unsigned int)(numerator % denominator);
_subcycles_until_event.clock_rate = (unsigned int)denominator;
cycles_until_event_ = (int)(numerator / denominator);
subcycles_until_event_.length = (unsigned int)(numerator % denominator);
subcycles_until_event_.clock_rate = (unsigned int)denominator;
}
Time TimedEventLoop::get_time_into_next_event()

View File

@ -90,10 +90,9 @@ namespace Storage {
Time get_time_into_next_event();
private:
unsigned int _input_clock_rate;
int _cycles_until_event;
Time _subcycles_until_event;
Time _event_interval;
unsigned int input_clock_rate_;
int cycles_until_event_;
Time subcycles_until_event_;
};
}