Add VGA draw hack
[jpcrr.git] / org / jpc / plugins / PCControl.java
blobcbf40bc0f34cc45199e8596fdd134beacc478391
1 /*
2 JPC-RR: A x86 PC Hardware Emulator
3 Release 1
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;
33 import java.io.*;
34 import java.util.*;
35 import java.util.zip.*;
36 import java.security.AccessControlException;
37 import javax.swing.*;
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;
88 protected PC pc;
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;
104 static
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)
117 return true;
118 //We are running. Do the absolute minimum since we are running in very delicate context.
119 shuttingDown = true;
120 stop();
121 return true;
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);
145 if (running)
146 return;
148 setTrapFlags();
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);
167 if(shuttingDown)
168 return; //Don't mess with UI when shutting down.
171 long profile = PROFILE_STOPPED;
172 if(pc != null)
173 profile |= PROFILE_HAVE_PC;
174 else
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);
183 try {
184 updateDisks();
185 } catch(Exception e) {
186 errorDialog(e, "Failed to update disk menus", null, "Dismiss");
189 if(pc != null) {
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);
205 disks.clear();
207 if(pc == null)
208 return;
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(),
222 PROFILE_HAVE_PC);
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);
236 public void main()
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)
243 pc.stop();
244 wasRunning = running;
245 try {
246 synchronized(this) {
247 waiting = true;
248 notifyAll();
249 wait();
250 waiting = false;
252 } catch(Exception e) {
256 if(!wasRunning)
257 pc.start();
258 wasRunning = running;
260 try {
261 pc.execute();
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");
265 if(!willCleanup)
266 SwingUtilities.invokeAndWait(new Thread() { public void run() { stopNoWait(); }});
267 running = false;
269 } catch (Exception e) {
270 errorDialog(e, "Hardware emulator internal error", this, "Dismiss");
271 try {
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);
284 this.pc = pc;
287 private void startExternal()
289 if(pc != null && !running)
290 if(!SwingUtilities.isEventDispatchThread())
291 try {
292 SwingUtilities.invokeAndWait(new Thread() { public void run() { PCControl.this.start(); }});
293 } catch(Exception e) {
295 else
296 start();
299 private void stopExternal()
301 if(pc != null && running)
302 if(!SwingUtilities.isEventDispatchThread())
303 try {
304 SwingUtilities.invokeAndWait(new Thread() { public void run() { PCControl.this.stop(); }});
305 } catch(Exception e) {
307 else
308 stop();
311 public boolean eci_state_save(String filename)
313 if(!running)
314 (new Thread(new SaveStateTask(filename, false))).start();
315 return !running;
318 public boolean eci_movie_save(String filename)
320 if(!running)
321 (new Thread(new SaveStateTask(filename, true))).start();
322 return !running;
325 public boolean eci_state_load(String filename)
327 if(!running)
328 (new Thread(new LoadStateTask(filename, LoadStateTask.MODE_NORMAL))).start();
329 return !running;
332 public boolean eci_state_load_noevents(String filename)
334 if(!running)
335 (new Thread(new LoadStateTask(filename, LoadStateTask.MODE_PRESERVE))).start();
336 return !running;
339 public boolean eci_movie_load(String filename)
341 if(!running)
342 (new Thread(new LoadStateTask(filename, LoadStateTask.MODE_MOVIEONLY))).start();
343 return !running;
346 public boolean eci_pc_assemble()
348 if(!running)
349 (new Thread(new AssembleTask())).start();
350 return !running;
353 public boolean eci_ram_dump_text(String filename)
355 if(!running)
356 (new Thread(new RAMDumpTask(filename, false))).start();
357 return !running;
360 public boolean eci_ram_dump_binary(String filename)
362 if(!running)
363 (new Thread(new RAMDumpTask(filename, true))).start();
364 return !running;
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()
399 startExternal();
402 public void eci_pc_stop()
404 stopExternal();
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) {
415 try {
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();
429 long ret = 0;
430 PhysicalAddressSpace addrSpace;
431 if(addr < 0 || addr > 0xFFFFFFFFL || (_size != 1 && _size != 2 && _size != 4))
432 return;
434 addrSpace = (PhysicalAddressSpace)currentProject.pc.getComponent(PhysicalAddressSpace.class);
435 if(_size == 1)
436 ret = (long)addrSpace.getByte((int)addr) & 0xFF;
437 else if(_size == 2)
438 ret = (long)addrSpace.getWord((int)addr) & 0xFFFF;
439 else if(_size == 4)
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))
454 return;
456 addrSpace = (PhysicalAddressSpace)currentProject.pc.getComponent(PhysicalAddressSpace.class);
457 if(_size == 1)
458 addrSpace.setByte((int)addr, (byte)_value);
459 else if(_size == 2)
460 addrSpace.setWord((int)addr, (short)_value);
461 else if(_size == 4)
462 addrSpace.setDoubleWord((int)addr, (int)_value);
466 public PCControl(Plugins manager) throws Exception
468 super("JPC-RR");
469 running = false;
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,
485 PROFILE_ALWAYS);
486 menuManager.addSelectableMenuItem("Breakpoints→Trap VRetrace End", this, "menuVRetraceEnd", null, false,
487 PROFILE_ALWAYS);
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();
526 this.pc = null;
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);
544 validate();
545 setVisible(true);
548 public void menuAssemble(String i, Object[] args)
550 (new Thread(new AssembleTask())).start();
553 public void menuStart(String i, Object[] args)
555 start();
558 public void menuStop(String i, Object[] args)
560 stop();
563 public void menuReset(String i, Object[] args)
565 reset();
568 public void menuNOFPU(String i, Object[] args)
570 pc.setFPUHack();
573 public void menuVGADRAW(String i, Object[] args)
575 pc.setVGADrawHack();
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);
604 } else
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();
665 running = true;
666 notifyAll();
669 private String prettyPrintTime(long ts)
671 String s = "";
673 if(ts >= 1000000000)
674 s = s + "" + (ts / 1000000000) + " ";
675 if(ts >= 100000000)
676 s = s + "" + (ts % 1000000000 / 100000000);
677 if(ts >= 10000000)
678 s = s + "" + (ts % 100000000 / 10000000);
679 if(ts >= 1000000)
680 s = s + "" + (ts % 10000000 / 1000000) + " ";
681 if(ts >= 100000)
682 s = s + "" + (ts % 1000000 / 100000);
683 if(ts >= 10000)
684 s = s + "" + (ts % 100000 / 10000);
685 if(ts >= 1000)
686 s = s + "" + (ts % 10000 / 1000) + " ";
687 if(ts >= 100)
688 s = s + "" + (ts % 1000 / 100);
689 if(ts >= 10)
690 s = s + "" + (ts % 100 / 10);
691 s = s + "" + (ts % 10);
692 return s;
695 protected synchronized void stopNoWait()
697 running = false;
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()
706 willCleanup = true;
707 pc.getTraceTrap().doPotentialTrap(TraceTrap.TRACE_STOP_IMMEDIATE);
708 running = false;
709 System.err.println("Informational: Waiting for PC to halt...");
710 while(!waiting)
711 try {
712 wait();
713 } catch(Exception e) {
715 willCleanup = false;
716 stopNoWait();
719 public JScrollPane getMonitorPane()
721 return null;
724 protected void reset()
726 pc.reboot();
729 public synchronized boolean isRunning()
731 return running;
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
760 File choosen;
761 Exception caught;
762 PleaseWait pw;
763 int _mode;
764 long oTime;
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();
772 choosen = null;
773 _mode = mode;
774 pw = new PleaseWait("Loading savestate...");
777 public LoadStateTask(String name, int mode)
779 this(mode);
780 choosen = new File(name);
783 protected void runPrepare()
785 PCControl.this.setEnabled(false);
786 if(choosen == null) {
787 int returnVal = 0;
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)");
792 else
793 returnVal = snapshotFileChooser.showDialog(PCControl.this, "LOAD JPC-RR Snapshot");
794 choosen = snapshotFileChooser.getSelectedFile();
796 if (returnVal != 0)
797 choosen = null;
799 pw.popUp();
802 protected void runFinish()
804 if(caught == null) {
805 try {
806 connectPC(pc = currentProject.pc);
807 System.err.println("Informational: Loadstate done");
808 } catch(Exception e) {
809 caught = e;
812 pw.popDown();
813 if(caught != null) {
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()
823 if(choosen == null)
824 return;
826 try {
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;
836 else
837 fullStatus.rerecords++;
838 else
839 fullStatus.rerecords++;
841 currentProject = fullStatus;
843 reader.close();
844 long times2 = System.currentTimeMillis();
845 System.err.println("Informational: Loadstate complete (" + (times2 - times1) + "ms).");
846 } catch(Exception e) {
847 caught = e;
852 private class SaveStateTask extends AsyncGUITask
854 File choosen;
855 Exception caught;
856 boolean movieOnly;
857 PleaseWait pw;
858 long oTime;
860 public SaveStateTask(boolean movie)
862 oTime = System.currentTimeMillis();
863 choosen = null;
864 movieOnly = movie;
865 pw = new PleaseWait("Saving savestate...");
868 public SaveStateTask(String name, boolean movie)
870 this(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();
882 if (returnVal != 0)
883 choosen = null;
885 pw.popUp();
888 protected void runFinish()
890 pw.popDown();
891 if(caught != null) {
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()
901 if(choosen == null)
902 return;
904 JRSRArchiveWriter writer = null;
906 try {
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);
912 writer.close();
913 long times2 = System.currentTimeMillis();
914 System.err.println("Informational: Savestate complete (" + (times2 - times1) + "ms).");
915 } catch(Exception e) {
916 if(writer != null)
917 try { writer.rollback(); } catch(Exception f) {}
918 caught = e;
923 private class StatusDumpTask extends AsyncGUITask
925 File choosen;
926 Exception caught;
927 PleaseWait pw;
929 public StatusDumpTask()
931 choosen = null;
932 pw = new PleaseWait("Saving status dump...");
935 public StatusDumpTask(String name)
937 this();
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();
948 if (returnVal != 0)
949 choosen = null;
951 pw.popUp();
954 protected void runFinish()
956 pw.popDown();
957 if(caught != null) {
958 errorDialog(caught, "Status dump failed", PCControl.this, "Dismiss");
960 PCControl.this.setEnabled(true);
961 PCControl.this.vPluginManager.signalCommandCompletion();
964 protected void runTask()
966 if(choosen == null)
967 return;
969 try {
970 OutputStream outb = new BufferedOutputStream(new FileOutputStream(choosen));
971 PrintStream out = new PrintStream(outb, false, "UTF-8");
972 StatusDumper sd = new StatusDumper(out);
973 pc.dumpStatus(sd);
974 out.flush();
975 outb.flush();
976 System.err.println("Informational: Dumped " + sd.dumpedObjects() + " objects");
977 } catch(Exception e) {
978 caught = e;
983 private class RAMDumpTask extends AsyncGUITask
985 File choosen;
986 Exception caught;
987 PleaseWait pw;
988 boolean binary;
990 public RAMDumpTask(boolean binFlag)
992 choosen = null;
993 pw = new PleaseWait("Saving RAM dump...");
994 binary = binFlag;
997 public RAMDumpTask(String name, boolean binFlag)
999 this(binFlag);
1000 choosen = new File(name);
1003 protected void runPrepare()
1005 PCControl.this.setEnabled(false);
1006 if(choosen == null) {
1007 int returnVal;
1008 if(binary)
1009 returnVal = snapshotFileChooser.showDialog(PCControl.this, "Save RAM dump");
1010 else
1011 returnVal = snapshotFileChooser.showDialog(PCControl.this, "Save RAM hexdump");
1012 choosen = snapshotFileChooser.getSelectedFile();
1014 if (returnVal != 0)
1015 choosen = null;
1017 pw.popUp();
1020 protected void runFinish()
1022 pw.popDown();
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()
1032 if(choosen == null)
1033 return;
1035 try {
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;
1041 int highBound = 0;
1042 int present = 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);
1048 present++;
1049 highBound = lowBound + 1;
1050 lowBound = addr.findFirstRAMPage(++lowBound);
1052 outb.flush();
1053 System.err.println("Informational: Dumped machine RAM (" + highBound + " pages examined, " +
1054 present + " pages present).");
1055 } catch(Exception e) {
1056 caught = e;
1060 private byte charForHex(int hvalue)
1062 if(hvalue < 10)
1063 return (byte)(hvalue + 48);
1064 else if(hvalue > 9 && hvalue < 16)
1065 return (byte)(hvalue + 55);
1066 else
1067 System.err.println("Unknown hex value: " + hvalue + ".");
1068 return 90;
1071 private void dumpPage(OutputStream stream, int pageNo, byte[] buffer) throws IOException
1073 int pageBufSize;
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.
1077 if(binary)
1078 pageBufSize = 4096; //Binary page buffer is 4096 bytes.
1079 else
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++) {
1087 if(j < 5)
1088 outputPage[57 * i + j] = charForHex((pageNo >>> (4 * (4 - j))) & 0xF);
1089 else if(j == 5)
1090 outputPage[57 * i + j] = charForHex(i / 16);
1091 else if(j == 6)
1092 outputPage[57 * i + j] = charForHex(i % 16);
1093 else if(j == 7)
1094 outputPage[57 * i + j] = 48;
1095 else if(j == 56)
1096 outputPage[57 * i + j] = 10;
1097 else if(j % 3 == 2)
1098 outputPage[57 * i + j] = 32;
1099 else if(j % 3 == 0)
1100 outputPage[57 * i + j] = charForHex(((int)buffer[16 * i + j / 3 - 3] & 0xFF) / 16);
1101 else if(j % 3 == 1)
1102 outputPage[57 * i + j] = charForHex(buffer[16 * i + j / 3 - 3] & 0xF);
1103 else
1104 System.err.println("Error: dumpPage: unhandled j = " + j + ".");
1108 stream.write(outputPage);
1112 private class AssembleTask extends AsyncGUITask
1114 Exception caught;
1115 PleaseWait pw;
1116 boolean canceled;
1118 public AssembleTask()
1120 pw = new PleaseWait("Assembling PC...");
1121 canceled = false;
1124 protected void runPrepare()
1126 PCControl.this.setEnabled(false);
1127 try {
1128 configDialog.popUp();
1129 } catch(Exception e) {
1130 caught = e;
1134 protected void runFinish()
1136 if(caught == null && !canceled) {
1137 try {
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;
1144 connectPC(pc);
1145 } catch(Exception e) {
1146 caught = e;
1149 if(!canceled)
1150 pw.popDown();
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();
1161 if(hw == null) {
1162 canceled = true;
1163 return;
1165 try {
1166 SwingUtilities.invokeAndWait(new Thread() { public void run() { pw.popUp(); }});
1167 } catch(Exception e) {
1170 try {
1171 pc = PC.createPC(hw);
1172 } catch(Exception e) {
1173 caught = e;
1178 private class AddDiskTask extends AsyncGUITask
1180 Exception caught;
1181 boolean canceled;
1182 NewDiskDialog dd;
1184 public AddDiskTask()
1186 canceled = false;
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);
1201 try {
1202 updateDisks();
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();
1212 if(res == null) {
1213 canceled = true;
1214 return;
1216 try {
1217 DiskImage img;
1218 pc.getDisks().addDisk(img = new DiskImage(res.diskFile, false));
1219 img.setName(res.diskName);
1220 } catch(Exception e) {
1221 caught = e;
1226 private class ChangeAuthorsTask extends AsyncGUITask
1228 Exception caught;
1229 boolean canceled;
1230 AuthorsDialog ad;
1232 public ChangeAuthorsTask()
1234 int authors = 0;
1235 int headers = 0;
1236 String[] authorNames = null;
1237 canceled = false;
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);
1245 if(authors > 0) {
1246 int j = 0;
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();
1277 if(res == null) {
1278 canceled = true;
1279 return;
1281 try {
1282 int newAuthors = 0;
1283 int oldAuthors = 0;
1284 int headers = 0;
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"))
1289 oldAuthors++;
1291 if(res.authors != null) {
1292 for(int i = 0; i < res.authors.length; i++)
1293 if(res.authors[i] != null)
1294 newAuthors++;
1296 if(headers == oldAuthors && newAuthors == 0) {
1297 //Remove all extra headers.
1298 currentProject.extraHeaders = null;
1299 return;
1302 String[][] newHeaders = new String[headers + newAuthors - oldAuthors][];
1303 int writePos = 0;
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) {
1317 caught = e;