ZXHawk: Bit of a tidy & readme update

This commit is contained in:
Asnivor 2018-06-13 15:17:19 +01:00
parent ac95d9eb72
commit 2bae423df8
10 changed files with 27 additions and 2550 deletions

View File

@ -281,27 +281,20 @@
<Compile Include="Computers\SinclairSpectrum\Hardware\Input\SinclairJoystick2.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Input\SinclairJoystick1.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Input\NullJoystick.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\SoundOuput\AYChip.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\SoundOuput\Buzzer.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Datacorder\DatacorderDevice.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Abstraction\IKeyboard.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Input\KempstonJoystick.cs" />
<Compile Include="Computers\SinclairSpectrum\Hardware\Abstraction\IBeeperDevice.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ULABase.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128KPlus2a\ZX128Plus2a.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128KPlus2a\ZX128Plus2a.Memory.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128KPlus2a\ZX128Plus2a.Port.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128KPlus2a\ZX128Plus2a.ULA.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128KPlus3\ZX128Plus3.ULA.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128K\ZX128.ULA.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum16K\ZX16.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum48K\ZX48.ULA.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Disk\CPCExtendedFloppyDisk.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Disk\CPCFloppyDisk.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Disk\DiskType.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\MediaConverter.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\MediaConverterType.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\MediaSerializer.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Tape\TapeCommand.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Tape\TapeDataBlock.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Tape\TapConverter.cs" />

View File

@ -1,788 +0,0 @@

