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