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
.plugins
;
32 import java
.awt
.Dimension
;
35 import java
.security
.AccessControlException
;
38 import org
.jpc
.emulator
.HardwareComponent
;
39 import org
.jpc
.emulator
.PC
;
40 import org
.jpc
.emulator
.EventRecorder
;
41 import org
.jpc
.emulator
.TraceTrap
;
42 import org
.jpc
.emulator
.memory
.PhysicalAddressSpace
;
43 import org
.jpc
.emulator
.StatusDumper
;
44 import org
.jpc
.emulator
.Clock
;
45 import org
.jpc
.diskimages
.BlockDevice
;
46 import org
.jpc
.diskimages
.DiskImageSet
;
47 import org
.jpc
.diskimages
.DiskImage
;
48 import org
.jpc
.pluginsaux
.PleaseWait
;
49 import org
.jpc
.pluginsaux
.AsyncGUITask
;
50 import org
.jpc
.pluginsaux
.NewDiskDialog
;
51 import org
.jpc
.pluginsaux
.AuthorsDialog
;
52 import org
.jpc
.pluginsaux
.PCConfigDialog
;
53 import org
.jpc
.pluginsaux
.DumpControlDialog
;
54 import org
.jpc
.pluginsaux
.MenuManager
;
55 import org
.jpc
.pluginsaux
.ImportDiskImage
;
56 import org
.jpc
.pluginsbase
.*;
57 import org
.jpc
.jrsr
.*;
59 import static org
.jpc
.Misc
.randomHexes
;
60 import static org
.jpc
.Misc
.errorDialog
;
61 import static org
.jpc
.Misc
.callShowOptionDialog
;
62 import static org
.jpc
.Misc
.moveWindow
;
63 import static org
.jpc
.Misc
.parseStringToComponents
;
64 import static org
.jpc
.Misc
.nextParseLine
;
65 import static org
.jpc
.Misc
.renameFile
;
67 public class PCControl
extends JFrame
implements Plugin
69 private static long PROFILE_ALWAYS
= 0;
70 private static long PROFILE_NO_PC
= 1;
71 private static long PROFILE_HAVE_PC
= 2;
72 private static long PROFILE_STOPPED
= 4;
73 private static long PROFILE_RUNNING
= 8;
74 private static long PROFILE_EVENTS
= 16;
75 private static long PROFILE_CDROM
= 32;
77 private static final long serialVersionUID
= 8;
78 private Plugins vPluginManager
;
80 private JFileChooser snapshotFileChooser
;
82 private Set
<String
> disks
;
86 private int trapFlags
;
88 private volatile boolean running
;
89 private volatile boolean waiting
;
90 private boolean uncompressedSave
;
91 private boolean willCleanup
;
92 private static final long[] stopTime
;
93 private static final String
[] stopLabel
;
94 private volatile long imminentTrapTime
;
95 private boolean shuttingDown
;
96 private int nativeWidth
;
97 private int nativeHeight
;
98 private PCConfigDialog configDialog
;
99 private DumpControlDialog dumpDialog
;
100 private MenuManager menuManager
;
101 private volatile boolean restoreFocus
;
103 private PC
.PCFullStatus currentProject
;
107 stopTime
= new long[] {-1, 0, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000,
108 5000000, 10000000, 20000000, 50000000, 100000000, 200000000, 500000000, 1000000000, 2000000000,
109 5000000000L, 10000000000L, 20000000000L, 50000000000L};
110 stopLabel
= new String
[] {"(unbounded)", "(singlestep)", "1µs", "2µs", "5µs", "10µs", "20µs", "50µs", "100µs",
111 "200µs", "500µs","1ms", "2ms", "5ms", "10ms", "20ms", "50ms", "100ms", "200ms", "500ms", "1s", "2s", "5s",
112 "10s", "20s", "50s"};
115 public boolean systemShutdown()
117 if(!running
|| pc
== null)
119 //We are running. Do the absolute minimum since we are running in very delicate context.
125 public void reconnect(PC pc
)
127 pcStopping(); //Do the equivalent effects.
128 dumpDialog
.clearDumps();
132 private void setTrapFlags()
134 pc
.getTraceTrap().setTrapFlags(trapFlags
);
137 public void pcStarting()
139 long profile
= PROFILE_HAVE_PC
| PROFILE_RUNNING
;
140 if(currentProject
!= null && currentProject
.events
!= null);
141 profile
|= PROFILE_EVENTS
;
142 if(pc
.getCDROMIndex() >= 0)
143 profile
|= PROFILE_CDROM
;
145 menuManager
.setProfile(profile
);
152 Clock sysClock
= (Clock
)pc
.getComponent(Clock
.class);
153 long current
= sysClock
.getTime();
154 if(imminentTrapTime
> 0) {
155 pc
.getTraceTrap().setTrapTime(current
+ imminentTrapTime
);
156 } else if(imminentTrapTime
== 0) {
157 //Hack: We set trace trap to trap immediately. It comes too late to abort next instruction, but
158 //early enough to abort one after that.
159 pc
.getTraceTrap().setTrapTime(current
);
161 if(currentProject
.events
!= null)
162 currentProject
.events
.setPCRunStatus(true);
165 public void pcStopping()
167 if(currentProject
.events
!= null)
168 currentProject
.events
.setPCRunStatus(false);
170 return; //Don't mess with UI when shutting down.
173 long profile
= PROFILE_STOPPED
;
175 profile
|= PROFILE_HAVE_PC
;
177 profile
|= PROFILE_NO_PC
;
178 if(currentProject
!= null && currentProject
.events
!= null);
179 profile
|= PROFILE_EVENTS
;
180 if(pc
.getCDROMIndex() >= 0)
181 profile
|= PROFILE_CDROM
;
183 menuManager
.setProfile(profile
);
187 } catch(Exception e
) {
188 errorDialog(e
, "Failed to update disk menus", null, "Dismiss");
192 pc
.getTraceTrap().clearTrapTime();
193 pc
.getTraceTrap().getAndClearTrapActive();
197 private String
diskNameByIdx(int idx
)
199 return pc
.getDisks().lookupDisk(idx
).getName();
202 private void updateDisks() throws Exception
204 for(String x
: disks
)
205 menuManager
.removeMenuItem(x
);
212 DiskImageSet imageSet
= pc
.getDisks();
213 int[] floppies
= imageSet
.diskIndicesByType(BlockDevice
.Type
.FLOPPY
);
214 int[] cdroms
= imageSet
.diskIndicesByType(BlockDevice
.Type
.CDROM
);
216 for(int i
= 0; i
< floppies
.length
; i
++) {
217 String name
= diskNameByIdx(floppies
[i
]);
218 menuManager
.addMenuItem("Drives→fda→" + name
, this, "menuChangeDisk", new Object
[]{new Integer(0),
219 new Integer(floppies
[i
])}, PROFILE_HAVE_PC
);
220 menuManager
.addMenuItem("Drives→fdb→" + name
, this, "menuChangeDisk", new Object
[]{new Integer(1),
221 new Integer(floppies
[i
])}, PROFILE_HAVE_PC
);
222 menuManager
.addSelectableMenuItem("Drives→Write Protect→" + name
, this, "menuWriteProtect",
223 new Object
[]{new Integer(floppies
[i
])}, imageSet
.lookupDisk(floppies
[i
]).isReadOnly(),
225 disks
.add("Drives→fda→" + name
);
226 disks
.add("Drives→fdb→" + name
);
227 disks
.add("Drives→Write Protect→" + name
);
230 for(int i
= 0; i
< cdroms
.length
; i
++) {
231 String name
= diskNameByIdx(cdroms
[i
]);
232 menuManager
.addMenuItem("Drives→CD-ROM→" + name
, this, "menuChangeDisk", new Object
[]{new Integer(1),
233 new Integer(cdroms
[i
])}, PROFILE_HAVE_PC
| PROFILE_CDROM
);
234 disks
.add("Drives→CD-ROM→" + name
);
240 boolean wasRunning
= false;
241 while(true) { //We will be killed by JVM.
242 //Wait for us to become runnable again.
243 while(!running
|| pc
== null) {
244 if(wasRunning
&& pc
!= null)
246 wasRunning
= running
;
254 } catch(Exception e
) {
260 wasRunning
= running
;
264 if(pc
.getHitTraceTrap()) {
265 if(pc
.getAndClearTripleFaulted())
266 callShowOptionDialog(this, "CPU shut itself down due to triple fault. Rebooting the system.", "Triple fault!", JOptionPane
.YES_NO_OPTION
, JOptionPane
.WARNING_MESSAGE
, null, new String
[]{"Dismiss"}, "Dismiss");
268 SwingUtilities
.invokeAndWait(new Thread() { public void run() { stopNoWait(); }});
271 } catch (Exception e
) {
272 errorDialog(e
, "Hardware emulator internal error", this, "Dismiss");
274 SwingUtilities
.invokeAndWait(new Thread() { public void run() { stopNoWait(); }});
275 } catch (Exception f
) {
282 public void connectPC(PC pc
)
284 currentProject
.pc
= pc
;
285 vPluginManager
.reconnect(pc
);
289 private void startExternal()
291 if(pc
!= null && !running
)
292 if(!SwingUtilities
.isEventDispatchThread())
294 SwingUtilities
.invokeAndWait(new Thread() { public void run() { PCControl
.this.start(); }});
295 } catch(Exception e
) {
301 private void stopExternal()
303 if(pc
!= null && running
)
304 if(!SwingUtilities
.isEventDispatchThread())
306 SwingUtilities
.invokeAndWait(new Thread() { public void run() { PCControl
.this.stop(); }});
307 } catch(Exception e
) {
313 public boolean eci_state_save(String filename
)
316 (new Thread(new SaveStateTask(filename
, false))).start();
320 public boolean eci_state_dump(String filename
)
323 (new Thread(new StatusDumpTask(filename
))).start();
327 public boolean eci_movie_save(String filename
)
330 (new Thread(new SaveStateTask(filename
, true))).start();
334 public boolean eci_state_load(String filename
)
337 (new Thread(new LoadStateTask(filename
, LoadStateTask
.MODE_NORMAL
))).start();
341 public boolean eci_state_load_noevents(String filename
)
344 (new Thread(new LoadStateTask(filename
, LoadStateTask
.MODE_PRESERVE
))).start();
348 public boolean eci_movie_load(String filename
)
351 (new Thread(new LoadStateTask(filename
, LoadStateTask
.MODE_MOVIEONLY
))).start();
355 public boolean eci_pc_assemble()
358 (new Thread(new AssembleTask())).start();
362 public boolean eci_ram_dump_text(String filename
)
365 (new Thread(new RAMDumpTask(filename
, false))).start();
369 public boolean eci_ram_dump_binary(String filename
)
372 (new Thread(new RAMDumpTask(filename
, true))).start();
376 public void eci_trap_vretrace_start_on()
378 trapFlags
|= TraceTrap
.TRACE_STOP_VRETRACE_START
;
381 public void eci_trap_vretrace_start_off()
383 trapFlags
&= ~TraceTrap
.TRACE_STOP_VRETRACE_START
;
386 public void eci_trap_vretrace_end_on()
388 trapFlags
|= TraceTrap
.TRACE_STOP_VRETRACE_END
;
391 public void eci_trap_vretrace_end_off()
393 trapFlags
&= ~TraceTrap
.TRACE_STOP_VRETRACE_END
;
396 public void eci_trap_timed_disable()
398 this.imminentTrapTime
= -1;
401 public void eci_trap_timed(Long time
)
403 this.imminentTrapTime
= time
.longValue();
406 public void eci_pc_start()
411 public void eci_pc_stop()
416 public void eci_pccontrol_setwinpos(Integer x
, Integer y
)
418 moveWindow(this, x
.intValue(), y
.intValue(), nativeWidth
, nativeHeight
);
421 public void eci_sendevent(String clazz
, String
[] rargs
)
423 System
.err
.println("Event to: '" + clazz
+ "':");
424 for(int i
= 0; i
< rargs
.length
; i
++) {
425 System
.err
.println("rargs[" + i
+ "]: '" + rargs
[i
] + "'.");
427 if(currentProject
.events
!= null) {
429 Class
<?
extends HardwareComponent
> x
= Class
.forName(clazz
).asSubclass(HardwareComponent
.class);
430 currentProject
.events
.addEvent(0L, x
, rargs
);
431 } catch(Exception e
) {
432 System
.err
.println("Error adding event: " + e
.getMessage());
437 public void eci_memory_read(Long address
, Integer size
)
439 if(currentProject
.pc
!= null) {
440 long addr
= address
.longValue();
441 long _size
= size
.intValue();
443 PhysicalAddressSpace addrSpace
;
444 if(addr
< 0 || addr
> 0xFFFFFFFFL
|| (_size
!= 1 && _size
!= 2 && _size
!= 4))
447 addrSpace
= (PhysicalAddressSpace
)currentProject
.pc
.getComponent(PhysicalAddressSpace
.class);
449 ret
= (long)addrSpace
.getByte((int)addr
) & 0xFF;
451 ret
= (long)addrSpace
.getWord((int)addr
) & 0xFFFF;
453 ret
= (long)addrSpace
.getDoubleWord((int)addr
) & 0xFFFFFFFFL
;
455 vPluginManager
.returnValue(ret
);
459 public void eci_memory_write(Long address
, Long value
, Integer size
)
461 if(currentProject
.pc
!= null) {
462 long addr
= address
.longValue();
463 long _size
= size
.intValue();
464 long _value
= value
.longValue();
465 PhysicalAddressSpace addrSpace
;
466 if(addr
< 0 || addr
> 0xFFFFFFFFL
|| (_size
!= 1 && _size
!= 2 && _size
!= 4))
469 addrSpace
= (PhysicalAddressSpace
)currentProject
.pc
.getComponent(PhysicalAddressSpace
.class);
471 addrSpace
.setByte((int)addr
, (byte)_value
);
473 addrSpace
.setWord((int)addr
, (short)_value
);
475 addrSpace
.setDoubleWord((int)addr
, (int)_value
);
479 public PCControl(Plugins manager
, String args
) throws Exception
483 UTFInputLineStream file
= null;
484 Map
<String
, String
> params
= parseStringToComponents(args
);
485 Set
<String
> used
= new HashSet
<String
>();
486 String extramenu
= params
.get("extramenu");
487 String uncompress
= params
.get("uncompressedsave");
488 if(uncompress
!= null)
489 uncompressedSave
= true;
490 if(extramenu
== null)
493 file
= new UTFInputLineStream(new FileInputStream(extramenu
));
496 String
[] line
= nextParseLine(file
);
499 if(line
.length
< 3 || line
[0].charAt(0) == '→') {
500 System
.err
.println("Warning: Bad extra menu item '" + line
[0] + "'.");
503 if(line
[0].length() == 0 || line
[0].charAt(line
[0].length() - 1) == '→') {
504 System
.err
.println("Warning: Bad extra menu item '" + line
[0] + "'.");
507 if(line
[0].indexOf("→→") >= 0) {
508 System
.err
.println("Warning: Bad extra menu item '" + line
[0] + "'.");
511 if(used
.contains(line
[0])) {
512 System
.err
.println("Warning: Duplicate extra menu item '" + line
[0] + "'.");
515 KeyStroke stroke
= null;
516 if(!line
[1].equals("<>")) {
517 stroke
= KeyStroke
.getKeyStroke(line
[1]);
519 System
.err
.println("Warning: Bad keystroke '" + line
[1] + "'.");
524 String
[] lineCommand
= Arrays
.copyOfRange(line
, 2, line
.length
);
526 menuManager
.addMenuItem("Extra→" + line
[0], this, "menuExtra", lineCommand
, PROFILE_ALWAYS
, stroke
);
529 } catch(IOException e
) {
530 errorDialog(e
, "Failed to load extra menu defintions", null, "dismiss");
534 setJMenuBar(menuManager
.getMainBar());
537 public PCControl(Plugins manager
) throws Exception
541 if(DiskImage
.getLibrary() == null)
542 throw new Exception("PCControl plugin requires disk library");
545 this.willCleanup
= false;
546 shuttingDown
= false;
547 configDialog
= new PCConfigDialog();
548 dumpDialog
= new DumpControlDialog(manager
);
550 menuManager
= new MenuManager();
552 menuManager
.setProfile(PROFILE_NO_PC
| PROFILE_STOPPED
);
554 menuManager
.addMenuItem("System→Assemble", this, "menuAssemble", null, PROFILE_STOPPED
);
555 menuManager
.addMenuItem("System→Start", this, "menuStart", null, PROFILE_STOPPED
| PROFILE_HAVE_PC
);
556 menuManager
.addMenuItem("System→Stop", this, "menuStop", null, PROFILE_RUNNING
);
557 menuManager
.addMenuItem("System→Reset", this, "menuReset", null, PROFILE_HAVE_PC
);
558 menuManager
.addMenuItem("System→Dumping control", this, "menuDump", null, PROFILE_HAVE_PC
| PROFILE_STOPPED
);
559 menuManager
.addMenuItem("System→Quit", this, "menuQuit", null, PROFILE_ALWAYS
);
560 menuManager
.addSelectableMenuItem("Breakpoints→Trap VRetrace Start", this, "menuVRetraceStart", null, false,
562 menuManager
.addSelectableMenuItem("Breakpoints→Trap VRetrace End", this, "menuVRetraceEnd", null, false,
564 menuManager
.addMenuItem("Snapshot→Change Run Authors", this, "menuChangeAuthors", null, PROFILE_HAVE_PC
);
565 menuManager
.addMenuItem("Snapshot→Save→Snapshot", this, "menuSave", new Object
[]{new Boolean(false)},
566 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
567 menuManager
.addMenuItem("Snapshot→Save→Movie", this, "menuSave", new Object
[]{new Boolean(true)},
568 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
569 menuManager
.addMenuItem("Snapshot→Save→Status Dump", this, "menuStatusDump", null,
570 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
571 menuManager
.addMenuItem("Snapshot→Load→Snapshot", this, "menuLoad",
572 new Object
[]{new Integer(LoadStateTask
.MODE_NORMAL
)}, PROFILE_STOPPED
);
573 menuManager
.addMenuItem("Snapshot→Load→Snapshot (preserve events)", this, "menuLoad",
574 new Object
[]{new Integer(LoadStateTask
.MODE_PRESERVE
)}, PROFILE_STOPPED
| PROFILE_EVENTS
);
575 menuManager
.addMenuItem("Snapshot→Load→Movie", this, "menuLoad",
576 new Object
[]{new Integer(LoadStateTask
.MODE_MOVIEONLY
)}, PROFILE_STOPPED
);
577 menuManager
.addMenuItem("Snapshot→RAM Dump→Hexadecimal", this, "menuRAMDump", new Object
[]{new Boolean(false)},
578 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
579 menuManager
.addMenuItem("Snapshot→RAM Dump→Binary", this, "menuRAMDump", new Object
[]{new Boolean(true)},
580 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
581 menuManager
.addMenuItem("Snapshot→Truncate Event Stream", this, "menuTruncate", null,
582 PROFILE_STOPPED
| PROFILE_EVENTS
);
584 for(int i
= 0; i
< stopLabel
.length
; i
++) {
585 menuManager
.addSelectableMenuItem("Breakpoints→Timed Stops→" + stopLabel
[i
], this, "menuTimedStop",
586 null, (i
== 0), PROFILE_ALWAYS
);
588 imminentTrapTime
= -1;
590 menuManager
.addMenuItem("Drives→fda→<Empty>", this, "menuChangeDisk", new Object
[]{new Integer(0),
591 new Integer(-1)}, PROFILE_HAVE_PC
);
592 menuManager
.addMenuItem("Drives→fdb→<Empty>", this, "menuChangeDisk", new Object
[]{new Integer(1),
593 new Integer(-1)}, PROFILE_HAVE_PC
);
594 menuManager
.addMenuItem("Drives→CD-ROM→<Empty>", this, "menuChangeDisk", new Object
[]{new Integer(2),
595 new Integer(-1)}, PROFILE_HAVE_PC
| PROFILE_CDROM
);
596 menuManager
.addMenuItem("Drives→Add image", this, "menuAddDisk", null, PROFILE_HAVE_PC
);
597 menuManager
.addMenuItem("Drives→Import Image", this, "menuImport", null, PROFILE_ALWAYS
);
599 menuManager
.addMenuItem("Debug→Hacks→NO_FPU", this, "menuNOFPU", null, PROFILE_HAVE_PC
);
600 menuManager
.addMenuItem("Debug→Hacks→VGA_DRAW", this, "menuVGADRAW", null, PROFILE_HAVE_PC
);
602 disks
= new HashSet
<String
>();
603 currentProject
= new PC
.PCFullStatus();
605 this.vPluginManager
= manager
;
607 setJMenuBar(menuManager
.getMainBar());
611 setDefaultCloseOperation(JFrame
.EXIT_ON_CLOSE
);
613 catch (AccessControlException e
)
615 System
.err
.println("Error: Not able to add some components to frame: " + e
.getMessage());
618 snapshotFileChooser
= new JFileChooser(System
.getProperty("user.dir"));
620 getContentPane().validate();
623 Dimension d
= getSize();
624 nativeWidth
= d
.width
;
625 nativeHeight
= d
.height
;
629 public void menuExtra(String i
, Object
[] args
)
631 if(args
.length
== 1) {
632 vPluginManager
.invokeExternalCommand((String
)args
[0], null);
634 String
[] rest
= Arrays
.copyOfRange(args
, 1, args
.length
, String
[].class);
635 vPluginManager
.invokeExternalCommand((String
)args
[0], rest
);
639 public void menuAssemble(String i
, Object
[] args
)
641 (new Thread(new AssembleTask())).start();
644 public void menuStart(String i
, Object
[] args
)
649 public void menuStop(String i
, Object
[] args
)
654 public void menuReset(String i
, Object
[] args
)
659 public void menuDump(String i
, Object
[] args
)
661 (new Thread(new DumpControlTask())).start();
664 public void menuImport(String i
, Object
[] args
)
667 new ImportDiskImage();
668 } catch(Exception e
) {
673 public void menuNOFPU(String i
, Object
[] args
)
678 public void menuVGADRAW(String i
, Object
[] args
)
683 public void menuQuit(String i
, Object
[] args
)
685 vPluginManager
.shutdownEmulator();
688 public void menuVRetraceStart(String i
, Object
[] args
)
690 trapFlags ^
= TraceTrap
.TRACE_STOP_VRETRACE_START
;
691 menuManager
.setSelected("Breakpoints→Trap VRetrace Start",
692 (trapFlags
& TraceTrap
.TRACE_STOP_VRETRACE_START
) == TraceTrap
.TRACE_STOP_VRETRACE_START
);
695 public void menuVRetraceEnd(String i
, Object
[] args
)
697 trapFlags ^
= TraceTrap
.TRACE_STOP_VRETRACE_END
;
698 menuManager
.setSelected("Breakpoints→Trap VRetrace End",
699 (trapFlags
& TraceTrap
.TRACE_STOP_VRETRACE_END
) == TraceTrap
.TRACE_STOP_VRETRACE_END
);
702 public void menuTimedStop(String i
, Object
[] args
)
704 for(int j
= 0; j
< stopLabel
.length
; j
++) {
705 String label
= "Breakpoints→Timed Stops→" + stopLabel
[j
];
706 if(i
.equals(label
)) {
707 this.imminentTrapTime
= stopTime
[j
];
708 menuManager
.select(label
);
710 menuManager
.unselect(label
);
714 public void menuSave(String i
, Object
[] args
)
717 (new Thread(new SaveStateTask(((Boolean
)args
[0]).booleanValue()))).start();
720 public void menuStatusDump(String i
, Object
[] args
)
723 (new Thread(new StatusDumpTask())).start();
726 public void menuLoad(String i
, Object
[] args
)
729 (new Thread(new LoadStateTask(((Integer
)args
[0]).intValue()))).start();
732 public void menuRAMDump(String i
, Object
[] args
)
735 (new Thread(new RAMDumpTask(((Boolean
)args
[0]).booleanValue()))).start();
738 public void menuTruncate(String i
, Object
[] args
)
740 currentProject
.events
.truncateEventStream();
743 public void menuChangeDisk(String i
, Object
[] args
)
745 changeFloppy(((Integer
)args
[0]).intValue(), ((Integer
)args
[1]).intValue());
748 public void menuWriteProtect(String i
, Object
[] args
)
750 int disk
= ((Integer
)args
[0]).intValue();
751 writeProtect(disk
, menuManager
.isSelected(i
));
752 DiskImageSet imageSet
= pc
.getDisks();
753 menuManager
.setSelected(i
, imageSet
.lookupDisk(disk
).isReadOnly());
756 public void menuAddDisk(String i
, Object
[] args
)
759 (new Thread(new AddDiskTask())).start();
762 public void menuChangeAuthors(String i
, Object
[] args
)
765 (new Thread(new ChangeAuthorsTask())).start();
768 public synchronized void start()
770 vPluginManager
.pcStarted();
775 private String
prettyPrintTime(long ts
)
780 s
= s
+ "" + (ts
/ 1000000000) + " ";
782 s
= s
+ "" + (ts
% 1000000000 / 100000000);
784 s
= s
+ "" + (ts
% 100000000 / 10000000);
786 s
= s
+ "" + (ts
% 10000000 / 1000000) + " ";
788 s
= s
+ "" + (ts
% 1000000 / 100000);
790 s
= s
+ "" + (ts
% 100000 / 10000);
792 s
= s
+ "" + (ts
% 10000 / 1000) + " ";
794 s
= s
+ "" + (ts
% 1000 / 100);
796 s
= s
+ "" + (ts
% 100 / 10);
797 s
= s
+ "" + (ts
% 10);
801 protected synchronized void stopNoWait()
804 vPluginManager
.pcStopped();
805 Clock sysClock
= (Clock
)pc
.getComponent(Clock
.class);
806 System
.err
.println("Notice: PC emulation stopped (at time sequence value " +
807 prettyPrintTime(sysClock
.getTime()) + ")");
810 public synchronized void stop()
813 pc
.getTraceTrap().doPotentialTrap(TraceTrap
.TRACE_STOP_IMMEDIATE
);
815 System
.err
.println("Informational: Waiting for PC to halt...");
819 } catch(Exception e
) {
825 public JScrollPane
getMonitorPane()
830 protected void reset()
835 public synchronized boolean isRunning()
840 private void changeFloppy(int drive
, int image
)
844 PC
.DiskChanger changer
= (PC
.DiskChanger
)pc
.getComponent(PC
.DiskChanger
.class);
845 changer
.changeFloppyDisk(drive
, image
);
846 } catch (Exception e
) {
847 System
.err
.println("Error: Failed to change disk");
848 errorDialog(e
, "Failed to change disk", null, "Dismiss");
852 private void writeProtect(int image
, boolean state
)
856 PC
.DiskChanger changer
= (PC
.DiskChanger
)pc
.getComponent(PC
.DiskChanger
.class);
857 changer
.wpFloppyDisk(image
, state
);
858 } catch (Exception e
) {
859 System
.err
.println("Error: Failed to change floppy write protect");
860 errorDialog(e
, "Failed to write (un)protect floppy", null, "Dismiss");
864 private class LoadStateTask
extends AsyncGUITask
871 private static final int MODE_NORMAL
= 1;
872 private static final int MODE_PRESERVE
= 2;
873 private static final int MODE_MOVIEONLY
= 3;
875 public LoadStateTask(int mode
)
877 oTime
= System
.currentTimeMillis();
880 pw
= new PleaseWait("Loading savestate...");
883 public LoadStateTask(String name
, int mode
)
886 choosen
= new File(name
);
889 protected void runPrepare()
891 PCControl
.this.setEnabled(false);
892 if(choosen
== null) {
894 if(_mode
== MODE_PRESERVE
)
895 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "LOAD JPC-RR Snapshot (PE)");
896 else if(_mode
== MODE_MOVIEONLY
)
897 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "LOAD JPC-RR Snapshot (MO)");
899 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "LOAD JPC-RR Snapshot");
900 choosen
= snapshotFileChooser
.getSelectedFile();
908 protected void runFinish()
912 connectPC(pc
= currentProject
.pc
);
913 System
.err
.println("Informational: Loadstate done");
914 } catch(Exception e
) {
920 errorDialog(caught
, "Load savestate failed", PCControl
.this, "Dismiss");
922 PCControl
.this.setEnabled(true);
924 PCControl
.this.requestFocus(true);
925 restoreFocus
= false;
926 System
.err
.println("Total save time: " + (System
.currentTimeMillis() - oTime
) + "ms.");
927 PCControl
.this.vPluginManager
.signalCommandCompletion();
930 protected void runTask()
936 System
.err
.println("Informational: Loading a snapshot of JPC-RR");
937 long times1
= System
.currentTimeMillis();
938 JRSRArchiveReader reader
= new JRSRArchiveReader(choosen
.getAbsolutePath());
940 PC
.PCFullStatus fullStatus
= PC
.loadSavestate(reader
, (_mode
== MODE_PRESERVE
) ?
941 currentProject
.events
: null, (_mode
== MODE_MOVIEONLY
));
942 if(currentProject
.projectID
!= null && fullStatus
.projectID
.equals(currentProject
.projectID
))
943 if(currentProject
.rerecords
> fullStatus
.rerecords
)
944 fullStatus
.rerecords
= currentProject
.rerecords
+ 1;
946 fullStatus
.rerecords
++;
948 fullStatus
.rerecords
++;
950 currentProject
= fullStatus
;
953 long times2
= System
.currentTimeMillis();
954 System
.err
.println("Informational: Loadstate complete (" + (times2
- times1
) + "ms).");
955 } catch(Exception e
) {
961 private class SaveStateTask
extends AsyncGUITask
969 public SaveStateTask(boolean movie
)
971 oTime
= System
.currentTimeMillis();
974 pw
= new PleaseWait("Saving savestate...");
977 public SaveStateTask(String name
, boolean movie
)
980 choosen
= new File(name
);
983 protected void runPrepare()
985 PCControl
.this.setEnabled(false);
986 if(choosen
== null) {
987 int returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, movieOnly ?
"Save JPC-RR Movie" :
988 "Save JPC-RR Snapshot");
989 choosen
= snapshotFileChooser
.getSelectedFile();
997 protected void runFinish()
1000 if(caught
!= null) {
1001 errorDialog(caught
, "Saving savestate failed", PCControl
.this, "Dismiss");
1003 PCControl
.this.setEnabled(true);
1005 PCControl
.this.requestFocus(true);
1006 restoreFocus
= false;
1007 System
.err
.println("Total save time: " + (System
.currentTimeMillis() - oTime
) + "ms.");
1008 PCControl
.this.vPluginManager
.signalCommandCompletion();
1011 protected void runTask()
1016 JRSRArchiveWriter writer
= null;
1019 System
.err
.println("Informational: Savestating...");
1020 long times1
= System
.currentTimeMillis();
1021 writer
= new JRSRArchiveWriter(choosen
.getAbsolutePath());
1022 PC
.saveSavestate(writer
, currentProject
, movieOnly
, uncompressedSave
);
1023 renameFile(choosen
, new File(choosen
.getAbsolutePath() + ".backup"));
1025 long times2
= System
.currentTimeMillis();
1026 System
.err
.println("Informational: Savestate complete (" + (times2
- times1
) + "ms).");
1027 } catch(Exception e
) {
1029 try { writer
.rollback(); } catch(Exception f
) {}
1035 private class StatusDumpTask
extends AsyncGUITask
1041 public StatusDumpTask()
1044 pw
= new PleaseWait("Saving status dump...");
1047 public StatusDumpTask(String name
)
1050 choosen
= new File(name
);
1053 protected void runPrepare()
1055 PCControl
.this.setEnabled(false);
1056 if(choosen
== null) {
1057 int returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "Save Status dump");
1058 choosen
= snapshotFileChooser
.getSelectedFile();
1066 protected void runFinish()
1069 if(caught
!= null) {
1070 errorDialog(caught
, "Status dump failed", PCControl
.this, "Dismiss");
1072 PCControl
.this.setEnabled(true);
1074 PCControl
.this.requestFocus(true);
1075 restoreFocus
= false;
1076 PCControl
.this.vPluginManager
.signalCommandCompletion();
1079 protected void runTask()
1085 OutputStream outb
= new BufferedOutputStream(new FileOutputStream(choosen
));
1086 PrintStream out
= new PrintStream(outb
, false, "UTF-8");
1087 StatusDumper sd
= new StatusDumper(out
);
1091 System
.err
.println("Informational: Dumped " + sd
.dumpedObjects() + " objects");
1092 } catch(Exception e
) {
1098 private class RAMDumpTask
extends AsyncGUITask
1105 public RAMDumpTask(boolean binFlag
)
1108 pw
= new PleaseWait("Saving RAM dump...");
1112 public RAMDumpTask(String name
, boolean binFlag
)
1115 choosen
= new File(name
);
1118 protected void runPrepare()
1120 PCControl
.this.setEnabled(false);
1121 if(choosen
== null) {
1124 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "Save RAM dump");
1126 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "Save RAM hexdump");
1127 choosen
= snapshotFileChooser
.getSelectedFile();
1135 protected void runFinish()
1138 if(caught
!= null) {
1139 errorDialog(caught
, "RAM dump failed", PCControl
.this, "Dismiss");
1141 PCControl
.this.setEnabled(true);
1143 PCControl
.this.requestFocus(true);
1144 restoreFocus
= false;
1145 PCControl
.this.vPluginManager
.signalCommandCompletion();
1148 protected void runTask()
1154 OutputStream outb
= new BufferedOutputStream(new FileOutputStream(choosen
));
1155 byte[] pagebuf
= new byte[4096];
1156 PhysicalAddressSpace addr
= (PhysicalAddressSpace
)pc
.getComponent(PhysicalAddressSpace
.class);
1157 int lowBound
= addr
.findFirstRAMPage(0);
1158 int firstUndumped
= 0;
1161 while(lowBound
>= 0) {
1162 for(; firstUndumped
< lowBound
; firstUndumped
++)
1163 dumpPage(outb
, firstUndumped
, null);
1164 addr
.readRAMPage(firstUndumped
++, pagebuf
);
1165 dumpPage(outb
, lowBound
, pagebuf
);
1167 highBound
= lowBound
+ 1;
1168 lowBound
= addr
.findFirstRAMPage(++lowBound
);
1171 System
.err
.println("Informational: Dumped machine RAM (" + highBound
+ " pages examined, " +
1172 present
+ " pages present).");
1173 } catch(Exception e
) {
1178 private byte charForHex(int hvalue
)
1181 return (byte)(hvalue
+ 48);
1182 else if(hvalue
> 9 && hvalue
< 16)
1183 return (byte)(hvalue
+ 55);
1185 System
.err
.println("Unknown hex value: " + hvalue
+ ".");
1189 private void dumpPage(OutputStream stream
, int pageNo
, byte[] buffer
) throws IOException
1192 pageNo
= pageNo
& 0xFFFFF; //Cut page numbers out of range.
1193 if(!binary
&& buffer
== null)
1194 return; //Don't dump null pages in non-binary mode.
1196 pageBufSize
= 4096; //Binary page buffer is 4096 bytes.
1198 pageBufSize
= 14592; //Hexdump page buffer is 14592 bytes.
1199 byte[] outputPage
= new byte[pageBufSize
];
1200 if(buffer
!= null && binary
) {
1201 System
.arraycopy(buffer
, 0, outputPage
, 0, 4096);
1202 } else if(buffer
!= null) { //Hex mode
1203 for(int i
= 0; i
< 256; i
++) {
1204 for(int j
= 0; j
< 57; j
++) {
1206 outputPage
[57 * i
+ j
] = charForHex((pageNo
>>> (4 * (4 - j
))) & 0xF);
1208 outputPage
[57 * i
+ j
] = charForHex(i
/ 16);
1210 outputPage
[57 * i
+ j
] = charForHex(i
% 16);
1212 outputPage
[57 * i
+ j
] = 48;
1214 outputPage
[57 * i
+ j
] = 10;
1216 outputPage
[57 * i
+ j
] = 32;
1218 outputPage
[57 * i
+ j
] = charForHex(((int)buffer
[16 * i
+ j
/ 3 - 3] & 0xFF) / 16);
1220 outputPage
[57 * i
+ j
] = charForHex(buffer
[16 * i
+ j
/ 3 - 3] & 0xF);
1222 System
.err
.println("Error: dumpPage: unhandled j = " + j
+ ".");
1226 stream
.write(outputPage
);
1230 private class AssembleTask
extends AsyncGUITask
1236 public AssembleTask()
1238 pw
= new PleaseWait("Assembling PC...");
1242 protected void runPrepare()
1244 PCControl
.this.setEnabled(false);
1246 configDialog
.popUp();
1247 } catch(Exception e
) {
1252 protected void runFinish()
1254 if(caught
== null && !canceled
) {
1256 currentProject
.projectID
= randomHexes(24);
1257 currentProject
.rerecords
= 0;
1258 currentProject
.events
= new EventRecorder();
1259 currentProject
.events
.attach(pc
, null);
1260 currentProject
.savestateID
= null;
1261 currentProject
.extraHeaders
= null;
1263 } catch(Exception e
) {
1269 if(caught
!= null) {
1270 errorDialog(caught
, "PC Assembly failed", PCControl
.this, "Dismiss");
1272 PCControl
.this.setEnabled(true);
1274 PCControl
.this.requestFocus(true);
1275 restoreFocus
= false;
1276 PCControl
.this.vPluginManager
.signalCommandCompletion();
1279 protected void runTask()
1283 PC
.PCHardwareInfo hw
= configDialog
.waitClose();
1289 SwingUtilities
.invokeAndWait(new Thread() { public void run() { pw
.popUp(); }});
1290 } catch(Exception e
) {
1294 pc
= PC
.createPC(hw
);
1295 } catch(Exception e
) {
1301 private class DumpControlTask
extends AsyncGUITask
1306 public DumpControlTask()
1310 protected void runPrepare()
1312 PCControl
.this.setEnabled(false);
1314 dumpDialog
.popUp(PCControl
.this.pc
);
1315 } catch(Exception e
) {
1320 protected void runFinish()
1322 if(caught
!= null) {
1323 errorDialog(caught
, "Opening dump control dialog failed", PCControl
.this, "Dismiss");
1325 PCControl
.this.setEnabled(true);
1327 PCControl
.this.requestFocus(true);
1328 restoreFocus
= false;
1331 protected void runTask()
1335 dumpDialog
.waitClose();
1339 private class AddDiskTask
extends AsyncGUITask
1344 public AddDiskTask()
1346 dd
= new NewDiskDialog();
1347 PCControl
.this.setEnabled(false);
1350 protected void runPrepare()
1354 protected void runFinish()
1356 if(caught
!= null) {
1357 errorDialog(caught
, "Adding disk failed", PCControl
.this, "Dismiss");
1359 PCControl
.this.setEnabled(true);
1361 PCControl
.this.requestFocus(true);
1362 restoreFocus
= false;
1365 } catch(Exception e
) {
1366 errorDialog(e
, "Failed to update disk menus", null, "Dismiss");
1368 PCControl
.this.vPluginManager
.signalCommandCompletion();
1371 protected void runTask()
1373 NewDiskDialog
.Response res
= dd
.waitClose();
1379 pc
.getDisks().addDisk(img
= new DiskImage(res
.diskFile
, false));
1380 img
.setName(res
.diskName
);
1381 } catch(Exception e
) {
1387 private class ChangeAuthorsTask
extends AsyncGUITask
1392 public ChangeAuthorsTask()
1396 String
[] authorNames
= null;
1398 if(currentProject
!= null && currentProject
.extraHeaders
!= null) {
1399 headers
= currentProject
.extraHeaders
.length
;
1400 for(int i
= 0; i
< headers
; i
++)
1401 if(currentProject
.extraHeaders
[i
][0].equals("AUTHORS"))
1402 authors
+= (currentProject
.extraHeaders
[i
].length
- 1);
1406 authorNames
= new String
[authors
];
1407 for(int i
= 0; i
< headers
; i
++) {
1408 if(currentProject
.extraHeaders
[i
][0].equals("AUTHORS")) {
1409 System
.arraycopy(currentProject
.extraHeaders
[i
], 1, authorNames
, j
,
1410 currentProject
.extraHeaders
[i
].length
- 1);
1411 j
+= (currentProject
.extraHeaders
[i
].length
- 1);
1416 ad
= new AuthorsDialog(authorNames
);
1417 PCControl
.this.setEnabled(false);
1420 protected void runPrepare()
1424 protected void runFinish()
1426 if(caught
!= null) {
1427 errorDialog(caught
, "Changing authors failed", PCControl
.this, "Dismiss");
1429 PCControl
.this.setEnabled(true);
1431 PCControl
.this.requestFocus(true);
1432 restoreFocus
= false;
1433 PCControl
.this.vPluginManager
.signalCommandCompletion();
1436 protected void runTask()
1438 AuthorsDialog
.Response res
= ad
.waitClose();
1446 if(currentProject
!= null && currentProject
.extraHeaders
!= null) {
1447 headers
= currentProject
.extraHeaders
.length
;
1448 for(int i
= 0; i
< headers
; i
++)
1449 if(currentProject
.extraHeaders
[i
][0].equals("AUTHORS"))
1452 if(res
.authors
!= null) {
1453 for(int i
= 0; i
< res
.authors
.length
; i
++)
1454 if(res
.authors
[i
] != null)
1457 if(headers
== oldAuthors
&& newAuthors
== 0) {
1458 //Remove all extra headers.
1459 currentProject
.extraHeaders
= null;
1463 String
[][] newHeaders
= new String
[headers
+ newAuthors
- oldAuthors
][];
1466 //Copy the non-authors headers.
1467 if(currentProject
!= null && currentProject
.extraHeaders
!= null) {
1468 for(int i
= 0; i
< headers
; i
++)
1469 if(!currentProject
.extraHeaders
[i
][0].equals("AUTHORS"))
1470 newHeaders
[writePos
++] = currentProject
.extraHeaders
[i
];
1472 if(res
.authors
!= null)
1473 for(int i
= 0; i
< res
.authors
.length
; i
++)
1474 if(res
.authors
[i
] != null)
1475 newHeaders
[writePos
++] = new String
[]{"AUTHORS", res
.authors
[i
]};
1476 currentProject
.extraHeaders
= newHeaders
;
1477 } catch(Exception e
) {