CPCHawk: PSG now sounds *almost* right

This commit is contained in:
Asnivor 2018-07-23 20:48:31 +01:00
parent 1268b09849
commit a7e0e728a7
13 changed files with 287 additions and 99 deletions

View File

@ -6,7 +6,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <summary>
/// Represents a PSG device (in this case an AY-3-891x)
/// </summary>
public interface IPSG : ISoundProvider, IPortIODevice
public interface IPSG : ISoundProvider
{
/// <summary>
/// Initlization routine
@ -17,6 +17,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
void SetFunction(int data);
//void ClockCycle();
/// <summary>
/// Activates a register
/// </summary>

View File

@ -71,6 +71,54 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
#endregion
public void ReadStatus(ref int data)
{
// read main status register
// this can happen at any time
data = ReadMainStatus();
if (writeDebug)
{
//outputString += data + ",,," + ActiveCommand.CommandCode + "\r\n";
workingArr[0] = data.ToString();
BuildCSVLine();
//System.IO.File.WriteAllText(outputfile, outputString);
}
}
public void ReadData(ref int data)
{
// Z80 is trying to read from the data register
data = ReadDataRegister();
if (writeDebug)
{
workingArr[2] = data.ToString();
//outputString += ",," + data + "," + ActiveCommand.CommandCode + "\r\n";
BuildCSVLine();
}
}
public void WriteData(int data)
{
// Z80 is attempting to write to the data register
WriteDataRegister((byte)data);
if (writeDebug)
{
//outputString += "," + data + ",," + ActiveCommand.CommandCode + "\r\n";
workingArr[1] = data.ToString();
BuildCSVLine();
//System.IO.File.WriteAllText(outputfile, outputString);
}
}
public void Motor(int data)
{
// set disk motor on/off
if (data > 0)
FDD_FLAG_MOTOR = true;
else
FDD_FLAG_MOTOR = false;
}
/// <summary>
/// Device responds to an IN instruction
/// </summary>

View File

@ -608,31 +608,31 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
if (CRCT.VSYNC && CRCT.HSYNC)
{
// both hsync and vsync active
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.HSYNCandVSYNC, VideoByte1, VideoByte2, ColourRegisters);
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.HSYNCandVSYNC, VideoByte1, VideoByte2, ColourRegisters.ToArray());
//CRT.AddScanlineCharacter(VLC, HCC++, RenderPhase.HSYNCandVSYNC, VideoByte1, VideoByte2, ColourRegisters);
}
else if (CRCT.VSYNC)
{
// vsync is active but hsync is not
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.VSYNC, VideoByte1, VideoByte2, ColourRegisters);
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.VSYNC, VideoByte1, VideoByte2, ColourRegisters.ToArray());
//CRT.AddScanlineCharacter(VLC, HCC++, RenderPhase.VSYNC, VideoByte1, VideoByte2, ColourRegisters);
}
else if (CRCT.HSYNC)
{
// hsync is active but vsync is not
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.HSYNC, VideoByte1, VideoByte2, ColourRegisters);
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.HSYNC, VideoByte1, VideoByte2, ColourRegisters.ToArray());
//CRT.AddScanlineCharacter(VLC, HCC++, RenderPhase.HSYNC, VideoByte1, VideoByte2, ColourRegisters);
}
else if (!CRCT.DISPTMG)
{
// border generation
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.BORDER, VideoByte1, VideoByte2, ColourRegisters);
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.BORDER, VideoByte1, VideoByte2, ColourRegisters.ToArray());
//CRT.AddScanlineCharacter(VLC, HCC++, RenderPhase.BORDER, VideoByte1, VideoByte2, ColourRegisters);
}
else if (CRCT.DISPTMG)
{
// pixels generated from video RAM
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.DISPLAY, VideoByte1, VideoByte2, ColourRegisters);
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.DISPLAY, VideoByte1, VideoByte2, ColourRegisters.ToArray());
}
}
@ -656,7 +656,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
case 0:
CRCT.ClockCycle();
//PSG.ClockCycle();
//PSG.ClockCycle(FrameClock);
WaitLine = false;
break;
case 1:

