Cosmetic changes.

Implemented video scanner and floating bus.

--HG--
extra : convert_revision : svn%3Affd33b8c-2492-42e0-bdc5-587b920b7d6d/trunk%4035682
This commit is contained in:
Sean Fausett 2009-12-13 05:53:53 +00:00
parent acd7892436
commit ae126d2552
23 changed files with 150 additions and 160 deletions

View File

@ -9,42 +9,18 @@ namespace Jellyfish.Library
public sealed partial class DirectSound public sealed partial class DirectSound
{ {
[Flags] [Flags]
private enum BufferCapabilities private enum BufferCapabilities { PrimaryBuffer = 0x00000001, CtrlPositionNotify = 0x00000100, StickyFocus = 0x00004000, GlobalFocus = 0x00008000 }
{
PrimaryBuffer = 0x00000001,
CtrlPositionNotify = 0x00000100,
StickyFocus = 0x00004000,
GlobalFocus = 0x00008000
}
[Flags] [Flags]
private enum BufferLock private enum BufferLock { None = 0x00000000, FromWriteCursor = 0x00000001, EntireBuffer = 0x00000002 }
{
None = 0x00000000,
FromWriteCursor = 0x00000001,
EntireBuffer = 0x00000002
}
[Flags] [Flags]
private enum BufferPlay private enum BufferPlay { Looping = 0x00000001 }
{
Looping = 0x00000001
}
[Flags] [Flags]
private enum BufferStatus private enum BufferStatus { Playing = 0x00000001, BufferLost = 0x00000002, Looping = 0x00000004, Terminated = 0x00000020 }
{
Playing = 0x00000001,
BufferLost = 0x00000002,
Looping = 0x00000004,
Terminated = 0x00000020
}
private enum CooperativeLevel private enum CooperativeLevel { Normal = 1, Priority = 2 }
{
Normal = 1,
Priority = 2
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
private sealed class BufferDescription private sealed class BufferDescription

View File

@ -10,16 +10,16 @@ public ApplicationBase(string name)
{ {
Name = name; Name = name;
UnhandledException += Application_UnhandledException; UnhandledException += OnApplicationUnhandledException;
//AppDomain.CurrentDomain.UnhandledException += AppDomain_UnhandledException; //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); 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); // MessageBox.Show(GetExceptionMessage(e.ExceptionObject as Exception), GetExceptionCaption("AppDomain Exception", e.IsTerminating), MessageBoxButton.OK);
//} //}

View File

@ -11,10 +11,10 @@ public FrameRateCounter()
{ {
InitializeComponent(); InitializeComponent();
CompositionTarget.Rendering += CompositionTarget_Rendering; CompositionTarget.Rendering += OnCompositionTargetRendering;
} }
private void CompositionTarget_Rendering(object sender, EventArgs e) private void OnCompositionTargetRendering(object sender, EventArgs e)
{ {
_frameCount++; _frameCount++;

View File

@ -14,18 +14,18 @@ public ApplicationBase(string name)
{ {
Name = name; Name = name;
DispatcherUnhandledException += Application_DispatcherUnhandledException; DispatcherUnhandledException += OnApplicationDispatcherUnhandledException;
AppDomain.CurrentDomain.UnhandledException += AppDomain_UnhandledException; 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)); MessageBox.Show(GetExceptionMessage(e.Exception), GetExceptionCaption("Application Dispatcher Exception", true));
Shutdown(); Shutdown();
e.Handled = true; 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)); MessageBox.Show(GetExceptionMessage(e.ExceptionObject as Exception), GetExceptionCaption("AppDomain Exception", e.IsTerminating));
} }

View File

@ -11,10 +11,10 @@ public FrameRateCounter()
{ {
InitializeComponent(); InitializeComponent();
CompositionTarget.Rendering += CompositionTarget_Rendering; CompositionTarget.Rendering += OnCompositionTargetRendering;
} }
private void CompositionTarget_Rendering(object sender, EventArgs e) private void OnCompositionTargetRendering(object sender, EventArgs e)
{ {
_frameCount++; _frameCount++;

View File

@ -10,8 +10,6 @@ public sealed partial class Cpu : MachineComponent
public Cpu(Machine machine) : public Cpu(Machine machine) :
base(machine) base(machine)
{ {
_updateEvent = UpdateEvent; // cache delegate; avoids garbage
ExecuteOpcode65N02 = new Action[OpcodeCount] ExecuteOpcode65N02 = new Action[OpcodeCount]
{ {
Execute65X02Brk00, Execute65X02Ora01, Execute65N02Nop02, Execute65N02Nop03, Execute65X02Brk00, Execute65X02Ora01, Execute65N02Nop02, Execute65N02Nop03,
@ -157,7 +155,7 @@ public override void Initialize()
UpdateSettings(); UpdateSettings();
Machine.Events.AddEvent(CyclesPerUpdate, _updateEvent); Machine.Video.VSync += OnVideoVSync;
} }
public override void Reset() public override void Reset()
@ -3212,21 +3210,20 @@ private void Execute65C02Tsb0C() // tsb abs
} }
#endregion #endregion
private void UpdateEvent() private void OnVideoVSync(object sender, EventArgs e)
{ {
UpdateSettings(); UpdateSettings();
if (Machine.Settings.Cpu.IsThrottled) if (Machine.Settings.Cpu.IsThrottled)
{ {
long elapsedTime = DateTime.UtcNow.Ticks - _lastTime; 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; _lastTime = DateTime.UtcNow.Ticks;
} }
Machine.Events.AddEvent(CyclesPerUpdate, _updateEvent);
} }
private void UpdateSettings() private void UpdateSettings()
@ -3245,8 +3242,6 @@ private void UpdateSettings()
public int Opcode { get; private set; } public int Opcode { get; private set; }
public long Cycles { get; private set; } public long Cycles { get; private set; }
private Action _updateEvent;
private Memory _memory; private Memory _memory;
private Action[] _executeOpcode; private Action[] _executeOpcode;

View File

@ -4,11 +4,6 @@ namespace Jellyfish.Virtu
{ {
public partial class Cpu 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 const int OpcodeCount = 256;
private readonly Action[] ExecuteOpcode65N02; private readonly Action[] ExecuteOpcode65N02;

View File

@ -96,19 +96,6 @@
</Member> </Member>
</Members> </Members>
</Type> </Type>
<Type Name="Video">
<Members>
<Member Name="#ReadFloatingBus()">
<Messages>
<Message TypeName="MarkMembersAsStatic" Category="Microsoft.Performance" CheckId="CA1822" Created="2009-12-11 09:07:49Z" FixCategory="DependsOnFix">
<Issue>
<Item>'Video.ReadFloatingBus()'</Item>
</Issue>
</Message>
</Messages>
</Member>
</Members>
</Type>
</Types> </Types>
</Namespace> </Namespace>
</Namespaces> </Namespaces>
@ -153,19 +140,6 @@
</Member> </Member>
</Members> </Members>
</Type> </Type>
<Type Name="Video">
<Members>
<Member Name="#ReadFloatingBus()">
<Messages>
<Message TypeName="MarkMembersAsStatic" Category="Microsoft.Performance" CheckId="CA1822" Created="2009-04-13 00:02:55Z" FixCategory="DependsOnFix">
<Issue>
<Item>'Video.ReadFloatingBus()'</Item>
</Issue>
</Message>
</Messages>
</Member>
</Members>
</Type>
</Types> </Types>
</Namespace> </Namespace>
</Namespaces> </Namespaces>
@ -180,9 +154,6 @@
<Rule TypeName="DoNotRaiseReservedExceptionTypes" Category="Microsoft.Usage" CheckId="CA2201"> <Rule TypeName="DoNotRaiseReservedExceptionTypes" Category="Microsoft.Usage" CheckId="CA2201">
<Resolution Name="TooGeneric">{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.</Resolution> <Resolution Name="TooGeneric">{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.</Resolution>
</Rule> </Rule>
<Rule TypeName="MarkMembersAsStatic" Category="Microsoft.Performance" CheckId="CA1822">
<Resolution Name="Default">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.</Resolution>
</Rule>
<Rule TypeName="RemoveUnusedLocals" Category="Microsoft.Performance" CheckId="CA1804"> <Rule TypeName="RemoveUnusedLocals" Category="Microsoft.Performance" CheckId="CA1804">
<Resolution Name="Default">{0} declares a variable, {1}, of type {2}, which is never used or is only assigned to. Use this variable or remove it.</Resolution> <Resolution Name="Default">{0} declares a variable, {1}, of type {2}, which is never used or is only assigned to. Use this variable or remove it.</Resolution>
</Rule> </Rule>

View File

@ -10,7 +10,6 @@ public sealed class Keyboard : MachineComponent
public Keyboard(Machine machine) : public Keyboard(Machine machine) :
base(machine) base(machine)
{ {
_pollEvent = PollEvent; // cache delegate; avoids garbage
} }
public override void Initialize() public override void Initialize()
@ -19,8 +18,7 @@ public override void Initialize()
_gamePortService = Machine.Services.GetService<GamePortService>(); _gamePortService = Machine.Services.GetService<GamePortService>();
_keyboardService.AsciiKeyDown += (sender, e) => Latch = e.AsciiKey; _keyboardService.AsciiKeyDown += (sender, e) => Latch = e.AsciiKey;
Machine.Video.VSync += OnVideoVSync;
Machine.Events.AddEvent(CyclesPerPoll, _pollEvent);
} }
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
@ -123,7 +121,7 @@ public void ResetStrobe()
Strobe = false; Strobe = false;
} }
private void PollEvent() private void OnVideoVSync(object sender, EventArgs e)
{ {
if (_keyboardService.IsResetKeyDown) if (_keyboardService.IsResetKeyDown)
{ {
@ -145,8 +143,6 @@ private void PollEvent()
Machine.Video.ToggleMonochrome(); Machine.Video.ToggleMonochrome();
_keyboardService.WaitForKeyUp(); _keyboardService.WaitForKeyUp();
} }
Machine.Events.AddEvent(CyclesPerPoll, _pollEvent);
} }
public bool IsAnyKeyDown { get { return _keyboardService.IsAnyKeyDown; } } 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 int Latch { get { return _latch; } set { _latch = value; Strobe = true; } }
private const int CyclesPerPoll = 17030;
private Action _pollEvent;
private KeyboardService _keyboardService; private KeyboardService _keyboardService;
private GamePortService _gamePortService; private GamePortService _gamePortService;

View File

@ -83,7 +83,7 @@ private void Run() // machine thread
{ {
do do
{ {
Events.RaiseEvents(Cpu.Execute()); Events.HandleEvents(Cpu.Execute());
} }
while (State == MachineState.Running); while (State == MachineState.Running);

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
namespace Jellyfish.Virtu namespace Jellyfish.Virtu
@ -78,8 +77,7 @@ public int FindEvent(Action action)
return 0; return 0;
} }
[SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] public void HandleEvents(int delta)
public void RaiseEvents(int delta)
{ {
LinkedListNode<MachineEvent> node = _used.First; LinkedListNode<MachineEvent> node = _used.First;
node.Value.Delta -= delta; node.Value.Delta -= delta;

View File

@ -38,7 +38,7 @@ public MachineSettings()
}; };
Video = new VideoSettings Video = new VideoSettings
{ {
IsFullScreen = false, IsMonochrome = false, ScannerType = 0, IsFullScreen = false, IsMonochrome = false, ScannerModes = ScannerModes.None,
Color = new ColorSettings Color = new ColorSettings
{ {
Black = 0x000000, Black = 0x000000,
@ -172,8 +172,8 @@ public void Deserialize(Stream stream)
Video = new VideoSettings Video = new VideoSettings
{ {
IsFullScreen = (bool)video.Attribute("IsFullScreen"), IsFullScreen = (bool)video.Attribute("IsFullScreen"),
IsMonochrome = (bool)video.Attribute("IsMonochrome"), IsMonochrome = (bool)video.Attribute("IsMonochrome"),
ScannerType = (int)video.Attribute("ScannerType"), ScannerModes = (ScannerModes)Enum.Parse(typeof(ScannerModes), (string)video.Attribute("ScannerModes"), true),
Color = new ColorSettings Color = new ColorSettings
{ {
Black = (uint)color.Attribute("Black"), Black = (uint)color.Attribute("Black"),
@ -269,7 +269,7 @@ public void Serialize(Stream stream)
new XElement(ns + "Video", new XElement(ns + "Video",
new XAttribute("IsFullScreen", Video.IsFullScreen), new XAttribute("IsFullScreen", Video.IsFullScreen),
new XAttribute("IsMonochrome", Video.IsMonochrome), new XAttribute("IsMonochrome", Video.IsMonochrome),
new XAttribute("ScannerType", Video.ScannerType), new XAttribute("ScannerModes", Video.ScannerModes),
new XElement(ns + "Color", new XElement(ns + "Color",
new XAttribute("Black", Video.Color.Black), new XAttribute("Black", Video.Color.Black),
new XAttribute("DarkBlue", Video.Color.DarkBlue), new XAttribute("DarkBlue", Video.Color.DarkBlue),
@ -377,11 +377,14 @@ public sealed class ColorSettings
public uint Monochrome { get; set; } public uint Monochrome { get; set; }
} }
[Flags]
public enum ScannerModes { None = 0x0, AppleII = 0x1, Pal = 0x2 } // defaults to AppleIIe, Ntsc
public sealed class VideoSettings public sealed class VideoSettings
{ {
public bool IsFullScreen { get; set; } public bool IsFullScreen { get; set; }
public bool IsMonochrome { get; set; } public bool IsMonochrome { get; set; }
public int ScannerType { get; set; } public ScannerModes ScannerModes { get; set; }
public ColorSettings Color { get; set; } public ColorSettings Color { get; set; }
}; };
} }

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading; using System.Threading;
namespace Jellyfish.Virtu.Services namespace Jellyfish.Virtu.Services
@ -31,8 +30,7 @@ protected KeyboardService(Machine machine) :
public abstract bool IsKeyDown(int key); public abstract bool IsKeyDown(int key);
[SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] protected void OnAsciiKeyDown(int asciiKey)
protected void RaiseAsciiKeyDown(int asciiKey)
{ {
EventHandler<AsciiKeyEventArgs> handler = AsciiKeyDown; EventHandler<AsciiKeyEventArgs> handler = AsciiKeyDown;
if (handler != null) if (handler != null)

View File

@ -26,11 +26,11 @@ public MainPage()
_machine.Services.AddService(typeof(VideoService), _videoService); _machine.Services.AddService(typeof(VideoService), _videoService);
Loaded += (sender, e) => _machine.Start(); Loaded += (sender, e) => _machine.Start();
CompositionTarget.Rendering += CompositionTarget_Rendering; CompositionTarget.Rendering += OnCompositionTargetRendering;
Application.Current.Exit += (sender, e) => _machine.Stop(); Application.Current.Exit += (sender, e) => _machine.Stop();
_disk1Button.Click += (sender, e) => DiskButton_Click(0); _disk1Button.Click += (sender, e) => OnDiskButtonClick(0);
_disk2Button.Click += (sender, e) => DiskButton_Click(1); _disk2Button.Click += (sender, e) => OnDiskButtonClick(1);
} }
public void Dispose() public void Dispose()
@ -43,14 +43,14 @@ public void Dispose()
_videoService.Dispose(); _videoService.Dispose();
} }
private void CompositionTarget_Rendering(object sender, EventArgs e) private void OnCompositionTargetRendering(object sender, EventArgs e)
{ {
_keyboardService.Update(); _keyboardService.Update();
_gamePortService.Update(); _gamePortService.Update();
_videoService.Update(); _videoService.Update();
} }
private void DiskButton_Click(int drive) private void OnDiskButtonClick(int drive)
{ {
OpenFileDialog dialog = new OpenFileDialog(); OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "Disk Files (*.dsk;*.nib)|*.dsk;*.nib|All Files (*.*)|*.*"; dialog.Filter = "Disk Files (*.dsk;*.nib)|*.dsk;*.nib|All Files (*.*)|*.*";

View File

@ -19,9 +19,9 @@ public SilverlightKeyboardService(Machine machine, UserControl page) :
_page = page; _page = page;
_page.KeyDown += Page_KeyDown; _page.KeyDown += OnPageKeyDown;
_page.KeyUp += Page_KeyUp; _page.KeyUp += OnPageKeyUp;
_page.LostFocus += Page_LostFocus; _page.LostFocus += OnPageLostFocus;
} }
public override bool IsKeyDown(int key) public override bool IsKeyDown(int key)
@ -62,7 +62,7 @@ private bool IsKeyDown(Key key)
return _states[(int)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; _states[(int)e.Key] = true;
IsAnyKeyDown = true; IsAnyKeyDown = true;
@ -70,19 +70,19 @@ private void Page_KeyDown(object sender, KeyEventArgs e)
int asciiKey = GetAsciiKey(e.Key, e.PlatformKeyCode); int asciiKey = GetAsciiKey(e.Key, e.PlatformKeyCode);
if (asciiKey >= 0) if (asciiKey >= 0)
{ {
RaiseAsciiKeyDown(asciiKey); OnAsciiKeyDown(asciiKey);
e.Handled = true; 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 _capsLock ^= (e.Key == Key.CapsLock); // SL is missing caps lock support; try to track manually
_states[(int)e.Key] = false; _states[(int)e.Key] = false;
_updateAnyKeyDown = true; _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; IsAnyKeyDown = false;
foreach (Key key in KeyValues) foreach (Key key in KeyValues)

View File

@ -29,9 +29,9 @@ public override void Initialize()
UpdateSettings(); UpdateSettings();
IsVBlank = true; IsVBlank = true;
Machine.Events.AddEvent((CyclesPerVBlank / 2), _leaveVBlankEvent); 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(_cyclesPerVSync, _resetVSyncEvent);
Machine.Events.AddEvent(CyclesPerFlash, _inverseTextEvent); Machine.Events.AddEvent(_cyclesPerFlash, _inverseTextEvent);
} }
public override void Reset() public override void Reset()
@ -92,8 +92,29 @@ public void DirtyScreenText()
public int ReadFloatingBus() public int ReadFloatingBus()
{ {
// TODO // derive scanner counters from current cycles into frame; assumes hcount and vcount preset at start of frame [3-13, 3-15, 3-16]
return 0x00; 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() public void SetCharSet()
@ -641,7 +662,7 @@ private void DrawDHiresM(int address, int x, int y)
#region Flush Methods #region Flush Methods
private void FlushRowMode0(int y) 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++) for (int x = 0; x < CellColumns; x++)
{ {
if (_isCellDirty[CellColumns * y + x]) if (_isCellDirty[CellColumns * y + x])
@ -654,7 +675,7 @@ private void FlushRowMode0(int y)
private void FlushRowMode1(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++) for (int x = 0; x < CellColumns; x++)
{ {
if (_isCellDirty[CellColumns * y + x]) if (_isCellDirty[CellColumns * y + x])
@ -667,7 +688,7 @@ private void FlushRowMode1(int y)
private void FlushRowMode2(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) for (int x = 0; x < 2 * CellColumns; x += 2)
{ {
if (_isCellDirty[CellColumns * y + x / 2]) if (_isCellDirty[CellColumns * y + x / 2])
@ -705,7 +726,7 @@ private void FlushRowMode4(int y)
private void FlushRowMode5(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 i = 0; i < CellHeight; i++, y++)
{ {
for (int x = 0; x < CellColumns; x++) for (int x = 0; x < CellColumns; x++)
@ -745,7 +766,7 @@ private void FlushRowMode7(int y)
private void FlushRowMode8(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++) for (int x = 0; x < CellColumns; x++)
{ {
if (_isCellDirty[CellColumns * y + x]) if (_isCellDirty[CellColumns * y + x])
@ -758,7 +779,7 @@ private void FlushRowMode8(int y)
private void FlushRowMode9(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) for (int x = 0; x < 2 * CellColumns; x += 2)
{ {
if (_isCellDirty[CellColumns * y + x / 2]) if (_isCellDirty[CellColumns * y + x / 2])
@ -796,7 +817,7 @@ private void FlushRowModeB(int y)
private void FlushRowModeC(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 i = 0; i < CellHeight; i++, y++)
{ {
for (int x = 0; x < CellColumns; x++) for (int x = 0; x < CellColumns; x++)
@ -812,7 +833,7 @@ private void FlushRowModeC(int y)
private void FlushRowModeD(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 i = 0; i < CellHeight; i++, y++)
{ {
for (int x = 0; x < CellColumns; x++) for (int x = 0; x < CellColumns; x++)
@ -854,7 +875,7 @@ private void FlushRowModeF(int y)
private void FlushRowEvent() 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 FlushRowMode[_memory.VideoMode](y - CellHeight); // in arrears
@ -866,7 +887,7 @@ private void FlushRowEvent()
{ {
IsVBlank = true; IsVBlank = true;
Machine.Events.AddEvent(CyclesPerVBlank, _leaveVBlankEvent); Machine.Events.AddEvent(_cyclesPerVBlank, _leaveVBlankEvent);
} }
} }
@ -886,7 +907,7 @@ private void InverseTextEvent()
DirtyScreenText(); DirtyScreenText();
Machine.Events.AddEvent(CyclesPerFlash, _inverseTextEvent); Machine.Events.AddEvent(_cyclesPerFlash, _inverseTextEvent);
} }
private void LeaveVBlankEvent() private void LeaveVBlankEvent()
@ -900,7 +921,13 @@ private void ResetVSyncEvent()
{ {
UpdateSettings(); 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) private void SetPixel(int x, int y, int color)
@ -949,6 +976,23 @@ private void UpdateSettings()
_colorPalette[ColorDHiresE] = settings.Color.Yellow; _colorPalette[ColorDHiresE] = settings.Color.Yellow;
_colorPalette[ColorDHiresF] = settings.Color.White; _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) if (_videoService.IsFullScreen != settings.IsFullScreen)
{ {
_videoService.ToggleFullScreen(); _videoService.ToggleFullScreen();
@ -956,6 +1000,9 @@ private void UpdateSettings()
} }
public bool IsVBlank { get; private set; } 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 _flushRowEvent;
private Action _inverseTextEvent; private Action _inverseTextEvent;
@ -969,5 +1016,12 @@ private void UpdateSettings()
private uint[] _colorPalette = new uint[ColorPaletteCount]; private uint[] _colorPalette = new uint[ColorPaletteCount];
private bool[] _isCellDirty = new bool[Height * CellColumns + 1]; // includes sentinel private bool[] _isCellDirty = new bool[Height * CellColumns + 1]; // includes sentinel
private bool _isTextInversed; private bool _isTextInversed;
private int _cyclesPerVBlank;
private int _cyclesPerVBlankPreset;
private int _cyclesPerVSync;
private int _cyclesPerFlash;
private int _vCountPreset;
private int _vLineLeaveVBlank;
private ScannerModes _scannerModes;
} }
} }

View File

@ -1596,9 +1596,17 @@ public partial class Video
private const int CyclesPerHBlank = 25; private const int CyclesPerHBlank = 25;
private const int CyclesPerHSync = 65; private const int CyclesPerHSync = 65;
private const int CyclesPerFlush = 8 * CyclesPerHSync; private const int CyclesPerFlush = 8 * CyclesPerHSync;
private const int CyclesPerVBlank = 70 * CyclesPerHSync; private const int CyclesPerSecond = 1022730;
private const int CyclesPerVSync = 262 * CyclesPerHSync;
private const int CyclesPerFlash = 16 * CyclesPerVSync; 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; public const int ModeCount = 16;
@ -1622,7 +1630,7 @@ public partial class Video
private readonly Action<int>[] FlushRowMode; private readonly Action<int>[] FlushRowMode;
private const int Width = 560; private const int Width = 560;
private const int Height = 192; private const int Height = VLineEnterVBlank;
private const int TextHeight = 8; private const int TextHeight = 8;
private const int TextRows = Height / TextHeight; private const int TextRows = Height / TextHeight;

View File

@ -26,11 +26,11 @@ public MainWindow()
_machine.Services.AddService(typeof(VideoService), _videoService); _machine.Services.AddService(typeof(VideoService), _videoService);
Loaded += (sender, e) => _machine.Start(); Loaded += (sender, e) => _machine.Start();
CompositionTarget.Rendering += CompositionTarget_Rendering; CompositionTarget.Rendering += OnCompositionTargetRendering;
Application.Current.Exit += (sender, e) => _machine.Stop(); Application.Current.Exit += (sender, e) => _machine.Stop();
_disk1Button.Click += (sender, e) => DiskButton_Click(0); _disk1Button.Click += (sender, e) => OnDiskButtonClick(0);
_disk2Button.Click += (sender, e) => DiskButton_Click(1); _disk2Button.Click += (sender, e) => OnDiskButtonClick(1);
} }
public void Dispose() public void Dispose()
@ -43,14 +43,14 @@ public void Dispose()
_videoService.Dispose(); _videoService.Dispose();
} }
private void CompositionTarget_Rendering(object sender, EventArgs e) private void OnCompositionTargetRendering(object sender, EventArgs e)
{ {
_keyboardService.Update(); _keyboardService.Update();
_gamePortService.Update(); _gamePortService.Update();
_videoService.Update(); _videoService.Update();
} }
private void DiskButton_Click(int drive) private void OnDiskButtonClick(int drive)
{ {
OpenFileDialog dialog = new OpenFileDialog(); OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "Disk Files (*.dsk;*.nib)|*.dsk;*.nib|All Files (*.*)|*.*"; dialog.Filter = "Disk Files (*.dsk;*.nib)|*.dsk;*.nib|All Files (*.*)|*.*";

View File

@ -18,7 +18,7 @@ public WpfAudioService(Machine machine, Window window) :
_window = window; _window = window;
_window.SourceInitialized += (sender, e) => _directSound.Start(_window.GetHandle()); _window.SourceInitialized += (sender, e) => _directSound.Start(_window.GetHandle());
_directSound.Update += DirectSound_Update; _directSound.Update += OnDirectSoundUpdate;
_window.Closed += (sender, e) => _directSound.Stop(); _window.Closed += (sender, e) => _directSound.Stop();
} }
@ -32,7 +32,7 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing); base.Dispose(disposing);
} }
private void DirectSound_Update(object sender, DirectSoundUpdateEventArgs e) private void OnDirectSoundUpdate(object sender, DirectSoundUpdateEventArgs e)
{ {
IntPtr buffer = e.Buffer; IntPtr buffer = e.Buffer;
Update(e.BufferSize, (source, count) => Update(e.BufferSize, (source, count) =>

View File

@ -18,8 +18,8 @@ public WpfKeyboardService(Machine machine, Window window) :
_window = window; _window = window;
_window.KeyDown += Window_KeyDown; _window.KeyDown += OnWindowKeyDown;
_window.KeyUp += Window_KeyUp; _window.KeyUp += OnWindowKeyUp;
_window.GotKeyboardFocus += (sender, e) => _updateAnyKeyDown = true; _window.GotKeyboardFocus += (sender, e) => _updateAnyKeyDown = true;
} }
@ -61,7 +61,7 @@ private bool IsKeyDown(Key key)
return _states[(int)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; _states[(int)((e.Key == Key.System) ? e.SystemKey : e.Key)] = true;
IsAnyKeyDown = true; IsAnyKeyDown = true;
@ -69,12 +69,12 @@ private void Window_KeyDown(object sender, KeyEventArgs e)
int asciiKey = GetAsciiKey(e.Key, e.KeyboardDevice); int asciiKey = GetAsciiKey(e.Key, e.KeyboardDevice);
if (asciiKey >= 0) if (asciiKey >= 0)
{ {
RaiseAsciiKeyDown(asciiKey); OnAsciiKeyDown(asciiKey);
e.Handled = true; 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; _states[(int)((e.Key == Key.System) ? e.SystemKey : e.Key)] = false;
_updateAnyKeyDown = true; _updateAnyKeyDown = true;

View File

@ -18,7 +18,7 @@ public XnaAudioService(Machine machine, GameBase game) :
_game = game; _game = game;
_directSound.Start(_game.Window.Handle); _directSound.Start(_game.Window.Handle);
_directSound.Update += DirectSound_Update; _directSound.Update += OnDirectSoundUpdate;
_game.Exiting += (sender, e) => _directSound.Stop(); _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; IntPtr buffer = e.Buffer;
Update(e.BufferSize, (source, count) => Update(e.BufferSize, (source, count) =>

View File

@ -48,7 +48,7 @@ public override void Update()
#endif #endif
if (asciiKey >= 0) if (asciiKey >= 0)
{ {
RaiseAsciiKeyDown(asciiKey); OnAsciiKeyDown(asciiKey);
} }
} }
} }
@ -73,7 +73,7 @@ public override void Update()
#endif #endif
if (asciiKey >= 0) if (asciiKey >= 0)
{ {
RaiseAsciiKeyDown(asciiKey); OnAsciiKeyDown(asciiKey);
} }
} }
} }

View File

@ -18,8 +18,8 @@ public XnaVideoService(Machine machine, GameBase game) :
_game = game; _game = game;
_game.GraphicsDeviceManager.PreparingDeviceSettings += GraphicsDeviceManager_PreparingDeviceSettings; _game.GraphicsDeviceManager.PreparingDeviceSettings += OnGraphicsDeviceManagerPreparingDeviceSettings;
_game.GraphicsDeviceService.DeviceCreated += GraphicsDeviceService_DeviceCreated; _game.GraphicsDeviceService.DeviceCreated += OnGraphicsDeviceServiceDeviceCreated;
_game.GraphicsDeviceService.DeviceReset += (sender, e) => SetTexturePosition(); _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; DisplayMode displayMode = e.GraphicsDeviceInformation.Adapter.CurrentDisplayMode;
PresentationParameters presentationParameters = e.GraphicsDeviceInformation.PresentationParameters; 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; _graphicsDevice = _game.GraphicsDevice;
_spriteBatch = new SpriteBatch(_graphicsDevice); _spriteBatch = new SpriteBatch(_graphicsDevice);