MSXHawk: audio
This commit is contained in:
parent
bfde89b9f3
commit
263fdfebc3
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue