/**
* This file is a part of JaC64 - a Java C64 Emulator
* Main Developer: Joakim Eriksson (Dreamfabric.com)
* Contact: joakime@sics.se
* Web: http://www.dreamfabric.com/c64
* ---------------------------------------------------
*/
/**
* @(#)C64Test.java Created date: 99-7-06
*
*/
import java.net.URL;
import java.applet.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.awt.image.*;
import java.util.*;
import java.io.*;
import com.dreamfabric.jac64.*;
import com.dreamfabric.c64utils.*;
/**
* A test program that starts the CPU and loads files into its RAM
*
* @author Joakim Eriksson (joakime@sics.se)
* @version $Revision: 1.15 $, $Date: 2006/05/01 14:57:57 $
*/
public class C64Test implements ActionListener, Runnable {
private C64Reader reader;
private C64Screen scr;
private boolean fullscreen = false;
private static final boolean FULLSCREEN = true; // false;
// Set the antialiasing to get the right look!
public final static RenderingHints AALIAS =
new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
private JFrame window;
private JPanel infoWindow;
private FileDialog fileDialog;
private JToggleButton debug;
private JToggleButton joy;
private JToggleButton sound;
private JToggleButton speed;
private JButton pause;
private JButton load;
private JButton normal;
private JButton full;
private JButton save;
private JButton vicdump;
private JButton color;
private JButton saveDisk;
private JButton openAssembler;
private ArrayList dirNames = new ArrayList();
private JList list;
private JTextField txt;
private JTextField prefix;
private JTextField file;
private JTextField fileName; // For save...
private JFrame asmFrame;
private JTextArea asmText;
private JTextField asmAdr;
private JButton asm;
private Assembler assembler;
private JLabel stext;
private int sprnum = 0;
private Image sprite = null;
private MemoryImageSource mis = null;
private int[] sprMem = new int[24 * 21]; // Sprite size
private JPanel sprPan;
private CPU cpu;
private int[] memory;
private int cset = 0;
private JWindow C64Scr;
private JFrame C64Win;
private Color lblue;
private Debugger monitor;
public C64Test(String file) {
// System.out.println("Locale: " + l);
// Locale.setDefault(Locale.US);
// l = Locale.getDefault();
// Set the buffer for SID chip to lower than 1 second...
SIDMixer.DL_BUFFER_SIZE = 16384;
monitor = new Debugger();
// monitor.setEnabled(true);
cpu = new CPU(monitor, "", new SELoader());
monitor.init(cpu);
memory = cpu.getMemory();
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);
if (FULLSCREEN) {
JFrame jf = new JFrame("nada");
jf.setVisible(true);
C64Scr = new JWindow(jf); // new JFrame("JaC64 - A Java C64 Emulator");
} else {
C64Win = new JFrame("JaC64 - A Java C64 Emulator");
}
// JMenuBar jbar = new JMenuBar();
// C64Scr.setJMenuBar(jbar);
// JMenu filem;
// jbar.add(filem = new JMenu("File"));
// filem.add(new JMenuItem("Open file"));
if (FULLSCREEN) {
C64Scr.setBackground(Color.black);
C64Scr.setForeground(lblue = new Color(VICConstants.COLOR_SETS[0][14]));
C64Scr.setLayout(new BorderLayout());
C64Scr.add(scr.getScreen(), BorderLayout.CENTER);
C64Scr.setSize(386 * 2, 284 * 2);
} else {
C64Win.setBackground(Color.black);
C64Win.setForeground(lblue = new Color(VICConstants.COLOR_SETS[0][14]));
C64Win.setLayout(new BorderLayout());
C64Win.add(scr.getScreen(), BorderLayout.CENTER);
C64Win.setFocusable(true);
}
if (!FULLSCREEN) {
C64Win.pack(); // C64Scr.setSize(380,300);
C64Win.setSize(386 * 2, 284 * 2);
C64Win.setResizable(true);
C64Win.setVisible(true);
} else {
C64Scr.setVisible(true);
}
for (int i = 0; i < 24 * 21; i++)
sprMem[i] = 0xfff04040 + i;
mis = new MemoryImageSource(24, 21, sprMem, 0, 24);
mis.setAnimated(true);
mis.setFullBufferUpdates(true);
if (FULLSCREEN) {
sprite = C64Scr.createImage(mis);
} else {
sprite = C64Win.createImage(mis);
}
AudioClip trackSound = null;
AudioClip motorSound = null;
URL url = getClass().getResource("sounds/track.wav");
System.out.println("Audio URL:" + url);
if (url != null) trackSound = Applet.newAudioClip(url);
url = getClass().getResource("sounds/motor.wav");
if (url != null) motorSound = Applet.newAudioClip(url);
else {
System.out.println("Could not load file... motor.wav");
}
scr.setSounds(trackSound, motorSound);
if (motorSound != null) {
motorSound.play();
}
}
private JLabel[] sprites = new JLabel[8];
public void init() {
window = new JFrame();
JButton butt;
window.setSize(320, 650);
window.setLocation(new Point(400, 100));
window.getContentPane().setLayout(new BorderLayout(5,5));
JPanel pan = new JPanel();
pan.setLayout(new GridLayout(11,2,1,1));
pan.add(load = new JButton("Load Program"));
load.addActionListener(this);
pan.add(save = new JButton("Save Program"));
save.addActionListener(this);
pan.add(full = new JButton("Full Screen"));
full.addActionListener(this);
pan.add(normal = new JButton("Normal Screen"));
normal.addActionListener(this);
pan.add(butt = new JButton("Reset"));
butt.addActionListener(this);
pan.add(butt = new JButton("Select Disk"));
butt.addActionListener(this);
pan.add(butt = new JButton("Dump Memory"));
butt.addActionListener(this);
pan.add(txt = new JTextField("0000"));
JPanel span = new JPanel(new BorderLayout(5,5));
span.add(stext = new JLabel("Sprite: 0"), BorderLayout.NORTH);
span.add(sprPan = new JPanel() {
public void paint(Graphics g) {
g.drawImage(sprite, 0, 0, null);
}
}, BorderLayout.CENTER);
sprPan.setOpaque(true);
span.add(butt = new JButton("UP"), BorderLayout.EAST);
butt.addActionListener(this);
sprPan.setPreferredSize(new Dimension(21, 24));
pan.add(span);
pan.add(debug = new JToggleButton("debug:false"));
debug.addActionListener(this);
pan.add(joy = new JToggleButton("joy:0"));
joy.addActionListener(this);
pan.add(vicdump = new JButton("Vic/SID Dump"));
vicdump.addActionListener(this);
pan.add(color = new JButton("ColorSet"));
color.addActionListener(this);
pan.add(sound = new JToggleButton("sound: on"));
sound.addActionListener(this);
pan.add(speed = new JToggleButton("FPS: 50 Hz"));
speed.addActionListener(this);
pan.add(prefix = new JTextField(""));
prefix.addActionListener(this);
pan.add(file = new JTextField(""));
pan.add(openAssembler = new JButton("Jasm64"));
openAssembler.addActionListener(this);
pan.add(pause = new JButton("Pause"));
pause.addActionListener(this);
pan.add(saveDisk = new JButton("Save Disk"));
saveDisk.addActionListener(this);
pan.add(fileName = new JTextField(""));
list = new JList();
list.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
load.doClick();
}
}
});
JScrollPane scroll = new JScrollPane(list);
scroll.setSize(200, 200);
window.getContentPane().add(scroll, BorderLayout.CENTER);
window.getContentPane().add(pan, BorderLayout.SOUTH);
infoWindow = new JPanel();
infoWindow.setLayout(new GridLayout(0, 1));
for (int i = 0; i < 8; i++) {
infoWindow.add(sprites[i] = new JLabel("0000"));
}
window.getContentPane().add(infoWindow, BorderLayout.NORTH);
// window.getContentPane().add(scr.getScreen(), BorderLayout.WEST);
window.pack();
window.setResizable(true);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
window.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
new Thread(this).start();
new Thread(new Runnable() {
public void run() {
while (true) {
try {
Thread.sleep(100);
} catch (Exception e) {
System.out.println("Error while sleeping!");
}
if (!fullscreen) {
window.setTitle("Scan rate: " + scr.getActualScanRate() +
" $1 = " + Integer.toString(memory[1], 16));
int spr0 = 0x03f8 + scr.videoMatrix;
int spr = 0;
int spr0X = C64Screen.IO_OFFSET + 0xd000;
int coll = memory[C64Screen.IO_OFFSET + 0xd01f];
int coll2 = memory[C64Screen.IO_OFFSET + 0xd01e];
int expy = memory[C64Screen.IO_OFFSET + 0xd017];
int expx = memory[C64Screen.IO_OFFSET + 0xd01d];
int yscroll = memory[C64Screen.IO_OFFSET + 0xd011] & 7;
int xscroll = memory[C64Screen.IO_OFFSET + 0xd016] & 7;
String xtra = "";
for (int i = 0; i < 8; i++)
{
xtra = "";
if (i == 0) xtra = " YScr:" + yscroll;
if (i == 1) xtra = " XScr:" + xscroll;
int spos =
scr.vicBank + (64 * (spr = memory[spr0 + i]));
int xpos = memory[spr0X + i * 2] +
((memory[spr0X + 0x10] & (1 << i)) != 0 ? 256 : 0);
int ypos = memory[spr0X + 1 + i * 2];
boolean c1 = (coll & (1 << i)) != 0;
boolean c2 = (coll2 & (1 << i)) != 0;
String x1 = (expx & (1 << i)) != 0 ? "X" : "x";
String y1 = (expy & (1 << i)) != 0 ? "Y" : "y";
sprites[i].setText("Spr " + i + (c1 ? " #": " _") +
(c2 ? "#": "_") + x1 + y1 +
" " + istr(xpos, 3) + "," +
istr(ypos, 3) + " " +
Integer.toString(spr)
+ " "
+ Integer.toString(spos, 16) + xtra);
}
// infoWindow.repaint();//set text does this
}
}
}
}).start();
}
// Slow apepnder of strings...
private String istr(int v, int t) {
String s = "" + v;
while (s.length() < t) {
s = "0" + s;
}
return s;
}
private void saveDisk(String filename) {
try {
reader.writeDisk(new FileOutputStream(filename));
} catch (Exception e) {
e.printStackTrace();
}
}
private void readDisk() {
if (fileDialog == null)
fileDialog = new FileDialog(window, "Select File to Load");
fileDialog.show();
String name = fileDialog.getDirectory() + fileDialog.getFile();
readDisk(name);
dirNames = reader.getDirNames();
list.setListData(dirNames.toArray());
if (dirNames.size() > 0) list.setSelectedIndex(0);
}
private void 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();
while(!scr.ready()) {
try {
Thread.sleep(100);
}catch (Exception e2) {
System.out.println("Exception while sleeping... C64Test");
}
}
reader.readPGM(name, -1);
cpu.runBasic();
}
}
public void run() {
cpu.start();
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == vicdump) {
scr.dumpGfxStat();
// TODO: Fix printStatus in all ExtChips... or in C64Screen!
// scr.getSIDs()[0].printStatus();
// scr.getSIDs()[1].printStatus();
// scr.getSIDs()[2].printStatus();
scr.getCIAs()[0].printStatus();
scr.getCIAs()[1].printStatus();
} else if (e.getSource() == joy) {
scr.setStick(!joy.isSelected());
joy.setText("joy:"+(joy.isSelected() ? "1":"0"));
} else if (e.getSource() == prefix) {
monitor.setPrefix(prefix.getText());
} else if (e.getSource() == openAssembler) {
openAssembler();
} else if (e.getSource() == save) {
System.out.println(reader.saveFile());
} else if (e.getSource() == color) {
scr.setColorSet(cset++ % VICConstants.COLOR_SETS.length);
} else if (e.getSource() == sound) {
scr.setSoundOn(sound.isSelected());
sound.setText("Sound: "+(sound.isSelected() ? "On":"Off"));
} else if (e.getSource() == pause) {
cpu.setPause(!cpu.pause);
} else if (e.getSource() == speed) {
if (speed.isSelected()) {
scr.setScanRate(0.1);
} else {
scr.setScanRate(50);
}
speed.setText("FPS: " + (speed.isSelected() ? "50/s":"1/s"));
} else if (e.getSource() == debug) {
monitor.setEnabled(debug.isSelected());
debug.setText("debug:"+(debug.isSelected() ? "true":"false"));
} else if (e.getActionCommand().startsWith("Res")) {
System.out.println("Reset!");
cpu.reset();
} else if (e.getSource() == full) {
setFull(true);
} else if (e.getSource() == normal) {
setFull(false);
} else if (e.getSource() == saveDisk) {
saveDisk(fileName.getText());
} else if (e.getActionCommand().startsWith("Loa")) {
Object o = list.getSelectedValue();
String str = "";
if (o instanceof DirEntry) {
DirEntry dire = (DirEntry) o;
str = dire.name;
} else {
str = (String) o;
}
if (str != null)
{
System.out.println("Should load: \"" + str + "\"");
cpu.reset(); // TODO: fix so that reset waits for reset of chips, etc.
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...");
}
}
// Does not always work....
System.out.println("Loading: \"" + str + "\"");
reader.readFile(str);
// monitor.setEnabled(true);
cpu.runBasic();
}
} else if (e.getActionCommand().startsWith("Sel")) {
readDisk();
} else if (e.getActionCommand().startsWith("UP")) {
sprnum = (sprnum + 1) & 0xff;
stext.setText("Sprite: " + sprnum);
int mpos = scr.vicBank + (64 * sprnum);
System.out.println("VicBank:" + Integer.toString(scr.vicBank, 16));
System.out.println("VideoMatrix:" +
Integer.toString(scr.videoMatrix, 16));
System.out.println("Raster Pos:" + scr.vbeam + " irq: " + scr.raster);
System.out.println("CharSet:" + Integer.toString(scr.charSet, 16));
System.out.println(
"Sprite Adr:" + Integer.toString(mpos, 16) + " = " + mpos);
int spr0 = 0x03f8 + scr.videoMatrix;
int spos = scr.vicBank + (64 * memory[spr0]);
System.out.println("Sprite 0:" + Integer.toString(spos, 16));
spos = scr.vicBank + (64 * memory[spr0 + 1]);
System.out.println("Sprite 1:" + Integer.toString(spos, 16));
spos = scr.vicBank + (64 * memory[spr0 + 2]);
System.out.println("Sprite 2:" + Integer.toString(spos, 16));
spos = scr.vicBank + (64 * memory[spr0 + 3]);
System.out.println("Sprite 3:" + Integer.toString(spos, 16));
spos = scr.vicBank + (64 * memory[spr0 + 4]);
System.out.println("Sprite 4:" + Integer.toString(spos, 16));
spos = scr.vicBank + (64 * memory[spr0 + 5]);
System.out.println("Sprite 5:" + Integer.toString(spos, 16));
int adr = 0;
for (int i = 0; i < 21; i++)
for (int x = 0; x < 3; x++)
{
int data = memory[mpos++];
for (int pos = 0; pos < 8; pos++)
sprMem[adr++] =
(data & (1 << pos)) != 0 ? 0xffffffff : 0xff000000;
}
mis.newPixels();
System.out.println("BasicON: " + cpu.basicROM);
System.out.println("KernalON: " + cpu.kernalROM);
System.out.println("charON: " + cpu.charROM);
System.out.println("ioON: " + cpu.ioON);
sprPan.repaint();
} else {
String strt = txt.getText();
int i = Integer.parseInt(strt, 16);
if (i < 0x15df00)
dumpMemory(i, 256);
}
}
private void openAssembler() {
if (asmFrame == null) {
assembler = new Assembler();
assembler.setMemory(memory);
asmFrame = new JFrame("Jasm64");
asmFrame.setBounds(100,100,400,400);
JPanel panel = new JPanel(new BorderLayout());
panel.add(new JScrollPane(asmText = new JTextArea(40, 40)),
BorderLayout.CENTER);
panel.add(asmAdr = new JTextField("$c000"), BorderLayout.NORTH);
panel.add(asm = new JButton("Assemble"), BorderLayout.SOUTH);
asm.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
assembler.assemble(asmText.getText(),
Assembler.parseInt(asmAdr.getText()));
}
});
asmFrame.getContentPane().add(panel);
}
asmFrame.setVisible(true);
}
private void dumpMemory(int address, int size) {
String tmp = "";
String tmp2 = "";
String t;
System.out.println("Dumping memory: " + address);
for (int i = 0; i < size; i++) {
t = Integer.toString(memory[i + address] & 0xff, 16);
if (t.length() < 2)
t = "0" + t;
tmp = tmp + t + " ";
if (i % 4 == 3) tmp += " ";
if (memory[i + address] > 32 && memory[i + address] < 99)
tmp2 += (char) memory[i + address];
else
tmp2 += ".";
if (i % 16 == 15) {
String adr = Integer.toString(address + i - 15, 16);
while (adr.length() < 4)
adr = "0" + adr;
System.out.println(adr + " " + tmp + " " + tmp2);
tmp = "";
tmp2 = "";
}
}
}
public void toggleFullScreen() {
System.out.println("Toggle fullscreen called!");
setFull(!fullscreen);
}
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;
}
public static void main(String[] name) {
String file = "/games/c64-demodiskette.c64";
if (name.length > 0)
file = name[0];
System.out.println("Loading " + file);
C64Test test = new C64Test(file);
test.init();
}
}