CPCHawk: Port IO changes, Interrupt generation, i8255 PPI chip, PSG IO & Keyboard/Joysticks
This commit is contained in:
parent
4192f764b1
commit
c0fcac5ab1
|
@ -154,9 +154,7 @@
|
||||||
<Compile Include="Computers\AmstradCPC\Hardware\Input\StandardKeyboard.cs" />
|
<Compile Include="Computers\AmstradCPC\Hardware\Input\StandardKeyboard.cs" />
|
||||||
<Compile Include="Computers\AmstradCPC\Hardware\SoundOutput\AY38912.cs" />
|
<Compile Include="Computers\AmstradCPC\Hardware\SoundOutput\AY38912.cs" />
|
||||||
<Compile Include="Computers\AmstradCPC\Hardware\SoundOutput\Beeper.cs" />
|
<Compile Include="Computers\AmstradCPC\Hardware\SoundOutput\Beeper.cs" />
|
||||||
<Compile Include="Computers\AmstradCPC\Hardware\SoundOutput\PSG.cs" />
|
|
||||||
<Compile Include="Computers\AmstradCPC\Machine\CPC464\CPC464.cs" />
|
<Compile Include="Computers\AmstradCPC\Machine\CPC464\CPC464.cs" />
|
||||||
<Compile Include="Computers\AmstradCPC\Machine\CPC464\CPC464.GateArray.cs" />
|
|
||||||
<Compile Include="Computers\AmstradCPC\Machine\CPC464\CPC464.Memory.cs" />
|
<Compile Include="Computers\AmstradCPC\Machine\CPC464\CPC464.Memory.cs" />
|
||||||
<Compile Include="Computers\AmstradCPC\Machine\CPC464\CPC464.Port.cs" />
|
<Compile Include="Computers\AmstradCPC\Machine\CPC464\CPC464.Port.cs" />
|
||||||
<Compile Include="Computers\AmstradCPC\Machine\CPCBase.cs" />
|
<Compile Include="Computers\AmstradCPC\Machine\CPCBase.cs" />
|
||||||
|
|
|
@ -43,19 +43,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
definition.BoolButtons.Add(s);
|
definition.BoolButtons.Add(s);
|
||||||
definition.CategoryLabels[s] = "J2";
|
definition.CategoryLabels[s] = "J2";
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
List<string> joys3 = new List<string>
|
|
||||||
{
|
|
||||||
// P3 Joystick
|
|
||||||
"P3 Up", "P3 Down", "P3 Left", "P3 Right", "P3 Button",
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var s in joys3)
|
|
||||||
{
|
|
||||||
definition.BoolButtons.Add(s);
|
|
||||||
definition.CategoryLabels[s] = "J3 (" + ((ZXSpectrumSyncSettings)SyncSettings as ZXSpectrumSyncSettings).JoystickType3.ToString() + ")";
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// keyboard
|
// keyboard
|
||||||
List<string> keys = new List<string>
|
List<string> keys = new List<string>
|
||||||
|
|
|
@ -15,6 +15,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
/// <param name="tStatesPerFrame"></param>
|
/// <param name="tStatesPerFrame"></param>
|
||||||
void Init(int sampleRate, int tStatesPerFrame);
|
void Init(int sampleRate, int tStatesPerFrame);
|
||||||
|
|
||||||
|
void SetFunction(int data);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Activates a register
|
/// Activates a register
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -41,6 +41,11 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
|
|
||||||
#region State Information
|
#region State Information
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signs whether the tape motor is running
|
||||||
|
/// </summary>
|
||||||
|
public bool TapeMotor;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internal counter used to trigger tape buzzer output
|
/// Internal counter used to trigger tape buzzer output
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -970,6 +975,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
ser.Sync("_monitorTimeOut", ref _monitorTimeOut);
|
ser.Sync("_monitorTimeOut", ref _monitorTimeOut);
|
||||||
ser.Sync("_monitorLastPC", ref _monitorLastPC);
|
ser.Sync("_monitorLastPC", ref _monitorLastPC);
|
||||||
ser.Sync("_monitorLastRegs", ref _monitorLastRegs, false);
|
ser.Sync("_monitorLastRegs", ref _monitorLastRegs, false);
|
||||||
|
ser.Sync("TapeMotor", ref TapeMotor);
|
||||||
ser.EndSection();
|
ser.EndSection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -652,7 +652,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
CRCT.ClockCycle();
|
CRCT.ClockCycle();
|
||||||
//psg clockcycle
|
//PSG.ClockCycle();
|
||||||
WaitLine = false;
|
WaitLine = false;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -675,9 +675,19 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!HSYNC && CRCT.HSYNC)
|
||||||
|
{
|
||||||
|
HSYNC = true;
|
||||||
|
}
|
||||||
|
|
||||||
// run the interrupt generator routine
|
// run the interrupt generator routine
|
||||||
InterruptGenerator();
|
InterruptGenerator();
|
||||||
|
|
||||||
|
if (!CRCT.HSYNC)
|
||||||
|
{
|
||||||
|
HSYNC = false;
|
||||||
|
}
|
||||||
|
|
||||||
// conditional CPU cycle
|
// conditional CPU cycle
|
||||||
DoConditionalCPUCycle();
|
DoConditionalCPUCycle();
|
||||||
|
|
||||||
|
@ -719,11 +729,16 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
{
|
{
|
||||||
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
|
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
|
||||||
BitArray dataBits = new BitArray(BitConverter.GetBytes((byte)result));
|
BitArray dataBits = new BitArray(BitConverter.GetBytes((byte)result));
|
||||||
|
byte portUpper = (byte)(port >> 8);
|
||||||
|
byte portLower = (byte)(port & 0xff);
|
||||||
|
|
||||||
|
// The gate array is selected when bit 15 of the I/O port address is set to "0" and bit 14 of the I/O port address is set to "1"
|
||||||
|
bool accessed = false;
|
||||||
|
if (!portUpper.Bit(7) && portUpper.Bit(6))
|
||||||
|
accessed = true;
|
||||||
|
|
||||||
// The gate array responds to port 0x7F
|
|
||||||
bool accessed = !portBits[15];
|
|
||||||
if (!accessed)
|
if (!accessed)
|
||||||
return false;
|
return accessed;
|
||||||
|
|
||||||
// Bit 9 and 8 of the data byte define the function to access
|
// Bit 9 and 8 of the data byte define the function to access
|
||||||
if (!dataBits[6] && !dataBits[7])
|
if (!dataBits[6] && !dataBits[7])
|
||||||
|
|
|
@ -800,6 +800,13 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
|
|
||||||
#region PortIODevice
|
#region PortIODevice
|
||||||
|
|
||||||
|
/*
|
||||||
|
#BCXX %x0xxxx00 xxxxxxxx 6845 CRTC Index - Write
|
||||||
|
#BDXX %x0xxxx01 xxxxxxxx 6845 CRTC Data Out - Write
|
||||||
|
#BEXX %x0xxxx10 xxxxxxxx 6845 CRTC Status (as far as supported) Read -
|
||||||
|
#BFXX %x0xxxx11 xxxxxxxx 6845 CRTC Data In (as far as supported) Read -
|
||||||
|
*/
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Device responds to an IN instruction
|
/// Device responds to an IN instruction
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -808,24 +815,30 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public bool ReadPort(ushort port, ref int result)
|
public bool ReadPort(ushort port, ref int result)
|
||||||
{
|
{
|
||||||
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
|
byte portUpper = (byte)(port >> 8);
|
||||||
|
byte portLower = (byte)(port & 0xff);
|
||||||
|
|
||||||
|
bool accessed = false;
|
||||||
|
|
||||||
// The 6845 is selected when bit 14 of the I/O port address is set to "0"
|
// The 6845 is selected when bit 14 of the I/O port address is set to "0"
|
||||||
bool accessed = !portBits[14];
|
if (portUpper.Bit(6))
|
||||||
if (!accessed)
|
return accessed;
|
||||||
return false;
|
|
||||||
|
|
||||||
// Bit 9 and 8 of the I/O port address define the function to access
|
// Bit 9 and 8 of the I/O port address define the function to access
|
||||||
if (portBits[8] == false && portBits[9] == true)
|
if (portUpper.Bit(1) && !portUpper.Bit(0))
|
||||||
{
|
{
|
||||||
// read status register
|
// read status register
|
||||||
accessed = ReadStatus(ref result);
|
accessed = ReadStatus(ref result);
|
||||||
}
|
}
|
||||||
else if (portBits[8] == true && portBits[9] == true)
|
else if ((portUpper & 3) == 3)
|
||||||
{
|
{
|
||||||
// read data register
|
// read data register
|
||||||
accessed = ReadRegister(ref result);
|
accessed = ReadRegister(ref result);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return accessed;
|
return accessed;
|
||||||
}
|
}
|
||||||
|
@ -838,26 +851,31 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public bool WritePort(ushort port, int result)
|
public bool WritePort(ushort port, int result)
|
||||||
{
|
{
|
||||||
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
|
byte portUpper = (byte)(port >> 8);
|
||||||
|
byte portLower = (byte)(port & 0xff);
|
||||||
|
|
||||||
|
bool accessed = false;
|
||||||
|
|
||||||
// The 6845 is selected when bit 14 of the I/O port address is set to "0"
|
// The 6845 is selected when bit 14 of the I/O port address is set to "0"
|
||||||
bool accessed = !portBits[14];
|
if (portUpper.Bit(6))
|
||||||
if (!accessed)
|
return accessed;
|
||||||
return false;
|
|
||||||
|
|
||||||
// Bit 9 and 8 of the I/O port address define the function to access
|
var func = portUpper & 3;
|
||||||
if (portBits[8] == false && portBits[9] == false)
|
|
||||||
|
switch (func)
|
||||||
{
|
{
|
||||||
// Select 6845 register
|
// reg select
|
||||||
RegisterSelect(result);
|
case 0:
|
||||||
}
|
RegisterSelect(result);
|
||||||
else if (portBits[8] == true && portBits[9] == false)
|
break;
|
||||||
{
|
|
||||||
// Write 6845 register data
|
// data write
|
||||||
WriteRegister(result);
|
case 1:
|
||||||
|
WriteRegister(result);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return accessed;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -884,53 +902,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
ser.Sync("VSYNCWidth", ref VSYNCWidth);
|
ser.Sync("VSYNCWidth", ref VSYNCWidth);
|
||||||
ser.Sync("VSYNCCounter", ref VSYNCCounter);
|
ser.Sync("VSYNCCounter", ref VSYNCCounter);
|
||||||
ser.EndSection();
|
ser.EndSection();
|
||||||
|
|
||||||
/*
|
|
||||||
* /// <summary>
|
|
||||||
/// Horizontal Character Count
|
|
||||||
/// </summary>
|
|
||||||
private int ;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Vertical Character Count
|
|
||||||
/// </summary>
|
|
||||||
private int ;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Vertical Scanline Count
|
|
||||||
/// </summary>
|
|
||||||
private int ;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Internal cycle counter
|
|
||||||
/// </summary>
|
|
||||||
private int ;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Signs that we have finished the last character row
|
|
||||||
/// </summary>
|
|
||||||
private bool ;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// HSYNC pulse width (in characters)
|
|
||||||
/// </summary>
|
|
||||||
private int ;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Internal HSYNC counter
|
|
||||||
/// </summary>
|
|
||||||
private int ;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// VSYNC pulse width (in characters)
|
|
||||||
/// </summary>
|
|
||||||
private int ;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Internal VSYNC counter
|
|
||||||
/// </summary>
|
|
||||||
private int ;
|
|
||||||
* */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
|
@ -284,6 +284,11 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
/// <param name="phase"></param>
|
/// <param name="phase"></param>
|
||||||
public void AddScanlineCharacter(int index, RenderPhase phase, byte vid1, byte vid2, int[] pens)
|
public void AddScanlineCharacter(int index, RenderPhase phase, byte vid1, byte vid2, int[] pens)
|
||||||
{
|
{
|
||||||
|
if (index >= 64)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (phase)
|
switch (phase)
|
||||||
{
|
{
|
||||||
case RenderPhase.BORDER:
|
case RenderPhase.BORDER:
|
||||||
|
|
|
@ -17,7 +17,18 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
public int CurrentLine
|
public int CurrentLine
|
||||||
{
|
{
|
||||||
get { return _currentLine; }
|
get { return _currentLine; }
|
||||||
set { _currentLine = value; }
|
set
|
||||||
|
{
|
||||||
|
// bits 0-3 contain the line
|
||||||
|
var line = value & 0x0f;
|
||||||
|
|
||||||
|
if (line > 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentLine = line;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool[] _keyStatus;
|
private bool[] _keyStatus;
|
||||||
|
@ -44,6 +55,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
public StandardKeyboard(CPCBase machine)
|
public StandardKeyboard(CPCBase machine)
|
||||||
{
|
{
|
||||||
_machine = machine;
|
_machine = machine;
|
||||||
|
//_machine.AYDevice.PortA_IN_CallBack = INCallback;
|
||||||
|
//_machine.AYDevice.PortA_OUT_CallBack = OUTCallback;
|
||||||
|
|
||||||
// scancode rows, ascending (Bit0 - Bit7)
|
// scancode rows, ascending (Bit0 - Bit7)
|
||||||
KeyboardMatrix = new string[]
|
KeyboardMatrix = new string[]
|
||||||
|
@ -92,13 +105,14 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public byte ReadCurrentLine()
|
public byte ReadCurrentLine()
|
||||||
{
|
{
|
||||||
var lin = _currentLine - 0x40;
|
var lin = _currentLine; // - 0x40;
|
||||||
var pos = lin * 8;
|
var pos = lin * 8;
|
||||||
var l = KeyStatus.Skip(pos).Take(8).Reverse().ToArray();
|
var l = KeyStatus.Skip(pos).Take(8).ToArray();
|
||||||
BitArray bi = new BitArray(l);
|
BitArray bi = new BitArray(l);
|
||||||
byte[] bytes = new byte[1];
|
byte[] bytes = new byte[1];
|
||||||
bi.CopyTo(bytes, 0);
|
bi.CopyTo(bytes, 0);
|
||||||
return bytes[0];
|
byte inv = (byte)(~bytes[0]);
|
||||||
|
return inv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -23,44 +23,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region State
|
|
||||||
|
|
||||||
private State PortA = new State("PortA");
|
|
||||||
private State PortB = new State("PortB");
|
|
||||||
private State PortCU = new State("PortCU");
|
|
||||||
private State PortCL = new State("PortCL");
|
|
||||||
|
|
||||||
private class State
|
|
||||||
{
|
|
||||||
public string Ident;
|
|
||||||
public int Data;
|
|
||||||
public bool Input;
|
|
||||||
public int OpMode;
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
OpMode = 0;
|
|
||||||
Input = true;
|
|
||||||
Data = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
public State(string ident)
|
|
||||||
{
|
|
||||||
Ident = ident;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SyncState(Serializer ser)
|
|
||||||
{
|
|
||||||
ser.BeginSection("PPI_" + Ident);
|
|
||||||
ser.Sync("Data", ref Data);
|
|
||||||
ser.Sync("Input", ref Input);
|
|
||||||
ser.Sync("OpMode", ref OpMode);
|
|
||||||
ser.EndSection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Construction
|
#region Construction
|
||||||
|
|
||||||
public PPI_8255(CPCBase machine)
|
public PPI_8255(CPCBase machine)
|
||||||
|
@ -71,83 +33,202 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Reset
|
#region Implementation
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
PortA.Reset();
|
|
||||||
PortB.Reset();
|
|
||||||
PortCL.Reset();
|
|
||||||
PortCU.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region PORT A
|
|
||||||
/*
|
|
||||||
I/O Mode 0,
|
|
||||||
For writing data to PSG all bits must be set to output,
|
|
||||||
for reading data from PSG all bits must be set to input (thereafter, output direction should be restored, for compatibility with the BIOS).
|
|
||||||
|
|
||||||
Bit Description Usage
|
|
||||||
7-0 PSG.DATA PSG Databus (Sound/Keyboard/Joystick)
|
|
||||||
*/
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads from Port A
|
/// BDIR Line connected to PSG
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
public bool BDIR
|
||||||
private int INPortA()
|
|
||||||
{
|
{
|
||||||
if (PortA.Input)
|
get { return Regs[PORT_C].Bit(7); }
|
||||||
{
|
|
||||||
// read from AY
|
|
||||||
return PSG.PortRead();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// return stored port data
|
|
||||||
return PortA.Data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// BC1 Line connected to PSG
|
||||||
|
/// </summary>
|
||||||
|
public bool BC1
|
||||||
|
{
|
||||||
|
get { return Regs[PORT_C].Bit(6); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Port Constants */
|
||||||
|
private const int PORT_A = 0;
|
||||||
|
private const int PORT_B = 1;
|
||||||
|
private const int PORT_C = 2;
|
||||||
|
private const int PORT_CONTROL = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The i8255 internal data registers
|
||||||
|
/// </summary>
|
||||||
|
private byte[] Regs = new byte[4];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the currently latched port direction for Port A
|
||||||
|
/// </summary>
|
||||||
|
private PortDirection DirPortA
|
||||||
|
{
|
||||||
|
get { return Regs[PORT_CONTROL].Bit(4) ? PortDirection.Input : PortDirection.Output; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the currently latched port direction for Port B
|
||||||
|
/// </summary>
|
||||||
|
private PortDirection DirPortB
|
||||||
|
{
|
||||||
|
get { return Regs[PORT_CONTROL].Bit(1) ? PortDirection.Input : PortDirection.Output; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the currently latched port direction for Port C (lower half)
|
||||||
|
/// </summary>
|
||||||
|
private PortDirection DirPortCL
|
||||||
|
{
|
||||||
|
get { return Regs[PORT_CONTROL].Bit(0) ? PortDirection.Input : PortDirection.Output; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the currently latched port direction for Port C (upper half)
|
||||||
|
/// </summary>
|
||||||
|
private PortDirection DirPortCU
|
||||||
|
{
|
||||||
|
get { return Regs[PORT_CONTROL].Bit(3) ? PortDirection.Input : PortDirection.Output; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#region OUT Methods
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes to Port A
|
/// Writes to Port A
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OUTPortA(int data)
|
private void OUTPortA(int data)
|
||||||
{
|
{
|
||||||
PortA.Data = data;
|
// latch the data
|
||||||
|
Regs[PORT_A] = (byte)data;
|
||||||
|
|
||||||
if (!PortA.Input)
|
if (DirPortA == PortDirection.Output)
|
||||||
{
|
{
|
||||||
// write to AY
|
// PSG write
|
||||||
PSG.PortWrite(data);
|
PSG.PortWrite(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes to Port B
|
||||||
|
/// </summary>
|
||||||
|
private void OUTPortB(int data)
|
||||||
|
{
|
||||||
|
// PortB is read only
|
||||||
|
// just latch the data
|
||||||
|
Regs[PORT_B] = (byte)data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes to Port C
|
||||||
|
/// </summary>
|
||||||
|
private void OUTPortC(int data)
|
||||||
|
{
|
||||||
|
// latch the data
|
||||||
|
Regs[PORT_C] = (byte)data;
|
||||||
|
|
||||||
|
if (DirPortCL == PortDirection.Output)
|
||||||
|
{
|
||||||
|
// lower Port C bits OUT
|
||||||
|
// keyboard line update
|
||||||
|
Keyboard.CurrentLine = Regs[PORT_C] & 0x0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DirPortCU == PortDirection.Output)
|
||||||
|
{
|
||||||
|
// upper Port C bits OUT
|
||||||
|
// write to PSG using latched data
|
||||||
|
PSG.SetFunction(data);
|
||||||
|
PSG.PortWrite(Regs[PORT_A]);
|
||||||
|
|
||||||
|
// cassete write data
|
||||||
|
//not implemeted
|
||||||
|
|
||||||
|
// cas motor control
|
||||||
|
Tape.TapeMotor = Regs[PORT_C].Bit(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes to the control register
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
private void OUTControl(int data)
|
||||||
|
{
|
||||||
|
if (data.Bit(7))
|
||||||
|
{
|
||||||
|
// update configuration
|
||||||
|
Regs[PORT_CONTROL] = (byte)data;
|
||||||
|
|
||||||
|
// Writing to PIO Control Register (with Bit7 set), automatically resets PIO Ports A,B,C to 00h each
|
||||||
|
Regs[PORT_A] = 0;
|
||||||
|
Regs[PORT_B] = 0;
|
||||||
|
Regs[PORT_C] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// register is used to set/reset a single bit in Port C
|
||||||
|
bool isSet = data.Bit(0);
|
||||||
|
|
||||||
|
// get the bit in PortC that we wish to change
|
||||||
|
var bit = (data >> 1) & 7;
|
||||||
|
|
||||||
|
// modify this bit
|
||||||
|
if (isSet)
|
||||||
|
{
|
||||||
|
Regs[PORT_C] = (byte)(Regs[PORT_C] | (bit * bit));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Regs[PORT_C] = (byte)(Regs[PORT_C] & ~(bit * bit));
|
||||||
|
}
|
||||||
|
|
||||||
|
// any other ouput business
|
||||||
|
if (DirPortCL == PortDirection.Output)
|
||||||
|
{
|
||||||
|
// update keyboard line
|
||||||
|
Keyboard.CurrentLine = Regs[PORT_C] & 0x0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DirPortCU == PortDirection.Output)
|
||||||
|
{
|
||||||
|
// write to PSG using latched data
|
||||||
|
PSG.SetFunction(data);
|
||||||
|
PSG.PortWrite(Regs[PORT_A]);
|
||||||
|
|
||||||
|
// cassete write data
|
||||||
|
//not implemeted
|
||||||
|
|
||||||
|
// cas motor control
|
||||||
|
Tape.TapeMotor = Regs[PORT_C].Bit(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Port B
|
#region IN Methods
|
||||||
/*
|
|
||||||
I/O Mode 0,
|
|
||||||
Input
|
|
||||||
|
|
||||||
Bit Description Usage in CPC Usage in KC Compact
|
/// <summary>
|
||||||
7 CAS.IN Cassette data input Same as on CPC
|
/// Reads from Port A
|
||||||
6 PRN.BUSY Parallel/Printer port ready signal, "1" = not ready, "0" = Ready Same as on CPC
|
/// </summary>
|
||||||
5 /EXP Expansion Port /EXP pin Same as on CPC
|
/// <returns></returns>
|
||||||
4 LK4 Screen Refresh Rate ("1"=50Hz, "0"=60Hz) Set to "1"=50Hz (but ignored by the KC BIOS, which always uses 50Hz even if LK4 is changed)
|
private int INPortA()
|
||||||
3 LK3 3bit Distributor ID. Usually set to 4=Awa, 5=Schneider, or 7=Amstrad, see LK-selectable Brand Names for details. Purpose unknown (set to "1")
|
{
|
||||||
2 LK2 Purpose unknown (set to "0")
|
if (DirPortA == PortDirection.Input)
|
||||||
1 LK1 Expansion Port /TEST pin
|
{
|
||||||
0 CRTC VSYNC Vertical Sync ("1"=VSYNC active, "0"=VSYNC inactive) Same as on CPC
|
// read from PSG
|
||||||
|
return PSG.PortRead();
|
||||||
LK1-4 are links on the mainboard ("0" bits are wired to GND). On CPC464,CPC664,CPC6128 and GX4000 they are labeled LK1-LK4, on the CPC464+ and CPC6128+ they are labeled LK101-LK103
|
}
|
||||||
(and LK104, presumably?).
|
else
|
||||||
Bit5 (/EXP) can be used by a expansion device to report its presence. "1" = device connected, "0" = device not connected.
|
{
|
||||||
This is not always used by all expansion devices. is it used by any expansions? [in the DDI-1 disc interface, /EXP connects to the ROM bank selection, bank 0 or bank 7]
|
// Port A is set to output
|
||||||
If port B is programmed as an output, you can make a fake vsync visible to the Gate-Array by writing 1 to bit 0. You can then turn it off by writing 0 to bit 0.
|
// return latched value
|
||||||
It is fake in the sense that it is not generated by the CRTC as it normally is. This fake vsync doesn't work on all CPCs. It is not known if it is dependent on CRTC or 8255 or both.
|
return Regs[PORT_A];
|
||||||
*/
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads from Port B
|
/// Reads from Port B
|
||||||
|
@ -155,8 +236,9 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private int INPortB()
|
private int INPortB()
|
||||||
{
|
{
|
||||||
if (PortB.Input)
|
if (DirPortB == PortDirection.Input)
|
||||||
{
|
{
|
||||||
|
// build the PortB output
|
||||||
// start with every bit reset
|
// start with every bit reset
|
||||||
BitArray rBits = new BitArray(8);
|
BitArray rBits = new BitArray(8);
|
||||||
|
|
||||||
|
@ -189,194 +271,81 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return PortB.Data;
|
// return the latched value
|
||||||
|
return Regs[PORT_B];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes to Port B
|
|
||||||
/// </summary>
|
|
||||||
private void OUTPortB(int data)
|
|
||||||
{
|
|
||||||
// just store the value
|
|
||||||
PortB.Data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Port C
|
|
||||||
/*
|
|
||||||
upper: I/O Mode 0, lower: I/O mode 0,
|
|
||||||
upper: output, lower: output
|
|
||||||
|
|
||||||
Bit Description Usage
|
|
||||||
7 PSG BDIR PSG function selection
|
|
||||||
6 PSG BC1
|
|
||||||
5 Cassette Write data Cassette Out (sometimes also used as Printer Bit7, see 8bit Printer Ports)
|
|
||||||
4 Cassette Motor Control set bit to "1" for motor on, or "0" for motor off
|
|
||||||
0-3 Keyboard line Select keyboard line to be scanned (0-15)
|
|
||||||
|
|
||||||
PSG function selection:
|
|
||||||
|
|
||||||
Bit 7 Bit 6 Function
|
|
||||||
0 0 Inactive
|
|
||||||
0 1 Read from selected PSG register
|
|
||||||
1 0 Write to selected PSG register
|
|
||||||
1 1 Select PSG register
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads from Port C
|
/// Reads from Port C
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private int INPortC()
|
private int INPortC()
|
||||||
{
|
{
|
||||||
var val = PortCU.Data;
|
// get the PortC value
|
||||||
|
int val = Regs[PORT_C];
|
||||||
|
|
||||||
if (PortCU.Input)
|
if (DirPortCU == PortDirection.Input)
|
||||||
val |= 0xf0;
|
{
|
||||||
|
// upper port C bits
|
||||||
|
// remove upper half
|
||||||
|
val &= 0x0f;
|
||||||
|
|
||||||
if (PortCL.Input)
|
// isolate control bits
|
||||||
|
var v = Regs[PORT_C] & 0xc0;
|
||||||
|
|
||||||
|
if (v == 0xc0)
|
||||||
|
{
|
||||||
|
// set reg is present. change to write reg
|
||||||
|
v = 0x80;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cas wr is always set
|
||||||
|
val |= v | 0x20;
|
||||||
|
|
||||||
|
if (Tape.TapeMotor)
|
||||||
|
{
|
||||||
|
val |= 0x10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DirPortCL == PortDirection.Input)
|
||||||
|
{
|
||||||
|
// lower port C bits
|
||||||
val |= 0x0f;
|
val |= 0x0f;
|
||||||
|
}
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes to Port C
|
|
||||||
/// </summary>
|
|
||||||
private void OUTPortC(int data)
|
|
||||||
{
|
|
||||||
PortCL.Data = data;
|
|
||||||
PortCU.Data = data;
|
|
||||||
|
|
||||||
if (!PortCU.Input)
|
|
||||||
{
|
|
||||||
// ay register set and write
|
|
||||||
PSG.SelectedRegister = data;
|
|
||||||
PSG.PortWrite(data);
|
|
||||||
|
|
||||||
// cassette motor control
|
|
||||||
byte val = (byte)data;
|
|
||||||
var motor = val.Bit(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PortCL.Input)
|
|
||||||
{
|
|
||||||
// which keyboard row to scan
|
|
||||||
Keyboard.CurrentLine = PortCL.Data & 0x0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region PPI Control
|
#endregion
|
||||||
/*
|
|
||||||
This register has two different functions depending on bit7 of the data written to this register.
|
|
||||||
|
|
||||||
PPI Control with Bit7=1
|
|
||||||
|
|
||||||
If Bit 7 is "1" then the other bits will initialize Port A-B as Input or Output:
|
#region Reset
|
||||||
|
|
||||||
Bit 0 IO-Cl Direction for Port C, lower bits (always 0=Output in CPC)
|
public void Reset()
|
||||||
Bit 1 IO-B Direction for Port B (always 1=Input in CPC)
|
|
||||||
Bit 2 MS0 Mode for Port B and Port Cl (always zero in CPC)
|
|
||||||
Bit 3 IO-Ch Direction for Port C, upper bits (always 0=Output in CPC)
|
|
||||||
Bit 4 IO-A Direction for Port A (0=Output, 1=Input)
|
|
||||||
Bit 5,6 MS0,MS1 Mode for Port A and Port Ch (always zero in CPC)
|
|
||||||
Bit 7 SF Must be "1" to setup the above bits
|
|
||||||
|
|
||||||
CAUTION: Writing to PIO Control Register (with Bit7 set), automatically resets PIO Ports A,B,C to 00h each!
|
|
||||||
In the CPC only Bit 4 is of interest, all other bits are always having the same value. In order to write to the PSG sound registers, a value of 82h must
|
|
||||||
be written to this register. In order to read from the keyboard (through PSG register 0Eh), a value of 92h must be written to this register.
|
|
||||||
|
|
||||||
PPI Control with Bit7=0
|
|
||||||
|
|
||||||
Otherwise, if Bit 7 is "0" then the register is used to set or clear a single bit in Port C:
|
|
||||||
|
|
||||||
Bit 0 B New value for the specified bit (0=Clear, 1=Set)
|
|
||||||
Bit 1-3 N0,N1,N2 Specifies the number of a bit (0-7) in Port C
|
|
||||||
Bit 4-6 - Not Used
|
|
||||||
Bit 7 SF Must be "0" in this case
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deals with bytes written to the control
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data"></param>
|
|
||||||
private void ControlHandler(int data)
|
|
||||||
{
|
{
|
||||||
byte val = (byte)data;
|
for (int i = 0; i < 3; i++)
|
||||||
|
|
||||||
// Bit7 = 1
|
|
||||||
if (val.Bit(7))
|
|
||||||
{
|
{
|
||||||
// Bit0 - Direction for Port C, lower bits
|
Regs[i] = 0xff;
|
||||||
PortCL.Input = val.Bit(0);
|
|
||||||
|
|
||||||
// Bit1 - Direction for Port B
|
|
||||||
PortB.Input = val.Bit(1);
|
|
||||||
|
|
||||||
// Bit2 - Mode for Port B and Port Cl (CPC always 0)
|
|
||||||
PortB.OpMode = 0;
|
|
||||||
PortCL.OpMode = 0;
|
|
||||||
|
|
||||||
// Bit3 - Direction for Port C, upper bits
|
|
||||||
PortCU.Input = val.Bit(3);
|
|
||||||
|
|
||||||
// Bit4 - Direction for Port A
|
|
||||||
PortA.Input = val.Bit(4);
|
|
||||||
|
|
||||||
// Bits 5,6 - Mode for Port A and Port Ch (CPC always 0)
|
|
||||||
PortA.OpMode = 0;
|
|
||||||
PortCU.OpMode = 0;
|
|
||||||
|
|
||||||
// reset ports
|
|
||||||
PortA.Data = 0x00;
|
|
||||||
PortB.Data = 0x00;
|
|
||||||
PortCL.Data = 0x00;
|
|
||||||
PortCU.Data = 0x00;
|
|
||||||
}
|
}
|
||||||
// Bit7 = 0
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Bit0 - New value for the specified bit (0=Clear, 1=Set)
|
|
||||||
var newBit = val.Bit(0);
|
|
||||||
|
|
||||||
// Bits 1-3 - Specifies the number of a bit (0-7) in Port C
|
Regs[3] = 0xff;
|
||||||
var bit = (data >> 1) & 7;
|
|
||||||
|
|
||||||
if (newBit)
|
|
||||||
{
|
|
||||||
// set the bit
|
|
||||||
PortCL.Data |= ~(1 << bit);
|
|
||||||
PortCU.Data |= ~(1 << bit);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// reset the bit
|
|
||||||
PortCL.Data &= ~(1 << bit);
|
|
||||||
PortCU.Data &= ~(1 << bit);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PortCL.Input)
|
|
||||||
{
|
|
||||||
// keyboard set row
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PortCU.Input)
|
|
||||||
{
|
|
||||||
// ay register set and write
|
|
||||||
PSG.SelectedRegister = val;
|
|
||||||
PSG.PortWrite(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region IPortIODevice
|
#region IPortIODevice
|
||||||
|
|
||||||
|
/*
|
||||||
|
#F4XX %xxxx0x00 xxxxxxxx 8255 PIO Port A (PSG Data) Read Write
|
||||||
|
#F5XX %xxxx0x01 xxxxxxxx 8255 PIO Port B (Vsync,PrnBusy,Tape,etc.) Read -
|
||||||
|
#F6XX %xxxx0x10 xxxxxxxx 8255 PIO Port C (KeybRow,Tape,PSG Control) - Write
|
||||||
|
#F7XX %xxxx0x11 xxxxxxxx 8255 PIO Control-Register - Write
|
||||||
|
*/
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Device responds to an IN instruction
|
/// Device responds to an IN instruction
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -385,39 +354,40 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public bool ReadPort(ushort port, ref int result)
|
public bool ReadPort(ushort port, ref int result)
|
||||||
{
|
{
|
||||||
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
|
byte portUpper = (byte)(port >> 8);
|
||||||
BitArray dataBits = new BitArray(BitConverter.GetBytes((byte)result));
|
byte portLower = (byte)(port & 0xff);
|
||||||
|
|
||||||
// The 8255 responds to bit 11 reset
|
// The 8255 responds to bit 11 reset with A10 and A12-A15 set
|
||||||
bool accessed = !portBits[11];
|
//if (portUpper.Bit(3))
|
||||||
if (!accessed)
|
//return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!portBits[8] && !portBits[9])
|
var PPIFunc = (port & 0x0300) >> 8; // portUpper & 3;
|
||||||
|
|
||||||
|
switch (PPIFunc)
|
||||||
{
|
{
|
||||||
// Port A Data
|
// Port A Read
|
||||||
// PSG (Sound/Keyboard/Joystick)
|
case 0:
|
||||||
result = INPortA();
|
|
||||||
}
|
// PSG (Sound/Keyboard/Joystick)
|
||||||
|
result = INPortA();
|
||||||
|
|
||||||
if (portBits[8] && !portBits[9])
|
break;
|
||||||
{
|
|
||||||
// Port B Data
|
|
||||||
// Vsync/Jumpers/PrinterBusy/CasIn/Exp
|
|
||||||
result = INPortB();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!portBits[8] && portBits[9])
|
// Port B Read
|
||||||
{
|
case 1:
|
||||||
// Port C Data
|
|
||||||
// KeybRow/CasOut/PSG
|
|
||||||
result = INPortC();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (portBits[8] && portBits[9])
|
// Vsync/Jumpers/PrinterBusy/CasIn/Exp
|
||||||
{
|
result = INPortB();
|
||||||
// Control
|
|
||||||
return false;
|
break;
|
||||||
|
|
||||||
|
// Port C Read (docs define this as write-only but we do need to do some processing)
|
||||||
|
case 2:
|
||||||
|
|
||||||
|
// KeybRow/CasOut/PSG
|
||||||
|
result = INPortC();
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -431,39 +401,48 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public bool WritePort(ushort port, int result)
|
public bool WritePort(ushort port, int result)
|
||||||
{
|
{
|
||||||
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
|
byte portUpper = (byte)(port >> 8);
|
||||||
BitArray dataBits = new BitArray(BitConverter.GetBytes((byte)result));
|
byte portLower = (byte)(port & 0xff);
|
||||||
|
|
||||||
// The 8255 responds to bit 11 reset
|
// The 8255 responds to bit 11 reset with A10 and A12-A15 set
|
||||||
bool accessed = !portBits[11];
|
if (portUpper.Bit(3))
|
||||||
if (!accessed)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!portBits[8] && !portBits[9])
|
var PPIFunc = portUpper & 3;
|
||||||
{
|
|
||||||
// Port A Data
|
|
||||||
// PSG (Sound/Keyboard/Joystick)
|
|
||||||
OUTPortA(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (portBits[8] && !portBits[9])
|
switch (PPIFunc)
|
||||||
{
|
{
|
||||||
// Port B Data
|
// Port A Write
|
||||||
// Vsync/Jumpers/PrinterBusy/CasIn/Exp
|
case 0:
|
||||||
OUTPortB(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!portBits[8] && portBits[9])
|
// PSG (Sound/Keyboard/Joystick)
|
||||||
{
|
OUTPortA(result);
|
||||||
// Port C Data
|
|
||||||
// KeybRow/CasOut/PSG
|
|
||||||
OUTPortC(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (portBits[8] && portBits[9])
|
break;
|
||||||
{
|
|
||||||
// Control
|
// Port B Write
|
||||||
ControlHandler(result);
|
case 1:
|
||||||
|
|
||||||
|
// Vsync/Jumpers/PrinterBusy/CasIn/Exp
|
||||||
|
OUTPortB(result);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Port C Write
|
||||||
|
case 2:
|
||||||
|
|
||||||
|
// KeybRow/CasOut/PSG
|
||||||
|
OUTPortC(result);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Control Register Write
|
||||||
|
case 3:
|
||||||
|
|
||||||
|
// Control
|
||||||
|
OUTControl((byte)result);
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -476,13 +455,16 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
public void SyncState(Serializer ser)
|
public void SyncState(Serializer ser)
|
||||||
{
|
{
|
||||||
ser.BeginSection("PPI");
|
ser.BeginSection("PPI");
|
||||||
PortA.SyncState(ser);
|
ser.Sync("Regs", ref Regs, false);
|
||||||
PortB.SyncState(ser);
|
|
||||||
PortCU.SyncState(ser);
|
|
||||||
PortCL.SyncState(ser);
|
|
||||||
ser.EndSection();
|
ser.EndSection();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PortDirection
|
||||||
|
{
|
||||||
|
Input,
|
||||||
|
Output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,20 +201,45 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 0: Inactive
|
||||||
|
/// 1: Read Register
|
||||||
|
/// 2: Write Register
|
||||||
|
/// 3: Select Register
|
||||||
|
/// </summary>
|
||||||
|
public int ActiveFunction;
|
||||||
|
|
||||||
|
public void SetFunction(int val)
|
||||||
|
{
|
||||||
|
int b = ((val & 0xc0) >> 6);
|
||||||
|
ActiveFunction = b;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads the value from the currently selected register
|
/// Reads the value from the currently selected register
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public int PortRead()
|
public int PortRead()
|
||||||
{
|
{
|
||||||
if (_activeRegister == 14)
|
if (ActiveFunction == 1)
|
||||||
{
|
{
|
||||||
// exteral keyboard register
|
if (_activeRegister == 14)
|
||||||
return _keyboard.ReadCurrentLine();
|
{
|
||||||
}
|
if (PortAInput)
|
||||||
|
{
|
||||||
|
// exteral keyboard register
|
||||||
|
return _keyboard.ReadCurrentLine();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return _keyboard.ReadCurrentLine() & _registers[_activeRegister];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_activeRegister < 16)
|
if (_activeRegister < 16)
|
||||||
return _registers[_activeRegister];
|
return _registers[_activeRegister];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -225,102 +250,127 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
public void PortWrite(int value)
|
public void PortWrite(int value)
|
||||||
{
|
{
|
||||||
if (_activeRegister == 14)
|
switch (ActiveFunction)
|
||||||
{
|
{
|
||||||
// external keyboard register
|
default:
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
break;
|
||||||
// Channel B (Combined Pitch)
|
// select reg
|
||||||
// (not written to directly)
|
|
||||||
case 2:
|
|
||||||
case 3:
|
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)
|
int b = (value & 0x0f);
|
||||||
{
|
SelectedRegister = b;
|
||||||
// attack
|
|
||||||
_eState = 0;
|
|
||||||
_eDirection = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// decay
|
|
||||||
_eState = 31;
|
|
||||||
_eDirection = -1;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 14:
|
|
||||||
// IO Port - not implemented
|
// write reg
|
||||||
|
case 2:
|
||||||
|
|
||||||
|
if (_activeRegister == 14)
|
||||||
|
{
|
||||||
|
// external keyboard register
|
||||||
|
//return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
PortAInput = ((value & 0x40) == 0);
|
||||||
|
PortBInput = ((value & 0x80) == 0);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -414,6 +464,10 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private byte _activeRegister;
|
private byte _activeRegister;
|
||||||
|
|
||||||
|
|
||||||
|
private bool PortAInput = true;
|
||||||
|
private bool PortBInput = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The frequency of the AY chip
|
/// The frequency of the AY chip
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -764,12 +818,16 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
{
|
{
|
||||||
ser.BeginSection("PSG-AY");
|
ser.BeginSection("PSG-AY");
|
||||||
|
|
||||||
|
ser.Sync("ActiveFunction", ref ActiveFunction);
|
||||||
|
|
||||||
ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame);
|
ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame);
|
||||||
ser.Sync("_sampleRate", ref _sampleRate);
|
ser.Sync("_sampleRate", ref _sampleRate);
|
||||||
ser.Sync("_samplesPerFrame", ref _samplesPerFrame);
|
ser.Sync("_samplesPerFrame", ref _samplesPerFrame);
|
||||||
ser.Sync("_tStatesPerSample", ref _tStatesPerSample);
|
ser.Sync("_tStatesPerSample", ref _tStatesPerSample);
|
||||||
ser.Sync("_audioBufferIndex", ref _audioBufferIndex);
|
ser.Sync("_audioBufferIndex", ref _audioBufferIndex);
|
||||||
ser.Sync("_audioBuffer", ref _audioBuffer, false);
|
ser.Sync("_audioBuffer", ref _audioBuffer, false);
|
||||||
|
ser.Sync("PortAInput", ref PortAInput);
|
||||||
|
ser.Sync("PortBInput", ref PortBInput);
|
||||||
|
|
||||||
ser.Sync("_registers", ref _registers, false);
|
ser.Sync("_registers", ref _registers, false);
|
||||||
ser.Sync("_activeRegister", ref _activeRegister);
|
ser.Sync("_activeRegister", ref _activeRegister);
|
||||||
|
|
|
@ -1,407 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using BizHawk.Common;
|
|
||||||
using BizHawk.Common.NumberExtensions;
|
|
||||||
using BizHawk.Emulation.Common;
|
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|
||||||
{
|
|
||||||
public sealed class PSG : ISoundProvider
|
|
||||||
{
|
|
||||||
private readonly BlipBuffer _blip = new BlipBuffer(4096);
|
|
||||||
private short[] _sampleBuffer = new short[0];
|
|
||||||
|
|
||||||
|
|
||||||
public PSG()
|
|
||||||
{
|
|
||||||
_blip.SetRates(4000000 / 4.0, 44100);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ushort[] Register = new ushort[16];
|
|
||||||
|
|
||||||
public int total_clock; // TODO: what is this used for?
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
clock_A = clock_B = clock_C = 0x1000;
|
|
||||||
noise_clock = 0x20;
|
|
||||||
|
|
||||||
for (int i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
Register[i] = 0x0000;
|
|
||||||
}
|
|
||||||
sync_psg_state();
|
|
||||||
DiscardSamples();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DiscardSamples()
|
|
||||||
{
|
|
||||||
_blip.Clear();
|
|
||||||
_sampleClock = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetSamplesAsync(short[] samples)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException("Async is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
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 GetSamplesSync(out short[] samples, out int nsamp)
|
|
||||||
{
|
|
||||||
_blip.EndFrame((uint)_sampleClock);
|
|
||||||
_sampleClock = 0;
|
|
||||||
|
|
||||||
nsamp = _blip.SamplesAvailable();
|
|
||||||
int targetLength = nsamp * 2;
|
|
||||||
if (_sampleBuffer.Length != targetLength)
|
|
||||||
{
|
|
||||||
_sampleBuffer = new short[targetLength];
|
|
||||||
}
|
|
||||||
|
|
||||||
_blip.ReadSamplesLeft(_sampleBuffer, nsamp);
|
|
||||||
for (int i = 0; i < _sampleBuffer.Length; i += 2)
|
|
||||||
{
|
|
||||||
_sampleBuffer[i + 1] = _sampleBuffer[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
samples = _sampleBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetSamples(short[] samples)
|
|
||||||
{
|
|
||||||
throw new Exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly int[] VolumeTable =
|
|
||||||
{
|
|
||||||
0x0000, 0x0055, 0x0079, 0x00AB, 0x00F1, 0x0155, 0x01E3, 0x02AA,
|
|
||||||
0x03C5, 0x0555, 0x078B, 0x0AAB, 0x0F16, 0x1555, 0x1E2B, 0x2AAA
|
|
||||||
};
|
|
||||||
|
|
||||||
private int _sampleClock;
|
|
||||||
private int _latchedSample;
|
|
||||||
|
|
||||||
private int TotalExecutedCycles;
|
|
||||||
private int PendingCycles;
|
|
||||||
private int psg_clock;
|
|
||||||
private int sq_per_A, sq_per_B, sq_per_C;
|
|
||||||
private int clock_A, clock_B, clock_C;
|
|
||||||
private int vol_A, vol_B, vol_C;
|
|
||||||
private bool A_on, B_on, C_on;
|
|
||||||
private bool A_up, B_up, C_up;
|
|
||||||
private bool A_noise, B_noise, C_noise;
|
|
||||||
|
|
||||||
private int env_per;
|
|
||||||
private int env_clock;
|
|
||||||
private int env_shape;
|
|
||||||
private int env_E;
|
|
||||||
private int E_up_down;
|
|
||||||
private int env_vol_A, env_vol_B, env_vol_C;
|
|
||||||
|
|
||||||
private int noise_clock;
|
|
||||||
private int noise_per;
|
|
||||||
private int noise = 0x1;
|
|
||||||
|
|
||||||
public Func<ushort, bool, ushort> ReadMemory;
|
|
||||||
public Func<ushort, ushort, bool, bool> WriteMemory;
|
|
||||||
|
|
||||||
public void SyncState(Serializer ser)
|
|
||||||
{
|
|
||||||
ser.BeginSection("PSG");
|
|
||||||
|
|
||||||
ser.Sync("Register", ref Register, false);
|
|
||||||
ser.Sync("Toal_executed_cycles", ref TotalExecutedCycles);
|
|
||||||
ser.Sync("Pending_Cycles", ref PendingCycles);
|
|
||||||
|
|
||||||
ser.Sync("psg_clock", ref psg_clock);
|
|
||||||
ser.Sync("clock_A", ref clock_A);
|
|
||||||
ser.Sync("clock_B", ref clock_B);
|
|
||||||
ser.Sync("clock_C", ref clock_C);
|
|
||||||
ser.Sync("noise_clock", ref noise_clock);
|
|
||||||
ser.Sync("env_clock", ref env_clock);
|
|
||||||
ser.Sync("A_up", ref A_up);
|
|
||||||
ser.Sync("B_up", ref B_up);
|
|
||||||
ser.Sync("C_up", ref C_up);
|
|
||||||
ser.Sync("noise", ref noise);
|
|
||||||
ser.Sync("env_E", ref env_E);
|
|
||||||
ser.Sync("E_up_down", ref E_up_down);
|
|
||||||
|
|
||||||
sync_psg_state();
|
|
||||||
|
|
||||||
ser.EndSection();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ushort? ReadPSG(ushort addr, bool peek)
|
|
||||||
{
|
|
||||||
if (addr >= 0x01F0 && addr <= 0x01FF)
|
|
||||||
{
|
|
||||||
return (ushort)(Register[addr - 0x01F0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sync_psg_state()
|
|
||||||
{
|
|
||||||
sq_per_A = (Register[0] & 0xFF) | (((Register[4] & 0xF) << 8));
|
|
||||||
if (sq_per_A == 0)
|
|
||||||
{
|
|
||||||
sq_per_A = 0x1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
sq_per_B = (Register[1] & 0xFF) | (((Register[5] & 0xF) << 8));
|
|
||||||
if (sq_per_B == 0)
|
|
||||||
{
|
|
||||||
sq_per_B = 0x1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
sq_per_C = (Register[2] & 0xFF) | (((Register[6] & 0xF) << 8));
|
|
||||||
if (sq_per_C == 0)
|
|
||||||
{
|
|
||||||
sq_per_C = 0x1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
env_per = (Register[3] & 0xFF) | (((Register[7] & 0xFF) << 8));
|
|
||||||
if (env_per == 0)
|
|
||||||
{
|
|
||||||
env_per = 0x10000;
|
|
||||||
}
|
|
||||||
|
|
||||||
env_per *= 2;
|
|
||||||
|
|
||||||
A_on = Register[8].Bit(0);
|
|
||||||
B_on = Register[8].Bit(1);
|
|
||||||
C_on = Register[8].Bit(2);
|
|
||||||
A_noise = Register[8].Bit(3);
|
|
||||||
B_noise = Register[8].Bit(4);
|
|
||||||
C_noise = Register[8].Bit(5);
|
|
||||||
|
|
||||||
noise_per = Register[9] & 0x1F;
|
|
||||||
if (noise_per == 0)
|
|
||||||
{
|
|
||||||
noise_per = 0x20;
|
|
||||||
}
|
|
||||||
|
|
||||||
var shape_select = Register[10] & 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[11] & 0xF;
|
|
||||||
env_vol_A = (Register[11] >> 4) & 0x3;
|
|
||||||
|
|
||||||
vol_B = Register[12] & 0xF;
|
|
||||||
env_vol_B = (Register[12] >> 4) & 0x3;
|
|
||||||
|
|
||||||
vol_C = Register[13] & 0xF;
|
|
||||||
env_vol_C = (Register[13] >> 4) & 0x3;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool WritePSG(ushort addr, ushort value, bool poke)
|
|
||||||
{
|
|
||||||
if (addr >= 0x01F0 && addr <= 0x01FF)
|
|
||||||
{
|
|
||||||
var reg = addr - 0x01F0;
|
|
||||||
|
|
||||||
value &= 0xFF;
|
|
||||||
|
|
||||||
if (reg == 4 || reg == 5 || reg == 6 || reg == 10)
|
|
||||||
value &= 0xF;
|
|
||||||
|
|
||||||
if (reg == 9)
|
|
||||||
value &= 0x1F;
|
|
||||||
|
|
||||||
if (reg == 11 || reg == 12 || reg == 13)
|
|
||||||
value &= 0x3F;
|
|
||||||
|
|
||||||
Register[addr - 0x01F0] = value;
|
|
||||||
|
|
||||||
sync_psg_state();
|
|
||||||
|
|
||||||
if (reg == 10)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void generate_sound(int cycles_to_do)
|
|
||||||
{
|
|
||||||
// there are 4 cpu cycles for every psg cycle
|
|
||||||
bool sound_out_A;
|
|
||||||
bool sound_out_B;
|
|
||||||
bool sound_out_C;
|
|
||||||
|
|
||||||
for (int i = 0; i < cycles_to_do; i++)
|
|
||||||
{
|
|
||||||
psg_clock++;
|
|
||||||
|
|
||||||
if (psg_clock == 4)
|
|
||||||
{
|
|
||||||
psg_clock = 0;
|
|
||||||
|
|
||||||
total_clock++;
|
|
||||||
|
|
||||||
clock_A--;
|
|
||||||
clock_B--;
|
|
||||||
clock_C--;
|
|
||||||
|
|
||||||
noise_clock--;
|
|
||||||
env_clock--;
|
|
||||||
|
|
||||||
// clock noise
|
|
||||||
if (noise_clock == 0)
|
|
||||||
{
|
|
||||||
noise = (noise >> 1) ^ (noise.Bit(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.Bit(0) | A_noise) & (A_on | A_up);
|
|
||||||
sound_out_B = (noise.Bit(0) | B_noise) & (B_on | B_up);
|
|
||||||
sound_out_C = (noise.Bit(0) | C_noise) & (C_on | C_up);
|
|
||||||
|
|
||||||
// now calculate the volume of each channel and add them together
|
|
||||||
int v;
|
|
||||||
|
|
||||||
if (env_vol_A == 0)
|
|
||||||
{
|
|
||||||
v = (short)(sound_out_A ? VolumeTable[vol_A] : 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int shift_A = 3 - env_vol_A;
|
|
||||||
if (shift_A < 0)
|
|
||||||
shift_A = 0;
|
|
||||||
v = (short)(sound_out_A ? (VolumeTable[env_E] >> shift_A) : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env_vol_B == 0)
|
|
||||||
{
|
|
||||||
v += (short)(sound_out_B ? VolumeTable[vol_B] : 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int shift_B = 3 - env_vol_B;
|
|
||||||
if (shift_B < 0)
|
|
||||||
shift_B = 0;
|
|
||||||
v += (short)(sound_out_B ? (VolumeTable[env_E] >> shift_B) : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env_vol_C == 0)
|
|
||||||
{
|
|
||||||
v += (short)(sound_out_C ? VolumeTable[vol_C] : 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int shift_C = 3 - env_vol_C;
|
|
||||||
if (shift_C < 0)
|
|
||||||
shift_C = 0;
|
|
||||||
v += (short)(sound_out_C ? (VolumeTable[env_E] >> shift_C) : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v != _latchedSample)
|
|
||||||
{
|
|
||||||
_blip.AddDelta((uint)_sampleClock, v - _latchedSample);
|
|
||||||
_latchedSample = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
_sampleClock++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|
||||||
{
|
|
||||||
public class GateArray_CPC464 : GateArrayBase
|
|
||||||
{
|
|
||||||
public GateArray_CPC464(CPCBase machine)
|
|
||||||
: base(machine)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -19,17 +20,87 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override byte ReadPort(ushort port)
|
public override byte ReadPort(ushort port)
|
||||||
{
|
{
|
||||||
|
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
|
||||||
|
byte portUpper = (byte)(port >> 8);
|
||||||
|
byte portLower = (byte)(port & 0xff);
|
||||||
|
|
||||||
int result = 0xff;
|
int result = 0xff;
|
||||||
|
|
||||||
|
if (DecodeINPort(port) == PortDevice.GateArray)
|
||||||
|
{
|
||||||
|
GateArray.ReadPort(port, ref result);
|
||||||
|
}
|
||||||
|
else if (DecodeINPort(port) == PortDevice.RAMManagement)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (DecodeINPort(port) == PortDevice.CRCT)
|
||||||
|
{
|
||||||
|
CRCT.ReadPort(port, ref result);
|
||||||
|
}
|
||||||
|
else if (DecodeINPort(port) == PortDevice.ROMSelect)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (DecodeINPort(port) == PortDevice.Printer)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (DecodeINPort(port) == PortDevice.PPI)
|
||||||
|
{
|
||||||
|
PPI.ReadPort(port, ref result);
|
||||||
|
}
|
||||||
|
else if (DecodeINPort(port) == PortDevice.Expansion)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return (byte)result;
|
||||||
|
|
||||||
|
if ((port & 0x8000) == 0)
|
||||||
|
{
|
||||||
|
if ((port & 0x4000) != 0)
|
||||||
|
{
|
||||||
|
GateArray.ReadPort(port, ref result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ram
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((port & 0x4000) == 0)
|
||||||
|
{
|
||||||
|
CRCT.ReadPort(port, ref result);
|
||||||
|
}
|
||||||
|
else if ((port & 0x0300) == 0)
|
||||||
|
{
|
||||||
|
// rom
|
||||||
|
}
|
||||||
|
else if ((port & 0x1000) == 0)
|
||||||
|
{
|
||||||
|
// printer
|
||||||
|
}
|
||||||
|
else if ((port & 0x0800) == 0)
|
||||||
|
{
|
||||||
|
PPI.ReadPort(port, ref result);
|
||||||
|
}
|
||||||
|
else if ((port & 0x0400) == 0)
|
||||||
|
{
|
||||||
|
// exp
|
||||||
|
}
|
||||||
|
|
||||||
|
return (byte)result;
|
||||||
|
|
||||||
|
|
||||||
if (CRCT.ReadPort(port, ref result))
|
if (CRCT.ReadPort(port, ref result))
|
||||||
{
|
{
|
||||||
return (byte)result;
|
return (byte)result;
|
||||||
}
|
}
|
||||||
else if (GateArray.ReadPort(port, ref result))
|
else if (PPI.ReadPort(port, ref result))
|
||||||
{
|
{
|
||||||
return (byte)result;
|
return (byte)result;
|
||||||
}
|
}
|
||||||
else if (PPI.ReadPort(port, ref result))
|
else if (GateArray.ReadPort(port, ref result))
|
||||||
{
|
{
|
||||||
return (byte)result;
|
return (byte)result;
|
||||||
}
|
}
|
||||||
|
@ -39,16 +110,63 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a byte of data to a specified port address
|
/// Writes a byte of data to a specified port address
|
||||||
|
/// Because of the port decoding, multiple devices can be written to
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="port"></param>
|
/// <param name="port"></param>
|
||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
public override void WritePort(ushort port, byte value)
|
public override void WritePort(ushort port, byte value)
|
||||||
{
|
{
|
||||||
if (CRCT.WritePort(port, (int)value))
|
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
|
||||||
|
BitArray dataBits = new BitArray(BitConverter.GetBytes(value));
|
||||||
|
byte portUpper = (byte)(port >> 8);
|
||||||
|
byte portLower = (byte)(port & 0xff);
|
||||||
|
|
||||||
|
var devs = DecodeOUTPort(port);
|
||||||
|
|
||||||
|
foreach (var d in devs)
|
||||||
|
{
|
||||||
|
if (d == PortDevice.GateArray)
|
||||||
|
{
|
||||||
|
GateArray.WritePort(port, value);
|
||||||
|
}
|
||||||
|
else if (d == PortDevice.RAMManagement)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (d == PortDevice.CRCT)
|
||||||
|
{
|
||||||
|
CRCT.WritePort(port, value);
|
||||||
|
}
|
||||||
|
else if (d == PortDevice.ROMSelect)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (d == PortDevice.Printer)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (d == PortDevice.PPI)
|
||||||
|
{
|
||||||
|
PPI.WritePort(port, value);
|
||||||
|
}
|
||||||
|
else if (d == PortDevice.Expansion)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (GateArray.WritePort(port, value))
|
||||||
{ }
|
{ }
|
||||||
else if (GateArray.WritePort(port, (int)value))
|
|
||||||
|
if (CRCT.WritePort(port, value))
|
||||||
{ }
|
{ }
|
||||||
else if (PPI.WritePort(port, (int)value))
|
|
||||||
|
// rom select
|
||||||
|
// printer port
|
||||||
|
|
||||||
|
if (PPI.WritePort(port, value))
|
||||||
{ }
|
{ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,14 +28,15 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
GateArray = new AmstradGateArray(this, AmstradGateArray.GateArrayType.Amstrad40007);
|
GateArray = new AmstradGateArray(this, AmstradGateArray.GateArrayType.Amstrad40007);
|
||||||
PPI = new PPI_8255(this);
|
PPI = new PPI_8255(this);
|
||||||
|
|
||||||
KeyboardDevice = new StandardKeyboard(this);
|
|
||||||
|
|
||||||
TapeBuzzer = new Beeper(this);
|
TapeBuzzer = new Beeper(this);
|
||||||
TapeBuzzer.Init(44100, FrameLength);
|
TapeBuzzer.Init(44100, FrameLength);
|
||||||
|
|
||||||
|
//AYDevice = new PSG(this, PSG.ay38910_type_t.AY38910_TYPE_8912, GateArray.PSGClockSpeed, 882 * 50);
|
||||||
AYDevice = new AY38912(this);
|
AYDevice = new AY38912(this);
|
||||||
AYDevice.Init(44100, FrameLength);
|
AYDevice.Init(44100, FrameLength);
|
||||||
|
|
||||||
|
KeyboardDevice = new StandardKeyboard(this);
|
||||||
|
|
||||||
TapeDevice = new DatacorderDevice();
|
TapeDevice = new DatacorderDevice();
|
||||||
TapeDevice.Init(this);
|
TapeDevice.Init(this);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
|
|
||||||
|
using BizHawk.Common.NumberExtensions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -20,5 +24,92 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
/// <param name="port"></param>
|
/// <param name="port"></param>
|
||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
public abstract void WritePort(ushort port, byte value);
|
public abstract void WritePort(ushort port, byte value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a single port device enum based on the port address
|
||||||
|
/// (for IN operations)
|
||||||
|
/// https://web.archive.org/web/20090808085929/http://www.cepece.info/amstrad/docs/iopord.html
|
||||||
|
/// http://www.cpcwiki.eu/index.php/I/O_Port_Summary
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="port"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected virtual PortDevice DecodeINPort(ushort port)
|
||||||
|
{
|
||||||
|
PortDevice dev = PortDevice.Unknown;
|
||||||
|
|
||||||
|
if (!port.Bit(15) && port.Bit(14))
|
||||||
|
dev = PortDevice.GateArray;
|
||||||
|
|
||||||
|
else if (!port.Bit(15))
|
||||||
|
dev = PortDevice.RAMManagement;
|
||||||
|
|
||||||
|
else if (!port.Bit(14))
|
||||||
|
dev = PortDevice.CRCT;
|
||||||
|
|
||||||
|
else if (!port.Bit(13))
|
||||||
|
dev = PortDevice.ROMSelect;
|
||||||
|
|
||||||
|
else if (!port.Bit(12))
|
||||||
|
dev = PortDevice.Printer;
|
||||||
|
|
||||||
|
else if (!port.Bit(11))
|
||||||
|
dev = PortDevice.PPI;
|
||||||
|
|
||||||
|
else if (!port.Bit(10))
|
||||||
|
dev = PortDevice.Expansion;
|
||||||
|
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list of port device enums based on the port address
|
||||||
|
/// (for OUT operations)
|
||||||
|
/// https://web.archive.org/web/20090808085929/http://www.cepece.info/amstrad/docs/iopord.html
|
||||||
|
/// http://www.cpcwiki.eu/index.php/I/O_Port_Summary
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="port"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected virtual List<PortDevice> DecodeOUTPort(ushort port)
|
||||||
|
{
|
||||||
|
List<PortDevice> devs = new List<PortDevice>();
|
||||||
|
|
||||||
|
if (!port.Bit(15) && port.Bit(14))
|
||||||
|
devs.Add(PortDevice.GateArray);
|
||||||
|
|
||||||
|
if (!port.Bit(15))
|
||||||
|
devs.Add(PortDevice.RAMManagement);
|
||||||
|
|
||||||
|
if (!port.Bit(14))
|
||||||
|
devs.Add(PortDevice.CRCT);
|
||||||
|
|
||||||
|
if (!port.Bit(13))
|
||||||
|
devs.Add(PortDevice.ROMSelect);
|
||||||
|
|
||||||
|
if (!port.Bit(12))
|
||||||
|
devs.Add(PortDevice.Printer);
|
||||||
|
|
||||||
|
if (!port.Bit(11))
|
||||||
|
devs.Add(PortDevice.PPI);
|
||||||
|
|
||||||
|
if (!port.Bit(10))
|
||||||
|
devs.Add(PortDevice.Expansion);
|
||||||
|
|
||||||
|
return devs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Potential port devices
|
||||||
|
/// </summary>
|
||||||
|
public enum PortDevice
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
GateArray,
|
||||||
|
RAMManagement,
|
||||||
|
CRCT,
|
||||||
|
ROMSelect,
|
||||||
|
Printer,
|
||||||
|
PPI,
|
||||||
|
Expansion
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -338,15 +338,11 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||||
//KeyboardDevice.SyncState(ser);
|
//KeyboardDevice.SyncState(ser);
|
||||||
//BuzzerDevice.SyncState(ser);
|
//BuzzerDevice.SyncState(ser);
|
||||||
TapeBuzzer.SyncState(ser);
|
TapeBuzzer.SyncState(ser);
|
||||||
|
|
||||||
|
AYDevice.SyncState(ser);
|
||||||
//.SyncState(ser);
|
//.SyncState(ser);
|
||||||
//CPUMon.SyncState(ser);
|
//CPUMon.SyncState(ser);
|
||||||
|
|
||||||
if (AYDevice != null)
|
|
||||||
{
|
|
||||||
AYDevice.SyncState(ser);
|
|
||||||
((AY38912)AYDevice as AY38912).PanningConfiguration = CPC.Settings.AYPanConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
ser.Sync("tapeMediaIndex", ref tapeMediaIndex);
|
ser.Sync("tapeMediaIndex", ref tapeMediaIndex);
|
||||||
if (ser.IsReader)
|
if (ser.IsReader)
|
||||||
TapeMediaIndex = tapeMediaIndex;
|
TapeMediaIndex = tapeMediaIndex;
|
||||||
|
|
|
@ -1,11 +1,33 @@
|
||||||
## CPCHawk
|
## CPCHawk
|
||||||
|
|
||||||
This may or may not work out. But I figured I could at least start by building on the existing ZXHawk implementation (the machines do have CPU, tape format, PSG and disk drive in common).
|
This may or may not work out. But I figured I could at least start by building on the existing ZXHawk implementation (the machines do have CPU, tape format, PSG and disk drive/controller in common).
|
||||||
|
|
||||||
We'll see how that goes...
|
We'll see how that goes...
|
||||||
|
|
||||||
#### Currently working:
|
#### In Place (but probably requires more work)
|
||||||
|
|
||||||
* Nothing. Just starting to get a code outline in place
|
* CPC464 model template
|
||||||
|
* Non-paged memory
|
||||||
|
* Standard lower and upper ROM
|
||||||
|
* Port IO decoding
|
||||||
|
* CRCT (Cathode Ray Tube Controller) chip emulation
|
||||||
|
* Amstrad Gate Array chip emulation
|
||||||
|
* Video rendering (mode 1)
|
||||||
|
* i8255 Programmable Peripheral Interface (PPI) chip emulation
|
||||||
|
* AY-3-8912 PSG Port IO
|
||||||
|
* Keyboard/Joystick
|
||||||
|
* .CDT tape image file support
|
||||||
|
|
||||||
|
#### Not Yet
|
||||||
|
|
||||||
|
* CPC664, CPC6128, CPC464plus, CPC6128plus, GX4000 models
|
||||||
|
* RAM banking
|
||||||
|
* Upper ROM banking
|
||||||
|
* Video rendering (modes 0, 2 & 3)
|
||||||
|
* AY-3-8912 PSG sound output
|
||||||
|
* Datacorder (tape) device
|
||||||
|
* FDC and FDD devices
|
||||||
|
* .DSK image parsing and identification (to auto differenciate from ZX Spectrum disk bootloader)
|
||||||
|
* Expansion IO
|
||||||
|
|
||||||
-Asnivor
|
-Asnivor
|
||||||
|
|
|
@ -101,6 +101,9 @@
|
||||||
<Name>BizHawk.Common</Name>
|
<Name>BizHawk.Common</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="ClassDiagram1.cd" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
@ -109,4 +112,4 @@
|
||||||
<Target Name="AfterBuild">
|
<Target Name="AfterBuild">
|
||||||
</Target>
|
</Target>
|
||||||
-->
|
-->
|
||||||
</Project>
|
</Project>
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ClassDiagram />
|
Loading…
Reference in New Issue