CPCHawk: Gatearray now displaying a picture (mode1)
This commit is contained in:
parent
6863368dd3
commit
4192f764b1
|
@ -139,7 +139,7 @@
|
|||
<Compile Include="Computers\AmstradCPC\Hardware\Abstraction\IKeyboard.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Abstraction\IPortIODevice.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Abstraction\IPSG.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\CRCT\CRCT_6845.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Display\CRCT_6845.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Datacorder\DatacorderDevice.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Disk\CHRN.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Disk\NECUPD765.cs" />
|
||||
|
@ -149,7 +149,8 @@
|
|||
<Compile Include="Computers\AmstradCPC\Hardware\Disk\NECUPD765.IPortIODevice.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Disk\NECUPD765.Timing.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Disk\NECUPS765.Static.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\GateArray\AmstradGateArray.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Display\AmstradGateArray.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Display\CRTDevice.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\Input\StandardKeyboard.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\SoundOutput\AY38912.cs" />
|
||||
<Compile Include="Computers\AmstradCPC\Hardware\SoundOutput\Beeper.cs" />
|
||||
|
|
|
@ -30,6 +30,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
|
|||
public Action IRQCallback = delegate () { };
|
||||
public Action NMICallback = delegate () { };
|
||||
|
||||
// this will be a few cycles off for now
|
||||
// it should suffice for now until Alyosha returns from hiatus
|
||||
public Action IRQACKCallback = delegate () { };
|
||||
|
||||
private void NMI_()
|
||||
{
|
||||
cur_instr = new ushort[]
|
||||
|
@ -47,7 +51,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
|
|||
|
||||
BUSRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 };
|
||||
MEMRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
// Mode 0 interrupts only take effect if a CALL or RST is on the data bus
|
||||
// Otherwise operation just continues as normal
|
||||
|
@ -67,7 +71,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
|
|||
|
||||
BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 };
|
||||
MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 };
|
||||
}
|
||||
|
||||
IRQACKCallback();
|
||||
}
|
||||
|
||||
// Just jump to $0038
|
||||
private void INTERRUPT_1()
|
||||
|
@ -89,7 +95,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
|
|||
|
||||
BUSRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 };
|
||||
MEMRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 };
|
||||
}
|
||||
|
||||
IRQACKCallback();
|
||||
}
|
||||
|
||||
// Interrupt mode 2 uses the I vector combined with a byte on the data bus
|
||||
private void INTERRUPT_2()
|
||||
|
@ -117,7 +125,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
|
|||
|
||||
BUSRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, W, 0 ,0 ,PCh, 0, 0, 0 };
|
||||
MEMRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 };
|
||||
}
|
||||
|
||||
IRQACKCallback();
|
||||
}
|
||||
|
||||
private void ResetInterrupts()
|
||||
{
|
||||
|
|
|
@ -63,11 +63,12 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
_cpu.ReadHardware = _machine.ReadPort;
|
||||
_cpu.WriteHardware = _machine.WritePort;
|
||||
_cpu.FetchDB = _machine.PushBus;
|
||||
_cpu.IRQACKCallback = _machine.GateArray.IORQA;
|
||||
//_cpu.OnExecFetch = _machine.CPUMon.OnExecFetch;
|
||||
|
||||
ser.Register<ITraceable>(_tracer);
|
||||
ser.Register<IDisassemblable>(_cpu);
|
||||
ser.Register<IVideoProvider>(_machine.GateArray);
|
||||
ser.Register<IVideoProvider>(_machine.CRT);
|
||||
|
||||
// initialize sound mixer and attach the various ISoundProvider devices
|
||||
SoundMixer = new SoundProviderMixer((int)(32767 / 10), "Tape Audio", (ISoundProvider)_machine.TapeBuzzer);
|
||||
|
|
|
@ -16,13 +16,14 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
/// http://www.cpcwiki.eu/index.php/Gate_Array
|
||||
/// https://web.archive.org/web/20170612081209/http://www.grimware.org/doku.php/documentations/devices/gatearray
|
||||
/// </summary>
|
||||
public class AmstradGateArray : IVideoProvider, IPortIODevice
|
||||
public class AmstradGateArray : IPortIODevice
|
||||
{
|
||||
#region Devices
|
||||
|
||||
private CPCBase _machine;
|
||||
private Z80A CPU => _machine.CPU;
|
||||
private CRCT_6845 CRCT => _machine.CRCT;
|
||||
private CRTDevice CRT => _machine.CRT;
|
||||
private IPSG PSG => _machine.AYDevice;
|
||||
private NECUPD765 FDC => _machine.UPDDiskDevice;
|
||||
private DatacorderDevice DATACORDER => _machine.TapeDevice;
|
||||
|
@ -67,84 +68,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
|
||||
#endregion
|
||||
|
||||
#region Palletes
|
||||
|
||||
/// <summary>
|
||||
/// The standard CPC Pallete (ordered by firmware #)
|
||||
/// http://www.cpcwiki.eu/index.php/CPC_Palette
|
||||
/// </summary>
|
||||
private static readonly int[] CPCFirmwarePalette =
|
||||
{
|
||||
Colors.ARGB(0x00, 0x00, 0x00), // Black
|
||||
Colors.ARGB(0x00, 0x00, 0x80), // Blue
|
||||
Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue
|
||||
Colors.ARGB(0x80, 0x00, 0x00), // Red
|
||||
Colors.ARGB(0x80, 0x00, 0x80), // Magenta
|
||||
Colors.ARGB(0x80, 0x00, 0xFF), // Mauve
|
||||
Colors.ARGB(0xFF, 0x00, 0x00), // Bright Red
|
||||
Colors.ARGB(0xFF, 0x00, 0x80), // Purple
|
||||
Colors.ARGB(0xFF, 0x00, 0xFF), // Bright Magenta
|
||||
Colors.ARGB(0x00, 0x80, 0x00), // Green
|
||||
Colors.ARGB(0x00, 0x80, 0x80), // Cyan
|
||||
Colors.ARGB(0x00, 0x80, 0xFF), // Sky Blue
|
||||
Colors.ARGB(0x80, 0x80, 0x00), // Yellow
|
||||
Colors.ARGB(0x80, 0x80, 0x80), // White
|
||||
Colors.ARGB(0x80, 0x80, 0xFF), // Pastel Blue
|
||||
Colors.ARGB(0xFF, 0x80, 0x00), // Orange
|
||||
Colors.ARGB(0xFF, 0x80, 0x80), // Pink
|
||||
Colors.ARGB(0xFF, 0x80, 0xFF), // Pastel Magenta
|
||||
Colors.ARGB(0x00, 0xFF, 0x00), // Bright Green
|
||||
Colors.ARGB(0x00, 0xFF, 0x80), // Sea Green
|
||||
Colors.ARGB(0x00, 0xFF, 0xFF), // Bright Cyan
|
||||
Colors.ARGB(0x80, 0xFF, 0x00), // Lime
|
||||
Colors.ARGB(0x80, 0xFF, 0x80), // Pastel Green
|
||||
Colors.ARGB(0x80, 0xFF, 0xFF), // Pastel Cyan
|
||||
Colors.ARGB(0xFF, 0xFF, 0x00), // Bright Yellow
|
||||
Colors.ARGB(0xFF, 0xFF, 0x80), // Pastel Yellow
|
||||
Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The standard CPC Pallete (ordered by hardware #)
|
||||
/// http://www.cpcwiki.eu/index.php/CPC_Palette
|
||||
/// </summary>
|
||||
private static readonly int[] CPCHardwarePalette =
|
||||
{
|
||||
Colors.ARGB(0x80, 0x80, 0x80), // White
|
||||
Colors.ARGB(0x80, 0x80, 0x80), // White (duplicate)
|
||||
Colors.ARGB(0x00, 0xFF, 0x80), // Sea Green
|
||||
Colors.ARGB(0xFF, 0xFF, 0x80), // Pastel Yellow
|
||||
Colors.ARGB(0x00, 0x00, 0x80), // Blue
|
||||
Colors.ARGB(0xFF, 0x00, 0x80), // Purple
|
||||
Colors.ARGB(0x00, 0x80, 0x80), // Cyan
|
||||
Colors.ARGB(0xFF, 0x80, 0x80), // Pink
|
||||
Colors.ARGB(0xFF, 0x00, 0x80), // Purple (duplicate)
|
||||
Colors.ARGB(0xFF, 0xFF, 0x80), // Pastel Yellow (duplicate)
|
||||
Colors.ARGB(0xFF, 0xFF, 0x00), // Bright Yellow
|
||||
Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White
|
||||
Colors.ARGB(0xFF, 0x00, 0x00), // Bright Red
|
||||
Colors.ARGB(0xFF, 0x00, 0xFF), // Bright Magenta
|
||||
Colors.ARGB(0xFF, 0x80, 0x00), // Orange
|
||||
Colors.ARGB(0xFF, 0x80, 0xFF), // Pastel Magenta
|
||||
Colors.ARGB(0x00, 0x00, 0x80), // Blue (duplicate)
|
||||
Colors.ARGB(0x00, 0xFF, 0x80), // Sea Green (duplicate)
|
||||
Colors.ARGB(0x00, 0xFF, 0x00), // Bright Green
|
||||
Colors.ARGB(0x00, 0xFF, 0xFF), // Bright Cyan
|
||||
Colors.ARGB(0x00, 0x00, 0x00), // Black
|
||||
Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue
|
||||
Colors.ARGB(0x00, 0x80, 0x00), // Green
|
||||
Colors.ARGB(0x00, 0x80, 0xFF), // Sky Blue
|
||||
Colors.ARGB(0x80, 0x00, 0x80), // Magenta
|
||||
Colors.ARGB(0x80, 0xFF, 0x80), // Pastel Green
|
||||
Colors.ARGB(0x80, 0xFF, 0x00), // Lime
|
||||
Colors.ARGB(0x80, 0xFF, 0xFF), // Pastel Cyan
|
||||
Colors.ARGB(0x80, 0x00, 0x00), // Red
|
||||
Colors.ARGB(0x80, 0x00, 0xFF), // Mauve
|
||||
Colors.ARGB(0x80, 0x80, 0x00), // Yellow
|
||||
Colors.ARGB(0x80, 0x80, 0xFF), // Pastel Blue
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Construction
|
||||
|
||||
|
@ -153,7 +77,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
_machine = machine;
|
||||
ChipType = chipType;
|
||||
//PenColours = new int[17];
|
||||
SetupScreenSize();
|
||||
CRT.SetupScreenSize();
|
||||
//Reset();
|
||||
}
|
||||
|
||||
|
@ -193,10 +117,10 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// 0-16: Pen Registers
|
||||
/// 17: Border Colour
|
||||
/// 0-15: Pen Registers
|
||||
/// 16: Border Colour
|
||||
/// </summary>
|
||||
private int[] ColourRegisters = new int[17];
|
||||
public int[] ColourRegisters = new int[17];
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected Pen
|
||||
|
@ -243,7 +167,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
set
|
||||
{
|
||||
_RMR = value;
|
||||
ScreenMode = _RMR & 0x03;
|
||||
//ScreenMode = _RMR & 0x03;
|
||||
|
||||
if ((_RMR & 0x08) != 0)
|
||||
_machine.UpperROMPaged = false;
|
||||
|
@ -254,6 +178,12 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
_machine.LowerROMPaged = false;
|
||||
else
|
||||
_machine.LowerROMPaged = true;
|
||||
|
||||
if (_RMR.Bit(4))
|
||||
{
|
||||
// reset interrupt counter
|
||||
InterruptCounter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -297,7 +227,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// The selected screen mode
|
||||
/// The selected screen mode (updated after every HSYNC)
|
||||
/// </summary>
|
||||
private int ScreenMode;
|
||||
|
||||
|
@ -344,12 +274,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Video mode change is syncronised with HSYNC. When the mode is change it takes effect
|
||||
/// from the next HSYNC
|
||||
/// </summary>
|
||||
private int LatchedMode;
|
||||
|
||||
/// <summary>
|
||||
/// Set when the HSYNC signal is detected from the CRCT
|
||||
/// </summary>
|
||||
|
@ -383,16 +307,34 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
private int InterruptHoldCounter;
|
||||
|
||||
/// <summary>
|
||||
/// 2 state field
|
||||
/// Because the renderer outputs 1 pixel for every 2 GA cycles
|
||||
/// Set at the start of a new frame
|
||||
/// </summary>
|
||||
private bool RendererFlipFlop = true;
|
||||
public bool IsNewFrame;
|
||||
|
||||
/// <summary>
|
||||
/// Used for counting the screen buffer positions
|
||||
/// Set when a new line is beginning
|
||||
/// </summary>
|
||||
private int RenderCounter;
|
||||
public bool IsNewLine;
|
||||
|
||||
/// <summary>
|
||||
/// Horizontal Character Counter
|
||||
/// </summary>
|
||||
private int HCC;
|
||||
|
||||
/// <summary>
|
||||
/// Vertical Line Counter
|
||||
/// </summary>
|
||||
private int VLC;
|
||||
|
||||
/// <summary>
|
||||
/// The first video byte fetched
|
||||
/// </summary>
|
||||
private byte VideoByte1;
|
||||
|
||||
/// <summary>
|
||||
/// The second video byte fetched
|
||||
/// </summary>
|
||||
private byte VideoByte2;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -406,14 +348,14 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
FrameClock++;
|
||||
ClockCounter++;
|
||||
|
||||
if (ClockCounter == 16)
|
||||
if (ClockCounter == 4)
|
||||
ClockCounter = 0;
|
||||
|
||||
// check for frame end
|
||||
if (FrameClock == GAFrameLength)
|
||||
if (FrameClock == FrameLength)
|
||||
{
|
||||
FrameClock = 0;
|
||||
//FrameEnd = true;
|
||||
FrameEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,8 +375,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
// /WAIT line is active
|
||||
switch (ClockCounter)
|
||||
{
|
||||
case 8:
|
||||
case 12:
|
||||
case 2:
|
||||
case 3:
|
||||
// gate array video fetch is occuring
|
||||
// check for memory access
|
||||
if (BUSRQ > 0)
|
||||
|
@ -444,7 +386,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
case 1:
|
||||
// CPU accesses RAM if it's performing a non-opcode read or write
|
||||
// assume for now that an opcode fetch is always looking at PC
|
||||
if (BUSRQ == PCh)
|
||||
|
@ -462,10 +404,145 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs one gate array (rendering) cycle
|
||||
/// Handles interrupt generation
|
||||
/// </summary>
|
||||
private void DoCycle()
|
||||
private void InterruptGenerator()
|
||||
{
|
||||
if (HSYNC && !CRCT.HSYNC)
|
||||
{
|
||||
// falling edge of the HSYNC detected
|
||||
InterruptCounter++;
|
||||
|
||||
if (CRCT.VSYNC)
|
||||
{
|
||||
if (HSYNC_counter >= 2)
|
||||
{
|
||||
// x2 HSYNC have happened during VSYNC
|
||||
if (InterruptCounter >= 32)
|
||||
{
|
||||
// no interrupt
|
||||
InterruptCounter = 0;
|
||||
}
|
||||
else if (InterruptCounter < 32)
|
||||
{
|
||||
// interrupt
|
||||
InterruptRaised = true;
|
||||
InterruptCounter = 0;
|
||||
}
|
||||
|
||||
HSYNC_counter = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
HSYNC_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
if (InterruptCounter == 52)
|
||||
{
|
||||
// gatearray should raise an interrupt
|
||||
InterruptRaised = true;
|
||||
InterruptCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (InterruptRaised)
|
||||
{
|
||||
// interrupt should been raised
|
||||
CPU.FlagI = true;
|
||||
InterruptHoldCounter++;
|
||||
|
||||
// the INT signal should be held low for 1.4us.
|
||||
// in gatearray cycles, this equates to 22.4
|
||||
// we will round down to 22 for emulation purposes
|
||||
if (InterruptHoldCounter >= 22)
|
||||
{
|
||||
CPU.FlagI = false;
|
||||
InterruptRaised = false;
|
||||
InterruptHoldCounter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The CRCT builds the picture in a strange way, so that the top left of the display area is the first pixel from
|
||||
/// video RAM. The borders come either side of the HSYNC and VSYNCs later on:
|
||||
/// https://web.archive.org/web/20170501112330im_/http://www.grimware.org/lib/exe/fetch.php/documentations/devices/crtc.6845/crtc.standard.video.frame.png?w=800&h=500
|
||||
/// Therefore when the gate array initialises, we will attempt end the frame early in order to
|
||||
/// sync up at the point where VSYNC is active and HSYNC just begins. This is roughly how a CRT monitor would display the picture.
|
||||
/// The CRT would start a new line at the point where an HSYNC is detected.
|
||||
/// </summary>
|
||||
private void FrameDetector()
|
||||
{
|
||||
if (CRCT.HSYNC && !IsNewLine)
|
||||
{
|
||||
// start of a new line on the next render cycle
|
||||
IsNewLine = true;
|
||||
|
||||
// process scanline
|
||||
CRT.CurrentLine.CommitScanline();
|
||||
|
||||
// check for end of frame
|
||||
if (CRCT.VSYNC && !IsNewFrame)
|
||||
{
|
||||
// start of a new frame on the next render cycle
|
||||
IsNewFrame = true;
|
||||
//FrameEnd = true;
|
||||
VLC = 0;
|
||||
}
|
||||
else if (!CRCT.VSYNC)
|
||||
{
|
||||
// increment line counter
|
||||
VLC++;
|
||||
IsNewFrame = false;
|
||||
}
|
||||
|
||||
HCC = 0;
|
||||
|
||||
// update screenmode
|
||||
ScreenMode = RMR & 0x03;
|
||||
CRT.CurrentLine.InitScanline(ScreenMode, VLC);
|
||||
//CRT.InitScanline(VLC, ScreenMode);
|
||||
}
|
||||
else if (!CRCT.HSYNC)
|
||||
{
|
||||
// reset the flags
|
||||
IsNewLine = false;
|
||||
IsNewFrame = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches a video RAM byte
|
||||
/// This happens at 2Mhz when a memory fetch is due
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
private void FetchByte(int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 1:
|
||||
VideoByte1 = _machine.FetchScreenMemory(CRCT.CurrentByteAddress);
|
||||
break;
|
||||
case 2:
|
||||
VideoByte2 = _machine.FetchScreenMemory((ushort)(CRCT.CurrentByteAddress + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called at 1Mhz
|
||||
/// Generates the internal screen layout (to be displayed at the end of the frame by the CRT)
|
||||
/// Each PixelGenerator cycle will process 1 horizontal character
|
||||
/// If the area to generate is in display RAM, 2 bytes will be processed
|
||||
/// </summary>
|
||||
private void PixelGenerator()
|
||||
{
|
||||
// mode 0: 160x200 pixels: 1 character == 1Mhz == 2 pixels == 4 bits per pixel = 2 pixels per screenbyte
|
||||
// mode 1: 320x200 pixels: 1 character == 1Mhz == 4 pixels == 2 bits per pixel = 4 pixels per screenbyte
|
||||
// mode 2: 640x200 pixels: 1 character == 1Mhz == 8 pixels == 1 bits per pixel = 8 pixels per screenbyte
|
||||
// mode 3: 160x200 pixels: 1 character == 1Mhz == 2 pixels == 2 bits per pixel = 2 pixels per screenbyte
|
||||
|
||||
/*
|
||||
http://www.cpcmania.com/Docs/Programming/Painting_pixels_introduction_to_video_memory.htm
|
||||
http://www.cantrell.org.uk/david/tech/cpc/cpc-firmware/
|
||||
|
@ -524,157 +601,34 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
Screen layout and generation: http://www.cpcwiki.eu/forum/programming/rupture/?action=dlattach;attach=16221
|
||||
*/
|
||||
|
||||
// run the interrupt generator routine
|
||||
InterruptGenerator();
|
||||
|
||||
#region Testing
|
||||
|
||||
if (CRCT.DISPTMG)
|
||||
if (CRCT.VSYNC && CRCT.HSYNC)
|
||||
{
|
||||
displayCounter++;
|
||||
}
|
||||
else if (CRCT.HSYNC)
|
||||
{
|
||||
hsyncCounter++;
|
||||
}
|
||||
else if (!CRCT.DISPTMG)
|
||||
{
|
||||
borderCounter++;
|
||||
// both hsync and vsync active
|
||||
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.HSYNCandVSYNC, VideoByte1, VideoByte2, ColourRegisters);
|
||||
//CRT.AddScanlineCharacter(VLC, HCC++, RenderPhase.HSYNCandVSYNC, VideoByte1, VideoByte2, ColourRegisters);
|
||||
}
|
||||
else if (CRCT.VSYNC)
|
||||
{
|
||||
vsyncCounter++;
|
||||
// vsync is active but hsync is not
|
||||
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.VSYNC, VideoByte1, VideoByte2, ColourRegisters);
|
||||
//CRT.AddScanlineCharacter(VLC, HCC++, RenderPhase.VSYNC, VideoByte1, VideoByte2, ColourRegisters);
|
||||
}
|
||||
|
||||
if (!CRCT.HSYNC && HSYNC)
|
||||
else if (CRCT.HSYNC)
|
||||
{
|
||||
// end of line
|
||||
displayCounter = 0;
|
||||
hsyncCounter = 0;
|
||||
borderCounter = 0;
|
||||
vsyncCounter = 0;
|
||||
|
||||
lineCounter++;
|
||||
// hsync is active but vsync is not
|
||||
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.HSYNC, VideoByte1, VideoByte2, ColourRegisters);
|
||||
//CRT.AddScanlineCharacter(VLC, HCC++, RenderPhase.HSYNC, VideoByte1, VideoByte2, ColourRegisters);
|
||||
}
|
||||
|
||||
if (borderCounter > 160)
|
||||
else if (!CRCT.DISPTMG)
|
||||
{
|
||||
|
||||
// border generation
|
||||
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.BORDER, VideoByte1, VideoByte2, ColourRegisters);
|
||||
//CRT.AddScanlineCharacter(VLC, HCC++, RenderPhase.BORDER, VideoByte1, VideoByte2, ColourRegisters);
|
||||
}
|
||||
|
||||
if (CRCT.VSYNC)
|
||||
else if (CRCT.DISPTMG)
|
||||
{
|
||||
|
||||
}
|
||||
if (!CRCT.VSYNC && VSYNC)
|
||||
{
|
||||
// end of screen
|
||||
lineCounter = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// When the start of the vertical sync is seen by the monitor it starts the next frame. This means border
|
||||
// is effectively split between top and bottom of the display. Border above the VSYNC is the bottom
|
||||
// border, Border below the VSYNC is the top border
|
||||
if (!VSYNC && CRCT.VSYNC)
|
||||
{
|
||||
VSYNC = true;
|
||||
FrameEnd = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// update HSYNC & VSYNC from CRCT
|
||||
HSYNC = CRCT.HSYNC;
|
||||
VSYNC = CRCT.VSYNC;
|
||||
|
||||
// 2 GA cycles per pixel
|
||||
RendererFlipFlop = !RendererFlipFlop;
|
||||
if (RendererFlipFlop)
|
||||
{
|
||||
if (HSYNC)
|
||||
{
|
||||
// HSYNC in progress
|
||||
// output black
|
||||
}
|
||||
else if (!CRCT.DISPTMG)
|
||||
{
|
||||
// outputting border colour
|
||||
ScreenBuffer[RenderCounter++] = CPCHardwarePalette[ColourRegisters[16]];
|
||||
}
|
||||
else if (CRCT.DISPTMG)
|
||||
{
|
||||
// outputting vid RAM
|
||||
Random rnd = new Random();
|
||||
ScreenBuffer[RenderCounter++] = CPCHardwarePalette[ColourRegisters[1]];
|
||||
}
|
||||
if (CRCT.VSYNC)
|
||||
{
|
||||
RenderCounter = 40;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int displayCounter = 0;
|
||||
int hsyncCounter = 0;
|
||||
int borderCounter = 0;
|
||||
int vsyncCounter = 0;
|
||||
|
||||
int lineCounter = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Handles interrupt generation
|
||||
/// </summary>
|
||||
private void InterruptGenerator()
|
||||
{
|
||||
if (HSYNC && !CRCT.HSYNC)
|
||||
{
|
||||
// falling edge of the HSYNC detected
|
||||
InterruptCounter++;
|
||||
|
||||
if (CRCT.VSYNC)
|
||||
{
|
||||
if (HSYNC_counter == 2)
|
||||
{
|
||||
// x2 HSYNC have happened during VSYNC
|
||||
if (InterruptCounter >= 32)
|
||||
{
|
||||
// no interrupt
|
||||
InterruptCounter = 0;
|
||||
}
|
||||
else if (InterruptCounter < 32)
|
||||
{
|
||||
// interrupt
|
||||
InterruptRaised = true;
|
||||
InterruptCounter = 0;
|
||||
}
|
||||
|
||||
HSYNC_counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (InterruptCounter == 52)
|
||||
{
|
||||
// gatearray should raise an interrupt
|
||||
InterruptRaised = true;
|
||||
InterruptCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (InterruptRaised)
|
||||
{
|
||||
// interrupt has been raised
|
||||
CPU.IFF1 = true;
|
||||
InterruptHoldCounter++;
|
||||
|
||||
// the INT signal should be held low for 1.4us.
|
||||
// in gatearray cycles, this equates to 22.4
|
||||
// we will round down to 22 for emulation purposes
|
||||
if (InterruptHoldCounter >= 22)
|
||||
{
|
||||
CPU.IFF1 = false;
|
||||
InterruptRaised = false;
|
||||
}
|
||||
// pixels generated from video RAM
|
||||
CRT.CurrentLine.AddScanlineCharacter(HCC++, RenderPhase.DISPLAY, VideoByte1, VideoByte2, ColourRegisters);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -683,119 +637,64 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// The gate array is clocked at 16Mhz
|
||||
/// It provides the CPU clock at 4Mhz
|
||||
/// The CRCT clock at 1Mhz
|
||||
/// The PSG clock at 1Mhz
|
||||
///
|
||||
/// Each time this method is called, the gatearray performs 16 cycles
|
||||
/// (equivalent to 4 uncontended CPU cycles)
|
||||
/// Called every CPU cycle
|
||||
/// In reality the GA is clocked at 16Mhz (4 times the frequency of the CPU)
|
||||
/// Therefore this method has to take care of:
|
||||
/// 4 GA cycles
|
||||
/// 1 CRCT cycle every 4 calls
|
||||
/// 1 PSG cycle every 4 calls
|
||||
/// 1 CPU cycle (uncontended)
|
||||
/// </summary>
|
||||
public void DoCycles()
|
||||
public void ClockCycle()
|
||||
{
|
||||
// 16 gatearray cycles
|
||||
// gatearray uses 4-phase clock to supply clocks to other devices
|
||||
switch (ClockCounter)
|
||||
{
|
||||
// 0Mhz
|
||||
case 0:
|
||||
// wait line inactive
|
||||
WaitLine = false;
|
||||
|
||||
CRCT.ClockCycle();
|
||||
//psg
|
||||
|
||||
// GA render cycle
|
||||
DoCycle();
|
||||
|
||||
// CPU
|
||||
DoConditionalCPUCycle();
|
||||
|
||||
// cycle the tape device
|
||||
if (FDC == null || !FDC.FDD_IsDiskLoaded)
|
||||
DATACORDER.TapeCycle();
|
||||
//psg clockcycle
|
||||
WaitLine = false;
|
||||
break;
|
||||
// 4Mhz
|
||||
case 4:
|
||||
// wait line active
|
||||
case 1:
|
||||
WaitLine = true;
|
||||
|
||||
// GA render cycle
|
||||
DoCycle();
|
||||
|
||||
// CPU
|
||||
DoConditionalCPUCycle();
|
||||
|
||||
// cycle the tape device
|
||||
if (FDC == null || !FDC.FDD_IsDiskLoaded)
|
||||
DATACORDER.TapeCycle();
|
||||
// detect new scanline and upcoming new frame on next render cycle
|
||||
FrameDetector();
|
||||
break;
|
||||
// 8Mhz
|
||||
case 8:
|
||||
// wait line active
|
||||
WaitLine = true;
|
||||
|
||||
// GA render cycle
|
||||
DoCycle();
|
||||
|
||||
// CPU
|
||||
DoConditionalCPUCycle();
|
||||
|
||||
case 2:
|
||||
// video fetch
|
||||
|
||||
// cycle the tape device
|
||||
if (FDC == null || !FDC.FDD_IsDiskLoaded)
|
||||
DATACORDER.TapeCycle();
|
||||
break;
|
||||
// 12Mhz
|
||||
case 12:
|
||||
// wait line active
|
||||
WaitLine = true;
|
||||
|
||||
// GA render cycle
|
||||
DoCycle();
|
||||
|
||||
// CPU
|
||||
DoConditionalCPUCycle();
|
||||
|
||||
// video fetch
|
||||
|
||||
// cycle the tape device
|
||||
if (FDC == null || !FDC.FDD_IsDiskLoaded)
|
||||
DATACORDER.TapeCycle();
|
||||
break;
|
||||
// all other GA cycles
|
||||
default:
|
||||
// GA render cycle
|
||||
DoCycle();
|
||||
//if (CRCT.DISPTMG)
|
||||
FetchByte(1);
|
||||
break;
|
||||
case 3:
|
||||
// video fetch and render
|
||||
WaitLine = true;
|
||||
//if (CRCT.DISPTMG)
|
||||
FetchByte(2);
|
||||
PixelGenerator();
|
||||
break;
|
||||
}
|
||||
|
||||
// run the interrupt generator routine
|
||||
InterruptGenerator();
|
||||
|
||||
// conditional CPU cycle
|
||||
DoConditionalCPUCycle();
|
||||
|
||||
AdvanceClock();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region VideoLookups
|
||||
|
||||
|
||||
public struct PixelLookupTable
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs at the start of a frame in order to setup the
|
||||
/// video buffer (in case the CRCT has changed anything)
|
||||
/// Called when the Z80 acknowledges an interrupt
|
||||
/// </summary>
|
||||
public void SetupVideo()
|
||||
public void IORQA()
|
||||
{
|
||||
|
||||
// bit 5 of the interrupt counter is reset
|
||||
InterruptCounter &= ~(1 << 5);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region IPortIODevice
|
||||
|
||||
/// <summary>
|
||||
|
@ -856,101 +755,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
|
||||
#endregion
|
||||
|
||||
#region IVideoProvider
|
||||
|
||||
/// <summary>
|
||||
/// Video output buffer
|
||||
/// </summary>
|
||||
public int[] ScreenBuffer;
|
||||
|
||||
private int _virtualWidth;
|
||||
private int _virtualHeight;
|
||||
private int _bufferWidth;
|
||||
private int _bufferHeight;
|
||||
|
||||
public int BackgroundColor
|
||||
{
|
||||
get { return CPCHardwarePalette[16]; }
|
||||
}
|
||||
|
||||
public int VirtualWidth
|
||||
{
|
||||
get { return _virtualWidth; }
|
||||
set { _virtualWidth = value; }
|
||||
}
|
||||
|
||||
public int VirtualHeight
|
||||
{
|
||||
get { return _virtualHeight; }
|
||||
set { _virtualHeight = value; }
|
||||
}
|
||||
|
||||
public int BufferWidth
|
||||
{
|
||||
get { return _bufferWidth; }
|
||||
set { _bufferWidth = value; }
|
||||
}
|
||||
|
||||
public int BufferHeight
|
||||
{
|
||||
get { return _bufferHeight; }
|
||||
set { _bufferHeight = value; }
|
||||
}
|
||||
|
||||
public int VsyncNumerator
|
||||
{
|
||||
get { return Z80ClockSpeed * 50; }
|
||||
set { }
|
||||
}
|
||||
|
||||
public int VsyncDenominator
|
||||
{
|
||||
get { return Z80ClockSpeed; }
|
||||
}
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
/*
|
||||
Random rnd = new Random();
|
||||
for (int i = 0; i < BufferWidth * BufferHeight; i++)
|
||||
{
|
||||
ScreenBuffer[i] = CPCHardwarePalette[rnd.Next(0, CPCHardwarePalette.Length - 1)];
|
||||
}
|
||||
*/
|
||||
//RenderCounter = 0;
|
||||
return ScreenBuffer;
|
||||
}
|
||||
|
||||
protected void SetupScreenSize()
|
||||
{
|
||||
/*
|
||||
* Rect Pixels: Mode 0: 160×200 pixels with 16 colors (4 bpp)
|
||||
Square Pixels: Mode 1: 320×200 pixels with 4 colors (2 bpp)
|
||||
Rect Pixels: Mode 2: 640×200 pixels with 2 colors (1 bpp)
|
||||
Rect Pixels: Mode 3: 160×200 pixels with 4 colors (2bpp) (this is not an official mode, but rather a side-effect of the hardware)
|
||||
*
|
||||
* */
|
||||
|
||||
// define maximum screen buffer size
|
||||
// to fit all possible resolutions, 640x400 should do it
|
||||
// therefore a scanline will take two buffer rows
|
||||
// and buffer columns will be:
|
||||
// Mode 1: 2 pixels
|
||||
// Mode 2: 1 pixel
|
||||
// Mode 0: 4 pixels
|
||||
// Mode 3: 4 pixels
|
||||
|
||||
BufferWidth = 400; // 640;
|
||||
BufferHeight = 400;
|
||||
VirtualHeight = BufferHeight;
|
||||
VirtualWidth = BufferWidth;
|
||||
ScreenBuffer = new int[BufferWidth * BufferHeight];
|
||||
croppedBuffer = ScreenBuffer;
|
||||
}
|
||||
|
||||
protected int[] croppedBuffer;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Serialization
|
||||
|
||||
|
@ -969,35 +774,19 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
ser.Sync("WaitLine", ref WaitLine);
|
||||
ser.Sync("_interruptCounter", ref _interruptCounter);
|
||||
ser.Sync("ScreenMode", ref ScreenMode);
|
||||
ser.Sync("LatchedMode", ref LatchedMode);
|
||||
ser.Sync("HSYNC", ref HSYNC);
|
||||
ser.Sync("HSYNC_falling", ref HSYNC_falling);
|
||||
ser.Sync("HSYNC_counter", ref HSYNC_counter);
|
||||
ser.Sync("VSYNC", ref VSYNC);
|
||||
ser.Sync("InterruptRaised", ref InterruptRaised);
|
||||
ser.Sync("InterruptHoldCounter", ref InterruptHoldCounter);
|
||||
ser.Sync("RendererFlipFlop", ref RendererFlipFlop);
|
||||
ser.Sync("_MA", ref _MA);
|
||||
ser.EndSection();
|
||||
|
||||
/*
|
||||
* /// <summary>
|
||||
/// Is set when an initial HSYNC is seen from the CRCT
|
||||
/// On real hardware interrupt generation is based on the falling edge of the HSYNC signal
|
||||
/// So in this emulation, once the falling edge is detected, processing happens
|
||||
/// </summary>
|
||||
private bool ;
|
||||
|
||||
/// <summary>
|
||||
/// Used to count HSYNCs during a VSYNC
|
||||
/// </summary>
|
||||
private int ;
|
||||
* */
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Enums
|
||||
#region Enums & Classes
|
||||
|
||||
public enum GateArrayType
|
||||
{
|
|
@ -1,4 +1,5 @@
|
|||
using BizHawk.Common;
|
||||
using BizHawk.Common.NumberExtensions;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
|
@ -59,7 +60,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
#region Public Lookups
|
||||
|
||||
/*
|
||||
* These are not accessible on real hardware
|
||||
* These are not accessible directlyon real hardware
|
||||
* It just makes screen generation easier to have these accessbile from the gate array
|
||||
*/
|
||||
|
||||
|
@ -147,7 +148,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
{
|
||||
get
|
||||
{
|
||||
return VSYNCWidth * ((int)Regs[MAX_RASTER_ADDR] + 1);
|
||||
return VSYNCWidth; // * ((int)Regs[MAX_RASTER_ADDR] + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,6 +163,53 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the starting video page address as specified within R12
|
||||
/// </summary>
|
||||
public int VideoPageBase
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Regs[12].Bit(4) && Regs[12].Bit(5))
|
||||
return 0x8000;
|
||||
|
||||
if (Regs[12].Bit(4) && !Regs[12].Bit(5))
|
||||
return 0x4000;
|
||||
|
||||
if (!Regs[12].Bit(4) && !Regs[12].Bit(5))
|
||||
return 0x0000;
|
||||
|
||||
return 0xC000;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the video buffer size as specified within R12
|
||||
/// </summary>
|
||||
public int VideoBufferSize
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Regs[12].Bit(3) && Regs[12].Bit(2))
|
||||
return 0x8000;
|
||||
|
||||
return 0x4000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Easier memory functions */
|
||||
|
||||
/// <summary>
|
||||
/// The current byte address
|
||||
/// </summary>
|
||||
public ushort CurrentByteAddress;
|
||||
|
||||
/// <summary>
|
||||
/// ByteCOunter
|
||||
/// </summary>
|
||||
public int ByteCounter;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Registers and State
|
||||
|
@ -387,6 +435,8 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
// HSYNC in progress
|
||||
HSYNCCounter++;
|
||||
|
||||
ByteCounter = 0;
|
||||
|
||||
if (HSYNCCounter >= HSYNCWidth)
|
||||
{
|
||||
// end of HSYNC
|
||||
|
@ -403,9 +453,20 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
{
|
||||
DISPTMG = false;
|
||||
}
|
||||
else if (VCC >= Regs[VER_DISPLAYED])
|
||||
{
|
||||
DISPTMG = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
DISPTMG = true;
|
||||
|
||||
var line = VCC;
|
||||
var row = VLC;
|
||||
var addr = VideoPageBase + (line * 0x50) + (row * 0x800) + (ByteCounter);
|
||||
CurrentByteAddress = (ushort)addr;
|
||||
|
||||
ByteCounter += 2;
|
||||
}
|
||||
|
||||
// check for the end of the current scanline
|
||||
|
@ -574,6 +635,16 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
if (SelectedRegister > 17)
|
||||
return;
|
||||
|
||||
if (SelectedRegister == DISP_START_ADDR_L)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if (SelectedRegister == DISP_START_ADDR_H)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Regs[SelectedRegister] = (byte)(data & CPCMask[SelectedRegister]);
|
||||
|
||||
if (SelectedRegister == HOR_AND_VER_SYNC_WIDTHS)
|
|
@ -0,0 +1,573 @@
|
|||
using BizHawk.Common;
|
||||
using BizHawk.Common.NumberExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||
{
|
||||
/// <summary>
|
||||
/// Render pixels to the screen
|
||||
/// </summary>
|
||||
public class CRTDevice : IVideoProvider
|
||||
{
|
||||
#region Devices
|
||||
|
||||
private CPCBase _machine;
|
||||
private CRCT_6845 CRCT => _machine.CRCT;
|
||||
private AmstradGateArray GateArray => _machine.GateArray;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
|
||||
public CRTDevice(CPCBase machine)
|
||||
{
|
||||
_machine = machine;
|
||||
CurrentLine = new ScanLine(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Palettes
|
||||
|
||||
/// <summary>
|
||||
/// The standard CPC Pallete (ordered by firmware #)
|
||||
/// http://www.cpcwiki.eu/index.php/CPC_Palette
|
||||
/// </summary>
|
||||
public static readonly int[] CPCFirmwarePalette =
|
||||
{
|
||||
Colors.ARGB(0x00, 0x00, 0x00), // Black
|
||||
Colors.ARGB(0x00, 0x00, 0x80), // Blue
|
||||
Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue
|
||||
Colors.ARGB(0x80, 0x00, 0x00), // Red
|
||||
Colors.ARGB(0x80, 0x00, 0x80), // Magenta
|
||||
Colors.ARGB(0x80, 0x00, 0xFF), // Mauve
|
||||
Colors.ARGB(0xFF, 0x00, 0x00), // Bright Red
|
||||
Colors.ARGB(0xFF, 0x00, 0x80), // Purple
|
||||
Colors.ARGB(0xFF, 0x00, 0xFF), // Bright Magenta
|
||||
Colors.ARGB(0x00, 0x80, 0x00), // Green
|
||||
Colors.ARGB(0x00, 0x80, 0x80), // Cyan
|
||||
Colors.ARGB(0x00, 0x80, 0xFF), // Sky Blue
|
||||
Colors.ARGB(0x80, 0x80, 0x00), // Yellow
|
||||
Colors.ARGB(0x80, 0x80, 0x80), // White
|
||||
Colors.ARGB(0x80, 0x80, 0xFF), // Pastel Blue
|
||||
Colors.ARGB(0xFF, 0x80, 0x00), // Orange
|
||||
Colors.ARGB(0xFF, 0x80, 0x80), // Pink
|
||||
Colors.ARGB(0xFF, 0x80, 0xFF), // Pastel Magenta
|
||||
Colors.ARGB(0x00, 0xFF, 0x00), // Bright Green
|
||||
Colors.ARGB(0x00, 0xFF, 0x80), // Sea Green
|
||||
Colors.ARGB(0x00, 0xFF, 0xFF), // Bright Cyan
|
||||
Colors.ARGB(0x80, 0xFF, 0x00), // Lime
|
||||
Colors.ARGB(0x80, 0xFF, 0x80), // Pastel Green
|
||||
Colors.ARGB(0x80, 0xFF, 0xFF), // Pastel Cyan
|
||||
Colors.ARGB(0xFF, 0xFF, 0x00), // Bright Yellow
|
||||
Colors.ARGB(0xFF, 0xFF, 0x80), // Pastel Yellow
|
||||
Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The standard CPC Pallete (ordered by hardware #)
|
||||
/// http://www.cpcwiki.eu/index.php/CPC_Palette
|
||||
/// </summary>
|
||||
public static readonly int[] CPCHardwarePalette =
|
||||
{
|
||||
Colors.ARGB(0x80, 0x80, 0x80), // White
|
||||
Colors.ARGB(0x80, 0x80, 0x80), // White (duplicate)
|
||||
Colors.ARGB(0x00, 0xFF, 0x80), // Sea Green
|
||||
Colors.ARGB(0xFF, 0xFF, 0x80), // Pastel Yellow
|
||||
Colors.ARGB(0x00, 0x00, 0x80), // Blue
|
||||
Colors.ARGB(0xFF, 0x00, 0x80), // Purple
|
||||
Colors.ARGB(0x00, 0x80, 0x80), // Cyan
|
||||
Colors.ARGB(0xFF, 0x80, 0x80), // Pink
|
||||
Colors.ARGB(0xFF, 0x00, 0x80), // Purple (duplicate)
|
||||
Colors.ARGB(0xFF, 0xFF, 0x80), // Pastel Yellow (duplicate)
|
||||
Colors.ARGB(0xFF, 0xFF, 0x00), // Bright Yellow
|
||||
Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White
|
||||
Colors.ARGB(0xFF, 0x00, 0x00), // Bright Red
|
||||
Colors.ARGB(0xFF, 0x00, 0xFF), // Bright Magenta
|
||||
Colors.ARGB(0xFF, 0x80, 0x00), // Orange
|
||||
Colors.ARGB(0xFF, 0x80, 0xFF), // Pastel Magenta
|
||||
Colors.ARGB(0x00, 0x00, 0x80), // Blue (duplicate)
|
||||
Colors.ARGB(0x00, 0xFF, 0x80), // Sea Green (duplicate)
|
||||
Colors.ARGB(0x00, 0xFF, 0x00), // Bright Green
|
||||
Colors.ARGB(0x00, 0xFF, 0xFF), // Bright Cyan
|
||||
Colors.ARGB(0x00, 0x00, 0x00), // Black
|
||||
Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue
|
||||
Colors.ARGB(0x00, 0x80, 0x00), // Green
|
||||
Colors.ARGB(0x00, 0x80, 0xFF), // Sky Blue
|
||||
Colors.ARGB(0x80, 0x00, 0x80), // Magenta
|
||||
Colors.ARGB(0x80, 0xFF, 0x80), // Pastel Green
|
||||
Colors.ARGB(0x80, 0xFF, 0x00), // Lime
|
||||
Colors.ARGB(0x80, 0xFF, 0xFF), // Pastel Cyan
|
||||
Colors.ARGB(0x80, 0x00, 0x00), // Red
|
||||
Colors.ARGB(0x80, 0x00, 0xFF), // Mauve
|
||||
Colors.ARGB(0x80, 0x80, 0x00), // Yellow
|
||||
Colors.ARGB(0x80, 0x80, 0xFF), // Pastel Blue
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Stuff
|
||||
|
||||
/// <summary>
|
||||
/// The current scanline that is being added to
|
||||
/// (will be processed and committed to the screen buffer every HSYNC)
|
||||
/// </summary>
|
||||
public ScanLine CurrentLine;
|
||||
|
||||
/// <summary>
|
||||
/// The number of top border scanlines to ommit when rendering
|
||||
/// </summary>
|
||||
public int TopLinesToTrim = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Count of rendered scanlines this frame
|
||||
/// </summary>
|
||||
public int ScanlineCounter = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Video buffer processing
|
||||
/// </summary>
|
||||
public int[] ProcessVideoBuffer()
|
||||
{
|
||||
return ScreenBuffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up buffers and the like at the start of a frame
|
||||
/// </summary>
|
||||
public void SetupVideo()
|
||||
{
|
||||
if (BufferHeight == 576)
|
||||
return;
|
||||
|
||||
BufferWidth = 800;
|
||||
BufferHeight = 576;
|
||||
|
||||
VirtualWidth = BufferWidth / 2;
|
||||
VirtualHeight = BufferHeight / 2;
|
||||
|
||||
ScreenBuffer = new int[BufferWidth * BufferHeight];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IVideoProvider
|
||||
|
||||
/// <summary>
|
||||
/// Video output buffer
|
||||
/// </summary>
|
||||
public int[] ScreenBuffer;
|
||||
|
||||
private int _virtualWidth;
|
||||
private int _virtualHeight;
|
||||
private int _bufferWidth;
|
||||
private int _bufferHeight;
|
||||
|
||||
public int BackgroundColor
|
||||
{
|
||||
get { return CPCHardwarePalette[0]; }
|
||||
}
|
||||
|
||||
public int VirtualWidth
|
||||
{
|
||||
get { return _virtualWidth; }
|
||||
set { _virtualWidth = value; }
|
||||
}
|
||||
|
||||
public int VirtualHeight
|
||||
{
|
||||
get { return _virtualHeight; }
|
||||
set { _virtualHeight = value; }
|
||||
}
|
||||
|
||||
public int BufferWidth
|
||||
{
|
||||
get { return _bufferWidth; }
|
||||
set { _bufferWidth = value; }
|
||||
}
|
||||
|
||||
public int BufferHeight
|
||||
{
|
||||
get { return _bufferHeight; }
|
||||
set { _bufferHeight = value; }
|
||||
}
|
||||
|
||||
public int VsyncNumerator
|
||||
{
|
||||
get { return GateArray.Z80ClockSpeed * 50; }
|
||||
set { }
|
||||
}
|
||||
|
||||
public int VsyncDenominator
|
||||
{
|
||||
get { return GateArray.Z80ClockSpeed; }
|
||||
}
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
return ProcessVideoBuffer();
|
||||
}
|
||||
|
||||
public void SetupScreenSize()
|
||||
{
|
||||
BufferWidth = 1024; // 512;
|
||||
BufferHeight = 768;
|
||||
VirtualHeight = BufferHeight;
|
||||
VirtualWidth = BufferWidth;
|
||||
ScreenBuffer = new int[BufferWidth * BufferHeight];
|
||||
croppedBuffer = ScreenBuffer;
|
||||
}
|
||||
|
||||
protected int[] croppedBuffer;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialization
|
||||
|
||||
public void SyncState(Serializer ser)
|
||||
{
|
||||
ser.BeginSection("CRT");
|
||||
ser.EndSection();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single scanline buffer
|
||||
/// </summary>
|
||||
public class ScanLine
|
||||
{
|
||||
/// <summary>
|
||||
/// Array of character information
|
||||
/// </summary>
|
||||
public Character[] Characters;
|
||||
|
||||
/// <summary>
|
||||
/// The screenmode that was set at the start of this scanline
|
||||
/// </summary>
|
||||
public int ScreenMode = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The scanline number (0 based)
|
||||
/// </summary>
|
||||
public int LineIndex;
|
||||
|
||||
/// <summary>
|
||||
/// The calling CRT device
|
||||
/// </summary>
|
||||
private CRTDevice CRT;
|
||||
|
||||
public ScanLine(CRTDevice crt)
|
||||
{
|
||||
Reset();
|
||||
CRT = crt;
|
||||
}
|
||||
|
||||
// To be run after scanline has been fully processed
|
||||
public void InitScanline(int screenMode, int lineIndex)
|
||||
{
|
||||
Reset();
|
||||
ScreenMode = screenMode;
|
||||
LineIndex = lineIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a single scanline character into the matrix
|
||||
/// </summary>
|
||||
/// <param name="charIndex"></param>
|
||||
/// <param name="phase"></param>
|
||||
public void AddScanlineCharacter(int index, RenderPhase phase, byte vid1, byte vid2, int[] pens)
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case RenderPhase.BORDER:
|
||||
AddBorderValue(index, CRTDevice.CPCHardwarePalette[pens[16]]);
|
||||
break;
|
||||
case RenderPhase.DISPLAY:
|
||||
AddDisplayValue(index, vid1, vid2, pens);
|
||||
break;
|
||||
default:
|
||||
AddSyncValue(index, phase);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a HSYNC, VSYNC or HSYNC+VSYNC character into the scanline
|
||||
/// </summary>
|
||||
/// <param name="charIndex"></param>
|
||||
/// <param name="phase"></param>
|
||||
private void AddSyncValue(int charIndex, RenderPhase phase)
|
||||
{
|
||||
Characters[charIndex].Phase = phase;
|
||||
Characters[charIndex].Pixels = new int[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a border character into the scanline
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="colourValue"></param>
|
||||
private void AddBorderValue(int charIndex, int colourValue)
|
||||
{
|
||||
Characters[charIndex].Phase = RenderPhase.BORDER;
|
||||
Characters[charIndex].Pixels = new int[8];
|
||||
|
||||
for (int i = 0; i < Characters[charIndex].Pixels.Length; i++)
|
||||
{
|
||||
Characters[charIndex].Pixels[i] = colourValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a display character into the scanline
|
||||
/// Pixel matrix is calculated based on the current ScreenMode
|
||||
/// </summary>
|
||||
/// <param name="charIndex"></param>
|
||||
/// <param name="vid1"></param>
|
||||
/// <param name="vid2"></param>
|
||||
public void AddDisplayValue(int charIndex, byte vid1, byte vid2, int[] pens)
|
||||
{
|
||||
Characters[charIndex].Phase = RenderPhase.DISPLAY;
|
||||
|
||||
// generate pixels based on screen mode
|
||||
switch (ScreenMode)
|
||||
{
|
||||
// 4 bits per pixel - 2 bytes - 4 pixels (8 CRT pixels)
|
||||
case 0:
|
||||
Characters[charIndex].Pixels = new int[4];
|
||||
|
||||
int m0Count = 0;
|
||||
|
||||
int m0B0P0i = vid1 & 170;
|
||||
int m0B0P0 = ((m0B0P0i & 0x80) >> 7) | ((m0B0P0i & 0x08) >> 2) | ((m0B0P0i & 0x20) >> 3) | ((m0B0P0i & 0x02 << 2));
|
||||
int m0B0P1i = vid1 & 85;
|
||||
int m0B0P1 = ((m0B0P1i & 0x40) >> 6) | ((m0B0P1i & 0x04) >> 1) | ((m0B0P1i & 0x10) >> 2) | ((m0B0P1i & 0x01 << 3));
|
||||
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[m0B0P0]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[m0B0P1]];
|
||||
|
||||
int m0B1P0i = vid1 & 170;
|
||||
int m0B1P0 = ((m0B1P0i & 0x80) >> 7) | ((m0B1P0i & 0x08) >> 2) | ((m0B1P0i & 0x20) >> 3) | ((m0B1P0i & 0x02 << 2));
|
||||
int m0B1P1i = vid1 & 85;
|
||||
int m0B1P1 = ((m0B1P1i & 0x40) >> 6) | ((m0B1P1i & 0x04) >> 1) | ((m0B1P1i & 0x10) >> 2) | ((m0B1P1i & 0x01 << 3));
|
||||
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[m0B1P0]];
|
||||
Characters[charIndex].Pixels[m0Count++] = CRTDevice.CPCHardwarePalette[pens[m0B1P1]];
|
||||
break;
|
||||
|
||||
// 2 bits per pixel - 2 bytes - 8 pixels (16 CRT pixels)
|
||||
case 1:
|
||||
Characters[charIndex].Pixels = new int[8];
|
||||
|
||||
int m1Count = 0;
|
||||
|
||||
int m1B0P0 = (((vid1 & 0x80) >> 7) | ((vid1 & 0x08) >> 2));
|
||||
int m1B0P1 = (((vid1 & 0x40) >> 6) | ((vid1 & 0x04) >> 1));
|
||||
int m1B0P2 = (((vid1 & 0x20) >> 5) | ((vid1 & 0x02)));
|
||||
int m1B0P3 = (((vid1 & 0x10) >> 4) | ((vid1 & 0x01) << 1));
|
||||
|
||||
Characters[charIndex].Pixels[m1Count++] = CRTDevice.CPCHardwarePalette[pens[m1B0P0]];
|
||||
Characters[charIndex].Pixels[m1Count++] = CRTDevice.CPCHardwarePalette[pens[m1B0P1]];
|
||||
Characters[charIndex].Pixels[m1Count++] = CRTDevice.CPCHardwarePalette[pens[m1B0P2]];
|
||||
Characters[charIndex].Pixels[m1Count++] = CRTDevice.CPCHardwarePalette[pens[m1B0P3]];
|
||||
|
||||
int m1B1P0 = (((vid2 & 0x80) >> 7) | ((vid2 & 0x08) >> 2));
|
||||
int m1B1P1 = (((vid2 & 0x40) >> 6) | ((vid2 & 0x04) >> 1));
|
||||
int m1B1P2 = (((vid2 & 0x20) >> 5) | ((vid2 & 0x02)));
|
||||
int m1B1P3 = (((vid2 & 0x10) >> 4) | ((vid2 & 0x01) << 1));
|
||||
|
||||
Characters[charIndex].Pixels[m1Count++] = CRTDevice.CPCHardwarePalette[pens[m1B1P0]];
|
||||
Characters[charIndex].Pixels[m1Count++] = CRTDevice.CPCHardwarePalette[pens[m1B1P1]];
|
||||
Characters[charIndex].Pixels[m1Count++] = CRTDevice.CPCHardwarePalette[pens[m1B1P2]];
|
||||
Characters[charIndex].Pixels[m1Count++] = CRTDevice.CPCHardwarePalette[pens[m1B1P3]];
|
||||
break;
|
||||
|
||||
// 1 bit per pixel - 2 bytes - 16 pixels (32 CRT pixels)
|
||||
case 2:
|
||||
Characters[charIndex].Pixels = new int[16];
|
||||
|
||||
int m2Count = 0;
|
||||
|
||||
for (int bit = 7; bit >= 0; bit--)
|
||||
{
|
||||
int val = vid1.Bit(bit) ? 1 : 0;
|
||||
Characters[charIndex].Pixels[m2Count++] = CRTDevice.CPCHardwarePalette[pens[val]];
|
||||
}
|
||||
|
||||
for (int bit = 7; bit >= 0; bit--)
|
||||
{
|
||||
int val = vid2.Bit(bit) ? 1 : 0;
|
||||
Characters[charIndex].Pixels[m2Count++] = CRTDevice.CPCHardwarePalette[pens[val]];
|
||||
}
|
||||
break;
|
||||
|
||||
// 4 bits per pixel - 2 bytes - 4 pixels (8 CRT pixels)
|
||||
case 3:
|
||||
Characters[charIndex].Pixels = new int[4];
|
||||
|
||||
int m3Count = 0;
|
||||
|
||||
int m3B0P0i = vid1 & 170;
|
||||
int m3B0P0 = ((m3B0P0i & 0x80) >> 7) | ((m3B0P0i & 0x08) >> 2) | ((m3B0P0i & 0x20) >> 3) | ((m3B0P0i & 0x02 << 2));
|
||||
int m3B0P1i = vid1 & 85;
|
||||
int m3B0P1 = ((m3B0P1i & 0x40) >> 6) | ((m3B0P1i & 0x04) >> 1) | ((m3B0P1i & 0x10) >> 2) | ((m3B0P1i & 0x01 << 3));
|
||||
|
||||
Characters[charIndex].Pixels[m3Count++] = CRTDevice.CPCHardwarePalette[pens[m3B0P0]];
|
||||
Characters[charIndex].Pixels[m3Count++] = CRTDevice.CPCHardwarePalette[pens[m3B0P1]];
|
||||
|
||||
int m3B1P0i = vid1 & 170;
|
||||
int m3B1P0 = ((m3B1P0i & 0x80) >> 7) | ((m3B1P0i & 0x08) >> 2) | ((m3B1P0i & 0x20) >> 3) | ((m3B1P0i & 0x02 << 2));
|
||||
int m3B1P1i = vid1 & 85;
|
||||
int m3B1P1 = ((m3B1P1i & 0x40) >> 6) | ((m3B1P1i & 0x04) >> 1) | ((m3B1P1i & 0x10) >> 2) | ((m3B1P1i & 0x01 << 3));
|
||||
|
||||
Characters[charIndex].Pixels[m3Count++] = CRTDevice.CPCHardwarePalette[pens[m3B1P0]];
|
||||
Characters[charIndex].Pixels[m3Count++] = CRTDevice.CPCHardwarePalette[pens[m3B1P1]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of pixels decoded in this scanline (border and display)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private int GetPixelCount()
|
||||
{
|
||||
int cnt = 0;
|
||||
|
||||
foreach (var c in Characters)
|
||||
{
|
||||
if (c.Pixels != null)
|
||||
cnt += c.Pixels.Length;
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called at the start of HSYNC
|
||||
/// Processes and adds the scanline to the Screen Buffer
|
||||
/// </summary>
|
||||
public void CommitScanline()
|
||||
{
|
||||
int hPix = GetPixelCount() * 2;
|
||||
int leftOver = CRT.BufferWidth - hPix;
|
||||
int lPad = leftOver / 2;
|
||||
int rPad = lPad;
|
||||
int rem = leftOver % 2;
|
||||
if (rem != 0)
|
||||
rPad += rem;
|
||||
|
||||
if (LineIndex < CRT.TopLinesToTrim)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// render out the scanline
|
||||
int pCount = (LineIndex - CRT.TopLinesToTrim) * 2 * CRT.BufferWidth;
|
||||
|
||||
// double up
|
||||
for (int s = 0; s < 2; s++)
|
||||
{
|
||||
// left padding
|
||||
for (int lP = 0; lP < lPad; lP++)
|
||||
{
|
||||
CRT.ScreenBuffer[pCount++] = 0;
|
||||
}
|
||||
|
||||
// border and display
|
||||
foreach (var c in Characters)
|
||||
{
|
||||
if (c.Pixels == null || c.Pixels.Length == 0)
|
||||
continue;
|
||||
|
||||
for (int p = 0; p < c.Pixels.Length; p++)
|
||||
{
|
||||
CRT.ScreenBuffer[pCount++] = c.Pixels[p];
|
||||
CRT.ScreenBuffer[pCount++] = c.Pixels[p];
|
||||
}
|
||||
}
|
||||
|
||||
// right padding
|
||||
for (int rP = 0; rP < rPad; rP++)
|
||||
{
|
||||
CRT.ScreenBuffer[pCount++] = 0;
|
||||
}
|
||||
|
||||
if (pCount != hPix)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CRT.ScanlineCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ScreenMode = 1;
|
||||
Characters = new Character[64];
|
||||
|
||||
for (int i = 0; i < Characters.Length; i++)
|
||||
{
|
||||
Characters[i] = new Character();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains data relating to one character written on one scanline
|
||||
/// </summary>
|
||||
public class Character
|
||||
{
|
||||
/// <summary>
|
||||
/// Array of pixels generated for this character
|
||||
/// </summary>
|
||||
public int[] Pixels;
|
||||
|
||||
/// <summary>
|
||||
/// The type (NONE/BORDER/DISPLAY/HSYNC/VSYNC/HSYNC+VSYNC
|
||||
/// </summary>
|
||||
public RenderPhase Phase = RenderPhase.NONE;
|
||||
|
||||
public Character()
|
||||
{
|
||||
Pixels = new int[0];
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum RenderPhase : int
|
||||
{
|
||||
/// <summary>
|
||||
/// Nothing
|
||||
/// </summary>
|
||||
NONE = 0,
|
||||
/// <summary>
|
||||
/// Border is being rendered
|
||||
/// </summary>
|
||||
BORDER = 1,
|
||||
/// <summary>
|
||||
/// Display rendered from video RAM
|
||||
/// </summary>
|
||||
DISPLAY = 2,
|
||||
/// <summary>
|
||||
/// HSYNC in progress
|
||||
/// </summary>
|
||||
HSYNC = 3,
|
||||
/// <summary>
|
||||
/// VSYNC in process
|
||||
/// </summary>
|
||||
VSYNC = 4,
|
||||
/// <summary>
|
||||
/// HSYNC occurs within a VSYNC
|
||||
/// </summary>
|
||||
HSYNCandVSYNC = 5
|
||||
}
|
||||
}
|
|
@ -213,7 +213,10 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
return _keyboard.ReadCurrentLine();
|
||||
}
|
||||
|
||||
return _registers[_activeRegister];
|
||||
if (_activeRegister < 16)
|
||||
return _registers[_activeRegister];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
FrameLength = 79872;
|
||||
|
||||
CRCT = new CRCT_6845(CRCT_6845.CRCTType.Motorola_MC6845, this);
|
||||
CRT = new CRTDevice(this);
|
||||
GateArray = new AmstradGateArray(this, AmstradGateArray.GateArrayType.Amstrad40007);
|
||||
PPI = new PPI_8255(this);
|
||||
|
||||
|
|
|
@ -94,9 +94,35 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
/// <returns></returns>
|
||||
public virtual byte FetchScreenMemory(ushort addr)
|
||||
{
|
||||
var value = ReadBus((ushort)((addr & 0x3FFF) + 0x4000));
|
||||
//var value = ReadBus(addr);
|
||||
return value;
|
||||
int divisor = addr / 0x4000;
|
||||
byte result = 0xff;
|
||||
|
||||
switch (divisor)
|
||||
{
|
||||
// 0x000
|
||||
case 0:
|
||||
result = RAM0[addr % 0x4000];
|
||||
break;
|
||||
|
||||
// 0x4000
|
||||
case 1:
|
||||
result = RAM1[addr % 0x4000];
|
||||
break;
|
||||
|
||||
// 0x8000
|
||||
case 2:
|
||||
result = RAM2[addr % 0x4000];
|
||||
break;
|
||||
|
||||
// 0xc000 or UpperROM
|
||||
case 3:
|
||||
result = RAM3[addr % 0x4000];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -62,6 +62,11 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
/// </summary>
|
||||
public AmstradGateArray GateArray { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Renders pixels to the screen
|
||||
/// </summary>
|
||||
public CRTDevice CRT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The PPI contoller chip
|
||||
/// </summary>
|
||||
|
@ -135,7 +140,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
public virtual void ExecuteFrame(bool render, bool renderSound)
|
||||
{
|
||||
GateArray.FrameEnd = false;
|
||||
//ULADevice.ULACycleCounter = CurrentFrameCycle;
|
||||
|
||||
InputRead = false;
|
||||
_render = render;
|
||||
|
@ -154,11 +158,16 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
|
||||
PollInput();
|
||||
|
||||
GateArray.SetupVideo();
|
||||
CRT.SetupVideo();
|
||||
CRT.ScanlineCounter = 0;
|
||||
|
||||
while (!GateArray.FrameEnd)
|
||||
{
|
||||
GateArray.DoCycles();
|
||||
GateArray.ClockCycle();
|
||||
|
||||
// cycle the tape device
|
||||
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
|
||||
TapeDevice.TapeCycle();
|
||||
}
|
||||
|
||||
OverFlow = (int)CurrentFrameCycle - GateArray.FrameLength;
|
||||
|
|
Loading…
Reference in New Issue