mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-23 20:29:42 +00:00
Converted all 'Components' to postfix underscores.
This commit is contained in:
parent
a0043ec336
commit
36bc558798
@ -57,64 +57,64 @@ template <class T> class MOS6522 {
|
||||
switch(address)
|
||||
{
|
||||
case 0x0:
|
||||
_registers.output[1] = value;
|
||||
static_cast<T *>(this)->set_port_output(Port::B, value, _registers.data_direction[1]); // TODO: handshake
|
||||
registers_.output[1] = value;
|
||||
static_cast<T *>(this)->set_port_output(Port::B, value, registers_.data_direction[1]); // TODO: handshake
|
||||
|
||||
_registers.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | ((_registers.peripheral_control&0x20) ? 0 : InterruptFlag::CB2ActiveEdge));
|
||||
registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | ((registers_.peripheral_control&0x20) ? 0 : InterruptFlag::CB2ActiveEdge));
|
||||
reevaluate_interrupts();
|
||||
break;
|
||||
case 0xf:
|
||||
case 0x1:
|
||||
_registers.output[0] = value;
|
||||
static_cast<T *>(this)->set_port_output(Port::A, value, _registers.data_direction[0]); // TODO: handshake
|
||||
registers_.output[0] = value;
|
||||
static_cast<T *>(this)->set_port_output(Port::A, value, registers_.data_direction[0]); // TODO: handshake
|
||||
|
||||
_registers.interrupt_flags &= ~(InterruptFlag::CA1ActiveEdge | ((_registers.peripheral_control&0x02) ? 0 : InterruptFlag::CB2ActiveEdge));
|
||||
registers_.interrupt_flags &= ~(InterruptFlag::CA1ActiveEdge | ((registers_.peripheral_control&0x02) ? 0 : InterruptFlag::CB2ActiveEdge));
|
||||
reevaluate_interrupts();
|
||||
break;
|
||||
// // No handshake, so write directly
|
||||
// _registers.output[0] = value;
|
||||
// registers_.output[0] = value;
|
||||
// static_cast<T *>(this)->set_port_output(0, value);
|
||||
// break;
|
||||
|
||||
case 0x2:
|
||||
_registers.data_direction[1] = value;
|
||||
registers_.data_direction[1] = value;
|
||||
break;
|
||||
case 0x3:
|
||||
_registers.data_direction[0] = value;
|
||||
registers_.data_direction[0] = value;
|
||||
break;
|
||||
|
||||
// Timer 1
|
||||
case 0x6: case 0x4: _registers.timer_latch[0] = (_registers.timer_latch[0]&0xff00) | value; break;
|
||||
case 0x6: case 0x4: registers_.timer_latch[0] = (registers_.timer_latch[0]&0xff00) | value; break;
|
||||
case 0x5: case 0x7:
|
||||
_registers.timer_latch[0] = (_registers.timer_latch[0]&0x00ff) | (uint16_t)(value << 8);
|
||||
_registers.interrupt_flags &= ~InterruptFlag::Timer1;
|
||||
registers_.timer_latch[0] = (registers_.timer_latch[0]&0x00ff) | (uint16_t)(value << 8);
|
||||
registers_.interrupt_flags &= ~InterruptFlag::Timer1;
|
||||
if(address == 0x05)
|
||||
{
|
||||
_registers.next_timer[0] = _registers.timer_latch[0];
|
||||
_timer_is_running[0] = true;
|
||||
registers_.next_timer[0] = registers_.timer_latch[0];
|
||||
timer_is_running_[0] = true;
|
||||
}
|
||||
reevaluate_interrupts();
|
||||
break;
|
||||
|
||||
// Timer 2
|
||||
case 0x8: _registers.timer_latch[1] = value; break;
|
||||
case 0x8: registers_.timer_latch[1] = value; break;
|
||||
case 0x9:
|
||||
_registers.interrupt_flags &= ~InterruptFlag::Timer2;
|
||||
_registers.next_timer[1] = _registers.timer_latch[1] | (uint16_t)(value << 8);
|
||||
_timer_is_running[1] = true;
|
||||
registers_.interrupt_flags &= ~InterruptFlag::Timer2;
|
||||
registers_.next_timer[1] = registers_.timer_latch[1] | (uint16_t)(value << 8);
|
||||
timer_is_running_[1] = true;
|
||||
reevaluate_interrupts();
|
||||
break;
|
||||
|
||||
// Shift
|
||||
case 0xa: _registers.shift = value; break;
|
||||
case 0xa: registers_.shift = value; break;
|
||||
|
||||
// Control
|
||||
case 0xb:
|
||||
_registers.auxiliary_control = value;
|
||||
registers_.auxiliary_control = value;
|
||||
break;
|
||||
case 0xc:
|
||||
// printf("Peripheral control %02x\n", value);
|
||||
_registers.peripheral_control = value;
|
||||
registers_.peripheral_control = value;
|
||||
|
||||
// TODO: simplify below; trying to avoid improper logging of unimplemented warnings in input mode
|
||||
if(value & 0x08)
|
||||
@ -139,14 +139,14 @@ template <class T> class MOS6522 {
|
||||
|
||||
// Interrupt control
|
||||
case 0xd:
|
||||
_registers.interrupt_flags &= ~value;
|
||||
registers_.interrupt_flags &= ~value;
|
||||
reevaluate_interrupts();
|
||||
break;
|
||||
case 0xe:
|
||||
if(value&0x80)
|
||||
_registers.interrupt_enable |= value;
|
||||
registers_.interrupt_enable |= value;
|
||||
else
|
||||
_registers.interrupt_enable &= ~value;
|
||||
registers_.interrupt_enable &= ~value;
|
||||
reevaluate_interrupts();
|
||||
break;
|
||||
}
|
||||
@ -160,41 +160,41 @@ template <class T> class MOS6522 {
|
||||
switch(address)
|
||||
{
|
||||
case 0x0:
|
||||
_registers.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | InterruptFlag::CB2ActiveEdge);
|
||||
registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | InterruptFlag::CB2ActiveEdge);
|
||||
reevaluate_interrupts();
|
||||
return get_port_input(Port::B, _registers.data_direction[1], _registers.output[1]);
|
||||
return get_port_input(Port::B, registers_.data_direction[1], registers_.output[1]);
|
||||
case 0xf: // TODO: handshake, latching
|
||||
case 0x1:
|
||||
_registers.interrupt_flags &= ~(InterruptFlag::CA1ActiveEdge | InterruptFlag::CA2ActiveEdge);
|
||||
registers_.interrupt_flags &= ~(InterruptFlag::CA1ActiveEdge | InterruptFlag::CA2ActiveEdge);
|
||||
reevaluate_interrupts();
|
||||
return get_port_input(Port::A, _registers.data_direction[0], _registers.output[0]);
|
||||
return get_port_input(Port::A, registers_.data_direction[0], registers_.output[0]);
|
||||
|
||||
case 0x2: return _registers.data_direction[1];
|
||||
case 0x3: return _registers.data_direction[0];
|
||||
case 0x2: return registers_.data_direction[1];
|
||||
case 0x3: return registers_.data_direction[0];
|
||||
|
||||
// Timer 1
|
||||
case 0x4:
|
||||
_registers.interrupt_flags &= ~InterruptFlag::Timer1;
|
||||
registers_.interrupt_flags &= ~InterruptFlag::Timer1;
|
||||
reevaluate_interrupts();
|
||||
return _registers.timer[0] & 0x00ff;
|
||||
case 0x5: return _registers.timer[0] >> 8;
|
||||
case 0x6: return _registers.timer_latch[0] & 0x00ff;
|
||||
case 0x7: return _registers.timer_latch[0] >> 8;
|
||||
return registers_.timer[0] & 0x00ff;
|
||||
case 0x5: return registers_.timer[0] >> 8;
|
||||
case 0x6: return registers_.timer_latch[0] & 0x00ff;
|
||||
case 0x7: return registers_.timer_latch[0] >> 8;
|
||||
|
||||
// Timer 2
|
||||
case 0x8:
|
||||
_registers.interrupt_flags &= ~InterruptFlag::Timer2;
|
||||
registers_.interrupt_flags &= ~InterruptFlag::Timer2;
|
||||
reevaluate_interrupts();
|
||||
return _registers.timer[1] & 0x00ff;
|
||||
case 0x9: return _registers.timer[1] >> 8;
|
||||
return registers_.timer[1] & 0x00ff;
|
||||
case 0x9: return registers_.timer[1] >> 8;
|
||||
|
||||
case 0xa: return _registers.shift;
|
||||
case 0xa: return registers_.shift;
|
||||
|
||||
case 0xb: return _registers.auxiliary_control;
|
||||
case 0xc: return _registers.peripheral_control;
|
||||
case 0xb: return registers_.auxiliary_control;
|
||||
case 0xc: return registers_.peripheral_control;
|
||||
|
||||
case 0xd: return _registers.interrupt_flags | (get_interrupt_line() ? 0x80 : 0x00);
|
||||
case 0xe: return _registers.interrupt_enable | 0x80;
|
||||
case 0xd: return registers_.interrupt_flags | (get_interrupt_line() ? 0x80 : 0x00);
|
||||
case 0xe: return registers_.interrupt_enable | 0x80;
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
@ -205,65 +205,65 @@ template <class T> class MOS6522 {
|
||||
switch(line)
|
||||
{
|
||||
case Line::One:
|
||||
if( value != _control_inputs[port].line_one &&
|
||||
value == !!(_registers.peripheral_control & (port ? 0x10 : 0x01))
|
||||
if( value != control_inputs_[port].line_one &&
|
||||
value == !!(registers_.peripheral_control & (port ? 0x10 : 0x01))
|
||||
)
|
||||
{
|
||||
_registers.interrupt_flags |= port ? InterruptFlag::CB1ActiveEdge : InterruptFlag::CA1ActiveEdge;
|
||||
registers_.interrupt_flags |= port ? InterruptFlag::CB1ActiveEdge : InterruptFlag::CA1ActiveEdge;
|
||||
reevaluate_interrupts();
|
||||
}
|
||||
_control_inputs[port].line_one = value;
|
||||
control_inputs_[port].line_one = value;
|
||||
break;
|
||||
|
||||
case Line::Two:
|
||||
// TODO: output modes, but probably elsewhere?
|
||||
if( value != _control_inputs[port].line_two && // i.e. value has changed ...
|
||||
!(_registers.peripheral_control & (port ? 0x80 : 0x08)) && // ... and line is input ...
|
||||
value == !!(_registers.peripheral_control & (port ? 0x40 : 0x04)) // ... and it's either high or low, as required
|
||||
if( value != control_inputs_[port].line_two && // i.e. value has changed ...
|
||||
!(registers_.peripheral_control & (port ? 0x80 : 0x08)) && // ... and line is input ...
|
||||
value == !!(registers_.peripheral_control & (port ? 0x40 : 0x04)) // ... and it's either high or low, as required
|
||||
)
|
||||
{
|
||||
_registers.interrupt_flags |= port ? InterruptFlag::CB2ActiveEdge : InterruptFlag::CA2ActiveEdge;
|
||||
registers_.interrupt_flags |= port ? InterruptFlag::CB2ActiveEdge : InterruptFlag::CA2ActiveEdge;
|
||||
reevaluate_interrupts();
|
||||
}
|
||||
_control_inputs[port].line_two = value;
|
||||
control_inputs_[port].line_two = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define phase2() \
|
||||
_registers.last_timer[0] = _registers.timer[0];\
|
||||
_registers.last_timer[1] = _registers.timer[1];\
|
||||
registers_.last_timer[0] = registers_.timer[0];\
|
||||
registers_.last_timer[1] = registers_.timer[1];\
|
||||
\
|
||||
if(_registers.timer_needs_reload)\
|
||||
if(registers_.timer_needs_reload)\
|
||||
{\
|
||||
_registers.timer_needs_reload = false;\
|
||||
_registers.timer[0] = _registers.timer_latch[0];\
|
||||
registers_.timer_needs_reload = false;\
|
||||
registers_.timer[0] = registers_.timer_latch[0];\
|
||||
}\
|
||||
else\
|
||||
_registers.timer[0] --;\
|
||||
registers_.timer[0] --;\
|
||||
\
|
||||
_registers.timer[1] --; \
|
||||
if(_registers.next_timer[0] >= 0) { _registers.timer[0] = (uint16_t)_registers.next_timer[0]; _registers.next_timer[0] = -1; }\
|
||||
if(_registers.next_timer[1] >= 0) { _registers.timer[1] = (uint16_t)_registers.next_timer[1]; _registers.next_timer[1] = -1; }\
|
||||
registers_.timer[1] --; \
|
||||
if(registers_.next_timer[0] >= 0) { registers_.timer[0] = (uint16_t)registers_.next_timer[0]; registers_.next_timer[0] = -1; }\
|
||||
if(registers_.next_timer[1] >= 0) { registers_.timer[1] = (uint16_t)registers_.next_timer[1]; registers_.next_timer[1] = -1; }\
|
||||
|
||||
// IRQ is raised on the half cycle after overflow
|
||||
#define phase1() \
|
||||
if((_registers.timer[1] == 0xffff) && !_registers.last_timer[1] && _timer_is_running[1])\
|
||||
if((registers_.timer[1] == 0xffff) && !registers_.last_timer[1] && timer_is_running_[1])\
|
||||
{\
|
||||
_timer_is_running[1] = false;\
|
||||
_registers.interrupt_flags |= InterruptFlag::Timer2;\
|
||||
timer_is_running_[1] = false;\
|
||||
registers_.interrupt_flags |= InterruptFlag::Timer2;\
|
||||
reevaluate_interrupts();\
|
||||
}\
|
||||
\
|
||||
if((_registers.timer[0] == 0xffff) && !_registers.last_timer[0] && _timer_is_running[0])\
|
||||
if((registers_.timer[0] == 0xffff) && !registers_.last_timer[0] && timer_is_running_[0])\
|
||||
{\
|
||||
_registers.interrupt_flags |= InterruptFlag::Timer1;\
|
||||
registers_.interrupt_flags |= InterruptFlag::Timer1;\
|
||||
reevaluate_interrupts();\
|
||||
\
|
||||
if(_registers.auxiliary_control&0x40)\
|
||||
_registers.timer_needs_reload = true;\
|
||||
if(registers_.auxiliary_control&0x40)\
|
||||
registers_.timer_needs_reload = true;\
|
||||
else\
|
||||
_timer_is_running[0] = false;\
|
||||
timer_is_running_[0] = false;\
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -281,7 +281,7 @@ template <class T> class MOS6522 {
|
||||
*/
|
||||
inline void run_for_half_cycles(unsigned int number_of_cycles)
|
||||
{
|
||||
if(_is_phase2)
|
||||
if(is_phase2_)
|
||||
{
|
||||
phase2();
|
||||
number_of_cycles--;
|
||||
@ -297,11 +297,11 @@ template <class T> class MOS6522 {
|
||||
if(number_of_cycles)
|
||||
{
|
||||
phase1();
|
||||
_is_phase2 = true;
|
||||
is_phase2_ = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_is_phase2 = false;
|
||||
is_phase2_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -326,14 +326,14 @@ template <class T> class MOS6522 {
|
||||
/*! @returns @c true if the IRQ line is currently active; @c false otherwise. */
|
||||
inline bool get_interrupt_line()
|
||||
{
|
||||
uint8_t interrupt_status = _registers.interrupt_flags & _registers.interrupt_enable & 0x7f;
|
||||
uint8_t interrupt_status = registers_.interrupt_flags & registers_.interrupt_enable & 0x7f;
|
||||
return !!interrupt_status;
|
||||
}
|
||||
|
||||
MOS6522() :
|
||||
_timer_is_running{false, false},
|
||||
_last_posted_interrupt_status(false),
|
||||
_is_phase2(false)
|
||||
timer_is_running_{false, false},
|
||||
last_posted_interrupt_status_(false),
|
||||
is_phase2_(false)
|
||||
{}
|
||||
|
||||
private:
|
||||
@ -351,16 +351,16 @@ template <class T> class MOS6522 {
|
||||
}
|
||||
|
||||
// Phase toggle
|
||||
bool _is_phase2;
|
||||
bool is_phase2_;
|
||||
|
||||
// Delegate and communications
|
||||
bool _last_posted_interrupt_status;
|
||||
bool last_posted_interrupt_status_;
|
||||
inline void reevaluate_interrupts()
|
||||
{
|
||||
bool new_interrupt_status = get_interrupt_line();
|
||||
if(new_interrupt_status != _last_posted_interrupt_status)
|
||||
if(new_interrupt_status != last_posted_interrupt_status_)
|
||||
{
|
||||
_last_posted_interrupt_status = new_interrupt_status;
|
||||
last_posted_interrupt_status_ = new_interrupt_status;
|
||||
static_cast<T *>(this)->set_interrupt_status(new_interrupt_status);
|
||||
}
|
||||
}
|
||||
@ -382,15 +382,15 @@ template <class T> class MOS6522 {
|
||||
interrupt_flags(0), interrupt_enable(0),
|
||||
last_timer{0, 0}, timer_needs_reload(false),
|
||||
next_timer{-1, -1} {}
|
||||
} _registers;
|
||||
} registers_;
|
||||
|
||||
// control state
|
||||
struct {
|
||||
bool line_one, line_two;
|
||||
} _control_inputs[2];
|
||||
} control_inputs_[2];
|
||||
|
||||
// Internal state other than the registers
|
||||
bool _timer_is_running[2];
|
||||
bool timer_is_running_[2];
|
||||
};
|
||||
|
||||
/*!
|
||||
@ -404,18 +404,18 @@ class MOS6522IRQDelegate {
|
||||
virtual void mos6522_did_change_interrupt_status(void *mos6522) = 0;
|
||||
};
|
||||
|
||||
void set_interrupt_delegate(Delegate *delegate)
|
||||
inline void set_interrupt_delegate(Delegate *delegate)
|
||||
{
|
||||
_delegate = delegate;
|
||||
delegate_ = delegate;
|
||||
}
|
||||
|
||||
void set_interrupt_status(bool new_status)
|
||||
inline void set_interrupt_status(bool new_status)
|
||||
{
|
||||
if(_delegate) _delegate->mos6522_did_change_interrupt_status(this);
|
||||
if(delegate_) delegate_->mos6522_did_change_interrupt_status(this);
|
||||
}
|
||||
|
||||
private:
|
||||
Delegate *_delegate;
|
||||
Delegate *delegate_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -27,8 +27,8 @@ namespace MOS {
|
||||
*/
|
||||
template <class T> class MOS6532 {
|
||||
public:
|
||||
inline void set_ram(uint16_t address, uint8_t value) { _ram[address&0x7f] = value; }
|
||||
inline uint8_t get_ram(uint16_t address) { return _ram[address & 0x7f]; }
|
||||
inline void set_ram(uint16_t address, uint8_t value) { ram_[address&0x7f] = value; }
|
||||
inline uint8_t get_ram(uint16_t address) { return ram_[address & 0x7f]; }
|
||||
|
||||
inline void set_register(int address, uint8_t value)
|
||||
{
|
||||
@ -36,13 +36,13 @@ template <class T> class MOS6532 {
|
||||
switch(decodedAddress) {
|
||||
// Port output
|
||||
case 0x00: case 0x02:
|
||||
_port[decodedAddress / 2].output = value;
|
||||
static_cast<T *>(this)->set_port_output(decodedAddress / 2, _port[decodedAddress/2].output, _port[decodedAddress / 2].output_mask);
|
||||
port_[decodedAddress / 2].output = value;
|
||||
static_cast<T *>(this)->set_port_output(decodedAddress / 2, port_[decodedAddress/2].output, port_[decodedAddress / 2].output_mask);
|
||||
set_port_did_change(decodedAddress / 2);
|
||||
break;
|
||||
case 0x01: case 0x03:
|
||||
_port[decodedAddress / 2].output_mask = value;
|
||||
static_cast<T *>(this)->set_port_output(decodedAddress / 2, _port[decodedAddress/2].output, _port[decodedAddress / 2].output_mask);
|
||||
port_[decodedAddress / 2].output_mask = value;
|
||||
static_cast<T *>(this)->set_port_output(decodedAddress / 2, port_[decodedAddress/2].output, port_[decodedAddress / 2].output_mask);
|
||||
set_port_did_change(decodedAddress / 2);
|
||||
break;
|
||||
|
||||
@ -50,16 +50,16 @@ template <class T> class MOS6532 {
|
||||
case 0x04: case 0x05: case 0x06: case 0x07:
|
||||
if(address & 0x10)
|
||||
{
|
||||
_timer.writtenShift = _timer.activeShift = (decodedAddress - 0x04) * 3 + (decodedAddress / 0x07); // i.e. 0, 3, 6, 10
|
||||
_timer.value = ((unsigned int)(value) << _timer.activeShift) | ((1 << _timer.activeShift)-1);
|
||||
_timer.interrupt_enabled = !!(address&0x08);
|
||||
_interrupt_status &= ~InterruptFlag::Timer;
|
||||
timer_.writtenShift = timer_.activeShift = (decodedAddress - 0x04) * 3 + (decodedAddress / 0x07); // i.e. 0, 3, 6, 10
|
||||
timer_.value = ((unsigned int)(value) << timer_.activeShift) | ((1 << timer_.activeShift)-1);
|
||||
timer_.interrupt_enabled = !!(address&0x08);
|
||||
interrupt_status_ &= ~InterruptFlag::Timer;
|
||||
evaluate_interrupts();
|
||||
}
|
||||
else
|
||||
{
|
||||
_a7_interrupt.enabled = !!(address&0x2);
|
||||
_a7_interrupt.active_on_positive = !!(address & 0x01);
|
||||
a7_interrupt_.enabled = !!(address&0x2);
|
||||
a7_interrupt_.active_on_positive = !!(address & 0x01);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -74,25 +74,25 @@ template <class T> class MOS6532 {
|
||||
{
|
||||
const int port = decodedAddress / 2;
|
||||
uint8_t input = static_cast<T *>(this)->get_port_input(port);
|
||||
return (input & ~_port[port].output_mask) | (_port[port].output & _port[port].output_mask);
|
||||
return (input & ~port_[port].output_mask) | (port_[port].output & port_[port].output_mask);
|
||||
}
|
||||
break;
|
||||
case 0x01: case 0x03:
|
||||
return _port[decodedAddress / 2].output_mask;
|
||||
return port_[decodedAddress / 2].output_mask;
|
||||
break;
|
||||
|
||||
// Timer and interrupt control
|
||||
case 0x04: case 0x06:
|
||||
{
|
||||
uint8_t value = (uint8_t)(_timer.value >> _timer.activeShift);
|
||||
_timer.interrupt_enabled = !!(address&0x08);
|
||||
_interrupt_status &= ~InterruptFlag::Timer;
|
||||
uint8_t value = (uint8_t)(timer_.value >> timer_.activeShift);
|
||||
timer_.interrupt_enabled = !!(address&0x08);
|
||||
interrupt_status_ &= ~InterruptFlag::Timer;
|
||||
evaluate_interrupts();
|
||||
|
||||
if(_timer.activeShift != _timer.writtenShift) {
|
||||
unsigned int shift = _timer.writtenShift - _timer.activeShift;
|
||||
_timer.value = (_timer.value << shift) | ((1 << shift) - 1);
|
||||
_timer.activeShift = _timer.writtenShift;
|
||||
if(timer_.activeShift != timer_.writtenShift) {
|
||||
unsigned int shift = timer_.writtenShift - timer_.activeShift;
|
||||
timer_.value = (timer_.value << shift) | ((1 << shift) - 1);
|
||||
timer_.activeShift = timer_.writtenShift;
|
||||
}
|
||||
|
||||
return value;
|
||||
@ -101,8 +101,8 @@ template <class T> class MOS6532 {
|
||||
|
||||
case 0x05: case 0x07:
|
||||
{
|
||||
uint8_t value = _interrupt_status;
|
||||
_interrupt_status &= ~InterruptFlag::PA7;
|
||||
uint8_t value = interrupt_status_;
|
||||
interrupt_status_ &= ~InterruptFlag::PA7;
|
||||
evaluate_interrupts();
|
||||
return value;
|
||||
}
|
||||
@ -115,39 +115,39 @@ template <class T> class MOS6532 {
|
||||
inline void run_for_cycles(unsigned int number_of_cycles)
|
||||
{
|
||||
// permit counting _to_ zero; counting _through_ zero initiates the other behaviour
|
||||
if(_timer.value >= number_of_cycles) {
|
||||
_timer.value -= number_of_cycles;
|
||||
if(timer_.value >= number_of_cycles) {
|
||||
timer_.value -= number_of_cycles;
|
||||
} else {
|
||||
number_of_cycles -= _timer.value;
|
||||
_timer.value = 0x100 - number_of_cycles;
|
||||
_timer.activeShift = 0;
|
||||
_interrupt_status |= InterruptFlag::Timer;
|
||||
number_of_cycles -= timer_.value;
|
||||
timer_.value = 0x100 - number_of_cycles;
|
||||
timer_.activeShift = 0;
|
||||
interrupt_status_ |= InterruptFlag::Timer;
|
||||
evaluate_interrupts();
|
||||
}
|
||||
}
|
||||
|
||||
MOS6532() :
|
||||
_interrupt_status(0),
|
||||
_port{{.output_mask = 0, .output = 0}, {.output_mask = 0, .output = 0}},
|
||||
_a7_interrupt({.last_port_value = 0, .enabled = false}),
|
||||
_interrupt_line(false)
|
||||
interrupt_status_(0),
|
||||
port_{{.output_mask = 0, .output = 0}, {.output_mask = 0, .output = 0}},
|
||||
a7_interrupt_({.last_port_value = 0, .enabled = false}),
|
||||
interrupt_line_(false)
|
||||
{}
|
||||
|
||||
inline void set_port_did_change(int port)
|
||||
{
|
||||
if(!port)
|
||||
{
|
||||
uint8_t new_port_a_value = (get_port_input(0) & ~_port[0].output_mask) | (_port[0].output & _port[0].output_mask);
|
||||
uint8_t difference = new_port_a_value ^ _a7_interrupt.last_port_value;
|
||||
_a7_interrupt.last_port_value = new_port_a_value;
|
||||
uint8_t new_port_a_value = (get_port_input(0) & ~port_[0].output_mask) | (port_[0].output & port_[0].output_mask);
|
||||
uint8_t difference = new_port_a_value ^ a7_interrupt_.last_port_value;
|
||||
a7_interrupt_.last_port_value = new_port_a_value;
|
||||
if(difference&0x80)
|
||||
{
|
||||
if(
|
||||
((new_port_a_value&0x80) && _a7_interrupt.active_on_positive) ||
|
||||
(!(new_port_a_value&0x80) && !_a7_interrupt.active_on_positive)
|
||||
((new_port_a_value&0x80) && a7_interrupt_.active_on_positive) ||
|
||||
(!(new_port_a_value&0x80) && !a7_interrupt_.active_on_positive)
|
||||
)
|
||||
{
|
||||
_interrupt_status |= InterruptFlag::PA7;
|
||||
interrupt_status_ |= InterruptFlag::PA7;
|
||||
evaluate_interrupts();
|
||||
}
|
||||
}
|
||||
@ -156,34 +156,34 @@ template <class T> class MOS6532 {
|
||||
|
||||
inline bool get_inerrupt_line()
|
||||
{
|
||||
return _interrupt_line;
|
||||
return interrupt_line_;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t _ram[128];
|
||||
uint8_t ram_[128];
|
||||
|
||||
struct {
|
||||
unsigned int value;
|
||||
unsigned int activeShift, writtenShift;
|
||||
bool interrupt_enabled;
|
||||
} _timer;
|
||||
} timer_;
|
||||
|
||||
struct {
|
||||
bool enabled;
|
||||
bool active_on_positive;
|
||||
uint8_t last_port_value;
|
||||
} _a7_interrupt;
|
||||
} a7_interrupt_;
|
||||
|
||||
struct {
|
||||
uint8_t output_mask, output;
|
||||
} _port[2];
|
||||
} port_[2];
|
||||
|
||||
uint8_t _interrupt_status;
|
||||
uint8_t interrupt_status_;
|
||||
enum InterruptFlag: uint8_t {
|
||||
Timer = 0x80,
|
||||
PA7 = 0x40
|
||||
};
|
||||
bool _interrupt_line;
|
||||
bool interrupt_line_;
|
||||
|
||||
// expected to be overridden
|
||||
uint8_t get_port_input(int port) { return 0xff; }
|
||||
@ -192,10 +192,10 @@ template <class T> class MOS6532 {
|
||||
|
||||
inline void evaluate_interrupts()
|
||||
{
|
||||
_interrupt_line =
|
||||
((_interrupt_status&InterruptFlag::Timer) && _timer.interrupt_enabled) ||
|
||||
((_interrupt_status&InterruptFlag::PA7) && _a7_interrupt.enabled);
|
||||
set_irq_line(_interrupt_line);
|
||||
interrupt_line_ =
|
||||
((interrupt_status_&InterruptFlag::Timer) && timer_.interrupt_enabled) ||
|
||||
((interrupt_status_&InterruptFlag::PA7) && a7_interrupt_.enabled);
|
||||
set_irq_line(interrupt_line_);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11,23 +11,23 @@
|
||||
using namespace MOS;
|
||||
|
||||
Speaker::Speaker() :
|
||||
_volume(0),
|
||||
_control_registers{0, 0, 0, 0},
|
||||
_shift_registers{0, 0, 0, 0},
|
||||
_counters{2, 1, 0, 0} // create a slight phase offset for the three channels
|
||||
volume_(0),
|
||||
control_registers_{0, 0, 0, 0},
|
||||
shift_registers_{0, 0, 0, 0},
|
||||
counters_{2, 1, 0, 0} // create a slight phase offset for the three channels
|
||||
{}
|
||||
|
||||
void Speaker::set_volume(uint8_t volume)
|
||||
{
|
||||
enqueue([=]() {
|
||||
_volume = volume;
|
||||
volume_ = volume;
|
||||
});
|
||||
}
|
||||
|
||||
void Speaker::set_control(int channel, uint8_t value)
|
||||
{
|
||||
enqueue([=]() {
|
||||
_control_registers[channel] = value;
|
||||
control_registers_[channel] = value;
|
||||
});
|
||||
}
|
||||
|
||||
@ -99,9 +99,9 @@ static uint8_t noise_pattern[] = {
|
||||
0xf0, 0xe1, 0xe0, 0x78, 0x70, 0x38, 0x3c, 0x3e, 0x1e, 0x3c, 0x1e, 0x1c, 0x70, 0x3c, 0x38, 0x3f,
|
||||
};
|
||||
|
||||
#define shift(r) _shift_registers[r] = (_shift_registers[r] << 1) | (((_shift_registers[r]^0x80)&_control_registers[r]) >> 7)
|
||||
#define increment(r) _shift_registers[r] = (_shift_registers[r]+1)%8191
|
||||
#define update(r, m, up) _counters[r]++; if((_counters[r] >> m) == 0x80) { up(r); _counters[r] = (unsigned int)(_control_registers[r]&0x7f) << m; }
|
||||
#define shift(r) shift_registers_[r] = (shift_registers_[r] << 1) | (((shift_registers_[r]^0x80)&control_registers_[r]) >> 7)
|
||||
#define increment(r) shift_registers_[r] = (shift_registers_[r]+1)%8191
|
||||
#define update(r, m, up) counters_[r]++; if((counters_[r] >> m) == 0x80) { up(r); counters_[r] = (unsigned int)(control_registers_[r]&0x7f) << m; }
|
||||
// Note on slightly askew test: as far as I can make out, if the value in the register is 0x7f then what's supposed to happen
|
||||
// is that the 0x7f is loaded, on the next clocked cycle the Vic spots a 0x7f, pumps the output, reloads, etc. No increment
|
||||
// ever occurs. It's conditional. I don't really want two conditionals if I can avoid it so I'm incrementing regardless and
|
||||
@ -120,11 +120,11 @@ void Speaker::get_samples(unsigned int number_of_samples, int16_t *target)
|
||||
// this sums the output of all three sounds channels plus a DC offset for volume;
|
||||
// TODO: what's the real ratio of this stuff?
|
||||
target[c] = (
|
||||
(_shift_registers[0]&1) +
|
||||
(_shift_registers[1]&1) +
|
||||
(_shift_registers[2]&1) +
|
||||
((noise_pattern[_shift_registers[3] >> 3] >> (_shift_registers[3]&7))&(_control_registers[3] >> 7)&1)
|
||||
) * _volume * 700 + _volume * 44;
|
||||
(shift_registers_[0]&1) +
|
||||
(shift_registers_[1]&1) +
|
||||
(shift_registers_[2]&1) +
|
||||
((noise_pattern[shift_registers_[3] >> 3] >> (shift_registers_[3]&7))&(control_registers_[3] >> 7)&1)
|
||||
) * volume_ * 700 + volume_ * 44;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,10 +26,10 @@ class Speaker: public ::Outputs::Filter<Speaker> {
|
||||
void skip_samples(unsigned int number_of_samples);
|
||||
|
||||
private:
|
||||
unsigned int _counters[4];
|
||||
unsigned int _shift_registers[4];
|
||||
uint8_t _control_registers[4];
|
||||
uint8_t _volume;
|
||||
unsigned int counters_[4];
|
||||
unsigned int shift_registers_[4];
|
||||
uint8_t control_registers_[4];
|
||||
uint8_t volume_;
|
||||
};
|
||||
|
||||
/*!
|
||||
@ -43,15 +43,15 @@ class Speaker: public ::Outputs::Filter<Speaker> {
|
||||
template <class T> class MOS6560 {
|
||||
public:
|
||||
MOS6560() :
|
||||
_crt(new Outputs::CRT::CRT(65*4, 4, Outputs::CRT::NTSC60, 1)),
|
||||
_speaker(new Speaker),
|
||||
_horizontal_counter(0),
|
||||
_vertical_counter(0),
|
||||
_cycles_since_speaker_update(0),
|
||||
_is_odd_frame(false),
|
||||
_is_odd_line(false)
|
||||
crt_(new Outputs::CRT::CRT(65*4, 4, Outputs::CRT::NTSC60, 1)),
|
||||
speaker_(new Speaker),
|
||||
horizontal_counter_(0),
|
||||
vertical_counter_(0),
|
||||
cycles_since_speaker_update_(0),
|
||||
is_odd_frame_(false),
|
||||
is_odd_line_(false)
|
||||
{
|
||||
_crt->set_composite_sampling_function(
|
||||
crt_->set_composite_sampling_function(
|
||||
"float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
|
||||
"{"
|
||||
"uint c = texture(texID, coordinate).r;"
|
||||
@ -69,11 +69,11 @@ template <class T> class MOS6560 {
|
||||
|
||||
void set_clock_rate(double clock_rate)
|
||||
{
|
||||
_speaker->set_input_rate((float)(clock_rate / 4.0));
|
||||
speaker_->set_input_rate((float)(clock_rate / 4.0));
|
||||
}
|
||||
|
||||
std::shared_ptr<Outputs::CRT::CRT> get_crt() { return _crt; }
|
||||
std::shared_ptr<Outputs::Speaker> get_speaker() { return _speaker; }
|
||||
std::shared_ptr<Outputs::CRT::CRT> get_crt() { return crt_; }
|
||||
std::shared_ptr<Outputs::Speaker> get_speaker() { return speaker_; }
|
||||
|
||||
enum OutputMode {
|
||||
PAL, NTSC
|
||||
@ -84,7 +84,7 @@ template <class T> class MOS6560 {
|
||||
*/
|
||||
void set_output_mode(OutputMode output_mode)
|
||||
{
|
||||
_output_mode = output_mode;
|
||||
output_mode_ = output_mode;
|
||||
uint8_t luminances[16] = { // range is 0–4
|
||||
0, 4, 1, 3, 2, 2, 1, 3,
|
||||
2, 1, 2, 1, 2, 3, 2, 3
|
||||
@ -105,38 +105,38 @@ template <class T> class MOS6560 {
|
||||
case OutputMode::PAL:
|
||||
chrominances = pal_chrominances;
|
||||
display_type = Outputs::CRT::PAL50;
|
||||
_timing.cycles_per_line = 71;
|
||||
_timing.line_counter_increment_offset = 0;
|
||||
_timing.lines_per_progressive_field = 312;
|
||||
_timing.supports_interlacing = false;
|
||||
timing_.cycles_per_line = 71;
|
||||
timing_.line_counter_increment_offset = 0;
|
||||
timing_.lines_per_progressive_field = 312;
|
||||
timing_.supports_interlacing = false;
|
||||
break;
|
||||
|
||||
case OutputMode::NTSC:
|
||||
chrominances = ntsc_chrominances;
|
||||
display_type = Outputs::CRT::NTSC60;
|
||||
_timing.cycles_per_line = 65;
|
||||
_timing.line_counter_increment_offset = 65 - 33; // TODO: this is a bit of a hack; separate vertical and horizontal counting
|
||||
_timing.lines_per_progressive_field = 261;
|
||||
_timing.supports_interlacing = true;
|
||||
timing_.cycles_per_line = 65;
|
||||
timing_.line_counter_increment_offset = 65 - 33; // TODO: this is a bit of a hack; separate vertical and horizontal counting
|
||||
timing_.lines_per_progressive_field = 261;
|
||||
timing_.supports_interlacing = true;
|
||||
break;
|
||||
}
|
||||
|
||||
_crt->set_new_display_type((unsigned int)(_timing.cycles_per_line*4), display_type);
|
||||
// _crt->set_visible_area(Outputs::CRT::Rect(0.1f, 0.1f, 0.8f, 0.8f));
|
||||
crt_->set_new_display_type((unsigned int)(timing_.cycles_per_line*4), display_type);
|
||||
// crt_->set_visible_area(Outputs::CRT::Rect(0.1f, 0.1f, 0.8f, 0.8f));
|
||||
|
||||
// switch(output_mode)
|
||||
// {
|
||||
// case OutputMode::PAL:
|
||||
// _crt->set_visible_area(_crt->get_rect_for_area(16, 237, 15*4, 55*4, 4.0f / 3.0f));
|
||||
// crt_->set_visible_area(crt_->get_rect_for_area(16, 237, 15*4, 55*4, 4.0f / 3.0f));
|
||||
// break;
|
||||
// case OutputMode::NTSC:
|
||||
// _crt->set_visible_area(_crt->get_rect_for_area(16, 237, 11*4, 55*4, 4.0f / 3.0f));
|
||||
// crt_->set_visible_area(crt_->get_rect_for_area(16, 237, 11*4, 55*4, 4.0f / 3.0f));
|
||||
// break;
|
||||
// }
|
||||
|
||||
for(int c = 0; c < 16; c++)
|
||||
{
|
||||
_colours[c] = (uint8_t)((luminances[c] << 4) | chrominances[c]);
|
||||
colours_[c] = (uint8_t)((luminances[c] << 4) | chrominances[c]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,88 +146,88 @@ template <class T> class MOS6560 {
|
||||
inline void run_for_cycles(unsigned int number_of_cycles)
|
||||
{
|
||||
// keep track of the amount of time since the speaker was updated; lazy updates are applied
|
||||
_cycles_since_speaker_update += number_of_cycles;
|
||||
cycles_since_speaker_update_ += number_of_cycles;
|
||||
|
||||
while(number_of_cycles--)
|
||||
{
|
||||
// keep an old copy of the vertical count because that test is a cycle later than the actual changes
|
||||
int previous_vertical_counter = _vertical_counter;
|
||||
int previous_vertical_counter = vertical_counter_;
|
||||
|
||||
// keep track of internal time relative to this scanline
|
||||
_horizontal_counter++;
|
||||
_full_frame_counter++;
|
||||
if(_horizontal_counter == _timing.cycles_per_line)
|
||||
horizontal_counter_++;
|
||||
full_frame_counter_++;
|
||||
if(horizontal_counter_ == timing_.cycles_per_line)
|
||||
{
|
||||
if(_horizontal_drawing_latch)
|
||||
if(horizontal_drawing_latch_)
|
||||
{
|
||||
_current_character_row++;
|
||||
current_character_row_++;
|
||||
if(
|
||||
(_current_character_row == 16) ||
|
||||
(_current_character_row == 8 && !_registers.tall_characters)
|
||||
(current_character_row_ == 16) ||
|
||||
(current_character_row_ == 8 && !registers_.tall_characters)
|
||||
) {
|
||||
_current_character_row = 0;
|
||||
_current_row++;
|
||||
current_character_row_ = 0;
|
||||
current_row_++;
|
||||
}
|
||||
|
||||
_pixel_line_cycle = -1;
|
||||
_columns_this_line = -1;
|
||||
_column_counter = -1;
|
||||
pixel_line_cycle_ = -1;
|
||||
columns_this_line_ = -1;
|
||||
column_counter_ = -1;
|
||||
}
|
||||
|
||||
_horizontal_counter = 0;
|
||||
if(_output_mode == OutputMode::PAL) _is_odd_line ^= true;
|
||||
_horizontal_drawing_latch = false;
|
||||
horizontal_counter_ = 0;
|
||||
if(output_mode_ == OutputMode::PAL) is_odd_line_ ^= true;
|
||||
horizontal_drawing_latch_ = false;
|
||||
|
||||
_vertical_counter ++;
|
||||
if(_vertical_counter == (_registers.interlaced ? (_is_odd_frame ? 262 : 263) : _timing.lines_per_progressive_field))
|
||||
vertical_counter_ ++;
|
||||
if(vertical_counter_ == (registers_.interlaced ? (is_odd_frame_ ? 262 : 263) : timing_.lines_per_progressive_field))
|
||||
{
|
||||
_vertical_counter = 0;
|
||||
_full_frame_counter = 0;
|
||||
vertical_counter_ = 0;
|
||||
full_frame_counter_ = 0;
|
||||
|
||||
if(_output_mode == OutputMode::NTSC) _is_odd_frame ^= true;
|
||||
_current_row = 0;
|
||||
_rows_this_field = -1;
|
||||
_vertical_drawing_latch = false;
|
||||
_base_video_matrix_address_counter = 0;
|
||||
_current_character_row = 0;
|
||||
if(output_mode_ == OutputMode::NTSC) is_odd_frame_ ^= true;
|
||||
current_row_ = 0;
|
||||
rows_this_field_ = -1;
|
||||
vertical_drawing_latch_ = false;
|
||||
base_video_matrix_address_counter_ = 0;
|
||||
current_character_row_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// check for vertical starting events
|
||||
_vertical_drawing_latch |= _registers.first_row_location == (previous_vertical_counter >> 1);
|
||||
_horizontal_drawing_latch |= _vertical_drawing_latch && (_horizontal_counter == _registers.first_column_location);
|
||||
vertical_drawing_latch_ |= registers_.first_row_location == (previous_vertical_counter >> 1);
|
||||
horizontal_drawing_latch_ |= vertical_drawing_latch_ && (horizontal_counter_ == registers_.first_column_location);
|
||||
|
||||
if(_pixel_line_cycle >= 0) _pixel_line_cycle++;
|
||||
switch(_pixel_line_cycle)
|
||||
if(pixel_line_cycle_ >= 0) pixel_line_cycle_++;
|
||||
switch(pixel_line_cycle_)
|
||||
{
|
||||
case -1:
|
||||
if(_horizontal_drawing_latch)
|
||||
if(horizontal_drawing_latch_)
|
||||
{
|
||||
_pixel_line_cycle = 0;
|
||||
_video_matrix_address_counter = _base_video_matrix_address_counter;
|
||||
pixel_line_cycle_ = 0;
|
||||
video_matrix_address_counter_ = base_video_matrix_address_counter_;
|
||||
}
|
||||
break;
|
||||
case 1: _columns_this_line = _registers.number_of_columns; break;
|
||||
case 2: if(_rows_this_field < 0) _rows_this_field = _registers.number_of_rows; break;
|
||||
case 3: if(_current_row < _rows_this_field) _column_counter = 0; break;
|
||||
case 1: columns_this_line_ = registers_.number_of_columns; break;
|
||||
case 2: if(rows_this_field_ < 0) rows_this_field_ = registers_.number_of_rows; break;
|
||||
case 3: if(current_row_ < rows_this_field_) column_counter_ = 0; break;
|
||||
}
|
||||
|
||||
uint16_t fetch_address = 0x1c;
|
||||
if(_column_counter >= 0 && _column_counter < _columns_this_line*2)
|
||||
if(column_counter_ >= 0 && column_counter_ < columns_this_line_*2)
|
||||
{
|
||||
if(_column_counter&1)
|
||||
if(column_counter_&1)
|
||||
{
|
||||
fetch_address = _registers.character_cell_start_address + (_character_code*(_registers.tall_characters ? 16 : 8)) + _current_character_row;
|
||||
fetch_address = registers_.character_cell_start_address + (character_code_*(registers_.tall_characters ? 16 : 8)) + current_character_row_;
|
||||
}
|
||||
else
|
||||
{
|
||||
fetch_address = (uint16_t)(_registers.video_matrix_start_address + _video_matrix_address_counter);
|
||||
_video_matrix_address_counter++;
|
||||
fetch_address = (uint16_t)(registers_.video_matrix_start_address + video_matrix_address_counter_);
|
||||
video_matrix_address_counter_++;
|
||||
if(
|
||||
(_current_character_row == 15) ||
|
||||
(_current_character_row == 7 && !_registers.tall_characters)
|
||||
(current_character_row_ == 15) ||
|
||||
(current_character_row_ == 7 && !registers_.tall_characters)
|
||||
) {
|
||||
_base_video_matrix_address_counter = _video_matrix_address_counter;
|
||||
base_video_matrix_address_counter_ = video_matrix_address_counter_;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -242,99 +242,99 @@ template <class T> class MOS6560 {
|
||||
// divide the byte it is set for 3:1 and then continue as usual.
|
||||
|
||||
// determine output state; colour burst and sync timing are currently a guess
|
||||
if(_horizontal_counter > _timing.cycles_per_line-4) _this_state = State::ColourBurst;
|
||||
else if(_horizontal_counter > _timing.cycles_per_line-7) _this_state = State::Sync;
|
||||
if(horizontal_counter_ > timing_.cycles_per_line-4) this_state_ = State::ColourBurst;
|
||||
else if(horizontal_counter_ > timing_.cycles_per_line-7) this_state_ = State::Sync;
|
||||
else
|
||||
{
|
||||
_this_state = (_column_counter >= 0 && _column_counter < _columns_this_line*2) ? State::Pixels : State::Border;
|
||||
this_state_ = (column_counter_ >= 0 && column_counter_ < columns_this_line_*2) ? State::Pixels : State::Border;
|
||||
}
|
||||
|
||||
// apply vertical sync
|
||||
if(
|
||||
(_vertical_counter < 3 && (_is_odd_frame || !_registers.interlaced)) ||
|
||||
(_registers.interlaced &&
|
||||
(vertical_counter_ < 3 && (is_odd_frame_ || !registers_.interlaced)) ||
|
||||
(registers_.interlaced &&
|
||||
(
|
||||
(_vertical_counter == 0 && _horizontal_counter > 32) ||
|
||||
(_vertical_counter == 1) || (_vertical_counter == 2) ||
|
||||
(_vertical_counter == 3 && _horizontal_counter <= 32)
|
||||
(vertical_counter_ == 0 && horizontal_counter_ > 32) ||
|
||||
(vertical_counter_ == 1) || (vertical_counter_ == 2) ||
|
||||
(vertical_counter_ == 3 && horizontal_counter_ <= 32)
|
||||
)
|
||||
))
|
||||
_this_state = State::Sync;
|
||||
this_state_ = State::Sync;
|
||||
|
||||
// update the CRT
|
||||
if(_this_state != _output_state)
|
||||
if(this_state_ != output_state_)
|
||||
{
|
||||
switch(_output_state)
|
||||
switch(output_state_)
|
||||
{
|
||||
case State::Sync: _crt->output_sync(_cycles_in_state * 4); break;
|
||||
case State::ColourBurst: _crt->output_colour_burst(_cycles_in_state * 4, (_is_odd_frame || _is_odd_line) ? 128 : 0, 0); break;
|
||||
case State::Border: output_border(_cycles_in_state * 4); break;
|
||||
case State::Pixels: _crt->output_data(_cycles_in_state * 4, 1); break;
|
||||
case State::Sync: crt_->output_sync(cycles_in_state_ * 4); break;
|
||||
case State::ColourBurst: crt_->output_colour_burst(cycles_in_state_ * 4, (is_odd_frame_ || is_odd_line_) ? 128 : 0, 0); break;
|
||||
case State::Border: output_border(cycles_in_state_ * 4); break;
|
||||
case State::Pixels: crt_->output_data(cycles_in_state_ * 4, 1); break;
|
||||
}
|
||||
_output_state = _this_state;
|
||||
_cycles_in_state = 0;
|
||||
output_state_ = this_state_;
|
||||
cycles_in_state_ = 0;
|
||||
|
||||
pixel_pointer = nullptr;
|
||||
if(_output_state == State::Pixels)
|
||||
if(output_state_ == State::Pixels)
|
||||
{
|
||||
pixel_pointer = _crt->allocate_write_area(260);
|
||||
pixel_pointer = crt_->allocate_write_area(260);
|
||||
}
|
||||
}
|
||||
_cycles_in_state++;
|
||||
cycles_in_state_++;
|
||||
|
||||
if(_this_state == State::Pixels)
|
||||
if(this_state_ == State::Pixels)
|
||||
{
|
||||
if(_column_counter&1)
|
||||
if(column_counter_&1)
|
||||
{
|
||||
_character_value = pixel_data;
|
||||
character_value_ = pixel_data;
|
||||
|
||||
if(pixel_pointer)
|
||||
{
|
||||
uint8_t cell_colour = _colours[_character_colour & 0x7];
|
||||
if(!(_character_colour&0x8))
|
||||
uint8_t cell_colour = colours_[character_colour_ & 0x7];
|
||||
if(!(character_colour_&0x8))
|
||||
{
|
||||
uint8_t colours[2];
|
||||
if(_registers.invertedCells)
|
||||
if(registers_.invertedCells)
|
||||
{
|
||||
colours[0] = cell_colour;
|
||||
colours[1] = _registers.backgroundColour;
|
||||
colours[1] = registers_.backgroundColour;
|
||||
}
|
||||
else
|
||||
{
|
||||
colours[0] = _registers.backgroundColour;
|
||||
colours[0] = registers_.backgroundColour;
|
||||
colours[1] = cell_colour;
|
||||
}
|
||||
pixel_pointer[0] = colours[(_character_value >> 7)&1];
|
||||
pixel_pointer[1] = colours[(_character_value >> 6)&1];
|
||||
pixel_pointer[2] = colours[(_character_value >> 5)&1];
|
||||
pixel_pointer[3] = colours[(_character_value >> 4)&1];
|
||||
pixel_pointer[4] = colours[(_character_value >> 3)&1];
|
||||
pixel_pointer[5] = colours[(_character_value >> 2)&1];
|
||||
pixel_pointer[6] = colours[(_character_value >> 1)&1];
|
||||
pixel_pointer[7] = colours[(_character_value >> 0)&1];
|
||||
pixel_pointer[0] = colours[(character_value_ >> 7)&1];
|
||||
pixel_pointer[1] = colours[(character_value_ >> 6)&1];
|
||||
pixel_pointer[2] = colours[(character_value_ >> 5)&1];
|
||||
pixel_pointer[3] = colours[(character_value_ >> 4)&1];
|
||||
pixel_pointer[4] = colours[(character_value_ >> 3)&1];
|
||||
pixel_pointer[5] = colours[(character_value_ >> 2)&1];
|
||||
pixel_pointer[6] = colours[(character_value_ >> 1)&1];
|
||||
pixel_pointer[7] = colours[(character_value_ >> 0)&1];
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t colours[4] = {_registers.backgroundColour, _registers.borderColour, cell_colour, _registers.auxiliary_colour};
|
||||
uint8_t colours[4] = {registers_.backgroundColour, registers_.borderColour, cell_colour, registers_.auxiliary_colour};
|
||||
pixel_pointer[0] =
|
||||
pixel_pointer[1] = colours[(_character_value >> 6)&3];
|
||||
pixel_pointer[1] = colours[(character_value_ >> 6)&3];
|
||||
pixel_pointer[2] =
|
||||
pixel_pointer[3] = colours[(_character_value >> 4)&3];
|
||||
pixel_pointer[3] = colours[(character_value_ >> 4)&3];
|
||||
pixel_pointer[4] =
|
||||
pixel_pointer[5] = colours[(_character_value >> 2)&3];
|
||||
pixel_pointer[5] = colours[(character_value_ >> 2)&3];
|
||||
pixel_pointer[6] =
|
||||
pixel_pointer[7] = colours[(_character_value >> 0)&3];
|
||||
pixel_pointer[7] = colours[(character_value_ >> 0)&3];
|
||||
}
|
||||
pixel_pointer += 8;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_character_code = pixel_data;
|
||||
_character_colour = colour_data;
|
||||
character_code_ = pixel_data;
|
||||
character_colour_ = colour_data;
|
||||
}
|
||||
|
||||
_column_counter++;
|
||||
column_counter_++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -350,31 +350,31 @@ template <class T> class MOS6560 {
|
||||
void set_register(int address, uint8_t value)
|
||||
{
|
||||
address &= 0xf;
|
||||
_registers.direct_values[address] = value;
|
||||
registers_.direct_values[address] = value;
|
||||
switch(address)
|
||||
{
|
||||
case 0x0:
|
||||
_registers.interlaced = !!(value&0x80) && _timing.supports_interlacing;
|
||||
_registers.first_column_location = value & 0x7f;
|
||||
registers_.interlaced = !!(value&0x80) && timing_.supports_interlacing;
|
||||
registers_.first_column_location = value & 0x7f;
|
||||
break;
|
||||
|
||||
case 0x1:
|
||||
_registers.first_row_location = value;
|
||||
registers_.first_row_location = value;
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
_registers.number_of_columns = value & 0x7f;
|
||||
_registers.video_matrix_start_address = (uint16_t)((_registers.video_matrix_start_address & 0x3c00) | ((value & 0x80) << 2));
|
||||
registers_.number_of_columns = value & 0x7f;
|
||||
registers_.video_matrix_start_address = (uint16_t)((registers_.video_matrix_start_address & 0x3c00) | ((value & 0x80) << 2));
|
||||
break;
|
||||
|
||||
case 0x3:
|
||||
_registers.number_of_rows = (value >> 1)&0x3f;
|
||||
_registers.tall_characters = !!(value&0x01);
|
||||
registers_.number_of_rows = (value >> 1)&0x3f;
|
||||
registers_.tall_characters = !!(value&0x01);
|
||||
break;
|
||||
|
||||
case 0x5:
|
||||
_registers.character_cell_start_address = (uint16_t)((value & 0x0f) << 10);
|
||||
_registers.video_matrix_start_address = (uint16_t)((_registers.video_matrix_start_address & 0x0200) | ((value & 0xf0) << 6));
|
||||
registers_.character_cell_start_address = (uint16_t)((value & 0x0f) << 10);
|
||||
registers_.video_matrix_start_address = (uint16_t)((registers_.video_matrix_start_address & 0x0200) | ((value & 0xf0) << 6));
|
||||
break;
|
||||
|
||||
case 0xa:
|
||||
@ -382,26 +382,26 @@ template <class T> class MOS6560 {
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
update_audio();
|
||||
_speaker->set_control(address - 0xa, value);
|
||||
speaker_->set_control(address - 0xa, value);
|
||||
break;
|
||||
|
||||
case 0xe:
|
||||
update_audio();
|
||||
_registers.auxiliary_colour = _colours[value >> 4];
|
||||
_speaker->set_volume(value & 0xf);
|
||||
registers_.auxiliary_colour = colours_[value >> 4];
|
||||
speaker_->set_volume(value & 0xf);
|
||||
break;
|
||||
|
||||
case 0xf:
|
||||
{
|
||||
uint8_t new_border_colour = _colours[value & 0x07];
|
||||
if(_this_state == State::Border && new_border_colour != _registers.borderColour)
|
||||
uint8_t new_border_colour = colours_[value & 0x07];
|
||||
if(this_state_ == State::Border && new_border_colour != registers_.borderColour)
|
||||
{
|
||||
output_border(_cycles_in_state * 4);
|
||||
_cycles_in_state = 0;
|
||||
output_border(cycles_in_state_ * 4);
|
||||
cycles_in_state_ = 0;
|
||||
}
|
||||
_registers.invertedCells = !((value >> 3)&1);
|
||||
_registers.borderColour = new_border_colour;
|
||||
_registers.backgroundColour = _colours[value >> 4];
|
||||
registers_.invertedCells = !((value >> 3)&1);
|
||||
registers_.borderColour = new_border_colour;
|
||||
registers_.backgroundColour = colours_[value >> 4];
|
||||
}
|
||||
break;
|
||||
|
||||
@ -418,24 +418,24 @@ template <class T> class MOS6560 {
|
||||
uint8_t get_register(int address)
|
||||
{
|
||||
address &= 0xf;
|
||||
int current_line = (_full_frame_counter + _timing.line_counter_increment_offset) / _timing.cycles_per_line;
|
||||
int current_line = (full_frame_counter_ + timing_.line_counter_increment_offset) / timing_.cycles_per_line;
|
||||
switch(address)
|
||||
{
|
||||
default: return _registers.direct_values[address];
|
||||
case 0x03: return (uint8_t)(current_line << 7) | (_registers.direct_values[3] & 0x7f);
|
||||
default: return registers_.direct_values[address];
|
||||
case 0x03: return (uint8_t)(current_line << 7) | (registers_.direct_values[3] & 0x7f);
|
||||
case 0x04: return (current_line >> 1) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Outputs::CRT::CRT> _crt;
|
||||
std::shared_ptr<Outputs::CRT::CRT> crt_;
|
||||
|
||||
std::shared_ptr<Speaker> _speaker;
|
||||
unsigned int _cycles_since_speaker_update;
|
||||
std::shared_ptr<Speaker> speaker_;
|
||||
unsigned int cycles_since_speaker_update_;
|
||||
void update_audio()
|
||||
{
|
||||
_speaker->run_for_cycles(_cycles_since_speaker_update >> 2);
|
||||
_cycles_since_speaker_update &= 3;
|
||||
speaker_->run_for_cycles(cycles_since_speaker_update_ >> 2);
|
||||
cycles_since_speaker_update_ &= 3;
|
||||
}
|
||||
|
||||
// register state
|
||||
@ -448,41 +448,41 @@ template <class T> class MOS6560 {
|
||||
bool invertedCells;
|
||||
|
||||
uint8_t direct_values[16];
|
||||
} _registers;
|
||||
} registers_;
|
||||
|
||||
// output state
|
||||
enum State {
|
||||
Sync, ColourBurst, Border, Pixels
|
||||
} _this_state, _output_state;
|
||||
unsigned int _cycles_in_state;
|
||||
} this_state_, output_state_;
|
||||
unsigned int cycles_in_state_;
|
||||
|
||||
// counters that cover an entire field
|
||||
int _horizontal_counter, _vertical_counter, _full_frame_counter;
|
||||
int horizontal_counter_, vertical_counter_, full_frame_counter_;
|
||||
|
||||
// latches dictating start and length of drawing
|
||||
bool _vertical_drawing_latch, _horizontal_drawing_latch;
|
||||
int _rows_this_field, _columns_this_line;
|
||||
bool vertical_drawing_latch_, horizontal_drawing_latch_;
|
||||
int rows_this_field_, columns_this_line_;
|
||||
|
||||
// current drawing position counter
|
||||
int _pixel_line_cycle, _column_counter;
|
||||
int _current_row;
|
||||
uint16_t _current_character_row;
|
||||
uint16_t _video_matrix_address_counter, _base_video_matrix_address_counter;
|
||||
int pixel_line_cycle_, column_counter_;
|
||||
int current_row_;
|
||||
uint16_t current_character_row_;
|
||||
uint16_t video_matrix_address_counter_, base_video_matrix_address_counter_;
|
||||
|
||||
// data latched from the bus
|
||||
uint8_t _character_code, _character_colour, _character_value;
|
||||
uint8_t character_code_, character_colour_, character_value_;
|
||||
|
||||
bool _is_odd_frame, _is_odd_line;
|
||||
bool is_odd_frame_, is_odd_line_;
|
||||
|
||||
// lookup table from 6560 colour index to appropriate PAL/NTSC value
|
||||
uint8_t _colours[16];
|
||||
uint8_t colours_[16];
|
||||
|
||||
uint8_t *pixel_pointer;
|
||||
void output_border(unsigned int number_of_cycles)
|
||||
{
|
||||
uint8_t *colour_pointer = _crt->allocate_write_area(1);
|
||||
if(colour_pointer) *colour_pointer = _registers.borderColour;
|
||||
_crt->output_level(number_of_cycles);
|
||||
uint8_t *colour_pointer = crt_->allocate_write_area(1);
|
||||
if(colour_pointer) *colour_pointer = registers_.borderColour;
|
||||
crt_->output_level(number_of_cycles);
|
||||
}
|
||||
|
||||
struct {
|
||||
@ -490,8 +490,8 @@ template <class T> class MOS6560 {
|
||||
int line_counter_increment_offset;
|
||||
int lines_per_progressive_field;
|
||||
bool supports_interlacing;
|
||||
} _timing;
|
||||
OutputMode _output_mode;
|
||||
} timing_;
|
||||
OutputMode output_mode_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -11,14 +11,14 @@
|
||||
using namespace GI;
|
||||
|
||||
AY38910::AY38910() :
|
||||
_selected_register(0),
|
||||
_tone_counters{0, 0, 0}, _tone_periods{0, 0, 0}, _tone_outputs{0, 0, 0},
|
||||
_noise_shift_register(0xffff), _noise_period(0), _noise_counter(0), _noise_output(0),
|
||||
_envelope_divider(0), _envelope_period(0), _envelope_position(0),
|
||||
_master_divider(0),
|
||||
_output_registers{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
selected_register_(0),
|
||||
tone_counters_{0, 0, 0}, tone_periods_{0, 0, 0}, tone_outputs_{0, 0, 0},
|
||||
noise_shift_register_(0xffff), noise_period_(0), noise_counter_(0), noise_output_(0),
|
||||
envelope_divider_(0), envelope_period_(0), envelope_position_(0),
|
||||
master_divider_(0),
|
||||
output_registers_{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
{
|
||||
_output_registers[8] = _output_registers[9] = _output_registers[10] = 0;
|
||||
output_registers_[8] = output_registers_[9] = output_registers_[10] = 0;
|
||||
|
||||
// set up envelope lookup tables
|
||||
for(int c = 0; c < 16; c++)
|
||||
@ -28,39 +28,39 @@ AY38910::AY38910() :
|
||||
switch(c)
|
||||
{
|
||||
case 0: case 1: case 2: case 3: case 9:
|
||||
_envelope_shapes[c][p] = (p < 16) ? (p^0xf) : 0;
|
||||
_envelope_overflow_masks[c] = 0x1f;
|
||||
envelope_shapes_[c][p] = (p < 16) ? (p^0xf) : 0;
|
||||
envelope_overflow_masks_[c] = 0x1f;
|
||||
break;
|
||||
case 4: case 5: case 6: case 7: case 15:
|
||||
_envelope_shapes[c][p] = (p < 16) ? p : 0;
|
||||
_envelope_overflow_masks[c] = 0x1f;
|
||||
envelope_shapes_[c][p] = (p < 16) ? p : 0;
|
||||
envelope_overflow_masks_[c] = 0x1f;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
_envelope_shapes[c][p] = (p & 0xf) ^ 0xf;
|
||||
_envelope_overflow_masks[c] = 0x00;
|
||||
envelope_shapes_[c][p] = (p & 0xf) ^ 0xf;
|
||||
envelope_overflow_masks_[c] = 0x00;
|
||||
break;
|
||||
case 12:
|
||||
_envelope_shapes[c][p] = (p & 0xf);
|
||||
_envelope_overflow_masks[c] = 0x00;
|
||||
envelope_shapes_[c][p] = (p & 0xf);
|
||||
envelope_overflow_masks_[c] = 0x00;
|
||||
break;
|
||||
|
||||
case 10:
|
||||
_envelope_shapes[c][p] = (p & 0xf) ^ ((p < 16) ? 0xf : 0x0);
|
||||
_envelope_overflow_masks[c] = 0x00;
|
||||
envelope_shapes_[c][p] = (p & 0xf) ^ ((p < 16) ? 0xf : 0x0);
|
||||
envelope_overflow_masks_[c] = 0x00;
|
||||
break;
|
||||
case 14:
|
||||
_envelope_shapes[c][p] = (p & 0xf) ^ ((p < 16) ? 0x0 : 0xf);
|
||||
_envelope_overflow_masks[c] = 0x00;
|
||||
envelope_shapes_[c][p] = (p & 0xf) ^ ((p < 16) ? 0x0 : 0xf);
|
||||
envelope_overflow_masks_[c] = 0x00;
|
||||
break;
|
||||
|
||||
case 11:
|
||||
_envelope_shapes[c][p] = (p < 16) ? (p^0xf) : 0xf;
|
||||
_envelope_overflow_masks[c] = 0x1f;
|
||||
envelope_shapes_[c][p] = (p < 16) ? (p^0xf) : 0xf;
|
||||
envelope_overflow_masks_[c] = 0x1f;
|
||||
break;
|
||||
case 13:
|
||||
_envelope_shapes[c][p] = (p < 16) ? p : 0xf;
|
||||
_envelope_overflow_masks[c] = 0x1f;
|
||||
envelope_shapes_[c][p] = (p < 16) ? p : 0xf;
|
||||
envelope_overflow_masks_[c] = 0x1f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -71,9 +71,9 @@ AY38910::AY38910() :
|
||||
float root_two = sqrtf(2.0f);
|
||||
for(int v = 0; v < 16; v++)
|
||||
{
|
||||
_volumes[v] = (int)(max_volume / powf(root_two, (float)(v ^ 0xf)));
|
||||
volumes_[v] = (int)(max_volume / powf(root_two, (float)(v ^ 0xf)));
|
||||
}
|
||||
_volumes[0] = 0;
|
||||
volumes_[0] = 0;
|
||||
}
|
||||
|
||||
void AY38910::set_clock_rate(double clock_rate)
|
||||
@ -84,21 +84,21 @@ void AY38910::set_clock_rate(double clock_rate)
|
||||
void AY38910::get_samples(unsigned int number_of_samples, int16_t *target)
|
||||
{
|
||||
int c = 0;
|
||||
while((_master_divider&15) && c < number_of_samples)
|
||||
while((master_divider_&15) && c < number_of_samples)
|
||||
{
|
||||
target[c] = _output_volume;
|
||||
_master_divider++;
|
||||
target[c] = output_volume_;
|
||||
master_divider_++;
|
||||
c++;
|
||||
}
|
||||
|
||||
while(c < number_of_samples)
|
||||
{
|
||||
#define step_channel(c) \
|
||||
if(_tone_counters[c]) _tone_counters[c]--;\
|
||||
if(tone_counters_[c]) tone_counters_[c]--;\
|
||||
else\
|
||||
{\
|
||||
_tone_outputs[c] ^= 1;\
|
||||
_tone_counters[c] = _tone_periods[c];\
|
||||
tone_outputs_[c] ^= 1;\
|
||||
tone_counters_[c] = tone_periods_[c];\
|
||||
}
|
||||
|
||||
// update the tone channels
|
||||
@ -110,48 +110,48 @@ void AY38910::get_samples(unsigned int number_of_samples, int16_t *target)
|
||||
|
||||
// ... the noise generator. This recomputes the new bit repeatedly but harmlessly, only shifting
|
||||
// it into the official 17 upon divider underflow.
|
||||
if(_noise_counter) _noise_counter--;
|
||||
if(noise_counter_) noise_counter_--;
|
||||
else
|
||||
{
|
||||
_noise_counter = _noise_period;
|
||||
_noise_output ^= _noise_shift_register&1;
|
||||
_noise_shift_register |= ((_noise_shift_register ^ (_noise_shift_register >> 3))&1) << 17;
|
||||
_noise_shift_register >>= 1;
|
||||
noise_counter_ = noise_period_;
|
||||
noise_output_ ^= noise_shift_register_&1;
|
||||
noise_shift_register_ |= ((noise_shift_register_ ^ (noise_shift_register_ >> 3))&1) << 17;
|
||||
noise_shift_register_ >>= 1;
|
||||
}
|
||||
|
||||
// ... and the envelope generator. Table based for pattern lookup, with a 'refill' step — a way of
|
||||
// implementing non-repeating patterns by locking them to table position 0x1f.
|
||||
if(_envelope_divider) _envelope_divider--;
|
||||
if(envelope_divider_) envelope_divider_--;
|
||||
else
|
||||
{
|
||||
_envelope_divider = _envelope_period;
|
||||
_envelope_position ++;
|
||||
if(_envelope_position == 32) _envelope_position = _envelope_overflow_masks[_output_registers[13]];
|
||||
envelope_divider_ = envelope_period_;
|
||||
envelope_position_ ++;
|
||||
if(envelope_position_ == 32) envelope_position_ = envelope_overflow_masks_[output_registers_[13]];
|
||||
}
|
||||
|
||||
evaluate_output_volume();
|
||||
|
||||
for(int ic = 0; ic < 16 && c < number_of_samples; ic++)
|
||||
{
|
||||
target[c] = _output_volume;
|
||||
target[c] = output_volume_;
|
||||
c++;
|
||||
_master_divider++;
|
||||
master_divider_++;
|
||||
}
|
||||
}
|
||||
|
||||
_master_divider &= 15;
|
||||
master_divider_ &= 15;
|
||||
}
|
||||
|
||||
void AY38910::evaluate_output_volume()
|
||||
{
|
||||
int envelope_volume = _envelope_shapes[_output_registers[13]][_envelope_position];
|
||||
int envelope_volume = envelope_shapes_[output_registers_[13]][envelope_position_];
|
||||
|
||||
// The output level for a channel is:
|
||||
// 1 if neither tone nor noise is enabled;
|
||||
// 0 if either tone or noise is enabled and its value is low.
|
||||
// The tone/noise enable bits use inverse logic — 0 = on, 1 = off — permitting the OR logic below.
|
||||
#define tone_level(c, tone_bit) (_tone_outputs[c] | (_output_registers[7] >> tone_bit))
|
||||
#define noise_level(c, noise_bit) (_noise_output | (_output_registers[7] >> noise_bit))
|
||||
#define tone_level(c, tone_bit) (tone_outputs_[c] | (output_registers_[7] >> tone_bit))
|
||||
#define noise_level(c, noise_bit) (noise_output_ | (output_registers_[7] >> noise_bit))
|
||||
|
||||
#define level(c, tone_bit, noise_bit) tone_level(c, tone_bit) & noise_level(c, noise_bit) & 1
|
||||
const int channel_levels[3] = {
|
||||
@ -163,7 +163,7 @@ void AY38910::evaluate_output_volume()
|
||||
|
||||
// Channel volume is a simple selection: if the bit at 0x10 is set, use the envelope volume; otherwise use the lower four bits
|
||||
#define channel_volume(c) \
|
||||
((_output_registers[c] >> 4)&1) * envelope_volume + (((_output_registers[c] >> 4)&1)^1) * (_output_registers[c]&0xf)
|
||||
((output_registers_[c] >> 4)&1) * envelope_volume + (((output_registers_[c] >> 4)&1)^1) * (output_registers_[c]&0xf)
|
||||
|
||||
const int volumes[3] = {
|
||||
channel_volume(8),
|
||||
@ -173,24 +173,24 @@ void AY38910::evaluate_output_volume()
|
||||
#undef channel_volume
|
||||
|
||||
// Mix additively.
|
||||
_output_volume = (int16_t)(
|
||||
_volumes[volumes[0]] * channel_levels[0] +
|
||||
_volumes[volumes[1]] * channel_levels[1] +
|
||||
_volumes[volumes[2]] * channel_levels[2]
|
||||
output_volume_ = (int16_t)(
|
||||
volumes_[volumes[0]] * channel_levels[0] +
|
||||
volumes_[volumes[1]] * channel_levels[1] +
|
||||
volumes_[volumes[2]] * channel_levels[2]
|
||||
);
|
||||
}
|
||||
|
||||
void AY38910::select_register(uint8_t r)
|
||||
{
|
||||
_selected_register = r & 0xf;
|
||||
selected_register_ = r & 0xf;
|
||||
}
|
||||
|
||||
void AY38910::set_register_value(uint8_t value)
|
||||
{
|
||||
_registers[_selected_register] = value;
|
||||
if(_selected_register < 14)
|
||||
registers_[selected_register_] = value;
|
||||
if(selected_register_ < 14)
|
||||
{
|
||||
int selected_register = _selected_register;
|
||||
int selected_register = selected_register_;
|
||||
enqueue([=] () {
|
||||
uint8_t masked_value = value;
|
||||
switch(selected_register)
|
||||
@ -201,34 +201,34 @@ void AY38910::set_register_value(uint8_t value)
|
||||
int channel = selected_register >> 1;
|
||||
|
||||
if(selected_register & 1)
|
||||
_tone_periods[channel] = (_tone_periods[channel] & 0xff) | (uint16_t)((value&0xf) << 8);
|
||||
tone_periods_[channel] = (tone_periods_[channel] & 0xff) | (uint16_t)((value&0xf) << 8);
|
||||
else
|
||||
_tone_periods[channel] = (_tone_periods[channel] & ~0xff) | value;
|
||||
_tone_counters[channel] = _tone_periods[channel];
|
||||
tone_periods_[channel] = (tone_periods_[channel] & ~0xff) | value;
|
||||
tone_counters_[channel] = tone_periods_[channel];
|
||||
}
|
||||
break;
|
||||
|
||||
case 6:
|
||||
_noise_period = value & 0x1f;
|
||||
_noise_counter = _noise_period;
|
||||
noise_period_ = value & 0x1f;
|
||||
noise_counter_ = noise_period_;
|
||||
break;
|
||||
|
||||
case 11:
|
||||
_envelope_period = (_envelope_period & ~0xff) | value;
|
||||
_envelope_divider = _envelope_period;
|
||||
envelope_period_ = (envelope_period_ & ~0xff) | value;
|
||||
envelope_divider_ = envelope_period_;
|
||||
break;
|
||||
|
||||
case 12:
|
||||
_envelope_period = (_envelope_period & 0xff) | (int)(value << 8);
|
||||
_envelope_divider = _envelope_period;
|
||||
envelope_period_ = (envelope_period_ & 0xff) | (int)(value << 8);
|
||||
envelope_divider_ = envelope_period_;
|
||||
break;
|
||||
|
||||
case 13:
|
||||
masked_value &= 0xf;
|
||||
_envelope_position = 0;
|
||||
envelope_position_ = 0;
|
||||
break;
|
||||
}
|
||||
_output_registers[selected_register] = masked_value;
|
||||
output_registers_[selected_register] = masked_value;
|
||||
evaluate_output_volume();
|
||||
});
|
||||
}
|
||||
@ -244,22 +244,22 @@ uint8_t AY38910::get_register_value()
|
||||
0xe0, 0xe0, 0xe0, 0x00, 0x00, 0xf0, 0x00, 0x00
|
||||
};
|
||||
|
||||
return _registers[_selected_register] | register_masks[_selected_register];
|
||||
return registers_[selected_register_] | register_masks[selected_register_];
|
||||
}
|
||||
|
||||
uint8_t AY38910::get_port_output(bool port_b)
|
||||
{
|
||||
return _registers[port_b ? 15 : 14];
|
||||
return registers_[port_b ? 15 : 14];
|
||||
}
|
||||
|
||||
void AY38910::set_data_input(uint8_t r)
|
||||
{
|
||||
_data_input = r;
|
||||
data_input_ = r;
|
||||
}
|
||||
|
||||
uint8_t AY38910::get_data_output()
|
||||
{
|
||||
return _data_output;
|
||||
return data_output_;
|
||||
}
|
||||
|
||||
void AY38910::set_control_lines(ControlLines control_lines)
|
||||
@ -277,15 +277,15 @@ void AY38910::set_control_lines(ControlLines control_lines)
|
||||
case (int)(BCDIR | BC2): new_state = Write; break;
|
||||
}
|
||||
|
||||
if(new_state != _control_state)
|
||||
if(new_state != control_state_)
|
||||
{
|
||||
_control_state = new_state;
|
||||
control_state_ = new_state;
|
||||
switch(new_state)
|
||||
{
|
||||
default: break;
|
||||
case LatchAddress: select_register(_data_input); break;
|
||||
case Write: set_register_value(_data_input); break;
|
||||
case Read: _data_output = get_register_value(); break;
|
||||
case LatchAddress: select_register(data_input_); break;
|
||||
case Write: set_register_value(data_input_); break;
|
||||
case Read: data_output_ = get_register_value(); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,42 +51,42 @@ class AY38910: public ::Outputs::Filter<AY38910> {
|
||||
void get_samples(unsigned int number_of_samples, int16_t *target);
|
||||
|
||||
private:
|
||||
int _selected_register;
|
||||
uint8_t _registers[16], _output_registers[16];
|
||||
int selected_register_;
|
||||
uint8_t registers_[16], output_registers_[16];
|
||||
|
||||
int _master_divider;
|
||||
int master_divider_;
|
||||
|
||||
int _tone_periods[3];
|
||||
int _tone_counters[3];
|
||||
int _tone_outputs[3];
|
||||
int tone_periods_[3];
|
||||
int tone_counters_[3];
|
||||
int tone_outputs_[3];
|
||||
|
||||
int _noise_period;
|
||||
int _noise_counter;
|
||||
int _noise_shift_register;
|
||||
int _noise_output;
|
||||
int noise_period_;
|
||||
int noise_counter_;
|
||||
int noise_shift_register_;
|
||||
int noise_output_;
|
||||
|
||||
int _envelope_period;
|
||||
int _envelope_divider;
|
||||
int _envelope_position;
|
||||
int _envelope_shapes[16][32];
|
||||
int _envelope_overflow_masks[16];
|
||||
int envelope_period_;
|
||||
int envelope_divider_;
|
||||
int envelope_position_;
|
||||
int envelope_shapes_[16][32];
|
||||
int envelope_overflow_masks_[16];
|
||||
|
||||
int _volumes[16];
|
||||
int volumes_[16];
|
||||
|
||||
enum ControlState {
|
||||
Inactive,
|
||||
LatchAddress,
|
||||
Read,
|
||||
Write
|
||||
} _control_state;
|
||||
} control_state_;
|
||||
|
||||
void select_register(uint8_t r);
|
||||
void set_register_value(uint8_t value);
|
||||
uint8_t get_register_value();
|
||||
|
||||
uint8_t _data_input, _data_output;
|
||||
uint8_t data_input_, data_output_;
|
||||
|
||||
int16_t _output_volume;
|
||||
int16_t output_volume_;
|
||||
inline void evaluate_output_volume();
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user