/*
JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine
Release Version 2.4
A project from the Physics Dept, The University of Oxford
Copyright (C) 2007-2010 The University of Oxford
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as published by
the Free Software Foundation.
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Details (including contact information) can be found at:
jpc.sourceforge.net
or the developer website
sourceforge.net/projects/jpc/
Conceived and Developed by:
Rhys Newman, Ian Preston, Chris Dennis
End of licence header
*/
package org.jpc.j2se;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
public abstract class Option {
private static final Hashtable<String, Option> names2options = new Hashtable();
public static final Opt config = opt("config"); // This one is special...
public static final Switch track_writes = createSwitch("track-writes"); // needed to use -mem with compare
public static final Switch debug_blocks = createSwitch("debug-blocks");
public static final Switch log_disam = createSwitch("log-disam");
public static final Switch log_disam_addresses = createSwitch("log-disam-addresses");
public static final Switch log_state = createSwitch("log-state");
public static final Switch log_blockentry = createSwitch("log-block-entry");
public static final Switch log_memory_maps = createSwitch("log-memory-maps");
public static final Switch compile = createSwitch("compile");
public static final Switch fullscreen = createSwitch("fullscreen");
public static final Switch history = createSwitch("history");
public static final Switch useBochs = createSwitch("bochs");
public static final Switch printCHS = createSwitch("printCHS");
public static final Switch help = createSwitch("help");
public static final Opt min_addr_watch = opt("min-addr-watch");
public static final Opt max_addr_watch = opt("max-addr-watch");
// required for deterministic execution
public static final Switch deterministic = createSwitch("deterministic");
public static final Opt startTime = opt("start-time");
public static final Switch noScreen = createSwitch("no-screen");
public static final Opt ss = opt("ss");
public static final Opt ram = opt("ram");
public static final Opt ips = opt("ips");
public static final Opt cpulevel = opt("cpulevel");
public static final Opt timeslowdown = opt("time-slowdown");
public static final Switch singlesteptime = createSwitch("single-step-time");
public static final Opt max_instructions_per_block = opt("max-block-size");
public static final Opt boot = opt("boot");
public static final Opt fda = opt("fda");
public static final Opt fdb = opt("fdb");
public static final Opt hda = opt("hda");
public static final Opt hdb = opt("hdb");
public static final Opt hdc = opt("hdc");
public static final Opt hdd = opt("hdd");
public static final Opt cdrom = opt("cdrom");
public static final Opt bios = opt("bios");
public static final Switch ethernet = createSwitch("ethernet");
public static final Switch sound = createSwitch("sound");
public static final Opt sounddevice = opt("sounddevice");
public static final Opt mixer_rate = opt("mixer_rate");
public static final Opt mixer_javabuffer = opt("mixer_java-buffer");
public static final Opt mixer_blocksize = opt("mixer_block-size");
public static final Switch mixer_nosound = createSwitch("mixer_no-sound");
public static final Opt mixer_prebuffer = opt("mixer_prebuffer");
public static final Opt mpu401 = opt("mpu401");
public static final Opt mididevice = opt("midi-device");
public static final Opt midiconfig = opt("midi-config");
public static final Opt sbbase = opt("sbbase");
public static final Opt sb_irq = opt("sb_irq");
public static final Opt sb_dma = opt("sb_dma");
public static final Opt sb_hdma = opt("sb_hdma");
public static final Switch sbmixer = createSwitch("sbmixer");
public static final Opt sbtype = opt("sbtype");
public static final Opt oplemu = opt("oplemu");
public static final Opt oplrate = opt("oplrate");
public static void printHelp() {
System.out.println("JPC Help");
System.out.println("Parameters may be specified on the command line or in a file. ");
System.out.println();
System.out.println("-help - display this help");
System.out.println("-config $file - read parameters from $file, any subsequent commandline parameters override parameters in the file");
System.out.println("-boot $device - the device to boot from out of fda (floppy), hda (hard drive 1), cdrom (CDROM drive)");
System.out.println("-fda $file - floppy image file");
System.out.println("-hda $file - hard disk image file");
System.out.println("-hda dir:$dir - directory to mount as a FAT32 hard disk");
System.out.println("-ss $file - snapshot file to load");
System.out.println("-ram $megabytes - the amount RAM the virtual machine should have");
System.out.println("-ips $number - number of emulated instructions per emulated second - a larger value will cause a slower apparent time in the VM");
System.out.println("-cpulevel $number - 4 = 486, 5 = Pentium, 6 = Pentium Pro");
System.out.println();
System.out.println("-sound - enable sound");
System.out.println();
System.out.println("Advanced Options:");
System.out.println("-bios - specify an alternate bios image");
System.out.println("-max-block-size $num - maximum number of instructions per basic block (A value of 1 will still have some blocks of length 2 due to mov ss,X, pop ss and sti)");
}
public static String[] parse(String[] source) {
ArrayList<String> tmp = new ArrayList<String>();
for (Iterator<Option> iterator = names2options.values().iterator(); iterator.hasNext(); ) {
Option next = iterator.next();
next.set = false;
}
int index = 0;
for (; index < source.length; index++) {
String arg = source[index];
if (arg.startsWith("-")) {
arg = arg.substring(1);
}
Option opt = names2options.get(arg);
if (opt == null) {
tmp.add(source[index]);
} else if ((opt == config) && (config.value() != null))
{
// exit recursion
opt.set = false;
index = opt.update(source, index);
}
else {
opt.set = true;
index = opt.update(source, index);
}
}
if (config.isSet())
return loadConfig(config.value());
if (config.value() != null)
((Option) config).set = true;
if (tmp.size() == source.length) {
return source;
} else {
return tmp.toArray(new String[tmp.size()]);
}
}
public static void saveConfig(File f) throws IOException
{
String conf = saveConfig();
BufferedWriter w = new BufferedWriter(new FileWriter(f));
w.write(conf);
w.flush();
w.close();
}
public static String[] loadConfig(String file)
{
try {
return loadConfig(new File(file));
} catch (IOException e)
{
System.out.println("Error loading config from file "+file);
}
return null;
}
public static String[] loadConfig(File f) throws IOException
{
StringBuilder b = new StringBuilder();
BufferedReader r = new BufferedReader(new FileReader(f));
String line;
while ((line = r.readLine()) != null)
{
b.append(line+" ");
}
String[] current = saveConfig().split("\n");
for (String s: current)
b.append(s + " ");
return parse(b.toString().split(" "));
}
public static String saveConfig()
{
StringBuilder b = new StringBuilder();
for (Option opt : names2options.values())
{
if (opt instanceof Switch)
{
if (opt.isSet())
b.append("-"+opt.getName()+"\n");
}
else if (opt instanceof Opt)
{
if (opt.isSet())
b.append("-"+opt.getName()+" "+((Opt) opt).value()+"\n");
}
}
return b.toString();
}
public static Option getParameter(String name)
{
return names2options.get(name);
}
public static Switch createSwitch(String name) {
Switch sw = (Switch) names2options.get(name);
if (sw == null) {
sw = new Switch(name);
}
return sw;
}
public static Opt opt(String name) {
Opt opt = (Opt) names2options.get(name);
if (opt == null) {
opt = new Opt(name);
}
return opt;
}
public static OptSet optSet(String name) {
OptSet opt = (OptSet) names2options.get(name);
if (opt == null) {
opt = new OptSet(name);
}
return opt;
}
public static Select select(String name) {
return select(name,"default");
}
public static Select select(String name,String defaultKey) {
Select opt = (Select) names2options.get(name);
if (opt == null) {
opt = new Select(name,defaultKey);
}
return opt;
}
private final String name;
private boolean set;
protected Option(String name) {
this.name = name;
names2options.put(name, this);
}
public String getName() {
return name;
}
public boolean isSet() {
return set;
}
public abstract Object getValue();
protected abstract int update(String[] args, int index);
public Object getInstance() {
return getInstance(null);
}
public Object getInstance(String defaultClass) {
Object o = getValue();
Class clazz = null;
try {
if (o instanceof Class) {
clazz = (Class) o;
} else if (o instanceof String) {
clazz = Class.forName(o.toString());
} else if (defaultClass!=null) {
clazz=Class.forName(defaultClass);
} else {
return null;
}
return clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static class OptSet extends Option {
private Collection<String> values = new LinkedHashSet<String>();
public OptSet(String name, String... defaults) {
super(name);
for (int i = 0; i < defaults.length; i++) {
String s = defaults[i];
values.add(s);
}
}
@Override
public Object getValue() {
return values.toArray(new String[values.size()]);
}
public String[] values() {
return (String[]) getValue();
}
@Override
protected int update(String[] args, int index) {
String value = args[++index];
values.add(value);
return index;
}
public void remove(String value) {
values.remove(value);
}
}
public static class Opt extends Option {
private String value;
public Opt(String name) {
super(name);
}
@Override
public int update(String[] args, int index) {
this.value = args[++index];
return index;
}
@Override
public Object getValue() {
return value;
}
public int intValue(int defaultValue) {
if (value != null) {
return Integer.parseInt(value.trim());
} else {
return defaultValue;
}
}
public int intValue(int defaultValue, int radix) {
if (value != null) {
return (int) Long.parseLong(value.trim(), radix);
} else {
return defaultValue;
}
}
public long longValue(long defaultValue, int radix) {
if (value != null) {
return Long.parseLong(value.trim(), radix);
} else {
return defaultValue;
}
}
public double doubleValue(double defaultValue) {
if (value != null) {
return Double.parseDouble(value.trim());
} else {
return defaultValue;
}
}
public String value(String defaultValue) {
if (value != null) {
return value;
} else {
return defaultValue;
}
}
public String value() {
return value;
}
public Object valueOf(Class type, Object defaultValue) {
if (value == null) return defaultValue;
Throwable t = null;
try {
try {
Method valueOf = type.getMethod("valueOf", String.class);
return valueOf.invoke(type, value);
} catch (NoSuchMethodException e) {
System.err.println(type + " :No suitable method");
}
try {
Constructor constructor = type.getConstructor(String.class);
return constructor.newInstance(value);
} catch (NoSuchMethodException e) {
System.err.println(type + " :No suitable Constructor");
}
} catch (Exception e) {
throw new RuntimeException("Nested Exception" + type, e);
}
throw new RuntimeException("Unable obtain value of " + type);
}
public Object instance(String defaultClassName) {
if (value == null) {
value = defaultClassName;
}
try {
return Class.forName(value.trim()).newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Nested Exception", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Nested Exception", e);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Nested Exception", e);
}
}
}
public static class Switch extends Option {
private boolean value;
public Switch(String name) {
super(name);
}
public boolean value() {
return value;
}
@Override
public Object getValue() {
return Boolean.valueOf(value);
}
@Override
public int update(String[] args, int index) {
value = true;
return index; // No Arguments
}
}
public static class Select extends Option {
private Map<String, Object> values = new LinkedHashMap<String, Object>();
private String defaultValue;
private String key;
public Select(String name, String defaultValue) {
super(name);
this.key = defaultValue;
this.defaultValue = defaultValue;
}
@Override
public Object getValue() {
Object o = values.get(key);
if (o == null) return values.get(defaultValue);
return o;
}
public Select entry(String key,Object value) {
values.put(key,value);
return this;
}
public Object getInstance() {
Object o = getValue();
Class clazz = null;
try {
if (o instanceof Class) {
clazz = (Class) o;
} else if (o instanceof String) {
clazz = Class.forName(o.toString());
} else {
return null;
}
return clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public int update(String[] args, int index) {
this.key = args[++index];
return index;
}
}
}