// 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();

	}

}

}

Proxy Information
Original URL
gemini://thingvellir.net/archive/dist/Jauc.java
Status Code
Success (20)
Meta
application/octet-stream
Capsule Response Time
1448.204489 milliseconds
Gemini-to-HTML Time
3.488387 milliseconds

This content has been proxied by September (ba2dc).