mirror of https://github.com/bsnes-emu/bsnes.git
Update to release v000r04.
byuu says: [screenshot of the Tetris title screen] Less than fourty-eight hours after starting on this. I guess you weren't kidding, Exophase.
This commit is contained in:
parent
71780949b0
commit
2330ed6e8c
|
@ -324,7 +324,7 @@ void CPU::op_rlca() {
|
|||
|
||||
void CPU::op_rla() {
|
||||
bool c = r[A] & 0x80;
|
||||
r[A] = (r[A] << 1) | (c << 0);
|
||||
r[A] = (r[A] << 1) | (r.f.c << 0);
|
||||
r.f.z = 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
|
@ -341,7 +341,7 @@ void CPU::op_rrca() {
|
|||
|
||||
void CPU::op_rra() {
|
||||
bool c = r[A] & 0x01;
|
||||
r[A] = (r[A] >> 1) | (c << 7);
|
||||
r[A] = (r[A] >> 1) | (r.f.c << 7);
|
||||
r.f.z = 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
|
|
|
@ -15,15 +15,57 @@ void CPU::Main() {
|
|||
void CPU::main() {
|
||||
while(true) {
|
||||
//print(disassemble(r[PC]), "\n");
|
||||
interrupt_test();
|
||||
uint8 opcode = op_read(r[PC]++);
|
||||
(this->*opcode_table[opcode])();
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::interrupt_test() {
|
||||
if(status.ime) {
|
||||
if(status.interrupt_request_vblank && status.interrupt_enable_vblank) {
|
||||
status.interrupt_request_vblank = 0;
|
||||
return interrupt_exec(0x0040);
|
||||
}
|
||||
|
||||
if(status.interrupt_request_stat && status.interrupt_enable_stat) {
|
||||
status.interrupt_request_stat = 0;
|
||||
return interrupt_exec(0x0048);
|
||||
}
|
||||
|
||||
if(status.interrupt_request_timer && status.interrupt_enable_timer) {
|
||||
status.interrupt_request_timer = 0;
|
||||
return interrupt_exec(0x0050);
|
||||
}
|
||||
|
||||
if(status.interrupt_request_serial && status.interrupt_enable_serial) {
|
||||
status.interrupt_request_serial = 0;
|
||||
return interrupt_exec(0x0058);
|
||||
}
|
||||
|
||||
if(status.interrupt_request_joypad && status.interrupt_enable_joypad) {
|
||||
status.interrupt_request_joypad = 0;
|
||||
return interrupt_exec(0x0060);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::interrupt_exec(uint16 pc) {
|
||||
status.ime = 0;
|
||||
op_write(--r[SP], r[PC] >> 8);
|
||||
op_write(--r[SP], r[PC] >> 0);
|
||||
r[PC] = pc;
|
||||
op_io();
|
||||
}
|
||||
|
||||
void CPU::power() {
|
||||
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 = 0xff80; n <= 0xfffe; n++) bus.mmio[n] = this; //HRAM
|
||||
for(unsigned n = 0xff00; n <= 0xff0f; n++) bus.mmio[n] = this; //MMIO
|
||||
for(unsigned n = 0xff80; n <= 0xffff; n++) bus.mmio[n] = this; //HRAM+IE
|
||||
|
||||
for(unsigned n = 0; n < 8192; n++) wram[n] = 0x00;
|
||||
for(unsigned n = 0; n < 128; n++) hram[n] = 0x00;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
@ -39,6 +81,34 @@ void CPU::reset() {
|
|||
r[HL] = 0x0000;
|
||||
|
||||
status.ime = 0;
|
||||
status.timer0 = 0;
|
||||
status.timer1 = 0;
|
||||
status.timer2 = 0;
|
||||
status.timer3 = 0;
|
||||
|
||||
status.p15 = 0;
|
||||
status.p14 = 0;
|
||||
|
||||
status.div = 0;
|
||||
|
||||
status.tima = 0;
|
||||
|
||||
status.tma = 0;
|
||||
|
||||
status.timer_enable = 0;
|
||||
status.timer_clock = 0;
|
||||
|
||||
status.interrupt_request_joypad = 0;
|
||||
status.interrupt_request_serial = 0;
|
||||
status.interrupt_request_timer = 0;
|
||||
status.interrupt_request_stat = 0;
|
||||
status.interrupt_request_vblank = 0;
|
||||
|
||||
status.interrupt_enable_joypad = 0;
|
||||
status.interrupt_enable_serial = 0;
|
||||
status.interrupt_enable_timer = 0;
|
||||
status.interrupt_enable_stat = 0;
|
||||
status.interrupt_enable_vblank = 0;
|
||||
}
|
||||
|
||||
CPU::CPU() {
|
||||
|
|
|
@ -5,6 +5,41 @@ struct CPU : Processor, MMIO {
|
|||
|
||||
struct Status {
|
||||
bool ime;
|
||||
unsigned timer0;
|
||||
unsigned timer1;
|
||||
unsigned timer2;
|
||||
unsigned timer3;
|
||||
|
||||
//$ff00 JOYP
|
||||
bool p15;
|
||||
bool p14;
|
||||
|
||||
//$ff04 DIV
|
||||
uint8 div;
|
||||
|
||||
//$ff05 TIMA
|
||||
uint8 tima;
|
||||
|
||||
//$ff06 TMA
|
||||
uint8 tma;
|
||||
|
||||
//$ff07 TAC
|
||||
bool timer_enable;
|
||||
unsigned timer_clock;
|
||||
|
||||
//$ff0f IF
|
||||
bool interrupt_request_joypad;
|
||||
bool interrupt_request_serial;
|
||||
bool interrupt_request_timer;
|
||||
bool interrupt_request_stat;
|
||||
bool interrupt_request_vblank;
|
||||
|
||||
//$ffff IE
|
||||
bool interrupt_enable_joypad;
|
||||
bool interrupt_enable_serial;
|
||||
bool interrupt_enable_timer;
|
||||
bool interrupt_enable_stat;
|
||||
bool interrupt_enable_vblank;
|
||||
} status;
|
||||
|
||||
uint8 wram[8192];
|
||||
|
@ -12,6 +47,8 @@ struct CPU : Processor, MMIO {
|
|||
|
||||
static void Main();
|
||||
void main();
|
||||
void interrupt_test();
|
||||
void interrupt_exec(uint16 pc);
|
||||
void power();
|
||||
void reset();
|
||||
CPU();
|
||||
|
|
|
@ -4,12 +4,122 @@ uint8 CPU::mmio_read(uint16 addr) {
|
|||
if(addr >= 0xc000 && addr <= 0xdfff) return wram[addr & 0x1fff];
|
||||
if(addr >= 0xe000 && addr <= 0xfdff) return wram[addr & 0x1fff];
|
||||
if(addr >= 0xff80 && addr <= 0xfffe) return hram[addr & 0x7f];
|
||||
|
||||
if(addr == 0xff00) { //JOYP
|
||||
unsigned keys = 0x0f;
|
||||
|
||||
if(status.p15 == 0 && status.p14 == 1) {
|
||||
keys = !system.interface->input_poll((unsigned)Input::Down) << 3;
|
||||
keys |= !system.interface->input_poll((unsigned)Input::Up) << 2;
|
||||
keys |= !system.interface->input_poll((unsigned)Input::Left) << 1;
|
||||
keys |= !system.interface->input_poll((unsigned)Input::Right) << 0;
|
||||
}
|
||||
|
||||
if(status.p15 == 1 && status.p14 == 0) {
|
||||
keys = !system.interface->input_poll((unsigned)Input::Start) << 3;
|
||||
keys |= !system.interface->input_poll((unsigned)Input::Select) << 2;
|
||||
keys |= !system.interface->input_poll((unsigned)Input::B) << 1;
|
||||
keys |= !system.interface->input_poll((unsigned)Input::A) << 0;
|
||||
}
|
||||
|
||||
return (status.p15 << 5)
|
||||
| (status.p14 << 4)
|
||||
| (keys << 0);
|
||||
}
|
||||
|
||||
if(addr == 0xff04) { //DIV
|
||||
return status.div;
|
||||
}
|
||||
|
||||
if(addr == 0xff05) { //TIMA
|
||||
return status.tima;
|
||||
}
|
||||
|
||||
if(addr == 0xff06) { //TMA
|
||||
return status.tma;
|
||||
}
|
||||
|
||||
if(addr == 0xff07) { //TAC
|
||||
return (status.timer_enable << 2)
|
||||
| (status.timer_clock << 0);
|
||||
}
|
||||
|
||||
if(addr == 0xff0f) { //IF
|
||||
return (status.interrupt_request_joypad << 4)
|
||||
| (status.interrupt_request_serial << 3)
|
||||
| (status.interrupt_request_timer << 2)
|
||||
| (status.interrupt_request_stat << 1)
|
||||
| (status.interrupt_request_vblank << 0);
|
||||
}
|
||||
|
||||
if(addr == 0xffff) { //IE
|
||||
return (status.interrupt_enable_joypad << 4)
|
||||
| (status.interrupt_enable_serial << 3)
|
||||
| (status.interrupt_enable_timer << 2)
|
||||
| (status.interrupt_enable_stat << 1)
|
||||
| (status.interrupt_enable_vblank << 0);
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void CPU::mmio_write(uint16 addr, uint8 data) {
|
||||
if(addr >= 0xc000 && addr <= 0xdfff) { wram[addr & 0x1fff] = data; return; }
|
||||
if(addr >= 0xe000 && addr <= 0xfdff) { wram[addr & 0x1fff] = data; return; }
|
||||
if(addr >= 0xff80 && addr <= 0xfffe) { hram[addr & 0x7f] = data; return; }
|
||||
|
||||
if(addr == 0xff00) { //JOYP
|
||||
status.p15 = data & 0x20;
|
||||
status.p14 = data & 0x10;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff01) { //SB
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff02) { //SC
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff04) { //DIV
|
||||
status.div = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff05) { //TIMA
|
||||
status.tima = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff06) { //TMA
|
||||
status.tma = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff07) { //TAC
|
||||
status.timer_enable = data & 0x04;
|
||||
status.timer_clock = data & 0x03;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff0f) { //IF
|
||||
status.interrupt_request_joypad = data & 0x10;
|
||||
status.interrupt_request_serial = data & 0x08;
|
||||
status.interrupt_request_timer = data & 0x04;
|
||||
status.interrupt_request_stat = data & 0x02;
|
||||
status.interrupt_request_vblank = data & 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xffff) { //IE
|
||||
status.interrupt_enable_joypad = data & 0x10;
|
||||
status.interrupt_enable_serial = data & 0x08;
|
||||
status.interrupt_enable_timer = data & 0x04;
|
||||
status.interrupt_enable_stat = data & 0x02;
|
||||
status.interrupt_enable_vblank = data & 0x01;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,26 +3,70 @@
|
|||
// 456 clocks/scanline
|
||||
// 154 scanlines/frame
|
||||
|
||||
//4194304 / 4096 = 1024
|
||||
//4194304 / 262144 = 16
|
||||
//4194304 / 65536 = 64
|
||||
//4394304 / 16384 = 256
|
||||
|
||||
#ifdef CPU_CPP
|
||||
|
||||
#include "opcode.cpp"
|
||||
|
||||
void CPU::add_clocks(unsigned clocks) {
|
||||
clock += clocks;
|
||||
status.timer0 += clocks;
|
||||
if(status.timer0 >= 16) timer_stage0();
|
||||
|
||||
if(clock >= 456) scanline();
|
||||
cpu.clock += clocks;
|
||||
if(cpu.clock >= 0) co_switch(scheduler.active_thread = lcd.thread);
|
||||
}
|
||||
|
||||
void CPU::scanline() {
|
||||
clock -= 456;
|
||||
void CPU::timer_stage0() { //262144hz
|
||||
if(status.timer_clock == 1) {
|
||||
if(++status.tima == 0) {
|
||||
status.tima = status.tma;
|
||||
status.interrupt_request_timer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
lcd.status.ly++;
|
||||
if(lcd.status.ly >= 154) frame();
|
||||
status.timer0 -= 16;
|
||||
if(++status.timer1 >= 4) timer_stage1();
|
||||
}
|
||||
|
||||
void CPU::frame() {
|
||||
lcd.status.ly -= 154;
|
||||
scheduler.exit();
|
||||
void CPU::timer_stage1() { // 65536hz
|
||||
if(status.timer_clock == 2) {
|
||||
if(++status.tima == 0) {
|
||||
status.tima = status.tma;
|
||||
status.interrupt_request_timer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
status.timer1 -= 4;
|
||||
if(++status.timer2 >= 4) timer_stage2();
|
||||
}
|
||||
|
||||
void CPU::timer_stage2() { // 16384hz
|
||||
if(status.timer_clock == 3) {
|
||||
if(++status.tima == 0) {
|
||||
status.tima = status.tma;
|
||||
status.interrupt_request_timer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
status.div++;
|
||||
|
||||
status.timer2 -= 4;
|
||||
if(++status.timer3 >= 4) timer_stage3();
|
||||
}
|
||||
|
||||
void CPU::timer_stage3() { // 4096hz
|
||||
if(status.timer_clock == 0) {
|
||||
if(++status.tima == 0) {
|
||||
status.tima = status.tma;
|
||||
status.interrupt_request_timer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
status.timer3 -= 4;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
void add_clocks(unsigned clocks);
|
||||
void scanline();
|
||||
void frame();
|
||||
void timer_stage0();
|
||||
void timer_stage1();
|
||||
void timer_stage2();
|
||||
void timer_stage3();
|
||||
|
||||
//opcode.cpp
|
||||
void op_io();
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
namespace GameBoy {
|
||||
namespace Info {
|
||||
static const char Name[] = "bgameboy";
|
||||
static const char Version[] = "000.03";
|
||||
static const char Version[] = "000.04";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,16 +6,120 @@ namespace GameBoy {
|
|||
#include "mmio/mmio.cpp"
|
||||
LCD lcd;
|
||||
|
||||
void LCD::Main() {
|
||||
lcd.main();
|
||||
}
|
||||
|
||||
void LCD::main() {
|
||||
while(true) {
|
||||
add_clocks(4);
|
||||
}
|
||||
}
|
||||
|
||||
void LCD::add_clocks(unsigned clocks) {
|
||||
status.lx += clocks;
|
||||
if(status.lx >= 456) scanline();
|
||||
|
||||
cpu.clock -= clocks;
|
||||
if(cpu.clock <= 0) co_switch(scheduler.active_thread = cpu.thread);
|
||||
}
|
||||
|
||||
void LCD::scanline() {
|
||||
status.lx -= 456;
|
||||
status.ly++;
|
||||
|
||||
if(status.ly == 144) cpu.status.interrupt_request_vblank = 1;
|
||||
//print("Vblank - ", cpu.status.ime, " - ", cpu.status.interrupt_enable_vblank, "\n"); }
|
||||
if(status.ly == 154) frame();
|
||||
|
||||
if(status.ly < 144) render();
|
||||
}
|
||||
|
||||
void LCD::frame() {
|
||||
system.interface->video_refresh(screen);
|
||||
system.interface->input_poll();
|
||||
|
||||
status.ly = 0;
|
||||
scheduler.exit();
|
||||
}
|
||||
|
||||
void LCD::render() {
|
||||
uint8_t *output = screen + status.ly * 160;
|
||||
uint8 y = status.ly + status.scy;
|
||||
uint16 tmaddr = (status.bg_tilemap_select == 0 ? 0x1800 : 0x1c00);
|
||||
tmaddr += (y >> 3) * 32;
|
||||
tmaddr += (status.scx >> 3);
|
||||
|
||||
for(unsigned t = 0; t < 20; t++) {
|
||||
unsigned tdaddr;
|
||||
if(status.bg_tiledata_select == 0) {
|
||||
tdaddr = 0x1000 + (int8)vram[tmaddr + t] * 16;
|
||||
} else {
|
||||
tdaddr = 0x0000 + vram[tmaddr + t] * 16;
|
||||
}
|
||||
tdaddr += (status.ly & 7) * 2;
|
||||
|
||||
uint8 d0 = vram[tdaddr + 0];
|
||||
uint8 d1 = vram[tdaddr + 1];
|
||||
|
||||
for(unsigned x = 0; x < 8; x++) {
|
||||
uint8 palette = ((d0 & 0x80) >> 7) + ((d1 & 0x80) >> 6);
|
||||
d0 <<= 1, d1 <<= 1;
|
||||
*output++ = (3 - status.bgp[palette]) * 0x55;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LCD::power() {
|
||||
for(unsigned n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this; //VRAM
|
||||
for(unsigned n = 0xff40; n <= 0xff4f; n++) bus.mmio[n] = this; //MMIO
|
||||
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
|
||||
|
||||
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;
|
||||
|
||||
status.display_enable = 0;
|
||||
status.window_tilemap_select = 0;
|
||||
status.window_display_enable = 0;
|
||||
status.bg_tiledata_select = 0;
|
||||
status.bg_tilemap_select = 0;
|
||||
status.obj_size = 0;
|
||||
status.obj_enable = 0;
|
||||
status.bg_display = 0;
|
||||
|
||||
status.interrupt_lyc = 0;
|
||||
status.interrupt_oam = 0;
|
||||
status.interrupt_vblank = 0;
|
||||
status.interrupt_hblank = 0;
|
||||
status.coincidence = 0;
|
||||
status.mode = 0;
|
||||
|
||||
status.scy = 0;
|
||||
|
||||
status.scx = 0;
|
||||
|
||||
status.ly = 0;
|
||||
|
||||
status.lyc = 0;
|
||||
|
||||
for(unsigned n = 0; n < 4; n++) {
|
||||
status.bgp[n] = n;
|
||||
status.obp0[n] = n;
|
||||
status.obp1[n] = n;
|
||||
}
|
||||
|
||||
status.wy = 0;
|
||||
|
||||
status.wx = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,12 +2,65 @@ struct LCD : Processor, MMIO {
|
|||
#include "mmio/mmio.hpp"
|
||||
|
||||
struct Status {
|
||||
unsigned ly;
|
||||
unsigned lx;
|
||||
|
||||
//$ff40 LCDC
|
||||
bool display_enable;
|
||||
bool window_tilemap_select;
|
||||
bool window_display_enable;
|
||||
bool bg_tiledata_select;
|
||||
bool bg_tilemap_select;
|
||||
bool obj_size;
|
||||
bool obj_enable;
|
||||
bool bg_display;
|
||||
|
||||
//$ff41 STAT
|
||||
bool interrupt_lyc;
|
||||
bool interrupt_oam;
|
||||
bool interrupt_vblank;
|
||||
bool interrupt_hblank;
|
||||
bool coincidence;
|
||||
unsigned mode;
|
||||
|
||||
//$ff42 SCY
|
||||
uint8 scy;
|
||||
|
||||
//$ff43 SCX
|
||||
uint8 scx;
|
||||
|
||||
//$ff44 LY
|
||||
uint8 ly;
|
||||
|
||||
//$ff45 LYC
|
||||
uint8 lyc;
|
||||
|
||||
//$ff47 BGP
|
||||
uint8 bgp[4];
|
||||
|
||||
//$ff48 OBP0
|
||||
uint8 obp0[4];
|
||||
|
||||
//$ff49 OBP1
|
||||
uint8 obp1[4];
|
||||
|
||||
//$ff4a WY
|
||||
uint8 wy;
|
||||
|
||||
//$ff4b WX
|
||||
uint8 wx;
|
||||
} status;
|
||||
|
||||
uint8 screen[160 * 144];
|
||||
uint8 vram[8192];
|
||||
uint8 oam[160];
|
||||
|
||||
static void Main();
|
||||
void main();
|
||||
void add_clocks(unsigned clocks);
|
||||
void scanline();
|
||||
void frame();
|
||||
void render();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
};
|
||||
|
|
|
@ -1,26 +1,159 @@
|
|||
#ifdef LCD_CPP
|
||||
|
||||
uint8 LCD::mmio_read(uint16 addr) {
|
||||
if(addr >= 0xa000 && addr <= 0xbfff) return vram[addr & 0x1fff];
|
||||
if(addr >= 0x8000 && addr <= 0x9fff) return vram[addr & 0x1fff];
|
||||
if(addr >= 0xfe00 && addr <= 0xfe9f) return oam[addr & 0xff];
|
||||
|
||||
//LY
|
||||
if(addr == 0xff44) {
|
||||
if(addr == 0xff40) { //LCDC
|
||||
return (status.display_enable << 7)
|
||||
| (status.window_tilemap_select << 6)
|
||||
| (status.window_display_enable << 5)
|
||||
| (status.bg_tiledata_select << 4)
|
||||
| (status.bg_tilemap_select << 3)
|
||||
| (status.obj_size << 2)
|
||||
| (status.obj_enable << 1)
|
||||
| (status.bg_display << 0);
|
||||
}
|
||||
|
||||
if(addr == 0xff41) { //STAT
|
||||
return (status.interrupt_lyc << 6)
|
||||
| (status.interrupt_oam << 5)
|
||||
| (status.interrupt_vblank << 4)
|
||||
| (status.interrupt_hblank << 3)
|
||||
| (status.coincidence << 2)
|
||||
| (status.mode << 0);
|
||||
}
|
||||
|
||||
if(addr == 0xff42) { //SCY
|
||||
return status.scy;
|
||||
}
|
||||
|
||||
if(addr == 0xff43) { //SCX
|
||||
return status.scx;
|
||||
}
|
||||
|
||||
if(addr == 0xff44) { //LY
|
||||
return status.ly;
|
||||
}
|
||||
|
||||
if(addr == 0xff45) { //LYC
|
||||
return status.lyc;
|
||||
}
|
||||
|
||||
if(addr == 0xff47) { //BGP
|
||||
return (status.bgp[3] << 6)
|
||||
| (status.bgp[2] << 4)
|
||||
| (status.bgp[1] << 2)
|
||||
| (status.bgp[0] << 0);
|
||||
}
|
||||
|
||||
if(addr == 0xff48) { //OBP0
|
||||
return (status.obp0[3] << 6)
|
||||
| (status.obp0[2] << 4)
|
||||
| (status.obp0[1] << 2)
|
||||
| (status.obp0[0] << 0);
|
||||
}
|
||||
|
||||
if(addr == 0xff49) { //OBP1
|
||||
return (status.obp1[3] << 6)
|
||||
| (status.obp1[2] << 4)
|
||||
| (status.obp1[1] << 2)
|
||||
| (status.obp1[0] << 0);
|
||||
}
|
||||
|
||||
if(addr == 0xff4a) { //WY
|
||||
return status.wy;
|
||||
}
|
||||
|
||||
if(addr == 0xff4b) { //WX
|
||||
return status.wx;
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void LCD::mmio_write(uint16 addr, uint8 data) {
|
||||
if(addr >= 0xa000 && addr <= 0xbfff) { vram[addr & 0x1fff] = data; return; }
|
||||
if(addr >= 0x8000 && addr <= 0x9fff) { vram[addr & 0x1fff] = data; return; }
|
||||
if(addr >= 0xfe00 && addr <= 0xfe9f) { oam[addr & 0xff] = data; return; }
|
||||
|
||||
//LY
|
||||
if(addr == 0xff44) {
|
||||
if(addr == 0xff40) { //LCDC
|
||||
status.display_enable = data & 0x80;
|
||||
status.window_tilemap_select = data & 0x40;
|
||||
status.window_display_enable = data & 0x20;
|
||||
status.bg_tiledata_select = data & 0x10;
|
||||
status.bg_tilemap_select = data & 0x08;
|
||||
status.obj_size = data & 0x04;
|
||||
status.obj_enable = data & 0x02;
|
||||
status.bg_display = data & 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff41) { //STAT
|
||||
status.interrupt_lyc = data & 0x40;
|
||||
status.interrupt_oam = data & 0x20;
|
||||
status.interrupt_vblank = data & 0x10;
|
||||
status.interrupt_hblank = data & 0x08;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff42) { //SCY
|
||||
status.scy = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff43) { //SCX
|
||||
status.scx = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff44) { //LY
|
||||
status.ly = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff45) { //LYC
|
||||
status.lyc = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff46) { //DMA
|
||||
//TODO
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff47) { //BGP
|
||||
status.bgp[3] = (data >> 6) & 3;
|
||||
status.bgp[2] = (data >> 4) & 3;
|
||||
status.bgp[1] = (data >> 2) & 3;
|
||||
status.bgp[0] = (data >> 0) & 3;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff48) { //OBP0
|
||||
status.obp0[3] = (data >> 6) & 3;
|
||||
status.obp0[2] = (data >> 4) & 3;
|
||||
status.obp0[1] = (data >> 2) & 3;
|
||||
status.obp0[0] = (data >> 0) & 3;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff49) { //OBP1
|
||||
status.obp1[3] = (data >> 6) & 3;
|
||||
status.obp1[2] = (data >> 4) & 3;
|
||||
status.obp1[1] = (data >> 2) & 3;
|
||||
status.obp1[0] = (data >> 0) & 3;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff4a) { //WY
|
||||
status.wy = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff4b) { //WX
|
||||
status.wx = data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
class Interface;
|
||||
|
||||
enum class Input : unsigned {
|
||||
Up, Down, Left, Right, B, A, Select, Start,
|
||||
};
|
||||
|
||||
class System {
|
||||
public:
|
||||
void init(Interface*);
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
#include <nall/string.hpp>
|
||||
using namespace nall;
|
||||
|
||||
#include <ruby/ruby.hpp>
|
||||
using namespace ruby;
|
||||
|
||||
#include <phoenix/phoenix.hpp>
|
||||
using namespace phoenix;
|
||||
|
||||
|
@ -18,6 +21,10 @@ using namespace phoenix;
|
|||
struct Application {
|
||||
bool quit;
|
||||
|
||||
Font proportionalFont;
|
||||
Font proportionalFontBold;
|
||||
Font monospaceFont;
|
||||
|
||||
void main(int argc, char **argv);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
MainWindow mainWindow;
|
||||
|
||||
void MainWindow::create() {
|
||||
Window::create(128, 128, 160 * 3, 144 * 3, { GameBoy::Info::Name, " v", GameBoy::Info::Version });
|
||||
Window::create(128, 128, 160 * 2, 144 * 2, { GameBoy::Info::Name, " v", GameBoy::Info::Version });
|
||||
setDefaultFont(application.proportionalFont);
|
||||
setFont(application.proportionalFontBold);
|
||||
|
||||
system.create(*this, "System");
|
||||
systemLoadCartridge.create(system, "Load Cartridge ...");
|
||||
|
@ -12,15 +14,15 @@ void MainWindow::create() {
|
|||
systemReset.setEnabled(false);
|
||||
|
||||
settings.create(*this, "Settings");
|
||||
settings.setEnabled(false);
|
||||
//settings.setEnabled(false);
|
||||
|
||||
tools.create(*this, "Tools");
|
||||
tools.setEnabled(false);
|
||||
//tools.setEnabled(false);
|
||||
|
||||
help.create(*this, "Help");
|
||||
helpAbout.create(help, "About ...");
|
||||
|
||||
viewport.create(*this, 0, 0, 160 * 3, 144 * 3);
|
||||
viewport.create(*this, 0, 0, 160 * 2, 144 * 2);
|
||||
|
||||
setMenuVisible(true);
|
||||
setStatusVisible(true);
|
||||
|
|
|
@ -1,15 +1,52 @@
|
|||
Interface interface;
|
||||
|
||||
void Interface::video_refresh(const uint8_t *data) {
|
||||
uint32_t *buffer;
|
||||
unsigned pitch;
|
||||
if(video.lock(buffer, pitch, 160, 144)) {
|
||||
for(unsigned y = 0; y < 144; y++) {
|
||||
uint32_t *line = buffer + y * (pitch >> 2);
|
||||
const uint8_t *source = data + y * 160;
|
||||
for(unsigned x = 0; x < 160; x++) {
|
||||
uint32_t color = *source++;
|
||||
*line++ = (color << 16) | (color << 8) | (color << 0);
|
||||
}
|
||||
}
|
||||
video.unlock();
|
||||
video.refresh();
|
||||
}
|
||||
|
||||
static unsigned frameCounter = 0;
|
||||
static time_t timeCounter = time(0);
|
||||
|
||||
frameCounter++;
|
||||
time_t currentTime = time(0);
|
||||
if(currentTime != timeCounter) {
|
||||
timeCounter = currentTime;
|
||||
mainWindow.setStatusText({ "FPS: ", frameCounter });
|
||||
frameCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::audio_sample(signed left, signed right) {
|
||||
}
|
||||
|
||||
void Interface::input_poll() {
|
||||
input.poll(inputState);
|
||||
}
|
||||
|
||||
bool Interface::input_poll(unsigned id) {
|
||||
switch(id) {
|
||||
case GameBoy::Input::Up: return inputState[keyboard(0)[Keyboard::Up]];
|
||||
case GameBoy::Input::Down: return inputState[keyboard(0)[Keyboard::Down]];
|
||||
case GameBoy::Input::Left: return inputState[keyboard(0)[Keyboard::Left]];
|
||||
case GameBoy::Input::Right: return inputState[keyboard(0)[Keyboard::Right]];
|
||||
case GameBoy::Input::B: return inputState[keyboard(0)[Keyboard::Z]];
|
||||
case GameBoy::Input::A: return inputState[keyboard(0)[Keyboard::X]];
|
||||
case GameBoy::Input::Select: return inputState[keyboard(0)[Keyboard::Apostrophe]];
|
||||
case GameBoy::Input::Start: return inputState[keyboard(0)[Keyboard::Return]];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
struct Interface : public GameBoy::Interface {
|
||||
int16_t inputState[Scancode::Limit];
|
||||
|
||||
void video_refresh(const uint8_t *data);
|
||||
void audio_sample(signed left, signed right);
|
||||
void input_poll();
|
||||
|
|
34
ui/main.cpp
34
ui/main.cpp
|
@ -8,34 +8,42 @@ Application application;
|
|||
void Application::main(int argc, char **argv) {
|
||||
quit = false;
|
||||
|
||||
mainWindow.create();
|
||||
#if defined(PHOENIX_WINDOWS)
|
||||
proportionalFont.create("Tahoma", 8);
|
||||
proportionalFontBold.create("Tahoma", 8, Font::Style::Bold);
|
||||
monospaceFont.create("Courier New", 8);
|
||||
#else
|
||||
proportionalFont.create("Sans", 8);
|
||||
proportionalFontBold.create("Sans", 8, Font::Style::Bold);
|
||||
monospaceFont.create("Liberation Mono", 8);
|
||||
#endif
|
||||
|
||||
mainWindow.create();
|
||||
mainWindow.setVisible();
|
||||
OS::run();
|
||||
|
||||
video.driver("OpenGL");
|
||||
video.set(Video::Handle, (uintptr_t)mainWindow.viewport.handle());
|
||||
video.set(Video::Synchronize, false);
|
||||
video.set(Video::Filter, (unsigned)0);
|
||||
video.init();
|
||||
|
||||
input.driver("SDL");
|
||||
input.set(Input::Handle, (uintptr_t)mainWindow.viewport.handle());
|
||||
input.init();
|
||||
|
||||
GameBoy::system.init(&interface);
|
||||
|
||||
unsigned frameCounter = 0;
|
||||
time_t timeCounter = time(0);
|
||||
|
||||
while(quit == false) {
|
||||
OS::run();
|
||||
|
||||
if(GameBoy::cartridge.loaded()) {
|
||||
GameBoy::system.run();
|
||||
|
||||
frameCounter++;
|
||||
time_t currentTime = time(0);
|
||||
if(currentTime != timeCounter) {
|
||||
timeCounter = currentTime;
|
||||
mainWindow.setStatusText({ "FPS: ", frameCounter });
|
||||
frameCounter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
application.main(argc, argv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue