Add SYSTEM header to note the system movie is for
[jpcrr.git] / org / jpc / emulator / PC.java
blobdf0360b0c8fd3f15d013952f5ad7822dd4c58a19
1 /*
2 JPC-RR: A x86 PC Hardware Emulator
3 Release 1
5 Copyright (C) 2007-2009 Isis Innovation Limited
6 Copyright (C) 2009-2010 H. Ilari Liusvaara
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 2 as published by
10 the Free Software Foundation.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 Based on JPC x86 PC Hardware emulator,
22 A project from the Physics Dept, The University of Oxford
24 Details about original JPC can be found at:
26 www-jpc.physics.ox.ac.uk
30 package org.jpc.emulator;
32 import org.jpc.emulator.motherboard.*;
33 import org.jpc.emulator.memory.*;
34 import org.jpc.emulator.pci.peripheral.*;
35 import org.jpc.emulator.pci.*;
36 import org.jpc.emulator.peripheral.*;
37 import org.jpc.emulator.processor.*;
38 import org.jpc.emulator.processor.fpu64.FpuState;
39 import org.jpc.diskimages.BlockDevice;
40 import org.jpc.diskimages.DiskImage;
41 import org.jpc.diskimages.DiskImageSet;
42 import org.jpc.diskimages.GenericBlockDevice;
43 import org.jpc.diskimages.ImageLibrary;
44 import org.jpc.diskimages.ImageMaker;
45 import org.jpc.jrsr.JRSRArchiveReader;
46 import org.jpc.jrsr.JRSRArchiveWriter;
47 import org.jpc.jrsr.UTFInputLineStream;
48 import org.jpc.jrsr.UTFOutputLineStream;
49 import org.jpc.jrsr.FourToFiveDecoder;
50 import org.jpc.jrsr.FourToFiveEncoder;
51 import 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;
91 public boolean flushOnModify;
93 public void dumpStatusPartial(StatusDumper output2) throws IOException
95 if(output2 != null)
96 return;
98 PrintStream output = System.err;
100 output.println("BIOS " + arrayToString(biosID));
101 output.println("VGABIOS " + arrayToString(vgaBIOSID));
102 if(hdaID != null)
103 output.println("HDA " + arrayToString(hdaID));
104 if(hdbID != null)
105 output.println("HDB " + arrayToString(hdbID));
106 if(hdcID != null)
107 output.println("HDC " + arrayToString(hdcID));
108 if(hddID != null)
109 output.println("HDD " + arrayToString(hddID));
110 int disks = 1 + images.highestDiskIndex();
111 for(int i = 0; i < disks; i++) {
112 DiskImage disk = images.lookupDisk(i);
113 if(disk != null)
114 output.println("DISK " + i + " " + arrayToString(disk.getImageID()));
116 if(initFDAIndex >= 0)
117 output.println("FDA " + initFDAIndex);
118 if(initFDBIndex >= 0)
119 output.println("FDB " + initFDBIndex);
120 if(initCDROMIndex >= 0)
121 output.println("CDROM " + initCDROMIndex);
122 output.println("INITIALTIME " + initRTCTime);
123 output.println("CPUDIVIDER " + (cpuDivider - 1));
124 if(fpuEmulator != null)
125 output.println("FPU " + fpuEmulator);
126 if(bootType == DriveSet.BootType.FLOPPY)
127 output.println("BOOT FLOPPY");
128 else if(bootType == DriveSet.BootType.HARD_DRIVE)
129 output.println("BOOT HDD");
130 else if(bootType == DriveSet.BootType.CDROM)
131 output.println("BOOT CDROM");
132 else if(bootType == null)
134 else
135 throw new IOException("Unknown boot type");
136 if(hwModules != null && !hwModules.isEmpty()) {
137 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
138 for(String p : e.getValue())
139 if(p != null)
140 output.println("LOADMODULEA " + e.getKey() + "(" + p + ")");
141 else
142 output.println("LOADMODULE " + e.getKey());
145 if(ioportDelayed)
146 output.println("IOPORTDELAY");
147 if(vgaHretrace)
148 output.println("VGAHRETRACE");
149 if(flushOnModify)
150 output.println("FLUSHONMODIFY");
153 public void dumpStatus(StatusDumper output)
155 if(output.dumped(this))
156 return;
158 output.println("#" + output.objectNumber(this) + ": PCHardwareInfo:");
159 try { dumpStatusPartial(output); } catch(Exception e) {}
160 output.endObject();
163 public void dumpSRPartial(SRDumper output) throws IOException
165 output.dumpArray(biosID);
166 output.dumpArray(vgaBIOSID);
167 output.dumpArray(hdaID);
168 output.dumpArray(hdbID);
169 output.dumpArray(hdcID);
170 output.dumpArray(hddID);
171 output.dumpObject(images);
172 output.dumpInt(initFDAIndex);
173 output.dumpInt(initFDBIndex);
174 output.dumpInt(initCDROMIndex);
175 output.dumpLong(initRTCTime);
176 output.dumpInt(cpuDivider);
177 output.dumpInt(memoryPages);
178 output.dumpString(fpuEmulator);
179 if(hwModules != null) {
180 output.dumpBoolean(true);
181 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
182 output.dumpBoolean(true);
183 output.dumpString(e.getKey());
184 for(String s : e.getValue()) {
185 output.dumpBoolean(true);
186 output.dumpString(s);
188 output.dumpBoolean(false);
190 output.dumpBoolean(false);
191 } else
192 output.dumpBoolean(false);
193 output.dumpByte(DriveSet.BootType.toNumeric(bootType));
194 output.dumpBoolean(ioportDelayed);
195 output.dumpBoolean(vgaHretrace);
196 output.dumpBoolean(flushOnModify);
199 public PCHardwareInfo()
201 images = new DiskImageSet();
204 public PCHardwareInfo(SRLoader input) throws IOException
206 input.objectCreated(this);
207 biosID = input.loadArrayByte();
208 vgaBIOSID = input.loadArrayByte();
209 hdaID = input.loadArrayByte();
210 hdbID = input.loadArrayByte();
211 hdcID = input.loadArrayByte();
212 hddID = input.loadArrayByte();
213 images = (DiskImageSet)input.loadObject();
214 initFDAIndex = input.loadInt();
215 initFDBIndex = input.loadInt();
216 initCDROMIndex = input.loadInt();
217 initRTCTime = input.loadLong();
218 cpuDivider = input.loadInt();
219 memoryPages = input.loadInt();
220 fpuEmulator = input.loadString();
221 boolean present = input.loadBoolean();
222 if(present) {
223 hwModules = new LinkedHashMap<String, Set<String>>();
224 present = input.loadBoolean();
225 while(present) {
226 String name = input.loadString();
227 hwModules.put(name, new LinkedHashSet<String>());
228 boolean present2 = input.loadBoolean();
229 while(present2) {
230 String params = input.loadString();
231 present2 = input.loadBoolean();
232 hwModules.get(name).add(params);
234 present = input.loadBoolean();
237 bootType = DriveSet.BootType.fromNumeric(input.loadByte());
238 ioportDelayed = false;
239 vgaHretrace = false;
240 flushOnModify = false;
241 if(input.objectEndsHere()) return;
242 ioportDelayed = input.loadBoolean();
243 if(input.objectEndsHere()) return;
244 vgaHretrace = input.loadBoolean();
245 if(input.objectEndsHere()) return;
246 flushOnModify = input.loadBoolean();
249 public void makeHWInfoSegment(UTFOutputLineStream output, DiskChanger changer) throws IOException
251 output.encodeLine("BIOS", arrayToString(biosID));
252 output.encodeLine("VGABIOS", arrayToString(vgaBIOSID));
253 output.encodeLine("HDA", arrayToString(hdaID));
254 output.encodeLine("HDB", arrayToString(hdbID));
255 output.encodeLine("HDC", arrayToString(hdcID));
256 output.encodeLine("HDD", arrayToString(hddID));
257 //TODO: When event recording becomes available, only save the disk images needed.
258 Set<Integer> usedDisks = changer.usedDiskSet();
259 int disks = 1 + images.highestDiskIndex();
260 for(int i = 0; i < disks; i++) {
261 DiskImage disk = images.lookupDisk(i);
262 if(disk != null && usedDisks.contains(i)) {
263 output.encodeLine("DISK", i, arrayToString(disk.getImageID()));
264 output.encodeLine("DISKNAME", i, disk.getName());
267 if(initFDAIndex >= 0) output.encodeLine("FDA", initFDAIndex);
268 if(initFDBIndex >= 0) output.encodeLine("FDB", initFDBIndex);
269 if(initCDROMIndex >= 0) output.encodeLine("CDROM", initCDROMIndex);
270 output.encodeLine("INITIALTIME", initRTCTime);
271 output.encodeLine("CPUDIVIDER", cpuDivider);
272 output.encodeLine("MEMORYSIZE", memoryPages);
273 output.encodeLine("FPU", fpuEmulator);
274 if(bootType == DriveSet.BootType.FLOPPY) output.encodeLine("BOOT", "FLOPPY");
275 else if(bootType == DriveSet.BootType.HARD_DRIVE) output.encodeLine("BOOT", "HDD");
276 else if(bootType == DriveSet.BootType.CDROM) output.encodeLine("BOOT", "CDROM");
277 else if(bootType == null)
279 else
280 throw new IOException("Unknown boot type");
281 if(hwModules != null && !hwModules.isEmpty()) {
282 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
283 for(String p : e.getValue())
284 if(p != null)
285 output.encodeLine("LOADMODULEA", e.getKey(), p);
286 else
287 output.encodeLine("LOADMODULE", e.getKey());
290 if(ioportDelayed)
291 output.encodeLine("IOPORTDELAY");
292 if(vgaHretrace)
293 output.encodeLine("VGAHRETRACE");
294 if(flushOnModify)
295 output.encodeLine("FLUSHONMODIFY");
298 public static int componentsForLine(String op)
300 if("BIOS".equals(op))
301 return 2;
302 if("VGABIOS".equals(op))
303 return 2;
304 if("HDA".equals(op))
305 return 2;
306 if("HDB".equals(op))
307 return 2;
308 if("HDC".equals(op))
309 return 2;
310 if("HDD".equals(op))
311 return 2;
312 if("FDA".equals(op))
313 return 2;
314 if("FDB".equals(op))
315 return 2;
316 if("CDROM".equals(op))
317 return 2;
318 if("INITIALTIME".equals(op))
319 return 2;
320 if("CPUDIVIDER".equals(op))
321 return 2;
322 if("MEMORYSIZE".equals(op))
323 return 2;
324 if("FPU".equals(op))
325 return 2;
326 if("BOOT".equals(op))
327 return 2;
328 if("LOADMODULE".equals(op))
329 return 2;
330 if("LOADMODULEA".equals(op))
331 return 3;
332 if("DISK".equals(op))
333 return 3;
334 if("DISKNAME".equals(op))
335 return 3;
336 if("IOPORTDELAY".equals(op))
337 return 1;
338 if("VGAHRETRACE".equals(op))
339 return 1;
340 if("FLUSHONMODIFY".equals(op))
341 return 1;
342 return 0;
346 public static PCHardwareInfo parseHWInfoSegment(UTFInputLineStream input) throws IOException
349 PCHardwareInfo hw = new PCHardwareInfo();
350 hw.initFDAIndex = -1;
351 hw.initFDBIndex = -1;
352 hw.initCDROMIndex = -1;
353 hw.images = new DiskImageSet();
354 hw.hwModules = new LinkedHashMap<String, Set<String>>();
355 String[] components = nextParseLine(input);
356 while(components != null) {
357 if(components.length != componentsForLine(components[0]))
358 throw new IOException("Bad " + components[0] + " line in ininitialization segment: " +
359 "expected " + componentsForLine(components[0]) + " components, got " + components.length);
360 if("BIOS".equals(components[0]))
361 hw.biosID = stringToArray(components[1]);
362 else if("VGABIOS".equals(components[0]))
363 hw.vgaBIOSID = stringToArray(components[1]);
364 else if("HDA".equals(components[0]))
365 hw.hdaID = stringToArray(components[1]);
366 else if("HDB".equals(components[0]))
367 hw.hdbID = stringToArray(components[1]);
368 else if("HDC".equals(components[0]))
369 hw.hdcID = stringToArray(components[1]);
370 else if("HDD".equals(components[0]))
371 hw.hddID = stringToArray(components[1]);
372 else if("DISK".equals(components[0])) {
373 int id;
374 try {
375 id = Integer.parseInt(components[1]);
376 if(id < 0)
377 throw new NumberFormatException("Bad id");
378 } catch(NumberFormatException e) {
379 throw new IOException("Bad DISK line in initialization segment");
381 hw.images.addDisk(id, new DiskImage(components[2], false));
382 } else if("DISKNAME".equals(components[0])) {
383 int id;
384 try {
385 id = Integer.parseInt(components[1]);
386 if(id < 0)
387 throw new NumberFormatException("Bad id");
388 hw.images.lookupDisk(id).setName(components[2]);
389 } catch(Exception e) {
390 throw new IOException("Bad DISKNAME line in initialization segment");
392 } else if("FDA".equals(components[0])) {
393 int id;
394 try {
395 id = Integer.parseInt(components[1]);
396 if(id < 0)
397 throw new NumberFormatException("Bad id");
398 } catch(NumberFormatException e) {
399 throw new IOException("Bad FDA line in initialization segment");
401 hw.initFDAIndex = id;
402 } else if("FDB".equals(components[0])) {
403 int id;
404 try {
405 id = Integer.parseInt(components[1]);
406 if(id < 0)
407 throw new NumberFormatException("Bad id");
408 } catch(NumberFormatException e) {
409 throw new IOException("Bad FDB line in initialization segment");
411 hw.initFDBIndex = id;
412 } else if("CDROM".equals(components[0])) {
413 int id;
414 try {
415 id = Integer.parseInt(components[1]);
416 if(id < 0)
417 throw new NumberFormatException("Bad id");
418 } catch(NumberFormatException e) {
419 throw new IOException("Bad CDROM line in initialization segment");
421 hw.initCDROMIndex = id;
422 } else if("INITIALTIME".equals(components[0])) {
423 long id;
424 try {
425 id = Long.parseLong(components[1]);
426 if(id < 0 || id > 4102444799999L)
427 throw new NumberFormatException("Bad id");
428 } catch(NumberFormatException e) {
429 throw new IOException("Bad INITIALTIME line in initialization segment");
431 hw.initRTCTime = id;
432 } else if("CPUDIVIDER".equals(components[0])) {
433 int id;
434 try {
435 id = Integer.parseInt(components[1]);
436 if(id < 1 || id > 256)
437 throw new NumberFormatException("Bad id");
438 } catch(NumberFormatException e) {
439 throw new IOException("Bad CPUDIVIDER line in initialization segment");
441 hw.cpuDivider = id;
442 } else if("MEMORYSIZE".equals(components[0])) {
443 int id;
444 try {
445 id = Integer.parseInt(components[1]);
446 if(id < 256 || id > 262144)
447 throw new NumberFormatException("Bad id");
448 } catch(NumberFormatException e) {
449 throw new IOException("Bad MEMORYSIZE line in initialization segment");
451 hw.memoryPages = id;
452 } else if("FPU".equals(components[0])) {
453 hw.fpuEmulator = components[1];
454 } else if("BOOT".equals(components[0])) {
455 if("FLOPPY".equals(components[1]))
456 hw.bootType = DriveSet.BootType.FLOPPY;
457 else if("HDD".equals(components[1]))
458 hw.bootType = DriveSet.BootType.HARD_DRIVE;
459 else if("CDROM".equals(components[1]))
460 hw.bootType = DriveSet.BootType.CDROM;
461 else
462 throw new IOException("Bad BOOT line in initialization segment");
463 } else if("LOADMODULE".equals(components[0])) {
464 if(!hw.hwModules.containsKey(components[1]))
465 hw.hwModules.put(components[1],new LinkedHashSet<String>());
466 hw.hwModules.get(components[1]).add(null);
467 } else if("LOADMODULEA".equals(components[0])) {
468 if(!hw.hwModules.containsKey(components[1]))
469 hw.hwModules.put(components[1],new LinkedHashSet<String>());
470 hw.hwModules.get(components[1]).add(components[2]);
471 } else if("IOPORTDELAY".equals(components[0])) {
472 hw.ioportDelayed = true;
473 } else if("VGAHRETRACE".equals(components[0])) {
474 hw.vgaHretrace = true;
475 } else if("FLUSHONMODIFY".equals(components[0])) {
476 hw.flushOnModify = true;
478 components = nextParseLine(input);
480 return hw;
485 public int sysRAMSize;
486 public int cpuClockDivider;
487 private PCHardwareInfo hwInfo;
489 public static volatile boolean compile = true;
491 private final Processor processor;
492 private final PhysicalAddressSpace physicalAddr;
493 private final LinearAddressSpace linearAddr;
494 private final Clock vmClock;
495 private final Set<HardwareComponent> parts;
496 private final CodeBlockManager manager;
497 private DiskImageSet images;
498 private final ResetButton brb;
499 private final DiskChanger diskChanger;
500 private final EventPoller poller;
502 private VGADigitalOut videoOut;
504 private TraceTrap traceTrap;
505 private boolean hitTraceTrap;
506 private boolean tripleFaulted;
507 private boolean rebootRequest;
509 private int cdromIndex;
511 private Map<String, SoundDigitalOut> soundOutputs;
513 public VGADigitalOut getVideoOutput()
515 return videoOut;
518 public HardwareComponent loadHardwareModule(String name, String params) throws IOException
520 Class<?> module;
521 if("".equals(params))
522 params = null;
524 try {
525 module = Class.forName(name);
526 } catch(Exception e) {
527 throw new IOException("Unable to find extension module \"" + name + "\".");
529 if(!HardwareComponent.class.isAssignableFrom(module)) {
530 throw new IOException("Extension module \"" + name + "\" is not valid hardware module.");
532 HardwareComponent c;
533 try {
534 boolean x = params.equals(""); //Intentionally cause NPE if params is null.
535 x = x & x; //Silence warning.
536 Constructor<?> cc = module.getConstructor(String.class);
537 c = (HardwareComponent)cc.newInstance(params);
538 } catch(NullPointerException e) {
539 try {
540 Constructor<?> cc = module.getConstructor();
541 c = (HardwareComponent)cc.newInstance();
542 } catch(InvocationTargetException f) {
543 Throwable e2 = f.getCause();
544 //If the exception is something unchecked, just pass it through.
545 if(e2 instanceof RuntimeException)
546 throw (RuntimeException)e2;
547 if(e2 instanceof Error) {
548 IOException ne = new IOException("Error while invoking constructor: " + e2);
549 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
550 throw ne;
552 //Also pass IOException through.
553 if(e2 instanceof IOException)
554 throw (IOException)e2;
555 //What the heck is that?
556 IOException ne = new IOException("Unknown exception while invoking module constructor: " + e2);
557 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
558 throw ne;
559 } catch(Exception f) {
560 throw new IOException("Unable to instantiate extension module \"" + name + "\".");
562 } catch(InvocationTargetException e) {
563 Throwable e2 = e.getCause();
564 //If the exception is something unchecked, just pass it through.
565 if(e2 instanceof RuntimeException)
566 throw (RuntimeException)e2;
567 if(e2 instanceof Error) {
568 IOException ne = new IOException("Error while invoking constructor: " + e2);
569 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
570 throw ne;
572 //Also pass IOException through.
573 if(e2 instanceof IOException)
574 throw (IOException)e2;
575 //What the heck is that?
576 IOException ne = new IOException("Unknown exception while invoking module constructor: " + e2);
577 ne.setStackTrace(e2.getStackTrace()); //Copy stack trace.
578 throw ne;
579 } catch(Exception f) {
580 throw new IOException("Unable to instantiate extension module \"" + name + "\": " + f.getMessage());
583 return c;
587 * Constructs a new <code>PC</code> instance with the specified external time-source and
588 * drive set.
589 * @param drives drive set for this instance.
590 * @throws java.io.IOException propogated from bios resource loading
592 public PC(DriveSet drives, int ramPages, int clockDivide, String sysBIOSImg, String vgaBIOSImg,
593 long initTime, DiskImageSet images, Map<String, Set<String>> hwModules, String fpuClass,
594 boolean ioportDelayed, boolean vgaHretrace, boolean flushOnModify)
595 throws IOException
597 parts = new LinkedHashSet<HardwareComponent>();
598 soundOutputs = new LinkedHashMap<String, SoundDigitalOut>();
600 cdromIndex = -1;
601 for(int i = 0; i < 4; i++) {
602 BlockDevice dev = drives.getHardDrive(i);
603 if(dev != null && dev.getType() == BlockDevice.Type.CDROM)
604 cdromIndex = i;
607 cpuClockDivider = clockDivide;
608 sysRAMSize = ramPages * 4096;
609 vmClock = new Clock();
611 if(hwModules != null)
612 for(Map.Entry<String,Set<String>> e : hwModules.entrySet()) {
613 String name = e.getKey();
614 for(String params : e.getValue()) {
615 System.err.println("Informational: Loading module \"" + name + "\".");
616 parts.add(loadHardwareModule(name, params));
620 parts.add(vmClock);
621 System.err.println("Informational: Creating CPU...");
622 processor = new Processor(vmClock, cpuClockDivider);
623 parts.add(processor);
624 manager = new CodeBlockManager();
626 processor.reloadCurrentBlockOnModification = flushOnModify;
628 System.err.println("Informational: Creating FPU...");
629 try {
630 if(fpuClass != null) {
631 Object fpu = Class.forName(fpuClass).getConstructor(Processor.class).newInstance(processor);
632 processor.setFPU((FpuState)fpu);
634 } catch(ClassCastException e) {
635 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
636 } catch(InvocationTargetException e) {
637 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
638 } catch(NoSuchMethodException e) {
639 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
640 } catch(IllegalAccessException e) {
641 throw new IOException("Bad FPU emulator class " + fpuClass + ": " + e.getMessage() + ".");
642 } catch(ClassNotFoundException e) {
643 throw new IOException("No such class: " + fpuClass + ".");
644 } catch(InstantiationException e) {
645 throw new IOException("Can't instantiate FPU emulator: " + e.getMessage() + ".");
648 System.err.println("Informational: Creating Reset Button...");
649 brb = new ResetButton(this);
650 parts.add(brb);
652 System.err.println("Informational: Creating Disk Changer..");
653 diskChanger = new DiskChanger(this);
654 parts.add(diskChanger);
656 System.err.println("Informational: Creating physical address space...");
657 physicalAddr = new PhysicalAddressSpace(manager, sysRAMSize);
658 parts.add(physicalAddr);
660 System.err.println("Informational: Creating linear address space...");
661 linearAddr = new LinearAddressSpace();
662 parts.add(linearAddr);
664 parts.add(drives);
666 //Motherboard
667 System.err.println("Informational: Creating I/O port handler...");
668 parts.add(new IOPortHandler(ioportDelayed));
669 System.err.println("Informational: Creating IRQ controller...");
670 parts.add(new InterruptController());
672 System.err.println("Informational: Creating primary DMA controller...");
673 parts.add(new DMAController(false, true));
674 System.err.println("Informational: Creating secondary DMA controller...");
675 parts.add(new DMAController(false, false));
677 System.err.println("Informational: Creating real time clock...");
678 parts.add(new RTC(0x70, 8, sysRAMSize, initTime));
679 System.err.println("Informational: Creating interval timer...");
680 parts.add(new IntervalTimer(0x40, 0));
681 System.err.println("Informational: Creating A20 Handler...");
682 parts.add(new GateA20Handler());
683 this.images = images;
685 //Peripherals
686 System.err.println("Informational: Creating IDE interface...");
687 parts.add(new PIIX3IDEInterface());
689 System.err.println("Informational: Creating Keyboard...");
690 parts.add(new Keyboard());
691 System.err.println("Informational: Creating floppy disk controller...");
692 parts.add(new FloppyController());
693 System.err.println("Informational: Creating PC speaker...");
694 soundOutputs.put("org.jpc.emulator.peripheral.PCSpeaker-0", new SoundDigitalOut(vmClock));
695 parts.add(new PCSpeaker(soundOutputs.get("org.jpc.emulator.peripheral.PCSpeaker-0")));
697 //PCI Stuff
698 System.err.println("Informational: Creating PCI Host Bridge...");
699 parts.add(new PCIHostBridge());
700 System.err.println("Informational: Creating PCI-to-ISA Bridge...");
701 parts.add(new PCIISABridge());
702 System.err.println("Informational: Creating PCI Bus...");
703 parts.add(new PCIBus());
705 //BIOSes
706 System.err.println("Informational: Creating system BIOS...");
707 parts.add(new SystemBIOS(sysBIOSImg));
708 System.err.println("Informational: Creating VGA BIOS...");
709 parts.add(new VGABIOS(vgaBIOSImg));
710 System.err.println("Informational: Creating trace trap...");
711 parts.add(traceTrap = new TraceTrap());
713 System.err.println("Informational: Creating event poller...");
714 poller = new EventPoller(vmClock);
716 System.err.println("Informational: Creating hardware info...");
717 hwInfo = new PCHardwareInfo();
719 DisplayController displayController = null;
720 for(HardwareComponent c : parts)
721 if(c instanceof DisplayController)
722 if(displayController == null)
723 displayController = (DisplayController)c;
724 else
725 throw new IOException("Can not have multiple display controllers: \"" +
726 c.getClass().getName() + "\" and \"" + displayController.getClass().getName() +
727 "\" are both display controllers.");
728 if(displayController == null)
730 System.err.println("Informational: Creating VGA card...");
731 VGACard card = new VGACard();
732 if(vgaHretrace)
733 card.enableVGAHretrace();
734 parts.add(card);
735 displayController = card;
737 videoOut = displayController.getOutputDevice();
739 System.err.println("Informational: Creating sound outputs...");
740 Map<String, Integer> numBase = new HashMap<String, Integer>();
741 for(HardwareComponent c : parts) {
742 if(!(c instanceof SoundOutputDevice))
743 continue;
744 SoundOutputDevice c2 = (SoundOutputDevice)c;
745 int channels = c2.requestedSoundChannels();
746 int base = 0;
747 if(numBase.containsKey(c.getClass().getName()))
748 base = numBase.get(c.getClass().getName()).intValue();
749 for(int i = 0; i < channels; i++) {
750 String outname = c.getClass().getName() + "-" + (base + i);
751 SoundDigitalOut sdo = new SoundDigitalOut(vmClock);
752 soundOutputs.put(outname, sdo);
753 c2.soundChannelCallback(sdo);
755 numBase.put(c.getClass().getName(), base + channels);
758 System.err.println("Informational: Configuring components...");
759 if(!configure())
760 throw new IllegalStateException("Can't initialize components (cyclic dependency?)");
761 System.err.println("Informational: PC initialization done.");
764 public SoundDigitalOut getSoundOut(String name)
766 return soundOutputs.get(name);
769 public Set<String> getSoundOutputs()
771 Set<String> outs = new TreeSet<String>();
772 for(Map.Entry<String, SoundDigitalOut> x : soundOutputs.entrySet())
773 outs.add(x.getKey());
774 return outs;
777 public int getCDROMIndex()
779 return cdromIndex;
782 public void dumpStatusPartial(StatusDumper output)
784 output.println("\tsysRAMSize " + sysRAMSize + " cpuClockDivider " + cpuClockDivider);
785 output.println("\ttripleFaulted " + tripleFaulted + " cdromIndex " + cdromIndex);
786 //hitTraceTrap not printed here.
787 output.println("\tprocessor <object #" + output.objectNumber(processor) + ">"); if(processor != null) processor.dumpStatus(output);
788 output.println("\tphysicalAddr <object #" + output.objectNumber(physicalAddr) + ">"); if(physicalAddr != null) physicalAddr.dumpStatus(output);
789 output.println("\tlinearAddr <object #" + output.objectNumber(linearAddr) + ">"); if(linearAddr != null) linearAddr.dumpStatus(output);
790 output.println("\tvmClock <object #" + output.objectNumber(vmClock) + ">"); if(vmClock != null) vmClock.dumpStatus(output);
791 output.println("\timages <object #" + output.objectNumber(images) + ">"); if(images != null) images.dumpStatus(output);
792 output.println("\ttraceTrap <object #" + output.objectNumber(traceTrap) + ">"); if(traceTrap != null) traceTrap.dumpStatus(output);
793 output.println("\thwInfo <object #" + output.objectNumber(hwInfo) + ">"); if(hwInfo != null) hwInfo.dumpStatus(output);
794 output.println("\thvideoOut <object #" + output.objectNumber(videoOut) + ">"); if(videoOut != null) videoOut.dumpStatus(output);
795 output.println("\tbrb <object #" + output.objectNumber(brb) + ">"); if(brb != null) brb.dumpStatus(output);
796 output.println("\tpoller <object #" + output.objectNumber(poller) + ">"); if(poller != null) poller.dumpStatus(output);
798 int i = 0;
799 for(HardwareComponent part : parts) {
800 output.println("\tparts[" + i++ + "] <object #" + output.objectNumber(part) + ">");
801 if(part != null) part.dumpStatus(output);
804 for(Map.Entry<String, SoundDigitalOut> c : soundOutputs.entrySet()) {
805 output.println("\tsoundOutputs[" + c.getKey() + "] <object #" + output.objectNumber(c.getValue()) + ">");
806 if(c.getValue() != null) c.getValue().dumpStatus(output);
810 public PC(SRLoader input) throws IOException
812 input.objectCreated(this);
813 cdromIndex = input.loadInt();
814 sysRAMSize = input.loadInt();
815 cpuClockDivider = input.loadInt();
816 processor = (Processor)input.loadObject();
817 physicalAddr = (PhysicalAddressSpace)input.loadObject();
818 linearAddr = (LinearAddressSpace)input.loadObject();
819 vmClock = (Clock)input.loadObject();
820 images = (DiskImageSet)(input.loadObject());
821 traceTrap = (TraceTrap)input.loadObject();
822 manager = (CodeBlockManager)input.loadObject();
823 hwInfo = (PCHardwareInfo)(input.loadObject());
824 videoOut = (VGADigitalOut)(input.loadObject());
825 hitTraceTrap = input.loadBoolean();
826 tripleFaulted = input.loadBoolean();
828 boolean present = input.loadBoolean();
829 parts = new LinkedHashSet<HardwareComponent>();
830 while(present) {
831 parts.add((HardwareComponent)input.loadObject());
832 present = input.loadBoolean();
834 rebootRequest = input.loadBoolean();
835 brb = (ResetButton)input.loadObject();
836 diskChanger = (DiskChanger)input.loadObject();
838 soundOutputs = new LinkedHashMap<String, SoundDigitalOut>();
839 present = input.loadBoolean();
840 while(present) {
841 String name = input.loadString();
842 soundOutputs.put(name, (SoundDigitalOut)input.loadObject());
843 present = input.loadBoolean();
845 poller = (EventPoller)(input.loadObject());
848 public void dumpStatus(StatusDumper output)
850 if(output.dumped(this))
851 return;
853 output.println("#" + output.objectNumber(this) + ": PC:");
854 dumpStatusPartial(output);
855 output.endObject();
858 public PCHardwareInfo getHardwareInfo()
860 return hwInfo;
863 public boolean getAndClearTripleFaulted()
865 boolean flag = tripleFaulted;
866 tripleFaulted = false;
867 return flag;
871 public void dumpSRPartial(SRDumper output) throws IOException
873 output.dumpInt(cdromIndex);
874 output.dumpInt(sysRAMSize);
875 output.dumpInt(cpuClockDivider);
876 output.dumpObject(processor);
877 output.dumpObject(physicalAddr);
878 output.dumpObject(linearAddr);
879 output.dumpObject(vmClock);
880 output.dumpObject(images);
881 output.dumpObject(traceTrap);
882 output.dumpObject(manager);
883 output.dumpObject(hwInfo);
884 output.dumpObject(videoOut);
885 output.dumpBoolean(hitTraceTrap);
886 output.dumpBoolean(tripleFaulted);
887 for(HardwareComponent part : parts) {
888 output.dumpBoolean(true);
889 output.dumpObject(part);
891 output.dumpBoolean(false);
892 output.dumpBoolean(rebootRequest);
893 output.dumpObject(brb);
894 output.dumpObject(diskChanger);
896 for(Map.Entry<String, SoundDigitalOut> c : soundOutputs.entrySet()) {
897 output.dumpBoolean(true);
898 output.dumpString(c.getKey());
899 output.dumpObject(c.getValue());
901 output.dumpBoolean(false);
902 output.dumpObject(poller);
905 public static Map<String, Set<String>> parseHWModules(String moduleString) throws IOException
907 Map<String, Set<String>> ret = new LinkedHashMap<String, Set<String>>();
909 while(moduleString != null && !moduleString.equals("")) {
910 String currentModule;
911 int parenDepth = 0;
912 int nameEnd = -1;
913 int paramsStart = -1;
914 int paramsEnd = -1;
915 int stringLen = moduleString.length();
916 boolean requireNextSep = false;
918 for(int i = 0; true; i++) {
919 int cp;
920 if(i < stringLen)
921 cp = moduleString.codePointAt(i);
922 else if(parenDepth == 0)
923 cp = ','; //Hack, consider last character seperator.
924 else
925 throw new IOException("Error in module string: unclosed '('.");
926 if(cp >= 0x10000)
927 i++; //Skip the next surrogate.
928 if((cp >= 0xD800 && cp < 0xE000) || ((cp & 0xFFFE) == 0xFFFE) || (cp >>> 16) > 16 || cp < 0)
929 throw new IOException("Error In module string: invalid Unicode character.");
930 if(requireNextSep && cp != ',')
931 throw new IOException("Error in module string: Expected ',' after ')' closing parameter list.");
932 else if(cp == ',' && i == 0)
933 throw new IOException("Error in module string: Blank module name not allowed.");
934 else if(cp == '(') {
935 if(parenDepth == 0) {
936 paramsStart = i + 1;
937 nameEnd = i - 1;
939 parenDepth++;
940 } else if(cp == ')') {
941 if(parenDepth == 0)
942 throw new IOException("Error in module string: Unpaired ')'.");
943 else if(parenDepth == 1) {
944 paramsEnd = i - 1;
945 requireNextSep = true;
947 parenDepth--;
948 } else if(cp == ',' && parenDepth == 0) {
949 if(nameEnd < 0)
950 nameEnd = i - 1;
951 currentModule = moduleString.substring(0, i);
952 if(i < stringLen ) {
953 moduleString = moduleString.substring(i + 1);
954 if(moduleString.equals(""))
955 throw new IOException("Error in module string: Blank module name not allowed.");
956 } else
957 moduleString = "";
958 break;
962 String name = currentModule.substring(0, nameEnd + 1);
963 String params = null;
964 if(paramsStart >= 0)
965 params = currentModule.substring(paramsStart, paramsEnd + 1);
967 if(ret.containsKey(name))
968 ret.get(name).add(params);
969 else {
970 Set<String> foo = new LinkedHashSet<String>();
971 foo.add(params);
972 ret.put(name, foo);
976 return ret;
980 private static GenericBlockDevice blockdeviceFor(String name) throws IOException
982 if(name == null)
983 return null;
984 return new GenericBlockDevice(new DiskImage(name, false));
987 public static PC createPC(PCHardwareInfo hw) throws IOException
989 PC pc;
990 String biosID = arrayToString(hw.biosID);
991 String vgaBIOSID = arrayToString(hw.vgaBIOSID);
992 BlockDevice hda = blockdeviceFor(arrayToString(hw.hdaID));
993 BlockDevice hdb = blockdeviceFor(arrayToString(hw.hdbID));
994 BlockDevice hdc = blockdeviceFor(arrayToString(hw.hdcID));
995 BlockDevice hdd = blockdeviceFor(arrayToString(hw.hddID));
996 if(hdc == null) {
997 hdc = new GenericBlockDevice(BlockDevice.Type.CDROM);
1000 DriveSet drives = new DriveSet(hw.bootType, hda, hdb, hdc, hdd);
1001 pc = new PC(drives, hw.memoryPages, hw.cpuDivider, biosID, vgaBIOSID, hw.initRTCTime, hw.images,
1002 hw.hwModules, hw.fpuEmulator, hw.ioportDelayed, hw.vgaHretrace, hw.flushOnModify);
1003 FloppyController fdc = (FloppyController)pc.getComponent(FloppyController.class);
1005 DiskImage img1 = pc.getDisks().lookupDisk(hw.initFDAIndex);
1006 fdc.changeDisk(img1, 0);
1008 DiskImage img2 = pc.getDisks().lookupDisk(hw.initFDBIndex);
1009 fdc.changeDisk(img2, 1);
1011 if(hdc.getType() == BlockDevice.Type.CDROM) {
1012 DiskImage img3 = pc.getDisks().lookupDisk(hw.initCDROMIndex);
1013 ((GenericBlockDevice)hdc).configure(img3);
1016 PCHardwareInfo hw2 = pc.getHardwareInfo();
1017 hw2.biosID = hw.biosID;
1018 hw2.vgaBIOSID = hw.vgaBIOSID;
1019 hw2.hdaID = hw.hdaID;
1020 hw2.hdbID = hw.hdbID;
1021 hw2.hdcID = hw.hdcID;
1022 hw2.hddID = hw.hddID;
1023 hw2.images = hw.images;
1024 hw2.initFDAIndex = hw.initFDAIndex;
1025 hw2.initFDBIndex = hw.initFDBIndex;
1026 hw2.initCDROMIndex = hw.initCDROMIndex;
1027 hw2.initRTCTime = hw.initRTCTime;
1028 hw2.cpuDivider = hw.cpuDivider;
1029 hw2.memoryPages = hw.memoryPages;
1030 hw2.bootType = hw.bootType;
1031 hw2.hwModules = hw.hwModules;
1032 hw2.fpuEmulator = hw.fpuEmulator;
1033 hw2.ioportDelayed = hw.ioportDelayed;
1034 hw2.vgaHretrace = hw.vgaHretrace;
1035 hw2.flushOnModify = hw.flushOnModify;
1036 return pc;
1040 * Starts this PC's attached clock instance.
1042 public void start()
1044 vmClock.resume();
1048 * Stops this PC's attached clock instance
1050 public void stop()
1052 vmClock.pause();
1056 * Inserts the specified floppy disk into the drive identified.
1057 * @param disk new floppy disk to be inserted.
1058 * @param index drive which the disk is inserted into.
1060 private void changeFloppyDisk(DiskImage disk, int index) throws IOException
1062 ((FloppyController)getComponent(FloppyController.class)).changeDisk(disk, index);
1065 public void changeFloppyDisk(int driveIndex, int diskIndex) throws IOException
1067 diskChanger.changeFloppyDisk(driveIndex, diskIndex);
1070 public void wpFloppyDisk(int diskIndex, boolean turnOn) throws IOException
1072 diskChanger.wpFloppyDisk(diskIndex, turnOn);
1075 public static class DiskChanger extends AbstractHardwareComponent implements SRDumpable, EventDispatchTarget
1077 private EventRecorder eRecorder; //Not saved.
1078 private PC upperBackref;
1079 private int currentDriveA; //Not saved.
1080 private int currentDriveB; //Not saved.
1081 private int currentCDROM; //Not saved.
1082 private Set<Integer> usedDisks; //Not saved.
1084 private void checkFloppyChange(int driveIndex, int diskIndex) throws IOException
1086 if(driveIndex == 2 && upperBackref.cdromIndex < 0)
1087 throw new IOException("No CD-ROM drive available");
1088 if(diskIndex < -1)
1089 throw new IOException("Illegal disk number");
1090 DiskImage disk = upperBackref.images.lookupDisk(diskIndex);
1091 if(driveIndex < 0 || driveIndex > 2)
1092 throw new IOException("Illegal drive number");
1093 if(diskIndex >= 0 && (diskIndex == currentDriveA || diskIndex == currentDriveB ||
1094 diskIndex == currentCDROM))
1095 throw new IOException("Specified disk is already in some drive");
1096 if(diskIndex < 0 && driveIndex == 0 && currentDriveA < 0)
1097 throw new IOException("No disk present in drive A");
1098 if(diskIndex < 0 && driveIndex == 1 && currentDriveB < 0)
1099 throw new IOException("No disk present in drive B");
1100 if(diskIndex < 0 && driveIndex == 2 && currentCDROM < 0)
1101 throw new IOException("No disk present in CD-ROM Drive");
1102 if(diskIndex > 0 && driveIndex < 2 && (disk == null || disk.getType() != BlockDevice.Type.FLOPPY))
1103 throw new IOException("Attempt to put non-floppy into drive A or B");
1104 if(diskIndex > 0 && driveIndex == 2 && (disk == null || disk.getType() != BlockDevice.Type.CDROM))
1105 throw new IOException("Attempt to put non-CDROM into CDROM drive");
1106 if(diskIndex > 0)
1107 usedDisks.add(new Integer(diskIndex));
1110 private void checkFloppyWP(int diskIndex, boolean turnOn) throws IOException
1112 if(diskIndex < 0)
1113 throw new IOException("Illegal floppy disk number");
1114 if(diskIndex == currentDriveA || diskIndex == currentDriveB)
1115 throw new IOException("Can not manipulate WP of disk in drive");
1116 DiskImage disk = upperBackref.images.lookupDisk(diskIndex);
1117 if(disk == null || disk.getType() != BlockDevice.Type.FLOPPY)
1118 throw new IOException("Can not manipulate WP of non-floppy disk");
1121 public synchronized void changeFloppyDisk(int driveIndex, int diskIndex) throws IOException
1123 checkFloppyChange(driveIndex, diskIndex);
1124 upperBackref.images.lookupDisk(diskIndex);
1125 try {
1126 if(driveIndex == 0)
1127 eRecorder.addEvent(-1, getClass(), new String[]{"FDA", "" + diskIndex});
1128 else if(driveIndex == 1)
1129 eRecorder.addEvent(-1, getClass(), new String[]{"FDB", "" + diskIndex});
1130 else if(driveIndex == 2)
1131 eRecorder.addEvent(-1, getClass(), new String[]{"CDROM", "" + diskIndex});
1132 } catch(Exception e) {}
1135 public synchronized void wpFloppyDisk(int diskIndex, boolean turnOn) throws IOException
1137 checkFloppyWP(diskIndex, turnOn);
1138 DiskImage disk = upperBackref.images.lookupDisk(diskIndex);
1139 try {
1140 if(turnOn && !disk.isReadOnly())
1141 eRecorder.addEvent(-1, getClass(), new String[]{"WRITEPROTECT", "" + diskIndex});
1142 else if(!turnOn && disk.isReadOnly())
1143 eRecorder.addEvent(-1, getClass(), new String[]{"WRITEUNPROTECT", "" + diskIndex});
1144 } catch(Exception e) {}
1147 public void doEvent(long timeStamp, String[] args, int level) throws IOException
1149 if(args == null || args.length != 2)
1150 throw new IOException("Invalid disk event parameters");
1151 int disk;
1152 try {
1153 disk = Integer.parseInt(args[1]);
1154 } catch(Exception e) {
1155 throw new IOException("Invalid disk number");
1157 DiskImage diskImg = upperBackref.images.lookupDisk(disk);
1159 if("FDA".equals(args[0])) {
1160 if(level <= EventRecorder.EVENT_STATE_EFFECT) {
1161 checkFloppyChange(0, disk);
1162 currentDriveA = disk;
1164 if(level == EventRecorder.EVENT_EXECUTE)
1165 upperBackref.changeFloppyDisk(diskImg, 0);
1166 } else if("FDB".equals(args[0])) {
1167 if(level <= EventRecorder.EVENT_STATE_EFFECT) {
1168 checkFloppyChange(1, disk);
1169 currentDriveB = disk;
1171 if(level == EventRecorder.EVENT_EXECUTE)
1172 upperBackref.changeFloppyDisk(diskImg, 1);
1173 } else if("CDROM".equals(args[0])) {
1174 if(level <= EventRecorder.EVENT_STATE_EFFECT) {
1175 checkFloppyChange(2, disk);
1176 currentCDROM = disk;
1178 DriveSet drives = (DriveSet)upperBackref.getComponent(DriveSet.class);
1179 if(level == EventRecorder.EVENT_EXECUTE)
1180 try {
1181 ((GenericBlockDevice)drives.getHardDrive(upperBackref.cdromIndex)).configure(diskImg);
1182 } catch(Exception e) {
1183 System.err.println("Warning: Unable to change disk in CD-ROM drive");
1185 } else if("WRITEPROTECT".equals(args[0])) {
1186 if(level <= EventRecorder.EVENT_STATE_EFFECT)
1187 checkFloppyWP(disk, true);
1188 if(level == EventRecorder.EVENT_EXECUTE)
1189 diskImg.setWP(true);
1190 } else if("WRITEUNPROTECT".equals(args[0])) {
1191 if(level <= EventRecorder.EVENT_STATE_EFFECT)
1192 checkFloppyWP(disk, false);
1193 if(level == EventRecorder.EVENT_EXECUTE)
1194 diskImg.setWP(false);
1195 } else
1196 throw new IOException("Invalid disk event type");
1199 public void startEventCheck()
1201 currentDriveA = upperBackref.hwInfo.initFDAIndex;
1202 currentDriveB = upperBackref.hwInfo.initFDBIndex;
1203 currentCDROM = upperBackref.hwInfo.initCDROMIndex;
1204 usedDisks = new HashSet<Integer>();
1205 if(currentDriveA >= 0)
1206 usedDisks.add(currentDriveA);
1207 if(currentDriveB >= 0)
1208 usedDisks.add(currentDriveB);
1209 if(currentCDROM >= 0)
1210 usedDisks.add(currentCDROM);
1213 private Set<Integer> usedDiskSet()
1215 return usedDisks;
1218 public void endEventCheck() throws IOException
1220 //Nothing to do.
1223 public DiskChanger(PC pc)
1225 upperBackref = pc;
1228 public DiskChanger(SRLoader input) throws IOException
1230 super(input);
1231 upperBackref = (PC)input.loadObject();
1234 public void dumpSRPartial(SRDumper output) throws IOException
1236 super.dumpSRPartial(output);
1237 output.dumpObject(upperBackref);
1240 public long getEventTimeLowBound(long stamp, String[] args) throws IOException
1242 return -1; //No timing constraints.
1245 public void setEventRecorder(EventRecorder recorder)
1247 eRecorder = recorder;
1250 public void dumpStatus(StatusDumper output)
1252 if(output.dumped(this))
1253 return;
1255 output.println("#" + output.objectNumber(this) + ": DiskChanger:");
1256 output.endObject();
1261 public DiskImageSet getDisks()
1263 return images;
1266 private boolean configure() {
1267 boolean fullyInitialised;
1268 int count = 0;
1269 do {
1270 fullyInitialised = true;
1271 for(HardwareComponent outer : parts) {
1272 if(outer.initialised())
1273 continue;
1275 for(HardwareComponent inner : parts)
1276 outer.acceptComponent(inner);
1278 fullyInitialised &= outer.initialised();
1280 count++;
1281 } while((fullyInitialised == false) && (count < 100));
1283 if(!fullyInitialised) {
1284 for(HardwareComponent hwc : parts)
1285 if(!hwc.initialised())
1286 System.err.println("Error: Component of type " + hwc.getClass() + " failed to initialize.");
1287 System.err.println("Critical error: PC component initialization failed.");
1288 return false;
1291 for(HardwareComponent hwc : parts)
1292 if(hwc instanceof PCIBus)
1293 ((PCIBus)hwc).biosInit();
1295 return true;
1298 public void setFPUHack()
1300 physicalAddr.setFPUHack();
1303 public void setVGADrawHack()
1305 HardwareComponent displayController = getComponent(VGACard.class);
1306 if(displayController != null)
1307 ((VGACard)displayController).setVGADrawHack();
1311 * Reset this PC back to its initial state.
1312 * <p>
1313 * This is roughly equivalent to a hard-reset (power down-up cycle).
1315 protected void reset()
1317 for(HardwareComponent hwc : parts)
1318 hwc.reset();
1319 configure();
1322 public void reboot()
1324 brb.reboot();
1327 public static class ResetButton extends AbstractHardwareComponent implements SRDumpable, EventDispatchTarget
1329 private EventRecorder eRecorder; //Not saved.
1330 private PC upperBackref;
1332 public EventRecorder getRecorder()
1334 return eRecorder;
1337 public void reboot()
1339 try {
1340 eRecorder.addEvent(-1, getClass(), null);
1341 } catch(Exception e) {}
1344 public void startEventCheck()
1346 //No state.
1349 public void doEvent(long timeStamp, String[] args, int level) throws IOException
1351 if(args != null)
1352 throw new IOException("Invalid reboot event");
1353 if(level == EventRecorder.EVENT_EXECUTE) {
1354 upperBackref.processor.eflagsMachineHalt = true;
1355 upperBackref.rebootRequest = true;
1359 public void endEventCheck() throws IOException
1363 public ResetButton(PC pc)
1365 upperBackref = pc;
1368 public ResetButton(SRLoader input) throws IOException
1370 super(input);
1371 upperBackref = (PC)input.loadObject();
1374 public void dumpSRPartial(SRDumper output) throws IOException
1376 super.dumpSRPartial(output);
1377 output.dumpObject(upperBackref);
1380 public long getEventTimeLowBound(long stamp, String[] args) throws IOException
1382 return -1; //No timing constraints.
1385 public void setEventRecorder(EventRecorder recorder)
1387 eRecorder = recorder;
1390 public void dumpStatus(StatusDumper output)
1392 if(output.dumped(this))
1393 return;
1395 output.println("#" + output.objectNumber(this) + ": ResetButton:");
1396 output.endObject();
1401 * Get an subclass of <code>cls</code> from this instance's parts list.
1402 * <p>
1403 * If <code>cls</code> is not assignment compatible with <code>HardwareComponent</code>
1404 * then this method will return null immediately.
1405 * @param cls component type required.
1406 * @return an instance of class <code>cls</code>, or <code>null</code> on failure
1408 public HardwareComponent getComponent(Class<? extends HardwareComponent> cls)
1410 if(!HardwareComponent.class.isAssignableFrom(cls))
1411 return null;
1413 for(HardwareComponent hwc : parts)
1414 if(cls.isInstance(hwc))
1415 return hwc;
1417 return null;
1420 public Set<HardwareComponent> allComponents()
1422 return parts;
1426 * Gets the processor instance associated with this PC.
1427 * @return associated processor instance.
1429 public Processor getProcessor()
1431 return processor;
1435 * Execute an arbitrarily large amount of code on this instance.
1436 * <p>
1437 * This method will execute continuously until there is either a mode switch,
1438 * or a unspecified large number of instructions have completed. It should
1439 * never run indefinitely.
1440 * @return total number of x86 instructions executed.
1442 public final int execute()
1444 if(processor.isProtectedMode())
1445 if(processor.isVirtual8086Mode())
1446 return executeVirtual8086();
1447 else
1448 return executeProtected();
1449 else
1450 return executeReal();
1453 public final int executeReal()
1455 int x86Count = 0;
1457 if(rebootRequest) {
1458 reset();
1459 rebootRequest = false;
1462 try {
1463 for(int i = 0; i < 100; i++) {
1464 int block;
1465 try {
1466 block = physicalAddr.executeReal(processor, processor.getInstructionPointer());
1467 } catch(org.jpc.emulator.processor.Processor.TripleFault e) {
1468 reset(); //Reboot the system to get the CPU back online.
1469 hitTraceTrap = true;
1470 tripleFaulted = true;
1471 break;
1473 x86Count += block;
1474 processor.instructionsExecuted += block;
1475 //Don't call this on aborted blocks. Doing so is probably good source of desyncs.
1476 if(!processor.eflagsLastAborted)
1477 processor.processRealModeInterrupts(1);
1478 if(traceTrap.getAndClearTrapActive()) {
1479 hitTraceTrap = true;
1480 break;
1482 if(rebootRequest) {
1483 reset();
1484 rebootRequest = false;
1485 break;
1488 } catch (ProcessorException p) {
1489 processor.handleRealModeException(p);
1490 } catch (ModeSwitchException e) {
1491 //System.err.println("Informational: CPU switching modes: " + e.toString());
1493 return x86Count;
1496 public TraceTrap getTraceTrap()
1498 return traceTrap;
1501 public boolean getHitTraceTrap()
1503 boolean tmp = hitTraceTrap;
1504 hitTraceTrap = false;
1505 return tmp;
1508 public final int executeProtected()
1510 int x86Count = 0;
1512 if(rebootRequest) {
1513 reset();
1514 rebootRequest = false;
1517 try {
1518 for(int i = 0; i < 100; i++) {
1519 int block;
1520 try {
1521 block= linearAddr.executeProtected(processor, processor.getInstructionPointer());
1522 } catch(org.jpc.emulator.processor.Processor.TripleFault e) {
1523 reset(); //Reboot the system to get the CPU back online.
1524 hitTraceTrap = true;
1525 tripleFaulted = true;
1526 break;
1528 x86Count += block;
1529 processor.instructionsExecuted += block;
1530 //Don't call this on aborted blocks. Doing so is probably good source of desyncs.
1531 if(!processor.eflagsLastAborted)
1532 processor.processProtectedModeInterrupts(1);
1533 if(traceTrap.getAndClearTrapActive()) {
1534 hitTraceTrap = true;
1535 break;
1537 if(rebootRequest) {
1538 reset();
1539 rebootRequest = false;
1540 break;
1543 } catch (ProcessorException p) {
1544 processor.handleProtectedModeException(p);
1545 } catch (ModeSwitchException e) {
1546 //System.err.println("Informational: CPU switching modes: " + e.toString());
1548 return x86Count;
1551 public final int executeVirtual8086()
1553 int x86Count = 0;
1555 if(rebootRequest) {
1556 reset();
1557 rebootRequest = false;
1560 try {
1561 for(int i = 0; i < 100; i++) {
1562 int block;
1563 try {
1564 block = linearAddr.executeVirtual8086(processor, processor.getInstructionPointer());
1565 } catch(org.jpc.emulator.processor.Processor.TripleFault e) {
1566 reset(); //Reboot the system to get the CPU back online.
1567 hitTraceTrap = true;
1568 tripleFaulted = true;
1569 break;
1571 x86Count += block;
1572 processor.instructionsExecuted += block;
1573 //Don't call this on aborted blocks. Doing so is probably good source of desyncs.
1574 if(!processor.eflagsLastAborted)
1575 processor.processVirtual8086ModeInterrupts(1);
1576 if(traceTrap.getAndClearTrapActive()) {
1577 hitTraceTrap = true;
1578 break;
1580 if(rebootRequest) {
1581 reset();
1582 rebootRequest = false;
1583 break;
1586 } catch (ProcessorException p) {
1587 processor.handleVirtual8086ModeException(p);
1588 } catch (ModeSwitchException e) {
1589 //System.err.println("Informational: CPU switching modes: " + e.toString());
1591 return x86Count;
1594 public static class PCFullStatus
1596 public PC pc; //Loaded SAVED.
1597 public EventRecorder events; //Loaded SAVED.
1598 public String projectID; //Loaded SAVED.
1599 public String savestateID; //Loaded SAVED.
1600 public long rerecords; //Loaded SAVED.
1601 public String[][] extraHeaders; //Loaded SAVED.
1604 private static void saveDiskInfo(UTFOutputLineStream lines, byte[] diskID)
1606 ImageLibrary lib = DiskImage.getLibrary();
1607 String fileName = lib.lookupFileName(diskID);
1608 if(fileName == null) {
1609 System.err.println("Warning: Can't find used disk from library (SHOULD NOT HAPPEN!).");
1610 return;
1612 try {
1613 ImageMaker.ParsedImage pimg = new ImageMaker.ParsedImage(fileName);
1614 RandomAccessFile image = new RandomAccessFile(fileName, "r");
1615 switch(pimg.typeCode) {
1616 case 0:
1617 lines.encodeLine("TYPE", "FLOPPY");
1618 break;
1619 case 1:
1620 lines.encodeLine("TYPE", "HDD");
1621 break;
1622 case 2:
1623 lines.encodeLine("TYPE", "CDROM");
1624 break;
1625 case 3:
1626 lines.encodeLine("TYPE", "BIOS");
1627 break;
1628 default:
1629 lines.encodeLine("TYPE", "UNKNOWN");
1630 break;
1632 lines.encodeLine("ID", new ImageLibrary.ByteArray(pimg.diskID));
1633 switch(pimg.typeCode) {
1634 case 0:
1635 case 1: //Floppies/HDD have the same fields.
1636 lines.encodeLine("TRACKS", pimg.tracks);
1637 lines.encodeLine("SIDES", pimg.sides);
1638 lines.encodeLine("SECTORS", pimg.sectors);
1639 case 2: //Floppies/HDD have these fields as well.
1640 lines.encodeLine("TOTALSECTORS", pimg.totalSectors);
1641 byte[] sector = new byte[512];
1642 byte[] zero = new byte[512];
1643 MessageDigest md = MessageDigest.getInstance("MD5");
1644 for(int i = 0; i < pimg.totalSectors; i++) {
1645 if(i < pimg.sectorOffsetMap.length && pimg.sectorOffsetMap[i] > 0) {
1646 image.seek(pimg.sectorOffsetMap[i]);
1647 if(image.read(sector) < 512) {
1648 throw new IOException("Failed to read sector from image file.");
1650 md.update(sector);
1651 } else
1652 md.update(zero);
1654 lines.encodeLine("IMAGEMD5", new ImageLibrary.ByteArray(md.digest()));
1655 break;
1656 case 3: //BIOS
1657 lines.encodeLine("IMAGELENGTH", pimg.rawImage.length);
1658 md = MessageDigest.getInstance("MD5");
1659 md.update(pimg.rawImage);
1660 lines.encodeLine("IMAGEMD5", new ImageLibrary.ByteArray(md.digest()));
1663 List<String> comments = pimg.comments;
1664 if(comments != null) {
1665 for(String x : comments)
1666 lines.encodeLine("COMMENT", x);
1668 image.close();
1669 } catch(Exception e) {
1670 System.err.println("Warning: Can't lookup disk information: " + e.getMessage() + "[" + e.getClass().getName() + "].");
1674 private static void saveDiskInfo(JRSRArchiveWriter writer, DiskImage image, Set<ImageLibrary.ByteArray> saved) throws IOException
1676 if(image == null)
1677 return;
1678 saveDiskInfo(writer, image.getImageID(), saved);
1681 private static void saveDiskInfo(JRSRArchiveWriter writer, byte[] diskID, Set<ImageLibrary.ByteArray> saved) throws IOException
1683 if(diskID == null)
1684 return;
1685 ImageLibrary.ByteArray id = new ImageLibrary.ByteArray(diskID);
1686 if(saved.contains(id))
1687 return;
1688 saved.add(id);
1689 UTFOutputLineStream lines = new UTFOutputLineStream(writer.addMember("diskinfo-" + arrayToString(diskID)));
1690 saveDiskInfo(lines, diskID);
1691 lines.close();
1694 public static void saveSavestate(JRSRArchiveWriter writer, PCFullStatus fullStatus, boolean movie, boolean noCompress)
1695 throws IOException
1697 fullStatus.savestateID = randomHexes(24);
1698 fullStatus.events.markSave(fullStatus.savestateID, fullStatus.rerecords);
1700 //Save the header.
1701 UTFOutputLineStream lines = new UTFOutputLineStream(writer.addMember("header"));
1702 lines.writeLine("PROJECTID " + fullStatus.projectID);
1703 if(!movie)
1704 lines.writeLine("SAVESTATEID " + fullStatus.savestateID);
1705 lines.writeLine("RERECORDS " + fullStatus.rerecords);
1706 lines.writeLine("SYSTEM PC-JPC-RR-r10");
1707 if(fullStatus.extraHeaders != null)
1708 for(int i = 0; i < fullStatus.extraHeaders.length; i++) {
1709 Object[] arr = new Object[fullStatus.extraHeaders[i].length];
1710 System.arraycopy(fullStatus.extraHeaders[i], 0, arr, 0, arr.length);
1711 lines.encodeLine(arr);
1713 lines.close();
1715 //Save intialization segment.
1716 lines = new UTFOutputLineStream(writer.addMember("initialization"));
1717 fullStatus.pc.getHardwareInfo().makeHWInfoSegment(lines, fullStatus.pc.diskChanger);
1718 lines.close();
1720 //Save savestate itsefl (if any).
1721 if(!movie) {
1722 FourToFiveEncoder entry = new FourToFiveEncoder(writer.addMember("savestate"));
1723 DeflaterOutputStream dos;
1724 Deflater deflater = new Deflater(noCompress ? Deflater.NO_COMPRESSION : Deflater.DEFAULT_COMPRESSION);
1725 OutputStream zip = dos = new DeflaterOutputStream(entry, deflater);
1726 SRDumper dumper = new SRDumper(zip);
1727 dumper.dumpObject(fullStatus.pc);
1728 dumper.flush();
1729 dos.close();
1731 OutputStream entry2 = writer.addMember("manifest");
1732 dumper.writeConstructorManifest(entry2);
1733 entry2.close();
1736 //Save the movie events.
1737 lines = new UTFOutputLineStream(writer.addMember("events"));
1738 fullStatus.events.saveEvents(lines);
1739 lines.close();
1741 //Save the disk info.
1742 PCHardwareInfo hw = fullStatus.pc.getHardwareInfo();
1743 DiskImageSet images = hw.images;
1744 int disks = 1 + images.highestDiskIndex();
1745 Set<ImageLibrary.ByteArray> imageSet = new HashSet<ImageLibrary.ByteArray>();
1746 for(int i = 0; i < disks; i++)
1747 saveDiskInfo(writer, images.lookupDisk(i), imageSet);
1748 saveDiskInfo(writer, hw.biosID, imageSet);
1749 saveDiskInfo(writer, hw.vgaBIOSID, imageSet);
1750 saveDiskInfo(writer, hw.hdaID, imageSet);
1751 saveDiskInfo(writer, hw.hdbID, imageSet);
1752 saveDiskInfo(writer, hw.hdcID, imageSet);
1753 saveDiskInfo(writer, hw.hddID, imageSet);
1755 //Save the output info.
1756 lines = new UTFOutputLineStream(writer.addMember("output-info"));
1757 lines.encodeLine("VIDEO");
1758 Set<String> sounds = fullStatus.pc.getSoundOutputs();
1759 for(String x : sounds)
1760 lines.encodeLine("AUDIO", x);
1761 lines.close();
1764 public static PCFullStatus loadSavestate(JRSRArchiveReader reader, boolean reuse, boolean forceMovie,
1765 PCFullStatus existing) throws IOException
1767 PCFullStatus fullStatus = new PCFullStatus();
1768 boolean ssPresent = false;
1769 UTFInputLineStream lines = new UTFInputLineStream(reader.readMember("header"));
1771 fullStatus.rerecords = -1;
1773 String[] components = nextParseLine(lines);
1774 while(components != null) {
1775 if("SAVESTATEID".equals(components[0])) {
1776 if(components.length != 2)
1777 throw new IOException("Bad " + components[0] + " line in header segment: " +
1778 "expected 2 components, got " + components.length);
1779 ssPresent = true;
1780 fullStatus.savestateID = components[1];
1781 } else if("PROJECTID".equals(components[0])) {
1782 if(components.length != 2)
1783 throw new IOException("Bad " + components[0] + " line in header segment: " +
1784 "expected 2 components, got " + components.length);
1785 fullStatus.projectID = components[1];
1786 } else if("RERECORDS".equals(components[0])) {
1787 if(components.length != 2)
1788 throw new IOException("Bad " + components[0] + " line in header segment: " +
1789 "expected 2 components, got " + components.length);
1790 try {
1791 fullStatus.rerecords = Long.parseLong(components[1]);
1792 if(fullStatus.rerecords < 0) {
1793 throw new IOException("Invalid rerecord count");
1795 } catch(NumberFormatException e) {
1796 throw new IOException("Invalid rerecord count");
1798 } else if("SYSTEM".equals(components[0])) {
1799 if(components.length != 2)
1800 throw new IOException("Bad " + components[0] + " line in header segment: " +
1801 "expected 2 components, got " + components.length);
1802 if(!"PC-JPC-RR-r10".equals(components[1]))
1803 throw new IOException("Invalid system type '" + components[1] + "'");
1804 } else {
1805 if(fullStatus.extraHeaders == null) {
1806 fullStatus.extraHeaders = new String[1][];
1807 fullStatus.extraHeaders[0] = components;
1808 } else {
1809 String[][] extraHeaders = new String[fullStatus.extraHeaders.length + 1][];
1810 System.arraycopy(fullStatus.extraHeaders, 0, extraHeaders, 0, fullStatus.extraHeaders.length);
1811 extraHeaders[fullStatus.extraHeaders.length] = components;
1812 fullStatus.extraHeaders = extraHeaders;
1815 components = nextParseLine(lines);
1818 if(fullStatus.projectID == null)
1819 throw new IOException("PROJECTID header missing");
1820 if(fullStatus.rerecords < 0)
1821 throw new IOException("RERECORDS header missing");
1823 if(ssPresent && !forceMovie) {
1824 InputStream entry = reader.readMember("manifest");
1825 if(!SRLoader.checkConstructorManifest(entry))
1826 throw new IOException("Wrong savestate version");
1827 entry.close();
1829 entry = new FourToFiveDecoder(reader.readMember("savestate"));
1830 SRLoader loader = new SRLoader(new InflaterInputStream(entry));
1831 fullStatus.pc = (PC)(loader.loadObject());
1832 entry.close();
1833 } else {
1834 lines = new UTFInputLineStream(reader.readMember("initialization"));
1835 PC.PCHardwareInfo hwInfo = PC.PCHardwareInfo.parseHWInfoSegment(lines);
1836 fullStatus.pc = createPC(hwInfo);
1839 if(reuse)
1840 fullStatus.events = existing.events;
1841 else {
1842 lines = new UTFInputLineStream(reader.readMember("events"));
1843 fullStatus.events = new EventRecorder(lines);
1846 if(reuse && (existing == null || !fullStatus.projectID.equals(existing.projectID)))
1847 throw new IOException("Savestate is not from current movie");
1849 fullStatus.events.attach(fullStatus.pc, forceMovie ? null : fullStatus.savestateID);
1851 if(!reuse) {
1852 fullStatus.events.setHeaders(fullStatus.extraHeaders);
1853 fullStatus.events.setRerecordCount(fullStatus.rerecords);
1856 if(existing == null || !fullStatus.projectID.equals(existing.projectID))
1857 fullStatus.rerecords++;
1858 else
1859 if(existing != null && existing.rerecords > fullStatus.rerecords)
1860 fullStatus.rerecords = existing.rerecords + 1;
1861 else
1862 fullStatus.rerecords++;
1864 return fullStatus;