MSXHawk: audio

This commit is contained in:
alyosha-tas 2020-01-24 19:47:16 -05:00
parent bfde89b9f3
commit 263fdfebc3
13 changed files with 553 additions and 476 deletions

View File

@ -67,12 +67,10 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
/// Get Video data
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="aud_buf_L">where to send left audio to</param>
/// <param name="aud_buf_R">where to send right audio to</param>
/// <param name="n_samp_L">number of left samples</param>
/// <param name="n_samp_R">number of right samples</param>
/// <param name="aud_buf">where to send left audio to</param>
/// <param name="n_samp">number of left samples</param>
[DllImport("MSXHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint MSX_get_audio(IntPtr core, uint[] aud_buf_L, uint[] aud_buf_R, ref uint n_samp_L, ref uint n_samp_R);
public static extern uint MSX_get_audio(IntPtr core, uint[] aud_buf, ref uint n_samp);
#endregion

View File

@ -70,27 +70,19 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
MSX_Pntr = IntPtr.Zero;
}
if (blip_L != null)
if (blip != null)
{
blip_L.Dispose();
blip_L = null;
}
if (blip_R != null)
{
blip_R.Dispose();
blip_R = null;
blip.Dispose();
blip = null;
}
}
#region Audio
public BlipBuffer blip_L = new BlipBuffer(4500);
public BlipBuffer blip_R = new BlipBuffer(4500);
public BlipBuffer blip = new BlipBuffer(4500);
public uint[] Aud_L = new uint [9000];
public uint[] Aud_R = new uint[9000];
public uint num_samp_L, num_samp_R;
public uint[] Aud = new uint [9000];
public uint num_samp;
const int blipbuffsize = 4500;
@ -116,32 +108,24 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
public void GetSamplesSync(out short[] samples, out int nsamp)
{
uint f_clock = LibMSX.MSX_get_audio(MSX_Pntr, Aud_L, Aud_R, ref num_samp_L, ref num_samp_R);
uint f_clock = LibMSX.MSX_get_audio(MSX_Pntr, Aud, ref num_samp);
for (int i = 0; i < num_samp_L;i++)
for (int i = 0; i < num_samp;i++)
{
blip_L.AddDelta(Aud_L[i * 2], (int)Aud_L[i * 2 + 1]);
blip.AddDelta(Aud[i * 2], (int)Aud[i * 2 + 1]);
}
for (int i = 0; i < num_samp_R; i++)
{
blip_R.AddDelta(Aud_R[i * 2], (int)Aud_R[i * 2 + 1]);
}
blip.EndFrame(f_clock);
blip_L.EndFrame(f_clock);
blip_R.EndFrame(f_clock);
nsamp = Math.Max(Math.Max(blip_L.SamplesAvailable(), blip_R.SamplesAvailable()), 1);
nsamp = blip.SamplesAvailable();
samples = new short[nsamp * 2];
blip_L.ReadSamplesLeft(samples, nsamp);
blip_R.ReadSamplesRight(samples, nsamp);
blip.ReadSamples(samples, nsamp, true);
}
public void DiscardSamples()
{
blip_L.Clear();
blip_R.Clear();
blip.Clear();
}
#endregion

View File

@ -43,8 +43,7 @@ namespace BizHawk.Emulation.Cores.Computers.MSX
LibMSX.MSX_load_bios(MSX_Pntr, Bios, Basic);
LibMSX.MSX_load(MSX_Pntr, RomData, (uint)RomData.Length, 0, RomData, (uint)RomData.Length, 0);
blip_L.SetRates(3579545, 44100);
blip_R.SetRates(3579545, 44100);
blip.SetRates(3579545, 44100);
(ServiceProvider as BasicServiceProvider).Register<ISoundProvider>(this);

View File

@ -281,7 +281,7 @@ namespace BizHawk.Emulation.Cores.ColecoVision
}
else
{
v = (short)(sound_out_A ? VolumeTable[vol_A] : 0);
v = (short)(sound_out_A ? VolumeTable[env_E] : 0);
}
if (env_vol_B == 0)

View File

@ -282,7 +282,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Vectrex
}
else
{
v = (short)(sound_out_A ? VolumeTable[vol_A] : 0);
v = (short)(sound_out_A ? VolumeTable[env_E] : 0);
}
if (env_vol_B == 0)

View File

