From 200a238b17a6e89c1b6f6ec22d9c90362cced28d Mon Sep 17 00:00:00 2001 From: Ledmington Date: Thu, 4 Dec 2025 17:51:09 +0100 Subject: [PATCH 1/5] Added MemoryAddress --- .../com/ledmington/mem/MemoryAddress.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 mem/src/main/java/com/ledmington/mem/MemoryAddress.java diff --git a/mem/src/main/java/com/ledmington/mem/MemoryAddress.java b/mem/src/main/java/com/ledmington/mem/MemoryAddress.java new file mode 100644 index 00000000..1a3ef39c --- /dev/null +++ b/mem/src/main/java/com/ledmington/mem/MemoryAddress.java @@ -0,0 +1,31 @@ +/* + * emu - Processor Emulator + * Copyright (C) 2023-2025 Filippo Barbari + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.ledmington.mem; + +/** A 64-bit memory address. */ +public record MemoryAddress(long address) implements Comparable { + @Override + public int compareTo(final MemoryAddress other) { + return Long.compareUnsigned(address, other.address); + } + + @Override + public String toString() { + return String.format("MemoryAddress(address=0x%016x)", address); + } +} From fa1f36ce2cd83e8105292918474f51552e2b276d Mon Sep 17 00:00:00 2001 From: Ledmington Date: Thu, 4 Dec 2025 18:39:13 +0100 Subject: [PATCH 2/5] wip --- .../main/java/com/ledmington/mem/Memory.java | 44 +++++++++---------- .../com/ledmington/mem/MemoryAddress.java | 4 ++ .../com/ledmington/mem/MemoryController.java | 24 +++++----- .../ledmington/mem/RandomAccessMemory.java | 8 ++-- 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/mem/src/main/java/com/ledmington/mem/Memory.java b/mem/src/main/java/com/ledmington/mem/Memory.java index a8314e89..a22d1c96 100644 --- a/mem/src/main/java/com/ledmington/mem/Memory.java +++ b/mem/src/main/java/com/ledmington/mem/Memory.java @@ -32,7 +32,7 @@ public interface Memory { * @param address The address to read from. * @return The byte word contained at the given address. */ - byte read(long address); + byte read(MemoryAddress address); /** * Reads 2 contiguous bytes (little-endian) starting at the given address. @@ -40,11 +40,11 @@ public interface Memory { * @param address The address to read from. * @return The 2-byte value that was stored at the given address. */ - default short read2(final long address) { + default short read2(final MemoryAddress address) { // Little-endian short x = 0; x = BitUtils.or(x, BitUtils.asShort(read(address))); - x = BitUtils.or(x, BitUtils.shl(BitUtils.asShort(read(address + 1L)), 8)); + x = BitUtils.or(x, BitUtils.shl(BitUtils.asShort(read(address.plus(1L))), 8)); return x; } @@ -54,13 +54,13 @@ default short read2(final long address) { * @param address The address to read from. * @return The 4-byte value that was stored at the given address. */ - default int read4(final long address) { + default int read4(final MemoryAddress address) { // Little-endian int x = 0; x |= BitUtils.asInt(read(address)); - x |= (BitUtils.asInt(read(address + 1L)) << 8); - x |= (BitUtils.asInt(read(address + 2L)) << 16); - x |= (BitUtils.asInt(read(address + 3L)) << 24); + x |= (BitUtils.asInt(read(address.plus(1L))) << 8); + x |= (BitUtils.asInt(read(address.plus(2L))) << 16); + x |= (BitUtils.asInt(read(address.plus(3L))) << 24); return x; } @@ -70,17 +70,17 @@ default int read4(final long address) { * @param address The address to read from. * @return The 8-byte value that was stored at the given address. */ - default long read8(final long address) { + default long read8(final MemoryAddress address) { // Little-endian long x = 0x0000000000000000L; x |= BitUtils.asLong(read(address)); - x |= (BitUtils.asLong(read(address + 1L)) << 8); - x |= (BitUtils.asLong(read(address + 2L)) << 16); - x |= (BitUtils.asLong(read(address + 3L)) << 24); - x |= (BitUtils.asLong(read(address + 4L)) << 32); - x |= (BitUtils.asLong(read(address + 5L)) << 40); - x |= (BitUtils.asLong(read(address + 6L)) << 48); - x |= (BitUtils.asLong(read(address + 7L)) << 56); + x |= (BitUtils.asLong(read(address.plus(1L))) << 8); + x |= (BitUtils.asLong(read(address.plus(2L))) << 16); + x |= (BitUtils.asLong(read(address.plus(3L))) << 24); + x |= (BitUtils.asLong(read(address.plus(4L))) << 32); + x |= (BitUtils.asLong(read(address.plus(5L))) << 40); + x |= (BitUtils.asLong(read(address.plus(6L))) << 48); + x |= (BitUtils.asLong(read(address.plus(7L))) << 56); return x; } @@ -90,7 +90,7 @@ default long read8(final long address) { * @param address The address to write at. * @param value The value to write. */ - void write(long address, byte value); + void write(MemoryAddress address, byte value); /** * Writes an arbitrary number of single-byte values contiguously in memory. @@ -98,9 +98,9 @@ default long read8(final long address) { * @param address The address to start writing from. * @param values THe array of values to write contiguously. */ - default void write(final long address, final byte[] values) { + default void write(final MemoryAddress address, final byte[] values) { for (int i = 0; i < values.length; i++) { - write(address + i, values[i]); + write(address.plus(i), values[i]); } } @@ -110,7 +110,7 @@ default void write(final long address, final byte[] values) { * @param address The address to write the value at. * @param value The value to be written. */ - default void write(final long address, final short value) { + default void write(final MemoryAddress address, final short value) { write(address, BitUtils.asLEBytes(value)); } @@ -120,7 +120,7 @@ default void write(final long address, final short value) { * @param address The address to write the value at. * @param value The value to be written. */ - default void write(final long address, final int value) { + default void write(final MemoryAddress address, final int value) { write(address, BitUtils.asLEBytes(value)); } @@ -130,7 +130,7 @@ default void write(final long address, final int value) { * @param address The address to write the value at. * @param value The value to be written. */ - default void write(final long address, final long value) { + default void write(final MemoryAddress address, final long value) { write(address, BitUtils.asLEBytes(value)); } @@ -141,5 +141,5 @@ default void write(final long address, final long value) { * @param address The address to be checked. * @return True if it is initialized, false otherwise. */ - boolean isInitialized(long address); + boolean isInitialized(MemoryAddress address); } diff --git a/mem/src/main/java/com/ledmington/mem/MemoryAddress.java b/mem/src/main/java/com/ledmington/mem/MemoryAddress.java index 1a3ef39c..4c6547f5 100644 --- a/mem/src/main/java/com/ledmington/mem/MemoryAddress.java +++ b/mem/src/main/java/com/ledmington/mem/MemoryAddress.java @@ -19,6 +19,10 @@ /** A 64-bit memory address. */ public record MemoryAddress(long address) implements Comparable { + public MemoryAddress plus(final long offset) { + return new MemoryAddress(address + offset); + } + @Override public int compareTo(final MemoryAddress other) { return Long.compareUnsigned(address, other.address); diff --git a/mem/src/main/java/com/ledmington/mem/MemoryController.java b/mem/src/main/java/com/ledmington/mem/MemoryController.java index c6751cbf..5e4ae005 100644 --- a/mem/src/main/java/com/ledmington/mem/MemoryController.java +++ b/mem/src/main/java/com/ledmington/mem/MemoryController.java @@ -97,16 +97,16 @@ public MemoryController(final Memory memory) { this(memory, true, true, false, false, false); } - private boolean canRead(final long address) { - return readableAddresses.get(address); + private boolean canRead(final MemoryAddress address) { + return readableAddresses.get(address.address()); } - private boolean canWrite(final long address) { - return writableAddresses.get(address); + private boolean canWrite(final MemoryAddress address) { + return writableAddresses.get(address.address()); } - private boolean canExecute(final long address) { - return executableAddresses.get(address); + private boolean canExecute(final MemoryAddress address) { + return executableAddresses.get(address.address()); } private String reportIllegalAccess( @@ -255,7 +255,7 @@ private void reportAccessToUninitialized(final long address, final int length) { */ @SuppressWarnings("PMD.AvoidLiteralsInIfCondition") public void setPermissions( - final long start, + final MemoryAddress start, final long numBytes, final boolean readable, final boolean writeable, @@ -295,7 +295,7 @@ private void checkInitialized(final long address, final int length) { } @Override - public byte read(final long address) { + public byte read(final MemoryAddress address) { checkRead(address, 1); checkInitialized(address, 1); return this.mem.read(address); @@ -308,7 +308,7 @@ public byte read(final long address) { * @return A 64-bit value read. */ @Override - public long read8(final long address) { + public long read8(final MemoryAddress address) { checkRead(address, 8); checkInitialized(address, 8); @@ -342,7 +342,7 @@ private void checkExecute(final long address) { * @param address The 64-bit address to read the instructions from. * @return The instruction byte at the given address. */ - public byte readCode(final long address) { + public byte readCode(final MemoryAddress address) { checkExecute(address); checkInitialized(address, 1); return mem.read(address); @@ -360,7 +360,7 @@ private void checkWrite(final long address, final int length) { } @Override - public void write(final long address, final byte value) { + public void write(final MemoryAddress address, final byte value) { checkWrite(address, 1); mem.write(address, value); } @@ -372,7 +372,7 @@ public void write(final long address, final byte value) { * @param value The 64-bit value to write. */ @Override - public void write(final long address, final long value) { + public void write(final MemoryAddress address, final long value) { checkWrite(address, 8); initialize(address, BitUtils.asLEBytes(value)); } diff --git a/mem/src/main/java/com/ledmington/mem/RandomAccessMemory.java b/mem/src/main/java/com/ledmington/mem/RandomAccessMemory.java index 94c49694..81e0beee 100644 --- a/mem/src/main/java/com/ledmington/mem/RandomAccessMemory.java +++ b/mem/src/main/java/com/ledmington/mem/RandomAccessMemory.java @@ -25,7 +25,7 @@ public final class RandomAccessMemory implements Memory { private final MemoryInitializer init; - private final Map m = new ConcurrentHashMap<>(); + private final Map m = new ConcurrentHashMap<>(); /** * Creates a RAM with the given memory initializer. @@ -37,17 +37,17 @@ public RandomAccessMemory(final MemoryInitializer init) { } @Override - public byte read(final long address) { + public byte read(final MemoryAddress address) { return isInitialized(address) ? m.get(address) : init.get(); } @Override - public void write(final long address, final byte value) { + public void write(final MemoryAddress address, final byte value) { m.put(address, value); } @Override - public boolean isInitialized(final long address) { + public boolean isInitialized(final MemoryAddress address) { return m.containsKey(address); } From e9cbb32d02b693864d8fac67dd1cff8f2aa7197f Mon Sep 17 00:00:00 2001 From: Ledmington Date: Fri, 23 Jan 2026 20:16:14 +0100 Subject: [PATCH 3/5] wip --- .../com/ledmington/mem/MemoryAddress.java | 2 +- .../com/ledmington/mem/MemoryController.java | 105 ++++++++------- .../java/com/ledmington/mem/PagedMemory.java | 36 ++--- .../com/ledmington/mem/TestPermissions.java | 125 ++++++++++-------- 4 files changed, 141 insertions(+), 127 deletions(-) diff --git a/mem/src/main/java/com/ledmington/mem/MemoryAddress.java b/mem/src/main/java/com/ledmington/mem/MemoryAddress.java index 4c6547f5..01b933d6 100644 --- a/mem/src/main/java/com/ledmington/mem/MemoryAddress.java +++ b/mem/src/main/java/com/ledmington/mem/MemoryAddress.java @@ -1,6 +1,6 @@ /* * emu - Processor Emulator - * Copyright (C) 2023-2025 Filippo Barbari + * Copyright (C) 2023-2026 Filippo Barbari * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/mem/src/main/java/com/ledmington/mem/MemoryController.java b/mem/src/main/java/com/ledmington/mem/MemoryController.java index 5e4ae005..db81ac7a 100644 --- a/mem/src/main/java/com/ledmington/mem/MemoryController.java +++ b/mem/src/main/java/com/ledmington/mem/MemoryController.java @@ -111,15 +111,16 @@ private boolean canExecute(final MemoryAddress address) { private String reportIllegalAccess( final String message, - final long address, + final MemoryAddress address, final int length, final String propertyName, - final Predicate allowed) { + final Predicate allowed) { final long linesAround = 5; final long bytesPerLine = 16; // The start of the given range must be in the middle 16-bytes aligned line - final long startAddress = address / bytesPerLine * bytesPerLine - bytesPerLine * linesAround; + final MemoryAddress startAddress = + new MemoryAddress(address.address() / bytesPerLine * bytesPerLine - bytesPerLine * linesAround); final StringBuilder sb = new StringBuilder(256); @@ -138,12 +139,12 @@ private String reportIllegalAccess( .append(TerminalUtils.ANSI_RESET) .append("\n\n"); - final Consumer printer = x -> { + final Consumer printer = x -> { final String s = isInitialized(x) ? String.format("%02x", mem.read(x)) : "xx"; sb.append(x == address ? '[' : ' '); // Print the bytes accessed in bold - if (x >= address && x < address + length) { + if (x.compareTo(address) >= 0 && x.compareTo(address.plus(length)) < 0) { sb.append(TerminalUtils.ANSI_BOLD); } if (allowed.test(x)) { @@ -151,7 +152,7 @@ private String reportIllegalAccess( } else { sb.append(TerminalUtils.ANSI_RED); } - sb.append(s).append(TerminalUtils.ANSI_RESET).append(x == (address + length - 1) ? ']' : ' '); + sb.append(s).append(TerminalUtils.ANSI_RESET).append(x.equals(address.plus(length - 1)) ? ']' : ' '); }; // print column headers @@ -159,27 +160,31 @@ private String reportIllegalAccess( // print lines before for (long r = 0L; r < linesAround; r++) { - sb.append(String.format(" 0x%016x: ", startAddress + (r * bytesPerLine))); + sb.append(String.format( + " 0x%016x: ", startAddress.plus(r * bytesPerLine).address())); for (long i = 0L; i < bytesPerLine; i++) { - final long x = startAddress + (r * bytesPerLine) + i; + final MemoryAddress x = startAddress.plus((r * bytesPerLine) + i); printer.accept(x); } sb.append('\n'); } // print line with the given address - sb.append(String.format(" 0x%016x: ", startAddress + (linesAround * bytesPerLine))); + sb.append(String.format( + " 0x%016x: ", startAddress.plus(linesAround * bytesPerLine).address())); for (long i = 0L; i < bytesPerLine; i++) { - final long x = startAddress + (linesAround * bytesPerLine) + i; + final MemoryAddress x = startAddress.plus((linesAround * bytesPerLine) + i); printer.accept(x); } sb.append('\n'); // print lines after for (long r = 0L; r < linesAround; r++) { - sb.append(String.format(" 0x%016x: ", startAddress + ((linesAround + 1 + r) * bytesPerLine))); + sb.append(String.format( + " 0x%016x: ", + startAddress.plus((linesAround + 1 + r) * bytesPerLine).address())); for (long i = 0L; i < bytesPerLine; i++) { - final long x = startAddress + ((linesAround + 1 + r) * bytesPerLine) + i; + final MemoryAddress x = startAddress.plus(((linesAround + 1 + r) * bytesPerLine) + i); printer.accept(x); } sb.append('\n'); @@ -197,46 +202,46 @@ private void checkLength(final int length) { } } - private void reportIllegalRead(final long address, final int length) { + private void reportIllegalRead(final MemoryAddress address, final int length) { checkLength(length); throw new IllegalReadException(reportIllegalAccess( String.format( "Attempted %d-byte read at%s non-readable address 0x%x", - length, isInitialized(address) ? "" : " uninitialized", address), + length, isInitialized(address) ? "" : " uninitialized", address.address()), address, length, "Readable", - readableAddresses::get)); + x -> readableAddresses.get(x.address()))); } - private void reportIllegalExecution(final long address) { + private void reportIllegalExecution(final MemoryAddress address) { checkLength(1); throw new IllegalExecutionException(reportIllegalAccess( String.format( "Attempted 1-byte execution at%s non-executable address 0x%x", - isInitialized(address) ? "" : " uninitialized", address), + isInitialized(address) ? "" : " uninitialized", address.address()), address, 1, "Executable", - executableAddresses::get)); + x -> executableAddresses.get(x.address()))); } - private void reportIllegalWrite(final long address, final int length) { + private void reportIllegalWrite(final MemoryAddress address, final int length) { checkLength(length); throw new IllegalWriteException(reportIllegalAccess( String.format( "Attempted %d-byte write at%s non-writable address 0x%x", - length, isInitialized(address) ? "" : " uninitialized", address), + length, isInitialized(address) ? "" : " uninitialized", address.address()), address, length, "Writable", - writableAddresses::get)); + x -> writableAddresses.get(x.address()))); } - private void reportAccessToUninitialized(final long address, final int length) { + private void reportAccessToUninitialized(final MemoryAddress address, final int length) { checkLength(length); throw new AccessToUninitializedMemoryException(reportIllegalAccess( - String.format("Attempted %d-byte access at uninitialized address 0x%x", length, address), + String.format("Attempted %d-byte access at uninitialized address 0x%x", length, address.address()), address, length, "Initialized", @@ -267,28 +272,28 @@ public void setPermissions( return; } - readableAddresses.set(start, numBytes, readable); - writableAddresses.set(start, numBytes, writeable); - executableAddresses.set(start, numBytes, executable); + readableAddresses.set(start.address(), numBytes, readable); + writableAddresses.set(start.address(), numBytes, writeable); + executableAddresses.set(start.address(), numBytes, executable); } - private void checkRead(final long address, final int length) { + private void checkRead(final MemoryAddress address, final int length) { if (!breakOnWrongPermissions) { return; } for (int i = 0; i < length; i++) { - if (!canRead(address + i)) { + if (!canRead(address.plus(i))) { reportIllegalRead(address, length); } } } - private void checkInitialized(final long address, final int length) { + private void checkInitialized(final MemoryAddress address, final int length) { if (!breakWhenReadingUninitializedMemory) { return; } for (int i = 0; i < length; i++) { - if (!isInitialized(address + i)) { + if (!isInitialized(address.plus(i))) { reportAccessToUninitialized(address, length); } } @@ -315,22 +320,22 @@ public long read8(final MemoryAddress address) { // Little-endian long x = 0x0000000000000000L; x |= BitUtils.asLong(mem.read(address)); - x |= (BitUtils.asLong(mem.read(address + 1L)) << 8); - x |= (BitUtils.asLong(mem.read(address + 2L)) << 16); - x |= (BitUtils.asLong(mem.read(address + 3L)) << 24); - x |= (BitUtils.asLong(mem.read(address + 4L)) << 32); - x |= (BitUtils.asLong(mem.read(address + 5L)) << 40); - x |= (BitUtils.asLong(mem.read(address + 6L)) << 48); - x |= (BitUtils.asLong(mem.read(address + 7L)) << 56); + x |= (BitUtils.asLong(mem.read(address.plus(1L))) << 8); + x |= (BitUtils.asLong(mem.read(address.plus(2L))) << 16); + x |= (BitUtils.asLong(mem.read(address.plus(3L))) << 24); + x |= (BitUtils.asLong(mem.read(address.plus(4L))) << 32); + x |= (BitUtils.asLong(mem.read(address.plus(5L))) << 40); + x |= (BitUtils.asLong(mem.read(address.plus(6L))) << 48); + x |= (BitUtils.asLong(mem.read(address.plus(7L))) << 56); return x; } - private void checkExecute(final long address) { + private void checkExecute(final MemoryAddress address) { if (!breakOnWrongPermissions) { return; } for (int i = 0; i < 1; i++) { - if (!canExecute(address + i)) { + if (!canExecute(address.plus(i))) { reportIllegalExecution(address); } } @@ -348,12 +353,12 @@ public byte readCode(final MemoryAddress address) { return mem.read(address); } - private void checkWrite(final long address, final int length) { + private void checkWrite(final MemoryAddress address, final int length) { if (!breakOnWrongPermissions) { return; } for (int i = 0; i < length; i++) { - if (!canWrite(address + i)) { + if (!canWrite(address.plus(i))) { reportIllegalWrite(address, length); } } @@ -378,7 +383,7 @@ public void write(final MemoryAddress address, final long value) { } @Override - public boolean isInitialized(final long address) { + public boolean isInitialized(final MemoryAddress address) { return mem.isInitialized(address); } @@ -389,9 +394,9 @@ public boolean isInitialized(final long address) { * @param numBytes The length of the address range. * @param value The 8-bit value to be written in each byte. */ - public void initialize(final long start, final long numBytes, final byte value) { + public void initialize(final MemoryAddress start, final long numBytes, final byte value) { for (long i = 0L; i < numBytes; i++) { - mem.write(start + i, value); + mem.write(start.plus(i), value); } } @@ -401,7 +406,7 @@ public void initialize(final long start, final long numBytes, final byte value) * @param address The address where to write the value. * @param value The value to be written. */ - public void initialize(final long address, final short value) { + public void initialize(final MemoryAddress address, final short value) { initialize(address, BitUtils.asLEBytes(value)); } @@ -411,7 +416,7 @@ public void initialize(final long address, final short value) { * @param address The address where to write the value. * @param value The value to be written. */ - public void initialize(final long address, final int value) { + public void initialize(final MemoryAddress address, final int value) { initialize(address, BitUtils.asLEBytes(value)); } @@ -421,7 +426,7 @@ public void initialize(final long address, final int value) { * @param address The address where to write the value. * @param value The value to be written. */ - public void initialize(final long address, final long value) { + public void initialize(final MemoryAddress address, final long value) { initialize(address, BitUtils.asLEBytes(value)); } @@ -431,9 +436,9 @@ public void initialize(final long address, final long value) { * @param start The start of the address range. * @param values The non-null array of 8-bit values to be written. */ - public void initialize(final long start, final byte[] values) { + public void initialize(final MemoryAddress start, final byte[] values) { for (int i = 0; i < values.length; i++) { - mem.write(start + i, values[i]); + mem.write(start.plus(i), values[i]); } } @@ -443,7 +448,7 @@ public void initialize(final long start, final byte[] values) { * @param address The address where to write the value. * @param value The value to be written. */ - public void initialize(final long address, final byte value) { + public void initialize(final MemoryAddress address, final byte value) { mem.write(address, value); } diff --git a/mem/src/main/java/com/ledmington/mem/PagedMemory.java b/mem/src/main/java/com/ledmington/mem/PagedMemory.java index 609d3d7a..14e744ad 100644 --- a/mem/src/main/java/com/ledmington/mem/PagedMemory.java +++ b/mem/src/main/java/com/ledmington/mem/PagedMemory.java @@ -35,7 +35,7 @@ public final class PagedMemory implements Memory { private final MemoryInitializer initializer; private final long pageSize; private final long pageSizeMask; - private final Map pages = new ConcurrentHashMap<>(); + private final Map pages = new ConcurrentHashMap<>(); private static final class Page { @@ -113,46 +113,46 @@ public PagedMemory(final MemoryInitializer initializer) { } /** Returns the given address aligned to a page size boundary. */ - private long getAlignedAddress(final long address) { - return address & this.pageSizeMask; + private MemoryAddress getAlignedAddress(final MemoryAddress address) { + return new MemoryAddress(address.address() & this.pageSizeMask); } /** Returns the given address localized inside a page. */ - private long getLocalAddress(final long address) { - return address & (~this.pageSizeMask); + private MemoryAddress getLocalAddress(final MemoryAddress address) { + return new MemoryAddress(address.address() & (~this.pageSizeMask)); } @Override - public byte read(final long address) { + public byte read(final MemoryAddress address) { // TODO: add overloaded versions of read2, read4 and read8 - final long alignedAddress = getAlignedAddress(address); + final MemoryAddress alignedAddress = getAlignedAddress(address); if (!this.pages.containsKey(alignedAddress)) { return this.initializer.get(); } - final long localAddress = getLocalAddress(address); - return this.pages.get(alignedAddress).bytes[Math.toIntExact(localAddress)]; + final MemoryAddress localAddress = getLocalAddress(address); + return this.pages.get(alignedAddress).bytes[Math.toIntExact(localAddress.address())]; } @Override - public void write(final long address, final byte value) { + public void write(final MemoryAddress address, final byte value) { // TODO: add overloaded versions of write2, write4, write8 and writeN - final long alignedAddress = getAlignedAddress(address); + final MemoryAddress alignedAddress = getAlignedAddress(address); if (!this.pages.containsKey(alignedAddress)) { this.pages.put(alignedAddress, new Page(pageSize, initializer)); } - final long localAddress = getLocalAddress(address); - this.pages.get(alignedAddress).initialized[Math.toIntExact(localAddress)] = true; - this.pages.get(alignedAddress).bytes[Math.toIntExact(localAddress)] = value; + final MemoryAddress localAddress = getLocalAddress(address); + this.pages.get(alignedAddress).initialized[Math.toIntExact(localAddress.address())] = true; + this.pages.get(alignedAddress).bytes[Math.toIntExact(localAddress.address())] = value; } @Override - public boolean isInitialized(final long address) { - final long alignedAddress = getAlignedAddress(address); + public boolean isInitialized(final MemoryAddress address) { + final MemoryAddress alignedAddress = getAlignedAddress(address); if (!this.pages.containsKey(alignedAddress)) { return false; } - final long localAddress = getLocalAddress(address); - return this.pages.get(alignedAddress).initialized[Math.toIntExact(localAddress)]; + final MemoryAddress localAddress = getLocalAddress(address); + return this.pages.get(alignedAddress).initialized[Math.toIntExact(localAddress.address())]; } @Override diff --git a/mem/src/test/java/com/ledmington/mem/TestPermissions.java b/mem/src/test/java/com/ledmington/mem/TestPermissions.java index 2b47aaff..79bb066f 100644 --- a/mem/src/test/java/com/ledmington/mem/TestPermissions.java +++ b/mem/src/test/java/com/ledmington/mem/TestPermissions.java @@ -48,74 +48,83 @@ void setup() { @Test void cantReadByDefault() { - final Set positions = - Stream.generate(rng::nextLong).distinct().limit(100).collect(Collectors.toSet()); + final Set positions = Stream.generate(rng::nextLong) + .distinct() + .limit(100) + .map(MemoryAddress::new) + .collect(Collectors.toSet()); - for (final long address : positions) { + for (final MemoryAddress address : positions) { assertThrows(IllegalReadException.class, () -> mem.read(address)); } } @Test void cantExecuteByDefault() { - final Set positions = - Stream.generate(rng::nextLong).distinct().limit(100).collect(Collectors.toSet()); + final Set positions = Stream.generate(rng::nextLong) + .distinct() + .limit(100) + .map(MemoryAddress::new) + .collect(Collectors.toSet()); - for (final long address : positions) { + for (final MemoryAddress address : positions) { assertThrows(IllegalExecutionException.class, () -> mem.readCode(address)); } } @Test void cantWriteByDefault() { - final Set positions = - Stream.generate(rng::nextLong).distinct().limit(100).collect(Collectors.toSet()); + final Set positions = Stream.generate(rng::nextLong) + .distinct() + .limit(100) + .map(MemoryAddress::new) + .collect(Collectors.toSet()); - for (final long address : positions) { + for (final MemoryAddress address : positions) { assertThrows(IllegalWriteException.class, () -> mem.write(address, BitUtils.asByte(rng.nextInt()))); } } @Test void canRead() { - final long start = rng.nextLong(); - final long end = start + 100L; - mem.setPermissions(start, end, true, false, false); - - for (long i = start; i <= end; i++) { - final long finalI = i; - assertDoesNotThrow(() -> mem.read(finalI)); - assertThrows(IllegalExecutionException.class, () -> mem.readCode(finalI)); - assertThrows(IllegalWriteException.class, () -> mem.write(finalI, BitUtils.asByte(rng.nextInt()))); + final MemoryAddress start = new MemoryAddress(rng.nextLong()); + final long numBytes = 100L; + mem.setPermissions(start, numBytes, true, false, false); + + for (long i = 0L; i < numBytes; i++) { + final MemoryAddress address = start.plus(i); + assertDoesNotThrow(() -> mem.read(address)); + assertThrows(IllegalExecutionException.class, () -> mem.readCode(address)); + assertThrows(IllegalWriteException.class, () -> mem.write(address, BitUtils.asByte(rng.nextInt()))); } } @Test void canReadSingleAddress() { - final long address = rng.nextLong(); + final MemoryAddress address = new MemoryAddress(rng.nextLong()); mem.setPermissions(address, 1L, true, false, false); - assertThrows(IllegalReadException.class, () -> mem.read(address - 1L)); - assertThrows(IllegalExecutionException.class, () -> mem.readCode(address - 1L)); - assertThrows(IllegalWriteException.class, () -> mem.write(address - 1L, (byte) rng.nextInt())); + assertThrows(IllegalReadException.class, () -> mem.read(address.plus(-1L))); + assertThrows(IllegalExecutionException.class, () -> mem.readCode(address.plus(-1L))); + assertThrows(IllegalWriteException.class, () -> mem.write(address.plus(-1L), (byte) rng.nextInt())); assertDoesNotThrow(() -> mem.read(address)); assertThrows(IllegalExecutionException.class, () -> mem.readCode(address)); assertThrows(IllegalWriteException.class, () -> mem.write(address, (byte) rng.nextInt())); - assertThrows(IllegalReadException.class, () -> mem.read(address + 1L)); - assertThrows(IllegalExecutionException.class, () -> mem.readCode(address + 1L)); - assertThrows(IllegalWriteException.class, () -> mem.write(address + 1L, (byte) rng.nextInt())); + assertThrows(IllegalReadException.class, () -> mem.read(address.plus(1L))); + assertThrows(IllegalExecutionException.class, () -> mem.readCode(address.plus(1L))); + assertThrows(IllegalWriteException.class, () -> mem.write(address.plus(1L), (byte) rng.nextInt())); } @Test void canWrite() { - final long start = rng.nextLong(); + final MemoryAddress start = new MemoryAddress(rng.nextLong()); final long numBytes = 100L; mem.setPermissions(start, numBytes, false, true, false); for (long i = 0L; i < numBytes; i++) { - final long address = start + i; + final MemoryAddress address = start.plus(i); assertDoesNotThrow(() -> mem.write(address, BitUtils.asByte(rng.nextInt()))); assertThrows(IllegalReadException.class, () -> mem.read(address)); assertThrows(IllegalExecutionException.class, () -> mem.readCode(address)); @@ -124,30 +133,30 @@ void canWrite() { @Test void canWriteSingleAddress() { - final long address = rng.nextLong(); + final MemoryAddress address = new MemoryAddress(rng.nextLong()); mem.setPermissions(address, 1L, false, true, false); - assertThrows(IllegalReadException.class, () -> mem.read(address - 1L)); - assertThrows(IllegalExecutionException.class, () -> mem.readCode(address - 1L)); - assertThrows(IllegalWriteException.class, () -> mem.write(address - 1L, (byte) rng.nextInt())); + assertThrows(IllegalReadException.class, () -> mem.read(address.plus(-1L))); + assertThrows(IllegalExecutionException.class, () -> mem.readCode(address.plus(-1L))); + assertThrows(IllegalWriteException.class, () -> mem.write(address.plus(-1L), (byte) rng.nextInt())); assertThrows(IllegalReadException.class, () -> mem.read(address)); assertThrows(IllegalExecutionException.class, () -> mem.readCode(address)); assertDoesNotThrow(() -> mem.write(address, (byte) rng.nextInt())); - assertThrows(IllegalReadException.class, () -> mem.read(address + 1L)); - assertThrows(IllegalExecutionException.class, () -> mem.readCode(address + 1L)); - assertThrows(IllegalWriteException.class, () -> mem.write(address + 1L, (byte) rng.nextInt())); + assertThrows(IllegalReadException.class, () -> mem.read(address.plus(1L))); + assertThrows(IllegalExecutionException.class, () -> mem.readCode(address.plus(1L))); + assertThrows(IllegalWriteException.class, () -> mem.write(address.plus(1L), (byte) rng.nextInt())); } @Test void canReadAndWrite() { - final long start = rng.nextLong(); + final MemoryAddress start = new MemoryAddress(rng.nextLong()); final long numBytes = 100L; mem.setPermissions(start, numBytes, true, true, false); for (long i = 0L; i < numBytes; i++) { - final long address = start + i; + final MemoryAddress address = start.plus(i); assertDoesNotThrow(() -> mem.read(address)); assertDoesNotThrow(() -> mem.write(address, BitUtils.asByte(rng.nextInt()))); assertThrows(IllegalExecutionException.class, () -> mem.readCode(address)); @@ -156,12 +165,12 @@ void canReadAndWrite() { @Test void canExecute() { - final long start = rng.nextLong(); + final MemoryAddress start = new MemoryAddress(rng.nextLong()); final long numBytes = 100L; mem.setPermissions(start, numBytes, false, false, true); for (long i = 0L; i < numBytes; i++) { - final long address = start + i; + final MemoryAddress address = start.plus(i); assertDoesNotThrow(() -> mem.readCode(address)); assertThrows(IllegalReadException.class, () -> mem.read(address)); assertThrows(IllegalWriteException.class, () -> mem.write(address, BitUtils.asByte(rng.nextInt()))); @@ -170,44 +179,44 @@ void canExecute() { @Test void canExecuteSingleAddress() { - final long address = rng.nextLong(); + final MemoryAddress address = new MemoryAddress(rng.nextLong()); mem.setPermissions(address, 1L, false, false, true); - assertThrows(IllegalReadException.class, () -> mem.read(address - 1L)); - assertThrows(IllegalExecutionException.class, () -> mem.readCode(address - 1L)); - assertThrows(IllegalWriteException.class, () -> mem.write(address - 1L, (byte) rng.nextInt())); + assertThrows(IllegalReadException.class, () -> mem.read(address.plus(-1L))); + assertThrows(IllegalExecutionException.class, () -> mem.readCode(address.plus(-1L))); + assertThrows(IllegalWriteException.class, () -> mem.write(address.plus(-1L), (byte) rng.nextInt())); assertThrows(IllegalReadException.class, () -> mem.read(address)); assertDoesNotThrow(() -> mem.readCode(address)); assertThrows(IllegalWriteException.class, () -> mem.write(address, (byte) rng.nextInt())); - assertThrows(IllegalReadException.class, () -> mem.read(address + 1L)); - assertThrows(IllegalExecutionException.class, () -> mem.readCode(address + 1L)); - assertThrows(IllegalWriteException.class, () -> mem.write(address + 1L, (byte) rng.nextInt())); + assertThrows(IllegalReadException.class, () -> mem.read(address.plus(1L))); + assertThrows(IllegalExecutionException.class, () -> mem.readCode(address.plus(1L))); + assertThrows(IllegalWriteException.class, () -> mem.write(address.plus(1L), (byte) rng.nextInt())); } @Test void canReadAndExecute() { - final long start = rng.nextLong(); - final long end = start + 100L; - mem.setPermissions(start, end, true, false, true); - - for (long i = start; i <= end; i++) { - final long finalI = i; - assertDoesNotThrow(() -> mem.read(finalI)); - assertDoesNotThrow(() -> mem.readCode(finalI)); - assertThrows(IllegalWriteException.class, () -> mem.write(finalI, BitUtils.asByte(rng.nextInt()))); + final MemoryAddress start = new MemoryAddress(rng.nextLong()); + final long numBytes = 100L; + mem.setPermissions(start, numBytes, true, false, true); + + for (long i = 0L; i < numBytes; i++) { + final MemoryAddress address = start.plus(i); + assertDoesNotThrow(() -> mem.read(address)); + assertDoesNotThrow(() -> mem.readCode(address)); + assertThrows(IllegalWriteException.class, () -> mem.write(address, BitUtils.asByte(rng.nextInt()))); } } @Test void canWriteAndExecute() { - final long start = rng.nextLong(); + final MemoryAddress start = new MemoryAddress(rng.nextLong()); final long numBytes = 100L; mem.setPermissions(start, numBytes, false, true, true); for (long i = 0L; i < numBytes; i++) { - final long address = start + i; + final MemoryAddress address = start.plus(i); assertDoesNotThrow(() -> mem.readCode(address)); assertDoesNotThrow(() -> mem.write(address, BitUtils.asByte(rng.nextInt()))); assertThrows(IllegalReadException.class, () -> mem.read(address)); @@ -216,12 +225,12 @@ void canWriteAndExecute() { @Test void canReadWriteAndExecute() { - final long start = rng.nextLong(); + final MemoryAddress start = new MemoryAddress(rng.nextLong()); final long numBytes = 100L; mem.setPermissions(start, numBytes, true, true, true); for (long i = 0L; i < numBytes; i++) { - final long address = start + i; + final MemoryAddress address = start.plus(i); assertDoesNotThrow(() -> mem.readCode(address)); assertDoesNotThrow(() -> mem.read(address)); assertDoesNotThrow(() -> mem.write(address, BitUtils.asByte(rng.nextInt()))); From 4c2a4b07ca823f903c667eaba6ee46d2bbef82a9 Mon Sep 17 00:00:00 2001 From: Ledmington Date: Mon, 4 May 2026 22:31:03 +0200 Subject: [PATCH 4/5] Finished adapting code to new MemoryAddress API --- .../java/com/ledmington/emu/ELFLoader.java | 20 ++++--- .../ledmington/emu/InstructionFetcher.java | 3 +- .../main/java/com/ledmington/emu/X86Cpu.java | 55 ++++++++++--------- .../com/ledmington/emu/TestExecution.java | 19 ++++--- .../emu/TestExecutionWithMemory.java | 47 ++++++++-------- .../main/java/com/ledmington/emudb/EmuDB.java | 7 ++- .../ledmington/emudb/MemoryByteBuffer.java | 3 +- .../com/ledmington/view/EmulatorView.java | 7 ++- .../com/ledmingtom/mem/BenchPagedMemory.java | 6 +- .../mem/BenchRandomAccessMemory.java | 6 +- .../com/ledmington/mem/MemoryAddress.java | 13 ++++- .../java/com/ledmington/mem/TestMemory.java | 13 +++-- .../ledmington/mem/TestMemoryController.java | 6 +- .../ledmington/mem/TestMemoryInitializer.java | 9 +-- .../mem/TestUninitializedMemory.java | 46 ++++++++++------ 15 files changed, 149 insertions(+), 111 deletions(-) diff --git a/core/src/main/java/com/ledmington/emu/ELFLoader.java b/core/src/main/java/com/ledmington/emu/ELFLoader.java index b4ff493f..8cdfa37c 100644 --- a/core/src/main/java/com/ledmington/emu/ELFLoader.java +++ b/core/src/main/java/com/ledmington/emu/ELFLoader.java @@ -47,6 +47,7 @@ import com.ledmington.elf.section.sym.SymbolTableEntry; import com.ledmington.elf.section.sym.SymbolTableEntryType; import com.ledmington.elf.section.sym.SymbolTableSection; +import com.ledmington.mem.MemoryAddress; import com.ledmington.mem.MemoryController; import com.ledmington.utils.BitUtils; import com.ledmington.utils.MiniLogger; @@ -135,8 +136,8 @@ public void load( */ final Instruction halt = new GeneralInstruction(Opcode.HLT); final byte[] haltEncoded = InstructionEncoder.toHex(halt, true); - mem.setPermissions(baseStackValue, haltEncoded.length, false, false, true); - mem.initialize(baseStackValue, haltEncoded); + mem.setPermissions(new MemoryAddress(baseStackValue), haltEncoded.length, false, false, true); + mem.initialize(new MemoryAddress(baseStackValue), haltEncoded); push(baseStackValue); } @@ -349,8 +350,8 @@ private void setupStack(final long stackTop, final long stackBottom) { logger.debug( "Setting stack size to %,d bytes (%.3f MiB) at 0x%016x-0x%016x", stackSize, stackSize / 1_048_576.0, stackBottom, stackTop - 1L); - mem.setPermissions(stackBottom, stackSize, true, true, false); - mem.initialize(stackBottom, stackSize, (byte) 0x00); + mem.setPermissions(new MemoryAddress(stackBottom), stackSize, true, true, false); + mem.initialize(new MemoryAddress(stackBottom), stackSize, (byte) 0x00); } private void loadCommandLineArgumentsAndEnvironmentVariables( @@ -469,10 +470,10 @@ private void loadCommandLineArgumentsAndEnvironmentVariables( throw new AssertionError("Content on the stack is not word-aligned."); } - mem.initialize(stackBase, content); + mem.initialize(new MemoryAddress(stackBase), content); // FIXME: setting the stack's permissions, again? - mem.setPermissions(stackBase, content.length, true, true, false); + mem.setPermissions(new MemoryAddress(stackBase), content.length, true, true, false); } private List getAuxiliaryVector(final ELF elf) { @@ -512,7 +513,8 @@ private void loadSegments(final ProgramHeaderTable pht, final long baseAddress) (phte.isReadable() ? "R" : "") + (phte.isWriteable() ? "W" : "") + (phte.isExecutable() ? "X" : "")); - mem.setPermissions(start, end, phte.isReadable(), phte.isWriteable(), phte.isExecutable()); + mem.setPermissions( + new MemoryAddress(start), end, phte.isReadable(), phte.isWriteable(), phte.isExecutable()); segmentIndex++; memorySegments.add(new Range(start, end)); @@ -544,7 +546,7 @@ private void initializeSection(final Section sec, final long baseAddress) { logger.debug( "Loading section %s in memory segment %,d at range 0x%x-0x%x (%,d bytes)", sec.getName(), segmentIndex, startVirtualAddress, startVirtualAddress + size, size); - mem.initialize(startVirtualAddress, size, (byte) 0x00); + mem.initialize(new MemoryAddress(startVirtualAddress), size, (byte) 0x00); } case LoadableSection ls -> { final long startVirtualAddress = baseAddress + sec.header().getVirtualAddress(); @@ -557,7 +559,7 @@ private void initializeSection(final Section sec, final long baseAddress) { startVirtualAddress, startVirtualAddress + content.length, content.length); - mem.initialize(startVirtualAddress, content); + mem.initialize(new MemoryAddress(startVirtualAddress), content); } default -> throw new IllegalArgumentException(String.format( diff --git a/core/src/main/java/com/ledmington/emu/InstructionFetcher.java b/core/src/main/java/com/ledmington/emu/InstructionFetcher.java index 085aebd4..3ee2a694 100644 --- a/core/src/main/java/com/ledmington/emu/InstructionFetcher.java +++ b/core/src/main/java/com/ledmington/emu/InstructionFetcher.java @@ -20,6 +20,7 @@ import java.util.Objects; import com.ledmington.cpu.x86.Register64; +import com.ledmington.mem.MemoryAddress; import com.ledmington.mem.MemoryController; import com.ledmington.utils.ReadOnlyByteBuffer; import com.ledmington.utils.SuppressFBWarnings; @@ -74,7 +75,7 @@ public long getPosition() { @Override public byte read() { - return mem.readCode(getPosition()); + return mem.readCode(new MemoryAddress(getPosition())); } @Override diff --git a/core/src/main/java/com/ledmington/emu/X86Cpu.java b/core/src/main/java/com/ledmington/emu/X86Cpu.java index a60aad75..edbac559 100644 --- a/core/src/main/java/com/ledmington/emu/X86Cpu.java +++ b/core/src/main/java/com/ledmington/emu/X86Cpu.java @@ -37,6 +37,7 @@ import com.ledmington.cpu.x86.Register8; import com.ledmington.emu.config.CPUConfig; import com.ledmington.mem.Memory; +import com.ledmington.mem.MemoryAddress; import com.ledmington.mem.MemoryController; import com.ledmington.utils.BitUtils; import com.ledmington.utils.MiniLogger; @@ -292,23 +293,23 @@ public void executeOne(final Instruction inst) { () -> getAsLongSX(inst.firstOperand()), () -> getAsLongSX(inst.secondOperand()), (a, b) -> a - b, - result -> {}, + _ -> {}, true, MathUtils::willCarrySub, MathUtils::willOverflowSub); case TEST -> { if (inst.firstOperand() instanceof final Register8 r1 && inst.secondOperand() instanceof final Register8 r2) { - op(() -> rf.get(r1), () -> rf.get(r2), BitUtils::and, result -> {}, true); + op(() -> rf.get(r1), () -> rf.get(r2), BitUtils::and, _ -> {}, true); } else if (inst.firstOperand() instanceof final Register16 r1 && inst.secondOperand() instanceof final Register16 r2) { - op(() -> rf.get(r1), () -> rf.get(r2), BitUtils::and, result -> {}, true); + op(() -> rf.get(r1), () -> rf.get(r2), BitUtils::and, _ -> {}, true); } else if (inst.firstOperand() instanceof final Register32 r1 && inst.secondOperand() instanceof final Register32 r2) { - op(() -> rf.get(r1), () -> rf.get(r2), (a, b) -> a & b, result -> {}, true); + op(() -> rf.get(r1), () -> rf.get(r2), (a, b) -> a & b, _ -> {}, true); } else if (inst.firstOperand() instanceof final Register64 r1 && inst.secondOperand() instanceof final Register64 r2) { - op(() -> rf.get(r1), () -> rf.get(r2), (a, b) -> a & b, result -> {}, true); + op(() -> rf.get(r1), () -> rf.get(r2), (a, b) -> a & b, _ -> {}, true); } else { throw new IllegalArgumentException(String.format("Don't know what to do with '%s'.", inst)); } @@ -380,27 +381,27 @@ public void executeOne(final Instruction inst) { rf.set(op1, imm.asInt()); } else if (inst.firstOperand() instanceof final IndirectOperand io && inst.secondOperand() instanceof final Register8 op2) { - final long address = computeIndirectOperand(io); + final MemoryAddress address = computeIndirectOperand(io); mem.write(address, rf.get(op2)); } else if (inst.firstOperand() instanceof final IndirectOperand io && inst.secondOperand() instanceof final Register16 op2) { - final long address = computeIndirectOperand(io); + final MemoryAddress address = computeIndirectOperand(io); mem.write(address, rf.get(op2)); } else if (inst.firstOperand() instanceof final IndirectOperand io && inst.secondOperand() instanceof final Register32 op2) { - final long address = computeIndirectOperand(io); + final MemoryAddress address = computeIndirectOperand(io); mem.write(address, rf.get(op2)); } else if (inst.firstOperand() instanceof final IndirectOperand io && inst.secondOperand() instanceof final Register64 op2) { - final long address = computeIndirectOperand(io); + final MemoryAddress address = computeIndirectOperand(io); mem.write(address, rf.get(op2)); } else if (inst.firstOperand() instanceof final IndirectOperand io && inst.secondOperand() instanceof final Immediate imm) { - final long address = computeIndirectOperand(io); + final MemoryAddress address = computeIndirectOperand(io); mem.write(address, imm.asInt()); } else if (inst.firstOperand() instanceof final Register32 op1 && inst.secondOperand() instanceof final IndirectOperand io) { - final long address = computeIndirectOperand(io); + final MemoryAddress address = computeIndirectOperand(io); rf.set(op1, mem.read4(address)); } else { throw new IllegalArgumentException( @@ -422,7 +423,7 @@ public void executeOne(final Instruction inst) { long rdi = rf.get(Register64.RDI); while (rcx != 0) { - mem.write(rdi, rax); + mem.write(new MemoryAddress(rdi), rax); rdi += increment; rcx--; } @@ -449,14 +450,16 @@ public void executeOne(final Instruction inst) { case LEA -> { final IndirectOperand src = (IndirectOperand) inst.secondOperand(); if (inst.firstOperand() instanceof final Register64 dest) { - final long address = computeIndirectOperand(src); - rf.set(dest, address); + final MemoryAddress address = computeIndirectOperand(src); + rf.set(dest, address.address()); } else if (inst.firstOperand() instanceof final Register32 dest) { - final int address = BitUtils.asInt(computeIndirectOperand(src)); + final int address = + BitUtils.asInt(computeIndirectOperand(src).address()); rf.set(dest, address); } else { final Register16 dest = (Register16) inst.firstOperand(); - final short address = BitUtils.asShort(computeIndirectOperand(src)); + final short address = + BitUtils.asShort(computeIndirectOperand(src).address()); rf.set(dest, address); } } @@ -471,7 +474,7 @@ public void executeOne(final Instruction inst) { final long relativeAddress = getAsLongSX(imm); jumpAddress = rip + relativeAddress; } else if (inst.firstOperand() instanceof final IndirectOperand io) { - jumpAddress = computeIndirectOperand(io); + jumpAddress = computeIndirectOperand(io).address(); } else { throw new IllegalStateException(); } @@ -557,7 +560,7 @@ private void jumpTo(final long offset) { } private void op(final IndirectOperand iop, final Register8 op2, final BiFunction task) { - final long address = computeIndirectOperand(iop); + final MemoryAddress address = computeIndirectOperand(iop); op(() -> mem.read(address), () -> rf.get(op2), task, result -> mem.write(address, result), true); } @@ -568,8 +571,8 @@ private void op(final Register8 op1, final Register8 op2, final BiFunction rf.set(op1, result), true, - (a, b) -> false, - (a, b) -> false); + (_, _) -> false, + (_, _) -> false); } private void op( @@ -742,7 +745,7 @@ private void push(final long value) { } rf.set(Register64.RSP, newRSP); - mem.write(newRSP, value); + mem.write(new MemoryAddress(newRSP), value); } /** @@ -758,7 +761,7 @@ private long pop() { throw new StackUnderflow(); } - final long value = mem.read8(rsp); + final long value = mem.read8(new MemoryAddress(rsp)); // the stack "grows downward", so it "pops upward" final long newRSP = rsp + 8L; @@ -777,7 +780,7 @@ private int getAsIntZX(final Operand op) { } private int getAsIntZX(final IndirectOperand io) { - final long address = computeIndirectOperand(io); + final MemoryAddress address = computeIndirectOperand(io); return switch (io.getPointerSize()) { case BYTE_PTR -> BitUtils.asInt(mem.read(address)); case WORD_PTR -> BitUtils.asInt(mem.read2(address)); @@ -811,7 +814,7 @@ private long getAsLongSX(final Immediate imm) { /** Returns a sign-extended long. */ private long getAsLongSX(final IndirectOperand io) { - final long address = computeIndirectOperand(io); + final MemoryAddress address = computeIndirectOperand(io); if (io.getPointerSize() == PointerSize.QWORD_PTR) { return mem.read8(address); } else { @@ -825,7 +828,7 @@ private long getAsLongSX(final IndirectOperand io) { * @param io The indirect operand pointing at the memory. * @return The pointed address. */ - public long computeIndirectOperand(final IndirectOperand io) { + public MemoryAddress computeIndirectOperand(final IndirectOperand io) { final long base = io.hasBase() ? (io.getBase() instanceof Register64 ? rf.get((Register64) io.getBase()) @@ -838,7 +841,7 @@ public long computeIndirectOperand(final IndirectOperand io) { : 0L; final long scale = io.hasScale() ? io.getScale() : 1L; final long displacement = io.hasDisplacement() ? io.getDisplacement() : 0L; - return base + index * scale + displacement; + return new MemoryAddress(base + index * scale + displacement); } @Override diff --git a/core/src/test/java/com/ledmington/emu/TestExecution.java b/core/src/test/java/com/ledmington/emu/TestExecution.java index ab93639a..8c0c36b7 100644 --- a/core/src/test/java/com/ledmington/emu/TestExecution.java +++ b/core/src/test/java/com/ledmington/emu/TestExecution.java @@ -43,6 +43,7 @@ import com.ledmington.cpu.x86.Register8; import com.ledmington.cpu.x86.SegmentRegister; import com.ledmington.mem.Memory; +import com.ledmington.mem.MemoryAddress; import com.ledmington.mem.MemoryController; import com.ledmington.utils.BitUtils; @@ -131,20 +132,20 @@ void setup() { cpu = X86Cpu.builder() .memory(new MemoryController(new Memory() { @Override - public byte read(final long address) { - throw new UnsupportedOperationException(String.format("Attempted read at 0x%016x", address)); + public byte read(final MemoryAddress address) { + throw new UnsupportedOperationException(String.format("Attempted read at %s.", address)); } @Override - public void write(final long address, final byte value) { + public void write(final MemoryAddress address, final byte value) { throw new UnsupportedOperationException( - String.format("Attempted write of 0x%02x at 0x%016x", value, address)); + String.format("Attempted write of 0x%02x at %s.", value, address)); } @Override - public boolean isInitialized(final long address) { + public boolean isInitialized(final MemoryAddress address) { throw new UnsupportedOperationException( - String.format("Attempted initialization check at 0x%016x", address)); + String.format("Attempted initialization check at %s.", address)); } })) .checkInstructions() @@ -570,7 +571,7 @@ void movsxd(final Register64 r1, final Register32 r2) { @ParameterizedTest @MethodSource("r16m16") void lea16(final Register16 dest, final IndirectOperand src) { - final short address = BitUtils.asShort(cpu.computeIndirectOperand(src)); + final short address = BitUtils.asShort(cpu.computeIndirectOperand(src).address()); final X86RegisterFile expected = new X86RegisterFile(cpu.getRegisters()); expected.set(dest, address); cpu.executeOne(new GeneralInstruction(Opcode.LEA, dest, src)); @@ -583,7 +584,7 @@ void lea16(final Register16 dest, final IndirectOperand src) { @ParameterizedTest @MethodSource("r32m32") void lea32(final Register32 dest, final IndirectOperand src) { - final int address = BitUtils.asInt(cpu.computeIndirectOperand(src)); + final int address = BitUtils.asInt(cpu.computeIndirectOperand(src).address()); final X86RegisterFile expected = new X86RegisterFile(cpu.getRegisters()); expected.set(dest, address); cpu.executeOne(new GeneralInstruction(Opcode.LEA, dest, src)); @@ -596,7 +597,7 @@ void lea32(final Register32 dest, final IndirectOperand src) { @ParameterizedTest @MethodSource("r64m64") void lea64(final Register64 dest, final IndirectOperand src) { - final long address = cpu.computeIndirectOperand(src); + final long address = cpu.computeIndirectOperand(src).address(); final X86RegisterFile expected = new X86RegisterFile(cpu.getRegisters()); expected.set(dest, address); cpu.executeOne(new GeneralInstruction(Opcode.LEA, dest, src)); diff --git a/core/src/test/java/com/ledmington/emu/TestExecutionWithMemory.java b/core/src/test/java/com/ledmington/emu/TestExecutionWithMemory.java index 194f26f2..1517522e 100644 --- a/core/src/test/java/com/ledmington/emu/TestExecutionWithMemory.java +++ b/core/src/test/java/com/ledmington/emu/TestExecutionWithMemory.java @@ -42,6 +42,7 @@ import com.ledmington.cpu.x86.Opcode; import com.ledmington.cpu.x86.PointerSize; import com.ledmington.cpu.x86.Register64; +import com.ledmington.mem.MemoryAddress; import com.ledmington.mem.MemoryController; import com.ledmington.mem.MemoryInitializer; import com.ledmington.mem.PagedMemory; @@ -95,8 +96,8 @@ void movMem64R64(final Register64 r1, final Register64 r2) { final long oldValue2 = rng.nextLong(); set(cpu, r1, oldValue1); set(cpu, r2, oldValue2); - mem.initialize(oldValue1, 8L, (byte) 0x00); - mem.setPermissions(oldValue1, 8L, false, true, false); + mem.initialize(new MemoryAddress(oldValue1), 8L, (byte) 0x00); + mem.setPermissions(new MemoryAddress(oldValue1), 8L, false, true, false); cpu.executeOne(new GeneralInstruction( Opcode.MOV, IndirectOperand.builder() @@ -104,8 +105,8 @@ void movMem64R64(final Register64 r1, final Register64 r2) { .base(r1) .build(), r2)); - mem.setPermissions(oldValue1, 8L, true, false, false); - final long x = mem.read8(oldValue1); + mem.setPermissions(new MemoryAddress(oldValue1), 8L, true, false, false); + final long x = mem.read8(new MemoryAddress(oldValue1)); assertEquals( oldValue2, x, @@ -124,8 +125,8 @@ void movR64Mem64(final Register64 r1, final Register64 r2) { final long oldValue2 = rng.nextLong(); set(cpu, r2, oldValue2); final long val = rng.nextLong(); - mem.initialize(oldValue2, val); - mem.setPermissions(oldValue2, 8L, true, false, false); + mem.initialize(new MemoryAddress(oldValue2), val); + mem.setPermissions(new MemoryAddress(oldValue2), 8L, true, false, false); cpu.executeOne(new GeneralInstruction( Opcode.MOV, r1, @@ -133,7 +134,7 @@ void movR64Mem64(final Register64 r1, final Register64 r2) { .base(r2) .pointer(PointerSize.QWORD_PTR) .build())); - final long x = mem.read8(oldValue2); + final long x = mem.read8(new MemoryAddress(oldValue2)); assertEquals( val, x, @@ -149,9 +150,9 @@ void push() { .build(); final long base = rng.nextLong(); - mem.setPermissions(base - 8L, base, true, true, false); + mem.setPermissions(new MemoryAddress(base - 8L), base, true, true, false); set(cpu, Register64.RSP, base); - mem.write(base - 8L, 0L); + mem.write(new MemoryAddress(base - 8L), 0L); final long val = rng.nextLong(); set(cpu, Register64.RAX, val); @@ -166,8 +167,8 @@ void push() { newRSP, cpu.getRegisters().get(Register64.RSP))); assertEquals( val, - mem.read8(newRSP), - () -> String.format("Expected 0x%016x but was 0x%016x.", val, mem.read8(newRSP))); + mem.read8(new MemoryAddress(newRSP)), + () -> String.format("Expected 0x%016x but was 0x%016x.", val, mem.read8(new MemoryAddress(newRSP)))); } @Test @@ -185,8 +186,8 @@ void pushOnFullStack() { .build(); set(cpu, Register64.RSP, stackTop); - mem.setPermissions(stackBottom, stackSize, true, true, false); - mem.initialize(stackBottom, stackSize, (byte) 0x00); + mem.setPermissions(new MemoryAddress(stackBottom), stackSize, true, true, false); + mem.initialize(new MemoryAddress(stackBottom), stackSize, (byte) 0x00); final long val = rng.nextLong(); set(cpu, Register64.RAX, val); @@ -220,10 +221,10 @@ void pop() { .stackSize(stackSize) .build(); - mem.setPermissions(stackTop, stackSize, true, true, false); + mem.setPermissions(new MemoryAddress(stackTop), stackSize, true, true, false); set(cpu, Register64.RSP, stackTop); final long val = rng.nextLong(); - mem.write(stackTop, val); + mem.write(new MemoryAddress(stackTop), val); cpu.executeOne(new GeneralInstruction(Opcode.POP, Register64.RAX)); @@ -251,8 +252,8 @@ void popOnEmptyStack() { final long stackBottom = stackTop - stackSize; set(cpu, Register64.RSP, stackTop); - mem.setPermissions(stackBottom, stackSize, true, true, false); - mem.initialize(stackBottom, stackSize, (byte) 0x00); + mem.setPermissions(new MemoryAddress(stackBottom), stackSize, true, true, false); + mem.initialize(new MemoryAddress(stackBottom), stackSize, (byte) 0x00); final Instruction pop = new GeneralInstruction(Opcode.POP, Register64.RAX); assertThrows( @@ -289,7 +290,7 @@ void pushAndPop(final int n) { // Doing n pushes of random values for (int i = 0; i < n; i++) { // Set memory to readable and writable - mem.setPermissions(stackTop - 8L * (i + 1), 8L, true, true, false); + mem.setPermissions(new MemoryAddress(stackTop - 8L * (i + 1)), 8L, true, true, false); set(cpu, Register64.RAX, values[i]); @@ -361,8 +362,8 @@ void pushAndPop(final int n) { private void writeFunction(final long functionAddress, final Instruction... code) { final byte[] functionCode = InstructionEncoder.toHex(true, code); - mem.initialize(functionAddress, functionCode); - mem.setPermissions(functionAddress, functionCode.length, false, false, true); + mem.initialize(new MemoryAddress(functionAddress), functionCode); + mem.setPermissions(new MemoryAddress(functionAddress), functionCode.length, false, false, true); } @Test @@ -379,7 +380,7 @@ void callAndReturn() { .stackSize(stackSize) .build(); - mem.setPermissions(stackBottom, stackSize, true, true, false); + mem.setPermissions(new MemoryAddress(stackBottom), stackSize, true, true, false); set(cpu, Register64.RSP, stackTop); // Setup RIP at random location @@ -416,8 +417,8 @@ void callAllocateAndReturn() { .stackSize(stackSize) .build(); - mem.initialize(stackBottom, stackSize, (byte) 0); - mem.setPermissions(stackBottom, stackSize, true, true, false); + mem.initialize(new MemoryAddress(stackBottom), stackSize, (byte) 0); + mem.setPermissions(new MemoryAddress(stackBottom), stackSize, true, true, false); set(cpu, Register64.RSP, stackTop); set(cpu, Register64.RBP, stackTop); diff --git a/emudb/src/main/java/com/ledmington/emudb/EmuDB.java b/emudb/src/main/java/com/ledmington/emudb/EmuDB.java index 148c90ab..09f804dd 100644 --- a/emudb/src/main/java/com/ledmington/emudb/EmuDB.java +++ b/emudb/src/main/java/com/ledmington/emudb/EmuDB.java @@ -55,6 +55,7 @@ import com.ledmington.emu.RegisterFile; import com.ledmington.emu.X86Cpu; import com.ledmington.mem.Memory; +import com.ledmington.mem.MemoryAddress; import com.ledmington.mem.MemoryController; import com.ledmington.mem.MemoryInitializer; import com.ledmington.mem.PagedMemory; @@ -151,7 +152,7 @@ private void showStack() { pos.functionName(), TerminalUtils.ANSI_RESET); - final long nextRbp = this.context.memory().read8(rbp); + final long nextRbp = this.context.memory().read8(new MemoryAddress(rbp)); // TODO: should we check if nextRbp is aligned? if (nextRbp <= rbp || nextRbp > stackTop || nextRbp < stackBottom) { // out.println(""); @@ -410,7 +411,9 @@ private void showMemory(final String... args) { currentAddress, TerminalUtils.ANSI_RESET); } - final String s = mem.isInitialized(currentAddress) ? String.format("%02x", mem.read(currentAddress)) : "xx"; + final String s = mem.isInitialized(new MemoryAddress(currentAddress)) + ? String.format("%02x", mem.read(new MemoryAddress(currentAddress))) + : "xx"; out.printf(currentAddress == address ? "[" + s + "]" : " " + s + " "); if (i % numBytesPerRow == numBytesPerRow - 1) { out.println(); diff --git a/emudb/src/main/java/com/ledmington/emudb/MemoryByteBuffer.java b/emudb/src/main/java/com/ledmington/emudb/MemoryByteBuffer.java index a6fb4768..bcc86d17 100644 --- a/emudb/src/main/java/com/ledmington/emudb/MemoryByteBuffer.java +++ b/emudb/src/main/java/com/ledmington/emudb/MemoryByteBuffer.java @@ -20,6 +20,7 @@ import java.util.Objects; import com.ledmington.mem.Memory; +import com.ledmington.mem.MemoryAddress; import com.ledmington.utils.ReadOnlyByteBuffer; import com.ledmington.utils.SuppressFBWarnings; @@ -66,6 +67,6 @@ public long getPosition() { @Override public byte read() { - return memory.read(position); + return memory.read(new MemoryAddress(position)); } } diff --git a/gui/src/main/java/com/ledmington/view/EmulatorView.java b/gui/src/main/java/com/ledmington/view/EmulatorView.java index 2191c771..6df1d956 100644 --- a/gui/src/main/java/com/ledmington/view/EmulatorView.java +++ b/gui/src/main/java/com/ledmington/view/EmulatorView.java @@ -55,6 +55,7 @@ import com.ledmington.emu.InstructionFetcher; import com.ledmington.emu.RFlags; import com.ledmington.emu.RegisterFile; +import com.ledmington.mem.MemoryAddress; import com.ledmington.mem.MemoryController; import com.ledmington.mem.PagedMemory; import com.ledmington.utils.MiniLogger; @@ -296,7 +297,7 @@ private void updateCode() { instString = "(bad)"; rip = startRIP + 1L; } catch (final DecodingException e) { - instString = String.format(".byte 0x%02x", this.mem.readCode(rip)); + instString = String.format(".byte 0x%02x", this.mem.readCode(new MemoryAddress(rip))); rip = startRIP + 1L; } sb.append(" : ").append(instString).append('\n'); @@ -316,8 +317,8 @@ private void updateMemory(final long baseAddress) { .append(String.format("%0" + (2 * ADDRESS_BYTES) + "x", address)) .append(" :"); } - if (this.mem.isInitialized(address)) { - sb.append(String.format(" %02x", this.mem.read(address))); + if (this.mem.isInitialized(new MemoryAddress(address))) { + sb.append(String.format(" %02x", this.mem.read(new MemoryAddress(address)))); } else { sb.append(" xx"); } diff --git a/mem/src/jmh/java/com/ledmingtom/mem/BenchPagedMemory.java b/mem/src/jmh/java/com/ledmingtom/mem/BenchPagedMemory.java index 9e7c31fd..b196422f 100644 --- a/mem/src/jmh/java/com/ledmingtom/mem/BenchPagedMemory.java +++ b/mem/src/jmh/java/com/ledmingtom/mem/BenchPagedMemory.java @@ -52,17 +52,17 @@ public class BenchPagedMemory { public BenchPagedMemory() { for (int i = 0; i < numBytes; i++) { - mem.write(startAddress + i, (byte) 0x99); + mem.write(new MemoryAddress(startAddress + i), (byte) 0x99); } } @Benchmark public void readInitializedAddress() { - mem.read(rng.nextLong(startAddress, startAddress + numBytes)); + mem.read(new MemoryAddress(rng.nextLong(startAddress, startAddress + numBytes))); } @Benchmark public void readUninitializedAddress() { - mem.read(rng.nextLong(Long.MIN_VALUE, -startAddress)); + mem.read(new MemoryAddress(rng.nextLong(Long.MIN_VALUE, -startAddress))); } } diff --git a/mem/src/jmh/java/com/ledmingtom/mem/BenchRandomAccessMemory.java b/mem/src/jmh/java/com/ledmingtom/mem/BenchRandomAccessMemory.java index 025733dc..ececc9b9 100644 --- a/mem/src/jmh/java/com/ledmingtom/mem/BenchRandomAccessMemory.java +++ b/mem/src/jmh/java/com/ledmingtom/mem/BenchRandomAccessMemory.java @@ -52,17 +52,17 @@ public class BenchRandomAccessMemory { public BenchRandomAccessMemory() { for (int i = 0; i < numBytes; i++) { - mem.write(startAddress + i, (byte) 0x99); + mem.write(new MemoryAddress(startAddress + i), (byte) 0x99); } } @Benchmark public void readInitializedAddress() { - mem.read(rng.nextLong(startAddress, startAddress + numBytes)); + mem.read(new MemoryAddress(rng.nextLong(startAddress, startAddress + numBytes))); } @Benchmark public void readUninitializedAddress() { - mem.read(rng.nextLong(Long.MIN_VALUE, -startAddress)); + mem.read(new MemoryAddress(rng.nextLong(Long.MIN_VALUE, -startAddress))); } } diff --git a/mem/src/main/java/com/ledmington/mem/MemoryAddress.java b/mem/src/main/java/com/ledmington/mem/MemoryAddress.java index 01b933d6..89364f97 100644 --- a/mem/src/main/java/com/ledmington/mem/MemoryAddress.java +++ b/mem/src/main/java/com/ledmington/mem/MemoryAddress.java @@ -17,8 +17,19 @@ */ package com.ledmington.mem; -/** A 64-bit memory address. */ +/** + * A 64-bit memory address. + * + * @param address The value of the address. + */ public record MemoryAddress(long address) implements Comparable { + + /** + * Adds the given offset to this MemoryAddress, creating a new one. + * + * @param offset The offset to add. + * @return A new instance of MemoryAddress. + */ public MemoryAddress plus(final long offset) { return new MemoryAddress(address + offset); } diff --git a/mem/src/test/java/com/ledmington/mem/TestMemory.java b/mem/src/test/java/com/ledmington/mem/TestMemory.java index 036b268e..be0f0277 100644 --- a/mem/src/test/java/com/ledmington/mem/TestMemory.java +++ b/mem/src/test/java/com/ledmington/mem/TestMemory.java @@ -37,12 +37,15 @@ abstract sealed class TestMemory permits TestMemoryController, TestRandomAccessM protected abstract Memory getMemory(); protected static Stream randomMemoryLocations() { - return Stream.generate(rng::nextLong).distinct().limit(100).map(Arguments::of); + return Stream.generate(() -> new MemoryAddress(rng.nextLong())) + .distinct() + .limit(100) + .map(Arguments::of); } @ParameterizedTest @MethodSource("randomMemoryLocations") - void singleByte(final long address) { + void singleByte(final MemoryAddress address) { final Memory mem = getMemory(); final byte value = 0x12; mem.write(address, value); @@ -52,7 +55,7 @@ void singleByte(final long address) { @ParameterizedTest @MethodSource("randomMemoryLocations") - void twoBytes(final long address) { + void twoBytes(final MemoryAddress address) { final Memory mem = getMemory(); final short value = 0x1234; mem.write(address, value); @@ -62,7 +65,7 @@ void twoBytes(final long address) { @ParameterizedTest @MethodSource("randomMemoryLocations") - void fourBytes(final long address) { + void fourBytes(final MemoryAddress address) { final Memory mem = getMemory(); final int value = 0x12345678; mem.write(address, value); @@ -72,7 +75,7 @@ void fourBytes(final long address) { @ParameterizedTest @MethodSource("randomMemoryLocations") - void eightBytes(final long address) { + void eightBytes(final MemoryAddress address) { final Memory mem = getMemory(); final long value = 0x0102030405060708L; mem.write(address, value); diff --git a/mem/src/test/java/com/ledmington/mem/TestMemoryController.java b/mem/src/test/java/com/ledmington/mem/TestMemoryController.java index 564ad912..7b49bdce 100644 --- a/mem/src/test/java/com/ledmington/mem/TestMemoryController.java +++ b/mem/src/test/java/com/ledmington/mem/TestMemoryController.java @@ -36,7 +36,7 @@ protected Memory getMemory() { @ParameterizedTest @MethodSource("randomMemoryLocations") - void granularPermissions(final long address) { + void granularPermissions(final MemoryAddress address) { final MemoryController mem = new MemoryController(new RandomAccessMemory(MemoryInitializer.random()), true, false); @@ -52,7 +52,7 @@ void granularPermissions(final long address) { void unalignedMultiByteRead(final long numBytes) { final MemoryController mem = new MemoryController(new RandomAccessMemory(MemoryInitializer.random()), true, true); - final long address = rng.nextLong(); + final MemoryAddress address = new MemoryAddress(rng.nextLong()); mem.setPermissions(address, numBytes, true, false, false); mem.initialize(address, 8, (byte) 0x00); assertThrows(IllegalReadException.class, () -> mem.read8(address)); @@ -63,7 +63,7 @@ void unalignedMultiByteRead(final long numBytes) { void unalignedMultiByteWrite(final long numBytes) { final MemoryController mem = new MemoryController(new RandomAccessMemory(MemoryInitializer.random()), true, true); - final long address = rng.nextLong(); + final MemoryAddress address = new MemoryAddress(rng.nextLong()); mem.setPermissions(address, numBytes, false, true, false); mem.initialize(address, 8, (byte) 0x00); assertThrows(IllegalWriteException.class, () -> mem.write(address, 0L)); diff --git a/mem/src/test/java/com/ledmington/mem/TestMemoryInitializer.java b/mem/src/test/java/com/ledmington/mem/TestMemoryInitializer.java index cc7c580e..fda9fe65 100644 --- a/mem/src/test/java/com/ledmington/mem/TestMemoryInitializer.java +++ b/mem/src/test/java/com/ledmington/mem/TestMemoryInitializer.java @@ -37,12 +37,13 @@ final class TestMemoryInitializer { void zero() { final Memory mem = new RandomAccessMemory(MemoryInitializer.zero()); for (int i = 0; i < 100; i++) { - final long address = rng.nextLong(); + final MemoryAddress address = new MemoryAddress(rng.nextLong()); assertEquals( (byte) 0x00, mem.read(address), () -> String.format( - "Expected read at 0x%016x to return 0 but was 0x%02x.", address, mem.read(address))); + "Expected read at 0x%016x to return 0 but was 0x%02x.", + address.address(), mem.read(address))); } } @@ -50,7 +51,7 @@ void zero() { void random() { final Memory mem = new RandomAccessMemory(MemoryInitializer.random()); assertTrue( - Stream.generate(rng::nextLong) + Stream.generate(() -> new MemoryAddress(rng.nextLong())) .map(mem::read) .limit(100) .collect(Collectors.toSet()) @@ -62,7 +63,7 @@ void random() { @Test void randomReturnsDifferentValueAtSamePlace() { final Memory mem = new RandomAccessMemory(MemoryInitializer.random()); - final long address = rng.nextLong(); + final MemoryAddress address = new MemoryAddress(rng.nextLong()); assertNotEquals(mem.read(address), mem.read(address)); } } diff --git a/mem/src/test/java/com/ledmington/mem/TestUninitializedMemory.java b/mem/src/test/java/com/ledmington/mem/TestUninitializedMemory.java index f1ab013c..57e06ed8 100644 --- a/mem/src/test/java/com/ledmington/mem/TestUninitializedMemory.java +++ b/mem/src/test/java/com/ledmington/mem/TestUninitializedMemory.java @@ -40,64 +40,74 @@ final class TestUninitializedMemory { @BeforeEach public void setup() { - // Creating a memory controller with all permissions on the whole range, but without initialising anything + // Creating a memory controller with all permissions on the whole range, but without initializing anything mem = new MemoryController(new RandomAccessMemory(MemoryInitializer.random()), true, true, true, true, true); } @Test void cannotRead() { - final Set positions = - Stream.generate(rng::nextLong).distinct().limit(100).collect(Collectors.toSet()); + final Set positions = Stream.generate(() -> new MemoryAddress(rng.nextLong())) + .distinct() + .limit(100) + .collect(Collectors.toSet()); - for (final long address : positions) { + for (final MemoryAddress address : positions) { assertThrows(AccessToUninitializedMemoryException.class, () -> mem.read(address)); } } @Test void canReadAfterInitialization() { - final Set positions = - Stream.generate(rng::nextLong).distinct().limit(100).collect(Collectors.toSet()); + final Set positions = Stream.generate(() -> new MemoryAddress(rng.nextLong())) + .distinct() + .limit(100) + .collect(Collectors.toSet()); - for (final long address : positions) { + for (final MemoryAddress address : positions) { mem.write(address, (byte) 0); } - for (final long address : positions) { + for (final MemoryAddress address : positions) { assertDoesNotThrow(() -> mem.read(address)); } } @Test void cannotExecute() { - final Set positions = - Stream.generate(rng::nextLong).distinct().limit(100).collect(Collectors.toSet()); + final Set positions = Stream.generate(() -> new MemoryAddress(rng.nextLong())) + .distinct() + .limit(100) + .collect(Collectors.toSet()); - for (final long address : positions) { + for (final MemoryAddress address : positions) { assertThrows(AccessToUninitializedMemoryException.class, () -> mem.readCode(address)); } } @Test void canExecuteAfterInitialization() { - final Set positions = - Stream.generate(rng::nextLong).distinct().limit(100).collect(Collectors.toSet()); + final Set positions = Stream.generate(() -> new MemoryAddress(rng.nextLong())) + .distinct() + .limit(100) + .collect(Collectors.toSet()); - for (final long address : positions) { + for (final MemoryAddress address : positions) { mem.write(address, (byte) 0); } - for (final long address : positions) { + for (final MemoryAddress address : positions) { assertDoesNotThrow(() -> mem.readCode(address)); } } @Test void canWrite() { - final Set positions = - Stream.generate(rng::nextLong).distinct().limit(100).collect(Collectors.toSet()); + final Set positions = Stream.generate(() -> new MemoryAddress(rng.nextLong())) + .distinct() + .limit(100) + .collect(Collectors.toSet()); - for (final long address : positions) { + for (final MemoryAddress address : positions) { assertDoesNotThrow(() -> mem.write(address, (byte) 0)); } } From 43a1b0775e4554466df16ea632e258d5b991b9c8 Mon Sep 17 00:00:00 2001 From: Ledmington Date: Mon, 4 May 2026 22:38:29 +0200 Subject: [PATCH 5/5] PMD fixes --- core/src/main/java/com/ledmington/emu/ELFLoader.java | 1 + core/src/main/java/com/ledmington/emu/X86Cpu.java | 2 +- gui/src/main/java/com/ledmington/view/EmulatorView.java | 9 +++++---- .../main/java/com/ledmington/mem/MemoryController.java | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/ledmington/emu/ELFLoader.java b/core/src/main/java/com/ledmington/emu/ELFLoader.java index 8cdfa37c..08678cf7 100644 --- a/core/src/main/java/com/ledmington/emu/ELFLoader.java +++ b/core/src/main/java/com/ledmington/emu/ELFLoader.java @@ -490,6 +490,7 @@ private List getAuxiliaryVector(final ELF elf) { new AuxiliaryEntry(AuxiliaryEntryType.AT_EGID, os.getEffectiveGroupID())); } + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") private void loadSegments(final ProgramHeaderTable pht, final long baseAddress) { logger.debug("Loading ELF segments into memory"); diff --git a/core/src/main/java/com/ledmington/emu/X86Cpu.java b/core/src/main/java/com/ledmington/emu/X86Cpu.java index edbac559..3a7f4f8f 100644 --- a/core/src/main/java/com/ledmington/emu/X86Cpu.java +++ b/core/src/main/java/com/ledmington/emu/X86Cpu.java @@ -144,7 +144,7 @@ public void executeOne() { } @Override - @SuppressWarnings({"PMD.NcssCount", "PMD.CognitiveComplexity"}) + @SuppressWarnings({"PMD.NcssCount", "PMD.CognitiveComplexity", "PMD.AvoidInstantiatingObjectsInLoops"}) public void executeOne(final Instruction inst) { assertIsRunning(); diff --git a/gui/src/main/java/com/ledmington/view/EmulatorView.java b/gui/src/main/java/com/ledmington/view/EmulatorView.java index 6df1d956..bd1fcf50 100644 --- a/gui/src/main/java/com/ledmington/view/EmulatorView.java +++ b/gui/src/main/java/com/ledmington/view/EmulatorView.java @@ -306,19 +306,20 @@ private void updateCode() { regFile.set(Register64.RIP, originalRIP); } + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") private void updateMemory(final long baseAddress) { final StringBuilder sb = new StringBuilder(); final int n = AppConstants.getMaxMemoryLines(); final int k = AppConstants.getMemoryBytesPerLine(); for (int i = 0; i < n * k; i++) { - final long address = baseAddress + (long) i * k; + final MemoryAddress address = new MemoryAddress(baseAddress + (long) i * k); if (i % k == 0) { sb.append("0x") - .append(String.format("%0" + (2 * ADDRESS_BYTES) + "x", address)) + .append(String.format("%0" + (2 * ADDRESS_BYTES) + "x", address.address())) .append(" :"); } - if (this.mem.isInitialized(new MemoryAddress(address))) { - sb.append(String.format(" %02x", this.mem.read(new MemoryAddress(address)))); + if (this.mem.isInitialized(address)) { + sb.append(String.format(" %02x", this.mem.read(address))); } else { sb.append(" xx"); } diff --git a/mem/src/main/java/com/ledmington/mem/MemoryController.java b/mem/src/main/java/com/ledmington/mem/MemoryController.java index db81ac7a..77caa595 100644 --- a/mem/src/main/java/com/ledmington/mem/MemoryController.java +++ b/mem/src/main/java/com/ledmington/mem/MemoryController.java @@ -141,7 +141,7 @@ private String reportIllegalAccess( final Consumer printer = x -> { final String s = isInitialized(x) ? String.format("%02x", mem.read(x)) : "xx"; - sb.append(x == address ? '[' : ' '); + sb.append(x.equals(address) ? '[' : ' '); // Print the bytes accessed in bold if (x.compareTo(address) >= 0 && x.compareTo(address.plus(length)) < 0) {