commit a3bca693a34434e54a24b19172e010536bb9b3ed
Author: Brian Swetland <swetland@frotz.net>
Date: Fri, 23 Aug 2013 21:42:36 -0700
zmachine for danger hiptop, I wrote back in 2002 or thereabouts
Diffstat:
12 files changed, 2963 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,25 @@
+Copyright 2002-2003 Brian J. Swetland
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions, and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions, and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/Makefile b/Makefile
@@ -0,0 +1,4 @@
+APPNAME := zmachine
+INTERFACES := Resources.java Events.java
+include hiptop.mk
+
diff --git a/ZDanger.java b/ZDanger.java
@@ -0,0 +1,392 @@
+// Z Machine V3/V4/V5 Runtime
+//
+// Copyright 2002-2003, Brian Swetland <swetland@frotz.net>
+// Available under a BSD-Style License. Share and Enjoy.
+//
+// glue between the runtime and the Danger UI
+
+package net.frotz.zruntime;
+
+import danger.app.DataStore;
+import danger.app.Event;
+import danger.ui.Menu;
+import java.util.Random;
+
+
+public class ZDanger extends ZScreen implements Runnable
+{
+ ZDanger(ZWindow win, int width, int height) {
+ this.win = win;
+ this.w = width;
+ this.h = height;
+ screen = new byte[width*height];
+ buffer = new byte[width];
+ r = new Random();
+ Clear();
+ buffered = true;
+ win0h = height;
+ win1h = 0;
+ win1cx = 0;
+ win1cy = 0;
+ LF = new char[1];
+ LF[0] = '\n';
+ }
+
+ public int GetWidth() { return w; }
+ public int GetHeight() { return h; }
+
+ void Clear() {
+ for(int i = 0; i < w*h; i++) screen[i] = (byte) ' ';
+ cx = 0;
+ cy = h - 1;
+ }
+
+ public void SetStatus(char line[], int len) {
+ int i;
+ if(win1h > 0){
+ int max = w-1;
+ if(len >= max) len = max;
+ for(i = 0; i < len; i++){
+ screen[i+1] = (byte) line[i];
+ }
+ screen[0] = (byte) ' ';
+ while(i < max) screen[1+i++] = (byte)' ';
+ win.invalidate();
+ }
+ }
+
+ public void run() {
+ for(;;) {
+ byte[] data = win.GetGameFile();
+ if(data == null) {
+ Clear();
+ Print("no game file installed.");
+ break;
+ }
+ try {
+ zm = new ZMachine(this, data);
+ Clear();
+ zm.run();
+ } catch (Throwable t) {
+ System.err.println("ZMachine: exception "+t);
+ t.printStackTrace();
+ }
+ }
+ }
+
+ public void SetWindow(int num) {
+ selected = num;
+ if(selected == 1){
+ win1cx = 0;
+ win1cy = 0;
+ } else {
+ win.invalidate();
+ }
+ }
+ public void SplitWindow(int height) {
+ win1h = height;
+ win0h = h - height;
+ }
+ public void EraseWindow(int num) {
+ if(num == -1) {
+ Clear();
+ win1h = 0;
+ win0h = h;
+ cx = 0;
+ cy = 0;
+ win1cx = 0;
+ win1cy = 0;
+ selected = 0;
+ count = 0;
+ }
+ }
+ public void MoveCursor(int x, int y) {
+ /* upper left is 1,1 */
+ x--;
+ y--;
+
+ /* gracefully deal with bogus locations */
+ if(x < 0) x = 0;
+ if(y < 0) y = 0;
+
+ if(selected == 0){
+ cx = x;
+ cy = win1h + y;
+ if(cx >= w) cx = w-1;
+ if(cy >= h) cy = h-1;
+ } else {
+ win1cx = x;
+ win1cy = y;
+ if(win1cx >= w) win1cx = w-1;
+ if(win1cy >= win1h) win1cy = win1h - 1;
+ }
+ }
+
+ public void Print(char data[], int len) {
+ int ptr = 0;
+
+ if(selected == 1){
+ if(win1h == 0) return;
+
+ while(ptr < len){
+ char ch = data[ptr++];
+
+ if(ch == 13 || ch == 10){
+ win1cy++;
+ win1cx=0;
+ } else {
+ screen[win1cx + win1cy * w] = (byte) ch;
+ win1cx++;
+ if(win1cx == w){
+ if(win1cy + 1 < win1h){
+ win1cy++;
+ win1cx = 0;
+ } else {
+ win1cx--;
+ }
+ }
+ }
+ if(win1cy >= win1h) win1cy = win1h - 1;
+ }
+ return;
+ }
+
+ if(!input_active) win.Transcript(data, 0, len);
+
+ while(ptr < len){
+ char c = data[ptr];
+ if((c == 13) || (c == 10)) {
+ DoNewLine();
+ } else {
+ if(buffered) {
+ buffer[buflen++] = (byte) c;
+ if(buflen == w){
+ int i;
+ for(i = buflen - 1; i > 0; i--){
+ if(buffer[i] == ' ') {
+ int t = buflen;
+ buflen = i ;
+ DoNewLine();
+ i++;
+ buflen = t - i;
+ System.arraycopy(buffer, i, buffer, 0, buflen);
+ i = -1;
+ break;
+ }
+ }
+ if(i != -1) DoNewLine();
+ }
+ } else {
+ screen[cx + cy * w] = (byte) c;
+ cx ++;
+ if(cx == w) DoNewLine();
+ }
+ }
+ ptr++;
+ }
+ win.invalidate();
+ }
+
+ void Flush() {
+ if(selected == 0) {
+ if(buffered) {
+ System.arraycopy(buffer, 0, screen, cx + cy * w, buflen);
+ cx += buflen;
+ buflen = 0;
+ }
+ }
+ }
+
+ public void NewLine() {
+ if(!input_active) win.Transcript(LF,0,1);
+ DoNewLine();
+ }
+
+ public void DoNewLine() {
+ Flush();
+
+ cx = 0;
+ cy++;
+ if(cy == h){
+ cy--;
+ scrollup();
+ }
+ count++;
+ if(count == (win0h - 1)){
+ int p = w * (h - 1);
+ screen[p+0] = (byte) '[';
+ screen[p+1] = (byte) 'M';
+ screen[p+2] = (byte) 'O';
+ screen[p+3] = (byte) 'R';
+ screen[p+4] = (byte) 'E';
+ screen[p+5] = (byte) ']';
+ win.invalidate();
+ synchronized (zm) {
+ paused = true;
+ try {
+ zm.wait();
+ } catch (Throwable t) {
+ }
+ }
+ screen[p+0] = (byte) ' ';
+ screen[p+1] = (byte) ' ';
+ screen[p+2] = (byte) ' ';
+ screen[p+3] = (byte) ' ';
+ screen[p+4] = (byte) ' ';
+ screen[p+5] = (byte) ' ';
+ count = 0;
+ }
+ }
+
+ void scrollup() {
+ int i,p;
+ System.arraycopy(screen, (win1h+1) * w, screen, win1h*w, w * (win0h - 1));
+
+ p = w * (h - 1);
+ for(i = 0; i < w; i++){
+ screen[p++] = (byte) ' ';
+ }
+
+ }
+
+ public void Backspace() {
+ cx--;
+ if(cx < 0){
+ cy--;
+ cx = w - 1;
+ }
+ screen[cx + cy * w] = (byte) ' ';
+ win.invalidate();
+ }
+
+ public void KeyPress(char key) {
+ if(paused) {
+ synchronized(zm) {
+ paused = false;
+ zm.notify();
+ }
+ return;
+ }
+ if(input_active) {
+ if(readchar == -1){
+ readchar = (int) key;
+ synchronized(zm){
+ input_active = false;
+ zm.notify();
+ }
+ return;
+ }
+ switch(key){
+ case 10:
+ case 13:
+ DoNewLine();
+ synchronized(zm) {
+ input_active = false;
+ zm.notify();
+ }
+ break;
+ case 8:
+ if(line_sz > 0) {
+ Backspace();
+ line_sz--;
+ }
+ break;
+ default:
+ line[line_sz++] = key;
+ PrintChar(key);
+ }
+ }
+ }
+
+ public int Read(){
+ win.invalidate();
+ try {
+ synchronized(zm) {
+ input_active = true;
+ readchar = -1;
+ zm.wait();
+ return readchar;
+ }
+ } catch (Throwable t){
+ }
+ return 0;
+ }
+
+ public int ReadLine(char buffer[]) {
+ Flush();
+ win.invalidate();
+ count = 0;
+ try {
+ synchronized(zm) {
+ buffered = false;
+ line = buffer;
+ input_active = true;
+ line_sz = 0;
+ zm.wait();
+ line = null;
+ buffered = true;
+ win.Transcript(buffer, 0, line_sz);
+ win.Transcript(LF, 0, 1);
+ return line_sz;
+ }
+ } catch (Throwable t) {
+ }
+ return 0;
+ }
+
+ public void Print(String s) {
+ // there ought to be a better way to do this.
+ char[] buf = new char[s.length()];
+ s.getChars(0, s.length(), buf, 0);
+ Print(buf, s.length());
+ }
+
+ public int Random(int limit) {
+ if((limit & 0x8000) != 0) {
+ r.setSeed(limit & 0x7fff);
+ return 0;
+ }
+ if(limit == 0) {
+ r = new Random();
+ return 0;
+ }
+ return r.nextInt(limit) + 1;
+ }
+
+ public boolean Save(byte state[]) {
+ return win.Save(state);
+ }
+ public byte[] Restore() {
+ return win.Restore();
+ }
+
+ int selected;
+ int readchar;
+
+ int win0h;
+ int win1h;
+ int win1cx;
+ int win1cy;
+
+ Random r;
+ byte screen[];
+ byte buffer[];
+ int buflen;
+
+ char line[];
+ int line_sz;
+ ZWindow win;
+ int w,h; /* screen dimensions */
+ int cx,cy; /* cursor position */
+ int count;
+
+ boolean input_active;
+ boolean buffered;
+ boolean paused;
+ boolean running;
+ ZMachine zm;
+
+ char[] LF;
+ static final boolean TRACE = false;
+
+}
diff --git a/ZMachine.java b/ZMachine.java
@@ -0,0 +1,1838 @@
+// Z Machine V3/V4/V5 Runtime
+//
+// Copyright 2002, Brian J. Swetland <swetland@frotz.net>
+// Available under a BSD-Style License. Share and Enjoy.
+
+package net.frotz.zruntime;
+
+import java.io.PrintStream;
+
+class ZFrame
+{
+ ZFrame prev;
+ int pc;
+ int sp;
+ int bp;
+ int res;
+ int locals[];
+ int llen;
+
+ ZFrame(int pc, int sp, int locals) {
+ this.pc = pc;
+ this.sp = sp;
+ this.bp = sp;
+ this.llen = locals + 1;
+ if(ZMachine.CONSERVE_MEMORY){
+ this.locals = new int[17];
+ } else {
+ this.locals = new int[locals + 1];
+ }
+ }
+}
+
+public class ZMachine
+{
+ class ZObject {
+ int Size;
+ int PropMax;
+
+ ZObject() {
+ Size = kObjSize;
+ PropMax = 31;
+ }
+
+ private static final int kObjParent = 4;
+ private static final int kObjSibling = 5;
+ private static final int kObjChild = 6;
+ private static final int kObjProps = 7;
+ private static final int kObjSize = 9;
+
+ boolean attr_test(int obj, int attr) {
+ return (U8(object_ptr + obj * kObjSize + (attr >> 3)) & (1 << (7 - (attr & 7)))) != 0;
+ }
+ void attr_set(int obj, int attr) {
+ mem[object_ptr + obj * kObjSize + (attr >> 3)] |= (1 << (7 - (attr & 7)));
+ }
+ void attr_clear(int obj, int attr){
+ mem[object_ptr + obj * kObjSize + (attr >> 3)] &= ~(1 << (7 - (attr & 7)));
+ }
+
+ boolean inside(int a, int b) {
+ return U8(object_ptr + a * kObjSize + kObjParent) == b;
+ }
+
+ int sibling(int obj) {
+ return U8(object_ptr + obj * kObjSize + kObjSibling);
+ }
+
+ int parent(int obj) {
+ return U8(object_ptr + obj * kObjSize + kObjParent);
+ }
+
+ int child(int obj) {
+ return U8(object_ptr + obj * kObjSize + kObjChild);
+ }
+
+ void remove(int obj) {
+ int ptr = object_ptr + obj * kObjSize;
+
+ int x = U8(ptr + kObjParent);
+
+ if(x == 0) return; /* no parent */
+
+ int n = U8(ptr + kObjSibling);
+ int c = U8(object_ptr + x * kObjSize + kObjChild);
+ if(c == obj){
+ /* immediate child -- simple case */
+ W8(object_ptr + x * kObjSize + kObjChild, n);
+ } else {
+ while(c != 0) {
+ c = object_ptr + c * kObjSize;
+ x = U8(c + kObjSibling);
+ if(x == obj) {
+ W8(c + kObjSibling, n);
+ break;
+ } else {
+ c = x;
+ }
+ }
+ }
+ W8(ptr + kObjSibling, 0);
+ W8(ptr + kObjParent, 0);
+ }
+
+ void insert(int obj, int dest) {
+ int objptr = object_ptr + obj * kObjSize;
+ int dstptr = object_ptr + dest * kObjSize;
+
+ if(U8(objptr + kObjParent) != 0) remove(obj);
+
+ /* the old child (if any) becomes our sibling */
+ W8(objptr + kObjSibling, U8(dstptr + kObjChild));
+ /* we become the new eldest child */
+ W8(dstptr + kObjChild, obj);
+ /* and update our parent ptr */
+ W8(objptr + kObjParent, dest);
+ }
+
+ void print(int obj) {
+ obj = U16(object_ptr + obj * kObjSize + kObjProps);
+ if(U8(obj) != 0){
+ ZSCII(obj + 1);
+ Print(zscii_buf,zscii_ptr);
+ }
+ }
+
+ String name(int _obj) {
+ int obj = U16(object_ptr + _obj * kObjSize + kObjProps);
+ if(U8(obj) != 0){
+ ZSCII(obj + 1);
+ return new String(zscii_buf,0,zscii_ptr);
+ } else {
+ return new String("#"+_obj);
+ }
+ }
+
+ int status(){
+ return U16(object_ptr + Load(16) * kObjSize + kObjProps);
+ }
+
+
+
+ int get_prop_len(int addr) {
+ if(addr == 0){
+ return 0;
+ } else {
+ return (U8(addr - 1) >> 5) + 1;
+ }
+ }
+
+ int get_prop_addr(int obj, int prop_id) {
+ int i, prop,sz;
+
+ obj = object_ptr + kObjSize * obj;
+ prop = U16(obj + kObjProps);
+ prop += U8(prop) * 2 + 1; /* skip name */
+
+ while((sz = U8(prop)) != 0){
+ if((sz & 0x1f) == prop_id) {
+ return prop + 1;
+ } else {
+ prop += (sz >> 5) + 2;
+ }
+ }
+
+ return 0;
+ }
+
+ int get_prop_next(int obj, int prop_id) {
+ int i, prop,sz;
+
+ obj = object_ptr + kObjSize * obj;
+ prop = U16(obj + kObjProps);
+ prop += U8(prop) * 2 + 1; /* skip name */
+
+ if(LOGGING) LOG("get prop next " + obj + " " + prop_id);
+
+ if(prop_id == 0) {
+ /* get first prop */
+ return U8(prop) & 0x1f;
+ } else {
+ while((sz = U8(prop)) != 0){
+ if((sz & 0x1f) == prop_id) {
+ return U8(prop + (sz >> 5) + 2) & 0x1f;
+ } else {
+ prop += (sz >> 5) + 2;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ int get_prop(int obj, int prop_id) {
+ int i, prop,sz;
+
+ if(LOGGING) LOG("get_prop "+obj+" #"+prop_id);
+
+ obj = object_ptr + kObjSize * obj;
+ prop = U16(obj + kObjProps);
+ prop += U8(prop) * 2 + 1; /* skip name */
+
+ while((sz = U8(prop)) != 0){
+ if((sz & 0x1f) == prop_id) {
+ switch(sz >> 5){
+ case 0:
+ return U8(prop + 1);
+ case 1:
+ return U16(prop + 1);
+ default:
+ DIE("property not byte or word sized");
+ }
+ } else {
+ prop += (sz >> 5) + 2;
+ }
+ }
+
+ return U16(default_prop_ptr + prop_id * 2);
+ }
+
+ void put_prop(int obj, int prop_id, int val) {
+ int i, prop,sz;
+
+ if(LOGGING) LOG("put_prop "+obj+" #"+prop_id+" = 0x"+HEX(val));
+
+ obj = object_ptr + kObjSize * obj;
+ prop = U16(obj + kObjProps);
+ prop += U8(prop) * 2 + 1; /* skip name */
+
+ while((sz = U8(prop)) != 0){
+ if((sz & 0x1f) == prop_id) {
+ switch(sz >> 5){
+ case 0:
+ W8(prop + 1, val);
+ return;
+ case 1:
+ W16(prop + 1, val);
+ return;
+ default:
+ DIE("property not byte or word sized");
+ }
+ } else {
+ prop += (sz >> 5) + 2;
+ }
+ }
+
+ DIE("property not found");
+ }
+
+/* void dump(int obj) {
+ LOG("dumpobj("+obj+")");
+ obj = object_ptr + kObjSize * obj;
+ int props = U16(obj+kObjProps);
+ int sz;
+ String s;
+ int i;
+
+ LOG("@"+HEX(obj)+":"+
+ " p:"+U8(obj+kObjParent)+" s:"+U8(obj+kObjSibling)+" c:"+U8(obj+kObjChild)+
+ " pr:"+HEX(props));
+ LOG("name: "+(U8(props) == 0 ? "*none*" : ZSTRING(props + 1)));
+
+ s = "attr:";
+ for(i = 0; i < 32; i++){
+ if(Attr(obj, i)) s = s + " A"+i;
+ }
+ LOG(s);
+ props += U8(props) * 2 + 1;
+
+ while((sz = U8(props)) != 0) {
+ int id = sz & 0x1f;
+ sz = (sz >> 5) + 1;
+ s = "P"+id+":";
+ for(i = 0; i < sz; i++) s = s + " " + HEX(U8(props + i + 1));
+ LOG(s);
+ props += sz + 1;
+ }
+ }
+*/
+ }
+
+ class ZObjectWide extends ZObject {
+ ZObjectWide() {
+ Size = kObjSize;
+ PropMax = 63;
+ }
+
+ private static final int kObjParent = 6;
+ private static final int kObjSibling = 8;
+ private static final int kObjChild = 10;
+ private static final int kObjProps = 12;
+ private static final int kObjSize = 14;
+
+ boolean attr_test(int obj, int attr) {
+ return (U8(object_ptr + obj * kObjSize + (attr >> 3)) & (1 << (7 - (attr & 7)))) != 0;
+ }
+ void attr_set(int obj, int attr) {
+ mem[object_ptr + obj * kObjSize + (attr >> 3)] |= (1 << (7 - (attr & 7)));
+ }
+ void attr_clear(int obj, int attr){
+ mem[object_ptr + obj * kObjSize + (attr >> 3)] &= ~(1 << (7 - (attr & 7)));
+ }
+
+ boolean inside(int a, int b) {
+ return U16(object_ptr + a * kObjSize + kObjParent) == b;
+ }
+
+ int sibling(int obj) {
+ return U16(object_ptr + obj * kObjSize + kObjSibling);
+ }
+
+ int parent(int obj) {
+ return U16(object_ptr + obj * kObjSize + kObjParent);
+ }
+
+ int child(int obj) {
+ return U16(object_ptr + obj * kObjSize + kObjChild);
+ }
+
+ void remove(int obj) {
+ int ptr = object_ptr + obj * kObjSize;
+
+ int x = U16(ptr + kObjParent);
+
+ if(x == 0) return; /* no parent */
+
+ int n = U16(ptr + kObjSibling);
+ int c = U16(object_ptr + x * kObjSize + kObjChild);
+ if(c == obj){
+ /* immediate child -- simple case */
+ W16(object_ptr + x * kObjSize + kObjChild, n);
+ } else {
+ while(c != 0) {
+ c = object_ptr + c * kObjSize;
+ x = U16(c + kObjSibling);
+ if(x == obj) {
+ W16(c + kObjSibling, n);
+ break;
+ } else {
+ c = x;
+ }
+ }
+ }
+ W16(ptr + kObjSibling, 0);
+ W16(ptr + kObjParent, 0);
+ }
+
+ void insert(int obj, int dest) {
+ int objptr = object_ptr + obj * kObjSize;
+ int dstptr = object_ptr + dest * kObjSize;
+
+ if(U16(objptr + kObjParent) != 0) remove(obj);
+
+ /* the old child (if any) becomes our sibling */
+ W16(objptr + kObjSibling, U16(dstptr + kObjChild));
+ /* we become the new eldest child */
+ W16(dstptr + kObjChild, obj);
+ /* and update our parent ptr */
+ W16(objptr + kObjParent, dest);
+ }
+
+ void print(int obj) {
+ obj = U16(object_ptr + obj * kObjSize + kObjProps);
+ if(U8(obj) != 0){
+ ZSCII(obj + 1);
+ Print(zscii_buf,zscii_ptr);
+ }
+ }
+
+ String name(int _obj) {
+ int obj = U16(object_ptr + _obj * kObjSize + kObjProps);
+ if(U8(obj) != 0){
+ ZSCII(obj + 1);
+ return new String(zscii_buf,0,zscii_ptr);
+ } else {
+ return new String("#"+_obj);
+ }
+ }
+
+ int status(){
+ //XXX ???
+ return U16(object_ptr + Load(16) * kObjSize + kObjProps);
+ }
+
+
+ // 1xnnnnnn 1xssssss
+ // 0snnnnnn
+ int get_prop_len(int addr) {
+ if(addr == 0){
+ return 0;
+ } else {
+ int sz = U8(addr - 1);
+ if((sz & 0x80) == 0){ // one-byte of size/number data
+ if((sz & 0x40) == 0){
+ return 1;
+ } else {
+ return 2;
+ }
+ } else { // two bytes of size/number data
+ sz = sz & 63;
+ if(sz == 0){
+ return 64;
+ } else {
+ return sz;
+ }
+ }
+ }
+ }
+
+ int last_sz;
+
+ int get_prop_addr(int obj, int prop_id) {
+ int id, prop,sz;
+
+ obj = object_ptr + kObjSize * obj;
+ prop = U16(obj + kObjProps);
+ prop += U8(prop) * 2 + 1; /* skip name */
+
+ for(;;){
+ sz = U8(prop);
+ id = sz & 0x3f;
+ if(sz == 0) break;
+ if(id == prop_id) {
+ if((sz & 0x80) == 0){
+ last_sz = sz;
+ return prop + 1;
+ } else {
+ return prop + 2;
+ }
+ } else {
+ if((sz & 0x80) == 0){
+ if((sz & 0x40) == 0){
+ prop += 2; /* 1 hdr, 1 data */
+ } else {
+ prop += 3; /* 1 hdr, 2 data */
+ }
+ } else {
+ sz = U8(prop + 1) & 0x3f;
+ if(sz == 0) {
+ prop += (64 + 2);
+ } else {
+ prop += (sz + 2);
+ }
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ int get_prop(int obj, int prop_id) {
+ if(LOGGING) LOG("get_prop "+obj+" #"+prop_id);
+
+ int addr = get_prop_addr(obj, prop_id);
+ int sz = last_sz;
+
+ if(addr == 0){
+ return U16(default_prop_ptr + prop_id * 2);
+ }
+
+ if((sz & 0x80) == 0){
+ if((sz & 0x40) == 0){
+ return U8(addr);
+ } else {
+ return U16(addr);
+ }
+ } else {
+ DIE("property not byte or word sized");
+ return 0;
+ }
+
+ }
+
+ void put_prop(int obj, int prop_id, int val) {
+ if(LOGGING) LOG("put_prop "+obj+" #"+prop_id+" = 0x"+HEX(val));
+
+ int addr = get_prop_addr(obj, prop_id);
+ int sz = last_sz;
+
+ if(addr == 0){
+ DIE("property not byte or word sized");
+ }
+
+ if((sz & 0x80) == 0){
+ if((sz & 0x40) == 0){
+ W8(addr, val);
+ return;
+ } else {
+ W16(addr, val);
+ return;
+ }
+ }
+ }
+
+ int get_prop_next(int obj, int prop_id) {
+ if(LOGGING) LOG("get prop next " + obj + " #" + prop_id);
+
+ if(prop_id == 0){
+ obj = object_ptr + kObjSize * obj;
+ int prop = U16(obj + kObjProps);
+ prop += U8(prop) * 2 + 1; /* skip name */
+ return U8(prop) & 0x3f;
+ }
+
+ int addr = get_prop_addr(obj, prop_id);
+ int sz = last_sz;
+
+ if(addr == 0) DIE("get_prop_next on nonexistant property");
+
+ if((sz & 0x80) == 0){
+ if((sz & 0x40) == 0){
+ addr += 1;
+ } else {
+ addr += 2;
+ }
+ } else {
+ sz = U8(addr-1) & 0x3f;
+ if(sz == 0) {
+ addr += 64;
+ } else {
+ addr += sz;
+ }
+ }
+ return U8(addr) & 0x3f;
+ }
+ }
+
+ String HEX(int n) {
+ return Integer.toHexString(n);
+ }
+
+ int U16(int addr) {
+ return ((mem[addr] & 0xff) << 8) | (mem[addr + 1] & 0xff);
+ }
+
+ int U32(int addr) {
+ return ((mem[addr] & 0xff) << 24) |
+ ((mem[addr + 1] & 0xff) << 16) |
+ ((mem[addr + 2] & 0xff) << 8) |
+ (mem[addr + 3] & 0xff);
+ }
+
+ void W16(int addr, int val) {
+ mem[addr] = (byte) (val >> 8);
+ mem[addr + 1] = (byte) val;
+ }
+
+ void W8(int addr, int val) {
+ mem[addr] = (byte) val;
+ }
+
+ int U8(int addr) {
+ return mem[addr] & 0xff;
+ }
+
+ int V8(int addr) {
+ int x = mem[addr] & 0xff;
+
+ switch(x){
+ case 0x00: /* pop from stack */
+ frame.sp--;
+ if(frame.sp < frame.bp) DIE("sp underflow");
+ return stack[frame.sp];
+ case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
+ case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a:
+ case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ return frame.locals[x];
+
+ default:
+ return U16(global_ptr + x * 2);
+ }
+ }
+
+ /* load from stack (0), locals (1-15), or globals (16-255) */
+ int Load(int id) {
+ switch(id){
+ case 0x00: /* pop from stack */
+ frame.sp--;
+ if(frame.sp < frame.bp) DIE("sp underflow");
+ return stack[frame.sp];
+ case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
+ case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a:
+ case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ return frame.locals[id];
+
+ default:
+ return U16(global_ptr + id * 2);
+ }
+ }
+
+ String Name(int id) {
+ switch(id){
+ case 0x00:
+ return "sp";
+ case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
+ case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a:
+ case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ return "l"+HEX(id-1);
+ default:
+ return "g"+HEX(id-16);
+ }
+
+ }
+
+ /* store to stack (0), locals (1-15), or globals (16-255) */
+ void Store(int id, int val) {
+ if(LOGGING) LOG("store " + Name(id) + " (" + val + ")");
+ if(false) if(TRACING && (id > 0)) {
+ System.out.println("[> 0x" + HEX(id) + " = " + (val&0xffff));
+ }
+
+ switch(id){
+ case 0x00: /* pop from stack */
+ stack[frame.sp++] = val & 0xffff;
+ return;
+ case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
+ case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a:
+ case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ frame.locals[id] = val & 0xffff;
+ return;
+ default:
+ if((id < 0) || (id > 255)) DIE("store out of range! " + id);
+ id = global_ptr + id * 2;
+ mem[id] = (byte) (val >> 8);
+ mem[id + 1] = (byte) val;
+ }
+ }
+
+ void Branch(boolean cond) {
+ int x = U8(frame.pc++);
+
+ /* branch on !cond bit */
+ if((x & 0x80) == 0) cond = !cond;
+
+ if(cond) {
+ /* short branch bit */
+ if((x & 0x40) != 0) {
+ x = x & 0x3f;
+ } else {
+ if((x & 0x20) == 0) {
+ x = x & 0x3f;
+ } else {
+ x = x | 0xffffffc0;
+ }
+ x = (x << 8) | U8(frame.pc++);
+ }
+
+ if(x == 0) {
+ Return(0);
+ } else if(x == 1) {
+ Return(1);
+ } else {
+ if(LOGGING) LOG("branch to 0x" + HEX(frame.pc + x - 2) + " by " + x);
+ frame.pc = frame.pc + x - 2;
+ }
+ } else {
+ /* gotta bump the pc if the short branch bit is clear
+ and we didn't take the branch... */
+ if((x & 0x40) == 0) frame.pc++;
+ }
+ }
+
+
+ void LOG(String s) {
+ System.out.println(s);
+ }
+
+ void DIE(String s) {
+ int i;
+ Println("*** oops ***");
+ for(i = 1; i < frame.llen; i++){
+ Println("L"+i+"="+frame.locals[i]+" (0x"+HEX(frame.locals[i])+")");
+ }
+ Println("Z: HALTED @"+HEX(frame.pc)+": "+s);
+ scr.exit();
+ }
+
+ void BADOP(int op) {
+ DIE("unknown opcode 0x"+HEX(op));
+ }
+
+ void BADOP(int op, int ex) {
+ DIE("unknown opcode 0x"+HEX(op)+", 0x"+HEX(ex));
+ }
+
+
+ public int execute(ZFrame f) {
+ int op, num, t, x;
+ int a,b,c,d;
+
+ num = 0;
+ t = 0;
+
+ frame = f;
+
+ a = 0;
+ b = 0;
+ c = 0;
+ d = 0;
+
+ for(;;){
+ f = frame;
+
+ int pc = f.pc;
+ op = U8(pc++);
+
+ /* decode arguments */
+ switch(op >> 4){
+ case 0x00:
+ case 0x01: /* 2OP, sconst, sconst */
+ a = U8(pc++);
+ b = U8(pc++);
+ num = 2;
+ break;
+ case 0x02:
+ case 0x03: /* 2OP, sconst, var */
+ a = U8(pc++);
+ b = V8(pc++);
+ num = 2;
+ break;
+ case 0x04:
+ case 0x05: /* 2OP, var, sconst */
+ a = V8(pc++);
+ b = U8(pc++);
+ num = 2;
+ break;
+ case 0x06:
+ case 0x07: /* 2OP, var, var */
+ a = V8(pc++);
+ b = V8(pc++);
+ num = 2;
+ break;
+ case 0x08: /* 1OP, lconst */
+ a = U16(pc);
+ pc += 2;
+ num = 1;
+ break;
+ case 0x09: /* 1OP, sconst */
+ a = U8(pc++);
+ num = 1;
+ break;
+ case 0x0a: /* 1OP, var */
+ a = V8(pc++);
+ num = 1;
+ break;
+ case 0x0b: /* 0OP */
+ if(op != 0xbe) break;
+ op = U8(pc++) + 0x100;
+ // read extended opcode and fall through
+ case 0x0c:
+ case 0x0d: /* 2OP, types-in-next */
+ case 0x0e: /* VAR, types-in-next */
+ case 0x0f:
+ x = U8(pc++); /* arg descriptor */
+ num = 0;
+ while(num < 4) {
+ switch((x >> (6 - num * 2)) & 3){
+ case 0: /* lconst */
+ t = U16(pc);
+ pc += 2;
+ break;
+ case 1: /* sconst */
+ t = U8(pc++);
+ break;
+ case 2: /* var */
+ t = V8(pc++);
+ break;
+ case 3: /* none */
+ t = -1;
+ break;
+ }
+ if(t == -1) break;
+ switch(num++){
+ case 0:
+ a = t;
+ break;
+ case 1:
+ b = t;
+ break;
+ case 2:
+ c = t;
+ break;
+ case 3:
+ d = t;
+ break;
+ }
+ }
+ //XXX check argcounts...
+ }
+
+ if(TRACING) {
+ String ss = "["+HEX(f.pc)+"] " + HEX(op);
+ if(num > 0) ss = ss + " " + a;
+ if(num > 1) ss = ss + " " + b;
+ if(num > 2) ss = ss + " " + c;
+ if(num > 3) ss = ss + " " + d;
+ System.out.println(ss);
+ }
+ f.pc = pc;
+
+
+ /* decode instruction */
+ switch(op){
+ /* 2OP instructions --------------- */
+ case 0x01: case 0x21: case 0x41: case 0x61: case 0xc1: // je a b ?(label)
+ switch(num){
+ case 0:
+ case 1:
+ Branch(true);
+ break;
+ case 2:
+ Branch(a == b);
+ break;
+ case 3:
+ Branch((a == b) || (a == c));
+ break;
+ case 4:
+ Branch((a == b) || (a == c) || (a == d));
+ break;
+ }
+ break;
+ case 0x02: case 0x22: case 0x42: case 0x62: case 0xc2: // jl a b ?(label)
+ Branch(((short)a) < ((short)b));
+ break;
+ case 0x03: case 0x23: case 0x43: case 0x63: case 0xc3: // jg a b ?(label)
+ Branch(((short)a) > ((short)b));
+ break;
+ case 0x04: case 0x24: case 0x44: case 0x64: case 0xc4: // dec_chk (var) val ?(label)
+ t = (Load(a) - 1) & 0xffff;
+ Store(a, t);
+ Branch(((short)t) < ((short)b));
+ break;
+ case 0x05: case 0x25: case 0x45: case 0x65: case 0xc5: // inc_chk (var) val ?(label)
+ t = (Load(a) + 1) & 0xffff;
+ Store(a, t);
+ Branch(((short)t) > ((short)b));
+ break;
+ case 0x06: case 0x26: case 0x46: case 0x66: case 0xc6: // jin obj1 obj2 ?(label)
+ Branch(obj.inside(a,b));
+ break;
+ case 0x07: case 0x27: case 0x47: case 0x67: case 0xc7: // test bitmap flags ?(label)
+ Branch((a & b) == b);
+ break;
+ case 0x08: case 0x28: case 0x48: case 0x68: case 0xc8: // or a b -> (result)
+ Store(U8(f.pc++), a | b);
+ break;
+ case 0x09: case 0x29: case 0x49: case 0x69: case 0xc9: // and a b -> (result)
+ Store(U8(f.pc++), a & b);
+ break;
+ case 0x0a: case 0x2a: case 0x4a: case 0x6a: case 0xca: // test_attr obj attr ?(label)
+ Branch(obj.attr_test(a, b));
+ break;
+ case 0x0b: case 0x2b: case 0x4b: case 0x6b: case 0xcb: // set_attr obj attr
+ obj.attr_set(a, b);
+ break;
+ case 0x0c: case 0x2c: case 0x4c: case 0x6c: case 0xcc: // clear_attr obj attr
+ obj.attr_clear(a, b);
+ break;
+ case 0x0d: case 0x2d: case 0x4d: case 0x6d: case 0xcd: // store (var) value
+ Store(a,b);
+ break;
+ case 0x0e: case 0x2e: case 0x4e: case 0x6e: case 0xce: // insert_obj object dest
+ obj.insert(a, b);
+ break;
+ case 0x0f: case 0x2f: case 0x4f: case 0x6f: case 0xcf: // loadw array word-idx -> (result)
+ Store(U8(f.pc++),U16(a + b * 2));
+ break;
+ case 0x10: case 0x30: case 0x50: case 0x70: case 0xd0: // loadb array byte-idx -> (result)
+ Store(U8(f.pc++),U8(a + b));
+ break;
+ case 0x11: case 0x31: case 0x51: case 0x71: case 0xd1: // get_prop obj prop -> (result)
+ Store(U8(f.pc++),obj.get_prop(a, b));
+ break;
+ case 0x12: case 0x32: case 0x52: case 0x72: case 0xd2: // get_prop_addr obj prop -> (result)
+ Store(U8(f.pc++),obj.get_prop_addr(a, b));
+ break;
+ case 0x13: case 0x33: case 0x53: case 0x73: case 0xd3: // get_next_prop obj prop -> (result)
+ Store(U8(f.pc++),obj.get_prop_next(a, b));
+ break;
+ case 0x14: case 0x34: case 0x54: case 0x74: case 0xd4: // add a b -> (result)
+ Store(U8(f.pc++), ((short)a) + ((short)b));
+ break;
+ case 0x15: case 0x35: case 0x55: case 0x75: case 0xd5: // sub a b -> (result)
+ Store(U8(f.pc++), ((short)a) - ((short)b));
+ break;
+ case 0x16: case 0x36: case 0x56: case 0x76: case 0xd6: // mul a b -> (result)
+ Store(U8(f.pc++), ((short)a) * ((short)b));
+ break;
+ case 0x17: case 0x37: case 0x57: case 0x77: case 0xd7: // div a b -> (result)
+ Store(U8(f.pc++), ((short)a) / ((short)b));
+ break;
+ case 0x18: case 0x38: case 0x58: case 0x78: case 0xd8: // mod a b -> (result)
+ Store(U8(f.pc++), ((short)a) % ((short)b));
+ break;
+ case 0x19: case 0x39: case 0x59: case 0x79: case 0xd9: // call_2s a b -> (result) V4
+ if(notV4) BADOP(op);
+ op_call(num, a, b, 0, 0, U8(f.pc++));
+ break;
+ case 0x1a: case 0x3a: case 0x5a: case 0x7a: case 0xda: // call_2n a b V5
+ if(notV5) BADOP(op);
+ op_call(num, a, b, 0, 0, -1);
+ break;
+ case 0x1b: case 0x3b: case 0x5b: case 0x7b: case 0xdb: // set_color fg bg V5
+ if(notV5) BADOP(op);
+ break; //TODO
+// case 0x1c: case 0x3c: case 0x5c: case 0x7c: case 0xdc: // throw value frame V5/6
+
+ /* 1OP instructions --------------- */
+ case 0x80: case 0x90: case 0xa0: // jz a ?(label)
+ Branch(a == 0);
+ break;
+ case 0x81: case 0x91: case 0xa1: // get_sibling object -> (result) ?(label)
+ Store(U8(f.pc++), t = obj.sibling(a));
+ Branch(t != 0);
+ break;
+ case 0x82: case 0x92: case 0xa2: // get_child object -> (result) ?(label)
+ Store(U8(f.pc++), t = obj.child(a));
+ Branch(t != 0);
+ break;
+ case 0x83: case 0x93: case 0xa3: // get_parent object -> (result)
+ Store(U8(f.pc++), obj.parent(a));
+ break;
+ case 0x84: case 0x94: case 0xa4: // get_prop_len prop-addr -> (result)
+ Store(U8(f.pc++), obj.get_prop_len(a));
+ break;
+ case 0x85: case 0x95: case 0xa5: // inc (variable)
+ Store(a, Load(a) + 1);
+ break;
+ case 0x86: case 0x96: case 0xa6: // dec (variable)
+ Store(a, Load(a) - 1);
+ break;
+ case 0x87: case 0x97: case 0xa7: // print_addr byte-addr-of-str
+ ZSCII(a);
+ Print(zscii_buf,zscii_ptr);
+ break;
+ case 0x88: case 0x98: case 0xa8: // call_1s a -> (result) V4
+ if(notV4) BADOP(op);
+ op_call(1, a, 0, 0, 0, U8(f.pc++));
+ break;
+ case 0x89: case 0x99: case 0xa9: // remove_obj object
+ obj.remove(a);
+ break;
+ case 0x8a: case 0x9a: case 0xaa: // print_obj object
+ obj.print(a);
+ break;
+ case 0x8b: case 0x9b: case 0xab: // ret value
+ Return(a);
+ break;
+ case 0x8c: case 0x9c: case 0xac: // jump ?(label)
+ f.pc = f.pc - 2 + ((short)a);
+ break;
+ case 0x8d: case 0x9d: case 0xad: // print_paddr pack-addr-of-str
+ ZSCII(a * PACK);
+ Print(zscii_buf,zscii_ptr);
+ break;
+ case 0x8e: case 0x9e: case 0xae: // load (variable) -> (result)
+ Store(U8(f.pc++), Load(a));
+ break;
+ case 0x8f: case 0x9f: case 0xaf: // not value -> (result)
+ if(V5){
+ // call func
+ op_call(1, a, 0, 0, 0, -1);
+ break;
+ } else {
+ // not value -> (result)
+ Store(U8(f.pc++), ~a);
+ }
+ break;
+ /* 0OP instructions --------------- */
+ case 0xb0: // rtrue
+ Return(1);
+ break;
+ case 0xb1: // rfalse
+ Return(0);
+ break;
+ case 0xb2: // print (literal-string)
+ frame.pc = ZSCII(frame.pc);
+ Print(zscii_buf, zscii_ptr);
+ break;
+ case 0xb3: // print_ret (literal-string)
+ frame.pc = ZSCII(frame.pc);
+ Print(zscii_buf, zscii_ptr);
+ scr.NewLine();
+ Return(1);
+ break;
+ case 0xb4: // nop
+ break;
+ case 0xb5:
+ if(V5) BADOP(op); // illegal
+ if(V4) BADOP(op); // save -> (result)
+ Branch(scr.Save(save())); // save ?(label)
+ break;
+ case 0xb6:
+ if(V5) BADOP(op); // illegal
+ if(V4) BADOP(op); // restore -> (result)
+ Branch(restore(scr.Restore())); // restore ?(label)
+ break;
+ case 0xb7: // restart
+ restart();
+ break;
+ case 0xb8: // ret_popped
+ Return(Load(0));
+ break;
+ case 0xb9: // pop
+ if(V5) BADOP(op); // catch -> (result)
+ frame.sp--;
+ if(frame.sp < frame.bp) DIE("stack underrun");
+ break;
+ case 0xba: // quit
+ restart();
+ break;
+ case 0xbb: // new_line
+ scr.NewLine();
+ break;
+ case 0xbc: // show_status
+ if(V3) UpdateStatus();
+ // illegal after 3, but zmachine spec says ignore it
+ break;
+ case 0xbd: // verify ?(label)
+ //XXX check checksum for real :-)
+ Branch(true);
+ break;
+ case 0xbf: // piracy ?(label)
+ if(notV5) BADOP(op);
+ Branch(true);
+ break;
+ /* VAR instructions --------------- */
+ case 0xe0: // call routine, arg0-3 -> (result)
+ op_call(num, a, b, c, d, U8(f.pc++));
+ break;
+ case 0xe1: // storew array word-index value
+ if(num != 3) DIE("storew needs 3 args");
+ W16(a + b * 2, c);
+ break;
+ case 0xe2: // storeb array byte-index value
+ if(num != 3) DIE("storeb needs 3 args");
+ W8(a + b, c);
+ break;
+ case 0xe3: // put-prop object property value
+ if(num != 3) DIE("putprop needs 3 args");
+ obj.put_prop(a,b,c);
+ break;
+ case 0xe4:
+ if(V3) {// sread text parse
+ UpdateStatus();
+ readline(a, b);
+ } else {
+ if(num < 4) d = 0;
+ if(num < 3) c = 0;
+// if((c != 0) || (d != 0)) DIE("timed read unsupported");
+ readline(a,b);
+ Store(U8(f.pc++),10);
+ }
+ break;
+ case 0xe5: // print_char out-char-code
+ PrintChar(a);
+ break;
+ case 0xe6: // print_num value
+ PrintNumber(a);
+ break;
+ case 0xe7: // random range -> (result)
+ Store(U8(f.pc++), scr.Random(a));
+ break;
+ case 0xe8: // push value
+ Store(0, a);
+ break;
+ case 0xe9: // pull (variable)
+ Store(a, Load(0));
+ break;
+ case 0xea: // split_window lines
+ scr.SplitWindow(a);
+ break;
+ case 0xeb: // set_window window
+ scr.SetWindow(a);
+ break;
+ case 0xec: // call_vs2 0..7 -> (result)
+ DIE("call_vs2");
+ case 0xed: // erase_window win V4
+ scr.EraseWindow((short)a);
+ break;
+ case 0xee: //XXX erase line value V4
+ System.err.println("ERASE LINE: "+a+","+b);
+ break;
+ case 0xef: // set_cursor line column V4
+ scr.MoveCursor((short)b,(short)a);
+ break;
+ case 0xf0: //XXX get_cursor array V4/6
+ BADOP(op);
+ break;
+ case 0xf1: //XXX set_text_style style V4
+ break;
+ case 0xf2: //XXX buffer_mode flag V4
+// System.err.println("BUFFER? "+(short)a);
+ break;
+ case 0xf3: //XXX output_stream number
+ a = (short) a;
+ if(a == 1) str_display = true;
+ if(a == -1) str_display = false;
+ break;
+ case 0xf4: //XXX input_stream number
+ break;
+ case 0xf5: //XXX soundfx V5
+ break;
+ case 0xf6: // read_char 1 time routine -> (result) V4
+ if((b != 0) || (c != 0)) DIE("timed read not supported");
+ int ch = scr.Read();
+ Store(U8(f.pc++), ch);
+ break;
+ case 0xf7: //XXX scan_table x table len form -> (result) V4
+ BADOP(op); break;
+ case 0xf8: // not value -> (result) V5/6
+ Store(U8(f.pc++), ~a);
+ break;
+ case 0xf9: // call_vn 0..3
+ if(notV5) BADOP(op);
+ op_call(num, a, b, c, d, -1);
+ break;
+ case 0xfa: // call_vn2 0..7 V5
+ BADOP(op); break;
+ case 0xfb: //XXX tokenize text parse dict flag V5
+ BADOP(op); break;
+ case 0xfc: //XXX encode_text zscii len from coded V5
+ BADOP(op); break;
+ case 0xfd: //XXX copy_table first second size V5
+ BADOP(op); break;
+ case 0xfe: //XXX print_table zscii width height skip V5
+ BADOP(op); break;
+ case 0xff: //XXX check_arg_count argument-number V5
+ BADOP(op); break;
+ case 0x100: //XXX save table bytes name -> (result) V5
+ if(notV5) BADOP(op);
+ if(num != 0) DIE("extended save unsupported");
+ if(scr.Save(save())){
+ Store(U8(f.pc++), 1);
+ } else {
+ Store(U8(f.pc++), 0);
+ }
+ break;
+ case 0x101: //XXX restore table bytes name -> (result) V5
+ if(notV5) BADOP(op);
+ if(num != 0) DIE("extended restore unsupported");
+ if(restore(scr.Restore())){
+ f = frame;
+ Store(U8(f.pc++), 2);
+ } else {
+ f = frame;
+ Store(U8(f.pc++), 0);
+ }
+ break;
+ case 0x102: //XXX log_shift number places -> (result) V5
+ BADOP(op); break;
+ case 0x103: //XXX arith_shift number places -> (result) V5
+ BADOP(op); break;
+ case 0x104: //XXX set_font font -> (result) V5
+ BADOP(op); break;
+ case 0x109: // save_undo -> (result) V5
+ if(notV5) BADOP(op);
+ Store(U8(f.pc++), -1);
+ break; // unsupported
+ case 0x10a: // restore_undo -> (result) V5
+ if(notV5) BADOP(op);
+ Store(U8(f.pc++), 0);
+ break; // ignored as unsupported
+ default:
+ BADOP(op);
+ }
+ }
+ }
+
+ void op_call(int argc, int pc, int a1, int a2, int a3, int res) {
+ int l, i;
+ ZFrame f;
+
+ if(argc < 1) DIE("call with no target");
+
+ pc *= PACK; /* unpack addr */
+ if(pc == 0) {
+ /* weird special case for calling 0 */
+ if(res != -1) Store(res,0);
+ return;
+ }
+
+ l = U8(pc++);
+ if((l < 0) || (l > 15)) DIE("bad local count " + l);
+
+ if(V5){
+ i = pc;
+ } else {
+ /* adjust for default arguments */
+ i = pc + l * 2;
+ }
+ if(CONSERVE_MEMORY){
+ if(freelist != null) {
+ f = freelist;
+ freelist = f.prev;
+ f.pc = i;
+ f.sp = frame.sp;
+ f.bp = frame.sp;
+ f.llen = l + 1;
+ } else {
+ f = new ZFrame(i, frame.sp, l);
+ }
+ } else {
+ f = new ZFrame(i, frame.sp, l);
+ }
+
+ l++;
+ if(V5){
+ /* zero locals */
+ for(i = 1; i < l; i++){
+ f.locals[i] = 0;
+ }
+ } else {
+ /* preload locals with default values */
+ for(i = 1; i < l; i ++){
+ f.locals[i] = U16(pc);
+ pc += 2;
+ }
+ }
+
+ /* load arguments into locals */
+ if(argc > l) argc = l;
+ if(argc > 1) {
+ f.locals[1] = a1;
+ if(argc > 2) {
+ f.locals[2] = a2;
+ if(argc > 3) {
+ f.locals[3] = a3;
+ }
+ }
+ }
+
+ if(LOGGING) {
+ String s = "locals #"+(l-1);
+ for(i = 1; i < l; i++){
+ s = s + ", "+f.locals[i];
+ }
+ LOG(s);
+ }
+ f.res = res;
+ f.prev = frame;
+ frame = f;
+ }
+
+ void Return(int value) {
+ if(LOGGING) LOG("return " + value + " -> 0x" + HEX(frame.prev.pc));
+ ZFrame f = frame;
+ frame = f.prev;
+ if(f.res != -1) Store(f.res,value);
+ if(CONSERVE_MEMORY) {
+ f.prev = freelist;
+ freelist = f.prev;
+ } else {
+ f.prev = null;
+ }
+ }
+
+
+ void UpdateStatus() {
+ int a = obj.status();
+ if(U8(a) != 0){
+ ZSCII(a + 1);
+ } else {
+ zscii_ptr = 0;
+ }
+ scr.SetStatus(zscii_buf, zscii_ptr);
+ }
+
+ char line[] = new char[256];
+
+ /* parsebuf: max, count, [ w:addr, len, off ] * count */
+ void readline(int text, int parse) {
+ int len = scr.ReadLine(line);
+ int max = U8(text);
+ int pmax = U8(parse);
+ int i,j,ptr,start;
+ text++;
+
+ if(V3) max -= 1;
+
+ /* workaround for buggy early games per zspec1.0 */
+ if(pmax > 59) pmax = 59;
+
+ if(LOGGING) LOG("readline " + max + " " + U8(parse));
+ parse += 2;
+
+ /* copy linebuffer to zmemory, terminating with nul and
+ truncating if needed */
+ if(len < max) max = len;
+ if(V3){
+ for(i = 0; i < max; i++){
+ W8(text + i, line[i]);
+ }
+ W8(text + i, 0);
+ } else {
+ W8(text, max);
+ for(i = 0; i < max; i++){
+ W8(text + i + 1, line[i]);
+ }
+ }
+ /* tokenize */
+ ptr = 0;
+ start = 0;
+ i = 0;
+
+ while(ptr < max) {
+ if((ptr-start) == 0) {
+ if(line[ptr] == ' ') {
+ start = ptr = ptr + 1;
+ continue;
+ }
+ }
+ /* check for term char special case */
+
+ for(j = 0; j < special_count; j++) {
+ if(line[ptr] == special_chars[j]){
+ if((ptr - start) > 0){
+ find_token(line, start, ptr - start, parse + i * 4);
+ i++;
+ }
+ if(j != 0) {
+ /* special #0 (space) does not get its own token */
+ find_token(line, ptr, 1, parse + i * 4);
+ i++;
+ }
+ start = ptr + 1;
+ break;
+ }
+ }
+
+ ptr++;
+ }
+
+ if((ptr - start) > 0){
+ find_token(line, start, ptr - start, parse + i * 4);
+ i++;
+ }
+
+ W8(parse - 1, i);
+ }
+
+ int token_workbuf[] = new int[16];
+
+ void encode(char in[], int off, int len, int out[], int max) {
+ int i;
+ for(i = 0; i < max; i++) {
+ if(len-- > 0) {
+ int x = in[off++];
+ if((x >= 'a') && (x <= 'z')){
+ out[i] = x - ('a' - 6);
+ continue;
+ }
+ if((x >= 'A') && (x <= 'Z')){
+ out[i] = x - ('A' - 6);
+ continue;
+ }
+ if((x >= '0') && (x <= '9')){
+ out[i++] = 5;
+ out[i] = x - ('0' - 8);
+ continue;
+ }
+ switch(x) {
+ case '.': x = 18; break;
+ case ',': x = 19; break;
+ case '!': x = 20; break;
+ case '?': x = 21; break;
+ case '_': x = 22; break;
+ case '#': x = 23; break;
+ case '\'': x = 24; break;
+ case '"': x = 25; break;
+ case '/': x = 26; break;
+ case '\\': x = 27; break;
+ case '-': x = 28; break;
+ case ':': x = 29; break;
+ case '(': x = 30; break;
+ case ')': x = 31; break;
+ default:
+ LOG("unknown inchar " + x);
+ x = 0;
+ }
+ out[i++] = 5;
+ out[i] = x;
+ } else {
+ out[i] = 5;
+ }
+ }
+ }
+
+
+ void find_token(char data[], int off, int len, int parse) {
+ int token = 0;
+ int t, low, high, pos, i, n, KEYSZ;
+ int buf[] = token_workbuf;
+ if(LOGGING) LOG("W: '"+new String(data,off,len)+"' @"+off+","+len);
+
+ if(V3) KEYSZ = 4;
+ else KEYSZ = 6;
+
+ encode(data, off, len, buf, KEYSZ*3/2);
+
+ t = (buf[0] << 10) | (buf[1] << 5) | buf[2];
+ buf[0] = (byte) (t >> 8);
+ buf[1] = (byte) t;
+ t = (buf[3] << 10) | (buf[4] << 5) | buf[5];
+ buf[2] = (byte) (t >> 8);
+ buf[3] = (byte) t;
+ if(V3){
+ buf[2] |= 0x80;
+ } else {
+ t = (buf[6] << 10) | (buf[7] << 5) | buf[8];
+ buf[4] = (byte) ((t >> 8) | 0x80);
+ buf[5] = (byte) t;
+ }
+
+ low = -1;
+ high = dent_count;
+ token = 0;
+
+ while((high - low) > 1){
+ pos = (high + low) / 2;
+ t = dict_ptr + dent_len * pos;
+
+ for(n = 0, i = 0; i < KEYSZ; i++){
+ n = (buf[i]&0xff) - (mem[t+i]&0xff);
+ if(n != 0) break;
+ }
+
+ if(n == 0) {
+ token = t;
+ break;
+ } else if(n > 0){
+ low = pos;
+ } else {
+ high = pos;
+ }
+ }
+
+ W16(parse, token);
+ W8(parse + 2, len);
+ W8(parse + 3, off + 1);
+ }
+
+ boolean Attr(int obj, int num) {
+ if((U8(obj + (num >> 3)) & (1 << (7 - (num & 7)))) == 0){
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+
+ String ZSTRING(int ptr) {
+ ZSCII(ptr);
+ return new String(zscii_buf,0,zscii_ptr);
+ }
+
+ int ZSCII(int ptr) {
+ zscii_ptr = 0; // at the beginning
+ zscii_mode = 0; // shift mode A0
+ return _ZSCII(ptr);
+ }
+
+ int _ZSCII(int ptr) {
+ boolean done;
+ int a,b;
+
+ do {
+ /* XAAAAABB BBBCCCCC */
+ a = mem[ptr++] & 0xff;
+ b = mem[ptr++] & 0xff;
+ ZCHAR((a >> 2) & 0x1f);
+ ZCHAR(((a & 3) << 3) | (b >> 5));
+ ZCHAR(b & 0x1f);
+ } while((a & 0x80) == 0);
+ return ptr;
+ }
+
+ void ZCHAR(int c) {
+ switch(zscii_mode){
+ case 0: // A0 mode
+ switch(c) {
+ case 1: case 2: case 3: // next char is an abbrev
+ zscii_mode = c + 2;
+ return;
+ case 4: // shift to A1
+ zscii_mode = 1;
+ return;
+ case 5: // shift to A2
+ zscii_mode = 2;
+ return;
+ }
+ zscii_buf[zscii_ptr++] = zscii_map[zscii_mode][c];
+ return;
+ case 1: // A1 mode
+ switch(c) {
+ case 1: case 2: case 3: // next char is an abbrev
+ zscii_mode = c + 2;
+ return;
+ case 4: // shift to A1
+ zscii_mode = 1;
+ return;
+ case 5: // shift to A2
+ zscii_mode = 2;
+ return;
+ }
+ zscii_buf[zscii_ptr++] = zscii_map[zscii_mode][c];
+ zscii_mode = 0;
+ return;
+ case 2: // A2 mode
+ switch(c) {
+ case 1: case 2: case 3: // next char is an abbrev
+ zscii_mode = c + 2;
+ return;
+ case 4: // shift to A1
+ zscii_mode = 1;
+ return;
+ case 5: // shift to A2
+ zscii_mode = 2;
+ return;
+ case 6:
+ zscii_mode = 6;
+ return;
+ }
+ zscii_buf[zscii_ptr++] = zscii_map[zscii_mode][c];
+ zscii_mode = 0;
+ return;
+ case 3: // Abbrev modes
+ case 4:
+ case 5:
+ c = ((zscii_mode - 3) * 32 + c);
+ zscii_mode = 0;
+ _ZSCII(U16(abbr_ptr + 2 * c) * 2);
+ zscii_mode = 0;
+ break;
+ case 6:
+ zscii_tmp = c;
+ zscii_mode = 7;
+ break;
+ case 7:
+ zscii_buf[zscii_ptr++] = (char) (c | ( zscii_tmp << 5 ));
+ zscii_mode = 0;
+ break;
+
+ }
+
+ }
+
+ char zscii_map[][] = {
+ { ' ' ,0 ,0 ,0 ,0 ,0 ,'a' ,'b' ,'c' ,'d' ,'e' ,'f' ,'g' ,'h' ,'i' ,'j' ,
+ 'k' ,'l' ,'m' ,'n' ,'o' ,'p' ,'q' ,'r' ,'s' ,'t' ,'u' ,'v' ,'w' ,'x' ,'y' ,'z' },
+ { 0 ,0 ,0 ,0 ,0 ,0 ,'A' ,'B' ,'C' ,'D' ,'E' ,'F' ,'G' ,'H' ,'I' ,'J' ,
+ 'K' ,'L' ,'M' ,'N' ,'O' ,'P' ,'Q' ,'R' ,'S' ,'T' ,'U' ,'V' ,'W' ,'X' ,'Y' ,'Z' },
+ { 0 ,0 ,0 ,0 ,0 ,0 ,' ' ,'\n','0' ,'1' ,'2' ,'3' ,'4' ,'5' ,'6' ,'7',
+ '8' ,'9' ,'.' ,',' ,'!' ,'?' ,'_' ,'#' ,'\'','"' ,'/' ,'\\','-' ,':' ,'(' ,')' }
+ };
+ int zscii_ptr;
+ int zscii_mode;
+ int zscii_tmp;
+ char zscii_buf[];
+
+ public void run() {
+ LOG("start");
+ int i;
+
+ //for(i = 0; i < (32*3); i++) LOG("'"+ZSTRING(U16(abbr_ptr + i * 2) * 2)+"'");
+
+ //for(i = 1; i < 251; i++) dumpobj(i);
+ try {
+ execute(new ZFrame(U16(6), 0, 0));
+ System.err.println("*** DONE ***");
+ } catch (Throwable t) {
+ t.printStackTrace();
+ DIE("jvm oops");
+ }
+ }
+
+ void restart() {
+ System.arraycopy(backup, 0, mem, 0, static_ptr);
+ syncstate();
+ frame = new ZFrame(U16(6), 0, 0);
+ freelist = null;
+ }
+
+ void SW(byte[] data, int addr, int val) {
+ data[addr] = (byte) ((val >> 24) & 0xff);
+ data[addr + 1] = (byte) ((val >> 16) & 0xff);
+ data[addr + 2] = (byte) ((val >> 8) & 0xff);
+ data[addr + 3] = (byte) (val & 0xff);
+ }
+
+ int LW(byte[] mem, int addr) {
+ return ((mem[addr] & 0xff) << 24) |
+ ((mem[addr + 1] & 0xff) << 16) |
+ ((mem[addr + 2] & 0xff) << 8) |
+ (mem[addr + 3] & 0xff);
+ }
+
+ byte[] save() {
+ int size, p, i, c;
+ ZFrame f;
+ byte[] state;
+
+ /* dynram, stack, stacksize, fcount */
+ size = static_ptr + frame.sp * 4 + 8;
+
+ for(c = 0, f = frame; f != null; f = f.prev) {
+ size += 16 + 4 * (f.locals.length - 1);
+ /* pc, sp, bp, llen, local (x llen) */
+ c ++;
+ }
+
+ state = new byte[size + 64];
+ System.arraycopy(mem, 0, state, 64, static_ptr);
+ p = static_ptr + 64;
+
+ /* save stack */
+ SW(state, p, frame.sp);
+ p += 4;
+ for(i = 0; i < frame.sp; i++) {
+ SW(state, p, stack[i]);
+ p += 4;
+ }
+
+
+ /* save framelist */
+ SW(state, p, c);
+ p += 4;
+ for(f = frame ; f != null; f = f.prev) {
+ SW(state, p, f.pc);
+ SW(state, p + 4, f.sp);
+ SW(state, p + 8, f.bp);
+ SW(state, p + 12, f.locals.length - 1);
+ p += 16;
+ for(i = 0; i < f.locals.length - 1; i++){
+ SW(state, p, f.locals[1 + i]);
+ p += 4;
+ }
+ }
+
+ return state;
+ }
+
+ boolean restore(byte[] state) {
+ int p = static_ptr + 64;
+ int c, i, j;
+ ZFrame f = null;
+
+ if((state == null) || (state.length < static_ptr)) return false;
+
+ /* sanity check that this is the same gamefile */
+ for(i = 0; i < 64; i++){
+ if(state[i+64] != mem[i]) return false;
+ }
+ /* restore dynamic ram */
+ System.arraycopy(state, 64, mem, 0, static_ptr);
+
+ /* restore stack */
+ c = LW(state, p);
+ p += 4;
+ for(i = 0; i < c; i++) {
+ stack[i] = LW(state, p);
+ p += 4;
+ }
+
+ /* restore framelist */
+ c = LW(state, p);
+ p += 4;
+ frame = null;
+ for(i = 0; i < c; i++) {
+ int pc = LW(state, p + 0);
+ int sp = LW(state, p + 4);
+ int bp = LW(state, p + 8);
+ int len = LW(state, p + 12);
+ p += 16;
+ if(f == null) {
+ f = new ZFrame(pc, sp, len);
+ frame = f;
+ } else {
+ ZFrame x = new ZFrame(pc, sp, len);
+ f.prev = x;
+ f = x;
+ }
+ f.bp = bp;
+ for(j = 0; j < len; j++){
+ f.locals[j + 1] = LW(state, p);
+ p += 4;
+ }
+ }
+
+ syncstate();
+ return true;
+ }
+
+ int special_count;
+ int special_chars[];
+
+ public ZMachine(ZScreen screen, byte[] data) {
+ int i;
+
+ LOG("init");
+ scr = screen;
+ mem = data;
+ stack = new int[4096];
+ zscii_buf = new char[4096];
+ int version = U8(0);
+
+ LOG("game version " + version);
+ switch(version){
+ case 1:
+ case 2:
+ case 3:
+ PACK = 2;
+ notV4 = true;
+ notV5 = true;
+ V3 = true;
+ obj = new ZObject();
+ break;
+ case 4:
+ PACK = 4;
+ V4 = true;
+ notV5 = true;
+ obj = new ZObjectWide();
+ break;
+ case 5:
+ PACK = 4;
+ V5 = true;
+ obj = new ZObjectWide();
+ break;
+ default:
+ throw new RuntimeException("unsupported version");
+ }
+ LOG("high memory @ 0x" + HEX(U16(4)));
+ LOG("entry pc @ 0x" + HEX(U16(6))); // XXX V6
+ dict_ptr = U16(8);
+ // load seps
+ special_count = U8(dict_ptr) + 1;
+ special_chars = new int[special_count];
+ special_chars[0] = ' ';
+ for(i = 1; i < special_count; i++){
+ special_chars[i] = U8(dict_ptr + i);
+ }
+
+ dict_ptr += U8(dict_ptr) + 1;
+ dent_len = U8(dict_ptr++);
+ dent_count = U16(dict_ptr);
+ dict_ptr += 2;
+ LOG("dictionary @ 0x" + HEX(U16(8)) + ": " + dent_count + " of " + dent_len);
+
+ LOG("object table @ 0x" + HEX(U16(10)));
+ LOG("global vars @ 0x" + HEX(U16(12)));
+ global_ptr = U16(12) - 32;
+ LOG("static memory @ 0x" + HEX(U16(14)));
+ static_ptr = U16(14);
+ LOG("abbrev table @ 0x" + HEX(U16(24)));
+ abbr_ptr = U16(24);
+ default_prop_ptr = U16(10);
+ /* object table is after default props and biased for 1-based access */
+ object_ptr = default_prop_ptr + obj.PropMax * 2 - obj.Size;
+ /* bias default props for 1-based access */
+ default_prop_ptr -= 2;
+
+ backup = new byte[static_ptr];
+ System.arraycopy(mem, 0, backup, 0, static_ptr);
+
+ syncstate();
+ }
+
+ /* let the interpreter know about our configuration */
+ void syncstate() {
+ /* screen size in lines by chars */
+ mem[0x20] = (byte) scr.GetHeight();
+ mem[0x21] = (byte) scr.GetWidth();
+ /* screen size in 'units' w by h */
+ mem[0x22] = (byte) scr.GetWidth();
+ mem[0x24] = (byte) scr.GetHeight();
+ /* font size in 'units' w by h */
+ mem[0x26] = 1;
+ mem[0x27] = 1;
+
+ scr.EraseWindow(-1);
+
+ if(V3){
+ scr.SplitWindow(1);
+ }
+ }
+
+ void Println(String s) {
+ char data[] = new char[s.length()];
+ s.getChars(0, s.length(), data, 0);
+ scr.Print(data,data.length);
+ scr.PrintChar('\n');
+ System.err.println(s);
+ }
+
+ void Print(char data[], int len) {
+ if(str_display) scr.Print(data,len);
+ }
+
+ void PrintChar(int ch) {
+ if(str_display) scr.PrintChar(ch);
+ }
+
+ void PrintNumber(int n) {
+ if(str_display) scr.PrintNumber(n);
+ }
+
+ boolean str_display = true;
+
+ void SetLog(PrintStream p) {}
+
+ ZObject obj;
+
+ int static_ptr;
+ int global_ptr; /* start of globals - 16 */
+ int object_ptr; /* start of object table */
+ int default_prop_ptr;
+ int dict_ptr;
+ int abbr_ptr;
+
+ int dent_len;
+ int dent_count;
+
+ int stack[];
+ ZFrame frame, freelist;
+
+ byte[] mem;
+ byte[] backup;
+ ZScreen scr;
+
+ int PACK;
+
+ boolean V3, V4, V5, notV4, notV5;
+
+ static final boolean TRACING = false;
+ static final boolean LOGGING = false;
+
+ /* reuse zframes instead of allocating new ones, if we can */
+ static final boolean CONSERVE_MEMORY = true;
+}
diff --git a/ZRuntime.java b/ZRuntime.java
@@ -0,0 +1,29 @@
+// Z Machine V3/V4/V5 Runtime
+//
+// Copyright 2002-2003, Brian Swetland <swetland@frotz.net>
+// Available under a BSD-Style License. Share and Enjoy.
+//
+// ZWindow - Danger Hiptop Application
+// ZScreen - Interface between ZMachine and a front-end
+// ZDanger - Danger Hiptop ZMachine front-end
+// ZMachine - 'Z' Virtual Machine
+// ZRuntime - Application main class
+//
+
+package net.frotz.zruntime;
+
+import danger.app.Application;
+
+public class ZRuntime extends Application
+{
+ public ZRuntime() { }
+
+ public void resume() {
+ if(win == null){
+ win = new ZWindow(this);
+ win.show();
+ }
+ }
+
+ ZWindow win;
+}
diff --git a/ZScreen.java b/ZScreen.java
@@ -0,0 +1,58 @@
+// Z Machine V3/V4/V5 Runtime
+//
+// Copyright 2002, Brian J. Swetland <swetland@frotz.net>
+// Available under a BSD-Style License. Share and Enjoy.
+//
+// Front-end Interface.
+// The back-end in ZMachine.java is platform-agnostic
+
+package net.frotz.zruntime;
+
+public class ZScreen
+{
+ public ZScreen() {}
+
+ public void NewLine() { }
+ public void Print(char data[], int len) { }
+
+ public int Read() { return ' '; }
+ public int ReadLine(char buffer[]) { return 0; }
+
+ public void exit() { for(;;) ; }
+ public int Random(int limit) { return 0; }
+ public void SetStatus(char line[], int len) {}
+
+ public int GetWidth() { return 40; }
+ public int GetHeight() { return 15; }
+
+ public void Restart() {}
+ public boolean Save(byte state[]) { return false; }
+ public byte[] Restore() { return null; }
+
+ public void SetWindow(int num) {}
+ public void SplitWindow(int height) {}
+ public void EraseWindow(int number) {}
+ public void MoveCursor(int x, int y) {}
+
+ public void PrintNumber(int num) {
+ int i = 16;
+ int j;
+
+ do {
+ nbuf[--i] = (char) ('0' + (num % 10));
+ num = num / 10;
+ } while(num > 0);
+
+ for(j = 0; i < 16; j++){
+ nbuf[j] = nbuf[i++];
+ }
+ Print(nbuf, j);
+ }
+
+ public void PrintChar(int ch) {
+ nbuf[0] = (char) ch;
+ Print(nbuf, 1);
+ }
+
+ char nbuf[] = new char[16];
+}
diff --git a/ZWindow.java b/ZWindow.java
@@ -0,0 +1,339 @@
+// Z Machine V3/V4/V5 Runtime
+//
+// Copyright 2002-2003, Brian Swetland <swetland@frotz.net>
+// Available under a BSD-Style License. Share and Enjoy.
+//
+// Hiptop Application Main Window
+
+package net.frotz.zruntime;
+
+import danger.app.Resource;
+import danger.app.ResourceDatabase;
+import danger.app.Event;
+import danger.app.Registrar;
+import danger.app.IPCMessage;
+
+import danger.ui.ScreenWindow;
+import danger.ui.Font;
+import danger.ui.Pen;
+import danger.ui.Color;
+import danger.ui.Window;
+import danger.ui.TextField;
+import danger.ui.EditText;
+import danger.ui.PopupMenu;
+
+import danger.app.DataStore;
+
+public class ZWindow extends ScreenWindow implements Resources, Events
+{
+ public ZWindow(ZRuntime app) {
+ super("ZMachine");
+ setFullScreen(true);
+ font = Font.findFont("Fixed5x7");
+ CH = font.getAscent() + font.getDescent();
+ CA = font.getAscent();
+ CW = font.charWidth('M');
+ w = getWidth() / CW;
+ h = getHeight() / CH;
+ System.err.println("[ " + w + " x " + h + " ]");
+ scr = new ZDanger(this, w, h);
+ screen = scr.screen;
+ this.app = app;
+ rdb = app.getResources();
+ rdb.addToMenuFromResource(getActionMenu(),MENU_MAIN,this,null);
+ }
+
+ byte[] GetGameFile() {
+ int b;
+ Resource rsrc;
+ rsrc = app.getResource(1000,1000);
+
+ if(rsrc != null) {
+ b = rsrc.getSize();
+ System.err.println("Gamefile: "+b+" bytes");
+ byte[] data = new byte[b];
+ rsrc.getBytes(data, 0, b);
+ return data;
+ } else {
+ return null;
+ }
+ }
+
+ void ScrollLine(byte data[], int off) {
+ }
+
+ Window dw;
+
+ public void Email(String subject, String body) {
+ IPCMessage msg = new IPCMessage();
+ msg.addItem("action", "send");
+ msg.addItem("subject", subject);
+ msg.addItem("body", body);
+ Registrar.sendMessage("Email", msg, null);
+ }
+
+ public boolean receiveEvent(Event e) {
+ switch(e.type) {
+ case DO_VIEW_NOTEPAD:
+ if(notepad == null) {
+ notepad = rdb.getScreen(SCREEN_NOTEPAD, this);
+ }
+ dw = notepad;
+ dw.show();
+ break;
+ case DO_VIEW_TRANSCRIPT:
+ dw = rdb.getScreen(SCREEN_TRANSCRIPT,this);
+ ((EditText)dw.getDescendantWithID(ID_TEXT_TRANSCRIPT)).setText(transcript.toString());
+ dw.show();
+ break;
+ case NOTEPAD_DISMISS:
+ case TRANSCRIPT_DISMISS:
+ show();
+ dw.hide();
+ dw = null;
+ break;
+ case DO_EMAIL_NOTEPAD:
+ Email("Game Notepad",((EditText)dw.getDescendantWithID(ID_TEXT_NOTEPAD)).toString());
+ break;
+ case DO_EMAIL_TRANSCRIPT:
+ Email("Game Transcript",((EditText)dw.getDescendantWithID(ID_TEXT_TRANSCRIPT)).toString());
+ break;
+ case DO_CLEAR_TRANSCRIPT:
+ transcript.setLength(0);
+ ((EditText)dw.getDescendantWithID(ID_TEXT_TRANSCRIPT)).setText("");
+ break;
+ case DO_ABOUT:
+ (rdb.getDialog(DIALOG_ABOUT)).show();
+ break;
+ case DO_RESTORE:
+ dw = rdb.getDialog(DIALOG_RESTORE,this);
+ String[] gamelist = getSavedGames();
+ if((gamelist == null) || (gamelist.length == 0)) {
+ notifyEngine(false);
+ break;
+ } else {
+ PopupMenu pm = (PopupMenu) dw.getDescendantWithID(ID_NAME_RESTORE);
+
+ for(int i = 0; i < gamelist.length; i++) {
+ if(gamelist[i] != null) {
+ System.err.println("Game["+i+"] = '"+gamelist[i]+"'");
+ pm.addItem(gamelist[i]);
+ }
+ }
+ }
+ dw.show();
+ break;
+ case DO_SAVE:
+ dw = rdb.getDialog(DIALOG_SAVE, this);
+ dw.show();
+ break;
+ case SAVE_CANCEL:
+ case RESTORE_CANCEL:
+ notifyEngine(false);
+ break;
+ case SAVE_OK:
+ TextField tf = (TextField) dw.getDescendantWithID(ID_NAME_SAVE);
+ savegame = tf.toString();
+ notifyEngine(true);
+ break;
+ case RESTORE_OK:
+ PopupMenu pm = ((PopupMenu) dw.getDescendantWithID(ID_NAME_RESTORE));
+ selection = pm.getValue();
+ notifyEngine(true);
+ break;
+ default:
+ return super.receiveEvent(e);
+ }
+ return true;
+ }
+
+ public boolean eventKeyUp(char c, Event e) {
+ scr.KeyPress(c);
+ return true;
+ }
+
+ public boolean eventWidgetUp(int widget, Event event) {
+ switch(widget) {
+ case Event.DEVICE_BUTTON_BACK:
+ app.returnToLauncher();
+ return true;
+ default:
+ return super.eventWidgetUp(widget,event);
+ }
+ }
+ public void paint(Pen p) {
+ int i, off;
+ p.setFont(font);
+ int BG = Color.WHITE;
+ int FG = Color.BLACK;
+ int sh = scr.win1h;
+
+ p.setColor(FG);
+ p.fillRect(0,0,w * CW,sh*CH);
+
+ p.setColor(BG);
+ for(i = 0, off = 0; i < sh; i++){
+ p.drawText(2, i * CH + CA, screen, off, w);
+ off += w;
+ }
+
+ p.fillRect(0, sh*CH, w*CW, h*CH);
+ p.setColor(FG);
+ for(; i < h; i++){
+ p.drawText(2, i * CH + CA, screen, off, w);
+ off += w;
+ }
+ p.setColor(BG);
+ p.fillRect(0, i*CH, getWidth(), getHeight());
+
+ if(scr.input_active){
+ p.setColor(Color.RED);
+ int x = scr.cx * CW + 2;
+ int y = scr.cy * CH;
+ p.fillRect(x, y, x + CW, y + CH);
+ }
+ }
+
+ public String[] getSavedGames() {
+ DataStore ds = getDataStore();
+ int i, j;
+ int count = ds.getRecordCount();
+ String[] out = new String[count];
+
+ for(i = 0; i < count; i++) {
+ byte[] data = ds.getRecordData(i);
+
+ if(data[0] == 'Z' && data[1] == '!' && data[2] == '1' && data[3] == '0'){
+ for(j = 32; j < 64; j++){
+ if(data[j] == 0) break;
+ }
+ out[i] = new String(data, 32, j-32);
+ } else {
+ out[i] = null;
+ }
+ }
+ return out;
+ }
+
+ public void saveGame(String gamename, byte[] data) {
+ int i,j,count;
+
+ DataStore ds = getDataStore();
+
+ byte str[] = gamename.getBytes();
+
+ data[0] = 'Z';
+ data[1] = '!';
+ data[2] = '1';
+ data[3] = '0';
+
+ for(i = 4; i < 64; i++) data[i] = 0;
+
+ for(i = 0; i < str.length; i++){
+ if(i == 32) break;
+ data[32 + i] = str[i];
+ }
+
+ count = ds.getRecordCount();
+ for(i = 0; i < count; i++){
+ byte game[] = ds.getRecordData(i);
+ for(j = 0; j < 64; j++){
+ if(game[j] != data[j]) break;
+ }
+ if(j == 64){
+ ds.setRecordData(i, data);
+ return;
+ }
+ }
+
+ ds.addRecord(data);
+ }
+
+ String usename = "SaveGame";
+ int count = 0;
+
+ DataStore getDataStore() {
+ DataStore ds = DataStore.findDataStore("saved-games");
+ if (ds == null) {
+ return DataStore.createDataStore("saved-games", true);
+ } else {
+ return ds;
+ }
+ }
+
+ boolean waitForUI() {
+ System.err.println("WaitForUI");
+ try {
+ synchronized(notify) {
+ success = false;
+ notify.wait();
+ }
+ } catch(Throwable t) {
+ }
+ System.err.println("DoneWaiting: " +success);
+
+ return success;
+ }
+
+ boolean success;
+ String savegame;
+ int selection;
+
+ void notifyEngine(boolean status) {
+ synchronized(notify){
+ success = status;
+ notify.notify();
+ }
+ }
+
+ public boolean Save(byte[] state) {
+ sendEvent(DO_SAVE, 0, 0, null);
+
+ savegame = null;
+ if(waitForUI() && (savegame != null) && (savegame.length() > 0)){
+ saveGame(savegame, state);
+ scr.Print("Saved game as '"+savegame+"'\n");
+ }
+ return true;
+ }
+
+ public byte[] Restore() {
+ selection = -1;
+ sendEvent(DO_RESTORE, 0, 0, null);
+ if(waitForUI() && (selection != -1)){
+ return getDataStore().getRecordData(selection);
+ } else {
+ return null;
+ }
+ }
+
+ public void show() {
+ super.show();
+ if(!scr.running){
+ scr.running = true;
+ (new Thread(scr,"ZMachine")).start();
+ }
+ }
+
+ public void Transcript(char data[], int off, int len) {
+ transcript.append(data, off, len);
+ }
+
+ StringBuffer transcript = new StringBuffer(8192);
+
+ final static int DO_RESTORE = 1;
+ final static int DO_SAVE = 2;
+
+ Object notify = new Object();
+
+ Font font;
+ ZDanger scr;
+ ZRuntime app;
+ Window notepad;
+ ResourceDatabase rdb;
+ int w,h;
+ int CW, CH, CA;
+ byte screen[];
+
+}
+
diff --git a/graphics/icon16.png b/graphics/icon16.png
Binary files differ.
diff --git a/graphics/icon32.png b/graphics/icon32.png
Binary files differ.
diff --git a/graphics/splash.png b/graphics/splash.png
Binary files differ.
diff --git a/hiptop.mk b/hiptop.mk
@@ -0,0 +1,125 @@
+
+# Set this to color or gray
+#
+SCREEN ?= color
+
+# Use this line to build against the released SDK
+#
+#SDKHOME := /home/swetland/sdk-40194
+
+# Use this line to build against a check out from
+# revision control. Not useful for non-Danger people.
+#
+#TREE := /home/swetland/exp/platform
+
+
+# -----------------------------------------------------------------------
+#
+# Apps needed for the build.
+#
+MD5SUM := md5sum
+JAVAC := javac
+JAVA := java
+
+# -----------------------------------------------------------------------
+#
+# The bits from here down should not need to be modified...
+#
+#
+ifneq ($(TREE),)
+LIBRARY := $(TREE)/libdanger/classes
+TOOLS := $(TREE)/bin
+LINK := $(TREE)/target/libs/library.link
+else
+ERROR := You must set SDKHOME
+endif
+
+ifneq ($(SDKHOME),)
+TOOLS := $(SDKHOME)/tools/linux
+LIBRARY := $(SDKHOME)/libs/library.jar
+LINK := $(SDKHOME)/libs/library.link
+LIBS := $(SDKHOME)/libs
+ERROR :=
+endif
+
+MKBUNDLE := $(TOOLS)/mkbundle
+DRC := $(TOOLS)/drc
+
+ifeq ($(APPNAME),)
+ERROR := You must set APPNAME
+endif
+
+ifneq ($(ERROR),)
+all:
+ @echo "*** ERROR: $(ERROR)"
+else
+
+ifeq ($(SCREEN),color)
+SIMFLAGS := -Dcom.danger.screen.color_space=color16
+else
+SIMFLAGS := -Dcom.danger.screen.color_space=gray
+endif
+
+JFLAGS := -classpath $(LIBRARY):. -d classes
+
+BUNDLE := $(APPNAME).bndl
+RSRC_SRC := $(APPNAME).rsrc
+RSRC_DB := $(APPNAME)-$(SCREEN).rdb
+
+# SRCS must be all source files, including the INTERFACES
+# which may or may not exist in the filesystem when we
+# first do a build (because clean removes them)
+#
+SRCS := $(shell find . -name \*.java)
+SRCS := $(filter-out $(addprefix ./, $(INTERFACES)),$(SRCS))
+SRCS += $(INTERFACES)
+
+# javac likes to figure out its own depend stuff but invariably screws
+# stuff up for me -- let's just find all the java files and force a
+# rebuild if the list of files changes in any way (files added, removed,
+# etc). When we build, we nuke the classes/ directory so that we don't
+# suffer when we remove a .java file and the .class file lingers
+#
+STAMP := $(shell find . -name \*.java | $(MD5SUM) | sed 's/.\ .*//g')
+STAMPFILE := stamp.$(STAMP)
+
+all: $(STAMPFILE) $(APPNAME).bndl
+
+$(STAMPFILE): $(SRCS) $(RSRC_DB)
+ @rm -rf classes stamp.*
+ @mkdir -p classes
+ $(JAVAC) $(JFLAGS) $(SRCS)
+ $(MKBUNDLE) -o classes/application.dat $(RSRC_DB)
+ @touch $(STAMPFILE)
+
+$(INTERFACES): $(RSRC_SRC)
+ $(DRC) -i $<
+
+%-gray.rdb: %.rsrc
+ $(DRC) -i $< -o $@
+
+%-color.rdb: %.rsrc
+ $(DRC) -C -i $< -o $@
+
+ifneq ($(BUNDLENAME),)
+SIMFLAGS += -Dcom.danger.autoboot=$(BUNDLENAME)
+endif
+
+SIMCLASSPATH := -classpath $(LIBS)/simulator.jar:$(LIBS)/library.jar:classes
+
+run: $(STAMPFILE)
+ $(JAVA) $(SIMFLAGS) $(SIMCLASSPATH) danger.Boot
+
+run-gray: $(STAMPFILE)
+ $(JAVA) -Dcom.danger.screen.color_space=gray $(SIMFLAGS) $(SIMCLASSPATH) danger.Boot
+
+%.bndl: $(STAMPFILE) $(RSRC_DB)
+ $(MKBUNDLE) -o $@ $(LINK) classes $(RSRC_DB) -l $(APPNAME).lst
+ifneq ($(TREE),)
+ cp -f $@ $(TREE)/target
+endif
+
+clean::
+ rm -rf classes $(INTERFACES) *~ *.rdb *.bndl *.lst stamp.*
+
+endif
diff --git a/zmachine.rsrc b/zmachine.rsrc
@@ -0,0 +1,153 @@
+# -*- coding: utf-8 -*-
+
+package "net.frotz.zruntime"
+
+interface "Resources.java"
+events "Events.java"
+
+string ID_APP_NAME "Z Machine"
+string ID_APP_CLASS "net.frotz.zruntime.ZRuntime"
+
+data 1000 1000 "game.dat"
+
+bitmap ID_LARGE_ICON "graphics/icon32.png"
+bitmap ID_SMALL_ICON "graphics/icon16.png"
+bitmap ID_SPLASH_SCREEN "graphics/splash.png"
+
+string ID_LOCALE "en_US"
+
+menu MENU_MAIN
+ menuItem
+ title="View Transcript"
+ event=DO_VIEW_TRANSCRIPT
+ shortcut='t'
+ menuItem
+ title="View Notepad"
+ event=DO_VIEW_NOTEPAD
+ shortcut='n'
+ menuItem
+ title="About ZMachine"
+ event=DO_ABOUT
+endMenu
+
+menu MENU_TRANSCRIPT
+ menuItem
+ title="View Notepad"
+ event=DO_VIEW_NOTEPAD
+ shortcut='n'
+ menuItem
+ title="Email Transcript"
+ event=DO_EMAIL_TRANSCRIPT
+ shortcut='e'
+ menuItem
+ title="Clear Transcript"
+ event=DO_CLEAR_TRANSCRIPT
+ menuItem
+ title="Return to Game"
+ shortcut='.'
+ event=TRANSCRIPT_DISMISS
+endMenu
+
+menu MENU_NOTEPAD
+ menuItem
+ title="View Transcript"
+ event=DO_VIEW_TRANSCRIPT
+ shortcut='t'
+ menuItem
+ title="Email Notepad"
+ event=DO_EMAIL_NOTEPAD
+ shortcut='e'
+ menuItem
+ title="Return to Game"
+ shortcut='.'
+ event=NOTEPAD_DISMISS
+endMenu
+
+dialog DIALOG_RESTORE
+ title="Restore Game" centerVertically centerHorizontally
+
+ button title=okButton position=buttonBottom1
+ event=RESTORE_OK
+
+ button title=cancelButton position=buttonTop1
+ event=RESTORE_CANCEL
+
+ popupMenu alignWithParentTop=4 left=42 fillToRight=3
+ id=ID_NAME_RESTORE
+ initialFocus
+
+ staticText text="Name" font=boldSysFont
+ positionToLeft=ID_NAME_RESTORE:2
+ alignVertical=ID_NAME_RESTORE
+
+endDialog
+
+dialog DIALOG_SAVE showCancel
+ title="Save Game" centerVertically centerHorizontally
+
+ button title=okButton position=buttonBottom1
+ event=SAVE_OK
+
+ button title=cancelButton position=buttonTop1
+ event=SAVE_CANCEL
+
+ textField alignWithParentTop=4 left=42 fillToRight=3
+ id=ID_NAME_SAVE
+ justifyLeft
+ maxLength=32
+ initialFocus
+
+ staticText text="Name" font=boldSysFont
+ positionToLeft=ID_NAME_SAVE:2
+ alignVertical=ID_NAME_SAVE
+endDialog
+
+dialog DIALOG_ABOUT centerVertically centerHorizontally
+ title = "About Z Machine"
+
+ button title=okButton position=buttonBottom1
+ initialFocus
+
+ staticText
+ alignWithParentTop=2
+ text="Copyright 2001-2003, Brian Swetland"
+ left=3 fillToRight=3
+ id=ID_COPYRIGHT
+ justifyCenter
+
+ staticText
+ positionBelow=ID_COPYRIGHT:2
+ text="Share and Enjoy!"
+ left=2 fillToRight=2
+ justifyCenter
+endDialog
+
+screen SCREEN_NOTEPAD
+ actionMenu=MENU_NOTEPAD
+ title="Game Notepad"
+
+ editText
+ alignWithParentTop=0
+ initialFocus
+ id=ID_TEXT_NOTEPAD
+ left=0 fillToRight=0 fillToBottom=0
+endScreen
+
+screen SCREEN_TRANSCRIPT
+ actionMenu=MENU_TRANSCRIPT
+ title="Game Transcript"
+
+ editText
+ alignWithParentTop=0
+ initialFocus
+ id=ID_TEXT_TRANSCRIPT
+ left=0 fillToRight=0 fillToBottom=0
+endScreen
+
+#alert ALERT_OVERWRITE
+# title="Overwrite Saved Game?"
+# text="Are you sure that you want to replace this existing saved game?"
+#
+# button title=cancelButton position=buttonBottom1
+# button title=okButton position=buttonBottom2
+#endAlert