@ -0,0 +1,439 @@
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
using namespace std;
namespace MSXHawk
{
class AY_3_8910
{
public:
#pragma region AY_3_8910
AY_3_8910()
{
Reset();
}
bool A_on, B_on, C_on;
bool A_up, B_up, C_up;
bool A_noise, B_noise, C_noise;
bool env_vol_A, env_vol_B, env_vol_C;
uint8_t env_shape;
uint8_t port_sel;
uint8_t vol_A, vol_B, vol_C;
uint8_t Register[16] = {};
uint32_t psg_clock;
uint32_t sq_per_A, sq_per_B, sq_per_C;
uint32_t clock_A, clock_B, clock_C;
uint32_t env_per;
uint32_t env_clock;
int32_t env_E;
int32_t E_up_down;
uint32_t noise_clock;
uint32_t noise_per;
uint32_t noise = 0x1;
uint32_t old_sample;
// non stated if only on frame boundaries
bool sound_out_A;
bool sound_out_B;
bool sound_out_C;
uint8_t Clock_Divider;
uint32_t current_sample;
uint32_t sampleclock;
uint32_t num_samples;
uint32_t samples[9000] = {};
void Reset()
{
clock_A = clock_B = clock_C = 0x1000;
noise_clock = 0x20;
port_sel = 0;
for (int i = 0; i < 16; i++)
{
Register[i] = 0x0;
}
sync_psg_state();
}
short Sample()
{
return current_sample;
}
const uint32_t VolumeTable[16] =
{
0x0000, 0x0055, 0x0079, 0x00AB, 0x00F1, 0x0155, 0x01E3, 0x02AA,
0x03C5, 0x0555, 0x078B, 0x0AAB, 0x0F16, 0x1555, 0x1E2B, 0x2AAA
};
uint8_t ReadReg()
{
return Register[port_sel];
}
void sync_psg_state()
{
sq_per_A = (Register[0] & 0xFF) | (((Register[1] & 0xF) << 8));
if (sq_per_A == 0)
{
sq_per_A = 0x1000;
}
sq_per_B = (Register[2] & 0xFF) | (((Register[3] & 0xF) << 8));
if (sq_per_B == 0)
{
sq_per_B = 0x1000;
}
sq_per_C = (Register[4] & 0xFF) | (((Register[5] & 0xF) << 8));
if (sq_per_C == 0)
{
sq_per_C = 0x1000;
}
env_per = (Register[11] & 0xFF) | (((Register[12] & 0xFF) << 8));
if (env_per == 0)
{
env_per = 0x10000;
}
env_per *= 2;
A_on = (Register[7] & 0x1) > 0;
B_on = (Register[7] & 0x2) > 0;
C_on = (Register[7] & 0x4) > 0;
A_noise = (Register[7] & 0x8) > 0;
B_noise = (Register[7] & 0x10) > 0;
C_noise = (Register[7] & 0x20) > 0;
noise_per = Register[6] & 0x1F;
if (noise_per == 0)
{
noise_per = 0x20;
}
uint8_t shape_select = Register[13] & 0xF;
if (shape_select < 4) { env_shape = 0; }
else if (shape_select < 8) { env_shape = 1; }
else { env_shape = 2 + (shape_select - 8); }
vol_A = Register[8] & 0xF;
env_vol_A = ((Register[8] >> 4) & 0x1) > 0;
vol_B = Register[9] & 0xF;
env_vol_B = ((Register[9] >> 4) & 0x1) > 0;
vol_C = Register[10] & 0xF;
env_vol_C = ((Register[10] >> 4) & 0x1) > 0;
}
void WriteReg(uint8_t value)
{
value &= 0xFF;
Register[port_sel] = value;
sync_psg_state();
if (port_sel == 13)
{
env_clock = env_per;
if (env_shape == 0 || env_shape == 2 || env_shape == 3 || env_shape == 4 || env_shape == 5)
{
env_E = 15;
E_up_down = -1;
}
else
{
env_E = 0;
E_up_down = 1;
}
}
}
void generate_sound()
{
// there are 8 cpu cycles for every psg cycle
clock_A--;
clock_B--;
clock_C--;
noise_clock--;
env_clock--;
// clock noise
if (noise_clock == 0)
{
noise = (noise >> 1) ^ (((noise &0x1) > 0) ? 0x10004 : 0);
noise_clock = noise_per;
}
if (env_clock == 0)
{
env_clock = env_per;
env_E += E_up_down;
if (env_E == 16 || env_E == -1)
{
// we just completed a period of the envelope, determine what to do now based on the envelope shape
if (env_shape == 0 || env_shape == 1 || env_shape == 3 || env_shape == 9)
{
E_up_down = 0;
env_E = 0;
}
else if (env_shape == 5 || env_shape == 7)
{
E_up_down = 0;
env_E = 15;
}
else if (env_shape == 4 || env_shape == 8)
{
if (env_E == 16)
{
env_E = 15;
E_up_down = -1;
}
else
{
env_E = 0;
E_up_down = 1;
}
}
else if (env_shape == 2)
{
env_E = 15;
}
else
{
env_E = 0;
}
}
}
if (clock_A == 0)
{
A_up = !A_up;
clock_A = sq_per_A;
}
if (clock_B == 0)
{
B_up = !B_up;
clock_B = sq_per_B;
}
if (clock_C == 0)
{
C_up = !C_up;
clock_C = sq_per_C;
}
sound_out_A = (((noise & 0x1) > 0) | A_noise) & (A_on | A_up);
sound_out_B = (((noise & 0x1) > 0) | B_noise) & (B_on | B_up);
sound_out_C = (((noise & 0x1) > 0) | C_noise) & (C_on | C_up);
// now calculate the volume of each channel and add them together
current_sample = 0;
if (env_vol_A)
{
current_sample = (sound_out_A ? VolumeTable[env_E] : 0);
}
else
{
current_sample = (sound_out_A ? VolumeTable[vol_A] : 0);
}
if (env_vol_B)
{
current_sample += (sound_out_B ? VolumeTable[env_E] : 0);
}
else
{
current_sample += (sound_out_B ? VolumeTable[vol_B] : 0);
}
if (env_vol_C)
{
current_sample += (sound_out_C ? VolumeTable[env_E] : 0);
}
else
{
current_sample += (sound_out_C ? VolumeTable[vol_C] : 0);
}
if ((current_sample != old_sample) && (num_samples < 4500))
{
samples[num_samples * 2] = sampleclock;
samples[num_samples * 2 + 1] = current_sample - old_sample;
num_samples++;
old_sample = current_sample;
}
}
#pragma endregion
#pragma region State Save / Load
uint8_t* SaveState(uint8_t* saver)
{
*saver = (uint8_t)(A_on ? 1 : 0); saver++;
*saver = (uint8_t)(B_on ? 1 : 0); saver++;
*saver = (uint8_t)(C_on ? 1 : 0); saver++;
*saver = (uint8_t)(A_up ? 1 : 0); saver++;
*saver = (uint8_t)(B_up ? 1 : 0); saver++;
*saver = (uint8_t)(C_up ? 1 : 0); saver++;
*saver = (uint8_t)(A_noise ? 1 : 0); saver++;
*saver = (uint8_t)(B_noise ? 1 : 0); saver++;
*saver = (uint8_t)(C_noise ? 1 : 0); saver++;
*saver = (uint8_t)(env_vol_A ? 1 : 0); saver++;
*saver = (uint8_t)(env_vol_B ? 1 : 0); saver++;
*saver = (uint8_t)(env_vol_C ? 1 : 0); saver++;
*saver = env_shape; saver++;
*saver = port_sel; saver++;
*saver = vol_A; saver++;
*saver = vol_B; saver++;
*saver = vol_C; saver++;
for (int i = 0; i < 16; i++) { *saver = Register[i]; saver++; }
*saver = (uint8_t)(psg_clock & 0xFF); saver++; *saver = (uint8_t)((psg_clock >> 8) & 0xFF); saver++;
*saver = (uint8_t)((psg_clock >> 16) & 0xFF); saver++; *saver = (uint8_t)((psg_clock >> 24) & 0xFF); saver++;
*saver = (uint8_t)(sq_per_A & 0xFF); saver++; *saver = (uint8_t)((sq_per_A >> 8) & 0xFF); saver++;
*saver = (uint8_t)((sq_per_A >> 16) & 0xFF); saver++; *saver = (uint8_t)((sq_per_A >> 24) & 0xFF); saver++;
*saver = (uint8_t)(sq_per_B & 0xFF); saver++; *saver = (uint8_t)((sq_per_B >> 8) & 0xFF); saver++;
*saver = (uint8_t)((sq_per_B >> 16) & 0xFF); saver++; *saver = (uint8_t)((sq_per_B >> 24) & 0xFF); saver++;
*saver = (uint8_t)(sq_per_C & 0xFF); saver++; *saver = (uint8_t)((sq_per_C >> 8) & 0xFF); saver++;
*saver = (uint8_t)((sq_per_C >> 16) & 0xFF); saver++; *saver = (uint8_t)((sq_per_C >> 24) & 0xFF); saver++;
*saver = (uint8_t)(clock_A & 0xFF); saver++; *saver = (uint8_t)((clock_A >> 8) & 0xFF); saver++;
*saver = (uint8_t)((clock_A >> 16) & 0xFF); saver++; *saver = (uint8_t)((clock_A >> 24) & 0xFF); saver++;
*saver = (uint8_t)(clock_B & 0xFF); saver++; *saver = (uint8_t)((clock_B >> 8) & 0xFF); saver++;
*saver = (uint8_t)((clock_B >> 16) & 0xFF); saver++; *saver = (uint8_t)((clock_B >> 24) & 0xFF); saver++;
*saver = (uint8_t)(clock_C & 0xFF); saver++; *saver = (uint8_t)((clock_C >> 8) & 0xFF); saver++;
*saver = (uint8_t)((clock_C >> 16) & 0xFF); saver++; *saver = (uint8_t)((clock_C >> 24) & 0xFF); saver++;
*saver = (uint8_t)(env_per & 0xFF); saver++; *saver = (uint8_t)((env_per >> 8) & 0xFF); saver++;
*saver = (uint8_t)((env_per >> 16) & 0xFF); saver++; *saver = (uint8_t)((env_per >> 24) & 0xFF); saver++;
*saver = (uint8_t)(env_clock & 0xFF); saver++; *saver = (uint8_t)((env_clock >> 8) & 0xFF); saver++;
*saver = (uint8_t)((env_clock >> 16) & 0xFF); saver++; *saver = (uint8_t)((env_clock >> 24) & 0xFF); saver++;
*saver = (uint8_t)(env_E & 0xFF); saver++; *saver = (uint8_t)((env_E >> 8) & 0xFF); saver++;
*saver = (uint8_t)((env_E >> 16) & 0xFF); saver++; *saver = (uint8_t)((env_E >> 24) & 0xFF); saver++;
*saver = (uint8_t)(E_up_down & 0xFF); saver++; *saver = (uint8_t)((E_up_down >> 8) & 0xFF); saver++;
*saver = (uint8_t)((E_up_down >> 16) & 0xFF); saver++; *saver = (uint8_t)((E_up_down >> 24) & 0xFF); saver++;
*saver = (uint8_t)(noise_clock & 0xFF); saver++; *saver = (uint8_t)((noise_clock >> 8) & 0xFF); saver++;
*saver = (uint8_t)((noise_clock >> 16) & 0xFF); saver++; *saver = (uint8_t)((noise_clock >> 24) & 0xFF); saver++;
*saver = (uint8_t)(noise_per & 0xFF); saver++; *saver = (uint8_t)((noise_per >> 8) & 0xFF); saver++;
*saver = (uint8_t)((noise_per >> 16) & 0xFF); saver++; *saver = (uint8_t)((noise_per >> 24) & 0xFF); saver++;
*saver = (uint8_t)(noise & 0xFF); saver++; *saver = (uint8_t)((noise >> 8) & 0xFF); saver++;
*saver = (uint8_t)((noise >> 16) & 0xFF); saver++; *saver = (uint8_t)((noise >> 24) & 0xFF); saver++;
*saver = (uint8_t)(old_sample & 0xFF); saver++; *saver = (uint8_t)((old_sample >> 8) & 0xFF); saver++;
*saver = (uint8_t)((old_sample >> 16) & 0xFF); saver++; *saver = (uint8_t)((old_sample >> 24) & 0xFF); saver++;
return saver;
}
uint8_t* LoadState(uint8_t* loader)
{
A_on = *loader == 1; loader++;
B_on = *loader == 1; loader++;
C_on = *loader == 1; loader++;
A_up = *loader == 1; loader++;
B_up = *loader == 1; loader++;
C_up = *loader == 1; loader++;
A_noise = *loader == 1; loader++;
B_noise = *loader == 1; loader++;
C_noise = *loader == 1; loader++;
env_vol_A = *loader == 1; loader++;
env_vol_B = *loader == 1; loader++;
env_vol_C = *loader == 1; loader++;
env_shape = *loader; loader++;
port_sel = *loader; loader++;
vol_A = *loader; loader++;
vol_B = *loader; loader++;
vol_C = *loader; loader++;
for (int i = 0; i < 16; i++) { Register[i] = *loader; loader++; }
psg_clock = *loader; loader++; psg_clock |= (*loader << 8); loader++;
psg_clock |= (*loader << 16); loader++; psg_clock |= (*loader << 24); loader++;
sq_per_A = *loader; loader++; sq_per_A |= (*loader << 8); loader++;
sq_per_A |= (*loader << 16); loader++; sq_per_A |= (*loader << 24); loader++;
sq_per_B = *loader; loader++; sq_per_B |= (*loader << 8); loader++;
sq_per_B |= (*loader << 16); loader++; sq_per_B |= (*loader << 24); loader++;
sq_per_C = *loader; loader++; sq_per_C |= (*loader << 8); loader++;
sq_per_C |= (*loader << 16); loader++; sq_per_C |= (*loader << 24); loader++;
clock_A = *loader; loader++; clock_A |= (*loader << 8); loader++;
clock_A |= (*loader << 16); loader++; clock_A |= (*loader << 24); loader++;
clock_B = *loader; loader++; clock_B |= (*loader << 8); loader++;
clock_B |= (*loader << 16); loader++; clock_B |= (*loader << 24); loader++;
clock_C = *loader; loader++; clock_C |= (*loader << 8); loader++;
clock_C |= (*loader << 16); loader++; clock_C |= (*loader << 24); loader++;
env_per = *loader; loader++; env_per |= (*loader << 8); loader++;
env_per |= (*loader << 16); loader++; env_per |= (*loader << 24); loader++;
env_clock = *loader; loader++; env_clock |= (*loader << 8); loader++;
env_clock |= (*loader << 16); loader++; env_clock |= (*loader << 24); loader++;
env_E = *loader; loader++; env_E |= (*loader << 8); loader++;
env_E |= (*loader << 16); loader++; env_E |= (*loader << 24); loader++;
E_up_down = *loader; loader++; E_up_down |= (*loader << 8); loader++;
E_up_down |= (*loader << 16); loader++; E_up_down |= (*loader << 24); loader++;
noise_clock = *loader; loader++; noise_clock |= (*loader << 8); loader++;
noise_clock |= (*loader << 16); loader++; noise_clock |= (*loader << 24); loader++;
noise_per = *loader; loader++; noise_per |= (*loader << 8); loader++;
noise_per |= (*loader << 16); loader++; noise_per |= (*loader << 24); loader++;
noise = *loader; loader++; noise |= (*loader << 8); loader++;
noise |= (*loader << 16); loader++; noise |= (*loader << 24); loader++;
old_sample = *loader; loader++; old_sample |= (*loader << 8); loader++;
old_sample |= (*loader << 16); loader++; old_sample |= (*loader << 24); loader++;
return loader;
}
#pragma endregion
};
}

