mirror of
https://github.com/simon987/Much-Assembly-Required.git
synced 2025-04-10 14:26:45 +00:00
Interrupts WIP #230
This commit is contained in:
parent
51c0d4c3c7
commit
b1d7121b22
@ -45,6 +45,11 @@ public class CPU implements MongoSerializable {
|
||||
*/
|
||||
private int codeSectionOffset;
|
||||
|
||||
private int interruptVectorTableOffset;
|
||||
private final int graceInstructionCount;
|
||||
private int graceInstructionLeft;
|
||||
private boolean isGracePeriod;
|
||||
|
||||
/**
|
||||
* Instruction pointer, always points to the next instruction
|
||||
*/
|
||||
@ -57,14 +62,10 @@ public class CPU implements MongoSerializable {
|
||||
|
||||
private int registerSetSize;
|
||||
|
||||
private static final char EXECUTION_COST_ADDR = 0x0050;
|
||||
private static final char EXECUTED_INS_ADDR = 0x0051;
|
||||
|
||||
public CPU() {
|
||||
instructionSet = new DefaultInstructionSet();
|
||||
registerSet = new DefaultRegisterSet();
|
||||
codeSectionOffset = GameServer.INSTANCE.getConfig().getInt("org_offset");
|
||||
private static final char EXECUTION_COST_ADDR = 0x0150;
|
||||
private static final char EXECUTED_INS_ADDR = 0x0151;
|
||||
|
||||
private void addInstructions() {
|
||||
instructionSet.add(new JmpInstruction(this));
|
||||
instructionSet.add(new JnzInstruction(this));
|
||||
instructionSet.add(new JzInstruction(this));
|
||||
@ -91,7 +92,20 @@ public class CPU implements MongoSerializable {
|
||||
instructionSet.add(new PopfInstruction(this));
|
||||
instructionSet.add(new JnaInstruction(this));
|
||||
instructionSet.add(new JaInstruction(this));
|
||||
instructionSet.add(new IntInstruction(this));
|
||||
instructionSet.add(new IretInstruction(this));
|
||||
}
|
||||
|
||||
public CPU() {
|
||||
instructionSet = new DefaultInstructionSet();
|
||||
registerSet = new DefaultRegisterSet();
|
||||
IServerConfiguration config = GameServer.INSTANCE.getConfig();
|
||||
codeSectionOffset = config.getInt("org_offset");
|
||||
graceInstructionCount = config.getInt("grace_instruction_count");
|
||||
|
||||
interruptVectorTableOffset = config.getInt("ivt_offset");
|
||||
|
||||
addInstructions();
|
||||
status = new Status();
|
||||
}
|
||||
|
||||
@ -102,41 +116,32 @@ public class CPU implements MongoSerializable {
|
||||
instructionSet = new DefaultInstructionSet();
|
||||
registerSet = new DefaultRegisterSet();
|
||||
codeSectionOffset = config.getInt("org_offset");
|
||||
graceInstructionCount = config.getInt("grace_instruction_count");
|
||||
|
||||
instructionSet.add(new JmpInstruction(this));
|
||||
instructionSet.add(new JnzInstruction(this));
|
||||
instructionSet.add(new JzInstruction(this));
|
||||
instructionSet.add(new JgInstruction(this));
|
||||
instructionSet.add(new JgeInstruction(this));
|
||||
instructionSet.add(new JleInstruction(this));
|
||||
instructionSet.add(new JlInstruction(this));
|
||||
instructionSet.add(new PushInstruction(this));
|
||||
instructionSet.add(new PopInstruction(this));
|
||||
instructionSet.add(new CallInstruction(this));
|
||||
instructionSet.add(new RetInstruction(this));
|
||||
instructionSet.add(new MulInstruction(this));
|
||||
instructionSet.add(new DivInstruction(this));
|
||||
instructionSet.add(new JnsInstruction(this));
|
||||
instructionSet.add(new JsInstruction(this));
|
||||
instructionSet.add(new HwiInstruction(this));
|
||||
instructionSet.add(new HwqInstruction(this));
|
||||
instructionSet.add(new XchgInstruction(this));
|
||||
instructionSet.add(new JcInstruction(this));
|
||||
instructionSet.add(new JncInstruction(this));
|
||||
instructionSet.add(new JnoInstruction(this));
|
||||
instructionSet.add(new JoInstruction(this));
|
||||
instructionSet.add(new PushfInstruction(this));
|
||||
instructionSet.add(new PopfInstruction(this));
|
||||
instructionSet.add(new JnaInstruction(this));
|
||||
instructionSet.add(new JaInstruction(this));
|
||||
interruptVectorTableOffset = config.getInt("ivt_offset");
|
||||
|
||||
addInstructions();
|
||||
|
||||
status = new Status();
|
||||
memory = new Memory(config.getInt("memory_size"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the IP to IVT + number and pushes flags then the old IP
|
||||
*/
|
||||
public void interrupt(int number) {
|
||||
Instruction push = instructionSet.get(PushInstruction.OPCODE);
|
||||
push.execute(status.toWord(), status);
|
||||
push.execute(ip, status);
|
||||
|
||||
this.setIp((char) memory.get(interruptVectorTableOffset + number));
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
status.clear();
|
||||
ip = codeSectionOffset;
|
||||
graceInstructionLeft = graceInstructionCount;
|
||||
isGracePeriod = false;
|
||||
}
|
||||
|
||||
public int execute(int timeout) {
|
||||
@ -151,17 +156,16 @@ public class CPU implements MongoSerializable {
|
||||
while (!status.isBreakFlag()) {
|
||||
counter++;
|
||||
|
||||
if (counter % 10000 == 0) {
|
||||
if (System.currentTimeMillis() > (startTime + timeout)) {
|
||||
LogManager.LOGGER.fine("CPU Timeout " + this + " after " + counter + "instructions (" + timeout + "ms): " + (double) counter / ((double) timeout / 1000) / 1000000 + "MHz");
|
||||
|
||||
//Write execution cost and instruction count to memory
|
||||
memory.set(EXECUTION_COST_ADDR, timeout);
|
||||
memory.set(EXECUTED_INS_ADDR, Util.getHigherWord(counter));
|
||||
memory.set(EXECUTED_INS_ADDR + 1, Util.getLowerWord(counter));
|
||||
|
||||
if (isGracePeriod) {
|
||||
if (graceInstructionLeft-- == 0) {
|
||||
writeExecutionStats(timeout, counter);
|
||||
return timeout;
|
||||
}
|
||||
} else if (counter % 10000 == 0) {
|
||||
if (System.currentTimeMillis() > (startTime + timeout)) {
|
||||
interrupt(32);
|
||||
isGracePeriod = true;
|
||||
}
|
||||
}
|
||||
|
||||
//fetch instruction
|
||||
@ -184,15 +188,17 @@ public class CPU implements MongoSerializable {
|
||||
|
||||
// LogManager.LOGGER.fine(counter + " instruction in " + elapsed + "ms : " + (double) counter / (elapsed / 1000) / 1000000 + "MHz");
|
||||
|
||||
|
||||
//Write execution cost and instruction count to memory
|
||||
memory.set(EXECUTION_COST_ADDR, elapsed);
|
||||
memory.set(EXECUTED_INS_ADDR, Util.getHigherWord(counter));
|
||||
memory.set(EXECUTED_INS_ADDR + 1, Util.getLowerWord(counter));
|
||||
writeExecutionStats(elapsed, counter);
|
||||
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
private void writeExecutionStats(int timeout, int counter) {
|
||||
memory.set(EXECUTION_COST_ADDR, timeout);
|
||||
memory.set(EXECUTED_INS_ADDR, Util.getHigherWord(counter));
|
||||
memory.set(EXECUTED_INS_ADDR + 1, Util.getLowerWord(counter));
|
||||
}
|
||||
|
||||
public void executeInstruction(Instruction instruction, int source, int destination) {
|
||||
|
||||
//Execute the instruction
|
||||
|
@ -59,7 +59,6 @@ public abstract class Instruction {
|
||||
*/
|
||||
public Status execute(Target dst, int dstIndex, int src, Status status) {
|
||||
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -133,7 +133,7 @@ public class Status implements Cloneable {
|
||||
this.errorFlag = errorFlag;
|
||||
}
|
||||
|
||||
public char toByte() {
|
||||
public char toWord() {
|
||||
char stat = 0;
|
||||
stat = (char) (stat | ((signFlag ? 1 : 0) << 3));
|
||||
stat = (char) (stat | ((zeroFlag ? 1 : 0) << 2));
|
||||
@ -142,7 +142,7 @@ public class Status implements Cloneable {
|
||||
return stat;
|
||||
}
|
||||
|
||||
public void fromByte(char stat) {
|
||||
public void fromWord(char stat) {
|
||||
setSignFlag((stat & (1 << 3)) != 0);
|
||||
setZeroFlag((stat & (1 << 2)) != 0);
|
||||
setCarryFlag((stat & (1 << 1)) != 0);
|
||||
|
@ -0,0 +1,35 @@
|
||||
package net.simon987.mar.server.assembly.instruction;
|
||||
|
||||
import net.simon987.mar.server.assembly.*;
|
||||
|
||||
/**
|
||||
* Software Interrupt
|
||||
* Pushes the flags register and the IP to the stack then
|
||||
* Sets the IP to the CPU [ivt_offset + src].
|
||||
*/
|
||||
public class IntInstruction extends Instruction {
|
||||
|
||||
public static final int OPCODE = 48;
|
||||
private final CPU cpu;
|
||||
|
||||
public IntInstruction(CPU cpu) {
|
||||
super("int", OPCODE);
|
||||
this.cpu = cpu;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status execute(int src, Status status) {
|
||||
cpu.interrupt(src);
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean operandsValid(Operand o1, Operand o2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean operandValid(Operand o1) {
|
||||
return o1.getType() == OperandType.IMMEDIATE16 && o1.getData() < 256;
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package net.simon987.mar.server.assembly.instruction;
|
||||
|
||||
import net.simon987.mar.server.assembly.*;
|
||||
|
||||
/**
|
||||
* Interrupt Return
|
||||
*
|
||||
* Pops the IP and status flag from the stack.
|
||||
*/
|
||||
public class IretInstruction extends Instruction {
|
||||
|
||||
public static final int OPCODE = 49;
|
||||
private final CPU cpu;
|
||||
|
||||
public IretInstruction(CPU cpu) {
|
||||
super("iret", OPCODE);
|
||||
this.cpu = cpu;
|
||||
}
|
||||
|
||||
public Status execute(Status status) {
|
||||
RegisterSet reg = cpu.getRegisterSet();
|
||||
|
||||
cpu.setIp((char) cpu.getMemory().get(reg.get(7))); //IP = (SP + 0)
|
||||
status.fromWord((char) cpu.getMemory().get(reg.get(7) + 1)); //Status = (SP + 1)
|
||||
reg.set(7, reg.get(7) + 2); //Increment SP (stack grows towards smaller)
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean noOperandsValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean operandsValid(Operand o1, Operand o2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean operandValid(Operand o1) {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ public class PopfInstruction extends Instruction {
|
||||
char flags = (char) cpu.getMemory().get(sp.getValue());
|
||||
|
||||
// Overwrite the CPU flags
|
||||
status.fromByte(flags);
|
||||
status.fromWord(flags);
|
||||
|
||||
// Increment SP
|
||||
sp.setValue(sp.getValue() + 1);
|
||||
|
@ -28,7 +28,7 @@ public class PushfInstruction extends Instruction {
|
||||
sp.setValue(sp.getValue() - 1);
|
||||
|
||||
// Push the current flags
|
||||
cpu.getMemory().set(sp.getValue(), status.toByte());
|
||||
cpu.getMemory().set(sp.getValue(), status.toWord());
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -44,7 +44,9 @@ wg_createNewOnRequest=0
|
||||
|
||||
#CPU
|
||||
tick_length=1000
|
||||
org_offset=512
|
||||
org_offset=4096
|
||||
ivt_offset=0
|
||||
grace_instruction_count=10000
|
||||
stack_bottom=65536
|
||||
memory_size=65536
|
||||
user_timeout=100
|
||||
|
@ -8,8 +8,6 @@ import net.simon987.mar.server.assembly.exception.CancelledException;
|
||||
|
||||
public class TestHelper {
|
||||
|
||||
private static final int TIMEOUT = 100;
|
||||
|
||||
public static Assembler getTestAsm() {
|
||||
IServerConfiguration config = getTestConfig();
|
||||
CPU cpu = getTestCpu();
|
||||
@ -20,7 +18,9 @@ public class TestHelper {
|
||||
IServerConfiguration configuration = new FakeConfiguration();
|
||||
|
||||
configuration.setInt("memory_size", 65536);
|
||||
configuration.setInt("org_offset", 400);
|
||||
configuration.setInt("org_offset", 4096);
|
||||
configuration.setInt("ivt_offset", 0);
|
||||
configuration.setInt("grace_instruction_count", 10000);
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@ -36,6 +36,10 @@ public class TestHelper {
|
||||
}
|
||||
|
||||
public static TestExecutionResult executeCode(String code) {
|
||||
return executeCode(code, 100);
|
||||
}
|
||||
|
||||
public static TestExecutionResult executeCode(String code, int timeout) {
|
||||
AssemblyResult ar = getTestAsm().parse(code);
|
||||
CPU cpu = TestHelper.getTestCpu();
|
||||
|
||||
@ -50,7 +54,7 @@ public class TestHelper {
|
||||
cpu.setCodeSectionOffset(ar.getCodeSectionOffset());
|
||||
|
||||
cpu.reset();
|
||||
cpu.execute(TIMEOUT);
|
||||
cpu.execute(timeout);
|
||||
|
||||
return new TestExecutionResult(cpu.getState(), host.callHistory, ar);
|
||||
}
|
||||
|
@ -0,0 +1,48 @@
|
||||
package net.simon987.mar.server.assembly.instruction;
|
||||
|
||||
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 IntInstructionTest {
|
||||
|
||||
@Test
|
||||
public void intSimple1() {
|
||||
String code = "" +
|
||||
"isr: \n" +
|
||||
" MOV X, 0x1234 \n" +
|
||||
" IRET \n" +
|
||||
".text \n" +
|
||||
"MOV [32], isr \n" +
|
||||
"INT 32 \n" +
|
||||
"MOV Y, 0x4567 \n" +
|
||||
"brk \n";
|
||||
|
||||
TestExecutionResult res = TestHelper.executeCode(code);
|
||||
|
||||
assertEquals(0x1234, res.regValue("X"));
|
||||
assertEquals(0x4567, res.regValue("Y"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void intExecutionLimit() {
|
||||
String code = "" +
|
||||
"isr: \n" +
|
||||
" MOV X, 0x1234 \n" +
|
||||
" BRK \n" +
|
||||
".text \n" +
|
||||
"MOV [32], isr \n" +
|
||||
"loop: \n" +
|
||||
"ADD A, 1 \n" +
|
||||
"JMP loop \n" +
|
||||
"MOV Y, 0x4567 \n" +
|
||||
"brk \n";
|
||||
|
||||
TestExecutionResult res = TestHelper.executeCode(code, 10);
|
||||
|
||||
assertEquals(0x1234, res.regValue("X"));
|
||||
assertEquals(0, res.regValue("Y"));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user