Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
7057608
fix(vga): ignore INT10H 0xFE probe from Tandy/PCjr programs
maximilien-noal Apr 17, 2026
1f587fc
feat: add X86InstructionBuilder and extend MemoryAsmWriter
maximilien-noal Apr 17, 2026
518199d
feat: DosPathResolver - executable lookup, new file resolution, 8.3 n…
maximilien-noal Apr 17, 2026
884c8ca
feat: drive abstraction - memory drives, virtual drive devices
maximilien-noal Apr 17, 2026
95c0c96
feat: DosFileManager - IOCTL fixes, file operations, search improvements
maximilien-noal Apr 17, 2026
eb1a2bb
fix(dos): console device off-by-one in data-available check
maximilien-noal Apr 17, 2026
fd9b339
feat(keyboard): enhanced INT16H (AH=10h/11h) + Ctrl-Break enqueue + s…
maximilien-noal Apr 17, 2026
c1e41f3
feat: batch execution engine + types + DosProcessManager integration …
maximilien-noal Apr 17, 2026
87bf0a4
feat: DosInt21Handler - WriteAssemblyInRam rewrite + new functions
maximilien-noal Apr 17, 2026
e5c61ed
feat: HeadlessGui keyboard simulation + test infrastructure rewrite
maximilien-noal Apr 17, 2026
580db5e
test: comprehensive integration tests for batch, INT 21h, IOCTL, keyb…
maximilien-noal Apr 17, 2026
613e077
chore: update copilot instructions
maximilien-noal Apr 17, 2026
cf47f1f
refactor: remove X86InstructionBuilder, use MemoryAsmWriter with Byte…
maximilien-noal Apr 17, 2026
e4bb035
chore: Removed dead IOCTL abstractions
maximilien-noal Apr 17, 2026
c80b535
refactor: IOCTL magic bytes to enums
maximilien-noal Apr 17, 2026
30fee84
refactor: split DosBatchEngine into partials
maximilien-noal Apr 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ Variants: `MemoryBasedDataStructureWithCsBaseAddress`, `MemoryBasedDataStructure
- **Rebuild and verify** - For any task that changes code or tests, rebuild the project and run the full test suite; do not stop until all tests are green.
- **Concise documentation** - XML docs should be precise and complete but not verbose; avoid excessive remarks
- **Ignore Machine class** - this is a legacy aggregator class; work directly with specific components (`CfgCpu`, `Memory`, `Stack`, etc.) instead
- **Enforce TDD** - ensure integration tests are present and not passing at first, then make them pass by updating the implementation.
- **Clear test code style** use explicit Arrange/Act/Assert structure, avoid long setup blocks, and reduce duplication with small helpers.

### Avalonia Telemetry
- **Avalonia telemetry must be disabled** when working on the codebase.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Spice86.Core.Emulator.InterruptHandlers.Common.MemoryWriter;
/// Writes x86 ASM instructions to Memory bus
/// </summary>
public class MemoryAsmWriter : MemoryWriter {
private readonly CallbackHandler _callbackHandler;
private readonly CallbackHandler? _callbackHandler;

/// <summary>
/// Initializes a new instance of the <see cref="MemoryAsmWriter"/> class with the specified memory as a data sink, beginningAddress and callbackHandler to register callbacks.
Expand All @@ -21,6 +21,14 @@ public MemoryAsmWriter(IIndexable memory, SegmentedAddress beginningAddress, Cal
_callbackHandler = callbackHandler;
}

/// <summary>
/// Initializes a new instance for writing x86 instructions to a byte array without callback support.
/// </summary>
/// <param name="memory">Byte-array-backed memory to write instructions into.</param>
/// <param name="beginningAddress">Where to start writing (typically 0:0x100 for COM images).</param>
public MemoryAsmWriter(IIndexable memory, SegmentedAddress beginningAddress) : base(memory, beginningAddress) {
}

/// <summary>
/// Registers a new callback that will call the given runnable with the manually defined callback number.<br/>
/// </summary>
Expand All @@ -29,18 +37,24 @@ public MemoryAsmWriter(IIndexable memory, SegmentedAddress beginningAddress, Cal
public void RegisterAndWriteCallback(byte callbackNumber, Action runnable) {
RegisterAndWriteCallback((ushort)callbackNumber, runnable);
}

/// <summary>
/// Registers a new callback that will call the given runnable.<br/>
/// Callback number is automatically allocated.
/// </summary>
/// <param name="runnable"></param>
public void RegisterAndWriteCallback(Action runnable) {
if (_callbackHandler is null) {
throw new UnrecoverableException("Cannot register callbacks without a CallbackHandler.");
}
ushort callbackNumber = _callbackHandler.AllocateNextCallback();
RegisterAndWriteCallback(callbackNumber, runnable);
}

private void RegisterAndWriteCallback(ushort callbackNumber, Action runnable) {
if (_callbackHandler is null) {
throw new UnrecoverableException("Cannot register callbacks without a CallbackHandler.");
}
Callback callback = new Callback(callbackNumber, runnable, CurrentAddress);
_callbackHandler.AddCallback(callback);
WriteCallback(callback.Index);
Expand Down Expand Up @@ -111,6 +125,13 @@ public void WriteNop() {
WriteUInt8(0x90);
}

/// <summary>
/// Writes a STI instruction to memory. Re-enables hardware interrupts (IF=1).
/// </summary>
public void WriteSti() {
WriteUInt8(0xFB);
}

/// <summary>
/// Writes a far CALL instruction to the given inMemoryAddressSwitcher default address. <br/>
/// Throws UnrecoverableException if DefaultAddressValue is not initialized. <br/>
Expand Down Expand Up @@ -143,4 +164,99 @@ public SegmentedAddress WriteFarCall(SegmentedAddress destination) {
public void WriteFarRet() {
WriteUInt8(0xCB);
}

/// <summary>
/// Writes MOV AH, imm8 instruction to memory.
/// </summary>
/// <param name="value">Value to move into AH.</param>
public void WriteMovAh(byte value) {
WriteUInt8(0xB4);
WriteUInt8(value);
}

/// <summary>
/// Writes MOV AX, imm16 instruction to memory.
/// </summary>
/// <param name="value">Value to move into AX.</param>
public void WriteMovAx(ushort value) {
WriteUInt8(0xB8);
WriteUInt16(value);
}

/// <summary>
/// Writes MOV DX, imm16 instruction to memory.
/// </summary>
/// <param name="value">Value to move into DX.</param>
public void WriteMovDx(ushort value) {
WriteUInt8(0xBA);
WriteUInt16(value);
}

/// <summary>
/// Writes CMP AL, imm8 instruction to memory.
/// </summary>
/// <param name="value">Value to compare with AL.</param>
public void WriteCmpAl(byte value) {
WriteUInt8(0x3C);
WriteUInt8(value);
}

/// <summary>
/// Writes CMP AH, imm8 instruction to memory.
/// </summary>
/// <param name="value">Value to compare with AH.</param>
public void WriteCmpAh(byte value) {
WriteUInt8(0x80);
WriteUInt8(0xFC);
WriteUInt8(value);
}

/// <summary>
/// Writes JA (Jump if Above) instruction to memory.
/// </summary>
/// <param name="offset">Signed offset for the jump.</param>
public void WriteJa(sbyte offset) {
WriteUInt8(0x77);
WriteInt8(offset);
}

/// <summary>
/// Writes JB (Jump if Below) instruction to memory.
/// </summary>
/// <param name="offset">Signed offset for the jump.</param>
public void WriteJb(sbyte offset) {
WriteUInt8(0x72);
WriteInt8(offset);
}

/// <summary>
/// Writes SUB AL, imm8 instruction to memory.
/// </summary>
/// <param name="value">Value to subtract from AL.</param>
public void WriteSubAl(byte value) {
WriteUInt8(0x2C);
WriteUInt8(value);
}

/// <summary>
/// Writes MOV AX, imm16 instruction with AH and AL bytes in sequence.
/// Used for setting both AL (exit code) and AH (0x4C for INT 21h terminate).
/// </summary>
/// <param name="alValue">Value for AL (exit code).</param>
/// <param name="ahValue">Value for AH (typically 0x4C for terminate function).</param>
public void WriteMovAxSplit(byte alValue, byte ahValue) {
WriteUInt8(0xB8);
WriteUInt8(alValue);
WriteUInt8(ahValue);
}

/// <summary>
/// Writes a range of raw bytes to memory.
/// </summary>
/// <param name="bytes">The bytes to write sequentially.</param>
public void WriteBytes(ReadOnlySpan<byte> bytes) {
foreach (byte b in bytes) {
WriteUInt8(b);
}
}
}
Loading
Loading