/**
* @(#)C64Applet.java Created date: 99-8-20
* Last update - 2005-01-01
*
*/
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.net.URL;
import javax.swing.*;
import java.io.*;
import java.util.Vector;
import java.util.StringTokenizer;
import com.dreamfabric.jac64.*;
import com.dreamfabric.c64utils.*;
import java.net.HttpURLConnection;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.applet.AudioClip;
/**
*
*
* @author Joakim Eriksson (joakime@sics.se)
* @version $Revision: 1.14 $, $Date: 2006/05/01 14:57:57 $
*/
public class C64Applet extends Applet implements Runnable, PatchListener {
private CPU cpu;
private int[] memory;
private boolean started = false;
private boolean stopping = false;
private C64Reader reader;
private String currentDisk;
private String loadFile;
private boolean stick = true; // Emulate joystick 1
private C64Screen screen;
private C64Canvas canvas;
private Vector files;
private boolean require1541 = false;
private IMonitor imon = new DefaultIMon();
private static Color lblue = new Color(VICConstants.COLOR_SETS[0][14]);
private Thread thread;
private String autostartDisk;
private String autostartProgram;
private String autoText;
private int autostartID = -1;
private int defaultStick = 0;
private int soundOn = 0;
private int doubleScreen = 0;
private boolean fullscreen = false;
private JFrame fullFrame = null;
private JWindow fullWin = null;
// -------------------------------------------------------------------
// Applet methods
// -------------------------------------------------------------------
public void init() {
System.out.println("###### Applet init() #######");
started = false;
stopping = false;
currentDisk = null;
if (cpu == null) {
SIDMixer.DL_BUFFER_SIZE = 16384;
System.out.println("starting CPU");
cpu = new CPU(imon, getCodeBase().toString(), new SELoader());
System.out.println("Status: initializing");
doubleScreen = getParameterAsInt("doubleScreen", 0);
int freescale = getParameterAsInt("freescale", 0);
screen = new C64Screen(imon, doubleScreen > 0);
cpu.init(screen);
screen.init(cpu);
if (freescale != 0) {
screen.setIntegerScaling(false);
}
// Not when emulating 1541!!
// cpu.patchROM(this);
memory = cpu.getMemory();
setLayout(new BorderLayout());
setBackground(Color.black);
setForeground(lblue);
// Get the diskdrive reader!!!
reader = new C64Reader();
reader.setCPU(cpu);
canvas = (C64Canvas) screen.getScreen();
fullscreen(fullscreen);
screen.registerHotKey(KeyEvent.VK_BACK_SPACE, KeyEvent.CTRL_DOWN_MASK |
KeyEvent.ALT_DOWN_MASK
, "reset()", cpu);
screen.registerHotKey(KeyEvent.VK_F12, KeyEvent.CTRL_DOWN_MASK
, "toggleFullscreen()", this);
repaint();
validate();
addKeyListener(canvas);
canvas.requestFocus();
// A test... for real 1541 emulation...
cpu.getDrive().setReader(reader);
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);
screen.setSounds(trackSound, motorSound);
setColorSet(getParameterAsInt("colorset", 0));
int rq1541 = getParameterAsInt("require1541", 0);
require1541 = (rq1541 == 1);
for (int i = 0, n = 12; i < n; i++) {
String f1 = getParameter("hotkey-f" + (i + 1));
if (f1 != null && f1.length() > 0) {
// 0 - 11 => ALT-F1-12
screen.registerHotKey(KeyEvent.VK_F1 + i,
KeyEvent.ALT_DOWN_MASK, f1, this);
}
}
System.out.println("*** INIT END ***");
}
}
public void toggleFullscreen() {
fullscreen(!fullscreen);
}
public void fullscreen(boolean full) {
fullscreen = full;
if (full) {
// Add opening a parent frame first... otherwise no keyinput
// will work...
remove(canvas);
if (fullFrame == null) {
fullFrame = new JFrame("-");
fullWin = new JWindow(fullFrame);
fullWin.addKeyListener(canvas);
}
screen.setAutoscale(true);
fullWin.add(canvas);
fullWin.setSize(100, 100);
System.out.println("Setting visible to true!!!");
fullWin.setVisible(true);
fullFrame.setVisible(true);
java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(fullWin);
fullWin.setFocusable(true);
} else {
if (fullFrame != null) {
fullFrame.setVisible(false);
fullWin.setVisible(false);
}
java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(null);
add(canvas, BorderLayout.CENTER);
validate();
}
}
private void autoload() {
Thread t = new Thread(new Runnable() {
public void run() {
// Autostart from disk/prg
autoText = getParameter("autostartCode");
autostartDisk = getParameter("autostartDisk");
if (autostartDisk != null) {
autostartProgram = getParameter("autostartPGM");
if (autostartProgram == null) {
autostartProgram = getParameter("autostartProgram");
}
} else {
autostartProgram = getParameter("autostartPGM");
autostartID = getParameterAsInt("autostartProgram", -1);
}
defaultStick = getParameterAsInt("joystick", 0);
soundOn = getParameterAsInt("soundOn", 1);
if (getParameterAsInt("extendedKeyboard", 0) != 0) {
screen.setKeyboardEmulation(true);
System.out.println("Extended keyboard emulation on!");
}
loadGamesList();
screen.setSoundOn(soundOn == 1);
screen.setStick(defaultStick == 0);
if (autostartDisk != null) {
if (autostartProgram != null) {
loadGame(autostartDisk, autostartProgram);
} else {
insertDisk(autostartDisk);
waitForKernal();
enterText(autoText);
}
} else if (autostartProgram != null) {
System.out.println("Autostart program:" + autostartProgram);
if (files != null && autostartProgram.equals("random")) {
int randomId = (int) (Math.random() * (files.size() / 2));
loadGame(randomId);
} else {
loadPGM(autostartProgram);
}
}
if (autostartID != -1) {
System.out.println("AutostartID: " + autostartID);
loadGame(autostartID);
}
started = true;
}
});
t.start();
}
public void start() {
System.out.println("###### Applet start() #######");
// Start the thread as late as possible so that other init is done
// before!
if (thread == null) {
thread = new Thread(this);
thread.start();
try {
Thread.sleep(1000);
} catch (Exception e) {
}
autoload();
} else {
unpause();
started = true;
}
}
public void stop() {
System.out.println("###### Applet stop() #######");
shutdown();
}
public void destroy() {
System.out.println("###### Applet destroy() #######");
if (screen != null && screen.getAudioDriver() != null)
screen.getAudioDriver().shutdown();
shutdown();
}
private void shutdown() {
stopping = true;
if (cpu != null)
cpu.stop();
if (screen != null) {
screen.deleteInterruptManagers();
screen.motorSound(false);
}
cpu = null;
screen = null;
removeKeyListener(canvas);
canvas = null;
}
public boolean isStarted() {
return started;
}
// -------------------------------------------------------------------
// Run loop!
// -------------------------------------------------------------------
public void run() {
if (started && !stopping) {
System.out.println( "Status: running");
cpu.start();
} else {
cpu.start();
}
stopping = false;
thread = null;
}
// -------------------------------------------------------------------
// Utils
// -------------------------------------------------------------------
private int getParameterAsInt(String paramName, int defVal) {
String val = getParameter(paramName);
System.out.println(paramName + " = " + val);
if (val != null) {
try {
return Integer.parseInt(val);
} catch (Exception e) {
System.out.println("Can not parse value: " + val);
}
}
return defVal;
}
private void loadGamesList() {
System.out.println("Trying to load games list");
try {
URL url = getResource("games.txt");
LineNumberReader reader =
new LineNumberReader(new InputStreamReader(url.openConnection().getInputStream()));
// game loop...
String games;
String disk;
files = new Vector();
while ((disk = reader.readLine()) != null) {
disk = disk.trim();
if (disk.toLowerCase().endsWith(".prg") ||
disk.toLowerCase().endsWith(".p00")) {
// System.out.println("Adding PGM file: " + disk);
files.addElement(disk);
files.addElement(disk);
} else {
// System.out.println("reading games from disk: " + disk);
games = reader.readLine();
if (games != null) {
games = games.trim();
StringTokenizer stok = new StringTokenizer(games, ",");
while (stok.hasMoreElements()) {
String game = stok.nextToken();
files.addElement(disk);
files.addElement(game);
System.out.println("Adding: " + game);
}
}
}
}
} catch (Exception e) {
System.out.println("Can not load games..." + e);
e.printStackTrace();
System.out.println( "No games to load...");
}
}
// -------------------------------------------------------------------
// For scripting!
// -------------------------------------------------------------------
public void setColorSet(int i) {
i = i % VICConstants.COLOR_SETS.length;
screen.setColorSet(i);
}
public void poke(int address, int data) {
if (address < 0xd000 || address >= 0xe000) {
int[] memory = cpu.getMemory();
memory[address & 0xffff] = data & 0xff;
} else {
cpu.poke(address & 0xffff, data & 0xff);
}
}
public int peek(int address) {
int[] memory = cpu.getMemory();
return memory[address & 0xffff];
}
public void pause() {
cpu.setPause(true);
}
public void unpause() {
cpu.setPause(false);
}
private void loadProgram(int item) {
String disk = (String) files.elementAt(item * 2);
String name = (String) files.elementAt(item * 2 + 1);
System.out.println("Index:" + item + " -> " + disk + " " + name);
loadProgram(disk, name);
}
private boolean loadProgram(String disk, String name) {
boolean em1541 = require1541;
if (disk.startsWith("@")) {
disk = disk.substring(1);
em1541 = true;
}
if (disk != currentDisk) {
URL url = getResource(disk);
currentDisk = disk;
disk = disk.toLowerCase();
if (disk.endsWith(".d64")) {
if (!reader.readDiskFromURL(url))
System.out.println("Status: problem while loading disk");
} else if (disk.endsWith(".t64")) {
if (!reader.readTapeFromURL(url))
System.out.println("Status: problem while loading tape");
} else if (disk.endsWith(".prg") || disk.endsWith(".p00")) {
if (!reader.readPGM(url, -1))
System.out.println("Status: problem while loading pgm");
else System.out.println("Status: loaded " + disk);
// Already read into memory!!!
return false;
}
}
if (em1541) {
System.out.println("Loading with C1541 emulation...");
enterText("load \"" + name + "\",8~");
enterText("run~");
return true;
} else {
for (int i = name.length(); i<16; i++)
name = name + " ";
if (reader.readFile(name) != null)
System.out.println("Status: loaded " + name);
else
System.out.println("Status: error while loading " + name);
return false;
}
}
public void loadPGM(String pgm) {
waitForKernal();
URL url = getResource(pgm);
if (!reader.readPGM(url, -1))
System.out.println("Status: problem while loading pgm");
cpu.runBasic();
canvas.requestFocus();
}
public void insertDisk(String urlstr) {
if (urlstr.startsWith("@")) {
urlstr = urlstr.substring(1);
}
URL url = getResource(urlstr);
if (!reader.readDiskFromURL(url)) {
System.out.println("Status: problem while inserting disk: " + url);
}
}
public void enterText(String txt) {
cpu.enterText(txt);
}
public void loadPGM(String disk, String game) {
loadGame(disk, game);
}
public void loadGame(String disk, String game) {
waitForKernal();
System.out.println("Loading " + game + " from " + disk);
// load program returns true if autostarting!
if (!loadProgram(disk, game))
cpu.runBasic();
canvas.requestFocus();
}
private void waitForKernal() {
while(!screen.ready()) {
try {
Thread.sleep(100);
} catch (Exception e2) {
System.out.println("Exception while sleeping... C64Applet");
}
}
}
public void loadGame(int item) {
waitForKernal();
// Does not always work....
loadProgram(item);
cpu.runBasic();
canvas.requestFocus();
}
// 0 -> stick 1 other -> stick 2
public void setStick(int stick) {
System.out.println("Setting stick: one ? " + (stick == 0));
screen.setStick(stick == 0);
canvas.requestFocus();
}
public void setSoundOn(boolean on) {
screen.setSoundOn(on);
}
public void setScanRate(int rate) {
screen.setScanRate(rate);
}
public void reset() {
System.out.println("Reset - no kill");
cpu.reset();
screen.reset();
canvas.requestFocus();
}
private URL getResource(String urls) {
URL url = this.getClass().getResource(urls);
if (url == null) try {
url = new URL(getCodeBase().toString() + urls);
} catch (Exception e) {}
return url;
}
public void setEffect(int id) {
// screen.getMixer().setEFX(id);
}
// Where should this be stored???
public void saveFile(String name, String author, String description) {
String data = "";
if (description == null)
description = "";
try {
data = "name=" + URLEncoder.encode(name, "UTF-8") +
"&description=" + URLEncoder.encode(description, "UTF-8") +
"&author=" + URLEncoder.encode(author, "UTF-8") +
"&file=" + reader.saveFile();
} catch (UnsupportedEncodingException uee) {
uee.printStackTrace();
return;
}
System.out.println("Saving file: " + data);
// Make a post!
try {
URL url = getResource("prgup.php");
URLConnection urlc = url.openConnection();
urlc.setDoOutput(true);
urlc.setUseCaches(false);
HttpURLConnection httpConnection = (HttpURLConnection) urlc;
httpConnection.setRequestMethod("POST");
httpConnection
.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
DataOutputStream out =
new DataOutputStream(httpConnection.getOutputStream());
out.writeBytes(data);
out.flush();
out.close();
InputStream is = httpConnection.getInputStream();
System.out.println("Read back:");
int c;
while ((c = is.read()) != -1) {
System.out.print((char) c);
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
// Patch listener
public boolean readFile(String str, int adr) {
str = str.trim();
System.out.println("Should load: \"" + str + "\"");
if ("$".equals(str)) {
// Should enter basic program for dir listing...
System.out.println("Entering basic data");
ArrayList vc = reader.getDirNames();
int pos = 2048;
int nextPos;
memory[pos++] = 0;
for (int i = 0, n = vc.size(); i < n; i++)
{
DirEntry ent = reader.getDirEntry((String) vc.get(i));
String name = ent.name;
nextPos = pos + 5 + name.length();
// Next position
System.out.println("Name: " + name + " " + name.length());
System.out.println("Next: " + nextPos);
System.out.println("Pos: " + pos);
memory[pos++] = nextPos & 0xff;
memory[pos++] = nextPos >> 8;
// Row number
memory[pos++] = ent.size & 0xff;
memory[pos++] = ent.size >> 8;
for (int j = 0; j < name.length(); j++)
memory[pos++] = name.charAt(j);
memory[pos++] = 0;
}
return true;
} else {
for (int i = str.length(); i < 16; i++)
str = str + " ";
return reader.readFile(str, adr) != null;
}
}
// 1 -> resid 6580, 2 -> resid 8580, 3 -> jac64
public void setSIDEmulation(int type) {
screen.setSID(type);
}
// -------------------------------------------------------------------
// For "cheating"
// Should have more than one AutoStore!
// -------------------------------------------------------------------
public void enableAutoStore(int max) {
cpu.setCheatEnabled(max);
}
public void setAutoStore(int index, String prefix) {
AutoStore as = new AutoStore(prefix);
cpu.setAutoStore(index, as);
}
public void protect(int address, int value) {
cpu.protect(address, value);
}
public void monitorRead(int address) {
cpu.monitorRead(address);
}
public void monitorWrite(int address) {
cpu.monitorWrite(address);
}
public void addAutoStoreRule(int index, String rule) {
cpu.getAutoStore(index).addRule(rule);
}
public void addAutoStoreStore(int index, int adr, int len, String name) {
cpu.getAutoStore(index).addStore(adr, len, name);
}
}