#include <stdint.h>

#include <avr/pgmspace.h>

#include "sn76489.h"

// Stores the SN76489 channel states.
struct sn76489_channel_t sn76489_channels[4];

uint8_t sn76489_latched_channel;
uint8_t sn76489_latched_mode;

uint16_t sn76489_shift_register;
uint8_t sn76489_shift_register_width;
uint16_t sn76489_tapped_bits;

uint8_t sn76489_stereo_mask;

uint8_t sn76489_noise_update;

uint32_t sn76489_frequency;

// Logarithmic amplitude table (approximate).
const uint8_t sn76489_amplitude_log[] PROGMEM = { 63, 50, 40, 32, 25, 20, 16, 13, 10, 8, 6, 5, 4, 3, 1, 0 };

// Initialises the SN76489.
void sn76489_init(void) {
	for (uint8_t ch = 0; ch < 4; ++ch) {
		sn76489_channels[ch].period = 0;
		sn76489_channels[ch].amplitude = 0;
		sn76489_channels[ch].counter = 0;
		sn76489_channels[ch].state = 0;
	}
	sn76489_shift_register_width = 16;
	sn76489_tapped_bits = 0x0009;
	sn76489_stereo_mask = 0xFF;
	sn76489_noise_update = 0;
	sn76489_frequency = 3579545;
}

// Writes a control byte to the emulated PSG.
void sn76489_write(uint8_t value) {
	if (value & 0x80) {
		// Latch/Data write.
		sn76489_latched_channel = (value >> 5) & 0x3;
		if ((sn76489_latched_mode = ((value >> 4) & 0x1))) {
			sn76489_channels[sn76489_latched_channel].amplitude = pgm_read_byte(sn76489_amplitude_log + (value & 0x0F));
		} else {
			sn76489_channels[sn76489_latched_channel].period &= 0xFFF0;
			sn76489_channels[sn76489_latched_channel].period |= (value & 0x0F);
			if (sn76489_latched_channel == 3) sn76489_shift_register = (1 << (sn76489_shift_register_width - 1));
		}
	} else {
		// Data write.
		if (sn76489_latched_mode) {
			sn76489_channels[sn76489_latched_channel].amplitude = pgm_read_byte(sn76489_amplitude_log + (value & 0x0F));
		} else {
			sn76489_channels[sn76489_latched_channel].period &= 0xF;
			sn76489_channels[sn76489_latched_channel].period |= ((value & 0x3F) << 4);
		}
	}
}