1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-03 11:30:02 +00:00
CLK/Components/AY38910/AY38910.cpp

104 lines
2.3 KiB
C++

//
// AY-3-8910.cpp
// Clock Signal
//
// Created by Thomas Harte on 14/10/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "AY38910.hpp"
using namespace GI;
AY38910::AY38910() : _selected_register(0), _channel_ouput{0, 0, 0}
{
_output_registers[8] = _output_registers[9] = _output_registers[10] = 0;
}
void AY38910::set_clock_rate(double clock_rate)
{
set_input_rate((float)clock_rate);
}
#define step(c) \
_channel_dividers[c] -= resulting_steps; \
if(!_channel_dividers[c]) \
{ \
_channel_dividers[c] = (int)_tone_generator_controls[c] + 1; \
_channel_ouput[c] ^= 1; \
}
void AY38910::get_samples(unsigned int number_of_samples, int16_t *target)
{
for(int c = 0; c < number_of_samples; c++)
{
// a master divider divides the clock by 16
int former_master_divider = _master_divider;
_master_divider++;
int resulting_steps = ((_master_divider ^ former_master_divider) >> 4) & 1;
// from that the three channels count down
step(0);
step(1);
step(2);
*target++ = (int16_t)((
((_output_registers[8]&0xf) * _channel_ouput[0]) +
((_output_registers[9]&0xf) * _channel_ouput[1]) +
((_output_registers[10]&0xf) * _channel_ouput[2])
) * 512);
}
}
void AY38910::skip_samples(unsigned int number_of_samples)
{
// TODO
}
void AY38910::select_register(uint8_t r)
{
_selected_register = r & 0xf;
}
void AY38910::set_register_value(uint8_t value)
{
_registers[_selected_register] = value;
if(value < 14)
{
int selected_register = _selected_register;
enqueue([=] () {
_output_registers[selected_register] = value;
switch(selected_register)
{
case 0: case 2: case 4:
_tone_generator_controls[selected_register >> 1] =
(_tone_generator_controls[selected_register >> 1] & ~0xff) | value;
break;
case 1: case 3: case 5:
_tone_generator_controls[selected_register >> 1] =
(_tone_generator_controls[selected_register >> 1] & 0xff) | (uint16_t)((value&0xf) << 8);
break;
case 11:
_envelope_period = (_envelope_period & ~0xff) | value;
break;
case 12:
_envelope_period = (_envelope_period & 0xff) | (uint16_t)(value << 8);
break;
}
});
}
}
uint8_t AY38910::get_register_value()
{
return _registers[_selected_register];
}
uint8_t AY38910::get_port_output(bool port_b)
{
return _registers[port_b ? 15 : 14];
}