From 713b016595818d319efa1fac2e9553abb7eb5465 Mon Sep 17 00:00:00 2001
From: Threetwosevensixseven <robin.guest@gmail.com>
Date: Mon, 16 Dec 2024 17:26:42 -0500
Subject: [PATCH] UARTReplacement now implements port 0x153b reads. This allows
 programs which read, mask, modify and write that port to not accidentally
 switch between ESP and Pi UARTs.

---
 .../Properties/AssemblyInfoExtra.cs           |  2 +-
 UARTReplacement/SerialPort.cs                 | 28 +++++++++++++++++--
 UARTReplacement/UARTReplacement_Device.cs     | 12 ++++++++
 UARTReplacement/UARTTargets.cs                |  8 ++++--
 4 files changed, 44 insertions(+), 6 deletions(-)

diff --git a/UARTReplacement/Properties/AssemblyInfoExtra.cs b/UARTReplacement/Properties/AssemblyInfoExtra.cs
index cb29ab6..ee294a3 100644
--- a/UARTReplacement/Properties/AssemblyInfoExtra.cs
+++ b/UARTReplacement/Properties/AssemblyInfoExtra.cs
@@ -4,7 +4,7 @@
 
 using System.Reflection;
 
-[assembly: AssemblyConfiguration("04697a9")]
+[assembly: AssemblyConfiguration("7e20a6b")]
 [assembly: AssemblyCopyright("Copyright © 2019-2024 Robin Verhagen-Guest")]
 [assembly: AssemblyVersion("1.6.0.0")]
 [assembly: AssemblyFileVersion("1.6.0.0")]
diff --git a/UARTReplacement/SerialPort.cs b/UARTReplacement/SerialPort.cs
index 8bf6b17..8554e3a 100644
--- a/UARTReplacement/SerialPort.cs
+++ b/UARTReplacement/SerialPort.cs
@@ -21,6 +21,7 @@ public class SerialPort : IDisposable
         private bool enablePiGpio5Output = false;
         private UARTTargets target;
         private string logPrefix;
+        private byte prescalerTop3Bits = 0x00;
 
         /// <summary>
         /// Creates an instance of the SerialPort class.
@@ -135,6 +136,12 @@ public int SetPrescalerAndClock(byte BaudByte, byte VideoTimingByte)
             int oldBaud = Baud;
             try
             {
+                if ((BaudByte & 0x10) == 0x10)
+                {
+                    // Get bits 14..16 of the new prescaler
+                    prescalerTop3Bits = (byte)(BaudByte & 0b111);
+                    //Console.WriteLine(UARTReplacement_Device.PluginName + "Prescaler 17..14: " + ToBin3(prescalerTop3Bits));
+                }
                 if (port == null)
                     return 0;
                 int mode = VideoTimingByte & 7;
@@ -170,8 +177,7 @@ public int SetPrescalerAndClock(byte BaudByte, byte VideoTimingByte)
                 }
                 if ((BaudByte & 0x10) == 0x10)
                 {
-                    // Get bits 14..16 of the new prescaler
-                    int newBits = (BaudByte & 0x07) << 14;
+                    int newBits = prescalerTop3Bits << 14;
                     // Mask out everything of the existing prescaler except bits 14..16
                     int oldBits = prescaler & 0x3fff;
                     // Combine the two sets of bits
@@ -195,6 +201,13 @@ public int SetPrescalerAndClock(byte BaudByte, byte VideoTimingByte)
             }
         }
 
+        private string ToBin3(byte value)
+        {
+            string val = Convert.ToString(value, 2).PadLeft(8, '0').ToLower();
+            return "0b" + val.Substring(5);
+        }
+
+
         /// <summary>
         /// Given a raw byte written to PORT_UART_RX, parses the high bit to decide whether it represents a change
         /// to bits 6:0 or 13:7, and updates the prescaler accordingly.
@@ -264,6 +277,17 @@ public int Baud
             }
         }
 
