diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
index f79ac13425..b8953a87ee 100644
--- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
+++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
@@ -139,7 +139,7 @@
-
+
@@ -149,7 +149,8 @@
-
+
+
diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs
index 6bc915c296..8b7852812f 100644
--- a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs
+++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.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()
{
diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/AmstradCPC.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/AmstradCPC.cs
index f1c17b0a10..18c0270eb6 100644
--- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/AmstradCPC.cs
+++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/AmstradCPC.cs
@@ -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(_tracer);
ser.Register(_cpu);
- ser.Register(_machine.GateArray);
+ ser.Register(_machine.CRT);
// initialize sound mixer and attach the various ISoundProvider devices
SoundMixer = new SoundProviderMixer((int)(32767 / 10), "Tape Audio", (ISoundProvider)_machine.TapeBuzzer);
diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/GateArray/AmstradGateArray.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/AmstradGateArray.cs
similarity index 65%
rename from BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/GateArray/AmstradGateArray.cs
rename to BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/AmstradGateArray.cs
index 3dfedd0464..390c9047e2 100644
--- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/GateArray/AmstradGateArray.cs
+++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/AmstradGateArray.cs
@@ -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
///
- 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
-
- ///
- /// The standard CPC Pallete (ordered by firmware #)
- /// http://www.cpcwiki.eu/index.php/CPC_Palette
- ///
- 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
- };
-
- ///
- /// The standard CPC Pallete (ordered by hardware #)
- /// http://www.cpcwiki.eu/index.php/CPC_Palette
- ///
- 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
}
///
- /// 0-16: Pen Registers
- /// 17: Border Colour
+ /// 0-15: Pen Registers
+ /// 16: Border Colour
///
- private int[] ColourRegisters = new int[17];
+ public int[] ColourRegisters = new int[17];
///
/// 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
}
///
- /// The selected screen mode
+ /// The selected screen mode (updated after every HSYNC)
///
private int ScreenMode;
@@ -344,12 +274,6 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
}
}
- ///
- /// Video mode change is syncronised with HSYNC. When the mode is change it takes effect
- /// from the next HSYNC
- ///
- private int LatchedMode;
-
///
/// Set when the HSYNC signal is detected from the CRCT
///
@@ -383,16 +307,34 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
private int InterruptHoldCounter;
///
- /// 2 state field
- /// Because the renderer outputs 1 pixel for every 2 GA cycles
+ /// Set at the start of a new frame
///
- private bool RendererFlipFlop = true;
+ public bool IsNewFrame;
///
- /// Used for counting the screen buffer positions
+ /// Set when a new line is beginning
///
- private int RenderCounter;
+ public bool IsNewLine;
+ ///
+ /// Horizontal Character Counter
+ ///
+ private int HCC;
+
+ ///
+ /// Vertical Line Counter
+ ///
+ private int VLC;
+
+ ///
+ /// The first video byte fetched
+ ///
+ private byte VideoByte1;
+
+ ///
+ /// The second video byte fetched
+ ///
+ 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
}
///
- /// Performs one gate array (rendering) cycle
+ /// Handles interrupt generation
///
- 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;
+ }
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// Fetches a video RAM byte
+ /// This happens at 2Mhz when a memory fetch is due
+ ///
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// 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
+ ///
+ 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;
-
- ///
- /// Handles interrupt generation
- ///
- 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
///
- /// 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)
///
- 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
- {
-
- }
-
///
- /// 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
///
- public void SetupVideo()
+ public void IORQA()
{
-
+ // bit 5 of the interrupt counter is reset
+ InterruptCounter &= ~(1 << 5);
}
#endregion
-
-
#region IPortIODevice
///
@@ -856,101 +755,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
#endregion
- #region IVideoProvider
-
- ///
- /// Video output buffer
- ///
- 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();
-
- /*
- * ///
- /// 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
- ///
- private bool ;
-
- ///
- /// Used to count HSYNCs during a VSYNC
- ///
- private int ;
- * */
}
#endregion
- #region Enums
+ #region Enums & Classes
public enum GateArrayType
{
diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/CRCT/CRCT_6845.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRCT_6845.cs
similarity index 94%
rename from BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/CRCT/CRCT_6845.cs
rename to BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRCT_6845.cs
index fc7f085359..8fc9d0d902 100644
--- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/CRCT/CRCT_6845.cs
+++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRCT_6845.cs
@@ -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
}
}
+ ///
+ /// Returns the starting video page address as specified within R12
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// Returns the video buffer size as specified within R12
+ ///
+ public int VideoBufferSize
+ {
+ get
+ {
+ if (Regs[12].Bit(3) && Regs[12].Bit(2))
+ return 0x8000;
+
+ return 0x4000;
+ }
+ }
+
+
+ /* Easier memory functions */
+
+ ///
+ /// The current byte address
+ ///
+ public ushort CurrentByteAddress;
+
+ ///
+ /// ByteCOunter
+ ///
+ 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)
diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRTDevice.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRTDevice.cs
new file mode 100644
index 0000000000..ed963b6a12
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/Display/CRTDevice.cs
@@ -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
+{
+ ///
+ /// Render pixels to the screen
+ ///
+ 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
+
+ ///
+ /// The standard CPC Pallete (ordered by firmware #)
+ /// http://www.cpcwiki.eu/index.php/CPC_Palette
+ ///
+ 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
+ };
+
+ ///
+ /// The standard CPC Pallete (ordered by hardware #)
+ /// http://www.cpcwiki.eu/index.php/CPC_Palette
+ ///
+ 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
+
+ ///
+ /// The current scanline that is being added to
+ /// (will be processed and committed to the screen buffer every HSYNC)
+ ///
+ public ScanLine CurrentLine;
+
+ ///
+ /// The number of top border scanlines to ommit when rendering
+ ///
+ public int TopLinesToTrim = 20;
+
+ ///
+ /// Count of rendered scanlines this frame
+ ///
+ public int ScanlineCounter = 0;
+
+ ///
+ /// Video buffer processing
+ ///
+ public int[] ProcessVideoBuffer()
+ {
+ return ScreenBuffer;
+ }
+
+ ///
+ /// Sets up buffers and the like at the start of a frame
+ ///
+ 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
+
+ ///
+ /// Video output buffer
+ ///
+ 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
+ }
+
+ ///
+ /// Represents a single scanline buffer
+ ///
+ public class ScanLine
+ {
+ ///
+ /// Array of character information
+ ///
+ public Character[] Characters;
+
+ ///
+ /// The screenmode that was set at the start of this scanline
+ ///
+ public int ScreenMode = 1;
+
+ ///
+ /// The scanline number (0 based)
+ ///
+ public int LineIndex;
+
+ ///
+ /// The calling CRT device
+ ///
+ 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;
+ }
+
+ ///
+ /// Adds a single scanline character into the matrix
+ ///
+ ///
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// Adds a HSYNC, VSYNC or HSYNC+VSYNC character into the scanline
+ ///
+ ///
+ ///
+ private void AddSyncValue(int charIndex, RenderPhase phase)
+ {
+ Characters[charIndex].Phase = phase;
+ Characters[charIndex].Pixels = new int[0];
+ }
+
+ ///
+ /// Adds a border character into the scanline
+ ///
+ ///
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// Adds a display character into the scanline
+ /// Pixel matrix is calculated based on the current ScreenMode
+ ///
+ ///
+ ///
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// Returns the number of pixels decoded in this scanline (border and display)
+ ///
+ ///
+ private int GetPixelCount()
+ {
+ int cnt = 0;
+
+ foreach (var c in Characters)
+ {
+ if (c.Pixels != null)
+ cnt += c.Pixels.Length;
+ }
+
+ return cnt;
+ }
+
+ ///
+ /// Called at the start of HSYNC
+ /// Processes and adds the scanline to the Screen Buffer
+ ///
+ 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();
+ }
+ }
+ }
+
+ ///
+ /// Contains data relating to one character written on one scanline
+ ///
+ public class Character
+ {
+ ///
+ /// Array of pixels generated for this character
+ ///
+ public int[] Pixels;
+
+ ///
+ /// The type (NONE/BORDER/DISPLAY/HSYNC/VSYNC/HSYNC+VSYNC
+ ///
+ public RenderPhase Phase = RenderPhase.NONE;
+
+ public Character()
+ {
+ Pixels = new int[0];
+ }
+ }
+
+ [Flags]
+ public enum RenderPhase : int
+ {
+ ///
+ /// Nothing
+ ///
+ NONE = 0,
+ ///
+ /// Border is being rendered
+ ///
+ BORDER = 1,
+ ///
+ /// Display rendered from video RAM
+ ///
+ DISPLAY = 2,
+ ///
+ /// HSYNC in progress
+ ///
+ HSYNC = 3,
+ ///
+ /// VSYNC in process
+ ///
+ VSYNC = 4,
+ ///
+ /// HSYNC occurs within a VSYNC
+ ///
+ HSYNCandVSYNC = 5
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/SoundOutput/AY38912.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/SoundOutput/AY38912.cs
index fff45153f0..c2aea3a75f 100644
--- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/SoundOutput/AY38912.cs
+++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Hardware/SoundOutput/AY38912.cs
@@ -213,7 +213,10 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
return _keyboard.ReadCurrentLine();
}
- return _registers[_activeRegister];
+ if (_activeRegister < 16)
+ return _registers[_activeRegister];
+
+ return 0;
}
///
diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPC464/CPC464.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPC464/CPC464.cs
index 4d199f00e3..cfe5fed334 100644
--- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPC464/CPC464.cs
+++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPC464/CPC464.cs
@@ -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);
diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.Memory.cs
index d5281943ff..4db61a5da3 100644
--- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.Memory.cs
+++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.Memory.cs
@@ -94,9 +94,35 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
///
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
diff --git a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.cs b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.cs
index b72690e174..09c858c239 100644
--- a/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.cs
+++ b/BizHawk.Emulation.Cores/Computers/AmstradCPC/Machine/CPCBase.cs
@@ -62,6 +62,11 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
///
public AmstradGateArray GateArray { get; set; }
+ ///
+ /// Renders pixels to the screen
+ ///
+ public CRTDevice CRT { get; set; }
+
///
/// The PPI contoller chip
///
@@ -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;