Merge branch 'master' (early part) into medusa
jump to
@@ -35,6 +35,7 @@ - Debugger: Conditional breakpoints and watchpoints
- Ability to select GB/GBC/SGB BIOS on console ports - Optional automatic state saving/loading - Access to ur0 and uma0 partitions on the Vita + - Partial support for MBC6, MMM01, TAMA and HuC-1 GB mappers Bugfixes: - GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749) - GB Serialize: Fix audio state loading@@ -62,6 +63,7 @@ - GBA BIOS: Fix BitUnPack final byte
- GB I/O: DMA register is R/W - GB Video: Fix SCX timing - GBA Video: Improve sprite cycle counting (fixes mgba.io/i/1126) + - GB, GBA Savedata: Fix savestate loading overwriting saves on reset Misc: - GBA Timer: Use global cycles for timers - GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
@@ -129,6 +129,11 @@ uint8_t latch;
GBMBC7Field eeprom; }; +struct GBMMM01State { + bool locked; + int currentBank0; +}; + struct GBPocketCamState { bool registersActive; uint8_t registers[0x36];@@ -143,6 +148,7 @@ union GBMBCState {
struct GBMBC1State mbc1; struct GBMBC6State mbc6; struct GBMBC7State mbc7; + struct GBMMM01State mmm01; struct GBPocketCamState pocketCam; struct GBTAMA5State tama5; };
@@ -374,6 +374,10 @@ uint16_t sr;
uint32_t writable; } mbc7; struct { + uint8_t locked; + uint8_t bank0; + } mmm01; + struct { uint8_t reserved[16]; } padding; };
@@ -562,7 +562,7 @@
if (audio->playingCh4 && !audio->ch4.envelope.dead) { --audio->ch4.envelope.nextStep; if (audio->ch4.envelope.nextStep == 0) { - int8_t sample = (audio->ch4.sample > 0) * 0x8; + int8_t sample = audio->ch4.sample > 0; _updateEnvelope(&audio->ch4.envelope); if (audio->ch4.envelope.dead == 2) { mTimingDeschedule(timing, &audio->ch4Event);@@ -575,52 +575,55 @@ }
} void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) { - int sampleLeft = 0; - int sampleRight = 0; + int dcOffset = audio->style == GB_AUDIO_GBA ? 0 : 0x8; + int sampleLeft = dcOffset; + int sampleRight = dcOffset; if (audio->playingCh1 && !audio->forceDisableCh[0]) { if (audio->ch1Left) { - sampleLeft += audio->ch1.sample; + sampleLeft -= audio->ch1.sample; } if (audio->ch1Right) { - sampleRight += audio->ch1.sample; + sampleRight -= audio->ch1.sample; } } if (audio->playingCh2 && !audio->forceDisableCh[1]) { if (audio->ch2Left) { - sampleLeft += audio->ch2.sample; + sampleLeft -= audio->ch2.sample; } if (audio->ch2Right) { - sampleRight += audio->ch2.sample; + sampleRight -= audio->ch2.sample; } } if (audio->playingCh3 && !audio->forceDisableCh[2]) { if (audio->ch3Left) { - sampleLeft += audio->ch3.sample; + sampleLeft -= audio->ch3.sample; } if (audio->ch3Right) { - sampleRight += audio->ch3.sample; + sampleRight -= audio->ch3.sample; } } if (audio->playingCh4 && !audio->forceDisableCh[3]) { if (audio->ch4Left) { - sampleLeft += audio->ch4.sample; + sampleLeft -= audio->ch4.sample; } if (audio->ch4Right) { - sampleRight += audio->ch4.sample; + sampleRight -= audio->ch4.sample; } } - int dcOffset = audio->style == GB_AUDIO_GBA ? 0 : 0x20A; - *left = (sampleLeft - dcOffset) * (1 + audio->volumeLeft); - *right = (sampleRight - dcOffset) * (1 + audio->volumeRight); + sampleLeft <<= 3; + sampleRight <<= 3; + + *left = sampleLeft * (1 + audio->volumeLeft); + *right = sampleRight * (1 + audio->volumeRight); } static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {@@ -644,6 +647,8 @@
if ((size_t) blip_samples_avail(audio->left) < audio->samples) { blip_add_delta(audio->left, audio->clock, sampleLeft - audio->lastLeft); blip_add_delta(audio->right, audio->clock, sampleRight - audio->lastRight); + audio->lastLeft = sampleLeft; + audio->lastRight = sampleRight; audio->clock += audio->sampleInterval; if (audio->clock >= CLOCKS_PER_BLIP_FRAME) { blip_end_frame(audio->left, CLOCKS_PER_BLIP_FRAME);@@ -651,8 +656,6 @@ blip_end_frame(audio->right, CLOCKS_PER_BLIP_FRAME);
audio->clock -= CLOCKS_PER_BLIP_FRAME; } } - audio->lastLeft = sampleLeft; - audio->lastRight = sampleRight; produced = blip_samples_avail(audio->left); if (audio->p->stream && audio->p->stream->postAudioFrame) { audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);@@ -719,7 +722,7 @@ return (envelope->initialVolume || envelope->direction) && envelope->dead != 2;
} static void _updateSquareSample(struct GBAudioSquareChannel* ch) { - ch->sample = ch->control.hi * ch->envelope.currentVolume * 0x8; + ch->sample = ch->control.hi * ch->envelope.currentVolume; } static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch) {@@ -820,19 +823,17 @@ int i;
int volume; switch (ch->volume) { case 0: - volume = 0; + volume = 4; break; case 1: - volume = 4; + volume = 0; break; case 2: - volume = 2; - break; - case 3: volume = 1; break; default: - volume = 3; + case 3: + volume = 2; break; } int start;@@ -870,7 +871,10 @@ }
ch->sample = bitsCarry >> 4; break; } - ch->sample *= volume * 2; + if (ch->volume > 3) { + ch->sample += ch->sample << 1; + } + ch->sample >>= volume; audio->ch3.readable = true; if (audio->style == GB_AUDIO_DMG) { mTimingDeschedule(audio->timing, &audio->ch3Fade);@@ -897,8 +901,7 @@ int32_t cycles = 0;
do { int lsb = ch->lfsr & 1; - ch->sample = lsb * 0x8; - ch->sample *= ch->envelope.currentVolume; + ch->sample = lsb * ch->envelope.currentVolume; ch->lfsr >>= 1; ch->lfsr ^= (lsb * 0x60) << (ch->power ? 0 : 8); cycles += baseCycles;
@@ -295,7 +295,6 @@ gb->memory.mbcType = GB_MBC_AUTODETECT;
gb->isPristine = false; gb->sramMaskWriteback = false; - GBSavedataUnmask(gb); GBSramDeinit(gb); if (gb->sramRealVf) { gb->sramRealVf->close(gb->sramRealVf);@@ -468,6 +467,7 @@ GBSIOReset(&gb->sio);
cpu->memory.setActiveRegion(cpu, cpu->pc); + gb->sramMaskWriteback = false; GBSavedataUnmask(gb); }
@@ -9,7 +9,10 @@ #include <mgba/core/interface.h>
#include <mgba/internal/lr35902/lr35902.h> #include <mgba/internal/gb/gb.h> #include <mgba/internal/gb/memory.h> +#include <mgba-util/crc32.h> #include <mgba-util/vfs.h> + +const uint32_t GB_LOGO_HASH = 0x46195417; mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC", "gb.mbc");@@ -27,6 +30,8 @@ static void _GBMBC3(struct GB*, uint16_t address, uint8_t value);
static void _GBMBC5(struct GB*, uint16_t address, uint8_t value); static void _GBMBC6(struct GB*, uint16_t address, uint8_t value); static void _GBMBC7(struct GB*, uint16_t address, uint8_t value); +static void _GBMMM01(struct GB*, uint16_t address, uint8_t value); +static void _GBHuC1(struct GB*, uint16_t address, uint8_t value); static void _GBHuC3(struct GB*, uint16_t address, uint8_t value); static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value); static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value);@@ -56,7 +61,7 @@ }
} void GBMBCSwitchBank0(struct GB* gb, int bank) { - size_t bankStart = bank * GB_SIZE_CART_BANK0 << gb->memory.mbcState.mbc1.multicartStride; + size_t bankStart = bank * GB_SIZE_CART_BANK0; if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) { mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank); bankStart &= (gb->memory.romSize - 1);@@ -134,6 +139,12 @@
void GBMBCInit(struct GB* gb) { const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; if (gb->memory.rom) { + if (gb->memory.romSize >= 0x8000) { + const struct GBCartridge* cartFooter = (const struct GBCartridge*) &gb->memory.rom[gb->memory.romSize - 0x7F00]; + if (doCrc32(cartFooter->logo, sizeof(cartFooter->logo)) == GB_LOGO_HASH) { + cart = cartFooter; + } + } switch (cart->ramSize) { case 0: gb->sramSize = 0;@@ -177,6 +188,11 @@ case 5:
case 6: gb->memory.mbcType = GB_MBC2; break; + case 0x0B: + case 0x0C: + case 0x0D: + gb->memory.mbcType = GB_MMM01; + break; case 0x0F: case 0x10: gb->memory.mbcType = GB_MBC3_RTC;@@ -255,12 +271,10 @@ gb->memory.mbcRead = _GBMBC7Read;
gb->sramSize = 0x100; break; case GB_MMM01: - mLOG(GB_MBC, WARN, "unimplemented MBC: MMM01"); - gb->memory.mbcWrite = _GBMBC1; + gb->memory.mbcWrite = _GBMMM01; break; case GB_HuC1: - mLOG(GB_MBC, WARN, "unimplemented MBC: HuC-1"); - gb->memory.mbcWrite = _GBMBC1; + gb->memory.mbcWrite = _GBHuC1; break; case GB_HuC3: gb->memory.mbcWrite = _GBHuC3;@@ -393,7 +407,7 @@ break;
case 0x2: bank &= 3; if (memory->mbcState.mbc1.mode) { - GBMBCSwitchBank0(gb, bank); + GBMBCSwitchBank0(gb, bank << gb->memory.mbcState.mbc1.multicartStride); GBMBCSwitchSramBank(gb, bank); } GBMBCSwitchBank(gb, (bank << memory->mbcState.mbc1.multicartStride) | (memory->currentBank & (stride - 1)));@@ -401,7 +415,7 @@ break;
case 0x3: memory->mbcState.mbc1.mode = value & 1; if (memory->mbcState.mbc1.mode) { - GBMBCSwitchBank0(gb, memory->currentBank >> memory->mbcState.mbc1.multicartStride); + GBMBCSwitchBank0(gb, memory->currentBank & ~((1 << memory->mbcState.mbc1.multicartStride) - 1)); } else { GBMBCSwitchBank0(gb, 0); GBMBCSwitchSramBank(gb, 0);@@ -819,6 +833,82 @@ } else if (GBMBC7FieldIsCS(value) && GBMBC7FieldIsCLK(old) && !GBMBC7FieldIsCLK(value)) {
value = GBMBC7FieldSetDO(value, GBMBC7FieldGetDO(old)); } mbc7->eeprom = value; +} + +void _GBMMM01(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + if (!memory->mbcState.mmm01.locked) { + switch (address >> 13) { + case 0x0: + memory->mbcState.mmm01.locked = true; + GBMBCSwitchBank0(gb, memory->mbcState.mmm01.currentBank0); + break; + case 0x1: + memory->mbcState.mmm01.currentBank0 &= ~0x7F; + memory->mbcState.mmm01.currentBank0 |= value & 0x7F; + break; + case 0x2: + memory->mbcState.mmm01.currentBank0 &= ~0x180; + memory->mbcState.mmm01.currentBank0 |= (value & 0x30) << 3; + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value); + break; + } + return; + } + switch (address >> 13) { + case 0x0: + switch (value) { + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + memory->sramAccess = false; + break; + } + break; + case 0x1: + GBMBCSwitchBank(gb, value + memory->mbcState.mmm01.currentBank0); + break; + case 0x2: + GBMBCSwitchSramBank(gb, value); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value); + break; + } +} + +void _GBHuC1(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value & 0x3F; + switch (address >> 13) { + case 0x0: + switch (value) { + case 0xE: + memory->sramAccess = false; + break; + default: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + } + break; + case 0x1: + GBMBCSwitchBank(gb, bank); + break; + case 0x2: + GBMBCSwitchSramBank(gb, value); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "HuC-1 unknown address: %04X:%02X", address, value); + break; + } } void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
@@ -176,6 +176,8 @@ gb->memory.hdmaEvent.callback = _GBMemoryHDMAService;
gb->memory.hdmaEvent.priority = 0x41; memset(&gb->memory.hram, 0, sizeof(gb->memory.hram)); + + GBMBCInit(gb); switch (gb->memory.mbcType) { case GB_MBC1: gb->memory.mbcState.mbc1.mode = 0;@@ -187,11 +189,12 @@ gb->memory.mbcState.mbc6.sramAccess = false;
GBMBCSwitchSramHalfBank(gb, 0, 0); GBMBCSwitchSramHalfBank(gb, 0, 1); break; + case GB_MMM01: + GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2); + GBMBCSwitchBank(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 1); default: memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState)); } - - GBMBCInit(gb); gb->memory.sramBank = gb->memory.sram; if (!gb->memory.wram) {@@ -688,6 +691,10 @@ state->memory.mbc7.srBits = memory->mbcState.mbc7.srBits;
STORE_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr); STORE_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable); break; + case GB_MMM01: + state->memory.mmm01.locked = memory->mbcState.mmm01.locked; + state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0; + break; default: break; }@@ -754,6 +761,15 @@ memory->mbcState.mbc7.latch = state->memory.mbc7.latch;
memory->mbcState.mbc7.srBits = state->memory.mbc7.srBits; LOAD_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr); LOAD_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable); + break; + case GB_MMM01: + memory->mbcState.mmm01.locked = state->memory.mmm01.locked; + memory->mbcState.mmm01.currentBank0 = state->memory.mmm01.bank0; + if (memory->mbcState.mmm01.locked) { + GBMBCSwitchBank0(gb, memory->mbcState.mmm01.currentBank0); + } else { + GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2); + } break; default: break;
@@ -185,6 +185,7 @@ cpu->gprs[ARM_SP] = SP_BASE_SYSTEM;
struct GBA* gba = (struct GBA*) cpu->master; if (!gba->rr || (!gba->rr->isPlaying(gba->rr) && !gba->rr->isRecording(gba->rr))) { + gba->memory.savedata.maskWriteback = false; GBASavedataUnmask(&gba->memory.savedata); }
@@ -10,6 +10,7 @@ #include "GBAApp.h"
#include "LogController.h" #include <mgba-util/png-io.h> +#include <mgba-util/vfs.h> #ifdef M_CORE_GBA #include <mgba/internal/gba/memory.h> #endif@@ -184,5 +185,6 @@ mMapCache* mapCache = mMapCacheSetGetPointer(&m_cacheSet->maps, m_map);
QImage map = m_rawMap.rgbSwapped(); PNGWritePixelsA(png, map.width(), map.height(), map.bytesPerLine() / 4, static_cast<const void*>(map.constBits())); PNGWriteClose(png, info); + vf->close(vf); } #endif
@@ -22,6 +22,7 @@ #include <mgba/internal/gb/gb.h>
#include <mgba/internal/gb/io.h> #endif #include <mgba-util/png-io.h> +#include <mgba-util/vfs.h> using namespace QGBA;@@ -283,6 +284,7 @@ }
PNGWritePixels8(png, m_objInfo.width * 8, m_objInfo.height * 8, m_objInfo.width * 8, static_cast<void*>(buffer)); PNGWriteClose(png, info); delete[] buffer; + vf->close(vf); } #endif
@@ -25,7 +25,7 @@ m_ui.setupUi(this);
m_ui.tile->setController(controller); connect(m_ui.tiles, &TilePainter::indexPressed, m_ui.tile, &AssetTile::selectIndex); - connect(m_ui.paletteId, &QAbstractSlider::valueChanged, this, &TileView::updatePalette); + connect(m_ui.paletteId, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TileView::updatePalette); switch (m_controller->platform()) { #ifdef M_CORE_GBA
@@ -6,7 +6,7 @@ <property name="geometry">
<rect> <x>0</x> <y>0</y> - <width>498</width> + <width>501</width> <height>335</height> </rect> </property>@@ -14,49 +14,10 @@ <property name="windowTitle">
<string>Tiles</string> </property> <layout class="QGridLayout" name="gridLayout"> + <item row="2" column="0"> + <widget class="QGBA::AssetTile" name="tile"/> + </item> <item row="1" column="0"> - <widget class="QCheckBox" name="palette256"> - <property name="text"> - <string>256 colors</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QSlider" name="paletteId"> - <property name="maximumSize"> - <size> - <width>170</width> - <height>16777215</height> - </size> - </property> - <property name="maximum"> - <number>15</number> - </property> - <property name="pageStep"> - <number>1</number> - </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="tickPosition"> - <enum>QSlider::TicksBelow</enum> - </property> - </widget> - </item> - <item row="4" column="0"> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - </spacer> - </item> - <item row="2" column="0"> <layout class="QHBoxLayout" name="horizontalLayout_4"> <item> <widget class="QSpinBox" name="magnification">@@ -105,7 +66,7 @@ <property name="geometry">
<rect> <x>0</x> <y>0</y> - <width>286</width> + <width>256</width> <height>768</height> </rect> </property>@@ -151,8 +112,36 @@ </layout>
</widget> </widget> </item> - <item row="3" column="0"> - <widget class="QGBA::AssetTile" name="tile"/> + <item row="4" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QSpinBox" name="paletteId"> + <property name="maximum"> + <number>15</number> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="palette256"> + <property name="text"> + <string>256 colors</string> + </property> + </widget> + </item> + </layout> </item> </layout> </widget>@@ -176,22 +165,6 @@ </customwidgets>
<resources/> <connections> <connection> - <sender>palette256</sender> - <signal>toggled(bool)</signal> - <receiver>paletteId</receiver> - <slot>setDisabled(bool)</slot> - <hints> - <hint type="sourcelabel"> - <x>100</x> - <y>54</y> - </hint> - <hint type="destinationlabel"> - <x>96</x> - <y>22</y> - </hint> - </hints> - </connection> - <connection> <sender>magnification</sender> <signal>valueChanged(int)</signal> <receiver>tiles</receiver>@@ -204,6 +177,22 @@ </hint>
<hint type="destinationlabel"> <x>339</x> <y>396</y> + </hint> + </hints> + </connection> + <connection> + <sender>palette256</sender> + <signal>toggled(bool)</signal> + <receiver>paletteId</receiver> + <slot>setDisabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>158</x> + <y>29</y> + </hint> + <hint type="destinationlabel"> + <x>44</x> + <y>29</y> </hint> </hints> </connection>
@@ -3,7 +3,7 @@ find_program(GXTEXCONV gxtexconv)
find_program(RAW2C raw2c) find_program(WIILOAD wiiload) -set(OS_DEFINES COLOR_16_BIT COLOR_5_6_5 USE_VFS_FILE IOAPI_NO_64) +set(OS_DEFINES COLOR_16_BIT COLOR_5_6_5 USE_VFS_FILE IOAPI_NO_64 FIXED_ROM_BUFFER) list(APPEND CORE_VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-file.c ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-dirent.c ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-devlist.c) include_directories(${CMAKE_CURRENT_BINARY_DIR})
@@ -253,7 +253,8 @@
memset(audioBuffer, 0, sizeof(audioBuffer)); #ifdef FIXED_ROM_BUFFER romBufferSize = SIZE_CART0; - romBuffer = anonymousMemoryMap(romBufferSize); + romBuffer = SYS_GetArena2Lo(); + SYS_SetArena2Lo((void*)((intptr_t) romBuffer + SIZE_CART0)); #endif #if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)@@ -529,10 +530,6 @@ VIDEO_SetBlack(true);
VIDEO_Flush(); VIDEO_WaitVSync(); mGUIDeinit(&runner); - -#ifdef FIXED_ROM_BUFFER - mappedMemoryFree(romBuffer, romBufferSize); -#endif free(fifo); free(texmem);