all repos — mgba @ e654c188f9669de13c9f36049b1c8f579d84c666

mGBA Game Boy Advance Emulator

Merge branch 'master' into medusa
Vicki Pfau vi@endrift.com
Sat, 28 Sep 2019 16:00:17 -0700
commit

e654c188f9669de13c9f36049b1c8f579d84c666

parent

6b84383d1ad29d9dd39a62a80c1c8c5878979f45

M .travis.yml.travis.yml

@@ -7,8 +7,8 @@ os: linux

env: - DOCKER_TAG=ubuntu:xenial - DOCKER_TAG=ubuntu:bionic -- DOCKER_TAG=ubuntu:cosmic - DOCKER_TAG=ubuntu:disco +- DOCKER_TAG=ubuntu:eoan - DOCKER_TAG=3ds - DOCKER_TAG=wii - DOCKER_TAG=vita
M CHANGESCHANGES

@@ -41,6 +41,7 @@ - Support for unlicensed Wisdom Tree Game Boy mapper

- Qt: Add export button for tile view (closes mgba.io/i/1507) - Qt: Add recent game list clearing (closes mgba.io/i/1380) - GB: Yanking gamepak now supported + - Qt: Memory range dumping (closes mgba.io/i/1298) Emulation fixes: - GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208) - GBA: Reset now reloads multiboot ROMs

@@ -48,39 +49,21 @@ - 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) - - GBA Video: Fix wrapped sprite mosaic clamping (fixes mgba.io/i/1432) - - GBA Memory: Fix STM to VRAM (fixes mgba.io/i/1430) - GB Audio: Only reset channel 3 sample in DMG mode - GB Audio: Sample inactive channels (fixes mgba.io/i/1455, mgba.io/i/1456) - - GB Video: Increment BCPS/OCPS even in mode 3 (fixes mgba.io/i/1462) - - GB Audio: Deschedule channel 3 when disabled (fixes mgba.io/i/1463) - - GB Audio: Deschedule channel 1 when disabled by sweep (fixes mgba.io/i/1467) - - GBA Memory: Fix STM/LDM to invalid VRAM - - GB: Fix savedata initialization (fixes mgba.io/i/1473, mgba.io/i/1478) - GB Memory: Better emulate 0xFEA0 region on DMG, MGB and AGB - - GB Printer: Reset printer buffer index after printing - GB Video: Fix mode 0 window edge case (fixes mgba.io/i/1519) - - GBA Audio: Fix channel 4 aliasing (fixes mgba.io/i/1265) - - GB Audio: Improve channel 4 supersampling + - GB Audio: Fix channel 4 volume (fixes mgba.io/i/1529) Other fixes: - Qt: Fix some Qt display driver race conditions - Core: Improved lockstep driver reliability (Le Hoang Quyen) - - Switch: Fix threading-related crash on second launch - - Qt: Fix FPS target maxing out at 59.727 (fixes mgba.io/i/1421) - - Core: Fix crashes if core directories aren't set - Qt: Fix menu bar staying hidden in full screen (fixes mgba.io/i/317) - GB SIO: Fix lockstep failing games aren't reloaded - - Core: Fix crash when exiting game with cheats loaded - - GBA Cheats: Fix PARv3 Thumb hooks - - mGUI: Fix crash if last loaded ROM directory disappears (fixes mgba.io/i/1466) - Libretro: Fix crash changing allowing opposing directions (hhromic) - - Qt: Fix race conditions initializing GDB stub - - GBA: Set up GPIO mapping on null and ELF ROM regions (fixes mgba.io/i/1481) - GBA Cheats: Fix value incrementing in CB slide codes (fixes mgba.io/i/1501) - Qt: Only show emulator restart warning once per settings saving - - Qt: Improve cheat view UX - - GB: Fix SGB controller incrementing (fixes mgba.io/i/1104) - FFmpeg: Drain recording buffers + - Shaders: Fix gba-color shader resolution (fixes mgba.io/i/1435) Misc: - GBA Savedata: EEPROM performance fixes - GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash

