Skip to content

Commit 4d6839f

Browse files
authored
fix: We mostly pass single step tests for 386 (#2135)
1 parent 53b9fe4 commit 4d6839f

135 files changed

Lines changed: 4110 additions & 1399 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/copilot-instructions.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ Variants: `MemoryBasedDataStructureWithCsBaseAddress`, `MemoryBasedDataStructure
110110

111111
### Critical AI Agent Guidelines
112112
- **This is a C# project** - never suggest Python solutions
113+
- **No scripts outside the project** - do NOT create temporary scripts (Python, Bash, PowerShell, Node, etc.) anywhere on the filesystem, including `/tmp`, the user's home directory, or any location outside the workspace. Do not create throwaway helper scripts inside the workspace either. Use the available tools (file editing, grep, search, terminal one-liners) directly. If a multi-step computation is truly needed, run it as an inline shell one-liner in the terminal without writing a file.
114+
- **Use `tmp/` for temporary files** - if a temporary file must be written (e.g., captured command output, intermediate data), place it inside the `tmp/` folder at the root of the repository. Never write to `/tmp` or any path outside the workspace.
113115
- **Avoid complexity** - keep cyclomatic complexity low, prefer simple, linear code over nested conditionals
114116
- **No optional parameters** - avoid nullable or optional parameters in new code
115117
- **Minimal comments** - write self-documenting code with clear names; avoid obvious comments

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,3 +397,6 @@ spice86dump*.json
397397
spice86dump*.bin
398398
spice86dump*.asm
399399
spice86dump*.txt
400+
401+
# workdir AI creates
402+
tmp/

clean.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
cd "$SCRIPT_DIR"
6+
7+
dotnet clean src/Spice86.sln
8+
find . -type d \( -name bin -o -name obj \) -exec rm -rf {} +

src/Spice86.Core/CLI/Configuration.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,4 +333,12 @@ public sealed class Configuration : CommandSettings {
333333
[DefaultValue(JitMode.InterpretedThenCompiled)]
334334
public JitMode JitMode { get; init; }
335335

336+
/// <summary>
337+
/// Controls whether an INT instruction whose IVT entry is 0:0 is treated as valid.
338+
/// When true, execution continues at physical address 0. When false (default), a fatal exception is raised
339+
/// to catch uninitialized IVT entries.
340+
/// </summary>
341+
[CommandOption("--AllowIvtAddress0")]
342+
public bool AllowIvtAddress0 { get; init; }
343+
336344
}

src/Spice86.Core/Emulator/CPU/Alu16.cs

Lines changed: 57 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public override ushort And(ushort value1, ushort value2) {
3434
UpdateFlags(res);
3535
_state.CarryFlag = false;
3636
_state.OverflowFlag = false;
37+
// Undocumented: real CPUs clear AF for logical operations
38+
_state.AuxiliaryFlag = false;
3739
return res;
3840
}
3941

@@ -71,6 +73,8 @@ public override int Imul(short value1, short value2) {
7173
bool doesNotFitInWord = res != (short)res;
7274
_state.OverflowFlag = doesNotFitInWord;
7375
_state.CarryFlag = doesNotFitInWord;
76+
UpdateFlags((ushort)res);
77+
_state.AuxiliaryFlag = false;
7478
return res;
7579
}
7680

@@ -94,8 +98,14 @@ public override ushort Or(ushort value1, ushort value2) {
9498
}
9599

96100
public override ushort Rcl(ushort value, byte count) {
97-
count = (byte) ((count & ShiftCountMask) % 17);
101+
int maskedCount = count & ShiftCountMask;
102+
if (maskedCount == 0) {
103+
return value;
104+
}
105+
106+
count = (byte)(maskedCount % 17);
98107
if (count == 0) {
108+
_state.OverflowFlag = _state.CarryFlag ^ ((value & MsbMask) != 0);
99109
return value;
100110
}
101111

@@ -115,8 +125,14 @@ public override ushort Rcl(ushort value, byte count) {
115125
}
116126

117127
public override ushort Rcr(ushort value, int count) {
118-
count = (count & ShiftCountMask) % 17;
128+
int maskedCount = count & ShiftCountMask;
129+
if (maskedCount == 0) {
130+
return value;
131+
}
132+
133+
count = maskedCount % 17;
119134
if (count == 0) {
135+
SetOverflowForRigthRotate16(value);
120136
return value;
121137
}
122138

@@ -135,31 +151,30 @@ public override ushort Rcr(ushort value, int count) {
135151
}
136152

137153
public override ushort Rol(ushort value, byte count) {
138-
count = (byte) ((count & ShiftCountMask) % 16);
139-
if (count == 0) {
154+
int maskedCount = count & ShiftCountMask;
155+
if (maskedCount == 0) {
140156
return value;
141157
}
142-
143-
int carry = value >> 16 - count & 0x1;
144-
ushort res = (ushort)(value << count);
145-
res = (ushort)(res | value >> 16 - count);
146-
_state.CarryFlag = carry != 0;
158+
int effective = maskedCount % 16;
159+
ushort res = effective == 0
160+
? value
161+
: (ushort)((value << effective) | (value >> 16 - effective));
162+
_state.CarryFlag = (res & 0x1) != 0;
147163
bool msb = (res & MsbMask) != 0;
148164
bool lsb = (res & 0x01) != 0;
149165
_state.OverflowFlag = msb ^ lsb;
150166
return res;
151167
}
152168
public override ushort Ror(ushort value, int count) {
153-
count = (count & ShiftCountMask) % 16;
154-
if (count == 0) {
169+
int maskedCount = count & ShiftCountMask;
170+
if (maskedCount == 0) {
155171
return value;
156172
}
157-
158-
int carry = value >> count - 1 & 0x1;
159-
int mask = (1 << 16 - count) - 1;
160-
ushort res = (ushort)(value >> count & mask);
161-
res = (ushort)(res | value << 16 - count);
162-
_state.CarryFlag = carry != 0;
173+
int effective = maskedCount % 16;
174+
ushort res = effective == 0
175+
? value
176+
: (ushort)((value >> effective) | (value << 16 - effective));
177+
_state.CarryFlag = (res & MsbMask) != 0;
163178
SetOverflowForRigthRotate16(res);
164179
return res;
165180
}
@@ -188,23 +203,28 @@ public override ushort Shl(ushort value, int count) {
188203
_state.CarryFlag = msbBefore != 0;
189204
ushort res = (ushort)(value << count);
190205
UpdateFlags(res);
191-
_state.OverflowFlag = ((res ^ value) & MsbMask) != 0;
206+
// See Alu8.Shl for the OF derivation rationale.
207+
_state.OverflowFlag = ((res & MsbMask) != 0) ^ _state.CarryFlag;
192208
return res;
193209
}
194210

195211
public override ushort Shld(ushort destination, ushort source, byte count) {
196212
count &= ShiftCountMask;
197-
switch (count) {
198-
case 0:
199-
return destination;
200-
case > 16:
201-
// Undefined. We shift the source in again.
202-
return (ushort)(source << (count - 16));
213+
if (count == 0) {
214+
return destination;
203215
}
204216

217+
// Real 386 behavior: for count <= 16 the documented SHLD formula applies;
218+
// for count > 16 (the 5-bit mask allows up to 31) the destination is fully
219+
// shifted out and the result equals ROL16(source, count - 16). Both branches
220+
// are expressed as a 32-bit ROL on a paired value, taking the upper 16 bits.
221+
uint combined = count > 16
222+
? ((uint)source << 16) | source
223+
: ((uint)destination << 16) | source;
224+
uint rotated = (combined << count) | (combined >> (32 - count));
205225
ushort msbBefore = (ushort)(destination & MsbMask);
206-
_state.CarryFlag = (destination >> (16 - count) & 1) != 0;
207-
ushort res = (ushort)((destination << count) | (source >> (16 - count)));
226+
_state.CarryFlag = ((combined >> (32 - count)) & 1) != 0;
227+
ushort res = (ushort)(rotated >> 16);
208228
UpdateFlags(res);
209229
ushort msb = (ushort)(res & MsbMask);
210230
_state.OverflowFlag = msb != msbBefore;
@@ -217,22 +237,16 @@ public override ushort Shrd(ushort destination, ushort source, byte count) {
217237
return destination;
218238
}
219239

240+
// Mirror of SHLD: for count > 16 the destination is fully shifted out and
241+
// the result equals ROR16(source, count - 16). Expressed as a 32-bit ROR
242+
// on a paired value, taking the lower 16 bits.
243+
uint combined = count > 16
244+
? ((uint)source << 16) | source
245+
: ((uint)source << 16) | destination;
246+
uint rotated = (combined >> count) | (combined << (32 - count));
220247
ushort msbBefore = (ushort)(destination & MsbMask);
221-
ushort res;
222-
223-
if (count > 16) {
224-
// Undefined. We shift the source in again (opposite direction of SHLD).
225-
int shiftFromSource = count - 16;
226-
res = (ushort)(source >> shiftFromSource);
227-
228-
// Carry flag is the last bit shifted out of the 32-bit concatenation.
229-
// For count > 16, this is bit (count - 17) of the source.
230-
_state.CarryFlag = ((source >> (count - 17)) & 1) != 0;
231-
} else {
232-
_state.CarryFlag = ((destination >> (count - 1)) & 1) != 0;
233-
res = (ushort)((destination >> count) | (source << (16 - count)));
234-
}
235-
248+
_state.CarryFlag = ((combined >> (count - 1)) & 1) != 0;
249+
ushort res = (ushort)rotated;
236250
UpdateFlags(res);
237251
ushort msb = (ushort)(res & MsbMask);
238252
_state.OverflowFlag = msb != msbBefore;
@@ -270,6 +284,8 @@ public override ushort Xor(ushort value1, ushort value2) {
270284
UpdateFlags(res);
271285
_state.CarryFlag = false;
272286
_state.OverflowFlag = false;
287+
// Undocumented: real CPUs clear AF for logical operations
288+
_state.AuxiliaryFlag = false;
273289
return res;
274290
}
275291

src/Spice86.Core/Emulator/CPU/Alu32.cs

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public override uint And(uint value1, uint value2) {
2828
UpdateFlags(res);
2929
_state.CarryFlag = false;
3030
_state.OverflowFlag = false;
31+
// Undocumented: real CPUs clear AF for logical operations
32+
_state.AuxiliaryFlag = false;
3133
return res;
3234
}
3335

@@ -64,6 +66,8 @@ public override long Imul(int value1, int value2) {
6466
bool doesNotFitInDWord = res != (int)res;
6567
_state.OverflowFlag = doesNotFitInDWord;
6668
_state.CarryFlag = doesNotFitInDWord;
69+
UpdateFlags((uint)res);
70+
_state.AuxiliaryFlag = false;
6771
return res;
6872
}
6973

@@ -93,17 +97,17 @@ public override uint Rcl(uint value, byte count) {
9397
}
9498

9599
bool oldCarry = _state.CarryFlag;
96-
uint carry = (value >> (32 - count)) & 0x1;
97-
uint res = value << count;
98-
uint mask = (1u << (count - 1)) - 1u;
99-
res |= (value >> (33 - count)) & mask;
100-
if (oldCarry) {
101-
res |= 1u << (count - 1);
102-
}
103-
104-
_state.CarryFlag = carry != 0;
100+
// Build a 33-bit value [carry][value] and rotate left by count.
101+
// Using ulong avoids the C# 32-bit shift mask that would otherwise
102+
// turn `value << 32` into `value << 0` for count==1 etc.
103+
ulong v = ((oldCarry ? 1UL : 0UL) << 32) | value;
104+
ulong rotated = ((v << count) | (v >> (33 - count))) & ((1UL << 33) - 1UL);
105+
uint res = (uint)rotated;
106+
bool newCarry = (rotated & (1UL << 32)) != 0;
107+
108+
_state.CarryFlag = newCarry;
105109
bool msb = (res & MsbMask) != 0;
106-
_state.OverflowFlag = _state.CarryFlag ^ msb;
110+
_state.OverflowFlag = newCarry ^ msb;
107111
return res;
108112
}
109113

@@ -114,15 +118,13 @@ public override uint Rcr(uint value, int count) {
114118
}
115119

116120
bool oldCarry = _state.CarryFlag;
117-
uint carry = (value >> (count - 1)) & 0x1;
118-
uint mask = (1u << (32 - count)) - 1u;
119-
uint res = (value >> count) & mask;
120-
res |= value << (33 - count);
121-
if (oldCarry) {
122-
res |= 1u << (32 - count);
123-
}
121+
// Build a 33-bit value [carry][value] and rotate right by count.
122+
ulong v = ((oldCarry ? 1UL : 0UL) << 32) | value;
123+
ulong rotated = ((v >> count) | (v << (33 - count))) & ((1UL << 33) - 1UL);
124+
uint res = (uint)rotated;
125+
bool newCarry = (rotated & (1UL << 32)) != 0;
124126

125-
_state.CarryFlag = carry != 0;
127+
_state.CarryFlag = newCarry;
126128
SetOverflowForRigthRotate32(res);
127129
return res;
128130
}
@@ -182,7 +184,8 @@ public override uint Shl(uint value, int count) {
182184
_state.CarryFlag = msbBefore != 0;
183185
uint res = value << count;
184186
UpdateFlags(res);
185-
_state.OverflowFlag = ((res ^ value) & MsbMask) != 0;
187+
// See Alu8.Shl for the OF derivation rationale.
188+
_state.OverflowFlag = ((res & MsbMask) != 0) ^ _state.CarryFlag;
186189
return res;
187190
}
188191

@@ -248,6 +251,8 @@ public override uint Xor(uint value1, uint value2) {
248251
UpdateFlags(res);
249252
_state.CarryFlag = false;
250253
_state.OverflowFlag = false;
254+
// Undocumented: real CPUs clear AF for logical operations
255+
_state.AuxiliaryFlag = false;
251256
return res;
252257
}
253258

0 commit comments

Comments
 (0)