diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3e1967b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+**/bin/Debug/*
+**/obj/Debug/*
+**/bin/Release/*
+**/obj/Release/*
+firmware/bin/*
+patch/bin/*
+patch/equates.h
+tools/*
diff --git a/DriveCom/DriveCom.sln b/DriveCom/DriveCom.sln
new file mode 100644
index 0000000..be504e7
--- /dev/null
+++ b/DriveCom/DriveCom.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Express 2012 for Windows Desktop
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DriveCom", "DriveCom\DriveCom.csproj", "{25CF4003-4DF8-4F3C-B9B5-50252C2CD2C2}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {25CF4003-4DF8-4F3C-B9B5-50252C2CD2C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {25CF4003-4DF8-4F3C-B9B5-50252C2CD2C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {25CF4003-4DF8-4F3C-B9B5-50252C2CD2C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {25CF4003-4DF8-4F3C-B9B5-50252C2CD2C2}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/DriveCom/DriveCom.v11.suo b/DriveCom/DriveCom.v11.suo
new file mode 100644
index 0000000..cfa5516
Binary files /dev/null and b/DriveCom/DriveCom.v11.suo differ
diff --git a/DriveCom/DriveCom/App.config b/DriveCom/DriveCom/App.config
new file mode 100644
index 0000000..58262a1
--- /dev/null
+++ b/DriveCom/DriveCom/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/DriveCom/DriveCom/DriveCom.csproj b/DriveCom/DriveCom/DriveCom.csproj
new file mode 100644
index 0000000..13695be
--- /dev/null
+++ b/DriveCom/DriveCom/DriveCom.csproj
@@ -0,0 +1,61 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {25CF4003-4DF8-4F3C-B9B5-50252C2CD2C2}
+ Exe
+ Properties
+ DriveCom
+ DriveCom
+ v4.0
+ 512
+
+
+
+ AnyCPU
+ true
+ full
+ false
+ ..\..\tools\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+
+
+ AnyCPU
+ pdbonly
+ true
+ ..\..\tools\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DriveCom/DriveCom/DriveCom.csproj.user b/DriveCom/DriveCom/DriveCom.csproj.user
new file mode 100644
index 0000000..9934927
--- /dev/null
+++ b/DriveCom/DriveCom/DriveCom.csproj.user
@@ -0,0 +1,9 @@
+
+
+
+ /drive=E /burner=C:\Users\Brandon\Documents\GitHub\PS2251-03\BINs\BN03V104M.BIN /firmware=C:\Users\Brandon\Documents\GitHub\PS2251-03\BINs\FW03FF01V10353M.BIN
+
+
+ /drive=E /burner="C:\Users\Brandon\Documents\GitHub\PS2251-03\BINs\BN03V104M.BIN"
+
+
\ No newline at end of file
diff --git a/DriveCom/DriveCom/PhisonDevice.cs b/DriveCom/DriveCom/PhisonDevice.cs
new file mode 100644
index 0000000..1398604
--- /dev/null
+++ b/DriveCom/DriveCom/PhisonDevice.cs
@@ -0,0 +1,374 @@
+using Microsoft.Win32.SafeHandles;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DriveCom
+{
+ public class PhisonDevice
+ {
+ private char _driveLetter;
+ private SafeFileHandle _handle;
+
+ public enum RunMode
+ {
+ Unknown,
+ BootMode,
+ Burner,
+ HardwareVerify,
+ Firmware
+ }
+
+ [Flags]
+ public enum EFileAttributes : uint
+ {
+ Readonly = 0x00000001,
+ Hidden = 0x00000002,
+ System = 0x00000004,
+ Directory = 0x00000010,
+ Archive = 0x00000020,
+ Device = 0x00000040,
+ Normal = 0x00000080,
+ Temporary = 0x00000100,
+ SparseFile = 0x00000200,
+ ReparsePoint = 0x00000400,
+ Compressed = 0x00000800,
+ Offline = 0x00001000,
+ NotContentIndexed = 0x00002000,
+ Encrypted = 0x00004000,
+ Write_Through = 0x80000000,
+ Overlapped = 0x40000000,
+ NoBuffering = 0x20000000,
+ RandomAccess = 0x10000000,
+ SequentialScan = 0x08000000,
+ DeleteOnClose = 0x04000000,
+ BackupSemantics = 0x02000000,
+ PosixSemantics = 0x01000000,
+ OpenReparsePoint = 0x00200000,
+ OpenNoRecall = 0x00100000,
+ FirstPipeInstance = 0x00080000
+ }
+
+ [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+ public static extern SafeFileHandle CreateFile(
+ string fileName,
+ [MarshalAs(UnmanagedType.U4)] FileAccess fileAccess,
+ [MarshalAs(UnmanagedType.U4)] FileShare fileShare,
+ IntPtr securityAttributes,
+ [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
+ [MarshalAs(UnmanagedType.U4)] EFileAttributes flags,
+ IntPtr template);
+
+ [DllImport("kernel32.dll")]
+ static public extern int CloseHandle(SafeFileHandle hObject);
+
+ public const byte SCSI_IOCTL_DATA_OUT = 0;
+ public const byte SCSI_IOCTL_DATA_IN = 1;
+
+ [StructLayout(LayoutKind.Sequential)]
+ class SCSI_PASS_THROUGH_DIRECT
+ {
+ private const int _CDB_LENGTH = 16;
+
+ public short Length;
+ public byte ScsiStatus;
+ public byte PathId;
+ public byte TargetId;
+ public byte Lun;
+ public byte CdbLength;
+ public byte SenseInfoLength;
+ public byte DataIn;
+ public int DataTransferLength;
+ public int TimeOutValue;
+ public IntPtr DataBuffer;
+ public uint SenseInfoOffset;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = _CDB_LENGTH)]
+ public byte[] Cdb;
+
+ public SCSI_PASS_THROUGH_DIRECT()
+ {
+ Cdb = new byte[_CDB_LENGTH];
+ }
+ };
+
+ [StructLayout(LayoutKind.Sequential)]
+ class SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
+ {
+ private const int _SENSE_LENGTH = 32;
+ internal SCSI_PASS_THROUGH_DIRECT sptd = new SCSI_PASS_THROUGH_DIRECT();
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = _SENSE_LENGTH)]
+ internal byte[] sense;
+
+ public SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER()
+ {
+ sense = new byte[_SENSE_LENGTH];
+ }
+ };
+
+ [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
+ static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
+ IntPtr lpInBuffer, uint nInBufferSize,
+ IntPtr lpOutBuffer, uint nOutBufferSize,
+ out uint lpBytesReturned, IntPtr lpOverlapped);
+
+ ///
+ /// Creates a reference to a device with a Phison USB controller.
+ ///
+ /// The Windows drive letter representing the device.
+ public PhisonDevice(char driveLetter)
+ {
+ _driveLetter = driveLetter;
+ }
+
+ ///
+ /// Opens a connection to the device.
+ ///
+ public void Open()
+ {
+ _handle = CreateFile(string.Format("\\\\.\\{0}:", _driveLetter), FileAccess.ReadWrite, FileShare.ReadWrite,
+ IntPtr.Zero, FileMode.Open, EFileAttributes.NoBuffering, IntPtr.Zero);
+ }
+
+ public byte[] RequestVendorInfo()
+ {
+ var data = SendCommand(new byte[] { 0x06, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
+ 512 + 16);
+ byte[] ret = null;
+
+ if (data != null)
+ {
+ ret = data.Take(512).ToArray();
+ }
+
+ return ret;
+ }
+
+ public ushort? GetChipType()
+ {
+ ushort? ret = null;
+ var info = RequestVendorInfo();
+
+ if (info != null)
+ {
+ if (info[0x17A] == (byte)'V' && info[0x17B] == (byte)'R')
+ {
+ var data = info.Skip(0x17E).Take(2).ToArray();
+ ret = (ushort)((data[0] << 8) | data[1]);
+ }
+ }
+
+ return ret;
+ }
+
+ public RunMode GetRunMode()
+ {
+ var ret = RunMode.Unknown;
+ var info = RequestVendorInfo();
+
+ if (info != null)
+ {
+ if (info[0x17A] == (byte)'V' && info[0x17B] == (byte)'R')
+ {
+ //TODO: Fix this, this is a dumb way of detecting it
+ switch (ASCIIEncoding.ASCII.GetString(info.Skip(0xA0).Take(8).ToArray()))
+ {
+ case " PRAM ":
+ ret = RunMode.BootMode;
+ break;
+ case " FW BURN":
+ ret = RunMode.Burner;
+ break;
+ case " HV TEST":
+ ret = RunMode.HardwareVerify;
+ break;
+ default:
+ ret = RunMode.Firmware;
+ break;
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ public void JumpToPRAM()
+ {
+ SendCommand(new byte[] { 0x06, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
+ }
+
+ public void JumpToBootMode()
+ {
+ SendCommand(new byte[] { 0x06, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
+ }
+
+ public void TransferFile(byte[] data)
+ {
+ TransferFile(data, 0x03, 0x02);
+ }
+
+ public void TransferFile(byte[] data, byte header, byte body)
+ {
+ var size = data.Length - 1024;
+
+ //Send header
+ SendCommand(new byte[] { 0x06, 0xB1, header, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, data.Take(0x200).ToArray());
+
+ //Get response
+ var response = SendCommand(new byte[] { 0x06, 0xB0, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 8);
+ if (response == null || response[0] != 0x55)
+ {
+ throw new InvalidOperationException("Header not accepted");
+ }
+
+ //Send body
+ int address = 0;
+ while (size > 0)
+ {
+ int chunkSize;
+ if (size > 0x8000)
+ {
+ chunkSize = 0x8000;
+ }
+ else
+ {
+ chunkSize = size;
+ }
+
+ int cmdAddress = address >> 9;
+ int cmdChunk = chunkSize >> 9;
+ SendCommand(new byte[] { 0x06, 0xB1, body, (byte)((cmdAddress >> 8) & 0xFF), (byte)(cmdAddress & 0xFF),
+ 0x00, 0x00, (byte)((cmdChunk >> 8) & 0xFF), (byte)(cmdChunk & 0xFF), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ data.Skip(address + 0x200).Take(chunkSize).ToArray());
+
+ //Get response
+ var r = SendCommand(new byte[] { 0x06, 0xB0, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 8);
+ if (r == null || r[0] != 0xA5)
+ {
+ throw new InvalidOperationException("Body not accepted");
+ }
+
+ address += chunkSize;
+ size -= chunkSize;
+ }
+ }
+
+ ///
+ /// Sends command with no attached data and returns expected response.
+ ///
+ ///
+ ///
+ ///
+ public byte[] SendCommand(byte[] cmd, int bytesExpected)
+ {
+ return _SendCommand(_handle, cmd, null, bytesExpected);
+ }
+
+ ///
+ /// Sends command with no attached data and no response.
+ ///
+ ///
+ public void SendCommand(byte[] cmd)
+ {
+ SendCommand(cmd, null);
+ }
+
+ ///
+ /// Sends command with attached data and no response.
+ ///
+ ///
+ ///
+ public void SendCommand(byte[] cmd, byte[] data)
+ {
+ _SendCommand(_handle, cmd, data, 0);
+ }
+
+ ///
+ /// Closes the connection to the device.
+ ///
+ public void Close()
+ {
+ if (_handle != null && !_handle.IsClosed)
+ {
+ _handle.Close();
+ }
+ }
+
+ private static byte[] _SendCommand(SafeFileHandle handle, byte[] cmd, byte[] data, int bytesExpected)
+ {
+ const int IOCTL_SCSI_PASS_THROUGH_DIRECT = 0x4D014;
+ const int TIMEOUT_SECS = 30;
+ SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER scsi = null;
+ IntPtr inBuffer = IntPtr.Zero;
+ byte[] ret = null;
+
+ try
+ {
+ scsi = new SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER();
+ scsi.sptd.Length = (short)Marshal.SizeOf(scsi.sptd);
+ scsi.sptd.TimeOutValue = TIMEOUT_SECS;
+ scsi.sptd.SenseInfoOffset = (uint)Marshal.OffsetOf(typeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER), "sense");
+ scsi.sptd.SenseInfoLength = (byte)scsi.sense.Length;
+ scsi.sptd.CdbLength = (byte)cmd.Length;
+ Array.Copy(cmd, scsi.sptd.Cdb, cmd.Length);
+ scsi.sptd.DataIn = data != null && data.Length > 0 ? SCSI_IOCTL_DATA_OUT : SCSI_IOCTL_DATA_IN;
+ scsi.sptd.DataTransferLength = data != null && data.Length > 0 ? data.Length : bytesExpected;
+ scsi.sptd.DataBuffer = Marshal.AllocHGlobal(scsi.sptd.DataTransferLength);
+ if (data != null && data.Length > 0)
+ {
+ Marshal.Copy(data, 0, scsi.sptd.DataBuffer, data.Length);
+ }
+
+ uint bytesReturned;
+ inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(scsi));
+ var size = (uint)Marshal.SizeOf(scsi);
+ Marshal.StructureToPtr(scsi, inBuffer, false);
+ if (!DeviceIoControl(handle.DangerousGetHandle(), IOCTL_SCSI_PASS_THROUGH_DIRECT,
+ inBuffer, size, inBuffer, size, out bytesReturned, IntPtr.Zero))
+ {
+ //Whoops, do something with the error code
+ int last = Marshal.GetLastWin32Error();
+ throw new InvalidOperationException("DeviceIoControl failed: " + last.ToString("X04"));
+ }
+ else
+ {
+ if (scsi.sptd.ScsiStatus != 0)
+ {
+ //Whoops, do something with the error code
+ throw new InvalidOperationException("SCSI command failed: " + scsi.sptd.ScsiStatus.ToString("X02"));
+ }
+ else
+ {
+ //Success, marshal back any data we received
+ if (scsi.sptd.DataTransferLength > 0)
+ {
+ ret = new byte[scsi.sptd.DataTransferLength];
+ Marshal.Copy(scsi.sptd.DataBuffer, ret, 0, ret.Length);
+ }
+ }
+ }
+ }
+ finally
+ {
+ /* Free any unmanaged resources */
+
+ if (scsi != null && scsi.sptd.DataBuffer != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(scsi.sptd.DataBuffer);
+ }
+
+ if (inBuffer != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(inBuffer);
+ }
+ }
+
+ return ret;
+ }
+ }
+}
diff --git a/DriveCom/DriveCom/PhisonDo.csproj.user b/DriveCom/DriveCom/PhisonDo.csproj.user
new file mode 100644
index 0000000..7b718e3
--- /dev/null
+++ b/DriveCom/DriveCom/PhisonDo.csproj.user
@@ -0,0 +1,6 @@
+
+
+
+ /drive=E /burner="C:\\Users\\Brandon\\Documents\GitHub\\PS2251-03\\BINs\BN03V104M.bin" /firmware="C:\\Users\\Brandon\\Documents\\GitHub\\PS2251-03\\patch\\bin\\patched.bin"
+
+
\ No newline at end of file
diff --git a/DriveCom/DriveCom/Properties/AssemblyInfo.cs b/DriveCom/DriveCom/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..96d2c25
--- /dev/null
+++ b/DriveCom/DriveCom/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("PhisonDo")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("PhisonDo")]
+[assembly: AssemblyCopyright("Copyright © 2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("ebfd9a94-1fd1-4595-a864-a3525ff4c875")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// 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("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/DriveCom/DriveCom/Startup.cs b/DriveCom/DriveCom/Startup.cs
new file mode 100644
index 0000000..a54a342
--- /dev/null
+++ b/DriveCom/DriveCom/Startup.cs
@@ -0,0 +1,428 @@
+using Microsoft.Win32.SafeHandles;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace DriveCom
+{
+ class Startup
+ {
+ private const int _WAIT_TIME_MS = 2000;
+ private static PhisonDevice _device = null;
+ private static string _burner;
+ private static string _firmware;
+ private static string _password;
+
+ public enum Action
+ {
+ None,
+ GetInfo,
+ SetPassword,
+ DumpFirmware,
+ SetBootMode,
+ SendExecutable,
+ SendFirmware
+ }
+
+ public enum ExitCode
+ {
+ Success = 0,
+ Failure = 1
+ }
+
+ static void Main(string[] args)
+ {
+ try
+ {
+ Environment.ExitCode = (int)ExitCode.Success;
+
+ var action = Action.None;
+ string drive = string.Empty;
+
+ foreach (var arg in args)
+ {
+ var parts = arg.TrimStart(new char[] { '/' }).Split(new char[] { '=' },
+ StringSplitOptions.RemoveEmptyEntries);
+ switch (parts[0].ToLower())
+ {
+ case "action":
+ {
+ action = (Action)Enum.Parse(typeof(Action), parts[1]);
+ break;
+ }
+ case "drive":
+ {
+ drive = parts[1];
+ break;
+ }
+ case "burner":
+ {
+ _burner = parts[1];
+ break;
+ }
+ case "firmware":
+ {
+ _firmware = parts[1];
+ break;
+ }
+ case "password":
+ {
+ _password = parts[1];
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ if (!string.IsNullOrEmpty(drive))
+ {
+ _OpenDrive(drive);
+ }
+
+ if (action != Action.None)
+ {
+ Console.WriteLine("Action specified: " + action.ToString());
+
+ switch (action)
+ {
+ case Action.DumpFirmware:
+ {
+ _DumpFirmware(_firmware);
+ break;
+ }
+ case Action.GetInfo:
+ {
+ _GetInfo();
+ break;
+ }
+ case Action.SendExecutable:
+ {
+ _ExecuteImage(_burner);
+ break;
+ }
+ case Action.SendFirmware:
+ {
+ _SendFirmware();
+ break;
+ }
+ case Action.SetBootMode:
+ {
+ _device.JumpToBootMode();
+ Thread.Sleep(_WAIT_TIME_MS);
+ break;
+ }
+ case Action.SetPassword:
+ {
+ _SendPassword(_password);
+ break;
+ }
+ default:
+ {
+ throw new ArgumentException("No/invalid action specified");
+ }
+ }
+ }
+ else
+ {
+ Console.WriteLine("No action specified, entering console.");
+
+ bool exiting = false;
+ while (!exiting)
+ {
+ Console.Write(">");
+ var line = Console.ReadLine();
+ var @params = line.Split(new char[] { ' ' });
+
+ try
+ {
+ switch (@params[0].ToLower())
+ {
+ case "open":
+ {
+ _OpenDrive(@params[1]);
+ break;
+ }
+ case "close":
+ {
+ _CloseDrive();
+ break;
+ }
+ case "mode":
+ {
+ Console.WriteLine("Mode: " + _GetInfo().ToString());
+ break;
+ }
+ case "info":
+ {
+ var data = _device.RequestVendorInfo();
+ Console.WriteLine(string.Format("Info: {0}...", BitConverter.ToString(data, 0, 16)));
+ break;
+ }
+ case "password":
+ {
+ _SendPassword(@params[1]);
+ break;
+ }
+ case "dump_xram":
+ {
+ var address = 0;
+ var data = new byte[0xF000];
+ for (int i = 0; i < data.Length; i++)
+ {
+ var result = _device.SendCommand(new byte[] { 0x06, 0x06,
+ (byte)((address >> 8) & 0xFF), (byte)(address & 0xFF), 0x00, 0x00, 0x00, 0x00 }, 1);
+ data[address] = result[0];
+ address++;
+ }
+
+ File.WriteAllBytes(@params[1], data);
+ break;
+ }
+ case "dump_firmware":
+ {
+ _DumpFirmware(@params[1]);
+ break;
+ }
+ case "nand_read":
+ {
+ var address = Convert.ToInt32(@params[1], 16);
+ var size = Convert.ToInt32(@params[2], 16);
+ var result = _device.SendCommand(new byte[] { 0x06, 0xB2, 0x10,
+ (byte)((address >> 8) & 0xFF), (byte)(address & 0xFF), 0x00, 0x00,
+ (byte)((size >> 8) & 0xFF), (byte)(size & 0xFF) }, size * 512);
+ Console.WriteLine(string.Format("Data: {0}...", BitConverter.ToString(result, 0, 16)));
+ break;
+ }
+ case "boot":
+ {
+ _device.JumpToBootMode();
+ Thread.Sleep(_WAIT_TIME_MS);
+ break;
+ }
+ case "set_burner":
+ {
+ _burner = @params[1];
+ break;
+ }
+ case "set_firmware":
+ {
+ _firmware = @params[1];
+ break;
+ }
+ case "burner":
+ {
+ _ExecuteImage(_burner);
+ break;
+ }
+ case "firmware":
+ {
+ _SendFirmware();
+ break;
+ }
+ case "peek":
+ {
+ var address = Convert.ToInt32(@params[1], 16);
+ var result = _device.SendCommand(new byte[] { 0x06, 0x06,
+ (byte)((address >> 8) & 0xFF), (byte)(address & 0xFF), 0x00, 0x00, 0x00, 0x00 }, 1);
+ Console.WriteLine("Value: " + result[0].ToString("X02"));
+ break;
+ }
+ case "poke":
+ {
+ var address = Convert.ToInt32(@params[1], 16);
+ var value = Convert.ToInt32(@params[2], 16);
+ _device.SendCommand(new byte[] { 0x06, 0x07,
+ (byte)((address >> 8) & 0xFF), (byte)(address & 0xFF), (byte)value, 0x00, 0x00 }, 1);
+ break;
+ }
+ case "ipeek":
+ {
+ var address = Convert.ToInt32(@params[1], 16);
+ var result = _device.SendCommand(new byte[] { 0x06, 0x08,
+ (byte)(address & 0xFF), 0x00, 0x00, 0x00, 0x00 }, 1);
+ Console.WriteLine("Value: " + result[0].ToString("X02"));
+ break;
+ }
+ case "ipoke":
+ {
+ var address = Convert.ToInt32(@params[1], 16);
+ var value = Convert.ToInt32(@params[2], 16);
+ _device.SendCommand(new byte[] { 0x06, 0x09,
+ (byte)(address & 0xFF), (byte)value, 0x00, 0x00 }, 1);
+ break;
+ }
+ case "quit":
+ case "exit":
+ {
+ exiting = true;
+ break;
+ }
+ default:
+ Console.WriteLine("Invalid command: " + @params[0]);
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("ERROR: " + ex.ToString());
+ }
+ }
+
+ Console.WriteLine("Done.");
+ }
+ }
+ catch (Exception ex)
+ {
+ Environment.ExitCode = (int)ExitCode.Failure;
+
+ Console.WriteLine("FATAL: " + ex.ToString());
+ }
+ finally
+ {
+ if (_device != null)
+ {
+ _device.Close();
+ }
+ }
+ }
+
+ private static void _OpenDrive(string drive)
+ {
+ _CloseDrive();
+
+ _device = new PhisonDevice(drive[0]);
+ _device.Open();
+ }
+
+ private static void _CloseDrive()
+ {
+ if (_device != null)
+ {
+ _device.Close();
+ _device = null;
+ }
+ }
+
+ private static void _DumpFirmware(string fileName)
+ {
+ var address = 0;
+ var data = new byte[0x32400];
+ var header = new byte[] { 0x42, 0x74, 0x50, 0x72, 0x61, 0x6D, 0x43, 0x64,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x10, 0x0B, 0x18 };
+ Array.Copy(header, 0, data, 0, header.Length);
+ while (address * 0x200 < data.Length)
+ {
+ var length = Math.Min(0x40 * 512, (data.Length - 0x400) - (address * 0x200));
+ var temp = length / 512;
+ var result = _device.SendCommand(new byte[] { 0x06, 0xB2, 0x10,
+ (byte)((address >> 8) & 0xFF), (byte)(address & 0xFF), 0x00, 0x00,
+ (byte)((temp >> 8) & 0xFF), (byte)(temp & 0xFF) }, length);
+ Array.Copy(result.Take(length).ToArray(), 0, data, 0x200 + address * 512, length);
+ address += 0x40;
+ }
+
+ var footer = new byte[] { 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6D,
+ 0x70, 0x20, 0x6D, 0x61, 0x72, 0x6B, 0x00, 0x03, 0x01, 0x00, 0x10, 0x01, 0x04, 0x10, 0x42 };
+ Array.Copy(footer, 0, data, data.Length - 0x200, footer.Length);
+ File.WriteAllBytes(fileName, data);
+ }
+
+ private static void _SendPassword(string password)
+ {
+ var data = new byte[0x200];
+ var pw = ASCIIEncoding.ASCII.GetBytes(password);
+ Array.Copy(pw, 0, data, 0x10, pw.Length);
+ _device.SendCommand(new byte[] { 0x0E, 0x00, 0x01, 0x55, 0xAA,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, data);
+ }
+
+ private static void _SendFirmware()
+ {
+ var mode = _GetInfo();
+ if (mode != PhisonDevice.RunMode.Burner)
+ {
+ if (mode != PhisonDevice.RunMode.BootMode)
+ {
+ Console.WriteLine("Switching to boot mode...");
+ _device.JumpToBootMode();
+ Thread.Sleep(_WAIT_TIME_MS);
+ }
+
+ _ExecuteImage(_burner);
+ }
+
+ _RunFirmware(_firmware);
+ }
+
+ private static PhisonDevice.RunMode _GetInfo()
+ {
+ Console.WriteLine("Gathering information...");
+ Console.WriteLine("Reported chip type: " + _device.GetChipType().GetValueOrDefault().ToString("X04"));
+
+ var ret = _device.GetRunMode();
+ Console.WriteLine("Mode: " + ret.ToString());
+
+ return ret;
+ }
+
+ private static void _ExecuteImage(string fileName)
+ {
+ //Read image
+ var file = new FileStream(fileName, FileMode.Open);
+ var fileData = new byte[file.Length];
+ file.Read(fileData, 0, fileData.Length);
+ file.Close();
+
+ //Load it
+ _device.TransferFile(fileData);
+ _device.JumpToPRAM();
+
+ //Wait a little bit
+ Thread.Sleep(_WAIT_TIME_MS);
+ }
+
+ private static void _RunFirmware(string fileName)
+ {
+ //Get file data
+ var fw = new FileStream(fileName, FileMode.Open);
+ var data = new byte[fw.Length];
+ fw.Read(data, 0, data.Length);
+ fw.Close();
+
+ //TODO: Find out what this actually does...
+ //Console.WriteLine("Sending scary B7 command (takes several seconds)...");
+ //_device.SendCommand(new byte[] { 0x06, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
+
+ Console.WriteLine("Rebooting...");
+ _device.JumpToBootMode();
+ Thread.Sleep(_WAIT_TIME_MS);
+
+ Console.WriteLine("Sending firmware...");
+ _device.TransferFile(data, 0x01, 0x00);
+ var ret = _device.SendCommand(new byte[] { 0x06, 0xEE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 64 + 8);
+ Thread.Sleep(_WAIT_TIME_MS);
+ _device.TransferFile(data, 0x03, 0x02);
+ ret = _device.SendCommand(new byte[] { 0x06, 0xEE, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }, 64 + 8);
+ Thread.Sleep(_WAIT_TIME_MS);
+ ret = _device.SendCommand(new byte[] { 0x06, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 64 + 8);
+ Thread.Sleep(_WAIT_TIME_MS);
+ ret = _device.SendCommand(new byte[] { 0x06, 0xEE, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }, 64 + 8);
+ Thread.Sleep(_WAIT_TIME_MS);
+
+ Console.WriteLine("Executing...");
+ _device.JumpToPRAM();
+ Thread.Sleep(_WAIT_TIME_MS);
+
+ //Display new mode, if we can actually get it
+ Console.WriteLine("Mode: " + _device.GetRunMode().ToString());
+ }
+ }
+}
diff --git a/EmbedPayload/EmbedPayload.exe.config b/EmbedPayload/EmbedPayload.exe.config
new file mode 100644
index 0000000..8e15646
--- /dev/null
+++ b/EmbedPayload/EmbedPayload.exe.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmbedPayload/EmbedPayload.sln b/EmbedPayload/EmbedPayload.sln
new file mode 100644
index 0000000..d41bf9a
--- /dev/null
+++ b/EmbedPayload/EmbedPayload.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Express 2012 for Windows Desktop
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmbedPayload", "EmbedPayload\EmbedPayload.csproj", "{E2381629-180B-44DE-8702-B63B4699A587}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E2381629-180B-44DE-8702-B63B4699A587}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E2381629-180B-44DE-8702-B63B4699A587}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E2381629-180B-44DE-8702-B63B4699A587}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E2381629-180B-44DE-8702-B63B4699A587}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/EmbedPayload/EmbedPayload.v11.suo b/EmbedPayload/EmbedPayload.v11.suo
new file mode 100644
index 0000000..47ca141
Binary files /dev/null and b/EmbedPayload/EmbedPayload.v11.suo differ
diff --git a/EmbedPayload/EmbedPayload.vshost.exe.config b/EmbedPayload/EmbedPayload.vshost.exe.config
new file mode 100644
index 0000000..8e15646
--- /dev/null
+++ b/EmbedPayload/EmbedPayload.vshost.exe.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmbedPayload/EmbedPayload/App.config b/EmbedPayload/EmbedPayload/App.config
new file mode 100644
index 0000000..8e15646
--- /dev/null
+++ b/EmbedPayload/EmbedPayload/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmbedPayload/EmbedPayload/EmbedPayload.csproj b/EmbedPayload/EmbedPayload/EmbedPayload.csproj
new file mode 100644
index 0000000..ffede20
--- /dev/null
+++ b/EmbedPayload/EmbedPayload/EmbedPayload.csproj
@@ -0,0 +1,58 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {E2381629-180B-44DE-8702-B63B4699A587}
+ Exe
+ Properties
+ EmbedPayload
+ EmbedPayload
+ v4.5
+ 512
+
+
+ AnyCPU
+ true
+ full
+ false
+ ..\..\tools\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ ..\..\tools\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmbedPayload/EmbedPayload/Properties/AssemblyInfo.cs b/EmbedPayload/EmbedPayload/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..612a796
--- /dev/null
+++ b/EmbedPayload/EmbedPayload/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("EmbedPayload")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Toshiba")]
+[assembly: AssemblyProduct("EmbedPayload")]
+[assembly: AssemblyCopyright("Copyright © Toshiba 2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("5cd1f91a-6190-490e-a5c8-6d200a7e00db")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// 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("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/EmbedPayload/EmbedPayload/Startup.cs b/EmbedPayload/EmbedPayload/Startup.cs
new file mode 100644
index 0000000..53e33d6
--- /dev/null
+++ b/EmbedPayload/EmbedPayload/Startup.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EmbedPayload
+{
+ class Startup
+ {
+ public enum ExitCode
+ {
+ Success = 0,
+ Failure = 1
+ }
+
+ static void Main(string[] args)
+ {
+ if (args.Length != 2)
+ {
+ Console.WriteLine("Usage: [payload BIN] [firmware image]");
+ return;
+ }
+
+ try
+ {
+ //Assume success at first
+ Environment.ExitCode = (int)ExitCode.Success;
+
+ //Read all bytes from input file
+ var payload = File.ReadAllBytes(args[0]);
+
+ //Read all bytes from output file:
+ var stream = new FileStream(args[1], FileMode.Open, FileAccess.ReadWrite);
+ var header = new byte[0x200];
+ stream.Read(header, 0, header.Length);
+ var data = new byte[0x6000];
+ stream.Read(data, 0, data.Length);
+
+ // Look for 0x12345678
+ var signature = new byte[] { 0x12, 0x34, 0x56, 0x78 };
+ int? address = null;
+ for (int i = 0; i < data.Length; i++)
+ {
+ bool match = true;
+ for (int j = 0; j < signature.Length; j++)
+ {
+ if (data[i + j] != signature[j])
+ {
+ match = false;
+ break;
+ }
+ }
+
+ if (match)
+ {
+ address = i;
+ break;
+ }
+ }
+
+ // When found, overwrite with input data
+ if (address.HasValue)
+ {
+ if ((0x200 + address.Value) >= 0x6000)
+ {
+ throw new InvalidOperationException("Insufficient memory to inject file!");
+ }
+
+ stream.Seek(0x200 + address.Value, SeekOrigin.Begin);
+ stream.Write(payload, 0, payload.Length);
+
+ //Save output file back out
+ stream.Close();
+ Console.WriteLine("File updated.");
+ }
+ else
+ {
+ Console.WriteLine("Signature not found!");
+ }
+ }
+ catch (Exception ex)
+ {
+ //Uh-oh
+ Environment.ExitCode = (int)ExitCode.Failure;
+
+ Console.WriteLine("FATAL: " + ex.ToString());
+ }
+ }
+ }
+}
diff --git a/Injector/Injector.sln b/Injector/Injector.sln
new file mode 100644
index 0000000..5c45d9a
--- /dev/null
+++ b/Injector/Injector.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Express 2012 for Windows Desktop
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Injector", "Injector\Injector.csproj", "{41480B6F-9051-43D1-9484-30A265D09D5D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {41480B6F-9051-43D1-9484-30A265D09D5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {41480B6F-9051-43D1-9484-30A265D09D5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {41480B6F-9051-43D1-9484-30A265D09D5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {41480B6F-9051-43D1-9484-30A265D09D5D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Injector/Injector.v11.suo b/Injector/Injector.v11.suo
new file mode 100644
index 0000000..cb55d50
Binary files /dev/null and b/Injector/Injector.v11.suo differ
diff --git a/Injector/Injector/App.config b/Injector/Injector/App.config
new file mode 100644
index 0000000..58262a1
--- /dev/null
+++ b/Injector/Injector/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/Injector/Injector/DoPatch.csproj.user b/Injector/Injector/DoPatch.csproj.user
new file mode 100644
index 0000000..0f80b6e
--- /dev/null
+++ b/Injector/Injector/DoPatch.csproj.user
@@ -0,0 +1,6 @@
+
+
+
+ /firmware="C:\\Users\\Brandon\\Documents\\GitHub\\PS2251-03\\BINs\\FW03FF01V10353M.BIN" /basecode="C:\\Users\\Brandon\\Documents\\GitHub\\PS2251-03\\patch\\bin\\output.bin" /action=FindFreeBlocks /output="C:\\Users\\Brandon\\Documents\\GitHub\\PS2251-03\\patch\\bin\\patched.bin" /baserst="C:\\Users\\Brandon\\Documents\\GitHub\\PS2251-03\\patch\\bin\\main.rst"
+
+
\ No newline at end of file
diff --git a/Injector/Injector/FirmwareImage.cs b/Injector/Injector/FirmwareImage.cs
new file mode 100644
index 0000000..b89eb11
--- /dev/null
+++ b/Injector/Injector/FirmwareImage.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Injector
+{
+ public class FirmwareImage
+ {
+ private string _fileName;
+ private byte[] _header;
+ private Dictionary _sections;
+ private byte[] _footer;
+
+ public FirmwareImage(string fileName)
+ {
+ _fileName = fileName;
+ _header = new byte[0x200];
+ _sections = new Dictionary();
+ _sections.Add(FirmwareSection.Base, new byte[0x6000]);
+ }
+
+ public byte[] GetSection(FirmwareSection section)
+ {
+ byte[] ret = null;
+
+ if (_sections.ContainsKey(section))
+ {
+ ret = _sections[section];
+ }
+
+ return ret;
+ }
+
+ public void Open()
+ {
+ FirmwareSection i = 0;
+
+ //Get the header and base page
+ var stream = new FileStream(_fileName, FileMode.Open);
+ var @base = GetSection(FirmwareSection.Base);
+ stream.Read(_header, 0, _header.Length);
+ stream.Read(@base, 0, @base.Length);
+
+ //Read in all the sections
+ while ((stream.Length - stream.Position) > 0x200)
+ {
+ var data = new byte[0x4000];
+ stream.Read(data, 0, data.Length);
+ _sections.Add(i++, data);
+ }
+
+ //If we have a footer, read it in
+ if ((stream.Length - stream.Position) == 0x200)
+ {
+ _footer = new byte[0x200];
+ stream.Read(_footer, 0, _footer.Length);
+ }
+
+ //All done
+ stream.Close();
+ }
+
+ public bool FindPattern(byte?[] pattern, out FirmwareSection section, out int address)
+ {
+ return FindPattern(pattern, 0, out section, out address);
+ }
+
+ public bool FindPattern(byte?[] pattern, int startingOffset, out FirmwareSection section, out int address)
+ {
+ bool ret = false;
+ section = FirmwareSection.Base;
+ address = 0;
+
+ foreach (var s in _sections)
+ {
+ for (int i = startingOffset; i < s.Value.Length; i++)
+ {
+ bool match = true;
+ for (int j = 0; j < pattern.Length; j++)
+ {
+ if (((i + j) >= s.Value.Length) ||
+ ((s.Value[i + j] != pattern[j]) && (pattern[j].HasValue)))
+ {
+ match = false;
+ break;
+ }
+ }
+
+ if (match)
+ {
+ section = s.Key;
+ address = i;
+ ret = true;
+ break;
+ }
+ }
+
+ if (ret)
+ {
+ break;
+ }
+ }
+
+ return ret;
+ }
+
+ public int FindLastFreeChunk(FirmwareSection section)
+ {
+ int ret = -1;
+
+ if (_sections.ContainsKey(section))
+ {
+ var data = _sections[section];
+ var repeating = data[data.Length - 1];
+ ret = data.Length - 2;
+
+ while (data[ret] == repeating)
+ {
+ ret--;
+ if (ret < 0)
+ {
+ break;
+ }
+ }
+ }
+
+ return ++ret;
+ }
+
+ public void Save(string fileName)
+ {
+ var output = new FileStream(fileName, FileMode.Create);
+ output.Write(_header, 0, _header.Length);
+ foreach (var section in _sections.OrderBy(t => t.Key))
+ {
+ output.Write(section.Value, 0, section.Value.Length);
+ }
+
+ if (_footer != null)
+ {
+ output.Write(_footer, 0, _footer.Length);
+ }
+
+ output.Close();
+ }
+ }
+}
diff --git a/Injector/Injector/FirmwareSection.cs b/Injector/Injector/FirmwareSection.cs
new file mode 100644
index 0000000..40c4c9d
--- /dev/null
+++ b/Injector/Injector/FirmwareSection.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Injector
+{
+ public enum FirmwareSection
+ {
+ None = -2,
+ Base = -1,
+ Section0 = 0x00,
+ Section1 = 0x01,
+ Section2 = 0x02,
+ Section3 = 0x03,
+ Section4 = 0x04,
+ Section5 = 0x05,
+ Section6 = 0x06,
+ Section7 = 0x07,
+ Section8 = 0x08,
+ Section9 = 0x09,
+ SectionA = 0x0A,
+ SectionB = 0x0B,
+ SectionC = 0x0C,
+ SectionD = 0x0D,
+ SectionE = 0x0E,
+ SectionF = 0x0F
+ }
+}
diff --git a/Injector/Injector/Injector.csproj b/Injector/Injector/Injector.csproj
new file mode 100644
index 0000000..c51311b
--- /dev/null
+++ b/Injector/Injector/Injector.csproj
@@ -0,0 +1,61 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {41480B6F-9051-43D1-9484-30A265D09D5D}
+ Exe
+ Properties
+ Injector
+ Injector
+ v4.0
+ 512
+
+
+
+ AnyCPU
+ true
+ full
+ false
+ ..\..\tools\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ ..\..\tools\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Injector/Injector/Injector.csproj.user b/Injector/Injector/Injector.csproj.user
new file mode 100644
index 0000000..44862ff
--- /dev/null
+++ b/Injector/Injector/Injector.csproj.user
@@ -0,0 +1,6 @@
+
+
+
+ /firmware=C:\Users\Brandon\Documents\GitHub\PS2251-03\BINs\FW03FF01V10353M.BIN /output=free.txt /action=FindFreeBlock
+
+
\ No newline at end of file
diff --git a/Injector/Injector/Properties/AssemblyInfo.cs b/Injector/Injector/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..34d8819
--- /dev/null
+++ b/Injector/Injector/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("DoPatch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Toshiba")]
+[assembly: AssemblyProduct("DoPatch")]
+[assembly: AssemblyCopyright("Copyright © Toshiba 2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("16a14751-01cb-4cb0-a3d1-7835d24f4711")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// 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("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Injector/Injector/Startup.cs b/Injector/Injector/Startup.cs
new file mode 100644
index 0000000..83165a3
--- /dev/null
+++ b/Injector/Injector/Startup.cs
@@ -0,0 +1,559 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Injector
+{
+ class Startup
+ {
+ private static string _firmwareImage;
+ private static string _outputFile;
+ private static FirmwareSection _section = FirmwareSection.None;
+ private static Dictionary _codeFiles;
+ private static Dictionary _rstFiles;
+
+ internal enum Action
+ {
+ None,
+ GenerateHFile,
+ FindFreeBlock,
+ ApplyPatches
+ }
+
+ internal enum ExitCode
+ {
+ Success = 0,
+ Failure = 1
+ }
+
+ static void Main(string[] args)
+ {
+ try
+ {
+ _codeFiles = new Dictionary();
+ _rstFiles = new Dictionary();
+
+ //Assume success to start with
+ Environment.ExitCode = (int)ExitCode.Success;
+
+ var action = Action.None;
+
+ //Parse command line arguments
+ foreach (var arg in args)
+ {
+ var parts = arg.TrimStart(new char[] { '/' }).Split(new char[] { '=' },
+ StringSplitOptions.RemoveEmptyEntries);
+ switch (parts[0].ToLower())
+ {
+ case "action":
+ {
+ action = (Action)Enum.Parse(typeof(Action), parts[1]);
+ Console.WriteLine("Action: " + action.ToString());
+ break;
+ }
+ case "section":
+ {
+ _section = (FirmwareSection)Enum.Parse(typeof(FirmwareSection), parts[1]);
+ Console.WriteLine("Section: " + _section.ToString());
+ break;
+ }
+ case "firmware":
+ {
+ _firmwareImage = parts[1];
+ Console.WriteLine("Firmware image: " + _firmwareImage);
+ _CheckFirmwareImage();
+ break;
+ }
+ case "output":
+ {
+ _outputFile = parts[1];
+ Console.WriteLine("Output file: " + _outputFile);
+ break;
+ }
+ default:
+ {
+ _ParseFileNames(ref _codeFiles, "code", parts[0], parts[1]);
+ _ParseFileNames(ref _rstFiles, "rst", parts[0], parts[1]);
+ break;
+ }
+ }
+ }
+
+ //Firmware image file name is always required
+ if (string.IsNullOrEmpty(_firmwareImage))
+ {
+ throw new ArgumentException("No/Invalid firmware image file name specified");
+ }
+
+ switch (action)
+ {
+ case Action.GenerateHFile:
+ {
+ if (string.IsNullOrEmpty(_outputFile))
+ {
+ throw new ArgumentException("No/Invalid output file name specified");
+ }
+
+ Console.WriteLine("Generating .h file...");
+
+ _GenerateHFile();
+ break;
+ }
+ case Action.ApplyPatches:
+ {
+ //Check required arguments for this action
+
+ if (string.IsNullOrEmpty(_outputFile))
+ {
+ throw new ArgumentException("No/Invalid output file name specified");
+ }
+
+ if (_codeFiles.Count == 0)
+ {
+ throw new ArgumentException("No code file name(s) specified");
+ }
+
+ if (_rstFiles.Count == 0)
+ {
+ throw new ArgumentException("No/Invalid RST file name specified");
+ }
+
+ Console.WriteLine("Applying patches...");
+ _ApplyPatches();
+ break;
+ }
+ case Action.FindFreeBlock:
+ {
+ //Check required arguments for this action
+ if (_section == FirmwareSection.None)
+ {
+ throw new ArgumentException("No/Invalid section specified");
+ }
+
+ Console.WriteLine("Retriving free space...");
+ _GetFreeSpaceToFile();
+ break;
+ }
+ default:
+ throw new ArgumentException("No/Invalid action specified");
+ }
+
+ Console.WriteLine("Done.");
+ }
+ catch (Exception ex)
+ {
+ //Uh-oh...
+ Environment.ExitCode = (int)ExitCode.Failure;
+
+ var asm = System.Reflection.Assembly.GetExecutingAssembly();
+ var asmName = asm.GetName();
+ Console.WriteLine(asmName.Name + " v" + asmName.Version.ToString(3));
+ Console.WriteLine("Actions:");
+ Console.WriteLine("\tGenerateHFile\tGenerates C .h file of common XRAM & function equates.");
+ Console.WriteLine("\tFindFreeBlock\tWrites amount of free space for a section to file.");
+ Console.WriteLine("\tApplyPatches\tApplies available patches from code into firmware image.");
+ Console.WriteLine();
+ Console.WriteLine("FATAL: " + ex.ToString());
+ }
+ }
+
+ private static void _CheckFirmwareImage()
+ {
+ var md5 = new MD5CryptoServiceProvider();
+ var verified = new List();
+ verified.Add("4C4C0001EC83102C4627D271FF8362A2");
+
+ var hash = BitConverter.ToString(md5.ComputeHash(File.ReadAllBytes(_firmwareImage)))
+ .Replace("-", string.Empty);
+ if (!verified.Contains(hash))
+ {
+ Console.WriteLine("WARNING! This firmware version has not been " +
+ "verified to work with these patches.");
+ }
+ }
+
+ private static void _ParseFileNames(ref Dictionary files,
+ string suffix, string name, string value)
+ {
+ if (name.ToLower().EndsWith(suffix))
+ {
+ var section = FirmwareSection.Base;
+ int s;
+
+ if (int.TryParse(name.Substring(0, name.Length - suffix.Length), out s))
+ {
+ section = (FirmwareSection)s;
+ }
+
+ files.Add(section, value);
+ Console.WriteLine(suffix + " " + section.ToString() + " file: " + value);
+ }
+ }
+
+ private static Dictionary _GetAddressMap(string fileName)
+ {
+ //Read in RST file and its label<->address map
+ var addressMap = new Dictionary();
+ var ret = new Dictionary();
+ var rst = new StreamReader(fileName, ASCIIEncoding.ASCII);
+
+ while (true)
+ {
+ var line = rst.ReadLine();
+ if (line == null)
+ {
+ break;
+ }
+
+ if (line.EndsWith(":"))
+ {
+ var parts = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ var label = parts[parts.Length - 1].TrimEnd(':');
+ var address = parts[0];
+
+ if (label.StartsWith("_"))
+ {
+ ret.Add(label, Convert.ToInt32(address, 16));
+ }
+ }
+ }
+
+ rst.Close();
+
+ return ret;
+ }
+
+ private static void _GenerateHFile()
+ {
+ var stream = new StreamWriter(_outputFile);
+
+ //Read in firmware image
+ var image = new FirmwareImage(_firmwareImage);
+ image.Open();
+
+ var pattern = new byte?[] { 0x90, 0xF0, 0xB8, 0xE0, //mov DPTR, #0xF0B8 \ movx a, @DPTR
+ 0x90, null, null, 0xF0, //mov DPTR, #0x???? \ movx @DPTR, a
+ 0x90, 0xF0, 0xB9, 0xE0 }; //mov DPTR, #0xF0B9 \ movx a, @DPTR \ movx DPTR, #0x????
+ FirmwareSection section;
+ int address;
+ if (image.FindPattern(pattern, out section, out address))
+ {
+ var a = image.GetSection(section)[address + 5] << 8;
+ a |= image.GetSection(section)[address + 6];
+
+ stream.WriteLine(string.Format("__xdata __at 0x{0} BYTE {1};", a.ToString("X04"), "bmRequestType"));
+ stream.WriteLine(string.Format("__xdata __at 0x{0} BYTE {1};", (a + 1).ToString("X04"), "bRequest"));
+ }
+
+ pattern = new byte?[] { 0x90, null, null, 0xE0, //mov DPTR, #0x???? \ movx a, @DPTR
+ 0xB4, 0x28 }; //cjne A, #0x28, ????
+ if (image.FindPattern(pattern, out section, out address))
+ {
+ var a = image.GetSection(section)[address + 1] << 8;
+ a |= image.GetSection(section)[address + 2];
+ stream.WriteLine(string.Format("__xdata __at 0x{0} BYTE {1}[16];", a.ToString("X04"), "scsi_cdb"));
+
+ stream.WriteLine(string.Format("#define {0} 0x{1}", "DEFAULT_READ_SECTOR_HANDLER", (address + 7).ToString("X04")));
+ pattern = new byte?[] { 0x90, (byte)((a >> 8) & 0xFF), (byte)(a & 0xFF), //mov DPTR, #scsi_tag
+ 0xE0, 0x12 }; //mvox A, @DPTR \ lcall 0x????
+ if (image.FindPattern(pattern, address, out section, out address))
+ {
+ stream.WriteLine(string.Format("#define {0} 0x{1}", "DEFAULT_CDB_HANDLER", address.ToString("X04")));
+ }
+ }
+
+ pattern = new byte?[] { 0x90, 0xF2, 0x1C, //mov DPTR, #0xF21C
+ 0x74, 0x55, 0xF0, //mov A, #0x55 \ movx @DPTR, A
+ 0x74, 0x53, 0xF0, //mov A, #0x53 \ movx @DPTR, A
+ 0x74, 0x42, 0xF0, //mov A, #0x42 \ movx @DPTR, A
+ 0x74, 0x53, 0xF0, //mov A, #0x53 \ movx @DPTR, A
+ 0x90 }; //mov DPTR, #0x????
+ if (image.FindPattern(pattern, out section, out address))
+ {
+ var a = image.GetSection(section)[address + pattern.Length] << 8;
+ a |= image.GetSection(section)[address + pattern.Length + 1];
+
+ stream.WriteLine(string.Format("__xdata __at 0x{0} BYTE {1}[4];", (a - 3).ToString("X04"), "scsi_tag"));
+ }
+
+ pattern = new byte?[] { 0xC0, 0xE0, 0xC0, 0x83, 0xC0, 0x82, //push ACC \ push DPH \ push DPL
+ 0x90, 0xF0, 0x20, 0xE0, //mov DPTR, #0xF020 \ movx A, @DPTR
+ 0x30, 0xE1, null, //jnb ACC.1, ????
+ 0x12, null, null, 0x90 }; //lcall ???? \ mov DPTR, #0x????
+ if (image.FindPattern(pattern, out section, out address))
+ {
+ var a = image.GetSection(section)[address + 17] << 8;
+ a |= image.GetSection(section)[address + 18];
+
+ stream.WriteLine(string.Format("__xdata __at 0x{0} BYTE {1};", a.ToString("X04"), "FW_EPIRQ"));
+ }
+
+ stream.WriteLine(string.Format("__xdata __at 0x{0} BYTE {1}[1024];", "B000", "EPBUF"));
+
+ stream.Close();
+ }
+
+ private static void _GetFreeSpaceToFile()
+ {
+ //Read in firmware image
+ var image = new FirmwareImage(_firmwareImage);
+ image.Open();
+
+ File.WriteAllText(_outputFile, "0x" + image.FindLastFreeChunk(_section).ToString("X04"));
+ }
+
+ private static void _ApplyPatches()
+ {
+ //Read in firmware image
+ var image = new FirmwareImage(_firmwareImage);
+ image.Open();
+
+ //Read in the RST files
+ var maps = new Dictionary>();
+ foreach (var file in _rstFiles)
+ {
+ maps.Add(file.Key, _GetAddressMap(file.Value));
+ }
+
+ //Find how much free space is left on each page
+ var emptyStart = new Dictionary();
+ for (FirmwareSection i = FirmwareSection.Base; i < FirmwareSection.SectionF; i++)
+ {
+ emptyStart.Add(i, image.FindLastFreeChunk(i));
+ }
+
+ //Embed our code files into the firmware image
+ foreach (var file in _codeFiles)
+ {
+ var code = File.ReadAllBytes(file.Value);
+ Array.Copy(code, 0, image.GetSection(file.Key), emptyStart[file.Key], code.Length);
+ emptyStart[file.Key] += code.Length;
+ }
+
+ //Find the off-page call stubs
+ var stubs = new Dictionary();
+ int saddr = 0;
+ var spattern = new byte?[] { 0xC0, 0x5B, 0x74, 0x08, //push RAM_5B \ mov A, #8
+ 0xC0, 0xE0, 0xC0, 0x82, 0xC0, 0x83, //push ACC \ push DPL \ push DPH
+ 0x75, 0x5B }; //mov RAM_5B, #0x??
+ FirmwareSection fs;
+ for (FirmwareSection i = FirmwareSection.Section0; i <= FirmwareSection.SectionF; i++)
+ {
+ if (image.FindPattern(spattern, saddr, out fs, out saddr))
+ {
+ stubs.Add(i, saddr);
+ saddr += spattern.Length; //move ahead so we can find the next stub
+ }
+ }
+
+ //Hook into control request handling
+ foreach (var map in maps)
+ {
+ if (map.Value.ContainsKey("_HandleControlRequest"))
+ {
+ var address = map.Value["_HandleControlRequest"];
+ var pattern = new byte?[] { 0x12, null, null, //lcall #0x????
+ 0x90, 0xFE, 0x82, 0xE0, //mov DPTR, #0xFE82 \ movx A, @DPTR
+ 0x54, 0xEF, 0xF0 }; //anl A, #0xEF \ movx @DPTR, A
+ FirmwareSection s;
+ int a;
+ if (image.FindPattern(pattern, out s, out a))
+ {
+ a = (image.GetSection(s)[a + 1] << 8) | image.GetSection(s)[a + 2];
+
+ image.GetSection(s)[a + 1] = (byte)((address >> 8) & 0xFF);
+ image.GetSection(s)[a + 2] = (byte)(address & 0xFF);
+ if (map.Key != FirmwareSection.Base)
+ {
+ image.GetSection(s)[a + 4] = (byte)((stubs[map.Key] >> 8) & 0xFF);
+ image.GetSection(s)[a + 5] = (byte)(stubs[map.Key] & 0xFF);
+ }
+ }
+ break;
+ }
+ }
+
+ //Replace the EP interrupt vector, handling all incoming and outgoing non-control data
+ foreach (var map in maps)
+ {
+ //This part must be on the base page
+ if (map.Value.ContainsKey("_EndpointInterrupt"))
+ {
+ var address = map.Value["_EndpointInterrupt"];
+ var s = image.GetSection(FirmwareSection.Base);
+ s[0x0014] = (byte)((address >> 8) & 0xFF);
+ s[0x0015] = (byte)(address & 0xFF);
+ }
+
+ if (map.Value.ContainsKey("_HandleEndpointInterrupt"))
+ {
+ //Find the base page location to patch
+ var pattern = new byte?[] { 0x30, 0xE1, null, //jnb ACC.1, #0x????
+ 0x12, null, null, //lcall #0x????
+ 0x90, 0xFE, 0x82, 0xE0, //mov DPTR, #0xFE82 \ movx A, @DPTR
+ 0x54, 0xEF, 0xF0 }; //anl A, #0xEF \ movx @DPTR, A
+ FirmwareSection ps;
+ int pa;
+ if (image.FindPattern(pattern, out ps, out pa))
+ {
+ //Create off-page stub for this if necessary
+ var address = map.Value["_HandleEndpointInterrupt"];
+ var stubAddress = address;
+ if (map.Key != FirmwareSection.Base)
+ {
+ stubAddress = emptyStart[FirmwareSection.Base];
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = 0x90;
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)((address >> 8) & 0xFF);
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)(address & 0xFF);
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = 0x02;
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)((stubs[map.Key] >> 8) & 0xFF);
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)(stubs[map.Key] & 0xFF);
+ }
+
+ //Apply the patch
+ var s = image.GetSection(ps);
+ s[pa + 0] = 0x60;
+ s[pa + 1] = 0x0B;
+ s[pa + 2] = 0x00;
+ s[pa + 4] = (byte)((stubAddress >> 8) & 0xFF);
+ s[pa + 5] = (byte)(stubAddress & 0xFF);
+ for (int i = 0; i < 7; i++)
+ {
+ s[pa + 6 + i] = 0x00;
+ }
+ }
+ }
+ }
+
+ //Apply CDB-handling code
+ foreach (var map in maps)
+ {
+ if (map.Value.ContainsKey("_HandleCDB"))
+ {
+ var pattern = new byte?[] { 0x90, null, null, 0xE0, //mov DPTR, #0x???? \ movx a, @DPTR
+ 0xB4, 0x28 }; //cjne A, #0x28, ????
+ FirmwareSection ps;
+ int pa;
+ if (image.FindPattern(pattern, out ps, out pa))
+ {
+ //Create off-page stub for this if necessary
+ var address = map.Value["_HandleCDB"];
+ var stubAddress = address;
+ if (map.Key != FirmwareSection.Base)
+ {
+ stubAddress = emptyStart[FirmwareSection.Base];
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = 0x90;
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)((address >> 8) & 0xFF);
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)(address & 0xFF);
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = 0x02;
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)((stubs[map.Key] >> 8) & 0xFF);
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)(stubs[map.Key] & 0xFF);
+ }
+
+ //Apply the patch
+ var s = image.GetSection(FirmwareSection.Base);
+ s[pa + 0] = 0x02;
+ s[pa + 1] = (byte)((stubAddress >> 8) & 0xFF);
+ s[pa + 2] = (byte)(stubAddress & 0xFF);
+ }
+ }
+ }
+
+ //Add our own code to the infinite loop
+ foreach (var map in maps)
+ {
+ if (map.Value.ContainsKey("_LoopDo"))
+ {
+ var pattern = new byte?[] { 0x90, null, null, 0xE0, //mov DPTR, #0x???? \ movx A, @DPTR
+ 0xB4, 0x01, null, //cjne A, #1, #0x????
+ 0x90, 0xF0, 0x79 }; //mov DPTR, #0xF079
+ FirmwareSection ps;
+ int pa;
+ if (image.FindPattern(pattern, out ps, out pa))
+ {
+ //Create off-page stub for this if necessary
+ var address = map.Value["_LoopDo"];
+ var stubAddress = address;
+ if (map.Key != FirmwareSection.Base)
+ {
+ stubAddress = emptyStart[FirmwareSection.Base];
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = 0x90;
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)((address >> 8) & 0xFF);
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)(address & 0xFF);
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = 0x02;
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)((stubs[map.Key] >> 8) & 0xFF);
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)(stubs[map.Key] & 0xFF);
+ }
+
+ var s = image.GetSection(ps);
+ var loopDoStart = emptyStart[FirmwareSection.Base];
+ s[emptyStart[FirmwareSection.Base]++] = 0x12;
+ s[emptyStart[FirmwareSection.Base]++] = (byte)((stubAddress >> 8) & 0xFF);
+ s[emptyStart[FirmwareSection.Base]++] = (byte)(stubAddress & 0xFF);
+ s[emptyStart[FirmwareSection.Base]++] = 0x90;
+ s[emptyStart[FirmwareSection.Base]++] = image.GetSection(ps)[pa + 1];
+ s[emptyStart[FirmwareSection.Base]++] = image.GetSection(ps)[pa + 2];
+ s[emptyStart[FirmwareSection.Base]++] = 0x22;
+ s[pa + 0] = 0x12;
+ s[pa + 1] = (byte)((loopDoStart >> 8) & 0xFF);
+ s[pa + 2] = (byte)(loopDoStart & 0xFF);
+ }
+ }
+ }
+
+ //Apply password patch code
+ foreach (var map in maps)
+ {
+ if (map.Value.ContainsKey("_PasswordReceived"))
+ {
+ var pattern = new byte?[] { 0x90, 0xF2, 0x4C, 0xF0, 0xA3, //mov DPTR, #0xF24C \ movx @DPTR, A \ inc DPTR
+ 0xC0, 0x83, 0xC0, 0x82, 0x12, //push DPH \ push DPL
+ null, null, 0xD0, 0x82, 0xD0, 0x83, 0xF0, //lcall #0x???? \ pop DPL \ pop DPH \ movx @DPTR, A
+ 0x90, 0xF2, 0x53, 0x74, 0x80, 0xF0, //mov DPTR, #0xF253 \ mov A, #0x80 \ movx @DPTR, A
+ 0x90, 0xF2, 0x53, 0xE0, //mov DPTR, #0xF253 \ movx A, @DPTR
+ 0x30, 0xE7, null, //jnb ACC.7, #0x????
+ 0x12, null, null, 0x40, null, //lcall #0x???? \ jc #0x????
+ 0x12, null, null, 0x7F, 0x00, 0x22 }; //lcall #0x???? \ mov R7, #0 \ ret
+ FirmwareSection ps;
+ int pa;
+ if (image.FindPattern(pattern, out ps, out pa))
+ {
+ //Create off-page stub for this if necessary
+ var address = map.Value["_PasswordReceived"];
+ var stubAddress = address;
+ if (map.Key != FirmwareSection.Base)
+ {
+ stubAddress = emptyStart[FirmwareSection.Base];
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = 0x90;
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)((address >> 8) & 0xFF);
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)(address & 0xFF);
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = 0x02;
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)((stubs[map.Key] >> 8) & 0xFF);
+ image.GetSection(FirmwareSection.Base)[emptyStart[FirmwareSection.Base]++] = (byte)(stubs[map.Key] & 0xFF);
+ }
+
+ //Apply the patch
+ pa += 0x24;
+ var passRecvdStart = emptyStart[ps] + (ps == FirmwareSection.Base ? 0x0000 : 0x5000);
+ image.GetSection(ps)[emptyStart[ps]++] = 0x12;
+ image.GetSection(ps)[emptyStart[ps]++] = image.GetSection(ps)[pa + 0];
+ image.GetSection(ps)[emptyStart[ps]++] = image.GetSection(ps)[pa + 1];
+ image.GetSection(ps)[emptyStart[ps]++] = 0x02;
+ image.GetSection(ps)[emptyStart[ps]++] = (byte)((stubAddress >> 8) & 0xFF);
+ image.GetSection(ps)[emptyStart[ps]++] = (byte)(stubAddress & 0xFF);
+ image.GetSection(ps)[pa + 0] = (byte)((passRecvdStart >> 8) & 0xFF);
+ image.GetSection(ps)[pa + 1] = (byte)(passRecvdStart & 0xFF);
+ }
+ }
+ }
+
+ //Write the resulting file out
+ image.Save(_outputFile);
+ }
+ }
+}
diff --git a/docs/PinsToShortUponPlugInForBootMode.jpg b/docs/PinsToShortUponPlugInForBootMode.jpg
new file mode 100644
index 0000000..ebb10c6
Binary files /dev/null and b/docs/PinsToShortUponPlugInForBootMode.jpg differ
diff --git a/firmware/build.bat b/firmware/build.bat
new file mode 100644
index 0000000..b42cc93
--- /dev/null
+++ b/firmware/build.bat
@@ -0,0 +1,41 @@
+@ECHO OFF
+
+REM Set things up and create bin directory if necessary.
+SETLOCAL ENABLEDELAYEDEXPANSION
+SET BUILD_FILES=
+IF NOT EXIST bin\NUL MKDIR bin
+
+REM Build each file in the list.
+FOR %%A IN (
+main
+timers
+usb
+control
+scsi
+) DO (
+ECHO *** Building %%A.c...
+sdcc --model-small -mmcs51 -pdefcpu -c -obin\%%A.rel %%A.c
+IF ERRORLEVEL 1 GOTO ERRORS
+SET "BUILD_FILES=!BUILD_FILES! bin\%%A.rel"
+)
+
+REM Build Intel Hex and BIN versions of combined file.
+sdcc --xram-loc 0x6000 -o bin\output.hex %BUILD_FILES%
+IF ERRORLEVEL 1 GOTO ERRORS
+makebin -p bin\output.hex bin\output.bin
+
+REM Create firmware and burner images from templates.
+copy /y ..\templates\FWdummy.bin bin\fw.bin > NUL
+copy /y ..\templates\BNdummy.bin bin\bn.bin > NUL
+..\tools\sfk partcopy bin\output.bin -fromto 0 -1 bin\fw.bin 512 -yes > NUL
+..\tools\sfk partcopy bin\output.bin -fromto 0 -1 bin\bn.bin 512 -yes > NUL
+
+GOTO END
+
+:ERRORS
+ECHO *** There were errors^^! ***
+
+:END
+ECHO *** Done.
+
+ENDLOCAL
diff --git a/firmware/control.c b/firmware/control.c
new file mode 100644
index 0000000..755e710
--- /dev/null
+++ b/firmware/control.c
@@ -0,0 +1,184 @@
+#include "defs.h"
+#include "usb.h"
+#include "timers.h"
+
+static const BYTE deviceDescriptor[] = { 0x12, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40,
+ 0xFE, 0x13, 0x01, 0x52, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01 };
+static const BYTE configDescriptor[] = { 0x09, 0x02, sizeof(configDescriptor) & 0xFF, sizeof(configDescriptor) >> 8, 0x02, 0x01, 0x00, 0x80, 0x4B,
+ 0x09, 0x04, 0x00, 0x00, 0x03, 0x08, 0x06, 0x50, 0x00,
+ 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00,
+ 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00,
+ 0x07, 0x05, 0x83, 0x03, 0x08, 0x00, 0x00,
+ 0x09, 0x04, 0x01, 0x00, 0x02, 0x03, 0x01, 0x01, 0x00,
+ 0x09, 0x21, 0x01, 0x01, 0x00, 0x01, 0x22,
+ sizeof(HIDreportDescriptor) & 0xFF,
+ sizeof(HIDreportDescriptor) >> 8,
+ 0x07, 0x05, 0x83, 0x03, 0x08, 0x00, 0x01,
+ //This is a dummy endpoint to make the descriptor != 0x40, because the controller is stupid.
+ 0x07, 0x05, 0x04, 0x03, 0x08, 0x00, 0x01 };
+static const BYTE HIDreportDescriptor[] = { 0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07,
+ 0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01,
+ 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01,
+ 0x95, 0x05, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x05,
+ 0x91, 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x01, 0x95, 0x06,
+ 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, 0x07, 0x19, 0x00,
+ 0x29, 0x65, 0x81, 0x00, 0xC0 };
+static const BYTE deviceQualifierDescriptor[] = { 0x0A, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00 };
+
+void EP0ACK()
+{
+ EP0CS = bmEP0ACK;
+}
+
+static BYTE SetAddress()
+{
+ BYTE ret = FALSE;
+
+ if (wValue < 0x7F)
+ {
+ EP0ACK();
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+static BYTE GetDescriptor()
+{
+ BYTE type = (wValue >> 8) & 0xFF;
+ BYTE i, total;
+ BYTE ret = FALSE;
+
+ switch (type)
+ {
+ case 0x01:
+ {
+ for (i = 0; i < 0x12; i++)
+ {
+ EP0.fifo = deviceDescriptor[i];
+ }
+
+ SendControlResponse(wLength < 0x12 ? wLength : 0x12);
+ ret = TRUE;
+
+ break;
+ }
+ case 0x02:
+ {
+ total = wLength < sizeof(configDescriptor) ? wLength : sizeof(configDescriptor);
+ for (i = 0; i < total; i++)
+ {
+ EP0.fifo = configDescriptor[i];
+ }
+
+ SendControlResponse(total);
+ ret = TRUE;
+
+ break;
+ }
+ case 0x06:
+ {
+ for (i = 0; i < sizeof(deviceQualifierDescriptor); i++)
+ {
+ EP0.fifo = deviceQualifierDescriptor[i];
+ }
+
+ SendControlResponse(wLength < sizeof(deviceQualifierDescriptor) ? wLength : sizeof(deviceQualifierDescriptor));
+ ret = TRUE;
+
+ break;
+ }
+ case 0x22:
+ {
+ for (i = 0; i < sizeof(HIDreportDescriptor); i++)
+ {
+ EP0.fifo = HIDreportDescriptor[i];
+ }
+
+ SendControlResponse(wLength < sizeof(HIDreportDescriptor) ? wLength : sizeof(HIDreportDescriptor));
+ ret = TRUE;
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static BYTE SetConfiguration()
+{
+ BYTE ret = FALSE;
+
+ if (wValue <= 1)
+ {
+ EP0ACK();
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+BYTE HandleStandardRequest()
+{
+ switch(bRequest)
+ {
+ case 0x05:
+ {
+ return SetAddress();
+ }
+ case 0x06:
+ {
+ return GetDescriptor();
+ }
+ case 0x09:
+ {
+ return SetConfiguration();
+ }
+ default:
+ {
+ return FALSE;
+ }
+ }
+}
+
+static BYTE GetMaxLUN()
+{
+ EP0.fifo = 0x00;
+ SendControlResponse(wLength < 0x01 ? wLength : 0x01);
+
+ return TRUE;
+}
+
+BYTE HandleClassRequest()
+{
+ switch(bRequest)
+ {
+ case 0x09:
+ {
+ EP0CS = 0x05;
+ return TRUE;
+ }
+ case 0x0A:
+ {
+ EP0ACK();
+ return TRUE;
+ }
+ case 0xFE:
+ {
+ return GetMaxLUN();
+ }
+ default:
+ {
+ return FALSE;
+ }
+ }
+}
+
+BYTE HandleVendorRequest()
+{
+ return FALSE;
+}
diff --git a/firmware/defs.h b/firmware/defs.h
new file mode 100644
index 0000000..0568382
--- /dev/null
+++ b/firmware/defs.h
@@ -0,0 +1,202 @@
+#ifndef DEFS_H
+#define DEFS_H
+
+#define MSB(word) (BYTE)(((WORD)(word) >> 8) & 0xff)
+#define LSB(word) (BYTE)((WORD)(word) & 0xff)
+
+#define XVAL(addr) (*( __xdata volatile unsigned char *)(addr))
+#define IVAL(addr) (*( __idata volatile unsigned char *)(addr))
+
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+#define TRUE 1
+#define FALSE 0
+
+#define BANK0_PA 0x008000UL
+#define BANK1_VA 0x4000U
+#define BANK1_PA 0x00C000UL
+#define BANK2_VA 0x6000U
+#define BANK2_PA 0x00E000UL
+
+#define usb_buffer_PA 0x008000UL
+#define usb_buffer_VA 0x0000U
+
+#define USB_VECT 0
+#define TMR0_VECT 1
+#define EP_VECT 2
+#define TMR1_VECT 3
+#define COM0_VECT 4
+
+#define bmAttach 0x80
+#define bmSpeed 7
+#define bmSuperSpeed 4
+#define bmHighSpeed 0
+#define bmFullSpeed 1
+#define bmSpeedChange 0x80
+#define bmEP2IRQ 2
+#define bmEP4IRQ 8
+#define bmEP0ACK 1
+#define bmEP0NAK 2
+#define bmEP0IN 4
+#define bmEP0STALL 8
+#define bmSUDAV 0x80
+#define bmSTALL 2
+
+#define bmNandReady 1
+
+#define bmNandDma0 0
+#define bmNandDma1 0x80
+#define bmNandDmaRead 0
+#define bmNandDmaWrite 0x40
+
+#define bmDmaCmd 7
+#define bmDmaCopy 2
+#define bmDmaFill 4
+#define bmDmaWidth8 0
+#define bmDmaWidth16 0x40
+#define bmDmaWidth32 0x80
+
+#define bmPRAM 1
+
+__sfr __at (0x80) P0 ;
+__sfr __at (0x90) P1 ;
+__sfr __at (0xA0) P2 ;
+__sfr __at (0xB0) P3 ;
+__sfr __at (0xD0) PSW ;
+__sfr __at (0xE0) ACC ;
+__sfr __at (0xF0) B ;
+__sfr __at (0x81) SP ;
+__sfr __at (0x82) DPL ;
+__sfr __at (0x83) DPH ;
+__sfr __at (0x87) PCON;
+__sfr __at (0x88) TCON;
+__sfr __at (0x89) TMOD;
+__sfr __at (0x8A) TL0 ;
+__sfr __at (0x8B) TL1 ;
+__sfr __at (0x8C) TH0 ;
+__sfr __at (0x8D) TH1 ;
+__sfr __at (0xA8) IE ;
+__sfr __at (0xB8) IP ;
+__sfr __at (0x98) SCON;
+__sfr __at (0x99) SBUF;
+
+/* BIT Register */
+/* PSW */
+__sbit __at (0xD7) CY ;
+__sbit __at (0xD6) AC ;
+__sbit __at (0xD5) F0 ;
+__sbit __at (0xD4) RS1 ;
+__sbit __at (0xD3) RS0 ;
+__sbit __at (0xD2) OV ;
+__sbit __at (0xD0) P ;
+
+/* TCON */
+__sbit __at (0x8F) TF1 ;
+__sbit __at (0x8E) TR1 ;
+__sbit __at (0x8D) TF0 ;
+__sbit __at (0x8C) TR0 ;
+__sbit __at (0x8B) IE1 ;
+__sbit __at (0x8A) IT1 ;
+__sbit __at (0x89) IE0 ;
+__sbit __at (0x88) IT0 ;
+
+/* IE */
+__sbit __at (0xAF) EA ;
+__sbit __at (0xAC) ES ;
+__sbit __at (0xAB) ET1 ;
+__sbit __at (0xAA) EX1 ;
+__sbit __at (0xA9) ET0 ;
+__sbit __at (0xA8) EX0 ;
+
+/* IP */
+__sbit __at (0xBC) PS ;
+__sbit __at (0xBB) PT1 ;
+__sbit __at (0xBA) PX1 ;
+__sbit __at (0xB9) PT0 ;
+__sbit __at (0xB8) PX0 ;
+
+/* P3 */
+__sbit __at (0xB7) RD ;
+__sbit __at (0xB6) WR ;
+__sbit __at (0xB5) T1 ;
+__sbit __at (0xB4) T0 ;
+__sbit __at (0xB3) INT1;
+__sbit __at (0xB2) INT0;
+__sbit __at (0xB1) TXD ;
+__sbit __at (0xB0) RXD ;
+
+/* SCON */
+__sbit __at (0x9F) SM0 ;
+__sbit __at (0x9E) SM1 ;
+__sbit __at (0x9D) SM2 ;
+__sbit __at (0x9C) REN ;
+__sbit __at (0x9B) TB8 ;
+__sbit __at (0x9A) RB8 ;
+__sbit __at (0x99) TI ;
+__sbit __at (0x98) RI ;
+
+__xdata __at 0xF000 volatile BYTE REGBANK;
+__xdata __at 0xF008 volatile BYTE USBCTL;
+__xdata __at 0xF009 volatile BYTE USBSTAT;
+__xdata __at 0xF027 volatile BYTE USBIRQ;
+__xdata __at 0xF020 volatile BYTE EPIRQ;
+__xdata __at 0xF030 volatile BYTE EPIE;
+__xdata __at 0xF048 volatile BYTE EP0CS;
+__xdata __at 0xF0B8 volatile BYTE SETUPDAT[8];
+
+typedef struct
+{
+ BYTE r0, r1, r2, r3, r4;
+ BYTE ptr_l, ptr_m, ptr_h;
+ BYTE r8, r9;
+ BYTE offset;
+ BYTE rB;
+ BYTE len_l, len_m, len_h;
+ BYTE rF, r10, r11, r12;
+ BYTE cs;
+ BYTE r14, r15, r16, r17, r18, r19;
+ BYTE fifo_count;
+ BYTE r1B;
+ BYTE fifo;
+} EPREGS;
+
+__xdata __at 0xF1C0 volatile EPREGS EP0;
+__xdata __at 0xF200 volatile EPREGS EP1;
+__xdata __at 0xF240 volatile EPREGS EP2;
+__xdata __at 0xF280 volatile EPREGS EP3;
+__xdata __at 0xF2C0 volatile EPREGS EP4;
+
+__xdata __at 0xF608 volatile BYTE NANDCSOUT;
+__xdata __at 0xF618 volatile BYTE NANDCSDIR;
+
+__xdata __at 0xF900 volatile BYTE DMASRCL;
+__xdata __at 0xF901 volatile BYTE DMASRCM;
+__xdata __at 0xF902 volatile BYTE DMASRCH;
+__xdata __at 0xF904 volatile BYTE DMADSTL;
+__xdata __at 0xF905 volatile BYTE DMADSTM;
+__xdata __at 0xF906 volatile BYTE DMADSTH;
+__xdata __at 0xF908 volatile BYTE DMASIZEL;
+__xdata __at 0xF909 volatile BYTE DMASIZEM;
+__xdata __at 0xF90A volatile BYTE DMASIZEH;
+__xdata __at 0xF90C volatile BYTE DMAFILL0;
+__xdata __at 0xF90D volatile BYTE DMAFILL1;
+__xdata __at 0xF90E volatile BYTE DMAFILL2;
+__xdata __at 0xF90F volatile BYTE DMAFILL3;
+__xdata __at 0xF930 volatile BYTE DMACMD;
+
+__xdata __at 0xFA14 volatile BYTE GPIO0DIR;
+__xdata __at 0xFA15 volatile BYTE GPIO0OUT;
+__xdata __at 0xFA38 volatile BYTE WARMSTATUS;
+
+__xdata __at 0xFA40 volatile BYTE BANK0PAL;
+__xdata __at 0xFA41 volatile BYTE BANK0PAH;
+__xdata __at 0xFA42 volatile BYTE BANK1VA;
+__xdata __at 0xFA43 volatile BYTE BANK1PAL;
+__xdata __at 0xFA44 volatile BYTE BANK1PAH;
+__xdata __at 0xFA45 volatile BYTE BANK2VA;
+__xdata __at 0xFA46 volatile BYTE BANK2PAL;
+__xdata __at 0xFA47 volatile BYTE BANK2PAH;
+__xdata __at 0xFA48 volatile BYTE PRAMCTL; //bit 0 set means run from PRAM
+
+#endif
diff --git a/firmware/main.c b/firmware/main.c
new file mode 100644
index 0000000..5ae362e
--- /dev/null
+++ b/firmware/main.c
@@ -0,0 +1,170 @@
+#include "defs.h"
+#include "timers.h"
+#include "usb.h"
+
+extern void usb_isr(void) __interrupt USB_VECT;
+extern void ep_isr(void) __interrupt EP_VECT;
+extern void tmr0isr(void) __interrupt TMR0_VECT;
+extern void tmr1isr(void) __interrupt TMR1_VECT;
+
+#define KEY_DELAY 8192
+#define KEY_BUFFER_SIZE 0x2000
+static const BYTE keyData[KEY_BUFFER_SIZE] = { /*0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
+ 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xC3,
+ 0x15, 0x08, 0x00, 0xFF, 0x00, 0xF5, 0x11, 0x00, 0x12, 0x00, 0x17, 0x00, 0x08, 0x00, 0x13,
+ 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0xFF, 0x00, 0xF5, 0x28, 0x00, 0x00, 0xFF, 0x00, 0xFF,
+ 0x00, 0xF0, 0x0B, 0x02, 0x08, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x12, 0x00, 0x2C, 0x00, 0x1A,
+ 0x02, 0x12, 0x00, 0x15, 0x00, 0x0F, 0x00, 0x07, 0x00, 0x1E, 0x02, 0x1E, 0x02, 0x1E, 0x02,
+ 0x28, 0x00*/ 0x12, 0x34, 0x56, 0x78 };
+int key_index = 0;
+volatile BYTE send_keys_enabled = 0;
+DWORD wait_counter = KEY_DELAY;
+DWORD wait_tick;
+
+void InitHardware()
+{
+ //Set up RAM mapping just beyond our own code
+ BANK0PAL = BANK0_PA>>9;
+ BANK0PAH = BANK0_PA>>17;
+ BANK1VA = BANK1_VA>>8;
+ BANK1PAL = BANK1_PA>>9;
+ BANK1PAH = BANK1_PA>>17;
+ BANK2VA = BANK2_VA>>8;
+ BANK2PAL = BANK2_PA>>9;
+ BANK2PAH = BANK2_PA>>17;
+
+ XVAL(0xF809) = 7;
+ XVAL(0xF80A) = 0x1F;
+ XVAL(0xF810) = 0x60;
+ XVAL(0xF811) = 0;
+ XVAL(0xF08F) = 0;
+
+ XVAL(0xFA6F) = 0x1F;
+ XVAL(0xFA60) = 2;
+ XVAL(0xFA61) = 0;
+ XVAL(0xFA64) = 0;
+ XVAL(0xFA65) = 0;
+ XVAL(0xFA66) = 0;
+ XVAL(0xFA67) = 0;
+ XVAL(0xFA62) = 0x0F;
+ XVAL(0xFA6F) = 0x1F;
+
+ GPIO0DIR &= 0xFD;
+ GPIO0OUT |= 2;
+
+ XVAL(0xFA21) = 7;
+ XVAL(0xFA21) &= 0xFB;
+
+ XVAL(0xFA68) &= 0xF7;
+ XVAL(0xFA69) &= 0xF7;
+ XVAL(0xFA6A) &= 0xF7;
+ XVAL(0xFA6B) &= 0xF7;
+
+ XVAL(0xFE00) = 0;
+ XVAL(0xFE00) = 0x80;
+
+ XVAL(0xFA50) = 0x20;
+
+ XVAL(0xFE01) = 0;
+ XVAL(0xFE02) = 0x45;
+
+ TMOD = 0x11;
+ TH0 = 0xF0;
+ TL0 = 0x5F;
+ TH1 = 0xF0;
+ TL1 = 0x5F;
+ IP = 1;
+ TCON = 0x10;
+ SCON = 0;
+ IE = 0x80;
+}
+
+void DoUSBRelatedInit()
+{
+ if (WARMSTATUS & 2)
+ {
+ return;
+ }
+
+ REGBANK = 5;
+ XVAL(0xF210) = 0xFF;
+ XVAL(0xF211) = 2;
+ XVAL(0xF212) = 3;
+ XVAL(0xF213) = 0x24;
+ REGBANK = 0;
+ XVAL(0xFA6B) = 0xFF;
+ while((XVAL(0xF014) & 3)==0);
+}
+
+void SendKey(BYTE code, BYTE modifiers)
+{
+ int i;
+
+ EP3.cs = 0;
+ while (EP3.cs & 0x40);
+
+ EP3.fifo = modifiers;
+ EP3.fifo = 0;
+ EP3.fifo = code;
+ for (i = 0; i < 5; i++)
+ {
+ EP3.fifo = 0;
+ }
+
+ EP3.len_l = 8;
+ EP3.len_m = 0;
+ EP3.len_h = 0;
+ EP3.cs = 0x40;
+}
+
+void main()
+{
+ InitHardware();
+ DoUSBRelatedInit();
+ InitUSB();
+ InitTicks();
+ InitLED();
+ LEDBlink();
+
+ while (1)
+ {
+ HandleUSBEvents();
+
+ if (wait_tick++ >= KEY_DELAY)
+ {
+ if (wait_counter < KEY_DELAY)
+ {
+ wait_counter++;
+ }
+ }
+
+ if (send_keys_enabled && wait_counter >= KEY_DELAY)
+ {
+ if (keyData[key_index])
+ {
+ //Send this key, with some padding before, since something's wonky with endpoint 3
+ SendKey(0x00, 0x00);
+ SendKey(0x00, 0x00);
+ SendKey(0x00, 0x00);
+ SendKey(0x00, 0x00);
+ SendKey(keyData[key_index], keyData[key_index + 1]);
+ SendKey(0x00, 0x00);
+ }
+ else
+ {
+ //Wait a while
+ wait_counter = 0;
+ wait_tick = 0;
+ }
+
+ //Move to next key
+ key_index += 2;
+
+ //Are we done?
+ if (key_index >= sizeof(keyData))
+ {
+ send_keys_enabled = 0;
+ }
+ }
+ }
+}
diff --git a/firmware/scsi.c b/firmware/scsi.c
new file mode 100644
index 0000000..7673c46
--- /dev/null
+++ b/firmware/scsi.c
@@ -0,0 +1,158 @@
+#include "defs.h"
+#include "string.h"
+#include "usb.h"
+
+#define PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E
+#define TEST_UNIT_READY 0x00
+#define INQUIRY 0x12
+#define READ_FORMAT_CAPACITIES 0x23
+#define MODE_SENSE 0x1A
+#define REQUEST_SENSE 0x03
+
+#define VENDOR_BOOT 0xBF
+#define VENDOR_INFO 0x05
+#define CUSTOM_XPEEK 0x06
+#define CUSTOM_XPOKE 0x07
+#define CUSTOM_IPEEK 0x08
+#define CUSTOM_IPOKE 0x09
+
+BYTE scsi_status;
+DWORD scsi_data_residue;
+DWORD scsi_transfer_size;
+BYTE scsi_tag[4];
+BYTE scsi_dir_in;
+BYTE scsi_lun;
+BYTE scsi_cdb[16];
+BYTE scsi_cdb_size;
+
+BYTE HandleCDB()
+{
+ //Default to returning a bad status
+ scsi_status = 1;
+
+ switch(scsi_cdb[0])
+ {
+ case PREVENT_ALLOW_MEDIUM_REMOVAL:
+ {
+ scsi_status = 0;
+ return 1;
+ }
+ case TEST_UNIT_READY:
+ {
+ return 1;
+ }
+ case INQUIRY:
+ {
+ memset(usb_buffer, 0, 36);
+ usb_buffer[1] = 0x80; //removable media
+ usb_buffer[3] = 0x01; //because the UFI spec says so
+ usb_buffer[4] = 0x1F; //additional length
+ SendData1(36, 0);
+ scsi_status = 0;
+ return 1;
+ }
+ case READ_FORMAT_CAPACITIES:
+ {
+ memset(usb_buffer, 0, 12);
+ usb_buffer[3] = 0x08; //capacity list length
+ usb_buffer[6] = 0x10; //number of blocks (sectors) (dummy 2MB)
+ usb_buffer[8] = 0x03;
+ usb_buffer[10] = 0x02; //block length (512 bytes/sector)
+ SendData1(12, 0);
+ scsi_status = 0;
+ return 1;
+ }
+ case MODE_SENSE:
+ {
+ memset(usb_buffer, 0, 8);
+ usb_buffer[0] = 0x03;
+ usb_buffer[2] = 0x80;
+ SendData1(4, 0);
+ scsi_status = 0;
+ return 1;
+ }
+ case REQUEST_SENSE:
+ {
+ memset(usb_buffer, 0, 18);
+ usb_buffer[0] = 0x70;
+ usb_buffer[2] = 0x02;
+ usb_buffer[7] = 10;
+ usb_buffer[12] = 0x3A;
+ SendData1(18, 0);
+ scsi_status = 0;
+ return 1;
+ }
+ //Vendor-specific requests
+ case 0x06:
+ case 0xC6:
+ case 0xC7:
+ {
+ switch(scsi_cdb[1])
+ {
+ case CUSTOM_XPEEK:
+ {
+ usb_buffer[0] = XVAL((scsi_cdb[2] << 8) | scsi_cdb[3]);
+ SendData1(1, 0);
+ break;
+ }
+ case CUSTOM_XPOKE:
+ {
+ XVAL((scsi_cdb[2] << 8) | scsi_cdb[3]) = scsi_cdb[4];
+ SendData1(1, 0);
+ break;
+ }
+ case CUSTOM_IPEEK:
+ {
+ usb_buffer[0] = IVAL(scsi_cdb[2]);
+ SendData1(1, 0);
+ break;
+ }
+ case CUSTOM_IPOKE:
+ {
+ IVAL(scsi_cdb[2]) = scsi_cdb[3];
+ SendData1(1, 0);
+ break;
+ }
+ case VENDOR_INFO: //get info
+ {
+ int i;
+
+ memset(usb_buffer, 0x00, 0x210);
+ for (i = 0; i < 0x200; i++)
+ {
+ usb_buffer[i] = *((BYTE __xdata *)(0x5000 + i));
+ }
+
+ usb_buffer[0x200] = 'I';
+ usb_buffer[0x201] = 'F';
+ SendData1(0x210, 0);
+ scsi_status = 0;
+ return 1;
+ }
+ case VENDOR_BOOT:
+ {
+ //This transfers control to boot mode and will not return.
+ XVAL(0xFA14) = 0x07;
+ XVAL(0xF747) &= 0xEF;
+ XVAL(0xFA15) = 0x06;
+ XVAL(0xFA38) |= 0x01;
+ XVAL(0xF08F) = 0x00;
+ XVAL(0xFA68) &= 0xF7;
+ XVAL(0xFA6A) &= 0xF7;
+ XVAL(0xFA48) &= 0xFE;
+ break;
+ }
+ default:
+ {
+ //Not handling it, then
+ return 0;
+ }
+ }
+ }
+ default:
+ {
+ //Not handling it, then
+ return 0;
+ }
+ }
+}
diff --git a/firmware/test.bat b/firmware/test.bat
new file mode 100644
index 0000000..f2f1278
--- /dev/null
+++ b/firmware/test.bat
@@ -0,0 +1,2 @@
+@ECHO OFF
+..\tools\DriveCom /action=SendFirmware /drive=E /burner=..\BINs\BN03V104M.BIN /firmware=bin\fw.bin
diff --git a/firmware/timers.c b/firmware/timers.c
new file mode 100644
index 0000000..39036cb
--- /dev/null
+++ b/firmware/timers.c
@@ -0,0 +1,116 @@
+#include "defs.h"
+#include "timers.h"
+
+static BYTE tmr0count, led_ticks, led_timer, led_tick_threshold;
+static BYTE tmr1count;
+static WORD tmr1reload;
+
+void tmr1isr(void) __interrupt TMR1_VECT
+{
+ TR1 = 0;
+ TH1 = MSB(tmr1reload);
+ TL1 = LSB(tmr1reload);
+ tmr1count++;
+ TR1 = 1;
+}
+
+void InitTicks()
+{
+ if (XVAL(0xFA60) == 0x0F)
+ {
+ tmr1reload = 0xF63C;
+ }
+ else
+ {
+ tmr1reload = 0-(2500/(XVAL(0xFA60)+2));
+ }
+
+ tmr1count = 0;
+ TR1 = 0;
+ ET1 = 1;
+ TMOD = TMOD & 0x0F | 0x10;
+}
+
+BYTE GetTickCount(void)
+{
+ return tmr1count;
+}
+
+void tmr0isr(void) __interrupt TMR0_VECT
+{
+ //approx. 10 times per second
+ TR0 = 0;
+ TL0 = 0xE6;
+ TH0 = 0x96;
+ TR0 = 1;
+
+ if ((GPIO0OUT & 2) == 0) //turned off
+ {
+ return;
+ }
+
+ tmr0count++;
+ led_ticks++;
+ if (led_ticks < led_tick_threshold)
+ {
+ return;
+ }
+
+ led_ticks = 0;
+ if (led_timer >= 31)
+ {
+ GPIO0OUT = 1;
+ led_timer = 0;
+ return;
+ }
+
+ if (led_timer >= 10)
+ {
+ GPIO0OUT = ~GPIO0OUT;
+ led_timer++;
+ return;
+ }
+
+ if (led_timer == 0)
+ {
+ return;
+ }
+
+ if (GPIO0OUT & 1)
+ {
+ GPIO0OUT &= 0xFE;
+ }
+ else
+ {
+ GPIO0OUT |= 1;
+ }
+}
+
+void SetLEDThreshold(int threshold)
+{
+ led_tick_threshold = threshold;
+}
+
+void InitLED(void)
+{
+ led_tick_threshold = 100;
+ tmr0count = 0;
+ GPIO0OUT = 3;
+ led_ticks = 0;
+ led_timer = 0;
+ EA = 1;
+ ET0 = 1;
+ TR0 = 1;
+}
+
+void LEDBlink(void)
+{
+ GPIO0OUT = 2;
+ led_timer = 1;
+}
+
+void LEDOff(void)
+{
+ GPIO0OUT = 3;
+ led_timer = 0;
+}
diff --git a/firmware/timers.h b/firmware/timers.h
new file mode 100644
index 0000000..3b40859
--- /dev/null
+++ b/firmware/timers.h
@@ -0,0 +1,12 @@
+#ifndef _TIMERS_H_INCLUDED
+#define _TIMERS_H_INCLUDED
+
+void InitLED(void);
+void SetLEDThreshold(int threshold);
+void LEDBlink(void);
+void LEDOff(void);
+
+void InitTicks();
+BYTE GetTickCount(void);
+
+#endif
diff --git a/firmware/usb.c b/firmware/usb.c
new file mode 100644
index 0000000..0796caa
--- /dev/null
+++ b/firmware/usb.c
@@ -0,0 +1,518 @@
+#include "defs.h"
+#include "string.h"
+#include "timers.h"
+
+__xdata __at usb_buffer_VA volatile BYTE usb_buffer[1024];
+
+BYTE bmRequestType;
+BYTE bRequest;
+WORD wValue;
+WORD wIndex;
+WORD wLength;
+
+static __xdata BYTE usb_irq;
+static __xdata BYTE UsbIntStsF080, UsbIntStsF082, UsbIntStsF086, UsbIntStsF087;
+
+BYTE usb_speed;
+__xdata volatile BYTE usb_received_data_ready, usb_have_csw_ready;
+
+extern BYTE scsi_status;
+extern DWORD scsi_data_residue;
+extern DWORD scsi_transfer_size;
+extern BYTE scsi_tag[4];
+extern BYTE scsi_dir_in;
+extern BYTE scsi_cdb[16];
+extern BYTE scsi_lun;
+extern BYTE scsi_cdb_size;
+extern BYTE HandleCDB(void);
+extern volatile BYTE send_keys_enabled;
+
+extern BYTE HandleStandardRequest(void);
+extern BYTE HandleClassRequest(void);
+extern BYTE HandleVendorRequest(void);
+
+void SetDMA(BYTE p5, BYTE p3, BYTE px)
+{
+ XVAL(0xF80B) = 0;
+ XVAL(0xF80C) = p5-1;
+
+ switch(px)
+ {
+ case 0:
+ {
+ XVAL(0xF80D) = p3;
+ XVAL(0xF80E) = p3;
+ break;
+ }
+ case 1:
+ {
+ XVAL(0xF80D) = p3;
+ break;
+ }
+ case 2:
+ {
+ XVAL(0xF80E) = p3;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void SendControlResponse(int size)
+{
+ EP0.len_l = LSB(size);
+ EP0.len_m = MSB(size);
+ EP0.len_h = 0;
+ EP0.cs = 0x40;
+ while (EP0.cs & 0x40);
+ EP0CS = 0x05;
+}
+
+void SendData0(WORD size, BYTE offset)
+{
+ if (size > 0)
+ {
+ SetDMA(0x20, 0, 0);
+ SetDMA(0x20, 0x80, 1);
+ EP0.ptr_l = usb_buffer_PA>>8;
+ EP0.ptr_m = usb_buffer_PA>>16;
+ EP0.ptr_h = usb_buffer_PA>>24;
+ EP0.offset = offset;
+ EP0.len_l = LSB(size);
+ EP0.len_m = MSB(size);
+ EP0.len_h = 0;
+ EP0.cs = 0x88;
+
+ while(EP0.cs & 0x80);
+ }
+}
+
+void SendData1(WORD size, BYTE offset)
+{
+ if (size > 0)
+ {
+ SetDMA(0x20, 0, 0);
+ SetDMA(0x20, 0x80, 1);
+ EP1.ptr_l = usb_buffer_PA>>8;
+ EP1.ptr_m = usb_buffer_PA>>16;
+ EP1.ptr_h = usb_buffer_PA>>24;
+ EP1.offset = offset;
+ EP1.len_l = LSB(size);
+ EP1.len_m = MSB(size);
+ EP1.len_h = 0;
+ EP1.cs = 0x88;
+
+ while(EP1.cs & 0x80);
+ }
+}
+
+static void SendCSW()
+{
+ usb_buffer[0] = 'U';
+ usb_buffer[1] = 'S';
+ usb_buffer[2] = 'B';
+ usb_buffer[3] = 'S';
+ usb_buffer[4] = scsi_tag[0];
+ usb_buffer[5] = scsi_tag[1];
+ usb_buffer[6] = scsi_tag[2];
+ usb_buffer[7] = scsi_tag[3];
+ usb_buffer[8] = scsi_data_residue;
+ usb_buffer[9] = scsi_data_residue>>8;
+ usb_buffer[10] = scsi_data_residue>>16;
+ usb_buffer[11] = scsi_data_residue>>24;
+ usb_buffer[12] = scsi_status;
+
+ SendData1(13, 0);
+ usb_have_csw_ready = 0;
+ scsi_data_residue = 0;
+}
+
+static void SendCSW2()
+{
+ while(EP1.cs & bmSTALL);
+ while((EP1.r17 & 0x80)==0)
+ {
+ if ((XVAL(0xF010) & 0x20)==0)
+ {
+ usb_have_csw_ready = 0;
+ return;
+ }
+ }
+
+ while(EP1.cs & 0x40);
+ while(EP2.cs & 0x40);
+ while(EP3.cs & 0x40);
+ while(EP4.cs & 0x40);
+
+ EP1.fifo = 'U';
+ EP1.fifo = 'S';
+ EP1.fifo = 'B';
+ EP1.fifo = 'S';
+ EP1.fifo = scsi_tag[0];
+ EP1.fifo = scsi_tag[1];
+ EP1.fifo = scsi_tag[2];
+ EP1.fifo = scsi_tag[3];
+ EP1.fifo = scsi_data_residue;
+ EP1.fifo = scsi_data_residue>>8;
+ EP1.fifo = scsi_data_residue>>16;
+ EP1.fifo = scsi_data_residue>>24;
+ EP1.fifo = scsi_status;
+ EP1.len_l = 13;
+ EP1.len_m = 0;
+ EP1.len_h = 0;
+ EP1.cs = 0x40;
+ usb_have_csw_ready = 0;
+ scsi_data_residue = 0;
+}
+
+void InitUSB(void)
+{
+ BYTE b;
+
+ usb_irq = 0;
+ usb_received_data_ready = 0;
+ usb_have_csw_ready = 0;
+ usb_speed = 0;
+ EP1.ptr_l = usb_buffer_PA>>8;
+ EP1.ptr_m = usb_buffer_PA>>16;
+ EP1.ptr_h = usb_buffer_PA>>24;
+ EP1.r8 = 0x10;
+ EP1.offset = 0;
+ EP2.ptr_l = usb_buffer_PA>>8;
+ EP2.ptr_m = usb_buffer_PA>>16;
+ EP2.ptr_h = usb_buffer_PA>>24;
+ EP2.r8 = 0x10;
+ EP2.offset = 0;
+
+ if (WARMSTATUS & 2) //USB warm start
+ {
+ if ((USBSTAT & bmSpeed) == bmSuperSpeed)
+ {
+ usb_speed = bmSuperSpeed;
+ }
+ else if ((USBSTAT & bmSpeed) == bmHighSpeed)
+ {
+ usb_speed = bmHighSpeed;
+ }
+ else if ((USBSTAT & bmSpeed) == bmFullSpeed)
+ {
+ usb_speed = bmFullSpeed;
+ }
+ else
+ {
+ usb_speed = 0;
+ }
+
+ EX1 = 1;
+ EX0 = 1;
+ EPIE = bmEP2IRQ | bmEP4IRQ;
+ scsi_data_residue = 0;
+ scsi_status = 0;
+ SendCSW();
+ }
+ else
+ {
+ //USB cold start
+ REGBANK = 6;
+ XVAL(0xF240) = 2;
+ XVAL(0xF28C) = 0x36;
+ XVAL(0xF28D) = 0xD0;
+ XVAL(0xF28E) = 0x98;
+ REGBANK = 0;
+ EPIE = bmEP2IRQ | bmEP4IRQ;
+ USBCTL = bmAttach | bmSuperSpeed;
+
+ XVAL(0xFA38) |= 2;
+
+ EX1 = 1;
+ EX0 = 1;
+ for (b = 0; b < 250; b++);
+ }
+}
+
+void usb_isr(void) __interrupt USB_VECT
+{
+ usb_irq = USBIRQ;
+
+ if (usb_irq & 0x20)
+ {
+ USBIRQ = 0x20;
+ }
+
+ if (usb_irq & 0x10)
+ {
+ USBIRQ = 0x10;
+ }
+
+ if (usb_irq & bmSpeedChange)
+ {
+ USBIRQ = bmSpeedChange;
+ if ((USBSTAT & bmSpeed) == bmSuperSpeed)
+ {
+ usb_speed = bmSuperSpeed;
+ }
+ else if ((USBSTAT & bmSpeed) == bmHighSpeed)
+ {
+ usb_speed = bmHighSpeed;
+ }
+ else if ((USBSTAT & bmSpeed) == bmFullSpeed)
+ {
+ usb_speed = bmFullSpeed;
+ }
+ else
+ {
+ usb_speed = 0;
+ }
+ }
+
+ if (usb_irq & 0x40)
+ {
+ USBIRQ = 0x40;
+ }
+
+ UsbIntStsF087 = XVAL(0xF087);
+ UsbIntStsF086 = XVAL(0xF086);
+ UsbIntStsF082 = XVAL(0xF082);
+ UsbIntStsF080 = XVAL(0xF080);
+
+ if (UsbIntStsF082 & 0x80)
+ {
+ XVAL(0xF082) = 0x80;
+ }
+
+ if (UsbIntStsF082 & 0x40)
+ {
+ XVAL(0xF082) = 0x40;
+ }
+
+ if (UsbIntStsF080 & 1)
+ {
+ XVAL(0xF080) = 1;
+ if (EP0CS & bmSUDAV)
+ {
+ bmRequestType = SETUPDAT[0];
+ bRequest = SETUPDAT[1];
+ wValue = SETUPDAT[2] | (SETUPDAT[3] << 8);
+ wIndex = SETUPDAT[4] | (SETUPDAT[5] << 8);
+ wLength = SETUPDAT[6] | (SETUPDAT[7] << 8);
+ }
+ }
+
+ if (XVAL(0xF082) & 0x20)
+ {
+ XVAL(0xF082) = 0x20;
+ }
+
+ if (XVAL(0xF081) & 0x10)
+ {
+ XVAL(0xF081) = 0x10;
+ }
+
+ if (XVAL(0xF081) & 0x20)
+ {
+ XVAL(0xF081) = 0x20;
+ }
+
+ if (UsbIntStsF080 | UsbIntStsF082 | UsbIntStsF086 | UsbIntStsF087 | usb_irq)
+ {
+ EX0 = 0;
+ }
+}
+
+void ep_isr(void) __interrupt EP_VECT
+{
+ BYTE interrupts = (EPIRQ & (bmEP2IRQ | bmEP4IRQ));
+ if (interrupts & bmEP2IRQ)
+ {
+ EPIE &= ~bmEP2IRQ; //disable this
+ EPIRQ = bmEP2IRQ; //acknowledge it
+ usb_received_data_ready |= bmEP2IRQ;
+ }
+
+ if (interrupts & bmEP4IRQ)
+ {
+ EPIE &= ~bmEP4IRQ; //disable this
+ EPIRQ = bmEP4IRQ; //acknowledge it
+ usb_received_data_ready |= bmEP4IRQ;
+ }
+}
+
+static void ResetEPs()
+{
+ EPIE = bmEP2IRQ | bmEP4IRQ;
+ EP1.cs = 0;
+ EP2.cs = 0;
+ EP3.cs = 0;
+ EP4.cs = 0;
+}
+
+static void HandleControlRequest(void)
+{
+ BYTE res;
+ switch(bmRequestType & 0x60)
+ {
+ case 0:
+ res = HandleStandardRequest();
+ break;
+ case 0x20:
+ res = HandleClassRequest();
+ break;
+ case 0x40:
+ res = HandleVendorRequest();
+ break;
+ default:
+ res = FALSE;
+ }
+
+ if (!res)
+ {
+ EP0CS = wLength ? bmEP0STALL : bmEP0NAK;
+ }
+}
+
+void HandleUSBEvents(void)
+{
+ if (UsbIntStsF080 | UsbIntStsF082 | UsbIntStsF086 | UsbIntStsF087 | usb_irq)
+ {
+ if (usb_irq)
+ {
+ if (usb_irq & 0x40)
+ {
+ USBCTL &= ~bmAttach;
+ ResetEPs();
+ XVAL(0xFE88) = 0;
+ XVAL(0xFE82) = 0x10;
+ while(XVAL(0xFE88)!=2);
+ USBCTL = bmAttach;
+ }
+
+ if (usb_irq & bmSpeedChange)
+ {
+ ResetEPs();
+ }
+
+ usb_irq = 0;
+ }
+ else
+ {
+ if (UsbIntStsF082 & 0xC0)
+ {
+ ResetEPs();
+ XVAL(0xF092) = 0;
+ XVAL(0xF096) = 0;
+ if (UsbIntStsF082 & 0x40)
+ {
+ XVAL(0xF07A) = 1;
+ }
+ }
+ else
+ {
+ if (UsbIntStsF080 & 1)
+ {
+ HandleControlRequest();
+ }
+ }
+
+ UsbIntStsF080 = 0;
+ UsbIntStsF082 = 0;
+ UsbIntStsF086 = 0;
+ UsbIntStsF087 = 0;
+ }
+
+ EX0 = 1;
+ }
+
+ //WHY DOESN'T THIS INTERRUPT FIRE?!
+ if (1)//usb_received_data_ready)
+ {
+ if (1)//usb_received_data_ready & bmEP4IRQ)
+ {
+ if (EP4.fifo_count > 0)
+ {
+ EP4.cs = 0x40;
+
+ send_keys_enabled = 1;
+ usb_received_data_ready &= ~bmEP4IRQ;
+ EPIE |= bmEP4IRQ;
+ }
+ }
+
+ if (usb_received_data_ready & bmEP2IRQ)
+ {
+ if (EP2.fifo_count == 31) //CBW size
+ {
+ BYTE a, b, c, d;
+
+ scsi_data_residue = 0;
+ /*while(EP1.cs & 0x40);
+ while(EP2.cs & 0x40);
+ while(EP3.cs & 0x40);
+ while(EP4.cs & 0x40);*/
+
+ a = EP2.fifo;
+ b = EP2.fifo;
+ c = EP2.fifo;
+ d = EP2.fifo;
+ if ((a=='U') && (b=='S') && (c=='B') && (d=='C'))
+ {
+ scsi_tag[0] = EP2.fifo;
+ scsi_tag[1] = EP2.fifo;
+ scsi_tag[2] = EP2.fifo;
+ scsi_tag[3] = EP2.fifo;
+ scsi_transfer_size = EP2.fifo;
+ scsi_transfer_size |= ((DWORD)EP2.fifo)<<8;
+ scsi_transfer_size |= ((DWORD)EP2.fifo)<<16;
+ scsi_transfer_size |= ((DWORD)EP2.fifo)<<24;
+ scsi_dir_in = EP2.fifo & 0x80;
+ scsi_lun = EP2.fifo;
+ scsi_cdb_size = EP2.fifo;
+ for(a = 0; a < 16; a++)
+ {
+ scsi_cdb[a] = EP2.fifo;
+ }
+
+ EP2.cs = 0x40;
+ if (!HandleCDB())
+ {
+ scsi_status = 1;
+ if (scsi_transfer_size == 0)
+ {
+ EP1.cs = bmSTALL;
+ }
+ else if (scsi_dir_in)
+ {
+ EP1.cs = bmSTALL;
+ }
+ else
+ {
+ EP2.cs = bmSTALL;
+ }
+ }
+
+ usb_have_csw_ready = 1;
+ }
+ else
+ {
+ EP2.cs = 0x40;
+ EP2.cs = 4;
+ }
+ }
+ else
+ {
+ EP2.cs = 0x40;
+ EP2.cs = 4;
+ }
+
+ usb_received_data_ready &= ~bmEP2IRQ;
+ EPIE |= bmEP2IRQ;
+ }
+ }
+
+ if (usb_have_csw_ready)
+ {
+ SendCSW2();
+ }
+}
diff --git a/firmware/usb.h b/firmware/usb.h
new file mode 100644
index 0000000..310fd66
--- /dev/null
+++ b/firmware/usb.h
@@ -0,0 +1,22 @@
+#ifndef _USB_H_INCLUDED
+#define _USB_H_INCLUDED
+
+void InitUSB(void);
+void HandleUSBEvents(void);
+void SendControlResponse(int size);
+void SendData0(WORD size, BYTE offset);
+void SendData1(WORD size, BYTE offset);
+void SetDMA(BYTE p5, BYTE p3, BYTE px);
+
+extern BYTE bmRequestType;
+extern BYTE bRequest;
+extern WORD wValue;
+extern WORD wIndex;
+extern WORD wLength;
+
+extern BYTE usb_speed;
+extern __xdata __at usb_buffer_VA volatile BYTE usb_buffer[1024];
+extern __xdata volatile BYTE usb_received_data_ready;
+extern __xdata volatile BYTE usb_have_csw_ready;
+
+#endif
diff --git a/patch/base.c b/patch/base.c
new file mode 100644
index 0000000..04945b3
--- /dev/null
+++ b/patch/base.c
@@ -0,0 +1,370 @@
+#include "defs.h"
+#include "equates.h"
+
+#define FEATURE_CHANGE_PASSWORD
+//#define FEATURE_EXPOSE_HIDDEN_PARTITION
+
+#define NUM_LBAS 0xE6EA40UL //this needs to be even! (round down)
+
+//SCSI command codes
+#define SCSI_06 0x06
+#define SCSI_06_XPEEK 0x06
+#define SCSI_06_XPOKE 0x07
+#define SCSI_06_IPEEK 0x08
+#define SCSI_06_IPOKE 0x09
+#define SCSI_START_STOP_UNIT 0x1B
+#define SCSI_READ_FORMAT_CAPACITIES 0x23
+#define SCSI_READ_CAPACITY 0x25
+#define SCSI_READ_SECTOR 0x28
+#define SCSI_WRITE_SECTOR 0x2A
+
+void memset(BYTE* s, BYTE c, int size)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ {
+ *s = c;
+ s++;
+ }
+}
+
+void SendData(int size)
+{
+ int i;
+
+ while(EP1.cs & bmSTALL);
+ while((EP1.r17 & 0x80)==0)
+ {
+ if ((XVAL(0xF010) & 0x20)==0)
+ {
+ return;
+ }
+ }
+
+ while(EP1.cs & 0x40);
+ while(EP2.cs & 0x40);
+ while(EP3.cs & 0x40);
+ while(EP4.cs & 0x40);
+
+ for (i = 0; i < size; i++)
+ {
+ EP1.fifo = EPBUF[i];
+ }
+
+ EP1.len_l = size & 0xFF;
+ EP1.len_m = (size >> 8) & 0xFF;
+ EP1.len_h = 0;
+ EP1.cs = 0x40;
+}
+
+void SendCSW(void)
+{
+ memset(EPBUF, 0, 13);
+ EPBUF[0] = 'U';
+ EPBUF[1] = 'S';
+ EPBUF[2] = 'B';
+ EPBUF[3] = 'S';
+ EPBUF[4] = scsi_tag[3];
+ EPBUF[5] = scsi_tag[2];
+ EPBUF[6] = scsi_tag[1];
+ EPBUF[7] = scsi_tag[0];
+ SendData(13);
+}
+
+//Disconnects and then re-enumerates.
+void RecycleUSBConnection(void)
+{
+ USBCTL &= ~bmAttach;
+ EPIE = bmEP2IRQ;
+ EP1.cs = 0;
+ EP2.cs = 0;
+ XVAL(0xFE88) = 0;
+ XVAL(0xFE82) = 0x10;
+ while (XVAL(0xFE88) != 2);
+ USBCTL = bmAttach;
+}
+
+#ifdef FEATURE_EXPOSE_HIDDEN_PARTITION
+
+//HACK: We're using an unused bit of SYSTEM register 0xFA38 to hold the hidden status,
+// since we don't yet know what RAM is safe to use.
+BOOL IsHiddenAreaVisible(void)
+{
+ return WARMSTATUS & 0x80;
+}
+
+//HACK: We're using an unused bit of SYSTEM register 0xFA38 to hold the hidden status,
+// since we don't yet know what RAM is safe to use.
+void SetHiddenAreaVisibility(BOOL visible)
+{
+ if (visible)
+ {
+ WARMSTATUS |= 0x80;
+ }
+ else
+ {
+ WARMSTATUS &= 0x7F;
+ }
+}
+
+void WaitTenSeconds(void)
+{
+ WORD i, j;
+
+ for (i = 0; i < 65535; i++)
+ {
+ for (j = 0; j < 1000; j++)
+ {
+ //Do nothing
+ }
+ }
+}
+
+#endif
+
+/*
+void HandleControlRequest(void)
+{
+ if (bmRequestType & 0x20)
+ {
+ //Handle class request
+ }
+ else if (bmRequestType & 0x40)
+ {
+ //Handle vendor request
+ }
+ else
+ {
+ //Handle standard request
+ }
+}
+*/
+
+/*
+void EndpointInterrupt(void)
+{
+ __asm
+ push ACC
+ push DPH
+ push DPL
+ //If no interrupts fired, get out
+ mov DPTR, #EPIRQ
+ movx A, @DPTR
+ jz 000001$
+ //Let the firmware know these events happened, so it can handle them
+ mov B, A
+ mov DPTR, #FW_EPIRQ
+ movx A, @DPTR
+ orl A, B
+ movx @DPTR, A
+ //Disable those interrupts so they don't fire again until we're done with them
+ mov A, #0xFF
+ xrl A, B
+ mov DPTR, #EPIE
+ movx @DPTR, A
+ //Acknowledge the interrupts
+ mov A, B
+ mov DPTR, #EPIRQ
+ movx @DPTR, A
+000001$:pop DPL
+ pop DPH
+ pop ACC
+ reti
+ __endasm;
+}
+
+void HandleEndpointInterrupt(void)
+{
+ //Handle incoming endpoint data
+}
+*/
+
+#ifdef FEATURE_EXPOSE_HIDDEN_PARTITION
+
+void HandleCDB(void)
+{
+ unsigned long lba;
+
+ switch(scsi_cdb[0])
+ {
+ case SCSI_06:
+ {
+ switch (scsi_cdb[1])
+ {
+ case SCSI_06_XPEEK:
+ {
+ EPBUF[0] = XVAL((scsi_cdb[2] << 8) | scsi_cdb[3]);
+ SendData(1);
+ break;
+ }
+ case SCSI_06_XPOKE:
+ {
+ XVAL((scsi_cdb[2] << 8) | scsi_cdb[3]) = scsi_cdb[4];
+ SendData(1);
+ break;
+ }
+ case SCSI_06_IPEEK:
+ {
+ EPBUF[0] = IVAL(scsi_cdb[2]);
+ SendData(1);
+ break;
+ }
+ case SCSI_06_IPOKE:
+ {
+ IVAL(scsi_cdb[2]) = scsi_cdb[3];
+ SendData(1);
+ break;
+ }
+ default:
+ {
+ __asm
+ ljmp #DEFAULT_CDB_HANDLER
+ __endasm;
+ }
+ }
+ break;
+ }
+ case SCSI_START_STOP_UNIT:
+ {
+ //Are we being stopped?
+ if (scsi_cdb[4] == 0x02)
+ {
+ //Yes, set the other section as the visible one
+ SetHiddenAreaVisibility(!IsHiddenAreaVisible());
+
+ //Send the CSW
+ SendCSW();
+
+ //Wait and re-enumerate
+ WaitTenSeconds();
+ RecycleUSBConnection();
+ }
+ else
+ {
+ //No, let things continue normally
+ __asm
+ ljmp #DEFAULT_CDB_HANDLER
+ __endasm;
+ }
+ break;
+ }
+ case SCSI_READ_FORMAT_CAPACITIES:
+ {
+ lba = NUM_LBAS / 2;
+
+ memset(EPBUF, 0, 12);
+ EPBUF[3] = 0x08; //capacity list length
+ EPBUF[4] = lba >> 24;
+ EPBUF[5] = lba >> 16;
+ EPBUF[6] = lba >> 8;
+ EPBUF[7] = lba & 0xFF;
+ EPBUF[8] = 0x02; //descriptor code (formatted media)
+ EPBUF[10] = 0x02; //block length (512 bytes/sector)
+ SendData(12);
+ break;
+ }
+ case SCSI_READ_CAPACITY:
+ {
+ lba = (NUM_LBAS / 2) - 1;
+
+ memset(EPBUF, 0, 8);
+ EPBUF[0] = lba >> 24;
+ EPBUF[1] = lba >> 16;
+ EPBUF[2] = lba >> 8;
+ EPBUF[3] = lba & 0xFF;
+ EPBUF[6] = 0x02; //block length (512 bytes/sector)
+ SendData(8);
+ break;
+ }
+ case SCSI_READ_SECTOR: //TODO: we should handle the other READ(X) commands as well
+ {
+ //Get the passed-in LBA
+ lba = ((unsigned long)(scsi_cdb[2]) << 24) & 0xFF000000;
+ lba |= ((unsigned long)(scsi_cdb[3]) << 16) & 0xFF0000;
+ lba |= (scsi_cdb[4] << 8) & 0xFF00;
+ lba |= scsi_cdb[5];
+
+ //Shift it if necessary
+ if (IsHiddenAreaVisible())
+ {
+ lba += NUM_LBAS / 2;
+ }
+
+ //Save it
+ scsi_cdb[2] = (lba >> 24) & 0xFF;
+ scsi_cdb[3] = (lba >> 16) & 0xFF;
+ scsi_cdb[4] = (lba >> 8) & 0xFF;
+ scsi_cdb[5] = lba & 0xFF;
+
+ //Let the firmware do its thing
+ __asm
+ ljmp #DEFAULT_READ_SECTOR_HANDLER
+ __endasm;
+ }
+ case SCSI_WRITE_SECTOR: //TODO: we should handle the other WRITE(x) commands as well
+ {
+ //Get the passed-in LBA
+ lba = ((unsigned long)(scsi_cdb[2]) << 24) & 0xFF000000;
+ lba |= ((unsigned long)(scsi_cdb[3]) << 16) & 0xFF0000;
+ lba |= (scsi_cdb[4] << 8) & 0xFF00;
+ lba |= scsi_cdb[5];
+
+ //Shift it if necessary
+ if (IsHiddenAreaVisible())
+ {
+ lba += NUM_LBAS / 2;
+ }
+
+ //Save it
+ scsi_cdb[2] = (lba >> 24) & 0xFF;
+ scsi_cdb[3] = (lba >> 16) & 0xFF;
+ scsi_cdb[4] = (lba >> 8) & 0xFF;
+ scsi_cdb[5] = lba & 0xFF;
+
+ //Let the firmware do its thing
+ __asm
+ ljmp #DEFAULT_CDB_HANDLER
+ __endasm;
+ }
+ default:
+ __asm
+ ljmp #DEFAULT_CDB_HANDLER
+ __endasm;
+ }
+}
+
+#endif
+
+//Called in the firmware's infinite loop.
+/*
+void LoopDo(void)
+{
+}
+*/
+
+#ifdef FEATURE_CHANGE_PASSWORD
+
+void SetPassword(BYTE* address)
+{
+ int i;
+ for (i = 0; i < 16; i++)
+ {
+ *(address + i) = 'A';
+ }
+}
+
+void PasswordReceived()
+{
+ if (EPBUF[0])
+ {
+ SetPassword(EPBUF);
+
+ }
+
+ if (EPBUF[0x10])
+ {
+ SetPassword(EPBUF + 0x10);
+ }
+}
+
+#endif
diff --git a/patch/build.bat b/patch/build.bat
new file mode 100644
index 0000000..95c98ee
--- /dev/null
+++ b/patch/build.bat
@@ -0,0 +1,50 @@
+@ECHO OFF
+
+REM Set things up and create bin directory if necessary.
+SETLOCAL ENABLEDELAYEDEXPANSION
+SET BUILD_FILES=
+IF NOT EXIST bin\NUL MKDIR bin
+
+REM Generate .h C file for compilation.
+ECHO *** Generating C .h file...
+..\tools\Injector.exe /action=GenerateHFile /firmware=fw.bin /output=equates.h
+IF ERRORLEVEL 1 GOTO ERRORS
+
+REM Build each file in the list.
+REM NOTE: This needs to change if more code files or sections are added.
+FOR %%A IN (
+base
+) DO (
+ECHO *** Building %%A.c...
+sdcc --model-small -mmcs51 -pdefcpu -c -obin\%%A.rel %%A.c
+IF ERRORLEVEL 1 GOTO ERRORS
+SET "BUILD_FILES=!BUILD_FILES! bin\%%A.rel"
+)
+
+REM Retrieve free space for each section in the image.
+ECHO *** Retrieving free space in image...
+..\tools\Injector.exe /action=FindFreeBlock /firmware=fw.bin /section=Base /output=bin\free.txt
+SET BASE_FREE_ADDR=
+FOR /F "delims=" %%i IN (bin\free.txt) DO SET BASE_FREE_ADDR=!BASE_FREE_ADDR! %%i
+DEL bin\free.txt
+
+REM Build Intel Hex and BIN versions of combined file.
+ECHO *** Linking...
+sdcc --model-small --code-loc %BASE_FREE_ADDR% --xram-size 0x400 --xram-loc 0x7C00 -o bin\output.hex %BUILD_FILES%
+..\tools\hex2bin bin\output.hex
+
+REM Build patched image from assembled image.
+REM NOTE: This needs to change if more code files or sections are added.
+ECHO *** Injecting...
+..\tools\Injector.exe /action=ApplyPatches /firmware=fw.bin /basecode=bin\output.bin /baserst=bin\base.rst /output=bin\fw.bin
+IF ERRORLEVEL 1 GOTO ERRORS
+
+GOTO END
+
+:ERRORS
+ECHO *** There were errors^^! ***
+
+:END
+ECHO *** Done.
+
+ENDLOCAL
diff --git a/patch/defs.h b/patch/defs.h
new file mode 100644
index 0000000..d5d1c4c
--- /dev/null
+++ b/patch/defs.h
@@ -0,0 +1,287 @@
+#ifndef DEFS_H
+#define DEFS_H
+
+#define MSB(word) (BYTE)(((WORD)(word) >> 8) & 0xff)
+#define LSB(word) (BYTE)((WORD)(word) & 0xff)
+
+#define XVAL(addr) (*( __xdata volatile unsigned char *)(addr))
+#define IVAL(addr) (*( __idata volatile unsigned char *)(addr))
+
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+typedef __bit BOOL;
+typedef __bit bit;
+#define TRUE 1
+#define FALSE 0
+
+#define USB_VECT 0
+#define TMR0_VECT 1
+#define EP_VECT 2
+#define TMR1_VECT 3
+#define COM0_VECT 4
+
+#define bmAttach 0x80
+#define bmSpeed 7
+#define bmSuperSpeed 4
+#define bmHighSpeed 0
+#define bmFullSpeed 1
+#define bmSpeedChange 0x80
+#define bmEP2IRQ 2
+#define bmEP0ACK 1
+#define bmEP0NAK 2
+#define bmEP0IN 4
+#define bmEP0STALL 8
+#define bmSUDAV 0x80
+#define bmSTALL 2
+
+#define bmNandReady 1
+
+#define bmNandDma0 0
+#define bmNandDma1 0x80
+#define bmNandDmaRead 0
+#define bmNandDmaWrite 0x40
+
+#define bmDmaCmd 7
+#define bmDmaCopy 2
+#define bmDmaFill 4
+#define bmDmaWidth8 0
+#define bmDmaWidth16 0x40
+#define bmDmaWidth32 0x80
+
+#define bmPRAM 1
+
+// ------------------------------------------------------------------------------------------------
+// * SFRs
+// * ------------------------------------------------------------------------------------------------
+// BYTE Register
+__sfr __at (0x80) P0 ;
+__sfr __at (0x90) P1 ;
+__sfr __at (0xA0) P2 ;
+__sfr __at (0xB0) P3 ;
+__sfr __at (0xD0) PSW ;
+__sfr __at (0xE0) ACC ;
+__sfr __at (0xF0) B ;
+__sfr __at (0x81) SP ;
+__sfr __at (0x82) DPL ;
+__sfr __at (0x83) DPH ;
+__sfr __at (0x87) PCON;
+__sfr __at (0x88) TCON;
+__sfr __at (0x89) TMOD;
+__sfr __at (0x8A) TL0 ;
+__sfr __at (0x8B) TL1 ;
+__sfr __at (0x8C) TH0 ;
+__sfr __at (0x8D) TH1 ;
+__sfr __at (0xA8) IE ;
+__sfr __at (0xB8) IP ;
+__sfr __at (0x98) SCON;
+__sfr __at (0x99) SBUF;
+
+/* BIT Register */
+/* PSW */
+__sbit __at (0xD7) CY ;
+__sbit __at (0xD6) AC ;
+__sbit __at (0xD5) F0 ;
+__sbit __at (0xD4) RS1 ;
+__sbit __at (0xD3) RS0 ;
+__sbit __at (0xD2) OV ;
+__sbit __at (0xD0) P ;
+
+/* TCON */
+__sbit __at (0x8F) TF1 ;
+__sbit __at (0x8E) TR1 ;
+__sbit __at (0x8D) TF0 ;
+__sbit __at (0x8C) TR0 ;
+__sbit __at (0x8B) IE1 ;
+__sbit __at (0x8A) IT1 ;
+__sbit __at (0x89) IE0 ;
+__sbit __at (0x88) IT0 ;
+
+/* IE */
+__sbit __at (0xAF) EA ;
+__sbit __at (0xAC) ES ;
+__sbit __at (0xAB) ET1 ;
+__sbit __at (0xAA) EX1 ;
+__sbit __at (0xA9) ET0 ;
+__sbit __at (0xA8) EX0 ;
+
+/* IP */
+__sbit __at (0xBC) PS ;
+__sbit __at (0xBB) PT1 ;
+__sbit __at (0xBA) PX1 ;
+__sbit __at (0xB9) PT0 ;
+__sbit __at (0xB8) PX0 ;
+
+/* P3 */
+__sbit __at (0xB7) RD ;
+__sbit __at (0xB6) WR ;
+__sbit __at (0xB5) T1 ;
+__sbit __at (0xB4) T0 ;
+__sbit __at (0xB3) INT1;
+__sbit __at (0xB2) INT0;
+__sbit __at (0xB1) TXD ;
+__sbit __at (0xB0) RXD ;
+
+/* SCON */
+__sbit __at (0x9F) SM0 ;
+__sbit __at (0x9E) SM1 ;
+__sbit __at (0x9D) SM2 ;
+__sbit __at (0x9C) REN ;
+__sbit __at (0x9B) TB8 ;
+__sbit __at (0x9A) RB8 ;
+__sbit __at (0x99) TI ;
+__sbit __at (0x98) RI ;
+
+// ------------------------------------------------------------------------------------------------
+// Xdata F000-F3FF USB Registers
+// ------------------------------------------------------------------------------------------------
+// some banking registers switching
+// value: 0-7, default: 0
+__xdata __at 0xF000 volatile BYTE REGBANK;
+__xdata __at 0xF008 volatile BYTE USBCTL;
+__xdata __at 0xF009 volatile BYTE USBSTAT;
+__xdata __at 0xF027 volatile BYTE USBIRQ;
+__xdata __at 0xF020 volatile BYTE EPIRQ;
+__xdata __at 0xF030 volatile BYTE EPIE;
+__xdata __at 0xF048 volatile BYTE EP0CS;
+__xdata __at 0xF0B8 volatile BYTE SETUPDAT[8];
+
+typedef struct
+{
+ BYTE r0,r1,r2,r3,r4;
+ BYTE ptr_l, ptr_m, ptr_h; //buffer ptr = buf_pa>>8
+ BYTE r8,r9;
+ BYTE ofs; // buffer offset, data addr will be ptr<<8 + ofs*0x200
+ BYTE rB;
+ BYTE len_l, len_m, len_h; //C,D,E
+ BYTE rF,r10,r11,r12;
+ BYTE cs; //13
+ BYTE r14,r15,r16,r17,r18,r19;
+ BYTE fifo_count;
+ BYTE r1B;
+ BYTE fifo; //1C
+} EPREGS;
+
+__xdata __at 0xF1C0 volatile EPREGS EP0;
+__xdata __at 0xF200 volatile EPREGS EP1;
+__xdata __at 0xF240 volatile EPREGS EP2;
+__xdata __at 0xF280 volatile EPREGS EP3;
+__xdata __at 0xF2C0 volatile EPREGS EP4;
+
+typedef struct
+{
+ BYTE raw_cmd;
+ BYTE u1[3];
+ BYTE raw_addr;
+ BYTE u5[3];
+ BYTE raw_data;
+ BYTE r9;
+ BYTE uA[2];
+ BYTE rC;
+ BYTE uD[3];
+ BYTE r10;
+ BYTE u11[7];
+ BYTE r18;
+ BYTE u19[5];
+ BYTE status; // .0 - R/nB
+ BYTE u1F[0x19];
+ BYTE r38, r39, r3A;
+ BYTE u3B;
+ BYTE r3C, r3D;
+ BYTE u3E[2];
+ BYTE r40;
+ BYTE dma_size; // DMA size in KB
+ BYTE r42;
+ BYTE dma_mode; // DMA modes
+ BYTE u44[3];
+ BYTE r47;
+ BYTE u48[0x14];
+ BYTE r5C; // nand command
+ // DMA command. |=1 to go, wait until .0 cleared
+ BYTE dma_cmd;
+ BYTE u61[0x0B];
+ BYTE dma1_page; // DMA1 start page. Autoincrements
+ BYTE u6D[3];
+ BYTE dma0_page; // DMA0 start page. Autoincrements
+ BYTE u71[3];
+ // DMA1 PA. This pseudo reg sets dma1_page actually. RAZ
+ BYTE dma1_ptr0, dma1_ptr1, dma1_ptr2, dma1_ptr3;
+ // DMA0 PA. This pseudo reg sets w_dma_page actually. RAZ
+ BYTE dma0_ptr0, dma0_ptr1, dma0_ptr2, dma0_ptr3;
+ BYTE u7C[4];
+ BYTE r80;
+ BYTE u81[0x1B];
+ BYTE page_size_l, page_size_h; // 9C full page size with spare
+ BYTE r9E, r9F;
+ BYTE uA0[0x4C];
+ BYTE rEC;
+ BYTE uED[0x13];
+} NANDREGS;
+
+__xdata __at 0xF400 volatile NANDREGS NFC0;
+__xdata __at 0xF500 volatile NANDREGS NFC1;
+
+__xdata __at 0xF608 volatile BYTE NANDCSOUT;
+__xdata __at 0xF618 volatile BYTE NANDCSDIR;
+//F638, F639 - scrambler control
+//F638 | 18 & 7F - turn off descrambler
+__xdata __at 0xF700 volatile NANDREGS NFCX;
+
+// DMA copy source / fill destination physical address
+__xdata __at 0xF900 volatile BYTE DMASRCL;
+__xdata __at 0xF901 volatile BYTE DMASRCM;
+__xdata __at 0xF902 volatile BYTE DMASRCH;
+
+// DMA copy destination physical address
+__xdata __at 0xF904 volatile BYTE DMADSTL;
+__xdata __at 0xF905 volatile BYTE DMADSTM;
+__xdata __at 0xF906 volatile BYTE DMADSTH;
+
+// DMA copy size in bytes (always in bytes, regardless of cmd width)
+__xdata __at 0xF908 volatile BYTE DMASIZEL;
+__xdata __at 0xF909 volatile BYTE DMASIZEM;
+__xdata __at 0xF90A volatile BYTE DMASIZEH;
+
+// DMA fill value
+__xdata __at 0xF90C volatile BYTE DMAFILL0;
+__xdata __at 0xF90D volatile BYTE DMAFILL1;
+__xdata __at 0xF90E volatile BYTE DMAFILL2;
+__xdata __at 0xF90F volatile BYTE DMAFILL3;
+
+// DMA command
+__xdata __at 0xF930 volatile BYTE DMACMD;
+
+// ------------------------------------------------------------------------------------------------
+// Xdata FA00-FAFF SYSTEM Registers
+// ------------------------------------------------------------------------------------------------
+__xdata __at 0xFA14 volatile BYTE GPIO0DIR;
+__xdata __at 0xFA15 volatile BYTE GPIO0OUT;
+__xdata __at 0xFA38 volatile BYTE WARMSTATUS;
+
+// XDATA banking
+// XDATA consists of 3 mapped areas: BANK0,1,2
+// BANK0 start is fixed at 0000, BANK1,2 starts at variable addresses
+// in case of overlapped addresses a bank with higher number has higher priority
+
+// xdata BANK0 mapping registers
+// maps XDATA VA 0000 to PA=BANK0PAH:L<<9, size - up to BANK1/2
+__xdata __at 0xFA40 volatile BYTE BANK0PAL;
+__xdata __at 0xFA41 volatile BYTE BANK0PAH;
+
+// xdata BANK1 mapping registers
+// maps XDATA VA=(BANK1VA & 0xFE)<<8 to PA=BANK1PAH:L<<9, size - up to BANK2
+__xdata __at 0xFA42 volatile BYTE BANK1VA;
+__xdata __at 0xFA43 volatile BYTE BANK1PAL;
+__xdata __at 0xFA44 volatile BYTE BANK1PAH;
+
+// xdata BANK2 mapping registers
+// maps XDATA VA=(BANK2VA & 0xFE)<<8 to PA=BANK2PAH:L<<9, size - up to F000
+__xdata __at 0xFA45 volatile BYTE BANK2VA;
+__xdata __at 0xFA46 volatile BYTE BANK2PAL;
+__xdata __at 0xFA47 volatile BYTE BANK2PAH;
+
+// PRAM/PROM switching with restart
+// value: bit0 =0 - run from PROM, =1 - run from PRAM. Changing this bit causes CPU restart!
+__xdata __at 0xFA48 volatile BYTE PRAMCTL;
+
+#endif
diff --git a/templates/BNdummy.bin b/templates/BNdummy.bin
new file mode 100644
index 0000000..6bfee1d
Binary files /dev/null and b/templates/BNdummy.bin differ
diff --git a/templates/FWdummy.bin b/templates/FWdummy.bin
new file mode 100644
index 0000000..49c8ac5
Binary files /dev/null and b/templates/FWdummy.bin differ