Skip to content

feature: protected mode support - #2311

Open
maximilien-noal wants to merge 12 commits into
masterfrom
feature/protected_mode
Open

feature: protected mode support#2311
maximilien-noal wants to merge 12 commits into
masterfrom
feature/protected_mode

Conversation

@maximilien-noal

Copy link
Copy Markdown
Member

Description of Changes

Full protected mode support.

Rationale behind Changes

An insane experiment.

Suggested Testing Steps

Not ready yet.

Copilot AI lite review requested due to automatic review settings August 14, 2026 11:51
@maximilien-noal maximilien-noal self-assigned this Aug 14, 2026
@maximilien-noal maximilien-noal added 32 bit Related to 32 bit programs support low priority Fixing this is not urgent, or would take too much manpower for too little gain compatibility Emulator compatibility with DOS apps CPU Related to the CPU memory Emulator memory access mode and layout hardware hardware device (can be virtual) emulation DOS Related to DOS labels Aug 14, 2026
@maximilien-noal maximilien-noal added XMS Related to XMS memory EMS Related to EMS memory labels Aug 14, 2026
@maximilien-noal
maximilien-noal marked this pull request as draft August 14, 2026 11:53
@maximilien-noal maximilien-noal changed the title feature: protected mode support, MMU enhancements, and unified extend… feature: protected mode support Aug 14, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces protected-mode (80386-class) execution support and related memory-system upgrades (descriptor caches, privilege checks, paging, and configurable RAM sizing), and extends the test suite with new fixtures and unit tests to validate those behaviors.

Changes:

  • Adds protected-mode segmentation primitives (descriptor table decoding, segment descriptor caches, CPL/IOPL checks, call gates, task switching, V86 mode scaffolding).
  • Introduces a paging translation layer (CR3-rooted 2-level walk) and expands MMU APIs to carry read/write intent and translate linear addresses.
  • Expands configuration and tests to support larger RAM sizes and validate protected-mode/paging behavior and generated-code paths.

Reviewed changes

