commit:d6770f0af70a437dd0eef8ee2602e49e537ffd6f
author:Chip Black
committer:Chip Black
date:Mon Sep 22 01:57:11 2008 -0500
parents:
Initial commit
diff --git a/BlinkenCPU.pro b/BlinkenCPU.pro
line changes: +5/-0
index 0000000..f168614
--- /dev/null
+++ b/BlinkenCPU.pro
@@ -0,0 +1,5 @@
+-libraryjars C:\WTK2.5.2\lib\jsr75.jar;C:\WTK2.5.2\lib\jsr082.jar;C:\WTK2.5.2\lib\midpapi20.jar;C:\WTK2.5.2\lib\cldcapi11.jar
+-injars C:\Documents and Settings\chip.NEO\j2mewtk\2.5.2\apps\BlinkenCPU\bin\BlinkenCPU.jar
+-outjar C:\DOCUME~1\chip.NEO\LOCALS~1\Temp\BlinkenCPU.jar
+-keep public class * extends javax.microedition.midlet.MIDlet 
+-dontusemixedcaseclassnames

diff --git a/project.properties b/project.properties
line changes: +24/-0
index 0000000..024502a
--- /dev/null
+++ b/project.properties
@@ -0,0 +1,24 @@
+J2ME-WS: false
+J2ME-XMLRPC: false
+JSR082: true
+JSR179: false
+JSR180: false
+JSR184: false
+JSR211: false
+JSR226: false
+JSR229: false
+JSR234: false
+JSR238: false
+JSR239: false
+JSR75: true
+MMAPI: false
+SATSA-APDU: false
+SATSA-CRYPTO: false
+SATSA-JCRMI: false
+SATSA-PKI: false
+WMA0: true
+WMA1.1: false
+WMA2.0: false
+configuration: CLDC1.1
+platform: CUSTOM
+profile: MIDP2.0

diff --git a/res/BlinkenCPU.png b/res/BlinkenCPU.png
line changes: +0/-0
index 0000000..af267c7
--- /dev/null
+++ b/res/BlinkenCPU.png

