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
;
102 private Map
<String
, List
<String
[]> > extraActions
;
104 private PC
.PCFullStatus currentProject
;
108 stopTime
= new long[] {-1, 0, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000,
109 5000000, 10000000, 20000000, 50000000, 100000000, 200000000, 500000000, 1000000000, 2000000000,
110 5000000000L, 10000000000L, 20000000000L, 50000000000L};
111 stopLabel
= new String
[] {"(unbounded)", "(singlestep)", "1µs", "2µs", "5µs", "10µs", "20µs", "50µs", "100µs",
112 "200µs", "500µs","1ms", "2ms", "5ms", "10ms", "20ms", "50ms", "100ms", "200ms", "500ms", "1s", "2s", "5s",
113 "10s", "20s", "50s"};
116 public boolean systemShutdown()
118 if(!running
|| pc
== null)
120 //We are running. Do the absolute minimum since we are running in very delicate context.
126 public void reconnect(PC pc
)
128 pcStopping(); //Do the equivalent effects.
129 dumpDialog
.clearDumps();
133 private void setTrapFlags()
135 pc
.getTraceTrap().setTrapFlags(trapFlags
);
138 public void pcStarting()
140 long profile
= PROFILE_HAVE_PC
| PROFILE_RUNNING
;
141 if(currentProject
!= null && currentProject
.events
!= null);
142 profile
|= PROFILE_EVENTS
;
143 if(pc
.getCDROMIndex() >= 0)
144 profile
|= PROFILE_CDROM
;
146 menuManager
.setProfile(profile
);
153 Clock sysClock
= (Clock
)pc
.getComponent(Clock
.class);
154 long current
= sysClock
.getTime();
155 if(imminentTrapTime
> 0) {
156 pc
.getTraceTrap().setTrapTime(current
+ imminentTrapTime
);
157 } else if(imminentTrapTime
== 0) {
158 //Hack: We set trace trap to trap immediately. It comes too late to abort next instruction, but
159 //early enough to abort one after that.
160 pc
.getTraceTrap().setTrapTime(current
);
162 if(currentProject
.events
!= null)
163 currentProject
.events
.setPCRunStatus(true);
166 public void pcStopping()
168 if(currentProject
.events
!= null)
169 currentProject
.events
.setPCRunStatus(false);
171 return; //Don't mess with UI when shutting down.
174 long profile
= PROFILE_STOPPED
;
176 profile
|= PROFILE_HAVE_PC
;
178 profile
|= PROFILE_NO_PC
;
179 if(currentProject
!= null && currentProject
.events
!= null);
180 profile
|= PROFILE_EVENTS
;
181 if(pc
.getCDROMIndex() >= 0)
182 profile
|= PROFILE_CDROM
;
184 menuManager
.setProfile(profile
);
188 } catch(Exception e
) {
189 errorDialog(e
, "Failed to update disk menus", null, "Dismiss");
193 pc
.getTraceTrap().clearTrapTime();
194 pc
.getTraceTrap().getAndClearTrapActive();
198 private String
diskNameByIdx(int idx
)
200 return pc
.getDisks().lookupDisk(idx
).getName();
203 private void updateDisks() throws Exception
205 for(String x
: disks
)
206 menuManager
.removeMenuItem(x
);
213 DiskImageSet imageSet
= pc
.getDisks();
214 int[] floppies
= imageSet
.diskIndicesByType(BlockDevice
.Type
.FLOPPY
);
215 int[] cdroms
= imageSet
.diskIndicesByType(BlockDevice
.Type
.CDROM
);
217 for(int i
= 0; i
< floppies
.length
; i
++) {
218 String name
= diskNameByIdx(floppies
[i
]);
219 menuManager
.addMenuItem("Drives→fda→" + name
, this, "menuChangeDisk", new Object
[]{new Integer(0),
220 new Integer(floppies
[i
])}, PROFILE_HAVE_PC
);
221 menuManager
.addMenuItem("Drives→fdb→" + name
, this, "menuChangeDisk", new Object
[]{new Integer(1),
222 new Integer(floppies
[i
])}, PROFILE_HAVE_PC
);
223 menuManager
.addSelectableMenuItem("Drives→Write Protect→" + name
, this, "menuWriteProtect",
224 new Object
[]{new Integer(floppies
[i
])}, imageSet
.lookupDisk(floppies
[i
]).isReadOnly(),
226 disks
.add("Drives→fda→" + name
);
227 disks
.add("Drives→fdb→" + name
);
228 disks
.add("Drives→Write Protect→" + name
);
231 for(int i
= 0; i
< cdroms
.length
; i
++) {
232 String name
= diskNameByIdx(cdroms
[i
]);
233 menuManager
.addMenuItem("Drives→CD-ROM→" + name
, this, "menuChangeDisk", new Object
[]{new Integer(1),
234 new Integer(cdroms
[i
])}, PROFILE_HAVE_PC
| PROFILE_CDROM
);
235 disks
.add("Drives→CD-ROM→" + name
);
241 boolean wasRunning
= false;
242 while(true) { //We will be killed by JVM.
243 //Wait for us to become runnable again.
244 while(!running
|| pc
== null) {
245 if(wasRunning
&& pc
!= null)
247 wasRunning
= running
;
255 } catch(Exception e
) {
261 wasRunning
= running
;
265 if(pc
.getHitTraceTrap()) {
266 if(pc
.getAndClearTripleFaulted())
267 callShowOptionDialog(this, "CPU shut itself down due to triple fault. Rebooting the system.",
268 "Triple fault!", JOptionPane
.YES_NO_OPTION
, JOptionPane
.WARNING_MESSAGE
, null,
269 new String
[]{"Dismiss"}, "Dismiss");
271 SwingUtilities
.invokeAndWait(new Thread() { public void run() { stopNoWait(); }});
274 } catch (Exception e
) {
275 errorDialog(e
, "Hardware emulator internal error", this, "Dismiss");
277 SwingUtilities
.invokeAndWait(new Thread() { public void run() { stopNoWait(); }});
278 } catch (Exception f
) {
285 public void connectPC(PC pc
)
287 currentProject
.pc
= pc
;
288 vPluginManager
.reconnect(pc
);
292 private void startExternal()
294 if(pc
!= null && !running
)
295 if(!SwingUtilities
.isEventDispatchThread())
297 SwingUtilities
.invokeAndWait(new Thread() { public void run() { PCControl
.this.start(); }});
298 } catch(Exception e
) {
304 private void stopExternal()
306 if(pc
!= null && running
)
307 if(!SwingUtilities
.isEventDispatchThread())
309 SwingUtilities
.invokeAndWait(new Thread() { public void run() { PCControl
.this.stop(); }});
310 } catch(Exception e
) {
316 public boolean eci_state_save(String filename
)
319 (new Thread(new SaveStateTask(filename
, false), "Savestate task thread")).start();
323 public boolean eci_state_dump(String filename
)
326 (new Thread(new StatusDumpTask(filename
), "Statedump task thread")).start();
330 public boolean eci_movie_save(String filename
)
333 (new Thread(new SaveStateTask(filename
, true), "Savestate task thread")).start();
337 public boolean eci_state_load(String filename
)
340 (new Thread(new LoadStateTask(filename
, LoadStateTask
.MODE_NORMAL
), "Loadstate task thread")).start();
344 public boolean eci_state_load_noevents(String filename
)
347 (new Thread(new LoadStateTask(filename
, LoadStateTask
.MODE_PRESERVE
), "Loadstate task thread")).start();
351 public boolean eci_movie_load(String filename
)
354 (new Thread(new LoadStateTask(filename
, LoadStateTask
.MODE_MOVIEONLY
), "Loadstate task thread")).start();
358 public boolean eci_pc_assemble()
361 (new Thread(new AssembleTask(), "Assemble task thread")).start();
365 public boolean eci_ram_dump_text(String filename
)
368 (new Thread(new RAMDumpTask(filename
, false), "RAM dump task thread")).start();
372 public boolean eci_ram_dump_binary(String filename
)
375 (new Thread(new RAMDumpTask(filename
, true), "RAM dump task thread")).start();
379 public void eci_trap_vretrace_start_on()
381 trapFlags
|= TraceTrap
.TRACE_STOP_VRETRACE_START
;
384 public void eci_trap_vretrace_start_off()
386 trapFlags
&= ~TraceTrap
.TRACE_STOP_VRETRACE_START
;
389 public void eci_trap_vretrace_end_on()
391 trapFlags
|= TraceTrap
.TRACE_STOP_VRETRACE_END
;
394 public void eci_trap_vretrace_end_off()
396 trapFlags
&= ~TraceTrap
.TRACE_STOP_VRETRACE_END
;
399 public void eci_trap_timed_disable()
401 this.imminentTrapTime
= -1;
404 public void eci_trap_timed(Long time
)
406 this.imminentTrapTime
= time
.longValue();
409 public void eci_pc_start()
414 public void eci_pc_stop()
419 public void eci_pccontrol_setwinpos(Integer x
, Integer y
)
421 moveWindow(this, x
.intValue(), y
.intValue(), nativeWidth
, nativeHeight
);
424 public void eci_sendevent(String clazz
, String
[] rargs
)
426 System
.err
.println("Event to: '" + clazz
+ "':");
427 for(int i
= 0; i
< rargs
.length
; i
++) {
428 System
.err
.println("rargs[" + i
+ "]: '" + rargs
[i
] + "'.");
430 if(currentProject
.events
!= null) {
432 Class
<?
extends HardwareComponent
> x
= Class
.forName(clazz
).asSubclass(HardwareComponent
.class);
433 currentProject
.events
.addEvent(0L, x
, rargs
);
434 } catch(Exception e
) {
435 System
.err
.println("Error adding event: " + e
.getMessage());
440 public void eci_memory_read(Long address
, Integer size
)
442 if(currentProject
.pc
!= null) {
443 long addr
= address
.longValue();
444 long _size
= size
.intValue();
446 PhysicalAddressSpace addrSpace
;
447 if(addr
< 0 || addr
> 0xFFFFFFFFL
|| (_size
!= 1 && _size
!= 2 && _size
!= 4))
450 addrSpace
= (PhysicalAddressSpace
)currentProject
.pc
.getComponent(PhysicalAddressSpace
.class);
452 ret
= (long)addrSpace
.getByte((int)addr
) & 0xFF;
454 ret
= (long)addrSpace
.getWord((int)addr
) & 0xFFFF;
456 ret
= (long)addrSpace
.getDoubleWord((int)addr
) & 0xFFFFFFFFL
;
458 vPluginManager
.returnValue(ret
);
462 public void eci_memory_write(Long address
, Long value
, Integer size
)
464 if(currentProject
.pc
!= null) {
465 long addr
= address
.longValue();
466 long _size
= size
.intValue();
467 long _value
= value
.longValue();
468 PhysicalAddressSpace addrSpace
;
469 if(addr
< 0 || addr
> 0xFFFFFFFFL
|| (_size
!= 1 && _size
!= 2 && _size
!= 4))
472 addrSpace
= (PhysicalAddressSpace
)currentProject
.pc
.getComponent(PhysicalAddressSpace
.class);
474 addrSpace
.setByte((int)addr
, (byte)_value
);
476 addrSpace
.setWord((int)addr
, (short)_value
);
478 addrSpace
.setDoubleWord((int)addr
, (int)_value
);
482 public PCControl(Plugins manager
, String args
) throws Exception
486 UTFInputLineStream file
= null;
487 Map
<String
, String
> params
= parseStringToComponents(args
);
488 Set
<String
> used
= new HashSet
<String
>();
489 String extramenu
= params
.get("extramenu");
490 String uncompress
= params
.get("uncompressedsave");
491 if(uncompress
!= null)
492 uncompressedSave
= true;
493 if(extramenu
== null)
496 file
= new UTFInputLineStream(new FileInputStream(extramenu
));
499 boolean exists
= false;
500 String
[] line
= nextParseLine(file
);
503 if(line
.length
< 3 || line
[0].charAt(0) == '→') {
504 System
.err
.println("Warning: Bad extra menu item '" + line
[0] + "'.");
507 if(line
[0].length() == 0 || line
[0].charAt(line
[0].length() - 1) == '→') {
508 System
.err
.println("Warning: Bad extra menu item '" + line
[0] + "'.");
511 if(line
[0].indexOf("→→") >= 0) {
512 System
.err
.println("Warning: Bad extra menu item '" + line
[0] + "'.");
515 if(used
.contains(line
[0]))
518 KeyStroke stroke
= null;
519 if(!line
[1].equals("<>")) {
520 stroke
= KeyStroke
.getKeyStroke(line
[1]);
522 System
.err
.println("Warning: Bad keystroke '" + line
[1] + "'.");
527 String
[] lineCommand
= Arrays
.copyOfRange(line
, 2, line
.length
);
529 List
<String
[]> commandList
= extraActions
.get(line
[0]);
530 if(commandList
== null)
531 extraActions
.put(line
[0], commandList
= new ArrayList
<String
[]>());
532 commandList
.add(lineCommand
);
535 menuManager
.addMenuItem("Extra→" + line
[0], this, "menuExtra", new String
[]{line
[0]}, PROFILE_ALWAYS
,
539 } catch(IOException e
) {
540 errorDialog(e
, "Failed to load extra menu defintions", null, "dismiss");
544 setJMenuBar(menuManager
.getMainBar());
547 public PCControl(Plugins manager
) throws Exception
551 if(DiskImage
.getLibrary() == null)
552 throw new Exception("PCControl plugin requires disk library");
555 this.willCleanup
= false;
556 shuttingDown
= false;
557 configDialog
= new PCConfigDialog();
558 dumpDialog
= new DumpControlDialog(manager
);
559 extraActions
= new HashMap
<String
, List
<String
[]> >();
560 menuManager
= new MenuManager();
562 menuManager
.setProfile(PROFILE_NO_PC
| PROFILE_STOPPED
);
564 menuManager
.addMenuItem("System→Assemble", this, "menuAssemble", null, PROFILE_STOPPED
);
565 menuManager
.addMenuItem("System→Start", this, "menuStart", null, PROFILE_STOPPED
| PROFILE_HAVE_PC
);
566 menuManager
.addMenuItem("System→Stop", this, "menuStop", null, PROFILE_RUNNING
);
567 menuManager
.addMenuItem("System→Reset", this, "menuReset", null, PROFILE_HAVE_PC
);
568 menuManager
.addMenuItem("System→Dumping control", this, "menuDump", null, PROFILE_HAVE_PC
| PROFILE_STOPPED
);
569 menuManager
.addMenuItem("System→Quit", this, "menuQuit", null, PROFILE_ALWAYS
);
570 menuManager
.addSelectableMenuItem("Breakpoints→Trap VRetrace Start", this, "menuVRetraceStart", null, false,
572 menuManager
.addSelectableMenuItem("Breakpoints→Trap VRetrace End", this, "menuVRetraceEnd", null, false,
574 menuManager
.addMenuItem("Snapshot→Change Run Authors", this, "menuChangeAuthors", null, PROFILE_HAVE_PC
);
575 menuManager
.addMenuItem("Snapshot→Save→Snapshot", this, "menuSave", new Object
[]{new Boolean(false)},
576 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
577 menuManager
.addMenuItem("Snapshot→Save→Movie", this, "menuSave", new Object
[]{new Boolean(true)},
578 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
579 menuManager
.addMenuItem("Snapshot→Save→Status Dump", this, "menuStatusDump", null,
580 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
581 menuManager
.addMenuItem("Snapshot→Load→Snapshot", this, "menuLoad",
582 new Object
[]{new Integer(LoadStateTask
.MODE_NORMAL
)}, PROFILE_STOPPED
);
583 menuManager
.addMenuItem("Snapshot→Load→Snapshot (preserve events)", this, "menuLoad",
584 new Object
[]{new Integer(LoadStateTask
.MODE_PRESERVE
)}, PROFILE_STOPPED
| PROFILE_EVENTS
);
585 menuManager
.addMenuItem("Snapshot→Load→Movie", this, "menuLoad",
586 new Object
[]{new Integer(LoadStateTask
.MODE_MOVIEONLY
)}, PROFILE_STOPPED
);
587 menuManager
.addMenuItem("Snapshot→RAM Dump→Hexadecimal", this, "menuRAMDump", new Object
[]{new Boolean(false)},
588 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
589 menuManager
.addMenuItem("Snapshot→RAM Dump→Binary", this, "menuRAMDump", new Object
[]{new Boolean(true)},
590 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
591 menuManager
.addMenuItem("Snapshot→Truncate Event Stream", this, "menuTruncate", null,
592 PROFILE_STOPPED
| PROFILE_EVENTS
);
594 for(int i
= 0; i
< stopLabel
.length
; i
++) {
595 menuManager
.addSelectableMenuItem("Breakpoints→Timed Stops→" + stopLabel
[i
], this, "menuTimedStop",
596 null, (i
== 0), PROFILE_ALWAYS
);
598 imminentTrapTime
= -1;
600 menuManager
.addMenuItem("Drives→fda→<Empty>", this, "menuChangeDisk", new Object
[]{new Integer(0),
601 new Integer(-1)}, PROFILE_HAVE_PC
);
602 menuManager
.addMenuItem("Drives→fdb→<Empty>", this, "menuChangeDisk", new Object
[]{new Integer(1),
603 new Integer(-1)}, PROFILE_HAVE_PC
);
604 menuManager
.addMenuItem("Drives→CD-ROM→<Empty>", this, "menuChangeDisk", new Object
[]{new Integer(2),
605 new Integer(-1)}, PROFILE_HAVE_PC
| PROFILE_CDROM
);
606 menuManager
.addMenuItem("Drives→Add image", this, "menuAddDisk", null, PROFILE_HAVE_PC
);
607 menuManager
.addMenuItem("Drives→Import Image", this, "menuImport", null, PROFILE_ALWAYS
);
609 menuManager
.addMenuItem("Debug→Hacks→NO_FPU", this, "menuNOFPU", null, PROFILE_HAVE_PC
);
610 menuManager
.addMenuItem("Debug→Hacks→VGA_DRAW", this, "menuVGADRAW", null, PROFILE_HAVE_PC
);
611 menuManager
.addMenuItem("Debug→Hacks→VGA_SCROLL_2", this, "menuVGASCROLL2", null, PROFILE_HAVE_PC
);
613 disks
= new HashSet
<String
>();
614 currentProject
= new PC
.PCFullStatus();
616 this.vPluginManager
= manager
;
618 setJMenuBar(menuManager
.getMainBar());
622 setDefaultCloseOperation(JFrame
.EXIT_ON_CLOSE
);
624 catch (AccessControlException e
)
626 System
.err
.println("Error: Not able to add some components to frame: " + e
.getMessage());
629 snapshotFileChooser
= new JFileChooser(System
.getProperty("user.dir"));
631 getContentPane().validate();
634 Dimension d
= getSize();
635 nativeWidth
= d
.width
;
636 nativeHeight
= d
.height
;
640 public void menuExtra(String i
, Object
[] args
)
642 final List
<String
[]> commandList
= extraActions
.get(args
[0]);
643 if(commandList
== null) {
644 System
.err
.println("Warning: Called extra menu with unknown entry '" + args
[0] + "'.");
648 //Run the functions on seperate thread to avoid deadlocking.
649 (new Thread(new Runnable() { public void run() { menuExtraThreadFunc(commandList
); }}, "Extra action thread")).start();
652 private void menuExtraThreadFunc(List
<String
[]> actions
)
654 for(String
[] i
: actions
) {
656 vPluginManager
.invokeExternalCommandSynchronous(i
[0], null);
658 String
[] rest
= Arrays
.copyOfRange(i
, 1, i
.length
, String
[].class);
659 vPluginManager
.invokeExternalCommandSynchronous(i
[0], rest
);
664 public void menuAssemble(String i
, Object
[] args
)
666 (new Thread(new AssembleTask(), "Assembe task thread")).start();
669 public void menuStart(String i
, Object
[] args
)
674 public void menuStop(String i
, Object
[] args
)
679 public void menuReset(String i
, Object
[] args
)
684 public void menuDump(String i
, Object
[] args
)
686 (new Thread(new DumpControlTask(), "Dump control task thread")).start();
689 public void menuImport(String i
, Object
[] args
)
692 new ImportDiskImage();
693 } catch(Exception e
) {
698 public void menuNOFPU(String i
, Object
[] args
)
703 public void menuVGADRAW(String i
, Object
[] args
)
708 public void menuVGASCROLL2(String i
, Object
[] args
)
710 pc
.setVGAScroll2Hack();
713 public void menuQuit(String i
, Object
[] args
)
715 vPluginManager
.shutdownEmulator();
718 public void menuVRetraceStart(String i
, Object
[] args
)
720 trapFlags ^
= TraceTrap
.TRACE_STOP_VRETRACE_START
;
721 menuManager
.setSelected("Breakpoints→Trap VRetrace Start",
722 (trapFlags
& TraceTrap
.TRACE_STOP_VRETRACE_START
) == TraceTrap
.TRACE_STOP_VRETRACE_START
);
725 public void menuVRetraceEnd(String i
, Object
[] args
)
727 trapFlags ^
= TraceTrap
.TRACE_STOP_VRETRACE_END
;
728 menuManager
.setSelected("Breakpoints→Trap VRetrace End",
729 (trapFlags
& TraceTrap
.TRACE_STOP_VRETRACE_END
) == TraceTrap
.TRACE_STOP_VRETRACE_END
);
732 public void menuTimedStop(String i
, Object
[] args
)
734 for(int j
= 0; j
< stopLabel
.length
; j
++) {
735 String label
= "Breakpoints→Timed Stops→" + stopLabel
[j
];
736 if(i
.equals(label
)) {
737 this.imminentTrapTime
= stopTime
[j
];
738 menuManager
.select(label
);
740 menuManager
.unselect(label
);
744 public void menuSave(String i
, Object
[] args
)
747 (new Thread(new SaveStateTask(((Boolean
)args
[0]).booleanValue()), "Savestate task thread")).start();
750 public void menuStatusDump(String i
, Object
[] args
)
753 (new Thread(new StatusDumpTask(), "Statusdump task thread")).start();
756 public void menuLoad(String i
, Object
[] args
)
759 (new Thread(new LoadStateTask(((Integer
)args
[0]).intValue()), "Loadstate task thread")).start();
762 public void menuRAMDump(String i
, Object
[] args
)
765 (new Thread(new RAMDumpTask(((Boolean
)args
[0]).booleanValue()), "RAM dump task thread")).start();
768 public void menuTruncate(String i
, Object
[] args
)
770 currentProject
.events
.truncateEventStream();
773 public void menuChangeDisk(String i
, Object
[] args
)
775 changeFloppy(((Integer
)args
[0]).intValue(), ((Integer
)args
[1]).intValue());
778 public void menuWriteProtect(String i
, Object
[] args
)
780 int disk
= ((Integer
)args
[0]).intValue();
781 writeProtect(disk
, menuManager
.isSelected(i
));
782 DiskImageSet imageSet
= pc
.getDisks();
783 menuManager
.setSelected(i
, imageSet
.lookupDisk(disk
).isReadOnly());
786 public void menuAddDisk(String i
, Object
[] args
)
789 (new Thread(new AddDiskTask(), "Add disk task thread")).start();
792 public void menuChangeAuthors(String i
, Object
[] args
)
795 (new Thread(new ChangeAuthorsTask(), "Change authors task thread")).start();
798 public synchronized void start()
800 vPluginManager
.pcStarted();
805 private String
prettyPrintTime(long ts
)
810 s
= s
+ "" + (ts
/ 1000000000) + " ";
812 s
= s
+ "" + (ts
% 1000000000 / 100000000);
814 s
= s
+ "" + (ts
% 100000000 / 10000000);
816 s
= s
+ "" + (ts
% 10000000 / 1000000) + " ";
818 s
= s
+ "" + (ts
% 1000000 / 100000);
820 s
= s
+ "" + (ts
% 100000 / 10000);
822 s
= s
+ "" + (ts
% 10000 / 1000) + " ";
824 s
= s
+ "" + (ts
% 1000 / 100);
826 s
= s
+ "" + (ts
% 100 / 10);
827 s
= s
+ "" + (ts
% 10);
831 protected synchronized void stopNoWait()
834 vPluginManager
.pcStopped();
835 Clock sysClock
= (Clock
)pc
.getComponent(Clock
.class);
836 System
.err
.println("Notice: PC emulation stopped (at time sequence value " +
837 prettyPrintTime(sysClock
.getTime()) + ")");
840 public synchronized void stop()
843 pc
.getTraceTrap().doPotentialTrap(TraceTrap
.TRACE_STOP_IMMEDIATE
);
845 System
.err
.println("Informational: Waiting for PC to halt...");
849 } catch(Exception e
) {
855 public JScrollPane
getMonitorPane()
860 protected void reset()
865 public synchronized boolean isRunning()
870 private void changeFloppy(int drive
, int image
)
874 PC
.DiskChanger changer
= (PC
.DiskChanger
)pc
.getComponent(PC
.DiskChanger
.class);
875 changer
.changeFloppyDisk(drive
, image
);
876 } catch (Exception e
) {
877 System
.err
.println("Error: Failed to change disk");
878 errorDialog(e
, "Failed to change disk", null, "Dismiss");
882 private void writeProtect(int image
, boolean state
)
886 PC
.DiskChanger changer
= (PC
.DiskChanger
)pc
.getComponent(PC
.DiskChanger
.class);
887 changer
.wpFloppyDisk(image
, state
);
888 } catch (Exception e
) {
889 System
.err
.println("Error: Failed to change floppy write protect");
890 errorDialog(e
, "Failed to write (un)protect floppy", null, "Dismiss");
894 private class LoadStateTask
extends AsyncGUITask
902 private static final int MODE_NORMAL
= 1;
903 private static final int MODE_PRESERVE
= 2;
904 private static final int MODE_MOVIEONLY
= 3;
906 public LoadStateTask(int mode
)
908 oTime
= System
.currentTimeMillis();
911 pw
= new PleaseWait("Loading savestate...");
914 public LoadStateTask(String name
, int mode
)
917 choosen
= new File(name
);
920 protected void runPrepare()
922 PCControl
.this.setEnabled(false);
923 if(choosen
== null) {
925 if(_mode
== MODE_PRESERVE
)
926 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "LOAD JPC-RR Snapshot (PE)");
927 else if(_mode
== MODE_MOVIEONLY
)
928 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "LOAD JPC-RR Snapshot (MO)");
930 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "LOAD JPC-RR Snapshot");
931 choosen
= snapshotFileChooser
.getSelectedFile();
939 private void doCycle()
941 (new Thread(new Runnable() { public void run() { synchronized(LoadStateTask
.this) {
942 pc
.getVideoOutput().holdOutput(); cycleDone
= true; LoadStateTask
.this.notifyAll();}}}, "VGA output cycle thread")).start();
948 } catch(Exception e
) {
952 protected void runFinish()
956 connectPC(pc
= currentProject
.pc
);
958 System
.err
.println("Informational: Loadstate done");
959 } catch(Exception e
) {
965 errorDialog(caught
, "Load savestate failed", PCControl
.this, "Dismiss");
967 PCControl
.this.setEnabled(true);
969 PCControl
.this.requestFocus(true);
970 restoreFocus
= false;
971 System
.err
.println("Total save time: " + (System
.currentTimeMillis() - oTime
) + "ms.");
972 PCControl
.this.vPluginManager
.signalCommandCompletion();
975 protected void runTask()
981 System
.err
.println("Informational: Loading a snapshot of JPC-RR");
982 long times1
= System
.currentTimeMillis();
983 JRSRArchiveReader reader
= new JRSRArchiveReader(choosen
.getAbsolutePath());
985 PC
.PCFullStatus fullStatus
= PC
.loadSavestate(reader
, _mode
== MODE_PRESERVE
, _mode
== MODE_MOVIEONLY
,
988 currentProject
= fullStatus
;
991 long times2
= System
.currentTimeMillis();
992 System
.err
.println("Informational: Loadstate complete (" + (times2
- times1
) + "ms).");
993 } catch(Exception e
) {
999 private class SaveStateTask
extends AsyncGUITask
1007 public SaveStateTask(boolean movie
)
1009 oTime
= System
.currentTimeMillis();
1012 pw
= new PleaseWait("Saving savestate...");
1015 public SaveStateTask(String name
, boolean movie
)
1018 choosen
= new File(name
);
1021 protected void runPrepare()
1023 PCControl
.this.setEnabled(false);
1024 if(choosen
== null) {
1025 int returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, movieOnly ?
"Save JPC-RR Movie" :
1026 "Save JPC-RR Snapshot");
1027 choosen
= snapshotFileChooser
.getSelectedFile();
1035 protected void runFinish()
1038 if(caught
!= null) {
1039 errorDialog(caught
, "Saving savestate failed", PCControl
.this, "Dismiss");
1041 PCControl
.this.setEnabled(true);
1043 PCControl
.this.requestFocus(true);
1044 restoreFocus
= false;
1045 System
.err
.println("Total save time: " + (System
.currentTimeMillis() - oTime
) + "ms.");
1046 PCControl
.this.vPluginManager
.signalCommandCompletion();
1049 protected void runTask()
1054 JRSRArchiveWriter writer
= null;
1057 System
.err
.println("Informational: Savestating...");
1058 long times1
= System
.currentTimeMillis();
1059 writer
= new JRSRArchiveWriter(choosen
.getAbsolutePath());
1060 PC
.saveSavestate(writer
, currentProject
, movieOnly
, uncompressedSave
);
1061 renameFile(choosen
, new File(choosen
.getAbsolutePath() + ".backup"));
1063 long times2
= System
.currentTimeMillis();
1064 System
.err
.println("Informational: Savestate complete (" + (times2
- times1
) + "ms).");
1065 } catch(Exception e
) {
1067 try { writer
.rollback(); } catch(Exception f
) {}
1073 private class StatusDumpTask
extends AsyncGUITask
1079 public StatusDumpTask()
1082 pw
= new PleaseWait("Saving status dump...");
1085 public StatusDumpTask(String name
)
1088 choosen
= new File(name
);
1091 protected void runPrepare()
1093 PCControl
.this.setEnabled(false);
1094 if(choosen
== null) {
1095 int returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "Save Status dump");
1096 choosen
= snapshotFileChooser
.getSelectedFile();
1104 protected void runFinish()
1107 if(caught
!= null) {
1108 errorDialog(caught
, "Status dump failed", PCControl
.this, "Dismiss");
1110 PCControl
.this.setEnabled(true);
1112 PCControl
.this.requestFocus(true);
1113 restoreFocus
= false;
1114 PCControl
.this.vPluginManager
.signalCommandCompletion();
1117 protected void runTask()
1123 OutputStream outb
= new BufferedOutputStream(new FileOutputStream(choosen
));
1124 PrintStream out
= new PrintStream(outb
, false, "UTF-8");
1125 StatusDumper sd
= new StatusDumper(out
);
1129 System
.err
.println("Informational: Dumped " + sd
.dumpedObjects() + " objects");
1130 } catch(Exception e
) {
1136 private class RAMDumpTask
extends AsyncGUITask
1143 public RAMDumpTask(boolean binFlag
)
1146 pw
= new PleaseWait("Saving RAM dump...");
1150 public RAMDumpTask(String name
, boolean binFlag
)
1153 choosen
= new File(name
);
1156 protected void runPrepare()
1158 PCControl
.this.setEnabled(false);
1159 if(choosen
== null) {
1162 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "Save RAM dump");
1164 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "Save RAM hexdump");
1165 choosen
= snapshotFileChooser
.getSelectedFile();
1173 protected void runFinish()
1176 if(caught
!= null) {
1177 errorDialog(caught
, "RAM dump failed", PCControl
.this, "Dismiss");
1179 PCControl
.this.setEnabled(true);
1181 PCControl
.this.requestFocus(true);
1182 restoreFocus
= false;
1183 PCControl
.this.vPluginManager
.signalCommandCompletion();
1186 protected void runTask()
1192 OutputStream outb
= new BufferedOutputStream(new FileOutputStream(choosen
));
1193 byte[] pagebuf
= new byte[4096];
1194 PhysicalAddressSpace addr
= (PhysicalAddressSpace
)pc
.getComponent(PhysicalAddressSpace
.class);
1195 int lowBound
= addr
.findFirstRAMPage(0);
1196 int firstUndumped
= 0;
1199 while(lowBound
>= 0) {
1200 for(; firstUndumped
< lowBound
; firstUndumped
++)
1201 dumpPage(outb
, firstUndumped
, null);
1202 addr
.readRAMPage(firstUndumped
++, pagebuf
);
1203 dumpPage(outb
, lowBound
, pagebuf
);
1205 highBound
= lowBound
+ 1;
1206 lowBound
= addr
.findFirstRAMPage(++lowBound
);
1209 System
.err
.println("Informational: Dumped machine RAM (" + highBound
+ " pages examined, " +
1210 present
+ " pages present).");
1211 } catch(Exception e
) {
1216 private byte charForHex(int hvalue
)
1219 return (byte)(hvalue
+ 48);
1220 else if(hvalue
> 9 && hvalue
< 16)
1221 return (byte)(hvalue
+ 55);
1223 System
.err
.println("Unknown hex value: " + hvalue
+ ".");
1227 private void dumpPage(OutputStream stream
, int pageNo
, byte[] buffer
) throws IOException
1230 pageNo
= pageNo
& 0xFFFFF; //Cut page numbers out of range.
1231 if(!binary
&& buffer
== null)
1232 return; //Don't dump null pages in non-binary mode.
1234 pageBufSize
= 4096; //Binary page buffer is 4096 bytes.
1236 pageBufSize
= 14592; //Hexdump page buffer is 14592 bytes.
1237 byte[] outputPage
= new byte[pageBufSize
];
1238 if(buffer
!= null && binary
) {
1239 System
.arraycopy(buffer
, 0, outputPage
, 0, 4096);
1240 } else if(buffer
!= null) { //Hex mode
1241 for(int i
= 0; i
< 256; i
++) {
1242 for(int j
= 0; j
< 57; j
++) {
1244 outputPage
[57 * i
+ j
] = charForHex((pageNo
>>> (4 * (4 - j
))) & 0xF);
1246 outputPage
[57 * i
+ j
] = charForHex(i
/ 16);
1248 outputPage
[57 * i
+ j
] = charForHex(i
% 16);
1250 outputPage
[57 * i
+ j
] = 48;
1252 outputPage
[57 * i
+ j
] = 10;
1254 outputPage
[57 * i
+ j
] = 32;
1256 outputPage
[57 * i
+ j
] = charForHex(((int)buffer
[16 * i
+ j
/ 3 - 3] & 0xFF) / 16);
1258 outputPage
[57 * i
+ j
] = charForHex(buffer
[16 * i
+ j
/ 3 - 3] & 0xF);
1260 System
.err
.println("Error: dumpPage: unhandled j = " + j
+ ".");
1264 stream
.write(outputPage
);
1268 private class AssembleTask
extends AsyncGUITask
1274 public AssembleTask()
1276 pw
= new PleaseWait("Assembling PC...");
1280 protected void runPrepare()
1282 PCControl
.this.setEnabled(false);
1284 configDialog
.popUp();
1285 } catch(Exception e
) {
1290 protected void runFinish()
1292 if(caught
== null && !canceled
) {
1294 currentProject
.projectID
= randomHexes(24);
1295 currentProject
.rerecords
= 0;
1296 currentProject
.events
= new EventRecorder();
1297 currentProject
.events
.attach(pc
, null);
1298 currentProject
.savestateID
= null;
1299 currentProject
.extraHeaders
= null;
1300 currentProject
.events
.setRerecordCount(0);
1301 currentProject
.events
.setHeaders(currentProject
.extraHeaders
);
1303 } catch(Exception e
) {
1309 if(caught
!= null) {
1310 errorDialog(caught
, "PC Assembly failed", PCControl
.this, "Dismiss");
1312 PCControl
.this.setEnabled(true);
1314 PCControl
.this.requestFocus(true);
1315 restoreFocus
= false;
1316 PCControl
.this.vPluginManager
.signalCommandCompletion();
1319 protected void runTask()
1323 PC
.PCHardwareInfo hw
= configDialog
.waitClose();
1329 SwingUtilities
.invokeAndWait(new Thread() { public void run() { pw
.popUp(); }});
1330 } catch(Exception e
) {
1334 pc
= PC
.createPC(hw
);
1335 } catch(Exception e
) {
1341 private class DumpControlTask
extends AsyncGUITask
1346 public DumpControlTask()
1350 protected void runPrepare()
1352 PCControl
.this.setEnabled(false);
1354 dumpDialog
.popUp(PCControl
.this.pc
);
1355 } catch(Exception e
) {
1360 protected void runFinish()
1362 if(caught
!= null) {
1363 errorDialog(caught
, "Opening dump control dialog failed", PCControl
.this, "Dismiss");
1365 PCControl
.this.setEnabled(true);
1367 PCControl
.this.requestFocus(true);
1368 restoreFocus
= false;
1371 protected void runTask()
1375 dumpDialog
.waitClose();
1379 private class AddDiskTask
extends AsyncGUITask
1384 public AddDiskTask()
1386 dd
= new NewDiskDialog();
1387 PCControl
.this.setEnabled(false);
1390 protected void runPrepare()
1394 protected void runFinish()
1396 if(caught
!= null) {
1397 errorDialog(caught
, "Adding disk failed", PCControl
.this, "Dismiss");
1399 PCControl
.this.setEnabled(true);
1401 PCControl
.this.requestFocus(true);
1402 restoreFocus
= false;
1405 } catch(Exception e
) {
1406 errorDialog(e
, "Failed to update disk menus", null, "Dismiss");
1408 PCControl
.this.vPluginManager
.signalCommandCompletion();
1411 protected void runTask()
1413 NewDiskDialog
.Response res
= dd
.waitClose();
1419 pc
.getDisks().addDisk(img
= new DiskImage(res
.diskFile
, false));
1420 img
.setName(res
.diskName
);
1421 } catch(Exception e
) {
1427 private class ChangeAuthorsTask
extends AsyncGUITask
1432 public ChangeAuthorsTask()
1436 AuthorsDialog
.AuthorElement
[] authorNames
= null;
1437 if(currentProject
!= null)
1438 authorNames
= AuthorsDialog
.readAuthorsFromHeaders(currentProject
.extraHeaders
);
1440 ad
= new AuthorsDialog(authorNames
);
1441 PCControl
.this.setEnabled(false);
1444 protected void runPrepare()
1448 protected void runFinish()
1450 if(caught
!= null) {
1451 errorDialog(caught
, "Changing authors failed", PCControl
.this, "Dismiss");
1453 PCControl
.this.setEnabled(true);
1455 PCControl
.this.requestFocus(true);
1456 restoreFocus
= false;
1457 PCControl
.this.vPluginManager
.signalCommandCompletion();
1460 protected void runTask()
1462 AuthorsDialog
.Response res
= ad
.waitClose();
1467 currentProject
.extraHeaders
= AuthorsDialog
.rewriteHeaderAuthors(currentProject
.extraHeaders
,
1469 currentProject
.events
.setHeaders(currentProject
.extraHeaders
);
1470 } catch(Exception e
) {