Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 37 additions & 18 deletions lib/source/hw/evmu_buzzer.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <evmu/hw/evmu_buzzer.h>
#include <evmu/hw/evmu_clock.h>
#include <evmu/hw/evmu_sfr.h>
#include "evmu_device_.h"
#include "evmu_buzzer_.h"
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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)] &
Expand Down Expand Up @@ -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);
Expand Down
59 changes: 54 additions & 5 deletions lib/source/hw/evmu_cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "../types/evmu_peripheral_.h"
#include <gimbal/meta/signals/gimbal_marshal.h>

#define EVMU_CPU_HALT_BATCH_NS_ 20000u

EVMU_EXPORT EvmuPc EvmuCpu_pc(const EvmuCpu* pSelf) {
return EVMU_CPU_(pSelf)->pc;
}
Expand Down Expand Up @@ -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;
}


Expand Down Expand Up @@ -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
Expand All @@ -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));
Expand All @@ -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;

}

Expand All @@ -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();
}
Expand Down
1 change: 1 addition & 0 deletions lib/source/hw/evmu_cpu_.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ typedef struct EvmuCpu_ {
EvmuRam_* pRam;

uint16_t pc;
uint32_t stepCyclesOverride;

struct {
EvmuInstruction encoded;
Expand Down