From ada37abe233ded73ebd34a40a6ebec8cfb543065 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 19 Oct 2016 21:13:22 -0400 Subject: [PATCH] Made an attempt to implement noise and envelopes. Not quite right yet. --- Components/AY38910/AY38910.cpp | 123 ++++++++++++++++++++++++++++----- Components/AY38910/AY38910.hpp | 17 +++-- 2 files changed, 120 insertions(+), 20 deletions(-) diff --git a/Components/AY38910/AY38910.cpp b/Components/AY38910/AY38910.cpp index a26f1deb7..b10b73d4c 100644 --- a/Components/AY38910/AY38910.cpp +++ b/Components/AY38910/AY38910.cpp @@ -10,9 +10,59 @@ using namespace GI; -AY38910::AY38910() : _selected_register(0), _channel_ouput{0, 0, 0}, _channel_dividers{0, 0, 0}, _tone_generator_controls{0, 0, 0} +AY38910::AY38910() : + _selected_register(0), + _channel_output{0, 0, 0}, _channel_dividers{0, 0, 0}, _tone_generator_controls{0, 0, 0}, + _noise_shift_register(0xffff), _noise_divider(0), _noise_output(0), + _envelope_divider(0), _envelope_period(0) { _output_registers[8] = _output_registers[9] = _output_registers[10] = 0; + + // set up envelope lookup tables + for(int c = 0; c < 16; c++) + { + for(int p = 0; p < 32; p++) + { + 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; + break; + case 4: case 5: case 6: case 7: case 15: + _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; + break; + case 12: + _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; + break; + case 14: + _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; + break; + case 13: + _envelope_shapes[c][p] = (p < 16) ? p : 0xf; + _envelope_overflow_masks[c] = 0x1f; + break; + } + } + } } void AY38910::set_clock_rate(double clock_rate) @@ -30,12 +80,12 @@ void AY38910::get_samples(unsigned int number_of_samples, int16_t *target) int resulting_steps = ((_master_divider ^ former_master_divider) >> 4) & 1; // from that the three channels count down -#define step(c) {\ + int did_underflow; +#define step(c) \ _channel_dividers[c] -= resulting_steps; \ - int did_underflow = (_channel_dividers[c] >> 15)&1; \ - _channel_ouput[c] ^= did_underflow; \ - _channel_dividers[c] = did_underflow * _tone_generator_controls[c] + (did_underflow^1) * _channel_dividers[c]; \ - } + did_underflow = (_channel_dividers[c] >> 15)&1; \ + _channel_output[c] ^= did_underflow; \ + _channel_dividers[c] = did_underflow * _tone_generator_controls[c] + (did_underflow^1) * _channel_dividers[c]; step(0); step(1); @@ -43,19 +93,51 @@ void AY38910::get_samples(unsigned int number_of_samples, int16_t *target) #undef step - // ... as does the envelope generator -// _envelope_divider -= resulting_steps; -// if(!_envelope_divider) + // ... as does the noise generator + _noise_divider -= resulting_steps; + did_underflow = (_noise_divider >> 15)&1; + _noise_divider = did_underflow * (_output_registers[6]&0x1f) + (did_underflow^1) * _noise_divider; + _noise_output ^= did_underflow&_noise_shift_register&1; + _noise_shift_register |= ((_noise_shift_register ^ (_noise_shift_register >> 3))&1) << 17; + _noise_shift_register >>= did_underflow; + + // ... and the envelope generator + _envelope_divider -= resulting_steps; + did_underflow = (_envelope_divider >> 15)&1; + _envelope_divider = did_underflow * _envelope_period + (did_underflow^1) * _envelope_divider; + _envelope_position += did_underflow; + +// if(_output_registers[13] == 13) // { -// _envelope_divider = _envelope_period; +// printf("[%d] %d", _envelope_divider, _envelope_position); // } -// if(_output_registers[9]) printf("%d %d / %d\n", _channel_ouput[1], _channel_dividers[1], _tone_generator_controls[1]); + int refill = _envelope_overflow_masks[_output_registers[13]] * (_envelope_position >> 5); + _envelope_position = (_envelope_position & 0x1f) | refill; + + int envelope_volume = _envelope_shapes[_output_registers[13]][_envelope_position & 0xf]; + +// if(_output_registers[13] == 13) +// { +// printf(": %d\n", envelope_volume); +// } + + int channel_levels[3] = { + (((((_output_registers[7] >> 0)&1)^1) & _channel_output[0]) | ((((_output_registers[7] >> 1)&1)^1) & _noise_output)) ^ 1, + (((((_output_registers[7] >> 2)&1)^1) & _channel_output[1]) | ((((_output_registers[7] >> 3)&1)^1) & _noise_output)) ^ 1, + (((((_output_registers[7] >> 4)&1)^1) & _channel_output[2]) | ((((_output_registers[7] >> 5)&1)^1) & _noise_output)) ^ 1, + }; + + int volumes[3] = { + ((_output_registers[8] >> 4)&1) * envelope_volume + (((_output_registers[8] >> 4)&1)^1) * (_output_registers[8]&0x1f), + ((_output_registers[9] >> 4)&1) * envelope_volume + (((_output_registers[9] >> 4)&1)^1) * (_output_registers[9]&0x1f), + ((_output_registers[10] >> 4)&1) * envelope_volume + (((_output_registers[10] >> 4)&1)^1) * (_output_registers[10]&0x1f), + }; target[c] = (int16_t)(( - (_output_registers[8]&0xf) * _channel_ouput[0] + - (_output_registers[9]&0xf) * _channel_ouput[1] + - (_output_registers[10]&0xf) * _channel_ouput[2] + volumes[0] * channel_levels[0] + + volumes[1] * channel_levels[1] + + volumes[2] * channel_levels[2] ) * 512); } } @@ -78,6 +160,7 @@ void AY38910::set_register_value(uint8_t value) { int selected_register = _selected_register; enqueue([=] () { + uint8_t masked_value = value; switch(selected_register) { case 0: case 2: case 4: @@ -92,13 +175,21 @@ void AY38910::set_register_value(uint8_t value) case 11: _envelope_period = (_envelope_period & ~0xff) | value; +// printf("e: %d", _envelope_period); break; case 12: - _envelope_period = (_envelope_period & 0xff) | (uint16_t)(value << 8); + _envelope_period = (_envelope_period & 0xff) | (int)(value << 8); +// printf("e: %d", _envelope_period); + break; + + case 13: + masked_value &= 0xf; + _envelope_position = 0; +// printf("envelope %d\n", masked_value); break; } - _output_registers[selected_register] = value; + _output_registers[selected_register] = masked_value; }); } } diff --git a/Components/AY38910/AY38910.hpp b/Components/AY38910/AY38910.hpp index 49080a482..2e79163a9 100644 --- a/Components/AY38910/AY38910.hpp +++ b/Components/AY38910/AY38910.hpp @@ -37,13 +37,22 @@ class AY38910: public ::Outputs::Filter { uint8_t _registers[16], _output_registers[16]; uint16_t _tone_generator_controls[3]; - uint16_t _envelope_period; + int _channel_dividers[3]; + int _channel_output[3]; + int _master_divider; - int _channel_dividers[3]; + + int _noise_divider; + int _noise_shift_register; + int _noise_output; + + int _envelope_period; int _envelope_divider; - int _evelope_volume; - int _channel_ouput[3]; + + int _envelope_position; + int _envelope_shapes[16][32]; + int _envelope_overflow_masks[16]; enum ControlState { Inactive,