Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2021-06-11 19:44:02 -07:00
commit b5d7a37aa6
30 changed files with 3124 additions and 2887 deletions

View File

@ -48,6 +48,7 @@ Features:
- New unlicensed GB mappers: Pokémon Jade/Diamond, BBD, and Hitek
- Stack tracing tools in ARM debugger (by ahigerd)
- Command scripts for CLI debugger (by ahigerd)
- Scheduled event dumping in CLI debugger
- ARM disassembler now resolves addresses to symbol names
- Add Game Boy Player feature support to ports
- Individual window types can now be toggled in debugging views
@ -130,6 +131,7 @@ Misc:
- Core: Improve support for ROM patch cheats, supporting disabling overlapping patches
- GB: Allow pausing event loop while CPU is blocked
- GB: Add support for sleep and shutdown callbacks
- GB Core: Return the current number of banks for ROM/SRAM, not theoretical max
- GB I/O: Implement preliminary support for PCM12/PCM34 (closes mgba.io/i/1468)
- GBA: Allow pausing event loop while CPU is blocked
- GBA BIOS: Division by zero should emit a FATAL error
@ -152,6 +154,8 @@ Misc:
- Qt: Redo OpenGL context thread handling (fixes mgba.io/i/1724)
- Qt: Discard additional frame draws if waiting fails
- Qt: Unify monospace font usage
- Qt: Add button to jump to log settings
- Qt: Use relative paths in portable mode when applicable (fixes mgba.io/i/838)
- SDL: Fall back to sw blit if OpenGL init fails
- Util: Reset vector size on deinit
- VFS: Change semantics of VFile.sync on mapped files (fixes mgba.io/i/1730)

View File

@ -203,6 +203,7 @@ void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc);
void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size);
void* mCoreGetMemoryBlockMasked(struct mCore* core, uint32_t start, size_t* size, uint32_t mask);
const struct mCoreMemoryBlock* mCoreGetMemoryBlockInfo(struct mCore* core, uint32_t address);
#ifdef USE_ELF
struct ELF;

View File