using BizHawk.Common;
using BizHawk.Emulation.Common;
using System;
using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/*
/// <summary>
/// AY-3-8912 Emulated Device
///
/// Based heavily on the YM-2149F / AY-3-8910 emulator used in Unreal Speccy
/// (Originally created under Public Domain license by SMT jan.2006)
///
/// https://github.com/mkoloberdin/unrealspeccy/blob/master/sndrender/sndchip.cpp
/// https://github.com/mkoloberdin/unrealspeccy/blob/master/sndrender/sndchip.h
/// </summary>
public class AYChip : IPSG
{
#region Device Fields
/// <summary>
/// The emulated machine (passed in via constructor)
/// </summary>
private SpectrumBase _machine;
private int _tStatesPerFrame;
private int _sampleRate;
private int _samplesPerFrame;
private int _tStatesPerSample;
private short[] _audioBuffer;
private int _audioBufferIndex;
private int _lastStateRendered;
#endregion
#region Construction & Initialization
/// <summary>
/// Main constructor
/// </summary>
public AYChip(SpectrumBase machine)
{
_machine = machine;
}
/// <summary>
/// Initialises the AY chip
/// </summary>
public void Init(int sampleRate, int tStatesPerFrame)
{
InitTiming(sampleRate, tStatesPerFrame);
UpdateVolume();
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
#region AY Implementation
#region Public Properties
/// <summary>
/// AY mixer panning configuration
/// </summary>
[Flags]
public enum AYPanConfig
{
MONO = 0,
ABC = 1,
ACB = 2,
BAC = 3,
BCA = 4,
CAB = 5,
CBA = 6,
}
/// <summary>
/// The AY panning configuration
/// </summary>
public AYPanConfig PanningConfiguration
{
get
{
return _currentPanTab;
}
set
{
if (value != _currentPanTab)
{
_currentPanTab = value;
UpdateVolume();
}
}
}
/// <summary>
/// The AY chip output volume
/// (0 - 100)
/// </summary>
public int Volume
{
get
{
return _volume;
}
set
{
//value = Math.Max(0, value);
//value = Math.Max(100, value);
if (_volume == value)
{
return;
}
_volume = value;
UpdateVolume();
}
}
/// <summary>
/// The currently selected register
/// </summary>
public int SelectedRegister
{
get { return _activeRegister; }
set
{
_activeRegister = (byte)value;
}
}
#endregion
#region Public Methods
/// <summary>
/// Resets the PSG
/// </summary>
public void Reset()
{
}
/// <summary>
/// Reads the value from the currently selected register
/// </summary>
/// <returns></returns>
public int PortRead()
{
return _registers[_activeRegister];
}
/// <summary>
/// Writes to the currently selected register
/// </summary>
/// <param name="value"></param>
public void PortWrite(int value)
{
if (_activeRegister >= 0x10)
return;
byte val = (byte)value;
if (((1 << _activeRegister) & ((1 << 1) | (1 << 3) | (1 << 5) | (1 << 13))) != 0)
val &= 0x0F;
if (((1 << _activeRegister) & ((1 << 6) | (1 << 8) | (1 << 9) | (1 << 10))) != 0)
val &= 0x1F;
if (_activeRegister != 13 && _registers[_activeRegister] == val)
return;
_registers[_activeRegister] = val;
switch (_activeRegister)
{
// Channel A (Combined Pitch)
// (not written to directly)
case 0:
case 1:
_dividerA = _registers[AY_A_FINE] | (_registers[AY_A_COARSE] << 8);
break;
// Channel B (Combined Pitch)
// (not written to directly)
case 2:
case 3:
_dividerB = _registers[AY_B_FINE] | (_registers[AY_B_COARSE] << 8);
break;
// Channel C (Combined Pitch)
// (not written to directly)
case 4:
case 5:
_dividerC = _registers[AY_C_FINE] | (_registers[AY_C_COARSE] << 8);
break;
// Noise Pitch
case 6:
_dividerN = val * 2;
break;
// Mixer
case 7:
_bit0 = 0 - ((val >> 0) & 1);
_bit1 = 0 - ((val >> 1) & 1);
_bit2 = 0 - ((val >> 2) & 1);
_bit3 = 0 - ((val >> 3) & 1);
_bit4 = 0 - ((val >> 4) & 1);
_bit5 = 0 - ((val >> 5) & 1);
break;
// Channel Volumes
case 8:
_eMaskA = (val & 0x10) != 0 ? -1 : 0;
_vA = ((val & 0x0F) * 2 + 1) & ~_eMaskA;
break;
case 9:
_eMaskB = (val & 0x10) != 0 ? -1 : 0;
_vB = ((val & 0x0F) * 2 + 1) & ~_eMaskB;
break;
case 10:
_eMaskC = (val & 0x10) != 0 ? -1 : 0;
_vC = ((val & 0x0F) * 2 + 1) & ~_eMaskC;
break;
// Envelope (Combined Duration)
// (not written to directly)
case 11:
case 12:
_dividerE = _registers[AY_E_FINE] | (_registers[AY_E_COARSE] << 8);
break;
// Envelope Shape
case 13:
// reset the envelope counter
_countE = 0;
if ((_registers[AY_E_SHAPE] & 4) != 0)
{
// attack
_eState = 0;
_eDirection = 1;
}
else
{
// decay
_eState = 31;
_eDirection = -1;
}
break;
case 14:
// IO Port - not implemented
break;
}
}
/// <summary>
/// Start of frame
/// </summary>
public void StartFrame()
{
_audioBufferIndex = 0;
BufferUpdate(0);
}
/// <summary>
/// End of frame
/// </summary>
public void EndFrame()
{
BufferUpdate(_tStatesPerFrame);
}
/// <summary>
/// Updates the audiobuffer based on the current frame t-state
/// </summary>
/// <param name="frameCycle"></param>
public void UpdateSound(int frameCycle)
{
BufferUpdate(frameCycle);
}
#endregion
#region Private Fields
/// <summary>
/// Register indicies
/// </summary>
private const int AY_A_FINE = 0;
private const int AY_A_COARSE = 1;
private const int AY_B_FINE = 2;
private const int AY_B_COARSE = 3;
private const int AY_C_FINE = 4;
private const int AY_C_COARSE = 5;
private const int AY_NOISEPITCH = 6;
private const int AY_MIXER = 7;
private const int AY_A_VOL = 8;
private const int AY_B_VOL = 9;
private const int AY_C_VOL = 10;
private const int AY_E_FINE = 11;
private const int AY_E_COARSE = 12;
private const int AY_E_SHAPE = 13;
private const int AY_PORT_A = 14;
private const int AY_PORT_B = 15;
/// <summary>
/// The register array
/*
The AY-3-8910/8912 contains 16 internal registers as follows:
Register Function Range
0 Channel A fine pitch 8-bit (0-255)
1 Channel A course pitch 4-bit (0-15)
2 Channel B fine pitch 8-bit (0-255)
3 Channel B course pitch 4-bit (0-15)
4 Channel C fine pitch 8-bit (0-255)
5 Channel C course pitch 4-bit (0-15)
6 Noise pitch 5-bit (0-31)
7 Mixer 8-bit (see below)
8 Channel A volume 4-bit (0-15, see below)
9 Channel B volume 4-bit (0-15, see below)
10 Channel C volume 4-bit (0-15, see below)
11 Envelope fine duration 8-bit (0-255)
12 Envelope course duration 8-bit (0-255)
13 Envelope shape 4-bit (0-15)
14 I/O port A 8-bit (0-255)
15 I/O port B 8-bit (0-255) (Not present on the AY-3-8912)
* The volume registers (8, 9 and 10) contain a 4-bit setting but if bit 5 is set then that channel uses the
envelope defined by register 13 and ignores its volume setting.
* The mixer (register 7) is made up of the following bits (low=enabled):
Bit: 7 6 5 4 3 2 1 0
Register: I/O I/O Noise Noise Noise Tone Tone Tone
Channel: B A C B A C B A
The AY-3-8912 ignores bit 7 of this register.
*/
/*
/// </summary>
private int[] _registers = new int[16];
/// <summary>
/// The currently selected register
/// </summary>
private byte _activeRegister;
/// <summary>
/// The frequency of the AY chip
/// </summary>
private static int _chipFrequency = 1773400;
/// <summary>
/// The rendering resolution of the chip
/// </summary>
private double _resolution = 50D * 8D / _chipFrequency;
/// <summary>
/// Channel generator state
/// </summary>
private int _bitA;
private int _bitB;
private int _bitC;
/// <summary>
/// Envelope state
/// </summary>
private int _eState;
/// <summary>
/// Envelope direction
/// </summary>
private int _eDirection;
/// <summary>
/// Noise seed
/// </summary>
private int _noiseSeed;
/// <summary>
/// Mixer state
/// </summary>
private int _bit0;
private int _bit1;
private int _bit2;
private int _bit3;
private int _bit4;
private int _bit5;
/// <summary>
/// Noise generator state
/// </summary>
private int _bitN;
/// <summary>
/// Envelope masks
/// </summary>
private int _eMaskA;
private int _eMaskB;
private int _eMaskC;
/// <summary>
/// Amplitudes
/// </summary>
private int _vA;
private int _vB;
private int _vC;
/// <summary>
/// Channel gen counters
/// </summary>
private int _countA;
private int _countB;
private int _countC;
/// <summary>
/// Envelope gen counter
/// </summary>
private int _countE;
/// <summary>
/// Noise gen counter
/// </summary>
private int _countN;
/// <summary>
/// Channel gen dividers
/// </summary>
private int _dividerA;
private int _dividerB;
private int _dividerC;
/// <summary>
/// Envelope gen divider
/// </summary>
private int _dividerE;
/// <summary>
/// Noise gen divider
/// </summary>
private int _dividerN;
/// <summary>
/// Panning table list
/// </summary>
private static List<uint[]> PanTabs = new List<uint[]>
{
// MONO
new uint[] { 50,50, 50,50, 50,50 },
// ABC
new uint[] { 100,10, 66,66, 10,100 },
// ACB
new uint[] { 100,10, 10,100, 66,66 },
// BAC
new uint[] { 66,66, 100,10, 10,100 },
// BCA
new uint[] { 10,100, 100,10, 66,66 },
// CAB
new uint[] { 66,66, 10,100, 100,10 },
// CBA
new uint[] { 10,100, 66,66, 100,10 }
};
/// <summary>
/// The currently selected panning configuration
/// </summary>
private AYPanConfig _currentPanTab = AYPanConfig.ABC;
/// <summary>
/// The current volume
/// </summary>
private int _volume = 75;
/// <summary>
/// Volume tables state
/// </summary>
private uint[][] _volumeTables;
/// <summary>
/// Volume table to be used
/// </summary>
private static uint[] AYVolumes = new uint[]
{
0x0000,0x0000,0x0340,0x0340,0x04C0,0x04C0,0x06F2,0x06F2,
0x0A44,0x0A44,0x0F13,0x0F13,0x1510,0x1510,0x227E,0x227E,
0x289F,0x289F,0x414E,0x414E,0x5B21,0x5B21,0x7258,0x7258,
0x905E,0x905E,0xB550,0xB550,0xD7A0,0xD7A0,0xFFFF,0xFFFF,
};
#endregion
#region Private Methods
/// <summary>
/// Forces an update of the volume tables
/// </summary>
private void UpdateVolume()
{
int upperFloor = 40000;
var inc = (0xFFFF - upperFloor) / 100;
var vol = inc * _volume; // ((ulong)0xFFFF * (ulong)_volume / 100UL) - 20000 ;
_volumeTables = new uint[6][];
// parent array
for (int j = 0; j < _volumeTables.Length; j++)
{
_volumeTables[j] = new uint[32];
// child array
for (int i = 0; i < _volumeTables[j].Length; i++)
{
_volumeTables[j][i] = (uint)(
(PanTabs[(int)_currentPanTab][j] * AYVolumes[i] * vol) /
(3 * 65535 * 100));
}
}
}
private int mult_const;
/// <summary>
/// Initializes timing information for the frame
/// </summary>
/// <param name="sampleRate"></param>
/// <param name="frameTactCount"></param>
private void InitTiming(int sampleRate, int frameTactCount)
{
_sampleRate = sampleRate;
_tStatesPerFrame = frameTactCount;
_samplesPerFrame = 882;
_tStatesPerSample = 79; //(int)Math.Round(((double)_tStatesPerFrame * 50D) /
//(16D * (double)_sampleRate),
//MidpointRounding.AwayFromZero);
//_samplesPerFrame = _tStatesPerFrame / _tStatesPerSample;
_audioBuffer = new short[_samplesPerFrame * 2]; //[_sampleRate / 50];
_audioBufferIndex = 0;
mult_const = ((_chipFrequency / 8) << 14) / _machine.ULADevice.ClockSpeed;
var aytickspercputick = (double)_machine.ULADevice.ClockSpeed / (double)_chipFrequency;
int ayCyclesPerSample = (int)((double)_tStatesPerSample * (double)aytickspercputick);
}
/// <summary>
/// Updates the audiobuffer based on the current frame t-state
/// </summary>
/// <param name="cycle"></param>
private void BufferUpdate(int cycle)
{
if (cycle > _tStatesPerFrame)
{
// we are outside of the frame - just process the last value
cycle = _tStatesPerFrame;
}
// get the current length of the audiobuffer
int bufferLength = _samplesPerFrame; // _audioBuffer.Length;
int toEnd = ((bufferLength * cycle) / _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++)
{
if (++_countA >= _dividerA)
{
_countA = 0;
_bitA ^= -1;
}
if (++_countB >= _dividerB)
{
_countB = 0;
_bitB ^= -1;
}
if (++_countC >= _dividerC)
{
_countC = 0;
_bitC ^= -1;
}
if (++_countN >= _dividerN)
{
_countN = 0;
_noiseSeed = (_noiseSeed * 2 + 1) ^ (((_noiseSeed >> 16) ^ (_noiseSeed >> 13)) & 1);
_bitN = 0 - ((_noiseSeed >> 16) & 1);
}
if (++_countE >= _dividerE)
{
_countE = 0;
_eState += +_eDirection;
if ((_eState & ~31) != 0)
{
var mask = (1 << _registers[AY_E_SHAPE]);
if ((mask & ((1 << 0) | (1 << 1) | (1 << 2) |
(1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) |
(1 << 7) | (1 << 9) | (1 << 15))) != 0)
{
_eState = _eDirection = 0;
}
else if ((mask & ((1 << 8) | (1 << 12))) != 0)
{
_eState &= 31;
}
else if ((mask & ((1 << 10) | (1 << 14))) != 0)
{
_eDirection = -_eDirection;
_eState += _eDirection;
}
else
{
// 11,13
_eState = 31;
_eDirection = 0;
}
}
}
}
// mix the sample
var mixA = ((_eMaskA & _eState) | _vA) & ((_bitA | _bit0) & (_bitN | _bit3));
var mixB = ((_eMaskB & _eState) | _vB) & ((_bitB | _bit1) & (_bitN | _bit4));
var mixC = ((_eMaskC & _eState) | _vC) & ((_bitC | _bit2) & (_bitN | _bit5));
var l = _volumeTables[0][mixA];
var r = _volumeTables[1][mixA];
l += _volumeTables[2][mixB];
r += _volumeTables[3][mixB];
l += _volumeTables[4][mixC];
r += _volumeTables[5][mixC];
_audioBuffer[_audioBufferIndex * 2] = (short)l;
_audioBuffer[(_audioBufferIndex * 2) + 1] = (short)r;
_audioBufferIndex++;
}
_lastStateRendered = cycle;
}
#endregion
#endregion
#region ISoundProvider
public bool CanProvideAsync => false;
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
throw new InvalidOperationException("Only Sync mode is supported.");
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void DiscardSamples()
{
_audioBuffer = new short[_samplesPerFrame * 2];
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
nsamp = _samplesPerFrame;
samples = _audioBuffer;
DiscardSamples();
}
#endregion
#region State Serialization
public int nullDump = 0;
/// <summary>
/// State serialization
/// </summary>
/// <param name="ser"></param>
public void SyncState(Serializer ser)
{
ser.BeginSection("PSG-AY");
ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame);
ser.Sync("_sampleRate", ref _sampleRate);
ser.Sync("_samplesPerFrame", ref _samplesPerFrame);
ser.Sync("_tStatesPerSample", ref _tStatesPerSample);
ser.Sync("_audioBufferIndex", ref _audioBufferIndex);
ser.Sync("_audioBuffer", ref _audioBuffer, false);
ser.Sync("_registers", ref _registers, false);
ser.Sync("_activeRegister", ref _activeRegister);
ser.Sync("_bitA", ref _bitA);
ser.Sync("_bitB", ref _bitB);
ser.Sync("_bitC", ref _bitC);
ser.Sync("_eState", ref _eState);
ser.Sync("_eDirection", ref _eDirection);
ser.Sync("_noiseSeed", ref _noiseSeed);
ser.Sync("_bit0", ref _bit0);
ser.Sync("_bit1", ref _bit1);
ser.Sync("_bit2", ref _bit2);
ser.Sync("_bit3", ref _bit3);
ser.Sync("_bit4", ref _bit4);
ser.Sync("_bit5", ref _bit5);
ser.Sync("_bitN", ref _bitN);
ser.Sync("_eMaskA", ref _eMaskA);
ser.Sync("_eMaskB", ref _eMaskB);
ser.Sync("_eMaskC", ref _eMaskC);
ser.Sync("_vA", ref _vA);
ser.Sync("_vB", ref _vB);
ser.Sync("_vC", ref _vC);
ser.Sync("_countA", ref _countA);
ser.Sync("_countB", ref _countB);
ser.Sync("_countC", ref _countC);
ser.Sync("_countE", ref _countE);
ser.Sync("_countN", ref _countN);
ser.Sync("_dividerA", ref _dividerA);
ser.Sync("_dividerB", ref _dividerB);
ser.Sync("_dividerC", ref _dividerC);
ser.Sync("_dividerE", ref _dividerE);
ser.Sync("_dividerN", ref _dividerN);
ser.SyncEnum("_currentPanTab", ref _currentPanTab);
ser.Sync("_volume", ref nullDump);
for (int i = 0; i < 6; i++)
{
ser.Sync("volTable" + i, ref _volumeTables[i], false);
}
if (ser.IsReader)
_volume = _machine.Spectrum.Settings.AYVolume;
ser.EndSection();
}
#endregion
}
*/
}

