diff --git a/CHANGES b/CHANGES index 7745f02da..a9b845585 100644 --- a/CHANGES +++ b/CHANGES @@ -23,17 +23,28 @@ Features: - Improved logging configuration - One-Player BattleChip/Progress/Beast Link Gate support - Add Game Boy Color palettes for original Game Boy games -Bugfixes: + - Debugger: Add unary operators and memory dereferencing + - GB: Expose platform information to CLI debugger +Emulation fixes: - GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208) - GBA: Reset now reloads multiboot ROMs - GBA BIOS: Fix multiboot entry point (fixes Magic Floor) + - 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) +Other fixes: - Qt: More app metadata fixes + - Qt: Fix load recent from archive (fixes mgba.io/i/1325) + - LR35902: Fix disassembly of several CB-prefix instructions Misc: - GBA Savedata: EEPROM performance fixes - GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash - GB Memory: Support running from blocked memory - Qt: Don't unload ROM immediately if it crashes - Debugger: Add breakpoint and watchpoint listing + - Qt: Add missing HEVC NVENC option (fixes mgba.io/i/1323) + - LR35902: Support PC-relative opcode decoding + - Qt: Improve camera initialization 0.7.1: (2019-02-24) Bugfixes: diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c7aee905..74906db99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -765,6 +765,7 @@ if(M_CORE_GB) ${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/debugger.c ${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/memory-debugger.c ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/debugger/cli.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/debugger/debugger.c ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/debugger/symbols.c) list(APPEND TEST_SRC ${LR35902_TEST_SRC} diff --git a/cinema/gb/window/007wne-hud/baseline_0000.png b/cinema/gb/window/007wne-hud/baseline_0000.png new file mode 100644 index 000000000..9ae2a2a5c Binary files /dev/null and b/cinema/gb/window/007wne-hud/baseline_0000.png differ diff --git a/cinema/gb/window/007wne-hud/baseline_0001.png b/cinema/gb/window/007wne-hud/baseline_0001.png new file mode 100644 index 000000000..9ae2a2a5c Binary files /dev/null and b/cinema/gb/window/007wne-hud/baseline_0001.png differ diff --git a/cinema/gb/window/007wne-hud/baseline_0002.png b/cinema/gb/window/007wne-hud/baseline_0002.png new file mode 100644 index 000000000..9ae2a2a5c Binary files /dev/null and b/cinema/gb/window/007wne-hud/baseline_0002.png differ diff --git a/cinema/gb/window/007wne-hud/baseline_0003.png b/cinema/gb/window/007wne-hud/baseline_0003.png new file mode 100644 index 000000000..9ae2a2a5c Binary files /dev/null and b/cinema/gb/window/007wne-hud/baseline_0003.png differ diff --git a/cinema/gb/window/007wne-hud/test.mvl b/cinema/gb/window/007wne-hud/test.mvl new file mode 100644 index 000000000..d5dc5d288 Binary files /dev/null and b/cinema/gb/window/007wne-hud/test.mvl differ diff --git a/include/mgba/internal/debugger/parser.h b/include/mgba/internal/debugger/parser.h index 6092c1f8c..37f194ddb 100644 --- a/include/mgba/internal/debugger/parser.h +++ b/include/mgba/internal/debugger/parser.h @@ -38,6 +38,7 @@ enum Operation { OP_NOT, OP_SHIFT_L, OP_SHIFT_R, + OP_DEREFERENCE, }; struct Token { diff --git a/include/mgba/internal/gb/debugger/debugger.h b/include/mgba/internal/gb/debugger/debugger.h new file mode 100644 index 000000000..2eeee8ed1 --- /dev/null +++ b/include/mgba/internal/gb/debugger/debugger.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2013-2019 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef GB_DEBUGGER_H +#define GB_DEBUGGER_H + +#include + +CXX_GUARD_START + +struct GB; +struct mDebuggerPlatform; + +struct mDebuggerPlatform* GBDebuggerCreate(struct GB* gb); + +CXX_GUARD_END + +#endif \ No newline at end of file diff --git a/include/mgba/internal/gb/renderers/software.h b/include/mgba/internal/gb/renderers/software.h index 3b8f7a210..fe4b1ff98 100644 --- a/include/mgba/internal/gb/renderers/software.h +++ b/include/mgba/internal/gb/renderers/software.h @@ -32,7 +32,9 @@ struct GBVideoSoftwareRenderer { uint8_t wy; uint8_t wx; uint8_t currentWy; + uint8_t currentWx; int lastY; + int lastX; bool hasWindow; GBRegisterLCDC lcdc; diff --git a/include/mgba/internal/lr35902/debugger/debugger.h b/include/mgba/internal/lr35902/debugger/debugger.h index 1114c5c1a..6e62b6ce9 100644 --- a/include/mgba/internal/lr35902/debugger/debugger.h +++ b/include/mgba/internal/lr35902/debugger/debugger.h @@ -20,6 +20,7 @@ struct LR35902Segment { const char* name; }; +struct CLIDebuggerSystem; struct LR35902Debugger { struct mDebuggerPlatform d; struct LR35902Core* cpu; @@ -31,6 +32,8 @@ struct LR35902Debugger { ssize_t nextId; const struct LR35902Segment* segments; + + void (*printStatus)(struct CLIDebuggerSystem*); }; struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void); diff --git a/include/mgba/internal/lr35902/decoder.h b/include/mgba/internal/lr35902/decoder.h index 101a104c3..ba9ceec36 100644 --- a/include/mgba/internal/lr35902/decoder.h +++ b/include/mgba/internal/lr35902/decoder.h @@ -86,6 +86,7 @@ enum { LR35902_OP_FLAG_MEMORY = 2, LR35902_OP_FLAG_INCREMENT = 4, LR35902_OP_FLAG_DECREMENT = 8, + LR35902_OP_FLAG_RELATIVE = 16, }; struct LR35902Operand { @@ -104,7 +105,7 @@ struct LR35902InstructionInfo { }; size_t LR35902Decode(uint8_t opcode, struct LR35902InstructionInfo* info); -int LR35902Disassemble(struct LR35902InstructionInfo* info, char* buffer, int blen); +int LR35902Disassemble(struct LR35902InstructionInfo* info, uint16_t pc, char* buffer, int blen); CXX_GUARD_END diff --git a/src/debugger/parser.c b/src/debugger/parser.c index 41eafc6d4..9d8db0262 100644 --- a/src/debugger/parser.c +++ b/src/debugger/parser.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include #include @@ -89,52 +90,77 @@ static void _lexOperator(struct LexVector* lv, char operator, enum LexState* sta break; } *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 = LexVectorAppend(lv); - lvNext->type = TOKEN_OPERATOR_TYPE; - *state = LEX_EXPECT_OPERATOR2; + struct Token lvNext; + lvNext.type = TOKEN_OPERATOR_TYPE; switch (operator) { case '=': - lvNext->operatorValue = OP_ASSIGN; + lvNext.operatorValue = OP_ASSIGN; break; case '+': - lvNext->operatorValue = OP_ADD; + lvNext.operatorValue = OP_ADD; break; case '-': - lvNext->operatorValue = OP_SUBTRACT; + lvNext.operatorValue = OP_SUBTRACT; break; case '*': - lvNext->operatorValue = OP_MULTIPLY; + lvNext.operatorValue = OP_MULTIPLY; break; case '/': - lvNext->operatorValue = OP_DIVIDE; + lvNext.operatorValue = OP_DIVIDE; break; case '%': - lvNext->operatorValue = OP_MODULO; + lvNext.operatorValue = OP_MODULO; break; case '&': - lvNext->operatorValue = OP_AND; + lvNext.operatorValue = OP_AND; break; case '|': - lvNext->operatorValue = OP_OR; + lvNext.operatorValue = OP_OR; break; case '^': - lvNext->operatorValue = OP_XOR; + lvNext.operatorValue = OP_XOR; break; case '<': - lvNext->operatorValue = OP_LESS; + lvNext.operatorValue = OP_LESS; break; case '>': - lvNext->operatorValue = OP_GREATER; + lvNext.operatorValue = OP_GREATER; break; case '!': - lvNext->operatorValue = OP_NOT; + lvNext.operatorValue = OP_NOT; break; default: - lvNext->type = TOKEN_ERROR_TYPE; - break; + *state = LEX_ERROR; + return; } + *state = LEX_EXPECT_OPERATOR2; + *LexVectorAppend(lv) = lvNext; } static void _lexValue(struct LexVector* lv, char token, uint32_t next, enum LexState* state) { @@ -247,6 +273,12 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length, co lvNext = LexVectorAppend(lv); lvNext->type = TOKEN_OPEN_PAREN_TYPE; break; + case '!': + case '-': + case '~': + case '*': + _lexOperator(lv, token, &state); + break; case ' ': case '\t': break; @@ -499,6 +531,7 @@ static const int _operatorPrecedence[] = { [OP_NOT] = 2, [OP_SHIFT_L] = 5, [OP_SHIFT_R] = 5, + [OP_DEREFERENCE] = 2, }; static struct ParseTree* _parseTreeCreate() { @@ -622,7 +655,7 @@ void parseFree(struct ParseTree* tree) { } } -static bool _performOperation(enum Operation operation, int32_t current, int32_t next, int32_t* value) { +static bool _performOperation(struct mDebugger* debugger, enum Operation operation, int32_t current, int32_t next, int32_t* value, int* segment) { switch (operation) { case OP_ASSIGN: current = next; @@ -683,12 +716,29 @@ static bool _performOperation(enum Operation operation, int32_t current, int32_t case OP_GE: current = current >= next; break; + case OP_NEGATE: + current = -next; + break; + case OP_FLIP: + current = ~next; + break; + case OP_NOT: + current = !next; + break; case OP_SHIFT_L: current <<= next; break; case OP_SHIFT_R: current >>= next; break; + case OP_DEREFERENCE: + if (*segment < 0) { + current = debugger->core->busRead8(debugger->core, next); + } else { + current = debugger->core->rawRead8(debugger->core, next, *segment); + } + *segment = -1; + break; default: return false; } @@ -714,13 +764,37 @@ bool mDebuggerEvaluateParseTree(struct mDebugger* debugger, struct ParseTree* tr } return mDebuggerEvaluateParseTree(debugger, tree->lhs, segment, NULL); case TOKEN_OPERATOR_TYPE: - if (!mDebuggerEvaluateParseTree(debugger, tree->lhs, &lhs, segment)) { - return false; + switch (tree->token.operatorValue) { + case OP_ASSIGN: + case OP_ADD: + case OP_SUBTRACT: + case OP_MULTIPLY: + case OP_DIVIDE: + case OP_MODULO: + case OP_AND: + case OP_OR: + case OP_XOR: + case OP_LESS: + case OP_GREATER: + case OP_EQUAL: + case OP_NOT_EQUAL: + case OP_LOGICAL_AND: + case OP_LOGICAL_OR: + case OP_LE: + case OP_GE: + case OP_SHIFT_L: + case OP_SHIFT_R: + if (!mDebuggerEvaluateParseTree(debugger, tree->lhs, &lhs, segment)) { + return false; + } + // Fall through + default: + if (!mDebuggerEvaluateParseTree(debugger, tree->rhs, &rhs, segment)) { + return false; + } + break; } - if (!mDebuggerEvaluateParseTree(debugger, tree->rhs, &rhs, segment)) { - return false; - } - return _performOperation(tree->token.operatorValue, lhs, rhs, value); + return _performOperation(debugger, tree->token.operatorValue, lhs, rhs, value, segment); case TOKEN_IDENTIFIER_TYPE: return mDebuggerLookupIdentifier(debugger, tree->token.identifierValue, value, segment); case TOKEN_ERROR_TYPE: diff --git a/src/gb/core.c b/src/gb/core.c index 5b9400662..729fb31ff 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -42,20 +43,6 @@ static const struct mCoreChannelInfo _GBAudioChannels[] = { { 3, "ch4", "Channel 4", "Noise" }, }; -static const struct LR35902Segment _GBSegments[] = { - { .name = "ROM", .start = GB_BASE_CART_BANK1, .end = GB_BASE_VRAM }, - { .name = "RAM", .start = GB_BASE_EXTERNAL_RAM, .end = GB_BASE_WORKING_RAM_BANK0 }, - { 0 } -}; - -static const struct LR35902Segment _GBCSegments[] = { - { .name = "ROM", .start = GB_BASE_CART_BANK1, .end = GB_BASE_VRAM }, - { .name = "RAM", .start = GB_BASE_EXTERNAL_RAM, .end = GB_BASE_WORKING_RAM_BANK0 }, - { .name = "WRAM", .start = GB_BASE_WORKING_RAM_BANK1, .end = 0xE000 }, - { .name = "VRAM", .start = GB_BASE_VRAM, .end = GB_BASE_EXTERNAL_RAM }, - { 0 } -}; - static const struct mCoreMemoryBlock _GBMemoryBlocks[] = { { -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL }, { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511 }, @@ -715,12 +702,7 @@ static struct mDebuggerPlatform* _GBCoreDebuggerPlatform(struct mCore* core) { struct GBCore* gbcore = (struct GBCore*) core; struct GB* gb = core->board; if (!gbcore->debuggerPlatform) { - struct LR35902Debugger* platform = (struct LR35902Debugger*) LR35902DebuggerPlatformCreate(); - if (gb->model >= GB_MODEL_CGB) { - platform->segments = _GBCSegments; - } else { - platform->segments = _GBSegments; - } + struct LR35902Debugger* platform = (struct LR35902Debugger*) GBDebuggerCreate(gb); gbcore->debuggerPlatform = &platform->d; } return gbcore->debuggerPlatform; diff --git a/src/gb/debugger/debugger.c b/src/gb/debugger/debugger.c new file mode 100644 index 000000000..41444806b --- /dev/null +++ b/src/gb/debugger/debugger.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2013-2019 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + #include + + #include + #include + #include + #include + #include + #include + +static const struct LR35902Segment _GBSegments[] = { + { .name = "ROM", .start = GB_BASE_CART_BANK1, .end = GB_BASE_VRAM }, + { .name = "RAM", .start = GB_BASE_EXTERNAL_RAM, .end = GB_BASE_WORKING_RAM_BANK0 }, + { 0 } +}; + +static const struct LR35902Segment _GBCSegments[] = { + { .name = "ROM", .start = GB_BASE_CART_BANK1, .end = GB_BASE_VRAM }, + { .name = "RAM", .start = GB_BASE_EXTERNAL_RAM, .end = GB_BASE_WORKING_RAM_BANK0 }, + { .name = "WRAM", .start = GB_BASE_WORKING_RAM_BANK1, .end = 0xE000 }, + { .name = "VRAM", .start = GB_BASE_VRAM, .end = GB_BASE_EXTERNAL_RAM }, + { 0 } +}; + +static void _printStatus(struct CLIDebuggerSystem* debugger) { + struct CLIDebuggerBackend* be = debugger->p->backend; + struct GB* gb = debugger->p->d.core->board; + be->printf(be, "IE: %02X IF: %02X IME: %i\n", gb->memory.ie, gb->memory.io[REG_IF], gb->memory.ime); + be->printf(be, "LCDC: %02X STAT: %02X LY: %02X\n", gb->memory.io[REG_LCDC], gb->memory.io[REG_STAT] | 0x80, gb->memory.io[REG_LY]); + be->printf(be, "Next video mode: %i\n", mTimingUntil(&gb->timing, &gb->video.modeEvent) / 4); +} + +struct mDebuggerPlatform* GBDebuggerCreate(struct GB* gb) { + struct LR35902Debugger* platform = (struct LR35902Debugger*) LR35902DebuggerPlatformCreate(); + if (gb->model >= GB_MODEL_CGB) { + platform->segments = _GBCSegments; + } else { + platform->segments = _GBSegments; + } + platform->printStatus = _printStatus; + return &platform->d; +} \ No newline at end of file diff --git a/src/gb/gb.c b/src/gb/gb.c index 038401c83..fc3c174b0 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -630,7 +630,7 @@ void GBDetectModel(struct GB* gb) { } void GBUpdateIRQs(struct GB* gb) { - int irqs = gb->memory.ie & gb->memory.io[REG_IF]; + int irqs = gb->memory.ie & gb->memory.io[REG_IF] & 0x1F; if (!irqs) { gb->cpu->irqPending = false; return; diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index c5e0d3c7a..4b1517ae0 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -190,7 +190,9 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum G softwareRenderer->scx = 0; softwareRenderer->wy = 0; softwareRenderer->currentWy = 0; + softwareRenderer->currentWx = 0; softwareRenderer->lastY = GB_VIDEO_VERTICAL_PIXELS; + softwareRenderer->lastX = 0; softwareRenderer->hasWindow = false; softwareRenderer->wx = 0; softwareRenderer->model = model; @@ -232,6 +234,9 @@ static void GBVideoSoftwareRendererUpdateWindow(struct GBVideoSoftwareRenderer* renderer->currentWy = GB_VIDEO_VERTICAL_PIXELS; } else { renderer->currentWy = renderer->lastY - renderer->wy; + if (renderer->lastY == renderer->wy && renderer->lastX > renderer->wx) { + ++renderer->currentWy; + } } } else { renderer->currentWy += renderer->lastY; @@ -489,6 +494,7 @@ static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, ui static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax) { struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; softwareRenderer->lastY = y; + softwareRenderer->lastX = endX; uint8_t* maps = &softwareRenderer->d.vram[GB_BASE_MAP]; if (GBRegisterLCDCIsTileMap(softwareRenderer->lcdc)) { maps += GB_SIZE_MAP; @@ -498,9 +504,10 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i } if (GBRegisterLCDCIsBgEnable(softwareRenderer->lcdc) || softwareRenderer->model >= GB_MODEL_CGB) { int wy = softwareRenderer->wy + softwareRenderer->currentWy; - if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && wy <= y && endX >= softwareRenderer->wx - 7) { - if (softwareRenderer->wx - 7 > 0 && !softwareRenderer->d.disableBG) { - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, softwareRenderer->wx - 7, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy); + int wx = softwareRenderer->wx + softwareRenderer->currentWx - 7; + if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && wy <= y && wx <= endX) { + if (wx > 0 && !softwareRenderer->d.disableBG) { + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, wx, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy); } maps = &softwareRenderer->d.vram[GB_BASE_MAP]; @@ -508,7 +515,7 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i maps += GB_SIZE_MAP; } if (!softwareRenderer->d.disableWIN) { - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx - softwareRenderer->offsetWx, y - wy - softwareRenderer->offsetWy); + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, wx, endX, -wx - softwareRenderer->offsetWx, y - wy - softwareRenderer->offsetWy); } } else if (!softwareRenderer->d.disableBG) { GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx - softwareRenderer->offsetScx, softwareRenderer->scy + y - softwareRenderer->offsetScy); @@ -612,6 +619,9 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* renderer, int y) { struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; + softwareRenderer->lastX = 0; + softwareRenderer->currentWx = 0; + if (softwareRenderer->sgbTransfer == 1) { size_t offset = 2 * ((y & 7) + (y >> 3) * GB_VIDEO_HORIZONTAL_PIXELS); if (offset >= 0x1000) { @@ -702,7 +712,9 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer) } } softwareRenderer->lastY = GB_VIDEO_VERTICAL_PIXELS; + softwareRenderer->lastX = 0; softwareRenderer->currentWy = 0; + softwareRenderer->currentWx = 0; softwareRenderer->hasWindow = false; } diff --git a/src/gb/video.c b/src/gb/video.c index d63460088..8aa978292 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -224,7 +224,6 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) { ++video->ly; video->p->memory.io[REG_LY] = video->ly; GBRegisterSTAT oldStat = video->stat; - video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->ly); if (video->ly < GB_VIDEO_VERTICAL_PIXELS) { next = GB_VIDEO_MODE_2_LENGTH; video->mode = 2; @@ -246,6 +245,14 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) { if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); } + + // LYC stat is delayed 1 T-cycle + oldStat = video->stat; + video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->ly); + if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) { + video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); + } + GBUpdateIRQs(video->p); video->p->memory.io[REG_STAT] = video->stat; mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate); diff --git a/src/gba/io.c b/src/gba/io.c index 897b6ce5e..f7a2f3202 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -548,16 +548,16 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { break; case REG_IE: gba->memory.io[REG_IE >> 1] = value; - GBATestIRQ(gba->cpu, 1); + GBATestIRQ(gba, 1); return; case REG_IF: value = gba->memory.io[REG_IF >> 1] & ~value; gba->memory.io[REG_IF >> 1] = value; - GBATestIRQ(gba->cpu, 1); + GBATestIRQ(gba, 1); return; case REG_IME: gba->memory.io[REG_IME >> 1] = value; - GBATestIRQ(gba->cpu, 1); + GBATestIRQ(gba, 1); return; case REG_MAX: // Some bad interrupt libraries will write to this diff --git a/src/lr35902/debugger/cli-debugger.c b/src/lr35902/debugger/cli-debugger.c index fe85bca27..300a3dd8f 100644 --- a/src/lr35902/debugger/cli-debugger.c +++ b/src/lr35902/debugger/cli-debugger.c @@ -74,7 +74,7 @@ static inline uint16_t _printLine(struct CLIDebugger* debugger, uint16_t address }; disPtr[0] = '\t'; ++disPtr; - LR35902Disassemble(&info, disPtr, sizeof(disassembly) - (disPtr - disassembly)); + LR35902Disassemble(&info, address, disPtr, sizeof(disassembly) - (disPtr - disassembly)); be->printf(be, "%s\n", disassembly); return address; } @@ -87,6 +87,7 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) { be->printf(be, "D: %02X E: %02X (DE: %04X)\n", cpu->d, cpu->e, cpu->de); be->printf(be, "H: %02X L: %02X (HL: %04X)\n", cpu->h, cpu->l, cpu->hl); be->printf(be, "PC: %04X SP: %04X\n", cpu->pc, cpu->sp); + _printFlags(be, cpu->f); struct LR35902Debugger* platDebugger = (struct LR35902Debugger*) debugger->p->d.platform; size_t i; @@ -96,7 +97,9 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) { if (i) { be->printf(be, "\n"); } - _printFlags(be, cpu->f); + if (platDebugger->printStatus) { + platDebugger->printStatus(debugger); + } _printLine(debugger->p, cpu->pc, cpu->memory.currentSegment(cpu, cpu->pc)); } diff --git a/src/lr35902/debugger/debugger.c b/src/lr35902/debugger/debugger.c index 398131d5d..fb7e4a247 100644 --- a/src/lr35902/debugger/debugger.c +++ b/src/lr35902/debugger/debugger.c @@ -75,21 +75,22 @@ static bool LR35902DebuggerGetRegister(struct mDebuggerPlatform*, const char* na static bool LR35902DebuggerSetRegister(struct mDebuggerPlatform*, const char* name, int32_t value); struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) { - struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct LR35902Debugger)); - platform->entered = LR35902DebuggerEnter; - platform->init = LR35902DebuggerInit; - platform->deinit = LR35902DebuggerDeinit; - platform->setBreakpoint = LR35902DebuggerSetBreakpoint; - platform->listBreakpoints = LR35902DebuggerListBreakpoints; - platform->clearBreakpoint = LR35902DebuggerClearBreakpoint; - platform->setWatchpoint = LR35902DebuggerSetWatchpoint; - platform->listWatchpoints = LR35902DebuggerListWatchpoints; - platform->checkBreakpoints = LR35902DebuggerCheckBreakpoints; - platform->hasBreakpoints = LR35902DebuggerHasBreakpoints; - platform->trace = LR35902DebuggerTrace; - platform->getRegister = LR35902DebuggerGetRegister; - platform->setRegister = LR35902DebuggerSetRegister; - return platform; + struct LR35902Debugger* platform = malloc(sizeof(struct LR35902Debugger)); + platform->d.entered = LR35902DebuggerEnter; + platform->d.init = LR35902DebuggerInit; + platform->d.deinit = LR35902DebuggerDeinit; + platform->d.setBreakpoint = LR35902DebuggerSetBreakpoint; + platform->d.listBreakpoints = LR35902DebuggerListBreakpoints; + platform->d.clearBreakpoint = LR35902DebuggerClearBreakpoint; + platform->d.setWatchpoint = LR35902DebuggerSetWatchpoint; + platform->d.listWatchpoints = LR35902DebuggerListWatchpoints; + platform->d.checkBreakpoints = LR35902DebuggerCheckBreakpoints; + platform->d.hasBreakpoints = LR35902DebuggerHasBreakpoints; + platform->d.trace = LR35902DebuggerTrace; + platform->d.getRegister = LR35902DebuggerGetRegister; + platform->d.setRegister = LR35902DebuggerSetRegister; + platform->printStatus = NULL; + return &platform->d; } void LR35902DebuggerInit(void* cpu, struct mDebuggerPlatform* platform) { @@ -215,7 +216,7 @@ static void LR35902DebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* disPtr[0] = ':'; disPtr[1] = ' '; disPtr += 2; - LR35902Disassemble(&info, disPtr, sizeof(disassembly) - (disPtr - disassembly)); + LR35902Disassemble(&info, address, disPtr, sizeof(disassembly) - (disPtr - disassembly)); *length = snprintf(out, *length, "A: %02X F: %02X B: %02X C: %02X D: %02X E: %02X H: %02X L: %02X SP: %04X PC: %02X:%04X | %s", cpu->a, cpu->f.packed, cpu->b, cpu->c, diff --git a/src/lr35902/decoder.c b/src/lr35902/decoder.c index 3d3098441..089b52c69 100644 --- a/src/lr35902/decoder.c +++ b/src/lr35902/decoder.c @@ -199,6 +199,7 @@ DEFINE_DECODER_LR35902(ADDSP, info->mnemonic = LR35902_MN_ADD; \ DEFINE_DECODER_LR35902(JR ## CONDITION_NAME, \ info->mnemonic = LR35902_MN_JR; \ info->condition = CONDITION; \ + info->op1.flags = LR35902_OP_FLAG_RELATIVE; \ return 1;) #define DEFINE_CALL_INSTRUCTION_LR35902(CONDITION_NAME, CONDITION) \ @@ -314,15 +315,21 @@ DEFINE_POPPUSH_DECODER_LR35902(DE); DEFINE_POPPUSH_DECODER_LR35902(HL); DEFINE_POPPUSH_DECODER_LR35902(AF); +#define DEFINE_CB_OP_DECODER_LR35902(NAME, BODY, OP) \ + DEFINE_DECODER_LR35902(NAME ## B, info->OP.reg = LR35902_REG_B; BODY) \ + DEFINE_DECODER_LR35902(NAME ## C, info->OP.reg = LR35902_REG_C; BODY) \ + DEFINE_DECODER_LR35902(NAME ## D, info->OP.reg = LR35902_REG_D; BODY) \ + DEFINE_DECODER_LR35902(NAME ## E, info->OP.reg = LR35902_REG_E; BODY) \ + DEFINE_DECODER_LR35902(NAME ## H, info->OP.reg = LR35902_REG_H; BODY) \ + DEFINE_DECODER_LR35902(NAME ## L, info->OP.reg = LR35902_REG_L; BODY) \ + DEFINE_DECODER_LR35902(NAME ## HL, info->OP.reg = LR35902_REG_HL; info->OP.flags = LR35902_OP_FLAG_MEMORY; BODY) \ + DEFINE_DECODER_LR35902(NAME ## A, info->OP.reg = LR35902_REG_A; BODY) + #define DEFINE_CB_2_DECODER_LR35902(NAME, BODY) \ - DEFINE_DECODER_LR35902(NAME ## B, info->op2.reg = LR35902_REG_B; BODY) \ - DEFINE_DECODER_LR35902(NAME ## C, info->op2.reg = LR35902_REG_C; BODY) \ - DEFINE_DECODER_LR35902(NAME ## D, info->op2.reg = LR35902_REG_D; BODY) \ - DEFINE_DECODER_LR35902(NAME ## E, info->op2.reg = LR35902_REG_E; BODY) \ - DEFINE_DECODER_LR35902(NAME ## H, info->op2.reg = LR35902_REG_H; BODY) \ - DEFINE_DECODER_LR35902(NAME ## L, info->op2.reg = LR35902_REG_L; BODY) \ - DEFINE_DECODER_LR35902(NAME ## HL, info->op2.reg = LR35902_REG_HL; info->op2.flags = LR35902_OP_FLAG_MEMORY; BODY) \ - DEFINE_DECODER_LR35902(NAME ## A, info->op2.reg = LR35902_REG_A; BODY) + DEFINE_CB_OP_DECODER_LR35902(NAME, BODY, op2) + +#define DEFINE_CB_1_DECODER_LR35902(NAME, BODY) \ + DEFINE_CB_OP_DECODER_LR35902(NAME, BODY, op1) #define DEFINE_CB_DECODER_LR35902(NAME, BODY) \ DEFINE_CB_2_DECODER_LR35902(NAME ## 0, info->op1.immediate = 0; BODY) \ @@ -339,17 +346,19 @@ DEFINE_CB_DECODER_LR35902(RES, info->mnemonic = LR35902_MN_RES) DEFINE_CB_DECODER_LR35902(SET, info->mnemonic = LR35902_MN_SET) #define DEFINE_CB_X_DECODER_LR35902(NAME) \ - DEFINE_CB_2_DECODER_LR35902(NAME, info->mnemonic = LR35902_MN_ ## NAME) \ - DEFINE_DECODER_LR35902(NAME ## A_, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_A) + DEFINE_CB_1_DECODER_LR35902(NAME, info->mnemonic = LR35902_MN_ ## NAME) \ + DEFINE_DECODER_LR35902(NAME ## A_, info->mnemonic = LR35902_MN_ ## NAME; \ + info->op1.flags = LR35902_OP_FLAG_IMPLICIT; \ + info->op1.reg = LR35902_REG_A;) DEFINE_CB_X_DECODER_LR35902(RL) DEFINE_CB_X_DECODER_LR35902(RLC) DEFINE_CB_X_DECODER_LR35902(RR) DEFINE_CB_X_DECODER_LR35902(RRC) -DEFINE_CB_2_DECODER_LR35902(SLA, info->mnemonic = LR35902_MN_SLA) -DEFINE_CB_2_DECODER_LR35902(SRA, info->mnemonic = LR35902_MN_SRA) -DEFINE_CB_2_DECODER_LR35902(SRL, info->mnemonic = LR35902_MN_SRL) -DEFINE_CB_2_DECODER_LR35902(SWAP, info->mnemonic = LR35902_MN_SWAP) +DEFINE_CB_1_DECODER_LR35902(SLA, info->mnemonic = LR35902_MN_SLA) +DEFINE_CB_1_DECODER_LR35902(SRA, info->mnemonic = LR35902_MN_SRA) +DEFINE_CB_1_DECODER_LR35902(SRL, info->mnemonic = LR35902_MN_SRL) +DEFINE_CB_1_DECODER_LR35902(SWAP, info->mnemonic = LR35902_MN_SWAP) DEFINE_DECODER_LR35902(DI, info->mnemonic = LR35902_MN_DI) DEFINE_DECODER_LR35902(EI, info->mnemonic = LR35902_MN_EI) @@ -489,7 +498,7 @@ static const char* _lr35902MnemonicStrings[] = { }; -static int _decodeOperand(struct LR35902Operand op, char* buffer, int blen) { +static int _decodeOperand(struct LR35902Operand op, uint16_t pc, char* buffer, int blen) { int total = 0; if (op.flags & LR35902_OP_FLAG_IMPLICIT) { return 0; @@ -503,7 +512,12 @@ static int _decodeOperand(struct LR35902Operand op, char* buffer, int blen) { int written = snprintf(buffer, blen - 1, "%s", _lr35902Registers[op.reg]); ADVANCE(written); } else { - int written = snprintf(buffer, blen - 1, "$%02X", op.immediate); + int written; + if (op.flags & LR35902_OP_FLAG_RELATIVE) { + written = snprintf(buffer, blen - 1, "$%04X", pc + (int8_t) op.immediate); + } else { + written = snprintf(buffer, blen - 1, "$%02X", op.immediate); + } ADVANCE(written); if (op.reg) { strncpy(buffer, "+", blen - 1); @@ -525,7 +539,7 @@ static int _decodeOperand(struct LR35902Operand op, char* buffer, int blen) { return total; } -int LR35902Disassemble(struct LR35902InstructionInfo* info, char* buffer, int blen) { +int LR35902Disassemble(struct LR35902InstructionInfo* info, uint16_t pc, char* buffer, int blen) { const char* mnemonic = _lr35902MnemonicStrings[info->mnemonic]; int written; int total = 0; @@ -545,7 +559,7 @@ int LR35902Disassemble(struct LR35902InstructionInfo* info, char* buffer, int bl } if (info->op1.reg || info->op1.immediate || info->op2.reg || info->op2.immediate) { - written = _decodeOperand(info->op1, buffer, blen); + written = _decodeOperand(info->op1, pc, buffer, blen); ADVANCE(written); } @@ -554,7 +568,7 @@ int LR35902Disassemble(struct LR35902InstructionInfo* info, char* buffer, int bl strncpy(buffer, ", ", blen - 1); ADVANCE(2); } - written = _decodeOperand(info->op2, buffer, blen); + written = _decodeOperand(info->op2, pc, buffer, blen); ADVANCE(written); } diff --git a/src/platform/qt/AbstractUpdater.cpp b/src/platform/qt/AbstractUpdater.cpp index c165ae1a0..02f23c956 100644 --- a/src/platform/qt/AbstractUpdater.cpp +++ b/src/platform/qt/AbstractUpdater.cpp @@ -85,5 +85,7 @@ void AbstractUpdater::updateDownloaded(QNetworkReply* reply) { } f.write(bytes); } + f.flush(); + f.close(); emit updateDone(true); } diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index eebcf3d35..325620c91 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -183,7 +183,7 @@ if(Qt5Multimedia_FOUND) list(APPEND SOURCE_FILES VideoDumper.cpp) if (WIN32 AND QT_STATIC) - list(APPEND QT_LIBRARIES Qt5::QWindowsAudioPlugin Qt5::DSServicePlugin + list(APPEND QT_LIBRARIES Qt5::QWindowsAudioPlugin Qt5::DSServicePlugin Qt5::QWindowsVistaStylePlugin strmiids mfuuid mfplat mf ksguid dxva2 evr d3d9) endif() list(APPEND QT_LIBRARIES Qt5::Multimedia) @@ -287,15 +287,15 @@ endif() if(QT_STATIC) find_library(QTPCRE NAMES qtpcre2 qtpcre) if(WIN32) - list(APPEND QT_LIBRARIES qwindows dwmapi imm32 uxtheme Qt5EventDispatcherSupport Qt5FontDatabaseSupport Qt5ThemeSupport) - set_target_properties(Qt5::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE};version;winmm;ws2_32") + list(APPEND QT_LIBRARIES qwindows dwmapi imm32 uxtheme Qt5EventDispatcherSupport Qt5FontDatabaseSupport Qt5ThemeSupport Qt5WindowsUIAutomationSupport) + set_target_properties(Qt5::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE};version;winmm;ssl;crypto;ws2_32;iphlpapi;crypt32;userenv;netapi32;wtsapi32") elseif(APPLE) find_package(Cups) find_package(Qt5PrintSupport) find_library(QTFREETYPE NAMES qtfreetype) find_library(QTHARFBUZZ NAMES qtharfbuzzng qtharfbuzz) find_library(QTPLATFORMSUPPORT NAMES Qt5PlatformSupport) - list(APPEND QT_LIBRARIES Cups Qt5::PrintSupport Qt5::QCocoaIntegrationPlugin Qt5::CoreAudioPlugin Qt5::AVFServicePlugin Qt5::QCocoaPrinterSupportPlugin ${QTPLATFORMSUPPORT} "-framework AVFoundation" "-framework CoreMedia") + list(APPEND QT_LIBRARIES Cups Qt5::PrintSupport Qt5::QCocoaIntegrationPlugin Qt5::CoreAudioPlugin Qt5::AVFServicePlugin Qt5::QCocoaPrinterSupportPlugin ${QTPLATFORMSUPPORT} "-framework AVFoundation" "-framework CoreMedia" "-framework SystemConfiguration" "-framework Security") set_target_properties(Qt5::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE};${QTHARFBUZZ};${QTFREETYPE}") link_directories() endif() diff --git a/src/platform/qt/CoreManager.cpp b/src/platform/qt/CoreManager.cpp index adb002c18..a77334cc5 100644 --- a/src/platform/qt/CoreManager.cpp +++ b/src/platform/qt/CoreManager.cpp @@ -49,7 +49,7 @@ CoreController* CoreManager::loadGame(const QString& path) { vf = vfclone; } dir->close(dir); - loadGame(vf, fname, base); + return loadGame(vf, fname, base); } else { LOG(QT, ERROR) << tr("Failed to open game file: %1").arg(path); } diff --git a/src/platform/qt/VideoDumper.cpp b/src/platform/qt/VideoDumper.cpp index c5ed93eb2..c5df8d208 100644 --- a/src/platform/qt/VideoDumper.cpp +++ b/src/platform/qt/VideoDumper.cpp @@ -23,14 +23,18 @@ bool VideoDumper::present(const QVideoFrame& frame) { QImage::Format format = QVideoFrame::imageFormatFromPixelFormat(vFormat); bool swap = false; if (format == QImage::Format_Invalid) { - vFormat = static_cast(vFormat - QVideoFrame::Format_BGRA32 + QVideoFrame::Format_ARGB32); - format = QVideoFrame::imageFormatFromPixelFormat(vFormat); - if (format == QImage::Format_ARGB32) { - format = QImage::Format_RGBA8888; - } else if (format == QImage::Format_ARGB32_Premultiplied) { - format = QImage::Format_RGBA8888_Premultiplied; + if (vFormat < QVideoFrame::Format_BGRA5658_Premultiplied) { + vFormat = static_cast(vFormat - QVideoFrame::Format_BGRA32 + QVideoFrame::Format_ARGB32); + format = QVideoFrame::imageFormatFromPixelFormat(vFormat); + if (format == QImage::Format_ARGB32) { + format = QImage::Format_RGBA8888; + } else if (format == QImage::Format_ARGB32_Premultiplied) { + format = QImage::Format_RGBA8888_Premultiplied; + } + swap = true; + } else { + return false; } - swap = true; } uchar* bits = mappedFrame.bits(); QImage image(bits, mappedFrame.width(), mappedFrame.height(), mappedFrame.bytesPerLine(), format); diff --git a/src/platform/qt/VideoView.ui b/src/platform/qt/VideoView.ui index 1e9dfe367..24a3c5e75 100644 --- a/src/platform/qt/VideoView.ui +++ b/src/platform/qt/VideoView.ui @@ -269,6 +269,11 @@ HEVC + + + HEVC (NVENC) + + VP8 diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index b4ef8c0cd..4381dfb41 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1253,7 +1253,7 @@ void Window::setupMenu(QMenuBar* menubar) { fileMenu->addSeparator(); #endif - QAction* about = new QAction(tr("About"), fileMenu); + QAction* about = new QAction(tr("About..."), fileMenu); connect(about, &QAction::triggered, openTView()); fileMenu->addAction(about); diff --git a/src/platform/qt/input/InputController.cpp b/src/platform/qt/input/InputController.cpp index e342abe94..a168ffe04 100644 --- a/src/platform/qt/input/InputController.cpp +++ b/src/platform/qt/input/InputController.cpp @@ -994,12 +994,23 @@ void InputController::setupCam() { #ifdef BUILD_QT_MULTIMEDIA if (!m_camera) { m_camera = std::make_unique(); + connect(m_camera.get(), &QCamera::statusChanged, this, &InputController::prepareCamSettings); } - QVideoFrame::PixelFormat format(QVideoFrame::Format_RGB32); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) + m_camera->setCaptureMode(QCamera::CaptureVideo); + m_camera->setViewfinder(&m_videoDumper); m_camera->load(); +#endif +} + +#ifdef BUILD_QT_MULTIMEDIA +void InputController::prepareCamSettings(QCamera::Status status) { + if (status != QCamera::LoadedStatus || m_camera->state() == QCamera::ActiveState) { + return; + } +#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) + QVideoFrame::PixelFormat format(QVideoFrame::Format_RGB32); QCameraViewfinderSettings settings; - QSize size(1920, 1080); + QSize size(1280, 720); auto cameraRes = m_camera->supportedViewfinderResolutions(settings); for (auto& cameraSize : cameraRes) { if (cameraSize.width() < m_image.w || cameraSize.height() < m_image.h) { @@ -1010,10 +1021,11 @@ void InputController::setupCam() { } } settings.setResolution(size); + auto cameraFormats = m_camera->supportedViewfinderPixelFormats(settings); auto goodFormats = m_videoDumper.supportedPixelFormats(); bool goodFormatFound = false; - for (auto& goodFormat : goodFormats) { + for (const auto& goodFormat : goodFormats) { if (cameraFormats.contains(goodFormat)) { settings.setPixelFormat(goodFormat); format = goodFormat; @@ -1023,20 +1035,20 @@ void InputController::setupCam() { } if (!goodFormatFound) { LOG(QT, WARN) << "Could not find a valid camera format!"; + for (const auto& format : cameraFormats) { + LOG(QT, WARN) << "Camera supported format: " << QString::number(format); + } } m_camera->setViewfinderSettings(settings); #endif - m_camera->setCaptureMode(QCamera::CaptureVideo); - m_camera->setViewfinder(&m_videoDumper); m_camera->start(); -#endif } +#endif void InputController::teardownCam() { #ifdef BUILD_QT_MULTIMEDIA if (m_camera) { m_camera->stop(); - m_camera.reset(); } #endif } diff --git a/src/platform/qt/input/InputController.h b/src/platform/qt/input/InputController.h index 0b62c9d1f..5451beeff 100644 --- a/src/platform/qt/input/InputController.h +++ b/src/platform/qt/input/InputController.h @@ -30,6 +30,7 @@ #ifdef BUILD_QT_MULTIMEDIA #include "VideoDumper.h" +#include #endif struct mRotationSource; @@ -144,6 +145,9 @@ protected: bool eventFilter(QObject*, QEvent*) override; private slots: +#ifdef BUILD_QT_MULTIMEDIA + void prepareCamSettings(QCamera::Status); +#endif void setupCam(); void teardownCam(); diff --git a/src/platform/qt/main.cpp b/src/platform/qt/main.cpp index 8edc96e79..ec6a1b9e8 100644 --- a/src/platform/qt/main.cpp +++ b/src/platform/qt/main.cpp @@ -21,6 +21,7 @@ #include #ifdef Q_OS_WIN Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); +Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin); #ifdef BUILD_QT_MULTIMEDIA Q_IMPORT_PLUGIN(QWindowsAudioPlugin); Q_IMPORT_PLUGIN(DSServicePlugin);