diff --git a/Library/DirectSoundInterop.cs b/Library/DirectSoundInterop.cs
index a066c97..b7244d3 100644
--- a/Library/DirectSoundInterop.cs
+++ b/Library/DirectSoundInterop.cs
@@ -9,42 +9,18 @@ namespace Jellyfish.Library
public sealed partial class DirectSound
{
[Flags]
- private enum BufferCapabilities
- {
- PrimaryBuffer = 0x00000001,
- CtrlPositionNotify = 0x00000100,
- StickyFocus = 0x00004000,
- GlobalFocus = 0x00008000
- }
+ private enum BufferCapabilities { PrimaryBuffer = 0x00000001, CtrlPositionNotify = 0x00000100, StickyFocus = 0x00004000, GlobalFocus = 0x00008000 }
[Flags]
- private enum BufferLock
- {
- None = 0x00000000,
- FromWriteCursor = 0x00000001,
- EntireBuffer = 0x00000002
- }
+ private enum BufferLock { None = 0x00000000, FromWriteCursor = 0x00000001, EntireBuffer = 0x00000002 }
[Flags]
- private enum BufferPlay
- {
- Looping = 0x00000001
- }
+ private enum BufferPlay { Looping = 0x00000001 }
[Flags]
- private enum BufferStatus
- {
- Playing = 0x00000001,
- BufferLost = 0x00000002,
- Looping = 0x00000004,
- Terminated = 0x00000020
- }
+ private enum BufferStatus { Playing = 0x00000001, BufferLost = 0x00000002, Looping = 0x00000004, Terminated = 0x00000020 }
- private enum CooperativeLevel
- {
- Normal = 1,
- Priority = 2
- }
+ private enum CooperativeLevel { Normal = 1, Priority = 2 }
[StructLayout(LayoutKind.Sequential)]
private sealed class BufferDescription
diff --git a/Library/Silverlight/ApplicationBase.cs b/Library/Silverlight/ApplicationBase.cs
index f090b83..dafeb81 100644
--- a/Library/Silverlight/ApplicationBase.cs
+++ b/Library/Silverlight/ApplicationBase.cs
@@ -10,16 +10,16 @@ public ApplicationBase(string name)
{
Name = name;
- UnhandledException += Application_UnhandledException;
- //AppDomain.CurrentDomain.UnhandledException += AppDomain_UnhandledException;
+ UnhandledException += OnApplicationUnhandledException;
+ //AppDomain.CurrentDomain.UnhandledException += OnAppDomainUnhandledException;
}
- private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
+ private void OnApplicationUnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
MessageBox.Show(GetExceptionMessage(e.ExceptionObject), GetExceptionCaption("Application Exception", false), MessageBoxButton.OK);
}
- //private void AppDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+ //private void OnAppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
//{
// MessageBox.Show(GetExceptionMessage(e.ExceptionObject as Exception), GetExceptionCaption("AppDomain Exception", e.IsTerminating), MessageBoxButton.OK);
//}
diff --git a/Library/Silverlight/FrameRateCounter.xaml.cs b/Library/Silverlight/FrameRateCounter.xaml.cs
index bbc650c..3a91b3d 100644
--- a/Library/Silverlight/FrameRateCounter.xaml.cs
+++ b/Library/Silverlight/FrameRateCounter.xaml.cs
@@ -11,10 +11,10 @@ public FrameRateCounter()
{
InitializeComponent();
- CompositionTarget.Rendering += CompositionTarget_Rendering;
+ CompositionTarget.Rendering += OnCompositionTargetRendering;
}
- private void CompositionTarget_Rendering(object sender, EventArgs e)
+ private void OnCompositionTargetRendering(object sender, EventArgs e)
{
_frameCount++;
diff --git a/Library/Wpf/ApplicationBase.cs b/Library/Wpf/ApplicationBase.cs
index e3ddfb5..61de726 100644
--- a/Library/Wpf/ApplicationBase.cs
+++ b/Library/Wpf/ApplicationBase.cs
@@ -14,18 +14,18 @@ public ApplicationBase(string name)
{
Name = name;
- DispatcherUnhandledException += Application_DispatcherUnhandledException;
- AppDomain.CurrentDomain.UnhandledException += AppDomain_UnhandledException;
+ DispatcherUnhandledException += OnApplicationDispatcherUnhandledException;
+ AppDomain.CurrentDomain.UnhandledException += OnAppDomainUnhandledException;
}
- private void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
+ private void OnApplicationDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
MessageBox.Show(GetExceptionMessage(e.Exception), GetExceptionCaption("Application Dispatcher Exception", true));
Shutdown();
e.Handled = true;
}
- private void AppDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+ private void OnAppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
MessageBox.Show(GetExceptionMessage(e.ExceptionObject as Exception), GetExceptionCaption("AppDomain Exception", e.IsTerminating));
}
diff --git a/Library/Wpf/FrameRateCounter.xaml.cs b/Library/Wpf/FrameRateCounter.xaml.cs
index bbc650c..3a91b3d 100644
--- a/Library/Wpf/FrameRateCounter.xaml.cs
+++ b/Library/Wpf/FrameRateCounter.xaml.cs
@@ -11,10 +11,10 @@ public FrameRateCounter()
{
InitializeComponent();
- CompositionTarget.Rendering += CompositionTarget_Rendering;
+ CompositionTarget.Rendering += OnCompositionTargetRendering;
}
- private void CompositionTarget_Rendering(object sender, EventArgs e)
+ private void OnCompositionTargetRendering(object sender, EventArgs e)
{
_frameCount++;
diff --git a/Virtu/Cpu.cs b/Virtu/Cpu.cs
index cc2e0d8..10657e8 100644
--- a/Virtu/Cpu.cs
+++ b/Virtu/Cpu.cs
@@ -10,8 +10,6 @@ public sealed partial class Cpu : MachineComponent
public Cpu(Machine machine) :
base(machine)
{
- _updateEvent = UpdateEvent; // cache delegate; avoids garbage
-
ExecuteOpcode65N02 = new Action[OpcodeCount]
{
Execute65X02Brk00, Execute65X02Ora01, Execute65N02Nop02, Execute65N02Nop03,
@@ -157,7 +155,7 @@ public override void Initialize()
UpdateSettings();
- Machine.Events.AddEvent(CyclesPerUpdate, _updateEvent);
+ Machine.Video.VSync += OnVideoVSync;
}
public override void Reset()
@@ -3212,21 +3210,20 @@ private void Execute65C02Tsb0C() // tsb abs
}
#endregion
- private void UpdateEvent()
+ private void OnVideoVSync(object sender, EventArgs e)
{
UpdateSettings();
if (Machine.Settings.Cpu.IsThrottled)
{
long elapsedTime = DateTime.UtcNow.Ticks - _lastTime;
- if (elapsedTime < TicksPerVSync)
+ long ticksPerVSync = Machine.Video.TicksPerVSync;
+ if (elapsedTime < ticksPerVSync)
{
- Thread.Sleep(TimeSpan.FromTicks(TicksPerVSync - elapsedTime).Milliseconds);
+ Thread.Sleep(TimeSpan.FromTicks(ticksPerVSync - elapsedTime).Milliseconds);
}
_lastTime = DateTime.UtcNow.Ticks;
}
-
- Machine.Events.AddEvent(CyclesPerUpdate, _updateEvent);
}
private void UpdateSettings()
@@ -3245,8 +3242,6 @@ private void UpdateSettings()
public int Opcode { get; private set; }
public long Cycles { get; private set; }
- private Action _updateEvent;
-
private Memory _memory;
private Action[] _executeOpcode;
diff --git a/Virtu/CpuData.cs b/Virtu/CpuData.cs
index ef70640..ff3d2d5 100644
--- a/Virtu/CpuData.cs
+++ b/Virtu/CpuData.cs
@@ -4,11 +4,6 @@ namespace Jellyfish.Virtu
{
public partial class Cpu
{
- private const int CyclesPerUpdate = 17030;
- private const int CyclesPerVSync = 17030;
- private const int CyclesPerSecond = 1022730;
- private static readonly long TicksPerVSync = TimeSpan.FromSeconds((double)CyclesPerVSync / CyclesPerSecond).Ticks;
-
private const int OpcodeCount = 256;
private readonly Action[] ExecuteOpcode65N02;
diff --git a/Virtu/Jellyfish.Virtu.FxCop b/Virtu/Jellyfish.Virtu.FxCop
index bc91225..9189152 100644
--- a/Virtu/Jellyfish.Virtu.FxCop
+++ b/Virtu/Jellyfish.Virtu.FxCop
@@ -96,19 +96,6 @@
-
-
-
-
-
-
- - 'Video.ReadFloatingBus()'
-
-
-
-
-
-
@@ -153,19 +140,6 @@
-
-
-
-
-
-
- - 'Video.ReadFloatingBus()'
-
-
-
-
-
-
@@ -180,9 +154,6 @@
{0} creates an exception of type {1}, an exception type that is not sufficiently specific and should never be raised by user code. If this exception instance might be thrown, use a different exception type.
-
- The 'this' parameter (or 'Me' in Visual Basic) of {0} is never used. Mark the member as static (or Shared in Visual Basic) or use 'this'/'Me' in the method body or at least one property accessor, if appropriate.
-
{0} declares a variable, {1}, of type {2}, which is never used or is only assigned to. Use this variable or remove it.
diff --git a/Virtu/Keyboard.cs b/Virtu/Keyboard.cs
index 51be1e9..1f4ed3c 100644
--- a/Virtu/Keyboard.cs
+++ b/Virtu/Keyboard.cs
@@ -10,7 +10,6 @@ public sealed class Keyboard : MachineComponent
public Keyboard(Machine machine) :
base(machine)
{
- _pollEvent = PollEvent; // cache delegate; avoids garbage
}
public override void Initialize()
@@ -19,8 +18,7 @@ public override void Initialize()
_gamePortService = Machine.Services.GetService();
_keyboardService.AsciiKeyDown += (sender, e) => Latch = e.AsciiKey;
-
- Machine.Events.AddEvent(CyclesPerPoll, _pollEvent);
+ Machine.Video.VSync += OnVideoVSync;
}
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
@@ -123,7 +121,7 @@ public void ResetStrobe()
Strobe = false;
}
- private void PollEvent()
+ private void OnVideoVSync(object sender, EventArgs e)
{
if (_keyboardService.IsResetKeyDown)
{
@@ -145,8 +143,6 @@ private void PollEvent()
Machine.Video.ToggleMonochrome();
_keyboardService.WaitForKeyUp();
}
-
- Machine.Events.AddEvent(CyclesPerPoll, _pollEvent);
}
public bool IsAnyKeyDown { get { return _keyboardService.IsAnyKeyDown; } }
@@ -154,10 +150,6 @@ private void PollEvent()
private int Latch { get { return _latch; } set { _latch = value; Strobe = true; } }
- private const int CyclesPerPoll = 17030;
-
- private Action _pollEvent;
-
private KeyboardService _keyboardService;
private GamePortService _gamePortService;
diff --git a/Virtu/Machine.cs b/Virtu/Machine.cs
index 5a99f7f..e82a1f8 100644
--- a/Virtu/Machine.cs
+++ b/Virtu/Machine.cs
@@ -83,7 +83,7 @@ private void Run() // machine thread
{
do
{
- Events.RaiseEvents(Cpu.Execute());
+ Events.HandleEvents(Cpu.Execute());
}
while (State == MachineState.Running);
diff --git a/Virtu/MachineEvents.cs b/Virtu/MachineEvents.cs
index 388d9c7..160a4ca 100644
--- a/Virtu/MachineEvents.cs
+++ b/Virtu/MachineEvents.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
using System.Globalization;
namespace Jellyfish.Virtu
@@ -78,8 +77,7 @@ public int FindEvent(Action action)
return 0;
}
- [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
- public void RaiseEvents(int delta)
+ public void HandleEvents(int delta)
{
LinkedListNode node = _used.First;
node.Value.Delta -= delta;
diff --git a/Virtu/MachineSettings.cs b/Virtu/MachineSettings.cs
index 100d2ce..b5354d3 100644
--- a/Virtu/MachineSettings.cs
+++ b/Virtu/MachineSettings.cs
@@ -38,7 +38,7 @@ public MachineSettings()
};
Video = new VideoSettings
{
- IsFullScreen = false, IsMonochrome = false, ScannerType = 0,
+ IsFullScreen = false, IsMonochrome = false, ScannerModes = ScannerModes.None,
Color = new ColorSettings
{
Black = 0x000000,
@@ -172,8 +172,8 @@ public void Deserialize(Stream stream)
Video = new VideoSettings
{
IsFullScreen = (bool)video.Attribute("IsFullScreen"),
- IsMonochrome = (bool)video.Attribute("IsMonochrome"),
- ScannerType = (int)video.Attribute("ScannerType"),
+ IsMonochrome = (bool)video.Attribute("IsMonochrome"),
+ ScannerModes = (ScannerModes)Enum.Parse(typeof(ScannerModes), (string)video.Attribute("ScannerModes"), true),
Color = new ColorSettings
{
Black = (uint)color.Attribute("Black"),
@@ -269,7 +269,7 @@ public void Serialize(Stream stream)
new XElement(ns + "Video",
new XAttribute("IsFullScreen", Video.IsFullScreen),
new XAttribute("IsMonochrome", Video.IsMonochrome),
- new XAttribute("ScannerType", Video.ScannerType),
+ new XAttribute("ScannerModes", Video.ScannerModes),
new XElement(ns + "Color",
new XAttribute("Black", Video.Color.Black),
new XAttribute("DarkBlue", Video.Color.DarkBlue),
@@ -377,11 +377,14 @@ public sealed class ColorSettings
public uint Monochrome { get; set; }
}
+ [Flags]
+ public enum ScannerModes { None = 0x0, AppleII = 0x1, Pal = 0x2 } // defaults to AppleIIe, Ntsc
+
public sealed class VideoSettings
{
public bool IsFullScreen { get; set; }
public bool IsMonochrome { get; set; }
- public int ScannerType { get; set; }
+ public ScannerModes ScannerModes { get; set; }
public ColorSettings Color { get; set; }
};
}
diff --git a/Virtu/Services/KeyboardService.cs b/Virtu/Services/KeyboardService.cs
index f124e56..31e9df4 100644
--- a/Virtu/Services/KeyboardService.cs
+++ b/Virtu/Services/KeyboardService.cs
@@ -1,5 +1,4 @@
using System;
-using System.Diagnostics.CodeAnalysis;
using System.Threading;
namespace Jellyfish.Virtu.Services
@@ -31,8 +30,7 @@ protected KeyboardService(Machine machine) :
public abstract bool IsKeyDown(int key);
- [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
- protected void RaiseAsciiKeyDown(int asciiKey)
+ protected void OnAsciiKeyDown(int asciiKey)
{
EventHandler handler = AsciiKeyDown;
if (handler != null)
diff --git a/Virtu/Silverlight/MainPage.xaml.cs b/Virtu/Silverlight/MainPage.xaml.cs
index a22d072..316a495 100644
--- a/Virtu/Silverlight/MainPage.xaml.cs
+++ b/Virtu/Silverlight/MainPage.xaml.cs
@@ -26,11 +26,11 @@ public MainPage()
_machine.Services.AddService(typeof(VideoService), _videoService);
Loaded += (sender, e) => _machine.Start();
- CompositionTarget.Rendering += CompositionTarget_Rendering;
+ CompositionTarget.Rendering += OnCompositionTargetRendering;
Application.Current.Exit += (sender, e) => _machine.Stop();
- _disk1Button.Click += (sender, e) => DiskButton_Click(0);
- _disk2Button.Click += (sender, e) => DiskButton_Click(1);
+ _disk1Button.Click += (sender, e) => OnDiskButtonClick(0);
+ _disk2Button.Click += (sender, e) => OnDiskButtonClick(1);
}
public void Dispose()
@@ -43,14 +43,14 @@ public void Dispose()
_videoService.Dispose();
}
- private void CompositionTarget_Rendering(object sender, EventArgs e)
+ private void OnCompositionTargetRendering(object sender, EventArgs e)
{
_keyboardService.Update();
_gamePortService.Update();
_videoService.Update();
}
- private void DiskButton_Click(int drive)
+ private void OnDiskButtonClick(int drive)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "Disk Files (*.dsk;*.nib)|*.dsk;*.nib|All Files (*.*)|*.*";
diff --git a/Virtu/Silverlight/Services/SilverlightKeyboardService.cs b/Virtu/Silverlight/Services/SilverlightKeyboardService.cs
index 8e6f058..1e9ac2e 100644
--- a/Virtu/Silverlight/Services/SilverlightKeyboardService.cs
+++ b/Virtu/Silverlight/Services/SilverlightKeyboardService.cs
@@ -19,9 +19,9 @@ public SilverlightKeyboardService(Machine machine, UserControl page) :
_page = page;
- _page.KeyDown += Page_KeyDown;
- _page.KeyUp += Page_KeyUp;
- _page.LostFocus += Page_LostFocus;
+ _page.KeyDown += OnPageKeyDown;
+ _page.KeyUp += OnPageKeyUp;
+ _page.LostFocus += OnPageLostFocus;
}
public override bool IsKeyDown(int key)
@@ -62,7 +62,7 @@ private bool IsKeyDown(Key key)
return _states[(int)key];
}
- private void Page_KeyDown(object sender, KeyEventArgs e)
+ private void OnPageKeyDown(object sender, KeyEventArgs e)
{
_states[(int)e.Key] = true;
IsAnyKeyDown = true;
@@ -70,19 +70,19 @@ private void Page_KeyDown(object sender, KeyEventArgs e)
int asciiKey = GetAsciiKey(e.Key, e.PlatformKeyCode);
if (asciiKey >= 0)
{
- RaiseAsciiKeyDown(asciiKey);
+ OnAsciiKeyDown(asciiKey);
e.Handled = true;
}
}
- private void Page_KeyUp(object sender, KeyEventArgs e)
+ private void OnPageKeyUp(object sender, KeyEventArgs e)
{
_capsLock ^= (e.Key == Key.CapsLock); // SL is missing caps lock support; try to track manually
_states[(int)e.Key] = false;
_updateAnyKeyDown = true;
}
- private void Page_LostFocus(object sender, RoutedEventArgs e) // reset keyboard state on lost focus; can't access keyboard state on got focus
+ private void OnPageLostFocus(object sender, RoutedEventArgs e) // reset keyboard state on lost focus; can't access keyboard state on got focus
{
IsAnyKeyDown = false;
foreach (Key key in KeyValues)
diff --git a/Virtu/Video.cs b/Virtu/Video.cs
index 2b7286d..9b8baea 100644
--- a/Virtu/Video.cs
+++ b/Virtu/Video.cs
@@ -29,9 +29,9 @@ public override void Initialize()
UpdateSettings();
IsVBlank = true;
- Machine.Events.AddEvent((CyclesPerVBlank / 2), _leaveVBlankEvent);
- Machine.Events.AddEvent(CyclesPerVSync, _resetVSyncEvent);
- Machine.Events.AddEvent(CyclesPerFlash, _inverseTextEvent);
+ Machine.Events.AddEvent(_cyclesPerVBlankPreset, _leaveVBlankEvent); // align flush events with scanner; assumes vcount preset at start of frame [3-15, 3-16]
+ Machine.Events.AddEvent(_cyclesPerVSync, _resetVSyncEvent);
+ Machine.Events.AddEvent(_cyclesPerFlash, _inverseTextEvent);
}
public override void Reset()
@@ -92,8 +92,29 @@ public void DirtyScreenText()
public int ReadFloatingBus()
{
- // TODO
- return 0x00;
+ // derive scanner counters from current cycles into frame; assumes hcount and vcount preset at start of frame [3-13, 3-15, 3-16]
+ int cycles = _cyclesPerVSync - Machine.Events.FindEvent(_resetVSyncEvent);
+ int hClock = cycles % CyclesPerHSync;
+ int hCount = (hClock != 0) ? HCountPreset + hClock - 1 : 0;
+ int vLine = cycles / CyclesPerHSync;
+ int vCount = _vCountPreset + vLine;
+
+ // derive scanner address [5-8]
+ int address = ((vCount << 4) & 0x0380) | ((0x0068 + (hCount & 0x0038) + (((vCount >> 1) & 0x0060) | ((vCount >> 3) & 0x0018))) & 0x0078) | (hCount & 0x0007);
+ if (_memory.IsHires && !(_memory.IsMixed && ((vCount & 0xA0) == 0xA0))) // hires but not actively mixed [5-13, 5-19]
+ {
+ address |= (_memory.IsVideoPage2 ? 0x4000 : 0x2000) | ((vCount << 10) & 0x1C00);
+ }
+ else
+ {
+ address |= _memory.IsVideoPage2 ? 0x0800 : 0x0400;
+ if (((_scannerModes & ScannerModes.AppleII) != 0) && (hCount < HCountLeaveHBlank))
+ {
+ address |= 0x1000;
+ }
+ }
+
+ return _memory.Read(address);
}
public void SetCharSet()
@@ -641,7 +662,7 @@ private void DrawDHiresM(int address, int x, int y)
#region Flush Methods
private void FlushRowMode0(int y)
{
- int address = (!_memory.IsVideoPage2 ? 0x0400 : 0x0800) + AddressOffset[y];
+ int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y];
for (int x = 0; x < CellColumns; x++)
{
if (_isCellDirty[CellColumns * y + x])
@@ -654,7 +675,7 @@ private void FlushRowMode0(int y)
private void FlushRowMode1(int y)
{
- int address = (!_memory.IsVideoPage2 ? 0x0400 : 0x0800) + AddressOffset[y];
+ int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y];
for (int x = 0; x < CellColumns; x++)
{
if (_isCellDirty[CellColumns * y + x])
@@ -667,7 +688,7 @@ private void FlushRowMode1(int y)
private void FlushRowMode2(int y)
{
- int address = (!_memory.IsVideoPage2 ? 0x0400 : 0x0800) + AddressOffset[y];
+ int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y];
for (int x = 0; x < 2 * CellColumns; x += 2)
{
if (_isCellDirty[CellColumns * y + x / 2])
@@ -705,7 +726,7 @@ private void FlushRowMode4(int y)
private void FlushRowMode5(int y)
{
- int address = !_memory.IsVideoPage2 ? 0x2000 : 0x4000;
+ int address = _memory.IsVideoPage2 ? 0x4000 : 0x2000;
for (int i = 0; i < CellHeight; i++, y++)
{
for (int x = 0; x < CellColumns; x++)
@@ -745,7 +766,7 @@ private void FlushRowMode7(int y)
private void FlushRowMode8(int y)
{
- int address = (!_memory.IsVideoPage2 ? 0x0400 : 0x0800) + AddressOffset[y];
+ int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y];
for (int x = 0; x < CellColumns; x++)
{
if (_isCellDirty[CellColumns * y + x])
@@ -758,7 +779,7 @@ private void FlushRowMode8(int y)
private void FlushRowMode9(int y)
{
- int address = (!_memory.IsVideoPage2 ? 0x0400 : 0x0800) + AddressOffset[y];
+ int address = (_memory.IsVideoPage2 ? 0x0800 : 0x0400) + AddressOffset[y];
for (int x = 0; x < 2 * CellColumns; x += 2)
{
if (_isCellDirty[CellColumns * y + x / 2])
@@ -796,7 +817,7 @@ private void FlushRowModeB(int y)
private void FlushRowModeC(int y)
{
- int address = !_memory.IsVideoPage2 ? 0x2000 : 0x4000;
+ int address = _memory.IsVideoPage2 ? 0x4000 : 0x2000;
for (int i = 0; i < CellHeight; i++, y++)
{
for (int x = 0; x < CellColumns; x++)
@@ -812,7 +833,7 @@ private void FlushRowModeC(int y)
private void FlushRowModeD(int y)
{
- int address = !_memory.IsVideoPage2 ? 0x2000 : 0x4000;
+ int address = _memory.IsVideoPage2 ? 0x4000 : 0x2000;
for (int i = 0; i < CellHeight; i++, y++)
{
for (int x = 0; x < CellColumns; x++)
@@ -854,7 +875,7 @@ private void FlushRowModeF(int y)
private void FlushRowEvent()
{
- int y = (CyclesPerVSync - (CyclesPerVBlank / 2) - Machine.Events.FindEvent(_resetVSyncEvent)) / CyclesPerHSync;
+ int y = (_cyclesPerVSync - _cyclesPerVBlankPreset - Machine.Events.FindEvent(_resetVSyncEvent)) / CyclesPerHSync;
FlushRowMode[_memory.VideoMode](y - CellHeight); // in arrears
@@ -866,7 +887,7 @@ private void FlushRowEvent()
{
IsVBlank = true;
- Machine.Events.AddEvent(CyclesPerVBlank, _leaveVBlankEvent);
+ Machine.Events.AddEvent(_cyclesPerVBlank, _leaveVBlankEvent);
}
}
@@ -886,7 +907,7 @@ private void InverseTextEvent()
DirtyScreenText();
- Machine.Events.AddEvent(CyclesPerFlash, _inverseTextEvent);
+ Machine.Events.AddEvent(_cyclesPerFlash, _inverseTextEvent);
}
private void LeaveVBlankEvent()
@@ -900,7 +921,13 @@ private void ResetVSyncEvent()
{
UpdateSettings();
- Machine.Events.AddEvent(CyclesPerVSync, _resetVSyncEvent);
+ EventHandler handler = VSync;
+ if (handler != null)
+ {
+ handler(this, EventArgs.Empty);
+ }
+
+ Machine.Events.AddEvent(_cyclesPerVSync, _resetVSyncEvent);
}
private void SetPixel(int x, int y, int color)
@@ -949,6 +976,23 @@ private void UpdateSettings()
_colorPalette[ColorDHiresE] = settings.Color.Yellow;
_colorPalette[ColorDHiresF] = settings.Color.White;
+ _scannerModes = settings.ScannerModes;
+ if ((_scannerModes & ScannerModes.Pal) != 0)
+ {
+ _vCountPreset = VCountPresetPal;
+ _vLineLeaveVBlank = VLineLeaveVBlankPal;
+ }
+ else
+ {
+ _vCountPreset = VCountPresetNtsc;
+ _vLineLeaveVBlank = VLineLeaveVBlankNtsc;
+ }
+
+ _cyclesPerVBlank = (_vLineLeaveVBlank - VLineEnterVBlank) * CyclesPerHSync;
+ _cyclesPerVBlankPreset = (_vLineLeaveVBlank - VLineTriggerPreset) * CyclesPerHSync; // cycles during vblank after vcount preset [3-15, 3-16]
+ _cyclesPerVSync = _vLineLeaveVBlank * CyclesPerHSync;
+ _cyclesPerFlash = VSyncsPerFlash * _cyclesPerVSync;
+
if (_videoService.IsFullScreen != settings.IsFullScreen)
{
_videoService.ToggleFullScreen();
@@ -956,6 +1000,9 @@ private void UpdateSettings()
}
public bool IsVBlank { get; private set; }
+ public long TicksPerVSync { get { return TimeSpan.FromSeconds((double)_cyclesPerVSync / CyclesPerSecond).Ticks; } }
+
+ public event EventHandler VSync;
private Action _flushRowEvent;
private Action _inverseTextEvent;
@@ -969,5 +1016,12 @@ private void UpdateSettings()
private uint[] _colorPalette = new uint[ColorPaletteCount];
private bool[] _isCellDirty = new bool[Height * CellColumns + 1]; // includes sentinel
private bool _isTextInversed;
+ private int _cyclesPerVBlank;
+ private int _cyclesPerVBlankPreset;
+ private int _cyclesPerVSync;
+ private int _cyclesPerFlash;
+ private int _vCountPreset;
+ private int _vLineLeaveVBlank;
+ private ScannerModes _scannerModes;
}
}
diff --git a/Virtu/VideoData.cs b/Virtu/VideoData.cs
index 1f3ec28..b6085fc 100644
--- a/Virtu/VideoData.cs
+++ b/Virtu/VideoData.cs
@@ -1596,9 +1596,17 @@ public partial class Video
private const int CyclesPerHBlank = 25;
private const int CyclesPerHSync = 65;
private const int CyclesPerFlush = 8 * CyclesPerHSync;
- private const int CyclesPerVBlank = 70 * CyclesPerHSync;
- private const int CyclesPerVSync = 262 * CyclesPerHSync;
- private const int CyclesPerFlash = 16 * CyclesPerVSync;
+ private const int CyclesPerSecond = 1022730;
+
+ private const int HCountPreset = 0x40; // hcount preset after hcount overflows -> HPE' low [3-13]
+ private const int HCountLeaveHBlank = 0x58; // hcount when leaving hblank [3-15]
+ private const int VCountPresetNtsc = 0xFA; // vcount preset after vcount overflows (NTSC) [3-13]
+ private const int VCountPresetPal = 0xC8; // vcount preset after vcount overflows (PAL) [3-17]
+ private const int VLineEnterVBlank = 192; // vline when entering vblank (NTSC & PAL) [3-17]
+ private const int VLineTriggerPreset = 256; // vline when vcount overflows and presets (NTSC & PAL) [3-15, 3-16]
+ private const int VLineLeaveVBlankNtsc = 262; // vline when leaving vblank (NTSC) [3-13]
+ private const int VLineLeaveVBlankPal = 312; // vline when leaving vblank (PAL) [3-17]
+ private const int VSyncsPerFlash = 16; // flash count using vcount overflow [3-17]
public const int ModeCount = 16;
@@ -1622,7 +1630,7 @@ public partial class Video
private readonly Action[] FlushRowMode;
private const int Width = 560;
- private const int Height = 192;
+ private const int Height = VLineEnterVBlank;
private const int TextHeight = 8;
private const int TextRows = Height / TextHeight;
diff --git a/Virtu/Wpf/MainWindow.xaml.cs b/Virtu/Wpf/MainWindow.xaml.cs
index 310711d..3d7d54f 100644
--- a/Virtu/Wpf/MainWindow.xaml.cs
+++ b/Virtu/Wpf/MainWindow.xaml.cs
@@ -26,11 +26,11 @@ public MainWindow()
_machine.Services.AddService(typeof(VideoService), _videoService);
Loaded += (sender, e) => _machine.Start();
- CompositionTarget.Rendering += CompositionTarget_Rendering;
+ CompositionTarget.Rendering += OnCompositionTargetRendering;
Application.Current.Exit += (sender, e) => _machine.Stop();
- _disk1Button.Click += (sender, e) => DiskButton_Click(0);
- _disk2Button.Click += (sender, e) => DiskButton_Click(1);
+ _disk1Button.Click += (sender, e) => OnDiskButtonClick(0);
+ _disk2Button.Click += (sender, e) => OnDiskButtonClick(1);
}
public void Dispose()
@@ -43,14 +43,14 @@ public void Dispose()
_videoService.Dispose();
}
- private void CompositionTarget_Rendering(object sender, EventArgs e)
+ private void OnCompositionTargetRendering(object sender, EventArgs e)
{
_keyboardService.Update();
_gamePortService.Update();
_videoService.Update();
}
- private void DiskButton_Click(int drive)
+ private void OnDiskButtonClick(int drive)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "Disk Files (*.dsk;*.nib)|*.dsk;*.nib|All Files (*.*)|*.*";
diff --git a/Virtu/Wpf/Services/WpfAudioService.cs b/Virtu/Wpf/Services/WpfAudioService.cs
index 744f764..b92d7c6 100644
--- a/Virtu/Wpf/Services/WpfAudioService.cs
+++ b/Virtu/Wpf/Services/WpfAudioService.cs
@@ -18,7 +18,7 @@ public WpfAudioService(Machine machine, Window window) :
_window = window;
_window.SourceInitialized += (sender, e) => _directSound.Start(_window.GetHandle());
- _directSound.Update += DirectSound_Update;
+ _directSound.Update += OnDirectSoundUpdate;
_window.Closed += (sender, e) => _directSound.Stop();
}
@@ -32,7 +32,7 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}
- private void DirectSound_Update(object sender, DirectSoundUpdateEventArgs e)
+ private void OnDirectSoundUpdate(object sender, DirectSoundUpdateEventArgs e)
{
IntPtr buffer = e.Buffer;
Update(e.BufferSize, (source, count) =>
diff --git a/Virtu/Wpf/Services/WpfKeyboardService.cs b/Virtu/Wpf/Services/WpfKeyboardService.cs
index 127beb1..ced7c04 100644
--- a/Virtu/Wpf/Services/WpfKeyboardService.cs
+++ b/Virtu/Wpf/Services/WpfKeyboardService.cs
@@ -18,8 +18,8 @@ public WpfKeyboardService(Machine machine, Window window) :
_window = window;
- _window.KeyDown += Window_KeyDown;
- _window.KeyUp += Window_KeyUp;
+ _window.KeyDown += OnWindowKeyDown;
+ _window.KeyUp += OnWindowKeyUp;
_window.GotKeyboardFocus += (sender, e) => _updateAnyKeyDown = true;
}
@@ -61,7 +61,7 @@ private bool IsKeyDown(Key key)
return _states[(int)key];
}
- private void Window_KeyDown(object sender, KeyEventArgs e)
+ private void OnWindowKeyDown(object sender, KeyEventArgs e)
{
_states[(int)((e.Key == Key.System) ? e.SystemKey : e.Key)] = true;
IsAnyKeyDown = true;
@@ -69,12 +69,12 @@ private void Window_KeyDown(object sender, KeyEventArgs e)
int asciiKey = GetAsciiKey(e.Key, e.KeyboardDevice);
if (asciiKey >= 0)
{
- RaiseAsciiKeyDown(asciiKey);
+ OnAsciiKeyDown(asciiKey);
e.Handled = true;
}
}
- private void Window_KeyUp(object sender, KeyEventArgs e)
+ private void OnWindowKeyUp(object sender, KeyEventArgs e)
{
_states[(int)((e.Key == Key.System) ? e.SystemKey : e.Key)] = false;
_updateAnyKeyDown = true;
diff --git a/Virtu/Xna/Services/XnaAudioService.cs b/Virtu/Xna/Services/XnaAudioService.cs
index 885a261..32c1857 100644
--- a/Virtu/Xna/Services/XnaAudioService.cs
+++ b/Virtu/Xna/Services/XnaAudioService.cs
@@ -18,7 +18,7 @@ public XnaAudioService(Machine machine, GameBase game) :
_game = game;
_directSound.Start(_game.Window.Handle);
- _directSound.Update += DirectSound_Update;
+ _directSound.Update += OnDirectSoundUpdate;
_game.Exiting += (sender, e) => _directSound.Stop();
}
@@ -30,7 +30,7 @@ protected override void Dispose(bool disposing)
}
}
- private void DirectSound_Update(object sender, DirectSoundUpdateEventArgs e)
+ private void OnDirectSoundUpdate(object sender, DirectSoundUpdateEventArgs e)
{
IntPtr buffer = e.Buffer;
Update(e.BufferSize, (source, count) =>
diff --git a/Virtu/Xna/Services/XnaKeyboardService.cs b/Virtu/Xna/Services/XnaKeyboardService.cs
index 72f91bb..a40f6da 100644
--- a/Virtu/Xna/Services/XnaKeyboardService.cs
+++ b/Virtu/Xna/Services/XnaKeyboardService.cs
@@ -48,7 +48,7 @@ public override void Update()
#endif
if (asciiKey >= 0)
{
- RaiseAsciiKeyDown(asciiKey);
+ OnAsciiKeyDown(asciiKey);
}
}
}
@@ -73,7 +73,7 @@ public override void Update()
#endif
if (asciiKey >= 0)
{
- RaiseAsciiKeyDown(asciiKey);
+ OnAsciiKeyDown(asciiKey);
}
}
}
diff --git a/Virtu/Xna/Services/XnaVideoService.cs b/Virtu/Xna/Services/XnaVideoService.cs
index bf72658..264ace8 100644
--- a/Virtu/Xna/Services/XnaVideoService.cs
+++ b/Virtu/Xna/Services/XnaVideoService.cs
@@ -18,8 +18,8 @@ public XnaVideoService(Machine machine, GameBase game) :
_game = game;
- _game.GraphicsDeviceManager.PreparingDeviceSettings += GraphicsDeviceManager_PreparingDeviceSettings;
- _game.GraphicsDeviceService.DeviceCreated += GraphicsDeviceService_DeviceCreated;
+ _game.GraphicsDeviceManager.PreparingDeviceSettings += OnGraphicsDeviceManagerPreparingDeviceSettings;
+ _game.GraphicsDeviceService.DeviceCreated += OnGraphicsDeviceServiceDeviceCreated;
_game.GraphicsDeviceService.DeviceReset += (sender, e) => SetTexturePosition();
}
@@ -68,7 +68,7 @@ protected override void Dispose(bool disposing)
}
}
- private void GraphicsDeviceManager_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
+ private void OnGraphicsDeviceManagerPreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
{
DisplayMode displayMode = e.GraphicsDeviceInformation.Adapter.CurrentDisplayMode;
PresentationParameters presentationParameters = e.GraphicsDeviceInformation.PresentationParameters;
@@ -83,7 +83,7 @@ private void GraphicsDeviceManager_PreparingDeviceSettings(object sender, Prepar
}
}
- private void GraphicsDeviceService_DeviceCreated(object sender, EventArgs e)
+ private void OnGraphicsDeviceServiceDeviceCreated(object sender, EventArgs e)
{
_graphicsDevice = _game.GraphicsDevice;
_spriteBatch = new SpriteBatch(_graphicsDevice);