diff --git a/gameboy/cartridge/cartridge.cpp b/gameboy/cartridge/cartridge.cpp index 611b35f5..53d3d66a 100755 --- a/gameboy/cartridge/cartridge.cpp +++ b/gameboy/cartridge/cartridge.cpp @@ -3,7 +3,8 @@ #define CARTRIDGE_CPP namespace GameBoy { -#include "mmio/mmio.cpp" +#include "mbc0/mbc0.cpp" +#include "mbc1/mbc1.cpp" Cartridge cartridge; void Cartridge::load(uint8_t *data, unsigned size) { @@ -18,7 +19,18 @@ void Cartridge::load(uint8_t *data, unsigned size) { info.cgbflag = romdata[0x0143]; info.sgbflag = romdata[0x0146]; - info.type = romdata[0x0147]; + + info.mapper = Mapper::Unknown; + info.ram = false; + info.battery = false; + info.rtc = false; + + switch(romdata[0x0147]) { default: + 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; + } switch(romdata[0x0148]) { default: case 0x00: info.romsize = 2 * 16 * 1024; break; @@ -42,6 +54,8 @@ void Cartridge::load(uint8_t *data, unsigned size) { case 0x03: info.ramsize = 32 * 1024; break; } + ramdata = new uint8_t[ramsize = info.ramsize](); + loaded = true; } @@ -53,13 +67,48 @@ void Cartridge::unload() { loaded = false; } +uint8 Cartridge::rom_read(unsigned addr) { + if(addr >= romsize) addr %= romsize; + return romdata[addr]; +} + +void Cartridge::rom_write(unsigned addr, uint8 data) { + if(addr >= romsize) addr %= romsize; + romdata[addr] = data; +} + +uint8 Cartridge::ram_read(unsigned addr) { + if(ramsize == 0) return 0x00; + if(addr >= ramsize) addr %= ramsize; + return ramdata[addr]; +} + +void Cartridge::ram_write(unsigned addr, uint8 data) { + if(ramsize == 0) return; + if(addr >= ramsize) addr %= ramsize; + ramdata[addr] = data; +} + void Cartridge::power() { - for(unsigned n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this; + mbc0.power(); + mbc1.power(); + + MMIO *mapper = 0; + switch(info.mapper) { + case Mapper::MBC0: mapper = &mbc0; break; + case Mapper::MBC1: mapper = &mbc1; 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() { diff --git a/gameboy/cartridge/cartridge.hpp b/gameboy/cartridge/cartridge.hpp index 4868fc56..03e4a1f2 100755 --- a/gameboy/cartridge/cartridge.hpp +++ b/gameboy/cartridge/cartridge.hpp @@ -1,11 +1,23 @@ -struct Cartridge : MMIO, property { - #include "mmio/mmio.hpp" +struct Cartridge : property { + #include "mbc0/mbc0.hpp" + #include "mbc1/mbc1.hpp" + + enum Mapper : unsigned { + MBC0, + MBC1, + Unknown, + }; struct Information { string name; uint8 cgbflag; uint8 sgbflag; - uint8 type; + + Mapper mapper; + bool ram; + bool battery; + bool rtc; + unsigned romsize; unsigned ramsize; } info; @@ -21,6 +33,11 @@ struct Cartridge : MMIO, property { void load(uint8_t *data, unsigned size); void unload(); + uint8 rom_read(unsigned addr); + void rom_write(unsigned addr, uint8 data); + uint8 ram_read(unsigned addr); + void ram_write(unsigned addr, uint8 data); + void power(); void reset(); diff --git a/gameboy/cartridge/mbc0/mbc0.cpp b/gameboy/cartridge/mbc0/mbc0.cpp new file mode 100755 index 00000000..85a8a505 --- /dev/null +++ b/gameboy/cartridge/mbc0/mbc0.cpp @@ -0,0 +1,17 @@ +#ifdef CARTRIDGE_CPP + +uint8 Cartridge::MBC0::mmio_read(uint16 addr) { + if(addr >= 0x0000 && addr <= 0x7fff) return cartridge.rom_read(addr); + return 0x00; +} + +void Cartridge::MBC0::mmio_write(uint16 addr, uint8 data) { +} + +void Cartridge::MBC0::power() { +} + +void Cartridge::MBC0::reset() { +} + +#endif diff --git a/gameboy/cartridge/mbc0/mbc0.hpp b/gameboy/cartridge/mbc0/mbc0.hpp new file mode 100755 index 00000000..fef9f9a9 --- /dev/null +++ b/gameboy/cartridge/mbc0/mbc0.hpp @@ -0,0 +1,6 @@ +struct MBC0 : MMIO { + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + void power(); + void reset(); +} mbc0; diff --git a/gameboy/cartridge/mbc1/mbc1.cpp b/gameboy/cartridge/mbc1/mbc1.cpp new file mode 100755 index 00000000..fedb6b79 --- /dev/null +++ b/gameboy/cartridge/mbc1/mbc1.cpp @@ -0,0 +1,62 @@ +#ifdef CARTRIDGE_CPP + +uint8 Cartridge::MBC1::mmio_read(uint16 addr) { + if(addr >= 0x0000 && addr <= 0x3fff) { + return cartridge.rom_read(addr); + } + + if(addr >= 0x4000 && addr <= 0x7fff) { + return cartridge.rom_read(rom_bank | (addr & 0x3fff)); + } + + if(addr >= 0xa000 && addr <= 0xbfff) { + if(ram_enable) return cartridge.ram_read(ram_bank | (addr & 0x1fff)); + return 0x00; + } +} + +void Cartridge::MBC1::mmio_write(uint16 addr, uint8 data) { + if(addr >= 0x0000 && addr <= 0x1fff) { + ram_enable = (data & 0x0f) == 0x0a; + } + + if(addr >= 0x2000 && addr <= 0x3fff) { + rom_select = data & 0x1f; + if(rom_select == 0) rom_select = 1; + } + + if(addr >= 0x4000 && addr <= 0x5fff) { + ram_select = data & 0x03; + } + + if(addr >= 0x6000 && addr <= 0x7fff) { + mode_select = data & 0x01; + } + + if(mode_select == 0) { + rom_bank = (ram_select << 19) | (rom_select << 14); + ram_bank = 0x00; + } else { + rom_bank = (rom_select << 14); + ram_bank = (ram_select << 13); + } + + if(addr >= 0xa000 && addr <= 0xbfff) { + if(ram_enable) cartridge.ram_write(ram_bank | (addr & 0x1fff), data); + } +} + +void Cartridge::MBC1::power() { +} + +void Cartridge::MBC1::reset() { + ram_enable = false; + rom_select = 0x01; + ram_select = 0x00; + mode_select = 0; + + rom_bank = 0x4000; + ram_bank = 0x0000; +} + +#endif diff --git a/gameboy/cartridge/mbc1/mbc1.hpp b/gameboy/cartridge/mbc1/mbc1.hpp new file mode 100755 index 00000000..516acde8 --- /dev/null +++ b/gameboy/cartridge/mbc1/mbc1.hpp @@ -0,0 +1,14 @@ +struct MBC1 : MMIO { + bool ram_enable; //0000-1fff + uint8 rom_select; //2000-3fff + uint8 ram_select; //4000-5fff + bool mode_select; //6000-7fff + + unsigned rom_bank; + unsigned ram_bank; + + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + void power(); + void reset(); +} mbc1; diff --git a/gameboy/cartridge/mmio/mmio.cpp b/gameboy/cartridge/mmio/mmio.cpp deleted file mode 100755 index 749b5895..00000000 --- a/gameboy/cartridge/mmio/mmio.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#ifdef CARTRIDGE_CPP - -uint8 Cartridge::mmio_read(uint16 addr) { - if(addr >= 0x0000 && addr <= 0x7fff) return romdata[addr]; -} - -void Cartridge::mmio_write(uint16 addr, uint8 data) { -} - -#endif diff --git a/gameboy/cartridge/mmio/mmio.hpp b/gameboy/cartridge/mmio/mmio.hpp deleted file mode 100755 index 92b8f614..00000000 --- a/gameboy/cartridge/mmio/mmio.hpp +++ /dev/null @@ -1,2 +0,0 @@ -uint8 mmio_read(uint16 addr); -void mmio_write(uint16 addr, uint8 data); diff --git a/gameboy/cpu/core/core.cpp b/gameboy/cpu/core/core.cpp index 317b936b..e1e226e2 100755 --- a/gameboy/cpu/core/core.cpp +++ b/gameboy/cpu/core/core.cpp @@ -566,11 +566,13 @@ void CPU::op_nop() { } void CPU::op_halt() { - //TODO + status.halt = true; + while(status.halt == true) op_io(); } void CPU::op_stop() { - //TODO + status.stop = true; + while(status.stop == true) op_io(); } void CPU::op_di() { diff --git a/gameboy/cpu/cpu.cpp b/gameboy/cpu/cpu.cpp index 70b4130f..f01523e4 100755 --- a/gameboy/cpu/cpu.cpp +++ b/gameboy/cpu/cpu.cpp @@ -21,6 +21,17 @@ void CPU::main() { } } +void CPU::interrupt_raise(CPU::Interrupt id) { + switch(id) { + case Interrupt::Vblank: status.interrupt_request_vblank = 1; break; + case Interrupt::Stat : status.interrupt_request_stat = 1; break; + case Interrupt::Timer : status.interrupt_request_timer = 1; break; + case Interrupt::Serial: status.interrupt_request_serial = 1; break; + case Interrupt::Joypad: status.interrupt_request_joypad = 1; break; + } + status.halt = false; +} + void CPU::interrupt_test() { if(status.ime) { if(status.interrupt_request_vblank && status.interrupt_enable_vblank) { @@ -80,6 +91,9 @@ void CPU::reset() { r[DE] = 0x0000; r[HL] = 0x0000; + status.halt = false; + status.stop = false; + status.ime = 0; status.timer0 = 0; status.timer1 = 0; diff --git a/gameboy/cpu/cpu.hpp b/gameboy/cpu/cpu.hpp index 28f4d211..7d410b35 100755 --- a/gameboy/cpu/cpu.hpp +++ b/gameboy/cpu/cpu.hpp @@ -3,7 +3,18 @@ struct CPU : Processor, MMIO { #include "mmio/mmio.hpp" #include "timing/timing.hpp" + enum class Interrupt : unsigned { + Vblank, + Stat, + Timer, + Serial, + Joypad, + }; + struct Status { + bool halt; + bool stop; + bool ime; unsigned timer0; unsigned timer1; @@ -48,6 +59,7 @@ struct CPU : Processor, MMIO { static void Main(); void main(); + void interrupt_raise(Interrupt id); void interrupt_test(); void interrupt_exec(uint16 pc); void power(); diff --git a/gameboy/cpu/mmio/mmio.cpp b/gameboy/cpu/mmio/mmio.cpp index e18b1e5e..610474b9 100755 --- a/gameboy/cpu/mmio/mmio.cpp +++ b/gameboy/cpu/mmio/mmio.cpp @@ -16,7 +16,7 @@ void CPU::mmio_joyp_poll() { status.joyp = 0x0f; if(status.p15 == 0) status.joyp &= button ^ 0x0f; if(status.p14 == 0) status.joyp &= dpad ^ 0x0f; - if(status.joyp != 0x0f) status.interrupt_request_joypad = 1; + if(status.joyp != 0x0f) interrupt_raise(Interrupt::Joypad); } uint8 CPU::mmio_read(uint16 addr) { diff --git a/gameboy/cpu/timing/timing.cpp b/gameboy/cpu/timing/timing.cpp index 0d96e62e..b62c5995 100755 --- a/gameboy/cpu/timing/timing.cpp +++ b/gameboy/cpu/timing/timing.cpp @@ -21,10 +21,10 @@ void CPU::add_clocks(unsigned clocks) { } void CPU::timer_stage0() { //262144hz - if(status.timer_clock == 1) { + if(status.timer_enable && status.timer_clock == 1) { if(++status.tima == 0) { status.tima = status.tma; - status.interrupt_request_timer = 1; + interrupt_raise(Interrupt::Timer); } } @@ -33,10 +33,10 @@ void CPU::timer_stage0() { //262144hz } void CPU::timer_stage1() { // 65536hz - if(status.timer_clock == 2) { + if(status.timer_enable && status.timer_clock == 2) { if(++status.tima == 0) { status.tima = status.tma; - status.interrupt_request_timer = 1; + interrupt_raise(Interrupt::Timer); } } @@ -45,10 +45,10 @@ void CPU::timer_stage1() { // 65536hz } void CPU::timer_stage2() { // 16384hz - if(status.timer_clock == 3) { + if(status.timer_enable && status.timer_clock == 3) { if(++status.tima == 0) { status.tima = status.tma; - status.interrupt_request_timer = 1; + interrupt_raise(Interrupt::Timer); } } @@ -59,10 +59,10 @@ void CPU::timer_stage2() { // 16384hz } void CPU::timer_stage3() { // 4096hz - if(status.timer_clock == 0) { + if(status.timer_enable && status.timer_clock == 0) { if(++status.tima == 0) { status.tima = status.tma; - status.interrupt_request_timer = 1; + interrupt_raise(Interrupt::Timer); } } diff --git a/gameboy/gameboy.hpp b/gameboy/gameboy.hpp index 4c131b7f..13c62158 100755 --- a/gameboy/gameboy.hpp +++ b/gameboy/gameboy.hpp @@ -5,7 +5,7 @@ namespace GameBoy { namespace Info { static const char Name[] = "bgameboy"; - static const char Version[] = "000.05"; + static const char Version[] = "000.06"; } } diff --git a/gameboy/lcd/lcd.cpp b/gameboy/lcd/lcd.cpp index be9818bc..abcf26ad 100755 --- a/gameboy/lcd/lcd.cpp +++ b/gameboy/lcd/lcd.cpp @@ -13,6 +13,10 @@ void LCD::Main() { void LCD::main() { while(true) { add_clocks(4); + + if(status.lx == 320) { + if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat); + } } } @@ -28,8 +32,14 @@ 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.interrupt_lyc == true) { + if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat); + } + + if(status.ly == 144) { + cpu.interrupt_raise(CPU::Interrupt::Vblank); + if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat); + } if(status.ly == 154) frame(); if(status.ly < 144) render(); @@ -45,54 +55,130 @@ void LCD::frame() { } 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; - } + for(unsigned n = 0; n < 160; n++) { + line[n].source = Line::Source::None; + line[n].output = 0; } - output = screen + status.ly * 160; + 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(); + } + + uint8_t *output = screen + status.ly * 160; + for(unsigned n = 0; n < 160; n++) { + output[n] = (3 - line[n].output) * 0x55; + } +} + +void LCD::render_bg() { + unsigned iy = (status.ly + status.scy) & 255; + unsigned ix = status.scx; + + 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]; + } + + ix = (ix + 1) & 255; + tx = (tx + 1) & 7; + } +} + +void LCD::render_window() { + if(status.wy > status.ly) return; + unsigned iy = (status.ly + status.wy) & 255; + unsigned ix = (status.wx - 7) & 255; + + 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]; + } + + ix = (ix + 1) & 255; + tx = (tx + 1) & 7; + } +} + +void LCD::render_obj() { + unsigned obj_size = (status.obj_size == 0 ? 8 : 16); + for(unsigned s = 0; s < 40; s++) { - unsigned sy = oam[(s << 2) + 0] - 9; - unsigned sx = oam[(s << 2) + 1] - 8; + unsigned sy = oam[(s << 2) + 0] - 16; + unsigned sx = oam[(s << 2) + 1] - 8; unsigned tile = oam[(s << 2) + 2]; unsigned attribute = oam[(s << 2) + 3]; - sy -= status.ly; - if(sy >= 8) continue; - if(attribute & 0x40||1) sy ^= 7; + sy = status.ly - sy; + if(sy >= obj_size) continue; + if(attribute & 0x40) sy ^= (obj_size - 1); - unsigned addr = tile * 16 + sy * 2; + unsigned tdaddr = tile * 16 + sy * 2; - uint8 d0 = vram[addr + 0]; - uint8 d1 = vram[addr + 1]; - unsigned xflip = attribute & 0x20 ? -7 : 0; + uint8 d0 = vram[tdaddr + 0]; + uint8 d1 = vram[tdaddr + 1]; + unsigned xflip = attribute & 0x20 ? 7 : 0; - for(unsigned x = 0; x < 8; x++) { + for(unsigned tx = 0; tx < 8; tx++) { uint8 palette = ((d0 & 0x80) >> 7) + ((d1 & 0x80) >> 6); d0 <<= 1, d1 <<= 1; if(palette == 0) continue; + palette = status.obp[(bool)(attribute & 0x10)][palette]; - output[sx + (x ^ xflip)] = (3 - palette) * 0x55; + unsigned ox = sx + (tx ^ xflip); + + if(ox <= 159) { + if((attribute & 0x80) == 1) { + if(line[ox].source == Line::Source::BG && line[ox].output > 0) continue; + } + line[ox].source = Line::Source::OBJ; + line[ox].output = palette; + } } } } @@ -121,21 +207,16 @@ void LCD::reset() { status.bg_tilemap_select = 0; status.obj_size = 0; status.obj_enable = 0; - status.bg_display = 0; + status.bg_enable = 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++) { @@ -145,7 +226,6 @@ void LCD::reset() { } status.wy = 0; - status.wx = 0; } diff --git a/gameboy/lcd/lcd.hpp b/gameboy/lcd/lcd.hpp index d819b3b0..cd3dcc12 100755 --- a/gameboy/lcd/lcd.hpp +++ b/gameboy/lcd/lcd.hpp @@ -12,15 +12,13 @@ struct LCD : Processor, MMIO { bool bg_tilemap_select; bool obj_size; bool obj_enable; - bool bg_display; + bool bg_enable; //$ff41 STAT bool interrupt_lyc; bool interrupt_oam; bool interrupt_vblank; bool interrupt_hblank; - bool coincidence; - unsigned mode; //$ff42 SCY uint8 scy; @@ -52,12 +50,20 @@ struct LCD : Processor, MMIO { uint8 vram[8192]; uint8 oam[160]; + struct Line { + enum class Source : unsigned { None, BG, OBJ, Window } source; + uint8 output; + } line[160]; + static void Main(); void main(); void add_clocks(unsigned clocks); void scanline(); void frame(); void render(); + void render_bg(); + void render_window(); + void render_obj(); void power(); void reset(); diff --git a/gameboy/lcd/mmio/mmio.cpp b/gameboy/lcd/mmio/mmio.cpp index 230ba463..aafef096 100755 --- a/gameboy/lcd/mmio/mmio.cpp +++ b/gameboy/lcd/mmio/mmio.cpp @@ -12,16 +12,21 @@ uint8 LCD::mmio_read(uint16 addr) { | (status.bg_tilemap_select << 3) | (status.obj_size << 2) | (status.obj_enable << 1) - | (status.bg_display << 0); + | (status.bg_enable << 0); } if(addr == 0xff41) { //STAT + unsigned mode; + if(status.ly >= 144) mode = 1; //Vblank + else if(status.lx >= 320) mode = 0; //Hblank + else mode = 3; //LCD transfer + return (status.interrupt_lyc << 6) | (status.interrupt_oam << 5) | (status.interrupt_vblank << 4) | (status.interrupt_hblank << 3) - | (status.coincidence << 2) - | (status.mode << 0); + | ((status.ly == status.lyc) << 2) + | (mode << 0); } if(addr == 0xff42) { //SCY @@ -84,7 +89,7 @@ void LCD::mmio_write(uint16 addr, uint8 data) { status.bg_tilemap_select = data & 0x08; status.obj_size = data & 0x04; status.obj_enable = data & 0x02; - status.bg_display = data & 0x01; + status.bg_enable = data & 0x01; return; } diff --git a/ui/general/main-window.cpp b/ui/general/main-window.cpp index 7aa7541e..9aa81741 100755 --- a/ui/general/main-window.cpp +++ b/ui/general/main-window.cpp @@ -33,7 +33,7 @@ void MainWindow::create() { }; systemLoadCartridge.onTick = []() { - string filename = OS::fileOpen(mainWindow, "Game Boy cartridges\t*.gb", "/media/sdb1/root/gameboy_images/"); + string filename = OS::fileOpen(mainWindow, "Game Boy cartridges\t*.gb,*.gbc", "/media/sdb1/root/gameboy_images/"); if(filename != "") utility.loadCartridge(filename); };