Clean up emulator internal error messages
[jpcrr.git] / org / jpc / emulator / PC.java
blobb45ff6af1cc02892d480a34969e53646532963e1
1 /*
2 JPC-RR: A x86 PC Hardware Emulator
3 Release 1
5 Copyright (C) 2007-2009 Isis Innovation Limited
6 Copyright (C) 2009 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 java.io.*;
52 import java.util.*;
53 import java.util.zip.*;
54 import java.lang.reflect.*;
55 import java.security.MessageDigest;
56 import org.jpc.emulator.memory.codeblock.CodeBlockManager;
58 import static org.jpc.Misc.arrayToString;
59 import static org.jpc.Misc.stringToArray;
60 import static org.jpc.Misc.nextParseLine;
61 import static org.jpc.Misc.randomHexes;
63 /**
64 * This class represents the emulated PC as a whole, and holds references
65 * to its main hardware components.
66 * @author Chris Dennis
67 * @author Ian Preston
69 public class PC implements SRDumpable
71 public static class PCHardwareInfo implements SRDumpable
73 public byte[] biosID;
74 public byte[] vgaBIOSID;
75 public byte[] hdaID;
76 public byte[] hdbID;
77 public byte[] hdcID;
78 public byte[] hddID;
79 public DiskImageSet images;
80 public int initFDAIndex;
81 public int initFDBIndex;
82 public int initCDROMIndex;
83 public long initRTCTime;
84 public int cpuDivider;
85 public int memoryPages;
86 public String fpuEmulator;
87 public Map<String, Set<String>> hwModules;
88 public DriveSet.BootType bootType;
89 public boolean ioportDelayed;
90 public boolean vgaHretrace;
92 public void dumpStatusPartial(StatusDumper output2) throws IOException
94 if(output2 != null)
95 return;
97 PrintStream output = System.err;
99 output.println("BIOS " + arrayToString(biosID));
100 output.println("VGABIOS " + arrayToString(vgaBIOSID));
101 if(hdaID != null)
102 output.println("HDA " + arrayToString(hdaID));
103 if(hdbID != null)
104 output.println("HDB " + arrayToString(hdbID));
105 if(hdcID != null)
106 output.println("HDC " + arrayToString(hdcID));
107 if(hddID != null)
108 output.println("HDD " + arrayToString(hddID));
109 int disks = 1 + images.highestDiskIndex();
110 for(int i = 0; i < disks; i++) {
111 DiskImage disk = images.lookupDisk(i);
112 if(disk != null)
113 output.println("DISK " + i + " " + arrayToString(disk.getImageID()));
115 if(initFDAIndex >= 0)
116 output.println("FDA " + initFDAIndex);
117 if(initFDBIndex >= 0)
118 output.println("FDB " + initFDBIndex);
119 if(initCDROMIndex >= 0)
120 output.println("CDROM " + initCDROMIndex);
121 output.println("INITIALTIME " + initRTCTime);
122 output.println("CPUDIVIDER " + (cpuDivider - 1));
123 if(fpuEmulator != null)
124 output.println("FPU " + fpuEmulator);
125 if(bootType == DriveSet.BootType.FLOPPY)
126 output.println("BOOT FLOPPY");
127 else if(bootType == DriveSet.BootType.HARD_DRIVE)
128 output.println("BOOT HDD");
129 else if(bootType == DriveSet.BootType.CDROM)
130 output.println("BOOT CDROM");
131 else if(bootType == null)
133 else
134 throw new IOException("Unknown boot type");
135 if(hwModules != null && !hwModules.isEmpty()) {
136 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
137 for(String p : e.getValue())
138 if(p != null)
139 output.println("LOADMODULEA " + e.getKey() + "(" + p + ")");
140 else
141 output.println("LOADMODULE " + e.getKey());
144 if(ioportDelayed)
145 output.println("IOPORTDELAY");
146 if(vgaHretrace)
147 output.println("VGAHRETRACE");
150 public void dumpStatus(StatusDumper output)
152 if(output.dumped(this))
153 return;
155 output.println("#" + output.objectNumber(this) + ": PCHardwareInfo:");
156 try { dumpStatusPartial(output); } catch(Exception e) {}
157 output.endObject();
160 public void dumpSRPartial(SRDumper output) throws IOException
162 output.dumpArray(biosID);
163 output.dumpArray(vgaBIOSID);
164 output.dumpArray(hdaID);
165 output.dumpArray(hdbID);
166 output.dumpArray(hdcID);
167 output.dumpArray(hddID);
168 output.dumpObject(images);
169 output.dumpInt(initFDAIndex);
170 output.dumpInt(initFDBIndex);
171 output.dumpInt(initCDROMIndex);
172 output.dumpLong(initRTCTime);
173 output.dumpInt(cpuDivider);
174 output.dumpInt(memoryPages);
175 output.dumpString(fpuEmulator);
176 if(hwModules != null) {
177 output.dumpBoolean(true);
178 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
179 output.dumpBoolean(true);
180 output.dumpString(e.getKey());
181 for(String s : e.getValue()) {
182 output.dumpBoolean(true);
183 output.dumpString(s);
185 output.dumpBoolean(false);
187 output.dumpBoolean(false);
188 } else
189 output.dumpBoolean(false);
190 output.dumpByte(DriveSet.BootType.toNumeric(bootType));
191 output.dumpBoolean(ioportDelayed);
192 output.dumpBoolean(vgaHretrace);
195 public PCHardwareInfo()
197 images = new DiskImageSet();
200 public PCHardwareInfo(SRLoader input) throws IOException
202 input.objectCreated(this);
203 biosID = input.loadArrayByte();
204 vgaBIOSID = input.loadArrayByte();
205 hdaID = input.loadArrayByte();
206 hdbID = input.loadArrayByte();
207 hdcID = input.loadArrayByte();
208 hddID = input.loadArrayByte();
209 images = (DiskImageSet)input.loadObject();
210 initFDAIndex = input.loadInt();
211 initFDBIndex = input.loadInt();
212 initCDROMIndex = input.loadInt();
213 initRTCTime = input.loadLong();
214 cpuDivider = input.loadInt();
215 memoryPages = input.loadInt();
216 fpuEmulator = input.loadString();
217 boolean present = input.loadBoolean();
218 if(present) {
219 hwModules = new LinkedHashMap<String, Set<String>>();
220 present = input.loadBoolean();
221 while(present) {
222 String name = input.loadString();
223 hwModules.put(name, new LinkedHashSet<String>());
224 boolean present2 = input.loadBoolean();
225 while(present2) {
226 String params = input.loadString();
227 present2 = input.loadBoolean();
228 hwModules.get(name).add(params);
230 present = input.loadBoolean();
233 bootType = DriveSet.BootType.fromNumeric(input.loadByte());
234 ioportDelayed = false;
235 vgaHretrace = false;
236 if(input.objectEndsHere())
237 return;
238 ioportDelayed = input.loadBoolean();
239 if(input.objectEndsHere())
240 return;
241 vgaHretrace = input.loadBoolean();
244 public void makeHWInfoSegment(UTFOutputLineStream output, DiskChanger changer) throws IOException
246 output.encodeLine("BIOS", arrayToString(biosID));
247 output.encodeLine("VGABIOS", arrayToString(vgaBIOSID));
248 output.encodeLine("HDA", arrayToString(hdaID));
249 output.encodeLine("HDB", arrayToString(hdbID));
250 output.encodeLine("HDC", arrayToString(hdcID));
251 output.encodeLine("HDD", arrayToString(hddID));
252 //TODO: When event recording becomes available, only save the disk images needed.
253 Set<Integer> usedDisks = changer.usedDiskSet();
254 int disks = 1 + images.highestDiskIndex();
255 for(int i = 0; i < disks; i++) {
256 DiskImage disk = images.lookupDisk(i);
257 if(disk != null && usedDisks.contains(i)) {
258 output.encodeLine("DISK", i, arrayToString(disk.getImageID()));
259 output.encodeLine("DISKNAME", i, disk.getName());
262 if(initFDAIndex >= 0) output.encodeLine("FDA", initFDAIndex);
263 if(initFDBIndex >= 0) output.encodeLine("FDB", initFDBIndex);
264 if(initCDROMIndex >= 0) output.encodeLine("CDROM", initCDROMIndex);
265 output.encodeLine("INITIALTIME", initRTCTime);
266 output.encodeLine("CPUDIVIDER", cpuDivider);
267 output.encodeLine("MEMORYSIZE", memoryPages);
268 output.encodeLine("FPU", fpuEmulator);
269 if(bootType == DriveSet.BootType.FLOPPY) output.encodeLine("BOOT", "FLOPPY");
270 else if(bootType == DriveSet.BootType.HARD_DRIVE) output.encodeLine("BOOT", "HDD");
271 else if(bootType == DriveSet.BootType.CDROM) output.encodeLine("BOOT", "CDROM");
272 else if(bootType == null)
274 else
275 throw new IOException("Unknown boot type");
276 if(hwModules != null && !hwModules.isEmpty()) {
277 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
278 for(String p : e.getValue())
279 if(p != null)
280 output.encodeLine("LOADMODULEA", e.getKey(), p);
281 else
282 output.encodeLine("LOADMODULE", e.getKey());
285 if(ioportDelayed)
286 output.encodeLine("IOPORTDELAY");
287 if(vgaHretrace)
288 output.encodeLine("VGAHRETRACE");
291 public static int componentsForLine(String op)
293 if("BIOS".equals(op))
294 return 2;
295 if("VGABIOS".equals(op))
296 return 2;
297 if("HDA".equals(op))
298 return 2;
299 if("HDB".equals(op))
300 return 2;
301 if("HDC".equals(op))
302 return 2;
303 if("HDD".equals(op))
304 return 2;
305 if("FDA".equals(op))
306 return 2;
307 if("FDB".equals(op))
308 return 2;
309 if("CDROM".equals(op))
310 return 2;
311 if("INITIALTIME".equals(op))
312 return 2;
313 if("CPUDIVIDER".equals(op))
314 return 2;
315 if("MEMORYSIZE".equals(op))
316 return 2;
317 if("FPU".equals(op))
318 return 2;
319 if("BOOT".equals(op))
320 return 2;
321 if("LOADMODULE".equals(op))
322 return 2;
323 if("LOADMODULEA".equals(op))
324 return 3;
325 if("DISK".equals(op))
326 return 3;
327 if("DISKNAME".equals(op))
328 return 3;
329 if("IOPORTDELAY".equals(op))
330 return 1;
331 if("VGAHRETRACE".equals(op))
332 return 1;
333 return 0;
337 public static PCHardwareInfo parseHWInfoSegment(UTFInputLineStream input) throws IOException
340 PCHardwareInfo hw = new PCHardwareInfo();
341 hw.initFDAIndex = -1;
342 hw.initFDBIndex = -1;
343 hw.initCDROMIndex = -1;
344 hw.images = new DiskImageSet();
345 hw.hwModules = new LinkedHashMap<String, Set<String>>();
346 String[] components = nextParseLine(input);
347 while(components != null) {
348 if(components.length != componentsForLine(components[0]))
349 throw new IOException("Bad " + components[0] + " line in ininitialization segment: " +
350 "expected " + componentsForLine(components[0]) + " components, got " + components.length);
351 if("BIOS".equals(components[0]))
352 hw.biosID = stringToArray(components[1]);
353 else if("VGABIOS".equals(components[0]))
354 hw.vgaBIOSID = stringToArray(components[1]);
355 else if("HDA".equals(components[0]))
356 hw.hdaID = stringToArray(components[1]);
357 else if("HDB".equals(components[0]))
358 hw.hdbID = stringToArray(components[1]);
359 else if("HDC".equals(components[0]))
360 hw.hdcID = stringToArray(components[1]);
361 else if("HDD".equals(components[0]))
362 hw.hddID = stringToArray(components[1]);
363 else if("DISK".equals(components[0])) {
364 int id;
365 try {
366 id = Integer.parseInt(components[1]);
367 if(id < 0)
368 throw new NumberFormatException("Bad id");
369 } catch(NumberFormatException e) {
370 throw new IOException("Bad DISK line in initialization segment");
372 hw.images.addDisk(id, new DiskImage(components[2], false));
373 } else if("DISKNAME".equals(components[0])) {
374 int id;
375 try {
376 id = Integer.parseInt(components[1]);
377 if(id < 0)
378 throw new NumberFormatException("Bad id");
379 hw.images.lookupDisk(id).setName(components[2]);
380 } catch(Exception e) {
381 throw new IOException("Bad DISKNAME line in initialization segment");
383 } else if("FDA".equals(components[0])) {
384 int id;
385 try {
386 id = Integer.parseInt(components[1]);
387 if(id < 0)
388 throw new NumberFormatException("Bad id");
389 } catch(NumberFormatException e) {
390 throw new IOException("Bad FDA line in initialization segment");
392 hw.initFDAIndex = id;
393 } else if("FDB".equals(components[0])) {
394 int id;
395 try {
396 id = Integer.parseInt(components[1]);
397 if(id < 0)
398 throw new NumberFormatException("Bad id");
399 } catch(NumberFormatException e) {
400 throw new IOException("Bad FDB line in initialization segment");
402 hw.initFDBIndex = id;
403 } else if("CDROM".equals(components[0])) {
404 int id;
405 try {
406 id = Integer.parseInt(components[1]);
407 if(id < 0)
408 throw new NumberFormatException("Bad id");
409 } catch(NumberFormatException e) {
410 throw new IOException("Bad CDROM line in initialization segment");
412 hw.initCDROMIndex = id;
413 } else if("INITIALTIME".equals(components[0])) {
414 long id;
415 try {
416 id = Long.parseLong(components[1]);
417 if(id < 0 || id > 4102444799999L)
418 throw new NumberFormatException("Bad id");
419 } catch(NumberFormatException e) {
420 throw new IOException("Bad INITIALTIME line in initialization segment");
422 hw.initRTCTime = id;
423 } else if("CPUDIVIDER".equals(components[0])) {
424 int id;
425 try {
426 id = Integer.parseInt(components[1]);
427 if(id < 1 || id > 256)
428 throw new NumberFormatException("Bad id");
429 } catch(NumberFormatException e) {
430 throw new IOException("Bad CPUDIVIDER line in initialization segment");
432 hw.cpuDivider = id;
433 } else if("MEMORYSIZE".equals(components[0])) {
434 int id;
435 try {
436 id = Integer.parseInt(components[1]);
437 if(id < 256 || id > 262144)
438 throw new NumberFormatException("Bad id");
439 } catch(NumberFormatException e) {
440 throw new IOException("Bad MEMORYSIZE line in initialization segment");
442 hw.memoryPages = id;
443 } else if("FPU".equals(components[0])) {
444 hw.fpuEmulator = components[1];
445 } else if("BOOT".equals(components[0])) {
446 if("FLOPPY".equals(components[1]))
447 hw.bootType = DriveSet.BootType.FLOPPY;
448 else if("HDD".equals(components[1]))
449 hw.bootType = DriveSet.BootType.HARD_DRIVE;
450 else if("CDROM".equals(components[1]))
451 hw.bootType = DriveSet.BootType.CDROM;
452 else
453 throw new IOException("Bad BOOT line in initialization segment");
454 } else if("LOADMODULE".equals(components[0])) {
455 if(!hw.hwModules.containsKey(components[1]))
456 hw.hwModules.put(components[1],new LinkedHashSet<String>());
457 hw.hwModules.get(components[1]).add(null);
458 } else if("LOADMODULEA".equals(components[0])) {
459 if(!hw.hwModules.containsKey(components[1]))
460 hw.hwModules.put(components[1],new LinkedHashSet<String>());
461 hw.hwModules.get(components[1]).add(components[2]);
462 } else if("IOPORTDELAY".equals(components[0])) {
463 hw.ioportDelayed = true;
464 } else if("VGAHRETRACE".equals(components[0])) {
465 hw.vgaHretrace = true;
467 components = nextParseLine(input);
469 return hw;
474 public int sysRAMSize;
475 public int cpuClockDivider;
476 private PCHardwareInfo hwInfo;
478 public static volatile boolean compile = true;
480 private final Processor processor;
481 private final PhysicalAddressSpace physicalAddr;
482 private final LinearAddressSpace linearAddr;
483 private final Clock vmClock;
484 private final Set<HardwareComponent> parts;
485 private final CodeBlockManager manager;
486 private DiskImageSet images;
487 private final ResetButton brb;
488 private final DiskChanger diskChanger;
489 private final EventPoller poller;
491 private VGADigitalOut videoOut;
493 private TraceTrap traceTrap;
494 private boolean hitTraceTrap;
495 private boolean tripleFaulted;
496 private boolean rebootRequest;
498 private int cdromIndex;
500 private Map<String, SoundDigitalOut> soundOutputs;
502 public VGADigitalOut getVideoOutput()
504 return videoOut;
507 public HardwareComponent loadHardwareModule(String name, String params) throws IOException
509 Class<?> module;
510 if("".equals(params))
511 params = null;
513 try {
514 module = Class.forName(name);
515 } catch(Exception e) {
516 throw new IOException("Unable to find extension module \"" + name + "\".");
518 if(!HardwareComponent.class.isAssignableFrom(module)) {
519 throw new IOException("Extension module \"" + name + "\" is not valid hardware module.");
521 HardwareComponent c;
522 try {
523 boolean x = params.equals(""); //Intentionally cause NPE if params is null.
524 x = x & x; //Silence warning.
525 Constructor<?> cc = module.getConstructor(String.class);
526 c = (HardwareComponent)cc.newInstance(params);
527 } catch(NullPointerException e) {
528 try {
529 Constructor<?> cc = module.getConstructor();
530 c = (HardwareComponent)cc.newInstance();
531 } catch(InvocationTargetException f) {
532 Throwable e2 = f.getCause();
533 //If the exception is something unchecked, just pass it through.
534 if(e2 instanceof RuntimeException)
535 throw (RuntimeException)e2;
536 if(e2 instanceof Error) {
537 IOException ne = new IOException("Error while invoking constructor: " + e2);
538 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
539 throw ne;
541 //Also pass IOException through.
542 if(e2 instanceof IOException)
543 throw (IOException)e2;
544 //What the heck is that?
545 IOException ne = new IOException("Unknown exception while invoking module constructor: " + e2);
546 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
547 throw ne;
548 } catch(Exception f) {
549 throw new IOException("Unable to instantiate extension module \"" + name + "\".");
551 } catch(InvocationTargetException e) {
552 Throwable e2 = e.getCause();
553 //If the exception is something unchecked, just pass it through.
554 if(e2 instanceof RuntimeException)
555 throw (RuntimeException)e2;
556 if(e2 instanceof Error) {
557 IOException ne = new IOException("Error while invoking constructor: " + e2);
558 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
559 throw ne;
561 //Also pass IOException through.
562 if(e2 instanceof IOException)
563 throw (IOException)e2;
564 //What the heck is that?
565 IOException ne = new IOException("Unknown exception while invoking module constructor: " + e2);
566 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
567 throw ne;
568 } catch(Exception f) {
569 throw new IOException("Unable to instantiate extension module \"" + name + "\": " + f.getMessage());
572 return c;
576 * Constructs a new <code>PC</code> instance with the specified external time-source and
577 * drive set.
578 * @param drives drive set for this instance.
579 * @throws java.io.IOException propogated from bios resource loading
581 public PC(DriveSet drives, int ramPages, int clockDivide, String sysBIOSImg, String vgaBIOSImg,
582 long initTime, DiskImageSet images, Map<String, Set<String>> hwModules, String fpuClass,
583 boolean ioportDelayed, boolean vgaHretrace)
584 throws IOException
586 parts = new LinkedHashSet<HardwareComponent>();
587 soundOutputs = new LinkedHashMap<String, SoundDigitalOut>();
589 cdromIndex = -1;
590 for(int i = 0; i < 4; i++) {
591 BlockDevice dev = drives.getHardDrive(i);
592 if(dev != null && dev.getType() == BlockDevice.Type.CDROM)
593 cdromIndex = i;
596 cpuClockDivider = clockDivide;
597 sysRAMSize = ramPages * 4096;
598 vmClock = new Clock();
600 if(hwModules != null)
601 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
602 String name = e.getKey();
603 for(String params : e.getValue()) {
604 System.err.println("Informational: Loading module \"" + name + "\".");
605 parts.add(loadHardwareModule(name, params));
609 parts.add(vmClock);
610 System.err.println("Informational: Creating CPU...");
611 processor = new Processor(vmClock, cpuClockDivider);
612 parts.add(processor);
613 manager = new CodeBlockManager();
615 System.err.println("Informational: Creating FPU...");
616 try {
617 if(fpuClass != null) {
618 Object fpu = Class.forName(fpuClass).getConstructor(Processor.class).newInstance(processor);
619 processor.setFPU((FpuState)fpu);
621 } catch(ClassCastException e) {
622 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
623 } catch(InvocationTargetException e) {
624 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
625 } catch(NoSuchMethodException e) {
626 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
627 } catch(IllegalAccessException e) {
628 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
629 } catch(ClassNotFoundException e) {
630 throw new IOException("No such class: " + fpuClass + ".");
631 } catch(InstantiationException e) {
632 throw new IOException("Can't instantiate FPU emulator: " + e.getMessage() + ".");
635 System.err.println("Informational: Creating Reset Button...");
636 brb = new ResetButton(this);
637 parts.add(brb);
639 System.err.println("Informational: Creating Disk Changer..");
640 diskChanger = new DiskChanger(this);
641 parts.add(diskChanger);
643 System.err.println("Informational: Creating physical address space...");
644 physicalAddr = new PhysicalAddressSpace(manager, sysRAMSize);
645 parts.add(physicalAddr);
647 System.err.println("Informational: Creating linear address space...");
648 linearAddr = new LinearAddressSpace();
649 parts.add(linearAddr);
651 parts.add(drives);
653 //Motherboard
654 System.err.println("Informational: Creating I/O port handler...");
655 parts.add(new IOPortHandler(ioportDelayed));
656 System.err.println("Informational: Creating IRQ controller...");
657 parts.add(new InterruptController());
659 System.err.println("Informational: Creating primary DMA controller...");
660 parts.add(new DMAController(false, true));
661 System.err.println("Informational: Creating secondary DMA controller...");
662 parts.add(new DMAController(false, false));
664 System.err.println("Informational: Creating real time clock...");
665 parts.add(new RTC(0x70, 8, sysRAMSize, initTime));
666 System.err.println("Informational: Creating interval timer...");
667 parts.add(new IntervalTimer(0x40, 0));
668 System.err.println("Informational: Creating A20 Handler...");
669 parts.add(new GateA20Handler());
670 this.images = images;
672 //Peripherals
673 System.err.println("Informational: Creating IDE interface...");
674 parts.add(new PIIX3IDEInterface());
676 System.err.println("Informational: Creating Keyboard...");
677 parts.add(new Keyboard());
678 System.err.println("Informational: Creating floppy disk controller...");
679 parts.add(new FloppyController());
680 System.err.println("Informational: Creating PC speaker...");
681 soundOutputs.put("org.jpc.emulator.peripheral.PCSpeaker-0", new SoundDigitalOut(vmClock));
682 parts.add(new PCSpeaker(soundOutputs.get("org.jpc.emulator.peripheral.PCSpeaker-0")));
684 //PCI Stuff
685 System.err.println("Informational: Creating PCI Host Bridge...");
686 parts.add(new PCIHostBridge());
687 System.err.println("Informational: Creating PCI-to-ISA Bridge...");
688 parts.add(new PCIISABridge());
689 System.err.println("Informational: Creating PCI Bus...");
690 parts.add(new PCIBus());
692 //BIOSes
693 System.err.println("Informational: Creating system BIOS...");
694 parts.add(new SystemBIOS(sysBIOSImg));
695 System.err.println("Informational: Creating VGA BIOS...");
696 parts.add(new VGABIOS(vgaBIOSImg));
697 System.err.println("Informational: Creating trace trap...");
698 parts.add(traceTrap = new TraceTrap());
700 System.err.println("Informational: Creating event poller...");
701 poller = new EventPoller(vmClock);
703 System.err.println("Informational: Creating hardware info...");
704 hwInfo = new PCHardwareInfo();
706 DisplayController displayController = null;
707 for(HardwareComponent c : parts)
708 if(c instanceof DisplayController)
709 if(displayController == null)
710 displayController = (DisplayController)c;
711 else
712 throw new IOException("Can not have multiple display controllers: \"" +
713 c.getClass().getName() + "\" and \"" + displayController.getClass().getName() +
714 "\" are both display controllers.");
715 if(displayController == null)
717 System.err.println("Informational: Creating VGA card...");
718 VGACard card = new VGACard();
719 if(vgaHretrace)
720 card.enableVGAHretrace();
721 parts.add(card);
722 displayController = card;
724 videoOut = displayController.getOutputDevice();
726 System.err.println("Informational: Creating sound outputs...");
727 Map<String, Integer> numBase = new HashMap<String, Integer>();
728 for(HardwareComponent c : parts) {
729 if(!(c instanceof SoundOutputDevice))
730 continue;
731 SoundOutputDevice c2 = (SoundOutputDevice)c;
732 int channels = c2.requestedSoundChannels();
733 int base = 0;
734 if(numBase.containsKey(c.getClass().getName()))
735 base = numBase.get(c.getClass().getName()).intValue();
736 for(int i = 0; i < channels; i++) {
737 String outname = c.getClass().getName() + "-" + (base + i);
738 SoundDigitalOut sdo = new SoundDigitalOut(vmClock);
739 soundOutputs.put(outname, sdo);
740 c2.soundChannelCallback(sdo);
742 numBase.put(c.getClass().getName(), base + channels);
745 System.err.println("Informational: Configuring components...");
746 if (!configure()) {
747 throw new IllegalStateException("Can't initialize components (cyclic dependency?)");
749 System.err.println("Informational: PC initialization done.");
752 public SoundDigitalOut getSoundOut(String name)
754 return soundOutputs.get(name);
757 public int getCDROMIndex()
759 return cdromIndex;
762 public void dumpStatusPartial(StatusDumper output)
764 output.println("\tsysRAMSize " + sysRAMSize + " cpuClockDivider " + cpuClockDivider);
765 output.println("\ttripleFaulted " + tripleFaulted + " cdromIndex " + cdromIndex);
766 //hitTraceTrap not printed here.
767 output.println("\tprocessor <object #" + output.objectNumber(processor) + ">"); if(processor != null) processor.dumpStatus(output);
768 output.println("\tphysicalAddr <object #" + output.objectNumber(physicalAddr) + ">"); if(physicalAddr != null) physicalAddr.dumpStatus(output);
769 output.println("\tlinearAddr <object #" + output.objectNumber(linearAddr) + ">"); if(linearAddr != null) linearAddr.dumpStatus(output);
770 output.println("\tvmClock <object #" + output.objectNumber(vmClock) + ">"); if(vmClock != null) vmClock.dumpStatus(output);
771 output.println("\timages <object #" + output.objectNumber(images) + ">"); if(images != null) images.dumpStatus(output);
772 output.println("\ttraceTrap <object #" + output.objectNumber(traceTrap) + ">"); if(traceTrap != null) traceTrap.dumpStatus(output);
773 output.println("\thwInfo <object #" + output.objectNumber(hwInfo) + ">"); if(hwInfo != null) hwInfo.dumpStatus(output);
774 output.println("\thvideoOut <object #" + output.objectNumber(videoOut) + ">"); if(videoOut != null) videoOut.dumpStatus(output);
775 output.println("\tbrb <object #" + output.objectNumber(brb) + ">"); if(brb != null) brb.dumpStatus(output);
776 output.println("\tpoller <object #" + output.objectNumber(poller) + ">"); if(poller != null) poller.dumpStatus(output);
778 int i = 0;
779 for (HardwareComponent part : parts) {
780 output.println("\tparts[" + i + "] <object #" + output.objectNumber(part) + ">"); if(part != null) part.dumpStatus(output);
781 i++;
784 for (Map.Entry<String, SoundDigitalOut> c : soundOutputs.entrySet()) {
785 output.println("\tsoundOutputs[" + c.getKey() + "] <object #" + output.objectNumber(c.getValue()) + ">"); if(c.getValue() != null) c.getValue().dumpStatus(output);
789 public PC(SRLoader input) throws IOException
791 input.objectCreated(this);
792 cdromIndex = input.loadInt();
793 sysRAMSize = input.loadInt();
794 cpuClockDivider = input.loadInt();
795 processor = (Processor)input.loadObject();
796 physicalAddr = (PhysicalAddressSpace)input.loadObject();
797 linearAddr = (LinearAddressSpace)input.loadObject();
798 vmClock = (Clock)input.loadObject();
799 images = (DiskImageSet)(input.loadObject());
800 traceTrap = (TraceTrap)input.loadObject();
801 manager = (CodeBlockManager)input.loadObject();
802 hwInfo = (PCHardwareInfo)(input.loadObject());
803 videoOut = (VGADigitalOut)(input.loadObject());
804 hitTraceTrap = input.loadBoolean();
805 tripleFaulted = input.loadBoolean();
807 boolean present = input.loadBoolean();
808 parts = new LinkedHashSet<HardwareComponent>();
809 while(present) {
810 parts.add((HardwareComponent)input.loadObject());
811 present = input.loadBoolean();
813 rebootRequest = input.loadBoolean();
814 brb = (ResetButton)input.loadObject();
815 diskChanger = (DiskChanger)input.loadObject();
817 soundOutputs = new LinkedHashMap<String, SoundDigitalOut>();
818 present = input.loadBoolean();
819 while(present) {
820 String name = input.loadString();
821 soundOutputs.put(name, (SoundDigitalOut)input.loadObject());
822 present = input.loadBoolean();
824 poller = (EventPoller)(input.loadObject());
827 public void dumpStatus(StatusDumper output)
829 if(output.dumped(this))
830 return;
832 output.println("#" + output.objectNumber(this) + ": PC:");
833 dumpStatusPartial(output);
834 output.endObject();
837 public PCHardwareInfo getHardwareInfo()
839 return hwInfo;
842 public boolean getAndClearTripleFaulted()
844 boolean flag = tripleFaulted;
845 tripleFaulted = false;
846 return flag;
850 public void dumpSRPartial(SRDumper output) throws IOException
852 output.dumpInt(cdromIndex);
853 output.dumpInt(sysRAMSize);
854 output.dumpInt(cpuClockDivider);
855 output.dumpObject(processor);
856 output.dumpObject(physicalAddr);
857 output.dumpObject(linearAddr);
858 output.dumpObject(vmClock);
859 output.dumpObject(images);
860 output.dumpObject(traceTrap);
861 output.dumpObject(manager);
862 output.dumpObject(hwInfo);
863 output.dumpObject(videoOut);
864 output.dumpBoolean(hitTraceTrap);
865 output.dumpBoolean(tripleFaulted);
866 for (HardwareComponent part : parts) {
867 output.dumpBoolean(true);
868 output.dumpObject(part);
870 output.dumpBoolean(false);
871 output.dumpBoolean(rebootRequest);
872 output.dumpObject(brb);
873 output.dumpObject(diskChanger);
875 for (Map.Entry<String, SoundDigitalOut> c : soundOutputs.entrySet()) {
876 output.dumpBoolean(true);
877 output.dumpString(c.getKey());
878 output.dumpObject(c.getValue());
880 output.dumpBoolean(false);
881 output.dumpObject(poller);
884 public static Map<String, Set<String>> parseHWModules(String moduleString) throws IOException
886 Map<String, Set<String>> ret = new LinkedHashMap<String, Set<String>>();
888 while(moduleString != null && !moduleString.equals("")) {
889 String currentModule;
890 int parenDepth = 0;
891 int nameEnd = -1;
892 int paramsStart = -1;
893 int paramsEnd = -1;
894 int stringLen = moduleString.length();
895 boolean requireNextSep = false;
897 for(int i = 0; true; i++) {
898 int cp;
899 if(i < stringLen)
900 cp = moduleString.codePointAt(i);
901 else if(parenDepth == 0)
902 cp = ','; //Hack, consider last character seperator.
903 else
904 throw new IOException("Error in module string: unclosed '('.");
905 if(cp >= 0x10000)
906 i++; //Skip the next surrogate.
907 if((cp >= 0xD800 && cp < 0xE000) || ((cp & 0xFFFE) == 0xFFFE) || (cp >>> 16) > 16 || cp < 0)
908 throw new IOException("Error In module string: invalid Unicode character.");
909 if(requireNextSep && cp != ',')
910 throw new IOException("Error in module string: Expected ',' after ')' closing parameter list.");
911 else if(cp == ',' && i == 0)
912 throw new IOException("Error in module string: Blank module name not allowed.");
913 else if(cp == '(') {
914 if(parenDepth == 0) {
915 paramsStart = i + 1;
916 nameEnd = i - 1;
918 parenDepth++;
919 } else if(cp == ')') {
920 if(parenDepth == 0)
921 throw new IOException("Error in module string: Unpaired ')'.");
922 else if(parenDepth == 1) {
923 paramsEnd = i - 1;
924 requireNextSep = true;
926 parenDepth--;
927 } else if(cp == ',' && parenDepth == 0) {
928 if(nameEnd < 0)
929 nameEnd = i - 1;
930 currentModule = moduleString.substring(0, i);
931 if(i < stringLen ) {
932 moduleString = moduleString.substring(i + 1);
933 if(moduleString.equals(""))
934 throw new IOException("Error in module string: Blank module name not allowed.");
935 } else
936 moduleString = "";
937 break;
941 String name = currentModule.substring(0, nameEnd + 1);
942 String params = null;
943 if(paramsStart >= 0)
944 params = currentModule.substring(paramsStart, paramsEnd + 1);
946 if(ret.containsKey(name))
947 ret.get(name).add(params);
948 else {
949 Set<String> foo = new LinkedHashSet<String>();
950 foo.add(params);
951 ret.put(name, foo);
955 return ret;
959 private static GenericBlockDevice blockdeviceFor(String name) throws IOException
961 if(name == null)
962 return null;
963 return new GenericBlockDevice(new DiskImage(name, false));
966 public static PC createPC(PCHardwareInfo hw) throws IOException
968 PC pc;
969 String biosID = arrayToString(hw.biosID);
970 String vgaBIOSID = arrayToString(hw.vgaBIOSID);
971 BlockDevice hda = blockdeviceFor(arrayToString(hw.hdaID));
972 BlockDevice hdb = blockdeviceFor(arrayToString(hw.hdbID));
973 BlockDevice hdc = blockdeviceFor(arrayToString(hw.hdcID));
974 BlockDevice hdd = blockdeviceFor(arrayToString(hw.hddID));
975 if(hdc == null) {
976 hdc = new GenericBlockDevice(BlockDevice.Type.CDROM);
979 DriveSet drives = new DriveSet(hw.bootType, hda, hdb, hdc, hdd);
980 pc = new PC(drives, hw.memoryPages, hw.cpuDivider, biosID, vgaBIOSID, hw.initRTCTime, hw.images,
981 hw.hwModules, hw.fpuEmulator, hw.ioportDelayed, hw.vgaHretrace);
982 FloppyController fdc = (FloppyController)pc.getComponent(FloppyController.class);
984 DiskImage img1 = pc.getDisks().lookupDisk(hw.initFDAIndex);
985 fdc.changeDisk(img1, 0);
987 DiskImage img2 = pc.getDisks().lookupDisk(hw.initFDBIndex);
988 fdc.changeDisk(img2, 1);
990 if(hdc.getType() == BlockDevice.Type.CDROM) {
991 DiskImage img3 = pc.getDisks().lookupDisk(hw.initCDROMIndex);
992 ((GenericBlockDevice)hdc).configure(img3);
995 PCHardwareInfo hw2 = pc.getHardwareInfo();
996 hw2.biosID = hw.biosID;
997 hw2.vgaBIOSID = hw.vgaBIOSID;
998 hw2.hdaID = hw.hdaID;
999 hw2.hdbID = hw.hdbID;
1000 hw2.hdcID = hw.hdcID;
1001 hw2.hddID = hw.hddID;
1002 hw2.images = hw.images;
1003 hw2.initFDAIndex = hw.initFDAIndex;
1004 hw2.initFDBIndex = hw.initFDBIndex;
1005 hw2.initCDROMIndex = hw.initCDROMIndex;
1006 hw2.initRTCTime = hw.initRTCTime;
1007 hw2.cpuDivider = hw.cpuDivider;
1008 hw2.memoryPages = hw.memoryPages;
1009 hw2.bootType = hw.bootType;
1010 hw2.hwModules = hw.hwModules;
1011 hw2.fpuEmulator = hw.fpuEmulator;
1012 hw2.ioportDelayed = hw.ioportDelayed;
1013 hw2.vgaHretrace = hw.vgaHretrace;
1014 return pc;
1018 * Starts this PC's attached clock instance.
1020 public void start()
1022 vmClock.resume();
1026 * Stops this PC's attached clock instance
1028 public void stop()
1030 vmClock.pause();
1034 * Inserts the specified floppy disk into the drive identified.
1035 * @param disk new floppy disk to be inserted.
1036 * @param index drive which the disk is inserted into.
1038 private void changeFloppyDisk(DiskImage disk, int index) throws IOException
1040 ((FloppyController)getComponent(FloppyController.class)).changeDisk(disk, index);
1043 public void changeFloppyDisk(int driveIndex, int diskIndex) throws IOException
1045 diskChanger.changeFloppyDisk(driveIndex, diskIndex);
1048 public void wpFloppyDisk(int diskIndex, boolean turnOn) throws IOException
1050 diskChanger.wpFloppyDisk(diskIndex, turnOn);
1053 public static class DiskChanger extends AbstractHardwareComponent implements SRDumpable, EventDispatchTarget
1055 private EventRecorder eRecorder; //Not saved.
1056 private PC upperBackref;
1057 private int currentDriveA; //Not saved.
1058 private int currentDriveB; //Not saved.
1059 private int currentCDROM; //Not saved.
1060 private Set<Integer> usedDisks; //Not saved.
1062 private void checkFloppyChange(int driveIndex, int diskIndex) throws IOException
1064 if(driveIndex == 2 && upperBackref.cdromIndex < 0)
1065 throw new IOException("No CD-ROM drive available");
1066 if(diskIndex < -1)
1067 throw new IOException("Illegal disk number");
1068 DiskImage disk = upperBackref.images.lookupDisk(diskIndex);
1069 if(driveIndex < 0 || driveIndex > 2)
1070 throw new IOException("Illegal drive number");
1071 if(diskIndex >= 0 && (diskIndex == currentDriveA || diskIndex == currentDriveB ||
1072 diskIndex == currentCDROM))
1073 throw new IOException("Specified disk is already in some drive");
1074 if(diskIndex < 0 && driveIndex == 0 && currentDriveA < 0)
1075 throw new IOException("No disk present in drive A");
1076 if(diskIndex < 0 && driveIndex == 1 && currentDriveB < 0)
1077 throw new IOException("No disk present in drive B");
1078 if(diskIndex < 0 && driveIndex == 2 && currentCDROM < 0)
1079 throw new IOException("No disk present in CD-ROM Drive");
1080 if(diskIndex > 0 && driveIndex < 2 && (disk == null || disk.getType() != BlockDevice.Type.FLOPPY))
1081 throw new IOException("Attempt to put non-floppy into drive A or B");
1082 if(diskIndex > 0 && driveIndex == 2 && (disk == null || disk.getType() != BlockDevice.Type.CDROM))
1083 throw new IOException("Attempt to put non-CDROM into CDROM drive");
1084 if(diskIndex > 0)
1085 usedDisks.add(new Integer(diskIndex));
1088 private void checkFloppyWP(int diskIndex, boolean turnOn) throws IOException
1090 if(diskIndex < 0)
1091 throw new IOException("Illegal floppy disk number");
1092 if(diskIndex == currentDriveA || diskIndex == currentDriveB)
1093 throw new IOException("Can not manipulate WP of disk in drive");
1094 DiskImage disk = upperBackref.images.lookupDisk(diskIndex);
1095 if(disk == null || disk.getType() != BlockDevice.Type.FLOPPY)
1096 throw new IOException("Can not manipulate WP of non-floppy disk");
1099 public synchronized void changeFloppyDisk(int driveIndex, int diskIndex) throws IOException
1101 checkFloppyChange(driveIndex, diskIndex);
1102 upperBackref.images.lookupDisk(diskIndex);
1103 try {
1104 if(driveIndex == 0)
1105 eRecorder.addEvent(-1, getClass(), new String[]{"FDA", "" + diskIndex});
1106 else if(driveIndex == 1)
1107 eRecorder.addEvent(-1, getClass(), new String[]{"FDB", "" + diskIndex});
1108 else if(driveIndex == 2)
1109 eRecorder.addEvent(-1, getClass(), new String[]{"CDROM", "" + diskIndex});
1110 } catch(Exception e) {}
1113 public synchronized void wpFloppyDisk(int diskIndex, boolean turnOn) throws IOException
1115 checkFloppyWP(diskIndex, turnOn);
1116 DiskImage disk = upperBackref.images.lookupDisk(diskIndex);
1117 try {
1118 if(turnOn && !disk.isReadOnly())
1119 eRecorder.addEvent(-1, getClass(), new String[]{"WRITEPROTECT", "" + diskIndex});
1120 else if(!turnOn && disk.isReadOnly())
1121 eRecorder.addEvent(-1, getClass(), new String[]{"WRITEUNPROTECT", "" + diskIndex});
1122 } catch(Exception e) {}
1125 public void doEvent(long timeStamp, String[] args, int level) throws IOException
1127 if(args == null || args.length != 2)
1128 throw new IOException("Invalid disk event parameters");
1129 int disk;
1130 try {
1131 disk = Integer.parseInt(args[1]);
1132 } catch(Exception e) {
1133 throw new IOException("Invalid disk number");
1135 DiskImage diskImg = upperBackref.images.lookupDisk(disk);
1137 if("FDA".equals(args[0])) {
1138 if(level <= EventRecorder.EVENT_STATE_EFFECT) {
1139 checkFloppyChange(0, disk);
1140 currentDriveA = disk;
1142 if(level == EventRecorder.EVENT_EXECUTE)
1143 upperBackref.changeFloppyDisk(diskImg, 0);
1144 } else if("FDB".equals(args[0])) {
1145 if(level <= EventRecorder.EVENT_STATE_EFFECT) {
1146 checkFloppyChange(1, disk);
1147 currentDriveB = disk;
1149 if(level == EventRecorder.EVENT_EXECUTE)
1150 upperBackref.changeFloppyDisk(diskImg, 1);
1151 } else if("CDROM".equals(args[0])) {
1152 if(level <= EventRecorder.EVENT_STATE_EFFECT) {
1153 checkFloppyChange(2, disk);
1154 currentCDROM = disk;
1156 DriveSet drives = (DriveSet)upperBackref.getComponent(DriveSet.class);
1157 if(level == EventRecorder.EVENT_EXECUTE)
1158 try {
1159 ((GenericBlockDevice)drives.getHardDrive(upperBackref.cdromIndex)).configure(diskImg);
1160 } catch(Exception e) {
1161 System.err.println("Warning: Unable to change disk in CD-ROM drive");
1163 } else if("WRITEPROTECT".equals(args[0])) {
1164 if(level <= EventRecorder.EVENT_STATE_EFFECT)
1165 checkFloppyWP(disk, true);
1166 if(level == EventRecorder.EVENT_EXECUTE)
1167 diskImg.setWP(true);
1168 } else if("WRITEUNPROTECT".equals(args[0])) {
1169 if(level <= EventRecorder.EVENT_STATE_EFFECT)
1170 checkFloppyWP(disk, false);
1171 if(level == EventRecorder.EVENT_EXECUTE)
1172 diskImg.setWP(false);
1173 } else
1174 throw new IOException("Invalid disk event type");
1177 public void startEventCheck()
1179 currentDriveA = upperBackref.hwInfo.initFDAIndex;
1180 currentDriveB = upperBackref.hwInfo.initFDBIndex;
1181 currentCDROM = upperBackref.hwInfo.initCDROMIndex;
1182 usedDisks = new HashSet<Integer>();
1183 if(currentDriveA >= 0)
1184 usedDisks.add(currentDriveA);
1185 if(currentDriveB >= 0)
1186 usedDisks.add(currentDriveB);
1187 if(currentCDROM >= 0)
1188 usedDisks.add(currentCDROM);
1191 private Set<Integer> usedDiskSet()
1193 return usedDisks;
1196 public void endEventCheck() throws IOException
1198 //Nothing to do.
1201 public DiskChanger(PC pc)
1203 upperBackref = pc;
1206 public DiskChanger(SRLoader input) throws IOException
1208 super(input);
1209 upperBackref = (PC)input.loadObject();
1212 public void dumpSRPartial(SRDumper output) throws IOException
1214 super.dumpSRPartial(output);
1215 output.dumpObject(upperBackref);
1218 public long getEventTimeLowBound(long stamp, String[] args) throws IOException
1220 return -1; //No timing constraints.
1223 public void setEventRecorder(EventRecorder recorder)
1225 eRecorder = recorder;
1228 public void dumpStatus(StatusDumper output)
1230 if(output.dumped(this))
1231 return;
1233 output.println("#" + output.objectNumber(this) + ": DiskChanger:");
1234 output.endObject();
1239 public DiskImageSet getDisks()
1241 return images;
1244 private boolean configure() {
1245 boolean fullyInitialised;
1246 int count = 0;
1247 do {
1248 fullyInitialised = true;
1249 for (HardwareComponent outer : parts) {
1250 if (outer.initialised()) {
1251 continue;
1254 for (HardwareComponent inner : parts) {
1255 outer.acceptComponent(inner);
1258 fullyInitialised &= outer.initialised();
1260 count++;
1261 } while ((fullyInitialised == false) && (count < 100));
1263 if (!fullyInitialised) {
1264 for(HardwareComponent hwc : parts) {
1265 if(!hwc.initialised()) {
1266 System.err.println("Error: Component of type " + hwc.getClass() + " failed to initialize.");
1269 System.err.println("Critical error: PC component initialization failed.");
1270 return false;
1273 for (HardwareComponent hwc : parts) {
1274 if (hwc instanceof PCIBus) {
1275 ((PCIBus) hwc).biosInit();
1279 return true;
1282 public void setFPUHack()
1284 physicalAddr.setFPUHack();
1287 public void setVGADrawHack()
1289 HardwareComponent displayController = getComponent(VGACard.class);
1290 if(displayController != null)
1291 ((VGACard)displayController).setVGADrawHack();
1295 * Reset this PC back to its initial state.
1296 * <p>
1297 * This is roughly equivalent to a hard-reset (power down-up cycle).
1299 protected void reset() {
1300 for (HardwareComponent hwc : parts) {
1301 hwc.reset();
1303 configure();
1306 public void reboot()
1308 brb.reboot();
1311 public static class ResetButton extends AbstractHardwareComponent implements SRDumpable, EventDispatchTarget
1313 private EventRecorder eRecorder; //Not saved.
1314 private PC upperBackref;
1316 public EventRecorder getRecorder()
1318 return eRecorder;
1321 public void reboot()
1323 try {
1324 eRecorder.addEvent(-1, getClass(), null);
1325 } catch(Exception e) {}
1328 public void startEventCheck()
1330 //No state.
1333 public void doEvent(long timeStamp, String[] args, int level) throws IOException
1335 if(args != null)
1336 throw new IOException("Invalid reboot event");
1337 if(level == EventRecorder.EVENT_EXECUTE) {
1338 upperBackref.processor.eflagsMachineHalt = true;
1339 upperBackref.rebootRequest = true;
1343 public void endEventCheck() throws IOException
1347 public ResetButton(PC pc)
1349 upperBackref = pc;
1352 public ResetButton(SRLoader input) throws IOException
1354 super(input);
1355 upperBackref = (PC)input.loadObject();
1358 public void dumpSRPartial(SRDumper output) throws IOException
1360 super.dumpSRPartial(output);
1361 output.dumpObject(upperBackref);
1364 public long getEventTimeLowBound(long stamp, String[] args) throws IOException
1366 return -1; //No timing constraints.
1369 public void setEventRecorder(EventRecorder recorder)
1371 eRecorder = recorder;
1374 public void dumpStatus(StatusDumper output)
1376 if(output.dumped(this))
1377 return;
1379 output.println("#" + output.objectNumber(this) + ": ResetButton:");
1380 output.endObject();
1385 * Get an subclass of <code>cls</code> from this instance's parts list.
1386 * <p>
1387 * If <code>cls</code> is not assignment compatible with <code>HardwareComponent</code>
1388 * then this method will return null immediately.
1389 * @param cls component type required.
1390 * @return an instance of class <code>cls</code>, or <code>null</code> on failure
1392 public HardwareComponent getComponent(Class<? extends HardwareComponent> cls) {
1393 if (!HardwareComponent.class.isAssignableFrom(cls)) {
1394 return null;
1397 for (HardwareComponent hwc : parts) {
1398 if (cls.isInstance(hwc)) {
1399 return hwc;
1402 return null;
1405 public Set<HardwareComponent> allComponents()
1407 return parts;
1411 * Gets the processor instance associated with this PC.
1412 * @return associated processor instance.
1414 public Processor getProcessor() {
1415 return processor;
1419 * Execute an arbitrarily large amount of code on this instance.
1420 * <p>
1421 * This method will execute continuously until there is either a mode switch,
1422 * or a unspecified large number of instructions have completed. It should
1423 * never run indefinitely.
1424 * @return total number of x86 instructions executed.
1426 public final int execute() {
1428 if (processor.isProtectedMode()) {
1429 if (processor.isVirtual8086Mode()) {
1430 return executeVirtual8086();
1431 } else {
1432 return executeProtected();
1434 } else {
1435 return executeReal();
1439 public final int executeReal()
1441 int x86Count = 0;
1443 if(rebootRequest) {
1444 reset();
1445 rebootRequest = false;
1450 for (int i = 0; i < 100; i++)
1452 int block;
1453 try {
1454 block = physicalAddr.executeReal(processor, processor.getInstructionPointer());
1455 } catch(org.jpc.emulator.processor.Processor.TripleFault e) {
1456 reset(); //Reboot the system to get the CPU back online.
1457 hitTraceTrap = true;
1458 tripleFaulted = true;
1459 break;
1461 x86Count += block;
1462 processor.instructionsExecuted += block;
1463 //Don't call this on aborted blocks. Doing so is probably good source of desyncs.
1464 if(!processor.eflagsLastAborted)
1465 processor.processRealModeInterrupts(1);
1466 if(traceTrap.getAndClearTrapActive()) {
1467 hitTraceTrap = true;
1468 break;
1470 if(rebootRequest) {
1471 reset();
1472 rebootRequest = false;
1473 break;
1476 } catch (ProcessorException p) {
1477 processor.handleRealModeException(p);
1479 catch (ModeSwitchException e)
1481 //System.err.println("Informational: CPU switching modes: " + e.toString());
1483 return x86Count;
1486 public TraceTrap getTraceTrap()
1488 return traceTrap;
1491 public boolean getHitTraceTrap()
1493 boolean tmp = hitTraceTrap;
1494 hitTraceTrap = false;
1495 return tmp;
1498 public final int executeProtected() {
1499 int x86Count = 0;
1501 if(rebootRequest) {
1502 reset();
1503 rebootRequest = false;
1508 for (int i = 0; i < 100; i++)
1510 int block;
1511 try {
1512 block= linearAddr.executeProtected(processor, processor.getInstructionPointer());
1513 } catch(org.jpc.emulator.processor.Processor.TripleFault e) {
1514 reset(); //Reboot the system to get the CPU back online.
1515 hitTraceTrap = true;
1516 tripleFaulted = true;
1517 break;
1519 x86Count += block;
1520 processor.instructionsExecuted += block;
1521 //Don't call this on aborted blocks. Doing so is probably good source of desyncs.
1522 if(!processor.eflagsLastAborted)
1523 processor.processProtectedModeInterrupts(1);
1524 if(traceTrap.getAndClearTrapActive()) {
1525 hitTraceTrap = true;
1526 break;
1528 if(rebootRequest) {
1529 reset();
1530 rebootRequest = false;
1531 break;
1534 } catch (ProcessorException p) {
1535 processor.handleProtectedModeException(p);
1537 catch (ModeSwitchException e)
1539 //System.err.println("Informational: CPU switching modes: " + e.toString());
1541 return x86Count;
1544 public final int executeVirtual8086() {
1545 int x86Count = 0;
1547 if(rebootRequest) {
1548 reset();
1549 rebootRequest = false;
1554 for (int i = 0; i < 100; i++)
1556 int block;
1557 try {
1558 block = linearAddr.executeVirtual8086(processor, processor.getInstructionPointer());
1559 } catch(org.jpc.emulator.processor.Processor.TripleFault e) {
1560 reset(); //Reboot the system to get the CPU back online.
1561 hitTraceTrap = true;
1562 tripleFaulted = true;
1563 break;
1565 x86Count += block;
1566 processor.instructionsExecuted += block;
1567 //Don't call this on aborted blocks. Doing so is probably good source of desyncs.
1568 if(!processor.eflagsLastAborted)
1569 processor.processVirtual8086ModeInterrupts(1);
1570 if(traceTrap.getAndClearTrapActive()) {
1571 hitTraceTrap = true;
1572 break;
1574 if(rebootRequest) {
1575 reset();
1576 rebootRequest = false;
1577 break;
1581 catch (ProcessorException p)
1583 processor.handleVirtual8086ModeException(p);
1585 catch (ModeSwitchException e)
1587 //System.err.println("Informational: CPU switching modes: " + e.toString());
1589 return x86Count;
1592 public static class PCFullStatus
1594 public PC pc; //Loaded SAVED.
1595 public EventRecorder events; //Loaded SAVED.
1596 public String projectID; //Loaded SAVED.
1597 public String savestateID; //Loaded SAVED.
1598 public long rerecords; //Loaded SAVED.
1599 public String[][] extraHeaders; //Loaded SAVED.
1602 private static void saveDiskInfo(UTFOutputLineStream lines, byte[] diskID)
1604 ImageLibrary lib = DiskImage.getLibrary();
1605 String fileName = lib.lookupFileName(diskID);
1606 if(fileName == null) {
1607 System.err.println("Warning: Can't find used disk from library (SHOULD NOT HAPPEN!).");
1608 return;
1610 try {
1611 ImageMaker.ParsedImage pimg = new ImageMaker.ParsedImage(fileName);
1612 RandomAccessFile image = new RandomAccessFile(fileName, "r");
1613 switch(pimg.typeCode) {
1614 case 0:
1615 lines.encodeLine("TYPE", "FLOPPY");
1616 break;
1617 case 1:
1618 lines.encodeLine("TYPE", "HDD");
1619 break;
1620 case 2:
1621 lines.encodeLine("TYPE", "CDROM");
1622 break;
1623 case 3:
1624 lines.encodeLine("TYPE", "BIOS");
1625 break;
1626 default:
1627 lines.encodeLine("TYPE", "UNKNOWN");
1628 break;
1630 lines.encodeLine("ID", new ImageLibrary.ByteArray(pimg.diskID));
1631 switch(pimg.typeCode) {
1632 case 0:
1633 case 1: //Floppies/HDD have the same fields.
1634 lines.encodeLine("TRACKS", pimg.tracks);
1635 lines.encodeLine("SIDES", pimg.sides);
1636 lines.encodeLine("SECTORS", pimg.sectors);
1637 case 2: //Floppies/HDD have these fields as well.
1638 lines.encodeLine("TOTALSECTORS", pimg.totalSectors);
1639 byte[] sector = new byte[512];
1640 byte[] zero = new byte[512];
1641 MessageDigest md = MessageDigest.getInstance("MD5");
1642 for(int i = 0; i < pimg.totalSectors; i++) {
1643 if(i < pimg.sectorOffsetMap.length && pimg.sectorOffsetMap[i] > 0) {
1644 image.seek(pimg.sectorOffsetMap[i]);
1645 if(image.read(sector) < 512) {
1646 throw new IOException("Failed to read sector from image file.");
1648 md.update(sector);
1649 } else
1650 md.update(zero);
1652 lines.encodeLine("IMAGEMD5", new ImageLibrary.ByteArray(md.digest()));
1653 break;
1654 case 3: //BIOS
1655 lines.encodeLine("IMAGELENGTH", pimg.rawImage.length);
1656 md = MessageDigest.getInstance("MD5");
1657 md.update(pimg.rawImage);
1658 lines.encodeLine("IMAGEMD5", new ImageLibrary.ByteArray(md.digest()));
1661 List<String> comments = pimg.comments;
1662 if(comments != null) {
1663 for(String x : comments)
1664 lines.encodeLine("COMMENT", x);
1666 } catch(Exception e) {
1667 System.err.println("Warning: Can't lookup disk information: " + e.getMessage() + "[" + e.getClass().getName() + "].");
1671 private static void saveDiskInfo(JRSRArchiveWriter writer, DiskImage image, Set<ImageLibrary.ByteArray> saved) throws IOException
1673 if(image == null)
1674 return;
1675 saveDiskInfo(writer, image.getImageID(), saved);
1678 private static void saveDiskInfo(JRSRArchiveWriter writer, byte[] diskID, Set<ImageLibrary.ByteArray> saved) throws IOException
1680 if(diskID == null)
1681 return;
1682 ImageLibrary.ByteArray id = new ImageLibrary.ByteArray(diskID);
1683 if(saved.contains(id))
1684 return;
1685 saved.add(id);
1686 UTFOutputLineStream lines = new UTFOutputLineStream(writer.addMember("diskinfo-" + arrayToString(diskID)));
1687 saveDiskInfo(lines, diskID);
1688 lines.close();
1691 public static void saveSavestate(JRSRArchiveWriter writer, PCFullStatus fullStatus, boolean movie)
1692 throws IOException
1694 fullStatus.savestateID = randomHexes(24);
1695 fullStatus.events.markSave(fullStatus.savestateID, fullStatus.rerecords);
1697 UTFOutputLineStream lines = new UTFOutputLineStream(writer.addMember("header"));
1698 lines.writeLine("PROJECTID " + fullStatus.projectID);
1699 if(!movie)
1700 lines.writeLine("SAVESTATEID " + fullStatus.savestateID);
1701 lines.writeLine("RERECORDS " + fullStatus.rerecords);
1702 if(fullStatus.extraHeaders != null)
1703 for(int i = 0; i < fullStatus.extraHeaders.length; i++) {
1704 Object[] arr = new Object[fullStatus.extraHeaders[i].length];
1705 System.arraycopy(fullStatus.extraHeaders[i], 0, arr, 0, arr.length);
1706 lines.encodeLine(arr);
1708 lines.close();
1710 lines = new UTFOutputLineStream(writer.addMember("initialization"));
1711 fullStatus.pc.getHardwareInfo().makeHWInfoSegment(lines, fullStatus.pc.diskChanger);
1712 lines.close();
1714 if(!movie) {
1715 FourToFiveEncoder entry = new FourToFiveEncoder(writer.addMember("savestate"));
1716 DeflaterOutputStream dos;
1717 OutputStream zip = dos = new DeflaterOutputStream(entry);
1718 SRDumper dumper = new SRDumper(zip);
1719 dumper.dumpObject(fullStatus.pc);
1720 dumper.flush();
1721 dos.close();
1723 OutputStream entry2 = writer.addMember("manifest");
1724 dumper.writeConstructorManifest(entry2);
1725 entry2.close();
1727 lines = new UTFOutputLineStream(writer.addMember("events"));
1728 fullStatus.events.saveEvents(lines);
1729 lines.close();
1731 PCHardwareInfo hw = fullStatus.pc.getHardwareInfo();
1732 DiskImageSet images = hw.images;
1733 int disks = 1 + images.highestDiskIndex();
1734 Set<ImageLibrary.ByteArray> imageSet = new HashSet<ImageLibrary.ByteArray>();
1735 for(int i = 0; i < disks; i++)
1736 saveDiskInfo(writer, images.lookupDisk(i), imageSet);
1737 saveDiskInfo(writer, hw.biosID, imageSet);
1738 saveDiskInfo(writer, hw.vgaBIOSID, imageSet);
1739 saveDiskInfo(writer, hw.hdaID, imageSet);
1740 saveDiskInfo(writer, hw.hdbID, imageSet);
1741 saveDiskInfo(writer, hw.hdcID, imageSet);
1742 saveDiskInfo(writer, hw.hddID, imageSet);
1745 public static PCFullStatus loadSavestate(JRSRArchiveReader reader, EventRecorder reuse, boolean forceMovie)
1746 throws IOException
1748 PCFullStatus fullStatus = new PCFullStatus();
1749 boolean ssPresent = false;
1750 UTFInputLineStream lines = new UTFInputLineStream(reader.readMember("header"));
1752 fullStatus.rerecords = -1;
1754 String[] components = nextParseLine(lines);
1755 while(components != null) {
1756 if("SAVESTATEID".equals(components[0])) {
1757 if(components.length != 2)
1758 throw new IOException("Bad " + components[0] + " line in header segment: " +
1759 "expected 2 components, got " + components.length);
1760 ssPresent = true;
1761 fullStatus.savestateID = components[1];
1762 } else if("PROJECTID".equals(components[0])) {
1763 if(components.length != 2)
1764 throw new IOException("Bad " + components[0] + " line in header segment: " +
1765 "expected 2 components, got " + components.length);
1766 fullStatus.projectID = components[1];
1767 } else if("RERECORDS".equals(components[0])) {
1768 if(components.length != 2)
1769 throw new IOException("Bad " + components[0] + " line in header segment: " +
1770 "expected 2 components, got " + components.length);
1771 try {
1772 fullStatus.rerecords = Long.parseLong(components[1]);
1773 if(fullStatus.rerecords < 0) {
1774 throw new IOException("Invalid rerecord count");
1776 } catch(NumberFormatException e) {
1777 throw new IOException("Invalid rerecord count");
1779 } else {
1780 if(fullStatus.extraHeaders == null) {
1781 fullStatus.extraHeaders = new String[1][];
1782 fullStatus.extraHeaders[0] = components;
1783 } else {
1784 String[][] extraHeaders = new String[fullStatus.extraHeaders.length + 1][];
1785 System.arraycopy(fullStatus.extraHeaders, 0, extraHeaders, 0, fullStatus.extraHeaders.length);
1786 extraHeaders[fullStatus.extraHeaders.length] = components;
1787 fullStatus.extraHeaders = extraHeaders;
1790 components = nextParseLine(lines);
1793 if(fullStatus.projectID == null)
1794 throw new IOException("PROJECTID header missing");
1795 if(fullStatus.rerecords < 0)
1796 throw new IOException("RERECORDS header missing");
1798 if(ssPresent && !forceMovie) {
1799 InputStream entry = reader.readMember("manifest");
1800 if(!SRLoader.checkConstructorManifest(entry))
1801 throw new IOException("Wrong savestate version");
1802 entry.close();
1804 entry = new FourToFiveDecoder(reader.readMember("savestate"));
1805 SRLoader loader = new SRLoader(new InflaterInputStream(entry));
1806 fullStatus.pc = (PC)(loader.loadObject());
1807 entry.close();
1808 } else {
1809 lines = new UTFInputLineStream(reader.readMember("initialization"));
1810 PC.PCHardwareInfo hwInfo = PC.PCHardwareInfo.parseHWInfoSegment(lines);
1811 fullStatus.pc = createPC(hwInfo);
1814 if(reuse != null) {
1815 fullStatus.events = reuse;
1816 } else {
1817 lines = new UTFInputLineStream(reader.readMember("events"));
1818 fullStatus.events = new EventRecorder(lines);
1820 fullStatus.events.attach(fullStatus.pc, forceMovie ? null : fullStatus.savestateID);
1822 return fullStatus;