mirror of
https://github.com/sicklittlemonkey/AppleIIGo.git
synced 2024-12-21 16:29:20 +00:00
Marc's last version 1.0.1
This commit is contained in:
parent
11913291dd
commit
7f130be8ec
BIN
Resources/Character Set.png
Normal file
BIN
Resources/Character Set.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
Resources/Glare.png
Normal file
BIN
Resources/Glare.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1001 B |
BIN
Resources/Paused.png
Normal file
BIN
Resources/Paused.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 537 B |
1119
Source/AppleDisplay.java
Normal file
1119
Source/AppleDisplay.java
Normal file
File diff suppressed because it is too large
Load Diff
445
Source/AppleIIGo.java
Normal file
445
Source/AppleIIGo.java
Normal file
@ -0,0 +1,445 @@
|
||||
|
||||
/**
|
||||
* AppleIIGo
|
||||
* The Java Apple II Emulator
|
||||
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
|
||||
* Released under the GPL
|
||||
*/
|
||||
|
||||
import java.applet.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.zip.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
/**
|
||||
* AppleIIGo class<p>
|
||||
* Connects EmAppleII, AppleCanvas
|
||||
*/
|
||||
public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||
MouseListener, MouseMotionListener {
|
||||
// Class instances
|
||||
private EmAppleII apple;
|
||||
private AppleDisplay display;
|
||||
private AppleSpeaker speaker;
|
||||
private DiskII disk;
|
||||
|
||||
// Machine variables
|
||||
private boolean isCpuPaused;
|
||||
private boolean isCpuDebugEnabled;
|
||||
|
||||
// Keyboard variables
|
||||
private boolean keyboardUppercaseOnly;
|
||||
|
||||
// Paddle variables
|
||||
private boolean isPaddleInverted;
|
||||
|
||||
// Disk variables
|
||||
private String diskDriveResource[] = new String[2];
|
||||
private boolean diskWritable;
|
||||
|
||||
/**
|
||||
* Debug
|
||||
*/
|
||||
private void debug(String message) {
|
||||
// System.out.println(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters
|
||||
*/
|
||||
private String getAppletParameter(String parameter, String defaultValue) {
|
||||
String value = getParameter(parameter);
|
||||
if ((value == null) || (value.length() == 0))
|
||||
return defaultValue;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* On applet initialization
|
||||
*/
|
||||
public void init() {
|
||||
debug("init()");
|
||||
|
||||
// Activate listeners
|
||||
addKeyListener(this);
|
||||
addMouseListener(this);
|
||||
addMouseMotionListener(this);
|
||||
|
||||
if (getAppletParameter("displayFocusOnStart", "true").equals("true"))
|
||||
addComponentListener(this);
|
||||
|
||||
// Initialize Apple II emulator
|
||||
apple = new EmAppleII();
|
||||
loadRom(getAppletParameter("cpuRom", ""));
|
||||
apple.setCpuSpeed(new Integer(getAppletParameter("cpuSpeed", "1000")).intValue());
|
||||
isCpuPaused = getAppletParameter("cpuPaused", "false").equals("true");
|
||||
isCpuDebugEnabled = getAppletParameter("cpuDebugEnabled", "false").equals("true");
|
||||
apple.setStepMode(getAppletParameter("cpuStepMode", "false").equals("true"));
|
||||
|
||||
// Keyboard
|
||||
keyboardUppercaseOnly = getAppletParameter("keyboardUppercaseOnly", "true").equals("true");
|
||||
|
||||
// Display
|
||||
display = new AppleDisplay(this, apple);
|
||||
display.setScale(new Float(getAppletParameter("displayScale", "1")).floatValue());
|
||||
display.setRefreshRate(new Integer(getAppletParameter("displayRefreshRate", "10")).intValue());
|
||||
display.setColorMode(new Integer(getAppletParameter("displayColorMode", "0")).intValue());
|
||||
display.setStatMode(getAppletParameter("displayStatMode", "false").equals("true"));
|
||||
display.setGlare(getAppletParameter("displayGlare", "false").equals("true"));
|
||||
|
||||
// Speaker
|
||||
speaker = new AppleSpeaker(apple);
|
||||
speaker.setVolume(new Integer(getAppletParameter("speakerVolume", "3")).intValue());
|
||||
|
||||
// Peripherals
|
||||
disk = new DiskII();
|
||||
apple.setPeripheral(disk, 6);
|
||||
|
||||
// Initialize disk drives
|
||||
diskWritable = getAppletParameter("diskWritable", "false").equals("true");
|
||||
mountDisk(0, getAppletParameter("diskDrive1", ""));
|
||||
mountDisk(1, getAppletParameter("diskDrive2", ""));
|
||||
|
||||
// Start CPU
|
||||
if (!isCpuPaused)
|
||||
resume();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start applet
|
||||
*/
|
||||
public void start() {
|
||||
debug("start()");
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop applet
|
||||
*/
|
||||
public void stop() {
|
||||
debug("stop()");
|
||||
}
|
||||
|
||||
/**
|
||||
* On applet destruction
|
||||
*/
|
||||
public void destroy() {
|
||||
debug("destroy()");
|
||||
unmountDisk(0);
|
||||
unmountDisk(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Public Java interface
|
||||
|
||||
/**
|
||||
* Javascript interface
|
||||
*/
|
||||
public void focus() {
|
||||
debug("focus()");
|
||||
requestFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause emulator
|
||||
*/
|
||||
public void pause() {
|
||||
debug("pause()");
|
||||
isCpuPaused = true;
|
||||
apple.setPaused(isCpuPaused);
|
||||
display.setPaused(isCpuPaused);
|
||||
// speaker.setPaused(isCpuPaused);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume emulator
|
||||
*/
|
||||
public void resume() {
|
||||
debug("resume()");
|
||||
isCpuPaused = false;
|
||||
// speaker.setPaused(isCpuPaused);
|
||||
display.setPaused(isCpuPaused);
|
||||
apple.setPaused(isCpuPaused);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restarts emulator
|
||||
*/
|
||||
public void restart() {
|
||||
debug("restart()");
|
||||
apple.restart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open input stream
|
||||
*/
|
||||
private InputStream openInputStream(String resource) {
|
||||
InputStream is = null;
|
||||
|
||||
try {
|
||||
URL url = new URL(getCodeBase(), resource);
|
||||
is = url.openStream();
|
||||
|
||||
if (resource.toLowerCase().endsWith(".gz"))
|
||||
is = new GZIPInputStream(is);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
return is;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open output stream
|
||||
*/
|
||||
private OutputStream openOutputStream(String resource) {
|
||||
OutputStream os = null;
|
||||
|
||||
try {
|
||||
if (!(resource.substring(0, 6).equals("http://")))
|
||||
os = new FileOutputStream(resource);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load ROM
|
||||
*/
|
||||
public boolean loadRom(String resource) {
|
||||
debug("loadRom(resource: " + resource + ")");
|
||||
boolean success = false;
|
||||
|
||||
try {
|
||||
InputStream is = openInputStream(resource);
|
||||
success = apple.loadRom(is);
|
||||
is.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount a disk
|
||||
*/
|
||||
public boolean mountDisk(int drive, String resource) {
|
||||
debug("mountDisk(drive: " + drive + ", resource: " + resource + ")");
|
||||
boolean success = false;
|
||||
|
||||
if ((drive < 0) || (drive > 2))
|
||||
return success;
|
||||
|
||||
try {
|
||||
unmountDisk(drive);
|
||||
|
||||
diskDriveResource[drive] = resource;
|
||||
|
||||
InputStream is = openInputStream(resource);
|
||||
success = disk.readDisk(drive, is, 254, false);
|
||||
is.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmount a disk
|
||||
*/
|
||||
public void unmountDisk(int drive) {
|
||||
debug("unmountDisk(drive: " + drive + ")");
|
||||
if ((drive < 0) || (drive > 2))
|
||||
return;
|
||||
|
||||
if (!diskWritable)
|
||||
return;
|
||||
|
||||
try {
|
||||
OutputStream os = openOutputStream(diskDriveResource[drive]);
|
||||
disk.writeDisk(drive, os);
|
||||
os.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set color mode
|
||||
*/
|
||||
public void setColorMode(int value) {
|
||||
debug("setColorMode(value: " + value + ")");
|
||||
display.setColorMode(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get disk activity
|
||||
*/
|
||||
public boolean getDiskActivity() {
|
||||
return (!isCpuPaused && disk.isMotorOn());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* KeyListener event handling
|
||||
*/
|
||||
public void keyTyped(KeyEvent e) {
|
||||
// Send to emulator
|
||||
int key = e.getKeyChar();
|
||||
if (key == 10)
|
||||
apple.setKeyLatch(13);
|
||||
else if (key < 128) {
|
||||
if (keyboardUppercaseOnly && (key >= 97) && (key <= 122))
|
||||
key -= 32;
|
||||
|
||||
apple.setKeyLatch(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void keyPressed(KeyEvent e) {
|
||||
switch(e.getKeyCode()) {
|
||||
case KeyEvent.VK_BACK_SPACE:
|
||||
case KeyEvent.VK_LEFT:
|
||||
apple.setKeyLatch(8);
|
||||
break;
|
||||
case KeyEvent.VK_RIGHT:
|
||||
apple.setKeyLatch(21);
|
||||
break;
|
||||
case KeyEvent.VK_UP:
|
||||
if (e.isControlDown())
|
||||
speaker.setVolume(speaker.getVolume() + 1);
|
||||
else
|
||||
apple.setKeyLatch(11);
|
||||
break;
|
||||
case KeyEvent.VK_DOWN:
|
||||
if (e.isControlDown())
|
||||
speaker.setVolume(speaker.getVolume() - 1);
|
||||
else
|
||||
apple.setKeyLatch(10);
|
||||
break;
|
||||
case KeyEvent.VK_ESCAPE:
|
||||
apple.setKeyLatch(27);
|
||||
break;
|
||||
case KeyEvent.VK_DELETE:
|
||||
apple.setKeyLatch(127);
|
||||
break;
|
||||
case KeyEvent.VK_HOME:
|
||||
if (e.isControlDown())
|
||||
apple.restart();
|
||||
else
|
||||
apple.reset();
|
||||
break;
|
||||
case KeyEvent.VK_F5:
|
||||
if (isCpuDebugEnabled)
|
||||
display.setStatMode(!display.getStatMode());
|
||||
break;
|
||||
case KeyEvent.VK_F6:
|
||||
if (isCpuDebugEnabled)
|
||||
apple.setStepMode(!apple.getStepMode());
|
||||
break;
|
||||
case KeyEvent.VK_F7:
|
||||
if (isCpuDebugEnabled) {
|
||||
apple.setStepMode(apple.getStepMode());
|
||||
apple.stepInstructions(1);
|
||||
}
|
||||
break;
|
||||
case KeyEvent.VK_F8:
|
||||
if (isCpuDebugEnabled) {
|
||||
apple.setStepMode(apple.getStepMode());
|
||||
apple.stepInstructions(128);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void keyReleased(KeyEvent e) {
|
||||
}
|
||||
|
||||
/**
|
||||
* ComponentListener event handling
|
||||
*/
|
||||
public void componentHidden(ComponentEvent e) {
|
||||
}
|
||||
|
||||
public void componentMoved(ComponentEvent e) {
|
||||
}
|
||||
|
||||
public void componentResized(ComponentEvent e) {
|
||||
}
|
||||
|
||||
public void componentShown(ComponentEvent e) {
|
||||
debug("componentShown()");
|
||||
|
||||
removeComponentListener(this);
|
||||
requestFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* MouseListener, MouseMotionListener event handling
|
||||
*/
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
try {
|
||||
getAppletContext().showDocument(new URL("javascript:flipMouseOver();"));
|
||||
} catch (MalformedURLException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseExited(MouseEvent e) {
|
||||
try {
|
||||
getAppletContext().showDocument(new URL("javascript:flipMouseOut();"));
|
||||
} catch (MalformedURLException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
public void mousePressed(MouseEvent e) {
|
||||
int modifiers = e.getModifiers();
|
||||
|
||||
if ((modifiers & InputEvent.BUTTON1_MASK) != 0)
|
||||
apple.paddle.setButton(0, true);
|
||||
if ((modifiers & InputEvent.BUTTON3_MASK) != 0)
|
||||
apple.paddle.setButton(1, true);
|
||||
}
|
||||
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
int modifiers = e.getModifiers();
|
||||
|
||||
if ((modifiers & InputEvent.BUTTON1_MASK) != 0)
|
||||
apple.paddle.setButton(0, false);
|
||||
if ((modifiers & InputEvent.BUTTON3_MASK) != 0)
|
||||
apple.paddle.setButton(1, false);
|
||||
}
|
||||
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
mouseMoved(e);
|
||||
}
|
||||
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
if (isPaddleInverted) {
|
||||
apple.paddle.setPaddlePos(0, (int) (display.getScale() * (255 - e.getY() * 256 / 192)));
|
||||
apple.paddle.setPaddlePos(1, (int) (display.getScale() * (255 - e.getX() * 256 / 280)));
|
||||
} else {
|
||||
apple.paddle.setPaddlePos(0, (int) (e.getX() * display.getScale() * 256 / 280));
|
||||
apple.paddle.setPaddlePos(1, (int) (e.getY() * display.getScale() * 256 / 192));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applet paint function
|
||||
*/
|
||||
public void paint(Graphics g) {
|
||||
display.paint(g);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applet update function
|
||||
*/
|
||||
public void update(Graphics g) {
|
||||
display.paint(g);
|
||||
}
|
||||
}
|
240
Source/AppleSpeaker.java
Normal file
240
Source/AppleSpeaker.java
Normal file
@ -0,0 +1,240 @@
|
||||
|
||||
/**
|
||||
* AppleIIGo
|
||||
* Speaker processing
|
||||
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
|
||||
* Released under the GPL
|
||||
*/
|
||||
|
||||
import javax.sound.sampled.*;
|
||||
|
||||
public class AppleSpeaker implements Runnable {
|
||||
// Instances of other classes
|
||||
private EmAppleII apple;
|
||||
|
||||
// Refresh
|
||||
private int refreshRate;
|
||||
private long refreshInterval;
|
||||
|
||||
// Sound stuff
|
||||
private static final int SPEAKER_BITS = 16;
|
||||
private static final int SPEAKER_SAMPLERATE = 44100;
|
||||
private static final int SPEAKER_CHANNELS = 1;
|
||||
private static final int SPEAKER_SAMPLESIZE = (SPEAKER_BITS * SPEAKER_CHANNELS / 8);
|
||||
private static final boolean SPEAKER_SIGNED = true;
|
||||
private static final boolean SPEAKER_BIGENDIAN = false;
|
||||
|
||||
private int clock, clockNextFlip, clockEnd;
|
||||
private boolean isFlipsBufferEmpty = true;
|
||||
|
||||
private SourceDataLine line;
|
||||
|
||||
private int bufferSamples;
|
||||
private int bufferSize;
|
||||
private byte[] buffer;
|
||||
|
||||
private int speakerVolume;
|
||||
private int speakerFlipsPointer;
|
||||
private int speakerFlipState;
|
||||
|
||||
private int[] speakerFlipStateToVolume = new int[2];
|
||||
private int speakerClocksPerSample;
|
||||
|
||||
// Thread stuff
|
||||
private boolean isPaused = true;
|
||||
private Thread thread;
|
||||
private String threadError = null;
|
||||
|
||||
|
||||
|
||||
public AppleSpeaker(EmAppleII apple) {
|
||||
this.apple = apple;
|
||||
|
||||
setVolume(4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set refresh rate
|
||||
*
|
||||
* @param value Speaker refresh rate in mHz
|
||||
*/
|
||||
private void setRefreshRate(int value) {
|
||||
if (value <= 0.0f)
|
||||
return;
|
||||
|
||||
this.refreshRate = value;
|
||||
refreshInterval = (int) (1000.0 / value);
|
||||
|
||||
speakerClocksPerSample = (int) (apple.getCpuSpeed() * 1000.0f / SPEAKER_SAMPLERATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get refresh rate
|
||||
*/
|
||||
private int getRefreshRate() {
|
||||
return refreshRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set speaker volume
|
||||
*/
|
||||
public void setVolume(int value) {
|
||||
if ((value < 0) || (value > 7))
|
||||
return;
|
||||
|
||||
speakerVolume = value;
|
||||
|
||||
int absVolume = 1 << (value + 8);
|
||||
speakerFlipStateToVolume[0] = -absVolume;
|
||||
speakerFlipStateToVolume[1] = absVolume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get speaker volume
|
||||
*/
|
||||
public int getVolume() {
|
||||
return speakerVolume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set pause state
|
||||
*/
|
||||
public void setPaused(boolean value) {
|
||||
if (isPaused == value)
|
||||
return;
|
||||
|
||||
isPaused = value;
|
||||
if (isPaused) {
|
||||
try {
|
||||
thread.join(1000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
if (line != null) {
|
||||
line.stop();
|
||||
line.close();
|
||||
}
|
||||
} else {
|
||||
setRefreshRate(apple.getRefreshRate());
|
||||
|
||||
AudioFormat audioFormat = new AudioFormat(
|
||||
SPEAKER_SAMPLERATE,
|
||||
SPEAKER_BITS,
|
||||
SPEAKER_CHANNELS,
|
||||
SPEAKER_SIGNED,
|
||||
SPEAKER_BIGENDIAN);
|
||||
|
||||
DataLine.Info info = new DataLine.Info(
|
||||
DataLine.class,
|
||||
audioFormat);
|
||||
|
||||
try {
|
||||
line = (SourceDataLine) AudioSystem.getLine(info);
|
||||
bufferSize = line.getBufferSize();
|
||||
bufferSamples = bufferSize / SPEAKER_SAMPLESIZE;
|
||||
|
||||
buffer = new byte[bufferSize];
|
||||
|
||||
line.open(audioFormat);
|
||||
line.start();
|
||||
} catch (LineUnavailableException e) {
|
||||
}
|
||||
|
||||
thread = new Thread(this);
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Speaker refresh thread
|
||||
*/
|
||||
public void run() {
|
||||
try {
|
||||
while (!isPaused) {
|
||||
long refreshStart = System.currentTimeMillis();
|
||||
long refreshDelay;
|
||||
|
||||
refreshSpeaker();
|
||||
|
||||
refreshDelay = System.currentTimeMillis() - refreshStart;
|
||||
|
||||
if (refreshDelay < refreshInterval)
|
||||
Thread.sleep(refreshInterval - refreshDelay);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Speaker refresh
|
||||
*/
|
||||
private void refreshSpeaker() {
|
||||
clockEnd = apple.clock;
|
||||
int bytes;
|
||||
|
||||
if (line == null)
|
||||
return;
|
||||
|
||||
while ((bytes = fillBuffer()) > 0) {
|
||||
line.write(buffer, 0, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill buffer
|
||||
*/
|
||||
private int fillBuffer() {
|
||||
int value = speakerFlipStateToVolume[speakerFlipState];
|
||||
int clockEndSample = clockEnd - speakerClocksPerSample;
|
||||
int bufferPointer = 0;
|
||||
|
||||
initNextFlip();
|
||||
while (bufferPointer < bufferSize) {
|
||||
if (clockEndSample == clock)
|
||||
break;
|
||||
|
||||
if (((clockEndSample - clock) & 0x7fffffff) > 0x3fffffff)
|
||||
break;
|
||||
|
||||
// Find all flips on current sample
|
||||
while (((clockNextFlip - clock) & 0x7fffffff) < speakerClocksPerSample) {
|
||||
getNextFlip();
|
||||
speakerFlipState = (speakerFlipState ^ 1);
|
||||
value = speakerFlipStateToVolume[speakerFlipState];
|
||||
}
|
||||
|
||||
// Write sample
|
||||
buffer[bufferPointer] = (byte) (value & 0xff);
|
||||
buffer[bufferPointer + 1] = (byte) (value >> 8);
|
||||
bufferPointer += SPEAKER_SAMPLESIZE;
|
||||
|
||||
clock += speakerClocksPerSample;
|
||||
}
|
||||
|
||||
return bufferPointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset next flip
|
||||
*/
|
||||
private void initNextFlip() {
|
||||
if (isFlipsBufferEmpty) {
|
||||
isFlipsBufferEmpty = false;
|
||||
getNextFlip();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets next flip
|
||||
*/
|
||||
private void getNextFlip() {
|
||||
if (speakerFlipsPointer == apple.speakerFlipsPointer) {
|
||||
clockNextFlip = clock + 0x3fffffff;
|
||||
isFlipsBufferEmpty = true;
|
||||
} else {
|
||||
clockNextFlip = apple.speakerFlips[speakerFlipsPointer];
|
||||
speakerFlipsPointer = (speakerFlipsPointer + 1) & apple.SPEAKER_FLIPS_MASK;
|
||||
}
|
||||
}
|
||||
}
|
503
Source/DiskII.java
Normal file
503
Source/DiskII.java
Normal file
@ -0,0 +1,503 @@
|
||||
|
||||
/**
|
||||
* AppleIIGo
|
||||
* Disk II Emulator
|
||||
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
|
||||
* Released under the GPL
|
||||
* Based on work by Doug Kwan
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class DiskII extends Peripheral {
|
||||
// ROM (with boot wait cycle optimization)
|
||||
private static final int[] rom = {
|
||||
0xA2,0x20,0xA0,0x00,0xA2,0x03,0x86,0x3C,0x8A,0x0A,0x24,0x3C,0xF0,0x10,0x05,0x3C,
|
||||
0x49,0xFF,0x29,0x7E,0xB0,0x08,0x4A,0xD0,0xFB,0x98,0x9D,0x56,0x03,0xC8,0xE8,0x10,
|
||||
0xE5,0x20,0x58,0xFF,0xBA,0xBD,0x00,0x01,0x0A,0x0A,0x0A,0x0A,0x85,0x2B,0xAA,0xBD,
|
||||
0x8E,0xC0,0xBD,0x8C,0xC0,0xBD,0x8A,0xC0,0xBD,0x89,0xC0,0xA0,0x50,0xBD,0x80,0xC0,
|
||||
0x98,0x29,0x03,0x0A,0x05,0x2B,0xAA,0xBD,0x81,0xC0,0xA9,0x56,0xa9,0x00,0xea,0x88,
|
||||
0x10,0xEB,0x85,0x26,0x85,0x3D,0x85,0x41,0xA9,0x08,0x85,0x27,0x18,0x08,0xBD,0x8C,
|
||||
0xC0,0x10,0xFB,0x49,0xD5,0xD0,0xF7,0xBD,0x8C,0xC0,0x10,0xFB,0xC9,0xAA,0xD0,0xF3,
|
||||
0xEA,0xBD,0x8C,0xC0,0x10,0xFB,0xC9,0x96,0xF0,0x09,0x28,0x90,0xDF,0x49,0xAD,0xF0,
|
||||
0x25,0xD0,0xD9,0xA0,0x03,0x85,0x40,0xBD,0x8C,0xC0,0x10,0xFB,0x2A,0x85,0x3C,0xBD,
|
||||
0x8C,0xC0,0x10,0xFB,0x25,0x3C,0x88,0xD0,0xEC,0x28,0xC5,0x3D,0xD0,0xBE,0xA5,0x40,
|
||||
0xC5,0x41,0xD0,0xB8,0xB0,0xB7,0xA0,0x56,0x84,0x3C,0xBC,0x8C,0xC0,0x10,0xFB,0x59,
|
||||
0xD6,0x02,0xA4,0x3C,0x88,0x99,0x00,0x03,0xD0,0xEE,0x84,0x3C,0xBC,0x8C,0xC0,0x10,
|
||||
0xFB,0x59,0xD6,0x02,0xA4,0x3C,0x91,0x26,0xC8,0xD0,0xEF,0xBC,0x8C,0xC0,0x10,0xFB,
|
||||
0x59,0xD6,0x02,0xD0,0x87,0xA0,0x00,0xA2,0x56,0xCA,0x30,0xFB,0xB1,0x26,0x5E,0x00,
|
||||
0x03,0x2A,0x5E,0x00,0x03,0x2A,0x91,0x26,0xC8,0xD0,0xEE,0xE6,0x27,0xE6,0x3D,0xA5,
|
||||
0x3D,0xCD,0x00,0x08,0xA6,0x2B,0x90,0xDB,0x4C,0x01,0x08,0x00,0x00,0x00,0x00,0x00,
|
||||
};
|
||||
|
||||
// Constants
|
||||
private static final int NUM_DRIVES = 2;
|
||||
private static final int DOS_NUM_SECTORS = 16;
|
||||
private static final int DOS_NUM_TRACKS = 35;
|
||||
private static final int DOS_TRACK_BYTES = 256 * DOS_NUM_SECTORS;
|
||||
private static final int RAW_TRACK_BYTES = 6250;
|
||||
|
||||
// Disk II direct access variables
|
||||
private int drive = 0;
|
||||
private boolean isMotorOn = false;
|
||||
|
||||
private byte[][][] disk = new byte[NUM_DRIVES][DOS_NUM_TRACKS][];
|
||||
private boolean[] isWriteProtected = new boolean[NUM_DRIVES];
|
||||
|
||||
private int currPhysTrack;
|
||||
private int currNibble;
|
||||
|
||||
// Caches
|
||||
private int[] driveCurrPhysTrack = new int[NUM_DRIVES];
|
||||
private byte[] realTrack;
|
||||
|
||||
/*
|
||||
* Disk II emulation:
|
||||
*
|
||||
* C0xD, C0xE -> Read write protect
|
||||
* C0xE, C0xC -> Read data from disk
|
||||
* Write data to disk -> C0xF, C0xC
|
||||
* Write data to disk -> C0xD, C0xC
|
||||
*
|
||||
* We use 'fast mode', i.e. no 65(C)02 clock reference
|
||||
* We use simplified track handling (only adjacent phases)
|
||||
*/
|
||||
|
||||
// Internal registers
|
||||
private int latchAddress;
|
||||
private int latchData;
|
||||
private boolean writeMode;
|
||||
|
||||
// GCR encoding and decoding tables
|
||||
private static final int[] gcrEncodingTable = {
|
||||
0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
|
||||
0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
|
||||
0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
|
||||
0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
|
||||
0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
|
||||
0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
|
||||
0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
|
||||
0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
|
||||
};
|
||||
private int[] gcrDecodingTable = new int[256];
|
||||
private int[] gcrSwapBit = {0, 2, 1, 3};
|
||||
private int[] gcrBuffer = new int[256];
|
||||
private int[] gcrBuffer2 = new int[86];
|
||||
|
||||
// Physical sector to DOS 3.3 logical sector table
|
||||
private static final int[] gcrLogicalSector = {
|
||||
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4,
|
||||
0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
|
||||
|
||||
// Temporary variables for conversion
|
||||
private byte[] gcrNibbles = new byte[RAW_TRACK_BYTES];
|
||||
private int gcrNibblesPos;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public DiskII() {
|
||||
super();
|
||||
|
||||
readDisk(0, null, 254, false);
|
||||
readDisk(1, null, 254, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* I/O read
|
||||
*
|
||||
* @param address Address
|
||||
*/
|
||||
public int ioRead(int address) {
|
||||
int phase;
|
||||
|
||||
switch (address & 0xf) {
|
||||
case 0x0:
|
||||
case 0x2:
|
||||
case 0x4:
|
||||
case 0x6:
|
||||
// Q0, Q1, Q2, Q3 off
|
||||
break;
|
||||
case 0x1:
|
||||
// Q0 on
|
||||
phase = currPhysTrack & 3;
|
||||
if (phase == 1) {
|
||||
if (currPhysTrack > 0)
|
||||
currPhysTrack--;
|
||||
} else if (phase == 3) {
|
||||
if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1))
|
||||
currPhysTrack++;
|
||||
}
|
||||
realTrack = disk[drive][currPhysTrack >> 1];
|
||||
break;
|
||||
case 0x3:
|
||||
// Q1 on
|
||||
phase = currPhysTrack & 3;
|
||||
if (phase == 2) {
|
||||
if (currPhysTrack > 0)
|
||||
currPhysTrack--;
|
||||
} else if (phase == 0) {
|
||||
if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1))
|
||||
currPhysTrack++;
|
||||
}
|
||||
realTrack = disk[drive][currPhysTrack >> 1];
|
||||
break;
|
||||
case 0x5:
|
||||
// Q2 on
|
||||
phase = currPhysTrack & 3;
|
||||
if (phase == 3) {
|
||||
if (currPhysTrack > 0)
|
||||
currPhysTrack--;
|
||||
} else if (phase == 1) {
|
||||
if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1))
|
||||
currPhysTrack++;
|
||||
}
|
||||
realTrack = disk[drive][currPhysTrack >> 1];
|
||||
break;
|
||||
case 0x7:
|
||||
// Q3 on
|
||||
phase = currPhysTrack & 3;
|
||||
if (phase == 0) {
|
||||
if (currPhysTrack > 0)
|
||||
currPhysTrack--;
|
||||
} else if (phase == 2) {
|
||||
if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1))
|
||||
currPhysTrack++;
|
||||
}
|
||||
realTrack = disk[drive][currPhysTrack >> 1];
|
||||
break;
|
||||
case 0x8:
|
||||
// Motor off
|
||||
isMotorOn = false;
|
||||
break;
|
||||
case 0x9:
|
||||
// Motor on
|
||||
isMotorOn = true;
|
||||
break;
|
||||
case 0xa:
|
||||
// Drive 1
|
||||
driveCurrPhysTrack[drive] = currPhysTrack;
|
||||
drive = 0;
|
||||
currPhysTrack = driveCurrPhysTrack[drive];
|
||||
|
||||
realTrack = disk[drive][currPhysTrack >> 1];
|
||||
break;
|
||||
case 0xb:
|
||||
// Drive 2
|
||||
driveCurrPhysTrack[drive] = currPhysTrack;
|
||||
drive = 1;
|
||||
currPhysTrack = driveCurrPhysTrack[drive];
|
||||
|
||||
realTrack = disk[drive][currPhysTrack >> 1];
|
||||
break;
|
||||
case 0xc:
|
||||
return ioLatchC();
|
||||
case 0xd:
|
||||
ioLatchD(0xff);
|
||||
break;
|
||||
case 0xe:
|
||||
return ioLatchE();
|
||||
case 0xf:
|
||||
ioLatchF(0xff);
|
||||
break;
|
||||
}
|
||||
|
||||
return rand.nextInt(256);
|
||||
}
|
||||
|
||||
/**
|
||||
* I/O write
|
||||
*
|
||||
* @param address Address
|
||||
*/
|
||||
public void ioWrite(int address, int value) {
|
||||
switch (address & 0xf) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
case 0x3:
|
||||
case 0x4:
|
||||
case 0x5:
|
||||
case 0x6:
|
||||
case 0x7:
|
||||
case 0x8:
|
||||
case 0x9:
|
||||
case 0xa:
|
||||
case 0xb:
|
||||
ioRead(address);
|
||||
break;
|
||||
case 0xc:
|
||||
ioLatchC();
|
||||
break;
|
||||
case 0xd:
|
||||
ioLatchD(value);
|
||||
break;
|
||||
case 0xe:
|
||||
ioLatchE();
|
||||
break;
|
||||
case 0xf:
|
||||
ioLatchF(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Memory read
|
||||
*
|
||||
* @param address Address
|
||||
*/
|
||||
public int memoryRead(int address) {
|
||||
return rom[address & 0xff];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset peripheral
|
||||
*/
|
||||
public void reset() {
|
||||
ioRead(0x8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a disk
|
||||
*
|
||||
* @param is InputStream
|
||||
* @param drive Disk II drive
|
||||
*/
|
||||
public boolean readDisk(int drive, InputStream is, int volume, boolean isWriteProtected) {
|
||||
byte[] track = new byte[RAW_TRACK_BYTES];
|
||||
|
||||
try {
|
||||
for (int trackNum = 0; trackNum < DOS_NUM_TRACKS; trackNum++) {
|
||||
disk[drive][trackNum] = new byte[RAW_TRACK_BYTES];
|
||||
|
||||
if (is != null) {
|
||||
is.read(track, 0, DOS_TRACK_BYTES);
|
||||
trackToNibbles(track, disk[drive][trackNum], volume, trackNum);
|
||||
}
|
||||
}
|
||||
|
||||
this.realTrack = disk[drive][currPhysTrack >> 1];
|
||||
this.isWriteProtected[drive] = isWriteProtected;
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a disk
|
||||
*
|
||||
* @param is InputStream
|
||||
* @param drive Disk II drive
|
||||
*/
|
||||
public boolean writeDisk(int drive, OutputStream os) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Motor on indicator
|
||||
*/
|
||||
public boolean isMotorOn() {
|
||||
return isMotorOn;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* I/O read Latch C
|
||||
*
|
||||
* @param address Address
|
||||
*/
|
||||
private int ioLatchC() {
|
||||
if (writeMode)
|
||||
// Write data: C0xD, C0xC
|
||||
realTrack[currNibble] = (byte) latchData;
|
||||
else
|
||||
// Read data: C0xE, C0xC
|
||||
latchData = (realTrack[currNibble] & 0xff);
|
||||
|
||||
currNibble++;
|
||||
if (currNibble >= RAW_TRACK_BYTES)
|
||||
currNibble = 0;
|
||||
|
||||
latchAddress = 0xc;
|
||||
return latchData;
|
||||
}
|
||||
|
||||
/**
|
||||
* I/O write Latch D
|
||||
*
|
||||
* @param address Address
|
||||
*/
|
||||
private void ioLatchD(int value) {
|
||||
// Prepare write
|
||||
writeMode = true;
|
||||
latchData = value;
|
||||
latchAddress = 0xd;
|
||||
}
|
||||
|
||||
/**
|
||||
* I/O read Latch E
|
||||
*
|
||||
* @param address Address
|
||||
*/
|
||||
private int ioLatchE() {
|
||||
// Read write-protect: C0xD, C0xE
|
||||
if (latchAddress == 0xd) {
|
||||
latchAddress = 0xe;
|
||||
return isWriteProtected[drive] ? 0x80 : 0x00;
|
||||
}
|
||||
|
||||
writeMode = false;
|
||||
latchAddress = 0xe;
|
||||
return 0x3c;
|
||||
}
|
||||
|
||||
/**
|
||||
* I/O write Latch F
|
||||
*
|
||||
* @param address Address
|
||||
*/
|
||||
private void ioLatchF(int value) {
|
||||
// Prepare write
|
||||
writeMode = true;
|
||||
latchData = value;
|
||||
latchAddress = 0xf;
|
||||
}
|
||||
|
||||
/**
|
||||
* TRACK CONVERSION ROUTINES
|
||||
*/
|
||||
|
||||
/**
|
||||
* Writes a nibble
|
||||
*
|
||||
* @param value Value
|
||||
*/
|
||||
private final void gcrWriteNibble(int value) {
|
||||
gcrNibbles[gcrNibblesPos] = (byte) value;
|
||||
gcrNibblesPos++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes sync bits
|
||||
*
|
||||
* @param length Number of bits
|
||||
*/
|
||||
private final void writeSync(int length) {
|
||||
while(length > 0) {
|
||||
length--;
|
||||
gcrWriteNibble(0xff);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an FM encoded value, used in writing address fields
|
||||
*
|
||||
* @param value Value
|
||||
*/
|
||||
private final void encode44(int value) {
|
||||
gcrWriteNibble((value >> 1) | 0xaa);
|
||||
gcrWriteNibble(value | 0xaa);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode in 6:2
|
||||
*
|
||||
* @param track Sectorized track data
|
||||
* @param offset Offset in this data
|
||||
*/
|
||||
private void encode62(byte[] track, int offset) {
|
||||
// 86 * 3 = 258, so the first two byte are encoded twice
|
||||
gcrBuffer2[0] = gcrSwapBit[track[offset + 1] & 0x03];
|
||||
gcrBuffer2[1] = gcrSwapBit[track[offset] & 0x03];
|
||||
|
||||
// Save higher 6 bits in gcrBuffer and lower 2 bits in gcrBuffer2
|
||||
for(int i = 255, j = 2; i >= 0; i--, j = j == 85 ? 0: j + 1) {
|
||||
gcrBuffer2[j] = ((gcrBuffer2[j] << 2) | gcrSwapBit[track[offset + i] & 0x03]);
|
||||
gcrBuffer[i] = (track[offset + i] & 0xff) >> 2;
|
||||
}
|
||||
|
||||
// Clear off higher 2 bits of GCR_buffer2 set in the last call
|
||||
for(int i = 0; i < 86; i++)
|
||||
gcrBuffer2[i] &= 0x3f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write address field
|
||||
*
|
||||
* @param track Sectorized track data
|
||||
* @param offset Offset in this data
|
||||
*/
|
||||
private final void writeAddressField(int volumeNum, int trackNum, int sectorNum) {
|
||||
// Write address mark
|
||||
gcrWriteNibble(0xd5);
|
||||
gcrWriteNibble(0xaa);
|
||||
gcrWriteNibble(0x96);
|
||||
|
||||
// Write volume, trackNum, sector & checksum
|
||||
encode44(volumeNum);
|
||||
encode44(trackNum);
|
||||
encode44(sectorNum);
|
||||
encode44(volumeNum ^ trackNum ^ sectorNum);
|
||||
|
||||
// Write epilogue
|
||||
gcrWriteNibble(0xde);
|
||||
gcrWriteNibble(0xaa);
|
||||
gcrWriteNibble(0xeb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data field
|
||||
*/
|
||||
private void writeDataField() {
|
||||
int last = 0;
|
||||
int checksum;
|
||||
|
||||
// Write prologue
|
||||
gcrWriteNibble(0xd5);
|
||||
gcrWriteNibble(0xaa);
|
||||
gcrWriteNibble(0xad);
|
||||
|
||||
// Write GCR encoded data
|
||||
for(int i = 0x55; i >= 0; i--) {
|
||||
checksum = last ^ gcrBuffer2[i];
|
||||
gcrWriteNibble(gcrEncodingTable[checksum]);
|
||||
last = gcrBuffer2[i];
|
||||
}
|
||||
for(int i = 0; i < 256; i++) {
|
||||
checksum = last ^ gcrBuffer[i];
|
||||
gcrWriteNibble(gcrEncodingTable[checksum]);
|
||||
last = gcrBuffer[i];
|
||||
}
|
||||
|
||||
// Write checksum
|
||||
gcrWriteNibble(gcrEncodingTable[last]);
|
||||
|
||||
// Write epilogue
|
||||
gcrWriteNibble(0xde);
|
||||
gcrWriteNibble(0xaa);
|
||||
gcrWriteNibble(0xeb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a track to nibbles
|
||||
*/
|
||||
private void trackToNibbles(byte[] track, byte[] nibbles, int volumeNum, int trackNum) {
|
||||
this.gcrNibbles = nibbles;
|
||||
gcrNibblesPos = 0;
|
||||
|
||||
for (int sectorNum = 0; sectorNum < DOS_NUM_SECTORS; sectorNum++) {
|
||||
encode62(track, gcrLogicalSector[sectorNum] << 8);
|
||||
writeSync(12);
|
||||
writeAddressField(volumeNum, trackNum, sectorNum);
|
||||
writeSync(8);
|
||||
writeDataField();
|
||||
}
|
||||
writeSync(RAW_TRACK_BYTES - gcrNibblesPos);
|
||||
}
|
||||
}
|
1617
Source/Em6502.java
Normal file
1617
Source/Em6502.java
Normal file
File diff suppressed because it is too large
Load Diff
1186
Source/EmAppleII.java
Normal file
1186
Source/EmAppleII.java
Normal file
File diff suppressed because it is too large
Load Diff
100
Source/Paddle.java
Normal file
100
Source/Paddle.java
Normal file
@ -0,0 +1,100 @@
|
||||
|
||||
/**
|
||||
* AppleIIGo
|
||||
* Apple II Emulator for J2ME
|
||||
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
|
||||
* Released under the GPL
|
||||
*/
|
||||
|
||||
public class Paddle {
|
||||
// Public variables
|
||||
public static final int PADDLE_LOW = 0;
|
||||
public static final int PADDLE_CENTER = 127;
|
||||
public static final int PADDLE_HIGH = 255;
|
||||
|
||||
public static final int PADDLEMODE_DIRECT = 0;
|
||||
public static final int PADDLEMODE_FILTERED = 1;
|
||||
|
||||
// Instances of other classes
|
||||
private EmAppleII apple;
|
||||
|
||||
// Button variables
|
||||
private int[] buttonRegister = new int[4];
|
||||
|
||||
// Paddle variables
|
||||
private int paddleMode;
|
||||
|
||||
private int[] paddleClockEvent = new int[4];
|
||||
private int[] paddleClockInc = new int[4];
|
||||
|
||||
/**
|
||||
* Paddle class constructor
|
||||
*
|
||||
* @param apple The EmAppleII instance
|
||||
*/
|
||||
public Paddle(EmAppleII apple) {
|
||||
this.apple = apple;
|
||||
|
||||
setPaddlePos(0, PADDLE_CENTER);
|
||||
setPaddlePos(1, PADDLE_CENTER);
|
||||
setPaddlePos(2, PADDLE_CENTER);
|
||||
setPaddlePos(3, PADDLE_CENTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set button state
|
||||
*
|
||||
* @param button Paddle button
|
||||
* @param state State
|
||||
*/
|
||||
public void setButton(int button, boolean pressed) {
|
||||
buttonRegister[button] = (pressed ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
/**
|
||||
* Button register
|
||||
*
|
||||
* @param button Paddle button
|
||||
*/
|
||||
public int getButtonRegister(int button) {
|
||||
return buttonRegister[button];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set paddle position
|
||||
*
|
||||
* @param address Address
|
||||
* @param value Value
|
||||
*/
|
||||
public void setPaddlePos(int paddle, int value) {
|
||||
/*
|
||||
* Magic formula, see ROM $FB1E-$FB2E,
|
||||
* We calculate the numbers of cycles after which
|
||||
* the RC circuit of a triggered paddle will discharge.
|
||||
*/
|
||||
paddleClockInc[paddle] = value * 11 + 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger paddle register
|
||||
*
|
||||
* @param address Address
|
||||
* @param value Value
|
||||
*/
|
||||
public void triggerRegister() {
|
||||
paddleClockEvent[0] = apple.clock + paddleClockInc[0];
|
||||
paddleClockEvent[1] = apple.clock + paddleClockInc[1];
|
||||
paddleClockEvent[2] = apple.clock + paddleClockInc[2];
|
||||
paddleClockEvent[3] = apple.clock + paddleClockInc[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get paddle register
|
||||
*
|
||||
* @param address Address
|
||||
* @param value Value
|
||||
*/
|
||||
public int getPaddleRegister(int paddle) {
|
||||
return ((((paddleClockEvent[paddle] - apple.clock) & 0x7fffffff) < 0x40000000) ? 0x80 : 0x00);
|
||||
}
|
||||
}
|
34
Source/Peripheral.java
Normal file
34
Source/Peripheral.java
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
/**
|
||||
* AppleIIGo
|
||||
* Slot interface
|
||||
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
|
||||
* Released under the GPL
|
||||
* Based on work by Steven E. Hugg
|
||||
*/
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class Peripheral {
|
||||
protected Random rand = new Random();
|
||||
|
||||
public Peripheral() {
|
||||
}
|
||||
|
||||
public int ioRead(int address) {
|
||||
return rand.nextInt(256);
|
||||
}
|
||||
|
||||
public void ioWrite(int address, int value) {
|
||||
}
|
||||
|
||||
public int memoryRead(int address) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void memoryWrite(int address, int value) {
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
}
|
||||
}
|
19
build.xml
Normal file
19
build.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<project default="jar">
|
||||
<target name="clean">
|
||||
<delete dir="Build" />
|
||||
</target>
|
||||
|
||||
<target name="compile">
|
||||
<mkdir dir="Build/classes"/>
|
||||
<javac srcdir="Source" destdir="Build/classes" optimize="yes"/>
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="compile">
|
||||
<mkdir dir="Build/jar"/>
|
||||
<copy todir="Build/classes/Resources">
|
||||
<fileset dir="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