Skip to content

Commit f8a6725

Browse files
feature: Full fat PS/2 controller, keyboard, and INT9H
1 parent ebe9a3e commit f8a6725

30 files changed

Lines changed: 3967 additions & 415 deletions
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace Spice86.Core.Emulator.Devices.Input.Keyboard;
2+
3+
public partial class Intel8042Controller {
4+
// Controller internal buffer
5+
private struct BufferEntry {
6+
public byte Data;
7+
public bool IsFromAux;
8+
public bool IsFromKbd;
9+
public bool SkipDelay;
10+
}
11+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
namespace Spice86.Core.Emulator.Devices.Input.Keyboard;
2+
3+
using Spice86.Core.Emulator.CPU;
4+
5+
using System;
6+
7+
public partial class Intel8042Controller {
8+
/// <summary>
9+
/// Configuration byte bit definitions.
10+
/// </summary>
11+
[Flags]
12+
public enum ConfigBits : byte {
13+
/// <summary>
14+
/// Bit 0: Keyboard IRQ enabled (IRQ1).
15+
/// </summary>
16+
KbdIrqEnabled = 1 << 0,
17+
/// <summary>
18+
/// Bit 1: Mouse IRQ enabled (IRQ12).
19+
/// </summary>
20+
MouseIrqEnabled = 1 << 1,
21+
/// <summary>
22+
/// Bit 2: Self-test passed.
23+
/// </summary>
24+
SelfTestPassed = 1 << 2,
25+
/// <summary>
26+
/// Bit 3: Reserved.
27+
/// </summary>
28+
Reserved3 = 1 << 3,
29+
/// <summary>
30+
/// Bit 4: Keyboard port disabled.
31+
/// </summary>
32+
DisableKbdPort = 1 << 4,
33+
/// <summary>
34+
/// Bit 5: Auxiliary (mouse) port disabled.
35+
/// </summary>
36+
DisableAuxPort = 1 << 5,
37+
/// <summary>
38+
/// Bit 6: Translation enabled (AT -&gt; XT).
39+
/// </summary>
40+
TranslationEnabled = 1 << 6,
41+
/// <summary>
42+
/// Bit 7: Reserved.
43+
/// </summary>
44+
Reserved7 = 1 << 7,
45+
}
46+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
namespace Spice86.Core.Emulator.Devices.Input.Keyboard;
2+
3+
using System.Diagnostics;
4+
5+
public partial class Intel8042Controller {
6+
/// <summary>
7+
/// Byte 0x00 of the controller memory - configuration byte
8+
/// </summary>
9+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
10+
public sealed class ConfigByte {
11+
public ConfigByte(byte initial = 0) => FromByte(initial);
12+
13+
// bit0: keyboard IRQ enabled (IRQ1)
14+
public bool KbdIrqEnabled { get; set; }
15+
// bit1: mouse IRQ enabled (IRQ12)
16+
public bool MouseIrqEnabled { get; set; }
17+
// bit2: self-test passed
18+
public bool SelfTestPassed { get; set; }
19+
// bit3: reserved
20+
public bool Reserved3 { get; set; }
21+
// bit4: keyboard port disabled
22+
public bool IsKeyboardPortDisabled { get; set; }
23+
// bit5: aux (mouse) port disabled
24+
public bool IsAuxPortDisabled { get; set; }
25+
// bit6: translation enabled (AT -> XT)
26+
public bool TranslationEnabled { get; set; }
27+
// bit7: reserved
28+
public bool Reserved7 { get; set; }
29+
30+
public byte ToByte() {
31+
byte v = 0;
32+
if (KbdIrqEnabled) v |= (byte)ConfigBits.KbdIrqEnabled;
33+
if (MouseIrqEnabled) v |= (byte)ConfigBits.MouseIrqEnabled;
34+
if (SelfTestPassed) v |= (byte)ConfigBits.SelfTestPassed;
35+
if (Reserved3) v |= (byte)ConfigBits.Reserved3;
36+
if (IsKeyboardPortDisabled) v |= (byte)ConfigBits.DisableKbdPort;
37+
if (IsAuxPortDisabled) v |= (byte)ConfigBits.DisableAuxPort;
38+
if (TranslationEnabled) v |= (byte)ConfigBits.TranslationEnabled;
39+
if (Reserved7) v |= (byte)ConfigBits.Reserved7;
40+
return v;
41+
}
42+
43+
public void FromByte(byte value) {
44+
KbdIrqEnabled = (value & (byte)ConfigBits.KbdIrqEnabled) != 0;
45+
MouseIrqEnabled = (value & (byte)ConfigBits.MouseIrqEnabled) != 0;
46+
SelfTestPassed = (value & (byte)ConfigBits.SelfTestPassed) != 0;
47+
Reserved3 = (value & (byte)ConfigBits.Reserved3) != 0;
48+
IsKeyboardPortDisabled = (value & (byte)ConfigBits.DisableKbdPort) != 0;
49+
IsAuxPortDisabled = (value & (byte)ConfigBits.DisableAuxPort) != 0;
50+
TranslationEnabled = (value & (byte)ConfigBits.TranslationEnabled) != 0;
51+
Reserved7 = (value & (byte)ConfigBits.Reserved7) != 0;
52+
}
53+
54+
private string DebuggerDisplay =>
55+
$"0x{ToByte():X2} (IRQ1={KbdIrqEnabled}, IRQ12={MouseIrqEnabled}, KbdDis={IsKeyboardPortDisabled}, AuxDis={IsAuxPortDisabled}, Xlate={TranslationEnabled}, SelfTest={SelfTestPassed})";
56+
}
57+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Spice86.Core.Emulator.Devices.Input.Keyboard;
2+
3+
public partial class Intel8042Controller {
4+
/// <summary>
5+
/// Controller mode values.
6+
/// </summary>
7+
public enum ControllerModeValue : byte {
8+
/// <summary>
9+
/// ISA (AT) mode.
10+
/// </summary>
11+
IsaAt = 0x00,
12+
/// <summary>
13+
/// PS/2 (MCA) mode.
14+
/// </summary>
15+
Ps2Mca = 0x01,
16+
}
17+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
namespace Spice86.Core.Emulator.Devices.Input.Keyboard;
2+
3+
using Spice86.Core.Emulator.CPU;
4+
5+
using System;
6+
7+
public partial class Intel8042Controller {
8+
/// <summary>
9+
/// Line parameter masks used by pulse-lines commands (0xF0-0xFF).
10+
/// </summary>
11+
[Flags]
12+
public enum LineParam : byte {
13+
/// <summary>
14+
/// Reset line mask (bit 0).
15+
/// </summary>
16+
Reset = 0b0001,
17+
/// <summary>
18+
/// All lines mask (bits 0..3 set).
19+
/// </summary>
20+
AllLines = 0b1111,
21+
/// <summary>
22+
/// All lines except Reset (bits 1..3 set).
23+
/// </summary>
24+
AllLinesExceptReset = 0b1110,
25+
}
26+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
namespace Spice86.Core.Emulator.Devices.Input.Keyboard;
2+
3+
using Spice86.Core.Emulator.CPU;
4+
5+
using System;
6+
7+
public partial class Intel8042Controller {
8+
/// <summary>
9+
/// Output port (P2) bit definitions.
10+
/// </summary>
11+
[Flags]
12+
public enum OutputPortBits : byte {
13+
/// <summary>
14+
/// Bit 0: 1 = normal, 0 = CPU reset asserted.
15+
/// </summary>
16+
ResetNotAsserted = 1 << 0,
17+
/// <summary>
18+
/// Bit 1: A20 line enabled.
19+
/// </summary>
20+
A20Enabled = 1 << 1,
21+
/// <summary>
22+
/// Bit 4: Keyboard IRQ1 active.
23+
/// </summary>
24+
KeyboardIrqActive = 1 << 4,
25+
/// <summary>
26+
/// Bit 5: Mouse IRQ12 active.
27+
/// </summary>
28+
MouseIrqActive = 1 << 5,
29+
}
30+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
namespace Spice86.Core.Emulator.Devices.Input.Keyboard;
2+
3+
public partial class Intel8042Controller {
4+
/// <summary>
5+
/// Common controller byte values used across 8042 operations.
6+
/// </summary>
7+
public enum Response : byte {
8+
/// <summary>
9+
/// Generic OK/zero value, used by port tests and various default reads.
10+
/// Also used as NUL terminator for the firmware copyright string.
11+
/// </summary>
12+
Ok = 0x00,
13+
/// <summary>
14+
/// Controller self-test passed value (result of command 0xAA).
15+
/// </summary>
16+
SelfTestPassed = 0x55,
17+
/// <summary>
18+
/// Password not installed or unsupported (command 0xA4 result).
19+
/// </summary>
20+
PasswordNotInstalled = 0xF1,
21+
/// <summary>
22+
/// Space key make code in Set 1 (used by diagnostic dump formatting).
23+
/// </summary>
24+
SpaceScanCodeSet1 = 0x39,
25+
}
26+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
namespace Spice86.Core.Emulator.Devices.Input.Keyboard;
2+
3+
using System.Diagnostics;
4+
5+
public partial class Intel8042Controller {
6+
/// <summary>
7+
/// Encapsulates the 8042 status register for easier debugging.
8+
/// </summary>
9+
[DebuggerDisplay("{DebuggerDisplay,nq}")]
10+
private sealed class Status {
11+
public Status(byte initial = 0) => FromByte(initial);
12+
13+
/// <summary>
14+
/// bit0: output buffer status (1 = data available)
15+
/// </summary>
16+
public bool IsDataPending { get; set; }
17+
18+
/// <summary>
19+
/// bit1: input buffer status (not used here, kept for completeness)
20+
/// </summary>
21+
public bool InputBufferFull { get; set; }
22+
23+
/// <summary>
24+
/// bit2: system flag (not used here)
25+
/// </summary>
26+
public bool SystemFlag { get; set; }
27+
28+
/// <summary>
29+
/// bit3: last write was command
30+
/// </summary>
31+
public bool WasLastWriteCmd { get; set; }
32+
33+
/// <summary>
34+
/// bit4: unused/reserved in this emulation
35+
/// </summary>
36+
public bool Reserved4 { get; set; }
37+
38+
/// <summary>
39+
/// bit5: data came from aux (mouse)
40+
/// </summary>
41+
public bool IsDataFromAux { get; set; }
42+
43+
/// <summary>
44+
/// bit6: timeout
45+
/// </summary>
46+
public bool Timeout { get; set; }
47+
48+
/// <summary>
49+
/// bit7: parity (not used here)
50+
/// </summary>
51+
public bool ParityError { get; set; }
52+
53+
/// <summary>
54+
/// Converts the current status flags to their byte representation.
55+
/// </summary>
56+
/// <returns>A byte value representing the combined status flags. Each bit corresponds to a specific status flag.</returns>
57+
public byte ToByte() {
58+
byte v = 0;
59+
if (IsDataPending) v |= (byte)StatusBits.OutputBufferFull;
60+
if (InputBufferFull) v |= (byte)StatusBits.InputBufferFull;
61+
if (SystemFlag) v |= (byte)StatusBits.SystemFlag;
62+
if (WasLastWriteCmd) v |= (byte)StatusBits.LastWriteWasCommand;
63+
if (Reserved4) v |= (byte)StatusBits.Reserved4;
64+
if (IsDataFromAux) v |= (byte)StatusBits.DataFromAux;
65+
if (Timeout) v |= (byte)StatusBits.Timeout;
66+
if (ParityError) v |= (byte)StatusBits.ParityError;
67+
return v;
68+
}
69+
70+
/// <summary>
71+
/// Parses the specified status byte and updates the corresponding status flags of the instance.
72+
/// </summary>
73+
/// <param name="value">A byte value representing the status bits to parse and apply.</param>
74+
public void FromByte(byte value) {
75+
IsDataPending = (value & (byte)StatusBits.OutputBufferFull) != 0;
76+
InputBufferFull = (value & (byte)StatusBits.InputBufferFull) != 0;
77+
SystemFlag = (value & (byte)StatusBits.SystemFlag) != 0;
78+
WasLastWriteCmd = (value & (byte)StatusBits.LastWriteWasCommand) != 0;
79+
Reserved4 = (value & (byte)StatusBits.Reserved4) != 0;
80+
IsDataFromAux = (value & (byte)StatusBits.DataFromAux) != 0;
81+
Timeout = (value & (byte)StatusBits.Timeout) != 0;
82+
ParityError = (value & (byte)StatusBits.ParityError) != 0;
83+
}
84+
85+
private string DebuggerDisplay =>
86+
$"0x{ToByte():X2} (New={IsDataPending}, Aux={IsDataFromAux}, Cmd={WasLastWriteCmd}, TO={Timeout})";
87+
}
88+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
namespace Spice86.Core.Emulator.Devices.Input.Keyboard;
2+
3+
using Spice86.Core.Emulator.CPU;
4+
5+
using System;
6+
7+
public partial class Intel8042Controller {
8+
/// <summary>
9+
/// Status register bit definitions.
10+
/// </summary>
11+
[Flags]
12+
public enum StatusBits : byte {
13+
/// <summary>
14+
/// Bit 0: Output buffer status (1 = data available).
15+
/// </summary>
16+
OutputBufferFull = 0x01,
17+
/// <summary>
18+
/// Bit 1: Input buffer status.
19+
/// </summary>
20+
InputBufferFull = 0x02,
21+
22+
/// <summary>
23+
/// Bit 2: System flag.
24+
/// </summary>
25+
SystemFlag = 0x04,
26+
/// <summary>
27+
/// Bit 3: Last write was a command.
28+
/// </summary>
29+
LastWriteWasCommand = 0x08,
30+
/// <summary>
31+
/// Bit 4: Reserved/unused.
32+
/// </summary>
33+
Reserved4 = 0x10,
34+
/// <summary>
35+
/// Bit 5: Data came from auxiliary device (mouse).
36+
/// </summary>
37+
DataFromAux = 0x20,
38+
/// <summary>
39+
/// Bit 6: Timeout.
40+
/// </summary>
41+
Timeout = 0x40,
42+
/// <summary>
43+
/// Bit 7: Parity error.
44+
/// </summary>
45+
ParityError = 0x80,
46+
}
47+
}

0 commit comments

Comments
 (0)