View File

@ -197,6 +197,19 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
}
}
/// <summary>
/// The offset into vRAM
/// </summary>
public int VideoRAMOffset
{
get
{
ushort combined = (ushort)(Regs[12] << 8 | Regs[13]);
int offset = combined & 0x3ff;
return offset;
}
}
/* Easier memory functions */
@ -210,6 +223,21 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// </summary>
public int ByteCounter;
/// <summary>
/// Set at every HSYNC
/// </summary>
public int LatchedRAMOffset;
/// <summary>
/// set at every HSYNC
/// </summary>
public int LatchedRAMStartAddress;
/// <summary>
/// set at every HSYNC
/// </summary>
public int LatchedScreenWidthBytes;
#endregion
#region Internal Registers and State
@ -383,12 +411,12 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <summary>
/// Vertical Character Count
/// </summary>
private int VCC;
public int VCC;
/// <summary>
/// Vertical Scanline Count
/// </summary>
private int VLC;
public int VLC;
/// <summary>
/// Internal cycle counter
@ -445,6 +473,11 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
}
}
if (HSYNC && HSYNCCounter == 1)
{
}
// move one horizontal character
HCC++;
@ -463,8 +496,15 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
var line = VCC;
var row = VLC;
var addr = VideoPageBase + (line * 0x50) + (row * 0x800) + (ByteCounter);
CurrentByteAddress = (ushort)addr;
var addrX = (LatchedRAMOffset * 2) + ((VCC * LatchedScreenWidthBytes) & 0x7ff) + ByteCounter;
// remove artifacts caused by certain hardware scrolling addresses
addrX &= 0x7ff;
var addrY = LatchedRAMStartAddress + (2048 * VLC);
//var addr = VideoPageBase + (line * (0x50)) + (row * 0x800) + (ByteCounter);
CurrentByteAddress = (ushort)(addrX + addrY);
ByteCounter += 2;
}
@ -558,6 +598,11 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
HSYNC = true;
HSYNCCounter = 0;
LatchedRAMStartAddress = VideoPageBase;
LatchedRAMOffset = VideoRAMOffset;
LatchedScreenWidthBytes = DisplayWidth * 2;
}
}
}

View File

@ -332,7 +332,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
switch (ScreenMode)
{
case 0:
Characters[charIndex].Pixels = new int[8];
Characters[charIndex].Pixels = new int[4];
break;
case 1:
Characters[charIndex].Pixels = new int[8];
@ -527,7 +527,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
}
// render out the scanline
int pCount = (LineIndex - CRT.TopLinesToTrim) * 2 * CRT.BufferWidth;
int pCount = (LineIndex - CRT.TopLinesToTrim) * vScale * CRT.BufferWidth;
// vScale
for (int s = 0; s < vScale; s++)

View File

