2 JPC-RR: A x86 PC Hardware Emulator
5 Copyright (C) 2007-2009 Isis Innovation Limited
6 Copyright (C) 2009 H. Ilari Liusvaara
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 2 as published by
10 the Free Software Foundation.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 Based on JPC x86 PC Hardware emulator,
22 A project from the Physics Dept, The University of Oxford
24 Details about original JPC can be found at:
26 www-jpc.physics.ox.ac.uk
30 package org
.jpc
.plugins
;
32 import java
.awt
.Dimension
;
35 import java
.util
.zip
.*;
36 import java
.security
.AccessControlException
;
39 import org
.jpc
.emulator
.HardwareComponent
;
40 import org
.jpc
.emulator
.PC
;
41 import org
.jpc
.emulator
.EventRecorder
;
42 import org
.jpc
.emulator
.TraceTrap
;
43 import org
.jpc
.emulator
.peripheral
.FloppyController
;
44 import org
.jpc
.emulator
.memory
.PhysicalAddressSpace
;
45 import org
.jpc
.emulator
.SRLoader
;
46 import org
.jpc
.emulator
.SRDumper
;
47 import org
.jpc
.emulator
.StatusDumper
;
48 import org
.jpc
.emulator
.Clock
;
49 import org
.jpc
.emulator
.DriveSet
;
50 import org
.jpc
.diskimages
.BlockDevice
;
51 import org
.jpc
.diskimages
.DiskImageSet
;
52 import org
.jpc
.diskimages
.GenericBlockDevice
;
53 import org
.jpc
.diskimages
.ImageLibrary
;
54 import org
.jpc
.diskimages
.DiskImage
;
55 import org
.jpc
.pluginsaux
.PleaseWait
;
56 import org
.jpc
.pluginsaux
.AsyncGUITask
;
57 import org
.jpc
.pluginsaux
.NewDiskDialog
;
58 import org
.jpc
.pluginsaux
.AuthorsDialog
;
59 import org
.jpc
.pluginsaux
.PCConfigDialog
;
60 import org
.jpc
.pluginsaux
.MenuManager
;
61 import org
.jpc
.pluginsbase
.*;
62 import org
.jpc
.jrsr
.*;
63 import org
.jpc
.ArgProcessor
;
65 import static org
.jpc
.Misc
.randomHexes
;
66 import static org
.jpc
.Misc
.errorDialog
;
67 import static org
.jpc
.Misc
.callShowOptionDialog
;
68 import static org
.jpc
.Misc
.moveWindow
;
70 public class PCControl
extends JFrame
implements Plugin
72 private static long PROFILE_ALWAYS
= 0;
73 private static long PROFILE_NO_PC
= 1;
74 private static long PROFILE_HAVE_PC
= 2;
75 private static long PROFILE_STOPPED
= 4;
76 private static long PROFILE_RUNNING
= 8;
77 private static long PROFILE_EVENTS
= 16;
78 private static long PROFILE_CDROM
= 32;
80 private static final long serialVersionUID
= 8;
81 private Plugins vPluginManager
;
83 private JFileChooser snapshotFileChooser
;
84 private ImageLibrary imgLibrary
;
86 private Set
<String
> disks
;
90 private int trapFlags
;
92 private volatile boolean running
;
93 private volatile boolean waiting
;
94 private boolean willCleanup
;
95 private static final long[] stopTime
;
96 private static final String
[] stopLabel
;
97 private volatile long imminentTrapTime
;
98 private boolean shuttingDown
;
99 private PCConfigDialog configDialog
;
100 private MenuManager menuManager
;
102 private PC
.PCFullStatus currentProject
;
106 stopTime
= new long[] {-1, 0, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000,
107 5000000, 10000000, 20000000, 50000000, 100000000, 200000000, 500000000, 1000000000, 2000000000,
108 5000000000L, 10000000000L, 20000000000L, 50000000000L};
109 stopLabel
= new String
[] {"(unbounded)", "(singlestep)", "1µs", "2µs", "5µs", "10µs", "20µs", "50µs", "100µs",
110 "200µs", "500µs","1ms", "2ms", "5ms", "10ms", "20ms", "50ms", "100ms", "200ms", "500ms", "1s", "2s", "5s",
111 "10s", "20s", "50s"};
114 public boolean systemShutdown()
116 if(!running
|| pc
== null)
118 //We are running. Do the absolute minimum since we are running in very delicate context.
124 public void reconnect(PC pc
)
126 pcStopping(); //Do the equivalent effects.
130 private void setTrapFlags()
132 pc
.getTraceTrap().setTrapFlags(trapFlags
);
135 public void pcStarting()
137 long profile
= PROFILE_HAVE_PC
| PROFILE_RUNNING
;
138 if(currentProject
!= null && currentProject
.events
!= null);
139 profile
|= PROFILE_EVENTS
;
140 if(pc
.getCDROMIndex() >= 0)
141 profile
|= PROFILE_CDROM
;
143 menuManager
.setProfile(profile
);
150 Clock sysClock
= (Clock
)pc
.getComponent(Clock
.class);
151 long current
= sysClock
.getTime();
152 if(imminentTrapTime
> 0) {
153 pc
.getTraceTrap().setTrapTime(current
+ imminentTrapTime
);
154 } else if(imminentTrapTime
== 0) {
155 //Hack: We set trace trap to trap immediately. It comes too late to abort next instruction, but
156 //early enough to abort one after that.
157 pc
.getTraceTrap().setTrapTime(current
);
159 if(currentProject
.events
!= null)
160 currentProject
.events
.setPCRunStatus(true);
163 public void pcStopping()
165 if(currentProject
.events
!= null)
166 currentProject
.events
.setPCRunStatus(false);
168 return; //Don't mess with UI when shutting down.
171 long profile
= PROFILE_STOPPED
;
173 profile
|= PROFILE_HAVE_PC
;
175 profile
|= PROFILE_NO_PC
;
176 if(currentProject
!= null && currentProject
.events
!= null);
177 profile
|= PROFILE_EVENTS
;
178 if(pc
.getCDROMIndex() >= 0)
179 profile
|= PROFILE_CDROM
;
181 menuManager
.setProfile(profile
);
185 } catch(Exception e
) {
186 errorDialog(e
, "Failed to update disk menus", null, "Dismiss");
190 pc
.getTraceTrap().clearTrapTime();
191 pc
.getTraceTrap().getAndClearTrapActive();
195 private String
diskNameByIdx(int idx
)
197 return pc
.getDisks().lookupDisk(idx
).getName();
200 private void updateDisks() throws Exception
202 for(String x
: disks
)
203 menuManager
.removeMenuItem(x
);
210 DiskImageSet imageSet
= pc
.getDisks();
211 int[] floppies
= imageSet
.diskIndicesByType(BlockDevice
.Type
.FLOPPY
);
212 int[] cdroms
= imageSet
.diskIndicesByType(BlockDevice
.Type
.CDROM
);
214 for(int i
= 0; i
< floppies
.length
; i
++) {
215 String name
= diskNameByIdx(floppies
[i
]);
216 menuManager
.addMenuItem("Drives→fda→" + name
, this, "menuChangeDisk", new Object
[]{new Integer(0),
217 new Integer(floppies
[i
])}, PROFILE_HAVE_PC
);
218 menuManager
.addMenuItem("Drives→fdb→" + name
, this, "menuChangeDisk", new Object
[]{new Integer(1),
219 new Integer(floppies
[i
])}, PROFILE_HAVE_PC
);
220 menuManager
.addSelectableMenuItem("Drives→Write Protect→" + name
, this, "menuWriteProtect",
221 new Object
[]{new Integer(floppies
[i
])}, imageSet
.lookupDisk(floppies
[i
]).isReadOnly(),
223 disks
.add("Drives→fda→" + name
);
224 disks
.add("Drives→fdb→" + name
);
225 disks
.add("Drives→Write Protect→" + name
);
228 for(int i
= 0; i
< cdroms
.length
; i
++) {
229 String name
= diskNameByIdx(cdroms
[i
]);
230 menuManager
.addMenuItem("Drives→CD-ROM→" + name
, this, "menuChangeDisk", new Object
[]{new Integer(1),
231 new Integer(cdroms
[i
])}, PROFILE_HAVE_PC
| PROFILE_CDROM
);
232 disks
.add("Drives→CD-ROM→" + name
);
238 boolean wasRunning
= false;
239 while(true) { //We will be killed by JVM.
240 //Wait for us to become runnable again.
241 while(!running
|| pc
== null) {
242 if(wasRunning
&& pc
!= null)
244 wasRunning
= running
;
252 } catch(Exception e
) {
258 wasRunning
= running
;
262 if(pc
.getHitTraceTrap()) {
263 if(pc
.getAndClearTripleFaulted())
264 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");
266 SwingUtilities
.invokeAndWait(new Thread() { public void run() { stopNoWait(); }});
269 } catch (Exception e
) {
270 errorDialog(e
, "Hardware emulator internal error", this, "Dismiss");
272 SwingUtilities
.invokeAndWait(new Thread() { public void run() { stopNoWait(); }});
273 } catch (Exception f
) {
280 public void connectPC(PC pc
)
282 currentProject
.pc
= pc
;
283 vPluginManager
.reconnect(pc
);
287 private void startExternal()
289 if(pc
!= null && !running
)
290 if(!SwingUtilities
.isEventDispatchThread())
292 SwingUtilities
.invokeAndWait(new Thread() { public void run() { PCControl
.this.start(); }});
293 } catch(Exception e
) {
299 private void stopExternal()
301 if(pc
!= null && running
)
302 if(!SwingUtilities
.isEventDispatchThread())
304 SwingUtilities
.invokeAndWait(new Thread() { public void run() { PCControl
.this.stop(); }});
305 } catch(Exception e
) {
311 public boolean eci_state_save(String filename
)
314 (new Thread(new SaveStateTask(filename
, false))).start();
318 public boolean eci_movie_save(String filename
)
321 (new Thread(new SaveStateTask(filename
, true))).start();
325 public boolean eci_state_load(String filename
)
328 (new Thread(new LoadStateTask(filename
, LoadStateTask
.MODE_NORMAL
))).start();
332 public boolean eci_state_load_noevents(String filename
)
335 (new Thread(new LoadStateTask(filename
, LoadStateTask
.MODE_PRESERVE
))).start();
339 public boolean eci_movie_load(String filename
)
342 (new Thread(new LoadStateTask(filename
, LoadStateTask
.MODE_MOVIEONLY
))).start();
346 public boolean eci_pc_assemble()
349 (new Thread(new AssembleTask())).start();
353 public boolean eci_ram_dump_text(String filename
)
356 (new Thread(new RAMDumpTask(filename
, false))).start();
360 public boolean eci_ram_dump_binary(String filename
)
363 (new Thread(new RAMDumpTask(filename
, true))).start();
367 public void eci_trap_vretrace_start_on()
369 trapFlags
|= TraceTrap
.TRACE_STOP_VRETRACE_START
;
372 public void eci_trap_vretrace_start_off()
374 trapFlags
&= ~TraceTrap
.TRACE_STOP_VRETRACE_START
;
377 public void eci_trap_vretrace_end_on()
379 trapFlags
|= TraceTrap
.TRACE_STOP_VRETRACE_END
;
382 public void eci_trap_vretrace_end_off()
384 trapFlags
&= ~TraceTrap
.TRACE_STOP_VRETRACE_END
;
387 public void eci_trap_timed_disable()
389 this.imminentTrapTime
= -1;
392 public void eci_trap_timed(Long time
)
394 this.imminentTrapTime
= time
.longValue();
397 public void eci_pc_start()
402 public void eci_pc_stop()
407 public void eci_pccontrol_setwinpos(Integer x
, Integer y
)
409 moveWindow(this, x
.intValue(), y
.intValue(), 720, 50);
412 public void eci_sendevent(String clazz
, String
[] rargs
)
414 if(currentProject
.events
!= null) {
416 Class
<?
extends HardwareComponent
> x
= Class
.forName(clazz
).asSubclass(HardwareComponent
.class);
417 currentProject
.events
.addEvent(0L, x
, rargs
);
418 } catch(Exception e
) {
419 System
.err
.println("Error adding event: " + e
.getMessage());
424 public void eci_memory_read(Long address
, Integer size
)
426 if(currentProject
.pc
!= null) {
427 long addr
= address
.longValue();
428 long _size
= size
.intValue();
430 PhysicalAddressSpace addrSpace
;
431 if(addr
< 0 || addr
> 0xFFFFFFFFL
|| (_size
!= 1 && _size
!= 2 && _size
!= 4))
434 addrSpace
= (PhysicalAddressSpace
)currentProject
.pc
.getComponent(PhysicalAddressSpace
.class);
436 ret
= (long)addrSpace
.getByte((int)addr
) & 0xFF;
438 ret
= (long)addrSpace
.getWord((int)addr
) & 0xFFFF;
440 ret
= (long)addrSpace
.getDoubleWord((int)addr
) & 0xFFFFFFFFL
;
442 vPluginManager
.returnValue(ret
);
446 public void eci_memory_write(Long address
, Long value
, Integer size
)
448 if(currentProject
.pc
!= null) {
449 long addr
= address
.longValue();
450 long _size
= size
.intValue();
451 long _value
= value
.longValue();
452 PhysicalAddressSpace addrSpace
;
453 if(addr
< 0 || addr
> 0xFFFFFFFFL
|| (_size
!= 1 && _size
!= 2 && _size
!= 4))
456 addrSpace
= (PhysicalAddressSpace
)currentProject
.pc
.getComponent(PhysicalAddressSpace
.class);
458 addrSpace
.setByte((int)addr
, (byte)_value
);
460 addrSpace
.setWord((int)addr
, (short)_value
);
462 addrSpace
.setDoubleWord((int)addr
, (int)_value
);
466 public PCControl(Plugins manager
) throws Exception
470 this.willCleanup
= false;
471 shuttingDown
= false;
472 configDialog
= new PCConfigDialog();
474 menuManager
= new MenuManager();
476 menuManager
.setProfile(PROFILE_NO_PC
| PROFILE_STOPPED
);
478 menuManager
.addMenuItem("File→Assemble", this, "menuAssemble", null, PROFILE_STOPPED
);
479 menuManager
.addMenuItem("File→Start", this, "menuStart", null, PROFILE_STOPPED
| PROFILE_HAVE_PC
);
480 menuManager
.addMenuItem("File→Stop", this, "menuStop", null, PROFILE_RUNNING
);
481 menuManager
.addMenuItem("File→Change Run Authors", this, "menuChangeAuthors", null, PROFILE_HAVE_PC
);
482 menuManager
.addMenuItem("File→Reset", this, "menuReset", null, PROFILE_HAVE_PC
);
483 menuManager
.addMenuItem("File→Quit", this, "menuQuit", null, PROFILE_ALWAYS
);
484 menuManager
.addSelectableMenuItem("Breakpoints→Trap VRetrace Start", this, "menuVRetraceStart", null, false,
486 menuManager
.addSelectableMenuItem("Breakpoints→Trap VRetrace End", this, "menuVRetraceEnd", null, false,
488 menuManager
.addMenuItem("Snapshot→Save→Snapshot", this, "menuSave", new Object
[]{new Boolean(false)},
489 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
490 menuManager
.addMenuItem("Snapshot→Save→Movie", this, "menuSave", new Object
[]{new Boolean(true)},
491 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
492 menuManager
.addMenuItem("Snapshot→Save→Status Dump", this, "menuStatusDump", null,
493 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
494 menuManager
.addMenuItem("Snapshot→Load→Snapshot", this, "menuLoad",
495 new Object
[]{new Integer(LoadStateTask
.MODE_NORMAL
)}, PROFILE_STOPPED
);
496 menuManager
.addMenuItem("Snapshot→Load→Snapshot (preserve events)", this, "menuLoad",
497 new Object
[]{new Integer(LoadStateTask
.MODE_PRESERVE
)}, PROFILE_STOPPED
| PROFILE_EVENTS
);
498 menuManager
.addMenuItem("Snapshot→Load→Movie", this, "menuLoad",
499 new Object
[]{new Integer(LoadStateTask
.MODE_MOVIEONLY
)}, PROFILE_STOPPED
);
500 menuManager
.addMenuItem("Snapshot→RAM Dump→Hexadecimal", this, "menuRAMDump", new Object
[]{new Boolean(false)},
501 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
502 menuManager
.addMenuItem("Snapshot→RAM Dump→Binary", this, "menuRAMDump", new Object
[]{new Boolean(true)},
503 PROFILE_HAVE_PC
| PROFILE_STOPPED
);
504 menuManager
.addMenuItem("Snapshot→Truncate Event Stream", this, "menuTruncate", null,
505 PROFILE_STOPPED
| PROFILE_EVENTS
);
507 for(int i
= 0; i
< stopLabel
.length
; i
++) {
508 menuManager
.addSelectableMenuItem("Breakpoints→Timed Stops→" + stopLabel
[i
], this, "menuTimedStop",
509 null, (i
== 0), PROFILE_ALWAYS
);
511 imminentTrapTime
= -1;
513 menuManager
.addMenuItem("Drives→fda→<Empty>", this, "menuChangeDisk", new Object
[]{new Integer(0),
514 new Integer(-1)}, PROFILE_HAVE_PC
);
515 menuManager
.addMenuItem("Drives→fdb→<Empty>", this, "menuChangeDisk", new Object
[]{new Integer(1),
516 new Integer(-1)}, PROFILE_HAVE_PC
);
517 menuManager
.addMenuItem("Drives→CD-ROM→<Empty>", this, "menuChangeDisk", new Object
[]{new Integer(2),
518 new Integer(-1)}, PROFILE_HAVE_PC
| PROFILE_CDROM
);
519 menuManager
.addMenuItem("Drives→Add image", this, "menuAddDisk", null, PROFILE_HAVE_PC
);
521 menuManager
.addMenuItem("Debug→Hacks→NO_FPU", this, "menuNOFPU", null, PROFILE_HAVE_PC
);
522 menuManager
.addMenuItem("Debug→Hacks→VGA_DRAW", this, "menuVGADRAW", null, PROFILE_HAVE_PC
);
524 disks
= new HashSet
<String
>();
525 currentProject
= new PC
.PCFullStatus();
527 this.vPluginManager
= manager
;
529 setJMenuBar(menuManager
.getMainBar());
533 setDefaultCloseOperation(JFrame
.EXIT_ON_CLOSE
);
535 catch (AccessControlException e
)
537 System
.err
.println("Error: Not able to add some components to frame: " + e
.getMessage());
540 snapshotFileChooser
= new JFileChooser(System
.getProperty("user.dir"));
542 getContentPane().validate();
543 setBounds(150, 150, 720, 50);
548 public void menuAssemble(String i
, Object
[] args
)
550 (new Thread(new AssembleTask())).start();
553 public void menuStart(String i
, Object
[] args
)
558 public void menuStop(String i
, Object
[] args
)
563 public void menuReset(String i
, Object
[] args
)
568 public void menuNOFPU(String i
, Object
[] args
)
573 public void menuVGADRAW(String i
, Object
[] args
)
578 public void menuQuit(String i
, Object
[] args
)
580 vPluginManager
.shutdownEmulator();
583 public void menuVRetraceStart(String i
, Object
[] args
)
585 trapFlags ^
= TraceTrap
.TRACE_STOP_VRETRACE_START
;
586 menuManager
.setSelected("Breakpoints→Trap VRetrace Start",
587 (trapFlags
& TraceTrap
.TRACE_STOP_VRETRACE_START
) == TraceTrap
.TRACE_STOP_VRETRACE_START
);
590 public void menuVRetraceEnd(String i
, Object
[] args
)
592 trapFlags ^
= TraceTrap
.TRACE_STOP_VRETRACE_END
;
593 menuManager
.setSelected("Breakpoints→Trap VRetrace End",
594 (trapFlags
& TraceTrap
.TRACE_STOP_VRETRACE_END
) == TraceTrap
.TRACE_STOP_VRETRACE_END
);
597 public void menuTimedStop(String i
, Object
[] args
)
599 for(int j
= 0; j
< stopLabel
.length
; j
++) {
600 String label
= "Breakpoints→Timed Stops→" + stopLabel
[j
];
601 if(i
.equals(label
)) {
602 this.imminentTrapTime
= stopTime
[j
];
603 menuManager
.select(label
);
605 menuManager
.unselect(label
);
609 public void menuSave(String i
, Object
[] args
)
611 (new Thread(new SaveStateTask(((Boolean
)args
[0]).booleanValue()))).start();
614 public void menuStatusDump(String i
, Object
[] args
)
616 (new Thread(new StatusDumpTask())).start();
619 public void menuLoad(String i
, Object
[] args
)
621 (new Thread(new LoadStateTask(((Integer
)args
[0]).intValue()))).start();
624 public void menuRAMDump(String i
, Object
[] args
)
626 (new Thread(new RAMDumpTask(((Boolean
)args
[0]).booleanValue()))).start();
629 public void menuTruncate(String i
, Object
[] args
)
631 currentProject
.events
.truncateEventStream();
634 public void menuChangeDisk(String i
, Object
[] args
)
636 changeFloppy(((Integer
)args
[0]).intValue(), ((Integer
)args
[1]).intValue());
639 public void menuWriteProtect(String i
, Object
[] args
)
641 int disk
= ((Integer
)args
[0]).intValue();
642 writeProtect(disk
, menuManager
.isSelected(i
));
643 DiskImageSet imageSet
= pc
.getDisks();
644 menuManager
.setSelected(i
, imageSet
.lookupDisk(disk
).isReadOnly());
647 public void menuAddDisk(String i
, Object
[] args
)
649 (new Thread(new AddDiskTask())).start();
652 public void menuChangeAuthors(String i
, Object
[] args
)
654 (new Thread(new ChangeAuthorsTask())).start();
657 public void setSize(Dimension d
)
659 super.setSize(new Dimension(720, 400));
662 public synchronized void start()
664 vPluginManager
.pcStarted();
669 private String
prettyPrintTime(long ts
)
674 s
= s
+ "" + (ts
/ 1000000000) + " ";
676 s
= s
+ "" + (ts
% 1000000000 / 100000000);
678 s
= s
+ "" + (ts
% 100000000 / 10000000);
680 s
= s
+ "" + (ts
% 10000000 / 1000000) + " ";
682 s
= s
+ "" + (ts
% 1000000 / 100000);
684 s
= s
+ "" + (ts
% 100000 / 10000);
686 s
= s
+ "" + (ts
% 10000 / 1000) + " ";
688 s
= s
+ "" + (ts
% 1000 / 100);
690 s
= s
+ "" + (ts
% 100 / 10);
691 s
= s
+ "" + (ts
% 10);
695 protected synchronized void stopNoWait()
698 vPluginManager
.pcStopped();
699 Clock sysClock
= (Clock
)pc
.getComponent(Clock
.class);
700 System
.err
.println("Notice: PC emulation stopped (at time sequence value " +
701 prettyPrintTime(sysClock
.getTime()) + ")");
704 public synchronized void stop()
707 pc
.getTraceTrap().doPotentialTrap(TraceTrap
.TRACE_STOP_IMMEDIATE
);
709 System
.err
.println("Informational: Waiting for PC to halt...");
713 } catch(Exception e
) {
719 public JScrollPane
getMonitorPane()
724 protected void reset()
729 public synchronized boolean isRunning()
734 private void changeFloppy(int drive
, int image
)
738 PC
.DiskChanger changer
= (PC
.DiskChanger
)pc
.getComponent(PC
.DiskChanger
.class);
739 changer
.changeFloppyDisk(drive
, image
);
740 } catch (Exception e
) {
741 System
.err
.println("Error: Failed to change disk");
742 errorDialog(e
, "Failed to change disk", null, "Dismiss");
746 private void writeProtect(int image
, boolean state
)
750 PC
.DiskChanger changer
= (PC
.DiskChanger
)pc
.getComponent(PC
.DiskChanger
.class);
751 changer
.wpFloppyDisk(image
, state
);
752 } catch (Exception e
) {
753 System
.err
.println("Error: Failed to change floppy write protect");
754 errorDialog(e
, "Failed to write (un)protect floppy", null, "Dismiss");
758 private class LoadStateTask
extends AsyncGUITask
765 private static final int MODE_NORMAL
= 1;
766 private static final int MODE_PRESERVE
= 2;
767 private static final int MODE_MOVIEONLY
= 3;
769 public LoadStateTask(int mode
)
771 oTime
= System
.currentTimeMillis();
774 pw
= new PleaseWait("Loading savestate...");
777 public LoadStateTask(String name
, int mode
)
780 choosen
= new File(name
);
783 protected void runPrepare()
785 PCControl
.this.setEnabled(false);
786 if(choosen
== null) {
788 if(_mode
== MODE_PRESERVE
)
789 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "LOAD JPC-RR Snapshot (PE)");
790 else if(_mode
== MODE_MOVIEONLY
)
791 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "LOAD JPC-RR Snapshot (MO)");
793 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "LOAD JPC-RR Snapshot");
794 choosen
= snapshotFileChooser
.getSelectedFile();
802 protected void runFinish()
806 connectPC(pc
= currentProject
.pc
);
807 System
.err
.println("Informational: Loadstate done");
808 } catch(Exception e
) {
814 errorDialog(caught
, "Load savestate failed", PCControl
.this, "Dismiss");
816 PCControl
.this.setEnabled(true);
817 System
.err
.println("Total save time: " + (System
.currentTimeMillis() - oTime
) + "ms.");
818 PCControl
.this.vPluginManager
.signalCommandCompletion();
821 protected void runTask()
827 System
.err
.println("Informational: Loading a snapshot of JPC-RR");
828 long times1
= System
.currentTimeMillis();
829 JRSRArchiveReader reader
= new JRSRArchiveReader(choosen
.getAbsolutePath());
831 PC
.PCFullStatus fullStatus
= PC
.loadSavestate(reader
, (_mode
== MODE_PRESERVE
) ?
832 currentProject
.events
: null, (_mode
== MODE_MOVIEONLY
));
833 if(currentProject
.projectID
!= null && fullStatus
.projectID
.equals(currentProject
.projectID
))
834 if(currentProject
.rerecords
> fullStatus
.rerecords
)
835 fullStatus
.rerecords
= currentProject
.rerecords
+ 1;
837 fullStatus
.rerecords
++;
839 fullStatus
.rerecords
++;
841 currentProject
= fullStatus
;
844 long times2
= System
.currentTimeMillis();
845 System
.err
.println("Informational: Loadstate complete (" + (times2
- times1
) + "ms).");
846 } catch(Exception e
) {
852 private class SaveStateTask
extends AsyncGUITask
860 public SaveStateTask(boolean movie
)
862 oTime
= System
.currentTimeMillis();
865 pw
= new PleaseWait("Saving savestate...");
868 public SaveStateTask(String name
, boolean movie
)
871 choosen
= new File(name
);
874 protected void runPrepare()
876 PCControl
.this.setEnabled(false);
877 if(choosen
== null) {
878 int returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, movieOnly ?
"Save JPC-RR Movie" :
879 "Save JPC-RR Snapshot");
880 choosen
= snapshotFileChooser
.getSelectedFile();
888 protected void runFinish()
892 errorDialog(caught
, "Saving savestate failed", PCControl
.this, "Dismiss");
894 PCControl
.this.setEnabled(true);
895 System
.err
.println("Total save time: " + (System
.currentTimeMillis() - oTime
) + "ms.");
896 PCControl
.this.vPluginManager
.signalCommandCompletion();
899 protected void runTask()
904 JRSRArchiveWriter writer
= null;
907 System
.err
.println("Informational: Savestating...");
908 long times1
= System
.currentTimeMillis();
909 choosen
.renameTo(new File(choosen
.getAbsolutePath() + ".backup"));
910 writer
= new JRSRArchiveWriter(choosen
.getAbsolutePath());
911 PC
.saveSavestate(writer
, currentProject
, movieOnly
);
913 long times2
= System
.currentTimeMillis();
914 System
.err
.println("Informational: Savestate complete (" + (times2
- times1
) + "ms).");
915 } catch(Exception e
) {
917 try { writer
.rollback(); } catch(Exception f
) {}
923 private class StatusDumpTask
extends AsyncGUITask
929 public StatusDumpTask()
932 pw
= new PleaseWait("Saving status dump...");
935 public StatusDumpTask(String name
)
938 choosen
= new File(name
);
941 protected void runPrepare()
943 PCControl
.this.setEnabled(false);
944 if(choosen
== null) {
945 int returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "Save Status dump");
946 choosen
= snapshotFileChooser
.getSelectedFile();
954 protected void runFinish()
958 errorDialog(caught
, "Status dump failed", PCControl
.this, "Dismiss");
960 PCControl
.this.setEnabled(true);
961 PCControl
.this.vPluginManager
.signalCommandCompletion();
964 protected void runTask()
970 OutputStream outb
= new BufferedOutputStream(new FileOutputStream(choosen
));
971 PrintStream out
= new PrintStream(outb
, false, "UTF-8");
972 StatusDumper sd
= new StatusDumper(out
);
976 System
.err
.println("Informational: Dumped " + sd
.dumpedObjects() + " objects");
977 } catch(Exception e
) {
983 private class RAMDumpTask
extends AsyncGUITask
990 public RAMDumpTask(boolean binFlag
)
993 pw
= new PleaseWait("Saving RAM dump...");
997 public RAMDumpTask(String name
, boolean binFlag
)
1000 choosen
= new File(name
);
1003 protected void runPrepare()
1005 PCControl
.this.setEnabled(false);
1006 if(choosen
== null) {
1009 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "Save RAM dump");
1011 returnVal
= snapshotFileChooser
.showDialog(PCControl
.this, "Save RAM hexdump");
1012 choosen
= snapshotFileChooser
.getSelectedFile();
1020 protected void runFinish()
1023 if(caught
!= null) {
1024 errorDialog(caught
, "RAM dump failed", PCControl
.this, "Dismiss");
1026 PCControl
.this.setEnabled(true);
1027 PCControl
.this.vPluginManager
.signalCommandCompletion();
1030 protected void runTask()
1036 OutputStream outb
= new BufferedOutputStream(new FileOutputStream(choosen
));
1037 byte[] pagebuf
= new byte[4096];
1038 PhysicalAddressSpace addr
= (PhysicalAddressSpace
)pc
.getComponent(PhysicalAddressSpace
.class);
1039 int lowBound
= addr
.findFirstRAMPage(0);
1040 int firstUndumped
= 0;
1043 while(lowBound
>= 0) {
1044 for(; firstUndumped
< lowBound
; firstUndumped
++)
1045 dumpPage(outb
, firstUndumped
, null);
1046 addr
.readRAMPage(firstUndumped
++, pagebuf
);
1047 dumpPage(outb
, lowBound
, pagebuf
);
1049 highBound
= lowBound
+ 1;
1050 lowBound
= addr
.findFirstRAMPage(++lowBound
);
1053 System
.err
.println("Informational: Dumped machine RAM (" + highBound
+ " pages examined, " +
1054 present
+ " pages present).");
1055 } catch(Exception e
) {
1060 private byte charForHex(int hvalue
)
1063 return (byte)(hvalue
+ 48);
1064 else if(hvalue
> 9 && hvalue
< 16)
1065 return (byte)(hvalue
+ 55);
1067 System
.err
.println("Unknown hex value: " + hvalue
+ ".");
1071 private void dumpPage(OutputStream stream
, int pageNo
, byte[] buffer
) throws IOException
1074 pageNo
= pageNo
& 0xFFFFF; //Cut page numbers out of range.
1075 if(!binary
&& buffer
== null)
1076 return; //Don't dump null pages in non-binary mode.
1078 pageBufSize
= 4096; //Binary page buffer is 4096 bytes.
1080 pageBufSize
= 14592; //Hexdump page buffer is 14592 bytes.
1081 byte[] outputPage
= new byte[pageBufSize
];
1082 if(buffer
!= null && binary
) {
1083 System
.arraycopy(buffer
, 0, outputPage
, 0, 4096);
1084 } else if(buffer
!= null) { //Hex mode
1085 for(int i
= 0; i
< 256; i
++) {
1086 for(int j
= 0; j
< 57; j
++) {
1088 outputPage
[57 * i
+ j
] = charForHex((pageNo
>>> (4 * (4 - j
))) & 0xF);
1090 outputPage
[57 * i
+ j
] = charForHex(i
/ 16);
1092 outputPage
[57 * i
+ j
] = charForHex(i
% 16);
1094 outputPage
[57 * i
+ j
] = 48;
1096 outputPage
[57 * i
+ j
] = 10;
1098 outputPage
[57 * i
+ j
] = 32;
1100 outputPage
[57 * i
+ j
] = charForHex(((int)buffer
[16 * i
+ j
/ 3 - 3] & 0xFF) / 16);
1102 outputPage
[57 * i
+ j
] = charForHex(buffer
[16 * i
+ j
/ 3 - 3] & 0xF);
1104 System
.err
.println("Error: dumpPage: unhandled j = " + j
+ ".");
1108 stream
.write(outputPage
);
1112 private class AssembleTask
extends AsyncGUITask
1118 public AssembleTask()
1120 pw
= new PleaseWait("Assembling PC...");
1124 protected void runPrepare()
1126 PCControl
.this.setEnabled(false);
1128 configDialog
.popUp();
1129 } catch(Exception e
) {
1134 protected void runFinish()
1136 if(caught
== null && !canceled
) {
1138 currentProject
.projectID
= randomHexes(24);
1139 currentProject
.rerecords
= 0;
1140 currentProject
.events
= new EventRecorder();
1141 currentProject
.events
.attach(pc
, null);
1142 currentProject
.savestateID
= null;
1143 currentProject
.extraHeaders
= null;
1145 } catch(Exception e
) {
1151 if(caught
!= null) {
1152 errorDialog(caught
, "PC Assembly failed", PCControl
.this, "Dismiss");
1154 PCControl
.this.setEnabled(true);
1155 PCControl
.this.vPluginManager
.signalCommandCompletion();
1158 protected void runTask()
1160 PC
.PCHardwareInfo hw
= configDialog
.waitClose();
1166 SwingUtilities
.invokeAndWait(new Thread() { public void run() { pw
.popUp(); }});
1167 } catch(Exception e
) {
1171 pc
= PC
.createPC(hw
);
1172 } catch(Exception e
) {
1178 private class AddDiskTask
extends AsyncGUITask
1184 public AddDiskTask()
1187 dd
= new NewDiskDialog();
1188 PCControl
.this.setEnabled(false);
1191 protected void runPrepare()
1195 protected void runFinish()
1197 if(caught
!= null) {
1198 errorDialog(caught
, "Adding disk failed", PCControl
.this, "Dismiss");
1200 PCControl
.this.setEnabled(true);
1203 } catch(Exception e
) {
1204 errorDialog(e
, "Failed to update disk menus", null, "Dismiss");
1206 PCControl
.this.vPluginManager
.signalCommandCompletion();
1209 protected void runTask()
1211 NewDiskDialog
.Response res
= dd
.waitClose();
1218 pc
.getDisks().addDisk(img
= new DiskImage(res
.diskFile
, false));
1219 img
.setName(res
.diskName
);
1220 } catch(Exception e
) {
1226 private class ChangeAuthorsTask
extends AsyncGUITask
1232 public ChangeAuthorsTask()
1236 String
[] authorNames
= null;
1239 if(currentProject
!= null && currentProject
.extraHeaders
!= null) {
1240 headers
= currentProject
.extraHeaders
.length
;
1241 for(int i
= 0; i
< headers
; i
++)
1242 if(currentProject
.extraHeaders
[i
][0].equals("AUTHORS"))
1243 authors
+= (currentProject
.extraHeaders
[i
].length
- 1);
1247 authorNames
= new String
[authors
];
1248 for(int i
= 0; i
< headers
; i
++) {
1249 if(currentProject
.extraHeaders
[i
][0].equals("AUTHORS")) {
1250 System
.arraycopy(currentProject
.extraHeaders
[i
], 1, authorNames
, j
,
1251 currentProject
.extraHeaders
[i
].length
- 1);
1252 j
+= (currentProject
.extraHeaders
[i
].length
- 1);
1257 ad
= new AuthorsDialog(authorNames
);
1258 PCControl
.this.setEnabled(false);
1261 protected void runPrepare()
1265 protected void runFinish()
1267 if(caught
!= null) {
1268 errorDialog(caught
, "Changing authors failed", PCControl
.this, "Dismiss");
1270 PCControl
.this.setEnabled(true);
1271 PCControl
.this.vPluginManager
.signalCommandCompletion();
1274 protected void runTask()
1276 AuthorsDialog
.Response res
= ad
.waitClose();
1285 if(currentProject
!= null && currentProject
.extraHeaders
!= null) {
1286 headers
= currentProject
.extraHeaders
.length
;
1287 for(int i
= 0; i
< headers
; i
++)
1288 if(currentProject
.extraHeaders
[i
][0].equals("AUTHORS"))
1291 if(res
.authors
!= null) {
1292 for(int i
= 0; i
< res
.authors
.length
; i
++)
1293 if(res
.authors
[i
] != null)
1296 if(headers
== oldAuthors
&& newAuthors
== 0) {
1297 //Remove all extra headers.
1298 currentProject
.extraHeaders
= null;
1302 String
[][] newHeaders
= new String
[headers
+ newAuthors
- oldAuthors
][];
1305 //Copy the non-authors headers.
1306 if(currentProject
!= null && currentProject
.extraHeaders
!= null) {
1307 for(int i
= 0; i
< headers
; i
++)
1308 if(!currentProject
.extraHeaders
[i
][0].equals("AUTHORS"))
1309 newHeaders
[writePos
++] = currentProject
.extraHeaders
[i
];
1311 if(res
.authors
!= null)
1312 for(int i
= 0; i
< res
.authors
.length
; i
++)
1313 if(res
.authors
[i
] != null)
1314 newHeaders
[writePos
++] = new String
[]{"AUTHORS", res
.authors
[i
]};
1315 currentProject
.extraHeaders
= newHeaders
;
1316 } catch(Exception e
) {