diff --git a/src/emulator.c b/src/emulator.c index 09cf615..ed54155 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -182,6 +182,9 @@ uint64_t emulatorGetStateSize(){ size += sizeof(uint16_t) * 9;//RX 8 * 16 SPI1 FIFO, 1 index is for FIFO full size += sizeof(uint16_t) * 9;//TX 8 * 16 SPI1 FIFO, 1 index is for FIFO full size += sizeof(uint8_t) * 4;//spi1(R/T)x(Read/Write)Position + size += sizeof(int32_t);//pwm1ClocksToNextSample + size += sizeof(uint8_t) * 6;//pwm1Fifo[6] + size += sizeof(uint8_t) * 2;//pwm1(Read/Write) size += sizeof(uint8_t) * 7;//palmMisc size += sizeof(uint32_t);//palmSdCard.command size += sizeof(uint8_t) * 2;//palmSdCard.response / palmSdCard.commandBitsRemaining @@ -293,6 +296,18 @@ bool emulatorSaveState(buffer_t buffer){ writeStateValueUint8(buffer.data + offset, spi1TxWritePosition); offset += sizeof(uint8_t); + //PWM1, audio + writeStateValueInt32(buffer.data + offset, pwm1ClocksToNextSample); + offset += sizeof(int32_t); + for(uint8_t fifoPosition = 0; fifoPosition < 6; fifoPosition++){ + writeStateValueUint8(buffer.data + offset, pwm1Fifo[fifoPosition]); + offset += sizeof(uint8_t); + } + writeStateValueUint8(buffer.data + offset, pwm1ReadPosition); + offset += sizeof(uint8_t); + writeStateValueUint8(buffer.data + offset, pwm1WritePosition); + offset += sizeof(uint8_t); + //misc writeStateValueBool(buffer.data + offset, palmMisc.powerButtonLed); offset += sizeof(uint8_t); @@ -428,6 +443,18 @@ bool emulatorLoadState(buffer_t buffer){ spi1TxWritePosition = readStateValueUint8(buffer.data + offset); offset += sizeof(uint8_t); + //PWM1, audio + pwm1ClocksToNextSample = readStateValueInt32(buffer.data + offset); + offset += sizeof(int32_t); + for(uint8_t fifoPosition = 0; fifoPosition < 6; fifoPosition++){ + pwm1Fifo[fifoPosition] = readStateValueUint8(buffer.data + offset); + offset += sizeof(uint8_t); + } + pwm1ReadPosition = readStateValueUint8(buffer.data + offset); + offset += sizeof(uint8_t); + pwm1WritePosition = readStateValueUint8(buffer.data + offset); + offset += sizeof(uint8_t); + //misc palmMisc.powerButtonLed = readStateValueBool(buffer.data + offset); offset += sizeof(uint8_t); diff --git a/src/hardwareRegisters.c b/src/hardwareRegisters.c index 2159663..cc239ea 100644 --- a/src/hardwareRegisters.c +++ b/src/hardwareRegisters.c @@ -28,6 +28,10 @@ uint8_t spi1RxReadPosition; uint8_t spi1RxWritePosition; uint8_t spi1TxReadPosition; uint8_t spi1TxWritePosition; +int32_t pwm1ClocksToNextSample; +uint8_t pwm1Fifo[6]; +uint8_t pwm1ReadPosition; +uint8_t pwm1WritePosition; static void checkInterrupts(); @@ -37,10 +41,36 @@ static double sysclksPerClk32(); static inline uint32_t audioGetFramePercentIncrementFromClk32s(uint32_t count); static inline uint32_t audioGetFramePercentIncrementFromSysclks(double count); static inline uint32_t audioGetFramePercentage(); +void runPwm1Sample(uint8_t sample);//fixme #include "hardwareRegistersAccessors.c.h" #include "hardwareRegistersTiming.c.h" +//this needs to be moved later!!! +void runPwm1Sample(uint8_t sample){ + uint16_t pwmc1 = registerArrayRead16(PWMC1); + + if(!(pwmc1 & 0x8000)){ + //SYSCLK + uint8_t prescaler = (pwmc1 >> 8 & 0x7F) + 1; + uint8_t clockDivider = 2 << (pwmc1 & 0x03); + uint8_t repeat = 1 << (pwmc1 >> 2 & 0x03); + double dutyCycle = dMin((double)sample / (registerArrayRead8(PWMP1) + 2), 1.0); + uint32_t audioNow = audioGetFramePercentage(); + uint32_t audioSampleDuration = audioGetFramePercentIncrementFromSysclks(prescaler * clockDivider); + uint32_t audioDutyCycle = audioSampleDuration * dutyCycle; + + for(uint8_t times = 0; times < repeat; times++){ + blip_add_delta(palmAudioResampler, audioNow, AUDIO_AMPLITUDE); + blip_add_delta(palmAudioResampler, audioNow + audioDutyCycle, -AUDIO_AMPLITUDE); + audioNow += audioSampleDuration; + } + } + else{ + //CLK32 + } +} + bool pllIsOn(){ return !(registerArrayRead16(PLLCR) & 0x0008); } @@ -607,30 +637,8 @@ void setHwRegister8(uint32_t address, uint8_t value){ case PWMS1 + 1: //write only if PWM1 and FIFOAV are set and a slot is available - if((registerArrayRead16(PWMC1) & 0x0030) == 0x0030){ - uint16_t pwmc1 = registerArrayRead16(PWMC1); - - if(!(pwmc1 & 0x8000)){ - //SYSCLK - uint8_t prescaler = (pwmc1 >> 8 & 0x7F) + 1; - uint8_t clockDivider = 2 << (pwmc1 & 0x03); - uint8_t repeat = 1 << (pwmc1 >> 2 & 0x03); - - double dutyCycle = dMin((double)value / (registerArrayRead8(PWMP1) + 2), 1.0); - uint32_t audioNow = audioGetFramePercentage(); - uint32_t audioSampleDuration = audioGetFramePercentIncrementFromSysclks(prescaler * clockDivider); - uint32_t audioDutyCycle = audioSampleDuration * dutyCycle; - - for(uint8_t times = 0; times < repeat; times++){ - blip_add_delta(palmAudioResampler, audioNow, AUDIO_AMPLITUDE); - blip_add_delta(palmAudioResampler, audioNow + audioDutyCycle, -AUDIO_AMPLITUDE); - audioNow += audioSampleDuration; - } - } - else{ - //CLK32 - } - } + if((registerArrayRead16(PWMC1) & 0x0030) == 0x0030) + pwm1FifoWrite(value); break; case PWMP1: @@ -1004,7 +1012,8 @@ void setHwRegister16(uint32_t address, uint16_t value){ case PWMS1: //write only if PWM1 and FIFOAV are set and a slot is available if((registerArrayRead16(PWMC1) & 0x0030) == 0x0030){ - //empty + pwm1FifoWrite(value >> 8); + pwm1FifoWrite(value & 0xFF); } break; diff --git a/src/hardwareRegistersAccessors.c.h b/src/hardwareRegistersAccessors.c.h index 310ec3c..108172c 100644 --- a/src/hardwareRegistersAccessors.c.h +++ b/src/hardwareRegistersAccessors.c.h @@ -18,7 +18,21 @@ static inline void registerArrayWrite8(uint32_t address, uint8_t value){BUFFER_W static inline void registerArrayWrite16(uint32_t address, uint16_t value){BUFFER_WRITE_16(palmReg, address, 0xFFF, value);} static inline void registerArrayWrite32(uint32_t address, uint32_t value){BUFFER_WRITE_32(palmReg, address, 0xFFF, value);} -//SPI1 FIFO accessors, this doesnt quite belong here but it is a memory accessor so its ok for now +//interrupt setters +static inline void setIprIsrBit(uint32_t interruptBit){ + //allows for setting an interrupt with masking by IMR and logging in IPR + uint32_t newIpr = registerArrayRead32(IPR) | interruptBit; + registerArrayWrite32(IPR, newIpr); + registerArrayWrite32(ISR, newIpr & ~registerArrayRead32(IMR)); +} + +static inline void clearIprIsrBit(uint32_t interruptBit){ + uint32_t newIpr = registerArrayRead32(IPR) & ~interruptBit; + registerArrayWrite32(IPR, newIpr); + registerArrayWrite32(ISR, newIpr & ~registerArrayRead32(IMR)); +} + +//SPI1 FIFO accessors static inline uint16_t spi1RxFifoRead(){ uint16_t value = spi1RxFifo[spi1RxReadPosition]; spi1RxReadPosition = (spi1RxReadPosition + 1) % 9; @@ -55,20 +69,64 @@ static inline uint8_t spi1TxFifoEntrys(){ return spi1TxWritePosition - spi1TxReadPosition; } +//PWM1 FIFO accessors +static inline uint8_t pwm1FifoEntrys(){ + //check for wraparound + if(pwm1WritePosition < pwm1ReadPosition) + return pwm1WritePosition + 6 - pwm1ReadPosition; + return pwm1WritePosition - pwm1ReadPosition; +} + +static inline uint8_t pwm1FifoCurrentValue(){ + return pwm1Fifo[pwm1ReadPosition]; +} + +static inline void pwm1FifoRemove(){ + if(pwm1FifoEntrys() > 0){ + pwm1ReadPosition = (pwm1ReadPosition + 1) % 6; + + //set FIFOAV + registerArrayWrite16(PWMC1, registerArrayRead16(PWMC1) | 0x0020); + } + if(pwm1FifoEntrys() < 2){ + uint16_t pwmc1 = registerArrayRead16(PWMC1); + + //trigger interrupt if enabled + if(pwmc1 & 0x0040) + setIprIsrBit(INT_PWM1); + + registerArrayWrite16(PWMC1, pwmc1 | 0x0080);//set IRQ bit + } +} + +static inline void pwm1FifoWrite(uint8_t value){ + if(pwm1FifoEntrys() < 5){ + pwm1Fifo[pwm1WritePosition] = value; + pwm1WritePosition = (pwm1WritePosition + 1) % 6; + + //clear FIFOAV if full + if(pwm1FifoEntrys() == 5) + registerArrayWrite16(PWMC1, registerArrayRead16(PWMC1) & 0xFFDF); + } +} + +static inline void pwm1FifoFlush(){ + pwm1ReadPosition = 0; + pwm1WritePosition = 0; + + //set FIFOAV + registerArrayWrite16(PWMC1, registerArrayRead16(PWMC1) | 0x0020); +} + +static inline uint16_t pwm1FifoSampleDuration(){ + uint16_t pwmc1 = registerArrayRead16(PWMC1); + uint8_t prescaler = (pwmc1 >> 8 & 0x7F) + 1; + uint8_t clockDivider = 2 << (pwmc1 & 0x03); + uint8_t repeat = 1 << (pwmc1 >> 2 & 0x03); + return prescaler * clockDivider * repeat; +} + //register setters -static inline void setIprIsrBit(uint32_t interruptBit){ - //allows for setting an interrupt with masking by IMR and logging in IPR - uint32_t newIpr = registerArrayRead32(IPR) | interruptBit; - registerArrayWrite32(IPR, newIpr); - registerArrayWrite32(ISR, newIpr & ~registerArrayRead32(IMR)); -} - -static inline void clearIprIsrBit(uint32_t interruptBit){ - uint32_t newIpr = registerArrayRead32(IPR) & ~interruptBit; - registerArrayWrite32(IPR, newIpr); - registerArrayWrite32(ISR, newIpr & ~registerArrayRead32(IMR)); -} - static inline void setCsa(uint16_t value){ chips[CHIP_A0_ROM].enable = value & 0x0001; chips[CHIP_A0_ROM].readOnly = value & 0x8000; @@ -376,11 +434,14 @@ static inline void setPwmc1(uint16_t value){ uint16_t oldPwmc1 = registerArrayRead16(PWMC1); //always have FIFOAV set right now, hack - value |= 0x0020; + if(pwm1FifoEntrys() < 5) + value |= 0x0020; - //PWM1 enabled, set IRQ and FIFOAV bit to fill FIFO - if(!(oldPwmc1 & 0x0010) && value & 0x0010) - value |= 0x00A0; + //PWM1 enabled, set IRQ + if(!(oldPwmc1 & 0x0010) && value & 0x0010){ + value |= 0x0080;//enable IRQ + pwm1ClocksToNextSample = 0;//when first sample is written output it immediatly + } //clear interrupt by write(reading can also clear the interrupt) if(oldPwmc1 & 0x0080 && !(value & 0x0080)){ @@ -396,8 +457,11 @@ static inline void setPwmc1(uint16_t value){ } //PWM1 disabled - if(oldPwmc1 & 0x0010 && !(value & 0x0010)) + if(oldPwmc1 & 0x0010 && !(value & 0x0010)){ + pwm1FifoFlush(); + value |= 0x0020;//set FIFOAV since FIFO is now empty, should be set by pwm1FifoFlush() value &= 0x80FF;//clear PWM prescaler value + } registerArrayWrite16(PWMC1, value); } @@ -554,6 +618,46 @@ static inline uint8_t getPortMValue(){ return ((registerArrayRead8(PMDATA) & registerArrayRead8(PMDIR)) | (~registerArrayRead8(PMDIR) & 0x20)) & registerArrayRead8(PMSEL); } +static inline void samplePwm1(bool forClk32, double sysclks){ + //clear PWM1 FIFO values if enough time has passed + uint16_t pwmc1 = registerArrayRead16(PWMC1); + int32_t decrement = forClk32 ? 1 : sysclks; + + //check if enabled and validate clock mode + if(!(pwmc1 & 0x0010) || forClk32 != (bool)(pwmc1 & 0x8000)) + return; + + //add cycles + if(pwm1FifoEntrys() > 0){ + pwm1ClocksToNextSample -= decrement; + if(pwm1ClocksToNextSample <= 0){ + while(pwm1FifoEntrys() > 0 && pwm1ClocksToNextSample <= 0){ + //switch out samples + runPwm1Sample(pwm1FifoCurrentValue()); + pwm1FifoRemove(); + pwm1ClocksToNextSample += pwm1FifoSampleDuration(); + } + + //dont carry negative cycles over when the FIFO runs out + if(pwm1FifoEntrys() == 0 && pwm1ClocksToNextSample < 0) + pwm1ClocksToNextSample = 0; + + /* + //interrupt to fill FIFO + if(pwm1FifoEntrys() < 2 && (pwmc1 & 0x00C0) == 0x0040){ + registerArrayWrite16(PWMC1, pwmc1 | 0x0080);//set IRQ bit + setIprIsrBit(INT_PWM1); + checkInterrupts(); + } + */ + } + } + else{ + //still allow the sample timer to run out but cap a 0 + pwm1ClocksToNextSample = sMax(pwm1ClocksToNextSample - decrement, 0); + } +} + static inline uint16_t getPwmc1(){ uint16_t returnValue = registerArrayRead16(PWMC1); diff --git a/src/hardwareRegistersTiming.c.h b/src/hardwareRegistersTiming.c.h index beec7fb..ffdeec6 100644 --- a/src/hardwareRegistersTiming.c.h +++ b/src/hardwareRegistersTiming.c.h @@ -327,6 +327,7 @@ void endClk32(){ timer1(TIMER_REASON_CLK32, 0); timer2(TIMER_REASON_CLK32, 0); + samplePwm1(true/*forClk32*/, 0.0); //PLLCR wake select wait if(pllWakeWait != -1){ @@ -343,9 +344,10 @@ void endClk32(){ } void addSysclks(double count){ - palmClk32Sysclks += count; timer1(TIMER_REASON_SYSCLK, count); timer2(TIMER_REASON_SYSCLK, count); + samplePwm1(false/*forClk32*/, count); + palmClk32Sysclks += count; } static inline uint32_t audioGetFramePercentIncrementFromClk32s(uint32_t count){