mirror of
https://github.com/simon987/Much-Assembly-Required.git
synced 2025-04-19 10:36:43 +00:00
More refactoring, easier assembly testing (#227)
This commit is contained in:
parent
ba78d2fd93
commit
ad0124508c
@ -8,6 +8,7 @@ import net.simon987.mar.server.assembly.Assembler;
|
|||||||
import net.simon987.mar.server.assembly.AssemblyResult;
|
import net.simon987.mar.server.assembly.AssemblyResult;
|
||||||
import net.simon987.mar.server.assembly.CPU;
|
import net.simon987.mar.server.assembly.CPU;
|
||||||
import net.simon987.mar.server.assembly.exception.CancelledException;
|
import net.simon987.mar.server.assembly.exception.CancelledException;
|
||||||
|
import net.simon987.mar.server.event.CpuInitialisationEvent;
|
||||||
import net.simon987.mar.server.event.GameEvent;
|
import net.simon987.mar.server.event.GameEvent;
|
||||||
import net.simon987.mar.server.event.GameEventListener;
|
import net.simon987.mar.server.event.GameEventListener;
|
||||||
import net.simon987.mar.server.event.UserCreationEvent;
|
import net.simon987.mar.server.event.UserCreationEvent;
|
||||||
@ -55,13 +56,20 @@ public class UserCreationListener implements GameEventListener {
|
|||||||
|
|
||||||
//Create CPU
|
//Create CPU
|
||||||
try {
|
try {
|
||||||
cubot.setCpu(new CPU(GameServer.INSTANCE.getConfig(), cubot));
|
CPU cpu = new CPU(config);
|
||||||
|
cubot.setCpu(cpu);
|
||||||
cubot.getCpu().setHardwareHost(cubot);
|
cubot.getCpu().setHardwareHost(cubot);
|
||||||
user.setUserCode(config.getString("new_user_code"));
|
user.setUserCode(config.getString("new_user_code"));
|
||||||
|
|
||||||
|
GameEvent initEvent = new CpuInitialisationEvent(cpu, cubot);
|
||||||
|
GameServer.INSTANCE.getEventDispatcher().dispatch(event);
|
||||||
|
if (initEvent.isCancelled()) {
|
||||||
|
throw new CancelledException();
|
||||||
|
}
|
||||||
|
|
||||||
//Compile user code
|
//Compile user code
|
||||||
AssemblyResult ar = new Assembler(cubot.getCpu().getInstructionSet(), cubot.getCpu().getRegisterSet(),
|
AssemblyResult ar = new Assembler(cpu.getInstructionSet(), cpu.getRegisterSet(),
|
||||||
GameServer.INSTANCE.getConfig()).parse(user.getUserCode());
|
config).parse(user.getUserCode());
|
||||||
|
|
||||||
cubot.getCpu().getMemory().clear();
|
cubot.getCpu().getMemory().clear();
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ public class AssemblyResult {
|
|||||||
/**
|
/**
|
||||||
* A list of labels
|
* A list of labels
|
||||||
*/
|
*/
|
||||||
HashMap<String, Character> labels = new HashMap<>(20);
|
public HashMap<String, Character> labels = new HashMap<>(20);
|
||||||
/**
|
/**
|
||||||
* List of exceptions encountered during the assembly attempt,
|
* List of exceptions encountered during the assembly attempt,
|
||||||
* they will be displayed in the editor
|
* they will be displayed in the editor
|
||||||
|
@ -98,7 +98,7 @@ public class CPU implements MongoSerializable {
|
|||||||
/**
|
/**
|
||||||
* Creates a new CPU
|
* Creates a new CPU
|
||||||
*/
|
*/
|
||||||
public CPU(IServerConfiguration config, ControllableUnit unit) throws CancelledException {
|
public CPU(IServerConfiguration config) throws CancelledException {
|
||||||
instructionSet = new DefaultInstructionSet();
|
instructionSet = new DefaultInstructionSet();
|
||||||
registerSet = new DefaultRegisterSet();
|
registerSet = new DefaultRegisterSet();
|
||||||
codeSectionOffset = config.getInt("org_offset");
|
codeSectionOffset = config.getInt("org_offset");
|
||||||
@ -132,12 +132,6 @@ public class CPU implements MongoSerializable {
|
|||||||
|
|
||||||
status = new Status();
|
status = new Status();
|
||||||
memory = new Memory(config.getInt("memory_size"));
|
memory = new Memory(config.getInt("memory_size"));
|
||||||
|
|
||||||
GameEvent event = new CpuInitialisationEvent(this, unit);
|
|
||||||
GameServer.INSTANCE.getEventDispatcher().dispatch(event);
|
|
||||||
if (event.isCancelled()) {
|
|
||||||
throw new CancelledException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
@ -398,15 +392,20 @@ public class CPU implements MongoSerializable {
|
|||||||
|
|
||||||
public static CPU deserialize(Document obj, ControllableUnit unit) throws CancelledException {
|
public static CPU deserialize(Document obj, ControllableUnit unit) throws CancelledException {
|
||||||
|
|
||||||
CPU cpu = new CPU(GameServer.INSTANCE.getConfig(), unit);
|
CPU cpu = new CPU(GameServer.INSTANCE.getConfig());
|
||||||
|
|
||||||
cpu.codeSectionOffset = obj.getInteger("codeSegmentOffset");
|
cpu.codeSectionOffset = obj.getInteger("codeSegmentOffset");
|
||||||
|
|
||||||
cpu.memory = new Memory((Document) obj.get("memory"));
|
cpu.memory = new Memory((Document) obj.get("memory"));
|
||||||
cpu.registerSet = RegisterSet.deserialize((Document) obj.get("registerSet"));
|
cpu.registerSet = RegisterSet.deserialize((Document) obj.get("registerSet"));
|
||||||
|
|
||||||
return cpu;
|
GameEvent event = new CpuInitialisationEvent(cpu, unit);
|
||||||
|
GameServer.INSTANCE.getEventDispatcher().dispatch(event);
|
||||||
|
if (event.isCancelled()) {
|
||||||
|
throw new CancelledException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InstructionSet getInstructionSet() {
|
public InstructionSet getInstructionSet() {
|
||||||
@ -466,4 +465,15 @@ public class CPU implements MongoSerializable {
|
|||||||
public void setHardwareHost(HardwareHost hardwareHost) {
|
public void setHardwareHost(HardwareHost hardwareHost) {
|
||||||
this.hardwareHost = hardwareHost;
|
this.hardwareHost = hardwareHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For testing/debugging, this creates an copy (please be mindful of the memory usage)
|
||||||
|
*/
|
||||||
|
public CpuState getState() {
|
||||||
|
return new CpuState(
|
||||||
|
registerSet.clone(),
|
||||||
|
memory.clone(),
|
||||||
|
status.clone()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
16
src/main/java/net/simon987/mar/server/assembly/CpuState.java
Normal file
16
src/main/java/net/simon987/mar/server/assembly/CpuState.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package net.simon987.mar.server.assembly;
|
||||||
|
|
||||||
|
public class CpuState {
|
||||||
|
|
||||||
|
public RegisterSet registers;
|
||||||
|
|
||||||
|
public Memory memory;
|
||||||
|
|
||||||
|
public Status status;
|
||||||
|
|
||||||
|
public CpuState(RegisterSet registers, Memory memory, Status status) {
|
||||||
|
this.registers = registers;
|
||||||
|
this.memory = memory;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
@ -9,14 +9,13 @@ class DefaultRegisterSet extends RegisterSet {
|
|||||||
DefaultRegisterSet() {
|
DefaultRegisterSet() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
addRegister(1, new Register("A"));
|
put(1, new Register("A"));
|
||||||
addRegister(2, new Register("B"));
|
put(2, new Register("B"));
|
||||||
addRegister(3, new Register("C"));
|
put(3, new Register("C"));
|
||||||
addRegister(4, new Register("D"));
|
put(4, new Register("D"));
|
||||||
addRegister(5, new Register("X"));
|
put(5, new Register("X"));
|
||||||
addRegister(6, new Register("Y"));
|
put(6, new Register("Y"));
|
||||||
addRegister(7, new Register("SP"));
|
put(7, new Register("SP"));
|
||||||
addRegister(8, new Register("BP"));
|
put(8, new Register("BP"));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import java.util.zip.InflaterOutputStream;
|
|||||||
/**
|
/**
|
||||||
* Represents the available memory for a CPU in the game universe
|
* Represents the available memory for a CPU in the game universe
|
||||||
*/
|
*/
|
||||||
public class Memory implements Target, MongoSerializable {
|
public class Memory implements Target, MongoSerializable, Cloneable {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -105,6 +105,7 @@ public class Memory implements Target, MongoSerializable {
|
|||||||
address = (char) address;
|
address = (char) address;
|
||||||
|
|
||||||
if (address >= words.length) {
|
if (address >= words.length) {
|
||||||
|
Thread.dumpStack();
|
||||||
LogManager.LOGGER.info("DEBUG: Trying to set memory out of bounds: " + address);
|
LogManager.LOGGER.info("DEBUG: Trying to set memory out of bounds: " + address);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -127,7 +128,7 @@ public class Memory implements Target, MongoSerializable {
|
|||||||
int address = rand.nextInt(blockSize) + offset;
|
int address = rand.nextInt(blockSize) + offset;
|
||||||
|
|
||||||
// Checking here avoids having a protected area at the end of the address space
|
// Checking here avoids having a protected area at the end of the address space
|
||||||
if(address < words.length) {
|
if (address < words.length) {
|
||||||
|
|
||||||
// Calculate bitmask by left-shifting 1 by a random value between 0 and 15
|
// Calculate bitmask by left-shifting 1 by a random value between 0 and 15
|
||||||
int bitmask = 1 << rand.nextInt(16);
|
int bitmask = 1 << rand.nextInt(16);
|
||||||
@ -186,4 +187,12 @@ public class Memory implements Target, MongoSerializable {
|
|||||||
public char[] getWords() {
|
public char[] getWords() {
|
||||||
return words;
|
return words;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Memory clone() {
|
||||||
|
Memory memory = new Memory(words.length);
|
||||||
|
memory.words = new char[words.length];
|
||||||
|
System.arraycopy(memory.words, 0, words, 0, words.length);
|
||||||
|
return memory;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package net.simon987.mar.server.assembly;
|
|||||||
/**
|
/**
|
||||||
* Represents a register in a cpu
|
* Represents a register in a cpu
|
||||||
*/
|
*/
|
||||||
public class Register {
|
public class Register implements Cloneable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the register
|
* Name of the register
|
||||||
@ -47,4 +47,14 @@ public class Register {
|
|||||||
return name + "=" + value;
|
return name + "=" + value;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register clone() {
|
||||||
|
try {
|
||||||
|
return (Register) super.clone();
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,29 +2,23 @@ package net.simon987.mar.server.assembly;
|
|||||||
|
|
||||||
|
|
||||||
import net.simon987.mar.server.io.MongoSerializable;
|
import net.simon987.mar.server.io.MongoSerializable;
|
||||||
import net.simon987.mar.server.logging.LogManager;
|
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
import org.json.simple.JSONArray;
|
import org.json.simple.JSONArray;
|
||||||
import org.json.simple.JSONObject;
|
import org.json.simple.JSONObject;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set of registers for a CPU
|
* A set of registers for a CPU
|
||||||
*/
|
*/
|
||||||
public class RegisterSet implements Target, MongoSerializable {
|
public class RegisterSet implements Target, MongoSerializable, Cloneable {
|
||||||
|
|
||||||
/**
|
// TODO configurable number of registers
|
||||||
* List of registers
|
private static final int REG_COUNT = 8 + 1;
|
||||||
*/
|
|
||||||
private final HashMap<Integer, Register> registers = new HashMap<>(8);
|
|
||||||
|
|
||||||
|
private final Register[] registers = new Register[REG_COUNT];
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an empty Register set
|
|
||||||
*/
|
|
||||||
public RegisterSet() {
|
public RegisterSet() {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -40,9 +34,8 @@ public class RegisterSet implements Target, MongoSerializable {
|
|||||||
|
|
||||||
name = name.toUpperCase();
|
name = name.toUpperCase();
|
||||||
|
|
||||||
for (Integer i : registers.keySet()) {
|
for (int i = 1; i < REG_COUNT; i++) {
|
||||||
if (registers.get(i).getName().equals(name)) {
|
if (registers[i].getName().equals(name)) {
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,7 +49,7 @@ public class RegisterSet implements Target, MongoSerializable {
|
|||||||
* @param index index of the register
|
* @param index index of the register
|
||||||
*/
|
*/
|
||||||
public Register getRegister(int index) {
|
public Register getRegister(int index) {
|
||||||
return registers.get(index);
|
return registers[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,7 +61,8 @@ public class RegisterSet implements Target, MongoSerializable {
|
|||||||
|
|
||||||
name = name.toUpperCase();
|
name = name.toUpperCase();
|
||||||
|
|
||||||
for (Register r : registers.values()) {
|
for (Register r : registers) {
|
||||||
|
if (r == null) continue;
|
||||||
if (r.getName().equals(name)) {
|
if (r.getName().equals(name)) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -86,15 +80,11 @@ public class RegisterSet implements Target, MongoSerializable {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int get(int address) {
|
public int get(int address) {
|
||||||
|
if (address <= 0 || address >= REG_COUNT) {
|
||||||
Register register = registers.get(address);
|
|
||||||
|
|
||||||
if (register != null) {
|
|
||||||
return register.getValue();
|
|
||||||
} else {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return registers[address].getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -105,23 +95,17 @@ public class RegisterSet implements Target, MongoSerializable {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void set(int address, int value) {
|
public void set(int address, int value) {
|
||||||
|
if (address <= 0 || address >= REG_COUNT) {
|
||||||
Register register = registers.get(address);
|
return;
|
||||||
|
|
||||||
if (register != null) {
|
|
||||||
register.setValue(value);
|
|
||||||
} else {
|
|
||||||
LogManager.LOGGER.info("DEBUG: trying to set unknown reg index : " + address);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void put(int index, Register register) {
|
registers[address].setValue(value);
|
||||||
registers.put(index, register);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
for (Register register : registers.values()) {
|
for (Register r : registers) {
|
||||||
register.setValue(0);
|
if (r == null) continue;
|
||||||
|
r.setValue(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,24 +118,24 @@ public class RegisterSet implements Target, MongoSerializable {
|
|||||||
* @param index Index of the register
|
* @param index Index of the register
|
||||||
* @param reg Register to add
|
* @param reg Register to add
|
||||||
*/
|
*/
|
||||||
void addRegister(int index, Register reg) {
|
public void put(int index, Register reg) {
|
||||||
registers.put(index, reg);
|
registers[index] = reg;
|
||||||
}
|
}
|
||||||
|
|
||||||
int size() {
|
int size() {
|
||||||
return registers.size();
|
return REG_COUNT - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Document mongoSerialise() {
|
public Document mongoSerialise() {
|
||||||
List<Document> registers = new ArrayList<>();
|
List<Document> registers = new ArrayList<>();
|
||||||
for (Integer index : this.registers.keySet()) {
|
for (int i = 1; i < REG_COUNT; i++) {
|
||||||
Document register = new Document();
|
Document register = new Document();
|
||||||
|
|
||||||
register.put("index", index);
|
register.put("index", i);
|
||||||
register.put("name", getRegister(index).getName());
|
register.put("name", getRegister(i).getName());
|
||||||
register.put("value", (int) getRegister(index).getValue());
|
register.put("value", (int) getRegister(i).getValue());
|
||||||
|
|
||||||
registers.add(register);
|
registers.add(register);
|
||||||
}
|
}
|
||||||
@ -169,11 +153,12 @@ public class RegisterSet implements Target, MongoSerializable {
|
|||||||
List registers = (ArrayList) obj.get("registers");
|
List registers = (ArrayList) obj.get("registers");
|
||||||
|
|
||||||
for (Object sRegister : registers) {
|
for (Object sRegister : registers) {
|
||||||
|
if (sRegister == null) continue;
|
||||||
|
|
||||||
Register register = new Register((String) ((Document) sRegister).get("name"));
|
Register register = new Register((String) ((Document) sRegister).get("name"));
|
||||||
register.setValue(((Document) sRegister).getInteger("value"));
|
register.setValue(((Document) sRegister).getInteger("value"));
|
||||||
|
|
||||||
registerSet.registers.put(((Document) sRegister).getInteger("index"), register);
|
registerSet.put(((Document) sRegister).getInteger("index"), register);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +176,7 @@ public class RegisterSet implements Target, MongoSerializable {
|
|||||||
Register register = new Register((String) jsonRegister.get("name"));
|
Register register = new Register((String) jsonRegister.get("name"));
|
||||||
register.setValue((int) (long) jsonRegister.get("value"));
|
register.setValue((int) (long) jsonRegister.get("value"));
|
||||||
|
|
||||||
registerSet.registers.put((int) (long) jsonRegister.get("index"), register);
|
registerSet.put((int) (long) jsonRegister.get("index"), register);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,10 +187,20 @@ public class RegisterSet implements Target, MongoSerializable {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
String str = "";
|
String str = "";
|
||||||
|
|
||||||
for (Integer index : registers.keySet()) {
|
for (int i = 1; i < REG_COUNT; i++) {
|
||||||
str += index + " " + registers.get(index).getName() + "=" + Util.toHex(registers.get(index).getValue()) + "\n";
|
str += i + " " + getRegister(i).getName() + "=" + Util.toHex(getRegister(i).getValue()) + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RegisterSet clone() {
|
||||||
|
RegisterSet rs = new RegisterSet();
|
||||||
|
|
||||||
|
for (int i = 1; i < REG_COUNT; i++) {
|
||||||
|
rs.put(i, getRegister(i).clone());
|
||||||
|
}
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package net.simon987.mar.server.assembly;
|
|||||||
/**
|
/**
|
||||||
* Represents the state of the processor
|
* Represents the state of the processor
|
||||||
*/
|
*/
|
||||||
public class Status {
|
public class Status implements Cloneable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to true when the result of
|
* Set to true when the result of
|
||||||
@ -148,4 +148,14 @@ public class Status {
|
|||||||
setCarryFlag((stat & (1 << 1)) != 0);
|
setCarryFlag((stat & (1 << 1)) != 0);
|
||||||
setOverflowFlag((stat & 1) != 0);
|
setOverflowFlag((stat & 1) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status clone() {
|
||||||
|
try {
|
||||||
|
return (Status) super.clone();
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,17 +23,12 @@ public class HwiInstruction extends Instruction {
|
|||||||
@Override
|
@Override
|
||||||
public Status execute(Target src, int srcIndex, Status status) {
|
public Status execute(Target src, int srcIndex, Status status) {
|
||||||
status.setErrorFlag(cpu.getHardwareHost().hardwareInterrupt(src.get(srcIndex), cpu.getStatus()));
|
status.setErrorFlag(cpu.getHardwareHost().hardwareInterrupt(src.get(srcIndex), cpu.getStatus()));
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Status execute(int src, Status status) {
|
public Status execute(int src, Status status) {
|
||||||
|
|
||||||
status.setErrorFlag(cpu.getHardwareHost().hardwareInterrupt(src, cpu.getStatus()));
|
status.setErrorFlag(cpu.getHardwareHost().hardwareInterrupt(src, cpu.getStatus()));
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,5 +12,4 @@ public interface HardwareHost {
|
|||||||
boolean hardwareInterrupt(int address, Status status);
|
boolean hardwareInterrupt(int address, Status status);
|
||||||
|
|
||||||
int hardwareQuery(int address);
|
int hardwareQuery(int address);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
53
src/test/java/net/simon987/mar/server/FakeHardwareHost.java
Normal file
53
src/test/java/net/simon987/mar/server/FakeHardwareHost.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package net.simon987.mar.server;
|
||||||
|
|
||||||
|
import net.simon987.mar.server.assembly.CPU;
|
||||||
|
import net.simon987.mar.server.assembly.CpuState;
|
||||||
|
import net.simon987.mar.server.assembly.HardwareModule;
|
||||||
|
import net.simon987.mar.server.assembly.Status;
|
||||||
|
import net.simon987.mar.server.game.objects.HardwareHost;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public class FakeHardwareHost implements HardwareHost {
|
||||||
|
|
||||||
|
public final List<HwiCall> callHistory = new ArrayList<>();
|
||||||
|
private final CPU cpu;
|
||||||
|
|
||||||
|
public FakeHardwareHost(CPU cpu) {
|
||||||
|
this.cpu = cpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attachHardware(HardwareModule hardware, int address) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void detachHardware(int address) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hardwareInterrupt(int address, Status status) {
|
||||||
|
callHistory.add(new HwiCall(address, cpu.getState()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hardwareQuery(int address) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HwiCall {
|
||||||
|
public HwiCall(int address, CpuState state) {
|
||||||
|
this.address = address;
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int address;
|
||||||
|
public CpuState state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
|||||||
|
package net.simon987.mar.server;
|
||||||
|
|
||||||
|
import net.simon987.mar.server.assembly.AssemblyResult;
|
||||||
|
import net.simon987.mar.server.assembly.CpuState;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TestExecutionResult {
|
||||||
|
|
||||||
|
public CpuState state;
|
||||||
|
|
||||||
|
public List<FakeHardwareHost.HwiCall> hwiHistory;
|
||||||
|
|
||||||
|
private AssemblyResult ar;
|
||||||
|
|
||||||
|
public TestExecutionResult(CpuState state, List<FakeHardwareHost.HwiCall> hwiHistory, AssemblyResult ar) {
|
||||||
|
this.state = state;
|
||||||
|
this.hwiHistory = hwiHistory;
|
||||||
|
this.ar = ar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int regValue(String register) {
|
||||||
|
return state.registers.getRegister(register).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int labelOffset(String label) {
|
||||||
|
return ar.labels.get(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int memValue(int offset) {
|
||||||
|
return state.memory.get(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add more utility methods here
|
||||||
|
}
|
@ -74,5 +74,4 @@ public class RegisterSetTest {
|
|||||||
//Test unknown indexes
|
//Test unknown indexes
|
||||||
registerSet.set(3, 10);
|
registerSet.set(3, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,57 @@
|
|||||||
package net.simon987.mar.server.assembly;
|
package net.simon987.mar.server.assembly;
|
||||||
|
|
||||||
import net.simon987.mar.server.FakeConfiguration;
|
import net.simon987.mar.server.FakeConfiguration;
|
||||||
|
import net.simon987.mar.server.FakeHardwareHost;
|
||||||
import net.simon987.mar.server.IServerConfiguration;
|
import net.simon987.mar.server.IServerConfiguration;
|
||||||
|
import net.simon987.mar.server.TestExecutionResult;
|
||||||
|
import net.simon987.mar.server.assembly.exception.CancelledException;
|
||||||
|
|
||||||
class TestHelper {
|
public class TestHelper {
|
||||||
|
|
||||||
static Assembler getTestAsm() {
|
private static final int TIMEOUT = 100;
|
||||||
|
|
||||||
IServerConfiguration configuration = new FakeConfiguration();
|
public static Assembler getTestAsm() {
|
||||||
|
IServerConfiguration config = getTestConfig();
|
||||||
configuration.setInt("memory_size", 1000);
|
CPU cpu = getTestCpu();
|
||||||
configuration.setInt("org_offset", 400);
|
return new Assembler(cpu.getInstructionSet(), cpu.getRegisterSet(), config);
|
||||||
|
|
||||||
return new Assembler(new DefaultInstructionSet(), new DefaultRegisterSet(), configuration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IServerConfiguration getTestConfig() {
|
||||||
|
IServerConfiguration configuration = new FakeConfiguration();
|
||||||
|
|
||||||
|
configuration.setInt("memory_size", 65536);
|
||||||
|
configuration.setInt("org_offset", 400);
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CPU getTestCpu() {
|
||||||
|
|
||||||
|
CPU cpu = null;
|
||||||
|
try {
|
||||||
|
cpu = new CPU(getTestConfig());
|
||||||
|
} catch (CancelledException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return cpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TestExecutionResult executeCode(String code) {
|
||||||
|
AssemblyResult ar = getTestAsm().parse(code);
|
||||||
|
CPU cpu = TestHelper.getTestCpu();
|
||||||
|
|
||||||
|
FakeHardwareHost host = new FakeHardwareHost(cpu);
|
||||||
|
cpu.setHardwareHost(host);
|
||||||
|
|
||||||
|
cpu.getMemory().clear();
|
||||||
|
|
||||||
|
char[] assembledCode = ar.getWords();
|
||||||
|
|
||||||
|
cpu.getMemory().write((char) ar.origin, assembledCode, 0, assembledCode.length);
|
||||||
|
cpu.setCodeSectionOffset(ar.getCodeSectionOffset());
|
||||||
|
|
||||||
|
cpu.reset();
|
||||||
|
cpu.execute(TIMEOUT);
|
||||||
|
|
||||||
|
return new TestExecutionResult(cpu.getState(), host.callHistory, ar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,30 @@
|
|||||||
package net.simon987.mar.server.assembly.instruction;
|
package net.simon987.mar.server.assembly.instruction;
|
||||||
|
|
||||||
|
import net.simon987.mar.server.FakeConfiguration;
|
||||||
|
import net.simon987.mar.server.IServerConfiguration;
|
||||||
|
import net.simon987.mar.server.TestExecutionResult;
|
||||||
|
import net.simon987.mar.server.assembly.*;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
public class CallInstructionTest {
|
public class CallInstructionTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void callSimple1() {
|
||||||
|
String code = "" +
|
||||||
|
"my_routine: \n" +
|
||||||
|
" MOV X, 0x1234 \n" +
|
||||||
|
" RET \n" +
|
||||||
|
".text \n" +
|
||||||
|
"CALL my_routine \n" +
|
||||||
|
"MOV Y, 0x4567 \n" +
|
||||||
|
"brk \n";
|
||||||
|
|
||||||
|
TestExecutionResult res = TestHelper.executeCode(code);
|
||||||
|
|
||||||
|
assertEquals(0x1234, res.regValue("X"));
|
||||||
|
assertEquals(0x4567, res.regValue("Y"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package net.simon987.mar.server.assembly.instruction;
|
||||||
|
|
||||||
|
import net.simon987.mar.server.FakeHardwareHost;
|
||||||
|
import net.simon987.mar.server.TestExecutionResult;
|
||||||
|
import net.simon987.mar.server.assembly.TestHelper;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class HwiInstructionTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hwiSimple1() {
|
||||||
|
String code = "" +
|
||||||
|
"MOV A, 0x123 \n" +
|
||||||
|
"HWI 0x4567 \n" +
|
||||||
|
"MOV A, 0x789 \n" +
|
||||||
|
"brk \n";
|
||||||
|
|
||||||
|
TestExecutionResult res = TestHelper.executeCode(code);
|
||||||
|
|
||||||
|
FakeHardwareHost.HwiCall call = res.hwiHistory.get(0);
|
||||||
|
assertEquals(0x4567, call.address);
|
||||||
|
assertEquals(0x123, call.state.registers.getRegister("A").getValue());
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user