/*
    JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine
    Release Version 2.4

    A project from the Physics Dept, The University of Oxford

    Copyright (C) 2007-2010 The University of Oxford

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 2 as published by
    the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
    Details (including contact information) can be found at: 

    jpc.sourceforge.net
    or the developer website
    sourceforge.net/projects/jpc/

    Conceived and Developed by:
    Rhys Newman, Ian Preston, Chris Dennis

    End of licence header
*/

package org.jpc.debugger;

import java.util.*;
import java.util.zip.*;
import java.io.*;
import java.text.DecimalFormat;
import java.awt.Color;
import java.awt.event.*;

import javax.swing.*;

import org.jpc.debugger.util.*;
import org.jpc.emulator.PC;
import org.jpc.emulator.processor.fpu64.FpuState64;
import org.jpc.j2se.Option;
import org.jpc.support.*;
import org.jpc.emulator.memory.*;
import org.jpc.emulator.processor.Processor;
import org.jpc.emulator.peripheral.Keyboard;
import org.jpc.emulator.pci.peripheral.VGACard;
import org.jpc.j2se.VirtualClock;

public class JPC extends ApplicationFrame implements ActionListener {

    private static JPC instance = null;
    private ObjectDatabase objects;
    private CodeBlockRecord codeBlocks;
    private RunMenu runMenu;
    private JDesktopPane desktop;
    private DiskSelector floppyDisk,  hardDisk, cdrom;
    private JMenuItem createPC,  scanForImages,  loadSnapshot,  saveSnapshot;
    private JMenuItem fpuFrame, processorFrame, physicalMemoryViewer, linearMemoryViewer, watchpoints, breakpoints, traceFrame,  monitor;

    private JPC(boolean fullScreen) {
        super("JPC Debugger");

        if (fullScreen) {
            setBoundsToMaximum();
        } else {
            setBounds(0, 0, 1024, 900);
        }
        objects = new ObjectDatabase();
        desktop = new JDesktopPane();
        add("Center", desktop);

        JFileChooser chooser = new JFileChooser(System.getProperty("user.dir"));
        chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        objects.addObject(chooser);

        JMenuBar bar = new JMenuBar();
        JMenu actions = new JMenu("Actions");
        createPC = actions.add("Create New PC");
        createPC.setEnabled(false);
        createPC.addActionListener(this);
        scanForImages = actions.add("Scan Directory for Images");
        scanForImages.addActionListener(this);
        actions.addSeparator();
        loadSnapshot = actions.add("Load Snapshot");
        loadSnapshot.addActionListener(this);
        saveSnapshot = actions.add("Save Snapshot");
        saveSnapshot.addActionListener(this);
        actions.addSeparator();
        actions.add("Quit").addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                frameCloseRequested();
            }
        });

        JMenu windows = new JMenu("Windows");
        monitor = windows.add("PC Monitor");
        monitor.addActionListener(this);
        processorFrame = windows.add("Processor Frame");
        processorFrame.addActionListener(this);
        fpuFrame = windows.add("FPU Frame");
        fpuFrame.addActionListener(this);
        physicalMemoryViewer = windows.add("Physical Memory Viewer");
        physicalMemoryViewer.addActionListener(this);
        linearMemoryViewer = windows.add("Linear Memory Viewer");
        linearMemoryViewer.addActionListener(this);
        breakpoints = windows.add("Breakpoints");
        breakpoints.addActionListener(this);
        watchpoints = windows.add("Watchpoints");
        watchpoints.addActionListener(this);
        traceFrame = windows.add("Execution Trace Frame");
        traceFrame.addActionListener(this);
