Update to release v000r07.

byuu says:

Changelog:
- fixed sprite Vflip check
- fixed up window rendering (well, mostly, works great in Megaman II but
  not so great in Makaitoushi SaGa)
- added MBC2, MBC5 (already had MBC0, MBC1)
- removed reset, hooked up power cycle and Vsync toggle
- some other stuff

Makaitoushi SaGa locks on the main menu after some graphical glitches on
the title screen, damn.
Shin Megami Tensei - Devichil Black Book locks up immediately, hitting
HALT opcodes all the time, damn again.

Megaman II should be fully playable now.
Contra 3 is really close, but goes crazy on the turtle boss fight.
This commit is contained in:
Tim Allen 2011-01-03 15:28:36 +11:00
parent ebd6a52811
commit afdb3c4d20
24 changed files with 239 additions and 142 deletions

View File

@ -5,7 +5,7 @@ ui := ui
# compiler
c := $(compiler) -std=gnu99
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
flags := -O3 -fomit-frame-pointer -I. -I$(gameboy)
flags := -O3 -fomit-frame-pointer -I.
link :=
objects :=

View File

@ -1,10 +1,12 @@
#include <gameboy.hpp>
#include <gameboy/gameboy.hpp>
#define CARTRIDGE_CPP
namespace GameBoy {
#include "mbc0/mbc0.cpp"
#include "mbc1/mbc1.cpp"
#include "mbc2/mbc2.cpp"
#include "mbc5/mbc5.cpp"
Cartridge cartridge;
void Cartridge::load(uint8_t *data, unsigned size) {
@ -25,11 +27,20 @@ void Cartridge::load(uint8_t *data, unsigned size) {
info.battery = false;
info.rtc = false;
switch(romdata[0x0147]) { default:
switch(romdata[0x0147]) {
case 0x00: info.mapper = Mapper::MBC0; break;
case 0x01: info.mapper = Mapper::MBC1; break;
case 0x02: info.mapper = Mapper::MBC1; info.ram = true; break;
case 0x03: info.mapper = Mapper::MBC1; info.ram = true; info.battery = true; break;
case 0x05: info.mapper = Mapper::MBC2; info.ram = true; break;
case 0x06: info.mapper = Mapper::MBC2; info.ram = true; info.battery = true; break;
case 0x19: info.mapper = Mapper::MBC5; break;
case 0x1a: info.mapper = Mapper::MBC5; info.ram = true; break;
case 0x1b: info.mapper = Mapper::MBC5; info.ram = true; info.battery = true; break;
case 0x1c: info.mapper = Mapper::MBC5; info.rumble = true; break;
case 0x1d: info.mapper = Mapper::MBC5; info.rumble = true; info.ram = true; break;
case 0x1e: info.mapper = Mapper::MBC5; info.rumble = true; info.ram = true; info.battery = true; break;
default: print("Unknown mapper: ", hex<2>(romdata[0x0147]), "\n"); break;
}
switch(romdata[0x0148]) { default:
@ -46,7 +57,6 @@ void Cartridge::load(uint8_t *data, unsigned size) {
case 0x54: info.romsize = 96 * 16 * 1024; break;
}
//TODO: MBC2 always stores 0x00 here; yet it has 512x4-bits RAM
switch(romdata[0x0149]) { default:
case 0x00: info.ramsize = 0 * 1024; break;
case 0x01: info.ramsize = 2 * 1024; break;
@ -54,6 +64,8 @@ void Cartridge::load(uint8_t *data, unsigned size) {
case 0x03: info.ramsize = 32 * 1024; break;
}
if(info.mapper == Mapper::MBC2) info.ramsize = 256; //512 x 4-bit
ramdata = new uint8_t[ramsize = info.ramsize]();
loaded = true;
@ -92,23 +104,21 @@ void Cartridge::ram_write(unsigned addr, uint8 data) {
void Cartridge::power() {
mbc0.power();
mbc1.power();
mbc2.power();
mbc5.power();
MMIO *mapper = 0;
switch(info.mapper) {
switch(info.mapper) { default:
case Mapper::MBC0: mapper = &mbc0; break;
case Mapper::MBC1: mapper = &mbc1; break;
case Mapper::MBC2: mapper = &mbc2; break;
case Mapper::MBC5: mapper = &mbc5; break;
}
if(mapper) {
for(unsigned n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = mapper;
for(unsigned n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = mapper;
}
reset();
}
void Cartridge::reset() {
mbc1.reset();
mbc1.reset();
}
Cartridge::Cartridge() {

View File

@ -1,22 +1,27 @@
struct Cartridge : property<Cartridge> {
#include "mbc0/mbc0.hpp"
#include "mbc1/mbc1.hpp"
#include "mbc2/mbc2.hpp"
#include "mbc5/mbc5.hpp"
enum Mapper : unsigned {
MBC0,
MBC1,
MBC2,
MBC5,
Unknown,
};
struct Information {
string name;
uint8 cgbflag;
uint8 sgbflag;
string name;
uint8 cgbflag;
uint8 sgbflag;
Mapper mapper;
bool ram;
bool battery;
bool rtc;
Mapper mapper;
bool ram;
bool battery;
bool rtc;
bool rumble;
unsigned romsize;
unsigned ramsize;
@ -39,7 +44,6 @@ struct Cartridge : property<Cartridge> {
void ram_write(unsigned addr, uint8 data);
void power();
void reset();
Cartridge();
~Cartridge();

View File

@ -11,7 +11,4 @@ void Cartridge::MBC0::mmio_write(uint16 addr, uint8 data) {
void Cartridge::MBC0::power() {
}
void Cartridge::MBC0::reset() {
}
#endif

View File

@ -2,5 +2,4 @@ struct MBC0 : MMIO {
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
void reset();
} mbc0;

View File

@ -47,9 +47,6 @@ void Cartridge::MBC1::mmio_write(uint16 addr, uint8 data) {
}
void Cartridge::MBC1::power() {
}
void Cartridge::MBC1::reset() {
ram_enable = false;
rom_select = 0x01;
ram_select = 0x00;

View File

@ -10,5 +10,4 @@ struct MBC1 : MMIO {
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
void reset();
} mbc1;

55
gameboy/cartridge/mbc2/mbc2.cpp Executable file
View File

@ -0,0 +1,55 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MBC2::mmio_read(uint16 addr) {
if(addr >= 0x0000 && addr <= 0x3fff) {
return cartridge.rom_read(addr);
}
if(addr >= 0x4000 && addr <= 0x7fff) {
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if(addr >= 0xa000 && addr <= 0xa1ff) {
if(ram_enable) {
uint8 data = cartridge.ram_read(addr & 0x1ff);
return (addr & 1) == 0 ? ((data >> 4) & 0x0f) : ((data >> 0) & 0x0f);
}
return 0x00;
}
return 0x00;
}
void Cartridge::MBC2::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0x0000 && addr <= 0x1fff) {
if((addr & 0x100) == 0) {
ram_enable = (data & 0x0f) == 0x0a;
}
}
if(addr >= 0x2000 && addr <= 0x3fff) {
if(addr & 0x100) {
rom_select = data & 0x0f;
if(rom_select == 0) rom_select = 1;
}
}
if(addr >= 0xa000 && addr <= 0xa1ff) {
if(ram_enable) {
addr &= 511;
if((addr & 1) == 0) {
cartridge.ram_write(addr, (cartridge.ram_read(addr) & 0x0f) | (data << 4));
} else {
cartridge.ram_write(addr, (cartridge.ram_read(addr) & 0xf0) | (data << 0));
}
return;
}
}
}
void Cartridge::MBC2::power() {
ram_enable = false;
rom_select = 0x01;
}
#endif

View File

@ -0,0 +1,8 @@
struct MBC2 : MMIO {
bool ram_enable; //0000-1fff
uint8 rom_select; //2000-3fff
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
} mbc2;

52
gameboy/cartridge/mbc5/mbc5.cpp Executable file
View File

@ -0,0 +1,52 @@
#ifdef CARTRIDGE_CPP
uint8 Cartridge::MBC5::mmio_read(uint16 addr) {
if(addr >= 0x0000 && addr <= 0x3fff) {
return cartridge.rom_read(addr);
}
if(addr >= 0x4000 && addr <= 0x7fff) {
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
}
if(addr >= 0xa000 && addr <= 0xbfff) {
if(ram_enable) {
return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
}
return 0x00;
}
return 0x00;
}
void Cartridge::MBC5::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0x0000 && addr <= 0x1fff) {
ram_enable = (data & 0x0f) == 0x0a;
}
if(addr >= 0x2000 && addr <= 0x2fff) {
rom_select = (rom_select & 0x0100) | data;
}
if(addr >= 0x3000 && addr <= 0x3fff) {
rom_select = ((data & 1) << 8) | (rom_select & 0x00ff);
}
if(addr >= 0x4000 && addr <= 0x5fff) {
ram_select = data & 0x0f;
}
if(addr >= 0xa000 && addr <= 0xbfff) {
if(ram_enable) {
cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
}
}
}
void Cartridge::MBC5::power() {
ram_enable = false;
rom_select = 0x001;
ram_select = 0x00;
}
#endif

View File

@ -0,0 +1,9 @@
struct MBC5 : MMIO {
bool ram_enable; //0000-1fff
uint16 rom_select; //2000-2fff + 3000-3fff
uint8 ram_select; //4000-5fff
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void power();
} mbc5;

View File

@ -1,4 +1,4 @@
#include <gameboy.hpp>
#include <gameboy/gameboy.hpp>
#define CPU_CPP
namespace GameBoy {
@ -67,9 +67,13 @@ void CPU::interrupt_exec(uint16 pc) {
op_write(--r[SP], r[PC] >> 0);
r[PC] = pc;
op_io();
op_io();
op_io();
}
void CPU::power() {
create(Main, 4 * 1024 * 1024);
for(unsigned n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM
for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror)
for(unsigned n = 0xff00; n <= 0xff0f; n++) bus.mmio[n] = this; //MMIO
@ -78,15 +82,9 @@ void CPU::power() {
for(unsigned n = 0; n < 8192; n++) wram[n] = 0x00;
for(unsigned n = 0; n < 128; n++) hram[n] = 0x00;
reset();
}
void CPU::reset() {
create(Main, 4 * 1024 * 1024);
r[PC] = 0x0100;
r[SP] = 0xfffe;
r[AF] = 0x0000;
r[AF] = 0x0100;
r[BC] = 0x0000;
r[DE] = 0x0000;
r[HL] = 0x0000;

View File

@ -63,7 +63,6 @@ struct CPU : Processor, MMIO {
void interrupt_test();
void interrupt_exec(uint16 pc);
void power();
void reset();
CPU();
};

View File

@ -5,7 +5,7 @@
namespace GameBoy {
namespace Info {
static const char Name[] = "bgameboy";
static const char Version[] = "000.06";
static const char Version[] = "000.07";
}
}
@ -43,10 +43,10 @@ namespace GameBoy {
inline Processor() : thread(0) {}
};
#include <system/system.hpp>
#include <scheduler/scheduler.hpp>
#include <memory/memory.hpp>
#include <cartridge/cartridge.hpp>
#include <cpu/cpu.hpp>
#include <lcd/lcd.hpp>
#include <gameboy/system/system.hpp>
#include <gameboy/scheduler/scheduler.hpp>
#include <gameboy/memory/memory.hpp>
#include <gameboy/cartridge/cartridge.hpp>
#include <gameboy/cpu/cpu.hpp>
#include <gameboy/lcd/lcd.hpp>
};

View File

@ -1,4 +1,4 @@
#include <gameboy.hpp>
#include <gameboy/gameboy.hpp>
#define LCD_CPP
namespace GameBoy {
@ -55,93 +55,66 @@ void LCD::frame() {
}
void LCD::render() {
for(unsigned n = 0; n < 160; n++) {
line[n].source = Line::Source::None;
line[n].output = 0;
}
for(unsigned n = 0; n < 160; n++) line[n] = 0x00;
if(status.display_enable == true) {
if(status.bg_enable == true) render_bg();
if(status.obj_enable == true) render_obj();
if(status.window_display_enable == true) render_window();
if(status.obj_enable == true) render_obj();
}
uint8_t *output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) {
output[n] = (3 - line[n].output) * 0x55;
for(unsigned n = 0; n < 160; n++) output[n] = (3 - line[n]) * 0x55;
}
uint16 LCD::read_tile(bool select, unsigned x, unsigned y) {
unsigned tmaddr = 0x1800 + (select << 10), tdaddr;
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
if(status.bg_tiledata_select == 0) {
tdaddr = 0x1000 + ((int8)vram[tmaddr] << 4);
} else {
tdaddr = 0x0000 + (vram[tmaddr] << 4);
}
tdaddr += (y & 7) << 1;
return (vram[tdaddr + 0] << 0) | (vram[tdaddr + 1] << 8);
}
void LCD::render_bg() {
unsigned iy = (status.ly + status.scy) & 255;
unsigned ix = status.scx;
unsigned ix = status.scx, tx = ix & 7;
uint8 mask = 0x80 >> tx;
unsigned data = read_tile(status.bg_tilemap_select, ix, iy), palette;
unsigned tmaddr = (status.bg_tilemap_select == 0 ? 0x1800 : 0x1c00);
unsigned tx = (ix - 7) & 7;
uint8 d0 = 0, d1 = 0;
for(signed ox = -7; ox < 160; ox++) {
if(tx == 0) {
unsigned tile = (((iy >> 3) * 32) + (ix >> 3)) & 0x3fff;
unsigned tdaddr;
if(status.bg_tiledata_select == 0) {
tdaddr = 0x1000 + (int8)vram[tmaddr + tile] * 16;
} else {
tdaddr = 0x0000 + vram[tmaddr + tile] * 16;
}
tdaddr += (iy & 7) * 2;
d0 = vram[tdaddr + 0];
d1 = vram[tdaddr + 1];
}
uint8 palette = ((d0 & 0x80) >> 7) + ((d1 & 0x80) >> 6);
d0 <<= 1, d1 <<= 1;
if(ox >= 0) {
line[ox].source = Line::Source::BG;
line[ox].output = status.bgp[palette];
}
for(unsigned ox = 0; ox < 160; ox++) {
palette = ((data & (mask << 0)) ? 1 : 0);
palette |= ((data & (mask << 8)) ? 2 : 0);
mask = (mask >> 1) | (mask << 7);
line[ox] = status.bgp[palette];
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) data = read_tile(status.bg_tilemap_select, ix, iy);
}
}
void LCD::render_window() {
if(status.wy > status.ly) return;
unsigned iy = (status.ly + status.wy) & 255;
unsigned ix = (status.wx - 7) & 255;
if(status.ly - status.wy >= 144U) return;
unsigned iy = status.ly - status.wy;
unsigned ix = (status.wx - 7) & 255, tx = ix & 7;
uint8 mask = 0x80 >> tx;
unsigned data = read_tile(status.window_tilemap_select, ix, iy), palette;
unsigned tmaddr = (status.window_tilemap_select == 0 ? 0x1800 : 0x1c00);
unsigned tx = (ix - 7) & 7;
uint8 d0 = 0, d1 = 0;
for(signed ox = -7; ox < 160; ox++) {
if(tx == 0) {
unsigned tile = (((iy >> 3) * 32) + (ix >> 3)) & 0x3fff;
unsigned tdaddr;
if(status.bg_tiledata_select == 0) {
tdaddr = 0x1000 + (int8)vram[tmaddr + tile] * 16;
} else {
tdaddr = 0x0000 + vram[tmaddr + tile] * 16;
}
tdaddr += (iy & 7) * 2;
d0 = vram[tdaddr + 0];
d1 = vram[tdaddr + 1];
}
uint8 palette = ((d0 & 0x80) >> 7) + ((d1 & 0x80) >> 6);
d0 <<= 1, d1 <<= 1;
if(ox >= 7) {
line[ox].source = Line::Source::Window;
line[ox].output = status.bgp[palette];
}
for(unsigned ox = 0; ox < 160; ox++) {
palette = ((data & (mask << 0)) ? 1 : 0);
palette |= ((data & (mask << 8)) ? 2 : 0);
mask = (mask >> 1) | (mask << 7);
if(ox - (status.wx - 7) < 160U) line[ox] = status.bgp[palette];
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) data = read_tile(status.window_tilemap_select, ix, iy);
}
}
@ -173,17 +146,18 @@ void LCD::render_obj() {
unsigned ox = sx + (tx ^ xflip);
if(ox <= 159) {
if((attribute & 0x80) == 1) {
if(line[ox].source == Line::Source::BG && line[ox].output > 0) continue;
if(attribute & 0x80) {
if(line[ox] > 0) continue;
}
line[ox].source = Line::Source::OBJ;
line[ox].output = palette;
line[ox] = palette;
}
}
}
}
void LCD::power() {
create(Main, 4 * 1024 * 1024);
for(unsigned n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
for(unsigned n = 0xff40; n <= 0xff4b; n++) bus.mmio[n] = this; //MMIO
for(unsigned n = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM
@ -191,11 +165,6 @@ void LCD::power() {
for(unsigned n = 0; n < 8192; n++) vram[n] = 0x00;
for(unsigned n = 0; n < 160; n++) oam [n] = 0x00;
reset();
}
void LCD::reset() {
create(Main, 4 * 1024 * 1024);
for(unsigned n = 0; n < 160 * 144; n++) screen[n] = 0x00;
status.lx = 0;

View File

@ -49,11 +49,7 @@ struct LCD : Processor, MMIO {
uint8 screen[160 * 144];
uint8 vram[8192];
uint8 oam[160];
struct Line {
enum class Source : unsigned { None, BG, OBJ, Window } source;
uint8 output;
} line[160];
uint8 line[160];
static void Main();
void main();
@ -61,12 +57,12 @@ struct LCD : Processor, MMIO {
void scanline();
void frame();
void render();
uint16 read_tile(bool select, unsigned x, unsigned y);
void render_bg();
void render_window();
void render_obj();
void power();
void reset();
};
extern LCD lcd;

View File

@ -1,4 +1,4 @@
#include <gameboy.hpp>
#include <gameboy/gameboy.hpp>
#define MEMORY_CPP
namespace GameBoy {

View File

@ -1,4 +1,4 @@
#include <gameboy.hpp>
#include <gameboy/gameboy.hpp>
#define SCHEDULER_CPP
namespace GameBoy {

View File

@ -1,4 +1,4 @@
#include <gameboy.hpp>
#include <gameboy/gameboy.hpp>
#define SYSTEM_CPP
namespace GameBoy {
@ -17,14 +17,6 @@ void System::power() {
scheduler.init();
}
void System::reset() {
bus.reset();
cartridge.power();
cpu.reset();
lcd.reset();
scheduler.init();
}
void System::run() {
scheduler.enter();
}

View File

@ -8,13 +8,12 @@ class System {
public:
void init(Interface*);
void power();
void reset();
void run();
//private:
Interface *interface;
};
#include <interface/interface.hpp>
#include <gameboy/interface/interface.hpp>
extern System system;

View File

@ -10,7 +10,7 @@ using namespace ruby;
#include <phoenix/phoenix.hpp>
using namespace phoenix;
#include <gameboy.hpp>
#include <gameboy/gameboy.hpp>
#include "interface.hpp"

View File

@ -9,12 +9,10 @@ void MainWindow::create() {
systemLoadCartridge.create(system, "Load Cartridge ...");
systemSeparator1.create(system);
systemPower.create(system, "Power Cycle");
systemPower.setEnabled(false);
systemReset.create(system, "Reset");
systemReset.setEnabled(false);
settings.create(*this, "Settings");
//settings.setEnabled(false);
settingsVideoSync.create(settings, "Synchronize Video");
settingsVideoSync.setChecked(true);
tools.create(*this, "Tools");
//tools.setEnabled(false);
@ -37,6 +35,14 @@ void MainWindow::create() {
if(filename != "") utility.loadCartridge(filename);
};
systemPower.onTick = []() {
if(GameBoy::cartridge.loaded()) GameBoy::system.power();
};
settingsVideoSync.onTick = []() {
video.set(Video::Synchronize, mainWindow.settingsVideoSync.checked());
};
helpAbout.onTick = []() {
MessageWindow::information(mainWindow, {
"bgameboy\n\n",

View File

@ -3,9 +3,9 @@ struct MainWindow : Window {
MenuItem systemLoadCartridge;
MenuSeparator systemSeparator1;
MenuItem systemPower;
MenuItem systemReset;
Menu settings;
MenuCheckItem settingsVideoSync;
Menu tools;

View File

@ -22,13 +22,21 @@ void Application::main(int argc, char **argv) {
mainWindow.setVisible();
OS::run();
#if defined(PHOENIX_WINDOWS)
video.driver("Direct3D");
#else
video.driver("OpenGL");
#endif
video.set(Video::Handle, (uintptr_t)mainWindow.viewport.handle());
video.set(Video::Synchronize, true);
video.set(Video::Filter, (unsigned)0);
video.init();
#if defined(PHOENIX_WINDOWS)
input.driver("RawInput");
#else
input.driver("SDL");
#endif
input.set(Input::Handle, (uintptr_t)mainWindow.viewport.handle());
input.init();