View File

@ -1,796 +0,0 @@

using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/*
/// <summary>
/// ULA (Uncommitted Logic Array) implementation
/// </summary>
public abstract class ULABase : IVideoProvider
{
#region General
/// <summary>
/// Length of the frame in T-States
/// </summary>
public int FrameLength;
/// <summary>
/// Emulated clock speed
/// </summary>
public int ClockSpeed;
/// <summary>
/// Whether machine is late or early timing model
/// </summary>
public bool LateTiming; //currently not implemented
/// <summary>
/// The current cycle within the current frame
/// </summary>
public int CurrentTStateInFrame;
protected SpectrumBase _machine;
#endregion
#region Palettes
/// <summary>
/// The standard ULA palette
/// </summary>
private static readonly int[] ULAPalette =
{
Colors.ARGB(0x00, 0x00, 0x00), // Black
Colors.ARGB(0x00, 0x00, 0xD7), // Blue
Colors.ARGB(0xD7, 0x00, 0x00), // Red
Colors.ARGB(0xD7, 0x00, 0xD7), // Magenta
Colors.ARGB(0x00, 0xD7, 0x00), // Green
Colors.ARGB(0x00, 0xD7, 0xD7), // Cyan
Colors.ARGB(0xD7, 0xD7, 0x00), // Yellow
Colors.ARGB(0xD7, 0xD7, 0xD7), // White
Colors.ARGB(0x00, 0x00, 0x00), // Bright Black
Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue
Colors.ARGB(0xFF, 0x00, 0x00), // Bright Red
Colors.ARGB(0xFF, 0x00, 0xFF), // Bright Magenta
Colors.ARGB(0x00, 0xFF, 0x00), // Bright Green
Colors.ARGB(0x00, 0xFF, 0xFF), // Bright Cyan
Colors.ARGB(0xFF, 0xFF, 0x00), // Bright Yellow
Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White
};
#endregion
#region Contention
/// <summary>
/// T-State at which to start applying contention
/// </summary>
public int contentionStartPeriod;
/// <summary>
/// T-State at which to end applying contention
/// </summary>
public int contentionEndPeriod;
/// <summary>
/// T-State memory contention delay mapping
/// </summary>
public byte[] contentionTable;
/// <summary>
/// Contention offset (used for testing)
/// </summary>
public int contentionOffset = 0; // -5;
#endregion
#region Screen Rendering
/// <summary>
/// Video output buffer
/// </summary>
public int[] ScreenBuffer;
/// <summary>
/// Screen rendering T-State info
/// </summary>
public RenderCycle[] RenderTable;
/// <summary>
/// Display memory
/// </summary>
protected byte[] screen;
/// <summary>
/// Attribute memory lookup (mapped 1:1 to screen for convenience)
/// </summary>
protected short[] attr;
/// <summary>
/// T-State display mapping
/// </summary>
protected short[] tstateToDisp;
/// <summary>
/// Table that stores T-State to screen/attribute address values
/// </summary>
public short[] floatingBusTable;
/// <summary>
/// Cycle at which the last render update took place
/// </summary>
protected long lastTState;
/// <summary>
/// T-States elapsed since last render update
/// </summary>
protected long elapsedTStates;
/// <summary>
/// T-State of top left raster pixel
/// </summary>
protected int actualULAStart;
/// <summary>
/// Offset into display memory based on current T-State
/// </summary>
protected int screenByteCtr;
/// <summary>
/// Offset into current pixel of rasterizer
/// </summary>
protected int ULAByteCtr;
/// <summary>
/// The current border colour
/// </summary>
public int borderColour;
/// <summary>
/// Signs whether the colour flash is ON or OFF
/// </summary>
protected bool flashOn = false;
protected int flashCounter;
public int FlashCounter
{
get { return flashCounter; }
set
{
flashCounter = value;
}
}
/// <summary>
/// Internal frame counter used for flasher operations
/// </summary>
protected int frameCounter = 0;
/// <summary>
/// Last 8-bit bitmap read from display memory
/// (Floating bus implementation)
/// </summary>
protected int lastPixelValue;
/// <summary>
/// Last 8-bit attr val read from attribute memory
/// (Floating bus implementation)
/// </summary>
protected int lastAttrValue;
/// <summary>
/// Last 8-bit bitmap read from display memory+1
/// (Floating bus implementation)
/// </summary>
protected int lastPixelValuePlusOne;
/// <summary>
/// Last 8-bit attr val read from attribute memory+1
/// (Floating bus implementation)
/// </summary>
protected int lastAttrValuePlusOne;
/// <summary>
/// Used to create the non-border display area
/// </summary>
protected int TtateAtLeft;
protected int TstateWidth;
protected int TstateAtTop;
protected int TstateHeight;
protected int TstateAtRight;
protected int TstateAtBottom;
/// <summary>
/// Total T-States in one scanline
/// </summary>
protected int TstatesPerScanline;
/// <summary>
/// Total pixels in one scanline
/// </summary>
protected int ScanLineWidth;
/// <summary>
/// Total chars in one PRINT row
/// </summary>
protected int CharRows;
/// <summary>
/// Total chars in one PRINT column
/// </summary>
protected int CharCols;
/// <summary>
/// Total pixels in one display row
/// </summary>
protected int ScreenWidth;
/// <summary>
/// Total pixels in one display column
/// </summary>
protected int ScreenHeight;
/// <summary>
/// Total pixels in top border
/// </summary>
protected int BorderTopHeight;
/// <summary>
/// Total pixels in bottom border
/// </summary>
protected int BorderBottomHeight;
/// <summary>
/// Total pixels in left border width
/// </summary>
protected int BorderLeftWidth;
/// <summary>
/// Total pixels in right border width
/// </summary>
protected int BorderRightWidth;
/// <summary>
/// Memory address of display start
/// </summary>
protected int DisplayStart;
/// <summary>
/// Total number of bytes of display memory
/// </summary>
protected int DisplayLength;
/// <summary>
/// Memory address of attribute start
/// </summary>
protected int AttributeStart;
/// <summary>
/// Total number of bytes of attribute memory
/// </summary>
protected int AttributeLength;
/// <summary>
/// Raised when ULA has finished painting the entire screen
/// </summary>
public bool needsPaint = false;
#endregion
#region Interrupt
/// <summary>
/// The t-state within the frame that an interrupt is raised
/// </summary>
public int InterruptStart;
/// <summary>
/// The number of T-States that the INT pin is simulated to be held low
/// </summary>
public int InterruptDuration = 32;
/// <summary>
/// Signs that an interrupt has been raised in this frame.
/// </summary>
protected bool InterruptRaised;
/// <summary>
/// Signs that the interrupt signal has been revoked
/// </summary>
protected bool InterruptRevoked;
/// <summary>
/// Resets the interrupt - this should happen every frame in order to raise
/// the VBLANK interrupt in the proceding frame
/// </summary>
public virtual void ResetInterrupt()
{
InterruptRaised = false;
InterruptRevoked = false;
}
/// <summary>
/// Generates an interrupt in the current phase if needed
/// </summary>
/// <param name="currentCycle"></param>
public virtual void CheckForInterrupt(long currentCycle)
{
if (InterruptRevoked)
{
// interrupt has already been handled
return;
}
if (currentCycle < InterruptStart)
{
// interrupt does not need to be raised yet
return;
}
if (currentCycle > InterruptStart + InterruptDuration)
{
// interrupt should have already been raised and the cpu may or
// may not have caught it. The time has passed so revoke the signal
InterruptRevoked = true;
_machine.CPU.FlagI = false;
return;
}
if (InterruptRaised)
{
// INT is raised but not yet revoked
// CPU has NOT handled it yet
return;
}
// Raise the interrupt
InterruptRaised = true;
_machine.CPU.FlagI = true;
// Signal the start of ULA processing
if (_machine._render)
ULAUpdateStart();
CalcFlashCounter();
}
#endregion
#region Construction & Initialisation
public ULABase(SpectrumBase machine)
{
_machine = machine;
borderType = _machine.Spectrum.SyncSettings.BorderType;
}
#endregion
#region Methods
/// <summary>
/// Resets the ULA chip
/// </summary>
public abstract void Reset();
/// <summary>
/// Builds the contention table for the emulated model
/// </summary>
public abstract void BuildContentionTable();
/// <summary>
/// Returns true if the given memory address should be contended
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
public abstract bool IsContended(int addr);
/// <summary>
/// Resets render state once interrupt is generated
/// </summary>
public void ULAUpdateStart()
{
ULAByteCtr = 0;
screenByteCtr = DisplayStart;
lastTState = actualULAStart;
needsPaint = true;
}
/// <summary>
/// Flash processing
/// </summary>
public void CalcFlashCounter()
{
flashCounter++;
if (flashCounter > 15)
{
flashOn = !flashOn;
flashCounter = 0;
}
}
/// <summary>
/// Builds the T-State to attribute map used with the floating bus
/// </summary>
public void BuildAttributeMap()
{
int start = DisplayStart;
for (int f = 0; f < DisplayLength; f++, start++)
{
int addrH = start >> 8; //div by 256
int addrL = start % 256;
int pixelY = (addrH & 0x07);
pixelY |= (addrL & (0xE0)) >> 2;
pixelY |= (addrH & (0x18)) << 3;
int attrIndex_Y = AttributeStart + ((pixelY >> 3) << 5);// pixel/8 * 32
addrL = start % 256;
int pixelX = addrL & (0x1F);
attr[f] = (short)(attrIndex_Y + pixelX);
}
}
/// <summary>
/// Returns the floating bus value
/// </summary>
public virtual void ReadFloatingBus(ref int result)
{
// Floating bus is read on the previous cycle
long _tStates = _machine.CurrentFrameCycle - 1;
// if we are on the top or bottom border return 0xff
if ((_tStates < contentionStartPeriod) || (_tStates > contentionEndPeriod))
{
result = 0xff;
}
else
{
if (floatingBusTable[_tStates] < 0)
{
result = 0xff;
}
else
{
result = _machine.ReadBus((ushort)floatingBusTable[_tStates]);
}
}
}
/// <summary>
/// Updates the screen buffer based on the number of T-States supplied
/// </summary>
/// <param name="_tstates"></param>
public virtual void UpdateScreenBuffer(long _tstates)
{
if (_tstates < actualULAStart)
{
return;
}
else if (_tstates >= FrameLength)
{
_tstates = FrameLength - 1;
needsPaint = true;
}
//the additional 1 tstate is required to get correct number of bytes to output in ircontention.sna
elapsedTStates = (_tstates + 1 - lastTState);
//It takes 4 tstates to write 1 byte. Or, 2 pixels per t-state.
long numBytes = (elapsedTStates >> 2) + ((elapsedTStates % 4) > 0 ? 1 : 0);
int pixelData;
int pixel2Data = 0xff;
int attrData;
int attr2Data;
int bright;
int ink;
int paper;
int flash;
for (int i = 0; i < numBytes; i++)
{
if (tstateToDisp[lastTState] > 1)
{
screenByteCtr = tstateToDisp[lastTState] - 16384; //adjust for actual screen offset
pixelData = _machine.FetchScreenMemory((ushort)screenByteCtr); //screen[screenByteCtr];
attrData = _machine.FetchScreenMemory((ushort)(attr[screenByteCtr] - 16384)); //screen[attr[screenByteCtr] - 16384];
lastPixelValue = pixelData;
lastAttrValue = attrData;
bright = (attrData & 0x40) >> 3;
flash = (attrData & 0x80) >> 7;
ink = (attrData & 0x07);
paper = ((attrData >> 3) & 0x7);
int paletteInk = ULAPalette[ink + bright];
int palettePaper = ULAPalette[paper + bright];
if (flashOn && (flash != 0)) //swap paper and ink when flash is on
{
int temp = paletteInk;
paletteInk = palettePaper;
palettePaper = temp;
}
for (int a = 0; a < 8; ++a)
{
if ((pixelData & 0x80) != 0)
{
ScreenBuffer[ULAByteCtr++] = paletteInk;
lastAttrValue = ink;
//pixelIsPaper = false;
}
else
{
ScreenBuffer[ULAByteCtr++] = palettePaper;
lastAttrValue = paper;
}
pixelData <<= 1;
}
}
else if (tstateToDisp[lastTState] == 1)
{
int bor = ULAPalette[borderColour];
for (int g = 0; g < 8; g++)
ScreenBuffer[ULAByteCtr++] = bor;
}
lastTState += 4;
}
}
#endregion
#region IVideoProvider
private int _virtualWidth;
private int _virtualHeight;
private int _bufferWidth;
private int _bufferHeight;
public int BackgroundColor
{
get { return ULAPalette[7]; } //ULAPalette[borderColour]; }
}
public int VirtualWidth
{
get { return _virtualWidth; }
set { _virtualWidth = value; }
}
public int VirtualHeight
{
get { return _virtualHeight; }
set { _virtualHeight = value; }
}
public int BufferWidth
{
get { return _bufferWidth; }
set { _bufferWidth = value; }
}
public int BufferHeight
{
get { return _bufferHeight; }
set { _bufferHeight = value; }
}
public int VsyncNumerator
{
get { return ClockSpeed * 50; }// ClockSpeed; }
set { }
}
public int VsyncDenominator
{
get { return ClockSpeed; }//FrameLength; }
}
public int[] GetVideoBuffer()
{
switch (borderType)
{
// Full side borders, no top or bottom border (giving *almost* 16:9 output)
case ZXSpectrum.BorderType.Widescreen:
// we are cropping out the top and bottom borders
var startPixelsToCrop = ScanLineWidth * BorderTopHeight;
var endPixelsToCrop = ScanLineWidth * BorderBottomHeight;
int index = 0;
for (int i = startPixelsToCrop; i < ScreenBuffer.Length - endPixelsToCrop; i++)
{
croppedBuffer[index++] = ScreenBuffer[i];
}
return croppedBuffer;
// The full spectrum border
case ZXSpectrum.BorderType.Full:
return ScreenBuffer;
case ZXSpectrum.BorderType.Medium:
// all border sizes now 24
var lR = BorderLeftWidth - 24;
var rR = BorderRightWidth - 24;
var tR = BorderTopHeight - 24;
var bR = BorderBottomHeight - 24;
var startP = ScanLineWidth * tR;
var endP = ScanLineWidth * bR;
int index2 = 0;
// line by line
for (int i = startP; i < ScreenBuffer.Length - endP; i += ScreenWidth + BorderLeftWidth + BorderRightWidth)
{
// each pixel in each line
for (int p = lR; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR; p++)
{
if (index2 == croppedBuffer.Length)
break;
croppedBuffer[index2++] = ScreenBuffer[i + p];
}
}
return croppedBuffer;
case ZXSpectrum.BorderType.Small:
// all border sizes now 24
var lR_ = BorderLeftWidth - 10;
var rR_ = BorderRightWidth - 10;
var tR_ = BorderTopHeight - 10;
var bR_ = BorderBottomHeight - 10;
var startP_ = ScanLineWidth * tR_;
var endP_ = ScanLineWidth * bR_;
int index2_ = 0;
// line by line
for (int i = startP_; i < ScreenBuffer.Length - endP_; i += ScreenWidth + BorderLeftWidth + BorderRightWidth)
{
// each pixel in each line
for (int p = lR_; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR_; p++)
{
if (index2_ == croppedBuffer.Length)
break;
croppedBuffer[index2_++] = ScreenBuffer[i + p];
}
}
return croppedBuffer;
case ZXSpectrum.BorderType.None:
// all border sizes now 24
var lR__ = BorderLeftWidth;
var rR__ = BorderRightWidth;
var tR__ = BorderTopHeight;
var bR__ = BorderBottomHeight;
var startP__ = ScanLineWidth * tR__;
var endP__ = ScanLineWidth * bR__;
int index2__ = 0;
// line by line
for (int i = startP__; i < ScreenBuffer.Length - endP__; i += ScreenWidth + BorderLeftWidth + BorderRightWidth)
{
// each pixel in each line
for (int p = lR__; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR__; p++)
{
if (index2__ == croppedBuffer.Length)
break;
croppedBuffer[index2__++] = ScreenBuffer[i + p];
}
}
return croppedBuffer;
}
return ScreenBuffer;
}
protected void SetupScreenSize()
{
switch (borderType)
{
case ZXSpectrum.BorderType.Full:
BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth;
BufferHeight = ScreenHeight + BorderTopHeight + BorderBottomHeight;
VirtualHeight = BufferHeight;
VirtualWidth = BufferWidth;
ScreenBuffer = new int[BufferWidth * BufferHeight];
break;
case ZXSpectrum.BorderType.Widescreen:
BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth;
BufferHeight = ScreenHeight;
VirtualHeight = BufferHeight;
VirtualWidth = BufferWidth;
croppedBuffer = new int[BufferWidth * BufferHeight];
break;
case ZXSpectrum.BorderType.Medium:
BufferWidth = ScreenWidth + (24) + (24);
BufferHeight = ScreenHeight + (24) + (24);
VirtualHeight = BufferHeight;
VirtualWidth = BufferWidth;
croppedBuffer = new int[BufferWidth * BufferHeight];
break;
case ZXSpectrum.BorderType.Small:
BufferWidth = ScreenWidth + (10) + (10);
BufferHeight = ScreenHeight + (10) + (10);
VirtualHeight = BufferHeight;
VirtualWidth = BufferWidth;
croppedBuffer = new int[BufferWidth * BufferHeight];
break;
case ZXSpectrum.BorderType.None:
BufferWidth = ScreenWidth;
BufferHeight = ScreenHeight;
VirtualHeight = BufferHeight;
VirtualWidth = BufferWidth;
croppedBuffer = new int[BufferWidth * BufferHeight];
break;
}
}
protected int[] croppedBuffer;
private ZXSpectrum.BorderType _borderType;
public ZXSpectrum.BorderType borderType
{
get { return _borderType; }
set { _borderType = value; }
}
#endregion
#region IStatable
public void SyncState(Serializer ser)
{
ser.BeginSection("ULA");
ser.Sync("ScreenBuffer", ref ScreenBuffer, false);
ser.Sync("FrameLength", ref FrameLength);
ser.Sync("ClockSpeed", ref ClockSpeed);
ser.Sync("LateTiming", ref LateTiming);
ser.Sync("borderColour", ref borderColour);
ser.EndSection();
}
#endregion
#region Attribution
/*
* Based on code from ArjunNair's Zero emulator (MIT Licensed)
* https://github.com/ArjunNair/Zero-Emulator
The MIT License (MIT)
Copyright (c) 2009 Arjun Nair
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
#endregion
}
/// <summary>
/// T-State display mapping
/// </summary>
public class RenderCycle
{
public RenderType Type { get; set; }
public short DisplayAddress { get; set; }
public short AttributeAddress { get; set; }
public int ContentionValue { get; set; }
public short FloatingBusAddress { get; set; }
}
public enum RenderType
{
None,
Border,
Display
}
*/
}

