imported brians code
authoradam <adam@megacz.com>
Tue, 16 Mar 2004 02:17:57 +0000 (18:17 -0800)
committeradam <adam@megacz.com>
Tue, 16 Mar 2004 02:17:57 +0000 (18:17 -0800)
darcs-hash:20040316021757-5007d-1291b958e0af75914befcfde6fcfb47d2c6b25c3.gz

52 files changed:
Makefile [new file with mode: 0644]
doc/COPYING [new file with mode: 0644]
doc/ChangeLog [new file with mode: 0644]
src/org/xwt/mips/ClassFileCompiler.java [new file with mode: 0644]
src/org/xwt/mips/ClassLoader.java [new file with mode: 0644]
src/org/xwt/mips/Compiler.java [new file with mode: 0644]
src/org/xwt/mips/ELF.java [new file with mode: 0644]
src/org/xwt/mips/Interpreter.java [new file with mode: 0644]
src/org/xwt/mips/JavaSourceCompiler.java [new file with mode: 0644]
src/org/xwt/mips/Registers.java [new file with mode: 0644]
src/org/xwt/mips/Runtime.java [new file with mode: 0644]
src/org/xwt/mips/UnixRuntime.java [new file with mode: 0644]
src/org/xwt/mips/crt0.c [new file with mode: 0644]
src/org/xwt/mips/linker.ld [new file with mode: 0644]
src/org/xwt/mips/support.s [new file with mode: 0644]
src/org/xwt/mips/support_aux.c [new file with mode: 0644]
src/org/xwt/mips/syscalls.h [new file with mode: 0644]
src/org/xwt/mips/util/SeekableByteArray.java [new file with mode: 0644]
src/org/xwt/mips/util/SeekableData.java [new file with mode: 0644]
src/org/xwt/mips/util/SeekableFile.java [new file with mode: 0644]
src/org/xwt/mips/util/SeekableInputStream.java [new file with mode: 0644]
src/tests/CXXTest.cc [new file with mode: 0644]
src/tests/CallTest.java [new file with mode: 0644]
src/tests/Echo.java [new file with mode: 0644]
src/tests/EchoHelper.c [new file with mode: 0644]
src/tests/Env.java [new file with mode: 0644]
src/tests/FDTest.java [new file with mode: 0644]
src/tests/FTBench.c [new file with mode: 0644]
src/tests/Fork.c [new file with mode: 0644]
src/tests/FreeTypeDemo.java [new file with mode: 0644]
src/tests/FreeTypeDemoHelper.c [new file with mode: 0644]
src/tests/Hello.c [new file with mode: 0644]
src/tests/MSPack.java [new file with mode: 0644]
src/tests/MSPackBench.c [new file with mode: 0644]
src/tests/MSPackHelper.c [new file with mode: 0644]
src/tests/Paranoia.c [new file with mode: 0644]
src/tests/Simple.c [new file with mode: 0644]
src/tests/SpeedTest.java [new file with mode: 0644]
src/tests/Test.c [new file with mode: 0644]
src/tests/check.sh [new file with mode: 0755]
upstream/Makefile [new file with mode: 0644]
upstream/patches/binutils-no64.patch [new file with mode: 0644]
upstream/patches/boehm-gc.patch [new file with mode: 0644]
upstream/patches/busybox.patch [new file with mode: 0644]
upstream/patches/freetype.patch [new file with mode: 0644]
upstream/patches/ft-nostdio.patch [new file with mode: 0644]
upstream/patches/gcc-fdata-sections-bss.patch [new file with mode: 0644]
upstream/patches/gcc-fixes.patch [new file with mode: 0644]
upstream/patches/libmspack.patch [new file with mode: 0644]
upstream/patches/newlib-malloc.patch [new file with mode: 0644]
upstream/patches/newlib-mips.patch [new file with mode: 0644]
upstream/patches/newlib-tzset.patch [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..3ea8fac
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,357 @@
+# 
+# What to build
+#
+
+doc/nestedvm.ivme04.pdf: doc/nestedvm.ivme04.tex doc/acmconf.cls
+       cd doc; pdflatex nestedvm.ivme04.tex
+
+pdf: doc/nestedvm.ivme04.pdf
+       open doc/nestedvm.ivme04.pdf
+
+# Java sources that are part of the compiler/interpreter
+java_sources = $(wildcard src/org/xwt/mips/*.java) $(wildcard src/org/xwt/mips/util/*.java)
+
+# Java sources that are part of the compiler/interpreter that are
+# generated from other sources
+java_gen_sources = $(patsubst %,build/org/xwt/mips/%.java, UsermodeConstants)
+
+# C sources that are part of the compiler/interpreter
+mips_sources = crt0.c support_aux.c
+mips_asm_sources = support.s
+
+mips2java_root = $(shell pwd)
+build = $(mips2java_root)/build
+tasks = upstream/tasks
+
+#
+# MIPS Settings (don't change these)
+#
+flags = -march=mips1
+MIPS_CC = mips-unknown-elf-gcc
+MIPS_CXX = mips-unknown-elf-g++
+
+# Be VERY careful about changing any of these as they can break binary 
+# compatibility and create hard to find bugs
+mips_optflags = -O3 -g \
+       -fno-rename-registers -freduce-all-givs \
+       -fno-peephole -fno-peephole2 -fmove-all-movables -fno-schedule-insns \
+       -fno-delayed-branch \
+       -mmemcpy \
+       -ffunction-sections -fdata-sections \
+       -falign-functions=512
+
+MIPS_CFLAGS = $(mips_optflags) $(flags) -I. -Wall -Wno-unused -Werror
+MIPS_LD = mips-unknown-elf-gcc
+MIPS_LDFLAGS= \
+       $(flags) -L$(build)/org/xwt/mips --static \
+       -T $(mips2java_root)/src/org/xwt/mips/linker.ld -Wl,--gc-sections
+MIPS_STRIP = mips-unknown-elf-strip
+
+# Java compiler/VM settings
+JAVAC = javac
+JAVA = java
+ifeq ($(firstword $(JAVAC)),gcj)
+       JAVAC_NODEBUG_FLAGS = -g0
+else
+       JAVAC_NODEBUG_FLAGS = -g:none
+endif
+
+CLASSPATH = build:upstream/build/bcel/bcel-5.1.jar
+
+GCJ = gcj
+EXE_EXT = 
+
+#####
+
+java_classes = \
+       $(java_sources:src/%.java=build/%.class) \
+       $(java_gen_sources:%.java=%.class)
+
+mips_objects = $(mips_sources:%.c=build/org/xwt/mips/%.o) $(mips_asm_sources:%.s=build/org/xwt/mips/%.o)
+
+.SECONDARY:
+
+PATH := $(mips2java_root)/upstream/install/bin:$(PATH)
+export PATH
+
+#
+# General Build Stuff
+#
+all: $(java_classes) $(mips_objects)
+ifdef NATIVE_MIPS2JAVA_COMPILER
+all: build/mips2java$(EXE_EXT) $(mips_objects)
+endif
+
+$(tasks)/%:
+       $(MAKE) -C upstream tasks/$* MIPS_LDFLAGS="$(MIPS_LDFLAGS)" MIPS_CFLAGS="$(flags) $(mips_optflags)"
+
+upstream_clean_%:
+       $(MAKE) -C upstream clean_$*
+
+errno_h = upstream/install/mips-unknown-elf/include/sys/errno.h
+$(errno_h): $(tasks)/build_newlib
+
+unistd_h = upstream/install/mips-unknown-elf/include/sys/unistd.h
+$(unistd_h): $(tasks)/build_newlib
+
+#
+# Interpreter/Compiler/Runtime Java Compilation
+#
+
+# This works around a gcj -C bug
+ifeq ($(firstword $(JAVAC)),gcj)
+build/org/xwt/mips/util/.Dummy.class:
+       mkdir -p `dirname $@`
+       touch $@
+$(java_classes): build/org/xwt/mips/util/.Dummy.class
+endif
+
+$(java_classes): $(tasks)/unpack_bcel $(java_sources) $(java_gen_sources)
+       $(JAVAC) -classpath $(CLASSPATH) -d build $(java_sources) $(java_gen_sources)
+
+build/org/xwt/mips/UsermodeConstants.java: src/org/xwt/mips/syscalls.h $(errno_h) $(unistd_h)
+       @mkdir -p `dirname $@`
+       cat $^ |\
+               (FILE=org.xwt.mips.UsermodeConstants;\
+                C="`echo $$FILE | sed 's/.*\.\([^\.]*\)$$/\1/;'`";\
+                P="`echo $$FILE | sed -n 's/\(.*\)\..*/\1/p;'`";\
+                [ -n "$$P" ] && echo "package $$P;";\
+                echo "public interface $$C {";\
+                tr '\t' ' ' | sed -n ';\
+                s/  */ /g;\
+                s/ *\# *define \([A-Z_][A-Za-z0-9_]*\) \([0-9][0-9x]*\)/    public static final int \1 = \2;/p';\
+                echo "}") > $@
+
+# FIXME: We're cramming more than we need into the binary here
+build/mips2java$(EXE_EXT): $(java_sources) $(java_gen_sources)
+       @mkdir -p `dirname $@`
+       $(GCJ) -s -o $@ --main=org.xwt.mips.Compiler $(java_sources) $(java_gen_sources)
+
+#
+# MIPS Binary compilation
+#
+build/%.o: src/%.c $(tasks)/full_toolchain
+       @mkdir -p `dirname $@`
+       $(MIPS_CC) $(MIPS_CFLAGS) $($(notdir $*)_CFLAGS) -c -o $@ $<
+
+build/%.o: src/%.s $(tasks)/full_toolchain
+       @mkdir -p `dirname $@`
+       $(MIPS_CC) -x assembler-with-cpp -c -o $@ $<
+
+%.s: %.c $(tasks)/full_toolchain
+       $(MIPS_CC) $(MIPS_CFLAGS) $($(notdir $*)_CFLAGS) -c -S -o $@ $<
+
+build/%.mips: build/%.o $(mips_objects)
+       $(MIPS_LD) -o $@ $< $(MIPS_LDFLAGS) $($(notdir $*)_LDFLAGS)
+
+build/%.mips: src/%.cc $(tasks)/full_toolchain $(mips_objects)
+       @mkdir -p `dirname $@`
+       $(MIPS_CXX) $(MIPS_CFLAGS) $($(notdir $*)_CFLAGS) $(MIPS_LDFLAGS) $($(notdir $*)_LDFLAGS) -o $@ $<
+
+build/%.mips.stripped: build/%.mips
+       cp $< $@
+       $(MIPS_STRIP) -s $@
+
+# MIPS Compiler generated class compilation
+ifdef DO_JAVASOURCE
+
+build/%.java: build/%.mips build/org/xwt/mips/JavaSourceCompiler.class $(tasks)/unpack_bcel 
+       $(JAVA) -cp $(CLASSPATH) org.xwt.mips.Compiler -outformat javasource $(compiler_flags) $($(notdir $*)_COMPILERFLAGS) $(subst /,.,$*) $< > build/$*.java
+
+build/%.class: build/%.java build/org/xwt/mips/Runtime.class
+       $(JAVAC) $(JAVAC_NODEBUG_FLAGS) -classpath build -d build $<
+else
+
+build/%.class: build/%.mips build/org/xwt/mips/ClassFileCompiler.class $(tasks)/unpack_bcel 
+       $(JAVA) -cp $(CLASSPATH) org.xwt.mips.Compiler -outformat class -outfile $@ $(compiler_flags) $($(notdir $*)_COMPILERFLAGS) $(subst /,.,$*) $<
+
+
+endif
+
+# General Java Class compilation
+build/%.class: src/%.java
+       $(JAVAC) -classpath build -d build $<
+
+clean:
+       rm -rf build/tests build/org/xwt/mips *.jar build/mips2java$(EXE_EXT)
+
+#
+# env.sh
+#
+env.sh: Makefile $(tasks)/full_toolchain build/org/xwt/mips/Compiler.class $(tasks)/unpack_bcel 
+       @rm -f "$@~"
+       @echo 'PATH="$(mips2java_root)/build:$(mips2java_root)/upstream/install/bin:$$PATH"; export PATH' >> $@~
+       @echo 'CC=mips-unknown-elf-gcc; export CC' >> $@~
+       @echo 'CXX=mips-unknown-elf-g++; export CXX' >> $@~
+       @echo 'AS=mips-unknown-elf-as; export AS' >> $@~
+       @echo 'LD=mips-unknown-elf-ar; export LD' >> $@~
+       @echo 'RANLIB=mips-unknown-elf-ranlib; export RANLIB' >> $@~
+       @echo 'CFLAGS="$(mips_optflags)"; export CFLAGS' >> $@~
+       @echo 'CXXFLAGS="$(mips_optflags)"; export CXXFLAGS' >> $@~
+       @echo 'LDFLAGS="$(MIPS_LDFLAGS)"; export LDFLAGS' >> $@~
+       @echo 'CLASSPATH=$(mips2java_root)/build:$(mips2java_root)/upstream/build/bcel/bcel-5.1.jar:.; export CLASSPATH' >> $@~
+       @mv "$@~" "$@"
+       @echo "$@ created successfully"
+
+#
+# Runtime.jar
+#
+
+runtime_util_classes = SeekableData SeekableByteArray SeekableFile SeekableInputStream
+runtime_classes = Runtime Registers UsermodeConstants  $(runtime_util_classes:%=util/%)
+unixruntime_classes = $(runtime_classes) UnixRuntime
+
+runtime.jar: $(runtime_classes:%=build/org/xwt/mips/%.class)
+       cd build && jar cf ../$@ $(runtime_classes:%=org/xwt/mips/%*.class)
+
+unixruntime.jar: $(unixruntime_classes:%=build/org/xwt/mips/%.class)
+       cd build && jar cf ../$@ $(unixruntime_classes:%=org/xwt/mips/%*.class)
+
+
+
+#
+# Tests
+# These are simply here for convenience. They aren't required 
+# to build or run mips2java
+#
+
+build/tests/Env.class: build/org/xwt/mips/Runtime.class build/org/xwt/mips/Interpreter.class
+
+# Generic Hello Worldish test
+test: build/tests/Test.class
+       $(JAVA) -cp build tests.Test "arg 1" "arg 2" "arg 3"
+inttest: build/tests/Test.mips build/org/xwt/mips/Interpreter.class
+       $(JAVA) -cp build org.xwt.mips.Interpreter build/tests/Test.mips "arg 1" "arg 2" "arg 3"
+cxxtest: build/tests/CXXTest.class
+       $(JAVA) -cp build tests.CXXTest
+
+# CallTest
+build/tests/CallTest.class: build/tests/Test.class
+calltest: build/tests/CallTest.class
+       $(JAVA) -cp build tests.CallTest `date|perl -pe 's/\D+/ /g;'` `id -u`
+
+# FDTest
+build/tests/FDTest.class: build/tests/Test.class
+fdtest: build/tests/FDTest.class
+       $(JAVA) -cp build tests.FDTest
+
+
+# Simple
+Simple_LDFLAGS = -nostdlib
+simpletest: build/tests/Simple.class
+       $(JAVA) -cp build tests.Simple
+
+# Paranoia
+Paranoia_CFLAGS = "-Wno-error"
+Paranoia_LDFLAGS = -lm
+paranoiatest: build/tests/Paranoia.class
+       $(JAVA) -cp build tests.Paranoia
+
+#
+# Freetype Stuff
+#
+FreeType_CFLAGS = -Iupstream/build/freetype/include
+FreeType_LDFLAGS =  -Lupstream/build/freetype/objs -lfreetype
+
+FreeTypeDemoHelper_CFLAGS = $(FreeType_CFLAGS)
+FreeTypeDemoHelper_LDFLAGS = $(FreeType_LDFLAGS)
+build/tests/FreeTypeDemoHelper.o: $(mips_objects) $(tasks)/build_freetype
+build/tests/FreeTypeDemoHelper.mips: 
+build/tests/FreeTypeDemo.class: build/tests/FreeTypeDemoHelper.class
+
+FTBench_CFLAGS =  $(FreeType_CFLAGS)
+FTBench_LDFLAGS = $(FreeType_LDFLAGS)
+build/tests/FTBench.o: $(tasks)/build_freetype
+
+#
+# MSPack Stuff
+#
+MSPackHelper_CFLAGS = -Iupstream/build/libmspack/mspack
+MSPackHelper_LDFLAGS = -Lupstream/build/libmspack/mspack -lmspack
+build/tests/MSPackHelper.o: $(mips_objects) $(tasks)/build_libmspack
+build/tests/MSPack.class: build/tests/MSPackHelper.class
+
+MSPackBench_CFLAGS = -Iupstream/build/libmspack/mspack
+MSPackBench_LDFLAGS = -Lupstream/build/libmspack/mspack -lmspack
+build/tests/MSPackBench.o: $(tasks)/build_libmspack
+
+#
+# Echo
+#
+build/tests/Echo.class: build/tests/EchoHelper.class
+
+#
+# Libjpeg
+#
+DJpeg_COMPILERFLAGS = -o onepage,pagesize=8m
+build/tests/DJpeg.mips: $(mips_objects) $(tasks)/build_libjpeg
+       @mkdir -p `dirname $@`
+       cp upstream/build/libjpeg/djpeg $@
+
+#
+# Busybox
+#
+BusyBox_COMPILERFLAGS = -o unixruntime
+build/tests/BusyBox.mips: $(mips_object) $(tasks)/build_busybox
+       @mkdir -p `dirname $@`
+       cp upstream/build/busybox/busybox $@
+
+busyboxtest: build/tests/BusyBox.class
+       $(JAVA) -cp build tests.BusyBox ash
+
+#
+# Boehm GC
+#
+build/tests/GCTest.mips: $(mips_objects) $(tasks)/build_boehmgc
+       @mkdir -p `dirname $@`
+       cp upstream/build/boehmgc/gctest $@
+
+boehmgctest: build/tests/Env.class build/tests/GCTest.class
+       $(JAVA) -cp build tests.Env GC_PRINT_STATS=1  tests.GCTest
+
+
+#
+# Speed tests
+#
+
+build/tests/SpeedTest.class: build/org/xwt/mips/Runtime.class
+
+tmp/thebride_1280.jpg:
+       @mkdir -p tmp
+       cd tmp && wget http://www.kill-bill.com/images/wallpaper/thebride_1280.jpg
+
+oldspeedtest: build/tests/DJpeg.class tmp/thebride_1280.jpg
+       bash -c "time $(JAVA) -cp build tests.DJpeg -targa -outfile tmp/thebride_1280.tga tmp/thebride_1280.jpg"
+       @echo "e90f6b915aee2fc0d2eb9fc60ace6203  tmp/thebride_1280.tga" | md5sum -c && echo "MD5 is OK"
+
+speedtest: build/tests/SpeedTest.class build/tests/DJpeg.class build/tests/FTBench.class tmp/thebride_1280.jpg build/tests/MSPackBench.class
+       @echo "Running DJpeg test..."
+       @$(JAVA) -cp build tests.SpeedTest tests.DJpeg 10 -targa -outfile tmp/thebride_1280.tga tmp/thebride_1280.jpg
+       @if [ -e tmp/mspack/Comic.TTF ]; then \
+               echo "Running FTBench test..."; \
+               $(JAVA) -cp build tests.SpeedTest tests.FTBench 10 tmp/mspack/Comic.TTF tmp/mspack/Comic.TTF.render; \
+       else \
+               echo "Run \"make check\" to get Arial.TTF for the FTBench test"; \
+       fi
+       @if [ -e tmp/mspack/comic32.exe ]; then \
+               echo "Running MSPackBench test..."; \
+               cd tmp/mspack && $(JAVA) -cp ../../build tests.SpeedTest tests.MSPackBench 10 *32.exe; \
+       else \
+               echo "Run \"make check\" to get the MS True Type fonts for the MSPackBench test"; \
+       fi
+
+intspeed: build/tests/DJpeg.mips  build/org/xwt/mips/Interpreter.class tmp/thebride_1280.jpg
+       time $(JAVA) -cp build org.xwt.mips.Interpreter build/tests/DJpeg.mips -targa  -outfile tmp/thebride_1280.tga tmp/thebride_1280.jpg
+       @echo "e90f6b915aee2fc0d2eb9fc60ace6203  tmp/thebride_1280.tga" | md5sum -c && echo "MD5 is OK"
+
+#
+# Verification checks
+#
+
+check: $(patsubst %,build/tests/%.class, FTBench MSPackBench DJpeg GCTest) tmp/thebride_1280.jpg
+       @/bin/bash ./src/tests/check.sh running_from_make
+
+compiletests: $(patsubst %,build/tests/%.class,FTBench MSPackBench DJpeg Test FreeTypeDemoHelper MSPackHelper EchoHelper BusyBox GCTest Fork)
+       @true
+
diff --git a/doc/COPYING b/doc/COPYING
new file mode 100644 (file)
index 0000000..5b6e7c6
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/doc/ChangeLog b/doc/ChangeLog
new file mode 100644 (file)
index 0000000..e3eaaaf
--- /dev/null
@@ -0,0 +1,59 @@
+January 11, 2003
+    * [brian] Added Unistd.h as an interface so we can use its constants in
+              Runtime
+    * [brian] -o lessconstants option to compiler to emit all constants as
+              an addition of a field value and a small constant. This
+              prevents the classfile constant pool from overflowing on large
+              applications. This has a small performance impact. 
+    * [brian] Added stubs for the rmdir syscall and a minimal implementation
+              of sysconf
+    * [brian] Emit data segments as a series of <32k strings to avoid
+              hitting classfile limits
+January 10, 2003
+    * [brian] The compiler now outputs data segments as Strings rather than
+              huge int arrays.
+    * [brian] Cleaned up Makefile and upstream/Makefile to separate the 
+              required mips2java stuff from the tests. Ideally the tests
+              should be completelly separate from the main makefile but
+              keeping them integrated allows for better dependency checking
+              and faster builds.
+January 8, 2003
+    * Much thanks goes to Mohan Embar for discovering and fixing most
+      of the issues below.
+    * [brian] Updated Makefile to allow bulding with gcj
+              (make JAVAC="gcj -C" JAVA="gij")
+    * [brian] Updated Runtime.java to silently ignore SecurityExceptions
+              thrown from System.getProperty()
+    * [brian] Compiler now uses a PrintWriter for output rather than
+              PrintStream and properly closes the file on exit
+    * [brian] Made the htojava script a bourne shell script rather than a
+              perl script to remove the dependency on perl.
+December 29, 2003
+    * [brian] Changed build process to use -ffunction-sections, 
+              -fdata-sections, and --gc-sections
+    * [brian] Update support.s to use function sections
+    * [brian] Make the interpreter report the pc/sourceline of 
+              exceptions.
+    * [brian] Remove stdio calls from FreeTypeHelper
+    * [brian] Added gcc patch to fix -fdata-sections bug
+    * [brian] Added freetype patch to avoid stdio
+December 2, 2003
+    * [brian] Cleaned up a bunch of //FEATUREs and //FIXMEs. No major
+              changes, just cleanup.
+November 30, 2003
+    * [brian] Added crude support for O_APPEND and O_TRUNC
+
+November 29, 2003
+    * [brian] Moved all sources under org/xwt/mips - modified Makefiles
+              accordingly.
+    * [brian] Added org.xwt.mips.util tree containing SeekableData, 
+              SeekableByteArray, SeekableFile, and SeekableInputStream. 
+    * [brian] Made ELF.java use SeekableData rather than RandomAccessFile
+    * [brian] Made Runtime.java use org.xwt.mips.util.Seekable* for most
+              file operations.
+    * [brian] Added construtors to Interpreter.java that accept an InputStream
+              or SeekableData.
+    * [brian] Added support for the /dev/fd/n namespace and /dev/{null,zero}
+    * [brian] Added test/FDTest.java example and updated tests/Test.c
+    * [brian] Fixed absolute pathame bug on Win32
+    
\ No newline at end of file
diff --git a/src/org/xwt/mips/ClassFileCompiler.java b/src/org/xwt/mips/ClassFileCompiler.java
new file mode 100644 (file)
index 0000000..6619777
--- /dev/null
@@ -0,0 +1,2160 @@
+package org.xwt.mips;
+
+import java.io.*;
+import org.xwt.mips.util.*;
+
+import org.apache.bcel.generic.*;
+
+// FEATURE: Use BCEL to do peephole optimization
+// FEATURE: Special mode to support single-precision only - regs are floats not ints
+
+/* FEATURE: Span large binaries across several classfiles
+ * We should be able to do this with no performance penalty
+ * Every method in the inner classes is static and takes the main class as an arg
+ * This makes them look just like methods in the main class because arg1 gets loaded into
+ * local register 0
+ */
+
+/* FEATURE: smarter with local regs
+ * Be even smarter with the use of local registers. We need to only load fields into
+ * local regs when they are actually used and only write to fields if the regs could have
+ * changed. This should allow us to put more regs in local vars. Right now putting all used 
+ * regs local vars makes code like this slower.
+ * 
+ * void work(int a, int b) {
+ *    if(a == b) {
+ *       fast path
+ *       return;
+ *    }
+ *    real work
+ * }
+ * Because all the regs used in "real work" are loaded/restored even for fast path
+ */
+
+
+public class ClassFileCompiler extends Compiler implements org.apache.bcel.Constants  {    
+    /** The stream to write the compiled output to */
+    private OutputStream os;
+    private PrintStream warn = System.err;
+    
+    private ClassGen cl;
+    private ConstantPoolGen cp;
+    private InstructionList clinitExtras = new InstructionList();
+    private InstructionList initExtras = new InstructionList();
+    private InstructionFactory fac; 
+    
+    // Handy wrappers around the BCEL functions
+    private InstructionList insnList;
+    private void selectMethod(MethodGen m) { insnList = m.getInstructionList();    }
+    private void selectList(InstructionList l) { insnList = l; }
+    private InstructionHandle a(Instruction i) { return insnList.append(i); }
+    private BranchHandle a(BranchInstruction i) { return insnList.append(i); }
+    private InstructionHandle a(InstructionList l) { return insnList.append(l); }
+    private InstructionHandle a(CompoundInstruction c) { return insnList.append(c); }
+    
+    // This works around a bug in InstructionList
+    // FEATURE: fix this in bcel, send them a patch, etc
+    private static class FixedInstructionList extends InstructionList {
+        public void move(InstructionHandle start, InstructionHandle end, InstructionHandle target) {
+            InstructionHandle extra = target != null && target == getEnd() ? append(InstructionConstants.NOP) : null;
+            super.move(start,end,target);
+            if(extra != null) try { delete(extra); } catch (TargetLostException e) { /* won't happen */ }
+        }
+    }
+    
+    private MethodGen newMethod(int flags, Type ret, Type[] args, String name) {
+        return new MethodGen(flags,ret,args,null,name,fullClassName,new FixedInstructionList(),cp);
+    }
+    
+    public ClassFileCompiler(String path, String className, OutputStream os) throws IOException { this(new SeekableFile(path),className,os); }
+    public ClassFileCompiler(SeekableData binary, String className, OutputStream os) throws IOException {
+        super(binary,className);
+        this.os = os;
+    }
+    
+    public void setWarnWriter(PrintStream warn) { this.warn = warn; }
+        
+    protected void _go() throws Exn, IOException {
+        if(lessConstants) throw new Exn("ClassFileCompiler doesn't support -o lessconstants");
+        if(!pruneCases) throw new Exn("-o prunecases MUST be enabled for ClassFileCompiler");
+
+        // Class
+        cl = new ClassGen(fullClassName,runtimeClass,source,ACC_SUPER|ACC_PUBLIC,null);
+        cp = cl.getConstantPool();
+        fac = new InstructionFactory(cl,cp);
+        
+        // Fields
+        cl.addField(new FieldGen(ACC_PRIVATE,Type.INT,"pc",cp).getField());
+        for(int i=1;i<32;i++)
+            cl.addField(new FieldGen(ACC_PRIVATE,Type.INT,"r"+i,cp).getField());
+        for(int i=0;i<32;i++)
+            cl.addField(new FieldGen(ACC_PRIVATE,Type.INT,"f"+i,cp).getField());
+
+        cl.addField(new FieldGen(ACC_PRIVATE,Type.INT,"hi",cp).getField());
+        cl.addField(new FieldGen(ACC_PRIVATE,Type.INT,"lo",cp).getField());
+        cl.addField(new FieldGen(ACC_PRIVATE,Type.INT,"fcsr",cp).getField());
+        
+        if(onePage) {
+            cl.addField(new FieldGen(ACC_PRIVATE|ACC_FINAL,new ArrayType(Type.INT,1),"page",cp).getField());
+
+            selectList(initExtras);
+            a(InstructionConstants.ALOAD_0);
+            a(InstructionConstants.DUP);
+            a(fac.createFieldAccess(fullClassName,"readPages",new ArrayType(Type.INT, 2), GETFIELD));
+            pushConst(0);
+            a(InstructionConstants.AALOAD);
+            a(fac.createFieldAccess(fullClassName,"page",new ArrayType(Type.INT,1), PUTFIELD));
+        }
+        
+        if(supportCall)
+            cl.addField(new FieldGen(ACC_PRIVATE|ACC_STATIC|ACC_FINAL,Type.getType("L"+hashClass.replace('.','/')+";"),"symbols",cp).getField()); 
+        
+        int highestAddr = 0;
+        
+        for(int i=0;i<elf.sheaders.length;i++) {
+            ELF.SHeader sheader = elf.sheaders[i];
+            String name = sheader.name;
+            // if this section doesn't get loaded into our address space don't worry about it
+            if(sheader.addr == 0x0) continue;
+            
+            highestAddr = Math.max(highestAddr, sheader.addr + sheader.size);
+            
+            if(name.equals(".text"))
+                emitText(sheader.addr, new DataInputStream(sheader.getInputStream()),sheader.size);
+            else if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"))
+                emitData(sheader.addr, new DataInputStream(sheader.getInputStream()), sheader.size,name.equals(".rodata")); 
+            else if(name.equals(".bss") || name.equals(".sbss"))                
+                emitBSS(sheader.addr,sheader.size);
+            else
+                throw new Exn("Unknown segment: " + name);
+        }
+        
+        ELF.SHeader text = elf.sectionWithName(".text");
+        
+        // Trampoline
+        MethodGen tramp = newMethod(ACC_PRIVATE,Type.VOID, Type.NO_ARGS, "trampoline");
+        tramp.addException("org.xwt.mips.Runtime$ExecutionException");
+        selectMethod(tramp);
+        
+        InstructionHandle start = a(InstructionConstants.ALOAD_0);
+        a(fac.createFieldAccess(fullClassName,"state",Type.INT, GETFIELD));
+        pushConst(Runtime.RUNNING);
+        BranchInstruction stateCheck =  InstructionFactory.createBranchInstruction(IF_ICMPNE,null);
+        a(stateCheck);
+        a(InstructionConstants.ALOAD_0);
+        a(InstructionConstants.ALOAD_0);
+        a(fac.createFieldAccess(fullClassName,"pc",Type.INT, GETFIELD));
+        pushConst(methodShift);
+        a(InstructionConstants.IUSHR);
+        
+        int beg = text.addr >>> methodShift;
+        int end = ((text.addr + text.size) >>> methodShift) + 1;
+
+        // This data is redundant but BCEL wants it
+        int[] matches = new int[end-beg];
+        for(int i=beg;i<end;i++)  matches[i-beg] = i;
+        TABLESWITCH ts = new TABLESWITCH(matches,new InstructionHandle[matches.length],null);
+        a(ts);
+        for(int n=beg;n<end;n++){
+            InstructionHandle h = a(fac.createInvoke(fullClassName,"run_"+toHex(n<<methodShift),Type.VOID,Type.NO_ARGS,INVOKESPECIAL));
+            a(InstructionFactory.createBranchInstruction(GOTO,start));
+            ts.setTarget(n-beg,h);
+        }
+        
+        ts.setTarget(a(InstructionConstants.POP)); // default case
+        a(fac.createNew("org.xwt.mips.Runtime$ExecutionException"));
+        a(InstructionConstants.DUP);
+        a(fac.createNew("java.lang.StringBuffer"));
+        a(InstructionConstants.DUP);
+        a(new PUSH(cp,"Jumped to invalid address in trampoline (r2: "));
+        a(fac.createInvoke("java.lang.StringBuffer","<init>",Type.VOID,new Type[]{Type.STRING},INVOKESPECIAL));
+        a(InstructionConstants.ALOAD_0);
+        a(fac.createFieldAccess(fullClassName,"r2",Type.INT, GETFIELD));
+        a(fac.createInvoke("java.lang.StringBuffer","append",Type.STRINGBUFFER,new Type[]{Type.INT},INVOKEVIRTUAL));
+        a(new PUSH(cp," pc:"));
+        a(fac.createInvoke("java.lang.StringBuffer","append",Type.STRINGBUFFER,new Type[]{Type.STRING},INVOKEVIRTUAL));
+        a(InstructionConstants.ALOAD_0);
+        a(fac.createFieldAccess(fullClassName,"pc",Type.INT, GETFIELD));
+        a(fac.createInvoke("java.lang.StringBuffer","append",Type.STRINGBUFFER,new Type[]{Type.INT},INVOKEVIRTUAL));
+        a(new PUSH(cp,')'));
+        a(fac.createInvoke("java.lang.StringBuffer","append",Type.STRINGBUFFER,new Type[]{Type.CHAR},INVOKEVIRTUAL));
+        a(fac.createInvoke("java.lang.StringBuffer","toString",Type.STRING,Type.NO_ARGS,INVOKEVIRTUAL));
+        a(fac.createInvoke("org.xwt.mips.Runtime$ExecutionException","<init>",Type.VOID,new Type[]{Type.STRING},INVOKESPECIAL));
+        a(InstructionConstants.ATHROW);
+        
+        stateCheck.setTarget(a(InstructionConstants.RETURN));
+        
+        tramp.setMaxStack();
+        tramp.setMaxLocals();
+        try {
+            cl.addMethod(tramp.getMethod());
+        } catch(ClassGenException e) {
+            e.printStackTrace(warn);
+            throw new Exn("Generation of the trampoline method failed. Try increasing maxInsnPerMethod");
+        }
+        
+        MethodGen init = newMethod(ACC_PUBLIC,Type.VOID, Type.NO_ARGS, "<init>");
+        selectMethod(init);
+        // Constructor
+        a(InstructionConstants.ALOAD_0);
+        pushConst(pageSize);
+        pushConst(totalPages);
+        pushConst(fastMem ? 0 : 1);
+        a(fac.createInvoke(runtimeClass,"<init>",Type.VOID,new Type[]{Type.INT,Type.INT,Type.BOOLEAN},INVOKESPECIAL));
+        a(InstructionConstants.ALOAD_0);
+        pushConst(gp.addr);
+        a(fac.createFieldAccess(fullClassName,"gp",Type.INT, PUTFIELD));
+        
+        a(InstructionConstants.ALOAD_0);
+        pushConst(elf.header.entry);
+        a(fac.createFieldAccess(fullClassName,"entryPoint",Type.INT, PUTFIELD));
+        
+        a(InstructionConstants.ALOAD_0);
+        pushConst(onePage ? ((highestAddr+4095)&~4095) : ((highestAddr+pageSize-1)&~(pageSize-1)));
+        a(fac.createFieldAccess(fullClassName,"brkAddr",Type.INT, PUTFIELD));
+        
+        if(userInfo != null) {
+            a(InstructionConstants.ALOAD_0);
+            pushConst(userInfo.addr);
+            a(fac.createFieldAccess(fullClassName,"userInfoBase",Type.INT, PUTFIELD));
+            a(InstructionConstants.ALOAD_0);
+            pushConst(userInfo.size);
+            a(fac.createFieldAccess(fullClassName,"userInfoSize",Type.INT, PUTFIELD));
+        }
+        a(initExtras);
+        a(InstructionConstants.ALOAD_0);
+        pushConst(Runtime.INITIALIZED);
+        a(fac.createFieldAccess(fullClassName,"state",Type.INT, PUTFIELD));
+        a(InstructionConstants.RETURN);
+        init.setMaxLocals();
+        init.setMaxStack();
+        cl.addMethod(init.getMethod());
+        
+        MethodGen clinit = newMethod(ACC_PRIVATE|ACC_STATIC,Type.VOID, Type.NO_ARGS, "<clinit>");
+        selectMethod(clinit);
+        a(clinitExtras);
+        
+        if(supportCall) {
+            a(fac.createNew(hashClass));
+            a(InstructionConstants.DUP);
+            a(InstructionConstants.DUP);
+            a(fac.createInvoke(hashClass,"<init>",Type.VOID,Type.NO_ARGS,INVOKESPECIAL));
+            a(fac.createFieldAccess(fullClassName,"symbols",Type.getType("L"+hashClass.replace('.','/')+";"), PUTSTATIC));
+            ELF.Symbol[] symbols = elf.getSymtab().symbols;
+            for(int i=0;i<symbols.length;i++) {
+                ELF.Symbol s = symbols[i];
+                if(s.type == ELF.Symbol.STT_FUNC && s.binding == ELF.Symbol.STB_GLOBAL && (s.name.equals("_call_helper") || !s.name.startsWith("_"))) {
+                    a(InstructionConstants.DUP);
+                    a(new PUSH(cp,s.name));
+                    a(fac.createNew("java.lang.Integer"));
+                    a(InstructionConstants.DUP);
+                    a(new PUSH(cp,s.addr));
+                    a(fac.createInvoke("java.lang.Integer","<init>",Type.VOID,new Type[]{Type.INT},INVOKESPECIAL));
+                    a(fac.createInvoke(hashClass,"put",Type.OBJECT,new Type[]{Type.OBJECT,Type.OBJECT},INVOKEVIRTUAL));
+                    a(InstructionConstants.POP);
+                }
+            }
+            a(InstructionConstants.POP);
+        }
+        
+        a(InstructionConstants.RETURN);
+        clinit.setMaxLocals();
+        clinit.setMaxStack();
+        cl.addMethod(clinit.getMethod());
+        
+        if(supportCall) {
+            MethodGen lookupSymbol = newMethod(ACC_PROTECTED,Type.INT,new Type[]{Type.STRING},"lookupSymbol");
+            selectMethod(lookupSymbol);
+            a(fac.createFieldAccess(fullClassName,"symbols",Type.getType("L"+hashClass.replace('.','/')+";"), GETSTATIC));
+            a(InstructionConstants.ALOAD_1);
+            a(fac.createInvoke(hashClass,"get",Type.OBJECT,new Type[]{Type.OBJECT},INVOKEVIRTUAL));
+            a(InstructionConstants.DUP);
+            BranchHandle bh = a(InstructionFactory.createBranchInstruction(IFNULL,null));
+            a(fac.createCheckCast(new ObjectType("java.lang.Integer")));
+            a(fac.createInvoke("java.lang.Integer","intValue",Type.INT,Type.NO_ARGS,INVOKEVIRTUAL));
+            a(InstructionConstants.IRETURN);
+            bh.setTarget(a(InstructionConstants.ICONST_M1));
+            a(InstructionConstants.IRETURN);     
+            lookupSymbol.setMaxLocals();
+            lookupSymbol.setMaxStack();
+            cl.addMethod(lookupSymbol.getMethod());
+        }
+        
+        MethodGen setCPUState = newMethod(ACC_PROTECTED,Type.VOID,new Type[]{Type.getType("Lorg/xwt/mips/Runtime$CPUState;")},"setCPUState");
+        selectMethod(setCPUState);
+        a(InstructionConstants.ALOAD_1);
+        a(fac.createFieldAccess("org.xwt.mips.Runtime$CPUState","r",new ArrayType(Type.INT,1),GETFIELD));
+        a(InstructionConstants.ASTORE_2);
+        for(int i=1;i<32;i++) {
+            a(InstructionConstants.ALOAD_0);
+            a(InstructionConstants.ALOAD_2);
+            pushConst(i);
+            a(InstructionConstants.IALOAD);
+            a(fac.createFieldAccess(fullClassName,"r"+i,Type.INT, PUTFIELD));
+        }
+        a(InstructionConstants.ALOAD_1);
+        a(fac.createFieldAccess("org.xwt.mips.Runtime$CPUState","f",new ArrayType(Type.INT,1),GETFIELD));
+        a(InstructionConstants.ASTORE_2);
+        for(int i=0;i<32;i++) {
+            a(InstructionConstants.ALOAD_0);
+            a(InstructionConstants.ALOAD_2);
+            pushConst(i);
+            a(InstructionConstants.IALOAD);
+            a(fac.createFieldAccess(fullClassName,"f"+i,Type.INT, PUTFIELD));
+        }
+        a(InstructionConstants.ALOAD_0);
+        a(InstructionConstants.ALOAD_1);
+        a(fac.createFieldAccess("org.xwt.mips.Runtime$CPUState","hi",Type.INT,GETFIELD));
+        a(fac.createFieldAccess(fullClassName,"hi",Type.INT, PUTFIELD));
+        a(InstructionConstants.ALOAD_0);
+        a(InstructionConstants.ALOAD_1);
+        a(fac.createFieldAccess("org.xwt.mips.Runtime$CPUState","lo",Type.INT,GETFIELD));
+        a(fac.createFieldAccess(fullClassName,"lo",Type.INT, PUTFIELD));
+        a(InstructionConstants.ALOAD_0);
+        a(InstructionConstants.ALOAD_1);
+        a(fac.createFieldAccess("org.xwt.mips.Runtime$CPUState","fcsr",Type.INT,GETFIELD));
+        a(fac.createFieldAccess(fullClassName,"fcsr",Type.INT, PUTFIELD));
+        a(InstructionConstants.ALOAD_0);
+        a(InstructionConstants.ALOAD_1);
+        a(fac.createFieldAccess("org.xwt.mips.Runtime$CPUState","pc",Type.INT,GETFIELD));
+        a(fac.createFieldAccess(fullClassName,"pc",Type.INT, PUTFIELD));
+        a(InstructionConstants.RETURN);
+        setCPUState.setMaxLocals();
+        setCPUState.setMaxStack();
+        cl.addMethod(setCPUState.getMethod());
+        
+        MethodGen getCPUState = newMethod(ACC_PROTECTED,Type.getType("Lorg/xwt/mips/Runtime$CPUState;"),Type.NO_ARGS,"getCPUState");
+        selectMethod(getCPUState);
+        a(fac.createNew("org.xwt.mips.Runtime$CPUState"));
+        a(InstructionConstants.DUP);
+        a(fac.createInvoke("org.xwt.mips.Runtime$CPUState","<init>",Type.VOID,Type.NO_ARGS,INVOKESPECIAL));
+        a(InstructionConstants.ASTORE_1);
+        
+        a(InstructionConstants.ALOAD_1);
+        a(fac.createFieldAccess("org.xwt.mips.Runtime$CPUState","r",new ArrayType(Type.INT,1),GETFIELD));
+        a(InstructionConstants.ASTORE_2);
+        for(int i=1;i<32;i++) {
+            a(InstructionConstants.ALOAD_2);
+            pushConst(i);
+            a(InstructionConstants.ALOAD_0);
+            a(fac.createFieldAccess(fullClassName,"r"+i,Type.INT, GETFIELD));
+            a(InstructionConstants.IASTORE);
+        }
+        
+        a(InstructionConstants.ALOAD_1);
+        a(fac.createFieldAccess("org.xwt.mips.Runtime$CPUState","f",new ArrayType(Type.INT,1),GETFIELD));
+        a(InstructionConstants.ASTORE_2);
+        for(int i=0;i<32;i++) {
+            a(InstructionConstants.ALOAD_2);
+            pushConst(i);
+            a(InstructionConstants.ALOAD_0);
+            a(fac.createFieldAccess(fullClassName,"f"+i,Type.INT, GETFIELD));
+            a(InstructionConstants.IASTORE);
+        }
+        a(InstructionConstants.ALOAD_1);
+        a(InstructionConstants.ALOAD_0);
+        a(fac.createFieldAccess(fullClassName,"hi",Type.INT, GETFIELD));
+        a(fac.createFieldAccess("org.xwt.mips.Runtime$CPUState","hi",Type.INT,PUTFIELD));
+        a(InstructionConstants.ALOAD_1);
+        a(InstructionConstants.ALOAD_0);
+        a(fac.createFieldAccess(fullClassName,"lo",Type.INT, GETFIELD));
+        a(fac.createFieldAccess("org.xwt.mips.Runtime$CPUState","lo",Type.INT,PUTFIELD));
+        a(InstructionConstants.ALOAD_1);
+        a(InstructionConstants.ALOAD_0);
+        a(fac.createFieldAccess(fullClassName,"fcsr",Type.INT, GETFIELD));
+        a(fac.createFieldAccess("org.xwt.mips.Runtime$CPUState","fcsr",Type.INT,PUTFIELD));
+        a(InstructionConstants.ALOAD_1);
+        a(InstructionConstants.ALOAD_0);
+        a(fac.createFieldAccess(fullClassName,"pc",Type.INT, GETFIELD));
+        a(fac.createFieldAccess("org.xwt.mips.Runtime$CPUState","pc",Type.INT,PUTFIELD));
+        
+        a(InstructionConstants.ALOAD_1);
+        a(InstructionConstants.ARETURN);
+        getCPUState.setMaxLocals();
+        getCPUState.setMaxStack();
+        cl.addMethod(getCPUState.getMethod());
+
+
+        MethodGen execute = newMethod(ACC_PROTECTED,Type.VOID,Type.NO_ARGS,"_execute");
+        selectMethod(execute);
+        a(InstructionConstants.ALOAD_0);
+        a(fac.createInvoke(fullClassName,"trampoline",Type.VOID,Type.NO_ARGS,INVOKESPECIAL));
+        a(InstructionConstants.RETURN);
+        execute.setMaxLocals();
+        execute.setMaxStack();
+        cl.addMethod(execute.getMethod());
+        
+        
+        MethodGen main = newMethod(ACC_STATIC|ACC_PUBLIC,Type.VOID,new Type[]{new ArrayType(Type.STRING,1)},"main");
+        selectMethod(main);
+        a(fac.createNew(fullClassName));
+        a(InstructionConstants.DUP);
+        a(fac.createInvoke(fullClassName,"<init>",Type.VOID,Type.NO_ARGS,INVOKESPECIAL));
+        a(new PUSH(cp,fullClassName));
+        a(InstructionConstants.ALOAD_0);
+        a(fac.createInvoke(fullClassName,"run",Type.INT,new Type[]{Type.STRING,new ArrayType(Type.STRING,1)},INVOKEVIRTUAL));
+        a(fac.createInvoke("java.lang.System","exit",Type.VOID,new Type[]{Type.INT},INVOKESTATIC));
+        a(InstructionConstants.RETURN);
+        main.setMaxLocals();
+        main.setMaxStack();
+        cl.addMethod(main.getMethod());
+        
+        cl.getJavaClass().dump(os);
+    }
+        
+    private static int initDataCount;
+    private void emitData(int addr, DataInputStream dis, int size, boolean readOnly) throws Exn,IOException {
+        if((addr&3)!=0 || (size&3)!=0) throw new Exn("Data section on weird boundaries");
+        int last = addr + size;
+        while(addr < last) {
+            int segSize = Math.min(size,28000); // must be a multiple of 56
+            StringBuffer sb = new StringBuffer();
+            for(int i=0;i<segSize;i+=7) {
+                long l = 0;
+                for(int j=0;j<7;j++) {
+                    l <<= 8;
+                    byte b = (i+j < size) ? dis.readByte() : 1;
+                    l |= (b & 0xffL);
+                }
+                for(int j=0;j<8;j++)
+                    sb.append((char) ((l>>>(7*(7-j)))&0x7f));
+            }
+            String fieldname =  "_data" + (++initDataCount);
+            cl.addField(new FieldGen(ACC_PRIVATE|ACC_STATIC|ACC_FINAL,new ArrayType(Type.INT,1),fieldname,cp).getField());
+            
+            selectList(clinitExtras);
+            a(new PUSH(cp,sb.toString()));
+            a(new PUSH(cp,segSize/4));
+            a(fac.createInvoke("org.xwt.mips.Runtime","decodeData",new ArrayType(Type.INT,1),new Type[]{Type.STRING,Type.INT},INVOKESTATIC));
+            a(fac.createPutStatic(fullClassName,fieldname,new ArrayType(Type.INT,1)));
+
+            selectList(initExtras);
+            a(InstructionConstants.ALOAD_0);
+            a(fac.createGetStatic(fullClassName,fieldname,new ArrayType(Type.INT,1)));
+            a(new PUSH(cp,addr));
+            a(new PUSH(cp,readOnly));
+            a(fac.createInvoke(fullClassName,"initPages",Type.VOID,new Type[]{new ArrayType(Type.INT,1),Type.INT,Type.BOOLEAN},INVOKEVIRTUAL));
+            
+            addr += segSize;
+            size -= segSize;
+        }
+        dis.close();
+    }
+    
+    private void emitBSS(int addr, int size) throws Exn {
+        if((addr&3)!=0) throw new Exn("BSS section on weird boundaries");
+        size = (size+3)&~3;
+        int count = size/4;
+        selectList(initExtras);
+        a(InstructionConstants.ALOAD_0);
+        a(new PUSH(cp,addr));
+        a(new PUSH(cp,count));
+        a(fac.createInvoke(fullClassName,"clearPages",Type.VOID,new Type[]{Type.INT,Type.INT},INVOKEVIRTUAL));
+    }
+    
+    // method state info
+    private boolean textDone;
+    private int startOfMethod = 0;
+    private int endOfMethod = 0;
+    private boolean unreachable = false;
+    private InstructionHandle[] jumpHandles;
+    private InstructionHandle defaultHandle;
+    private InstructionHandle returnHandle;
+    private InstructionHandle realStart;
+    private MethodGen curMethod;
+    
+    private boolean jumpable(int addr) { return jumpableAddresses.contains(new Integer(addr)); }
+    
+    private void emitText(int addr, DataInputStream dis, int size) throws Exn,IOException {
+        if(textDone) throw new Exn("Multiple text segments");
+        textDone = true;
+        
+        if((addr&3)!=0 || (size&3)!=0) throw new Exn("Section on weird boundaries");
+        int count = size/4;
+        int insn,nextInsn=-1;
+        boolean skipNext = true;
+        
+        for(int i=0;i<count;i++,addr+=4) {
+            insn = skipNext ? dis.readInt() : nextInsn;
+            nextInsn = (i == count-1) ? -1 : dis.readInt();
+            if(addr >= endOfMethod) { endMethod(addr); startMethod(addr); }
+            if(jumpHandles[(addr-startOfMethod)/4] != null) {
+                // Move the fake jump target to the current location
+                insnList.move(jumpHandles[(addr-startOfMethod)/4],insnList.getEnd());
+                unreachable = false;
+            } else if(unreachable) {
+                continue;
+            }
+            try {
+                skipNext = emitInstruction(addr,insn,nextInsn);
+            } catch(RuntimeException e) {
+                warn.println("Exception at " + toHex(addr));
+                throw e;
+            }
+            if(skipNext) { addr+=4; i++; }
+        }
+        endMethod(0);
+        dis.close();
+    }
+    
+    private void startMethod(int first) {
+        startOfMethod = first & methodMask;
+        endOfMethod = startOfMethod + maxBytesPerMethod;
+        curMethod = newMethod(ACC_PRIVATE,Type.VOID,Type.NO_ARGS,"run_" + toHex(startOfMethod));
+        selectMethod(curMethod);
+        
+        int[] buf = new int[maxBytesPerMethod/4];
+        jumpHandles = new InstructionHandle[maxBytesPerMethod/4];
+        int n=0;
+        for(int addr=first;addr<endOfMethod;addr+=4) {
+            if(jumpable(addr)) {
+                buf[n++] = addr;
+                // append NOPs for GOTO jumps (these will be moved to the correct location later)
+                jumpHandles[(addr-startOfMethod)/4] = a(InstructionConstants.NOP);
+            }
+        }
+        
+        // append NOP for default case (throw exn) (this will be moved later)
+        defaultHandle = a(InstructionConstants.NOP);
+        returnHandle = a(InstructionConstants.NOP);
+        
+        int[] matches = new int[n];
+        System.arraycopy(buf,0,matches,0,n);
+        InstructionHandle[] targets = new InstructionHandle[n];
+        for(int i=0;i<matches.length;i++)
+            targets[i] = jumpHandles[(matches[i]-startOfMethod)/4];
+        
+        
+        // First instruction of the actual method - everything above this should be removed
+        // before we get to the end
+        realStart = a(InstructionConstants.NOP);
+        
+        if(onePage) {
+            a(InstructionConstants.ALOAD_0);
+            a(fac.createFieldAccess(fullClassName,"page",new ArrayType(Type.INT,1), GETFIELD));
+            a(InstructionConstants.ASTORE_2);
+        } else {
+            a(InstructionConstants.ALOAD_0);
+            a(fac.createFieldAccess(fullClassName,"readPages",new ArrayType(Type.INT,2), GETFIELD));
+            a(InstructionConstants.ASTORE_2);
+            a(InstructionConstants.ALOAD_0);
+            a(fac.createFieldAccess(fullClassName,"writePages",new ArrayType(Type.INT,2), GETFIELD));
+            a(InstructionFactory.createStore(Type.OBJECT,3));
+        }
+        
+        LOOKUPSWITCH initialSwitch = new LOOKUPSWITCH(matches,targets,defaultHandle);
+        a(InstructionConstants.ALOAD_0);
+        a(fac.createFieldAccess(fullClassName,"pc",Type.INT, GETFIELD));
+        a(initialSwitch);     
+    }
+    
+    private void endMethod(int firstAddrOfNext) {
+        if(startOfMethod == 0) return;
+        
+        if(!unreachable) {
+            preSetPC();
+            pushConst(firstAddrOfNext);
+            setPC();
+            // mark the start of the next method as jumpable
+            jumpableAddresses.add(new Integer(firstAddrOfNext));
+        }
+        
+        insnList.move(returnHandle,insnList.getEnd());
+        fixupRegs();
+        a(InstructionConstants.RETURN);
+        
+        // move the default jump target (lookupswitch) to before the throw
+        insnList.move(defaultHandle,insnList.getEnd());
+        if(debugCompiler) {
+            a(fac.createNew("org.xwt.mips.Runtime$ExecutionException"));
+            a(InstructionConstants.DUP);
+            a(fac.createNew("java.lang.StringBuffer"));
+            a(InstructionConstants.DUP);
+            a(new PUSH(cp,"Jumped to invalid address: "));
+            a(fac.createInvoke("java.lang.StringBuffer","<init>",Type.VOID,new Type[]{Type.STRING},INVOKESPECIAL));
+            a(InstructionConstants.ALOAD_0);
+            a(fac.createFieldAccess(fullClassName,"pc",Type.INT, GETFIELD));
+            a(fac.createInvoke("java.lang.StringBuffer","append",Type.STRINGBUFFER,new Type[]{Type.INT},INVOKEVIRTUAL));
+            a(fac.createInvoke("java.lang.StringBuffer","toString",Type.STRING,Type.NO_ARGS,INVOKEVIRTUAL));
+            a(fac.createInvoke("org.xwt.mips.Runtime$ExecutionException","<init>",Type.VOID,new Type[]{Type.STRING},INVOKESPECIAL));
+            a(InstructionConstants.ATHROW);
+        } else {
+            a(fac.createNew("org.xwt.mips.Runtime$ExecutionException"));
+            a(InstructionConstants.DUP);
+            a(new PUSH(cp,"Jumped to invalid address"));
+            a(fac.createInvoke("org.xwt.mips.Runtime$ExecutionException","<init>",Type.VOID,new Type[]{Type.STRING},INVOKESPECIAL));
+            a(InstructionConstants.ATHROW);
+        }
+
+        if(insnList.getStart() != realStart) {
+            System.err.println(insnList);
+            throw new Error("A jumpHandle wasn't moved into place");
+        }
+        
+        curMethod.removeNOPs();
+        curMethod.setMaxLocals();
+        curMethod.setMaxStack();
+        
+        cl.addMethod(curMethod.getMethod());
+        
+        endOfMethod = startOfMethod = 0;
+    }
+
+
+    private void leaveMethod() {
+        a(InstructionFactory.createBranchInstruction(GOTO,returnHandle));
+    }
+
+    private void branch(int pc, int target) {
+        if((pc&methodMask) == (target&methodMask)) {
+            a(InstructionFactory.createBranchInstruction(GOTO,jumpHandles[(target-startOfMethod)/4]));
+        } else {
+            preSetPC();
+            pushConst(target);
+            setPC();
+            leaveMethod();
+        }
+    }
+    
+    // This assumes everything needed by ifInsn is already on the stack
+    private boolean doIfInstruction(short op, int pc, int target, int nextInsn) throws Exn {
+        emitInstruction(-1,nextInsn,-1); // delay slot
+        BranchHandle h;
+        IfInstruction ifInsn = (IfInstruction) InstructionFactory.createBranchInstruction(op,null);
+        if((target&methodMask) == (pc&methodMask)) {
+            h = a(ifInsn);
+            h.setTarget(jumpHandles[(target-startOfMethod)/4]);
+        } else {
+            h = a(ifInsn.negate());
+            branch(pc,target);
+            h.setTarget(a(InstructionConstants.NOP));
+        }
+        if(!jumpable(pc+4)) return true; // done - skip it
+        
+        //System.err.println("Delay slot is jumpable - This code is untested + " + toHex(nextInsn));
+        if(pc+4==endOfMethod) {
+            // the delay slot is at the start of the next method
+            jumpableAddresses.add(new Integer(pc+8)); // make the 2nd insn of the next method jumpable
+            branch(pc,pc+8); // jump over it
+            //System.err.println("delay slot: " + toHex(pc+8));
+            unreachable = true;
+            return false; // we still need to output it
+        } else {
+            //System.err.println("jumped over delay slot: " + toHex(pc+4));
+            // add another copy and jump over
+            h = a(InstructionFactory.createBranchInstruction(GOTO,null));
+            insnList.move(jumpHandles[(pc+4-startOfMethod)/4],insnList.getEnd());
+            emitInstruction(-1,nextInsn,-1); // delay slot
+            h.setTarget(a(InstructionConstants.NOP));
+            return true;
+        }
+    }
+    
+    private boolean emitInstruction(int pc, int insn, int nextInsn) throws Exn {
+        if(insn == -1) throw new Exn("insn is -1");
+        
+        int op = (insn >>> 26) & 0xff;                 // bits 26-31
+        int rs = (insn >>> 21) & 0x1f;                 // bits 21-25
+        int rt = (insn >>> 16) & 0x1f;                 // bits 16-20 
+        int ft = (insn >>> 16) & 0x1f;
+        int rd = (insn >>> 11) & 0x1f;                 // bits 11-15
+        int fs = (insn >>> 11) & 0x1f;
+        int shamt = (insn >>> 6) & 0x1f;               // bits 6-10
+        int fd = (insn >>> 6) & 0x1f;
+        int subcode = insn & 0x3f;                     // bits 0-5 
+        int breakCode = (insn >>> 6) & 0xfffff;         // bits 6-20
+
+        int jumpTarget = (insn & 0x03ffffff);          // bits 0-25
+        int unsignedImmediate = insn & 0xffff;
+        int signedImmediate = (insn << 16) >> 16;
+        int branchTarget = signedImmediate;
+
+        // temporaries
+        BranchHandle b1,b2;
+        
+        switch(op) {
+        case 0: {
+            switch(subcode) {
+            case 0: // SLL
+                if(insn == 0) break; 
+                preSetReg(R+rd);
+                pushRegWZ(R+rt);
+                pushConst(shamt);
+                a(InstructionConstants.ISHL);
+                setReg();
+                break;
+            case 2: // SRL
+                preSetReg(R+rd);
+                pushRegWZ(R+rt);
+                pushConst(shamt);
+                a(InstructionConstants.IUSHR);
+                setReg();
+                break;
+            case 3: // SRA
+                preSetReg(R+rd);
+                pushRegWZ(R+rt);
+                pushConst(shamt);
+                a(InstructionConstants.ISHR);        
+                setReg();
+                break;
+            case 4: // SLLV
+                preSetReg(R+rd);
+                pushRegWZ(R+rt);
+                pushRegWZ(R+rs);
+                a(InstructionConstants.ISHL);
+                setReg();
+                break;
+            case 6: // SRLV
+                preSetReg(R+rd);
+                pushRegWZ(R+rt);
+                pushRegWZ(R+rs);
+                a(InstructionConstants.IUSHR);
+                setReg();
+                break;
+            case 7: // SRAV
+                preSetReg(R+rd);
+                pushRegWZ(R+rt);
+                pushRegWZ(R+rs);
+                a(InstructionConstants.ISHR);
+                setReg();
+                break;
+            case 8: // JR
+                if(pc == -1) throw new Exn("pc modifying insn in delay slot");
+                emitInstruction(-1,nextInsn,-1);
+                preSetPC();
+                pushRegWZ(R+rs);
+                setPC();
+                leaveMethod();
+                unreachable = true;
+                break;
+            case 9: // JALR
+                if(pc == -1) throw new Exn("pc modifying insn in delay slot");
+                emitInstruction(-1,nextInsn,-1);
+                preSetPC();
+                pushRegWZ(R+rs);
+                setPC();
+                
+                preSetReg(R+RA);
+                pushConst(pc+8);
+                setReg();
+                leaveMethod();
+                unreachable = true;
+                break;
+            case 12: // SYSCALL
+                preSetPC();
+                pushConst(pc);
+                setPC();
+                
+                restoreChangedRegs();
+                
+                preSetReg(R+V0);
+                a(InstructionConstants.ALOAD_0);
+                pushRegZ(R+V0);
+                pushRegZ(R+A0);
+                pushRegZ(R+A1);
+                pushRegZ(R+A2);
+                pushRegZ(R+A3);
+                a(fac.createInvoke(fullClassName,"syscall",Type.INT,new Type[]{Type.INT,Type.INT,Type.INT,Type.INT,Type.INT},INVOKEVIRTUAL));
+                setReg();
+                
+                a(InstructionConstants.ALOAD_0);
+                a(fac.createFieldAccess(fullClassName,"state",Type.INT, GETFIELD));
+                pushConst(Runtime.RUNNING);
+                b1 = a(InstructionFactory.createBranchInstruction(IF_ICMPEQ,null));
+                preSetPC();
+                pushConst(pc+4);
+                setPC();
+                leaveMethod();
+                b1.setTarget(a(InstructionConstants.NOP));
+                
+                break;
+            case 13: // BREAK
+                a(fac.createNew("org.xwt.mips.Runtime$ExecutionException"));
+                a(InstructionConstants.DUP);
+                a(new PUSH(cp,"BREAK Code " + toHex(breakCode)));
+                a(fac.createInvoke("org.xwt.mips.Runtime$ExecutionException","<init>",Type.VOID,new Type[]{Type.STRING},INVOKESPECIAL));
+                a(InstructionConstants.ATHROW);
+                unreachable = true;
+                break;
+            case 16: // MFHI
+                preSetReg(R+rd);
+                pushReg(HI);
+                setReg();
+                break;
+            case 17: // MTHI
+                preSetReg(HI);
+                pushRegZ(R+rs);
+                setReg();
+                break;
+            case 18: // MFLO
+                preSetReg(R+rd);
+                pushReg(LO);
+                setReg();
+                break;
+            case 19: // MTLO
+                preSetReg(LO);
+                pushRegZ(R+rs);
+                setReg();
+                break;
+            case 24: // MULT
+                pushRegWZ(R+rs);
+                a(InstructionConstants.I2L);
+                pushRegWZ(R+rt);
+                a(InstructionConstants.I2L);
+                a(InstructionConstants.LMUL);
+                a(InstructionConstants.DUP2);
+                
+                a(InstructionConstants.L2I);
+                if(preSetReg(LO))
+                    a(InstructionConstants.SWAP);
+                setReg();
+                
+                pushConst(32);
+                a(InstructionConstants.LUSHR);
+                a(InstructionConstants.L2I);
+                if(preSetReg(HI))
+                    a(InstructionConstants.SWAP);
+                setReg();
+                
+                break;
+            case 25: // MULTU
+                pushRegWZ(R+rs);
+                a(InstructionConstants.I2L);
+                pushConst(0xffffffffL);
+                a(InstructionConstants.LAND);
+                pushRegWZ(R+rt);
+                a(InstructionConstants.I2L);
+                pushConst(0xffffffffL);
+                a(InstructionConstants.LAND);
+                a(InstructionConstants.LMUL);
+                a(InstructionConstants.DUP2);
+                
+                a(InstructionConstants.L2I);
+                if(preSetReg(LO))
+                    a(InstructionConstants.SWAP);
+                setReg();
+                
+                pushConst(32);
+                a(InstructionConstants.LUSHR);
+                a(InstructionConstants.L2I);
+                if(preSetReg(HI))
+                    a(InstructionConstants.SWAP);
+                setReg();
+                
+                break;
+            case 26: // DIV
+                pushRegWZ(R+rs);
+                pushRegWZ(R+rt);
+                a(InstructionConstants.DUP2);
+                
+                a(InstructionConstants.IDIV);
+                if(preSetReg(LO))
+                    a(InstructionConstants.SWAP);
+                setReg();
+                
+                a(InstructionConstants.IREM);
+                if(preSetReg(HI))
+                    a(InstructionConstants.SWAP);
+                setReg();
+                
+                break;
+            case 27: { // DIVU
+                pushRegWZ(R+rt);
+                a(InstructionConstants.DUP);
+                setTmp();
+                b1 = a(InstructionFactory.createBranchInstruction(IFEQ,null));
+                
+                pushRegWZ(R+rs);
+                a(InstructionConstants.I2L);
+                pushConst(0xffffffffL);
+                a(InstructionConstants.LAND);
+                a(InstructionConstants.DUP2);
+                pushTmp();
+                a(InstructionConstants.I2L);
+                pushConst(0xffffffffL);
+                
+                a(InstructionConstants.LAND);
+                a(InstructionConstants.DUP2_X2);
+                a(InstructionConstants.LDIV);
+                
+                a(InstructionConstants.L2I);
+                if(preSetReg(LO))
+                    a(InstructionConstants.SWAP);
+                setReg();
+                
+                a(InstructionConstants.LREM);
+                a(InstructionConstants.L2I);
+                if(preSetReg(HI))
+                    a(InstructionConstants.SWAP);
+                setReg();
+                
+                b1.setTarget(a(InstructionConstants.NOP));
+                
+                break;
+            }
+            case 32: // ADD
+                throw new Exn("ADD (add with oveflow trap) not suported");
+            case 33: // ADDU
+                   preSetReg(R+rd);
+                if(rt != 0 && rs != 0) {
+                    pushReg(R+rs);
+                    pushReg(R+rt);
+                    a(InstructionConstants.IADD);
+                } else if(rs != 0) {
+                    pushReg(R+rs);
+                } else {
+                    pushRegZ(R+rt);
+                }
+                setReg();
+                break;
+            case 34: // SUB
+                throw new Exn("SUB (add with oveflow trap) not suported");
+            case 35: // SUBU
+                preSetReg(R+rd);
+                if(rt != 0 && rs != 0) {
+                    pushReg(R+rs);
+                    pushReg(R+rt);
+                    a(InstructionConstants.ISUB);
+                } else if(rt != 0) {
+                    pushReg(R+rt);
+                    a(InstructionConstants.INEG);
+                } else {
+                    pushRegZ(R+rs);
+                }
+                setReg();                
+                break;
+            case 36: // AND
+                preSetReg(R+rd);
+                pushRegWZ(R+rs);
+                pushRegWZ(R+rt);
+                a(InstructionConstants.IAND);
+                setReg();
+                break;
+            case 37: // OR
+                preSetReg(R+rd);
+                pushRegWZ(R+rs);
+                pushRegWZ(R+rt);
+                a(InstructionConstants.IOR);
+                setReg();
+                break;
+            case 38: // XOR
+                preSetReg(R+rd);
+                pushRegWZ(R+rs);
+                pushRegWZ(R+rt);
+                a(InstructionConstants.IXOR);
+                setReg();
+                break;
+            case 39: // NOR
+                preSetReg(R+rd);
+                if(rs != 0 || rt != 0) {
+                    if(rs != 0 && rt != 0) {
+                        pushReg(R+rs);
+                        pushReg(R+rt);
+                        a(InstructionConstants.IOR);
+                    } else if(rs != 0) {
+                        pushReg(R+rs);
+                    } else {
+                        pushReg(R+rt);
+                    }
+                    a(InstructionConstants.ICONST_M1);
+                    a(InstructionConstants.IXOR);
+                } else {
+                    pushConst(-1);
+                }
+                setReg();
+                break;
+            case 42: // SLT
+                preSetReg(R+rd);
+                if(rs != rt) {
+                    pushRegZ(R+rs);
+                    pushRegZ(R+rt);
+                    b1 = a(InstructionFactory.createBranchInstruction(IF_ICMPLT,null));
+                    a(InstructionConstants.ICONST_0);
+                    b2 = a(InstructionFactory.createBranchInstruction(GOTO,null));
+                    b1.setTarget(a(InstructionConstants.ICONST_1));
+                    b2.setTarget(a(InstructionConstants.NOP));
+                } else {
+                    pushConst(0);
+                }
+                setReg();
+                break;
+            case 43: // SLTU
+                preSetReg(R+rd);
+                if(rs != rt) {
+                    if(rs != 0) {
+                        pushReg(R+rs);
+                        a(InstructionConstants.I2L);
+                        pushConst(0xffffffffL);
+                        a(InstructionConstants.LAND);
+                        pushReg(R+rt);
+                        a(InstructionConstants.I2L);
+                        pushConst(0xffffffffL);
+                        a(InstructionConstants.LAND);
+                        a(InstructionConstants.LCMP);
+                        b1 = a(InstructionFactory.createBranchInstruction(IFLT,null));
+                    } else {
+                        pushReg(R+rt);
+                        b1 = a(InstructionFactory.createBranchInstruction(IFNE,null));
+                    }
+                    a(InstructionConstants.ICONST_0);
+                    b2 = a(InstructionFactory.createBranchInstruction(GOTO,null));
+                    b1.setTarget(a(InstructionConstants.ICONST_1));
+                    b2.setTarget(a(InstructionConstants.NOP));
+                } else {
+                    pushConst(0);
+                }
+                setReg();
+                break;
+            default:
+                throw new RuntimeException("Illegal instruction 0/" + subcode);
+            }
+            break;
+        }
+        case 1: {
+            switch(rt) {
+            case 0: // BLTZ
+                if(pc == -1) throw new Exn("pc modifying insn in delay slot");
+                pushRegWZ(R+rs);
+                return doIfInstruction(IFLT,pc,pc+branchTarget*4+4,nextInsn);
+            case 1: // BGEZ
+                if(pc == -1) throw new Exn("pc modifying insn in delay slot");
+                pushRegWZ(R+rs);
+                return doIfInstruction(IFGE,pc,pc+branchTarget*4+4,nextInsn);
+            case 16: // BLTZAL
+                if(pc == -1) throw new Exn("pc modifying insn in delay slot");
+                pushRegWZ(R+rs);
+                b1 = a(InstructionFactory.createBranchInstruction(IFGE,null));
+                emitInstruction(-1,nextInsn,-1);
+                preSetReg(R+RA);
+                pushConst(pc+8);
+                setReg();
+                branch(pc,pc+branchTarget*4+4);
+                b1.setTarget(a(InstructionConstants.NOP));
+                break;
+            case 17: // BGEZAL
+                if(pc == -1) throw new Exn("pc modifying insn in delay slot");
+                b1 = null;
+                if(rs != 0) { // r0 is always >= 0
+                    pushRegWZ(R+rs);
+                    b1 = a(InstructionFactory.createBranchInstruction(IFLT,null));
+                }
+                emitInstruction(-1,nextInsn,-1);
+                preSetReg(R+RA);
+                pushConst(pc+8);
+                setReg();
+                branch(pc,pc+branchTarget*4+4);
+                if(b1 != null) b1.setTarget(a(InstructionConstants.NOP));
+                if(b1 == null) unreachable = true;
+                break;
+            default:
+                throw new RuntimeException("Illegal Instruction 1/" + rt);
+            }
+            break;
+        }
+        case 2: { // J
+            if(pc == -1) throw new Exn("pc modifying insn in delay slot");
+            emitInstruction(-1,nextInsn,-1);
+            branch(pc,(pc&0xf0000000)|(jumpTarget << 2));
+            unreachable = true;
+            break;
+        }
+        case 3: { // JAL
+            if(pc == -1) throw new Exn("pc modifying insn in delay slot");
+            int target = (pc&0xf0000000)|(jumpTarget << 2);
+            emitInstruction(-1,nextInsn,-1);
+            if(optimizedMemcpy && (target == memcpy || target == memset)) {
+                a(InstructionConstants.ALOAD_0);
+                pushRegZ(R+4);
+                pushRegZ(R+5);
+                pushRegZ(R+6);
+                a(fac.createInvoke(fullClassName,target==memcpy ? "memcpy" : "memset", Type.VOID, new Type[]{Type.INT,Type.INT,Type.INT},INVOKEVIRTUAL));
+                preSetReg(R+2);
+                pushReg(R+4);
+                setReg();
+                branch(pc,pc+8);
+            } else {
+                preSetReg(R+RA);
+                pushConst(pc+8);
+                setReg();
+                branch(pc, target);
+            }
+            unreachable = true;
+            break;
+        }
+        case 4: // BEQ
+            if(pc == -1) throw new Exn("pc modifying insn in delay slot");
+            if(rs == rt) {
+                emitInstruction(-1,nextInsn,-1);
+                branch(pc,pc+branchTarget*4+4);
+                unreachable = true;
+            } else if(rs == 0 || rt == 0) {
+                pushReg(rt == 0 ? R+rs : R+rt);
+                return doIfInstruction(IFEQ,pc,pc+branchTarget*4+4,nextInsn);
+            } else {
+                pushReg(R+rs);
+                pushReg(R+rt);
+                return doIfInstruction(IF_ICMPEQ,pc,pc+branchTarget*4+4,nextInsn);
+            }
+            break;
+        case 5: // BNE       
+            if(pc == -1) throw new Exn("pc modifying insn in delay slot");
+            pushRegWZ(R+rs);
+            if(rt == 0) {
+                return doIfInstruction(IFNE,pc,pc+branchTarget*4+4,nextInsn);
+            } else {
+                pushReg(R+rt);
+                return doIfInstruction(IF_ICMPNE,pc,pc+branchTarget*4+4,nextInsn);
+            }
+        case 6: //BLEZ
+            if(pc == -1) throw new Exn("pc modifying insn in delay slot");
+            pushRegWZ(R+rs);
+            return doIfInstruction(IFLE,pc,pc+branchTarget*4+4,nextInsn);
+        case 7: //BGTZ
+            if(pc == -1) throw new Exn("pc modifying insn in delay slot");
+            pushRegWZ(R+rs);
+            return doIfInstruction(IFGT,pc,pc+branchTarget*4+4,nextInsn);
+        case 8: // ADDI
+            throw new Exn("ADDI (add immediate with oveflow trap) not suported");
+        case 9: // ADDIU
+            preSetReg(R+rt);
+            addiu(rs,signedImmediate);
+            setReg();            
+            break;
+        case 10: // SLTI
+            preSetReg(R+rt);
+            pushRegWZ(R+rs);
+            pushConst(signedImmediate);
+            b1 = a(InstructionFactory.createBranchInstruction(IF_ICMPLT,null));
+            a(InstructionConstants.ICONST_0);
+            b2 = a(InstructionFactory.createBranchInstruction(GOTO,null));
+            b1.setTarget(a(InstructionConstants.ICONST_1));
+            b2.setTarget(a(InstructionConstants.NOP));
+            setReg();
+            break;
+        case 11: // SLTIU
+            preSetReg(R+rt);
+            pushRegWZ(R+rs);
+            a(InstructionConstants.I2L);
+            pushConst(0xffffffffL);
+            a(InstructionConstants.LAND);
+            pushConst((long)unsignedImmediate);
+            a(InstructionConstants.LCMP);
+            
+            b1 = a(InstructionFactory.createBranchInstruction(IFLT,null));
+            a(InstructionConstants.ICONST_0);
+            b2 = a(InstructionFactory.createBranchInstruction(GOTO,null));
+            b1.setTarget(a(InstructionConstants.ICONST_1));
+            b2.setTarget(a(InstructionConstants.NOP));
+            
+            setReg();
+            break;            
+        case 12: // ANDI
+            preSetReg(R+rt);
+            pushRegWZ(R+rs);
+            pushConst(unsignedImmediate);
+            a(InstructionConstants.IAND);
+            setReg();
+            break;
+        case 13: // ORI
+            preSetReg(R+rt);
+            if(rs != 0 && unsignedImmediate != 0) {
+                pushReg(R+rs);
+                pushConst(unsignedImmediate);
+                a(InstructionConstants.IOR);
+            } else if(rs != 0){
+                pushReg(R+rs);
+            } else {
+                pushConst(unsignedImmediate);
+            }
+            setReg();
+            break;
+        case 14: // XORI
+            preSetReg(R+rt);
+            pushRegWZ(R+rs);
+            pushConst(unsignedImmediate);
+            a(InstructionConstants.IXOR);
+            setReg();
+            break;
+        case 15: // LUI
+            preSetReg(R+rt);
+            pushConst(unsignedImmediate << 16);
+            setReg();
+            break;
+        case 16:
+            throw new Exn("TLB/Exception support not implemented");
+        case 17: { // FPU
+            switch(rs) {
+            case 0: // MFC.1
+                preSetReg(R+rt);
+                pushReg(F+rd);
+                setReg();
+                break;
+            case 2: // CFC.1
+                if(fs != 31) throw new Exn("FCR " + fs + " unavailable");
+                preSetReg(R+rt);
+                pushReg(FCSR);
+                setReg();
+                break;
+            case 4: // MTC.1
+                preSetReg(F+rd);
+                if(rt != 0)
+                    pushReg(R+rt);
+                else
+                    pushConst(0);
+                setReg();
+                break;
+            case 6: // CTC.1
+                if(fs != 31) throw new Exn("FCR " + fs + " unavailable");
+                preSetReg(FCSR);
+                pushReg(R+rt);
+                setReg();
+                break;
+            case 8: {// BC1F, BC1T
+                pushReg(FCSR);
+                pushConst(0x800000);
+                a(InstructionConstants.IAND);
+                return doIfInstruction(((insn>>>16)&1) == 0 ? IFEQ : IFNE,pc,pc+branchTarget*4+4,nextInsn);
+            }
+            case 16:
+            case 17: 
+            { // Single/Double math
+                boolean d = rs == 17;
+                switch(subcode) {
+                case 0: // ADD.X
+                    preSetDouble(F+fd,d);
+                    pushDouble(F+fs,d);
+                    pushDouble(F+ft,d);
+                    a(d ? InstructionConstants.DADD : InstructionConstants.FADD);
+                    setDouble(d);
+                    break;
+                case 1: // SUB.X
+                    preSetDouble(F+fd,d);
+                    pushDouble(F+fs,d);
+                    pushDouble(F+ft,d);
+                    a(d ? InstructionConstants.DSUB : InstructionConstants.FSUB);
+                    setDouble(d);                    
+                    break;
+                case 2: // MUL.X
+                    preSetDouble(F+fd,d);
+                    pushDouble(F+fs,d);
+                    pushDouble(F+ft,d);
+                    a(d ? InstructionConstants.DMUL : InstructionConstants.FMUL);
+                    setDouble(d);                    
+                    break;
+                case 3: // DIV.X
+                    preSetDouble(F+fd,d);
+                    pushDouble(F+fs,d);
+                    pushDouble(F+ft,d);
+                    a(d ? InstructionConstants.DDIV : InstructionConstants.FDIV);
+                    setDouble(d);                    
+                    break;
+                case 5: // ABS.X
+                    preSetDouble(F+fd,d);
+                    // NOTE: We can't use fneg/dneg here since they'll turn +0.0 into -0.0
+                    
+                    pushDouble(F+fs,d);
+                    a(d ? InstructionConstants.DUP2 : InstructionConstants.DUP);
+                    a(d ? InstructionConstants.DCONST_0 : InstructionConstants.FCONST_0);
+                    a(d ? InstructionConstants.DCMPG : InstructionConstants.FCMPG);
+                    
+                    b1 = a(InstructionFactory.createBranchInstruction(IFGT,null));
+                    a(d ? InstructionConstants.DCONST_0 : InstructionConstants.FCONST_0);
+                    if(d) {
+                        a(InstructionConstants.DUP2_X2);
+                        a(InstructionConstants.POP2);
+                    } else {
+                        a(InstructionConstants.POP);
+                    }
+                    a(InstructionConstants.DSUB);
+                    
+                    b1.setTarget(setDouble(d));
+                    
+                    break;
+                case 6: // MOV.X
+                    preSetReg(F+fd);
+                    pushReg(F+fs);
+                    setReg();
+                    
+                    preSetReg(F+fd+1);
+                    pushReg(F+fs+1);
+                    setReg();
+                    
+                    break;
+                case 7: // NEG.X
+                    preSetDouble(F+fd,d);
+                    pushDouble(F+fs,d);
+                    a(d ? InstructionConstants.DNEG : InstructionConstants.FNEG);
+                    setDouble(d);                    
+                    break;
+                case 32: // CVT.S.X
+                    preSetFloat(F+fd);
+                    pushDouble(F+fd,d);
+                    if(d) a(InstructionConstants.D2F);
+                    setFloat();
+                    break;
+                case 33: // CVT.D.X
+                    preSetDouble(F+fd);
+                    pushDouble(F+fd,d);
+                    if(!d) a(InstructionConstants.F2D);
+                    setDouble();
+                    break;
+                case 36: { // CVT.W.D
+                    int[] matches = new int[4];
+                    for(int i=0;i<4;i++) matches[i] = i;
+                    
+                    TABLESWITCH ts = new  TABLESWITCH(matches,new InstructionHandle[4], null);
+                    
+                    preSetReg(F+fd);
+                    pushDouble(F+fs,d);
+                    pushReg(FCSR);
+                    a(InstructionConstants.ICONST_3);
+                    a(InstructionConstants.IAND);
+                    a(ts);
+                    
+                    // Round towards plus infinity
+                    ts.setTarget(2,a(InstructionConstants.NOP));
+                    if(!d) a(InstructionConstants.F2D); // Ugh.. java.lang.Math doesn't have a float ceil/floor
+                    a(fac.createInvoke("java.lang.Math","ceil",Type.DOUBLE,new Type[]{Type.DOUBLE},INVOKESTATIC));
+                    if(!d) a(InstructionConstants.D2F);
+                    b1 = a(InstructionFactory.createBranchInstruction(GOTO,null));
+                    
+                    // Round to nearest
+                    ts.setTarget(0,d ? pushConst(0.5d) : pushConst(0.5f));
+                    a(d ? InstructionConstants.DADD : InstructionConstants.FADD);
+                    // fall through
+                    
+                    // Round towards minus infinity
+                    ts.setTarget(3,a(InstructionConstants.NOP));
+                    if(!d) a(InstructionConstants.F2D);
+                    a(fac.createInvoke("java.lang.Math","floor",Type.DOUBLE,new Type[]{Type.DOUBLE},INVOKESTATIC));
+                    if(!d) a(InstructionConstants.D2F);
+                    
+                    InstructionHandle h = a(d ? InstructionConstants.D2I : InstructionConstants.F2I);
+                    setReg();
+                    
+                    ts.setTarget(1,h);
+                    ts.setTarget(h);
+                    b1.setTarget(h);
+                                        
+                    break;
+                }
+                case 50: // C.EQ.D
+                case 60: // C.LT.D
+                case 62: // C.LE.D
+                    preSetReg(FCSR);
+                    pushReg(FCSR);
+                    pushConst(~0x800000);
+                    a(InstructionConstants.IAND);
+                    pushDouble(F+fs,d);
+                    pushDouble(F+ft,d);
+                    a(d ? InstructionConstants.DCMPG : InstructionConstants.FCMPG);
+                    switch(subcode) {
+                        case 50: b1 = a(InstructionFactory.createBranchInstruction(IFEQ,null)); break;
+                        case 60: b1 = a(InstructionFactory.createBranchInstruction(IFLT,null)); break;
+                        case 62: b1 = a(InstructionFactory.createBranchInstruction(IFLE,null)); break;
+                        default: b1 = null;
+                    }
+                    
+                    pushConst(0x000000);
+                    b2 = a(InstructionFactory.createBranchInstruction(GOTO,null));
+                    b1.setTarget(pushConst(0x800000));
+                    b2.setTarget(a(InstructionConstants.IOR));
+                    setReg();
+                    break;
+                default: throw new Exn("Invalid Instruction 17/" + rs + "/" + subcode);
+                }
+                break;
+            }
+            case 20: { // Integer
+                switch(subcode) {
+                case 32: // CVT.S.W
+                    preSetFloat(F+fd);
+                    pushReg(F+fs);
+                    a(InstructionConstants.I2F);
+                    setFloat();
+                    break;
+                case 33: // CVT.D.W
+                    preSetDouble(F+fd);
+                    pushReg(F+fs);
+                    a(InstructionConstants.I2D);
+                    setDouble();
+                    break;
+                default: throw new Exn("Invalid Instruction 17/" + rs + "/" + subcode);
+                }
+                break; 
+            }
+            default:
+                throw new Exn("Invalid Instruction 17/" + rs);
+            }
+            break;
+        }
+        case 18: case 19:
+            throw new Exn("coprocessor 2 and 3 instructions not available");
+        case 32: { // LB
+            preSetReg(R+rt);
+            addiu(R+rs,signedImmediate);
+            setTmp();
+            preMemRead();
+            pushTmp();
+            memRead(true);
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_M1);
+            a(InstructionConstants.IXOR);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IUSHR);
+            a(InstructionConstants.I2B);
+            setReg();
+            break; 
+        }
+        case 33: { // LH
+            preSetReg(R+rt);
+            addiu(R+rs,signedImmediate);
+            setTmp();
+            preMemRead();
+            pushTmp();
+            memRead(true);
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_M1);
+            a(InstructionConstants.IXOR);
+            a(InstructionConstants.ICONST_2);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IUSHR);
+            a(InstructionConstants.I2S);
+            setReg();            
+            break; 
+        }
+        case 34: { // LWL;
+            preSetReg(R+rt);
+            addiu(R+rs,signedImmediate);
+            setTmp(); // addr
+            
+            pushRegWZ(R+rt);
+            pushConst(0x00ffffff);
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_M1);
+            a(InstructionConstants.IXOR);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IUSHR);
+            a(InstructionConstants.IAND);
+            
+            preMemRead();
+            pushTmp();
+            memRead(true);
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IOR);
+            
+            setReg();
+            
+            break;
+            
+        }
+        case 35: // LW
+            preSetReg(R+rt);
+            memRead(R+rs,signedImmediate);
+            setReg();
+            break;
+        case 36: { // LBU
+            preSetReg(R+rt);
+            addiu(R+rs,signedImmediate);
+            setTmp();
+            preMemRead();
+            pushTmp();
+            memRead(true);
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_M1);
+            a(InstructionConstants.IXOR);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IUSHR);
+            pushConst(0xff);
+            a(InstructionConstants.IAND);
+            setReg();
+            break; 
+        }
+        case 37: { // LHU
+            preSetReg(R+rt);
+            addiu(R+rs,signedImmediate);
+            setTmp();
+            preMemRead();
+            pushTmp();
+            memRead(true);
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_M1);
+            a(InstructionConstants.IXOR);
+            a(InstructionConstants.ICONST_2);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IUSHR);
+            
+            // chars are unsigend so this works
+            a(InstructionConstants.I2C);
+            setReg();
+            break; 
+        }
+        case 38: { // LWR            
+            preSetReg(R+rt);
+            addiu(R+rs,signedImmediate);
+            setTmp(); // addr
+            
+            pushRegWZ(R+rt);
+            pushConst(0xffffff00);
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IAND);
+            
+            preMemRead();
+            pushTmp();
+            memRead(true);
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_M1);
+            a(InstructionConstants.IXOR);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IUSHR);
+            a(InstructionConstants.IOR);
+            
+            
+            setReg();
+            break;
+        }
+        case 40: { // SB            
+            addiu(R+rs,signedImmediate);
+            setTmp(); // addr
+            
+            // FEATURE: DO the preMemRead(true) thing for the rest of the S* instructions
+            preMemRead(true);
+            pushTmp();
+            memRead(true);
+            
+            pushConst(0xff000000);
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IUSHR);
+            a(InstructionConstants.ICONST_M1);
+            a(InstructionConstants.IXOR);
+            a(InstructionConstants.IAND);
+            
+            if(rt != 0) {
+                pushReg(R+rt);
+                pushConst(0xff);
+                a(InstructionConstants.IAND);
+            } else {
+                pushConst(0);
+            }
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_M1);
+            a(InstructionConstants.IXOR);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IOR);
+            
+            memWrite();
+            
+            break;
+        }
+        case 41: { // SH    
+            preMemWrite1();
+            
+            addiu(R+rs,signedImmediate);
+            
+            a(InstructionConstants.DUP);
+            setTmp(); // addr
+            
+            preMemWrite2(true);
+            
+            preMemRead();
+            pushTmp();
+            memRead(true);
+            
+            pushConst(0xffff);
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_2);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IAND);
+            
+            if(rt != 0) {
+                pushReg(R+rt);
+                pushConst(0xffff);
+                a(InstructionConstants.IAND);
+            } else {
+                pushConst(0);
+            }
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_M1);
+            a(InstructionConstants.IXOR);
+            a(InstructionConstants.ICONST_2);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IOR);
+            
+            memWrite();
+            
+            break;            
+        }
+        case 42: { // SWL
+            preMemWrite1();
+            
+            addiu(R+rs,signedImmediate);
+            a(InstructionConstants.DUP);
+            setTmp(); // addr
+
+            preMemWrite2(true);
+            
+            preMemRead();
+            pushTmp();
+            memRead(true);
+            
+            pushConst(0xffffff00);
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_M1);
+            a(InstructionConstants.IXOR);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IAND);
+            
+            pushRegWZ(R+rt);
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IUSHR);
+            a(InstructionConstants.IOR);
+            
+            memWrite();
+            break;
+            
+        }
+        case 43: // SW
+            preMemWrite1();
+            preMemWrite2(R+rs,signedImmediate);
+            pushRegZ(R+rt);
+            memWrite();
+            break;
+        case 46: { // SWR
+            preMemWrite1();
+            
+            addiu(R+rs,signedImmediate);
+            a(InstructionConstants.DUP);
+            setTmp(); // addr
+            
+            preMemWrite2(true);
+            
+            preMemRead();
+            pushTmp();
+            memRead(true);
+            
+            pushConst(0x00ffffff);
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IUSHR);
+            a(InstructionConstants.IAND);
+            
+            pushRegWZ(R+rt);
+            pushTmp();
+            
+            a(InstructionConstants.ICONST_M1);
+            a(InstructionConstants.IXOR);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.IAND);
+            a(InstructionConstants.ICONST_3);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.ISHL);
+            a(InstructionConstants.IOR);
+                    
+            memWrite();
+            break;
+        }
+        // FEATURE: This need to be atomic if we ever support threads (see SWC0/SC)
+        case 48: // LWC0/LL
+            preSetReg(R+rt);
+            memRead(R+rs,signedImmediate);
+            setReg();
+            break;
+            
+        case 49: // LWC1
+            preSetReg(F+rt);
+            memRead(R+rs,signedImmediate);
+            setReg();
+            break;
+        
+        /* FEATURE: This needs to fail (set rt to 0) if the memory location was modified
+         * between the LL and SC if we every support threads.
+         */
+        case 56: // SWC0/SC
+            preSetReg(R+rt);
+            preMemWrite1();
+            preMemWrite2(R+rs,signedImmediate);
+            pushReg(R+rt);
+            memWrite();
+            pushConst(1);
+            setReg();
+            break;
+            
+        case 57: // SWC1
+            preMemWrite1();
+            preMemWrite2(R+rs,signedImmediate);
+            pushReg(F+rt);
+            memWrite();
+            break;
+        default:
+            throw new Exn("Invalid Instruction: " + op + " at " + toHex(pc));
+        }
+        return false; 
+    }
+    
+    // Helper functions for emitText
+    
+    private static final int R = 0;
+    private static final int F = 32;
+    private static final int HI = 64;
+    private static final int LO = 65;
+    private static final int FCSR = 66;
+    private static final int REG_COUNT=67;
+        
+    private int[] regLocalMapping = new int[REG_COUNT];  
+    private int[] regLocalReadCount = new int[REG_COUNT];
+    private int[] regLocalWriteCount = new int[REG_COUNT];
+    private int nextAvailLocal; 
+    
+    private int getLocalForReg(int reg) {
+        if(regLocalMapping[reg] != 0) return regLocalMapping[reg];
+        if(nextAvailLocal == 0) nextAvailLocal = onePage ? 3 : 4;
+        regLocalMapping[reg] = nextAvailLocal++;
+        return regLocalMapping[reg];
+    }
+    
+    private void fixupRegs() {
+        InstructionHandle prev = realStart;
+        for(int i=0;i<REG_COUNT;i++) {
+            if(regLocalMapping[i] == 0) continue; 
+            
+            prev = insnList.append(prev,InstructionConstants.ALOAD_0);
+            prev = insnList.append(prev,fac.createFieldAccess(fullClassName,regField(i),Type.INT, GETFIELD));
+            prev = insnList.append(prev,InstructionFactory.createStore(Type.INT,regLocalMapping[i]));
+            
+            if(regLocalWriteCount[i] > 0) {
+                a(InstructionConstants.ALOAD_0);
+                a(InstructionFactory.createLoad(Type.INT,regLocalMapping[i]));
+                a(fac.createFieldAccess(fullClassName,regField(i),Type.INT, PUTFIELD));
+            }
+            
+            regLocalMapping[i] = regLocalReadCount[i] = regLocalWriteCount[i] = 0;
+        }
+        nextAvailLocal = 0;
+    }
+    
+    private void restoreChangedRegs() {
+        for(int i=0;i<REG_COUNT;i++) {
+            if(regLocalWriteCount[i] > 0) {
+                a(InstructionConstants.ALOAD_0);
+                a(InstructionFactory.createLoad(Type.INT,regLocalMapping[i]));
+                a(fac.createFieldAccess(fullClassName,regField(i),Type.INT, PUTFIELD));                
+            }
+        }
+    }
+    
+    private String regField(int reg) {
+        String field;
+        switch(reg) {
+            case HI: field = "hi"; break;
+            case LO: field = "lo"; break;
+            case FCSR: field = "fcsr"; break;
+            default:
+                if(reg > R && reg < R+32) field="r"+(reg-R);
+                else if(reg >= F && reg < F+32) field="f"+(reg-F);
+                else throw new IllegalArgumentException(""+reg);
+        }
+        return field;
+    }
+    
+    private boolean doLocal(int reg) { return reg == R+2 || reg == R+3 || reg == R+4 || reg == R+29; }
+    
+    private InstructionHandle pushRegWZ(int reg) {
+        if(reg == R+0) {
+            warn.println("Warning: Pushing r0!");
+            new Exception().printStackTrace(warn);
+        }
+        return pushRegZ(reg);
+    }
+    
+    private InstructionHandle pushRegZ(int reg) {
+        if(reg == R+0) return pushConst(0);
+        else return pushReg(reg);
+    }
+    
+    
+    private InstructionHandle pushReg(int reg) {
+        InstructionHandle h;
+        if(doLocal(reg)) {
+            regLocalReadCount[reg]++;
+            h = a(InstructionFactory.createLoad(Type.INT,getLocalForReg(reg)));
+        } else {
+            h = a(InstructionConstants.ALOAD_0);
+            a(fac.createFieldAccess(fullClassName,regField(reg),Type.INT, GETFIELD));
+        }
+        return h;
+    }
+    
+    private int preSetRegStackPos;
+    private int[] preSetRegStack = new int[8];
+    
+    // This can push ONE or ZERO words to the stack. If it pushed one it returns true
+    private boolean preSetReg(int reg) {
+        regField(reg); // just to check for validity
+        preSetRegStack[preSetRegStackPos] = reg;
+        preSetRegStackPos++;
+        if(doLocal(reg)) {
+            return false;
+        } else {
+            a(InstructionConstants.ALOAD_0);
+            return true;
+        }
+    }
+    
+    private InstructionHandle setReg() {
+        if(preSetRegStackPos==0) throw new RuntimeException("didn't do preSetReg");
+        preSetRegStackPos--;
+        int reg = preSetRegStack[preSetRegStackPos];
+        InstructionHandle h;
+        if(doLocal(reg)) {
+            h = a(InstructionFactory.createStore(Type.INT,getLocalForReg(reg)));
+            regLocalWriteCount[reg]++;
+        } else {
+            h = a(fac.createFieldAccess(fullClassName,regField(reg),Type.INT, PUTFIELD));
+        }
+        return h;
+    }
+    
+    private InstructionHandle preSetPC() { return a(InstructionConstants.ALOAD_0); }
+    private InstructionHandle setPC() { return a(fac.createFieldAccess(fullClassName,"pc",Type.INT, PUTFIELD)); }
+    
+    //unused - private InstructionHandle pushFloat(int reg) throws CompilationException { return pushDouble(reg,false); }
+    //unused - private InstructionHandle pushDouble(int reg) throws CompilationException { return pushDouble(reg,true); }
+    private InstructionHandle pushDouble(int reg, boolean d) throws Exn {
+        if(reg < F || reg >= F+32) throw new IllegalArgumentException(""+reg);
+        InstructionHandle h;
+        if(d) {
+            if(reg == F+31) throw new Exn("Tried to use a double in f31");
+            h = pushReg(reg+1);
+            a(InstructionConstants.I2L);
+            pushConst(32);
+            a(InstructionConstants.LSHL);
+            pushReg(reg);
+            a(InstructionConstants.I2L);
+            pushConst(0xffffffffL);
+            a(InstructionConstants.LAND);
+            a(InstructionConstants.LOR);
+            //p("invokestatic java/lang/Double/longBitsToDouble(J)D");
+            a(fac.createInvoke("java.lang.Double","longBitsToDouble",Type.DOUBLE,new Type[]{Type.LONG},INVOKESTATIC));
+        } else {
+            h = pushReg(reg);
+            a(fac.createInvoke("java.lang.Float","intBitsToFloat",Type.FLOAT,new Type[]{Type.INT},INVOKESTATIC));
+        }
+        return h;
+    }
+    
+    private void preSetFloat(int reg) { preSetDouble(reg,false); }
+    private void preSetDouble(int reg) { preSetDouble(reg,true); }
+    private void preSetDouble(int reg, boolean d) { preSetReg(reg); }
+    
+    private InstructionHandle setFloat() throws Exn { return setDouble(false); }
+    private InstructionHandle setDouble() throws Exn { return setDouble(true); }
+    private InstructionHandle setDouble(boolean d) throws Exn {
+        int reg = preSetRegStack[preSetRegStackPos-1];
+        if(reg < F || reg >= F+32) throw new IllegalArgumentException(""+reg);
+        //p("invokestatic java/lang/Double/doubleToLongBits(D)J");
+        InstructionHandle h;
+        if(d) {
+            if(reg == F+31) throw new Exn("Tried to use a double in f31");
+            h = a(fac.createInvoke("java.lang.Double","doubleToLongBits",Type.LONG,new Type[]{Type.DOUBLE},INVOKESTATIC));
+            a(InstructionConstants.DUP2);
+            pushConst(32);
+            a(InstructionConstants.LUSHR);
+            a(InstructionConstants.L2I);
+            if(preSetReg(reg+1))
+                a(InstructionConstants.SWAP);
+            setReg();
+            a(InstructionConstants.L2I);
+            setReg(); // preSetReg was already done for this by preSetDouble
+        } else {
+            h = a(fac.createInvoke("java.lang.Float","floatToRawIntBits",Type.INT,new Type[]{Type.FLOAT},INVOKESTATIC));
+            setReg();   
+        }
+        return h;
+    }
+        
+    private InstructionHandle pushConst(int n) {
+        if(n >= 0 && n <= 5) {
+            switch(n) {
+                case 0: return a(InstructionConstants.ICONST_0);
+                case 1: return a(InstructionConstants.ICONST_1);
+                case 2: return a(InstructionConstants.ICONST_2);
+                case 3: return a(InstructionConstants.ICONST_3);
+                case 4: return a(InstructionConstants.ICONST_4);
+                case 5: return a(InstructionConstants.ICONST_5);
+                default: return null;
+            }
+        } else if(n == -1) {
+            return a(InstructionConstants.ICONST_M1);
+        } else if(n >= -128 && n <= 127) {
+            return a(new BIPUSH((byte) n));
+        } else if(n >= -32768 && n <= 32767) {
+            return a(new SIPUSH((short) n));
+        } else {
+            return a(new PUSH(cp,n));
+        }
+    }
+    
+    private InstructionHandle pushConst(long l) { return a(new PUSH(cp,l)); }
+    private InstructionHandle pushConst(float f) { return a(new PUSH(cp,f)); }
+    private InstructionHandle pushConst(double d) { return a(new PUSH(cp,d)); }
+    
+    private void pushTmp() { a(InstructionConstants.ILOAD_1); }
+    private void setTmp() { a(InstructionConstants.ISTORE_1); }
+    
+    private void addiu(int reg, int offset) {
+        if(reg != R+0 && offset != 0) {
+            pushReg(reg);
+            pushConst(offset);
+            a(InstructionConstants.IADD);
+        } else if(reg != R+0) {
+            pushReg(reg);
+        } else {
+            pushConst(offset);
+        }        
+    }
+    private int memWriteStage;
+    private void preMemWrite1() {
+        if(memWriteStage!=0) throw new Error("pending preMemWrite1/2");
+        memWriteStage=1;
+        if(onePage)
+            a(InstructionConstants.ALOAD_2);
+        else if(fastMem)
+            a(InstructionFactory.createLoad(Type.OBJECT,3));
+        else
+            a(InstructionConstants.ALOAD_0);
+    }
+    
+    private void preMemWrite2(int reg, int offset) {
+        addiu(reg,offset);
+        preMemWrite2();
+    }
+    
+    private void preMemWrite2() { preMemWrite2(false); }
+    private void preMemWrite2(boolean addrInTmp) {
+        if(memWriteStage!=1) throw new Error("pending preMemWrite2 or no preMemWrite1");
+        memWriteStage=2;
+        
+        if(nullPointerCheck) {
+            a(InstructionConstants.DUP);
+            a(InstructionConstants.ALOAD_0);
+            a(InstructionConstants.SWAP);
+            a(fac.createInvoke(fullClassName,"nullPointerCheck",Type.VOID,new Type[]{Type.INT},INVOKEVIRTUAL));
+        }
+        
+        if(onePage) {
+            a(InstructionConstants.ICONST_2);
+            a(InstructionConstants.IUSHR);
+        } else if(fastMem) {
+            if(!addrInTmp)
+                a(InstructionConstants.DUP_X1);
+            pushConst(pageShift);
+            a(InstructionConstants.IUSHR);
+            a(InstructionConstants.AALOAD);
+            if(addrInTmp)
+                pushTmp();
+            else
+                a(InstructionConstants.SWAP);
+            a(InstructionConstants.ICONST_2);
+            a(InstructionConstants.IUSHR);
+            pushConst((pageSize>>2)-1);
+            a(InstructionConstants.IAND);            
+        }
+    }
+    
+    // pops an address and value off the stack, sets *addr to value
+    private void memWrite() {
+        if(memWriteStage!=2) throw new Error("didn't do preMemWrite1 or preMemWrite2");
+        memWriteStage=0;
+                
+        if(onePage) {
+            a(InstructionConstants.IASTORE);
+        } else if(fastMem) {
+            a(InstructionConstants.IASTORE);
+        } else {
+            a(fac.createInvoke(fullClassName,"unsafeMemWrite",Type.VOID,new Type[]{Type.INT,Type.INT},INVOKEVIRTUAL));
+        }
+        
+    }
+    
+    // reads the word at r[reg]+offset
+    private void memRead(int reg, int offset) {
+        preMemRead();
+        addiu(reg,offset);
+        memRead();
+    }
+    
+    private boolean didPreMemRead;
+    private boolean preMemReadDoPreWrite;
+    
+    private void preMemRead() { preMemRead(false); }
+    private void preMemRead(boolean preWrite) {
+        if(didPreMemRead) throw new Error("pending preMemRead");
+        didPreMemRead = true;
+        preMemReadDoPreWrite = preWrite;
+        if(onePage)
+            a(InstructionConstants.ALOAD_2);
+        else if(fastMem)
+            a(InstructionFactory.createLoad(Type.OBJECT,preWrite ? 3 : 2));
+        else
+            a(InstructionConstants.ALOAD_0);
+    }
+    // memRead pops an address off the stack, reads the value at that addr, and pushed the value
+    // preMemRead MUST be called BEFORE the addresses is pushed
+    private void memRead() { memRead(false); }
+    
+    private void memRead(boolean addrInTmp) {
+        if(!didPreMemRead) throw new Error("didn't do preMemRead");
+        didPreMemRead = false;
+        if(preMemReadDoPreWrite)
+            memWriteStage=2; 
+            
+        if(nullPointerCheck) {
+            a(InstructionConstants.DUP);
+            a(InstructionConstants.ALOAD_0);
+            a(InstructionConstants.SWAP);
+            a(fac.createInvoke(fullClassName,"nullPointerCheck",Type.VOID,new Type[]{Type.INT},INVOKEVIRTUAL));
+        }
+        
+        if(onePage) {
+            // p(target + "= page[(" + addr + ")>>>2];");
+            a(InstructionConstants.ICONST_2);
+            a(InstructionConstants.IUSHR);
+            if(preMemReadDoPreWrite)
+                a(InstructionConstants.DUP2);
+            a(InstructionConstants.IALOAD);
+        } else if(fastMem) {
+            //p(target  + " = readPages[("+addr+")>>>"+pageShift+"][(("+addr+")>>>2)&"+toHex((pageSize>>2)-1)+"];");
+            
+            if(!addrInTmp)
+                a(InstructionConstants.DUP_X1);
+            pushConst(pageShift);
+            a(InstructionConstants.IUSHR);
+            a(InstructionConstants.AALOAD);
+            if(addrInTmp)
+                pushTmp();
+            else
+                a(InstructionConstants.SWAP);
+            a(InstructionConstants.ICONST_2);
+            a(InstructionConstants.IUSHR);
+            pushConst((pageSize>>2)-1);
+            a(InstructionConstants.IAND);
+            if(preMemReadDoPreWrite)
+                a(InstructionConstants.DUP2);
+            a(InstructionConstants.IALOAD);
+            
+        } else {
+            if(preMemReadDoPreWrite)
+                a(InstructionConstants.DUP2);
+            a(fac.createInvoke(fullClassName,"unsafeMemWrite",Type.INT,new Type[]{Type.INT},INVOKEVIRTUAL));
+        }
+    }
+    
+    
+    // This might come in handy for something else
+    /*private boolean touchesReg(int insn, int reg) {
+        if((reg < R+0 || reg >= R+32) && reg != FCSR) throw new IllegalArgumentException(""+reg);
+        if(reg == R+0) return false; // r0 is never modified
+        int op = (insn >>> 26) & 0xff;                 // bits 26-31
+        int subcode = insn & 0x3f;                     // bits 0-5 
+        int rd = (insn >>> 11) & 0x1f;                 // bits 11-15
+        int rt = (insn >>> 16) & 0x1f;                 // bits 16-20 
+        int rs = (insn >>> 21) & 0x1f;                 // bits 21-25
+        
+        switch(op) {
+        case 0:
+            if(subcode >= 0 && subcode <= 7) return reg == R+rd; // Shift ops
+            if(subcode >= 32 && subcode <= 43) return reg == R+rd; // Other math ops 
+            if(subcode >= 24 && subcode <= 27) return reg == HI || reg == LO; // MULT/DIV
+            break;
+        case 13: return false; // BREAK
+        case 17:
+            switch(rs) {
+                case 0: return reg == R+rt; // MFC.1
+                case 2: return reg == R+rt; // CFC.1
+                case 4: return false; // MTC.1
+                case 6: return false; // CTC.1
+                case 16: // Single 
+                case 17: // Double
+                    if(subcode == 50 || subcode == 60 || subcode == 62) return reg == FCSR;
+                    return false; // everything else just touches f0-f31
+                case 20: return false; // Integer - just touches f0-f31
+            }
+            break;
+        default:
+            if(op >= 8 && op <= 15) return reg == R+rt; // XXXI instructions
+            if(op >= 40 && op <= 46) return false; // Memory WRITE ops
+            if(op == 49) return reg == F+rt; // LWC1
+            if(op == 57) return false; // SWC1
+            break;
+        }
+        warn.println("Unknown instruction in touchesReg()- assuming it modifies all regs " + op + " " + subcode);
+        new Exception().fillInStackTrace().printStackTrace(warn);
+        return true;
+    }*/
+}
diff --git a/src/org/xwt/mips/ClassLoader.java b/src/org/xwt/mips/ClassLoader.java
new file mode 100644 (file)
index 0000000..510d309
--- /dev/null
@@ -0,0 +1,44 @@
+package org.xwt.mips;
+
+import java.io.*;
+
+// FEATURE: This is just a quick hack, it is really ugly and broken
+
+public class ClassLoader extends java.lang.ClassLoader {
+    public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        Class c;
+        if(name.startsWith("mips.")) {
+            String path = name.substring(5).replace('.','/') + ".mips";
+            try {
+                ByteArrayOutputStream bos = new ByteArrayOutputStream();
+                new ClassFileCompiler(path,name,bos).go();
+                bos.close();
+                byte[] buf = bos.toByteArray();
+                c = defineClass(name,buf,0,buf.length);
+            } catch(IOException e) {
+                throw new ClassNotFoundException(name);
+            } catch(Compiler.Exn e) {
+                throw new ClassNotFoundException(e.getMessage());
+            }
+        } else {
+            c = findSystemClass(name);
+        }
+            
+        if(c == null) throw new ClassNotFoundException(name);
+        if(resolve) resolveClass(c);
+        return c;
+    }
+    
+    public Class classFromBinary(String path) throws ClassNotFoundException {
+        if(!path.endsWith(".mips")) throw new IllegalArgumentException("isn't a .mips");
+        return loadClass("mips." + path.substring(0,path.length()-5).replace('/','.'));
+    }
+    
+    public Runtime runtimeFromBinary(String path) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
+        return (Runtime) classFromBinary(path).newInstance();
+    }
+    
+    public static void main(String[] args) throws Exception {
+        System.exit(new ClassLoader().runtimeFromBinary(args[0]).run(args));
+    }
+}
diff --git a/src/org/xwt/mips/Compiler.java b/src/org/xwt/mips/Compiler.java
new file mode 100644 (file)
index 0000000..de457e7
--- /dev/null
@@ -0,0 +1,540 @@
+// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
+
+package org.xwt.mips;
+
+import java.util.*;
+import java.io.*;
+
+import org.xwt.mips.util.SeekableData;
+import org.xwt.mips.util.SeekableFile;
+
+public abstract class Compiler implements Registers {    
+    /** The ELF binary being read */
+    protected ELF elf;
+    
+    /** The name of the class beging generated */
+    protected final String fullClassName;
+    
+    /** The name of the binary this class is begin generated from */
+    protected String source = "unknown.mips.binary";
+    public void setSource(String source) { this.source = source; }
+        
+    /** Thrown when the compilation fails for some reason */
+    protected static class Exn extends Exception { public Exn(String s) { super(s); } }
+    
+    // Set this to true to enable fast memory access 
+    // When this is enabled a Java RuntimeException will be thrown when a page fault occures. When it is disabled
+    // a FaultException will be throw which is easier to catch and deal with, however. as the name implies, this is slower
+    protected boolean fastMem = true;
+    
+    // This MUST be a power of two. If it is not horrible things will happen
+    // NOTE: This value can be much higher without breaking the classfile 
+    // specs (around 1024) but Hotstop seems to do much better with smaller
+    // methods. 
+    protected int maxInsnPerMethod = 128;
+    
+    // non-configurable
+    protected int maxBytesPerMethod;
+    protected int methodMask;
+    protected int methodShift;
+    protected void maxInsnPerMethodInit() throws Exn {
+        if((maxInsnPerMethod&(maxInsnPerMethod-1)) != 0) throw new Exn("maxBytesPerMethod is not a power of two");
+        maxBytesPerMethod = maxInsnPerMethod*4;
+        methodMask = ~(maxBytesPerMethod-1);
+        while(maxBytesPerMethod>>>methodShift != 1) methodShift++;
+    }
+    
+    // True to try to determine which case statement are needed and only include them
+    protected boolean pruneCases = true;
+    
+    protected boolean assumeTailCalls = true;
+    
+    protected boolean optimizedMemcpy = true;
+    
+    // True to insert some code in the output to help diagnore compiler problems
+    protected boolean debugCompiler = false;
+    
+    // True to print various statistics about the compilation
+    protected boolean printStats = false;
+    
+    // True to generate runtime statistics that slow execution down significantly
+    protected boolean runtimeStats = false;
+    
+    protected boolean supportCall = true;
+    
+    protected boolean nullPointerCheck = false;
+    
+    protected String runtimeClass = "org.xwt.mips.Runtime";
+    
+    protected String hashClass = "java.util.Hashtable";
+    
+    protected boolean unixRuntime;
+    
+    protected boolean lessConstants = false;
+            
+    protected int pageSize = 4096;
+    protected int totalPages = 65536;
+    protected int pageShift;
+    protected boolean onePage;
+    
+    protected void pageSizeInit() throws Exn {
+        try {
+            Runtime.checkPageSize(pageSize,totalPages);
+        } catch(IllegalArgumentException e) {
+            throw new Exn(e.getMessage());
+        }
+        while(pageSize>>>pageShift != 1) pageShift++;
+    }
+    
+    /** The address of the memcpy function in the binary (used for optimizedMemcpy) */
+    protected int memcpy;
+
+    /** The address of the memset function in the binary (used for optimizedMemcpy) */
+    protected int memset;
+    
+    /** A set of all addresses that can be jumped too (only available if pruneCases == true) */
+    protected Set jumpableAddresses;
+    
+    /** Some important symbols */
+    ELF.Symbol userInfo, gp;
+    
+    private static void usage() {
+        System.err.println("Usage: java Compiler [-outfile output.java] [-o options] [-dumpoptions] <classname> <binary.mips>");
+        System.err.println("-o takes mount(8) like options and can be specified multiple times");
+        System.err.println("Available options:");
+        for(int i=0;i<options.length;i+=2)
+            System.err.print(options[i] + ": " + wrapAndIndent(options[i+1],18-2-options[i].length(),18,62));
+        System.exit(1);
+    }
+    
+    public static void main(String[] args) throws IOException {
+        String outfile = null;
+        String o = null;
+        String className = null;
+        String mipsBinaryFileName = null;
+        String outformat = null;
+        boolean dumpOptions = false;
+        int arg = 0;
+        while(args.length-arg > 0) {
+            if(args[arg].equals("-outfile")) {
+                arg++;
+                if(arg==args.length) usage();
+                outfile = args[arg];
+            } else if(args[arg].equals("-outformat")) {
+                arg++;
+                if(arg==args.length) usage();
+                outformat = args[arg];
+            } else if(args[arg].equals("-o")) {
+                arg++;
+                if(arg==args.length) usage();
+                if(o==null || o.length() == 0)
+                    o = args[arg];
+                else if(args[arg].length() != 0)
+                    o += "," + args[arg];
+            } else if(args[arg].equals("-dumpoptions")) {
+                dumpOptions = true;
+            } else if(className == null) {
+                className = args[arg];
+            } else if(mipsBinaryFileName == null) {
+                mipsBinaryFileName = args[arg];
+            } else {
+                usage();
+            }
+            arg++;
+        }
+        if(className == null || mipsBinaryFileName == null) usage();
+        
+        SeekableData mipsBinary = new SeekableFile(mipsBinaryFileName);
+        
+        Writer w = null;
+        OutputStream os = null;
+        Compiler comp = null;
+        if(outformat == null || outformat.equals("class")) {
+            if(outfile == null) {
+                System.err.println("Refusing to write a classfile to stdout - use -outfile foo.class");
+                System.exit(1);
+            }
+            os = new FileOutputStream(outfile);
+            comp = new ClassFileCompiler(mipsBinary,className,os);
+        } else if(outformat.equals("javasource") || outformat .equals("java")) {
+            w = outfile == null ? new OutputStreamWriter(System.out): new FileWriter(outfile);
+            comp = new JavaSourceCompiler(mipsBinary,className,w);
+        } else {
+            System.err.println("Unknown output format: " + outformat);
+            System.exit(1);
+        }
+        
+        comp.parseOptions(o);
+        comp.setSource(mipsBinaryFileName);
+        
+        if(dumpOptions) {
+            System.err.println("== Options ==");
+            for(int i=0;i<options.length;i+=2)
+                System.err.println(options[i] + ": " + comp.getOption(options[i]).get());
+            System.err.println("== End Options ==");
+        }
+            
+        try {
+            comp.go();
+        } catch(Exn e) {
+            System.err.println("Compiler Error: " + e.getMessage());
+            System.exit(1);
+        } finally {
+            if(w != null) w.close();
+            if(os != null) os.close();
+        }
+    }
+        
+    public Compiler(SeekableData binary, String fullClassName) throws IOException {
+        this.fullClassName = fullClassName;
+        elf = new ELF(binary);
+        
+        if(elf.header.type != ELF.ELFHeader.ET_EXEC) throw new IOException("Binary is not an executable");
+        if(elf.header.machine != ELF.ELFHeader.EM_MIPS) throw new IOException("Binary is not for the MIPS I Architecture");
+        if(elf.ident.data != ELF.ELFIdent.ELFDATA2MSB) throw new IOException("Binary is not big endian");
+    }
+
+    protected abstract void _go() throws Exn, IOException;
+    
+    private boolean used;
+    public void go() throws Exn, IOException {
+        if(used) throw new RuntimeException("Compiler instances are good for one shot only");
+        used = true;
+        
+        if(onePage && pageSize <= 4096) pageSize = 4*1024*1024;
+        if(nullPointerCheck && !fastMem) throw new Exn("fastMem must be enabled for nullPointerCheck to be of any use");
+        if(onePage && !fastMem) throw new Exn("fastMem must be enabled for onePage to be of any use");
+        if(totalPages == 1 && !onePage) throw new Exn("totalPages == 1 and onePage is not set");
+        if(onePage) totalPages = 1;
+
+        maxInsnPerMethodInit();
+        pageSizeInit();
+        
+        // Get a copy of the symbol table in the elf binary
+        ELF.Symtab symtab = elf.getSymtab();
+        if(symtab == null) throw new Exn("Binary has no symtab (did you strip it?)");
+        ELF.Symbol sym;
+        
+        // Check for some functions we can override
+        sym = symtab.getGlobalSymbol("memcpy");
+        memcpy = sym == null ? -1 : sym.addr;
+        
+        sym = symtab.getGlobalSymbol("memset");
+        memset = sym == null ? -1 : sym.addr;
+        
+        userInfo = symtab.getGlobalSymbol("user_info");
+        gp = symtab.getGlobalSymbol("_gp");
+        if(gp == null) throw new Exn("no _gp symbol (did you strip the binary?)");   
+        
+        if(pruneCases) {
+            // Find all possible branches
+            jumpableAddresses = new HashSet();
+            
+            jumpableAddresses.add(new Integer(elf.header.entry));
+            
+            ELF.SHeader text = elf.sectionWithName(".text");
+            if(text == null) throw new Exn("No .text segment");
+            
+            findBranchesInSymtab(symtab,jumpableAddresses);
+            
+            for(int i=0;i<elf.sheaders.length;i++) {
+                ELF.SHeader sheader = elf.sheaders[i];
+                String name = sheader.name;
+                // if this section doesn't get loaded into our address space don't worry about it
+                if(sheader.addr == 0x0) continue;
+                if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"))
+                    findBranchesInData(new DataInputStream(sheader.getInputStream()),sheader.size,jumpableAddresses,text.addr,text.addr+text.size);
+            }
+            
+            findBranchesInText(text.addr,new DataInputStream(text.getInputStream()),text.size,jumpableAddresses);            
+        }
+
+        if(unixRuntime && runtimeClass.startsWith("org.xwt.mips.")) runtimeClass = "org.xwt.mips.UnixRuntime";
+        
+        for(int i=0;i<elf.sheaders.length;i++) {
+            String name = elf.sheaders[i].name;
+            if(elf.sheaders[i].addr != 0 && !(
+                name.equals(".text")|| name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") ||
+                name.equals(".ctors") || name.equals(".dtors") || name.equals(".bss") || name.equals(".sbss")))
+                    throw new Exn("Unknown section: " + name);
+        }
+        _go();
+    }
+    
+    private void findBranchesInSymtab(ELF.Symtab symtab, Set jumps) {
+        ELF.Symbol[] symbols = symtab.symbols;
+        int n=0;
+        for(int i=0;i<symbols.length;i++) {
+            ELF.Symbol s = symbols[i];
+            if(s.type == ELF.Symbol.STT_FUNC) {
+                if(jumps.add(new Integer(s.addr))) {
+                    //System.err.println("Adding symbol from symtab: " + s.name + " at " + toHex(s.addr));
+                    n++;
+                }
+            }
+        }
+        if(printStats) System.err.println("Found " + n + " additional possible branch targets in Symtab");
+    }
+    
+    private void findBranchesInText(int base, DataInputStream dis, int size, Set jumps) throws IOException {
+        int count = size/4;
+        int pc = base;
+        int n=0;
+        int[] lui_val = new int[32];
+        int[] lui_pc = new int[32];
+        //Interpreter inter = new Interpreter(source);
+        
+        for(int i=0;i<count;i++,pc+=4) {
+            int insn = dis.readInt();
+            int op = (insn >>> 26) & 0xff; 
+            int rs = (insn >>> 21) & 0x1f;
+            int rt = (insn >>> 16) & 0x1f;
+            int signedImmediate = (insn << 16) >> 16;
+            int unsignedImmediate = insn & 0xffff;
+            int branchTarget = signedImmediate;
+            int jumpTarget = (insn & 0x03ffffff);
+            int subcode = insn & 0x3f;
+            
+            switch(op) {
+                case 0:
+                    switch(subcode) {
+                        case 9: // JALR
+                            if(jumps.add(new Integer(pc+8))) n++; // return address
+                            break;
+                        case 12: // SYSCALL
+                            if(jumps.add(new Integer(pc+4))) n++; 
+                            break;
+                    }
+                    break;
+                case 1:
+                    switch(rt) {
+                        case 16: // BLTZAL
+                        case 17: // BGTZAL
+                            if(jumps.add(new Integer(pc+8))) n++; // return address
+                            // fall through
+                        case 0: // BLTZ
+                        case 1: // BGEZ
+                            if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
+                            break;
+                    }
+                    break;
+                case 3: // JAL
+                    if(jumps.add(new Integer(pc+8))) n++; // return address
+                    // fall through
+                case 2: // J
+                    if(jumps.add(new Integer((pc&0xf0000000)|(jumpTarget << 2)))) n++;
+                    break;
+                case 4: // BEQ
+                case 5: // BNE
+                case 6: // BLEZ
+                case 7: // BGTZ
+                    if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
+                    break;
+                case 9: { // ADDIU
+                    if(pc - lui_pc[rs] <= 4*32) {
+                        int t = (lui_val[rs]<<16)+signedImmediate;
+                        if((t&3)==0 && t >= base && t < base+size) {
+                            if(jumps.add(new Integer(t))) {
+                                //System.err.println("Possible jump to " + toHex(t) + " (" + inter.sourceLine(t) + ") from " + toHex(pc) + " (" + inter.sourceLine(pc) + ")");
+                                n++;
+                            }
+                        }
+                        // we just blew it away
+                        if(rt == rs) lui_pc[rs] = 0;
+                    }
+                    break;
+                }
+                case 15: { // LUI
+                    lui_val[rt] = unsignedImmediate;
+                    lui_pc[rt] = pc;
+                    break;
+                }
+                    
+                case 17: // FPU Instructions
+                    switch(rs) {
+                        case 8: // BC1F, BC1T
+                            if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
+                            break;
+                    }
+                    break;
+            }
+        }
+        dis.close();
+        if(printStats) System.err.println("Found " + n + " additional possible branch targets in Text segment");
+    }
+    
+    private void findBranchesInData(DataInputStream dis, int size, Set jumps, int textStart, int textEnd) throws IOException {
+        int count = size/4;
+        int n=0;
+        for(int i=0;i<count;i++) {
+            int word = dis.readInt();
+            if((word&3)==0 && word >= textStart && word < textEnd) {
+                if(jumps.add(new Integer(word))) {
+                    //System.err.println("Added " + toHex(word) + " as possible branch target (fron data segment)");
+                    n++;
+                }
+            }
+        }
+        dis.close();
+        if(n>0 && printStats) System.err.println("Found " + n + " additional possible branch targets in Data segment");
+    }
+    
+    // Helper functions for pretty output
+    protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
+    protected final static String toHex8(int n) {
+        String s = Long.toString(n & 0xffffffffL, 16);
+        StringBuffer sb = new StringBuffer("0x");
+        for(int i=8-s.length();i>0;i--) sb.append('0');
+        sb.append(s);
+        return sb.toString();
+    }
+
+    protected final static String toOctal3(int n) {
+        char[] buf = new char[3];
+        for(int i=2;i>=0;i--) {
+            buf[i] = (char) ('0' + (n & 7));
+            n >>= 3;
+        }
+        return new String(buf);
+    }
+
+    // Option parsing
+    private class Option {
+        private java.lang.reflect.Field field;
+        public Option(String name) throws NoSuchFieldException { field = name==null ? null : Compiler.class.getDeclaredField(name); }
+        public void set(Object val) {
+            if(field == null) return;
+            try {
+                field.setAccessible(true);
+                field.set(Compiler.this,val);
+            } catch(IllegalAccessException e) {
+                System.err.println(e);
+            }
+        }
+        public Object get() {
+            if(field == null) return null;
+            try {
+                field.setAccessible(true);
+                return field.get(Compiler.this);
+            } catch(IllegalAccessException e) {
+                System.err.println(e); return null;
+            }
+        }
+        public Class getType() { return field == null ? null : field.getType(); }
+    }
+    
+    private static String[] options = {
+        "fastMem",          "Enable fast memory access - RuntimeExceptions will be thrown on faults",
+        "nullPointerCheck", "Enables checking at runtime for null pointer accessses (slows things down a bit, only applicable with fastMem)",
+        "maxInsnPerMethod", "Maximum number of MIPS instructions per java method (128 is optimal with Hotspot)",
+        "pruneCases",       "Remove unnecessary case 0xAABCCDD blocks from methods - may break some weird code",
+        "assumeTailCalls",  "Assume the JIT optimizes tail calls",
+        "optimizedMemcpy",  "Use an optimized java version of memcpy where possible",
+        "debugCompiler",    "Output information in the generated code for debugging the compiler - will slow down generated code significantly",
+        "printStats",       "Output some useful statistics about the compilation",
+        "runtimeStats",     "Keep track of some statistics at runtime in the generated code - will slow down generated code significantly",
+        "supportCall",      "Keep a stripped down version of the symbol table in the generated code to support the call() method",
+        "runtimeClass",     "Full classname of the Runtime class (default: Runtime) - use this is you put Runtime in a package",
+        "hashClass",        "Full classname of a Hashtable class (default: java.util.HashMap) - this must support get() and put()",
+        "unixRuntime",      "Use the UnixRuntime (has support for fork, wai, du, pipe, etc)",
+        "pageSize",         "The page size (must be a power of two)",
+        "totalPages",       "Total number of pages (total mem = pageSize*totalPages, must be a power of two)",
+        "onePage",          "One page hack (FIXME: document this better)",
+        "lessConstants",    "Use less constants at the cost of speed (FIXME: document this better)"
+    };
+        
+    private Option getOption(String name) {
+        name = name.toLowerCase();
+        try {
+            for(int i=0;i<options.length;i+=2)
+                if(options[i].toLowerCase().equals(name))
+                    return new Option(options[i]);
+            return null;
+        } catch(NoSuchFieldException e) {
+            return null;
+        }
+    }
+    
+    public void parseOptions(String opts) {
+        if(opts == null || opts.length() == 0) return;
+        StringTokenizer st = new StringTokenizer(opts,",");
+        while(st.hasMoreElements()) {
+            String tok = st.nextToken();
+            String key;
+            String val;
+            if(tok.indexOf("=") != -1) {
+                key = tok.substring(0,tok.indexOf("="));
+                val = tok.substring(tok.indexOf("=")+1);
+            } else if(tok.startsWith("no")) {
+                key = tok.substring(2);
+                val = "false";
+            } else {
+                key = tok;
+                val = "true";
+            }
+            Option opt = getOption(key);
+            if(opt == null) {
+                System.err.println("WARNING: No such option: " + key);
+                continue;
+            }
+            
+            if(opt.getType() == String.class)
+                opt.set(val);
+            else if(opt.getType() == Integer.TYPE)
+                try {
+                    opt.set(parseInt(val));
+                } catch(NumberFormatException e) {
+                    System.err.println("WARNING: " + val + " is not an integer");
+                }
+            else if(opt.getType() == Boolean.TYPE)
+                opt.set(new Boolean(val.toLowerCase().equals("true")||val.toLowerCase().equals("yes")));
+            else
+                throw new Error("Unknown type: " + opt.getType());
+        }
+    }
+        
+    private static Integer parseInt(String s) {
+        int mult = 1;
+        s = s.toLowerCase();
+        if(!s.startsWith("0x") && s.endsWith("m")) { s = s.substring(0,s.length()-1); mult = 1024*1024; }
+        else if(!s.startsWith("0x") && s.endsWith("k")) { s = s.substring(0,s.length()-1); mult = 1024; }
+        int n;
+        if(s.length() > 2 && s.startsWith("0x")) n = Integer.parseInt(s.substring(2),16);
+        else n = Integer.parseInt(s);
+        return new Integer(n*mult);
+    }
+    
+    private static String wrapAndIndent(String s, int firstindent, int indent, int width) {
+        StringTokenizer st = new StringTokenizer(s," ");
+        StringBuffer sb = new StringBuffer();
+        for(int i=0;i<firstindent;i++)
+            sb.append(' ');
+        int sofar = 0;
+        while(st.hasMoreTokens()) {
+            String tok = st.nextToken();
+            if(tok.length() + sofar + 1 > width && sofar > 0) {
+                sb.append('\n');
+                for(int i=0;i<indent;i++) sb.append(' ');
+                sofar = 0;
+            } else if(sofar > 0) {
+                sb.append(' ');
+                sofar++;
+            }
+            sb.append(tok);
+            sofar += tok.length();
+        }
+        sb.append('\n');
+        return sb.toString();
+    }
+    
+    // This ugliness is to work around a gcj static linking bug (Bug 12908)
+    // The best solution is to force gnu.java.locale.Calendar to be linked in but this'll do
+    protected static String dateTime() {
+        try {
+            return new Date().toString();
+        } catch(RuntimeException e) {
+            return "<unknown>";
+        }
+    }
+}
+
diff --git a/src/org/xwt/mips/ELF.java b/src/org/xwt/mips/ELF.java
new file mode 100644 (file)
index 0000000..18698f2
--- /dev/null
@@ -0,0 +1,381 @@
+package org.xwt.mips;
+
+import org.xwt.mips.util.*;
+import java.io.*;
+
+public class ELF {
+    private SeekableData data;
+    
+    public ELFIdent ident;
+    public ELFHeader header;
+    public PHeader[] pheaders;
+    public SHeader[] sheaders;
+    
+    private byte[] stringTable;
+    
+    private boolean sectionReaderActive;
+    
+    
+    private void readFully(byte[] buf) throws IOException {
+        int len = buf.length;
+        int pos = 0;
+        while(len > 0) {
+            int n = data.read(buf,pos,len);
+            if(n == -1) throw new IOException("EOF");
+            pos += n;
+            len -= n;
+        }
+    }
+    
+    private int readIntBE() throws IOException {
+        byte[] buf = new byte[4];
+        readFully(buf);
+        return ((buf[0]&0xff)<<24)|((buf[1]&0xff)<<16)|((buf[2]&0xff)<<8)|((buf[3]&0xff)<<0);
+    }
+    private int readInt() throws IOException {
+        int x = readIntBE();
+        if(ident!=null && ident.data == ELFIdent.ELFDATA2LSB) 
+            x = ((x<<24)&0xff000000) | ((x<<8)&0xff0000) | ((x>>>8)&0xff00) | ((x>>24)&0xff);
+        return x;
+    }
+    
+    private short readShortBE() throws IOException {
+        byte[] buf = new byte[2];
+        readFully(buf);
+        return (short)(((buf[0]&0xff)<<8)|((buf[1]&0xff)<<0));
+    }
+    private short readShort() throws IOException {
+        short x = readShortBE();
+        if(ident!=null && ident.data == ELFIdent.ELFDATA2LSB) 
+            x = (short)((((x<<8)&0xff00) | ((x>>8)&0xff))&0xffff);
+        return x;
+    }
+    
+    private byte readByte() throws IOException {
+        byte[] buf = new byte[1];
+        readFully(buf);
+        return buf[0];
+    }
+        
+    public class ELFIdent {
+        private static final int ELF_MAGIC = 0x7f454c46; // '\177', 'E', 'L', 'F'
+
+        public static final int ELFCLASSNONE = 0;
+        public static final int ELFCLASS32 = 1;
+        public static final int ELFCLASS64 = 2;
+        public byte klass;
+
+
+        public static final int ELFDATANONE = 0;
+        public static final int ELFDATA2LSB = 1;
+        public static final int ELFDATA2MSB = 2;        
+        public byte data;
+        public byte osabi;
+        public byte abiversion;
+                
+        ELFIdent() throws IOException {
+            if(readIntBE() != ELF_MAGIC) throw new ELFException("Bad Magic (is: " );
+            
+            klass = readByte();
+            if(klass != ELFCLASS32) throw new ELFException("org.xwt.mips.ELF does not suport 64-bit binaries");
+            
+            data = readByte();
+            if(data != ELFDATA2LSB && data != ELFDATA2MSB) throw new ELFException("Unknown byte order");
+            
+            readByte(); // version
+            osabi = readByte();
+            abiversion = readByte();
+            for(int i=0;i<7;i++) readByte(); // padding
+        }
+    }
+    
+    public class ELFHeader {
+        public static final short ET_EXEC = 2;
+        public short type;
+        
+        public static final short EM_MIPS = 8;
+        public short machine;
+        
+        public int version;
+        public int entry;
+        public int phoff;
+        public int shoff;
+        public int flags;
+        public short ehsize;
+        public short phentsize;
+        public short phnum;
+        public short shentsize;
+        public short shnum;
+        public short shstrndx;
+
+        ELFHeader() throws IOException {
+            type = readShort();
+            machine = readShort();
+            version = readInt();
+            if(version != 1) throw new ELFException("version != 1");
+            entry = readInt();
+            phoff = readInt();
+            shoff = readInt();
+            flags = readInt();
+            ehsize = readShort();
+            phentsize = readShort();
+            phnum = readShort();
+            shentsize = readShort();
+            shnum = readShort();
+            shstrndx = readShort();
+        }
+    }
+    
+    public class PHeader {
+        public int type;
+        public int offset;
+        public int vaddr;
+        public int paddr;
+        public int filesz;
+        public int memsz;
+        public int flags;
+        public int align;
+        
+        public static final int PF_X = 0x1;
+        public static final int PF_W = 0x2;
+        public static final int PF_R = 0x4;
+        
+        public static final int PT_LOAD = 1;
+        
+        PHeader() throws IOException {
+            type = readInt();
+            offset = readInt();
+            vaddr = readInt();
+            paddr = readInt();
+            filesz = readInt();
+            memsz = readInt();
+            flags = readInt();
+            align = readInt();
+            if(filesz > memsz) throw new ELFException("ELF inconsistency: filesz > memsz (" + toHex(filesz) + " > " + toHex(memsz) + ")");
+        }
+        
+        public boolean writable() { return (flags & PF_W) != 0; }
+        
+        public InputStream getInputStream() throws IOException {
+            return new BufferedInputStream(new SectionInputStream(
+                offset,offset+filesz));
+        }
+    }
+    
+    public class SHeader {
+        int nameidx;
+        public String name;
+        public int type;
+        public int flags;
+        public int addr;
+        public int offset;
+        public int size;
+        public int link;
+        public int info;
+        public int addralign;
+        public int entsize;
+        
+        public static final int SHT_SYMTAB = 2;
+        public static final int SHT_STRTAB = 3;
+        public static final int SHT_NOBITS = 8;
+        
+        SHeader() throws IOException {
+            nameidx = readInt();
+            type = readInt();
+            flags = readInt();
+            addr = readInt();
+            offset = readInt();
+            size = readInt();
+            link = readInt();
+            info = readInt();
+            addralign = readInt();
+            entsize = readInt();
+        }
+        
+        public InputStream getInputStream() throws IOException {
+            return new BufferedInputStream(new SectionInputStream(
+                offset, type == SHT_NOBITS ? 0 : offset+size));
+        }
+        
+        public boolean isText() { return name.equals(".text"); }
+        public boolean isData() { return name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"); }
+        public boolean isBSS() { return name.equals(".bss") || name.equals(".sbss"); }
+    }
+    
+    public ELF(String file) throws IOException, ELFException { this(new SeekableFile(file,false)); }
+    public ELF(SeekableData data) throws IOException, ELFException {
+        this.data = data;
+        ident = new ELFIdent();
+        header = new ELFHeader();
+        pheaders = new PHeader[header.phnum];
+        for(int i=0;i<header.phnum;i++) {
+            data.seek(header.phoff+i*header.phentsize);
+            pheaders[i] = new PHeader();
+        }
+        sheaders = new SHeader[header.shnum];
+        for(int i=0;i<header.shnum;i++) {
+            data.seek(header.shoff+i*header.shentsize);
+            sheaders[i] = new SHeader();
+        }
+        if(header.shstrndx < 0 || header.shstrndx >= header.shnum) throw new ELFException("Bad shstrndx");
+        data.seek(sheaders[header.shstrndx].offset);
+        stringTable = new byte[sheaders[header.shstrndx].size];
+        readFully(stringTable);
+        
+        for(int i=0;i<header.shnum;i++) {
+            SHeader s = sheaders[i];
+            s.name = getString(s.nameidx);
+        }
+    }
+    
+    private String getString(int off) { return getString(off,stringTable); }
+    private String getString(int off,byte[] strtab) {
+        StringBuffer sb = new StringBuffer();
+        if(off < 0 || off >= strtab.length) return "<invalid strtab entry>";
+        while(off >= 0 && off < strtab.length && strtab[off] != 0) sb.append((char)strtab[off++]);
+        return sb.toString();
+    }
+    
+    public SHeader sectionWithName(String name) {
+        for(int i=0;i<sheaders.length;i++)
+            if(sheaders[i].name.equals(name))
+                return sheaders[i];
+        return null;
+    }
+    
+    public class ELFException extends IOException { ELFException(String s) { super(s); } }
+    
+    private class SectionInputStream extends InputStream {
+        private int pos;
+        private int maxpos;
+        SectionInputStream(int start, int end) throws IOException {
+            if(sectionReaderActive)
+                throw new IOException("Section reader already active");
+            sectionReaderActive = true;
+            pos = start;
+            data.seek(pos);
+            maxpos = end;
+        }
+        
+        private int bytesLeft() { return maxpos - pos; }
+        public int read() throws IOException {
+            byte[] buf = new byte[1];
+            return read(buf,0,1) == -1 ? -1 : (buf[0]&0xff);
+        }
+        public int read(byte[] b, int off, int len) throws IOException {
+            int n = data.read(b,off,Math.min(len,bytesLeft())); if(n > 0) pos += n; return n;
+        }
+        public void close() { sectionReaderActive = false; }
+    }
+    
+    private Symtab _symtab;
+    public Symtab getSymtab() throws IOException {
+        if(_symtab != null) return _symtab;
+        
+        if(sectionReaderActive) throw new ELFException("Can't read the symtab while a section reader is active");
+        
+        SHeader sh = sectionWithName(".symtab");
+        if(sh == null || sh.type != SHeader.SHT_SYMTAB) return null;
+        
+        SHeader sth = sectionWithName(".strtab");
+        if(sth == null || sth.type != SHeader.SHT_STRTAB) return null;
+        
+        byte[] strtab = new byte[sth.size];
+        DataInputStream dis = new DataInputStream(sth.getInputStream());
+        dis.readFully(strtab);
+        dis.close();
+        
+        return _symtab = new Symtab(sh.offset, sh.size,strtab);
+    }
+    
+    public class  Symtab {
+        public Symbol[] symbols;
+        
+        Symtab(int off, int size, byte[] strtab) throws IOException {
+            data.seek(off);
+            int count = size/16;
+            symbols = new Symbol[count];
+            for(int i=0;i<count;i++) symbols[i] = new Symbol(strtab);
+        }
+        
+        public Symbol getSymbol(String name) {
+            Symbol sym = null;
+            for(int i=0;i<symbols.length;i++) {
+                if(symbols[i].name.equals(name)) {
+                    if(sym == null)
+                        sym = symbols[i];
+                    else
+                        System.err.println("WARNING: Multiple symbol matches for " + name);
+                }
+            }
+            return sym;
+        }
+        
+        public Symbol getGlobalSymbol(String name) {
+            for(int i=0;i<symbols.length;i++) 
+                if(symbols[i].binding == Symbol.STB_GLOBAL && symbols[i].name.equals(name))
+                    return symbols[i];
+            return null;
+        }
+    }
+    
+    public class Symbol {
+        public String name;
+        public int addr;
+        public int size;
+        public byte info;
+        public byte type;
+        public byte binding;
+        public byte other;
+        public SHeader sheader;
+        
+        public final static int STT_FUNC = 2;
+        public final static int STB_GLOBAL = 1;
+        
+        Symbol(byte[] strtab) throws IOException {
+            name = getString(readInt(),strtab);
+            addr = readInt();
+            size = readInt();
+            info = readByte();
+            type = (byte)(info&0xf);
+            binding = (byte)(info>>4);
+            other = readByte();
+            // FEATURE: This should point to some other entry or something
+            readShort();
+        }
+    }
+    
+    private static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
+    
+    public static void main(String[] args) throws IOException {
+        ELF elf = new ELF(new SeekableInputStream(new FileInputStream(args[0])));
+        System.out.println("Type: " + toHex(elf.header.type));
+        System.out.println("Machine: " + toHex(elf.header.machine));
+        System.out.println("Entry: " + toHex(elf.header.entry));
+        for(int i=0;i<elf.pheaders.length;i++) {
+            ELF.PHeader ph = elf.pheaders[i];
+            System.out.println("PHeader " + toHex(i));
+            System.out.println("\tOffset: " + ph.offset);
+            System.out.println("\tVaddr: " + toHex(ph.vaddr));
+            System.out.println("\tFile Size: " + ph.filesz);
+            System.out.println("\tMem Size: " + ph.memsz);
+        }
+        for(int i=0;i<elf.sheaders.length;i++) {
+            ELF.SHeader sh = elf.sheaders[i];
+            System.out.println("SHeader " + toHex(i));
+            System.out.println("\tName: " + sh.name);
+            System.out.println("\tOffset: " + sh.offset);
+            System.out.println("\tAddr: " + toHex(sh.addr));
+            System.out.println("\tSize: " + sh.size);
+            System.out.println("\tType: " + toHex(sh.type));
+        }
+        Symtab symtab = elf.getSymtab();
+        if(symtab != null) {
+            System.out.println("Symbol table:");
+            for(int i=0;i<symtab.symbols.length;i++)
+                System.out.println("\t" + symtab.symbols[i].name + " -> " + toHex(symtab.symbols[i].addr));
+        } else {
+            System.out.println("Symbol table: None");
+        }
+    }
+}
diff --git a/src/org/xwt/mips/Interpreter.java b/src/org/xwt/mips/Interpreter.java
new file mode 100644 (file)
index 0000000..f95df92
--- /dev/null
@@ -0,0 +1,757 @@
+// Copyright 2003 Brian Alliet
+// Based on org.xwt.imp.MIPS by Adam Megacz
+// Portions Copyright 2003 Adam Megacz
+
+package org.xwt.mips;
+
+import org.xwt.mips.util.*;
+import java.io.*;
+
+public class Interpreter extends UnixRuntime {
+    // Registers
+    private int[] registers = new int[32];
+    private int hi,lo;
+    
+    // Floating Point Registers
+    private int[] fpregs = new int[32];
+    // 24-31 - unused
+    // 23 - conditional bit
+    // 18-22 - unused
+    // 12-17 - cause bits (unimplemented)
+    // 7-11  - enables bits (unimplemented)
+    // 2-6   - flags (unimplemented)
+    // 0-1   - rounding mode (only implemented for fixed point conversions)
+    private int fcsr;
+    
+    private int pc;
+    
+    // The filename if the binary we're running
+    public String image;
+    private ELF.Symtab symtab;
+    
+    // Register Operations
+    private final void setFC(boolean b) { fcsr = (fcsr&~0x800000) | (b ? 0x800000 : 0x000000); }
+    private final int roundingMode() { return fcsr & 3; /* bits 0-1 */ }
+    private final double getDouble(int r) {
+        return Double.longBitsToDouble(((fpregs[r+1]&0xffffffffL) << 32) | (fpregs[r]&0xffffffffL));
+    }
+    private final void setDouble(int r, double d) {
+        long l = Double.doubleToLongBits(d);
+        fpregs[r+1] = (int)(l >>> 32); fpregs[r] = (int)l;
+    }
+    private final float getFloat(int r) { return Float.intBitsToFloat(fpregs[r]); }
+    private final void setFloat(int r, float f) { fpregs[r] = Float.floatToRawIntBits(f); }
+    
+    protected void _execute() throws ExecutionException {
+        try {
+            runSome();
+        } catch(ExecutionException e) {
+            e.setLocation(toHex(pc) + ": " + sourceLine(pc));
+            throw e;
+        }
+    }
+    
+    // Main interpretor
+    // the return value is meaningless, its just to catch people typing "return" by accident
+    private final int runSome() throws FaultException,ExecutionException {
+        int[] r = registers;
+        int[] f = fpregs;
+        int pc = this.pc;
+        int nextPC = pc + 4;
+    try {
+    OUTER: for(;;) {
+        int insn;
+        try {
+            insn = readPages[pc>>>PAGE_SHIFT][(pc>>>2)&PAGE_WORDS-1];
+        } catch (RuntimeException e) {
+            insn = memRead(pc);
+        }
+
+        int op = (insn >>> 26) & 0xff;                 // bits 26-31
+        int rs = (insn >>> 21) & 0x1f;                 // bits 21-25
+        int rt = (insn >>> 16) & 0x1f;                 // bits 16-20 
+        int ft = (insn >>> 16) & 0x1f;
+        int rd = (insn >>> 11) & 0x1f;                 // bits 11-15
+        int fs = (insn >>> 11) & 0x1f;
+        int shamt = (insn >>> 6) & 0x1f;               // bits 6-10
+        int fd = (insn >>> 6) & 0x1f;
+        int subcode = insn & 0x3f;                     // bits 0-5  
+
+        int jumpTarget = (insn & 0x03ffffff);          // bits 0-25
+        int unsignedImmediate = insn & 0xffff;
+        int signedImmediate = (insn << 16) >> 16;
+        int branchTarget = signedImmediate;
+
+        int tmp, addr; // temporaries
+        
+        r[ZERO] = 0;
+    
+        switch(op) {
+            case 0: {
+                switch(subcode) {
+                    case 0: // SLL
+                        if(insn == 0) break;
+                        r[rd] = r[rt] << shamt;
+                        break;
+                    case 2: // SRL
+                        r[rd] = r[rt] >>> shamt;
+                        break;
+                    case 3: // SRA
+                        r[rd] = r[rt] >> shamt;
+                        break;
+                    case 4: // SLLV
+                        r[rd] = r[rt] << (r[rs]&0x1f);
+                        break;
+                    case 6: // SRLV
+                        r[rd] = r[rt] >>> (r[rs]&0x1f);
+                        break;
+                    case 7: // SRAV
+                        r[rd] = r[rt] >> (r[rs]&0x1f);
+                        break;
+                    case 8: // JR
+                        tmp = r[rs]; pc += 4; nextPC = tmp;
+                        continue OUTER;
+                    case 9: // JALR
+                        tmp = r[rs]; pc += 4; r[rd] = pc+4; nextPC = tmp;
+                        continue OUTER;
+                    case 12: // SYSCALL
+                        this.pc = pc;
+                        r[V0] = syscall(r[V0],r[A0],r[A1],r[A2],r[A3]);
+                        if(state != RUNNING) { this.pc = nextPC; break OUTER; }
+                        break;
+                    case 13: // BREAK
+                        throw new ExecutionException("Break");
+                    case 16: // MFHI
+                        r[rd] = hi;
+                        break;
+                    case 17: // MTHI
+                        hi = r[rs];
+                        break;
+                    case 18: // MFLO
+                        r[rd] = lo;
+                        break;
+                    case 19: // MTLO
+                        lo = r[rs];
+                        break;
+                    case 24: { // MULT
+                        long hilo = (long)(r[rs]) * ((long)r[rt]);
+                        hi = (int) (hilo >>> 32);
+                        lo = (int) hilo;
+                        break;
+                    }
+                    case 25: { // MULTU
+                        long hilo = (r[rs] & 0xffffffffL) * (r[rt] & 0xffffffffL);
+                        hi = (int) (hilo >>> 32);
+                        lo = (int) hilo;
+                        break;
+                    }
+                    case 26: // DIV
+                        hi = r[rs]%r[rt];
+                        lo = r[rs]/r[rt];
+                        break;
+                    case 27: // DIVU
+                        if(rt != 0) {
+                            hi = (int)((r[rs] & 0xffffffffL) % (r[rt] & 0xffffffffL));
+                            lo = (int)((r[rs] & 0xffffffffL) / (r[rt] & 0xffffffffL));
+                        }
+                        break;
+                    case 32: // ADD
+                        throw new ExecutionException("ADD (add with oveflow trap) not suported");
+                        /*This must trap on overflow
+                        r[rd] = r[rs] + r[rt];
+                        break;*/
+                    case 33: // ADDU
+                        r[rd] = r[rs] + r[rt];
+                        break;
+                    case 34: // SUB
+                        throw new ExecutionException("SUB (sub with oveflow trap) not suported");
+                        /*This must trap on overflow
+                        r[rd] = r[rs] - r[rt];
+                        break;*/
+                    case 35: // SUBU
+                        r[rd] = r[rs] - r[rt];
+                        break;
+                    case 36: // AND
+                        r[rd] = r[rs] & r[rt];
+                        break;
+                    case 37: // OR
+                        r[rd] = r[rs] | r[rt];
+                        break;
+                    case 38: // XOR
+                        r[rd] = r[rs] ^ r[rt];
+                        break;
+                    case 39: // NOR
+                        r[rd] = ~(r[rs] | r[rt]);
+                        break;
+                    case 42: // SLT
+                        r[rd] = r[rs] < r[rt] ? 1 : 0;
+                        break;
+                    case 43: // SLTU
+                        r[rd] = ((r[rs] & 0xffffffffL) < (r[rt] & 0xffffffffL)) ? 1 : 0;
+                        break;
+                    default:
+                        throw new ExecutionException("Illegal instruction 0/" + subcode);
+                }
+                break;
+            }
+            case 1: {
+                switch(rt) {
+                    case 0: // BLTZ
+                        if(r[rs] < 0) {
+                            pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;                   
+                            continue OUTER;
+                        }
+                        break;
+                    case 1: // BGEZ
+                        if(r[rs] >= 0) {
+                            pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
+                            continue OUTER;
+                        }
+                        break;
+                    case 16: // BLTZAL
+                        if(r[rs] < 0) {
+                            pc += 4; r[RA] = pc+4; tmp = pc + branchTarget*4; nextPC = tmp;
+                            continue OUTER;
+                        }
+                        break;
+                    case 17: // BGEZAL
+                        if(r[rs] >= 0) {
+                            pc += 4; r[RA] = pc+4; tmp = pc + branchTarget*4; nextPC = tmp;  
+                            continue OUTER;
+                        }
+                        break;
+                    default:
+                        throw new ExecutionException("Illegal Instruction");
+                }
+                break;
+            }
+            case 2: { // J
+                tmp = (pc&0xf0000000) | (jumpTarget << 2);
+                pc+=4; nextPC = tmp;
+                continue OUTER;
+            }
+            case 3: { // JAL
+                tmp = (pc&0xf0000000) | (jumpTarget << 2);
+                pc+=4; r[RA] = pc+4; nextPC = tmp;
+                continue OUTER;
+            }
+            case 4: // BEQ
+                if(r[rs] == r[rt]) {
+                    pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
+                    continue OUTER;
+                }
+                break;
+            case 5: // BNE                
+                if(r[rs] != r[rt]) {
+                    pc += 4; tmp = pc + branchTarget*4; nextPC = tmp; 
+                    continue OUTER;
+                }
+                break;
+            case 6: //BLEZ
+                if(r[rs] <= 0) {
+                    pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
+                    continue OUTER;
+                }
+                break;
+            case 7: //BGTZ
+                if(r[rs] > 0) {
+                    pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
+                    continue OUTER;
+                }
+                break;
+            case 8: // ADDI
+                r[rt] = r[rs] + signedImmediate;
+                break;
+            case 9: // ADDIU
+                r[rt] = r[rs] + signedImmediate;
+                break;
+            case 10: // SLTI
+                r[rt] = r[rs] < signedImmediate ? 1 : 0;
+                break;
+            case 11: // SLTIU
+                r[rt] = (r[rs]&0xffffffffL) < (unsignedImmediate&0xffffffffL) ? 1 : 0;
+                break;
+            case 12: // ANDI
+                r[rt] = r[rs] & unsignedImmediate;
+                break;
+            case 13: // ORI
+                r[rt] = r[rs] | unsignedImmediate;
+                break;
+            case 14: // XORI
+                r[rt] = r[rs] ^ unsignedImmediate;
+                break;
+            case 15: // LUI
+                r[rt] = unsignedImmediate << 16;
+                break;
+            case 16:
+                throw new ExecutionException("TLB/Exception support not implemented");
+            case 17: { // FPU
+                boolean debug = false;
+                String line = debug ? sourceLine(pc) : "";
+                boolean debugon = debug && (line.indexOf("dtoa.c:51") >= 0 || line.indexOf("dtoa.c:52") >= 0 || line.indexOf("test.c") >= 0);
+                if(rs > 8 && debugon)
+                    System.out.println("               FP Op: " + op + "/" + rs + "/" + subcode + " " + line);
+                if(roundingMode() != 0 && rs != 6 /*CTC.1*/ && !((rs==16 || rs==17) && subcode == 36 /* CVT.W.Z */))
+                    throw new ExecutionException("Non-cvt.w.z operation attempted with roundingMode != round to nearest");
+                switch(rs) {
+                    case 0: // MFC.1
+                        r[rt] = f[rd];
+                        break;
+                    case 2: // CFC.1
+                        if(fs != 31) throw new ExecutionException("FCR " + fs + " unavailable");
+                        r[rt] = fcsr;
+                        break;
+                    case 4: // MTC.1
+                        f[rd] = r[rt];
+                        break;
+                    case 6: // CTC.1
+                        if(fs != 31) throw new ExecutionException("FCR " + fs + " unavailable");
+                        fcsr = r[rt];   
+                        break;
+                    case 8: // BC1F, BC1T
+                        if(((fcsr&0x800000)!=0) == (((insn>>>16)&1)!=0)) {
+                            pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
+                            continue OUTER;
+                        }
+                        break;
+                    case 16: {  // Single
+                        switch(subcode) {
+                            case 0: // ADD.S
+                                setFloat(fd,getFloat(fs)+getFloat(ft));
+                                break;
+                            case 1: // SUB.S
+                                setFloat(fd,getFloat(fs)-getFloat(ft));
+                                break;
+                            case 2: // MUL.S
+                                setFloat(fd,getFloat(fs)*getFloat(ft));
+                                break;
+                            case 3: // DIV.S
+                                setFloat(fd,getFloat(fs)/getFloat(ft));
+                                break;
+                            case 5: // ABS.S
+                                setFloat(fd,Math.abs(getFloat(fs)));
+                                break;
+                            case 6: // MOV.S
+                                f[fd] = f[fs];
+                                break;
+                            case 7: // NEG.S
+                                setFloat(fd,-getFloat(fs));
+                                break;
+                            case 33: // CVT.D.S
+                                setDouble(fd,getFloat(fs));
+                                break;
+                            case 36: // CVT.W.S
+                                switch(roundingMode()) {
+                                    case 0: f[fd] = (int)Math.floor(getFloat(fs)+0.5f); break; // Round to nearest
+                                    case 1: f[fd] = (int)getFloat(fs); break; // Round towards zero
+                                    case 2: f[fd] = (int)Math.ceil(getFloat(fs)); break; // Round towards plus infinity
+                                    case 3: f[fd] = (int)Math.floor(getFloat(fs)); break; // Round towards minus infinity
+                                }
+                                break;
+                            case 50: // C.EQ.S
+                                setFC(getFloat(fs) == getFloat(ft));
+                                break;
+                            case 60: // C.LT.S
+                                setFC(getFloat(fs) < getFloat(ft));
+                                break;
+                            default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+                        }
+                        break;
+                    }
+                    case 17: { // Double
+                        switch(subcode) {
+                            case 0: // ADD.D
+                                setDouble(fd,getDouble(fs)+getDouble(ft));
+                                break;
+                            case 1: // SUB.D
+                                if(debugon) System.out.println("f" + fd + " = f" + fs + " (" + getDouble(fs) + ") - f" + ft + " (" + getDouble(ft) + ")");
+                                setDouble(fd,getDouble(fs)-getDouble(ft));
+                                break;
+                            case 2: // MUL.D
+                                if(debugon) System.out.println("f" + fd + " = f" + fs + " (" + getDouble(fs) + ") * f" + ft + " (" + getDouble(ft) + ")");
+                                setDouble(fd,getDouble(fs)*getDouble(ft));
+                                if(debugon) System.out.println("f" + fd + " = " + getDouble(fd));
+                                break;
+                            case 3: // DIV.D
+                                setDouble(fd,getDouble(fs)/getDouble(ft));
+                                break;
+                            case 5: // ABS.D
+                                setDouble(fd,Math.abs(getDouble(fs)));
+                                break;
+                            case 6: // MOV.D
+                                f[fd] = f[fs];
+                                f[fd+1] = f[fs+1];
+                                break;
+                            case 7: // NEG.D
+                                setDouble(fd,-getDouble(fs));
+                                break;
+                            case 32: // CVT.S.D
+                                setFloat(fd,(float)getDouble(fs));
+                                break;
+                            case 36: // CVT.W.D
+                                if(debugon) System.out.println("CVT.W.D rm: " + roundingMode() + " f" + fs + ":" + getDouble(fs));
+                                switch(roundingMode()) {
+                                    case 0: f[fd] = (int)Math.floor(getDouble(fs)+0.5); break; // Round to nearest
+                                    case 1: f[fd] = (int)getDouble(fs); break; // Round towards zero
+                                    case 2: f[fd] = (int)Math.ceil(getDouble(fs)); break; // Round towards plus infinity
+                                    case 3: f[fd] = (int)Math.floor(getDouble(fs)); break; // Round towards minus infinity
+                                }
+                                if(debugon) System.out.println("CVT.W.D: f" + fd + ":" + f[fd]);
+                                break;
+                            case 50: // C.EQ.D
+                                setFC(getDouble(fs) == getDouble(ft));
+                                break;
+                            case 60: // C.LT.D
+                                setFC(getDouble(fs) < getDouble(ft));
+                                break;
+                            case 62: // C.LE.D
+                                setFC(getDouble(fs) <= getDouble(ft));
+                                break;                                
+                            default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+                        }
+                        break;
+                    }
+                    case 20: { // Integer
+                        switch(subcode) {
+                            case 33: // CVT.D.W
+                                setDouble(fd,f[fs]);
+                                break;
+                            default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+                        }
+                        break;
+                    }
+                    default:
+                        throw new ExecutionException("Invalid Instruction 17/" + rs);
+                }
+                break;
+            }
+            case 18: case 19:
+                throw new ExecutionException("No coprocessor installed");
+            case 32: { // LB
+                addr = r[rs] + signedImmediate;
+                try {
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+                } catch(RuntimeException e) {
+                    tmp = memRead(addr&~3);
+                }
+                switch(addr&3) {
+                    case 0: tmp = (tmp>>>24)&0xff; break;
+                    case 1: tmp = (tmp>>>16)&0xff; break;
+                    case 2: tmp = (tmp>>> 8)&0xff; break;
+                    case 3: tmp = (tmp>>> 0)&0xff; break;
+                }
+                if((tmp&0x80)!=0) tmp |= 0xffffff00; // sign extend
+                r[rt] = tmp;
+                break;
+            }
+            case 33: { // LH
+                addr = r[rs] + signedImmediate;
+                try {
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+                } catch(RuntimeException e) {
+                    tmp = memRead(addr&~3);
+                }
+                switch(addr&2) {
+                    case 0: tmp = (tmp>>>16)&0xffff; break;
+                    case 2: tmp = (tmp>>> 0)&0xffff; break;
+                }
+                if((tmp&0x8000)!=0) tmp |= 0xffff0000; // sign extend
+                r[rt] = tmp;
+                break;              
+            }
+            case 34: { // LWL;
+                addr = r[rs] + signedImmediate;
+                try {
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+                } catch(RuntimeException e) {
+                    tmp = memRead(addr&~3);
+                }
+                switch(addr&3) {
+                    case 0: r[rt] = (r[rt]&0x00000000)|(tmp<< 0); break;
+                    case 1: r[rt] = (r[rt]&0x000000ff)|(tmp<< 8); break;
+                    case 2: r[rt] = (r[rt]&0x0000ffff)|(tmp<<16); break;
+                    case 3: r[rt] = (r[rt]&0x00ffffff)|(tmp<<24); break;
+                }
+                break;
+            }
+            case 35: // LW
+                addr = r[rs] + signedImmediate;
+                try {
+                    r[rt] = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+                } catch(RuntimeException e) {
+                    r[rt] = memRead(addr);
+                }
+                break;
+            case 36: { // LBU
+                addr = r[rs] + signedImmediate;
+                try {
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+                } catch(RuntimeException e) {
+                    tmp = memRead(addr);
+                }
+                switch(addr&3) {
+                    case 0: r[rt] = (tmp>>>24)&0xff; break;
+                    case 1: r[rt] = (tmp>>>16)&0xff; break;
+                    case 2: r[rt] = (tmp>>> 8)&0xff; break;
+                    case 3: r[rt] = (tmp>>> 0)&0xff; break;
+                }
+                break;
+            }
+            case 37: { // LHU
+                addr = r[rs] + signedImmediate;
+                try {
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+                } catch(RuntimeException e) {
+                    tmp = memRead(addr&~3);
+                }
+                switch(addr&2) {
+                    case 0: r[rt] = (tmp>>>16)&0xffff; break;
+                    case 2: r[rt] = (tmp>>> 0)&0xffff; break;
+                }
+                break;
+            }
+            case 38: { // LWR
+                addr = r[rs] + signedImmediate;
+                try {
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+                } catch(RuntimeException e) {
+                    tmp = memRead(addr&~3);
+                }
+                switch(addr&3) {
+                    case 0: r[rt] = (r[rt]&0xffffff00)|(tmp>>>24); break;
+                    case 1: r[rt] = (r[rt]&0xffff0000)|(tmp>>>16); break;
+                    case 2: r[rt] = (r[rt]&0xff000000)|(tmp>>> 8); break;
+                    case 3: r[rt] = (r[rt]&0x00000000)|(tmp>>> 0); break;
+                }
+                break;
+            }
+            case 40: { // SB
+                addr = r[rs] + signedImmediate;
+                try {
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+                } catch(RuntimeException e) {
+                    tmp = memRead(addr&~3);
+                }
+                switch(addr&3) {
+                    case 0: tmp = (tmp&0x00ffffff) | ((r[rt]&0xff)<<24); break;
+                    case 1: tmp = (tmp&0xff00ffff) | ((r[rt]&0xff)<<16); break;
+                    case 2: tmp = (tmp&0xffff00ff) | ((r[rt]&0xff)<< 8); break;
+                    case 3: tmp = (tmp&0xffffff00) | ((r[rt]&0xff)<< 0); break;
+                }
+                try {
+                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
+                } catch(RuntimeException e) {
+                    memWrite(addr&~3,tmp);
+                }
+                break;
+            }
+            case 41: { // SH
+                addr = r[rs] + signedImmediate;
+                try {
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
+                } catch(RuntimeException e) {
+                    tmp = memRead(addr&~3);
+                }
+                switch(addr&2) {
+                    case 0: tmp = (tmp&0x0000ffff) | ((r[rt]&0xffff)<<16); break;
+                    case 2: tmp = (tmp&0xffff0000) | ((r[rt]&0xffff)<< 0); break;
+                }
+                try {
+                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
+                } catch(RuntimeException e) {
+                    memWrite(addr&~3,tmp);
+                }
+                break;
+            }
+            case 42: { // SWL
+                addr = r[rs] + signedImmediate;
+                tmp = memRead(addr&~3);
+                switch(addr&3) {
+                    case 0: tmp=(tmp&0x00000000)|(r[rt]>>> 0); break;
+                    case 1: tmp=(tmp&0xff000000)|(r[rt]>>> 8); break;
+                    case 2: tmp=(tmp&0xffff0000)|(r[rt]>>>16); break;
+                    case 3: tmp=(tmp&0xffffff00)|(r[rt]>>>24); break;
+                }
+                try {
+                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
+                } catch(RuntimeException e) {
+                    memWrite(addr&~3,tmp);
+                }
+                break;
+            }
+            case 43: // SW
+                addr = r[rs] + signedImmediate;
+                try {
+                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = r[rt];
+                } catch(RuntimeException e) {
+                    memWrite(addr&~3,r[rt]);
+                }
+                break;
+            case 46: { // SWR
+                addr = r[rs] + signedImmediate;
+                tmp = memRead(addr&~3);
+                switch(addr&3) {
+                    case 0: tmp=(tmp&0x00ffffff)|(r[rt]<<24); break;
+                    case 1: tmp=(tmp&0x0000ffff)|(r[rt]<<16); break;
+                    case 2: tmp=(tmp&0x000000ff)|(r[rt]<< 8); break;
+                    case 3: tmp=(tmp&0x00000000)|(r[rt]<< 0); break;
+                }
+                memWrite(addr&~3,tmp);
+                break;
+            }
+            // FEATURE: Needs to be atomic w/ threads
+            case 48: // LWC0/LL
+                r[rt] = memRead(r[rs] + signedImmediate);
+                break;
+            case 49: // LWC1
+                f[rt] = memRead(r[rs] + signedImmediate);
+                break;
+            // FEATURE: Needs to be atomic w/ threads
+            case 56:
+                memWrite(r[rs] + signedImmediate,r[rt]);
+                r[rt] = 1;
+                break;
+            case 57: // SWC1
+                memWrite(r[rs] + signedImmediate,f[rt]);
+                break;
+            default:
+                throw new ExecutionException("Invalid Instruction: " + op);
+        }
+        pc = nextPC;
+        nextPC = pc + 4;
+    } // for(;;)
+    } catch(ExecutionException e) {
+        this.pc = pc;
+        throw e;
+    }
+        return 0;
+    }
+    
+    public int lookupSymbol(String name) {
+        ELF.Symbol sym = symtab.getGlobalSymbol(name);
+        return sym == null ? -1 : sym.addr;
+    }
+    
+    // Image loading function
+    private void loadImage(SeekableData data) throws IOException {
+        if(state != UNINITIALIZED) throw new IllegalStateException("loadImage called on initialized runtime");
+        
+        ELF elf = new ELF(data);
+        symtab = elf.getSymtab();
+        
+        if(elf.header.type != ELF.ELFHeader.ET_EXEC) throw new IOException("Binary is not an executable");
+        if(elf.header.machine != ELF.ELFHeader.EM_MIPS) throw new IOException("Binary is not for the MIPS I Architecture");
+        if(elf.ident.data != ELF.ELFIdent.ELFDATA2MSB) throw new IOException("Binary is not big endian");
+        
+        entryPoint = elf.header.entry;
+        
+        ELF.Symtab symtab = elf.getSymtab();
+        if(symtab == null) throw new IOException("No symtab in binary (did you strip it?)");
+        ELF.Symbol userInfo = symtab.getGlobalSymbol("user_info");
+        ELF.Symbol gpsym = symtab.getGlobalSymbol("_gp");
+        
+        if(gpsym == null) throw new IOException("NO _gp symbol!");
+        gp = gpsym.addr;
+        
+        if(userInfo != null) {
+            userInfoBase = userInfo.addr;
+            userInfoSize = userInfo.size;
+        }
+        
+        ELF.PHeader[] pheaders = elf.pheaders;
+        int brk = 0;
+        for(int i=0;i<pheaders.length;i++) {
+            ELF.PHeader ph = pheaders[i];
+            if(ph.type != ELF.PHeader.PT_LOAD) continue;
+            int memsize = ph.memsz;
+            int filesize = ph.filesz;
+            if(memsize == 0) continue;
+            if(memsize < 0) throw new IOException("pheader size too large");
+            int addr = ph.vaddr;
+            if(addr == 0x0) throw new IOException("pheader vaddr == 0x0");
+            brk = max(addr+memsize,brk);
+            
+            for(int j=0;j<memsize+PAGE_SIZE-1;j+=PAGE_SIZE) {
+                int page = (j+addr) >>> PAGE_SHIFT;
+                if(readPages[page] == null)
+                    readPages[page] = new int[PAGE_WORDS];
+                if(ph.writable()) writePages[page] = readPages[page];
+            }
+            if(filesize != 0) {
+                filesize = filesize & ~3;
+                DataInputStream dis = new DataInputStream(ph.getInputStream());
+                do {
+                    readPages[addr >>> PAGE_SHIFT][(addr >>> 2)&(PAGE_WORDS-1)] = dis.readInt();
+                    addr+=4;
+                    filesize-=4;
+                } while(filesize > 0);
+                dis.close();
+            }
+        }
+        brkAddr = (brk+PAGE_SIZE-1)&~(PAGE_SIZE-1);
+        state = INITIALIZED;
+    }
+    
+    protected void setCPUState(CPUState state) {
+        for(int i=1;i<32;i++) registers[i] = state.r[i];
+        for(int i=0;i<32;i++) fpregs[i] = state.f[i];
+        hi=state.hi; lo=state.lo; fcsr=state.fcsr;
+        pc=state.pc;
+    }
+    
+    protected CPUState getCPUState() {
+        CPUState state = new CPUState();
+        for(int i=1;i<32;i++) state.r[i] = registers[i];
+        for(int i=0;i<32;i++) state.f[i] = fpregs[i];
+        state.hi=hi; state.lo=lo; state.fcsr=fcsr;
+        state.pc=pc;
+        return state;
+    }
+    
+    // This is package private for fork() which does all kinds of ugly things behind the scenes
+    Interpreter() { super(4096,65536,true); }
+    public Interpreter(SeekableData data) throws IOException { this(); loadImage(data); }
+    public Interpreter(String filename) throws IOException {
+        this(new SeekableFile(filename,false));
+        image = filename;
+    }
+    public Interpreter(InputStream is) throws IOException { this(new SeekableInputStream(is)); }
+    
+    // Debug functions
+    // NOTE: This probably requires a jdk > 1.1, however, it is only used for debugging
+    private java.util.HashMap sourceLineCache;
+    public String sourceLine(int pc) {
+        final String addr2line = "mips-unknown-elf-addr2line";
+        String line = (String) (sourceLineCache == null ? null : sourceLineCache.get(new Integer(pc)));
+        if(line != null) return line;
+        if(image==null) return null;
+        try {
+            Process p = java.lang.Runtime.getRuntime().exec(new String[]{addr2line,"-e",image,toHex(pc)});
+            line = new BufferedReader(new InputStreamReader(p.getInputStream())).readLine();
+            if(line == null) return null;
+            while(line.startsWith("../")) line = line.substring(3);
+            if(sourceLineCache == null) sourceLineCache = new java.util.HashMap();
+            sourceLineCache.put(new Integer(pc),line);
+            return line;
+        } catch(IOException e) {
+            return null;
+        }
+    }
+    
+    public class DebugShutdownHook implements Runnable {
+        public void run() {
+            int pc = Interpreter.this.pc;
+            if(getState() == RUNNING)
+                System.err.print("\nCPU Executing " + toHex(pc) + ": " + sourceLine(pc) + "\n");
+        }
+    }
+
+    public static void main(String[] argv) throws Exception {
+        String image = argv[0];
+        Interpreter emu = new Interpreter(image);
+        java.lang.Runtime.getRuntime().addShutdownHook(new Thread(emu.new DebugShutdownHook()));
+        int status = emu.run(argv);
+        System.err.println("Exit status: " + status);
+        System.exit(status);
+    }
+}
diff --git a/src/org/xwt/mips/JavaSourceCompiler.java b/src/org/xwt/mips/JavaSourceCompiler.java
new file mode 100644 (file)
index 0000000..5bf7e5d
--- /dev/null
@@ -0,0 +1,933 @@
+package org.xwt.mips;
+
+import java.util.*;
+import java.io.*;
+import org.xwt.mips.util.SeekableData;
+
+public class JavaSourceCompiler extends Compiler {
+    /** Stores the "case r XXX: ... run_YYYY();" blocks generated by the emitText method/ */
+    private StringBuffer runs = new StringBuffer();
+    /** Stores the "initData" and "cleadData" calls generated by the emitData and emitBSS methods */
+    private StringBuffer inits = new StringBuffer();
+    /** Stores lines to go in the class scope */
+    private StringBuffer classLevel = new StringBuffer();
+    
+    /** The stream to write the compiled output to */
+    private PrintWriter out;
+
+    /** Prints a blank line to the output stream */
+    private void p() { out.println(); }
+    /** prints the given string (indented by <i>indent</i>*4 spaces) to the output stream */ 
+    private void p(String s) { out.println(indents[indent] + s); }
+    private void pblock(StringBuffer sb) { out.print(sb.toString()); }
+    
+    /** Used by the p() method to add indentation */
+    private int indent;
+    
+    private static String indents[] = new String[16];
+    static { String s=""; for(int i=0;i<indents.length;i++,s=s+"    ") indents[i] = s; }
+    
+    public JavaSourceCompiler(SeekableData binary, String className, Writer w)  throws IOException {
+        super(binary,className);
+        out = new PrintWriter(w);
+    }
+    
+    protected void _go() throws Exn, IOException {
+        String packageName;
+        String className;
+        if (fullClassName.indexOf('.') != -1) {
+            packageName = fullClassName.substring(0, fullClassName.lastIndexOf('.'));
+            className = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
+        } else {
+            className = fullClassName;
+            packageName = null;
+        }
+        
+        p("/* This file was generated from " + source + " by Mips2Java on " + dateTime() + " */");
+        if (packageName != null) p("package " + packageName + ";");
+        if(runtimeStats) p("import java.util.*;");
+        p();
+        p("public class " + className + " extends " + runtimeClass + " {");
+        indent++;
+        
+        p("/* program counter */");
+        p("private int pc = 0;");
+        if(debugCompiler)
+            p("private int lastPC = 0;");
+        p();
+        p("/* General Purpose registers */");
+        p("private final static int r0 = 0;");
+        p("private int      r1,  r2,  r3,  r4,  r5,  r6,  r7,");
+        p("            r8,  r9,  r10, r11, r12, r13, r14, r15,");
+        p("            r16, r17, r18, r19, r20, r21, r22, r23,");
+        p("            r24, r25, r26, r27, r28, r29, r30, r31,");
+        p("            hi = 0, lo = 0;");
+        p("/* FP registers */");
+        p("private int f0,  f1,  f2,  f3,  f4,  f5,  f6,  f7,");
+        p("            f8,  f9,  f10, f11, f12, f13, f14, f15,");
+        p("            f16, f17, f18, f19, f20, f21, f22, f23,");
+        p("            f24, f25, f26, f27, f28, f29, f30, f31;");
+        p("/* FP Control Register */");
+        p("private int fcsr = 0;");
+        p();
+        
+        if(onePage) p("private final int[] page = readPages[0];");
+                
+        // Generate main body functions (run_XXXX() blocks, _data[] arrays, etc) 
+        int highestAddr = 0;
+        
+        for(int i=0;i<elf.sheaders.length;i++) {
+            ELF.SHeader sheader = elf.sheaders[i];
+            String name = sheader.name;
+            // if this section doesn't get loaded into our address space don't worry about it
+            if(sheader.addr == 0x0) continue;
+            
+            highestAddr = Math.max(highestAddr, sheader.addr + sheader.size);
+            
+            if(name.equals(".text"))
+                emitText(sheader.addr, new DataInputStream(sheader.getInputStream()),sheader.size);
+            else if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"))
+                emitData(sheader.addr, new DataInputStream(sheader.getInputStream()), sheader.size,name.equals(".rodata")); 
+            else if(name.equals(".bss") || name.equals(".sbss"))                
+                emitBSS(sheader.addr,sheader.size);
+            else
+                throw new Exn("Unknown segment: " + name);
+        }
+        p();
+        
+        pblock(classLevel);
+        p();
+        
+        // Trampoline (dispatch calls to the appropriate run_XXX() methods
+        p("private final void trampoline() throws ExecutionException {");
+        indent++;
+        p("while(state == RUNNING) {");
+        indent++;
+        p("switch(pc>>>" + methodShift+ ") {");
+        indent++;
+        pblock(runs);
+        p("default: throw new ExecutionException(\"invalid address 0x\" + Long.toString(this.pc&0xffffffffL,16) + \": r2: \" + r2);");
+        indent--; p("}");
+        indent--; p("}");
+        indent--; p("}");
+        p();
+        
+        // Constructor
+        p("public " + className + "() {");
+        indent++;
+        p("super(" + pageSize + "," + totalPages + "," + (fastMem?"false":"true") + ");");
+        p("entryPoint = " + toHex(elf.header.entry) + ";");
+        if(userInfo != null) {
+            p("userInfoBase=" + toHex(userInfo.addr) + ";");
+            p("userInfoSize=" + userInfo.size + ";");
+        }
+        p("gp = " + toHex(gp.addr) + ";");
+        if(onePage)
+            p("brkAddr = " + toHex((highestAddr+4095)&~4095) + ";");
+        else
+            p("brkAddr = " + toHex((highestAddr+pageSize-1)&~(pageSize-1)) + ";");
+        pblock(inits);
+        p("state = INITIALIZED;");
+        indent--;
+        p("}");
+        p();
+        
+        // main() function
+        p("public static void main(String[] args) throws Exception {");
+        indent++;
+        p("" + className + " me = new " + className + "();");
+        p("int status = me.run(\"" + fullClassName + "\",args);");
+        if(runtimeStats) p("me.printStats();");
+        p("System.exit(status);");
+        indent--;
+        p("}");
+        p();
+        
+        // Runtime abstract methods
+        p("protected void _execute() throws ExecutionException { trampoline(); }");
+        p();
+        
+        p("protected void setCPUState(CPUState state) {");
+        indent++;
+        for(int i=1;i<32;i++) p("r" + i + "=state.r[" + i + "];");
+        for(int i=0;i<32;i++) p("f" + i + "=state.f[" + i + "];");
+        p("hi=state.hi; lo=state.lo; fcsr=state.fcsr;");
+        p("pc=state.pc;");
+        indent--;
+        p("}");
+        p("protected CPUState getCPUState() {");
+        indent++;
+        p("CPUState state = new CPUState();");
+        for(int i=1;i<32;i++) p("state.r[" + i + "]=r" + i+ ";");
+        for(int i=0;i<32;i++) p("state.f[" + i + "]=f" + i +";");
+        p("state.hi=hi; state.lo=lo; state.fcsr=fcsr;");
+        p("state.pc=pc;");
+        p("return state;");
+        indent--;
+        p("}");
+        p();
+        
+        if(supportCall) {
+            p("private static final " + hashClass + " symbols = new " + hashClass + "();");
+            p("static {");
+            indent++;
+            ELF.Symbol[] symbols = elf.getSymtab().symbols;
+            for(int i=0;i<symbols.length;i++) {
+                ELF.Symbol s = symbols[i];
+                if(s.type == ELF.Symbol.STT_FUNC && s.binding == ELF.Symbol.STB_GLOBAL && (s.name.equals("_call_helper") || !s.name.startsWith("_")))
+                    p("symbols.put(\"" + s.name + "\",new Integer(" + toHex(s.addr) + "));");
+            }
+            indent--;
+            p("}");
+            p("public int lookupSymbol(String symbol) { Integer i = (Integer) symbols.get(symbol); return i==null ? -1 : i.intValue(); }");
+            p();
+        }
+        
+        // Runtime stats
+        if(runtimeStats) {
+            p("private HashMap counters = new HashMap();");
+            p("private void inc(String k) { Long i = (Long)counters.get(k); counters.put(k,new Long(i==null ? 1 : i.longValue() + 1)); }");
+            p("private void printStats() {");
+            p(" Iterator i = new TreeSet(counters.keySet()).iterator();");
+            p(" while(i.hasNext()) { Object o = i.next(); System.err.println(\"\" + o + \": \" + counters.get(o)); }");
+            p("}");
+            p();
+        }
+        
+        indent--;
+        p("}");
+    }
+    
+    private int startOfMethod = 0;
+    private int endOfMethod = 0;
+    
+    private void startMethod(int addr) {
+        addr &= ~(maxBytesPerMethod-1);
+        startOfMethod = addr;
+        endOfMethod = addr + maxBytesPerMethod;
+        String methodName = "run_" + Long.toString(addr & 0xffffffffL, 16);
+        runs.append(indents[4] + "case " + toHex(addr>>>methodShift) + ": " + methodName + "(); break; \n");
+        p("private final void " + methodName + "() throws ExecutionException { /"+"* " + toHex(addr) + " - " + toHex(endOfMethod) + " *" + "/");
+        indent++;
+        p("int addr, tmp;");
+        p("for(;;) {");
+        indent++;
+        p("switch(pc) {");
+        indent++;
+    }
+    
+    private void endMethod() { endMethod(endOfMethod); }
+    private void endMethod(int lastAddr) {
+        if(startOfMethod == 0) return;
+        // FEATURE: We should be able to use if(!unreachable) here (i think)
+        // This isn't strictly necessary; its just here to work around unreachable code errors
+        p("case " + toHex(lastAddr) + ":");
+        indent++;
+        p("pc=" + constant(lastAddr) + ";");
+        leaveMethod();
+        indent--;
+        if(debugCompiler)
+            p("default: throw new ExecutionException(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16)  + \" (got here from 0x\" + Long.toString(lastPC&0xffffffffL,16)+\")\");");
+        else
+            p("default: throw new ExecutionException(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
+        indent--;
+        p("}"); // end switch
+        p("/* NOT REACHED */");
+        indent--;
+        p("}"); // end for
+        indent--;
+        p("}"); // end method
+        endOfMethod = startOfMethod = 0;
+    }
+    
+    private HashMap relativeAddrs = new HashMap();  
+    private String constant(int target) {
+        if(target >= 4096 && lessConstants) {
+            int n = target & ~1023;
+            String var = "N_" + toHex8(n);
+            if(relativeAddrs.get(new Integer(n)) == null) {
+                relativeAddrs.put(new Integer(n),Boolean.TRUE);
+                classLevel.append(indents[1] + "private static int " + var + " = " + toHex(n) + ";\n");
+            }
+            return "(" + var + " + " + toHex(target - n) + ")";
+        } else {
+            return toHex(target);
+        }
+    }
+    
+    private void branch(int pc, int target) {
+        if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
+        p("pc=" + constant(target) + ";");
+        if(target == 0)
+            p("throw new ExecutionException(\"Branch to addr 0x0\");");
+        else if((pc&methodMask) == (target&methodMask))
+            p("continue;");
+        else if(assumeTailCalls)
+            p("run_" +  Long.toString((target&methodMask)&0xffffffffL, 16) + "(); return;");
+        else
+            leaveMethod();
+    }
+    
+    private void leaveMethod() {
+        p("return;");
+    }
+    
+    private boolean textDone;
+    private void emitText(int addr, DataInputStream dis, int size) throws Exn,IOException {
+        if(textDone) throw new Exn("Multiple text segments");
+        textDone = true;
+        
+        if((addr&3)!=0 || (size&3)!=0) throw new Exn("Section on weird boundaries");
+        int count = size/4;
+        int nextInsn = dis.readInt();
+        if(nextInsn == -1) throw new Error("Actually read -1 at " + toHex(addr));
+        int insn;
+        
+        for(int i=0;i<count;i++,addr+=4) {
+            insn = nextInsn;
+            nextInsn = (i == count-1) ? -1 : dis.readInt();
+            if(addr >= endOfMethod) { endMethod(); startMethod(addr); }
+            if(jumpableAddresses==null || addr == startOfMethod || jumpableAddresses.contains(new Integer(addr))) {
+                p("case " + toHex(addr) + ":");
+                unreachable = false;
+            } else if(unreachable) {
+                continue;
+            } else if(debugCompiler) {
+                p("/" + "* pc = " + toHex(addr) + "*" + "/");
+            }
+            indent++;
+            emitInstruction(addr,insn,nextInsn);
+            indent--;
+        }
+        endMethod(addr);
+        p();
+        dis.close();
+    }
+    
+    private int initDataCount = 0;
+    private void emitData(int addr, DataInputStream dis, int size, boolean readOnly) throws Exn,IOException {
+        if((addr&3)!=0 || (size&3)!=0) throw new Exn("Data section on weird boundaries");
+        int last = addr + size;
+        while(addr < last) {
+            int segSize = Math.min(size,28000); // must be a multiple of 56
+            StringBuffer sb = new StringBuffer();
+            for(int i=0;i<segSize;i+=7) {
+                long l = 0;
+                for(int j=0;j<7;j++) {
+                    l <<= 8;
+                    byte b = (i+j < size) ? dis.readByte() : 1;
+                    l |= (b & 0xffL);
+                }
+                for(int j=0;j<8;j++) {
+                    char c = (char) ((l>>>(7*(7-j)))&0x7f);
+                    if(c=='\n') sb.append("\\n"); 
+                    else if(c=='\r') sb.append("\\r");
+                    else if(c=='\\') sb.append("\\\\");
+                    else if(c=='"') sb.append("\\\"");
+                    else if(c >= 32 && c <= 126) sb.append(c);
+                    else sb.append("\\" +  toOctal3(c));
+                }
+            }
+            String varname =  "_data" + (++initDataCount);
+            p("private static final int[] " + varname + " = decodeData(\"" + sb.toString() + "\"," + toHex(segSize/4) + ");");
+            inits.append(indents[2] + "initPages(" + varname +"," + toHex(addr) + "," + (readOnly?"true":"false") + ");\n");
+            addr += segSize;
+            size -= segSize;
+        }
+        dis.close();
+    }
+
+    private void emitBSS(int addr, int size) throws Exn {
+        if((addr&3)!=0) throw new Exn("BSS section on weird boundaries");
+        size = (size+3)&~3;
+        int count = size/4;
+        inits.append(indents[2] + "clearPages(" + toHex(addr) + "," + toHex(count) + ");\n");
+    }
+
+    // True if the current code path is unreachable (any instruction with a case statement is reachable)
+    private boolean unreachable = false;
+    
+    private void emitInstruction(int pc, int insn, int nextInsn) throws IOException,Exn {
+        if(insn == -1) throw new Error("insn is -1");
+        
+        int op = (insn >>> 26) & 0xff;                 // bits 26-31
+        int rs = (insn >>> 21) & 0x1f;                 // bits 21-25
+        int rt = (insn >>> 16) & 0x1f;                 // bits 16-20 
+        int ft = (insn >>> 16) & 0x1f;
+        int rd = (insn >>> 11) & 0x1f;                 // bits 11-15
+        int fs = (insn >>> 11) & 0x1f;
+        int shamt = (insn >>> 6) & 0x1f;               // bits 6-10
+        int fd = (insn >>> 6) & 0x1f;
+        int subcode = insn & 0x3f;                     // bits 0-5  
+
+        int jumpTarget = (insn & 0x03ffffff);          // bits 0-25
+        int unsignedImmediate = insn & 0xffff;
+        int signedImmediate = (insn << 16) >> 16;
+        int branchTarget = signedImmediate;
+
+        int tmp; // temporaries
+        
+        //if(pc%64==0) p("System.err.println(\"Executing: " + toHex(pc) + "\");");
+        //p("/" + "*" + (pc == -1 ? "Delay Slot"  : toHex(pc)) + " *" + "/ ");
+        if(pc==-1) p("/" + "* Next insn is delay slot *" + "/ ");
+        
+        if(runtimeStats && op != 0) p("inc(\"opcode: " + op + "\");");
+        switch(op) {
+            case 0: {
+                if(runtimeStats && insn != 0) p("inc(\"opcode: 0/" + subcode + "\");");
+                switch(subcode) {
+                    case 0: // SLL
+                        if(insn != 0) 
+                            p( "r"+rd+" = r"+rt+" << "+shamt+";");
+                        break;
+                    case 2: // SRL
+                        p( "r"+rd+" = r"+rt+" >>> "+shamt+";");
+                        break;
+                    case 3: // SRA
+                        p( "r"+rd+" = r"+rt+" >> "+shamt+";");
+                        break;
+                    case 4: // SLLV
+                        p( "r"+rd+" = r"+rt+" << (r"+rs+"&0x1f);");
+                        break;
+                    case 6: // SRLV
+                        p( "r"+rd+" = r"+rt+" >>> (r"+rs+"&0x1f);");
+                        break;
+                    case 7: // SRAV
+                        p( "r"+rd+" = r"+rt+" >> (r"+rs+"&0x1f);");
+                        break;
+                    case 8: // JR
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        emitInstruction(-1,nextInsn,-1);
+                        if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
+                        p("pc=r" + rs + ";");
+                        leaveMethod();
+                        unreachable = true;
+                        break;
+                    case 9: // JALR
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        emitInstruction(-1,nextInsn,-1);
+                        if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
+                        p("pc=r" + rs + ";");
+                        p("r" + RA + "=" + constant(pc+8 /*skip this insn and delay slot*/) + ";");
+                        leaveMethod();
+                        unreachable = true;
+                        break;
+                    case 12: // SYSCALL
+                        p("pc = " + toHex(pc) + ";");
+                        p( "r"+V0+" = syscall(r"+V0+",r"+A0+",r"+A1+",r"+A2+",r"+A3+");");
+                        p("if (state != RUNNING) {");
+                            indent++;
+                            p("pc = " + toHex(pc+4) + ";");
+                            leaveMethod();
+                            indent--;
+                        p("}");
+                        break;
+                    case 13: // BREAK
+                        p( "throw new ExecutionException(\"Break\");");
+                        break;
+                    case 16: // MFHI
+                        p( "r"+rd+" = hi;");
+                        break;
+                    case 17: // MTHI
+                        p( "hi = r"+rs+";");
+                        break;
+                    case 18: // MFLO
+                        p( "r"+rd+" = lo;");
+                        break;
+                    case 19: // MTLO
+                        p( "lo = r"+rs+";");
+                        break;
+                    case 24: // MULT
+                        p( "{ long hilo = (long)(r"+rs+") * ((long)r"+rt+"); " +
+                             "hi = (int) (hilo >>> 32); " +
+                             "lo = (int) hilo; }");
+                        break;
+                    case 25: // MULTU
+                        p( "{ long hilo = (r"+rs+" & 0xffffffffL) * (r"+rt+" & 0xffffffffL); " +
+                             "hi = (int) (hilo >>> 32); " +
+                             "lo = (int) hilo; } ");
+                        break;
+                    case 26: // DIV
+                        p( "hi = r"+rs+"%r"+rt+"; lo = r"+rs+"/r"+rt+";");
+                        break;
+                    case 27: // DIVU
+                        p("if(r"+rt+"!=0) {");
+                        p( "hi = (int)((r"+rs+" & 0xffffffffL) % (r"+rt+" & 0xffffffffL)); " +
+                             "lo = (int)((r"+rs+" & 0xffffffffL) / (r"+rt+" & 0xffffffffL));");
+                        p("}");
+                        break;
+                    case 32: // ADD
+                         throw new Exn("ADD (add with oveflow trap) not suported");
+                        /*This must trap on overflow
+                        p( "r"+rd+" = r"+rs+" + r"+rt+";");
+                        break;*/
+                    case 33: // ADDU
+                        p( "r"+rd+" = r"+rs+" + r"+rt+";");
+                        break;
+                    case 34: // SUB
+                         throw new Exn("SUB (add with oveflow trap) not suported");
+                        /*This must trap on overflow
+                        p( "r"+rd+" = r"+rs+" - r"+rt+";");
+                        break;*/
+                    case 35: // SUBU
+                        p( "r"+rd+" = r"+rs+" - r"+rt+";");
+                        break;
+                    case 36: // AND
+                        p( "r"+rd+" = r"+rs+" & r"+rt+";");
+                        break;
+                    case 37: // OR
+                        p( "r"+rd+" = r"+rs+" | r"+rt+";");
+                        break;
+                    case 38: // XOR
+                        p( "r"+rd+" = r"+rs+" ^ r"+rt+";");
+                        break;
+                    case 39: // NOR
+                        p( "r"+rd+" = ~(r"+rs+" | r"+rt+");");
+                        break;
+                    case 42: // SLT
+                        p( "r"+rd+" = r"+rs+" < r"+rt+" ? 1 : 0;");
+                        break;
+                    case 43: // SLTU
+                        p( "r"+rd+" = ((r"+rs+" & 0xffffffffL) < (r"+rt+" & 0xffffffffL)) ? 1 : 0;");
+                        break;
+                    default:
+                        throw new RuntimeException("Illegal instruction 0/" + subcode);
+                }
+                break;
+            }
+            case 1: {
+                switch(rt) {
+                    case 0: // BLTZ
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        p("if(r" + rs + " < 0) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
+                        break;
+                    case 1: // BGEZ
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        p("if(r" + rs + " >= 0) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
+                        break;
+                    case 16: // BLTZAL
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        p("if(r" + rs + " < 0) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            p("r" + RA + "=" + constant(pc+8 /*skip this insn and delay slot*/) + ";");
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
+                        break;
+                    case 17: // BGEZAL
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        p("if(r" + rs + " >= 0) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            p("r" + RA + "=" + constant(pc+8 /*skip this insn and delay slot*/) + ";");
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
+                        break;
+                    default:
+                        throw new RuntimeException("Illegal Instruction 1/" + rt);
+                }
+                break;
+            }
+            case 2: { // J
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                emitInstruction(-1,nextInsn,-1);
+                branch(pc,(pc&0xf0000000)|(jumpTarget << 2));
+                unreachable = true;
+                break;
+            }
+            case 3: { // JAL
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                int target = (pc&0xf0000000)|(jumpTarget << 2);
+                emitInstruction(-1,nextInsn,-1);
+                if(optimizedMemcpy && (target == memcpy || target == memset)) {
+                    if(target == memcpy)
+                        p("memcpy(r4,r5,r6);");
+                    else if(target == memset)
+                        p("memset(r4,r5,r6);");
+                    p("r2 = r4;");
+                    branch(pc,pc+8);
+                } else {
+                    p("r" + RA + "=" + constant(pc+8 /*skip this insn and delay slot*/) + ";");
+                    branch(pc, target);
+                }
+                unreachable = true;
+                break;
+            }
+            case 4: // BEQ
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                p("if(r" + rs + " == r" + rt + ") {");
+                    indent++;
+                    emitInstruction(-1,nextInsn,-1);
+                    branch(pc,pc+branchTarget*4+4);
+                    indent--;
+                p("}");
+                break;
+            case 5: // BNE       
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                p("if(r" + rs + " != r" + rt + ") {");
+                    indent++;
+                    emitInstruction(-1,nextInsn,-1);
+                    branch(pc,pc+branchTarget*4+4);
+                    indent--;
+                p("}");
+                break;
+            case 6: //BLEZ
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                p("if(r" + rs + " <= 0) {");
+                    indent++;
+                    emitInstruction(-1,nextInsn,-1);
+                    branch(pc,pc+branchTarget*4+4);
+                    indent--;
+                p("}");
+                break;
+            case 7: //BGTZ
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                p("if(r" + rs + " > 0) {");
+                    indent++;
+                    emitInstruction(-1,nextInsn,-1);
+                    branch(pc,pc+branchTarget*4+4);
+                    indent--;
+                p("}");
+                break;
+            case 8: // ADDI
+                p( "r"+rt+" = r"+rs+" + "+signedImmediate +";");
+                break;
+            case 9: // ADDIU
+                p( "r"+rt+" = r"+rs+" + "+signedImmediate+";");
+                break;
+            case 10: // SLTI
+                p( "r"+rt+" = r"+rs+" < "+signedImmediate+" ? 1 : 0;");
+                break;
+            case 11: // SLTIU
+                p( "r"+rt+" = (r"+rs+"&0xffffffffL) < ("+unsignedImmediate+"&0xffffffffL) ? 1 : 0;");
+                break;
+            case 12: // ANDI
+                p( "r"+rt+" = r"+rs+" & "+unsignedImmediate+";");
+                break;
+            case 13: // ORI
+                p( "r"+rt+" = r"+rs+" | "+unsignedImmediate+";");
+                break;
+            case 14: // XORI
+                p( "r"+rt+" = r"+rs+" ^ "+unsignedImmediate+";");
+                break;
+            case 15: // LUI
+                p( "r"+rt+" = "+unsignedImmediate+" << 16;");
+                break;
+            case 16:
+                throw new Exn("TLB/Exception support not implemented");
+            case 17: { // FPU
+                switch(rs) {
+                    case 0: // MFC.1
+                        p( "r"+rt+" = f"+rd+";");
+                        break;
+                    case 2: // CFC.1
+                        if(fs != 31) throw new Exn("FCR " + fs + " unavailable");
+                        p( "r"+rt+" = fcsr;");
+                        break;
+                    case 4: // MTC.1
+                        p( "f"+rd+" = r"+rt+";");
+                        break;
+                    case 6: // CTC.1
+                        if(fs != 31) throw new Exn("FCR " + fs + " unavailable");
+                        p( "fcsr = r"+rt+";");
+                        break;
+                    case 8: {// BC1F, BC1T
+                        tmp = (insn>>>16)&1;
+                        p("if(((fcsr&0x800000)!=0) == (" + tmp + "!=0)) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
+                        break;
+                    }
+                    case 16: {  // Single 
+                        switch(subcode) {
+                            case 0: // ADD.S
+                                p(setFloat(fd,getFloat(fs)+"+"+getFloat(ft)));
+                                break;
+                            case 1: // SUB.S
+                                p(setFloat(fd,getFloat(fs)+"-"+getFloat(ft)));
+                                break;
+                            case 2: // MUL.S
+                                p(setFloat(fd,getFloat(fs)+"*"+getFloat(ft)));
+                                break;
+                            case 3: // DIV.S
+                                p(setFloat(fd,getFloat(fs)+"/"+getFloat(ft)));
+                                break;
+                            case 5: // ABS.S
+                                p(setFloat(fd,"Math.abs("+getFloat(fs)+")"));
+                                break;
+                            case 6: // MOV.S
+                                p("f"+fd+" = f"+fs+"; // MOV.S");
+                                break;
+                            case 7: // NEG.S
+                                p(setFloat(fd,"-"+getFloat(fs)));
+                                break;
+                            case 33: // CVT.D.S
+                                p(setDouble(fd,"(float)"+getFloat(fs)));
+                                break;
+                            case 36: // CVT.W.D
+                                p("switch(fcsr & 3) {");
+                                    indent++;
+                                    p("case 0: f"+fd+" = (int)Math.floor("+getFloat(fs)+"+0.5); break; // Round to nearest");
+                                    p("case 1: f"+fd+" = (int)"+getFloat(fs)+"; break; // Round towards zero");
+                                    p("case 2: f"+fd+" = (int)Math.ceil("+getFloat(fs)+"); break; // Round towards plus infinity");
+                                    p("case 3: f"+fd+" = (int)Math.floor("+getFloat(fs)+"); break; // Round towards minus infinity");
+                                    indent--;
+                                p("}");
+                                break;
+                            case 50: // C.EQ.S
+                                p("fcsr = (fcsr&~0x800000) | (("+getFloat(fs)+"=="+getFloat(ft)+") ? 0x800000 : 0x000000);");
+                                break;
+                            case 60: // C.LT.S
+                                p("fcsr = (fcsr&~0x800000) | (("+getFloat(fs)+"<"+getFloat(ft)+") ? 0x800000 : 0x000000);");
+                                break;
+                            case 62: // C.LE.S
+                                p("fcsr = (fcsr&~0x800000) | (("+getFloat(fs)+"<="+getFloat(ft)+") ? 0x800000 : 0x000000);");
+                                break;                                
+                            default: throw new Exn("Invalid Instruction 17/" + rs + "/" + subcode);
+                        }
+                        break;
+                    }
+                    case 17: { // Double
+                        switch(subcode) {
+                            case 0: // ADD.D
+                                p(setDouble(fd,getDouble(fs)+"+"+getDouble(ft)));
+                                break;
+                            case 1: // SUB.D
+                                p(setDouble(fd,getDouble(fs)+"-"+getDouble(ft)));
+                                break;
+                            case 2: // MUL.D
+                                p(setDouble(fd,getDouble(fs)+"*"+getDouble(ft)));
+                                break;
+                            case 3: // DIV.D
+                                p(setDouble(fd,getDouble(fs)+"/"+getDouble(ft)));
+                                break;
+                            case 5: // ABS.D
+                                p(setDouble(fd,"Math.abs("+getDouble(fs)+")"));
+                                break;
+                            case 6: // MOV.D
+                                p("f"+fd+" = f"+fs+";");
+                                p("f"+(fd+1)+" = f"+(fs+1)+";");
+                                break;
+                            case 7: // NEG.D
+                                p(setDouble(fd,"-"+getDouble(fs)));
+                                break;
+                            case 32: // CVT.S.D
+                                p(setFloat(fd,"(float)"+getDouble(fs)));
+                                break;
+                            case 36: // CVT.W.D
+                                p("switch(fcsr & 3) {");
+                                    indent++;
+                                    p("case 0: f"+fd+" = (int)Math.floor("+getDouble(fs)+"+0.5); break; // Round to nearest");
+                                    p("case 1: f"+fd+" = (int)"+getDouble(fs)+"; break; // Round towards zero");
+                                    p("case 2: f"+fd+" = (int)Math.ceil("+getDouble(fs)+"); break; // Round towards plus infinity");
+                                    p("case 3: f"+fd+" = (int)Math.floor("+getDouble(fs)+"); break; // Round towards minus infinity");
+                                    indent--;
+                                p("}");
+                                break;
+                            case 50: // C.EQ.D
+                                p("fcsr = (fcsr&~0x800000) | (("+getDouble(fs)+"=="+getDouble(ft)+") ? 0x800000 : 0x000000);");                                
+                                break;
+                            case 60: // C.LT.D
+                                p("fcsr = (fcsr&~0x800000) | (("+getDouble(fs)+"<"+getDouble(ft)+") ? 0x800000 : 0x000000);");                                
+                                break;
+                            case 62: // C.LE.D
+                                p("fcsr = (fcsr&~0x800000) | (("+getDouble(fs)+"<="+getDouble(ft)+") ? 0x800000 : 0x000000);");                                
+                                break;                                
+                            default: throw new Exn("Invalid Instruction 17/" + rs + "/" + subcode);
+                        }
+                        break;
+                    }
+                    case 20: { // Integer
+                        switch(subcode) {
+                            case 32: // CVT.S.W
+                                p(" // CVS.S.W");
+                                p(setFloat(fd,"((float)f"+fs+")"));
+                                break;
+                            case 33: // CVT.D.W
+                                p(setDouble(fd,"((double)f"+fs+")"));
+                                break;
+                            default: throw new Exn("Invalid Instruction 17/" + rs + "/" + subcode);
+                        }
+                        break; 
+                    }
+                    default:
+                        throw new Exn("Invalid Instruction 17/" + rs);
+                }
+                break;
+            }
+            case 18: case 19:
+                throw new Exn("coprocessor 2 and 3 instructions not available");
+            case 32: { // LB
+                if(runtimeStats) p("inc(\"LB\");");
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr","tmp");
+                p("tmp = (tmp>>>(((~addr)&3)<<3)) & 0xff;");
+                p("if((tmp&0x80)!=0) tmp |= 0xffffff00; /* sign extend */");
+                p("r"+rt+" = tmp;");
+                break; 
+            }
+            case 33: { // LH
+                if(runtimeStats) p("inc(\"LH\");");
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr","tmp");
+                p("tmp = (tmp>>>(((~addr)&2)<<3)) & 0xffff;");
+                p("if((tmp&0x8000)!=0) tmp |= 0xffff0000; /* sign extend */");
+                p("r"+rt+" = tmp;");
+                break; 
+            }
+            case 34: { // LWL;
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr","tmp");
+                p("r" + rt + " = (r"+rt+"&(0x00ffffff>>>(((~addr)&3)<<3)))|(tmp<<((addr&3)<<3));");
+                break;
+                /*p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                indent++;
+                p("case 0: r"+rt+" = (r"+rt+"&0x00000000)|(tmp<< 0); break;");
+                p("case 1: r"+rt+" = (r"+rt+"&0x000000ff)|(tmp<< 8); break;");
+                p("case 2: r"+rt+" = (r"+rt+"&0x0000ffff)|(tmp<<16); break;");
+                p("case 3: r"+rt+" = (r"+rt+"&0x00ffffff)|(tmp<<24); break;");
+                indent--;
+                p("}");
+                break;*/
+            }
+            case 35: // LW
+                if(runtimeStats) p("inc(\"LW\");");
+                memRead("r" + rs +"+"+signedImmediate,"r"+rt);
+                break;
+            case 36: { // LBU
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr","tmp");
+                p("tmp = (tmp>>>(((~addr)&3)<<3)) & 0xff;");
+                p("r"+rt+" = tmp;");
+                break; 
+            }
+            case 37: { // LHU
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr","tmp");
+                p("tmp = (tmp>>>(((~addr)&2)<<3)) & 0xffff;");
+                p("r"+rt+" = tmp;");
+                break; 
+            }
+            case 38: { // LWR
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr","tmp");
+                p("r" + rt + " = (r"+rt+"&(0xffffff00<<((addr&3)<<3)))|(tmp>>>(((~addr)&3)<<3));");
+                break;
+                
+                /*p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                indent++;
+                p("case 0: r"+rt+" = (r"+rt+"&0xffffff00)|(tmp>>>24); break;");
+                p("case 1: r"+rt+" = (r"+rt+"&0xffff0000)|(tmp>>>16); break;");
+                p("case 2: r"+rt+" = (r"+rt+"&0xff000000)|(tmp>>> 8); break;");
+                p("case 3: r"+rt+" = (r"+rt+"&0x00000000)|(tmp>>> 0); break;");
+                indent--;
+                p("}");
+                break;*/
+                
+            }
+            case 40: { // SB
+                if(runtimeStats) p("inc(\"SB\");");
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr","tmp");
+                p("tmp = (tmp&~(0xff000000>>>((addr&3)<<3)))|((r"+rt+"&0xff)<<(((~addr)&3)<<3));");
+                memWrite("addr","tmp");
+                break;
+            }
+            case 41: { // SH
+                if(runtimeStats) p("inc(\"SH\");");
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr","tmp");
+                p("tmp = (tmp&(0xffff<<((addr&2)<<3)))|((r" + rt + "&0xffff)<<(((~addr)&2)<<3));");
+                memWrite("addr","tmp");
+                break;
+            }
+            case 42: { // SWL
+                p(" // SWL");
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr","tmp");
+                p("tmp = (tmp&(0xffffff00<<(((~addr)&3)<<3)))|(r"+rt+">>>((addr&3)<<3));");
+                memWrite("addr","tmp");
+                break;
+            }
+            case 43: // SW
+                if(runtimeStats) p("inc(\"SW\");");
+                memWrite("r"+rs+"+"+signedImmediate,"r" + rt);
+                break;
+            case 46: { // SWR
+                p(" // SWR");
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr","tmp");
+                p("tmp = (tmp&(0x00ffffff>>>((addr&3)<<3)))|(r"+rt+"<<(((~addr)&3)<<3));");
+                memWrite("addr","tmp");
+                break;
+            }
+            // FEATURE: Need to be atomic if threads
+            case 48: // LWC0/LL
+                memRead("r"+rs+"+"+signedImmediate,"r"+rt);
+                break;
+            case 49: // LWC1
+                memRead("r"+rs+"+"+signedImmediate,"f"+rt);
+                break;
+            // FEATURE: Needs to be atomic if threads
+            case 56: // SWC1/SC
+                memWrite("r"+rs+"+"+signedImmediate,"r"+rt);
+                p("r" + rt + "=1;");
+                break;
+            case 57: // SWC1
+                memWrite("r"+rs+"+"+signedImmediate,"f"+rt);
+                break;
+            default:
+                throw new Exn("Invalid Instruction: " + op + " at " + toHex(pc));
+        }
+    }
+    
+    // Helper functions for emitText
+    // NOTE: memWrite and memRead MUST discard the last two bits of addr
+    private void memWrite(String addr, String target) {
+        if(nullPointerCheck) p("nullPointerCheck(" + addr + ");");
+        if(onePage)
+            p("page[(" + addr + ")>>>2] = " + target + ";");
+        else if(fastMem)
+            p("writePages[("+addr+")>>>"+pageShift+"][(("+addr+")>>>2)&"+toHex((pageSize>>2)-1)+"] = " + target + ";");
+        else
+            p("unsafeMemWrite(" + addr + "," + target + ");");
+    }
+    private void memRead(String addr, String target) {
+        if(nullPointerCheck) p("nullPointerCheck(" + addr + ");");
+        if(onePage)
+            p(target + "= page[(" + addr + ")>>>2];");
+        else if(fastMem)
+            p(target  + " = readPages[("+addr+")>>>"+pageShift+"][(("+addr+")>>>2)&"+toHex((pageSize>>2)-1)+"];");
+        else
+            p(target + " = unsafeMemRead(" + addr + ");");
+    }
+    private static String getFloat(int r) { return "(Float.intBitsToFloat(f"+r+"))"; }
+    private static String getDouble(int r) {
+        return "(Double.longBitsToDouble(((f"+(r+1)+"&0xffffffffL) << 32) | (f"+r+"&0xffffffffL)))";
+    }
+    private static String setFloat(int r, String expr) { return "f"+r+"=Float.floatToRawIntBits("+expr+");"; }
+    private static String setDouble(int r, String expr) {
+        return "{ long l = Double.doubleToLongBits("+expr+"); "+
+            "f"+(r+1)+" = (int)(l >>> 32); f"+r+" = (int)l; }";
+    }
+}
+    
\ No newline at end of file
diff --git a/src/org/xwt/mips/Registers.java b/src/org/xwt/mips/Registers.java
new file mode 100644 (file)
index 0000000..14b2825
--- /dev/null
@@ -0,0 +1,42 @@
+package org.xwt.mips;
+
+interface Registers {
+    // Register Names
+    public final static int ZERO = 0; // Immutable, hardwired to 0
+    public final static int AT = 1;  // Reserved for assembler
+    public final static int K0 = 26; // Reserved for kernel 
+    public final static int K1 = 27; // Reserved for kernel 
+    public final static int GP = 28; // Global pointer (the middle of .sdata/.sbss)
+    public final static int SP = 29; // Stack pointer
+    public final static int FP = 30; // Frame Pointer
+    public final static int RA = 31; // Return Address
+    
+    // Return values (caller saved)
+    public final static int V0 = 2;
+    public final static int V1 = 3;
+    // Argument Registers (caller saved)
+    public final static int A0 = 4; 
+    public final static int A1 = 5;
+    public final static int A2 = 6;
+    public final static int A3 = 7;
+    // Temporaries (caller saved)
+    public final static int T0 = 8;
+    public final static int T1 = 9;
+    public final static int T2 = 10;
+    public final static int T3 = 11;
+    public final static int T4 = 12;
+    public final static int T5 = 13;
+    public final static int T6 = 14;
+    public final static int T7 = 15;
+    public final static int T8 = 24;
+    public final static int T9 = 25;
+    // Saved (callee saved)
+    public final static int S0 = 16;
+    public final static int S1 = 17;
+    public final static int S2 = 18;
+    public final static int S3 = 19;
+    public final static int S4 = 20;
+    public final static int S5 = 21;
+    public final static int S6 = 22;
+    public final static int S7 = 23;
+}
diff --git a/src/org/xwt/mips/Runtime.java b/src/org/xwt/mips/Runtime.java
new file mode 100644 (file)
index 0000000..6a5c4b6
--- /dev/null
@@ -0,0 +1,1256 @@
+// Copyright 2003 Brian Alliet
+// Based on org.xwt.imp.MIPS by Adam Megacz
+// Portions Copyright 2003 Adam Megacz
+
+package org.xwt.mips;
+
+import org.xwt.mips.util.*;
+import java.io.*;
+import java.util.Arrays;
+
+// FEATURE: Look over the public API, make sure we're exposing a bare minimum
+// (we might make this an interface in the future)
+
+public abstract class Runtime implements UsermodeConstants,Registers {
+    /** Pages are 4k in size */
+    protected final int PAGE_SIZE;
+    protected final int PAGE_WORDS;
+    protected final int PAGE_SHIFT;
+    protected final int TOTAL_PAGES;
+    /** This is the upper limit of the pages allocated by the sbrk() syscall. */
+    protected final int BRK_LIMIT;
+    protected final int STACK_BOTTOM;
+
+    /** This is the maximum size of command line arguments */
+    public final static int ARGS_MAX = 1024*1024;
+    
+    /** True if we allow empty pages (_emptyPage) to exist in memory.
+       Empty pages are pages which are allocated by the program but do not contain any
+       data yet (they are all 0s). If empty pages are allowed subclasses must always
+       access main memory with the memRead and memWrite functions */
+    private final boolean allowEmptyPages;
+    /** the "empty page" */
+    private final static int[] _emptyPage = new int[0];
+    
+    protected final static boolean isEmptyPage(int[] page) { return page == _emptyPage; }
+    
+    /** Returns a new empty page (_emptyPage is empty pages are enabled or a new zero'd page) */
+    private final int[] emptyPage() { return allowEmptyPages ? _emptyPage : new int[PAGE_WORDS]; }
+    
+    /** Readable main memory pages */
+    protected final int[][] readPages;
+    /** Writable main memory pages.
+        If the page is writable writePages[x] == readPages[x]; if not writePages[x] == null. */
+    protected final int[][] writePages;
+    
+    /** The current break between the heap and unallocated memory */
+    protected int brkAddr;
+        
+    /** The program's entry point */
+    protected int entryPoint;
+
+    /** The location of the _user_info block (or 0 is there is none) */
+    protected int userInfoBase;
+    protected int userInfoSize;
+    
+    /** The location of the global pointer */
+    protected int gp;
+    
+    /** When the process started */
+    private long startTime;
+    
+    /** State constant: There is no program loaded in memory */
+    public final static int UNINITIALIZED = 0; 
+    /**  Text/Data loaded in memory  */
+    public final static int INITIALIZED = 1;
+    /** Program is executing instructions */
+    public final static int RUNNING = 2;
+    /** Prgram has been started but is paused */
+    public final static int PAUSED = 3;
+    /** Program is executing a callJava() method */
+    public final static int CALLJAVA = 4;
+    /** Program has exited (it cannot currently be restarted) */
+    public final static int DONE = 5;
+        
+    /** The current state (UNINITIALIZED, INITIALIZED, RUNNING, PAUSED, or DONE) */
+    protected int state = UNINITIALIZED;
+    /** @see Runtime#state state */
+    public final int getState() { return state; }
+    
+    /** The exit status if the process (only valid if state==DONE) 
+        @see Runtime#state */
+    protected int exitStatus;
+    public ExecutionException exitException;
+    
+    /** Maximum number of open file descriptors */
+    final static int OPEN_MAX = 256;
+    /** Table containing all open file descriptors. (Entries are null if the fd is not in use */
+    FD[] fds  = new FD[OPEN_MAX];
+        
+    /** Temporary buffer for read/write operations */
+    private byte[] _byteBuf = null;
+    /** Max size of temporary buffer
+        @see Runtime#_byteBuf */
+    private final static int MAX_CHUNK = 15*1024*1024;
+        
+    /** Subclasses should actually execute program in this method. They should continue 
+        executing until state != RUNNING. Only syscall() can modify state. It is safe 
+        to only check the state attribute after a call to syscall() */
+    protected abstract void _execute() throws ExecutionException;
+    
+    /** Subclasses should return the address of the symbol <i>symbol</i> or -1 it it doesn't exits in this method 
+        This method is only required if the call() function is used */
+    protected int lookupSymbol(String symbol) { return -1; }
+    
+    /** Subclasses should returns a CPUState object representing the cpu state */
+    protected abstract CPUState getCPUState();
+    
+    /** Subclasses should set the CPUState to the state held in <i>state</i> */
+    protected abstract void setCPUState(CPUState state);
+
+    static void checkPageSize(int pageSize, int totalPages) throws IllegalArgumentException {
+        if(pageSize < 256) throw new IllegalArgumentException("pageSize too small");
+        if((pageSize&(pageSize-1)) != 0) throw new IllegalArgumentException("pageSize must be a power of two");
+        if((totalPages&(totalPages-1)) != 0) throw new IllegalArgumentException("totalPages must be a power of two");
+        if(totalPages != 1 && totalPages < 256) throw new IllegalArgumentException("totalPages too small");
+        if(totalPages * pageSize < 4*1024*1024) throw new IllegalArgumentException("total memory too small (" + totalPages + "*" + pageSize + ")");
+    }
+    
+    protected Runtime(int pageSize, int totalPages, boolean allowEmptyPages) {
+        this.allowEmptyPages = allowEmptyPages;
+        
+        checkPageSize(pageSize,totalPages);
+        
+        PAGE_SIZE = pageSize;
+        PAGE_WORDS = pageSize>>>2;
+        int pageShift = 0;
+        while(pageSize>>>pageShift != 1) pageShift++;
+        PAGE_SHIFT = pageShift;
+        
+        TOTAL_PAGES = totalPages;
+        
+        readPages = new int[TOTAL_PAGES][];
+        writePages = new int[TOTAL_PAGES][];
+        
+        if(TOTAL_PAGES == 1) {
+            readPages[0] = writePages[0] = new int[PAGE_WORDS];
+            BRK_LIMIT = STACK_BOTTOM = 0;
+        } else {
+            int stackPages = max(TOTAL_PAGES>>>8,(1024*1024)>>>PAGE_SHIFT);
+            STACK_BOTTOM = (TOTAL_PAGES - stackPages) * PAGE_SIZE;
+            // leave some unmapped pages between the stack and the heap
+            BRK_LIMIT = STACK_BOTTOM - 4*PAGE_SIZE;
+        
+            for(int i=0;i<stackPages;i++)
+                readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage();
+        }
+        
+        addFD(new StdinFD(System.in));
+        addFD(new StdoutFD(System.out));
+        addFD(new StdoutFD(System.err));
+    }
+    
+    /** Copy everything from <i>src</i> to <i>addr</i> initializing uninitialized pages if required. 
+       Newly initalized pages will be marked read-only if <i>ro</i> is set */
+    protected final void initPages(int[] src, int addr, boolean ro) {
+        for(int i=0;i<src.length;) {
+            int page = addr >>> PAGE_SHIFT;
+            int start = (addr&(PAGE_SIZE-1))>>2;
+            int elements = min(PAGE_WORDS-start,src.length-i);
+            if(readPages[page]==null) {
+                initPage(page,ro);
+            } else if(!ro) {
+                if(writePages[page] == null) writePages[page] = readPages[page];
+            }
+            System.arraycopy(src,i,readPages[page],start,elements);
+            i += elements;
+            addr += elements*4;
+        }
+    }
+    
+    /** Initialize <i>words</i> of pages starting at <i>addr</i> to 0 */
+    protected final void clearPages(int addr, int words) {
+        for(int i=0;i<words;) {
+            int page = addr >>> PAGE_SHIFT;
+            int start = (addr&(PAGE_SIZE-1))>>2;
+            int elements = min(PAGE_WORDS-start,words-i);
+            if(readPages[page]==null) {
+                readPages[page] = writePages[page] = emptyPage();
+            } else {
+                if(writePages[page] == null) writePages[page] = readPages[page];
+                for(int j=start;j<start+elements;j++) writePages[page][j] = 0;
+            }
+            i += elements;
+            addr += elements*4;
+        }
+    }
+    
+    /** Copies <i>length</i> bytes from the processes memory space starting at
+        <i>addr</i> INTO a java byte array <i>a</i> */
+    public final void copyin(int addr, byte[] buf, int count) throws ReadFaultException {
+        int x=0;
+        if((addr&3)!=0) {
+            int word = memRead(addr&~3);
+            switch(addr&3) {
+                case 1: buf[x++] = (byte)((word>>>16)&0xff); if(--count==0) break;
+                case 2: buf[x++] = (byte)((word>>> 8)&0xff); if(--count==0) break;
+                case 3: buf[x++] = (byte)((word>>> 0)&0xff); if(--count==0) break;
+            }
+            addr = (addr&~3)+4;
+        }
+        if((count&~3) != 0) {
+            int c = count>>>2;
+            int a = addr>>>2;
+            while(c != 0) {
+                int[] page = readPages[a >>> (PAGE_SHIFT-2)];
+                if(page == null) throw new ReadFaultException(a<<2);
+                int index = a&(PAGE_WORDS-1);
+                int n = min(c,PAGE_WORDS-index);
+                if(page != _emptyPage) {
+                    for(int i=0;i<n;i++,x+=4) {
+                        int word = page[index+i];
+                        buf[x+0] = (byte)((word>>>24)&0xff); buf[x+1] = (byte)((word>>>16)&0xff);
+                        buf[x+2] = (byte)((word>>> 8)&0xff); buf[x+3] = (byte)((word>>> 0)&0xff);                        
+                    }
+                }
+                a += n; c -=n;
+            }
+            addr = a<<2; count &=3;
+        }
+        if(count != 0) {
+            int word = memRead(addr);
+            switch(count) {
+                case 3: buf[x+2] = (byte)((word>>>8)&0xff);
+                case 2: buf[x+1] = (byte)((word>>>16)&0xff);
+                case 1: buf[x+0] = (byte)((word>>>24)&0xff);
+            }
+        }
+    }
+    
+    /** Copies <i>length</i> bytes OUT OF the java array <i>a</i> into the processes memory
+        space at <i>addr</i> */
+    public final void copyout(byte[] buf, int addr, int count) throws FaultException {
+        int x=0;
+        if((addr&3)!=0) {
+            int word = memRead(addr&~3);
+            switch(addr&3) {
+                case 1: word = (word&0xff00ffff)|((buf[x++]&0xff)<<16); if(--count==0) break;
+                case 2: word = (word&0xffff00ff)|((buf[x++]&0xff)<< 8); if(--count==0) break;
+                case 3: word = (word&0xffffff00)|((buf[x++]&0xff)<< 0); if(--count==0) break;
+            }
+            memWrite(addr&~3,word);
+            addr += x;
+        }
+        if((count&~3) != 0) {
+            int c = count>>>2;
+            int a = addr>>>2;
+            while(c != 0) {
+                int[] page = writePages[a >>> (PAGE_SHIFT-2)];
+                if(page == null) throw new WriteFaultException(a<<2);
+                if(page == _emptyPage) page = initPage(a >>> (PAGE_SHIFT-2));
+                int index = a&(PAGE_WORDS-1);
+                int n = min(c,PAGE_WORDS-index);
+                for(int i=0;i<n;i++,x+=4)
+                    page[index+i] = ((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8)|((buf[x+3]&0xff)<<0);
+                a += n; c -=n;
+            }
+            addr = a<<2; count&=3;
+        }
+        if(count != 0) {
+            int word = memRead(addr);
+            switch(count) {
+                case 1: word = (word&0x00ffffff)|((buf[x+0]&0xff)<<24); break;
+                case 2: word = (word&0x0000ffff)|((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16); break;
+                case 3: word = (word&0x000000ff)|((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8); break;
+            }
+            memWrite(addr,word);
+        }
+    }
+    
+    public final void memcpy(int dst, int src, int count) throws FaultException {
+        if((dst&3) == 0 && (src&3)==0) {
+            if((count&~3) != 0) {
+                int c = count>>2;
+                int s = src>>>2;
+                int d = dst>>>2;
+                while(c != 0) {
+                    int[] srcPage = readPages[s>>>(PAGE_SHIFT-2)];
+                    if(srcPage == null) throw new ReadFaultException(s<<2);
+                    int[] dstPage = writePages[d>>>(PAGE_SHIFT-2)];
+                    if(dstPage == null) throw new WriteFaultException(d<<2);
+                    int srcIndex = (s&(PAGE_WORDS-1));
+                    int dstIndex = (d&(PAGE_WORDS-1));
+                    int n = min(c,PAGE_WORDS-max(srcIndex,dstIndex));
+                    if(srcPage != _emptyPage) {
+                        if(dstPage == _emptyPage) dstPage = initPage(d>>>(PAGE_SHIFT-2));
+                        System.arraycopy(srcPage,srcIndex,dstPage,dstIndex,n);
+                    } else if(srcPage == _emptyPage && dstPage != _emptyPage) {
+                        Arrays.fill(dstPage,dstIndex,dstIndex+n,0);
+                    }
+                    s += n; d += n; c -= n;
+                }
+                src = s<<2; dst = d<<2; count&=3;
+            }
+            if(count != 0) {
+                int word1 = memRead(src);
+                int word2 = memRead(dst);
+                switch(count) {
+                    case 1: memWrite(dst,(word1&0xff000000)|(word2&0x00ffffff)); break;
+                    case 2: memWrite(dst,(word1&0xffff0000)|(word2&0x0000ffff)); break;
+                    case 3: memWrite(dst,(word1&0xffffff00)|(word2&0x000000ff)); break;
+                }
+            }
+        } else {
+            while(count > 0) {
+                int n = min(count,MAX_CHUNK);
+                byte[] buf = byteBuf(n);
+                copyin(src,buf,n);
+                copyout(buf,dst,n);
+                count -= n; src += n; dst += n;
+            }
+        }
+    }
+    
+    public final void memset(int addr, int ch, int count) throws FaultException {
+        int fourBytes = ((ch&0xff)<<24)|((ch&0xff)<<16)|((ch&0xff)<<8)|((ch&0xff)<<0);
+        if((addr&3)!=0) {
+            int word = memRead(addr&~3);
+            switch(addr&3) {
+                case 1: word = (word&0xff00ffff)|((ch&0xff)<<16); if(--count==0) break;
+                case 2: word = (word&0xffff00ff)|((ch&0xff)<< 8); if(--count==0) break;
+                case 3: word = (word&0xffffff00)|((ch&0xff)<< 0); if(--count==0) break;
+            }
+            memWrite(addr&~3,word);
+            addr = (addr&~3)+4;
+        }
+        if((count&~3) != 0) {
+            int c = count>>2;
+            int a = addr>>>2;
+            while(c != 0) {
+                int[] page = readPages[a>>>(PAGE_SHIFT-2)];
+                if(page == null) throw new WriteFaultException(a<<2);
+                int index = (a&(PAGE_WORDS-1));
+                int n = min(c,PAGE_WORDS-index);
+                if(page != _emptyPage || ch != 0) {
+                    if(page == _emptyPage) page = initPage(a>>>(PAGE_SHIFT-2));
+                    Arrays.fill(page,index,index+n,fourBytes);
+                }
+                a += n; c -= n;
+            }
+            addr = a<<2; count&=3;
+        }
+        if(count != 0) {
+            int word = memRead(addr);
+            switch(count) {
+                case 1: word = (word&0x00ffffff)|(fourBytes&0xff000000); break;
+                case 2: word = (word&0x0000ffff)|(fourBytes&0xffff0000); break;
+                case 3: word = (word&0x000000ff)|(fourBytes&0xffffff00); break;
+            }
+            memWrite(addr,word);
+        }
+    }
+    
+    /** Read a word from the processes memory at <i>addr</i> */
+    public final int memRead(int addr) throws ReadFaultException  {
+        if((addr & 3) != 0) throw new ReadFaultException(addr);
+        return unsafeMemRead(addr);
+    }
+       
+    protected final int unsafeMemRead(int addr) throws ReadFaultException {
+        int page = addr >>> PAGE_SHIFT;
+        int entry = (addr >>> 2) & (PAGE_WORDS-1);
+        try {
+            return readPages[page][entry];
+        } catch(ArrayIndexOutOfBoundsException e) {
+            if(page < 0) throw e; // should never happen
+            if(page >= readPages.length) throw new ReadFaultException(addr);
+            if(readPages[page] != _emptyPage) throw e; // should never happen
+            initPage(page);
+            return 0;
+        } catch(NullPointerException e) {
+            throw new ReadFaultException(addr);
+        }
+    }
+    
+    /** Writes a word to the processes memory at <i>addr</i> */
+    public final void memWrite(int addr, int value) throws WriteFaultException  {
+        if((addr & 3) != 0) throw new WriteFaultException(addr);
+        unsafeMemWrite(addr,value);
+    }
+    
+    protected final void unsafeMemWrite(int addr, int value) throws WriteFaultException {
+        int page = addr >>> PAGE_SHIFT;
+        int entry = (addr>>>2)&(PAGE_WORDS-1);
+        try {
+            writePages[page][entry] = value;
+        } catch(ArrayIndexOutOfBoundsException e) {
+            if(page < 0) throw e;// should never happen
+            if(page >= writePages.length) throw new WriteFaultException(addr);
+            if(readPages[page] != _emptyPage) throw e; // should never happen
+            initPage(page);
+            writePages[page][entry] = value;
+        } catch(NullPointerException e) {
+            throw new WriteFaultException(addr);
+        }
+    }
+    
+    /** Created a new non-empty writable page at page number <i>page</i> */
+    private final int[] initPage(int page) { return initPage(page,false); }
+    /** Created a new non-empty page at page number <i>page</i>. If <i>ro</i> is set the page will be read-only */
+    private final int[] initPage(int page, boolean ro) {
+        int[] buf = new int[PAGE_WORDS];
+        writePages[page] = ro ? null : buf;
+        readPages[page] = buf;
+        return buf;
+    }
+    
+    /** Returns the exit status of the process. (only valid if state == DONE) 
+        @see Runtime#state */
+    public final int exitStatus() {
+        if(state != DONE) throw new IllegalStateException("exitStatus() called in an inappropriate state");
+        return exitStatus;
+    }
+        
+    private int addStringArray(String[] strings, int topAddr) {
+        int count = strings.length;
+        int total = 0; /* null last table entry  */
+        for(int i=0;i<count;i++) total += strings[i].length() + 1;
+        if(total >= ARGS_MAX) throw new IllegalArgumentException("arguments/environ too big");
+        total += (count+1)*4;
+        int start = (topAddr - total)&~3;
+        int addr = start + (count+1)*4;
+        int[] table = new int[count+1];
+        try {
+            for(int i=0;i<count;i++) {
+                byte[] a = getBytes(strings[i]);
+                table[i] = addr;
+                copyout(a,addr,a.length);
+                memset(addr+a.length,0,1);
+                addr += a.length + 1;
+            }
+            addr=start;
+            for(int i=0;i<count+1;i++) {
+                memWrite(addr,table[i]);
+                addr += 4;
+            }
+        } catch(FaultException e) {
+            // should never happen
+            throw new Error(e);
+        }
+        return start;
+    }
+    
+    protected String[] createEnv(String[] extra) { if(extra == null) extra = new String[0]; return extra; }
+    
+    /** Sets word number <i>index</i> in the _user_info table to <i>word</i>
+     * The user_info table is a chunk of memory in the program's memory defined by the
+     * symbol "user_info". The compiler/interpreter automatically determine the size
+     * and location of the user_info table from the ELF symbol table. setUserInfo and
+     * getUserInfo are used to modify the words in the user_info table. */
+    public void setUserInfo(int index, int word) {
+        if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
+        try {
+            memWrite(userInfoBase+index*4,word);
+        } catch(FaultException e) { throw new Error("should never happen: " + e); }
+    }
+    
+    /** Returns the word in the _user_info table entry <i>index</i>
+        @see Runtime#setUserInfo(int,int) setUserInfo */
+    public int getUserInfo(int index) {
+        if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
+        try {
+            return memRead(userInfoBase+index*4);
+        } catch(FaultException e) { throw new Error("should never happen: " + e); }
+    }
+    
+    /** Calls _execute() (subclass's execute()) and catches exceptions */
+    private void __execute() {
+        try {
+            _execute();
+        } catch(FaultException e) {
+            e.printStackTrace();
+            sys_exit(128+11); // SIGSEGV
+            exitException = e;
+        } catch(ExecutionException e) {
+            e.printStackTrace();
+            System.err.println(e);
+            sys_exit(128+4); // SIGILL
+            exitException = e;
+        }
+    }
+    
+    /** Executes the process until the PAUSE syscall is invoked or the process exits. Returns true if the process exited. */
+    public final boolean execute()  {
+        if(state != PAUSED) throw new IllegalStateException("execute() called in inappropriate state");
+        if(startTime == 0) startTime = System.currentTimeMillis();
+        state = RUNNING;
+        __execute();
+        if(state != PAUSED && state != DONE) throw new IllegalStateException("execute() ended up in an inappropriate state (" + state + ")");
+        return state == DONE;
+    }
+    
+    public final int run() { return run(null); }
+    public final int run(String argv0, String[] rest) {
+        String[] args = new String[rest.length+1];
+        System.arraycopy(rest,0,args,1,rest.length);
+        args[0] = argv0;
+        return run(args);
+    }
+    public final int run(String[] args) { return run(args,null); }
+    
+    /** Runs the process until it exits and returns the exit status.
+        If the process executes the PAUSE syscall execution will be paused for 500ms and a warning will be displayed */
+    public final int run(String[] args, String[] env) {
+        start(args,env);
+        for(;;) {
+            if(execute()) break;
+            System.err.println("WARNING: Pause requested while executing run()");
+            try { Thread.sleep(500); } catch(InterruptedException e) { /* noop */ }
+        }
+        return exitStatus();
+    }
+
+    public final void start() { start(null); }
+    public final void start(String[] args) { start(args,null); }
+    
+    /** Initializes the process and prepairs it to be executed with execute() */
+    public final void start(String[] args, String[] environ)  {
+        int sp, argsAddr, envAddr;
+        if(state != INITIALIZED) throw new IllegalStateException("start() called in inappropriate state");
+
+        if(args == null) args = new String[]{getClass().getName()};
+        
+        sp = TOTAL_PAGES*PAGE_SIZE-512;
+        sp = argsAddr = addStringArray(args,sp);
+        sp = envAddr = addStringArray(createEnv(environ),sp);
+        sp &= ~15;
+        
+        CPUState cpuState = new CPUState();
+        cpuState.r[A0] = argsAddr;
+        cpuState.r[A1] = envAddr;
+        cpuState.r[SP] = sp;
+        cpuState.r[RA] = 0xdeadbeef;
+        cpuState.r[GP] = gp;
+        cpuState.pc = entryPoint;
+        setCPUState(cpuState);
+                
+        state = PAUSED;
+        
+        _start();
+    }
+    
+    /** Hook for subclasses to do their own startup */
+    protected void _start() { /* noop */ }
+    
+    public final int call(String sym) throws CallException { return call(sym,0,0,0,0,0,0,0); }
+    public final int call(String sym, int a0) throws CallException  { return call(sym,a0,0,0,0,0,0,0); }
+    public final int call(String sym, int a0, int a1) throws CallException  { return call(sym,a0,a1,0,0,0,0,0); }
+    public final int call(String sym, int a0, int a1, int a2) throws CallException  { return call(sym,a0,a1,a2,0,0,0,0); }
+    public final int call(String sym, int a0, int a1, int a2, int a3) throws CallException  { return call(sym,a0,a1,a2,a3,0,0,0); }
+    public final int call(String sym, int a0, int a1, int a2, int a3, int a4) throws CallException  { return call(sym,a0,a1,a2,a3,a4,0,0); }
+    public final int call(String sym, int a0, int a1, int a2, int a3, int a4, int a5) throws CallException  { return call(sym,a0,a1,a2,a3,a4,a5,0); }
+    
+    /** Calls a function in the process with the given arguments */
+    public final int call(String sym, int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws CallException {
+        int func = lookupSymbol(sym);
+        if(func == -1) throw new CallException(sym + " not found");
+        int helper = lookupSymbol("_call_helper");
+        if(helper == -1) throw new CallException("_call_helper not found");
+        return call(helper,func,a0,a1,a2,a3,a4,a5,a6);
+    }
+    
+    /** Executes the code at <i>addr</i> in the process setting A0-A3 and S0-S3 to the given arguments
+        and returns the contents of V1 when the the pause syscall is invoked */
+    public final int call(int addr, int a0, int a1, int a2, int a3, int s0, int s1, int s2, int s3) {
+        if(state != PAUSED && state != CALLJAVA) throw new IllegalStateException("call() called in inappropriate state");
+        int oldState = state;
+        CPUState saved = getCPUState();
+        CPUState cpustate = new CPUState();
+        cpustate.r[SP] = saved.r[SP]&~15;
+        cpustate.r[RA] = 0xdeadbeef;
+        cpustate.r[A0] = a0;
+        cpustate.r[A1] = a1;
+        cpustate.r[A2] = a2;
+        cpustate.r[A3] = a3;
+        cpustate.r[S0] = s0;
+        cpustate.r[S1] = s1;
+        cpustate.r[S2] = s2;
+        cpustate.r[S3] = s3;
+        cpustate.r[GP] = gp;
+        cpustate.pc = addr;
+        
+        state = RUNNING;
+
+        setCPUState(cpustate);
+        __execute();
+        cpustate = getCPUState();
+        setCPUState(saved);
+
+        if(state != PAUSED)
+            System.out.println("WARNING: Process exit()ed while servicing a call() request");
+        else
+            state = oldState;
+        
+        return cpustate.r[V1];
+    }
+    
+    // FEATURE: This is ugly - we should have some kind of way  to specify a callback rather than requiring subclassing
+    protected int callJava(int a, int b, int c, int d) {
+        System.err.println("WARNING: Default implementation of callJava() called with args " + toHex(a) + "," + toHex(b) + "," + toHex(c) + "," + toHex(d));
+        return 0;
+    }
+    
+    /** Determines if the process can access <i>fileName</i>. The default implementation simply logs 
+        the request and allows it */
+    protected boolean allowFileAccess(String fileName, boolean write) {
+        //System.err.println("Allowing " + (write?"write":"read-only") + " access to " + fileName);
+        return true;
+    }
+    
+    /** Allocated an entry in the FileDescriptor table for <i>fd</i> and returns the number.
+        Returns -1 if the table is full. This can be used by subclasses to use custom file
+        descriptors */
+    public int addFD(FD fd) {
+        int i;
+        for(i=0;i<OPEN_MAX;i++) if(fds[i] == null) break;
+        if(i==OPEN_MAX) return -1;
+        fds[i] = fd;
+        return i;
+    }
+
+    /** Closes file descriptor <i>fdn</i> and removes it from the file descriptor table */
+    public boolean closeFD(int fdn) {
+        if(fdn < 0 || fdn >= OPEN_MAX) return false;
+        if(fds[fdn] == null) return false;
+        fds[fdn].close();
+        fds[fdn] = null;        
+        return true;
+    }
+
+    // FEATURE: These should be pulled in from UsermodeConstants but fcntl.h is hard to parse
+    public static final int RD_ONLY = 0;
+    public static final int WR_ONLY = 1;
+    public static final int RDWR = 2;
+    
+    public static final int O_CREAT = 0x0200;
+    public static final int O_EXCL = 0x0800;
+    public static final int O_APPEND = 0x0008;
+    public static final int O_TRUNC = 0x0400;
+    public static final int O_NONBLOCK = 0x4000;
+    
+    // FEATURE: Lots of duplicate code between this and UnixRuntime.HostFS.open()
+    protected FD open(String path, int flags, int mode) throws IOException {
+        final File f = new File(path);
+        // NOTE: createNewFile is a Java2 function
+        if((flags & (O_EXCL|O_CREAT)) == (O_EXCL|O_CREAT))
+            if(!f.createNewFile()) throw new ErrnoException(EEXIST);
+        if(!f.exists() && (flags&O_CREAT) == 0) return null;
+        if(f.isDirectory()) return null;
+        final SeekableFile sf = new SeekableFile(path,mode!=RD_ONLY);
+        if((flags&O_TRUNC)!=0) sf.setLength(0);
+        return new SeekableFD(sf,flags) {
+            protected FStat _fstat() { return new HostFStat(f) {
+                public int size() {
+                    try { return sf.length(); } catch(IOException e) { return 0; }
+                }
+            };}
+        };
+    }
+    
+    /** The open syscall */
+    private int sys_open(int addr, int flags, int mode) {
+        if((flags & O_NONBLOCK) != 0) {
+            System.err.println("WARNING: O_NONBLOCK not supported");
+            return -EOPNOTSUPP;
+        }
+
+        try {
+            FD fd = open(cstring(addr),flags,mode);
+            if(fd == null) return -ENOENT;
+            int fdn = addFD(fd);
+            if(fdn == -1) {
+                fd.close();
+                return -ENFILE;
+            }
+            return fdn;
+        }
+        catch(ErrnoException e) { return -e.errno; }
+        catch(FileNotFoundException e) {
+            if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
+            return -ENOENT;
+        }
+        catch(IOException e) { return -EIO; }
+        catch(FaultException e) { return -EFAULT; }
+    }
+
+    /** The write syscall */
+    private int sys_write(int fdn, int addr, int count) {
+        count = Math.min(count,MAX_CHUNK);
+        if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
+        if(fds[fdn] == null || !fds[fdn].writable()) return -EBADFD;
+        try {
+            byte[] buf = byteBuf(count);
+            copyin(addr,buf,count);
+            return fds[fdn].write(buf,0,count);
+        } catch(FaultException e) {
+            System.err.println(e);
+            return -EFAULT;
+        } catch(IOException e) {
+            // FEATURE: We should support signals and send a SIGPIPE
+            if(e.getMessage().equals("Pipe closed")) return sys_exit(128+13);
+            System.err.println(e);
+            return -EIO;
+        }
+    }
+
+    /** The read syscall */
+    private int sys_read(int fdn, int addr, int count) {
+        count = Math.min(count,MAX_CHUNK);
+        if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
+        if(fds[fdn] == null || !fds[fdn].readable()) return -EBADFD;
+        try {
+            byte[] buf = byteBuf(count);
+            int n = fds[fdn].read(buf,0,count);
+            copyout(buf,addr,n);
+            return n;
+        } catch(FaultException e) {
+            System.err.println(e);
+            return -EFAULT;
+        } catch(IOException e) {
+            System.err.println(e);
+            return -EIO;
+        }
+    }
+    
+    /** The close syscall */
+    private int sys_close(int fdn) {
+        return closeFD(fdn) ? 0 : -EBADFD;
+    }
+
+    
+    /** The seek syscall */
+    private int sys_lseek(int fdn, int offset, int whence) {
+        if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
+        if(fds[fdn] == null) return -EBADFD;
+        if(whence != SEEK_SET && whence !=  SEEK_CUR && whence !=  SEEK_END) return -EINVAL;
+        try {
+            int n = fds[fdn].seek(offset,whence);
+            return n < 0 ? -ESPIPE : n;
+        } catch(IOException e) {
+            return -ESPIPE;
+        }
+    }
+    
+    /** The stat/fstat syscall helper */
+    int stat(FStat fs, int addr) {
+        try {
+            memWrite(addr+0,(fs.dev()<<16)|(fs.inode()&0xffff)); // st_dev (top 16), // st_ino (bottom 16)
+            memWrite(addr+4,((fs.type()&0xf000))|(fs.mode()&0xfff)); // st_mode
+            memWrite(addr+8,1<<16); // st_nlink (top 16) // st_uid (bottom 16)
+            memWrite(addr+12,0); // st_gid (top 16) // st_rdev (bottom 16)
+            memWrite(addr+16,fs.size()); // st_size
+            memWrite(addr+20,fs.atime()); // st_atime
+            // memWrite(addr+24,0) // st_spare1
+            memWrite(addr+28,fs.mtime()); // st_mtime
+            // memWrite(addr+32,0) // st_spare2
+            memWrite(addr+36,fs.ctime()); // st_ctime
+            // memWrite(addr+40,0) // st_spare3
+            memWrite(addr+44,fs.blksize()); // st_bklsize;
+            memWrite(addr+48,fs.blocks()); // st_blocks
+            // memWrite(addr+52,0) // st_spare4[0]
+            // memWrite(addr+56,0) // st_spare4[1]
+        } catch(FaultException e) {
+            System.err.println(e);
+            return -EFAULT;
+        }
+        return 0;
+    }
+    
+    /** The fstat syscall */
+    private int sys_fstat(int fdn, int addr) {
+        if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
+        if(fds[fdn] == null) return -EBADFD;
+        return stat(fds[fdn].fstat(),addr);
+    }
+    
+    /*
+    struct timeval {
+    long tv_sec;
+    long tv_usec;
+    };
+    */
+    private int sys_gettimeofday(int timevalAddr, int timezoneAddr) {
+        long now = System.currentTimeMillis();
+        int tv_sec = (int)(now / 1000);
+        int tv_usec = (int)((now%1000)*1000);
+        try {
+            memWrite(timevalAddr+0,tv_sec);
+            memWrite(timevalAddr+4,tv_usec);
+            return 0;
+        } catch(FaultException e) {
+            return -EFAULT;
+        }
+    }
+    
+    private int sys_sleep(int sec) {
+        if(sec < 0) sec = Integer.MAX_VALUE;
+        try {
+            Thread.sleep((long)sec*1000);
+            return 0;
+        } catch(InterruptedException e) {
+            return -1;
+        }
+    }
+    
+    /*
+      #define _CLOCKS_PER_SEC_ 1000
+      #define    _CLOCK_T_    unsigned long
+    struct tms {
+      clock_t   tms_utime;
+      clock_t   tms_stime;
+      clock_t   tms_cutime;    
+      clock_t   tms_cstime;
+    };*/
+   
+    private int sys_times(int tms) {
+        long now = System.currentTimeMillis();
+        int userTime = (int)((now - startTime)/16);
+        int sysTime = (int)((now - startTime)/16);
+        
+        try {
+            if(tms!=0) {
+                memWrite(tms+0,userTime);
+                memWrite(tms+4,sysTime);
+                memWrite(tms+8,userTime);
+                memWrite(tms+12,sysTime);
+            }
+        } catch(FaultException e) {
+            return -EFAULT;
+        }
+        return (int)now;
+    }
+    
+    private int sys_sysconf(int n) {
+        switch(n) {
+            case _SC_CLK_TCK: return 1000;
+            default:
+                System.err.println("WARNING: Attempted to use unknown sysconf key: " + n);
+                return -EINVAL;
+        }
+    }
+    
+    /** The sbrk syscall. This can also be used by subclasses to allocate memory.
+        <i>incr</i> is how much to increase the break by */
+    public int sbrk(int incr) {
+        if(incr < 0) return -ENOMEM;
+        if(incr==0) return brkAddr;
+        incr = (incr+3)&~3;
+        int oldBrk = brkAddr;
+        int newBrk = oldBrk + incr;
+        if(TOTAL_PAGES == 1) {
+            CPUState state = getCPUState();
+            if(newBrk >= state.r[SP] - 65536) {
+                System.err.println("WARNING: brk too close to stack pointer");
+                return -ENOMEM;
+            }
+        } else if(newBrk >= BRK_LIMIT) {
+            System.err.println("WARNING: Hit BRK_LIMIT");
+            return -ENOMEM;
+        }
+        if(TOTAL_PAGES != 1) {
+            try {
+                for(int i=(oldBrk+PAGE_SIZE-1)>>>PAGE_SHIFT;i<((newBrk+PAGE_SIZE-1)>>>PAGE_SHIFT);i++)
+                    readPages[i] = writePages[i] = emptyPage();
+            } catch(OutOfMemoryError e) {
+                System.err.println("WARNING: Caught OOM Exception in sbrk: " + e);
+                return -ENOMEM;
+            }
+        }
+        brkAddr = newBrk;
+        return oldBrk;
+    }
+
+    /** The getpid syscall */
+    private int sys_getpid() { return getPid(); }
+    protected int getPid() { return 1; }
+    
+    private int sys_calljava(int a, int b, int c, int d) {
+        if(state != RUNNING) throw new IllegalStateException("wound up calling sys_calljava while not in RUNNING");
+        state = CALLJAVA;
+        int ret = callJava(a,b,c,d);
+        state = RUNNING;
+        return ret;
+    }
+        
+    private int sys_pause() {
+        state = PAUSED;
+        return 0;
+    }
+    
+    private int sys_getpagesize() { return TOTAL_PAGES == 1 ? 4096 : PAGE_SIZE; }
+    
+    private int sys_isatty(int fdn) {
+        if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
+        if(fds[fdn] == null) return -EBADFD;
+        return fds[fdn].isatty() ? 1 : 0;
+    }
+
+    
+    /** Hook for subclasses to do something when the process exits (MUST set state = DONE) */
+    protected void _exit() { state = DONE; }
+    private int sys_exit(int status) {
+        exitStatus = status;
+        for(int i=0;i<fds.length;i++) if(fds[i] != null) sys_close(i);
+        _exit();
+        return 0;
+    }
+       
+    private int sys_fcntl(int fdn, int cmd, int arg) {
+        final int F_DUPFD = 0;
+        final int F_GETFL = 3;
+        int i;
+            
+        if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
+        if(fds[fdn] == null) return -EBADFD;
+        FD fd = fds[fdn];
+        
+        switch(cmd) {
+            case F_DUPFD:
+                if(arg < 0 || arg >= OPEN_MAX) return -EINVAL;
+                for(i=arg;i<OPEN_MAX;i++) if(fds[i]==null) break;
+                if(i==OPEN_MAX) return -EMFILE;
+                fds[i] = fd.dup();
+                return 0;
+            case F_GETFL:
+                int flags = 0;
+                if(fd.writable() && fd.readable())  flags = 2;
+                else if(fd.writable()) flags = 1;
+                return flags;
+            default:
+                System.err.println("WARNING: Unknown fcntl command: " + cmd);
+                return -ENOSYS;
+        }
+    }
+            
+    /** The syscall dispatcher.
+        The should be called by subclasses when the syscall instruction is invoked.
+        <i>syscall</i> should be the contents of V0 and <i>a</i>, <i>b</i>, <i>c</i>, and <i>d</i> should be 
+        the contenst of A0, A1, A2, and A3. The call MAY change the state
+        @see Runtime#state state */
+    protected int syscall(int syscall, int a, int b, int c, int d) {
+        switch(syscall) {
+            case SYS_null: return 0;
+            case SYS_exit: return sys_exit(a);
+            case SYS_pause: return sys_pause();
+            case SYS_write: return sys_write(a,b,c);
+            case SYS_fstat: return sys_fstat(a,b);
+            case SYS_sbrk: return sbrk(a);
+            case SYS_open: return sys_open(a,b,c);
+            case SYS_close: return sys_close(a);
+            case SYS_read: return sys_read(a,b,c);
+            case SYS_lseek: return sys_lseek(a,b,c);
+            case SYS_getpid: return sys_getpid();
+            case SYS_calljava: return sys_calljava(a,b,c,d);
+            case SYS_gettimeofday: return sys_gettimeofday(a,b);
+            case SYS_sleep: return sys_sleep(a);
+            case SYS_times: return sys_times(a);
+            case SYS_getpagesize: return sys_getpagesize();
+            case SYS_isatty: return sys_isatty(a);
+            case SYS_fcntl: return sys_fcntl(a,b,c);
+            case SYS_sysconf: return sys_sysconf(a);
+
+            case SYS_kill:
+            case SYS_fork:
+            case SYS_pipe:
+            case SYS_dup2:
+            case SYS_waitpid:
+            case SYS_stat:
+            case SYS_mkdir:
+            case SYS_getcwd:
+            case SYS_chdir:
+                System.err.println("Attempted to use a UnixRuntime syscall in Runtime (" + syscall + ")");
+                return -ENOSYS;
+            default:
+                System.err.println("Attempted to use unknown syscall: " + syscall);
+                return -ENOSYS;
+        }
+    }
+    
+    public int xmalloc(int size) { int p=malloc(size); if(p==0) throw new RuntimeException("malloc() failed"); return p; }
+    public int xrealloc(int addr,int newsize) { int p=realloc(addr,newsize); if(p==0) throw new RuntimeException("realloc() failed"); return p; }
+    public int realloc(int addr, int newsize) { try { return call("realloc",addr,newsize); } catch(CallException e) { return 0; } }
+    public int malloc(int size) { try { return call("malloc",size); } catch(CallException e) { return 0; } }
+    public void free(int p) { try { if(p!=0) call("free",p); } catch(CallException e) { /*noop*/ } }
+    
+    /** Helper function to create a cstring in main memory */
+    public int strdup(String s) {
+        byte[] a;
+        if(s == null) s = "(null)";
+        byte[] a2 = getBytes(s);
+        a = new byte[a2.length+1];
+        System.arraycopy(a2,0,a,0,a2.length);
+        int addr = malloc(a.length);
+        if(addr == 0) return 0;
+        try {
+            copyout(a,addr,a.length);
+        } catch(FaultException e) {
+            free(addr);
+            return 0;
+        }
+        return addr;
+    }
+    
+    /** Helper function to read a cstring from main memory */
+    public String cstring(int addr) throws ReadFaultException {
+        StringBuffer sb = new StringBuffer();
+        for(;;) {
+            int word = memRead(addr&~3);
+            switch(addr&3) {
+                case 0: if(((word>>>24)&0xff)==0) return sb.toString(); sb.append((char)((word>>>24)&0xff)); addr++;
+                case 1: if(((word>>>16)&0xff)==0) return sb.toString(); sb.append((char)((word>>>16)&0xff)); addr++;
+                case 2: if(((word>>> 8)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 8)&0xff)); addr++;
+                case 3: if(((word>>> 0)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 0)&0xff)); addr++;
+            }
+        }
+    }
+    
+    /** File Descriptor class */
+    public static abstract class FD {
+        private int refCount = 1;
+    
+        /** returns true if the fd is readable */
+        public boolean readable() { return false; }
+        /** returns true if the fd is writable */
+        public boolean writable() { return false; }
+        
+        /** Read some bytes. Should return the number of bytes read, 0 on EOF, or throw an IOException on error */
+        public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
+        /** Write. Should return the number of bytes written or throw an IOException on error */
+        public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
+
+        /** Seek in the filedescriptor. Whence is SEEK_SET, SEEK_CUR, or SEEK_END. Should return -1 on error or the new position. */
+        public int seek(int n, int whence)  throws IOException  { return -1; }
+        
+        /** Should return true if this is a tty */
+        public boolean isatty() { return false; }
+        
+        private FStat cachedFStat = null;
+        public final FStat fstat() {
+            if(cachedFStat == null) cachedFStat = _fstat(); 
+            return cachedFStat;
+        }
+        
+        protected abstract FStat _fstat();
+        
+        /** Closes the fd */
+        public final void close() { if(--refCount==0) _close(); }
+        protected void _close() { /* noop*/ }
+        
+        FD dup() { refCount++; return this; }
+    }
+        
+    /** FileDescriptor class for normal files */
+    public abstract static class SeekableFD extends FD {
+        private final int flags;
+        private final SeekableData data;
+        public boolean readable() { return (flags&3) != WR_ONLY; }
+        public boolean writable() { return (flags&3) != RD_ONLY; }
+        
+        SeekableFD(SeekableData data, int flags) { this.data = data; this.flags = flags; }
+        
+        protected abstract FStat _fstat();
+
+        public int seek(int n, int whence) throws IOException {
+            switch(whence) {
+                case SEEK_SET: break;
+                case SEEK_CUR: n += data.pos(); break;
+                case SEEK_END: n += data.length(); break;
+                default: return -1;
+            }
+            data.seek(n);
+            return n;
+        }
+        
+        public int write(byte[] a, int off, int length) throws IOException {
+            // NOTE: There is race condition here but we can't fix it in pure java
+            if((flags&O_APPEND) != 0) seek(0,SEEK_END);
+            return data.write(a,off,length);
+        }
+        
+        public int read(byte[] a, int off, int length) throws IOException {
+            int n = data.read(a,off,length);
+            return n < 0 ? 0 : n;
+        }
+        
+        protected void _close() { try { data.close(); } catch(IOException e) { /*ignore*/ } }        
+    }
+    
+    public static class OutputStreamFD extends FD {
+        private OutputStream os;
+        public boolean writable() { return true; }
+        public OutputStreamFD(OutputStream os) { this.os = os; }
+        public int write(byte[] a, int off, int length) throws IOException { os.write(a,off,length); return length; }
+        public void _close() { try { os.close(); } catch(IOException e) { /*ignore*/ }  }
+        public FStat _fstat() { return new FStat(); }
+    }
+    
+    public static class InputStreamFD extends FD {
+        private InputStream is;
+        public boolean readable() { return true; }
+        public InputStreamFD(InputStream is) { this.is = is; }
+        public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; }
+        public void _close() { try { is.close(); } catch(IOException e) { /*ignore*/ } }
+        public FStat _fstat() { return new FStat(); }
+    }
+    
+    protected static class StdinFD extends InputStreamFD {
+        public StdinFD(InputStream is) { super(is); }
+        public void _close() { /* noop */ }
+        public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
+        public boolean isatty() { return true; }
+    }
+    protected static class StdoutFD extends OutputStreamFD {
+        public StdoutFD(OutputStream os) { super(os); }
+        public void _close() { /* noop */ }
+        public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
+        public boolean isatty() { return true; }
+    }
+    
+    public static class FStat {
+        public static final int S_IFIFO = 0010000;
+        public static final int S_IFCHR = 0020000;
+        public static final int S_IFDIR = 0040000;
+        public static final int S_IFREG = 0100000;
+        
+        public int dev() { return -1; }
+        // FEATURE: inode numbers are calculated inconsistently throught the runtime
+        public int inode() { return hashCode() & 0xfffff; }
+        public int mode() { return 0; }
+        public int type() { return S_IFIFO; }
+        public int nlink() { return 0; }
+        public int uid() { return 0; }
+        public int gid() { return 0; }
+        public int size() { return 0; }
+        public int atime() { return 0; }
+        public int mtime() { return 0; }
+        public int ctime() { return 0; }
+        public int blksize() { return 512; }
+        public int blocks() { return (size()+blksize()-1)/blksize(); }        
+    }
+    
+    protected static class HostFStat extends FStat {
+        private final File f;
+        private final boolean executable; 
+        public HostFStat(File f) {
+            this.f = f;
+            String name = f.getName();
+            // FEATURE: This is ugly.. maybe we should do a file(1) type check
+            executable = name.endsWith(".mips") || name.endsWith(".sh");
+        }
+        public int dev() { return 1; }
+        public int inode() { return f.getName().hashCode() & 0xffff; }
+        public int type() { return f.isDirectory() ? S_IFDIR : S_IFREG; }
+        public int nlink() { return 1; }
+        public int mode() {
+            int mode = 0;
+            boolean canread = f.canRead();
+            if(canread && (executable || f.isDirectory())) mode |= 0111;
+            if(canread) mode |= 0444;
+            if(f.canWrite()) mode |= 0222;
+            return mode;
+        }
+        public int size() { return (int) f.length(); }
+        public int mtime() { return (int)(f.lastModified()/1000); }
+    }
+    
+    // Exceptions
+    public class ReadFaultException extends FaultException {
+        public ReadFaultException(int addr) { super(addr); }
+    }
+    public class WriteFaultException extends FaultException {
+        public WriteFaultException(int addr) { super(addr); }
+    }
+    public abstract class FaultException extends ExecutionException {
+        public int addr;
+        public FaultException(int addr) { super("fault at: " + toHex(addr)); this.addr = addr; }
+    }
+    public static class ExecutionException extends Exception {
+        private String message = "(null)";
+        private String location = "(unknown)";
+        public ExecutionException() { /* noop */ }
+        public ExecutionException(String s) { if(s != null) message = s; }
+        void setLocation(String s) { location = s == null ? "(unknown)" : s; }
+        public final String getMessage() { return message + " at " + location; }
+    }
+    public static class CallException extends Exception {
+        public CallException(String s) { super(s); }
+    }
+    
+    protected static class ErrnoException extends IOException {
+        public int errno;
+        public ErrnoException(int errno) { super("Errno: " + errno); this.errno = errno; }
+    }
+    
+    // CPU State
+    protected static class CPUState {
+        public CPUState() { /* noop */ }
+        /* GPRs */
+        public int[] r = new int[32];
+        /* Floating point regs */
+        public int[] f = new int[32];
+        public int hi, lo;
+        public int fcsr;
+        public int pc;
+    }
+    
+    // Null pointer check helper function
+    protected final void nullPointerCheck(int addr) throws ExecutionException {
+        if(TOTAL_PAGES==1 ? addr < 65536 : (addr>>>PAGE_SHIFT) < 16)
+            throw new ExecutionException("Attempted to dereference a null pointer " + toHex(addr));
+    }
+    
+    // Utility functions
+    private byte[] byteBuf(int size) {
+        if(_byteBuf==null) _byteBuf = new byte[size];
+        else if(_byteBuf.length < size)
+            _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK)];
+        return _byteBuf;
+    }
+    
+    protected static String getSystemProperty(String key) {
+        try {
+            return System.getProperty(key);
+        } catch(SecurityException e) {
+            return null;
+        }
+    }
+    
+    /** Decode an packed string.. FEATURE: document this better */
+    protected static final int[] decodeData(String s, int words) {
+        if(s.length() % 8 != 0) throw new IllegalArgumentException("string length must be a multiple of 8");
+        if((s.length() / 8) * 7 < words*4) throw new IllegalArgumentException("string isn't big enough");
+        int[] buf = new int[words];
+        int prev = 0, left=0;
+        for(int i=0,n=0;n<words;i+=8) {
+            long l = 0;
+            for(int j=0;j<8;j++) { l <<= 7; l |= s.charAt(i+j) & 0x7f; }
+            if(left > 0) buf[n++] = prev | (int)(l>>>(56-left));
+            if(n < words) buf[n++] = (int) (l >>> (24-left));
+            left = (left + 8) & 0x1f;
+            prev = (int)(l << left);
+        }
+        return buf;
+    }
+    
+    protected static byte[] getBytes(String s) {
+        try {
+            return s.getBytes("ISO-8859-1");
+        } catch(UnsupportedEncodingException e) {
+            return null; // should never happen
+        }
+    }
+    
+    protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
+    protected final static int min(int a, int b) { return a < b ? a : b; }
+    protected final static int max(int a, int b) { return a > b ? a : b; }
+}
diff --git a/src/org/xwt/mips/UnixRuntime.java b/src/org/xwt/mips/UnixRuntime.java
new file mode 100644 (file)
index 0000000..8572ab9
--- /dev/null
@@ -0,0 +1,620 @@
+package org.xwt.mips;
+
+import org.xwt.mips.util.*;
+import java.io.*;
+import java.util.*;
+
+public abstract class UnixRuntime extends Runtime {
+    /** The pid of this "process" */
+    private int pid;
+    private int ppid;
+    protected int getPid() { return pid; }
+    
+    /** processes filesystem */
+    private FS fs;
+    public FS getFS() { return fs; }
+    public void setFS(FS fs) {
+        if(state >= RUNNING) throw new IllegalStateException("Can't change fs while process is running");
+        this.fs = fs;
+    }
+    
+    /** proceses current working directory */
+    private String cwd;
+    
+    /* Static stuff */
+    // FEATURE: Most of this is O(n) or worse - fix it
+    private final Object waitNotification = new Object();
+    private final static int MAX_TASKS = 256;
+    private final static UnixRuntime[] tasks = new UnixRuntime[MAX_TASKS];
+    private static int addTask(UnixRuntime rt) {
+        synchronized(tasks) {
+            for(int i=1;i<MAX_TASKS;i++) {
+                if(tasks[i] == null) {
+                    tasks[i] = rt;
+                    rt.pid = i;
+                    return i;
+                }
+            }
+            return -1;
+        }
+    }
+    private static void removeTask(UnixRuntime rt) {
+        synchronized(tasks) {
+            for(int i=1;i<MAX_TASKS;i++)
+                if(tasks[i] == rt) { tasks[i] = null; break; }
+        }
+    }
+    
+    public UnixRuntime(int pageSize, int totalPages, boolean allowEmptyPages) {
+        super(pageSize,totalPages,allowEmptyPages);
+        
+        HostFS root = new HostFS();
+        fs = new UnixOverlayFS(root);
+        
+        String dir = root.hostCWD();
+        try {
+            chdir(dir == null ? "/" : dir);
+        } catch(FileNotFoundException e) {
+            e.printStackTrace();
+            cwd = "/";
+        }
+    }
+    
+    private static String posixTZ() {
+        StringBuffer sb = new StringBuffer();
+        TimeZone zone = TimeZone.getDefault();
+        int off = zone.getRawOffset() / 1000;
+        sb.append(zone.getDisplayName(false,TimeZone.SHORT));
+        if(off > 0) sb.append("-");
+        else off = -off;
+        sb.append(off/3600); off = off%3600;
+        if(off > 0) sb.append(":").append(off/60); off=off%60;
+        if(off > 0) sb.append(":").append(off);
+        if(zone.useDaylightTime())
+            sb.append(zone.getDisplayName(true,TimeZone.SHORT));
+        return sb.toString();
+    }
+    
+    private static boolean envHas(String key,String[] environ) {
+        for(int i=0;i<environ.length;i++)
+            if(environ[i]!=null && environ[i].startsWith(key + "=")) return true;
+        return false;
+    }
+    
+    protected String[] createEnv(String[] extra) {
+        String[] defaults = new String[5];
+        int n=0;
+        if(extra == null) extra = new String[0];
+        if(!envHas("USER",extra) && getSystemProperty("user.name") != null)
+            defaults[n++] = "USER=" + getSystemProperty("user.name");
+        if(!envHas("HOME",extra) && getSystemProperty("user.name") != null)
+            defaults[n++] = "HOME=" + getSystemProperty("user.home");
+        if(!envHas("SHELL",extra)) defaults[n++] = "SHELL=/bin/sh";
+        if(!envHas("TERM",extra))  defaults[n++] = "TERM=vt100";
+        if(!envHas("TZ",extra))    defaults[n++] = "TZ=" + posixTZ();
+        String[] env = new String[extra.length+n];
+        for(int i=0;i<n;i++) env[i] = defaults[i];
+        for(int i=0;i<extra.length;i++) env[n++] = extra[i];
+        return env;
+    }
+    
+    protected void _start() {
+        if(addTask(this) < 0) throw new Error("Task list full");
+    }
+    
+    protected void _exit() {
+        synchronized(tasks) {
+            if(ppid == 0) removeTask(this);
+            for(int i=0;i<MAX_TASKS;i++) {
+                if(tasks[i] != null && tasks[i].ppid == pid) {
+                    if(tasks[i].state == DONE) removeTask(tasks[i]);
+                    else tasks[i].ppid = 0;
+                }
+            }
+            state = DONE;
+            if(ppid != 0) synchronized(tasks[ppid].waitNotification) { tasks[ppid].waitNotification.notify(); }
+        }
+    }
+
+    protected int syscall(int syscall, int a, int b, int c, int d) {
+        switch(syscall) {
+            case SYS_kill: return sys_kill(a,b);
+            case SYS_fork: return sys_fork();
+            case SYS_pipe: return sys_pipe(a);
+            case SYS_dup2: return sys_dup2(a,b);
+            case SYS_waitpid: return sys_waitpid(a,b,c);
+            case SYS_stat: return sys_stat(a,b);
+            case SYS_mkdir: return sys_mkdir(a,b);
+            case SYS_getcwd: return sys_getcwd(a,b);
+            case SYS_chdir: return sys_chdir(a);
+
+            default: return super.syscall(syscall,a,b,c,d);
+        }
+    }
+    
+    protected FD open(String path, int flags, int mode) throws IOException { return fs.open(cleanupPath(path),flags,mode); }
+
+    // FEATURE: Allow simple, broken signal delivery to other processes 
+    // (check if a signal was delivered before and after syscalls)
+    // FEATURE: Implement raise() in terms of call("raise",...) - kinda cheap, but it keeps the complexity in newlib
+    /** The kill syscall.
+       SIGSTOP, SIGTSTO, SIGTTIN, and SIGTTOUT pause&