bsnes-plus/supergameboy/interface/interface.cpp
2020-02-27 18:43:15 -05:00

429 lines
12 KiB
C++

SuperGameBoy supergameboy;
#include <nall/snes/sgb.hpp>
static void op_step_default(uint32_t pc) {
uint8_t op = supergameboy.read_gb(pc);
uint8_t op1 = supergameboy.read_gb(pc + 1);
uint8_t op2 = supergameboy.read_gb(pc + 2);
printf("%06x %s\n", pc, GBCPU::disassemble(pc, op, op1, op2)());
}
static void op_read_default(uint32_t addr, uint8_t data) {
printf("op_read %06x => %02x\n", addr, data);
}
static void op_write_default(uint32_t addr, uint8_t data) {
printf("op_write %06x <= %02x\n", addr, data);
}
const uint8_t SuperGameBoy::bootroms[2][256] =
{
/* SGB1 boot ROM */
{
0x31, 0xfe, 0xff, 0x3e, 0x30, 0xe0, 0x00, 0xaf, 0x21, 0xff, 0x9f, 0x32, 0xcb, 0x7c, 0x20, 0xfb,
0x21, 0x26, 0xff, 0x0e, 0x11, 0x3e, 0x80, 0x32, 0xe2, 0x0c, 0x3e, 0xf3, 0xe2, 0x32, 0x3e, 0x77,
0x77, 0x3e, 0xfc, 0xe0, 0x47, 0x21, 0x5f, 0xc0, 0x0e, 0x08, 0xaf, 0x32, 0x0d, 0x20, 0xfc, 0x11,
0x4f, 0x01, 0x3e, 0xfb, 0x0e, 0x06, 0xf5, 0x06, 0x00, 0x1a, 0x1b, 0x32, 0x80, 0x47, 0x0d, 0x20,
0xf8, 0x32, 0xf1, 0x32, 0x0e, 0x0e, 0xd6, 0x02, 0xfe, 0xef, 0x20, 0xea, 0x11, 0x04, 0x01, 0x21,
0x10, 0x80, 0x1a, 0xcd, 0xd3, 0x00, 0xcd, 0xd4, 0x00, 0x13, 0x7b, 0xfe, 0x34, 0x20, 0xf3, 0x11,
0xe6, 0x00, 0x06, 0x08, 0x1a, 0x13, 0x22, 0x23, 0x05, 0x20, 0xf9, 0x3e, 0x19, 0xea, 0x10, 0x99,
0x21, 0x2f, 0x99, 0x0e, 0x0c, 0x3d, 0x28, 0x08, 0x32, 0x0d, 0x20, 0xf9, 0x2e, 0x0f, 0x18, 0xf3,
0x3e, 0x91, 0xe0, 0x40, 0x21, 0x00, 0xc0, 0x0e, 0x00, 0x3e, 0x00, 0xe2, 0x3e, 0x30, 0xe2, 0x06,
0x10, 0x1e, 0x08, 0x2a, 0x57, 0xcb, 0x42, 0x3e, 0x10, 0x20, 0x02, 0x3e, 0x20, 0xe2, 0x3e, 0x30,
0xe2, 0xcb, 0x1a, 0x1d, 0x20, 0xef, 0x05, 0x20, 0xe8, 0x3e, 0x20, 0xe2, 0x3e, 0x30, 0xe2, 0xcd,
0xc2, 0x00, 0x7d, 0xfe, 0x60, 0x20, 0xd2, 0x0e, 0x13, 0x3e, 0xc1, 0xe2, 0x0c, 0x3e, 0x07, 0xe2,
0x18, 0x3a, 0x16, 0x04, 0xf0, 0x44, 0xfe, 0x90, 0x20, 0xfa, 0x1e, 0x00, 0x1d, 0x20, 0xfd, 0x15,
0x20, 0xf2, 0xc9, 0x4f, 0x06, 0x04, 0xc5, 0xcb, 0x11, 0x17, 0xc1, 0xcb, 0x11, 0x17, 0x05, 0x20,
0xf5, 0x22, 0x23, 0x22, 0x23, 0xc9, 0x3c, 0x42, 0xb9, 0xa5, 0xb9, 0xa5, 0x42, 0x3c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x01, 0xe0, 0x50
},
/* SGB2 boot ROM */
{
0x31, 0xfe, 0xff, 0x3e, 0x30, 0xe0, 0x00, 0xaf, 0x21, 0xff, 0x9f, 0x32, 0xcb, 0x7c, 0x20, 0xfb,
0x21, 0x26, 0xff, 0x0e, 0x11, 0x3e, 0x80, 0x32, 0xe2, 0x0c, 0x3e, 0xf3, 0xe2, 0x32, 0x3e, 0x77,
0x77, 0x3e, 0xfc, 0xe0, 0x47, 0x21, 0x5f, 0xc0, 0x0e, 0x08, 0xaf, 0x32, 0x0d, 0x20, 0xfc, 0x11,
0x4f, 0x01, 0x3e, 0xfb, 0x0e, 0x06, 0xf5, 0x06, 0x00, 0x1a, 0x1b, 0x32, 0x80, 0x47, 0x0d, 0x20,
0xf8, 0x32, 0xf1, 0x32, 0x0e, 0x0e, 0xd6, 0x02, 0xfe, 0xef, 0x20, 0xea, 0x11, 0x04, 0x01, 0x21,
0x10, 0x80, 0x1a, 0xcd, 0xd3, 0x00, 0xcd, 0xd4, 0x00, 0x13, 0x7b, 0xfe, 0x34, 0x20, 0xf3, 0x11,
0xe6, 0x00, 0x06, 0x08, 0x1a, 0x13, 0x22, 0x23, 0x05, 0x20, 0xf9, 0x3e, 0x19, 0xea, 0x10, 0x99,
0x21, 0x2f, 0x99, 0x0e, 0x0c, 0x3d, 0x28, 0x08, 0x32, 0x0d, 0x20, 0xf9, 0x2e, 0x0f, 0x18, 0xf3,
0x3e, 0x91, 0xe0, 0x40, 0x21, 0x00, 0xc0, 0x0e, 0x00, 0x3e, 0x00, 0xe2, 0x3e, 0x30, 0xe2, 0x06,
0x10, 0x1e, 0x08, 0x2a, 0x57, 0xcb, 0x42, 0x3e, 0x10, 0x20, 0x02, 0x3e, 0x20, 0xe2, 0x3e, 0x30,
0xe2, 0xcb, 0x1a, 0x1d, 0x20, 0xef, 0x05, 0x20, 0xe8, 0x3e, 0x20, 0xe2, 0x3e, 0x30, 0xe2, 0xcd,
0xc2, 0x00, 0x7d, 0xfe, 0x60, 0x20, 0xd2, 0x0e, 0x13, 0x3e, 0xc1, 0xe2, 0x0c, 0x3e, 0x07, 0xe2,
0x18, 0x3a, 0x16, 0x04, 0xf0, 0x44, 0xfe, 0x90, 0x20, 0xfa, 0x1e, 0x00, 0x1d, 0x20, 0xfd, 0x15,
0x20, 0xf2, 0xc9, 0x4f, 0x06, 0x04, 0xc5, 0xcb, 0x11, 0x17, 0xc1, 0xcb, 0x11, 0x17, 0x05, 0x20,
0xf5, 0x22, 0x23, 0x22, 0x23, 0xc9, 0x3c, 0x42, 0xb9, 0xa5, 0xb9, 0xa5, 0x42, 0x3c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xff, 0xe0, 0x50
}
};
//====================
//SuperGameBoy::Packet
//====================
const char SuperGameBoy::command_name[32][64] = {
"PAL01", "PAL23", "PAL03", "PAL12",
"ATTR_BLK", "ATTR_LIN", "ATTR_DIV", "ATTR_CHR",
"SOUND", "SOU_TRN", "PAL_SET", "PAL_TRN",
"ATRC_EN", "TEST_EN", "ICON_EN", "DATA_SND",
"DATA_TRN", "MLT_REQ", "JUMP", "CHR_TRN",
"PCT_TRN", "ATTR_TRN", "ATTR_SET", "MASK_EN",
"OBJ_TRN", "19_???", "1A_???", "1B_???",
"1C_???", "1D_???", "1E_ROM", "1F_???",
};
void SuperGameBoy::joyp_write(bool p15, bool p14) {
//===============
//joypad handling
//===============
if(p15 == 1 && p14 == 1) {
if(joyp14lock == 0) {
joyp14lock = 1;
joyp_id = (joyp_id + 1) & mmio.mlt_req;
}
}
if(p15 == 0) {
if(joyp15lock == 1) {
joyp14lock ^= 1;
}
}
joyp15lock = p15;
//===============
//packet handling
//===============
if(p15 == 0 && p14 == 0) {
//pulse
pulselock = false;
packetoffset = 0;
bitoffset = 0;
strobelock = true;
packetlock = false;
return;
}
if(pulselock) return;
if(p15 == 1 && p14 == 1) {
strobelock = false;
return;
}
if(strobelock) {
if(p15 == 1 || p14 == 1) {
//malformed packet
packetlock = false;
pulselock = true;
bitoffset = 0;
packetoffset = 0;
} else {
return;
}
}
//p15:1, p14:0 = 0
//p15:0, p14:1 = 1
bool bit = (p15 == 0);
strobelock = true;
if(packetlock) {
if(p15 == 1 && p14 == 0) {
if(packetsize < 64) packet[packetsize++] = joyp_packet;
packetlock = false;
pulselock = true;
}
return;
}
bitdata = (bit << 7) | (bitdata >> 1);
if(++bitoffset < 8) return;
bitoffset = 0;
joyp_packet[packetoffset] = bitdata;
if(++packetoffset < 16) return;
packetlock = true;
}
//==================
//SuperGameBoy::Core
//==================
static uint8_t null_rom[32768];
bool SuperGameBoy::init(bool version_) {
if(!romdata) { romdata = null_rom; romsize = 32768; }
version = version_;
gambatte_ = new gambatte::GB;
gambatte_->setInputGetter(this);
gambatte_->setDebugHandler(this);
gambatte_->setScanlineCallback(SuperGameBoy::scanline);
return true;
}
void SuperGameBoy::term() {
if(gambatte_) {
delete gambatte_;
gambatte_ = 0;
}
}
const uint8_t* SuperGameBoy::bootrom() {
return bootroms[!!version];
}
unsigned SuperGameBoy::run(uint32_t *samplebuffer, unsigned samples) {
if((mmio.r6003 & 0x80) == 0) {
//Gameboy is inactive
samplebuffer[0] = 0;
return 1;
}
size_t samples_ = samples;
gambatte_->runFor(buffer, 160, samplebuffer, samples_);
return samples_;
}
/* static */
void SuperGameBoy::scanline(unsigned line) {
if (line < 144 && line >> 3 != supergameboy.vram_row) {
supergameboy.render();
supergameboy.vram_row = line >> 3;
supergameboy.mmio.write_buf = (supergameboy.mmio.write_buf + 1) & 3;
}
}
void SuperGameBoy::save() {
gambatte_->saveSavedata();
}
void SuperGameBoy::serialize(nall::serializer &s) {
s.integer(vram_row);
s.array(vram);
s.integer(mmio.write_buf);
s.integer(mmio.read_buf);
s.integer(mmio.r6003);
s.integer(mmio.r6004);
s.integer(mmio.r6005);
s.integer(mmio.r6006);
s.integer(mmio.r6007);
s.array(mmio.r7000);
s.integer(mmio.read_pos);
s.integer(mmio.mlt_req);
for(unsigned i = 0; i < 64; i++) s.array(packet[i].data);
s.integer(packetsize);
s.integer(joyp_id);
s.integer(joyp15lock);
s.integer(joyp14lock);
s.integer(pulselock);
s.integer(strobelock);
s.integer(packetlock);
s.array(joyp_packet.data);
s.integer(packetoffset);
s.integer(bitdata);
s.integer(bitoffset);
uint8_t *savestate = new uint8_t[256 * 1024];
if(s.mode() == serializer::Load) {
s.array(savestate, 256 * 1024);
file fp;
if(fp.open("supergameboy-state.tmp", file::mode::write)) {
fp.write(savestate, 256 * 1024);
fp.close();
gambatte_->loadState("supergameboy-state.tmp");
unlink("supergameboy-state.tmp");
}
} else if(s.mode() == serializer::Save) {
gambatte_->saveState(0, 0, "supergameboy-state.tmp");
file fp;
if(fp.open("supergameboy-state.tmp", file::mode::read)) {
fp.read(savestate, fp.size() < 256 * 1024 ? fp.size() : 256 * 1024);
fp.close();
}
unlink("supergameboy-state.tmp");
s.array(savestate, 256 * 1024);
} else if(s.mode() == serializer::Size) {
s.array(savestate, 256 * 1024);
}
delete[] savestate;
}
void SuperGameBoy::power() {
gambatte_->load(gambatte::GB::FORCE_DMG);
mmio_reset();
}
void SuperGameBoy::reset() {
gambatte_->reset();
mmio_reset();
}
uint8_t SuperGameBoy::read(uint16_t addr) {
//LY counter
if(addr == 0x6000) {
return (vram_row << 3) | mmio.write_buf;
}
//command ready port
if(addr == 0x6002) {
bool data = packetsize > 0;
if(data) {
for(unsigned i = 0; i < 16; i++) mmio.r7000[i] = packet[0][i];
packetsize--;
for(unsigned i = 0; i < packetsize; i++) packet[i] = packet[i + 1];
}
return data;
}
//command port
if((addr & 0xfff0) == 0x7000) {
return mmio.r7000[addr & 15];
}
if(addr == 0x7800) {
uint8_t data = vram[mmio.read_buf*320 + mmio.read_pos];
mmio.read_pos = (mmio.read_pos + 1) % 320;
return data;
}
return 0x00;
}
uint8_t SuperGameBoy::read_gb(uint16_t addr) {
return gambatte_->debugRead(addr);
}
void SuperGameBoy::write(uint16_t addr, uint8_t data) {
if(addr == 0x6001) {
mmio.read_buf = data & 3;
mmio.read_pos = 0;
}
//control port
//d7 = /RESET line (0 = stop, 1 = run)
//d5..4 = multiplayer select
if(addr == 0x6003) {
if((mmio.r6003 & 0x80) == 0x00 && (data & 0x80) == 0x80) {
reset();
}
mmio.mlt_req = (data & 0x30) >> 4;
if(mmio.mlt_req == 2) mmio.mlt_req = 3;
joyp_id &= mmio.mlt_req;
mmio.r6003 = data;
return;
}
if(addr == 0x6004) { mmio.r6004 = data; return; } //joypad 1 state
if(addr == 0x6005) { mmio.r6005 = data; return; } //joypad 2 state
if(addr == 0x6006) { mmio.r6006 = data; return; } //joypad 3 state
if(addr == 0x6007) { mmio.r6007 = data; return; } //joypad 4 state
}
void SuperGameBoy::write_gb(uint16_t addr, uint8_t data) {
gambatte_->debugWrite(addr, data);
}
void SuperGameBoy::mmio_reset() {
mmio.write_buf = 0;
mmio.read_buf = 0;
mmio.r6003 = 0x00;
mmio.r6004 = 0xff;
mmio.r6005 = 0xff;
mmio.r6006 = 0xff;
mmio.r6007 = 0xff;
for(unsigned n = 0; n < 16; n++) mmio.r7000[n] = 0;
mmio.read_pos = 0;
mmio.mlt_req = 0;
packetsize = 0;
vram_row = 0;
memset(vram, 0, 320*4);
joyp_id = 0;
joyp15lock = 0;
joyp14lock = 0;
pulselock = true;
}
void SuperGameBoy::render() {
uint32_t *source = buffer + vram_row * 160 * 8;
uint8_t *dest = vram + (mmio.write_buf * 320);
memset(dest, 0x00, 320);
for(unsigned y = vram_row * 8; y < vram_row * 8 + 8; y++) {
for(unsigned x = 0; x < 160; x++) {
unsigned pixel = *source++ / 0x555555;
pixel ^= 3;
unsigned addr = (x / 8 * 16) + ((y & 7) * 2);
dest[addr + 0] |= ((pixel & 1) >> 0) << (7 - (x & 7));
dest[addr + 1] |= ((pixel & 2) >> 1) << (7 - (x & 7));
}
}
}
//==========================
//Gambatte::InputGetter
//==========================
unsigned SuperGameBoy::operator()() {
unsigned inputState = 0x00;
unsigned data = 0xFF;
switch(joyp_id) {
case 0: data = mmio.r6004; break;
case 1: data = mmio.r6005; break;
case 2: data = mmio.r6006; break;
case 3: data = mmio.r6007; break;
}
inputState |= (joyp_id << 8);
if (!(data & 0x80)) inputState |= gambatte::InputGetter::START;
if (!(data & 0x40)) inputState |= gambatte::InputGetter::SELECT;
if (!(data & 0x20)) inputState |= gambatte::InputGetter::B;
if (!(data & 0x10)) inputState |= gambatte::InputGetter::A;
if (!(data & 0x08)) inputState |= gambatte::InputGetter::DOWN;
if (!(data & 0x04)) inputState |= gambatte::InputGetter::UP;
if (!(data & 0x02)) inputState |= gambatte::InputGetter::LEFT;
if (!(data & 0x01)) inputState |= gambatte::InputGetter::RIGHT;
return inputState;
}
//==========================
//SuperGameBoy::Construction
//==========================
SuperGameBoy::SuperGameBoy() : gambatte_(0) {
romdata = ramdata = rtcdata = 0;
romsize = ramsize = rtcsize = 0;
buffer = new uint32_t[160 * 144];
op_step = op_step_default;
op_call = op_step_default;
op_ret = op_step_default;
op_irq = op_step_default;
op_read = op_read_default;
op_readpc = op_read_default;
op_write = op_write_default;
}
SuperGameBoy::~SuperGameBoy() {
delete[] buffer;
}