mirror of https://github.com/bsnes-emu/bsnes.git
374 lines
8.4 KiB
C++
Executable File
374 lines
8.4 KiB
C++
Executable File
SuperGameBoy supergameboy;
|
|
|
|
//====================
|
|
//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(joyp15lock == 0 && joyp14lock == 0) {
|
|
joyp15lock = 1;
|
|
joyp14lock = 1;
|
|
joyp_id = (joyp_id + 1) & 3;
|
|
}
|
|
}
|
|
|
|
if(p15 == 0 && p14 == 1) joyp15lock = 0;
|
|
if(p15 == 1 && p14 == 0) joyp14lock = 0;
|
|
|
|
//===============
|
|
//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((joyp_packet[0] >> 3) == 0x11) {
|
|
mmio.mlt_req = joyp_packet[1] & 3;
|
|
if(mmio.mlt_req == 2) mmio.mlt_req = 3;
|
|
joyp_id = 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->setVideoBlitter(this);
|
|
gambatte->setInputStateGetter(this);
|
|
|
|
return true;
|
|
}
|
|
|
|
void SuperGameBoy::term() {
|
|
if(gambatte) {
|
|
delete gambatte;
|
|
gambatte = 0;
|
|
}
|
|
}
|
|
|
|
unsigned SuperGameBoy::run(uint32_t *samplebuffer, unsigned samples) {
|
|
if((mmio.r6003 & 0x80) == 0) {
|
|
//Gameboy is inactive
|
|
samplebuffer[0] = 0;
|
|
return 1;
|
|
}
|
|
|
|
return gambatte->runFor(samplebuffer, samples);
|
|
}
|
|
|
|
void SuperGameBoy::save() {
|
|
gambatte->saveSavedata();
|
|
}
|
|
|
|
void SuperGameBoy::serialize(nall::serializer &s) {
|
|
s.integer(vram_row);
|
|
s.array(vram);
|
|
|
|
s.integer(mmio.r6000);
|
|
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.r7800);
|
|
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("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(true);
|
|
mmio_reset();
|
|
}
|
|
|
|
void SuperGameBoy::reset() {
|
|
gambatte->reset();
|
|
mmio_reset();
|
|
}
|
|
|
|
void SuperGameBoy::row(unsigned row) {
|
|
mmio.r7800 = 0;
|
|
vram_row = row;
|
|
render(vram_row);
|
|
}
|
|
|
|
uint8_t SuperGameBoy::read(uint16_t addr) {
|
|
//LY counter
|
|
if(addr == 0x6000) {
|
|
return gambatte->lyCounter();
|
|
}
|
|
|
|
//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.r7800];
|
|
mmio.r7800 = (mmio.r7800 + 1) % 320;
|
|
return data;
|
|
}
|
|
|
|
return 0x00;
|
|
}
|
|
|
|
void SuperGameBoy::write(uint16_t addr, uint8_t data) {
|
|
//control port
|
|
//d7 = /RESET line (0 = stop, 1 = run)
|
|
if(addr == 0x6003) {
|
|
if((mmio.r6003 & 0x80) == 0x00 && (data & 0x80) == 0x80) {
|
|
reset();
|
|
command_1e();
|
|
}
|
|
|
|
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::mmio_reset() {
|
|
mmio.r6000 = 0x00;
|
|
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.r7800 = 0;
|
|
mmio.mlt_req = 0;
|
|
|
|
packetsize = 0;
|
|
|
|
vram_row = 0;
|
|
memset(vram, 0, 320);
|
|
|
|
joyp_id = 3;
|
|
joyp15lock = 0;
|
|
joyp14lock = 0;
|
|
pulselock = true;
|
|
}
|
|
|
|
//simulate 256-byte internal SGB BIOS on /RESET
|
|
void SuperGameBoy::command_1e() {
|
|
for(unsigned i = 0; i < 6; i++) {
|
|
Packet p;
|
|
p[0] = 0xf1 + (i << 1);
|
|
p[1] = 0;
|
|
for(unsigned n = 2; n < 16; n++) {
|
|
uint8_t data = romdata[0x0104 + (i * 14) + (n - 2)];
|
|
p[1] += data;
|
|
p[n] = data;
|
|
}
|
|
if(packetsize < 64) packet[packetsize++] = p;
|
|
}
|
|
}
|
|
|
|
void SuperGameBoy::render(unsigned row) {
|
|
gambatte->updateVideo();
|
|
|
|
uint32_t *source = buffer + row * 160 * 8;
|
|
memset(vram, 0x00, 320);
|
|
|
|
for(unsigned y = row * 8; y < 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);
|
|
vram[addr + 0] |= ((pixel & 1) >> 0) << (7 - (x & 7));
|
|
vram[addr + 1] |= ((pixel & 2) >> 1) << (7 - (x & 7));
|
|
}
|
|
}
|
|
}
|
|
|
|
//======================
|
|
//Gambatte::VideoBlitter
|
|
//======================
|
|
|
|
//should always be 160x144, as no filters are used
|
|
void SuperGameBoy::setBufferDimensions(unsigned width, unsigned height) {
|
|
if(buffer) delete[] buffer;
|
|
buffer = new uint32_t[width * height];
|
|
bufferWidth = width;
|
|
bufferHeight = height;
|
|
}
|
|
|
|
const Gambatte::PixelBuffer SuperGameBoy::inBuffer() {
|
|
Gambatte::PixelBuffer pixelBuffer;
|
|
pixelBuffer.pixels = (void*)buffer;
|
|
pixelBuffer.format = Gambatte::PixelBuffer::RGB32;
|
|
pixelBuffer.pitch = bufferWidth;
|
|
return pixelBuffer;
|
|
}
|
|
|
|
void SuperGameBoy::blit() {
|
|
}
|
|
|
|
//==========================
|
|
//Gambatte::InputStateGetter
|
|
//==========================
|
|
|
|
const Gambatte::InputState& SuperGameBoy::operator()() {
|
|
inputState.joypadId = 0x0f - (joyp_id & mmio.mlt_req);
|
|
|
|
unsigned data = 0x00;
|
|
switch(joyp_id & mmio.mlt_req) {
|
|
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.startButton = !(data & 0x80);
|
|
inputState.selectButton = !(data & 0x40);
|
|
inputState.bButton = !(data & 0x20);
|
|
inputState.aButton = !(data & 0x10);
|
|
inputState.dpadDown = !(data & 0x08);
|
|
inputState.dpadUp = !(data & 0x04);
|
|
inputState.dpadLeft = !(data & 0x02);
|
|
inputState.dpadRight = !(data & 0x01);
|
|
|
|
return inputState;
|
|
}
|
|
|
|
//==========================
|
|
//SuperGameBoy::Construction
|
|
//==========================
|
|
|
|
SuperGameBoy::SuperGameBoy() : gambatte(0), buffer(0) {
|
|
romdata = ramdata = rtcdata = 0;
|
|
romsize = ramsize = rtcsize = 0;
|
|
}
|
|
|
|
SuperGameBoy::~SuperGameBoy() {
|
|
if(buffer) delete[] buffer;
|
|
}
|