View File

@ -1,194 +0,0 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/*
class ULA128 : ULABase
{
#region Construction
public ULA128(SpectrumBase machine)
: base(machine)
{
InterruptStart = 0;
//LongestOperationCycles = 64 + 2;
FrameLength = 70908;
ClockSpeed = 3546900;
contentionTable = new byte[70930];
floatingBusTable = new short[70930];
for (int f = 0; f < 70930; f++)
floatingBusTable[f] = -1;
CharRows = 24;
CharCols = 32;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48;
BorderBottomHeight = 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
DisplayStart = 16384;
DisplayLength = 6144;
AttributeStart = 22528;
AttributeLength = 768;
borderColour = 7;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
TstatesPerScanline = 228;
TstateAtTop = BorderTopHeight * TstatesPerScanline;
TstateAtBottom = BorderBottomHeight * TstatesPerScanline;
tstateToDisp = new short[FrameLength];
ScreenBuffer = new int[ScanLineWidth * BorderTopHeight //48 lines of border
+ ScanLineWidth * ScreenHeight //border + main + border of 192 lines
+ ScanLineWidth * BorderBottomHeight]; //56 lines of border
attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped
SetupScreenSize();
Reset();
}
#endregion
#region Misc Operations
public override void Reset()
{
contentionStartPeriod = 14361; // + LateTiming;
contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline);
screen = _machine.RAM5;
screenByteCtr = DisplayStart;
ULAByteCtr = 0;
actualULAStart = 14366 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming;
lastTState = actualULAStart;
BuildAttributeMap();
BuildContentionTable();
}
#endregion
#region Contention Methods
public override bool IsContended(int addr)
{
addr = addr & 0xc000;
if (addr == 0x4000)
{
// low port contention
return true;
}
if (addr == 0xc000)
{
// high port contention - check for contended bank paged in
switch (_machine.RAMPaged)
{
case 1:
case 3:
case 5:
case 7:
return true;
}
}
return false;
}
public override void BuildContentionTable()
{
int t = contentionStartPeriod;
while (t < contentionEndPeriod)
{
//for 128 t-states
for (int i = 0; i < 128; i += 8)
{
contentionTable[t++] = 6;
contentionTable[t++] = 5;
contentionTable[t++] = 4;
contentionTable[t++] = 3;
contentionTable[t++] = 2;
contentionTable[t++] = 1;
contentionTable[t++] = 0;
contentionTable[t++] = 0;
}
t += (TstatesPerScanline - 128);
}
//build top half of tstateToDisp table
//vertical retrace period
for (t = 0; t < actualULAStart; t++)
tstateToDisp[t] = 0;
//next 48 are actual border
while (t < actualULAStart + (TstateAtTop))
{
for (int g = 0; g < 176; g++)
tstateToDisp[t++] = 1;
for (int g = 176; g < TstatesPerScanline; g++)
tstateToDisp[t++] = 0;
}
//build middle half
int _x = 0;
int _y = 0;
int scrval = 2;
while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline))
{
for (int g = 0; g < 24; g++)
tstateToDisp[t++] = 1;
for (int g = 24; g < 24 + 128; g++)
{
//Map screenaddr to tstate
if (g % 4 == 0)
{
scrval = (((((_y & 0xc0) >> 3) | (_y & 0x07) | (0x40)) << 8)) | (((_x >> 3) & 0x1f) | ((_y & 0x38) << 2));
_x += 8;
}
tstateToDisp[t++] = (short)scrval;
}
_y++;
for (int g = 24 + 128; g < 24 + 128 + 24; g++)
tstateToDisp[t++] = 1;
for (int g = 24 + 128 + 24; g < 24 + 128 + 24 + 52; g++)
tstateToDisp[t++] = 0;
}
int h = contentionStartPeriod + 3;
while (h < contentionEndPeriod + 3)
{
for (int j = 0; j < 128; j += 8)
{
floatingBusTable[h] = tstateToDisp[h + 2];
floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)];
floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4];
floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)];
h += 8;
}
h += TstatesPerScanline - 128;
}
//build bottom half
while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom))
{
for (int g = 0; g < 176; g++)
tstateToDisp[t++] = 1;
for (int g = 176; g < TstatesPerScanline; g++)
tstateToDisp[t++] = 0;
}
}
#endregion
}
*/
}

