Little bit of documentation and dead code removal.
This commit is contained in:
@@ -44,14 +44,64 @@ public interface MemoryMap {
|
||||
*/
|
||||
void removeDevice(final MemoryMappedDevice device);
|
||||
|
||||
/**
|
||||
* Returns the memory range the specified device currently occupies in this mapping, if any.
|
||||
*
|
||||
* @param device the device to get the memory range for.
|
||||
* @return the range the device occupies, if it is in this mapping.
|
||||
*/
|
||||
Optional<MemoryRange> getMemoryRange(final MemoryMappedDevice device);
|
||||
|
||||
/**
|
||||
* Returns the memory range that the specified address fall into, if any.
|
||||
* <p>
|
||||
* This is useful for getting a direct reference to a {@link MemoryMappedDevice} at
|
||||
* a specific memory location, usually to perform multiple read or write operations
|
||||
* on it without having to go through the slower {@link #load(int, int)} and
|
||||
* {@link #store(int, int, int)} calls.
|
||||
*
|
||||
* @param address the address to get a memory range for.
|
||||
* @return the memory range the address falls into, if any.
|
||||
*/
|
||||
@Nullable
|
||||
MemoryRange getMemoryRange(final int address);
|
||||
|
||||
/**
|
||||
* Marks a location in memory dirty.
|
||||
* <p>
|
||||
* This may be called by systems in parallel to performing actual store operations
|
||||
* directly on {@link MemoryMappedDevice}s.
|
||||
*
|
||||
* @param range the memory range in which data has changed.
|
||||
* @param offset the offset inside that memory range at which data has changed.
|
||||
*/
|
||||
void setDirty(final MemoryRange range, final int offset);
|
||||
|
||||
/**
|
||||
* Reads a value from the specified physical address.
|
||||
* <p>
|
||||
* When performing many operations on an address range that is known to be occupied by
|
||||
* a single device, it is more efficient to obtain a reference to that device via
|
||||
* {@link #getMemoryRange(int)} instead and operate on that device directly.
|
||||
*
|
||||
* @param address the physical address to read a value from.
|
||||
* @param sizeLog2 the size of the value to read. See {@link li.cil.circuity.api.vm.device.memory.Sizes}.
|
||||
* @return the value read from the specified location.
|
||||
* @throws MemoryAccessException if an error occurred accessing the memory a the specified location.
|
||||
*/
|
||||
int load(final int address, final int sizeLog2) throws MemoryAccessException;
|
||||
|
||||
/**
|
||||
* Writes a value to the specified physical address.
|
||||
* <p>
|
||||
* When performing many operations on an address range that is known to be occupied by
|
||||
* a single device, it is more efficient to obtain a reference to that device via
|
||||
* {@link #getMemoryRange(int)} instead and operate on that device directly.
|
||||
*
|
||||
* @param address the physical address to write a value to.
|
||||
* @param value the value to write to the specified location.
|
||||
* @param sizeLog2 the size of the value to write. See {@link li.cil.circuity.api.vm.device.memory.Sizes}.
|
||||
* @throws MemoryAccessException if an error occurred accessing the memory a the specified location.
|
||||
*/
|
||||
void store(final int address, final int value, final int sizeLog2) throws MemoryAccessException;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,24 @@ import li.cil.circuity.api.vm.device.memory.MemoryMappedDevice;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a segment of memory mapped by a {@link MemoryMap}.
|
||||
*/
|
||||
public final class MemoryRange {
|
||||
/**
|
||||
* The device assigned to this memory range.
|
||||
*/
|
||||
public final MemoryMappedDevice device;
|
||||
public final int start, end; // both are inclusive
|
||||
|
||||
/**
|
||||
* The first byte-aligned address inside this memory range (inclusive).
|
||||
*/
|
||||
public final int start;
|
||||
|
||||
/**
|
||||
* The last byte-aligned address inside this memory range (inclusive).
|
||||
*/
|
||||
public final int end;
|
||||
|
||||
public MemoryRange(final MemoryMappedDevice device, final int start, final int end) {
|
||||
if (Integer.compareUnsigned(start, end) > 0) {
|
||||
@@ -22,18 +37,42 @@ public final class MemoryRange {
|
||||
this(device, address, address + device.getLength() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The address of this memory range.
|
||||
* <p>
|
||||
* This is the same as {@link #start}.
|
||||
*
|
||||
* @return the address of this memory range.
|
||||
*/
|
||||
public int address() {
|
||||
return start;
|
||||
}
|
||||
|
||||
/**
|
||||
* The size of this memory range, in bytes.
|
||||
*
|
||||
* @return the size of this memory range.
|
||||
*/
|
||||
public final int size() {
|
||||
return end - start + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified address is contained within this memory range.
|
||||
*
|
||||
* @param address the address to check for.
|
||||
* @return {@code true} if the address falls into this memory range; {@code false} otherwise.
|
||||
*/
|
||||
public boolean contains(final int address) {
|
||||
return Integer.compareUnsigned(address, start) >= 0 && Integer.compareUnsigned(address, end) <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified memory range intersects with this memory range.
|
||||
*
|
||||
* @param other the memory range to check for.
|
||||
* @return {@code true} if the memory range intersects this memory range; {@code false} otherwise.
|
||||
*/
|
||||
public boolean intersects(final MemoryRange other) {
|
||||
return Integer.compareUnsigned(start, other.end) <= 0 && Integer.compareUnsigned(end, other.start) >= 0;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,14 @@ package li.cil.circuity.api.vm.device;
|
||||
|
||||
import li.cil.circuity.api.vm.Interrupt;
|
||||
|
||||
/**
|
||||
* An interrupt source is a device that can raise and lower interrupts in an {@link InterruptController}.
|
||||
*/
|
||||
public interface InterruptSource extends Device {
|
||||
/**
|
||||
* Returns all interrupts used by this device.
|
||||
*
|
||||
* @return the interrupts used by this device.
|
||||
*/
|
||||
Iterable<Interrupt> getInterrupts();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
package li.cil.circuity.api.vm.device;
|
||||
|
||||
/**
|
||||
* Steppable devices can be advanced by some number of <em>cycles</em>.
|
||||
* <p>
|
||||
* Cycles in this context are an abstract concept and interpretation is up to the device
|
||||
* implementing this interface. This can be actual emulated clock cycles, number of
|
||||
* instructions processed or simply a generic value representing time.
|
||||
* <p>
|
||||
* Devices implementing this must not make any assumptions on the number of cycles
|
||||
* they are allowed at a single time. This number may change wildly between calls.
|
||||
* However, while they should not, devices may run for more cycles than specified.
|
||||
*/
|
||||
public interface Steppable extends Device {
|
||||
/**
|
||||
* Advance this device by the given number of cycles.
|
||||
*
|
||||
* @param cycles the number of cycles to advance the device by.
|
||||
*/
|
||||
void step(final int cycles);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
package li.cil.circuity.api.vm.device.memory;
|
||||
|
||||
/**
|
||||
* Base class for all memory related exceptions.
|
||||
* <p>
|
||||
* This exception may be thrown whenever memory mapped in a {@link li.cil.circuity.api.vm.MemoryMap}
|
||||
* is accessed, specifically any {@link MemoryMappedDevice} may throw these exceptions to signal an
|
||||
* invalid access.
|
||||
*/
|
||||
public class MemoryAccessException extends Exception {
|
||||
private final int address;
|
||||
|
||||
|
||||
@@ -8,15 +8,56 @@ import li.cil.circuity.api.vm.device.Device;
|
||||
* so that they can be accessed via a memory range using the same mechanisms used for accessing RAM.
|
||||
*/
|
||||
public interface MemoryMappedDevice extends Device {
|
||||
/**
|
||||
* The number of bytes this device occupies in memory.
|
||||
* <p>
|
||||
* This is used by a {@link MemoryMap} to compute the {@link li.cil.circuity.api.vm.MemoryRange} the
|
||||
* device will occupy.
|
||||
*
|
||||
* @return the size of the device in bytes.
|
||||
*/
|
||||
int getLength();
|
||||
|
||||
/**
|
||||
* Returns a bitmask indicating the value sizes supported by this device.
|
||||
* <p>
|
||||
* Code accessing a memory mapped device, e.g. {@link MemoryMap}s, may use this to verify
|
||||
* load and store requests before calling {@link #load(int, int)} and {@link #store(int, int, int)}
|
||||
* on a device.
|
||||
*
|
||||
* @return a bit mask indicating the supported value sizes.
|
||||
*/
|
||||
default int getSupportedSizes() {
|
||||
return (1 << Sizes.SIZE_8_LOG2) |
|
||||
(1 << Sizes.SIZE_16_LOG2) |
|
||||
(1 << Sizes.SIZE_32_LOG2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a value from this device.
|
||||
* <p>
|
||||
* Most devices that are not {@link PhysicalMemory} will cause side-effects from
|
||||
* having certain areas of their memory written to.
|
||||
*
|
||||
* @param offset the offset local to the device to read from.
|
||||
* @param sizeLog2 the size of the value to read, log2. See {@link Sizes}.
|
||||
* @return the value read from the device.
|
||||
* @throws MemoryAccessException if there was an error accessing the data in this device.
|
||||
* @see Sizes
|
||||
*/
|
||||
int load(final int offset, final int sizeLog2) throws MemoryAccessException;
|
||||
|
||||
/**
|
||||
* Writes a value to this device.
|
||||
* <p>
|
||||
* Most devices that are not {@link PhysicalMemory} may cause side-effects from
|
||||
* having certain areas of their memory written to.
|
||||
*
|
||||
* @param offset the offset local to the device to write to.
|
||||
* @param value the value to write to the device.
|
||||
* @param sizeLog2 the size of the value to write, log2. See {@link Sizes}.
|
||||
* @throws MemoryAccessException if there was an error accessing the data in this device.
|
||||
* @see Sizes
|
||||
*/
|
||||
void store(final int offset, final int value, final int sizeLog2) throws MemoryAccessException;
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package li.cil.circuity.api.vm.device.memory;
|
||||
|
||||
public interface MemoryStream {
|
||||
boolean canLoad16();
|
||||
|
||||
short load16() throws MemoryAccessException;
|
||||
|
||||
boolean canLoad32();
|
||||
|
||||
int load32() throws MemoryAccessException;
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
package li.cil.circuity.api.vm.device.memory;
|
||||
|
||||
import li.cil.circuity.api.vm.MemoryMap;
|
||||
|
||||
/**
|
||||
* Instances marked with this interface can be treated as random-access memory.
|
||||
* <p>
|
||||
* {@link MemoryMap}s may use this to decide whether a memory
|
||||
* region can be stored in a translation lookaside buffer.
|
||||
* For example, CPUs may use this to decide whether a memory region can be stored
|
||||
* in a translation look-aside buffer.
|
||||
* <p>
|
||||
* In particular, implementing this interface communicates that values written to
|
||||
* this device can be read back from the same address, and that the {@link li.cil.circuity.api.vm.MemoryRange}
|
||||
* they occupy can be used as a continuous whole, without any inaccessible areas in
|
||||
* it.
|
||||
*/
|
||||
public interface PhysicalMemory extends MemoryMappedDevice {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package li.cil.circuity.api.vm.device.memory;
|
||||
|
||||
/**
|
||||
* Constants for different value sizes.
|
||||
* <p>
|
||||
* These constants are named by their bit width.
|
||||
*/
|
||||
public final class Sizes {
|
||||
public static final int SIZE_8 = 8;
|
||||
public static final int SIZE_16 = 16;
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.lwjgl.glfw.GLFW;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class RISCVTestScreen extends Screen {
|
||||
@@ -34,6 +35,7 @@ public final class RISCVTestScreen extends Screen {
|
||||
private final Terminal terminal = new Terminal();
|
||||
private VirtualMachineRunner runner;
|
||||
private UART16550A uart;
|
||||
private VirtIOBlockDevice hdd;
|
||||
|
||||
private VirtIOConsoleDevice console;
|
||||
private VirtIOKeyboardDevice keyboard;
|
||||
@@ -70,6 +72,14 @@ public final class RISCVTestScreen extends Screen {
|
||||
public void removed() {
|
||||
super.removed();
|
||||
|
||||
if (hdd != null) {
|
||||
try {
|
||||
hdd.close();
|
||||
} catch (final IOException e) {
|
||||
LOGGER.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (minecraft != null) {
|
||||
minecraft.keyboardListener.enableRepeatEvents(false);
|
||||
}
|
||||
@@ -160,7 +170,7 @@ public final class RISCVTestScreen extends Screen {
|
||||
final R5Board board = new R5Board();
|
||||
final PhysicalMemory rom = Memory.create(128 * 1024);
|
||||
final PhysicalMemory memory = Memory.create(32 * 1014 * 1024);
|
||||
final VirtIOBlockDevice hdd = new VirtIOBlockDevice(board.getMemoryMap(), ByteBufferBlockDevice.createFromFile(rootfsFile, true));
|
||||
hdd = new VirtIOBlockDevice(board.getMemoryMap(), ByteBufferBlockDevice.createFromFile(rootfsFile, true));
|
||||
uart = new UART16550A();
|
||||
console = new VirtIOConsoleDevice(board.getMemoryMap());
|
||||
keyboard = new VirtIOKeyboardDevice(board.getMemoryMap());
|
||||
|
||||
@@ -11,6 +11,14 @@ import li.cil.circuity.api.vm.device.memory.Sizes;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Implements a 16550A UART.
|
||||
* <p>
|
||||
* This is not a cycle-correct implementation. It does not care about baudrates and
|
||||
* timeout delays. But it's good enough to pump data into and out of a virtual machine.
|
||||
* <p>
|
||||
* See: https://web.archive.org/web/20200207194832/https://www.lammertbies.nl/comm/info/serial-uart
|
||||
*/
|
||||
@SuppressWarnings("PointlessBitwiseExpression")
|
||||
public final class UART16550A implements Resettable, Steppable, MemoryMappedDevice, InterruptSource {
|
||||
private static final int UART_RBR_OFFSET = 0; // Receive buffer register (Read-only)
|
||||
|
||||
@@ -9,6 +9,9 @@ import li.cil.circuity.vm.device.memory.exception.StoreFaultException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* Simple physical memory implementation backed by a {@link ByteBuffer}.
|
||||
*/
|
||||
public class ByteBufferMemory implements PhysicalMemory {
|
||||
private final ByteBuffer data;
|
||||
|
||||
@@ -26,40 +29,38 @@ public class ByteBufferMemory implements PhysicalMemory {
|
||||
|
||||
@Override
|
||||
public int load(final int offset, final int sizeLog2) throws MemoryAccessException {
|
||||
try {
|
||||
switch (sizeLog2) {
|
||||
case Sizes.SIZE_8_LOG2:
|
||||
return data.get(offset);
|
||||
case Sizes.SIZE_16_LOG2:
|
||||
return data.getShort(offset);
|
||||
case Sizes.SIZE_32_LOG2:
|
||||
return data.getInt(offset);
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
if (offset < 0 || offset >= data.limit()) {
|
||||
throw new LoadFaultException(offset);
|
||||
}
|
||||
switch (sizeLog2) {
|
||||
case Sizes.SIZE_8_LOG2:
|
||||
return data.get(offset);
|
||||
case Sizes.SIZE_16_LOG2:
|
||||
return data.getShort(offset);
|
||||
case Sizes.SIZE_32_LOG2:
|
||||
return data.getInt(offset);
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(final int offset, final int value, final int sizeLog2) throws MemoryAccessException {
|
||||
try {
|
||||
switch (sizeLog2) {
|
||||
case Sizes.SIZE_8_LOG2:
|
||||
data.put(offset, (byte) value);
|
||||
break;
|
||||
case Sizes.SIZE_16_LOG2:
|
||||
data.putShort(offset, (short) value);
|
||||
break;
|
||||
case Sizes.SIZE_32_LOG2:
|
||||
data.putInt(offset, value);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
if (offset < 0 || offset >= data.limit()) {
|
||||
throw new StoreFaultException(offset);
|
||||
}
|
||||
switch (sizeLog2) {
|
||||
case Sizes.SIZE_8_LOG2:
|
||||
data.put(offset, (byte) value);
|
||||
break;
|
||||
case Sizes.SIZE_16_LOG2:
|
||||
data.putShort(offset, (short) value);
|
||||
break;
|
||||
case Sizes.SIZE_32_LOG2:
|
||||
data.putInt(offset, value);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user