@@ -100,16 +83,47 @@ - Qt: Increase maximum magnifications and scaling

- Qt: Add native FPS button to settings view - Qt: Improve sync code - Switch: Dynamic display resizing - - Qt: Make mute menu option also toggle fast-forward mute (fixes mgba.io/i/1424) - Vita: L2/R2 and L3/R3 can now be mapped on PSTV (fixes mgba.io/i/1292) - mGUI: Remember name and position of last loaded game - Core: Create game-related paths if they don't exist (fixes mgba.io/i/1446) - Qt: Add option to pause on minimizing window (closes mgba.io/i/1379) - Switch: Support file associations - - Qt: Show error message if file failed to load - Qt: Scale pixel color values to full range (fixes mgba.io/i/1511) - Qt, OpenGL: Disable integer scaling for dimensions that don't fit - Feature: Switch from ImageMagick to FFmpeg for GIF generation + - OpenGL: Only resize textures when needed + - GBA BIOS: Fix clobbered registers in CpuSet (fixes mgba.io/i/1531) + - Qt: Remove What's This icon from dialogs + +0.7.3: (2019-09-15) +Emulation fixes: + - GB: Fix savedata initialization (fixes mgba.io/i/1473, mgba.io/i/1478) + - GB: Fix SGB controller incrementing (fixes mgba.io/i/1104) + - GB Audio: Improve channel 4 supersampling + - GB Printer: Reset printer buffer index after printing + - GB Audio: Deschedule channel 3 when disabled (fixes mgba.io/i/1463) + - GB Audio: Deschedule channel 1 when disabled by sweep (fixes mgba.io/i/1467) + - GB Video: Increment BCPS/OCPS even in mode 3 (fixes mgba.io/i/1462) + - GBA Memory: Fix STM to VRAM (fixes mgba.io/i/1430) + - GBA Memory: Fix STM/LDM to invalid VRAM + - GBA Video: Fix wrapped sprite mosaic clamping (fixes mgba.io/i/1432) + - GBA Audio: Fix channel 4 aliasing (fixes mgba.io/i/1265) +Other fixes: + - Core: Fix crashes if core directories aren't set + - Core: Fix crash when exiting game with cheats loaded + - GBA: Set up GPIO mapping on null and ELF ROM regions (fixes mgba.io/i/1481) + - GBA Cheats: Fix PARv3 Thumb hooks + - GBA Cheats: Fix value incrementing in CB slide codes (fixes mgba.io/i/1501) + - Qt: Fix FPS target maxing out at 59.727 (fixes mgba.io/i/1421) + - Qt: Cap audio buffer size to 8192 (fixes mgba.io/i/1433) + - Qt: Fix race conditions initializing GDB stub + - Qt: Improve cheat view UX + - Libretro: Fix crash changing allowing opposing directions (hhromic) + - mGUI: Fix crash if last loaded ROM directory disappears (fixes mgba.io/i/1466) + - Switch: Fix threading-related crash on second launch +Misc: + - Qt: Make mute menu option also toggle fast-forward mute (fixes mgba.io/i/1424) + - Qt: Show error message if file failed to load 0.7.2: (2019-05-25) Emulation fixes:
M res/shaders/gba-color.shader/manifest.inires/shaders/gba-color.shader/manifest.ini

@@ -7,6 +7,8 @@

[pass.0] fragmentShader=gba-color.fs blend=1 +width=-1 +height=-1 [pass.0.uniform.darken_screen] type=float
M src/gb/audio.csrc/gb/audio.c

@@ -770,7 +770,7 @@ }

