mirror of
https://github.com/libretro/Mu.git
synced 2026-09-22 22:56:12 +00:00
See description below
Fix FIFO incaauracys. Remove extra left shift on SPI1 data after the final bit is in. Remove old image when last state is deleted. Set active state to the newest one on save state and update image accordingly. Set spi1RxOverflowed to false on boot. Move variables to top of function in mainwindow constructor.
This commit is contained in:
@@ -316,7 +316,7 @@ uint32_t EmuWrapper::loadState(const QString& path){
|
||||
return error;
|
||||
}
|
||||
|
||||
uint32_t EmuWrapper::installApplication(QString path){
|
||||
uint32_t EmuWrapper::installApplication(const QString& path){
|
||||
bool wasPaused = isPaused();
|
||||
uint32_t error = EMU_ERROR_INVALID_PARAMETER;
|
||||
QFile appFile(path);
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
bool isRunning() const{return emuRunning;}
|
||||
bool isPaused() const{return emuPaused;}
|
||||
|
||||
uint32_t installApplication(QString path);
|
||||
uint32_t installApplication(const QString& path);
|
||||
|
||||
std::vector<QString>& getDebugStrings();
|
||||
std::vector<uint64_t>& getDuplicateCallCount();
|
||||
|
||||
@@ -53,9 +53,10 @@ void printLastRun(){
|
||||
MainWindow::MainWindow(QWidget* parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow){
|
||||
ui->setupUi(this);
|
||||
|
||||
QAudioFormat format;
|
||||
QString resourceDirPath;
|
||||
|
||||
//audio output
|
||||
format.setSampleRate(AUDIO_SAMPLE_RATE);
|
||||
format.setChannelCount(2);
|
||||
format.setSampleSize(16);
|
||||
@@ -67,6 +68,7 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||
#endif
|
||||
format.setSampleType(QAudioFormat::SignedInt);
|
||||
|
||||
//submodules
|
||||
settings = new QSettings(QDir::homePath() + "/MuCfg.txt", QSettings::IniFormat);//settings is public, create it first
|
||||
stateManager = new StateManager(this);
|
||||
emuDebugger = new DebugViewer(this);
|
||||
@@ -74,6 +76,27 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||
audioDevice = new QAudioOutput(format, this);
|
||||
audioOut = audioDevice->start();
|
||||
|
||||
//resource directory
|
||||
resourceDirPath = settings->value("resourceDirectory", "").toString();
|
||||
|
||||
//get default path if path not set
|
||||
if(resourceDirPath == ""){
|
||||
#if defined(Q_OS_ANDROID)
|
||||
resourceDirPath = "/sdcard/Mu";
|
||||
#elif defined(Q_OS_IOS)
|
||||
resourceDirPath = "/var/mobile/Media/Mu";
|
||||
#else
|
||||
resourceDirPath = QDir::homePath() + "/Mu";
|
||||
#endif
|
||||
settings->setValue("resourceDirectory", resourceDirPath);
|
||||
}
|
||||
|
||||
//create directory tree, in case someone deleted it since the emu was last run or it was never created
|
||||
createHomeDirectoryTree(resourceDirPath);
|
||||
|
||||
//GUI
|
||||
ui->setupUi(this);
|
||||
|
||||
//this makes the display window and button icons resize properly
|
||||
ui->centralWidget->installEventFilter(this);
|
||||
ui->centralWidget->setObjectName("centralWidget");
|
||||
@@ -97,29 +120,9 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||
ui->screenshot->installEventFilter(this);
|
||||
ui->stateManager->installEventFilter(this);
|
||||
|
||||
|
||||
QString resourceDirPath = settings->value("resourceDirectory", "").toString();
|
||||
|
||||
//use default path if path not set
|
||||
if(resourceDirPath == ""){
|
||||
#if defined(Q_OS_ANDROID)
|
||||
resourceDirPath = "/sdcard/Mu";
|
||||
#elif defined(Q_OS_IOS)
|
||||
resourceDirPath = "/var/mobile/Media/Mu";
|
||||
#else
|
||||
resourceDirPath = QDir::homePath() + "/Mu";
|
||||
#endif
|
||||
settings->setValue("resourceDirectory", resourceDirPath);
|
||||
}
|
||||
|
||||
//create directory tree, in case someone deleted it since the emu was last run
|
||||
createHomeDirectoryTree(resourceDirPath);
|
||||
|
||||
|
||||
#if !defined(EMU_DEBUG) || defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
|
||||
ui->debugger->hide();
|
||||
#endif
|
||||
|
||||
connect(refreshDisplay, SIGNAL(timeout()), this, SLOT(updateDisplay()));
|
||||
refreshDisplay->start(1000 / EMU_FPS);//update display every X milliseconds
|
||||
}
|
||||
@@ -372,7 +375,6 @@ void MainWindow::on_stateManager_clicked(){
|
||||
if(!wasPaused)
|
||||
emu.pause();
|
||||
|
||||
stateManager->updateStateList();
|
||||
stateManager->exec();
|
||||
|
||||
if(!wasPaused)
|
||||
|
||||
@@ -20,12 +20,21 @@ StateManager::StateManager(QWidget* parent) :
|
||||
//this allows resizing the screenshot of the savestate
|
||||
ui->statePreview->installEventFilter(this);
|
||||
ui->statePreview->setObjectName("statePreview");
|
||||
|
||||
updateStateList();
|
||||
}
|
||||
|
||||
StateManager::~StateManager(){
|
||||
delete ui;
|
||||
}
|
||||
|
||||
bool StateManager::eventFilter(QObject* object, QEvent* event){
|
||||
if(object->objectName() == "statePreview" && event->type() == QEvent::Resize)
|
||||
updateStatePreview();
|
||||
|
||||
return QDialog::eventFilter(object, event);
|
||||
}
|
||||
|
||||
void StateManager::updateStateList(){
|
||||
MainWindow* parent = (MainWindow*)parentWidget();
|
||||
QString saveDirPath = parent->settings->value("resourceDirectory", "").toString() + "/saveStates";
|
||||
@@ -47,21 +56,27 @@ void StateManager::updateStateList(){
|
||||
}
|
||||
}
|
||||
|
||||
bool StateManager::eventFilter(QObject* object, QEvent* event){
|
||||
if(object->objectName() == "statePreview" && event->type() == QEvent::Resize)
|
||||
updateStatePreview();
|
||||
|
||||
return QDialog::eventFilter(object, event);
|
||||
}
|
||||
|
||||
void StateManager::updateStatePreview(){
|
||||
if(ui->states->currentItem()){
|
||||
MainWindow* parent = (MainWindow*)parentWidget();
|
||||
QString statePath = parent->settings->value("resourceDirectory", "").toString() + "/saveStates/" + ui->states->currentItem()->text();
|
||||
|
||||
ui->statePreview->setPixmap(QPixmap(statePath + ".png").scaled(ui->statePreview->width() * 0.98, ui->statePreview->height() * 0.98, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
ui->statePreview->update();
|
||||
}
|
||||
else{
|
||||
//remove outdated image
|
||||
ui->statePreview->clear();
|
||||
}
|
||||
|
||||
ui->statePreview->update();
|
||||
}
|
||||
|
||||
int StateManager::getStateIndexRowByName(const QString& name){
|
||||
for(int index = 0; index < ui->states->count(); index++)
|
||||
if(ui->states->item(index)->text() == name)
|
||||
return index;
|
||||
|
||||
return -1;//nothing by that name exists
|
||||
}
|
||||
|
||||
void StateManager::on_saveState_clicked(){
|
||||
@@ -72,6 +87,8 @@ void StateManager::on_saveState_clicked(){
|
||||
parent->emu.saveState(statePath + ".state");
|
||||
parent->emu.getFramebuffer().save(statePath + ".png");
|
||||
updateStateList();
|
||||
|
||||
ui->states->setCurrentRow(getStateIndexRowByName(ui->newStateName->text()));//this also updates the preview image
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +109,7 @@ void StateManager::on_deleteState_clicked(){
|
||||
QFile(statePath + ".state").remove();
|
||||
QFile(statePath + ".png").remove();
|
||||
updateStateList();
|
||||
|
||||
ui->states->setCurrentRow(0);//pick first valid entry since the old one is no longer valid
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,12 @@ public:
|
||||
explicit StateManager(QWidget* parent = nullptr);
|
||||
~StateManager();
|
||||
|
||||
void updateStateList();
|
||||
|
||||
private slots:
|
||||
bool eventFilter(QObject* object, QEvent* event);
|
||||
|
||||
void updateStateList();
|
||||
void updateStatePreview();
|
||||
int getStateIndexRowByName(const QString& name);
|
||||
|
||||
void on_saveState_clicked();
|
||||
void on_loadState_clicked();
|
||||
|
||||
@@ -434,6 +434,7 @@ uint16_t getHwRegister16(uint32_t address){
|
||||
uint16_t fifoVal = spi1RxFifoRead();
|
||||
//check if SPI1 interrupts changed
|
||||
setSpiIntCs(registerArrayRead16(SPIINTCS));
|
||||
debugLog("SPIRXD read, FIFO value:0x%04X, SPIINTCS:0x%04X\n", fifoVal, registerArrayRead16(SPIINTCS));
|
||||
return fifoVal;
|
||||
}
|
||||
|
||||
@@ -1020,6 +1021,7 @@ void resetHwRegisters(void){
|
||||
memset(spi1TxFifo, 0x00, sizeof(spi1TxFifo));
|
||||
spi1RxReadPosition = 0;
|
||||
spi1RxWritePosition = 0;
|
||||
spi1RxOverflowed = false;
|
||||
spi1TxReadPosition = 0;
|
||||
spi1TxWritePosition = 0;
|
||||
pwm1ClocksToNextSample = 0;
|
||||
|
||||
@@ -40,21 +40,22 @@ static uint8_t spi1RxFifoEntrys(void){
|
||||
}
|
||||
|
||||
static uint16_t spi1RxFifoRead(void){
|
||||
uint16_t value = spi1RxFifo[spi1RxReadPosition];
|
||||
if(spi1RxFifoEntrys() > 0)
|
||||
spi1RxReadPosition = (spi1RxReadPosition + 1) % 9;
|
||||
spi1RxOverflowed = false;
|
||||
return value;
|
||||
|
||||
return spi1RxFifo[spi1RxReadPosition];
|
||||
}
|
||||
|
||||
static void spi1RxFifoWrite(uint16_t value){
|
||||
if(spi1RxFifoEntrys() < 8){
|
||||
spi1RxWritePosition = (spi1RxWritePosition + 1) % 9;
|
||||
spi1RxFifo[spi1RxWritePosition] = value;
|
||||
}
|
||||
else{
|
||||
spi1RxOverflowed = true;
|
||||
debugLog("SPI1 RX FIFO overflowed\n");
|
||||
}
|
||||
spi1RxFifo[spi1RxWritePosition] = value;
|
||||
}
|
||||
|
||||
static void spi1RxFifoFlush(void){
|
||||
@@ -69,10 +70,9 @@ static uint8_t spi1TxFifoEntrys(void){
|
||||
}
|
||||
|
||||
static uint16_t spi1TxFifoRead(void){
|
||||
uint16_t value = spi1TxFifo[spi1TxReadPosition];
|
||||
//dont need a safety check here, the emulator will always check that data is present before trying to access it
|
||||
spi1TxReadPosition = (spi1TxReadPosition + 1) % 9;
|
||||
return value;
|
||||
return spi1TxFifo[spi1TxReadPosition];
|
||||
}
|
||||
|
||||
static void spi1TxFifoWrite(uint16_t value){
|
||||
@@ -95,7 +95,6 @@ static uint8_t pwm1FifoEntrys(void){
|
||||
}
|
||||
|
||||
int32_t pwm1FifoRunSample(int32_t now, int32_t clockOffset){
|
||||
uint8_t sample = pwm1Fifo[pwm1ReadPosition];
|
||||
uint16_t period = registerArrayRead8(PWMP1) + 2;
|
||||
uint16_t pwmc1 = registerArrayRead16(PWMC1);
|
||||
uint8_t prescaler = (pwmc1 >> 8 & 0x7F) + 1;
|
||||
@@ -103,9 +102,14 @@ int32_t pwm1FifoRunSample(int32_t now, int32_t clockOffset){
|
||||
uint8_t repeat = 1 << (pwmc1 >> 2 & 0x03);
|
||||
int32_t audioNow = now + clockOffset;
|
||||
int32_t audioSampleDuration = (pwmc1 & 0x8000)/*CLKSRC*/ ? audioGetFramePercentIncrementFromClk32s(period * prescaler * clockDivider) : audioGetFramePercentIncrementFromSysclks(period * prescaler * clockDivider);
|
||||
float dutyCycle = fMin((float)sample / period, 1.00);
|
||||
float dutyCycle;
|
||||
uint8_t index;
|
||||
|
||||
//try to get next sample, if none are available play old sample
|
||||
if(pwm1FifoEntrys() > 0)
|
||||
pwm1ReadPosition = (pwm1ReadPosition + 1) % 6;
|
||||
dutyCycle = fMin((float)pwm1Fifo[pwm1ReadPosition] / period, 1.00);
|
||||
|
||||
for(index = 0; index < repeat; index++){
|
||||
#if !defined(EMU_NO_SAFETY)
|
||||
if(audioNow + audioSampleDuration >= AUDIO_CLOCK_RATE)
|
||||
@@ -117,10 +121,6 @@ int32_t pwm1FifoRunSample(int32_t now, int32_t clockOffset){
|
||||
audioNow += audioSampleDuration;
|
||||
}
|
||||
|
||||
//remove used entry
|
||||
if(pwm1FifoEntrys() > 0)
|
||||
pwm1ReadPosition = (pwm1ReadPosition + 1) % 6;
|
||||
|
||||
//check for interrupt
|
||||
if(pwm1FifoEntrys() < 2){
|
||||
//trigger interrupt if enabled
|
||||
@@ -336,7 +336,7 @@ static void setSpiIntCs(uint16_t value){
|
||||
newSpiIntCs |= (rxEntrys >= 4) << 4;//RH
|
||||
newSpiIntCs |= (rxEntrys > 0) << 3;//RR
|
||||
newSpiIntCs |= (txEntrys == 8) << 2;//TF
|
||||
newSpiIntCs |= (txEntrys >= 4) << 1;//TH
|
||||
newSpiIntCs |= (txEntrys >= 4) << 1;//TH, the datasheet contradicts itself on whether its more than or equal to 4 empty or full slots
|
||||
newSpiIntCs |= txEntrys == 0;//TE
|
||||
|
||||
//if interrupt state changed update interrupts too, top 8 bits are just the enable bits for the bottom 8
|
||||
@@ -358,7 +358,7 @@ static void setSpiCont1(uint16_t value){
|
||||
//debugLog("SPICONT1 write, old value:0x%04X, value:0x%04X\n", oldSpiCont1, value);
|
||||
|
||||
//SPI1 disabled
|
||||
if(oldSpiCont1 & 0x0200 && !(value & 0x2000)){
|
||||
if(oldSpiCont1 & 0x0200 && !(value & 0x0200)){
|
||||
spi1RxFifoFlush();
|
||||
spi1TxFifoFlush();
|
||||
}
|
||||
@@ -380,8 +380,8 @@ static void setSpiCont1(uint16_t value){
|
||||
|
||||
//The most significant bit is output when the CPU loads the transmitted data, 13.2.3 SPI 1 Phase and Polarity Configurations MC68VZ328UM.pdf
|
||||
for(bits = 0; bits < bitCount; bits++){
|
||||
newRxFifoEntry |= sdCardExchangeBit(!!(currentTxFifoEntry & startBit));
|
||||
newRxFifoEntry <<= 1;
|
||||
newRxFifoEntry |= sdCardExchangeBit(!!(currentTxFifoEntry & startBit));
|
||||
currentTxFifoEntry <<= 1;
|
||||
}
|
||||
|
||||
@@ -398,6 +398,8 @@ static void setSpiCont1(uint16_t value){
|
||||
//update SPIINTCS interrupt bits
|
||||
setSpiIntCs(registerArrayRead16(SPIINTCS));
|
||||
|
||||
debugLog("Transfer complete, SPIINTCS:0x%04X\n", registerArrayRead16(SPIINTCS));
|
||||
|
||||
registerArrayWrite16(SPICONT1, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ static void sdCardDoResponseR1(uint8_t r1){
|
||||
palmSdCard.response = SD_CARD_RESPONSE_SHIFT_OUT;
|
||||
palmSdCard.responseState = (uint64_t)r1 << 56;
|
||||
palmSdCard.responseState |= (uint64_t)1 << 55;//add shift termination bit
|
||||
//palmSdCard.responseState >>= 8;
|
||||
}
|
||||
|
||||
static void sdCardDoResponseR3(uint8_t r1){
|
||||
@@ -74,6 +75,7 @@ static void sdCardDoResponseR3(uint8_t r1){
|
||||
palmSdCard.responseState = (uint64_t)r1 << 56;
|
||||
palmSdCard.responseState |= (uint64_t)sdCardOcr << 24;
|
||||
palmSdCard.responseState |= (uint64_t)1 << 23;//add shift termination bit
|
||||
//palmSdCard.responseState >>= 8;
|
||||
}
|
||||
|
||||
void sdCardReset(void){
|
||||
|
||||
Reference in New Issue
Block a user