From 1fd8b1b299e785ee75df6ec45bfb6a8d434af684 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 5 Mar 2019 17:37:21 -0800 Subject: [PATCH 01/10] Qt: Support switching webcams --- CHANGES | 1 + src/platform/qt/InputController.cpp | 31 ++++++++++++++++++++++++++++- src/platform/qt/InputController.h | 4 ++++ src/platform/qt/SettingsView.cpp | 16 +++++++++++++++ src/platform/qt/SettingsView.h | 1 + src/platform/qt/SettingsView.ui | 26 +++++++++++++++++++++--- src/platform/qt/Window.cpp | 1 + 7 files changed, 76 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index e644bca84..52b42650c 100644 --- a/CHANGES +++ b/CHANGES @@ -25,6 +25,7 @@ Misc: - Qt: Add missing HEVC NVENC option (fixes mgba.io/i/1323) - LR35902: Support PC-relative opcode decoding - Qt: Improve camera initialization + - Qt: Support switching webcams 0.7.1: (2019-02-24) Bugfixes: diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index 8869dac82..b655e86d9 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -15,7 +15,7 @@ #include #include #ifdef BUILD_QT_MULTIMEDIA -#include +#include #include #endif @@ -98,6 +98,10 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren } #ifdef BUILD_QT_MULTIMEDIA if (image->p->m_config->getQtOption("cameraDriver").toInt() == static_cast(CameraDriver::QT_MULTIMEDIA)) { + QByteArray camera = image->p->m_config->getQtOption("camera").toByteArray(); + if (!camera.isNull()) { + QMetaObject::invokeMethod(image->p, "setCamera", Q_ARG(QByteArray, camera)); + } QMetaObject::invokeMethod(image->p, "setupCam"); } #endif @@ -691,6 +695,17 @@ void InputController::setCamImage(const QImage& image) { m_image.outOfDate = true; } +QList> InputController::listCameras() const { + QList> out; +#ifdef BUILD_QT_MULTIMEDIA + QList cams = QCameraInfo::availableCameras(); + for (const auto& cam : cams) { + out.append(qMakePair(cam.deviceName().toLatin1(), cam.description())); + } +#endif + return out; +} + void InputController::increaseLuminanceLevel() { setLuminanceLevel(m_luxLevel + 1); } @@ -783,3 +798,17 @@ void InputController::teardownCam() { } #endif } + +void InputController::setCamera(const QByteArray& name) { +#ifdef BUILD_QT_MULTIMEDIA + bool needsRestart = false; + if (m_camera) { + needsRestart = m_camera->state() == QCamera::ActiveState; + } + m_camera = std::make_unique(name); + connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings); + if (needsRestart) { + setupCam(); + } +#endif +} \ No newline at end of file diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index d15bd0e22..6418343c1 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -96,6 +96,8 @@ public: void stealFocus(QWidget* focus); void releaseFocus(QWidget* focus); + QList> listCameras() const; + mRumble* rumble(); mRotationSource* rotationSource(); mImageSource* imageSource() { return &m_image; } @@ -122,6 +124,8 @@ public slots: void loadCamImage(const QString& path); void setCamImage(const QImage& image); + void setCamera(const QByteArray& id); + private slots: #ifdef BUILD_QT_MULTIMEDIA void prepareCamSettings(QCamera::Status); diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 271c59652..1dbf92d35 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -180,12 +180,22 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC m_ui.cameraDriver->addItem(tr("None (Still Image)"), static_cast(InputController::CameraDriver::NONE)); if (cameraDriver.isNull() || cameraDriver.toInt() == static_cast(InputController::CameraDriver::NONE)) { m_ui.cameraDriver->setCurrentIndex(m_ui.cameraDriver->count() - 1); + m_ui.camera->setEnabled(false); } #ifdef BUILD_QT_MULTIMEDIA m_ui.cameraDriver->addItem(tr("Qt Multimedia"), static_cast(InputController::CameraDriver::QT_MULTIMEDIA)); if (!cameraDriver.isNull() && cameraDriver.toInt() == static_cast(InputController::CameraDriver::QT_MULTIMEDIA)) { m_ui.cameraDriver->setCurrentIndex(m_ui.cameraDriver->count() - 1); + m_ui.camera->setEnabled(true); + } + QList> cameras = inputController->listCameras(); + QByteArray currentCamera = m_controller->getQtOption("camera").toByteArray(); + for (const auto& camera : cameras) { + m_ui.camera->addItem(camera.second, camera.first); + if (camera.first == currentCamera) { + m_ui.camera->setCurrentIndex(m_ui.camera->count() - 1); + } } #endif @@ -442,6 +452,12 @@ void SettingsView::updateConfig() { emit cameraDriverChanged(); } + QVariant camera = m_ui.camera->itemData(m_ui.camera->currentIndex()); + if (camera != m_controller->getQtOption("camera")) { + m_controller->setQtOption("camera", camera); + emit cameraChanged(camera.toByteArray()); + } + QLocale language = m_ui.languages->itemData(m_ui.languages->currentIndex()).toLocale(); if (language != m_controller->getQtOption("language").toLocale() && !(language.bcp47Name() == QLocale::system().bcp47Name() && m_controller->getQtOption("language").isNull())) { m_controller->setQtOption("language", language.bcp47Name()); diff --git a/src/platform/qt/SettingsView.h b/src/platform/qt/SettingsView.h index 017b55693..b04d21805 100644 --- a/src/platform/qt/SettingsView.h +++ b/src/platform/qt/SettingsView.h @@ -39,6 +39,7 @@ signals: void audioDriverChanged(); void displayDriverChanged(); void cameraDriverChanged(); + void cameraChanged(const QByteArray&); void pathsChanged(); void languageChanged(); void libraryCleared(); diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index 41fb9a7de..792d4b4e8 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -1261,7 +1261,7 @@ - Game Boy model + Game Boy model: @@ -1297,7 +1297,7 @@ - Super Game Boy model + Super Game Boy model: @@ -1333,7 +1333,7 @@ - Game Boy Color model + Game Boy Color model: @@ -1672,6 +1672,26 @@ + + + + Camera: + + + + + + + false + + + + 0 + 0 + + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 7a8c97dfc..aef833f42 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -443,6 +443,7 @@ void Window::openSettingsWindow() { connect(settingsWindow, &SettingsView::displayDriverChanged, this, &Window::reloadDisplayDriver); connect(settingsWindow, &SettingsView::audioDriverChanged, this, &Window::reloadAudioDriver); connect(settingsWindow, &SettingsView::cameraDriverChanged, this, &Window::mustRestart); + connect(settingsWindow, &SettingsView::cameraChanged, &m_inputController, &InputController::setCamera); connect(settingsWindow, &SettingsView::languageChanged, this, &Window::mustRestart); connect(settingsWindow, &SettingsView::pathsChanged, this, &Window::reloadConfig); #ifdef USE_SQLITE3 From 51030d98fd01679834abf8d99812ac3e5fe0793e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 5 Mar 2019 18:20:30 -0800 Subject: [PATCH 02/10] Qt: Fix camera on Windows --- src/platform/qt/InputController.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index b655e86d9..fd15333fd 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -806,9 +806,9 @@ void InputController::setCamera(const QByteArray& name) { needsRestart = m_camera->state() == QCamera::ActiveState; } m_camera = std::make_unique(name); - connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings); + connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection); if (needsRestart) { setupCam(); } #endif -} \ No newline at end of file +} From d9fef21f92e7438c11ffc29cb1dc1f233d731f9c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 5 Mar 2019 18:24:10 -0800 Subject: [PATCH 03/10] Qt: Fix camera on Windows part 2 --- src/platform/qt/InputController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index fd15333fd..9935626bc 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -740,7 +740,7 @@ void InputController::setupCam() { #ifdef BUILD_QT_MULTIMEDIA if (!m_camera) { m_camera = std::make_unique(); - connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings); + connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings, Qt::QueuedConnection); } m_camera->setCaptureMode(QCamera::CaptureVideo); m_camera->setViewfinder(&m_videoDumper); From 5d129e26bf53c68261b217ac9b28950e148915c7 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 6 Mar 2019 00:22:00 -0800 Subject: [PATCH 04/10] Test: Add tests for unary operators --- src/debugger/test/lexer.c | 57 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/debugger/test/lexer.c b/src/debugger/test/lexer.c index 89e1024df..c9ba4e953 100644 --- a/src/debugger/test/lexer.c +++ b/src/debugger/test/lexer.c @@ -350,6 +350,58 @@ M_TEST_DEFINE(lexIdentifierGreaterOperator) { assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_GREATER); } +M_TEST_DEFINE(lexNotOperator) { + LEX("!1"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->operatorValue, OP_NOT); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1); +} + +M_TEST_DEFINE(lexNotNotOperator) { + LEX("!!1"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->operatorValue, OP_NOT); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_NOT); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 2)->uintValue, 1); +} + +M_TEST_DEFINE(lexNegateOperator) { + LEX("-1"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->operatorValue, OP_NEGATE); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1); +} + +M_TEST_DEFINE(lexFlipOperator) { + LEX("~1"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->operatorValue, OP_FLIP); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1); +} + +M_TEST_DEFINE(lexDereferenceOperator) { + LEX("*1"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->operatorValue, OP_DEREFERENCE); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1); +} + M_TEST_DEFINE(lexEqualsOperator) { LEX("1=="); @@ -814,6 +866,11 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Lexer, cmocka_unit_test(lexIdentifierLessOperator), cmocka_unit_test(lexGreaterOperator), cmocka_unit_test(lexIdentifierGreaterOperator), + cmocka_unit_test(lexNotOperator), + cmocka_unit_test(lexNotNotOperator), + cmocka_unit_test(lexNegateOperator), + cmocka_unit_test(lexFlipOperator), + cmocka_unit_test(lexDereferenceOperator), cmocka_unit_test(lexEqualsOperator), cmocka_unit_test(lexIdentifierEqualsOperator), cmocka_unit_test(lexNotEqualsOperator), From e0b1caf48c7f39585fa3d84b7dec18cbf351ca13 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 7 Mar 2019 18:52:22 -0800 Subject: [PATCH 05/10] Debugger: Make operator lexing a bit more generic --- src/debugger/parser.c | 105 ++++++++------------- src/debugger/test/lexer.c | 183 ------------------------------------- src/debugger/test/parser.c | 32 ++++++- 3 files changed, 69 insertions(+), 251 deletions(-) diff --git a/src/debugger/parser.c b/src/debugger/parser.c index 9d8db0262..41197b53e 100644 --- a/src/debugger/parser.c +++ b/src/debugger/parser.c @@ -21,7 +21,6 @@ enum LexState { LEX_EXPECT_HEX_FIRST, LEX_EXPECT_HEX, LEX_EXPECT_PREFIX, - LEX_EXPECT_OPERATOR, LEX_EXPECT_OPERATOR2, }; @@ -91,31 +90,6 @@ static void _lexOperator(struct LexVector* lv, char operator, enum LexState* sta } *state = LEX_ERROR; } - if (*state == LEX_ROOT || *state == LEX_ERROR) { - struct Token lvNext; - lvNext.type = TOKEN_OPERATOR_TYPE; - *state = LEX_ROOT; - switch (operator) { - case '-': - lvNext.operatorValue = OP_NEGATE; - break; - case '~': - lvNext.operatorValue = OP_FLIP; - break; - case '!': - lvNext.operatorValue = OP_NOT; - break; - case '*': - lvNext.operatorValue = OP_DEREFERENCE; - break; - default: - lvNext.type = TOKEN_ERROR_TYPE; - *state = LEX_ERROR; - return; - } - *LexVectorAppend(lv) = lvNext; - return; - } struct Token lvNext; lvNext.type = TOKEN_OPERATOR_TYPE; switch (operator) { @@ -155,6 +129,9 @@ static void _lexOperator(struct LexVector* lv, char operator, enum LexState* sta case '!': lvNext.operatorValue = OP_NOT; break; + case '~': + lvNext.operatorValue = OP_FLIP; + break; default: *state = LEX_ERROR; return; @@ -190,14 +167,14 @@ static void _lexValue(struct LexVector* lv, char token, uint32_t next, enum LexS lvNext->uintValue = next; lvNext = LexVectorAppend(lv); lvNext->type = TOKEN_CLOSE_PAREN_TYPE; - *state = LEX_EXPECT_OPERATOR; + *state = LEX_ROOT; break; case ' ': case '\t': lvNext = LexVectorAppend(lv); lvNext->type = TOKEN_UINT_TYPE; lvNext->uintValue = next; - *state = LEX_EXPECT_OPERATOR; + *state = LEX_ROOT; break; default: *state = LEX_ERROR; @@ -264,21 +241,30 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length, co state = LEX_EXPECT_HEX_FIRST; next = 0; break; - case '%': - state = LEX_EXPECT_BINARY_FIRST; - next = 0; - break; case '(': state = LEX_ROOT; lvNext = LexVectorAppend(lv); lvNext->type = TOKEN_OPEN_PAREN_TYPE; break; - case '!': + case '=': + case '+': case '-': - case '~': case '*': + case '/': + case '%': + case '&': + case '|': + case '^': + case '<': + case '>': + case '!': + case '~': _lexOperator(lv, token, &state); break; + case ')': + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_CLOSE_PAREN_TYPE; + break; case ' ': case '\t': break; @@ -305,6 +291,7 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length, co case '<': case '>': case '!': + case '~': lvNext = LexVectorAppend(lv); lvNext->type = TOKEN_IDENTIFIER_TYPE; lvNext->identifierValue = strndup(tokenStart, string - tokenStart - 1); @@ -316,14 +303,14 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length, co lvNext->identifierValue = strndup(tokenStart, string - tokenStart - 1); lvNext = LexVectorAppend(lv); lvNext->type = TOKEN_CLOSE_PAREN_TYPE; - state = LEX_EXPECT_OPERATOR; + state = LEX_ROOT; break; case ' ': case '\t': lvNext = LexVectorAppend(lv); lvNext->type = TOKEN_IDENTIFIER_TYPE; lvNext->identifierValue = strndup(tokenStart, string - tokenStart - 1); - state = LEX_EXPECT_OPERATOR; + state = LEX_ROOT; break; default: break; @@ -446,33 +433,6 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length, co break; } break; - case LEX_EXPECT_OPERATOR: - switch (token) { - case '=': - case '+': - case '-': - case '*': - case '/': - case '%': - case '&': - case '|': - case '^': - case '<': - case '>': - case '!': - _lexOperator(lv, token, &state); - break; - case ')': - lvNext = LexVectorAppend(lv); - lvNext->type = TOKEN_CLOSE_PAREN_TYPE; - break; - case ' ': - case '\t': - break; - default: - state = LEX_ERROR; - } - break; case LEX_ERROR: // This shouldn't be reached break; @@ -494,7 +454,6 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length, co lvNext->identifierValue = strndup(tokenStart, string - tokenStart); break; case LEX_ROOT: - case LEX_EXPECT_OPERATOR: case LEX_EXPECT_OPERATOR2: break; case LEX_EXPECT_BINARY_FIRST: @@ -568,9 +527,6 @@ static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, siz tree->rhs = _parseTreeCreate(); tree->token.type = TOKEN_SEGMENT_TYPE; i = _parseExpression(tree->rhs, lv, i + 1, precedence, openParens); - if (tree->token.type == TOKEN_ERROR_TYPE) { - tree->token.type = TOKEN_ERROR_TYPE; - } break; case TOKEN_OPEN_PAREN_TYPE: ++*openParens; @@ -583,6 +539,21 @@ static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, siz --*openParens; return i + 1; case TOKEN_OPERATOR_TYPE: + if (tree->token.type == TOKEN_ERROR_TYPE) { + switch (token->operatorValue) { + case OP_SUBTRACT: + token->operatorValue = OP_NEGATE; + break; + case OP_MULTIPLY: + token->operatorValue = OP_DEREFERENCE; + break; + case OP_NOT: + case OP_FLIP: + break; + default: + break; + } + } newPrecedence = _operatorPrecedence[token->operatorValue]; if (newPrecedence < precedence) { newTree = _parseTreeCreate(); diff --git a/src/debugger/test/lexer.c b/src/debugger/test/lexer.c index c9ba4e953..ef2f228e6 100644 --- a/src/debugger/test/lexer.c +++ b/src/debugger/test/lexer.c @@ -59,14 +59,6 @@ M_TEST_DEFINE(lexBinary) { assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 2); } -M_TEST_DEFINE(lexSigilBinary) { - LEX("%10"); - - assert_int_equal(LexVectorSize(lv), 1); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 2); -} - M_TEST_DEFINE(lexHex) { LEX("0x10"); @@ -111,13 +103,6 @@ M_TEST_DEFINE(lexTruncatedBinary) { assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_ERROR_TYPE); } -M_TEST_DEFINE(lexTruncatedSigilBinary) { - LEX("%"); - - assert_int_equal(LexVectorSize(lv), 1); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_ERROR_TYPE); -} - M_TEST_DEFINE(lexTruncatedSigilHex) { LEX("$"); @@ -372,16 +357,6 @@ M_TEST_DEFINE(lexNotNotOperator) { assert_int_equal(LexVectorGetPointer(lv, 2)->uintValue, 1); } -M_TEST_DEFINE(lexNegateOperator) { - LEX("-1"); - - assert_int_equal(LexVectorSize(lv), 2); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPERATOR_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 0)->operatorValue, OP_NEGATE); - assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1); -} - M_TEST_DEFINE(lexFlipOperator) { LEX("~1"); @@ -392,16 +367,6 @@ M_TEST_DEFINE(lexFlipOperator) { assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1); } -M_TEST_DEFINE(lexDereferenceOperator) { - LEX("*1"); - - assert_int_equal(LexVectorSize(lv), 2); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPERATOR_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 0)->operatorValue, OP_DEREFERENCE); - assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1); -} - M_TEST_DEFINE(lexEqualsOperator) { LEX("1=="); @@ -562,138 +527,6 @@ M_TEST_DEFINE(lexIdentifierShiftROperator) { assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_SHIFT_R); } -M_TEST_DEFINE(lexEqualsInvalidOperator) { - LEX("1=|"); - - assert_int_equal(LexVectorSize(lv), 3); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); - assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_ASSIGN); - assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); -} - -M_TEST_DEFINE(lexIdentifierEqualsInvalidOperator) { - LEX("x=|"); - - assert_int_equal(LexVectorSize(lv), 3); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); - assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); - assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_ASSIGN); - assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); -} - -M_TEST_DEFINE(lexNotInvalidOperator) { - LEX("1!|"); - - assert_int_equal(LexVectorSize(lv), 3); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); - assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_NOT); - assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); -} - -M_TEST_DEFINE(lexIdentifierNotInvalidOperator) { - LEX("x!|"); - - assert_int_equal(LexVectorSize(lv), 3); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); - assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); - assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_NOT); - assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); -} - -M_TEST_DEFINE(lexLessInvalidOperator) { - LEX("1<|"); - - assert_int_equal(LexVectorSize(lv), 3); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); - assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_LESS); - assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); -} - -M_TEST_DEFINE(lexIdentifierLessInvalidOperator) { - LEX("x<|"); - - assert_int_equal(LexVectorSize(lv), 3); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); - assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); - assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_LESS); - assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); -} - -M_TEST_DEFINE(lexGreaterInvalidOperator) { - LEX("1>|"); - - assert_int_equal(LexVectorSize(lv), 3); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); - assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_GREATER); - assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); -} - -M_TEST_DEFINE(lexIdentifierGreaterInvalidOperator) { - LEX("x>|"); - - assert_int_equal(LexVectorSize(lv), 3); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); - assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); - assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_GREATER); - assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); -} - -M_TEST_DEFINE(lexAndInvalidOperator) { - LEX("1&|"); - - assert_int_equal(LexVectorSize(lv), 3); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); - assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_AND); - assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); -} - -M_TEST_DEFINE(lexIdentifierAndInvalidOperator) { - LEX("x&|"); - - assert_int_equal(LexVectorSize(lv), 3); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); - assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); - assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_AND); - assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); -} - -M_TEST_DEFINE(lexOrInvalidOperator) { - LEX("1|>"); - - assert_int_equal(LexVectorSize(lv), 3); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); - assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_OR); - assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); -} - -M_TEST_DEFINE(lexIdentifierOrInvalidOperator) { - LEX("x|>"); - - assert_int_equal(LexVectorSize(lv), 3); - assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); - assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); - assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); - assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_OR); - assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); -} - M_TEST_DEFINE(lexSimpleExpression) { LEX("1+1"); @@ -834,7 +667,6 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Lexer, cmocka_unit_test(lexInt), cmocka_unit_test(lexDecimal), cmocka_unit_test(lexBinary), - cmocka_unit_test(lexSigilBinary), cmocka_unit_test(lexHex), cmocka_unit_test(lexSigilHex), cmocka_unit_test(lexSigilSegmentHex), @@ -844,7 +676,6 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Lexer, cmocka_unit_test(lexTruncatedHex), cmocka_unit_test(lexTruncatedSigilHex), cmocka_unit_test(lexTruncatedBinary), - cmocka_unit_test(lexTruncatedSigilBinary), cmocka_unit_test(lexIdentifier), cmocka_unit_test(lexAddOperator), cmocka_unit_test(lexIdentifierAddOperator), @@ -868,9 +699,7 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Lexer, cmocka_unit_test(lexIdentifierGreaterOperator), cmocka_unit_test(lexNotOperator), cmocka_unit_test(lexNotNotOperator), - cmocka_unit_test(lexNegateOperator), cmocka_unit_test(lexFlipOperator), - cmocka_unit_test(lexDereferenceOperator), cmocka_unit_test(lexEqualsOperator), cmocka_unit_test(lexIdentifierEqualsOperator), cmocka_unit_test(lexNotEqualsOperator), @@ -887,18 +716,6 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Lexer, cmocka_unit_test(lexIdentifierShiftLOperator), cmocka_unit_test(lexShiftROperator), cmocka_unit_test(lexIdentifierShiftROperator), - cmocka_unit_test(lexEqualsInvalidOperator), - cmocka_unit_test(lexIdentifierEqualsInvalidOperator), - cmocka_unit_test(lexNotInvalidOperator), - cmocka_unit_test(lexIdentifierNotInvalidOperator), - cmocka_unit_test(lexLessInvalidOperator), - cmocka_unit_test(lexIdentifierLessInvalidOperator), - cmocka_unit_test(lexGreaterInvalidOperator), - cmocka_unit_test(lexIdentifierGreaterInvalidOperator), - cmocka_unit_test(lexAndInvalidOperator), - cmocka_unit_test(lexIdentifierAndInvalidOperator), - cmocka_unit_test(lexOrInvalidOperator), - cmocka_unit_test(lexIdentifierOrInvalidOperator), cmocka_unit_test(lexSimpleExpression), cmocka_unit_test(lexOpenParen), cmocka_unit_test(lexCloseParen), diff --git a/src/debugger/test/parser.c b/src/debugger/test/parser.c index 412fc65c9..a2635f4fb 100644 --- a/src/debugger/test/parser.c +++ b/src/debugger/test/parser.c @@ -56,6 +56,12 @@ M_TEST_DEFINE(parseLexError) { assert_int_equal(tree->token.type, TOKEN_ERROR_TYPE); } +M_TEST_DEFINE(parseError) { + PARSE("1 2"); + + assert_int_equal(tree->token.type, TOKEN_ERROR_TYPE); +} + M_TEST_DEFINE(parseSimpleExpression) { PARSE("1+2"); @@ -108,11 +114,35 @@ M_TEST_DEFINE(parseParentheticalAddMultplyExpression) { assert_int_equal(tree->rhs->token.uintValue, 3); } +M_TEST_DEFINE(parseIsolatedOperator) { + PARSE("+"); + + assert_int_equal(tree->token.type, TOKEN_OPERATOR_TYPE); + assert_int_equal(tree->lhs->token.type, TOKEN_ERROR_TYPE); + assert_int_equal(tree->rhs->token.type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(parseUnaryChainedOperator) { + PARSE("1+*2"); + + assert_int_equal(tree->token.type, TOKEN_OPERATOR_TYPE); + assert_int_equal(tree->token.operatorValue, OP_ADD); + assert_int_equal(tree->lhs->token.type, TOKEN_UINT_TYPE); + assert_int_equal(tree->lhs->token.uintValue, 1); + assert_int_equal(tree->rhs->token.type, TOKEN_OPERATOR_TYPE); + assert_int_equal(tree->rhs->token.operatorValue, OP_DEREFERENCE); + assert_int_equal(tree->rhs->rhs->token.type, TOKEN_UINT_TYPE); + assert_int_equal(tree->rhs->rhs->token.uintValue, 2); +} + M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Parser, cmocka_unit_test(parseEmpty), cmocka_unit_test(parseInt), cmocka_unit_test(parseLexError), + cmocka_unit_test(parseError), cmocka_unit_test(parseSimpleExpression), cmocka_unit_test(parseAddMultplyExpression), cmocka_unit_test(parseParentheticalExpression), - cmocka_unit_test(parseParentheticalAddMultplyExpression)) + cmocka_unit_test(parseParentheticalAddMultplyExpression), + cmocka_unit_test(parseIsolatedOperator), + cmocka_unit_test(parseUnaryChainedOperator)) From 348c1fd74148b8c117765637e11c3180ed1c262c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 7 Mar 2019 18:53:41 -0800 Subject: [PATCH 06/10] Debugger: Fix nargs-style argument passing --- src/debugger/cli-debugger.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index e77e317ce..5c133325f 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -746,10 +746,11 @@ static int _tryCommands(struct CLIDebugger* debugger, struct CLIDebuggerCommandS if (commands[i].format[arg] == '+') { dvNext = _parseArg(debugger, args, adjusted, lastArg); - --args; + --arg; } else { nextArgMandatory = isupper(commands[i].format[arg]) || (commands[i].format[arg] == '*'); dvNext = _parseArg(debugger, args, adjusted, commands[i].format[arg]); + lastArg = commands[i].format[arg]; } args += adjusted; From 4aff3016384eb379c580d1276b616d1d2ec60a7b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 7 Mar 2019 18:54:18 -0800 Subject: [PATCH 07/10] Debugger: Print now chains arguments into a single expression --- src/debugger/cli-debugger.c | 121 ++++++++++++++++++++++++------------ 1 file changed, 81 insertions(+), 40 deletions(-) diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index 5c133325f..1cdfc20e8 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -28,6 +28,8 @@ const char* ERROR_MISSING_ARGS = "Arguments missing"; // TODO: share const char* ERROR_OVERFLOW = "Arguments overflow"; const char* ERROR_INVALID_ARGS = "Invalid arguments"; +static struct ParseTree* _parseTree(const char** string); + #if !defined(NDEBUG) && !defined(_WIN32) static void _breakInto(struct CLIDebugger*, struct CLIDebugVector*); #endif @@ -84,12 +86,12 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = { { "listw", _listWatchpoints, "", "List watchpoints" }, { "n", _next, "", "Execute next instruction" }, { "next", _next, "", "Execute next instruction" }, - { "p", _print, "I", "Print a value" }, - { "p/t", _printBin, "I", "Print a value as binary" }, - { "p/x", _printHex, "I", "Print a value as hexadecimal" }, - { "print", _print, "I", "Print a value" }, - { "print/t", _printBin, "I", "Print a value as binary" }, - { "print/x", _printHex, "I", "Print a value as hexadecimal" }, + { "p", _print, "S+", "Print a value" }, + { "p/t", _printBin, "S+", "Print a value as binary" }, + { "p/x", _printHex, "S+", "Print a value as hexadecimal" }, + { "print", _print, "S+", "Print a value" }, + { "print/t", _printBin, "S+", "Print a value as binary" }, + { "print/x", _printHex, "S+", "Print a value as hexadecimal" }, { "q", _quit, "", "Quit the emulator" }, { "quit", _quit, "", "Quit the emulator" }, { "reset", _reset, "", "Reset the emulation" }, @@ -158,33 +160,72 @@ static void _disassemble(struct CLIDebugger* debugger, struct CLIDebugVector* dv debugger->system->disassemble(debugger->system, dv); } -static void _print(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { - for (; dv; dv = dv->next) { - if (dv->segmentValue >= 0) { - debugger->backend->printf(debugger->backend, " $%02X:%04X", dv->segmentValue, dv->intValue); - continue; - } - debugger->backend->printf(debugger->backend, " %u", dv->intValue); +static bool _parseExpression(struct mDebugger* debugger, struct CLIDebugVector* dv, int32_t* intValue, int* segmentValue) { + size_t args = 0; + struct CLIDebugVector* accum; + for (accum = dv; accum; accum = accum->next) { + ++args; + } + const char** arglist = malloc(sizeof(const char*) * (args + 1)); + args = 0; + for (accum = dv; accum; accum = accum->next) { + arglist[args] = accum->charValue; + ++args; + } + arglist[args] = NULL; + struct ParseTree* tree = _parseTree(arglist); + free(arglist); + + if (!tree) { + return false; + } + if (!mDebuggerEvaluateParseTree(debugger, tree, intValue, segmentValue)) { + parseFree(tree); + free(tree); + return false; + } + parseFree(tree); + free(tree); + return true; +} + +static void _print(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + int32_t intValue = 0; + int segmentValue = -1; + if (!_parseExpression(&debugger->d, dv, &intValue, &segmentValue)) { + debugger->backend->printf(debugger->backend, "Parse error\n"); + return; + } + if (segmentValue >= 0) { + debugger->backend->printf(debugger->backend, " $%02X:%04X\n", segmentValue, intValue); + } else { + debugger->backend->printf(debugger->backend, " %u\n", intValue); } - debugger->backend->printf(debugger->backend, "\n"); } static void _printBin(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { - for (; dv; dv = dv->next) { - debugger->backend->printf(debugger->backend, " 0b"); - int i = 32; - while (i--) { - debugger->backend->printf(debugger->backend, "%u", (dv->intValue >> i) & 1); - } + int32_t intValue = 0; + int segmentValue = -1; + if (!_parseExpression(&debugger->d, dv, &intValue, &segmentValue)) { + debugger->backend->printf(debugger->backend, "Parse error\n"); + return; + } + debugger->backend->printf(debugger->backend, " 0b"); + int i = 32; + while (i--) { + debugger->backend->printf(debugger->backend, "%u", (intValue >> i) & 1); } debugger->backend->printf(debugger->backend, "\n"); } static void _printHex(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { - for (; dv; dv = dv->next) { - debugger->backend->printf(debugger->backend, " 0x%08X", dv->intValue); + int32_t intValue = 0; + int segmentValue = -1; + if (!_parseExpression(&debugger->d, dv, &intValue, &segmentValue)) { + debugger->backend->printf(debugger->backend, "Parse error\n"); + return; } - debugger->backend->printf(debugger->backend, "\n"); + debugger->backend->printf(debugger->backend, " 0x%08X\n", intValue); } static void _printHelp(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { @@ -460,31 +501,31 @@ static void _source(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { } #endif -static struct ParseTree* _parseTree(const char* string) { +static struct ParseTree* _parseTree(const char** string) { struct LexVector lv; bool error = false; LexVectorInit(&lv, 0); - size_t length = strlen(string); - size_t adjusted = lexExpression(&lv, string, length, NULL); - struct ParseTree* tree = malloc(sizeof(*tree)); - if (!adjusted) { - error = true; - } else { - parseLexedExpression(tree, &lv); - - if (adjusted > length) { + size_t i; + for (i = 0; string[i]; ++i) { + size_t length = strlen(string[i]); + size_t adjusted = lexExpression(&lv, string[i], length, NULL); + if (!adjusted || adjusted > length) { error = true; - } else { - length -= adjusted; - string += adjusted; } } + struct ParseTree* tree = NULL; + if (!error) { + tree = malloc(sizeof(*tree)); + parseLexedExpression(tree, &lv); + } lexFree(&lv); LexVectorClear(&lv); LexVectorDeinit(&lv); if (error) { - parseFree(tree); - free(tree); + if (tree) { + parseFree(tree); + free(tree); + } return NULL; } else { return tree; @@ -502,7 +543,7 @@ static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* .type = BREAKPOINT_HARDWARE }; if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) { - struct ParseTree* tree = _parseTree(dv->next->charValue); + struct ParseTree* tree = _parseTree((const char*[]) { dv->next->charValue, NULL }); if (tree) { breakpoint.condition = tree; } else { @@ -528,7 +569,7 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* .type = type }; if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) { - struct ParseTree* tree = _parseTree(dv->next->charValue); + struct ParseTree* tree = _parseTree((const char*[]) { dv->next->charValue, NULL }); if (tree) { watchpoint.condition = tree; } else { From 0425fa805c464ed8bae501039738ab051b0d0b39 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 8 Mar 2019 09:55:51 -0800 Subject: [PATCH 08/10] Core: Add keysRead callback --- CHANGES | 1 + include/mgba/core/interface.h | 1 + src/gb/io.c | 9 +++++++++ src/gba/io.c | 9 +++++++++ 4 files changed, 20 insertions(+) diff --git a/CHANGES b/CHANGES index 52b42650c..eb0a4e69d 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,7 @@ Misc: - LR35902: Support PC-relative opcode decoding - Qt: Improve camera initialization - Qt: Support switching webcams + - Core: Add keysRead callback 0.7.1: (2019-02-24) Bugfixes: diff --git a/include/mgba/core/interface.h b/include/mgba/core/interface.h index 0581c0bf2..2e9ff63a7 100644 --- a/include/mgba/core/interface.h +++ b/include/mgba/core/interface.h @@ -86,6 +86,7 @@ struct mCoreCallbacks { void (*videoFrameEnded)(void* context); void (*coreCrashed)(void* context); void (*sleep)(void* context); + void (*keysRead)(void* context); }; DECLARE_VECTOR(mCoreCallbacksList, struct mCoreCallbacks); diff --git a/src/gb/io.c b/src/gb/io.c index 84b3a35bc..beecce12a 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -576,6 +576,15 @@ static uint8_t _readKeysFiltered(struct GB* gb) { uint8_t GBIORead(struct GB* gb, unsigned address) { switch (address) { case REG_JOYP: + { + size_t c; + for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c); + if (callbacks->keysRead) { + callbacks->keysRead(callbacks->context); + } + } + } return _readKeysFiltered(gb); case REG_IE: return gb->memory.ie; diff --git a/src/gba/io.c b/src/gba/io.c index 68c77c8d5..2946e6ab7 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -725,6 +725,15 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { break; case REG_KEYINPUT: + { + size_t c; + for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c); + if (callbacks->keysRead) { + callbacks->keysRead(callbacks->context); + } + } + } if (gba->rr && gba->rr->isPlaying(gba->rr)) { return 0x3FF ^ gba->rr->queryInput(gba->rr); } else { From c6556260955cb92d5f42e95d248e91c44515a915 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 9 Mar 2019 11:27:52 -0800 Subject: [PATCH 09/10] GBA Video: Fix scanline cache with scale factor change edge cases --- CHANGES | 1 + src/gba/renderers/video-software.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGES b/CHANGES index eb0a4e69d..80b70b01c 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,7 @@ Emulation fixes: - GB Video: Delay LYC STAT check (fixes mgba.io/i/1331) - GB Video: Fix window being enabled mid-scanline (fixes mgba.io/i/1328) - GB I/O: Filter IE top bits properly (fixes mgba.io/i/1329) + - GBA Video: Fix scanline cache with scale factor change edge cases Other fixes: - Qt: More app metadata fixes - Qt: Fix load recent from archive (fixes mgba.io/i/1325) diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index f31239d09..6c4853bab 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -539,6 +539,14 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render dirty = true; } + if (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) != 0) { + if (softwareRenderer->cache[y].scale[0][0] != softwareRenderer->bg[2].sx || + softwareRenderer->cache[y].scale[0][1] != softwareRenderer->bg[2].sy || + softwareRenderer->cache[y].scale[1][0] != softwareRenderer->bg[3].sx || + softwareRenderer->cache[y].scale[1][1] != softwareRenderer->bg[3].sy) { + dirty = true; + } + } softwareRenderer->cache[y].scale[0][0] = softwareRenderer->bg[2].sx; softwareRenderer->cache[y].scale[0][1] = softwareRenderer->bg[2].sy; softwareRenderer->cache[y].scale[1][0] = softwareRenderer->bg[3].sx; From a04cb97653efc918e4879771c6ae06000200b43c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 9 Mar 2019 11:31:38 -0800 Subject: [PATCH 10/10] GBA DMA: Fix DMA0-2 lengths (fixes #1344) --- CHANGES | 1 + src/gba/io.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 80b70b01c..3cbbde329 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,7 @@ Emulation fixes: - GB Video: Fix window being enabled mid-scanline (fixes mgba.io/i/1328) - GB I/O: Filter IE top bits properly (fixes mgba.io/i/1329) - GBA Video: Fix scanline cache with scale factor change edge cases + - GBA DMA: Fix DMA0-2 lengths (fixes mgba.io/i/1344) Other fixes: - Qt: More app metadata fixes - Qt: Fix load recent from archive (fixes mgba.io/i/1325) diff --git a/src/gba/io.c b/src/gba/io.c index 2946e6ab7..c5a5a7c71 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -461,19 +461,19 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { break; case REG_DMA0CNT_LO: - GBADMAWriteCNT_LO(gba, 0, value); + GBADMAWriteCNT_LO(gba, 0, value & 0x3FFF); break; case REG_DMA0CNT_HI: value = GBADMAWriteCNT_HI(gba, 0, value); break; case REG_DMA1CNT_LO: - GBADMAWriteCNT_LO(gba, 1, value); + GBADMAWriteCNT_LO(gba, 1, value & 0x3FFF); break; case REG_DMA1CNT_HI: value = GBADMAWriteCNT_HI(gba, 1, value); break; case REG_DMA2CNT_LO: - GBADMAWriteCNT_LO(gba, 2, value); + GBADMAWriteCNT_LO(gba, 2, value & 0x3FFF); break; case REG_DMA2CNT_HI: value = GBADMAWriteCNT_HI(gba, 2, value);