summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--wpspec/wpspec/App.xaml.cs1
-rw-r--r--wpspec/wpspec/GamePage.xaml.cs29
-rw-r--r--wpspec/wpspec/Spectrum/Emulation.cs235
-rw-r--r--wpspec/wpspec/wpspec.csproj1
4 files changed, 250 insertions, 16 deletions
diff --git a/wpspec/wpspec/App.xaml.cs b/wpspec/wpspec/App.xaml.cs
index 7c27124..8b490e8 100644
--- a/wpspec/wpspec/App.xaml.cs
+++ b/wpspec/wpspec/App.xaml.cs
@@ -83,6 +83,7 @@ namespace wpspec
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
+ Shared.Spectrum = new Spectrum.Emulation();
}
// Code to execute when the application is activated (brought to foreground)
diff --git a/wpspec/wpspec/GamePage.xaml.cs b/wpspec/wpspec/GamePage.xaml.cs
index 4d8ff1a..f85977b 100644
--- a/wpspec/wpspec/GamePage.xaml.cs
+++ b/wpspec/wpspec/GamePage.xaml.cs
@@ -13,6 +13,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using wpspec.Spectrum;
+using System.Diagnostics;
namespace wpspec
{
@@ -23,7 +24,6 @@ namespace wpspec
private ContentManager contentManager;
private GameTimer timer;
private SpriteBatch spriteBatch;
- private Emulation spectrum;
#endregion
@@ -46,10 +46,6 @@ namespace wpspec
timer.UpdateInterval = TimeSpan.FromMilliseconds(20);
timer.Update += OnUpdate;
timer.Draw += OnDraw;
-
- // Create the emulation
- //
- spectrum = new Emulation();
}
#endregion
@@ -66,8 +62,6 @@ namespace wpspec
//
spriteBatch = new SpriteBatch(SharedGraphicsDeviceManager.Current.GraphicsDevice);
- // TODO: use this.content to load your game content here
-
// Start the timer
//
timer.Start();
@@ -97,6 +91,9 @@ namespace wpspec
/// </summary>
private void OnUpdate(object sender, GameTimerEventArgs e)
{
+ // TODO: Softkeyboard
+
+ Shared.Spectrum.Run();
}
/// <summary>
@@ -105,6 +102,24 @@ namespace wpspec
private void OnDraw(object sender, GameTimerEventArgs e)
{
SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.Black);
+
+ DateTime now = DateTime.Now;
+
+ using (Texture2D screen =
+ new Texture2D
+ (SharedGraphicsDeviceManager.Current.GraphicsDevice,
+ Shared.Spectrum.Width,
+ Shared.Spectrum.Height,
+ false, SurfaceFormat.Color))
+ {
+ Shared.Spectrum.Update();
+ screen.SetData(Shared.Spectrum.Screen);
+
+ spriteBatch.Begin();
+ spriteBatch.Draw(screen, Vector2.Zero, Color.White);
+ spriteBatch.End();
+ Debug.WriteLine("Render - {0} msec", (DateTime.Now - now).TotalMilliseconds);
+ }
}
#endregion
diff --git a/wpspec/wpspec/Spectrum/Emulation.cs b/wpspec/wpspec/Spectrum/Emulation.cs
index 58fdb83..0e4a286 100644
--- a/wpspec/wpspec/Spectrum/Emulation.cs
+++ b/wpspec/wpspec/Spectrum/Emulation.cs
@@ -1,19 +1,13 @@
using System;
-using System.Net;
using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Documents;
-using System.Windows.Ink;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Animation;
-using System.Windows.Shapes;
using Noddybox.Emulation;
using Noddybox.Emulation.EightBit;
using Noddybox.Emulation.EightBit.Z80;
using System.Reflection;
using System.Windows.Resources;
using System.IO;
+using Microsoft.Xna.Framework;
+using System.Diagnostics;
namespace wpspec.Spectrum
{
@@ -30,15 +24,53 @@ namespace wpspec.Spectrum
private byte[] tape;
+ private readonly byte[] matrix = new byte[9];
+
+ private readonly int[] screen = new int[192];
+
+ private readonly uint[] colours = new uint[16]
+ {
+ new Color(0x00, 0x00, 0x00).PackedValue, // BLACK
+ new Color(0x00, 0x00, 0xe6).PackedValue, // BLUE
+ new Color(0xe6, 0x00, 0x00).PackedValue, // RED
+ new Color(0xe6, 0x00, 0xe6).PackedValue, // MAGENTA
+ new Color(0x00, 0xe6, 0x00).PackedValue, // GREEN
+ new Color(0x00, 0xe6, 0xe6).PackedValue, // CYAN
+ new Color(0xe6, 0xe6, 0x00).PackedValue, // YELLOW
+ new Color(0xe6, 0xe6, 0xe6).PackedValue, // WHITE
+ new Color(0x00, 0x00, 0x00).PackedValue, // BLACK (BRIGHT)
+ new Color(0x00, 0x00, 0xff).PackedValue, // BLUE (BRIGHT)
+ new Color(0xff, 0x00, 0x00).PackedValue, // RED (BRIGHT)
+ new Color(0xff, 0x00, 0xff).PackedValue, // MAGENTA (BRIGHT)
+ new Color(0x00, 0xff, 0x00).PackedValue, // GREEN (BRIGHT)
+ new Color(0x00, 0xff, 0xff).PackedValue, // CYAN (BRIGHT)
+ new Color(0xff, 0xff, 0x00).PackedValue, // YELLOW (BRIGHT)
+ new Color(0xff, 0xff, 0xff).PackedValue // WHITE (BRIGHT)
+ };
+
+ private bool flash;
+
+ private int flashCounter;
+
#endregion
#region IMemory Members
+ /// <summary>
+ /// Read from Spectrum memory.
+ /// </summary>
+ /// <param name="address">The address to read.</param>
+ /// <returns>The byte at the address.</returns>
byte IMemory.Read(ushort address)
{
return mem[address];
}
+ /// <summary>
+ /// Write to Spectrum memory. ROM writes are ignored.
+ /// </summary>
+ /// <param name="address">The address to write to.</param>
+ /// <param name="value">The byte to write to the address.</param>
void IMemory.Write(ushort address, byte value)
{
if (address >= 0x4000)
@@ -51,11 +83,61 @@ namespace wpspec.Spectrum
#region IDevice Members
+ /// <summary>
+ /// Read from attached Spectrum devices.
+ /// </summary>
+ /// <param name="device">The address of the device.</param>
+ /// <returns>The byte returned by the device.</returns>
byte IDevice.Read(ushort device)
{
- return 0;
+ Register16 addr = new Register16(device);
+ byte b = 0xff;
+
+ switch(addr.low)
+ {
+ // Kempston joystick
+ //
+ case 0x1f:
+ b = matrix[8];
+ break;
+
+ // Fuller joystick
+ //
+ case 0x7f:
+ b = matrix[8];
+ break;
+
+ // ULA
+ //
+ default:
+ if ((addr.low & 1) == 0)
+ {
+ int m = 0;
+ int r = 1;
+
+ while(r < 256)
+ {
+ if ((addr.high & r) == 0)
+ {
+ b &= matrix[m];
+ }
+
+ r *= 2;
+ m++;
+ }
+ }
+
+ break;
+ }
+
+ return b;
}
+ /// <summary>
+ /// Write to an attached Spectrum device.
+ /// </summary>
+ /// <param name="device">The address of the device.</param>
+ /// <param name="value">The byte to send to the device.</param>
void IDevice.Write(ushort device, byte value)
{
// Do nothing
@@ -64,6 +146,23 @@ namespace wpspec.Spectrum
#endregion
#region Public Members
+
+ /// <summary>
+ /// Get the width of the emulated screen in pixels.
+ /// </summary>
+ public int Width {get {return 256;}}
+
+ /// <summary>
+ /// Get the height of the emulated screen in pixels.
+ /// </summary>
+ public int Height {get {return 192;}}
+
+ /// <summary>
+ /// Get the emulated screen contents as packed RGBA values.
+ /// This array must have <see cref="Width"/> times <see cref="Height"/>
+ /// elements.
+ /// </summary>
+ public uint[] Screen {get; private set;}
/// <summary>
/// Get/set the tape file to load.
@@ -79,9 +178,99 @@ namespace wpspec.Spectrum
/// </summary>
public void Reset()
{
+ // Initialise the Spectrum
+ //
+ for(int f = 0; f < 8; f++)
+ {
+ matrix[f] = 0x1f;
+ }
+
+ matrix[8] = 0;
+
+ // Initialise the Z80
+ //
z80.Reset();
}
+ /// <summary>
+ /// Run the emulation for a frame.
+ /// </summary>
+ public void Run()
+ {
+ clock.StartFrame();
+ DateTime now = DateTime.Now;
+ z80.Run();
+ z80.MaskableInterrupt(0);
+ Debug.WriteLine("Exec - {0} msec", (DateTime.Now - now).TotalMilliseconds);
+ }
+
+ /// <summary>
+ /// Update the emulation display.
+ /// </summary>
+ public void Update()
+ {
+ uint[] scr = Screen;
+ int addr;
+ int attr;
+ int ink;
+ int paper;
+ int t;
+ int p = 0;
+ byte b;
+
+ DateTime now = DateTime.Now;
+ for(int y = 0; y < 192; y++)
+ {
+ addr = screen[y];
+
+ for(int x = 0; x < 32; x++)
+ {
+ attr = mem[0x5800 + x + y / 8 * 32];
+ ink = attr & 0x07;
+ paper = (attr & 0x38) >> 3;
+
+ if ((attr & 0x40) == 0x40)
+ {
+ ink += 8;
+ paper += 8;
+ }
+
+ if ((attr & 0x80) == 0x80 && flash)
+ {
+ t = ink;
+ ink = paper;
+ paper = t;
+ }
+
+ b = mem[addr + x];
+
+ for(int r = 128; r > 0; r /= 2)
+ {
+ if ((b & r) == r)
+ {
+ scr[p++] = colours[ink];
+ }
+ else
+ {
+ scr[p++] = colours[paper];
+ }
+ }
+ }
+ }
+
+ for(int f = 0; f < 192; f++)
+ {
+ scr[f] = colours[(flashCounter + f) % 16];
+ }
+
+ if (++flashCounter == 16)
+ {
+ flashCounter = 0;
+ flash = !flash;
+ }
+ Debug.WriteLine("Draw - {0} msec", (DateTime.Now - now).TotalMilliseconds);
+ }
+
#endregion
#region Constructors
@@ -100,9 +289,37 @@ namespace wpspec.Spectrum
file.Stream.Close();
+ // Set up screen and accelerators
+ //
+ Screen = new uint[Width * Height];
+
+ int c = 0;
+ int r = 0;
+
+ for(int f = 0; f < 192; f++)
+ {
+ screen[f] = 0x4000 + (c * 8 * 32) + (r * 32);
+
+ c++;
+
+ if ((c % 8) == 0)
+ {
+ if (++r == 8)
+ {
+ r = 0;
+ }
+ else
+ {
+ c -= 8;
+ }
+ }
+ }
+
// Initialise the CPU
//
z80.Initialise(this, this, clock);
+
+ Reset();
}
#endregion
diff --git a/wpspec/wpspec/wpspec.csproj b/wpspec/wpspec/wpspec.csproj
index 8b4dc6f..633c10c 100644
--- a/wpspec/wpspec/wpspec.csproj
+++ b/wpspec/wpspec/wpspec.csproj
@@ -97,6 +97,7 @@
<Compile Include="SettingsPage.xaml.cs">
<DependentUpon>SettingsPage.xaml</DependentUpon>
</Compile>
+ <Compile Include="Shared.cs" />
<Compile Include="Spectrum\Emulation.cs" />
</ItemGroup>
<ItemGroup>