6
6
using Spice86 . Core . Emulator . Devices . ExternalInput ;
7
7
using Spice86 . Core . Emulator . IOPorts ;
8
8
using Spice86 . Core . Emulator . Memory ;
9
- using Spice86 . Core . Emulator . VM ;
10
9
using Spice86 . Shared . Interfaces ;
11
10
12
11
using System ;
16
15
/// C# port of intel8042.cpp - The PS/2 keyboard and mouse controller.
17
16
/// </summary>
18
17
public sealed class Intel8042Controller : DefaultIOPortHandler {
19
- private const uint IrqNumKbdPcjr = 6 ;
20
18
private const uint IrqNumKbdIbmPc = 1 ;
21
19
private const uint IrqNumMouse = 12 ;
22
20
@@ -25,7 +23,7 @@ public sealed class Intel8042Controller : DefaultIOPortHandler {
25
23
26
24
private const int BufferSize = 64 ; // in bytes
27
25
// delay appropriate for 20-30 kHz serial clock and 11 bits/byte
28
- private const double PortDelayMs = 0. 300;
26
+ private const long PortDelayCycles = 300 ; // Converted from 0.3ms to cycles
29
27
30
28
// Controller internal buffer
31
29
private struct BufferEntry {
@@ -42,10 +40,9 @@ private struct BufferEntry {
42
40
private int _waitingBytesFromAux = 0 ;
43
41
private int _waitingBytesFromKbd = 0 ;
44
42
45
- // true = delay timer is in progress
46
- private bool _delayRunning = false ;
47
- // true = delay timer expired, event can be sent immediately
48
- private bool _delayExpired = true ;
43
+ // CPU cycle-based delay tracking
44
+ private long _delayExpiryCycles = 0 ;
45
+ private bool _delayActive = false ;
49
46
50
47
// Executing command, do not notify devices about readiness for accepting frame
51
48
private bool _shouldSkipDeviceNotify = false ;
@@ -67,25 +64,25 @@ private struct BufferEntry {
67
64
68
65
private readonly A20Gate _a20Gate ;
69
66
private readonly DualPic _dualPic ;
70
- private readonly EmulatorEventClock _eventClock ;
67
+ private readonly State _cpuState ;
71
68
72
69
public PS2Keyboard KeyboardDevice { get ; }
73
70
74
71
/// <summary>
75
72
/// Initializes a new instance of the <see cref="Intel8042Controller"/> class.
76
73
/// </summary>
77
74
public Intel8042Controller ( State state , IOPortDispatcher ioPortDispatcher ,
78
- A20Gate a20Gate , DualPic dualPic , EmulatorEventClock eventClock ,
75
+ A20Gate a20Gate , DualPic dualPic ,
79
76
ILoggerService loggerService , bool failOnUnhandledPort ,
80
77
IGuiKeyboardEvents ? gui = null )
81
78
: base ( state , failOnUnhandledPort , loggerService )
82
79
{
83
80
_a20Gate = a20Gate ;
84
81
_dualPic = dualPic ;
85
- _eventClock = eventClock ;
82
+ _cpuState = state ;
86
83
87
84
// Create the keyboard implementation
88
- KeyboardDevice = new PS2Keyboard ( this , eventClock , loggerService , gui ) ;
85
+ KeyboardDevice = new PS2Keyboard ( this , state , loggerService , gui ) ;
89
86
90
87
InitPortHandlers ( ioPortDispatcher ) ;
91
88
@@ -103,19 +100,19 @@ private void InitPortHandlers(IOPortDispatcher ioPortDispatcher) {
103
100
// ***************************************************************************
104
101
105
102
private void WarnBufferFull ( ) {
106
- const uint thresholdMs = 15 * 1000 ; // 15 seconds
103
+ const long thresholdCycles = 15000000 ; // 15 seconds worth of cycles
107
104
108
105
// Static-like behavior using class fields
109
- if ( ! _bufferFullWarned || ( _eventClock . EmulatorUpTimeInMs - _lastBufferFullTimestamp > thresholdMs ) ) {
106
+ if ( ! _bufferFullWarned || ( _cpuState . Cycles - _lastBufferFullCycles > thresholdCycles ) ) {
110
107
if ( _loggerService . IsEnabled ( LogEventLevel . Warning ) ) {
111
108
_loggerService . Warning ( "I8042: Internal buffer overflow" ) ;
112
109
}
113
- _lastBufferFullTimestamp = ( uint ) _eventClock . EmulatorUpTimeInMs ;
110
+ _lastBufferFullCycles = _cpuState . Cycles ;
114
111
_bufferFullWarned = true ;
115
112
}
116
113
}
117
114
private bool _bufferFullWarned = false ;
118
- private uint _lastBufferFullTimestamp = 0 ;
115
+ private long _lastBufferFullCycles = 0 ;
119
116
120
117
private void WarnControllerMode ( ) {
121
118
if ( ! _controllerModeWarned ) {
@@ -238,7 +235,6 @@ private uint GetIrqMouse() {
238
235
}
239
236
240
237
private uint GetIrqKeyboard ( ) {
241
- // Note: We simplified machine detection, always use standard IRQ1
242
238
return IrqNumKbdIbmPc ;
243
239
}
244
240
@@ -288,20 +284,22 @@ private void EnforceBufferSpace(int numBytes = 1) {
288
284
}
289
285
}
290
286
291
- private void DelayHandler ( ) {
292
- _delayRunning = false ;
293
- _delayExpired = true ;
294
-
295
- MaybeTransferBuffer ( ) ;
287
+ private void StartDelay ( long delayCycles = PortDelayCycles ) {
288
+ _delayExpiryCycles = _cpuState . Cycles + delayCycles ;
289
+ _delayActive = true ;
296
290
}
297
291
298
- private void RestartDelayTimer ( double timeMs = PortDelayMs ) {
299
- if ( _delayRunning ) {
300
- _eventClock . RemoveEvent ( "I8042DelayHandler" ) ;
292
+ private bool IsDelayExpired ( ) {
293
+ if ( ! _delayActive ) {
294
+ return true ;
295
+ }
296
+
297
+ if ( _cpuState . Cycles >= _delayExpiryCycles ) {
298
+ _delayActive = false ;
299
+ return true ;
301
300
}
302
- _eventClock . AddEvent ( DelayHandler , timeMs , "I8042DelayHandler" ) ;
303
- _delayRunning = true ;
304
- _delayExpired = false ;
301
+
302
+ return false ;
305
303
}
306
304
307
305
private void MaybeTransferBuffer ( ) {
@@ -313,7 +311,7 @@ private void MaybeTransferBuffer() {
313
311
314
312
// If not set to skip the delay, do not send byte until timer expires
315
313
int idx = _bufferStartIdx ;
316
- if ( ! _delayExpired && ! _buffer [ idx ] . SkipDelay ) {
314
+ if ( ! IsDelayExpired ( ) && ! _buffer [ idx ] . SkipDelay ) {
317
315
return ;
318
316
}
319
317
@@ -329,7 +327,7 @@ private void MaybeTransferBuffer() {
329
327
_statusByte &= unchecked ( ( byte ) ~ ( 1 << 5 ) ) ; // is_data_from_aux = false
330
328
}
331
329
_statusByte |= ( 1 << 0 ) ; // is_data_new = true
332
- RestartDelayTimer ( ) ;
330
+ StartDelay ( ) ;
333
331
ActivateIrqsIfNeeded ( ) ;
334
332
}
335
333
@@ -598,7 +596,7 @@ private void ExecuteCommand(KeyboardCommand command) {
598
596
// Read test bits:
599
597
// bit 0: keyboard clock in
600
598
// bit 1: (AT) keyboard data in, or (PS/2) mouse clock in
601
- // Not fully implemented, follows DOSBox-X behaviour.
599
+ // Not fully implemented
602
600
WarnReadTestInputs ( ) ;
603
601
FlushBuffer ( ) ;
604
602
BufferAdd ( 0x00 ) ;
@@ -629,9 +627,6 @@ private void ExecuteCommand(KeyboardCommand command) {
629
627
}
630
628
631
629
private void ExecuteCommand ( KeyboardCommand command , byte param ) {
632
- // LOG_INFO("I8042: Command 0x%02x, parameter 0x%02x",
633
- // static_cast<int>(command), param);
634
-
635
630
switch ( command ) {
636
631
case KeyboardCommand . WriteByteConfig : // 0x60
637
632
// Writes the keyboard controller configuration byte
@@ -671,7 +666,7 @@ private void ExecuteCommand(KeyboardCommand command, byte param) {
671
666
// To prevent excessive inter-module communication,
672
667
// aux (mouse) part is implemented completely within
673
668
// the mouse module
674
- RestartDelayTimer ( PortDelayMs * 2 ) ; // 'round trip' delay
669
+ StartDelay ( PortDelayCycles * 2 ) ; // 'round trip' delay
675
670
_statusByte &= unchecked ( ( byte ) ~ ( 1 << 6 ) ) ; // clear timeout flag
676
671
// TODO: Mouse support
677
672
// _statusByte |= (1 << 6) * (!MOUSEPS2_PortWrite(param));
@@ -775,7 +770,7 @@ public override byte ReadByte(ushort port) {
775
770
// Enforce the simulated data transfer delay, as some software
776
771
// (Tyrian 2000 setup) reads the port without waiting for the
777
772
// interrupt.
778
- RestartDelayTimer ( PortDelayMs ) ;
773
+ StartDelay ( PortDelayCycles ) ;
779
774
780
775
return retVal ;
781
776
@@ -817,7 +812,7 @@ public override void WriteByte(ushort port, byte value) {
817
812
_configByte &= unchecked ( ( byte ) ~ ( 1 << 4 ) ) ; // auto-enable keyboard port
818
813
819
814
FlushBuffer ( ) ;
820
- RestartDelayTimer ( PortDelayMs * 2 ) ; // 'round trip' delay
815
+ StartDelay ( PortDelayCycles * 2 ) ; // 'round trip' delay
821
816
KeyboardDevice . PortWrite ( value ) ;
822
817
}
823
818
break ;
0 commit comments