mirror of
https://github.com/sicklittlemonkey/AppleIIGo.git
synced 2024-12-14 00:30:06 +00:00
Version 1.0.2 - changes by Nick
- improved sound sync by moving AppleSpeaker into the main thread - added version (F1) - added multiple disks & switching (F3, F4) - added ZIP archive support - fixed HTTP disk image access bug
This commit is contained in:
parent
7f130be8ec
commit
2e59a5c31b
@ -2,17 +2,38 @@
|
||||
/**
|
||||
* AppleIIGo
|
||||
* The Java Apple II Emulator
|
||||
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
|
||||
* (C) 2006 by Marc S. Ressl (ressl@lonetree.com)
|
||||
* (C) 2008 by Nick Westgate (Nick.Westgate@gmail.com)
|
||||
* Released under the GPL
|
||||
*
|
||||
* Change list:
|
||||
*
|
||||
* Version 1.0.2 - changes by Nick:
|
||||
* - improved sound sync by moving AppleSpeaker into the main thread
|
||||
* - added version (F1)
|
||||
* - added multiple disks & switching (F3, F4)
|
||||
* - added ZIP archive support
|
||||
* - fixed HTTP disk image access bug
|
||||
*/
|
||||
|
||||
import java.applet.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.zip.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.Graphics2D;
|
||||
import java.applet.Applet;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.ComponentListener;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.event.MouseMotionListener;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
/**
|
||||
* AppleIIGo class<p>
|
||||
@ -20,10 +41,13 @@ import java.awt.Graphics2D;
|
||||
*/
|
||||
public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||
MouseListener, MouseMotionListener {
|
||||
|
||||
final String version = "1.0.2";
|
||||
final String versionString = "AppleIIGo Version " + version;
|
||||
|
||||
// Class instances
|
||||
private EmAppleII apple;
|
||||
private AppleDisplay display;
|
||||
private AppleSpeaker speaker;
|
||||
private DiskII disk;
|
||||
|
||||
// Machine variables
|
||||
@ -39,6 +63,10 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||
// Disk variables
|
||||
private String diskDriveResource[] = new String[2];
|
||||
private boolean diskWritable;
|
||||
private String[] disks0;
|
||||
private String[] disks1;
|
||||
private int disk0;
|
||||
private int disk1;
|
||||
|
||||
/**
|
||||
* Debug
|
||||
@ -91,8 +119,8 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||
display.setGlare(getAppletParameter("displayGlare", "false").equals("true"));
|
||||
|
||||
// Speaker
|
||||
speaker = new AppleSpeaker(apple);
|
||||
speaker.setVolume(new Integer(getAppletParameter("speakerVolume", "3")).intValue());
|
||||
apple.speaker = new AppleSpeaker(apple);
|
||||
apple.speaker.setVolume(new Integer(getAppletParameter("speakerVolume", "6")).intValue());
|
||||
|
||||
// Peripherals
|
||||
disk = new DiskII();
|
||||
@ -100,8 +128,12 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||
|
||||
// Initialize disk drives
|
||||
diskWritable = getAppletParameter("diskWritable", "false").equals("true");
|
||||
mountDisk(0, getAppletParameter("diskDrive1", ""));
|
||||
mountDisk(1, getAppletParameter("diskDrive2", ""));
|
||||
disks0 = getAppletParameter("diskDrive1", "").split("[|]");
|
||||
disk0 = 0;
|
||||
disks1 = getAppletParameter("diskDrive2", "").split("[|]");
|
||||
disk1 = 0;
|
||||
mountDisk(0, disks0[disk0]);
|
||||
mountDisk(1, disks1[disk1]);
|
||||
|
||||
// Start CPU
|
||||
if (!isCpuPaused)
|
||||
@ -112,6 +144,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||
* Start applet
|
||||
*/
|
||||
public void start() {
|
||||
debug("AppleIIGo Version " + version);
|
||||
debug("start()");
|
||||
}
|
||||
|
||||
@ -151,7 +184,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||
isCpuPaused = true;
|
||||
apple.setPaused(isCpuPaused);
|
||||
display.setPaused(isCpuPaused);
|
||||
// speaker.setPaused(isCpuPaused);
|
||||
apple.speaker.setPaused(isCpuPaused);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -160,7 +193,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||
public void resume() {
|
||||
debug("resume()");
|
||||
isCpuPaused = false;
|
||||
// speaker.setPaused(isCpuPaused);
|
||||
apple.speaker.setPaused(isCpuPaused);
|
||||
display.setPaused(isCpuPaused);
|
||||
apple.setPaused(isCpuPaused);
|
||||
}
|
||||
@ -176,19 +209,37 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||
/**
|
||||
* Open input stream
|
||||
*/
|
||||
private InputStream openInputStream(String resource) {
|
||||
private DataInputStream openInputStream(String resource) {
|
||||
InputStream is = null;
|
||||
|
||||
try {
|
||||
URL url = new URL(getCodeBase(), resource);
|
||||
is = url.openStream();
|
||||
debug("resource: " + url.toString());
|
||||
|
||||
is = url.openStream();
|
||||
if (resource.toLowerCase().endsWith(".gz"))
|
||||
{
|
||||
is = new GZIPInputStream(is);
|
||||
}
|
||||
else if (resource.toLowerCase().endsWith(".zip"))
|
||||
{
|
||||
is = new ZipInputStream(is);
|
||||
((ZipInputStream)is).getNextEntry();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
debug("Exeption: " + e.getLocalizedMessage());
|
||||
}
|
||||
|
||||
return is;
|
||||
if (is == null)
|
||||
{
|
||||
debug("failed");
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("ok");
|
||||
return new DataInputStream(is);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,10 +265,11 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||
boolean success = false;
|
||||
|
||||
try {
|
||||
InputStream is = openInputStream(resource);
|
||||
DataInputStream is = openInputStream(resource);
|
||||
success = apple.loadRom(is);
|
||||
is.close();
|
||||
} catch (Exception e) {
|
||||
debug("Exeption: " + e.getLocalizedMessage());
|
||||
}
|
||||
|
||||
return success;
|
||||
@ -238,10 +290,11 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||
|
||||
diskDriveResource[drive] = resource;
|
||||
|
||||
InputStream is = openInputStream(resource);
|
||||
DataInputStream is = openInputStream(resource);
|
||||
success = disk.readDisk(drive, is, 254, false);
|
||||
is.close();
|
||||
} catch (Exception e) {
|
||||
debug("Exeption: " + e.getLocalizedMessage());
|
||||
}
|
||||
|
||||
return success;
|
||||
@ -310,13 +363,13 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||
break;
|
||||
case KeyEvent.VK_UP:
|
||||
if (e.isControlDown())
|
||||
speaker.setVolume(speaker.getVolume() + 1);
|
||||
apple.speaker.setVolume(apple.speaker.getVolume() + 1);
|
||||
else
|
||||
apple.setKeyLatch(11);
|
||||
break;
|
||||
case KeyEvent.VK_DOWN:
|
||||
if (e.isControlDown())
|
||||
speaker.setVolume(speaker.getVolume() - 1);
|
||||
apple.speaker.setVolume(apple.speaker.getVolume() - 1);
|
||||
else
|
||||
apple.setKeyLatch(10);
|
||||
break;
|
||||
@ -332,6 +385,23 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||
else
|
||||
apple.reset();
|
||||
break;
|
||||
case KeyEvent.VK_F1:
|
||||
showStatus("AppleIIGo Version " + version);
|
||||
break;
|
||||
case KeyEvent.VK_F3:
|
||||
if (disks0.length > 1) {
|
||||
disk0 = ++disk0 % disks0.length;
|
||||
mountDisk(0, disks0[disk0]);
|
||||
showStatus("Disk 1: " + disks0[disk0]);
|
||||
}
|
||||
break;
|
||||
case KeyEvent.VK_F4:
|
||||
if (disks1.length > 1) {
|
||||
disk1 = ++disk1 % disks1.length;
|
||||
mountDisk(1, disks1[disk1]);
|
||||
showStatus("Disk 2: " + disks1[disk1]);
|
||||
}
|
||||
break;
|
||||
case KeyEvent.VK_F5:
|
||||
if (isCpuDebugEnabled)
|
||||
display.setStatMode(!display.getStatMode());
|
||||
|
@ -10,7 +10,7 @@ import javax.sound.sampled.*;
|
||||
|
||||
public class AppleSpeaker implements Runnable {
|
||||
// Instances of other classes
|
||||
private EmAppleII apple;
|
||||
private EmAppleII apple;
|
||||
|
||||
// Refresh
|
||||
private int refreshRate;
|
||||
@ -29,7 +29,6 @@ public class AppleSpeaker implements Runnable {
|
||||
|
||||
private SourceDataLine line;
|
||||
|
||||
private int bufferSamples;
|
||||
private int bufferSize;
|
||||
private byte[] buffer;
|
||||
|
||||
@ -43,11 +42,8 @@ public class AppleSpeaker implements Runnable {
|
||||
// Thread stuff
|
||||
private boolean isPaused = true;
|
||||
private Thread thread;
|
||||
private String threadError = null;
|
||||
|
||||
|
||||
|
||||
public AppleSpeaker(EmAppleII apple) {
|
||||
public AppleSpeaker(EmAppleII apple) {
|
||||
this.apple = apple;
|
||||
|
||||
setVolume(4);
|
||||
@ -66,14 +62,14 @@ public class AppleSpeaker implements Runnable {
|
||||
refreshInterval = (int) (1000.0 / value);
|
||||
|
||||
speakerClocksPerSample = (int) (apple.getCpuSpeed() * 1000.0f / SPEAKER_SAMPLERATE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get refresh rate
|
||||
*/
|
||||
private int getRefreshRate() {
|
||||
return refreshRate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set speaker volume
|
||||
@ -94,7 +90,7 @@ public class AppleSpeaker implements Runnable {
|
||||
*/
|
||||
public int getVolume() {
|
||||
return speakerVolume;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set pause state
|
||||
@ -130,8 +126,6 @@ public class AppleSpeaker implements Runnable {
|
||||
try {
|
||||
line = (SourceDataLine) AudioSystem.getLine(info);
|
||||
bufferSize = line.getBufferSize();
|
||||
bufferSamples = bufferSize / SPEAKER_SAMPLESIZE;
|
||||
|
||||
buffer = new byte[bufferSize];
|
||||
|
||||
line.open(audioFormat);
|
||||
@ -139,15 +133,17 @@ public class AppleSpeaker implements Runnable {
|
||||
} catch (LineUnavailableException e) {
|
||||
}
|
||||
|
||||
thread = new Thread(this);
|
||||
thread.start();
|
||||
// TODO: this thread is not created any more (nick)
|
||||
//thread = new Thread(this);
|
||||
//thread.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Speaker refresh thread
|
||||
* TODO: this thread is not created any more (nick)
|
||||
*/
|
||||
public void run() {
|
||||
public void run() {
|
||||
try {
|
||||
while (!isPaused) {
|
||||
long refreshStart = System.currentTimeMillis();
|
||||
@ -169,7 +165,7 @@ public class AppleSpeaker implements Runnable {
|
||||
/**
|
||||
* Speaker refresh
|
||||
*/
|
||||
private void refreshSpeaker() {
|
||||
public void refreshSpeaker() {
|
||||
clockEnd = apple.clock;
|
||||
int bytes;
|
||||
|
||||
@ -184,7 +180,7 @@ public class AppleSpeaker implements Runnable {
|
||||
/**
|
||||
* Fill buffer
|
||||
*/
|
||||
private int fillBuffer() {
|
||||
private int fillBuffer() {
|
||||
int value = speakerFlipStateToVolume[speakerFlipState];
|
||||
int clockEndSample = clockEnd - speakerClocksPerSample;
|
||||
int bufferPointer = 0;
|
||||
@ -234,7 +230,7 @@ public class AppleSpeaker implements Runnable {
|
||||
isFlipsBufferEmpty = true;
|
||||
} else {
|
||||
clockNextFlip = apple.speakerFlips[speakerFlipsPointer];
|
||||
speakerFlipsPointer = (speakerFlipsPointer + 1) & apple.SPEAKER_FLIPS_MASK;
|
||||
speakerFlipsPointer = (speakerFlipsPointer + 1) & EmAppleII.SPEAKER_FLIPS_MASK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -265,7 +265,7 @@ public class DiskII extends Peripheral {
|
||||
* @param is InputStream
|
||||
* @param drive Disk II drive
|
||||
*/
|
||||
public boolean readDisk(int drive, InputStream is, int volume, boolean isWriteProtected) {
|
||||
public boolean readDisk(int drive, DataInputStream is, int volume, boolean isWriteProtected) {
|
||||
byte[] track = new byte[RAW_TRACK_BYTES];
|
||||
|
||||
try {
|
||||
@ -273,7 +273,7 @@ public class DiskII extends Peripheral {
|
||||
disk[drive][trackNum] = new byte[RAW_TRACK_BYTES];
|
||||
|
||||
if (is != null) {
|
||||
is.read(track, 0, DOS_TRACK_BYTES);
|
||||
is.readFully(track, 0, DOS_TRACK_BYTES);
|
||||
trackToNibbles(track, disk[drive][trackNum], volume, trackNum);
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ public class EmAppleII extends Em6502 implements Runnable {
|
||||
// Peripherals
|
||||
public Paddle paddle;
|
||||
public Peripheral[] slots;
|
||||
public AppleSpeaker speaker;
|
||||
|
||||
// Graphics (dirty buffer every 0x80 bytes)
|
||||
public int graphicsMode;
|
||||
@ -240,15 +241,11 @@ public class EmAppleII extends Em6502 implements Runnable {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean loadRom(InputStream is) {
|
||||
public boolean loadRom(DataInputStream is) throws IOException {
|
||||
byte[] rom = new byte[0x8000];
|
||||
int offset = 0;
|
||||
|
||||
try {
|
||||
is.read(rom, 0, 0x08000);
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
is.readFully(rom, 0, 0x08000);
|
||||
|
||||
if (isValidRom(rom, 0x0))
|
||||
offset = 0x0;
|
||||
@ -1115,6 +1112,11 @@ public class EmAppleII extends Em6502 implements Runnable {
|
||||
|
||||
/**
|
||||
* Emulator thread
|
||||
*
|
||||
* TODO: The speaker has been merged into this thread.
|
||||
* This keeps it in sync but it still needs some work.
|
||||
* Speeding up the CPU (or adding fast disk access as below)
|
||||
* requires proper refactoring of the AppleSpeaker class.
|
||||
*/
|
||||
public void run() {
|
||||
try {
|
||||
@ -1135,6 +1137,10 @@ public class EmAppleII extends Em6502 implements Runnable {
|
||||
clocksNeeded -= executeInstructions(1 + (clocksNeeded >> 3));
|
||||
}
|
||||
|
||||
// TODO: need something like the following for fast disk access
|
||||
//if (slots[6] instanceof DiskII && !((DiskII)slots[6]).isMotorOn())
|
||||
|
||||
speaker.refreshSpeaker(); // NOTE: this blocks, syncing emulation and sound
|
||||
refreshDelay = System.currentTimeMillis() - refreshStart;
|
||||
|
||||
refreshDelayCumulative += refreshDelay;
|
||||
|
@ -3,17 +3,16 @@
|
||||
<delete dir="Build" />
|
||||
</target>
|
||||
|
||||
<target name="compile">
|
||||
<target name="compile" depends="clean">
|
||||
<mkdir dir="Build/classes"/>
|
||||
<javac srcdir="Source" destdir="Build/classes" optimize="yes"/>
|
||||
<javac srcdir="src" destdir="Build/classes" source="1.4" target="1.4"/>
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="compile">
|
||||
<mkdir dir="Build/jar"/>
|
||||
<copy todir="Build/classes/Resources">
|
||||
<fileset dir="Resources"/>
|
||||
<fileset dir="src/Resources"/>
|
||||
</copy>
|
||||
<jar destfile="Build/jar/AppleIIGo.jar" basedir="Build/classes"/>
|
||||
<copy file="Build/jar/AppleIIGo.jar" todir="/Users/mressl/Library/Widgets/AppleIIGo.wdgt"/>
|
||||
</target>
|
||||
</project>
|
||||
|
Loading…
Reference in New Issue
Block a user