git @ Cat's Eye Technologies JaC64 / master JaC64.java
master

Tree @master (Download .tar.gz)

JaC64.java @masterraw · history · blame

/**
 * encoding: UTF-8
 * JaC64 - Application for JaC64 emulator
 * A Swing UI for the JaC64 emulator for download to Java enabled
 * Desktop computers.
 * Created: Sat Dec 08 23:27:15 2007
 *
 * @author Joakim Eriksson, Dreamfabric / joakime@sics.se
 * @version 1.0
 */
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;

import java.net.*;
import com.dreamfabric.jac64.*;
import com.dreamfabric.c64utils.*;

public class JaC64 implements ActionListener, KeyEventDispatcher {

  private static final String ABOUT_MESSAGE = 
    "JaC64 version: " + C64Screen.version + "\n" +
    "JaC64 is a Java-based C64 emulator by Joakim Eriksson\n" +
    "The SID emulation use the resid Java port by Ken Händel\n\n" +
    "For more information see: http://www.jac64.com/";

  private C64Reader reader;
  private C64Screen scr;
  private boolean fullscreen = false;

  private CPU cpu;
  private JFrame C64Win;
  private KeyListener c64Canvas;
  private FileDialog fileDialog;

  private JMenuItem load;
  private JTable fileTable;
  private JDialog loadFile;
  private DirEntry[] dirEntries;

  private static final String[] SID_TYPES = new String[] {"SID: resid MOS 6581", 
    "SID: resid MOS 8580", "SID: JaC64 Original"};
  
  private static final String[] JOYSTICK = new String[] {"Joystick in port 1",
    "Joystick in port 2"};
  
  private TableModel dataModel = new AbstractTableModel() {
    public final String[] NAMES = new String[] {"File name", "Size", "Type"};
    public int getColumnCount() { return 3;}
    public int getRowCount() { return (dirEntries != null ? dirEntries.length : 0);}
    public Object getValueAt(int row, int col) { 
      if (col == 0) return dirEntries[row].name;
      if (col == 1) return new Integer(dirEntries[row].size);
      if (col == 2) return dirEntries[row].getTypeString();
      return "-";
    }
    public String getColumnName(int col) {
      return NAMES[col];
    }
  };
  
  public JaC64() {
    SIDMixer.DL_BUFFER_SIZE = 16384;
    Debugger monitor = new Debugger();

    cpu = new CPU(monitor, "", new SELoader());
    scr = new C64Screen(monitor, true);
    cpu.init(scr);

    // Reader available after init!
    scr.init(cpu);

    scr.registerHotKey(KeyEvent.VK_BACK_SPACE, KeyEvent.CTRL_DOWN_MASK |
		       KeyEvent.ALT_DOWN_MASK
		       , "reset()", cpu);

    scr.registerHotKey(KeyEvent.VK_F12, KeyEvent.CTRL_DOWN_MASK
		       , "toggleFullScreen()", this);

    reader = new C64Reader(); // scr.getDiskDrive().getReader();
    reader.setCPU(cpu);
    cpu.getDrive().setReader(reader);

    C64Win = new JFrame("JaC64 - A Java C64 Emulator");
    C64Win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    JMenuBar jbar = new JMenuBar();
    C64Win.setJMenuBar(jbar);
    JMenu filem;
    JMenuItem mi;
    jbar.add(filem = new JMenu("File"));
    filem.add(mi = new JMenuItem("Open File/Disk"));
    mi.addActionListener(this);
    filem.add(load = new JMenuItem("Load File"));
    load.addActionListener(this);
    filem.add(mi = new JMenuItem("Reset"));
    mi.addActionListener(this);
    filem.add(mi = new JMenuItem("Hard Reset"));
    mi.addActionListener(this);
    filem.add(mi = new JMenuItem("About JaC64"));
    mi.addActionListener(this);

    jbar.add(filem = new JMenu("Settings"));
    JMenu subm;
    filem.add(subm = new JMenu("Color Set"));
    createRadioMenu(subm, new String[] {"Color Set 1 - JaC64 original",
        "Color Set 2 - darker", "Color Set 3 - softer", 
        "Color Set 4 - Win VICE"}, 0);

    filem.add(subm = new JMenu("SID Emulation"));
    createRadioMenu(subm, SID_TYPES, 0);
    
    filem.add(subm = new JMenu("Joystick Port"));
    createRadioMenu(subm, JOYSTICK, 0);
    
    C64Win.setBackground(Color.black);
    C64Win.setForeground(Color.black);
    C64Win.setLayout(new BorderLayout());
    C64Win.getContentPane().add(scr.getScreen(),
				BorderLayout.CENTER);
    C64Win.setFocusable(true);

    C64Win.pack(); //	C64Scr.setSize(380,300);
    C64Win.setSize(386 * 2 + 10, 284 * 2 + 70);
    C64Win.setResizable(true);
    C64Win.setVisible(true);
    
    KeyboardFocusManager.
      getCurrentKeyboardFocusManager().addKeyEventDispatcher(this);

    // Setup disk sounds
    AudioClip trackSound = null;
    AudioClip motorSound = null;
    URL url = getClass().getResource("sounds/track.wav");
    if (url != null) trackSound = Applet.newAudioClip(url);
    url = getClass().getResource("sounds/motor.wav");
    if (url != null) motorSound = Applet.newAudioClip(url);
    scr.setSounds(trackSound, motorSound);
    c64Canvas = (KeyListener) scr.getScreen();
  }

