Merge branch 'master' (early part) into medusa
2
CHANGES
|
@ -35,6 +35,7 @@ Features:
|
||||||
- Ability to select GB/GBC/SGB BIOS on console ports
|
- Ability to select GB/GBC/SGB BIOS on console ports
|
||||||
- Optional automatic state saving/loading
|
- Optional automatic state saving/loading
|
||||||
- Access to ur0 and uma0 partitions on the Vita
|
- Access to ur0 and uma0 partitions on the Vita
|
||||||
|
- Partial support for MBC6, MMM01, TAMA and HuC-1 GB mappers
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
- GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749)
|
- GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749)
|
||||||
- GB Serialize: Fix audio state loading
|
- GB Serialize: Fix audio state loading
|
||||||
|
@ -62,6 +63,7 @@ Bugfixes:
|
||||||
- GB I/O: DMA register is R/W
|
- GB I/O: DMA register is R/W
|
||||||
- GB Video: Fix SCX timing
|
- GB Video: Fix SCX timing
|
||||||
- GBA Video: Improve sprite cycle counting (fixes mgba.io/i/1126)
|
- GBA Video: Improve sprite cycle counting (fixes mgba.io/i/1126)
|
||||||
|
- GB, GBA Savedata: Fix savestate loading overwriting saves on reset
|
||||||
Misc:
|
Misc:
|
||||||
- GBA Timer: Use global cycles for timers
|
- GBA Timer: Use global cycles for timers
|
||||||
- GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
|
- GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
|
||||||
|
|
|
@ -53,15 +53,12 @@ The following mappers are fully supported:
|
||||||
|
|
||||||
The following mappers are partially supported:
|
The following mappers are partially supported:
|
||||||
|
|
||||||
|
- MBC6
|
||||||
|
- MMM01
|
||||||
- Pocket Cam
|
- Pocket Cam
|
||||||
- TAMA5
|
- TAMA5
|
||||||
- HuC-3
|
|
||||||
|
|
||||||
The following mappers are not currently supported:
|
|
||||||
|
|
||||||
- MBC6
|
|
||||||
- HuC-1
|
- HuC-1
|
||||||
- MMM01
|
- HuC-3
|
||||||
|
|
||||||
### Planned features
|
### Planned features
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.0 KiB |
|
@ -129,6 +129,11 @@ struct GBMBC7State {
|
||||||
GBMBC7Field eeprom;
|
GBMBC7Field eeprom;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GBMMM01State {
|
||||||
|
bool locked;
|
||||||
|
int currentBank0;
|
||||||
|
};
|
||||||
|
|
||||||
struct GBPocketCamState {
|
struct GBPocketCamState {
|
||||||
bool registersActive;
|
bool registersActive;
|
||||||
uint8_t registers[0x36];
|
uint8_t registers[0x36];
|
||||||
|
@ -143,6 +148,7 @@ union GBMBCState {
|
||||||
struct GBMBC1State mbc1;
|
struct GBMBC1State mbc1;
|
||||||
struct GBMBC6State mbc6;
|
struct GBMBC6State mbc6;
|
||||||
struct GBMBC7State mbc7;
|
struct GBMBC7State mbc7;
|
||||||
|
struct GBMMM01State mmm01;
|
||||||
struct GBPocketCamState pocketCam;
|
struct GBPocketCamState pocketCam;
|
||||||
struct GBTAMA5State tama5;
|
struct GBTAMA5State tama5;
|
||||||
};
|
};
|
||||||
|
|
|
@ -373,6 +373,10 @@ struct GBSerializedState {
|
||||||
uint16_t sr;
|
uint16_t sr;
|
||||||
uint32_t writable;
|
uint32_t writable;
|
||||||
} mbc7;
|
} mbc7;
|
||||||
|
struct {
|
||||||
|
uint8_t locked;
|
||||||
|
uint8_t bank0;
|
||||||
|
} mmm01;
|
||||||
struct {
|
struct {
|
||||||
uint8_t reserved[16];
|
uint8_t reserved[16];
|
||||||
} padding;
|
} padding;
|
||||||
|
|
|
@ -562,7 +562,7 @@ void GBAudioUpdateFrame(struct GBAudio* audio, struct mTiming* timing) {
|
||||||
if (audio->playingCh4 && !audio->ch4.envelope.dead) {
|
if (audio->playingCh4 && !audio->ch4.envelope.dead) {
|
||||||
--audio->ch4.envelope.nextStep;
|
--audio->ch4.envelope.nextStep;
|
||||||
if (audio->ch4.envelope.nextStep == 0) {
|
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);
|
_updateEnvelope(&audio->ch4.envelope);
|
||||||
if (audio->ch4.envelope.dead == 2) {
|
if (audio->ch4.envelope.dead == 2) {
|
||||||
mTimingDeschedule(timing, &audio->ch4Event);
|
mTimingDeschedule(timing, &audio->ch4Event);
|
||||||
|
@ -575,52 +575,55 @@ void GBAudioUpdateFrame(struct GBAudio* audio, struct mTiming* timing) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) {
|
void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) {
|
||||||
int sampleLeft = 0;
|
int dcOffset = audio->style == GB_AUDIO_GBA ? 0 : 0x8;
|
||||||
int sampleRight = 0;
|
int sampleLeft = dcOffset;
|
||||||
|
int sampleRight = dcOffset;
|
||||||
|
|
||||||
if (audio->playingCh1 && !audio->forceDisableCh[0]) {
|
if (audio->playingCh1 && !audio->forceDisableCh[0]) {
|
||||||
if (audio->ch1Left) {
|
if (audio->ch1Left) {
|
||||||
sampleLeft += audio->ch1.sample;
|
sampleLeft -= audio->ch1.sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio->ch1Right) {
|
if (audio->ch1Right) {
|
||||||
sampleRight += audio->ch1.sample;
|
sampleRight -= audio->ch1.sample;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio->playingCh2 && !audio->forceDisableCh[1]) {
|
if (audio->playingCh2 && !audio->forceDisableCh[1]) {
|
||||||
if (audio->ch2Left) {
|
if (audio->ch2Left) {
|
||||||
sampleLeft += audio->ch2.sample;
|
sampleLeft -= audio->ch2.sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio->ch2Right) {
|
if (audio->ch2Right) {
|
||||||
sampleRight += audio->ch2.sample;
|
sampleRight -= audio->ch2.sample;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio->playingCh3 && !audio->forceDisableCh[2]) {
|
if (audio->playingCh3 && !audio->forceDisableCh[2]) {
|
||||||
if (audio->ch3Left) {
|
if (audio->ch3Left) {
|
||||||
sampleLeft += audio->ch3.sample;
|
sampleLeft -= audio->ch3.sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio->ch3Right) {
|
if (audio->ch3Right) {
|
||||||
sampleRight += audio->ch3.sample;
|
sampleRight -= audio->ch3.sample;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio->playingCh4 && !audio->forceDisableCh[3]) {
|
if (audio->playingCh4 && !audio->forceDisableCh[3]) {
|
||||||
if (audio->ch4Left) {
|
if (audio->ch4Left) {
|
||||||
sampleLeft += audio->ch4.sample;
|
sampleLeft -= audio->ch4.sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio->ch4Right) {
|
if (audio->ch4Right) {
|
||||||
sampleRight += audio->ch4.sample;
|
sampleRight -= audio->ch4.sample;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int dcOffset = audio->style == GB_AUDIO_GBA ? 0 : 0x20A;
|
sampleLeft <<= 3;
|
||||||
*left = (sampleLeft - dcOffset) * (1 + audio->volumeLeft);
|
sampleRight <<= 3;
|
||||||
*right = (sampleRight - dcOffset) * (1 + audio->volumeRight);
|
|
||||||
|
*left = sampleLeft * (1 + audio->volumeLeft);
|
||||||
|
*right = sampleRight * (1 + audio->volumeRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||||
|
@ -644,6 +647,8 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||||
if ((size_t) blip_samples_avail(audio->left) < audio->samples) {
|
if ((size_t) blip_samples_avail(audio->left) < audio->samples) {
|
||||||
blip_add_delta(audio->left, audio->clock, sampleLeft - audio->lastLeft);
|
blip_add_delta(audio->left, audio->clock, sampleLeft - audio->lastLeft);
|
||||||
blip_add_delta(audio->right, audio->clock, sampleRight - audio->lastRight);
|
blip_add_delta(audio->right, audio->clock, sampleRight - audio->lastRight);
|
||||||
|
audio->lastLeft = sampleLeft;
|
||||||
|
audio->lastRight = sampleRight;
|
||||||
audio->clock += audio->sampleInterval;
|
audio->clock += audio->sampleInterval;
|
||||||
if (audio->clock >= CLOCKS_PER_BLIP_FRAME) {
|
if (audio->clock >= CLOCKS_PER_BLIP_FRAME) {
|
||||||
blip_end_frame(audio->left, CLOCKS_PER_BLIP_FRAME);
|
blip_end_frame(audio->left, CLOCKS_PER_BLIP_FRAME);
|
||||||
|
@ -651,8 +656,6 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||||
audio->clock -= CLOCKS_PER_BLIP_FRAME;
|
audio->clock -= CLOCKS_PER_BLIP_FRAME;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
audio->lastLeft = sampleLeft;
|
|
||||||
audio->lastRight = sampleRight;
|
|
||||||
produced = blip_samples_avail(audio->left);
|
produced = blip_samples_avail(audio->left);
|
||||||
if (audio->p->stream && audio->p->stream->postAudioFrame) {
|
if (audio->p->stream && audio->p->stream->postAudioFrame) {
|
||||||
audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
|
audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
|
||||||
|
@ -719,7 +722,7 @@ bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value, enum GBAudi
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _updateSquareSample(struct GBAudioSquareChannel* ch) {
|
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) {
|
static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch) {
|
||||||
|
@ -820,19 +823,17 @@ static void _updateChannel3(struct mTiming* timing, void* user, uint32_t cyclesL
|
||||||
int volume;
|
int volume;
|
||||||
switch (ch->volume) {
|
switch (ch->volume) {
|
||||||
case 0:
|
case 0:
|
||||||
volume = 0;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
volume = 4;
|
volume = 4;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 1:
|
||||||
volume = 2;
|
volume = 0;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 2:
|
||||||
volume = 1;
|
volume = 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
volume = 3;
|
case 3:
|
||||||
|
volume = 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int start;
|
int start;
|
||||||
|
@ -870,7 +871,10 @@ static void _updateChannel3(struct mTiming* timing, void* user, uint32_t cyclesL
|
||||||
ch->sample = bitsCarry >> 4;
|
ch->sample = bitsCarry >> 4;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ch->sample *= volume * 2;
|
if (ch->volume > 3) {
|
||||||
|
ch->sample += ch->sample << 1;
|
||||||
|
}
|
||||||
|
ch->sample >>= volume;
|
||||||
audio->ch3.readable = true;
|
audio->ch3.readable = true;
|
||||||
if (audio->style == GB_AUDIO_DMG) {
|
if (audio->style == GB_AUDIO_DMG) {
|
||||||
mTimingDeschedule(audio->timing, &audio->ch3Fade);
|
mTimingDeschedule(audio->timing, &audio->ch3Fade);
|
||||||
|
@ -897,8 +901,7 @@ static void _updateChannel4(struct mTiming* timing, void* user, uint32_t cyclesL
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int lsb = ch->lfsr & 1;
|
int lsb = ch->lfsr & 1;
|
||||||
ch->sample = lsb * 0x8;
|
ch->sample = lsb * ch->envelope.currentVolume;
|
||||||
ch->sample *= ch->envelope.currentVolume;
|
|
||||||
ch->lfsr >>= 1;
|
ch->lfsr >>= 1;
|
||||||
ch->lfsr ^= (lsb * 0x60) << (ch->power ? 0 : 8);
|
ch->lfsr ^= (lsb * 0x60) << (ch->power ? 0 : 8);
|
||||||
cycles += baseCycles;
|
cycles += baseCycles;
|
||||||
|
|
|
@ -295,7 +295,6 @@ void GBUnloadROM(struct GB* gb) {
|
||||||
gb->isPristine = false;
|
gb->isPristine = false;
|
||||||
|
|
||||||
gb->sramMaskWriteback = false;
|
gb->sramMaskWriteback = false;
|
||||||
GBSavedataUnmask(gb);
|
|
||||||
GBSramDeinit(gb);
|
GBSramDeinit(gb);
|
||||||
if (gb->sramRealVf) {
|
if (gb->sramRealVf) {
|
||||||
gb->sramRealVf->close(gb->sramRealVf);
|
gb->sramRealVf->close(gb->sramRealVf);
|
||||||
|
@ -468,6 +467,7 @@ void GBReset(struct LR35902Core* cpu) {
|
||||||
|
|
||||||
cpu->memory.setActiveRegion(cpu, cpu->pc);
|
cpu->memory.setActiveRegion(cpu, cpu->pc);
|
||||||
|
|
||||||
|
gb->sramMaskWriteback = false;
|
||||||
GBSavedataUnmask(gb);
|
GBSavedataUnmask(gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
104
src/gb/mbc.c
|
@ -9,8 +9,11 @@
|
||||||
#include <mgba/internal/lr35902/lr35902.h>
|
#include <mgba/internal/lr35902/lr35902.h>
|
||||||
#include <mgba/internal/gb/gb.h>
|
#include <mgba/internal/gb/gb.h>
|
||||||
#include <mgba/internal/gb/memory.h>
|
#include <mgba/internal/gb/memory.h>
|
||||||
|
#include <mgba-util/crc32.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
|
const uint32_t GB_LOGO_HASH = 0x46195417;
|
||||||
|
|
||||||
mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC", "gb.mbc");
|
mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC", "gb.mbc");
|
||||||
|
|
||||||
static void _GBMBCNone(struct GB* gb, uint16_t address, uint8_t value) {
|
static void _GBMBCNone(struct GB* gb, uint16_t address, uint8_t value) {
|
||||||
|
@ -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 _GBMBC5(struct GB*, uint16_t address, uint8_t value);
|
||||||
static void _GBMBC6(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 _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 _GBHuC3(struct GB*, uint16_t address, uint8_t value);
|
||||||
static void _GBPocketCam(struct GB* 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);
|
static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value);
|
||||||
|
@ -56,7 +61,7 @@ void GBMBCSwitchBank(struct GB* gb, int bank) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBMBCSwitchBank0(struct GB* gb, int bank) {
|
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) {
|
if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) {
|
||||||
mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
|
mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
|
||||||
bankStart &= (gb->memory.romSize - 1);
|
bankStart &= (gb->memory.romSize - 1);
|
||||||
|
@ -134,6 +139,12 @@ void GBMBCSwitchSramHalfBank(struct GB* gb, int half, int bank) {
|
||||||
void GBMBCInit(struct GB* gb) {
|
void GBMBCInit(struct GB* gb) {
|
||||||
const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
|
const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
|
||||||
if (gb->memory.rom) {
|
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) {
|
switch (cart->ramSize) {
|
||||||
case 0:
|
case 0:
|
||||||
gb->sramSize = 0;
|
gb->sramSize = 0;
|
||||||
|
@ -177,6 +188,11 @@ void GBMBCInit(struct GB* gb) {
|
||||||
case 6:
|
case 6:
|
||||||
gb->memory.mbcType = GB_MBC2;
|
gb->memory.mbcType = GB_MBC2;
|
||||||
break;
|
break;
|
||||||
|
case 0x0B:
|
||||||
|
case 0x0C:
|
||||||
|
case 0x0D:
|
||||||
|
gb->memory.mbcType = GB_MMM01;
|
||||||
|
break;
|
||||||
case 0x0F:
|
case 0x0F:
|
||||||
case 0x10:
|
case 0x10:
|
||||||
gb->memory.mbcType = GB_MBC3_RTC;
|
gb->memory.mbcType = GB_MBC3_RTC;
|
||||||
|
@ -255,12 +271,10 @@ void GBMBCInit(struct GB* gb) {
|
||||||
gb->sramSize = 0x100;
|
gb->sramSize = 0x100;
|
||||||
break;
|
break;
|
||||||
case GB_MMM01:
|
case GB_MMM01:
|
||||||
mLOG(GB_MBC, WARN, "unimplemented MBC: MMM01");
|
gb->memory.mbcWrite = _GBMMM01;
|
||||||
gb->memory.mbcWrite = _GBMBC1;
|
|
||||||
break;
|
break;
|
||||||
case GB_HuC1:
|
case GB_HuC1:
|
||||||
mLOG(GB_MBC, WARN, "unimplemented MBC: HuC-1");
|
gb->memory.mbcWrite = _GBHuC1;
|
||||||
gb->memory.mbcWrite = _GBMBC1;
|
|
||||||
break;
|
break;
|
||||||
case GB_HuC3:
|
case GB_HuC3:
|
||||||
gb->memory.mbcWrite = _GBHuC3;
|
gb->memory.mbcWrite = _GBHuC3;
|
||||||
|
@ -393,7 +407,7 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) {
|
||||||
case 0x2:
|
case 0x2:
|
||||||
bank &= 3;
|
bank &= 3;
|
||||||
if (memory->mbcState.mbc1.mode) {
|
if (memory->mbcState.mbc1.mode) {
|
||||||
GBMBCSwitchBank0(gb, bank);
|
GBMBCSwitchBank0(gb, bank << gb->memory.mbcState.mbc1.multicartStride);
|
||||||
GBMBCSwitchSramBank(gb, bank);
|
GBMBCSwitchSramBank(gb, bank);
|
||||||
}
|
}
|
||||||
GBMBCSwitchBank(gb, (bank << memory->mbcState.mbc1.multicartStride) | (memory->currentBank & (stride - 1)));
|
GBMBCSwitchBank(gb, (bank << memory->mbcState.mbc1.multicartStride) | (memory->currentBank & (stride - 1)));
|
||||||
|
@ -401,7 +415,7 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) {
|
||||||
case 0x3:
|
case 0x3:
|
||||||
memory->mbcState.mbc1.mode = value & 1;
|
memory->mbcState.mbc1.mode = value & 1;
|
||||||
if (memory->mbcState.mbc1.mode) {
|
if (memory->mbcState.mbc1.mode) {
|
||||||
GBMBCSwitchBank0(gb, memory->currentBank >> memory->mbcState.mbc1.multicartStride);
|
GBMBCSwitchBank0(gb, memory->currentBank & ~((1 << memory->mbcState.mbc1.multicartStride) - 1));
|
||||||
} else {
|
} else {
|
||||||
GBMBCSwitchBank0(gb, 0);
|
GBMBCSwitchBank0(gb, 0);
|
||||||
GBMBCSwitchSramBank(gb, 0);
|
GBMBCSwitchSramBank(gb, 0);
|
||||||
|
@ -821,6 +835,82 @@ static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t valu
|
||||||
mbc7->eeprom = value;
|
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) {
|
void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
|
||||||
struct GBMemory* memory = &gb->memory;
|
struct GBMemory* memory = &gb->memory;
|
||||||
int bank = value & 0x3F;
|
int bank = value & 0x3F;
|
||||||
|
|
|
@ -176,6 +176,8 @@ void GBMemoryReset(struct GB* gb) {
|
||||||
gb->memory.hdmaEvent.priority = 0x41;
|
gb->memory.hdmaEvent.priority = 0x41;
|
||||||
|
|
||||||
memset(&gb->memory.hram, 0, sizeof(gb->memory.hram));
|
memset(&gb->memory.hram, 0, sizeof(gb->memory.hram));
|
||||||
|
|
||||||
|
GBMBCInit(gb);
|
||||||
switch (gb->memory.mbcType) {
|
switch (gb->memory.mbcType) {
|
||||||
case GB_MBC1:
|
case GB_MBC1:
|
||||||
gb->memory.mbcState.mbc1.mode = 0;
|
gb->memory.mbcState.mbc1.mode = 0;
|
||||||
|
@ -187,11 +189,12 @@ void GBMemoryReset(struct GB* gb) {
|
||||||
GBMBCSwitchSramHalfBank(gb, 0, 0);
|
GBMBCSwitchSramHalfBank(gb, 0, 0);
|
||||||
GBMBCSwitchSramHalfBank(gb, 0, 1);
|
GBMBCSwitchSramHalfBank(gb, 0, 1);
|
||||||
break;
|
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:
|
default:
|
||||||
memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState));
|
memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState));
|
||||||
}
|
}
|
||||||
|
|
||||||
GBMBCInit(gb);
|
|
||||||
gb->memory.sramBank = gb->memory.sram;
|
gb->memory.sramBank = gb->memory.sram;
|
||||||
|
|
||||||
if (!gb->memory.wram) {
|
if (!gb->memory.wram) {
|
||||||
|
@ -688,6 +691,10 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
||||||
STORE_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr);
|
STORE_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr);
|
||||||
STORE_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable);
|
STORE_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable);
|
||||||
break;
|
break;
|
||||||
|
case GB_MMM01:
|
||||||
|
state->memory.mmm01.locked = memory->mbcState.mmm01.locked;
|
||||||
|
state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -755,6 +762,15 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
||||||
LOAD_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr);
|
LOAD_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr);
|
||||||
LOAD_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable);
|
LOAD_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable);
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,6 +185,7 @@ void GBAReset(struct ARMCore* cpu) {
|
||||||
|
|
||||||
struct GBA* gba = (struct GBA*) cpu->master;
|
struct GBA* gba = (struct GBA*) cpu->master;
|
||||||
if (!gba->rr || (!gba->rr->isPlaying(gba->rr) && !gba->rr->isRecording(gba->rr))) {
|
if (!gba->rr || (!gba->rr->isPlaying(gba->rr) && !gba->rr->isRecording(gba->rr))) {
|
||||||
|
gba->memory.savedata.maskWriteback = false;
|
||||||
GBASavedataUnmask(&gba->memory.savedata);
|
GBASavedataUnmask(&gba->memory.savedata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "LogController.h"
|
#include "LogController.h"
|
||||||
|
|
||||||
#include <mgba-util/png-io.h>
|
#include <mgba-util/png-io.h>
|
||||||
|
#include <mgba-util/vfs.h>
|
||||||
#ifdef M_CORE_GBA
|
#ifdef M_CORE_GBA
|
||||||
#include <mgba/internal/gba/memory.h>
|
#include <mgba/internal/gba/memory.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -184,5 +185,6 @@ void MapView::exportMap() {
|
||||||
QImage map = m_rawMap.rgbSwapped();
|
QImage map = m_rawMap.rgbSwapped();
|
||||||
PNGWritePixelsA(png, map.width(), map.height(), map.bytesPerLine() / 4, static_cast<const void*>(map.constBits()));
|
PNGWritePixelsA(png, map.width(), map.height(), map.bytesPerLine() / 4, static_cast<const void*>(map.constBits()));
|
||||||
PNGWriteClose(png, info);
|
PNGWriteClose(png, info);
|
||||||
|
vf->close(vf);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <mgba/internal/gb/io.h>
|
#include <mgba/internal/gb/io.h>
|
||||||
#endif
|
#endif
|
||||||
#include <mgba-util/png-io.h>
|
#include <mgba-util/png-io.h>
|
||||||
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
|
@ -283,6 +284,7 @@ void ObjView::exportObj() {
|
||||||
PNGWritePixels8(png, m_objInfo.width * 8, m_objInfo.height * 8, m_objInfo.width * 8, static_cast<void*>(buffer));
|
PNGWritePixels8(png, m_objInfo.width * 8, m_objInfo.height * 8, m_objInfo.width * 8, static_cast<void*>(buffer));
|
||||||
PNGWriteClose(png, info);
|
PNGWriteClose(png, info);
|
||||||
delete[] buffer;
|
delete[] buffer;
|
||||||
|
vf->close(vf);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ TileView::TileView(std::shared_ptr<CoreController> controller, QWidget* parent)
|
||||||
m_ui.tile->setController(controller);
|
m_ui.tile->setController(controller);
|
||||||
|
|
||||||
connect(m_ui.tiles, &TilePainter::indexPressed, m_ui.tile, &AssetTile::selectIndex);
|
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()) {
|
switch (m_controller->platform()) {
|
||||||
#ifdef M_CORE_GBA
|
#ifdef M_CORE_GBA
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>498</width>
|
<width>501</width>
|
||||||
<height>335</height>
|
<height>335</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -14,49 +14,10 @@
|
||||||
<string>Tiles</string>
|
<string>Tiles</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<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">
|
<item row="2" column="0">
|
||||||
|
<widget class="QGBA::AssetTile" name="tile"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSpinBox" name="magnification">
|
<widget class="QSpinBox" name="magnification">
|
||||||
|
@ -105,7 +66,7 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>286</width>
|
<width>256</width>
|
||||||
<height>768</height>
|
<height>768</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -151,8 +112,36 @@
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="4" column="0">
|
||||||
<widget class="QGBA::AssetTile" name="tile"/>
|
<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>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -175,22 +164,6 @@
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections>
|
<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>
|
<connection>
|
||||||
<sender>magnification</sender>
|
<sender>magnification</sender>
|
||||||
<signal>valueChanged(int)</signal>
|
<signal>valueChanged(int)</signal>
|
||||||
|
@ -207,5 +180,21 @@
|
||||||
</hint>
|
</hint>
|
||||||
</hints>
|
</hints>
|
||||||
</connection>
|
</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>
|
||||||
</connections>
|
</connections>
|
||||||
</ui>
|
</ui>
|
||||||
|
|
|
@ -3,7 +3,7 @@ find_program(GXTEXCONV gxtexconv)
|
||||||
find_program(RAW2C raw2c)
|
find_program(RAW2C raw2c)
|
||||||
find_program(WIILOAD wiiload)
|
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)
|
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})
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
|
@ -253,7 +253,8 @@ int main(int argc, char* argv[]) {
|
||||||
memset(audioBuffer, 0, sizeof(audioBuffer));
|
memset(audioBuffer, 0, sizeof(audioBuffer));
|
||||||
#ifdef FIXED_ROM_BUFFER
|
#ifdef FIXED_ROM_BUFFER
|
||||||
romBufferSize = SIZE_CART0;
|
romBufferSize = SIZE_CART0;
|
||||||
romBuffer = anonymousMemoryMap(romBufferSize);
|
romBuffer = SYS_GetArena2Lo();
|
||||||
|
SYS_SetArena2Lo((void*)((intptr_t) romBuffer + SIZE_CART0));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
|
#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
|
||||||
|
@ -530,10 +531,6 @@ int main(int argc, char* argv[]) {
|
||||||
VIDEO_WaitVSync();
|
VIDEO_WaitVSync();
|
||||||
mGUIDeinit(&runner);
|
mGUIDeinit(&runner);
|
||||||
|
|
||||||
#ifdef FIXED_ROM_BUFFER
|
|
||||||
mappedMemoryFree(romBuffer, romBufferSize);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
free(fifo);
|
free(fifo);
|
||||||
free(texmem);
|
free(texmem);
|
||||||
free(rescaleTexmem);
|
free(rescaleTexmem);
|
||||||
|
|