Refactor system flags
[jpcrr.git] / org / jpc / emulator / PC.java
blobde89cc778df97b6cc99aad54208d0695212319b5
1 /*
2 JPC-RR: A x86 PC Hardware Emulator
3 Release 1
5 Copyright (C) 2007-2009 Isis Innovation Limited
6 Copyright (C) 2009-2010 H. Ilari Liusvaara
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 2 as published by
10 the Free Software Foundation.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 Based on JPC x86 PC Hardware emulator,
22 A project from the Physics Dept, The University of Oxford
24 Details about original JPC can be found at:
26 www-jpc.physics.ox.ac.uk
30 package org.jpc.emulator;
32 import org.jpc.emulator.motherboard.*;
33 import org.jpc.emulator.memory.*;
34 import org.jpc.emulator.pci.peripheral.*;
35 import org.jpc.emulator.pci.*;
36 import org.jpc.emulator.peripheral.*;
37 import org.jpc.emulator.processor.*;
38 import org.jpc.emulator.processor.fpu64.FpuState;
39 import org.jpc.diskimages.BlockDevice;
40 import org.jpc.diskimages.DiskImage;
41 import org.jpc.diskimages.DiskImageSet;
42 import org.jpc.diskimages.GenericBlockDevice;
43 import org.jpc.diskimages.ImageLibrary;
44 import org.jpc.diskimages.ImageMaker;
45 import org.jpc.jrsr.JRSRArchiveReader;
46 import org.jpc.jrsr.JRSRArchiveWriter;
47 import org.jpc.jrsr.UTFInputLineStream;
48 import org.jpc.jrsr.UTFOutputLineStream;
49 import org.jpc.jrsr.FourToFiveDecoder;
50 import org.jpc.jrsr.FourToFiveEncoder;
51 import org.jpc.output.Output;
52 import org.jpc.output.OutputChannelDummy;
53 import java.io.*;
54 import java.util.*;
55 import java.util.zip.*;
56 import java.lang.reflect.*;
57 import java.security.MessageDigest;
58 import org.jpc.emulator.memory.codeblock.CodeBlockManager;
60 import static org.jpc.Misc.arrayToString;
61 import static org.jpc.Misc.stringToArray;
62 import static org.jpc.Misc.nextParseLine;
63 import static org.jpc.Misc.randomHexes;
65 /**
66 * This class represents the emulated PC as a whole, and holds references
67 * to its main hardware components.
68 * @author Chris Dennis
69 * @author Ian Preston
71 public class PC implements SRDumpable
73 public static class PCHardwareInfo implements SRDumpable
75 public byte[] biosID;
76 public byte[] vgaBIOSID;
77 public byte[] hdaID;
78 public byte[] hdbID;
79 public byte[] hdcID;
80 public byte[] hddID;
81 public DiskImageSet images;
82 public int initFDAIndex;
83 public int initFDBIndex;
84 public int initCDROMIndex;
85 public long initRTCTime;
86 public int cpuDivider;
87 public int memoryPages;
88 public String fpuEmulator;
89 public Map<String, Set<String>> hwModules;
90 public DriveSet.BootType bootType;
91 public Map<String, Boolean> booleanOptions;
92 public Map<String, Integer> intOptions;
94 public void dumpStatusPartial(StatusDumper output2) throws IOException
96 if(output2 != null)
97 return;
99 PrintStream output = System.err;
101 output.println("BIOS " + arrayToString(biosID));
102 output.println("VGABIOS " + arrayToString(vgaBIOSID));
103 if(hdaID != null)
104 output.println("HDA " + arrayToString(hdaID));
105 if(hdbID != null)
106 output.println("HDB " + arrayToString(hdbID));
107 if(hdcID != null)
108 output.println("HDC " + arrayToString(hdcID));
109 if(hddID != null)
110 output.println("HDD " + arrayToString(hddID));
111 int disks = 1 + images.highestDiskIndex();
112 for(int i = 0; i < disks; i++) {
113 DiskImage disk = images.lookupDisk(i);
114 if(disk != null)
115 output.println("DISK " + i + " " + arrayToString(disk.getImageID()));
117 if(initFDAIndex >= 0)
118 output.println("FDA " + initFDAIndex);
119 if(initFDBIndex >= 0)
120 output.println("FDB " + initFDBIndex);
121 if(initCDROMIndex >= 0)
122 output.println("CDROM " + initCDROMIndex);
123 output.println("INITIALTIME " + initRTCTime);
124 output.println("CPUDIVIDER " + (cpuDivider - 1));
125 if(fpuEmulator != null)
126 output.println("FPU " + fpuEmulator);
127 if(bootType == DriveSet.BootType.FLOPPY)
128 output.println("BOOT FLOPPY");
129 else if(bootType == DriveSet.BootType.HARD_DRIVE)
130 output.println("BOOT HDD");
131 else if(bootType == DriveSet.BootType.CDROM)
132 output.println("BOOT CDROM");
133 else if(bootType == null)
135 else
136 throw new IOException("Unknown boot type");
137 if(hwModules != null && !hwModules.isEmpty()) {
138 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
139 for(String p : e.getValue())
140 if(p != null)
141 output.println("LOADMODULEA " + e.getKey() + "(" + p + ")");
142 else
143 output.println("LOADMODULE " + e.getKey());
146 if(booleanOptions != null)
147 for(Map.Entry<String, Boolean> setting : booleanOptions.entrySet())
148 if(setting.getValue().booleanValue())
149 output.println(setting.getKey());
150 if(intOptions != null)
151 for(Map.Entry<String, Integer> setting : intOptions.entrySet())
152 if(setting.getValue().intValue() != 0)
153 output.println(setting.getKey() + " " + setting.getValue());
156 public void dumpStatus(StatusDumper output)
158 if(output.dumped(this))
159 return;
161 output.println("#" + output.objectNumber(this) + ": PCHardwareInfo:");
162 try { dumpStatusPartial(output); } catch(Exception e) {}
163 output.endObject();
166 public void dumpSRPartial(SRDumper output) throws IOException
168 output.dumpArray(biosID);
169 output.dumpArray(vgaBIOSID);
170 output.dumpArray(hdaID);
171 output.dumpArray(hdbID);
172 output.dumpArray(hdcID);
173 output.dumpArray(hddID);
174 output.dumpObject(images);
175 output.dumpInt(initFDAIndex);
176 output.dumpInt(initFDBIndex);
177 output.dumpInt(initCDROMIndex);
178 output.dumpLong(initRTCTime);
179 output.dumpInt(cpuDivider);
180 output.dumpInt(memoryPages);
181 output.dumpString(fpuEmulator);
182 if(hwModules != null) {
183 output.dumpBoolean(true);
184 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
185 output.dumpBoolean(true);
186 output.dumpString(e.getKey());
187 for(String s : e.getValue()) {
188 output.dumpBoolean(true);
189 output.dumpString(s);
191 output.dumpBoolean(false);
193 output.dumpBoolean(false);
194 } else
195 output.dumpBoolean(false);
196 output.dumpByte(DriveSet.BootType.toNumeric(bootType));
197 //Following are old system setting bits. They are RESERVED now.
198 output.dumpBoolean(false);
199 output.dumpBoolean(false);
200 output.dumpBoolean(false);
201 //The new system setting stuff.
202 if(intOptions != null)
203 for(Map.Entry<String, Boolean> setting : booleanOptions.entrySet())
204 if(setting.getValue().booleanValue()) {
205 output.dumpBoolean(true);
206 output.dumpString(setting.getKey());
208 output.dumpBoolean(false);
209 if(intOptions != null)
210 for(Map.Entry<String, Integer> setting : intOptions.entrySet())
211 if(setting.getValue().intValue() != 0) {
212 output.dumpBoolean(true);
213 output.dumpString(setting.getKey());
214 output.dumpInt(setting.getValue());
216 output.dumpBoolean(false);
219 public PCHardwareInfo()
221 images = new DiskImageSet();
224 public PCHardwareInfo(SRLoader input) throws IOException
226 input.objectCreated(this);
227 biosID = input.loadArrayByte();
228 vgaBIOSID = input.loadArrayByte();
229 hdaID = input.loadArrayByte();
230 hdbID = input.loadArrayByte();
231 hdcID = input.loadArrayByte();
232 hddID = input.loadArrayByte();
233 images = (DiskImageSet)input.loadObject();
234 initFDAIndex = input.loadInt();
235 initFDBIndex = input.loadInt();
236 initCDROMIndex = input.loadInt();
237 initRTCTime = input.loadLong();
238 cpuDivider = input.loadInt();
239 memoryPages = input.loadInt();
240 fpuEmulator = input.loadString();
241 boolean present = input.loadBoolean();
242 if(present) {
243 hwModules = new LinkedHashMap<String, Set<String>>();
244 present = input.loadBoolean();
245 while(present) {
246 String name = input.loadString();
247 hwModules.put(name, new LinkedHashSet<String>());
248 boolean present2 = input.loadBoolean();
249 while(present2) {
250 String params = input.loadString();
251 present2 = input.loadBoolean();
252 hwModules.get(name).add(params);
254 present = input.loadBoolean();
257 bootType = DriveSet.BootType.fromNumeric(input.loadByte());
258 //Compat stuff.
259 boolean ioportDelayed = input.loadBoolean();
260 boolean vgaHretrace = input.loadBoolean();
261 boolean flushOnModify = input.loadBoolean();
262 booleanOptions = new TreeMap<String, Boolean>();
263 intOptions = new TreeMap<String, Integer>();
264 if(ioportDelayed)
265 booleanOptions.put("IOPORTDELAY", true);
266 if(vgaHretrace)
267 booleanOptions.put("VGAHRETRACE", true);
268 if(flushOnModify)
269 booleanOptions.put("FLUSHONMODIFY", true);
270 //Real settings stuff.
271 while(input.loadBoolean())
272 booleanOptions.put(input.loadString(), true);
273 while(input.loadBoolean()) {
274 String name = input.loadString();
275 int value = input.loadInt();
276 intOptions.put(name, value);
280 public void makeHWInfoSegment(UTFOutputLineStream output, DiskChanger changer) throws IOException
282 output.encodeLine("BIOS", arrayToString(biosID));
283 output.encodeLine("VGABIOS", arrayToString(vgaBIOSID));
284 output.encodeLine("HDA", arrayToString(hdaID));
285 output.encodeLine("HDB", arrayToString(hdbID));
286 output.encodeLine("HDC", arrayToString(hdcID));
287 output.encodeLine("HDD", arrayToString(hddID));
288 //TODO: When event recording becomes available, only save the disk images needed.
289 Set<Integer> usedDisks = changer.usedDiskSet();
290 int disks = 1 + images.highestDiskIndex();
291 for(int i = 0; i < disks; i++) {
292 DiskImage disk = images.lookupDisk(i);
293 if(disk != null && usedDisks.contains(i)) {
294 output.encodeLine("DISK", i, arrayToString(disk.getImageID()));
295 output.encodeLine("DISKNAME", i, disk.getName());
298 if(initFDAIndex >= 0) output.encodeLine("FDA", initFDAIndex);
299 if(initFDBIndex >= 0) output.encodeLine("FDB", initFDBIndex);
300 if(initCDROMIndex >= 0) output.encodeLine("CDROM", initCDROMIndex);
301 output.encodeLine("INITIALTIME", initRTCTime);
302 output.encodeLine("CPUDIVIDER", cpuDivider);
303 output.encodeLine("MEMORYSIZE", memoryPages);
304 output.encodeLine("FPU", fpuEmulator);
305 if(bootType == DriveSet.BootType.FLOPPY) output.encodeLine("BOOT", "FLOPPY");
306 else if(bootType == DriveSet.BootType.HARD_DRIVE) output.encodeLine("BOOT", "HDD");
307 else if(bootType == DriveSet.BootType.CDROM) output.encodeLine("BOOT", "CDROM");
308 else if(bootType == null)
310 else
311 throw new IOException("Unknown boot type");
312 if(hwModules != null && !hwModules.isEmpty()) {
313 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
314 for(String p : e.getValue())
315 if(p != null)
316 output.encodeLine("LOADMODULEA", e.getKey(), p);
317 else
318 output.encodeLine("LOADMODULE", e.getKey());
321 if(booleanOptions != null)
322 for(Map.Entry<String, Boolean> setting : booleanOptions.entrySet())
323 if(setting.getValue().booleanValue())
324 output.encodeLine(setting.getKey());
325 if(intOptions != null)
326 for(Map.Entry<String, Integer> setting : intOptions.entrySet())
327 if(setting.getValue().intValue() != 0)
328 output.encodeLine(setting.getKey(), setting.getValue().toString());
331 public static int componentsForLine(String op)
333 if("BIOS".equals(op))
334 return 2;
335 if("VGABIOS".equals(op))
336 return 2;
337 if("HDA".equals(op))
338 return 2;
339 if("HDB".equals(op))
340 return 2;
341 if("HDC".equals(op))
342 return 2;
343 if("HDD".equals(op))
344 return 2;
345 if("FDA".equals(op))
346 return 2;
347 if("FDB".equals(op))
348 return 2;
349 if("CDROM".equals(op))
350 return 2;
351 if("INITIALTIME".equals(op))
352 return 2;
353 if("CPUDIVIDER".equals(op))
354 return 2;
355 if("MEMORYSIZE".equals(op))
356 return 2;
357 if("FPU".equals(op))
358 return 2;
359 if("BOOT".equals(op))
360 return 2;
361 if("LOADMODULE".equals(op))
362 return 2;
363 if("LOADMODULEA".equals(op))
364 return 3;
365 if("DISK".equals(op))
366 return 3;
367 if("DISKNAME".equals(op))
368 return 3;
369 return 0;
373 public static PCHardwareInfo parseHWInfoSegment(UTFInputLineStream input) throws IOException
376 PCHardwareInfo hw = new PCHardwareInfo();
377 hw.booleanOptions = new TreeMap<String, Boolean>();
378 hw.intOptions = new TreeMap<String, Integer>();
379 hw.initFDAIndex = -1;
380 hw.initFDBIndex = -1;
381 hw.initCDROMIndex = -1;
382 hw.images = new DiskImageSet();
383 hw.hwModules = new LinkedHashMap<String, Set<String>>();
384 String[] components = nextParseLine(input);
385 while(components != null) {
386 if(componentsForLine(components[0]) == 0 && components.length == 1) {
387 hw.booleanOptions.put(components[0], true);
388 components = nextParseLine(input);
389 continue;
391 if(componentsForLine(components[0]) == 0 && components.length == 2) {
392 try {
393 hw.intOptions.put(components[0], Integer.parseInt(components[1]));
394 } catch(Exception e) {
395 throw new IOException("Bad " + components[0] + " line in initialization segment");
397 components = nextParseLine(input);
398 continue;
400 if(components.length != componentsForLine(components[0]))
401 throw new IOException("Bad " + components[0] + " line in ininitialization segment: " +
402 "expected " + componentsForLine(components[0]) + " components, got " + components.length);
403 if("BIOS".equals(components[0]))
404 hw.biosID = stringToArray(components[1]);
405 else if("VGABIOS".equals(components[0]))
406 hw.vgaBIOSID = stringToArray(components[1]);
407 else if("HDA".equals(components[0]))
408 hw.hdaID = stringToArray(components[1]);
409 else if("HDB".equals(components[0]))
410 hw.hdbID = stringToArray(components[1]);
411 else if("HDC".equals(components[0]))
412 hw.hdcID = stringToArray(components[1]);
413 else if("HDD".equals(components[0]))
414 hw.hddID = stringToArray(components[1]);
415 else if("DISK".equals(components[0])) {
416 int id;
417 try {
418 id = Integer.parseInt(components[1]);
419 if(id < 0)
420 throw new NumberFormatException("Bad id");
421 } catch(NumberFormatException e) {
422 throw new IOException("Bad DISK line in initialization segment");
424 hw.images.addDisk(id, new DiskImage(components[2], false));
425 } else if("DISKNAME".equals(components[0])) {
426 int id;
427 try {
428 id = Integer.parseInt(components[1]);
429 if(id < 0)
430 throw new NumberFormatException("Bad id");
431 hw.images.lookupDisk(id).setName(components[2]);
432 } catch(Exception e) {
433 throw new IOException("Bad DISKNAME line in initialization segment");
435 } else if("FDA".equals(components[0])) {
436 int id;
437 try {
438 id = Integer.parseInt(components[1]);
439 if(id < 0)
440 throw new NumberFormatException("Bad id");
441 } catch(NumberFormatException e) {
442 throw new IOException("Bad FDA line in initialization segment");
444 hw.initFDAIndex = id;
445 } else if("FDB".equals(components[0])) {
446 int id;
447 try {
448 id = Integer.parseInt(components[1]);
449 if(id < 0)
450 throw new NumberFormatException("Bad id");
451 } catch(NumberFormatException e) {
452 throw new IOException("Bad FDB line in initialization segment");
454 hw.initFDBIndex = id;
455 } else if("CDROM".equals(components[0])) {
456 int id;
457 try {
458 id = Integer.parseInt(components[1]);
459 if(id < 0)
460 throw new NumberFormatException("Bad id");
461 } catch(NumberFormatException e) {
462 throw new IOException("Bad CDROM line in initialization segment");
464 hw.initCDROMIndex = id;
465 } else if("INITIALTIME".equals(components[0])) {
466 long id;
467 try {
468 id = Long.parseLong(components[1]);
469 if(id < 0 || id > 4102444799999L)
470 throw new NumberFormatException("Bad id");
471 } catch(NumberFormatException e) {
472 throw new IOException("Bad INITIALTIME line in initialization segment");
474 hw.initRTCTime = id;
475 } else if("CPUDIVIDER".equals(components[0])) {
476 int id;
477 try {
478 id = Integer.parseInt(components[1]);
479 if(id < 1 || id > 256)
480 throw new NumberFormatException("Bad id");
481 } catch(NumberFormatException e) {
482 throw new IOException("Bad CPUDIVIDER line in initialization segment");
484 hw.cpuDivider = id;
485 } else if("MEMORYSIZE".equals(components[0])) {
486 int id;
487 try {
488 id = Integer.parseInt(components[1]);
489 if(id < 256 || id > 262144)
490 throw new NumberFormatException("Bad id");
491 } catch(NumberFormatException e) {
492 throw new IOException("Bad MEMORYSIZE line in initialization segment");
494 hw.memoryPages = id;
495 } else if("FPU".equals(components[0])) {
496 hw.fpuEmulator = components[1];
497 } else if("BOOT".equals(components[0])) {
498 if("FLOPPY".equals(components[1]))
499 hw.bootType = DriveSet.BootType.FLOPPY;
500 else if("HDD".equals(components[1]))
501 hw.bootType = DriveSet.BootType.HARD_DRIVE;
502 else if("CDROM".equals(components[1]))
503 hw.bootType = DriveSet.BootType.CDROM;
504 else
505 throw new IOException("Bad BOOT line in initialization segment");
506 } else if("LOADMODULE".equals(components[0])) {
507 if(!hw.hwModules.containsKey(components[1]))
508 hw.hwModules.put(components[1],new LinkedHashSet<String>());
509 hw.hwModules.get(components[1]).add(null);
510 } else if("LOADMODULEA".equals(components[0])) {
511 if(!hw.hwModules.containsKey(components[1]))
512 hw.hwModules.put(components[1],new LinkedHashSet<String>());
513 hw.hwModules.get(components[1]).add(components[2]);
515 components = nextParseLine(input);
517 return hw;
522 public int sysRAMSize;
523 public int cpuClockDivider;
524 private PCHardwareInfo hwInfo;
526 public static volatile boolean compile = true;
528 private final Processor processor;
529 private final PhysicalAddressSpace physicalAddr;
530 private final LinearAddressSpace linearAddr;
531 private final Clock vmClock;
532 private final Set<HardwareComponent> parts;
533 private final CodeBlockManager manager;
534 private DiskImageSet images;
535 private final ResetButton brb;
536 private final DiskChanger diskChanger;
537 private final EventPoller poller;
539 private Output outputs;
540 private OutputChannelDummy dummyChannel;
542 private TraceTrap traceTrap;
543 private boolean hitTraceTrap;
544 private boolean tripleFaulted;
545 private boolean rebootRequest;
547 private int cdromIndex;
549 public Output getOutputs()
551 return outputs;
554 public HardwareComponent loadHardwareModule(String name, String params) throws IOException
556 Class<?> module;
557 if("".equals(params))
558 params = null;
560 try {
561 module = Class.forName(name);
562 } catch(Exception e) {
563 throw new IOException("Unable to find extension module \"" + name + "\".");
565 if(!HardwareComponent.class.isAssignableFrom(module)) {
566 throw new IOException("Extension module \"" + name + "\" is not valid hardware module.");
568 HardwareComponent c;
569 try {
570 boolean x = params.equals(""); //Intentionally cause NPE if params is null.
571 x = x & x; //Silence warning.
572 Constructor<?> cc = module.getConstructor(String.class);
573 c = (HardwareComponent)cc.newInstance(params);
574 } catch(NullPointerException e) {
575 try {
576 Constructor<?> cc = module.getConstructor();
577 c = (HardwareComponent)cc.newInstance();
578 } catch(InvocationTargetException f) {
579 Throwable e2 = f.getCause();
580 //If the exception is something unchecked, just pass it through.
581 if(e2 instanceof RuntimeException)
582 throw (RuntimeException)e2;
583 if(e2 instanceof Error) {
584 IOException ne = new IOException("Error while invoking constructor: " + e2);
585 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
586 throw ne;
588 //Also pass IOException through.
589 if(e2 instanceof IOException)
590 throw (IOException)e2;
591 //What the heck is that?
592 IOException ne = new IOException("Unknown exception while invoking module constructor: " + e2);
593 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
594 throw ne;
595 } catch(Exception f) {
596 throw new IOException("Unable to instantiate extension module \"" + name + "\".");
598 } catch(InvocationTargetException e) {
599 Throwable e2 = e.getCause();
600 //If the exception is something unchecked, just pass it through.
601 if(e2 instanceof RuntimeException)
602 throw (RuntimeException)e2;
603 if(e2 instanceof Error) {
604 IOException ne = new IOException("Error while invoking constructor: " + e2);
605 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
606 throw ne;
608 //Also pass IOException through.
609 if(e2 instanceof IOException)
610 throw (IOException)e2;
611 //What the heck is that?
612 IOException ne = new IOException("Unknown exception while invoking module constructor: " + e2);
613 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
614 throw ne;
615 } catch(Exception f) {
616 throw new IOException("Unable to instantiate extension module \"" + name + "\": " + f.getMessage());
619 return c;
623 * Constructs a new <code>PC</code> instance with the specified external time-source and
624 * drive set.
625 * @param drives drive set for this instance.
626 * @throws java.io.IOException propogated from bios resource loading
628 public PC(DriveSet drives, int ramPages, int clockDivide, String sysBIOSImg, String vgaBIOSImg,
629 long initTime, DiskImageSet images, Map<String, Set<String>> hwModules, String fpuClass,
630 Map<String, Boolean> bools, Map<String, Integer> ints)
631 throws IOException
633 parts = new LinkedHashSet<HardwareComponent>();
635 cdromIndex = -1;
636 for(int i = 0; i < 4; i++) {
637 BlockDevice dev = drives.getHardDrive(i);
638 if(dev != null && dev.getType() == BlockDevice.Type.CDROM)
639 cdromIndex = i;
642 cpuClockDivider = clockDivide;
643 sysRAMSize = ramPages * 4096;
644 vmClock = new Clock();
646 if(hwModules != null)
647 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
648 String name = e.getKey();
649 for(String params : e.getValue()) {
650 System.err.println("Informational: Loading module \"" + name + "\".");
651 parts.add(loadHardwareModule(name, params));
657 parts.add(vmClock);
658 System.err.println("Informational: Creating Outputs...");
659 outputs = new Output();
660 dummyChannel = new OutputChannelDummy(outputs, "<DUMMY>");
661 System.err.println("Informational: Creating CPU...");
662 processor = new Processor(vmClock, cpuClockDivider);
663 parts.add(processor);
664 manager = new CodeBlockManager();
666 System.err.println("Informational: Creating FPU...");
667 try {
668 if(fpuClass != null) {
669 Object fpu = Class.forName(fpuClass).getConstructor(Processor.class).newInstance(processor);
670 processor.setFPU((FpuState)fpu);
672 } catch(ClassCastException e) {
673 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
674 } catch(InvocationTargetException e) {
675 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
676 } catch(NoSuchMethodException e) {
677 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
678 } catch(IllegalAccessException e) {
679 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
680 } catch(ClassNotFoundException e) {
681 throw new IOException("No such class: " + fpuClass + ".");
682 } catch(InstantiationException e) {
683 throw new IOException("Can't instantiate FPU emulator: " + e.getMessage() + ".");
686 System.err.println("Informational: Creating Reset Button...");
687 brb = new ResetButton(this);
688 parts.add(brb);
690 System.err.println("Informational: Creating Disk Changer..");
691 diskChanger = new DiskChanger(this);
692 parts.add(diskChanger);
694 System.err.println("Informational: Creating physical address space...");
695 physicalAddr = new PhysicalAddressSpace(manager, sysRAMSize);
696 parts.add(physicalAddr);
698 System.err.println("Informational: Creating linear address space...");
699 linearAddr = new LinearAddressSpace();
700 parts.add(linearAddr);
702 parts.add(drives);
704 //Motherboard
705 System.err.println("Informational: Creating I/O port handler...");
706 parts.add(new IOPortHandler());
707 System.err.println("Informational: Creating IRQ controller...");
708 parts.add(new InterruptController());
710 System.err.println("Informational: Creating primary DMA controller...");
711 parts.add(new DMAController(false, true));
712 System.err.println("Informational: Creating secondary DMA controller...");
713 parts.add(new DMAController(false, false));
715 System.err.println("Informational: Creating real time clock...");
716 parts.add(new RTC(0x70, 8, sysRAMSize, initTime));
717 System.err.println("Informational: Creating interval timer...");
718 parts.add(new IntervalTimer(0x40, 0));
719 System.err.println("Informational: Creating A20 Handler...");
720 parts.add(new GateA20Handler());
721 this.images = images;
723 //Peripherals
724 System.err.println("Informational: Creating IDE interface...");
725 parts.add(new PIIX3IDEInterface());
727 System.err.println("Informational: Creating Keyboard...");
728 parts.add(new Keyboard());
729 System.err.println("Informational: Creating floppy disk controller...");
730 parts.add(new FloppyController());
731 System.err.println("Informational: Creating PC speaker...");
732 parts.add(new PCSpeaker(outputs, "org.jpc.emulator.peripheral.PCSpeaker-0"));
734 //PCI Stuff
735 System.err.println("Informational: Creating PCI Host Bridge...");
736 parts.add(new PCIHostBridge());
737 System.err.println("Informational: Creating PCI-to-ISA Bridge...");
738 parts.add(new PCIISABridge());
739 System.err.println("Informational: Creating PCI Bus...");
740 parts.add(new PCIBus());
742 //BIOSes
743 System.err.println("Informational: Creating system BIOS...");
744 parts.add(new SystemBIOS(sysBIOSImg));
745 System.err.println("Informational: Creating VGA BIOS...");
746 parts.add(new VGABIOS(vgaBIOSImg));
747 System.err.println("Informational: Creating trace trap...");
748 parts.add(traceTrap = new TraceTrap());
750 System.err.println("Informational: Creating event poller...");
751 poller = new EventPoller(vmClock);
753 System.err.println("Informational: Creating hardware info...");
754 hwInfo = new PCHardwareInfo();
756 DisplayController displayController = null;
757 for(HardwareComponent c : parts)
758 if(c instanceof DisplayController)
759 if(displayController == null)
760 displayController = (DisplayController)c;
761 else
762 throw new IOException("Can not have multiple display controllers: \"" +
763 c.getClass().getName() + "\" and \"" + displayController.getClass().getName() +
764 "\" are both display controllers.");
765 if(displayController == null) {
766 System.err.println("Informational: Creating VGA card...");
767 VGACard card = new VGACard();
768 parts.add(card);
769 displayController = card;
771 displayController.getOutputDevice().setSink(outputs, "<VIDEO>");
773 System.err.println("Informational: Creating sound outputs...");
774 Map<String, Integer> numBase = new HashMap<String, Integer>();
775 for(HardwareComponent c : parts) {
776 if(!(c instanceof SoundOutputDevice))
777 continue;
778 SoundOutputDevice c2 = (SoundOutputDevice)c;
779 int channels = c2.requestedSoundChannels();
780 int base = 0;
781 if(numBase.containsKey(c.getClass().getName()))
782 base = numBase.get(c.getClass().getName()).intValue();
783 for(int i = 0; i < channels; i++) {
784 String outname = c.getClass().getName() + "-" + (base + i);
785 c2.soundChannelCallback(outputs, outname);
787 numBase.put(c.getClass().getName(), base + channels);
790 System.err.println("Informational: Setting system flags...");
791 Set<String> found = new HashSet<String>();
792 for(HardwareComponent part : parts) {
793 Class<?> clazz = part.getClass();
794 Field[] fields = clazz.getDeclaredFields();
795 for(Field field : fields)
796 if(field.getName().startsWith("SYSFLAG_")) {
797 String name = field.getName().substring(8);
798 found.add(name);
799 Boolean b = null;
800 Integer i = null;
801 if(bools != null)
802 b = bools.get(name);
803 if(ints != null)
804 i = ints.get(name);
805 if(b != null && b.booleanValue()) {
806 try {
807 field.setBoolean(part, true);
808 } catch(IllegalAccessException e) {
809 throw new IOException("System flag field for " + name + " is of wrong type");
812 if(i != null && i.intValue() != 0) {
813 try {
814 field.setInt(part, i.intValue());
815 } catch(IllegalAccessException e) {
816 throw new IOException("System flag field for " + name + " is of wrong type");
821 if(bools != null)
822 for(Map.Entry<String, Boolean> b : bools.entrySet())
823 if(!found.contains(b.getKey()))
824 throw new IOException("Unrecognized system flag " + b.getKey() + " encountered");
825 if(ints != null)
826 for(Map.Entry<String, Integer> i : ints.entrySet())
827 if(!found.contains(i.getKey()))
828 throw new IOException("Unrecognized system flag " + i.getKey() + " encountered");
830 System.err.println("Informational: Configuring components...");
831 if(!configure())
832 throw new IllegalStateException("Can't initialize components (cyclic dependency?)");
833 System.err.println("Informational: PC initialization done.");
836 public int getCDROMIndex()
838 return cdromIndex;
841 public DriveSet getDrives()
843 for(HardwareComponent c2 : parts)
844 if(c2 instanceof DriveSet)
845 return (DriveSet)c2;
846 return null;
849 public void dumpStatusPartial(StatusDumper output)
851 output.println("\tsysRAMSize " + sysRAMSize + " cpuClockDivider " + cpuClockDivider);
852 output.println("\ttripleFaulted " + tripleFaulted + " cdromIndex " + cdromIndex);
853 //hitTraceTrap not printed here.
854 output.println("\tprocessor <object #" + output.objectNumber(processor) + ">"); if(processor != null) processor.dumpStatus(output);
855 output.println("\tphysicalAddr <object #" + output.objectNumber(physicalAddr) + ">"); if(physicalAddr != null) physicalAddr.dumpStatus(output);
856 output.println("\tlinearAddr <object #" + output.objectNumber(linearAddr) + ">"); if(linearAddr != null) linearAddr.dumpStatus(output);
857 output.println("\tvmClock <object #" + output.objectNumber(vmClock) + ">"); if(vmClock != null) vmClock.dumpStatus(output);
858 output.println("\timages <object #" + output.objectNumber(images) + ">"); if(images != null) images.dumpStatus(output);
859 output.println("\ttraceTrap <object #" + output.objectNumber(traceTrap) + ">"); if(traceTrap != null) traceTrap.dumpStatus(output);
860 output.println("\thwInfo <object #" + output.objectNumber(hwInfo) + ">"); if(hwInfo != null) hwInfo.dumpStatus(output);
861 output.println("\toutputs <object #" + output.objectNumber(outputs) + ">"); if(outputs != null) outputs.dumpStatus(output);
862 output.println("\tbrb <object #" + output.objectNumber(brb) + ">"); if(brb != null) brb.dumpStatus(output);
863 output.println("\tpoller <object #" + output.objectNumber(poller) + ">"); if(poller != null) poller.dumpStatus(output);
864 output.println("\tdummyChannel <object #" + output.objectNumber(dummyChannel) + ">"); if(dummyChannel != null) dummyChannel.dumpStatus(output);
866 int i = 0;
867 for(HardwareComponent part : parts) {
868 output.println("\tparts[" + i++ + "] <object #" + output.objectNumber(part) + ">");
869 if(part != null) part.dumpStatus(output);
874 public long getTime()
876 return vmClock.getTime();
879 public PC(SRLoader input) throws IOException
881 input.objectCreated(this);
882 cdromIndex = input.loadInt();
883 sysRAMSize = input.loadInt();
884 cpuClockDivider = input.loadInt();
885 processor = (Processor)input.loadObject();
886 physicalAddr = (PhysicalAddressSpace)input.loadObject();
887 linearAddr = (LinearAddressSpace)input.loadObject();
888 vmClock = (Clock)input.loadObject();
889 images = (DiskImageSet)(input.loadObject());
890 traceTrap = (TraceTrap)input.loadObject();
891 manager = (CodeBlockManager)input.loadObject();
892 hwInfo = (PCHardwareInfo)(input.loadObject());
893 outputs = (Output)(input.loadObject());
894 hitTraceTrap = input.loadBoolean();
895 tripleFaulted = input.loadBoolean();
897 boolean present = input.loadBoolean();
898 parts = new LinkedHashSet<HardwareComponent>();
899 while(present) {
900 parts.add((HardwareComponent)input.loadObject());
901 present = input.loadBoolean();
903 rebootRequest = input.loadBoolean();
904 brb = (ResetButton)input.loadObject();
905 diskChanger = (DiskChanger)input.loadObject();
907 poller = (EventPoller)(input.loadObject());
908 dummyChannel = (OutputChannelDummy)input.loadObject();
911 public void dumpStatus(StatusDumper output)
913 if(output.dumped(this))
914 return;
916 output.println("#" + output.objectNumber(this) + ": PC:");
917 dumpStatusPartial(output);
918 output.endObject();
921 public PCHardwareInfo getHardwareInfo()
923 return hwInfo;
926 public boolean getAndClearTripleFaulted()
928 boolean flag = tripleFaulted;
929 tripleFaulted = false;
930 return flag;
934 public void dumpSRPartial(SRDumper output) throws IOException
936 output.dumpInt(cdromIndex);
937 output.dumpInt(sysRAMSize);
938 output.dumpInt(cpuClockDivider);
939 output.dumpObject(processor);
940 output.dumpObject(physicalAddr);
941 output.dumpObject(linearAddr);
942 output.dumpObject(vmClock);
943 output.dumpObject(images);
944 output.dumpObject(traceTrap);
945 output.dumpObject(manager);
946 output.dumpObject(hwInfo);
947 output.dumpObject(outputs);
948 output.dumpBoolean(hitTraceTrap);
949 output.dumpBoolean(tripleFaulted);
950 for(HardwareComponent part : parts) {
951 output.dumpBoolean(true);
952 output.dumpObject(part);
954 output.dumpBoolean(false);
955 output.dumpBoolean(rebootRequest);
956 output.dumpObject(brb);
957 output.dumpObject(diskChanger);
959 output.dumpObject(poller);
960 output.dumpObject(dummyChannel);
963 public static Map<String, Set<String>> parseHWModules(String moduleString) throws IOException
965 Map<String, Set<String>> ret = new LinkedHashMap<String, Set<String>>();
967 while(moduleString != null && !moduleString.equals("")) {
968 String currentModule;
969 int parenDepth = 0;
970 int nameEnd = -1;
971 int paramsStart = -1;
972 int paramsEnd = -1;
973 int stringLen = moduleString.length();
974 boolean requireNextSep = false;
976 for(int i = 0; true; i++) {
977 int cp;
978 if(i < stringLen)
979 cp = moduleString.codePointAt(i);
980 else if(parenDepth == 0)
981 cp = ','; //Hack, consider last character seperator.
982 else
983 throw new IOException("Error in module string: unclosed '('.");
984 if(cp >= 0x10000)
985 i++; //Skip the next surrogate.
986 if((cp >= 0xD800 && cp < 0xE000) || ((cp & 0xFFFE) == 0xFFFE) || (cp >>> 16) > 16 || cp < 0)
987 throw new IOException("Error In module string: invalid Unicode character.");
988 if(requireNextSep && cp != ',')
989 throw new IOException("Error in module string: Expected ',' after ')' closing parameter list.");
990 else if(cp == ',' && i == 0)
991 throw new IOException("Error in module string: Blank module name not allowed.");
992 else if(cp == '(') {
993 if(parenDepth == 0) {
994 paramsStart = i + 1;
995 nameEnd = i - 1;
997 parenDepth++;
998 } else if(cp == ')') {
999 if(parenDepth == 0)
1000 throw new IOException("Error in module string: Unpaired ')'.");
1001 else if(parenDepth == 1) {
1002 paramsEnd = i - 1;
1003 requireNextSep = true;
1005 parenDepth--;
1006 } else if(cp == ',' && parenDepth == 0) {
1007 if(nameEnd < 0)
1008 nameEnd = i - 1;
1009 currentModule = moduleString.substring(0, i);
1010 if(i < stringLen ) {
1011 moduleString = moduleString.substring(i + 1);
1012 if(moduleString.equals(""))
1013 throw new IOException("Error in module string: Blank module name not allowed.");
1014 } else
1015 moduleString = "";
1016 break;
1020 String name = currentModule.substring(0, nameEnd + 1);
1021 String params = null;
1022 if(paramsStart >= 0)
1023 params = currentModule.substring(paramsStart, paramsEnd + 1);
1025 if(ret.containsKey(name))
1026 ret.get(name).add(params);
1027 else {
1028 Set<String> foo = new LinkedHashSet<String>();
1029 foo.add(params);
1030 ret.put(name, foo);
1034 return ret;
1038 private static GenericBlockDevice blockdeviceFor(String name) throws IOException
1040 if(name == null)
1041 return null;
1042 return new GenericBlockDevice(new DiskImage(name, false));
1045 public static PC createPC(PCHardwareInfo hw) throws IOException
1047 PC pc;
1048 String biosID = arrayToString(hw.biosID);
1049 String vgaBIOSID = arrayToString(hw.vgaBIOSID);
1050 BlockDevice hda = blockdeviceFor(arrayToString(hw.hdaID));
1051 BlockDevice hdb = blockdeviceFor(arrayToString(hw.hdbID));
1052 BlockDevice hdc = blockdeviceFor(arrayToString(hw.hdcID));
1053 BlockDevice hdd = blockdeviceFor(arrayToString(hw.hddID));
1054 if(hdc == null) {
1055 hdc = new GenericBlockDevice(BlockDevice.Type.CDROM);
1058 DriveSet drives = new DriveSet(hw.bootType, hda, hdb, hdc, hdd);
1059 pc = new PC(drives, hw.memoryPages, hw.cpuDivider, biosID, vgaBIOSID, hw.initRTCTime, hw.images,
1060 hw.hwModules, hw.fpuEmulator, hw.booleanOptions, hw.intOptions);
1061 FloppyController fdc = (FloppyController)pc.getComponent(FloppyController.class);
1063 DiskImage img1 = pc.getDisks().lookupDisk(hw.initFDAIndex);
1064 fdc.changeDisk(img1, 0);
1066 DiskImage img2 = pc.getDisks().lookupDisk(hw.initFDBIndex);
1067 fdc.changeDisk(img2, 1);
1069 if(hdc.getType() == BlockDevice.Type.CDROM) {
1070 DiskImage img3 = pc.getDisks().lookupDisk(hw.initCDROMIndex);
1071 ((GenericBlockDevice)hdc).configure(img3);
1074 PCHardwareInfo hw2 = pc.getHardwareInfo();
1075 hw2.biosID = hw.biosID;
1076 hw2.vgaBIOSID = hw.vgaBIOSID;
1077 hw2.hdaID = hw.hdaID;
1078 hw2.hdbID = hw.hdbID;
1079 hw2.hdcID = hw.hdcID;
1080 hw2.hddID = hw.hddID;
1081 hw2.images = hw.images;
1082 hw2.initFDAIndex = hw.initFDAIndex;
1083 hw2.initFDBIndex = hw.initFDBIndex;
1084 hw2.initCDROMIndex = hw.initCDROMIndex;
1085 hw2.initRTCTime = hw.initRTCTime;
1086 hw2.cpuDivider = hw.cpuDivider;
1087 hw2.memoryPages = hw.memoryPages;
1088 hw2.bootType = hw.bootType;
1089 hw2.hwModules = hw.hwModules;
1090 hw2.fpuEmulator = hw.fpuEmulator;
1091 hw2.booleanOptions = hw.booleanOptions;
1092 hw2.intOptions = hw.intOptions;
1093 return pc;
1097 * Starts this PC's attached clock instance.
1099 public void start()
1101 vmClock.resume();
1105 * Stops this PC's attached clock instance
1107 public void stop()
1109 dummyChannel.addFrameDummy(vmClock.getTime());
1110 vmClock.pause();
1114 * Inserts the specified floppy disk into the drive identified.
1115 * @param disk new floppy disk to be inserted.
1116 * @param index drive which the disk is inserted into.
1118 private void changeFloppyDisk(DiskImage disk, int index) throws IOException
1120 ((FloppyController)getComponent(FloppyController.class)).changeDisk(disk, index);
1123 public void changeFloppyDisk(int driveIndex, int diskIndex) throws IOException
1125 diskChanger.changeFloppyDisk(driveIndex, diskIndex);
1128 public void wpFloppyDisk(int diskIndex, boolean turnOn) throws IOException
1130 diskChanger.wpFloppyDisk(diskIndex, turnOn);
1133 public static class DiskChanger extends AbstractHardwareComponent implements SRDumpable, EventDispatchTarget
1135 private EventRecorder eRecorder; //Not saved.
1136 private PC upperBackref;
1137 private int currentDriveA; //Not saved.
1138 private int currentDriveB; //Not saved.
1139 private int currentCDROM; //Not saved.
1140 private Set<Integer> usedDisks; //Not saved.
1142 private void checkFloppyChange(int driveIndex, int diskIndex) throws IOException
1144 if(driveIndex == 2 && upperBackref.cdromIndex < 0)
1145 throw new IOException("No CD-ROM drive available");
1146 if(diskIndex < -1)
1147 throw new IOException("Illegal disk number");
1148 DiskImage disk = upperBackref.images.lookupDisk(diskIndex);
1149 if(driveIndex < 0 || driveIndex > 2)
1150 throw new IOException("Illegal drive number");
1151 if(diskIndex >= 0 && (diskIndex == currentDriveA || diskIndex == currentDriveB ||
1152 diskIndex == currentCDROM))
1153 throw new IOException("Specified disk is already in some drive");
1154 if(diskIndex < 0 && driveIndex == 0 && currentDriveA < 0)
1155 throw new IOException("No disk present in drive A");
1156 if(diskIndex < 0 && driveIndex == 1 && currentDriveB < 0)
1157 throw new IOException("No disk present in drive B");
1158 if(diskIndex < 0 && driveIndex == 2 && currentCDROM < 0)
1159 throw new IOException("No disk present in CD-ROM Drive");
1160 if(diskIndex > 0 && driveIndex < 2 && (disk == null || disk.getType() != BlockDevice.Type.FLOPPY))
1161 throw new IOException("Attempt to put non-floppy into drive A or B");
1162 if(diskIndex > 0 && driveIndex == 2 && (disk == null || disk.getType() != BlockDevice.Type.CDROM))
1163 throw new IOException("Attempt to put non-CDROM into CDROM drive");
1164 if(diskIndex > 0)
1165 usedDisks.add(new Integer(diskIndex));
1168 private void checkFloppyWP(int diskIndex, boolean turnOn) throws IOException
1170 if(diskIndex < 0)
1171 throw new IOException("Illegal floppy disk number");
1172 if(diskIndex == currentDriveA || diskIndex == currentDriveB)
1173 throw new IOException("Can not manipulate WP of disk in drive");
1174 DiskImage disk = upperBackref.images.lookupDisk(diskIndex);
1175 if(disk == null || disk.getType() != BlockDevice.Type.FLOPPY)
1176 throw new IOException("Can not manipulate WP of non-floppy disk");
1179 public synchronized void changeFloppyDisk(int driveIndex, int diskIndex) throws IOException
1181 checkFloppyChange(driveIndex, diskIndex);
1182 upperBackref.images.lookupDisk(diskIndex);
1183 try {
1184 if(driveIndex == 0)
1185 eRecorder.addEvent(-1, getClass(), new String[]{"FDA", "" + diskIndex});
1186 else if(driveIndex == 1)
1187 eRecorder.addEvent(-1, getClass(), new String[]{"FDB", "" + diskIndex});
1188 else if(driveIndex == 2)
1189 eRecorder.addEvent(-1, getClass(), new String[]{"CDROM", "" + diskIndex});
1190 } catch(Exception e) {}
1193 public synchronized void wpFloppyDisk(int diskIndex, boolean turnOn) throws IOException
1195 checkFloppyWP(diskIndex, turnOn);
1196 DiskImage disk = upperBackref.images.lookupDisk(diskIndex);
1197 try {
1198 if(turnOn && !disk.isReadOnly())
1199 eRecorder.addEvent(-1, getClass(), new String[]{"WRITEPROTECT", "" + diskIndex});
1200 else if(!turnOn && disk.isReadOnly())
1201 eRecorder.addEvent(-1, getClass(), new String[]{"WRITEUNPROTECT", "" + diskIndex});
1202 } catch(Exception e) {}
1205 public void doEvent(long timeStamp, String[] args, int level) throws IOException
1207 if(args == null || args.length != 2)
1208 throw new IOException("Invalid disk event parameters");
1209 int disk;
1210 try {
1211 disk = Integer.parseInt(args[1]);
1212 } catch(Exception e) {
1213 throw new IOException("Invalid disk number");
1215 DiskImage diskImg = upperBackref.images.lookupDisk(disk);
1217 if("FDA".equals(args[0])) {
1218 if(level <= EventRecorder.EVENT_STATE_EFFECT) {
1219 checkFloppyChange(0, disk);
1220 currentDriveA = disk;
1222 if(level == EventRecorder.EVENT_EXECUTE)
1223 upperBackref.changeFloppyDisk(diskImg, 0);
1224 } else if("FDB".equals(args[0])) {
1225 if(level <= EventRecorder.EVENT_STATE_EFFECT) {
1226 checkFloppyChange(1, disk);
1227 currentDriveB = disk;
1229 if(level == EventRecorder.EVENT_EXECUTE)
1230 upperBackref.changeFloppyDisk(diskImg, 1);
1231 } else if("CDROM".equals(args[0])) {
1232 if(level <= EventRecorder.EVENT_STATE_EFFECT) {
1233 checkFloppyChange(2, disk);
1234 currentCDROM = disk;
1236 DriveSet drives = (DriveSet)upperBackref.getComponent(DriveSet.class);
1237 if(level == EventRecorder.EVENT_EXECUTE)
1238 try {
1239 ((GenericBlockDevice)drives.getHardDrive(upperBackref.cdromIndex)).configure(diskImg);
1240 } catch(Exception e) {
1241 System.err.println("Warning: Unable to change disk in CD-ROM drive");
1243 } else if("WRITEPROTECT".equals(args[0])) {
1244 if(level <= EventRecorder.EVENT_STATE_EFFECT)
1245 checkFloppyWP(disk, true);
1246 if(level == EventRecorder.EVENT_EXECUTE)
1247 diskImg.setWP(true);
1248 } else if("WRITEUNPROTECT".equals(args[0])) {
1249 if(level <= EventRecorder.EVENT_STATE_EFFECT)
1250 checkFloppyWP(disk, false);
1251 if(level == EventRecorder.EVENT_EXECUTE)
1252 diskImg.setWP(false);
1253 } else
1254 throw new IOException("Invalid disk event type");
1257 public void startEventCheck()
1259 currentDriveA = upperBackref.hwInfo.initFDAIndex;
1260 currentDriveB = upperBackref.hwInfo.initFDBIndex;
1261 currentCDROM = upperBackref.hwInfo.initCDROMIndex;
1262 usedDisks = new HashSet<Integer>();
1263 if(currentDriveA >= 0)
1264 usedDisks.add(currentDriveA);
1265 if(currentDriveB >= 0)
1266 usedDisks.add(currentDriveB);
1267 if(currentCDROM >= 0)
1268 usedDisks.add(currentCDROM);
1271 private Set<Integer> usedDiskSet()
1273 return usedDisks;
1276 public void endEventCheck() throws IOException
1278 //Nothing to do.
1281 public DiskChanger(PC pc)
1283 upperBackref = pc;
1286 public DiskChanger(SRLoader input) throws IOException
1288 super(input);
1289 upperBackref = (PC)input.loadObject();
1292 public void dumpSRPartial(SRDumper output) throws IOException
1294 super.dumpSRPartial(output);
1295 output.dumpObject(upperBackref);
1298 public long getEventTimeLowBound(long stamp, String[] args) throws IOException
1300 return -1; //No timing constraints.
1303 public void setEventRecorder(EventRecorder recorder)
1305 eRecorder = recorder;
1308 public void dumpStatus(StatusDumper output)
1310 if(output.dumped(this))
1311 return;
1313 output.println("#" + output.objectNumber(this) + ": DiskChanger:");
1314 output.endObject();
1319 public DiskImageSet getDisks()
1321 return images;
1324 private boolean configure() {
1325 boolean fullyInitialised;
1326 int count = 0;
1327 do {
1328 fullyInitialised = true;
1329 for(HardwareComponent outer : parts) {
1330 if(outer.initialised())
1331 continue;
1333 for(HardwareComponent inner : parts)
1334 outer.acceptComponent(inner);
1336 fullyInitialised &= outer.initialised();
1338 count++;
1339 } while((fullyInitialised == false) && (count < 100));
1341 if(!fullyInitialised) {
1342 for(HardwareComponent hwc : parts)
1343 if(!hwc.initialised())
1344 System.err.println("Error: Component of type " + hwc.getClass() + " failed to initialize.");
1345 System.err.println("Critical error: PC component initialization failed.");
1346 return false;
1349 for(HardwareComponent hwc : parts)
1350 if(hwc instanceof PCIBus)
1351 ((PCIBus)hwc).biosInit();
1353 return true;
1356 public void setFPUHack()
1358 physicalAddr.setFPUHack();
1361 public void setVGADrawHack()
1363 HardwareComponent displayController = getComponent(VGACard.class);
1364 if(displayController != null)
1365 ((VGACard)displayController).setVGADrawHack();
1368 public void setVGAScroll2Hack()
1370 HardwareComponent displayController = getComponent(VGACard.class);
1371 if(displayController != null)
1372 ((VGACard)displayController).setVGAScroll2Hack();
1376 * Reset this PC back to its initial state.
1377 * <p>
1378 * This is roughly equivalent to a hard-reset (power down-up cycle).
1380 protected void reset()
1382 for(HardwareComponent hwc : parts)
1383 hwc.reset();
1384 configure();
1387 public void reboot()
1389 brb.reboot();
1392 public static class ResetButton extends AbstractHardwareComponent implements SRDumpable, EventDispatchTarget
1394 private EventRecorder eRecorder; //Not saved.
1395 private PC upperBackref;
1397 public EventRecorder getRecorder()
1399 return eRecorder;
1402 public void reboot()
1404 try {
1405 eRecorder.addEvent(-1, getClass(), null);
1406 } catch(Exception e) {}
1409 public void startEventCheck()
1411 //No state.
1414 public void doEvent(long timeStamp, String[] args, int level) throws IOException
1416 if(args != null)
1417 throw new IOException("Invalid reboot event");
1418 if(level == EventRecorder.EVENT_EXECUTE) {
1419 upperBackref.processor.eflagsMachineHalt = true;
1420 upperBackref.rebootRequest = true;
1424 public void endEventCheck() throws IOException
1428 public ResetButton(PC pc)
1430 upperBackref = pc;
1433 public ResetButton(SRLoader input) throws IOException
1435 super(input);
1436 upperBackref = (PC)input.loadObject();
1439 public void dumpSRPartial(SRDumper output) throws IOException
1441 super.dumpSRPartial(output);
1442 output.dumpObject(upperBackref);
1445 public long getEventTimeLowBound(long stamp, String[] args) throws IOException
1447 return -1; //No timing constraints.
1450 public void setEventRecorder(EventRecorder recorder)
1452 eRecorder = recorder;
1455 public void dumpStatus(StatusDumper output)
1457 if(output.dumped(this))
1458 return;
1460 output.println("#" + output.objectNumber(this) + ": ResetButton:");
1461 output.endObject();
1466 * Get an subclass of <code>cls</code> from this instance's parts list.
1467 * <p>
1468 * If <code>cls</code> is not assignment compatible with <code>HardwareComponent</code>
1469 * then this method will return null immediately.
1470 * @param cls component type required.
1471 * @return an instance of class <code>cls</code>, or <code>null</code> on failure
1473 public HardwareComponent getComponent(Class<?> cls)
1475 for(HardwareComponent hwc : parts)
1476 if(cls.isInstance(hwc))
1477 return hwc;
1479 return null;
1482 public Set<HardwareComponent> allComponents()
1484 return parts;
1488 * Gets the processor instance associated with this PC.
1489 * @return associated processor instance.
1491 public Processor getProcessor()
1493 return processor;
1497 * Execute an arbitrarily large amount of code on this instance.
1498 * <p>
1499 * This method will execute continuously until there is either a mode switch,
1500 * or a unspecified large number of instructions have completed. It should
1501 * never run indefinitely.
1502 * @return total number of x86 instructions executed.
1504 public final int execute()
1506 if(processor.isProtectedMode())
1507 if(processor.isVirtual8086Mode())
1508 return executeVirtual8086();
1509 else
1510 return executeProtected();
1511 else
1512 return executeReal();
1515 public final int executeReal()
1517 int x86Count = 0;
1519 if(rebootRequest) {
1520 reset();
1521 rebootRequest = false;
1524 try {
1525 for(int i = 0; i < 100; i++) {
1526 int block;
1527 try {
1528 block = physicalAddr.executeReal(processor, processor.getInstructionPointer());
1529 } catch(org.jpc.emulator.processor.Processor.TripleFault e) {
1530 reset(); //Reboot the system to get the CPU back online.
1531 hitTraceTrap = true;
1532 tripleFaulted = true;
1533 break;
1535 x86Count += block;
1536 processor.instructionsExecuted += block;
1537 //Don't call this on aborted blocks. Doing so is probably good source of desyncs.
1538 if(!processor.eflagsLastAborted)
1539 processor.processRealModeInterrupts(1);
1540 if(traceTrap.getAndClearTrapActive()) {
1541 hitTraceTrap = true;
1542 break;
1544 if(rebootRequest) {
1545 reset();
1546 rebootRequest = false;
1547 break;
1550 } catch (ProcessorException p) {
1551 processor.handleRealModeException(p);
1552 } catch (ModeSwitchException e) {
1553 //System.err.println("Informational: CPU switching modes: " + e.toString());
1555 return x86Count;
1558 public TraceTrap getTraceTrap()
1560 return traceTrap;
1563 public boolean getHitTraceTrap()
1565 boolean tmp = hitTraceTrap;
1566 hitTraceTrap = false;
1567 return tmp;
1570 public final int executeProtected()
1572 int x86Count = 0;
1574 if(rebootRequest) {
1575 reset();
1576 rebootRequest = false;
1579 try {
1580 for(int i = 0; i < 100; i++) {
1581 int block;
1582 try {
1583 block= linearAddr.executeProtected(processor, processor.getInstructionPointer());
1584 } catch(org.jpc.emulator.processor.Processor.TripleFault e) {
1585 reset(); //Reboot the system to get the CPU back online.
1586 hitTraceTrap = true;
1587 tripleFaulted = true;
1588 break;
1590 x86Count += block;
1591 processor.instructionsExecuted += block;
1592 //Don't call this on aborted blocks. Doing so is probably good source of desyncs.
1593 if(!processor.eflagsLastAborted)
1594 processor.processProtectedModeInterrupts(1);
1595 if(traceTrap.getAndClearTrapActive()) {
1596 hitTraceTrap = true;
1597 break;
1599 if(rebootRequest) {
1600 reset();
1601 rebootRequest = false;
1602 break;
1605 } catch (ProcessorException p) {
1606 processor.handleProtectedModeException(p);
1607 } catch (ModeSwitchException e) {
1608 //System.err.println("Informational: CPU switching modes: " + e.toString());
1610 return x86Count;
1613 public final int executeVirtual8086()
1615 int x86Count = 0;
1617 if(rebootRequest) {
1618 reset();
1619 rebootRequest = false;
1622 try {
1623 for(int i = 0; i < 100; i++) {
1624 int block;
1625 try {
1626 block = linearAddr.executeVirtual8086(processor, processor.getInstructionPointer());
1627 } catch(org.jpc.emulator.processor.Processor.TripleFault e) {
1628 reset(); //Reboot the system to get the CPU back online.
1629 hitTraceTrap = true;
1630 tripleFaulted = true;
1631 break;
1633 x86Count += block;
1634 processor.instructionsExecuted += block;
1635 //Don't call this on aborted blocks. Doing so is probably good source of desyncs.
1636 if(!processor.eflagsLastAborted)
1637 processor.processVirtual8086ModeInterrupts(1);
1638 if(traceTrap.getAndClearTrapActive()) {
1639 hitTraceTrap = true;
1640 break;
1642 if(rebootRequest) {
1643 reset();
1644 rebootRequest = false;
1645 break;
1648 } catch (ProcessorException p) {
1649 processor.handleVirtual8086ModeException(p);
1650 } catch (ModeSwitchException e) {
1651 //System.err.println("Informational: CPU switching modes: " + e.toString());
1653 return x86Count;
1656 public static class PCFullStatus
1658 public PC pc; //Loaded SAVED.
1659 public EventRecorder events; //Loaded SAVED.
1660 public String projectID; //Loaded SAVED.
1661 public String savestateID; //Loaded SAVED.
1662 public long rerecords; //Loaded SAVED.
1663 public String[][] extraHeaders; //Loaded SAVED.
1666 private static void saveDiskInfo(UTFOutputLineStream lines, byte[] diskID)
1668 ImageLibrary lib = DiskImage.getLibrary();
1669 String fileName = lib.lookupFileName(diskID);
1670 if(fileName == null) {
1671 System.err.println("Warning: Can't find used disk from library (SHOULD NOT HAPPEN!).");
1672 return;
1674 try {
1675 ImageMaker.ParsedImage pimg = new ImageMaker.ParsedImage(fileName);
1676 RandomAccessFile image = new RandomAccessFile(fileName, "r");
1677 switch(pimg.typeCode) {
1678 case 0:
1679 lines.encodeLine("TYPE", "FLOPPY");
1680 break;
1681 case 1:
1682 lines.encodeLine("TYPE", "HDD");
1683 break;
1684 case 2:
1685 lines.encodeLine("TYPE", "CDROM");
1686 break;
1687 case 3:
1688 lines.encodeLine("TYPE", "BIOS");
1689 break;
1690 default:
1691 lines.encodeLine("TYPE", "UNKNOWN");
1692 break;
1694 lines.encodeLine("ID", new ImageLibrary.ByteArray(pimg.diskID));
1695 switch(pimg.typeCode) {
1696 case 0:
1697 case 1: //Floppies/HDD have the same fields.
1698 lines.encodeLine("TRACKS", pimg.tracks);
1699 lines.encodeLine("SIDES", pimg.sides);
1700 lines.encodeLine("SECTORS", pimg.sectors);
1701 case 2: //Floppies/HDD have these fields as well.
1702 lines.encodeLine("TOTALSECTORS", pimg.totalSectors);
1703 byte[] sector = new byte[512];
1704 byte[] zero = new byte[512];
1705 MessageDigest md = MessageDigest.getInstance("MD5");
1706 for(int i = 0; i < pimg.totalSectors; i++) {
1707 if(i < pimg.sectorOffsetMap.length && pimg.sectorOffsetMap[i] > 0) {
1708 image.seek(pimg.sectorOffsetMap[i]);
1709 if(image.read(sector) < 512) {
1710 throw new IOException("Failed to read sector from image file.");
1712 md.update(sector);
1713 } else
1714 md.update(zero);
1716 lines.encodeLine("IMAGEMD5", new ImageLibrary.ByteArray(md.digest()));
1717 break;
1718 case 3: //BIOS
1719 lines.encodeLine("IMAGELENGTH", pimg.rawImage.length);
1720 md = MessageDigest.getInstance("MD5");
1721 md.update(pimg.rawImage);
1722 lines.encodeLine("IMAGEMD5", new ImageLibrary.ByteArray(md.digest()));
1725 List<String> comments = pimg.comments;
1726 if(comments != null) {
1727 for(String x : comments)
1728 lines.encodeLine("COMMENT", x);
1730 image.close();
1731 } catch(Exception e) {
1732 System.err.println("Warning: Can't lookup disk information: " + e.getMessage() + "[" + e.getClass().getName() + "].");
1736 private static void saveDiskInfo(JRSRArchiveWriter writer, DiskImage image, Set<ImageLibrary.ByteArray> saved) throws IOException
1738 if(image == null)
1739 return;
1740 saveDiskInfo(writer, image.getImageID(), saved);
1743 private static void saveDiskInfo(JRSRArchiveWriter writer, byte[] diskID, Set<ImageLibrary.ByteArray> saved) throws IOException
1745 if(diskID == null)
1746 return;
1747 ImageLibrary.ByteArray id = new ImageLibrary.ByteArray(diskID);
1748 if(saved.contains(id))
1749 return;
1750 saved.add(id);
1751 UTFOutputLineStream lines = new UTFOutputLineStream(writer.addMember("diskinfo-" + arrayToString(diskID)));
1752 saveDiskInfo(lines, diskID);
1753 lines.close();
1756 public static void saveSavestate(JRSRArchiveWriter writer, PCFullStatus fullStatus, boolean movie, boolean noCompress)
1757 throws IOException
1759 fullStatus.savestateID = randomHexes(24);
1760 fullStatus.events.markSave(fullStatus.savestateID, fullStatus.rerecords);
1762 //Save the header.
1763 UTFOutputLineStream lines = new UTFOutputLineStream(writer.addMember("header"));
1764 lines.writeLine("PROJECTID " + fullStatus.projectID);
1765 if(!movie)
1766 lines.writeLine("SAVESTATEID " + fullStatus.savestateID);
1767 lines.writeLine("RERECORDS " + fullStatus.rerecords);
1768 lines.writeLine("SYSTEM PC-JPC-RR-r10");
1769 if(fullStatus.extraHeaders != null)
1770 for(int i = 0; i < fullStatus.extraHeaders.length; i++) {
1771 Object[] arr = new Object[fullStatus.extraHeaders[i].length];
1772 System.arraycopy(fullStatus.extraHeaders[i], 0, arr, 0, arr.length);
1773 lines.encodeLine(arr);
1775 lines.close();
1777 //Save intialization segment.
1778 lines = new UTFOutputLineStream(writer.addMember("initialization"));
1779 fullStatus.pc.getHardwareInfo().makeHWInfoSegment(lines, fullStatus.pc.diskChanger);
1780 lines.close();
1782 //Save savestate itsefl (if any).
1783 if(!movie) {
1784 FourToFiveEncoder entry = new FourToFiveEncoder(writer.addMember("savestate"));
1785 DeflaterOutputStream dos;
1786 Deflater deflater = new Deflater(noCompress ? Deflater.NO_COMPRESSION : Deflater.DEFAULT_COMPRESSION);
1787 OutputStream zip = dos = new DeflaterOutputStream(entry, deflater);
1788 SRDumper dumper = new SRDumper(zip);
1789 dumper.dumpObject(fullStatus.pc);
1790 dumper.flush();
1791 dos.close();
1793 OutputStream entry2 = writer.addMember("manifest");
1794 dumper.writeConstructorManifest(entry2);
1795 entry2.close();
1798 //Save the movie events.
1799 lines = new UTFOutputLineStream(writer.addMember("events"));
1800 fullStatus.events.saveEvents(lines);
1801 lines.close();
1803 //Save the disk info.
1804 PCHardwareInfo hw = fullStatus.pc.getHardwareInfo();
1805 DiskImageSet images = hw.images;
1806 int disks = 1 + images.highestDiskIndex();
1807 Set<ImageLibrary.ByteArray> imageSet = new HashSet<ImageLibrary.ByteArray>();
1808 for(int i = 0; i < disks; i++)
1809 saveDiskInfo(writer, images.lookupDisk(i), imageSet);
1810 saveDiskInfo(writer, hw.biosID, imageSet);
1811 saveDiskInfo(writer, hw.vgaBIOSID, imageSet);
1812 saveDiskInfo(writer, hw.hdaID, imageSet);
1813 saveDiskInfo(writer, hw.hdbID, imageSet);
1814 saveDiskInfo(writer, hw.hdcID, imageSet);
1815 saveDiskInfo(writer, hw.hddID, imageSet);
1818 public static PCFullStatus loadSavestate(JRSRArchiveReader reader, boolean reuse, boolean forceMovie,
1819 PCFullStatus existing) throws IOException
1821 PCFullStatus fullStatus = new PCFullStatus();
1822 boolean ssPresent = false;
1823 UTFInputLineStream lines = new UTFInputLineStream(reader.readMember("header"));
1825 fullStatus.rerecords = -1;
1827 String[] components = nextParseLine(lines);
1828 while(components != null) {
1829 if("SAVESTATEID".equals(components[0])) {
1830 if(components.length != 2)
1831 throw new IOException("Bad " + components[0] + " line in header segment: " +
1832 "expected 2 components, got " + components.length);
1833 ssPresent = true;
1834 fullStatus.savestateID = components[1];
1835 } else if("PROJECTID".equals(components[0])) {
1836 if(components.length != 2)
1837 throw new IOException("Bad " + components[0] + " line in header segment: " +
1838 "expected 2 components, got " + components.length);
1839 fullStatus.projectID = components[1];
1840 } else if("RERECORDS".equals(components[0])) {
1841 if(components.length != 2)
1842 throw new IOException("Bad " + components[0] + " line in header segment: " +
1843 "expected 2 components, got " + components.length);
1844 try {
1845 fullStatus.rerecords = Long.parseLong(components[1]);
1846 if(fullStatus.rerecords < 0) {
1847 throw new IOException("Invalid rerecord count");
1849 } catch(NumberFormatException e) {
1850 throw new IOException("Invalid rerecord count");
1852 } else if("SYSTEM".equals(components[0])) {
1853 if(components.length != 2)
1854 throw new IOException("Bad " + components[0] + " line in header segment: " +
1855 "expected 2 components, got " + components.length);
1856 if(!"PC-JPC-RR-r10".equals(components[1]))
1857 throw new IOException("Invalid system type '" + components[1] + "'");
1858 } else {
1859 if(fullStatus.extraHeaders == null) {
1860 fullStatus.extraHeaders = new String[1][];
1861 fullStatus.extraHeaders[0] = components;
1862 } else {
1863 String[][] extraHeaders = new String[fullStatus.extraHeaders.length + 1][];
1864 System.arraycopy(fullStatus.extraHeaders, 0, extraHeaders, 0, fullStatus.extraHeaders.length);
1865 extraHeaders[fullStatus.extraHeaders.length] = components;
1866 fullStatus.extraHeaders = extraHeaders;
1869 components = nextParseLine(lines);
1872 if(fullStatus.projectID == null)
1873 throw new IOException("PROJECTID header missing");
1874 if(fullStatus.rerecords < 0)
1875 throw new IOException("RERECORDS header missing");
1877 if(ssPresent && !forceMovie) {
1878 InputStream entry = reader.readMember("manifest");
1879 if(!SRLoader.checkConstructorManifest(entry))
1880 throw new IOException("Wrong savestate version");
1881 entry.close();
1883 entry = new FourToFiveDecoder(reader.readMember("savestate"));
1884 SRLoader loader = new SRLoader(new InflaterInputStream(entry));
1885 fullStatus.pc = (PC)(loader.loadObject());
1886 entry.close();
1887 } else {
1888 lines = new UTFInputLineStream(reader.readMember("initialization"));
1889 PC.PCHardwareInfo hwInfo = PC.PCHardwareInfo.parseHWInfoSegment(lines);
1890 fullStatus.pc = createPC(hwInfo);
1893 if(reuse)
1894 fullStatus.events = existing.events;
1895 else {
1896 lines = new UTFInputLineStream(reader.readMember("events"));
1897 fullStatus.events = new EventRecorder(lines);
1900 if(reuse && (existing == null || !fullStatus.projectID.equals(existing.projectID)))
1901 throw new IOException("Savestate is not from current movie");
1903 fullStatus.events.attach(fullStatus.pc, forceMovie ? null : fullStatus.savestateID);
1905 if(!reuse) {
1906 fullStatus.events.setHeaders(fullStatus.extraHeaders);
1907 fullStatus.events.setRerecordCount(fullStatus.rerecords);
1910 if(existing == null || !fullStatus.projectID.equals(existing.projectID))
1911 fullStatus.rerecords++;
1912 else
1913 if(existing != null && existing.rerecords > fullStatus.rerecords)
1914 fullStatus.rerecords = existing.rerecords + 1;
1915 else
1916 fullStatus.rerecords++;
1918 return fullStatus;