+        /// <summary>
+        /// Returns the value for a PORT_UART_CONTROL (0x153b) read.
+        /// Bit 6 is 0 for ESP and 1 for Pi. Bits 2..0 are the top three bits of the prescaler.
+        /// All other bits are 0.
+        /// </summary>
+        /// <returns>The byte value to be returned by a PORT_UART_CONTROL (0x153b) read.</returns>
+        public byte GetUartControlValue()
+        {
+            return (byte)((int)target | prescalerTop3Bits);
+        }
+
         public void EspReset(byte ResetByte)
         {
             try
diff --git a/UARTReplacement/UARTReplacement_Device.cs b/UARTReplacement/UARTReplacement_Device.cs
index f056938..e0b6c0d 100644
--- a/UARTReplacement/UARTReplacement_Device.cs
+++ b/UARTReplacement/UARTReplacement_Device.cs
@@ -63,6 +63,7 @@ public List<sIO> Init(iCSpect _CSpect)
                 ports.Add(new sIO(PORT_UART_RX, eAccess.Port_Write));
                 ports.Add(new sIO(PORT_UART_RX, eAccess.Port_Read));
                 ports.Add(new sIO(PORT_UART_CONTROL, eAccess.Port_Write));
+                ports.Add(new sIO(PORT_UART_CONTROL, eAccess.Port_Read));
                 ports.Add(new sIO(REG_RESET, eAccess.NextReg_Write));
                 ports.Add(new sIO(REG_ESP_GPIO_ENABLE, eAccess.NextReg_Write));
                 ports.Add(new sIO(REG_ESP_GPIO, eAccess.NextReg_Write));
@@ -123,6 +124,7 @@ public bool Write(eAccess _type, int _port, int _id, byte _value)
                         // But just in case it can vary, let's do it. It doesn't seem to cause performance issues to do so.
                         var oldTarget = Target;
                         var target = (_value & 64) == 0 ? UARTTargets.ESP : UARTTargets.Pi;
+                        //Console.WriteLine(UARTReplacement_Device.PluginName + "OUT 0x153b, " + ToBin(_value));
                         Target = target;
                         if (oldTarget != Target)
                             Console.WriteLine(UARTReplacement_Device.PluginName + "Target changed to " + Target.ToString());
@@ -136,6 +138,7 @@ public bool Write(eAccess _type, int _port, int _id, byte _value)
                         return true;
                     case PORT_UART_RX:
                         // Writes to this port represent changes to the prescaler which cause the UART baud to change
+                        //Console.WriteLine(UARTReplacement_Device.PluginName + "OUT 0x143b, " + ToBin(_value));
                         currentPort.SetPrescaler(_value);
                         return true;
                     case REG_RESET:
@@ -183,6 +186,9 @@ public byte Read(eAccess _type, int _port, int _id, out bool _isvalid)
             {
                 switch (_port)
                 {
+                    case PORT_UART_CONTROL:
+                        _isvalid = true;
+                        return currentPort.GetUartControlValue();
                     case PORT_UART_RX:
                         if (UART_RX_Internal)
                         {
@@ -308,6 +314,12 @@ private object currentSync
                 return Target == UARTTargets.ESP ? espSync : piSync;
             }
         }
+
+        private string ToBin(byte value)
+        {
+            string val = Convert.ToString(value, 2).PadLeft(8, '0').ToLower();
+            return "0b" + val.Substring(0, 4) + "'" + val.Substring(4);
+        }
     }
 }
 
diff --git a/UARTReplacement/UARTTargets.cs b/UARTReplacement/UARTTargets.cs
index d1351f7..ec3d844 100644
--- a/UARTReplacement/UARTTargets.cs
+++ b/UARTReplacement/UARTTargets.cs
@@ -7,11 +7,13 @@
 namespace Plugins.UARTReplacement
 {
     /// <summary>
-    /// This represents the two different Next UARTs. Currently only the ESP UART is replaced by this plugin.
+    /// This represents the two different Next UARTs.
+    /// The numeric value determines what is returned from a port PORT_UART_CONTROL (0x153b) read.
+    /// Bits 17..15 of the prescaler value are ORed into the bottom three bits of this value.
     /// </summary>
     public enum UARTTargets
     {
-        ESP,
-        Pi
+        ESP = 0b0_0_000_000,
+        Pi = 0b0_1_000_000
     }
 }