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

View File

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

View File

@ -11,23 +11,23 @@
using namespace MOS; using namespace MOS;
Speaker::Speaker() : Speaker::Speaker() :
_volume(0), volume_(0),
_control_registers{0, 0, 0, 0}, control_registers_{0, 0, 0, 0},
_shift_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 counters_{2, 1, 0, 0} // create a slight phase offset for the three channels
{} {}
void Speaker::set_volume(uint8_t volume) void Speaker::set_volume(uint8_t volume)
{ {
enqueue([=]() { enqueue([=]() {
_volume = volume; volume_ = volume;
}); });
} }
void Speaker::set_control(int channel, uint8_t value) void Speaker::set_control(int channel, uint8_t value)
{ {
enqueue([=]() { 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, 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 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 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 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 // 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 // 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 // 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; // this sums the output of all three sounds channels plus a DC offset for volume;
// TODO: what's the real ratio of this stuff? // TODO: what's the real ratio of this stuff?
target[c] = ( target[c] = (
(_shift_registers[0]&1) + (shift_registers_[0]&1) +
(_shift_registers[1]&1) + (shift_registers_[1]&1) +
(_shift_registers[2]&1) + (shift_registers_[2]&1) +
((noise_pattern[_shift_registers[3] >> 3] >> (_shift_registers[3]&7))&(_control_registers[3] >> 7)&1) ((noise_pattern[shift_registers_[3] >> 3] >> (shift_registers_[3]&7))&(control_registers_[3] >> 7)&1)
) * _volume * 700 + _volume * 44; ) * volume_ * 700 + volume_ * 44;
} }
} }

View File

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

View File