static int16_t _coalesceNoiseChannel(struct GBAudioNoiseChannel* ch) { if (!ch->nSamples) { - return ch->sample; + return ch->sample << 3; } // TODO keep track of timing int16_t sample = (ch->samples << 3) / ch->nSamples;
M src/gb/core.csrc/gb/core.c

@@ -45,7 +45,7 @@ };

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 }, + { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 }, { GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { GB_REGION_EXTERNAL_RAM, "sram", "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_BASE_EXTERNAL_RAM + GB_SIZE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM * 4, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 3 }, { GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2 , GB_SIZE_WORKING_RAM_BANK0 * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },

@@ -56,10 +56,10 @@ };

static const struct mCoreMemoryBlock _GBCMemoryBlocks[] = { { -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 }, + { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 }, { GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 }, { GB_REGION_EXTERNAL_RAM, "sram", "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_BASE_EXTERNAL_RAM + GB_SIZE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM * 4, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 3 }, - { GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2, GB_SIZE_WORKING_RAM_BANK0 * 8, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 7 }, + { GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2, GB_SIZE_WORKING_RAM_BANK0 * 8, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 7, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 }, { GB_BASE_OAM, "oam", "OAM", "OBJ Attribute Memory", GB_BASE_OAM, GB_BASE_OAM + GB_SIZE_OAM, GB_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { GB_BASE_IO, "io", "MMIO", "Memory-Mapped I/O", GB_BASE_IO, GB_BASE_IO + GB_SIZE_IO, GB_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { GB_BASE_HRAM, "hram", "HRAM", "High RAM", GB_BASE_HRAM, GB_BASE_HRAM + GB_SIZE_HRAM, GB_SIZE_HRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
M src/gba/hle-bios.csrc/gba/hle-bios.c

@@ -6,7 +6,7 @@ const uint8_t hleBios[SIZE_BIOS] = {

0x06, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x0b, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1, 0x2c, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0x03, 0xa0, 0xe3, - 0x03, 0x10, 0xd0, 0xe5, 0xea, 0x00, 0x51, 0xe3, 0xec, 0x01, 0x9f, 0x15, + 0x03, 0x10, 0xd0, 0xe5, 0xea, 0x00, 0x51, 0xe3, 0xf8, 0x01, 0x9f, 0x15, 0x10, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, 0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02, 0x00, 0x58, 0x2d, 0xe9, 0x02, 0xb0, 0x5e, 0xe5, 0x9c, 0xc0, 0xa0, 0xe3,

@@ -20,7 +20,7 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, - 0xc8, 0x01, 0x00, 0x00, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3, + 0xd4, 0x01, 0x00, 0x00, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3, 0x00, 0xe0, 0x8f, 0xe2, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x5e, 0xe5, 0x01, 0x00, 0xa0, 0xe3, 0x01, 0x10, 0xa0, 0xe3, 0x0c, 0x40, 0x2d, 0xe9,

@@ -29,18 +29,19 @@ 0x01, 0x20, 0xa0, 0xe3, 0x03, 0x00, 0x00, 0x0a, 0xb8, 0x30, 0x5c, 0xe1,

0x01, 0x30, 0xc3, 0xe1, 0xb8, 0x30, 0x4c, 0xe1, 0x01, 0x03, 0xcc, 0xe5, 0x08, 0x02, 0xcc, 0xe5, 0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0x13, 0xe0, 0x01, 0x30, 0x23, 0x10, 0xb8, 0x30, 0x4c, 0x11, 0x08, 0x22, 0xcc, 0xe5, - 0xf7, 0xff, 0xff, 0x0a, 0x0c, 0x80, 0xbd, 0xe8, 0x00, 0x40, 0x2d, 0xe9, - 0x02, 0x36, 0xa0, 0xe1, 0x01, 0x04, 0x12, 0xe3, 0x0f, 0x00, 0x00, 0x0a, - 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a, 0x23, 0x35, 0x81, 0xe0, - 0x04, 0x00, 0xb0, 0xe8, 0x03, 0x00, 0x51, 0xe1, 0x04, 0x00, 0xa1, 0xb8, - 0xfc, 0xff, 0xff, 0xba, 0x14, 0x00, 0x00, 0xea, 0x01, 0x00, 0xc0, 0xe3, - 0x01, 0x10, 0xc1, 0xe3, 0xa3, 0x35, 0x81, 0xe0, 0xb0, 0x20, 0xd0, 0xe1, - 0x03, 0x00, 0x51, 0xe1, 0xb2, 0x20, 0xc1, 0xb0, 0xfc, 0xff, 0xff, 0xba, - 0x0c, 0x00, 0x00, 0xea, 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a, - 0x23, 0x35, 0x81, 0xe0, 0x03, 0x00, 0x51, 0xe1, 0x04, 0x00, 0xb0, 0xb8, - 0x04, 0x00, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba, 0x04, 0x00, 0x00, 0xea, - 0xa3, 0x35, 0x81, 0xe0, 0x03, 0x00, 0x51, 0xe1, 0xb2, 0x20, 0xd0, 0xb0, - 0xb2, 0x20, 0xc1, 0xb0, 0xfb, 0xff, 0xff, 0xba, 0x00, 0x80, 0xbd, 0xe8, + 0xf7, 0xff, 0xff, 0x0a, 0x0c, 0x80, 0xbd, 0xe8, 0x30, 0x40, 0x2d, 0xe9, + 0x02, 0x46, 0xa0, 0xe1, 0x00, 0xc0, 0xa0, 0xe1, 0x01, 0x50, 0xa0, 0xe1, + 0x01, 0x04, 0x12, 0xe3, 0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3, + 0x05, 0x00, 0x00, 0x0a, 0x24, 0x45, 0x85, 0xe0, 0x08, 0x00, 0xbc, 0xe8, + 0x04, 0x00, 0x55, 0xe1, 0x08, 0x00, 0xa5, 0xb8, 0xfc, 0xff, 0xff, 0xba, + 0x14, 0x00, 0x00, 0xea, 0x01, 0xc0, 0xcc, 0xe3, 0x01, 0x50, 0xc5, 0xe3, + 0xa4, 0x45, 0x85, 0xe0, 0xb0, 0x30, 0xdc, 0xe1, 0x04, 0x00, 0x55, 0xe1, + 0xb2, 0x30, 0xc5, 0xb0, 0xfc, 0xff, 0xff, 0xba, 0x0c, 0x00, 0x00, 0xea, + 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a, 0x24, 0x45, 0x85, 0xe0, + 0x04, 0x00, 0x55, 0xe1, 0x08, 0x00, 0xbc, 0xb8, 0x08, 0x00, 0xa5, 0xb8, + 0xfb, 0xff, 0xff, 0xba, 0x04, 0x00, 0x00, 0xea, 0xa4, 0x45, 0x85, 0xe0, + 0x04, 0x00, 0x55, 0xe1, 0xb2, 0x30, 0xdc, 0xb0, 0xb2, 0x30, 0xc5, 0xb0, + 0xfb, 0xff, 0xff, 0xba, 0x17, 0x3e, 0xa0, 0xe3, 0x30, 0x80, 0xbd, 0xe8, 0xf0, 0x47, 0x2d, 0xe9, 0x01, 0x04, 0x12, 0xe3, 0x02, 0x36, 0xa0, 0xe1, 0x23, 0x25, 0x81, 0xe0, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x30, 0x90, 0xe5, 0x03, 0x40, 0xa0, 0xe1, 0x03, 0x50, 0xa0, 0xe1, 0x03, 0x60, 0xa0, 0xe1,
M src/gba/hle-bios.makesrc/gba/hle-bios.make

@@ -15,4 +15,4 @@ echo '#include "hle-bios.h"' > $@

echo >> $@ echo '#include <mgba/internal/gba/memory.h>' >> $@ echo >> $@ - xxd -i $< | sed -e 's/unsigned char hle_bios_bin\[\]/const uint8_t hleBios[SIZE_BIOS]/' | grep -v hle_bios_bin_len >> $@ + xxd -i $< | sed -e 's/unsigned char hle_bios_bin\[\]/const uint8_t hleBios[SIZE_BIOS]/' -e 's/^ \+/\t/' | grep -v hle_bios_bin_len >> $@
M src/gba/hle-bios.ssrc/gba/hle-bios.s

@@ -65,7 +65,7 @@ .word ArcTan

.word ArcTan2 .word CpuSet .word CpuFastSet -# ... The rest of this table isn't needed if the rest aren't implemented +@ ... The rest of this table isn't needed if the rest aren't implemented irqBase: stmfd sp!, {r0-r3, r12, lr}

@@ -83,7 +83,7 @@ mov r1, #1

IntrWait: stmfd sp!, {r2-r3, lr} mov r12, #0x04000000 -# See if we want to return immediately +@ See if we want to return immediately cmp r0, #0 mov r0, #0 mov r2, #1

@@ -91,11 +91,11 @@ beq 1f

ldrh r3, [r12, #-8] bic r3, r1 strh r3, [r12, #-8] -# Halt +@ Halt 0: strb r0, [r12, #0x301] 1: -# Check which interrupts were acknowledged +@ Check which interrupts were acknowledged strb r0, [r12, #0x208] ldrh r3, [r12, #-8] ands r3, r1

@@ -106,54 +106,57 @@ beq 0b

ldmfd sp!, {r2-r3, pc} CpuSet: -stmfd sp!, {lr} -mov r3, r2, lsl #12 +stmfd sp!, {r4, r5, lr} +mov r4, r2, lsl #12 +mov r12, r0 +mov r5, r1 tst r2, #0x01000000 beq 0f -# Fill +@ Fill tst r2, #0x04000000 beq 1f -# Word -add r3, r1, r3, lsr #10 -ldmia r0!, {r2} +@ Word +add r4, r5, r4, lsr #10 +ldmia r12!, {r3} 2: -cmp r1, r3 -stmltia r1!, {r2} +cmp r5, r4 +stmltia r5!, {r3} blt 2b b 3f -# Halfword +@ Halfword 1: -bic r0, #1 -bic r1, #1 -add r3, r1, r3, lsr #11 -ldrh r2, [r0] +bic r12, #1 +bic r5, #1 +add r4, r5, r4, lsr #11 +ldrh r3, [r12] 2: -cmp r1, r3 -strlth r2, [r1], #2 +cmp r5, r4 +strlth r3, [r5], #2 blt 2b b 3f -# Copy +@ Copy 0: tst r2, #0x04000000 beq 1f -# Word -add r3, r1, r3, lsr #10 +@ Word +add r4, r5, r4, lsr #10 2: -cmp r1, r3 -ldmltia r0!, {r2} -stmltia r1!, {r2} +cmp r5, r4 +ldmltia r12!, {r3} +stmltia r5!, {r3} blt 2b b 3f -# Halfword +@ Halfword 1: -add r3, r1, r3, lsr #11 +add r4, r5, r4, lsr #11 2: -cmp r1, r3 -ldrlth r2, [r0], #2 -strlth r2, [r1], #2 +cmp r5, r4 +ldrlth r3, [r12], #2 +strlth r3, [r5], #2 blt 2b 3: -ldmfd sp!, {pc} +mov r3, #0x170 @ Match official BIOS's clobbered r3 +ldmfd sp!, {r4, r5, pc} CpuFastSet: stmfd sp!, {r4-r10, lr}

@@ -161,7 +164,7 @@ tst r2, #0x01000000

mov r3, r2, lsl #12 add r2, r1, r3, lsr #10 beq 0f -# Fill +@ Fill ldr r3, [r0] mov r4, r3 mov r5, r3

@@ -175,7 +178,7 @@ cmp r1, r2

stmltia r1!, {r3-r10} blt 1b b 2f -# Copy +@ Copy 0: cmp r1, r2 ldmltia r0!, {r3-r10}
M src/platform/opengl/gles2.csrc/platform/opengl/gles2.c

@@ -178,6 +178,15 @@ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);

#else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); #endif + + size_t n; + for (n = 0; n < context->nShaders; ++n) { + if (context->shaders[n].width < 0 || context->shaders[n].height < 0) { + context->shaders[n].dirty = true; + } + } + context->initialShader.dirty = true; + context->interframeShader.dirty = true; } static void mGLES2ContextDeinit(struct VideoBackend* v) {

@@ -191,6 +200,7 @@ free(context->initialShader.uniforms);

} static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h) { + struct mGLES2Context* context = (struct mGLES2Context*) v; unsigned drawW = w; unsigned drawH = h; if (v->lockAspectRatio) {

@@ -208,6 +218,12 @@ if (drawH >= v->height) {

drawH -= drawH % v->height; } } + size_t n; + for (n = 0; n < context->nShaders; ++n) { + if (context->shaders[n].width == 0 || context->shaders[n].height == 0) { + context->shaders[n].dirty = true; + } + } glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH); }

@@ -221,8 +237,6 @@ }

