From 0d6c2c21d4423fad32a5877dae0ac26d7c20fe9e Mon Sep 17 00:00:00 2001 From: Ian C Date: Mon, 1 Oct 2012 20:54:16 +0000 Subject: Last check-in before we abandon a "real" ULA emulation and just cheat! --- WPZX81/WPZX81/SplashScreenImage.jpg | Bin 93849 -> 97272 bytes WPZX81/WPZX81/ZX81/Emulation.cs | 129 +++++++++++++++++++++++++++++++----- 2 files changed, 114 insertions(+), 15 deletions(-) diff --git a/WPZX81/WPZX81/SplashScreenImage.jpg b/WPZX81/WPZX81/SplashScreenImage.jpg index 33ebf76..a13be9d 100644 Binary files a/WPZX81/WPZX81/SplashScreenImage.jpg and b/WPZX81/WPZX81/SplashScreenImage.jpg 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 times /// elements. /// - public ushort[] Screen {get; private set;} + public ushort[] Screen {get {return display.Screen;}} #endregion @@ -271,8 +337,10 @@ namespace WPZX81.ZX81 /// public void Run() { - vsyncNoted = false; + display.Reset(); + Z80.ExecutedInstructions = 0; Z80.Run(OperationCallback); + Debug.WriteLine("Executed instructions {0}", Z80.ExecutedInstructions); } /// @@ -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, -- cgit v1.2.3