/*
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.support;
//Do not even think about adding an import line to this class - especially not import java.net.*!
import java.io.IOException;
import java.io.DataOutput;
import java.io.DataInput;
import java.util.logging.*;
import org.jpc.emulator.AbstractHardwareComponent;
/**
* Represents the set of disk drive devices associated with this emulator
* instance.
* @author Chris Dennis
*/
public class DriveSet extends AbstractHardwareComponent
{
private static final Logger LOGGING = Logger.getLogger(DriveSet.class.getName());
public static enum BootType {FLOPPY, HARD_DRIVE, CDROM;}
private static enum Devices {
DEFAULT("org.jpc.support.FileBackedSeekableIODevice"),
dir("org.jpc.support.TreeBlockDevice"),
mem("org.jpc.support.ArrayBackedSeekableIODevice"),
net("org.jpc.support.RemoteSeekableIODevice");
private final String clazzname;
Devices(String clazz)
{
clazzname = clazz;
}
public Object getInstance()
{
try {
Class clazz = Class.forName(clazzname);
return clazz.newInstance();
} catch (ClassNotFoundException e) {
LOGGING.log(Level.WARNING, "Drive device class not found", e);
return null;
} catch (InstantiationException e) {
LOGGING.log(Level.WARNING, "Drive device couldn't be instantiated", e);
return null;
} catch (IllegalAccessException e) {
LOGGING.log(Level.WARNING, "Drive device couldn't be instantiated", e);
return null;
}
}
}
private BootType bootType;
private BlockDevice[] floppies;
private BlockDevice[] ides;
private String[] initialArgs;
/**
* Constructs a driveset with one hard disk, one floppy disk and the
* specified boot device type.
* @param boot boot device
* @param floppyDrive first floppy device
* @param hardDrive primary master hard disk device
*/
public DriveSet(BootType boot, BlockDevice floppyDrive, BlockDevice hardDrive)
{
this(boot, floppyDrive, null, hardDrive, null, null, null);
}
/**
* Constructs a driveset with all parameters specified.
* <p>
* A drive set can be composed of at most four ide devices and two floppy
* drive devices.
* @param boot boot device
* @param floppyDriveA first floppy device
* @param floppyDriveB second floppy device
* @param hardDriveA primary master hard disk
* @param hardDriveB primary slave hard disk
* @param hardDriveC secondary master hard disk
* @param hardDriveD secondary slave hard disk
*/
public DriveSet(BootType boot, BlockDevice floppyDriveA, BlockDevice floppyDriveB, BlockDevice hardDriveA, BlockDevice hardDriveB, BlockDevice hardDriveC, BlockDevice hardDriveD)
{
this.bootType = boot;
floppies = new BlockDevice[2];
floppies[0] = floppyDriveA;
floppies[1] = floppyDriveB;
ides = new BlockDevice[4];
ides[0] = hardDriveA;
ides[1] = hardDriveB;
ides[2] = (hardDriveC == null) ? new CDROMBlockDevice() : hardDriveC;
ides[3] = hardDriveD;
}
private void setInitialArgs(String[] init)
{
initialArgs = init;
}
/**
* Returns the i'th hard drive device.
* <p>
* Devices are numbered from 0 to 3 inclusive in order: primary master,
* primary slave, secondary master, secondary slave.
* @param index drive index
* @return hard drive block device
*/
public BlockDevice getHardDrive(int index)
{
return ides[index];
}
public void setHardDrive(int index, BlockDevice device)
{
ides[index] = device;
}
/**
* Returns the i'th floppy drive device.
* <p>
* The drives are numbered sequentially A:, B:.
* @param index floppy drive index
* @return floppy drive block device
*/
public BlockDevice getFloppyDrive(int index)
{
return floppies[index];
}
/**
* Returns the current boot device as determined by the boot type parameter.
* @return boot block device
*/
public BlockDevice getBootDevice()
{
switch (bootType) {
case FLOPPY:
return floppies[0];
case CDROM:
return ides[2];
case HARD_DRIVE:
return ides[0];
default:
return null;
}
}
/**
* Returns the boot type being used by this driveset.
* @return boot type
*/
public BootType getBootType()
{
return bootType;
}
private static Object createDevice(String spec)
{
if (spec == null) {
return null;
}
if ((spec.indexOf("\"") == 0) && (spec.indexOf("\"", 1) > 0))
spec = spec.substring(1, spec.length()-2);
int colon = spec.indexOf(':');
String deviceKey = "DEFAULT";
String deviceSpec = spec;
if ((colon >= 0) && (spec.indexOf("\\") != colon + 1)) {
deviceKey = spec.substring(0, colon);
deviceSpec = spec.substring(colon + 1);
}
Object device;
if (deviceKey.startsWith("caching")) {
deviceKey = "DEFAULT";
int secondcolon = deviceSpec.indexOf(':');
if ((secondcolon > 0) && (deviceSpec.indexOf("\\") != secondcolon + 1)) {
deviceSpec = deviceSpec.substring(secondcolon + 1);
deviceKey = deviceSpec.substring(0, secondcolon);
}
device = Devices.valueOf(deviceKey).getInstance();
device = new CachingSeekableIODevice((SeekableIODevice) device);
} else
device = Devices.valueOf(deviceKey).getInstance();
if (device instanceof SeekableIODevice) {
try {
((SeekableIODevice) device).configure(deviceSpec);
} catch (IOException e) {
return null;
}
return device;
} else if (device instanceof BlockDevice) {
try {
((BlockDevice) device).configure(deviceSpec);
} catch (IOException e) {
return null;
}
return device;
} else {
return device;
}
}
private static BlockDevice createFloppyBlockDevice(String spec)
{
Object device = createDevice(spec);
if (device instanceof SeekableIODevice)
return new FloppyBlockDevice((SeekableIODevice) device);
else
return (BlockDevice) device;
}
private static BlockDevice createHardDiskBlockDevice(String spec)
{
Object device = createDevice(spec);
if (device instanceof SeekableIODevice)
return new HDBlockDevice((SeekableIODevice) device);
else
return (BlockDevice) device;
}
private static BlockDevice createCdRomBlockDevice(String spec)
{
Object device = createDevice(spec);
if (device instanceof SeekableIODevice)
return new CDROMBlockDevice((SeekableIODevice) device);
else
return (BlockDevice) device;
}
public void saveState(DataOutput output) throws IOException
{
output.writeInt(initialArgs.length);
for (int i = 0; i < initialArgs.length; i++)
output.writeUTF(initialArgs[i]);
}
public void loadState(DataInput input) throws IOException
{
int len = input.readInt();
String[] newArgs = new String[len];
for (int i = 0; i < len; i++)
newArgs[i] = input.readUTF();
initialArgs = newArgs;
//do not load drives
DriveSet temp = buildFromArgs(initialArgs);
floppies = new BlockDevice[2];
for (int i = 0; i < floppies.length; i++)
floppies[i] = temp.getFloppyDrive(i);
for (int i = 0; i < ides.length; i++)
ides[i] = temp.getHardDrive(i);
}
/**
* Constructs a driveset instance by parsing the given command line
* arguments.
* @param args command line argument array
* @return resultant <code>DriveSet</code>
*/
public static DriveSet buildFromArgs(String[] args)
{
String[] initialArgs = args.clone();
BlockDevice floppyA = createFloppyBlockDevice(ArgProcessor.findVariable(args, "-fda", null));
BlockDevice floppyB = createFloppyBlockDevice(ArgProcessor.findVariable(args, "-fdb", null));
BlockDevice hardDiskA = createHardDiskBlockDevice(ArgProcessor.findVariable(args, "-hda", null));
BlockDevice hardDiskB = createHardDiskBlockDevice(ArgProcessor.findVariable(args, "-hdb", null));
BlockDevice hardDiskC = createHardDiskBlockDevice(ArgProcessor.findVariable(args, "-hdc", null));
BlockDevice hardDiskD = createHardDiskBlockDevice(ArgProcessor.findVariable(args, "-hdd", null));
String cdromSpec = ArgProcessor.findVariable(args, "-cdrom", null);
if (cdromSpec != null)
hardDiskC = createCdRomBlockDevice(cdromSpec);
BootType boot = BootType.FLOPPY;
String bootArg = ArgProcessor.findVariable(args, "-boot", null);
if ("fda".equalsIgnoreCase(bootArg))
boot = BootType.FLOPPY;
else if ("hda".equalsIgnoreCase(bootArg))
boot = BootType.HARD_DRIVE;
else if ("cdrom".equalsIgnoreCase(bootArg))
boot = BootType.CDROM;
else if (hardDiskA != null)
boot = BootType.HARD_DRIVE;
else if (hardDiskC instanceof CDROMBlockDevice)
boot = BootType.CDROM;
DriveSet temp = new DriveSet(boot, floppyA, floppyB, hardDiskA, hardDiskB, hardDiskC, hardDiskD);
temp.setInitialArgs(initialArgs);
return temp;
}
public void close()
{
for (BlockDevice d: ides)
if (d != null)
d.close();
for (BlockDevice d: floppies)
if (d != null)
d.close();
}
}