@ -24,10 +24,12 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
private int _tStatesPerFrame;
private int _sampleRate;
private int _samplesPerFrame;
private int _tStatesPerSample;
private double _tStatesPerSample;
private short[] _audioBuffer;
private int _audioBufferIndex;
private int _lastStateRendered;
private int _clockCyclesPerFrame;
private int _cyclesPerSample;
#endregion
@ -39,6 +41,11 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
public AY38912(CPCBase machine)
{
_machine = machine;
//_blipL.SetRates(1000000, 44100);
//_blipL.SetRates((_machine.GateArray.FrameLength * 50) / 4, 44100);
//_blipR.SetRates(1000000, 44100);
//_blipR.SetRates((_machine.GateArray.FrameLength * 50) / 4, 44100);
}
/// <summary>
@ -51,46 +58,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
Reset();
}
#endregion
#region IPortIODevice
public bool ReadPort(ushort port, ref int value)
{
if (port != 0xfffd)
{
// port read is not addressing this device
return false;
}
value = PortRead();
return true;
}
public bool WritePort(ushort port, int value)
{
if (port == 0xfffd)
{
// register select
SelectedRegister = value & 0x0f;
return true;
}
else if (port == 0xbffd)
{
// Update the audiobuffer based on the current CPU cycle
// (this process the previous data BEFORE writing to the currently selected register)
int d = (int)(_machine.CurrentFrameCycle);
BufferUpdate(d);
// write to register
PortWrite(value);
return true;
}
return false;
}
#endregion
#endregion
#region AY Implementation
@ -239,7 +207,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
if (_activeRegister < 16)
return _registers[_activeRegister];
}
return 0;
}
@ -366,11 +334,13 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
break;
}
// do audio processing
BufferUpdate((int)_machine.CurrentFrameCycle);
break;
}
}
/// <summary>
@ -396,7 +366,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <param name="frameCycle"></param>
public void UpdateSound(int frameCycle)
{
BufferUpdate(frameCycle);
BufferUpdate(frameCycle);
}
#endregion
@ -471,7 +441,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <summary>
/// The frequency of the AY chip
/// </summary>
private static int _chipFrequency = 1773400;
private static int _chipFrequency = 1000000; // 1773400;
/// <summary>
/// The rendering resolution of the chip
@ -640,8 +610,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
}
}
private int mult_const;
/// <summary>
/// Initializes timing information for the frame
/// </summary>
@ -651,21 +619,19 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
_sampleRate = sampleRate;
_tStatesPerFrame = frameTactCount;
_samplesPerFrame = 882;
_samplesPerFrame = sampleRate / 50; //882
_tStatesPerSample = 79; //(int)Math.Round(((double)_tStatesPerFrame * 50D) /
//(16D * (double)_sampleRate),
//MidpointRounding.AwayFromZero);
//_samplesPerFrame = _tStatesPerFrame / _tStatesPerSample;
_audioBuffer = new short[_samplesPerFrame * 2]; //[_sampleRate / 50];
_tStatesPerSample = (double)frameTactCount / (double)_samplesPerFrame; // 90; //(int)Math.Round(((double)_tStatesPerFrame * 50D) /
//(16D * (double)_sampleRate),
//MidpointRounding.AwayFromZero);
_audioBuffer = new short[_samplesPerFrame * 2];
_audioBufferIndex = 0;
mult_const = ((_chipFrequency / 8) << 14) / _machine.GateArray.Z80ClockSpeed;
var aytickspercputick = (double)_machine.GateArray.Z80ClockSpeed / (double)_chipFrequency;
int ayCyclesPerSample = (int)((double)_tStatesPerSample * (double)aytickspercputick);
ticksPerSample = ((double)_chipFrequency / sampleRate / 8);
}
private double ticksPerSample;
private double tickCounter = 0;
/// <summary>
/// Updates the audiobuffer based on the current frame t-state
@ -682,14 +648,18 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
// get the current length of the audiobuffer
int bufferLength = _samplesPerFrame; // _audioBuffer.Length;
int toEnd = ((bufferLength * cycle) / _tStatesPerFrame);
double toEnd = ((double)(bufferLength * cycle) / (double)_tStatesPerFrame);
// loop through the number of samples we need to render
while (_audioBufferIndex < toEnd)
{
// run the AY chip processing at the correct resolution
for (int i = 0; i < _tStatesPerSample / 14; i++)
tickCounter += ticksPerSample;
while (tickCounter > 0)
{
tickCounter--;
if (++_countA >= _dividerA)
{
_countA = 0;
@ -795,6 +765,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
public void DiscardSamples()
{
_audioBuffer = new short[_samplesPerFrame * 2];
//_blipL.Clear();
//_blipR.Clear();
}
public void GetSamplesSync(out short[] samples, out int nsamp)
@ -802,6 +774,43 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
nsamp = _samplesPerFrame;
samples = _audioBuffer;
DiscardSamples();
tickCounter = 0;
return;
/*
_blipL.EndFrame((uint)SampleClock);
_blipR.EndFrame((uint)SampleClock);
SampleClock = 0;
int sampL = _blipL.SamplesAvailable();
int sampR = _blipR.SamplesAvailable();
if (sampL > sampR)
nsamp = sampL;
else
nsamp = sampR;
short[] buffL = new short[sampL];
short[] buffR = new short[sampR];
_blipL.ReadSamples(buffL, sampL - 1, false);
_blipR.ReadSamples(buffR, sampR - 1, false);
if (_audioBuffer.Length != nsamp * 2)
_audioBuffer = new short[nsamp * 2];
int p = 0;
for (int i = 0; i < nsamp; i++)
{
if (i < sampL)
_audioBuffer[p++] = buffL[i];
if (i < sampR)
_audioBuffer[p++] = buffR[i];
}
//nsamp = _samplesPerFrame;
samples = _audioBuffer;
DiscardSamples();
*/
}
#endregion
@ -823,7 +832,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame);
ser.Sync("_sampleRate", ref _sampleRate);
ser.Sync("_samplesPerFrame", ref _samplesPerFrame);
ser.Sync("_tStatesPerSample", ref _tStatesPerSample);
//ser.Sync("_tStatesPerSample", ref _tStatesPerSample);
ser.Sync("_audioBufferIndex", ref _audioBufferIndex);
ser.Sync("_audioBuffer", ref _audioBuffer, false);
ser.Sync("PortAInput", ref PortAInput);

View File

@ -73,7 +73,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <summary>
/// Device blipbuffer
/// </summary>
private readonly BlipBuffer blip = new BlipBuffer(1024);
private readonly BlipBuffer blip = new BlipBuffer(883);
#endregion
@ -122,7 +122,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// </summary>
public void Init(int sampleRate, int tStatesPerFrame)
{
blip.SetRates((tStatesPerFrame * 50), sampleRate);
blip.SetRates((4000000), sampleRate);
_sampleRate = sampleRate;
_tStatesPerFrame = tStatesPerFrame;
}

View File

@ -49,7 +49,20 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
}
else if (DecodeINPort(port) == PortDevice.Expansion)
{
if (!port.Bit(7))
{
// FDC
if (port.Bit(8) && !port.Bit(0))
{
// FDC status register
UPDDiskDevice.ReadStatus(ref result);
}
if (port.Bit(8) && port.Bit(0))
{
// FDC data register
UPDDiskDevice.ReadData(ref result);
}
}
}
return (byte)result;
@ -104,7 +117,20 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
}
else if (d == PortDevice.Expansion)
{
if (!port.Bit(7))
{
// FDC
if (port.Bit(8) && !port.Bit(0) || port.Bit(8) && port.Bit(0))
{
// FDC data register
UPDDiskDevice.WriteData(value);
}
if ((!port.Bit(8) && !port.Bit(0)) || (!port.Bit(8) && port.Bit(0)))
{
// FDC motor
UPDDiskDevice.Motor(value);
}
}
}
}