View File

@ -1,198 +0,0 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/*
class ULAPlus2a : ULABase
{
#region Construction
public ULAPlus2a(SpectrumBase machine)
: base(machine)
{
InterruptStart = 0;
//LongestOperationCycles = 64 + 2;
FrameLength = 70908;
ClockSpeed = 3546900;
contentionTable = new byte[70930];
floatingBusTable = new short[70930];
for (int f = 0; f < 70930; f++)
floatingBusTable[f] = -1;
CharRows = 24;
CharCols = 32;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48;
BorderBottomHeight = 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
DisplayStart = 16384;
DisplayLength = 6144;
AttributeStart = 22528;
AttributeLength = 768;
borderColour = 7;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
TstatesPerScanline = 228;
TstateAtTop = BorderTopHeight * TstatesPerScanline;
TstateAtBottom = BorderBottomHeight * TstatesPerScanline;
tstateToDisp = new short[FrameLength];
ScreenBuffer = new int[ScanLineWidth * BorderTopHeight //48 lines of border
+ ScanLineWidth * ScreenHeight //border + main + border of 192 lines
+ ScanLineWidth * BorderBottomHeight]; //56 lines of border
attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped
SetupScreenSize();
Reset();
}
#endregion
#region Misc Operations
public override void Reset()
{
contentionStartPeriod = 14361; // + LateTiming;
contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline);
screen = _machine.RAM5;
screenByteCtr = DisplayStart;
ULAByteCtr = 0;
actualULAStart = 14365 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming;
lastTState = actualULAStart;
BuildAttributeMap();
BuildContentionTable();
}
#endregion
#region Contention Methods
public override bool IsContended(int addr)
{
addr = addr & 0xc000;
if (addr == 0x4000)
{
// low port contention
return true;
}
if (addr == 0xc000)
{
// high port contention - check for contended bank paged in
switch (_machine.RAMPaged)
{
case 4:
case 5:
case 6:
case 7:
return true;
}
}
return false;
}
public override void BuildContentionTable()
{
int t = contentionStartPeriod;
while (t < contentionEndPeriod)
{
contentionTable[t++] = 1;
contentionTable[t++] = 0;
//for 128 t-states
for (int i = 0; i < 128; i += 8)
{
contentionTable[t++] = 7;
contentionTable[t++] = 6;
contentionTable[t++] = 5;
contentionTable[t++] = 4;
contentionTable[t++] = 3;
contentionTable[t++] = 2;
contentionTable[t++] = 1;
contentionTable[t++] = 0;
}
t += (TstatesPerScanline - 128) - 2;
}
//build top half of tstateToDisp table
//vertical retrace period
for (t = 0; t < actualULAStart; t++)
tstateToDisp[t] = 0;
//next 48 are actual border
while (t < actualULAStart + (TstateAtTop))
{
for (int g = 0; g < 176; g++)
tstateToDisp[t++] = 1;
for (int g = 176; g < TstatesPerScanline; g++)
tstateToDisp[t++] = 0;
}
//build middle half
int _x = 0;
int _y = 0;
int scrval = 2;
while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline))
{
for (int g = 0; g < 24; g++)
tstateToDisp[t++] = 1;
for (int g = 24; g < 24 + 128; g++)
{
//Map screenaddr to tstate
if (g % 4 == 0)
{
scrval = (((((_y & 0xc0) >> 3) | (_y & 0x07) | (0x40)) << 8)) | (((_x >> 3) & 0x1f) | ((_y & 0x38) << 2));
_x += 8;
}
tstateToDisp[t++] = (short)scrval;
}
_y++;
for (int g = 24 + 128; g < 24 + 128 + 24; g++)
tstateToDisp[t++] = 1;
for (int g = 24 + 128 + 24; g < 24 + 128 + 24 + 52; g++)
tstateToDisp[t++] = 0;
}
int h = contentionStartPeriod + 3;
while (h < contentionEndPeriod + 3)
{
for (int j = 0; j < 128; j += 8)
{
floatingBusTable[h] = tstateToDisp[h + 2];
floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)];
floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4];
floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)];
h += 8;
}
h += TstatesPerScanline - 128;
}
//build bottom half
while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom))
{
for (int g = 0; g < 176; g++)
tstateToDisp[t++] = 1;
for (int g = 176; g < TstatesPerScanline; g++)
tstateToDisp[t++] = 0;
}
}
#endregion
}
*/
}

