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);