Change VGA offset for 8d2 to 0
[jpcrr.git] / org / jpc / plugins / PCControl.java
blobc60fe8928ca4675b5553b95ca53a69945e8a0c92
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.*;
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;
84 protected PC pc;
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;
106 static
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)
119 return true;
120 //We are running. Do the absolute minimum since we are running in very delicate context.
121 shuttingDown = true;
122 stop();
123 return true;
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);
148 if (running)
149 return;
151 setTrapFlags();
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);
170 if(shuttingDown)
171 return; //Don't mess with UI when shutting down.
174 long profile = PROFILE_STOPPED;
175 if(pc != null)
176 profile |= PROFILE_HAVE_PC;
177 else
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);
186 try {
187 updateDisks();
188 } catch(Exception e) {
189 errorDialog(e, "Failed to update disk menus", null, "Dismiss");
192 if(pc != null) {
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);
208 disks.clear();
210 if(pc == null)
211 return;
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(),
225 PROFILE_HAVE_PC);
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);
239 public void main()
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)
246 pc.stop();
247 wasRunning = running;
248 try {
249 synchronized(this) {
250 waiting = true;
251 notifyAll();
252 wait();
253 waiting = false;
255 } catch(Exception e) {
259 if(!wasRunning)
260 pc.start();
261 wasRunning = running;
263 try {
264 pc.execute();
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");
270 if(!willCleanup)
271 SwingUtilities.invokeAndWait(new Thread() { public void run() { stopNoWait(); }});
272 running = false;
274 } catch (Exception e) {
275 errorDialog(e, "Hardware emulator internal error", this, "Dismiss");
276 try {
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);
289 this.pc = pc;
292 private void startExternal()
294 if(pc != null && !running)
295 if(!SwingUtilities.isEventDispatchThread())
296 try {
297 SwingUtilities.invokeAndWait(new Thread() { public void run() { PCControl.this.start(); }});
298 } catch(Exception e) {
300 else
301 start();
304 private void stopExternal()
306 if(pc != null && running)
307 if(!SwingUtilities.isEventDispatchThread())
308 try {
309 SwingUtilities.invokeAndWait(new Thread() { public void run() { PCControl.this.stop(); }});
310 } catch(Exception e) {
312 else
313 stop();
316 public boolean eci_state_save(String filename)
318 if(!running)
319 (new Thread(new SaveStateTask(filename, false), "Savestate task thread")).start();
320 return !running;
323 public boolean eci_state_dump(String filename)
325 if(!running)
326 (new Thread(new StatusDumpTask(filename), "Statedump task thread")).start();
327 return !running;
330 public boolean eci_movie_save(String filename)
332 if(!running)
333 (new Thread(new SaveStateTask(filename, true), "Savestate task thread")).start();
334 return !running;
337 public boolean eci_state_load(String filename)
339 if(!running)
340 (new Thread(new LoadStateTask(filename, LoadStateTask.MODE_NORMAL), "Loadstate task thread")).start();
341 return !running;
344 public boolean eci_state_load_noevents(String filename)
346 if(!running)
347 (new Thread(new LoadStateTask(filename, LoadStateTask.MODE_PRESERVE), "Loadstate task thread")).start();
348 return !running;
351 public boolean eci_movie_load(String filename)
353 if(!running)
354 (new Thread(new LoadStateTask(filename, LoadStateTask.MODE_MOVIEONLY), "Loadstate task thread")).start();
355 return !running;
358 public boolean eci_pc_assemble()
360 if(!running)
361 (new Thread(new AssembleTask(), "Assemble task thread")).start();
362 return !running;
365 public boolean eci_ram_dump_text(String filename)
367 if(!running)
368 (new Thread(new RAMDumpTask(filename, false), "RAM dump task thread")).start();
369 return !running;
372 public boolean eci_ram_dump_binary(String filename)
374 if(!running)
375 (new Thread(new RAMDumpTask(filename, true), "RAM dump task thread")).start();
376 return !running;
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()
411 startExternal();
414 public void eci_pc_stop()
416 stopExternal();
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) {
431 try {
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();
445 long ret = 0;
446 PhysicalAddressSpace addrSpace;
447 if(addr < 0 || addr > 0xFFFFFFFFL || (_size != 1 && _size != 2 && _size != 4))
448 return;
450 addrSpace = (PhysicalAddressSpace)currentProject.pc.getComponent(PhysicalAddressSpace.class);
451 if(_size == 1)
452 ret = (long)addrSpace.getByte((int)addr) & 0xFF;
453 else if(_size == 2)
454 ret = (long)addrSpace.getWord((int)addr) & 0xFFFF;
455 else if(_size == 4)
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))
470 return;
472 addrSpace = (PhysicalAddressSpace)currentProject.pc.getComponent(PhysicalAddressSpace.class);
473 if(_size == 1)
474 addrSpace.setByte((int)addr, (byte)_value);
475 else if(_size == 2)
476 addrSpace.setWord((int)addr, (short)_value);
477 else if(_size == 4)
478 addrSpace.setDoubleWord((int)addr, (int)_value);
482 public PCControl(Plugins manager, String args) throws Exception
484 this(manager);
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)
494 return;
495 try {
496 file = new UTFInputLineStream(new FileInputStream(extramenu));
498 while(true) {
499 boolean exists = false;
500 String[] line = nextParseLine(file);
501 if(line == null)
502 break;
503 if(line.length < 3 || line[0].charAt(0) == '→') {
504 System.err.println("Warning: Bad extra menu item '" + line[0] + "'.");
505 continue;
507 if(line[0].length() == 0 || line[0].charAt(line[0].length() - 1) == '→') {
508 System.err.println("Warning: Bad extra menu item '" + line[0] + "'.");
509 continue;
511 if(line[0].indexOf("→→") >= 0) {
512 System.err.println("Warning: Bad extra menu item '" + line[0] + "'.");
513 continue;
515 if(used.contains(line[0]))
516 exists = true;
518 KeyStroke stroke = null;
519 if(!line[1].equals("<>")) {
520 stroke = KeyStroke.getKeyStroke(line[1]);
521 if(stroke == null) {
522 System.err.println("Warning: Bad keystroke '" + line[1] + "'.");
527 String[] lineCommand = Arrays.copyOfRange(line, 2, line.length);
528 used.add(line[0]);
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);
534 if(!exists)
535 menuManager.addMenuItem("Extra→" + line[0], this, "menuExtra", new String[]{line[0]}, PROFILE_ALWAYS,
536 stroke);
538 file.close();
539 } catch(IOException e) {
540 errorDialog(e, "Failed to load extra menu defintions", null, "dismiss");
541 if(file != null)
542 file.close();
544 setJMenuBar(menuManager.getMainBar());
547 public PCControl(Plugins manager) throws Exception
549 super("JPC-RR");
551 if(DiskImage.getLibrary() == null)
552 throw new Exception("PCControl plugin requires disk library");
554 running = false;
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,
571 PROFILE_ALWAYS);
572 menuManager.addSelectableMenuItem("Breakpoints→Trap VRetrace End", this, "menuVRetraceEnd", null, false,
573 PROFILE_ALWAYS);
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();
615 this.pc = null;
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();
632 validate();
633 pack();
634 Dimension d = getSize();
635 nativeWidth = d.width;
636 nativeHeight = d.height;
637 setVisible(true);
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] + "'.");
645 return;
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) {
655 if(i.length == 1) {
656 vPluginManager.invokeExternalCommandSynchronous(i[0], null);
657 } else {
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)
671 start();
674 public void menuStop(String i, Object[] args)
676 stop();
679 public void menuReset(String i, Object[] args)
681 reset();
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)
691 try {
692 new ImportDiskImage();
693 } catch(Exception e) {
694 e.printStackTrace();
698 public void menuNOFPU(String i, Object[] args)
700 pc.setFPUHack();
703 public void menuVGADRAW(String i, Object[] args)
705 pc.setVGADrawHack();
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);
739 } else
740 menuManager.unselect(label);
744 public void menuSave(String i, Object[] args)
746 restoreFocus = true;
747 (new Thread(new SaveStateTask(((Boolean)args[0]).booleanValue()), "Savestate task thread")).start();
750 public void menuStatusDump(String i, Object[] args)
752 restoreFocus = true;
753 (new Thread(new StatusDumpTask(), "Statusdump task thread")).start();
756 public void menuLoad(String i, Object[] args)
758 restoreFocus = true;
759 (new Thread(new LoadStateTask(((Integer)args[0]).intValue()), "Loadstate task thread")).start();
762 public void menuRAMDump(String i, Object[] args)
764 restoreFocus = true;
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)
788 restoreFocus = true;
789 (new Thread(new AddDiskTask(), "Add disk task thread")).start();
792 public void menuChangeAuthors(String i, Object[] args)
794 restoreFocus = true;
795 (new Thread(new ChangeAuthorsTask(), "Change authors task thread")).start();
798 public synchronized void start()
800 vPluginManager.pcStarted();
801 running = true;
802 notifyAll();
805 private String prettyPrintTime(long ts)
807 String s = "";
809 if(ts >= 1000000000)
810 s = s + "" + (ts / 1000000000) + " ";
811 if(ts >= 100000000)
812 s = s + "" + (ts % 1000000000 / 100000000);
813 if(ts >= 10000000)
814 s = s + "" + (ts % 100000000 / 10000000);
815 if(ts >= 1000000)
816 s = s + "" + (ts % 10000000 / 1000000) + " ";
817 if(ts >= 100000)
818 s = s + "" + (ts % 1000000 / 100000);
819 if(ts >= 10000)
820 s = s + "" + (ts % 100000 / 10000);
821 if(ts >= 1000)
822 s = s + "" + (ts % 10000 / 1000) + " ";
823 if(ts >= 100)
824 s = s + "" + (ts % 1000 / 100);
825 if(ts >= 10)
826 s = s + "" + (ts % 100 / 10);
827 s = s + "" + (ts % 10);
828 return s;
831 protected synchronized void stopNoWait()
833 running = false;
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()
842 willCleanup = true;
843 pc.getTraceTrap().doPotentialTrap(TraceTrap.TRACE_STOP_IMMEDIATE);
844 running = false;
845 System.err.println("Informational: Waiting for PC to halt...");
846 while(!waiting)
847 try {
848 wait();
849 } catch(Exception e) {
851 willCleanup = false;
852 stopNoWait();
855 public JScrollPane getMonitorPane()
857 return null;
860 protected void reset()
862 pc.reboot();
865 public synchronized boolean isRunning()
867 return running;
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
896 File choosen;
897 Exception caught;
898 PleaseWait pw;
899 boolean cycleDone;
900 int _mode;
901 long oTime;
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();
909 choosen = null;
910 _mode = mode;
911 pw = new PleaseWait("Loading savestate...");
914 public LoadStateTask(String name, int mode)
916 this(mode);
917 choosen = new File(name);
920 protected void runPrepare()
922 PCControl.this.setEnabled(false);
923 if(choosen == null) {
924 int returnVal = 0;
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)");
929 else
930 returnVal = snapshotFileChooser.showDialog(PCControl.this, "LOAD JPC-RR Snapshot");
931 choosen = snapshotFileChooser.getSelectedFile();
933 if (returnVal != 0)
934 choosen = null;
936 pw.popUp();
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();
943 while(cycleDone)
944 try {
945 synchronized(this) {
946 wait();
948 } catch(Exception e) {
952 protected void runFinish()
954 if(caught == null) {
955 try {
956 connectPC(pc = currentProject.pc);
957 doCycle();
958 System.err.println("Informational: Loadstate done");
959 } catch(Exception e) {
960 caught = e;
963 pw.popDown();
964 if(caught != null) {
965 errorDialog(caught, "Load savestate failed", PCControl.this, "Dismiss");
967 PCControl.this.setEnabled(true);
968 if(restoreFocus)
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()
977 if(choosen == null)
978 return;
980 try {
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,
986 currentProject);
988 currentProject = fullStatus;
990 reader.close();
991 long times2 = System.currentTimeMillis();
992 System.err.println("Informational: Loadstate complete (" + (times2 - times1) + "ms).");
993 } catch(Exception e) {
994 caught = e;
999 private class SaveStateTask extends AsyncGUITask
1001 File choosen;
1002 Exception caught;
1003 boolean movieOnly;
1004 PleaseWait pw;
1005 long oTime;
1007 public SaveStateTask(boolean movie)
1009 oTime = System.currentTimeMillis();
1010 choosen = null;
1011 movieOnly = movie;
1012 pw = new PleaseWait("Saving savestate...");
1015 public SaveStateTask(String name, boolean movie)
1017 this(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();
1029 if (returnVal != 0)
1030 choosen = null;
1032 pw.popUp();
1035 protected void runFinish()
1037 pw.popDown();
1038 if(caught != null) {
1039 errorDialog(caught, "Saving savestate failed", PCControl.this, "Dismiss");
1041 PCControl.this.setEnabled(true);
1042 if(restoreFocus)
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()
1051 if(choosen == null)
1052 return;
1054 JRSRArchiveWriter writer = null;
1056 try {
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"));
1062 writer.close();
1063 long times2 = System.currentTimeMillis();
1064 System.err.println("Informational: Savestate complete (" + (times2 - times1) + "ms).");
1065 } catch(Exception e) {
1066 if(writer != null)
1067 try { writer.rollback(); } catch(Exception f) {}
1068 caught = e;
1073 private class StatusDumpTask extends AsyncGUITask
1075 File choosen;
1076 Exception caught;
1077 PleaseWait pw;
1079 public StatusDumpTask()
1081 choosen = null;
1082 pw = new PleaseWait("Saving status dump...");
1085 public StatusDumpTask(String name)
1087 this();
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();
1098 if (returnVal != 0)
1099 choosen = null;
1101 pw.popUp();
1104 protected void runFinish()
1106 pw.popDown();
1107 if(caught != null) {
1108 errorDialog(caught, "Status dump failed", PCControl.this, "Dismiss");
1110 PCControl.this.setEnabled(true);
1111 if(restoreFocus)
1112 PCControl.this.requestFocus(true);
1113 restoreFocus = false;
1114 PCControl.this.vPluginManager.signalCommandCompletion();
1117 protected void runTask()
1119 if(choosen == null)
1120 return;
1122 try {
1123 OutputStream outb = new BufferedOutputStream(new FileOutputStream(choosen));
1124 PrintStream out = new PrintStream(outb, false, "UTF-8");
1125 StatusDumper sd = new StatusDumper(out);
1126 pc.dumpStatus(sd);
1127 out.flush();
1128 outb.flush();
1129 System.err.println("Informational: Dumped " + sd.dumpedObjects() + " objects");
1130 } catch(Exception e) {
1131 caught = e;
1136 private class RAMDumpTask extends AsyncGUITask
1138 File choosen;
1139 Exception caught;
1140 PleaseWait pw;
1141 boolean binary;
1143 public RAMDumpTask(boolean binFlag)
1145 choosen = null;
1146 pw = new PleaseWait("Saving RAM dump...");
1147 binary = binFlag;
1150 public RAMDumpTask(String name, boolean binFlag)
1152 this(binFlag);
1153 choosen = new File(name);
1156 protected void runPrepare()
1158 PCControl.this.setEnabled(false);
1159 if(choosen == null) {
1160 int returnVal;
1161 if(binary)
1162 returnVal = snapshotFileChooser.showDialog(PCControl.this, "Save RAM dump");
1163 else
1164 returnVal = snapshotFileChooser.showDialog(PCControl.this, "Save RAM hexdump");
1165 choosen = snapshotFileChooser.getSelectedFile();
1167 if (returnVal != 0)
1168 choosen = null;
1170 pw.popUp();
1173 protected void runFinish()
1175 pw.popDown();
1176 if(caught != null) {
1177 errorDialog(caught, "RAM dump failed", PCControl.this, "Dismiss");
1179 PCControl.this.setEnabled(true);
1180 if(restoreFocus)
1181 PCControl.this.requestFocus(true);
1182 restoreFocus = false;
1183 PCControl.this.vPluginManager.signalCommandCompletion();
1186 protected void runTask()
1188 if(choosen == null)
1189 return;
1191 try {
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;
1197 int highBound = 0;
1198 int present = 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);
1204 present++;
1205 highBound = lowBound + 1;
1206 lowBound = addr.findFirstRAMPage(++lowBound);
1208 outb.flush();
1209 System.err.println("Informational: Dumped machine RAM (" + highBound + " pages examined, " +
1210 present + " pages present).");
1211 } catch(Exception e) {
1212 caught = e;
1216 private byte charForHex(int hvalue)
1218 if(hvalue < 10)
1219 return (byte)(hvalue + 48);
1220 else if(hvalue > 9 && hvalue < 16)
1221 return (byte)(hvalue + 55);
1222 else
1223 System.err.println("Unknown hex value: " + hvalue + ".");
1224 return 90;
1227 private void dumpPage(OutputStream stream, int pageNo, byte[] buffer) throws IOException
1229 int pageBufSize;
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.
1233 if(binary)
1234 pageBufSize = 4096; //Binary page buffer is 4096 bytes.
1235 else
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++) {
1243 if(j < 5)
1244 outputPage[57 * i + j] = charForHex((pageNo >>> (4 * (4 - j))) & 0xF);
1245 else if(j == 5)
1246 outputPage[57 * i + j] = charForHex(i / 16);
1247 else if(j == 6)
1248 outputPage[57 * i + j] = charForHex(i % 16);
1249 else if(j == 7)
1250 outputPage[57 * i + j] = 48;
1251 else if(j == 56)
1252 outputPage[57 * i + j] = 10;
1253 else if(j % 3 == 2)
1254 outputPage[57 * i + j] = 32;
1255 else if(j % 3 == 0)
1256 outputPage[57 * i + j] = charForHex(((int)buffer[16 * i + j / 3 - 3] & 0xFF) / 16);
1257 else if(j % 3 == 1)
1258 outputPage[57 * i + j] = charForHex(buffer[16 * i + j / 3 - 3] & 0xF);
1259 else
1260 System.err.println("Error: dumpPage: unhandled j = " + j + ".");
1264 stream.write(outputPage);
1268 private class AssembleTask extends AsyncGUITask
1270 Exception caught;
1271 PleaseWait pw;
1272 boolean canceled;
1274 public AssembleTask()
1276 pw = new PleaseWait("Assembling PC...");
1277 canceled = false;
1280 protected void runPrepare()
1282 PCControl.this.setEnabled(false);
1283 try {
1284 configDialog.popUp();
1285 } catch(Exception e) {
1286 caught = e;
1290 protected void runFinish()
1292 if(caught == null && !canceled) {
1293 try {
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);
1302 connectPC(pc);
1303 } catch(Exception e) {
1304 caught = e;
1307 if(!canceled)
1308 pw.popDown();
1309 if(caught != null) {
1310 errorDialog(caught, "PC Assembly failed", PCControl.this, "Dismiss");
1312 PCControl.this.setEnabled(true);
1313 if(restoreFocus)
1314 PCControl.this.requestFocus(true);
1315 restoreFocus = false;
1316 PCControl.this.vPluginManager.signalCommandCompletion();
1319 protected void runTask()
1321 if(caught != null)
1322 return;
1323 PC.PCHardwareInfo hw = configDialog.waitClose();
1324 if(hw == null) {
1325 canceled = true;
1326 return;
1328 try {
1329 SwingUtilities.invokeAndWait(new Thread() { public void run() { pw.popUp(); }});
1330 } catch(Exception e) {
1333 try {
1334 pc = PC.createPC(hw);
1335 } catch(Exception e) {
1336 caught = e;
1341 private class DumpControlTask extends AsyncGUITask
1343 Exception caught;
1344 boolean canceled;
1346 public DumpControlTask()
1350 protected void runPrepare()
1352 PCControl.this.setEnabled(false);
1353 try {
1354 dumpDialog.popUp(PCControl.this.pc);
1355 } catch(Exception e) {
1356 caught = 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);
1366 if(restoreFocus)
1367 PCControl.this.requestFocus(true);
1368 restoreFocus = false;
1371 protected void runTask()
1373 if(caught != null)
1374 return;
1375 dumpDialog.waitClose();
1379 private class AddDiskTask extends AsyncGUITask
1381 Exception caught;
1382 NewDiskDialog dd;
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);
1400 if(restoreFocus)
1401 PCControl.this.requestFocus(true);
1402 restoreFocus = false;
1403 try {
1404 updateDisks();
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();
1414 if(res == null) {
1415 return;
1417 try {
1418 DiskImage img;
1419 pc.getDisks().addDisk(img = new DiskImage(res.diskFile, false));
1420 img.setName(res.diskName);
1421 } catch(Exception e) {
1422 caught = e;
1427 private class ChangeAuthorsTask extends AsyncGUITask
1429 Exception caught;
1430 AuthorsDialog ad;
1432 public ChangeAuthorsTask()
1434 int authors = 0;
1435 int headers = 0;
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);
1454 if(restoreFocus)
1455 PCControl.this.requestFocus(true);
1456 restoreFocus = false;
1457 PCControl.this.vPluginManager.signalCommandCompletion();
1460 protected void runTask()
1462 AuthorsDialog.Response res = ad.waitClose();
1463 if(res == null) {
1464 return;
1466 try {
1467 currentProject.extraHeaders = AuthorsDialog.rewriteHeaderAuthors(currentProject.extraHeaders,
1468 res.authors);
1469 currentProject.events.setHeaders(currentProject.extraHeaders);
1470 } catch(Exception e) {
1471 caught = e;