Add emuname for telling apart multiple emulators
[jpcrr.git] / org / jpc / plugins / PCControl.java
blob9f988c8d8188c0b203e67847e21e9231a5197492
1 /*
2 JPC-RR: A x86 PC Hardware Emulator
3 Release 1
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;
33 import java.io.*;
34 import java.util.*;
35 import java.security.AccessControlException;
36 import javax.swing.*;
37 import java.awt.dnd.*;
38 import java.awt.datatransfer.*;
39 import javax.swing.border.EtchedBorder;
41 import org.jpc.emulator.HardwareComponent;
42 import org.jpc.emulator.PC;
43 import org.jpc.emulator.EventRecorder;
44 import org.jpc.emulator.TraceTrap;
45 import org.jpc.emulator.memory.PhysicalAddressSpace;
46 import org.jpc.emulator.StatusDumper;
47 import org.jpc.emulator.Clock;
48 import org.jpc.emulator.VGADigitalOut;
49 import org.jpc.diskimages.BlockDevice;
50 import org.jpc.diskimages.DiskImageSet;
51 import org.jpc.diskimages.DiskImage;
52 import org.jpc.pluginsaux.PleaseWait;
53 import org.jpc.pluginsaux.AsyncGUITask;
54 import org.jpc.pluginsaux.NewDiskDialog;
55 import org.jpc.pluginsaux.AuthorsDialog;
56 import org.jpc.pluginsaux.PCConfigDialog;
57 import org.jpc.pluginsaux.DumpControlDialog;
58 import org.jpc.pluginsaux.MenuManager;
59 import org.jpc.pluginsaux.PCMonitorPanel;
60 import org.jpc.pluginsaux.PCMonitorPanelEmbedder;
61 import org.jpc.pluginsaux.ImportDiskImage;
62 import org.jpc.Misc;
63 import org.jpc.pluginsbase.*;
64 import org.jpc.jrsr.*;
66 import static org.jpc.Misc.randomHexes;
67 import static org.jpc.Misc.errorDialog;
68 import static org.jpc.Misc.callShowOptionDialog;
69 import static org.jpc.Misc.moveWindow;
70 import static org.jpc.Misc.parseStringToComponents;
71 import static org.jpc.Misc.nextParseLine;
72 import static org.jpc.Misc.renameFile;
74 public class PCControl implements Plugin, PCMonitorPanelEmbedder
76 private static long PROFILE_ALWAYS = 0;
77 private static long PROFILE_NO_PC = 1;
78 private static long PROFILE_HAVE_PC = 2;
79 private static long PROFILE_STOPPED = 4;
80 private static long PROFILE_RUNNING = 8;
81 private static long PROFILE_EVENTS = 16;
82 private static long PROFILE_CDROM = 32;
83 private static String SAVESTATE_LABEL = "Savestating...";
84 private static String LOADSTATE_LABEL = "Loadstating...";
85 private static String RAMDUMP_LABEL = "Dumping RAM...";
86 private static String STATUSDUMP_LABEL = "Dumping status...";
87 private static String DUMPCONTROL_LABEL = "Dump control open...";
88 private static String ASSEMBLE_LABEL = "Assembling system...";
89 private static String ADDDISK_LABEL = "Adding new disk...";
90 private static String CHANGEAUTHORS_LABEL = "Changing run authors...";
92 private static final long serialVersionUID = 8;
93 private Plugins vPluginManager;
95 private JFrame window;
96 private JFileChooser snapshotFileChooser;
97 private DropTarget dropTarget;
98 private LoadstateDropTarget loadstateDropTarget;
100 private Set<String> disks;
102 protected PC pc;
104 private int trapFlags;
106 private volatile boolean running;
107 private volatile boolean waiting;
108 private boolean uncompressedSave;
109 private static final long[] stopTime;
110 private static final String[] stopLabel;
111 private volatile long imminentTrapTime;
112 private boolean shuttingDown;
113 private int nativeWidth;
114 private int nativeHeight;
115 private PCConfigDialog configDialog;
116 private DumpControlDialog dumpDialog;
117 private MenuManager menuManager;
118 private Map<String, List<String[]> > extraActions;
119 private PCMonitorPanel panel;
120 private JLabel statusBar;
121 private volatile int currentResolutionWidth;
122 private volatile int currentResolutionHeight;
123 private volatile Runnable taskToDo;
124 private volatile String taskLabel;
125 private boolean cycleDone;
127 private PC.PCFullStatus currentProject;
129 class LoadstateDropTarget implements DropTargetListener
131 public void dragEnter(DropTargetDragEvent e) {}
132 public void dragOver(DropTargetDragEvent e) {}
133 public void dragExit(DropTargetEvent e) {}
134 public void dropActionChanged(DropTargetDragEvent e) {}
136 public void drop(DropTargetDropEvent e)
138 if(running) {
139 e.rejectDrop();
140 return;
142 e.acceptDrop(DnDConstants.ACTION_COPY);
143 int i = 0;
144 for(DataFlavor f : e.getCurrentDataFlavors()) {
145 try {
146 Transferable t = e.getTransferable();
147 Object d = t.getTransferData(f);
148 if(f.isMimeTypeEqual("text/uri-list") && d.getClass() == String.class) {
149 String url = (String)d;
150 if(url.indexOf(10) >= 0) {
151 callShowOptionDialog(window, "Hey, only single file at time!",
152 "DnD error", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null,
153 new String[]{"Dismiss"}, "Dismiss");
154 e.dropComplete(false);
155 return;
157 e.dropComplete(handleURLDropped(url));
158 return;
160 } catch(Exception ex) {
161 errorDialog(ex, "Failed to get DnD data", null, "Dismiss");
162 e.dropComplete(false);
163 return;
166 for(DataFlavor f : e.getCurrentDataFlavors()) {
167 i = 0;
168 try {
169 i++;
170 Transferable t = e.getTransferable();
171 Object d = t.getTransferData(f);
172 System.err.println("Notice: Format #" + i + ":" + d.getClass().getName() + "(" + f + ")");
173 } catch(Exception ex) {
174 System.err.println("Notice: Format #" + i + ": <ERROR>(" + f + ")");
177 callShowOptionDialog(window, "Can't recognize file to load from drop (debugging information dumped to console).",
178 "DnD error", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null,
179 new String[]{"Dismiss"}, "Dismiss");
180 e.dropComplete(false);
184 private boolean handleURLDropped(String url)
186 if(!url.startsWith("file:///")) {
187 callShowOptionDialog(window, "Can't load remote resource.",
188 "DnD error", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null,
189 new String[]{"Dismiss"}, "Dismiss");
190 return false;
192 url = url.substring(7);
193 setTask(new LoadStateTask(url, LoadStateTask.MODE_NORMAL), LOADSTATE_LABEL);
194 return true;
197 static
199 stopTime = new long[] {-1, 0, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000,
200 5000000, 10000000, 20000000, 50000000, 100000000, 200000000, 500000000, 1000000000, 2000000000,
201 5000000000L, 10000000000L, 20000000000L, 50000000000L};
202 stopLabel = new String[] {"(unbounded)", "(singlestep)", "1µs", "2µs", "5µs", "10µs", "20µs", "50µs", "100µs",
203 "200µs", "500µs","1ms", "2ms", "5ms", "10ms", "20ms", "50ms", "100ms", "200ms", "500ms", "1s", "2s", "5s",
204 "10s", "20s", "50s"};
207 public boolean systemShutdown()
209 if(!running || pc == null)
210 return true;
211 //We are running. Do the absolute minimum since we are running in very delicate context.
212 shuttingDown = true;
213 stop();
214 return true;
217 public void reconnect(PC pc)
219 pcStopping(); //Do the equivalent effects.
220 panel.reconnect(pc);
221 updateStatusBar();
222 dumpDialog.clearDumps();
225 public void notifySizeChange(int w, int h)
227 final int w2 = w;
228 final int h2 = h;
230 SwingUtilities.invokeLater(new Runnable() { public void run() {
231 window.pack();
232 Dimension d = window.getSize();
233 nativeWidth = d.width;
234 nativeHeight = d.height;
235 currentResolutionWidth = w2;
236 currentResolutionHeight = h2;
237 updateStatusBarEventThread();
238 }});
241 public void notifyFrameReceived(int w, int h)
243 currentResolutionWidth = w;
244 currentResolutionHeight = h;
245 updateStatusBar();
248 private void setTrapFlags()
250 pc.getTraceTrap().setTrapFlags(trapFlags);
253 public void pcStarting()
255 long profile = PROFILE_HAVE_PC | PROFILE_RUNNING;
256 if(currentProject != null && currentProject.events != null);
257 profile |= PROFILE_EVENTS;
258 if(pc.getCDROMIndex() >= 0)
259 profile |= PROFILE_CDROM;
261 menuManager.setProfile(profile);
263 if (running)
264 return;
266 setTrapFlags();
268 Clock sysClock = (Clock)pc.getComponent(Clock.class);
269 long current = sysClock.getTime();
270 if(imminentTrapTime > 0) {
271 pc.getTraceTrap().setTrapTime(current + imminentTrapTime);
272 } else if(imminentTrapTime == 0) {
273 //Hack: We set trace trap to trap immediately. It comes too late to abort next instruction, but
274 //early enough to abort one after that.
275 pc.getTraceTrap().setTrapTime(current);
277 if(currentProject.events != null)
278 currentProject.events.setPCRunStatus(true);
281 public void pcStopping()
283 if(currentProject.events != null)
284 currentProject.events.setPCRunStatus(false);
285 if(shuttingDown)
286 return; //Don't mess with UI when shutting down.
289 long profile = PROFILE_STOPPED;
290 if(pc != null)
291 profile |= PROFILE_HAVE_PC;
292 else
293 profile |= PROFILE_NO_PC;
294 if(currentProject != null && currentProject.events != null);
295 profile |= PROFILE_EVENTS;
296 if(pc.getCDROMIndex() >= 0)
297 profile |= PROFILE_CDROM;
299 menuManager.setProfile(profile);
300 updateStatusBar();
302 try {
303 updateDisks();
304 } catch(Exception e) {
305 errorDialog(e, "Failed to update disk menus", null, "Dismiss");
308 if(pc != null) {
309 pc.getTraceTrap().clearTrapTime();
310 pc.getTraceTrap().getAndClearTrapActive();
314 private String diskNameByIdx(int idx)
316 return pc.getDisks().lookupDisk(idx).getName();
319 private void updateDisks() throws Exception
321 for(String x : disks)
322 menuManager.removeMenuItem(x);
324 disks.clear();
326 if(pc == null)
327 return;
329 DiskImageSet imageSet = pc.getDisks();
330 int[] floppies = imageSet.diskIndicesByType(BlockDevice.Type.FLOPPY);
331 int[] cdroms = imageSet.diskIndicesByType(BlockDevice.Type.CDROM);
333 for(int i = 0; i < floppies.length; i++) {
334 String name = diskNameByIdx(floppies[i]);
335 menuManager.addMenuItem("Drives→fda→" + name, this, "menuChangeDisk", new Object[]{new Integer(0),
336 new Integer(floppies[i])}, PROFILE_HAVE_PC);
337 menuManager.addMenuItem("Drives→fdb→" + name, this, "menuChangeDisk", new Object[]{new Integer(1),
338 new Integer(floppies[i])}, PROFILE_HAVE_PC);
339 menuManager.addSelectableMenuItem("Drives→Write Protect→" + name, this, "menuWriteProtect",
340 new Object[]{new Integer(floppies[i])}, imageSet.lookupDisk(floppies[i]).isReadOnly(),
341 PROFILE_HAVE_PC);
342 disks.add("Drives→fda→" + name);
343 disks.add("Drives→fdb→" + name);
344 disks.add("Drives→Write Protect→" + name);
347 for(int i = 0; i < cdroms.length; i++) {
348 String name = diskNameByIdx(cdroms[i]);
349 menuManager.addMenuItem("Drives→CD-ROM→" + name, this, "menuChangeDisk", new Object[]{new Integer(1),
350 new Integer(cdroms[i])}, PROFILE_HAVE_PC | PROFILE_CDROM);
351 disks.add("Drives→CD-ROM→" + name);
355 private synchronized boolean setTask(Runnable task, String label)
357 boolean run = running;
358 if(run || taskToDo != null)
359 return false; //Can't do tasks with PC running or existing task.
360 taskToDo = task;
361 taskLabel = label;
362 notifyAll();
363 updateStatusBar();
364 return true;
367 public void main()
369 boolean wasRunning = false;
370 while(true) { //We will be killed by JVM.
371 //Wait for us to become runnable again.
372 while((!running || pc == null) && taskToDo == null) {
373 if(!running && wasRunning && pc != null)
374 pc.stop();
375 wasRunning = running;
376 try {
377 synchronized(this) {
378 if((running && pc != null) || taskToDo != null)
379 continue;
380 waiting = true;
381 notifyAll();
382 wait();
383 waiting = false;
385 } catch(Exception e) {
389 if(running && !wasRunning)
390 pc.start();
391 wasRunning = running;
393 if(taskToDo != null) {
394 taskToDo.run();
395 taskToDo = null;
396 updateStatusBar();
397 continue;
400 try {
401 pc.execute();
402 if(pc.getHitTraceTrap()) {
403 if(pc.getAndClearTripleFaulted())
404 callShowOptionDialog(window, "CPU shut itself down due to triple fault. Rebooting the system.",
405 "Triple fault!", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null,
406 new String[]{"Dismiss"}, "Dismiss");
407 SwingUtilities.invokeAndWait(new Thread() { public void run() { stopNoWait(); }});
408 running = false;
409 doCycle(pc);
411 } catch (Exception e) {
412 running = false;
413 doCycle(pc);
414 errorDialog(e, "Hardware emulator internal error", window, "Dismiss");
415 try {
416 SwingUtilities.invokeAndWait(new Thread() { public void run() { stopNoWait(); }});
417 } catch (Exception f) {
424 public void connectPC(PC pc)
426 currentProject.pc = pc;
427 vPluginManager.reconnect(pc);
428 this.pc = pc;
431 private void startExternal()
433 if(pc != null && !running)
434 if(!SwingUtilities.isEventDispatchThread())
435 try {
436 SwingUtilities.invokeAndWait(new Thread() { public void run() { PCControl.this.start(); }});
437 } catch(Exception e) {
439 else
440 start();
443 private void stopExternal()
445 if(pc != null && running)
446 if(!SwingUtilities.isEventDispatchThread())
447 try {
448 SwingUtilities.invokeAndWait(new Thread() { public void run() { PCControl.this.stop(); }});
449 } catch(Exception e) {
451 else
452 stop();
455 public boolean eci_state_save(String filename)
457 return setTask(new SaveStateTask(filename, false), SAVESTATE_LABEL);
460 public boolean eci_state_dump(String filename)
462 return setTask(new StatusDumpTask(filename), STATUSDUMP_LABEL);
465 public boolean eci_movie_save(String filename)
467 return setTask(new SaveStateTask(filename, true), SAVESTATE_LABEL);
470 public boolean eci_state_load(String filename)
472 return setTask(new LoadStateTask(filename, LoadStateTask.MODE_NORMAL), LOADSTATE_LABEL);
475 public boolean eci_state_load_noevents(String filename)
477 return setTask(new LoadStateTask(filename, LoadStateTask.MODE_PRESERVE), LOADSTATE_LABEL);
480 public boolean eci_movie_load(String filename)
482 return setTask(new LoadStateTask(filename, LoadStateTask.MODE_MOVIEONLY), LOADSTATE_LABEL);
485 public boolean eci_pc_assemble()
487 return setTask(new AssembleTask(), ASSEMBLE_LABEL);
490 public boolean eci_ram_dump_text(String filename)
492 return setTask(new RAMDumpTask(filename, false), RAMDUMP_LABEL);
495 public boolean eci_ram_dump_binary(String filename)
497 return setTask(new RAMDumpTask(filename, true), RAMDUMP_LABEL);
500 public void eci_trap_vretrace_start_on()
502 trapFlags |= TraceTrap.TRACE_STOP_VRETRACE_START;
505 public void eci_trap_vretrace_start_off()
507 trapFlags &= ~TraceTrap.TRACE_STOP_VRETRACE_START;
510 public void eci_trap_vretrace_end_on()
512 trapFlags |= TraceTrap.TRACE_STOP_VRETRACE_END;
515 public void eci_trap_vretrace_end_off()
517 trapFlags &= ~TraceTrap.TRACE_STOP_VRETRACE_END;
520 public void eci_trap_timed_disable()
522 this.imminentTrapTime = -1;
525 public void eci_trap_timed(Long time)
527 this.imminentTrapTime = time.longValue();
530 public void eci_pc_start()
532 startExternal();
535 public void eci_pc_stop()
537 stopExternal();
540 public void eci_pccontrol_setwinpos(Integer x, Integer y)
542 moveWindow(window, x.intValue(), y.intValue(), nativeWidth, nativeHeight);
545 public void eci_sendevent(String clazz, String[] rargs)
547 System.err.println("Event to: '" + clazz + "':");
548 for(int i = 0; i < rargs.length; i++) {
549 System.err.println("rargs[" + i + "]: '" + rargs[i] + "'.");
551 if(currentProject.events != null) {
552 try {
553 Class <? extends HardwareComponent> x = Class.forName(clazz).asSubclass(HardwareComponent.class);
554 currentProject.events.addEvent(0L, x, rargs);
555 } catch(Exception e) {
556 System.err.println("Error adding event: " + e.getMessage());
561 public void eci_sendevent_lowbound(Long timeMin, String clazz, String[] rargs)
563 System.err.println("Event to: '" + clazz + "' (with low bound of " + timeMin + "):");
564 for(int i = 0; i < rargs.length; i++) {
565 System.err.println("rargs[" + i + "]: '" + rargs[i] + "'.");
567 if(currentProject.events != null) {
568 try {
569 Class <? extends HardwareComponent> x = Class.forName(clazz).asSubclass(HardwareComponent.class);
570 currentProject.events.addEvent(timeMin, x, rargs);
571 } catch(Exception e) {
572 System.err.println("Error adding event: " + e.getMessage());
577 public void eci_memory_read(Long address, Integer size)
579 if(currentProject.pc != null) {
580 long addr = address.longValue();
581 long _size = size.intValue();
582 long ret = 0;
583 PhysicalAddressSpace addrSpace;
584 if(addr < 0 || addr > 0xFFFFFFFFL || (_size != 1 && _size != 2 && _size != 4))
585 return;
587 addrSpace = (PhysicalAddressSpace)currentProject.pc.getComponent(PhysicalAddressSpace.class);
588 if(_size == 1)
589 ret = (long)addrSpace.getByte((int)addr) & 0xFF;
590 else if(_size == 2)
591 ret = (long)addrSpace.getWord((int)addr) & 0xFFFF;
592 else if(_size == 4)
593 ret = (long)addrSpace.getDoubleWord((int)addr) & 0xFFFFFFFFL;
595 vPluginManager.returnValue(ret);
599 public void eci_memory_write(Long address, Long value, Integer size)
601 if(currentProject.pc != null) {
602 long addr = address.longValue();
603 long _size = size.intValue();
604 long _value = value.longValue();
605 PhysicalAddressSpace addrSpace;
606 if(addr < 0 || addr > 0xFFFFFFFFL || (_size != 1 && _size != 2 && _size != 4))
607 return;
609 addrSpace = (PhysicalAddressSpace)currentProject.pc.getComponent(PhysicalAddressSpace.class);
610 if(_size == 1)
611 addrSpace.setByte((int)addr, (byte)_value);
612 else if(_size == 2)
613 addrSpace.setWord((int)addr, (short)_value);
614 else if(_size == 4)
615 addrSpace.setDoubleWord((int)addr, (int)_value);
619 public PCControl(Plugins manager, String args) throws Exception
621 this(manager);
623 UTFInputLineStream file = null;
624 Map<String, String> params = parseStringToComponents(args);
625 Set<String> used = new HashSet<String>();
626 String extramenu = params.get("extramenu");
627 String uncompress = params.get("uncompressedsave");
628 if(uncompress != null)
629 uncompressedSave = true;
630 if(extramenu == null)
631 return;
632 try {
633 file = new UTFInputLineStream(new FileInputStream(extramenu));
635 while(true) {
636 boolean exists = false;
637 String[] line = nextParseLine(file);
638 if(line == null)
639 break;
640 if(line.length < 3 || line[0].charAt(0) == '→') {
641 System.err.println("Warning: Bad extra menu item '" + line[0] + "'.");
642 continue;
644 if(line[0].length() == 0 || line[0].charAt(line[0].length() - 1) == '→') {
645 System.err.println("Warning: Bad extra menu item '" + line[0] + "'.");
646 continue;
648 if(line[0].indexOf("→→") >= 0) {
649 System.err.println("Warning: Bad extra menu item '" + line[0] + "'.");
650 continue;
652 if(used.contains(line[0]))
653 exists = true;
655 KeyStroke stroke = null;
656 if(!line[1].equals("<>")) {
657 stroke = KeyStroke.getKeyStroke(line[1]);
658 if(stroke == null) {
659 System.err.println("Warning: Bad keystroke '" + line[1] + "'.");
664 String[] lineCommand = Arrays.copyOfRange(line, 2, line.length);
665 used.add(line[0]);
666 List<String[]> commandList = extraActions.get(line[0]);
667 if(commandList == null)
668 extraActions.put(line[0], commandList = new ArrayList<String[]>());
669 commandList.add(lineCommand);
671 if(!exists)
672 menuManager.addMenuItem("Extra→" + line[0], this, "menuExtra", new String[]{line[0]}, PROFILE_ALWAYS,
673 stroke);
675 file.close();
676 } catch(IOException e) {
677 errorDialog(e, "Failed to load extra menu defintions", null, "dismiss");
678 if(file != null)
679 file.close();
681 window.setJMenuBar(menuManager.getMainBar());
684 public PCControl(Plugins manager) throws Exception
686 window = new JFrame("JPC-RR" + Misc.emuname);
688 if(DiskImage.getLibrary() == null)
689 throw new Exception("PCControl plugin requires disk library");
691 running = false;
692 shuttingDown = false;
693 configDialog = new PCConfigDialog();
694 dumpDialog = new DumpControlDialog(manager);
695 extraActions = new HashMap<String, List<String[]> >();
696 menuManager = new MenuManager();
698 menuManager.setProfile(PROFILE_NO_PC | PROFILE_STOPPED);
700 menuManager.addMenuItem("System→Assemble", this, "menuAssemble", null, PROFILE_STOPPED);
701 menuManager.addMenuItem("System→Start", this, "menuStart", null, PROFILE_STOPPED | PROFILE_HAVE_PC);
702 menuManager.addMenuItem("System→Stop", this, "menuStop", null, PROFILE_RUNNING);
703 menuManager.addMenuItem("System→Reset", this, "menuReset", null, PROFILE_HAVE_PC);
704 menuManager.addMenuItem("System→Dumping control", this, "menuDump", null, PROFILE_HAVE_PC | PROFILE_STOPPED);
705 menuManager.addMenuItem("System→Quit", this, "menuQuit", null, PROFILE_ALWAYS);
706 menuManager.addSelectableMenuItem("Breakpoints→Trap VRetrace Start", this, "menuVRetraceStart", null, false,
707 PROFILE_ALWAYS);
708 menuManager.addSelectableMenuItem("Breakpoints→Trap VRetrace End", this, "menuVRetraceEnd", null, false,
709 PROFILE_ALWAYS);
710 menuManager.addMenuItem("Snapshot→Change Run Authors", this, "menuChangeAuthors", null, PROFILE_HAVE_PC);
711 menuManager.addMenuItem("Snapshot→Save→Snapshot", this, "menuSave", new Object[]{new Boolean(false)},
712 PROFILE_HAVE_PC | PROFILE_STOPPED);
713 menuManager.addMenuItem("Snapshot→Save→Movie", this, "menuSave", new Object[]{new Boolean(true)},
714 PROFILE_HAVE_PC | PROFILE_STOPPED);
715 menuManager.addMenuItem("Snapshot→Save→Status Dump", this, "menuStatusDump", null,
716 PROFILE_HAVE_PC | PROFILE_STOPPED);
717 menuManager.addMenuItem("Snapshot→Load→Snapshot", this, "menuLoad",
718 new Object[]{new Integer(LoadStateTask.MODE_NORMAL)}, PROFILE_STOPPED);
719 menuManager.addMenuItem("Snapshot→Load→Snapshot (preserve events)", this, "menuLoad",
720 new Object[]{new Integer(LoadStateTask.MODE_PRESERVE)}, PROFILE_STOPPED | PROFILE_EVENTS);
721 menuManager.addMenuItem("Snapshot→Load→Movie", this, "menuLoad",
722 new Object[]{new Integer(LoadStateTask.MODE_MOVIEONLY)}, PROFILE_STOPPED);
723 menuManager.addMenuItem("Snapshot→RAM Dump→Hexadecimal", this, "menuRAMDump", new Object[]{new Boolean(false)},
724 PROFILE_HAVE_PC | PROFILE_STOPPED);
725 menuManager.addMenuItem("Snapshot→RAM Dump→Binary", this, "menuRAMDump", new Object[]{new Boolean(true)},
726 PROFILE_HAVE_PC | PROFILE_STOPPED);
727 menuManager.addMenuItem("Snapshot→Truncate Event Stream", this, "menuTruncate", null,
728 PROFILE_STOPPED | PROFILE_EVENTS);
730 for(int i = 0; i < stopLabel.length; i++) {
731 menuManager.addSelectableMenuItem("Breakpoints→Timed Stops→" + stopLabel[i], this, "menuTimedStop",
732 null, (i == 0), PROFILE_ALWAYS);
734 imminentTrapTime = -1;
736 menuManager.addMenuItem("Drives→fda→<Empty>", this, "menuChangeDisk", new Object[]{new Integer(0),
737 new Integer(-1)}, PROFILE_HAVE_PC);
738 menuManager.addMenuItem("Drives→fdb→<Empty>", this, "menuChangeDisk", new Object[]{new Integer(1),
739 new Integer(-1)}, PROFILE_HAVE_PC);
740 menuManager.addMenuItem("Drives→CD-ROM→<Empty>", this, "menuChangeDisk", new Object[]{new Integer(2),
741 new Integer(-1)}, PROFILE_HAVE_PC | PROFILE_CDROM);
742 menuManager.addMenuItem("Drives→Add image", this, "menuAddDisk", null, PROFILE_HAVE_PC);
743 menuManager.addMenuItem("Drives→Import Image", this, "menuImport", null, PROFILE_ALWAYS);
745 menuManager.addMenuItem("Debug→Hacks→NO_FPU", this, "menuNOFPU", null, PROFILE_HAVE_PC);
746 menuManager.addMenuItem("Debug→Hacks→VGA_DRAW", this, "menuVGADRAW", null, PROFILE_HAVE_PC);
747 menuManager.addMenuItem("Debug→Hacks→VGA_SCROLL_2", this, "menuVGASCROLL2", null, PROFILE_HAVE_PC);
749 disks = new HashSet<String>();
750 currentProject = new PC.PCFullStatus();
751 this.pc = null;
752 this.vPluginManager = manager;
754 panel = new PCMonitorPanel(this);
755 loadstateDropTarget = new LoadstateDropTarget();
756 dropTarget = new DropTarget(panel.getMonitorPanel(), loadstateDropTarget);
757 statusBar = new JLabel("");
758 statusBar.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
759 manager.addSlaveObject(this, panel);
760 panel.startThread();
762 window.getContentPane().add("Center", panel.getMonitorPanel());
763 window.getContentPane().add("South", statusBar);
764 JMenuBar bar = menuManager.getMainBar();
765 for(JMenu menu : panel.getMenusNeeded())
766 bar.add(menu);
767 window.setJMenuBar(bar);
769 try {
770 window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
771 } catch (AccessControlException e) {
772 System.err.println("Error: Not able to add some components to frame: " + e.getMessage());
775 snapshotFileChooser = new JFileChooser(System.getProperty("user.dir"));
777 window.getContentPane().validate();
778 window.validate();
779 window.pack();
780 Dimension d = window.getSize();
781 nativeWidth = d.width;
782 nativeHeight = d.height;
783 updateStatusBarEventThread();
784 window.setVisible(true);
787 private void updateStatusBar()
789 if(vPluginManager.isShuttingDown())
790 return; //Too much of deadlock risk.
791 SwingUtilities.invokeLater(new Runnable() { public void run() { updateStatusBarEventThread(); }});
794 private void updateStatusBarEventThread()
796 String text1;
797 if(currentProject.pc != null && taskToDo == null) {
798 long timeNow = ((Clock)currentProject.pc.getComponent(Clock.class)).getTime();
799 long timeEnd = currentProject.events.getLastEventTime();
800 text1 = " Time: " + (timeNow / 1000000) + "ms, movie length: " + (timeEnd / 1000000) + "ms";
801 if(currentResolutionWidth > 0 && currentResolutionHeight > 0)
802 text1 = text1 + ", resolution: " + currentResolutionWidth + "*" + currentResolutionHeight;
803 else
804 text1 = text1 + ", resolution: <No valid signal>";
805 if(currentProject.events.isAtMovieEnd())
806 text1 = text1 + " (At movie end)";
807 } else if(taskToDo != null)
808 text1 = taskLabel;
809 else
810 text1 = " NO PC CONNECTED";
812 statusBar.setText(text1);
815 public void menuExtra(String i, Object[] args)
817 final List<String[]> commandList = extraActions.get(args[0]);
818 if(commandList == null) {
819 System.err.println("Warning: Called extra menu with unknown entry '" + args[0] + "'.");
820 return;
823 //Run the functions on seperate thread to avoid deadlocking.
824 (new Thread(new Runnable() { public void run() { menuExtraThreadFunc(commandList); }}, "Extra action thread")).start();
827 private void menuExtraThreadFunc(List<String[]> actions)
829 for(String[] i : actions) {
830 if(i.length == 1) {
831 vPluginManager.invokeExternalCommandSynchronous(i[0], null);
832 } else {
833 String[] rest = Arrays.copyOfRange(i, 1, i.length, String[].class);
834 vPluginManager.invokeExternalCommandSynchronous(i[0], rest);
839 public void menuAssemble(String i, Object[] args)
841 setTask(new AssembleTask(), ASSEMBLE_LABEL);
844 public void menuStart(String i, Object[] args)
846 start();
849 public void menuStop(String i, Object[] args)
851 stop();
854 public void menuReset(String i, Object[] args)
856 reset();
859 public void menuDump(String i, Object[] args)
861 setTask(new DumpControlTask(), DUMPCONTROL_LABEL);
864 public void menuImport(String i, Object[] args)
866 try {
867 new ImportDiskImage();
868 } catch(Exception e) {
869 e.printStackTrace();
873 public void menuNOFPU(String i, Object[] args)
875 pc.setFPUHack();
878 public void menuVGADRAW(String i, Object[] args)
880 pc.setVGADrawHack();
883 public void menuVGASCROLL2(String i, Object[] args)
885 pc.setVGAScroll2Hack();
888 public void menuQuit(String i, Object[] args)
890 vPluginManager.shutdownEmulator();
893 public void menuVRetraceStart(String i, Object[] args)
895 trapFlags ^= TraceTrap.TRACE_STOP_VRETRACE_START;
896 menuManager.setSelected("Breakpoints→Trap VRetrace Start",
897 (trapFlags & TraceTrap.TRACE_STOP_VRETRACE_START) == TraceTrap.TRACE_STOP_VRETRACE_START);
900 public void menuVRetraceEnd(String i, Object[] args)
902 trapFlags ^= TraceTrap.TRACE_STOP_VRETRACE_END;
903 menuManager.setSelected("Breakpoints→Trap VRetrace End",
904 (trapFlags & TraceTrap.TRACE_STOP_VRETRACE_END) == TraceTrap.TRACE_STOP_VRETRACE_END);
907 public void menuTimedStop(String i, Object[] args)
909 for(int j = 0; j < stopLabel.length; j++) {
910 String label = "Breakpoints→Timed Stops→" + stopLabel[j];
911 if(i.equals(label)) {
912 this.imminentTrapTime = stopTime[j];
913 menuManager.select(label);
914 } else
915 menuManager.unselect(label);
919 public void menuSave(String i, Object[] args)
921 setTask(new SaveStateTask(((Boolean)args[0]).booleanValue()), SAVESTATE_LABEL);
924 public void menuStatusDump(String i, Object[] args)
926 setTask(new StatusDumpTask(), STATUSDUMP_LABEL);
929 public void menuLoad(String i, Object[] args)
931 setTask(new LoadStateTask(((Integer)args[0]).intValue()), LOADSTATE_LABEL);
934 public void menuRAMDump(String i, Object[] args)
936 setTask(new RAMDumpTask(((Boolean)args[0]).booleanValue()), RAMDUMP_LABEL);
939 public void menuTruncate(String i, Object[] args)
941 currentProject.events.truncateEventStream();
944 public void menuChangeDisk(String i, Object[] args)
946 changeFloppy(((Integer)args[0]).intValue(), ((Integer)args[1]).intValue());
949 public void menuWriteProtect(String i, Object[] args)
951 int disk = ((Integer)args[0]).intValue();
952 writeProtect(disk, menuManager.isSelected(i));
953 DiskImageSet imageSet = pc.getDisks();
954 menuManager.setSelected(i, imageSet.lookupDisk(disk).isReadOnly());
957 public void menuAddDisk(String i, Object[] args)
959 setTask(new AddDiskTask(), ADDDISK_LABEL);
962 public void menuChangeAuthors(String i, Object[] args)
964 setTask(new ChangeAuthorsTask(), CHANGEAUTHORS_LABEL);
967 public synchronized void start()
969 if(taskToDo != null)
970 return;
971 vPluginManager.pcStarted();
972 running = true;
973 notifyAll();
976 private String prettyPrintTime(long ts)
978 String s = "";
980 if(ts >= 1000000000)
981 s = s + "" + (ts / 1000000000) + " ";
982 if(ts >= 100000000)
983 s = s + "" + (ts % 1000000000 / 100000000);
984 if(ts >= 10000000)
985 s = s + "" + (ts % 100000000 / 10000000);
986 if(ts >= 1000000)
987 s = s + "" + (ts % 10000000 / 1000000) + " ";
988 if(ts >= 100000)
989 s = s + "" + (ts % 1000000 / 100000);
990 if(ts >= 10000)
991 s = s + "" + (ts % 100000 / 10000);
992 if(ts >= 1000)
993 s = s + "" + (ts % 10000 / 1000) + " ";
994 if(ts >= 100)
995 s = s + "" + (ts % 1000 / 100);
996 if(ts >= 10)
997 s = s + "" + (ts % 100 / 10);
998 s = s + "" + (ts % 10);
999 return s;
1002 protected synchronized void stopNoWait()
1004 running = false;
1005 vPluginManager.pcStopped();
1006 Clock sysClock = (Clock)pc.getComponent(Clock.class);
1007 System.err.println("Notice: PC emulation stopped (at time sequence value " +
1008 prettyPrintTime(sysClock.getTime()) + ")");
1011 public synchronized void stop()
1013 pc.getTraceTrap().doPotentialTrap(TraceTrap.TRACE_STOP_IMMEDIATE);
1014 System.err.println("Informational: Waiting for PC to halt...");
1017 public JScrollPane getMonitorPane()
1019 return null;
1022 protected void reset()
1024 pc.reboot();
1027 public synchronized boolean isRunning()
1029 return running;
1032 private void changeFloppy(int drive, int image)
1036 PC.DiskChanger changer = (PC.DiskChanger)pc.getComponent(PC.DiskChanger.class);
1037 changer.changeFloppyDisk(drive, image);
1038 } catch (Exception e) {
1039 System.err.println("Error: Failed to change disk");
1040 errorDialog(e, "Failed to change disk", null, "Dismiss");
1044 private void writeProtect(int image, boolean state)
1048 PC.DiskChanger changer = (PC.DiskChanger)pc.getComponent(PC.DiskChanger.class);
1049 changer.wpFloppyDisk(image, state);
1050 } catch (Exception e) {
1051 System.err.println("Error: Failed to change floppy write protect");
1052 errorDialog(e, "Failed to write (un)protect floppy", null, "Dismiss");
1056 private class LoadStateTask extends AsyncGUITask
1058 File choosen;
1059 Exception caught;
1060 int _mode;
1061 long oTime;
1062 private static final int MODE_NORMAL = 1;
1063 private static final int MODE_PRESERVE = 2;
1064 private static final int MODE_MOVIEONLY = 3;
1066 public LoadStateTask(int mode)
1068 oTime = System.currentTimeMillis();
1069 choosen = null;
1070 _mode = mode;
1073 public LoadStateTask(String name, int mode)
1075 this(mode);
1076 choosen = new File(name);
1079 protected void runPrepare()
1081 if(choosen == null) {
1082 int returnVal = 0;
1083 if(_mode == MODE_PRESERVE)
1084 returnVal = snapshotFileChooser.showDialog(window, "LOAD JPC-RR Snapshot (PE)");
1085 else if(_mode == MODE_MOVIEONLY)
1086 returnVal = snapshotFileChooser.showDialog(window, "LOAD JPC-RR Snapshot (MO)");
1087 else
1088 returnVal = snapshotFileChooser.showDialog(window, "LOAD JPC-RR Snapshot");
1089 choosen = snapshotFileChooser.getSelectedFile();
1091 if (returnVal != 0)
1092 choosen = null;
1096 protected void runFinish()
1098 if(choosen == null)
1099 return;
1101 if(caught == null) {
1102 try {
1103 connectPC(pc = currentProject.pc);
1104 doCycle(pc);
1105 System.err.println("Informational: Loadstate done");
1106 } catch(Exception e) {
1107 caught = e;
1110 if(caught != null) {
1111 errorDialog(caught, "Load savestate failed", window, "Dismiss");
1113 System.err.println("Total save time: " + (System.currentTimeMillis() - oTime) + "ms.");
1114 PCControl.this.vPluginManager.signalCommandCompletion();
1117 protected void runTask()
1119 if(choosen == null)
1120 return;
1122 try {
1123 System.err.println("Informational: Loading a snapshot of JPC-RR");
1124 long times1 = System.currentTimeMillis();
1125 JRSRArchiveReader reader = new JRSRArchiveReader(choosen.getAbsolutePath());
1127 PC.PCFullStatus fullStatus = PC.loadSavestate(reader, _mode == MODE_PRESERVE, _mode == MODE_MOVIEONLY,
1128 currentProject);
1130 currentProject = fullStatus;
1132 reader.close();
1133 long times2 = System.currentTimeMillis();
1134 System.err.println("Informational: Loadstate complete (" + (times2 - times1) + "ms).");
1135 } catch(Exception e) {
1136 caught = e;
1141 private void doCycle(PC _pc)
1143 final PC _xpc = _pc;
1144 cycleDone = false;
1145 (new Thread(new Runnable() { public void run() { synchronized(PCControl.this) {
1146 _xpc.getVideoOutput().holdOutput(); cycleDone = true; PCControl.this.notifyAll();}}}, "VGA output cycle thread")).start();
1147 while(cycleDone)
1148 try {
1149 synchronized(this) {
1150 if(cycleDone)
1151 break;
1152 wait();
1154 } catch(Exception e) {
1158 private class SaveStateTask extends AsyncGUITask
1160 File choosen;
1161 Exception caught;
1162 boolean movieOnly;
1163 long oTime;
1165 public SaveStateTask(boolean movie)
1167 oTime = System.currentTimeMillis();
1168 choosen = null;
1169 movieOnly = movie;
1172 public SaveStateTask(String name, boolean movie)
1174 this(movie);
1175 choosen = new File(name);
1178 protected void runPrepare()
1180 if(choosen == null) {
1181 int returnVal = snapshotFileChooser.showDialog(window, movieOnly ? "Save JPC-RR Movie" :
1182 "Save JPC-RR Snapshot");
1183 choosen = snapshotFileChooser.getSelectedFile();
1185 if (returnVal != 0)
1186 choosen = null;
1190 protected void runFinish()
1192 if(caught != null) {
1193 errorDialog(caught, "Saving savestate failed", window, "Dismiss");
1195 System.err.println("Total save time: " + (System.currentTimeMillis() - oTime) + "ms.");
1196 PCControl.this.vPluginManager.signalCommandCompletion();
1199 protected void runTask()
1201 if(choosen == null)
1202 return;
1204 JRSRArchiveWriter writer = null;
1206 try {
1207 System.err.println("Informational: Savestating...");
1208 long times1 = System.currentTimeMillis();
1209 writer = new JRSRArchiveWriter(choosen.getAbsolutePath());
1210 PC.saveSavestate(writer, currentProject, movieOnly, uncompressedSave);
1211 renameFile(choosen, new File(choosen.getAbsolutePath() + ".backup"));
1212 writer.close();
1213 long times2 = System.currentTimeMillis();
1214 System.err.println("Informational: Savestate complete (" + (times2 - times1) + "ms).");
1215 } catch(Exception e) {
1216 if(writer != null)
1217 try { writer.rollback(); } catch(Exception f) {}
1218 caught = e;
1223 private class StatusDumpTask extends AsyncGUITask
1225 File choosen;
1226 Exception caught;
1228 public StatusDumpTask()
1230 choosen = null;
1233 public StatusDumpTask(String name)
1235 this();
1236 choosen = new File(name);
1239 protected void runPrepare()
1241 if(choosen == null) {
1242 int returnVal = snapshotFileChooser.showDialog(window, "Save Status dump");
1243 choosen = snapshotFileChooser.getSelectedFile();
1245 if (returnVal != 0)
1246 choosen = null;
1250 protected void runFinish()
1252 if(caught != null) {
1253 errorDialog(caught, "Status dump failed", window, "Dismiss");
1255 PCControl.this.vPluginManager.signalCommandCompletion();
1258 protected void runTask()
1260 if(choosen == null)
1261 return;
1263 try {
1264 OutputStream outb = new BufferedOutputStream(new FileOutputStream(choosen));
1265 PrintStream out = new PrintStream(outb, false, "UTF-8");
1266 StatusDumper sd = new StatusDumper(out);
1267 pc.dumpStatus(sd);
1268 out.flush();
1269 outb.flush();
1270 System.err.println("Informational: Dumped " + sd.dumpedObjects() + " objects");
1271 } catch(Exception e) {
1272 caught = e;
1277 private class RAMDumpTask extends AsyncGUITask
1279 File choosen;
1280 Exception caught;
1281 boolean binary;
1283 public RAMDumpTask(boolean binFlag)
1285 choosen = null;
1286 binary = binFlag;
1289 public RAMDumpTask(String name, boolean binFlag)
1291 this(binFlag);
1292 choosen = new File(name);
1295 protected void runPrepare()
1297 if(choosen == null) {
1298 int returnVal;
1299 if(binary)
1300 returnVal = snapshotFileChooser.showDialog(window, "Save RAM dump");
1301 else
1302 returnVal = snapshotFileChooser.showDialog(window, "Save RAM hexdump");
1303 choosen = snapshotFileChooser.getSelectedFile();
1305 if (returnVal != 0)
1306 choosen = null;
1310 protected void runFinish()
1312 if(caught != null) {
1313 errorDialog(caught, "RAM dump failed", window, "Dismiss");
1315 PCControl.this.vPluginManager.signalCommandCompletion();
1318 protected void runTask()
1320 if(choosen == null)
1321 return;
1323 try {
1324 OutputStream outb = new BufferedOutputStream(new FileOutputStream(choosen));
1325 byte[] pagebuf = new byte[4096];
1326 PhysicalAddressSpace addr = (PhysicalAddressSpace)pc.getComponent(PhysicalAddressSpace.class);
1327 int lowBound = addr.findFirstRAMPage(0);
1328 int firstUndumped = 0;
1329 int highBound = 0;
1330 int present = 0;
1331 while(lowBound >= 0) {
1332 for(; firstUndumped < lowBound; firstUndumped++)
1333 dumpPage(outb, firstUndumped, null);
1334 addr.readRAMPage(firstUndumped++, pagebuf);
1335 dumpPage(outb, lowBound, pagebuf);
1336 present++;
1337 highBound = lowBound + 1;
1338 lowBound = addr.findFirstRAMPage(++lowBound);
1340 outb.flush();
1341 System.err.println("Informational: Dumped machine RAM (" + highBound + " pages examined, " +
1342 present + " pages present).");
1343 } catch(Exception e) {
1344 caught = e;
1348 private byte charForHex(int hvalue)
1350 if(hvalue < 10)
1351 return (byte)(hvalue + 48);
1352 else if(hvalue > 9 && hvalue < 16)
1353 return (byte)(hvalue + 55);
1354 else
1355 System.err.println("Unknown hex value: " + hvalue + ".");
1356 return 90;
1359 private void dumpPage(OutputStream stream, int pageNo, byte[] buffer) throws IOException
1361 int pageBufSize;
1362 pageNo = pageNo & 0xFFFFF; //Cut page numbers out of range.
1363 if(!binary && buffer == null)
1364 return; //Don't dump null pages in non-binary mode.
1365 if(binary)
1366 pageBufSize = 4096; //Binary page buffer is 4096 bytes.
1367 else
1368 pageBufSize = 14592; //Hexdump page buffer is 14592 bytes.
1369 byte[] outputPage = new byte[pageBufSize];
1370 if(buffer != null && binary) {
1371 System.arraycopy(buffer, 0, outputPage, 0, 4096);
1372 } else if(buffer != null) { //Hex mode
1373 for(int i = 0; i < 256; i++) {
1374 for(int j = 0; j < 57; j++) {
1375 if(j < 5)
1376 outputPage[57 * i + j] = charForHex((pageNo >>> (4 * (4 - j))) & 0xF);
1377 else if(j == 5)
1378 outputPage[57 * i + j] = charForHex(i / 16);
1379 else if(j == 6)
1380 outputPage[57 * i + j] = charForHex(i % 16);
1381 else if(j == 7)
1382 outputPage[57 * i + j] = 48;
1383 else if(j == 56)
1384 outputPage[57 * i + j] = 10;
1385 else if(j % 3 == 2)
1386 outputPage[57 * i + j] = 32;
1387 else if(j % 3 == 0)
1388 outputPage[57 * i + j] = charForHex(((int)buffer[16 * i + j / 3 - 3] & 0xFF) / 16);
1389 else if(j % 3 == 1)
1390 outputPage[57 * i + j] = charForHex(buffer[16 * i + j / 3 - 3] & 0xF);
1391 else
1392 System.err.println("Error: dumpPage: unhandled j = " + j + ".");
1396 stream.write(outputPage);
1400 private class AssembleTask extends AsyncGUITask
1402 Exception caught;
1403 boolean canceled;
1405 public AssembleTask()
1407 canceled = false;
1410 protected void runPrepare()
1412 try {
1413 configDialog.popUp();
1414 } catch(Exception e) {
1415 caught = e;
1419 protected void runFinish()
1421 if(caught == null && !canceled) {
1422 try {
1423 currentProject.projectID = randomHexes(24);
1424 currentProject.rerecords = 0;
1425 currentProject.events = new EventRecorder();
1426 currentProject.events.attach(pc, null);
1427 currentProject.savestateID = null;
1428 currentProject.extraHeaders = null;
1429 currentProject.events.setRerecordCount(0);
1430 currentProject.events.setHeaders(currentProject.extraHeaders);
1431 connectPC(pc);
1432 } catch(Exception e) {
1433 caught = e;
1436 if(caught != null) {
1437 errorDialog(caught, "PC Assembly failed", window, "Dismiss");
1439 PCControl.this.vPluginManager.signalCommandCompletion();
1442 protected void runTask()
1444 if(caught != null)
1445 return;
1446 PC.PCHardwareInfo hw = configDialog.waitClose();
1447 if(hw == null) {
1448 canceled = true;
1449 return;
1452 try {
1453 pc = PC.createPC(hw);
1454 } catch(Exception e) {
1455 caught = e;
1460 private class DumpControlTask extends AsyncGUITask
1462 Exception caught;
1463 boolean canceled;
1465 public DumpControlTask()
1469 protected void runPrepare()
1471 try {
1472 dumpDialog.popUp(PCControl.this.pc);
1473 } catch(Exception e) {
1474 caught = e;
1478 protected void runFinish()
1480 if(caught != null) {
1481 errorDialog(caught, "Opening dump control dialog failed", window, "Dismiss");
1485 protected void runTask()
1487 if(caught != null)
1488 return;
1489 dumpDialog.waitClose();
1493 private class AddDiskTask extends AsyncGUITask
1495 Exception caught;
1496 NewDiskDialog dd;
1498 public AddDiskTask()
1500 dd = new NewDiskDialog();
1503 protected void runPrepare()
1507 protected void runFinish()
1509 if(caught != null) {
1510 errorDialog(caught, "Adding disk failed", window, "Dismiss");
1512 try {
1513 updateDisks();
1514 } catch(Exception e) {
1515 errorDialog(e, "Failed to update disk menus", null, "Dismiss");
1517 PCControl.this.vPluginManager.signalCommandCompletion();
1520 protected void runTask()
1522 NewDiskDialog.Response res = dd.waitClose();
1523 if(res == null) {
1524 return;
1526 try {
1527 DiskImage img;
1528 pc.getDisks().addDisk(img = new DiskImage(res.diskFile, false));
1529 img.setName(res.diskName);
1530 } catch(Exception e) {
1531 caught = e;
1536 private class ChangeAuthorsTask extends AsyncGUITask
1538 Exception caught;
1539 AuthorsDialog ad;
1541 public ChangeAuthorsTask()
1543 int authors = 0;
1544 int headers = 0;
1545 AuthorsDialog.AuthorElement[] authorNames = null;
1546 if(currentProject != null)
1547 authorNames = AuthorsDialog.readAuthorsFromHeaders(currentProject.extraHeaders);
1549 ad = new AuthorsDialog(authorNames);
1552 protected void runPrepare()
1556 protected void runFinish()
1558 if(caught != null) {
1559 errorDialog(caught, "Changing authors failed", window, "Dismiss");
1561 PCControl.this.vPluginManager.signalCommandCompletion();
1564 protected void runTask()
1566 AuthorsDialog.Response res = ad.waitClose();
1567 if(res == null) {
1568 return;
1570 try {
1571 currentProject.extraHeaders = AuthorsDialog.rewriteHeaderAuthors(currentProject.extraHeaders,
1572 res.authors);
1573 currentProject.events.setHeaders(currentProject.extraHeaders);
1574 } catch(Exception e) {
1575 caught = e;