#include "tia-audio.h" namespace tia { namespace audio { static const uint16_t BIT_PATTERN_15 = 0x7556; static const uint32_t BIT_PATTERN_31 = 0x74567254; static const uint8_t LFSR511_TABLE[511] = { 1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,1,1,1,0,0,1,0,1, 0,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,1,1,1,0,0,0,0,0,1,0,1,0,1,0,0, 1,0,0,1,0,0,0,1,0,0,1,1,0,0,0,1,0,1,1,1,0,0,1,1,1,0,1,0,1,1,0, 1,0,0,0,1,1,0,1,1,0,0,0,1,1,0,0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0, 0,0,1,1,0,1,1,1,1,0,1,0,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,1,0,0,0, 1,0,0,0,0,1,0,0,1,0,1,0,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,1, 1,1,0,1,0,0,0,1,0,1,0,0,1,1,1,0,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0, 1,0,0,1,1,1,1,1,1,0,1,1,0,1,1,1,0,0,1,0,0,0,0,1,1,1,0,1,1,0,1, 1,1,0,0,1,0,1,1,0,1,1,0,0,1,1,1,1,1,0,0,1,1,0,1,0,1,0,1,0,0,0, 1,0,1,1,1,1,0,0,1,0,1,0,0,0,1,0,1,1,0,1,0,1,1,1,0,1,1,0,0,0,0, 1,0,1,1,0,0,1,0,1,0,1,1,1,1,0,1,1,0,1,0,0,1,0,1,0,0,1,0,1,1,0, 0,0,0,0,1,0,0,0,1,1,1,1,1,1,0,1,0,0,0,0,0,1,1,1,0,0,0,1,0,1,0, 1,1,0,1,1,0,1,0,1,0,0,1,0,0,1,1,0,1,0,0,0,0,0,0,0,1,1,0,1,1,1, 0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,1,1,1,0,1,1,1,1,0,0,1,1,1, 1,0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,1,0, 1,0,1,1,0,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1,0,0,1,1,0,1,1,1,0,1,0, 1,0,0,0,0,0,1,1 }; uint8_t get_lfsr511_bit(uint32_t step) { return LFSR511_TABLE[step % 511]; } uint8_t get_poly_mode_bit(uint8_t audc, uint32_t step) { switch (audc & 0x0F) { case 0: case 11: return 1; // Direct volume (PCM output) case 1: // 4-bit Poly (15 steps) return (BIT_PATTERN_15 >> (step % 15)) & 1; case 2: // 5-bit Poly -> 4-bit Poly divisor (31 steps) return (BIT_PATTERN_31 >> (step % 31)) & 1; case 3: { // 5-bit Poly XOR 4-bit Poly (465 steps) uint8_t b5 = (BIT_PATTERN_31 >> (step % 31)) & 1; uint8_t b4 = (BIT_PATTERN_15 >> (step % 15)) & 1; return b5 ^ b4; } case 4: case 5: // Pure Tone: Divide frequency clock by 2 return (step & 1); case 6: case 10: // Divide by 31 then divide by 2 return ((step / 31) & 1); case 7: case 9: // 5-bit Poly output variant return ((BIT_PATTERN_31 >> ((step / 2) % 31)) & 1); case 8: // White Noise (9-bit Poly / 511 steps) return get_lfsr511_bit(step); case 12: case 13: // Pure Tone: Divide frequency clock by 6 return ((step / 3) & 1); case 14: // Divide by 93 (31 * 3) then divide by 2 return ((step / 93) & 1); case 15: // 5-bit Poly clocked down by 6 return ((BIT_PATTERN_31 >> ((step / 6) % 31)) & 1); default: return 0; } } struct channel { uint8_t control = 0; uint8_t frequency = 0; uint8_t volume = 0; uint8_t freq_counter = 0; uint32_t poly_counter = 0; }; channel channels[2]; void tick() { for (int i=0; i<2; ++i) { if (channels[i].freq_counter == 0) { channels[i].freq_counter = channels[i].frequency + 1; channels[i].poly_counter++; } else { channels[i].freq_counter--; } } } void write(uint16_t address, uint8_t value) { switch (address) { case 0x15: // AUDC0 channels[0].control = value & 0x0f; break; case 0x16: // AUDC1 channels[1].control = value & 0x0f; break; case 0x17: // AUDF0 channels[0].frequency = value & 0x1f; break; case 0x18: // AUDF1 channels[1].frequency = value & 0x1f; break; case 0x19: // AUDV0 channels[0].volume = value & 0x0f; break; case 0x1A: // AUDV1 channels[1].volume = value & 0x0f; break; } } uint8_t get_sample0() { return get_sample(0); } uint8_t get_sample1() { return get_sample(1); } uint8_t get_sample(uint8_t channel) { if (channels[channel].volume == 0) return 0; bool bit_out = false; switch (channels[channel].control) { case 0: case 11: // Set to 1: Direct volume output (PCM mode) bit_out = true; break; case 1: // 4-bit Poly (15-step repeating sequence) bit_out = (BIT_PATTERN_15 >> (channels[channel].poly_counter % 15)) & 1; break; case 2: // 5-bit Poly (31-step repeating sequence) bit_out = (BIT_PATTERN_31 >> (channels[channel].poly_counter % 31)) & 1; break; case 4: case 5: // Pure tone (Divide by 2 square wave) bit_out = (channels[channel].poly_counter & 1); break; case 8: // White noise (9-bit Poly / 511-step sequence) bit_out = get_lfsr511_bit(channels[channel].poly_counter % 511); break; case 12: case 13: // Pure tone (Divide by 6 square wave) bit_out = ((channels[channel].poly_counter / 3) & 1); break; default: // Handle remaining poly modes (3, 6, 7, 9, 10, 14, 15) via lookup table bit_out = get_poly_mode_bit(channels[channel].control, channels[channel].poly_counter); break; } return bit_out ? channels[channel].volume * 17 : 0; } } }