diff --git a/Main/Basic/HiResTimer.cs b/Main/Basic/HiResTimer.cs
new file mode 100644
index 0000000..06c6700
--- /dev/null
+++ b/Main/Basic/HiResTimer.cs
@@ -0,0 +1,236 @@
+///////////////////////////////////////////////////////////////////////////////
+// File: HiResTimer.cs
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (C) KGy SOFT, 2005-2019 - All Rights Reserved
+//
+// You should have received a copy of the LICENSE file at the top-level
+// directory of this distribution. If not, then this file is considered as
+// an illegal copy.
+//
+// Unauthorized copying of this file, via any medium is strictly prohibited.
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+
+namespace KGySoft.CoreLibraries
+{
+ ///
+ /// Represents a high resolution timer that allows precise timing even with sub-milliseconds intervals.
+ /// The timer executes on a separated high priority thread.
+ ///
+ public class HiResTimer
+ {
+ ///
+ /// The number of ticks per one millisecond.
+ ///
+ private static readonly float tickFrequency = 1000f / Stopwatch.Frequency;
+
+ private volatile float interval;
+ private volatile float ignoreElapsedThreshold = Single.PositiveInfinity;
+ private volatile bool isRunning;
+
+ ///
+ /// Occurs when the elapses.
+ ///
+ public event EventHandler Elapsed;
+
+
+ ///
+ /// Gets or sets the interval, in milliseconds, before event is triggered.
+ /// Fractional values are allowed, too. When zero, the event is triggered as often as possible.
+ ///
Default value: 1.0, if initialized by the default constructor; otherwise, as specified in the constructor.
+ ///
+ ///
+ /// The interval in milliseconds. For example, 1000 represents one second and 0.001 represents one microsecond.
+ ///
+ /// is negative or .
+ ///
+ /// Please note that if is smaller than 16, then the timer may consume much CPU when running.
+ ///
+ public float Interval
+ {
+ get => interval;
+ set
+ {
+ if (value < 0f || Single.IsNaN(value))
+ {
+ throw new ArgumentOutOfRangeException("This value is not allowed: " + value);
+ }
+ interval = value;
+ }
+ }
+
+ ///
+ /// Gets or sets a threshold value, in milliseconds, to ignore an event (and thus trying to catch up the timer)
+ /// if the next invoke is late by the given value. Value must not be zero but fractions are allowed.
+ ///
Default value: +∞.
+ ///
+ ///
+ ///
+ /// If the value of this property is too low (smaller than the execution time of the event), it may
+ /// cause that the event is never triggered again.
+ ///
+ ///
+ /// is zero or negative or .
+ public float IgnoreElapsedThreshold
+ {
+ get => ignoreElapsedThreshold;
+ set
+ {
+ if (value <= 0f || Single.IsNaN(value))
+ {
+ throw new ArgumentOutOfRangeException("This value is not allowed: " + value);
+ }
+ ignoreElapsedThreshold = value;
+ }
+ }
+
+ ///
+ /// Gets or sets whether the event should be triggered.
+ ///
Default value: .
+ ///
+ ///
+ /// if enabled; otherwise, .
+ ///
+ public bool Enabled
+ {
+ get => isRunning;
+ set
+ {
+ if (value)
+ Start();
+ else
+ Stop();
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class with 1ms interval.
+ ///
+ public HiResTimer() : this(1f)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with a specified .
+ ///
+ /// The time, in milliseconds, between events. Value must be non-negative. Fractional values are allowed.
+ /// is negative or .
+ public HiResTimer(float interval)
+ {
+ if (interval < 0f || Single.IsNaN(interval))
+ {
+ throw new ArgumentOutOfRangeException("This value is not allowed: " + interval);
+ }
+ this.interval = interval;
+ }
+
+ private static float ElapsedHiRes(Stopwatch stopwatch) => stopwatch.ElapsedTicks * tickFrequency;
+
+ ///
+ /// Starts raising the event by enabling the timer.
+ ///
+ public void Start()
+ {
+ if (isRunning)
+ return;
+
+ isRunning = true;
+ Thread thread = new Thread(ExecuteTimer) { Priority = ThreadPriority.Highest };
+ thread.Start();
+ }
+
+ ///
+ /// Stops raising the event by disabling the timer.
+ ///
+ public void Stop() => isRunning = false;
+
+ ///
+ /// The timer loop on a dedicated thread.
+ /// Works like an inverse SpinWait in terms of sleeping/spinning strategy: while SpinWait spins for short periods in the beginning and then starts to sleep,
+ /// this timer sleeps more often in the beginning (if there is enough time), and starts to spin just before triggering the next event.
+ ///
+ private void ExecuteTimer()
+ {
+ int fallouts = 0;
+ float nextTrigger = 0f;
+
+ Stopwatch stopwatch = new Stopwatch();
+ stopwatch.Start();
+
+ while (isRunning)
+ {
+ float intervalLocal = interval;
+ nextTrigger += intervalLocal;
+ float elapsed;
+
+ while (true)
+ {
+ elapsed = ElapsedHiRes(stopwatch);
+ float diff = nextTrigger - elapsed;
+ if (diff <= 0f)
+ break;
+
+ if (diff < 1f)
+ Thread.SpinWait(10);
+ else if (diff < 10f)
+ Thread.SpinWait(100);
+ else
+ {
+ // By default Sleep(1) lasts about 15.5 ms (if not configured otherwise for the application by WinMM, for example)
+ // so not allowing sleeping under 16 ms. Not sleeping for more than 50 ms so interval changes/stopping can be detected.
+ if (diff >= 16f)
+ Thread.Sleep(diff >= 100f ? 50 : 1);
+ else
+ {
+ Thread.SpinWait(1000);
+ Thread.Sleep(0);
+ }
+
+ // if we have a larger time to wait, we check if the interval has been changed in the meantime
+ float newInterval = interval;
+
+ // ReSharper disable once CompareOfFloatsByEqualityOperator
+ if (intervalLocal != newInterval)
+ {
+ nextTrigger += newInterval - intervalLocal;
+ intervalLocal = newInterval;
+ }
+ }
+
+ if (!isRunning)
+ return;
+ }
+
+
+ float delay = elapsed - nextTrigger;
+ if (delay >= ignoreElapsedThreshold)
+ {
+ fallouts += 1;
+ continue;
+ }
+
+ Elapsed?.Invoke(this, new HiResTimerElapsedEventArgs(delay, fallouts));
+ fallouts = 0;
+
+ // restarting the timer in every hour to prevent precision problems
+ if (stopwatch.Elapsed.TotalHours >= 1d)
+ {
+#if NET35
+ stopwatch.Reset();
+ stopwatch.Start();
+#else
+ stopwatch.Restart();
+#endif
+ nextTrigger = 0f;
+ }
+ }
+
+ stopwatch.Stop();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Main/Basic/HiResTimerElapsedEventArgs.cs b/Main/Basic/HiResTimerElapsedEventArgs.cs
new file mode 100644
index 0000000..5fb45c9
--- /dev/null
+++ b/Main/Basic/HiResTimerElapsedEventArgs.cs
@@ -0,0 +1,57 @@
+#region Copyright
+
+///////////////////////////////////////////////////////////////////////////////
+// File: HiResTimerElapsedEventArgs.cs
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (C) KGy SOFT, 2005-2019 - All Rights Reserved
+//
+// You should have received a copy of the LICENSE file at the top-level
+// directory of this distribution. If not, then this file is considered as
+// an illegal copy.
+//
+// Unauthorized copying of this file, via any medium is strictly prohibited.
+///////////////////////////////////////////////////////////////////////////////
+
+#endregion
+
+#region Usings
+
+using System;
+
+#endregion
+
+namespace KGySoft.CoreLibraries
+{
+ ///
+ /// Provides data for the HiResTimer.Elapsed event.
+ ///
+ public class HiResTimerElapsedEventArgs : EventArgs
+ {
+ #region Properties
+
+ ///
+ /// Gets the delay, in milliseconds, of the triggering of the HiResTimer.Elapsed event
+ /// compared to when it should have been called.
+ ///
+ public float Delay { get; }
+
+ ///
+ /// Gets the number of the fallen out HiResTimer.Enabled events since the last invoke.
+ /// The value is nonzero if a larger delay occurred than the value of the HiResTimer.IgnoreElapsedThreshold property
+ /// and thus one or more HiResTimer.Elapsed events were skipped to catch up the timer.
+ ///
+ public int Fallouts { get; }
+
+ #endregion
+
+ #region Constructors
+
+ internal HiResTimerElapsedEventArgs(float delay, int fallouts)
+ {
+ Delay = delay;
+ Fallouts = fallouts;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Main/Basic/MultimediaTimer.cs b/Main/Basic/MultimediaTimer.cs
deleted file mode 100644
index 4067f14..0000000
--- a/Main/Basic/MultimediaTimer.cs
+++ /dev/null
@@ -1,376 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using System.Diagnostics;
-
-namespace FoenixIDE.Timers
-{
- // This code was added from a www source.
- // Thanks to Laureano Lopez http://www.linkedin.com/in/lopezlaureano.
- // Source: http://www.softwareinteractions.com/blog/2009/12/7/using-the-multimedia-timer-from-c.html
-
-
- // Summary:
- // Represents the method that will handle the
- // System.Timers.MultimediaTimer.Elapsed event
- // of a System.Timers.MultimediaTimer.
- //
- // Parameters:
- // sender:
- // The source of the event.
- //
- // e:
- // An System.Timers.MultimediaElapsedEventHandler object that contains
- // the event data.
- public delegate void MultimediaElapsedEventHandler(object sender,
- MultimediaElapsedEventArgs e);
-
- // Summary:
- // Provides data for the System.Timers.Timer.Elapsed event.
- public class MultimediaElapsedEventArgs : EventArgs
- {
- // Summary:
- // Gets the time the System.Timers.Multimedia.Elapsed event was
- // raised.
- //
- // Returns:
- // The time the System.Timers.Multimedia.Elapsed event was raised.
- public DateTime SignalTime { get; internal set; }
-
- internal MultimediaElapsedEventArgs()
- {
- SignalTime = DateTime.Now;
- }
- }
-
- // Summary:
- // Generates recurring events in an application.
- public class MultimediaTimer : IDisposable
- {
- //Lib API declarations
- ///
- /// Times the set event.
- ///
- /// The u delay.
- /// The u resolution.
- /// The lp time proc.
- /// The dw user.
- /// The fu event.
- ///
- [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
- private static extern uint timeSetEvent(uint uDelay, uint uResolution,
- TimerCallback lpTimeProc, UIntPtr dwUser, uint fuEvent);
-
- ///
- /// Times the kill event.
- ///
- /// The u timer ID.
- ///
- [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
- private static extern uint timeKillEvent(uint uTimerID);
-
- ///
- /// Times the get time.
- ///
- ///
- [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
- private static extern uint timeGetTime();
-
- ///
- /// Times the begin period.
- ///
- /// The u period.
- ///
- [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
- private static extern uint timeBeginPeriod(uint uPeriod);
-
- ///
- /// Times the end period.
- ///
- /// The u period.
- ///
- [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
- private static extern uint timeEndPeriod(uint uPeriod);
-
- // Use this to pin the timerCallback functions to avoid improper garbage collection
- private GCHandle _gcHandle;
-
-
- ///
- ///Timer type definitions
- ///
- [Flags]
- public enum fuEvent : uint
- {
- ///
- /// OneHzSignalEvent occurs once, after uDelay milliseconds.
- ///
- TIME_ONESHOT = 0,
- ///
- ///
- ///
- TIME_PERIODIC = 1,
- ///
- /// callback is function
- ///
- TIME_CALLBACK_FUNCTION = 0x0000,
-
- }
-
- ///
- /// Delegate definition for the API callback
- ///
- private delegate void TimerCallback(uint uTimerID, uint uMsg,
- UIntPtr dwUser, UIntPtr dw1, UIntPtr dw2);
-
- ///
- /// The current timer instance ID
- ///
- private uint id = 0;
-
- ///
- /// The callback used by the the API
- ///
- private TimerCallback timerCallback;
-
-
- ///
- /// Initializes a new instance of the System.Timers.MultimediaTimer
- // class, and sets all the properties to their initial values.
- ///
- public MultimediaTimer()
- {
- Interval = 100;
- AutoReset = true;
- Enabled = false;
- //Initialize the API callback
- timerCallback = CallbackFunction;
- // pin the timerCallback to a fixed memory address, such that the c# GC won't mess with it.
- _gcHandle = GCHandle.Alloc(timerCallback);
- }
-
- ///
- /// Initializes a new instance of the System.Timers.MultimediaTimer
- /// class, and sets the
- /// System.Timers.MultimediaTimer.Interval property to the specified
- /// time period.
- ///
- /// Parameters:
- /// interval:
- /// The time, in milliseconds, between events.
- ///
- /// Exceptions:
- /// System.ArgumentException:
- /// The value of the interval parameter is less than or equal to
- /// zero, or greater than System.Int32.MaxValue.
- ///
- /// The interval.
- public MultimediaTimer(uint interval)
- {
- Interval = interval;
- AutoReset = true;
- Enabled = false;
- //Initialize the API callback
- timerCallback = CallbackFunction;
- _gcHandle = GCHandle.Alloc(timerCallback);
- }
-
-
- ///
- /// Gets or sets a value indicating whether the
- /// System.Timers.MultimediaTimer should raise
- /// the System.Timers.MultimediaTimer.Elapsed event each time the
- /// specified interval elapses
- /// or only after the first time it elapses.
- ///
- /// Returns:
- /// true if the System.Timers.MultimediaTimer should raise the
- /// System.Timers.MultimediaTimer.Elapsed
- /// event each time the interval elapses; false if it should raise
- /// the System.Timers.MultimediaTimer.Elapsed
- /// event only once, after the first time the interval elapses. The
- /// default is true.
- ///
- /// true if [auto reset]; otherwise, false.
- public bool AutoReset { get; set; }
-
- ///
- /// Gets or sets a value indicating whether the
- /// System.Timers.MultimediaTimer should raise
- /// the System.Timers.MultimediaTimer.Elapsed event.
- ///
- /// Returns:
- /// true if the System.Timers.MultimediaTimer should raise the
- /// System.Timers.MultimediaTimer.Elapsed
- /// event; otherwise, false. The default is false.
- ///
- ///
- /// true if enabled; otherwise, false.
- public bool Enabled { get; private set; }
-
- private object syncLock = new object();
-
- ///
- /// Gets or sets the interval at which to raise the
- /// System.Timers.MultimediaTimer.Elapsed event.
- ///
- /// Returns:
- /// The time, in milliseconds, between raisings of the
- /// System.Timers.MultimediaTimer.Elapsed
- /// event. The default is 100 milliseconds.
- ///
- /// Exceptions:
- /// System.ArgumentException:
- /// The interval is less than or equal to zero.
- ///
- /// The interval.
- public uint Interval { get; set; }
-
-
- ///
- /// Occurs when the interval elapses.
- ///
- public event MultimediaElapsedEventHandler Elapsed;
-
-
- ///
- /// Releases the resources used by the System.Timers.MultimediaTimer.
- ///
- public void Close()
- {
- Dispose();
- }
-
-
- ///
- /// Starts raising the System.Timers.MultimediaTimer.Elapsed event by
- /// setting System.Timers.MultimediaTimer.Enabled
- /// to true.
- ///
- /// Exceptions:
- /// System.ArgumentOutOfRangeException:
- /// The System.Timers.MultimediaTimer is created with an interval
- /// equal to or greater than
- /// System.Int32.MaxValue + 1, or set to an interval less than zero.
- ///
- public void Start()
- {
- lock (syncLock)
- {
- //Kill any existing timer
- Stop();
- Enabled = false;
-
- //Set the timer type flags
- fuEvent f = fuEvent.TIME_CALLBACK_FUNCTION | (AutoReset ?
- fuEvent.TIME_PERIODIC : fuEvent.TIME_ONESHOT);
-
- id = timeSetEvent(Interval, 0, timerCallback, UIntPtr.Zero,
- (uint)f);
- if (id == 0)
- {
- throw new Exception("timeSetEvent error");
- }
- Debug.WriteLine("MultimediaTimer " + id.ToString() + " started");
- Enabled = true;
- }
- }
-
-
- ///
- /// Stops raising the System.Timers.MultimediaTimer.Elapsed event by
- /// setting System.Timers.MultimediaTimer.Enabled
- /// to false.
- ///
- public void Stop()
- {
- lock (syncLock)
- {
- if (id != 0)
- {
- timeKillEvent(id);
- Debug.WriteLine("MultimediaTimer " + id.ToString() + " stopped");
- id = 0;
- Enabled = false;
- }
- }
- }
-
- ///
- /// Called when [timer].
- ///
- protected virtual void OnTimer()
- {
- Elapsed?.Invoke(this, new MultimediaElapsedEventArgs());
- }
-
- ///
- /// CBs the func.
- ///
- /// The u timer ID.
- /// The u MSG.
- /// The dw user.
- /// The DW1.
- /// The DW2.
- private void CallbackFunction(uint uTimerID, uint uMsg, UIntPtr dwUser,
- UIntPtr dw1, UIntPtr dw2)
- {
- //Callback from the MultimediaTimer API that fires the Timer event.
- // Note we are in a different thread here
- OnTimer();
- }
-
- #region IDisposable Members
-
- private bool _disposed = false;
-
-
- ///
- /// Performs application-defined tasks associated with freeing,
- /// releasing, or resetting unmanaged resources.
- /// Releases all resources used by the current
- /// System.Timers.MultimediaTimer.
- ///
- /// Parameters:
- /// disposing:
- /// true to release both managed and unmanaged resources; false to
- /// release only
- /// unmanaged resources.
- ///
- public void Dispose()
- {
- _gcHandle.Free();
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- ///
- /// Releases unmanaged and - optionally - managed resources
- ///
- /// true to release both managed and
- /// unmanaged resources; false to release only unmanaged
- /// resources.
- private void Dispose(bool disposing)
- {
- if (!_disposed)
- {
- if (disposing)
- {
- Stop();
- }
- }
- _disposed = true;
- }
-
- ///
- /// Releases unmanaged resources and performs other cleanup operations
- /// before the
- /// is reclaimed by garbage collection.
- ///
- ~MultimediaTimer()
- {
- Dispose(false);
- }
-
- #endregion
- }
-}
diff --git a/Main/Devices/TimerRegister.cs b/Main/Devices/TimerRegister.cs
index 5f30c2c..04c4325 100644
--- a/Main/Devices/TimerRegister.cs
+++ b/Main/Devices/TimerRegister.cs
@@ -1,15 +1,11 @@
-using FoenixIDE.Timers;
+using KGySoft.CoreLibraries;
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace FoenixIDE.Simulator.Devices
{
public class TimerRegister : MemoryLocations.MemoryRAM
{
- private MultimediaTimer hiresTimer = null;
+ private HiResTimer hiresTimer = null;
public delegate void RaiseInterruptFunction();
public RaiseInterruptFunction TimerInterruptDelegate;
@@ -17,8 +13,8 @@ public class TimerRegister : MemoryLocations.MemoryRAM
public TimerRegister(int StartAddress, int Length) : base(StartAddress, Length)
{
- hiresTimer = new MultimediaTimer(1000);
- hiresTimer.Elapsed += new MultimediaElapsedEventHandler(Timer_Tick);
+ hiresTimer = new HiResTimer(1000);
+ hiresTimer.Elapsed += Timer_Tick;
}
public override void WriteByte(int Address, byte Value)
@@ -51,8 +47,6 @@ public override void WriteByte(int Address, byte Value)
{
hiresTimer.Interval = adjInterval;
}
-
-
}
}
diff --git a/Main/Display/GPU_Common.cs b/Main/Display/GPU_Common.cs
index e900614..fdfb1d7 100644
--- a/Main/Display/GPU_Common.cs
+++ b/Main/Display/GPU_Common.cs
@@ -158,6 +158,11 @@ public void SetTilesetBaseAddress(int val)
TilesetBaseAddress = val;
}
+ int SpriteBaseAddress;
+ public void SetSpriteBaseAddress(int val)
+ {
+ SpriteBaseAddress = val;
+ }
private int[] GetTextLUT(byte fg, byte bg, bool gamma)
{
int[] values = new int[2];
@@ -444,18 +449,11 @@ private unsafe void DrawTiles(int* p, bool gammaCorrection, byte TextColumns, in
{
tilesetPointers[i] = VICKY.ReadLong(TilesetBaseAddress + i * 4) & 0x3F_FFFF;
byte tilesetConfig = VICKY.ReadByte(TilesetBaseAddress + i * 4 + 3);
- if (mode == 0)
- {
- strides[i] = (tilesetConfig & 8) != 0 ? strideLine : tileSize;
- }
- else
- {
- strides[i] = strideLine;
- }
+ strides[i] = (tilesetConfig & 8) != 0 ? strideLine : tileSize;
}
for (int i = 0; i < tilemapItemCount; i++)
{
- byte tile = tiles[i * 2];
+ byte tile = (byte)(tiles[i * 2]);
byte tilesetReg = tiles[i * 2 + 1];
byte tileset = (byte)(tilesetReg & 7);
//byte tileLUT = (byte)((tilesetReg & 0x38) >> 3);
@@ -465,7 +463,7 @@ private unsafe void DrawTiles(int* p, bool gammaCorrection, byte TextColumns, in
int strideX = strides[tileset];
if (strideX == tileSize)
{
- tilesetOffsets[i] = tilesetPointer + (tile % 16) * strideLine + (tile / 16) * strideLine * 16 + tileYOffset * tileSize;
+ tilesetOffsets[i] = tilesetPointer + (tile % 16) * tileSize * tileSize + (tile / 16) * tileSize * tileSize * 16 + tileYOffset * tileSize;
}
else
{
@@ -533,48 +531,72 @@ private unsafe void DrawTiles(int* p, bool gammaCorrection, byte TextColumns, in
}
}
}
- private unsafe void DrawSprites(int* p, bool gammaCorrection, byte layer, bool bkgrnd, int borderXSize, int borderYSize, int line, int width, int height)
+ const int screenOffset = 32;
+ private unsafe void DrawSprites(int* p, bool gammaCorrection, byte layer, bool bkgrnd, int borderXSize, int borderYSize, int line, int width, int height, bool dX, bool dY)
{
// There are 32 possible sprites to choose from.
for (int s = 63; s > -1; s--)
{
- int addrSprite = MemoryMap.SPRITE_CONTROL_REGISTER_ADDR + s * 8 - VICKY.StartAddress;
+ int addrSprite = SpriteBaseAddress + s * 8;
byte reg = VICKY.ReadByte(addrSprite);
// if the set is not enabled, we're done.
- byte spriteLayer = (byte)((reg & 0x70) >> 4);
+ byte spriteLayer = (mode == 0) ? ((byte)((reg & 0x70) >> 4)) : ((byte)((reg & 0x18) >> 3));
+ // if the sprite is enabled and the layer matches, then check the line
if ((reg & 1) != 0 && layer == spriteLayer)
{
- int posY = VICKY.ReadWord(addrSprite + 6) - 32;
- if ((line >= posY && line < posY + 32))
+ byte spriteSize = 32;
+ if (mode == 1)
+ {
+ switch ((reg & 0x60) >> 5)
+ {
+ case 1:
+ spriteSize = 24;
+ break;
+ case 2:
+ spriteSize = 16;
+ break;
+ case 3:
+ spriteSize = 8;
+ break;
+ }
+ }
+ int posY = VICKY.ReadWord(addrSprite + 6) - screenOffset;
+ int actualLine = dY ? line / 2 : line;
+ if ((actualLine >= posY && actualLine < posY + spriteSize))
{
// TODO Fix this when Vicky II fixes the LUT issue
- byte lutIndex = (byte)(((reg & 14) >> 1)); // 8 possible LUTs
- int lutAddress = MemoryMap.GRP_LUT_BASE_ADDR - VICKY.StartAddress + lutIndex * 1024;
+ byte lutIndex = (mode == 0) ? (byte)(((reg & 0xC) >> 1)) : (byte)(((reg & 6) >> 1));
+
+ int lutAddress = lutBaseAddress + lutIndex * 1024;
bool striding = (reg & 0x80) == 0x80;
int spriteAddress = VICKY.ReadLong(addrSprite + 1) & 0x3F_FFFF;
- int posX = VICKY.ReadWord(addrSprite + 4) - 32;
+ int posX = VICKY.ReadWord(addrSprite + 4) - screenOffset;
+ if (dX)
+ {
+ posX *= 2;
+ }
- if (posX >= (width - borderXSize) || posY >= (height - borderYSize) || (posX + 32) < 0 || (posY + 32) < 0)
+ if (posX >= (width - borderXSize) || posY >= (height - borderYSize) || (posX + screenOffset) < 0 || (posY + screenOffset) < 0)
{
continue;
}
- int spriteWidth = 32;
+ int spriteWidth = spriteSize;
int xOffset = 0;
// Check for sprite bleeding on the left-hand-side
if (posX < borderXSize)
{
xOffset = borderXSize - posX;
posX = borderXSize;
- spriteWidth = 32 - xOffset;
+ spriteWidth = spriteSize - xOffset;
if (spriteWidth == 0)
{
continue;
}
}
// Check for sprite bleeding on the right-hand side
- if (posX + 32 > width - borderXSize)
+ if (posX + spriteSize > width - borderXSize)
{
spriteWidth = width - borderXSize - posX;
if (spriteWidth == 0)
@@ -586,19 +608,21 @@ private unsafe void DrawSprites(int* p, bool gammaCorrection, byte layer, bool b
int clrVal = 0;
byte pixVal = 0;
- // Sprites are 32 x 32
- int sline = line - posY;
+ int sline = actualLine - posY;
int lineOffset = line * STRIDE;
int* ptr = p + lineOffset;
for (int col = xOffset; col < xOffset + spriteWidth; col++)
{
// Lookup the pixel in the tileset - if the value is 0, it's transparent
- pixVal = VRAM.ReadByte(spriteAddress + col + sline * 32);
+ pixVal = VRAM.ReadByte(spriteAddress + col + sline * spriteSize);
if (pixVal != 0)
{
clrVal = GetLUTValue(lutIndex, pixVal, gammaCorrection);
- //System.Runtime.InteropServices.Marshal.WriteInt32(p, (lineOffset + (col-xOffset + posX)) * 4, value);
- ptr[col - xOffset + posX] = clrVal;
+ ptr[(dX ? col * 2: col) - xOffset + posX] = clrVal;
+ if (dX)
+ {
+ ptr[col * 2 +1 - xOffset + posX] = clrVal;
+ }
}
}
}
diff --git a/Main/Display/Gpu.Designer.cs b/Main/Display/Gpu.Designer.cs
index 53da73e..7d87011 100644
--- a/Main/Display/Gpu.Designer.cs
+++ b/Main/Display/Gpu.Designer.cs
@@ -17,6 +17,13 @@ protected override void Dispose(bool disposing)
{
components.Dispose();
}
+ if (disposing && (components != null))
+ {
+ BackgroundTextBrush.Dispose();
+ BorderBrush.Dispose();
+ CursorBrush.Dispose();
+ InvertedBrush.Dispose();
+ }
base.Dispose(disposing);
}
diff --git a/Main/Display/Gpu.cs b/Main/Display/Gpu.cs
index e4b7ce6..6e99e52 100644
--- a/Main/Display/Gpu.cs
+++ b/Main/Display/Gpu.cs
@@ -1,16 +1,9 @@
using System;
-using System.Collections.Generic;
-using System.ComponentModel;
using System.Drawing;
-using System.Text;
using System.Windows.Forms;
-using System.Drawing.Text;
using System.Drawing.Imaging;
-using FoenixIDE.Simulator.FileFormat;
using FoenixIDE.MemoryLocations;
-using System.Diagnostics;
-using FoenixIDE.Timers;
-using System.Threading.Tasks;
+using KGySoft.CoreLibraries;
namespace FoenixIDE.Display
{
@@ -24,7 +17,6 @@ public unsafe partial class Gpu : UserControl
const int CHAR_WIDTH = 8;
const int CHAR_HEIGHT = 8;
- const int SPRITE_SIZE = 32;
public MemoryRAM VRAM = null;
public MemoryRAM VICKY = null;
@@ -39,12 +31,8 @@ public unsafe partial class Gpu : UserControl
public delegate void GpuUpdateFunction();
public GpuUpdateFunction GpuUpdated;
- //Timer gpuRefreshTimer = new Timer
- //{
- // Interval = 15
- //};
- // In debug mode, draw the screen twice per second, instead of 60 times.
- private MultimediaTimer hiresTimer = new MultimediaTimer(500);
+ private HiResTimer hiresTimer;
+
private static readonly int[] vs = new int[256 * 8];
private int[] lutCache = vs;
@@ -52,6 +40,8 @@ public Gpu()
{
InitializeComponent();
this.Load += new EventHandler(Gpu_Load);
+ hiresTimer = new HiResTimer(500);
+ hiresTimer.Elapsed += GpuRefreshTimer_Tick;
}
void Gpu_Load(object sender, EventArgs e)
@@ -60,12 +50,9 @@ void Gpu_Load(object sender, EventArgs e)
BlinkingCounter = BLINK_RATE;
this.DoubleBuffered = true;
- //gpuRefreshTimer.Tick += new EventHandler(GpuRefreshTimer_Tick);
- hiresTimer.Elapsed += new MultimediaElapsedEventHandler(GpuRefreshTimer_Tick);
if (DesignMode)
{
- //gpuRefreshTimer.Enabled = false;
hiresTimer.Stop();
}
else
@@ -77,7 +64,6 @@ void Gpu_Load(object sender, EventArgs e)
int sidemargin = ParentForm.Width - ClientRectangle.Width;
ParentForm.Height = htarget + topmargin;
ParentForm.Width = (int)Math.Ceiling(htarget * 1.6) + sidemargin;
- //gpuRefreshTimer.Enabled = true;
hiresTimer.Start();
}
}
@@ -88,7 +74,6 @@ void Gpu_Load(object sender, EventArgs e)
///
private int BLINK_RATE = 30;
private int BlinkingCounter;
-
void GpuRefreshTimer_Tick(object sender, EventArgs e)
{
if (BlinkingCounter-- == 0)
@@ -103,6 +88,13 @@ void GpuRefreshTimer_Tick(object sender, EventArgs e)
}
}
+ public void StopTimer()
+ {
+ hiresTimer.Interval = 1000;
+ hiresTimer.Elapsed -= GpuRefreshTimer_Tick;
+ hiresTimer.Stop();
+ }
+
public void SetRefreshPeriod(uint time)
{
BLINK_RATE = (int)(1000 / time /2);
@@ -317,7 +309,7 @@ unsafe void Gpu_Paint(object sender, PaintEventArgs e)
// Layer 12 - sprite layer 6
if ((MCRegister & 0x20) != 0)
{
- DrawSprites(bitmapPointer, gammaCorrection, 6, displayBorder, borderXSize, borderYSize, line, res.X, res.Y);
+ DrawSprites(bitmapPointer, gammaCorrection, 6, displayBorder, borderXSize, borderYSize, line, res.X, res.Y, false, false);
}
// Layer 11 - bitmap 1
if ((MCRegister & 0x8) == 0x8)
@@ -327,7 +319,7 @@ unsafe void Gpu_Paint(object sender, PaintEventArgs e)
// Layer 10 - sprite layer 5
if ((MCRegister & 0x20) != 0)
{
- DrawSprites(bitmapPointer, gammaCorrection, 5, displayBorder, borderXSize, borderYSize, line, res.X, res.Y);
+ DrawSprites(bitmapPointer, gammaCorrection, 5, displayBorder, borderXSize, borderYSize, line, res.X, res.Y, false, false);
}
// Layer 9 - tilemap layer 3
if ((MCRegister & 0x10) == 0x10)
@@ -337,7 +329,7 @@ unsafe void Gpu_Paint(object sender, PaintEventArgs e)
// Layer 8 - sprite layer 4
if ((MCRegister & 0x20) != 0)
{
- DrawSprites(bitmapPointer, gammaCorrection, 4, displayBorder, borderXSize, borderYSize, line, res.X, res.Y);
+ DrawSprites(bitmapPointer, gammaCorrection, 4, displayBorder, borderXSize, borderYSize, line, res.X, res.Y, false, false);
}
// Layer 7 - tilemap layer 2
if ((MCRegister & 0x10) == 0x10)
@@ -347,7 +339,7 @@ unsafe void Gpu_Paint(object sender, PaintEventArgs e)
// Layer 6 - sprite layer 3
if ((MCRegister & 0x20) != 0)
{
- DrawSprites(bitmapPointer, gammaCorrection, 3, displayBorder, borderXSize, borderYSize, line, res.X, res.Y);
+ DrawSprites(bitmapPointer, gammaCorrection, 3, displayBorder, borderXSize, borderYSize, line, res.X, res.Y, false, false);
}
// Layer 5 - tilemap layer 1
if ((MCRegister & 0x10) == 0x10)
@@ -357,7 +349,7 @@ unsafe void Gpu_Paint(object sender, PaintEventArgs e)
// Layer 4 - sprite layer 2
if ((MCRegister & 0x20) != 0)
{
- DrawSprites(bitmapPointer, gammaCorrection, 2, displayBorder, borderXSize, borderYSize, line, res.X, res.Y);
+ DrawSprites(bitmapPointer, gammaCorrection, 2, displayBorder, borderXSize, borderYSize, line, res.X, res.Y, false, false);
}
// Layer 3 - tilemap layer 0
if ((MCRegister & 0x10) == 0x10)
@@ -367,7 +359,7 @@ unsafe void Gpu_Paint(object sender, PaintEventArgs e)
// Layer 2 - sprite layer 1
if ((MCRegister & 0x20) != 0)
{
- DrawSprites(bitmapPointer, gammaCorrection, 1, displayBorder, borderXSize, borderYSize, line, res.X, res.Y);
+ DrawSprites(bitmapPointer, gammaCorrection, 1, displayBorder, borderXSize, borderYSize, line, res.X, res.Y, false, false);
}
// Layer 1 - bitmap layer 0
if ((MCRegister & 0x8) == 0x8)
@@ -377,7 +369,7 @@ unsafe void Gpu_Paint(object sender, PaintEventArgs e)
// Layer 0 - sprite layer 0
if ((MCRegister & 0x20) != 0)
{
- DrawSprites(bitmapPointer, gammaCorrection, 0, displayBorder, borderXSize, borderYSize, line, res.X, res.Y);
+ DrawSprites(bitmapPointer, gammaCorrection, 0, displayBorder, borderXSize, borderYSize, line, res.X, res.Y, false, false);
}
}
else
@@ -392,7 +384,7 @@ unsafe void Gpu_Paint(object sender, PaintEventArgs e)
if ((MCRegister & 0x20) != 0)
{
- DrawSprites(bitmapPointer, gammaCorrection, 0, displayBorder, borderXSize, borderYSize, line, 320, BitmapY);
+ DrawSprites(bitmapPointer, gammaCorrection, 0, displayBorder, borderXSize, borderYSize, line, res.X, BitmapY, doubleX, doubleY);
}
if ((MCRegister & 0x8) != 0 || (MCRegister & 0x10) != 0)
{
@@ -421,7 +413,7 @@ unsafe void Gpu_Paint(object sender, PaintEventArgs e)
}
if ((MCRegister & 0x20) != 0)
{
- DrawSprites(bitmapPointer, gammaCorrection, 1, displayBorder, borderXSize, borderYSize, line, res.X, BitmapY);
+ DrawSprites(bitmapPointer, gammaCorrection, 1, displayBorder, borderXSize, borderYSize, line, res.X, BitmapY, doubleX, doubleY);
}
if ((MCRegister & 0x8) != 0 || (MCRegister & 0x10) != 0)
{
@@ -450,7 +442,7 @@ unsafe void Gpu_Paint(object sender, PaintEventArgs e)
}
if ((MCRegister & 0x20) != 0)
{
- DrawSprites(bitmapPointer, gammaCorrection, 2, displayBorder, borderXSize, borderYSize, line, res.X, BitmapY);
+ DrawSprites(bitmapPointer, gammaCorrection, 2, displayBorder, borderXSize, borderYSize, line, res.X, BitmapY, doubleX, doubleY);
}
if ((MCRegister & 0x8) != 0 || (MCRegister & 0x10) != 0)
{
@@ -479,7 +471,7 @@ unsafe void Gpu_Paint(object sender, PaintEventArgs e)
}
if ((MCRegister & 0x20) != 0)
{
- DrawSprites(bitmapPointer, gammaCorrection, 3, displayBorder, borderXSize, borderYSize, line, res.X, BitmapY);
+ DrawSprites(bitmapPointer, gammaCorrection, 3, displayBorder, borderXSize, borderYSize, line, res.X, BitmapY, doubleX, doubleY);
}
}
}
@@ -494,7 +486,10 @@ unsafe void Gpu_Paint(object sender, PaintEventArgs e)
}
if (!TileEditorMode)
{
- DrawMouse(bitmapPointer, gammaCorrection, line, res.X, res.Y);
+ if (mode == 0)
+ {
+ DrawMouse(bitmapPointer, gammaCorrection, line, res.X, res.Y);
+ }
}
}
diff --git a/Main/FoenixIDE.csproj b/Main/FoenixIDE.csproj
index 740dd5c..a13ca38 100644
--- a/Main/FoenixIDE.csproj
+++ b/Main/FoenixIDE.csproj
@@ -52,7 +52,8 @@
pdbonly
true
..\bin\Release\
- TRACE
+
+
prompt
4
false
@@ -80,6 +81,8 @@
+
+
Component
@@ -339,7 +342,6 @@
UploaderWindow.cs
-
ViewControl.cs
diff --git a/Main/MemoryLocations/MemoryManager.cs b/Main/MemoryLocations/MemoryManager.cs
index 19008e2..3cd0666 100644
--- a/Main/MemoryLocations/MemoryManager.cs
+++ b/Main/MemoryLocations/MemoryManager.cs
@@ -287,20 +287,13 @@ public void GetDeviceAt(int Address, out IMappable Device, out int DeviceAddress
byte segment = (byte)(Address >> 13); // take the top 3 bits
offset = MMU.GetPage((byte)(MMURegister & 3), segment) * 8192;
// bits 4 and 5 of MMURegister determine which LUT is being edited
- if (Address >= 8 && Address <= 0xF)
+ if ((MMURegister & 0x80) != 0 && Address >= 8 && Address <= 0xF)
{
Device = MMU;
MMU.SetActiveLUT((byte)((MMURegister & 0x30) >> 4));
DeviceAddress = Address;
return;
}
- if ((MMURegister & 0x80) != 0 && Address >= 0x10 && Address <= 0x2F)
- {
- Device = MMU;
- MMU.SetActiveLUT((byte)((Address - 0x10)>>3));
- DeviceAddress = (Address & 7) + 8;
- return;
- }
Device = RAM;
offset = MMU.GetPage((byte)(MMURegister & 3), (byte)(Address >> 13)) * 8192;
diff --git a/Main/Processor/CPU.cs b/Main/Processor/CPU.cs
index 881085f..878e085 100644
--- a/Main/Processor/CPU.cs
+++ b/Main/Processor/CPU.cs
@@ -156,7 +156,7 @@ public bool ExecuteNext()
}
// TODO - if pc > RAM size, then throw an exception
- CurrentOpcode = opcodes[MemMgr.RAM.ReadByte(PC)];
+ CurrentOpcode = opcodes[MemMgr.ReadByte(PC)];
if (CurrentOpcode != null)
{
OpcodeLength = CurrentOpcode.Length;
@@ -258,11 +258,11 @@ public int ReadSignature(int length, int pc)
switch (length)
{
case 2:
- return MemMgr.RAM.ReadByte(pc + 1);
+ return MemMgr.ReadByte(pc + 1);
case 3:
- return MemMgr.RAM.ReadWord(pc + 1);
+ return MemMgr.ReadWord(pc + 1);
case 4:
- return MemMgr.RAM.ReadLong(pc + 1);
+ return MemMgr.ReadLong(pc + 1);
}
return 0;
diff --git a/Main/Properties/AssemblyInfo.cs b/Main/Properties/AssemblyInfo.cs
index 684eb5e..1d4e3a8 100644
--- a/Main/Properties/AssemblyInfo.cs
+++ b/Main/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("0.6.0.1")]
-[assembly: AssemblyFileVersion("0.6.0.1")]
+[assembly: AssemblyVersion("0.6.0.2")]
+[assembly: AssemblyFileVersion("0.6.0.2")]
diff --git a/Main/UI/AssetLoader.cs b/Main/UI/AssetLoader.cs
index b68836c..850b680 100644
--- a/Main/UI/AssetLoader.cs
+++ b/Main/UI/AssetLoader.cs
@@ -177,10 +177,43 @@ private void StoreButton_Click(object sender, EventArgs e)
{
case ".png":
Bitmap png = new Bitmap(FileNameTextBox.Text, false);
+ if (FileTypesCombo.SelectedIndex == 1)
+ {
+ if (png.Width == 16)
+ {
+ conversionStride = 16;
+ maxHeight = 256 * 16;
+ }
+ }
+ else if(FileTypesCombo.SelectedIndex == 2)
+ {
+ if (png.Width == 8)
+ {
+ conversionStride = 8;
+ maxHeight = 256 * 8;
+ }
+ }
+
ConvertBitmapToRaw(png, res, (byte)LUTCombo.SelectedIndex, conversionStride, maxHeight);
break;
case ".bmp":
Bitmap bmp = new Bitmap(FileNameTextBox.Text, false);
+ if (FileTypesCombo.SelectedIndex == 1)
+ {
+ if (bmp.Width == 16)
+ {
+ conversionStride = 16;
+ maxHeight = 256 * 16;
+ }
+ }
+ else if (FileTypesCombo.SelectedIndex == 2)
+ {
+ if (bmp.Width == 8)
+ {
+ conversionStride = 8;
+ maxHeight = 256 * 8;
+ }
+ }
ConvertBitmapToRaw(bmp, res, (byte)LUTCombo.SelectedIndex, conversionStride, maxHeight);
break;
default:
@@ -250,6 +283,7 @@ private unsafe void ConvertBitmapToRaw(Bitmap bitmap, ResourceChecker.Resource r
{ }
done = true;
+
// Reset the Lookup Table
lut = new List(256)
{
@@ -397,6 +431,18 @@ private unsafe void ConvertBitmapToRaw(Bitmap bitmap, ResourceChecker.Resource r
MemMgrRef.VICKY.WriteByte(lutBaseAddress + 4 * i, LowByte(rbg));
MemMgrRef.VICKY.WriteByte(lutBaseAddress + 4 * i + 1, MidByte(rbg));
MemMgrRef.VICKY.WriteByte(lutBaseAddress + 4 * i + 2, HighByte(rbg));
+ MemMgrRef.VICKY.WriteByte(lutBaseAddress + 4 * i + 3, 0);
+ }
+ // if the Overwrite checkbox is checked, then complete with zeros
+ if (checkOverwriteLUT.Checked)
+ {
+ for (int i= lut.Count; i < 256; i++)
+ {
+ MemMgrRef.VICKY.WriteByte(lutBaseAddress + 4 * i, 0);
+ MemMgrRef.VICKY.WriteByte(lutBaseAddress + 4 * i + 1, 0);
+ MemMgrRef.VICKY.WriteByte(lutBaseAddress + 4 * i + 2, 0);
+ MemMgrRef.VICKY.WriteByte(lutBaseAddress + 4 * i + 3, 0);
+ }
}
}
diff --git a/Main/UI/CPUWindow.cs b/Main/UI/CPUWindow.cs
index 01d18db..5507204 100644
--- a/Main/UI/CPUWindow.cs
+++ b/Main/UI/CPUWindow.cs
@@ -436,7 +436,7 @@ public void RunButton_Click(object sender, EventArgs e)
InterruptMatchesCheckboxes();
registerDisplay1.RegistersReadOnly(true);
- MainWindow.Instance.setGpuPeriod(16);
+ MainWindow.Instance.setGpuPeriod(17);
kernel.CPU.DebugPause = false;
lastLine.Text = "";
kernel.CPU.CPUThread = new Thread(new ThreadStart(ThreadProc));
diff --git a/Main/UI/MainWindow.cs b/Main/UI/MainWindow.cs
index 61d1361..c0a777e 100644
--- a/Main/UI/MainWindow.cs
+++ b/Main/UI/MainWindow.cs
@@ -203,6 +203,8 @@ private void BasicWindow_Load(object sender, EventArgs e)
gpu.SetBitmapControlRegister(MemoryMap.BITMAP_CONTROL_REGISTER_ADDR - gpu.VICKY.StartAddress);
gpu.SetTileMapBaseAddress(MemoryMap.TILE_CONTROL_REGISTER_ADDR - gpu.VICKY.StartAddress);
gpu.SetTilesetBaseAddress(MemoryMap.TILESET_BASE_ADDR - gpu.VICKY.StartAddress);
+
+ gpu.SetSpriteBaseAddress(MemoryMap.SPRITE_CONTROL_REGISTER_ADDR - gpu.VICKY.StartAddress);
}
else
{
@@ -229,6 +231,7 @@ private void BasicWindow_Load(object sender, EventArgs e)
gpu.SetBitmapControlRegister(0xD100 - 0xC000); // IO Page 0
gpu.SetTileMapBaseAddress(0xD200 - 0xC000);
gpu.SetTilesetBaseAddress(0xD280 - 0xC000);
+ gpu.SetSpriteBaseAddress(0xD900 - 0xC000);
}
if (disabledIRQs)
@@ -336,13 +339,26 @@ private void ShowMemoryWindow()
}
else
{
+ memoryWindow.Memory = kernel.CPU.MemMgr;
memoryWindow.BringToFront();
}
- memoryWindow.WriteMCRBytes += WriteMCRBytesToVicky;
- memoryWindow.ReadMCRBytes += ReadMCRBytesFromVicky;
+ if (memoryWindow.WriteMCRBytes == null)
+ {
+ memoryWindow.WriteMCRBytes += WriteMCRBytesToVicky;
+ }
+ if (memoryWindow.ReadMCRBytes == null)
+ {
+ memoryWindow.ReadMCRBytes += ReadMCRBytesFromVicky;
+ }
memoryWindow.UpdateMCRButtons();
- memoryWindow.SetGamma += UpdateGamma;
- memoryWindow.SetHiRes += UpdateHiRes;
+ if (memoryWindow.SetGamma == null)
+ {
+ memoryWindow.SetGamma += UpdateGamma;
+ }
+ if (memoryWindow.SetHiRes == null)
+ {
+ memoryWindow.SetHiRes += UpdateHiRes;
+ }
}
public void SerialTransmitByte(byte Value)
@@ -657,20 +673,6 @@ private void BasicWindow_KeyUp(object sender, KeyEventArgs e)
private void ExitToolStripMenuItem_Click(object sender, EventArgs e)
{
- gpu.StartOfFrame = null;
- gpu.StartOfLine = null;
- if (debugWindow != null)
- {
- debugWindow.Close();
- }
- if (memoryWindow != null)
- {
- memoryWindow.Close();
- }
- if (GGF != null)
- {
- GGF.Close();
- }
this.Close();
}
@@ -713,6 +715,10 @@ private void Gpu_Update_Cps_Fps()
previousFrame = currentFrame;
Write_CPS_FPS_Safe("CPS: " + cps.ToString("N0"), "FPS: " + fps.ToString("N0"));
}
+ else
+ {
+ Write_CPS_FPS_Safe("CPS: Stopped", "FPS: N/A");
+ }
WriteRTCTime(currentTime);
}
else
@@ -853,8 +859,10 @@ private void DefaultKernelToolStripMenuItem_Click(object sender, EventArgs e)
private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
{
+ gpu.StopTimer();
gpu.StartOfFrame = null;
gpu.StartOfLine = null;
+
ModeText.Text = "Shutting down CPU thread";
if (kernel.CPU != null)
{
@@ -865,6 +873,19 @@ private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
kernel.CPU.CPUThread.Join(1000);
}
}
+
+ if (debugWindow != null)
+ {
+ debugWindow.Close();
+ }
+ if (memoryWindow != null)
+ {
+ memoryWindow.Close();
+ }
+ if (GGF != null)
+ {
+ GGF.Close();
+ }
}
private void MenuOpenExecutableFile_Click(object sender, EventArgs e)
diff --git a/Main/UI/UploaderWindow.cs b/Main/UI/UploaderWindow.cs
index 2377592..02608f7 100644
--- a/Main/UI/UploaderWindow.cs
+++ b/Main/UI/UploaderWindow.cs
@@ -3,16 +3,11 @@
using FoenixIDE.Simulator.FileFormat;
using System;
using System.Collections.Generic;
-using System.ComponentModel;
using System.Diagnostics;
-using System.Drawing;
using System.IO;
using System.IO.Ports;
-using System.Text;
using System.Threading;
-using System.Threading.Tasks;
using System.Windows.Forms;
-using Timer = System.Windows.Forms.Timer;
namespace FoenixIDE.UI
{
@@ -47,6 +42,9 @@ public void SetBoardVersion(BoardVersion ver)
case BoardVersion.RevUPlus:
RevModeLabel.Text = "Mode: RevU+";
break;
+ case BoardVersion.RevJr:
+ RevModeLabel.Text = "Mode: F256Jr";
+ break;
}
}
@@ -267,6 +265,10 @@ private void SendBinaryButton_Click(object sender, EventArgs e)
{
BaseBankAddress = 0x18_0000;
}
+ else if (boardVersion == BoardVersion.RevJr)
+ {
+ BaseBankAddress = 0;
+ }
if (SendFileRadio.Checked)
{
@@ -625,7 +627,7 @@ private void SendData(byte[] buffer, int startAddress, int size)
// DataBuffer = The buffer where the loaded Binary File resides
// FnxAddressPtr = Pointer where to put the Data in the Fnx
// i = Pointer Inside the data buffer
- // Size_Of_File = Size of the Payload we want to transfer which ought to be smaller than 8192
+ // Size_Of_File = Size of the Payload we want to transfer which ought to be smaller than 2048 bytes
PreparePacket2Write(buffer, startAddress, 0, size);
UploadProgressBar.Increment(size);
}
@@ -769,7 +771,16 @@ public void PreparePacket2Write(byte[] buffer, int FNXMemPointer, int FilePointe
// Maximum transmission size is 8192
if (Size > 8192)
{
- Size = 8192;
+ if (boardVersion != BoardVersion.RevJr)
+ {
+ Size = 8192;
+ Console.WriteLine("PreparePacket2Write: output truncated to 8K bytes.");
+ }
+ else
+ {
+ Size = 2048;
+ Console.WriteLine("PreparePacket2Write: output truncated to 2K bytes.");
+ }
}
byte[] commandBuffer = new byte[8 + Size];
@@ -778,8 +789,8 @@ public void PreparePacket2Write(byte[] buffer, int FNXMemPointer, int FilePointe
commandBuffer[2] = (byte)((FNXMemPointer >> 16) & 0xFF); // (H)24Bit Addy - Where to Store the Data
commandBuffer[3] = (byte)((FNXMemPointer >> 8) & 0xFF); // (M)24Bit Addy - Where to Store the Data
commandBuffer[4] = (byte)(FNXMemPointer & 0xFF); // (L)24Bit Addy - Where to Store the Data
- commandBuffer[5] = (byte)((Size >> 8) & 0xFF); // (H)16Bit Size - How many bytes to Store (Max 8Kbytes for now)
- commandBuffer[6] = (byte)(Size & 0xFF); // (L)16Bit Size - How many bytes to Store (Max 8Kbytes for now)
+ commandBuffer[5] = (byte)((Size >> 8) & 0xFF); // (H)16Bit Size - How many bytes to Store (Max 8Kbytes for now - 2K for Junior)
+ commandBuffer[6] = (byte)(Size & 0xFF); // (L)16Bit Size - How many bytes to Store (Max 8Kbytes for now - 2K for Junior)
Array.Copy(buffer, FilePointer, commandBuffer, 7, Size);
TxProcessLRC(commandBuffer);
@@ -789,6 +800,10 @@ public void PreparePacket2Write(byte[] buffer, int FNXMemPointer, int FilePointe
SendMessage(commandBuffer, null); // Tx the requested Payload Size (Plus Header and LRC), No Payload to be received aside of the Status.
}
+ /**
+ * address: the address to read from, in the machine
+ * size: the number of bytes to read
+ */
public byte[] PreparePacket2Read(int address, int size)
{
if (size > 0)
diff --git a/Main/roms/kernel_F256jr.hex b/Main/roms/kernel_F256jr.hex
new file mode 100644
index 0000000..778e1c5
--- /dev/null
+++ b/Main/roms/kernel_F256jr.hex
@@ -0,0 +1,295 @@
+:20E000003F0D000D5245414459204445564943450044495200535441540052445300434CCF
+:20E0200053004C495354004C4F41440044524956450052554E005359530048454C50000D8D
+:20E0400054797065202768656C702720666F722068656C702E0D000D537570706F7274652D
+:20E060006420636F6D6D616E64733A0D202020636C73202020202020202020436C656172DA
+:20E0800073207468652073637265656E2E0D20202064726976652023202020202043686168
+:20E0A0006E6765732074686520647269766520746F20232E0D202020646972202020202058
+:20E0C00020202020446973706C61797320746865206469726563746F72792E0D2020206C1A
+:20E0E0006F616422666E616D6522204C6F6164732074686520676976656E2066696C652014
+:20E10000272C31272E0D2020206C69737420202020202020204C4953547320646972656327
+:20E12000746F7269657320616E642073696D706C652070726F6772616D732E0D20202072B4
+:20E14000756E20202020202020202052756E73206C6F616465642070726F6772616D732ED2
+:20E160000D20202068656C70202020202020202053686F777320746869732068656C702EA6
+:20E180000D0D001EE035E211E06BE222E02EEE27E00EEF2CE04EE232E0C1F23AE0A0E10054
+:20E1A000A95785A5A9E085A64C16E7200BE7A9E085C4A03F209EE28000A90885C764C864C8
+:20E1C000C964CA64CB20DCE120F0E12002E290F8A000209EE280F1A90D4C9BE2A003209E2E
+:20E1E000E2A920209BE2A5C70930209BE24CD7E1A200DA20CFFFFA9D0002E8C90DD0F34CC1
+:20E20000B9F918A200BC83E1F016E8E82024E2B00B2021E290032028E74CDCE1E8E880E598
+:20E22000607C83E184C3A00018B1C3F007C8D9FF01F0F53860A90C209BE21860B90002C9C8
+:20E2400020F006C90DF0051860C8D0F03860203CE2A909B00EB90002C938F008C939F004ED
+:20E26000A9093860E93085C71880F8DA5AA29AA0E2A90120BDFFA900A6C7A00020BAFFA9AF
+:20E2800000A201A00820D5FFB00D20B7FFF005A905388003202EEE7AFA60244CB9F984C3D5
+:0EE2A000A000B1C3F006209BE2C880F6186013
+:20E500000000010002000100030001000200010004000100020001000300010002000100E1
+:20E520000500010002000100030001000200010004000100020001000300010002000100BC
+:20E5400006000100020001000300010002000100040001000200010003000100020001009B
+:20E5600005000100020001000300010002000100040001000200010003000100020001007C
+:20E5800007000100020001000300010002000100040001000200010003000100020001005A
+:20E5A00005000100020001000300010002000100040001000200010003000100020001003C
+:20E5C00006000100020001000300010002000100040001000200010003000100020001001B
+:20E5E0000500010002000100030001000200010004000100020001000300010002000100FC
+:20E600004F70656E4B45524E414C202D206120636C65616E2D726F6F6D20696D706C656D2C
+:20E62000656E746174696F6E206F662074686520433634204B45524E414C204142490D433C
+:20E640006F707972696768742032303232204A6573736965204F626572726575746572207C
+:20E660003C476164676574404861636B7772656E63684C6162732E636F6D3E2E0D52656C49
+:20E68000656173656420756E646572207468652047504C33206C6963656E7365207769742C
+:20E6A0006820746865206B65726E656C20657863657074696F6E3A0D6170706C696361746C
+:20E6C000696F6E732077686963682073696D706C79207573652074686520414249206172E3
+:20E6E00065206E6F7420276465726976656420776F726B73272E0D00CB6020A6ED2018ED5F
+:20E70000208CEC60E6A3D002E6A460A90085A5A9E685A64C16E7A000B1A5F00A20D2FFC83D
+:20E72000D0F6E6A680F2186048A94A85A5A9E785A6A000B1A5F00620B9F9C880F668186998
+:20E740003020B9F9A90D20B9F9604572726F722000A0FFC8B1A5F00451A7F0F76064012030
+:20E7600037EC20FCED2081FF2084FF4C6EE7A200BD94E785A5E8BD94E785A6E8BD94E78595
+:20E78000A7E8BD94E785A8E82051E7D0037C94E7E8E880DCA6E704A0AFE7B2E70480BAE7CA
+:20E7A000BDE700E0BEE743424D4241534943006C00A042415349433032004C0080004CAB69
+:20E7C000E1A5BB6005BB85BB60208DF3B0F66024BA300585B9C6BA6048A5B920CAF390034B
+:20E7E00020C4E76885B96024BA1009A5B920D4F3B0D264BA6064BB64BA60C9109002A980DB
+:20E80000604C8AF320F5E720FAE7B008094020DEF3B001604CC4E720E7E7B008A95F20F119
+:20E82000F3B001604CC4E720F5E720FAE7B008092020DEF3B001604CC4E720E7E7B008A9B7
+:20E840003F20F1F3B001604CC4E720FAE7B008096020DEF3B001604CC4E786A784A8AAA4AB
+:20E86000B22080E8B01120B7FF0900D00B20EBE8B00620D2E8B0016020C4E71880F938605B
+:20E88000A5AFD004A9083860A5B120B1FFB020A9F020DEF3B01920B1E8B01420AEFFB00FB5
+:20E8A000A5B120B4FFB008A9002096FFB0016018605AA000B1AD20C6E820A8FFB006C8C461
+:20E8C000AF90F1187A60C9619006C97BB0024920186020ABFFB013A5B120B1FFB00CA9E087
+:20E8E00020DEF3B00520AEFFB00060AAF002A202EA20A5FFB02185A520A5FFB01A85A6985B
+:20E90000F008A5A585A7A5A685A8EA20A5FF900EC940F0154CC4E720C4E7A90438607C2F05
+:20E92000E9E6A7D002E6A890E218A6A7A4A86033E937E992A780EAD2A7F0E6A9103820142B
+:20E94000E980E7A9028501AD00C01A8D00C080F7206CE9B008B94403D004A9033860B945A8
+:20E9600003C9089002A9080A0A0AAA60C90A9004A90138600A0A0AA86020B0E9485A48A046
+:20E9800000B99AE9F00620B9F9C880F56818693020B9F920A5E97A683860492F4F204552A5
+:20E9A000524F522000A90A20B9F9A90D20B9F96048DA5AA90D20B9F9A000B9440320D3E957
+:20E9C000C8982907D0F4A90D20B9F9C050D0EB7AFA6860484A4A4A4A20E6E968290F20E64F
+:20E9E000E9A9204CB9F9AABDEDE94CB9F9303132333435363738396162636465660064C0A6
+:20EA000064C2A2009E4403E8E050D0F84C0FEAA900A200A0002032EA2040EAA200207EEA89
+:20EA2000A901A203A0002032EA2040EAA2012088EA6085B086B184B26085AF86AD84AE60D1
+:20EA4000A5B0206CE9B007B94403F005A9024C79E9A5B1994503A5B2994603A5B10A0A0A03
+:20EA6000AA20E5EBB0E8602050E990034C79E94CF0EBDA8A2050E990032079E9FA605A209D
+:20EA800072EAB00286BC7A605A2072EAB00286BD7A6018605AA5BC2050E9B00320F0EBA5C8
+:20EAA000BD2050E9B00320F0EBA200207EEAA2012088EA187A60DA5AA5BC2050E9B00FB9DB
+:20EAC0004503D00520D4EA800820E8EB90032079E97AFA60A5C0D01EA5E385C185C220D679
+:20EAE000EC900520F8E680F6C90DF0082038EB20B9F980EA85C0A4C1C4E3D00864C064C261
+:20EB0000A90D8026DAA601A9028501B1E5297F8601FAE6C1C92090DEC922F010A6C2D00AFD
+:20EB2000C97BB006C961900249201860A5C264C2D002E6C2A92280F2DAA200DD4DEBF00871
+:20EB4000E8E8E00CD0F58003BD4EEBFA609601970599109A0E9B029C06DAA5BC2050E9B05A
+:20EB60000520E8EB90032079E9FA090060DA5A48A5BD2050E990067A2079E9800668482006
+:20EB8000ECEB687AFA605AA900482050E9B00320F0EB681AC90AD0F1200FEA7A601BEC21DF
+:20EBA000EC25EC1EECF3EBF3EBF3EBF3EBF3EBF3EBF3EBF3EB06EC0CEC10EC09ECF3EBF3B2
+:20EBC000EBF3EBF3EBF3EBF3EBF3EBF3EBF3EBF3EBF3EBF3EBF3EBF3EBF3EBF3EBF3EBF355
+:20EBE000EBF3EBF3EB7C9DEB187CA1EB387CA1EB7CA3EBA9054C79E9A9019944031860A9F9
+:20EC00000099440318604CF8EB4CFFEBA9001860B004A900186020B9F918604CF8EB4CFF7D
+:20EC2000EBA9001860B00920D6EC9003A9001860A9074C79E91860A2027400E8E0A3D0F957
+:20EC40009C00019C0101A2009E0002E8E090D0F8A2009E0003E8E034D0F8A200A00818208E
+:20EC60007AECA200A0A018206EEC6085B7609005A6B5A4B66086B584B6609005A6B3A4B4F9
+:20EC80006086B384B46060A2DCA000609C94039C95039C960360AD9603D004A9FF1860200F
+:20ECA000CCFF789C94039C9503589C9603A9003860C903D0038D9603DA380878AE94039DA3
+:20ECC0003403CA1002A20FEC9503F0078E9403BADE010128FA60DAAE9503EC940338F00CDD
+:20ECE000BD3403CA1002A20F8E950318FA604CE4F7A250A03C60B0032049F9A6E3A6E4601E
+:20ED00007C00047C02047C04047C06047C08047C0A047C0C047C0E049C9703A9008004AAAE
+:20ED2000203AED186910D0F7186038AE9703F00948BD00048D970368186048AD97039D0007
+:20ED4000048E970368186048DA5AA000B1A59D0004E8C8C010D0F57AFA6818609E00059EBA
+:20ED6000010560480878BD0105990302989D01052868186048BC0005D02338BC0105F024B7
+:20ED80000878BC01059E010528B9030248BD0005990302989D00057AD0EFBC0005B903020D
+:20EDA0009D00051868609C9803A990A820CCED186904D0F71860480878AC9803F00AB90357
+:20EDC000028D98032868186028683860480878AD98039903028C9803286818602CEE2CEEC8
+:20EDE0002CEE40EA67EA7EEA88EA92EAB6EA6DEB96EC59EB86EB2CEE5AE87EE848DAA20073
+:20EE0000BDDCED9D1403E8C920D0F5FA686086A584A6A000B00BB1A5991403C8C020D0F63C
+:20EE200060B9140391A5C8C020D0F660386020D7E1DA5AA200A000A90185A5A90885A620E8
+:20EE40007EEE85AB207EEE05ABF03D207EEE85AB207EEE85AC208CEE207EEEF01C3006C903
+:20EE600020B011800DC99EF00280075AA036209EE27AA92020B9F980DF20D7E180C1B1A591
+:20EE8000C8D002E6A60900607AFA1860DA5A64C5A200A00020C6EE9008E6C5E820D5EE80F6
+:20EEA000F3A5C5F0098A18693020B9F9A200C8C8C00AD0E0A5C5D005A93020B9F9A9202070
+:20EEC000B9F97AFA1860A5ACD9E6EE9007D005A5ABD9E5EE6038A5ABF9E5EE85ABA5ACF900
+:20EEE000E6EE85AC601027E80364000A0001005052478FEF7072678FEF5047584CF07067EC
+:20EF0000784CF050475AD2F070677AD2F00064C864C9203CE29001602037EFB019A5AFC9C3
+:20EF2000059010A5AF38E904A8B1ADC92ED004C84C5AEF4C8FEF60C85AB90002C922F00A99
+:20EF4000C90DF003C8D0F2387A6098BA38FD0101F0F5FAA00220BDFF1860A200BDEFEEF0C2
+:20EF60000E2073EF90068A6904AA80F07CF2EEA90438605A38B1AD5DEFEED011C8B1AD5D2B
+:20EF8000F0EED009C8B1AD5DF1EED001187A60A001A5AFC901D009B2ADC924D0034C6BE245
+:20EFA000A900A6C7A00120BAFFA90020D5FFB0034CB4EF60A5A585C8A5A685C9A5A5C901DE
+:20EFC000D049A5A6C908D04364C864C9A0002001F085AB2001F005ABF0312001F02001F0AB
+:20EFE0002001F0F0E910F9C99ED0F52001F0C9309019C93AB01538E930200DF02001F08078
+:20F00000EDB1A5C8D002E6A6090060186048202AF02033F02033F0203EF02033F06885CA56
+:20F0200064CB203EF064CA64CB60A5C885CAA5C985CB60A5C80A85C8A5C92A85C96018A5F5
+:20F04000C865CA85C8A5C965CB85C960A900A6C7A00020BAFF20F5E72080E8B01920B7FF6E
+:20F060000900D013207DF090084820D2E86838800620D2E8B001602028E71880F9A20020CA
+:20F08000A5FFB023DDBAF0D035E8E004D0F1A20020A5FFB01295CC95C8E8E004D0F220A5A7
+:20F0A000FF90074940F00D4C28E7206FF2A2CC20C3F090EA18A6CCA4CD6050475800A9109A
+:20F0C0003880E4F600D00AF601D006F602D002F60360A900A6C7A00020BAFF20F5E72080A9
+:20F0E000E8B01920B7FF0900D0132003F190084820D2E86838800620D2E8B001602028E78F
+:20F100001880F9A902850120A5FFB065C95AF00AC97AF00CA910384C71F1A90385D48006CE
+:20F12000A90485D48000A20074C874CC74D0E8E004D0F5A20020A5FFB04F800520A5FFB0F8
+:20F140003095CCE8E4D4D0F4A20020A5FFB02295D0E8E4D4D0F420A1F1D00DA200B5CC9572
+:20F16000C8E8E4D4D0F780CB20A5FF90074940F0A34C28E78D4FC0206FF2A2CC20C3F020C5
+:20F180008FF120A1F1D0E180AA4940D0E4186018A200B5D0E90095D0B005E8E004D0F318C4
+:20F1A00060A5D005D105D205D3600078A2FF9ABA8E9903ADAAF1D017EEAAF120F0F120160F
+:20F1C000F2B0096401A93385004C5DE74C28E720E4F7A9E985A5A9F185A6A000B1A5F0066B
+:20F1E00020B9F9C880F64CF6FF55706C6F616400A9802007F2A9902007F2A9A02007F2A9BA
+:20F20000B02007F26400608500A2008A95089510E8E008D0F66020D9F22000F520FAE62058
+:20F22000E4F7B00A6401202FF220B8F7B00060201AFBA9498D0004A9F28D0104A900A00086
+:20F2400020D0F5A90020D8F5602030FB4C04E79C99034048DA5AA500640085D5A50185D6F9
+:20F260002097F5A5D68501A5D585007AFA6840DAA6CFD009A6CED00F92CC18FA60A902854B
+:20F2800001EE4EC03880F480F424CE30F070EE48A5CC85D7A5CD291F09C085D8A5CD85D91C
+:20F2A000A5CE06D92A06D92A06D92AA2048601A2908600850E6892D7A906850E6400188039
+:20F2C000BAA5C805C9F00BA5CA05CBD00520D6F21860A90438606CC8006401A9008D20D6C0
+:20F2E000A91A8D21D6A9018D22D62080F3A9038D20D6A92A8D21D6A9018D22D62080F3A914
+:20F30000018D20D6A9238D21D6A9018D22D62080F3A9078D20D6A92C8D21D6A9018D22D69C
+:20F320002080F3A9028D20D6A9148D21D6A9018D22D62080F3A9028D20D6A9168D21D6A9EF
+:20F34000018D22D62080F3A9458D20D6A9188D21D6A9018D22D62080F34C5CF36401A99FD4
+:20F360008D00D68D10D6A9BF8D00D68D10D6A9DF8D00D68D10D6A9FF8D00D68D10D6186025
+:20F38000AD22D62901C901F0F76085DA60A5DCF004A9403860DAA60164019701EE4FC064F9
+:20F3A00001A5A318690585DBAD81D64A900CA5DAA5A3C5DB90F2A502800CAD80D648AD819F
+:20F3C000D6290485DC688601FA60DAA60164018D82D68027DAA60164018D83D6801D97010D
+:20F3E0001701EE4EC06401DAA60164018D80D6800ADAA60164018D81D6800064DC200FF494
+:20F40000AD81D6291018F00338A9808601FA60A9322017F43AD0FA2020F4201DF42020F4BF
+:09F420002023F4EAEAEAEAEA60BA
+:20F50000486401A9FF8D6CD68D6DD68D68D68D69D6AD60D68D60D6AD61D68D61D6A9368DA6
+:20F520000204A9F58D0304A902A000999A03C8C010D0F858681860A0028401484A4A4A4AE3
+:20F54000A8B954F58D20C068290FA8B954F58D21C064016030313233343536373839616247
+:20F560006364656600485AA0028401484A4A4A4AA8B986F58D22C068290FA8B986F58D2349
+:20F58000C064017A686030313233343536373839616263646566006401AE60D6F011BC009C
+:20F5A000E5B9C8F58D60D6BE9A032000ED80E86401AE61D6F011BC00E5B9C8F58D61D6BE79
+:20F5C000A2032000ED80E8600102040810204080C010B003999A0360C910B00DDA20EAF52A
+:20F5E00049FF3D6CD69D6CD6FA60A2008908F001E829075AA8B9C8F57A60C910B00BDA20EF
+:20F60000EAF51D6CD69D6CD6FA6052F7B6F7B6F7EEF699F79EF730F793F7202AEDB0092006
+:20F6200029F69004203AED3860A90220FAF5A90320FAF5A9AC208EF6A9A7208EF620A0F620
+:20F64000B02B206EF6B026A9AA20AEF6B01FC95538D01A206EF6B015BD05050904207CF6A0
+:20F66000B00BA90A85A5A9F685A62047ED60A92020AEF6B0F829BC290F4C7CF648A96020F3
+:20F680008EF668B0082097F6B0039D05056020C1F6B0038D44D66020C1F6B0038D40D66041
+:20F6A000A06420B4F6B00588D0F838601860208EF690016020BDF6B003AD40D660A90180FF
+:20F6C0000848A90220C9F668609D0705A5A39D080518AD44D649023D0705F00160A5A338A9
+:20F6E000FD0805C91EB0F520F8E680E680FE205CED9E0905A9FF9D02058AA00420D0F5A975
+:20F70000A88D55D6A9618D56D6A9008D57D69C51D69C52D69C53D6A9028D54D6A9648D1902
+:20F72000D09C1AD0A9018D18D0A90420D8F518605A20B6ED90027A609901026899000220FA
+:20F7400063EDFE0205D003204CF71860A90D8D50D660AD44D68902D021BD0A05BC0905D034
+:20F76000132074EDB017DE0205B90002D010B9010220CCED8D40D69E0905204CF760A9D48A
+:20F780008D44D69D0905B901029D0A0520CCED80E980BFA90420FAF560AD40D61860B9B26C
+:20F7A000F71D0505207CF6B008B9B4F7208EF6B000600102AEA818606401201AF6B024206F
+:20F7C00009EDB01FDA8AA002203AFCB0032009EDFAB010DA8AA003203AFCB0032009EDFA65
+:20F7E000B001186020F2F7A9E685E764E82014F9186064019C00D09C01D0A9018D00D02086
+:20F800000FF820A8F820B5F82060F89C10D060A200BD20F89D00D89D40D8E8E040D0F26035
+:20F8200000000000FFFFFF0000008800EEFFAA00CC44CC0055CC0000AA00000077DDDD00D4
+:20F840005588DD00004466007777FF00333333007777770066FFAA00FF880000BBBBBB0092
+:20F86000DA5AA401A901850164E5A9D085E6A200208AF8E8A5E5690485E5D0F4A5E61A85D7
+:20F88000E6C9E0D0EB84017AFA605AA003A9FF209EF88A883005209EF880F87A1860482992
+:20F8A000E091E5680A0A0A609C04D09C07D09C06D09C05D060A50148A901850120C4F86884
+:20F8C00085011860DA5A64DD64DFA9C085DEA9C485E0A204A000A701B1DD27014A91DD492F
+:20F8E000FF91DFC8D0F0E6DEE6E0CAD0E97AFA60DA5AA000A6E2F015B1DD91DFC8D0F9E65A
+:20F90000DEE6E0CAD0F28005B1DD91DFC8C4E1D0F77AFA6048DA5AA9028501A9202034F969
+:20F92000A9038501A5E72034F9A200A0002049F97AFA686064E5A0C084E6A214A00091E5FD
+:20F94000C8D0FBE6E6CAD0F66086E384E464E698C03C9002A03B0A0A26E665E49002E6E675
+:20F960000A26E60A26E60A26E60A26E685E5A5E669C085E6A50148A90285012082F96885FF
+:20F980000160A4E3A2038601B1E564018D13D0A2028601B1E5498064018D12D0A5E38D1461
+:20F9A000D09C15D0A5E48D16D09C17D0A90B8D10D09C11D0A20286016048DA5AA601DAA2AF
+:20F9C00002860120D4F9A6E3A4E42049F9FA86017AFA6860A6E3A4E4482049F968C92090E5
+:20F9E00007C980B03A4CBAFAC905F033C9119007C91BD02B4C63FA0AAA7CFCF91EFA91FA1B
+:20FA00006CFA1EFA1EFA94FA73FAADFAA3FAAEFA53FAD0FA64FA54FA84FA1EFA7CFA60A298
+:20FA200000DD33FAF00AE8E8E8E018D0F44CF3FA7C34FA1184FA124BFA1367FA14A3FA1D43
+:20FA400073FA9153FA9250FA9314F9A28086E86064E8606064E3A4E4C8C03CD0034CDCFA5B
+:20FA600084E460604C14F964E364E460A5E3F002C6E360A5E3C94FB002E6E360A5E43A3025
+:20FA80000285E460A5E41AC93CD0034CDCFA85E46064E360A05088F006B1E5C920F0F78436
+:20FAA000E380D0206CFAA4E3A92091E56060A92020BAFAA5E32903D0F560A4E305E891E5A7
+:20FAC000E601A5E791E5C601C8C050F08784E360A4E3A92091E5C8C050D0F960A9C085DECD
+:20FAE00085E0A95085DD64DFA9C085E1A91285E24CF0F8A200DD0AFBF006E8E010D0F66066
+:20FB00008A0A0A0A0A090685E76090051C9F9C1E1F9E8195969798999A9B6401A9FF8D034B
+:20FB2000DCA9008D02DCA9FFA20795E9CA10FB606401A97FA2008D01DC8DAA03AD00DC8DE8
+:20FB4000AB0355E9F0032053FBE8ADAA034A0980B0E460A8B900E58DAC03A8B9C8F549FF67
+:20FB600035E995E9B9C8F52DAB03D00548207DFB6815E995E9ADAB0355E9D0D7608A1A0AB1
+:20FB80000A0AEDAC03A8B9AAFBC95FF01C2FE90E4FEA0F7FEF0CDFE90209804CB1EC291F0F
+:20FBA00080F4B9EAFBC95FD0ED605F715F20325F60312F5E3D5F5F3B2A7E2C403A2E2D6CA5
+:20FBC000702B6E6F6B6D306A6939767568623867793778746663366472355F65737A346159
+:20FBE00077330E85838187060D085F515F20225F7E213F5E3D5F5F5D2A7E3C405B3E2D4CA8
+:20FC0000502B4E4F4B4D304A4929565548422847592758544643264452255F45535A244158
+:20FC200057231085838187020D08C6FCC4FCC4FC58FCC4FCC4FCC4FCBEFC202AEDB0189DE7
+:20FC40000205989D030538E9029D0405A92A85A5A9FC85A62047ED609E07059E08059E091A
+:20FC6000052067FDA9009E05058ABC030520D0F5BD030520D8F5DA8AA85AB90205AAB90438
+:20FC800005A8200FED7AB024A9FF20AEFCB01DA5A3990605B90505C90018D01020F8E6A5FB
+:20FCA000A338F90605C9B490EB20F9FCFA605A48B90205AAB90405A8682012ED7A60BD0367
+:20FCC000054CFAF518608AA8BE0205200CEDBE0505E00A9002A2007CDAFCE4FC03FD12FD35
+:20FCE00060FD66FD4CF9FCC9AAF00EC9FCF009C9FDF005A9FF4CAEFC60A904990505A9F42E
+:20FD00004CAEFCC9FAF005A9D44CAEFCA904990505608003C9AA6048B90805990705B90947
+:20FD20000599080568990905B90805C9F0D008B90705D90905F00BEAB9070529CF4908F023
+:20FD40001960A906990505B907052060FDB908052060FDB909052060FD60A90899050560FB
+:20FD60005A20A7FD7A60609CAD039CAE03DAA2009EAF03E8E020D0F8FA1860A200DD8FFD99
+:20FD8000F009E8E8E018D0F5A90060BD90FD6014136C967098749C11156997710475997DC3
+:20FDA00094729A7A956B9BC9F0F035C9E0F035C9E1F048C984B01DAEAD03F005207BFD807B
+:20FDC00004AABD82FEAEAE03F01EAA29F0C910D0039E9F039CAD039CAE0360207BFD80E526
+:20FDE0008DAE03608DAD036020D4FDAA29F0C910D0049D9F03608AC900F0062002FE20B18E
+:20FE0000EC6048A900A200BCAF03F0031D1FFEE8E009D0F3BA8902D00F8901D01568600178
+:20FE20000102020404080801BD0101291F9D010180EBBD0101203DFE9D010180E0C96190C0
+:20FE400007C97BB003492060A000D95AFEF007C8C8C028D0F560B95BFE60312132403323EA
+:20FE600034243525365E3726382A392830292D5F3D2B5B7B5D7D5C7C3B3A27222C3C2E3EB5
+:20FE80002F3F008900858381828C008A888684096000001410001271310000007A736177B2
+:20FEA000320000637864653433000020766674723500006E62686779360000006D6A75371D
+:20FEC0003800002C6B696F303900002E2F6C3B702D00000027005B3D000018110D5D005CC3
+:20FEE0000000000000000000080000A100A4A7000000A0AEA2A5A6A81BB08BAAA3ABACA988
+:0BFF00009D00000000879E0000000034
+:20FF81004CEEEC4CFEE94C37EC4CFCED4C0EEE4C6BEC4C4AE84C4AE84C6EEC4C7AEC4C8652
+:20FFA100EC4C01E84CC9E74CCFE74C17E84C3AE84C27E84C04E84CC1E74C32EA4C39EA4CBE
+:20FFC10040EA4C67EA4C7EEA4C88EA4C92EA4CB6EA4C6DEB4C5AE84C7EE84C35EC4C35EC75
+:18FFE1004C96EC4C59EB4C86EB4C35EC4CF1EC4CF6EC4C87EC4CF6FF29
+:06FFFA004FF2ABF153F2DF
+:20C0000000000000000000007E81A581BD99817E3C7EDBFFC37E3C0000EEFEFE7C381000E7
+:20C0200010387CFE7C381000003C18FFFF08180010387CFEFE1038000000183C1800000094
+:20C04000FFFFE7C3E7FFFFFF003C428181423C00FFC3BD7E7EBDC3FF0103070F1F3F7FFF66
+:20C06000FFFEFCF8F0E0C0800406070404FCF8000C0A0D0BF9F91F1F00927C44C67C92002E
+:20C08000000060787E7860000000061E7E1E0600187E181818187E186666666666006600BC
+:20C0A000FFB67636363636007EC1DC22221F837E0000007E7E000000187E18187E1800FFA7
+:20C0C000187E18181818180018181818187E1800000406FF06040000002060FF6020000032
+:20C0E000000000C0C0C0FF00002466FF66240000000010387CFE0000000000FE7C3810006A
+:20C100000000000000000000303030303000300066660000000000006C6CFE6CFE6C6C001B
+:20C12000107CD27C867C1000F096FC183E72DE0030483078CECC78000C0C18000000000089
+:20C140001060C0C0C0601000100C0606060C1000005438FE385400000018187E1818000081
+:20C1600000000000000018700000007E00000000000000000000180002060C183060C00025
+:20C180007CCEDEF6E6E67C001838781818183C007CC6060C3060FE007CC6063C06C67C003F
+:20C1A0000E1E3666FE060600FEC0C0FC0606FC007CC6C0FCC6C67C00FE060C18306060000D
+:20C1C0007CC6C67CC6C67C007CC6C67E06C67C000030000000300000003000000030200025
+:20C1E000001C3060301C000000007E007E0000000070180C187000007CC60C183000300069
+:20C200007C829AAAAA9E7C007CC6C6FEC6C6C600FC66667C6666FC007CC6C0C0C0C67C00F0
+:20C22000FC6666666666FC00FE6268786862FE00FE6268786860F0007CC6C6C0DEC67C0020
+:20C24000C6C6C6FEC6C6C6003C18181818183C001E0C0C0C0CCC7800C6CCD8F0D8CCC600F6
+:20C26000F06060606062FE00C6EEFED6C6C6C600C6E6F6DECEC6C6007CC6C6C6C6C67C0064
+:20C28000FC66667C6060F0007CC6C6C6C6C67C0CFC66667C6666E6007CC6C07C06C67C000C
+:20C2A0007E5A181818183C00C6C6C6C6C6C67C00C6C6C6C6C66C3800C6C6C6C6D6EEC600C6
+:20C2C000C66C3838386CC6006666663C18183C00FEC60C183066FE001C18181818181C004C
+:20C2E000C06030180C0602007030303030307000000010386CC6000000000000000000FF79
+:20C30000303018000000000000007C067EC67E00C0C0FCC6C6C6FC0000007CC6C0C67C0053
+:20C3200006067EC6C6C67E0000007CC6FEC07C003C6660F06060600000007EC6C67E067C0B
+:20C34000C0C0FCC6C6C6C6001800381818183C00000C001C0C0CCC78C0C0C6D8F0D8C60045
+:20C360003818181818183C000000EEFED6C6C6000000FCC6C6C6C60000007CC6C6C67C0025
+:20C380000000FCC6C6FCC0C000007EC6C67E06060000DE766060600000007CC07C067C0057
+:20C3A00018187E1818181E000000C6C6C6C67E000000C6C6C66C38000000C6C6D6FEC600B7
+:20C3C0000000C66C386CC6000000C6C6C67E067C0000FE0C1860FE000E18187018180E0003
+:20C3E0001818180018181800E030301C3030E0000000709A0E0000000000183C66FF000040
+:20C400007CC6C0C0C67C18706600C6C6C6C67E000E187CC6FEC07C0018247C067EC67E0072
+:20C4200066007C067EC67E00380C7C067EC67E0018007C067EC67E0000007CC0C07C18706E
+:20C4400018247CC6FEC07C0066007CC6FEC07C0070187CC6FEC07C006600381818183C001C
+:20C460001824381818183C00380C381818183C0066007CC6FEC6C60018007CC6FEC6C600AE
+:20C480000E18FE607860FE0000007C1A7ED87E007ED8D8DEF8D8DE0018247CC6C6C67C0098
+:20C4A00066007CC6C6C67C00380C7CC6C6C67C001824C6C6C6C67E00380CC6C6C6C67E0092
+:20C4C0006600C6C6C67E067C667CC6C6C6C67C00C600C6C6C6C67C00187CC6C0C67C180060
+:20C4E0001E3230783070FE00663C187E183C1800FCC6FCC0CCDECC0E001C3230FC30F00066
+:20C500000E187C067EC67E001A30381818183C000E187CC6C6C67C000E18C6C6C6C67E007F
+:20C520006698FCC6C6C6C6006698E6F6DECEC6007C067EC67E00FE007CC6C6C67C00FE0013
+:20C540001800183060C67C000000FEC0C0C0C0000000FE0606060600C0C0C0DE060C1E0077
+:20C56000C0C0C0CC1C3E0C00300030303030300000366CD86C36000000D86C366CD800004F
+:20C58000AAAAAAAAAAAAAAAAAA55AA55AA55AA5544224422442244221818181818181818F7
+:20C5A000181818F818181818181818F818F81818363636F636363636000000FE3636363615
+:20C5C000000000F818F81818363636F606F636363636363636363636000000FE06F636360D
+:20C5E000363636F606FE0000363636FE00000000181818F818F80000000000F81818181857
+:20C600001818181F00000000181818FF00000000000000FF181818181818181F1818181846
+:20C62000000000FF00000000181818FF181818181818181F181F18183636363736363636D5
+:20C6400036363637303F00000000003F30373636363636F700FF0000000000FF00F7363686
+:20C660003636363730373636000000FF00FF0000363636F700F73636181818FF00FF0000CE
+:20C68000363636FF00000000000000FF00FF1818000000FF363636363636363F0000000013
+:20C6A0001818181F181F00000000001F181F18180000003F36363636363636FF36363636C6
+:20C6C000181818FF18FF1818181818F8000000000000001F18181818FFFFFFFFFFFFFFFF15
+:20C6E00000000000FFFFFFFFF0F0F0F0F0F0F0F00F0F0F0F0F0F0F0FFFFFFFFF000000004A
+:20C7000000007798987700001C3666FCC6C6FCC0FE626060606060000000FF666666660028
+:20C72000FE6230183062FE0000003F66C6CC780000003333333E30F00000FF1818181800BC
+:20C740003C183C66663C183C007CC6FEC67C0000007EC3C3C366E7001E193C66C6CC780074
+:20C76000000066999966000000037CCEE67CC000003EC0FEC03E0000007EC3C3C3C30000C8
+:20C7800000FE00FE00FE000018187E18187E000070180C187000FE001C3060301C00FE0033
+:20C7A000000E1B18181818181818181818D870000018007E001800000076DC0076DC0000C6
+:20C7C0003C663C000000000000183C180000000000000000180000000F0C0C0CEC6C380034
+:20C7E000D8ECCCCC00000000F030C0F0000000000000003C3C3C3C0000000000000000001D
+:00000001FF
diff --git a/Main/roms/kernel_F256jr.lst b/Main/roms/kernel_F256jr.lst
new file mode 100644
index 0000000..d0317c5
--- /dev/null
+++ b/Main/roms/kernel_F256jr.lst
@@ -0,0 +1,5950 @@
+
+; 64tass Turbo Assembler Macro V1.54.1900 listing file
+; 64tass.exe -I . -C -Wall -Werror -Wno-shadow -x --verbose-list -b -L bin/C256jr.lst -o bin/C256jr.bin core/kernel.asm core/iec.asm core/io.asm core/dev.asm core/dev_screen.asm core/dev_keyboard.asm core/rtc.asm core/sys.asm core/keyboard.asm core/video.asm core/device.asm core/token.asm core/vectors.asm core/cli.asm core/cli_list.asm core/cli_load.asm platform/jr/jr.asm platform/jr/iec.asm platform/jr/irq.asm platform/jr/ps2.asm platform/jr/console.asm platform/jr/c64kbd.asm platform/jr/FPGA/TinyVicky_Def.asm platform/jr/FPGA/interrupt_def.asm hardware/hardware.asm hardware/i8042.asm hardware/ps2.asm hardware/ps2_kbd2.asm hardware/keys.asm
+; Wed Oct 19 20:40:21 2022
+
+;Offset ;Hex ;Monitor ;Source
+
+;****** Processing input file: core/kernel.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; Memory layout and general support for TinyCore device drivers.
+
+ .cpu "w65c02"
+
+ ;reserved = $0000 ; $00 - $02
+ ;basic = $0002 ; $02 - $90
+ * = $0090 ; $90 - $fb kernel
+ * = $00a3 ; $90 - $fb kernel
+.00a3 DP .dsection dp
+ .cerror * >= $00fb, "Out of dp space."
+
+ * = $0100 ; Stack
+>0100 Stack .fill $100
+
+ * = $0200 ; BASIC, some KERNAL
+>0200 Tokens .fill $90 ; BASIC
+.0290 p2end
+.0290 tokens_start
+ .cerror * > $02ff, "Out of kmem space."
+
+ * = $0300
+>0300 .fill $34 ; Shared vectors
+.0334 p3end
+.0334 KBUF .dsection kbuf ; kernal
+.0344 KMEM .dsection kmem ; KERNAL
+ .cerror * > $03ff, "Out of kbuf space."
+
+ * = $0400 ; Device tables (from the TinyCore kernel)
+ .dsection kpages
+
+
+=$800 free_mem = $800 ; Traditional start.
+
+
+
+ ; $e000 - $e500 contains a simple command line shell which may be
+ ; used to load applications in the absence of either CBM BASIC or
+ ; a more general ROM. If CBM BASIC is bundled, it will overwrite
+ ; this section of the kernel.
+
+ * = $e000
+ .dsection shell
+ .cerror * > $e4ff, "Out of shell space."
+
+ ; Start of the kernel proper, pushed back to accomodate the use of
+ ; CBM BASIC.
+ * = $e500
+ .dsection tables
+ .dsection kernel
+ .cerror * > $fff0, "Out of kernel space."
+
+ * = $ff81
+ kernel .namespace
+ .dstruct vectors
+.ff81 4c ee ec jmp $ecee SCINIT jmp scinit
+.ff84 4c fe e9 jmp $e9fe IOINIT jmp io.ioinit
+.ff87 4c 37 ec jmp $ec37 RAMTAS jmp ramtas
+.ff8a 4c fc ed jmp $edfc RESTOR jmp restor
+.ff8d 4c 0e ee jmp $ee0e VECTOR jmp vector
+.ff90 4c 6b ec jmp $ec6b SETMSG jmp setmsg
+.ff93 4c 4a e8 jmp $e84a LSTNSA jmp iec.lstnsa
+.ff96 4c 4a e8 jmp $e84a TALKSA jmp iec.talksa
+.ff99 4c 6e ec jmp $ec6e MEMBOT jmp membot
+.ff9c 4c 7a ec jmp $ec7a MEMTOP jmp memtop
+.ff9f 4c 86 ec jmp $ec86 SCNKEY jmp scnkey
+.ffa2 4c 01 e8 jmp $e801 SETTMO jmp iec.settmo
+.ffa5 4c c9 e7 jmp $e7c9 IECIN jmp iec.iecin
+.ffa8 4c cf e7 jmp $e7cf IECOUT jmp iec.iecout
+.ffab 4c 17 e8 jmp $e817 UNTALK jmp iec.untalk
+.ffae 4c 3a e8 jmp $e83a UNLSTN jmp iec.unlstn
+.ffb1 4c 27 e8 jmp $e827 LISTEN jmp iec.listen
+.ffb4 4c 04 e8 jmp $e804 TALK jmp iec.talk
+.ffb7 4c c1 e7 jmp $e7c1 READST jmp iec.readst
+.ffba 4c 32 ea jmp $ea32 SETLFS jmp io.setlfs
+.ffbd 4c 39 ea jmp $ea39 SETNAM jmp io.setnam
+.ffc0 4c 40 ea jmp $ea40 OPEN jmp io.open
+.ffc3 4c 67 ea jmp $ea67 CLOSE jmp io.close
+.ffc6 4c 7e ea jmp $ea7e CHKIN jmp io.chkin
+.ffc9 4c 88 ea jmp $ea88 CHKOUT jmp io.chkout
+.ffcc 4c 92 ea jmp $ea92 CLRCHN jmp io.clrchn
+.ffcf 4c b6 ea jmp $eab6 CHRIN jmp io.chrin
+.ffd2 4c 6d eb jmp $eb6d CHROUT jmp io.chrout
+.ffd5 4c 5a e8 jmp $e85a LOAD jmp iec.load
+.ffd8 4c 7e e8 jmp $e87e SAVE jmp iec.save
+.ffdb 4c 35 ec jmp $ec35 SETTIM jmp settim
+.ffde 4c 35 ec jmp $ec35 RDTIM jmp rdtim
+.ffe1 4c 96 ec jmp $ec96 STOP jmp keyboard.stop
+.ffe4 4c 59 eb jmp $eb59 GETIN jmp io.getin
+.ffe7 4c 86 eb jmp $eb86 CLALL jmp io.clall
+.ffea 4c 35 ec jmp $ec35 UDTIM jmp udtim
+.ffed 4c f1 ec jmp $ecf1 SCREEN jmp screen
+.fff0 4c f6 ec jmp $ecf6 PLOT jmp plot
+.fff3 4c 87 ec jmp $ec87 IOBASE jmp iobase
+ .ends
+ .endn
+
+ .section kpages
+.0400 frame
+>0400 Devices .fill 256
+>0500 DevState .fill 256
+ .send
+
+ .namespace kernel
+
+ .section dp
+>00a3 ticks .word ?
+>00a5 src .word ? ; src ptr for copy operations.
+>00a7 dest .word ? ; dest ptr for copy operations.
+>00a9 size .word ? ; size of data at dest.
+>00ab tos_l .byte ? ; For BCD conversions
+>00ac tos_h .byte ?
+>00ad fname .word ? ; file name pointer
+>00af fname_len .byte ? ; file name length
+>00b0 cur_logical .byte ? ; current logical device
+>00b1 cur_device .byte ? ; current assoc dev #
+>00b2 cur_addr .byte ? ; current associated secondary addr
+ .send
+
+
+ .section dp
+>00b3 mem_start .word ?
+>00b5 mem_end .word ?
+>00b7 msg_switch .byte ?
+>00b8 current_dev .byte ?
+ .send
+
+
+=1 TOO_MANY_FILES = 1
+=2 FILE_OPEN = 2
+=3 FILE_NOT_OPEN = 3
+=4 FILE_NOT_FOUND = 4
+=5 DEVICE_NOT_PRESENT = 5
+=6 NOT_INPUT_FILE = 6
+=7 NOT_OUTPUT_FILE = 7
+=8 MISSING_FILE_NAME = 8
+=9 ILLEGAL_DEVICE_NUMBER = 9
+
+ .section kernel
+
+.e600 copyright
+>e600 4f 70 65 6e 4b 45 52 4e .text "OpenKERNAL - a clean-room implementation of the C64 KERNAL ABI",13
+>e608 41 4c 20 2d 20 61 20 63 6c 65 61 6e 2d 72 6f 6f
+>e618 6d 20 69 6d 70 6c 65 6d 65 6e 74 61 74 69 6f 6e
+>e628 20 6f 66 20 74 68 65 20 43 36 34 20 4b 45 52 4e
+>e638 41 4c 20 41 42 49 0d
+>e63f 43 6f 70 79 72 69 67 68 .text "Copyright 2022 Jessie Oberreuter .",13
+>e647 74 20 32 30 32 32 20 4a 65 73 73 69 65 20 4f 62
+>e657 65 72 72 65 75 74 65 72 20 3c 47 61 64 67 65 74
+>e667 40 48 61 63 6b 77 72 65 6e 63 68 4c 61 62 73 2e
+>e677 63 6f 6d 3e 2e 0d
+>e67d 52 65 6c 65 61 73 65 64 .text "Released under the GPL3 license with the kernel exception:",13
+>e685 20 75 6e 64 65 72 20 74 68 65 20 47 50 4c 33 20
+>e695 6c 69 63 65 6e 73 65 20 77 69 74 68 20 74 68 65
+>e6a5 20 6b 65 72 6e 65 6c 20 65 78 63 65 70 74 69 6f
+>e6b5 6e 3a 0d
+>e6b8 61 70 70 6c 69 63 61 74 .text "applications which simply use the ABI are not 'derived works'.", 13
+>e6c0 69 6f 6e 73 20 77 68 69 63 68 20 73 69 6d 70 6c
+>e6d0 79 20 75 73 65 20 74 68 65 20 41 42 49 20 61 72
+>e6e0 65 20 6e 6f 74 20 27 64 65 72 69 76 65 64 20 77
+>e6f0 6f 72 6b 73 27 2e 0d
+>e6f7 00 .byte 0
+
+ thread .namespace ; For devices borrowed from the TinyCore kernel.
+.e6f8 cb wai yield wai
+.e6f9 60 rts rts
+ .endn
+
+.e6fa init
+ ; Initialize device driver services.
+.e6fa 20 a6 ed jsr $eda6 jsr token.init
+.e6fd 20 18 ed jsr $ed18 jsr device.init
+.e700 20 8c ec jsr $ec8c jsr keyboard.init
+.e703 60 rts rts
+
+.e704 tick
+.e704 e6 a3 inc $a3 inc kernel.ticks
+.e706 d0 02 bne $e70a bne _end
+.e708 e6 a4 inc $a4 inc kernel.ticks+1
+.e70a 60 rts _end rts
+
+
+.e70b banner
+.e70b a9 00 lda #$00 lda #copyright
+.e711 85 a6 sta $a6 sta src+1
+.e713 4c 16 e7 jmp $e716 jmp puts
+
+.e716 puts
+.e716 a0 00 ldy #$00 ldy #0
+.e718 b1 a5 lda ($a5),y _loop lda (src),y
+.e71a f0 0a beq $e726 beq _done
+.e71c 20 d2 ff jsr $ffd2 jsr CHROUT
+.e71f c8 iny iny
+.e720 d0 f6 bne $e718 bne _loop
+.e722 e6 a6 inc $a6 inc src+1
+.e724 80 f2 bra $e718 bra _loop
+.e726 _done
+.e726 18 clc clc
+.e727 60 rts rts
+
+.e728 error
+.e728 48 pha pha
+.e729 a9 4a lda #$4a lda #<_msg
+.e72b 85 a5 sta $a5 sta src
+.e72d a9 e7 lda #$e7 lda #>_msg
+.e72f 85 a6 sta $a6 sta src+1
+.e731 a0 00 ldy #$00 ldy #0
+.e733 b1 a5 lda ($a5),y _loop lda (src),y
+.e735 f0 06 beq $e73d beq _done
+.e737 20 b9 f9 jsr $f9b9 jsr platform.console.putc
+.e73a c8 iny iny
+.e73b 80 f6 bra $e733 bra _loop
+.e73d _done
+.e73d 68 pla pla
+.e73e 18 clc clc
+.e73f 69 30 adc #$30 adc #'0'
+.e741 20 b9 f9 jsr $f9b9 jsr platform.console.putc
+.e744 a9 0d lda #$0d lda #13
+.e746 20 b9 f9 jsr $f9b9 jsr platform.console.putc
+.e749 60 rts rts
+>e74a 45 72 72 6f 72 20 00 _msg .null "Error "
+
+.e751 a0 ff ldy #$ff strcmp ldy #$ff
+.e753 c8 iny _loop iny
+.e754 b1 a5 lda ($a5),y lda (src),y
+.e756 f0 04 beq $e75c beq _out
+.e758 51 a7 eor ($a7),y eor (dest),y
+.e75a f0 f7 beq $e753 beq _loop
+.e75c 60 rts _out rts
+
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ; CBM stuff below ... move to another file.
+
+=$a000 basic = $a000
+
+.e75d start
+.e75d 64 01 stz $01 stz $1 ; Default to the sys map.
+
+.e75f 20 37 ec jsr $ec37 jsr ramtas
+.e762 20 fc ed jsr $edfc jsr restor
+.e765 20 81 ff jsr $ff81 jsr SCINIT
+.e768 20 84 ff jsr $ff84 jsr IOINIT
+.e76b 4c 6e e7 jmp $e76e jmp chain
+
+.e76e chain
+.e76e a2 00 ldx #$00 ldx #0
+.e770 _loop
+ ; Point src and dest at the expected signature and offset
+.e770 bd 94 e7 lda $e794,x lda _table,x
+.e773 85 a5 sta $a5 sta src+0
+.e775 e8 inx inx
+.e776 bd 94 e7 lda $e794,x lda _table,x
+.e779 85 a6 sta $a6 sta src+1
+.e77b e8 inx inx
+.e77c bd 94 e7 lda $e794,x lda _table,x
+.e77f 85 a7 sta $a7 sta dest+0
+.e781 e8 inx inx
+.e782 bd 94 e7 lda $e794,x lda _table,x
+.e785 85 a8 sta $a8 sta dest+1
+.e787 e8 inx inx
+
+ ; Chain on signature match.
+.e788 20 51 e7 jsr $e751 jsr strcmp
+.e78b d0 03 bne $e790 bne _next
+.e78d 7c 94 e7 jmp ($e794,x) jmp (_table,x)
+.e790 e8 inx _next inx
+.e791 e8 inx inx
+.e792 80 dc bra $e770 bra _loop
+.e794 _table
+>e794 a6 e7 04 a0 af e7 .word cbm_bytes, $a004, cbm_start ; Check for CBM BASIC
+>e79a b2 e7 04 80 ba e7 .word bas02_bytes, $8004, bas02_start ; Check for BASIC02
+>e7a0 bd e7 00 e0 be e7 .word cli_bytes, $e000, cli_start ; Always last
+
+>e7a6 43 42 4d 42 41 53 49 43 cbm_bytes .null "CBMBASIC"
+>e7ae 00
+.e7af 6c 00 a0 jmp ($a000) cbm_start jmp (basic)
+
+>e7b2 42 41 53 49 43 30 32 00 bas02_bytes .null "BASIC02"
+.e7ba 4c 00 80 jmp $8000 bas02_start jmp $8000
+
+>e7bd 00 cli_bytes .null "" ; Fall-through match.
+.e7be 4c ab e1 jmp $e1ab cli_start jmp shell.start
+
+
+.0000 vectors .struct
+.0000 4c ee ec jmp $ecee SCINIT jmp scinit
+.0003 4c fe e9 jmp $e9fe IOINIT jmp io.ioinit
+.0006 4c 37 ec jmp $ec37 RAMTAS jmp ramtas
+.0009 4c fc ed jmp $edfc RESTOR jmp restor
+.000c 4c 0e ee jmp $ee0e VECTOR jmp vector
+.000f 4c 6b ec jmp $ec6b SETMSG jmp setmsg
+.0012 4c 4a e8 jmp $e84a LSTNSA jmp iec.lstnsa
+.0015 4c 4a e8 jmp $e84a TALKSA jmp iec.talksa
+.0018 4c 6e ec jmp $ec6e MEMBOT jmp membot
+.001b 4c 7a ec jmp $ec7a MEMTOP jmp memtop
+.001e 4c 86 ec jmp $ec86 SCNKEY jmp scnkey
+.0021 4c 01 e8 jmp $e801 SETTMO jmp iec.settmo
+.0024 4c c9 e7 jmp $e7c9 IECIN jmp iec.iecin
+.0027 4c cf e7 jmp $e7cf IECOUT jmp iec.iecout
+.002a 4c 17 e8 jmp $e817 UNTALK jmp iec.untalk
+.002d 4c 3a e8 jmp $e83a UNLSTN jmp iec.unlstn
+.0030 4c 27 e8 jmp $e827 LISTEN jmp iec.listen
+.0033 4c 04 e8 jmp $e804 TALK jmp iec.talk
+.0036 4c c1 e7 jmp $e7c1 READST jmp iec.readst
+.0039 4c 32 ea jmp $ea32 SETLFS jmp io.setlfs
+.003c 4c 39 ea jmp $ea39 SETNAM jmp io.setnam
+.003f 4c 40 ea jmp $ea40 OPEN jmp io.open
+.0042 4c 67 ea jmp $ea67 CLOSE jmp io.close
+.0045 4c 7e ea jmp $ea7e CHKIN jmp io.chkin
+.0048 4c 88 ea jmp $ea88 CHKOUT jmp io.chkout
+.004b 4c 92 ea jmp $ea92 CLRCHN jmp io.clrchn
+.004e 4c b6 ea jmp $eab6 CHRIN jmp io.chrin
+.0051 4c 6d eb jmp $eb6d CHROUT jmp io.chrout
+.0054 4c 5a e8 jmp $e85a LOAD jmp iec.load
+.0057 4c 7e e8 jmp $e87e SAVE jmp iec.save
+.005a 4c 35 ec jmp $ec35 SETTIM jmp settim
+.005d 4c 35 ec jmp $ec35 RDTIM jmp rdtim
+.0060 4c 96 ec jmp $ec96 STOP jmp keyboard.stop
+.0063 4c 59 eb jmp $eb59 GETIN jmp io.getin
+.0066 4c 86 eb jmp $eb86 CLALL jmp io.clall
+.0069 4c 35 ec jmp $ec35 UDTIM jmp udtim
+.006c 4c f1 ec jmp $ecf1 SCREEN jmp screen
+.006f 4c f6 ec jmp $ecf6 PLOT jmp plot
+.0072 4c 87 ec jmp $ec87 IOBASE jmp iobase
+ .ends
+
+
+ .send
+ .endn
+
+
+;****** Processing input file: core/iec.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ .cpu "w65c02"
+
+ .namespace kernel
+ iec .namespace
+
+ .section dp
+>00b9 queue .byte ? ; queue to detect when to signal end-of-data
+>00ba queued .byte ? ; negative if a byte is in the queue
+>00bb status .byte ? ; IEC status
+ .send
+
+ .section kernel
+
+=1 TIMEOUT_WRITE = 1
+=2 TIMEOUT_READ = 2
+=16 MISMATCH = 16
+=64 EOI = 64
+=128 NO_DEVICE = 128
+
+.e7c1 readst
+.e7c1 a5 bb lda $bb lda status
+.e7c3 60 rts rts
+
+.e7c4 error
+
+ ; Internal function.
+ ; Updates io.status with the bits set in A.
+ ; Does not change the state of the carry.
+
+.e7c4 05 bb ora $bb ora status
+.e7c6 85 bb sta $bb sta status
+.e7c8 60 rts rts
+
+.e7c9 iecin
+.e7c9 20 8d f3 jsr $f38d jsr platform.iec.read_byte
+.e7cc b0 f6 bcs $e7c4 bcs error
+.e7ce 60 rts rts
+
+.e7cf iecout
+.e7cf queue_data
+
+ ; Delays writes of data bytes to the IEC bus by one character.
+ ; This enables the stack to automatically determine when to
+ ; signal that this is the last data byte in its sequence.
+ ; On error, sets carry and returns the READST value in A.
+
+.e7cf 24 ba bit $ba bit queued
+.e7d1 30 05 bmi $e7d8 bmi _swap
+.e7d3 85 b9 sta $b9 sta queue
+.e7d5 c6 ba dec $ba dec queued
+.e7d7 60 rts rts
+.e7d8 _swap
+.e7d8 48 pha pha
+.e7d9 a5 b9 lda $b9 lda queue
+.e7db 20 ca f3 jsr $f3ca jsr platform.iec.write_byte
+.e7de 90 03 bcc $e7e3 bcc _okay
+.e7e0 20 c4 e7 jsr $e7c4 jsr error
+.e7e3 68 pla _okay pla
+.e7e4 85 b9 sta $b9 sta queue
+.e7e6 60 rts _out rts
+
+
+.e7e7 flush_queue
+
+ ; Internal function
+ ; If there is a byte remaining in the queue, send it
+ ; using the IEC "last byte" protocol.
+ ; On error, sets carry and returns the READST value in A.
+
+.e7e7 24 ba bit $ba bit queued
+.e7e9 10 09 bpl $e7f4 bpl _done
+.e7eb a5 b9 lda $b9 lda queue
+.e7ed 20 d4 f3 jsr $f3d4 jsr platform.iec.write_last_byte
+.e7f0 b0 d2 bcs $e7c4 bcs error
+.e7f2 64 ba stz $ba stz queued
+.e7f4 60 rts _done rts
+
+
+.e7f5 reset
+
+ ; Internal function.
+ ; Clears the write queue and the READST value.
+
+.e7f5 64 bb stz $bb stz status
+.e7f7 64 ba stz $ba stz queued
+.e7f9 60 rts rts
+
+.e7fa check_dev
+
+ ; Internal function.
+ ; Ensures that the device number in A is valid for the IEC
+ ; bus. Sets the carry and returns A=#NO_DEVICE on error.
+
+.e7fa c9 10 cmp #$10 cmp #16
+.e7fc 90 02 bcc $e800 bcc _out
+.e7fe a9 80 lda #$80 lda #NO_DEVICE
+.e800 60 rts _out rts
+
+.e801 settmo
+ ; Implemented by platform; repeated here for consistency.
+
+.e801 4c 8a f3 jmp $f38a jmp platform.iec.settmo
+
+.e804 talk
+
+ ; IN: A = device (8..15)
+ ; NOTE: The iec routines don't appear to return any status;
+ ; instead, users are expected to call READST after
+ ; each invocation. In this particular implementation,
+ ; carry will be set, and A will contain the value to
+ ; be returned by READST.
+
+.e804 20 f5 e7 jsr $e7f5 jsr reset
+.e807 20 fa e7 jsr $e7fa jsr check_dev
+.e80a b0 08 bcs $e814 bcs _error
+.e80c 09 40 ora #$40 ora #$40
+.e80e 20 de f3 jsr $f3de jsr platform.iec.send_atn_byte
+.e811 b0 01 bcs $e814 bcs _error
+.e813 60 rts rts
+.e814 4c c4 e7 jmp $e7c4 _error jmp error
+
+
+.e817 untalk
+
+ ; NOTE: The iec routines don't appear to return any status;
+ ; instead, users are expected to call READST after
+ ; each invocation. In this particular implementation,
+ ; carry will be set, and A will contain the value to
+ ; be returned by READST.
+
+.e817 20 e7 e7 jsr $e7e7 jsr flush_queue
+.e81a b0 08 bcs $e824 bcs _error
+.e81c a9 5f lda #$5f lda #$5f
+.e81e 20 f1 f3 jsr $f3f1 jsr platform.iec.send_atn_last_byte
+.e821 b0 01 bcs $e824 bcs _error
+.e823 60 rts rts
+.e824 4c c4 e7 jmp $e7c4 _error jmp error
+
+.e827 listen
+
+ ; IN: A = device (8..15)
+ ; NOTE: The iec routines don't appear to return any status;
+ ; instead, users are expected to call READST after
+ ; each invocation. In this particular implementation,
+ ; carry will be set, and A will contain the value to
+ ; be returned by READST.
+
+.e827 20 f5 e7 jsr $e7f5 jsr reset
+.e82a 20 fa e7 jsr $e7fa jsr check_dev
+.e82d b0 08 bcs $e837 bcs _error
+.e82f 09 20 ora #$20 ora #$20
+.e831 20 de f3 jsr $f3de jsr platform.iec.send_atn_byte
+.e834 b0 01 bcs $e837 bcs _error
+.e836 60 rts rts
+.e837 4c c4 e7 jmp $e7c4 _error jmp error
+
+
+.e83a unlstn
+
+ ; NOTE: The iec routines don't appear to return any status;
+ ; instead, users are expected to call READST after
+ ; each invocation. In this particular implementation,
+ ; carry will be set, and A will contain the value to
+ ; be returned by READST.
+
+.e83a 20 e7 e7 jsr $e7e7 jsr flush_queue
+.e83d b0 08 bcs $e847 bcs _error
+.e83f a9 3f lda #$3f lda #$3f
+.e841 20 f1 f3 jsr $f3f1 jsr platform.iec.send_atn_last_byte
+.e844 b0 01 bcs $e847 bcs _error
+.e846 60 rts rts
+.e847 4c c4 e7 jmp $e7c4 _error jmp error
+
+.e84a talksa
+.e84a lstnsa
+
+ ; IN: A = device (8..15)
+ ; NOTE: The iec routines don't appear to return any status;
+ ; instead, users are expected to call READST after
+ ; each invocation. In this particular implementation,
+ ; carry will be set, and A will contain the value to
+ ; be returned by READST.
+ ;
+ ; These two routines are nominally separate to hint the kernel
+ ; about what the bus is expected to do. On the C256 Jr., the
+ ; state machine in the FPGA automatically handles both cases.
+
+.e84a 20 fa e7 jsr $e7fa jsr check_dev
+.e84d b0 08 bcs $e857 bcs _error
+.e84f 09 60 ora #$60 ora #$60
+.e851 20 de f3 jsr $f3de jsr platform.iec.send_atn_byte
+.e854 b0 01 bcs $e857 bcs _error
+.e856 60 rts rts
+.e857 4c c4 e7 jmp $e7c4 _error jmp error
+
+
+
+.e85a load
+
+ ; IN: Device and sub set using SETLFS
+ ; File name set using SETNAM
+ ; A= 0->read, 1..255->verify
+ ; X/Y = dest address (if secondary != 0)
+ ;
+ ; OUT: X/Y = end address, or carry set and A = error.
+ ;
+ ; NOTE: On error, A is a KERNAL error, NOT a READST vlaue!
+
+ ; initialize dest; may be overriden later
+.e85a 86 a7 stx $a7 stx dest+0
+.e85c 84 a8 sty $a8 sty dest+1
+
+ ; X = read/verify
+.e85e aa tax tax
+
+ ; Y = flag to use address in file over dest address above.
+.e85f a4 b2 ldy $b2 ldy cur_addr
+
+ ; Open the file for read.
+ ; NOTE: returns a KERNAL error; must check READST as well!
+.e861 20 80 e8 jsr $e880 jsr open_file_for_read
+.e864 b0 11 bcs $e877 bcs _out
+.e866 20 b7 ff jsr $ffb7 jsr READST
+.e869 09 00 ora #$00 ora #0
+.e86b d0 0b bne $e878 bne _error
+
+ ; Read the file, sets X/Y to last address.
+.e86d 20 eb e8 jsr $e8eb jsr read_verify_pgm_data
+.e870 b0 06 bcs $e878 bcs _error
+
+.e872 20 d2 e8 jsr $e8d2 jsr close_file
+.e875 b0 01 bcs $e878 bcs _error
+
+.e877 60 rts _out rts
+.e878 _error
+.e878 20 c4 e7 jsr $e7c4 jsr error ; Update status for READST.
+.e87b 18 clc clc ; I/O errors aren't KERANL errors...
+.e87c 80 f9 bra $e877 bra _out
+
+
+.e87e save
+.e87e 38 sec sec
+.e87f 60 rts rts
+
+.e880 open_file_for_read
+
+ ; Internal function.
+ ;
+ ; IN: Device and sub set using SETLFS
+ ; File name set using SETNAM
+ ;
+ ; OUT: Carry set and A = a KERNEL error
+ ; (MISSING_FILE_NAME) on error.
+ ;
+ ; NOTE: On IEC error, Sets READST and CLEARS the carry.
+
+.e880 a5 af lda $af lda fname_len
+.e882 d0 04 bne $e888 bne _open
+.e884 a9 08 lda #$08 lda #MISSING_FILE_NAME ; Only KERNAL error returned.
+.e886 38 sec sec
+.e887 60 rts rts
+
+.e888 _open
+.e888 a5 b1 lda $b1 lda cur_device
+.e88a 20 b1 ff jsr $ffb1 jsr LISTEN
+.e88d b0 20 bcs $e8af bcs _error
+
+.e88f a9 f0 lda #$f0 lda #$f0 ; Open channel 0
+.e891 20 de f3 jsr $f3de jsr platform.iec.send_atn_byte
+.e894 b0 19 bcs $e8af bcs _error
+
+.e896 20 b1 e8 jsr $e8b1 jsr send_fname
+.e899 b0 14 bcs $e8af bcs _error
+
+.e89b 20 ae ff jsr $ffae jsr UNLSTN
+.e89e b0 0f bcs $e8af bcs _error
+
+.e8a0 a5 b1 lda $b1 lda cur_device
+.e8a2 20 b4 ff jsr $ffb4 jsr TALK
+.e8a5 b0 08 bcs $e8af bcs _error
+
+.e8a7 a9 00 lda #$00 lda #0 ; Channel 0; Channel 1 magic is internal.
+.e8a9 20 96 ff jsr $ff96 jsr TALKSA ; Reopen channel; tells drive to send.
+.e8ac b0 01 bcs $e8af bcs _error
+
+.e8ae 60 rts rts
+
+.e8af _error
+ ; Not a KERNEL error; must call READST for more information.
+.e8af 18 clc clc
+.e8b0 60 rts rts
+
+
+.e8b1 send_fname
+
+ ; Internal function
+ ; Writes the filename (from SETFN) via the IECOUT.
+ ; Returns the IEC error status.
+
+.e8b1 5a phy phy
+.e8b2 a0 00 ldy #$00 ldy #0
+.e8b4 _loop
+.e8b4 b1 ad lda ($ad),y lda (fname),y
+.e8b6 20 c6 e8 jsr $e8c6 jsr to_upper
+.e8b9 20 a8 ff jsr $ffa8 jsr IECOUT
+.e8bc b0 06 bcs $e8c4 bcs _out
+.e8be c8 iny iny
+.e8bf c4 af cpy $af cpy fname_len
+.e8c1 90 f1 bcc $e8b4 bcc _loop
+.e8c3 18 clc clc
+.e8c4 _out
+.e8c4 7a ply ply
+.e8c5 60 rts rts
+
+.e8c6 to_upper
+.e8c6 c9 61 cmp #$61 cmp #'a'
+.e8c8 90 06 bcc $e8d0 bcc _okay
+.e8ca c9 7b cmp #$7b cmp #'z'+1
+.e8cc b0 02 bcs $e8d0 bcs _okay
+.e8ce 49 20 eor #$20 eor #$20
+.e8d0 18 clc _okay clc
+.e8d1 60 rts rts
+
+.e8d2 close_file
+
+ ; Internal function
+ ; Closes the current reading or writing file.
+ ; Returns the IEC error status.
+
+.e8d2 20 ab ff jsr $ffab jsr UNTALK
+.e8d5 b0 13 bcs $e8ea bcs _out
+
+.e8d7 a5 b1 lda $b1 lda cur_device
+.e8d9 20 b1 ff jsr $ffb1 jsr LISTEN
+.e8dc b0 0c bcs $e8ea bcs _out
+
+.e8de a9 e0 lda #$e0 lda #$e0 ; Close channel 0
+.e8e0 20 de f3 jsr $f3de jsr platform.iec.send_atn_byte
+.e8e3 b0 05 bcs $e8ea bcs _out
+
+.e8e5 20 ae ff jsr $ffae jsr UNLSTN
+.e8e8 b0 00 bcs $e8ea bcs _out
+
+.e8ea 60 rts _out rts
+
+
+.e8eb read_verify_pgm_data
+
+ ; Internal funciton.
+ ; Implements READ/VERIFY for PGM files.
+ ;
+ ; IN: Y=0 (load to dest) or Y=1 (load to embedded address)
+ ; X=0 (read) or 1..255 (verify)
+ ;
+ ; Out: X:Y = last address read/verified
+ ; On error, Carry set, and A = IEC error (READST value)
+
+
+ ; X = 0/2 read/verify
+.e8eb aa tax tax
+.e8ec f0 02 beq $e8f0 beq _emode ; read
+.e8ee a2 02 ldx #$02 ldx #2 ; verify
+.e8f0 ea nop _emode nop
+
+ ; Read the would-be load-address into src
+.e8f1 20 a5 ff jsr $ffa5 jsr IECIN
+.e8f4 b0 21 bcs $e917 bcs _notfound
+.e8f6 85 a5 sta $a5 sta src+0
+.e8f8 20 a5 ff jsr $ffa5 jsr IECIN
+.e8fb b0 1a bcs $e917 bcs _notfound
+.e8fd 85 a6 sta $a6 sta src+1
+
+ ; Update dest ptr if the sub-channel (Y) is 1
+.e8ff 98 tya tya
+.e900 f0 08 beq $e90a beq _edest
+.e902 a5 a5 lda $a5 lda src+0
+.e904 85 a7 sta $a7 sta dest+0
+.e906 a5 a6 lda $a6 lda src+1
+.e908 85 a8 sta $a8 sta dest+1
+.e90a ea nop _edest nop
+
+.e90b _loop
+.e90b 20 a5 ff jsr $ffa5 jsr IECIN
+.e90e 90 0e bcc $e91e bcc _found
+.e910 c9 40 cmp #$40 cmp #EOI
+.e912 f0 15 beq $e929 beq _done
+.e914 4c c4 e7 jmp $e7c4 _error jmp error ; Forward the IEC error status.
+.e917 20 c4 e7 jsr $e7c4 _notfound jsr error ; Forward the IEC error status.
+.e91a a9 04 lda #$04 lda #FILE_NOT_FOUND
+.e91c 38 sec sec
+.e91d 60 rts rts
+.e91e 7c 2f e9 jmp ($e92f,x) _found jmp (_op,x)
+.e921 _cont
+.e921 e6 a7 inc $a7 inc dest
+.e923 d0 02 bne $e927 bne _next
+.e925 e6 a8 inc $a8 inc dest+1
+.e927 _next
+.e927 90 e2 bcc $e90b bcc _loop
+.e929 18 clc _done clc
+.e92a _out
+.e92a a6 a7 ldx $a7 ldx dest+0
+.e92c a4 a8 ldy $a8 ldy dest+1
+.e92e 60 rts rts
+.e92f _op
+>e92f 33 e9 .word _load
+>e931 37 e9 .word _verify
+.e933 _load
+.e933 92 a7 sta ($a7) sta (dest)
+.e935 80 ea bra $e921 bra _cont
+.e937 _verify
+.e937 d2 a7 cmp ($a7) cmp (dest)
+.e939 f0 e6 beq $e921 beq _cont
+.e93b a9 10 lda #$10 lda #MISMATCH
+.e93d 38 sec sec
+.e93e 20 14 e9 jsr $e914 jsr _error
+.e941 80 e7 bra $e92a bra _out ; Mismatch still returns X/Y.
+
+ .send
+ .endn
+ .endn
+
+
+;****** Processing input file: core/io.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ .cpu "w65c02"
+
+ .namespace kernel
+ .namespace io
+
+ .section dp
+
+>00bc cur_in .byte ? ; current input device
+>00bd cur_out .byte ? ; current output device
+>00be io_last .byte ? ; device # of most recent read/write operation
+>00bf io_status .byte ? ; status from most recent read/write operation
+
+ ; These could be moved to the file object
+>00c0 scraping .byte ? ; screen scraping bool.
+>00c1 scrape_x .byte ? ; screen scrape x value.
+>00c2 quoted .byte ? ; screen scrape inside quotes.
+
+ .send
+
+=10 MAX_FILES = 10
+
+ .section kmem
+>0344 files .fill MAX_FILES * 8
+ .send
+
+ file .namespace
+ .virtual files
+>0344 state .byte ? ; OPEN/CLOSED state
+>0345 device .byte ?
+>0346 secondary .byte ?
+ .endv
+ .endn
+
+ .section kernel
+
+.e943 spin
+.e943 a9 02 lda #$02 lda #2
+.e945 85 01 sta $01 sta $1
+.e947 _loop
+.e947 ad 00 c0 lda $c000 lda $c000
+.e94a 1a inc a inc a
+.e94b 8d 00 c0 sta $c000 sta $c000
+.e94e 80 f7 bra $e947 bra _loop
+
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+.e950 find:
+
+ ; IN: A = logical file #
+ ; SUCCESS:
+ ; Carry clear
+ ; X->device entry
+ ; Y->file entry
+ ; FAIL:
+ ; Carry set
+ ; A = FILE_NOT_OPEN or TOO_MANY_FILES
+
+.e950 20 6c e9 jsr $e96c jsr link
+.e953 b0 08 bcs $e95d bcs _out
+.e955 b9 44 03 lda $0344,y lda file.state,y
+.e958 d0 04 bne $e95e bne _device
+.e95a a9 03 lda #$03 lda #FILE_NOT_OPEN
+.e95c 38 sec sec
+.e95d 60 rts _out rts
+
+.e95e _device
+.e95e b9 45 03 lda $0345,y lda file.device,y
+.e961 c9 08 cmp #$08 cmp #8
+.e963 90 02 bcc $e967 bcc _found
+.e965 a9 08 lda #$08 lda #8
+
+.e967 _found
+.e967 0a asl a asl a
+.e968 0a asl a asl a
+.e969 0a asl a asl a
+.e96a aa tax tax
+.e96b 60 rts rts
+
+.e96c link:
+ ; IN: A = logical file #
+ ; SUCCESS:
+ ; Carry clear
+ ; Y->file entry
+ ; FAIL:
+ ; Carry set
+ ; A = TOO_MANY_FILES
+
+.e96c c9 0a cmp #$0a cmp #MAX_FILES
+.e96e 90 04 bcc $e974 bcc _link
+.e970 a9 01 lda #$01 lda #TOO_MANY_FILES
+.e972 38 sec sec
+.e973 60 rts rts
+
+.e974 0a asl a _link asl a
+.e975 0a asl a asl a
+.e976 0a asl a asl a
+.e977 a8 tay tay
+.e978 60 rts rts
+
+.e979 error
+.e979 20 b0 e9 jsr $e9b0 jsr dump
+.e97c 48 pha pha
+.e97d 5a phy phy
+.e97e 48 pha pha
+.e97f a0 00 ldy #$00 ldy #0
+.e981 b9 9a e9 lda $e99a,y _loop lda _msg,y
+.e984 f0 06 beq $e98c beq _number
+.e986 20 b9 f9 jsr $f9b9 jsr platform.console.putc
+.e989 c8 iny iny
+.e98a 80 f5 bra $e981 bra _loop
+.e98c 68 pla _number pla
+.e98d 18 clc clc
+.e98e 69 30 adc #$30 adc #'0'
+.e990 20 b9 f9 jsr $f9b9 jsr platform.console.putc
+.e993 20 a5 e9 jsr $e9a5 jsr crlf
+.e996 7a ply ply
+.e997 68 pla pla
+.e998 38 sec sec
+.e999 60 rts rts
+>e99a 49 2f 4f 20 45 52 52 4f _msg .null "I/O ERROR "
+>e9a2 52 20 00
+
+.e9a5 crlf
+.e9a5 a9 0a lda #$0a lda #$0a
+.e9a7 20 b9 f9 jsr $f9b9 jsr platform.console.putc
+.e9aa a9 0d lda #$0d lda #$0d
+.e9ac 20 b9 f9 jsr $f9b9 jsr platform.console.putc
+.e9af 60 rts rts
+
+.e9b0 dump
+.e9b0 48 pha pha
+.e9b1 da phx phx
+.e9b2 5a phy phy
+.e9b3 a9 0d lda #$0d lda #$0d
+.e9b5 20 b9 f9 jsr $f9b9 jsr platform.console.putc
+.e9b8 a0 00 ldy #$00 ldy #0
+.e9ba b9 44 03 lda $0344,y _loop lda files,y
+.e9bd 20 d3 e9 jsr $e9d3 jsr _pb
+.e9c0 c8 iny iny
+.e9c1 98 tya tya
+.e9c2 29 07 and #$07 and #$07
+.e9c4 d0 f4 bne $e9ba bne _loop
+.e9c6 a9 0d lda #$0d lda #$0d
+.e9c8 20 b9 f9 jsr $f9b9 jsr platform.console.putc
+.e9cb c0 50 cpy #$50 cpy #80
+.e9cd d0 eb bne $e9ba bne _loop
+
+.e9cf 7a ply ply
+.e9d0 fa plx plx
+.e9d1 68 pla pla
+.e9d2 60 rts rts
+.e9d3 _pb
+.e9d3 48 pha pha
+.e9d4 4a lsr a lsr a
+.e9d5 4a lsr a lsr a
+.e9d6 4a lsr a lsr a
+.e9d7 4a lsr a lsr a
+.e9d8 20 e6 e9 jsr $e9e6 jsr _nibble
+.e9db 68 pla pla
+.e9dc 29 0f and #$0f and #$0f
+.e9de 20 e6 e9 jsr $e9e6 jsr _nibble
+.e9e1 a9 20 lda #$20 lda #' '
+.e9e3 4c b9 f9 jmp $f9b9 jmp platform.console.putc
+.e9e6 _nibble
+.e9e6 aa tax tax
+.e9e7 bd ed e9 lda $e9ed,x lda _hex,x
+.e9ea 4c b9 f9 jmp $f9b9 jmp platform.console.putc
+>e9ed 30 31 32 33 34 35 36 37 _hex .null "0123456789abcdef"
+>e9f5 38 39 61 62 63 64 65 66 00
+
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+.e9fe ioinit
+.e9fe 64 c0 stz $c0 stz scraping ; Not presently screen scraping.
+.ea00 64 c2 stz $c2 stz quoted ; Not presently in a quoted string
+
+ ; Init (zero) the files table
+.ea02 a2 00 ldx #$00 ldx #0
+.ea04 9e 44 03 stz $0344,x _loop stz files,x
+.ea07 e8 inx inx
+.ea08 e0 50 cpx #$50 cpx #8 * MAX_FILES
+.ea0a d0 f8 bne $ea04 bne _loop
+.ea0c 4c 0f ea jmp $ea0f jmp reopen
+
+.ea0f reopen
+ ; Open keyboard as stdin
+.ea0f a9 00 lda #$00 lda #0 ; stdin
+.ea11 a2 00 ldx #$00 ldx #0 ; keyboard
+.ea13 a0 00 ldy #$00 ldy #0 ; dummy sub-device
+.ea15 20 32 ea jsr $ea32 jsr setlfs
+.ea18 20 40 ea jsr $ea40 jsr open
+.ea1b a2 00 ldx #$00 ldx #0
+.ea1d 20 7e ea jsr $ea7e jsr chkin
+
+ ; Open screen as stdout
+.ea20 a9 01 lda #$01 lda #1 ; stdout
+.ea22 a2 03 ldx #$03 ldx #3 ; screen
+.ea24 a0 00 ldy #$00 ldy #0 ; dummy sub-device
+.ea26 20 32 ea jsr $ea32 jsr setlfs
+.ea29 20 40 ea jsr $ea40 jsr open
+.ea2c a2 01 ldx #$01 ldx #1
+.ea2e 20 88 ea jsr $ea88 jsr chkout
+
+.ea31 60 rts rts
+
+
+.ea32 setlfs
+.ea32 85 b0 sta $b0 sta cur_logical
+.ea34 86 b1 stx $b1 stx cur_device
+.ea36 84 b2 sty $b2 sty cur_addr
+.ea38 60 rts rts
+
+.ea39 setnam
+.ea39 85 af sta $af sta fname_len
+.ea3b 86 ad stx $ad stx fname+0
+.ea3d 84 ae sty $ae sty fname+1
+.ea3f 60 rts rts
+
+.ea40 open
+.ea40 a5 b0 lda $b0 lda cur_logical
+.ea42 20 6c e9 jsr $e96c jsr link
+.ea45 b0 07 bcs $ea4e bcs _error
+.ea47 b9 44 03 lda $0344,y lda file.state,y
+.ea4a f0 05 beq $ea51 beq _open
+.ea4c a9 02 lda #$02 lda #FILE_OPEN
+.ea4e 4c 79 e9 jmp $e979 _error jmp error
+
+.ea51 _open
+.ea51 a5 b1 lda $b1 lda cur_device
+.ea53 99 45 03 sta $0345,y sta file.device,y
+.ea56 a5 b2 lda $b2 lda cur_addr
+.ea58 99 46 03 sta $0346,y sta file.secondary,y
+
+.ea5b a5 b1 lda $b1 lda cur_device
+.ea5d 0a asl a asl a
+.ea5e 0a asl a asl a
+.ea5f 0a asl a asl a
+.ea60 aa tax tax
+
+.ea61 20 e5 eb jsr $ebe5 jsr open_x
+.ea64 b0 e8 bcs $ea4e bcs _error
+.ea66 60 rts rts
+
+
+.ea67 close
+.ea67 20 50 e9 jsr $e950 jsr find
+.ea6a 90 03 bcc $ea6f bcc _close
+.ea6c 4c 79 e9 jmp $e979 jmp error
+.ea6f 4c f0 eb jmp $ebf0 _close jmp close_x
+
+.ea72 chk
+ ; IN: X = logical file ID
+ ; Reports and returns an error if the file is invalid.
+.ea72 da phx phx
+.ea73 8a txa txa
+.ea74 20 50 e9 jsr $e950 jsr find
+.ea77 90 03 bcc $ea7c bcc _out
+.ea79 20 79 e9 jsr $e979 jsr error
+.ea7c fa plx _out plx
+.ea7d 60 rts rts
+
+.ea7e chkin
+.ea7e 5a phy phy
+.ea7f 20 72 ea jsr $ea72 jsr chk
+.ea82 b0 02 bcs $ea86 bcs _out
+.ea84 86 bc stx $bc stx cur_in
+.ea86 7a ply _out ply
+.ea87 60 rts rts
+.ea88 chkout
+.ea88 5a phy phy
+.ea89 20 72 ea jsr $ea72 jsr chk
+.ea8c b0 02 bcs $ea90 bcs _out
+.ea8e 86 bd stx $bd stx cur_out
+.ea90 7a ply _out ply
+.ea91 60 rts rts
+
+.ea92 clrchn ; TODO: not sure how this is /supposed/ to work.
+.ea92 18 clc clc
+.ea93 60 rts rts
+.ea94 5a phy phy
+
+.ea95 a5 bc lda $bc _stdin lda cur_in
+.ea97 20 50 e9 jsr $e950 jsr find
+.ea9a b0 03 bcs $ea9f bcs _stdout
+.ea9c 20 f0 eb jsr $ebf0 jsr close_x
+
+.ea9f a5 bd lda $bd _stdout lda cur_out
+.eaa1 20 50 e9 jsr $e950 jsr find
+.eaa4 b0 03 bcs $eaa9 bcs _reset
+.eaa6 20 f0 eb jsr $ebf0 jsr close_x
+
+ ; Reset stdin/stdout to 0/1
+.eaa9 a2 00 ldx #$00 _reset ldx #0
+.eaab 20 7e ea jsr $ea7e jsr chkin
+.eaae a2 01 ldx #$01 ldx #1
+.eab0 20 88 ea jsr $ea88 jsr chkout
+
+.eab3 18 clc clc
+.eab4 7a ply ply
+.eab5 60 rts rts
+
+.eab6 chrin
+.eab6 da phx phx
+.eab7 5a phy phy
+.eab8 a5 bc lda $bc lda cur_in
+.eaba 20 50 e9 jsr $e950 jsr find
+.eabd b0 0f bcs $eace bcs _error
+
+.eabf b9 45 03 lda $0345,y lda file.device,y
+.eac2 d0 05 bne $eac9 bne _read
+
+.eac4 20 d4 ea jsr $ead4 _screen jsr screen
+.eac7 80 08 bra $ead1 bra _out
+
+.eac9 20 e8 eb jsr $ebe8 _read jsr read_x
+.eacc 90 03 bcc $ead1 bcc _out
+.eace _error
+.eace 20 79 e9 jsr $e979 jsr error
+.ead1 _out
+.ead1 7a ply ply
+.ead2 fa plx plx
+.ead3 60 rts rts
+
+.ead4 screen
+ ; Resume an inprogress screen-scrape
+.ead4 a5 c0 lda $c0 lda scraping
+.ead6 d0 1e bne $eaf6 bne _next
+
+ ; Begin screen editing
+.ead8 a5 e3 lda $e3 lda platform.console.cur_x
+.eada 85 c1 sta $c1 sta scrape_x ; Start of input
+.eadc 85 c2 sta $c2 sta quoted ; Disables toupper if not at col zero
+
+.eade 20 d6 ec jsr $ecd6 _read jsr kernel.keyboard.deque
+.eae1 90 05 bcc $eae8 bcc _key
+.eae3 20 f8 e6 jsr $e6f8 jsr kernel.thread.yield
+.eae6 80 f6 bra $eade bra _read
+
+.eae8 c9 0d cmp #$0d _key cmp #13 ; ENTER
+.eaea f0 08 beq $eaf4 beq _scrape
+.eaec 20 38 eb jsr $eb38 jsr emacs
+.eaef 20 b9 f9 jsr $f9b9 jsr platform.console.putc
+.eaf2 80 ea bra $eade bra _read
+
+.eaf4 _scrape
+.eaf4 85 c0 sta $c0 sta scraping ; 13 (enter) is non-zero.
+.eaf6 _next
+.eaf6 a4 c1 ldy $c1 ldy scrape_x
+.eaf8 c4 e3 cpy $e3 cpy platform.console.cur_x
+.eafa d0 08 bne $eb04 bne _getchar
+.eafc 64 c0 stz $c0 stz scraping
+.eafe 64 c2 stz $c2 stz quoted
+.eb00 a9 0d lda #$0d lda #13
+.eb02 80 26 bra $eb2a bra _okay
+
+.eb04 _getchar
+.eb04 da phx phx
+.eb05 a6 01 ldx $01 ldx $1 ; TODO: move to console driver.
+.eb07 a9 02 lda #$02 lda #2
+.eb09 85 01 sta $01 sta $1
+.eb0b b1 e5 lda ($e5),y lda (platform.console.ptr),y
+.eb0d 29 7f and #$7f and #$7f
+.eb0f 86 01 stx $01 stx $1
+.eb11 fa plx plx
+
+.eb12 e6 c1 inc $c1 inc scrape_x
+.eb14 c9 20 cmp #$20 cmp #32
+.eb16 90 de bcc $eaf6 bcc _next ; can't generate these
+.eb18 c9 22 cmp #$22 cmp #$22 ; Quote
+.eb1a f0 10 beq $eb2c beq _quote
+.eb1c a6 c2 ldx $c2 ldx quoted
+.eb1e d0 0a bne $eb2a bne _okay
+.eb20 c9 7b cmp #$7b cmp #'z'+1
+.eb22 b0 06 bcs $eb2a bcs _okay
+.eb24 c9 61 cmp #$61 cmp #'a'
+.eb26 90 02 bcc $eb2a bcc _okay
+.eb28 49 20 eor #$20 eor #$20 ; toupper
+.eb2a _okay
+.eb2a 18 clc clc
+.eb2b 60 rts rts
+
+.eb2c _quote
+.eb2c a5 c2 lda $c2 lda quoted
+.eb2e 64 c2 stz $c2 stz quoted
+.eb30 d0 02 bne $eb34 bne _ret
+.eb32 e6 c2 inc $c2 inc quoted
+.eb34 a9 22 lda #$22 _ret lda #$22 ; quote
+.eb36 80 f2 bra $eb2a bra _okay
+
+ map .macro key, ctrl
+ .endm
+.eb38 emacs
+.eb38 da phx phx
+.eb39 a2 00 ldx #$00 ldx #0
+.eb3b dd 4d eb cmp $eb4d,x _loop cmp _map,x
+.eb3e f0 08 beq $eb48 beq _found
+.eb40 e8 inx inx
+.eb41 e8 inx inx
+.eb42 e0 0c cpx #$0c cpx #_end
+.eb44 d0 f5 bne $eb3b bne _loop
+.eb46 80 03 bra $eb4b bra _out
+.eb48 bd 4e eb lda $eb4e,x _found lda _map+1,x
+.eb4b fa plx _out plx
+.eb4c 60 rts rts
+.eb4d _map
+>eb4d 96 01 .byte HOME, ('a' & 31)
+>eb4f 97 05 .byte END, ('e' & 31)
+>eb51 99 10 .byte UP, ('p' & 31)
+>eb53 9a 0e .byte DOWN, ('n' & 31)
+>eb55 9b 02 .byte LEFT, ('b' & 31)
+>eb57 9c 06 .byte RIGHT, ('f' & 31)
+=12 _end = * - _map
+
+.eb59 getin
+.eb59 da phx phx
+.eb5a a5 bc lda $bc lda cur_in
+.eb5c 20 50 e9 jsr $e950 jsr find
+.eb5f b0 05 bcs $eb66 bcs _error
+.eb61 20 e8 eb jsr $ebe8 jsr read_x
+.eb64 90 03 bcc $eb69 bcc _done
+.eb66 20 79 e9 jsr $e979 _error jsr error
+.eb69 fa plx _done plx
+.eb6a 09 00 ora #$00 ora #0
+.eb6c 60 rts rts
+
+.eb6d chrout
+.eb6d da phx phx
+.eb6e 5a phy phy
+.eb6f 48 pha pha
+.eb70 a5 bd lda $bd lda cur_out
+.eb72 20 50 e9 jsr $e950 jsr find
+.eb75 90 06 bcc $eb7d bcc _okay
+.eb77 7a ply ply ; drop the character
+.eb78 20 79 e9 jsr $e979 jsr error
+.eb7b 80 06 bra $eb83 bra _done
+.eb7d 68 pla _okay pla
+.eb7e 48 pha pha
+.eb7f 20 ec eb jsr $ebec jsr write_x
+.eb82 68 pla pla
+.eb83 7a ply _done ply
+.eb84 fa plx plx
+.eb85 60 rts rts
+
+.eb86 clall
+.eb86 5a phy phy
+
+ ; Close all
+ ; Manually, to hide errors.
+.eb87 a9 00 lda #$00 lda #0
+.eb89 48 pha _loop pha
+.eb8a 20 50 e9 jsr $e950 jsr find
+.eb8d b0 03 bcs $eb92 bcs _next
+.eb8f 20 f0 eb jsr $ebf0 jsr close_x
+.eb92 68 pla _next pla
+.eb93 1a inc a inc a
+.eb94 c9 0a cmp #$0a cmp #MAX_FILES
+.eb96 d0 f1 bne $eb89 bne _loop
+
+ ; Reset stdin/stdout
+.eb98 20 0f ea jsr $ea0f jsr reopen
+.eb9b 7a ply ply
+.eb9c 60 rts rts
+
+
+
+ .send
+ .endn
+ .endn
+
+
+
+;****** Processing input file: core/dev.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ .cpu "w65c02"
+
+ .namespace kernel
+ io .namespace
+ .section kernel
+
+ mkdev .macro DEV
+ .endm
+
+.eb9d devices
+>eb9d 1b ec 21 ec 25 ec 1e ec .word keyboard_open, keyboard_stat, keyboard_io, keyboard_close
+>eba5 f3 eb f3 eb f3 eb f3 eb .word missing_open, missing_stat, missing_io, missing_close
+>ebad f3 eb f3 eb f3 eb f3 eb .word missing_open, missing_stat, missing_io, missing_close
+>ebb5 06 ec 0c ec 10 ec 09 ec .word screen_open, screen_stat, screen_io, screen_close
+>ebbd f3 eb f3 eb f3 eb f3 eb .word missing_open, missing_stat, missing_io, missing_close
+>ebc5 f3 eb f3 eb f3 eb f3 eb .word missing_open, missing_stat, missing_io, missing_close
+>ebcd f3 eb f3 eb f3 eb f3 eb .word missing_open, missing_stat, missing_io, missing_close
+>ebd5 f3 eb f3 eb f3 eb f3 eb .word missing_open, missing_stat, missing_io, missing_close
+>ebdd f3 eb f3 eb f3 eb f3 eb .word missing_open, missing_stat, missing_io, missing_close
+
+ device .namespace
+ .virtual devices
+>eb9d open .word ?
+>eb9f stat .word ?
+>eba1 io .word ?
+>eba3 close .word ?
+ .endv
+ .endn
+
+.ebe5 7c 9d eb jmp ($eb9d,x) open_x jmp (device.open,x)
+.ebe8 18 clc read_x clc
+.ebe9 7c a1 eb jmp ($eba1,x) jmp (device.io,x)
+.ebec 38 sec write_x sec
+.ebed 7c a1 eb jmp ($eba1,x) jmp (device.io,x)
+.ebf0 7c a3 eb jmp ($eba3,x) close_x jmp (device.close,x)
+
+.ebf3 missing_open
+.ebf3 missing_stat
+.ebf3 missing_io
+.ebf3 missing_close
+.ebf3 a9 05 lda #$05 lda #DEVICE_NOT_PRESENT
+.ebf5 4c 79 e9 jmp $e979 jmp error
+
+.ebf8 simple_open
+.ebf8 a9 01 lda #$01 lda #1 ; open
+.ebfa 99 44 03 sta $0344,y sta file.state,y
+.ebfd 18 clc clc
+.ebfe 60 rts rts
+
+.ebff simple_close
+.ebff a9 00 lda #$00 lda #0
+.ec01 99 44 03 sta $0344,y sta file.state,y
+.ec04 18 clc clc
+.ec05 60 rts rts
+
+
+ .send
+ .endn
+ .endn
+
+
+
+;****** Processing input file: core/dev_screen.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ .cpu "w65c02"
+
+ .namespace kernel
+ .namespace io
+
+ .section kernel
+
+
+
+.ec06 screen_open
+.ec06 4c f8 eb jmp $ebf8 jmp simple_open
+
+.ec09 screen_close
+.ec09 4c ff eb jmp $ebff jmp simple_close
+
+.ec0c screen_stat
+.ec0c a9 00 lda #$00 lda #0
+.ec0e 18 clc clc
+.ec0f 60 rts rts
+
+.ec10 screen_io
+.ec10 b0 04 bcs $ec16 bcs _write
+.ec12 a9 00 lda #$00 lda #0
+.ec14 18 clc clc
+.ec15 60 rts rts
+.ec16 20 b9 f9 jsr $f9b9 _write jsr platform.console.putc
+.ec19 18 clc clc
+.ec1a 60 rts rts
+
+
+ .send
+ .endn
+ .endn
+
+
+;****** Processing input file: core/dev_keyboard.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ .cpu "w65c02"
+
+ .namespace kernel
+ .namespace io
+
+ .section kernel
+
+.ec1b keyboard_open
+.ec1b 4c f8 eb jmp $ebf8 jmp simple_open
+
+.ec1e keyboard_close
+.ec1e 4c ff eb jmp $ebff jmp simple_close
+
+.ec21 keyboard_stat
+.ec21 a9 00 lda #$00 lda #0
+.ec23 18 clc clc
+.ec24 60 rts rts
+
+.ec25 keyboard_io
+.ec25 b0 09 bcs $ec30 bcs _write
+.ec27 20 d6 ec jsr $ecd6 _loop jsr keyboard.deque
+.ec2a 90 03 bcc $ec2f bcc _out
+.ec2c a9 00 lda #$00 lda #0
+.ec2e 18 clc clc
+.ec2f 60 rts _out rts
+.ec30 a9 07 lda #$07 _write lda #NOT_OUTPUT_FILE
+.ec32 4c 79 e9 jmp $e979 jmp error
+
+ .send
+ .endn
+ .endn
+
+
+
+;****** Processing input file: core/rtc.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ .cpu "65c02"
+
+ .namespace kernel
+ .section kernel
+
+.ec35 settim
+.ec35 rdtim
+.ec35 udtim
+.ec35 18 clc clc
+.ec36 60 rts rts
+
+ .send
+ .endn
+
+
+;****** Processing input file: core/sys.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ .cpu "w65c02"
+
+ .namespace kernel
+ .section kernel
+
+
+.ec37 ramtas
+
+.ec37 a2 02 ldx #$02 ldx #2
+.ec39 74 00 stz $00,x _11 stz $0,x
+.ec3b e8 inx inx
+.ec3c e0 a3 cpx #$a3 cpx #free_mem
+.ec5e 18 clc clc ; set
+.ec5f 20 7a ec jsr $ec7a jsr memtop
+
+.ec62 a2 00 ldx #$00 ldx #0
+.ec64 a0 a0 ldy #$a0 ldy #>basic
+.ec66 18 clc clc ; set
+.ec67 20 6e ec jsr $ec6e jsr membot
+
+.ec6a 60 rts rts
+
+
+.ec6b setmsg
+.ec6b 85 b7 sta $b7 sta msg_switch
+.ec6d 60 rts rts
+
+.ec6e membot
+.ec6e 90 05 bcc $ec75 bcc _save
+
+.ec70 a6 b5 ldx $b5 _load ldx mem_end+0
+.ec72 a4 b6 ldy $b6 ldy mem_end+1
+.ec74 60 rts rts
+
+.ec75 86 b5 stx $b5 _save stx mem_end+0
+.ec77 84 b6 sty $b6 sty mem_end+1
+.ec79 60 rts rts
+
+.ec7a memtop
+.ec7a 90 05 bcc $ec81 bcc _save
+
+.ec7c a6 b3 ldx $b3 _load ldx mem_start+0
+.ec7e a4 b4 ldy $b4 ldy mem_start+1
+.ec80 60 rts rts
+
+.ec81 86 b3 stx $b3 _save stx mem_start+0
+.ec83 84 b4 sty $b4 sty mem_start+1
+.ec85 60 rts rts
+
+
+
+.ec86 scnkey
+ ; PS2 keyboard is interrupt driven.
+ ; May be used to force a CIA scan.
+.ec86 60 rts rts
+
+.ec87 iobase
+.ec87 a2 dc ldx #$dc ldx #$dc
+.ec89 a0 00 ldy #$00 ldy #$00
+.ec8b 60 rts rts
+
+
+ .send
+ .endn
+
+
+
+
+
+;****** Processing input file: core/keyboard.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ .cpu "w65c02"
+
+ .namespace kernel
+ keyboard .namespace
+
+ .section kmem
+>0394 head .byte ?
+>0395 tail .byte ?
+>0396 ctrl_c .byte ?
+ .send
+
+=16 BUF_SIZE = 16
+
+ .section kbuf
+>0334 buf .fill BUF_SIZE
+ .send
+
+ .section kernel
+
+.ec8c init
+.ec8c 9c 94 03 stz $0394 stz head
+.ec8f 9c 95 03 stz $0395 stz tail
+.ec92 9c 96 03 stz $0396 stz ctrl_c
+.ec95 60 rts rts
+.ec96 stop
+ ; See if a ctrl_c has been queued.
+.ec96 ad 96 03 lda $0396 lda ctrl_c
+.ec99 d0 04 bne $ec9f bne _stop
+
+ ; No stop detected.
+.ec9b a9 ff lda #$ff lda #$ff
+.ec9d 18 clc clc
+.ec9e 60 rts rts
+
+.ec9f _stop
+ ; Nominally reset the I/O paths.
+.ec9f 20 cc ff jsr $ffcc jsr CLRCHN
+
+ ; Flush the keyboard queue.
+.eca2 78 sei sei
+.eca3 9c 94 03 stz $0394 stz head
+.eca6 9c 95 03 stz $0395 stz tail
+.eca9 58 cli cli
+
+ ; Clear the ctrl_c condition.
+.ecaa 9c 96 03 stz $0396 stz ctrl_c
+
+ ; Return 'stopped' status.
+.ecad a9 00 lda #$00 lda #0
+.ecaf 38 sec sec
+.ecb0 60 rts rts
+
+.ecb1 enque
+ ; A = character to enqueue.
+ ; Carry set if the queue is full.
+ ; Code is thread-safe to support multiple event sources.
+
+.ecb1 c9 03 cmp #$03 cmp #3 ; ctrl_c
+.ecb3 d0 03 bne $ecb8 bne _enque
+.ecb5 8d 96 03 sta $0396 sta ctrl_c
+.ecb8 _enque
+.ecb8 da phx phx
+.ecb9 38 sec sec ; Pre-emptively set carry
+.ecba 08 php php ; Carry is on the stack.
+.ecbb 78 sei sei
+.ecbc ae 94 03 ldx $0394 ldx head
+.ecbf 9d 34 03 sta $0334,x sta buf,x
+.ecc2 ca dex dex
+.ecc3 10 02 bpl $ecc7 bpl _ok
+.ecc5 a2 0f ldx #$0f ldx #BUF_SIZE-1
+.ecc7 ec 95 03 cpx $0395 _ok cpx tail
+.ecca f0 07 beq $ecd3 beq _out
+.eccc 8e 94 03 stx $0394 stx head
+.eccf ba tsx tsx
+.ecd0 de 01 01 dec $0101,x dec Stack+1,x ; Clear carry
+.ecd3 28 plp _out plp
+.ecd4 fa plx plx
+.ecd5 60 rts rts
+
+
+.ecd6 deque
+ ; A <- character, or carry set on empty.
+ ; Not thread safe, as the KERNAL calls are not thread safe.
+.ecd6 da phx phx
+.ecd7 ae 95 03 ldx $0395 ldx tail
+.ecda ec 94 03 cpx $0394 cpx head
+.ecdd 38 sec sec
+.ecde f0 0c beq $ecec beq _out
+.ece0 bd 34 03 lda $0334,x lda buf,x
+.ece3 ca dex dex
+.ece4 10 02 bpl $ece8 bpl _ok
+.ece6 a2 0f ldx #$0f ldx #BUF_SIZE-1
+.ece8 8e 95 03 stx $0395 _ok stx tail
+.eceb 18 clc clc
+.ecec fa plx _out plx
+.eced 60 rts rts
+
+ .send
+ .endn
+ .endn
+
+
+;****** Processing input file: core/video.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ .cpu "65c02"
+
+ .namespace kernel
+ .section kernel
+
+.ecee scinit
+.ecee 4c e4 f7 jmp $f7e4 jmp platform.console.init
+
+.ecf1 screen
+.ecf1 a2 50 ldx #$50 ldx #platform.console.COLS
+.ecf3 a0 3c ldy #$3c ldy #platform.console.ROWS
+.ecf5 60 rts rts
+
+.ecf6 plot
+.ecf6 b0 03 bcs $ecfb bcs _fetch
+.ecf8 20 49 f9 jsr $f949 jsr platform.console.gotoxy
+.ecfb a6 e3 ldx $e3 _fetch ldx platform.console.cur_x
+.ecfd a6 e4 ldx $e4 ldx platform.console.cur_y
+.ecff 60 rts rts
+
+ .send
+ .endn
+
+
+;****** Processing input file: core/device.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; This file is from the w6502c TinyCore kernel by the same author.
+
+ .cpu "w65c02"
+
+ .namespace kernel
+
+ dev .namespace
+ .virtual Devices
+
+ ; External functions
+>0400 data .word ? ; Data ready
+>0402 status .word ? ; Status change
+>0404 fetch .word ? ; Device requests data to send
+
+ ; Internal functions
+>0406 open .word ? ; Call to open device
+>0408 get .word ? ; Call to get device data
+>040a set .word ? ; Call to set device data
+>040c send .word ? ; Call to send data
+>040e close .word ? ; Call to close device
+.0410 size .endv
+ .endn
+
+ device .namespace
+
+ mkdev .macro PREFIX
+ .endm
+
+ .section kmem
+>0397 entries .byte ? ; List of free device entries
+ .send
+
+ .section kernel
+
+.ed00 7c 00 04 jmp ($0400,x) data jmp (kernel.dev.data,x)
+.ed03 7c 02 04 jmp ($0402,x) status jmp (kernel.dev.status,x)
+.ed06 7c 04 04 jmp ($0404,x) fetch jmp (kernel.dev.fetch,x)
+.ed09 7c 06 04 jmp ($0406,x) open jmp (kernel.dev.open,x)
+.ed0c 7c 08 04 jmp ($0408,x) get jmp (kernel.dev.get,x)
+.ed0f 7c 0a 04 jmp ($040a,x) set jmp (kernel.dev.set,x)
+.ed12 7c 0c 04 jmp ($040c,x) send jmp (kernel.dev.send,x)
+.ed15 7c 0e 04 jmp ($040e,x) close jmp (kernel.dev.close,x)
+
+.ed18 init
+.ed18 9c 97 03 stz $0397 stz entries
+.ed1b a9 00 lda #$00 lda #0
+.ed1d 80 04 bra $ed23 bra _next ; Reserve the first one.
+.ed1f aa tax _loop tax
+.ed20 20 3a ed jsr $ed3a jsr free
+.ed23 18 clc _next clc
+.ed24 69 10 adc #$10 adc #0500 head .byte ?
+>0501 tail .byte ?
+ .endv
+
+.ed5c init
+.ed5c 9e 00 05 stz $0500,x stz head,x
+.ed5f 9e 01 05 stz $0501,x stz tail,x
+.ed62 60 rts rts
+
+.ed63 enque
+ ; X = queue, Y = token
+
+.ed63 48 pha pha
+.ed64 08 php php
+.ed65 78 sei sei
+.ed66 bd 01 05 lda $0501,x lda tail,x
+.ed69 99 03 02 sta $0203,y sta kernel.token.entry.next,y
+.ed6c 98 tya tya
+.ed6d 9d 01 05 sta $0501,x sta tail,x
+.ed70 28 plp plp
+.ed71 68 pla pla
+.ed72 18 clc clc
+.ed73 60 rts rts
+
+.ed74 deque
+ ; OUT: Y = dequed token; carry set on empty
+
+.ed74 48 pha pha
+
+.ed75 bc 00 05 ldy $0500,x ldy head,x
+.ed78 d0 23 bne $ed9d bne _found
+
+.ed7a 38 sec sec
+.ed7b bc 01 05 ldy $0501,x ldy tail,x
+.ed7e f0 24 beq $eda4 beq _out
+
+ ; Safely take the tail (into y)
+.ed80 08 php php
+.ed81 78 sei sei
+.ed82 bc 01 05 ldy $0501,x ldy tail,x
+.ed85 9e 01 05 stz $0501,x stz tail,x
+.ed88 28 plp plp
+
+ ; Reverse into head
+.ed89 b9 03 02 lda $0203,y _loop lda kernel.token.entry.next,y ; next in A
+.ed8c 48 pha pha ; next on stack
+.ed8d bd 00 05 lda $0500,x lda head,x
+.ed90 99 03 02 sta $0203,y sta kernel.token.entry.next,y
+.ed93 98 tya tya
+.ed94 9d 00 05 sta $0500,x sta head,x
+.ed97 7a ply ply ; next in Y
+.ed98 d0 ef bne $ed89 bne _loop
+
+ ; "Find" the head (just where we left it)
+.ed9a bc 00 05 ldy $0500,x ldy head,x
+
+.ed9d _found
+.ed9d b9 03 02 lda $0203,y lda kernel.token.entry.next,y
+.eda0 9d 00 05 sta $0500,x sta head,x
+.eda3 18 clc clc
+
+.eda4 68 pla _out pla
+.eda5 60 rts rts
+
+ .endn
+ .send
+ .endn
+ .endn
+
+
+;****** Processing input file: core/token.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; This file is from the w6502c TinyCore kernel by the same author.
+
+ .cpu "w65c02"
+
+ .namespace kernel
+ token .namespace
+
+ entry .namespace
+ .virtual Tokens
+>0200 data .fill 3
+>0203 next .byte ?
+.0204 end .endv
+=4 size = end - Tokens
+ .endn
+
+ .section kmem
+>0398 entries .byte ? ; free list
+ .send
+
+ .section kernel
+
+.eda6 init
+.eda6 9c 98 03 stz $0398 stz entries
+.eda9 a9 90 lda #$90 lda #.
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ .cpu "65c02"
+
+ .namespace kernel
+ .section kernel
+
+
+
+
+.eddc ivec_start
+>eddc 2c ee .word irq
+>edde 2c ee .word break
+>ede0 2c ee .word nmi
+>ede2 40 ea .word io.open
+>ede4 67 ea .word io.close
+>ede6 7e ea .word io.chkin
+>ede8 88 ea .word io.chkout
+>edea 92 ea .word io.clrchn
+>edec b6 ea .word io.chrin
+>edee 6d eb .word io.chrout
+>edf0 96 ec .word keyboard.stop
+>edf2 59 eb .word io.getin
+>edf4 86 eb .word io.clall
+>edf6 2c ee .word user
+>edf8 5a e8 .word iec.load
+>edfa 7e e8 .word iec.save
+.edfc ivec_end
+=32 ivec_size = ivec_end - ivec_start
+
+
+
+.edfc restor
+.edfc 48 pha pha
+.edfd da phx phx
+.edfe a2 00 ldx #$00 ldx #0
+.ee00 bd dc ed lda $eddc,x _loop lda ivec_start,x
+.ee03 9d 14 03 sta $0314,x sta $314,x
+.ee06 e8 inx inx
+.ee07 c9 20 cmp #$20 cmp #ivec_size
+.ee09 d0 f5 bne $ee00 bne _loop
+.ee0b fa plx plx
+.ee0c 68 pla pla
+.ee0d 60 rts rts
+
+.ee0e vector
+.ee0e 86 a5 stx $a5 stx src+0
+.ee10 84 a6 sty $a6 sty src+1
+
+.ee12 a0 00 ldy #$00 ldy #0
+.ee14 b0 0b bcs $ee21 bcs _out
+
+.ee16 b1 a5 lda ($a5),y _in lda (src),y
+.ee18 99 14 03 sta $0314,y sta $314,y
+.ee1b c8 iny iny
+.ee1c c0 20 cpy #$20 cpy #ivec_size
+.ee1e d0 f6 bne $ee16 bne _in
+.ee20 60 rts rts
+
+.ee21 b9 14 03 lda $0314,y _out lda $314,y
+.ee24 91 a5 sta ($a5),y sta (src),y
+.ee26 c8 iny iny
+.ee27 c0 20 cpy #$20 cpy #ivec_size
+.ee29 d0 f6 bne $ee21 bne _out
+.ee2b 60 rts rts
+
+
+
+.ee2c irq
+.ee2c break
+.ee2c nmi
+.ee2c user
+.ee2c 38 sec sec
+.ee2d 60 rts rts
+
+
+ .send
+ .endn
+
+
+;****** Processing input file: core/cli.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; Simple command-line interface for use when nothing else is included.
+
+ .cpu "w65c02"
+
+ .namespace kernel
+ shell .namespace
+
+ .virtual Tokens ; $90 bytes here in page 2
+>0200 cmd .fill 80
+ .endv
+
+ .section dp
+>00c3 str_ptr .word ?
+>00c5 printing .word ?
+>00c7 device .byte ?
+ .send
+
+ .section shell
+
+.e000 strings:
+ str .namespace
+>e000 3f 0d 00 unknown .text "?", 13, 0
+>e003 0d 52 45 41 44 59 20 44 prompt .text 13,"READY DEVICE",0
+>e00b 45 56 49 43 45 00
+>e011 44 49 52 00 dir .null "DIR"
+>e015 53 54 41 54 00 stat .null "STAT"
+>e01a 52 44 53 00 rds .null "RDS"
+>e01e 43 4c 53 00 cls .null "CLS"
+>e022 4c 49 53 54 00 list .null "LIST"
+>e027 4c 4f 41 44 00 load .null "LOAD"
+>e02c 44 52 49 56 45 00 drive .null "DRIVE"
+>e032 52 55 4e 00 run .null "RUN"
+>e036 53 59 53 00 sys .null "SYS"
+>e03a 48 45 4c 50 00 help .null "HELP"
+>e03f 0d 54 79 70 65 20 27 68 intro .text 13,"Type 'help' for help.",13,0
+>e047 65 6c 70 27 20 66 6f 72 20 68 65 6c 70 2e 0d 00
+ .endn
+
+.e057 help_text
+>e057 0d 53 75 70 70 6f 72 74 .text 13,"Supported commands:",13
+>e05f 65 64 20 63 6f 6d 6d 61 6e 64 73 3a 0d
+>e06c 20 20 20 63 6c 73 20 20 .text " cls Clears the screen.",13
+>e074 20 20 20 20 20 20 20 43 6c 65 61 72 73 20 74 68
+>e084 65 20 73 63 72 65 65 6e 2e 0d
+>e08e 20 20 20 64 72 69 76 65 .text " drive # Changes the drive to #.",13
+>e096 20 23 20 20 20 20 20 43 68 61 6e 67 65 73 20 74
+>e0a6 68 65 20 64 72 69 76 65 20 74 6f 20 23 2e 0d
+>e0b5 20 20 20 64 69 72 20 20 .text " dir Displays the directory.",13
+>e0bd 20 20 20 20 20 20 20 44 69 73 70 6c 61 79 73 20
+>e0cd 74 68 65 20 64 69 72 65 63 74 6f 72 79 2e 0d
+>e0dc 20 20 20 6c 6f 61 64 22 .text " load",$22,"fname",$22," Loads the given file ',1'.", 13
+>e0e4 66 6e 61 6d 65 22 20 4c 6f 61 64 73 20 74 68 65
+>e0f4 20 67 69 76 65 6e 20 66 69 6c 65 20 27 2c 31 27
+>e104 2e 0d
+>e106 20 20 20 6c 69 73 74 20 .text " list LISTs directories and simple programs.",13
+>e10e 20 20 20 20 20 20 20 4c 49 53 54 73 20 64 69 72
+>e11e 65 63 74 6f 72 69 65 73 20 61 6e 64 20 73 69 6d
+>e12e 70 6c 65 20 70 72 6f 67 72 61 6d 73 2e 0d
+>e13c 20 20 20 72 75 6e 20 20 .text " run Runs loaded programs.",13
+>e144 20 20 20 20 20 20 20 52 75 6e 73 20 6c 6f 61 64
+>e154 65 64 20 70 72 6f 67 72 61 6d 73 2e 0d
+>e161 20 20 20 68 65 6c 70 20 .text " help Shows this help.",13
+>e169 20 20 20 20 20 20 20 53 68 6f 77 73 20 74 68 69
+>e179 73 20 68 65 6c 70 2e 0d
+>e181 0d 00 .text 13,0
+
+.e183 commands
+>e183 1e e0 35 e2 .word str.cls, cls
+>e187 11 e0 6b e2 .word str.dir, dir
+>e18b 22 e0 2e ee .word str.list, list
+>e18f 27 e0 0e ef .word str.load, load
+>e193 2c e0 4e e2 .word str.drive, drive
+>e197 32 e0 c1 f2 .word str.run, platform.far_exec
+>e19b 3a e0 a0 e1 .word str.help, help
+>e19f 00 .byte 0
+
+.e1a0 help
+.e1a0 a9 57 lda #$57 lda #help_text
+.e1a6 85 a6 sta $a6 sta src+1
+.e1a8 4c 16 e7 jmp $e716 jmp kernel.puts
+
+.e1ab start
+.e1ab 20 0b e7 jsr $e70b jsr banner
+
+.e1ae a9 e0 lda #$e0 lda #>strings
+.e1b0 85 c4 sta $c4 sta str_ptr+1
+
+.e1b2 a0 3f ldy #$3f ldy #_fname
+.e271 a9 01 lda #$01 lda #1
+.e273 20 bd ff jsr $ffbd jsr SETNAM
+
+ ; Request operation on 0,device,0
+.e276 a9 00 lda #$00 lda #0 ; Logical device # ... not meaningful here.
+.e278 a6 c7 ldx $c7 ldx device
+.e27a a0 00 ldy #$00 ldy #0 ; No sub-device / "command" -> use $0801
+.e27c 20 ba ff jsr $ffba jsr SETLFS
+
+ ; Load the data
+.e27f a9 00 lda #$00 lda #0 ; load, not verify
+.e281 a2 01 ldx #$01 ldx #<$801
+.e283 a0 08 ldy #$08 ldy #>$801
+.e285 20 d5 ff jsr $ffd5 jsr LOAD
+.e288 b0 0d bcs $e297 bcs _out
+.e28a 20 b7 ff jsr $ffb7 jsr READST
+.e28d f0 05 beq $e294 beq _list
+.e28f a9 05 lda #$05 lda #DEVICE_NOT_PRESENT
+.e291 38 sec sec
+.e292 80 03 bra $e297 bra _out
+
+.e294 _list
+ ; Show the data
+.e294 20 2e ee jsr $ee2e jsr list
+.e297 _out
+.e297 7a ply ply
+.e298 fa plx plx
+.e299 60 rts rts
+>e29a 24 _fname .text "$"
+
+
+
+.e29b putc
+ ; IN: A = character code
+.e29b 4c b9 f9 jmp $f9b9 jmp platform.console.putc
+
+.e29e puts
+ ; IN: Y=LSB of a string in 'strings' above.
+.e29e 84 c3 sty $c3 sty str_ptr
+.e2a0 a0 00 ldy #$00 ldy #0
+.e2a2 b1 c3 lda ($c3),y _loop lda (str_ptr),y
+.e2a4 f0 06 beq $e2ac beq _out
+.e2a6 20 9b e2 jsr $e29b jsr putc
+.e2a9 c8 iny iny
+.e2aa 80 f6 bra $e2a2 bra _loop
+.e2ac _out
+.e2ac 18 clc clc
+.e2ad 60 rts rts
+
+
+ .send
+ .endn
+ .endn
+
+
+ .if false
+ .endif
+
+
+
+;****** Processing input file: core/cli_list.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; Simple command-line interface for use when nothing else is included.
+
+ .cpu "w65c02"
+
+ .namespace kernel
+ .namespace shell
+
+ .section kernel
+.ee2e list
+.ee2e 20 d7 e1 jsr $e1d7 jsr cr
+
+.ee31 da phx phx
+.ee32 5a phy phy
+
+.ee33 a2 00 ldx #$00 ldx #0 ; count MSB
+.ee35 a0 00 ldy #$00 ldy #0 ; count LSB
+
+.ee37 a9 01 lda #$01 lda #<$801
+.ee39 85 a5 sta $a5 sta src+0
+.ee3b a9 08 lda #$08 lda #>$801
+.ee3d 85 a6 sta $a6 sta src+1
+
+.ee3f _line
+ ; File ends when the would-be next address is zero.
+.ee3f 20 7e ee jsr $ee7e jsr _fetch
+.ee42 85 ab sta $ab sta tos_l ; tmp
+.ee44 20 7e ee jsr $ee7e jsr _fetch
+.ee47 05 ab ora $ab ora tos_l
+.ee49 f0 3d beq $ee88 beq _done
+
+ ; Fetch the line number
+.ee4b 20 7e ee jsr $ee7e jsr _fetch
+.ee4e 85 ab sta $ab sta tos_l
+.ee50 20 7e ee jsr $ee7e jsr _fetch
+.ee53 85 ac sta $ac sta tos_h
+
+ ; Print the line number
+.ee55 20 8c ee jsr $ee8c jsr print_number
+
+ ; Print the rest of the line
+.ee58 _loop
+.ee58 20 7e ee jsr $ee7e jsr _fetch
+.ee5b f0 1c beq $ee79 beq _eol
+.ee5d 30 06 bmi $ee65 bmi _ext
+.ee5f c9 20 cmp #$20 cmp #32
+.ee61 b0 11 bcs $ee74 bcs _putc
+.ee63 80 0d bra $ee72 bra _fill
+.ee65 c9 9e cmp #$9e _ext cmp #$9e
+.ee67 f0 02 beq $ee6b beq _sys
+.ee69 80 07 bra $ee72 bra _fill
+.ee6b _sys
+.ee6b 5a phy phy
+.ee6c a0 36 ldy #$36 ldy #eee5 10 27 e8 03 64 00 0a 00 .word 10000, 1000, 100, 10, 1
+>eeed 01 00
+
+ .send
+ .endn
+ .endn
+
+
+
+;****** Processing input file: core/cli_load.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; Simple command-line interface for use when nothing else is included.
+
+ .cpu "w65c02"
+
+ .namespace kernel
+ .namespace shell
+
+ .section dp
+>00c8 far_addr .fill 4
+>00cc far_dest .fill 4
+>00d0 far_count .fill 4
+>00d4 addr_len .byte ?
+ .send
+
+ .section kernel
+
+.eeef extensions
+>eeef 50 52 47 8f ef .text "PRG", load_prg
+>eef4 70 72 67 8f ef .text "prg", load_prg
+>eef9 50 47 58 4c f0 .text "PGX", load_pgx
+>eefe 70 67 78 4c f0 .text "pgx", load_pgx
+>ef03 50 47 5a d2 f0 .text "PGZ", load_pgz
+>ef08 70 67 7a d2 f0 .text "pgz", load_pgz
+>ef0d 00 .byte 0
+
+.ef0e load
+.ef0e 64 c8 stz $c8 stz far_addr+0
+.ef10 64 c9 stz $c9 stz far_addr+1
+
+.ef12 20 3c e2 jsr $e23c jsr find_arg
+.ef15 90 01 bcc $ef18 bcc _quote
+.ef17 60 rts rts
+
+.ef18 _quote
+.ef18 20 37 ef jsr $ef37 jsr set_fname
+.ef1b b0 19 bcs $ef36 bcs _out
+
+.ef1d a5 af lda $af lda fname_len
+.ef1f c9 05 cmp #$05 cmp #5 ; has an extension?
+.ef21 90 10 bcc $ef33 bcc _prg ; No, default to prg.
+
+ ; Y = start of extension (starting with the '.').
+.ef23 a5 af lda $af lda fname_len
+.ef25 38 sec sec
+.ef26 e9 04 sbc #$04 sbc #4 ; should be start of ext
+.ef28 a8 tay tay
+
+ ; Does it start with a '.'?
+.ef29 b1 ad lda ($ad),y lda (fname),y
+.ef2b c9 2e cmp #$2e cmp #'.'
+.ef2d d0 04 bne $ef33 bne _prg ; No, default to prg.
+
+.ef2f c8 iny iny
+.ef30 4c 5a ef jmp $ef5a jmp load_by_extension
+.ef33 4c 8f ef jmp $ef8f _prg jmp load_prg
+.ef36 60 rts _out rts
+
+
+.ef37 set_fname
+.ef37 c8 iny iny
+.ef38 5a phy phy
+.ef39 b9 00 02 lda $0200,y _loop lda cmd,y
+.ef3c c9 22 cmp #$22 cmp #$22 ; Quote
+.ef3e f0 0a beq $ef4a beq _setnam
+.ef40 c9 0d cmp #$0d cmp #13
+.ef42 f0 03 beq $ef47 beq _error
+.ef44 c8 iny iny
+.ef45 d0 f2 bne $ef39 bne _loop
+.ef47 _error
+.ef47 38 sec sec
+.ef48 _done
+.ef48 7a ply ply
+.ef49 60 rts rts
+
+.ef4a _setnam
+ ; A = length of string
+.ef4a 98 tya tya
+.ef4b ba tsx tsx
+.ef4c 38 sec sec
+.ef4d fd 01 01 sbc $0101,x sbc $101,x ; Start of string (on stack)
+.ef50 f0 f5 beq $ef47 beq _error
+
+ ; X = LSB of string
+.ef52 fa plx plx
+
+ ; Y = MSB of string
+.ef53 a0 02 ldy #$02 ldy #>cmd
+
+.ef55 20 bd ff jsr $ffbd jsr SETNAM
+.ef58 18 clc clc
+.ef59 60 rts rts
+
+.ef5a load_by_extension
+
+.ef5a a2 00 ldx #$00 ldx #0
+.ef5c bd ef ee lda $eeef,x _loop lda extensions,x
+.ef5f f0 0e beq $ef6f beq _failed
+.ef61 20 73 ef jsr $ef73 jsr _cmp
+.ef64 90 06 bcc $ef6c bcc _found
+.ef66 8a txa txa
+.ef67 69 04 adc #$04 adc #4 ; 5 with the carry
+.ef69 aa tax tax
+.ef6a 80 f0 bra $ef5c bra _loop
+.ef6c 7c f2 ee jmp ($eef2,x) _found jmp (extensions+3,x)
+.ef6f _failed
+.ef6f a9 04 lda #$04 lda #4 ; TODO
+.ef71 38 sec sec
+.ef72 60 rts rts
+.ef73 _cmp
+.ef73 5a phy phy
+.ef74 38 sec sec
+
+.ef75 b1 ad lda ($ad),y lda (fname),y
+.ef77 5d ef ee eor $eeef,x eor extensions+0,x
+.ef7a d0 11 bne $ef8d bne _out
+
+.ef7c c8 iny iny
+.ef7d b1 ad lda ($ad),y lda (fname),y
+.ef7f 5d f0 ee eor $eef0,x eor extensions+1,x
+.ef82 d0 09 bne $ef8d bne _out
+
+.ef84 c8 iny iny
+.ef85 b1 ad lda ($ad),y lda (fname),y
+.ef87 5d f1 ee eor $eef1,x eor extensions+2,x
+.ef8a d0 01 bne $ef8d bne _out
+
+.ef8c 18 clc clc
+.ef8d _out
+.ef8d 7a ply ply
+.ef8e 60 rts rts
+
+
+.ef8f load_prg
+
+ ; Set Y=0 for fname == "$", Y=1 (binary) otherwise.
+.ef8f a0 01 ldy #$01 ldy #1
+.ef91 a5 af lda $af lda fname_len
+.ef93 c9 01 cmp #$01 cmp #1
+.ef95 d0 09 bne $efa0 bne _setlfs
+.ef97 b2 ad lda ($ad) lda (fname)
+.ef99 c9 24 cmp #$24 cmp #'$'
+.ef9b d0 03 bne $efa0 bne _setlfs
+.ef9d 4c 6b e2 jmp $e26b jmp dir
+
+.efa0 _setlfs
+.efa0 a9 00 lda #$00 lda #0 ; Logical device # ... not meaningful here.
+.efa2 a6 c7 ldx $c7 ldx device
+.efa4 a0 01 ldy #$01 ldy #1 ; Use native load address
+.efa6 20 ba ff jsr $ffba jsr SETLFS
+
+ ; Load the data
+.efa9 a9 00 lda #$00 lda #0 ; load, not verify
+.efab 20 d5 ff jsr $ffd5 jsr LOAD
+.efae b0 03 bcs $efb3 bcs _out
+
+.efb0 4c b4 ef jmp $efb4 jmp find_sys
+
+.efb3 60 rts _out rts
+
+.efb4 find_sys
+
+ ; Copy the embedded load address to far_addr
+.efb4 a5 a5 lda $a5 lda src+0
+.efb6 85 c8 sta $c8 sta far_addr+0
+.efb8 a5 a6 lda $a6 lda src+1
+.efba 85 c9 sta $c9 sta far_addr+1
+
+ ; If the embedded address is not $801, it is the start addr.
+.efbc a5 a5 lda $a5 lda src+0
+.efbe c9 01 cmp #$01 cmp #<$801
+.efc0 d0 49 bne $f00b bne _done
+
+.efc2 a5 a6 lda $a6 lda src+1
+.efc4 c9 08 cmp #$08 cmp #>$801
+.efc6 d0 43 bne $f00b bne _done
+
+ ; BASIC header; get far_addr from a SYS call.
+.efc8 64 c8 stz $c8 stz far_addr+0
+.efca 64 c9 stz $c9 stz far_addr+1
+
+ ; BASIC parser
+.efcc a0 00 ldy #$00 ldy #0
+.efce _line
+ ; File ends when the would-be next address is zero.
+.efce 20 01 f0 jsr $f001 jsr _fetch
+.efd1 85 ab sta $ab sta tos_l ; tmp
+.efd3 20 01 f0 jsr $f001 jsr _fetch
+.efd6 05 ab ora $ab ora tos_l
+.efd8 f0 31 beq $f00b beq _done
+
+ ; Skip the line number
+.efda 20 01 f0 jsr $f001 jsr _fetch
+.efdd 20 01 f0 jsr $f001 jsr _fetch
+
+ ; Scan the rest of the line
+.efe0 _loop
+.efe0 20 01 f0 jsr $f001 jsr _fetch
+.efe3 f0 e9 beq $efce beq _line
+.efe5 10 f9 bpl $efe0 bpl _loop
+.efe7 c9 9e cmp #$9e cmp #$9e ; SYS
+.efe9 d0 f5 bne $efe0 bne _loop
+
+ ; SYS token found; skip any following spaces.
+.efeb 20 01 f0 jsr $f001 _spaces jsr _fetch
+ ;beq _done
+ ;cmp #' '
+ ;beq _spaces
+
+ ; atoi the following digits.
+.efee c9 30 cmp #$30 _digits cmp #'0'
+.eff0 90 19 bcc $f00b bcc _done
+.eff2 c9 3a cmp #$3a cmp #'9'+1
+.eff4 b0 15 bcs $f00b bcs _done
+.eff6 38 sec sec
+.eff7 e9 30 sbc #$30 sbc #'0'
+.eff9 20 0d f0 jsr $f00d jsr mul_add
+.effc 20 01 f0 jsr $f001 jsr _fetch
+.efff 80 ed bra $efee bra _digits
+
+.f001 _fetch
+.f001 b1 a5 lda ($a5),y lda (src),y
+.f003 c8 iny iny
+.f004 d0 02 bne $f008 bne _fetched
+.f006 e6 a6 inc $a6 inc src+1
+.f008 _fetched
+.f008 09 00 ora #$00 ora #0
+.f00a 60 rts rts
+
+.f00b _done
+.f00b 18 clc clc
+.f00c 60 rts rts
+
+.f00d mul_add
+ ; Multiply far_addr by 10.
+.f00d 48 pha pha
+.f00e 20 2a f0 jsr $f02a jsr _copy
+.f011 20 33 f0 jsr $f033 jsr _x2
+.f014 20 33 f0 jsr $f033 jsr _x2
+.f017 20 3e f0 jsr $f03e jsr _add
+.f01a 20 33 f0 jsr $f033 jsr _x2
+.f01d 68 pla pla
+
+ ; Add the decimal digit in A.
+.f01e 85 ca sta $ca sta far_addr+2
+.f020 64 cb stz $cb stz far_addr+3
+.f022 20 3e f0 jsr $f03e jsr _add
+
+ ; Zero the upper bits
+.f025 64 ca stz $ca stz far_addr+2
+.f027 64 cb stz $cb stz far_addr+3
+.f029 60 rts rts
+
+.f02a _copy
+.f02a a5 c8 lda $c8 lda far_addr+0
+.f02c 85 ca sta $ca sta far_addr+2
+.f02e a5 c9 lda $c9 lda far_addr+1
+.f030 85 cb sta $cb sta far_addr+3
+.f032 60 rts rts
+.f033 _x2
+.f033 a5 c8 lda $c8 lda far_addr+0
+.f035 0a asl a asl a
+.f036 85 c8 sta $c8 sta far_addr+0
+.f038 a5 c9 lda $c9 lda far_addr+1
+.f03a 2a rol a rol a
+.f03b 85 c9 sta $c9 sta far_addr+1
+.f03d 60 rts rts
+.f03e _add
+.f03e 18 clc clc
+.f03f a5 c8 lda $c8 lda far_addr+0
+.f041 65 ca adc $ca adc far_addr+2
+.f043 85 c8 sta $c8 sta far_addr+0
+.f045 a5 c9 lda $c9 lda far_addr+1
+.f047 65 cb adc $cb adc far_addr+3
+.f049 85 c9 sta $c9 sta far_addr+1
+.f04b 60 rts rts
+
+
+.f04c load_pgx
+
+ ; IN: File name set using SETNAM
+ ;
+ ; OUT: X/Y = end address, or carry set and A = iec error.
+
+.f04c a9 00 lda #$00 lda #0 ; Logical device # ... not meaningful here.
+.f04e a6 c7 ldx $c7 ldx device
+.f050 a0 00 ldy #$00 ldy #0 ; Not used
+.f052 20 ba ff jsr $ffba jsr SETLFS
+
+ ; Reset the iec queue and status
+.f055 20 f5 e7 jsr $e7f5 jsr kernel.iec.reset
+
+ ; Open the file for read.
+ ; NOTE: returns a KERNAL error; must check READST as well!
+.f058 20 80 e8 jsr $e880 jsr kernel.iec.open_file_for_read
+.f05b b0 19 bcs $f076 bcs _out
+.f05d 20 b7 ff jsr $ffb7 jsr READST
+.f060 09 00 ora #$00 ora #0
+.f062 d0 13 bne $f077 bne _error
+
+ ; Read the file, sets X/Y to last address.
+.f064 20 7d f0 jsr $f07d jsr read_pgx_data
+.f067 90 08 bcc $f071 bcc _close
+
+ ; Try to close the file while preserving the original error.
+.f069 48 pha pha
+.f06a 20 d2 e8 jsr $e8d2 jsr kernel.iec.close_file
+.f06d 68 pla pla
+.f06e 38 sec sec
+.f06f 80 06 bra $f077 bra _error
+.f071 _close
+.f071 20 d2 e8 jsr $e8d2 jsr kernel.iec.close_file
+.f074 b0 01 bcs $f077 bcs _error
+
+.f076 60 rts _out rts
+.f077 _error
+.f077 20 28 e7 jsr $e728 jsr error
+.f07a 18 clc clc
+.f07b 80 f9 bra $f076 bra _out
+
+
+.f07d read_pgx_data
+
+ ; Internal funciton.
+ ; Implements load-body for .pgx files.
+ ;
+ ; IN: SETNAM and SETLFS have been called.
+ ;
+ ; Out: X:Y = end address, far_addr = exec address
+ ; On error, Carry set, and A = IEC error (READST value)
+
+ ; Make sure it's a PGX file.
+.f07d a2 00 ldx #$00 ldx #0
+.f07f 20 a5 ff jsr $ffa5 _signature jsr IECIN
+.f082 b0 23 bcs $f0a7 bcs _error
+.f084 dd ba f0 cmp $f0ba,x cmp _ident,x
+.f087 d0 35 bne $f0be bne _mismatch
+.f089 e8 inx inx
+.f08a e0 04 cpx #$04 cpx #4
+.f08c d0 f1 bne $f07f bne _signature
+
+ ; Read the dest and exec addresses.
+.f08e a2 00 ldx #$00 ldx #0
+.f090 20 a5 ff jsr $ffa5 _addr jsr IECIN
+.f093 b0 12 bcs $f0a7 bcs _error
+.f095 95 cc sta $cc,x sta far_dest,x
+.f097 95 c8 sta $c8,x sta far_addr,x
+.f099 e8 inx inx
+.f09a e0 04 cpx #$04 cpx #4
+.f09c d0 f2 bne $f090 bne _addr
+
+.f09e _loop
+.f09e 20 a5 ff jsr $ffa5 jsr IECIN
+.f0a1 90 07 bcc $f0aa bcc _found
+.f0a3 49 40 eor #$40 eor #kernel.iec.EOI
+.f0a5 f0 0d beq $f0b4 beq _done
+.f0a7 4c 28 e7 jmp $e728 _error jmp error ; Forward the IEC error status.
+
+.f0aa _found
+.f0aa 20 6f f2 jsr $f26f jsr platform.far_store
+.f0ad a2 cc ldx #$cc ldx #far_dest
+.f0af 20 c3 f0 jsr $f0c3 jsr far_inc
+
+.f0b2 90 ea bcc $f09e bcc _loop
+.f0b4 18 clc _done clc
+.f0b5 _out
+.f0b5 a6 cc ldx $cc ldx far_dest+0
+.f0b7 a4 cd ldy $cd ldy far_dest+1
+.f0b9 60 rts rts
+
+>f0ba 50 47 58 00 _ident .text "PGX",0 ; 6502 family
+.f0be a9 10 lda #$10 _mismatch lda #kernel.iec.MISMATCH
+.f0c0 38 sec sec
+.f0c1 80 e4 bra $f0a7 bra _error
+
+
+.f0c3 far_inc
+ ; IN: X->32 bit long in DP
+.f0c3 f6 00 inc $00,x inc 0,x
+.f0c5 d0 0a bne $f0d1 bne _done
+.f0c7 f6 01 inc $01,x inc 1,x
+.f0c9 d0 06 bne $f0d1 bne _done
+.f0cb f6 02 inc $02,x inc 2,x
+.f0cd d0 02 bne $f0d1 bne _done
+.f0cf f6 03 inc $03,x inc 3,x
+.f0d1 60 rts _done rts
+
+
+.f0d2 load_pgz ; TODO: share this code with pgx
+
+ ; IN: File name set using SETNAM
+ ;
+ ; OUT: X/Y = end address, or carry set and A = iec error.
+
+.f0d2 a9 00 lda #$00 lda #0 ; Logical device # ... not meaningful here.
+.f0d4 a6 c7 ldx $c7 ldx device
+.f0d6 a0 00 ldy #$00 ldy #0 ; Not used
+.f0d8 20 ba ff jsr $ffba jsr SETLFS
+
+ ; Reset the iec queue and status
+.f0db 20 f5 e7 jsr $e7f5 jsr kernel.iec.reset
+
+ ; Open the file for read.
+ ; NOTE: returns a KERNAL error; must check READST as well!
+.f0de 20 80 e8 jsr $e880 jsr kernel.iec.open_file_for_read
+.f0e1 b0 19 bcs $f0fc bcs _out
+.f0e3 20 b7 ff jsr $ffb7 jsr READST
+.f0e6 09 00 ora #$00 ora #0
+.f0e8 d0 13 bne $f0fd bne _error
+
+ ; Read the file, sets X/Y to last address.
+.f0ea 20 03 f1 jsr $f103 jsr read_pgz_data
+.f0ed 90 08 bcc $f0f7 bcc _close
+
+ ; Try to close the file while preserving the original error.
+.f0ef 48 pha pha
+.f0f0 20 d2 e8 jsr $e8d2 jsr kernel.iec.close_file
+.f0f3 68 pla pla
+.f0f4 38 sec sec
+.f0f5 80 06 bra $f0fd bra _error
+.f0f7 _close
+.f0f7 20 d2 e8 jsr $e8d2 jsr kernel.iec.close_file
+.f0fa b0 01 bcs $f0fd bcs _error
+
+.f0fc 60 rts _out rts
+.f0fd _error
+.f0fd 20 28 e7 jsr $e728 jsr error
+.f100 18 clc clc
+.f101 80 f9 bra $f0fc bra _out
+
+
+.f103 read_pgz_data
+
+.f103 a9 02 lda #$02 lda #2
+.f105 85 01 sta $01 sta $1
+
+ ; Internal funciton.
+ ; Implements load-body for .pgz files.
+ ;
+ ; IN: SETNAM and SETLFS have been called.
+ ;
+ ; Out: X:Y = end address, far_addr = exec address
+ ; On error, Carry set, and A = IEC error (READST value)
+
+.f107 20 a5 ff jsr $ffa5 jsr IECIN
+.f10a b0 65 bcs $f171 bcs _error
+.f10c c9 5a cmp #$5a cmp #'Z'
+.f10e f0 0a beq $f11a beq _pgz24
+.f110 c9 7a cmp #$7a cmp #'z'
+.f112 f0 0c beq $f120 beq _pgz32
+.f114 a9 10 lda #$10 _mismatch lda #kernel.iec.MISMATCH
+.f116 38 sec sec
+.f117 4c 71 f1 jmp $f171 jmp _error
+
+.f11a a9 03 lda #$03 _pgz24 lda #3
+.f11c 85 d4 sta $d4 sta addr_len
+.f11e 80 06 bra $f126 bra _read
+
+.f120 a9 04 lda #$04 _pgz32 lda #4
+.f122 85 d4 sta $d4 sta addr_len
+.f124 80 00 bra $f126 bra _read
+
+.f126 _read
+.f126 a2 00 ldx #$00 ldx #0
+.f128 _zero
+.f128 74 c8 stz $c8,x stz far_addr,x
+.f12a 74 cc stz $cc,x stz far_dest,x
+.f12c 74 d0 stz $d0,x stz far_count,x
+.f12e e8 inx inx
+.f12f e0 04 cpx #$04 cpx #4
+.f131 d0 f5 bne $f128 bne _zero
+
+.f133 _block
+ ; Read the dest address.
+.f133 a2 00 ldx #$00 ldx #0
+.f135 20 a5 ff jsr $ffa5 jsr IECIN
+.f138 b0 4f bcs $f189 bcs _end
+.f13a 80 05 bra $f141 bra _next
+.f13c 20 a5 ff jsr $ffa5 _dest jsr IECIN
+.f13f b0 30 bcs $f171 bcs _error
+.f141 95 cc sta $cc,x _next sta far_dest,x
+.f143 e8 inx inx
+.f144 e4 d4 cpx $d4 cpx addr_len
+.f146 d0 f4 bne $f13c bne _dest
+
+ ; Read the byte count into far_count
+.f148 a2 00 ldx #$00 ldx #0
+.f14a 20 a5 ff jsr $ffa5 _count jsr IECIN
+.f14d b0 22 bcs $f171 bcs _error
+.f14f 95 d0 sta $d0,x sta far_count,x
+.f151 e8 inx inx
+.f152 e4 d4 cpx $d4 cpx addr_len
+.f154 d0 f4 bne $f14a bne _count
+
+ ; See if this is an empty block (start addr)
+.f156 20 a1 f1 jsr $f1a1 jsr test_far_count
+.f159 d0 0d bne $f168 bne _loop ; Nope, read in the data
+
+ ; Empty block implies the block address is the start address
+.f15b a2 00 ldx #$00 ldx #0
+.f15d b5 cc lda $cc,x _copy lda far_dest,x
+.f15f 95 c8 sta $c8,x sta far_addr,x
+.f161 e8 inx inx
+.f162 e4 d4 cpx $d4 cpx addr_len
+.f164 d0 f7 bne $f15d bne _copy
+
+.f166 80 cb bra $f133 bra _block
+
+.f168 _loop
+.f168 20 a5 ff jsr $ffa5 jsr IECIN
+.f16b 90 07 bcc $f174 bcc _found
+.f16d 49 40 eor #$40 eor #kernel.iec.EOI
+.f16f f0 a3 beq $f114 beq _mismatch
+.f171 4c 28 e7 jmp $e728 _error jmp error ; Forward the IEC error status.
+
+.f174 _found
+ ;ldx far_dest
+ ;sta $c000,x
+.f174 8d 4f c0 sta $c04f sta $c000+79
+.f177 20 6f f2 jsr $f26f jsr platform.far_store
+.f17a a2 cc ldx #$cc ldx #far_dest
+.f17c 20 c3 f0 jsr $f0c3 jsr far_inc
+.f17f 20 8f f1 jsr $f18f jsr dec_far_count
+.f182 20 a1 f1 jsr $f1a1 jsr test_far_count
+.f185 d0 e1 bne $f168 bne _loop
+.f187 80 aa bra $f133 bra _block
+
+.f189 _end
+.f189 49 40 eor #$40 eor #kernel.iec.EOI
+.f18b d0 e4 bne $f171 bne _error
+.f18d 18 clc clc
+.f18e 60 rts rts
+
+
+.f18f dec_far_count
+.f18f 18 clc clc ; Subtracting one.
+.f190 a2 00 ldx #$00 ldx #0
+.f192 b5 d0 lda $d0,x _loop lda far_count,x
+.f194 e9 00 sbc #$00 sbc #0
+.f196 95 d0 sta $d0,x sta far_count,x
+.f198 b0 05 bcs $f19f bcs _done
+.f19a e8 inx inx
+.f19b e0 04 cpx #$04 cpx #4
+.f19d d0 f3 bne $f192 bne _loop
+.f19f 18 clc _done clc
+.f1a0 60 rts rts
+
+.f1a1 test_far_count
+.f1a1 a5 d0 lda $d0 lda far_count+0
+.f1a3 05 d1 ora $d1 ora far_count+1
+.f1a5 05 d2 ora $d2 ora far_count+2
+.f1a7 05 d3 ora $d3 ora far_count+3
+.f1a9 60 rts rts
+
+ .send
+ .endn
+ .endn
+
+
+
+;****** Processing input file: platform/jr/jr.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; Startup for OpenKERNAL on the C256 Foenix Jr.
+
+ .cpu "w65c02"
+
+ * = $fff6 ; Keep the Jr's CPU busy during code upload.
+.fff6 4c f6 ff jmp $fff6 wreset jmp wreset
+
+ * = $fffa ; Hardware vectors.
+>fffa 4f f2 .word platform.hw_nmi
+>fffc ab f1 .word platform.hw_reset
+>fffe 53 f2 .word platform.hw_irq
+
+ platform .namespace
+
+ .section dp
+>00d5 mmuctl .byte ? ; Holds $0 during interrupt processing.
+>00d6 iomap .byte ? ; Holds $1 during interrupt processing.
+>00d7 ptr .word ? ; for far_write
+>00d9 tmp .byte ? ; for far_write
+ .send
+
+ .section kmem
+>0399 nmi_flag .byte ?
+ .send
+
+ spin .macro OFFSET
+ .endm
+
+ .section kernel
+
+>f1aa 00 booted .byte 0 ; Reset detect; overwritten by a code push.
+
+.f1ab hw_reset:
+
+.f1ab 78 sei sei
+
+ ; Initialize the stack pointer
+.f1ac a2 ff ldx #$ff ldx #$ff
+.f1ae 9a txs txs
+
+ ; "clear" the NMI flag.
+.f1af ba tsx tsx
+.f1b0 8e 99 03 stx $0399 stx nmi_flag
+
+ ; Check for a reset after the kernel has started.
+.f1b3 ad aa f1 lda $f1aa lda booted
+.f1b6 d0 17 bne $f1cf bne upload ; Enter "wait for upload" mode.
+.f1b8 ee aa f1 inc $f1aa inc booted
+
+ ; Set up MMU LUTs
+.f1bb 20 f0 f1 jsr $f1f0 jsr mmu_init
+
+ ; Initialize the hardware
+.f1be 20 16 f2 jsr $f216 jsr init
+.f1c1 b0 09 bcs $f1cc bcs _error
+
+ ; Default $c000 to general I/O.
+.f1c3 64 01 stz $01 stz $1
+
+ ; Switch to MMU 3 and chain to the kernel.
+.f1c5 a9 33 lda #$33 lda #%00110011 ; LUT3 mapped and pre-set for edit.
+.f1c7 85 00 sta $00 sta $0
+.f1c9 4c 5d e7 jmp $e75d jmp kernel.start
+.f1cc 4c 28 e7 jmp $e728 _error jmp kernel.error
+
+.f1cf upload: ; TODO: use kernel string service
+.f1cf 20 e4 f7 jsr $f7e4 jsr console.init
+.f1d2 a9 e9 lda #$e9 lda #<_msg
+.f1d4 85 a5 sta $a5 sta kernel.src
+.f1d6 a9 f1 lda #$f1 lda #>_msg
+.f1d8 85 a6 sta $a6 sta kernel.src+1
+.f1da a0 00 ldy #$00 ldy #0
+.f1dc b1 a5 lda ($a5),y _loop lda (kernel.src),y
+.f1de f0 06 beq $f1e6 beq _done
+.f1e0 20 b9 f9 jsr $f9b9 jsr console.putc
+.f1e3 c8 iny iny
+.f1e4 80 f6 bra $f1dc bra _loop
+.f1e6 4c f6 ff jmp $fff6 _done jmp wreset
+>f1e9 55 70 6c 6f 61 64 00 _msg .null "Upload"
+
+
+.f1f0 mmu_init
+
+ ; Set up MMU LUTs 1-3 to match MMU0 while interrupts are off.
+.f1f0 a9 80 lda #$80 lda #%10000000 ; Edit MMU 0 (MMU0 mapped)
+.f1f2 20 07 f2 jsr $f207 jsr _fill
+.f1f5 a9 90 lda #$90 lda #%10010000 ; Edit MMU 1 (MMU0 mapped)
+.f1f7 20 07 f2 jsr $f207 jsr _fill
+.f1fa a9 a0 lda #$a0 lda #%10100000 ; Edit MMU 2 (MMU0 mapped)
+.f1fc 20 07 f2 jsr $f207 jsr _fill
+.f1ff a9 b0 lda #$b0 lda #%10110000 ; Edit MMU 3 (MMU0 mapped)
+.f201 20 07 f2 jsr $f207 jsr _fill
+
+.f204 64 00 stz $00 stz $0 ; Return MMU0, no LUT mapped.
+.f206 60 rts rts
+.f207 _fill
+.f207 85 00 sta $00 sta $0
+.f209 a2 00 ldx #$00 ldx #0
+.f20b _loop
+.f20b 8a txa txa
+.f20c 95 08 sta $08,x sta $8,x
+.f20e 95 10 sta $10,x sta $10,x
+.f210 e8 inx inx
+.f211 e0 08 cpx #$08 cpx #8
+.f213 d0 f6 bne $f20b bne _loop
+
+.f215 60 rts rts
+
+.f216 init
+.f216 20 d9 f2 jsr $f2d9 jsr INIT_CODEC
+.f219 20 00 f5 jsr $f500 jsr irq.init
+.f21c 20 fa e6 jsr $e6fa jsr kernel.init
+.f21f 20 e4 f7 jsr $f7e4 jsr console.init
+.f222 b0 0a bcs $f22e bcs _out
+
+.f224 64 01 stz $01 stz $1
+.f226 20 2f f2 jsr $f22f jsr tick_init
+.f229 20 b8 f7 jsr $f7b8 jsr ps2.init
+.f22c b0 00 bcs $f22e bcs _out
+
+.f22e 60 rts _out rts
+
+
+.f22f tick_init
+ ; TODO: allocate the device handle.
+
+.f22f 20 1a fb jsr $fb1a jsr c64kbd.init
+
+.f232 a9 49 lda #$49 lda #tick
+.f239 8d 01 04 sta $0401 sta frame+1
+
+.f23c a9 00 lda #$00 lda #.
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ .cpu "w65c02"
+
+ .namespace platform
+ iec .namespace
+
+ ; The C256 Foenix Jr. has the low-level IEC protocol implemented in its FPGA.
+
+ ; IEC
+
+ ; Writting:
+=$d680 TALKER_CMD = $D680 ; Write all Command here, save $3F, $5F
+=$d681 TALKER_CMD_LAST = $D681 ; This is for $3F or $5F Only
+=$d682 TALKER_DTA = $D682 ; Any other data, write here
+=$d683 TALKER_DTA_LAST = $D683 ; Write to this address for the last data to send
+
+ ; Reading:
+=$d680 LISTNER_DTA = $D680 ; Read Data From FIFO
+=$d681 LISTNER_FIFO_STAT = $D681 ; Bit[0] Empty Flag (1 = Empty, 0 = Data in FIFO)
+=$d682 LISTNER_FIFO_CNT_LO = $D682
+=$d683 LISTNER_FIFO_CNT_HI = $D683
+
+=1 STAT_RX_NEMPTY = 1
+=2 STAT_RX_FULL = 2
+=4 STAT_RX_EOI = 4
+=16 STAT_NO_ACK = 16 ; Device not preset
+
+ .section dp
+>00da iec_timeout .byte ?
+>00db mark .byte ?
+>00dc eoi .byte ?
+ .send
+
+ .section kernel
+
+.f38a settmo
+.f38a 85 da sta $da sta iec_timeout
+.f38c 60 rts rts
+
+.f38d read_byte
+
+ ; Return EOI if the stream has hit EOI.
+.f38d a5 dc lda $dc lda eoi
+.f38f f0 04 beq $f395 beq _read
+.f391 a9 40 lda #$40 lda #kernel.iec.EOI
+.f393 38 sec sec
+.f394 60 rts rts
+
+.f395 _read
+ ; Save I/O map and switch to I/O Zero.
+.f395 da phx phx
+.f396 a6 01 ldx $01 ldx $1
+.f398 64 01 stz $01 stz $1
+
+.f39a 97 01 smb 1,$01 smb 1,$1
+.f39c ee 4f c0 inc $c04f inc $c000+79
+.f39f 64 01 stz $01 stz $1
+
+ ; Set 'mark' to the future timeout time.
+ ; The C64 claims to time out after 64ms.
+ ; To be safe (not knowing where we are
+ ; in the current "tick" cycle), we wait
+ ; ~0.066 - ~0.08s.
+.f3a1 a5 a3 lda $a3 lda kernel.ticks
+.f3a3 18 clc clc
+.f3a4 69 05 adc #$05 adc #5 ; 4=0.66s + 1
+.f3a6 85 db sta $db sta mark
+.f3a8 _loop
+.f3a8 ad 81 d6 lda $d681 lda LISTNER_FIFO_STAT
+.f3ab 4a lsr a lsr a ; Carry set if fifo is empty
+.f3ac 90 0c bcc $f3ba bcc _found
+
+ ; If timeouts are disabled, just keep trying...
+.f3ae a5 da lda $da lda iec_timeout
+ ;bpl _loop ; no timeout check
+
+ ; Otherwise, keep trying until we reach mark.
+.f3b0 a5 a3 lda $a3 lda kernel.ticks
+.f3b2 c5 db cmp $db cmp mark
+.f3b4 90 f2 bcc $f3a8 bcc _loop
+
+ ; Report a timeout; carry is already set.
+.f3b6 a5 02 lda $02 lda kernel.iec.TIMEOUT_READ
+.f3b8 80 0c bra $f3c6 bra _out
+
+.f3ba _found
+ ; Read the data.
+.f3ba ad 80 d6 lda $d680 lda LISTNER_DTA
+
+ ; Update our internal EOI flag.
+.f3bd 48 pha pha
+.f3be ad 81 d6 lda $d681 lda LISTNER_FIFO_STAT
+.f3c1 29 04 and #$04 and #STAT_RX_EOI
+.f3c3 85 dc sta $dc sta eoi
+.f3c5 68 pla pla
+
+.f3c6 _out
+ ; Restore I/O map.
+.f3c6 86 01 stx $01 stx $1
+.f3c8 fa plx plx
+.f3c9 60 rts rts
+
+
+.f3ca write_byte
+.f3ca da phx phx
+.f3cb a6 01 ldx $01 ldx $1
+.f3cd 64 01 stz $01 stz $1
+.f3cf 8d 82 d6 sta $d682 sta TALKER_DTA
+.f3d2 80 27 bra $f3fb bra ret_stat
+
+.f3d4 write_last_byte
+.f3d4 da phx phx
+.f3d5 a6 01 ldx $01 ldx $1
+.f3d7 64 01 stz $01 stz $1
+.f3d9 8d 83 d6 sta $d683 sta TALKER_DTA_LAST
+.f3dc 80 1d bra $f3fb bra ret_stat
+
+.f3de send_atn_byte
+.f3de 97 01 smb 1,$01 smb 1,$1
+.f3e0 17 01 rmb 1,$01 rmb 1,$1
+.f3e2 ee 4e c0 inc $c04e inc $c000+78
+.f3e5 64 01 stz $01 stz $1
+.f3e7 da phx phx
+.f3e8 a6 01 ldx $01 ldx $1
+.f3ea 64 01 stz $01 stz $1
+.f3ec 8d 80 d6 sta $d680 sta TALKER_CMD
+.f3ef 80 0a bra $f3fb bra ret_stat
+
+.f3f1 send_atn_last_byte
+.f3f1 da phx phx
+.f3f2 a6 01 ldx $01 ldx $1
+.f3f4 64 01 stz $01 stz $1
+.f3f6 8d 81 d6 sta $d681 sta TALKER_CMD_LAST
+.f3f9 80 00 bra $f3fb bra ret_stat
+
+.f3fb ret_stat
+.f3fb 64 dc stz $dc stz eoi
+.f3fd 20 0f f4 jsr $f40f jsr delay500us ; Better would be wait for empty tx fifo or timeout.
+.f400 ad 81 d6 lda $d681 lda LISTNER_FIFO_STAT
+.f403 29 10 and #$10 and #STAT_NO_ACK
+.f405 18 clc clc
+.f406 f0 03 beq $f40b beq _out
+.f408 38 sec sec
+.f409 a9 80 lda #$80 lda #kernel.iec.NO_DEVICE ; TODO: when is this set?
+.f40b _out
+.f40b 86 01 stx $01 stx $1
+.f40d fa plx plx
+.f40e 60 rts rts
+
+.f40f delay500us
+.f40f a9 32 lda #$32 lda #50 ; Kill ~500us
+.f411 20 17 f4 jsr $f417 _loop jsr _delay10us
+.f414 3a dec a dec a
+.f415 d0 fa bne $f411 bne _loop
+
+.f417 20 20 f4 jsr $f420 _delay10us jsr _delay2
+.f41a 20 1d f4 jsr $f41d _delay8 jsr _delay4
+.f41d 20 20 f4 jsr $f420 _delay4 jsr _delay2
+.f420 20 23 f4 jsr $f423 _delay2 jsr _delay1
+.f423 _delay1 ; Kill 8 clock cycles ... slightly larger than 1Mhz = 1us
+.f423 ea nop nop
+.f424 ea nop nop
+.f425 ea nop nop
+.f426 ea nop nop
+.f427 ea nop nop
+.f428 60 rts rts
+
+ .send
+ .endn
+ .endn
+
+
+;****** Processing input file: platform/jr/irq.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; This file is from the w6502c TinyCore kernel by the same author.
+
+ .cpu "w65c02"
+
+ irq .namespace
+
+ ; Interrupt Sources
+ .virtual 0
+>0000 frame .byte ?
+>0001 line .byte ?
+>0002 ps2_0 .byte ?
+>0003 ps2_1 .byte ?
+>0004 timer0 .byte ?
+>0005 timer1 .byte ?
+>0006 dma .byte ?
+>0007 .byte ?
+>0008 serial .byte ?
+>0009 col0 .byte ?
+>000a col1 .byte ?
+>000b col2 .byte ?
+>000c rtc .byte ?
+>000d via .byte ?
+>000e iec .byte ?
+>000f sdc .byte ?
+.0010 max .endv
+
+ ; Dispatch table
+ .section kmem
+>039a irq0 .fill 8
+>03a2 irq1 .fill 8
+ .send
+
+ ; Interrupt priotity table
+ .section tables
+>e500 00 00 01 00 02 00 01 00 first_bit: .byte 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e508 03 00 01 00 02 00 01 00
+>e510 04 00 01 00 02 00 01 00 .byte 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e518 03 00 01 00 02 00 01 00
+>e520 05 00 01 00 02 00 01 00 .byte 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e528 03 00 01 00 02 00 01 00
+>e530 04 00 01 00 02 00 01 00 .byte 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e538 03 00 01 00 02 00 01 00
+>e540 06 00 01 00 02 00 01 00 .byte 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e548 03 00 01 00 02 00 01 00
+>e550 04 00 01 00 02 00 01 00 .byte 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e558 03 00 01 00 02 00 01 00
+>e560 05 00 01 00 02 00 01 00 .byte 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e568 03 00 01 00 02 00 01 00
+>e570 04 00 01 00 02 00 01 00 .byte 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e578 03 00 01 00 02 00 01 00
+>e580 07 00 01 00 02 00 01 00 .byte 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e588 03 00 01 00 02 00 01 00
+>e590 04 00 01 00 02 00 01 00 .byte 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e598 03 00 01 00 02 00 01 00
+>e5a0 05 00 01 00 02 00 01 00 .byte 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e5a8 03 00 01 00 02 00 01 00
+>e5b0 04 00 01 00 02 00 01 00 .byte 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e5b8 03 00 01 00 02 00 01 00
+>e5c0 06 00 01 00 02 00 01 00 .byte 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e5c8 03 00 01 00 02 00 01 00
+>e5d0 04 00 01 00 02 00 01 00 .byte 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e5d8 03 00 01 00 02 00 01 00
+>e5e0 05 00 01 00 02 00 01 00 .byte 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e5e8 03 00 01 00 02 00 01 00
+>e5f0 04 00 01 00 02 00 01 00 .byte 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+>e5f8 03 00 01 00 02 00 01 00
+ .send
+
+ .section kernel
+>f429 .align 256
+.f500 init:
+.f500 48 pha pha
+
+.f501 64 01 stz $01 stz $1
+
+ ; Begin with all interrupts masked.
+ ; Begin with all interrupts on the falling edge.
+.f503 a9 ff lda #$ff lda #$ff
+.f505 8d 6c d6 sta $d66c sta INT_MASK_REG0
+.f508 8d 6d d6 sta $d66d sta INT_MASK_REG1
+.f50b 8d 68 d6 sta $d668 sta INT_EDGE_REG0
+.f50e 8d 69 d6 sta $d669 sta INT_EDGE_REG1
+.f511 ad 60 d6 lda $d660 lda INT_PENDING_REG0
+.f514 8d 60 d6 sta $d660 sta INT_PENDING_REG0
+.f517 ad 61 d6 lda $d661 lda INT_PENDING_REG1
+.f51a 8d 61 d6 sta $d661 sta INT_PENDING_REG1
+
+ ; Polarities aren't presently initialized in the
+ ; official Foenix kernel; leaving them uninitialized
+ ; here.
+ ; lda #0
+ ; sta INT_POL_REG0
+ ; sta INT_POL_REG2
+
+.f51d a9 36 lda #$36 lda #dummy
+.f524 8d 03 04 sta $0403 sta Devices+3
+.f527 a9 02 lda #$02 lda #2
+.f529 a0 00 ldy #$00 ldy #0
+.f52b 99 9a 03 sta $039a,y _loop sta irq0,y
+.f52e c8 iny iny
+.f52f c0 10 cpy #$10 cpy #16
+.f531 d0 f8 bne $f52b bne _loop
+
+.f533 58 cli cli
+.f534 68 pla pla
+.f535 18 clc clc
+.f536 60 rts dummy rts
+
+.f537 show
+.f537 a0 02 ldy #$02 ldy #2
+.f539 84 01 sty $01 sty $1
+.f53b 48 pha pha
+.f53c 4a lsr a lsr a
+.f53d 4a lsr a lsr a
+.f53e 4a lsr a lsr a
+.f53f 4a lsr a lsr a
+.f540 a8 tay tay
+.f541 b9 54 f5 lda $f554,y lda _hex,y
+.f544 8d 20 c0 sta $c020 sta $c020
+.f547 68 pla pla
+.f548 29 0f and #$0f and #$0f
+.f54a a8 tay tay
+.f54b b9 54 f5 lda $f554,y lda _hex,y
+.f54e 8d 21 c0 sta $c021 sta $c021
+.f551 64 01 stz $01 stz $1
+.f553 60 rts rts
+>f554 30 31 32 33 34 35 36 37 _hex .null "0123456789abcdef"
+>f55c 38 39 61 62 63 64 65 66 00
+.f565 show2
+.f565 48 pha pha
+.f566 5a phy phy
+.f567 a0 02 ldy #$02 ldy #2
+.f569 84 01 sty $01 sty $1
+.f56b 48 pha pha
+.f56c 4a lsr a lsr a
+.f56d 4a lsr a lsr a
+.f56e 4a lsr a lsr a
+.f56f 4a lsr a lsr a
+.f570 a8 tay tay
+.f571 b9 86 f5 lda $f586,y lda _hex,y
+.f574 8d 22 c0 sta $c022 sta $c022
+.f577 68 pla pla
+.f578 29 0f and #$0f and #$0f
+.f57a a8 tay tay
+.f57b b9 86 f5 lda $f586,y lda _hex,y
+.f57e 8d 23 c0 sta $c023 sta $c023
+.f581 64 01 stz $01 stz $1
+.f583 7a ply ply
+.f584 68 pla pla
+.f585 60 rts rts
+>f586 30 31 32 33 34 35 36 37 _hex .null "0123456789abcdef"
+>f58e 38 39 61 62 63 64 65 66 00
+
+.f597 dispatch:
+
+.f597 64 01 stz $01 _reg0 stz $1
+.f599 ae 60 d6 ldx $d660 ldx INT_PENDING_REG0
+.f59c f0 11 beq $f5af beq _reg1
+.f59e bc 00 e5 ldy $e500,x ldy first_bit,x ; 0..7
+.f5a1 b9 c8 f5 lda $f5c8,y lda bit,y ; 1, 2, 4, ...
+.f5a4 8d 60 d6 sta $d660 sta INT_PENDING_REG0
+.f5a7 be 9a 03 ldx $039a,y ldx irq0,y
+.f5aa 20 00 ed jsr $ed00 jsr kernel.device.data
+.f5ad 80 e8 bra $f597 bra _reg0
+
+.f5af 64 01 stz $01 _reg1 stz $1
+.f5b1 ae 61 d6 ldx $d661 ldx INT_PENDING_REG1
+.f5b4 f0 11 beq $f5c7 beq _reg2
+.f5b6 bc 00 e5 ldy $e500,x ldy first_bit,b,x
+.f5b9 b9 c8 f5 lda $f5c8,y lda bit,b,y
+.f5bc 8d 61 d6 sta $d661 sta INT_PENDING_REG1
+.f5bf be a2 03 ldx $03a2,y ldx irq1,y
+.f5c2 20 00 ed jsr $ed00 jsr kernel.device.data
+.f5c5 80 e8 bra $f5af bra _reg1
+
+.f5c7 60 rts _reg2 rts
+
+>f5c8 01 02 04 08 10 20 40 80 bit: .byte 1,2,4,8,16,32,64,128
+
+.f5d0 install:
+ ; IN: A -> lsb of a vector in Devices
+ ; Y -> requested IRQ ID
+
+.f5d0 c0 10 cpy #$10 cpy #max
+.f5d2 b0 03 bcs $f5d7 bcs _out
+
+.f5d4 99 9a 03 sta $039a,y sta irq0,y
+.f5d7 60 rts _out rts
+
+
+.f5d8 enable:
+ ; IN: A -> requested IRQ ID to enable.
+
+.f5d8 c9 10 cmp #$10 cmp #max
+.f5da b0 0d bcs $f5e9 bcs _out
+
+.f5dc da phx phx
+.f5dd 20 ea f5 jsr $f5ea jsr map
+.f5e0 49 ff eor #$ff eor #255 ; clear bit to enable source.
+.f5e2 3d 6c d6 and $d66c,x and INT_MASK_REG0,x
+.f5e5 9d 6c d6 sta $d66c,x sta INT_MASK_REG0,x
+.f5e8 fa plx plx
+
+.f5e9 60 rts _out rts
+
+.f5ea map:
+ ; A = IRQ #
+ ; X <- IRQth byte
+ ; A <- IRQth bit set
+
+ ; Offset X to the IRQth byte.
+.f5ea a2 00 ldx #$00 ldx #0
+.f5ec 89 08 bit #$08 bit #8
+.f5ee f0 01 beq $f5f1 beq _bit
+.f5f0 e8 inx inx
+
+.f5f1 29 07 and #$07 _bit and #7
+.f5f3 5a phy phy
+.f5f4 a8 tay tay
+.f5f5 b9 c8 f5 lda $f5c8,y lda bit,y
+.f5f8 7a ply ply
+.f5f9 60 rts rts
+
+.f5fa disable:
+ ; IN: A -> requested IRQ ID to diable.
+
+.f5fa c9 10 cmp #$10 cmp #max
+.f5fc b0 0b bcs $f609 bcs _out
+
+.f5fe da phx phx
+.f5ff 20 ea f5 jsr $f5ea jsr map
+.f602 1d 6c d6 ora $d66c,x ora INT_MASK_REG0,x
+.f605 9d 6c d6 sta $d66c,x sta INT_MASK_REG0,x
+.f608 fa plx plx
+
+.f609 60 rts _out rts
+
+
+ .send
+ .endn
+
+
+;****** Processing input file: platform/jr/ps2.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; Instantiate an i8042 PS2 stack.
+
+ .cpu "w65c02"
+
+ .namespace platform
+ ps2 .namespace
+
+ .section kernel
+
+.f60a i8042
+
+=$d640 BASE = $D640
+
+ self .namespace
+ .virtual DevState
+>0500 queue .word ? ; write queue, must be first
+>0502 qstate .word ? ; atomic queue count for irq management
+>0504 lower .byte ? ; Lower handler
+>0505 config .byte ? ; Current i8042 config
+>0506 last .byte ? ; tick at time of last data write
+>0507 mask .byte ? ; bit to check for ready status
+>0508 mark .byte ? ; ticks at start of status wait loop
+>0509 pending .byte ? ; true if 'data' contains pending data.
+>050a data .byte ? ; delayed write while waiting for the i8042.
+ .endv
+ .endn
+
+=4 CMD = 4
+=0 DATA = 0
+=4 STATUS = 4
+
+.f60a vectors
+>f60a 52 f7 .word ps2_data
+>f60c b6 f7 .word ps2_status
+>f60e b6 f7 .word ps2_fetch
+>f610 ee f6 .word ps2_open
+>f612 99 f7 .word ps2_get
+>f614 9e f7 .word ps2_set
+>f616 30 f7 .word ps2_send
+>f618 93 f7 .word ps2_close
+
+.f61a init
+.f61a 20 2a ed jsr $ed2a jsr kernel.device.alloc
+.f61d b0 09 bcs $f628 bcs _out
+.f61f 20 29 f6 jsr $f629 jsr ps2_init
+.f622 90 04 bcc $f628 bcc _out
+.f624 20 3a ed jsr $ed3a jsr kernel.device.free
+.f627 38 sec sec
+.f628 60 rts _out rts
+
+.f629 ps2_init
+ ; 0. Disable IRQs
+.f629 a9 02 lda #$02 lda #irq.ps2_0
+.f62b 20 fa f5 jsr $f5fa jsr irq.disable
+.f62e a9 03 lda #$03 lda #irq.ps2_1
+.f630 20 fa f5 jsr $f5fa jsr irq.disable
+
+ ; 1. Init the USB controllers (N/A)
+ ; 2. Determine if the i8042 exists (N/A)
+
+ ; 3. Disable the ports
+
+ ; Dispable the first port
+.f633 a9 ac lda #$ac lda #$ac
+.f635 20 8e f6 jsr $f68e jsr send_cmd
+
+ ; Disable the second port
+.f638 a9 a7 lda #$a7 lda #$a7
+.f63a 20 8e f6 jsr $f68e jsr send_cmd
+
+ ; 4. Flush the recv queueu
+.f63d 20 a0 f6 jsr $f6a0 jsr flush
+.f640 b0 2b bcs $f66d bcs _out
+
+ ; 5. Configure the controller
+.f642 20 6e f6 jsr $f66e jsr _configure
+.f645 b0 26 bcs $f66d bcs _out
+
+
+ ; 6. Test the controller
+.f647 a9 aa lda #$aa lda #$aa
+.f649 20 ae f6 jsr $f6ae jsr txrx
+.f64c b0 1f bcs $f66d bcs _out
+.f64e c9 55 cmp #$55 cmp #$55
+.f650 38 sec sec
+.f651 d0 1a bne $f66d bne _out
+.f653 20 6e f6 jsr $f66e jsr _configure
+.f656 b0 15 bcs $f66d bcs _out
+
+ ; 7. Enable port 2 and recheck bit 5 (N/A)
+
+ ; 8. Test the ports (meh)
+
+ ; 9. Set the post bit
+.f658 bd 05 05 lda $0505,x lda self.config,x
+.f65b 09 04 ora #$04 ora #4
+.f65d 20 7c f6 jsr $f67c jsr send_conf
+.f660 b0 0b bcs $f66d bcs _out
+.f662 _vectors
+.f662 a9 0a lda #$0a lda #vectors
+.f668 85 a6 sta $a6 sta kernel.src+1
+.f66a 20 47 ed jsr $ed47 jsr kernel.device.install
+
+.f66d 60 rts _out rts
+
+.f66e _configure
+.f66e a9 20 lda #$20 lda #$20 ; CmdGetConfig
+.f670 20 ae f6 jsr $f6ae jsr txrx
+.f673 b0 f8 bcs $f66d bcs _out
+.f675 29 bc and #$bc and #255-1-2-64
+.f677 29 0f and #$0f and #$0f
+.f679 4c 7c f6 jmp $f67c jmp send_conf
+
+.f67c send_conf
+.f67c 48 pha pha
+.f67d a9 60 lda #$60 lda #$60 ; CmdSetConfig
+.f67f 20 8e f6 jsr $f68e jsr send_cmd
+.f682 68 pla pla
+.f683 b0 08 bcs $f68d bcs _end
+.f685 20 97 f6 jsr $f697 jsr send_data
+.f688 b0 03 bcs $f68d bcs _end
+.f68a 9d 05 05 sta $0505,x sta self.config,b,x
+.f68d 60 rts _end rts
+
+
+.f68e send_cmd:
+.f68e 20 c1 f6 jsr $f6c1 jsr tx_wait
+.f691 b0 03 bcs $f696 bcs _out
+.f693 8d 44 d6 sta $d644 sta BASE+CMD
+.f696 60 rts _out rts
+
+.f697 send_data:
+.f697 20 c1 f6 jsr $f6c1 jsr tx_wait
+.f69a b0 03 bcs $f69f bcs _out
+.f69c 8d 40 d6 sta $d640 sta BASE+DATA
+.f69f 60 rts _out rts
+
+.f6a0 a0 64 ldy #$64 flush ldy #100 ; Max bytes to eat.
+.f6a2 20 b4 f6 jsr $f6b4 _flush jsr recv_data
+.f6a5 b0 05 bcs $f6ac bcs _flushed
+.f6a7 88 dey dey
+.f6a8 d0 f8 bne $f6a2 bne _flush
+.f6aa 38 sec sec
+.f6ab 60 rts rts
+.f6ac 18 clc _flushed clc
+.f6ad 60 rts rts
+
+.f6ae txrx:
+.f6ae 20 8e f6 jsr $f68e jsr send_cmd
+.f6b1 90 01 bcc $f6b4 bcc recv_data
+.f6b3 60 rts rts
+
+.f6b4 recv_data:
+.f6b4 20 bd f6 jsr $f6bd jsr rx_wait
+.f6b7 b0 03 bcs $f6bc bcs _out
+.f6b9 ad 40 d6 lda $d640 lda BASE+DATA
+.f6bc 60 rts _out rts
+
+.f6bd rx_wait:
+.f6bd a9 01 lda #$01 lda #1
+.f6bf 80 08 bra $f6c9 bra wait
+
+.f6c1 tx_wait:
+.f6c1 48 pha pha
+.f6c2 a9 02 lda #$02 lda #2
+.f6c4 20 c9 f6 jsr $f6c9 jsr wait
+.f6c7 68 pla pla
+.f6c8 60 rts rts
+
+.f6c9 wait:
+ ; IN: A = bit to wait on
+ ; OUT: Carry set on timeout
+
+.f6c9 9d 07 05 sta $0507,x sta self.mask,x
+.f6cc a5 a3 lda $a3 lda kernel.ticks
+.f6ce 9d 08 05 sta $0508,x sta self.mark,x
+
+.f6d1 18 clc clc
+.f6d2 ad 44 d6 lda $d644 _loop lda BASE+STATUS
+.f6d5 49 02 eor #$02 eor #2 ; normalize
+.f6d7 3d 07 05 and $0507,x and self.mask,x
+.f6da f0 01 beq $f6dd beq _wait
+.f6dc 60 rts _out rts
+
+.f6dd a5 a3 lda $a3 _wait lda kernel.ticks
+.f6df 38 sec sec
+.f6e0 fd 08 05 sbc $0508,x sbc self.mark,x
+.f6e3 c9 1e cmp #$1e cmp #30
+.f6e5 b0 f5 bcs $f6dc bcs _out
+.f6e7 20 f8 e6 jsr $e6f8 jsr kernel.thread.yield
+.f6ea 80 e6 bra $f6d2 bra _loop
+
+.f6ec hang
+ .if false
+ .endif
+.f6ec 80 fe bra $f6ec bra hang
+
+=54864 TIMER0_CTRL_REG = $d650+$0
+=54865 TIMER0_CHARGE_L = $d650+$1
+=54866 TIMER0_CHARGE_M = $d650+$2
+=54867 TIMER0_CHARGE_H = $d650+$3
+=54868 TIMER0_CMP_REG = $d650+$4
+=54869 TIMER0_CMP_L = $d650+$5
+=54870 TIMER0_CMP_M = $d650+$6
+=54871 TIMER0_CMP_H = $d650+$7
+
+=$01 TMR0_EN = $01
+=$02 TMR0_SCLR = $02
+=$04 TMR0_SLOAD = $04 ; Use SLOAD is
+=$08 TMR0_UPDWN = $08
+
+=$01 TMR0_CMP_RECLR = $01 ; set to one for it to cycle when Counting up
+=$02 TMR0_CMP_RELOAD = $02 ; Set to one for it to reload when Counting Down
+
+ .virtual Tokens
+>0200 port .byte ?
+>0201 data .byte ?
+ .endv
+
+
+.f6ee ps2_open
+
+.f6ee 20 5c ed jsr $ed5c jsr kernel.device.queue.init
+.f6f1 9e 09 05 stz $0509,x stz self.pending,x
+
+.f6f4 a9 ff lda #$ff lda #$ff
+.f6f6 9d 02 05 sta $0502,x sta self.qstate,x ; qstate == 0 when count is 1.
+
+.f6f9 8a txa txa
+.f6fa a0 04 ldy #$04 ldy #irq.timer0
+.f6fc 20 d0 f5 jsr $f5d0 jsr irq.install
+
+.f6ff a9 a8 lda #$a8 lda #$a8
+.f701 8d 55 d6 sta $d655 sta TIMER0_CMP_L
+.f704 a9 61 lda #$61 lda #$61
+.f706 8d 56 d6 sta $d656 sta TIMER0_CMP_M
+.f709 a9 00 lda #$00 lda #$0
+.f70b 8d 57 d6 sta $d657 sta TIMER0_CMP_H
+
+.f70e 9c 51 d6 stz $d651 stz TIMER0_CHARGE_L
+.f711 9c 52 d6 stz $d652 stz TIMER0_CHARGE_M
+.f714 9c 53 d6 stz $d653 stz TIMER0_CHARGE_H
+
+.f717 a9 02 lda #$02 lda #TMR0_CMP_RELOAD
+.f719 8d 54 d6 sta $d654 sta TIMER0_CMP_REG
+
+.f71c a9 64 lda #$64 lda #100
+.f71e 8d 19 d0 sta $d019 sta VKY_LINE_CMP_VALUE_LO
+.f721 9c 1a d0 stz $d01a stz VKY_LINE_CMP_VALUE_HI
+.f724 a9 01 lda #$01 lda #1
+.f726 8d 18 d0 sta $d018 sta VKY_LINE_IRQ_CTRL_REG
+
+.f729 a9 04 lda #$04 lda #irq.timer0
+.f72b 20 d8 f5 jsr $f5d8 jsr irq.enable
+
+.f72e 18 clc clc
+.f72f 60 rts rts
+
+
+.f730 ps2_send
+ ; Asynchronously send the byte in A to the device at port Y.
+ ; A = byte, Y = port
+ ; Carry set on error (no free tokens)
+.f730 5a phy phy
+
+.f731 20 b6 ed jsr $edb6 jsr kernel.token.alloc
+.f734 90 02 bcc $f738 bcc _queue
+.f736 7a ply ply
+.f737 60 rts rts
+
+.f738 _queue
+.f738 99 01 02 sta $0201,y sta data,y
+.f73b 68 pla pla
+.f73c 99 00 02 sta $0200,y sta port,y
+
+.f73f 20 63 ed jsr $ed63 jsr kernel.device.queue.enque
+.f742 fe 02 05 inc $0502,x inc self.qstate,x
+.f745 d0 03 bne $f74a bne _ack
+.f747 20 4c f7 jsr $f74c jsr timer_resume
+
+.f74a _ack
+ .if false
+ .endif
+.f74a 18 clc clc
+.f74b 60 rts rts
+
+
+.f74c timer_resume
+.f74c a9 0d lda #$0d lda #TMR0_EN | TMR0_SLOAD | TMR0_UPDWN
+.f74e 8d 50 d6 sta $d650 sta TIMER0_CTRL_REG
+ .if false
+ .endif
+.f751 60 rts rts
+
+.f752 ps2_data
+ ; IRQ handler for asynchronously sending bytes to PS2 devices.
+
+ ; Verify that the i8042 can accept writes
+.f752 ad 44 d6 lda $d644 lda BASE+STATUS
+.f755 89 02 bit #$02 bit #2
+.f757 d0 21 bne $f77a bne _wait
+
+ ; If we have pending data, send it now.
+.f759 bd 0a 05 lda $050a,x lda self.data,x
+.f75c bc 09 05 ldy $0509,x ldy self.pending,x
+.f75f d0 13 bne $f774 bne _send
+
+.f761 20 74 ed jsr $ed74 jsr kernel.device.queue.deque
+.f764 b0 17 bcs $f77d bcs _done
+.f766 de 02 05 dec $0502,x dec self.qstate,x
+
+.f769 b9 00 02 lda $0200,y lda port,y
+.f76c d0 10 bne $f77e bne _prefix
+
+.f76e b9 01 02 lda $0201,y lda data,y
+.f771 20 cc ed jsr $edcc jsr kernel.token.free
+
+.f774 8d 40 d6 sta $d640 _send sta BASE+DATA
+.f777 9e 09 05 stz $0509,x stz self.pending,x
+ .if false
+ .endif
+
+.f77a 20 4c f7 jsr $f74c _wait jsr timer_resume
+.f77d 60 rts _done rts
+
+.f77e _prefix
+
+ ; Send the prefix command for a write to the second port
+.f77e a9 d4 lda #$d4 lda #$d4
+.f780 8d 44 d6 sta $d644 sta BASE+CMD
+
+ ; Flag pending
+.f783 9d 09 05 sta $0509,x sta self.pending,x ; force pending to non-zero (ie #$d4)
+
+ ; Queue the data byte
+.f786 b9 01 02 lda $0201,y lda data,y
+.f789 9d 0a 05 sta $050a,x sta self.data,x
+.f78c 20 cc ed jsr $edcc jsr kernel.token.free
+
+.f78f 80 e9 bra $f77a bra _wait ; The i8042 core lies...
+.f791 80 bf bra $f752 bra ps2_data ; Try to send straight away (queuing i8042)
+
+.f793 ps2_close
+.f793 a9 04 lda #$04 lda #irq.timer0
+.f795 20 fa f5 jsr $f5fa jsr irq.disable
+.f798 60 rts rts
+
+.f799 ps2_get
+.f799 ad 40 d6 lda $d640 lda BASE+DATA
+.f79c 18 clc clc
+.f79d 60 rts rts
+
+.f79e ps2_set
+ ; Enable (/disable) the port
+ ; Y = port #
+
+ ; Enable the interrupt on the i8042
+.f79e b9 b2 f7 lda $f7b2,y lda _irq,y
+.f7a1 1d 05 05 ora $0505,x ora self.config,x
+.f7a4 20 7c f6 jsr $f67c jsr send_conf
+.f7a7 b0 08 bcs $f7b1 bcs _out
+
+ ; Enable the port
+.f7a9 b9 b4 f7 lda $f7b4,y lda _port,y
+.f7ac 20 8e f6 jsr $f68e jsr send_cmd
+.f7af b0 00 bcs $f7b1 bcs _out
+
+.f7b1 60 rts _out rts
+
+>f7b2 01 02 _irq .byte $01, $02
+>f7b4 ae a8 _port .byte $ae, $a8
+
+
+.f7b6 ps2_status
+.f7b6 ps2_fetch
+.f7b6 18 clc clc
+.f7b7 60 rts rts
+
+
+.f7b8 init
+.f7b8 64 01 stz $01 stz $1 ; The i8042 registers are in General I/O.
+
+ ; Init and open the i8042 device.
+.f7ba 20 1a f6 jsr $f61a jsr i8042.init
+.f7bd b0 24 bcs $f7e3 bcs _out
+.f7bf 20 09 ed jsr $ed09 jsr kernel.device.open
+.f7c2 b0 1f bcs $f7e3 bcs _out
+
+ ; Init and open the first PS2 device.
+.f7c4 da phx phx
+.f7c5 8a txa txa
+.f7c6 a0 02 ldy #$02 ldy #irq.ps2_0
+.f7c8 20 3a fc jsr $fc3a jsr hardware.ps2.init
+.f7cb b0 03 bcs $f7d0 bcs _e1
+.f7cd 20 09 ed jsr $ed09 jsr kernel.device.open
+.f7d0 fa plx _e1 plx
+.f7d1 b0 10 bcs $f7e3 bcs _out
+
+ ; Init and open the second PS2 device.
+.f7d3 da phx phx
+.f7d4 8a txa txa
+.f7d5 a0 03 ldy #$03 ldy #irq.ps2_1
+.f7d7 20 3a fc jsr $fc3a jsr hardware.ps2.init
+.f7da b0 03 bcs $f7df bcs _e2
+.f7dc 20 09 ed jsr $ed09 jsr kernel.device.open
+.f7df fa plx _e2 plx
+.f7e0 b0 01 bcs $f7e3 bcs _out
+
+.f7e2 18 clc clc
+.f7e3 60 rts _out rts
+
+
+ .send
+ .endn
+ .endn
+
+
+
+;****** Processing input file: platform/jr/console.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; Low level Console driver.
+ ; TODO: move screen editor to kernel.
+
+ .cpu "w65c02"
+
+ .namespace platform
+ console .namespace
+
+ * = $c000
+>c000 00 00 00 00 00 00 00 00 .binary "platform/jr/Bm437_PhoenixEGA_8x8.bin"
+>c008 7e 81 a5 81 bd 99 81 7e 3c 7e db ff c3 7e 3c 00
+>c018 00 ee fe fe 7c 38 10 00 10 38 7c fe 7c 38 10 00
+>c028 00 3c 18 ff ff 08 18 00 10 38 7c fe fe 10 38 00
+>c038 00 00 18 3c 18 00 00 00 ff ff e7 c3 e7 ff ff ff
+>c048 00 3c 42 81 81 42 3c 00 ff c3 bd 7e 7e bd c3 ff
+>c058 01 03 07 0f 1f 3f 7f ff ff fe fc f8 f0 e0 c0 80
+>c068 04 06 07 04 04 fc f8 00 0c 0a 0d 0b f9 f9 1f 1f
+>c078 00 92 7c 44 c6 7c 92 00 00 00 60 78 7e 78 60 00
+>c088 00 00 06 1e 7e 1e 06 00 18 7e 18 18 18 18 7e 18
+>c098 66 66 66 66 66 00 66 00 ff b6 76 36 36 36 36 00
+>c0a8 7e c1 dc 22 22 1f 83 7e 00 00 00 7e 7e 00 00 00
+>c0b8 18 7e 18 18 7e 18 00 ff 18 7e 18 18 18 18 18 00
+>c0c8 18 18 18 18 18 7e 18 00 00 04 06 ff 06 04 00 00
+>c0d8 00 20 60 ff 60 20 00 00 00 00 00 c0 c0 c0 ff 00
+>c0e8 00 24 66 ff 66 24 00 00 00 00 10 38 7c fe 00 00
+>c0f8 00 00 00 fe 7c 38 10 00 00 00 00 00 00 00 00 00
+>c108 30 30 30 30 30 00 30 00 66 66 00 00 00 00 00 00
+>c118 6c 6c fe 6c fe 6c 6c 00 10 7c d2 7c 86 7c 10 00
+>c128 f0 96 fc 18 3e 72 de 00 30 48 30 78 ce cc 78 00
+>c138 0c 0c 18 00 00 00 00 00 10 60 c0 c0 c0 60 10 00
+>c148 10 0c 06 06 06 0c 10 00 00 54 38 fe 38 54 00 00
+>c158 00 18 18 7e 18 18 00 00 00 00 00 00 00 00 18 70
+>c168 00 00 00 7e 00 00 00 00 00 00 00 00 00 00 18 00
+>c178 02 06 0c 18 30 60 c0 00 7c ce de f6 e6 e6 7c 00
+>c188 18 38 78 18 18 18 3c 00 7c c6 06 0c 30 60 fe 00
+>c198 7c c6 06 3c 06 c6 7c 00 0e 1e 36 66 fe 06 06 00
+>c1a8 fe c0 c0 fc 06 06 fc 00 7c c6 c0 fc c6 c6 7c 00
+>c1b8 fe 06 0c 18 30 60 60 00 7c c6 c6 7c c6 c6 7c 00
+>c1c8 7c c6 c6 7e 06 c6 7c 00 00 30 00 00 00 30 00 00
+>c1d8 00 30 00 00 00 30 20 00 00 1c 30 60 30 1c 00 00
+>c1e8 00 00 7e 00 7e 00 00 00 00 70 18 0c 18 70 00 00
+>c1f8 7c c6 0c 18 30 00 30 00 7c 82 9a aa aa 9e 7c 00
+>c208 7c c6 c6 fe c6 c6 c6 00 fc 66 66 7c 66 66 fc 00
+>c218 7c c6 c0 c0 c0 c6 7c 00 fc 66 66 66 66 66 fc 00
+>c228 fe 62 68 78 68 62 fe 00 fe 62 68 78 68 60 f0 00
+>c238 7c c6 c6 c0 de c6 7c 00 c6 c6 c6 fe c6 c6 c6 00
+>c248 3c 18 18 18 18 18 3c 00 1e 0c 0c 0c 0c cc 78 00
+>c258 c6 cc d8 f0 d8 cc c6 00 f0 60 60 60 60 62 fe 00
+>c268 c6 ee fe d6 c6 c6 c6 00 c6 e6 f6 de ce c6 c6 00
+>c278 7c c6 c6 c6 c6 c6 7c 00 fc 66 66 7c 60 60 f0 00
+>c288 7c c6 c6 c6 c6 c6 7c 0c fc 66 66 7c 66 66 e6 00
+>c298 7c c6 c0 7c 06 c6 7c 00 7e 5a 18 18 18 18 3c 00
+>c2a8 c6 c6 c6 c6 c6 c6 7c 00 c6 c6 c6 c6 c6 6c 38 00
+>c2b8 c6 c6 c6 c6 d6 ee c6 00 c6 6c 38 38 38 6c c6 00
+>c2c8 66 66 66 3c 18 18 3c 00 fe c6 0c 18 30 66 fe 00
+>c2d8 1c 18 18 18 18 18 1c 00 c0 60 30 18 0c 06 02 00
+>c2e8 70 30 30 30 30 30 70 00 00 00 10 38 6c c6 00 00
+>c2f8 00 00 00 00 00 00 00 ff 30 30 18 00 00 00 00 00
+>c308 00 00 7c 06 7e c6 7e 00 c0 c0 fc c6 c6 c6 fc 00
+>c318 00 00 7c c6 c0 c6 7c 00 06 06 7e c6 c6 c6 7e 00
+>c328 00 00 7c c6 fe c0 7c 00 3c 66 60 f0 60 60 60 00
+>c338 00 00 7e c6 c6 7e 06 7c c0 c0 fc c6 c6 c6 c6 00
+>c348 18 00 38 18 18 18 3c 00 00 0c 00 1c 0c 0c cc 78
+>c358 c0 c0 c6 d8 f0 d8 c6 00 38 18 18 18 18 18 3c 00
+>c368 00 00 ee fe d6 c6 c6 00 00 00 fc c6 c6 c6 c6 00
+>c378 00 00 7c c6 c6 c6 7c 00 00 00 fc c6 c6 fc c0 c0
+>c388 00 00 7e c6 c6 7e 06 06 00 00 de 76 60 60 60 00
+>c398 00 00 7c c0 7c 06 7c 00 18 18 7e 18 18 18 1e 00
+>c3a8 00 00 c6 c6 c6 c6 7e 00 00 00 c6 c6 c6 6c 38 00
+>c3b8 00 00 c6 c6 d6 fe c6 00 00 00 c6 6c 38 6c c6 00
+>c3c8 00 00 c6 c6 c6 7e 06 7c 00 00 fe 0c 18 60 fe 00
+>c3d8 0e 18 18 70 18 18 0e 00 18 18 18 00 18 18 18 00
+>c3e8 e0 30 30 1c 30 30 e0 00 00 00 70 9a 0e 00 00 00
+>c3f8 00 00 18 3c 66 ff 00 00 7c c6 c0 c0 c6 7c 18 70
+>c408 66 00 c6 c6 c6 c6 7e 00 0e 18 7c c6 fe c0 7c 00
+>c418 18 24 7c 06 7e c6 7e 00 66 00 7c 06 7e c6 7e 00
+>c428 38 0c 7c 06 7e c6 7e 00 18 00 7c 06 7e c6 7e 00
+>c438 00 00 7c c0 c0 7c 18 70 18 24 7c c6 fe c0 7c 00
+>c448 66 00 7c c6 fe c0 7c 00 70 18 7c c6 fe c0 7c 00
+>c458 66 00 38 18 18 18 3c 00 18 24 38 18 18 18 3c 00
+>c468 38 0c 38 18 18 18 3c 00 66 00 7c c6 fe c6 c6 00
+>c478 18 00 7c c6 fe c6 c6 00 0e 18 fe 60 78 60 fe 00
+>c488 00 00 7c 1a 7e d8 7e 00 7e d8 d8 de f8 d8 de 00
+>c498 18 24 7c c6 c6 c6 7c 00 66 00 7c c6 c6 c6 7c 00
+>c4a8 38 0c 7c c6 c6 c6 7c 00 18 24 c6 c6 c6 c6 7e 00
+>c4b8 38 0c c6 c6 c6 c6 7e 00 66 00 c6 c6 c6 7e 06 7c
+>c4c8 66 7c c6 c6 c6 c6 7c 00 c6 00 c6 c6 c6 c6 7c 00
+>c4d8 18 7c c6 c0 c6 7c 18 00 1e 32 30 78 30 70 fe 00
+>c4e8 66 3c 18 7e 18 3c 18 00 fc c6 fc c0 cc de cc 0e
+>c4f8 00 1c 32 30 fc 30 f0 00 0e 18 7c 06 7e c6 7e 00
+>c508 1a 30 38 18 18 18 3c 00 0e 18 7c c6 c6 c6 7c 00
+>c518 0e 18 c6 c6 c6 c6 7e 00 66 98 fc c6 c6 c6 c6 00
+>c528 66 98 e6 f6 de ce c6 00 7c 06 7e c6 7e 00 fe 00
+>c538 7c c6 c6 c6 7c 00 fe 00 18 00 18 30 60 c6 7c 00
+>c548 00 00 fe c0 c0 c0 c0 00 00 00 fe 06 06 06 06 00
+>c558 c0 c0 c0 de 06 0c 1e 00 c0 c0 c0 cc 1c 3e 0c 00
+>c568 30 00 30 30 30 30 30 00 00 36 6c d8 6c 36 00 00
+>c578 00 d8 6c 36 6c d8 00 00 aa aa aa aa aa aa aa aa
+>c588 aa 55 aa 55 aa 55 aa 55 44 22 44 22 44 22 44 22
+>c598 18 18 18 18 18 18 18 18 18 18 18 f8 18 18 18 18
+>c5a8 18 18 18 f8 18 f8 18 18 36 36 36 f6 36 36 36 36
+>c5b8 00 00 00 fe 36 36 36 36 00 00 00 f8 18 f8 18 18
+>c5c8 36 36 36 f6 06 f6 36 36 36 36 36 36 36 36 36 36
+>c5d8 00 00 00 fe 06 f6 36 36 36 36 36 f6 06 fe 00 00
+>c5e8 36 36 36 fe 00 00 00 00 18 18 18 f8 18 f8 00 00
+>c5f8 00 00 00 f8 18 18 18 18 18 18 18 1f 00 00 00 00
+>c608 18 18 18 ff 00 00 00 00 00 00 00 ff 18 18 18 18
+>c618 18 18 18 1f 18 18 18 18 00 00 00 ff 00 00 00 00
+>c628 18 18 18 ff 18 18 18 18 18 18 18 1f 18 1f 18 18
+>c638 36 36 36 37 36 36 36 36 36 36 36 37 30 3f 00 00
+>c648 00 00 00 3f 30 37 36 36 36 36 36 f7 00 ff 00 00
+>c658 00 00 00 ff 00 f7 36 36 36 36 36 37 30 37 36 36
+>c668 00 00 00 ff 00 ff 00 00 36 36 36 f7 00 f7 36 36
+>c678 18 18 18 ff 00 ff 00 00 36 36 36 ff 00 00 00 00
+>c688 00 00 00 ff 00 ff 18 18 00 00 00 ff 36 36 36 36
+>c698 36 36 36 3f 00 00 00 00 18 18 18 1f 18 1f 00 00
+>c6a8 00 00 00 1f 18 1f 18 18 00 00 00 3f 36 36 36 36
+>c6b8 36 36 36 ff 36 36 36 36 18 18 18 ff 18 ff 18 18
+>c6c8 18 18 18 f8 00 00 00 00 00 00 00 1f 18 18 18 18
+>c6d8 ff ff ff ff ff ff ff ff 00 00 00 00 ff ff ff ff
+>c6e8 f0 f0 f0 f0 f0 f0 f0 f0 0f 0f 0f 0f 0f 0f 0f 0f
+>c6f8 ff ff ff ff 00 00 00 00 00 00 77 98 98 77 00 00
+>c708 1c 36 66 fc c6 c6 fc c0 fe 62 60 60 60 60 60 00
+>c718 00 00 ff 66 66 66 66 00 fe 62 30 18 30 62 fe 00
+>c728 00 00 3f 66 c6 cc 78 00 00 00 33 33 33 3e 30 f0
+>c738 00 00 ff 18 18 18 18 00 3c 18 3c 66 66 3c 18 3c
+>c748 00 7c c6 fe c6 7c 00 00 00 7e c3 c3 c3 66 e7 00
+>c758 1e 19 3c 66 c6 cc 78 00 00 00 66 99 99 66 00 00
+>c768 00 03 7c ce e6 7c c0 00 00 3e c0 fe c0 3e 00 00
+>c778 00 7e c3 c3 c3 c3 00 00 00 fe 00 fe 00 fe 00 00
+>c788 18 18 7e 18 18 7e 00 00 70 18 0c 18 70 00 fe 00
+>c798 1c 30 60 30 1c 00 fe 00 00 0e 1b 18 18 18 18 18
+>c7a8 18 18 18 18 18 d8 70 00 00 18 00 7e 00 18 00 00
+>c7b8 00 76 dc 00 76 dc 00 00 3c 66 3c 00 00 00 00 00
+>c7c8 00 18 3c 18 00 00 00 00 00 00 00 00 18 00 00 00
+>c7d8 0f 0c 0c 0c ec 6c 38 00 d8 ec cc cc 00 00 00 00
+>c7e8 f0 30 c0 f0 00 00 00 00 00 00 00 3c 3c 3c 3c 00
+>c7f8 00 00 00 00 00 00 00 00
+
+
+=60 ROWS = 60
+=80 COLS = 80
+=4 TABS = 4
+
+
+ ; IO PAGE 0
+=$d800 TEXT_LUT_FG = $D800
+=$d840 TEXT_LUT_BG = $D840
+ ; Text Memory
+=$c000 TEXT_MEM = $C000 ; IO Page 2
+=$c000 COLOR_MEM = $C000 ; IO Page 3
+
+ .section dp
+>00dd src .word ?
+>00df dest .word ?
+>00e1 count .word ?
+
+>00e3 cur_x .byte ?
+>00e4 cur_y .byte ?
+>00e5 ptr .word ?
+>00e7 color .byte ?
+>00e8 rev .byte ?
+ .send
+
+
+ .section kernel
+
+.f7e4 init
+.f7e4 20 f2 f7 jsr $f7f2 jsr TinyVky_Init
+.f7e7 a9 e6 lda #$e6 lda #$e6
+.f7e9 85 e7 sta $e7 sta color
+.f7eb 64 e8 stz $e8 stz rev
+.f7ed 20 14 f9 jsr $f914 jsr cls
+
+.f7f0 18 clc clc
+.f7f1 60 rts rts
+
+
+.f7f2 TinyVky_Init:
+.f7f2 64 01 stz $01 stz $1
+
+.f7f4 9c 00 d0 stz $d000 stz MASTER_CTRL_REG_L ; Everything off during init.
+.f7f7 9c 01 d0 stz $d001 stz MASTER_CTRL_REG_H ; 640x480
+
+.f7fa a9 01 lda #$01 lda #Mstr_Ctrl_Text_Mode_En;
+.f7fc 8d 00 d0 sta $d000 sta MASTER_CTRL_REG_L
+
+.f7ff 20 0f f8 jsr $f80f jsr init_text_palette
+.f802 20 a8 f8 jsr $f8a8 jsr init_border
+.f805 20 b5 f8 jsr $f8b5 jsr init_font
+.f808 20 60 f8 jsr $f860 jsr init_graphics_palettes
+
+ ; We'll manage our own cursor
+.f80b 9c 10 d0 stz $d010 stz VKY_TXT_CURSOR_CTRL_REG
+
+.f80e 60 rts rts
+
+.f80f init_text_palette
+
+.f80f a2 00 ldx #$00 ldx #0
+.f811 bd 20 f8 lda $f820,x _loop lda _palette,x
+.f814 9d 00 d8 sta $d800,x sta TEXT_LUT_FG,x
+.f817 9d 40 d8 sta $d840,x sta TEXT_LUT_BG,x
+.f81a e8 inx inx
+.f81b e0 40 cpx #$40 cpx #64
+.f81d d0 f2 bne $f811 bne _loop
+.f81f 60 rts rts
+.f820 _palette
+>f820 00 00 00 00 .dword $000000
+>f824 ff ff ff 00 .dword $ffffff
+>f828 00 00 88 00 .dword $880000
+>f82c ee ff aa 00 .dword $aaffee
+>f830 cc 44 cc 00 .dword $cc44cc
+>f834 55 cc 00 00 .dword $00cc55
+>f838 aa 00 00 00 .dword $0000aa
+>f83c 77 dd dd 00 .dword $dddd77
+>f840 55 88 dd 00 .dword $dd8855
+>f844 00 44 66 00 .dword $664400
+>f848 77 77 ff 00 .dword $ff7777
+>f84c 33 33 33 00 .dword $333333
+>f850 77 77 77 00 .dword $777777
+>f854 66 ff aa 00 .dword $aaff66
+>f858 ff 88 00 00 .dword $0088ff
+>f85c bb bb bb 00 .dword $bbbbbb
+
+.f860 init_graphics_palettes
+
+.f860 da phx phx
+.f861 5a phy phy
+
+ ; Save I/O page
+.f862 a4 01 ldy $01 ldy $1
+
+ ; Switch to I/O Page 1 (font and color LUTs)
+.f864 a9 01 lda #$01 lda #1
+.f866 85 01 sta $01 sta $1
+
+ ; Init ptr
+.f868 64 e5 stz $e5 stz ptr+0
+.f86a a9 d0 lda #$d0 lda #$d0
+.f86c 85 e6 sta $e6 sta ptr+1
+
+.f86e a2 00 ldx #$00 ldx #0 ; Starting color byte.
+.f870 _loop
+ ; Write the next color entry
+.f870 20 8a f8 jsr $f88a jsr write_bgra
+.f873 e8 inx inx
+
+ ; Advance the pointer; X will wrap around on its own
+
+.f874 a5 e5 lda $e5 lda ptr
+.f876 69 04 adc #$04 adc #4
+.f878 85 e5 sta $e5 sta ptr
+.f87a d0 f4 bne $f870 bne _loop
+
+.f87c a5 e6 lda $e6 lda ptr+1
+.f87e 1a inc a inc a
+.f87f 85 e6 sta $e6 sta ptr+1
+.f881 c9 e0 cmp #$e0 cmp #$e0
+.f883 d0 eb bne $f870 bne _loop
+
+ ; Restore I/O page
+.f885 84 01 sty $01 sty $1
+
+.f887 7a ply ply
+.f888 fa plx plx
+.f889 60 rts rts
+
+.f88a write_bgra
+ ; X = rrrgggbb
+ ; A palette entry consists of four consecutive bytes: B, G, R, A.
+
+.f88a 5a phy phy
+.f88b a0 03 ldy #$03 ldy #3 ; Working backwards: A,R,G,B
+
+ ; Write the Alpha value
+.f88d a9 ff lda #$ff lda #255
+.f88f 20 9e f8 jsr $f89e jsr _write
+
+ ; Write the RGB values
+.f892 8a txa txa
+.f893 88 dey _loop dey
+.f894 30 05 bmi $f89b bmi _done
+.f896 20 9e f8 jsr $f89e jsr _write
+.f899 80 f8 bra $f893 bra _loop
+
+.f89b 7a ply _done ply
+.f89c 18 clc clc
+.f89d 60 rts rts
+
+.f89e _write
+ ; Write the upper bits to (ptr),y
+.f89e 48 pha pha
+.f89f 29 e0 and #$e0 and #%111_00000
+.f8a1 91 e5 sta ($e5),y sta (ptr),y
+.f8a3 68 pla pla
+
+ ; Shift in the next set of bits (blue truncated, alpha zero).
+.f8a4 0a asl a asl a
+.f8a5 0a asl a asl a
+.f8a6 0a asl a asl a
+
+.f8a7 60 rts rts
+
+.f8a8 init_border
+.f8a8 9c 04 d0 stz $d004 stz BORDER_CTRL_REG
+.f8ab 9c 07 d0 stz $d007 stz BORDER_COLOR_R
+.f8ae 9c 06 d0 stz $d006 stz BORDER_COLOR_G
+.f8b1 9c 05 d0 stz $d005 stz BORDER_COLOR_B
+.f8b4 60 rts rts
+
+
+.f8b5 init_font:
+.f8b5 a5 01 lda $01 lda $1
+.f8b7 48 pha pha
+
+.f8b8 a9 01 lda #$01 lda #1
+.f8ba 85 01 sta $01 sta $1
+.f8bc 20 c4 f8 jsr $f8c4 jsr _install
+
+.f8bf 68 pla pla
+.f8c0 85 01 sta $01 sta $1
+
+.f8c2 18 clc clc
+.f8c3 60 rts rts
+
+.f8c4 _install
+.f8c4 da phx phx
+.f8c5 5a phy phy
+
+.f8c6 64 dd stz $dd stz src+0
+.f8c8 64 df stz $df stz dest+0
+.f8ca a9 c0 lda #$c0 lda #$c0
+.f8cc 85 de sta $de sta src+1
+.f8ce a9 c4 lda #$c4 lda #$c4
+.f8d0 85 e0 sta $e0 sta dest+1
+
+.f8d2 a2 04 ldx #$04 ldx #4
+.f8d4 a0 00 ldy #$00 ldy #0
+.f8d6 _loop
+.f8d6 a7 01 smb 2,$01 smb 2,$1
+.f8d8 b1 dd lda ($dd),y lda (src),y
+.f8da 27 01 rmb 2,$01 rmb 2,$1
+.f8dc 4a lsr a lsr a ; Jr is dropping the first pixel.
+.f8dd 91 dd sta ($dd),y sta (src),y
+.f8df 49 ff eor #$ff eor #$ff
+.f8e1 91 df sta ($df),y sta (dest),y
+.f8e3 c8 iny iny
+.f8e4 d0 f0 bne $f8d6 bne _loop
+.f8e6 e6 de inc $de inc src+1
+.f8e8 e6 e0 inc $e0 inc dest+1
+.f8ea ca dex dex
+.f8eb d0 e9 bne $f8d6 bne _loop
+
+.f8ed 7a ply ply
+.f8ee fa plx plx
+.f8ef 60 rts rts
+
+.f8f0 long_move
+.f8f0 da phx phx
+.f8f1 5a phy phy
+
+.f8f2 a0 00 ldy #$00 ldy #0
+.f8f4 a6 e2 ldx $e2 ldx count+1
+.f8f6 f0 15 beq $f90d beq _small
+
+.f8f8 b1 dd lda ($dd),y _large lda (src),y
+.f8fa 91 df sta ($df),y sta (dest),y
+.f8fc c8 iny iny
+.f8fd d0 f9 bne $f8f8 bne _large
+.f8ff e6 de inc $de inc src+1
+.f901 e6 e0 inc $e0 inc dest+1
+.f903 ca dex dex
+.f904 d0 f2 bne $f8f8 bne _large
+.f906 80 05 bra $f90d bra _small
+
+.f908 b1 dd lda ($dd),y _loop lda (src),y
+.f90a 91 df sta ($df),y sta (dest),y
+
+.f90c c8 iny iny
+.f90d c4 e1 cpy $e1 _small cpy count
+.f90f d0 f7 bne $f908 bne _loop
+
+.f911 7a ply ply
+.f912 fa plx plx
+.f913 60 rts rts
+
+.f914 cls
+.f914 48 pha pha
+.f915 da phx phx
+.f916 5a phy phy
+
+.f917 a9 02 lda #$02 lda #2
+.f919 85 01 sta $01 sta $1
+.f91b a9 20 lda #$20 lda #' '
+.f91d 20 34 f9 jsr $f934 jsr _fill
+
+.f920 a9 03 lda #$03 lda #3
+.f922 85 01 sta $01 sta $1
+.f924 a5 e7 lda $e7 lda color
+.f926 20 34 f9 jsr $f934 jsr _fill
+
+.f929 a2 00 ldx #$00 ldx #0
+.f92b a0 00 ldy #$00 ldy #0
+.f92d 20 49 f9 jsr $f949 jsr gotoxy
+
+.f930 7a ply ply
+.f931 fa plx plx
+.f932 68 pla pla
+.f933 60 rts rts
+
+.f934 _fill
+.f934 64 e5 stz $e5 stz ptr+0
+.f936 a0 c0 ldy #$c0 ldy #$c0
+.f938 84 e6 sty $e6 sty ptr+1
+.f93a a2 14 ldx #$14 ldx #$14
+.f93c a0 00 ldy #$00 ldy #0
+.f93e 91 e5 sta ($e5),y _loop sta (ptr),y
+.f940 c8 iny iny
+.f941 d0 fb bne $f93e bne _loop
+.f943 e6 e6 inc $e6 inc ptr+1
+.f945 ca dex dex
+.f946 d0 f6 bne $f93e bne _loop
+.f948 60 rts rts
+
+
+.f949 gotoxy
+.f949 86 e3 stx $e3 stx cur_x
+.f94b 84 e4 sty $e4 sty cur_y
+
+ ; 80 = 64 + 16 = 0101 0000
+
+.f94d 64 e6 stz $e6 stz ptr+1
+.f94f 98 tya tya
+.f950 c0 3c cpy #$3c cpy #60
+.f952 90 02 bcc $f956 bcc _ok
+.f954 a0 3b ldy #$3b ldy #59
+.f956 _ok
+.f956 0a asl a asl a ; x2
+.f957 0a asl a asl a ; x4
+.f958 26 e6 rol $e6 rol ptr+1
+.f95a 65 e4 adc $e4 adc cur_y ; x5
+.f95c 90 02 bcc $f960 bcc _nc
+.f95e e6 e6 inc $e6 inc ptr+1
+.f960 0a asl a _nc asl a ; x10
+.f961 26 e6 rol $e6 rol ptr+1
+.f963 0a asl a asl a ; x20
+.f964 26 e6 rol $e6 rol ptr+1
+.f966 0a asl a asl a ; x40
+.f967 26 e6 rol $e6 rol ptr+1
+.f969 0a asl a asl a ; x80
+.f96a 26 e6 rol $e6 rol ptr+1
+.f96c 85 e5 sta $e5 sta ptr+0
+
+.f96e a5 e6 lda $e6 lda ptr+1
+.f970 69 c0 adc #$c0 adc #$c0
+.f972 85 e6 sta $e6 sta ptr+1
+
+ ; Save/restore the state of the i/o bit
+ ; while moving the cursor.
+.f974 a5 01 lda $01 lda $1
+.f976 48 pha pha
+.f977 a9 02 lda #$02 lda #2
+.f979 85 01 sta $01 sta $1
+.f97b 20 82 f9 jsr $f982 jsr cursor
+.f97e 68 pla pla
+.f97f 85 01 sta $01 sta $1
+.f981 60 rts rts
+
+.f982 cursor
+.f982 a4 e3 ldy $e3 ldy cur_x
+
+.f984 a2 03 ldx #$03 ldx #3 ; color memory
+.f986 86 01 stx $01 stx $1
+.f988 b1 e5 lda ($e5),y lda (ptr),y
+.f98a 64 01 stz $01 stz $1
+.f98c 8d 13 d0 sta $d013 sta VKY_TXT_CURSOR_COLR_REG
+
+.f98f a2 02 ldx #$02 ldx #2 ; text memory
+.f991 86 01 stx $01 stx $1
+.f993 b1 e5 lda ($e5),y lda (ptr),y
+.f995 49 80 eor #$80 eor #$80
+.f997 64 01 stz $01 stz $1
+.f999 8d 12 d0 sta $d012 sta VKY_TXT_CURSOR_CHAR_REG
+
+.f99c a5 e3 lda $e3 lda cur_x
+.f99e 8d 14 d0 sta $d014 sta VKY_TXT_CURSOR_X_REG_L
+.f9a1 9c 15 d0 stz $d015 stz VKY_TXT_CURSOR_X_REG_H
+
+.f9a4 a5 e4 lda $e4 lda cur_y
+.f9a6 8d 16 d0 sta $d016 sta VKY_TXT_CURSOR_Y_REG_L
+.f9a9 9c 17 d0 stz $d017 stz VKY_TXT_CURSOR_Y_REG_H
+
+.f9ac a9 0b lda #$0b lda #Vky_Cursor_Enable | Vky_Cursor_Flash_Rate0 | 8
+.f9ae 8d 10 d0 sta $d010 sta VKY_TXT_CURSOR_CTRL_REG
+.f9b1 9c 11 d0 stz $d011 stz VKY_TXT_START_ADD_PTR
+
+.f9b4 a2 02 ldx #$02 ldx #2
+.f9b6 86 01 stx $01 stx $1
+
+.f9b8 60 rts rts
+
+
+.f9b9 puts
+.f9b9 putc
+.f9b9 48 pha pha
+.f9ba da phx phx
+.f9bb 5a phy phy
+
+.f9bc a6 01 ldx $01 ldx $1
+.f9be da phx phx
+
+.f9bf a2 02 ldx #$02 ldx #2
+.f9c1 86 01 stx $01 stx $1
+
+.f9c3 20 d4 f9 jsr $f9d4 jsr _putc
+.f9c6 a6 e3 ldx $e3 ldx cur_x
+.f9c8 a4 e4 ldy $e4 ldy cur_y
+.f9ca 20 49 f9 jsr $f949 jsr gotoxy
+
+.f9cd fa plx plx
+.f9ce 86 01 stx $01 stx $1
+
+.f9d0 7a ply ply
+.f9d1 fa plx plx
+.f9d2 68 pla pla
+.f9d3 60 rts rts
+
+.f9d4 _putc
+
+.f9d4 a6 e3 ldx $e3 ldx cur_x
+.f9d6 a4 e4 ldy $e4 ldy cur_y
+.f9d8 48 pha pha
+.f9d9 20 49 f9 jsr $f949 jsr gotoxy ; Init line ptr; TODO: just init line ptr
+.f9dc 68 pla pla
+
+.f9dd c9 20 cmp #$20 cmp #$20
+.f9df 90 07 bcc $f9e8 bcc _ctrl
+
+.f9e1 c9 80 cmp #$80 cmp #$80
+.f9e3 b0 3a bcs $fa1f bcs cbm
+
+.f9e5 4c ba fa jmp $faba jmp insert
+
+.f9e8 _ctrl
+.f9e8 c9 05 cmp #$05 cmp #5 ; CBM white; occludes ^e->eol below
+.f9ea f0 33 beq $fa1f beq cbm
+.f9ec c9 11 cmp #$11 cmp #17
+.f9ee 90 07 bcc $f9f7 bcc _indexed
+.f9f0 c9 1b cmp #$1b cmp #27 ; esc
+.f9f2 d0 2b bne $fa1f bne cbm
+.f9f4 4c 63 fa jmp $fa63 jmp esc
+
+.f9f7 _indexed
+.f9f7 0a asl a asl a
+.f9f8 aa tax tax
+.f9f9 7c fc f9 jmp ($f9fc,x) jmp (_table,x)
+.f9fc _table
+>f9fc 1e fa .word ignore ; Key is only for documentation.
+>f9fe 91 fa .word begin ; Key is only for documentation.
+>fa00 6c fa .word left ; Key is only for documentation.
+>fa02 1e fa .word ignore ; Key is only for documentation.
+>fa04 1e fa .word ignore ; Key is only for documentation.
+>fa06 94 fa .word end ; Key is only for documentation.
+>fa08 73 fa .word right ; Key is only for documentation.
+>fa0a ad fa .word bell ; Key is only for documentation.
+>fa0c a3 fa .word backspace ; Key is only for documentation.
+>fa0e ae fa .word tab ; Key is only for documentation.
+>fa10 53 fa .word lf ; Key is only for documentation.
+>fa12 d0 fa .word kill ; Key is only for documentation.
+>fa14 64 fa .word ff ; Key is only for documentation.
+>fa16 54 fa .word cr ; Key is only for documentation.
+>fa18 84 fa .word down ; Key is only for documentation.
+>fa1a 1e fa .word ignore ; Key is only for documentation.
+>fa1c 7c fa .word up ; Key is only for documentation.
+
+.fa1e 60 rts ignore rts
+
+ ctrl .macro key, function
+ .endm
+
+.fa1f cbm
+ ; Slower, but I can far-jump
+.fa1f a2 00 ldx #$00 ldx #0
+.fa21 dd 33 fa cmp $fa33,x _loop cmp _table,x
+.fa24 f0 0a beq $fa30 beq _found
+.fa26 e8 inx inx
+.fa27 e8 inx inx
+.fa28 e8 inx inx
+.fa29 e0 18 cpx #$18 cpx #_end
+.fa2b d0 f4 bne $fa21 bne _loop
+.fa2d 4c f3 fa jmp $faf3 jmp set_color ; maybe it's a color command
+
+.fa30 7c 34 fa jmp ($fa34,x) _found jmp (_table+1,x)
+
+.fa33 _table
+>fa33 11 .byte $11
+>fa34 84 fa .word down
+>fa36 12 .byte $12
+>fa37 4b fa .word reverse
+>fa39 13 .byte $13
+>fa3a 67 fa .word home
+>fa3c 14 .byte $14
+>fa3d a3 fa .word backspace
+>fa3f 1d .byte $1d
+>fa40 73 fa .word right
+>fa42 91 .byte $91
+>fa43 53 fa .word lf
+>fa45 92 .byte $92
+>fa46 50 fa .word unreverse
+>fa48 93 .byte $93
+>fa49 14 f9 .word cls
+=24 _end = * - _table
+
+ entry .macro code, function
+ .endm
+
+.fa4b reverse
+.fa4b a2 80 ldx #$80 ldx #128
+.fa4d 86 e8 stx $e8 stx rev
+.fa4f 60 rts rts
+
+.fa50 unreverse
+.fa50 64 e8 stz $e8 stz rev
+.fa52 60 rts rts
+
+.fa53 60 rts lf rts
+
+.fa54 cr
+.fa54 64 e3 stz $e3 stz cur_x
+.fa56 _lf
+.fa56 a4 e4 ldy $e4 ldy cur_y
+.fa58 c8 iny iny
+.fa59 c0 3c cpy #$3c cpy #ROWS
+.fa5b d0 03 bne $fa60 bne _out
+.fa5d 4c dc fa jmp $fadc jmp scroll
+.fa60 _out
+.fa60 84 e4 sty $e4 sty cur_y
+.fa62 60 rts rts
+
+.fa63 esc
+.fa63 60 rts rts ; TODO: vt100 sequences
+
+.fa64 ff
+.fa64 4c 14 f9 jmp $f914 jmp cls
+
+.fa67 home
+.fa67 64 e3 stz $e3 stz cur_x
+.fa69 64 e4 stz $e4 stz cur_y
+.fa6b 60 rts rts
+
+.fa6c left
+.fa6c a5 e3 lda $e3 lda cur_x
+.fa6e f0 02 beq $fa72 beq _out
+.fa70 c6 e3 dec $e3 dec cur_x
+.fa72 60 rts _out rts
+
+.fa73 right
+.fa73 a5 e3 lda $e3 lda cur_x
+.fa75 c9 4f cmp #$4f cmp #COLS-1
+.fa77 b0 02 bcs $fa7b bcs _out
+.fa79 e6 e3 inc $e3 inc cur_x
+.fa7b 60 rts _out rts
+
+.fa7c up
+.fa7c a5 e4 lda $e4 lda cur_y
+.fa7e 3a dec a dec a
+.fa7f 30 02 bmi $fa83 bmi _out
+.fa81 85 e4 sta $e4 sta cur_y
+.fa83 60 rts _out rts
+
+.fa84 down
+.fa84 a5 e4 lda $e4 lda cur_y
+.fa86 1a inc a inc a
+.fa87 c9 3c cmp #$3c cmp #ROWS
+.fa89 d0 03 bne $fa8e bne _okay
+.fa8b 4c dc fa jmp $fadc jmp scroll
+.fa8e 85 e4 sta $e4 _okay sta cur_y
+.fa90 60 rts rts
+
+.fa91 begin
+.fa91 64 e3 stz $e3 stz cur_x
+.fa93 60 rts rts
+
+.fa94 end
+.fa94 a0 50 ldy #$50 ldy #COLS
+.fa96 88 dey _loop dey
+.fa97 f0 06 beq $fa9f beq _done
+.fa99 b1 e5 lda ($e5),y lda (ptr),y
+.fa9b c9 20 cmp #$20 cmp #32
+.fa9d f0 f7 beq $fa96 beq _loop
+.fa9f 84 e3 sty $e3 _done sty cur_x
+.faa1 80 d0 bra $fa73 bra right
+
+.faa3 backspace
+.faa3 20 6c fa jsr $fa6c jsr left
+.faa6 a4 e3 ldy $e3 ldy cur_x
+.faa8 a9 20 lda #$20 lda #32
+.faaa 91 e5 sta ($e5),y sta (ptr),y
+.faac 60 rts rts
+
+.faad bell
+.faad 60 rts rts ; TODO: flash or support sound
+
+.faae tab
+.faae a9 20 lda #$20 lda #32
+.fab0 20 ba fa jsr $faba jsr insert
+.fab3 a5 e3 lda $e3 lda cur_x
+.fab5 29 03 and #$03 and #TABS-1
+.fab7 d0 f5 bne $faae bne tab
+.fab9 60 rts rts
+
+.faba insert
+ ; ASCII for the rest
+ ; Someone else can do PETSCII
+.faba a4 e3 ldy $e3 ldy cur_x
+.fabc 05 e8 ora $e8 ora rev
+.fabe 91 e5 sta ($e5),y sta (ptr),y
+.fac0 e6 01 inc $01 inc $1
+.fac2 a5 e7 lda $e7 lda color
+.fac4 91 e5 sta ($e5),y sta (ptr),y
+.fac6 c6 01 dec $01 dec $1
+.fac8 c8 iny iny
+.fac9 c0 50 cpy #$50 cpy #COLS
+.facb f0 87 beq $fa54 beq cr
+.facd 84 e3 sty $e3 sty cur_x
+.facf 60 rts _done rts
+
+.fad0 kill
+.fad0 a4 e3 ldy $e3 ldy cur_x
+.fad2 a9 20 lda #$20 lda #32
+.fad4 91 e5 sta ($e5),y _loop sta (ptr),y
+.fad6 c8 iny iny
+.fad7 c0 50 cpy #$50 cpy #COLS
+.fad9 d0 f9 bne $fad4 bne _loop
+.fadb 60 rts rts
+
+.fadc scroll
+.fadc a9 c0 lda #$c0 lda #$c0
+.fade 85 de sta $de sta src+1
+.fae0 85 e0 sta $e0 sta dest+1
+
+.fae2 a9 50 lda #$50 lda #80
+.fae4 85 dd sta $dd sta src
+.fae6 64 df stz $df stz dest
+
+.fae8 a9 c0 lda #$c0 lda #COLS*(ROWS)
+.faee 85 e2 sta $e2 sta count+1
+
+.faf0 4c f0 f8 jmp $f8f0 jmp long_move
+
+.faf3 set_color
+.faf3 a2 00 ldx #$00 ldx #0
+.faf5 dd 0a fb cmp $fb0a,x _loop cmp _table,x
+.faf8 f0 06 beq $fb00 beq _found
+.fafa e8 inx inx
+.fafb e0 10 cpx #$10 cpx #_end
+.fafd d0 f6 bne $faf5 bne _loop
+.faff 60 rts rts
+.fb00 _found
+.fb00 8a txa txa
+.fb01 0a asl a asl a
+.fb02 0a asl a asl a
+.fb03 0a asl a asl a
+.fb04 0a asl a asl a
+.fb05 09 06 ora #$06 ora #6
+.fb07 85 e7 sta $e7 sta color
+.fb09 60 rts rts
+.fb0a _table
+>fb0a 90 .byte $90 ; color.black
+>fb0b 05 .byte $05 ; color.white
+>fb0c 1c .byte $1c ; color.red
+>fb0d 9f .byte $9f ; color.cyan
+>fb0e 9c .byte $9c ; color.violet
+>fb0f 1e .byte $1e ; color.green
+>fb10 1f .byte $1f ; color.blue
+>fb11 9e .byte $9e ; color.yellow
+>fb12 81 .byte $81 ; color.orange
+>fb13 95 .byte $95 ; color.brown
+>fb14 96 .byte $96 ; color.lred
+>fb15 97 .byte $97 ; color.grey1
+>fb16 98 .byte $98 ; color.grey2
+>fb17 99 .byte $99 ; color.lgreen
+>fb18 9a .byte $9a ; color.blue
+>fb19 9b .byte $9b ; color.grey3
+=16 _end = * - _table
+
+ .send
+ .endn
+ .endn
+
+
+;****** Processing input file: platform/jr/c64kbd.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; Driver for a VIC20/C264 keyboard connected to the 6522 port.
+
+ .cpu "w65c02"
+
+ .namespace platform
+ c64kbd .namespace
+
+
+=$dc01 PRA = $dc01 ; CIA#1 (Port Register A)
+=$dc03 DDRA = $dc03 ; CIA#1 (Data Direction Register A)
+
+=$dc00 PRB = $dc00 ; CIA#1 (Port Register B)
+=$dc02 DDRB = $dc02 ; CIA#1 (Data Direction Register B)
+
+ .section kmem
+>03aa mask .byte ? ; Copy of PRA output
+>03ab hold .byte ? ; Copy of PRB during processing
+>03ac bitno .byte ? ; # of the col bit being processed
+ .send
+
+ .section dp ; So we can branch on bits :).
+>00e9 state: .fill 8
+ .send
+
+ .section kernel
+
+.fb1a init:
+.fb1a 64 01 stz $01 stz $1
+
+.fb1c a9 ff lda #$ff lda #$ff ; CIA#1 port A = outputs
+.fb1e 8d 03 dc sta $dc03 sta DDRA
+
+.fb21 a9 00 lda #$00 lda #$00 ; CIA#1 port B = inputs
+.fb23 8d 02 dc sta $dc02 sta DDRB
+
+ ; Init the roll-table
+.fb26 a9 ff lda #$ff lda #$ff ; no key grounded
+.fb28 a2 07 ldx #$07 ldx #7
+.fb2a 95 e9 sta $e9,x _loop sta state,x
+.fb2c ca dex dex
+.fb2d 10 fb bpl $fb2a bpl _loop
+.fb2f 60 rts rts
+
+.fb30 scan
+.fb30 64 01 stz $01 stz $1
+.fb32 a9 7f lda #$7f lda #$7f
+.fb34 a2 00 ldx #$00 ldx #0
+
+.fb36 _loop
+.fb36 8d 01 dc sta $dc01 sta PRA
+.fb39 8d aa 03 sta $03aa sta mask
+
+.fb3c ad 00 dc lda $dc00 lda PRB
+.fb3f 8d ab 03 sta $03ab sta hold
+.fb42 55 e9 eor $e9,x eor state,x
+.fb44 f0 03 beq $fb49 beq _next
+
+.fb46 20 53 fb jsr $fb53 jsr report
+
+.fb49 _next
+.fb49 e8 inx inx
+.fb4a ad aa 03 lda $03aa lda mask
+.fb4d 4a lsr a lsr a
+.fb4e 09 80 ora #$80 ora #$80
+.fb50 b0 e4 bcs $fb36 bcs _loop
+.fb52 60 rts rts
+
+
+.fb53 report
+
+ ; Current state doesn't match last state.
+ ; Walk the bits and report any new keys.
+
+.fb53 _loop ; Process any bits that differ between PRB and state,x
+
+ ; Y->next diff bit to check
+.fb53 a8 tay tay
+.fb54 b9 00 e5 lda $e500,y lda irq.first_bit,y
+.fb57 8d ac 03 sta $03ac sta bitno
+.fb5a a8 tay tay
+
+ ; Clear the current state for this bit
+.fb5b b9 c8 f5 lda $f5c8,y lda irq.bit,y ; 'A' contains a single diff-bit
+.fb5e 49 ff eor #$ff eor #$ff
+.fb60 35 e9 and $e9,x and state,x
+.fb62 95 e9 sta $e9,x sta state,x
+
+ ; Report key and update the state
+.fb64 b9 c8 f5 lda $f5c8,y lda irq.bit,y ; 'A' contains a single diff-bit
+.fb67 2d ab 03 and $03ab and hold ; Get the state of this specific bit
+.fb6a d0 05 bne $fb71 bne _save ; Key is released; no action.
+.fb6c 48 pha pha
+.fb6d 20 7d fb jsr $fb7d jsr _report ; Key is pressed; report it.
+.fb70 68 pla pla
+.fb71 _save
+ ; Save the state of the bit
+.fb71 15 e9 ora $e9,x ora state,x
+.fb73 95 e9 sta $e9,x sta state,x
+.fb75 _next
+.fb75 ad ab 03 lda $03ab lda hold
+.fb78 55 e9 eor $e9,x eor state,x
+.fb7a d0 d7 bne $fb53 bne _loop
+
+.fb7c 60 rts _done rts
+
+.fb7d _report
+ ; A = row #
+.fb7d 8a txa txa ; Row #
+
+ ; Bit numbers are the reverse of
+ ; the table order, so advance one
+ ; row and then "back up" by bitno.
+.fb7e 1a inc a inc a
+
+ ; A = table offset for row
+.fb7f 0a asl a asl a
+.fb80 0a asl a asl a
+.fb81 0a asl a asl a
+
+ ; A = table entry for key
+.fb82 ed ac 03 sbc $03ac sbc bitno
+
+ ; Y-> table entry
+.fb85 a8 tay tay
+
+.fb86 b9 aa fb lda $fbaa,y lda keytab,y
+.fb89 c9 5f cmp #$5f cmp #'_'
+.fb8b f0 1c beq $fba9 beq _out ; Don't report meta keys
+
+.fb8d 2f e9 0e bbr 2,$e9,$fb9e bbr 2,state+0,_ctrl ; CTRL
+.fb90 4f ea 0f bbr 4,$ea,$fba2 bbr 4,state+1,_shift ; RSHIFT
+.fb93 7f ef 0c bbr 7,$ef,$fba2 bbr 7,state+6,_shift ; LSHIFT
+
+.fb96 df e9 02 bbs 5,$e9,$fb9b _alt bbs 5,state+0,_queue ; C= (ALT)
+.fb99 09 80 ora #$80 ora #$80
+
+.fb9b _queue
+.fb9b 4c b1 ec jmp $ecb1 jmp kernel.keyboard.enque
+
+.fb9e _ctrl
+.fb9e 29 1f and #$1f and #$1f
+.fba0 80 f4 bra $fb96 bra _alt
+
+.fba2 _shift
+.fba2 b9 ea fb lda $fbea,y lda shift,y
+.fba5 c9 5f cmp #$5f cmp #'_'
+.fba7 d0 ed bne $fb96 bne _alt
+
+.fba9 60 rts _out rts
+
+ .enc "none"
+>fbaa 5f 71 5f 20 32 5f 60 31 keytab: .text "_q_ 2_`1"
+>fbb2 2f 5e 3d 5f 5f 3b 2a 7e .text "/^=__;*~"
+>fbba 2c 40 3a 2e 2d 6c 70 2b .text ",@:.-lp+"
+>fbc2 6e 6f 6b 6d 30 6a 69 39 .text "nokm0ji9"
+>fbca 76 75 68 62 38 67 79 37 .text "vuhb8gy7"
+>fbd2 78 74 66 63 36 64 72 35 .text "xtfc6dr5"
+>fbda 5f 65 73 7a 34 61 77 33 .text "_esz4aw3"
+>fbe2 0e 85 83 81 87 06 0d 08 .byte 'N'-64, 5+128, 3+128, 1+128, 7+128, 'F'-64, 13, 8
+
+>fbea 5f 51 5f 20 22 5f 7e 21 shift: .text "_Q_ ",34,"_~!"
+>fbf2 3f 5e 3d 5f 5f 5d 2a 7e .text "?^=__]*~"
+>fbfa 3c 40 5b 3e 2d 4c 50 2b .text "<@[>-LP+"
+>fc02 4e 4f 4b 4d 30 4a 49 29 .text "NOKM0JI)"
+>fc0a 56 55 48 42 28 47 59 27 .text "VUHB(GY'"
+>fc12 58 54 46 43 26 44 52 25 .text "XTFC&DR%"
+>fc1a 5f 45 53 5a 24 41 57 23 .text "_ESZ$AW#"
+>fc22 10 85 83 81 87 02 0d 08 .byte 'P'-64, 5+128, 3+128, 1+128, 7+128, 'B'-64, 13, 8
+
+ .send
+ .endn
+ .endn
+
+
+;****** Processing input file: platform/jr/FPGA/TinyVicky_Def.asm
+
+ ;Internal Tiny VICKY Registers and Internal Memory Locations (LUTs)
+ ; IO Page 0
+=$d000 MASTER_CTRL_REG_L = $D000
+ ;Control Bits Fields
+=$01 Mstr_Ctrl_Text_Mode_En = $01 ; Enable the Text Mode
+=$02 Mstr_Ctrl_Text_Overlay = $02 ; Enable the Overlay of the text mode on top of Graphic Mode (the Background Color is ignored)
+=$04 Mstr_Ctrl_Graph_Mode_En = $04 ; Enable the Graphic Mode
+=$08 Mstr_Ctrl_Bitmap_En = $08 ; Enable the Bitmap Module In Vicky
+=$10 Mstr_Ctrl_TileMap_En = $10 ; Enable the Tile Module in Vicky
+=$20 Mstr_Ctrl_Sprite_En = $20 ; Enable the Sprite Module in Vicky
+=$40 Mstr_Ctrl_GAMMA_En = $40 ; this Enable the GAMMA correction - The Analog and DVI have different color value, the GAMMA is great to correct the difference
+=$80 Mstr_Ctrl_Disable_Vid = $80 ; This will disable the Scanning of the Video hence giving 100% bandwith to the CPU
+=$d001 MASTER_CTRL_REG_H = $D001
+ ; Reserved - TBD
+=$d002 VKY_RESERVED_00 = $D002
+=$d003 VKY_RESERVED_01 = $D003
+ ;
+=$d004 BORDER_CTRL_REG = $D004 ; Bit[0] - Enable (1 by default) Bit[4..6]: X Scroll Offset ( Will scroll Left) (Acceptable Value: 0..7)
+=$01 Border_Ctrl_Enable = $01
+=$d005 BORDER_COLOR_B = $D005
+=$d006 BORDER_COLOR_G = $D006
+=$d007 BORDER_COLOR_R = $D007
+=$d008 BORDER_X_SIZE = $D008; X- Values: 0 - 32 (Default: 32)
+=$d009 BORDER_Y_SIZE = $D009; Y- Values 0 -32 (Default: 32)
+ ; Reserved - TBD
+=$d00a VKY_RESERVED_02 = $D00A
+=$d00b VKY_RESERVED_03 = $D00B
+=$d00c VKY_RESERVED_04 = $D00C
+ ; Valid in Graphics Mode Only
+=$d00d BACKGROUND_COLOR_B = $D00D ; When in Graphic Mode, if a pixel is "0" then the Background pixel is chosen
+=$d00e BACKGROUND_COLOR_G = $D00E
+=$d00f BACKGROUND_COLOR_R = $D00F ;
+ ; Cursor Registers
+=$d010 VKY_TXT_CURSOR_CTRL_REG = $D010 ;[0] enable [1..2] flash rate [3] no flash
+=$01 Vky_Cursor_Enable = $01
+=$02 Vky_Cursor_Flash_Rate0 = $02
+=$04 Vky_Cursor_Flash_Rate1 = $04
+=$08 Vky_Cursor_No_Flash = $08
+=$d011 VKY_TXT_START_ADD_PTR = $D011 ; This is an offset to change the Starting address of the Text Mode Buffer (in x)
+=$d012 VKY_TXT_CURSOR_CHAR_REG = $D012
+=$d013 VKY_TXT_CURSOR_COLR_REG = $D013
+=$d014 VKY_TXT_CURSOR_X_REG_L = $D014
+=$d015 VKY_TXT_CURSOR_X_REG_H = $D015
+=$d016 VKY_TXT_CURSOR_Y_REG_L = $D016
+=$d017 VKY_TXT_CURSOR_Y_REG_H = $D017
+ ; Line Interrupt
+=$d018 VKY_LINE_IRQ_CTRL_REG = $D018 ;[0] - Enable Line 0 - WRITE ONLY
+=$d019 VKY_LINE_CMP_VALUE_LO = $D019 ;Write Only [7:0]
+=$d01a VKY_LINE_CMP_VALUE_HI = $D01A ;Write Only [3:0]
+
+=$d018 VKY_PIXEL_X_POS_LO = $D018 ; This is Where on the video line is the Pixel
+=$d019 VKY_PIXEL_X_POS_HI = $D019 ; Or what pixel is being displayed when the register is read
+=$d01a VKY_LINE_Y_POS_LO = $D01A ; This is the Line Value of the Raster
+=$d01b VKY_LINE_Y_POS_HI = $D01B ;
+
+;****** Processing input file: platform/jr/FPGA/interrupt_def.asm
+
+ ; Pending Interrupt (Read and Write Back to Clear)
+=$d660 INT_PENDING_REG0 = $D660 ;
+=$d661 INT_PENDING_REG1 = $D661 ;
+=$d662 INT_PENDING_REG2 = $D662 ; NOT USED
+=$d663 INT_PENDING_REG3 = $D663 ; NOT USED
+ ; Polarity Set
+=$d664 INT_POL_REG0 = $D664 ;
+=$d665 INT_POL_REG1 = $D665 ;
+=$d666 INT_POL_REG2 = $D666 ; NOT USED
+=$d667 INT_POL_REG3 = $D667 ; NOT USED
+ ; Edge Detection Enable
+=$d668 INT_EDGE_REG0 = $D668 ;
+=$d669 INT_EDGE_REG1 = $D669 ;
+=$d66a INT_EDGE_REG2 = $D66A ; NOT USED
+=$d66b INT_EDGE_REG3 = $D66B ; NOT USED
+ ; Mask
+=$d66c INT_MASK_REG0 = $D66C ;
+=$d66d INT_MASK_REG1 = $D66D ;
+=$d66e INT_MASK_REG2 = $D66E ; NOT USED
+=$d66f INT_MASK_REG3 = $D66F ; NOT USED
+ ; Interrupt Bit Definition
+ ; Register Block 0
+=$01 JR0_INT00_SOF = $01 ;Start of Frame @ 60FPS
+=$02 JR0_INT01_SOL = $02 ;Start of Line (Programmable)
+=$04 JR0_INT02_KBD = $04 ;
+=$08 JR0_INT03_MOUSE = $08 ;
+=$10 JR0_INT04_TMR0 = $10 ;
+=$20 JR0_INT05_TMR1 = $20 ;Real-Time Clock Interrupt
+=$40 JR0_INT06_DMA = $40 ;Floppy Disk Controller
+=$80 JR0_INT07_TBD = $80 ; Mouse Interrupt (INT12 in SuperIO IOspace)
+ ; Register Block 1
+=$01 JR1_INT00_UART = $01 ;Keyboard Interrupt
+=$02 JR1_INT01_COL0 = $02 ;TYVKY Collision TBD
+=$04 JR1_INT02_COL1 = $04 ;TYVKY Collision TBD
+=$08 JR1_INT03_COL2 = $08 ;TYVKY Collision TBD
+=$10 JR1_INT04_RTC = $10 ;Serial Port 1
+=$20 JR1_INT05_VIA = $20 ;Midi Controller Interrupt
+=$40 JR1_INT06_IEC = $40 ;Parallel Port
+=$80 JR1_INT07_SDCARD = $80 ;SDCard Insert
+
+
+;****** Processing input file: hardware/hardware.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ hardware .namespace
+ .endn
+
+
+
+;****** Processing input file: hardware/i8042.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; This file is from the w6502c TinyCore kernel by the same author.
+
+ .cpu "w65c02"
+
+ .namespace hardware
+
+ i8042 .macro BASE = $D640
+ .endm
+ .endn
+
+
+;****** Processing input file: hardware/ps2.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; This file borrows from the w6502c TinyCore kernel by the same author.
+
+ .cpu "w65c02"
+
+ .namespace hardware
+
+ ps2 .namespace
+
+ self .namespace
+ .virtual DevState
+>0500 dummy .word ? ; TODO: Crazy?
+>0502 upper .byte ? ; upper handler
+>0503 irq .byte ? ; IRQ vector
+>0504 port .byte ? ; PS2 port (0/1)
+>0505 state .byte ? ; State machine state.
+>0506 wait .byte ? ; starting tick
+>0507 rx1 .byte ? ; least recent auto-detect byte
+>0508 rx2 .byte ? ; middle auto-detect byte
+>0509 rx3 .byte ? ; most recent auto-detect byte
+ .endv
+ .endn
+
+ .section kernel
+
+.fc2a vectors
+>fc2a c6 fc .word dev_data
+>fc2c c4 fc .word dev_status
+>fc2e c4 fc .word dev_fetch
+>fc30 58 fc .word dev_open
+>fc32 c4 fc .word dev_get
+>fc34 c4 fc .word dev_set
+>fc36 c4 fc .word dev_send
+>fc38 be fc .word dev_close
+
+.fc3a init
+ ; A = upper, Y = IRQ
+ ; X <- initialized device, or carry set on error
+
+.fc3a 20 2a ed jsr $ed2a jsr kernel.device.alloc
+.fc3d b0 18 bcs $fc57 bcs _out
+
+.fc3f 9d 02 05 sta $0502,x sta self.upper,x
+.fc42 98 tya tya
+.fc43 9d 03 05 sta $0503,x sta self.irq,x
+
+.fc46 38 sec sec
+.fc47 e9 02 sbc #$02 sbc #irq.ps2_0
+.fc49 9d 04 05 sta $0504,x sta self.port,x
+
+.fc4c a9 2a lda #$2a lda #vectors
+.fc52 85 a6 sta $a6 sta kernel.src+1
+.fc54 20 47 ed jsr $ed47 jsr kernel.device.install
+
+.fc57 60 rts _out rts
+
+.fc58 dev_open
+
+.fc58 9e 07 05 stz $0507,x stz self.rx1,x
+.fc5b 9e 08 05 stz $0508,x stz self.rx2,x
+.fc5e 9e 09 05 stz $0509,x stz self.rx3,x
+.fc61 20 67 fd jsr $fd67 jsr hardware.kbd2.init
+
+ ; Wait for reset success
+.fc64 a9 00 lda #$00 lda #state.reset
+.fc66 9e 05 05 stz $0505,x stz self.state,x
+
+ ; Install our IRQ handler
+.fc69 8a txa txa
+.fc6a bc 03 05 ldy $0503,x ldy self.irq,x
+.fc6d 20 d0 f5 jsr $f5d0 jsr irq.install
+
+ ; Enable our IRQ
+.fc70 bd 03 05 lda $0503,x lda self.irq,x
+.fc73 20 d8 f5 jsr $f5d8 jsr irq.enable
+
+.fc76 da phx phx
+.fc77 8a txa txa
+.fc78 a8 tay tay
+
+ ; Enable the port and interrupts
+.fc79 5a phy phy
+.fc7a b9 02 05 lda $0502,y lda self.upper,y
+.fc7d aa tax tax
+.fc7e b9 04 05 lda $0504,y lda self.port,y
+.fc81 a8 tay tay
+.fc82 20 0f ed jsr $ed0f jsr kernel.device.set
+.fc85 7a ply ply
+.fc86 b0 24 bcs $fcac bcs _out
+
+ ; Send a reset
+.fc88 a9 ff lda #$ff lda #$ff ; Reset
+.fc8a 20 ae fc jsr $fcae jsr send_cmd
+.fc8d b0 1d bcs $fcac bcs _out
+
+ ; Wait for the state to change
+.fc8f a5 a3 lda $a3 lda kernel.ticks
+.fc91 99 06 05 sta $0506,y sta self.wait,y
+.fc94 b9 05 05 lda $0505,y _loop lda self.state,y
+.fc97 c9 00 cmp #$00 cmp #state.reset
+.fc99 18 clc clc
+.fc9a d0 10 bne $fcac bne _out
+.fc9c 20 f8 e6 jsr $e6f8 jsr kernel.thread.yield
+.fc9f a5 a3 lda $a3 lda kernel.ticks
+.fca1 38 sec sec
+.fca2 f9 06 05 sbc $0506,y sbc self.wait,y
+.fca5 c9 b4 cmp #$b4 cmp #60*3
+.fca7 90 eb bcc $fc94 bcc _loop
+
+ ; Ugh, no response ... just send some enables.
+.fca9 20 f9 fc jsr $fcf9 jsr report
+
+.fcac fa plx _out plx
+.fcad 60 rts rts
+
+.fcae send_cmd
+.fcae 5a phy phy
+.fcaf 48 pha pha
+
+.fcb0 b9 02 05 lda $0502,y lda self.upper,y
+.fcb3 aa tax tax
+
+.fcb4 b9 04 05 lda $0504,y lda self.port,y
+.fcb7 a8 tay tay
+
+.fcb8 68 pla pla
+.fcb9 20 12 ed jsr $ed12 jsr kernel.device.send
+.fcbc 7a ply ply
+
+.fcbd 60 rts rts
+
+.fcbe dev_close
+.fcbe bd 03 05 lda $0503,x lda self.irq,x
+.fcc1 4c fa f5 jmp $f5fa jmp irq.disable
+
+.fcc4 dev_get
+.fcc4 dev_set
+.fcc4 dev_send
+.fcc4 dev_status
+.fcc4 dev_fetch
+.fcc4 18 clc clc
+.fcc5 60 rts rts
+
+.fcc6 dev_data
+.fcc6 8a txa txa
+.fcc7 a8 tay tay
+
+ ; Spin the IRQ counter
+ .if false
+ .endif
+.fcc8 be 02 05 ldx $0502,y ldx self.upper,y
+.fccb 20 0c ed jsr $ed0c jsr kernel.device.get
+
+.fcce be 05 05 ldx $0505,y ldx self.state,y
+.fcd1 e0 0a cpx #$0a cpx #state.end
+.fcd3 90 02 bcc $fcd7 bcc _ok
+.fcd5 a2 00 ldx #$00 ldx #0
+.fcd7 _ok
+.fcd7 7c da fc jmp ($fcda,x) jmp (_table,x)
+
+.fcda _table .dstruct state
+>fcda e4 fc reset .word wait_reset
+>fcdc 03 fd ack .word wait_ack
+>fcde 12 fd data .word wait_data
+>fce0 60 fd kbd2 .word state_kbd2 ; Mode-2 keyboard state machine
+>fce2 66 fd mouse0 .word state_mouse0 ; Original 3-byte state machine
+.fce4 end .ends
+
+.0000 state .struct
+>0000 e4 fc reset .word wait_reset
+>0002 03 fd ack .word wait_ack
+>0004 12 fd data .word wait_data
+>0006 60 fd kbd2 .word state_kbd2 ; Mode-2 keyboard state machine
+>0008 66 fd mouse0 .word state_mouse0 ; Original 3-byte state machine
+.000a end .ends
+
+
+
+.fce4 wait_reset
+ ; Whatever we got, advance
+.fce4 4c f9 fc jmp $fcf9 jmp report
+
+
+.fce7 c9 aa cmp #$aa cmp #$aa ; self test passed
+.fce9 f0 0e beq $fcf9 beq report
+
+.fceb c9 fc cmp #$fc cmp #$fc
+.fced f0 09 beq $fcf8 beq _done ; Reset failed, wait for user.
+.fcef c9 fd cmp #$fd cmp #$fd
+.fcf1 f0 05 beq $fcf8 beq _done ; Reset failed, wait for user.
+
+.fcf3 a9 ff lda #$ff lda #$ff ; reset
+.fcf5 4c ae fc jmp $fcae jmp send_cmd ; Resend requested
+
+.fcf8 60 rts _done rts
+
+.fcf9 report
+.fcf9 a9 04 lda #$04 lda #state.data
+.fcfb 99 05 05 sta $0505,y sta self.state,y
+
+ ; Request reporting
+.fcfe a9 f4 lda #$f4 lda #$f4 ; Enable reporting
+.fd00 4c ae fc jmp $fcae jmp send_cmd
+
+.fd03 wait_ack
+.fd03 c9 fa cmp #$fa cmp #$fa
+.fd05 f0 05 beq $fd0c beq _next
+
+.fd07 a9 d4 lda #$d4 lda #$d4
+.fd09 4c ae fc jmp $fcae jmp send_cmd
+
+.fd0c a9 04 lda #$04 _next lda #state.data
+.fd0e 99 05 05 sta $0505,y sta self.state,y
+.fd11 60 rts rts
+
+.fd12 wait_data
+.fd12 80 03 bra $fd17 bra wait_auto
+
+.fd14 c9 aa cmp #$aa cmp #$aa
+ ;beq report ; Hot-plug event
+.fd16 60 rts rts
+
+
+
+.fd17 wait_auto
+.fd17 48 pha pha
+.fd18 b9 08 05 lda $0508,y lda self.rx2,b,y
+.fd1b 99 07 05 sta $0507,y sta self.rx1,b,y
+.fd1e b9 09 05 lda $0509,y lda self.rx3,b,y
+.fd21 99 08 05 sta $0508,y sta self.rx2,b,y
+.fd24 68 pla pla
+.fd25 99 09 05 sta $0509,y sta self.rx3,b,y
+
+ ; Mode2 keyboard?
+.fd28 b9 08 05 lda $0508,y _check2 lda self.rx2,b,y
+.fd2b c9 f0 cmp #$f0 cmp #$f0 ; release
+.fd2d d0 08 bne $fd37 bne _not2
+.fd2f b9 07 05 lda $0507,y lda self.rx1,b,y
+.fd32 d9 09 05 cmp $0509,y cmp self.rx3,b,y
+.fd35 f0 0b beq $fd42 beq _mode2
+.fd37 ea nop _not2 nop
+
+ ; Mouse?
+.fd38 b9 07 05 lda $0507,y lda self.rx1,b,y
+.fd3b 29 cf and #$cf and #$cf
+.fd3d 49 08 eor #$08 eor #$08
+.fd3f f0 19 beq $fd5a beq _mouse
+
+.fd41 60 rts _out rts
+
+.fd42 _mode2
+ ; Switch to the mode2 machine.
+.fd42 a9 06 lda #$06 lda #state.kbd2
+.fd44 99 05 05 sta $0505,y sta self.state,b,y
+
+ ; Forward the last keypress to the mode2 machine.
+.fd47 b9 07 05 lda $0507,y lda self.rx1,b,y
+.fd4a 20 60 fd jsr $fd60 jsr state_kbd2
+.fd4d b9 08 05 lda $0508,y lda self.rx2,b,y
+.fd50 20 60 fd jsr $fd60 jsr state_kbd2
+.fd53 b9 09 05 lda $0509,y lda self.rx3,b,y
+.fd56 20 60 fd jsr $fd60 jsr state_kbd2
+
+.fd59 60 rts rts
+
+.fd5a _mouse
+ ; Discard the detected movement;
+ ; switch to standard 3-byte mouse machine.
+.fd5a a9 08 lda #$08 lda #state.mouse0
+.fd5c 99 05 05 sta $0505,y sta self.state,b,y
+.fd5f 60 rts rts
+
+.fd60 state_kbd2
+ ; Replace with a local state machine.
+.fd60 5a phy phy
+.fd61 20 a7 fd jsr $fda7 jsr hardware.kbd2.accept
+.fd64 7a ply ply
+.fd65 60 rts rts
+
+.fd66 state_mouse0
+ ; Just drop the byte for now
+.fd66 60 rts rts
+
+
+ .send
+ .endn
+ .endn
+
+
+;****** Processing input file: hardware/ps2_kbd2.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; This file is from the w6502c TinyCore kernel by the same author.
+
+ .cpu "w65c02"
+
+ .namespace hardware
+
+ kbd2 .namespace
+
+ .section kmem
+>03ad e0 .byte ?
+>03ae release .byte ?
+>03af flags: .fill 16
+
+ .send
+
+ .section kernel
+
+.fd67 init
+.fd67 9c ad 03 stz $03ad stz e0
+.fd6a 9c ae 03 stz $03ae stz release
+.fd6d da phx phx
+.fd6e a2 00 ldx #$00 ldx #0
+.fd70 9e af 03 stz $03af,x _loop stz flags,x
+.fd73 e8 inx inx
+.fd74 e0 20 cpx #$20 cpx #32
+.fd76 d0 f8 bne $fd70 bne _loop
+.fd78 fa plx plx
+.fd79 18 clc clc
+.fd7a 60 rts rts
+
+.fd7b search
+ ; Map keys prefixed with $e0.
+.fd7b a2 00 ldx #$00 ldx #0
+.fd7d dd 8f fd cmp $fd8f,x _loop cmp _etab,x
+.fd80 f0 09 beq $fd8b beq _found
+.fd82 e8 inx inx
+.fd83 e8 inx inx
+.fd84 e0 18 cpx #$18 cpx #_end
+.fd86 d0 f5 bne $fd7d bne _loop
+.fd88 a9 00 lda #$00 lda #0 ; Ignore for the moment
+.fd8a 60 rts rts
+
+.fd8b bd 90 fd lda $fd90,x _found lda _etab+1,x
+.fd8e 60 rts rts
+
+.fd8f _etab
+>fd8f 14 13 .byte $14, RCTRL
+>fd91 6c 96 .byte $6c, HOME
+>fd93 70 98 .byte $70, INS
+>fd95 74 9c .byte $74, RIGHT
+>fd97 11 15 .byte $11, RALT
+>fd99 69 97 .byte $69, END
+>fd9b 71 04 .byte $71, DEL
+>fd9d 75 99 .byte $75, UP
+>fd9f 7d 94 .byte $7d, PUP
+>fda1 72 9a .byte $72, DOWN
+>fda3 7a 95 .byte $7a, PDN
+>fda5 6b 9b .byte $6b, LEFT
+=24 _end = * - _etab
+
+.fda7 accept:
+.fda7 c9 f0 cmp #$f0 cmp #$f0 ; Key release
+.fda9 f0 35 beq $fde0 beq _release
+
+.fdab c9 e0 cmp #$e0 cmp #$e0 ; Media/extended prefix
+.fdad f0 35 beq $fde4 beq _e0
+
+.fdaf c9 e1 cmp #$e1 cmp #$e1 ; Pause...
+.fdb1 f0 48 beq $fdfb beq _send
+
+.fdb3 c9 84 cmp #$84 cmp #$84 ; Apparently true
+.fdb5 b0 1d bcs $fdd4 bcs _drop
+
+.fdb7 ae ad 03 ldx $03ad ldx e0
+.fdba f0 05 beq $fdc1 beq _std
+.fdbc 20 7b fd jsr $fd7b jsr search
+.fdbf 80 04 bra $fdc5 bra _code
+.fdc1 _std
+.fdc1 aa tax tax
+.fdc2 bd 82 fe lda $fe82,x lda keymap,x
+
+.fdc5 ae ae 03 ldx $03ae _code ldx release
+.fdc8 f0 1e beq $fde8 beq _press
+
+ ; Clear mode flags
+.fdca aa tax tax
+.fdcb 29 f0 and #$f0 and #$f0
+.fdcd c9 10 cmp #$10 cmp #16
+.fdcf d0 03 bne $fdd4 bne _drop
+.fdd1 9e 9f 03 stz $039f,x stz flags-16,x
+
+.fdd4 9c ad 03 stz $03ad _drop stz e0
+.fdd7 9c ae 03 stz $03ae stz release
+.fdda 60 rts rts
+
+.fddb 20 7b fd jsr $fd7b _search jsr search
+.fdde 80 e5 bra $fdc5 bra _code
+
+
+.fde0 8d ae 03 sta $03ae _release sta release
+.fde3 60 rts rts
+
+.fde4 8d ad 03 sta $03ad _e0 sta e0,b
+.fde7 60 rts rts
+
+.fde8 20 d4 fd jsr $fdd4 _press jsr _drop
+.fdeb aa tax tax
+.fdec 29 f0 and #$f0 and #$f0
+.fdee c9 10 cmp #$10 cmp #16
+.fdf0 d0 04 bne $fdf6 bne _key
+.fdf2 9d 9f 03 sta $039f,x sta flags-16,x
+.fdf5 60 rts rts
+
+.fdf6 8a txa _key txa
+.fdf7 c9 00 cmp #$00 cmp #0
+.fdf9 f0 06 beq $fe01 beq _end
+.fdfb 20 02 fe jsr $fe02 _send jsr _flags
+
+ .if false
+ .endif
+.fdfe 20 b1 ec jsr $ecb1 jsr kernel.keyboard.enque
+
+.fe01 60 rts _end rts
+
+.fe02 48 pha _flags pha
+.fe03 a9 00 lda #$00 lda #0
+.fe05 a2 00 ldx #$00 ldx #0
+.fe07 bc af 03 ldy $03af,x _loop ldy flags,x
+.fe0a f0 03 beq $fe0f beq _next
+.fe0c 1d 1f fe ora $fe1f,x ora _meta,x
+.fe0f e8 inx _next inx
+.fe10 e0 09 cpx #$09 cpx #9
+.fe12 d0 f3 bne $fe07 bne _loop
+.fe14 ba tsx tsx
+.fe15 89 02 bit #$02 bit #2
+.fe17 d0 0f bne $fe28 bne _ctrl
+.fe19 89 01 bit #$01 bit #1
+.fe1b d0 15 bne $fe32 bne _shift
+.fe1d 68 pla _out pla
+.fe1e 60 rts rts
+>fe1f 01 01 02 02 04 04 08 08 _meta .byte 1,1,2,2,4,4,8,8,1
+>fe27 01
+.fe28 bd 01 01 lda $0101,x _ctrl lda Stack+1,x
+.fe2b 29 1f and #$1f and #$1f
+.fe2d 9d 01 01 sta $0101,x sta Stack+1,x
+.fe30 80 eb bra $fe1d bra _out
+.fe32 bd 01 01 lda $0101,x _shift lda Stack+1,x
+.fe35 20 3d fe jsr $fe3d jsr shift
+.fe38 9d 01 01 sta $0101,x sta Stack+1,x
+.fe3b 80 e0 bra $fe1d bra _out
+
+.fe3d shift
+.fe3d c9 61 cmp #$61 cmp #'a'
+.fe3f 90 07 bcc $fe48 bcc _find
+.fe41 c9 7b cmp #$7b cmp #'z'+1
+.fe43 b0 03 bcs $fe48 bcs _find
+.fe45 49 20 eor #$20 eor #$20
+.fe47 60 rts rts
+.fe48 _find
+.fe48 a0 00 ldy #$00 ldy #0
+.fe4a d9 5a fe cmp $fe5a,y _loop cmp _map,y
+.fe4d f0 07 beq $fe56 beq _found
+.fe4f c8 iny iny
+.fe50 c8 iny iny
+.fe51 c0 28 cpy #$28 cpy #_end
+.fe53 d0 f5 bne $fe4a bne _loop
+.fe55 60 rts rts
+.fe56 b9 5b fe lda $fe5b,y _found lda _map+1,y
+.fe59 60 rts rts
+.fe5a _map
+>fe5a 31 21 .byte '1', '!'
+>fe5c 32 40 .byte '2', '@'
+>fe5e 33 23 .byte '3', '#'
+>fe60 34 24 .byte '4', '$'
+>fe62 35 25 .byte '5', '%'
+>fe64 36 5e .byte '6', '^'
+>fe66 37 26 .byte '7', '&'
+>fe68 38 2a .byte '8', '*'
+>fe6a 39 28 .byte '9', '('
+>fe6c 30 29 .byte '0', ')'
+>fe6e 2d 5f .byte '-', '_'
+>fe70 3d 2b .byte '=', '+'
+
+>fe72 5b 7b .byte '[', '{'
+>fe74 5d 7d .byte ']', '}'
+>fe76 5c 7c .byte $5c, '|'
+
+>fe78 3b 3a .byte ';', ':'
+>fe7a 27 22 .byte $27, $22
+
+>fe7c 2c 3c .byte ',', '<'
+>fe7e 2e 3e .byte '.', '>'
+>fe80 2f 3f .byte '/', '?'
+
+=40 _end = * - _map
+
+
+
+.fe82 keymap:
+
+>fe82 00 89 00 85 83 81 82 8c .byte 0, F9, 0, F5, F3, F1, F2, F12
+>fe8a 00 8a 88 86 84 09 60 00 .byte 0, F10, F8, F6, F4, 9, '`', 0
+>fe92 00 14 10 00 12 71 31 00 .byte 0, LALT, LSHIFT, 0, LCTRL, 'q', '1', 0
+>fe9a 00 00 7a 73 61 77 32 00 .byte 0, 0, 'z', 's', 'a', 'w', '2', 0
+>fea2 00 63 78 64 65 34 33 00 .byte 0, 'c', 'x', 'd', 'e', '4', '3', 0
+>feaa 00 20 76 66 74 72 35 00 .byte 0, ' ', 'v', 'f', 't', 'r', '5', 0
+>feb2 00 6e 62 68 67 79 36 00 .byte 0, 'n', 'b', 'h', 'g', 'y', '6', 0
+>feba 00 00 6d 6a 75 37 38 00 .byte 0, 0, 'm', 'j', 'u', '7', '8', 0
+>fec2 00 2c 6b 69 6f 30 39 00 .byte 0, ',', 'k', 'i', 'o', '0', '9', 0
+>feca 00 2e 2f 6c 3b 70 2d 00 .byte 0, '.', '/', 'l', ';', 'p', '-', 0
+>fed2 00 00 27 00 5b 3d 00 00 .byte 0, 0, "'", 0, '[', '=', 0, 0
+>feda 18 11 0d 5d 00 5c 00 00 .byte CAPS, RSHIFT, RETURN, ']', 0, '\', 0, 0
+>fee2 00 00 00 00 00 00 08 00 .byte 0, 0, 0, 0, 0, 0, BKSP, 0
+>feea 00 a1 00 a4 a7 00 00 00 .byte 0, K1, 0, K4, K7, 0, 0, 0
+>fef2 a0 ae a2 a5 a6 a8 1b b0 .byte K0, KPOINT, K2, K5, K6, K8, ESC, NUM
+>fefa 8b aa a3 ab ac a9 9d 00 .byte F11, KPLUS, K3, KMINUS, KTIMES, K9, SCROLL, 0
+>ff02 00 00 00 87 9e 00 00 00 .byte 0, 0, 0, F7, SYSREQ, 0, 0, 0, 0
+>ff0a 00
+
+ .send
+ .endn
+ .endn
+
+
+;****** Processing input file: hardware/keys.asm
+
+ ; OpenKERNAL - a clean-room implementation of the C64's KERNAL ABI.
+ ; Copyright 2022 Jessie Oberreuter .
+ ; SPDX-License-Identifier: GPL-3.0-only
+
+ ; This file is from the w6502c TinyCore kernel by the same author.
+
+=16 LSHIFT = 16
+=17 RSHIFT = 17
+=18 LCTRL = 18
+=19 RCTRL = 19
+=20 LALT = 20
+=21 RALT = 21
+=22 LMETA = 22
+=23 RMETA = 23
+=24 CAPS = 24
+
+=$e1 PAUSE = $e1
+=129 F1 = 129
+=130 F2 = 130
+=131 F3 = 131
+=132 F4 = 132
+=133 F5 = 133
+=134 F6 = 134
+=135 F7 = 135
+=136 F8 = 136
+=137 F9 = 137
+=138 F10 = 138
+=139 F11 = 139
+=140 F12 = 140
+
+=148 PUP = 148
+=149 PDN = 149
+=150 HOME = 150
+=151 END = 151
+=152 INS = 152
+=153 UP = 153
+=154 DOWN = 154
+=155 LEFT = 155
+=156 RIGHT = 156
+=157 SCROLL = 157
+=158 SYSREQ = 158
+=159 BREAK = 159
+
+=160 K0 = 160
+=161 K1 = 161
+=162 K2 = 162
+=163 K3 = 163
+=164 K4 = 164
+=165 K5 = 165
+=166 K6 = 166
+=167 K7 = 167
+=168 K8 = 168
+=169 K9 = 169
+=170 KPLUS = 170
+=171 KMINUS = 171
+=172 KTIMES = 172
+=173 KDIV = 173
+=174 KPOINT = 174
+=175 KENTER = 175
+=176 NUM = 176
+
+=177 POWER = 177
+=178 SLEEP = 178
+=179 WAKE = 179
+=180 PRTSCR = 180
+=181 MENU = 181
+
+=4 DEL = 4
+=13 RETURN = 13
+=27 ESC = 27
+=9 TAB = 9
+=8 BKSP = 8
+
+
+
+;****** End of listing
diff --git a/Release Notes.txt b/Release Notes.txt
index 734894e..b76f08d 100644
--- a/Release Notes.txt
+++ b/Release Notes.txt
@@ -1,3 +1,12 @@
+Release 0.6.0.2
+---------------
+Fixed CPU reading incorrect page when MMU is present in F256Jr.
+Fixed LUT editing issue.
+Fixed Tileset stride issue.
+Removed MultimediaTimer and replaced with a native C# HiResTimer.
+Added sprite functionality for the F256Jr.
+In the Uploader window, when in F256Jr mode, the buffer size is 2K.
+
Release 0.6.0.1
---------------
Implemented tiles for F256Jr.
diff --git a/bin/Release/FoenixIDE.exe b/bin/Release/FoenixIDE.exe
index 246b974..3b5803d 100644
Binary files a/bin/Release/FoenixIDE.exe and b/bin/Release/FoenixIDE.exe differ