summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--WPZX81/WPZX81/SplashScreenImage.jpgbin93849 -> 97272 bytes
-rw-r--r--WPZX81/WPZX81/ZX81/Emulation.cs129
2 files changed, 114 insertions, 15 deletions
diff --git a/WPZX81/WPZX81/SplashScreenImage.jpg b/WPZX81/WPZX81/SplashScreenImage.jpg
index 33ebf76..a13be9d 100644
--- a/WPZX81/WPZX81/SplashScreenImage.jpg
+++ b/WPZX81/WPZX81/SplashScreenImage.jpg
Binary files differ
diff --git a/WPZX81/WPZX81/ZX81/Emulation.cs b/WPZX81/WPZX81/ZX81/Emulation.cs
index 5c3e01e..f7a851a 100644
--- a/WPZX81/WPZX81/ZX81/Emulation.cs
+++ b/WPZX81/WPZX81/ZX81/Emulation.cs
@@ -33,6 +33,61 @@ namespace WPZX81.ZX81
{
#region Private data
+ private class Display
+ {
+ private ushort[] screen;
+ private int x;
+ private int y;
+ private int w;
+ private int h;
+
+ public Display(int w, int h)
+ {
+ this.screen = new ushort[w * h];
+ this.w = w;
+ this.h = h;
+ this.x = 0;
+ this.y = 0;
+ }
+
+ public ushort[] Screen {get {return screen;}}
+
+ private int count;
+ private int invalid;
+
+ public void Reset()
+ {
+ Debug.WriteLine("Display: {0} writes done ({1} invalid). Ended up at {2},{3} out of {4}, {5}", count, invalid, x, y, w, h);
+ count = 0;
+ invalid = 0;
+
+ x = 0;
+ y = 0;
+
+ this.screen = new ushort[w * h];
+ }
+
+ public void Write(ushort v)
+ {
+ if (x < w)
+ {
+ screen[x++ + y * w] = v;
+ }
+ else
+ {
+ invalid++;
+ }
+
+ count++;
+ }
+
+ public void NextLine()
+ {
+ x = 0;
+ y = (y + 1) % h;
+ }
+ }
+
private Clock clock;
private byte[] mem;
private int vsync;
@@ -43,6 +98,8 @@ namespace WPZX81.ZX81
private int rambot;
private int ramtop;
private byte[] matrix;
+ private int lastR6;
+ private Display display;
// Some system variables
//
@@ -186,7 +243,6 @@ namespace WPZX81.ZX81
}
}
-
private bool OperationCallback(ICpu sender)
{
// During VSYNC, keep resetting the clock
@@ -213,21 +269,31 @@ namespace WPZX81.ZX81
// Maskable interupts are tied to bit 6 of the refresh register.
//
- if ((Z80.Refresh | 0x20) == 0)
+ if ((Z80.Refresh & 0x40) == 0) // && lastR6 == 0x20)
{
Z80.MaskableInterrupt(0xff);
+ display.NextLine();
+ lineCounter = (lineCounter + 1) % 8;
}
- // The frame finishes when VSYNC is triggered (ie. HYSNC is off). Note we wait until we've had a good few opcodes with VSYNC happening.
+ // Record last bit 6 of the refresh register
+ //
+ lastR6 = Z80.Refresh & 0x20;
+
+ // The frame finishes when VSYNC is triggered (ie. HYSNC is off).
+ // Note we wait until we've had a good few opcodes with VSYNC happening as some hi-res routines trigger short blasts of VSYNC.
//
- if (vsync > 10 && !vsyncNoted)
+ if (vsync > 20 && !vsyncNoted)
{
+ display.Reset();
vsyncNoted = true;
- return true;
+ return false;
}
else
{
- return false;
+ // Make sure we're not in a state where good operations aren't happening
+ //
+ return (Z80.ExecutedInstructions < 65000);
}
}
@@ -260,7 +326,7 @@ namespace WPZX81.ZX81
/// This array must have <see cref="Width"/> times <see cref="Height"/>
/// elements.
/// </summary>
- public ushort[] Screen {get; private set;}
+ public ushort[] Screen {get {return display.Screen;}}
#endregion
@@ -271,8 +337,10 @@ namespace WPZX81.ZX81
/// </summary>
public void Run()
{
- vsyncNoted = false;
+ display.Reset();
+ Z80.ExecutedInstructions = 0;
Z80.Run(OperationCallback);
+ Debug.WriteLine("Executed instructions {0}", Z80.ExecutedInstructions);
}
/// <summary>
@@ -301,11 +369,13 @@ namespace WPZX81.ZX81
hsync = false;
vsyncNoted = false;
nmiGenerator = false;
+ lastR6 = 0;
+ lineCounter = 0;
rambot = Settings.StaticRamAt0x2000 ? 0x2000 : 0x4000;
ramtop = Settings.RamPack ? 0x8000 : 0x4400;
- new Random().NextBytes(mem);
+ mem.Initialize();
for(int f = 0 ; f < 8; f++)
{
@@ -340,7 +410,7 @@ namespace WPZX81.ZX81
{
matrix = new byte[8];
mem = new byte[0x10000];
- Screen = new ushort[Width * Height];
+ display = new Display(Width, Height);
clock = new Clock(16000, 50, 1, 50);
Z80 = new Z80Cpu();
Z80.Initialise(this, this, clock);
@@ -354,20 +424,47 @@ namespace WPZX81.ZX81
byte IMemory.Read(ushort address)
{
- // Reads above 0x8000 are grabbed by the ULA to generate the display.
+ // Reads above 0x8000 are grabbed by the ULA to generate the display. We only do this if the hysnc generator is enabled.
//
- if (address >= 0x8000)
+ if (address >= 0x8000 && hsync)
{
byte b = mem[address & 0x7fff];
- // Opcodes with bit 6 are still passed to the CPU
+ // Opcodes with bit 6 are still passed to the CPU.
//
if ((b & 0x20) == 0x20)
{
+ for(int pixel = 0; pixel < 8; pixel++)
+ {
+ display.Write(0xffff);
+ }
+
return b;
}
else
{
+ int inv = (b & 0x80);
+ b |= 0x3f;
+
+ int bitmap = mem[(int)Z80.InterruptVector << 8 | (int)b << 3 | lineCounter];
+
+ if (inv != 0)
+ {
+ bitmap = ~bitmap;
+ }
+
+ for(int pixel = 7; pixel >= 0; pixel--)
+ {
+ if ((bitmap & (1 << pixel)) == 0)
+ {
+ display.Write(0xffff);
+ }
+ else
+ {
+ display.Write(0);
+ }
+ }
+
// Return NOP
//
return 0;
@@ -375,7 +472,7 @@ namespace WPZX81.ZX81
}
else
{
- return mem[address & 0x7fff];
+ return mem[address];
}
}
@@ -407,7 +504,8 @@ namespace WPZX81.ZX81
// Rather than just a boolean, we not how many opcode VSYNC has been active. This stops us getting
// confused by IN A,(FE) / OUT (FF),A pairs used to reset the ULA LINECTR.
//
- vsync = 0;
+ vsync = 1;
+ vsyncNoted = false;
if (!nmiGenerator)
{
@@ -470,6 +568,7 @@ namespace WPZX81.ZX81
//
case 0xfe:
nmiGenerator = true;
+ clock.CycleCount = 0;
break;
// The ZX81 sends an OUT FF,A after the VSYNC to start HYSNC generation,