diff --git a/bugs/unimplementedHardware.txt b/bugs/unimplementedHardware.txt index 458544f..ee18015 100644 --- a/bugs/unimplementedHardware.txt +++ b/bugs/unimplementedHardware.txt @@ -25,6 +25,7 @@ ICR POL(1,2,3,6) may flip the pin value as well as the interrupt, POL5 does not edge triggered INT* don't clear on write to ISR when masked in IMR(at least that seems to be the reason) if a sound interrupt is triggered while the button interrupt is disabled the button interrupt will still trigger(in galax game) PWM1 output value is not a direct range cast of 0<->255 to 0<->32767, its additive, see properPwmSineWave.png +inductor dosent properly drain when PWM1 gets disabled Debug tools: ADS7846 channels can't be read in single reference mode in hwTestSuite diff --git a/qtBuildSystem/Mu/Mu.pro b/qtBuildSystem/Mu/Mu.pro index a6106a0..0244101 100644 --- a/qtBuildSystem/Mu/Mu.pro +++ b/qtBuildSystem/Mu/Mu.pro @@ -84,7 +84,8 @@ SOURCES += \ src/pdiUsbD12.c \ src/sdCard.c \ src/sed1376.c \ - src/silkscreen.c + src/silkscreen.c \ + src/audio/inductor.c HEADERS += \ src/audio/blip_buf.h \ @@ -117,7 +118,8 @@ HEADERS += \ emuwrapper.h \ mainwindow.h \ statemanager.h \ - touchscreen.h + touchscreen.h \ + src/audio/inductor.h FORMS += \ mainwindow.ui \ diff --git a/src/audio/inductor.c b/src/audio/inductor.c new file mode 100644 index 0000000..e5be09a --- /dev/null +++ b/src/audio/inductor.c @@ -0,0 +1,29 @@ +#include +#include + +#include "../portability.h" +#include "../emulator.h" +#include "blip_buf.h" + + +#define INDUCTOR_RANGE 10000 +#define INDUCTOR_CLOCK_POWER 0.80 + + +int32_t inductorCurrentCharge; +int32_t inductorLastAudioSample; + + +void inductorReset(){ + inductorCurrentCharge = 0; + inductorLastAudioSample = 0; +} + +void inductorAddClocks(int32_t clocks, bool charge){ + inductorCurrentCharge = sClamp(-INDUCTOR_RANGE, inductorCurrentCharge + (charge ? +clocks : -clocks) * INDUCTOR_CLOCK_POWER, INDUCTOR_RANGE); +} + +void inductorSampleAudio(int32_t now){ + blip_add_delta(palmAudioResampler, now, inductorCurrentCharge - inductorLastAudioSample); + inductorLastAudioSample = inductorCurrentCharge; +} diff --git a/src/audio/inductor.h b/src/audio/inductor.h new file mode 100644 index 0000000..1ea5319 --- /dev/null +++ b/src/audio/inductor.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +extern int32_t inductorCurrentCharge; +extern int32_t inductorLastAudioSample; + +void inductorReset(); + +void inductorAddClocks(int32_t clocks, bool charge); +void inductorSampleAudio(int32_t now); diff --git a/src/emulator.c b/src/emulator.c index d4378d8..2ca66d5 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -5,6 +5,7 @@ #include "m68k/m68k.h" #include "audio/blip_buf.h" +#include "audio/inductor.h" #include "m68328.h" #include "emulator.h" #include "hardwareRegisters.h" @@ -107,6 +108,7 @@ uint32_t emulatorInit(buffer_t palmRomDump, buffer_t palmBootDump, uint32_t spec sed1376Reset(); ads7846Reset(); pdiUsbD12Reset(); + inductorReset(); sandboxInit(); memset(&palmInput, 0x00, sizeof(palmInput)); @@ -144,11 +146,11 @@ void emulatorExit(){ void emulatorReset(){ //reset doesnt clear RAM or SD card, all programs are stored in RAM or on SD card debugLog("Reset triggered, PC:0x%08X\n", m68k_get_reg(NULL, M68K_REG_PPC)); - blip_add_delta(palmAudioResampler, 0, -pwm1LastSampleDelta); m68328Reset(); sed1376Reset(); ads7846Reset(); pdiUsbD12Reset(); + inductorReset(); } void emulatorSetRtc(uint16_t days, uint8_t hours, uint8_t minutes, uint8_t seconds){ @@ -186,7 +188,7 @@ uint64_t emulatorGetStateSize(){ size += sizeof(int32_t);//pwm1ClocksToNextSample size += sizeof(uint8_t) * 6;//pwm1Fifo[6] size += sizeof(uint8_t) * 2;//pwm1(Read/Write) - size += sizeof(int32_t);//pwm1LastSampleDelta + size += sizeof(int32_t) * 2;//inductorCurrentCharge / inductorLastAudioSample size += sizeof(uint8_t) * 7;//palmMisc size += sizeof(uint32_t);//palmSdCard.command size += sizeof(uint8_t) * 2;//palmSdCard.response / palmSdCard.commandBitsRemaining @@ -311,7 +313,9 @@ bool emulatorSaveState(buffer_t buffer){ offset += sizeof(uint8_t); writeStateValueUint8(buffer.data + offset, pwm1WritePosition); offset += sizeof(uint8_t); - writeStateValueInt32(buffer.data + offset, pwm1LastSampleDelta); + writeStateValueInt32(buffer.data + offset, inductorCurrentCharge); + offset += sizeof(int32_t); + writeStateValueInt32(buffer.data + offset, inductorLastAudioSample); offset += sizeof(int32_t); //misc @@ -462,10 +466,10 @@ bool emulatorLoadState(buffer_t buffer){ offset += sizeof(uint8_t); pwm1WritePosition = readStateValueUint8(buffer.data + offset); offset += sizeof(uint8_t); - blip_add_delta(palmAudioResampler, 0, -pwm1LastSampleDelta); - pwm1LastSampleDelta = readStateValueInt32(buffer.data + offset); + inductorCurrentCharge = readStateValueInt32(buffer.data + offset); + offset += sizeof(int32_t); + inductorLastAudioSample = readStateValueInt32(buffer.data + offset); offset += sizeof(int32_t); - blip_add_delta(palmAudioResampler, 0, pwm1LastSampleDelta); //misc palmMisc.powerButtonLed = readStateValueBool(buffer.data + offset); diff --git a/src/emulator.h b/src/emulator.h index 2c4701b..13bb778 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -120,7 +120,7 @@ typedef struct{ //system constants #define CRYSTAL_FREQUENCY 32768 -#define AUDIO_AMPLITUDE 1000000 +#define AUDIO_VOLUME 10//1000000 #define AUDIO_SAMPLES_PER_FRAME (AUDIO_SAMPLE_RATE / EMU_FPS) #define AUDIO_END_OF_FRAME (235929600 / EMU_FPS)//smallest amount of time a second can be split into:(2.0 * (14.0 * (255 + 1.0) + 15 + 1.0)) * 32768 == 235929600, used to convert the variable timing of SYSCLK and CLK32 to a fixed location in the current frame 0<->AUDIO_END_OF_FRAME #define AUDIO_WAIT_FOR_SAMPLE INT32_MIN diff --git a/src/hardwareRegisters.c b/src/hardwareRegisters.c index 81435a6..c26d11c 100644 --- a/src/hardwareRegisters.c +++ b/src/hardwareRegisters.c @@ -34,7 +34,6 @@ int32_t pwm1ClocksToNextSample; uint8_t pwm1Fifo[6]; uint8_t pwm1ReadPosition; uint8_t pwm1WritePosition; -int32_t pwm1LastSampleDelta; static void checkInterrupts(); @@ -1083,7 +1082,6 @@ void resetHwRegisters(){ memset(pwm1Fifo, 0x00, sizeof(pwm1Fifo)); pwm1ReadPosition = 0; pwm1WritePosition = 0; - pwm1LastSampleDelta = 0; memset(chips, 0x00, sizeof(chips)); //all chip selects are disabled at boot and CSA0 is mapped to 0x00000000 and covers the entire address range until CSA is set enabled diff --git a/src/hardwareRegisters.h b/src/hardwareRegisters.h index bc3e576..ab36e67 100644 --- a/src/hardwareRegisters.h +++ b/src/hardwareRegisters.h @@ -80,7 +80,6 @@ extern int32_t pwm1ClocksToNextSample; extern uint8_t pwm1Fifo[]; extern uint8_t pwm1ReadPosition; extern uint8_t pwm1WritePosition; -extern int32_t pwm1LastSampleDelta; //timing void beginClk32(); diff --git a/src/hardwareRegistersAccessors.c.h b/src/hardwareRegistersAccessors.c.h index ad22cab..6ae3a77 100644 --- a/src/hardwareRegistersAccessors.c.h +++ b/src/hardwareRegistersAccessors.c.h @@ -85,12 +85,18 @@ int32_t pwm1FifoRunSample(int32_t now, int32_t clockOffset){ uint8_t prescaler = (pwmc1 >> 8 & 0x7F) + 1; uint8_t clockDivider = 2 << (pwmc1 & 0x03); uint8_t repeat = 1 << (pwmc1 >> 2 & 0x03); - int32_t audioStart = now + clockOffset; - int32_t audioSampleDuration = usingClk32 ? audioGetFramePercentIncrementFromClk32s(period * prescaler * clockDivider * repeat) : audioGetFramePercentIncrementFromSysclks(period * prescaler * clockDivider * repeat); - int32_t audioDeew = dMin((double)sample / period, 1.0)/*dutyCycle*/ * AUDIO_AMPLITUDE; + int32_t audioNow = now + clockOffset; + int32_t audioSampleDuration = usingClk32 ? audioGetFramePercentIncrementFromClk32s(period * prescaler * clockDivider) : audioGetFramePercentIncrementFromSysclks(period * prescaler * clockDivider); + int32_t audioDutyCycle = dMin((double)sample / period, 1.0)/*dutyCycle*/ * audioSampleDuration; - blip_add_delta(palmAudioResampler, audioStart, audioDeew - pwm1LastSampleDelta); - pwm1LastSampleDelta = audioDeew; + //relay all the samples to the inductor + for(uint8_t index = 0; index < repeat; index++){ + inductorAddClocks(audioDutyCycle, true); + inductorSampleAudio(audioNow + audioDutyCycle); + inductorAddClocks(audioSampleDuration - audioDutyCycle, false); + inductorSampleAudio(audioNow + audioSampleDuration); + audioNow += audioSampleDuration; + } //remove used entry if(pwm1FifoEntrys() > 0) @@ -106,12 +112,7 @@ int32_t pwm1FifoRunSample(int32_t now, int32_t clockOffset){ registerArrayWrite16(PWMC1, pwmc1 | 0x0080);//set IRQ bit } - return audioSampleDuration; -} - -static inline void pwm1FifoFinished(int32_t now){ - blip_add_delta(palmAudioResampler, now, -pwm1LastSampleDelta); - pwm1LastSampleDelta = 0; + return audioSampleDuration * repeat; } static inline void pwm1FifoWrite(uint8_t value){ @@ -619,18 +620,23 @@ static inline uint8_t getPortMValue(){ static inline void samplePwm1(bool forClk32, double sysclks){ //clear PWM1 FIFO values if enough time has passed uint16_t pwmc1 = registerArrayRead16(PWMC1); + int32_t audioClocks; //check if enabled and validate clock mode if(!(pwmc1 & 0x0010) || forClk32 != (bool)(pwmc1 & 0x8000)) return; + //this calculation is fairly heavy, only do it after we know the clock mode is valid + audioClocks = forClk32 ? audioGetFramePercentIncrementFromClk32s(1) : audioGetFramePercentIncrementFromSysclks(sysclks); + //add cycles if(pwm1ClocksToNextSample != AUDIO_WAIT_FOR_SAMPLE) - pwm1ClocksToNextSample -= forClk32 ? audioGetFramePercentIncrementFromClk32s(1) : audioGetFramePercentIncrementFromSysclks(sysclks); + pwm1ClocksToNextSample -= audioClocks; //use samples if(pwm1ClocksToNextSample <= 0){ int32_t audioNow = audioGetFramePercentage(); + int32_t audioClocksLeft = audioClocks; while(pwm1FifoEntrys() > 0 && pwm1ClocksToNextSample <= 0){ int32_t audioUsed; @@ -648,12 +654,17 @@ static inline void samplePwm1(bool forClk32, double sysclks){ } audioNow += audioUsed; + audioClocksLeft -= audioUsed;//goes negative when extra clocks are used } //all samples used and its still negative, wait for next sample - if(pwm1ClocksToNextSample != AUDIO_WAIT_FOR_SAMPLE && pwm1ClocksToNextSample <= 0){ - pwm1FifoFinished(audioNow); + if(pwm1ClocksToNextSample <= 0) pwm1ClocksToNextSample = AUDIO_WAIT_FOR_SAMPLE; + + //add 0 cycles to inductor when theres no samples + if(audioClocksLeft > 0){ + inductorAddClocks(audioClocksLeft, false); + inductorSampleAudio(audioNow + audioClocksLeft); } } } diff --git a/src/makefile.all b/src/makefile.all index 9807d57..86aa27d 100644 --- a/src/makefile.all +++ b/src/makefile.all @@ -14,4 +14,5 @@ EMU_SOURCES_C := $(EMU_PATH)/emulator.c \ $(EMU_PATH)/m68k/m68kdasm.c \ $(EMU_PATH)/m68k/m68kcpu.c \ $(EMU_PATH)/audio/blip_buf.c \ + $(EMU_PATH)/audio/inductor.c \ $(EMU_PATH)/debug/sandbox.c