void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) { GLint viewport[4]; - glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo); - glGetIntegerv(GL_VIEWPORT, viewport); int drawW = shader->width; int drawH = shader->height;

@@ -246,6 +260,19 @@ padH = 0;

drawW -= drawW % context->d.width; drawH -= drawH % context->d.height; } + + if (shader->dirty) { + if (shader->tex && (shader->width <= 0 || shader->height <= 0)) { + GLint oldTex; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTex); + glBindTexture(GL_TEXTURE_2D, shader->tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, drawW, drawH, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldTex); + } + shader->dirty = false; + } + + glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo); glViewport(padW, padH, drawW, drawH); if (shader->blend) { glEnable(GL_BLEND);

@@ -256,14 +283,6 @@ glClearColor(0.f, 0.f, 0.f, 1.f);

glClear(GL_COLOR_BUFFER_BIT); } - if (shader->tex && (shader->width <= 0 || shader->height <= 0)) { - GLint oldTex; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTex); - glBindTexture(GL_TEXTURE_2D, shader->tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, drawW, drawH, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glBindTexture(GL_TEXTURE_2D, oldTex); - } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST); glUseProgram(shader->program);

@@ -340,6 +359,7 @@ GLint viewport[4];

glGetIntegerv(GL_VIEWPORT, viewport); context->finalShader.filter = v->filter; + context->finalShader.dirty = true; _drawShader(context, &context->initialShader); if (v->interframeBlending) { context->interframeShader.blend = true;

@@ -404,6 +424,7 @@ shader->height = height;

shader->integerScaling = integerScaling; shader->filter = false; shader->blend = false; + shader->dirty = true; shader->uniforms = uniforms; shader->nUniforms = nUniforms; glGenFramebuffers(1, &shader->fbo);
M src/platform/opengl/gles2.hsrc/platform/opengl/gles2.h

@@ -60,6 +60,7 @@ int height;

bool integerScaling; bool filter; bool blend; + bool dirty; GLuint tex; GLuint fbo; GLuint vao;
M src/platform/qt/ArchiveInspector.cppsrc/platform/qt/ArchiveInspector.cpp

@@ -10,7 +10,7 @@

using namespace QGBA; ArchiveInspector::ArchiveInspector(const QString& filename, QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) { m_ui.setupUi(this); connect(m_ui.archiveView, &LibraryController::doneLoading, [this]() {
M src/platform/qt/CMakeLists.txtsrc/platform/qt/CMakeLists.txt

@@ -83,6 +83,7 @@ LogController.cpp

LogConfigModel.cpp LogView.cpp MapView.cpp + MemoryDump.cpp MemoryModel.cpp MemorySearch.cpp MemoryView.cpp

@@ -129,6 +130,7 @@ IOViewer.ui

LoadSaveState.ui LogView.ui MapView.ui + MemoryDump.ui MemorySearch.ui MemoryView.ui ObjView.ui
M src/platform/qt/IOViewer.cppsrc/platform/qt/IOViewer.cpp

@@ -1024,7 +1024,7 @@ return s_registers;

} IOViewer::IOViewer(std::shared_ptr<CoreController> controller, QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) , m_controller(controller) { m_ui.setupUi(this);
A src/platform/qt/MemoryDump.cpp

@@ -0,0 +1,120 @@

+/* 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 "MemoryDump.h" + +#include "CoreController.h" +#include "GBAApp.h" +#include "LogController.h" + +using namespace QGBA; + +MemoryDump::MemoryDump(std::shared_ptr<CoreController> controller, QWidget* parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) + , m_controller(controller) +{ + m_ui.setupUi(this); + + connect(this, &QDialog::accepted, this, &MemoryDump::save); +} + +void MemoryDump::save() { + QString filename = GBAApp::app()->getSaveFileName(this, tr("Save memory region")); + if (filename.isNull()) { + return; + } + QFile outfile(filename); + if (!outfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + LOG(QT, WARN) << tr("Failed to open output file: %1").arg(filename); + return; + } + QByteArray out(serialize()); + outfile.write(out); +} + +void MemoryDump::setAddress(uint32_t start) { + m_ui.address->setValue(start); +} + +void MemoryDump::setSegment(int seg) { + m_ui.segment->setValue(seg); +} + +void MemoryDump::setByteCount(uint32_t count) { + m_ui.bytes->setValue(count); +} + +QByteArray MemoryDump::serialize() { + CoreController::Interrupter interrupter(m_controller); + mCore* core = m_controller->thread()->core; + const mCoreMemoryBlock* blocks; + size_t nBlocks = core->listMemoryBlocks(core, &blocks); + + int size = m_ui.bytes->value(); + uint32_t start = m_ui.address->value(); + int segment = m_ui.segment->value(); + bool spanSegments = m_ui.spanSegments->isChecked(); + + QByteArray mem; + while (size) { + const mCoreMemoryBlock* bestMatch = NULL; + const char* block = NULL; + size_t blockSize = 0; + for (size_t i = 0; i < nBlocks; ++i) { + if (blocks[i].end <= start) { + continue; + } + if (blocks[i].start > start) { + continue; + } + block = static_cast<const char*>(core->getMemoryBlock(core, blocks[i].id, &blockSize)); + if (block) { + bestMatch = &blocks[i]; + break; + } else if (!bestMatch) { + bestMatch = &blocks[i]; + blockSize = 0; + } + } + if (!spanSegments) { + blockSize = bestMatch->end - bestMatch->start; + } else if (!blockSize) { + blockSize = bestMatch->size; + } + size_t segmentSize = bestMatch->end - bestMatch->start; + if (bestMatch->segmentStart) { + segmentSize = bestMatch->segmentStart - bestMatch->start; + } + if (segment > 0) { + start += segment * segmentSize; + } + int maxFromRegion = blockSize - (start - bestMatch->start); + if (maxFromRegion <= 0) { + continue; + } + int fromRegion = std::min(size, maxFromRegion); + if (block && (segment >= 0 || segmentSize == blockSize)) { + block = &block[start - bestMatch->start]; + mem.append(QByteArray::fromRawData(block, fromRegion)); + size -= fromRegion; + start += fromRegion; + if (spanSegments) { + break; + } + } else { + for (int i = 0; i < 16; ++i) { + char datum = core->rawRead8(core, start, segment); + mem.append(datum); + ++start; + --size; + if (!size) { + break; + } + } + } + } + + return mem; +}
A src/platform/qt/MemoryDump.h

@@ -0,0 +1,38 @@

+/* 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/. */ +#pragma once + +#include "ui_MemoryDump.h" + +#include <memory> + +namespace QGBA { + +class CoreController; + +class MemoryDump : public QDialog { +Q_OBJECT + +public: + MemoryDump(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr); + +public slots: + void setSegment(int); + void setAddress(uint32_t address); + void setByteCount(uint32_t); + +private slots: + void save(); + +private: + QByteArray serialize(); + + Ui::MemoryDump m_ui; + + std::shared_ptr<CoreController> m_controller; +}; + +}
A src/platform/qt/MemoryDump.ui

@@ -0,0 +1,159 @@

+<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MemoryDump</class> + <widget class="QDialog" name="MemoryDump"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>448</width> + <height>208</height> + </rect> + </property> + <property name="windowTitle"> + <string>Save Memory Range</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Start Address:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QSpinBox" name="segment"> + <property name="minimum"> + <number>-1</number> + </property> + <property name="maximum"> + <number>511</number> + </property> + <property name="displayIntegerBase"> + <number>16</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="address"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="accelerated"> + <bool>true</bool> + </property> + <property name="prefix"> + <string>0x</string> + </property> + <property name="maximum"> + <number>268435455</number> + </property> + <property name="singleStep"> + <number>16</number> + </property> + <property name="displayIntegerBase"> + <number>16</number> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Byte Count:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QSpinBox" name="bytes"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="prefix"> + <string>0x</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>268435456</number> + </property> + <property name="value"> + <number>256</number> + </property> + <property name="displayIntegerBase"> + <number>16</number> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QCheckBox" name="spanSegments"> + <property name="text"> + <string>Dump across banks</string> + </property> + </widget> + </item> + <item row="3" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>MemoryDump</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>MemoryDump</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>
M src/platform/qt/MemoryView.cppsrc/platform/qt/MemoryView.cpp

@@ -7,6 +7,7 @@

#include "MemoryView.h" #include "CoreController.h" +#include "MemoryDump.h" #include <mgba/core/core.h>

@@ -44,6 +45,7 @@ connect(m_ui.width32, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(4); });

connect(m_ui.setAddress, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, static_cast<void (MemoryView::*)(uint32_t)>(&MemoryView::jumpToAddress)); connect(m_ui.hexfield, &MemoryModel::selectionChanged, this, &MemoryView::updateSelection); + connect(m_ui.saveRange, &QAbstractButton::clicked, this, &MemoryView::saveRange); connect(controller.get(), &CoreController::stopping, this, &QWidget::close);

@@ -69,6 +71,7 @@

m_region = qMakePair(info.start, info.end); m_ui.segments->setValue(-1); m_ui.segments->setVisible(info.maxSegment > 0); + m_ui.segmentColon->setVisible(info.maxSegment > 0); m_ui.segments->setMaximum(info.maxSegment); m_ui.hexfield->setRegion(info.start, info.end - info.start, info.shortName); }

@@ -142,3 +145,12 @@ m_ui.uintVal->setText(QString::number(value.u32));

break; } } + +void MemoryView::saveRange() { + MemoryDump* memdump = new MemoryDump(m_controller); + memdump->setAttribute(Qt::WA_DeleteOnClose); + memdump->setAddress(m_selection.first); + memdump->setSegment(m_ui.segments->value()); + memdump->setByteCount(m_selection.second - m_selection.first); + memdump->show(); +}
M src/platform/qt/MemoryView.hsrc/platform/qt/MemoryView.h

