summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2012-07-11 23:16:05 +0000
committerIan C <ianc@noddybox.co.uk>2012-07-11 23:16:05 +0000
commit68d1358a25617e30c18722ba441b9a37424a64d2 (patch)
treed72dbc0252628a32cff42f9a32ad185535a6e48e
parent1160f0f1093f4792d68c71840204d1060b285548 (diff)
Work in progress on ZX81 display/ULA emulation.
-rw-r--r--WPZX81/WPZX81/Resources/Strings.Designer.cs4
-rw-r--r--WPZX81/WPZX81/Resources/Strings.resx2
-rw-r--r--WPZX81/WPZX81/ZX81/Emulation.cs300
3 files changed, 149 insertions, 157 deletions
diff --git a/WPZX81/WPZX81/Resources/Strings.Designer.cs b/WPZX81/WPZX81/Resources/Strings.Designer.cs
index b869c1f..580f2cd 100644
--- a/WPZX81/WPZX81/Resources/Strings.Designer.cs
+++ b/WPZX81/WPZX81/Resources/Strings.Designer.cs
@@ -97,11 +97,11 @@ namespace WPZX81.Resources {
}
/// <summary>
- /// Looks up a localized string similar to WPZX81 is Copyright © 2012 noddybox
+ /// Looks up a localized string similar to WPZX81 is Copyright © 2012 Ian Cowburn
///
///This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
///
- ///This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more detai [rest of string was truncated]&quot;;.
+ ///This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more de [rest of string was truncated]&quot;;.
/// </summary>
public static string CopyrightText {
get {
diff --git a/WPZX81/WPZX81/Resources/Strings.resx b/WPZX81/WPZX81/Resources/Strings.resx
index 674127f..cc1f291 100644
--- a/WPZX81/WPZX81/Resources/Strings.resx
+++ b/WPZX81/WPZX81/Resources/Strings.resx
@@ -130,7 +130,7 @@
<value>Cancel</value>
</data>
<data name="CopyrightText" xml:space="preserve">
- <value>WPZX81 is Copyright © 2012 noddybox
+ <value>WPZX81 is Copyright © 2012 Ian Cowburn
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
diff --git a/WPZX81/WPZX81/ZX81/Emulation.cs b/WPZX81/WPZX81/ZX81/Emulation.cs
index 8df4c39..5c3e01e 100644
--- a/WPZX81/WPZX81/ZX81/Emulation.cs
+++ b/WPZX81/WPZX81/ZX81/Emulation.cs
@@ -35,15 +35,14 @@ namespace WPZX81.ZX81
private Clock clock;
private byte[] mem;
- private bool fast;
- private bool started;
- private bool waitkey;
- private int lastI;
+ private int vsync;
+ private bool hsync;
+ private int lineCounter;
+ private bool vsyncNoted;
+ private bool nmiGenerator;
private int rambot;
private int ramtop;
private byte[] matrix;
- private uint prev_last_key1;
- private uint prev_last_key2;
// Some system variables
//
@@ -150,19 +149,19 @@ namespace WPZX81.ZX81
{
Patch(0x2fc, savePatch);
Patch(0x347, loadPatch);
- Patch(0x4ca, fastPatch);
- Patch(0x2bb, keyboardPatch);
- Patch(0xf3a, pausePatch);
+ // Patch(0x4ca, fastPatch);
+ // Patch(0x2bb, keyboardPatch);
+ // Patch(0xf3a, pausePatch);
// Trust me, we have a ZX81... Honestly.
//
- mem[0x21c]=0x00;
- mem[0x21d]=0x00;
+ // 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;
+ // mem[0x0079]=0;
+ // mem[0x02ec]=0;
}
/// <summary>
@@ -182,105 +181,54 @@ namespace WPZX81.ZX81
// TODO
break;
- case ED_WAITKEY:
- waitkey = true;
- started = true;
- break;
-
- case ED_ENDWAITKEY:
- waitkey = false;
- break;
-
- case ED_PAUSE:
- // TODO
- break;
-
default:
break;
}
}
- /// <summary>
- /// Do various housekeeping of the system variables which we do ourselves due the patching.
- /// </summary>
- private void Housekeeping()
+ private bool OperationCallback(ICpu sender)
{
- // British ZX81
- //
- mem[MARGIN] = 55;
-
- // Update FRAMES
- //
- if (!fast)
- {
- ushort frame = PeekWord(FRAMES);
- frame &= 0x7fff;
-
- if (frame > 0)
- {
- frame--;
- }
-
- frame |= 0x8000;
-
- PokeWord(FRAMES, frame);
- }
-
- // If not started, quit out
+ // During VSYNC, keep resetting the clock
//
- if (!started)
+ if (vsync > 0)
{
- prev_last_key1 = 0;
- prev_last_key2 = 0;
- return;
+ vsync++;
+ clock.CycleCount = 0;
}
-
- // Update LASTK
- //
- uint lastk1 = 0;
- uint lastk2 = 0;
-
- for(int row = 0 ; row < 8; row++)
+ else
{
- uint b = 0;
-
- b = (uint)((~matrix[row] & 0x1fu) << 1);
-
- if (row == 0)
+ // If we're out of VSYNC, see if we need to generate an interrupt
+ //
+ if (clock.CycleCount >= 224)
{
- uint shift = b & 2;
+ clock.CycleCount -= 224;
- b &= ~2u;
- b |= (shift>>1);
- }
-
- if (b > 0)
- {
- if (b > 1)
+ if (nmiGenerator)
{
- lastk1 |= (uint)(1 << row);
+ Z80.NonMaskableInterrupt(0xff);
}
-
- lastk2 |= b;
}
}
- if (lastk1 > 0 && (lastk1 != prev_last_key1 ||
- lastk2 != prev_last_key2))
+ // Maskable interupts are tied to bit 6 of the refresh register.
+ //
+ if ((Z80.Refresh | 0x20) == 0)
{
- mem[CDFLAG] |= 1;
+ Z80.MaskableInterrupt(0xff);
+ }
+
+ // 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.
+ //
+ if (vsync > 10 && !vsyncNoted)
+ {
+ vsyncNoted = true;
+ return true;
}
else
{
- mem[CDFLAG] &= 0xfe;
+ return false;
}
-
- mem[LASTK1] = (byte)(lastk1^0xff);
- mem[LASTK2] = (byte)(lastk2^0xff);
-
- prev_last_key1 = lastk1;
- prev_last_key2 = lastk2;
}
#endregion
@@ -323,32 +271,8 @@ namespace WPZX81.ZX81
/// </summary>
public void Run()
{
- Stopwatch s = Stopwatch.StartNew();
-
- //for(int f = 1; f <= Settings.FramesPerUpdate; f++)
- //{
- // if (fast)
- // {
- // clock.StartFrame();
- // Z80.Run();
- // clock.StartFrame();
- // Z80.Run();
- // clock.StartFrame();
- // Z80.Run();
- // }
-
- // 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);
+ vsyncNoted = false;
+ Z80.Run(OperationCallback);
}
/// <summary>
@@ -373,15 +297,15 @@ namespace WPZX81.ZX81
/// </summary>
public void Reset()
{
- lastI = -1;
- fast = true;
- started = false;
- waitkey = false;
+ vsync = 0;
+ hsync = false;
+ vsyncNoted = false;
+ nmiGenerator = false;
rambot = Settings.StaticRamAt0x2000 ? 0x2000 : 0x4000;
ramtop = Settings.RamPack ? 0x8000 : 0x4400;
- mem.Initialize();
+ new Random().NextBytes(mem);
for(int f = 0 ; f < 8; f++)
{
@@ -430,7 +354,29 @@ namespace WPZX81.ZX81
byte IMemory.Read(ushort address)
{
- return mem[address & 0x7fff];
+ // Reads above 0x8000 are grabbed by the ULA to generate the display.
+ //
+ if (address >= 0x8000)
+ {
+ byte b = mem[address & 0x7fff];
+
+ // Opcodes with bit 6 are still passed to the CPU
+ //
+ if ((b & 0x20) == 0x20)
+ {
+ return b;
+ }
+ else
+ {
+ // Return NOP
+ //
+ return 0;
+ }
+ }
+ else
+ {
+ return mem[address & 0x7fff];
+ }
}
void IMemory.Write(ushort address, byte value)
@@ -451,41 +397,60 @@ namespace WPZX81.ZX81
// ULA
//
- if ((device & 0xff) == 0xfe)
+ switch(device & 0xff)
{
- 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!
+ // ULA
//
- b |= 0x60;
+ case 0xfe:
+
+ // Once the ULA is read the vsync video level is triggered and the HYSNC switched off if NMI generation is off.
+ // 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;
+
+ if (!nmiGenerator)
+ {
+ hsync = false;
+ }
+
+ 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;
+ break;
+
+ default:
+ break;
}
return b;
@@ -493,6 +458,33 @@ namespace WPZX81.ZX81
void IDevice.Write(ushort device, byte value)
{
+ switch(device & 0xff)
+ {
+ // Turns off the NMI generator
+ //
+ case 0xfd:
+ nmiGenerator = false;
+ break;
+
+ // Turns on the NMI generator
+ //
+ case 0xfe:
+ nmiGenerator = true;
+ break;
+
+ // The ZX81 sends an OUT FF,A after the VSYNC to start HYSNC generation,
+ // which puts the ULA back to 'white' output and releases the reset on the
+ // ULA LINECTR.
+ //
+ case 0xff:
+ vsync = 0;
+ hsync = true;
+ lineCounter = 0;
+ break;
+
+ default:
+ break;
+ }
}
#endregion