View File

@ -157,19 +157,13 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// </summary>
protected void LoadDiskMedia()
{
/*
if (this.GetType() != typeof(ZX128Plus3))
if (this.GetType() == typeof(CPC464))
{
Spectrum.CoreComm.ShowMessage("You are trying to load one of more disk images.\n\n Please select ZX Spectrum +3 emulation immediately and reboot the core");
CPC.CoreComm.ShowMessage("You are trying to load one of more disk images.\n\n Please select something other than CPC 464 emulation immediately and reboot the core");
return;
}
else
{
//Spectrum.CoreComm.ShowMessage("You are attempting to load a disk into the +3 disk drive.\n\nThis DOES NOT currently work properly but IS under active development.");
}
UPDDiskDevice.FDD_LoadDisk(diskImages[diskMediaIndex]);
*/
}
/// <summary>
@ -194,7 +188,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
// cdt tape file
return CPCMediaType.Tape;
}
// not found
return CPCMediaType.None;

View File

@ -63,9 +63,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// </summary>
public int RAM64KBank;
#endregion
#region Memory Related Methods

View File

@ -152,8 +152,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
if (_renderSound)
{
if (AYDevice != null)
AYDevice.StartFrame();
AYDevice.StartFrame();
}
PollInput();

View File

@ -58,6 +58,55 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
#endregion
/// <summary>
/// CDT format is essentially exactly the same as the TZX format
/// However all timings are based on spectrum timings (3.5Mhz)
/// so need to be adjusted for the CPC (4Mhz)
/// </summary>
/// <param name="db"></param>
/// <returns></returns>
private TapeDataBlock ConvertClock(TapeDataBlock db)
{
TapeDataBlock tb = new TapeDataBlock();
tb.BlockDescription = db.BlockDescription;
tb.BlockID = db.BlockID;
tb.Command = db.Command;
tb.DataPeriods = new List<int>();
tb.InitialPulseLevel = db.InitialPulseLevel;
tb.MetaData = db.MetaData;
tb.PauseInMS = db.PauseInMS;
double multiplier = (double)4 / (double)3.5;
double cycleScale = ((40 << 16) / 35);
double origPeriods = db.DataPeriods.Count();
for (int i = 0; i < origPeriods; i++)
{
int orig = db.DataPeriods[i];
int np = (int)((double)orig * multiplier);
int nnp = ClockAdjust(orig);
tb.DataPeriods.Add(np);
}
return tb;
}
private int ClockAdjust(int val)
{
int cycleScale = ((40 << 16) / 35);
int res = (val * cycleScale) >> 16;
return res;
}
private int Scale => ((40 << 16) / 35);
private int Adjust(int val)
{
return (int)((val * CLOCK_MULTIPLIER));
}
private const double CLOCK_MULTIPLIER = 1.142857;
/// <summary>
/// Returns TRUE if tzx header is detected
/// </summary>
@ -142,6 +191,17 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
ProcessBlock(data, ID);
}
/*
// convert for Amstrad CPC
List<TapeDataBlock> newBlocks = new List<TapeDataBlock>();
for (int i = 0; i < _datacorder.DataBlocks.Count(); i++)
{
newBlocks.Add(ConvertClock(_datacorder.DataBlocks[i]));
}
_datacorder.DataBlocks.Clear();
_datacorder.DataBlocks.AddRange(newBlocks);
*/
}
/// <summary>
@ -1632,6 +1692,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
int bitsInLastByte = 8
)
{
// first get the block description
string description = string.Empty;
@ -1857,12 +1918,16 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
)
{
int pilotCount = 3220;
/*
// pilot count needs to be ascertained from flag byte
int pilotCount;
if (blockData[0] < 128)
pilotCount = 8063;
else
pilotCount = 3223;
*/
// now we can decode
var nBlock = DecodeDataBlock

View File

@ -184,8 +184,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
}
else
{
// just process what we have as-is
}
// just process what we have as-is
}
}
// mix the soundproviders together
@ -197,10 +197,14 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
short sectorVal = 0;
foreach (var sp in SoundProviders)
{
if (sp.Buffer[i] > sp.MaxVolume)
sectorVal += (short)sp.MaxVolume;
else
sectorVal += sp.Buffer[i];
if (i < sp.Buffer.Length)
{
if (sp.Buffer[i] > sp.MaxVolume)
sectorVal += (short)sp.MaxVolume;
else
sectorVal += sp.Buffer[i];
}
}
samples[i] = sectorVal;