From 3b6a888332ea4b276fa93465a2be93fb89d96385 Mon Sep 17 00:00:00 2001 From: mr-sven Date: Thu, 7 Apr 2022 12:19:02 +0200 Subject: [PATCH] Initial commit --- .gitattributes | 63 + .gitignore | 342 ++++ README.md | 128 ++ .../ByteToHexadecimalConverter.cs | 31 + Sim80C51.Toolbox.Wpf/EqualityConverter.cs | 18 + Sim80C51.Toolbox.Wpf/GreaterThanConverter.cs | 22 + Sim80C51.Toolbox.Wpf/IconHelper.cs | 42 + Sim80C51.Toolbox.Wpf/InputDialog.xaml | 23 + Sim80C51.Toolbox.Wpf/InputDialog.xaml.cs | 39 + Sim80C51.Toolbox.Wpf/NotifyPropertyChanged.cs | 22 + Sim80C51.Toolbox.Wpf/RelayCommand.cs | 74 + .../Sim80C51.Toolbox.Wpf.csproj | 10 + Sim80C51.Toolbox.Wpf/TwoColumnGrid.cs | 123 ++ Sim80C51.sln | 31 + Sim80C51/App.xaml | 9 + Sim80C51/App.xaml.cs | 17 + Sim80C51/AssemblyInfo.cs | 10 + Sim80C51/ByteRow.cs | 116 ++ Sim80C51/Common/InstructionType.cs | 106 ++ Sim80C51/Common/IntelHex.cs | 253 +++ Sim80C51/Common/ListingCollection.cs | 124 ++ Sim80C51/Common/ListingEntry.cs | 52 + Sim80C51/Common/ListingFactory.cs | 1397 +++++++++++++++++ Sim80C51/Controls/CPU/ICPUControl.cs | 9 + Sim80C51/Controls/CPU/P80C552.xaml | 262 ++++ Sim80C51/Controls/CPU/P80C552.xaml.cs | 18 + Sim80C51/Controls/ListingEditor.xaml | 112 ++ Sim80C51/Controls/ListingEditor.xaml.cs | 134 ++ Sim80C51/Controls/ListingEditorContext.cs | 313 ++++ Sim80C51/Controls/Memory.xaml | 71 + Sim80C51/Controls/Memory.xaml.cs | 28 + Sim80C51/Controls/MemoryContext.cs | 119 ++ Sim80C51/Processors/C80C51.Logic.cs | 368 +++++ Sim80C51/Processors/C80C51.Process.cs | 829 ++++++++++ Sim80C51/Processors/C80C51.cs | 317 ++++ Sim80C51/Processors/I80C51.cs | 49 + Sim80C51/Processors/IVAttribute.cs | 15 + Sim80C51/Processors/P80C552.cs | 574 +++++++ Sim80C51/Processors/SFR16Attribute.cs | 15 + Sim80C51/Processors/SFRAttribute.cs | 13 + Sim80C51/Processors/SFRBitAttribute.cs | 17 + Sim80C51/SelectRamWindow.xaml | 34 + Sim80C51/SelectRamWindow.xaml.cs | 57 + Sim80C51/Sim80C51.csproj | 24 + Sim80C51/SimulatorWindow.xaml | 124 ++ Sim80C51/SimulatorWindow.xaml.cs | 20 + Sim80C51/SimulatorWindowContext.cs | 628 ++++++++ Sim80C51/WSpace/Workspace.cs | 18 + Sim80C51/WSpace/XMemConfig.cs | 12 + mainwindow.jpg | Bin 0 -> 114715 bytes 50 files changed, 7232 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 README.md create mode 100644 Sim80C51.Toolbox.Wpf/ByteToHexadecimalConverter.cs create mode 100644 Sim80C51.Toolbox.Wpf/EqualityConverter.cs create mode 100644 Sim80C51.Toolbox.Wpf/GreaterThanConverter.cs create mode 100644 Sim80C51.Toolbox.Wpf/IconHelper.cs create mode 100644 Sim80C51.Toolbox.Wpf/InputDialog.xaml create mode 100644 Sim80C51.Toolbox.Wpf/InputDialog.xaml.cs create mode 100644 Sim80C51.Toolbox.Wpf/NotifyPropertyChanged.cs create mode 100644 Sim80C51.Toolbox.Wpf/RelayCommand.cs create mode 100644 Sim80C51.Toolbox.Wpf/Sim80C51.Toolbox.Wpf.csproj create mode 100644 Sim80C51.Toolbox.Wpf/TwoColumnGrid.cs create mode 100644 Sim80C51.sln create mode 100644 Sim80C51/App.xaml create mode 100644 Sim80C51/App.xaml.cs create mode 100644 Sim80C51/AssemblyInfo.cs create mode 100644 Sim80C51/ByteRow.cs create mode 100644 Sim80C51/Common/InstructionType.cs create mode 100644 Sim80C51/Common/IntelHex.cs create mode 100644 Sim80C51/Common/ListingCollection.cs create mode 100644 Sim80C51/Common/ListingEntry.cs create mode 100644 Sim80C51/Common/ListingFactory.cs create mode 100644 Sim80C51/Controls/CPU/ICPUControl.cs create mode 100644 Sim80C51/Controls/CPU/P80C552.xaml create mode 100644 Sim80C51/Controls/CPU/P80C552.xaml.cs create mode 100644 Sim80C51/Controls/ListingEditor.xaml create mode 100644 Sim80C51/Controls/ListingEditor.xaml.cs create mode 100644 Sim80C51/Controls/ListingEditorContext.cs create mode 100644 Sim80C51/Controls/Memory.xaml create mode 100644 Sim80C51/Controls/Memory.xaml.cs create mode 100644 Sim80C51/Controls/MemoryContext.cs create mode 100644 Sim80C51/Processors/C80C51.Logic.cs create mode 100644 Sim80C51/Processors/C80C51.Process.cs create mode 100644 Sim80C51/Processors/C80C51.cs create mode 100644 Sim80C51/Processors/I80C51.cs create mode 100644 Sim80C51/Processors/IVAttribute.cs create mode 100644 Sim80C51/Processors/P80C552.cs create mode 100644 Sim80C51/Processors/SFR16Attribute.cs create mode 100644 Sim80C51/Processors/SFRAttribute.cs create mode 100644 Sim80C51/Processors/SFRBitAttribute.cs create mode 100644 Sim80C51/SelectRamWindow.xaml create mode 100644 Sim80C51/SelectRamWindow.xaml.cs create mode 100644 Sim80C51/Sim80C51.csproj create mode 100644 Sim80C51/SimulatorWindow.xaml create mode 100644 Sim80C51/SimulatorWindow.xaml.cs create mode 100644 Sim80C51/SimulatorWindowContext.cs create mode 100644 Sim80C51/WSpace/Workspace.cs create mode 100644 Sim80C51/WSpace/XMemConfig.cs create mode 100644 mainwindow.jpg diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..de9e0d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,342 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- Backup*.rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +res/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..6d37549 --- /dev/null +++ b/README.md @@ -0,0 +1,128 @@ +# Sim80C51 + +This is an Application for generate listings from ROM dumps and simmulate 80C51 processor. + +Current implemented Processors: + +* Philips 80C552 + +The current workspace state, including memories and CPU state can be saved and loaded for continuing work or debugging later. + +You can add multiple external RAM spaces direct empty or load a binary to a definded XRAM address. + +You can also enable MT48 Timekeeper function on every RAM block, it uses a `DispatcherTimer` to update the time in the MT48 time registers. It supports stop functionalities via the control registers. Write is not supported it will always update the time from current system clock. + +Empty RAMs are initialized with `0x00`, if using the MT48 mode the second half of the RAM is initialized with `0xff` which is default by my analysis. + +At the moment it is possible to Single Step the CPU and to Play/Pause the execution. + +Navigate from Breakpoint list to code and navigate to current `PC` is possible. + +![Main Window](mainwindow.jpg) + +## Listing Editor + +The editor can load listings and import ROM dumps from binary or Intel HEX files. + +The editor supports the following key commands at the moment: + +* C - generate code from `DB` statement, possible switch case jumps `JMP @A+DPTR` +* L - generate or update label +* J - follow jump label +* K - update comment +* B - add breakpoint + +## Listing Format + +The listings are stored and loaded in the following format: + +* ROM Address - 4 hex chars +* two spaces +* Instruction code - pairs of 2 hex chars devided by space +* min two spaces +* optional Label ending with `:` +* min two spaces +* Instruction +* min one space +* Instruction Arguments devided by `,` +* Optional comment beginnig with `;` + +Example: +``` +0000 02 01 00 RESET: LJMP INIT +0003 FF FF FF FF FF FF FF FF DB ffh, ffh, ffh, ffh, ffh, ffh, ffh, ffh ; ........ +000B C0 D0 T0: PUSH PSW +000D C0 E0 PUSH ACC +000F C0 F0 PUSH B +``` + +Every unidentified code, empty memory or other data is grouped in `DB` instructions by the length of 8 bytes. + +## RAM View + +The Core memory is splitted in up to three parts, the first part is the lower 128 byte addressable RAM, the second (yellow) is the upper 128 byte SFR, possible third (blue) is the 128 byte indirect addressable RAM if available. + +The RAMs are direct editable, and saveable. Editing the SFR space is not updating the Processor UI because of missing `PropertyChanged` event on register name, but the register is updated. So it is recommendet to update the SFRs via the Processor UI. + +RAM save is available via Right-Click on the View. + +## Processor view + +The Processor view is customized for each processor and shows all SFR values. + +## Adding a new Processor type + +As reference use class `Sim80C51.Processors.P80C552`. + +* Extend `Sim80C51.Processors.C80C51` and `Sim80C51.Processors.I80C51`. +* Override `Reset` method for initialize additional registers. +* Use `DisplayName` Attribute for Dropdown text. +* Create a `UserControl` for Processor View + +Getter and setter methods and `Interrupt` method using reflection to determine the caller. + +### Define interrupt method + +The name format `Interrupt_` is required for the `ListingFactory` to determine the IV name. Use `IVAttribute` to specify the parameters. + +```csharp +[IV(0x002B, 2)] // Interrupt entry address: 0x002B, Priority: 2 +public void Interrupt_S1() { Interrupt(); } // Name S1 +``` + +### Define a 8-bit register + +Assign the `SFRAttribute` to set the SFR address. + +```csharp +[SFR(0xC6)] +public byte ADCH { get => GetMemFromProp(); set { SetMemFromProp(value); } } +``` + +### Define a 16-bit register + +Assign the `SFR16Attribute` to set the 8-bit register names, high and low. + +```csharp +[SFR16(nameof(CTH3), nameof(CTL3))] +public ushort CT3 { get => GetMem16FromProp(); set { SetMem16FromProp(value); } } +``` + +### Define a single bit + +Assign the `SFRBitAttribute` to set the 8-bit register name and the bit number. If the bit is direct addressable, set `Addressable` parameter. Default is `false`. + +```csharp +[SFRBit(nameof(S1CON), 0, true)] +public bool CR0 { get => GetBitFromProp(); set { SetBitFromProp(value); } } +``` + +Bit fields on Ports P0 to P4 should not set the `Addressable` parameter, because they will be automatically declared as Addressable in the `ListingFactory` via the names `P0.0` to `P4.7`. + +## Things to do + +- [] check the EA bit on IEN0 Register set to disable all Interrupts +- [] adding HD44780 display control +- [] display PWM out based on Crystal speed +- [] show serial baud based on Crystal speed +- [] many more diff --git a/Sim80C51.Toolbox.Wpf/ByteToHexadecimalConverter.cs b/Sim80C51.Toolbox.Wpf/ByteToHexadecimalConverter.cs new file mode 100644 index 0000000..aead736 --- /dev/null +++ b/Sim80C51.Toolbox.Wpf/ByteToHexadecimalConverter.cs @@ -0,0 +1,31 @@ +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace Sim80C51.Toolbox.Wpf +{ + [ValueConversion(typeof(byte), typeof(string))] + public class ByteToHexadecimalConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null || targetType != typeof(string)) return DependencyProperty.UnsetValue; + if (!byte.TryParse(value.ToString(), out byte byteValue)) return DependencyProperty.UnsetValue; + if (parameter == null) + { + return byteValue.ToString("X2"); + } + return byteValue.ToString(parameter.ToString()); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null || targetType != typeof(byte)) return DependencyProperty.UnsetValue; + string stringValue = value.ToString()!; + byte returnValue; + try { returnValue = System.Convert.ToByte(stringValue, 16); } + catch { return DependencyProperty.UnsetValue; } + return returnValue; + } + } +} diff --git a/Sim80C51.Toolbox.Wpf/EqualityConverter.cs b/Sim80C51.Toolbox.Wpf/EqualityConverter.cs new file mode 100644 index 0000000..327edf8 --- /dev/null +++ b/Sim80C51.Toolbox.Wpf/EqualityConverter.cs @@ -0,0 +1,18 @@ +using System.Globalization; +using System.Windows.Data; + +namespace Sim80C51.Toolbox.Wpf +{ + public class EqualityConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + return values.Length < 2 ? false : (object)values[0].Equals(values[1]); + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Sim80C51.Toolbox.Wpf/GreaterThanConverter.cs b/Sim80C51.Toolbox.Wpf/GreaterThanConverter.cs new file mode 100644 index 0000000..35bbe99 --- /dev/null +++ b/Sim80C51.Toolbox.Wpf/GreaterThanConverter.cs @@ -0,0 +1,22 @@ +using System.Globalization; +using System.Windows.Data; + +namespace Sim80C51.Toolbox.Wpf +{ + public class GreaterThanConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is double) + { + return ((double)value) > double.Parse(parameter as string ?? string.Empty); + } + return ((int)value) > int.Parse(parameter as string ?? string.Empty); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Sim80C51.Toolbox.Wpf/IconHelper.cs b/Sim80C51.Toolbox.Wpf/IconHelper.cs new file mode 100644 index 0000000..a39c25b --- /dev/null +++ b/Sim80C51.Toolbox.Wpf/IconHelper.cs @@ -0,0 +1,42 @@ +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Interop; + +namespace Sim80C51.Toolbox.Wpf +{ + public static class IconHelper + { + [DllImport("user32.dll")] + static extern int GetWindowLong(IntPtr hwnd, int index); + + [DllImport("user32.dll")] + static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle); + + [DllImport("user32.dll")] + static extern bool SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags); + + [DllImport("user32.dll")] + static extern IntPtr SendMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam); + + const int GWL_EXSTYLE = -20; + const int WS_EX_DLGMODALFRAME = 0x0001; + const int SWP_NOSIZE = 0x0001; + const int SWP_NOMOVE = 0x0002; + const int SWP_NOZORDER = 0x0004; + const int SWP_FRAMECHANGED = 0x0020; + const uint WM_SETICON = 0x0080; + + public static void RemoveIcon(Window window) + { + // Get this window's handle + IntPtr hwnd = new WindowInteropHelper(window).Handle; + + // Change the extended window style to not show a window icon + int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE); + _ = SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME); + + // Update the window's non-client area to reflect the changes + SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + } + } +} diff --git a/Sim80C51.Toolbox.Wpf/InputDialog.xaml b/Sim80C51.Toolbox.Wpf/InputDialog.xaml new file mode 100644 index 0000000..9e8419b --- /dev/null +++ b/Sim80C51.Toolbox.Wpf/InputDialog.xaml @@ -0,0 +1,23 @@ + + + + + + + + + + Answer + + + + + + + diff --git a/Sim80C51.Toolbox.Wpf/InputDialog.xaml.cs b/Sim80C51.Toolbox.Wpf/InputDialog.xaml.cs new file mode 100644 index 0000000..cc9489f --- /dev/null +++ b/Sim80C51.Toolbox.Wpf/InputDialog.xaml.cs @@ -0,0 +1,39 @@ +using System.Windows; + +namespace Sim80C51.Toolbox.Wpf +{ + /// + /// Interaction logic for InputDialog.xaml + /// + public partial class InputDialog : Window + { + public InputDialog(string title, string question, string defaultAnswer = "") + { + InitializeComponent(); + lblQuestion.Content = question; + txtAnswer.Text = defaultAnswer; + Title = title; + } + + protected override void OnSourceInitialized(EventArgs e) + { + IconHelper.RemoveIcon(this); + } + + private void Button_Click(object sender, RoutedEventArgs e) + { + DialogResult = true; + } + + public string Answer + { + get { return txtAnswer.Text; } + } + + private void Window_Loaded(object sender, RoutedEventArgs e) + { + txtAnswer.SelectAll(); + txtAnswer.Focus(); + } + } +} diff --git a/Sim80C51.Toolbox.Wpf/NotifyPropertyChanged.cs b/Sim80C51.Toolbox.Wpf/NotifyPropertyChanged.cs new file mode 100644 index 0000000..6737744 --- /dev/null +++ b/Sim80C51.Toolbox.Wpf/NotifyPropertyChanged.cs @@ -0,0 +1,22 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace Sim80C51.Toolbox.Wpf +{ + public class NotifyPropertyChanged : INotifyPropertyChanged + { + public event PropertyChangedEventHandler? PropertyChanged; + protected void DoPropertyChanged([CallerMemberName] string? propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + protected void DoPropertyChanged(params string[] propertyNames) + { + foreach (string propertyName in propertyNames) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } + } +} diff --git a/Sim80C51.Toolbox.Wpf/RelayCommand.cs b/Sim80C51.Toolbox.Wpf/RelayCommand.cs new file mode 100644 index 0000000..6305d47 --- /dev/null +++ b/Sim80C51.Toolbox.Wpf/RelayCommand.cs @@ -0,0 +1,74 @@ +using System.Runtime.Serialization; +using System.Windows.Input; + +namespace Sim80C51.Toolbox.Wpf +{ + [DataContract] + public class RelayCommand : ICommand + { + private Action execute; + + private Predicate canExecute; + + private readonly Func nameDetector = () => string.Empty; + + private event EventHandler? CanExecuteChangedInternal; + + [DataMember] + public string Name { get { return nameDetector.Invoke(); } } + + + public RelayCommand(Action execute) : this(() => string.Empty, execute, DefaultCanExecute) { } + public RelayCommand(Func name, Action execute) : this(name, execute, DefaultCanExecute) { } + public RelayCommand(Action execute, Predicate canExecute) : this(() => string.Empty, execute, canExecute) { } + public RelayCommand(Func name, Action execute, Predicate canExecute) + { + this.execute = execute ?? throw new ArgumentNullException(nameof(execute)); + this.canExecute = canExecute ?? throw new ArgumentNullException(nameof(canExecute)); + nameDetector = name ?? throw new ArgumentNullException(nameof(name)); + } + + public event EventHandler? CanExecuteChanged + { + add + { + CanExecuteChangedInternal += value; + } + + remove + { + CanExecuteChangedInternal -= value; + } + } + + public bool CanExecute(object? parameter) + { + return canExecute != null && canExecute(parameter); + } + + public void Execute(object? parameter) + { + execute(parameter); + } + + public void OnCanExecuteChanged() + { + EventHandler? handler = CanExecuteChangedInternal; + if (handler != null) + { + handler.Invoke(this, EventArgs.Empty); + } + } + + public void Destroy() + { + canExecute = _ => false; + execute = _ => { return; }; + } + + private static bool DefaultCanExecute(object? parameter) + { + return true; + } + } +} diff --git a/Sim80C51.Toolbox.Wpf/Sim80C51.Toolbox.Wpf.csproj b/Sim80C51.Toolbox.Wpf/Sim80C51.Toolbox.Wpf.csproj new file mode 100644 index 0000000..a00abee --- /dev/null +++ b/Sim80C51.Toolbox.Wpf/Sim80C51.Toolbox.Wpf.csproj @@ -0,0 +1,10 @@ + + + + net6.0-windows + enable + enable + true + + + diff --git a/Sim80C51.Toolbox.Wpf/TwoColumnGrid.cs b/Sim80C51.Toolbox.Wpf/TwoColumnGrid.cs new file mode 100644 index 0000000..5587181 --- /dev/null +++ b/Sim80C51.Toolbox.Wpf/TwoColumnGrid.cs @@ -0,0 +1,123 @@ +using System.Windows; +using System.Windows.Controls; + +namespace Sim80C51.Toolbox.Wpf +{ + /// + /// Defines a table that has two columns with any number of rows. + /// + /// + /// This panel is designed for use in configuration/settings windows where you typically + /// have a pairs of "Label: SomeControl" organized in rows. + /// + /// The width of the first column is determined by the widest item that column and the width of the + /// second column is expanded to occupy all remaining space. + /// + /// Written by: Isak Savo, isak.savo@gmail.com + /// Licensed under the Code Project Open License http://www.codeproject.com/info/cpol10.aspx + /// + public class TwoColumnGrid : Panel + { + private double Column1Width; + private readonly List RowHeights = new(); + + /// + /// Gets or sets the amount of spacing (in device independent pixels) between the rows. + /// + public double RowSpacing + { + get { return (double)GetValue(RowSpacingProperty); } + set { SetValue(RowSpacingProperty, value); } + } + + /// + /// Identifies the ColumnSpacing dependency property + /// + public static readonly DependencyProperty RowSpacingProperty = + DependencyProperty.Register("RowSpacing", typeof(double), typeof(TwoColumnGrid), + new FrameworkPropertyMetadata(0.0d, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)); + + /// + /// Gets or sets the amount of spacing (in device independent pixels) between the columns. + /// + public double ColumnSpacing + { + get { return (double)GetValue(ColumnSpacingProperty); } + set { SetValue(ColumnSpacingProperty, value); } + } + + /// + /// Identifies the ColumnSpacing dependency property + /// + public static readonly DependencyProperty ColumnSpacingProperty = + DependencyProperty.Register("ColumnSpacing", typeof(double), typeof(TwoColumnGrid), + new FrameworkPropertyMetadata(0.0d, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)); + + + /// + /// Measures the size required for all the child elements in this panel. + /// + /// The size constraint given by our parent. + /// The requested size for this panel including all children + protected override Size MeasureOverride(Size constraint) + { + double col1Width = 0; + double col2Width = 0; + RowHeights.Clear(); + // First, measure all the left column children + for (int i = 0; i < VisualChildrenCount; i += 2) + { + UIElement child = Children[i]; + child.Measure(constraint); + col1Width = Math.Max(child.DesiredSize.Width, col1Width); + RowHeights.Add(child.DesiredSize.Height); + } + // Then, measure all the right column children, they get whatever remains in width + double newWidth = Math.Max(0, constraint.Width - col1Width - ColumnSpacing); + Size newConstraint = new(newWidth, constraint.Height); + for (int i = 1; i < VisualChildrenCount; i += 2) + { + UIElement child = Children[i]; + child.Measure(newConstraint); + col2Width = Math.Max(child.DesiredSize.Width, col2Width); + RowHeights[i / 2] = Math.Max(RowHeights[i / 2], child.DesiredSize.Height); + } + + Column1Width = col1Width; + return new Size( + col1Width + ColumnSpacing + col2Width, + RowHeights.Sum() + ((RowHeights.Count - 1) * RowSpacing)); + } + + /// + /// Position elements and determine the final size for this panel. + /// + /// The final area where child elements should be positioned. + /// The final size required by this panel + protected override Size ArrangeOverride(Size arrangeSize) + { + double y = 0; + for (int i = 0; i < VisualChildrenCount; i++) + { + UIElement child = Children[i]; + double height = RowHeights[i / 2]; + if (i % 2 == 0) + { + // Left child + Rect r = new(0, y, Column1Width, height); + child.Arrange(r); + } + else + { + // Right child + Rect r = new(Column1Width + ColumnSpacing, y, arrangeSize.Width - Column1Width - ColumnSpacing, height); + child.Arrange(r); + y += height; + y += RowSpacing; + } + } + return base.ArrangeOverride(arrangeSize); + } + + } +} diff --git a/Sim80C51.sln b/Sim80C51.sln new file mode 100644 index 0000000..618f292 --- /dev/null +++ b/Sim80C51.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32126.317 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sim80C51", "Sim80C51\Sim80C51.csproj", "{1CD8264F-17B9-43F7-99ED-4CCB9DFC1686}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sim80C51.Toolbox.Wpf", "Sim80C51.Toolbox.Wpf\Sim80C51.Toolbox.Wpf.csproj", "{8B43BF01-2149-4B9C-9D46-90A7B6F57CFD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1CD8264F-17B9-43F7-99ED-4CCB9DFC1686}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1CD8264F-17B9-43F7-99ED-4CCB9DFC1686}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1CD8264F-17B9-43F7-99ED-4CCB9DFC1686}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1CD8264F-17B9-43F7-99ED-4CCB9DFC1686}.Release|Any CPU.Build.0 = Release|Any CPU + {8B43BF01-2149-4B9C-9D46-90A7B6F57CFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B43BF01-2149-4B9C-9D46-90A7B6F57CFD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B43BF01-2149-4B9C-9D46-90A7B6F57CFD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B43BF01-2149-4B9C-9D46-90A7B6F57CFD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C70AED53-992A-4BFE-B8F1-3F3C35904DB2} + EndGlobalSection +EndGlobal diff --git a/Sim80C51/App.xaml b/Sim80C51/App.xaml new file mode 100644 index 0000000..29ba08d --- /dev/null +++ b/Sim80C51/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/Sim80C51/App.xaml.cs b/Sim80C51/App.xaml.cs new file mode 100644 index 0000000..13aaa75 --- /dev/null +++ b/Sim80C51/App.xaml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace Sim80C51 +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/Sim80C51/AssemblyInfo.cs b/Sim80C51/AssemblyInfo.cs new file mode 100644 index 0000000..8b5504e --- /dev/null +++ b/Sim80C51/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/Sim80C51/ByteRow.cs b/Sim80C51/ByteRow.cs new file mode 100644 index 0000000..cff8c63 --- /dev/null +++ b/Sim80C51/ByteRow.cs @@ -0,0 +1,116 @@ +using Sim80C51.Toolbox.Wpf; +using System.Collections.ObjectModel; +using System.IO; + +namespace Sim80C51 +{ + public class ByteRow : NotifyPropertyChanged + { + public const int ROW_WIDTH = 16; + + public ObservableCollection Row { get => row; } + private readonly ObservableCollection row; + + public int Index { get; } + + public string Ansi + { + get + { + string str = System.Text.Encoding.Default.GetString(Row.ToArray()); + return new string(str.Select(c => c < 0x20 ? '.' : c > 0x7f ? '.' : c).ToArray()); + } + } + + public byte this[int i] + { + get { return Row[i]; } + set { if (Row[i] != value) { Row[i] = value; DoPropertyChanged(nameof(Ansi)); } } + } + + public ByteRow(int index, byte initValue = 0x00) + { + Index = index; + row = new(Enumerable.Repeat(initValue, ROW_WIDTH).ToArray()); + Row.CollectionChanged += Row_CollectionChanged; + } + + public ByteRow(int index, byte[] data) + { + Index = index; + if (data.Length >= ROW_WIDTH) + { + row = new(data[..ROW_WIDTH]); + } + else + { + row = new(data); + while (row.Count < ROW_WIDTH) + { + row.Add(0x00); + } + } + Row.CollectionChanged += Row_CollectionChanged; + } + + private void Row_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + DoPropertyChanged(nameof(Ansi)); + } + + public void UpdateFromBuffer(byte[] buf) + { + for (int i = 0; i < buf.Length && i < ROW_WIDTH; i++) + { + Row[i] = buf[i]; + } + } + + public static ByteRow[] InitRows(int size, bool enableMT48 = false) + { + ByteRow[] result = new ByteRow[size / ROW_WIDTH]; + for (int i = 0; i < result.Length; i++) + { + if (i < result.Length / 2 || !enableMT48) + { + result[i] = new ByteRow(i); + } + else if(i == result.Length-1) + { + result[i] = new ByteRow(i, Enumerable.Repeat((byte)0xff, ROW_WIDTH / 2).ToArray()); + } + else + { + result[i] = new ByteRow(i, 0xff); + } + } + return result; + } + + public static ByteRow[] FromStream(Stream stream) + { + List res = new(); + + byte[] buf = new byte[ROW_WIDTH]; + int row = 0; + + while ((_ = stream.Read(buf, 0, buf.Length)) > 0) + { + res.Add(new(row++, buf)); + } + + return res.ToArray(); + } + + public static MemoryStream ToMemoryStream(IEnumerable Rows) + { + MemoryStream ms = new(); + foreach (ByteRow row in Rows) + { + ms.Write(row.Row.ToArray()); + } + ms.Position = 0; + return ms; + } + } +} diff --git a/Sim80C51/Common/InstructionType.cs b/Sim80C51/Common/InstructionType.cs new file mode 100644 index 0000000..937667b --- /dev/null +++ b/Sim80C51/Common/InstructionType.cs @@ -0,0 +1,106 @@ +using System.ComponentModel; + +namespace Sim80C51.Common +{ + public enum InstructionType + { + [Description("Absolute Call")] + ACALL, + [Description("Add Accumulator")] + ADD, + [Description("Add Accumulator With Carry")] + ADDC, + [Description("Absolute Jump")] + AJMP, + [Description("Bitwise AND")] + ANL, + [Description("Compare and Jump if Not Equal")] + CJNE, + [Description("Clear Register")] + CLR, + [Description("Complement Register")] + CPL, + [Description("Decimal Adjust")] + DA, + [Description("Decrement Register")] + DEC, + [Description("Divide Accumulator by B")] + DIV, + [Description("Decrement Register and Jump if Not Zero")] + DJNZ, + [Description("Increment Register")] + INC, + [Description("Jump if Bit Set")] + JB, + [Description("Jump if Bit Set and Clear Bit")] + JBC, + [Description("Jump if Carry Set")] + JC, + [Description("Jump to Address")] + JMP, + [Description("Jump if Bit Not Set")] + JNB, + [Description("Jump if Carry Not Set")] + JNC, + [Description("Jump if Accumulator Not Zero")] + JNZ, + [Description("Jump if Accumulator Zero")] + JZ, + [Description("Long Call")] + LCALL, + [Description("Long Jump")] + LJMP, + [Description("Move Memory")] + MOV, + [Description("Move Code Memory")] + MOVC, + [Description("Move Extended Memory")] + MOVX, + [Description("Multiply Accumulator by B")] + MUL, + [Description("No Operation")] + NOP, + [Description("Bitwise OR")] + ORL, + [Description("Pop Value From Stack")] + POP, + [Description("Push Value Onto Stack")] + PUSH, + [Description("Return From Subroutine")] + RET, + [Description("Return From Interrupt")] + RETI, + [Description("Rotate Accumulator Left")] + RL, + [Description("Rotate Accumulator Left Through Carry")] + RLC, + [Description("Rotate Accumulator Right")] + RR, + [Description("Rotate Accumulator Right Through Carry")] + RRC, + [Description("Set Bit")] + SETB, + [Description("Short Jump")] + SJMP, + [Description("Subtract From Accumulator With Borrow")] + SUBB, + [Description("Swap Accumulator Nibbles")] + SWAP, + [Description("Exchange Bytes")] + XCH, + [Description("Exchange Digits")] + XCHD, + [Description("Bitwise Exclusive OR")] + XRL, + + // Assembler commands + [Description("Define Byte")] + DB, + [Description("Define Word")] + DW, + [Description("Define a Bit")] + DBIT, + [Description("Origin")] + ORG, + } +} diff --git a/Sim80C51/Common/IntelHex.cs b/Sim80C51/Common/IntelHex.cs new file mode 100644 index 0000000..40f8316 --- /dev/null +++ b/Sim80C51/Common/IntelHex.cs @@ -0,0 +1,253 @@ +using System.IO; + +namespace Sim80C51.Common +{ + /// + /// IntelHexStructure provides the internal data structure which will be used by the IntelHex class. + /// This class is used for internal processing and is declared public to allow the application that instantiates + /// the IntelHex class access to the internal storage. + /// + public class IntelHexStructure + { + public ushort address; //< The 16-bit address field. + //< The 8-bit array data field, which has a maximum size of 256 bytes. + public byte[] data = new byte[IntelHex.IHEX_MAX_DATA_LEN / 2]; + public int dataLen; //< The number of bytes of data stored in this record. + public int type; //< The Intel HEX8 record type of this record. + public byte checksum; //< The checksum of this record. + + private byte CalcChecksum() + { + // Add the data count, type, address, and data bytes together + byte cChecksum = (byte)dataLen; + cChecksum += (byte)type; + cChecksum += (byte)address; + cChecksum += (byte)((address & 0xFF00) >> 8); + for (int i = 0; i < dataLen; i++) + { + cChecksum += data[i]; + } + // Two's complement on checksum + return (byte)(~cChecksum + 1); + } + + public void SetChecksum() + { + checksum = CalcChecksum(); + } + + public bool Verify() + { + byte chec = CalcChecksum(); + return checksum == chec; + } + } + + /// + /// IntelHex is the base class to work with Intel Hex records. + /// This class will contain all necessary functions to process data using the Intel Hex record standard. + /// + public static class IntelHex + { + // Offsets and lengths of various fields in an Intel HEX8 record + const int IHEX_COUNT_OFFSET = 1; + const int IHEX_COUNT_LEN = 2; + const int IHEX_ADDRESS_OFFSET = 3; + const int IHEX_ADDRESS_LEN = 4; + const int IHEX_TYPE_OFFSET = 7; + const int IHEX_TYPE_LEN = 2; + const int IHEX_DATA_OFFSET = 9; + const int IHEX_CHECKSUM_LEN = 2; + public const int IHEX_MAX_DATA_LEN = 512; + // Ascii hex encoded length of a single byte + const int IHEX_ASCII_HEX_BYTE_LEN = 2; + // Start code offset and value + const int IHEX_START_CODE_OFFSET = 0; + const char IHEX_START_CODE = ':'; + + public const int IHEX_TYPE_DATA = 0; //< Data Record + public const int IHEX_TYPE_EOF = 1; //< End of File Record + public const int IHEX_TYPE_02 = 2; //< Extended Segment Address Record + public const int IHEX_TYPE_03 = 3; //< Start Segment Address Record + public const int IHEX_TYPE_04 = 4; //< Extended Linear Address Record + public const int IHEX_TYPE_05 = 5; //< Start Linear Address Record + + + /// + /// Initializes a new IntelHex structure that is returned upon successful completion of the function, + /// including up to 16-bit integer address, 8-bit data array, and size of 8-bit data array. + /// + /// The type of Intel HEX record to be defined by the record. + /// The 16-, 24-, or 32-bit address of the record. + /// An array of 8-bit data bytes. + /// The number of data bytes passed in the array. + /// IntelHexStructure instance or null, if null then query Status class variable for the error. + public static IntelHexStructure? NewRecord(int type, ushort address, byte[] data, int dataLen) + { + // Data length size check, assertion of irec pointer + if (dataLen < 0 || dataLen > IHEX_MAX_DATA_LEN / 2) + { + return null; + } + + IntelHexStructure irec = new(); + irec.type = type; + irec.address = address; + if (data != null) + { + Array.Copy(data, irec.data, (long)dataLen); + } + + irec.dataLen = dataLen; + irec.SetChecksum(); + + return irec; + } + + /// + /// Utility function to read an Intel HEX8 record from a file + /// + /// An instance of the StreamReader class to allow reading the file data. + /// IntelHexStructure instance or null, if null then query Status class variable for the error. + public static IntelHexStructure? Read(StreamReader inStream) + { + string? recordBuff; + int dataCount, i; + + try + { + // Read Line will return a line from the file. + recordBuff = inStream.ReadLine(); + } + catch (Exception) + { + return null; + } + + // Check if we hit a newline + if (recordBuff == null || recordBuff.Length == 0) + { + return null; + } + + // Size check for start code, count, address, and type fields + if (recordBuff.Length < (1 + IHEX_COUNT_LEN + IHEX_ADDRESS_LEN + IHEX_TYPE_LEN)) + { + return null; + } + + // Check the for colon start code + if (recordBuff[IHEX_START_CODE_OFFSET] != IHEX_START_CODE) + { + return null; + } + + IntelHexStructure irec = new(); + + // Copy the ASCII hex encoding of the count field into hexBuff, convert it to a usable integer + dataCount = Convert.ToInt16(recordBuff.Substring(IHEX_COUNT_OFFSET, IHEX_COUNT_LEN), 16); + + // Copy the ASCII hex encoding of the address field into hexBuff, convert it to a usable integer + irec.address = Convert.ToUInt16(recordBuff.Substring(IHEX_ADDRESS_OFFSET, IHEX_ADDRESS_LEN), 16); + + // Copy the ASCII hex encoding of the address field into hexBuff, convert it to a usable integer + irec.type = Convert.ToInt16(recordBuff.Substring(IHEX_TYPE_OFFSET, IHEX_TYPE_LEN), 16); + + // Size check for start code, count, address, type, data and checksum fields + if (recordBuff.Length < (1 + IHEX_COUNT_LEN + IHEX_ADDRESS_LEN + IHEX_TYPE_LEN + dataCount * 2 + IHEX_CHECKSUM_LEN)) + { + return null; + } + + // Loop through each ASCII hex byte of the data field, pull it out into hexBuff, + // convert it and store the result in the data buffer of the Intel HEX8 record + for (i = 0; i < dataCount; i++) + { + // Times two i because every byte is represented by two ASCII hex characters + irec.data[i] = Convert.ToByte(recordBuff.Substring(IHEX_DATA_OFFSET + 2 * i, IHEX_ASCII_HEX_BYTE_LEN), 16); + } + irec.dataLen = dataCount; + + // Copy the ASCII hex encoding of the checksum field into hexBuff, convert it to a usable integer + irec.checksum = Convert.ToByte(recordBuff.Substring(IHEX_DATA_OFFSET + dataCount * 2, IHEX_CHECKSUM_LEN), 16); + + if (!irec.Verify()) + { + return null; + } + + return irec; + } + + // Utility function to write an Intel HEX8 record to a file + public static void Write(IntelHexStructure irec, StreamWriter outStream) + { + // Check that the data length is in range + if (irec.dataLen > IHEX_MAX_DATA_LEN / 2) + { + return; + } + + try + { + // Write the start code, data count, address, and type fields + outStream.Write(string.Format("{0}{1:X2}{2:X4}{3:X2}", IHEX_START_CODE, irec.dataLen, irec.address, irec.type)); + // Write the data bytes + for (int i = 0; i < irec.dataLen; i++) + { + outStream.Write(string.Format("{0:X2}", irec.data[i])); + } + // Last but not least, the checksum + outStream.WriteLine(string.Format("{0:X2}", irec.checksum)); + } + catch (Exception) + { + return; + } + + return; + } + + /// + /// Utility function to print the information stored in an Intel HEX8 record + /// + /// A boolean set to false by default, if set to true will provide extended information. + /// String which provides the output of the function, this does not write directly to the console. + public static string Print(IntelHexStructure irec, bool verbose = false) + { + int i; + string returnString; + + if (verbose) + { + returnString = string.Format("Intel HEX8 Record Type: \t{0}\n", irec.type); + returnString += string.Format("Intel HEX8 Record Address: \t0x{0:X4}\n", irec.address); + returnString += string.Format("Intel HEX8 Record Data: \t["); + for (i = 0; i < irec.dataLen; i++) + { + if (i + 1 < irec.dataLen) + { + returnString += string.Format("0x{0:X02}, ", irec.data[i]); + } + else + { + returnString += string.Format("0x{0:X02}", irec.data[i]); + } + } + returnString += string.Format("]\n"); + returnString += string.Format("Intel HEX8 Record Checksum: \t0x{0:X2}\n", irec.checksum); + } + else + { + returnString = string.Format("{0}{1:X2}{2:X4}{3:X2}", IHEX_START_CODE, irec.dataLen, irec.address, irec.type); + for (i = 0; i < irec.dataLen; i++) + { + returnString += string.Format("{0:X2}", irec.data[i]); + } + + returnString += string.Format("{0:X2}", irec.checksum); + } + return (returnString); + } + } +} diff --git a/Sim80C51/Common/ListingCollection.cs b/Sim80C51/Common/ListingCollection.cs new file mode 100644 index 0000000..2aeed84 --- /dev/null +++ b/Sim80C51/Common/ListingCollection.cs @@ -0,0 +1,124 @@ +using System.Collections.ObjectModel; + +namespace Sim80C51.Common +{ + public class ListingCollection : ObservableCollection + { + private readonly Dictionary entriesByAddress = new(); + private readonly Dictionary entriesByLabel = new(); + + public new void Remove(ListingEntry entry) + { + entriesByAddress.Remove(entry.Address); + if (!string.IsNullOrEmpty(entry.Label)) + { + entriesByLabel.Remove(entry.Label); + } + base.Remove(entry); + } + + public new void Clear() + { + entriesByAddress.Clear(); + entriesByLabel.Clear(); + base.Clear(); + } + + public new void Add(ListingEntry entry) + { + entriesByAddress.Add(entry.Address, entry); + if (!string.IsNullOrEmpty(entry.Label)) + { + entriesByLabel.Add(entry.Label, entry); + } + + for (int i = 0; i < Count; i++) + { + if (this[i].Address > entry.Address) + { + base.Insert(i, entry); + return; + } + } + base.Add(entry); + } + + public void RemoveByAddress(ushort address) + { + if (entriesByAddress.ContainsKey(address)) + { + ListingEntry toRemove = entriesByAddress[address]; + base.Remove(toRemove); + entriesByAddress.Remove(address); + if (!string.IsNullOrEmpty(toRemove.Label)) + { + entriesByLabel.Remove(toRemove.Label); + } + } + } + + public bool Contains(ushort address) + { + return entriesByAddress.ContainsKey(address); + } + + public bool Contains(string label) + { + return entriesByLabel.ContainsKey(label); + } + + public ListingEntry? GetByAddress(ushort address) + { + if (entriesByAddress.ContainsKey(address)) + { + return entriesByAddress[address]; + } + + return null; + } + + public ListingEntry? GetByLabel(string label) + { + if (entriesByLabel.ContainsKey(label)) + { + return entriesByLabel[label]; + } + return null; + } + + public void SetLabel(ushort address, string label) + { + ListingEntry? entry = GetByAddress(address); + if (entry == null || entry.Label == label || string.IsNullOrEmpty(label)) + { + return; + } + SetLabel(entry, label); + } + + public void SetLabel(ListingEntry entry, string label) + { + if (entry.Label == label || string.IsNullOrEmpty(label)) + { + return; + } + + if (string.IsNullOrEmpty(entry.Label)) + { + entry.Label = label; + entriesByLabel.Add(entry.Label, entry); + return; + } + + entriesByLabel.Remove(entry.Label); + foreach (ListingEntry uEntry in Items.Where(e => e.Arguments.Count > 0 && e.Arguments.Last() == entry.Label)) + { + uEntry.Arguments[^1] = label; + uEntry.UpdateStrings(); + } + + entry.Label = label; + entriesByLabel.Add(entry.Label, entry); + } + } +} diff --git a/Sim80C51/Common/ListingEntry.cs b/Sim80C51/Common/ListingEntry.cs new file mode 100644 index 0000000..1e2e80a --- /dev/null +++ b/Sim80C51/Common/ListingEntry.cs @@ -0,0 +1,52 @@ +using Sim80C51.Toolbox.Wpf; + +namespace Sim80C51.Common +{ + public class ListingEntry : NotifyPropertyChanged + { + public ushort Address { get => address; set { address = value; DoPropertyChanged(); } } + private ushort address; + + public List Data { get => data; set { data = value; DoPropertyChanged(); } } + private List data = new(); + + public string DataString { get => dataString; private set { dataString = value; DoPropertyChanged(); } } + private string dataString = string.Empty; + + public string? Label { get => label; set { label = value; DoPropertyChanged(); } } + private string? label; + + public InstructionType Instruction { get => instruction; set { instruction = value; DoPropertyChanged(); } } + private InstructionType instruction; + + public List Arguments { get => arguments; set { arguments = value; DoPropertyChanged(); } } + private List arguments = new(); + + public string ArgumentString { get => argumentString; private set { argumentString = value; DoPropertyChanged(); } } + private string argumentString = string.Empty; + + public string? Comment { get => comment; set { comment = value; DoPropertyChanged(); } } + private string? comment; + + // for call and jump + public ushort TargetAddress { get => targetAddress; set { targetAddress = value; DoPropertyChanged(); } } + private ushort targetAddress; + + public void UpdateStrings() + { + DataString = BitConverter.ToString(Data.ToArray()).Replace('-', ' '); + ArgumentString = string.Join(", ", arguments); + } + + public override string ToString() + { + string result = Address.ToString("X4") + " "; + result += $"{DataString,-23} "; + result += string.Format("{0,-21}", string.IsNullOrEmpty(label) ? "" : label + ": "); + result += $"{Instruction,-6}"; + result += $"{ArgumentString,-21}"; + result += string.IsNullOrEmpty(Comment) ? "" : " ; " + Comment; + return result; + } + } +} diff --git a/Sim80C51/Common/ListingFactory.cs b/Sim80C51/Common/ListingFactory.cs new file mode 100644 index 0000000..efe840f --- /dev/null +++ b/Sim80C51/Common/ListingFactory.cs @@ -0,0 +1,1397 @@ +using System.Diagnostics; +using System.IO; +using System.Reflection; + +namespace Sim80C51.Common +{ + public class ListingFactory + { + public const byte CMD_NOP = 0x00; + public const byte CMD_AJMP = 0x01; + public const byte CMD_LJMP = 0x02; + public const byte CMD_RR = 0x03; + public const byte CMD_INC = 0x04; + public const byte CMD_JBC = 0x10; + public const byte CMD_ACALL = 0x11; + public const byte CMD_LCALL = 0x12; + public const byte CMD_RRC = 0x13; + public const byte CMD_DEC = 0x14; + public const byte CMD_JB = 0x20; + public const byte CMD_RET = 0x22; + public const byte CMD_RL = 0x23; + public const byte CMD_ADD = 0x24; + public const byte CMD_JNB = 0x30; + public const byte CMD_RETI = 0x32; + public const byte CMD_RLC = 0x33; + public const byte CMD_ADDC = 0x34; + public const byte CMD_JC = 0x40; + public const byte CMD_ORL = 0x42; + public const byte CMD_ANL = 0x52; + public const byte CMD_JNC = 0x50; + public const byte CMD_JZ = 0x60; + public const byte CMD_XRL = 0x62; + public const byte CMD_JNZ = 0x70; + public const byte CMD_ORL_C = 0x72; + public const byte CMD_JMP = 0x73; + public const byte CMD_MOV_DATA = 0x74; + public const byte CMD_SJMP = 0x80; + public const byte CMD_ANL_C = 0x82; + public const byte CMD_MOVC_PC = 0x83; + public const byte CMD_DIV = 0x84; + public const byte CMD_MOV_IRAM = 0x85; + public const byte CMD_MOV_DPTR = 0x90; + public const byte CMD_MOV_BIT = 0x92; + public const byte CMD_MOVC_DPTR = 0x93; + public const byte CMD_SUBB = 0x94; + public const byte CMD_ORL_C2 = 0xA0; + public const byte CMD_MOV_C = 0xA2; + public const byte CMD_INC_DPTR = 0xA3; + public const byte CMD_MUL = 0xA4; + public const byte CMD_MOV_IRAM2 = 0xA6; + public const byte CMD_ANL_C2 = 0xB0; + public const byte CMD_CPL = 0xB2; + public const byte CMD_CJNE = 0xB4; + public const byte CMD_PUSH = 0xC0; + public const byte CMD_CLR = 0xC2; + public const byte CMD_SWAP = 0xC4; + public const byte CMD_XCH = 0xC5; + public const byte CMD_POP = 0xD0; + public const byte CMD_SETB = 0xD2; + public const byte CMD_DA = 0xD4; + public const byte CMD_DJNZ_IRAM = 0xD5; + public const byte CMD_XCHD = 0xD6; + public const byte CMD_DJNZ = 0xD8; + public const byte CMD_MOVX = 0xE0; + public const byte CMD_CLR_A = 0xE4; + public const byte CMD_MOV_A = 0xE5; + public const byte CMD_MOVX_A = 0xF0; + public const byte CMD_CPL_A = 0xF4; + public const byte CMD_MOV_A2 = 0xF5; + + private const int DB_DEFAULT_SIZE = 8; + + public ListingCollection? Listing => listing; + private ListingCollection? listing; + + private readonly Dictionary ivBits = new(); + private readonly Dictionary reverseSfrMap = new(); + private readonly Dictionary reverseSfrBitMap = new(); + private readonly Dictionary ivMap = new(); + + public ListingFactory(Type processorType) + { + if (!processorType.GetInterfaces().Contains(typeof(Processors.I80C51))) + { + throw new ArgumentException("Type not implementing interface I80C51", nameof(processorType)); + } + + // build first register map + foreach (PropertyInfo pInfo in processorType.GetProperties()) + { + if (pInfo.GetCustomAttribute() is Processors.SFRAttribute sfrAttr) + { + reverseSfrMap.Add(sfrAttr.Address, pInfo.Name); + + // ports 0-4 are also direct addressable + if (pInfo.Name == "P0" || pInfo.Name == "P1" || pInfo.Name == "P2" || pInfo.Name == "P3" || pInfo.Name == "P4") + { + for(int i = 0; i < 8; i++) + { + reverseSfrBitMap.Add((ushort)(sfrAttr.Address + i), pInfo.Name + "." + i); + } + } + } + } + + // build bitmap + foreach (PropertyInfo pInfo in processorType.GetProperties()) + { + if (pInfo.GetCustomAttribute() is Processors.SFRBitAttribute sfrBitAttr && sfrBitAttr.Addressable) + { + ushort regAddr = reverseSfrMap.FirstOrDefault(sf => sf.Value == sfrBitAttr.SFRName).Key; + reverseSfrBitMap.Add((ushort)(regAddr + sfrBitAttr.Bit), pInfo.Name); + + if ((sfrBitAttr.SFRName == "IEN0" || sfrBitAttr.SFRName == "IEN1") && pInfo.Name != "EA") + { + ivBits.Add(pInfo.Name, sfrBitAttr); + } + } + } + + // build IV Map + foreach (MethodInfo mInfo in processorType.GetMethods()) + { + if (mInfo.GetCustomAttribute() is Processors.IVAttribute ivAttr) + { + ivMap.Add(mInfo.Name["Interrupt_".Length..], ivAttr.Address); + } + } + } + + public ListingFactory WithListing(ListingCollection listing) + { + this.listing = listing; + return this; + } + + public string? TryGetIVLabel(ushort address) + { + if (ivMap.Any(i => i.Value == address)) + { + return ivMap.FirstOrDefault(i => i.Value == address).Key; + } + return null; + } + + public ListingCollection Build(BinaryReader br, string? label = "RESET") + { + if (listing == null) + { + listing = new(); + } + + Stack branches = new(); + + ListingEntry entry; + + ushort lastDptr = 0; + + try + { + while ((entry = ParseEntry(br, label)) != null) + { + RemoveOverlappingDbEntries(entry.Address, (ushort)(entry.Address + entry.Data.Count)); + + if (!listing.Contains(entry.Address)) + { + listing.Add(entry); + } + + label = null; + sbyte signedAddress; + + // collect calls, jumps and branches + switch (entry.Instruction) + { + case InstructionType.JMP: + // assume code jumps should be in range of 20 + if (Math.Abs(entry.Address - lastDptr) < 20) + { + br.BaseStream.Position = lastDptr; + label = GetLabelForAddress(lastDptr); + } + break; + case InstructionType.LJMP: + br.BaseStream.Position = entry.TargetAddress; + label = GetLabelForAddress(entry.TargetAddress); + entry.Arguments.Add(label); + break; + + case InstructionType.AJMP: + entry.TargetAddress = (ushort)((br.BaseStream.Position & 0xF800) | entry.TargetAddress); + br.BaseStream.Position = entry.TargetAddress; + label = GetLabelForAddress(entry.TargetAddress); + entry.Arguments.Add(label); + break; + + case InstructionType.SJMP: + signedAddress = unchecked((sbyte)entry.TargetAddress); + entry.TargetAddress = (ushort)(br.BaseStream.Position + signedAddress); + br.BaseStream.Position = entry.TargetAddress; + label = GetLabelForAddress(entry.TargetAddress); + entry.Arguments.Add(label); + break; + + case InstructionType.ACALL: + entry.TargetAddress = (ushort)((br.BaseStream.Position & 0xF800) | entry.TargetAddress); + entry.Arguments.Add(GetLabelForAddress(entry.TargetAddress)); + branches.Push(entry); + break; + + case InstructionType.LCALL: + entry.Arguments.Add(GetLabelForAddress(entry.TargetAddress)); + branches.Push(entry); + break; + + case InstructionType.DJNZ: + case InstructionType.JBC: + case InstructionType.JB: + case InstructionType.JNB: + case InstructionType.CJNE: + case InstructionType.JC: + case InstructionType.JNC: + case InstructionType.JZ: + case InstructionType.JNZ: + signedAddress = unchecked((sbyte)entry.TargetAddress); + entry.TargetAddress = (ushort)(br.BaseStream.Position + signedAddress); + entry.Arguments.Add(GetLabelForAddress(entry.TargetAddress)); + branches.Push(entry); + break; + + // Track MOV to IEN Registers for later IV Scan + case InstructionType.MOV: + if (entry.Arguments[0] == "IEN0") + { + CheckAddIv("IEN0", entry.Arguments[1], 7, branches); + } + else if (entry.Arguments[0] == "IEN1") + { + CheckAddIv("IEN1", entry.Arguments[1], 8, branches); + } + + // store last DPTR Value for code Jumps (JMP) + if (entry.Arguments[0] == "DPTR") + { + lastDptr = ParseIntermediateUShort(entry.Arguments[1]); + } + break; + + // Track SETB to IEN Registers for later IV Scan + case InstructionType.SETB: + if (ivBits.ContainsKey(entry.Arguments[0])) + { + string ivName = entry.Arguments[0][1..]; + if (ivMap.ContainsKey(ivName)) + { + branches.Push(new ListingEntry() + { + TargetAddress = ivMap[ivName], + Arguments = new() { ivName } + }); + } + } + break; + } + + entry.UpdateStrings(); + + ushort address = (ushort)br.BaseStream.Position; + + RemoveStepDbEntry(address); + + // check if next listing exists or function return to scan next branch from list + if (listing.Contains(address) || entry.Instruction == InstructionType.RET || entry.Instruction == InstructionType.RETI) + { + if (!string.IsNullOrEmpty(label) && string.IsNullOrEmpty(listing.GetByAddress(address)?.Label)) + { + listing.SetLabel(address, label); + } + + bool breakParser = true; + while (branches.Count > 0) + { + ListingEntry branch = branches.Pop(); + label = branch.Arguments.Last(); + + RemoveStepDbEntry(branch.TargetAddress); + + if (listing.GetByAddress(branch.TargetAddress) is ListingEntry listingEntry && string.IsNullOrEmpty(listingEntry.Label)) + { + listing.SetLabel(branch.TargetAddress, label); + } + else if (!listing.Contains(branch.TargetAddress)) + { + address = branch.TargetAddress; + br.BaseStream.Position = branch.TargetAddress; + breakParser = false; + break; + } + } + + if (breakParser) + { + break; + } + } + } + + // scan for data + int dataAddress = 0; + while (dataAddress < br.BaseStream.Length) + { + ushort address = (ushort)dataAddress; + + // step over next listing + if (listing.GetByAddress(address) is ListingEntry listingEntry) + { + dataAddress += listingEntry.Data.Count; + continue; + } + + + int next = (int)br.BaseStream.Length; + + try + { + next = listing.Where(l => l.Address > address).Select(l => l.Address).Min(); + } + catch + { + } + + // read gap + br.BaseStream.Position = address; + byte[] data = br.ReadBytes(next - address); + + int dataPos = 0; + while (dataPos < data.Length) + { + int length = Math.Min(DB_DEFAULT_SIZE, data.Length - dataPos); + + byte[] buffer = data[dataPos..(dataPos + length)]; + + entry = new ListingEntry() + { + Address = address, + Instruction = InstructionType.DB, + Data = buffer.ToList(), + Comment = ToAnsiString(buffer), + Arguments = buffer.Select(c => c.ToString("x2") + 'h').ToList() + }; + entry.UpdateStrings(); + listing.Add(entry); + + address += (ushort)length; + dataPos += length; + } + + dataAddress = next; + } + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + } + + return listing; + } + + private string GetLabelForAddress(ushort address) + { + if (listing!.GetByAddress(address) is ListingEntry entry && !string.IsNullOrEmpty(entry.Label)) + { + return entry.Label; + } + return $"code_{address:X4}"; + } + + private void RemoveStepDbEntry(ushort address) + { + // check if next address is a DB, if yes remove + if (listing!.GetByAddress(address) is ListingEntry entry && entry.Instruction == InstructionType.DB) + { + listing!.RemoveByAddress(address); + } + } + + private void RemoveOverlappingDbEntries(ushort start, ushort end) + { + ListingEntry[]? overlap = listing!.Where(e => e.Instruction == InstructionType.DB && + start < e.Address + e.Data.Count && + e.Address < end)?.ToArray(); + + if (overlap == null || overlap.Length == 0) + { + return; + } + + foreach(ListingEntry entry in overlap) + { + listing!.Remove(entry); + } + } + + private void CheckAddIv(string IEN, string valueStr, int maxBits, Stack branches) + { + byte value = ParseIntermediateByte(valueStr); + for (int i = 0; i < maxBits; i++) + { + string ivName = ivBits.FirstOrDefault(v => v.Value.SFRName == IEN && v.Value.Bit == i).Key; + ivName = ivName[1..]; + byte mask = (byte)(1 << i); + if ((value & mask) != mask) + { + continue; + } + + if (!ivMap.ContainsKey(ivName)) + { + continue; + } + + branches.Push(new ListingEntry() + { + TargetAddress = ivMap[ivName], + Arguments = new() { ivName } + }); + } + } + + public ListingEntry ParseEntry(BinaryReader br, string? label = null) + { + ushort address = (ushort)br.BaseStream.Position; + byte instruction = br.ReadByte(); + + ListingEntry result = new() + { + Address = address, + Label = label, + Data = new() { instruction } + }; + + switch (instruction) + { + case CMD_NOP: + result.Instruction = InstructionType.NOP; + break; + + case CMD_RET: + result.Instruction = InstructionType.RET; + break; + + case CMD_RETI: + result.Instruction = InstructionType.RETI; + break; + + #region Jumps + case CMD_AJMP: // page0 + case CMD_AJMP + 0x20: // page1 + case CMD_AJMP + 0x40: // page2 + case CMD_AJMP + 0x60: // page3 + case CMD_AJMP + 0x80: // page4 + case CMD_AJMP + 0xA0: // page5 + case CMD_AJMP + 0xC0: // page6 + case CMD_AJMP + 0xE0: // page7 + result.Instruction = InstructionType.AJMP; + result.Data.Add(br.ReadByte()); + result.TargetAddress = (ushort)((instruction & 0xe0) << 3 | result.Data[1]); + break; + + case CMD_LJMP: + result.Instruction = InstructionType.LJMP; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.TargetAddress = (ushort)(result.Data[1] << 8 | result.Data[2]); + break; + + case CMD_SJMP: + result.Instruction = InstructionType.SJMP; + result.Data.Add(br.ReadByte()); + result.TargetAddress = result.Data[1]; + break; + + case CMD_JMP: + result.Instruction = InstructionType.JMP; + result.Arguments.Add("@A+DPTR"); + break; + #endregion + + case CMD_ACALL: // page0 + case CMD_ACALL + 0x20: // page1 + case CMD_ACALL + 0x40: // page2 + case CMD_ACALL + 0x60: // page3 + case CMD_ACALL + 0x80: // page4 + case CMD_ACALL + 0xA0: // page5 + case CMD_ACALL + 0xC0: // page6 + case CMD_ACALL + 0xE0: // page7 + result.Instruction = InstructionType.ACALL; + result.Data.Add(br.ReadByte()); + result.TargetAddress = (ushort)((instruction & 0xe0) << 3 | result.Data[1]); + break; + + case CMD_LCALL: + result.Instruction = InstructionType.LCALL; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.TargetAddress = (ushort)(result.Data[1] << 8 | result.Data[2]); + break; + + case CMD_RR: + result.Instruction = InstructionType.RR; + result.Arguments.Add("A"); + break; + + case CMD_RRC: + result.Instruction = InstructionType.RRC; + result.Arguments.Add("A"); + break; + + case CMD_RL: + result.Instruction = InstructionType.RL; + result.Arguments.Add("A"); + break; + + case CMD_RLC: + result.Instruction = InstructionType.RLC; + result.Arguments.Add("A"); + break; + + case CMD_DIV: + result.Instruction = InstructionType.DIV; + result.Arguments.Add("AB"); + break; + + case CMD_MUL: + result.Instruction = InstructionType.MUL; + result.Arguments.Add("AB"); + break; + + case CMD_SWAP: + result.Instruction = InstructionType.SWAP; + result.Arguments.Add("A"); + break; + + case CMD_DA: + result.Instruction = InstructionType.DA; + result.Arguments.Add("A"); + break; + + case CMD_PUSH: + result.Instruction = InstructionType.PUSH; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + break; + + case CMD_POP: + result.Instruction = InstructionType.POP; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + break; + + case CMD_XCHD: // R0 + case CMD_XCHD + 0x01: // R1 + result.Instruction = InstructionType.XCHD; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + break; + + #region CPL // complete + case CMD_CPL: + result.Instruction = InstructionType.CPL; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(BitName(result.Data[1])); + break; + + case CMD_CPL + 0x01: // C + result.Instruction = InstructionType.CPL; + result.Arguments.Add("C"); + break; + + case CMD_CPL_A: + result.Instruction = InstructionType.CPL; + result.Arguments.Add("A"); + break; + #endregion + + #region MOVC // complete + case CMD_MOVC_PC: + result.Instruction = InstructionType.MOVC; + result.Arguments.Add("A"); + result.Arguments.Add("@A+PC"); + break; + + case CMD_MOVC_DPTR: + result.Instruction = InstructionType.MOVC; + result.Arguments.Add("A"); + result.Arguments.Add("@A+DPTR"); + break; + #endregion + + #region MOV // complete + case CMD_MOV_DATA: // A + result.Instruction = InstructionType.MOV; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[1])); + break; + + case CMD_MOV_DATA + 0x01: // iram addr + result.Instruction = InstructionType.MOV; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[2])); + break; + + case CMD_MOV_DATA + 0x02: // @R0 + case CMD_MOV_DATA + 0x03: // @R1 + result.Instruction = InstructionType.MOV; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[1])); + break; + + case CMD_MOV_DATA + 0x04: // R0 + case CMD_MOV_DATA + 0x05: // R1 + case CMD_MOV_DATA + 0x06: // R2 + case CMD_MOV_DATA + 0x07: // R3 + case CMD_MOV_DATA + 0x08: // R4 + case CMD_MOV_DATA + 0x09: // R5 + case CMD_MOV_DATA + 0x0a: // R6 + case CMD_MOV_DATA + 0x0b: // R7 + result.Instruction = InstructionType.MOV; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[1])); + break; + + case CMD_MOV_IRAM: // iram addr + result.Instruction = InstructionType.MOV; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[2])); // command parameter in reverse order. + result.Arguments.Add(RamName(result.Data[1])); // command parameter in reverse order. + break; + + case CMD_MOV_IRAM + 0x01: // iram addr, @R0 + case CMD_MOV_IRAM + 0x02: // iram addr, @R1 + result.Instruction = InstructionType.MOV; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + break; + + case CMD_MOV_IRAM + 0x03: // iram addr, R0 + case CMD_MOV_IRAM + 0x04: // iram addr, R1 + case CMD_MOV_IRAM + 0x05: // iram addr, R2 + case CMD_MOV_IRAM + 0x06: // iram addr, R3 + case CMD_MOV_IRAM + 0x07: // iram addr, R4 + case CMD_MOV_IRAM + 0x08: // iram addr, R5 + case CMD_MOV_IRAM + 0x09: // iram addr, R6 + case CMD_MOV_IRAM + 0x0a: // iram addr, R7 + result.Instruction = InstructionType.MOV; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + break; + + case CMD_MOV_IRAM2: // @R0, iram addr + case CMD_MOV_IRAM2 + 0x01: // @R1, iram addr + result.Instruction = InstructionType.MOV; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + result.Arguments.Add(RamName(result.Data[1])); + break; + + case CMD_MOV_IRAM2 + 0x02: // R0, iram addr + case CMD_MOV_IRAM2 + 0x03: // R1, iram addr + case CMD_MOV_IRAM2 + 0x04: // R2, iram addr + case CMD_MOV_IRAM2 + 0x05: // R3, iram addr + case CMD_MOV_IRAM2 + 0x06: // R4, iram addr + case CMD_MOV_IRAM2 + 0x07: // R5, iram addr + case CMD_MOV_IRAM2 + 0x08: // R6, iram addr + case CMD_MOV_IRAM2 + 0x09: // R7, iram addr + result.Instruction = InstructionType.MOV; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + result.Arguments.Add(RamName(result.Data[1])); + break; + + case CMD_MOV_A: // A, iram addr + result.Instruction = InstructionType.MOV; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(RamName(result.Data[1])); + break; + + case CMD_MOV_A + 0x01: // A, @R0 + case CMD_MOV_A + 0x02: // A, @R1 + result.Instruction = InstructionType.MOV; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + break; + + case CMD_MOV_A + 0x03: // A, R0 + case CMD_MOV_A + 0x04: // A, R1 + case CMD_MOV_A + 0x05: // A, R2 + case CMD_MOV_A + 0x06: // A, R3 + case CMD_MOV_A + 0x07: // A, R4 + case CMD_MOV_A + 0x08: // A, R5 + case CMD_MOV_A + 0x09: // A, R6 + case CMD_MOV_A + 0x0a: // A, R7 + result.Instruction = InstructionType.MOV; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + break; + + case CMD_MOV_DPTR: + result.Instruction = InstructionType.MOV; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.Arguments.Add("DPTR"); + result.Arguments.Add(string.Format("#{0:X4}h", result.Data[1] << 8 | result.Data[2])); + break; + + case CMD_MOV_BIT: + result.Instruction = InstructionType.MOV; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(BitName(result.Data[1])); + result.Arguments.Add("C"); + break; + + case CMD_MOV_C: + result.Instruction = InstructionType.MOV; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("C"); + result.Arguments.Add(BitName(result.Data[1])); + break; + + case CMD_MOV_A2: // iram addr, A + result.Instruction = InstructionType.MOV; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + result.Arguments.Add("A"); + break; + + case CMD_MOV_A2 + 0x01: // @R0, A + case CMD_MOV_A2 + 0x02: // @R1, A + result.Instruction = InstructionType.MOV; + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + result.Arguments.Add("A"); + break; + + case CMD_MOV_A2 + 0x03: // R0, A + case CMD_MOV_A2 + 0x04: // R1, A + case CMD_MOV_A2 + 0x05: // R2, A + case CMD_MOV_A2 + 0x06: // R3, A + case CMD_MOV_A2 + 0x07: // R4, A + case CMD_MOV_A2 + 0x08: // R5, A + case CMD_MOV_A2 + 0x09: // R6, A + case CMD_MOV_A2 + 0x0a: // R7, A + result.Instruction = InstructionType.MOV; + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + result.Arguments.Add("A"); + break; + #endregion + + #region ORL // complete + case CMD_ORL: // iram addr, A + result.Instruction = InstructionType.ORL; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + result.Arguments.Add("A"); + break; + + case CMD_ORL + 0x01: // iram addr, #data + result.Instruction = InstructionType.ORL; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[2])); + break; + + case CMD_ORL + 0x02: // A, #data + result.Instruction = InstructionType.ORL; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[1])); + break; + + case CMD_ORL + 0x03: // A, iram addr + result.Instruction = InstructionType.ORL; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(RamName(result.Data[1])); + break; + + case CMD_ORL + 0x04: // A, @R0 + case CMD_ORL + 0x05: // A, @R1 + result.Instruction = InstructionType.ORL; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + break; + + case CMD_ORL + 0x06: // R0 + case CMD_ORL + 0x07: // R1 + case CMD_ORL + 0x08: // R2 + case CMD_ORL + 0x09: // R3 + case CMD_ORL + 0x0a: // R4 + case CMD_ORL + 0x0b: // R5 + case CMD_ORL + 0x0c: // R6 + case CMD_ORL + 0x0d: // R7 + result.Instruction = InstructionType.ORL; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + break; + + case CMD_ORL_C: // C, bit addr + result.Instruction = InstructionType.ORL; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("C"); + result.Arguments.Add(BitName(result.Data[1])); + break; + + case CMD_ORL_C2: // C, /bit addr + result.Instruction = InstructionType.ORL; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("C"); + result.Arguments.Add("/" + BitName(result.Data[1])); + break; + #endregion + + #region ANL // complete + case CMD_ANL: // iram addr, A + result.Instruction = InstructionType.ANL; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + result.Arguments.Add("A"); + break; + + case CMD_ANL + 0x01: // iram addr, #data + result.Instruction = InstructionType.ANL; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[2])); + break; + + case CMD_ANL + 0x02: // A, #data + result.Instruction = InstructionType.ANL; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[1])); + break; + + case CMD_ANL + 0x03: // A, iram addr + result.Instruction = InstructionType.ANL; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(RamName(result.Data[1])); + break; + + case CMD_ANL + 0x04: // A, @R0 + case CMD_ANL + 0x05: // A, @R1 + result.Instruction = InstructionType.ANL; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + break; + + case CMD_ANL + 0x06: // R0 + case CMD_ANL + 0x07: // R1 + case CMD_ANL + 0x08: // R2 + case CMD_ANL + 0x09: // R3 + case CMD_ANL + 0x0a: // R4 + case CMD_ANL + 0x0b: // R5 + case CMD_ANL + 0x0c: // R6 + case CMD_ANL + 0x0d: // R7 + result.Instruction = InstructionType.ANL; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + break; + + case CMD_ANL_C: // C,bit addr + result.Instruction = InstructionType.ANL; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("C"); + result.Arguments.Add(BitName(result.Data[1])); + break; + + case CMD_ANL_C2: // C,/bit addr + result.Instruction = InstructionType.ANL; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("C"); + result.Arguments.Add("/" + BitName(result.Data[1])); + break; + #endregion + + #region XRL // complete + case CMD_XRL: // iram addr, A + result.Instruction = InstructionType.XRL; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + result.Arguments.Add("A"); + break; + + case CMD_XRL + 0x01: // iram addr, #data + result.Instruction = InstructionType.XRL; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[2])); + break; + + case CMD_XRL + 0x02: // A, #data + result.Instruction = InstructionType.XRL; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[1])); + break; + + case CMD_XRL + 0x03: // A, iram addr + result.Instruction = InstructionType.XRL; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(RamName(result.Data[1])); + break; + + case CMD_XRL + 0x04: // A, @R0 + case CMD_XRL + 0x05: // A, @R1 + result.Instruction = InstructionType.XRL; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + break; + + case CMD_XRL + 0x06: // R0 + case CMD_XRL + 0x07: // R1 + case CMD_XRL + 0x08: // R2 + case CMD_XRL + 0x09: // R3 + case CMD_XRL + 0x0a: // R4 + case CMD_XRL + 0x0b: // R5 + case CMD_XRL + 0x0c: // R6 + case CMD_XRL + 0x0d: // R7 + result.Instruction = InstructionType.XRL; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + break; + #endregion + + #region DJNZ // complete + case CMD_DJNZ_IRAM: + result.Instruction = InstructionType.DJNZ; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + result.TargetAddress = result.Data[2]; + break; + + case CMD_DJNZ: // R0 + case CMD_DJNZ + 0x01: // R1 + case CMD_DJNZ + 0x02: // R2 + case CMD_DJNZ + 0x03: // R3 + case CMD_DJNZ + 0x04: // R4 + case CMD_DJNZ + 0x05: // R5 + case CMD_DJNZ + 0x06: // R6 + case CMD_DJNZ + 0x07: // R7 + result.Instruction = InstructionType.DJNZ; + result.Data.Add(br.ReadByte()); + result.TargetAddress = result.Data[1]; + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + break; + #endregion + + #region CLR // complete + case CMD_CLR: // bit + result.Instruction = InstructionType.CLR; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(BitName(result.Data[1])); + break; + + case CMD_CLR + 0x01: // C + result.Instruction = InstructionType.CLR; + result.Arguments.Add("C"); + break; + + case CMD_CLR_A: + result.Instruction = InstructionType.CLR; + result.Arguments.Add("A"); + break; + #endregion + + #region SETB // complete + case CMD_SETB: // bit addr + result.Instruction = InstructionType.SETB; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(BitName(result.Data[1])); + break; + + case CMD_SETB + 0x01: // C + result.Instruction = InstructionType.SETB; + result.Arguments.Add("C"); + break; + #endregion + + #region Conditional Jumps + case CMD_JBC: + result.Instruction = InstructionType.JBC; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.Arguments.Add(BitName(result.Data[1])); + result.TargetAddress = result.Data[2]; + break; + + case CMD_JB: + result.Instruction = InstructionType.JB; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.Arguments.Add(BitName(result.Data[1])); + result.TargetAddress = result.Data[2]; + break; + + case CMD_JNB: + result.Instruction = InstructionType.JNB; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.Arguments.Add(BitName(result.Data[1])); + result.TargetAddress = result.Data[2]; + break; + + case CMD_JC: + result.Instruction = InstructionType.JC; + result.Data.Add(br.ReadByte()); + result.TargetAddress = result.Data[1]; + break; + + case CMD_JNC: + result.Instruction = InstructionType.JNC; + result.Data.Add(br.ReadByte()); + result.TargetAddress = result.Data[1]; + break; + + case CMD_JZ: + result.Instruction = InstructionType.JZ; + result.Data.Add(br.ReadByte()); + result.TargetAddress = result.Data[1]; + break; + + case CMD_JNZ: + result.Instruction = InstructionType.JNZ; + result.Data.Add(br.ReadByte()); + result.TargetAddress = result.Data[1]; + break; + #endregion + + #region INC // complete + case CMD_INC: // A + result.Instruction = InstructionType.INC; + result.Arguments.Add("A"); + break; + + case CMD_INC + 0x01: // iram addr + result.Instruction = InstructionType.INC; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + break; + + case CMD_INC + 0x02: // @R0 + case CMD_INC + 0x03: // @R1 + result.Instruction = InstructionType.INC; + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + break; + + case CMD_INC + 0x04: // R0 + case CMD_INC + 0x05: // R1 + case CMD_INC + 0x06: // R2 + case CMD_INC + 0x07: // R3 + case CMD_INC + 0x08: // R4 + case CMD_INC + 0x09: // R5 + case CMD_INC + 0x0a: // R6 + case CMD_INC + 0x0b: // R7 + result.Instruction = InstructionType.INC; + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + break; + + case CMD_INC_DPTR: + result.Instruction = InstructionType.INC; + result.Arguments.Add("DPTR"); + break; + #endregion + + #region DEC // complete + case CMD_DEC: // A + result.Instruction = InstructionType.DEC; + result.Arguments.Add("A"); + break; + + case CMD_DEC + 0x01: // iram addr + result.Instruction = InstructionType.DEC; + result.Data.Add(br.ReadByte()); + result.Arguments.Add(RamName(result.Data[1])); + break; + + case CMD_DEC + 0x02: // @R0 + case CMD_DEC + 0x03: // @R1 + result.Instruction = InstructionType.DEC; + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + break; + + case CMD_DEC + 0x04: // R0 + case CMD_DEC + 0x05: // R1 + case CMD_DEC + 0x06: // R2 + case CMD_DEC + 0x07: // R3 + case CMD_DEC + 0x08: // R4 + case CMD_DEC + 0x09: // R5 + case CMD_DEC + 0x0a: // R6 + case CMD_DEC + 0x0b: // R7 + result.Instruction = InstructionType.DEC; + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + break; + #endregion + + #region SUBB // complete + case CMD_SUBB: // #data + result.Instruction = InstructionType.SUBB; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[1])); + break; + + case CMD_SUBB + 0x01: // iram addr + result.Instruction = InstructionType.SUBB; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(RamName(result.Data[1])); + break; + + case CMD_SUBB + 0x02: // @R0 + case CMD_SUBB + 0x03: // @R1 + result.Instruction = InstructionType.SUBB; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + break; + + case CMD_SUBB + 0x04: // R0 + case CMD_SUBB + 0x05: // R1 + case CMD_SUBB + 0x06: // R2 + case CMD_SUBB + 0x07: // R3 + case CMD_SUBB + 0x08: // R4 + case CMD_SUBB + 0x09: // R5 + case CMD_SUBB + 0x0a: // R6 + case CMD_SUBB + 0x0b: // R7 + result.Instruction = InstructionType.SUBB; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + break; + #endregion + + #region MOVX // complete + case CMD_MOVX: // @DPTR + result.Instruction = InstructionType.MOVX; + result.Arguments.Add("A"); + result.Arguments.Add("@DPTR"); + break; + + case CMD_MOVX + 0x02: // @R0 + case CMD_MOVX + 0x03: // @R1 + result.Instruction = InstructionType.MOVX; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + break; + + case CMD_MOVX_A: // @DPTR + result.Instruction = InstructionType.MOVX; + result.Arguments.Add("@DPTR"); + result.Arguments.Add("A"); + break; + + case CMD_MOVX_A + 0x02: // @R0 + case CMD_MOVX_A + 0x03: // @R1 + result.Instruction = InstructionType.MOVX; + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + result.Arguments.Add("A"); + break; + #endregion + + #region CJNE // complete + case CMD_CJNE: // A, #data + result.Instruction = InstructionType.CJNE; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[1])); + result.TargetAddress = result.Data[2]; + break; + + case CMD_CJNE + 0x01: // A, iram addr + result.Instruction = InstructionType.CJNE; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(RamName(result.Data[1])); + result.TargetAddress = result.Data[2]; + break; + + case CMD_CJNE + 0x02: // @R0 + case CMD_CJNE + 0x03: // @R1 + result.Instruction = InstructionType.CJNE; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[1])); + result.TargetAddress = result.Data[2]; + break; + + case CMD_CJNE + 0x04: // R0 + case CMD_CJNE + 0x05: // R1 + case CMD_CJNE + 0x06: // R2 + case CMD_CJNE + 0x07: // R3 + case CMD_CJNE + 0x08: // R4 + case CMD_CJNE + 0x09: // R5 + case CMD_CJNE + 0x0a: // R6 + case CMD_CJNE + 0x0b: // R7 + result.Instruction = InstructionType.CJNE; + result.Data.Add(br.ReadByte()); + result.Data.Add(br.ReadByte()); + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[1])); + result.TargetAddress = result.Data[2]; + break; + #endregion + + #region ADD // complete + case CMD_ADD: // A, #data + result.Instruction = InstructionType.ADD; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[1])); + break; + + case CMD_ADD + 0x01: // A, iram addr + result.Instruction = InstructionType.ADD; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(RamName(result.Data[1])); + break; + + case CMD_ADD + 0x02: // A, @R0 + case CMD_ADD + 0x03: // A, @R1 + result.Instruction = InstructionType.ADD; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + break; + + case CMD_ADD + 0x04: // A, R0 + case CMD_ADD + 0x05: // A, R1 + case CMD_ADD + 0x06: // A, R2 + case CMD_ADD + 0x07: // A, R3 + case CMD_ADD + 0x08: // A, R4 + case CMD_ADD + 0x09: // A, R5 + case CMD_ADD + 0x0a: // A, R6 + case CMD_ADD + 0x0b: // A, R7 + result.Instruction = InstructionType.ADD; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + break; + #endregion + + #region ADDC // complete + case CMD_ADDC: // A, #data + result.Instruction = InstructionType.ADDC; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("#{0:X2}h", result.Data[1])); + break; + + case CMD_ADDC + 0x01: // A, iram addr + result.Instruction = InstructionType.ADDC; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(RamName(result.Data[1])); + break; + + case CMD_ADDC + 0x02: // A, @R0 + case CMD_ADDC + 0x03: // A, @R1 + result.Instruction = InstructionType.ADDC; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + break; + + case CMD_ADDC + 0x04: // A, R0 + case CMD_ADDC + 0x05: // A, R1 + case CMD_ADDC + 0x06: // A, R2 + case CMD_ADDC + 0x07: // A, R3 + case CMD_ADDC + 0x08: // A, R4 + case CMD_ADDC + 0x09: // A, R5 + case CMD_ADDC + 0x0a: // A, R6 + case CMD_ADDC + 0x0b: // A, R7 + result.Instruction = InstructionType.ADDC; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + break; + #endregion + + #region XCH // complete + case CMD_XCH: // iram addr + result.Instruction = InstructionType.XCH; + result.Data.Add(br.ReadByte()); + result.Arguments.Add("A"); + result.Arguments.Add(RamName(result.Data[1])); + break; + + case CMD_XCH + 0x01: // @R0 + case CMD_XCH + 0x02: // @R1 + result.Instruction = InstructionType.XCH; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("@R{0}", instruction & 0x01)); + break; + + case CMD_XCH + 0x03: // R0 + case CMD_XCH + 0x04: // R1 + case CMD_XCH + 0x05: // R2 + case CMD_XCH + 0x06: // R3 + case CMD_XCH + 0x07: // R4 + case CMD_XCH + 0x08: // R5 + case CMD_XCH + 0x09: // R6 + case CMD_XCH + 0x0a: // R7 + result.Instruction = InstructionType.XCH; + result.Arguments.Add("A"); + result.Arguments.Add(string.Format("R{0}", instruction & 0x07)); + break; + #endregion + + case 0xA5: + Debug.WriteLine("Unknown"); + break; + default: + throw new NotImplementedException(instruction.ToString("X2")); + } + + return result; + } + + private string RamName(byte address) + { + if (reverseSfrMap.ContainsKey(address)) + { + return reverseSfrMap[address]; + } + return string.Format("RAM_{0:X2}", address); + } + + private string BitName(byte address) + { + if (reverseSfrBitMap.ContainsKey(address)) + { + return reverseSfrBitMap[address]; + } + + byte ramAddr = (byte)(address & 0xF8); + return string.Format("RAM_{0:X2}.{1}", ramAddr, address & 0x07); + } + + public static byte ParseIntermediateByte(string value) + { + if (!value.StartsWith("#")) + { + throw new ArgumentOutOfRangeException(value); + } + + if (value.EndsWith("h")) + { + return Convert.ToByte(value[1..3], 16); + } + + return (byte)int.Parse(value[1..]); + } + + public static ushort ParseIntermediateUShort(string value) + { + if (!value.StartsWith("#")) + { + throw new ArgumentOutOfRangeException(value); + } + + if (value.EndsWith("h")) + { + return Convert.ToUInt16(value[1..5], 16); + } + + return (ushort)int.Parse(value[1..]); + } + + public static string ToAnsiString(byte[] data) + { + string str = System.Text.Encoding.Default.GetString(data); + return new string(str.Select(c => c < 0x20 ? '.' : c > 0x7f ? '.' : c).ToArray()); + } + } +} diff --git a/Sim80C51/Controls/CPU/ICPUControl.cs b/Sim80C51/Controls/CPU/ICPUControl.cs new file mode 100644 index 0000000..6264aa7 --- /dev/null +++ b/Sim80C51/Controls/CPU/ICPUControl.cs @@ -0,0 +1,9 @@ +using Sim80C51.Processors; + +namespace Sim80C51.Controls.CPU +{ + public interface ICPUControl + { + I80C51? CPUContext { get; } + } +} diff --git a/Sim80C51/Controls/CPU/P80C552.xaml b/Sim80C51/Controls/CPU/P80C552.xaml new file mode 100644 index 0000000..f794ebe --- /dev/null +++ b/Sim80C51/Controls/CPU/P80C552.xaml @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + R0: + + R1: + + R2: + + R3: + + R4: + + R5: + + R6: + + R7: + + + + + + ACC: + + B: + + PSW: + + PCON: + + SP: + + DPH: + + DPL: + + T3: + + EW: + + + + + + IEN0: + + IEN1: + + IP0: + + IP1: + + ADCH: + + ADCON: + + + + + + S0BUF: + + S0CON: + + S1ADR: + + S1DAT: + + S1STA: + + S1CON: + + + + + + P0: + + P1: + + P2: + + P3: + + P4: + + P5: + + + + + + PWMP: + + PWM0: + + PWM1: + + TMOD: + + TCON: + + TH0: + + TL0: + + TH1: + + TL1: + + + + + + + TM2CON: + + TM2IR: + + TMH2: + + TML2: + + RTE: + + STE: + + CTCON: + + + + CTH0: + + CTL0: + + CTH1: + + CTL1: + + CTH2: + + CTL2: + + CTH3: + + CTL3: + + + + CMH0: + + CML0: + + CMH1: + + CML1: + + CMH2: + + CML2: + + + + + + + + + + + + + + + + + + + + + + + + CY + AC + F0 + RS1 + RS0 + OV + F1 + P + + + + + + + + + + + + + + PC: + + DPTR: + + T0: + + T1: + + TM2: + + CT0: + + CT1: + + + + CT2: + + CT3: + + CM0: + + CM1: + + CM2: + + + + + + + + + diff --git a/Sim80C51/Controls/CPU/P80C552.xaml.cs b/Sim80C51/Controls/CPU/P80C552.xaml.cs new file mode 100644 index 0000000..fb5f866 --- /dev/null +++ b/Sim80C51/Controls/CPU/P80C552.xaml.cs @@ -0,0 +1,18 @@ +using Sim80C51.Processors; +using System.Windows.Controls; + +namespace Sim80C51.Controls.CPU +{ + /// + /// Interaction logic for P80C552.xaml + /// + public partial class P80C552 : UserControl, ICPUControl + { + public P80C552() + { + InitializeComponent(); + } + + public I80C51? CPUContext => DataContext as I80C51; + } +} diff --git a/Sim80C51/Controls/ListingEditor.xaml b/Sim80C51/Controls/ListingEditor.xaml new file mode 100644 index 0000000..6523440 --- /dev/null +++ b/Sim80C51/Controls/ListingEditor.xaml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sim80C51/Controls/ListingEditor.xaml.cs b/Sim80C51/Controls/ListingEditor.xaml.cs new file mode 100644 index 0000000..6039773 --- /dev/null +++ b/Sim80C51/Controls/ListingEditor.xaml.cs @@ -0,0 +1,134 @@ +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +namespace Sim80C51.Controls +{ + /// + /// Interaction logic for ListingEditor.xaml + /// + public partial class ListingEditor : UserControl + { + public ListingEditor() + { + InitializeComponent(); + } + + private void ListView_KeyUp(object sender, KeyEventArgs e) + { + if (e.Key == Key.C) + { + (DataContext as ListingEditorContext)?.CreateCode(); + } + else if (e.Key == Key.L) + { + (DataContext as ListingEditorContext)?.UpdateLabel(); + } + else if (e.Key == Key.K) + { + (DataContext as ListingEditorContext)?.UpdateComment(); + } + else if (e.Key == Key.J) + { + (DataContext as ListingEditorContext)?.Jump(); + } + else if (e.Key == Key.B) + { + (DataContext as ListingEditorContext)?.BreakPoint(); + } + } + + private void ListingView_KeyDown(object sender, KeyEventArgs e) + { + Key[] ignore = new[] + { + Key.B, + Key.C, + Key.J, + Key.K, + Key.L, + }; + + if (ignore.Contains(e.Key)) + { + e.Handled = true; + } + } + + private void ListingView_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (ListingView.SelectedItem != null) + { + ListingView.ScrollIntoView(ListingView.SelectedItem); + UIElement selectedElement = (UIElement)ListingView.ItemContainerGenerator.ContainerFromItem(ListingView.SelectedItem); + if (selectedElement != null) + { + ListingView.ScrollIntoView(ListingView.SelectedItem); + selectedElement.Focus(); + } + } + } + + public bool IsItemVisible(int Index) + { + if (ListingView.Items.Count == 0) + { + return false; + } + + UIElement lbi = (UIElement)ListingView.ItemContainerGenerator.ContainerFromIndex(0); + + if (VisualTreeHelper.GetParent(lbi) is VirtualizingStackPanel vsp) + { + int FirstVisibleItem = (int)vsp.VerticalOffset; + int VisibleItemCount = (int)vsp.ViewportHeight; + return Index >= FirstVisibleItem && Index <= FirstVisibleItem + VisibleItemCount; + } + + return false; + } + + /* + public bool IsListScrolledUp() + { + if (ListingView.Items.Count == 0) + { + return false; + } + + UIElement lbi = (UIElement)ListingView.ItemContainerGenerator.ContainerFromIndex(0); + + if (VisualTreeHelper.GetParent(lbi) is VirtualizingStackPanel vsp) + { + int FirstVisibleItem = (int)vsp.VerticalOffset; + return FirstVisibleItem <= 0; + } + return false; + } + + public bool IsListScrolledDown(ListBox lb) + + { + + if (lb.Items.Count == 0) + + return false; + + ListBoxItem lbi = lb.ItemContainerGenerator.ContainerFromIndex(0) as ListBoxItem; + + VirtualizingStackPanel vsp = VisualTreeHelper.GetParent(lbi) as VirtualizingStackPanel; + + int FirstVisibleItem = (int)vsp.VerticalOffset; + + int VisibleItemCount = (int)vsp.ViewportHeight; + + if (VisibleItemCount + FirstVisibleItem == lb.Items.Count) + + return true; + + return false; + + }*/ + } +} diff --git a/Sim80C51/Controls/ListingEditorContext.cs b/Sim80C51/Controls/ListingEditorContext.cs new file mode 100644 index 0000000..7bf29b2 --- /dev/null +++ b/Sim80C51/Controls/ListingEditorContext.cs @@ -0,0 +1,313 @@ +using Sim80C51.Common; +using Sim80C51.Toolbox.Wpf; +using System.IO; +using System.Text.RegularExpressions; +using System.Windows; +using System.Windows.Threading; + +namespace Sim80C51.Controls +{ + public class ListingEditorContext : NotifyPropertyChanged + { + private static readonly InstructionType[] jumpInstructions = new[] { + //Instruction.JMP, Address based Jump + InstructionType.LJMP, + InstructionType.AJMP, + InstructionType.SJMP, + InstructionType.ACALL, + InstructionType.LCALL, + InstructionType.DJNZ, + InstructionType.JBC, + InstructionType.JB, + InstructionType.JNB, + InstructionType.CJNE, + InstructionType.JC, + InstructionType.JNC, + InstructionType.JZ, + InstructionType.JNZ + }; + + private ListingFactory? factory; + private BinaryReader? reader; + + public ListingCollection Listing { get => listing; } + private readonly ListingCollection listing = new(); + + public ListingEntry? SelectedListingEntry { get => selectedListingEntry; set { selectedListingEntry = value; DoPropertyChanged(); } } + private ListingEntry? selectedListingEntry; + + public ushort HighlightAddress { get => highlightAddress; set { highlightAddress = value; DoPropertyChanged(); } } + private ushort highlightAddress = 0; + + public Action? AddBreakPoint { get ; set; } + + public ListingEditorContext() + { + } + + #region UI Calls + public void CreateCode() + { + if (SelectedListingEntry?.Instruction != InstructionType.DB || reader == null || factory == null) + { + return; + } + + string? label = factory.TryGetIVLabel(SelectedListingEntry.Address); + + if (label == null) + { + InputDialog dlg = new("Set Label", "New Label:", $"code_{SelectedListingEntry.Address:X4}") { Owner = Application.Current.MainWindow }; + if (dlg.ShowDialog() == false) + { + return; + } + if (!string.IsNullOrEmpty(dlg.Answer)) + { + label = dlg.Answer; + } + } + + ushort currentAddress = SelectedListingEntry.Address; + SelectedListingEntry = null; + reader.BaseStream.Position = currentAddress; + factory.Build(reader, label); + + Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { })); + + SelectedListingEntry = Listing.GetByAddress(currentAddress); + } + + public void UpdateLabel() + { + if (SelectedListingEntry == null) + { + return; + } + + InputDialog dlg = new("Update Label", "Label:", SelectedListingEntry.Label ?? string.Empty) { Owner = Application.Current.MainWindow }; + if (dlg.ShowDialog() == false) + { + return; + } + + // check updates + if (string.IsNullOrEmpty(dlg.Answer) || dlg.Answer == SelectedListingEntry.Label) + { + return; + } + + // check if exists + if (Listing.GetByLabel(dlg.Answer) != null) + { + MessageBox.Show($"Label '{dlg.Answer}' exists!", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + Listing.SetLabel(SelectedListingEntry, dlg.Answer); + } + + public void UpdateComment() + { + if (SelectedListingEntry == null) + { + return; + } + InputDialog dlg = new("Update Comment", "Comment:", SelectedListingEntry.Comment ?? string.Empty) { Owner = Application.Current.MainWindow }; + if (dlg.ShowDialog() == false) + { + return; + } + SelectedListingEntry.Comment = dlg.Answer; + } + + public void Jump() + { + if (SelectedListingEntry == null) + { + return; + } + + if (jumpInstructions.Contains(SelectedListingEntry.Instruction)) + { + string targetLabel = SelectedListingEntry.Arguments.Last(); + SelectedListingEntry = Listing.GetByLabel(targetLabel); + } + } + + public void BreakPoint() + { + if (SelectedListingEntry == null) + { + return; + } + + AddBreakPoint?.Invoke(SelectedListingEntry.Address); + } + #endregion + + public ListingEntry? GetFromAddress(ushort address) + { + return Listing.GetByAddress(address); + } + + public byte GetCodeByte(ushort address) + { + reader!.BaseStream.Position = address; + return reader.ReadByte(); + } + + public void SetProcessorType(Type processorType) + { + factory = new(processorType); + factory.WithListing(listing); + } + + #region Load and save Listing or binary + public void LoadFromListingStream(Stream stream) + { + if (factory == null) + { + return; + } + + Regex listingLineMatch = new(@"^(?
[0-9a-f]{4}) (?([0-9a-f]{2} )+) +((?