CPCHawk: Port IO changes, Interrupt generation, i8255 PPI chip, PSG IO & Keyboard/Joysticks

This commit is contained in:
Asnivor 2018-07-18 08:12:44 +01:00
parent 4192f764b1
commit c0fcac5ab1
19 changed files with 784 additions and 937 deletions

View File

@ -154,9 +154,7 @@
<Compile Include="Computers\AmstradCPC\Hardware\Input\StandardKeyboard.cs" />
<Compile Include="Computers\AmstradCPC\Hardware\SoundOutput\AY38912.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.GateArray.cs" />
<Compile Include="Computers\AmstradCPC\Machine\CPC464\CPC464.Memory.cs" />
<Compile Include="Computers\AmstradCPC\Machine\CPC464\CPC464.Port.cs" />
<Compile Include="Computers\AmstradCPC\Machine\CPCBase.cs" />

View File

@ -43,19 +43,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
definition.BoolButtons.Add(s);
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
List<string> keys = new List<string>

View File

@ -15,6 +15,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <param name="tStatesPerFrame"></param>
void Init(int sampleRate, int tStatesPerFrame);
void SetFunction(int data);
/// <summary>
/// Activates a register
/// </summary>

View File

@ -41,6 +41,11 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
#region State Information
/// <summary>
/// Signs whether the tape motor is running
/// </summary>
public bool TapeMotor;
/// <summary>
/// Internal counter used to trigger tape buzzer output
/// </summary>
@ -970,6 +975,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
ser.Sync("_monitorTimeOut", ref _monitorTimeOut);
ser.Sync("_monitorLastPC", ref _monitorLastPC);
ser.Sync("_monitorLastRegs", ref _monitorLastRegs, false);
ser.Sync("TapeMotor", ref TapeMotor);
ser.EndSection();
}

View File

@ -652,7 +652,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
case 0:
CRCT.ClockCycle();
//psg clockcycle
//PSG.ClockCycle();
WaitLine = false;
break;
case 1:
@ -675,9 +675,19 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
break;
}
if (!HSYNC && CRCT.HSYNC)
{
HSYNC = true;
}
// run the interrupt generator routine
InterruptGenerator();
if (!CRCT.HSYNC)
{
HSYNC = false;
}
// conditional CPU cycle
DoConditionalCPUCycle();
@ -719,11 +729,16 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
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)
return false;
return accessed;
// Bit 9 and 8 of the data byte define the function to access
if (!dataBits[6] && !dataBits[7])

View File

@ -800,6 +800,13 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
#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>
/// Device responds to an IN instruction
/// </summary>
@ -808,24 +815,30 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <returns></returns>
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"
bool accessed = !portBits[14];
if (!accessed)
return false;
if (portUpper.Bit(6))
return accessed;
// 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
accessed = ReadStatus(ref result);
}
else if (portBits[8] == true && portBits[9] == true)
else if ((portUpper & 3) == 3)
{
// read data register
accessed = ReadRegister(ref result);
}
else
{
result = 0;
}
return accessed;
}
@ -838,26 +851,31 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <returns></returns>
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"
bool accessed = !portBits[14];
if (!accessed)
return false;
if (portUpper.Bit(6))
return accessed;
// Bit 9 and 8 of the I/O port address define the function to access
if (portBits[8] == false && portBits[9] == false)
var func = portUpper & 3;
switch (func)
{
// Select 6845 register
RegisterSelect(result);
}
else if (portBits[8] == true && portBits[9] == false)
{
// Write 6845 register data
WriteRegister(result);
// reg select
case 0:
RegisterSelect(result);
break;
// data write
case 1:
WriteRegister(result);
break;
}
return true;
return accessed;
}
#endregion
@ -884,53 +902,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
ser.Sync("VSYNCWidth", ref VSYNCWidth);
ser.Sync("VSYNCCounter", ref VSYNCCounter);
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

View File