@ -11,14 +11,14 @@
using namespace GI; using namespace GI;
AY38910::AY38910() : AY38910::AY38910() :
_selected_register(0), selected_register_(0),
_tone_counters{0, 0, 0}, _tone_periods{0, 0, 0}, _tone_outputs{0, 0, 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), noise_shift_register_(0xffff), noise_period_(0), noise_counter_(0), noise_output_(0),
_envelope_divider(0), _envelope_period(0), _envelope_position(0), envelope_divider_(0), envelope_period_(0), envelope_position_(0),
_master_divider(0), master_divider_(0),
_output_registers{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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 // set up envelope lookup tables
for(int c = 0; c < 16; c++) for(int c = 0; c < 16; c++)
@ -28,39 +28,39 @@ AY38910::AY38910() :
switch(c) switch(c)
{ {
case 0: case 1: case 2: case 3: case 9: case 0: case 1: case 2: case 3: case 9:
_envelope_shapes[c][p] = (p < 16) ? (p^0xf) : 0; envelope_shapes_[c][p] = (p < 16) ? (p^0xf) : 0;
_envelope_overflow_masks[c] = 0x1f; envelope_overflow_masks_[c] = 0x1f;
break; break;
case 4: case 5: case 6: case 7: case 15: case 4: case 5: case 6: case 7: case 15:
_envelope_shapes[c][p] = (p < 16) ? p : 0; envelope_shapes_[c][p] = (p < 16) ? p : 0;
_envelope_overflow_masks[c] = 0x1f; envelope_overflow_masks_[c] = 0x1f;
break; break;
case 8: case 8:
_envelope_shapes[c][p] = (p & 0xf) ^ 0xf; envelope_shapes_[c][p] = (p & 0xf) ^ 0xf;
_envelope_overflow_masks[c] = 0x00; envelope_overflow_masks_[c] = 0x00;
break; break;
case 12: case 12:
_envelope_shapes[c][p] = (p & 0xf); envelope_shapes_[c][p] = (p & 0xf);
_envelope_overflow_masks[c] = 0x00; envelope_overflow_masks_[c] = 0x00;
break; break;
case 10: case 10:
_envelope_shapes[c][p] = (p & 0xf) ^ ((p < 16) ? 0xf : 0x0); envelope_shapes_[c][p] = (p & 0xf) ^ ((p < 16) ? 0xf : 0x0);
_envelope_overflow_masks[c] = 0x00; envelope_overflow_masks_[c] = 0x00;
break; break;
case 14: case 14:
_envelope_shapes[c][p] = (p & 0xf) ^ ((p < 16) ? 0x0 : 0xf); envelope_shapes_[c][p] = (p & 0xf) ^ ((p < 16) ? 0x0 : 0xf);
_envelope_overflow_masks[c] = 0x00; envelope_overflow_masks_[c] = 0x00;
break; break;
case 11: case 11:
_envelope_shapes[c][p] = (p < 16) ? (p^0xf) : 0xf; envelope_shapes_[c][p] = (p < 16) ? (p^0xf) : 0xf;
_envelope_overflow_masks[c] = 0x1f; envelope_overflow_masks_[c] = 0x1f;
break; break;
case 13: case 13:
_envelope_shapes[c][p] = (p < 16) ? p : 0xf; envelope_shapes_[c][p] = (p < 16) ? p : 0xf;
_envelope_overflow_masks[c] = 0x1f; envelope_overflow_masks_[c] = 0x1f;
break; break;
} }
} }
@ -71,9 +71,9 @@ AY38910::AY38910() :
float root_two = sqrtf(2.0f); float root_two = sqrtf(2.0f);
for(int v = 0; v < 16; v++) 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) 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) void AY38910::get_samples(unsigned int number_of_samples, int16_t *target)
{ {
int c = 0; int c = 0;
while((_master_divider&15) && c < number_of_samples) while((master_divider_&15) && c < number_of_samples)
{ {
target[c] = _output_volume; target[c] = output_volume_;
_master_divider++; master_divider_++;
c++; c++;
} }
while(c < number_of_samples) while(c < number_of_samples)
{ {
#define step_channel(c) \ #define step_channel(c) \
if(_tone_counters[c]) _tone_counters[c]--;\ if(tone_counters_[c]) tone_counters_[c]--;\
else\ else\
{\ {\
_tone_outputs[c] ^= 1;\ tone_outputs_[c] ^= 1;\
_tone_counters[c] = _tone_periods[c];\ tone_counters_[c] = tone_periods_[c];\
} }
// update the tone channels // 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 // ... the noise generator. This recomputes the new bit repeatedly but harmlessly, only shifting
// it into the official 17 upon divider underflow. // it into the official 17 upon divider underflow.
if(_noise_counter) _noise_counter--; if(noise_counter_) noise_counter_--;
else else
{ {
_noise_counter = _noise_period; noise_counter_ = noise_period_;
_noise_output ^= _noise_shift_register&1; noise_output_ ^= noise_shift_register_&1;
_noise_shift_register |= ((_noise_shift_register ^ (_noise_shift_register >> 3))&1) << 17; noise_shift_register_ |= ((noise_shift_register_ ^ (noise_shift_register_ >> 3))&1) << 17;
_noise_shift_register >>= 1; noise_shift_register_ >>= 1;
} }
// ... and the envelope generator. Table based for pattern lookup, with a 'refill' step — a way of // ... 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. // implementing non-repeating patterns by locking them to table position 0x1f.
if(_envelope_divider) _envelope_divider--; if(envelope_divider_) envelope_divider_--;
else else
{ {
_envelope_divider = _envelope_period; envelope_divider_ = envelope_period_;
_envelope_position ++; envelope_position_ ++;
if(_envelope_position == 32) _envelope_position = _envelope_overflow_masks[_output_registers[13]]; if(envelope_position_ == 32) envelope_position_ = envelope_overflow_masks_[output_registers_[13]];
} }
evaluate_output_volume(); evaluate_output_volume();
for(int ic = 0; ic < 16 && c < number_of_samples; ic++) for(int ic = 0; ic < 16 && c < number_of_samples; ic++)
{ {
target[c] = _output_volume; target[c] = output_volume_;
c++; c++;
_master_divider++; master_divider_++;
} }
} }
_master_divider &= 15; master_divider_ &= 15;
} }
void AY38910::evaluate_output_volume() 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: // The output level for a channel is:
// 1 if neither tone nor noise is enabled; // 1 if neither tone nor noise is enabled;
// 0 if either tone or noise is enabled and its value is low. // 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. // 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 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 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 #define level(c, tone_bit, noise_bit) tone_level(c, tone_bit) & noise_level(c, noise_bit) & 1
const int channel_levels[3] = { 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 // 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) \ #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] = { const int volumes[3] = {
channel_volume(8), channel_volume(8),
@ -173,24 +173,24 @@ void AY38910::evaluate_output_volume()
#undef channel_volume #undef channel_volume
// Mix additively. // Mix additively.
_output_volume = (int16_t)( output_volume_ = (int16_t)(
_volumes[volumes[0]] * channel_levels[0] + volumes_[volumes[0]] * channel_levels[0] +
_volumes[volumes[1]] * channel_levels[1] + volumes_[volumes[1]] * channel_levels[1] +
_volumes[volumes[2]] * channel_levels[2] volumes_[volumes[2]] * channel_levels[2]
); );
} }
void AY38910::select_register(uint8_t r) void AY38910::select_register(uint8_t r)
{ {
_selected_register = r & 0xf; selected_register_ = r & 0xf;
} }
void AY38910::set_register_value(uint8_t value) void AY38910::set_register_value(uint8_t value)
{ {
_registers[_selected_register] = value; registers_[selected_register_] = value;
if(_selected_register < 14) if(selected_register_ < 14)
{ {
int selected_register = _selected_register; int selected_register = selected_register_;
enqueue([=] () { enqueue([=] () {
uint8_t masked_value = value; uint8_t masked_value = value;
switch(selected_register) switch(selected_register)
@ -201,34 +201,34 @@ void AY38910::set_register_value(uint8_t value)
int channel = selected_register >> 1; int channel = selected_register >> 1;
if(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 else
_tone_periods[channel] = (_tone_periods[channel] & ~0xff) | value; tone_periods_[channel] = (tone_periods_[channel] & ~0xff) | value;
_tone_counters[channel] = _tone_periods[channel]; tone_counters_[channel] = tone_periods_[channel];
} }
break; break;
case 6: case 6:
_noise_period = value & 0x1f; noise_period_ = value & 0x1f;
_noise_counter = _noise_period; noise_counter_ = noise_period_;
break; break;
case 11: case 11:
_envelope_period = (_envelope_period & ~0xff) | value; envelope_period_ = (envelope_period_ & ~0xff) | value;
_envelope_divider = _envelope_period; envelope_divider_ = envelope_period_;
break; break;
case 12: case 12:
_envelope_period = (_envelope_period & 0xff) | (int)(value << 8); envelope_period_ = (envelope_period_ & 0xff) | (int)(value << 8);
_envelope_divider = _envelope_period; envelope_divider_ = envelope_period_;
break; break;
case 13: case 13:
masked_value &= 0xf; masked_value &= 0xf;
_envelope_position = 0; envelope_position_ = 0;
break; break;
} }
_output_registers[selected_register] = masked_value; output_registers_[selected_register] = masked_value;
evaluate_output_volume(); evaluate_output_volume();
}); });
} }
@ -244,22 +244,22 @@ uint8_t AY38910::get_register_value()
0xe0, 0xe0, 0xe0, 0x00, 0x00, 0xf0, 0x00, 0x00 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) 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) void AY38910::set_data_input(uint8_t r)
{ {
_data_input = r; data_input_ = r;
} }
uint8_t AY38910::get_data_output() uint8_t AY38910::get_data_output()
{ {
return _data_output; return data_output_;
} }
void AY38910::set_control_lines(ControlLines control_lines) 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; 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) switch(new_state)
{ {
default: break; default: break;
case LatchAddress: select_register(_data_input); break; case LatchAddress: select_register(data_input_); break;
case Write: set_register_value(_data_input); break; case Write: set_register_value(data_input_); break;
case Read: _data_output = get_register_value(); 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); void get_samples(unsigned int number_of_samples, int16_t *target);
private: private:
int _selected_register; int selected_register_;
uint8_t _registers[16], _output_registers[16]; uint8_t registers_[16], output_registers_[16];
int _master_divider; int master_divider_;
int _tone_periods[3]; int tone_periods_[3];
int _tone_counters[3]; int tone_counters_[3];
int _tone_outputs[3]; int tone_outputs_[3];
int _noise_period; int noise_period_;
int _noise_counter; int noise_counter_;
int _noise_shift_register; int noise_shift_register_;
int _noise_output; int noise_output_;
int _envelope_period; int envelope_period_;
int _envelope_divider; int envelope_divider_;
int _envelope_position; int envelope_position_;
int _envelope_shapes[16][32]; int envelope_shapes_[16][32];
int _envelope_overflow_masks[16]; int envelope_overflow_masks_[16];
int _volumes[16]; int volumes_[16];
enum ControlState { enum ControlState {
Inactive, Inactive,
LatchAddress, LatchAddress,
Read, Read,
Write Write
} _control_state; } control_state_;
void select_register(uint8_t r); void select_register(uint8_t r);
void set_register_value(uint8_t value); void set_register_value(uint8_t value);
uint8_t get_register_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(); 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 <stdint.h>
#include "../../Processors/6502/CPU6502.hpp" #include "../../Processors/6502/CPU6502.hpp"
#include "../../Components/6532/6532.hpp"
#include "../CRTMachine.hpp" #include "../CRTMachine.hpp"
#include "PIA.hpp"
#include "Speaker.hpp"
#include "../ConfigurationTarget.hpp" #include "../ConfigurationTarget.hpp"
#include "Atari2600Inputs.h" #include "Atari2600Inputs.h"
@ -23,56 +24,6 @@ namespace Atari2600 {
const unsigned int number_of_upcoming_events = 6; const unsigned int number_of_upcoming_events = 6;
const unsigned int number_of_recorded_counters = 7; 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: class Machine:
public CPU6502::Processor<Machine>, public CPU6502::Processor<Machine>,
public CRTMachine::Machine, public CRTMachine::Machine,
@ -95,26 +46,26 @@ class Machine:
// to satisfy CRTMachine::Machine // to satisfy CRTMachine::Machine
virtual void setup_output(float aspect_ratio); virtual void setup_output(float aspect_ratio);
virtual void close_output(); virtual void close_output();
virtual std::shared_ptr<Outputs::CRT::CRT> get_crt() { return _crt; } 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::Speaker> get_speaker() { return speaker_; }
virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); } virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); }
// TODO: different rate for PAL // TODO: different rate for PAL
private: private:
uint8_t *_rom, *_romPages[4]; uint8_t *rom_, *rom_pages_[4];
size_t _rom_size; size_t rom_size_;
// the RIOT // the RIOT
PIA _mos6532; PIA mos6532_;
// playfield registers // playfield registers
uint8_t _playfieldControl; uint8_t playfield_control_;
uint8_t _playfieldColour; uint8_t playfield_colour_;
uint8_t _backgroundColour; uint8_t background_colour_;
uint8_t _playfield[41]; uint8_t playfield_[41];
// ... and derivatives // ... and derivatives
int _ballSize, _missileSize[2]; int ball_size_, missile_size_[2];
// delayed clock events // delayed clock events
enum OutputState { enum OutputState {
@ -136,12 +87,12 @@ class Machine:
int updates; int updates;
OutputState state; OutputState state;
uint8_t playfieldPixel; uint8_t playfield_pixel;
int counter; int counter;
Event() : updates(0), playfieldPixel(0) {} Event() : updates(0), playfield_pixel(0) {}
} _upcomingEvents[number_of_upcoming_events]; } upcoming_events_[number_of_upcoming_events];
unsigned int _upcomingEventsPointer; unsigned int upcoming_events_pointer_;
// object counters // object counters
struct ObjectCounter { 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 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() : count(0), pixel(0), broad_pixel(0) {}
} _objectCounter[number_of_recorded_counters][5]; } object_counter_[number_of_recorded_counters][5];
unsigned int _objectCounterPointer; unsigned int object_counter_pointer_;
// the latched playfield output // the latched playfield output
uint8_t _playfieldOutput, _nextPlayfieldOutput; uint8_t playfield_output_, next_playfield_output_;
// player registers // player registers
uint8_t _playerColour[2]; uint8_t player_colour_[2];
uint8_t _playerReflectionMask[2]; uint8_t player_reflection_mask_[2];
uint8_t _playerGraphics[2][2]; uint8_t player_graphics_[2][2];
uint8_t _playerGraphicsSelector[2]; uint8_t player_graphics_selector_[2];
bool _playerStart[2];
// object flags // object flags
bool _hasSecondCopy[2]; bool has_second_copy_[2];
bool _hasThirdCopy[2]; bool has_third_copy_[2];
bool _hasFourthCopy[2]; bool has_fourth_copy_[2];
uint8_t _objectMotion[5]; // the value stored to this counter's motion register uint8_t object_motions_[5]; // the value stored to this counter's motion register
// player + missile registers // player + missile registers
uint8_t _playerAndMissileSize[2]; uint8_t player_and_missile_size_[2];
// missile registers // missile registers
uint8_t _missileGraphicsEnable[2]; uint8_t missile_graphics_enable_[2];
bool _missileGraphicsReset[2]; bool missile_graphics_reset_[2];
// ball registers // ball registers
uint8_t _ballGraphicsEnable[2]; uint8_t ball_graphics_enable_[2];
uint8_t _ballGraphicsSelector; uint8_t ball_graphics_selector_;
// graphics output // graphics output
unsigned int _horizontalTimer; unsigned int horizontal_timer_;
bool _vSyncEnabled, _vBlankEnabled; bool vsync_enabled_, vblank_enabled_;
// horizontal motion control // horizontal motion control
uint8_t _hMoveCounter; uint8_t hmove_counter_;
uint8_t _hMoveFlags; uint8_t hmove_flags_;
// joystick state // joystick state
uint8_t _tiaInputValue[2]; uint8_t tia_input_value_[2];
// collisions // collisions
uint8_t _collisions[8]; uint8_t collisions_[8];
void output_pixels(unsigned int count); void output_pixels(unsigned int count);
uint8_t get_output_pixel(); uint8_t get_output_pixel();
void update_timers(int mask); void update_timers(int mask);
// outputs // outputs
std::shared_ptr<Outputs::CRT::CRT> _crt; std::shared_ptr<Outputs::CRT::CRT> crt_;
std::shared_ptr<Speaker> _speaker; std::shared_ptr<Speaker> speaker_;
// current mode // current mode
bool _is_pal_region; bool is_pal_region_;
// speaker backlog accumlation counter // speaker backlog accumlation counter
unsigned int _cycles_since_speaker_update; unsigned int cycles_since_speaker_update_;
void update_audio(); void update_audio();
// latched output state // latched output state
unsigned int _lastOutputStateDuration; unsigned int last_output_state_duration_;
OutputState _stateByExtendTime[2][57]; OutputState state_by_extend_time_[2][57];
OutputState *_stateByTime; OutputState *state_by_time_;
OutputState _lastOutputState; OutputState last_output_state_;
uint8_t *_outputBuffer; uint8_t *output_buffer_;
// lookup table for collision reporting // lookup table for collision reporting
uint8_t _reportedCollisions[64][8]; uint8_t reported_collisions_[64][8];
void setup_reported_collisions(); 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; using namespace Commodore::C1540;
Machine::Machine() : Machine::Machine() :
_shift_register(0), shift_register_(0),
Storage::Disk::Controller(1000000, 4, 300) 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 // attach the serial port to its VIA and vice versa
_serialPort->set_serial_port_via(_serialPortVIA); serial_port_->set_serial_port_via(serial_port_VIA_);
_serialPortVIA->set_serial_port(_serialPort); serial_port_VIA_->set_serial_port(serial_port_);
// set this instance as the delegate to receive interrupt requests from both VIAs // set this instance as the delegate to receive interrupt requests from both VIAs
_serialPortVIA->set_interrupt_delegate(this); serial_port_VIA_->set_interrupt_delegate(this);
_driveVIA.set_interrupt_delegate(this); drive_VIA_.set_interrupt_delegate(this);
_driveVIA.set_delegate(this); drive_VIA_.set_delegate(this);
// set a bit rate // set a bit rate
set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(3)); 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) 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) 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): 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(address < 0x800)
{ {
if(isReadOperation(operation)) if(isReadOperation(operation))
*value = _ram[address]; *value = ram_[address];
else else
_ram[address] = *value; ram_[address] = *value;
} }
else if(address >= 0xc000) else if(address >= 0xc000)
{ {
if(isReadOperation(operation)) if(isReadOperation(operation))
*value = _rom[address & 0x3fff]; *value = rom_[address & 0x3fff];
} }
else if(address >= 0x1800 && address <= 0x180f) else if(address >= 0x1800 && address <= 0x180f)
{ {
if(isReadOperation(operation)) if(isReadOperation(operation))
*value = _serialPortVIA->get_register(address); *value = serial_port_VIA_->get_register(address);
else else
_serialPortVIA->set_register(address, *value); serial_port_VIA_->set_register(address, *value);
} }
else if(address >= 0x1c00 && address <= 0x1c0f) else if(address >= 0x1c00 && address <= 0x1c0f)
{ {
if(isReadOperation(operation)) if(isReadOperation(operation))
*value = _driveVIA.get_register(address); *value = drive_VIA_.get_register(address);
else else
_driveVIA.set_register(address, *value); drive_VIA_.set_register(address, *value);
} }
_serialPortVIA->run_for_cycles(1); serial_port_VIA_->run_for_cycles(1);
_driveVIA.run_for_cycles(1); drive_VIA_.run_for_cycles(1);
return 1; return 1;
} }
void Machine::set_rom(const uint8_t *rom) 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) 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) void Machine::run_for_cycles(int number_of_cycles)
{ {
CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles);
set_motor_on(_driveVIA.get_motor_enabled()); set_motor_on(drive_VIA_.get_motor_enabled());
if(_driveVIA.get_motor_enabled()) // TODO: motor speed up/down if(drive_VIA_.get_motor_enabled()) // TODO: motor speed up/down
Storage::Disk::Controller::run_for_cycles(number_of_cycles); 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) void Machine::mos6522_did_change_interrupt_status(void *mos6522)
{ {
// both VIAs are connected to the IRQ line // 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 #pragma mark - Disk drive
void Machine::process_input_bit(int value, unsigned int cycles_since_index_hole) void Machine::process_input_bit(int value, unsigned int cycles_since_index_hole)
{ {
_shift_register = (_shift_register << 1) | value; shift_register_ = (shift_register_ << 1) | value;
if((_shift_register & 0x3ff) == 0x3ff) if((shift_register_ & 0x3ff) == 0x3ff)
{ {
_driveVIA.set_sync_detected(true); 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 bit_window_offset_ = -1; // i.e. this bit isn't the first within a data window, but the next might be
} }
else else
{ {
_driveVIA.set_sync_detected(false); drive_VIA_.set_sync_detected(false);
} }
_bit_window_offset++; bit_window_offset_++;
if(_bit_window_offset == 8) if(bit_window_offset_ == 8)
{ {
_driveVIA.set_data_input((uint8_t)_shift_register); drive_VIA_.set_data_input((uint8_t)shift_register_);
_bit_window_offset = 0; bit_window_offset_ = 0;
if(_driveVIA.get_should_set_overflow()) if(drive_VIA_.get_should_set_overflow())
{ {
set_overflow_line(true); set_overflow_line(true);
} }
@ -171,12 +153,12 @@ void Machine::drive_via_did_set_data_density(void *driveVIA, int density)
#pragma mark - SerialPortVIA #pragma mark - SerialPortVIA
SerialPortVIA::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) uint8_t SerialPortVIA::get_port_input(Port port)
{ {
if(port) return _portB; if(port) return port_b_;
return 0xff; return 0xff;
} }
@ -184,10 +166,10 @@ void SerialPortVIA::set_port_output(Port port, uint8_t value, uint8_t mask)
{ {
if(port) if(port)
{ {
std::shared_ptr<::Commodore::Serial::Port> serialPort = _serialPort.lock(); std::shared_ptr<::Commodore::Serial::Port> serialPort = serial_port_.lock();
if(serialPort) { if(serialPort) {
_attention_acknowledge_level = !(value&0x10); attention_acknowledge_level_ = !(value&0x10);
_data_level_output = (value&0x02); data_level_output_ = (value&0x02);
serialPort->set_output(::Commodore::Serial::Line::Clock, (::Commodore::Serial::LineLevel)!(value&0x08)); serialPort->set_output(::Commodore::Serial::Line::Clock, (::Commodore::Serial::LineLevel)!(value&0x08));
update_data_line(); update_data_line();
@ -200,31 +182,30 @@ void SerialPortVIA::set_serial_line_state(::Commodore::Serial::Line line, bool v
switch(line) switch(line)
{ {
default: break; default: break;
case ::Commodore::Serial::Line::Data: _portB = (_portB & ~0x01) | (value ? 0x00 : 0x01); break; case ::Commodore::Serial::Line::Data: port_b_ = (port_b_ & ~0x01) | (value ? 0x00 : 0x01); break;
case ::Commodore::Serial::Line::Clock: _portB = (_portB & ~0x04) | (value ? 0x00 : 0x04); break; case ::Commodore::Serial::Line::Clock: port_b_ = (port_b_ & ~0x04) | (value ? 0x00 : 0x04); break;
case ::Commodore::Serial::Line::Attention: case ::Commodore::Serial::Line::Attention:
_attention_level_input = !value; attention_level_input_ = !value;
_portB = (_portB & ~0x80) | (value ? 0x00 : 0x80); port_b_ = (port_b_ & ~0x80) | (value ? 0x00 : 0x80);
set_control_line_input(Port::A, Line::One, !value); set_control_line_input(Port::A, Line::One, !value);
update_data_line(); update_data_line();
break; 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() 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) 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" // "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, serialPort->set_output(::Commodore::Serial::Line::Data,
(::Commodore::Serial::LineLevel)(!_data_level_output (::Commodore::Serial::LineLevel)(!data_level_output_ && (attention_level_input_ != attention_acknowledge_level_)));
&& (_attention_level_input != _attention_acknowledge_level)));
} }
} }
@ -232,35 +213,35 @@ void SerialPortVIA::update_data_line()
void DriveVIA::set_delegate(Delegate *delegate) void DriveVIA::set_delegate(Delegate *delegate)
{ {
_delegate = delegate; delegate_ = delegate;
} }
// write protect tab uncovered // 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) { 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) { 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) { void DriveVIA::set_data_input(uint8_t value) {
_port_a = value; port_a_ = value;
} }
bool DriveVIA::get_should_set_overflow() { bool DriveVIA::get_should_set_overflow() {
return _should_set_overflow; return should_set_overflow_;
} }
bool DriveVIA::get_motor_enabled() { bool DriveVIA::get_motor_enabled() {
return _drive_motor; return drive_motor_;
} }
void DriveVIA::set_control_line_output(Port port, Line line, bool value) { void DriveVIA::set_control_line_output(Port port, Line line, bool value) {
if(port == Port::A && line == Line::Two) { 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) if(port)
{ {
// record drive motor state // record drive motor state
_drive_motor = !!(value&4); drive_motor_ = !!(value&4);
// check for a head step // 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(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 // check for a change in density
int density_difference = (_previous_port_b_output^value) & (3 << 5); int density_difference = (previous_port_b_output_^value) & (3 << 5);
if(density_difference && _delegate) 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 // TODO: something with the drive LED
// printf("LED: %s\n", value&8 ? "On" : "Off"); // printf("LED: %s\n", value&8 ? "On" : "Off");
_previous_port_b_output = value; previous_port_b_output_ = value;
} }
} }
#pragma mark - SerialPort #pragma mark - SerialPort
void SerialPort::set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level) { 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); if(serialPortVIA) serialPortVIA->set_serial_line_state(line, (bool)level);
} }
void SerialPort::set_serial_port_via(std::shared_ptr<SerialPortVIA> serialPortVIA) { void SerialPort::set_serial_port_via(const std::shared_ptr<SerialPortVIA> &serialPortVIA) {
_serialPortVIA = serialPortVIA; serial_port_VIA_ = serialPortVIA;
} }

View File

@ -41,17 +41,17 @@ class SerialPortVIA: public MOS::MOS6522<SerialPortVIA>, public MOS::MOS6522IRQD
SerialPortVIA(); 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_port_output(Port, uint8_t value, uint8_t mask);
void set_serial_line_state(::Commodore::Serial::Line line, bool value); 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: private:
uint8_t _portB; uint8_t port_b_;
std::weak_ptr<::Commodore::Serial::Port> _serialPort; std::weak_ptr<::Commodore::Serial::Port> serial_port_;
bool _attention_acknowledge_level, _attention_level_input, _data_level_output; bool attention_acknowledge_level_, attention_level_input_, data_level_output_;
void update_data_line(); 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_step_head(void *driveVIA, int direction) = 0;
virtual void drive_via_did_set_data_density(void *driveVIA, int density) = 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; 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); uint8_t get_port_input(Port port);
void set_sync_detected(bool sync_detected); void set_sync_detected(bool);
void set_data_input(uint8_t value); void set_data_input(uint8_t);
bool get_should_set_overflow(); bool get_should_set_overflow();
bool get_motor_enabled(); 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: private:
uint8_t _port_b, _port_a; uint8_t port_b_, port_a_;
bool _should_set_overflow; bool should_set_overflow_;
bool _drive_motor; bool drive_motor_;
uint8_t _previous_port_b_output; uint8_t previous_port_b_output_;
Delegate *_delegate; Delegate *delegate_;
}; };
/*! /*!
@ -109,11 +109,11 @@ class DriveVIA: public MOS::MOS6522<DriveVIA>, public MOS::MOS6522IRQDelegate {
*/ */
class SerialPort : public ::Commodore::Serial::Port { class SerialPort : public ::Commodore::Serial::Port {
public: public:
void set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level); void set_input(::Commodore::Serial::Line, ::Commodore::Serial::LineLevel);
void set_serial_port_via(std::shared_ptr<SerialPortVIA> serialPortVIA); void set_serial_port_via(const std::shared_ptr<SerialPortVIA> &);
private: 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); void drive_via_did_set_data_density(void *driveVIA, int density);
private: private:
uint8_t _ram[0x800]; uint8_t ram_[0x800];
uint8_t _rom[0x4000]; uint8_t rom_[0x4000];
std::shared_ptr<SerialPortVIA> _serialPortVIA; std::shared_ptr<SerialPortVIA> serial_port_VIA_;
std::shared_ptr<SerialPort> _serialPort; std::shared_ptr<SerialPort> serial_port_;
DriveVIA _driveVIA; 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_input_bit(int value, unsigned int cycles_since_index_hole);
virtual void process_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) 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++) for(int line = (int)ServiceRequest; line <= (int)Reset; line++)
{ {
// the addition of a new device may change the line output... // the addition of a new device may change the line output...
set_line_output_did_change((Line)line); set_line_output_did_change((Line)line);
// ... but the new device will need to be told the current state regardless // ... 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 // i.e. I believe these lines to be open collector
LineLevel new_line_level = High; 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(); std::shared_ptr<Port> locked_port = port.lock();
if(locked_port) if(locked_port)
@ -55,11 +55,11 @@ void Bus::set_line_output_did_change(Line line)
} }
// post an update only if one occurred // 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(); std::shared_ptr<Port> locked_port = port.lock();
if(locked_port) if(locked_port)
@ -74,20 +74,20 @@ void Bus::set_line_output_did_change(Line line)
void DebugPort::set_input(Line line, LineLevel value) 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"); 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 else
{ {
if(line == Line::Clock && value) 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--; incoming_count_--;
if(_incoming_count == 0) printf("[Bus] Observed %02x\n", _incoming_byte); if(incoming_count_ == 0) printf("[Bus] Observed %02x\n", incoming_byte_);
} }
} }

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,9 @@
#include "../CRTMachine.hpp" #include "../CRTMachine.hpp"
#include "../Typer.hpp" #include "../Typer.hpp"
#include "Plus3.hpp" #include "Plus3.hpp"
#include "Speaker.hpp"
#include "Tape.hpp"
#include "Interrupts.hpp"
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
@ -35,15 +38,6 @@ enum ROMSlot: uint8_t {
ROMSlotOS, ROMSlotDFS, ROMSlotADFS ROMSlotOS, ROMSlotDFS, ROMSlotADFS
}; };
enum Interrupt: uint8_t {
PowerOnReset = 0x02,
DisplayEnd = 0x04,
RealTimeClock = 0x08,
ReceiveDataFull = 0x10,
TransmitDataEmpty = 0x20,
HighToneDetect = 0x40
};
enum Key: uint16_t { enum Key: uint16_t {
KeySpace = 0x0000 | 0x08, KeyCopy = 0x0000 | 0x02, KeyRight = 0x0000 | 0x01, KeySpace = 0x0000 | 0x08, KeyCopy = 0x0000 | 0x02, KeyRight = 0x0000 | 0x01,
KeyDelete = 0x0010 | 0x08, KeyReturn = 0x0010 | 0x04, KeyDown = 0x0010 | 0x02, KeyLeft = 0x0010 | 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, 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. @abstract Represents an Acorn Electron.
@ -152,7 +80,7 @@ class Machine:
void set_key_state(uint16_t key, bool isPressed); void set_key_state(uint16_t key, bool isPressed);
void clear_all_keys(); 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 // to satisfy ConfigurationTarget::Machine
void configure_as_target(const StaticAnalyser::Target &target); void configure_as_target(const StaticAnalyser::Target &target);
@ -164,8 +92,8 @@ class Machine:
// to satisfy CRTMachine::Machine // to satisfy CRTMachine::Machine
virtual void setup_output(float aspect_ratio); virtual void setup_output(float aspect_ratio);
virtual void close_output(); virtual void close_output();
virtual std::shared_ptr<Outputs::CRT::CRT> get_crt() { return _crt; } 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::Speaker> get_speaker() { return speaker_; }
virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); } virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); }
// to satisfy Tape::Delegate // to satisfy Tape::Delegate
@ -189,25 +117,25 @@ class Machine:
inline void evaluate_interrupts(); inline void evaluate_interrupts();
// Things that directly constitute the memory map. // Things that directly constitute the memory map.
uint8_t _roms[16][16384]; uint8_t roms_[16][16384];
bool _rom_write_masks[16]; bool rom_write_masks_[16];
uint8_t _os[16384], _ram[32768]; uint8_t os_[16384], ram_[32768];
std::vector<uint8_t> _dfs, _adfs; std::vector<uint8_t> dfs_, adfs_;
// Things affected by registers, explicitly or otherwise. // Things affected by registers, explicitly or otherwise.
uint8_t _interrupt_status, _interrupt_control; uint8_t interrupt_status_, interrupt_control_;
uint8_t _palette[16]; uint8_t palette_[16];
uint8_t _key_states[14]; uint8_t key_states_[14];
ROMSlot _active_rom; ROMSlot active_rom_;
bool _keyboard_is_active, _basic_is_active; bool keyboard_is_active_, basic_is_active_;
uint8_t _screen_mode; uint8_t screen_mode_;
uint16_t _screenModeBaseAddress; uint16_t screen_mode_base_address_;
uint16_t _startScreenAddress; uint16_t start_screen_address_;
// Counters related to simultaneous subsystems // Counters related to simultaneous subsystems
unsigned int _frameCycles, _displayOutputPosition; unsigned int frame_cycles_, display_output_position_;
unsigned int _audioOutputPosition, _audioOutputPositionError; unsigned int audio_output_position_, audio_output_position_error_;
uint8_t _phase; uint8_t phase_;
struct { struct {
uint16_t forty1bpp[256]; uint16_t forty1bpp[256];
@ -215,30 +143,30 @@ class Machine:
uint32_t eighty1bpp[256]; uint32_t eighty1bpp[256];
uint16_t eighty2bpp[256]; uint16_t eighty2bpp[256];
uint8_t eighty4bpp[256]; uint8_t eighty4bpp[256];
} _paletteTables; } palette_tables_;
// Display generation. // Display generation.
uint16_t _startLineAddress, _currentScreenAddress; uint16_t start_line_address_, current_screen_address_;
int _current_pixel_line, _current_pixel_column, _current_character_row; int current_pixel_line_, current_pixel_column_, current_character_row_;
uint8_t _last_pixel_byte; uint8_t last_pixel_byte_;
bool _isBlankLine; bool is_blank_line_;
// CRT output // CRT output
uint8_t *_current_output_target, *_initial_output_target; uint8_t *current_output_target_, *initial_output_target_;
unsigned int _current_output_divider; unsigned int current_output_divider_;
// Tape // Tape
Tape _tape; Tape tape_;
bool _use_fast_tape_hack; bool use_fast_tape_hack_;
bool _fast_load_is_in_data; bool fast_load_is_in_data_;
// Disk // Disk
std::unique_ptr<Plus3> _plus3; std::unique_ptr<Plus3> plus3_;
bool is_holding_shift_; bool is_holding_shift_;
// Outputs // Outputs
std::shared_ptr<Outputs::CRT::CRT> _crt; std::shared_ptr<Outputs::CRT::CRT> crt_;
std::shared_ptr<Speaker> _speaker; std::shared_ptr<Speaker> speaker_;
bool speaker_is_enabled_; 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 // 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 // do anything until at least the second, regardless of machine
if(!keyboard_read_count_) keyboard_read_count_++; if(!keyboard_read_count_) keyboard_read_count_++;
else if(!_typer->type_next_character()) else if(!typer_->type_next_character())
{ {
clear_all_keys(); clear_all_keys();
_typer.reset(); typer_.reset();
} }
} }