View File

@ -4,7 +4,7 @@
#include <string>
#include "Z80A.h"
#include "PSG.h"
#include "AY_3_8910.h"
#include "TMS9918A.h"
#include "Memory.h"
@ -26,7 +26,7 @@ namespace MSXHawk
TMS9918A vdp;
Z80A cpu;
SN76489sms psg;
AY_3_8910 psg;
MemoryManager MemMap;
void Load_BIOS(uint8_t* bios, uint8_t* basic)
@ -49,8 +49,7 @@ namespace MSXHawk
uint32_t scanlinesPerFrame = 262;
vdp.SpriteLimit = true;
psg.num_samples_L = 0;
psg.num_samples_R = 0;
psg.num_samples = 0;
psg.sampleclock = 0;
for (uint32_t i = 0; i < scanlinesPerFrame; i++)
@ -90,19 +89,13 @@ namespace MSXHawk
std::memcpy(dst, src, sizeof uint32_t * 256 * 192);
}
uint32_t GetAudio(uint32_t* dest_L, uint32_t* dest_R, uint32_t* n_samp_L, uint32_t* n_samp_R)
uint32_t GetAudio(uint32_t* dest, uint32_t* n_samp)
{
uint32_t* src_L = psg.samples_L;
uint32_t* dst_L = dest_L;
uint32_t* src = psg.samples;
uint32_t* dst = dest;
std::memcpy(dst_L, src_L, sizeof uint32_t * psg.num_samples_L * 2);
n_samp_L[0] = psg.num_samples_L;
uint32_t* src_R = psg.samples_R;
uint32_t* dst_R = dest_R;
std::memcpy(dst_R, src_R, sizeof uint32_t * psg.num_samples_R * 2);
n_samp_R[0] = psg.num_samples_R;
std::memcpy(dst, src, sizeof uint32_t * psg.num_samples * 2);
n_samp[0] = psg.num_samples;
return psg.sampleclock;
}

