Skip to content

Commit

Permalink
Improved ELFViewer
Browse files Browse the repository at this point in the history
  • Loading branch information
Ledmington committed Oct 28, 2024
1 parent c6266fa commit 067902e
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 67 deletions.
9 changes: 6 additions & 3 deletions emu-gui/src/main/java/com/ledmington/emu/AppConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ public static int getMaxCodeInstructions() {
}

public static void setMaxCodeInstructions(final int n) {
if (n < 1) {
final int minCodeInstructions = 1;
if (n < minCodeInstructions) {
throw new IllegalArgumentException(String.format("Invalid max code instructions %,d", n));
}
MAX_CODE_INSTRUCTIONS = n;
Expand All @@ -108,7 +109,8 @@ public static int getMaxMemoryLines() {
}

public static void setMaxMemoryLines(final int n) {
if (n < 1) {
final int minMemoryLines = 1;
if (n < minMemoryLines) {
throw new IllegalArgumentException(String.format("Invalid max memory lines %,d", n));
}
MAX_MEMORY_LINES = n;
Expand All @@ -121,7 +123,8 @@ public static int getMemoryBytesPerLine() {
}

public static void setMemoryBytesPerLine(final int n) {
if (n < 1) {
final int minMemoryBytesPerLine = 1;
if (n < minMemoryBytesPerLine) {
throw new IllegalArgumentException(String.format("Invalid memory bytes per line %,d", n));
}
MEMORY_BYTES_PER_LINE = n;
Expand Down
137 changes: 75 additions & 62 deletions emu-gui/src/main/java/com/ledmington/emu/ELFView.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
Expand All @@ -32,6 +33,7 @@
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;

Expand All @@ -47,6 +49,11 @@

public final class ELFView extends BorderPane {

private static final int MAX_ROWS = 16;
private static final int MAX_GROUPS_PER_ROW = 4;
private static final int MAX_BYTES_PER_GROUP = 4;
private static final int MAX_BYTES_PER_ROW = MAX_GROUPS_PER_ROW * MAX_BYTES_PER_GROUP;

private record Range(int offset, int length) {

private static final int MINIMUM_ALLOWED_LENGTH = 1;
Expand All @@ -63,49 +70,26 @@ public Range(final int singleByte) {
}

private final Stage parent;
private final TextArea textArea = new TextArea();
private final TextArea addressArea = new TextArea();
private final TextArea hexContentArea = new TextArea();
private final TextArea asciiContentArea = new TextArea();
private final TreeView<Label> tree;
private static final int nBytesPerBlock = 4;
private static final int nBlocksPerRow = 4;
private static final int nBytesPerRow = nBytesPerBlock * nBlocksPerRow;

private static int computeTextPosition(final int byteIndex) {
/*
* Here we can compute start and end of the range with a "closed" formula
* because we assume that the output is strictly formatted.
* If this is not the case, the formula needs to be rewritten.
*/

// counting all characters (space + '0x' + hex_address + ' : ')
final int prefixLength = 1 + 2 + 8 + 3;
// (' ' + actual characters + '\n')
final int suffixLength = 3 + nBytesPerRow + 1;
// (... + actual bytes + spaces between blocks + ...)
final int totalRowLength = prefixLength + nBytesPerRow * 2 + (nBlocksPerRow - 1) + suffixLength;
// final int byteIdInRow = byteIndex % nBytesPerRow;
final int byteIdInBlock = byteIndex % nBytesPerBlock;
final int myRow = byteIndex / nBytesPerRow;
final int myBlock = byteIndex / nBytesPerBlock;
final int blockIdInRow = myBlock % nBlocksPerRow;
return myRow * totalRowLength // rows before
+ prefixLength // prefix of my row
+ blockIdInRow * (nBytesPerBlock * 2) // bytes in blocks before mine
+ (blockIdInRow - 1) // spaces between blocks
+ byteIdInBlock * 2 // bytes before me
;
}

private final BiFunction<String, Range, TreeItem<Label>> factory = (name, range) -> {
final Label lbl = new Label(name);
lbl.setOnMouseClicked(e -> {
if (range.length() == 1) {
final int pos = computeTextPosition(range.offset());
textArea.selectRange(pos, pos + 2);
return;
}
final int startRowIndex = range.offset() / MAX_BYTES_PER_ROW;
final int endRowIndex = (range.offset() + range.length()) / MAX_BYTES_PER_ROW;

addressArea.selectRange(
startRowIndex * (2 + 16 + 1), Math.max(startRowIndex + 1, endRowIndex) * (2 + 16 + 1));

asciiContentArea.selectRange(
startRowIndex * (MAX_BYTES_PER_ROW + 1) + (range.offset() % MAX_BYTES_PER_ROW),
endRowIndex * (MAX_BYTES_PER_ROW + 1) + ((range.offset() + range.length()) % MAX_BYTES_PER_ROW));

textArea.selectRange(
computeTextPosition(range.offset()), computeTextPosition(range.offset() + range.length()));
// TODO
hexContentArea.selectRange(range.offset(), range.offset() + range.length());
});
return new TreeItem<>(lbl);
};
Expand All @@ -117,9 +101,29 @@ public ELFView(final Stage parent) {
tree = new TreeView<>(root);
tree.setEditable(false);
this.setLeft(tree);
textArea.setEditable(false);
this.setRight(textArea);

final Font defaultFont = new Font(AppConstants.getDefaultMonospaceFont(), AppConstants.getDefaultFontSize());

final GridPane grid = new GridPane(10, 2);

addressArea.setEditable(false);
addressArea.setFont(defaultFont);
addressArea.setText(String.join("\n", Collections.nCopies(MAX_ROWS, "0x0000000000000000")));
grid.add(addressArea, 0, 0);

hexContentArea.setEditable(false);
hexContentArea.setFont(defaultFont);
hexContentArea.setText(String.join("\n", Collections.nCopies(MAX_ROWS, "00000000 00000000 00000000 00000000")));
grid.add(hexContentArea, 1, 0);

asciiContentArea.setEditable(false);
asciiContentArea.setFont(defaultFont);
asciiContentArea.setText(String.join("\n", Collections.nCopies(MAX_ROWS, "................")));
grid.add(asciiContentArea, 2, 0);

this.setRight(grid);
this.setPadding(new Insets(5));
parent.sizeToScene();
}

public void loadFile(final File elfFile) {
Expand All @@ -135,7 +139,7 @@ public void loadFile(final File elfFile) {

final ELF elf = ELFReader.read(fileBytes);
initializeTreeView(elf);
initializeTextArea(fileBytes);
initializeGridView(fileBytes);
this.parent.sizeToScene();
}

Expand Down Expand Up @@ -303,34 +307,43 @@ private void initializeTreeView(final ELF elf) {
root.getChildren().addAll(List.of(fileHeader, PHTRoot, SHTRoot));
}

private void initializeTextArea(final byte[] fileBytes) {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < fileBytes.length; i++) {
if (i % nBytesPerRow == 0) {
// start of the row
sb.append(" 0x").append(String.format("%08x", i)).append(" : ");
}
sb.append(String.format("%02x", fileBytes[i]));
if (i % nBytesPerBlock == nBytesPerBlock - 1) {
sb.append(' ');
}
if (i % nBytesPerRow == nBytesPerRow - 1) {
sb.append(" ");
final int startOfTheRow = i - (i % nBytesPerRow);
for (int j = 0; j < nBytesPerRow; j++) {
final byte x = fileBytes[startOfTheRow + j];
if (isAsciiPrintable(x)) {
sb.append((char) x);
private void initializeGridView(final byte[] fileBytes) {
// we only care about the first MAX_ROWS * MAX_GROUPS_PER_ROW *
// MAX_BYTES_PER_GROUP bytes
final StringBuilder sbAddress = new StringBuilder();
final StringBuilder sbHex = new StringBuilder();
final StringBuilder sbAscii = new StringBuilder();

for (int row = 0; row < MAX_ROWS; row++) {
sbAddress.append(String.format("0x%016x", (long) (row * MAX_GROUPS_PER_ROW * MAX_BYTES_PER_GROUP)));

for (int group = 0; group < MAX_GROUPS_PER_ROW; group++) {
for (int b = 0; b < MAX_BYTES_PER_GROUP; b++) {
final int byteIndex =
row * MAX_GROUPS_PER_ROW * MAX_BYTES_PER_GROUP + group * MAX_BYTES_PER_GROUP + b;
sbHex.append(String.format("%02x", fileBytes[byteIndex]));
if (isAsciiPrintable(fileBytes[byteIndex])) {
sbAscii.append((char) fileBytes[byteIndex]);
} else {
sb.append('.');
sbAscii.append('.');
}
}
sb.append('\n');

if (group < MAX_GROUPS_PER_ROW - 1) {
sbHex.append(' ');
}
}

if (row < MAX_ROWS - 1) {
sbAddress.append('\n');
sbHex.append('\n');
sbAscii.append('\n');
}
}

textArea.setFont(new Font(AppConstants.getDefaultMonospaceFont(), AppConstants.getDefaultFontSize()));
this.textArea.setText(sb.toString());
addressArea.setText(sbAddress.toString());
hexContentArea.setText(sbHex.toString());
asciiContentArea.setText(sbAscii.toString());
}

private boolean isAsciiPrintable(final byte x) {
Expand Down
4 changes: 2 additions & 2 deletions emu-gui/src/main/java/com/ledmington/emu/X86CpuAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ public void setEntryPoint(final long address) {

@Override
public void execute() {
throw new Error("Not implemented");
cpu.execute();
}

@Override
public void executeOne() {
throw new Error("Not implemented");
cpu.executeOne();
}

@Override
Expand Down
4 changes: 4 additions & 0 deletions emu/src/main/java/com/ledmington/emu/X86Cpu.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,26 @@ public X86Cpu(final X86RegisterFile rf, final MemoryController mem) {
this.dec = new InstructionDecoderV1(instFetch);
}

@Override
public void setEntryPoint(final long entryPointVirtualAddress) {
this.entryPointVirtualAddress = entryPointVirtualAddress;
this.instFetch.setPosition(entryPointVirtualAddress);
logger.debug("Entry point virtual address : 0x%x", entryPointVirtualAddress);
}

@Override
public void execute() {
while (state != State.HALTED) {
executeOne();
}
}

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

@Override
public void executeOne(final Instruction inst) {
logger.debug(inst.toIntelSyntax());
switch (inst.opcode()) {
Expand Down

0 comments on commit 067902e

Please sign in to comment.