Skip to content

Commit

Permalink
Improved TestExecution
Browse files Browse the repository at this point in the history
  • Loading branch information
Ledmington committed Jan 2, 2025
1 parent d7c75a6 commit 1863eef
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 28 deletions.
2 changes: 2 additions & 0 deletions emu/src/main/java/com/ledmington/emu/RegisterFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,6 @@ public interface RegisterFile extends ImmutableRegisterFile {
* @param v The value to be written.
*/
void set(final RFlags f, final boolean v);

void resetFlags();
}
80 changes: 59 additions & 21 deletions emu/src/main/java/com/ledmington/emu/X86Cpu.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.ledmington.cpu.x86.InstructionDecoder;
import com.ledmington.cpu.x86.InstructionDecoderV1;
import com.ledmington.cpu.x86.Register;
import com.ledmington.cpu.x86.Register16;
import com.ledmington.cpu.x86.Register32;
import com.ledmington.cpu.x86.Register64;
import com.ledmington.cpu.x86.Register8;
Expand Down Expand Up @@ -108,34 +109,67 @@ public void executeOne(final Instruction inst) {
};
final long result = r1 - r2;
rf.set(op1, result);
rf.resetFlags();
rf.set(RFlags.ZERO, result == 0L);
} else {
throw new IllegalArgumentException(String.format(
"Don't know what to do when SUB has %,d bits", ((Register) inst.firstOperand()).bits()));
}
}
case ADD -> {
if (inst.firstOperand() instanceof IndirectOperand iop) {
final long address = computeIndirectOperand(rf, iop);
final Register8 op2 = (Register8) inst.secondOperand();
final byte r1 = mem.read(address);
final byte r2 = rf.get(op2);
final byte result = BitUtils.asByte(r1 + r2);
mem.write(address, result);
rf.set(RFlags.ZERO, result == 0);
} else {
final Register64 op1 = (Register64) inst.firstOperand();
final long r1 = rf.get(op1);
final long r2 =
switch (inst.secondOperand()) {
case Register64 op2 -> rf.get(op2);
case Immediate imm -> imm.asLong();
default -> throw new IllegalArgumentException(
String.format("Unknown second argument type %s", inst.secondOperand()));
};
final long result = r1 + r2;
rf.set(op1, result);
rf.set(RFlags.ZERO, result == 0L);
switch (inst.firstOperand()) {
case Register8 op1 -> {
final byte r1 = rf.get(op1);
final byte r2 = rf.get((Register8) inst.secondOperand());
final byte result = BitUtils.asByte(r1 + r2);
rf.set(op1, result);
rf.resetFlags();
rf.set(RFlags.ZERO, result == 0);
}
case Register16 op1 -> {
final short r1 = rf.get(op1);
final short r2 = rf.get((Register16) inst.secondOperand());
final short result = BitUtils.asShort(r1 + r2);
rf.set(op1, result);
rf.resetFlags();
rf.set(RFlags.ZERO, result == 0);
}
case Register32 op1 -> {
final int r1 = rf.get(op1);
final int r2 = rf.get((Register32) inst.secondOperand());
final int result = r1 + r2;
rf.set(op1, result);
rf.resetFlags();
rf.set(RFlags.ZERO, result == 0);
}
case Register64 op1 -> {
final long r1 = rf.get(op1);
final long r2 =
switch (inst.secondOperand()) {
case Register64 op2 -> rf.get(op2);
case Immediate imm -> imm.asLong();
default -> throw new IllegalArgumentException(
String.format("Unknown second argument type %s", inst.secondOperand()));
};
final long result = r1 + r2;
rf.set(op1, result);
rf.resetFlags();
rf.set(RFlags.ZERO, result == 0L);
}
case IndirectOperand iop -> {
final long address = computeIndirectOperand(rf, iop);
final Register8 op2 = (Register8) inst.secondOperand();
final byte r1 = mem.read(address);
final byte r2 = rf.get(op2);
final byte result = BitUtils.asByte(r1 + r2);
mem.write(address, result);
rf.resetFlags();
rf.set(RFlags.ZERO, result == 0);
}
default -> {
throw new IllegalArgumentException(
String.format("Don't know what to do with ADD and %s.", inst.firstOperand()));
}
}
}
case SHR -> {
Expand All @@ -144,6 +178,7 @@ public void executeOne(final Instruction inst) {
final byte imm = ((Immediate) inst.secondOperand()).asByte();
final long result = r1 >>> imm;
rf.set((Register64) inst.firstOperand(), result);
rf.resetFlags();
rf.set(RFlags.ZERO, result == 0L);
} else {
throw new IllegalArgumentException(String.format(
Expand All @@ -156,6 +191,7 @@ public void executeOne(final Instruction inst) {
final byte imm = ((Immediate) inst.secondOperand()).asByte();
final long result = r1 >> imm;
rf.set(op1, result);
rf.resetFlags();
rf.set(RFlags.ZERO, result == 0L);
} else {
throw new IllegalArgumentException(String.format(
Expand All @@ -168,6 +204,7 @@ public void executeOne(final Instruction inst) {
final byte imm = rf.get((Register8) inst.secondOperand());
final long result = r1 << imm;
rf.set((Register64) inst.firstOperand(), result);
rf.resetFlags();
rf.set(RFlags.ZERO, result == 0L);
} else {
throw new IllegalArgumentException(String.format(
Expand Down Expand Up @@ -200,6 +237,7 @@ public void executeOne(final Instruction inst) {
case TEST -> {
final long r1 = rf.get((Register64) inst.firstOperand());
final long r2 = rf.get((Register64) inst.secondOperand());
rf.resetFlags();
rf.set(RFlags.ZERO, (r1 & r2) == 0L);
}
case JMP -> {
Expand Down
5 changes: 5 additions & 0 deletions emu/src/main/java/com/ledmington/emu/X86RegisterFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,11 @@ public void set(final RFlags f, final boolean v) {
}
}

@Override
public void resetFlags() {
rflags = 0L;
}

private void set(final RFlags f) {
rflags |= (1L << f.bit());
}
Expand Down
95 changes: 88 additions & 7 deletions emu/src/test/java/com/ledmington/emu/TestExecution.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
import com.ledmington.cpu.x86.Instruction;
import com.ledmington.cpu.x86.Opcode;
import com.ledmington.cpu.x86.Register16;
import com.ledmington.cpu.x86.Register32;
import com.ledmington.cpu.x86.Register64;
import com.ledmington.cpu.x86.Register8;
import com.ledmington.mem.Memory;
import com.ledmington.mem.MemoryController;
import com.ledmington.utils.BitUtils;
Expand Down Expand Up @@ -76,19 +78,96 @@ void setup() {
}
}

private static Stream<Arguments> pairs() {
private static Stream<Arguments> pairs64() {
return Arrays.stream(Register64.values())
.flatMap(r -> Arrays.stream(Register64.values()).map(x -> Arguments.of(r, x)));
}

private static Stream<Arguments> pairs32() {
return Arrays.stream(Register32.values())
.flatMap(r -> Arrays.stream(Register64.values()).map(x -> Arguments.of(r, x)));
}

private static Stream<Arguments> pairs16() {
return Arrays.stream(Register16.values())
.flatMap(r -> Arrays.stream(Register64.values()).map(x -> Arguments.of(r, x)));
}

private static Stream<Arguments> pairs8() {
return Arrays.stream(Register8.values())
.flatMap(r -> Arrays.stream(Register64.values()).map(x -> Arguments.of(r, x)));
}

@Test
void add() {
void add64() {
for (final Register64 a : Register64.values()) {
for (final Register64 b : Register64.values()) {
final long oldValue1 = cpu.getRegisters().get(a);
final long oldValue2 = cpu.getRegisters().get(b);
final X86RegisterFile expected = new X86RegisterFile(cpu.getRegisters());
expected.set(a, oldValue1 + oldValue2);
final long result = oldValue1 + oldValue2;
expected.set(a, result);
expected.set(RFlags.ZERO, result == 0L);
cpu.executeOne(new Instruction(Opcode.ADD, a, b));
assertEquals(
expected,
cpu.getRegisters(),
() -> String.format(
"Expected register file to be '%s' but was '%s'.", expected, cpu.getRegisters()));
}
}
}

@Test
void add32() {
for (final Register32 a : Register32.values()) {
for (final Register32 b : Register32.values()) {
final int oldValue1 = cpu.getRegisters().get(a);
final int oldValue2 = cpu.getRegisters().get(b);
final X86RegisterFile expected = new X86RegisterFile(cpu.getRegisters());
final int result = oldValue1 + oldValue2;
expected.set(a, result);
expected.set(RFlags.ZERO, result == 0L);
cpu.executeOne(new Instruction(Opcode.ADD, a, b));
assertEquals(
expected,
cpu.getRegisters(),
() -> String.format(
"Expected register file to be '%s' but was '%s'.", expected, cpu.getRegisters()));
}
}
}

@Test
void add16() {
for (final Register16 a : Register16.values()) {
for (final Register16 b : Register16.values()) {
final short oldValue1 = cpu.getRegisters().get(a);
final short oldValue2 = cpu.getRegisters().get(b);
final X86RegisterFile expected = new X86RegisterFile(cpu.getRegisters());
final short result = BitUtils.asShort(oldValue1 + oldValue2);
expected.set(a, result);
expected.set(RFlags.ZERO, result == 0);
cpu.executeOne(new Instruction(Opcode.ADD, a, b));
assertEquals(
expected,
cpu.getRegisters(),
() -> String.format(
"Expected register file to be '%s' but was '%s'.", expected, cpu.getRegisters()));
}
}
}

@Test
void add8() {
for (final Register8 a : Register8.values()) {
for (final Register8 b : Register8.values()) {
final byte oldValue1 = cpu.getRegisters().get(a);
final byte oldValue2 = cpu.getRegisters().get(b);
final X86RegisterFile expected = new X86RegisterFile(cpu.getRegisters());
final byte result = BitUtils.asByte(oldValue1 + oldValue2);
expected.set(a, result);
expected.set(RFlags.ZERO, result == 0);
cpu.executeOne(new Instruction(Opcode.ADD, a, b));
assertEquals(
expected,
Expand All @@ -100,12 +179,14 @@ void add() {
}

@ParameterizedTest
@MethodSource("pairs")
@MethodSource("pairs64")
void sub(final Register64 r1, final Register64 r2) {
final long oldValue1 = cpu.getRegisters().get(r1);
final long oldValue2 = cpu.getRegisters().get(r2);
final X86RegisterFile expected = new X86RegisterFile(cpu.getRegisters());
expected.set(r1, oldValue1 - oldValue2);
final long result = oldValue1 - oldValue2;
expected.set(r1, result);
expected.set(RFlags.ZERO, result == 0L);
cpu.executeOne(new Instruction(Opcode.SUB, r1, r2));
assertEquals(
expected,
Expand All @@ -114,7 +195,7 @@ void sub(final Register64 r1, final Register64 r2) {
}

@ParameterizedTest
@MethodSource("pairs")
@MethodSource("pairs64")
void xor(final Register64 r1, final Register64 r2) {
final long oldValue1 = cpu.getRegisters().get(r1);
final long oldValue2 = cpu.getRegisters().get(r2);
Expand All @@ -128,7 +209,7 @@ void xor(final Register64 r1, final Register64 r2) {
}

@ParameterizedTest
@MethodSource("pairs")
@MethodSource("pairs64")
void and(final Register64 r1, final Register64 r2) {
final long oldValue1 = cpu.getRegisters().get(r1);
final long oldValue2 = cpu.getRegisters().get(r2);
Expand Down

0 comments on commit 1863eef

Please sign in to comment.