View File

@ -1,198 +0,0 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/*
class ULAPlus3 : ULABase
{
#region Construction
public ULAPlus3(SpectrumBase machine)
: base(machine)
{
InterruptStart = 0;
//LongestOperationCycles = 64 + 2;
FrameLength = 70908;
ClockSpeed = 3546900;
contentionTable = new byte[70930];
floatingBusTable = new short[70930];
for (int f = 0; f < 70930; f++)
floatingBusTable[f] = -1;
CharRows = 24;
CharCols = 32;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48;
BorderBottomHeight = 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
DisplayStart = 16384;
DisplayLength = 6144;
AttributeStart = 22528;
AttributeLength = 768;
borderColour = 7;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
TstatesPerScanline = 228;
TstateAtTop = BorderTopHeight * TstatesPerScanline;
TstateAtBottom = BorderBottomHeight * TstatesPerScanline;
tstateToDisp = new short[FrameLength];
ScreenBuffer = new int[ScanLineWidth * BorderTopHeight //48 lines of border
+ ScanLineWidth * ScreenHeight //border + main + border of 192 lines
+ ScanLineWidth * BorderBottomHeight]; //56 lines of border
attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped
SetupScreenSize();
Reset();
}
#endregion
#region Misc Operations
public override void Reset()
{
contentionStartPeriod = 14361; // + LateTiming;
contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline);
screen = _machine.RAM5;
screenByteCtr = DisplayStart;
ULAByteCtr = 0;
actualULAStart = 14365 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming;
lastTState = actualULAStart;
BuildAttributeMap();
BuildContentionTable();
}
#endregion
#region Contention Methods
public override bool IsContended(int addr)
{
addr = addr & 0xc000;
if (addr == 0x4000)
{
// low port contention
return true;
}
if (addr == 0xc000)
{
// high port contention - check for contended bank paged in
switch (_machine.RAMPaged)
{
case 4:
case 5:
case 6:
case 7:
return true;
}
}
return false;
}
public override void BuildContentionTable()
{
int t = contentionStartPeriod;
while (t < contentionEndPeriod)
{
contentionTable[t++] = 1;
contentionTable[t++] = 0;
//for 128 t-states
for (int i = 0; i < 128; i += 8)
{
contentionTable[t++] = 7;
contentionTable[t++] = 6;
contentionTable[t++] = 5;
contentionTable[t++] = 4;
contentionTable[t++] = 3;
contentionTable[t++] = 2;
contentionTable[t++] = 1;
contentionTable[t++] = 0;
}
t += (TstatesPerScanline - 128) - 2;
}
//build top half of tstateToDisp table
//vertical retrace period
for (t = 0; t < actualULAStart; t++)
tstateToDisp[t] = 0;
//next 48 are actual border
while (t < actualULAStart + (TstateAtTop))
{
for (int g = 0; g < 176; g++)
tstateToDisp[t++] = 1;
for (int g = 176; g < TstatesPerScanline; g++)
tstateToDisp[t++] = 0;
}
//build middle half
int _x = 0;
int _y = 0;
int scrval = 2;
while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline))
{
for (int g = 0; g < 24; g++)
tstateToDisp[t++] = 1;
for (int g = 24; g < 24 + 128; g++)
{
//Map screenaddr to tstate
if (g % 4 == 0)
{
scrval = (((((_y & 0xc0) >> 3) | (_y & 0x07) | (0x40)) << 8)) | (((_x >> 3) & 0x1f) | ((_y & 0x38) << 2));
_x += 8;
}
tstateToDisp[t++] = (short)scrval;
}
_y++;
for (int g = 24 + 128; g < 24 + 128 + 24; g++)
tstateToDisp[t++] = 1;
for (int g = 24 + 128 + 24; g < 24 + 128 + 24 + 52; g++)
tstateToDisp[t++] = 0;
}
int h = contentionStartPeriod + 3;
while (h < contentionEndPeriod + 3)
{
for (int j = 0; j < 128; j += 8)
{
floatingBusTable[h] = tstateToDisp[h + 2];
floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)];
floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4];
floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)];
h += 8;
}
h += TstatesPerScanline - 128;
}
//build bottom half
while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom))
{
for (int g = 0; g < 176; g++)
tstateToDisp[t++] = 1;
for (int g = 176; g < TstatesPerScanline; g++)
tstateToDisp[t++] = 0;
}
}
#endregion
}
*/
}