View File

@ -54,9 +54,9 @@ MSXHawk_EXPORT void MSX_get_video(MSXCore* p, uint32_t* dest)
}
// send audio data to external audio provider
MSXHawk_EXPORT uint32_t MSX_get_audio(MSXCore* p, uint32_t* dest_L, uint32_t* dest_R, uint32_t* n_samp_L, uint32_t* n_samp_R)
MSXHawk_EXPORT uint32_t MSX_get_audio(MSXCore* p, uint32_t* dest, uint32_t* n_samp)
{
return p->GetAudio(dest_L, dest_R, n_samp_L, n_samp_R);
return p->GetAudio(dest, n_samp);
}
#pragma region State Save / Load

View File

@ -161,7 +161,7 @@
<ClInclude Include="Core.h" />
<ClInclude Include="Memory.h" />
<ClInclude Include="TMS9918A.h" />
<ClInclude Include="PSG.h" />
<ClInclude Include="AY_3_8910.h" />
<ClInclude Include="Z80A.h" />
<ClInclude Include="MSXHawk.h" />
</ItemGroup>

View File

@ -6,7 +6,7 @@
#include "Memory.h"
#include "Z80A.h"
#include "TMS9918A.h"
#include "PSG.h"
#include "AY_3_8910.h"
using namespace std;
@ -24,9 +24,9 @@ namespace MSXHawk
{
return vdp_pntr->ReadVdpStatus();
}
else if (port == 0xA1)
else if (port == 0xA2)
{
// not readable
return psg_pntr->ReadReg();
}
else if (port == 0xA8)
{
@ -47,7 +47,11 @@ namespace MSXHawk
else if(port == 0x99) // VDP
{
vdp_pntr->WriteVdpControl(value);
}
}
else if (port == 0xA0)
{
psg_pntr->port_sel = (value & 0xF);
}
else if (port == 0xA1)
{
psg_pntr->WriteReg(value);

View File

@ -9,14 +9,14 @@ namespace MSXHawk
{
class Z80A;
class TMS9918A;
class SN76489sms;
class AY_3_8910;
class MemoryManager
{
public:
TMS9918A* vdp_pntr = nullptr;
SN76489sms* psg_pntr = nullptr;
AY_3_8910* psg_pntr = nullptr;
Z80A* cpu_pntr = nullptr;
uint8_t* rom_1 = nullptr;
uint8_t* rom_2 = nullptr;

View File

@ -1,360 +0,0 @@
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
using namespace std;
namespace MSXHawk
{
class SN76489sms
{
public:
#pragma region PSG
bool vol_tone;
bool noise_type;
bool noise_bit;
bool A_L, B_L, C_L, noise_L;
bool A_R, B_R, C_R, noise_R;
bool A_up, B_up, C_up;
uint8_t Chan_vol[4];
uint8_t stereo_panning;
uint8_t chan_sel;
uint8_t noise_rate;
uint16_t Chan_tone[4];
uint32_t psg_clock;
uint32_t clock_A, clock_B, clock_C;
uint32_t noise_clock;
uint32_t noise;
// only old_sample_L/R is savestated, this only works if savestates are only made at frame boundaries
// These would need to be included for subframe states
uint32_t old_sample_L;
uint32_t old_sample_R;
uint32_t current_sample_L;
uint32_t current_sample_R;
uint32_t sampleclock;
uint32_t num_samples_L;
uint32_t num_samples_R;
uint32_t samples_L[9000] = {};
uint32_t samples_R[9000] = {};
uint32_t Clock_Divider;
const uint8_t LogScale[16] = { 255, 203, 161, 128, 102, 86, 64, 51, 40, 32, 26, 20, 16, 13, 10, 0 };
SN76489sms()
{
Reset();
}
void Reset()
{
clock_A = clock_B = clock_C = 0x1000;
noise_clock = 0x10;
chan_sel = 0;
// reset the shift register
noise = 0x40000;
Chan_vol[0] = 0xF;
Chan_vol[1] = 0xF;
Chan_vol[2] = 0xF;
Chan_vol[3] = 0xF;
Set_Panning(0xFF);
}
void Set_Panning(uint8_t value)
{
A_L = (value & 0x10) != 0;
A_R = (value & 0x01) != 0;
B_L = (value & 0x20) != 0;
B_R = (value & 0x02) != 0;
C_L = (value & 0x40) != 0;
C_R = (value & 0x04) != 0;
noise_L = (value & 0x80) != 0;
noise_R = (value & 0x08) != 0;
stereo_panning = value;
}
uint8_t ReadReg()
{
// not used, reading not allowed, just return 0xFF
return 0xFF;
}
void WriteReg(uint8_t value)
{
// if bit 7 is set, change the latch, otherwise modify the currently latched register
if ((value & 0x80) > 0)
{
chan_sel = (value >> 5) & 3;
vol_tone = ((value & 0x10) > 0);
if (vol_tone)
{
Chan_vol[chan_sel] = (uint8_t)(value & 0xF);
}
else
{
if (chan_sel < 3)
{
Chan_tone[chan_sel] &= 0x3F0;
Chan_tone[chan_sel] |= (uint16_t)(value & 0xF);
}
else
{
noise_type = ((value & 0x4) > 0);
noise_rate = value & 3;
// reset the shift register
noise = 0x40000;
}
}
}
else
{
if (vol_tone)
{
Chan_vol[chan_sel] = (uint8_t)(value & 0xF);
}
else
{
if (chan_sel < 3)
{
Chan_tone[chan_sel] &= 0xF;
Chan_tone[chan_sel] |= (uint16_t)((value & 0x3F) << 4);
}
else
{
noise_type = ((value & 0x4) > 0);
noise_rate = value & 3;
// reset the shift register
noise = 0x40000;
}
}
}
}
void generate_sound()
{
clock_A--;
clock_B--;
clock_C--;
noise_clock--;
// clock noise
if (noise_clock == 0)
{
noise_bit = ((noise & 1) > 0);
if (noise_type)
{
noise = (((noise & 1) ^ ((noise >> 1) & 1)) << 14) | (noise >> 1);
}
else
{
noise = ((noise & 1) << 14) | (noise >> 1);
}
if (noise_rate == 0)
{
noise_clock = 0x10;
}
else if (noise_rate == 1)
{
noise_clock = 0x20;
}
else if (noise_rate == 2)
{
noise_clock = 0x40;
}
else
{
noise_clock = Chan_tone[2] + 1;
}
noise_clock *= 2;
}
if (clock_A == 0)
{
A_up = !A_up;
clock_A = Chan_tone[0] + 1;
}
if (clock_B == 0)
{
B_up = !B_up;
clock_B = Chan_tone[1] + 1;
}
if (clock_C == 0)
{
C_up = !C_up;
clock_C = Chan_tone[2] + 1;
}
// now calculate the volume of each channel and add them together
current_sample_L = (A_L ? (A_up ? LogScale[Chan_vol[0]] * 42 : 0) : 0);
current_sample_L += (B_L ? (B_up ? LogScale[Chan_vol[1]] * 42 : 0) : 0);
current_sample_L += (C_L ? (C_up ? LogScale[Chan_vol[2]] * 42 : 0) : 0);
current_sample_L += (noise_L ? (noise_bit ? LogScale[Chan_vol[3]] * 42 : 0) : 0);
current_sample_R = (A_R ? (A_up ? LogScale[Chan_vol[0]] * 42 : 0) : 0);
current_sample_R += (B_R ? (B_up ? LogScale[Chan_vol[1]] * 42 : 0) : 0);
current_sample_R += (C_R ? (C_up ? LogScale[Chan_vol[2]] * 42 : 0) : 0);
current_sample_R += (noise_R ? (noise_bit ? LogScale[Chan_vol[3]] * 42 : 0) : 0);
if ((current_sample_L != old_sample_L) && (num_samples_L < 4500))
{
samples_L[num_samples_L * 2] = sampleclock;
samples_L[num_samples_L * 2 + 1] = current_sample_L - old_sample_L;
num_samples_L++;
old_sample_L = current_sample_L;
}
if ((current_sample_R != old_sample_R) && (num_samples_R < 4500))
{
samples_R[num_samples_R * 2] = sampleclock;
samples_R[num_samples_R * 2 + 1] = current_sample_R - old_sample_R;
num_samples_R++;
old_sample_R = current_sample_R;
}
}
#pragma endregion
#pragma region State Save / Load
uint8_t* SaveState(uint8_t* saver)
{
*saver = (uint8_t)(vol_tone ? 1 : 0); saver++;
*saver = (uint8_t)(noise_type ? 1 : 0); saver++;
*saver = (uint8_t)(noise_bit ? 1 : 0); saver++;
*saver = (uint8_t)(A_L ? 1 : 0); saver++;
*saver = (uint8_t)(B_L ? 1 : 0); saver++;
*saver = (uint8_t)(C_L ? 1 : 0); saver++;
*saver = (uint8_t)(noise_L ? 1 : 0); saver++;
*saver = (uint8_t)(A_R ? 1 : 0); saver++;
*saver = (uint8_t)(B_R ? 1 : 0); saver++;
*saver = (uint8_t)(C_R ? 1 : 0); saver++;
*saver = (uint8_t)(noise_R ? 1 : 0); saver++;
*saver = (uint8_t)(A_up ? 1 : 0); saver++;
*saver = (uint8_t)(B_up ? 1 : 0); saver++;
*saver = (uint8_t)(C_up ? 1 : 0); saver++;
*saver = Chan_vol[0]; saver++;
*saver = Chan_vol[1]; saver++;
*saver = Chan_vol[2]; saver++;
*saver = Chan_vol[3]; saver++;
*saver = stereo_panning; saver++;
*saver = chan_sel; saver++;
*saver = noise_rate; saver++;
*saver = (uint8_t)(Chan_tone[0] & 0xFF); saver++; *saver = (uint8_t)((Chan_tone[0] >> 8) & 0xFF); saver++;
*saver = (uint8_t)(Chan_tone[1] & 0xFF); saver++; *saver = (uint8_t)((Chan_tone[1] >> 8) & 0xFF); saver++;
*saver = (uint8_t)(Chan_tone[2] & 0xFF); saver++; *saver = (uint8_t)((Chan_tone[2] >> 8) & 0xFF); saver++;
*saver = (uint8_t)(Chan_tone[3] & 0xFF); saver++; *saver = (uint8_t)((Chan_tone[3] >> 8) & 0xFF); saver++;
*saver = (uint8_t)(psg_clock & 0xFF); saver++; *saver = (uint8_t)((psg_clock >> 8) & 0xFF); saver++;
*saver = (uint8_t)((psg_clock >> 16) & 0xFF); saver++; *saver = (uint8_t)((psg_clock >> 24) & 0xFF); saver++;
*saver = (uint8_t)(clock_A & 0xFF); saver++; *saver = (uint8_t)((clock_A >> 8) & 0xFF); saver++;
*saver = (uint8_t)((clock_A >> 16) & 0xFF); saver++; *saver = (uint8_t)((clock_A >> 24) & 0xFF); saver++;
*saver = (uint8_t)(clock_B & 0xFF); saver++; *saver = (uint8_t)((clock_B >> 8) & 0xFF); saver++;
*saver = (uint8_t)((clock_B >> 16) & 0xFF); saver++; *saver = (uint8_t)((clock_B >> 24) & 0xFF); saver++;
*saver = (uint8_t)(clock_C & 0xFF); saver++; *saver = (uint8_t)((clock_C >> 8) & 0xFF); saver++;
*saver = (uint8_t)((clock_C >> 16) & 0xFF); saver++; *saver = (uint8_t)((clock_C >> 24) & 0xFF); saver++;
*saver = (uint8_t)(noise_clock & 0xFF); saver++; *saver = (uint8_t)((noise_clock >> 8) & 0xFF); saver++;
*saver = (uint8_t)((noise_clock >> 16) & 0xFF); saver++; *saver = (uint8_t)((noise_clock >> 24) & 0xFF); saver++;
*saver = (uint8_t)(noise & 0xFF); saver++; *saver = (uint8_t)((noise >> 8) & 0xFF); saver++;
*saver = (uint8_t)((noise >> 16) & 0xFF); saver++; *saver = (uint8_t)((noise >> 24) & 0xFF); saver++;
*saver = (uint8_t)(old_sample_L & 0xFF); saver++; *saver = (uint8_t)((old_sample_L >> 8) & 0xFF); saver++;
*saver = (uint8_t)((old_sample_L >> 16) & 0xFF); saver++; *saver = (uint8_t)((old_sample_L >> 24) & 0xFF); saver++;
*saver = (uint8_t)(old_sample_R & 0xFF); saver++; *saver = (uint8_t)((old_sample_R >> 8) & 0xFF); saver++;
*saver = (uint8_t)((old_sample_R >> 16) & 0xFF); saver++; *saver = (uint8_t)((old_sample_R >> 24) & 0xFF); saver++;
return saver;
}
uint8_t* LoadState(uint8_t* loader)
{
vol_tone = *loader == 1; loader++;
noise_type = *loader == 1; loader++;
noise_bit = *loader == 1; loader++;
A_L = *loader == 1; loader++;
B_L = *loader == 1; loader++;
C_L = *loader == 1; loader++;
noise_L = *loader == 1; loader++;
A_R = *loader == 1; loader++;
B_R = *loader == 1; loader++;
C_R = *loader == 1; loader++;
noise_R = *loader == 1; loader++;
A_up = *loader == 1; loader++;
B_up = *loader == 1; loader++;
C_up = *loader == 1; loader++;
Chan_vol[0] = *loader; loader++;
Chan_vol[1] = *loader; loader++;
Chan_vol[2] = *loader; loader++;
Chan_vol[3] = *loader; loader++;
stereo_panning = *loader; loader++;
chan_sel = *loader; loader++;
noise_rate = *loader; loader++;
Chan_tone[0] = *loader; loader++; Chan_tone[0] |= (*loader << 8); loader++;
Chan_tone[1] = *loader; loader++; Chan_tone[1] |= (*loader << 8); loader++;
Chan_tone[2] = *loader; loader++; Chan_tone[2] |= (*loader << 8); loader++;
Chan_tone[3] = *loader; loader++; Chan_tone[3] |= (*loader << 8); loader++;
psg_clock = *loader; loader++; psg_clock |= (*loader << 8); loader++;
psg_clock |= (*loader << 16); loader++; psg_clock |= (*loader << 24); loader++;
clock_A = *loader; loader++; clock_A |= (*loader << 8); loader++;
clock_A |= (*loader << 16); loader++; clock_A |= (*loader << 24); loader++;
clock_B = *loader; loader++; clock_B |= (*loader << 8); loader++;
clock_B |= (*loader << 16); loader++; clock_B |= (*loader << 24); loader++;
clock_C = *loader; loader++; clock_C |= (*loader << 8); loader++;
clock_C |= (*loader << 16); loader++; clock_C |= (*loader << 24); loader++;
noise_clock = *loader; loader++; noise_clock |= (*loader << 8); loader++;
noise_clock |= (*loader << 16); loader++; noise_clock |= (*loader << 24); loader++;
noise = *loader; loader++; noise |= (*loader << 8); loader++;
noise |= (*loader << 16); loader++; noise |= (*loader << 24); loader++;
old_sample_L = *loader; loader++; old_sample_L |= (*loader << 8); loader++;
old_sample_L |= (*loader << 16); loader++; old_sample_L |= (*loader << 24); loader++;
old_sample_R = *loader; loader++; old_sample_R |= (*loader << 8); loader++;
old_sample_R |= (*loader << 16); loader++; old_sample_R |= (*loader << 24); loader++;
return loader;
}
#pragma endregion
};
}

View File

@ -45,11 +45,35 @@ namespace MSXHawk
uint32_t TmsSpriteAttributeBase;
uint32_t FrameBuffer[192 * 256] = {};
uint8_t ScanlinePriorityBuffer[256] = {};
uint8_t SpriteCollisionBuffer[256] = {};
// constants after load, not stated
uint32_t BackgroundColor = 0;
uint32_t IPeriod = 228;
// temporary variables not stated if on frame boundary
bool is_top;
uint32_t yc;
uint32_t yofs;
uint32_t FrameBufferOffset;
uint32_t PatternNameOffset;
uint32_t ScreenBGColor;
uint32_t yrow;
uint32_t PatternGeneratorOffset;
uint32_t ColorOffset;
uint32_t pn;
uint32_t pv;
uint32_t colorEntry;
uint32_t fgIndex;
uint32_t bgIndex;
uint32_t fgColor;
uint32_t bgColor;
uint32_t lColorIndex;
uint32_t rColorIndex;
uint32_t lColor;
uint32_t rColor;
uint32_t PaletteTMS9918[16] =
{
0xFF000000,
@ -105,8 +129,7 @@ namespace MSXHawk
case 0x40: // write VRAM
break;
case 0x80: // VDP register write
uint32_t reg = value & 0x0F;
WriteRegister(reg, VdpLatch);
WriteRegister(value & 0x0F, VdpLatch);
break;
}
}
@ -218,21 +241,21 @@ namespace MSXHawk
return;
}
uint32_t yc = scanLine / 8;
uint32_t yofs = scanLine % 8;
uint32_t FrameBufferOffset = scanLine * 256;
uint32_t PatternNameOffset = TmsPatternNameTableBase + (yc * 32);
uint32_t ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
yc = scanLine / 8;
yofs = scanLine % 8;
FrameBufferOffset = scanLine * 256;
PatternNameOffset = TmsPatternNameTableBase + (yc * 32);
ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (uint32_t xc = 0; xc < 32; xc++)
{
uint32_t pn = VRAM[PatternNameOffset++];
uint32_t pv = VRAM[PatternGeneratorBase + (pn * 8) + yofs];
uint32_t colorEntry = VRAM[ColorTableBase + (pn / 8)];
uint32_t fgIndex = (colorEntry >> 4) & 0x0F;
uint32_t bgIndex = colorEntry & 0x0F;
uint32_t fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
uint32_t bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex];
pn = VRAM[PatternNameOffset++];
pv = VRAM[PatternGeneratorBase + (pn * 8) + yofs];
colorEntry = VRAM[ColorTableBase + (pn / 8)];
fgIndex = (colorEntry >> 4) & 0x0F;
bgIndex = colorEntry & 0x0F;
fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex];
FrameBuffer[FrameBufferOffset++] = ((pv & 0x80) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x40) > 0) ? fgColor : bgColor;
@ -253,21 +276,21 @@ namespace MSXHawk
return;
}
uint32_t yc = scanLine / 8;
uint32_t yofs = scanLine % 8;
uint32_t FrameBufferOffset = scanLine * 256;
uint32_t PatternNameOffset = TmsPatternNameTableBase + (yc * 40);
uint32_t ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
yc = scanLine / 8;
yofs = scanLine % 8;
FrameBufferOffset = scanLine * 256;
PatternNameOffset = TmsPatternNameTableBase + (yc * 40);
ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (uint32_t xc = 0; xc < 40; xc++)
{
uint32_t pn = VRAM[PatternNameOffset++];
uint32_t pv = VRAM[PatternGeneratorBase + (pn * 8) + yofs];
uint32_t colorEntry = Registers[7];
uint32_t fgIndex = (colorEntry >> 4) & 0x0F;
uint32_t bgIndex = colorEntry & 0x0F;
uint32_t fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
uint32_t bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex];
pn = VRAM[PatternNameOffset++];
pv = VRAM[PatternGeneratorBase + (pn * 8) + yofs];
colorEntry = Registers[7];
fgIndex = (colorEntry >> 4) & 0x0F;
bgIndex = colorEntry & 0x0F;
fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex];
FrameBuffer[FrameBufferOffset++] = ((pv & 0x80) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x40) > 0) ? fgColor : bgColor;
@ -286,23 +309,23 @@ namespace MSXHawk
return;
}
uint32_t yrow = scanLine / 8;
uint32_t yofs = scanLine % 8;
uint32_t FrameBufferOffset = scanLine * 256;
uint32_t PatternNameOffset = TmsPatternNameTableBase + (yrow * 32);
uint32_t PatternGeneratorOffset = (((Registers[4] & 4) << 11) & 0x2000);
uint32_t ColorOffset = (ColorTableBase & 0x2000);
uint32_t ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
yrow = scanLine / 8;
yofs = scanLine % 8;
FrameBufferOffset = scanLine * 256;
PatternNameOffset = TmsPatternNameTableBase + (yrow * 32);
PatternGeneratorOffset = (((Registers[4] & 4) << 11) & 0x2000);
ColorOffset = (ColorTableBase & 0x2000);
ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (uint32_t xc = 0; xc < 32; xc++)
{
uint32_t pn = VRAM[PatternNameOffset++] + ((yrow / 8) * 0x100);
uint32_t pv = VRAM[PatternGeneratorOffset + (pn * 8) + yofs];
uint32_t colorEntry = VRAM[ColorOffset + (pn * 8) + yofs];
uint32_t fgIndex = (colorEntry >> 4) & 0x0F;
uint32_t bgIndex = colorEntry & 0x0F;
uint32_t fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
uint32_t bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex];
pn = VRAM[PatternNameOffset++] + ((yrow / 8) * 0x100);
pv = VRAM[PatternGeneratorOffset + (pn * 8) + yofs];
colorEntry = VRAM[ColorOffset + (pn * 8) + yofs];
fgIndex = (colorEntry >> 4) & 0x0F;
bgIndex = colorEntry & 0x0F;
fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex];
FrameBuffer[FrameBufferOffset++] = ((pv & 0x80) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x40) > 0) ? fgColor : bgColor;
@ -323,21 +346,21 @@ namespace MSXHawk
return;
}
uint32_t yc = scanLine / 8;
bool top = (scanLine & 4) == 0; // am I in the top 4 pixels of an 8-pixel character?
uint32_t FrameBufferOffset = scanLine * 256;
uint32_t PatternNameOffset = TmsPatternNameTableBase + (yc * 32);
uint32_t ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
yc = scanLine / 8;
is_top = (scanLine & 4) == 0; // am I in the top 4 pixels of an 8-pixel character?
FrameBufferOffset = scanLine * 256;
PatternNameOffset = TmsPatternNameTableBase + (yc * 32);
ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (uint32_t xc = 0; xc < 32; xc++)
{
uint32_t pn = VRAM[PatternNameOffset++];
uint32_t pv = VRAM[PatternGeneratorBase + (pn * 8) + ((yc & 3) * 2) + (top ? 0 : 1)];
pn = VRAM[PatternNameOffset++];
pv = VRAM[PatternGeneratorBase + (pn * 8) + ((yc & 3) * 2) + (is_top ? 0 : 1)];
uint32_t lColorIndex = pv & 0xF;
uint32_t rColorIndex = pv >> 4;
uint32_t lColor = lColorIndex == 0 ? ScreenBGColor : PaletteTMS9918[lColorIndex];
uint32_t rColor = rColorIndex == 0 ? ScreenBGColor : PaletteTMS9918[rColorIndex];
lColorIndex = pv & 0xF;
rColorIndex = pv >> 4;
lColor = lColorIndex == 0 ? ScreenBGColor : PaletteTMS9918[lColorIndex];
rColor = rColorIndex == 0 ? ScreenBGColor : PaletteTMS9918[rColorIndex];
FrameBuffer[FrameBufferOffset++] = lColor;
FrameBuffer[FrameBufferOffset++] = lColor;
@ -350,9 +373,6 @@ namespace MSXHawk
}
}
uint8_t ScanlinePriorityBuffer[256] = {};
uint8_t SpriteCollisionBuffer[256] = {};
void RenderTmsSprites(int32_t scanLine)
{
if (EnableDoubledSprites() == false)