2016-10-15 01:18:03 +00:00
|
|
|
|
//
|
|
|
|
|
// AY-3-8910.cpp
|
|
|
|
|
// Clock Signal
|
|
|
|
|
//
|
|
|
|
|
// Created by Thomas Harte on 14/10/2016.
|
2018-05-13 19:19:52 +00:00
|
|
|
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
2016-10-15 01:18:03 +00:00
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#include "AY38910.hpp"
|
|
|
|
|
|
2017-12-19 02:39:23 +00:00
|
|
|
|
#include <cmath>
|
|
|
|
|
|
2017-08-16 02:47:17 +00:00
|
|
|
|
using namespace GI::AY38910;
|
2016-10-15 01:18:03 +00:00
|
|
|
|
|
2019-12-19 00:28:41 +00:00
|
|
|
|
AY38910::AY38910(Personality personality, Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) {
|
2019-12-19 03:03:02 +00:00
|
|
|
|
// Don't use the low bit of the envelope position if this is an AY.
|
|
|
|
|
envelope_position_mask_ |= personality == Personality::AY38910;
|
|
|
|
|
|
2019-12-19 00:28:41 +00:00
|
|
|
|
// Set up envelope lookup tables.
|
2017-03-26 18:34:47 +00:00
|
|
|
|
for(int c = 0; c < 16; c++) {
|
2019-12-19 03:03:02 +00:00
|
|
|
|
for(int p = 0; p < 64; p++) {
|
2017-03-26 18:34:47 +00:00
|
|
|
|
switch(c) {
|
2016-10-20 01:13:22 +00:00
|
|
|
|
case 0: case 1: case 2: case 3: case 9:
|
2019-12-19 03:03:02 +00:00
|
|
|
|
/* Envelope: \____ */
|
|
|
|
|
envelope_shapes_[c][p] = (p < 32) ? (p^0x1f) : 0;
|
|
|
|
|
envelope_overflow_masks_[c] = 0x3f;
|
2016-10-20 01:13:22 +00:00
|
|
|
|
break;
|
|
|
|
|
case 4: case 5: case 6: case 7: case 15:
|
2019-12-19 03:03:02 +00:00
|
|
|
|
/* Envelope: /____ */
|
|
|
|
|
envelope_shapes_[c][p] = (p < 32) ? p : 0;
|
|
|
|
|
envelope_overflow_masks_[c] = 0x3f;
|
2016-10-20 01:13:22 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 8:
|
2019-12-19 03:03:02 +00:00
|
|
|
|
/* Envelope: \\\\\\\\ */
|
|
|
|
|
envelope_shapes_[c][p] = (p & 0x1f) ^ 0x1f;
|
2016-12-03 15:51:09 +00:00
|
|
|
|
envelope_overflow_masks_[c] = 0x00;
|
2016-10-20 01:13:22 +00:00
|
|
|
|
break;
|
|
|
|
|
case 12:
|
2019-12-19 03:03:02 +00:00
|
|
|
|
/* Envelope: //////// */
|
|
|
|
|
envelope_shapes_[c][p] = (p & 0x1f);
|
2016-12-03 15:51:09 +00:00
|
|
|
|
envelope_overflow_masks_[c] = 0x00;
|
2016-10-20 01:13:22 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 10:
|
2019-12-19 03:03:02 +00:00
|
|
|
|
/* Envelope: \/\/\/\/ */
|
|
|
|
|
envelope_shapes_[c][p] = (p & 0x1f) ^ ((p < 32) ? 0x1f : 0x0);
|
2016-12-03 15:51:09 +00:00
|
|
|
|
envelope_overflow_masks_[c] = 0x00;
|
2016-10-20 01:13:22 +00:00
|
|
|
|
break;
|
|
|
|
|
case 14:
|
2019-12-19 03:03:02 +00:00
|
|
|
|
/* Envelope: /\/\/\/\ */
|
|
|
|
|
envelope_shapes_[c][p] = (p & 0x1f) ^ ((p < 32) ? 0x0 : 0x1f);
|
2016-12-03 15:51:09 +00:00
|
|
|
|
envelope_overflow_masks_[c] = 0x00;
|
2016-10-20 01:13:22 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 11:
|
2019-12-19 03:03:02 +00:00
|
|
|
|
/* Envelope: \------ (if - is high) */
|
|
|
|
|
envelope_shapes_[c][p] = (p < 32) ? (p^0x1f) : 0x1f;
|
|
|
|
|
envelope_overflow_masks_[c] = 0x3f;
|
2016-10-20 01:13:22 +00:00
|
|
|
|
break;
|
|
|
|
|
case 13:
|
2019-12-19 03:03:02 +00:00
|
|
|
|
/* Envelope: /------- */
|
|
|
|
|
envelope_shapes_[c][p] = (p < 32) ? p : 0x1f;
|
|
|
|
|
envelope_overflow_masks_[c] = 0x3f;
|
2016-10-20 01:13:22 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-20 03:07:51 +00:00
|
|
|
|
|
2018-03-09 18:23:18 +00:00
|
|
|
|
set_sample_volume_range(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AY38910::set_sample_volume_range(std::int16_t range) {
|
2016-10-20 03:07:51 +00:00
|
|
|
|
// set up volume lookup table
|
2019-12-22 05:22:17 +00:00
|
|
|
|
const float max_volume = float(range) / 3.0f; // As there are three channels.
|
|
|
|
|
constexpr float root_two = 1.414213562373095f;
|
2019-12-19 03:03:02 +00:00
|
|
|
|
for(int v = 0; v < 32; v++) {
|
|
|
|
|
volumes_[v] = int(max_volume / powf(root_two, float(v ^ 0x1f) / 2.0f));
|
2016-10-20 03:07:51 +00:00
|
|
|
|
}
|
2018-03-09 18:23:18 +00:00
|
|
|
|
evaluate_output_volume();
|
2016-10-15 01:18:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-18 02:26:06 +00:00
|
|
|
|
void AY38910::get_samples(std::size_t number_of_samples, int16_t *target) {
|
2019-12-19 03:03:02 +00:00
|
|
|
|
// Note on structure below: the real AY has a built-in divider of 8
|
|
|
|
|
// prior to applying its tone and noise dividers. But the YM fills the
|
|
|
|
|
// same total periods for noise and tone with double-precision envelopes.
|
|
|
|
|
// Therefore this class implements a divider of 4 and doubles the tone
|
|
|
|
|
// and noise periods. The envelope ticks along at the divide-by-four rate,
|
|
|
|
|
// but if this is an AY rather than a YM then its lowest bit is forced to 1,
|
|
|
|
|
// matching the YM datasheet's depiction of envelope level 31 as equal to
|
|
|
|
|
// programmatic volume 15, envelope level 29 as equal to programmatic 14, etc.
|
|
|
|
|
|
2018-01-07 04:16:01 +00:00
|
|
|
|
std::size_t c = 0;
|
2019-12-19 03:03:02 +00:00
|
|
|
|
while((master_divider_&3) && c < number_of_samples) {
|
2016-12-03 15:51:09 +00:00
|
|
|
|
target[c] = output_volume_;
|
|
|
|
|
master_divider_++;
|
2016-10-24 00:32:48 +00:00
|
|
|
|
c++;
|
|
|
|
|
}
|
2016-10-20 01:43:18 +00:00
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
while(c < number_of_samples) {
|
2016-10-24 00:32:48 +00:00
|
|
|
|
#define step_channel(c) \
|
2016-12-03 15:51:09 +00:00
|
|
|
|
if(tone_counters_[c]) tone_counters_[c]--;\
|
2017-03-26 18:34:47 +00:00
|
|
|
|
else {\
|
2016-12-03 15:51:09 +00:00
|
|
|
|
tone_outputs_[c] ^= 1;\
|
2019-12-19 03:03:02 +00:00
|
|
|
|
tone_counters_[c] = tone_periods_[c] << 1;\
|
2016-10-24 00:32:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-19 03:03:02 +00:00
|
|
|
|
// Update the tone channels.
|
2016-10-20 01:43:18 +00:00
|
|
|
|
step_channel(0);
|
|
|
|
|
step_channel(1);
|
|
|
|
|
step_channel(2);
|
|
|
|
|
|
2016-10-22 00:05:38 +00:00
|
|
|
|
#undef step_channel
|
|
|
|
|
|
2019-12-19 03:03:02 +00:00
|
|
|
|
// Update the noise generator. This recomputes the new bit repeatedly but harmlessly, only shifting
|
2016-10-20 01:51:35 +00:00
|
|
|
|
// it into the official 17 upon divider underflow.
|
2016-12-03 15:51:09 +00:00
|
|
|
|
if(noise_counter_) noise_counter_--;
|
2017-03-26 18:34:47 +00:00
|
|
|
|
else {
|
2019-12-19 03:03:02 +00:00
|
|
|
|
noise_counter_ = noise_period_ << 1; // To cover the double resolution of envelopes.
|
2016-12-03 15:51:09 +00:00
|
|
|
|
noise_output_ ^= noise_shift_register_&1;
|
|
|
|
|
noise_shift_register_ |= ((noise_shift_register_ ^ (noise_shift_register_ >> 3))&1) << 17;
|
|
|
|
|
noise_shift_register_ >>= 1;
|
2016-10-22 00:05:38 +00:00
|
|
|
|
}
|
2016-10-20 01:13:22 +00:00
|
|
|
|
|
2019-12-19 03:03:02 +00:00
|
|
|
|
// Update the envelope generator. Table based for pattern lookup, with a 'refill' step: a way of
|
|
|
|
|
// implementing non-repeating patterns by locking them to the final table position.
|
2016-12-03 15:51:09 +00:00
|
|
|
|
if(envelope_divider_) envelope_divider_--;
|
2017-03-26 18:34:47 +00:00
|
|
|
|
else {
|
2016-12-03 15:51:09 +00:00
|
|
|
|
envelope_divider_ = envelope_period_;
|
|
|
|
|
envelope_position_ ++;
|
2019-12-19 03:03:02 +00:00
|
|
|
|
if(envelope_position_ == 64) envelope_position_ = envelope_overflow_masks_[output_registers_[13]];
|
2016-10-22 00:05:38 +00:00
|
|
|
|
}
|
2016-10-20 01:13:22 +00:00
|
|
|
|
|
2016-10-22 00:05:38 +00:00
|
|
|
|
evaluate_output_volume();
|
2016-10-20 01:43:18 +00:00
|
|
|
|
|
2019-12-19 03:03:02 +00:00
|
|
|
|
for(int ic = 0; ic < 4 && c < number_of_samples; ic++) {
|
2016-12-03 15:51:09 +00:00
|
|
|
|
target[c] = output_volume_;
|
2016-10-22 00:05:38 +00:00
|
|
|
|
c++;
|
2016-12-03 15:51:09 +00:00
|
|
|
|
master_divider_++;
|
2016-10-22 00:05:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-31 02:51:08 +00:00
|
|
|
|
|
2019-12-19 03:03:02 +00:00
|
|
|
|
master_divider_ &= 3;
|
2016-10-22 00:05:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
void AY38910::evaluate_output_volume() {
|
2019-12-19 03:03:02 +00:00
|
|
|
|
int envelope_volume = envelope_shapes_[output_registers_[13]][envelope_position_ | envelope_position_mask_];
|
2016-10-22 00:05:38 +00:00
|
|
|
|
|
|
|
|
|
// 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.
|
2018-05-13 19:34:31 +00:00
|
|
|
|
// The tone/noise enable bits use inverse logic; 0 = on, 1 = off; permitting the OR logic below.
|
2016-12-03 15:51:09 +00:00
|
|
|
|
#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))
|
2016-10-19 02:20:12 +00:00
|
|
|
|
|
2016-11-10 02:21:17 +00:00
|
|
|
|
#define level(c, tone_bit, noise_bit) tone_level(c, tone_bit) & noise_level(c, noise_bit) & 1
|
|
|
|
|
const int channel_levels[3] = {
|
2016-10-22 00:05:38 +00:00
|
|
|
|
level(0, 0, 3),
|
|
|
|
|
level(1, 1, 4),
|
|
|
|
|
level(2, 2, 5),
|
|
|
|
|
};
|
2016-10-20 01:43:18 +00:00
|
|
|
|
#undef level
|
|
|
|
|
|
2019-12-19 03:03:02 +00:00
|
|
|
|
// Channel volume is a simple selection: if the bit at 0x10 is set, use the envelope volume; otherwise use the lower four bits,
|
|
|
|
|
// mapped to the range 1–31 in case this is a YM.
|
2016-10-20 01:43:18 +00:00
|
|
|
|
#define channel_volume(c) \
|
2019-12-19 03:03:02 +00:00
|
|
|
|
((output_registers_[c] >> 4)&1) * envelope_volume + (((output_registers_[c] >> 4)&1)^1) * (((output_registers_[c]&0xf) << 1) + 1)
|
2016-10-20 01:13:22 +00:00
|
|
|
|
|
2016-11-10 02:21:17 +00:00
|
|
|
|
const int volumes[3] = {
|
2016-10-22 00:05:38 +00:00
|
|
|
|
channel_volume(8),
|
|
|
|
|
channel_volume(9),
|
|
|
|
|
channel_volume(10)
|
|
|
|
|
};
|
2016-10-20 01:43:18 +00:00
|
|
|
|
#undef channel_volume
|
2016-10-19 02:20:12 +00:00
|
|
|
|
|
2016-10-22 00:05:38 +00:00
|
|
|
|
// Mix additively.
|
2017-10-04 02:04:15 +00:00
|
|
|
|
output_volume_ = static_cast<int16_t>(
|
2016-12-03 15:51:09 +00:00
|
|
|
|
volumes_[volumes[0]] * channel_levels[0] +
|
|
|
|
|
volumes_[volumes[1]] * channel_levels[1] +
|
|
|
|
|
volumes_[volumes[2]] * channel_levels[2]
|
2016-10-22 00:05:38 +00:00
|
|
|
|
);
|
2016-10-15 01:18:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-03 18:08:33 +00:00
|
|
|
|
bool AY38910::is_zero_level() {
|
|
|
|
|
// Confirm that the AY is trivially at the zero level if all three volume controls are set to fixed zero.
|
|
|
|
|
return output_registers_[0x8] == 0 && output_registers_[0x9] == 0 && output_registers_[0xa] == 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-12 20:59:11 +00:00
|
|
|
|
// MARK: - Register manipulation
|
2017-08-02 23:45:58 +00:00
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
void AY38910::select_register(uint8_t r) {
|
2017-08-07 23:51:36 +00:00
|
|
|
|
selected_register_ = r;
|
2016-10-15 01:35:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
void AY38910::set_register_value(uint8_t value) {
|
2019-03-06 01:20:26 +00:00
|
|
|
|
// There are only 16 registers.
|
2017-08-07 23:51:36 +00:00
|
|
|
|
if(selected_register_ > 15) return;
|
2019-03-06 01:20:26 +00:00
|
|
|
|
|
|
|
|
|
// If this is a register that affects audio output, enqueue a mutation onto the
|
|
|
|
|
// audio generation thread.
|
2017-03-26 18:34:47 +00:00
|
|
|
|
if(selected_register_ < 14) {
|
2019-03-06 01:20:26 +00:00
|
|
|
|
const int selected_register = selected_register_;
|
2017-12-18 02:26:06 +00:00
|
|
|
|
task_queue_.defer([=] () {
|
2019-03-06 01:20:26 +00:00
|
|
|
|
// Perform any register-specific mutation to output generation.
|
2016-10-20 01:13:22 +00:00
|
|
|
|
uint8_t masked_value = value;
|
2017-03-26 18:34:47 +00:00
|
|
|
|
switch(selected_register) {
|
2016-10-15 21:45:39 +00:00
|
|
|
|
case 0: case 2: case 4:
|
2017-03-26 18:34:47 +00:00
|
|
|
|
case 1: case 3: case 5: {
|
2016-10-22 02:12:44 +00:00
|
|
|
|
int channel = selected_register >> 1;
|
2016-10-22 02:16:44 +00:00
|
|
|
|
|
2016-10-22 02:12:44 +00:00
|
|
|
|
if(selected_register & 1)
|
2017-10-04 02:04:15 +00:00
|
|
|
|
tone_periods_[channel] = (tone_periods_[channel] & 0xff) | static_cast<uint16_t>((value&0xf) << 8);
|
2016-10-22 02:12:44 +00:00
|
|
|
|
else
|
2016-12-03 15:51:09 +00:00
|
|
|
|
tone_periods_[channel] = (tone_periods_[channel] & ~0xff) | value;
|
2016-10-22 02:12:44 +00:00
|
|
|
|
}
|
2016-10-15 21:45:39 +00:00
|
|
|
|
break;
|
|
|
|
|
|
2016-10-22 00:07:14 +00:00
|
|
|
|
case 6:
|
2016-12-03 15:51:09 +00:00
|
|
|
|
noise_period_ = value & 0x1f;
|
2016-10-22 00:07:14 +00:00
|
|
|
|
break;
|
|
|
|
|
|
2016-10-15 21:45:39 +00:00
|
|
|
|
case 11:
|
2016-12-03 15:51:09 +00:00
|
|
|
|
envelope_period_ = (envelope_period_ & ~0xff) | value;
|
2016-10-15 21:45:39 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 12:
|
2017-10-22 01:00:40 +00:00
|
|
|
|
envelope_period_ = (envelope_period_ & 0xff) | static_cast<int>(value << 8);
|
2016-10-20 01:13:22 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 13:
|
|
|
|
|
masked_value &= 0xf;
|
2016-12-03 15:51:09 +00:00
|
|
|
|
envelope_position_ = 0;
|
2016-10-15 21:45:39 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-03-06 01:20:26 +00:00
|
|
|
|
|
|
|
|
|
// Store a copy of the current register within the storage used by the audio generation
|
|
|
|
|
// thread, and apply any changes to output volume.
|
2016-12-03 15:51:09 +00:00
|
|
|
|
output_registers_[selected_register] = masked_value;
|
2016-10-22 00:05:38 +00:00
|
|
|
|
evaluate_output_volume();
|
2016-10-15 21:45:39 +00:00
|
|
|
|
});
|
|
|
|
|
}
|
2019-03-06 01:20:26 +00:00
|
|
|
|
|
|
|
|
|
// Decide which outputs are going to need updating (if any).
|
|
|
|
|
bool update_port_a = false;
|
|
|
|
|
bool update_port_b = true;
|
|
|
|
|
if(port_handler_) {
|
|
|
|
|
if(selected_register_ == 7) {
|
|
|
|
|
const uint8_t io_change = registers_[7] ^ value;
|
|
|
|
|
update_port_b = !!(io_change&0x80);
|
|
|
|
|
update_port_a = !!(io_change&0x40);
|
|
|
|
|
} else {
|
|
|
|
|
update_port_b = selected_register_ == 15;
|
|
|
|
|
update_port_a = selected_register_ != 15;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Keep a copy of the new value that is usable from the emulation thread.
|
|
|
|
|
registers_[selected_register_] = value;
|
|
|
|
|
|
|
|
|
|
// Update ports as required.
|
|
|
|
|
if(update_port_b) set_port_output(true);
|
|
|
|
|
if(update_port_a) set_port_output(false);
|
2016-10-15 01:35:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
uint8_t AY38910::get_register_value() {
|
2017-08-16 13:29:48 +00:00
|
|
|
|
// This table ensures that bits that aren't defined within the AY are returned as 0s
|
|
|
|
|
// when read, conforming to CPC-sourced unit tests.
|
2016-11-10 02:21:17 +00:00
|
|
|
|
const uint8_t register_masks[16] = {
|
2017-08-16 13:29:48 +00:00
|
|
|
|
0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0x1f, 0xff,
|
|
|
|
|
0x1f, 0x1f, 0x1f, 0xff, 0xff, 0x0f, 0xff, 0xff
|
2016-11-10 02:21:17 +00:00
|
|
|
|
};
|
|
|
|
|
|
2017-08-07 23:51:36 +00:00
|
|
|
|
if(selected_register_ > 15) return 0xff;
|
2018-06-26 23:31:16 +00:00
|
|
|
|
return registers_[selected_register_] & register_masks[selected_register_];
|
2016-10-15 01:44:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-26 23:31:16 +00:00
|
|
|
|
// MARK: - Port querying
|
2017-08-02 23:45:58 +00:00
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
uint8_t AY38910::get_port_output(bool port_b) {
|
2016-12-03 15:51:09 +00:00
|
|
|
|
return registers_[port_b ? 15 : 14];
|
2016-10-15 01:35:15 +00:00
|
|
|
|
}
|
2016-10-18 23:32:15 +00:00
|
|
|
|
|
2017-11-12 20:59:11 +00:00
|
|
|
|
// MARK: - Bus handling
|
2017-08-02 23:45:58 +00:00
|
|
|
|
|
2017-08-16 02:47:17 +00:00
|
|
|
|
void AY38910::set_port_handler(PortHandler *handler) {
|
|
|
|
|
port_handler_ = handler;
|
2019-03-06 01:20:26 +00:00
|
|
|
|
set_port_output(true);
|
|
|
|
|
set_port_output(false);
|
2017-08-16 02:47:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
void AY38910::set_data_input(uint8_t r) {
|
2016-12-03 15:51:09 +00:00
|
|
|
|
data_input_ = r;
|
2017-08-02 23:45:58 +00:00
|
|
|
|
update_bus();
|
2016-10-18 23:32:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-06 01:20:26 +00:00
|
|
|
|
void AY38910::set_port_output(bool port_b) {
|
|
|
|
|
// Per the data sheet: "each [IO] pin is provided with an on-chip pull-up resistor,
|
|
|
|
|
// so that when in the "input" mode, all pins will read normally high". Therefore,
|
|
|
|
|
// report programmer selection of input mode as creating an output of 0xff.
|
|
|
|
|
if(port_handler_) {
|
|
|
|
|
const bool is_output = !!(registers_[7] & (port_b ? 0x80 : 0x40));
|
|
|
|
|
port_handler_->set_port_output(port_b, is_output ? registers_[port_b ? 15 : 14] : 0xff);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
uint8_t AY38910::get_data_output() {
|
2018-06-26 23:31:16 +00:00
|
|
|
|
if(control_state_ == Read && selected_register_ >= 14 && selected_register_ < 16) {
|
|
|
|
|
// Per http://cpctech.cpc-live.com/docs/psgnotes.htm if a port is defined as output then the
|
|
|
|
|
// value returned to the CPU when reading it is the and of the output value and any input.
|
|
|
|
|
// If it's defined as input then you just get the input.
|
|
|
|
|
const uint8_t mask = port_handler_ ? port_handler_->get_port_input(selected_register_ == 15) : 0xff;
|
|
|
|
|
|
|
|
|
|
switch(selected_register_) {
|
2018-11-24 03:32:32 +00:00
|
|
|
|
default: break;
|
2018-06-26 23:31:16 +00:00
|
|
|
|
case 14: return mask & ((registers_[0x7] & 0x40) ? registers_[14] : 0xff);
|
|
|
|
|
case 15: return mask & ((registers_[0x7] & 0x80) ? registers_[15] : 0xff);
|
2017-08-16 02:47:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-03 15:51:09 +00:00
|
|
|
|
return data_output_;
|
2016-10-18 23:32:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
void AY38910::set_control_lines(ControlLines control_lines) {
|
2017-10-22 01:00:40 +00:00
|
|
|
|
switch(static_cast<int>(control_lines)) {
|
2017-08-02 23:45:58 +00:00
|
|
|
|
default: control_state_ = Inactive; break;
|
2016-10-18 23:32:15 +00:00
|
|
|
|
|
2017-10-22 01:00:40 +00:00
|
|
|
|
case static_cast<int>(BDIR | BC2 | BC1):
|
2017-08-01 21:06:57 +00:00
|
|
|
|
case BDIR:
|
2017-08-02 23:45:58 +00:00
|
|
|
|
case BC1: control_state_ = LatchAddress; break;
|
2016-10-18 23:32:15 +00:00
|
|
|
|
|
2017-10-22 01:00:40 +00:00
|
|
|
|
case static_cast<int>(BC2 | BC1): control_state_ = Read; break;
|
|
|
|
|
case static_cast<int>(BDIR | BC2): control_state_ = Write; break;
|
2016-10-18 23:32:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-02 23:45:58 +00:00
|
|
|
|
update_bus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AY38910::update_bus() {
|
2018-06-26 23:31:16 +00:00
|
|
|
|
// Assume no output, unless this turns out to be a read.
|
2018-06-26 00:48:24 +00:00
|
|
|
|
data_output_ = 0xff;
|
2017-08-02 23:45:58 +00:00
|
|
|
|
switch(control_state_) {
|
2018-11-24 03:32:32 +00:00
|
|
|
|
default: break;
|
2017-08-02 23:45:58 +00:00
|
|
|
|
case LatchAddress: select_register(data_input_); break;
|
|
|
|
|
case Write: set_register_value(data_input_); break;
|
|
|
|
|
case Read: data_output_ = get_register_value(); break;
|
|
|
|
|
}
|
2016-10-18 23:32:15 +00:00
|
|
|
|
}
|