Copilot reviewed 89 out of 102 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/Spice86.Tests/Spice86Creator.cs Allows tests to configure CPU model, heavy CPU logging, and RAM size.
tests/Spice86.Tests/Resources/cpuTests/protectedmode_unified_pool.asm Adds a protected-mode fixture that writes to high physical memory.
tests/Spice86.Tests/Resources/cpuTests/asmsrc/test386.asm/src/test386.asm Adds a build-time option to skip unverified diagnostic-print tests.
tests/Spice86.Tests/Resources/cpuTests/asmsrc/test386.asm/src/configuration.asm Introduces SKIP_UNVERIFIED_TESTS configuration knob for test386.
tests/Spice86.Tests/RealModeMmuTest.cs Updates MMU tests for new isWrite signatures.
tests/Spice86.Tests/PrivilegeChecksTests.cs Adds unit tests for CPL/IOPL and data/stack segment-load privilege rules.
tests/Spice86.Tests/PagingUnitTests.cs Adds unit tests for paging translation and error-code semantics.
tests/Spice86.Tests/McpServerToolStateTests.cs Makes MCP disassembly boundary behavior deterministic via RAM sizing.
tests/Spice86.Tests/McpIntegrationContext.cs Plumbs RAM size through MCP integration context creation.
tests/Spice86.Tests/LinearAddressOverrideTest.cs Adds a test for linear-address keyed override lookup across descriptor edits.
tests/Spice86.Tests/GeneratedCodeRunOptions.cs Adds CPU model selection to generated-code test options.
tests/Spice86.Tests/GeneratedCodeMachineTestRunner.cs Passes CPU model through to Spice86Creator in generated-code runs.
tests/Spice86.Tests/GeneratedCodeMachineTest.cs Adds multiple generated-code protected-mode coverage tests.
tests/Spice86.Tests/CfgCodeAddressTest.cs Adds equality/ordering tests for CfgCodeAddress.
src/Spice86/Spice86DependencyInjection.cs Allocates RAM based on Configuration.RamSizeKb and uses new MMU factory.
src/Spice86.Shared/Emulator/Memory/CfgCodeAddress.cs Introduces an address type that can represent segmented or flat-linear code addresses.
src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/CSharpAstEmitter.cs Enhances control-transfer lowering for far jumps/calls and interrupt returns.
src/Spice86.Core/Emulator/ReverseEngineer/CfgCodeGeneration/CpuFaultWrapper.cs Changes fault-target resolution to use ResolveCpuFaultTarget(...).
src/Spice86.Core/Emulator/Memory/Mmu/RealModeMmu8086.cs Updates IMMU API and adds linear-translation no-op method.
src/Spice86.Core/Emulator/Memory/Mmu/RealModeMmu386.cs Updates IMMU API and adds linear-translation no-op method.
src/Spice86.Core/Emulator/Memory/Mmu/ProtectedModeMmu386.cs Adds protected-mode segment translation using cached descriptors.
src/Spice86.Core/Emulator/Memory/Mmu/PagingUnit.cs Implements 80386 2-level paging walk with A/D bit updates and faults.
src/Spice86.Core/Emulator/Memory/Mmu/PagingMmu.cs Wraps an MMU to apply paging translation when enabled.
src/Spice86.Core/Emulator/Memory/Mmu/IMmu.cs Extends MMU contract with isWrite and linear-address translation.
src/Spice86.Core/Emulator/Memory/Mmu/CpuMmuFactory.cs Creates the composed MMU pipeline for a CPU model (real/protected + paging).
src/Spice86.Core/Emulator/Memory/Mmu/CpuMmu.cs Dispatches segment translation based on CPU mode (special-casing CS).
src/Spice86.Core/Emulator/Memory/Memory.cs Updates segmented writes to pass isWrite: true through MMU translation.
src/Spice86.Core/Emulator/Memory/Indexer/UInt8Indexer.cs Threads isWrite through segmented reads/writes.
src/Spice86.Core/Emulator/Memory/Indexer/UInt32Indexer.cs Threads isWrite through segmented reads/writes.
src/Spice86.Core/Emulator/Memory/Indexer/UInt16Indexer.cs Threads isWrite through segmented reads/writes.
src/Spice86.Core/Emulator/Memory/Indexer/UInt16BigEndianIndexer.cs Threads isWrite through segmented reads/writes.
src/Spice86.Core/Emulator/Memory/Indexer/SegmentedAddress32Indexer.cs Updates access checks for new CheckAccess(..., isWrite) signature.
src/Spice86.Core/Emulator/Memory/Indexer/MemoryIndexer.cs Updates access checks to pass read vs write intent.
src/Spice86.Core/Emulator/Memory/A20Gate.cs Corrects A20 masking behavior to gate only bit 20 and allow full address space when enabled.
src/Spice86.Core/Emulator/LoadableFile/ExecutableFileLoader.cs Keeps CS descriptor cache in sync when loaders set CS:IP directly.
src/Spice86.Core/Emulator/CPU/State.cs Adds EIP, descriptor caches, control regs, table regs, CPL/IOPL, and CPU-mode derivation.
src/Spice86.Core/Emulator/CPU/Registers/SystemSegmentRegister.cs Adds a combined selector+cache holder for LDTR/TR.
src/Spice86.Core/Emulator/CPU/Registers/SegmentSelector.cs Adds selector decoding helpers (TI/RPL/index/error-code masking).
src/Spice86.Core/Emulator/CPU/Registers/SegmentDescriptorCaches.cs Adds storage for hidden descriptor caches for segment registers.
src/Spice86.Core/Emulator/CPU/Registers/SegmentDescriptorCache.cs Adds a cached segment descriptor representation and real-mode synthesis.
src/Spice86.Core/Emulator/CPU/Registers/DescriptorTableRegister.cs Adds GDTR/IDTR representation.
src/Spice86.Core/Emulator/CPU/Registers/ControlRegisters.cs Adds CR0/CR2/CR3/CR4 storage with key bit helpers.
src/Spice86.Core/Emulator/CPU/Flags.cs Stops forcing off IOPL/NT on 386 and adds masks/helpers for VM/IOPL/NT bits.
src/Spice86.Core/Emulator/CPU/Exceptions/CpuStackSegmentFaultException.cs Adjusts #SS default error-code behavior.
src/Spice86.Core/Emulator/CPU/Exceptions/CpuSegmentNotPresentException.cs Adds #NP exception type.
src/Spice86.Core/Emulator/CPU/Exceptions/CpuPageFaultException.cs Adds #PF exception type.
src/Spice86.Core/Emulator/CPU/Exceptions/CpuGeneralProtectionFaultException.cs Adjusts #GP default error-code behavior.
src/Spice86.Core/Emulator/CPU/DescriptorTables/TaskSwitchOperations.cs Implements CALL/IRET-driven nested hardware task switching.
src/Spice86.Core/Emulator/CPU/DescriptorTables/TaskStateSegment.cs Implements TSS field layout and save/load routines.
src/Spice86.Core/Emulator/CPU/DescriptorTables/SegmentAndControlRegisterOperations.cs Centralizes segment loads and GDTR/IDTR/control-reg operations for both execution paths.
src/Spice86.Core/Emulator/CPU/DescriptorTables/RawSegmentDescriptor.cs Implements raw 8-byte segment descriptor decoding.
src/Spice86.Core/Emulator/CPU/DescriptorTables/RawGateDescriptor.cs Implements raw 8-byte gate descriptor decoding.
src/Spice86.Core/Emulator/CPU/DescriptorTables/ProtectedModeCallGateDispatcher.cs Implements protected-mode call gate dispatch for CALL/JMP.
src/Spice86.Core/Emulator/CPU/DescriptorTables/PrivilegeChecks.cs Adds shared privilege checks (IOPL/CPL0, segment-load checks, far transfer checks).
src/Spice86.Core/Emulator/CPU/DescriptorTables/GateType.cs Defines gate type encodings for protected-mode gates.
src/Spice86.Core/Emulator/CPU/DescriptorTables/DescriptorTableReader.cs Centralizes descriptor-table lookup and bounds logic.
src/Spice86.Core/Emulator/CPU/CpuMode.cs Adds CPU mode enum (Real/Protected/V86).
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/XlatParser.cs Fixes address-size selection for XLAT parsing.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/XchgRmParser.cs Fixes address-size selection for XCHG r/m parsing.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/SystemSegmentParser.cs Adds parsing for SLDT/STR/LLDT/LTR/VERR/VERW (0F 00 group).
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/SimpleInstructionParser.cs Hooks CLTS execution helper call into AST.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/SegRegPushPopParser.cs Routes POP Sreg through segment-load helper for cache/privilege correctness.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/PushaParser.cs Fixes stack-pointer width handling for PUSHA.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/MovSregRm16Parser.cs Routes MOV Sreg, r/m16 through segment-load helper.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/MemoryStringOpParser.cs Fixes address-size selection for string ops parsing.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/LxsParser.cs Routes LxS segment load through segment-load helper and fixes address-size selection.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/LarLslParser.cs Adds parsing for LAR/LSL.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/IoStringParser.cs Adds IOPL gating for INS/OUTS and fixes address-size selection.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/FlagControlParser.cs Adds IOPL gating for CLI/STI in CFG execution path.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/EnterParser.cs Moves ENTER semantics to a runtime stack helper to respect SS D/B behavior.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/DescriptorTableParser.cs Adds parsing for LGDT/SGDT/LIDT/SIDT/SMSW/LMSW (0F 01 group).
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/ControlRegisterParser.cs Adds parsing for MOV to/from CR0/2/3/4.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/BoundParser.cs Fixes address-size selection for BOUND parsing.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/BaseInstructionParser.cs Adds IOPL gating to IN/OUT AST emission and refactors immediate reads.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/ArplParser.cs Adds parsing for ARPL with correct conditional write-back behavior.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/ParsingContext.cs Makes operand/address size depend on CS D/B default with 0x66/0x67 toggling.
src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/InstructionParser.cs Wires in new instruction parsers and CS D/B-aware parsing context creation.
src/Spice86.Core/Emulator/CPU/CfgCpu/InstructionExecutor/Expressions/AstExpressionBuilder.cs Updates expression generation for new MMU signatures and helper calls.
src/Spice86.Core/Emulator/CPU/CfgCpu/Ast/Instruction/InstructionOperation.cs Adds enum values for new instruction operations (ARPL/LAR/LSL/etc.).
src/Spice86.Core/Emulator/CPU/CfgCpu/Ast/Builder/StackAstBuilder.cs Consolidates LEAVE emission into a single runtime helper call.
src/Spice86.Core/Emulator/CPU/CfgCpu/Ast/Builder/AstBuilder.cs Updates address-type selection API.
src/Spice86.Core/CLI/Configuration.cs Adds configurable RAM size option and defaults/constants.
Suppressed comments (1)

