summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--WPZX81/WPZX81/GamePage.xaml.cs8
-rw-r--r--WPZX81/WPZX81/ZX81/Emulation.cs346
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
/// </summary>
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
//
@@ -79,13 +88,90 @@ namespace WPZX81.ZX81
#region Private methods
/// <summary>
+ /// Reads a 16-bit word from memory.
+ /// </summary>
+ /// <param name="addr">The address to start reading from.</param>
+ /// <returns>The returned word.</returns>
+ private ushort PeekWord(ushort addr)
+ {
+ Register16 r = new Register16(0);
+
+ r.low = mem[addr++];
+ r.high = mem[addr];
+
+ return r.reg;
+ }
+
+ /// <summary>
+ /// Write a 16-bit word to memory.
+ /// </summary>
+ /// <param name="addr">The address to start writing to.</param>
+ /// <param name="val">The value to write.</param>
+ private void PokeWord(ushort addr, ushort val)
+ {
+ Register16 r = new Register16(val);
+
+ mem[addr++] = r.low;
+ mem[addr] = r.high;
+ }
+
+ /// <summary>
/// Loads the font from the ZX81 according to the I register.
/// </summary>
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);
+ }
+ }
+
+ /// <summary>
+ /// Patches a route to an address in memory.
+ /// </summary>
+ /// <param name="address">The starting address to patch.</param>
+ /// <param name="patch">The patch as an array of bytes.</param>
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)
+ /// <summary>
+ /// Do various housekeeping of the system variables which we do ourselves due the patching.
+ /// </summary>
+ 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
/// </summary>
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);
}
/// <summary>
@@ -205,12 +362,64 @@ namespace WPZX81.ZX81
/// <param name="batch">The sprite batch to use for updates.</param>
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
+
}
}