From 15ec7e8023c69827a146a2b92a62530ab13ceb8f Mon Sep 17 00:00:00 2001 From: Kljunas2 Date: Sat, 25 Jan 2025 22:25:11 +0100 Subject: [PATCH 01/12] Implement interrupts --- src/sic/asm/parsing/OperandParser.java | 2 +- src/sic/ast/instructions/InstructionF2n.java | 2 +- src/sic/common/Opcode.java | 80 ++++++++++ src/sic/sim/vm/Interrupt.java | 81 ++++++++++ src/sic/sim/vm/Machine.java | 154 ++++++++++++------- src/sic/sim/vm/Registers.java | 84 ++++++++-- tests/interrupts.asm | 77 ++++++++++ tests/interrupts_timer.asm | 54 +++++++ 8 files changed, 465 insertions(+), 69 deletions(-) create mode 100644 src/sic/sim/vm/Interrupt.java create mode 100644 tests/interrupts.asm create mode 100644 tests/interrupts_timer.asm diff --git a/src/sic/asm/parsing/OperandParser.java b/src/sic/asm/parsing/OperandParser.java index fcef247..0b9ea94 100644 --- a/src/sic/asm/parsing/OperandParser.java +++ b/src/sic/asm/parsing/OperandParser.java @@ -97,7 +97,7 @@ private Command parseF1(Location loc, String label, Mnemonic mnemonic) { } private Command parseF2n(Location loc, String label, Mnemonic mnemonic) throws AsmError { - int n = parser.readInt(0, 15); + int n = parser.readInt(0, 255); return new InstructionF2n(loc, label, mnemonic, n); } diff --git a/src/sic/ast/instructions/InstructionF2n.java b/src/sic/ast/instructions/InstructionF2n.java index 5a5d2c8..5014fe3 100644 --- a/src/sic/ast/instructions/InstructionF2n.java +++ b/src/sic/ast/instructions/InstructionF2n.java @@ -25,7 +25,7 @@ public String operandToString() { @Override public void emitRawCode(byte[] data, int loc) { - emitRawCode(data, loc, number, 0); + emitRawCode(data, loc, (number>>4)&0xf, number&0xf); } @Override diff --git a/src/sic/common/Opcode.java b/src/sic/common/Opcode.java index 31ef50b..c5a6d55 100644 --- a/src/sic/common/Opcode.java +++ b/src/sic/common/Opcode.java @@ -104,6 +104,86 @@ public class Opcode { "TD", null, "STSW", "SSK", "SIO", "HIO", "TIO", null }; + public static boolean isF1(int opcode) { + switch (opcode) { + case FLOAT: + case FIX: + case NORM: + case SIO: + case HIO: + case TIO: + return true; + } + return false; + } + + public static boolean isF2(int opcode) { + switch (opcode) { + case ADDR: + case SUBR: + case MULR: + case DIVR: + case COMPR: + case SHIFTL: + case SHIFTR: + case RMO: + case CLEAR: + case TIXR: + case SVC: + return true; + } + return false; + } + + public static boolean isF34(int opcode) { + switch (opcode) { + case STA: + case STX: + case STL: + case STCH: + case STB: + case STS: + case STF: + case STT: + case STSW: + case JEQ: + case JGT: + case JLT: + case J: + case RSUB: + case JSUB: + case LDA: + case LDX: + case LDL: + case LDCH: + case LDB: + case LDS: + case LDF: + case LDT: + case ADD: + case SUB: + case MUL: + case DIV: + case AND: + case OR: + case COMP: + case TIX: + case RD: + case WD: + case TD: + case ADDF: + case SUBF: + case MULF: + case DIVF: + case COMPF: + case LPS: + case STI: + case SSK: + return true; + } + return false; + } + public static String getName(int opcode) { // 0 <= opcode <= 256 return opcodeToNames[opcode >> 2]; diff --git a/src/sic/sim/vm/Interrupt.java b/src/sic/sim/vm/Interrupt.java new file mode 100644 index 0000000..0e069f2 --- /dev/null +++ b/src/sic/sim/vm/Interrupt.java @@ -0,0 +1,81 @@ +package sic.sim.vm; + +import sic.sim.breakpoints.ReadDataBreakpointException; +import sic.sim.breakpoints.WriteDataBreakpointException; + +public class Interrupt { + public enum IClass { + SVC(8), + PROGRAM(4), + TIMER(2), + IO(1); + + public final int value; + + IClass(int value) { + this.value = value; + } + } + + public IClass CLASS; + private int ICODE; + + public Interrupt(IClass iclass, int icode) { + CLASS = iclass; + ICODE = icode; + //System.out.printf("created interrupt %s, icode. 0x%x%n", CLASS.name(), ICODE); + } + + public static int getWorkArea(IClass CLASS) { + switch (CLASS) { + case IClass.SVC: + return 0x100; + case IClass.PROGRAM: + return 0x130; + case IClass.TIMER: + return 0x160; + case IClass.IO: + return 0x190; + } + return -1; + } + + private void saveRegisters(Registers registers, Memory memory) throws WriteDataBreakpointException { + int addr = Interrupt.getWorkArea(CLASS); + memory.setWord(addr+6, registers.getSW()); + memory.setWord(addr+9, registers.getPC()); + memory.setWord(addr+12, registers.getA()); + memory.setWord(addr+15, registers.getX()); + memory.setWord(addr+18, registers.getL()); + memory.setWord(addr+21, registers.getB()); + memory.setWord(addr+24, registers.getS()); + memory.setWord(addr+27, registers.getT()); + memory.setFloat(addr+30, registers.getF()); + } + + private void restoreRegisters(Registers registers, Memory memory) throws ReadDataBreakpointException { + int addr = Interrupt.getWorkArea(CLASS); + } + + public void trigger(Registers registers, Memory memory) throws ReadDataBreakpointException, WriteDataBreakpointException { + saveRegisters(registers, memory); + int addr = Interrupt.getWorkArea(CLASS); + //System.out.printf("triggering interrupt %s, icode: 0x%x, jumping to: 0x%6x%n", CLASS.name(), ICODE, addr); + registers.setSW(memory.getWord(addr)); + registers.setPC(memory.getWord(addr+3)); + } + + /* + public enum ProgramType { + ILLEG_INSTR, + PRIVILEGED_INSTR, + ADDR_OUT_OF_RANGE, + MEM_PROTECT, + OVERFLOW, + PAGE_FAULT, + SEG_FAULT, + SEG_PROTECT_VIOLATION, + SEG_LEN_EXCEEDED, + } + */ +} diff --git a/src/sic/sim/vm/Machine.java b/src/sic/sim/vm/Machine.java index 2d923dd..7975799 100644 --- a/src/sic/sim/vm/Machine.java +++ b/src/sic/sim/vm/Machine.java @@ -21,6 +21,13 @@ public class Machine { public final Memory memory; public final Devices devices; + + private int timer = 0; + private Interrupt svcInt = null; + private Interrupt programInt = null; + private Interrupt timerInt = null; + private Interrupt ioInt = null; + // ************ Statistics private int instructionCount; @@ -106,7 +113,7 @@ private boolean execF1(int opcode) { return true; } - private boolean execF2(int opcode, int operand) { + private boolean execF2(int opcode, int operand) throws ReadDataBreakpointException, WriteDataBreakpointException { // Format 2: OP o1, o2 - two 4-bit operands int o1 = (operand & 0xF0) >> 4; int o2 = (operand & 0x0F); @@ -122,15 +129,22 @@ private boolean execF2(int opcode, int operand) { registers.set(o2, registers.gets(o2) / divisor); } break; - case Opcode.COMPR: registers.setSWAfterCompare(registers.gets(o1) - registers.gets(o2)); break; + case Opcode.COMPR: registers.setCC(registers.gets(o1) - registers.gets(o2)); break; case Opcode.SHIFTL: registers.set(o1, registers.get(o1) << (o2 + 1) | registers.get(o1) >> (24 - o2 - 1)); break; case Opcode.SHIFTR: registers.set(o1, registers.gets(o1) >> (o2 + 1)); break; case Opcode.RMO: registers.set(o2, registers.get(o1)); break; case Opcode.CLEAR: registers.set(o1, 0); break; case Opcode.TIXR: registers.setX(registers.getX()+1); - registers.setSWAfterCompare(registers.getXs() - registers.gets(o1)); + registers.setCC(registers.getXs() - registers.gets(o1)); + break; + case Opcode.SVC: + if (!registers.intEnabled(Interrupt.IClass.SVC)) { + Logger.fmterr("SVC is disabled"); + break; + } + registers.setICODE(operand); + svcInt = new Interrupt(Interrupt.IClass.SVC, operand); break; - case Opcode.SVC: notImplemented("SVC"); break; default: return false; } return true; @@ -188,6 +202,18 @@ private void storeFloat(Flags flags, int operand, double _float) throws WriteDat memory.setFloat(addr, _float); } + public void lps(int addr) throws ReadDataBreakpointException { + registers.setSW(memory.getWord(addr+6)); + registers.setPC(memory.getWord(addr+9)); + registers.setA(memory.getWord(addr+12)); + registers.setX(memory.getWord(addr+15)); + registers.setL(memory.getWord(addr+18)); + registers.setB(memory.getWord(addr+21)); + registers.setS(memory.getWord(addr+24)); + registers.setT(memory.getWord(addr+27)); + registers.setF(memory.getFloat(addr+30)); + } + private boolean execSICF3F4(int opcode, Flags flags, int operand) throws DataBreakpointException { // Formats: SIC, F3, F4 switch (opcode) { @@ -233,24 +259,24 @@ private boolean execSICF3F4(int opcode, Flags flags, int operand) throws DataBre break; case Opcode.AND: registers.setA(registers.getA() & loadWord(flags, operand)); break; case Opcode.OR: registers.setA(registers.getA() | loadWord(flags, operand)); break; - case Opcode.COMP: registers.setSWAfterCompare(registers.getAs() - SICXE.swordToInt(loadWord(flags, operand))); break; + case Opcode.COMP: registers.setCC(registers.getAs() - SICXE.swordToInt(loadWord(flags, operand))); break; case Opcode.TIX: registers.setX(registers.getX() + 1); - registers.setSWAfterCompare(registers.getXs() - SICXE.swordToInt(loadWord(flags, operand))); break; + registers.setCC(registers.getXs() - SICXE.swordToInt(loadWord(flags, operand))); break; // input/output case Opcode.RD: registers.setALo(devices.read(loadByte(flags, operand))); break; case Opcode.WD: devices.write(loadByte(flags, operand), registers.getALo()); break; - case Opcode.TD: registers.setSWAfterCompare(devices.test(loadByte(flags, operand)) ? -1 : 0); break; + case Opcode.TD: registers.setCC(devices.test(loadByte(flags, operand)) ? -1 : 0); break; // floating point arithmetic case Opcode.ADDF: registers.setF(registers.getF() + loadFloat(flags, operand)); break; case Opcode.SUBF: registers.setF(registers.getF() - loadFloat(flags, operand)); break; case Opcode.MULF: registers.setF(registers.getF() * loadFloat(flags, operand)); break; case Opcode.DIVF: registers.setF(registers.getF() / loadFloat(flags, operand)); break; case Opcode.COMPF: double sub = registers.getF() - loadFloat(flags, operand); - registers.setSWAfterCompare(sub > 0 ? 1 : (sub < 0 ? -1 : 0)); + registers.setCC(sub > 0 ? 1 : (sub < 0 ? -1 : 0)); break; // others - case Opcode.LPS: notImplemented("LPS"); break; - case Opcode.STI: notImplemented("STI"); break; + case Opcode.LPS: lps(operand); break; + case Opcode.STI: timer = loadWord(flags, operand); break; case Opcode.SSK: notImplemented("SSK"); break; default: return false; } @@ -272,54 +298,56 @@ public void execute() throws DataBreakpointException { lastExecAddr.setSpanLength(0); // fetch first byte int opcode = fetch(); - // try format 1 - if (execF1(opcode)) { + if (Opcode.isF1(opcode)) { + execF1(opcode); lastExecAddr.setSpanLength(1); - return; - } - // fetch one more byte - int op = fetch(); - // try format 2 - if (execF2(opcode, op)) { + } else if (Opcode.isF2(opcode)) { + int op = fetch(); + execF2(opcode, op); lastExecAddr.setSpanLength(2); - return; - } - // otherwise it is format SIC, F3 or F4 - Flags flags = new Flags(opcode, op); - int instructionSize = 0; - // operand depends on instruction format - int operand; - // check if standard SIC - if (flags.isSic()) { - operand = flags.operandSic(op, fetch()); - instructionSize = 3; - // check if F4 (extended) - } else if (flags.isExtended()) { - operand = flags.operandF4(op, fetch(), fetch()); - if (flags.isRelative()) invalidAddressing(); - instructionSize = 4; - // otherwise it is F3 + } else if (Opcode.isF34(opcode & 0xFC)) { + int op = fetch(); + Flags flags = new Flags(opcode, op); + int instructionSize = 0; + // operand depends on instruction format + int operand; + // check if standard SIC + if (flags.isSic()) { + operand = flags.operandSic(op, fetch()); + instructionSize = 3; + // check if F4 (extended) + } else if (flags.isExtended()) { + operand = flags.operandF4(op, fetch(), fetch()); + if (flags.isRelative()) invalidAddressing(); + instructionSize = 4; + // otherwise it is F3 + } else { + instructionSize = 3; + operand = flags.operandF3(op, fetch()); + if (flags.isPCRelative()) + operand = flags.operandPCRelative(operand) + registers.getPC(); + else if (flags.isBaseRelative()) + operand += registers.getB(); + else if (!flags.isAbsolute()) + invalidAddressing(); // both PC and base at the same time + } + // SIC, F3, F4 -- all support indexed addressing, but only when simple TA calculation used + if (flags.isIndexed()) + if (flags.isSimple()) operand += registers.getXs(); + else if(flags.isIndirect()) indirectX = true; + else invalidAddressing(); + // try to execute + execSICF3F4(opcode & 0xFC, flags, operand); + lastExecAddr.setSpanLength(instructionSize); } else { - instructionSize = 3; - operand = flags.operandF3(op, fetch()); - if (flags.isPCRelative()) - operand = flags.operandPCRelative(operand) + registers.getPC(); - else if (flags.isBaseRelative()) - operand += registers.getB(); - else if (!flags.isAbsolute()) - invalidAddressing(); // both PC and base at the same time + invalidOpcode(opcode); } - // SIC, F3, F4 -- all support indexed addressing, but only when simple TA calculation used - if (flags.isIndexed()) - if (flags.isSimple()) operand += registers.getXs(); - else if(flags.isIndirect()) indirectX = true; - else invalidAddressing(); - // try to execute - if (execSICF3F4(opcode & 0xFC, flags, operand)) { - lastExecAddr.setSpanLength(instructionSize); - return; + timer--; + //System.out.printf("timer: %d%n", timer); + if (timer <= 0 && registers.intEnabled(Interrupt.IClass.TIMER)) { + timerInt = new Interrupt(Interrupt.IClass.TIMER, 0); } - invalidOpcode(opcode); + triggerInterrupts(); } @@ -349,4 +377,24 @@ public Integer getAddressBelowLastJSUB() { else return this.addressBelowJSUB.peek(); } + public void triggerInterrupts() throws ReadDataBreakpointException, WriteDataBreakpointException { + // setting interrupt to null clears it + if (svcInt != null) { + svcInt.trigger(registers, memory); + svcInt = null; + } else if (programInt != null) { + programInt.trigger(registers, memory); + programInt = null; + } else if (timerInt != null) { + timerInt.trigger(registers, memory); + timerInt = null; + } else if (ioInt != null) { + // does it get cleared automatically? + // I think on most architectures the interrupt handler must + // clear the interrupt manually. + ioInt.trigger(registers, memory); + ioInt = null; + } + } + } diff --git a/src/sic/sim/vm/Registers.java b/src/sic/sim/vm/Registers.java index 751b521..19ad23b 100644 --- a/src/sic/sim/vm/Registers.java +++ b/src/sic/sim/vm/Registers.java @@ -17,8 +17,23 @@ public class Registers { private int S, T, B; // SIC/XE 48-bit float register private double F; - // condition code of status word register - private int CC; // TODO: full status word support + + private class StatusWord { + // 0 = user, 1 = supervisor + public int MODE = 1; + // 0 = running, 1 = idle + public int IDLE = 0; + // process identifier + public int ID = 0; + // condition code + public int CC = 0; + // interrupt mask + public int MASK = 0; + // interrupt code + public int ICODE = 0; + } + + private StatusWord SW = new StatusWord(); // ***** getters/setters ******************** // get ... unsigned @@ -128,31 +143,72 @@ public void setF(double val) { } public int getSW() { - if (CC == 0) return 0; - else if (CC < 0) return 0x40; - else return 0x80; + int value = 0; + value |= SW.MODE; + value |= SW.IDLE<<1; + value |= SW.ID<<2; + int cc = 0; + if (SW.CC > 0) { + cc = 0x1; + } else if (SW.CC < 0) { + cc = 0x2; + } + value |= cc<<6; + value |= SW.MASK<<8; + value |= SW.ICODE<<16; + return value; } public boolean isLower() { - return CC < 0; + return SW.CC < 0; } public boolean isEqual() { - return CC == 0; + return SW.CC == 0; } public boolean isGreater() { - return CC > 0; + return SW.CC > 0; + } + + public boolean isSupervisor() { + return SW.MODE == 1; + } + + public boolean isIdle() { + return SW.IDLE == 1; + } + + public int processID() { + return SW.ID; + } + + public void setICODE(int value) { + SW.ICODE = value & 0xff; } public void setSW(int value) { - if ((value & 0x40) == 0x40) CC = -1; - else if ((value & 0x80) == 0x80) CC = 1; - else CC = 0; + SW.MODE = value&0x1; + SW.IDLE = (value>>1)&0x1; + SW.ID = (value>>2)&0xf; + int cc = (value>>6)&0x3; + if (cc == 0x1) { + SW.CC = -1; + } else if (cc == 0x2) { + SW.CC = 1; + } else { + SW.CC = 0; + } + SW.MASK = (value>>8)&0xf; + SW.ICODE = (value>>16)&0x8; + } + + public void setCC(int compare) { + SW.CC = compare; } - public void setSWAfterCompare(int compare) { - CC = compare; + public boolean intEnabled(Interrupt.IClass c) { + return (SW.MASK & c.value) > 0; } // ***** getter/setter by register index **** @@ -223,7 +279,7 @@ public void reset() { A = X = L = 0; B = S = T = 0; F = 0; - CC = 0; + SW = new StatusWord(); } public Registers() { diff --git a/tests/interrupts.asm b/tests/interrupts.asm new file mode 100644 index 0000000..1bc662e --- /dev/null +++ b/tests/interrupts.asm @@ -0,0 +1,77 @@ +ints START 0 + + + ORG 0x100 + . first byte will be filled by the interrupt's value + . second nibble of the second byte is interrupt mask (0 = disable all interrupts) + . the third byte goes to supervisor mode +svc_int WORD 0x000001 +svc_haddr RESW 1 + RESW 10 . 9 registers, one of them 2 WORDS long + +svc_handler LDCH #0x53 + WD #1 + LDCH #0x56 + WD #1 + LDCH #0x43 + WD #1 + LDCH #0x20 + WD #1 + + . print code + LDCH #0x30 + WD #1 + LDCH #0x58 + WD #1 + + STSW sw + CLEAR A + LDCH sw + SHIFTR A,4 + AND #0xf + COMP #0xa + JLT add + ADD #0x7 +add ADD #0x30 + WD #1 + + LDCH sw + AND #0xf + COMP #0xa + JLT add2 + ADD #0x7 +add2 ADD #0x30 + WD #1 + LDCH #0x0a + WD #1 + SVC 13 + + LPS #svc_int + + +init_regs RESW 2 + WORD 0x000800 . enable SVC, user mode +init_addr RESW 1 + RESW 8 . init all registers to 0 + + +sw RESW 1 + + . switch to user mode, enable interrupts +entry LDA #svc_handler + STA svc_haddr + LDA #program + STA init_addr + LPS #init_regs + + .ORG 1000 +program LDCH #0x41 + WD #1 + SVC 0x1a + SVC 0x2f + WD #1 + LDCH #0x42 + WD #1 +halt J halt + + END entry diff --git a/tests/interrupts_timer.asm b/tests/interrupts_timer.asm new file mode 100644 index 0000000..e10c0d8 --- /dev/null +++ b/tests/interrupts_timer.asm @@ -0,0 +1,54 @@ +ints START 0 + + + ORG 0x160 + . first byte will be filled by the interrupt's value + . second nibble of the second byte is interrupt mask (0 = disable all interrupts) + . the third byte goes to supervisor mode +svc_int WORD 0x000001 +svc_haddr RESW 1 + RESW 10 . 9 registers, one of them 2 WORDS long + +svc_handler + LDCH #0x0a + WD #1 + LDCH #0x54 + WD #1 + LDCH #0x49 + WD #1 + LDCH #0x4d + WD #1 + LDCH #0x0a + WD #1 + CLEAR A + RD #3 + STA rand + STI rand + //SVC 13 + + LPS #svc_int + +rand WORD 1 + + +init_regs RESW 2 + WORD 0x000200 . enable SVC, user mode +init_addr RESW 1 + RESW 8 . init all registers to 0 + + +sw RESW 1 + + . switch to user mode, enable interrupts +entry LDA #svc_handler + STA svc_haddr + LDA #program + STA init_addr + STI #40 + LPS #init_regs + +program LDCH #0x41 + WD #1 +halt J program + + END entry From 012ac4c8b1b9f8f5a15fae139f4935c450b89356 Mon Sep 17 00:00:00 2001 From: Kljunas2 Date: Sun, 26 Jan 2025 00:05:13 +0100 Subject: [PATCH 02/12] Implement some Program interrupts --- src/sic/common/Opcode.java | 76 ++++++++-------------------------- src/sic/sim/views/CPUView.java | 4 ++ src/sic/sim/vm/Interrupt.java | 48 ++++++++++++--------- src/sic/sim/vm/Machine.java | 54 ++++++++++++++++++------ tests/interrupts_priv.asm | 38 +++++++++++++++++ 5 files changed, 129 insertions(+), 91 deletions(-) create mode 100644 tests/interrupts_priv.asm diff --git a/src/sic/common/Opcode.java b/src/sic/common/Opcode.java index c5a6d55..0e4baee 100644 --- a/src/sic/common/Opcode.java +++ b/src/sic/common/Opcode.java @@ -106,12 +106,7 @@ public class Opcode { public static boolean isF1(int opcode) { switch (opcode) { - case FLOAT: - case FIX: - case NORM: - case SIO: - case HIO: - case TIO: + case FLOAT, FIX, NORM, SIO, HIO, TIO: return true; } return false; @@ -119,66 +114,29 @@ public static boolean isF1(int opcode) { public static boolean isF2(int opcode) { switch (opcode) { - case ADDR: - case SUBR: - case MULR: - case DIVR: - case COMPR: - case SHIFTL: - case SHIFTR: - case RMO: - case CLEAR: - case TIXR: - case SVC: + case ADDR, SUBR, MULR, DIVR, COMPR, SHIFTL, SHIFTR, RMO, CLEAR, TIXR, SVC: return true; } return false; } public static boolean isF34(int opcode) { + switch (opcode & 0xFC) { + case STA, STX, STL, STCH, STB, STS, STF, STT, STSW, JEQ, JGT, JLT, J, + RSUB, JSUB, LDA, LDX, LDL, LDCH, LDB, LDS, LDF, LDT, ADD, SUB, MUL, + DIV, AND, OR, COMP, TIX, RD, WD, TD, ADDF, SUBF, MULF, DIVF, COMPF, + LPS, STI, SSK: + return true; + } + return false; + } + + public static boolean isPrivileged(int opcode) { + if (isF34(opcode)) { + opcode &= 0xFC; + } switch (opcode) { - case STA: - case STX: - case STL: - case STCH: - case STB: - case STS: - case STF: - case STT: - case STSW: - case JEQ: - case JGT: - case JLT: - case J: - case RSUB: - case JSUB: - case LDA: - case LDX: - case LDL: - case LDCH: - case LDB: - case LDS: - case LDF: - case LDT: - case ADD: - case SUB: - case MUL: - case DIV: - case AND: - case OR: - case COMP: - case TIX: - case RD: - case WD: - case TD: - case ADDF: - case SUBF: - case MULF: - case DIVF: - case COMPF: - case LPS: - case STI: - case SSK: + case HIO, LPS, /*RD,*/ SIO, SSK, STI, STSW, TD, TIO: /*WD:*/ return true; } return false; diff --git a/src/sic/sim/views/CPUView.java b/src/sic/sim/views/CPUView.java index 5ddfcbe..0411322 100644 --- a/src/sic/sim/views/CPUView.java +++ b/src/sic/sim/views/CPUView.java @@ -26,6 +26,7 @@ public class CPUView { private final Color colorNochange = Colors.fg; private final Color colorChange = Color.BLUE; + private final Color colorSupervisor = Color.RED; private final Executor executor; private final Machine machine; @@ -47,6 +48,7 @@ public class CPUView { private JButton btnStartStop; public JPanel mainPanel; private JLabel lblInfo; + private JLabel swLabel; public CPUView(final Executor executor, final Disassembler disassembler) { this.executor = executor; @@ -160,6 +162,7 @@ public void updateView() { updateRegWord(regT, registers.getT()); updateRegWord(regB, registers.getB()); updateRegWord(regSW, registers.getSW()); + swLabel.setForeground(registers.isSupervisor() ? colorSupervisor : colorNochange); updateRegFloat(registers.getF()); updateRegWord(regPC, registers.getPC()); // @@ -401,6 +404,7 @@ public void updateView() { label5.setLabelFor(regT); label6.setLabelFor(regB); label7.setLabelFor(regSW); + swLabel = label7; label8.setLabelFor(regF); label9.setLabelFor(regPC); } diff --git a/src/sic/sim/vm/Interrupt.java b/src/sic/sim/vm/Interrupt.java index 0e069f2..4dc3d87 100644 --- a/src/sic/sim/vm/Interrupt.java +++ b/src/sic/sim/vm/Interrupt.java @@ -17,24 +17,46 @@ public enum IClass { } } + public enum ProgICODE { + ILLEGAL_INSTRUCTION(0x00), + PRIVILEGED_INSTRUCTION(0x01), + ADDR_OUT_OF_RANGE(0x02), + MEM_PROTECT(0x03), + OVERFLOW(0x04), + PAGE_FAULT(0x10), + SEG_FAULT(0x11), + SEG_PROTECTION_VIOLATION(0x12), + SEG_LEN_EXCEEDED(0x13); + + public final int value; + + ProgICODE(int value) { + this.value = value; + } + } + public IClass CLASS; private int ICODE; public Interrupt(IClass iclass, int icode) { CLASS = iclass; ICODE = icode; - //System.out.printf("created interrupt %s, icode. 0x%x%n", CLASS.name(), ICODE); + } + + public Interrupt(IClass iclass, ProgICODE icode) { + CLASS = iclass; + ICODE = icode.value; } public static int getWorkArea(IClass CLASS) { switch (CLASS) { - case IClass.SVC: + case SVC: return 0x100; - case IClass.PROGRAM: + case PROGRAM: return 0x130; - case IClass.TIMER: + case TIMER: return 0x160; - case IClass.IO: + case IO: return 0x190; } return -1; @@ -60,22 +82,8 @@ private void restoreRegisters(Registers registers, Memory memory) throws ReadDat public void trigger(Registers registers, Memory memory) throws ReadDataBreakpointException, WriteDataBreakpointException { saveRegisters(registers, memory); int addr = Interrupt.getWorkArea(CLASS); - //System.out.printf("triggering interrupt %s, icode: 0x%x, jumping to: 0x%6x%n", CLASS.name(), ICODE, addr); registers.setSW(memory.getWord(addr)); registers.setPC(memory.getWord(addr+3)); + registers.setICODE(ICODE); } - - /* - public enum ProgramType { - ILLEG_INSTR, - PRIVILEGED_INSTR, - ADDR_OUT_OF_RANGE, - MEM_PROTECT, - OVERFLOW, - PAGE_FAULT, - SEG_FAULT, - SEG_PROTECT_VIOLATION, - SEG_LEN_EXCEEDED, - } - */ } diff --git a/src/sic/sim/vm/Machine.java b/src/sic/sim/vm/Machine.java index 7975799..f9fc778 100644 --- a/src/sic/sim/vm/Machine.java +++ b/src/sic/sim/vm/Machine.java @@ -92,11 +92,21 @@ private void notImplemented(String mnemonic) { } private void invalidOpcode(int opcode) { - Logger.fmterr("Invalid opcode '%d'.", opcode); + if ( registers.intEnabled(Interrupt.IClass.PROGRAM)) { + programInt = new Interrupt(Interrupt.IClass.PROGRAM, + Interrupt.ProgICODE.ILLEGAL_INSTRUCTION); + } else { + Logger.fmterr("Invalid opcode '%d'.", opcode); + } } private void invalidAddressing() { - Logger.err("Invalid addressing."); + if ( registers.intEnabled(Interrupt.IClass.PROGRAM)) { + programInt = new Interrupt(Interrupt.IClass.PROGRAM, + Interrupt.ProgICODE.ILLEGAL_INSTRUCTION); + } else { + Logger.err("Invalid addressing."); + } } private boolean execF1(int opcode) { @@ -124,9 +134,14 @@ private boolean execF2(int opcode, int operand) throws ReadDataBreakpointExcepti case Opcode.DIVR: int divisor = registers.get(o1); if (divisor == 0) { + if ( registers.intEnabled(Interrupt.IClass.PROGRAM)) { + programInt = new Interrupt(Interrupt.IClass.PROGRAM, + Interrupt.ProgICODE.ILLEGAL_INSTRUCTION); + } else { System.out.println("division by zero"); + } } else { - registers.set(o2, registers.gets(o2) / divisor); + registers.set(o2, registers.gets(o2) / divisor); } break; case Opcode.COMPR: registers.setCC(registers.gets(o1) - registers.gets(o2)); break; @@ -142,7 +157,6 @@ private boolean execF2(int opcode, int operand) throws ReadDataBreakpointExcepti Logger.fmterr("SVC is disabled"); break; } - registers.setICODE(operand); svcInt = new Interrupt(Interrupt.IClass.SVC, operand); break; default: return false; @@ -177,11 +191,11 @@ private double loadFloat(Flags flags, int operand) throws ReadDataBreakpointExce // use of TA for store: addr / addr of addr private int resolveAddr(Flags flags, int addr) { if (flags.isIndirect()) { - addr = memory.getWordRaw(addr); - if (indirectX) - addr += registers.getXs(); - } - return addr; + addr = memory.getWordRaw(addr); + if (indirectX) + addr += registers.getXs(); + } + return addr; } private void storeWord(Flags flags, int operand, int word) throws WriteDataBreakpointException { @@ -298,14 +312,31 @@ public void execute() throws DataBreakpointException { lastExecAddr.setSpanLength(0); // fetch first byte int opcode = fetch(); - if (Opcode.isF1(opcode)) { + if (Opcode.isPrivileged(opcode) + && !registers.isSupervisor() + && registers.intEnabled(Interrupt.IClass.PROGRAM)) { + programInt = new Interrupt(Interrupt.IClass.PROGRAM, + Interrupt.ProgICODE.PRIVILEGED_INSTRUCTION); + // There has to be a better way to do this. + // Maybe separating decode and execute. + if (Opcode.isF2(opcode)) { + fetch(); + } else if (Opcode.isF34(opcode)) { + int op = fetch(); + Flags flags = new Flags(opcode, op); + fetch(); + if (flags.isExtended()) { + fetch(); + } + } + } else if (Opcode.isF1(opcode)) { execF1(opcode); lastExecAddr.setSpanLength(1); } else if (Opcode.isF2(opcode)) { int op = fetch(); execF2(opcode, op); lastExecAddr.setSpanLength(2); - } else if (Opcode.isF34(opcode & 0xFC)) { + } else if (Opcode.isF34(opcode)) { int op = fetch(); Flags flags = new Flags(opcode, op); int instructionSize = 0; @@ -343,7 +374,6 @@ else if (!flags.isAbsolute()) invalidOpcode(opcode); } timer--; - //System.out.printf("timer: %d%n", timer); if (timer <= 0 && registers.intEnabled(Interrupt.IClass.TIMER)) { timerInt = new Interrupt(Interrupt.IClass.TIMER, 0); } diff --git a/tests/interrupts_priv.asm b/tests/interrupts_priv.asm new file mode 100644 index 0000000..344bb82 --- /dev/null +++ b/tests/interrupts_priv.asm @@ -0,0 +1,38 @@ +ints START 0 + + + ORG 0x130 + . first byte will be filled by the interrupt's value + . second nibble of the second byte is interrupt mask (0 = disable all interrupts) + . the third byte goes to supervisor mode +prog_int WORD 0x000001 +prog_haddr RESW 1 + RESW 10 . 9 registers, one of them 2 WORDS long + +prog_handler + TD #1 + LPS #prog_int + +rand WORD 1 + + +init_regs RESW 2 + WORD 0x000400 . enable PROG, user mode +init_addr RESW 1 + RESW 8 . init all registers to 0 + + +sw RESW 1 + + . switch to user mode, enable interrupts +entry LDA #prog_handler + STA prog_haddr + LDA #program + STA init_addr + STI #40 + LPS #init_regs + +program TD #1 + J program + + END entry From 5a18f6809de6a524744b434bf5cb8eea834d9354 Mon Sep 17 00:00:00 2001 From: Kljunas2 Date: Mon, 27 Jan 2025 20:24:48 +0100 Subject: [PATCH 03/12] Refactor Machine.java --- .gitignore | 36 +- src/sic/common/Opcode.java | 3 - src/sic/disasm/Disassembler.java | 20 +- src/sic/sim/Executor.java | 2 +- src/sic/sim/vm/Machine.java | 645 ++++++++++++++++--------------- 5 files changed, 358 insertions(+), 348 deletions(-) diff --git a/.gitignore b/.gitignore index 7e20958..36fb924 100644 --- a/.gitignore +++ b/.gitignore @@ -4,36 +4,6 @@ out addons/out /.project *.class -tests/linker/factorial/factdemo.obj -tests/linker/factorial/linfac2_demo.obj -tests/linker/factorial/linfac_demo.obj -tests/linker/factorial/outfac.obj -tests/linker/multi/out.obj -tests/linker/nametest/extd.log -tests/linker/nametest/extd.lst -tests/linker/nametest/first.log -tests/linker/nametest/first.lst -tests/linker/nametest/nmtest.obj -tests/linker/nametest/test.log -tests/linker/nametest/test.lst -tests/linker/one/out.obj -tests/linker/partial/prtial.obj -tests/linker/stack/demostack.obj -tests/linker/factorial/ending.log -tests/linker/factorial/ending.lst -tests/linker/factorial/fact.log -tests/linker/factorial/fact.lst -tests/linker/factorial/linked.obj -tests/linker/factorial/main.log -tests/linker/factorial/main.lst -tests/linker/factorial/print.log -tests/linker/factorial/print.lst -tests/linker/factorial/print2.asm -tests/linker/factorial/print2.log -tests/linker/factorial/print2.lst -tests/linker/factorial/prt2fc.obj -tests/linker/factorial/stack.log -tests/linker/factorial/stack.lst -tests/linker/one2/linked.obj -tests/linker/one2/prog.log -tests/linker/one2/prog.lst +tests/**/*.log +tests/**/*.lst +tests/**/*.obj diff --git a/src/sic/common/Opcode.java b/src/sic/common/Opcode.java index 0e4baee..4c74fc4 100644 --- a/src/sic/common/Opcode.java +++ b/src/sic/common/Opcode.java @@ -132,9 +132,6 @@ public static boolean isF34(int opcode) { } public static boolean isPrivileged(int opcode) { - if (isF34(opcode)) { - opcode &= 0xFC; - } switch (opcode) { case HIO, LPS, /*RD,*/ SIO, SSK, STI, STSW, TD, TIO: /*WD:*/ return true; diff --git a/src/sic/disasm/Disassembler.java b/src/sic/disasm/Disassembler.java index db216ab..001a9d4 100644 --- a/src/sic/disasm/Disassembler.java +++ b/src/sic/disasm/Disassembler.java @@ -63,7 +63,7 @@ public void prev(int count) { } private int fetchAddr; - protected int fetch() { + protected int fetchByte() { if (fetchAddr < 0) return 0; if (fetchAddr > SICXE.MAX_ADDR) return 0; return machine.memory.getByteRaw(fetchAddr++); @@ -71,7 +71,7 @@ protected int fetch() { public Instruction disassemble(int addr) { this.fetchAddr = addr; - int opcode = fetch(); + int opcode = fetchByte(); String name = Opcode.getName(opcode & 0xFC); if (name == null) return null; Mnemonic mnemonic = mnemonics.get(name); @@ -81,24 +81,24 @@ public Instruction disassemble(int addr) { case F1: return new InstructionF1(loc, "", mnemonic); case F2n: - return new InstructionF2n(loc, "", mnemonic, fetch() >> 4); + return new InstructionF2n(loc, "", mnemonic, fetchByte() >> 4); case F2r: - return new InstructionF2r(loc, "", mnemonic, fetch() >> 4); + return new InstructionF2r(loc, "", mnemonic, fetchByte() >> 4); case F2rn: - b1 = fetch(); + b1 = fetchByte(); return new InstructionF2rn(loc, "", mnemonic, (b1 & 0xF0) >> 4, (b1 & 0x0F) + 1); case F2rr: - b1 = fetch(); + b1 = fetchByte(); return new InstructionF2rr(loc, "", mnemonic, (b1 & 0xF0) >> 4, b1 & 0x0F); case F3: - fetch(); fetch(); // should be zero? + fetchByte(); fetchByte(); // should be zero? return new InstructionF3(loc, "", mnemonic); case F3m: case F4m: - b1 = fetch(); b2 = fetch(); + b1 = fetchByte(); b2 = fetchByte(); Flags flags = new Flags(opcode, b1); if (flags.isExtended()) { - int operand = flags.operandF4(b1, b2, fetch()); + int operand = flags.operandF4(b1, b2, fetchByte()); mnemonic = mnemonics.get("+" + name); return new InstructionF4m(loc, "", mnemonic, flags, operand, null); } @@ -138,4 +138,4 @@ public int getNextPCLocation() { return getLocationAfter(machine.registers.getPC()); } -} \ No newline at end of file +} diff --git a/src/sic/sim/Executor.java b/src/sic/sim/Executor.java index 83f9771..84cfd7d 100644 --- a/src/sic/sim/Executor.java +++ b/src/sic/sim/Executor.java @@ -170,7 +170,7 @@ public void runToAddress(int stopAddress) { * Step out of the current sub procedure */ public void stepOut() { - Integer addressAfterLastJSUB = machine.getAddressBelowLastJSUB(); + Integer addressAfterLastJSUB = machine.getReturnAddress(); if (addressAfterLastJSUB == null) return; runUntil(machine -> machine.registers.getPC() == addressAfterLastJSUB); } diff --git a/src/sic/sim/vm/Machine.java b/src/sic/sim/vm/Machine.java index f9fc778..ecd1632 100644 --- a/src/sic/sim/vm/Machine.java +++ b/src/sic/sim/vm/Machine.java @@ -11,48 +11,31 @@ * @author jure */ public class Machine { - + // ************ Constants public static final int MAX_ADDRESS = (1 << 20) - 1; // 1048576 - 1 public static final int MAX_DEVICE = 255; // ************ Machine parts - - public final Registers registers; - public final Memory memory; - public final Devices devices; - + public final Registers registers = new Registers(); + public final Memory memory = new Memory(MAX_ADDRESS+1); + public final Devices devices = new Devices(MAX_DEVICE+1); private int timer = 0; + private Interrupt svcInt = null; private Interrupt programInt = null; private Interrupt timerInt = null; private Interrupt ioInt = null; - // ************ Statistics - - private int instructionCount; - private MemorySpan lastExecAddr; - private MemorySpan lastExecRead; - private MemorySpan lastExecWrite; + private Stack callStack = new Stack<>(); - - private Stack addressBelowJSUB = new Stack<>(); - - private boolean indirectX = false; - - // ************ Constructor - - public Machine() { - this.registers = new Registers(); - this.memory = new Memory(MAX_ADDRESS+1); - this.devices = new Devices(MAX_DEVICE+1); - this.lastExecRead = new MemorySpan(); - this.lastExecWrite = new MemorySpan(); - this.lastExecAddr = new MemorySpan(); - } + // ************ Statistics + private int instructionCount = 0; + private MemorySpan lastExecAddr = new MemorySpan(); + private MemorySpan lastExecRead = new MemorySpan(); + private MemorySpan lastExecWrite = new MemorySpan(); // ************ getters/setters - public int getInstructionCount() { return instructionCount; } @@ -85,276 +68,362 @@ public void clearLastExecReadWrite() { lastExecAddr.clear(); } + /** + * Get the address on the top of the call stack, so we can step out. + * @return null if no item on stack - no JSUB encountered, otherwise last address. + */ + public Integer getReturnAddress() { + if (callStack.isEmpty()) { + return null; + } + return callStack.peek(); + } + // ********** Execution ********************* + // ********** Instruction types ********************* + private abstract class Instruction { + public int opcode; - private void notImplemented(String mnemonic) { - Logger.fmterr("Instruction '%s' not implemented!", mnemonic); - } + abstract public void execute() throws DataBreakpointException; - private void invalidOpcode(int opcode) { - if ( registers.intEnabled(Interrupt.IClass.PROGRAM)) { - programInt = new Interrupt(Interrupt.IClass.PROGRAM, - Interrupt.ProgICODE.ILLEGAL_INSTRUCTION); - } else { - Logger.fmterr("Invalid opcode '%d'.", opcode); + public boolean isPrivileged() { + return Opcode.isPrivileged(opcode); } } - private void invalidAddressing() { - if ( registers.intEnabled(Interrupt.IClass.PROGRAM)) { - programInt = new Interrupt(Interrupt.IClass.PROGRAM, - Interrupt.ProgICODE.ILLEGAL_INSTRUCTION); - } else { - Logger.err("Invalid addressing."); + private class InstructionF1 extends Instruction { + public InstructionF1(int opcode) { + this.opcode = opcode; } - } - private boolean execF1(int opcode) { - // Format 1: no operand - switch (opcode) { - case Opcode.FLOAT: registers.setF((double) registers.getAs()); break; - case Opcode.FIX: registers.setA((int) registers.getF()); break; - case Opcode.NORM: notImplemented("NORM"); break; - case Opcode.SIO: notImplemented("SIO"); break; - case Opcode.HIO: notImplemented("HIO"); break; - case Opcode.TIO: notImplemented("TIO"); break; - default: return false; + @Override + public void execute() throws DataBreakpointException { + switch (opcode) { + case Opcode.FLOAT: registers.setF((double) registers.getAs()); break; + case Opcode.FIX: registers.setA((int) registers.getF()); break; + case Opcode.NORM: notImplemented("NORM"); break; + case Opcode.SIO: notImplemented("SIO"); break; + case Opcode.HIO: notImplemented("HIO"); break; + case Opcode.TIO: notImplemented("TIO"); break; + } } - return true; } - private boolean execF2(int opcode, int operand) throws ReadDataBreakpointException, WriteDataBreakpointException { - // Format 2: OP o1, o2 - two 4-bit operands - int o1 = (operand & 0xF0) >> 4; - int o2 = (operand & 0x0F); - switch (opcode) { - case Opcode.ADDR: registers.set(o2, registers.get(o2) + registers.get(o1)); break; - case Opcode.SUBR: registers.set(o2, registers.get(o2) - registers.get(o1)); break; - case Opcode.MULR: registers.set(o2, registers.get(o2) * registers.get(o1)); break; - case Opcode.DIVR: - int divisor = registers.get(o1); - if (divisor == 0) { - if ( registers.intEnabled(Interrupt.IClass.PROGRAM)) { - programInt = new Interrupt(Interrupt.IClass.PROGRAM, - Interrupt.ProgICODE.ILLEGAL_INSTRUCTION); + private class InstructionF2 extends Instruction { + public int operand; + + public InstructionF2(int opcode, int operand) { + this.opcode = opcode; + this.operand = operand; + } + + @Override + public void execute() throws DataBreakpointException { + // Format 2: OP o1, o2 - two 4-bit operands + int o1 = (operand & 0xF0) >> 4; + int o2 = (operand & 0x0F); + switch (opcode) { + case Opcode.ADDR: registers.set(o2, registers.get(o2) + registers.get(o1)); break; + case Opcode.SUBR: registers.set(o2, registers.get(o2) - registers.get(o1)); break; + case Opcode.MULR: registers.set(o2, registers.get(o2) * registers.get(o1)); break; + case Opcode.DIVR: + int divisor = registers.get(o1); + if (divisor == 0) { + if ( registers.intEnabled(Interrupt.IClass.PROGRAM)) { + programInt = new Interrupt(Interrupt.IClass.PROGRAM, + Interrupt.ProgICODE.ILLEGAL_INSTRUCTION); + } else { + System.out.println("division by zero"); + } } else { - System.out.println("division by zero"); + registers.set(o2, registers.gets(o2) / divisor); } + break; + case Opcode.COMPR: registers.setCC(registers.gets(o1) - registers.gets(o2)); break; + case Opcode.SHIFTL: registers.set(o1, registers.get(o1) << (o2 + 1) | registers.get(o1) >> (24 - o2 - 1)); break; + case Opcode.SHIFTR: registers.set(o1, registers.gets(o1) >> (o2 + 1)); break; + case Opcode.RMO: registers.set(o2, registers.get(o1)); break; + case Opcode.CLEAR: registers.set(o1, 0); break; + case Opcode.TIXR: + registers.setX(registers.getX()+1); + registers.setCC(registers.getXs() - registers.gets(o1)); + break; + case Opcode.SVC: + if (!registers.intEnabled(Interrupt.IClass.SVC)) { + Logger.fmterr("SVC is disabled"); + break; + } + svcInt = new Interrupt(Interrupt.IClass.SVC, operand); + break; + } + } + } + + private class InstructionSICF3F4 extends Instruction { + public Flags flags; + public int operand; + + public InstructionSICF3F4(int opcode, Flags flags, int operand) { + this.opcode = opcode; + this.flags = flags; + this.operand = operand; + } + + @Override + public void execute() throws DataBreakpointException { + // Formats: SIC, F3, F4 + switch (opcode) { + // ***** immediate addressing not possible ***** + // stores + case Opcode.STA: storeWord(flags, operand, registers.getA()); break; + case Opcode.STX: storeWord(flags, operand, registers.getX()); break; + case Opcode.STL: storeWord(flags, operand, registers.getL()); break; + case Opcode.STCH: storeByte(flags, operand, registers.getA()); break; + case Opcode.STB: storeWord(flags, operand, registers.getB()); break; + case Opcode.STS: storeWord(flags, operand, registers.getS()); break; + case Opcode.STF: storeFloat(flags, operand, registers.getF()); break; + case Opcode.STT: storeWord(flags, operand, registers.getT()); break; + case Opcode.STSW: storeWord(flags, operand, registers.getSW()); break; + + // jumps + case Opcode.JEQ: if (registers.isEqual()) registers.setPC(resolveAddr(flags, operand)); break; + case Opcode.JGT: if (registers.isGreater()) registers.setPC(resolveAddr(flags, operand)); break; + case Opcode.JLT: if (registers.isLower()) registers.setPC(resolveAddr(flags, operand)); break; + case Opcode.J: registers.setPC(resolveAddr(flags, operand)); break; + case Opcode.RSUB: + registers.setPC(registers.getL()); + callStack.pop(); + break; + case Opcode.JSUB: + registers.setL(registers.getPC()); + callStack.push(registers.getPC()); + registers.setPC(resolveAddr(flags, operand)); + break; + // ***** immediate addressing possible ***** + + // loads + case Opcode.LDA: registers.setA(loadWord(flags, operand)); break; + case Opcode.LDX: registers.setX(loadWord(flags, operand)); break; + case Opcode.LDL: registers.setL(loadWord(flags, operand)); break; + case Opcode.LDCH: registers.setALo(loadByte(flags, operand)); break; + case Opcode.LDB: registers.setB(loadWord(flags, operand)); break; + case Opcode.LDS: registers.setS(loadWord(flags, operand)); break; + case Opcode.LDF: registers.setF(loadFloat(flags, operand)); break; + case Opcode.LDT: registers.setT(loadWord(flags, operand)); break; + + // arithmetic + case Opcode.ADD: registers.setA(registers.getA() + loadWord(flags, operand)); break; + case Opcode.SUB: registers.setA(registers.getA() - loadWord(flags, operand)); break; + case Opcode.MUL: registers.setA(registers.getA() * loadWord(flags, operand)); break; + case Opcode.DIV: + int divisor = SICXE.swordToInt(loadWord(flags, operand)); + if (divisor == 0) { + System.out.println("division by zero"); } else { - registers.set(o2, registers.gets(o2) / divisor); + registers.setA(registers.getAs() / divisor); } break; - case Opcode.COMPR: registers.setCC(registers.gets(o1) - registers.gets(o2)); break; - case Opcode.SHIFTL: registers.set(o1, registers.get(o1) << (o2 + 1) | registers.get(o1) >> (24 - o2 - 1)); break; - case Opcode.SHIFTR: registers.set(o1, registers.gets(o1) >> (o2 + 1)); break; - case Opcode.RMO: registers.set(o2, registers.get(o1)); break; - case Opcode.CLEAR: registers.set(o1, 0); break; - case Opcode.TIXR: registers.setX(registers.getX()+1); - registers.setCC(registers.getXs() - registers.gets(o1)); - break; - case Opcode.SVC: - if (!registers.intEnabled(Interrupt.IClass.SVC)) { - Logger.fmterr("SVC is disabled"); + case Opcode.AND: registers.setA(registers.getA() & loadWord(flags, operand)); break; + case Opcode.OR: registers.setA(registers.getA() | loadWord(flags, operand)); break; + case Opcode.COMP: registers.setCC(registers.getAs() - SICXE.swordToInt(loadWord(flags, operand))); break; + case Opcode.TIX: + registers.setX(registers.getX() + 1); + registers.setCC(registers.getXs() - SICXE.swordToInt(loadWord(flags, operand))); break; - } - svcInt = new Interrupt(Interrupt.IClass.SVC, operand); - break; - default: return false; + + // input/output + case Opcode.RD: registers.setALo(devices.read(loadByte(flags, operand))); break; + case Opcode.WD: devices.write(loadByte(flags, operand), registers.getALo()); break; + case Opcode.TD: registers.setCC(devices.test(loadByte(flags, operand)) ? -1 : 0); break; + + // floating point arithmetic + case Opcode.ADDF: registers.setF(registers.getF() + loadFloat(flags, operand)); break; + case Opcode.SUBF: registers.setF(registers.getF() - loadFloat(flags, operand)); break; + case Opcode.MULF: registers.setF(registers.getF() * loadFloat(flags, operand)); break; + case Opcode.DIVF: registers.setF(registers.getF() / loadFloat(flags, operand)); break; + case Opcode.COMPF: + double sub = registers.getF() - loadFloat(flags, operand); + registers.setCC(sub > 0 ? 1 : (sub < 0 ? -1 : 0)); + break; + // others + case Opcode.LPS: lps(operand); break; + case Opcode.STI: timer = loadWord(flags, operand); break; + case Opcode.SSK: notImplemented("SSK"); break; + } } - return true; - } - // load + @Override + public boolean isPrivileged() { + return Opcode.isPrivileged(opcode & 0xFC); + } - - private int loadWord(Flags flags, int operand) throws ReadDataBreakpointException { - if (flags.isImmediate()) return operand; - int addr = resolveAddr(flags, operand); - setLastExecRead(addr, 3); - return memory.getWord(addr); - } + private int loadWord(Flags flags, int operand) throws ReadDataBreakpointException { + if (flags.isImmediate()) return operand; + int addr = resolveAddr(flags, operand); + setLastExecRead(addr, 3); + return memory.getWord(addr); + } - private int loadByte(Flags flags, int operand) throws ReadDataBreakpointException { - if (flags.isImmediate()) return operand; - int addr = resolveAddr(flags, operand); - setLastExecRead(addr, 1); - return memory.getByte(addr); - } + private int loadByte(Flags flags, int operand) throws ReadDataBreakpointException { + if (flags.isImmediate()) return operand; + int addr = resolveAddr(flags, operand); + setLastExecRead(addr, 1); + return memory.getByte(addr); + } - private double loadFloat(Flags flags, int operand) throws ReadDataBreakpointException { - if (flags.isImmediate()) return operand; - int addr = resolveAddr(flags, operand); - setLastExecRead(addr, 6); - return memory.getFloat(addr); - } + private double loadFloat(Flags flags, int operand) throws ReadDataBreakpointException { + if (flags.isImmediate()) return operand; + int addr = resolveAddr(flags, operand); + setLastExecRead(addr, 6); + return memory.getFloat(addr); + } - // use of TA for store: addr / addr of addr - private int resolveAddr(Flags flags, int addr) { - if (flags.isIndirect()) { - addr = memory.getWordRaw(addr); - if (indirectX) - addr += registers.getXs(); + private void storeWord(Flags flags, int operand, int word) throws WriteDataBreakpointException { + int addr = resolveAddr(flags, operand); + setLastExecWrite(addr, 3); + memory.setWord(addr, word); } - return addr; - } - private void storeWord(Flags flags, int operand, int word) throws WriteDataBreakpointException { - int addr = resolveAddr(flags, operand); - setLastExecWrite(addr, 3); - memory.setWord(addr, word); - } + private void storeByte(Flags flags, int operand, int _byte) throws WriteDataBreakpointException { + int addr = resolveAddr(flags, operand); + setLastExecWrite(addr, 1); + memory.setByte(addr, _byte); + } + + private void storeFloat(Flags flags, int operand, double _float) throws WriteDataBreakpointException { + int addr = resolveAddr(flags, operand); + setLastExecWrite(addr, 6); + memory.setFloat(addr, _float); + } + + // use of TA for store: addr / addr of addr + private int resolveAddr(Flags flags, int addr) { + if (flags.isIndirect()) { + addr = memory.getWordRaw(addr); + if (flags.isIndexed()) + addr += registers.getXs(); + } + return addr; + } + + private void lps(int addr) throws ReadDataBreakpointException { + registers.setSW(memory.getWord(addr+6)); + registers.setPC(memory.getWord(addr+9)); + registers.setA(memory.getWord(addr+12)); + registers.setX(memory.getWord(addr+15)); + registers.setL(memory.getWord(addr+18)); + registers.setB(memory.getWord(addr+21)); + registers.setS(memory.getWord(addr+24)); + registers.setT(memory.getWord(addr+27)); + registers.setF(memory.getFloat(addr+30)); + } - private void storeByte(Flags flags, int operand, int _byte) throws WriteDataBreakpointException { - int addr = resolveAddr(flags, operand); - setLastExecWrite(addr, 1); - memory.setByte(addr, _byte); } - private void storeFloat(Flags flags, int operand, double _float) throws WriteDataBreakpointException { - int addr = resolveAddr(flags, operand); - setLastExecWrite(addr, 6); - memory.setFloat(addr, _float); + private class InvalidInstruction extends Instruction { + public InvalidInstruction(int opcode) { + this.opcode = opcode; + } + + @Override + public void execute() throws DataBreakpointException { + if ( registers.intEnabled(Interrupt.IClass.PROGRAM)) { + programInt = new Interrupt(Interrupt.IClass.PROGRAM, + Interrupt.ProgICODE.ILLEGAL_INSTRUCTION); + } else { + Logger.fmterr("Invalid opcode '%d'.", opcode); + } + } + + @Override + public boolean isPrivileged() { + return false; + } } - public void lps(int addr) throws ReadDataBreakpointException { - registers.setSW(memory.getWord(addr+6)); - registers.setPC(memory.getWord(addr+9)); - registers.setA(memory.getWord(addr+12)); - registers.setX(memory.getWord(addr+15)); - registers.setL(memory.getWord(addr+18)); - registers.setB(memory.getWord(addr+21)); - registers.setS(memory.getWord(addr+24)); - registers.setT(memory.getWord(addr+27)); - registers.setF(memory.getFloat(addr+30)); + // ********** Utility functions for fetch and execute ***************** + private void notImplemented(String mnemonic) { + Logger.fmterr("Instruction '%s' not implemented!", mnemonic); } - private boolean execSICF3F4(int opcode, Flags flags, int operand) throws DataBreakpointException { - // Formats: SIC, F3, F4 - switch (opcode) { - // ***** immediate addressing not possible ***** - // stores - case Opcode.STA: storeWord(flags, operand, registers.getA()); break; - case Opcode.STX: storeWord(flags, operand, registers.getX()); break; - case Opcode.STL: storeWord(flags, operand, registers.getL()); break; - case Opcode.STCH: storeByte(flags, operand, registers.getA()); break; - case Opcode.STB: storeWord(flags, operand, registers.getB()); break; - case Opcode.STS: storeWord(flags, operand, registers.getS()); break; - case Opcode.STF: storeFloat(flags, operand, registers.getF()); break; - case Opcode.STT: storeWord(flags, operand, registers.getT()); break; - case Opcode.STSW: storeWord(flags, operand, registers.getSW()); break; - // jumps - case Opcode.JEQ: if (registers.isEqual()) registers.setPC(resolveAddr(flags, operand)); break; - case Opcode.JGT: if (registers.isGreater()) registers.setPC(resolveAddr(flags, operand)); break; - case Opcode.JLT: if (registers.isLower()) registers.setPC(resolveAddr(flags, operand)); break; - case Opcode.J: registers.setPC(resolveAddr(flags, operand)); break; - case Opcode.RSUB: registers.setPC(registers.getL()); popJSUB(); break; - case Opcode.JSUB: registers.setL(registers.getPC()); pushJSUB(); registers.setPC(resolveAddr(flags, operand)); break; - // ***** immediate addressing possible ***** - // loads - case Opcode.LDA: registers.setA(loadWord(flags, operand)); break; - case Opcode.LDX: registers.setX(loadWord(flags, operand)); break; - case Opcode.LDL: registers.setL(loadWord(flags, operand)); break; - case Opcode.LDCH: registers.setALo(loadByte(flags, operand)); break; - case Opcode.LDB: registers.setB(loadWord(flags, operand)); break; - case Opcode.LDS: registers.setS(loadWord(flags, operand)); break; - case Opcode.LDF: registers.setF(loadFloat(flags, operand)); break; - case Opcode.LDT: registers.setT(loadWord(flags, operand)); break; - // arithmetic - case Opcode.ADD: registers.setA(registers.getA() + loadWord(flags, operand)); break; - case Opcode.SUB: registers.setA(registers.getA() - loadWord(flags, operand)); break; - case Opcode.MUL: registers.setA(registers.getA() * loadWord(flags, operand)); break; - case Opcode.DIV: - int divisor = SICXE.swordToInt(loadWord(flags, operand)); - if (divisor == 0) { - System.out.println("division by zero"); - } else { - registers.setA(registers.getAs() / divisor); - } - break; - case Opcode.AND: registers.setA(registers.getA() & loadWord(flags, operand)); break; - case Opcode.OR: registers.setA(registers.getA() | loadWord(flags, operand)); break; - case Opcode.COMP: registers.setCC(registers.getAs() - SICXE.swordToInt(loadWord(flags, operand))); break; - case Opcode.TIX: registers.setX(registers.getX() + 1); - registers.setCC(registers.getXs() - SICXE.swordToInt(loadWord(flags, operand))); break; - // input/output - case Opcode.RD: registers.setALo(devices.read(loadByte(flags, operand))); break; - case Opcode.WD: devices.write(loadByte(flags, operand), registers.getALo()); break; - case Opcode.TD: registers.setCC(devices.test(loadByte(flags, operand)) ? -1 : 0); break; - // floating point arithmetic - case Opcode.ADDF: registers.setF(registers.getF() + loadFloat(flags, operand)); break; - case Opcode.SUBF: registers.setF(registers.getF() - loadFloat(flags, operand)); break; - case Opcode.MULF: registers.setF(registers.getF() * loadFloat(flags, operand)); break; - case Opcode.DIVF: registers.setF(registers.getF() / loadFloat(flags, operand)); break; - case Opcode.COMPF: double sub = registers.getF() - loadFloat(flags, operand); - registers.setCC(sub > 0 ? 1 : (sub < 0 ? -1 : 0)); - break; - // others - case Opcode.LPS: lps(operand); break; - case Opcode.STI: timer = loadWord(flags, operand); break; - case Opcode.SSK: notImplemented("SSK"); break; - default: return false; + private void invalidAddressing() { + if ( registers.intEnabled(Interrupt.IClass.PROGRAM)) { + programInt = new Interrupt(Interrupt.IClass.PROGRAM, + Interrupt.ProgICODE.ILLEGAL_INSTRUCTION); + } else { + Logger.err("Invalid addressing."); } - return true; } - public int fetch() { + public int fetchByte() { int b = memory.getByteRaw(registers.getPC()); registers.incPC(); return b; } - public void execute() throws DataBreakpointException { - indirectX = false; + public void triggerInterrupts() throws DataBreakpointException { + // Setting interrupt to null clears it. + if (svcInt != null) { + svcInt.trigger(registers, memory); + svcInt = null; + } else if (programInt != null) { + programInt.trigger(registers, memory); + programInt = null; + } else if (timerInt != null) { + timerInt.trigger(registers, memory); + timerInt = null; + } else if (ioInt != null) { + // TODO: IO interrupts. + // We should probably support multiple simultaneous IO interrupts. + // Should they also get cleared by the "HW" device? + ioInt.trigger(registers, memory); + ioInt = null; + } + } + + // Each clock cycle performs two steps: + // 1. fetch and decode + // 2. execute + public Instruction fetchDecode() { + int instructionSize = 0; + Instruction instruction = null; + int opcode = 0; + instructionCount++; - lastExecRead.clear(); - lastExecWrite.clear(); + clearLastExecReadWrite(); + lastExecAddr.setStartAddress(registers.getPC()); lastExecAddr.setSpanLength(0); - // fetch first byte - int opcode = fetch(); - if (Opcode.isPrivileged(opcode) - && !registers.isSupervisor() - && registers.intEnabled(Interrupt.IClass.PROGRAM)) { - programInt = new Interrupt(Interrupt.IClass.PROGRAM, - Interrupt.ProgICODE.PRIVILEGED_INSTRUCTION); - // There has to be a better way to do this. - // Maybe separating decode and execute. - if (Opcode.isF2(opcode)) { - fetch(); - } else if (Opcode.isF34(opcode)) { - int op = fetch(); - Flags flags = new Flags(opcode, op); - fetch(); - if (flags.isExtended()) { - fetch(); - } - } - } else if (Opcode.isF1(opcode)) { - execF1(opcode); - lastExecAddr.setSpanLength(1); + + // Fetch the first byte. + opcode = fetchByte(); + if (Opcode.isF1(opcode)) { + instruction = new InstructionF1(opcode); + instructionSize = 1; } else if (Opcode.isF2(opcode)) { - int op = fetch(); - execF2(opcode, op); - lastExecAddr.setSpanLength(2); + int op = fetchByte(); + instruction = new InstructionF2(opcode, op); + instructionSize = 2; } else if (Opcode.isF34(opcode)) { - int op = fetch(); + int op = fetchByte(); Flags flags = new Flags(opcode, op); - int instructionSize = 0; // operand depends on instruction format int operand; - // check if standard SIC if (flags.isSic()) { - operand = flags.operandSic(op, fetch()); + // ****** Standard SIC ****** + operand = flags.operandSic(op, fetchByte()); instructionSize = 3; - // check if F4 (extended) } else if (flags.isExtended()) { - operand = flags.operandF4(op, fetch(), fetch()); - if (flags.isRelative()) invalidAddressing(); + // ****** F4 (extended) ****** + operand = flags.operandF4(op, fetchByte(), fetchByte()); + if (flags.isRelative()) { + invalidAddressing(); + } instructionSize = 4; - // otherwise it is F3 } else { + // ****** F3 ****** instructionSize = 3; - operand = flags.operandF3(op, fetch()); + operand = flags.operandF3(op, fetchByte()); if (flags.isPCRelative()) operand = flags.operandPCRelative(operand) + registers.getPC(); else if (flags.isBaseRelative()) @@ -362,16 +431,38 @@ else if (flags.isBaseRelative()) else if (!flags.isAbsolute()) invalidAddressing(); // both PC and base at the same time } - // SIC, F3, F4 -- all support indexed addressing, but only when simple TA calculation used - if (flags.isIndexed()) - if (flags.isSimple()) operand += registers.getXs(); - else if(flags.isIndirect()) indirectX = true; - else invalidAddressing(); - // try to execute - execSICF3F4(opcode & 0xFC, flags, operand); - lastExecAddr.setSpanLength(instructionSize); + if (flags.isIndexed()) { + // SIC, F3, F4 -- all support indexed addressing, but only when + // simple TA calculation used + // Indirect indexed addressing is an extension of SicTools to + // simplify complex programs. + if (flags.isSimple()) { + operand += registers.getXs(); + } else if (flags.isIndirect()) { + // Method resolveAddr will add X after first resolution. + // I hope empty elif clause is more readable than + // negation of this condition for invalid addressing. + } else { + invalidAddressing(); + } + } + instruction = new InstructionSICF3F4(opcode & 0xFC, flags, operand); } else { - invalidOpcode(opcode); + instruction = new InvalidInstruction(opcode); + } + lastExecAddr.setSpanLength(instructionSize); + return instruction; + } + + public void execute() throws DataBreakpointException { + Instruction instruction = fetchDecode(); + if (instruction.isPrivileged() + && !registers.isSupervisor() + && registers.intEnabled(Interrupt.IClass.PROGRAM)) { + programInt = new Interrupt(Interrupt.IClass.PROGRAM, + Interrupt.ProgICODE.PRIVILEGED_INSTRUCTION); + } else { + instruction.execute(); } timer--; if (timer <= 0 && registers.intEnabled(Interrupt.IClass.TIMER)) { @@ -379,52 +470,4 @@ else if (!flags.isAbsolute()) } triggerInterrupts(); } - - - // ********** Step over functionality ***************** - - /** - * Push the address bellow current JSUB to the stack, so we can step out of procedure later. - */ - private void pushJSUB() { - this.addressBelowJSUB.push(this.registers.getPC()); - } - - /** - * Pop the last address bellow current JSUB from the stack, since we got out of current function. - * (to be called with RSUB) - */ - private void popJSUB() { - this.addressBelowJSUB.pop(); - } - - /** - * Get the address below the last JSUB was executed, so we can step out. - * @return null if no item on stack - no JSUB encountered, otherwise last address. - */ - public Integer getAddressBelowLastJSUB() { - if (this.addressBelowJSUB.isEmpty()) return null; - else return this.addressBelowJSUB.peek(); - } - - public void triggerInterrupts() throws ReadDataBreakpointException, WriteDataBreakpointException { - // setting interrupt to null clears it - if (svcInt != null) { - svcInt.trigger(registers, memory); - svcInt = null; - } else if (programInt != null) { - programInt.trigger(registers, memory); - programInt = null; - } else if (timerInt != null) { - timerInt.trigger(registers, memory); - timerInt = null; - } else if (ioInt != null) { - // does it get cleared automatically? - // I think on most architectures the interrupt handler must - // clear the interrupt manually. - ioInt.trigger(registers, memory); - ioInt = null; - } - } - } From 3989e75f4833cb34b5b8d36edb4b28617a3756ce Mon Sep 17 00:00:00 2001 From: Kljunas2 Date: Mon, 27 Jan 2025 21:07:53 +0100 Subject: [PATCH 04/12] Minor fixes, mostly to tests. --- src/sic/common/Opcode.java | 4 +- src/sic/sim/vm/Machine.java | 202 ++++++++++++------ src/sic/sim/vm/Registers.java | 1 + tests/interrupts.asm | 11 +- ...pts_priv.asm => interrupts_privileged.asm} | 9 +- tests/interrupts_timer.asm | 52 ++--- 6 files changed, 174 insertions(+), 105 deletions(-) rename tests/{interrupts_priv.asm => interrupts_privileged.asm} (66%) diff --git a/src/sic/common/Opcode.java b/src/sic/common/Opcode.java index 4c74fc4..2008832 100644 --- a/src/sic/common/Opcode.java +++ b/src/sic/common/Opcode.java @@ -121,7 +121,7 @@ public static boolean isF2(int opcode) { } public static boolean isF34(int opcode) { - switch (opcode & 0xFC) { + switch (opcode) { case STA, STX, STL, STCH, STB, STS, STF, STT, STSW, JEQ, JGT, JLT, J, RSUB, JSUB, LDA, LDX, LDL, LDCH, LDB, LDS, LDF, LDT, ADD, SUB, MUL, DIV, AND, OR, COMP, TIX, RD, WD, TD, ADDF, SUBF, MULF, DIVF, COMPF, @@ -133,7 +133,7 @@ public static boolean isF34(int opcode) { public static boolean isPrivileged(int opcode) { switch (opcode) { - case HIO, LPS, /*RD,*/ SIO, SSK, STI, STSW, TD, TIO: /*WD:*/ + case HIO, LPS, RD, SIO, SSK, STI, STSW, TD, TIO, WD: return true; } return false; diff --git a/src/sic/sim/vm/Machine.java b/src/sic/sim/vm/Machine.java index ecd1632..031b78a 100644 --- a/src/sic/sim/vm/Machine.java +++ b/src/sic/sim/vm/Machine.java @@ -82,7 +82,7 @@ public Integer getReturnAddress() { // ********** Execution ********************* // ********** Instruction types ********************* private abstract class Instruction { - public int opcode; + private int opcode; abstract public void execute() throws DataBreakpointException; @@ -99,18 +99,24 @@ public InstructionF1(int opcode) { @Override public void execute() throws DataBreakpointException { switch (opcode) { - case Opcode.FLOAT: registers.setF((double) registers.getAs()); break; - case Opcode.FIX: registers.setA((int) registers.getF()); break; - case Opcode.NORM: notImplemented("NORM"); break; - case Opcode.SIO: notImplemented("SIO"); break; - case Opcode.HIO: notImplemented("HIO"); break; - case Opcode.TIO: notImplemented("TIO"); break; + case Opcode.FLOAT: + registers.setF((double) registers.getAs()); break; + case Opcode.FIX: + registers.setA((int) registers.getF()); break; + case Opcode.NORM: + notImplemented("NORM"); break; + case Opcode.SIO: + notImplemented("SIO"); break; + case Opcode.HIO: + notImplemented("HIO"); break; + case Opcode.TIO: + notImplemented("TIO"); break; } } } private class InstructionF2 extends Instruction { - public int operand; + private int operand; public InstructionF2(int opcode, int operand) { this.opcode = opcode; @@ -123,9 +129,12 @@ public void execute() throws DataBreakpointException { int o1 = (operand & 0xF0) >> 4; int o2 = (operand & 0x0F); switch (opcode) { - case Opcode.ADDR: registers.set(o2, registers.get(o2) + registers.get(o1)); break; - case Opcode.SUBR: registers.set(o2, registers.get(o2) - registers.get(o1)); break; - case Opcode.MULR: registers.set(o2, registers.get(o2) * registers.get(o1)); break; + case Opcode.ADDR: + registers.set(o2, registers.get(o2) + registers.get(o1)); break; + case Opcode.SUBR: + registers.set(o2, registers.get(o2) - registers.get(o1)); break; + case Opcode.MULR: + registers.set(o2, registers.get(o2) * registers.get(o1)); break; case Opcode.DIVR: int divisor = registers.get(o1); if (divisor == 0) { @@ -139,11 +148,16 @@ public void execute() throws DataBreakpointException { registers.set(o2, registers.gets(o2) / divisor); } break; - case Opcode.COMPR: registers.setCC(registers.gets(o1) - registers.gets(o2)); break; - case Opcode.SHIFTL: registers.set(o1, registers.get(o1) << (o2 + 1) | registers.get(o1) >> (24 - o2 - 1)); break; - case Opcode.SHIFTR: registers.set(o1, registers.gets(o1) >> (o2 + 1)); break; - case Opcode.RMO: registers.set(o2, registers.get(o1)); break; - case Opcode.CLEAR: registers.set(o1, 0); break; + case Opcode.COMPR: + registers.setCC(registers.gets(o1) - registers.gets(o2)); break; + case Opcode.SHIFTL: + registers.set(o1, registers.get(o1) << (o2 + 1) | registers.get(o1) >> (24 - o2 - 1)); break; + case Opcode.SHIFTR: + registers.set(o1, registers.gets(o1) >> (o2 + 1)); break; + case Opcode.RMO: + registers.set(o2, registers.get(o1)); break; + case Opcode.CLEAR: + registers.set(o1, 0); break; case Opcode.TIXR: registers.setX(registers.getX()+1); registers.setCC(registers.getXs() - registers.gets(o1)); @@ -160,8 +174,8 @@ public void execute() throws DataBreakpointException { } private class InstructionSICF3F4 extends Instruction { - public Flags flags; - public int operand; + private Flags flags; + private int operand; public InstructionSICF3F4(int opcode, Flags flags, int operand) { this.opcode = opcode; @@ -175,21 +189,43 @@ public void execute() throws DataBreakpointException { switch (opcode) { // ***** immediate addressing not possible ***** // stores - case Opcode.STA: storeWord(flags, operand, registers.getA()); break; - case Opcode.STX: storeWord(flags, operand, registers.getX()); break; - case Opcode.STL: storeWord(flags, operand, registers.getL()); break; - case Opcode.STCH: storeByte(flags, operand, registers.getA()); break; - case Opcode.STB: storeWord(flags, operand, registers.getB()); break; - case Opcode.STS: storeWord(flags, operand, registers.getS()); break; - case Opcode.STF: storeFloat(flags, operand, registers.getF()); break; - case Opcode.STT: storeWord(flags, operand, registers.getT()); break; - case Opcode.STSW: storeWord(flags, operand, registers.getSW()); break; + case Opcode.STA: + storeWord(flags, operand, registers.getA()); break; + case Opcode.STX: + storeWord(flags, operand, registers.getX()); break; + case Opcode.STL: + storeWord(flags, operand, registers.getL()); break; + case Opcode.STCH: + storeByte(flags, operand, registers.getA()); break; + case Opcode.STB: + storeWord(flags, operand, registers.getB()); break; + case Opcode.STS: + storeWord(flags, operand, registers.getS()); break; + case Opcode.STF: + storeFloat(flags, operand, registers.getF()); break; + case Opcode.STT: + storeWord(flags, operand, registers.getT()); break; + case Opcode.STSW: + storeWord(flags, operand, registers.getSW()); break; // jumps - case Opcode.JEQ: if (registers.isEqual()) registers.setPC(resolveAddr(flags, operand)); break; - case Opcode.JGT: if (registers.isGreater()) registers.setPC(resolveAddr(flags, operand)); break; - case Opcode.JLT: if (registers.isLower()) registers.setPC(resolveAddr(flags, operand)); break; - case Opcode.J: registers.setPC(resolveAddr(flags, operand)); break; + case Opcode.JEQ: + if (registers.isEqual()) { + registers.setPC(resolveAddr(flags, operand)); + }; + break; + case Opcode.JGT: + if (registers.isGreater()) { + registers.setPC(resolveAddr(flags, operand)); + }; + break; + case Opcode.JLT: + if (registers.isLower()) { + registers.setPC(resolveAddr(flags, operand)); + }; + break; + case Opcode.J: + registers.setPC(resolveAddr(flags, operand)); break; case Opcode.RSUB: registers.setPC(registers.getL()); callStack.pop(); @@ -202,19 +238,30 @@ public void execute() throws DataBreakpointException { // ***** immediate addressing possible ***** // loads - case Opcode.LDA: registers.setA(loadWord(flags, operand)); break; - case Opcode.LDX: registers.setX(loadWord(flags, operand)); break; - case Opcode.LDL: registers.setL(loadWord(flags, operand)); break; - case Opcode.LDCH: registers.setALo(loadByte(flags, operand)); break; - case Opcode.LDB: registers.setB(loadWord(flags, operand)); break; - case Opcode.LDS: registers.setS(loadWord(flags, operand)); break; - case Opcode.LDF: registers.setF(loadFloat(flags, operand)); break; - case Opcode.LDT: registers.setT(loadWord(flags, operand)); break; + case Opcode.LDA: + registers.setA(loadWord(flags, operand)); break; + case Opcode.LDX: + registers.setX(loadWord(flags, operand)); break; + case Opcode.LDL: + registers.setL(loadWord(flags, operand)); break; + case Opcode.LDCH: + registers.setALo(loadByte(flags, operand)); break; + case Opcode.LDB: + registers.setB(loadWord(flags, operand)); break; + case Opcode.LDS: + registers.setS(loadWord(flags, operand)); break; + case Opcode.LDF: + registers.setF(loadFloat(flags, operand)); break; + case Opcode.LDT: + registers.setT(loadWord(flags, operand)); break; // arithmetic - case Opcode.ADD: registers.setA(registers.getA() + loadWord(flags, operand)); break; - case Opcode.SUB: registers.setA(registers.getA() - loadWord(flags, operand)); break; - case Opcode.MUL: registers.setA(registers.getA() * loadWord(flags, operand)); break; + case Opcode.ADD: + registers.setA(registers.getA() + loadWord(flags, operand)); break; + case Opcode.SUB: + registers.setA(registers.getA() - loadWord(flags, operand)); break; + case Opcode.MUL: + registers.setA(registers.getA() * loadWord(flags, operand)); break; case Opcode.DIV: int divisor = SICXE.swordToInt(loadWord(flags, operand)); if (divisor == 0) { @@ -223,40 +270,49 @@ public void execute() throws DataBreakpointException { registers.setA(registers.getAs() / divisor); } break; - case Opcode.AND: registers.setA(registers.getA() & loadWord(flags, operand)); break; - case Opcode.OR: registers.setA(registers.getA() | loadWord(flags, operand)); break; - case Opcode.COMP: registers.setCC(registers.getAs() - SICXE.swordToInt(loadWord(flags, operand))); break; + case Opcode.AND: + registers.setA(registers.getA() & loadWord(flags, operand)); break; + case Opcode.OR: + registers.setA(registers.getA() | loadWord(flags, operand)); break; + case Opcode.COMP: + registers.setCC(registers.getAs() - SICXE.swordToInt(loadWord(flags, operand))); break; case Opcode.TIX: registers.setX(registers.getX() + 1); registers.setCC(registers.getXs() - SICXE.swordToInt(loadWord(flags, operand))); break; // input/output - case Opcode.RD: registers.setALo(devices.read(loadByte(flags, operand))); break; - case Opcode.WD: devices.write(loadByte(flags, operand), registers.getALo()); break; - case Opcode.TD: registers.setCC(devices.test(loadByte(flags, operand)) ? -1 : 0); break; + case Opcode.RD: + registers.setALo(devices.read(loadByte(flags, operand))); break; + case Opcode.WD: + devices.write(loadByte(flags, operand), registers.getALo()); break; + case Opcode.TD: + registers.setCC(devices.test(loadByte(flags, operand)) ? -1 : 0); break; // floating point arithmetic - case Opcode.ADDF: registers.setF(registers.getF() + loadFloat(flags, operand)); break; - case Opcode.SUBF: registers.setF(registers.getF() - loadFloat(flags, operand)); break; - case Opcode.MULF: registers.setF(registers.getF() * loadFloat(flags, operand)); break; - case Opcode.DIVF: registers.setF(registers.getF() / loadFloat(flags, operand)); break; + case Opcode.ADDF: + registers.setF(registers.getF() + loadFloat(flags, operand)); break; + case Opcode.SUBF: + registers.setF(registers.getF() - loadFloat(flags, operand)); break; + case Opcode.MULF: + registers.setF(registers.getF() * loadFloat(flags, operand)); break; + case Opcode.DIVF: + registers.setF(registers.getF() / loadFloat(flags, operand)); break; case Opcode.COMPF: double sub = registers.getF() - loadFloat(flags, operand); registers.setCC(sub > 0 ? 1 : (sub < 0 ? -1 : 0)); break; + // others - case Opcode.LPS: lps(operand); break; - case Opcode.STI: timer = loadWord(flags, operand); break; - case Opcode.SSK: notImplemented("SSK"); break; + case Opcode.LPS: + lps(operand); break; + case Opcode.STI: + timer = loadWord(flags, operand); break; + case Opcode.SSK: + notImplemented("SSK"); break; } } - @Override - public boolean isPrivileged() { - return Opcode.isPrivileged(opcode & 0xFC); - } - private int loadWord(Flags flags, int operand) throws ReadDataBreakpointException { if (flags.isImmediate()) return operand; int addr = resolveAddr(flags, operand); @@ -404,33 +460,39 @@ public Instruction fetchDecode() { int op = fetchByte(); instruction = new InstructionF2(opcode, op); instructionSize = 2; - } else if (Opcode.isF34(opcode)) { + } else if (Opcode.isF34(opcode & 0xFC)) { int op = fetchByte(); Flags flags = new Flags(opcode, op); - // operand depends on instruction format int operand; if (flags.isSic()) { - // ****** Standard SIC ****** - operand = flags.operandSic(op, fetchByte()); + // ****** Standard SIC ******* instructionSize = 3; + operand = flags.operandSic(op, fetchByte()); } else if (flags.isExtended()) { // ****** F4 (extended) ****** + instructionSize = 4; operand = flags.operandF4(op, fetchByte(), fetchByte()); + if (flags.isRelative()) { invalidAddressing(); } - instructionSize = 4; } else { - // ****** F3 ****** + // ****** F3 ***************** instructionSize = 3; operand = flags.operandF3(op, fetchByte()); - if (flags.isPCRelative()) + + // Handle relative addressing. + if (flags.isPCRelative()) { operand = flags.operandPCRelative(operand) + registers.getPC(); - else if (flags.isBaseRelative()) + } else if (flags.isBaseRelative()) { operand += registers.getB(); - else if (!flags.isAbsolute()) - invalidAddressing(); // both PC and base at the same time + } else if (!flags.isAbsolute()) { + // both PC and base at the same time + invalidAddressing(); + } } + + // Handle indexed addressing. if (flags.isIndexed()) { // SIC, F3, F4 -- all support indexed addressing, but only when // simple TA calculation used diff --git a/src/sic/sim/vm/Registers.java b/src/sic/sim/vm/Registers.java index 19ad23b..7a58efc 100644 --- a/src/sic/sim/vm/Registers.java +++ b/src/sic/sim/vm/Registers.java @@ -49,6 +49,7 @@ public void setPC(int val) { public void incPC() { if (++PC > Machine.MAX_ADDRESS) { + // TODO: interrupt? Logger.fmterr("PC register overflow."); PC = 0; } diff --git a/tests/interrupts.asm b/tests/interrupts.asm index 1bc662e..dba379d 100644 --- a/tests/interrupts.asm +++ b/tests/interrupts.asm @@ -44,13 +44,13 @@ add2 ADD #0x30 WD #1 LDCH #0x0a WD #1 + . This SVC interrupt will not happen because all interrupts are masked. SVC 13 - LPS #svc_int init_regs RESW 2 - WORD 0x000800 . enable SVC, user mode + WORD 0x000801 . enable SVC, supervisor mode (because of WD) init_addr RESW 1 RESW 8 . init all registers to 0 @@ -60,12 +60,13 @@ sw RESW 1 . switch to user mode, enable interrupts entry LDA #svc_handler STA svc_haddr - LDA #program + LDA #main STA init_addr LPS #init_regs - .ORG 1000 -program LDCH #0x41 +. main function: print A, then request two interrupts and print A (to show that +. the register stays the same) and then B +main LDCH #0x41 WD #1 SVC 0x1a SVC 0x2f diff --git a/tests/interrupts_priv.asm b/tests/interrupts_privileged.asm similarity index 66% rename from tests/interrupts_priv.asm rename to tests/interrupts_privileged.asm index 344bb82..d4108ef 100644 --- a/tests/interrupts_priv.asm +++ b/tests/interrupts_privileged.asm @@ -3,14 +3,15 @@ ints START 0 ORG 0x130 . first byte will be filled by the interrupt's value - . second nibble of the second byte is interrupt mask (0 = disable all interrupts) + . second nibble of the second byte is interrupt mask (don't disable + . any interrupt here, to demonstrate successful execution of TD) . the third byte goes to supervisor mode -prog_int WORD 0x000001 +prog_int WORD 0x000f01 prog_haddr RESW 1 RESW 10 . 9 registers, one of them 2 WORDS long prog_handler - TD #1 + TD #1 . This TD will not trigger an interrupt. LPS #prog_int rand WORD 1 @@ -32,7 +33,7 @@ entry LDA #prog_handler STI #40 LPS #init_regs -program TD #1 +program TD #1 . This TD will trigger an interrupt. J program END entry diff --git a/tests/interrupts_timer.asm b/tests/interrupts_timer.asm index e10c0d8..e4df6ec 100644 --- a/tests/interrupts_timer.asm +++ b/tests/interrupts_timer.asm @@ -1,15 +1,21 @@ -ints START 0 - +. Run with rand.jar addon. +. You can play with the program: +. Stop the execution. If SW is equal to 0x000201, set it to 1 and resume execution. +. This will disable interrupts and only As will be printed. Then stop it again +. and reset SW back to 0x201. Interrupts will be enabled again. +ints START 0 +. Register work area for timer interrupts ORG 0x160 . first byte will be filled by the interrupt's value . second nibble of the second byte is interrupt mask (0 = disable all interrupts) . the third byte goes to supervisor mode -svc_int WORD 0x000001 -svc_haddr RESW 1 - RESW 10 . 9 registers, one of them 2 WORDS long +timer_int WORD 0x000001 . SW will be set to this after the interrupt has been triggered. +timer_haddr RESW 1 . The address of handler routine. + RESW 10 . 9 registers, one of them (F) 2 WORDS long -svc_handler +timer_handler + . Write "\nTIM\n" LDCH #0x0a WD #1 LDCH #0x54 @@ -20,35 +26,33 @@ svc_handler WD #1 LDCH #0x0a WD #1 + . Get a random number and set the timer to it. CLEAR A RD #3 STA rand STI rand - //SVC 13 + . Return to the program. + LPS #timer_int +rand RESW 1 - LPS #svc_int -rand WORD 1 - - -init_regs RESW 2 - WORD 0x000200 . enable SVC, user mode -init_addr RESW 1 +init_regs RESW 2 + . software will set the SW to the following value: + WORD 0x000201 . enable TIMER interrupts, supervisor mode (we need WD) +init_addr RESW 1 . The start address of the program. RESW 8 . init all registers to 0 -sw RESW 1 - - . switch to user mode, enable interrupts -entry LDA #svc_handler - STA svc_haddr - LDA #program - STA init_addr - STI #40 + . Initialization: +entry LDA #timer_handler + STA timer_haddr . set the value of timer_haddr to #timer_handler. + LDA #main + STA init_addr . set the value of init_addr to #main. LPS #init_regs -program LDCH #0x41 +. main function: loop that prints the character A. +main LDCH #0x41 WD #1 -halt J program + J main END entry From 81b203ca9a39eaf5a850bc4eb35e8d25c2aa0fdd Mon Sep 17 00:00:00 2001 From: Kljunas2 Date: Mon, 27 Jan 2025 21:29:17 +0100 Subject: [PATCH 05/12] Implement idle state, pseudo halt is also disabled if interrupts are enabled. --- src/sic/sim/Executor.java | 9 ++++++--- src/sic/sim/vm/Machine.java | 22 ++++++++++---------- tests/idle.asm | 40 +++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 tests/idle.asm diff --git a/src/sic/sim/Executor.java b/src/sic/sim/Executor.java index 84cfd7d..562c96f 100644 --- a/src/sic/sim/Executor.java +++ b/src/sic/sim/Executor.java @@ -4,6 +4,7 @@ import sic.sim.breakpoints.DataBreakpointException; import sic.sim.breakpoints.DataBreakpoints; import sic.sim.vm.Machine; +import sic.sim.vm.Interrupt; import java.awt.event.ActionListener; import java.util.Timer; @@ -67,7 +68,7 @@ private void timerTickUntil(Predicate stopPredicate) { int oldPC = machine.registers.getPC(); try { - machine.execute(); + machine.step(); if (!dataBreakpoints.isEnabled()) { // Enable data breakpoints in case they got disabled because they were triggered. @@ -83,7 +84,9 @@ private void timerTickUntil(Predicate stopPredicate) { hasChanged = true; // check if the same instruction: halt J halt - if (oldPC == machine.registers.getPC()) { + if (oldPC == machine.registers.getPC() + && !machine.registers.intEnabled(Interrupt.IClass.TIMER) + && !machine.registers.intEnabled(Interrupt.IClass.IO)) { stop(); if (printStats) { System.out.printf("Instructions executed: %d\n", machine.getInstructionCount()); @@ -137,7 +140,7 @@ public void step() { dataBreakpoints.disable(); try { - machine.execute(); + machine.step(); } catch (DataBreakpointException ex) { // Shouldn't be triggered when breakpoints are disabled } diff --git a/src/sic/sim/vm/Machine.java b/src/sic/sim/vm/Machine.java index 031b78a..f09c656 100644 --- a/src/sic/sim/vm/Machine.java +++ b/src/sic/sim/vm/Machine.java @@ -82,7 +82,7 @@ public Integer getReturnAddress() { // ********** Execution ********************* // ********** Instruction types ********************* private abstract class Instruction { - private int opcode; + protected int opcode; abstract public void execute() throws DataBreakpointException; @@ -516,15 +516,17 @@ public Instruction fetchDecode() { return instruction; } - public void execute() throws DataBreakpointException { - Instruction instruction = fetchDecode(); - if (instruction.isPrivileged() - && !registers.isSupervisor() - && registers.intEnabled(Interrupt.IClass.PROGRAM)) { - programInt = new Interrupt(Interrupt.IClass.PROGRAM, - Interrupt.ProgICODE.PRIVILEGED_INSTRUCTION); - } else { - instruction.execute(); + public void step() throws DataBreakpointException { + if (!registers.isIdle()) { + Instruction instruction = fetchDecode(); + if (instruction.isPrivileged() + && !registers.isSupervisor() + && registers.intEnabled(Interrupt.IClass.PROGRAM)) { + programInt = new Interrupt(Interrupt.IClass.PROGRAM, + Interrupt.ProgICODE.PRIVILEGED_INSTRUCTION); + } else { + instruction.execute(); + } } timer--; if (timer <= 0 && registers.intEnabled(Interrupt.IClass.TIMER)) { diff --git a/tests/idle.asm b/tests/idle.asm new file mode 100644 index 0000000..a34202c --- /dev/null +++ b/tests/idle.asm @@ -0,0 +1,40 @@ +idle START 0 + . this should get executed but it won't because processor is + . in idle state. PC won't change, nothing will be ouput. + . You can verify in CPU view that A is 0x41 ('A') and instruction is + . WD #1. + . If you set the SW (on line 28 of this file) to 0x201, then the As will be printed. +start WD #1 + J start + + ORG 0x160 +timer_int WORD 0x000001 +timer_haddr RESW 1 + RESW 10 + +timer_handler + . Write "X\n" + LDCH #0x58 + WD #1 + LDCH #0x0a + WD #1 + STI time + LPS #timer_int + +time WORD 40 + + +init_regs RESW 2 + . software will set the SW to the following value: + WORD 0x000202 . enable TIMER interrupts, IDLE state, user mode + WORD 0 . The start address of the program (in that case nothing will be run). + WORD 0x41 + RESW 8 . init all registers to 0 + + + . Initialization: +entry LDA #timer_handler + STA timer_haddr . set the value of timer_haddr to #timer_handler. + LPS #init_regs + + END entry From 48efa72a92472a9cd527a41837ff3f49d42e8040 Mon Sep 17 00:00:00 2001 From: Kljunas2 Date: Fri, 2 May 2025 12:44:39 +0200 Subject: [PATCH 06/12] Cleanup code --- src/sic/ast/instructions/InstructionF2n.java | 1 + src/sic/sim/Executor.java | 4 +- .../components/treetable/JTreeTable.java | 2 +- src/sic/sim/vm/Interrupt.java | 67 ++++++++-------- src/sic/sim/vm/Machine.java | 77 ++++++++++--------- src/sic/sim/vm/Registers.java | 18 ++--- 6 files changed, 87 insertions(+), 82 deletions(-) diff --git a/src/sic/ast/instructions/InstructionF2n.java b/src/sic/ast/instructions/InstructionF2n.java index 5014fe3..a34556d 100644 --- a/src/sic/ast/instructions/InstructionF2n.java +++ b/src/sic/ast/instructions/InstructionF2n.java @@ -25,6 +25,7 @@ public String operandToString() { @Override public void emitRawCode(byte[] data, int loc) { + // The original "specification" is a bit unclear about the size of number. emitRawCode(data, loc, (number>>4)&0xf, number&0xf); } diff --git a/src/sic/sim/Executor.java b/src/sic/sim/Executor.java index 562c96f..45d2cde 100644 --- a/src/sic/sim/Executor.java +++ b/src/sic/sim/Executor.java @@ -85,8 +85,8 @@ private void timerTickUntil(Predicate stopPredicate) { hasChanged = true; // check if the same instruction: halt J halt if (oldPC == machine.registers.getPC() - && !machine.registers.intEnabled(Interrupt.IClass.TIMER) - && !machine.registers.intEnabled(Interrupt.IClass.IO)) { + && !machine.registers.intEnabled(Interrupt.IntClass.TIMER) + && !machine.registers.intEnabled(Interrupt.IntClass.IO)) { stop(); if (printStats) { System.out.printf("Instructions executed: %d\n", machine.getInstructionCount()); diff --git a/src/sic/sim/views/components/treetable/JTreeTable.java b/src/sic/sim/views/components/treetable/JTreeTable.java index 3e01baa..43f1c39 100644 --- a/src/sic/sim/views/components/treetable/JTreeTable.java +++ b/src/sic/sim/views/components/treetable/JTreeTable.java @@ -268,7 +268,7 @@ public boolean isCellEditable(EventObject e) { if (getColumnClass(counter) == TreeTableModel.class) { MouseEvent me = (MouseEvent)e; MouseEvent newME = new MouseEvent(tree, me.getID(), - me.getWhen(), me.getModifiers(), + me.getWhen(), me.getModifiersEx(), me.getX() - getCellRect(0, counter, true).x, me.getY(), me.getClickCount(), me.isPopupTrigger()); diff --git a/src/sic/sim/vm/Interrupt.java b/src/sic/sim/vm/Interrupt.java index 4dc3d87..c80d979 100644 --- a/src/sic/sim/vm/Interrupt.java +++ b/src/sic/sim/vm/Interrupt.java @@ -4,7 +4,7 @@ import sic.sim.breakpoints.WriteDataBreakpointException; public class Interrupt { - public enum IClass { + public enum IntClass { SVC(8), PROGRAM(4), TIMER(2), @@ -12,12 +12,12 @@ public enum IClass { public final int value; - IClass(int value) { + IntClass(int value) { this.value = value; } } - public enum ProgICODE { + public enum ProgramIntCode { ILLEGAL_INSTRUCTION(0x00), PRIVILEGED_INSTRUCTION(0x01), ADDR_OUT_OF_RANGE(0x02), @@ -30,26 +30,30 @@ public enum ProgICODE { public final int value; - ProgICODE(int value) { + ProgramIntCode(int value) { this.value = value; } } - public IClass CLASS; - private int ICODE; + public IntClass intClass; + private int intCode = 0; - public Interrupt(IClass iclass, int icode) { - CLASS = iclass; - ICODE = icode; + public Interrupt(IntClass intClass) { + this.intClass = intClass; } - public Interrupt(IClass iclass, ProgICODE icode) { - CLASS = iclass; - ICODE = icode.value; + public Interrupt(IntClass intClass, int intCode) { + this.intClass = intClass; + this.intCode = intCode; } - public static int getWorkArea(IClass CLASS) { - switch (CLASS) { + public Interrupt(IntClass intClass, ProgramIntCode intCode) { + this.intClass = intClass; + this.intCode = intCode.value; + } + + public int getWorkArea() { + switch (intClass) { case SVC: return 0x100; case PROGRAM: @@ -62,28 +66,21 @@ public static int getWorkArea(IClass CLASS) { return -1; } - private void saveRegisters(Registers registers, Memory memory) throws WriteDataBreakpointException { - int addr = Interrupt.getWorkArea(CLASS); - memory.setWord(addr+6, registers.getSW()); - memory.setWord(addr+9, registers.getPC()); - memory.setWord(addr+12, registers.getA()); - memory.setWord(addr+15, registers.getX()); - memory.setWord(addr+18, registers.getL()); - memory.setWord(addr+21, registers.getB()); - memory.setWord(addr+24, registers.getS()); - memory.setWord(addr+27, registers.getT()); - memory.setFloat(addr+30, registers.getF()); - } + public void trigger(Registers registers, Memory memory) throws ReadDataBreakpointException, WriteDataBreakpointException { + int addr = getWorkArea(); - private void restoreRegisters(Registers registers, Memory memory) throws ReadDataBreakpointException { - int addr = Interrupt.getWorkArea(CLASS); - } + memory.setWordRaw(addr+6, registers.getSW()); + memory.setWordRaw(addr+9, registers.getPC()); + memory.setWordRaw(addr+12, registers.getA()); + memory.setWordRaw(addr+15, registers.getX()); + memory.setWordRaw(addr+18, registers.getL()); + memory.setWordRaw(addr+21, registers.getB()); + memory.setWordRaw(addr+24, registers.getS()); + memory.setWordRaw(addr+27, registers.getT()); + memory.setFloatRaw(addr+30, registers.getF()); - public void trigger(Registers registers, Memory memory) throws ReadDataBreakpointException, WriteDataBreakpointException { - saveRegisters(registers, memory); - int addr = Interrupt.getWorkArea(CLASS); - registers.setSW(memory.getWord(addr)); - registers.setPC(memory.getWord(addr+3)); - registers.setICODE(ICODE); + registers.setSW(memory.getWordRaw(addr)); + registers.setPC(memory.getWordRaw(addr+3)); + registers.setIntCode(intCode); } } diff --git a/src/sic/sim/vm/Machine.java b/src/sic/sim/vm/Machine.java index f09c656..ba9f518 100644 --- a/src/sic/sim/vm/Machine.java +++ b/src/sic/sim/vm/Machine.java @@ -138,12 +138,7 @@ public void execute() throws DataBreakpointException { case Opcode.DIVR: int divisor = registers.get(o1); if (divisor == 0) { - if ( registers.intEnabled(Interrupt.IClass.PROGRAM)) { - programInt = new Interrupt(Interrupt.IClass.PROGRAM, - Interrupt.ProgICODE.ILLEGAL_INSTRUCTION); - } else { - System.out.println("division by zero"); - } + divisionByZero(); } else { registers.set(o2, registers.gets(o2) / divisor); } @@ -163,11 +158,11 @@ public void execute() throws DataBreakpointException { registers.setCC(registers.getXs() - registers.gets(o1)); break; case Opcode.SVC: - if (!registers.intEnabled(Interrupt.IClass.SVC)) { + if (!registers.intEnabled(Interrupt.IntClass.SVC)) { Logger.fmterr("SVC is disabled"); break; } - svcInt = new Interrupt(Interrupt.IClass.SVC, operand); + svcInt = new Interrupt(Interrupt.IntClass.SVC, operand); break; } } @@ -210,17 +205,17 @@ public void execute() throws DataBreakpointException { // jumps case Opcode.JEQ: - if (registers.isEqual()) { + if (registers.ccIsEqual()) { registers.setPC(resolveAddr(flags, operand)); }; break; case Opcode.JGT: - if (registers.isGreater()) { + if (registers.ccIsGreater()) { registers.setPC(resolveAddr(flags, operand)); }; break; case Opcode.JLT: - if (registers.isLower()) { + if (registers.ccIsLower()) { registers.setPC(resolveAddr(flags, operand)); }; break; @@ -265,7 +260,7 @@ public void execute() throws DataBreakpointException { case Opcode.DIV: int divisor = SICXE.swordToInt(loadWord(flags, operand)); if (divisor == 0) { - System.out.println("division by zero"); + divisionByZero(); } else { registers.setA(registers.getAs() / divisor); } @@ -356,6 +351,7 @@ private void storeFloat(Flags flags, int operand, double _float) throws WriteDat private int resolveAddr(Flags flags, int addr) { if (flags.isIndirect()) { addr = memory.getWordRaw(addr); + // SicTools extension if (flags.isIndexed()) addr += registers.getXs(); } @@ -363,15 +359,15 @@ private int resolveAddr(Flags flags, int addr) { } private void lps(int addr) throws ReadDataBreakpointException { - registers.setSW(memory.getWord(addr+6)); - registers.setPC(memory.getWord(addr+9)); - registers.setA(memory.getWord(addr+12)); - registers.setX(memory.getWord(addr+15)); - registers.setL(memory.getWord(addr+18)); - registers.setB(memory.getWord(addr+21)); - registers.setS(memory.getWord(addr+24)); - registers.setT(memory.getWord(addr+27)); - registers.setF(memory.getFloat(addr+30)); + registers.setSW(memory.getWordRaw(addr+6)); + registers.setPC(memory.getWordRaw(addr+9)); + registers.setA(memory.getWordRaw(addr+12)); + registers.setX(memory.getWordRaw(addr+15)); + registers.setL(memory.getWordRaw(addr+18)); + registers.setB(memory.getWordRaw(addr+21)); + registers.setS(memory.getWordRaw(addr+24)); + registers.setT(memory.getWordRaw(addr+27)); + registers.setF(memory.getFloatRaw(addr+30)); } } @@ -383,9 +379,9 @@ public InvalidInstruction(int opcode) { @Override public void execute() throws DataBreakpointException { - if ( registers.intEnabled(Interrupt.IClass.PROGRAM)) { - programInt = new Interrupt(Interrupt.IClass.PROGRAM, - Interrupt.ProgICODE.ILLEGAL_INSTRUCTION); + if ( registers.intEnabled(Interrupt.IntClass.PROGRAM)) { + programInt = new Interrupt(Interrupt.IntClass.PROGRAM, + Interrupt.ProgramIntCode.ILLEGAL_INSTRUCTION); } else { Logger.fmterr("Invalid opcode '%d'.", opcode); } @@ -403,14 +399,23 @@ private void notImplemented(String mnemonic) { } private void invalidAddressing() { - if ( registers.intEnabled(Interrupt.IClass.PROGRAM)) { - programInt = new Interrupt(Interrupt.IClass.PROGRAM, - Interrupt.ProgICODE.ILLEGAL_INSTRUCTION); + if ( registers.intEnabled(Interrupt.IntClass.PROGRAM)) { + programInt = new Interrupt(Interrupt.IntClass.PROGRAM, + Interrupt.ProgramIntCode.ILLEGAL_INSTRUCTION); } else { Logger.err("Invalid addressing."); } } + private void divisionByZero() { + if ( registers.intEnabled(Interrupt.IntClass.PROGRAM)) { + programInt = new Interrupt(Interrupt.IntClass.PROGRAM, + Interrupt.ProgramIntCode.ILLEGAL_INSTRUCTION); + } else { + Logger.err("division by zero"); + } + } + public int fetchByte() { int b = memory.getByteRaw(registers.getPC()); registers.incPC(); @@ -501,7 +506,7 @@ public Instruction fetchDecode() { if (flags.isSimple()) { operand += registers.getXs(); } else if (flags.isIndirect()) { - // Method resolveAddr will add X after first resolution. + // Method resolveAddr will add X after the first resolution. // I hope empty elif clause is more readable than // negation of this condition for invalid addressing. } else { @@ -519,18 +524,20 @@ public Instruction fetchDecode() { public void step() throws DataBreakpointException { if (!registers.isIdle()) { Instruction instruction = fetchDecode(); - if (instruction.isPrivileged() - && !registers.isSupervisor() - && registers.intEnabled(Interrupt.IClass.PROGRAM)) { - programInt = new Interrupt(Interrupt.IClass.PROGRAM, - Interrupt.ProgICODE.PRIVILEGED_INSTRUCTION); + if (instruction.isPrivileged() && !registers.isSupervisor()) { + if (registers.intEnabled(Interrupt.IntClass.PROGRAM)) { + programInt = new Interrupt(Interrupt.IntClass.PROGRAM, + Interrupt.ProgramIntCode.PRIVILEGED_INSTRUCTION); + } else { + Logger.err("skipping execution of privileged instruction"); + } } else { instruction.execute(); } } timer--; - if (timer <= 0 && registers.intEnabled(Interrupt.IClass.TIMER)) { - timerInt = new Interrupt(Interrupt.IClass.TIMER, 0); + if (timer <= 0 && registers.intEnabled(Interrupt.IntClass.TIMER) && timerInt == null) { + timerInt = new Interrupt(Interrupt.IntClass.TIMER); } triggerInterrupts(); } diff --git a/src/sic/sim/vm/Registers.java b/src/sic/sim/vm/Registers.java index 7a58efc..882a9a5 100644 --- a/src/sic/sim/vm/Registers.java +++ b/src/sic/sim/vm/Registers.java @@ -30,7 +30,7 @@ private class StatusWord { // interrupt mask public int MASK = 0; // interrupt code - public int ICODE = 0; + public int INT_CODE = 0; } private StatusWord SW = new StatusWord(); @@ -156,19 +156,19 @@ public int getSW() { } value |= cc<<6; value |= SW.MASK<<8; - value |= SW.ICODE<<16; + value |= SW.INT_CODE<<16; return value; } - public boolean isLower() { + public boolean ccIsLower() { return SW.CC < 0; } - public boolean isEqual() { + public boolean ccIsEqual() { return SW.CC == 0; } - public boolean isGreater() { + public boolean ccIsGreater() { return SW.CC > 0; } @@ -184,8 +184,8 @@ public int processID() { return SW.ID; } - public void setICODE(int value) { - SW.ICODE = value & 0xff; + public void setIntCode(int value) { + SW.INT_CODE = value & 0xff; } public void setSW(int value) { @@ -201,14 +201,14 @@ public void setSW(int value) { SW.CC = 0; } SW.MASK = (value>>8)&0xf; - SW.ICODE = (value>>16)&0x8; + SW.INT_CODE = (value>>16)&0x8; } public void setCC(int compare) { SW.CC = compare; } - public boolean intEnabled(Interrupt.IClass c) { + public boolean intEnabled(Interrupt.IntClass c) { return (SW.MASK & c.value) > 0; } From d58fe6f0322930ec8ea5485f3bae478387209b2e Mon Sep 17 00:00:00 2001 From: Kljunas2 Date: Fri, 15 Aug 2025 01:56:54 +0200 Subject: [PATCH 07/12] Enable setting the F register from the CPU view --- src/sic/sim/views/CPUView.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sic/sim/views/CPUView.java b/src/sic/sim/views/CPUView.java index 0411322..bea60c3 100644 --- a/src/sic/sim/views/CPUView.java +++ b/src/sic/sim/views/CPUView.java @@ -123,6 +123,18 @@ public void actionPerformed(ActionEvent evt) { updateView(); } }); + regF.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + registers.setF(SICXE.bitsToFloat(Long.parseLong(regF.getText(), 16))); + updateView(); + } + }); + regFF.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + registers.setF(Double.parseDouble(regFF.getText())); + updateView(); + } + }); updateView(); } From 778bd9fea7ca9f1b2ca861d0d6a5264f167593b0 Mon Sep 17 00:00:00 2001 From: Kljunas2 Date: Fri, 15 Aug 2025 02:01:51 +0200 Subject: [PATCH 08/12] Move timer to the Registers class --- src/sic/sim/vm/Machine.java | 8 +++----- src/sic/sim/vm/Registers.java | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/sic/sim/vm/Machine.java b/src/sic/sim/vm/Machine.java index ba9f518..8f2e8d8 100644 --- a/src/sic/sim/vm/Machine.java +++ b/src/sic/sim/vm/Machine.java @@ -20,8 +20,6 @@ public class Machine { public final Memory memory = new Memory(MAX_ADDRESS+1); public final Devices devices = new Devices(MAX_DEVICE+1); - private int timer = 0; - private Interrupt svcInt = null; private Interrupt programInt = null; private Interrupt timerInt = null; @@ -302,7 +300,7 @@ public void execute() throws DataBreakpointException { case Opcode.LPS: lps(operand); break; case Opcode.STI: - timer = loadWord(flags, operand); break; + registers.setTimer(loadWord(flags, operand)); break; case Opcode.SSK: notImplemented("SSK"); break; } @@ -535,8 +533,8 @@ public void step() throws DataBreakpointException { instruction.execute(); } } - timer--; - if (timer <= 0 && registers.intEnabled(Interrupt.IntClass.TIMER) && timerInt == null) { + registers.tickTimer(); + if (registers.getTimer() <= 0 && registers.intEnabled(Interrupt.IntClass.TIMER) && timerInt == null) { timerInt = new Interrupt(Interrupt.IntClass.TIMER); } triggerInterrupts(); diff --git a/src/sic/sim/vm/Registers.java b/src/sic/sim/vm/Registers.java index 882a9a5..e263e26 100644 --- a/src/sic/sim/vm/Registers.java +++ b/src/sic/sim/vm/Registers.java @@ -18,6 +18,8 @@ public class Registers { // SIC/XE 48-bit float register private double F; + private int timer = 0; + private class StatusWord { // 0 = user, 1 = supervisor public int MODE = 1; @@ -212,6 +214,18 @@ public boolean intEnabled(Interrupt.IntClass c) { return (SW.MASK & c.value) > 0; } + public int getTimer() { + return timer; + } + + public void setTimer(int timer) { + this.timer = timer; + } + + public void tickTimer() { + timer--; + } + // ***** getter/setter by register index **** public static final int rA = 0; From 36f9cd8ad9032c6467d076a431499060e8b4cf6b Mon Sep 17 00:00:00 2001 From: Kljunas2 Date: Fri, 15 Aug 2025 02:02:39 +0200 Subject: [PATCH 09/12] Fix parsing SW fields from the value --- src/sic/sim/vm/Registers.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sic/sim/vm/Registers.java b/src/sic/sim/vm/Registers.java index e263e26..c673b24 100644 --- a/src/sic/sim/vm/Registers.java +++ b/src/sic/sim/vm/Registers.java @@ -203,7 +203,7 @@ public void setSW(int value) { SW.CC = 0; } SW.MASK = (value>>8)&0xf; - SW.INT_CODE = (value>>16)&0x8; + SW.INT_CODE = (value>>16)&0xff; } public void setCC(int compare) { From 89754b6a086f1b0b7c2f2676e813c52cbfa60c98 Mon Sep 17 00:00:00 2001 From: Kljunas2 Date: Fri, 15 Aug 2025 02:13:06 +0200 Subject: [PATCH 10/12] Add timer and SW explanation to the CPU view --- src/sic/sim/views/CPUView.java | 283 ++++++++++++++++++++------------- src/sic/sim/vm/Registers.java | 21 +++ 2 files changed, 194 insertions(+), 110 deletions(-) diff --git a/src/sic/sim/views/CPUView.java b/src/sic/sim/views/CPUView.java index bea60c3..52acf1c 100644 --- a/src/sic/sim/views/CPUView.java +++ b/src/sic/sim/views/CPUView.java @@ -6,6 +6,7 @@ import sic.disasm.Disassembler; import sic.sim.Executor; import sic.sim.Colors; +import sic.sim.vm.Interrupt; import sic.sim.vm.Machine; import sic.sim.vm.Registers; @@ -26,7 +27,6 @@ public class CPUView { private final Color colorNochange = Colors.fg; private final Color colorChange = Color.BLUE; - private final Color colorSupervisor = Color.RED; private final Executor executor; private final Machine machine; @@ -44,11 +44,12 @@ public class CPUView { private JTextField regFF; private JTextField regPC; private JTextField txtInstruction; + private JTextField timer; private JButton btnStep; private JButton btnStartStop; public JPanel mainPanel; private JLabel lblInfo; - private JLabel swLabel; + private JLabel lblInfoSW; public CPUView(final Executor executor, final Disassembler disassembler) { this.executor = executor; @@ -123,6 +124,12 @@ public void actionPerformed(ActionEvent evt) { updateView(); } }); + timer.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + registers.setTimer(Integer.parseInt(timer.getText())); + updateView(); + } + }); regF.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { registers.setF(SICXE.bitsToFloat(Long.parseLong(regF.getText(), 16))); @@ -138,6 +145,12 @@ public void actionPerformed(ActionEvent evt) { updateView(); } + private void updateTimer(int val) { + String str = String.format("%d", val); + timer.setForeground(registers.intEnabled(Interrupt.IntClass.TIMER) ? Colors.fg : Colors.selectionInactiveFg); + timer.setText(str); + } + private void updateRegWord(JTextField txt, int val) { String str = Conversion.wordToHex(val); if (txt.getText().equals(str)) @@ -174,221 +187,266 @@ public void updateView() { updateRegWord(regT, registers.getT()); updateRegWord(regB, registers.getB()); updateRegWord(regSW, registers.getSW()); - swLabel.setForeground(registers.isSupervisor() ? colorSupervisor : colorNochange); updateRegFloat(registers.getF()); updateRegWord(regPC, registers.getPC()); + updateTimer(registers.getTimer()); // btnStartStop.setText(executor.isRunning() ? "Stop" : "Start"); // Command cmd = disassembler.disassemble(registers.getPC()); txtInstruction.setText(cmd == null ? "" : cmd.toString()); lblInfo.setText(cmd == null ? "" : "" + cmd.explain() + ""); + lblInfoSW.setText(cmd == null ? "" : "" + registers.explainSW() + ""); } { -// GUI initializer generated by IntelliJ IDEA GUI Designer -// >>> IMPORTANT!! <<< -// DO NOT EDIT OR ADD ANY CODE HERE! - $$$setupUI$$$(); + setupUI(); } - /** - * Method generated by IntelliJ IDEA GUI Designer - * >>> IMPORTANT!! <<< - * DO NOT edit this method OR call it in your code! - * - * @noinspection ALL - */ - private void $$$setupUI$$$() { + private void setupUI() { + GridBagConstraints gbc; + mainPanel = new JPanel(); - mainPanel.setLayout(new BorderLayout(0, 0)); + mainPanel.setLayout(new GridBagLayout()); mainPanel.setEnabled(true); mainPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLoweredBevelBorder(), "CPU", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null)); - final JPanel panel1 = new JPanel(); - panel1.setLayout(new GridBagLayout()); - panel1.setAlignmentX(0.5f); - panel1.setAutoscrolls(false); - panel1.setEnabled(true); - mainPanel.add(panel1, BorderLayout.CENTER); - final JLabel label1 = new JLabel(); - label1.setText("X"); - GridBagConstraints gbc; + + // register A + final JLabel labelA = new JLabel(); + labelA.setText("A"); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.anchor = GridBagConstraints.EAST; + mainPanel.add(labelA, gbc); + + regA = new JTextField(); + regA.setColumns(8); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 0; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + mainPanel.add(regA, gbc); + + // register x + final JLabel labelX = new JLabel(); + labelX.setText("X"); gbc = new GridBagConstraints(); gbc.gridx = 2; gbc.gridy = 0; gbc.anchor = GridBagConstraints.EAST; - panel1.add(label1, gbc); + mainPanel.add(labelX, gbc); + regX = new JTextField(); regX.setColumns(8); - regX.setText("000002"); gbc = new GridBagConstraints(); gbc.gridx = 3; gbc.gridy = 0; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; - panel1.add(regX, gbc); - final JLabel label2 = new JLabel(); - label2.setText("L"); + mainPanel.add(regX, gbc); + + // register L + final JLabel labelL = new JLabel(); + labelL.setText("L"); gbc = new GridBagConstraints(); gbc.gridx = 4; gbc.gridy = 0; gbc.anchor = GridBagConstraints.EAST; - panel1.add(label2, gbc); + mainPanel.add(labelL, gbc); + regL = new JTextField(); regL.setColumns(8); - regL.setText("000003"); gbc = new GridBagConstraints(); gbc.gridx = 5; gbc.gridy = 0; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; - panel1.add(regL, gbc); - regS = new JTextField(); - regS.setText("000004"); + mainPanel.add(regL, gbc); + + // register S + final JLabel labelS = new JLabel(); + labelS.setText("S"); gbc = new GridBagConstraints(); - gbc.gridx = 1; + gbc.gridx = 0; gbc.gridy = 1; - gbc.anchor = GridBagConstraints.WEST; - gbc.fill = GridBagConstraints.HORIZONTAL; - panel1.add(regS, gbc); - regA = new JTextField(); - regA.setColumns(8); - regA.setText("000001"); + gbc.anchor = GridBagConstraints.EAST; + mainPanel.add(labelS, gbc); + + regS = new JTextField(); gbc = new GridBagConstraints(); gbc.gridx = 1; - gbc.gridy = 0; + gbc.gridy = 1; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; - panel1.add(regA, gbc); - final JLabel label3 = new JLabel(); - label3.setText("S"); - gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 1; - gbc.anchor = GridBagConstraints.EAST; - panel1.add(label3, gbc); - final JLabel label4 = new JLabel(); - label4.setText("A"); - gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 0; - gbc.anchor = GridBagConstraints.EAST; - panel1.add(label4, gbc); - final JLabel label5 = new JLabel(); - label5.setText("T"); + mainPanel.add(regS, gbc); + + // register T + final JLabel labelT = new JLabel(); + labelT.setText("T"); gbc = new GridBagConstraints(); gbc.gridx = 2; gbc.gridy = 1; gbc.anchor = GridBagConstraints.EAST; - panel1.add(label5, gbc); + mainPanel.add(labelT, gbc); + regT = new JTextField(); - regT.setText("000005"); gbc = new GridBagConstraints(); gbc.gridx = 3; gbc.gridy = 1; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; - panel1.add(regT, gbc); - final JLabel label6 = new JLabel(); - label6.setText("B"); + mainPanel.add(regT, gbc); + + // register B + final JLabel labelB = new JLabel(); + labelB.setText("B"); gbc = new GridBagConstraints(); gbc.gridx = 4; gbc.gridy = 1; gbc.anchor = GridBagConstraints.EAST; - panel1.add(label6, gbc); + mainPanel.add(labelB, gbc); + regB = new JTextField(); - regB.setText("000006"); gbc = new GridBagConstraints(); gbc.gridx = 5; gbc.gridy = 1; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; - panel1.add(regB, gbc); - final JLabel label7 = new JLabel(); - label7.setText("SW"); + mainPanel.add(regB, gbc); + + // timer + final JLabel labelTimer = new JLabel(); + labelTimer.setText("TIM"); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 2; gbc.anchor = GridBagConstraints.EAST; - panel1.add(label7, gbc); - regSW = new JTextField(); - regSW.setText("000007"); + mainPanel.add(labelTimer, gbc); + + timer = new JTextField(); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 2; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; - panel1.add(regSW, gbc); - final JLabel label8 = new JLabel(); - label8.setText("F"); + mainPanel.add(timer, gbc); + + // register F + final JLabel labelF = new JLabel(); + labelF.setText("F"); gbc = new GridBagConstraints(); gbc.gridx = 2; gbc.gridy = 2; gbc.anchor = GridBagConstraints.WEST; - panel1.add(label8, gbc); + mainPanel.add(labelF, gbc); + regF = new JTextField(); regF.setColumns(8); - regF.setText("000000000008"); gbc = new GridBagConstraints(); gbc.gridx = 3; gbc.gridy = 2; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; - panel1.add(regF, gbc); + mainPanel.add(regF, gbc); + regFF = new JTextField(); - regFF.setText("0.0"); gbc = new GridBagConstraints(); gbc.gridx = 4; gbc.gridy = 2; gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; - panel1.add(regFF, gbc); - final JLabel label9 = new JLabel(); - label9.setText("PC"); + mainPanel.add(regFF, gbc); + + // register SW + final JLabel labelSW = new JLabel(); + labelSW.setText("SW"); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 3; gbc.anchor = GridBagConstraints.EAST; - panel1.add(label9, gbc); - regPC = new JTextField(); - regPC.setText("000009"); + mainPanel.add(labelSW, gbc); + + regSW = new JTextField(); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 3; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; - panel1.add(regPC, gbc); + mainPanel.add(regSW, gbc); + + // SW explanation + lblInfoSW = new JLabel(); + Font lblInfoFont = this.$$$getFont$$$("Courier", -1, 12, lblInfoSW.getFont()); + if (lblInfoFont != null) lblInfoSW.setFont(lblInfoFont); + lblInfoSW.putClientProperty("html.disable", Boolean.FALSE); + lblInfoSW.setToolTipText("MASK shows which interrupts are enabled:
" + +"SVC, PROGRAM, TIMER or IO
" + +"CC: 1 (LT), 2 (GT) or 0 (EQ)
" + +"IDLE: Idle or Running
" + +"MODE: User or Supervisor"); + gbc = new GridBagConstraints(); + gbc.gridx = 3; + gbc.gridy = 3; + gbc.gridwidth = 3; + gbc.gridheight = 1; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(8, 0, 0, 0); + mainPanel.add(lblInfoSW, gbc); + + // register PC + final JLabel labelPC = new JLabel(); + labelPC.setText("PC"); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 4; + gbc.anchor = GridBagConstraints.EAST; + mainPanel.add(labelPC, gbc); + regPC = new JTextField(); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 4; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + mainPanel.add(regPC, gbc); + + // disassembled instruction txtInstruction = new JTextField(); - txtInstruction.setText("LDA 0"); txtInstruction.putClientProperty("html.disable", Boolean.FALSE); gbc = new GridBagConstraints(); gbc.gridx = 2; - gbc.gridy = 3; + gbc.gridy = 4; gbc.gridwidth = 4; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; - panel1.add(txtInstruction, gbc); + mainPanel.add(txtInstruction, gbc); + + // start/stop button btnStartStop = new JButton(); - btnStartStop.setText("Start"); btnStartStop.setMnemonic('S'); btnStartStop.setDisplayedMnemonicIndex(0); gbc = new GridBagConstraints(); gbc.gridx = 1; - gbc.gridy = 4; + gbc.gridy = 5; gbc.fill = GridBagConstraints.HORIZONTAL; - panel1.add(btnStartStop, gbc); + mainPanel.add(btnStartStop, gbc); + + // step button btnStep = new JButton(); btnStep.setText("Step"); btnStep.setMnemonic('T'); btnStep.setDisplayedMnemonicIndex(1); gbc = new GridBagConstraints(); gbc.gridx = 1; - gbc.gridy = 5; + gbc.gridy = 6; gbc.fill = GridBagConstraints.HORIZONTAL; - panel1.add(btnStep, gbc); + mainPanel.add(btnStep, gbc); + + // instruction explanation lblInfo = new JLabel(); - Font lblInfoFont = this.$$$getFont$$$("Courier", -1, 12, lblInfo.getFont()); + lblInfoFont = this.$$$getFont$$$("Courier", -1, 12, lblInfo.getFont()); if (lblInfoFont != null) lblInfo.setFont(lblInfoFont); lblInfo.setPreferredSize(new Dimension(280, 120)); lblInfo.setText(""); @@ -397,28 +455,33 @@ public void updateView() { lblInfo.putClientProperty("html.disable", Boolean.FALSE); gbc = new GridBagConstraints(); gbc.gridx = 3; - gbc.gridy = 4; + gbc.gridy = 5; gbc.gridwidth = 3; gbc.gridheight = 3; gbc.fill = GridBagConstraints.BOTH; gbc.insets = new Insets(8, 0, 0, 0); - panel1.add(lblInfo, gbc); + mainPanel.add(lblInfo, gbc); + + + // spacer final JPanel spacer1 = new JPanel(); gbc = new GridBagConstraints(); gbc.gridx = 1; - gbc.gridy = 6; + gbc.gridy = 7; gbc.fill = GridBagConstraints.VERTICAL; - panel1.add(spacer1, gbc); - label1.setLabelFor(regX); - label2.setLabelFor(regL); - label3.setLabelFor(regS); - label4.setLabelFor(regA); - label5.setLabelFor(regT); - label6.setLabelFor(regB); - label7.setLabelFor(regSW); - swLabel = label7; - label8.setLabelFor(regF); - label9.setLabelFor(regPC); + mainPanel.add(spacer1, gbc); + + // labels + labelX.setLabelFor(regX); + labelL.setLabelFor(regL); + labelS.setLabelFor(regS); + labelA.setLabelFor(regA); + labelT.setLabelFor(regT); + labelB.setLabelFor(regB); + labelSW.setLabelFor(regSW); + labelF.setLabelFor(regF); + labelPC.setLabelFor(regPC); + labelTimer.setLabelFor(timer); } /** diff --git a/src/sic/sim/vm/Registers.java b/src/sic/sim/vm/Registers.java index c673b24..81d17ef 100644 --- a/src/sic/sim/vm/Registers.java +++ b/src/sic/sim/vm/Registers.java @@ -206,6 +206,27 @@ public void setSW(int value) { SW.INT_CODE = (value>>16)&0xff; } + public String explainSW() { + int cc = 0; + if (SW.CC > 0) { + cc = 0x1; + } else if (SW.CC < 0) { + cc = 0x2; + } + // I hate java + String mask = String.format("%s%s%s%s", + intEnabled(Interrupt.IntClass.SVC) ? "S" : "-", + intEnabled(Interrupt.IntClass.PROGRAM) ? "P" : "-", + intEnabled(Interrupt.IntClass.TIMER) ? "T" : "-", + intEnabled(Interrupt.IntClass.IO) ? "I" : "-"); + + return String.format("ICODE: 0x%02x MASK: %s CC: %d
" + + "ID: %d IDLE: %s MODE: %s", + SW.INT_CODE, mask, cc, + SW.ID, SW.IDLE == 0 ? "R" : "I", SW.MODE == 1 ? "color: red;" : "", + SW.MODE == 1 ? "S" : "U"); + } + public void setCC(int compare) { SW.CC = compare; } From 832895ec7a40ab40255d8588de98dd98859f8ac2 Mon Sep 17 00:00:00 2001 From: Kljunas2 Date: Sat, 6 Dec 2025 13:38:05 +0100 Subject: [PATCH 11/12] Fix IllegalArgumentException I had assumed it is enough to set text for start/stop button in the update method. But since the displayedMnemonicIndex is set, the empty text throws an exception. --- src/sic/sim/views/CPUView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sic/sim/views/CPUView.java b/src/sic/sim/views/CPUView.java index 52acf1c..98ad7da 100644 --- a/src/sic/sim/views/CPUView.java +++ b/src/sic/sim/views/CPUView.java @@ -425,6 +425,7 @@ private void setupUI() { // start/stop button btnStartStop = new JButton(); + btnStartStop.setText("Start"); btnStartStop.setMnemonic('S'); btnStartStop.setDisplayedMnemonicIndex(0); gbc = new GridBagConstraints(); From e7aa364cdcdcbdf180be7155b0cb7bbcf68f22f5 Mon Sep 17 00:00:00 2001 From: Kljunas2 Date: Sat, 6 Dec 2025 16:24:02 +0100 Subject: [PATCH 12/12] Also reset timer with other registers --- src/sic/sim/vm/Registers.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sic/sim/vm/Registers.java b/src/sic/sim/vm/Registers.java index 81d17ef..195900d 100644 --- a/src/sic/sim/vm/Registers.java +++ b/src/sic/sim/vm/Registers.java @@ -315,6 +315,7 @@ public void reset() { A = X = L = 0; B = S = T = 0; F = 0; + timer = 0; SW = new StatusWord(); }