Skip to content

Commit

Permalink
more documentation
Browse files Browse the repository at this point in the history
Signed-off-by: Maximilien Noal <noal.maximilien@gmail.com>
  • Loading branch information
maximilien-noal committed Jan 15, 2024
1 parent e987aa5 commit 60b43a3
Show file tree
Hide file tree
Showing 4 changed files with 284 additions and 21 deletions.
19 changes: 17 additions & 2 deletions src/Spice86.Core/Emulator/CPU/Alu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public abstract class Alu<TUnsigned, TSigned, TUnsignedUpper, TSignedUpper>
/// <summary>
/// Initializes a new instance.
/// </summary>
/// <param name="state">The state of the CPU.</param>
/// <param name="state">The CPU registers and flags.</param>
public Alu(State state) {
_state = state;
}
Expand Down Expand Up @@ -91,6 +91,10 @@ public TUnsigned Dec(TUnsigned value1) {

public abstract TUnsigned And(TUnsigned value1, TUnsigned value2);
public abstract TUnsigned Or(TUnsigned value1, TUnsigned value2);

/// <summary>
/// The XOR operation, also known as the exclusive OR operation, compares two values and returns 1 if they are equal and 0 if they are not equal.
/// </summary>
public abstract TUnsigned Xor(TUnsigned value1, TUnsigned value2);

public abstract TUnsigned Rcl(TUnsigned value, byte count);
Expand Down Expand Up @@ -139,17 +143,28 @@ protected void SetCarryFlagForRightShifts(uint value, int count) {
/// <summary>
/// Sets the parity flag by looking at the lowest byte of the value
/// </summary>
/// <param name="value"></param>
/// <param name="value">The ulong value we take the lowest byte from</param>
protected void SetParityFlag(ulong value) {
_state.ParityFlag = IsParity((byte)value);
}

/// <summary>
/// Sets the zero flag by checking if the value is zero
/// </summary>
/// <param name="value">The value to check</param>
protected void SetZeroFlag(ulong value) {
_state.ZeroFlag = value == 0;
}

/// <summary>
/// Sets the value of the sign flag.
/// </summary>
protected abstract void SetSignFlag(TUnsigned value);

/// <summary>
/// Sets the zero, parity and sign flags.
/// </summary>
/// <param name="value">The value to assign to the flag</param>
public void UpdateFlags(TUnsigned value) {
SetZeroFlag(ulong.CreateTruncating(value));
SetParityFlag(ulong.CreateTruncating(value));
Expand Down
36 changes: 34 additions & 2 deletions src/Spice86.Core/Emulator/CPU/InstructionsImpl/Instructions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,21 @@ public abstract class Instructions {
protected readonly Memory.IMemory Memory;
protected readonly ModRM ModRM;

/// <summary>
/// Gets the memory address of ES:DI.
/// </summary>
protected uint MemoryAddressEsDi => MemoryUtils.ToPhysicalAddress(State.ES, State.DI);

protected uint MemoryAddressOverridableDsSi => ModRM.GetAddress(SegmentRegisters.DsIndex, State.SI);

protected uint DsNextUint16Address => ModRM.GetAddress(SegmentRegisters.DsIndex, Cpu.NextUint16());

/// <summary>
/// Initializes a new instance of the <see cref="Instructions"/> class.
/// </summary>
/// <param name="cpu">The emulated CPU</param>
/// <param name="memory">The memory bus</param>
/// <param name="modRm">The class that parses the ModRM byte of some instructions.</param>
public Instructions(Cpu cpu, Memory.IMemory memory, ModRM modRm) {
Cpu = cpu;
State = cpu.State;
Expand Down Expand Up @@ -67,10 +76,33 @@ public Instructions(Cpu cpu, Memory.IMemory memory, ModRM modRm) {
public abstract void CmpRegRm();
public abstract void CmpAccImm();

// MOVS
/// <summary>
/// MOVS (Move String) moves the string element pointed to by ESI to the
/// location pointed to by EDI. MOVSB operates on byte elements, MOVSW operates
/// on word elements, and MOVSD operates on doublewords. The destination segment
/// register cannot be overridden by a segment override prefix, but the source
/// segment register can be overridden. <br/>
/// The MOVS instruction, when accompanied by the REP prefix, operates as a
/// memory-to-memory block transfer. To set up for this operation, the program
/// must initialize ECX and the register pairs ESI and EDI. ECX specifies the
/// number of bytes, words, or doublewords in the block.<br/>
/// If DF=0, the program must point ESI to the first element of the source
/// string and point EDI to the destination address for the first element. If
/// DF=1, the program must point these two registers to the last element of the
/// source string and to the destination address for the last element,
/// respectively.
/// </summary>
public abstract void Movs();

// CMPS
/// <summary>
/// CMPS (Compare Strings) subtracts the destination string element (at ES:EDI)
/// from the source string element (at ESI) and updates the flags AF, SF, PF, CF
/// and OF. If the string elements are equal, ZF=1; otherwise, ZF=0. If DF=0,
/// the processor increments the memory pointers (ESI and EDI) for the two
/// strings. CMPSB compares bytes, CMPSW compares words, and CMPSD compares
/// doublewords. The segment register used for the source address can be changed
/// with a segment override prefix while the destination segment register cannot be overridden
/// </summary>
public abstract void Cmps();

protected void AdvanceSI(short diff) {
Expand Down
65 changes: 64 additions & 1 deletion src/Spice86.Core/Emulator/CPU/ModRM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,33 @@ namespace Spice86.Core.Emulator.CPU;

using Spice86.Shared.Utils;

/// <summary>
/// Represents the ModRM byte of some instructions
/// </summary>
public class ModRM {
private readonly Cpu _cpu;
private readonly Memory.IMemory _memory;
private readonly IMemory _memory;
private readonly State _state;
private int _registerMemoryIndex;

/// <summary>
/// Initializes a new instance.
/// </summary>
/// <param name="memory">The memory bus</param>
/// <param name="cpu">The emulated CPU</param>
/// <param name="state">The CPU Registers and Flags</param>
public ModRM(IMemory memory, Cpu cpu, State state) {
_cpu = cpu;
_memory = memory;
_state = state;
}

/// <summary>
/// Returns the linear address the ModRM byte can point at.
/// </summary>
/// <param name="defaultSegmentRegisterIndex">The segment part of the segmented address.</param>
/// <param name="offset">The offset part of the segmented address.</param>
/// <returns>The segment:offset computed into a linear address.</returns>
public uint GetAddress(int defaultSegmentRegisterIndex, ushort offset) {
int? segmentIndex = _state.SegmentOverrideIndex;
segmentIndex ??= defaultSegmentRegisterIndex;
Expand All @@ -25,32 +40,62 @@ public uint GetAddress(int defaultSegmentRegisterIndex, ushort offset) {
return MemoryUtils.ToPhysicalAddress(segment, offset);
}

/// <summary>
/// Gets the linear address the ModRM byte can point at. Can be <c>null</c>.
/// </summary>
public uint? MemoryAddress { get; private set; }

/// <summary>
/// Gets the memory offset of the ModRM byte can point at. Can be <c>null</c>.
/// </summary>
public ushort? MemoryOffset { get; private set; }

/// <summary>
/// Gets or sets the value of the 32 bit register pointed at by the <see cref="RegisterIndex"/> property.
/// </summary>
public uint R32 { get => _state.Registers.GetRegister32(RegisterIndex); set => _state.Registers.SetRegister32(RegisterIndex, value); }

/// <summary>
/// Gets or sets the value of the 16 bit register pointed at by the <see cref="RegisterIndex"/> property.
/// </summary>
public ushort R16 { get => _state.Registers.GetRegister16(RegisterIndex); set => _state.Registers.SetRegister16(RegisterIndex, value); }

/// <summary>
/// Gets or sets the value of the 8 bit register pointed at by the <see cref="RegisterIndex"/> property.
/// </summary>
public byte R8 { get => _state.Registers.GetRegisterFromHighLowIndex8(RegisterIndex); set => _state.Registers.SetRegisterFromHighLowIndex8(RegisterIndex, value); }

/// <summary>
/// Gets the index of the register pointed at by the ModRM byte.
/// </summary>
public int RegisterIndex { get; private set; }

/// <summary>
/// If <see cref="MemoryAddress"/> is <c>null</c>, returns the 32 bit value of the register pointed at by the _registerMemoryIndex field. <br/>
/// If <see cref="MemoryAddress"/> is not <c>null</c>, returns the 32 bit value at the linear address pointed at by the <see cref="MemoryAddress"/> property.
/// </summary>
public uint GetRm32() {
if (MemoryAddress == null) {
return _state.Registers.GetRegister32(_registerMemoryIndex);
}
return _memory.UInt32[(uint)MemoryAddress];
}

/// <summary>
/// If <see cref="MemoryAddress"/> is <c>null</c>, returns the 16 bit value of the register pointed at by the _registerMemoryIndex field. <br/>
/// If <see cref="MemoryAddress"/> is not <c>null</c>, returns the 16 bit value at the linear address pointed at by the <see cref="MemoryAddress"/> property.
/// </summary>
public ushort GetRm16() {
if (MemoryAddress == null) {
return _state.Registers.GetRegister16(_registerMemoryIndex);
}
return _memory.UInt16[(uint)MemoryAddress];
}

/// <summary>
/// If <see cref="MemoryAddress"/> is <c>null</c>, returns the 8 bit value of the register pointed at by the _registerMemoryIndex field. <br/>
/// If <see cref="MemoryAddress"/> is not <c>null</c>, returns the 8 bit value at the linear address pointed at by the <see cref="MemoryAddress"/> property.
/// </summary>
public byte GetRm8() {
if (MemoryAddress == null) {
return _state.Registers.GetRegisterFromHighLowIndex8(_registerMemoryIndex);
Expand All @@ -63,6 +108,9 @@ public byte GetRm8() {
/// </summary>
public ushort SegmentRegister { get => _state.SegmentRegisters.GetRegister16(RegisterIndex); set => _state.SegmentRegisters.SetRegister16(RegisterIndex, value); }

/// <summary>
/// Parses the ModRM byte of the instruction and sets the <see cref="RegisterIndex"/>, <see cref="MemoryOffset"/> and <see cref="MemoryAddress"/> properties.
/// </summary>
public void Read() {
byte modRM = _cpu.NextUint8();
/*
Expand Down Expand Up @@ -106,6 +154,11 @@ public void Read() {
MemoryAddress = GetAddress(segmentRegisterIndex, (ushort)MemoryOffset);
}

/// <summary>
/// If <see cref="MemoryAddress"/> is <c>null</c>, sets the value of the 32 bit register pointed at by the _registerMemoryIndex field. <br/>
/// If <see cref="MemoryAddress"/> is not <c>null</c>, sets the 32 bit value at the linear address pointed at by the <see cref="MemoryAddress"/> property.
/// </summary>
/// <param name="value">The value to copy.</param>
public void SetRm32(uint value) {
if (MemoryAddress == null) {
_state.Registers.SetRegister32(_registerMemoryIndex, value);
Expand All @@ -114,6 +167,11 @@ public void SetRm32(uint value) {
}
}

/// <summary>
/// If <see cref="MemoryAddress"/> is <c>null</c>, sets the value of the 16 bit register pointed at by the _registerMemoryIndex field. <br/>
/// If <see cref="MemoryAddress"/> is not <c>null</c>, sets the 16 bit value at the linear address pointed at by the <see cref="MemoryAddress"/> property.
/// </summary>
/// <param name="value">The value to copy.</param>
public void SetRm16(ushort value) {
if (MemoryAddress == null) {
_state.Registers.SetRegister16(_registerMemoryIndex, value);
Expand All @@ -122,6 +180,11 @@ public void SetRm16(ushort value) {
}
}

/// <summary>
/// If <see cref="MemoryAddress"/> is <c>null</c>, sets the value of the 8 bit register pointed at by the _registerMemoryIndex field. <br/>
/// If <see cref="MemoryAddress"/> is not <c>null</c>, sets the 8 bit value at the linear address pointed at by the <see cref="MemoryAddress"/> property.
/// </summary>
/// <param name="value">The value to copy.</param>
public void SetRm8(byte value) {
if (MemoryAddress == null) {
_state.Registers.SetRegisterFromHighLowIndex8(_registerMemoryIndex, value);
Expand Down
Loading

0 comments on commit 60b43a3

Please sign in to comment.