tests/Spice86.Tests/LinearAddressOverrideTest.cs:61

  • Null-forgiving operator (!) is used here even though nullability is already asserted. ! is banned in this repo; invoke the delegate via an explicit null check/throw instead.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tests/Spice86.Tests/LinearAddressOverrideTest.cs Outdated
Comment thread src/Spice86/Spice86DependencyInjection.cs
@maximilien-noal
maximilien-noal marked this pull request as ready for review August 14, 2026 12:00
@maximilien-noal
maximilien-noal marked this pull request as draft August 14, 2026 12:00
Comment thread src/Spice86.Core/Emulator/Memory/Mmu/ProtectedModeMmu386.cs Dismissed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 89 out of 102 changed files in this pull request and generated 3 comments.

Suppressed comments (16)

src/Spice86.Core/Emulator/CPU/DescriptorTables/ProtectedModeCallGateDispatcher.cs:137

  • The JMP-through-call-gate path has the same exception-vector error as the CALL path: a non-present gate must raise #NP with the gate selector, not #GP.
    src/Spice86.Core/Emulator/CPU/DescriptorTables/TaskSwitchOperations.cs:111
  • Returning through a task backlink is also a hardware task switch and must set CR0.TS. If the nested task executed CLTS, the caller currently resumes with TS still clear.
    src/Spice86.Core/Emulator/Memory/Mmu/ProtectedModeMmu386.cs:52
  • Writes through a code-segment selector are currently allowed because this condition only rejects non-writable data segments. Code segments are never writable, even when their readable bit is set, so a protected-mode store through CS (or another register loaded with readable code) will incorrectly modify memory instead of raising #GP.
    src/Spice86.Core/Emulator/Memory/Mmu/ProtectedModeMmu386.cs:82
  • Resolving a hidden descriptor cache from the selector value is ambiguous. Multiple segment registers may legally contain the same selector while retaining different cached descriptors after the GDT entry is edited and only one register is reloaded; this loop then uses whichever register appears first, so DS/SS accesses can use CS/ES's stale base or limit. The MMU API needs the originating segment-register identity (or cache) rather than only the raw selector.
    src/Spice86.Core/Emulator/CPU/State.cs:221
  • IP is not a writable subregister of EIP like AX is of EAX. A 16-bit control transfer loads a 16-bit instruction pointer and clears the upper EIP bits; preserving them lets stale high bits reappear when execution later enters a 32-bit code segment.
    src/Spice86.Core/Emulator/CPU/CfgCpu/Parser/SpecificParsers/ControlRegisterParser.cs:36
  • CR4 does not exist on the configured INTEL_80386 model, so accepting register number 4 makes MOV CR4 execute instead of raising #UD. Restrict this parser to CR0, CR2, and CR3 for the only CPU model on which these opcodes are enabled.
        if (crNumber is not (0 or 2 or 3 or 4)) {

src/Spice86.Core/Emulator/CPU/DescriptorTables/RawGateDescriptor.cs:26

  • The gate decoder discards byte 4, which contains the call-gate parameter count. On an inter-privilege call, the CPU must copy that many parameters from the old stack to the new ring stack; without retaining this field, every nonzero-count call gate produces an incomplete stack frame.
    src/Spice86.Core/Emulator/CPU/DescriptorTables/ProtectedModeCallGateDispatcher.cs:71
  • A non-present call gate raises #NP(selector), not #GP. Returning the wrong vector changes which handler runs and the saved exception frame.

This issue also appears on line 136 of the same file.
src/Spice86.Core/CLI/Configuration.cs:43

  • RamSizeKb is documented as capped at 64 MB, but no validation enforces either that cap or a positive minimum. The composition root immediately casts the value to uint, so a negative CLI value becomes a huge allocation and values above the stated maximum are also accepted. Validate the range before the cast/allocation and report a command-line error.
    [CommandOption("--RamSizeKb <RAMSIZEKB>")]
    [DefaultValue(RamSizeDefaultKb)]
    public int RamSizeKb { get; init; } = RamSizeDefaultKb;

tests/Spice86.Tests/Resources/cpuTests/protectedmode_unified_pool.asm:1

  • This new MachineTest fixture has no matching GeneratedCodeMachineTest entry. Repository test policy requires every ASM MachineTest scenario to run through the generated-code path too; add a protectedmode_unified_pool generated-code case with the same 80386 model and high-memory assertion.
    tests/Spice86.Tests/LinearAddressOverrideTest.cs:62
  • The null-forgiving operator is prohibited by this repository's coding rules. The preceding FluentAssertions check already throws on null, so use a null-conditional invocation rather than suppressing nullable analysis.
    tests/Spice86.Tests/LinearAddressOverrideTest.cs:77
  • This adds a second top-level type to the file, contrary to the repository's one-top-level-type-per-file convention. Move LinearOverrideProbe to its own file or make it a private nested type of LinearAddressOverrideTest.
    tests/Spice86.Tests/Resources/cpuTests/asmsrc/test386.asm/src/configuration.asm:46
  • The new skip branch is disabled in the checked-in source, and there is no build override for this symbol. Rebuilding with the documented NASM source therefore still runs the unverified tail, while Test386ProtectedMode describes its fixture as SKIP_UNVERIFIED_TESTS=1. Make the protected-mode fixture's build configuration reproducibly define this as 1.
    src/Spice86.Core/Emulator/CPU/DescriptorTables/TaskSwitchOperations.cs:81
  • Every hardware task switch must set CR0.TS. This forward switch updates TR and NT but leaves TaskSwitched unchanged, so code in the new task will not receive the expected device-not-available behavior after a prior CLTS.

This issue also appears on line 110 of the same file.
src/Spice86.Core/Emulator/Memory/Mmu/ProtectedModeMmu386.cs:55

  • This limit check treats every data segment as expand-up. For an expand-down data/stack descriptor (type bit 2 set), valid offsets are above the encoded limit up to 0xFFFF or 0xFFFFFFFF according to D/B; the current logic rejects valid accesses and permits invalid low addresses. Decode the expand-down attribute and apply its inverted limit rule.
    src/Spice86.Core/Emulator/CPU/DescriptorTables/SegmentAndControlRegisterOperations.cs:173
  • LAR currently reports every present descriptor as valid without applying the required visibility check against max(CPL, RPL) and DPL. Ring-3 code can therefore inspect descriptors that should leave ZF clear and preserve the destination. Apply the descriptor-type and privilege rules before returning true.

Comment thread src/Spice86.Core/Emulator/CPU/Stack.cs Dismissed
@maximilien-noal maximilien-noal removed EMS Related to EMS memory DOS Related to DOS labels Aug 18, 2026
@maximilien-noal
maximilien-noal marked this pull request as ready for review August 18, 2026 13:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

32 bit Related to 32 bit programs support compatibility Emulator compatibility with DOS apps CPU Related to the CPU hardware hardware device (can be virtual) emulation low priority Fixing this is not urgent, or would take too much manpower for too little gain memory Emulator memory access mode and layout XMS Related to XMS memory

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants