Skip to content

Commit

Permalink
Various PMD and javadoc fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Ledmington committed Dec 8, 2024
1 parent 7716936 commit 1204545
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 21 deletions.
2 changes: 2 additions & 0 deletions emu/src/main/java/com/ledmington/emu/InstructionFetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.ledmington.cpu.x86.Register64;
import com.ledmington.mem.MemoryController;
import com.ledmington.utils.ReadOnlyByteBuffer;
import com.ledmington.utils.SuppressFBWarnings;

/** A class which represents the part of the emulated CPU which reads instructions from memory during execution. */
public final class InstructionFetcher implements ReadOnlyByteBuffer {
Expand All @@ -35,6 +36,7 @@ public final class InstructionFetcher implements ReadOnlyByteBuffer {
* @param mem The memory controller to retrieve instructions from.
* @param regFile The register file to get and set the instruction pointer.
*/
@SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "At the moment we need these objects as they are.")
public InstructionFetcher(final MemoryController mem, final RegisterFile regFile) {
this.mem = Objects.requireNonNull(mem);
this.regFile = Objects.requireNonNull(regFile);
Expand Down
22 changes: 21 additions & 1 deletion emu/src/main/java/com/ledmington/emu/X86Cpu.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,33 @@ public class X86Cpu implements X86Emulator {

private static final MiniLogger logger = MiniLogger.getLogger("x86-emu");

/** The state of the CPU. */
protected enum State {

/** The default state of the CPU, meaning that it is able to execute instructions. */
RUNNING,

/** The state in which the CPU has completed execution. */
HALTED
}

private final RegisterFile rf = new X86RegisterFile();
private final MemoryController mem;
private final InstructionFetcher instFetch;
private final InstructionDecoder dec;

/**
* The current state of the CPU. Children classes cna modify this field before executing instructions or to forcibly
* terminate execution.
*/
protected State state = State.RUNNING;

/**
* Creates a new x86 CPU with the given memory controller.
*
* @param mem The object to be used to access the memory.
*/
@SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "At the moment we need this object as it is.")
public X86Cpu(final MemoryController mem) {
this.mem = Objects.requireNonNull(mem);
this.instFetch = new InstructionFetcher(mem, rf);
Expand All @@ -69,13 +80,21 @@ public void execute() {
}
}

private void assertIsRunning() {
if (state != State.RUNNING) {
throw new IllegalStateException("Cannot execute instruction if the state is not RUNNING.");
}
}

@Override
public void executeOne() {
assertIsRunning();
executeOne(dec.decode());
}

@Override
public void executeOne(final Instruction inst) {
assertIsRunning();
logger.debug(inst.toIntelSyntax());
switch (inst.opcode()) {
case SUB -> {
Expand Down Expand Up @@ -226,7 +245,8 @@ public void executeOne(final Instruction inst) {
final long prev = rf.get(Register64.RSP) + 8L;

// If we read 0x0, we have exhausted the stack
if (mem.read8(prev) == 0L) {
final long zero = 0L;
if (mem.read8(prev) == zero) {
state = State.HALTED;
} else {
rf.set(Register64.RSP, prev);
Expand Down
36 changes: 32 additions & 4 deletions emu/src/main/java/com/ledmington/emu/X86RegisterFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
import static com.ledmington.utils.BitUtils.shr;

import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.ledmington.cpu.x86.Register16;
import com.ledmington.cpu.x86.Register32;
Expand All @@ -50,6 +52,20 @@ public final class X86RegisterFile implements RegisterFile {
/** Creates the register file initializing every register to 0. */
public X86RegisterFile() {}

/**
* Creates a register file by copying the one given in input.
*
* @param regFile The register file to be copied.
*/
public X86RegisterFile(final ImmutableRegisterFile regFile) {
Objects.requireNonNull(regFile);
final X86RegisterFile regs = (X86RegisterFile) regFile;
System.arraycopy(regs.gpr, 0, this.gpr, 0, 16);
System.arraycopy(regs.seg, 0, this.seg, 0, 6);
this.rip = regs.rip;
this.rflags = regs.rflags;
}

@Override
public byte get(final Register8 r) {
return switch (r) {
Expand Down Expand Up @@ -276,9 +292,14 @@ private void reset(final RFlags f) {
public String toString() {
return "X86RegisterFile("
+ Arrays.stream(Register64.values())
.map(r -> String.format("%s=0x%016x", r.toIntelSyntax(), get(r)))
.collect(Collectors.joining(";"))
+ ")";
.map(r -> String.format("%s=0x%016x", r.name(), get(r)))
.collect(Collectors.joining(","))
+ ","
+ Stream.of(Register16.CS, Register16.DS, Register16.ES, Register16.FS, Register16.GS, Register16.SS)
.map(r -> String.format("%s=0x%016x", r.name(), get(r)))
.collect(Collectors.joining(","))
+ ",RFLAGS="
+ String.format("0x%016x", rflags) + ")";
}

@Override
Expand All @@ -287,7 +308,11 @@ public int hashCode() {
for (final long r : gpr) {
h = 31 * h + HashUtils.hash(r);
}
for (final short s : seg) {
h = 31 * h + HashUtils.hash(s);
}
h = 31 * h + HashUtils.hash(rip);
h = 31 * h + HashUtils.hash(rflags);
return h;
}

Expand All @@ -303,6 +328,9 @@ public boolean equals(final Object other) {
return false;
}
final X86RegisterFile regs = (X86RegisterFile) other;
return Arrays.equals(this.gpr, regs.gpr) && this.rip == regs.rip;
return Arrays.equals(this.gpr, regs.gpr)
&& Arrays.equals(this.seg, regs.seg)
&& this.rip == regs.rip
&& this.rflags == regs.rflags;
}
}
16 changes: 5 additions & 11 deletions emu/src/test/java/com/ledmington/emu/TestExecution.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,12 @@ void add() {
final Register64 r2 = Register64.values()[rng.nextInt(0, Register64.values().length)];
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);
cpu.executeOne(new Instruction(Opcode.ADD, r1, r2));
assertEquals(
oldValue1 + oldValue2,
cpu.getRegisters().get(r1),
() -> String.format(
"Expected register %s to have value 0x%016x but was 0x%016x",
r1, oldValue1 + oldValue2, cpu.getRegisters().get(r1)));
assertEquals(
oldValue2,
cpu.getRegisters().get(r2),
() -> String.format(
"Expected register %s to have value 0x%016x but was 0x%016x",
r2, oldValue2, cpu.getRegisters().get(r2)));
expected,
cpu.getRegisters(),
() -> String.format("Expected register file to be '%s' but was '%s'.", expected, cpu.getRegisters()));
}
}
3 changes: 2 additions & 1 deletion id/src/main/java/com/ledmington/cpu/x86/IndirectOperand.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ public String toIntelSyntax() {
sb.append(reg2.toIntelSyntax());
shouldAddSign = true;
}
if (constant != 1) {
final int uselessConstant = 1;
if (constant != uselessConstant) {
sb.append('*').append(constant);
shouldAddSign = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
/** This class allows an easier construction of an IndirectOperand object. */
public final class IndirectOperandBuilder {

private static final int DEFAULT_CONSTANT = 1;

private Register baseRegister;
private int c = 1;
private int c = DEFAULT_CONSTANT;
private Register indexRegister;
private Long displacement;
private DisplacementType displacementType = DisplacementType.LONG;
Expand Down Expand Up @@ -66,7 +68,7 @@ public IndirectOperandBuilder reg1(final Register r) {
* @return This instance of IndirectOperandBuilder.
*/
public IndirectOperandBuilder constant(final int c) {
if (this.c != 1) {
if (this.c != DEFAULT_CONSTANT) {
throw new IllegalArgumentException("Cannot define constant twice");
}
if (c != 1 && c != 2 && c != 4 && c != 8) {
Expand Down Expand Up @@ -184,7 +186,7 @@ public IndirectOperand build() {

return new IndirectOperand(baseRegister, indexRegister, c, displacement, displacementType, ptrSize);
} else {
if (c != 1) {
if (c != DEFAULT_CONSTANT) {
if (indexRegister == null) {
throw new IllegalArgumentException(
"Cannot build an IndirectOperand with no reg1, no reg2, constant=" + c + ", "
Expand Down
5 changes: 5 additions & 0 deletions id/src/main/java/com/ledmington/cpu/x86/Instruction.java
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@ public String toIntelSyntax() {
return sb.toString();
}

/**
* Checks whether this instruction is part of the legacy/compatibility x86 set.
*
* @return True if it is legacy, false otherwise.
*/
public boolean isLegacy() {
if (code == Opcode.ENDBR32) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@ public final class UnrecognizedPrefix extends RuntimeException {
@Serial
private static final long serialVersionUID = -5061777630768344350L;

/**
* Creates a new UnrecognizedPrefix runtime exception with a proper message.
*
* @param type The type of the prefix.
* @param position The position of the unrecognized prefix.
*/
public UnrecognizedPrefix(final String type, final long position) {
super(String.format("Found an unrecognized " + type + " prefix at byte 0x%016x", position));
super(String.format("Found an unrecognized %s prefix at byte 0x%016x", type, position));
}
}
5 changes: 5 additions & 0 deletions mem/src/main/java/com/ledmington/mem/MemoryController.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import com.ledmington.utils.BitUtils;
import com.ledmington.utils.IntervalArray;
import com.ledmington.utils.SuppressFBWarnings;

/** This is the part of the memory which implements read-write-execute permissions. */
public final class MemoryController implements Memory {
Expand All @@ -37,9 +38,11 @@ public final class MemoryController implements Memory {
/**
* Creates a MemoryController with the given initializer.
*
* @param memory The Memory object to wrap with permission checking.
* @param breakOnWrongPermissions Decides whether this controller should throw an exception when accessing memory
* with the wrong permissions.
*/
@SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "At the moment we need this object as it is.")
public MemoryController(final Memory memory, final boolean breakOnWrongPermissions) {
this.mem = Objects.requireNonNull(memory);
this.breakOnWrongPermissions = breakOnWrongPermissions;
Expand All @@ -48,6 +51,8 @@ public MemoryController(final Memory memory, final boolean breakOnWrongPermissio
/**
* Creates a MemoryController with the given initializer and the default behavior of breaking when accessing memory
* with the wrong permissions.
*
* @param memory The Memory object to wrap with permission checking.
*/
public MemoryController(final Memory memory) {
this(memory, true);
Expand Down

0 comments on commit 1204545

Please sign in to comment.