//         frequencies = windows.add("Opcode Frequency Frame");
//         frequencies.addActionListener(this);

        JMenu tools = new JMenu("Tools");
        tools.add("Create Blank Disk (file)").addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                createBlankHardDisk();
            }
        });

        runMenu = new RunMenu();

        floppyDisk = new DiskSelector("FD", Color.red);
        hardDisk = new DiskSelector("HD", Color.blue);
        cdrom = new DiskSelector("CD", Color.blue);

        bar.add(actions);
        bar.add(windows);
        bar.add(runMenu);
        bar.add(tools);
        bar.add(floppyDisk);
        bar.add(hardDisk);
        bar.add(cdrom);

        bar.add(Box.createHorizontalGlue());
        bar.add(new Hz());

        codeBlocks = null;
        setJMenuBar(bar);

        resyncImageSelection(new File(System.getProperty("user.dir")));
    }

    public int executeStep()
    {
        return runMenu.executeStep();
    }

    private void initialLayout()
    {
        ProcessorFrame pf = new ProcessorFrame();
        objects.addObject(pf);
        addInternalFrame(desktop, -10, 10, pf);
        ExecutionTraceFrame tr = new ExecutionTraceFrame();
        addInternalFrame(desktop, -(20 + pf.getWidth()), 10, tr);
    }

    private void resyncImageSelection(File dir) {
        floppyDisk.rescan(dir);
        hardDisk.rescan(dir);
        cdrom.rescan(dir);

        checkBootEnabled();
    }

    private void checkBootEnabled() {
        createPC.setEnabled(floppyDisk.isBootDevice() || hardDisk.isBootDevice() || cdrom.isBootDevice());
    }

    class DiskSelector extends JMenu implements ActionListener {

        String mainTitle;
        ButtonGroup group;
        List<File> diskImages;
        Map<ButtonModel, File> lookup;
        JCheckBoxMenuItem bootFrom;
        JMenuItem openFile;

        public DiskSelector(String mainTitle, Color fg) {
            super(mainTitle);
            this.mainTitle = mainTitle;
            setForeground(fg);

            lookup = new HashMap();
            diskImages = new Vector();
            group = new ButtonGroup();
            bootFrom = new JCheckBoxMenuItem("Set as Boot Device");
            bootFrom.addActionListener(this);
            openFile = new JMenuItem("Select Image File");
            openFile.addActionListener(this);
        }

        public void actionPerformed(ActionEvent evt) {
            if (evt.getSource() == openFile) {
                JFileChooser chooser = (JFileChooser) objects.getObject(JFileChooser.class);
                if (chooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION) {
                    return;
                }
                rescan(chooser.getSelectedFile());
            }

            resetTitle();
        }

        public void setSelectedFile(File f, boolean isBootDevice) {
            rescan(f);
            bootFrom.setState(isBootDevice);
            resetTitle();
        }

        private void resetTitle() {
            String fileName = "";
            File f = getSelectedFile();
            if (f != null) {
                fileName = f.getAbsolutePath();
            }
            if (isBootDevice()) {
                setText(mainTitle + " >" + fileName + "<");
            } else {
                setText(mainTitle + " " + fileName);
            }
            checkBootEnabled();
            if (bootFrom.getState() && (getSelectedFile() == null)) {
                bootFrom.setState(false);
            }
        }

        public File getSelectedFile() {
            ButtonModel selectedModel = group.getSelection();
            if (selectedModel == null) {
                return null;
            }
            return lookup.get(selectedModel);
        }

        public boolean isBootDevice() {
            return bootFrom.getState() && (getSelectedFile() != null);
        }

        void rescan(File f) {
            File selected = getSelectedFile();
            boolean isBoot = isBootDevice();

            for (int i = diskImages.size() - 1; i >= 0; i--) {
                if (!diskImages.get(i).exists()) {
                    diskImages.remove(i);
                }
            }
            if (f.isDirectory()) {
                File[] files = f.listFiles();
                for (int i = 0; i < files.length; i++) {
                    if (files[i].getName().toLowerCase().endsWith(".img")) {
                        if (!diskImages.contains(files[i])) {
                            diskImages.add(files[i]);
                        }
                    }
                }
            } else if (f.exists()) {
                boolean found = false;
                for (int i = 0; i < diskImages.size(); i++) {
                    if (diskImages.get(i).getAbsolutePath().equals(f.getAbsolutePath())) {
                        selected = diskImages.get(i);
                        found = true;
                    }
                }
                if (!found) {
                    diskImages.add(f);
                    selected = f;
                }
            }

            removeAll();
            lookup.clear();

            group = new ButtonGroup();
            bootFrom.setState(isBoot);
            add(bootFrom);
            addSeparator();

            for (int i = 0; i < diskImages.size(); i++) {
                File ff = diskImages.get(i);
                JRadioButtonMenuItem item = new JRadioButtonMenuItem(ff.getAbsolutePath());
                item.addActionListener(this);
                lookup.put(item.getModel(), ff);

                group.add(item);
                add(item);

                if (ff.equals(selected)) {
                    group.setSelected(item.getModel(), true);
                }
            }

            addSeparator();
            add(openFile);
        }
    }

    public Object get(Class cls) {
        return objects.getObject(cls);
    }

    public ObjectDatabase objects() {
        return objects;
    }

    public JDesktopPane getDesktop() {
        return desktop;
    }

    protected void frameCloseRequested() {
        BreakpointsFrame bp = (BreakpointsFrame) objects.getObject(BreakpointsFrame.class);
        if ((bp != null) && bp.isEdited()) {
            bp.dispose();
        }
        WatchpointsFrame wp = (WatchpointsFrame) objects.getObject(WatchpointsFrame.class);
        if ((wp != null) && wp.isEdited()) {
            wp.dispose();
        }
        System.exit(0);
    }

    public void bringToFront(JInternalFrame f) {
        desktop.moveToFront(f);
        desktop.setSelectedFrame(f);
    }

    public void actionPerformed(ActionEvent evt) {
        Object src = evt.getSource();

        if (src == scanForImages) {
            JFileChooser chooser = (JFileChooser) objects.getObject(JFileChooser.class);
            if (chooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION) {
                return;
            }
            File dir = chooser.getSelectedFile();
            if (!dir.isDirectory()) {
                dir = dir.getParentFile();
            }
            resyncImageSelection(dir);
        } else if (src == createPC) {
            try {
                File floppyImage = floppyDisk.getSelectedFile();
                File hardImage = hardDisk.getSelectedFile();
                File cdImage = cdrom.getSelectedFile();

                DriveSet.BootType bootType;
                if (floppyDisk.isBootDevice()) {
                    if (!floppyImage.exists()) {
                        alert("Floppy Image: " + floppyImage + " does not exist", "Boot", JOptionPane.ERROR_MESSAGE);
                        return;
                    }
                    bootType = DriveSet.BootType.FLOPPY;
                } else if (hardDisk.isBootDevice())
                {
                    if (!hardImage.exists()) {
                        alert("Hard disk Image: " + hardImage + " does not exist", "Boot", JOptionPane.ERROR_MESSAGE);
                        return;
                    }
                    bootType = DriveSet.BootType.HARD_DRIVE;
                } else {
                    if (!cdImage.exists()) {
                        alert("CD Image: " + cdImage + " does not exist", "Boot", JOptionPane.ERROR_MESSAGE);
                        return;
                    }
                    bootType = DriveSet.BootType.CDROM;
                }

                String[] args;
                int argc = 0;
                if (floppyImage != null) {
                    argc += 2;
                }
                if (hardImage != null) {
                    argc += 2;
                }
                if (cdImage != null) {
                    argc += 2;
                }
                if (argc > 2) {
                    argc += 2;
                }
                args = new String[argc];

                int pos = 0;
                if (floppyImage != null) {
                    args[pos++] = "-fda";
                    args[pos++] = floppyImage.getAbsolutePath();
                }
                if (hardImage != null) {
                    args[pos++] = "-hda";
                    args[pos++] = hardImage.getAbsolutePath();
                }
                if (cdImage != null) {
                    args[pos++] = "-cdrom";
                    args[pos++] = cdImage.getAbsolutePath();
                }
                if (pos <= (argc - 2)) {
                    args[pos++] = "-boot";
                    if (bootType == DriveSet.BootType.HARD_DRIVE) {
                        args[pos++] = "hda";
                    } else if (bootType == DriveSet.BootType.CDROM) {
                        args[pos++] = "cdrom";
                    } else {
                        args[pos++] = "fda";
                    }
                }

                instance.createPC(args);
                resyncImageSelection(new File(System.getProperty("user.dir")));
            } catch (Exception e) {
                alert("Failed to create PC: " + e, "Boot", JOptionPane.ERROR_MESSAGE);
            }
        } else if (src == loadSnapshot) {
            runMenu.stop();
            JFileChooser fc = new JFileChooser();
            try {
                BufferedReader in = new BufferedReader(new FileReader("prefs.txt"));
                String path = in.readLine();
                in.close();
                if (path != null) {
                    File f = new File(path);
                    if (f.isDirectory()) {
                        fc.setCurrentDirectory(f);
                    }
                }
            } catch (Exception e) {
            }

            int returnVal = fc.showDialog(this, "Load JPC Snapshot");
            File file = fc.getSelectedFile();
            try {
                if (file != null) {
                    BufferedWriter out = new BufferedWriter(new FileWriter("prefs.txt"));
                    out.write(file.getPath());
                    out.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (returnVal == 0) {
                try {
                    System.out.println("Loading a snapshot of JPC");
                    ZipInputStream zin = new ZipInputStream(new FileInputStream(file));
                    zin.getNextEntry();
                    ((PC) objects.getObject(PC.class)).loadState(zin);
                    zin.closeEntry();
                    ((PCMonitorFrame) objects.getObject(PCMonitorFrame.class)).resizeDisplay();
                    zin.getNextEntry();
                    ((PCMonitorFrame) objects.getObject(PCMonitorFrame.class)).loadMonitorState(zin);
                    zin.closeEntry();
                    zin.close();
                    System.out.println("done");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } else if (src == saveSnapshot) {
            runMenu.stop();
            JFileChooser fc = new JFileChooser();
            try {
                BufferedReader in = new BufferedReader(new FileReader("prefs.txt"));
                String path = in.readLine();
                in.close();
                if (path != null) {
                    File f = new File(path);
                    if (f.isDirectory()) {
                        fc.setCurrentDirectory(f);
                    }
                }
            } catch (Exception e) {
            }

            int returnVal = fc.showDialog(this, "Save JPC Snapshot");
            File file = fc.getSelectedFile();
            try {
                if (file != null) {
                    BufferedWriter out = new BufferedWriter(new FileWriter("prefs.txt"));
                    out.write(file.getPath());
                    out.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (returnVal == 0) {
                try {
                    ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(file));
                    zip.putNextEntry(new ZipEntry("pc"));
                    ((PC) objects.getObject(PC.class)).saveState(zip);
                    zip.closeEntry();
                    zip.putNextEntry(new ZipEntry("monitor"));
                    ((PCMonitorFrame) objects.getObject(PCMonitorFrame.class)).saveState(zip);
                    zip.closeEntry();
                    zip.finish();
                    zip.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } else if (src == processorFrame) {
            ProcessorFrame pf = (ProcessorFrame) objects.getObject(ProcessorFrame.class);
            if (pf != null) {
                bringToFront(pf);
            } else {
                pf = new ProcessorFrame();
                objects.addObject(pf);
                addInternalFrame(desktop, 10, 10, pf);
            }
        } else if (src == fpuFrame) {
            FPUFrame pf = (FPUFrame) objects.getObject(FPUFrame.class);
            if (pf != null) {
                bringToFront(pf);
            } else {
                pf = new FPUFrame();
                addInternalFrame(desktop, 10, 10, pf);
            }
        } else if (src == physicalMemoryViewer) {
            MemoryViewer mv = (MemoryViewer) objects.getObject(MemoryViewer.class);

            if (mv != null) {
                bringToFront(mv);
            } else {
                mv = new MemoryViewer("Physical Memory");
                addInternalFrame(desktop, 360, 50, mv);
            }
        } else if (src == linearMemoryViewer) {
            LinearMemoryViewer lmv = (LinearMemoryViewer) objects.getObject(LinearMemoryViewer.class);

            if (lmv != null) {
                bringToFront(lmv);
            } else {
                lmv = new LinearMemoryViewer("Linear Memory");
                addInternalFrame(desktop, 360, 50, lmv);
            }
        } else if (src == breakpoints) {
            BreakpointsFrame bp = (BreakpointsFrame) objects.getObject(BreakpointsFrame.class);
            if (bp != null) {
                bringToFront(bp);
            } else {
                bp = new BreakpointsFrame();
                addInternalFrame(desktop, 10, 560, bp);
            }
        } else if (src == watchpoints) {
            WatchpointsFrame wp = (WatchpointsFrame) objects.getObject(WatchpointsFrame.class);
            if (wp != null) {
                bringToFront(wp);
            } else {
                wp = new WatchpointsFrame();
                addInternalFrame(desktop, 550, 560, wp);
            }
        } else if (src == traceFrame) {
            ExecutionTraceFrame tr = (ExecutionTraceFrame) objects.getObject(ExecutionTraceFrame.class);
            if (tr != null) {
                bringToFront(tr);
            } else {
                tr = new ExecutionTraceFrame();
                objects.addObject(tr);
                addInternalFrame(desktop, 30, 100, tr);
            }
        } else if (src == monitor) {
            PCMonitorFrame m = (PCMonitorFrame) objects.getObject(PCMonitorFrame.class);
            if (m != null) {
                bringToFront(m);
            } else {
                m = new PCMonitorFrame();
                addInternalFrame(desktop, 10, 10, m);
            }
        }
//         else if (src == frequencies)
//         {
//             OpcodeFrequencyFrame f = (OpcodeFrequencyFrame) objects.getObject(OpcodeFrequencyFrame.class);
//             if (f != null)
//                 bringToFront(f);
//             else
//             {
//                 f = new OpcodeFrequencyFrame();
//                 addInternalFrame(desktop, 550, 30, f);
//             }
//         }

        refresh();
    }

    public void notifyExecutionStarted() {
        for (Object obj : objects.entries()) {
            if (!(obj instanceof PCListener)) {
                continue;
            }
            try {
                ((PCListener) obj).executionStarted();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void notifyExecutionStopped() {
        for (Object obj : objects.entries()) {
            if (!(obj instanceof PCListener)) {
                continue;
            }
            try {
                ((PCListener) obj).executionStopped();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void notifyPCDisposed() {
        for (Object obj : objects.entries()) {
            if (!(obj instanceof PCListener)) {
                continue;
            }
            try {
                ((PCListener) obj).pcDisposed();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void notifyPCCreated() {
        for (Object obj : objects.entries()) {
            if (!(obj instanceof PCListener)) {
                continue;
            }
            try {
                ((PCListener) obj).pcCreated();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void refresh() {
        for (Object obj : objects.entries()) {
            if (!(obj instanceof PCListener)) {
                continue;
            }
            try {
                ((PCListener) obj).refreshDetails();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public PC loadNewPC(PC pc) {
        PC oldPC = (PC) objects.removeObject(PC.class);
        if (oldPC != null) {
            notifyPCDisposed();
        }
        JInternalFrame[] frames = desktop.getAllFrames();
        for (int i = 0; i < frames.length; i++) {
            frames[i].dispose();
        }

        objects.removeObject(Processor.class);
        objects.removeObject(PhysicalAddressSpace.class);
        objects.removeObject(LinearAddressSpace.class);
        objects.removeObject(VGACard.class);
        objects.removeObject(Keyboard.class);
        objects.removeObject(ProcessorAccess.class);
        objects.removeObject(CodeBlockRecord.class);

        for (int i = 0; i < 10; i++) {
            System.gc();
            try {
                Thread.sleep(100);
            } catch (Exception e) {
            }
        }

        setTitle("JPC Debugger - Boot Device: " + ((DriveSet) pc.getComponent(DriveSet.class)).getBootDevice());
        objects.addObject(pc);
        objects.addObject(pc.getProcessor());
        objects.addObject(pc.getComponent(LinearAddressSpace.class));
        objects.addObject(pc.getComponent(PhysicalAddressSpace.class));
        objects.addObject(pc.getComponent(VGACard.class));
        objects.addObject(pc.getComponent(Keyboard.class));

        codeBlocks = new CodeBlockRecord(pc);
        objects.addObject(codeBlocks);
        ProcessorAccess pca = ProcessorAccess.create(runMenu.isTimeTravel(), pc.getProcessor());
        objects.addObject(ProcessorAccess.class, pca);
        FPUAccess fpua = new FPUAccess((FpuState64)pc.getProcessor().fpu);
        objects.addObject(fpua);

        runMenu.refresh();
        notifyPCCreated();

        monitor.doClick();

        return pc;
    }

    public PC createPC(String[] args) throws IOException
    {
        PC pc = new PC(new VirtualClock(), args);
        loadNewPC(pc);

        String snapShot = ArgProcessor.findVariable(args, "ss", null);
        if (snapShot == null) {
            return pc;
        }
        File file = new File(snapShot);
        ZipInputStream zin = new ZipInputStream(new FileInputStream(file));
        zin.getNextEntry();
        pc.loadState(zin);
        zin.closeEntry();
        ((PCMonitorFrame) objects.getObject(PCMonitorFrame.class)).resizeDisplay();
        zin.getNextEntry();
        ((PCMonitorFrame) objects.getObject(PCMonitorFrame.class)).loadMonitorState(zin);
        zin.closeEntry();
        zin.close();
        return pc;
    }

    class Hz extends JLabel implements ActionListener {

        DecimalFormat fmt;
        long lastCount, lastTime;

        Hz() {
            super("MHz = 0");
            fmt = new DecimalFormat("#.##");
            lastTime = System.currentTimeMillis();
            javax.swing.Timer timer = new javax.swing.Timer(1000, this);
            timer.setRepeats(true);
            timer.start();
        }

        public void actionPerformed(ActionEvent evt) {
            if (codeBlocks == null) {
                return;
            }
            long ticks = ((PC)objects.getObject(PC.class)).getState()[16];
            long count = codeBlocks.getInstructionCount();
            long decoded = codeBlocks.getDecodedCount();
            long executed = codeBlocks.getExecutedBlockCount();
            long now = System.currentTimeMillis();

            double mhz = 1000.0 * (count - lastCount) / (now - lastTime) / 1000000;
            setText("Decoded: (" + decoded + " x86 Instr) | Executed: (" + commaSeparate(count) + " x86 Instr) (" + executed + " Blocks) ("+commaSeparate(ticks)+" ticks) | " + fmt.format(mhz) + " MHz");
            lastCount = count;
            lastTime = now;
        }

        public String commaSeparate(long n) {
            String s = Long.toString(n);
            if (s.length() < 4) {
                return s;
            }
            StringBuffer buf = new StringBuffer();
            int offset = (s.length() % 3);
            if (offset == 0) {
                buf.append(s.substring(0, 3));
                offset = 3;
                for (int i = 0; i < (int) ((s.length() - 1) / 3); i++) {
                    buf.append(",");
                    buf.append(s.substring(offset + 3 * i, offset + 3 * i + 3));
                }
            } else {
                buf.append(s.substring(0, offset));
                for (int i = 0; i < (int) ((s.length() - 1) / 3); i++) {
                    buf.append(",");
                    buf.append(s.substring(offset + 3 * i, offset + 3 * i + 3));
                }
            }
            return buf.toString();
        }
    }

    public void createBlankHardDisk() {
        try {
            JFileChooser chooser = (JFileChooser) objects.getObject(JFileChooser.class);
            if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) {
                return;
            }
            String sizeString = JOptionPane.showInputDialog(this, "Enter the size in MB for the disk", "Disk Image Creation", JOptionPane.QUESTION_MESSAGE);
            if (sizeString == null) {
                return;
            }
            long size = Long.parseLong(sizeString) * 1024l * 1024l;
            if (size < 0) {
                throw new Exception("Negative file size");
            }
            RandomAccessFile f = new RandomAccessFile(chooser.getSelectedFile(), "rw");
            f.setLength(size);
            f.close();
        } catch (Exception e) {
            alert("Failed to create blank disk " + e, "Create Disk", JOptionPane.ERROR_MESSAGE);
        }
    }

    public static Object getObject(Class cls) {
        return instance.get(cls);
    }

    public static Object getPC()
    {
        return instance.getObject(PC.class);
    }

    public static JPC getInstance() {
        return instance;
    }

    public static void main(String[] args) throws IOException {
        List<String> tmp = new ArrayList<String>();
        tmp.add("-debug-blocks");
        tmp.add("-fullscreen");
        tmp.addAll(Arrays.asList(args));
        args = Option.parse(tmp.toArray(new String[tmp.size()]));
        initialise();

        boolean fullScreen = Option.fullscreen.isSet();
        instance = new JPC(fullScreen);
        instance.validate();
        instance.setVisible(true);

        if ((args.length > 0) || Option.config.isSet() || Option.boot.isSet()) {
            instance.createPC(args);
        }
        instance.initialLayout();
    }
}
