diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e9a0838 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,151 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# All files +[*] +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true + +# Code files +[*.{cs,csproj,sln,props,targets}] +indent_style = tab +indent_size = 2 +tab_width = 2 + +# XML project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_style = tab +indent_size = 2 +tab_width = 2 + +# XML config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_style = tab +indent_size = 2 +tab_width = 2 + +# JSON files +[*.json] +indent_style = tab +indent_size = 2 +tab_width = 2 + +# Markdown files +[*.md] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = false + +# Dotnet code style settings: +[*.cs] +# IDE0055: Fix formatting +dotnet_diagnostic.IDE0055.severity = warning + +# Organize usings +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false + +# this. preferences +dotnet_style_qualification_for_field = true:suggestion +dotnet_style_qualification_for_property = true:suggestion +dotnet_style_qualification_for_method = true:suggestion +dotnet_style_qualification_for_event = true:suggestion + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion +dotnet_style_readonly_field = true:suggestion + +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent + +# CSharp code style settings: +[*.cs] +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = no_change +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents_when_block = false + +# Spacing options +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false + +# Wrapping preferences +csharp_preserve_single_line_statements = false +csharp_preserve_single_line_blocks = true + +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# var preferences +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = false:suggestion + +# Expression-bodied members +csharp_style_expression_bodied_methods = when_on_single_line:suggestion +csharp_style_expression_bodied_constructors = false:suggestion +csharp_style_expression_bodied_operators = when_on_single_line:suggestion +csharp_style_expression_bodied_properties = when_on_single_line:suggestion +csharp_style_expression_bodied_indexers = when_on_single_line:suggestion +csharp_style_expression_bodied_accessors = when_on_single_line:suggestion +csharp_style_expression_bodied_lambdas = when_on_single_line:suggestion +csharp_style_expression_bodied_local_functions = when_on_single_line:suggestion + +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion + +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion + +# Expression preferences +csharp_prefer_braces = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion diff --git a/.github/workflows/dotnet-desktop.yml b/.github/workflows/dotnet-desktop.yml index e76fe70..3219653 100644 --- a/.github/workflows/dotnet-desktop.yml +++ b/.github/workflows/dotnet-desktop.yml @@ -40,7 +40,6 @@ name: .NET Desktop CI on: push: - branches: [ main ] pull_request: branches: [ main ] @@ -68,7 +67,7 @@ jobs: - name: Install .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: 8.0.x + dotnet-version: 10.0.x # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild - name: Setup MSBuild.exe @@ -79,3 +78,9 @@ jobs: run: msbuild $env:Solution_Name /restore /p:Configuration=$env:Configuration env: Configuration: ${{ matrix.configuration }} + + # Run tests + - name: Run tests + run: dotnet test $env:Solution_Name --configuration $env:Configuration --no-build --verbosity normal + env: + Configuration: ${{ matrix.configuration }} diff --git a/MixEmul.sln b/MixEmul.sln index 2e59bb4..e29ae6d 100644 --- a/MixEmul.sln +++ b/MixEmul.sln @@ -23,28 +23,92 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{E76E698C-4 docs\MixEmul Documentation 0.4.0.pdf = docs\MixEmul Documentation 0.4.0.pdf EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MixLib.Tests", "tests\MixLib.Tests\MixLib.Tests.csproj", "{B9BEEA8E-0FE4-4AED-8A59-C1E8245F604F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MixAssembler.Tests", "tests\MixAssembler.Tests\MixAssembler.Tests.csproj", "{3E248D49-7799-449E-87C5-E5EA0FC49185}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {3CE2CBD5-6C29-437B-844B-A32C1029128A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3CE2CBD5-6C29-437B-844B-A32C1029128A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3CE2CBD5-6C29-437B-844B-A32C1029128A}.Debug|x64.ActiveCfg = Debug|Any CPU + {3CE2CBD5-6C29-437B-844B-A32C1029128A}.Debug|x64.Build.0 = Debug|Any CPU + {3CE2CBD5-6C29-437B-844B-A32C1029128A}.Debug|x86.ActiveCfg = Debug|Any CPU + {3CE2CBD5-6C29-437B-844B-A32C1029128A}.Debug|x86.Build.0 = Debug|Any CPU {3CE2CBD5-6C29-437B-844B-A32C1029128A}.Release|Any CPU.ActiveCfg = Release|Any CPU {3CE2CBD5-6C29-437B-844B-A32C1029128A}.Release|Any CPU.Build.0 = Release|Any CPU + {3CE2CBD5-6C29-437B-844B-A32C1029128A}.Release|x64.ActiveCfg = Release|Any CPU + {3CE2CBD5-6C29-437B-844B-A32C1029128A}.Release|x64.Build.0 = Release|Any CPU + {3CE2CBD5-6C29-437B-844B-A32C1029128A}.Release|x86.ActiveCfg = Release|Any CPU + {3CE2CBD5-6C29-437B-844B-A32C1029128A}.Release|x86.Build.0 = Release|Any CPU {8947C313-9E7E-4558-A9C1-DCB59F4C32E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8947C313-9E7E-4558-A9C1-DCB59F4C32E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8947C313-9E7E-4558-A9C1-DCB59F4C32E2}.Debug|x64.ActiveCfg = Debug|Any CPU + {8947C313-9E7E-4558-A9C1-DCB59F4C32E2}.Debug|x64.Build.0 = Debug|Any CPU + {8947C313-9E7E-4558-A9C1-DCB59F4C32E2}.Debug|x86.ActiveCfg = Debug|Any CPU + {8947C313-9E7E-4558-A9C1-DCB59F4C32E2}.Debug|x86.Build.0 = Debug|Any CPU {8947C313-9E7E-4558-A9C1-DCB59F4C32E2}.Release|Any CPU.ActiveCfg = Release|Any CPU {8947C313-9E7E-4558-A9C1-DCB59F4C32E2}.Release|Any CPU.Build.0 = Release|Any CPU + {8947C313-9E7E-4558-A9C1-DCB59F4C32E2}.Release|x64.ActiveCfg = Release|Any CPU + {8947C313-9E7E-4558-A9C1-DCB59F4C32E2}.Release|x64.Build.0 = Release|Any CPU + {8947C313-9E7E-4558-A9C1-DCB59F4C32E2}.Release|x86.ActiveCfg = Release|Any CPU + {8947C313-9E7E-4558-A9C1-DCB59F4C32E2}.Release|x86.Build.0 = Release|Any CPU {5865F55B-9F09-47FC-8DB6-84AF7B5990EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5865F55B-9F09-47FC-8DB6-84AF7B5990EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5865F55B-9F09-47FC-8DB6-84AF7B5990EE}.Debug|x64.ActiveCfg = Debug|Any CPU + {5865F55B-9F09-47FC-8DB6-84AF7B5990EE}.Debug|x64.Build.0 = Debug|Any CPU + {5865F55B-9F09-47FC-8DB6-84AF7B5990EE}.Debug|x86.ActiveCfg = Debug|Any CPU + {5865F55B-9F09-47FC-8DB6-84AF7B5990EE}.Debug|x86.Build.0 = Debug|Any CPU {5865F55B-9F09-47FC-8DB6-84AF7B5990EE}.Release|Any CPU.ActiveCfg = Release|Any CPU {5865F55B-9F09-47FC-8DB6-84AF7B5990EE}.Release|Any CPU.Build.0 = Release|Any CPU + {5865F55B-9F09-47FC-8DB6-84AF7B5990EE}.Release|x64.ActiveCfg = Release|Any CPU + {5865F55B-9F09-47FC-8DB6-84AF7B5990EE}.Release|x64.Build.0 = Release|Any CPU + {5865F55B-9F09-47FC-8DB6-84AF7B5990EE}.Release|x86.ActiveCfg = Release|Any CPU + {5865F55B-9F09-47FC-8DB6-84AF7B5990EE}.Release|x86.Build.0 = Release|Any CPU + {B9BEEA8E-0FE4-4AED-8A59-C1E8245F604F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9BEEA8E-0FE4-4AED-8A59-C1E8245F604F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9BEEA8E-0FE4-4AED-8A59-C1E8245F604F}.Debug|x64.ActiveCfg = Debug|Any CPU + {B9BEEA8E-0FE4-4AED-8A59-C1E8245F604F}.Debug|x64.Build.0 = Debug|Any CPU + {B9BEEA8E-0FE4-4AED-8A59-C1E8245F604F}.Debug|x86.ActiveCfg = Debug|Any CPU + {B9BEEA8E-0FE4-4AED-8A59-C1E8245F604F}.Debug|x86.Build.0 = Debug|Any CPU + {B9BEEA8E-0FE4-4AED-8A59-C1E8245F604F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9BEEA8E-0FE4-4AED-8A59-C1E8245F604F}.Release|Any CPU.Build.0 = Release|Any CPU + {B9BEEA8E-0FE4-4AED-8A59-C1E8245F604F}.Release|x64.ActiveCfg = Release|Any CPU + {B9BEEA8E-0FE4-4AED-8A59-C1E8245F604F}.Release|x64.Build.0 = Release|Any CPU + {B9BEEA8E-0FE4-4AED-8A59-C1E8245F604F}.Release|x86.ActiveCfg = Release|Any CPU + {B9BEEA8E-0FE4-4AED-8A59-C1E8245F604F}.Release|x86.Build.0 = Release|Any CPU + {3E248D49-7799-449E-87C5-E5EA0FC49185}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E248D49-7799-449E-87C5-E5EA0FC49185}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E248D49-7799-449E-87C5-E5EA0FC49185}.Debug|x64.ActiveCfg = Debug|Any CPU + {3E248D49-7799-449E-87C5-E5EA0FC49185}.Debug|x64.Build.0 = Debug|Any CPU + {3E248D49-7799-449E-87C5-E5EA0FC49185}.Debug|x86.ActiveCfg = Debug|Any CPU + {3E248D49-7799-449E-87C5-E5EA0FC49185}.Debug|x86.Build.0 = Debug|Any CPU + {3E248D49-7799-449E-87C5-E5EA0FC49185}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E248D49-7799-449E-87C5-E5EA0FC49185}.Release|Any CPU.Build.0 = Release|Any CPU + {3E248D49-7799-449E-87C5-E5EA0FC49185}.Release|x64.ActiveCfg = Release|Any CPU + {3E248D49-7799-449E-87C5-E5EA0FC49185}.Release|x64.Build.0 = Release|Any CPU + {3E248D49-7799-449E-87C5-E5EA0FC49185}.Release|x86.ActiveCfg = Release|Any CPU + {3E248D49-7799-449E-87C5-E5EA0FC49185}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B9BEEA8E-0FE4-4AED-8A59-C1E8245F604F} = {0AB3BF05-4346-4AA6-1389-037BE0695223} + {3E248D49-7799-449E-87C5-E5EA0FC49185} = {0AB3BF05-4346-4AA6-1389-037BE0695223} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1B4C5CE4-B012-43B1-B37A-034B5866A9AE} EndGlobalSection diff --git a/README.md b/README.md index 539ab8a..ebbdb16 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # ![MixEmul](http://rbergen.home.xs4all.nl/mixemullogo.jpg)   MixEmul + [![.NET Desktop CI](https://github.com/rbergen/MixEmul/actions/workflows/dotnet-desktop.yml/badge.svg)](https://github.com/rbergen/MixEmul/actions/workflows/dotnet-desktop.yml) + ## What is it? + MixEmul is an emulator for the MIX computer that is described in The Art of Computer Programming (TAOCP) series of books by D.E. Knuth. MIX is a hypothetical, non-existent computer with features similar to those of real computers of the 1960s. It ‑ or more accurately, its assembly language MIXAL ‑ is used as the foundation for the text of the books. MixEmul completely emulates the entire MIX instruction set. With that I mean that MixEmul not only performs the actions that an instruction should ‑ like multiplying when it encounters a MUL instruction ‑, but it does it in the way MIX would. For example, the time unit ("tick") counts that are included in TAOCP Volume 1 are implemented as well. Also, as TAOCP specifies, I/O operations are performed in the background. @@ -13,15 +16,18 @@ Oh, I'd almost forget, it incorporates a MIXAL assembler too. This means that yo In a few words: MixEmul contains all the features that I think I need to be able to better absorb the contents of the TAOCP books. ## Can I get it? + Well, obviously the answer is yes; you're looking at its sources as we speak. However, getting it to run doesn't even require you to build it yourself. The latest binary version I have seen working myself can be downloaded from [the releases page](https://github.com/rbergen/MixEmul/releases). ## What's with all the security warnings? + For incomprehensible reasons, MIX enthusiasts are not as common as you or I would like. This means that MixEmul is not downloaded as often as, for instance, Google Chrome. Furthermore, I loved writing MixEmul and I love maintaining it, but out of principle I refuse to cash out for things like code signing certificates. This means that on an average computer, your browser, your OS and probably your virus scanner will all object to downloading and running MixEmul. I'm afraid you'll just have to trust me when I say that MixEmul was not written to cause you or your computer any harm. Over the years, I have spent quite a few hours figuring out first how to improve MixEmul and then how to code those improvements; causing trouble is never an improvement in my book. So again, trust me when I say that MixEmul is safe. Or don't trust me, and miss out on what I still consider to be the best MIX emulator available. :) ## Can I support the author? + Yes. You can support me through [PayPal](https://www.paypal.me/rutgervanbergen). _- Rutger van Bergen - [github.com/rbergen](https://github.com/rbergen)_ diff --git a/docs/Changes.txt b/docs/Changes.txt index e3b98e6..24d4a92 100644 --- a/docs/Changes.txt +++ b/docs/Changes.txt @@ -9,6 +9,13 @@ x: Bugfix ========================================= +0.5.2.0 (2026-03-08) +* Bumped all projects to .NET 10.0 +* Applied C# 14 based improvements +* Added unit tests for a number of (foundational) classes +* Fixed a number of minor bugs +* Fixed a number of formatting issues in the source code + 0.5.1.2 (2024-08-23) * Bumped Lib and Assembler to .NET 8.0 * Applied .NET 8.0 and C# 12 based improvements @@ -219,4 +226,4 @@ x Fixed IndexOutOfRangeException that could occur when using the arrow down key x Minor errors in class member naming and visibility fixed 0.1.2424.30916 (2006-08-21) -* First public release \ No newline at end of file +* First public release diff --git a/src/MixAssembler/Finding/AssemblyError.cs b/src/MixAssembler/Finding/AssemblyError.cs index d913378..185fcea 100644 --- a/src/MixAssembler/Finding/AssemblyError.cs +++ b/src/MixAssembler/Finding/AssemblyError.cs @@ -2,8 +2,8 @@ namespace MixAssembler.Finding { - public class AssemblyError(int lineNumber, LineSection lineSection, int causeStartIndex, int causeLength, ValidationError error) + public class AssemblyError(int lineNumber, LineSection lineSection, int causeStartIndex, int causeLength, ValidationError error) : ValidationFinding(Severity.Error, lineNumber, lineSection, causeStartIndex, causeLength, error) { - } + } } diff --git a/src/MixAssembler/Finding/AssemblyFinding.cs b/src/MixAssembler/Finding/AssemblyFinding.cs index f0acf1b..6ea3ccc 100644 --- a/src/MixAssembler/Finding/AssemblyFinding.cs +++ b/src/MixAssembler/Finding/AssemblyFinding.cs @@ -4,7 +4,7 @@ namespace MixAssembler.Finding { public abstract class AssemblyFinding(Severity severity, int lineNumber, LineSection section, int startCharIndex, int length) - { + { public int Length => length; public int LineNumber => lineNumber; public LineSection LineSection => section; diff --git a/src/MixAssembler/Finding/AssemblyInfo.cs b/src/MixAssembler/Finding/AssemblyInfo.cs index 99f8f84..868ce73 100644 --- a/src/MixAssembler/Finding/AssemblyInfo.cs +++ b/src/MixAssembler/Finding/AssemblyInfo.cs @@ -1,6 +1,6 @@ namespace MixAssembler.Finding { - public class AssemblyInfo(string message, int lineNumber, LineSection lineSection, int startCharIndex, int length) + public class AssemblyInfo(string message, int lineNumber, LineSection lineSection, int startCharIndex, int length) : AssemblyFinding(MixLib.Misc.Severity.Info, lineNumber, lineSection, startCharIndex, length) { public override string Message diff --git a/src/MixAssembler/Finding/AssemblyWarning.cs b/src/MixAssembler/Finding/AssemblyWarning.cs index bbe10e5..2ec5a11 100644 --- a/src/MixAssembler/Finding/AssemblyWarning.cs +++ b/src/MixAssembler/Finding/AssemblyWarning.cs @@ -2,8 +2,8 @@ namespace MixAssembler.Finding { - public class AssemblyWarning(int lineNumber, LineSection lineSection, int causeStartIndex, int causeLength, ValidationError error) + public class AssemblyWarning(int lineNumber, LineSection lineSection, int causeStartIndex, int causeLength, ValidationError error) : ValidationFinding(Severity.Warning, lineNumber, lineSection, causeStartIndex, causeLength, error) { - } + } } diff --git a/src/MixAssembler/Finding/ValidationFinding.cs b/src/MixAssembler/Finding/ValidationFinding.cs index 53d3736..3cf5325 100644 --- a/src/MixAssembler/Finding/ValidationFinding.cs +++ b/src/MixAssembler/Finding/ValidationFinding.cs @@ -2,7 +2,7 @@ namespace MixAssembler.Finding { - public class ValidationFinding(Severity severity, int lineNumber, LineSection lineSection, int causeStartIndex, int causeLength, ValidationError error) + public class ValidationFinding(Severity severity, int lineNumber, LineSection lineSection, int causeStartIndex, int causeLength, ValidationError error) : AssemblyFinding(severity, lineNumber, lineSection, causeStartIndex, causeLength) { public ValidationError Error => error; diff --git a/src/MixAssembler/Instruction/LoaderInstructionParameters.cs b/src/MixAssembler/Instruction/LoaderInstructionParameters.cs index 1dc3c2d..b628916 100644 --- a/src/MixAssembler/Instruction/LoaderInstructionParameters.cs +++ b/src/MixAssembler/Instruction/LoaderInstructionParameters.cs @@ -5,18 +5,18 @@ namespace MixAssembler.Instruction { - /// - /// This class contains the parameters that are required to create an instance object of a specific loader instruction "template". - /// - /// The (static) ParseAddressField method is to be called during the parsing of the MIXAL source (first assembly pass), to create a parameter object based on the loader instruction's address field. - /// The CreateInstance method should be called during the second assembly pass, to create the actual loader instruction instance. - /// - /// - /// Constructor for this class. - /// - /// The parameter value associated with the instruction. The type of the value depends on the loader instruction it is connected with - /// The length (character count) of the value in the source code it was parsed from - public class LoaderInstructionParameters(IValue value, int textLength) : IInstructionParameters + /// + /// This class contains the parameters that are required to create an instance object of a specific loader instruction "template". + /// + /// The (static) ParseAddressField method is to be called during the parsing of the MIXAL source (first assembly pass), to create a parameter object based on the loader instruction's address field. + /// The CreateInstance method should be called during the second assembly pass, to create the actual loader instruction instance. + /// + /// + /// Constructor for this class. + /// + /// The parameter value associated with the instruction. The type of the value depends on the loader instruction it is connected with + /// The length (character count) of the value in the source code it was parsed from + public class LoaderInstructionParameters(IValue value, int textLength) : IInstructionParameters { /// /// Create a loader instruction instance with the parameters contained in this object. diff --git a/src/MixAssembler/Instruction/MixInstructionParameters.cs b/src/MixAssembler/Instruction/MixInstructionParameters.cs index 814f85c..9240b30 100644 --- a/src/MixAssembler/Instruction/MixInstructionParameters.cs +++ b/src/MixAssembler/Instruction/MixInstructionParameters.cs @@ -118,7 +118,7 @@ private MixByte GetFieldSpecValue(AssemblingStatus status, MixInstruction mixIns /// /// public static IInstructionParameters ParseAddressField(string addressField, ParsingStatus status) - { + { var indexCharIndex = addressField.IndexOf(','); var sectionCharIndex = addressField.IndexOf('(', Math.Max(indexCharIndex, 0)); diff --git a/src/MixAssembler/Instruction/ParsedSourceLine.cs b/src/MixAssembler/Instruction/ParsedSourceLine.cs index 987badf..e4751a6 100644 --- a/src/MixAssembler/Instruction/ParsedSourceLine.cs +++ b/src/MixAssembler/Instruction/ParsedSourceLine.cs @@ -10,7 +10,7 @@ namespace MixAssembler.Instruction /// instruction does not necessarily have to be executable by MIX; it can also concern a loader instruction, for instance. In /// addition, the instruction may be invalid. /// - public class ParsedSourceLine(int lineNumber, string locationField, string opField, string addressField, string comment, InstructionBase instruction, IInstructionParameters parameters) + public class ParsedSourceLine(int lineNumber, string locationField, string opField, string addressField, string comment, InstructionBase instruction, IInstructionParameters parameters) : PreInstruction(instruction, parameters) { public string AddressField => addressField; diff --git a/src/MixAssembler/Instruction/PreInstruction.cs b/src/MixAssembler/Instruction/PreInstruction.cs index e33532f..d3fe8e9 100644 --- a/src/MixAssembler/Instruction/PreInstruction.cs +++ b/src/MixAssembler/Instruction/PreInstruction.cs @@ -7,7 +7,7 @@ namespace MixAssembler.Instruction /// Specifically, it contains an instruction "template" and the parameters required to create an actual instance of that instruction. /// public class PreInstruction(InstructionBase instruction, IInstructionParameters parameters) - { + { public InstructionBase Instruction => instruction; public IInstructionParameters Parameters => parameters; diff --git a/src/MixAssembler/MixAssembler.csproj b/src/MixAssembler/MixAssembler.csproj index 7e5bdde..bb75cd6 100644 --- a/src/MixAssembler/MixAssembler.csproj +++ b/src/MixAssembler/MixAssembler.csproj @@ -1,6 +1,6 @@  - net8.0-windows7.0 + net10.0-windows7.0 Library diff --git a/src/MixAssembler/Symbol/LocalSymbol.cs b/src/MixAssembler/Symbol/LocalSymbol.cs index 174ee0a..b4d1e67 100644 --- a/src/MixAssembler/Symbol/LocalSymbol.cs +++ b/src/MixAssembler/Symbol/LocalSymbol.cs @@ -118,7 +118,7 @@ private class Reference(LocalSymbol referee, Reference.Directions direction) : I private readonly Directions mDirection = direction; private readonly LocalSymbol mReferee = referee; - public long GetMagnitude(int currentAddress) + public long GetMagnitude(int currentAddress) => GetValue(currentAddress); public Word.Signs GetSign(int currentAddress) diff --git a/src/MixEmul/Components/AssemblyFindingListView.cs b/src/MixEmul/Components/AssemblyFindingListView.cs index 940aa4b..40725b8 100644 --- a/src/MixEmul/Components/AssemblyFindingListView.cs +++ b/src/MixEmul/Components/AssemblyFindingListView.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using MixAssembler.Finding; @@ -53,7 +54,7 @@ private void SelectedIndexChanged(object sender, EventArgs e) private void AddFinding(AssemblyFinding finding) { - var item = new ListViewItem(new string[] { finding.Severity.ToString(), (finding.LineNumber == int.MinValue) ? string.Empty : (finding.LineNumber + 1).ToString(), finding.Message }, (int)finding.Severity) + var item = new ListViewItem([finding.Severity.ToString(), (finding.LineNumber == int.MinValue) ? string.Empty : (finding.LineNumber + 1).ToString(), finding.Message], (int)finding.Severity) { Tag = finding }; @@ -61,6 +62,7 @@ private void AddFinding(AssemblyFinding finding) mFindingsListView.Items.Add(item); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public AssemblyFindingCollection Findings { set @@ -91,6 +93,7 @@ public AssemblyFinding SelectedFinding } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ImageList SeverityImageList { get => mFindingsListView.SmallImageList; diff --git a/src/MixEmul/Components/BinaryFileDeviceEditor.cs b/src/MixEmul/Components/BinaryFileDeviceEditor.cs index 7580605..1e7c6a7 100644 --- a/src/MixEmul/Components/BinaryFileDeviceEditor.cs +++ b/src/MixEmul/Components/BinaryFileDeviceEditor.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.IO; using System.Timers; using System.Windows.Forms; @@ -140,6 +141,7 @@ private void Initialize(long recordCount, long wordsPerRecord, OpenStreamCallbac ProcessVisibility(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string RecordName { get => this.recordName; @@ -159,6 +161,7 @@ private void ProcessSupportsAppending() this.truncateButton.Visible = supportsAppending; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ShowReadOnly { get => this.showReadOnly; @@ -570,12 +573,14 @@ private void Revert() Update(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ReadOnly { get => this.readOnlyCheckBox.Checked; set => this.readOnlyCheckBox.Checked = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ResizeInProgress { get => this.wordEditorList.ResizeInProgress; diff --git a/src/MixEmul/Components/DeviceEditorForm.cs b/src/MixEmul/Components/DeviceEditorForm.cs index fe42862..b45fdc0 100644 --- a/src/MixEmul/Components/DeviceEditorForm.cs +++ b/src/MixEmul/Components/DeviceEditorForm.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Windows.Forms; using MixLib; using MixLib.Device; @@ -119,6 +120,7 @@ public void ShowDevice(MixDevice device) } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Devices Devices { get => this.devices; diff --git a/src/MixEmul/Components/DeviceMixByteCollectionEditor.cs b/src/MixEmul/Components/DeviceMixByteCollectionEditor.cs index d239a9f..86d49c7 100644 --- a/src/MixEmul/Components/DeviceMixByteCollectionEditor.cs +++ b/src/MixEmul/Components/DeviceMixByteCollectionEditor.cs @@ -1,6 +1,7 @@ namespace MixGui.Components { using System; + using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using MixGui.Events; @@ -110,6 +111,7 @@ public void UpdateLayout() this.charTextBox.UpdateLayout(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int MixByteCollectionIndex { get => this.mixByteCollectionIndex; @@ -126,6 +128,7 @@ public int MixByteCollectionIndex private void SetIndexLabelText() => this.mixByteCollectionIndexLabel.Text = this.mixByteCollectionIndex.ToString("D" + this.indexCharCount) + ":"; + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int IndexCharCount { get => this.indexCharCount; @@ -143,6 +146,7 @@ public int IndexCharCount } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IMixByteCollection DeviceMixByteCollection { get => this.deviceMixByteCollection; @@ -154,12 +158,14 @@ public IMixByteCollection DeviceMixByteCollection } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ReadOnly { get => this.charTextBox.ReadOnly; set => this.charTextBox.ReadOnly = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IMixByteCollection MixByteCollectionValue { get => DeviceMixByteCollection; diff --git a/src/MixEmul/Components/DeviceStatusControl.cs b/src/MixEmul/Components/DeviceStatusControl.cs index cd818c0..25b9d1c 100644 --- a/src/MixEmul/Components/DeviceStatusControl.cs +++ b/src/MixEmul/Components/DeviceStatusControl.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using MixGui.Settings; @@ -17,6 +18,7 @@ public class DeviceStatusControl : UserControl private readonly ToolStripMenuItem resetMenuItem; private readonly Label statusLabel; + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ToolTip ToolTip { get; set; } public DeviceStatusControl() : this(null) { } @@ -120,6 +122,7 @@ public void UpdateLayout() Update(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public MixDevice Device { get => this.device; diff --git a/src/MixEmul/Components/DeviceWordEditor.cs b/src/MixEmul/Components/DeviceWordEditor.cs index bca8a93..2919b52 100644 --- a/src/MixEmul/Components/DeviceWordEditor.cs +++ b/src/MixEmul/Components/DeviceWordEditor.cs @@ -1,5 +1,6 @@ using System; using System.Drawing; +using System.ComponentModel; using System.Windows.Forms; using MixGui.Events; using MixGui.Settings; @@ -102,6 +103,7 @@ public void UpdateLayout() this.fullWordEditor.UpdateLayout(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int WordIndex { get => this.wordIndex; @@ -112,6 +114,7 @@ public int WordIndex } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IFullWord DeviceWord { get => this.deviceWord; @@ -122,12 +125,14 @@ public IFullWord DeviceWord } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ReadOnly { get => this.fullWordEditor.ReadOnly; set => this.fullWordEditor.ReadOnly = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IWord WordValue { get => DeviceWord; diff --git a/src/MixEmul/Components/DevicesControl.cs b/src/MixEmul/Components/DevicesControl.cs index 6d7c967..4b19a0c 100644 --- a/src/MixEmul/Components/DevicesControl.cs +++ b/src/MixEmul/Components/DevicesControl.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using MixGui.Events; @@ -169,6 +170,7 @@ public void UpdateLayout() control.UpdateLayout(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public MixLib.Devices Devices { get => this.devices; @@ -202,6 +204,7 @@ public MixLib.Devices Devices private void DevicesControl_DoubleClick(object sender, EventArgs e) => DeviceDoubleClick?.Invoke(this, new DeviceEventArgs(((DeviceStatusControl)sender).Device)); + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public LayoutStructure Structure { get => this.structure; @@ -222,6 +225,7 @@ public enum Breaks Section } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ToolTip ToolTip { get => this.toolTip; diff --git a/src/MixEmul/Components/EditorList.cs b/src/MixEmul/Components/EditorList.cs index 5e759e0..57cc124 100644 --- a/src/MixEmul/Components/EditorList.cs +++ b/src/MixEmul/Components/EditorList.cs @@ -1,7 +1,9 @@ using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Drawing; +using System.Threading; using System.Windows.Forms; using MixGui.Utils; using MixLib.Type; @@ -10,7 +12,7 @@ namespace MixGui.Components { public class EditorList : UserControl, IEnumerable where T : IEditor { - private readonly object editorsSyncRoot; + private readonly Lock editorsLock; private VScrollBar indexScrollBar; private int firstVisibleIndex; private bool readOnly; @@ -43,7 +45,7 @@ public EditorList(int minIndex = 0, int maxIndex = -1, CreateEditorCallback crea this.resizeInProgress = false; this.sizeAdaptationPending = false; - this.editorsSyncRoot = new object(); + this.editorsLock = new(); this.editors = []; InitializeComponent(); @@ -108,7 +110,7 @@ private void AdaptToSize() int editorsToAddCount = (visibleEditorCount - this.editors.Count) + 1; FirstVisibleIndex = this.firstVisibleIndex; - lock (this.editorsSyncRoot) + lock (this.editorsLock) { IsReloading = true; @@ -310,6 +312,7 @@ public void MakeIndexVisible(int index) FirstVisibleIndex = index; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public LoadEditorCallback LoadEditor { get => this.loadEditor; @@ -325,6 +328,7 @@ public LoadEditorCallback LoadEditor } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public CreateEditorCallback CreateEditor { get => this.createEditor; @@ -343,6 +347,7 @@ public CreateEditorCallback CreateEditor } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ResizeInProgress { get => this.resizeInProgress; @@ -383,7 +388,7 @@ private void This_SizeChanged(object sender, EventArgs e) public new void Update() { - lock (this.editorsSyncRoot) + lock (this.editorsLock) { for (int i = 0; i < this.editors.Count; i++) { @@ -399,7 +404,7 @@ public void UpdateLayout() { SuspendLayout(); - lock (this.editorsSyncRoot) + lock (this.editorsLock) { foreach (T editor in this.editors) editor.UpdateLayout(); @@ -408,6 +413,7 @@ public void UpdateLayout() ResumeLayout(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int FirstVisibleIndex { get => this.firstVisibleIndex; @@ -429,7 +435,7 @@ public int FirstVisibleIndex this.firstVisibleIndex = value; - lock (this.editorsSyncRoot) + lock (this.editorsLock) { IsReloading = true; @@ -464,6 +470,7 @@ public int FirstVisibleIndex } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ReadOnly { get => this.readOnly; @@ -473,7 +480,7 @@ public bool ReadOnly return; this.readOnly = value; - lock (this.editorsSyncRoot) + lock (this.editorsLock) { foreach (T editor in this.editors) editor.ReadOnly = this.readOnly; @@ -497,6 +504,7 @@ public int VisibleEditorCount } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int MaxIndex { get => this.maxIndex; @@ -510,6 +518,7 @@ public int MaxIndex } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int MinIndex { get => this.minIndex; diff --git a/src/MixEmul/Components/FieldKeyEventArgs.cs b/src/MixEmul/Components/FieldKeyEventArgs.cs index 9f20b75..461526a 100644 --- a/src/MixEmul/Components/FieldKeyEventArgs.cs +++ b/src/MixEmul/Components/FieldKeyEventArgs.cs @@ -8,5 +8,5 @@ public class FieldKeyEventArgs(Keys keyData, FieldTypes field, int? index) : Ind public FieldTypes Field => @field; public FieldKeyEventArgs(Keys keyData, FieldTypes field) : this(keyData, field, null) { } - } + } } diff --git a/src/MixEmul/Components/FullWordEditor.cs b/src/MixEmul/Components/FullWordEditor.cs index d5e4500..52ba7b7 100644 --- a/src/MixEmul/Components/FullWordEditor.cs +++ b/src/MixEmul/Components/FullWordEditor.cs @@ -1,5 +1,6 @@ using System; using System.Drawing; +using System.ComponentModel; using System.Windows.Forms; using MixGui.Events; using MixLib.Type; @@ -142,6 +143,7 @@ public void UpdateLayout() this.wordCharTextBox.UpdateLayout(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IFullWord FullWord { get => this.fullWord; @@ -153,6 +155,7 @@ public IFullWord FullWord } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ReadOnly { get => this.readOnly; @@ -167,6 +170,7 @@ public bool ReadOnly } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IWord WordValue { get => FullWord; diff --git a/src/MixEmul/Components/IndexKeyEventArgs.cs b/src/MixEmul/Components/IndexKeyEventArgs.cs index 6aa7ec6..9248a76 100644 --- a/src/MixEmul/Components/IndexKeyEventArgs.cs +++ b/src/MixEmul/Components/IndexKeyEventArgs.cs @@ -5,5 +5,5 @@ namespace MixGui.Components public class IndexKeyEventArgs(Keys keyData, int? index) : KeyEventArgs(keyData) { public int? Index => index; - } + } } diff --git a/src/MixEmul/Components/InstructionInstanceTextBox.cs b/src/MixEmul/Components/InstructionInstanceTextBox.cs index 938b48d..7ca8ab8 100644 --- a/src/MixEmul/Components/InstructionInstanceTextBox.cs +++ b/src/MixEmul/Components/InstructionInstanceTextBox.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Drawing; using System.Linq; using System.Text; @@ -43,10 +44,15 @@ public class InstructionInstanceTextBox : RichTextBox, IWordEditor, IEscapeConsu private readonly ToolStripMenuItem showIndexedAddressMenuItem; private readonly ContextMenuStrip contextMenu; + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int MemoryMinIndex { get; set; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int MemoryMaxIndex { get; set; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IndexedAddressCalculatorCallback IndexedAddressCalculatorCallback { get; set; } = null; + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public SymbolCollection Symbols { get; set; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int MemoryAddress { get; set; } @@ -89,7 +95,8 @@ public InstructionInstanceTextBox(IFullWord instructionWord = null) Update(); } - public bool ShowSourceLineToolTip + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool ShowSourceLineToolTip { get => this.showShourceLineToolTip; set @@ -360,6 +367,7 @@ private void This_TextChanged(object sender, EventArgs e) SetEditMode(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new string Text { get => base.Text; @@ -518,6 +526,7 @@ public void UpdateLayout() ResumeLayout(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IFullWord InstructionWord { get => this.instructionWord; @@ -538,6 +547,7 @@ public IFullWord InstructionWord } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ToolTip ToolTip { get => this.toolTip; @@ -548,6 +558,7 @@ public ToolTip ToolTip } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IWord WordValue { get => this.instructionWord; diff --git a/src/MixEmul/Components/LinkedItemsSelectorControl.cs b/src/MixEmul/Components/LinkedItemsSelectorControl.cs index ca7d3cd..4b490a3 100644 --- a/src/MixEmul/Components/LinkedItemsSelectorControl.cs +++ b/src/MixEmul/Components/LinkedItemsSelectorControl.cs @@ -159,8 +159,7 @@ private LinkedItem InsertItem(LinkedItem insertBefore, T item) Previous = insertBefore.Previous }; - if (insertee.Previous != null) - insertee.Previous.Next = insertee; + insertee.Previous?.Next = insertee; insertee.Next = insertBefore; insertBefore.Previous = insertee; @@ -199,10 +198,10 @@ public void Clear() public class ItemSelectedEventArgs(T selectedItem) : EventArgs { public T SelectedItem { get; private set; } = selectedItem; - } + } public class LinkedItem(LinkedItem previous, T item, LinkedItem next) - { + { public LinkedItem Previous { get; set; } = previous; public LinkedItem Next { get; set; } = next; public T Item { get; private set; } = item; diff --git a/src/MixEmul/Components/LogListView.cs b/src/MixEmul/Components/LogListView.cs index 3853680..3c981ef 100644 --- a/src/MixEmul/Components/LogListView.cs +++ b/src/MixEmul/Components/LogListView.cs @@ -1,5 +1,6 @@ using System; using System.Drawing; +using System.ComponentModel; using System.Windows.Forms; using MixGui.Events; using MixLib.Misc; @@ -73,14 +74,14 @@ public LogListView() } public void AddLogLine(LogLine line) - => this.listView.Items.Insert(0, new ListViewItem(new string[] - { - line.Severity.ToString(), - line.ModuleName ?? string.Empty, - (line.Address == -1) ? string.Empty : line.Address.ToString("D4"), - line.Title ?? string.Empty, - line.Message ?? string.Empty - }, (int)line.Severity)); + => this.listView.Items.Insert(0, new ListViewItem( + [ + line.Severity.ToString(), + line.ModuleName ?? string.Empty, + (line.Address == -1) ? string.Empty : line.Address.ToString("D4"), + line.Title ?? string.Empty, + line.Message ?? string.Empty + ], (int)line.Severity)); protected virtual void OnAddressSelected(AddressSelectedEventArgs args) => AddressSelected?.Invoke(this, args); @@ -143,6 +144,7 @@ public int SelectedAddress } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ImageList SeverityImageList { get => this.listView.SmallImageList; diff --git a/src/MixEmul/Components/LongValueTextBox.cs b/src/MixEmul/Components/LongValueTextBox.cs index 11989c8..eb9cf2f 100644 --- a/src/MixEmul/Components/LongValueTextBox.cs +++ b/src/MixEmul/Components/LongValueTextBox.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using MixGui.Settings; @@ -21,6 +22,7 @@ public class LongValueTextBox : TextBox, IEscapeConsumer, INavigableControl private bool updating; public bool SupportSign { get; private set; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ClearZero { get; set; } public event ValueChangedEventHandler ValueChanged; @@ -291,6 +293,7 @@ private void ChangeSign() } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Word.Signs Sign { get => this.sign; @@ -356,6 +359,7 @@ public void UpdateLayout() ResumeLayout(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public long LongValue { get => this.sign.ApplyTo(this.magnitude); @@ -374,6 +378,7 @@ public long LongValue } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public long Magnitude { get => this.magnitude; @@ -386,6 +391,7 @@ public long Magnitude } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public long MaxValue { get => this.maxValue; @@ -400,6 +406,7 @@ public long MaxValue } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public long MinValue { get => this.minValue; @@ -414,6 +421,7 @@ public long MinValue } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool SupportNegativeZero { get => this.supportNegativeZero; @@ -435,12 +443,12 @@ public override string Text public class ValueChangedEventArgs(Word.Signs oldSign, long oldMagnitude, Word.Signs newSign, long newMagnitude) : EventArgs { - public long OldMagnitude { get; private set; } = oldMagnitude; - public Word.Signs OldSign { get; private set; } = oldSign; - public long NewMagnitude { get; private set; } = newMagnitude; - public Word.Signs NewSign { get; private set; } = newSign; + public long OldMagnitude { get; private set; } = oldMagnitude; + public Word.Signs OldSign { get; private set; } = oldSign; + public long NewMagnitude { get; private set; } = newMagnitude; + public Word.Signs NewSign { get; private set; } = newSign; - public long NewValue + public long NewValue => NewSign.ApplyTo(NewMagnitude); public long OldValue diff --git a/src/MixEmul/Components/LongValueToolStripTextBox.cs b/src/MixEmul/Components/LongValueToolStripTextBox.cs index d457467..4b097a1 100644 --- a/src/MixEmul/Components/LongValueToolStripTextBox.cs +++ b/src/MixEmul/Components/LongValueToolStripTextBox.cs @@ -39,24 +39,28 @@ public bool SupportSign private void Control_SizeChanged(object sender, EventArgs e) => Size = Control.Size; + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public long LongValue { get => ((LongValueTextBox)Control).LongValue; set => ((LongValueTextBox)Control).LongValue = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public long MaxValue { get => ((LongValueTextBox)Control).MaxValue; set => ((LongValueTextBox)Control).MaxValue = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public long MinValue { get => ((LongValueTextBox)Control).MinValue; set => ((LongValueTextBox)Control).MinValue = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ClearZero { get => ((LongValueTextBox)Control).ClearZero; diff --git a/src/MixEmul/Components/MemoryEditor.cs b/src/MixEmul/Components/MemoryEditor.cs index 92c6402..3b04a42 100644 --- a/src/MixEmul/Components/MemoryEditor.cs +++ b/src/MixEmul/Components/MemoryEditor.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.ComponentModel; using System.Drawing; using System.Linq; using System.Windows.Forms; @@ -45,7 +46,7 @@ public MemoryEditor(IMemory memory = null) this.readOnly = false; this.markedAddress = -1; - this.profilingMaxCounts = new long[Enum.GetValues(typeof(GuiSettings.ProfilingInfoType)).Length]; + this.profilingMaxCounts = new long[Enum.GetValues().Length]; this.breakpoints = SortedList.Synchronized([]); @@ -92,6 +93,7 @@ private void AddressDoubleClick(object sender, EventArgs e) private void MemoryEditor_SizeChanged(object sender, EventArgs e) => SetUpDownButtonStates(NavigationDirection.None); + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ToolTip ToolTip { get => this.toolTip; @@ -437,6 +439,7 @@ private void FirstAddressTextBox_ValueChanged(LongValueTextBox source, LongValue this.addressHistorySelector.AddItem(oldViewInfo, new EditorListViewInfo { FirstVisibleIndex = this.wordEditorList.FirstVisibleIndex }); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IndexedAddressCalculatorCallback IndexedAddressCalculatorCallback { get => this.indexedAddressCalculatorCallback; @@ -452,14 +455,14 @@ public IndexedAddressCalculatorCallback IndexedAddressCalculatorCallback } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ResizeInProgress { get => this.wordEditorList != null && this.wordEditorList.ResizeInProgress; set { - if (this.wordEditorList != null) - this.wordEditorList.ResizeInProgress = value; + this.wordEditorList?.ResizeInProgress = value; } } @@ -486,13 +489,13 @@ public void UpdateLayout() ResumeLayout(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int FirstVisibleAddress { get => this.wordEditorList != null ? this.wordEditorList.FirstVisibleIndex : 0; set { - if (this.wordEditorList != null) - this.wordEditorList.FirstVisibleIndex = value; + this.wordEditorList?.FirstVisibleIndex = value; } } @@ -530,6 +533,7 @@ public void FindMatch(SearchParameters options) activeEditor.Select(result.FieldIndex, options.SearchText.Length); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int MarkedAddress { get => this.markedAddress; @@ -554,6 +558,7 @@ private void SetAddressMarkIfVisible(bool mark) ((MemoryWordEditor)this.wordEditorList[this.markedAddress - firstVisibleIndex]).Marked = mark; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IMemory Memory { get => this.memory; @@ -569,6 +574,7 @@ public IMemory Memory } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ReadOnly { get => this.readOnly; @@ -578,11 +584,11 @@ public bool ReadOnly return; this.readOnly = value; - if (this.wordEditorList != null) - this.wordEditorList.ReadOnly = this.readOnly; + this.wordEditorList?.ReadOnly = this.readOnly; } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public SymbolCollection Symbols { get => this.symbols; diff --git a/src/MixEmul/Components/MemoryExportDialog.cs b/src/MixEmul/Components/MemoryExportDialog.cs index e53db39..f4d4640 100644 --- a/src/MixEmul/Components/MemoryExportDialog.cs +++ b/src/MixEmul/Components/MemoryExportDialog.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Windows.Forms; namespace MixGui.Components @@ -8,6 +9,7 @@ public partial class MemoryExportDialog : Form public MemoryExportDialog() => InitializeComponent(); + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int MinMemoryIndex { set @@ -18,6 +20,7 @@ public int MinMemoryIndex } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int MaxMemoryIndex { set @@ -28,18 +31,21 @@ public int MaxMemoryIndex } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int FromAddress { get => (int)this.fromAddressUpDown.Value; set => this.fromAddressUpDown.Value = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int ToAddress { get => (int)this.toAddressUpDown.Value; set => this.toAddressUpDown.Value = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int ProgramCounter { get => (int)this.programCounterUpDown.Value; diff --git a/src/MixEmul/Components/MemoryWordEditor.cs b/src/MixEmul/Components/MemoryWordEditor.cs index ac31058..9763ce2 100644 --- a/src/MixEmul/Components/MemoryWordEditor.cs +++ b/src/MixEmul/Components/MemoryWordEditor.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using MixGui.Events; @@ -112,6 +113,7 @@ private void AddressLabel_DoubleClick(object sender, EventArgs e) private void BreakPointBox_CheckedChanged(object sender, EventArgs e) => OnBreakpointCheckedChanged(e); + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public GetMaxProfilingCountCallback GetMaxProfilingCount { get => this.getMaxProfilingCount; @@ -337,12 +339,14 @@ private void UpdateProfilingLayout(bool force = false) } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int MemoryMinIndex { get => this.instructionTextBox.MemoryMinIndex; set => this.instructionTextBox.MemoryMinIndex = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int MemoryMaxIndex { get => this.instructionTextBox.MemoryMaxIndex; @@ -438,18 +442,21 @@ public void UpdateLayout() UpdateProfilingLayout(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool BreakPointChecked { get => this.breakPointBox.Checked; set => this.breakPointBox.Checked = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IndexedAddressCalculatorCallback IndexedAddressCalculatorCallback { get => this.instructionTextBox.IndexedAddressCalculatorCallback; set => this.instructionTextBox.IndexedAddressCalculatorCallback = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool Marked { get => this.marked; @@ -463,6 +470,7 @@ public bool Marked } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IMemoryFullWord MemoryWord { get => this.memoryWord; @@ -490,6 +498,7 @@ public IMemoryFullWord MemoryWord } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ReadOnly { get => this.readOnly; @@ -504,12 +513,14 @@ public bool ReadOnly } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public SymbolCollection Symbols { get => this.instructionTextBox.Symbols; set => this.instructionTextBox.Symbols = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ToolTip ToolTip { set @@ -519,6 +530,7 @@ public ToolTip ToolTip } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IWord WordValue { get => MemoryWord; diff --git a/src/MixEmul/Components/MixByteCollectionCharTextBox.cs b/src/MixEmul/Components/MixByteCollectionCharTextBox.cs index 5eb24ab..c74f195 100644 --- a/src/MixEmul/Components/MixByteCollectionCharTextBox.cs +++ b/src/MixEmul/Components/MixByteCollectionCharTextBox.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Drawing; using System.Linq; using System.Windows.Forms; @@ -20,6 +21,7 @@ public class MixByteCollectionCharTextBox : TextBox, IMixByteCollectionEditor, I private bool updating; private IMixByteCollection byteCollection; + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool UseEditMode { get; set; } public event MixByteCollectionEditorValueChangedEventHandler ValueChanged; @@ -271,6 +273,7 @@ private void UpdateValue() Update(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IMixByteCollection MixByteCollectionValue { get => this.byteCollection; diff --git a/src/MixEmul/Components/MixByteCollectionEditorList.cs b/src/MixEmul/Components/MixByteCollectionEditorList.cs index 555858f..cad6c37 100644 --- a/src/MixEmul/Components/MixByteCollectionEditorList.cs +++ b/src/MixEmul/Components/MixByteCollectionEditorList.cs @@ -1,8 +1,8 @@  namespace MixGui.Components { - public class MixByteCollectionEditorList(int maxWordCount = 0, EditorList.CreateEditorCallback createEditor = null, EditorList.LoadEditorCallback loadEditor = null) + public class MixByteCollectionEditorList(int maxWordCount = 0, EditorList.CreateEditorCallback createEditor = null, EditorList.LoadEditorCallback loadEditor = null) : EditorList(0, maxWordCount - 1, createEditor, loadEditor) { - } + } } diff --git a/src/MixEmul/Components/MixByteTextBox.cs b/src/MixEmul/Components/MixByteTextBox.cs index 3b7c32d..2762752 100644 --- a/src/MixEmul/Components/MixByteTextBox.cs +++ b/src/MixEmul/Components/MixByteTextBox.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using MixGui.Settings; @@ -181,6 +182,7 @@ public void UpdateLayout() ResumeLayout(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public MixByte MixByteValue { get => this.byteValue; @@ -199,9 +201,9 @@ public sealed override string Text public class ValueChangedEventArgs(MixByte oldValue, MixByte newValue) : EventArgs { - public MixByte NewValue { get; private set; } = newValue; - public MixByte OldValue { get; private set; } = oldValue; - } + public MixByte NewValue { get; private set; } = newValue; + public MixByte OldValue { get; private set; } = oldValue; + } public delegate void ValueChangedEventHandler(MixByteTextBox sender, MixByteTextBox.ValueChangedEventArgs e); } diff --git a/src/MixEmul/Components/PreferencesForm.cs b/src/MixEmul/Components/PreferencesForm.cs index 9a7aa9c..30acd84 100644 --- a/src/MixEmul/Components/PreferencesForm.cs +++ b/src/MixEmul/Components/PreferencesForm.cs @@ -241,7 +241,7 @@ private void FillTickCountSelectionBox() this.tickCountSelectionBox.Items.AddRange([.. list]); if (this.tickCountSelectionBox.Items.Count > 0) - this.tickCountSelectionBox.SelectedIndex = 0; + this.tickCountSelectionBox.SelectedIndex = 0; UpdateTickCountControls((TickCountComboBoxItem)this.tickCountSelectionBox.SelectedItem); } @@ -1033,7 +1033,7 @@ private void OkButton_Click(object sender, EventArgs e) this.configuration.FloatingPointMemoryWordCount = this.floatingPointMemoryWordCount; - this.configuration.LoaderCards = this.loaderCardsDefaultButton.Enabled ? this.loaderCardTextBoxes.Select(box => box.MixByteCollectionValue.ToString(true)).ToArray() : null; + this.configuration.LoaderCards = this.loaderCardsDefaultButton.Enabled ? [.. this.loaderCardTextBoxes.Select(box => box.MixByteCollectionValue.ToString(true))] : null; try { @@ -1047,8 +1047,8 @@ private void OkButton_Click(object sender, EventArgs e) DialogResult = DialogResult.OK; } - private void TickCountBox_ValueChanged(LongValueTextBox source, LongValueTextBox.ValueChangedEventArgs args) - => SetTickCountValue(); + private void TickCountBox_ValueChanged(LongValueTextBox source, LongValueTextBox.ValueChangedEventArgs args) + => SetTickCountValue(); private void TickCountDefaultButton_Click(object sender, EventArgs e) { @@ -1248,14 +1248,14 @@ private void FloatingPointMemoryWordCountBox_ValueChanged(LongValueTextBox sourc /// Instances of this class are used to store the color settings /// private class ColorComboBoxItem(string name) - { - private readonly string mDisplayName = GetDisplayName(name); - public bool IsDefault { get; private set; } = true; - public string Name { get; private set; } = name; + { + private readonly string mDisplayName = GetDisplayName(name); + public bool IsDefault { get; private set; } = true; + public string Name { get; private set; } = name; - private Color mSetColor; + private Color mSetColor; - public bool IsBackground => !Name.EndsWith("Text", StringComparison.Ordinal); + public bool IsBackground => !Name.EndsWith("Text", StringComparison.Ordinal); public override string ToString() => mDisplayName; @@ -1294,11 +1294,11 @@ public Color Color /// Instances of this class are used to store the device file settings /// private class DeviceFileComboBoxItem(FileBasedDevice device) - { - private readonly FileBasedDevice mDevice = device; - private string mFilePath; + { + private readonly FileBasedDevice mDevice = device; + private string mFilePath; - public int Id => mDevice.Id; + public int Id => mDevice.Id; public bool IsDefault => mFilePath == null; @@ -1317,13 +1317,13 @@ public string FilePath /// Instances of this class are used to store the tick count settings /// private class TickCountComboBoxItem(string name) - { - private readonly string mDisplayName = GetDisplayName(name); - private bool mIsDefault = true; - private readonly string mName = name; - private int mSetTickCount; + { + private readonly string mDisplayName = GetDisplayName(name); + private bool mIsDefault = true; + private readonly string mName = name; + private int mSetTickCount; - public bool IsDefault => mIsDefault; + public bool IsDefault => mIsDefault; public string Name => mName; @@ -1361,10 +1361,10 @@ public int TickCount } private class DeviceReloadIntervalComboBoxItem(int milliSeconds) - { - public int MilliSeconds { get; set; } = milliSeconds; + { + public int MilliSeconds { get; set; } = milliSeconds; - public override string ToString() + public override string ToString() { if (MilliSeconds < 1000) return MilliSeconds.ToString("##0 ms"); diff --git a/src/MixEmul/Components/RegistersEditor.cs b/src/MixEmul/Components/RegistersEditor.cs index b24f25b..cf59f7d 100644 --- a/src/MixEmul/Components/RegistersEditor.cs +++ b/src/MixEmul/Components/RegistersEditor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using MixLib.Type; @@ -246,7 +247,7 @@ public RegistersEditor(MixLib.Registers registers = null) ResumeLayout(false); } - private void OverflowBox_CheckedChanged(object sender, EventArgs e) + private void OverflowBox_CheckedChanged(object sender, EventArgs e) => this.registers.OverflowIndicator = this.overflowBox.Checked; private void This_KeyDown(object sender, KeyEventArgs e) @@ -293,6 +294,7 @@ public void UpdateLayout() editor.UpdateLayout(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ReadOnly { get => this.readOnly; @@ -311,6 +313,7 @@ public bool ReadOnly } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public MixLib.Registers Registers { get => this.registers; diff --git a/src/MixEmul/Components/SearchDialog.cs b/src/MixEmul/Components/SearchDialog.cs index dde2c22..bd762e0 100644 --- a/src/MixEmul/Components/SearchDialog.cs +++ b/src/MixEmul/Components/SearchDialog.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Windows.Forms; using MixGui.Events; using MixLib.Type; @@ -18,9 +19,10 @@ public SearchDialog() private void FieldCheckedChanged(object sender, EventArgs e) => SetFindButtonEnabledState(); - private void SearchTextBox_ValueChanged(IMixByteCollectionEditor sender, MixByteCollectionEditorValueChangedEventArgs args) + private void SearchTextBox_ValueChanged(IMixByteCollectionEditor sender, MixByteCollectionEditorValueChangedEventArgs args) => SetFindButtonEnabledState(); + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public SearchParameters SearchParameters { get => this.searchParameters; @@ -36,7 +38,7 @@ public SearchParameters SearchParameters } } - private void SetFindButtonEnabledState() + private void SetFindButtonEnabledState() => this.findButton.Enabled = this.searchTextBox.MixByteCollectionValue.ToString(true).Trim() != string.Empty && (this.valueCheckBox.Checked || this.charsCheckBox.Checked || this.instructionCheckBox.Checked); private void FindButton_Click(object sender, EventArgs e) diff --git a/src/MixEmul/Components/SourceAndFindingsForm.cs b/src/MixEmul/Components/SourceAndFindingsForm.cs index 29eeeaa..4bc6039 100644 --- a/src/MixEmul/Components/SourceAndFindingsForm.cs +++ b/src/MixEmul/Components/SourceAndFindingsForm.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Windows.Forms; using MixAssembler.Finding; using MixAssembler.Instruction; @@ -28,7 +29,7 @@ public SourceAndFindingsForm() this.findingListView.SelectionChanged += FindingListView_SelectionChanged; } - public void UpdateLayout() + public void UpdateLayout() => this.sourceControl.UpdateLayout(); private void InitializeComponent() @@ -107,9 +108,9 @@ private void InitializeComponent() // // this.findingListView // - this.findingListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); + this.findingListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.findingListView.Location = new System.Drawing.Point(0, 0); this.findingListView.Name = "_findingListView"; this.findingListView.SeverityImageList = null; @@ -163,7 +164,7 @@ private void InitializeComponent() } - private void FindingListView_SelectionChanged(AssemblyFindingListView sender, AssemblyFindingListView.SelectionChangedEventArgs args) + private void FindingListView_SelectionChanged(AssemblyFindingListView sender, AssemblyFindingListView.SelectionChangedEventArgs args) => this.sourceControl.MarkedFinding = args.SelectedFinding; public void SetInstructionsAndFindings(PreInstruction[] instructions, InstructionInstanceBase[] instances, AssemblyFindingCollection findings) @@ -179,7 +180,7 @@ public void SetInstructionsAndFindings(PreInstruction[] instructions, Instructio private void SetStatusBarText(AssemblyFindingCollection findings) { - var severityNames = Enum.GetNames(typeof(Severity)); + var severityNames = Enum.GetNames(); int[] severityCounts = new int[severityNames.Length]; foreach (AssemblyFinding finding in findings) @@ -214,6 +215,7 @@ private void SetStatusBarText(AssemblyFindingCollection findings) this.toolStripStatusLabel.Text = message; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ImageList SeverityImageList { get => this.findingListView.SeverityImageList; diff --git a/src/MixEmul/Components/SourceCodeControl.cs b/src/MixEmul/Components/SourceCodeControl.cs index 0aa1069..30b0cae 100644 --- a/src/MixEmul/Components/SourceCodeControl.cs +++ b/src/MixEmul/Components/SourceCodeControl.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using MixAssembler; @@ -13,7 +14,7 @@ namespace MixGui.Components public class SourceCodeControl : UserControl { private const string LineNumberSeparator = " │ "; - private static readonly Color[] findingColors = + private static readonly Color[] findingColors = [ GuiSettings.GetColor(GuiSettings.DebugText), GuiSettings.GetColor(GuiSettings.InfoText), @@ -245,6 +246,7 @@ public void UpdateLayout() this.commentColor = GuiSettings.GetColor(GuiSettings.CommentFieldText); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public AssemblyFindingCollection Findings { set @@ -274,6 +276,7 @@ public AssemblyFindingCollection Findings } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public PreInstruction[] Instructions { set @@ -317,6 +320,7 @@ public PreInstruction[] Instructions } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public AssemblyFinding MarkedFinding { get => this.markedFinding; @@ -338,29 +342,29 @@ private enum MarkOperation } private class ProcessedSourceLine(ParsedSourceLine sourceLine, int lineTextIndex) - { - public ParsedSourceLine SourceLine { get; private set; } = sourceLine; - public int LineTextIndex { get; private set; } = lineTextIndex; + { + public ParsedSourceLine SourceLine { get; private set; } = sourceLine; + public int LineTextIndex { get; private set; } = lineTextIndex; - public int CommentTextIndex => - !SourceLine.IsCommentLine ? AddressTextIndex + AddressTextLength + Parser.FieldSpacing : LineTextIndex; + public int CommentTextIndex => + !SourceLine.IsCommentLine ? AddressTextIndex + AddressTextLength + Parser.FieldSpacing : LineTextIndex; public int LineTextLength => - (SourceLine.Comment == string.Empty ? AddressTextIndex + AddressTextLength : CommentTextIndex + SourceLine.Comment.Length) - LineTextIndex; + (SourceLine.Comment == string.Empty ? AddressTextIndex + AddressTextLength : CommentTextIndex + SourceLine.Comment.Length) - LineTextIndex; - public int LocTextIndex + public int LocTextIndex => LineTextIndex; - public int LocTextLength + public int LocTextLength => !SourceLine.IsCommentLine ? Math.Max(Parser.MinLocLength, SourceLine.LocationField.Length) : 0; - public int OpTextIndex + public int OpTextIndex => !SourceLine.IsCommentLine ? LocTextIndex + LocTextLength + Parser.FieldSpacing : LineTextIndex; - public int OpTextLength + public int OpTextLength => !SourceLine.IsCommentLine ? Math.Max(Parser.MinOpLength, SourceLine.OpField.Length) : 0; - public int AddressTextIndex + public int AddressTextIndex => !SourceLine.IsCommentLine ? OpTextIndex + OpTextLength + Parser.FieldSpacing : LineTextIndex; public int AddressTextLength diff --git a/src/MixEmul/Components/SymbolListView.cs b/src/MixEmul/Components/SymbolListView.cs index c76d3c5..bb6b5af 100644 --- a/src/MixEmul/Components/SymbolListView.cs +++ b/src/MixEmul/Components/SymbolListView.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Windows.Forms; using MixAssembler.Symbol; using MixGui.Events; @@ -13,7 +14,9 @@ public partial class SymbolListView : UserControl private const int NameFieldIndex = 0; private const int ValueFieldIndex = 1; + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int MemoryMinIndex { get; set; } = 0; + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int MemoryMaxIndex { get; set; } = 0; public event AddressSelectedHandler AddressSelected; @@ -33,19 +36,19 @@ public SymbolListView() this.listView.SelectedIndexChanged += ListView_SelectedIndexChanged; } - private void SymbolValueTextBox_TextChanged(object sender, EventArgs e) + private void SymbolValueTextBox_TextChanged(object sender, EventArgs e) => SetEnabledStates(); - private void SymbolNameTextBox_TextChanged(object sender, EventArgs e) + private void SymbolNameTextBox_TextChanged(object sender, EventArgs e) => SetEnabledStates(); - private void SetEnabledStates() + private void SetEnabledStates() => SetEnabledStates(true); - private void ListView_DoubleClick(object sender, EventArgs e) + private void ListView_DoubleClick(object sender, EventArgs e) => ValueSelected(); - protected virtual void OnAddressSelected(AddressSelectedEventArgs args) + protected virtual void OnAddressSelected(AddressSelectedEventArgs args) => AddressSelected?.Invoke(this, args); private void SetEnabledStates(bool updateSelectedItem) @@ -70,6 +73,7 @@ private void SetEnabledStates(bool updateSelectedItem) } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public SymbolCollection Symbols { get => this.symbols; diff --git a/src/MixEmul/Components/TeletypeForm.cs b/src/MixEmul/Components/TeletypeForm.cs index 1d0f8b9..f12c871 100644 --- a/src/MixEmul/Components/TeletypeForm.cs +++ b/src/MixEmul/Components/TeletypeForm.cs @@ -29,10 +29,10 @@ public TeletypeForm(MixLib.Device.TeletypeDevice teleType) TeletypeDevice = teleType; } - private void ClearButton_Click(object sender, EventArgs e) + private void ClearButton_Click(object sender, EventArgs e) => ClearOutput(); - private void SendButton_Click(object sender, EventArgs e) + private void SendButton_Click(object sender, EventArgs e) => SendInput(); private void This_Activated(object sender, EventArgs e) @@ -302,17 +302,20 @@ private void This_VisibleChanged(object sender, EventArgs e) this.toolStripStatusLabel.Text = "Idle"; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool EchoInput { get => this.echoInputCheckBox.Checked; set => this.echoInputCheckBox.Checked = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string StatusText { set => this.toolStripStatusLabel.Text = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public MixLib.Device.TeletypeDevice TeletypeDevice { get => this.teletypeDevice; @@ -322,12 +325,12 @@ public MixLib.Device.TeletypeDevice TeletypeDevice { this.teletypeDevice = value; - if (this.teletypeDevice != null) - this.teletypeDevice.OutputAdded += OutputAdded; + this.teletypeDevice?.OutputAdded += OutputAdded; } } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new bool TopMost { get => base.TopMost; diff --git a/src/MixEmul/Components/TextFileDeviceEditor.cs b/src/MixEmul/Components/TextFileDeviceEditor.cs index c32145d..5d5d060 100644 --- a/src/MixEmul/Components/TextFileDeviceEditor.cs +++ b/src/MixEmul/Components/TextFileDeviceEditor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.IO; using System.Linq; using System.Timers; @@ -66,31 +67,31 @@ public TextFileDeviceEditor() this.loading = false; } - public bool SupportsAppending + public bool SupportsAppending => !this.readOnlyCheckBox.Checked; - private bool LoadRecords() + private bool LoadRecords() => LoadRecords(LoadErrorHandlingMode.AbortOnError); - public new void Update() + public new void Update() => this.mixByteCollectionEditorList.Update(); - public bool Save() + public bool Save() => WriteRecords(); - private void SaveButton_Click(object sender, EventArgs e) + private void SaveButton_Click(object sender, EventArgs e) => WriteRecords(); - private void RevertButton_Click(object sender, EventArgs e) + private void RevertButton_Click(object sender, EventArgs e) => Revert(); - private void DeviceFileWatcher_Event(object sender, FileSystemEventArgs e) + private void DeviceFileWatcher_Event(object sender, FileSystemEventArgs e) => HandleFileWatcherEvent(); - private void DeviceEditor_VisibleChanged(object sender, EventArgs e) + private void DeviceEditor_VisibleChanged(object sender, EventArgs e) => ProcessVisibility(); - private void DeviceFileWatcher_Renamed(object sender, RenamedEventArgs e) + private void DeviceFileWatcher_Renamed(object sender, RenamedEventArgs e) => HandleFileWatcherEvent(); public void Initialize(int bytesPerRecord, OpenStreamCallback openStream, ReadMixByteCallback readBytes, WriteMixByteCallback writeBytes) @@ -119,6 +120,7 @@ public void Initialize(int bytesPerRecord, OpenStreamCallback openStream, ReadMi ProcessVisibility(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string RecordName { get => this.recordName; @@ -138,6 +140,7 @@ private void ProcessSupportsAppending() this.truncateButton.Enabled = supportsAppending && DeviceRecordCount > 1; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ShowReadOnly { get => this.showReadOnly; @@ -477,10 +480,10 @@ private void ReadOnlyCheckBox_CheckedChanged(object sender, EventArgs e) ProcessReadOnlySettings(); } - private void FirstRecordTextBox_ValueChanged(LongValueTextBox source, LongValueTextBox.ValueChangedEventArgs args) + private void FirstRecordTextBox_ValueChanged(LongValueTextBox source, LongValueTextBox.ValueChangedEventArgs args) => this.mixByteCollectionEditorList.FirstVisibleIndex = (int)args.NewValue; - private void MixByteCollectionEditorList_FirstVisibleIndexChanged(EditorList sender, MixByteCollectionEditorList.FirstVisibleIndexChangedEventArgs args) + private void MixByteCollectionEditorList_FirstVisibleIndexChanged(EditorList sender, MixByteCollectionEditorList.FirstVisibleIndexChangedEventArgs args) => this.firstRecordTextBox.LongValue = this.mixByteCollectionEditorList.FirstVisibleIndex; private void Revert() @@ -497,12 +500,14 @@ private void Revert() Update(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ReadOnly { get => this.readOnlyCheckBox.Checked; set => this.readOnlyCheckBox.Checked = value; } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ResizeInProgress { get => this.mixByteCollectionEditorList.ResizeInProgress; diff --git a/src/MixEmul/Components/ToolStripCycleButton.cs b/src/MixEmul/Components/ToolStripCycleButton.cs index 344daf0..eed117f 100644 --- a/src/MixEmul/Components/ToolStripCycleButton.cs +++ b/src/MixEmul/Components/ToolStripCycleButton.cs @@ -18,7 +18,7 @@ public partial class ToolStripCycleButton : ToolStripControlHost public event EventHandler ValueChanged; - public ToolStripCycleButton(IContainer container) : this() + public ToolStripCycleButton(IContainer container) : this() => container.Add(this); public ToolStripCycleButton() @@ -34,7 +34,7 @@ public ToolStripCycleButton() Text = string.Empty; } - protected void OnValueChanged(EventArgs e) + protected void OnValueChanged(EventArgs e) => ValueChanged?.Invoke(this, e); private void ToolStripCycleButton_Paint(object sender, PaintEventArgs e) @@ -51,7 +51,7 @@ private void Control_Click(object sender, EventArgs e) SetCurrentStep(this.currentStep.NextStep); } - private void Control_SizeChanged(object sender, EventArgs e) + private void Control_SizeChanged(object sender, EventArgs e) => Size = Control.Size; public Step AddStep(object value) @@ -104,6 +104,7 @@ public void AddStep(Step step) SetCurrentStep(step); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public object Value { get => this.currentStep?.Value; @@ -112,9 +113,9 @@ public object Value if (value == null) return; - var step = this.steps.FirstOrDefault(s => s.Value is IComparable comparable ? comparable.CompareTo(value) == 0 : s.Value.Equals(value)) + var step = this.steps.FirstOrDefault(s => s.Value is IComparable comparable ? comparable.CompareTo(value) == 0 : s.Value.Equals(value)) ?? throw new ArgumentException($"no step with Value {value} exists"); - + SetCurrentStep(step); } } @@ -130,16 +131,16 @@ private void SetCurrentStep(Step step) } public class Step(object value, string text, Step nextStep) - { - public object Value { get; private set; } = value; - public string Text { get; private set; } = text; - public Step NextStep { get; set; } = nextStep; + { + public object Value { get; private set; } = value; + public string Text { get; private set; } = text; + public Step NextStep { get; set; } = nextStep; - public Step(object value, string text) : this(value, text, null) { } + public Step(object value, string text) : this(value, text, null) { } - public Step(object value) : this(value, value.ToString(), null) { } + public Step(object value) : this(value, value.ToString(), null) { } - public Step(object value, Step nextStep) : this(value, value.ToString(), nextStep) { } + public Step(object value, Step nextStep) : this(value, value.ToString(), nextStep) { } + } } - } } diff --git a/src/MixEmul/Components/WordEditor.cs b/src/MixEmul/Components/WordEditor.cs index 1c446de..17fa429 100644 --- a/src/MixEmul/Components/WordEditor.cs +++ b/src/MixEmul/Components/WordEditor.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using MixGui.Events; @@ -30,7 +31,7 @@ public WordEditor(IWord word) : this(word, true) { } public WordEditor(int byteCount) : this(byteCount, true) { } - public WordEditor(IWord word, bool includeSign) : this(word.ByteCount, includeSign) + public WordEditor(IWord word, bool includeSign) : this(word.ByteCount, includeSign) => WordValue = word; public WordEditor(int byteCount, bool includeSign) @@ -60,18 +61,18 @@ public WordEditor(int byteCount, bool includeSign) InitializeComponent(); } - public int? CaretIndex + public int? CaretIndex => null; - public Control EditorControl + public Control EditorControl => this; - public bool Focus(FieldTypes? field, int? index) - => ByteCount > 0 - ? this.byteTextBoxes[field == FieldTypes.LastByte ? ByteCount - 1 : 0].Focus() + public bool Focus(FieldTypes? field, int? index) + => ByteCount > 0 + ? this.byteTextBoxes[field == FieldTypes.LastByte ? ByteCount - 1 : 0].Focus() : this.signButton.Focus(); - protected virtual void OnValueChanged(WordEditorValueChangedEventArgs args) + protected virtual void OnValueChanged(WordEditorValueChangedEventArgs args) => ValueChanged?.Invoke(this, args); private void ByteValueChanged(MixByteTextBox textBox, MixByteTextBox.ValueChangedEventArgs args) @@ -276,6 +277,7 @@ public void UpdateLayout() box.UpdateLayout(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int ByteCount { get => this.word.ByteCount; @@ -289,6 +291,7 @@ public int ByteCount } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool IncludeSign { get => this.includeSign; @@ -302,6 +305,7 @@ public bool IncludeSign } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ReadOnly { get => this.readOnly; @@ -318,6 +322,7 @@ public bool ReadOnly } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IWord WordValue { get => this.word; diff --git a/src/MixEmul/Components/WordEditorList.cs b/src/MixEmul/Components/WordEditorList.cs index 9b12c0f..b6709fb 100644 --- a/src/MixEmul/Components/WordEditorList.cs +++ b/src/MixEmul/Components/WordEditorList.cs @@ -1,8 +1,8 @@ namespace MixGui.Components { - public class WordEditorList(int minIndex, int maxIndex, EditorList.CreateEditorCallback createEditor, EditorList.LoadEditorCallback loadEditor) + public class WordEditorList(int minIndex, int maxIndex, EditorList.CreateEditorCallback createEditor, EditorList.LoadEditorCallback loadEditor) : EditorList(minIndex, maxIndex, createEditor, loadEditor) { public WordEditorList() : this(0, -1, null, null) { } - } + } } diff --git a/src/MixEmul/Components/WordValueEditor.cs b/src/MixEmul/Components/WordValueEditor.cs index 23e78e4..4417588 100644 --- a/src/MixEmul/Components/WordValueEditor.cs +++ b/src/MixEmul/Components/WordValueEditor.cs @@ -1,4 +1,5 @@ -using System.Drawing; +using System.ComponentModel; +using System.Drawing; using System.Windows.Forms; using MixGui.Events; using MixGui.Utils; @@ -21,7 +22,7 @@ public class WordValueEditor : UserControl, IWordEditor, INavigableControl, IEsc public WordValueEditor() : this(FullWord.ByteCount, true) { } - public WordValueEditor(IWord word) : this(word, true) { } + public WordValueEditor(IWord word) : this(word, true) { } public WordValueEditor(int byteCount) : this(byteCount, true) { } @@ -72,26 +73,26 @@ public WordValueEditor(int byteCount, bool includeSign) SizeComponent(); } - public Control EditorControl + public Control EditorControl => this; - public FieldTypes? FocusedField + public FieldTypes? FocusedField => this.longValueTextBox.Focused ? FieldTypes.Value : this.wordEditor.FocusedField; - public int? CaretIndex - => FocusedField == FieldTypes.Value - ? this.longValueTextBox.SelectionStart + this.longValueTextBox.SelectionLength + public int? CaretIndex + => FocusedField == FieldTypes.Value + ? this.longValueTextBox.SelectionStart + this.longValueTextBox.SelectionLength : this.wordEditor.CaretIndex; - public bool Focus(FieldTypes? field, int? index) - => field == FieldTypes.Value - ? this.longValueTextBox.FocusWithIndex(index) + public bool Focus(FieldTypes? field, int? index) + => field == FieldTypes.Value + ? this.longValueTextBox.FocusWithIndex(index) : this.wordEditor.Focus(field, index); - protected virtual void OnValueChanged(WordEditorValueChangedEventArgs args) + protected virtual void OnValueChanged(WordEditorValueChangedEventArgs args) => ValueChanged?.Invoke(this, args); - protected override void Dispose(bool disposing) + protected override void Dispose(bool disposing) { } @@ -187,6 +188,7 @@ private void WordEditor_ValueChanged(IWordEditor sender, WordEditorValueChangedE OnValueChanged(args); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int ByteCount { get => this.wordEditor.ByteCount; @@ -200,6 +202,7 @@ public int ByteCount } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool IncludeSign { get => this.includeSign; @@ -214,6 +217,7 @@ public bool IncludeSign } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ReadOnly { get => this.readOnly; @@ -228,6 +232,7 @@ public bool ReadOnly } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int TextBoxWidth { get => this.textBoxWidth; @@ -241,6 +246,7 @@ public int TextBoxWidth } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IWord WordValue { get => this.wordEditor.WordValue; diff --git a/src/MixEmul/Events/AddressSelectedEventArgs.cs b/src/MixEmul/Events/AddressSelectedEventArgs.cs index da71d53..ad56853 100644 --- a/src/MixEmul/Events/AddressSelectedEventArgs.cs +++ b/src/MixEmul/Events/AddressSelectedEventArgs.cs @@ -5,5 +5,5 @@ public class AddressSelectedEventArgs(int selectedAddress) : EventArgs { public int SelectedAddress => selectedAddress; - } + } } diff --git a/src/MixEmul/Events/DeviceEventArgs.cs b/src/MixEmul/Events/DeviceEventArgs.cs index dc28842..1619d31 100644 --- a/src/MixEmul/Events/DeviceEventArgs.cs +++ b/src/MixEmul/Events/DeviceEventArgs.cs @@ -6,5 +6,5 @@ namespace MixGui.Events public class DeviceEventArgs(MixDevice device) : EventArgs { public MixDevice Device => device; - } + } } diff --git a/src/MixEmul/Events/MixByteCollectionValueEditorChangedEventArgs.cs b/src/MixEmul/Events/MixByteCollectionValueEditorChangedEventArgs.cs index 0948f67..79c360a 100644 --- a/src/MixEmul/Events/MixByteCollectionValueEditorChangedEventArgs.cs +++ b/src/MixEmul/Events/MixByteCollectionValueEditorChangedEventArgs.cs @@ -7,5 +7,5 @@ public class MixByteCollectionEditorValueChangedEventArgs(IMixByteCollection old { public IMixByteCollection NewValue => newValue; public IMixByteCollection OldValue => oldValue; - } + } } diff --git a/src/MixEmul/Events/WordValueEditorChangedEventArgs.cs b/src/MixEmul/Events/WordValueEditorChangedEventArgs.cs index f6b1535..4c5e495 100644 --- a/src/MixEmul/Events/WordValueEditorChangedEventArgs.cs +++ b/src/MixEmul/Events/WordValueEditorChangedEventArgs.cs @@ -7,5 +7,5 @@ public class WordEditorValueChangedEventArgs(IWord oldValue, IWord newValue) : E { public IWord NewValue => newValue; public IWord OldValue => oldValue; - } + } } diff --git a/src/MixEmul/MixEmul.csproj b/src/MixEmul/MixEmul.csproj index 415a65d..e455f9d 100644 --- a/src/MixEmul/MixEmul.csproj +++ b/src/MixEmul/MixEmul.csproj @@ -1,6 +1,6 @@  - net8.0-windows7.0 + net10.0-windows WinExe MixGui.MixForm ..\..\img\MixFormIcon.ico diff --git a/src/MixEmul/MixForm.cs b/src/MixEmul/MixForm.cs index 30059af..4e5ffc8 100644 --- a/src/MixEmul/MixForm.cs +++ b/src/MixEmul/MixForm.cs @@ -1395,7 +1395,7 @@ private void UpdateStatusIndicator() UpdateStatusIndicator(); if (this.mix.Status == ModuleBase.RunStatus.InvalidInstruction || this.mix.Status == ModuleBase.RunStatus.RuntimeError) - pcVisible = true; + pcVisible = true; if (pcVisible) this.mainMemoryEditor.MakeAddressVisible(this.mix.ProgramCounter, this.mix.Status == ModuleBase.RunStatus.Idle); @@ -1441,6 +1441,7 @@ public void UpdateLayout() this.deviceEditor.UpdateLayout(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ReadOnly { get => this.readOnly; @@ -1484,8 +1485,7 @@ private void ImplementFloatingPointPCChange(int address) this.mix.FloatingPointModule.ProgramCounter = address; - if (this.floatingPointMemoryEditor != null) - this.floatingPointMemoryEditor.MarkedAddress = this.mix.FloatingPointModule.ProgramCounter; + this.floatingPointMemoryEditor?.MarkedAddress = this.mix.FloatingPointModule.ProgramCounter; } } diff --git a/src/MixEmul/MixForm.handlers.cs b/src/MixEmul/MixForm.handlers.cs index 85fb214..9d22719 100644 --- a/src/MixEmul/MixForm.handlers.cs +++ b/src/MixEmul/MixForm.handlers.cs @@ -15,25 +15,25 @@ namespace MixGui { public partial class MixForm { - private void ExitMenuItem_Click(object sender, EventArgs e) + private void ExitMenuItem_Click(object sender, EventArgs e) => Close(); - private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) + private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) => HandleException(e.Exception); - private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) => HandleException((Exception)e.ExceptionObject); - private void RunItem_Click(object sender, EventArgs e) + private void RunItem_Click(object sender, EventArgs e) => SwitchRunningState(); - private void ShowPCButton_Click(object sender, EventArgs e) + private void ShowPCButton_Click(object sender, EventArgs e) => this.mainMemoryEditor.MakeAddressVisible(this.mix.ProgramCounter); - private void TeletypeItem_Click(object sender, EventArgs e) + private void TeletypeItem_Click(object sender, EventArgs e) => SwitchTeletypeVisibility(); - private void FloatingPointMemoryEditor_AddressSelected(object sender, AddressSelectedEventArgs args) + private void FloatingPointMemoryEditor_AddressSelected(object sender, AddressSelectedEventArgs args) => ImplementFloatingPointPCChange(args.SelectedAddress); private void DevicesControl_DeviceDoubleClick(object sender, DeviceEventArgs e) diff --git a/src/MixEmul/Properties/PublishProfiles/Folder-x64.pubxml b/src/MixEmul/Properties/PublishProfiles/Folder-x64.pubxml index e95b867..171da1d 100644 --- a/src/MixEmul/Properties/PublishProfiles/Folder-x64.pubxml +++ b/src/MixEmul/Properties/PublishProfiles/Folder-x64.pubxml @@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. Any CPU ..\..\bin\publish\x64 FileSystem - net8.0-windows + net10.0-windows true win-x64 true diff --git a/src/MixEmul/Properties/PublishProfiles/Folder-x86.pubxml b/src/MixEmul/Properties/PublishProfiles/Folder-x86.pubxml index 1ca2e48..8f41e0d 100644 --- a/src/MixEmul/Properties/PublishProfiles/Folder-x86.pubxml +++ b/src/MixEmul/Properties/PublishProfiles/Folder-x86.pubxml @@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. Any CPU ..\..\bin\publish\x86 FileSystem - net8.0-windows + net10.0-windows win-x86 true true diff --git a/src/MixEmul/Settings/Configuration.cs b/src/MixEmul/Settings/Configuration.cs index 6cb2900..44f1041 100644 --- a/src/MixEmul/Settings/Configuration.cs +++ b/src/MixEmul/Settings/Configuration.cs @@ -88,8 +88,8 @@ public static Configuration Load(string directory) if (File.Exists(path)) configuration = JsonSerializer.Deserialize(File.ReadAllBytes(path), JsonSerializerOptions); } - catch (Exception) - {} + catch (Exception) + { } configuration ??= new(); diff --git a/src/MixEmul/Utils/CardDeckExporter.cs b/src/MixEmul/Utils/CardDeckExporter.cs index 6cab1b8..beeea8e 100644 --- a/src/MixEmul/Utils/CardDeckExporter.cs +++ b/src/MixEmul/Utils/CardDeckExporter.cs @@ -20,10 +20,10 @@ public static class CardDeckExporter private const int MaxWordsPerCard = 7; - private static string GetTransLine(int programCounter) + private static string GetTransLine(int programCounter) => "TRANS0" + GetAddressText(programCounter); - private static char GetNegativeDigit(char digit) + private static char GetNegativeDigit(char digit) => MixByte.MixChars[MixByte.MixChars.IndexOf(digit) - 30]; public static string[] LoaderCards diff --git a/src/MixLib/Device/CardReaderDevice.cs b/src/MixLib/Device/CardReaderDevice.cs index 20ac104..22ba76c 100644 --- a/src/MixLib/Device/CardReaderDevice.cs +++ b/src/MixLib/Device/CardReaderDevice.cs @@ -17,19 +17,19 @@ public class CardReaderDevice : FileBasedDevice private const int MyRecordWordCount = 16; public const int BytesPerRecord = MyRecordWordCount * FullWord.ByteCount; - public CardReaderDevice(int id) : base(id, FileNamePrefix) + public CardReaderDevice(int id) : base(id, FileNamePrefix) => UpdateSettings(); - public override int RecordWordCount + public override int RecordWordCount => MyRecordWordCount; - public override string ShortName + public override string ShortName => MyShortName; - public override bool SupportsInput + public override bool SupportsInput => true; - public override bool SupportsOutput + public override bool SupportsOutput => false; public sealed override void UpdateSettings() @@ -53,10 +53,10 @@ public sealed override void UpdateSettings() private class OpenStreamStep : StreamStep { - public override string StatusDescription + public override string StatusDescription => OpeningDescription; - public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) + public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) => new Instance(streamStatus); private new class Instance(StreamStatus streamStatus) : StreamStep.Instance(streamStatus) diff --git a/src/MixLib/Device/CardWriterDevice.cs b/src/MixLib/Device/CardWriterDevice.cs index c017ec7..6fb497e 100644 --- a/src/MixLib/Device/CardWriterDevice.cs +++ b/src/MixLib/Device/CardWriterDevice.cs @@ -17,19 +17,19 @@ public class CardWriterDevice : FileBasedDevice private const int MyRecordWordCount = 16; public const int BytesPerRecord = MyRecordWordCount * FullWord.ByteCount; - public CardWriterDevice(int id) : base(id, FileNamePrefix) + public CardWriterDevice(int id) : base(id, FileNamePrefix) => UpdateSettings(); - public override int RecordWordCount + public override int RecordWordCount => MyRecordWordCount; - public override string ShortName + public override string ShortName => MyShortName; - public override bool SupportsInput + public override bool SupportsInput => false; - public override bool SupportsOutput + public override bool SupportsOutput => true; public sealed override void UpdateSettings() @@ -54,10 +54,10 @@ public sealed override void UpdateSettings() private class OpenStreamStep : StreamStep { - public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) + public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) => new Instance(streamStatus); - public override string StatusDescription + public override string StatusDescription => OpeningDescription; private new class Instance(StreamStatus streamStatus) : StreamStep.Instance(streamStatus) diff --git a/src/MixLib/Device/DiskDevice.cs b/src/MixLib/Device/DiskDevice.cs index ba9e973..4d22294 100644 --- a/src/MixLib/Device/DiskDevice.cs +++ b/src/MixLib/Device/DiskDevice.cs @@ -19,22 +19,22 @@ public class DiskDevice : FileBasedDevice public const long SectorCount = 4096; public const int WordsPerSector = 100; - public DiskDevice(int id) : base(id, FileNamePrefix) + public DiskDevice(int id) : base(id, FileNamePrefix) => UpdateSettings(); - public override int RecordWordCount + public override int RecordWordCount => WordsPerSector; - public override string ShortName + public override string ShortName => MyShortName; - public override bool SupportsInput + public override bool SupportsInput => true; - public override bool SupportsOutput + public override bool SupportsOutput => true; - public static long CalculateBytePosition(long sector) + public static long CalculateBytePosition(long sector) => sector * WordsPerSector * (FullWord.ByteCount + 1); public sealed override void UpdateSettings() @@ -97,10 +97,10 @@ public sealed override void UpdateSettings() private class OpenStreamStep : StreamStep { - public override string StatusDescription + public override string StatusDescription => OpeningDescription; - public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) + public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) => new Instance(streamStatus); private new class Instance(StreamStatus streamStatus) : StreamStep.Instance(streamStatus) diff --git a/src/MixLib/Device/InOutputOperands.cs b/src/MixLib/Device/InOutputOperands.cs index 863946f..83362d9 100644 --- a/src/MixLib/Device/InOutputOperands.cs +++ b/src/MixLib/Device/InOutputOperands.cs @@ -4,12 +4,12 @@ namespace MixLib.Device { public class InOutputOperands(IMemory memory, int memAddress, int sector, InterruptQueueCallback callback) - { + { public IMemory Memory => memory; public int MValue => memAddress; public int Sector => sector; public InterruptQueueCallback InterruptQueueCallback => callback; public InOutputOperands(IMemory memory, int memAddress, int sector) : this(memory, memAddress, sector, null) { } - } + } } diff --git a/src/MixLib/Device/MixDevice.cs b/src/MixLib/Device/MixDevice.cs index 5f599fa..f64f46d 100644 --- a/src/MixLib/Device/MixDevice.cs +++ b/src/MixLib/Device/MixDevice.cs @@ -7,7 +7,7 @@ namespace MixLib.Device { public abstract class MixDevice(int id) - { + { private const string IdleDescription = "Idle"; private const string ReadingDescription = "Reading from memory"; private const string WritingDescription = "Writing to memory"; @@ -15,8 +15,8 @@ public abstract class MixDevice(int id) private InOutputOperands currentOperands; private DeviceStep.Instance currentStepInstance; - protected DeviceStep CurrentStep { get; private set; } = null; - protected DeviceStep FirstInputDeviceStep { get; set; } + protected DeviceStep CurrentStep { get; private set; } = null; + protected DeviceStep FirstInputDeviceStep { get; set; } protected DeviceStep FirstIocDeviceStep { get; set; } protected DeviceStep FirstOutputDeviceStep { get; set; } public int Id => id; @@ -28,22 +28,22 @@ public abstract class MixDevice(int id) public event ReportingEventHandler ReportingEvent; - public bool Busy + public bool Busy => CurrentStep != null; - public string StatusDescription + public string StatusDescription => Busy ? CurrentStep.StatusDescription : IdleDescription; - protected virtual DeviceStep.Instance GetCurrentStepInstance() + protected virtual DeviceStep.Instance GetCurrentStepInstance() => CurrentStep.CreateInstance(); - public override string ToString() + public override string ToString() => ShortName + ": " + Id; - protected virtual void OnReportingEvent(ReportingEventArgs args) + protected virtual void OnReportingEvent(ReportingEventArgs args) => ReportingEvent?.Invoke(this, args); - private void StepInstance_Reporting(object sender, ReportingEventArgs args) + private void StepInstance_Reporting(object sender, ReportingEventArgs args) => OnReportingEvent(args); public virtual void Reset() @@ -120,10 +120,10 @@ public void Tick() protected class ReadFromMemoryStep(bool includeSign, int recordWordCount) : TickingStep(recordWordCount) { - public override string StatusDescription + public override string StatusDescription => ReadingDescription; - protected override TickingStep.Instance CreateTickingInstance() + protected override TickingStep.Instance CreateTickingInstance() => new Instance(TickCount, includeSign); private new class Instance : TickingStep.Instance @@ -168,7 +168,7 @@ protected override void ProcessTick() protected class WriteToMemoryStep(bool includeSign, int recordWordCount) : TickingStep(recordWordCount) { - public override string StatusDescription => WritingDescription; + public override string StatusDescription => WritingDescription; protected override TickingStep.Instance CreateTickingInstance() => new Instance(TickCount, includeSign); diff --git a/src/MixLib/Device/PaperTapeDevice.cs b/src/MixLib/Device/PaperTapeDevice.cs index d9a501e..d3296b6 100644 --- a/src/MixLib/Device/PaperTapeDevice.cs +++ b/src/MixLib/Device/PaperTapeDevice.cs @@ -18,19 +18,19 @@ public class PaperTapeDevice : FileBasedDevice private const int MyRecordWordCount = 14; public const int BytesPerRecord = MyRecordWordCount * FullWord.ByteCount; - public PaperTapeDevice(int id) : base(id, FileNamePrefix) + public PaperTapeDevice(int id) : base(id, FileNamePrefix) => UpdateSettings(); - public override int RecordWordCount + public override int RecordWordCount => MyRecordWordCount; - public override string ShortName + public override string ShortName => MyShortName; - public override bool SupportsInput + public override bool SupportsInput => true; - public override bool SupportsOutput + public override bool SupportsOutput => false; public sealed override void UpdateSettings() @@ -86,10 +86,10 @@ public override bool Tick() private class RewindStep : StreamStep { - public override string StatusDescription + public override string StatusDescription => RewindingDescription; - public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) + public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) => new Instance(streamStatus); private new class Instance(StreamStatus streamStatus) : StreamStep.Instance(streamStatus) diff --git a/src/MixLib/Device/PrinterDevice.cs b/src/MixLib/Device/PrinterDevice.cs index 58cacdc..d1a76d2 100644 --- a/src/MixLib/Device/PrinterDevice.cs +++ b/src/MixLib/Device/PrinterDevice.cs @@ -21,19 +21,19 @@ public class PrinterDevice : FileBasedDevice public PrinterDevice(int id) - : base(id, FileNamePrefix) + : base(id, FileNamePrefix) => UpdateSettings(); - public override int RecordWordCount + public override int RecordWordCount => MyRecordWordCount; - public override string ShortName + public override string ShortName => MyShortName; - public override bool SupportsInput + public override bool SupportsInput => false; - public override bool SupportsOutput + public override bool SupportsOutput => true; public sealed override void UpdateSettings() @@ -69,10 +69,10 @@ public sealed override void UpdateSettings() private class OpenStreamStep : StreamStep { - public override string StatusDescription + public override string StatusDescription => OpeningDescription; - public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) + public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) => new Instance(streamStatus); private new class Instance(StreamStatus streamStatus) : StreamStep.Instance(streamStatus) diff --git a/src/MixLib/Device/Settings/DeviceSettings.cs b/src/MixLib/Device/Settings/DeviceSettings.cs index 258d263..85ca8bf 100644 --- a/src/MixLib/Device/Settings/DeviceSettings.cs +++ b/src/MixLib/Device/Settings/DeviceSettings.cs @@ -46,13 +46,13 @@ static DeviceSettings() deviceReloadInterval = UnsetDeviceReloadInterval; } - public static bool IsKnownTickCount(string name) + public static bool IsKnownTickCount(string name) => defaultTickCounts.ContainsKey(name); - public static int GetDefaultTickCount(string name) + public static int GetDefaultTickCount(string name) => IsKnownTickCount(name) ? defaultTickCounts[name] : DefaultDefaultTickCount; - public static int GetTickCount(string name) + public static int GetTickCount(string name) => TickCounts.TryGetValue(name, out var value) ? value : GetDefaultTickCount(name); public static string DefaultDeviceFilesDirectory diff --git a/src/MixLib/Device/Step/BinaryReadStep.cs b/src/MixLib/Device/Step/BinaryReadStep.cs index 27df067..cc0338f 100644 --- a/src/MixLib/Device/Step/BinaryReadStep.cs +++ b/src/MixLib/Device/Step/BinaryReadStep.cs @@ -12,7 +12,7 @@ public class BinaryReadStep(int recordWordCount) : StreamStep private const string MyStatusDescription = "Reading binary data"; - public override string StatusDescription + public override string StatusDescription => MyStatusDescription; public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) => new Instance(streamStatus, this.recordWordCount); @@ -61,7 +61,7 @@ public static IFullWord[] ReadWords(Stream stream, int wordCount) { private MixByte[] mReadBytes; - public override object OutputForNextStep + public override object OutputForNextStep => mReadBytes; public override bool Tick() diff --git a/src/MixLib/Device/Step/BinaryWriteStep.cs b/src/MixLib/Device/Step/BinaryWriteStep.cs index 56f0ba8..e661a05 100644 --- a/src/MixLib/Device/Step/BinaryWriteStep.cs +++ b/src/MixLib/Device/Step/BinaryWriteStep.cs @@ -10,10 +10,10 @@ public class BinaryWriteStep(int recordWordCount) : StreamStep { private const string MyStatusDescription = "Writing binary data"; - public override string StatusDescription + public override string StatusDescription => MyStatusDescription; - public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) + public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) => new Instance(streamStatus, recordWordCount); public static void WriteWords(Stream stream, int wordCount, IFullWord[] writeWords) diff --git a/src/MixLib/Device/Step/CloseStreamStep.cs b/src/MixLib/Device/Step/CloseStreamStep.cs index 95fe585..5d55238 100644 --- a/src/MixLib/Device/Step/CloseStreamStep.cs +++ b/src/MixLib/Device/Step/CloseStreamStep.cs @@ -4,10 +4,10 @@ public class CloseStreamStep : StreamStep { private const string MyStatusDescription = "Ending data transfer"; - public override string StatusDescription + public override string StatusDescription => MyStatusDescription; - public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) + public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) => new Instance(streamStatus); private new class Instance(StreamStatus streamStatus) : StreamStep.Instance(streamStatus) diff --git a/src/MixLib/Device/Step/DeviceStep.cs b/src/MixLib/Device/Step/DeviceStep.cs index 58f01aa..22f774b 100644 --- a/src/MixLib/Device/Step/DeviceStep.cs +++ b/src/MixLib/Device/Step/DeviceStep.cs @@ -20,10 +20,10 @@ public abstract class Instance public event ReportingEventHandler ReportingEvent; - public virtual object OutputForNextStep + public virtual object OutputForNextStep => this.passthrough; - protected virtual void OnReportingEvent(ReportingEventArgs args) + protected virtual void OnReportingEvent(ReportingEventArgs args) => ReportingEvent?.Invoke(this, args); protected Instance() { } diff --git a/src/MixLib/Device/Step/NoOpStep.cs b/src/MixLib/Device/Step/NoOpStep.cs index ada3fe8..7da130e 100644 --- a/src/MixLib/Device/Step/NoOpStep.cs +++ b/src/MixLib/Device/Step/NoOpStep.cs @@ -2,10 +2,10 @@ { public class NoOpStep(int tickCount, string statusDescription) : TickingStep(tickCount) { - public override string StatusDescription + public override string StatusDescription => statusDescription; - protected override TickingStep.Instance CreateTickingInstance() + protected override TickingStep.Instance CreateTickingInstance() => new Instance(TickCount); protected new class Instance(int tickCount) : TickingStep.Instance(tickCount) diff --git a/src/MixLib/Device/Step/StreamStep.cs b/src/MixLib/Device/Step/StreamStep.cs index 6fc8897..836ca31 100644 --- a/src/MixLib/Device/Step/StreamStep.cs +++ b/src/MixLib/Device/Step/StreamStep.cs @@ -4,7 +4,7 @@ public abstract class StreamStep : DeviceStep { protected StreamStep() { } - public override DeviceStep.Instance CreateInstance() + public override DeviceStep.Instance CreateInstance() => CreateStreamInstance(null); public abstract Instance CreateStreamInstance(StreamStatus streamStatus); diff --git a/src/MixLib/Device/Step/TickingStep.cs b/src/MixLib/Device/Step/TickingStep.cs index 3e2f6f6..2ecd838 100644 --- a/src/MixLib/Device/Step/TickingStep.cs +++ b/src/MixLib/Device/Step/TickingStep.cs @@ -2,16 +2,16 @@ { public abstract class TickingStep(int tickCount) : DeviceStep { - protected int TickCount => tickCount; + protected int TickCount => tickCount; - public sealed override DeviceStep.Instance CreateInstance() => CreateTickingInstance(); + public sealed override DeviceStep.Instance CreateInstance() => CreateTickingInstance(); protected abstract Instance CreateTickingInstance(); protected new abstract class Instance(int tickCount) : DeviceStep.Instance { - protected int CurrentTick { get; private set; } = 0; - protected int TickCount => tickCount; + protected int CurrentTick { get; private set; } = 0; + protected int TickCount => tickCount; protected abstract void ProcessTick(); diff --git a/src/MixLib/Device/StreamStatus.cs b/src/MixLib/Device/StreamStatus.cs index 8605997..2e01890 100644 --- a/src/MixLib/Device/StreamStatus.cs +++ b/src/MixLib/Device/StreamStatus.cs @@ -14,10 +14,10 @@ public class StreamStatus public event ReportingEventHandler ReportingEvent; - public StreamStatus() + public StreamStatus() => Reset(); - private void OnReportingEvent(ReportingEventArgs args) + private void OnReportingEvent(ReportingEventArgs args) => ReportingEvent?.Invoke(this, args); public void CloseStream() diff --git a/src/MixLib/Device/TapeDevice.cs b/src/MixLib/Device/TapeDevice.cs index 75bda09..24b186c 100644 --- a/src/MixLib/Device/TapeDevice.cs +++ b/src/MixLib/Device/TapeDevice.cs @@ -19,25 +19,25 @@ public class TapeDevice : FileBasedDevice public const int WordsPerRecord = 100; private const int BytesPerRecord = WordsPerRecord * (FullWord.ByteCount + 1); - public TapeDevice(int id) : base(id, FileNamePrefix) + public TapeDevice(int id) : base(id, FileNamePrefix) => UpdateSettings(); - public override int RecordWordCount + public override int RecordWordCount => WordsPerRecord; - public override string ShortName + public override string ShortName => MyShortName; - public override bool SupportsInput + public override bool SupportsInput => true; - public override bool SupportsOutput + public override bool SupportsOutput => true; - public static long CalculateBytePosition(long record) + public static long CalculateBytePosition(long record) => record * BytesPerRecord; - public static long CalculateRecordCount(FileStream stream) + public static long CalculateRecordCount(FileStream stream) => stream.Length / BytesPerRecord; public sealed override void UpdateSettings() @@ -80,10 +80,10 @@ public sealed override void UpdateSettings() private class OpenStreamStep : StreamStep { - public override string StatusDescription + public override string StatusDescription => OpeningDescription; - public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) + public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) => new Instance(streamStatus); private new class Instance(StreamStatus streamStatus) : StreamStep.Instance(streamStatus) @@ -110,10 +110,10 @@ public override bool Tick() private class SeekStep : StreamStep { - public override string StatusDescription + public override string StatusDescription => WindingDescription; - public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) + public override StreamStep.Instance CreateStreamInstance(StreamStatus streamStatus) => new Instance(streamStatus); private new class Instance(StreamStatus streamStatus) : StreamStep.Instance(streamStatus) @@ -128,8 +128,8 @@ public override bool Tick() if (!StreamStatus.PositionSet) InitiateStreamPosition(); - this.ticksLeft = (Operands.MValue == 0 - ? (StreamStatus.Position / ((FullWord.ByteCount + 1) * WordsPerRecord)) + this.ticksLeft = (Operands.MValue == 0 + ? (StreamStatus.Position / ((FullWord.ByteCount + 1) * WordsPerRecord)) : Math.Abs(Operands.MValue)) * DeviceSettings.GetTickCount(DeviceSettings.TapeRecordWind); } diff --git a/src/MixLib/Device/TeletypeDevice.cs b/src/MixLib/Device/TeletypeDevice.cs index c978f1f..e847d4a 100644 --- a/src/MixLib/Device/TeletypeDevice.cs +++ b/src/MixLib/Device/TeletypeDevice.cs @@ -100,10 +100,10 @@ public sealed override void UpdateSettings() private class ReadLineStep(Queue inputBuffer) : DeviceStep { - public override string StatusDescription + public override string StatusDescription => ReadingDescription; - public override DeviceStep.Instance CreateInstance() + public override DeviceStep.Instance CreateInstance() => new Instance(inputBuffer); public new class Instance(Queue inputBuffer) : DeviceStep.Instance @@ -147,10 +147,10 @@ public override bool Tick() private class WriteLineStep(Queue outputBuffer) : DeviceStep { - public override string StatusDescription + public override string StatusDescription => WritingDescription; - public override DeviceStep.Instance CreateInstance() + public override DeviceStep.Instance CreateInstance() => new Instance(outputBuffer); public new class Instance(Queue outputBuffer) : DeviceStep.Instance diff --git a/src/MixLib/Devices.cs b/src/MixLib/Devices.cs index 242d336..ca6a959 100644 --- a/src/MixLib/Devices.cs +++ b/src/MixLib/Devices.cs @@ -42,19 +42,19 @@ public Devices() device.ReportingEvent += Device_Reporting; } - public int Count + public int Count => this.devices.Length; - public MixDevice this[int index] + public MixDevice this[int index] => this.devices[index]; - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() => this.devices.GetEnumerator(); - protected virtual void OnDeviceReportingEvent(DeviceReportingEventArgs args) + protected virtual void OnDeviceReportingEvent(DeviceReportingEventArgs args) => DeviceReportingEvent?.Invoke(this, args); - private void Device_Reporting(object sender, ReportingEventArgs args) + private void Device_Reporting(object sender, ReportingEventArgs args) => OnDeviceReportingEvent(new DeviceReportingEventArgs((MixDevice)sender, args.Severity, args.Message)); public void Reset() diff --git a/src/MixLib/Events/DeviceReportingEventArgs.cs b/src/MixLib/Events/DeviceReportingEventArgs.cs index 4fe2990..f2cbaff 100644 --- a/src/MixLib/Events/DeviceReportingEventArgs.cs +++ b/src/MixLib/Events/DeviceReportingEventArgs.cs @@ -6,5 +6,5 @@ namespace MixLib.Events public class DeviceReportingEventArgs(MixDevice reportingDevice, Severity severity, string message) : ReportingEventArgs(severity, message) { public MixDevice ReportingDevice => reportingDevice; - } + } } diff --git a/src/MixLib/Events/ReportingEventArgs.cs b/src/MixLib/Events/ReportingEventArgs.cs index 6b0eb5f..f038205 100644 --- a/src/MixLib/Events/ReportingEventArgs.cs +++ b/src/MixLib/Events/ReportingEventArgs.cs @@ -6,5 +6,5 @@ public class ReportingEventArgs(Misc.Severity severity, string message) : EventA { public string Message => message; public Misc.Severity Severity => severity; - } + } } diff --git a/src/MixLib/Instruction/AddressTransferInstructions.cs b/src/MixLib/Instruction/AddressTransferInstructions.cs index 0c50328..406c724 100644 --- a/src/MixLib/Instruction/AddressTransferInstructions.cs +++ b/src/MixLib/Instruction/AddressTransferInstructions.cs @@ -13,7 +13,7 @@ public static class AddressTransferInstructions /// /// Method for performing DECx instructions /// - public static bool Decrease(ModuleBase module, MixInstruction.Instance instance) + public static bool Decrease(ModuleBase module, MixInstruction.Instance instance) => DoIncrease(module, instance, instance.MixInstruction.Opcode - OpcodeBase, true); private static bool DoEnter(ModuleBase module, MixInstruction.Instance instance, int registerIndex, bool negateSign) @@ -69,19 +69,19 @@ private static bool DoIncrease(ModuleBase module, MixInstruction.Instance instan /// /// Method for performing ENTx instructions /// - public static bool Enter(ModuleBase module, MixInstruction.Instance instance) + public static bool Enter(ModuleBase module, MixInstruction.Instance instance) => DoEnter(module, instance, instance.MixInstruction.Opcode - OpcodeBase, false); /// /// Method for performing ENNx instructions /// - public static bool EnterNegative(ModuleBase module, MixInstruction.Instance instance) + public static bool EnterNegative(ModuleBase module, MixInstruction.Instance instance) => DoEnter(module, instance, instance.MixInstruction.Opcode - OpcodeBase, true); /// /// Method for performing INCx instructions /// - public static bool Increase(ModuleBase module, MixInstruction.Instance instance) + public static bool Increase(ModuleBase module, MixInstruction.Instance instance) => DoIncrease(module, instance, instance.MixInstruction.Opcode - OpcodeBase, false); } } diff --git a/src/MixLib/Instruction/FloatingPointInstructions.cs b/src/MixLib/Instruction/FloatingPointInstructions.cs index 2e2c663..7741a4e 100644 --- a/src/MixLib/Instruction/FloatingPointInstructions.cs +++ b/src/MixLib/Instruction/FloatingPointInstructions.cs @@ -38,7 +38,7 @@ public static bool DoFloatingPoint(ModuleBase module, MixInstruction.Instance in executionStatus ??= new ExecutionStatus(module.Mode, module.ProgramCounter, instance.Instruction.Mnemonic); if (executionStatus.CurrentStep == ExecutionStatus.Step.Initialize && !InitializeInstruction(module, instance, floatingPointModule)) - return false; + return false; executionStatus.OverflowDetected |= floatingPointModule.Tick(); @@ -60,7 +60,7 @@ public static bool DoFloatingPoint(ModuleBase module, MixInstruction.Instance in case ModuleBase.RunStatus.InvalidInstruction: case ModuleBase.RunStatus.RuntimeError: - module.ReportRuntimeError($"Floating point module failed to execute instruction { executionStatus.Mnemonic}"); + module.ReportRuntimeError($"Floating point module failed to execute instruction {executionStatus.Mnemonic}"); module.Mode = executionStatus.Mode; executionStatus = null; diff --git a/src/MixLib/Instruction/InstanceValidationError.cs b/src/MixLib/Instruction/InstanceValidationError.cs index 3a712c3..01dfa5e 100644 --- a/src/MixLib/Instruction/InstanceValidationError.cs +++ b/src/MixLib/Instruction/InstanceValidationError.cs @@ -6,13 +6,13 @@ public class InstanceValidationError : ValidationError { public Sources Source { get; private set; } - public InstanceValidationError(Sources source, string message) : base(message) + public InstanceValidationError(Sources source, string message) : base(message) => Source = source; - public InstanceValidationError(Sources source, int validLowerBound, int validUpperBound) : base(validLowerBound, validUpperBound) + public InstanceValidationError(Sources source, int validLowerBound, int validUpperBound) : base(validLowerBound, validUpperBound) => Source = source; - public InstanceValidationError(Sources source, string message, int validLowerBound, int validUpperBound) : base(message, validLowerBound, validUpperBound) + public InstanceValidationError(Sources source, string message, int validLowerBound, int validUpperBound) : base(message, validLowerBound, validUpperBound) => Source = source; public override string CompiledMessage diff --git a/src/MixLib/Instruction/InstructionBase.cs b/src/MixLib/Instruction/InstructionBase.cs index a3e6b25..01c03d6 100644 --- a/src/MixLib/Instruction/InstructionBase.cs +++ b/src/MixLib/Instruction/InstructionBase.cs @@ -6,7 +6,7 @@ /// It defines the attributes common to all types, which basically is the fact that they all have a mnemonic. /// public abstract class InstructionBase(string mnemonic) - { + { public string Mnemonic => mnemonic; - } + } } diff --git a/src/MixLib/Instruction/InstructionHelpers.cs b/src/MixLib/Instruction/InstructionHelpers.cs index e47f1d0..65b6b39 100644 --- a/src/MixLib/Instruction/InstructionHelpers.cs +++ b/src/MixLib/Instruction/InstructionHelpers.cs @@ -6,10 +6,10 @@ public static class InstructionHelpers { public const int InvalidAddress = int.MinValue; - public static int GetValidIndexedAddress(ModuleBase module, int addressBase, int registerIndex) + public static int GetValidIndexedAddress(ModuleBase module, int addressBase, int registerIndex) => GetValidIndexedAddress(module, addressBase, registerIndex, true); - public static InstanceValidationError[] ValidateIndex(MixInstruction.Instance instance) + public static InstanceValidationError[] ValidateIndex(MixInstruction.Instance instance) => instance.Index > 6 ? [new InstanceValidationError(InstanceValidationError.Sources.Index, 0, 6)] : null; public static int GetValidIndexedAddress(ModuleBase module, int addressBase, int registerIndex, bool reportErrors) diff --git a/src/MixLib/Instruction/InstructionText.cs b/src/MixLib/Instruction/InstructionText.cs index bb1454c..f1fb48b 100644 --- a/src/MixLib/Instruction/InstructionText.cs +++ b/src/MixLib/Instruction/InstructionText.cs @@ -3,14 +3,14 @@ namespace MixLib.Instruction { public class InstructionText(MixInstruction.Instance instance) - { - public string Index + { + public string Index => instance.Index != 0 ? "," + instance.Index : string.Empty; - public string InstanceText + public string InstanceText => Mnemonic + " " + Address + Index + Field; - public string Mnemonic + public string Mnemonic => instance.MixInstruction.Mnemonic; private string FieldText @@ -31,7 +31,7 @@ public string Address get { var addressText = instance.AddressMagnitude.ToString(); - + if (instance.AddressSign.IsNegative()) addressText = '-' + addressText; diff --git a/src/MixLib/Instruction/JumpInstructions.cs b/src/MixLib/Instruction/JumpInstructions.cs index a35b893..6722cee 100644 --- a/src/MixLib/Instruction/JumpInstructions.cs +++ b/src/MixLib/Instruction/JumpInstructions.cs @@ -38,7 +38,7 @@ private static void DoJump(ModuleBase module, int indexedAddress, bool saveJ) /// /// Public jump method for use by other classes /// - public static void Jump(ModuleBase module, int indexedAddress) + public static void Jump(ModuleBase module, int indexedAddress) => DoJump(module, indexedAddress, false); /// diff --git a/src/MixLib/Instruction/MiscInstructions.cs b/src/MixLib/Instruction/MiscInstructions.cs index 28e7f0b..5a6773c 100644 --- a/src/MixLib/Instruction/MiscInstructions.cs +++ b/src/MixLib/Instruction/MiscInstructions.cs @@ -186,16 +186,16 @@ public static bool Move(ModuleBase module, MixInstruction.Instance instance) /// several ticks, the exact number of ticks depending on the number of words moved. /// private class MoveStatus(int programCounter, int fromAddress, int toAddress, byte wordCount) - { - public int CurrentWord { get; private set; } = 0; - public WordStates CurrentWordState { get; set; } = WordStates.BeforeMove; - public int FromAddress => fromAddress; - public int ProgramCounter => programCounter; - public int ToAddress => toAddress; - public byte WordCount => wordCount; - - public void NextWord() - => CurrentWord++; + { + public int CurrentWord { get; private set; } = 0; + public WordStates CurrentWordState { get; set; } = WordStates.BeforeMove; + public int FromAddress => fromAddress; + public int ProgramCounter => programCounter; + public int ToAddress => toAddress; + public byte WordCount => wordCount; + + public void NextWord() + => CurrentWord++; public enum WordStates { diff --git a/src/MixLib/Instruction/RuntimeValidationError.cs b/src/MixLib/Instruction/RuntimeValidationError.cs index d531103..bd52826 100644 --- a/src/MixLib/Instruction/RuntimeValidationError.cs +++ b/src/MixLib/Instruction/RuntimeValidationError.cs @@ -4,14 +4,14 @@ namespace MixLib.Instruction { public class RuntimeValidationError(int programCounter, MixInstruction.Instance instance, string message, int validLowerBound, int validUpperBound, int actualValue) : ValidationError(message, validLowerBound, validUpperBound) { - public int ActualValue => actualValue; - public MixInstruction.Instance Instance => instance; - public int ProgramCounter => programCounter; + public int ActualValue => actualValue; + public MixInstruction.Instance Instance => instance; + public int ProgramCounter => programCounter; - public RuntimeValidationError(int programCounter, MixInstruction.Instance instance, string message) - : this(programCounter, instance, message, int.MinValue, int.MinValue, int.MinValue) { } + public RuntimeValidationError(int programCounter, MixInstruction.Instance instance, string message) + : this(programCounter, instance, message, int.MinValue, int.MinValue, int.MinValue) { } public RuntimeValidationError(int programCounter, MixInstruction.Instance instance, int validLowerBound, int validUpperBound, int actualValue) : this(programCounter, instance, null, validLowerBound, validUpperBound, actualValue) { } - } + } } diff --git a/src/MixLib/InstructionSet.cs b/src/MixLib/InstructionSet.cs index 75d79f5..863ab40 100644 --- a/src/MixLib/InstructionSet.cs +++ b/src/MixLib/InstructionSet.cs @@ -250,10 +250,10 @@ public InstructionSet() AddInstruction("INT", 5, new FieldSpec(9), 2, executor, null); } - private void AddInstruction(string mnemonic, byte opcode, MetaFieldSpec metaFieldSpec, int tickCount, MixInstruction.Executor executor, MixInstruction.Validator validator) + private void AddInstruction(string mnemonic, byte opcode, MetaFieldSpec metaFieldSpec, int tickCount, MixInstruction.Executor executor, MixInstruction.Validator validator) => AddInstruction(mnemonic, new MixInstruction(opcode, mnemonic, metaFieldSpec, tickCount, executor, validator)); - private void AddInstruction(string mnemonic, byte opcode, FieldSpec fieldSpec, int tickCount, MixInstruction.Executor executor, MixInstruction.Validator validator) + private void AddInstruction(string mnemonic, byte opcode, FieldSpec fieldSpec, int tickCount, MixInstruction.Executor executor, MixInstruction.Validator validator) => AddInstruction(mnemonic, new MixInstruction(opcode, fieldSpec, mnemonic, tickCount, executor, validator)); private void AddInstruction(string mnemonic, MixInstruction instruction) @@ -301,7 +301,7 @@ public MixInstruction GetInstruction(byte opcode, FieldSpec fieldSpec) return defaultInstruction; } - public MixInstruction this[string mnemonic] + public MixInstruction this[string mnemonic] => this.mnemonicInstructionMap.TryGetValue(mnemonic, out var value) ? value : null; public MixInstruction[] this[byte opcode] diff --git a/src/MixLib/LoaderInstructions.cs b/src/MixLib/LoaderInstructions.cs index 9772c0b..4aa690b 100644 --- a/src/MixLib/LoaderInstructions.cs +++ b/src/MixLib/LoaderInstructions.cs @@ -15,10 +15,10 @@ public LoaderInstructions() AddInstruction("END", LoaderInstruction.Operations.SetProgramCounter, false); } - public LoaderInstruction this[string mnemonic] + public LoaderInstruction this[string mnemonic] => this.instructions.TryGetValue(mnemonic, out var value) ? value : null; - private void AddInstruction(string mnemonic, LoaderInstruction.Operations operation, bool alphanumeric) + private void AddInstruction(string mnemonic, LoaderInstruction.Operations operation, bool alphanumeric) => this.instructions.Add(mnemonic, new LoaderInstruction(mnemonic, operation, alphanumeric)); } } diff --git a/src/MixLib/Memory.cs b/src/MixLib/Memory.cs index e66d5fa..f129ebb 100644 --- a/src/MixLib/Memory.cs +++ b/src/MixLib/Memory.cs @@ -2,34 +2,27 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Threading; using MixLib.Type; namespace MixLib { - public class Memory : IMemory, IEnumerable + public class Memory(int minIndex, int maxIndex) : IMemory, IEnumerable { public static readonly FieldSpec DefaultFieldSpec = new(0, 5); - private readonly SortedDictionary words; - private readonly object syncRoot; + private readonly SortedDictionary words = []; + private readonly Lock syncLock = new(); - public int MinWordIndex { get; set; } - public int MaxWordIndex { get; set; } + public int MinWordIndex { get; set; } = minIndex; + public int MaxWordIndex { get; set; } = maxIndex; - public Memory(int minIndex, int maxIndex) - { - MinWordIndex = minIndex; - MaxWordIndex = maxIndex; - this.words = []; - this.syncRoot = ((ICollection)this.words).SyncRoot; - } - - public int WordCount + public int WordCount => MaxWordIndex - MinWordIndex + 1; public void ResetProfilingCounts() { - lock (this.syncRoot) + lock (syncLock) { foreach (MemoryFullWord word in this.words.Values) word.ResetProfilingCounts(); @@ -40,7 +33,7 @@ public long MaxProfilingTickCount { get { - lock (this.syncRoot) + lock (syncLock) { return this.words.Count == 0 ? 0 : this.words.Values.Select(w => w.ProfilingTickCount).Max(); } @@ -51,7 +44,7 @@ public long MaxProfilingExecutionCount { get { - lock (this.syncRoot) + lock (syncLock) { return this.words.Count == 0 ? 0 : this.words.Values.Select(w => w.ProfilingExecutionCount).Max(); } @@ -69,7 +62,7 @@ public SearchResult FindMatch(SearchParameters options) while (!searchWrapped || index < startIndex) { - lock (this.syncRoot) + lock (syncLock) { this.words.TryGetValue(index, out word); } @@ -105,7 +98,7 @@ public SearchResult FindMatch(SearchParameters options) KeyValuePair? pair; - lock (this.syncRoot) + lock (syncLock) { pair = this.words.Cast?>().FirstOrDefault(kvp => kvp.Value.Key > index); } @@ -131,7 +124,7 @@ public SearchResult FindMatch(SearchParameters options) public IEnumerator GetEnumerator() { - lock (this.syncRoot) + lock (syncLock) { return this.words.Values.GetEnumerator(); } @@ -139,7 +132,7 @@ public IEnumerator GetEnumerator() public void ClearSourceLines() { - lock (this.syncRoot) + lock (syncLock) { foreach (MemoryFullWord word in this.words.Values) word.SourceLine = null; @@ -148,7 +141,7 @@ public void ClearSourceLines() public void Reset() { - lock (this.syncRoot) + lock (syncLock) { this.words.Clear(); } @@ -163,7 +156,7 @@ public IMemoryFullWord this[int index] MemoryFullWord word; - lock (this.syncRoot) + lock (syncLock) { this.words.TryGetValue(index, out word); } @@ -188,7 +181,7 @@ public MemoryFullWord GetRealWord(int index) { MemoryFullWord word; - lock (this.syncRoot) + lock (syncLock) { if (!this.words.TryGetValue(index, out word)) { @@ -202,7 +195,7 @@ public MemoryFullWord GetRealWord(int index) public bool HasContents(int index) { - lock (this.syncRoot) + lock (syncLock) { return this.words.ContainsKey(index) && !this.words[index].IsEmpty; } @@ -215,7 +208,7 @@ public bool HasContents(int index) var collection = this.words.TakeWhile(kvp => kvp.Key < index).Reverse().SkipWhile(kvp => kvp.Value.IsEmpty); - lock (this.syncRoot) + lock (syncLock) { return collection.Any() ? collection.First().Key : null; } @@ -228,7 +221,7 @@ public bool HasContents(int index) var collection = this.words.SkipWhile(kvp => kvp.Key <= index || kvp.Value.IsEmpty); - lock (this.syncRoot) + lock (syncLock) { return collection.Any() ? collection.First().Key : null; } @@ -236,7 +229,7 @@ public bool HasContents(int index) public void ResetRealWord(int index) { - lock (this.syncRoot) + lock (syncLock) { this.words.Remove(index); } @@ -244,7 +237,7 @@ public void ResetRealWord(int index) public void ClearRealWordSourceLine(int index) { - lock (this.syncRoot) + lock (syncLock) { if (this.words.TryGetValue(index, out var value)) value.SourceLine = null; diff --git a/src/MixLib/Misc/LogLine.cs b/src/MixLib/Misc/LogLine.cs index b38d4d2..4a5331f 100644 --- a/src/MixLib/Misc/LogLine.cs +++ b/src/MixLib/Misc/LogLine.cs @@ -2,7 +2,7 @@ namespace MixLib.Misc { public class LogLine(string moduleName, Severity severity, int address, string title, string message) - { + { public const int NoAddress = -1; public int Address => address; public string Message => message; @@ -11,6 +11,6 @@ public class LogLine(string moduleName, Severity severity, int address, string t public string ModuleName => moduleName; public LogLine(string moduleName, Severity severity, string title, string message) - : this(moduleName, severity, NoAddress, title, message) { } - } + : this(moduleName, severity, NoAddress, title, message) { } + } } diff --git a/src/MixLib/Mix.cs b/src/MixLib/Mix.cs index 8395fa9..be06ca1 100644 --- a/src/MixLib/Mix.cs +++ b/src/MixLib/Mix.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; @@ -36,7 +36,7 @@ public class Mix : ModuleBase, IDisposable private RunStatus status; private RunMode mode; private bool interruptExecuted; - private readonly object syncRoot = new(); + private readonly Lock syncLock = new(); public FloatingPointModule FloatingPointModule { get; private set; } public long TickCounter { get; private set; } @@ -383,14 +383,14 @@ public bool RunDetached { get { - lock (this.syncRoot) + lock (syncLock) { return this.runDetached; } } set { - lock (this.syncRoot) + lock (syncLock) { this.runDetached = value; } @@ -415,7 +415,7 @@ public override RunStatus Status { get { - lock (this.syncRoot) + lock (syncLock) { return this.status; } @@ -423,7 +423,7 @@ public override RunStatus Status protected set { - lock (this.syncRoot) + lock (syncLock) { this.status = value; } diff --git a/src/MixLib/MixLib.csproj b/src/MixLib/MixLib.csproj index 4f4c0df..03f9905 100644 --- a/src/MixLib/MixLib.csproj +++ b/src/MixLib/MixLib.csproj @@ -1,6 +1,6 @@  - net8.0-windows7.0 + net10.0-windows7.0 Library diff --git a/src/MixLib/Modules/FloatingPointModule.cs b/src/MixLib/Modules/FloatingPointModule.cs index 1247375..1805b7c 100644 --- a/src/MixLib/Modules/FloatingPointModule.cs +++ b/src/MixLib/Modules/FloatingPointModule.cs @@ -44,22 +44,22 @@ public FloatingPointModule(Mix mix) this.status = RunStatus.Idle; } - public override string ModuleName + public override string ModuleName => MyModuleName; - public override IMemory Memory + public override IMemory Memory => this.memory; - public override IMemory FullMemory + public override IMemory FullMemory => this.fullMemory; - public override Registers Registers + public override Registers Registers => this.mix.Registers; - public override void AddLogLine(LogLine line) + public override void AddLogLine(LogLine line) => this.mix.AddLogLine(line); - public override void ResetProfilingCounts() + public override void ResetProfilingCounts() => this.fullMemory.ResetProfilingCounts(); public override RunStatus Status diff --git a/src/MixLib/Modules/ModuleBase.cs b/src/MixLib/Modules/ModuleBase.cs index 983c92d..f22a0a1 100644 --- a/src/MixLib/Modules/ModuleBase.cs +++ b/src/MixLib/Modules/ModuleBase.cs @@ -16,7 +16,7 @@ public abstract class ModuleBase public abstract RunStatus Status { get; protected set; } public abstract RunMode Mode { get; set; } - public virtual Devices Devices + public virtual Devices Devices => null; protected bool IsBreakpointSet(int address) diff --git a/src/MixLib/Modules/Settings/ModuleSettings.cs b/src/MixLib/Modules/Settings/ModuleSettings.cs index dab71b2..55bb189 100644 --- a/src/MixLib/Modules/Settings/ModuleSettings.cs +++ b/src/MixLib/Modules/Settings/ModuleSettings.cs @@ -27,7 +27,7 @@ public static bool FloatingPointEnabledDefined if (!value) floatingPointEnabled = null; - else + else floatingPointEnabled ??= FloatingPointEnabledDefault; } } @@ -58,7 +58,7 @@ public static bool FloatingPointMemoryWordCountDefined if (!value) floatingPointMemoryWordCount = null; - else + else floatingPointMemoryWordCount ??= FloatingPointMemoryWordCountDefault; } } diff --git a/src/MixLib/Registers.cs b/src/MixLib/Registers.cs index 4fa02e5..b7b60af 100644 --- a/src/MixLib/Registers.cs +++ b/src/MixLib/Registers.cs @@ -33,37 +33,37 @@ public Registers() } } - public Register this[int offset] + public Register this[int offset] => this.registers[offset]; - public Register this[Offset offset] + public Register this[Offset offset] => this[(int)offset]; - public Register RI1 + public Register RI1 => this.registers[(int)Offset.rI1]; - public Register RI2 + public Register RI2 => this.registers[(int)Offset.rI2]; - public Register RI3 + public Register RI3 => this.registers[(int)Offset.rI3]; - public Register RI4 + public Register RI4 => this.registers[(int)Offset.rI4]; - public Register RI5 + public Register RI5 => this.registers[(int)Offset.rI5]; - public Register RI6 + public Register RI6 => this.registers[(int)Offset.rI6]; - public Register RA => - this.registers[(int)Offset.rA]; + public Register RA + => this.registers[(int)Offset.rA]; - public Register RJ + public Register RJ => this.rJ; - public Register RX + public Register RX => this.registers[(int)Offset.rX]; public int GetIndexedAddress(int mValue, int index) diff --git a/src/MixLib/Type/FieldSpec.cs b/src/MixLib/Type/FieldSpec.cs index 1c89674..217bdbf 100644 --- a/src/MixLib/Type/FieldSpec.cs +++ b/src/MixLib/Type/FieldSpec.cs @@ -6,7 +6,7 @@ public class FieldSpec { public MixByte MixByteValue { get; private set; } - public FieldSpec(MixByte value) + public FieldSpec(MixByte value) => MixByteValue = value; public FieldSpec(int lowBound, int highBound) diff --git a/src/MixLib/Type/FullWord.cs b/src/MixLib/Type/FullWord.cs index ffbd30d..e313a68 100644 --- a/src/MixLib/Type/FullWord.cs +++ b/src/MixLib/Type/FullWord.cs @@ -6,7 +6,7 @@ public class FullWord : Word, IFullWord public FullWord() : base(ByteCount) { } - public FullWord(long value) : this() + public FullWord(long value) : this() => LongValue = value; public FullWord(Signs sign, long magnitude) : this() @@ -15,7 +15,7 @@ public FullWord(Signs sign, long magnitude) : this() Sign = sign; } - public static implicit operator FullWord(long value) + public static implicit operator FullWord(long value) => new(value); public override object Clone() diff --git a/src/MixLib/Type/MemoryFullWord.cs b/src/MixLib/Type/MemoryFullWord.cs index 7780310..7a4fa7d 100644 --- a/src/MixLib/Type/MemoryFullWord.cs +++ b/src/MixLib/Type/MemoryFullWord.cs @@ -10,19 +10,19 @@ public class MemoryFullWord : FullWord, IMemoryFullWord public long ProfilingTickCount { get; private set; } public long ProfilingExecutionCount { get; private set; } - public MemoryFullWord(int index) + public MemoryFullWord(int index) => Index = index; - public MemoryFullWord(int index, long value) : base(value) + public MemoryFullWord(int index, long value) : base(value) => Index = index; - public override bool IsEmpty + public override bool IsEmpty => base.IsEmpty && SourceLine == null; - public void IncreaseProfilingTickCount(int ticks) + public void IncreaseProfilingTickCount(int ticks) => ProfilingTickCount += ticks; - public void IncreaseProfilingExecutionCount() + public void IncreaseProfilingExecutionCount() => ProfilingExecutionCount++; public SearchResult FindMatch(SearchParameters options) diff --git a/src/MixLib/Type/MemoryView.cs b/src/MixLib/Type/MemoryView.cs index cdf6900..7a321fb 100644 --- a/src/MixLib/Type/MemoryView.cs +++ b/src/MixLib/Type/MemoryView.cs @@ -27,22 +27,22 @@ public MemoryView(IMemory sourceMemory, int minIndex, int maxIndex, int indexOff this.indexOffset = indexOffset; } - public int WordCount + public int WordCount => this.maxIndex - this.minIndex + 1; - public long MaxProfilingTickCount + public long MaxProfilingTickCount => SourceMemory.MaxProfilingTickCount; - public long MaxProfilingExecutionCount + public long MaxProfilingExecutionCount => SourceMemory.MaxProfilingExecutionCount; - public MemoryFullWord GetRealWord(int index) + public MemoryFullWord GetRealWord(int index) => SourceMemory.GetRealWord(index + this.indexOffset); - public void ResetRealWord(int index) + public void ResetRealWord(int index) => SourceMemory.ResetRealWord(index + this.indexOffset); - public void ClearRealWordSourceLine(int index) + public void ClearRealWordSourceLine(int index) => SourceMemory.ClearRealWordSourceLine(index + this.indexOffset); private void ValidateParameters(int minIndex, int maxIndex, int offset) diff --git a/src/MixLib/Type/MixByteCollection.cs b/src/MixLib/Type/MixByteCollection.cs index 6b87d99..d863f3e 100644 --- a/src/MixLib/Type/MixByteCollection.cs +++ b/src/MixLib/Type/MixByteCollection.cs @@ -24,16 +24,16 @@ public MixByteCollection(MixByte[] bytes) this.bytes = bytes; } - public int MaxByteCount + public int MaxByteCount => this.maxByteCount; - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => this.bytes.GetEnumerator(); - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() => ((IEnumerable)this.bytes).GetEnumerator(); - public MixByte[] ToArray() + public MixByte[] ToArray() => (MixByte[])this.bytes.Clone(); public void Load(string text) diff --git a/src/MixLib/Type/Register.cs b/src/MixLib/Type/Register.cs index 14051f8..c953dfb 100644 --- a/src/MixLib/Type/Register.cs +++ b/src/MixLib/Type/Register.cs @@ -3,10 +3,10 @@ public abstract class Register(int byteCount, int paddingByteCount) : Word(byteCount) { - public int ByteCountWithPadding + public int ByteCountWithPadding => paddingByteCount + ByteCount; - public MixByte GetByteWithPadding(int index) + public MixByte GetByteWithPadding(int index) => index < paddingByteCount ? 0 : base[index - paddingByteCount]; public FullWord FullWordValue diff --git a/src/MixLib/Type/SymbolBase.cs b/src/MixLib/Type/SymbolBase.cs index 9831577..0d5411b 100644 --- a/src/MixLib/Type/SymbolBase.cs +++ b/src/MixLib/Type/SymbolBase.cs @@ -5,16 +5,16 @@ public abstract class SymbolBase(string name) : IValue { public string Name => name; - public virtual long MemoryWordValue + public virtual long MemoryWordValue => 0L; - public virtual long MemoryWordMagnitude + public virtual long MemoryWordMagnitude => 0L; - public virtual Word.Signs MemoryWordSign + public virtual Word.Signs MemoryWordSign => Word.Signs.Positive; - public virtual bool IsMultiValuedSymbol + public virtual bool IsMultiValuedSymbol => false; public abstract long GetValue(int currentAddress); diff --git a/src/MixLib/Type/SymbolCollection.cs b/src/MixLib/Type/SymbolCollection.cs index cbae497..9aeb022 100644 --- a/src/MixLib/Type/SymbolCollection.cs +++ b/src/MixLib/Type/SymbolCollection.cs @@ -8,31 +8,31 @@ public sealed class SymbolCollection : IEnumerable, IEnumerable { private readonly SortedList list = []; - public int Count + public int Count => this.list.Count; - public SymbolBase this[string name] + public SymbolBase this[string name] => this.list.TryGetValue(name, out var value) ? value : null; - public bool Contains(SymbolBase value) + public bool Contains(SymbolBase value) => Contains(value.Name); - public bool Contains(string value) + public bool Contains(string value) => this.list.ContainsKey(value); - private IEnumerator GetEnumerator() + private IEnumerator GetEnumerator() => this.list.Values.GetEnumerator(); - public void Remove(SymbolBase value) + public void Remove(SymbolBase value) => Remove(value.Name); - public void Remove(string name) + public void Remove(string name) => this.list.Remove(name); - IEnumerator IEnumerable.GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public void Add(SymbolBase value) diff --git a/src/MixLib/Type/VirtualMemoryFullWord.cs b/src/MixLib/Type/VirtualMemoryFullWord.cs index 277e152..7dcf04c 100644 --- a/src/MixLib/Type/VirtualMemoryFullWord.cs +++ b/src/MixLib/Type/VirtualMemoryFullWord.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Threading; using MixLib.Instruction; using MixLib.Utils; @@ -16,7 +17,7 @@ public class VirtualMemoryFullWord(IMemory memory, int index) : IMemoryFullWord private MemoryFullWord realWord; private readonly IMemory memory = memory; - private readonly object syncRoot = new(); + private readonly Lock syncLock = new(); public int Index => index; @@ -70,7 +71,7 @@ public bool IsEmpty => !RealWordFetched || this.realWord.IsEmpty; public MixByte[] ToArray() - => [..ActiveWord]; + => [.. ActiveWord]; public MixByte[] Slice(int startIndex, int count) => ActiveWord.Slice(startIndex, count); @@ -83,7 +84,7 @@ public string ToString(bool asChars) private void FetchRealWordIfNotFetched() { - lock (this.syncRoot) + lock (syncLock) { if (this.realWord == null) FetchRealWord(); @@ -94,7 +95,7 @@ private bool RealWordFetched { get { - lock (this.syncRoot) + lock (syncLock) { return this.realWord != null; } @@ -106,7 +107,7 @@ private void FetchRealWord() private bool FetchRealWordConditionally(Func condition) { - lock (this.syncRoot) + lock (syncLock) { if (this.realWord == null) { diff --git a/src/MixLib/Type/Word.cs b/src/MixLib/Type/Word.cs index 696bf5a..4e5358e 100644 --- a/src/MixLib/Type/Word.cs +++ b/src/MixLib/Type/Word.cs @@ -1,6 +1,8 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; +using System.Text; using System.Threading; using MixLib.Events; @@ -10,7 +12,7 @@ public class Word : ICloneable, IWord { public event WordValueChangedEventHandler WordValueChanged; - private readonly ReaderWriterLock accessLock; + private readonly Lock accessLock = new(); private readonly MixByte[] bytes; private Signs sign; @@ -33,7 +35,6 @@ private Word(int byteCount, Signs sign) ByteCount = byteCount; this.sign = sign; this.bytes = new MixByte[byteCount]; - this.accessLock = new ReaderWriterLock(); } public void Load(string text) @@ -108,62 +109,44 @@ public void InvertSign() public string ToString(bool asChars) { - string stringValue; - if (asChars) { - stringValue = string.Empty; - - foreach (MixByte mixByte in this.bytes) - stringValue += (char)mixByte; + return new string([.. this.bytes.Select(b => (char)b)]); } else { - stringValue = Sign.ToChar().ToString(); + var sb = new StringBuilder(); + sb.Append(Sign.ToChar()); foreach (MixByte mixByte in this.bytes) - stringValue += ' ' + mixByte; - } + { + sb.Append(' '); + sb.Append(mixByte.ToString()); + } - return stringValue; + return sb.ToString(); + } } public MixByte this[int index] { get { - MixByte selectedByte; - - this.accessLock.AcquireReaderLock(Timeout.Infinite); - - try + lock (accessLock) { - selectedByte = this.bytes[index]; + return this.bytes[index]; } - finally - { - this.accessLock.ReleaseReaderLock(); - } - - return selectedByte; } set { - this.accessLock.AcquireWriterLock(Timeout.Infinite); - - try + lock (accessLock) { MixByte oldValue = this.bytes[index]; - this.bytes[index] = value ?? new MixByte(); if (oldValue.ByteValue == this.bytes[index].ByteValue) return; } - finally - { - this.accessLock.ReleaseWriterLock(); - } OnWordValueChanged(); } @@ -173,25 +156,14 @@ public long LongValue { get { - long longValue; - this.accessLock.AcquireReaderLock(Timeout.Infinite); - - try + lock (accessLock) { - longValue = BytesToLong(Sign, this.bytes); + return BytesToLong(Sign, this.bytes); } - finally - { - this.accessLock.ReleaseReaderLock(); - } - - return longValue; } set { - this.accessLock.AcquireWriterLock(Timeout.Infinite); - - try + lock (accessLock) { if (value == LongValue) return; @@ -199,10 +171,6 @@ public long LongValue SetMagnitudeLongValue(value); this.sign = value.GetSign(); } - finally - { - this.accessLock.ReleaseWriterLock(); - } OnWordValueChanged(); } @@ -230,26 +198,14 @@ public MixByte[] Magnitude { get { - MixByte[] bytes; - - this.accessLock.AcquireReaderLock(Timeout.Infinite); - - try - { - bytes = (MixByte[])this.bytes.Clone(); - } - finally + lock (accessLock) { - this.accessLock.ReleaseReaderLock(); + return (MixByte[])this.bytes.Clone(); } - - return bytes; } set { - this.accessLock.AcquireWriterLock(Timeout.Infinite); - - try + lock (accessLock) { long oldValue = MagnitudeLongValue; @@ -276,10 +232,6 @@ public MixByte[] Magnitude if (oldValue == MagnitudeLongValue) return; } - finally - { - this.accessLock.ReleaseWriterLock(); - } OnWordValueChanged(); } @@ -289,34 +241,18 @@ public long MagnitudeLongValue { get { - long longValue; - - this.accessLock.AcquireReaderLock(Timeout.Infinite); - - try - { - longValue = BytesToLong(this.bytes); - } - finally + lock (accessLock) { - this.accessLock.ReleaseReaderLock(); + return BytesToLong(this.bytes); } - - return longValue; } set { - this.accessLock.AcquireWriterLock(Timeout.Infinite); - - try + lock (accessLock) { if (SetMagnitudeLongValue(value) == value.GetMagnitude()) return; } - finally - { - this.accessLock.ReleaseWriterLock(); - } OnWordValueChanged(); } @@ -326,32 +262,20 @@ public Signs Sign { get { - this.accessLock.AcquireReaderLock(Timeout.Infinite); - - try + lock (accessLock) { return this.sign; } - finally - { - this.accessLock.ReleaseReaderLock(); - } } set { - this.accessLock.AcquireWriterLock(Timeout.Infinite); - - try + lock (accessLock) { if (value == this.sign) return; this.sign = value; } - finally - { - this.accessLock.ReleaseWriterLock(); - } OnWordValueChanged(); } diff --git a/src/MixLib/Type/WordField.cs b/src/MixLib/Type/WordField.cs index e722f68..b96256b 100644 --- a/src/MixLib/Type/WordField.cs +++ b/src/MixLib/Type/WordField.cs @@ -6,7 +6,7 @@ public class WordField : Word { private readonly FieldSpec fieldSpec; - private WordField(FieldSpec fieldSpec, int byteCount) : base(byteCount) + private WordField(FieldSpec fieldSpec, int byteCount) : base(byteCount) => this.fieldSpec = fieldSpec; public void ApplyToFullWord(IFullWord word) diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 5015442..7239ea2 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -3,7 +3,7 @@ [assembly: AssemblyCompany("Rutger van Bergen")] [assembly: AssemblyProduct("MixEmul")] -[assembly: AssemblyCopyright("© Copyright 2024, Rutger van Bergen")] +[assembly: AssemblyCopyright("© Copyright 2026, Rutger van Bergen")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: AssemblyConfiguration("")] @@ -16,5 +16,5 @@ // Build Number // Revision // -[assembly: AssemblyVersion("0.5.1.2")] -[assembly: AssemblyFileVersion("0.5.1.2")] +[assembly: AssemblyVersion("0.5.2.0")] +[assembly: AssemblyFileVersion("0.5.2.0")] diff --git a/tests/MixAssembler.Tests/Finding/AssemblyFindingTests.cs b/tests/MixAssembler.Tests/Finding/AssemblyFindingTests.cs new file mode 100644 index 0000000..860aa4a --- /dev/null +++ b/tests/MixAssembler.Tests/Finding/AssemblyFindingTests.cs @@ -0,0 +1,97 @@ +using MixAssembler.Finding; +using MixLib.Misc; + +namespace MixAssembler.Tests.Finding; + +public class AssemblyFindingTests +{ + [Fact] + public void AssemblyError_ShouldHaveErrorSeverity() + { + var error = new ValidationError("Test error"); + var assemblyError = new AssemblyError(10, LineSection.OpField, 5, 3, error); + + assemblyError.Severity.Should().Be(Severity.Error); + } + + [Fact] + public void AssemblyWarning_ShouldHaveWarningSeverity() + { + var error = new ValidationError("Test warning"); + var assemblyWarning = new AssemblyWarning(10, LineSection.OpField, 5, 3, error); + + assemblyWarning.Severity.Should().Be(Severity.Warning); + } + + [Fact] + public void AssemblyError_ShouldStoreLineNumber() + { + var error = new ValidationError("Test error"); + var assemblyError = new AssemblyError(42, LineSection.OpField, 5, 3, error); + + assemblyError.LineNumber.Should().Be(42); + } + + [Fact] + public void AssemblyError_ShouldStoreLineSection() + { + var error = new ValidationError("Test error"); + var assemblyError = new AssemblyError(10, LineSection.AddressField, 5, 3, error); + + assemblyError.LineSection.Should().Be(LineSection.AddressField); + } + + [Fact] + public void AssemblyError_ShouldStoreStartCharIndex() + { + var error = new ValidationError("Test error"); + var assemblyError = new AssemblyError(10, LineSection.OpField, 15, 3, error); + + assemblyError.StartCharIndex.Should().Be(15); + } + + [Fact] + public void AssemblyError_ShouldStoreLength() + { + var error = new ValidationError("Test error"); + var assemblyError = new AssemblyError(10, LineSection.OpField, 5, 7, error); + + assemblyError.Length.Should().Be(7); + } + + [Fact] + public void ValidationFinding_Message_ShouldReturnValidationErrorMessage() + { + var error = new ValidationError("Invalid instruction"); + var assemblyError = new AssemblyError(10, LineSection.OpField, 5, 3, error); + + assemblyError.Message.Should().Be("Invalid instruction"); + } + + [Fact] + public void ValidationFinding_WithBoundsError_MessageShouldIncludeBounds() + { + var error = new ValidationError(0, 100); + var assemblyError = new AssemblyError(10, LineSection.AddressField, 5, 3, error); + + assemblyError.Message.Should().Be("value must be between 0 and 100"); + } + + [Fact] + public void ValidationFinding_WithMessageAndBounds_MessageShouldIncludeBoth() + { + var error = new ValidationError("Address out of range", -3999, 3999); + var assemblyError = new AssemblyError(10, LineSection.AddressField, 5, 4, error); + + assemblyError.Message.Should().Be("Address out of range (must be between -3999 and 3999)"); + } + + [Fact] + public void ValidationFinding_Error_ShouldReturnValidationError() + { + var error = new ValidationError("Test error"); + var assemblyError = new AssemblyError(10, LineSection.OpField, 5, 3, error); + + assemblyError.Error.Should().BeSameAs(error); + } +} diff --git a/tests/MixAssembler.Tests/GlobalUsings.cs b/tests/MixAssembler.Tests/GlobalUsings.cs new file mode 100644 index 0000000..acc5a69 --- /dev/null +++ b/tests/MixAssembler.Tests/GlobalUsings.cs @@ -0,0 +1,3 @@ +global using System; +global using FluentAssertions; +global using Xunit; diff --git a/tests/MixAssembler.Tests/MixAssembler.Tests.csproj b/tests/MixAssembler.Tests/MixAssembler.Tests.csproj new file mode 100644 index 0000000..2dc2e05 --- /dev/null +++ b/tests/MixAssembler.Tests/MixAssembler.Tests.csproj @@ -0,0 +1,27 @@ + + + + net10.0-windows7.0 + false + true + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/tests/MixAssembler.Tests/Value/NumberValueTests.cs b/tests/MixAssembler.Tests/Value/NumberValueTests.cs new file mode 100644 index 0000000..27f3986 --- /dev/null +++ b/tests/MixAssembler.Tests/Value/NumberValueTests.cs @@ -0,0 +1,158 @@ +using MixAssembler.Value; +using MixLib.Type; + +namespace MixAssembler.Tests.Value; + +public class NumberValueTests +{ + [Theory] + [InlineData(0L, Word.Signs.Positive, 0L)] + [InlineData(42L, Word.Signs.Positive, 42L)] + [InlineData(100L, Word.Signs.Positive, 100L)] + [InlineData(-42L, Word.Signs.Negative, 42L)] + [InlineData(-100L, Word.Signs.Negative, 100L)] + public void Constructor_WithLongValue_ShouldSetSignAndMagnitude(long value, Word.Signs expectedSign, long expectedMagnitude) + { + var numberValue = new NumberValue(value); + + numberValue.Sign.Should().Be(expectedSign); + numberValue.Magnitude.Should().Be(expectedMagnitude); + } + + [Theory] + [InlineData(Word.Signs.Positive, 42L)] + [InlineData(Word.Signs.Negative, 100L)] + [InlineData(Word.Signs.Positive, 0L)] + public void Constructor_WithSignAndMagnitude_ShouldSetProperties(Word.Signs sign, long magnitude) + { + var numberValue = new NumberValue(sign, magnitude); + + numberValue.Sign.Should().Be(sign); + numberValue.Magnitude.Should().Be(magnitude); + } + + [Theory] + [InlineData(Word.Signs.Positive, 42L, 42L)] + [InlineData(Word.Signs.Negative, 42L, -42L)] + [InlineData(Word.Signs.Positive, 0L, 0L)] + [InlineData(Word.Signs.Negative, 0L, 0L)] + public void GetValue_ShouldApplySignToMagnitude(Word.Signs sign, long magnitude, long expectedValue) + { + var numberValue = new NumberValue(sign, magnitude); + + var result = numberValue.GetValue(0); + + result.Should().Be(expectedValue); + } + + [Fact] + public void GetValue_CurrentAddressParameter_IsIgnored() + { + var numberValue = new NumberValue(42); + + var result1 = numberValue.GetValue(0); + var result2 = numberValue.GetValue(1000); + var result3 = numberValue.GetValue(-500); + + result1.Should().Be(result2).And.Be(result3); + } + + [Fact] + public void IsValueDefined_ShouldAlwaysReturnTrue() + { + var numberValue = new NumberValue(42); + + numberValue.IsValueDefined(0).Should().BeTrue(); + numberValue.IsValueDefined(100).Should().BeTrue(); + numberValue.IsValueDefined(-100).Should().BeTrue(); + } + + [Theory] + [InlineData(42L, 42L)] + [InlineData(-42L, 42L)] + [InlineData(0L, 0L)] + public void GetMagnitude_ShouldReturnMagnitude(long value, long expectedMagnitude) + { + var numberValue = new NumberValue(value); + + var result = numberValue.GetMagnitude(0); + + result.Should().Be(expectedMagnitude); + } + + [Theory] + [InlineData(42L, Word.Signs.Positive)] + [InlineData(-42L, Word.Signs.Negative)] + [InlineData(0L, Word.Signs.Positive)] + public void GetSign_ShouldReturnSign(long value, Word.Signs expectedSign) + { + var numberValue = new NumberValue(value); + + var result = numberValue.GetSign(0); + + result.Should().Be(expectedSign); + } + + [Theory] + [InlineData("0")] + [InlineData("42")] + [InlineData("100")] + [InlineData("1234567890")] // Exactly 10 digits - max length + public void ParseValue_WithValidNumericString_ShouldReturnNumberValue(string text) + { + var result = NumberValue.ParseValue(text, 0, null); + + result.Should().NotBeNull(); + result.Should().BeOfType(); + ((NumberValue)result).Magnitude.Should().Be(long.Parse(text)); + } + + [Theory] + [InlineData("")] // Empty + [InlineData("12345678901")] // 11 digits - too long + [InlineData("abc")] // Non-numeric + [InlineData("12a34")] // Mixed + [InlineData("-42")] // Negative sign + [InlineData("+42")] // Positive sign + [InlineData("1 2 3")] // Spaces + public void ParseValue_WithInvalidString_ShouldReturnNull(string text) + { + var result = NumberValue.ParseValue(text, 0, null); + + result.Should().BeNull(); + } + + [Fact] + public void ParseValue_WithTenDigits_ShouldHandleCorrectly() + { + var tenDigits = "1234567890"; + + var result = NumberValue.ParseValue(tenDigits, 0, null); + + result.Should().NotBeNull(); + ((NumberValue)result).Magnitude.Should().Be(1234567890L); + } + + [Fact] + public void ParseValue_SectionCharIndexParameter_IsIgnored() + { + var result1 = NumberValue.ParseValue("42", 0, null); + var result2 = NumberValue.ParseValue("42", 100, null); + + result1.Should().NotBeNull(); + result2.Should().NotBeNull(); + ((NumberValue)result1).Magnitude.Should().Be(((NumberValue)result2).Magnitude); + } + + [Theory] + [InlineData("0", 0L)] + [InlineData("00042", 42L)] + [InlineData("0000000001", 1L)] + public void ParseValue_WithLeadingZeros_ShouldParseCorrectly(string text, long expectedValue) + { + var result = NumberValue.ParseValue(text, 0, null); + + result.Should().NotBeNull(); + ((NumberValue)result).Magnitude.Should().Be(expectedValue); + } +} diff --git a/tests/MixLib.Tests/GlobalUsings.cs b/tests/MixLib.Tests/GlobalUsings.cs new file mode 100644 index 0000000..acc5a69 --- /dev/null +++ b/tests/MixLib.Tests/GlobalUsings.cs @@ -0,0 +1,3 @@ +global using System; +global using FluentAssertions; +global using Xunit; diff --git a/tests/MixLib.Tests/Instruction/InstructionHelpersTests.cs b/tests/MixLib.Tests/Instruction/InstructionHelpersTests.cs new file mode 100644 index 0000000..829dcbc --- /dev/null +++ b/tests/MixLib.Tests/Instruction/InstructionHelpersTests.cs @@ -0,0 +1,17 @@ +using MixLib.Instruction; + +namespace MixLib.Tests.Instruction; + +public class InstructionHelpersTests +{ + [Fact] + public void InvalidAddress_ShouldBeIntMinValue() + { + InstructionHelpers.InvalidAddress.Should().Be(int.MinValue); + } + + // Note: The ValidateIndex and ValidateIndexAndFieldSpec methods require + // MixInstruction.Instance objects which are complex to construct in unit tests. + // These methods are better tested through integration tests with actual instructions. + // The constant validation above ensures the InvalidAddress sentinel value is correct. +} diff --git a/tests/MixLib.Tests/MemoryTests.cs b/tests/MixLib.Tests/MemoryTests.cs new file mode 100644 index 0000000..910d7bb --- /dev/null +++ b/tests/MixLib.Tests/MemoryTests.cs @@ -0,0 +1,213 @@ +using System.Collections.Generic; +using MixLib; +using MixLib.Type; + +namespace MixLib.Tests; + +public class MemoryTests +{ + [Fact] + public void Constructor_ShouldSetMinAndMaxIndices() + { + var memory = new Memory(-100, 100); + + memory.MinWordIndex.Should().Be(-100); + memory.MaxWordIndex.Should().Be(100); + } + + [Fact] + public void WordCount_ShouldReturnCorrectCount() + { + var memory = new Memory(-100, 100); + + memory.WordCount.Should().Be(201); // -100 to 100 inclusive + } + + [Fact] + public void Indexer_Get_WithinBounds_ShouldReturnWord() + { + var memory = new Memory(0, 100); + + var word = memory[50]; + + word.Should().NotBeNull(); + word.Should().BeAssignableTo(); + } + + [Fact] + public void Indexer_Get_BelowMinIndex_ShouldThrowArgumentOutOfRangeException() + { + var memory = new Memory(0, 100); + + var act = () => memory[-1]; + + act.Should().Throw() + .WithParameterName("index") + .WithMessage("*must be between MinWordIndex*"); + } + + [Fact] + public void Indexer_Get_AboveMaxIndex_ShouldThrowArgumentOutOfRangeException() + { + var memory = new Memory(0, 100); + + var act = () => memory[101]; + + act.Should().Throw() + .WithParameterName("index"); + } + + [Fact] + public void Indexer_Set_ShouldStoreWord() + { + var memory = new Memory(0, 100); + var word = memory.GetRealWord(50); + word.LongValue = 42; + + memory[50].LongValue.Should().Be(42); + } + + [Fact] + public void Indexer_Set_BelowMinIndex_ShouldThrowIndexOutOfRangeException() + { + var memory = new Memory(0, 100); + var word = memory.GetRealWord(50); + + var act = () => memory[-1] = word; + + act.Should().Throw() + .WithMessage("*must be between MinWordIndex*"); + } + + [Fact] + public void Indexer_Set_AboveMaxIndex_ShouldThrowIndexOutOfRangeException() + { + var memory = new Memory(0, 100); + var word = memory.GetRealWord(50); + + var act = () => memory[101] = word; + + act.Should().Throw(); + } + + [Fact] + public void HasContents_WithStoredWord_ShouldReturnTrue() + { + var memory = new Memory(0, 100); + memory.GetRealWord(50).LongValue = 42; + + var result = memory.HasContents(50); + + result.Should().BeTrue(); + } + + [Fact] + public void HasContents_WithEmptyWord_ShouldReturnFalse() + { + var memory = new Memory(0, 100); + memory.GetRealWord(50).LongValue = 0; + + var result = memory.HasContents(50); + + result.Should().BeFalse(); + } + + [Fact] + public void HasContents_WithoutStoredWord_ShouldReturnFalse() + { + var memory = new Memory(0, 100); + + var result = memory.HasContents(50); + + result.Should().BeFalse(); + } + + [Fact] + public void Reset_ShouldClearAllMemory() + { + var memory = new Memory(0, 100); + memory.GetRealWord(10).LongValue = 42; + memory.GetRealWord(20).LongValue = 99; + + memory.Reset(); + + memory.HasContents(10).Should().BeFalse(); + memory.HasContents(20).Should().BeFalse(); + } + + [Fact] + public void GetRealWord_ShouldReturnMemoryFullWord() + { + var memory = new Memory(0, 100); + + var word = memory.GetRealWord(50); + + word.Should().NotBeNull(); + word.Should().BeOfType(); + } + + [Fact] + public void GetRealWord_CalledTwice_ShouldReturnSameInstance() + { + var memory = new Memory(0, 100); + + var word1 = memory.GetRealWord(50); + var word2 = memory.GetRealWord(50); + + word1.Should().BeSameAs(word2); + } + + [Fact] + public void DefaultFieldSpec_ShouldCoverFullWord() + { + var fieldSpec = Memory.DefaultFieldSpec; + + fieldSpec.LowBound.Should().Be(0); + fieldSpec.HighBound.Should().Be(5); + } + + [Fact] + public void Indexer_Set_WithSign_ShouldPreserveSign() + { + var memory = new Memory(0, 100); + var word = memory.GetRealWord(50); + word.LongValue = -42; + + memory[50].Sign.Should().Be(Word.Signs.Negative); + memory[50].LongValue.Should().Be(-42); + } + + [Fact] + public void Indexer_Set_WithBytes_ShouldPreserveBytes() + { + var memory = new Memory(0, 100); + var word = memory.GetRealWord(10); + word[0] = 10; + word[1] = 20; + word[2] = 30; + word[3] = 40; + word[4] = 50; + + memory[10][0].ByteValue.Should().Be(10); + memory[10][1].ByteValue.Should().Be(20); + memory[10][2].ByteValue.Should().Be(30); + memory[10][3].ByteValue.Should().Be(40); + memory[10][4].ByteValue.Should().Be(50); + } + + [Fact] + public void Enumeration_ShouldIterateStoredWords() + { + var memory = new Memory(0, 100); + memory.GetRealWord(10).LongValue = 42; + memory.GetRealWord(20).LongValue = 99; + + var list = new List(); + foreach (IMemoryFullWord word in memory) + { + list.Add(word); + } + + list.Should().HaveCount(2); + } +} diff --git a/tests/MixLib.Tests/Misc/ValidationErrorTests.cs b/tests/MixLib.Tests/Misc/ValidationErrorTests.cs new file mode 100644 index 0000000..5864fc5 --- /dev/null +++ b/tests/MixLib.Tests/Misc/ValidationErrorTests.cs @@ -0,0 +1,107 @@ +using MixLib.Misc; + +namespace MixLib.Tests.Misc; + +public class ValidationErrorTests +{ + [Fact] + public void Constructor_WithMessageOnly_ShouldSetMessage() + { + var error = new ValidationError("Invalid value"); + + error.Message.Should().Be("Invalid value"); + error.BoundsSpecified.Should().BeFalse(); + } + + [Fact] + public void Constructor_WithBoundsOnly_ShouldSetBounds() + { + var error = new ValidationError(0, 100); + + error.ValidLowerBound.Should().Be(0); + error.ValidUpperBound.Should().Be(100); + error.BoundsSpecified.Should().BeTrue(); + error.Message.Should().BeNull(); + } + + [Fact] + public void Constructor_WithMessageAndBounds_ShouldSetBoth() + { + var error = new ValidationError("Invalid range", -100, 100); + + error.Message.Should().Be("Invalid range"); + error.ValidLowerBound.Should().Be(-100); + error.ValidUpperBound.Should().Be(100); + error.BoundsSpecified.Should().BeTrue(); + } + + [Fact] + public void CompiledMessage_WithMessageOnly_ShouldReturnMessage() + { + var error = new ValidationError("Test message"); + + error.CompiledMessage.Should().Be("Test message"); + } + + [Fact] + public void CompiledMessage_WithBoundsOnly_ShouldFormatBounds() + { + var error = new ValidationError(0, 63); + + error.CompiledMessage.Should().Be("value must be between 0 and 63"); + } + + [Fact] + public void CompiledMessage_WithMessageAndBounds_ShouldCombineMessageAndBounds() + { + var error = new ValidationError("Field value out of range", 0, 5); + + error.CompiledMessage.Should().Be("Field value out of range (must be between 0 and 5)"); + } + + [Fact] + public void CompiledMessage_WithNegativeBounds_ShouldFormatCorrectly() + { + var error = new ValidationError(-3999, 3999); + + error.CompiledMessage.Should().Be("value must be between -3999 and 3999"); + } + + [Fact] + public void BoundsSpecified_WithMessageOnlyConstructor_ShouldBeFalse() + { + var error = new ValidationError("Message"); + + error.BoundsSpecified.Should().BeFalse(); + } + + [Fact] + public void BoundsSpecified_WithBoundsConstructor_ShouldBeTrue() + { + var error = new ValidationError(0, 100); + + error.BoundsSpecified.Should().BeTrue(); + } + + [Fact] + public void ValidLowerBound_WithoutBounds_ShouldBeUnspecified() + { + var error = new ValidationError("Message"); + + error.ValidLowerBound.Should().Be(ValidationError.Unspecified); + } + + [Fact] + public void ValidUpperBound_WithoutBounds_ShouldBeUnspecified() + { + var error = new ValidationError("Message"); + + error.ValidUpperBound.Should().Be(ValidationError.Unspecified); + } + + [Fact] + public void Unspecified_ShouldBeIntMinValue() + { + ValidationError.Unspecified.Should().Be(int.MinValue); + } +} diff --git a/tests/MixLib.Tests/MixLib.Tests.csproj b/tests/MixLib.Tests/MixLib.Tests.csproj new file mode 100644 index 0000000..ae992e9 --- /dev/null +++ b/tests/MixLib.Tests/MixLib.Tests.csproj @@ -0,0 +1,27 @@ + + + + net10.0-windows7.0 + false + true + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/tests/MixLib.Tests/RegistersTests.cs b/tests/MixLib.Tests/RegistersTests.cs new file mode 100644 index 0000000..4b91417 --- /dev/null +++ b/tests/MixLib.Tests/RegistersTests.cs @@ -0,0 +1,182 @@ +using MixLib; +using MixLib.Type; + +namespace MixLib.Tests; + +public class RegistersTests +{ + [Fact] + public void Constructor_ShouldInitializeAllRegisters() + { + var registers = new Registers(); + + registers.RA.Should().NotBeNull(); + registers.RX.Should().NotBeNull(); + registers.RJ.Should().NotBeNull(); + registers.RI1.Should().NotBeNull(); + registers.RI2.Should().NotBeNull(); + registers.RI3.Should().NotBeNull(); + registers.RI4.Should().NotBeNull(); + registers.RI5.Should().NotBeNull(); + registers.RI6.Should().NotBeNull(); + } + + [Fact] + public void Constructor_ShouldSetDefaultIndicators() + { + var registers = new Registers(); + + registers.CompareIndicator.Should().Be(Registers.CompValues.Equal); + registers.OverflowIndicator.Should().BeFalse(); + } + + [Fact] + public void RA_ShouldBeFullWordRegister() + { + var registers = new Registers(); + + registers.RA.Should().BeOfType(); + registers.RA.ByteCount.Should().Be(5); + } + + [Fact] + public void RX_ShouldBeFullWordRegister() + { + var registers = new Registers(); + + registers.RX.Should().BeOfType(); + registers.RX.ByteCount.Should().Be(5); + } + + [Fact] + public void RJ_ShouldBeAddressRegister() + { + var registers = new Registers(); + + registers.RJ.Should().BeOfType(); + registers.RJ.ByteCount.Should().Be(2); + } + + [Theory] + [InlineData(Registers.Offset.rI1)] + [InlineData(Registers.Offset.rI2)] + [InlineData(Registers.Offset.rI3)] + [InlineData(Registers.Offset.rI4)] + [InlineData(Registers.Offset.rI5)] + [InlineData(Registers.Offset.rI6)] + public void IndexRegisters_ShouldBeIndexRegister(Registers.Offset offset) + { + var registers = new Registers(); + + registers[offset].Should().BeOfType(); + registers[offset].ByteCount.Should().Be(2); + } + + [Fact] + public void Indexer_WithIntOffset_ShouldAccessRegister() + { + var registers = new Registers(); + + registers[(int)Registers.Offset.rA].Should().BeSameAs(registers.RA); + registers[(int)Registers.Offset.rI1].Should().BeSameAs(registers.RI1); + } + + [Fact] + public void Indexer_WithEnumOffset_ShouldAccessRegister() + { + var registers = new Registers(); + + registers[Registers.Offset.rA].Should().BeSameAs(registers.RA); + registers[Registers.Offset.rI1].Should().BeSameAs(registers.RI1); + } + + [Theory] + [InlineData(100, 0)] + [InlineData(100, 1)] + [InlineData(100, 2)] + public void GetIndexedAddress_WithZeroIndex_ShouldReturnBaseAddress(int baseAddress, int registerOffset) + { + var registers = new Registers(); + if (registerOffset > 0) + { + registers[(int)Registers.Offset.rI1 + registerOffset - 1].LongValue = 50; + } + + var result = registers.GetIndexedAddress(baseAddress, 0); + + result.Should().Be(baseAddress); + } + + [Theory] + [InlineData(100, 1, 10, 110)] + [InlineData(100, 2, 20, 120)] + [InlineData(100, 6, -10, 90)] + public void GetIndexedAddress_WithIndexRegister_ShouldAddRegisterValue(int baseAddress, int index, int registerValue, int expected) + { + var registers = new Registers(); + registers[(int)Registers.Offset.rI1 + index - 1].LongValue = registerValue; + + var result = registers.GetIndexedAddress(baseAddress, index); + + result.Should().Be(expected); + } + + [Theory] + [InlineData(-1)] + [InlineData(7)] + [InlineData(10)] + public void GetIndexedAddress_WithInvalidIndex_ShouldThrowArgumentException(int invalidIndex) + { + var registers = new Registers(); + + var act = () => registers.GetIndexedAddress(100, invalidIndex); + + act.Should().Throw() + .WithParameterName("index"); + } + + [Fact] + public void CompareIndicator_SetAndGet_ShouldWork() + { + var registers = new Registers(); + + registers.CompareIndicator = Registers.CompValues.Less; + + registers.CompareIndicator.Should().Be(Registers.CompValues.Less); + } + + [Fact] + public void OverflowIndicator_SetAndGet_ShouldWork() + { + var registers = new Registers(); + + registers.OverflowIndicator = true; + + registers.OverflowIndicator.Should().BeTrue(); + } + + [Fact] + public void RegisterCount_ShouldBeNine() + { + Registers.RegisterCount.Should().Be(9); // rA, rX, rJ, rI1-rI6 + } + + [Fact] + public void MaxOffset_ShouldBeSeven() + { + Registers.MaxOffset.Should().Be(7); + } + + [Fact] + public void PropertyAccessors_ShouldReturnCorrectRegisters() + { + var registers = new Registers(); + + registers.RI1.Should().BeSameAs(registers[(int)Registers.Offset.rI1]); + registers.RI2.Should().BeSameAs(registers[(int)Registers.Offset.rI2]); + registers.RI3.Should().BeSameAs(registers[(int)Registers.Offset.rI3]); + registers.RI4.Should().BeSameAs(registers[(int)Registers.Offset.rI4]); + registers.RI5.Should().BeSameAs(registers[(int)Registers.Offset.rI5]); + registers.RI6.Should().BeSameAs(registers[(int)Registers.Offset.rI6]); + } +} diff --git a/tests/MixLib.Tests/Type/AddressRegisterTests.cs b/tests/MixLib.Tests/Type/AddressRegisterTests.cs new file mode 100644 index 0000000..b4bbf86 --- /dev/null +++ b/tests/MixLib.Tests/Type/AddressRegisterTests.cs @@ -0,0 +1,96 @@ +using MixLib.Type; + +namespace MixLib.Tests.Type; + +public class AddressRegisterTests +{ + [Fact] + public void Constructor_ShouldCreateTwoByteRegister() + { + var register = new AddressRegister(); + + register.ByteCount.Should().Be(2); + register.Sign.Should().Be(Word.Signs.Positive); + } + + [Fact] + public void ByteCountWithPadding_ShouldBeTwo() + { + var register = new AddressRegister(); + + register.ByteCountWithPadding.Should().Be(2); + } + + [Fact] + public void GetByteWithPadding_ShouldReturnActualBytes() + { + var register = new AddressRegister(); + register[0] = 10; + register[1] = 20; + + register.GetByteWithPadding(0).ByteValue.Should().Be(10); + register.GetByteWithPadding(1).ByteValue.Should().Be(20); + } + + [Fact] + public void FullWordValue_ShouldPadToFiveBytes() + { + var register = new AddressRegister(); + register[0] = 10; + register[1] = 20; + register.Sign = Word.Signs.Negative; + + var fullWord = register.FullWordValue; + + fullWord[0].ByteValue.Should().Be(10); + fullWord[1].ByteValue.Should().Be(20); + fullWord[2].ByteValue.Should().Be(0); + fullWord[3].ByteValue.Should().Be(0); + fullWord[4].ByteValue.Should().Be(0); + fullWord.Sign.Should().Be(Word.Signs.Negative); + } + + [Fact] + public void DefaultFieldSpec_ShouldCoverTwoBytes() + { + var fieldSpec = AddressRegister.DefaultFieldSpec; + + fieldSpec.LowBound.Should().Be(0); + fieldSpec.HighBound.Should().Be(2); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(100, 100)] + [InlineData(-100, -100)] + public void LongValue_SetAndGet_ShouldWork(long value, long expected) + { + var register = new AddressRegister(); + + register.LongValue = value; + + register.LongValue.Should().Be(expected); + } + + [Fact] + public void Indexer_ShouldAccessBytes() + { + var register = new AddressRegister(); + + register[0] = 42; + register[1] = 24; + + register[0].ByteValue.Should().Be(42); + register[1].ByteValue.Should().Be(24); + } + + [Fact] + public void Sign_SetAndGet_ShouldWork() + { + var register = new AddressRegister(); + + register.Sign = Word.Signs.Negative; + + register.Sign.Should().Be(Word.Signs.Negative); + } +} diff --git a/tests/MixLib.Tests/Type/ExtensionMethodsTests.cs b/tests/MixLib.Tests/Type/ExtensionMethodsTests.cs new file mode 100644 index 0000000..6475e13 --- /dev/null +++ b/tests/MixLib.Tests/Type/ExtensionMethodsTests.cs @@ -0,0 +1,173 @@ +using MixLib.Type; + +namespace MixLib.Tests.Type; + +public class ExtensionMethodsTests +{ + [Theory] + [InlineData(Word.Signs.Positive, 100L, 100L)] + [InlineData(Word.Signs.Negative, 100L, -100L)] + [InlineData(Word.Signs.Positive, 0L, 0L)] + [InlineData(Word.Signs.Negative, 0L, 0L)] + public void ApplyTo_ShouldApplySignToMagnitude(Word.Signs sign, long magnitude, long expected) + { + var result = sign.ApplyTo(magnitude); + + result.Should().Be(expected); + } + + [Theory] + [InlineData(100L, Word.Signs.Positive, 100L)] + [InlineData(100L, Word.Signs.Negative, -100L)] + public void Apply_ShouldApplySignToMagnitude(long magnitude, Word.Signs sign, long expected) + { + var result = magnitude.Apply(sign); + + result.Should().Be(expected); + } + + [Theory] + [InlineData(42L, Word.Signs.Positive)] + [InlineData(-42L, Word.Signs.Negative)] + [InlineData(0L, Word.Signs.Positive)] + public void GetSign_Long_ShouldReturnCorrectSign(long value, Word.Signs expectedSign) + { + var result = value.GetSign(); + + result.Should().Be(expectedSign); + } + + [Theory] + [InlineData(42L, 42L)] + [InlineData(-42L, 42L)] + [InlineData(0L, 0L)] + public void GetMagnitude_Long_ShouldReturnAbsoluteValue(long value, long expected) + { + var result = value.GetMagnitude(); + + result.Should().Be(expected); + } + + [Theory] + [InlineData(42, 42)] + [InlineData(-42, 42)] + [InlineData(0, 0)] + public void GetMagnitude_Int_ShouldReturnAbsoluteValue(int value, int expected) + { + var result = value.GetMagnitude(); + + result.Should().Be(expected); + } + + [Theory] + [InlineData(42.5, Word.Signs.Positive)] + [InlineData(-42.5, Word.Signs.Negative)] + [InlineData(0.0, Word.Signs.Positive)] + public void GetSign_Decimal_ShouldReturnCorrectSign(double valueDouble, Word.Signs expectedSign) + { + var value = (decimal)valueDouble; + var result = value.GetSign(); + + result.Should().Be(expectedSign); + } + + [Theory] + [InlineData(42.5, 42.5)] + [InlineData(-42.5, 42.5)] + [InlineData(0.0, 0.0)] + public void GetMagnitude_Decimal_ShouldReturnAbsoluteValue(double valueDouble, double expectedDouble) + { + var value = (decimal)valueDouble; + var expected = (decimal)expectedDouble; + + var result = value.GetMagnitude(); + + result.Should().Be(expected); + } + + [Theory] + [InlineData(Word.Signs.Positive, Word.Signs.Negative)] + [InlineData(Word.Signs.Negative, Word.Signs.Positive)] + public void Invert_ShouldInvertSign(Word.Signs sign, Word.Signs expectedSign) + { + var result = sign.Invert(); + + result.Should().Be(expectedSign); + } + + [Theory] + [InlineData(Word.Signs.Positive, '+')] + [InlineData(Word.Signs.Negative, '-')] + public void ToChar_Sign_ShouldReturnCorrectCharacter(Word.Signs sign, char expectedChar) + { + var result = sign.ToChar(); + + result.Should().Be(expectedChar); + } + + [Theory] + [InlineData(Word.Signs.Positive, true)] + [InlineData(Word.Signs.Negative, false)] + public void IsPositive_ShouldReturnCorrectValue(Word.Signs sign, bool expected) + { + var result = sign.IsPositive(); + + result.Should().Be(expected); + } + + [Theory] + [InlineData(Word.Signs.Positive, false)] + [InlineData(Word.Signs.Negative, true)] + public void IsNegative_ShouldReturnCorrectValue(Word.Signs sign, bool expected) + { + var result = sign.IsNegative(); + + result.Should().Be(expected); + } + + [Theory] + [InlineData('+', Word.Signs.Positive)] + [InlineData('-', Word.Signs.Negative)] + [InlineData('x', Word.Signs.Positive)] // Non-minus defaults to positive + public void ToSign_Char_ShouldReturnCorrectSign(char ch, Word.Signs expectedSign) + { + var result = ch.ToSign(); + + result.Should().Be(expectedSign); + } + + [Theory] + [InlineData(Registers.CompValues.Equal, Registers.CompValues.Greater)] + [InlineData(Registers.CompValues.Greater, Registers.CompValues.Less)] + [InlineData(Registers.CompValues.Less, Registers.CompValues.Equal)] + public void Next_CompValues_ShouldCycle(Registers.CompValues current, Registers.CompValues expected) + { + var result = current.Next(); + + result.Should().Be(expected); + } + + [Theory] + [InlineData(Registers.CompValues.Less, '<')] + [InlineData(Registers.CompValues.Equal, '=')] + [InlineData(Registers.CompValues.Greater, '>')] + public void ToChar_CompValues_ShouldReturnCorrectCharacter(Registers.CompValues compValue, char expectedChar) + { + var result = compValue.ToChar(); + + result.Should().Be(expectedChar); + } + + [Theory] + [InlineData(-1, Registers.CompValues.Less)] + [InlineData(0, Registers.CompValues.Equal)] + [InlineData(1, Registers.CompValues.Greater)] + [InlineData(-100, Registers.CompValues.Less)] + [InlineData(100, Registers.CompValues.Greater)] + public void ToCompValue_ShouldConvertIntToCompValue(int value, Registers.CompValues expected) + { + var result = value.ToCompValue(); + + result.Should().Be(expected); + } +} diff --git a/tests/MixLib.Tests/Type/FieldSpecTests.cs b/tests/MixLib.Tests/Type/FieldSpecTests.cs new file mode 100644 index 0000000..e041baf --- /dev/null +++ b/tests/MixLib.Tests/Type/FieldSpecTests.cs @@ -0,0 +1,219 @@ +using MixLib.Type; + +namespace MixLib.Tests.Type; + +public class FieldSpecTests +{ + [Theory] + [InlineData(0, 5, true)] + [InlineData(1, 5, true)] + [InlineData(0, 0, true)] + [InlineData(3, 5, true)] + [InlineData(1, 1, true)] + [InlineData(0, 6, true)] // Special case for floating point + public void Constructor_WithValidBounds_ShouldCreateFieldSpec(int lowBound, int highBound, bool shouldBeValid) + { + var fieldSpec = new FieldSpec(lowBound, highBound); + + fieldSpec.Should().NotBeNull(); + fieldSpec.LowBound.Should().Be(lowBound); + fieldSpec.HighBound.Should().Be(highBound); + fieldSpec.IsValid.Should().Be(shouldBeValid); + } + + [Theory] + [InlineData(6, 5)] // Low > high + [InlineData(-1, 5)] // Low < 0 + [InlineData(0, 7)] // High > 6 (not floating point) + [InlineData(1, 6)] // High = 6 but low != 0 + public void Constructor_WithInvalidBounds_ShouldThrowArgumentException(int lowBound, int highBound) + { + var act = () => new FieldSpec(lowBound, highBound); + + act.Should().Throw() + .WithMessage("low and/or high bounds are invalid"); + } + + [Fact] + public void Constructor_WithMixByte_ShouldSetMixByteValue() + { + var mixByte = new MixByte(13); // 1:5 + var fieldSpec = new FieldSpec(mixByte); + + fieldSpec.MixByteValue.ByteValue.Should().Be(13); + fieldSpec.LowBound.Should().Be(1); + fieldSpec.HighBound.Should().Be(5); + } + + [Fact] + public void IncludesSign_WithLowBoundZero_ShouldBeTrue() + { + var fieldSpec = new FieldSpec(0, 5); + + fieldSpec.IncludesSign.Should().BeTrue(); + } + + [Fact] + public void IncludesSign_WithLowBoundNotZero_ShouldBeFalse() + { + var fieldSpec = new FieldSpec(1, 5); + + fieldSpec.IncludesSign.Should().BeFalse(); + } + + [Theory] + [InlineData(0, 5, 0, 4)] // Sign + all bytes + [InlineData(1, 5, 0, 4)] // All bytes, no sign + [InlineData(3, 5, 2, 4)] // Partial field + [InlineData(0, 0, 0, -1)] // Sign only -> special case + public void ByteIndices_ShouldCalculateCorrectly(int lowBound, int highBound, int expectedLowIndex, int expectedHighIndex) + { + var fieldSpec = new FieldSpec(lowBound, highBound); + + fieldSpec.LowBoundByteIndex.Should().Be(expectedLowIndex); + if (expectedHighIndex >= 0) + { + fieldSpec.HighBoundByteIndex.Should().Be(expectedHighIndex); + } + } + + [Theory] + [InlineData(0, 5, 5)] + [InlineData(1, 5, 5)] + [InlineData(3, 5, 3)] + [InlineData(1, 1, 1)] + public void ByteCount_ShouldReturnCorrectCount(int lowBound, int highBound, int expectedCount) + { + var fieldSpec = new FieldSpec(lowBound, highBound); + + fieldSpec.ByteCount.Should().Be(expectedCount); + } + + [Fact] + public void FloatingPoint_WithBounds0And6_ShouldBeTrue() + { + var fieldSpec = new FieldSpec(0, 6); + + fieldSpec.FloatingPoint.Should().BeTrue(); + } + + [Theory] + [InlineData(0, 5)] + [InlineData(1, 5)] + [InlineData(1, 1)] + public void FloatingPoint_WithOtherBounds_ShouldBeFalse(int lowBound, int highBound) + { + var fieldSpec = new FieldSpec(lowBound, highBound); + + fieldSpec.FloatingPoint.Should().BeFalse(); + } + + [Theory] + [InlineData(0, 5, "(0:5)")] + [InlineData(1, 5, "(1:5)")] + [InlineData(3, 3, "(3:3)")] + public void ToString_WithValidSpec_ShouldFormatCorrectly(int lowBound, int highBound, string expected) + { + var fieldSpec = new FieldSpec(lowBound, highBound); + + fieldSpec.ToString().Should().Be(expected); + } + + [Fact] + public void Equals_WithSameValues_ShouldReturnTrue() + { + var fieldSpec1 = new FieldSpec(1, 5); + var fieldSpec2 = new FieldSpec(1, 5); + + fieldSpec1.Equals(fieldSpec2).Should().BeTrue(); + (fieldSpec1 == fieldSpec2).Should().BeTrue(); + } + + [Fact] + public void Equals_WithDifferentValues_ShouldReturnFalse() + { + var fieldSpec1 = new FieldSpec(1, 5); + var fieldSpec2 = new FieldSpec(0, 5); + + fieldSpec1.Equals(fieldSpec2).Should().BeFalse(); + (fieldSpec1 != fieldSpec2).Should().BeTrue(); + } + + [Fact] + public void GetHashCode_WithSameValues_ShouldReturnSameHash() + { + var fieldSpec1 = new FieldSpec(1, 5); + var fieldSpec2 = new FieldSpec(1, 5); + + fieldSpec1.GetHashCode().Should().Be(fieldSpec2.GetHashCode()); + } + + [Theory] + [InlineData(0, 5, true)] + [InlineData(1, 5, true)] + [InlineData(0, 6, true)] + [InlineData(6, 5, false)] + [InlineData(-1, 5, false)] + public void IsValidFieldSpec_StaticMethod_ShouldValidateCorrectly(int lowBound, int highBound, bool expectedValid) + { + var isValid = FieldSpec.IsValidFieldSpec(lowBound, highBound); + + isValid.Should().Be(expectedValid); + } + + [Theory] + [InlineData(13, true)] // 1:5 + [InlineData(5, true)] // 0:5 + [InlineData(63, false)] // 7:7 - invalid + public void IsValidFieldSpec_WithMixByte_ShouldValidateCorrectly(byte value, bool expectedValid) + { + var mixByte = new MixByte(value); + var isValid = FieldSpec.IsValidFieldSpec(mixByte); + + isValid.Should().Be(expectedValid); + } + + [Fact] + public void LowBound_WithInvalidFieldSpec_ShouldThrowInvalidOperationException() + { + var invalidMixByte = new MixByte(63); // 7:7 + var fieldSpec = new FieldSpec(invalidMixByte); + + var act = () => fieldSpec.LowBound; + + act.Should().Throw() + .WithMessage("this MixFieldSpec is not valid"); + } + + [Fact] + public void HighBound_WithInvalidFieldSpec_ShouldThrowInvalidOperationException() + { + var invalidMixByte = new MixByte(63); // 7:7 + var fieldSpec = new FieldSpec(invalidMixByte); + + var act = () => fieldSpec.HighBound; + + act.Should().Throw() + .WithMessage("this MixFieldSpec is not valid"); + } + + [Fact] + public void MixByteValue_ShouldEncodeFieldSpecCorrectly() + { + var fieldSpec = new FieldSpec(1, 5); + + fieldSpec.MixByteValue.ByteValue.Should().Be(13); // (1 * 8) + 5 = 13 + } + + [Theory] + [InlineData(0, 0, 0)] + [InlineData(1, 5, 13)] + [InlineData(0, 5, 5)] + [InlineData(3, 3, 27)] + public void MixByteValue_ForVariousBounds_ShouldEncodeCorrectly(int lowBound, int highBound, byte expectedValue) + { + var fieldSpec = new FieldSpec(lowBound, highBound); + + fieldSpec.MixByteValue.ByteValue.Should().Be(expectedValue); + } +} diff --git a/tests/MixLib.Tests/Type/FullWordRegisterTests.cs b/tests/MixLib.Tests/Type/FullWordRegisterTests.cs new file mode 100644 index 0000000..f0df5fd --- /dev/null +++ b/tests/MixLib.Tests/Type/FullWordRegisterTests.cs @@ -0,0 +1,124 @@ +using MixLib.Type; + +namespace MixLib.Tests.Type; + +public class FullWordRegisterTests +{ + [Fact] + public void Constructor_ShouldCreateFiveByteRegister() + { + var register = new FullWordRegister(); + + register.ByteCount.Should().Be(5); + register.Sign.Should().Be(Word.Signs.Positive); + } + + [Fact] + public void ByteCountWithPadding_ShouldBeFive() + { + var register = new FullWordRegister(); + + register.ByteCountWithPadding.Should().Be(5); + } + + [Fact] + public void PaddingByteCount_ShouldBeZero() + { + FullWordRegister.PaddingByteCount.Should().Be(0); + } + + [Fact] + public void RegisterByteCount_ShouldBeFive() + { + FullWordRegister.RegisterByteCount.Should().Be(5); + } + + [Fact] + public void GetByteWithPadding_ShouldReturnActualBytes() + { + var register = new FullWordRegister(); + register[0] = 10; + register[1] = 20; + register[2] = 30; + register[3] = 40; + register[4] = 50; + + register.GetByteWithPadding(0).ByteValue.Should().Be(10); + register.GetByteWithPadding(1).ByteValue.Should().Be(20); + register.GetByteWithPadding(2).ByteValue.Should().Be(30); + register.GetByteWithPadding(3).ByteValue.Should().Be(40); + register.GetByteWithPadding(4).ByteValue.Should().Be(50); + } + + [Fact] + public void FullWordValue_ShouldMatchRegisterExactly() + { + var register = new FullWordRegister(); + register[0] = 10; + register[1] = 20; + register[2] = 30; + register[3] = 40; + register[4] = 50; + register.Sign = Word.Signs.Negative; + + var fullWord = register.FullWordValue; + + fullWord[0].ByteValue.Should().Be(10); + fullWord[1].ByteValue.Should().Be(20); + fullWord[2].ByteValue.Should().Be(30); + fullWord[3].ByteValue.Should().Be(40); + fullWord[4].ByteValue.Should().Be(50); + fullWord.Sign.Should().Be(Word.Signs.Negative); + } + + [Fact] + public void DefaultFieldSpec_ShouldCoverAllFiveBytes() + { + var fieldSpec = FullWordRegister.DefaultFieldSpec; + + fieldSpec.LowBound.Should().Be(0); + fieldSpec.HighBound.Should().Be(5); + } + + [Theory] + [InlineData(0L, 0L)] + [InlineData(12345L, 12345L)] + [InlineData(-12345L, -12345L)] + [InlineData(1073741823L, 1073741823L)] + public void LongValue_SetAndGet_ShouldWork(long value, long expected) + { + var register = new FullWordRegister(); + + register.LongValue = value; + + register.LongValue.Should().Be(expected); + } + + [Fact] + public void Indexer_ShouldAccessAllBytes() + { + var register = new FullWordRegister(); + + register[0] = 1; + register[1] = 2; + register[2] = 3; + register[3] = 4; + register[4] = 5; + + register[0].ByteValue.Should().Be(1); + register[1].ByteValue.Should().Be(2); + register[2].ByteValue.Should().Be(3); + register[3].ByteValue.Should().Be(4); + register[4].ByteValue.Should().Be(5); + } + + [Fact] + public void Sign_SetAndGet_ShouldWork() + { + var register = new FullWordRegister(); + + register.Sign = Word.Signs.Negative; + + register.Sign.Should().Be(Word.Signs.Negative); + } +} diff --git a/tests/MixLib.Tests/Type/FullWordTests.cs b/tests/MixLib.Tests/Type/FullWordTests.cs new file mode 100644 index 0000000..f78886b --- /dev/null +++ b/tests/MixLib.Tests/Type/FullWordTests.cs @@ -0,0 +1,194 @@ +using MixLib.Type; + +namespace MixLib.Tests.Type; + +public class FullWordTests +{ + [Fact] + public void Constructor_Default_ShouldCreateZeroValue() + { + var word = new FullWord(); + + word.LongValue.Should().Be(0); + word.Sign.Should().Be(Word.Signs.Positive); + ((Word)word).ByteCount.Should().Be(5); + } + + [Theory] + [InlineData(0L)] + [InlineData(1L)] + [InlineData(100L)] + [InlineData(-100L)] + [InlineData(1073741823L)] // Max positive value for 5 bytes (2^30 - 1) + [InlineData(-1073741823L)] + public void Constructor_WithLongValue_ShouldSetValue(long value) + { + var word = new FullWord(value); + + word.LongValue.Should().Be(value); + } + + [Theory] + [InlineData(Word.Signs.Positive, 100L, 100L)] + [InlineData(Word.Signs.Negative, 100L, -100L)] + [InlineData(Word.Signs.Positive, 0L, 0L)] + public void Constructor_WithSignAndMagnitude_ShouldSetCorrectValue(Word.Signs sign, long magnitude, long expectedValue) + { + var word = new FullWord(sign, magnitude); + + word.LongValue.Should().Be(expectedValue); + word.Sign.Should().Be(sign); + } + + [Fact] + public void ImplicitConversion_FromLong_ShouldCreateFullWord() + { + FullWord word = 42L; + + word.LongValue.Should().Be(42); + } + + [Fact] + public void Clone_ShouldCreateIndependentCopy() + { + var original = new FullWord(42); + var clone = (FullWord)original.Clone(); + + clone.LongValue.Should().Be(original.LongValue); + + clone.LongValue = 100; + + original.LongValue.Should().Be(42); + clone.LongValue.Should().Be(100); + } + + [Fact] + public void ByteCount_ShouldBeFive() + { + var word = new FullWord(); + + ((Word)word).ByteCount.Should().Be(5); + } + + [Fact] + public void LongValue_Set_ShouldUpdateValue() + { + var word = new FullWord(42); + + word.LongValue = 100; + + word.LongValue.Should().Be(100); + } + + [Fact] + public void Sign_NegativeValue_ShouldBeNegative() + { + var word = new FullWord(-42); + + word.Sign.Should().Be(Word.Signs.Negative); + } + + [Fact] + public void Sign_PositiveValue_ShouldBePositive() + { + var word = new FullWord(42); + + word.Sign.Should().Be(Word.Signs.Positive); + } + + [Fact] + public void InvertSign_ShouldChangeSign() + { + var word = new FullWord(42); + + word.InvertSign(); + + word.Sign.Should().Be(Word.Signs.Negative); + word.LongValue.Should().Be(-42); + } + + [Fact] + public void IsEmpty_WithZeroValue_ShouldBeTrue() + { + var word = new FullWord(0); + + word.IsEmpty.Should().BeTrue(); + } + + [Fact] + public void IsEmpty_WithNonZeroValue_ShouldBeFalse() + { + var word = new FullWord(42); + + word.IsEmpty.Should().BeFalse(); + } + + [Fact] + public void MagnitudeLongValue_ShouldReturnAbsoluteValue() + { + var positiveWord = new FullWord(42); + var negativeWord = new FullWord(-42); + + positiveWord.MagnitudeLongValue.Should().Be(42); + negativeWord.MagnitudeLongValue.Should().Be(42); + } + + [Fact] + public void Indexer_ShouldAccessIndividualBytes() + { + var word = new FullWord(); + + word[0] = new MixByte(10); + word[1] = new MixByte(20); + + word[0].ByteValue.Should().Be(10); + word[1].ByteValue.Should().Be(20); + } + + [Theory] + [InlineData(42, "+ 00 00 00 00 42")] + [InlineData(-42, "- 00 00 00 00 42")] + [InlineData(0, "+ 00 00 00 00 00")] + public void ToString_WithoutChars_ShouldFormatCorrectly(long value, string expected) + { + var word = new FullWord(value); + + word.ToString(false).Should().Be(expected); + } + + [Fact] + public void Load_WithText_ShouldLoadCharacters() + { + var word = new FullWord(); + + word.Load("HELLO"); + + word[0].CharValue.Should().Be('H'); + word[1].CharValue.Should().Be('E'); + word[2].CharValue.Should().Be('L'); + word[3].CharValue.Should().Be('L'); + word[4].CharValue.Should().Be('O'); + } + + [Fact] + public void Load_WithShortText_ShouldPadWithZeros() + { + var word = new FullWord(); + + word.Load("HI"); + + word[0].CharValue.Should().Be('H'); + word[1].CharValue.Should().Be('I'); + word[2].ByteValue.Should().Be(0); + word[3].ByteValue.Should().Be(0); + word[4].ByteValue.Should().Be(0); + } + + [Fact] + public void MaxMagnitude_ShouldBeCorrect() + { + var word = new FullWord(); + + word.MaxMagnitude.Should().Be((1L << 30) - 1); + } +} diff --git a/tests/MixLib.Tests/Type/IndexRegisterTests.cs b/tests/MixLib.Tests/Type/IndexRegisterTests.cs new file mode 100644 index 0000000..45d135e --- /dev/null +++ b/tests/MixLib.Tests/Type/IndexRegisterTests.cs @@ -0,0 +1,110 @@ +using MixLib.Type; + +namespace MixLib.Tests.Type; + +public class IndexRegisterTests +{ + [Fact] + public void Constructor_ShouldCreateTwoByteRegister() + { + var register = new IndexRegister(); + + register.ByteCount.Should().Be(2); + register.Sign.Should().Be(Word.Signs.Positive); + } + + [Fact] + public void ByteCountWithPadding_ShouldBeFive() + { + var register = new IndexRegister(); + + register.ByteCountWithPadding.Should().Be(5); + } + + [Fact] + public void PaddingByteCount_ShouldBeThree() + { + IndexRegister.PaddingByteCount.Should().Be(3); + } + + [Fact] + public void RegisterByteCount_ShouldBeTwo() + { + IndexRegister.RegisterByteCount.Should().Be(2); + } + + [Fact] + public void GetByteWithPadding_WithinPadding_ShouldReturnZero() + { + var register = new IndexRegister(); + register[0] = 10; + register[1] = 20; + + register.GetByteWithPadding(0).ByteValue.Should().Be(0); + register.GetByteWithPadding(1).ByteValue.Should().Be(0); + register.GetByteWithPadding(2).ByteValue.Should().Be(0); + } + + [Fact] + public void GetByteWithPadding_AfterPadding_ShouldReturnActualByte() + { + var register = new IndexRegister(); + register[0] = 10; + register[1] = 20; + + register.GetByteWithPadding(3).ByteValue.Should().Be(10); + register.GetByteWithPadding(4).ByteValue.Should().Be(20); + } + + [Fact] + public void FullWordValue_ShouldIncludePadding() + { + var register = new IndexRegister(); + register[0] = 10; + register[1] = 20; + register.Sign = Word.Signs.Negative; + + var fullWord = register.FullWordValue; + + fullWord[0].ByteValue.Should().Be(0); + fullWord[1].ByteValue.Should().Be(0); + fullWord[2].ByteValue.Should().Be(0); + fullWord[3].ByteValue.Should().Be(10); + fullWord[4].ByteValue.Should().Be(20); + fullWord.Sign.Should().Be(Word.Signs.Negative); + } + + [Fact] + public void DefaultFieldSpec_ShouldCoverFullWord() + { + var fieldSpec = IndexRegister.DefaultFieldSpec; + + fieldSpec.LowBound.Should().Be(0); + fieldSpec.HighBound.Should().Be(5); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(100, 100)] + [InlineData(-100, -100)] + public void LongValue_SetAndGet_ShouldWork(long value, long expected) + { + var register = new IndexRegister(); + + register.LongValue = value; + + register.LongValue.Should().Be(expected); + } + + [Fact] + public void Indexer_ShouldAccessBytes() + { + var register = new IndexRegister(); + + register[0] = 42; + register[1] = 24; + + register[0].ByteValue.Should().Be(42); + register[1].ByteValue.Should().Be(24); + } +} diff --git a/tests/MixLib.Tests/Type/MixByteCollectionTests.cs b/tests/MixLib.Tests/Type/MixByteCollectionTests.cs new file mode 100644 index 0000000..3aa5c5e --- /dev/null +++ b/tests/MixLib.Tests/Type/MixByteCollectionTests.cs @@ -0,0 +1,234 @@ +using MixLib.Type; +using System.Collections.Generic; + +namespace MixLib.Tests.Type; + +public class MixByteCollectionTests +{ + [Theory] + [InlineData(1)] + [InlineData(5)] + [InlineData(10)] + [InlineData(100)] + public void Constructor_WithByteCount_ShouldCreateCollectionWithZeros(int byteCount) + { + var collection = new MixByteCollection(byteCount); + + collection.MaxByteCount.Should().Be(byteCount); + + for (int i = 0; i < byteCount; i++) + { + collection[i].ByteValue.Should().Be(0); + } + } + + [Fact] + public void Constructor_WithByteArray_ShouldCopyArray() + { + var bytes = new MixByte[] { 10, 20, 30, 40, 50 }; + + var collection = new MixByteCollection(bytes); + + collection.MaxByteCount.Should().Be(5); + collection[0].ByteValue.Should().Be(10); + collection[1].ByteValue.Should().Be(20); + collection[2].ByteValue.Should().Be(30); + collection[3].ByteValue.Should().Be(40); + collection[4].ByteValue.Should().Be(50); + } + + [Fact] + public void Indexer_Get_ShouldReturnByte() + { + var collection = new MixByteCollection(5); + collection[2] = new MixByte(42); + + var result = collection[2]; + + result.ByteValue.Should().Be(42); + } + + [Fact] + public void Indexer_Set_ShouldUpdateByte() + { + var collection = new MixByteCollection(5); + + collection[2] = new MixByte(42); + + collection[2].ByteValue.Should().Be(42); + } + + [Fact] + public void Load_WithText_ShouldLoadCharacters() + { + var collection = new MixByteCollection(5); + + collection.Load("HELLO"); + + collection[0].CharValue.Should().Be('H'); + collection[1].CharValue.Should().Be('E'); + collection[2].CharValue.Should().Be('L'); + collection[3].CharValue.Should().Be('L'); + collection[4].CharValue.Should().Be('O'); + } + + [Fact] + public void Load_WithShortText_ShouldPadWithZeros() + { + var collection = new MixByteCollection(5); + + collection.Load("HI"); + + collection[0].CharValue.Should().Be('H'); + collection[1].CharValue.Should().Be('I'); + collection[2].ByteValue.Should().Be(0); + collection[3].ByteValue.Should().Be(0); + collection[4].ByteValue.Should().Be(0); + } + + [Fact] + public void Load_WithLongText_ShouldTruncate() + { + var collection = new MixByteCollection(3); + + collection.Load("HELLO"); + + collection.MaxByteCount.Should().Be(3); + collection[0].CharValue.Should().Be('H'); + collection[1].CharValue.Should().Be('E'); + collection[2].CharValue.Should().Be('L'); + } + + [Fact] + public void Load_WithNull_ShouldFillWithZeros() + { + var collection = new MixByteCollection(5); + collection.Load("TEST"); + + collection.Load(null); + + for (int i = 0; i < 5; i++) + { + collection[i].ByteValue.Should().Be(0); + } + } + + [Fact] + public void Load_WithEmptyString_ShouldFillWithZeros() + { + var collection = new MixByteCollection(5); + collection.Load("TEST"); + + collection.Load(""); + + for (int i = 0; i < 5; i++) + { + collection[i].ByteValue.Should().Be(0); + } + } + + [Fact] + public void ToString_WithAsCharsTrue_ShouldReturnCharacterString() + { + var collection = new MixByteCollection(5); + collection.Load("HELLO"); + + var result = collection.ToString(true); + + result.Should().Be("HELLO"); + } + + [Fact] + public void ToString_WithAsCharsTrue_ShouldTrimTrailingSpaces() + { + var collection = new MixByteCollection(5); + collection.Load("HI"); + + var result = collection.ToString(true); + + result.Should().Be("HI"); + } + + [Fact] + public void ToArray_ShouldReturnClonedArray() + { + var collection = new MixByteCollection(3); + collection[0] = 10; + collection[1] = 20; + collection[2] = 30; + + var array = collection.ToArray(); + + array.Length.Should().Be(3); + array[0].ByteValue.Should().Be(10); + array[1].ByteValue.Should().Be(20); + array[2].ByteValue.Should().Be(30); + } + + [Fact] + public void ToArray_ShouldReturnIndependentCopy() + { + var collection = new MixByteCollection(3); + collection[0] = 10; + + var array = collection.ToArray(); + collection[0] = 20; + + array[0].ByteValue.Should().Be(10); + collection[0].ByteValue.Should().Be(20); + } + + [Fact] + public void Clone_ShouldCreateIndependentCopy() + { + var original = new MixByteCollection(3); + original[0] = 10; + original[1] = 20; + original[2] = 30; + + var clone = (MixByteCollection)original.Clone(); + + clone.MaxByteCount.Should().Be(original.MaxByteCount); + clone[0].ByteValue.Should().Be(10); + clone[1].ByteValue.Should().Be(20); + clone[2].ByteValue.Should().Be(30); + } + + [Fact] + public void Clone_ShouldBeIndependent() + { + var original = new MixByteCollection(3); + original[0] = 10; + + var clone = (MixByteCollection)original.Clone(); + clone[0] = 20; + + original[0].ByteValue.Should().Be(10); + clone[0].ByteValue.Should().Be(20); + } + + [Fact] + public void GetEnumerator_ShouldIterateAllBytes() + { + var collection = new MixByteCollection(3); + collection[0] = 10; + collection[1] = 20; + collection[2] = 30; + + var list = new List(); + foreach (MixByte b in collection) + { + list.Add(b.ByteValue); + } + + list.Should().Equal(10, 20, 30); + } + + [Fact] + public void MaxByteCount_ShouldReturnCorrectValue() + { + var collection = new MixByteCollection(7); + + collection.MaxByteCount.Should().Be(7); + } +} diff --git a/tests/MixLib.Tests/Type/MixByteTests.cs b/tests/MixLib.Tests/Type/MixByteTests.cs new file mode 100644 index 0000000..1bae7a0 --- /dev/null +++ b/tests/MixLib.Tests/Type/MixByteTests.cs @@ -0,0 +1,282 @@ +using MixLib.Type; + +namespace MixLib.Tests.Type; + +public class MixByteTests +{ + [Fact] + public void Constructor_DefaultConstructor_ShouldCreateZeroValue() + { + var mixByte = new MixByte(); + + mixByte.ByteValue.Should().Be(0); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(32)] + [InlineData(63)] + public void Constructor_WithValidByteValue_ShouldSetValue(byte value) + { + var mixByte = new MixByte(value); + + mixByte.ByteValue.Should().Be(value); + } + + [Fact] + public void Constructor_WithValueGreaterThanMaxValue_ShouldThrowArgumentException() + { + byte invalidValue = MixByte.MaxValue + 1; + + var act = () => new MixByte(invalidValue); + + act.Should().Throw() + .WithParameterName("value") + .WithMessage("value too large for MixByte*"); + } + + [Theory] + [InlineData(' ', 0)] + [InlineData('A', 1)] + [InlineData('I', 9)] + [InlineData('0', 30)] + [InlineData('9', 39)] + public void Constructor_WithValidChar_ShouldSetCorrectByteValue(char ch, byte expectedValue) + { + var mixByte = new MixByte(ch); + + mixByte.ByteValue.Should().Be(expectedValue); + } + + [Fact] + public void Constructor_WithUnknownChar_ShouldSetMaxValue() + { + var mixByte = new MixByte('~'); + + mixByte.ByteValue.Should().Be(MixByte.MaxValue); + } + + [Theory] + [InlineData(0, ' ')] + [InlineData(1, 'A')] + [InlineData(9, 'I')] + [InlineData(30, '0')] + [InlineData(39, '9')] + public void CharValue_ShouldReturnCorrectCharacter(byte value, char expectedChar) + { + var mixByte = new MixByte(value); + + mixByte.CharValue.Should().Be(expectedChar); + } + + [Fact] + public void CharValue_WithValueGreaterThanCharsLength_ShouldReturnLastChar() + { + var mixByte = new MixByte(MixByte.MaxValue); + + mixByte.CharValue.Should().Be(MixByte.MixChars[^1]); + } + + [Theory] + [InlineData(10, 5, 15)] + [InlineData(0, 10, 10)] + [InlineData(63, 0, 63)] + public void AddOperator_ShouldReturnCorrectSum(byte byteValue, byte delta, int expected) + { + var mixByte = new MixByte(byteValue); + + var result = mixByte + delta; + + result.Should().Be(expected); + } + + [Theory] + [InlineData(20, 4, 5)] + [InlineData(63, 1, 63)] + [InlineData(0, 1, 0)] + public void DivideOperator_ShouldReturnCorrectQuotient(byte byteValue, byte divisor, int expected) + { + var mixByte = new MixByte(byteValue); + + var result = mixByte / divisor; + + result.Should().Be(expected); + } + + [Theory] + [InlineData(10, 5, 5)] + [InlineData(20, 3, 17)] + [InlineData(5, 5, 0)] + public void SubtractOperator_ShouldReturnCorrectDifference(byte byteValue, byte delta, int expected) + { + var mixByte = new MixByte(byteValue); + + var result = mixByte - delta; + + result.Should().Be(expected); + } + + [Theory] + [InlineData(20, 6, 2)] + [InlineData(63, 10, 3)] + public void ModuloOperator_ShouldReturnCorrectRemainder(byte byteValue, byte divisor, int expected) + { + var mixByte = new MixByte(byteValue); + + var result = mixByte % divisor; + + result.Should().Be(expected); + } + + [Fact] + public void EqualityOperator_WithEqualValues_ShouldReturnTrue() + { + var mixByte = new MixByte(42); + + (mixByte == 42).Should().BeTrue(); + } + + [Fact] + public void EqualityOperator_WithDifferentValues_ShouldReturnFalse() + { + var mixByte = new MixByte(42); + + (mixByte == 10).Should().BeFalse(); + } + + [Fact] + public void InequalityOperator_WithDifferentValues_ShouldReturnTrue() + { + var mixByte = new MixByte(42); + + (mixByte != 10).Should().BeTrue(); + } + + [Fact] + public void GreaterThanOperator_ShouldCompareCorrectly() + { + var mixByte = new MixByte(42); + + (mixByte > 10).Should().BeTrue(); + (mixByte > 42).Should().BeFalse(); + (mixByte > 50).Should().BeFalse(); + } + + [Fact] + public void LessThanOperator_ShouldCompareCorrectly() + { + var mixByte = new MixByte(42); + + (mixByte < 50).Should().BeTrue(); + (mixByte < 42).Should().BeFalse(); + (mixByte < 10).Should().BeFalse(); + } + + [Fact] + public void ImplicitConversionToByte_ShouldReturnByteValue() + { + var mixByte = new MixByte(42); + + byte result = mixByte; + + result.Should().Be(42); + } + + [Fact] + public void ImplicitConversionToChar_ShouldReturnCharValue() + { + var mixByte = new MixByte(1); + + char result = mixByte; + + result.Should().Be('A'); + } + + [Fact] + public void ImplicitConversionToInt_ShouldReturnByteValue() + { + var mixByte = new MixByte(42); + + int result = mixByte; + + result.Should().Be(42); + } + + [Fact] + public void ImplicitConversionFromByte_ShouldCreateMixByte() + { + MixByte mixByte = (byte)42; + + mixByte.ByteValue.Should().Be(42); + } + + [Fact] + public void ImplicitConversionFromChar_ShouldCreateMixByte() + { + MixByte mixByte = 'A'; + + mixByte.ByteValue.Should().Be(1); + } + + [Fact] + public void ImplicitConversionFromInt_ShouldCreateMixByte() + { + MixByte mixByte = 42; + + mixByte.ByteValue.Should().Be(42); + } + + [Fact] + public void ToString_ShouldReturnTwoDigitString() + { + var mixByte = new MixByte(5); + + mixByte.ToString().Should().Be("05"); + } + + [Fact] + public void Equals_WithSameValue_ShouldReturnTrue() + { + var mixByte1 = new MixByte(42); + var mixByte2 = new MixByte(42); + + mixByte1.Equals(mixByte2).Should().BeTrue(); + } + + [Fact] + public void Equals_WithDifferentValue_ShouldReturnFalse() + { + var mixByte1 = new MixByte(42); + var mixByte2 = new MixByte(10); + + mixByte1.Equals(mixByte2).Should().BeFalse(); + } + + [Fact] + public void GetHashCode_WithSameValue_ShouldReturnSameHashCode() + { + var mixByte1 = new MixByte(42); + var mixByte2 = new MixByte(42); + + mixByte1.GetHashCode().Should().Be(mixByte2.GetHashCode()); + } + + [Fact] + public void MaxValue_ShouldBe63() + { + MixByte.MaxValue.Should().Be(63); + } + + [Fact] + public void MinValue_ShouldBe0() + { + MixByte.MinValue.Should().Be(0); + } + + [Fact] + public void BitCount_ShouldBe6() + { + MixByte.BitCount.Should().Be(6); + } +} diff --git a/tests/MixLib.Tests/Type/WordFieldTests.cs b/tests/MixLib.Tests/Type/WordFieldTests.cs new file mode 100644 index 0000000..e58d15b --- /dev/null +++ b/tests/MixLib.Tests/Type/WordFieldTests.cs @@ -0,0 +1,296 @@ +using MixLib.Type; + +namespace MixLib.Tests.Type; + +public class WordFieldTests +{ + [Fact] + public void LoadFromFullWord_WithFullField_ShouldLoadAllBytes() + { + var word = new FullWord(12345); + var fieldSpec = new FieldSpec(1, 5); + + var field = WordField.LoadFromFullWord(fieldSpec, word); + + field.Should().NotBeNull(); + ((Word)field).ByteCount.Should().Be(5); + } + + [Fact] + public void LoadFromFullWord_WithSignIncluded_ShouldCopySign() + { + var word = new FullWord(-42); + var fieldSpec = new FieldSpec(0, 5); + + var field = WordField.LoadFromFullWord(fieldSpec, word); + + field.Sign.Should().Be(Word.Signs.Negative); + } + + [Fact] + public void LoadFromFullWord_WithoutSign_ShouldHavePositiveSign() + { + var word = new FullWord(-42); + var fieldSpec = new FieldSpec(1, 5); + + var field = WordField.LoadFromFullWord(fieldSpec, word); + + field.Sign.Should().Be(Word.Signs.Positive); + } + + [Fact] + public void LoadFromFullWord_ShouldLoadCorrectBytes() + { + var word = new FullWord(); + word[0] = 10; + word[1] = 20; + word[2] = 30; + word[3] = 40; + word[4] = 50; + + var fieldSpec = new FieldSpec(2, 4); + var field = WordField.LoadFromFullWord(fieldSpec, word); + + ((Word)field).ByteCount.Should().Be(3); + field[0].ByteValue.Should().Be(20); + field[1].ByteValue.Should().Be(30); + field[2].ByteValue.Should().Be(40); + } + + [Fact] + public void LoadFromFullWord_WithSingleByte_ShouldWork() + { + var word = new FullWord(); + word[2] = 42; + + var fieldSpec = new FieldSpec(3, 3); + var field = WordField.LoadFromFullWord(fieldSpec, word); + + ((Word)field).ByteCount.Should().Be(1); + field[0].ByteValue.Should().Be(42); + } + + [Fact] + public void ApplyToFullWord_ShouldCopyBytesToWord() + { + var word = new FullWord(); + var fieldSpec = new FieldSpec(2, 4); + var field = WordField.LoadFromFullWord(fieldSpec, word); + + field[0] = 10; + field[1] = 20; + field[2] = 30; + + var targetWord = new FullWord(); + field.ApplyToFullWord(targetWord); + + targetWord[1].ByteValue.Should().Be(10); + targetWord[2].ByteValue.Should().Be(20); + targetWord[3].ByteValue.Should().Be(30); + } + + [Fact] + public void ApplyToFullWord_WithSignIncluded_ShouldCopySign() + { + var word = new FullWord(); + var fieldSpec = new FieldSpec(0, 3); + var field = WordField.LoadFromFullWord(fieldSpec, word); + field.Sign = Word.Signs.Negative; + + var targetWord = new FullWord(); + field.ApplyToFullWord(targetWord); + + targetWord.Sign.Should().Be(Word.Signs.Negative); + } + + [Fact] + public void ApplyToFullWord_WithoutSign_ShouldNotChangeSign() + { + var word = new FullWord(); + var fieldSpec = new FieldSpec(1, 3); + var field = WordField.LoadFromFullWord(fieldSpec, word); + field.Sign = Word.Signs.Negative; + + var targetWord = new FullWord(); + targetWord.Sign = Word.Signs.Positive; + field.ApplyToFullWord(targetWord); + + targetWord.Sign.Should().Be(Word.Signs.Positive); + } + + [Fact] + public void LoadFromRegister_WithIndexRegister_ShouldWork() + { + var register = new IndexRegister(); + register[0] = 10; + register[1] = 20; + + var fieldSpec = new FieldSpec(0, 2); + var field = WordField.LoadFromRegister(fieldSpec, register); + + ((Word)field).ByteCount.Should().Be(2); + field[0].ByteValue.Should().Be(10); + field[1].ByteValue.Should().Be(20); + } + + [Fact] + public void LoadFromRegister_WithSign_ShouldCopySign() + { + var register = new IndexRegister(); + register.Sign = Word.Signs.Negative; + + var fieldSpec = new FieldSpec(0, 2); + var field = WordField.LoadFromRegister(fieldSpec, register); + + field.Sign.Should().Be(Word.Signs.Negative); + } + + [Fact] + public void LoadFromRegister_WithoutSign_ShouldHavePositiveSign() + { + var register = new IndexRegister(); + register.Sign = Word.Signs.Negative; + + var fieldSpec = new FieldSpec(1, 2); + var field = WordField.LoadFromRegister(fieldSpec, register); + + field.Sign.Should().Be(Word.Signs.Positive); + } + + [Fact] + public void LoadFromRegister_WithPadding_ShouldHandleCorrectly() + { + var register = new IndexRegister(); + register[0] = 10; + register[1] = 20; + + var fieldSpec = new FieldSpec(0, 5); + var field = WordField.LoadFromRegister(fieldSpec, register); + + ((Word)field).ByteCount.Should().Be(5); + field[0].ByteValue.Should().Be(0); + field[1].ByteValue.Should().Be(0); + field[2].ByteValue.Should().Be(0); + field[3].ByteValue.Should().Be(10); + field[4].ByteValue.Should().Be(20); + } + + [Fact] + public void LoadFromRegister_WithLargerFieldThanRegister_ShouldThrowArgumentOutOfRangeException() + { + var register = new IndexRegister(); // ByteCountWithPadding = 5 + // We need a field that has ByteCount > 5, but such fields don't exist in valid FieldSpecs + // because the max is (0,6) which still has ByteCount of 5 + // This test documents that limitation + var fieldSpec = new FieldSpec(1, 5); // ByteCount = 5, fits exactly + + var field = WordField.LoadFromRegister(fieldSpec, register); + + field.Should().NotBeNull(); + } + + [Fact] + public void ApplyToRegister_ShouldCopyBytesToRegister() + { + var word = new FullWord(); + var fieldSpec = new FieldSpec(1, 2); + var field = WordField.LoadFromFullWord(fieldSpec, word); + field[0] = 10; + field[1] = 20; + + var register = new IndexRegister(); + field.ApplyToRegister(register); + + register[0].ByteValue.Should().Be(10); + register[1].ByteValue.Should().Be(20); + } + + [Fact] + public void ApplyToRegister_ShouldCopySign() + { + var word = new FullWord(); + var fieldSpec = new FieldSpec(0, 2); + var field = WordField.LoadFromFullWord(fieldSpec, word); + field.Sign = Word.Signs.Negative; + + var register = new IndexRegister(); + field.ApplyToRegister(register); + + register.Sign.Should().Be(Word.Signs.Negative); + } + + [Fact] + public void ApplyToRegister_WithPadding_ShouldPadWithZeros() + { + var word = new FullWord(); + var fieldSpec = new FieldSpec(1, 1); + var field = WordField.LoadFromFullWord(fieldSpec, word); + field[0] = 42; + + var register = new IndexRegister(); + register[0] = 55; + register[1] = 44; + + field.ApplyToRegister(register); + + register[0].ByteValue.Should().Be(0); + register[1].ByteValue.Should().Be(42); + } + + [Fact] + public void ApplyToRegister_WithLargerFieldThanRegister_DocumentsLimitation() + { + // The maximum valid field spec has ByteCount of 5 ((0,6) or (1,5)) + // which matches IndexRegister.ByteCountWithPadding of 5 + // So there's no valid FieldSpec that would throw this exception for IndexRegister + var word = new FullWord(); + var fieldSpec = new FieldSpec(1, 5); // ByteCount = 5 + var field = WordField.LoadFromFullWord(fieldSpec, word); + + var register = new IndexRegister(); + + // This should work without throwing + field.ApplyToRegister(register); + + register.Should().NotBeNull(); + } + + [Theory] + [InlineData(100, 100, 0)] + [InlineData(100, 50, 1)] + [InlineData(50, 100, -1)] + [InlineData(-100, -100, 0)] + [InlineData(-50, -100, 1)] + [InlineData(-100, -50, -1)] + public void CompareTo_ShouldCompareFieldValues(long value1, long value2, int expectedComparison) + { + var word1 = new FullWord(value1); + var word2 = new FullWord(value2); + var fieldSpec = new FieldSpec(0, 5); + + var field = WordField.LoadFromFullWord(fieldSpec, word1); + + var result = field.CompareTo(word2); + + result.Should().Be(expectedComparison); + } + + [Fact] + public void CompareTo_WithPartialField_ShouldCompareCorrectly() + { + var word1 = new FullWord(); + word1[2] = 10; + word1[3] = 20; + + var word2 = new FullWord(); + word2[2] = 10; + word2[3] = 30; + + var fieldSpec = new FieldSpec(3, 4); + var field = WordField.LoadFromFullWord(fieldSpec, word1); + + var result = field.CompareTo(word2); + + result.Should().BeLessThan(0); + } +} diff --git a/tests/MixLib.Tests/Type/WordTests.cs b/tests/MixLib.Tests/Type/WordTests.cs new file mode 100644 index 0000000..7285460 --- /dev/null +++ b/tests/MixLib.Tests/Type/WordTests.cs @@ -0,0 +1,282 @@ +using System.Collections.Generic; +using MixLib.Type; + +namespace MixLib.Tests.Type; + +public class WordTests +{ + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(5)] + public void Constructor_WithByteCount_ShouldCreateWordWithZeros(int byteCount) + { + var word = new Word(byteCount); + + word.ByteCount.Should().Be(byteCount); + word.Sign.Should().Be(Word.Signs.Positive); + word.LongValue.Should().Be(0); + } + + [Fact] + public void Constructor_WithBytesAndSign_ShouldSetValues() + { + var bytes = new MixByte[] { 10, 20, 30 }; + + var word = new Word(bytes, Word.Signs.Negative); + + word.ByteCount.Should().Be(3); + word.Sign.Should().Be(Word.Signs.Negative); + word[0].ByteValue.Should().Be(10); + word[1].ByteValue.Should().Be(20); + word[2].ByteValue.Should().Be(30); + } + + [Fact] + public void BytesToLong_WithPositiveSign_ShouldCalculateCorrectly() + { + var bytes = new MixByte[] { 0, 0, 1, 2, 3 }; + + var result = Word.BytesToLong(bytes); + + result.Should().Be((1 << 12) + (2 << 6) + 3); + } + + [Fact] + public void BytesToLong_WithSignParameter_ShouldApplySign() + { + var bytes = new MixByte[] { 0, 0, 1, 2, 3 }; + + var result = Word.BytesToLong(Word.Signs.Negative, bytes); + + result.Should().BeNegative(); + result.Should().Be(-((1 << 12) + (2 << 6) + 3)); + } + + [Fact] + public void Slice_ShouldReturnSubArray() + { + var word = new Word(5); + word[0] = 10; + word[1] = 20; + word[2] = 30; + word[3] = 40; + word[4] = 50; + + var slice = word.Slice(1, 3); + + slice.Length.Should().Be(3); + slice[0].ByteValue.Should().Be(20); + slice[1].ByteValue.Should().Be(30); + slice[2].ByteValue.Should().Be(40); + } + + [Fact] + public void InvertSign_ShouldToggleSign() + { + var word = new Word(3); + word.Sign = Word.Signs.Positive; + + word.InvertSign(); + + word.Sign.Should().Be(Word.Signs.Negative); + + word.InvertSign(); + + word.Sign.Should().Be(Word.Signs.Positive); + } + + [Fact] + public void ToString_WithAsCharsFalse_ShouldIncludeSignAndBytes() + { + var word = new Word(3); + word[0] = 1; + word[1] = 2; + word[2] = 3; + word.Sign = Word.Signs.Positive; + + var result = word.ToString(false); + + result.Should().StartWith("+"); + result.Should().Contain("1"); + result.Should().Contain("2"); + result.Should().Contain("3"); + } + + [Fact] + public void ToString_WithAsCharsTrue_ShouldReturnCharacterString() + { + var word = new Word(5); + word.Load("HELLO"); + + var result = word.ToString(true); + + result.Should().Be("HELLO"); + } + + [Fact] + public void Magnitude_Get_ShouldReturnByteArrayCopy() + { + var word = new Word(3); + word[0] = 10; + word[1] = 20; + word[2] = 30; + + var magnitude = word.Magnitude; + + magnitude.Length.Should().Be(3); + magnitude[0].ByteValue.Should().Be(10); + magnitude[1].ByteValue.Should().Be(20); + magnitude[2].ByteValue.Should().Be(30); + } + + [Fact] + public void Magnitude_Set_WithSmallerArray_ShouldPadWithZeros() + { + var word = new Word(5); + word.Magnitude = new MixByte[] { 10, 20 }; + + word[0].ByteValue.Should().Be(0); + word[1].ByteValue.Should().Be(0); + word[2].ByteValue.Should().Be(0); + word[3].ByteValue.Should().Be(10); + word[4].ByteValue.Should().Be(20); + } + + [Fact] + public void Magnitude_Set_WithLargerArray_ShouldTruncate() + { + var word = new Word(3); + word.Magnitude = new MixByte[] { 10, 20, 30, 40, 50 }; + + word.ByteCount.Should().Be(3); + word[0].ByteValue.Should().Be(30); + word[1].ByteValue.Should().Be(40); + word[2].ByteValue.Should().Be(50); + } + + [Fact] + public void MagnitudeLongValue_ShouldReturnAbsoluteValue() + { + var word = new Word(3); + word.Sign = Word.Signs.Negative; + word[0] = 1; + word[1] = 2; + word[2] = 3; + + var magnitude = word.MagnitudeLongValue; + + magnitude.Should().BeGreaterThan(0); + magnitude.Should().Be((1 << 12) + (2 << 6) + 3); + } + + [Fact] + public void MagnitudeLongValue_Set_ShouldNotChangeSign() + { + var word = new Word(3); + word.Sign = Word.Signs.Negative; + + word.MagnitudeLongValue = 42; + + word.Sign.Should().Be(Word.Signs.Negative); + word.MagnitudeLongValue.Should().Be(42); + } + + [Fact] + public void ToArray_ShouldReturnMagnitude() + { + var word = new Word(3); + word[0] = 10; + word[1] = 20; + word[2] = 30; + + var array = word.ToArray(); + + array.Should().BeEquivalentTo(word.Magnitude); + } + + [Fact] + public void IsEmpty_WithZeroAndPositive_ShouldBeTrue() + { + var word = new Word(3); + + word.IsEmpty.Should().BeTrue(); + } + + [Fact] + public void IsEmpty_WithZeroAndNegative_ShouldBeFalse() + { + var word = new Word(3); + word.Sign = Word.Signs.Negative; + + word.IsEmpty.Should().BeFalse(); + } + + [Fact] + public void IsEmpty_WithNonZero_ShouldBeFalse() + { + var word = new Word(3); + word[0] = 1; + + word.IsEmpty.Should().BeFalse(); + } + + [Fact] + public void Count_ShouldReturnByteCount() + { + var word = new Word(7); + + word.Count.Should().Be(7); + } + + [Fact] + public void MaxByteCount_ShouldReturnByteCount() + { + var word = new Word(7); + + word.MaxByteCount.Should().Be(7); + } + + [Fact] + public void BitCount_ShouldBeByteCountTimesSix() + { + var word = new Word(5); + + word.BitCount.Should().Be(30); + } + + [Fact] + public void MaxMagnitude_ShouldBeCorrect() + { + var word = new Word(2); + + word.MaxMagnitude.Should().Be((1 << 12) - 1); + } + + [Fact] + public void Enumeration_ShouldIterateBytes() + { + var word = new Word(3); + word[0] = 10; + word[1] = 20; + word[2] = 30; + + var list = new List(); + foreach (MixByte b in word) + { + list.Add(b.ByteValue); + } + + list.Should().Equal(10, 20, 30); + } + + [Fact] + public void Sign_SetAndGet_ShouldWork() + { + var word = new Word(3); + + word.Sign = Word.Signs.Negative; + + word.Sign.Should().Be(Word.Signs.Negative); + } +} diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..4a748dc --- /dev/null +++ b/tests/README.md @@ -0,0 +1,95 @@ +# MixEmul Test Suite + +This directory contains the unit test projects for the MixEmul solution. + +## Test Projects + +### MixLib.Tests +Tests for the core MixLib library, including: + +- **Type Tests** (`Type/`) + - `MixByteTests.cs` - Tests for the 6-bit MixByte type (constructors, operators, conversions) + - `FullWordTests.cs` - Tests for 5-byte FullWord type (value operations, sign handling, cloning) + - `WordTests.cs` - Tests for Word base class (static methods, slicing, magnitude operations) + - `FieldSpecTests.cs` - Tests for field specifications (bounds validation, byte indexing) + - `ExtensionMethodsTests.cs` - Tests for extension methods (sign operations, comparison values) + - `MixByteCollectionTests.cs` - Tests for MixByte collections (loading, cloning, enumeration) + - `WordFieldTests.cs` - Tests for word field operations (loading, applying, comparing) + - `IndexRegisterTests.cs` - Tests for 2-byte index registers with padding + - `AddressRegisterTests.cs` - Tests for 2-byte address registers + - `FullWordRegisterTests.cs` - Tests for 5-byte registers + +- **Core Component Tests** + - `MemoryTests.cs` - Tests for Memory class (indexing, bounds checking, storage) + - `RegistersTests.cs` - Tests for Registers collection (initialization, indexing, addressing) + +- **Instruction Tests** (`Instruction/`) + - `InstructionHelpersTests.cs` - Tests for instruction utility methods + +- **Misc Tests** (`Misc/`) + - `ValidationErrorTests.cs` - Tests for validation error messages and formatting + +**Total: 325 tests** + +### MixAssembler.Tests +Tests for the MixAssembler library, including: + +- **Finding Tests** (`Finding/`) + - `AssemblyFindingTests.cs` - Tests for assembly errors and warnings (severity, location tracking, message formatting) + +- **Value Tests** (`Value/`) + - `NumberValueTests.cs` - Tests for numeric literal parsing and value operations + +**Total: 46 tests** + +## Running Tests + +### Run all tests in the solution: +```powershell +dotnet test MixEmul.sln +``` + +### Run tests for a specific project: +```powershell +dotnet test tests\MixLib.Tests\MixLib.Tests.csproj +dotnet test tests\MixAssembler.Tests\MixAssembler.Tests.csproj +``` + +### Run tests with detailed output: +```powershell +dotnet test --verbosity detailed +``` + +### Run tests with coverage: +```powershell +dotnet test --collect:"XPlat Code Coverage" +``` + +## Test Framework + +- **xUnit 2.9.2** - Modern, extensible test framework +- **FluentAssertions 7.0.0** - Readable assertion library +- **Target Framework**: .NET 10 (net10.0-windows7.0) + +## Test Philosophy + +These tests are designed to: +1. **Validate intent**, not just implementation - Tests verify proper behavior, which may help identify bugs in production code +2. **Follow best practices** - Using modern patterns like Theory/InlineData for parameterized tests +3. **Be maintainable** - Clear naming, logical organization, and comprehensive coverage of core functionality +4. **Be fast** - Unit tests run in milliseconds, suitable for frequent execution during development + +## Coverage Focus + +The current test suite focuses on: +- Core type system (MixByte, Word, FullWord, FieldSpec) +- Type conversions and operators +- Validation and error reporting +- Extension methods for type manipulation + +Future test additions could cover: +- Instruction execution +- Device I/O operations +- Memory and register operations +- Assembly parsing +- Mix computer simulation