working on mockingboard support

This commit is contained in:
Jorj Bauer 2017-02-24 10:15:17 -05:00
parent 4c5537a658
commit e942d8a4dd
21 changed files with 756 additions and 127 deletions

View File

@ -4,7 +4,7 @@ CXXFLAGS=-Wall -I .. -I . -I apple -I opencv -O3
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

View File

@ -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
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
==

View File

@ -10,6 +10,7 @@
#include "applemmu-rom.h"
#include "physicalspeaker.h"
#include "cpu.h"
#include "mockingboard.h"
#include "globals.h"
@ -69,6 +70,11 @@ uint8_t AppleMMU::read(uint16_t address)
address <= 0xC0FF) {
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];
@ -88,6 +94,12 @@ void AppleMMU::write(uint16_t address, uint8_t 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
if (address >= 0xC100 && address <= 0xCFFF)
return;

View File

@ -23,6 +23,9 @@ AppleVM::AppleVM()
parallel = new ParallelCard();
((AppleMMU *)mmu)->setSlot(1, parallel);
mockingboard = new Mockingboard();
((AppleMMU *)mmu)->setSlot(4, mockingboard);
#ifdef TEENSYDUINO
teensyClock = new TeensyClock((AppleMMU *)mmu);
((AppleMMU *)mmu)->setSlot(7, teensyClock);
@ -35,6 +38,8 @@ AppleVM::~AppleVM()
delete teensyClock;
#endif
delete disk6;
delete parallel;
delete mockingboard;
}
// fixme: make member vars
@ -56,6 +61,7 @@ void AppleVM::cpuMaintenance(uint32_t cycles)
keyboard->maintainKeyboard(cycles);
parallel->update();
mockingboard->update(cycles);
}
void AppleVM::Reset()

View File

@ -6,6 +6,7 @@
#include "diskii.h"
#include "vmkeyboard.h"
#include "parallelcard.h"
#include "mockingboard.h"
#ifdef TEENSYDUINO
#include "teensy-clock.h"
#endif
@ -34,6 +35,7 @@ class AppleVM : public VM {
DiskII *disk6;
VMKeyboard *keyboard;
ParallelCard *parallel;
Mockingboard *mockingboard;
#ifdef TEENSYDUINO
TeensyClock *teensyClock;
#endif

129
apple/ay8910.cpp Normal file
View File

@ -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);
}
}

28
apple/ay8910.h Normal file
View File

@ -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

View File

@ -411,12 +411,12 @@ const char *DiskII::DiskName(int8_t num)
void DiskII::loadROM(uint8_t *toWhere)
{
#ifdef TEENSYDUINO
Serial.println("loading slot rom");
Serial.println("loading DiskII rom");
for (uint16_t i=0; i<=0xFF; i++) {
toWhere[i] = pgm_read_byte(&romData[i]);
}
#else
printf("loading slot rom\n");
printf("loading DiskII rom\n");
memcpy(toWhere, romData, 256);
#endif
}

69
apple/mockingboard.cpp Normal file
View File

@ -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);
}

34
apple/mockingboard.h Normal file
View File

@ -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

151
apple/sy6522.cpp Normal file
View File

@ -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);
}

54
apple/sy6522.h Normal file
View File

@ -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

View File

@ -4,8 +4,6 @@
#include <termios.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 "opencv-display.h"
#include "opencv-keyboard.h"
@ -16,39 +14,17 @@
#include "globals.h"
#include "timeutil.h"
//#define SHOWFPS
//#define SHOWPC
//#define DEBUGCPU
//#define SHOWMEMPAGE
#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)
struct timespec nextInstructionTime, startTime;
static struct timespec nextInstructionTime, startTime;
uint64_t hitcount = 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_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...
}
// 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) {
struct timespec currentTime;
@ -226,9 +106,14 @@ static void *cpu_thread(void *dummyptr) {
#endif
timespec_add_cycles(&startTime, g_cpu->cycles + executed, &nextInstructionTime);
g_speaker->beginMixing();
// The paddles need to be triggered in real-time on the CPU
// clock. That happens from the VM's CPU maintenance poller.
((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
{

View File

@ -1,13 +1,99 @@
#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(&currentTime);
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()
{
pclose(f);
}
void DummySpeaker::toggleAtCycle(uint32_t c)
{
nextTransitionAt = 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++;
}

View File

@ -1,15 +1,28 @@
#ifndef __DUMMYSPEAKER_H
#define __DUMMYSPEAKER_H
#include <stdio.h>
#include <stdint.h>
#include "physicalspeaker.h"
class DummySpeaker : public PhysicalSpeaker {
public:
DummySpeaker();
virtual ~DummySpeaker();
virtual void toggleAtCycle(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

143
opencv/timeutil.h Normal file
View File

@ -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) ;
}

View File

@ -9,6 +9,9 @@ class PhysicalSpeaker {
virtual void toggleAtCycle(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;
};

View File

@ -1 +0,0 @@
../apple/slot.cpp

View File

@ -31,3 +31,8 @@ void TeensySpeaker::maintainSpeaker(uint32_t c)
analogWriteDAC0(speakerState ? g_volume : 0); // max: 4095
}
}
bool TeensySpeaker::currentState()
{
return speakerState;
}

View File

@ -10,6 +10,7 @@ class TeensySpeaker : public PhysicalSpeaker {
virtual void toggleAtCycle(uint32_t c);
virtual void maintainSpeaker(uint32_t c);
virtual bool currentState();
private:
bool speakerState;

View File

@ -219,8 +219,9 @@ void runCPU()
#endif
}
g_speaker->maintainSpeaker(g_cpu->cycles);
g_speaker->beginMixing();
((AppleVM *)g_vm)->cpuMaintenance(g_cpu->cycles);
g_speaker->maintainSpeaker(g_cpu->cycles);
}
// FIXME: move these memory-related functions elsewhere...