  private void createRadioMenu(JMenu subm, String[] names, int selected) {
    ButtonGroup group = new ButtonGroup();
    JRadioButtonMenuItem mi;
    for (int i = 0; i < names.length; i++) {
      subm.add(mi = new JRadioButtonMenuItem(names[i]));
      if (i == selected)
        mi.setSelected(true);
      mi.addActionListener(this);
      group.add(mi);      
    }
  }
  
  public boolean dispatchKeyEvent(KeyEvent e) {
    if (C64Win.isFocused()) {
      if (e.getID() == KeyEvent.KEY_PRESSED) {
        c64Canvas.keyPressed(e);
      } else if (e.getID() == KeyEvent.KEY_RELEASED) {
        c64Canvas.keyReleased(e);
      } else if (e.getID() == KeyEvent.KEY_TYPED) {
        c64Canvas.keyTyped(e);
      }
      return true;
    }
    return false;
  }

  public void actionPerformed(ActionEvent ae) {
    String cmd = ae.getActionCommand();
    if ("Open File/Disk".equals(cmd)) {
      if (fileTable == null) {
        // Show the table somewhere!!!
        fileTable = new JTable(dataModel);
        fileTable.getColumnModel().getColumn(1).setMaxWidth(50);
        fileTable.getColumnModel().getColumn(2).setMaxWidth(50);
        fileTable.setShowGrid(false);
        fileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      }
      readDisk();
    } else if ("Reset".equals(cmd)){
      cpu.reset();
    } else if ("Hard Reset".equals(cmd)){
      cpu.hardReset();
    } else if ("About JaC64".equals(cmd)) {
      showAbout();
    } else if ("Load File".equals(cmd)) {
      if (loadFile == null) {
        loadFile = new JDialog(C64Win, "Load file from disk");
        loadFile.setAlwaysOnTop(true);
        loadFile.setVisible(true);
        loadFile.setLayout(new BorderLayout());
        loadFile.add(new JScrollPane(fileTable), BorderLayout.CENTER);
        loadFile.add(fileTable.getTableHeader(), BorderLayout.NORTH);
        JPanel jp = new JPanel();
        JButton jb;
        jp.add(jb = new JButton("Load file"));
        jb.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent ae) {
            reader.readFile(dirEntries[fileTable.getSelectedRow()].name);
            loadFile.setVisible(false);
          }
        });
        jp.add(jb = new JButton("Cancel"));
        jb.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent ae) {
            loadFile.setVisible(false);
          }
        });    
        loadFile.add(jp, BorderLayout.SOUTH);
        loadFile.setSize(300, 400);
      }
      loadFile.setVisible(true);
    } else if (cmd.startsWith("Color Set")) {
      int cs = cmd.charAt(10) - '1';
      System.out.println("Color set: " + cs);
      scr.setColorSet(cs);
    } else if (cmd.equals(SID_TYPES[0])) {
      scr.setSID(C64Screen.RESID_6581);
    } else if (cmd.equals(SID_TYPES[1])) {
        scr.setSID(C64Screen.RESID_8580);
    } else if (cmd.equals(SID_TYPES[2])) {
        scr.setSID(C64Screen.JACSID);
    } else if (cmd.equals(JOYSTICK[0])) {
      scr.setStick(true);
    } else if (cmd.equals(JOYSTICK[1])) {
      scr.setStick(false);
    }
  }

  private void showAbout() {
    JOptionPane.showMessageDialog(C64Win, ABOUT_MESSAGE,
        "JaC64 - The Java C64 Emulator", JOptionPane.INFORMATION_MESSAGE);
  }
  
  public void toggleFullScreen() {
    System.out.println("Toggle fullscreen called!");
    setFull(!fullscreen);
  }

  private void readDisk() {
    if (fileDialog == null)
      fileDialog = new FileDialog(C64Win, "Select File/Disk to Load");
    fileDialog.setVisible(true);

    String name = fileDialog.getDirectory() + fileDialog.getFile();
    if (!readDisk(name)) {
      dirEntries = (DirEntry[]) reader.getDirNames().toArray(new DirEntry[0]);
      fileTable.tableChanged(new TableModelEvent(dataModel));
    }
  }

  private boolean readDisk(String name) {
    System.out.println("READING FROM: " + name);

    if ((name.toLowerCase()).endsWith(".d64"))
      reader.readDiskFromFile(name);
    else if ((name.toLowerCase()).endsWith(".t64"))
      reader.readTapeFromFile(name);
    else if (name.toLowerCase().endsWith(".prg") ||
        name.toLowerCase().endsWith(".p00")) {
      cpu.reset();
      try {
        Thread.sleep(10);
      }catch (Exception e2) {
        System.out.println("Exception while sleeping...");
      }
      while(!scr.ready()) {
        try {
          Thread.sleep(100);
        }catch (Exception e2) {
          System.out.println("Exception while sleeping...");
        }
      }
      reader.readPGM(name, -1);
      cpu.runBasic();
      return true;
    }
    return false;
  }

  private boolean readDisk(URL url) {
    String name = url.toString();

    System.out.println("READING FROM URL: " + name);

    if ((name.toLowerCase()).endsWith(".d64"))
      reader.readDiskFromURL(url);
    else if ((name.toLowerCase()).endsWith(".t64"))
      reader.readTapeFromURL(url);
    else if (name.toLowerCase().endsWith(".prg") ||
             name.toLowerCase().endsWith(".p00")) {
      cpu.reset();
      try {
        Thread.sleep(10);
      }catch (Exception e2) {
        System.out.println("Exception while sleeping...");
      }
      while(!scr.ready()) {
        try {
          Thread.sleep(100);
        }catch (Exception e2) {
          System.out.println("Exception while sleeping...");
        }
      }
      reader.readPGM(url, -1);
      cpu.runBasic();
      return true;
    }
    return false;
  }

  private void setFull(boolean full) {
//     JWindow jw = full ? C64Scr : null;
//     java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().
//       getDefaultScreenDevice().setFullScreenWindow(jw);
//     if (!full) {
//       C64Scr.setSize(386 * 2, 284 * 2);
//       C64Scr.validate();
//     }
//     fullscreen = full;
  }

  private void waitForKernal() {
    while(!scr.ready()) {
      try {
	Thread.sleep(100);
      } catch (Exception e2) {
	System.out.println("Exception while sleeping...");
      }
    }
  }

  private void autoStart(String filename) {
    Thread t = new Thread(new Runnable() {
      public void run() {
        waitForKernal();
        System.out.println("Kernal READY!");
        URL url = getClass().getResource(filename);
        if (url != null) {
          readDisk(url);
        } else {
          readDisk(filename);
        }
      }
    });
    t.start();
  }

  public static void main(String[] args) {
    String autostart = null;

    for (int i = 0; i < args.length; i++) {
      if (args[i].equals("-a")) {
        i++;
        autostart = args[i];
      } else {
        System.out.println("Usage: java [-cp <classpath>] JaC64 [-a <autostart(.d64|.t64|.prg|.p00)>]");
        System.exit(1);
      }
    }

    JaC64 emu = new JaC64();
    if (autostart != null) {
      emu.autoStart(autostart);
    }

    emu.cpu.start();
  }
}