@ -284,6 +284,11 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <param name="phase"></param>
public void AddScanlineCharacter(int index, RenderPhase phase, byte vid1, byte vid2, int[] pens)
{
if (index >= 64)
{
return;
}
switch (phase)
{
case RenderPhase.BORDER:

View File

@ -17,7 +17,18 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
public int 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;
@ -44,6 +55,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
public StandardKeyboard(CPCBase machine)
{
_machine = machine;
//_machine.AYDevice.PortA_IN_CallBack = INCallback;
//_machine.AYDevice.PortA_OUT_CallBack = OUTCallback;
// scancode rows, ascending (Bit0 - Bit7)
KeyboardMatrix = new string[]
@ -92,13 +105,14 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <returns></returns>
public byte ReadCurrentLine()
{
var lin = _currentLine - 0x40;
var lin = _currentLine; // - 0x40;
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);
byte[] bytes = new byte[1];
bi.CopyTo(bytes, 0);
return bytes[0];
byte inv = (byte)(~bytes[0]);
return inv;
}
/// <summary>

View File

@ -23,44 +23,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
#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
public PPI_8255(CPCBase machine)
@ -71,83 +33,202 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
#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>
/// Reads from Port A
/// BDIR Line connected to PSG
/// </summary>
/// <returns></returns>
private int INPortA()
public bool BDIR
{
if (PortA.Input)
{
// read from AY
return PSG.PortRead();
}
else
{
// return stored port data
return PortA.Data;
}
get { return Regs[PORT_C].Bit(7); }
}
/// <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>
/// Writes to Port A
/// </summary>
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);
}
}
/// <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
#region Port B
/*
I/O Mode 0,
Input
#region IN Methods
Bit Description Usage in CPC Usage in KC Compact
7 CAS.IN Cassette data input Same as on CPC
6 PRN.BUSY Parallel/Printer port ready signal, "1" = not ready, "0" = Ready Same as on CPC
5 /EXP Expansion Port /EXP pin Same as on CPC
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)
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")
1 LK1 Expansion Port /TEST pin
0 CRTC VSYNC Vertical Sync ("1"=VSYNC active, "0"=VSYNC inactive) Same as on CPC
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?).
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]
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.
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.
*/
/// <summary>
/// Reads from Port A
/// </summary>
/// <returns></returns>
private int INPortA()
{
if (DirPortA == PortDirection.Input)
{
// read from PSG
return PSG.PortRead();
}
else
{
// Port A is set to output
// return latched value
return Regs[PORT_A];
}
}
/// <summary>
/// Reads from Port B
@ -155,8 +236,9 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <returns></returns>
private int INPortB()
{
if (PortB.Input)
if (DirPortB == PortDirection.Input)
{
// build the PortB output
// start with every bit reset
BitArray rBits = new BitArray(8);
@ -189,194 +271,81 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
}
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>
/// Reads from Port C
/// </summary>
/// <returns></returns>
private int INPortC()
{
var val = PortCU.Data;
// get the PortC value
int val = Regs[PORT_C];
if (PortCU.Input)
val |= 0xf0;
if (DirPortCU == PortDirection.Input)
{
// 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;
}
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
#region PPI Control
/*
This register has two different functions depending on bit7 of the data written to this register.
PPI Control with Bit7=1
#endregion
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)
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)
public void Reset()
{
byte val = (byte)data;
// Bit7 = 1
if (val.Bit(7))
for (int i = 0; i < 3; i++)
{
// Bit0 - Direction for Port C, lower bits
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;
Regs[i] = 0xff;
}
// 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
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);
}
}
Regs[3] = 0xff;
}
#endregion
#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>
/// Device responds to an IN instruction
/// </summary>
@ -385,39 +354,40 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <returns></returns>
public bool ReadPort(ushort port, ref int result)
{
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
BitArray dataBits = new BitArray(BitConverter.GetBytes((byte)result));
byte portUpper = (byte)(port >> 8);
byte portLower = (byte)(port & 0xff);
// The 8255 responds to bit 11 reset
bool accessed = !portBits[11];
if (!accessed)
return false;
// The 8255 responds to bit 11 reset with A10 and A12-A15 set
//if (portUpper.Bit(3))
//return false;
if (!portBits[8] && !portBits[9])
var PPIFunc = (port & 0x0300) >> 8; // portUpper & 3;
switch (PPIFunc)
{
// Port A Data
// PSG (Sound/Keyboard/Joystick)
result = INPortA();
}
// Port A Read
case 0:
// PSG (Sound/Keyboard/Joystick)
result = INPortA();
if (portBits[8] && !portBits[9])
{
// Port B Data
// Vsync/Jumpers/PrinterBusy/CasIn/Exp
result = INPortB();
}
break;
if (!portBits[8] && portBits[9])
{
// Port C Data
// KeybRow/CasOut/PSG
result = INPortC();
}
// Port B Read
case 1:
if (portBits[8] && portBits[9])
{
// Control
return false;
// Vsync/Jumpers/PrinterBusy/CasIn/Exp
result = INPortB();
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;
@ -431,39 +401,48 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <returns></returns>
public bool WritePort(ushort port, int result)
{
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
BitArray dataBits = new BitArray(BitConverter.GetBytes((byte)result));
byte portUpper = (byte)(port >> 8);
byte portLower = (byte)(port & 0xff);
// The 8255 responds to bit 11 reset
bool accessed = !portBits[11];
if (!accessed)
// The 8255 responds to bit 11 reset with A10 and A12-A15 set
if (portUpper.Bit(3))
return false;
if (!portBits[8] && !portBits[9])
{
// Port A Data
// PSG (Sound/Keyboard/Joystick)
OUTPortA(result);
}
var PPIFunc = portUpper & 3;
if (portBits[8] && !portBits[9])
switch (PPIFunc)
{
// Port B Data
// Vsync/Jumpers/PrinterBusy/CasIn/Exp
OUTPortB(result);
}
// Port A Write
case 0:
if (!portBits[8] && portBits[9])
{
// Port C Data
// KeybRow/CasOut/PSG
OUTPortC(result);
}
// PSG (Sound/Keyboard/Joystick)
OUTPortA(result);
if (portBits[8] && portBits[9])
{
// Control
ControlHandler(result);
break;
// Port B Write
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;
@ -476,13 +455,16 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
public void SyncState(Serializer ser)
{
ser.BeginSection("PPI");
PortA.SyncState(ser);
PortB.SyncState(ser);
PortCU.SyncState(ser);
PortCL.SyncState(ser);
ser.Sync("Regs", ref Regs, false);
ser.EndSection();
}
#endregion
#endregion
}
public enum PortDirection
{
Input,
Output
}
}

View File

@ -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>
/// Reads the value from the currently selected register
/// </summary>
/// <returns></returns>
public int PortRead()
{
if (_activeRegister == 14)
if (ActiveFunction == 1)
{
// exteral keyboard register
return _keyboard.ReadCurrentLine();
}
if (_activeRegister == 14)
{
if (PortAInput)
{
// exteral keyboard register
return _keyboard.ReadCurrentLine();
}
else
{
return _keyboard.ReadCurrentLine() & _registers[_activeRegister];
}
}
if (_activeRegister < 16)
return _registers[_activeRegister];
if (_activeRegister < 16)
return _registers[_activeRegister];
}
return 0;
}
@ -225,102 +250,127 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <param name="value"></param>
public void PortWrite(int value)
{
if (_activeRegister == 14)
switch (ActiveFunction)
{
// 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);
default:
break;
// Channel B (Combined Pitch)
// (not written to directly)
case 2:
// select reg
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;
}
int b = (value & 0x0f);
SelectedRegister = b;
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;
}
}
/// <summary>
@ -414,6 +464,10 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// </summary>
private byte _activeRegister;
private bool PortAInput = true;
private bool PortBInput = true;
/// <summary>
/// The frequency of the AY chip
/// </summary>
@ -764,12 +818,16 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
ser.BeginSection("PSG-AY");
ser.Sync("ActiveFunction", ref ActiveFunction);
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("PortAInput", ref PortAInput);
ser.Sync("PortBInput", ref PortBInput);
ser.Sync("_registers", ref _registers, false);
ser.Sync("_activeRegister", ref _activeRegister);

View File

@ -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++;
}
}
}
}
}

View File

@ -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)
{
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -19,17 +20,87 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <returns></returns>
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;
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))
{
return (byte)result;
}
else if (GateArray.ReadPort(port, ref result))
else if (PPI.ReadPort(port, ref result))
{
return (byte)result;
}
else if (PPI.ReadPort(port, ref result))
else if (GateArray.ReadPort(port, ref result))
{
return (byte)result;
}
@ -39,16 +110,63 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <summary>
/// Writes a byte of data to a specified port address
/// Because of the port decoding, multiple devices can be written to
/// </summary>
/// <param name="port"></param>
/// <param name="value"></param>
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))
{ }
}
}

View File

@ -28,14 +28,15 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
GateArray = new AmstradGateArray(this, AmstradGateArray.GateArrayType.Amstrad40007);
PPI = new PPI_8255(this);
KeyboardDevice = new StandardKeyboard(this);
TapeBuzzer = new Beeper(this);
TapeBuzzer.Init(44100, FrameLength);
//AYDevice = new PSG(this, PSG.ay38910_type_t.AY38910_TYPE_8912, GateArray.PSGClockSpeed, 882 * 50);
AYDevice = new AY38912(this);
AYDevice.Init(44100, FrameLength);
KeyboardDevice = new StandardKeyboard(this);
TapeDevice = new DatacorderDevice();
TapeDevice.Init(this);

View File

@ -1,4 +1,8 @@

using BizHawk.Common.NumberExtensions;
using System;
using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
@ -20,5 +24,92 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// <param name="port"></param>
/// <param name="value"></param>
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
}
}
}

View File

@ -338,15 +338,11 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
//KeyboardDevice.SyncState(ser);
//BuzzerDevice.SyncState(ser);
TapeBuzzer.SyncState(ser);
AYDevice.SyncState(ser);
//.SyncState(ser);
//CPUMon.SyncState(ser);
if (AYDevice != null)
{
AYDevice.SyncState(ser);
((AY38912)AYDevice as AY38912).PanningConfiguration = CPC.Settings.AYPanConfig;
}
ser.Sync("tapeMediaIndex", ref tapeMediaIndex);
if (ser.IsReader)
TapeMediaIndex = tapeMediaIndex;

View File

@ -1,11 +1,33 @@
## 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...
#### 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

View File

@ -101,6 +101,9 @@
<Name>BizHawk.Common</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="ClassDiagram1.cd" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 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.
@ -109,4 +112,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<ClassDiagram />