View File

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

View File

@ -35,31 +35,31 @@ class Typer {
const char EndString = 0x03; // i.e. ASCII end of text const char EndString = 0x03; // i.e. ASCII end of text
private: private:
char *_string; char *string_;
int _frequency; int frequency_;
int _counter; int counter_;
int _phase; int phase_;
Delegate *_delegate; Delegate *delegate_;
size_t _string_pointer; size_t string_pointer_;
}; };
class TypeRecipient: public Typer::Delegate { class TypeRecipient: public Typer::Delegate {
public: public:
void set_typer_for_string(const char *string) 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) void typer_reset(Typer *typer)
{ {
clear_all_keys(); clear_all_keys();
_typer.reset(); typer_.reset();
} }
protected: protected:
virtual int get_typer_delay() { return 0; } virtual int get_typer_delay() { return 0; }
virtual int get_typer_frequency() { 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 */; }; 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; };
4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD69F921D98760000243FE1 /* AcornADF.cpp */; }; 4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD69F921D98760000243FE1 /* AcornADF.cpp */; };
4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE77A2C1D84ADFB00BC3827 /* File.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 */; }; 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; };
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; }; 4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; };
4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = "<group>"; };
@ -1042,8 +1053,11 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4B2E2D971C3A06EC00138695 /* Atari2600.cpp */, 4B2E2D971C3A06EC00138695 /* Atari2600.cpp */,
4B2E2D981C3A06EC00138695 /* Atari2600.hpp */, 4BEA52641DF3472B007E74F2 /* Speaker.cpp */,
4B2E2D991C3A06EC00138695 /* Atari2600Inputs.h */, 4B2E2D991C3A06EC00138695 /* Atari2600Inputs.h */,
4B2E2D981C3A06EC00138695 /* Atari2600.hpp */,
4BEA52651DF3472B007E74F2 /* Speaker.hpp */,
4BEA52671DF34909007E74F2 /* PIA.hpp */,
); );
path = Atari2600; path = Atari2600;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1052,10 +1066,15 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4B2E2D9B1C3A070400138695 /* Electron.cpp */, 4B2E2D9B1C3A070400138695 /* Electron.cpp */,
4B2E2D9C1C3A070400138695 /* Electron.hpp */,
4B30512E1D98ACC600B4FED8 /* Plus3.cpp */, 4B30512E1D98ACC600B4FED8 /* Plus3.cpp */,
4B30512F1D98ACC600B4FED8 /* Plus3.hpp */, 4BEA52611DF339D7007E74F2 /* Speaker.cpp */,
4BEA525D1DF33323007E74F2 /* Tape.cpp */,
4BC8A62B1DCE60E000DAC693 /* Typer.cpp */, 4BC8A62B1DCE60E000DAC693 /* Typer.cpp */,
4B2E2D9C1C3A070400138695 /* Electron.hpp */,
4BEA52601DF3343A007E74F2 /* Interrupts.hpp */,
4B30512F1D98ACC600B4FED8 /* Plus3.hpp */,
4BEA52621DF339D7007E74F2 /* Speaker.hpp */,
4BEA525F1DF333D8007E74F2 /* Tape.hpp */,
); );
name = Electron; name = Electron;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2314,6 +2333,7 @@
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */, 4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */,
4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */,
4BC8A62D1DCE60E000DAC693 /* Typer.cpp in Sources */, 4BC8A62D1DCE60E000DAC693 /* Typer.cpp in Sources */,
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */, 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */,
4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */, 4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */,
@ -2335,6 +2355,7 @@
4B2A332F1DB86869002876E3 /* OricOptionsPanel.swift in Sources */, 4B2A332F1DB86869002876E3 /* OricOptionsPanel.swift in Sources */,
4B2A53A11D117D36003C6002 /* CSAtari2600.mm in Sources */, 4B2A53A11D117D36003C6002 /* CSAtari2600.mm in Sources */,
4BF829661D8F732B001BAE39 /* Disk.cpp in Sources */, 4BF829661D8F732B001BAE39 /* Disk.cpp in Sources */,
4BEA52631DF339D7007E74F2 /* Speaker.cpp in Sources */,
4BC5E4921D7ED365008CF980 /* StaticAnalyser.cpp in Sources */, 4BC5E4921D7ED365008CF980 /* StaticAnalyser.cpp in Sources */,
4BC830D11D6E7C690000A26F /* Tape.cpp in Sources */, 4BC830D11D6E7C690000A26F /* Tape.cpp in Sources */,
4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */, 4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */,
@ -2378,6 +2399,7 @@
4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */, 4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */,
4BCA6CC81D9DD9F000C2D7B2 /* CommodoreROM.cpp in Sources */, 4BCA6CC81D9DD9F000C2D7B2 /* CommodoreROM.cpp in Sources */,
4BA22B071D8817CE0008C640 /* Disk.cpp in Sources */, 4BA22B071D8817CE0008C640 /* Disk.cpp in Sources */,
4BEA52661DF3472B007E74F2 /* Speaker.cpp in Sources */,
4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */, 4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */,
4B4C83701D4F623200CD541F /* D64.cpp in Sources */, 4B4C83701D4F623200CD541F /* D64.cpp in Sources */,
4B5073071DDD3B9400C48FBD /* ArrayBuilder.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) float get_ideal_clock_rate_in_range(float minimum, float maximum)
{ {
// return twice the cut off, if applicable // 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 // 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 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 // otherwise, return the maximum
return maximum; return maximum;
@ -54,29 +54,29 @@ class Speaker {
void set_output_rate(float cycles_per_second, int buffer_size) void set_output_rate(float cycles_per_second, int buffer_size)
{ {
_output_cycles_per_second = cycles_per_second; output_cycles_per_second_ = cycles_per_second;
if(_buffer_size != buffer_size) if(buffer_size_ != buffer_size)
{ {
_buffer_in_progress.reset(new int16_t[buffer_size]); buffer_in_progress_.reset(new int16_t[buffer_size]);
_buffer_size = buffer_size; buffer_size_ = buffer_size;
} }
set_needs_updated_filter_coefficients(); set_needs_updated_filter_coefficients();
} }
void set_output_quality(int number_of_taps) 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(); set_needs_updated_filter_coefficients();
} }
void set_delegate(Delegate *delegate) void set_delegate(Delegate *delegate)
{ {
_delegate = delegate; delegate_ = delegate;
} }
void set_input_rate(float cycles_per_second) 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(); set_needs_updated_filter_coefficients();
} }
@ -85,19 +85,19 @@ class Speaker {
*/ */
void set_high_frequency_cut_off(float high_frequency) 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(); 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. Ensures any deferred processing occurs now.
*/ */
void flush() void flush()
{ {
std::shared_ptr<std::list<std::function<void(void)>>> queued_functions = _queued_functions; std::shared_ptr<std::list<std::function<void(void)>>> queued_functions = queued_functions_;
_queued_functions.reset(); queued_functions_.reset();
_queue->enqueue([queued_functions] { _queue->enqueue([queued_functions] {
for(auto function : *queued_functions) for(auto function : *queued_functions)
{ {
@ -109,24 +109,24 @@ class Speaker {
protected: protected:
void enqueue(std::function<void(void)> function) void enqueue(std::function<void(void)> function)
{ {
if(!_queued_functions) _queued_functions.reset(new std::list<std::function<void(void)>>); if(!queued_functions_) queued_functions_.reset(new std::list<std::function<void(void)>>);
_queued_functions->push_back(function); 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; std::unique_ptr<int16_t> buffer_in_progress_;
float _high_frequency_cut_off; float high_frequency_cut_off_;
int _buffer_size; int buffer_size_;
int _buffer_in_progress_pointer; int buffer_in_progress_pointer_;
int _number_of_taps, _requested_number_of_taps; int number_of_taps_, requested_number_of_taps_;
bool _coefficients_are_dirty; bool coefficients_are_dirty_;
Delegate *_delegate; 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() void set_needs_updated_filter_coefficients()
{ {
_coefficients_are_dirty = true; coefficients_are_dirty_ = true;
} }
void get_samples(unsigned int quantity, int16_t *target) {} void get_samples(unsigned int quantity, int16_t *target) {}
@ -160,26 +160,26 @@ template <class T> class Filter: public Speaker {
{ {
enqueue([=]() { enqueue([=]() {
unsigned int cycles_remaining = input_cycles; 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 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) 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; 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]); static_cast<T *>(this)->get_samples(cycles_to_read, &buffer_in_progress_.get()[buffer_in_progress_pointer_]);
_buffer_in_progress_pointer += cycles_to_read; buffer_in_progress_pointer_ += cycles_to_read;
// announce to delegate if full // announce to delegate if full
if(_buffer_in_progress_pointer == _buffer_size) if(buffer_in_progress_pointer_ == buffer_size_)
{ {
_buffer_in_progress_pointer = 0; buffer_in_progress_pointer_ = 0;
if(_delegate) 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 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) while(cycles_remaining)
{ {
unsigned int cycles_to_read = (unsigned int)std::min((int)cycles_remaining, _number_of_taps - _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]); static_cast<T *>(this)->get_samples(cycles_to_read, &input_buffer_.get()[input_buffer_depth_]);
cycles_remaining -= cycles_to_read; 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_.get()[buffer_in_progress_pointer_] = filter_->apply(input_buffer_.get());
_buffer_in_progress_pointer++; buffer_in_progress_pointer_++;
// announce to delegate if full // announce to delegate if full
if(_buffer_in_progress_pointer == _buffer_size) if(buffer_in_progress_pointer_ == buffer_size_)
{ {
_buffer_in_progress_pointer = 0; buffer_in_progress_pointer_ = 0;
if(_delegate) 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 // 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 // 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. // anything. Otherwise skip as required to get to the next sample batch and don't expect to reuse.
uint64_t steps = _stepper->step(); uint64_t steps = stepper_->step();
if(steps < _number_of_taps) if(steps < number_of_taps_)
{ {
int16_t *input_buffer = _input_buffer.get(); int16_t *input_buffer = input_buffer_.get();
memmove(input_buffer, &input_buffer[steps], sizeof(int16_t) * ((size_t)_number_of_taps - (size_t)steps)); memmove(input_buffer, &input_buffer[steps], sizeof(int16_t) * ((size_t)number_of_taps_ - (size_t)steps));
_input_buffer_depth -= steps; input_buffer_depth_ -= steps;
} }
else else
{ {
if(steps > _number_of_taps) if(steps > number_of_taps_)
static_cast<T *>(this)->skip_samples((unsigned int)steps - (unsigned int)_number_of_taps); static_cast<T *>(this)->skip_samples((unsigned int)steps - (unsigned int)number_of_taps_);
_input_buffer_depth = 0; input_buffer_depth_ = 0;
} }
} }
} }
@ -241,44 +241,44 @@ template <class T> class Filter: public Speaker {
} }
private: private:
std::unique_ptr<SignalProcessing::Stepper> _stepper; std::unique_ptr<SignalProcessing::Stepper> stepper_;
std::unique_ptr<SignalProcessing::FIRFilter> _filter; std::unique_ptr<SignalProcessing::FIRFilter> filter_;
std::unique_ptr<int16_t> _input_buffer; std::unique_ptr<int16_t> input_buffer_;
int _input_buffer_depth; int input_buffer_depth_;
void update_filter_coefficients() void update_filter_coefficients()
{ {
// make a guess at a good number of taps if this hasn't been provided explicitly // 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 else
{ {
_number_of_taps = (int)ceilf((_input_cycles_per_second + _output_cycles_per_second) / _output_cycles_per_second); number_of_taps_ = (int)ceilf((input_cycles_per_second_ + output_cycles_per_second_) / output_cycles_per_second_);
_number_of_taps *= 2; number_of_taps_ *= 2;
_number_of_taps |= 1; number_of_taps_ |= 1;
} }
_coefficients_are_dirty = false; coefficients_are_dirty_ = false;
_buffer_in_progress_pointer = 0; 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; 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 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_.reset(new int16_t[number_of_taps_]);
_input_buffer_depth = 0; 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; std::vector<uint8_t> data;
}; };
const std::list<Segment> &get_segments() { return _segments; } const std::list<Segment> &get_segments() { return segments_; }
protected: protected:
std::list<Segment> _segments; std::list<Segment> segments_;
}; };
} }

View File

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

View File

@ -46,5 +46,5 @@ PRG::PRG(const char *file_name)
if(!Storage::Cartridge::Encodings::CommodoreROM::isROM(contents)) if(!Storage::Cartridge::Encodings::CommodoreROM::isROM(contents))
throw ErrorNotROM; 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; using namespace Storage;
DigitalPhaseLockedLoop::DigitalPhaseLockedLoop(int clocks_per_bit, int tolerance, size_t length_of_history) : DigitalPhaseLockedLoop::DigitalPhaseLockedLoop(int clocks_per_bit, int tolerance, size_t length_of_history) :
_clocks_per_bit(clocks_per_bit), clocks_per_bit_(clocks_per_bit),
_tolerance(tolerance), tolerance_(tolerance),
_phase(0), phase_(0),
_window_length(clocks_per_bit), 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) void DigitalPhaseLockedLoop::run_for_cycles(int number_of_cycles)
{ {
_phase += number_of_cycles; phase_ += number_of_cycles;
if(_phase >= _window_length) 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 // 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++) 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; window_was_filled_ = false;
_phase %= _window_length; phase_ %= window_length_;
} }
} }
void DigitalPhaseLockedLoop::add_pulse() void DigitalPhaseLockedLoop::add_pulse()
{ {
if(!_window_was_filled) if(!window_was_filled_)
{ {
if(_delegate) _delegate->digital_phase_locked_loop_output_bit(1); if(delegate_) delegate_->digital_phase_locked_loop_output_bit(1);
_window_was_filled = true; window_was_filled_ = true;
post_phase_error(_phase - (_window_length >> 1)); post_phase_error(phase_ - (window_length_ >> 1));
} }
} }
void DigitalPhaseLockedLoop::post_phase_error(int error) void DigitalPhaseLockedLoop::post_phase_error(int error)
{ {
// use a simple spring mechanism as a lowpass filter for phase // 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 // 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(); size_t phase_error_history_size = phase_error_history->size();
(*phase_error_history)[_phase_error_pointer] = error; (*phase_error_history)[phase_error_pointer_] = error;
_phase_error_pointer = (_phase_error_pointer + 1)%phase_error_history_size; phase_error_pointer_ = (phase_error_pointer_ + 1)%phase_error_history_size;
int total_error = 0; int total_error = 0;
for(size_t c = 0; c < phase_error_history_size; c++) 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]; total_error += (*phase_error_history)[c];
} }
int denominator = (int)(phase_error_history_size * 4); int denominator = (int)(phase_error_history_size * 4);
_window_length += (total_error + (denominator >> 1)) / denominator; 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_ = 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) void set_delegate(Delegate *delegate)
{ {
_delegate = delegate; delegate_ = delegate;
} }
private: private:
Delegate *_delegate; Delegate *delegate_;
void post_phase_error(int error); void post_phase_error(int error);
std::unique_ptr<std::vector<int>> _phase_error_history; std::unique_ptr<std::vector<int>> phase_error_history_;
size_t _phase_error_pointer; size_t phase_error_pointer_;
int _phase; int phase_;
int _window_length; int window_length_;
bool _window_was_filled; bool window_was_filled_;
int _clocks_per_bit; int clocks_per_bit_;
int _tolerance; int tolerance_;
}; };
} }

View File

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

View File

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

View File

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

View File

@ -13,7 +13,7 @@ using namespace Storage::Disk;
PCMTrack::PCMTrack(std::vector<PCMSegment> segments) PCMTrack::PCMTrack(std::vector<PCMSegment> segments)
{ {
_segments = std::move(segments); segments_ = std::move(segments);
fix_length(); fix_length();
} }
@ -21,7 +21,7 @@ PCMTrack::PCMTrack(PCMSegment segment)
{ {
segment.length_of_a_bit.length = 1; segment.length_of_a_bit.length = 1;
segment.length_of_a_bit.clock_rate = 1; segment.length_of_a_bit.clock_rate = 1;
_segments.push_back(std::move(segment)); segments_.push_back(std::move(segment));
fix_length(); 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 // 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 // to be a flux transition
_next_event.type = Track::Event::FluxTransition; next_event_.type = Track::Event::FluxTransition;
_next_event.length.length = 0; next_event_.length.length = 0;
while(_segment_pointer < _segments.size()) while(segment_pointer_ < segments_.size())
{ {
unsigned int clock_multiplier = _track_clock_rate / _segments[_segment_pointer].length_of_a_bit.clock_rate; 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 bit_length = clock_multiplier * segments_[segment_pointer_].length_of_a_bit.length;
const uint8_t *segment_data = &_segments[_segment_pointer].data[0]; const uint8_t *segment_data = &segments_[segment_pointer_].data[0];
while(_bit_pointer < _segments[_segment_pointer].number_of_bits) while(bit_pointer_ < segments_[segment_pointer_].number_of_bits)
{ {
// for timing simplicity, bits are modelled as happening at the end of their window // 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? // 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)); int bit = segment_data[bit_pointer_ >> 3] & (0x80 >> (bit_pointer_&7));
_bit_pointer++; bit_pointer_++;
_next_event.length.length += bit_length; next_event_.length.length += bit_length;
if(bit) return _next_event; if(bit) return next_event_;
} }
_bit_pointer = 0; bit_pointer_ = 0;
_segment_pointer++; segment_pointer_++;
} }
// check whether we actually reached the index hole // check whether we actually reached the index hole
if(_segment_pointer == _segments.size()) if(segment_pointer_ == segments_.size())
{ {
_segment_pointer = 0; segment_pointer_ = 0;
_next_event.type = Track::Event::IndexHole; next_event_.type = Track::Event::IndexHole;
} }
return _next_event; return next_event_;
} }
Storage::Time PCMTrack::seek_to(Time time_since_index_hole) 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 // pick a common clock rate for counting time on this track and multiply up the time being sought appropriately
Time time_so_far; 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.length *= time_so_far.clock_rate / time_since_index_hole.clock_rate;
time_since_index_hole.clock_rate = time_so_far.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 // 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 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 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 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 // 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; 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); unsigned int time_found = time_remaining - (time_remaining % bit_length);
// resolve that into the stateful bit count // 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 // update and return the time sought to
time_so_far.length += time_found; 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 // otherwise, accumulate time and keep moving
time_so_far.length += time_in_this_segment; time_so_far.length += time_in_this_segment;
_segment_pointer++; segment_pointer_++;
} }
return time_since_index_hole; return time_since_index_hole;
} }
@ -103,19 +103,19 @@ Storage::Time PCMTrack::seek_to(Time time_since_index_hole)
void PCMTrack::fix_length() void PCMTrack::fix_length()
{ {
// find the least common multiple of all segment clock rates // find the least common multiple of all segment clock rates
_track_clock_rate = _segments[0].length_of_a_bit.clock_rate; track_clock_rate_ = segments_[0].length_of_a_bit.clock_rate;
for(size_t c = 1; c < _segments.size(); c++) 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 // thereby determine the total length, storing it to next_event as the track-total divisor
_next_event.length.clock_rate = 0; next_event_.length.clock_rate = 0;
for(size_t c = 0; c < _segments.size(); c++) for(size_t c = 0; c < segments_.size(); c++)
{ {
unsigned int multiplier = _track_clock_rate / _segments[c].length_of_a_bit.clock_rate; 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; 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: private:
// storage for the segments that describe this track // 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 // a helper to determine the overall track clock rate and it's length
void fix_length(); void fix_length();
// the event perpetually returned; impliedly contains the length of the entire track // 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 // 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 // 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 // a pointer to the first bit to consider as the next event
size_t _segment_pointer; size_t segment_pointer_;
size_t _bit_pointer; size_t bit_pointer_;
}; };
} }

