// JAUC interpreter - Ada 2022 CC0-1.0 // https://thingvellir.net/jauc.html package net.thingvellir; import java.util.Stack; import java.util.Arrays; public class Jauc { static final int INTR_BADCODE = 0; static final int INTR_ADDR_STACK = 1; static final int INTR_DATA_STACK = 2; static final int INTR_BADMEM = 7; public boolean halted = false; short[] code; byte[] data; Stack dataStack = new Stack(); Stack retAddrStack = new Stack(); int rip = 8; int rirp = 8; long cycles = 0; int[] reg = new int[] {0, 0, 0, 0, 0, 0, 0, 0}; boolean intHandlerMode = false; public Jauc(short[] code, int memSize) { this.code = code; this.data = new byte[memSize]; } void interrupt(int i) { if (this.intHandlerMode) { System.out.format("Double-fault %d at %X\n", i, rip); this.halted = true; return; } System.out.format("Interrupt %d at %X\n", i, rip); this.rirp = this.rip + 1; this.intHandlerMode = true; if (code[i] == 0) { System.out.println("Halted"); this.halted = true; } else { this.rip = Short.toUnsignedInt(code[i]); } } public void tick() { if (this.halted) { return; } cycles++; if (this.rip < 0 || this.rip >= this.code.length) { this.interrupt(INTR_BADMEM); this.interrupt(INTR_BADMEM); return; } Op op = this.decode(Short.toUnsignedInt(this.code[this.rip])); this.rip = this.exec(op); } Op decode(int code) { int op = code >> 11; int arg = code & 0xFF; int regT = (code >> 8) & 7; int regA = arg >> 4; int regB = arg & 0xF; return switch (op) { case 0, 18, 21, 26 -> new OpJ(op, arg); case 1, 2, 3, 6, 14, 15, 16, 17, 19, 20 -> new OpI(op, regT, arg); case 4, 5, 7, 8, 9, 10, 11, 12, 13, 22, 23, 24, 25 -> new OpA(op, regT, regA, regB); default -> new OpInvalid(op, arg); }; } int exec(Op op) { if (op instanceof OpInvalid) { this.interrupt(INTR_BADCODE); return this.rip + 1; } // the real juicy difficult bit switch (op.opcode) { // HLT case 0: { this.halted = true; break; } // OIO case 1: { OpI oop = (OpI)op; System.out.format("IO %d Out: %02X\n", oop.arg, getRegister(oop.regT) & 0xFF); break; } // IIO case 2: { OpI oop = (OpI)op; setRegister(oop.regT, oop.arg); break; } // LDI case 3: { OpI oop = (OpI)op; setRegister(oop.regT, oop.arg); break; } // ADD case 4: { OpA oop = (OpA)op; int val = getRegister(oop.regA) + getRegister(oop.regB); setRegister(oop.regT, val); break; } // SUB case 5: { OpA oop = (OpA)op; int val = getRegister(oop.regA) - getRegister(oop.regB); setRegister(oop.regT, val); break; } // ADI case 6: { OpI oop = (OpI)op; int val = getRegister(oop.regT) + oop.arg; setRegister(oop.regT, val); break; } // AND case 7: { OpA oop = (OpA)op; setRegister(oop.regT, (getRegister(oop.regA) & getRegister(oop.regB)) & 0xFF); break; } // BOR case 8: { OpA oop = (OpA)op; setRegister(oop.regT, (getRegister(oop.regA) | getRegister(oop.regB)) & 0xFF); break; } // XOR case 9: { OpA oop = (OpA)op; setRegister(oop.regT, (getRegister(oop.regA) ^ getRegister(oop.regB)) & 0xFF); break; } // SHL case 10: { OpA oop = (OpA)op; int val = getRegister(oop.regA) << getRegister(oop.regB); setRegister(oop.regT, val); break; } // SHR case 11: { OpA oop = (OpA)op; int val = getRegister(oop.regA) >> getRegister(oop.regB); setRegister(oop.regT, val); break; } // SSR case 12: { OpA oop = (OpA)op; setRegister(oop.regT, signShift8(getRegister(oop.regA), getRegister(oop.regB))); break; } // BRO case 13: { OpA oop = (OpA)op; if (oop.regT == 7) { setRegister(oop.regT, rotateRight16(getRegister(oop.regA), getRegister(oop.regB))); } else { setRegister(oop.regT, rotateRight8(getRegister(oop.regA), getRegister(oop.regB))); } break; } // SPU case 14: { OpI oop = (OpI)op; pushData(getRegister(oop.regT)); break; } // SPO case 15: { OpI oop = (OpI)op; setRegister(oop.regT, popData()); break; } // LSJ case 16: { OpI oop = (OpI)op; byte offset = (byte)oop.arg; pushAddr((short)(this.rip + 1)); return this.rip + offset; } // USJ case 17: { OpI oop = (OpI)op; byte offset = (byte)oop.arg; return this.rip + offset; } // LLJ case 18: { OpJ oop = (OpJ)op; pushAddr((short)(this.rip + 1)); return getRegister(7); } // CSJ case 19: { OpI oop = (OpI)op; if (getRegister(oop.regT) != 0) { byte offset = (byte)oop.arg; return this.rip + offset; } break; } // CLJ case 20: { OpI oop = (OpI)op; pushAddr((short)(this.rip + 1)); if (getRegister(oop.regT) != 0) { return getRegister(7); } break; } // RET case 21: { return popAddr(); } // CLT case 22: { OpA oop = (OpA)op; if (getRegister(oop.regA) < getRegister(oop.regB)) { setRegister(oop.regT, 1); } else { setRegister(oop.regT, 0); } break; } // CEQ case 23: { OpA oop = (OpA)op; if (getRegister(oop.regA) == getRegister(oop.regB)) { setRegister(oop.regT, 1); } else { setRegister(oop.regT, 0); } break; } // SVR case 24: { OpA oop = (OpA)op; int addr = getRegister(oop.regB) << 8; addr += getRegister(oop.regA); setMem(addr, getRegister(oop.regT)); break; } // LDR case 25: { OpA oop = (OpA)op; int addr = getRegister(oop.regB) << 8; addr += getRegister(oop.regA); setRegister(oop.regT, getMem(addr)); break; } // IRE case 26: { return this.rirp; } // ??? default: { interrupt(INTR_BADCODE); break; } } return this.rip + 1; } int signShift8(int i, int bits) { // handle single bytes if (i > 0x7F && i < 0x100) { byte buf = (byte)i; return Byte.toUnsignedInt((byte)(buf >> i)); // special case for register 8 } else if (i > 0x7FFF) { short buf = (short)i; return Short.toUnsignedInt((short)(buf >> i)); } else { return i >> bits; } } int rotateRight8(int i, int bits) { bits &= 7; i &= 0xFF; return (i >> bits) | (i << (8 - bits)); } int rotateRight16(int i, int bits) { bits &= 15; i &= 0xFFFF; return (i >> bits) | (i << (16 - bits)); } int getRegister(int i) { return this.reg[i] & 0xFF; } void setRegister(int i, int val) { if (i == 0) { return; } else if (i == 7) { this.reg[i] = val & 0xFFFF; } else { this.reg[i] = val & 0xFF; } } int getMem(int i) { if (i < 0 || i >= this.data.length) { this.interrupt(INTR_BADMEM); return 0; } else { return this.data[i]; } } void setMem(int i, int val) { if (i < 0 || i >= this.data.length) { this.interrupt(INTR_BADMEM); } else if (val > 0xFF) { this.interrupt(INTR_BADCODE); } else { this.data[i] = (byte)(val & 0xFF); } } void pushAddr(int a) { if (this.retAddrStack.size() > 255) { interrupt(INTR_ADDR_STACK); } else { this.retAddrStack.push((short)(a & 0xFFFF)); } } int popAddr() { if (this.retAddrStack.empty()) { interrupt(INTR_ADDR_STACK); return 0; } else { return Short.toUnsignedInt(this.retAddrStack.pop()); } } void pushData(int a) { if (this.dataStack.size() > 255) { interrupt(INTR_DATA_STACK); } else { this.dataStack.push((byte)(a & 0xFF)); } } int popData() { if (this.dataStack.empty()) { interrupt(INTR_DATA_STACK); return 0; } else { return Byte.toUnsignedInt(this.dataStack.pop()); } } public long getCycles() { return this.cycles; } public String getRegisterDump() { return String.format("%02X %02X %02X %02X %02X %02X %02X %04X %04X %04X", getRegister(0), getRegister(1), getRegister(2), getRegister(3), getRegister(4), getRegister(5), getRegister(6), getRegister(7), this.rip, this.rirp); } abstract class Op { public int opcode; } class OpI extends Op { public int regT, arg; public OpI(int op, int t, int arg) { this.opcode = op; this.regT = t; this.arg = arg; } } class OpJ extends Op { public int arg; public OpJ(int op, int arg) { this.opcode = op; this.arg = arg; } } class OpA extends Op { public int regT, regA, regB; public OpA(int op, int t, int a, int b) { this.opcode = op; this.regT = t; this.regA = a; this.regB = b; } } class OpInvalid extends OpJ { public OpInvalid(int op, int arg) { super(op, arg); } } public static void main(String[] args) { short[] instructions = new short[] { // interrupt vectors 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1148, // LDI r1, 'H' 0x0901, // OIO r1, $1 0x1165, // LDI r1, 'e' 0x0901, // OIO r1, $1 0x116c, // LDI r1, 'l' 0x0901, // OIO r1, $1 0x116c, // LDI r1, 'l' 0x0901, // OIO r1, $1 0x116f, // LDI r1, 'o' 0x0901, // OIO r1, $1 0x0000, // HLT }; Jauc jauc = new Jauc(instructions, 8); while (!jauc.halted) { System.out.println(jauc.getRegisterDump()); jauc.tick(); } } }