diff --git a/lib/source/hw/evmu_buzzer.c b/lib/source/hw/evmu_buzzer.c index f025c85..87d459f 100755 --- a/lib/source/hw/evmu_buzzer.c +++ b/lib/source/hw/evmu_buzzer.c @@ -1,4 +1,5 @@ #include +#include #include #include "evmu_device_.h" #include "evmu_buzzer_.h" @@ -55,6 +56,18 @@ static void EvmuBuzzer_latchMode1Registers_(EvmuBuzzer_* pSelf_, pSelf_->activeT1lc = pSelf_->pRam->sfr[EVMU_SFR_OFFSET(EVMU_ADDRESS_SFR_T1LC)]; } +static size_t EvmuBuzzer_pcmFrequencyForClock_(const EvmuBuzzer* pSelf) { + EvmuDevice* pDevice = EvmuPeripheral_device(EVMU_PERIPHERAL(pSelf)); + const EvmuTicks cycleTicks = EvmuClock_systemTicksPerCycle(pDevice->pClock); + + if(!cycleTicks) + return 0; + + // PCM playback models one Timer1 cycle per sample, so the sample rate must + // track the currently selected VMU system clock. + return (size_t)((1000000000ull + (cycleTicks / 2u)) / cycleTicks); +} + static void EvmuBuzzer_updateTone_(EvmuBuzzer* pSelf) { EvmuBuzzer_* pSelf_ = EVMU_BUZZER_(pSelf); const uint16_t period = @@ -94,6 +107,7 @@ void EvmuBuzzer__memorySink_(EvmuBuzzer_* pSelf_, EvmuAddress address, EvmuWord case EVMU_ADDRESS_SFR_P1DDR: case EVMU_ADDRESS_SFR_P1FCR: case EVMU_ADDRESS_SFR_P1: + case EVMU_ADDRESS_SFR_OCR: { const GblBool running = pSelf_->pRam->sfr[EVMU_SFR_OFFSET(EVMU_ADDRESS_SFR_T1CNT)] & @@ -192,34 +206,39 @@ EVMU_EXPORT EVMU_RESULT EvmuBuzzer_setTone(EvmuBuzzer* pSelf, if(!pSelf_->enabled) GBL_CTX_DONE(); - //Check to see if wave is different from what's in the audio buffer - if(pSelf_->tonePeriod != tonePeriod || pSelf_->toneInvPulseLength != toneInvPulseLength) { - // Prevent crazy numbers from blowing up - if(tonePeriod < toneInvPulseLength) - tonePeriod = toneInvPulseLength; + // Prevent crazy numbers from blowing up + if(tonePeriod < toneInvPulseLength) + tonePeriod = toneInvPulseLength; + + const size_t pcmFrequency = EvmuBuzzer_pcmFrequencyForClock_(pSelf); + const size_t sampleSize = tonePeriod; - float periodSample = 0.000183f; - float freqSample = roundf(1.0f/periodSample); - int freqSamplei = freqSample; - float periodSecs = tonePeriod * periodSample; - float samples = freqSample * periodSecs; - int sampleSize = roundf(samples); - float invDutyCycle = (float)toneInvPulseLength/(float)tonePeriod; - int activeCycle = roundf(invDutyCycle*(float)sampleSize); + // Check to see if the square wave or the underlying Timer1 clock changed. + if(pSelf_->tonePeriod != tonePeriod || + pSelf_->toneInvPulseLength != toneInvPulseLength || + pSelf_->pcmFrequency != pcmFrequency || + pSelf_->pcmSamples != sampleSize) + { + const size_t adjustedSampleSize = tonePeriod; + const size_t activeCycle = toneInvPulseLength; - GBL_ASSERT(sampleSize <= (int)sizeof(pSelf_->pcmBuffer), "PCM buffer is too small!"); + GBL_ASSERT(adjustedSampleSize <= sizeof(pSelf_->pcmBuffer), + "PCM buffer is too small!"); memset(pSelf_->pcmBuffer, 0x7f, activeCycle); - memset(&pSelf_->pcmBuffer[activeCycle], 0xff, sampleSize-activeCycle); + memset(&pSelf_->pcmBuffer[activeCycle], + 0xff, + adjustedSampleSize - activeCycle); //cache wave data for buffer pSelf_->tonePeriod = tonePeriod; pSelf_->toneInvPulseLength = toneInvPulseLength; - pSelf_->pcmSamples = sampleSize; - pSelf_->pcmFrequency = freqSamplei; + pSelf_->pcmSamples = adjustedSampleSize; + pSelf_->pcmFrequency = pcmFrequency; pSelf->pcmChanged = GBL_TRUE; - const GblBool flat = (!activeCycle || sampleSize == activeCycle); + const GblBool flat = + (!activeCycle || adjustedSampleSize == activeCycle); if(pSelf_->active) GBL_VCALL(EvmuBuzzer, pFnStopPcm, pSelf); diff --git a/lib/source/hw/evmu_cpu.c b/lib/source/hw/evmu_cpu.c index 7150d80..585c062 100755 --- a/lib/source/hw/evmu_cpu.c +++ b/lib/source/hw/evmu_cpu.c @@ -13,6 +13,8 @@ #include "../types/evmu_peripheral_.h" #include +#define EVMU_CPU_HALT_BATCH_NS_ 20000u + EVMU_EXPORT EvmuPc EvmuCpu_pc(const EvmuCpu* pSelf) { return EVMU_CPU_(pSelf)->pc; } @@ -70,16 +72,29 @@ EVMU_EXPORT double EvmuCpu_secs(const EvmuCpu* pSelf) { EvmuClock* pClock = EvmuPeripheral_device(EVMU_PERIPHERAL(pSelf))->pClock; const EvmuWord pcon = pRam->sfr[EVMU_SFR_OFFSET(EVMU_ADDRESS_SFR_PCON)]; - - return EvmuClock_systemSecsPerCycle(pClock) * + const size_t stepCycles = + pSelf_->stepCyclesOverride? + pSelf_->stepCyclesOverride : (((pcon & (EVMU_SFR_PCON_HALT_MASK | EVMU_SFR_PCON_HOLD_MASK)) == 0)? - (double)EvmuIsa_format(pSelf_->curInstr.encoded.bytes[EVMU_INSTRUCTION_BYTE_OPCODE])->cc : 1.0); + EvmuIsa_format(pSelf_->curInstr.encoded.bytes[EVMU_INSTRUCTION_BYTE_OPCODE])->cc : + 1u); + + return EvmuClock_systemSecsPerCycle(pClock) * (double)stepCycles; } EVMU_EXPORT size_t EvmuCpu_cycles(const EvmuCpu* pSelf) { - EvmuCpu_* pSelf_ = EVMU_CPU_(pSelf); - return EvmuIsa_format(pSelf_->curInstr.encoded.bytes[EVMU_INSTRUCTION_BYTE_OPCODE])->cc; + EvmuCpu_* pSelf_ = EVMU_CPU_(pSelf); + EvmuRam_* pRam = pSelf_->pRam; + const EvmuWord pcon = + pRam->sfr[EVMU_SFR_OFFSET(EVMU_ADDRESS_SFR_PCON)]; + + if(pSelf_->stepCyclesOverride) + return pSelf_->stepCyclesOverride; + + return ((pcon & (EVMU_SFR_PCON_HALT_MASK | EVMU_SFR_PCON_HOLD_MASK)) == 0)? + EvmuIsa_format(pSelf_->curInstr.encoded.bytes[EVMU_INSTRUCTION_BYTE_OPCODE])->cc : + 1u; } @@ -576,6 +591,7 @@ static EVMU_RESULT EvmuCpu_IBehavior_update_(EvmuIBehavior* pIBehav, EvmuTicks t GBL_CTX_BEGIN(NULL); EvmuCpu* pSelf = EVMU_CPU(pIBehav); + EvmuCpu_* pSelf_ = EVMU_CPU_(pSelf); EvmuDevice* pDevice = EvmuPeripheral_device(EVMU_PERIPHERAL(pIBehav)); EvmuDevice_* pDevice_ = EVMU_DEVICE_(pDevice); //do timing in time domain, so when clock frequency changes, it's automatically handled @@ -589,6 +605,37 @@ static EVMU_RESULT EvmuCpu_IBehavior_update_(EvmuIBehavior* pIBehav, EvmuTicks t pDevice_->pRam->sfr[EVMU_SFR_OFFSET(EVMU_ADDRESS_SFR_PCON)]; const GblBool hold = (pcon & EVMU_SFR_PCON_HOLD_MASK) != 0; const GblBool halt = (pcon & EVMU_SFR_PCON_HALT_MASK) != 0; + pSelf_->stepCyclesOverride = 0; + + if(halt && !hold) { + unsigned stepCycles = 1u; + + if(!pDevice_->pPic->intReq) { + const EvmuTicks cycleTicks = EvmuClock_systemTicksPerCycle(pDevice->pClock); + const EvmuTicks remainingTicks = + (EvmuTicks)(((deltaTime - time) * 1000000000.0) + 0.5); + EvmuTicks maxBatchCycles = cycleTicks? + (EVMU_CPU_HALT_BATCH_NS_ / cycleTicks) : + 0u; + + if(!maxBatchCycles) + maxBatchCycles = 1u; + + if(cycleTicks) { + EvmuTicks remainingCycles = remainingTicks / cycleTicks; + if(remainingTicks % cycleTicks) + ++remainingCycles; + if(!remainingCycles) + remainingCycles = 1u; + if(maxBatchCycles > remainingCycles) + maxBatchCycles = remainingCycles; + } + + stepCycles = (unsigned)maxBatchCycles; + } + + pSelf_->stepCyclesOverride = stepCycles; + } if(!hold) { EvmuPic_update(EVMU_PIC_PUBLIC_(pDevice_->pPic)); @@ -602,6 +649,7 @@ static EVMU_RESULT EvmuCpu_IBehavior_update_(EvmuIBehavior* pIBehav, EvmuTicks t time += cpuTime; if(!hold) EvmuIBehavior_update(EVMU_IBEHAVIOR(pDevice->pLcd), cpuTime*1000000.0); + pSelf_->stepCyclesOverride = 0; } @@ -618,6 +666,7 @@ static GBL_RESULT EvmuCpu_IBehavior_reset_(EvmuIBehavior* pSelf) { memset(&EVMU_CPU_(pSelf)->curInstr.encoded, 0, sizeof(EvmuInstruction)); memset(&EVMU_CPU_(pSelf)->curInstr.decoded, 0, sizeof(EvmuDecodedInstruction)); EVMU_CPU_(pSelf)->curInstr.pFormat = EvmuIsa_format(EVMU_OPCODE_NOP); + EVMU_CPU_(pSelf)->stepCyclesOverride = 0; GBL_CTX_END(); } diff --git a/lib/source/hw/evmu_cpu_.h b/lib/source/hw/evmu_cpu_.h index f304789..fe9be49 100644 --- a/lib/source/hw/evmu_cpu_.h +++ b/lib/source/hw/evmu_cpu_.h @@ -34,6 +34,7 @@ typedef struct EvmuCpu_ { EvmuRam_* pRam; uint16_t pc; + uint32_t stepCyclesOverride; struct { EvmuInstruction encoded;