@ -107,14 +107,17 @@ static void _patchROM(struct mCheatDevice* device, struct mCheatSet* cheats) {
struct mCheatPatch* patch = mCheatPatchListGetPointer(&cheats->romPatches, i);
int segment = -1;
if (patch->check && patch->segment < 0) {
int maxSegment = 0;
for (segment = 0; segment < maxSegment; ++segment) {
const struct mCoreMemoryBlock* block = mCoreGetMemoryBlockInfo(device->p, patch->address);
if (!block) {
continue;
}
for (segment = 0; segment < block->maxSegment; ++segment) {
uint32_t value = _readMemSegment(device->p, patch->address, segment, patch->width);
if (value == patch->checkValue) {
break;
}
}
if (segment == maxSegment) {
if (segment == block->maxSegment) {
continue;
}
}

View File

@ -373,6 +373,17 @@ void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size) {
}
void* mCoreGetMemoryBlockMasked(struct mCore* core, uint32_t start, size_t* size, uint32_t mask) {
const struct mCoreMemoryBlock* block = mCoreGetMemoryBlockInfo(core, start);
if (!block || !(block->flags & mask)) {
return NULL;
}
uint8_t* out = core->getMemoryBlock(core, block->id, size);
out += start - block->start;
*size -= start - block->start;
return out;
}
const struct mCoreMemoryBlock* mCoreGetMemoryBlockInfo(struct mCore* core, uint32_t address) {
const struct mCoreMemoryBlock* blocks;
size_t nBlocks = core->listMemoryBlocks(core, &blocks);
size_t i;
@ -380,19 +391,13 @@ void* mCoreGetMemoryBlockMasked(struct mCore* core, uint32_t start, size_t* size
if (!(blocks[i].flags & mCORE_MEMORY_MAPPED)) {
continue;
}
if (!(blocks[i].flags & mask)) {
if (address < blocks[i].start) {
continue;
}
if (start < blocks[i].start) {
if (address >= blocks[i].start + blocks[i].size) {
continue;
}
if (start >= blocks[i].start + blocks[i].size) {
continue;
}
uint8_t* out = core->getMemoryBlock(core, blocks[i].id, size);
out += start - blocks[i].start;
*size -= start - blocks[i].start;
return out;
return &blocks[i];
}
return NULL;
}

View File

@ -8,6 +8,7 @@
#include <mgba/internal/debugger/symbols.h>
#include <mgba/core/core.h>
#include <mgba/core/timing.h>
#include <mgba/core/version.h>
#include <mgba/internal/debugger/parser.h>
#include <mgba-util/string.h>
@ -66,6 +67,7 @@ static void _writeWord(struct CLIDebugger*, struct CLIDebugVector*);
static void _dumpByte(struct CLIDebugger*, struct CLIDebugVector*);
static void _dumpHalfword(struct CLIDebugger*, struct CLIDebugVector*);
static void _dumpWord(struct CLIDebugger*, struct CLIDebugVector*);
static void _events(struct CLIDebugger*, struct CLIDebugVector*);
#ifdef ENABLE_SCRIPTING
static void _source(struct CLIDebugger*, struct CLIDebugVector*);
#endif
@ -81,6 +83,7 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
{ "continue", _continue, "", "Continue execution" },
{ "delete", _clearBreakpoint, "I", "Delete a breakpoint or watchpoint" },
{ "disassemble", _disassemble, "Ii", "Disassemble instructions" },
{ "events", _events, "", "Print list of scheduled events" },
{ "finish", _finish, "", "Execute until current stack frame returns" },
{ "help", _printHelp, "S", "Print help" },
{ "listb", _listBreakpoints, "", "List breakpoints" },
@ -768,6 +771,15 @@ static void _printStatus(struct CLIDebugger* debugger, struct CLIDebugVector* dv
debugger->system->printStatus(debugger->system);
}
static void _events(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
UNUSED(dv);
struct mTiming* timing = debugger->d.core->timing;
struct mTimingEvent* next = timing->root;
for (; next; next = next->next) {
debugger->backend->printf(debugger->backend, "%s in %i cycles\n", next->name, mTimingUntil(timing, next));
}
}
struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* string, size_t length) {
if (!string || length < 1) {
return 0;

View File

@ -80,6 +80,7 @@ struct GBCore {
const struct Configuration* overrides;
struct mDebuggerPlatform* debuggerPlatform;
struct mCheatDevice* cheatDevice;
struct mCoreMemoryBlock memoryBlocks[8];
};
static bool _GBCoreInit(struct mCore* core) {
@ -101,6 +102,7 @@ static bool _GBCoreInit(struct mCore* core) {
#ifndef MINIMAL_CORE
gbcore->logContext = NULL;
#endif
memcpy(gbcore->memoryBlocks, _GBMemoryBlocks, sizeof(_GBMemoryBlocks));
GBCreate(gb);
memset(gbcore->components, 0, sizeof(gbcore->components));
@ -571,6 +573,26 @@ static void _GBCoreReset(struct mCore* core) {
}
#endif
if (gb->model < GB_MODEL_CGB) {
memcpy(gbcore->memoryBlocks, _GBMemoryBlocks, sizeof(_GBMemoryBlocks));
} else {
memcpy(gbcore->memoryBlocks, _GBCMemoryBlocks, sizeof(_GBCMemoryBlocks));
}
size_t i;
for (i = 0; i < sizeof(gbcore->memoryBlocks) / sizeof(*gbcore->memoryBlocks); ++i) {
if (gbcore->memoryBlocks[i].id == GB_REGION_CART_BANK0) {
gbcore->memoryBlocks[i].maxSegment = gb->memory.romSize / GB_SIZE_CART_BANK0;
} else if (gbcore->memoryBlocks[i].id == GB_REGION_EXTERNAL_RAM) {
gbcore->memoryBlocks[i].maxSegment = gb->sramSize / GB_SIZE_EXTERNAL_RAM;
} else {
continue;
}
if (gbcore->memoryBlocks[i].maxSegment) {
--gbcore->memoryBlocks[i].maxSegment;
}
}
SM83Reset(core->cpu);
if (core->opts.skipBios) {
@ -755,20 +777,9 @@ static void _GBCoreRawWrite32(struct mCore* core, uint32_t address, int segment,
}
size_t _GBListMemoryBlocks(const struct mCore* core, const struct mCoreMemoryBlock** blocks) {
const struct GB* gb = core->board;
switch (gb->model) {
case GB_MODEL_DMG:
case GB_MODEL_MGB:
case GB_MODEL_SGB:
case GB_MODEL_SGB2:
default:
*blocks = _GBMemoryBlocks;
return sizeof(_GBMemoryBlocks) / sizeof(*_GBMemoryBlocks);
case GB_MODEL_CGB:
case GB_MODEL_AGB:
*blocks = _GBCMemoryBlocks;
return sizeof(_GBCMemoryBlocks) / sizeof(*_GBCMemoryBlocks);
}
struct GBCore* gbcore = (struct GBCore*) core;
*blocks = gbcore->memoryBlocks;
return sizeof(gbcore->memoryBlocks) / sizeof(*gbcore->memoryBlocks);
}
void* _GBGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) {

View File

@ -707,15 +707,16 @@ void GBProcessEvents(struct SM83Core* cpu) {
int32_t nextEvent;
cpu->cycles = 0;
#ifdef USE_DEBUGGERS
gb->timing.globalCycles += cycles;
#endif
cpu->nextEvent = INT_MAX;
nextEvent = cycles;
do {
#ifdef USE_DEBUGGERS
gb->timing.globalCycles += nextEvent;
#endif
nextEvent = mTimingTick(&gb->timing, nextEvent);
} while (gb->cpuBlocked && !gb->earlyExit);
} while (gb->cpuBlocked);
// This loop cannot early exit until the SM83 run loop properly handles mid-M-cycle-exits
cpu->nextEvent = nextEvent;
if (cpu->halted) {

View File

@ -595,10 +595,10 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
memset(&softwareRenderer->row[startX], 0, (endX - startX) * sizeof(softwareRenderer->row[0]));
}
if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc) && !softwareRenderer->d.disableOBJ) {
if (startX == 0) {
_cleanOAM(softwareRenderer, y);
}
if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc) && !softwareRenderer->d.disableOBJ) {
int i;
for (i = 0; i < softwareRenderer->objMax; ++i) {
GBVideoSoftwareRendererDrawObj(softwareRenderer, &softwareRenderer->obj[i], startX, endX, y);

View File

@ -288,7 +288,7 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
int32_t cycles = cpu->cycles;
cpu->cycles = 0;
#ifdef USE_DEBUGGERS
gba->timing.globalCycles += cycles;
gba->timing.globalCycles += cycles < nextEvent ? nextEvent : cycles;
#endif
#ifndef NDEBUG
if (cycles < 0) {

View File

@ -6,13 +6,14 @@
#include "LogView.h"
#include "LogController.h"
#include "Window.h"
#include <QTextBlock>
#include <QTextCursor>
using namespace QGBA;
LogView::LogView(LogController* log, QWidget* parent)
LogView::LogView(LogController* log, Window* window, QWidget* parent)
: QWidget(parent)
{
m_ui.setupUi(this);
@ -38,6 +39,9 @@ LogView::LogView(LogController* log, QWidget* parent)
setLevel(mLOG_GAME_ERROR, set);
});
connect(m_ui.clear, &QAbstractButton::clicked, this, &LogView::clear);
connect(m_ui.advanced, &QAbstractButton::clicked, this, [window]() {
window->openSettingsWindow(SettingsView::Page::LOGGING);
});
connect(m_ui.maxLines, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
this, &LogView::setMaxLines);
m_ui.maxLines->setValue(DEFAULT_LINE_LIMIT);

View File

@ -13,12 +13,13 @@
namespace QGBA {
class LogController;
class Window;
class LogView : public QWidget {
Q_OBJECT
public:
LogView(LogController* log, QWidget* parent = nullptr);
LogView(LogController* log, Window* window, QWidget* parent = nullptr);
signals:
void levelsEnabled(int levels);

View File

@ -102,6 +102,13 @@
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="advanced">
<property name="text">
<string>Advanced settings</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">

View File

@ -24,7 +24,7 @@
using namespace QGBA;
SettingsView::SettingsView(ConfigController* controller, InputController* inputController, LogController* logController, QWidget* parent)
SettingsView::SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, LogController* logController, QWidget* parent)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
, m_controller(controller)
, m_input(inputController)
@ -32,7 +32,17 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
{
m_ui.setupUi(this);
m_pageIndex[Page::AV] = 0;
m_pageIndex[Page::INTERFACE] = 1;
m_pageIndex[Page::EMULATION] = 2;
m_pageIndex[Page::ENHANCEMENTS] = 3;
m_pageIndex[Page::BIOS] = 4;
m_pageIndex[Page::PATHS] = 5;
m_pageIndex[Page::LOGGING] = 6;
#ifdef M_CORE_GB
m_pageIndex[Page::GB] = 7;
for (auto model : GameBoy::modelList()) {
m_ui.gbModel->addItem(GameBoy::modelName(model), model);
m_ui.sgbModel->addItem(GameBoy::modelName(model), model);
@ -75,11 +85,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
}
});
connect(m_ui.savegameBrowse, &QAbstractButton::pressed, [this] () {
QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory");
if (!path.isNull()) {
m_ui.savegameSameDir->setChecked(false);
m_ui.savegamePath->setText(path);
}
selectPath(m_ui.savegamePath, m_ui.savegameSameDir);
});
if (m_ui.savestatePath->text().isEmpty()) {
@ -91,11 +97,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
}
});
connect(m_ui.savestateBrowse, &QAbstractButton::pressed, [this] () {
QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory");
if (!path.isNull()) {
m_ui.savestateSameDir->setChecked(false);
m_ui.savestatePath->setText(path);
}
selectPath(m_ui.savestatePath, m_ui.savestateSameDir);
});
if (m_ui.screenshotPath->text().isEmpty()) {
@ -107,11 +109,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
}
});
connect(m_ui.screenshotBrowse, &QAbstractButton::pressed, [this] () {
QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory");
if (!path.isNull()) {
m_ui.screenshotSameDir->setChecked(false);
m_ui.screenshotPath->setText(path);
}
selectPath(m_ui.screenshotPath, m_ui.screenshotSameDir);
});
if (m_ui.patchPath->text().isEmpty()) {
@ -123,11 +121,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
}
});
connect(m_ui.patchBrowse, &QAbstractButton::pressed, [this] () {
QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory");
if (!path.isNull()) {
m_ui.patchSameDir->setChecked(false);
m_ui.patchPath->setText(path);
}
selectPath(m_ui.patchPath, m_ui.patchSameDir);
});
if (m_ui.cheatsPath->text().isEmpty()) {
@ -139,11 +133,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
}
});
connect(m_ui.cheatsBrowse, &QAbstractButton::pressed, [this] () {
QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory");
if (!path.isNull()) {
m_ui.cheatsSameDir->setChecked(false);
m_ui.cheatsPath->setText(path);
}
selectPath(m_ui.cheatsPath, m_ui.cheatsSameDir);
});
connect(m_ui.clearCache, &QAbstractButton::pressed, this, &SettingsView::libraryCleared);
@ -325,6 +315,11 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
m_ui.logFile->setText(path);
}
});
ShortcutView* shortcutView = new ShortcutView();
shortcutView->setController(shortcutController);
shortcutView->setInputController(inputController);
addPage(tr("Shortcuts"), shortcutView, Page::SHORTCUTS);
}
SettingsView::~SettingsView() {
@ -352,10 +347,33 @@ void SettingsView::setShaderSelector(ShaderSelector* shaderSelector) {
#endif
}
void SettingsView::selectPage(SettingsView::Page page) {
m_ui.tabs->setCurrentRow(m_pageIndex[page]);
}
QString SettingsView::makePortablePath(const QString& path) {
if (m_controller->isPortable()) {
QDir configDir(m_controller->configDir());
QFileInfo pathInfo(path);
if (pathInfo.canonicalPath() == configDir.canonicalPath()) {
return configDir.relativeFilePath(pathInfo.canonicalFilePath());
}
}
return path;
}
void SettingsView::selectBios(QLineEdit* bios) {
QString filename = GBAApp::app()->getOpenFileName(this, tr("Select BIOS"));
if (!filename.isEmpty()) {
bios->setText(filename);
bios->setText(makePortablePath(filename));
}
}
void SettingsView::selectPath(QLineEdit* field, QCheckBox* sameDir) {
QString path = GBAApp::app()->getOpenDirectoryName(this, tr("Select directory"));
if (!path.isNull()) {
sameDir->setChecked(false);
field->setText(makePortablePath(path));
}
}
@ -705,6 +723,12 @@ void SettingsView::reloadConfig() {
loadSetting("videoScale", m_ui.videoScale, 1);
}
void SettingsView::addPage(const QString& name, QWidget* view, Page index) {
m_pageIndex[index] = m_ui.tabs->count();
m_ui.tabs->addItem(name);
m_ui.stackedWidget->addWidget(view);
}
void SettingsView::saveSetting(const char* key, const QAbstractButton* field) {
m_controller->setOption(key, field->isChecked());
m_controller->updateOption(key);

View File

@ -6,6 +6,7 @@
#pragma once
#include <QDialog>
#include <QMap>
#include "ColorPicker.h"
#include "LogConfigModel.h"
@ -24,12 +25,28 @@ class ConfigController;
class InputController;
class InputIndex;
class ShaderSelector;
class ShortcutController;
class SettingsView : public QDialog {
Q_OBJECT
public:
SettingsView(ConfigController* controller, InputController* inputController, LogController* logController, QWidget* parent = nullptr);
enum class Page {
AV,
INTERFACE,
EMULATION,
ENHANCEMENTS,
BIOS,
PATHS,
LOGGING,
GB,
KEYBOARD,
CONTROLLERS,
SHORTCUTS,
SHADERS,
};
SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, LogController* logController, QWidget* parent = nullptr);
~SettingsView();
void setShaderSelector(ShaderSelector* shaderSelector);
@ -45,8 +62,12 @@ signals:
void languageChanged();
void libraryCleared();
public slots:
void selectPage(Page);
private slots:
void selectBios(QLineEdit*);
void selectPath(QLineEdit*, QCheckBox*);
void updateConfig();
void reloadConfig();
@ -63,6 +84,12 @@ private:
ColorPicker m_colorPickers[12];
#endif
QMap<Page, int> m_pageIndex;
QString makePortablePath(const QString& path);
void addPage(const QString& name, QWidget* view, Page index);
void saveSetting(const char* key, const QAbstractButton*);
void saveSetting(const char* key, const QComboBox*);
void saveSetting(const char* key, const QDoubleSpinBox*);

View File

@ -50,7 +50,6 @@
#include "ReportView.h"
#include "ROMInfo.h"
#include "SensorView.h"
#include "SettingsView.h"
#include "ShaderSelector.h"
#include "ShortcutController.h"
#include "TileView.h"
@ -104,7 +103,7 @@ using namespace QGBA;
Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWidget* parent)
: QMainWindow(parent)
, m_manager(manager)
, m_logView(new LogView(&m_log))
, m_logView(new LogView(&m_log, this))
, m_screenWidget(new WindowBackground())
, m_config(config)
, m_inputController(playerId, this)
@ -501,7 +500,11 @@ void Window::exportSharkport() {
}
void Window::openSettingsWindow() {
SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController, &m_log);
openSettingsWindow(SettingsView::Page::AV);
}
void Window::openSettingsWindow(SettingsView::Page page) {
SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController, m_shortcutController, &m_log);
#if defined(BUILD_GL) || defined(BUILD_GLES2)
if (m_display->supportsShaders()) {
settingsWindow->setShaderSelector(m_shaderView.get());
@ -518,6 +521,7 @@ void Window::openSettingsWindow() {
connect(settingsWindow, &SettingsView::libraryCleared, m_libraryView, &LibraryController::clear);
#endif
openView(settingsWindow);
settingsWindow->selectPage(page);
}
void Window::startVideoLog() {

View File

@ -22,6 +22,8 @@
#include "InputController.h"
#include "LoadSaveState.h"
#include "LogController.h"
#include "SettingsView.h"
struct mArguments;
namespace QGBA {
@ -95,6 +97,7 @@ public slots:
void exportSharkport();
void openSettingsWindow();
void openSettingsWindow(SettingsView::Page);
void startVideoLog();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -915,7 +915,7 @@ void _gameUnloaded(struct mGUIRunner* runner) {
void _gameLoaded(struct mGUIRunner* runner) {
reconfigureScreen(runner);
if (runner->core->platform(runner->core) == PLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
if (runner->core->platform(runner->core) == mPLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
int i;
for (i = 0; i < 6; ++i) {
u32 result = WPAD_SetMotionPlus(0, 1);