From 9b1051ab7efdb2012401c0c5ac397a9ed97a6fd2 Mon Sep 17 00:00:00 2001 From: Ian C Date: Tue, 20 Mar 2012 23:46:19 +0000 Subject: Non-working development checkin. Need to investigate whether the timing of the number opcodes per frame is way off. --- WPZX81/WPZX81/GamePage.xaml.cs | 8 + WPZX81/WPZX81/ZX81/Emulation.cs | 346 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 326 insertions(+), 28 deletions(-) diff --git a/WPZX81/WPZX81/GamePage.xaml.cs b/WPZX81/WPZX81/GamePage.xaml.cs index 2741945..26b0324 100644 --- a/WPZX81/WPZX81/GamePage.xaml.cs +++ b/WPZX81/WPZX81/GamePage.xaml.cs @@ -12,6 +12,7 @@ using Microsoft.Phone.Controls; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; +using System.Diagnostics; namespace WPZX81 { @@ -64,8 +65,15 @@ namespace WPZX81 /// private void OnDraw(object sender, GameTimerEventArgs e) { + Stopwatch s = Stopwatch.StartNew(); + SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.Black); + spriteBatch.Begin(); Shared.ZX81.UpdateDisplay(spriteBatch); + spriteBatch.End(); + + s.Stop(); + Debug.WriteLine("*** RENDER {0} msec {1} ticks", s.ElapsedMilliseconds, s.ElapsedTicks); } } } \ No newline at end of file diff --git a/WPZX81/WPZX81/ZX81/Emulation.cs b/WPZX81/WPZX81/ZX81/Emulation.cs index 9620052..7e6bbbf 100644 --- a/WPZX81/WPZX81/ZX81/Emulation.cs +++ b/WPZX81/WPZX81/ZX81/Emulation.cs @@ -4,6 +4,8 @@ using Noddybox.Emulation.EightBit; using Noddybox.Emulation.EightBit.Z80; using Microsoft.Xna.Framework.Graphics; using System.Windows.Resources; +using Microsoft.Xna.Framework; +using System.Diagnostics; namespace WPZX81.ZX81 { @@ -18,18 +20,25 @@ namespace WPZX81.ZX81 private byte[] mem; private Z80Cpu z80; private bool fast; + private bool started; + private bool waitkey; private int lastI; private int rambot; private int ramtop; + private byte[] matrix; + private uint prev_last_key1; + private uint prev_last_key2; + private Texture2D[] font; // Some system variables // - private const int E_LINE = 16404; - private const int LASTK1 = 16421; - private const int LASTK2 = 16422; - private const int MARGIN = 16424; - private const int FRAMES = 16436; - private const int CDFLAG = 16443; + private const ushort E_LINE = 16404; + private const ushort LASTK1 = 16421; + private const ushort LASTK2 = 16422; + private const ushort MARGIN = 16424; + private const ushort FRAMES = 16436; + private const ushort CDFLAG = 16443; + private const ushort D_FILE = 16396; // ED Hooks // @@ -78,14 +87,91 @@ namespace WPZX81.ZX81 #region Private methods + /// + /// Reads a 16-bit word from memory. + /// + /// The address to start reading from. + /// The returned word. + private ushort PeekWord(ushort addr) + { + Register16 r = new Register16(0); + + r.low = mem[addr++]; + r.high = mem[addr]; + + return r.reg; + } + + /// + /// Write a 16-bit word to memory. + /// + /// The address to start writing to. + /// The value to write. + private void PokeWord(ushort addr, ushort val) + { + Register16 r = new Register16(val); + + mem[addr++] = r.low; + mem[addr] = r.high; + } + /// /// Loads the font from the ZX81 according to the I register. /// private void LoadFont() { - } + ushort[] data = new ushort[64]; + Register16 addr = new Register16(0); + + addr.high = z80.InterruptVector; + + Debug.WriteLine("Loading font from 0x{0:X4}", addr.reg); + + for(int f = 0 ; f < 64; f++) + { + if (font[f] != null) + { + font[f].Dispose(); + } + font[f] = new Texture2D(SharedGraphicsDeviceManager.Current.GraphicsDevice, 8, 8, false, SurfaceFormat.Bgr565); + font[f+128] = new Texture2D(SharedGraphicsDeviceManager.Current.GraphicsDevice, 8, 8, false, SurfaceFormat.Bgr565); + int i = 0; + + for(int r = 0 ; r < 8; r++) + { + for(int b = 128; b > 0; b /= 2) + { + if ((mem[addr.reg] & b) != 0) + { + data[i++] = ushort.MaxValue; + } + else + { + data[i++] = ushort.MinValue; + } + } + + addr.reg++; + } + + font[f].SetData(data); + + for(i = 0; i < 64; i++) + { + data[i] ^= ushort.MaxValue; + } + + font[f+128].SetData(data); + } + } + + /// + /// Patches a route to an address in memory. + /// + /// The starting address to patch. + /// The patch as an array of bytes. private void Patch(ushort address, byte[] patch) { foreach (byte b in patch) @@ -126,18 +212,24 @@ namespace WPZX81.ZX81 switch (e.Opcode) { case ED_SAVE: + // TODO break; case ED_LOAD: + // TODO break; case ED_WAITKEY: + waitkey = true; + started = true; break; case ED_ENDWAITKEY: + waitkey = false; break; case ED_PAUSE: + // TODO break; default: @@ -145,34 +237,87 @@ namespace WPZX81.ZX81 } } - #endregion - - #region IMemory Members - byte IMemory.Read(ushort address) + /// + /// Do various housekeeping of the system variables which we do ourselves due the patching. + /// + private void Housekeeping() { - return mem[address]; - } + // British ZX81 + // + mem[MARGIN] = 55; - void IMemory.Write(ushort address, byte value) - { - if (address >= rambot && address <= ramtop) + // Update FRAMES + // + if (!fast) { - mem[address] = value; + ushort frame = PeekWord(FRAMES); + frame &= 0x7fff; + + if (frame > 0) + { + frame--; + } + + frame |= 0x8000; + + PokeWord(FRAMES, frame); } - } - #endregion + // If not started, quit out + // + if (!started) + { + prev_last_key1 = 0; + prev_last_key2 = 0; + return; + } - #region IDevice Members + // Update LASTK + // + uint lastk1 = 0; + uint lastk2 = 0; - byte IDevice.Read(ushort device) - { - return 0; - } + for(int row = 0 ; row < 8; row++) + { + uint b = 0; - void IDevice.Write(ushort device, byte value) - { + b = (uint)((~matrix[row] & 0x1fu) << 1); + + if (row == 0) + { + uint shift = b & 2; + + b &= ~2u; + b |= (shift>>1); + } + + if (b > 0) + { + if (b > 1) + { + lastk1 |= (uint)(1 << row); + } + + lastk2 |= b; + } + } + + if (lastk1 > 0 && (lastk1 != prev_last_key1 || + lastk2 != prev_last_key2)) + { + mem[CDFLAG] |= 1; + } + else + { + mem[CDFLAG] &= 0xfe; + } + + mem[LASTK1] = (byte)(lastk1^0xff); + mem[LASTK2] = (byte)(lastk2^0xff); + + prev_last_key1 = lastk1; + prev_last_key2 = lastk2; } #endregion @@ -184,6 +329,10 @@ namespace WPZX81.ZX81 /// public void Run() { + Stopwatch s = Stopwatch.StartNew(); + + z80.ExecutedInstructions = 0; + if (fast) { clock.StartFrame(); @@ -192,11 +341,19 @@ namespace WPZX81.ZX81 z80.Run(); clock.StartFrame(); z80.Run(); - clock.StartFrame(); } + clock.StartFrame(); z80.Run(); z80.MaskableInterrupt(0); + + if (z80.StackPointer < 0x8000) + { + Housekeeping(); + } + + s.Stop(); + Debug.WriteLine("*** RUN {0} msec {1} ticks (PC = 0x{2:X4}, opcodes = {3})", s.ElapsedMilliseconds, s.ElapsedTicks, z80.ProgramCounter, z80.ExecutedInstructions); } /// @@ -205,12 +362,64 @@ namespace WPZX81.ZX81 /// The sprite batch to use for updates. public void UpdateDisplay(SpriteBatch batch) { + Stopwatch s = Stopwatch.StartNew(); + // Check if character pointer has updated // if (z80.InterruptVector != lastI) { LoadFont(); + lastI = z80.InterruptVector; + } + + // Update display + // + if (fast) + { + for(int y = 0; y < 24; y++) + { + for(int x = 0; x < 32; x++) + { + batch.Draw(font[8], new Vector2(x * 8, y * 8), Color.White); + } + } + } + else + { + ushort dfile = PeekWord(D_FILE); + + for(int y = 0; y < 24; y++) + { + for(int x = 0; x < 32; x++) + { + // Check for HALT on collapsed display files + // + if (mem[dfile] == 118) + { + batch.Draw(font[0], new Vector2(x * 8, y * 8), Color.White); + } + else + { + byte c = mem[dfile++]; + + if ((c > 64 && c < 128) || c > 191) + { + c = 0; + } + + batch.Draw(font[c], new Vector2(x * 8, y * 8), Color.White); + } + } + + if (mem[dfile] == 118) + { + dfile++; + } + } } + + s.Stop(); + Debug.WriteLine("*** UPDATE {0} msec {1} ticks", s.ElapsedMilliseconds, s.ElapsedTicks); } @@ -221,12 +430,19 @@ namespace WPZX81.ZX81 { lastI = -1; fast = true; + started = false; + waitkey = false; rambot = Settings.StaticRamAt0x2000 ? 0x2000 : 0x4000; ramtop = Settings.RamPack ? 0x8000 : 0x4400; mem.Initialize(); + for(int f = 0 ; f < 8; f++) + { + matrix[f] = 0x1f; + } + Uri uri = new Uri("Resources/zx81.rom", UriKind.Relative); StreamResourceInfo file = App.GetResourceStream(uri); @@ -253,7 +469,9 @@ namespace WPZX81.ZX81 public Emulation() { - mem = new byte[0x8000]; + font = new Texture2D[256]; + matrix = new byte[8]; + mem = new byte[0x10000]; clock = new Clock(16000 * 2, 50); z80 = new Z80Cpu(); z80.Initialise(this, this, clock); @@ -262,5 +480,77 @@ namespace WPZX81.ZX81 } #endregion + + #region IMemory Members + + byte IMemory.Read(ushort address) + { + return mem[address]; + } + + void IMemory.Write(ushort address, byte value) + { + if (address >= rambot && address <= ramtop) + { + mem[address] = value; + } + } + + #endregion + + #region IDevice Members + + byte IDevice.Read(ushort device) + { + byte b = 0xff; + + // ULA + // + if ((device & 0xff) == 0xfe) + { + switch(device&0xff00) + { + case 0xfe00: + b=matrix[0]; + break; + case 0xfd00: + b=matrix[1]; + break; + case 0xfb00: + b=matrix[2]; + break; + case 0xf700: + b=matrix[3]; + break; + case 0xef00: + b=matrix[4]; + break; + case 0xdf00: + b=matrix[5]; + break; + case 0xbf00: + b=matrix[6]; + break; + case 0x7f00: + b=matrix[7]; + break; + } + + // Some code expects some of the top bits set... Of course, whether + // or not this may be worse as other code doesn't expect the bits, + // we shall find out! + // + b |= 0x60; + } + + return b; + } + + void IDevice.Write(ushort device, byte value) + { + } + + #endregion + } } -- cgit v1.2.3