View File

@ -1,211 +0,0 @@

using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/*
class ULA48 : ULABase
{
#region Construction
public ULA48(SpectrumBase machine)
: base(machine)
{
InterruptStart = 0;// 5; // 0; // 3; // 0;// 32;
//LongestOperationCycles = 32;
FrameLength = 69888;
ClockSpeed = 3500000;
contentionTable = new byte[70930];
floatingBusTable = new short[70930];
for (int f = 0; f < 70930; f++)
floatingBusTable[f] = -1;
CharRows = 24;
CharCols = 32;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48;
BorderBottomHeight = 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
DisplayStart = 16384;
DisplayLength = 6144;
AttributeStart = 22528;
AttributeLength = 768;
borderColour = 7;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
TstatesPerScanline = 224;
TstateAtTop = BorderTopHeight * TstatesPerScanline;
TstateAtBottom = BorderBottomHeight * TstatesPerScanline;
tstateToDisp = new short[FrameLength];
ScreenBuffer = new int[ScanLineWidth * BorderTopHeight //48 lines of border
+ ScanLineWidth * ScreenHeight //border + main + border of 192 lines
+ ScanLineWidth * BorderBottomHeight]; //56 lines of border
attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped
SetupScreenSize();
Reset();
}
#endregion
#region Misc Operations
public override void Reset()
{
contentionOffset = 0;
InterruptStart = 0;
contentionStartPeriod = 14335;// + contentionOffset; // + LateTiming;
contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline);
screen = _machine.RAM0;
screenByteCtr = DisplayStart;
ULAByteCtr = 0;
actualULAStart = 14340 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming;
lastTState = actualULAStart;
BuildAttributeMap();
BuildContentionTable();
}
#endregion
#region Contention Methods
public override bool IsContended(int addr)
{
if ((addr & 49152) == 16384)
return true;
return false;
}
public override void BuildContentionTable()
{
int t = contentionStartPeriod;
while (t < contentionEndPeriod)
{
//for 128 t-states
for (int i = 0; i < 128; i += 8)
{
contentionTable[t++] = 6;
contentionTable[t++] = 5;
contentionTable[t++] = 4;
contentionTable[t++] = 3;
contentionTable[t++] = 2;
contentionTable[t++] = 1;
contentionTable[t++] = 0;
contentionTable[t++] = 0;
}
t += (TstatesPerScanline - 128); //24 tstates of right border + left border + 48 tstates of retrace
}
//build top half of tstateToDisp table
//vertical retrace period
for (t = 0; t < actualULAStart; t++)
tstateToDisp[t] = 0;
//next 48 are actual border
while (t < actualULAStart + (TstateAtTop))
{
//border(24t) + screen (128t) + border(24t) = 176 tstates
for (int g = 0; g < 176; g++)
tstateToDisp[t++] = 1;
//horizontal retrace
for (int g = 176; g < TstatesPerScanline; g++)
tstateToDisp[t++] = 0;
}
//build middle half of display
int _x = 0;
int _y = 0;
int scrval = 2;
while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline))
{
//left border
for (int g = 0; g < 24; g++)
tstateToDisp[t++] = 1;
//screen
for (int g = 24; g < 24 + 128; g++)
{
//Map screenaddr to tstate
if (g % 4 == 0)
{
scrval = (((((_y & 0xc0) >> 3) | (_y & 0x07) | (0x40)) << 8)) | (((_x >> 3) & 0x1f) | ((_y & 0x38) << 2));
_x += 8;
}
tstateToDisp[t++] = (short)scrval;
}
_y++;
//right border
for (int g = 24 + 128; g < 24 + 128 + 24; g++)
tstateToDisp[t++] = 1;
//horizontal retrace
for (int g = 24 + 128 + 24; g < 24 + 128 + 24 + 48; g++)
tstateToDisp[t++] = 0;
}
int h = contentionStartPeriod + 3;
while (h < contentionEndPeriod + 3)
{
for (int j = 0; j < 128; j += 8)
{
floatingBusTable[h] = tstateToDisp[h + 2]; //screen address
floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)]; //attr address
floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4]; //screen address + 1
floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)]; //attr address + 1
h += 8;
}
h += TstatesPerScanline - 128;
}
// offset floating bus table
var offset = 0;
if (offset != 0)
{
var fbt = floatingBusTable.ToArray();
for (int i = 0; i < FrameLength; i++)
{
var off = i + offset;
if (off < 0)
{
off = FrameLength - 1 + off;
}
else if (off >= FrameLength)
{
off = off - FrameLength;
}
fbt[off] = floatingBusTable[i];
}
floatingBusTable = fbt.ToArray();
}
//build bottom border
while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom))
{
//border(24t) + screen (128t) + border(24t) = 176 tstates
for (int g = 0; g < 176; g++)
tstateToDisp[t++] = 1;
//horizontal retrace
for (int g = 176; g < TstatesPerScanline; g++)
tstateToDisp[t++] = 0;
}
}
#endregion
}
*/
}