@@ -28,6 +28,7 @@ void setIndex(int);

void setSegment(int); void updateSelection(uint32_t start, uint32_t end); void updateStatus(); + void saveRange(); private: Ui::MemoryView m_ui;
M src/platform/qt/MemoryView.uisrc/platform/qt/MemoryView.ui

@@ -6,8 +6,8 @@ <property name="geometry">

<rect> <x>0</x> <y>0</y> - <width>822</width> - <height>886</height> + <width>874</width> + <height>900</height> </rect> </property> <property name="windowTitle">

@@ -49,6 +49,13 @@ <number>0</number>

</property> <property name="displayIntegerBase"> <number>16</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="segmentColon"> + <property name="text"> + <string>:</string> </property> </widget> </item>

@@ -268,6 +275,13 @@ <item>

<widget class="QPushButton" name="save"> <property name="text"> <string>Save Selection</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="saveRange"> + <property name="text"> + <string>Save Range</string> </property> </widget> </item>
M src/platform/qt/PlacementControl.cppsrc/platform/qt/PlacementControl.cpp

@@ -14,7 +14,7 @@

using namespace QGBA; PlacementControl::PlacementControl(std::shared_ptr<CoreController> controller, QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) , m_controller(controller) { m_ui.setupUi(this);
M src/platform/qt/PrinterView.cppsrc/platform/qt/PrinterView.cpp

@@ -13,7 +13,7 @@

using namespace QGBA; PrinterView::PrinterView(std::shared_ptr<CoreController> controller, QWidget* parent) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) , m_controller(controller) { m_ui.setupUi(this);