* improved Super Game Boy emulation and fixed SGB save states [LIJI32]
This commit is contained in:
byuu 2019-07-20 21:28:55 +09:00
parent f3022fd907
commit 78a6a2e7d7
7 changed files with 105 additions and 126 deletions

View File

@ -32,7 +32,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "bsnes";
static const string Version = "107.9";
static const string Version = "107.10";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org/";

View File

@ -127,13 +127,13 @@ static void display_vblank(GB_gameboy_t *gb)
if (GB_is_hle_sgb(gb)) {
GB_sgb_render(gb);
}
if (gb->turbo) {
if (GB_timing_sync_turbo(gb)) {
return;
}
}
if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || gb->stopped) || gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON)) {
/* LCD is off, set screen to white or black (if LCD is on in stop mode) */
if (gb->sgb) {
@ -423,7 +423,7 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->sprite_palettes_rgb[oam_fifo_item->palette * 4 + pixel];
}
}
if (gb->model & GB_MODEL_NO_SFC_BIT) {
if (gb->icd_pixel_callback) {
gb->icd_pixel_callback(gb, icd_pixel);
@ -769,10 +769,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
fifo_push_bg_row(&gb->bg_fifo, 0, 0, 0, false, false);
/* Todo: find out actual access time of SCX */
gb->position_in_line = - (gb->io_registers[GB_IO_SCX] & 7) - 8;
// Todo: unverified timing
gb->current_lcd_line++;
gb->current_lcd_line++; // Todo: unverified timing
if (gb->current_lcd_line == LINES && GB_is_sgb(gb)) {
display_vblank(gb);
}
@ -912,7 +909,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
}
GB_SLEEP(gb, display, 11, LINE_LENGTH - gb->cycles_for_line);
gb->mode_for_interrupt = 2;
if (gb->icd_hreset_callback) {
gb->icd_hreset_callback(gb);
}

View File

@ -10,16 +10,15 @@ ICD icd;
namespace SameBoy {
static auto hreset(GB_gameboy_t*) -> void {
icd.ly++;
icd.ppuScanline();
icd.ppuHreset();
}
static auto vreset(GB_gameboy_t*) -> void {
icd.ly = 0;
icd.ppuVreset();
}
static auto icd_pixel(GB_gameboy_t*, uint8_t pixel) -> void {
icd.ppuOutput(pixel);
icd.ppuWrite(pixel);
}
static auto joyp_write(GB_gameboy_t*, uint8_t value) -> void {
@ -35,7 +34,7 @@ namespace SameBoy {
static auto sample(GB_gameboy_t*, GB_sample_t* sample) -> void {
float left = sample->left / 32768.0f;
float right = sample->right / 32768.0f;
icd.apuOutput(left, right);
icd.apuWrite(left, right);
}
static auto vblank(GB_gameboy_t*) -> void {
@ -63,7 +62,8 @@ auto ICD::main() -> void {
auto ICD::load() -> bool {
information = {};
GB_random_set_enabled(false);
//todo: connect to SFC random enable setting
//GB_random_set_enabled(false);
if(Frequency == 0) {
GB_init(&sameboy, GB_MODEL_SGB_NO_SFC);
GB_load_boot_rom_from_buffer(&sameboy, (const unsigned char*)&SGB1BootROM[0], 256);
@ -96,7 +96,6 @@ auto ICD::load() -> bool {
fp->read(data, size);
GB_load_rom_from_buffer(&sameboy, data, size);
} else return unload(), false;
ly = 0;
return true;
}
@ -104,10 +103,31 @@ auto ICD::unload() -> void {
GB_free(&sameboy);
}
auto ICD::power() -> void {
auto ICD::power(bool reset) -> void {
//SGB1 uses CPU oscillator; SGB2 uses dedicated oscillator
create(ICD::Enter, (Frequency ? Frequency : system.cpuFrequency()) / 5.0);
stream = Emulator::audio.createStream(2, uint((Frequency ? Frequency : system.cpuFrequency()) / 5.0 / 128.0));
if(!reset) {
stream = Emulator::audio.createStream(2, uint((Frequency ? Frequency : system.cpuFrequency()) / 5.0 / 128.0));
}
for(auto& packet : this->packet) packet = {};
packetSize = 0;
joypID = 3;
joyp14Lock = 0;
joyp15Lock = 0;
pulseLock = 1;
strobeLock = 0;
packetLock = 0;
joypPacket = {};
packetOffset = 0;
bitData = 0;
bitOffset = 0;
for(auto& n : output) n = 0xff;
readBank = 0;
readAddress = 0;
writeBank = 0;
r6003 = 0x00;
r6004 = 0xff;
@ -117,45 +137,8 @@ auto ICD::power() -> void {
for(auto& r : r7000) r = 0x00;
mltReq = 0;
for(auto& n : output) n = 0xff;
readBank = 0;
readAddress = 0;
writeBank = 0;
writeX = 0;
writeY = 0;
packetSize = 0;
joypID = 3;
joyp15Lock = 0;
joyp14Lock = 0;
pulseLock = true;
GB_reset(&sameboy);
}
auto ICD::reset() -> void {
create(ICD::Enter, (Frequency ? Frequency : system.cpuFrequency()) / 5.0);
r6003 = 0x00;
r6004 = 0xff;
r6005 = 0xff;
r6006 = 0xff;
r6007 = 0xff;
for(auto& r : r7000) r = 0x00;
mltReq = 0;
for(auto& n : output) n = 0xff;
readBank = 0;
readAddress = 0;
writeBank = 0;
writeX = 0;
writeY = 0;
packetSize = 0;
joypID = 3;
joyp15Lock = 0;
joyp14Lock = 0;
pulseLock = true;
hcounter = 0;
vcounter = 0;
GB_reset(&sameboy);
}

View File

@ -8,13 +8,13 @@ struct ICD : Emulator::Platform, Thread {
auto load() -> bool;
auto unload() -> void;
auto power() -> void;
auto reset() -> void; //software reset
auto power(bool reset = false) -> void;
//interface.cpp
auto ppuScanline() -> void;
auto ppuOutput(uint2 color) -> void;
auto apuOutput(float left, float right) -> void;
auto ppuHreset() -> void;
auto ppuVreset() -> void;
auto ppuWrite(uint2 color) -> void;
auto apuWrite(float left, float right) -> void;
auto joypWrite(bool p14, bool p15) -> void;
//io.cpp
@ -33,21 +33,27 @@ struct ICD : Emulator::Platform, Thread {
private:
struct Packet {
auto operator[](uint addr) -> uint8& { return data[addr & 15]; }
auto operator[](uint4 address) -> uint8& { return data[address]; }
uint8 data[16];
};
Packet packet[64];
uint packetSize;
uint7 packetSize;
uint joypID;
bool joyp15Lock;
bool joyp14Lock;
bool pulseLock;
bool strobeLock;
bool packetLock;
uint2 joypID;
uint1 joyp14Lock;
uint1 joyp15Lock;
uint1 pulseLock;
uint1 strobeLock;
uint1 packetLock;
Packet joypPacket;
uint8 packetOffset;
uint8 bitData, bitOffset;
uint4 packetOffset;
uint8 bitData;
uint3 bitOffset;
uint8 output[4 * 512];
uint2 readBank;
uint9 readAddress;
uint2 writeBank;
uint8 r6003; //control port
uint8 r6004; //joypad 1
@ -57,12 +63,8 @@ private:
uint8 r7000[16]; //JOYP packet data
uint8 mltReq; //number of active joypads
uint8 output[4 * 512];
uint readBank;
uint readAddress;
uint writeBank;
uint writeX;
uint writeY;
uint8 hcounter;
uint8 vcounter;
struct Information {
uint pathID = 0;
@ -74,7 +76,6 @@ public:
//as the offsets of all member variables will be wrong compared to what the C SameBoy code expects.
GB_gameboy_t sameboy;
uint32_t bitmap[160 * 144];
uint8_t ly = 0;
};
extern ICD icd;

View File

@ -1,33 +1,35 @@
auto ICD::ppuScanline() -> void {
if(++writeY == 8) {
writeBank = (writeBank + 1) & 3;
writeY = 0;
}
writeX = 0;
auto ICD::ppuHreset() -> void {
hcounter = 0;
vcounter++;
if((uint3)vcounter == 0) writeBank++;
}
auto ICD::ppuOutput(uint2 color) -> void {
if(writeX >= 160) return; // Unverified behavior
if(writeY >= 8) return; // Should never happen
uint addr = (writeBank & 3) * 512 + writeY * 2 + writeX / 8 * 16;
output[addr + 0] = (output[addr + 0] << 1) | !!(color & 1);
output[addr + 1] = (output[addr + 1] << 1) | !!(color & 2);
writeX++;
auto ICD::ppuVreset() -> void {
hcounter = 0;
vcounter = 0;
}
auto ICD::apuOutput(float left, float right) -> void {
auto ICD::ppuWrite(uint2 color) -> void {
auto x = (uint8)hcounter++;
auto y = (uint3)vcounter;
if(x >= 160) return; //unverified behavior
uint11 address = writeBank * 512 + y * 2 + x / 8 * 16;
output[address + 0] = (output[address + 0] << 1) | !!(color & 1);
output[address + 1] = (output[address + 1] << 1) | !!(color & 2);
}
auto ICD::apuWrite(float left, float right) -> void {
float samples[] = {left, right};
stream->write(samples);
}
auto ICD::joypWrite(bool p14, bool p15) -> void {
//joypad handling
if(p15 == 1 && p14 == 1) {
if(joyp15Lock == 0 && joyp14Lock == 0) {
joyp15Lock = 1;
if(p14 == 1 && p15 == 1) {
if(joyp14Lock == 0 && joyp15Lock == 0) {
joyp14Lock = 1;
joyp15Lock = 1;
joypID++;
if(mltReq == 0) joypID &= 0; //1-player mode
if(mltReq == 1) joypID &= 1; //2-player mode
@ -43,17 +45,17 @@ auto ICD::joypWrite(bool p14, bool p15) -> void {
if(joypID == 3) joypad = r6007;
uint4 input = 0xf;
if(p15 == 1 && p14 == 1) input = 0xf - joypID;
if(p14 == 1 && p15 == 1) input = 0xf - joypID;
if(p14 == 0) input &= bits(joypad,0-3); //d-pad
if(p15 == 0) input &= bits(joypad,4-7); //buttons
GB_icd_set_joyp(&sameboy, input);
if(p15 == 0 && p14 == 1) joyp15Lock = 0;
if(p15 == 1 && p14 == 0) joyp14Lock = 0;
if(p14 == 0 && p15 == 1) joyp14Lock = 0;
if(p14 == 1 && p15 == 0) joyp15Lock = 0;
//packet handling
if(p15 == 0 && p14 == 0) { //pulse
if(p14 == 0 && p15 == 0) { //pulse
pulseLock = false;
packetOffset = 0;
bitOffset = 0;
@ -64,13 +66,13 @@ auto ICD::joypWrite(bool p14, bool p15) -> void {
if(pulseLock) return;
if(p15 == 1 && p14 == 1) {
if(p14 == 1 && p15 == 1) {
strobeLock = false;
return;
}
if(strobeLock) {
if(p15 == 1 || p14 == 1) { //malformed packet
if(p14 == 1 || p15 == 1) { //malformed packet
packetLock = false;
pulseLock = true;
bitOffset = 0;
@ -80,13 +82,13 @@ auto ICD::joypWrite(bool p14, bool p15) -> void {
}
}
//p15:1, p14:0 = 0
//p15:0, p14:1 = 1
bool bit = (p15 == 0);
//p14:0, p15:1 = 0
//p14:1, p15:0 = 1
bool bit = p15 == 0;
strobeLock = true;
if(packetLock) {
if(p15 == 1 && p14 == 0) {
if(p14 == 0 && p15 == 1) {
if((joypPacket[0] >> 3) == 0x11) {
mltReq = joypPacket[1] & 3;
joypID = 3; //required: the next time P14==1 && P15==1; increment and start from ID=0 (Joypad 1)
@ -99,11 +101,11 @@ auto ICD::joypWrite(bool p14, bool p15) -> void {
return;
}
bitData = (bit << 7) | (bitData >> 1);
if(++bitOffset < 8) return;
bitData = bit << 7 | bitData >> 1;
if(++bitOffset) return;
bitOffset = 0;
joypPacket[packetOffset] = bitData;
if(++packetOffset < 16) return;
if(++packetOffset) return;
packetLock = true;
}

View File

@ -3,8 +3,7 @@ auto ICD::readIO(uint addr, uint8 data) -> uint8 {
//LY counter
if(addr == 0x6000) {
uint y = ly;
return (y & ~7) | writeBank;
return vcounter & ~7 | writeBank;
}
//command ready port
@ -54,7 +53,7 @@ auto ICD::writeIO(uint addr, uint8 data) -> void {
//d1,d0: 0 = frequency divider (clock rate adjust)
if(addr == 0x6003) {
if((r6003 & 0x80) == 0x00 && (data & 0x80) == 0x80) {
reset();
power(true); //soft reset
}
auto frequency = system.cpuFrequency();
switch(data & 3) {

View File

@ -3,25 +3,21 @@ auto ICD::serialize(serializer& s) -> void {
auto size = GB_get_save_state_size(&sameboy);
auto data = new uint8_t[size];
if(s.mode() == serializer::Save) {
GB_save_state_to_buffer(&sameboy, data);
}
s.array(data, size);
if(s.mode() == serializer::Load) {
GB_load_state_from_buffer(&sameboy, data, size);
}
delete[] data;
for(auto n : range(64)) s.array(packet[n].data);
s.integer(packetSize);
s.integer(joypID);
s.integer(joyp15Lock);
s.integer(joyp14Lock);
s.integer(joyp15Lock);
s.integer(pulseLock);
s.integer(strobeLock);
s.integer(packetLock);
@ -30,6 +26,11 @@ auto ICD::serialize(serializer& s) -> void {
s.integer(bitData);
s.integer(bitOffset);
s.array(output);
s.integer(readBank);
s.integer(readAddress);
s.integer(writeBank);
s.integer(r6003);
s.integer(r6004);
s.integer(r6005);
@ -38,10 +39,6 @@ auto ICD::serialize(serializer& s) -> void {
s.array(r7000);
s.integer(mltReq);
s.array(output);
s.integer(readBank);
s.integer(readAddress);
s.integer(writeBank);
s.integer(writeX);
s.integer(writeY);
s.integer(hcounter);
s.integer(vcounter);
}