diff --git a/mbc1.cpp b/mbc1.cpp new file mode 100644 index 0000000..f90d1e1 --- /dev/null +++ b/mbc1.cpp @@ -0,0 +1,200 @@ +#include "mbc1.h" +#include "mem.h" +#include +#include + +namespace mbc1 +{ + #define ROM_BANK_SIZE 0x4000 + #define RAM_BANK_SIZE 0x2000 + + uint8_t bootrom[256]; + uint8_t *rom; + uint8_t vram[8192]; + uint8_t exram[4 * RAM_BANK_SIZE]; + uint8_t wram[8192]; + uint8_t hram[512]; + + uint8_t tags[65536]; + + uint8_t current_rom_bank = 1; + uint8_t current_ram_bank = 0; + bool ram_enabled = false; + bool banking_mode = false; // false = ROM banking mode, true = RAM banking mode + + uint8_t getKeypad(uint8_t value) + { + const uint8_t *keys = SDL_GetKeyboardState(NULL); + value = value & 0xf0; + if (value & 0x10) { + if (!keys[SDL_SCANCODE_RETURN]) value = value | 0x8; + if (!keys[SDL_SCANCODE_SPACE]) value = value | 0x4; + if (!keys[SDL_SCANCODE_Z]) value = value | 0x2; + if (!keys[SDL_SCANCODE_X]) value = value | 0x1; + } else if (value & 0x20) { + if (!keys[SDL_SCANCODE_DOWN]) value = value | 0x8; + if (!keys[SDL_SCANCODE_UP]) value = value | 0x4; + if (!keys[SDL_SCANCODE_LEFT]) value = value | 0x2; + if (!keys[SDL_SCANCODE_RIGHT]) value = value | 0x1; + } else { + value = value | 0x0f; + } + return value; + } + + uint8_t readMem(uint16_t address) + { + if (address < 0x8000) { + if ( (address < 0x0100) && ((hram[0x150]&0x01)==0) ) return bootrom[address]; + if (address < 0x4000) { + // ROM Bank 0 + return rom[address]; + } else { + // Switchable ROM bank + uint32_t banked_address = (current_rom_bank * ROM_BANK_SIZE) + (address - 0x4000); + return rom[banked_address]; + } + } else if (address < 0xA000) { + return vram[address - 0x8000]; + } else if (address < 0xC000) { + if (ram_enabled) { + uint32_t banked_address = (current_ram_bank * RAM_BANK_SIZE) + (address - 0xA000); + return exram[banked_address]; + } + return 0xFF; // Return open bus value when RAM is disabled + } else if (address < 0xE000) { + return wram[address - 0xC000]; + } else if (address < 0xFE00) { + return wram[address - 0xE000]; + } else { + if (address==0xFF00) { + hram[address - 0XFE00] = getKeypad(hram[address - 0XFE00]); + } + return hram[address - 0XFE00]; + } + } + + void writeMem(uint16_t address, uint8_t value) + { + if (address < 0x8000) { + if (address < 0x2000) { + // Enable/disable RAM + ram_enabled = (value & 0x0F) == 0x0A; + } else if (address < 0x4000) { + // Select ROM bank + value &= 0x1F; // Lower 5 bits are used + if (value == 0) value = 1; // Bank 0 is not allowed + current_rom_bank = (current_rom_bank & 0x60) | value; + } else if (address < 0x6000) { + // Select RAM bank or upper bits of ROM bank + if (banking_mode) { + current_ram_bank = value & 0x03; // 2 bits for RAM bank + } else { + current_rom_bank = (current_rom_bank & 0x1F) | ((value & 0x03) << 5); + } + } else { + // Select banking mode + banking_mode = value & 0x01; + } + } else if (address < 0xA000) { + vram[address - 0x8000] = value; + } else if (address < 0xC000) { + if (ram_enabled) { + uint32_t banked_address = (current_ram_bank * RAM_BANK_SIZE) + (address - 0xA000); + exram[banked_address] = value; + } + } else if (address < 0xE000) { + wram[address - 0xC000] = value; + } else if (address < 0xFE00) { + wram[address - 0xE000] = value; + } else { + if ( (address==0xFF50) && ((value&0x01) != 1) ) return; //Only allow disabling boot room + if ( (address==0xFF00) ) { value = value & 0x30; } + if ( (address==0xFF04) ) { hram[address-0xFE00] = 0; return; } + if ( (address==0xFF46) ) mem::init_dma_transfer(value); + hram[address - 0xFE00] = value; + } + } + + uint8_t getTag(uint16_t address) + { + return tags[address]; + } + + void setTag(uint16_t address, uint8_t value) + { + tags[address] = value; + } + + void saveState(FILE* f) + { + + } + + void loadState(FILE *f) + { + + } + + uint8_t* rawPtr(uint16_t address) + { + if (address < 0x8000) { + if ( (address < 0x0100) && ((hram[0x150]&0x01)==0) ) return &bootrom[address]; + if (address < 0x4000) { + // ROM Bank 0 + return &rom[address]; + } else { + // Switchable ROM bank + uint32_t banked_address = (current_rom_bank * ROM_BANK_SIZE) + (address - 0x4000); + return &rom[banked_address]; + } + } else if (address < 0xA000) { + return &vram[address - 0x8000]; + } else if (address < 0xC000) { + if (ram_enabled) { + uint32_t banked_address = (current_ram_bank * RAM_BANK_SIZE) + (address - 0xA000); + return &exram[banked_address]; + } + return nullptr; // Return open bus value when RAM is disabled + } else if (address < 0xE000) { + return &wram[address - 0xC000]; + } else if (address < 0xFE00) { + return &wram[address - 0xE000]; + } else { + return &hram[address - 0XFE00]; + } + } + + void reset() + { + FILE *f = fopen("dmg_boot.bin", "rb"); + if (!f) { printf("ABORTING: 'dmg_boot.bin' not found!\n"); exit(1); } + fseek(f, 0, SEEK_END); + const int size = ftell(f); + fseek(f, 0, SEEK_SET); + fread(bootrom, size, 1, f); + fclose(f); + + for (int i=0; i<8192; ++i) { vram[i] = 0; } + for (int i=0; i<4*RAM_BANK_SIZE; ++i) { exram[i] = 0; } + for (int i=0; i<8192; ++i) { wram[i] = 0; } + for (int i=0; i<512; ++i) { hram[i] = 0; } + for (int i=0; i<65536; ++i) { tags[i] = MEMTAG_NONE; } + } + + void init(uint8_t *rom, uint32_t rom_size, uint32_t ram_size) + { + mem::readMem = mbc1::readMem; + mem::writeMem = mbc1::writeMem; + mem::getTag = mbc1::getTag; + mem::setTag = mbc1::setTag; + mem::saveState = mbc1::saveState; + mem::loadState = mbc1::loadState; + mem::reset = mbc1::reset; + mem::rawPtr = mbc1::rawPtr; + + mbc1::rom = rom; + + reset(); + } +} \ No newline at end of file diff --git a/mbc1.h b/mbc1.h new file mode 100644 index 0000000..8452049 --- /dev/null +++ b/mbc1.h @@ -0,0 +1,7 @@ +#pragma once +#include + +namespace mbc1 +{ + void init(uint8_t *rom, uint32_t rom_size, uint32_t ram_size); +} diff --git a/mem.cpp b/mem.cpp index dd38a65..9ce33e7 100644 --- a/mem.cpp +++ b/mem.cpp @@ -4,6 +4,7 @@ #include #include "mbc_none.h" +#include "mbc1.h" namespace mem { void (*reset)(void); @@ -39,6 +40,11 @@ namespace mem case 0x00: mbc_none::init(rom, rom_size, ram_size); break; + case 0x01: + case 0x02: + case 0x03: + mbc1::init(rom, rom_size, ram_size); + break; }; }