// JAUC interpreter - Ada sarahsooup@protonmail.com 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<Byte> dataStack = new Stack<Byte>();
Stack<Short> retAddrStack = new Stack<Short>();
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();
}
}
}
application/octet-stream
This content has been proxied by September (ba2dc).