- [NEW] mòdul zx_system per a gestionar la vida i canvi de systemes (48K, 128K...)
256 lines
9.2 KiB
C++
256 lines
9.2 KiB
C++
#include "ay-3-8912.h"
|
|
#include "z80.h"
|
|
#include <SDL2/SDL.h>
|
|
#include "zx_ula.h"
|
|
|
|
#define MIXER_REG_A_TONE 0x01
|
|
#define MIXER_REG_B_TONE 0x02
|
|
#define MIXER_REG_C_TONE 0x04
|
|
#define MIXER_REG_A_NOISE 0x08
|
|
#define MIXER_REG_B_NOISE 0x10
|
|
#define MIXER_REG_C_NOISE 0x20
|
|
#define MIXER_REG_PORT_A 0x40
|
|
#define MIXER_REG_PORT_B 0x80
|
|
|
|
#define ENVELOPE_HOLD 0x01
|
|
#define ENVELOPE_ALTERNATE 0x02
|
|
#define ENVELOPE_ATTACK 0x04
|
|
#define ENVELOPE_CONTINUE 0x08
|
|
|
|
namespace audio
|
|
{
|
|
bool enabled = false;
|
|
float cycles_per_sample;
|
|
|
|
uint8_t volume_table[16] {0,2,3,4,6,8,11,16,23,32,45,64,90,128,180,255};
|
|
SDL_AudioDeviceID sdlAudioDevice;
|
|
|
|
uint8_t selected_register {0};
|
|
uint8_t registers[16];
|
|
|
|
uint32_t channel_a_tone_freq;
|
|
uint32_t channel_a_tone_freq_counter;
|
|
uint8_t channel_a_tone_level;
|
|
uint8_t channel_a_level;
|
|
|
|
uint32_t channel_b_tone_freq;
|
|
uint32_t channel_b_tone_freq_counter;
|
|
uint8_t channel_b_tone_level;
|
|
uint8_t channel_b_level;
|
|
|
|
uint32_t channel_c_tone_freq;
|
|
uint32_t channel_c_tone_freq_counter;
|
|
uint8_t channel_c_tone_level;
|
|
uint8_t channel_c_level;
|
|
|
|
uint32_t noise_freq;
|
|
uint32_t noise_freq_counter;
|
|
uint8_t noise_level;
|
|
uint32_t shiftreg;
|
|
|
|
uint32_t envelope_freq;
|
|
uint32_t envelope_freq_counter;
|
|
int8_t envelope_volume;
|
|
int8_t envelope_direction;
|
|
|
|
void select_register(int port, int val)
|
|
{
|
|
selected_register = val & 0xf;
|
|
}
|
|
|
|
int read_register(int port)
|
|
{
|
|
return registers[selected_register];
|
|
}
|
|
|
|
void write_register(int port, int val)
|
|
{
|
|
registers[selected_register] = val;
|
|
|
|
switch (selected_register) {
|
|
case 0:
|
|
case 1: {
|
|
uint32_t freq = uint32_t(registers[0]) + (uint32_t(registers[1]) * 256);
|
|
channel_a_tone_freq = (freq==0?1:freq)<<4;
|
|
channel_a_tone_freq_counter = 0;
|
|
break;
|
|
}
|
|
case 2:
|
|
case 3: {
|
|
uint32_t freq = uint32_t(registers[2]) + (uint32_t(registers[3]) * 256);
|
|
channel_b_tone_freq = (freq==0?1:freq)<<4;
|
|
channel_b_tone_freq_counter = 0;
|
|
break;
|
|
}
|
|
case 4:
|
|
case 5: {
|
|
uint32_t freq = uint32_t(registers[4]) + (uint32_t(registers[5]) * 256);
|
|
channel_c_tone_freq = (freq==0?1:freq)<<4;
|
|
channel_c_tone_freq_counter = 0;
|
|
break;
|
|
}
|
|
case 6: {
|
|
uint32_t freq = (registers[6] & 0x1f);
|
|
noise_freq = (freq==0?1:freq)<<4;
|
|
noise_freq_counter = 0;
|
|
break;
|
|
}
|
|
case 11:
|
|
case 12: {
|
|
uint32_t freq = registers[11] | (registers[12] << 8);
|
|
envelope_freq = (freq==0?1:freq)<<4;
|
|
break;
|
|
}
|
|
case 13:
|
|
if (registers[13]&ENVELOPE_ATTACK) {
|
|
envelope_volume = 0;
|
|
envelope_direction = 1;
|
|
} else {
|
|
envelope_volume = 15;
|
|
envelope_direction = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void init()
|
|
{
|
|
reset();
|
|
z80::connect_port(0xfffd, 0xffff, audio::read_register, audio::select_register);
|
|
z80::connect_port(0xbffd, 0xffff, nullptr, audio::write_register);
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
enabled = true;
|
|
selected_register = 0;
|
|
for (int i=0; i<16;++i) registers[i]=0;
|
|
}
|
|
|
|
void update(uint32_t dt)
|
|
{
|
|
if (!enabled) return;
|
|
//dt = dt >> 1;
|
|
|
|
// Oscillate (0-1) channel A tone level given its frequency
|
|
channel_a_tone_freq_counter+=dt;
|
|
if (channel_a_tone_freq_counter >= channel_a_tone_freq) {
|
|
channel_a_tone_freq_counter -= channel_a_tone_freq;
|
|
channel_a_tone_level = channel_a_tone_level ^ 1;
|
|
}
|
|
|
|
// Oscillate (0-1) channel B tone level given its frequency
|
|
channel_b_tone_freq_counter+=dt;
|
|
if (channel_b_tone_freq_counter >= channel_b_tone_freq) {
|
|
channel_b_tone_freq_counter -= channel_b_tone_freq;
|
|
channel_b_tone_level = channel_b_tone_level ^ 1;
|
|
}
|
|
|
|
// Oscillate (0-1) channel C tone level given its frequency
|
|
channel_c_tone_freq_counter+=dt;
|
|
if (channel_c_tone_freq_counter >= channel_c_tone_freq) {
|
|
channel_c_tone_freq_counter -= channel_c_tone_freq;
|
|
channel_c_tone_level = channel_c_tone_level ^ 1;
|
|
}
|
|
|
|
// Oscillate (0-1) noise level given its frequency and shift register
|
|
noise_freq_counter+=dt;
|
|
if (noise_freq_counter >= noise_freq) {
|
|
noise_freq_counter -= noise_freq;
|
|
//noise_level = noise_level ^ shiftreg;
|
|
//uint32_t newbit = shiftreg ^ (shiftreg >> 3);
|
|
//shiftreg = ((shiftreg >> 1) & 0xffff) | ((newbit << 16) & 0x10000);
|
|
shiftreg = (shiftreg * 2 + 1) ^ (((shiftreg >> 16) ^ (shiftreg >> 13)) & 1);
|
|
noise_level = ((shiftreg >> 16) & 1);
|
|
}
|
|
|
|
|
|
// Develop (0-15) envelope volume given its frequency and shape
|
|
envelope_freq_counter+=dt;
|
|
if (envelope_freq_counter >= envelope_freq) {
|
|
envelope_freq_counter -= envelope_freq;
|
|
|
|
envelope_volume += envelope_direction;
|
|
if ( (envelope_volume > 15) || (envelope_volume < 0) ) {
|
|
switch(registers[13]&0xf) {
|
|
case 8:
|
|
case 12:
|
|
envelope_volume &= 0x0f;
|
|
break;
|
|
case 10:
|
|
case 14:
|
|
envelope_direction = -envelope_direction;
|
|
envelope_volume += envelope_direction;
|
|
case 11:
|
|
case 13:
|
|
envelope_direction = 0;
|
|
envelope_volume = 15;
|
|
default:
|
|
envelope_direction = 0;
|
|
envelope_volume = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t get_sample()
|
|
{
|
|
// Mix tone and noise on channel A given register 7 values
|
|
channel_a_level = 1;
|
|
if ((registers[7] & MIXER_REG_A_TONE)==0) channel_a_level &= channel_a_tone_level;
|
|
if ((registers[7] & MIXER_REG_A_NOISE)==0) channel_a_level &= noise_level;
|
|
|
|
// Mix tone and noise on channel B given register 7 values
|
|
channel_b_level = 1;
|
|
if ((registers[7] & MIXER_REG_B_TONE)==0) channel_b_level &= channel_b_tone_level;
|
|
if ((registers[7] & MIXER_REG_B_NOISE)==0) channel_b_level &= noise_level;
|
|
|
|
// Mix tone and noise on channel C given register 7 values
|
|
channel_c_level = 1;
|
|
if ((registers[7] & MIXER_REG_C_TONE)==0) channel_c_level &= channel_c_tone_level;
|
|
if ((registers[7] & MIXER_REG_C_NOISE)==0) channel_c_level &= noise_level;
|
|
|
|
//zx_ula::set_border_color(channel_a_tone_level&0xf);
|
|
|
|
const uint8_t channel_a_volume = (registers[8]&0x10) ? envelope_volume : registers[8]&0xf;
|
|
const uint8_t channel_b_volume = (registers[9]&0x10) ? envelope_volume : registers[9]&0xf;
|
|
const uint8_t channel_c_volume = (registers[10]&0x10) ? envelope_volume : registers[10]&0xf;
|
|
|
|
const uint8_t channel_a_sample = volume_table[(channel_a_level&1) * channel_a_volume] >> 1;
|
|
const uint8_t channel_b_sample = volume_table[(channel_b_level&1) * channel_b_volume] >> 1;
|
|
const uint8_t channel_c_sample = volume_table[(channel_c_level&1) * channel_c_volume] >> 1;
|
|
|
|
uint8_t sample = (channel_a_sample+channel_b_sample+channel_c_sample)&0xff;
|
|
return sample;
|
|
}
|
|
|
|
namespace debug
|
|
{
|
|
uint8_t get_register(uint8_t num) { return registers[num]; }
|
|
uint32_t get_channel_a_tone_freq() { return channel_a_tone_freq; }
|
|
uint32_t get_channel_a_tone_freq_counter() { return channel_a_tone_freq_counter; }
|
|
uint8_t get_channel_a_tone_level() { return channel_a_tone_level; }
|
|
uint8_t get_channel_a_level() { return channel_a_level; }
|
|
|
|
uint32_t get_channel_b_tone_freq() { return channel_b_tone_freq; }
|
|
uint32_t get_channel_b_tone_freq_counter() { return channel_b_tone_freq_counter; }
|
|
uint8_t get_channel_b_tone_level() { return channel_b_tone_level; }
|
|
uint8_t get_channel_b_level() { return channel_b_level; }
|
|
|
|
uint32_t get_channel_c_tone_freq() { return channel_c_tone_freq; }
|
|
uint32_t get_channel_c_tone_freq_counter() { return channel_c_tone_freq_counter; }
|
|
uint8_t get_channel_c_tone_level() { return channel_c_tone_level; }
|
|
uint8_t get_channel_c_level() { return channel_c_level; }
|
|
|
|
uint32_t get_noise_freq() { return noise_freq; }
|
|
uint32_t get_noise_freq_counter() { return noise_freq_counter; }
|
|
uint8_t get_noise_level() { return noise_level; }
|
|
uint32_t get_shiftreg() { return shiftreg; }
|
|
|
|
uint32_t get_envelope_freq() { return envelope_freq; }
|
|
uint32_t get_envelope_freq_counter() { return envelope_freq_counter; }
|
|
int8_t get_envelope_volume() { return envelope_volume; }
|
|
int8_t get_envelope_direction() { return envelope_direction; }
|
|
}
|
|
|
|
}
|