From 6620cbe08b1ba9f3abf79577d68c8ebf3c93a765 Mon Sep 17 00:00:00 2001 From: Ian C Date: Mon, 19 Mar 2012 23:39:03 +0000 Subject: Further updates and initial start of emulation code. --- WPZX81/WPZX81/App.xaml.cs | 1 + WPZX81/WPZX81/GamePage.xaml.cs | 21 +-- WPZX81/WPZX81/Resources/Strings.Designer.cs | 9 ++ WPZX81/WPZX81/Resources/Strings.resx | 3 + WPZX81/WPZX81/Resources/zx81.rom | Bin 0 -> 8192 bytes WPZX81/WPZX81/Settings.cs | 35 +++++ WPZX81/WPZX81/SettingsPage.xaml | 9 +- WPZX81/WPZX81/SettingsPage.xaml.cs | 12 ++ WPZX81/WPZX81/Shared.cs | 2 +- WPZX81/WPZX81/WPZX81.csproj | 1 + WPZX81/WPZX81/ZX81/Emulation.cs | 218 ++++++++++++++++++++++++++-- 11 files changed, 280 insertions(+), 31 deletions(-) create mode 100644 WPZX81/WPZX81/Resources/zx81.rom diff --git a/WPZX81/WPZX81/App.xaml.cs b/WPZX81/WPZX81/App.xaml.cs index 0167d21..b16ca7b 100644 --- a/WPZX81/WPZX81/App.xaml.cs +++ b/WPZX81/WPZX81/App.xaml.cs @@ -83,6 +83,7 @@ namespace WPZX81 // This code will not execute when the application is reactivated private void Application_Launching(object sender, LaunchingEventArgs e) { + Shared.ZX81 = new ZX81.Emulation(); } // Code to execute when the application is activated (brought to foreground) diff --git a/WPZX81/WPZX81/GamePage.xaml.cs b/WPZX81/WPZX81/GamePage.xaml.cs index 36d97d0..2741945 100644 --- a/WPZX81/WPZX81/GamePage.xaml.cs +++ b/WPZX81/WPZX81/GamePage.xaml.cs @@ -30,35 +30,23 @@ namespace WPZX81 // Create a timer for this page timer = new GameTimer(); - timer.UpdateInterval = TimeSpan.FromTicks(333333); + timer.UpdateInterval = TimeSpan.FromMilliseconds(40); // Updates at 25 fps timer.Update += OnUpdate; timer.Draw += OnDraw; } protected override void OnNavigatedTo(NavigationEventArgs e) { - // Set the sharing mode of the graphics device to turn on XNA rendering SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(true); - - // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(SharedGraphicsDeviceManager.Current.GraphicsDevice); - - // TODO: use this.content to load your game content here - - // Start the timer timer.Start(); - base.OnNavigatedTo(e); } protected override void OnNavigatedFrom(NavigationEventArgs e) { - // Stop the timer timer.Stop(); - - // Set the sharing mode of the graphics device to turn off XNA rendering SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(false); - base.OnNavigatedFrom(e); } @@ -68,7 +56,7 @@ namespace WPZX81 /// private void OnUpdate(object sender, GameTimerEventArgs e) { - // TODO: Add your update logic here + Shared.ZX81.Run(); } /// @@ -76,9 +64,8 @@ namespace WPZX81 /// private void OnDraw(object sender, GameTimerEventArgs e) { - SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue); - - // TODO: Add your drawing code here + SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.Black); + Shared.ZX81.UpdateDisplay(spriteBatch); } } } \ No newline at end of file diff --git a/WPZX81/WPZX81/Resources/Strings.Designer.cs b/WPZX81/WPZX81/Resources/Strings.Designer.cs index ba9ae34..a6bb352 100644 --- a/WPZX81/WPZX81/Resources/Strings.Designer.cs +++ b/WPZX81/WPZX81/Resources/Strings.Designer.cs @@ -181,6 +181,15 @@ namespace WPZX81.Resources { } } + /// + /// Looks up a localized string similar to 16K RAM Pack Inserted. + /// + public static string RamPackText { + get { + return ResourceManager.GetString("RamPackText", resourceCulture); + } + } + /// /// Looks up a localized string similar to URL for remote location of .P files. /// diff --git a/WPZX81/WPZX81/Resources/Strings.resx b/WPZX81/WPZX81/Resources/Strings.resx index 0f583b1..c892b78 100644 --- a/WPZX81/WPZX81/Resources/Strings.resx +++ b/WPZX81/WPZX81/Resources/Strings.resx @@ -208,4 +208,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY Sticky Shift Key + + 16K RAM Pack Inserted + \ No newline at end of file diff --git a/WPZX81/WPZX81/Resources/zx81.rom b/WPZX81/WPZX81/Resources/zx81.rom new file mode 100644 index 0000000..557ddcb Binary files /dev/null and b/WPZX81/WPZX81/Resources/zx81.rom differ diff --git a/WPZX81/WPZX81/Settings.cs b/WPZX81/WPZX81/Settings.cs index 93616fe..7243bc3 100644 --- a/WPZX81/WPZX81/Settings.cs +++ b/WPZX81/WPZX81/Settings.cs @@ -41,10 +41,45 @@ namespace WPZX81 } + /// + /// Get/set static RAM at 0x2000 setting. + /// + public static bool StaticRamAt0x2000 + { + get {return staticRamAt0x2000;} + set + { + if (staticRamAt0x2000 != value) + { + staticRamAt0x2000 = value; + Save("StaticRamAt0x2000", staticRamAt0x2000); + } + } + } + + /// + /// Get/set 16K RAM pack setting. + /// + public static bool RamPack + { + get {return ramPack;} + set + { + if (ramPack != value) + { + ramPack = value; + Save("RamPack", staticRamAt0x2000); + } + } + } + + #region Private code private static bool stickyShift = Load("StickyShift", false); private static string pFileUrl = Load("PFileUrl", "http://enter-a-url.here/"); + private static bool staticRamAt0x2000 = Load("StaticRamAt0x2000", false); + private static bool ramPack = Load("RamPack", true); private static T Load(string name, T defaultValue) { diff --git a/WPZX81/WPZX81/SettingsPage.xaml b/WPZX81/WPZX81/SettingsPage.xaml index 6c925e1..4243415 100644 --- a/WPZX81/WPZX81/SettingsPage.xaml +++ b/WPZX81/WPZX81/SettingsPage.xaml @@ -42,15 +42,22 @@ + + + Grid.Row="3" /> + diff --git a/WPZX81/WPZX81/SettingsPage.xaml.cs b/WPZX81/WPZX81/SettingsPage.xaml.cs index d6d5019..5bf0c94 100644 --- a/WPZX81/WPZX81/SettingsPage.xaml.cs +++ b/WPZX81/WPZX81/SettingsPage.xaml.cs @@ -32,5 +32,17 @@ namespace WPZX81 get {return Settings.StickyShift;} set {Settings.StickyShift = value;} } + + public bool StaticRamAt0x2000Setting + { + get {return Settings.StaticRamAt0x2000;} + set {Settings.StaticRamAt0x2000 = value;} + } + + public bool RamPackSetting + { + get {return Settings.RamPack;} + set {Settings.RamPack = value;} + } } } \ No newline at end of file diff --git a/WPZX81/WPZX81/Shared.cs b/WPZX81/WPZX81/Shared.cs index 38382b2..04f4588 100644 --- a/WPZX81/WPZX81/Shared.cs +++ b/WPZX81/WPZX81/Shared.cs @@ -10,7 +10,7 @@ using System.Windows.Media.Animation; using System.Windows.Shapes; using WPZX81.ZX81; -namespace wpspec +namespace WPZX81 { /// /// Contains shared objects between the Silverlight and XNA domains diff --git a/WPZX81/WPZX81/WPZX81.csproj b/WPZX81/WPZX81/WPZX81.csproj index 1fd98bd..8e80b73 100644 --- a/WPZX81/WPZX81/WPZX81.csproj +++ b/WPZX81/WPZX81/WPZX81.csproj @@ -114,6 +114,7 @@ + diff --git a/WPZX81/WPZX81/ZX81/Emulation.cs b/WPZX81/WPZX81/ZX81/Emulation.cs index 663b297..9620052 100644 --- a/WPZX81/WPZX81/ZX81/Emulation.cs +++ b/WPZX81/WPZX81/ZX81/Emulation.cs @@ -2,6 +2,8 @@ using Noddybox.Emulation; using Noddybox.Emulation.EightBit; using Noddybox.Emulation.EightBit.Z80; +using Microsoft.Xna.Framework.Graphics; +using System.Windows.Resources; namespace WPZX81.ZX81 { @@ -12,26 +14,152 @@ namespace WPZX81.ZX81 { #region Private data - private readonly Clock clock; - private readonly byte[] mem; - private readonly Z80Cpu z80; + private Clock clock; + private byte[] mem; + private Z80Cpu z80; + private bool fast; + private int lastI; + private int rambot; + private int ramtop; + + // 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; + + // ED Hooks + // + private const int ED_SAVE = 0xf0; + private const int ED_LOAD = 0xf1; + private const int ED_WAITKEY = 0xf2; + private const int ED_ENDWAITKEY = 0xf3; + private const int ED_PAUSE = 0xf4; + + // ROM patches + // + private byte[] savePatch = new byte[] + { + 0xed, ED_SAVE, /* (SAVE) */ + 0xc3, 0x07, 0x02, /* JP $0207 */ + }; + + private byte[] loadPatch = new byte[] + { + 0xed, ED_LOAD, /* (LOAD) */ + 0xc3, 0x07, 0x02, /* JP $0207 */ + }; + + private byte[] fastPatch = new byte[] + { + 0xed, ED_WAITKEY, /* (START KEY WAIT) */ + 0xcb,0x46, /* L: bit 0,(hl) */ + 0x28,0xfc, /* jr z,L */ + 0xed, ED_ENDWAITKEY, /* (END KEY WAIT) */ + 0x00, /* nop */ + }; + + private byte[] keyboardPatch = new byte[] + { + 0x2a,0x25,0x40, /* ld hl,(LASTK) */ + 0xc9, /* ret */ + }; + + private byte[] pausePatch = new byte[] + { + 0xed, ED_PAUSE, /* (PAUSE) */ + 0x00, /* nop */ + }; #endregion #region Private methods + /// + /// Loads the font from the ZX81 according to the I register. + /// + private void LoadFont() + { + } + + + private void Patch(ushort address, byte[] patch) + { + foreach (byte b in patch) + { + mem[address++] = b; + } + } + + /// + /// Patch the ROM to make life easier. + /// + private void PatchROM() + { + Patch(0x2fc, savePatch); + Patch(0x347, loadPatch); + Patch(0x4ca, fastPatch); + Patch(0x2bb, keyboardPatch); + Patch(0xf3a, pausePatch); + + // Trust me, we have a ZX81... Honestly. + // + mem[0x21c]=0x00; + mem[0x21d]=0x00; + + // Remove HALTs as we don't do NMI and the NMI generator properly + // + mem[0x0079]=0; + mem[0x02ec]=0; + } + + /// + /// Handles ED hooks. + /// + /// + /// + private void EDCallback(object sender, Z80CpuEventArgs e) + { + switch (e.Opcode) + { + case ED_SAVE: + break; + + case ED_LOAD: + break; + + case ED_WAITKEY: + break; + + case ED_ENDWAITKEY: + break; + + case ED_PAUSE: + break; + + default: + break; + } + } + #endregion #region IMemory Members byte IMemory.Read(ushort address) { - throw new NotImplementedException(); + return mem[address]; } void IMemory.Write(ushort address, byte value) { - throw new NotImplementedException(); + if (address >= rambot && address <= ramtop) + { + mem[address] = value; + } } #endregion @@ -40,31 +168,97 @@ namespace WPZX81.ZX81 byte IDevice.Read(ushort device) { - throw new NotImplementedException(); + return 0; } void IDevice.Write(ushort device, byte value) { - throw new NotImplementedException(); } #endregion #region Public methods + /// + /// Run the emulation for a frame. + /// + public void Run() + { + if (fast) + { + clock.StartFrame(); + z80.Run(); + clock.StartFrame(); + z80.Run(); + clock.StartFrame(); + z80.Run(); + clock.StartFrame(); + } + + z80.Run(); + z80.MaskableInterrupt(0); + } + + /// + /// Updates the display. + /// + /// The sprite batch to use for updates. + public void UpdateDisplay(SpriteBatch batch) + { + // Check if character pointer has updated + // + if (z80.InterruptVector != lastI) + { + LoadFont(); + } + } + + + /// + /// Reset the ZX81. + /// + public void Reset() + { + lastI = -1; + fast = true; + + rambot = Settings.StaticRamAt0x2000 ? 0x2000 : 0x4000; + ramtop = Settings.RamPack ? 0x8000 : 0x4400; + + mem.Initialize(); + + Uri uri = new Uri("Resources/zx81.rom", UriKind.Relative); + StreamResourceInfo file = App.GetResourceStream(uri); + + file.Stream.Read(mem, 0, 0x2000); + file.Stream.Close(); + + if (!Settings.StaticRamAt0x2000) + { + for(int f = 0; f < 0x2000; f++) + { + mem[0x2000 + f] = mem[f]; + } + } + + PatchROM(); + + clock.Reset(); + z80.Reset(); + } + #endregion #region Constructors public Emulation() { - mem = new byte[0x10000]; + mem = new byte[0x8000]; + clock = new Clock(16000 * 2, 50); z80 = new Z80Cpu(); - clock = new Clock(10000, 50); - - WPZX81.Resources.Strings s = new Resources.Strings(); - z80.Initialise(this, this, clock); + z80.EDNopEvent += EDCallback; + Reset(); } #endregion -- cgit v1.2.3