Add wait for PC to stop function to Lua
[jpcrr.git] / org / jpc / emulator / PC.java
blob9a3d4122d20e98e0b3382dc3b5959ce8e39a52d4
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;
91 public void dumpStatusPartial(StatusDumper output2) throws IOException
93 if(output2 != null)
94 return;
96 PrintStream output = System.err;
98 output.println("BIOS " + arrayToString(biosID));
99 output.println("VGABIOS " + arrayToString(vgaBIOSID));
100 if(hdaID != null)
101 output.println("HDA " + arrayToString(hdaID));
102 if(hdbID != null)
103 output.println("HDB " + arrayToString(hdbID));
104 if(hdcID != null)
105 output.println("HDC " + arrayToString(hdcID));
106 if(hddID != null)
107 output.println("HDD " + arrayToString(hddID));
108 int disks = 1 + images.highestDiskIndex();
109 for(int i = 0; i < disks; i++) {
110 DiskImage disk = images.lookupDisk(i);
111 if(disk != null)
112 output.println("DISK " + i + " " + arrayToString(disk.getImageID()));
114 if(initFDAIndex >= 0)
115 output.println("FDA " + initFDAIndex);
116 if(initFDBIndex >= 0)
117 output.println("FDB " + initFDBIndex);
118 if(initCDROMIndex >= 0)
119 output.println("CDROM " + initCDROMIndex);
120 output.println("INITIALTIME " + initRTCTime);
121 output.println("CPUDIVIDER " + (cpuDivider - 1));
122 if(fpuEmulator != null)
123 output.println("FPU " + fpuEmulator);
124 if(bootType == DriveSet.BootType.FLOPPY)
125 output.println("BOOT FLOPPY");
126 else if(bootType == DriveSet.BootType.HARD_DRIVE)
127 output.println("BOOT HDD");
128 else if(bootType == DriveSet.BootType.CDROM)
129 output.println("BOOT CDROM");
130 else if(bootType == null)
132 else
133 throw new IOException("Unknown boot type");
134 if(hwModules != null && !hwModules.isEmpty()) {
135 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
136 for(String p : e.getValue())
137 if(p != null)
138 output.println("LOADMODULEA " + e.getKey() + "(" + p + ")");
139 else
140 output.println("LOADMODULE " + e.getKey());
143 if(ioportDelayed)
144 output.println("IOPORTDELAY");
147 public void dumpStatus(StatusDumper output)
149 if(output.dumped(this))
150 return;
152 output.println("#" + output.objectNumber(this) + ": PCHardwareInfo:");
153 try { dumpStatusPartial(output); } catch(Exception e) {}
154 output.endObject();
157 public void dumpSRPartial(SRDumper output) throws IOException
159 output.dumpArray(biosID);
160 output.dumpArray(vgaBIOSID);
161 output.dumpArray(hdaID);
162 output.dumpArray(hdbID);
163 output.dumpArray(hdcID);
164 output.dumpArray(hddID);
165 output.dumpObject(images);
166 output.dumpInt(initFDAIndex);
167 output.dumpInt(initFDBIndex);
168 output.dumpInt(initCDROMIndex);
169 output.dumpLong(initRTCTime);
170 output.dumpInt(cpuDivider);
171 output.dumpInt(memoryPages);
172 output.dumpString(fpuEmulator);
173 if(hwModules != null) {
174 output.dumpBoolean(true);
175 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
176 output.dumpBoolean(true);
177 output.dumpString(e.getKey());
178 for(String s : e.getValue()) {
179 output.dumpBoolean(true);
180 output.dumpString(s);
182 output.dumpBoolean(false);
184 output.dumpBoolean(false);
185 } else
186 output.dumpBoolean(false);
187 output.dumpByte(DriveSet.BootType.toNumeric(bootType));
188 output.dumpBoolean(ioportDelayed);
191 public PCHardwareInfo()
193 images = new DiskImageSet();
196 public PCHardwareInfo(SRLoader input) throws IOException
198 input.objectCreated(this);
199 biosID = input.loadArrayByte();
200 vgaBIOSID = input.loadArrayByte();
201 hdaID = input.loadArrayByte();
202 hdbID = input.loadArrayByte();
203 hdcID = input.loadArrayByte();
204 hddID = input.loadArrayByte();
205 images = (DiskImageSet)input.loadObject();
206 initFDAIndex = input.loadInt();
207 initFDBIndex = input.loadInt();
208 initCDROMIndex = input.loadInt();
209 initRTCTime = input.loadLong();
210 cpuDivider = input.loadInt();
211 memoryPages = input.loadInt();
212 fpuEmulator = input.loadString();
213 boolean present = input.loadBoolean();
214 if(present) {
215 hwModules = new LinkedHashMap<String, Set<String>>();
216 present = input.loadBoolean();
217 while(present) {
218 String name = input.loadString();
219 hwModules.put(name, new LinkedHashSet<String>());
220 boolean present2 = input.loadBoolean();
221 while(present2) {
222 String params = input.loadString();
223 present2 = input.loadBoolean();
224 hwModules.get(name).add(params);
226 present = input.loadBoolean();
229 bootType = DriveSet.BootType.fromNumeric(input.loadByte());
230 ioportDelayed = false;
231 if(input.objectEndsHere())
232 return;
233 ioportDelayed = input.loadBoolean();
236 public void makeHWInfoSegment(UTFOutputLineStream output, DiskChanger changer) throws IOException
238 output.encodeLine("BIOS", arrayToString(biosID));
239 output.encodeLine("VGABIOS", arrayToString(vgaBIOSID));
240 output.encodeLine("HDA", arrayToString(hdaID));
241 output.encodeLine("HDB", arrayToString(hdbID));
242 output.encodeLine("HDC", arrayToString(hdcID));
243 output.encodeLine("HDD", arrayToString(hddID));
244 //TODO: When event recording becomes available, only save the disk images needed.
245 Set<Integer> usedDisks = changer.usedDiskSet();
246 int disks = 1 + images.highestDiskIndex();
247 for(int i = 0; i < disks; i++) {
248 DiskImage disk = images.lookupDisk(i);
249 if(disk != null && usedDisks.contains(i)) {
250 output.encodeLine("DISK", i, arrayToString(disk.getImageID()));
251 output.encodeLine("DISKNAME", i, disk.getName());
254 if(initFDAIndex >= 0) output.encodeLine("FDA", initFDAIndex);
255 if(initFDBIndex >= 0) output.encodeLine("FDB", initFDBIndex);
256 if(initCDROMIndex >= 0) output.encodeLine("CDROM", initCDROMIndex);
257 output.encodeLine("INITIALTIME", initRTCTime);
258 output.encodeLine("CPUDIVIDER", cpuDivider);
259 output.encodeLine("MEMORYSIZE", memoryPages);
260 output.encodeLine("FPU", fpuEmulator);
261 if(bootType == DriveSet.BootType.FLOPPY) output.encodeLine("BOOT", "FLOPPY");
262 else if(bootType == DriveSet.BootType.HARD_DRIVE) output.encodeLine("BOOT", "HDD");
263 else if(bootType == DriveSet.BootType.CDROM) output.encodeLine("BOOT", "CDROM");
264 else if(bootType == null)
266 else
267 throw new IOException("Unknown boot type");
268 if(hwModules != null && !hwModules.isEmpty()) {
269 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
270 for(String p : e.getValue())
271 if(p != null)
272 output.encodeLine("LOADMODULEA", e.getKey(), p);
273 else
274 output.encodeLine("LOADMODULE", e.getKey());
277 if(ioportDelayed)
278 output.encodeLine("IOPORTDELAY");
281 public static int componentsForLine(String op)
283 if("BIOS".equals(op))
284 return 2;
285 if("VGABIOS".equals(op))
286 return 2;
287 if("HDA".equals(op))
288 return 2;
289 if("HDB".equals(op))
290 return 2;
291 if("HDC".equals(op))
292 return 2;
293 if("HDD".equals(op))
294 return 2;
295 if("FDA".equals(op))
296 return 2;
297 if("FDB".equals(op))
298 return 2;
299 if("CDROM".equals(op))
300 return 2;
301 if("INITIALTIME".equals(op))
302 return 2;
303 if("CPUDIVIDER".equals(op))
304 return 2;
305 if("MEMORYSIZE".equals(op))
306 return 2;
307 if("FPU".equals(op))
308 return 2;
309 if("BOOT".equals(op))
310 return 2;
311 if("LOADMODULE".equals(op))
312 return 2;
313 if("LOADMODULEA".equals(op))
314 return 3;
315 if("DISK".equals(op))
316 return 3;
317 if("DISKNAME".equals(op))
318 return 3;
319 if("IOPORTDELAY".equals(op))
320 return 1;
321 return 0;
325 public static PCHardwareInfo parseHWInfoSegment(UTFInputLineStream input) throws IOException
328 PCHardwareInfo hw = new PCHardwareInfo();
329 hw.initFDAIndex = -1;
330 hw.initFDBIndex = -1;
331 hw.initCDROMIndex = -1;
332 hw.images = new DiskImageSet();
333 hw.hwModules = new LinkedHashMap<String, Set<String>>();
334 String[] components = nextParseLine(input);
335 while(components != null) {
336 if(components.length != componentsForLine(components[0]))
337 throw new IOException("Bad " + components[0] + " line in ininitialization segment: " +
338 "expected " + componentsForLine(components[0]) + " components, got " + components.length);
339 if("BIOS".equals(components[0]))
340 hw.biosID = stringToArray(components[1]);
341 else if("VGABIOS".equals(components[0]))
342 hw.vgaBIOSID = stringToArray(components[1]);
343 else if("HDA".equals(components[0]))
344 hw.hdaID = stringToArray(components[1]);
345 else if("HDB".equals(components[0]))
346 hw.hdbID = stringToArray(components[1]);
347 else if("HDC".equals(components[0]))
348 hw.hdcID = stringToArray(components[1]);
349 else if("HDD".equals(components[0]))
350 hw.hddID = stringToArray(components[1]);
351 else if("DISK".equals(components[0])) {
352 int id;
353 try {
354 id = Integer.parseInt(components[1]);
355 if(id < 0)
356 throw new NumberFormatException("Bad id");
357 } catch(NumberFormatException e) {
358 throw new IOException("Bad DISK line in initialization segment");
360 hw.images.addDisk(id, new DiskImage(components[2], false));
361 } else if("DISKNAME".equals(components[0])) {
362 int id;
363 try {
364 id = Integer.parseInt(components[1]);
365 if(id < 0)
366 throw new NumberFormatException("Bad id");
367 hw.images.lookupDisk(id).setName(components[2]);
368 } catch(Exception e) {
369 throw new IOException("Bad DISKNAME line in initialization segment");
371 } else if("FDA".equals(components[0])) {
372 int id;
373 try {
374 id = Integer.parseInt(components[1]);
375 if(id < 0)
376 throw new NumberFormatException("Bad id");
377 } catch(NumberFormatException e) {
378 throw new IOException("Bad FDA line in initialization segment");
380 hw.initFDAIndex = id;
381 } else if("FDB".equals(components[0])) {
382 int id;
383 try {
384 id = Integer.parseInt(components[1]);
385 if(id < 0)
386 throw new NumberFormatException("Bad id");
387 } catch(NumberFormatException e) {
388 throw new IOException("Bad FDB line in initialization segment");
390 hw.initFDBIndex = id;
391 } else if("CDROM".equals(components[0])) {
392 int id;
393 try {
394 id = Integer.parseInt(components[1]);
395 if(id < 0)
396 throw new NumberFormatException("Bad id");
397 } catch(NumberFormatException e) {
398 throw new IOException("Bad CDROM line in initialization segment");
400 hw.initCDROMIndex = id;
401 } else if("INITIALTIME".equals(components[0])) {
402 long id;
403 try {
404 id = Long.parseLong(components[1]);
405 if(id < 0 || id > 4102444799999L)
406 throw new NumberFormatException("Bad id");
407 } catch(NumberFormatException e) {
408 throw new IOException("Bad INITIALTIME line in initialization segment");
410 hw.initRTCTime = id;
411 } else if("CPUDIVIDER".equals(components[0])) {
412 int id;
413 try {
414 id = Integer.parseInt(components[1]);
415 if(id < 1 || id > 256)
416 throw new NumberFormatException("Bad id");
417 } catch(NumberFormatException e) {
418 throw new IOException("Bad CPUDIVIDER line in initialization segment");
420 hw.cpuDivider = id;
421 } else if("MEMORYSIZE".equals(components[0])) {
422 int id;
423 try {
424 id = Integer.parseInt(components[1]);
425 if(id < 256 || id > 262144)
426 throw new NumberFormatException("Bad id");
427 } catch(NumberFormatException e) {
428 throw new IOException("Bad MEMORYSIZE line in initialization segment");
430 hw.memoryPages = id;
431 } else if("FPU".equals(components[0])) {
432 hw.fpuEmulator = components[1];
433 } else if("BOOT".equals(components[0])) {
434 if("FLOPPY".equals(components[1]))
435 hw.bootType = DriveSet.BootType.FLOPPY;
436 else if("HDD".equals(components[1]))
437 hw.bootType = DriveSet.BootType.HARD_DRIVE;
438 else if("CDROM".equals(components[1]))
439 hw.bootType = DriveSet.BootType.CDROM;
440 else
441 throw new IOException("Bad BOOT line in initialization segment");
442 } else if("LOADMODULE".equals(components[0])) {
443 if(!hw.hwModules.containsKey(components[1]))
444 hw.hwModules.put(components[1],new LinkedHashSet<String>());
445 hw.hwModules.get(components[1]).add(null);
446 } else if("LOADMODULEA".equals(components[0])) {
447 if(!hw.hwModules.containsKey(components[1]))
448 hw.hwModules.put(components[1],new LinkedHashSet<String>());
449 hw.hwModules.get(components[1]).add(components[2]);
450 } else if("IOPORTDELAY".equals(components[0])) {
451 hw.ioportDelayed = true;
453 components = nextParseLine(input);
455 return hw;
460 public int sysRAMSize;
461 public int cpuClockDivider;
462 private PCHardwareInfo hwInfo;
464 public static volatile boolean compile = true;
466 private final Processor processor;
467 private final PhysicalAddressSpace physicalAddr;
468 private final LinearAddressSpace linearAddr;
469 private final Clock vmClock;
470 private final Set<HardwareComponent> parts;
471 private final CodeBlockManager manager;
472 private DiskImageSet images;
473 private final ResetButton brb;
474 private final DiskChanger diskChanger;
475 private final EventPoller poller;
477 private VGADigitalOut videoOut;
479 private TraceTrap traceTrap;
480 private boolean hitTraceTrap;
481 private boolean tripleFaulted;
482 private boolean rebootRequest;
484 private int cdromIndex;
486 private Map<String, SoundDigitalOut> soundOutputs;
488 public VGADigitalOut getVideoOutput()
490 return videoOut;
493 public HardwareComponent loadHardwareModule(String name, String params) throws IOException
495 Class<?> module;
496 if("".equals(params))
497 params = null;
499 try {
500 module = Class.forName(name);
501 } catch(Exception e) {
502 throw new IOException("Unable to find extension module \"" + name + "\".");
504 if(!HardwareComponent.class.isAssignableFrom(module)) {
505 throw new IOException("Extension module \"" + name + "\" is not valid hardware module.");
507 HardwareComponent c;
508 try {
509 boolean x = params.equals(""); //Intentionally cause NPE if params is null.
510 x = x & x; //Silence warning.
511 Constructor<?> cc = module.getConstructor(String.class);
512 c = (HardwareComponent)cc.newInstance(params);
513 } catch(NullPointerException e) {
514 try {
515 Constructor<?> cc = module.getConstructor();
516 c = (HardwareComponent)cc.newInstance();
517 } catch(InvocationTargetException f) {
518 Throwable e2 = f.getCause();
519 //If the exception is something unchecked, just pass it through.
520 if(e2 instanceof RuntimeException)
521 throw (RuntimeException)e2;
522 if(e2 instanceof Error) {
523 IOException ne = new IOException("Error while invoking constructor: " + e2);
524 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
525 throw ne;
527 //Also pass IOException through.
528 if(e2 instanceof IOException)
529 throw (IOException)e2;
530 //What the heck is that?
531 IOException ne = new IOException("Unknown exception while invoking module constructor: " + e2);
532 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
533 throw ne;
534 } catch(Exception f) {
535 throw new IOException("Unable to instantiate extension module \"" + name + "\".");
537 } catch(InvocationTargetException e) {
538 Throwable e2 = e.getCause();
539 //If the exception is something unchecked, just pass it through.
540 if(e2 instanceof RuntimeException)
541 throw (RuntimeException)e2;
542 if(e2 instanceof Error) {
543 IOException ne = new IOException("Error while invoking constructor: " + e2);
544 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
545 throw ne;
547 //Also pass IOException through.
548 if(e2 instanceof IOException)
549 throw (IOException)e2;
550 //What the heck is that?
551 IOException ne = new IOException("Unknown exception while invoking module constructor: " + e2);
552 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
553 throw ne;
554 } catch(Exception f) {
555 throw new IOException("Unable to instantiate extension module \"" + name + "\": " + f.getMessage());
558 return c;
562 * Constructs a new <code>PC</code> instance with the specified external time-source and
563 * drive set.
564 * @param drives drive set for this instance.
565 * @throws java.io.IOException propogated from bios resource loading
567 public PC(DriveSet drives, int ramPages, int clockDivide, String sysBIOSImg, String vgaBIOSImg,
568 long initTime, DiskImageSet images, Map<String, Set<String>> hwModules, String fpuClass,
569 boolean ioportDelayed)
570 throws IOException
572 parts = new LinkedHashSet<HardwareComponent>();
573 soundOutputs = new LinkedHashMap<String, SoundDigitalOut>();
575 cdromIndex = -1;
576 for(int i = 0; i < 4; i++) {
577 BlockDevice dev = drives.getHardDrive(i);
578 if(dev != null && dev.getType() == BlockDevice.Type.CDROM)
579 cdromIndex = i;
582 cpuClockDivider = clockDivide;
583 sysRAMSize = ramPages * 4096;
584 vmClock = new Clock();
586 if(hwModules != null)
587 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
588 String name = e.getKey();
589 for(String params : e.getValue()) {
590 System.err.println("Informational: Loading module \"" + name + "\".");
591 parts.add(loadHardwareModule(name, params));
595 parts.add(vmClock);
596 System.err.println("Informational: Creating CPU...");
597 processor = new Processor(vmClock, cpuClockDivider);
598 parts.add(processor);
599 manager = new CodeBlockManager();
601 System.err.println("Informational: Creating FPU...");
602 try {
603 if(fpuClass != null) {
604 Object fpu = Class.forName(fpuClass).getConstructor(Processor.class).newInstance(processor);
605 processor.setFPU((FpuState)fpu);
607 } catch(ClassCastException e) {
608 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
609 } catch(InvocationTargetException e) {
610 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
611 } catch(NoSuchMethodException e) {
612 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
613 } catch(IllegalAccessException e) {
614 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
615 } catch(ClassNotFoundException e) {
616 throw new IOException("No such class: " + fpuClass + ".");
617 } catch(InstantiationException e) {
618 throw new IOException("Can't instantiate FPU emulator: " + e.getMessage() + ".");
621 System.err.println("Informational: Creating Reset Button...");
622 brb = new ResetButton(this);
623 parts.add(brb);
625 System.err.println("Informational: Creating Disk Changer..");
626 diskChanger = new DiskChanger(this);
627 parts.add(diskChanger);
629 System.err.println("Informational: Creating physical address space...");
630 physicalAddr = new PhysicalAddressSpace(manager, sysRAMSize);
631 parts.add(physicalAddr);
633 System.err.println("Informational: Creating linear address space...");
634 linearAddr = new LinearAddressSpace();
635 parts.add(linearAddr);
637 parts.add(drives);
639 //Motherboard
640 System.err.println("Informational: Creating I/O port handler...");
641 parts.add(new IOPortHandler(ioportDelayed));
642 System.err.println("Informational: Creating IRQ controller...");
643 parts.add(new InterruptController());
645 System.err.println("Informational: Creating primary DMA controller...");
646 parts.add(new DMAController(false, true));
647 System.err.println("Informational: Creating secondary DMA controller...");
648 parts.add(new DMAController(false, false));
650 System.err.println("Informational: Creating real time clock...");
651 parts.add(new RTC(0x70, 8, sysRAMSize, initTime));
652 System.err.println("Informational: Creating interval timer...");
653 parts.add(new IntervalTimer(0x40, 0));
654 System.err.println("Informational: Creating A20 Handler...");
655 parts.add(new GateA20Handler());
656 this.images = images;
658 //Peripherals
659 System.err.println("Informational: Creating IDE interface...");
660 parts.add(new PIIX3IDEInterface());
662 System.err.println("Informational: Creating Keyboard...");
663 parts.add(new Keyboard());
664 System.err.println("Informational: Creating floppy disk controller...");
665 parts.add(new FloppyController());
666 System.err.println("Informational: Creating PC speaker...");
667 soundOutputs.put("org.jpc.emulator.peripheral.PCSpeaker-0", new SoundDigitalOut(vmClock));
668 parts.add(new PCSpeaker(soundOutputs.get("org.jpc.emulator.peripheral.PCSpeaker-0")));
670 //PCI Stuff
671 System.err.println("Informational: Creating PCI Host Bridge...");
672 parts.add(new PCIHostBridge());
673 System.err.println("Informational: Creating PCI-to-ISA Bridge...");
674 parts.add(new PCIISABridge());
675 System.err.println("Informational: Creating PCI Bus...");
676 parts.add(new PCIBus());
678 //BIOSes
679 System.err.println("Informational: Creating system BIOS...");
680 parts.add(new SystemBIOS(sysBIOSImg));
681 System.err.println("Informational: Creating VGA BIOS...");
682 parts.add(new VGABIOS(vgaBIOSImg));
683 System.err.println("Informational: Creating trace trap...");
684 parts.add(traceTrap = new TraceTrap());
686 System.err.println("Informational: Creating event poller...");
687 poller = new EventPoller(vmClock);
689 System.err.println("Informational: Creating hardware info...");
690 hwInfo = new PCHardwareInfo();
692 DisplayController displayController = null;
693 for(HardwareComponent c : parts)
694 if(c instanceof DisplayController)
695 if(displayController == null)
696 displayController = (DisplayController)c;
697 else
698 throw new IOException("Can not have multiple display controllers: \"" +
699 c.getClass().getName() + "\" and \"" + displayController.getClass().getName() +
700 "\" are both display controllers.");
701 if(displayController == null)
703 System.err.println("Informational: Creating VGA card...");
704 VGACard card = new VGACard();
705 parts.add(card);
706 displayController = card;
708 videoOut = displayController.getOutputDevice();
710 System.err.println("Informational: Creating sound outputs...");
711 Map<String, Integer> numBase = new HashMap<String, Integer>();
712 for(HardwareComponent c : parts) {
713 if(!(c instanceof SoundOutputDevice))
714 continue;
715 SoundOutputDevice c2 = (SoundOutputDevice)c;
716 int channels = c2.requestedSoundChannels();
717 int base = 0;
718 if(numBase.containsKey(c.getClass().getName()))
719 base = numBase.get(c.getClass().getName()).intValue();
720 for(int i = 0; i < channels; i++) {
721 String outname = c.getClass().getName() + "-" + (base + i);
722 SoundDigitalOut sdo = new SoundDigitalOut(vmClock);
723 soundOutputs.put(outname, sdo);
724 c2.soundChannelCallback(sdo);
726 numBase.put(c.getClass().getName(), base + channels);
729 System.err.println("Informational: Configuring components...");
730 if (!configure()) {
731 throw new IllegalStateException("PC Configuration failed");
733 System.err.println("Informational: PC initialization done.");
736 public SoundDigitalOut getSoundOut(String name)
738 return soundOutputs.get(name);
741 public int getCDROMIndex()
743 return cdromIndex;
746 public void dumpStatusPartial(StatusDumper output)
748 output.println("\tsysRAMSize " + sysRAMSize + " cpuClockDivider " + cpuClockDivider);
749 output.println("\ttripleFaulted " + tripleFaulted + " cdromIndex " + cdromIndex);
750 //hitTraceTrap not printed here.
751 output.println("\tprocessor <object #" + output.objectNumber(processor) + ">"); if(processor != null) processor.dumpStatus(output);
752 output.println("\tphysicalAddr <object #" + output.objectNumber(physicalAddr) + ">"); if(physicalAddr != null) physicalAddr.dumpStatus(output);
753 output.println("\tlinearAddr <object #" + output.objectNumber(linearAddr) + ">"); if(linearAddr != null) linearAddr.dumpStatus(output);
754 output.println("\tvmClock <object #" + output.objectNumber(vmClock) + ">"); if(vmClock != null) vmClock.dumpStatus(output);
755 output.println("\timages <object #" + output.objectNumber(images) + ">"); if(images != null) images.dumpStatus(output);
756 output.println("\ttraceTrap <object #" + output.objectNumber(traceTrap) + ">"); if(traceTrap != null) traceTrap.dumpStatus(output);
757 output.println("\thwInfo <object #" + output.objectNumber(hwInfo) + ">"); if(hwInfo != null) hwInfo.dumpStatus(output);
758 output.println("\thvideoOut <object #" + output.objectNumber(videoOut) + ">"); if(videoOut != null) videoOut.dumpStatus(output);
759 output.println("\tbrb <object #" + output.objectNumber(brb) + ">"); if(brb != null) brb.dumpStatus(output);
760 output.println("\tpoller <object #" + output.objectNumber(poller) + ">"); if(poller != null) poller.dumpStatus(output);
762 int i = 0;
763 for (HardwareComponent part : parts) {
764 output.println("\tparts[" + i + "] <object #" + output.objectNumber(part) + ">"); if(part != null) part.dumpStatus(output);
765 i++;
768 for (Map.Entry<String, SoundDigitalOut> c : soundOutputs.entrySet()) {
769 output.println("\tsoundOutputs[" + c.getKey() + "] <object #" + output.objectNumber(c.getValue()) + ">"); if(c.getValue() != null) c.getValue().dumpStatus(output);
773 public PC(SRLoader input) throws IOException
775 input.objectCreated(this);
776 cdromIndex = input.loadInt();
777 sysRAMSize = input.loadInt();
778 cpuClockDivider = input.loadInt();
779 processor = (Processor)input.loadObject();
780 physicalAddr = (PhysicalAddressSpace)input.loadObject();
781 linearAddr = (LinearAddressSpace)input.loadObject();
782 vmClock = (Clock)input.loadObject();
783 images = (DiskImageSet)(input.loadObject());
784 traceTrap = (TraceTrap)input.loadObject();
785 manager = (CodeBlockManager)input.loadObject();
786 hwInfo = (PCHardwareInfo)(input.loadObject());
787 videoOut = (VGADigitalOut)(input.loadObject());
788 hitTraceTrap = input.loadBoolean();
789 tripleFaulted = input.loadBoolean();
791 boolean present = input.loadBoolean();
792 parts = new LinkedHashSet<HardwareComponent>();
793 while(present) {
794 parts.add((HardwareComponent)input.loadObject());
795 present = input.loadBoolean();
797 rebootRequest = input.loadBoolean();
798 brb = (ResetButton)input.loadObject();
799 diskChanger = (DiskChanger)input.loadObject();
801 soundOutputs = new LinkedHashMap<String, SoundDigitalOut>();
802 present = input.loadBoolean();
803 while(present) {
804 String name = input.loadString();
805 soundOutputs.put(name, (SoundDigitalOut)input.loadObject());
806 present = input.loadBoolean();
808 poller = (EventPoller)(input.loadObject());
811 public void dumpStatus(StatusDumper output)
813 if(output.dumped(this))
814 return;
816 output.println("#" + output.objectNumber(this) + ": PC:");
817 dumpStatusPartial(output);
818 output.endObject();
821 public PCHardwareInfo getHardwareInfo()
823 return hwInfo;
826 public boolean getAndClearTripleFaulted()
828 boolean flag = tripleFaulted;
829 tripleFaulted = false;
830 return flag;
834 public void dumpSRPartial(SRDumper output) throws IOException
836 output.dumpInt(cdromIndex);
837 output.dumpInt(sysRAMSize);
838 output.dumpInt(cpuClockDivider);
839 output.dumpObject(processor);
840 output.dumpObject(physicalAddr);
841 output.dumpObject(linearAddr);
842 output.dumpObject(vmClock);
843 output.dumpObject(images);
844 output.dumpObject(traceTrap);
845 output.dumpObject(manager);
846 output.dumpObject(hwInfo);
847 output.dumpObject(videoOut);
848 output.dumpBoolean(hitTraceTrap);
849 output.dumpBoolean(tripleFaulted);
850 for (HardwareComponent part : parts) {
851 output.dumpBoolean(true);
852 output.dumpObject(part);
854 output.dumpBoolean(false);
855 output.dumpBoolean(rebootRequest);
856 output.dumpObject(brb);
857 output.dumpObject(diskChanger);
859 for (Map.Entry<String, SoundDigitalOut> c : soundOutputs.entrySet()) {
860 output.dumpBoolean(true);
861 output.dumpString(c.getKey());
862 output.dumpObject(c.getValue());
864 output.dumpBoolean(false);
865 output.dumpObject(poller);
868 public static Map<String, Set<String>> parseHWModules(String moduleString) throws IOException
870 Map<String, Set<String>> ret = new LinkedHashMap<String, Set<String>>();
872 while(moduleString != null && !moduleString.equals("")) {
873 String currentModule;
874 int parenDepth = 0;
875 int nameEnd = -1;
876 int paramsStart = -1;
877 int paramsEnd = -1;
878 int stringLen = moduleString.length();
879 boolean requireNextSep = false;
881 for(int i = 0; true; i++) {
882 int cp;
883 if(i < stringLen)
884 cp = moduleString.codePointAt(i);
885 else if(parenDepth == 0)
886 cp = ','; //Hack, consider last character seperator.
887 else
888 throw new IOException("Error in module string: unclosed '('.");
889 if(cp >= 0x10000)
890 i++; //Skip the next surrogate.
891 if((cp >= 0xD800 && cp < 0xE000) || ((cp & 0xFFFE) == 0xFFFE) || (cp >>> 16) > 16 || cp < 0)
892 throw new IOException("Error In module string: invalid Unicode character.");
893 if(requireNextSep && cp != ',')
894 throw new IOException("Error in module string: Expected ',' after ')' closing parameter list.");
895 else if(cp == ',' && i == 0)
896 throw new IOException("Error in module string: Blank module name not allowed.");
897 else if(cp == '(') {
898 if(parenDepth == 0) {
899 paramsStart = i + 1;
900 nameEnd = i - 1;
902 parenDepth++;
903 } else if(cp == ')') {
904 if(parenDepth == 0)
905 throw new IOException("Error in module string: Unpaired ')'.");
906 else if(parenDepth == 1) {
907 paramsEnd = i - 1;
908 requireNextSep = true;
910 parenDepth--;
911 } else if(cp == ',' && parenDepth == 0) {
912 if(nameEnd < 0)
913 nameEnd = i - 1;
914 currentModule = moduleString.substring(0, i);
915 if(i < stringLen ) {
916 moduleString = moduleString.substring(i + 1);
917 if(moduleString.equals(""))
918 throw new IOException("Error in module string: Blank module name not allowed.");
919 } else
920 moduleString = "";
921 break;
925 String name = currentModule.substring(0, nameEnd + 1);
926 String params = null;
927 if(paramsStart >= 0)
928 params = currentModule.substring(paramsStart, paramsEnd + 1);
930 if(ret.containsKey(name))
931 ret.get(name).add(params);
932 else {
933 Set<String> foo = new LinkedHashSet<String>();
934 foo.add(params);
935 ret.put(name, foo);
939 return ret;
943 private static GenericBlockDevice blockdeviceFor(String name) throws IOException
945 if(name == null)
946 return null;
947 return new GenericBlockDevice(new DiskImage(name, false));
950 public static PC createPC(PCHardwareInfo hw) throws IOException
952 PC pc;
953 String biosID = arrayToString(hw.biosID);
954 String vgaBIOSID = arrayToString(hw.vgaBIOSID);
955 BlockDevice hda = blockdeviceFor(arrayToString(hw.hdaID));
956 BlockDevice hdb = blockdeviceFor(arrayToString(hw.hdbID));
957 BlockDevice hdc = blockdeviceFor(arrayToString(hw.hdcID));
958 BlockDevice hdd = blockdeviceFor(arrayToString(hw.hddID));
959 if(hdc == null) {
960 hdc = new GenericBlockDevice(BlockDevice.Type.CDROM);
963 DriveSet drives = new DriveSet(hw.bootType, hda, hdb, hdc, hdd);
964 pc = new PC(drives, hw.memoryPages, hw.cpuDivider, biosID, vgaBIOSID, hw.initRTCTime, hw.images,
965 hw.hwModules, hw.fpuEmulator, hw.ioportDelayed);
966 FloppyController fdc = (FloppyController)pc.getComponent(FloppyController.class);
968 DiskImage img1 = pc.getDisks().lookupDisk(hw.initFDAIndex);
969 fdc.changeDisk(img1, 0);
971 DiskImage img2 = pc.getDisks().lookupDisk(hw.initFDBIndex);
972 fdc.changeDisk(img2, 1);
974 if(hdc.getType() == BlockDevice.Type.CDROM) {
975 DiskImage img3 = pc.getDisks().lookupDisk(hw.initCDROMIndex);
976 ((GenericBlockDevice)hdc).configure(img3);
979 PCHardwareInfo hw2 = pc.getHardwareInfo();
980 hw2.biosID = hw.biosID;
981 hw2.vgaBIOSID = hw.vgaBIOSID;
982 hw2.hdaID = hw.hdaID;
983 hw2.hdbID = hw.hdbID;
984 hw2.hdcID = hw.hdcID;
985 hw2.hddID = hw.hddID;
986 hw2.images = hw.images;
987 hw2.initFDAIndex = hw.initFDAIndex;
988 hw2.initFDBIndex = hw.initFDBIndex;
989 hw2.initCDROMIndex = hw.initCDROMIndex;
990 hw2.initRTCTime = hw.initRTCTime;
991 hw2.cpuDivider = hw.cpuDivider;
992 hw2.memoryPages = hw.memoryPages;
993 hw2.bootType = hw.bootType;
994 hw2.hwModules = hw.hwModules;
995 hw2.fpuEmulator = hw.fpuEmulator;
996 hw2.ioportDelayed = hw.ioportDelayed;
997 return pc;
1001 * Starts this PC's attached clock instance.
1003 public void start()
1005 vmClock.resume();
1009 * Stops this PC's attached clock instance
1011 public void stop()
1013 vmClock.pause();
1017 * Inserts the specified floppy disk into the drive identified.
1018 * @param disk new floppy disk to be inserted.
1019 * @param index drive which the disk is inserted into.
1021 private void changeFloppyDisk(DiskImage disk, int index) throws IOException
1023 ((FloppyController)getComponent(FloppyController.class)).changeDisk(disk, index);
1026 public void changeFloppyDisk(int driveIndex, int diskIndex) throws IOException
1028 diskChanger.changeFloppyDisk(driveIndex, diskIndex);
1031 public void wpFloppyDisk(int diskIndex, boolean turnOn) throws IOException
1033 diskChanger.wpFloppyDisk(diskIndex, turnOn);
1036 public static class DiskChanger extends AbstractHardwareComponent implements SRDumpable, EventDispatchTarget
1038 private EventRecorder eRecorder; //Not saved.
1039 private PC upperBackref;
1040 private int currentDriveA; //Not saved.
1041 private int currentDriveB; //Not saved.
1042 private int currentCDROM; //Not saved.
1043 private Set<Integer> usedDisks; //Not saved.
1045 private void checkFloppyChange(int driveIndex, int diskIndex) throws IOException
1047 if(driveIndex == 2 && upperBackref.cdromIndex < 0)
1048 throw new IOException("No CD-ROM drive available");
1049 if(diskIndex < -1)
1050 throw new IOException("Illegal disk number");
1051 DiskImage disk = upperBackref.images.lookupDisk(diskIndex);
1052 if(driveIndex < 0 || driveIndex > 2)
1053 throw new IOException("Illegal drive number");
1054 if(diskIndex >= 0 && (diskIndex == currentDriveA || diskIndex == currentDriveB ||
1055 diskIndex == currentCDROM))
1056 throw new IOException("Specified disk is already in some drive");
1057 if(diskIndex < 0 && driveIndex == 0 && currentDriveA < 0)
1058 throw new IOException("No disk present in drive A");
1059 if(diskIndex < 0 && driveIndex == 1 && currentDriveB < 0)
1060 throw new IOException("No disk present in drive B");
1061 if(diskIndex < 0 && driveIndex == 2 && currentCDROM < 0)
1062 throw new IOException("No disk present in CD-ROM Drive");
1063 if(diskIndex > 0 && driveIndex < 2 && (disk == null || disk.getType() != BlockDevice.Type.FLOPPY))
1064 throw new IOException("Attempt to put non-floppy into drive A or B");
1065 if(diskIndex > 0 && driveIndex == 2 && (disk == null || disk.getType() != BlockDevice.Type.CDROM))
1066 throw new IOException("Attempt to put non-CDROM into CDROM drive");
1067 if(diskIndex > 0)
1068 usedDisks.add(new Integer(diskIndex));
1071 private void checkFloppyWP(int diskIndex, boolean turnOn) throws IOException
1073 if(diskIndex < 0)
1074 throw new IOException("Illegal floppy disk number");
1075 if(diskIndex == currentDriveA || diskIndex == currentDriveB)
1076 throw new IOException("Can not manipulate WP of disk in drive");
1077 DiskImage disk = upperBackref.images.lookupDisk(diskIndex);
1078 if(disk == null || disk.getType() != BlockDevice.Type.FLOPPY)
1079 throw new IOException("Can not manipulate WP of non-floppy disk");
1082 public synchronized void changeFloppyDisk(int driveIndex, int diskIndex) throws IOException
1084 checkFloppyChange(driveIndex, diskIndex);
1085 upperBackref.images.lookupDisk(diskIndex);
1086 try {
1087 if(driveIndex == 0)
1088 eRecorder.addEvent(-1, getClass(), new String[]{"FDA", "" + diskIndex});
1089 else if(driveIndex == 1)
1090 eRecorder.addEvent(-1, getClass(), new String[]{"FDB", "" + diskIndex});
1091 else if(driveIndex == 2)
1092 eRecorder.addEvent(-1, getClass(), new String[]{"CDROM", "" + diskIndex});
1093 } catch(Exception e) {}
1096 public synchronized void wpFloppyDisk(int diskIndex, boolean turnOn) throws IOException
1098 checkFloppyWP(diskIndex, turnOn);
1099 DiskImage disk = upperBackref.images.lookupDisk(diskIndex);
1100 try {
1101 if(turnOn && !disk.isReadOnly())
1102 eRecorder.addEvent(-1, getClass(), new String[]{"WRITEPROTECT", "" + diskIndex});
1103 else if(!turnOn && disk.isReadOnly())
1104 eRecorder.addEvent(-1, getClass(), new String[]{"WRITEUNPROTECT", "" + diskIndex});
1105 } catch(Exception e) {}
1108 public void doEvent(long timeStamp, String[] args, int level) throws IOException
1110 if(args == null || args.length != 2)
1111 throw new IOException("Invalid disk event parameters");
1112 int disk;
1113 try {
1114 disk = Integer.parseInt(args[1]);
1115 } catch(Exception e) {
1116 throw new IOException("Invalid disk number");
1118 DiskImage diskImg = upperBackref.images.lookupDisk(disk);
1120 if("FDA".equals(args[0])) {
1121 if(level <= EventRecorder.EVENT_STATE_EFFECT) {
1122 checkFloppyChange(0, disk);
1123 currentDriveA = disk;
1125 if(level == EventRecorder.EVENT_EXECUTE)
1126 upperBackref.changeFloppyDisk(diskImg, 0);
1127 } else if("FDB".equals(args[0])) {
1128 if(level <= EventRecorder.EVENT_STATE_EFFECT) {
1129 checkFloppyChange(1, disk);
1130 currentDriveB = disk;
1132 if(level == EventRecorder.EVENT_EXECUTE)
1133 upperBackref.changeFloppyDisk(diskImg, 1);
1134 } else if("CDROM".equals(args[0])) {
1135 if(level <= EventRecorder.EVENT_STATE_EFFECT) {
1136 checkFloppyChange(2, disk);
1137 currentCDROM = disk;
1139 DriveSet drives = (DriveSet)upperBackref.getComponent(DriveSet.class);
1140 if(level == EventRecorder.EVENT_EXECUTE)
1141 try {
1142 ((GenericBlockDevice)drives.getHardDrive(upperBackref.cdromIndex)).configure(diskImg);
1143 } catch(Exception e) {
1144 System.err.println("Warning: Unable to change disk in CD-ROM drive");
1146 } else if("WRITEPROTECT".equals(args[0])) {
1147 if(level <= EventRecorder.EVENT_STATE_EFFECT)
1148 checkFloppyWP(disk, true);
1149 if(level == EventRecorder.EVENT_EXECUTE)
1150 diskImg.setWP(true);
1151 } else if("WRITEUNPROTECT".equals(args[0])) {
1152 if(level <= EventRecorder.EVENT_STATE_EFFECT)
1153 checkFloppyWP(disk, false);
1154 if(level == EventRecorder.EVENT_EXECUTE)
1155 diskImg.setWP(false);
1156 } else
1157 throw new IOException("Invalid disk event type");
1160 public void startEventCheck()
1162 currentDriveA = upperBackref.hwInfo.initFDAIndex;
1163 currentDriveB = upperBackref.hwInfo.initFDBIndex;
1164 currentCDROM = upperBackref.hwInfo.initCDROMIndex;
1165 usedDisks = new HashSet<Integer>();
1166 if(currentDriveA >= 0)
1167 usedDisks.add(currentDriveA);
1168 if(currentDriveB >= 0)
1169 usedDisks.add(currentDriveB);
1170 if(currentCDROM >= 0)
1171 usedDisks.add(currentCDROM);
1174 private Set<Integer> usedDiskSet()
1176 return usedDisks;
1179 public void endEventCheck() throws IOException
1181 //Nothing to do.
1184 public DiskChanger(PC pc)
1186 upperBackref = pc;
1189 public DiskChanger(SRLoader input) throws IOException
1191 super(input);
1192 upperBackref = (PC)input.loadObject();
1195 public void dumpSRPartial(SRDumper output) throws IOException
1197 super.dumpSRPartial(output);
1198 output.dumpObject(upperBackref);
1201 public long getEventTimeLowBound(long stamp, String[] args) throws IOException
1203 return -1; //No timing constraints.
1206 public void setEventRecorder(EventRecorder recorder)
1208 eRecorder = recorder;
1211 public void dumpStatus(StatusDumper output)
1213 if(output.dumped(this))
1214 return;
1216 output.println("#" + output.objectNumber(this) + ": DiskChanger:");
1217 output.endObject();
1222 public DiskImageSet getDisks()
1224 return images;
1227 private boolean configure() {
1228 boolean fullyInitialised;
1229 int count = 0;
1230 do {
1231 fullyInitialised = true;
1232 for (HardwareComponent outer : parts) {
1233 if (outer.initialised()) {
1234 continue;
1237 for (HardwareComponent inner : parts) {
1238 outer.acceptComponent(inner);
1241 fullyInitialised &= outer.initialised();
1243 count++;
1244 } while ((fullyInitialised == false) && (count < 100));
1246 if (!fullyInitialised) {
1247 for(HardwareComponent hwc : parts) {
1248 if(!hwc.initialised()) {
1249 System.err.println("Error: Component of type " + hwc.getClass() + " failed to initialize.");
1252 System.err.println("Critical error: PC component initialization failed.");
1253 return false;
1256 for (HardwareComponent hwc : parts) {
1257 if (hwc instanceof PCIBus) {
1258 ((PCIBus) hwc).biosInit();
1262 return true;
1265 public void setFPUHack()
1267 physicalAddr.setFPUHack();
1270 public void setVGADrawHack()
1272 HardwareComponent displayController = getComponent(VGACard.class);
1273 if(displayController != null)
1274 ((VGACard)displayController).setVGADrawHack();
1278 * Reset this PC back to its initial state.
1279 * <p>
1280 * This is roughly equivalent to a hard-reset (power down-up cycle).
1282 protected void reset() {
1283 for (HardwareComponent hwc : parts) {
1284 hwc.reset();
1286 configure();
1289 public void reboot()
1291 brb.reboot();
1294 public static class ResetButton extends AbstractHardwareComponent implements SRDumpable, EventDispatchTarget
1296 private EventRecorder eRecorder; //Not saved.
1297 private PC upperBackref;
1299 public EventRecorder getRecorder()
1301 return eRecorder;
1304 public void reboot()
1306 try {
1307 eRecorder.addEvent(-1, getClass(), null);
1308 } catch(Exception e) {}
1311 public void startEventCheck()
1313 //No state.
1316 public void doEvent(long timeStamp, String[] args, int level) throws IOException
1318 if(args != null)
1319 throw new IOException("Invalid reboot event");
1320 if(level == EventRecorder.EVENT_EXECUTE) {
1321 upperBackref.processor.eflagsMachineHalt = true;
1322 upperBackref.rebootRequest = true;
1326 public void endEventCheck() throws IOException
1330 public ResetButton(PC pc)
1332 upperBackref = pc;
1335 public ResetButton(SRLoader input) throws IOException
1337 super(input);
1338 upperBackref = (PC)input.loadObject();
1341 public void dumpSRPartial(SRDumper output) throws IOException
1343 super.dumpSRPartial(output);
1344 output.dumpObject(upperBackref);
1347 public long getEventTimeLowBound(long stamp, String[] args) throws IOException
1349 return -1; //No timing constraints.
1352 public void setEventRecorder(EventRecorder recorder)
1354 eRecorder = recorder;
1357 public void dumpStatus(StatusDumper output)
1359 if(output.dumped(this))
1360 return;
1362 output.println("#" + output.objectNumber(this) + ": ResetButton:");
1363 output.endObject();
1368 * Get an subclass of <code>cls</code> from this instance's parts list.
1369 * <p>
1370 * If <code>cls</code> is not assignment compatible with <code>HardwareComponent</code>
1371 * then this method will return null immediately.
1372 * @param cls component type required.
1373 * @return an instance of class <code>cls</code>, or <code>null</code> on failure
1375 public HardwareComponent getComponent(Class<? extends HardwareComponent> cls) {
1376 if (!HardwareComponent.class.isAssignableFrom(cls)) {
1377 return null;
1380 for (HardwareComponent hwc : parts) {
1381 if (cls.isInstance(hwc)) {
1382 return hwc;
1385 return null;
1388 public Set<HardwareComponent> allComponents()
1390 return parts;
1394 * Gets the processor instance associated with this PC.
1395 * @return associated processor instance.
1397 public Processor getProcessor() {
1398 return processor;
1402 * Execute an arbitrarily large amount of code on this instance.
1403 * <p>
1404 * This method will execute continuously until there is either a mode switch,
1405 * or a unspecified large number of instructions have completed. It should
1406 * never run indefinitely.
1407 * @return total number of x86 instructions executed.
1409 public final int execute() {
1411 if (processor.isProtectedMode()) {
1412 if (processor.isVirtual8086Mode()) {
1413 return executeVirtual8086();
1414 } else {
1415 return executeProtected();
1417 } else {
1418 return executeReal();
1422 public final int executeReal()
1424 int x86Count = 0;
1426 if(rebootRequest) {
1427 reset();
1428 rebootRequest = false;
1433 for (int i = 0; i < 100; i++)
1435 int block;
1436 try {
1437 block = physicalAddr.executeReal(processor, processor.getInstructionPointer());
1438 } catch(org.jpc.emulator.processor.Processor.TripleFault e) {
1439 reset(); //Reboot the system to get the CPU back online.
1440 hitTraceTrap = true;
1441 tripleFaulted = true;
1442 break;
1444 x86Count += block;
1445 processor.instructionsExecuted += block;
1446 //Don't call this on aborted blocks. Doing so is probably good source of desyncs.
1447 if(!processor.eflagsLastAborted)
1448 processor.processRealModeInterrupts(1);
1449 if(traceTrap.getAndClearTrapActive()) {
1450 hitTraceTrap = true;
1451 break;
1453 if(rebootRequest) {
1454 reset();
1455 rebootRequest = false;
1456 break;
1459 } catch (ProcessorException p) {
1460 processor.handleRealModeException(p);
1462 catch (ModeSwitchException e)
1464 //System.err.println("Informational: CPU switching modes: " + e.toString());
1466 return x86Count;
1469 public TraceTrap getTraceTrap()
1471 return traceTrap;
1474 public boolean getHitTraceTrap()
1476 boolean tmp = hitTraceTrap;
1477 hitTraceTrap = false;
1478 return tmp;
1481 public final int executeProtected() {
1482 int x86Count = 0;
1484 if(rebootRequest) {
1485 reset();
1486 rebootRequest = false;
1491 for (int i = 0; i < 100; i++)
1493 int block;
1494 try {
1495 block= linearAddr.executeProtected(processor, processor.getInstructionPointer());
1496 } catch(org.jpc.emulator.processor.Processor.TripleFault e) {
1497 reset(); //Reboot the system to get the CPU back online.
1498 hitTraceTrap = true;
1499 tripleFaulted = true;
1500 break;
1502 x86Count += block;
1503 processor.instructionsExecuted += block;
1504 //Don't call this on aborted blocks. Doing so is probably good source of desyncs.
1505 if(!processor.eflagsLastAborted)
1506 processor.processProtectedModeInterrupts(1);
1507 if(traceTrap.getAndClearTrapActive()) {
1508 hitTraceTrap = true;
1509 break;
1511 if(rebootRequest) {
1512 reset();
1513 rebootRequest = false;
1514 break;
1517 } catch (ProcessorException p) {
1518 processor.handleProtectedModeException(p);
1520 catch (ModeSwitchException e)
1522 //System.err.println("Informational: CPU switching modes: " + e.toString());
1524 return x86Count;
1527 public final int executeVirtual8086() {
1528 int x86Count = 0;
1530 if(rebootRequest) {
1531 reset();
1532 rebootRequest = false;
1537 for (int i = 0; i < 100; i++)
1539 int block;
1540 try {
1541 block = linearAddr.executeVirtual8086(processor, processor.getInstructionPointer());
1542 } catch(org.jpc.emulator.processor.Processor.TripleFault e) {
1543 reset(); //Reboot the system to get the CPU back online.
1544 hitTraceTrap = true;
1545 tripleFaulted = true;
1546 break;
1548 x86Count += block;
1549 processor.instructionsExecuted += block;
1550 //Don't call this on aborted blocks. Doing so is probably good source of desyncs.
1551 if(!processor.eflagsLastAborted)
1552 processor.processVirtual8086ModeInterrupts(1);
1553 if(traceTrap.getAndClearTrapActive()) {
1554 hitTraceTrap = true;
1555 break;
1557 if(rebootRequest) {
1558 reset();
1559 rebootRequest = false;
1560 break;
1564 catch (ProcessorException p)
1566 processor.handleVirtual8086ModeException(p);
1568 catch (ModeSwitchException e)
1570 //System.err.println("Informational: CPU switching modes: " + e.toString());
1572 return x86Count;
1575 public static class PCFullStatus
1577 public PC pc; //Loaded SAVED.
1578 public EventRecorder events; //Loaded SAVED.
1579 public String projectID; //Loaded SAVED.
1580 public String savestateID; //Loaded SAVED.
1581 public long rerecords; //Loaded SAVED.
1582 public String[][] extraHeaders; //Loaded SAVED.
1585 private static void saveDiskInfo(UTFOutputLineStream lines, byte[] diskID)
1587 ImageLibrary lib = DiskImage.getLibrary();
1588 String fileName = lib.lookupFileName(diskID);
1589 if(fileName == null) {
1590 System.err.println("Warning: Can't find used disk from library (SHOULD NOT HAPPEN!).");
1591 return;
1593 try {
1594 ImageMaker.ParsedImage pimg = new ImageMaker.ParsedImage(fileName);
1595 RandomAccessFile image = new RandomAccessFile(fileName, "r");
1596 switch(pimg.typeCode) {
1597 case 0:
1598 lines.encodeLine("TYPE", "FLOPPY");
1599 break;
1600 case 1:
1601 lines.encodeLine("TYPE", "HDD");
1602 break;
1603 case 2:
1604 lines.encodeLine("TYPE", "CDROM");
1605 break;
1606 case 3:
1607 lines.encodeLine("TYPE", "BIOS");
1608 break;
1609 default:
1610 lines.encodeLine("TYPE", "UNKNOWN");
1611 break;
1613 lines.encodeLine("ID", new ImageLibrary.ByteArray(pimg.diskID));
1614 switch(pimg.typeCode) {
1615 case 0:
1616 case 1: //Floppies/HDD have the same fields.
1617 lines.encodeLine("TRACKS", pimg.tracks);
1618 lines.encodeLine("SIDES", pimg.sides);
1619 lines.encodeLine("SECTORS", pimg.sectors);
1620 case 2: //Floppies/HDD have these fields as well.
1621 lines.encodeLine("TOTALSECTORS", pimg.totalSectors);
1622 byte[] sector = new byte[512];
1623 byte[] zero = new byte[512];
1624 MessageDigest md = MessageDigest.getInstance("MD5");
1625 for(int i = 0; i < pimg.totalSectors; i++) {
1626 if(i < pimg.sectorOffsetMap.length && pimg.sectorOffsetMap[i] > 0) {
1627 image.seek(pimg.sectorOffsetMap[i]);
1628 if(image.read(sector) < 512) {
1629 throw new IOException("Failed to read sector from image file.");
1631 md.update(sector);
1632 } else
1633 md.update(zero);
1635 lines.encodeLine("IMAGEMD5", new ImageLibrary.ByteArray(md.digest()));
1636 break;
1637 case 3: //BIOS
1638 lines.encodeLine("IMAGELENGTH", pimg.rawImage.length);
1639 md = MessageDigest.getInstance("MD5");
1640 md.update(pimg.rawImage);
1641 lines.encodeLine("IMAGEMD5", new ImageLibrary.ByteArray(md.digest()));
1644 List<String> comments = pimg.comments;
1645 if(comments != null) {
1646 for(String x : comments)
1647 lines.encodeLine("COMMENT", x);
1649 } catch(Exception e) {
1650 System.err.println("Warning: Can't lookup disk information: " + e.getMessage() + "[" + e.getClass().getName() + "].");
1654 private static void saveDiskInfo(JRSRArchiveWriter writer, DiskImage image, Set<ImageLibrary.ByteArray> saved) throws IOException
1656 if(image == null)
1657 return;
1658 saveDiskInfo(writer, image.getImageID(), saved);
1661 private static void saveDiskInfo(JRSRArchiveWriter writer, byte[] diskID, Set<ImageLibrary.ByteArray> saved) throws IOException
1663 if(diskID == null)
1664 return;
1665 ImageLibrary.ByteArray id = new ImageLibrary.ByteArray(diskID);
1666 if(saved.contains(id))
1667 return;
1668 saved.add(id);
1669 UTFOutputLineStream lines = new UTFOutputLineStream(writer.addMember("diskinfo-" + arrayToString(diskID)));
1670 saveDiskInfo(lines, diskID);
1671 lines.close();
1674 public static void saveSavestate(JRSRArchiveWriter writer, PCFullStatus fullStatus, boolean movie)
1675 throws IOException
1677 fullStatus.savestateID = randomHexes(24);
1678 fullStatus.events.markSave(fullStatus.savestateID, fullStatus.rerecords);
1680 UTFOutputLineStream lines = new UTFOutputLineStream(writer.addMember("header"));
1681 lines.writeLine("PROJECTID " + fullStatus.projectID);
1682 if(!movie)
1683 lines.writeLine("SAVESTATEID " + fullStatus.savestateID);
1684 lines.writeLine("RERECORDS " + fullStatus.rerecords);
1685 if(fullStatus.extraHeaders != null)
1686 for(int i = 0; i < fullStatus.extraHeaders.length; i++) {
1687 Object[] arr = new Object[fullStatus.extraHeaders[i].length];
1688 System.arraycopy(fullStatus.extraHeaders[i], 0, arr, 0, arr.length);
1689 lines.encodeLine(arr);
1691 lines.close();
1693 lines = new UTFOutputLineStream(writer.addMember("initialization"));
1694 fullStatus.pc.getHardwareInfo().makeHWInfoSegment(lines, fullStatus.pc.diskChanger);
1695 lines.close();
1697 if(!movie) {
1698 FourToFiveEncoder entry = new FourToFiveEncoder(writer.addMember("savestate"));
1699 DeflaterOutputStream dos;
1700 OutputStream zip = dos = new DeflaterOutputStream(entry);
1701 SRDumper dumper = new SRDumper(zip);
1702 dumper.dumpObject(fullStatus.pc);
1703 dumper.flush();
1704 dos.close();
1706 OutputStream entry2 = writer.addMember("manifest");
1707 dumper.writeConstructorManifest(entry2);
1708 entry2.close();
1710 lines = new UTFOutputLineStream(writer.addMember("events"));
1711 fullStatus.events.saveEvents(lines);
1712 lines.close();
1714 PCHardwareInfo hw = fullStatus.pc.getHardwareInfo();
1715 DiskImageSet images = hw.images;
1716 int disks = 1 + images.highestDiskIndex();
1717 Set<ImageLibrary.ByteArray> imageSet = new HashSet<ImageLibrary.ByteArray>();
1718 for(int i = 0; i < disks; i++)
1719 saveDiskInfo(writer, images.lookupDisk(i), imageSet);
1720 saveDiskInfo(writer, hw.biosID, imageSet);
1721 saveDiskInfo(writer, hw.vgaBIOSID, imageSet);
1722 saveDiskInfo(writer, hw.hdaID, imageSet);
1723 saveDiskInfo(writer, hw.hdbID, imageSet);
1724 saveDiskInfo(writer, hw.hdcID, imageSet);
1725 saveDiskInfo(writer, hw.hddID, imageSet);
1728 public static PCFullStatus loadSavestate(JRSRArchiveReader reader, EventRecorder reuse, boolean forceMovie)
1729 throws IOException
1731 PCFullStatus fullStatus = new PCFullStatus();
1732 boolean ssPresent = false;
1733 UTFInputLineStream lines = new UTFInputLineStream(reader.readMember("header"));
1735 fullStatus.rerecords = -1;
1737 String[] components = nextParseLine(lines);
1738 while(components != null) {
1739 if("SAVESTATEID".equals(components[0])) {
1740 if(components.length != 2)
1741 throw new IOException("Bad " + components[0] + " line in header segment: " +
1742 "expected 2 components, got " + components.length);
1743 ssPresent = true;
1744 fullStatus.savestateID = components[1];
1745 } else if("PROJECTID".equals(components[0])) {
1746 if(components.length != 2)
1747 throw new IOException("Bad " + components[0] + " line in header segment: " +
1748 "expected 2 components, got " + components.length);
1749 fullStatus.projectID = components[1];
1750 } else if("RERECORDS".equals(components[0])) {
1751 if(components.length != 2)
1752 throw new IOException("Bad " + components[0] + " line in header segment: " +
1753 "expected 2 components, got " + components.length);
1754 try {
1755 fullStatus.rerecords = Long.parseLong(components[1]);
1756 if(fullStatus.rerecords < 0) {
1757 throw new IOException("Invalid rerecord count");
1759 } catch(NumberFormatException e) {
1760 throw new IOException("Invalid rerecord count");
1762 } else {
1763 if(fullStatus.extraHeaders == null) {
1764 fullStatus.extraHeaders = new String[1][];
1765 fullStatus.extraHeaders[0] = components;
1766 } else {
1767 String[][] extraHeaders = new String[fullStatus.extraHeaders.length + 1][];
1768 System.arraycopy(fullStatus.extraHeaders, 0, extraHeaders, 0, fullStatus.extraHeaders.length);
1769 extraHeaders[fullStatus.extraHeaders.length] = components;
1770 fullStatus.extraHeaders = extraHeaders;
1773 components = nextParseLine(lines);
1776 if(fullStatus.projectID == null)
1777 throw new IOException("PROJECTID header missing");
1778 if(fullStatus.rerecords < 0)
1779 throw new IOException("RERECORDS header missing");
1781 if(ssPresent && !forceMovie) {
1782 InputStream entry = reader.readMember("manifest");
1783 if(!SRLoader.checkConstructorManifest(entry))
1784 throw new IOException("Wrong savestate version");
1785 entry.close();
1787 entry = new FourToFiveDecoder(reader.readMember("savestate"));
1788 SRLoader loader = new SRLoader(new InflaterInputStream(entry));
1789 fullStatus.pc = (PC)(loader.loadObject());
1790 entry.close();
1791 } else {
1792 lines = new UTFInputLineStream(reader.readMember("initialization"));
1793 PC.PCHardwareInfo hwInfo = PC.PCHardwareInfo.parseHWInfoSegment(lines);
1794 fullStatus.pc = createPC(hwInfo);
1797 if(reuse != null) {
1798 fullStatus.events = reuse;
1799 } else {
1800 lines = new UTFInputLineStream(reader.readMember("events"));
1801 fullStatus.events = new EventRecorder(lines);
1803 fullStatus.events.attach(fullStatus.pc, forceMovie ? null : fullStatus.savestateID);
1805 return fullStatus;