mirror of
https://github.com/libretro/Mu.git
synced 2026-09-22 22:56:12 +00:00
Mostly working audio, better than POSE!
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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){
|
||||
|
||||
Reference in New Issue
Block a user