View File

@ -1,126 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/*
/// <summary>
/// Abtract class that represents all Media Serializers
/// </summary>
public abstract class MediaSerializer
{
/// <summary>
/// The type of serializer
/// </summary>
public abstract MediaSerializationType FormatType { get; }
/// <summary>
/// Signs whether this class can be used to serialize
/// </summary>
public virtual bool IsSerializer
{
get
{
return false;
}
}
/// <summary>
/// Signs whether this class can be used to de-serialize
/// </summary>
public virtual bool IsDeSerializer
{
get
{
return false;
}
}
/// <summary>
/// Serialization method
/// </summary>
/// <param name="data"></param>
public virtual void Serialize(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"Serialize operation is not implemented for this serializer");
}
/// <summary>
/// DeSerialization method
/// </summary>
/// <param name="data"></param>
public virtual void DeSerialize(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"DeSerialize operation is not implemented for this serializer");
}
/// <summary>
/// Serializer does a quick check, returns TRUE if file is detected as this type
/// </summary>
/// <param name="data"></param>
public virtual bool CheckType(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"Check type operation is not implemented for this serializer");
}
#region Static Tools
/// <summary>
/// Converts an int32 value into a byte array
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected static byte[] GetBytes(int value)
{
byte[] buf = new byte[4];
buf[0] = (byte)value;
buf[1] = (byte)(value >> 8);
buf[2] = (byte)(value >> 16);
buf[3] = (byte)(value >> 24);
return buf;
}
/// <summary>
/// Returns an int32 from a byte array based on offset
/// </summary>
/// <param name="buf"></param>
/// <param name="offsetIndex"></param>
/// <returns></returns>
protected static int GetInt32(byte[] buf, int offsetIndex)
{
return buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24;
}
/// <summary>
/// Returns an uint16 from a byte array based on offset
/// </summary>
/// <param name="buf"></param>
/// <param name="offsetIndex"></param>
/// <returns></returns>
protected static ushort GetWordValue(byte[] buf, int offsetIndex)
{
return (ushort)(buf[offsetIndex] | buf[offsetIndex + 1] << 8);
}
/// <summary>
/// Updates a byte array with a uint16 value based on offset
/// </summary>
/// <param name="buf"></param>
/// <param name="offsetIndex"></param>
/// <param name="value"></param>
protected static void SetWordValue(byte[] buf, int offsetIndex, ushort value)
{
buf[offsetIndex] = (byte)value;
buf[offsetIndex + 1] = (byte)(value >> 8);
}
#endregion
}
*/
}

View File

@ -12,7 +12,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
[Core(
"ZXHawk",
"Asnivor",
"Asnivor, Alyosha",
isPorted: false,
isReleased: false)]
public partial class ZXSpectrum : IRegionable, IDriveLight

View File

@ -1,39 +1,34 @@
## ZXHawk
At the moment this is experimental and is still being worked on.
ZXHawk is still in dev but is potentially nearing a release state.
### Implemented and working (as far as I can tell)
* IEmulator
* ZX Spectrum 48k, 128k, +2 & +2A models
* ULA video output (implementing IVideoProvider)
* ULA Mode 1 VBLANK interrupt generation
* IM2 Interrupts and DataBus implementation (thanks Aloysha)
* Beeper/Buzzer output (implementing ISoundProvider)
* AY-3-8912 sound chip implementation (multiple stereo or mono panning options available as a setting)
* Keyboard input (implementing IInputPollable)
* Default keyboard keymappings
* Kempston, Cursor and Sinclair (Left & Right) joysticks emulated
* Tape device that will load spectrum games in realtime (*.tzx and *.tap)
* Most tape protection/loading schemes that I've tested are currently working
* IStatable
* ISettable core settings
* IDebuggable (for what it's worth)
* DeterministicEmulation as a SyncSetting, LagFrame detection and FrameAdvance render & renderSound bools respected (when DeterministicEmulation == false)
* Tape auto-loading routines (as a setting - default ON)
* Basic tape block navigation (NextBlock, PrevBlock)
* Tape-related OSD messages (verbosity level configured in settings)
### Whats in the box?
* Emulates the Sinclair ZX Spectrum 16k, 48k, 128k, +2, +2A & +3
* Accurate Z80A implementation
* Precise screen timing, floating bus, memory contention and port contention for all models
* Full keyboard emulation
* Kempston, Cursor and Sinclair joysticks emulated
* Full beeper and AY-3-3912 sound emulation
* Tape device (datacorder) emulation
* Internal 3" disk drive emulation (found in the +3 model)
* Currently supports the following tape image formats: *.tzx, *.tap
* Currently supports the following disk image formats (+3 only): *.dsk
* Fully integrated into the Bizhawk ecosystem
* See the ZXSpectrum menu for all available configuration options
### Work in progress
* ZX Spectrum +3 emulation (partially working, see below)
* Exact emulator timings
* Floating memory bus emulation
* TASStudio (need to verify that this works as it should)
### Firmware
ZXHawk ships with the official ZX Spectrum ROMs embedded (licensed by Amstrad).
### Not working
* +3 disk drive - no implementation yet
* Hard & Soft Reset menu options in the client (they are greyed out for some reason)
"Amstrad have kindly given their permission for the redistribution of their copyrighted material but retain that copyright"
http://www.worldofspectrum.org/permits/amstrad-roms.txt
### Help needed
* I'm not a TASer, i've never TASed before. It would be really useful if someone (anyone) can build this branch and test this core from a TAS-workflow / TAStudio perpective. There may still be some work to do an exact timings and memory contention, but otherwise this core is able to play the majority of speccy games out there.
### Issues
* Tape images are read-only. This may change in the future, but with bizhawk's savestate system this is not strictly a necessity
* Disk images are currently read-only as well. There is certain write functionality implemented within the emulated UPD756A disk controller (in order to make games work that require this), but this is not persistent
* Disk drive emulation timing is currently not accurate, meaning that disk games will load faster than they would on a real +3. Due to how the Spectrum interfaces with the disk controller though, this should not cause any compatibility issues
Any questions, issues or bug reports, either use the GitHub issue tracker, or post in the forum thread:
http://tasvideos.org/forum/viewtopic.php?t=20004
-Asnivor