View File

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

View File

@ -38,7 +38,7 @@ class Parser: public Storage::Tape::Parser<WaveType, SymbolType> {
private: private:
void process_pulse(Storage::Tape::Tape::Pulse pulse); void process_pulse(Storage::Tape::Tape::Pulse pulse);
void inspect_waves(const std::vector<WaveType> &waves); 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() : Parser::Parser() :
Storage::Tape::Parser<WaveType, SymbolType>(), Storage::Tape::Parser<WaveType, SymbolType>(),
_wave_period(0.0f), wave_period_(0.0f),
_previous_was_high(false), previous_was_high_(false),
_parity_byte(0) {} parity_byte_(0) {}
/*! /*!
Advances to the next block on the tape, treating it as a header, then consumes, parses, and returns it. 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(); if(next_byte != value) set_error_flag();
} }
void Parser::reset_parity_byte() { _parity_byte = 0; } void Parser::reset_parity_byte() { parity_byte_ = 0; }
uint8_t Parser::get_parity_byte() { return _parity_byte; } uint8_t Parser::get_parity_byte() { return parity_byte_; }
void Parser::add_parity_byte(uint8_t byte) { _parity_byte ^= 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. 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 // medium: 262µs => 0.000524s cycle
// long: 342µs => 0.000684s cycle // long: 342µs => 0.000684s cycle
bool is_high = pulse.type == Storage::Tape::Tape::Pulse::High; 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); if(wave_period_ >= 0.000764) push_wave(WaveType::Unrecognised);
else if(_wave_period >= 0.000604) push_wave(WaveType::Long); 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.000444) push_wave(WaveType::Medium);
else if(_wave_period >= 0.000284) push_wave(WaveType::Short); else if(wave_period_ >= 0.000284) push_wave(WaveType::Short);
else push_wave(WaveType::Unrecognised); else push_wave(WaveType::Unrecognised);
_wave_period = 0.0f; wave_period_ = 0.0f;
} }
_wave_period += pulse.length.get_float(); wave_period_ += pulse.length.get_float();
_previous_was_high = is_high; 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); 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(); void reset_parity_byte();
uint8_t get_parity_byte(); uint8_t get_parity_byte();
void add_parity_byte(uint8_t 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. a long, medium, short or unrecognised wave period.
*/ */
void process_pulse(Storage::Tape::Tape::Pulse pulse); void process_pulse(Storage::Tape::Tape::Pulse pulse);
bool _previous_was_high; bool previous_was_high_;
float _wave_period; float wave_period_;
/*! /*!
Per the contract with StaticAnalyser::TapeParser; produces any of a word marker, an end-of-block marker, 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) int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape, bool use_fast_encoding)
{ {
_detection_mode = use_fast_encoding ? FastZero : SlowZero; detection_mode_ = use_fast_encoding ? FastZero : SlowZero;
_cycle_length = 0.0f; cycle_length_ = 0.0f;
int result = 0; int result = 0;
int bit_count = 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); SymbolType symbol = get_next_symbol(tape);
if(!bit_count && symbol != SymbolType::Zero) continue; 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; result |= ((symbol == SymbolType::One) ? 1 : 0) << bit_count;
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) 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()) while(!tape->is_at_end())
{ {
SymbolType symbol = get_next_symbol(tape); 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; const float maximum_long_length = 0.001456f;
bool wave_is_high = pulse.type == Storage::Tape::Tape::Pulse::High; 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); 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_medium_length) push_wave(WaveType::Medium);
else if(_cycle_length < maximum_long_length) push_wave(WaveType::Long); else if(cycle_length_ < maximum_long_length) push_wave(WaveType::Long);
else push_wave(WaveType::Unrecognised); else push_wave(WaveType::Unrecognised);
_cycle_length = 0.0f; cycle_length_ = 0.0f;
} }
_wave_was_high = wave_is_high; wave_was_high_ = wave_is_high;
_cycle_length += pulse.length.get_float(); cycle_length_ += pulse.length.get_float();
} }
void Parser::inspect_waves(const std::vector<WaveType> &waves) void Parser::inspect_waves(const std::vector<WaveType> &waves)
{ {
switch(_detection_mode) switch(detection_mode_)
{ {
case FastZero: case FastZero:
if(waves.empty()) return; if(waves.empty()) return;

View File

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

View File

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

View File

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

View File

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

View File

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