diff --git a/src/BlinkenCPU.java b/src/BlinkenCPU.java
line changes: +125/-0
index 0000000..0503127
--- /dev/null
+++ b/src/BlinkenCPU.java
@@ -0,0 +1,125 @@
+import java.io.*;
+import javax.microedition.lcdui.*;
+import javax.microedition.io.*;
+import javax.microedition.io.file.*;
+import javax.microedition.midlet.MIDlet;
+
+public class BlinkenCPU extends MIDlet implements CommandListener, Runnable {
+	private BlinkenCanvas canvas;
+	private Display display;
+	private BlinkenEngine engine;
+	private Thread thread;
+
+	private static final Command start = new Command("Start", Command.SCREEN, 1);
+	private static final Command stop = new Command("Stop", Command.SCREEN, 1);
+	private static final Command load = new Command("Load", Command.SCREEN, 2);
+	private static final Command clear = new Command("Clear", Command.SCREEN, 3);
+	private static final Command exit = new Command("Exit", Command.EXIT, 2);
+	private static final Command darn = new Command("Darn", Command.EXIT, 1);
+
+
+	public void startApp() {
+		display = Display.getDisplay(this);
+		engine = new BlinkenEngine();
+		engine.loadState();
+		canvas = new BlinkenCanvas(engine);
+
+		canvas.addCommand(load);
+		canvas.addCommand(clear);
+		canvas.addCommand(exit);
+		if (engine.loaded())
+			canvas.addCommand(start);
+		canvas.setCommandListener(this);
+		canvas.draw();
+
+		display.setCurrent(canvas);
+
+		thread = new Thread(this);
+		thread.start();
+	}
+
+	public void pauseApp() {
+		display.flashBacklight(0);
+	}
+
+	public void destroyApp(boolean unconditional) {
+		display.flashBacklight(0);
+		engine.saveState();
+	}
+
+	public void run() {
+		while (true) {
+			if (engine.running()) {
+				try {
+					display.flashBacklight(0x7FFFFFFF);
+					engine.step();
+				} catch (BlinkenEngineException e) {
+					engine.stop();
+					shitSauce("Execution error", e.error);
+				}
+			}
+			canvas.update();
+			canvas.draw();
+			try {
+				Thread.sleep(100);
+			} catch(InterruptedException e) {
+			}
+		}
+	}
+
+	public void commandAction(Command c, Displayable d) {
+		if (c == start) {
+			if (engine.loaded()) {
+				engine.run();
+				canvas.removeCommand(start);
+				canvas.addCommand(stop);
+			} else {
+				display.setCurrent(new Alert("Code not loaded"));
+			}
+		} else if (c == stop) {
+			engine.stop();
+			canvas.removeCommand(stop);
+			canvas.addCommand(start);
+			display.flashBacklight(0);
+		} else if (c == load) {
+			BlinkenFileSelect bfs = new BlinkenFileSelect(this, engine, display);
+			bfs.start();
+		} else if (c == clear) {
+			engine.clear();
+		} else if (c == exit) {
+			destroyApp(true);
+			notifyDestroyed();
+		} else if (c == darn) {
+			display.setCurrent(canvas);
+		}
+	}
+
+	public void loadFile(String file) {
+		try {
+			FileConnection fc = (FileConnection) Connector.open(file, Connector.READ);
+			if (!fc.exists()) {
+				display.setCurrent(new Alert("File does not exist"));
+				return;
+			}
+			InputStream f = fc.openInputStream();
+			try {
+				engine.compile(f);
+				canvas.addCommand(start);
+			} catch (BlinkenEngineException e) {
+				shitSauce("Failed to compile " + file, e.error);
+			}
+			f.close();
+			fc.close();
+			display.setCurrent(canvas);
+		} catch (IOException e) {
+			display.setCurrent(new Alert("File Open Failed\n" + e));
+		}
+	}
+
+	public void shitSauce(String title, String error) {
+		TextBox b = new TextBox(title, error, 1024, TextField.ANY | TextField.UNEDITABLE);
+		b.addCommand(darn);
+		b.setCommandListener(this);
+		display.setCurrent(b);
+	}
+}

diff --git a/src/BlinkenCanvas.java b/src/BlinkenCanvas.java
line changes: +52/-0
index 0000000..702a6e6
--- /dev/null
+++ b/src/BlinkenCanvas.java
@@ -0,0 +1,52 @@
+import javax.microedition.lcdui.game.GameCanvas;
+import javax.microedition.lcdui.Graphics;
+
+public class BlinkenCanvas extends GameCanvas {
+	private BlinkenEngine engine;
+	private int x, y;
+
+	public BlinkenCanvas(BlinkenEngine e) {
+		super(false);
+		engine = e;
+		x = y = 0;
+	}
+
+	public void draw() {
+		Graphics g = getGraphics();
+		g.setColor(0);
+		g.fillRect(0, 0, getWidth(), getHeight());
+
+		for (int i = 0; i < engine.width; i++) {
+			for (int j = 0; j < engine.height; j++) {
+				if (engine.values[i][j] != 0) {
+					g.setColor(0xFF0000);
+				} else {
+					g.setColor(0x400000);
+				}
+				g.fillArc(i*8, j*8, 8, 8, 0, 360);
+			}
+		}
+		g.setColor(0x00FF00);
+		g.drawRect(x*8, y*8, 8, 8);
+		flushGraphics();
+	}
+
+	public void update() {
+		int keys = getKeyStates();
+		if ((keys & LEFT_PRESSED) != 0) {
+			x--;
+			if (x < 0) x = engine.width - 1;
+		} else if ((keys & RIGHT_PRESSED) != 0) {
+			x++;
+			x = x % engine.width;
+		} else if ((keys & UP_PRESSED) != 0) {
+			y--;
+			if (y < 0) y = engine.height - 1;
+		} else if ((keys & DOWN_PRESSED) != 0) {
+			y++;
+			y = y % engine.height;
+		} else if ((keys & FIRE_PRESSED) != 0) {
+			engine.values[x][y] = engine.values[x][y] != 0 ? 0 : 1;
+		}
+	}
+}

