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