From 2e59a5c31b0259cf3d7f28e653d857961c884175 Mon Sep 17 00:00:00 2001 From: sicklittlemonkey Date: Sat, 1 Aug 2015 19:09:49 +1200 Subject: [PATCH] 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 --- Source/AppleIIGo.java | 114 +++++++++++++++++++++++++++++++-------- Source/AppleSpeaker.java | 30 +++++------ Source/DiskII.java | 4 +- Source/EmAppleII.java | 18 ++++--- build.xml | 7 ++- 5 files changed, 122 insertions(+), 51 deletions(-) diff --git a/Source/AppleIIGo.java b/Source/AppleIIGo.java index 3ef8e9b..8e58227 100644 --- a/Source/AppleIIGo.java +++ b/Source/AppleIIGo.java @@ -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

@@ -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()); diff --git a/Source/AppleSpeaker.java b/Source/AppleSpeaker.java index 4edcc38..84b1eaa 100644 --- a/Source/AppleSpeaker.java +++ b/Source/AppleSpeaker.java @@ -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; } } } diff --git a/Source/DiskII.java b/Source/DiskII.java index 1cc0dbb..d9037da 100644 --- a/Source/DiskII.java +++ b/Source/DiskII.java @@ -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); } } diff --git a/Source/EmAppleII.java b/Source/EmAppleII.java index 1d82c33..1fb7186 100644 --- a/Source/EmAppleII.java +++ b/Source/EmAppleII.java @@ -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; diff --git a/build.xml b/build.xml index 523613c..820cec8 100644 --- a/build.xml +++ b/build.xml @@ -3,17 +3,16 @@ - + - + - + -