Fix for random divide by zero error and a 100% fix to support mockingboard detection in both SkyFox and the French Touch "Not So Cheap Tunes" disk. Lesson learned: Timer1 is initialized in a free-running state on power-up.

This commit is contained in:
Brendan Robert 2015-09-05 17:28:41 -05:00
parent df7209930c
commit 674d9d6980
3 changed files with 90 additions and 45 deletions

View File

@ -159,14 +159,17 @@ public class CardMockingboard extends Card implements Runnable {
if (e.getType().isRead()) {
int val = controller.readRegister(register & 0x0f);
e.setNewValue(val);
// System.out.println("Read "+Integer.toHexString(register)+" == "+val);
} else {
controller.writeRegister(register & 0x0f, e.getNewValue());
// System.out.println("Write "+Integer.toHexString(register)+" == "+e.getNewValue());
}
}
@Override
protected void handleIOAccess(int register, TYPE type, int value, RAMEvent e) {
// Oddly, all IO is done at the firmware address bank. It's a strange card.
// System.out.println("MB I/O Access "+type.name()+" "+register+":"+value);
e.setNewValue(computer.getVideo().getFloatingBus());
}
long ticksSinceLastPlayback = 0;
@ -319,6 +322,9 @@ public class CardMockingboard extends Card implements Runnable {
int zeroSamples = 0;
setRun(true);
while (isRunning()) {
while (!computer.isRunning()) {
Thread.sleep(500);
}
computer.getMotherboard().requestSpeed(this);
playSound(leftBuffer, rightBuffer);
int p = 0;
@ -381,6 +387,8 @@ public class CardMockingboard extends Card implements Runnable {
} catch (LineUnavailableException ex) {
Logger.getLogger(CardMockingboard.class
.getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(CardMockingboard.class.getName()).log(Level.SEVERE, null, ex);
} finally {
computer.getMotherboard().cancelSpeedRequest(this);
System.out.println("Mockingboard playback stopped");

View File

@ -23,17 +23,22 @@ import jace.core.Device;
/**
* Implementation of 6522 VIA chip
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public abstract class R6522 extends Device {
public R6522(Computer computer) {
super(computer);
timer1freerun = true;
timer1running = true;
timer1latch = 0x1fff;
timer1interruptEnabled = false;
setRun(true);
}
// 6522 VIA
// http://www.applevault.com/twiki/Main/Mockingboard/6522.pdf
// I/O registers
public static enum Register {
ORB(0), // Output Register B
@ -54,13 +59,16 @@ public abstract class R6522 extends Device {
ORAH(15);// Output Register A (no handshake)
int val;
Register(int v) {
val = v;
}
static public Register fromInt(int i) {
for (Register r : Register.values()) {
if (r.val == i) return r;
if (r.val == i) {
return r;
}
}
return null;
}
@ -130,14 +138,16 @@ public abstract class R6522 extends Device {
protected String getDeviceName() {
return "6522 VIA Chip";
}
@Override
public void tick() {
if (timer1running) {
timer1counter --;
timer1counter--;
if (timer1counter < 0) {
timer1counter = timer1latch;
if (!timer1freerun) timer1running = false;
if (!timer1freerun) {
timer1running = false;
}
if (timer1interruptEnabled) {
// System.out.println("Timer 1 generated interrupt");
timer1IRQ = true;
@ -146,7 +156,7 @@ public abstract class R6522 extends Device {
}
}
if (timer2running) {
timer2counter --;
timer2counter--;
if (timer2counter < 0) {
timer2running = false;
timer2counter = timer2latch;
@ -156,39 +166,42 @@ public abstract class R6522 extends Device {
}
}
}
if (!timer1running && !timer2running)
if (!timer1running && !timer2running) {
setRun(false);
}
}
@Override
public void attach() {
// Start chip
}
@Override
public void reconfigure() {
// Reset
}
public void writeRegister(int reg, int value) {
value &= 0x0ff;
public void writeRegister(int reg, int val) {
int value = val & 0x0ff;
Register r = Register.fromInt(reg);
// System.out.println("Writing "+(value&0x0ff)+" to register "+r.toString());
switch (r) {
case ORB:
if (dataDirectionB == 0) break;
value = value & dataDirectionB;
sendOutputB(value);
if (dataDirectionB == 0) {
break;
}
sendOutputB(value & dataDirectionB);
break;
case ORA:
// case ORAH:
if (dataDirectionA == 0) break;
value = value & dataDirectionA;
sendOutputA(value);
if (dataDirectionA == 0) {
break;
}
sendOutputA(value & dataDirectionA);
break;
case DDRB:
dataDirectionB = value;
break;
break;
case DDRA:
dataDirectionA = value;
break;
@ -223,39 +236,55 @@ public abstract class R6522 extends Device {
case ACR:
// SHIFT REGISTER NOT IMPLEMENTED
timer1freerun = (value & 64) != 0;
if (timer1freerun) timer1running = true;
if (timer1freerun) {
timer1running = true;
setRun(true);
}
break;
case PCR:
// TODO: Implement if Votrax (SSI) is to be supported
break;
case IFR:
if ((value & 64) != 0) timer1IRQ = false;
if ((value & 32) != 0) timer2IRQ = false;
if ((value & 64) != 0) {
timer1IRQ = false;
}
if ((value & 32) != 0) {
timer2IRQ = false;
}
break;
case IER:
boolean enable = (value & 128) != 0;
if ((value & 64) != 0) timer1interruptEnabled = enable;
if ((value & 32) != 0) timer2interruptEnabled = enable;
if ((value & 64) != 0) {
timer1interruptEnabled = enable;
}
if ((value & 32) != 0) {
timer2interruptEnabled = enable;
}
break;
default:
}
}
// Whatever uses 6522 will want to know when it is outputting values
// So to hook that in, these abstract methods will be defined as appropriate
public abstract void sendOutputA(int value);
public abstract void sendOutputB(int value);
public abstract void sendOutputB(int value);
public int readRegister(int reg) {
Register r = Register.fromInt(reg);
// System.out.println("Reading register "+r.toString());
switch (r) {
case ORB:
if (dataDirectionB == 0x0ff) break;
if (dataDirectionB == 0x0ff) {
break;
}
return receiveOutputB() & (dataDirectionB ^ 0x0ff);
case ORA:
case ORAH:
if (dataDirectionA == 0x0ff) break;
if (dataDirectionA == 0x0ff) {
break;
}
return receiveOutputA() & (dataDirectionA ^ 0x0ff);
case DDRB:
return dataDirectionB;
@ -263,14 +292,8 @@ public abstract class R6522 extends Device {
return dataDirectionA;
case T1CL:
timer1IRQ = false;
// int out = timer1counter & 0x0ff;
// Behavior to get SkyFox to detect mockingboard -- thanks to tom@snsys.com!
timer1counter -= 8;
// return out;
return timer1counter & 0x0ff;
return timer1counter & 0x0ff;
case T1CH:
// Behavior to get SkyFox to detect mockingboard -- thanks to tom@snsys.com!
timer1counter -= 8;
return (timer1counter & 0x0ff00) >> 8;
case T1LL:
return timer1latch & 0x0ff;
@ -286,24 +309,38 @@ public abstract class R6522 extends Device {
return 0;
case ACR:
// SHIFT REGISTER NOT IMPLEMENTED
if (timer1freerun) return 64;
if (timer1freerun) {
return 64;
}
return 0;
case PCR:
break;
case IFR:
int val = 0;
if (timer1IRQ) val |= 64;
if (timer2IRQ) val |= 32;
if (val != 0) val |= 128;
if (timer1IRQ) {
val |= 64;
}
if (timer2IRQ) {
val |= 32;
}
if (val != 0) {
val |= 128;
}
return val;
case IER:
val = 128;
if (timer1interruptEnabled) val |= 64;
if (timer2interruptEnabled) val |= 32;
if (timer1interruptEnabled) {
val |= 64;
}
if (timer2interruptEnabled) {
val |= 32;
}
return val;
}
return 0;
}
public abstract int receiveOutputA();
public abstract int receiveOutputB();
}
}

View File

@ -50,7 +50,7 @@ public class TimedGenerator {
}
public void setRate(int clock, int sample_rate) {
sampleRate = sample_rate;
sampleRate = sample_rate == 0 ? 44100 : sample_rate;
this.clock = clock;
cyclesPerSample = clock / sampleRate;
}