diff --git a/src/BlinkenEngine.java b/src/BlinkenEngine.java
line changes: +476/-0
index 0000000..f3716ac
--- /dev/null
+++ b/src/BlinkenEngine.java
@@ -0,0 +1,476 @@
+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());
+		}
+	}
+}

diff --git a/src/BlinkenEngineException.java b/src/BlinkenEngineException.java
line changes: +9/-0
index 0000000..b536d67
--- /dev/null
+++ b/src/BlinkenEngineException.java
@@ -0,0 +1,9 @@
+// Java: Raising the bar for pedantry
+
+public class BlinkenEngineException extends Exception {
+	public String error;
+
+	public BlinkenEngineException(String e) {
+		error = e;
+	}
+}

diff --git a/src/BlinkenFileSelect.java b/src/BlinkenFileSelect.java
line changes: +93/-0
index 0000000..b4060bc
--- /dev/null
+++ b/src/BlinkenFileSelect.java
@@ -0,0 +1,93 @@
+import javax.microedition.lcdui.*;
+import javax.microedition.io.*;
+import javax.microedition.io.file.*;
+import java.io.IOException;
+import java.util.Vector;
+import java.util.Enumeration;
+
+class LoadThread extends Thread {
+	private BlinkenCPU blink;
+	private String file;
+
+	public LoadThread(BlinkenCPU b, String fn) {
+		super();
+		blink = b;
+		file = fn;
+	}
+
+	public void run() {
+		blink.loadFile(file);
+	}
+}
+
+
+public class BlinkenFileSelect extends Thread implements CommandListener {
+	private List fileSelect;
+	private BlinkenCPU blink;
+	private Display display;
+	private BlinkenEngine engine;
+	private String location;
+
+	private static final Command load = new Command("Load", Command.OK, 1);
+	private static final String[] locations = { "file:///other/", "file:///c:/other/", "file:///" };
+
+	public BlinkenFileSelect(BlinkenCPU b, BlinkenEngine e, Display d) {
+		super();
+		blink = b;
+		engine = e;
+		display = d;
+	}
+
+	public void run() {
+		FileConnection fc = null;
+		Enumeration roots = FileSystemRegistry.listRoots();
+
+		while (roots.hasMoreElements()) {
+			location = "file:///" + roots.nextElement() + "other/";
+			try {
+				fc = (FileConnection) Connector.open(location, Connector.READ);
+			} catch (IOException e) {
+				blink.shitSauce("IO Error", "Could not open " + location);
+				continue;
+			}
+			if (fc.isDirectory()) {
+				break;
+			} else {
+				fc = null;
+			}
+		}
+		if (fc == null) {
+			blink.shitSauce("Directory not found", "No suitable directory found for data files");
+			return;
+		}
+
+		Vector v = new Vector();
+		Enumeration names;
+		try {
+			names = fc.list("*.bc", false);
+		} catch (IOException e) {
+			blink.shitSauce("IO Error", "Could not list directory");
+			return;
+		}
+		while (names.hasMoreElements())
+			v.addElement(names.nextElement());
+		if (v.size() == 0) {
+			display.setCurrent(new Alert("No files found"));
+			return;
+		}
+		String[] files = new String[v.size()];
+		v.copyInto(files);
+		
+		fileSelect = new List("Select Code", List.IMPLICIT, files, null);
+		fileSelect.setSelectCommand(load);
+		fileSelect.setCommandListener(this);
+		display.setCurrent(fileSelect);
+	}
+
+	public void commandAction(Command c, Displayable d) {
+		if (c == load) {
+			LoadThread lt = new LoadThread(blink, location + fileSelect.getString(fileSelect.getSelectedIndex()));
+			lt.start();
+		}
+	}
+}