mirror of https://github.com/JorjBauer/aiie.git
working on mockingboard support
This commit is contained in:
parent
4c5537a658
commit
e942d8a4dd
2
Makefile
2
Makefile
|
@ -4,7 +4,7 @@ CXXFLAGS=-Wall -I .. -I . -I apple -I opencv -O3
|
||||||
|
|
||||||
TSRC=cpu.cpp util/testharness.cpp
|
TSRC=cpu.cpp util/testharness.cpp
|
||||||
|
|
||||||
OPENCVOBJS=cpu.o opencv/dummy-speaker.o opencv/opencv-display.o opencv/opencv-keyboard.o opencv/opencv-paddles.o opencv/opencv-filemanager.o apple/appledisplay.o apple/applekeyboard.o apple/applemmu.o apple/applevm.o apple/diskii.o apple/nibutil.o RingBuffer.o globals.o opencv/aiie.o apple/parallelcard.o apple/fx80.o opencv/opencv-printer.o
|
OPENCVOBJS=cpu.o opencv/dummy-speaker.o opencv/opencv-display.o opencv/opencv-keyboard.o opencv/opencv-paddles.o opencv/opencv-filemanager.o apple/appledisplay.o apple/applekeyboard.o apple/applemmu.o apple/applevm.o apple/diskii.o apple/nibutil.o RingBuffer.o globals.o opencv/aiie.o apple/parallelcard.o apple/fx80.o opencv/opencv-printer.o apple/mockingboard.o apple/sy6522.o apple/ay8910.o
|
||||||
|
|
||||||
ROMS=apple/applemmu-rom.h apple/diskii-rom.h apple/parallel-rom.h
|
ROMS=apple/applemmu-rom.h apple/diskii-rom.h apple/parallel-rom.h
|
||||||
|
|
||||||
|
|
|
@ -218,6 +218,14 @@ another project lying around that directly manipulated OpenCV bitmap
|
||||||
data. It's functional, and the Mac build is only about functional
|
data. It's functional, and the Mac build is only about functional
|
||||||
testing (for me).
|
testing (for me).
|
||||||
|
|
||||||
|
Mockingboard
|
||||||
|
============
|
||||||
|
|
||||||
|
Mockingboard support is slowly taking shape, based on the schematic in
|
||||||
|
the Apple II Documentation Project:
|
||||||
|
|
||||||
|
https://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/Interface%20Cards/Audio/Sweet%20Microsystems%20Mockingboard/Schematics/Mockingboard%20Schematic.gif
|
||||||
|
|
||||||
VM
|
VM
|
||||||
==
|
==
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "applemmu-rom.h"
|
#include "applemmu-rom.h"
|
||||||
#include "physicalspeaker.h"
|
#include "physicalspeaker.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
|
#include "mockingboard.h"
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
|
||||||
|
@ -70,6 +71,11 @@ uint8_t AppleMMU::read(uint16_t address)
|
||||||
return readSwitches(address);
|
return readSwitches(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: assumes slot 4 is a mockingboard
|
||||||
|
if (slots[4] && address >= 0xC400 && address <= 0xC4FF) {
|
||||||
|
return ((Mockingboard *)slots[4])->read(address);
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t res = readPages[(address & 0xFF00) >> 8][address & 0xFF];
|
uint8_t res = readPages[(address & 0xFF00) >> 8][address & 0xFF];
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
@ -88,6 +94,12 @@ void AppleMMU::write(uint16_t address, uint8_t v)
|
||||||
return writeSwitches(address, v);
|
return writeSwitches(address, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: assumes slot4 is a mockingboard
|
||||||
|
if (slots[4] && address >= 0xC400 && address <= 0xC4FF) {
|
||||||
|
((Mockingboard *)slots[4])->write(address, v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Don't allow writes to ROM
|
// Don't allow writes to ROM
|
||||||
if (address >= 0xC100 && address <= 0xCFFF)
|
if (address >= 0xC100 && address <= 0xCFFF)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -23,6 +23,9 @@ AppleVM::AppleVM()
|
||||||
parallel = new ParallelCard();
|
parallel = new ParallelCard();
|
||||||
((AppleMMU *)mmu)->setSlot(1, parallel);
|
((AppleMMU *)mmu)->setSlot(1, parallel);
|
||||||
|
|
||||||
|
mockingboard = new Mockingboard();
|
||||||
|
((AppleMMU *)mmu)->setSlot(4, mockingboard);
|
||||||
|
|
||||||
#ifdef TEENSYDUINO
|
#ifdef TEENSYDUINO
|
||||||
teensyClock = new TeensyClock((AppleMMU *)mmu);
|
teensyClock = new TeensyClock((AppleMMU *)mmu);
|
||||||
((AppleMMU *)mmu)->setSlot(7, teensyClock);
|
((AppleMMU *)mmu)->setSlot(7, teensyClock);
|
||||||
|
@ -35,6 +38,8 @@ AppleVM::~AppleVM()
|
||||||
delete teensyClock;
|
delete teensyClock;
|
||||||
#endif
|
#endif
|
||||||
delete disk6;
|
delete disk6;
|
||||||
|
delete parallel;
|
||||||
|
delete mockingboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fixme: make member vars
|
// fixme: make member vars
|
||||||
|
@ -56,6 +61,7 @@ void AppleVM::cpuMaintenance(uint32_t cycles)
|
||||||
|
|
||||||
keyboard->maintainKeyboard(cycles);
|
keyboard->maintainKeyboard(cycles);
|
||||||
parallel->update();
|
parallel->update();
|
||||||
|
mockingboard->update(cycles);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppleVM::Reset()
|
void AppleVM::Reset()
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "diskii.h"
|
#include "diskii.h"
|
||||||
#include "vmkeyboard.h"
|
#include "vmkeyboard.h"
|
||||||
#include "parallelcard.h"
|
#include "parallelcard.h"
|
||||||
|
#include "mockingboard.h"
|
||||||
#ifdef TEENSYDUINO
|
#ifdef TEENSYDUINO
|
||||||
#include "teensy-clock.h"
|
#include "teensy-clock.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -34,6 +35,7 @@ class AppleVM : public VM {
|
||||||
DiskII *disk6;
|
DiskII *disk6;
|
||||||
VMKeyboard *keyboard;
|
VMKeyboard *keyboard;
|
||||||
ParallelCard *parallel;
|
ParallelCard *parallel;
|
||||||
|
Mockingboard *mockingboard;
|
||||||
#ifdef TEENSYDUINO
|
#ifdef TEENSYDUINO
|
||||||
TeensyClock *teensyClock;
|
TeensyClock *teensyClock;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
#include "ay8910.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
AY8910::AY8910()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AY8910::Reset()
|
||||||
|
{
|
||||||
|
printf("AY8910 reset\n");
|
||||||
|
curRegister = 0;
|
||||||
|
for (uint8_t i=0; i<16; i++)
|
||||||
|
r[i] = 0xFF;
|
||||||
|
waveformFlipTimer[0] = waveformFlipTimer[1] = waveformFlipTimer[2] = 0;
|
||||||
|
outputState[0] = outputState[1] = outputState[2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t AY8910::read(uint8_t reg)
|
||||||
|
{
|
||||||
|
// FIXME: does anything ever need to read from this?
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reg represents BC1, BDIR, /RST in bits 0, 1, 2.
|
||||||
|
// val is the state of those three bits.
|
||||||
|
// PortA is the state of whatever's currently on PortA when we do it.
|
||||||
|
void AY8910::write(uint8_t reg, uint8_t PortA)
|
||||||
|
{
|
||||||
|
// Bit 2 (1 << 2 == 0x04) is wired to the Reset pin. If it goes low,
|
||||||
|
// we reset the virtual chip.
|
||||||
|
if ((reg & 0x04) == 0) {
|
||||||
|
Reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bit 0 (1 << 0 == 0x01) is the BC1 pin. BC2 is hard-wired to +5v.
|
||||||
|
// We can ignore bit 3, b/c that was just checked above & triggered
|
||||||
|
// a reset.
|
||||||
|
reg &= ~0x04;
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case 0: // bDir==0 && BC1 == 0 (IAB)
|
||||||
|
// Puts the DA bus in high-impedance state. Nothing for us to do?
|
||||||
|
return;
|
||||||
|
case 1: // bDir==0 && BC1 == 1 (DTB)
|
||||||
|
// Contents of the currently addressed register are put in DA. FIXME?
|
||||||
|
return;
|
||||||
|
case 2: // bDir==1 && BC1 == 0 (DWS)
|
||||||
|
// Write current PortA to PSG
|
||||||
|
printf("Set register %d to %X\n", reg, PortA);
|
||||||
|
r[curRegister] = PortA;
|
||||||
|
if (curRegister <= 1) {
|
||||||
|
cycleTime[0] = cycleTimeForPSG(0);
|
||||||
|
} else if (curRegister <= 3) {
|
||||||
|
cycleTime[1] = cycleTimeForPSG(1);
|
||||||
|
} else if (curRegister <= 5) {
|
||||||
|
cycleTime[2] = cycleTimeForPSG(2);
|
||||||
|
} else if (curRegister == 7) {
|
||||||
|
cycleTime[0] = cycleTimeForPSG(0);
|
||||||
|
cycleTime[1] = cycleTimeForPSG(1);
|
||||||
|
cycleTime[2] = cycleTimeForPSG(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
case 3: // bDir==1 && BC1 == 1 (INTAK)
|
||||||
|
// Select current register
|
||||||
|
curRegister = PortA & 0xF;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The lowest frequency the AY8910 makes is 30.6 Hz, which is ~33431
|
||||||
|
// clock cycles.
|
||||||
|
//
|
||||||
|
// The highest frequency produced is 125kHz, which is ~8 cycles.
|
||||||
|
//
|
||||||
|
// The highest practicable, given our 24-cycle-main-loop, is
|
||||||
|
// 41kHz. Which should be plenty fine.
|
||||||
|
//
|
||||||
|
// Conversely: we should be able to call update() as slowly as once
|
||||||
|
// every 60-ish clock cycles before we start noticing it in the output
|
||||||
|
// audio.
|
||||||
|
uint16_t AY8910::cycleTimeForPSG(uint8_t psg)
|
||||||
|
{
|
||||||
|
// Convert the current registers in to a cycle count for how long
|
||||||
|
// between flips of 0-to-1 from the square wave generator.
|
||||||
|
|
||||||
|
uint16_t regVal = (r[1+(psg*2)] << 8) | (r[0 + (psg*2)]);
|
||||||
|
if (regVal == 0) regVal++;
|
||||||
|
|
||||||
|
// Ft = 4MHz / (32 * regVal); our clock is 1MHz
|
||||||
|
// so we should return (32 * regVal) / 4 ?
|
||||||
|
|
||||||
|
return (32 * regVal) / 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AY8910::update(uint32_t cpuCycleCount)
|
||||||
|
{
|
||||||
|
// For any waveformFlipTimer that is > 0: if cpuCycleCount is larger
|
||||||
|
// than the timer, we'll flip state. (It's a square wave!)
|
||||||
|
|
||||||
|
for (uint8_t i=0; i<3; i++) {
|
||||||
|
uint32_t cc = cycleTime[i];
|
||||||
|
|
||||||
|
if (cc == 0) {
|
||||||
|
waveformFlipTimer[i] = 0;
|
||||||
|
} else {
|
||||||
|
if (!waveformFlipTimer[i]) {
|
||||||
|
// start a cycle, if necessary
|
||||||
|
waveformFlipTimer[i] = cpuCycleCount + cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waveformFlipTimer[i] && waveformFlipTimer[i] <= cpuCycleCount) {
|
||||||
|
// flip when it's time to flip
|
||||||
|
waveformFlipTimer[i] += cc;
|
||||||
|
outputState[i] = !outputState[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If any of the square waves is on, then we want to be on.
|
||||||
|
|
||||||
|
// r[i+8] is the amplitude control.
|
||||||
|
// FIXME: if r[i+8] & 0x10, then it's an envelope-specific amplitude
|
||||||
|
g_speaker->mixOutput(outputState[i] ? (r[i+8] & 0x0F) : 0x00);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef __AY8910_H
|
||||||
|
#define __AY8910_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
class AY8910 {
|
||||||
|
public:
|
||||||
|
AY8910();
|
||||||
|
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
uint8_t read(uint8_t reg);
|
||||||
|
void write(uint8_t reg, uint8_t PortA);
|
||||||
|
|
||||||
|
void update(uint32_t cpuCycleCount);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint16_t cycleTimeForPSG(uint8_t psg);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t curRegister;
|
||||||
|
uint8_t r[16];
|
||||||
|
uint32_t waveformFlipTimer[3];
|
||||||
|
uint8_t outputState[3];
|
||||||
|
uint16_t cycleTime[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -411,12 +411,12 @@ const char *DiskII::DiskName(int8_t num)
|
||||||
void DiskII::loadROM(uint8_t *toWhere)
|
void DiskII::loadROM(uint8_t *toWhere)
|
||||||
{
|
{
|
||||||
#ifdef TEENSYDUINO
|
#ifdef TEENSYDUINO
|
||||||
Serial.println("loading slot rom");
|
Serial.println("loading DiskII rom");
|
||||||
for (uint16_t i=0; i<=0xFF; i++) {
|
for (uint16_t i=0; i<=0xFF; i++) {
|
||||||
toWhere[i] = pgm_read_byte(&romData[i]);
|
toWhere[i] = pgm_read_byte(&romData[i]);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
printf("loading slot rom\n");
|
printf("loading DiskII rom\n");
|
||||||
memcpy(toWhere, romData, 256);
|
memcpy(toWhere, romData, 256);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
#include "mockingboard.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
Mockingboard::Mockingboard()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Mockingboard::~Mockingboard()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mockingboard::Reset()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Mockingboard::readSwitches(uint8_t s)
|
||||||
|
{
|
||||||
|
// There are never any reads to the I/O switches
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mockingboard::writeSwitches(uint8_t s, uint8_t v)
|
||||||
|
{
|
||||||
|
// There are never any writes to the I/O switches
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mockingboard::loadROM(uint8_t *toWhere)
|
||||||
|
{
|
||||||
|
// We don't need a ROM; we're going to work via direct interaction
|
||||||
|
// with memory 0xC400 - 0xC4FF
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Mockingboard::read(uint16_t address)
|
||||||
|
{
|
||||||
|
address &= 0xFF;
|
||||||
|
if ( (address >= 0x00 &&
|
||||||
|
address <= 0x0F) ||
|
||||||
|
(address >= 0x80 &&
|
||||||
|
address <= 0x8F) ) {
|
||||||
|
uint8_t idx = (address & 0x80 ? 1 : 0);
|
||||||
|
if (idx == 0) { // FIXME: just debugging; remove this 'if'
|
||||||
|
return sy6522[idx].read(address & 0x0F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mockingboard::write(uint16_t address, uint8_t val)
|
||||||
|
{
|
||||||
|
address &= 0xFF;
|
||||||
|
if ( (address >= 0x00 &&
|
||||||
|
address <= 0x0F) ||
|
||||||
|
(address >= 0x80 &&
|
||||||
|
address <= 0x8F) ) {
|
||||||
|
uint8_t idx = (address & 0x80 ? 1 : 0);
|
||||||
|
if (idx == 0) { // FIXME: just debugging; remove this 'if'
|
||||||
|
return sy6522[idx].write(address & 0x0F, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mockingboard::update(uint32_t cycles)
|
||||||
|
{
|
||||||
|
sy6522[0].update(cycles);
|
||||||
|
// debugging: disabled the second update for the moment
|
||||||
|
// sy6522[1].update(cycles);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef __MOCKINGBOARD_H
|
||||||
|
#define __MOCKINGBOARD_H
|
||||||
|
|
||||||
|
#ifdef TEENSYDUINO
|
||||||
|
#include <Arduino.h>
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "applemmu.h"
|
||||||
|
#include "slot.h"
|
||||||
|
#include "SY6522.h"
|
||||||
|
|
||||||
|
class Mockingboard : public Slot {
|
||||||
|
public:
|
||||||
|
Mockingboard();
|
||||||
|
virtual ~Mockingboard();
|
||||||
|
|
||||||
|
virtual void Reset(); // used by BIOS cold-boot
|
||||||
|
virtual uint8_t readSwitches(uint8_t s);
|
||||||
|
virtual void writeSwitches(uint8_t s, uint8_t v);
|
||||||
|
virtual void loadROM(uint8_t *toWhere);
|
||||||
|
|
||||||
|
void update(uint32_t cycles);
|
||||||
|
|
||||||
|
uint8_t read(uint16_t address);
|
||||||
|
void write(uint16_t address, uint8_t val);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SY6522 sy6522[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,151 @@
|
||||||
|
#include "sy6522.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
SY6522::SY6522()
|
||||||
|
{
|
||||||
|
ORB = ORA = 0;
|
||||||
|
DDRB = DDRA = 0x00;
|
||||||
|
T1_CTR = T2_CTR = 0;
|
||||||
|
T1_CTR_LATCH = T2_CTR_LATCH = 0;
|
||||||
|
ACR = 0x20; // free-running; FIXME: constant?
|
||||||
|
PCR = 0xB0; // FIXME: ?
|
||||||
|
IFR = 0x00; // FIXME: ?
|
||||||
|
IER = 0x90; // FIXME: ?
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SY6522::read(uint8_t address)
|
||||||
|
{
|
||||||
|
switch (address) {
|
||||||
|
case SY_ORB:
|
||||||
|
return ORB;
|
||||||
|
case SY_ORA:
|
||||||
|
return ORA;
|
||||||
|
case SY_DDRB:
|
||||||
|
return DDRB;
|
||||||
|
case SY_DDRA:
|
||||||
|
return DDRA;
|
||||||
|
case SY_TMR1L:
|
||||||
|
// FIXME: also updates IFR?
|
||||||
|
return (T1_CTR & 0xFF);
|
||||||
|
case SY_TMR1H:
|
||||||
|
return (T1_CTR >> 8);
|
||||||
|
case SY_TMR1LL:
|
||||||
|
return (T1_CTR_LATCH & 0xFF);
|
||||||
|
case SY_TMR1HL:
|
||||||
|
return (T1_CTR_LATCH >> 8);
|
||||||
|
case SY_TMR2L:
|
||||||
|
// FIXME: alos udpates IFR?
|
||||||
|
return (T2_CTR & 0xFF);
|
||||||
|
case SY_TMR2H:
|
||||||
|
return (T2_CTR >> 8);
|
||||||
|
case SY_SS:
|
||||||
|
// FIXME: floating
|
||||||
|
return 0xFF;
|
||||||
|
case SY_ACR:
|
||||||
|
return ACR;
|
||||||
|
case SY_PCR:
|
||||||
|
return PCR;
|
||||||
|
case SY_IFR:
|
||||||
|
return IFR;
|
||||||
|
case SY_IER:
|
||||||
|
return 0x80 | IER;
|
||||||
|
case SY_ORANOHS:
|
||||||
|
return ORA;
|
||||||
|
}
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SY6522::write(uint8_t address, uint8_t val)
|
||||||
|
{
|
||||||
|
printf("SY6522: %X = %02X\n", address, val);
|
||||||
|
switch (address) {
|
||||||
|
case SY_ORB:
|
||||||
|
val &= DDRB;
|
||||||
|
ORB = val;
|
||||||
|
ay8910[0].write(val, ORA & DDRA);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SY_ORA:
|
||||||
|
ORA = val & DDRA;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SY_DDRB:
|
||||||
|
DDRB = val;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SY_DDRA:
|
||||||
|
DDRA = val;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SY_TMR1L:
|
||||||
|
case SY_TMR1LL:
|
||||||
|
T1_CTR_LATCH = (T1_CTR_LATCH & 0xFF00) | val;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SY_TMR1H:
|
||||||
|
// FIXME: clear interrupt flag
|
||||||
|
T1_CTR_LATCH = (T1_CTR_LATCH & 0x00FF) | (val << 8);
|
||||||
|
T1_CTR = T1_CTR_LATCH;
|
||||||
|
// FIXME: start timer?
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SY_TMR1HL:
|
||||||
|
T1_CTR_LATCH = (T1_CTR_LATCH & 0x00FF) | (val << 8);
|
||||||
|
// FIXME: clear interrupt flag
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SY_TMR2L:
|
||||||
|
T2_CTR_LATCH = (T2_CTR_LATCH & 0xFF00) | val;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SY_TMR2H:
|
||||||
|
// FIXME: clear timer2 interrupt flag
|
||||||
|
T2_CTR_LATCH = (T2_CTR_LATCH & 0x00FF) | (val << 8);
|
||||||
|
T2_CTR = T2_CTR_LATCH;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SY_SS:
|
||||||
|
// FIXME: what is this for?
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SY_ACR:
|
||||||
|
ACR = val;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SY_PCR:
|
||||||
|
PCR = val;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SY_IFR:
|
||||||
|
// Clear whatever low bits are set in IFR.
|
||||||
|
val |= 0x80;
|
||||||
|
val ^= 0x7F;
|
||||||
|
IFR &= val;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SY_IER:
|
||||||
|
if (val & 0x80) {
|
||||||
|
// Set bits based on val
|
||||||
|
val &= 0x7F;
|
||||||
|
IER |= val;
|
||||||
|
// FIXME: start timer if necessary?
|
||||||
|
} else {
|
||||||
|
// Clear whatever low bits are set in IER.
|
||||||
|
val |= 0x80;
|
||||||
|
val ^= 0x7F;
|
||||||
|
IER &= val;
|
||||||
|
// FIXME: stop timer if it's running?
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SY_ORANOHS:
|
||||||
|
// FIXME: what is this for?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SY6522::update(uint32_t cycles)
|
||||||
|
{
|
||||||
|
ay8910[0].update(cycles);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
#ifndef __SY6522_H
|
||||||
|
#define __SY6522_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "ay8910.h"
|
||||||
|
|
||||||
|
// 6522 interface registers
|
||||||
|
enum {
|
||||||
|
SY_ORB = 0x00, // ORB
|
||||||
|
SY_ORA = 0x01, // ORA
|
||||||
|
SY_DDRB = 0x02, // DDRB
|
||||||
|
SY_DDRA = 0x03, // DDRA
|
||||||
|
SY_TMR1L = 0x04, // TIMER1L_COUNTER
|
||||||
|
SY_TMR1H = 0x05, // TIMER1H_COUNTER
|
||||||
|
SY_TMR1LL = 0x06, // TIMER1L_LATCH
|
||||||
|
SY_TMR1HL = 0x07, // TIMER1H_LATCH
|
||||||
|
SY_TMR2L = 0x08, // TIMER2L
|
||||||
|
SY_TMR2H = 0x09, // TIMER2H
|
||||||
|
SY_SS = 0x0a, // SERIAL_SHIFT
|
||||||
|
SY_ACR = 0x0b, // ACR
|
||||||
|
SY_PCR = 0x0c, // PCR
|
||||||
|
SY_IFR = 0x0d, // IFR
|
||||||
|
SY_IER = 0x0e, // IER
|
||||||
|
SY_ORANOHS = 0x0f // ORA_NO_HS
|
||||||
|
};
|
||||||
|
|
||||||
|
class SY6522 {
|
||||||
|
public:
|
||||||
|
SY6522();
|
||||||
|
|
||||||
|
uint8_t read(uint8_t address);
|
||||||
|
void write(uint8_t address, uint8_t val);
|
||||||
|
|
||||||
|
void update(uint32_t cycles);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t ORB; // port B
|
||||||
|
uint8_t ORA; // port A
|
||||||
|
uint8_t DDRB; // data direction register
|
||||||
|
uint8_t DDRA; //
|
||||||
|
uint16_t T1_CTR; // counters
|
||||||
|
uint16_t T1_CTR_LATCH;
|
||||||
|
uint16_t T2_CTR;
|
||||||
|
uint16_t T2_CTR_LATCH;
|
||||||
|
uint8_t ACR; // Aux Control Register
|
||||||
|
uint8_t PCR; // Peripheral Control Register
|
||||||
|
uint8_t IFR; // Interrupt Flag Register
|
||||||
|
uint8_t IER; // Interrupt Enable Register
|
||||||
|
|
||||||
|
AY8910 ay8910[1]; // FIXME: an array in case we support more than one ... ?
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
129
opencv/aiie.cpp
129
opencv/aiie.cpp
|
@ -4,8 +4,6 @@
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
#include <mach/mach_time.h>
|
|
||||||
// Derived from http://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x
|
|
||||||
#include "applevm.h"
|
#include "applevm.h"
|
||||||
#include "opencv-display.h"
|
#include "opencv-display.h"
|
||||||
#include "opencv-keyboard.h"
|
#include "opencv-keyboard.h"
|
||||||
|
@ -16,39 +14,17 @@
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
|
||||||
|
#include "timeutil.h"
|
||||||
|
|
||||||
//#define SHOWFPS
|
//#define SHOWFPS
|
||||||
//#define SHOWPC
|
//#define SHOWPC
|
||||||
//#define DEBUGCPU
|
//#define DEBUGCPU
|
||||||
//#define SHOWMEMPAGE
|
//#define SHOWMEMPAGE
|
||||||
|
|
||||||
#define ORWL_NANO (+1.0E-9)
|
static struct timespec nextInstructionTime, startTime;
|
||||||
#define ORWL_GIGA UINT64_C(1000000000)
|
|
||||||
#define NANOSECONDS_PER_SECOND 1000000000UL
|
|
||||||
#define CYCLES_PER_SECOND 1023000UL
|
|
||||||
#define NANOSECONDS_PER_CYCLE (NANOSECONDS_PER_SECOND / CYCLES_PER_SECOND)
|
|
||||||
|
|
||||||
struct timespec nextInstructionTime, startTime;
|
|
||||||
uint64_t hitcount = 0;
|
uint64_t hitcount = 0;
|
||||||
uint64_t misscount = 0;
|
uint64_t misscount = 0;
|
||||||
|
|
||||||
static double orwl_timebase = 0.0;
|
|
||||||
static uint64_t orwl_timestart = 0;
|
|
||||||
static void _init_darwin_shim(void) {
|
|
||||||
mach_timebase_info_data_t tb = { 0 };
|
|
||||||
mach_timebase_info(&tb);
|
|
||||||
orwl_timebase = tb.numer;
|
|
||||||
orwl_timebase /= tb.denom;
|
|
||||||
orwl_timestart = mach_absolute_time();
|
|
||||||
}
|
|
||||||
|
|
||||||
int do_gettime(struct timespec *tp) {
|
|
||||||
double diff = (mach_absolute_time() - orwl_timestart) * orwl_timebase;
|
|
||||||
tp->tv_sec = diff * ORWL_NANO;
|
|
||||||
tp->tv_nsec = diff - (tp->tv_sec * ORWL_GIGA);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define NB_ENABLE 1
|
#define NB_ENABLE 1
|
||||||
#define NB_DISABLE 0
|
#define NB_DISABLE 0
|
||||||
|
|
||||||
|
@ -96,102 +72,6 @@ void write(void *arg, uint16_t address, uint8_t v)
|
||||||
// no action; this is a dummy function until we've finished initializing...
|
// no action; this is a dummy function until we've finished initializing...
|
||||||
}
|
}
|
||||||
|
|
||||||
// adds the number of microseconds that 'cycles' takes to *start and
|
|
||||||
// returns it in *out
|
|
||||||
void timespec_add_cycles(struct timespec *start,
|
|
||||||
uint32_t cycles,
|
|
||||||
struct timespec *out)
|
|
||||||
{
|
|
||||||
out->tv_sec = start->tv_sec;
|
|
||||||
out->tv_nsec = start->tv_nsec;
|
|
||||||
|
|
||||||
uint64_t nanosToAdd = NANOSECONDS_PER_CYCLE * cycles;
|
|
||||||
out->tv_sec += (nanosToAdd / NANOSECONDS_PER_SECOND);
|
|
||||||
out->tv_nsec += (nanosToAdd % NANOSECONDS_PER_SECOND);
|
|
||||||
|
|
||||||
if (out->tv_nsec >= 1000000000L) {
|
|
||||||
out->tv_sec++ ;
|
|
||||||
out->tv_nsec -= 1000000000L;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void timespec_diff(struct timespec *start,
|
|
||||||
struct timespec *end,
|
|
||||||
struct timespec *diff,
|
|
||||||
bool *negative) {
|
|
||||||
struct timespec t;
|
|
||||||
|
|
||||||
if (negative)
|
|
||||||
{
|
|
||||||
*negative = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if start > end, swizzle...
|
|
||||||
if ( (start->tv_sec > end->tv_sec) || ((start->tv_sec == end->tv_sec) && (start->tv_nsec > end->tv_nsec)) )
|
|
||||||
{
|
|
||||||
t=*start;
|
|
||||||
*start=*end;
|
|
||||||
*end=t;
|
|
||||||
if (negative)
|
|
||||||
{
|
|
||||||
*negative = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// assuming time_t is signed ...
|
|
||||||
if (end->tv_nsec < start->tv_nsec)
|
|
||||||
{
|
|
||||||
t.tv_sec = end->tv_sec - start->tv_sec - 1;
|
|
||||||
t.tv_nsec = 1000000000 + end->tv_nsec - start->tv_nsec;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
t.tv_sec = end->tv_sec - start->tv_sec;
|
|
||||||
t.tv_nsec = end->tv_nsec - start->tv_nsec;
|
|
||||||
}
|
|
||||||
|
|
||||||
diff->tv_sec = t.tv_sec;
|
|
||||||
diff->tv_nsec = t.tv_nsec;
|
|
||||||
}
|
|
||||||
|
|
||||||
// tsCompare: return -1, 0, 1 for (a < b), (a == b), (a > b)
|
|
||||||
int8_t tsCompare(struct timespec *A, struct timespec *B)
|
|
||||||
{
|
|
||||||
if (A->tv_sec < B->tv_sec)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (A->tv_sec > B->tv_sec)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (A->tv_nsec < B->tv_nsec)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (A->tv_nsec > B->tv_nsec)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct timespec tsSubtract(struct timespec time1, struct timespec time2)
|
|
||||||
{
|
|
||||||
struct timespec result;
|
|
||||||
if ((time1.tv_sec < time2.tv_sec) ||
|
|
||||||
((time1.tv_sec == time2.tv_sec) &&
|
|
||||||
(time1.tv_nsec <= time2.tv_nsec))) {/* TIME1 <= TIME2? */
|
|
||||||
result.tv_sec = result.tv_nsec = 0 ;
|
|
||||||
} else {/* TIME1 > TIME2 */
|
|
||||||
result.tv_sec = time1.tv_sec - time2.tv_sec ;
|
|
||||||
if (time1.tv_nsec < time2.tv_nsec) {
|
|
||||||
result.tv_nsec = time1.tv_nsec + 1000000000L - time2.tv_nsec ;
|
|
||||||
result.tv_sec-- ;/* Borrow a second. */
|
|
||||||
} else {
|
|
||||||
result.tv_nsec = time1.tv_nsec - time2.tv_nsec ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (result) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *cpu_thread(void *dummyptr) {
|
static void *cpu_thread(void *dummyptr) {
|
||||||
struct timespec currentTime;
|
struct timespec currentTime;
|
||||||
|
|
||||||
|
@ -226,10 +106,15 @@ static void *cpu_thread(void *dummyptr) {
|
||||||
#endif
|
#endif
|
||||||
timespec_add_cycles(&startTime, g_cpu->cycles + executed, &nextInstructionTime);
|
timespec_add_cycles(&startTime, g_cpu->cycles + executed, &nextInstructionTime);
|
||||||
|
|
||||||
|
g_speaker->beginMixing();
|
||||||
|
|
||||||
// The paddles need to be triggered in real-time on the CPU
|
// The paddles need to be triggered in real-time on the CPU
|
||||||
// clock. That happens from the VM's CPU maintenance poller.
|
// clock. That happens from the VM's CPU maintenance poller.
|
||||||
((AppleVM *)g_vm)->cpuMaintenance(g_cpu->cycles);
|
((AppleVM *)g_vm)->cpuMaintenance(g_cpu->cycles);
|
||||||
|
|
||||||
|
// cpuMaintenance also maintained the sound card; update the speaker after
|
||||||
|
g_speaker->maintainSpeaker(g_cpu->cycles);
|
||||||
|
|
||||||
#ifdef DEBUGCPU
|
#ifdef DEBUGCPU
|
||||||
{
|
{
|
||||||
uint8_t p = g_cpu->flags;
|
uint8_t p = g_cpu->flags;
|
||||||
|
|
|
@ -1,13 +1,99 @@
|
||||||
#include "dummy-speaker.h"
|
#include "dummy-speaker.h"
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "timeutil.h"
|
||||||
|
|
||||||
|
// FIXME: Globals; ick.
|
||||||
|
static pthread_t speakerThreadID;
|
||||||
|
static uint8_t curSpeakerData = 0x00;
|
||||||
|
|
||||||
|
static uint64_t hitcount;
|
||||||
|
static uint64_t misscount;
|
||||||
|
|
||||||
|
static void *speaker_thread(void *dummyptr) {
|
||||||
|
struct timespec currentTime;
|
||||||
|
struct timespec startTime;
|
||||||
|
struct timespec nextSampleTime;
|
||||||
|
|
||||||
|
_init_darwin_shim();
|
||||||
|
do_gettime(&startTime);
|
||||||
|
do_gettime(&nextSampleTime);
|
||||||
|
|
||||||
|
FILE *f = popen("play -q -t raw -b 8 -e unsigned-integer -r 8000 -", "w");
|
||||||
|
|
||||||
|
uint64_t sampleCount = 0;
|
||||||
|
while (1) {
|
||||||
|
do_gettime(¤tTime);
|
||||||
|
struct timespec diff = tsSubtract(nextSampleTime, currentTime);
|
||||||
|
if (diff.tv_sec >= 0 && diff.tv_nsec >= 0) {
|
||||||
|
nanosleep(&diff, NULL);
|
||||||
|
hitcount++;
|
||||||
|
} else
|
||||||
|
misscount++;
|
||||||
|
|
||||||
|
if ((sampleCount & 0xFFFF) == 0) {
|
||||||
|
printf("sound hit: %lld miss: %lld\n", hitcount, misscount);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputc(curSpeakerData & 0xFF, f); fflush(f);
|
||||||
|
nextSampleTime = startTime;
|
||||||
|
timespec_add_ms(&startTime, sampleCount * 1000 / 8000, &nextSampleTime);
|
||||||
|
sampleCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DummySpeaker::DummySpeaker()
|
||||||
|
{
|
||||||
|
mixerValue = 0;
|
||||||
|
_init_darwin_shim(); // set up the clock interface
|
||||||
|
|
||||||
|
if (!pthread_create(&speakerThreadID, NULL, &speaker_thread, (void *)NULL)) {
|
||||||
|
printf("speaker thread created\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DummySpeaker::~DummySpeaker()
|
DummySpeaker::~DummySpeaker()
|
||||||
{
|
{
|
||||||
|
pclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DummySpeaker::toggleAtCycle(uint32_t c)
|
void DummySpeaker::toggleAtCycle(uint32_t c)
|
||||||
{
|
{
|
||||||
|
nextTransitionAt = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DummySpeaker::maintainSpeaker(uint32_t c)
|
void DummySpeaker::maintainSpeaker(uint32_t c)
|
||||||
{
|
{
|
||||||
|
/* if (nextTransitionAt && c >= nextTransitionAt) {
|
||||||
|
// Override the mixer with a 1-bit "Terribad" audio sample change
|
||||||
|
mixerValue = speakerState ? 0x00 : (0xFF<<3); // <<3 b/c of the >>=3 below
|
||||||
|
nextTransitionAt = 0;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if (numMixed) {
|
||||||
|
mixerValue /= numMixed;
|
||||||
|
}
|
||||||
|
speakerState = mixerValue;
|
||||||
|
|
||||||
|
// FIXME: duplication of above? using a global? fix fix fix.
|
||||||
|
curSpeakerData = mixerValue & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DummySpeaker::currentState()
|
||||||
|
{
|
||||||
|
return speakerState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DummySpeaker::beginMixing()
|
||||||
|
{
|
||||||
|
mixerValue = 0;
|
||||||
|
numMixed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DummySpeaker::mixOutput(uint8_t v)
|
||||||
|
{
|
||||||
|
mixerValue += v;
|
||||||
|
numMixed++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,28 @@
|
||||||
#ifndef __DUMMYSPEAKER_H
|
#ifndef __DUMMYSPEAKER_H
|
||||||
#define __DUMMYSPEAKER_H
|
#define __DUMMYSPEAKER_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "physicalspeaker.h"
|
#include "physicalspeaker.h"
|
||||||
|
|
||||||
class DummySpeaker : public PhysicalSpeaker {
|
class DummySpeaker : public PhysicalSpeaker {
|
||||||
public:
|
public:
|
||||||
|
DummySpeaker();
|
||||||
virtual ~DummySpeaker();
|
virtual ~DummySpeaker();
|
||||||
|
|
||||||
virtual void toggleAtCycle(uint32_t c);
|
virtual void toggleAtCycle(uint32_t c);
|
||||||
virtual void maintainSpeaker(uint32_t c);
|
virtual void maintainSpeaker(uint32_t c);
|
||||||
|
virtual bool currentState();
|
||||||
|
virtual void beginMixing();
|
||||||
|
virtual void mixOutput(uint8_t v);
|
||||||
|
private:
|
||||||
|
bool speakerState;
|
||||||
|
|
||||||
|
uint32_t mixerValue;
|
||||||
|
uint8_t numMixed;
|
||||||
|
uint32_t nextTransitionAt;
|
||||||
|
|
||||||
|
FILE *f;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
#include <time.h>
|
||||||
|
#include <mach/mach_time.h>
|
||||||
|
// Derived from
|
||||||
|
// http://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x
|
||||||
|
|
||||||
|
#define ORWL_NANO (+1.0E-9)
|
||||||
|
#define ORWL_GIGA UINT64_C(1000000000)
|
||||||
|
#define NANOSECONDS_PER_SECOND 1000000000UL
|
||||||
|
#define CYCLES_PER_SECOND 1023000UL
|
||||||
|
#define NANOSECONDS_PER_CYCLE (NANOSECONDS_PER_SECOND / CYCLES_PER_SECOND)
|
||||||
|
|
||||||
|
static double orwl_timebase = 0.0;
|
||||||
|
static uint64_t orwl_timestart = 0;
|
||||||
|
static void _init_darwin_shim(void) {
|
||||||
|
mach_timebase_info_data_t tb = { 0 };
|
||||||
|
mach_timebase_info(&tb);
|
||||||
|
orwl_timebase = tb.numer;
|
||||||
|
orwl_timebase /= tb.denom;
|
||||||
|
orwl_timestart = mach_absolute_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_gettime(struct timespec *tp) {
|
||||||
|
double diff = (mach_absolute_time() - orwl_timestart) * orwl_timebase;
|
||||||
|
tp->tv_sec = diff * ORWL_NANO;
|
||||||
|
tp->tv_nsec = diff - (tp->tv_sec * ORWL_GIGA);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// adds the number of microseconds that 'cycles' takes to *start and
|
||||||
|
// returns it in *out
|
||||||
|
static void timespec_add_cycles(struct timespec *start,
|
||||||
|
uint32_t cycles,
|
||||||
|
struct timespec *out)
|
||||||
|
{
|
||||||
|
out->tv_sec = start->tv_sec;
|
||||||
|
out->tv_nsec = start->tv_nsec;
|
||||||
|
|
||||||
|
uint64_t nanosToAdd = NANOSECONDS_PER_CYCLE * cycles;
|
||||||
|
out->tv_sec += (nanosToAdd / NANOSECONDS_PER_SECOND);
|
||||||
|
out->tv_nsec += (nanosToAdd % NANOSECONDS_PER_SECOND);
|
||||||
|
|
||||||
|
if (out->tv_nsec >= 1000000000L) {
|
||||||
|
out->tv_sec++ ;
|
||||||
|
out->tv_nsec -= 1000000000L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adds the number of microseconds given to *start and
|
||||||
|
// returns it in *out
|
||||||
|
static void timespec_add_ms(struct timespec *start,
|
||||||
|
uint64_t micros,
|
||||||
|
struct timespec *out)
|
||||||
|
{
|
||||||
|
out->tv_sec = start->tv_sec;
|
||||||
|
out->tv_nsec = start->tv_nsec;
|
||||||
|
|
||||||
|
uint64_t nanosToAdd = micros * 1000000L;
|
||||||
|
out->tv_sec += (nanosToAdd / NANOSECONDS_PER_SECOND);
|
||||||
|
out->tv_nsec += (nanosToAdd % NANOSECONDS_PER_SECOND);
|
||||||
|
|
||||||
|
if (out->tv_nsec >= 1000000000L) {
|
||||||
|
out->tv_sec++ ;
|
||||||
|
out->tv_nsec -= 1000000000L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void timespec_diff(struct timespec *start,
|
||||||
|
struct timespec *end,
|
||||||
|
struct timespec *diff,
|
||||||
|
bool *negative) {
|
||||||
|
struct timespec t;
|
||||||
|
|
||||||
|
if (negative)
|
||||||
|
{
|
||||||
|
*negative = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if start > end, swizzle...
|
||||||
|
if ( (start->tv_sec > end->tv_sec) || ((start->tv_sec == end->tv_sec) && (start->tv_nsec > end->tv_nsec)) )
|
||||||
|
{
|
||||||
|
t=*start;
|
||||||
|
*start=*end;
|
||||||
|
*end=t;
|
||||||
|
if (negative)
|
||||||
|
{
|
||||||
|
*negative = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assuming time_t is signed ...
|
||||||
|
if (end->tv_nsec < start->tv_nsec)
|
||||||
|
{
|
||||||
|
t.tv_sec = end->tv_sec - start->tv_sec - 1;
|
||||||
|
t.tv_nsec = 1000000000 + end->tv_nsec - start->tv_nsec;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t.tv_sec = end->tv_sec - start->tv_sec;
|
||||||
|
t.tv_nsec = end->tv_nsec - start->tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
diff->tv_sec = t.tv_sec;
|
||||||
|
diff->tv_nsec = t.tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tsCompare: return -1, 0, 1 for (a < b), (a == b), (a > b)
|
||||||
|
static int8_t tsCompare(struct timespec *A, struct timespec *B)
|
||||||
|
{
|
||||||
|
if (A->tv_sec < B->tv_sec)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (A->tv_sec > B->tv_sec)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (A->tv_nsec < B->tv_nsec)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (A->tv_nsec > B->tv_nsec)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct timespec tsSubtract(struct timespec time1, struct timespec time2)
|
||||||
|
{
|
||||||
|
struct timespec result;
|
||||||
|
if ((time1.tv_sec < time2.tv_sec) ||
|
||||||
|
((time1.tv_sec == time2.tv_sec) &&
|
||||||
|
(time1.tv_nsec <= time2.tv_nsec))) {/* TIME1 <= TIME2? */
|
||||||
|
result.tv_sec = result.tv_nsec = 0 ;
|
||||||
|
} else {/* TIME1 > TIME2 */
|
||||||
|
result.tv_sec = time1.tv_sec - time2.tv_sec ;
|
||||||
|
if (time1.tv_nsec < time2.tv_nsec) {
|
||||||
|
result.tv_nsec = time1.tv_nsec + 1000000000L - time2.tv_nsec ;
|
||||||
|
result.tv_sec-- ;/* Borrow a second. */
|
||||||
|
} else {
|
||||||
|
result.tv_nsec = time1.tv_nsec - time2.tv_nsec ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result) ;
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,9 @@ class PhysicalSpeaker {
|
||||||
|
|
||||||
virtual void toggleAtCycle(uint32_t c) = 0;
|
virtual void toggleAtCycle(uint32_t c) = 0;
|
||||||
virtual void maintainSpeaker(uint32_t c) = 0;
|
virtual void maintainSpeaker(uint32_t c) = 0;
|
||||||
|
virtual void beginMixing() = 0;
|
||||||
|
virtual void mixOutput(uint8_t v) = 0;
|
||||||
|
virtual bool currentState() = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
../apple/slot.cpp
|
|
|
@ -31,3 +31,8 @@ void TeensySpeaker::maintainSpeaker(uint32_t c)
|
||||||
analogWriteDAC0(speakerState ? g_volume : 0); // max: 4095
|
analogWriteDAC0(speakerState ? g_volume : 0); // max: 4095
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TeensySpeaker::currentState()
|
||||||
|
{
|
||||||
|
return speakerState;
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ class TeensySpeaker : public PhysicalSpeaker {
|
||||||
|
|
||||||
virtual void toggleAtCycle(uint32_t c);
|
virtual void toggleAtCycle(uint32_t c);
|
||||||
virtual void maintainSpeaker(uint32_t c);
|
virtual void maintainSpeaker(uint32_t c);
|
||||||
|
virtual bool currentState();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool speakerState;
|
bool speakerState;
|
||||||
|
|
|
@ -219,8 +219,9 @@ void runCPU()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
g_speaker->maintainSpeaker(g_cpu->cycles);
|
g_speaker->beginMixing();
|
||||||
((AppleVM *)g_vm)->cpuMaintenance(g_cpu->cycles);
|
((AppleVM *)g_vm)->cpuMaintenance(g_cpu->cycles);
|
||||||
|
g_speaker->maintainSpeaker(g_cpu->cycles);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: move these memory-related functions elsewhere...
|
// FIXME: move these memory-related functions elsewhere...
|
||||||
|
|
Loading…
Reference in New Issue