/src/BlinkenEngine.java
import javax.microedition.io.*;
import javax.microedition.rms.*;
import java.io.*;
import java.util.Vector;
public class BlinkenEngine {
public int width;
public int height;
public int[][] values;
private int[][] acc;
private int[][] store;
private boolean running;
// instruction codes
private static final int AND = 0;
private static final int OR = 1;
private static final int NOT = 2;
private static final int XOR = 3;
private static final int ADD = 4;
private static final int SUB = 5;
private static final int GTI = 6;
private static final int LTI = 7;
private static final int EQI = 8;
private static final int NEI = 9;
private static final int STO = 10;
private static final int RCL = 11;
private static final int SWP = 12;
private static final int ZERO = 13;
private static final int INC = 14;
private static final int DEC = 15;
// Memory reference codes
private static final int NORTH = 0;
private static final int EAST = 1;
private static final int SOUTH = 2;
private static final int WEST = 3;
private static final int NORTHEAST = 4;
private static final int SOUTHEAST = 5;
private static final int SOUTHWEST = 6;
private static final int NORTHWEST = 7;
private static final int SELF = 8;
private static final int STORE = 9;
private class BlinkenCode {
public int i;
public int m;
public BlinkenCode(int instruction, int memory) {
this.i = instruction;
this.m = memory;
}
}
private BlinkenCode[] code = null;
public BlinkenEngine(int w, int h) {
width = w;
height = h;
values = new int[w][h];
acc = new int[w][h];
store = new int[w][h];
running = false;
}
public BlinkenEngine() {
this(16,16);
}
private BlinkenCode parseLine(InputStream f) throws BlinkenEngineException {
String s = "";
String cmd = null, arg = null;
Vector words = new Vector();
int c, a;
while (true) {
try {
c = f.read();
} catch (IOException e) {
c = -1;
}
if (c == -1 || c == '\r' || c == '\n') {
words.addElement(s);
break;
} else if (c == ' ' || c == ' ') {
words.addElement(s);
s = "";
} else {
s += (char)c;
}
}
if (words.size() >= 1) {
cmd = ((String)words.elementAt(0)).toLowerCase();
if (cmd.length() == 0) {
if (c == -1) return null;
else return parseLine(f);
}
}
if (words.size() >= 2) {
arg = ((String)words.elementAt(1)).toLowerCase();
}
if (cmd.equals("and")) {
c = AND;
} else if (cmd.equals("or")) {
c = OR;
} else if (cmd.equals("not")) {
c = NOT;
} else if (cmd.equals("xor")) {
c = XOR;
} else if (cmd.equals("add")) {
c = ADD;
} else if (cmd.equals("sub")) {
c = SUB;
} else if (cmd.equals("gti")) {
c = GTI;
} else if (cmd.equals("lti")) {
c = LTI;
} else if (cmd.equals("eqi")) {
c = EQI;
} else if (cmd.equals("nei")) {
c = NEI;
} else if (cmd.equals("sto")) {
c = STO;
} else if (cmd.equals("rcl")) {
c = RCL;
} else if (cmd.equals("swp")) {
c = SWP;
} else if (cmd.equals("zero")) {
c = ZERO;
} else if (cmd.equals("inc")) {
c = INC;
} else if (cmd.equals("dec")) {
c = DEC;
} else {
throw new BlinkenEngineException("Unknown instruction " + cmd);
}
switch (c) {
case NOT:
case STO:
case RCL:
case SWP:
case ZERO:
case INC:
case DEC:
a = 0;
break;
case LTI:
case GTI:
case EQI:
case NEI:
a = Integer.parseInt(arg);
break;
default:
arg = arg.toLowerCase();
if (arg.equals("n")) {
a = NORTH;
} else if (arg.equals("e")) {
a = EAST;
} else if (arg.equals("s")) {
a = SOUTH;
} else if (arg.equals("w")) {
a = WEST;
} else if (arg.equals("ne")) {
a = NORTHEAST;
} else if (arg.equals("se")) {
a = SOUTHEAST;
} else if (arg.equals("nw")) {
a = NORTHWEST;
} else if (arg.equals("sw")) {
a = SOUTHWEST;
} else if (arg.equals("self")) {
a = SELF;
} else if (arg.equals("o")) {
a = STORE;
} else {
throw new BlinkenEngineException("Invalid memory reference " + arg);
}
}
return new BlinkenCode(c, a);
}
public void compile(InputStream f) throws BlinkenEngineException {
BlinkenCode c;
Vector v = new Vector();
while (true) {
c = parseLine(f);
if (c == null) break;
v.addElement(c);
}
code = new BlinkenCode[v.size()];
v.copyInto(code);
}
public void run() {
running = true;
}
public void stop() {
running = false;
}
public void clear() {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
values[i][j] = store[i][j] = 0;
}
}
}
public boolean running() {
return running;
}
public boolean loaded() {
return code != null;
}
public void step() throws BlinkenEngineException {
int PC;
if (!running) return;
// First, copy values into acc
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
acc[i][j] = values[i][j];
}
}
for (PC = 0; PC < code.length; PC++) {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
int v = 0;
switch (code[PC].i) {
case NOT:
case GTI:
case LTI:
case EQI:
case NEI:
case STO:
case RCL:
case SWP:
case ZERO:
case INC:
case DEC:
break;
default:
if (code[PC].m == STORE) {
v = store[i][j];
} else {
int ii = i;
int jj = j;
switch (code[PC].m) {
case NORTH:
jj--;
break;
case EAST:
ii++;
break;
case SOUTH:
jj++;
break;
case WEST:
ii--;
break;
case NORTHEAST:
jj--;
ii++;
break;
case SOUTHEAST:
jj++;
ii++;
break;
case SOUTHWEST:
jj++;
ii--;
break;
case NORTHWEST:
jj--;
ii--;
break;
case SELF:
}
if (ii < 0)
ii += width;
else
ii %= width;
if (jj < 0)
jj += height;
else
jj %= height;
v = values[ii][jj];
}
}
switch (code[PC].i) {
case AND:
acc[i][j] = acc[i][j] & v;
break;
case OR:
acc[i][j] = acc[i][j] | v;
break;
case NOT:
acc[i][j] = (~acc[i][j]) & 1;
break;
case XOR:
acc[i][j] = acc[i][j] ^ v;
break;
case ADD:
acc[i][j] += v;
break;
case SUB:
acc[i][j] -= v;
break;
case GTI:
acc[i][j] = (acc[i][j] > code[PC].m ? 1 : 0);
break;
case LTI:
acc[i][j] = (acc[i][j] < code[PC].m ? 1 : 0);
break;
case EQI:
acc[i][j] = (acc[i][j] == code[PC].m ? 1 : 0);
break;
case NEI:
acc[i][j] = (acc[i][j] != code[PC].m ? 1 : 0);
break;
case STO:
store[i][j] = acc[i][j];
break;
case RCL:
acc[i][j] = store[i][j];
break;
case SWP:
int tmp = acc[i][j];
acc[i][j] = store[i][j];
store[i][j] = tmp;
break;
case ZERO:
acc[i][j] = 0;
break;
case INC:
acc[i][j]++;
break;
case DEC:
acc[i][j]--;
break;
default:
throw new BlinkenEngineException("Bad instruction code: " + code[PC].i);
}
}
}
}
// Swap values and acc
int[][] tmp = values;
values = acc;
acc = tmp;
}
// Save the engine state in a RecordStore
public void saveState() {
RecordStore rs = null;
byte[] tmp;
try {
RecordStore.deleteRecordStore("state");
} catch (RecordStoreException e) {
System.out.println("Could not delete RecordStore. No biggie. " + e.toString());
}
try {
rs = RecordStore.openRecordStore("state", true);
} catch (RecordStoreException e) {
System.out.println("Could not open RecordStore: " + e.toString());
return;
}
try {
ByteArrayOutputStream b = new ByteArrayOutputStream();
DataOutputStream f = new DataOutputStream(b);
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
f.writeInt(values[i][j]);
f.writeInt(store[i][j]);
}
}
tmp = b.toByteArray();
f.close();
b.close();
} catch (IOException e) {
System.out.println("IO Error storing data: " + e.toString());
tmp = new byte[0];
}
try {
rs.addRecord(tmp, 0, tmp.length);
} catch (RecordStoreException e) {
System.out.println("Error writing data: " + e.toString());
}
if (code != null) {
tmp = new byte[code.length * 2];
for (int i = 0; i < code.length; i++) {
tmp[i*2] = (byte) code[i].i;
tmp[i*2+1] = (byte) code[i].m;
}
} else {
tmp = new byte[0];
}
try {
rs.addRecord(tmp, 0, tmp.length);
} catch (RecordStoreException e) {
System.out.println("Failure adding code to RecordStore: " + e.toString());
}
try {
rs.closeRecordStore();
} catch (RecordStoreException e) {
}
}
// Load state from RecordStore
public void loadState() {
RecordStore rs;
byte[] tmp;
try {
rs = RecordStore.openRecordStore("state", false);
} catch (RecordStoreException e) {
System.out.println("Could not open RecordStore: " + e.toString());
return;
}
try {
tmp = rs.getRecord(1);
ByteArrayInputStream b = new ByteArrayInputStream(tmp);
DataInputStream f = new DataInputStream(b);
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
values[i][j] = f.readInt();
store[i][j] = f.readInt();
}
}
f.close();
b.close();
} catch (RecordStoreException e) {
System.out.println("Error retrieving data record: " + e.toString());
} catch (IOException e) {
System.out.println("Error reading data: " + e.toString());
}
try {
tmp = rs.getRecord(2);
if (tmp != null) {
code = new BlinkenCode[tmp.length / 2];
for (int i = 0; i < code.length; i++) {
code[i] = new BlinkenCode((int)tmp[i*2], (int)tmp[i*2+1]);
}
}
} catch (RecordStoreException e) {
System.out.println("Could not read Code: " + e.toString());
}
try {
rs.closeRecordStore();
} catch (RecordStoreException e) {
System.out.println("Failure closing RecordStore: " + e.toString());
}
}
}