2 JPC-RR: A x86 PC Hardware Emulator
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
;
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
;
64 * This class represents the emulated PC as a whole, and holds references
65 * to its main hardware components.
66 * @author Chris Dennis
69 public class PC
implements SRDumpable
71 public static class PCHardwareInfo
implements SRDumpable
74 public byte[] vgaBIOSID
;
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
96 PrintStream output
= System
.err
;
98 output
.println("BIOS " + arrayToString(biosID
));
99 output
.println("VGABIOS " + arrayToString(vgaBIOSID
));
101 output
.println("HDA " + arrayToString(hdaID
));
103 output
.println("HDB " + arrayToString(hdbID
));
105 output
.println("HDC " + arrayToString(hdcID
));
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
);
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)
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())
138 output
.println("LOADMODULEA " + e
.getKey() + "(" + p
+ ")");
140 output
.println("LOADMODULE " + e
.getKey());
144 output
.println("IOPORTDELAY");
147 public void dumpStatus(StatusDumper output
)
149 if(output
.dumped(this))
152 output
.println("#" + output
.objectNumber(this) + ": PCHardwareInfo:");
153 try { dumpStatusPartial(output
); } catch(Exception e
) {}
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);
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();
215 hwModules
= new LinkedHashMap
<String
, Set
<String
>>();
216 present
= input
.loadBoolean();
218 String name
= input
.loadString();
219 hwModules
.put(name
, new LinkedHashSet
<String
>());
220 boolean present2
= input
.loadBoolean();
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())
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)
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())
272 output
.encodeLine("LOADMODULEA", e
.getKey(), p
);
274 output
.encodeLine("LOADMODULE", e
.getKey());
278 output
.encodeLine("IOPORTDELAY");
281 public static int componentsForLine(String op
)
283 if("BIOS".equals(op
))
285 if("VGABIOS".equals(op
))
299 if("CDROM".equals(op
))
301 if("INITIALTIME".equals(op
))
303 if("CPUDIVIDER".equals(op
))
305 if("MEMORYSIZE".equals(op
))
309 if("BOOT".equals(op
))
311 if("LOADMODULE".equals(op
))
313 if("LOADMODULEA".equals(op
))
315 if("DISK".equals(op
))
317 if("DISKNAME".equals(op
))
319 if("IOPORTDELAY".equals(op
))
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])) {
354 id
= Integer
.parseInt(components
[1]);
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])) {
364 id
= Integer
.parseInt(components
[1]);
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])) {
374 id
= Integer
.parseInt(components
[1]);
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])) {
384 id
= Integer
.parseInt(components
[1]);
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])) {
394 id
= Integer
.parseInt(components
[1]);
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])) {
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");
411 } else if("CPUDIVIDER".equals(components
[0])) {
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");
421 } else if("MEMORYSIZE".equals(components
[0])) {
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");
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
;
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
);
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()
493 public HardwareComponent
loadHardwareModule(String name
, String params
) throws IOException
496 if("".equals(params
))
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.");
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
) {
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.
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.
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.
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.
554 } catch(Exception f
) {
555 throw new IOException("Unable to instantiate extension module \"" + name
+ "\": " + f
.getMessage());
562 * Constructs a new <code>PC</code> instance with the specified external time-source and
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
)
572 parts
= new LinkedHashSet
<HardwareComponent
>();
573 soundOutputs
= new LinkedHashMap
<String
, SoundDigitalOut
>();
576 for(int i
= 0; i
< 4; i
++) {
577 BlockDevice dev
= drives
.getHardDrive(i
);
578 if(dev
!= null && dev
.getType() == BlockDevice
.Type
.CDROM
)
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
));
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...");
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);
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
);
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
;
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")));
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());
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
;
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();
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
))
715 SoundOutputDevice c2
= (SoundOutputDevice
)c
;
716 int channels
= c2
.requestedSoundChannels();
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...");
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()
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
);
763 for (HardwareComponent part
: parts
) {
764 output
.println("\tparts[" + i
+ "] <object #" + output
.objectNumber(part
) + ">"); if(part
!= null) part
.dumpStatus(output
);
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
>();
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();
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))
816 output
.println("#" + output
.objectNumber(this) + ": PC:");
817 dumpStatusPartial(output
);
821 public PCHardwareInfo
getHardwareInfo()
826 public boolean getAndClearTripleFaulted()
828 boolean flag
= tripleFaulted
;
829 tripleFaulted
= false;
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
;
876 int paramsStart
= -1;
878 int stringLen
= moduleString
.length();
879 boolean requireNextSep
= false;
881 for(int i
= 0; true; i
++) {
884 cp
= moduleString
.codePointAt(i
);
885 else if(parenDepth
== 0)
886 cp
= ','; //Hack, consider last character seperator.
888 throw new IOException("Error in module string: unclosed '('.");
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.");
898 if(parenDepth
== 0) {
903 } else if(cp
== ')') {
905 throw new IOException("Error in module string: Unpaired ')'.");
906 else if(parenDepth
== 1) {
908 requireNextSep
= true;
911 } else if(cp
== ',' && parenDepth
== 0) {
914 currentModule
= moduleString
.substring(0, i
);
916 moduleString
= moduleString
.substring(i
+ 1);
917 if(moduleString
.equals(""))
918 throw new IOException("Error in module string: Blank module name not allowed.");
925 String name
= currentModule
.substring(0, nameEnd
+ 1);
926 String params
= null;
928 params
= currentModule
.substring(paramsStart
, paramsEnd
+ 1);
930 if(ret
.containsKey(name
))
931 ret
.get(name
).add(params
);
933 Set
<String
> foo
= new LinkedHashSet
<String
>();
943 private static GenericBlockDevice
blockdeviceFor(String name
) throws IOException
947 return new GenericBlockDevice(new DiskImage(name
, false));
950 public static PC
createPC(PCHardwareInfo hw
) throws IOException
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
));
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
;
1001 * Starts this PC's attached clock instance.
1009 * Stops this PC's attached clock instance
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");
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");
1068 usedDisks
.add(new Integer(diskIndex
));
1071 private void checkFloppyWP(int diskIndex
, boolean turnOn
) throws IOException
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
);
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
);
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");
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
)
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);
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()
1179 public void endEventCheck() throws IOException
1184 public DiskChanger(PC pc
)
1189 public DiskChanger(SRLoader input
) throws IOException
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))
1216 output
.println("#" + output
.objectNumber(this) + ": DiskChanger:");
1222 public DiskImageSet
getDisks()
1227 private boolean configure() {
1228 boolean fullyInitialised
;
1231 fullyInitialised
= true;
1232 for (HardwareComponent outer
: parts
) {
1233 if (outer
.initialised()) {
1237 for (HardwareComponent inner
: parts
) {
1238 outer
.acceptComponent(inner
);
1241 fullyInitialised
&= outer
.initialised();
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.");
1256 for (HardwareComponent hwc
: parts
) {
1257 if (hwc
instanceof PCIBus
) {
1258 ((PCIBus
) hwc
).biosInit();
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.
1280 * This is roughly equivalent to a hard-reset (power down-up cycle).
1282 protected void reset() {
1283 for (HardwareComponent hwc
: parts
) {
1289 public void 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()
1304 public void reboot()
1307 eRecorder
.addEvent(-1, getClass(), null);
1308 } catch(Exception e
) {}
1311 public void startEventCheck()
1316 public void doEvent(long timeStamp
, String
[] args
, int level
) throws IOException
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
)
1335 public ResetButton(SRLoader input
) throws IOException
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))
1362 output
.println("#" + output
.objectNumber(this) + ": ResetButton:");
1368 * Get an subclass of <code>cls</code> from this instance's parts list.
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
)) {
1380 for (HardwareComponent hwc
: parts
) {
1381 if (cls
.isInstance(hwc
)) {
1388 public Set
<HardwareComponent
> allComponents()
1394 * Gets the processor instance associated with this PC.
1395 * @return associated processor instance.
1397 public Processor
getProcessor() {
1402 * Execute an arbitrarily large amount of code on this instance.
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();
1415 return executeProtected();
1418 return executeReal();
1422 public final int executeReal()
1428 rebootRequest
= false;
1433 for (int i
= 0; i
< 100; i
++)
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;
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;
1455 rebootRequest
= false;
1459 } catch (ProcessorException p
) {
1460 processor
.handleRealModeException(p
);
1462 catch (ModeSwitchException e
)
1464 //System.err.println("Informational: CPU switching modes: " + e.toString());
1469 public TraceTrap
getTraceTrap()
1474 public boolean getHitTraceTrap()
1476 boolean tmp
= hitTraceTrap
;
1477 hitTraceTrap
= false;
1481 public final int executeProtected() {
1486 rebootRequest
= false;
1491 for (int i
= 0; i
< 100; i
++)
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;
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;
1513 rebootRequest
= false;
1517 } catch (ProcessorException p
) {
1518 processor
.handleProtectedModeException(p
);
1520 catch (ModeSwitchException e
)
1522 //System.err.println("Informational: CPU switching modes: " + e.toString());
1527 public final int executeVirtual8086() {
1532 rebootRequest
= false;
1537 for (int i
= 0; i
< 100; i
++)
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;
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;
1559 rebootRequest
= false;
1564 catch (ProcessorException p
)
1566 processor
.handleVirtual8086ModeException(p
);
1568 catch (ModeSwitchException e
)
1570 //System.err.println("Informational: CPU switching modes: " + e.toString());
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!).");
1594 ImageMaker
.ParsedImage pimg
= new ImageMaker
.ParsedImage(fileName
);
1595 RandomAccessFile image
= new RandomAccessFile(fileName
, "r");
1596 switch(pimg
.typeCode
) {
1598 lines
.encodeLine("TYPE", "FLOPPY");
1601 lines
.encodeLine("TYPE", "HDD");
1604 lines
.encodeLine("TYPE", "CDROM");
1607 lines
.encodeLine("TYPE", "BIOS");
1610 lines
.encodeLine("TYPE", "UNKNOWN");
1613 lines
.encodeLine("ID", new ImageLibrary
.ByteArray(pimg
.diskID
));
1614 switch(pimg
.typeCode
) {
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.");
1635 lines
.encodeLine("IMAGEMD5", new ImageLibrary
.ByteArray(md
.digest()));
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
1658 saveDiskInfo(writer
, image
.getImageID(), saved
);
1661 private static void saveDiskInfo(JRSRArchiveWriter writer
, byte[] diskID
, Set
<ImageLibrary
.ByteArray
> saved
) throws IOException
1665 ImageLibrary
.ByteArray id
= new ImageLibrary
.ByteArray(diskID
);
1666 if(saved
.contains(id
))
1669 UTFOutputLineStream lines
= new UTFOutputLineStream(writer
.addMember("diskinfo-" + arrayToString(diskID
)));
1670 saveDiskInfo(lines
, diskID
);
1674 public static void saveSavestate(JRSRArchiveWriter writer
, PCFullStatus fullStatus
, boolean movie
)
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
);
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
);
1693 lines
= new UTFOutputLineStream(writer
.addMember("initialization"));
1694 fullStatus
.pc
.getHardwareInfo().makeHWInfoSegment(lines
, fullStatus
.pc
.diskChanger
);
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
);
1706 OutputStream entry2
= writer
.addMember("manifest");
1707 dumper
.writeConstructorManifest(entry2
);
1710 lines
= new UTFOutputLineStream(writer
.addMember("events"));
1711 fullStatus
.events
.saveEvents(lines
);
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
)
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
);
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
);
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");
1763 if(fullStatus
.extraHeaders
== null) {
1764 fullStatus
.extraHeaders
= new String
[1][];
1765 fullStatus
.extraHeaders
[0] = components
;
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");
1787 entry
= new FourToFiveDecoder(reader
.readMember("savestate"));
1788 SRLoader loader
= new SRLoader(new InflaterInputStream(entry
));
1789 fullStatus
.pc
= (PC
)(loader
.loadObject());
1792 lines
= new UTFInputLineStream(reader
.readMember("initialization"));
1793 PC
.PCHardwareInfo hwInfo
= PC
.PCHardwareInfo
.parseHWInfoSegment(lines
);
1794 fullStatus
.pc
= createPC(hwInfo
);
1798 fullStatus
.events
= reuse
;
1800 lines
= new UTFInputLineStream(reader
.readMember("events"));
1801 fullStatus
.events
= new EventRecorder(lines
);
1803 fullStatus
.events
.attach(fullStatus
.pc
, forceMovie ?
null : fullStatus
.savestateID
);