Files
gameboy/mem.cpp

245 lines
7.0 KiB
C++

#include "mem.h"
#include "sm83.h"
#include <stdlib.h>
#include <stdio.h>
#include <SDL2/SDL.h>
#include "APU.h"
#include "mbc_none.h"
#include "mbc1.h"
#define DIV hram[0x104] // 0xff04 - 0xfe00
#define TIMA hram[0x105] // 0xff05 - 0xfe00
#define TMA hram[0x106] // 0xff06 - 0xfe00
#define TAC hram[0x107] // 0xff07 - 0xfe00
namespace mem
{
void (*resetMbc)(void);
uint8_t (*readRom)(uint16_t);
void (*writeRom)(uint16_t, uint8_t);
uint8_t (*readRam)(uint16_t);
void (*writeRam)(uint16_t, uint8_t);
uint8_t bootrom[256];
uint8_t *rom;
uint8_t vram[8192];
uint8_t wram[8192];
uint8_t hram[512];
uint8_t tags[65536];
char *title = nullptr;
uint32_t rom_size = 0;
uint32_t ram_size = 0;
uint16_t dma_address = 0;
uint8_t dma_pos = 160;
uint16_t dma_dots = 0;
uint16_t div_counter = 0;
uint16_t timer_counter = 0;
void init(uint8_t* rom, const int size)
{
title = (char*)&rom[0x134];
uint8_t mapper_type = rom[0x147];
rom_size = 32768 * (1 << rom[0x148]);
int sizes[] = { 0, 0, 8, 32, 128, 64};
ram_size = sizes[rom[0x149]] * 1024;
switch (mapper_type)
{
case 0x00:
mbc_none::init(rom, rom_size, ram_size);
mem::resetMbc = mbc_none::reset;
mem::readRom = mbc_none::readRom;
mem::writeRom = mbc_none::writeRom;
mem::readRam = mbc_none::readRam;
mem::writeRam = mbc_none::writeRam;
break;
case 0x01:
case 0x02:
case 0x03:
mbc1::init(rom, rom_size, ram_size);
mem::resetMbc = mbc1::reset;
mem::readRom = mbc1::readRom;
mem::writeRom = mbc1::writeRom;
mem::readRam = mbc1::readRam;
mem::writeRam = mbc1::writeRam;
break;
};
APU::init();
}
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<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; }
resetMbc();
APU::reset();
}
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];
return readRom(address);
} else if (address < 0xA000) {
return vram[address - 0x8000];
} else if (address < 0xC000) {
return readRam(address);
} 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]);
} else if (address>=0xFF10 && address<=0xFF26) { return APU::readRegister(address); }
return hram[address - 0XFE00];
}
}
void writeMem(uint16_t address, uint8_t value)
{
if (address < 0x8000) {
writeRom(address, value);
} else if (address < 0xA000) {
vram[address - 0x8000] = value;
} else if (address < 0xC000) {
writeRam(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
} else if ( (address==0xFF00) ) {
value = value & 0x30;
} else if ( (address==0xFF04) ) {
hram[address-0xFE00] = 0;
return;
} else if ( (address==0xFF0F) ) { // IF
hram[address-0xFE00] = value;
sm83::processInterrupts();
return;
} else if (address>=0xFF10 && address<=0xFF26) { // APU
APU::writeRegister(address, value);
return;
} else if ( (address==0xFF46) ) { // OAM DMA
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)
{
return &hram[address-0xfe00];
}
void init_dma_transfer(uint8_t source)
{
dma_address = source << 8;
dma_pos = 0;
dma_dots = 0;
}
uint16_t timer_frequencies[4] { 256*4, 4*4, 16*4, 64*4 };
void update_mapped(const uint32_t dt)
{
// DIV Divider register (0xFF04) (val com a timer bàsic)
div_counter += dt;
if (div_counter>=256) {
div_counter -= 256;
bool bitset = DIV&0x10;
DIV++;
if (bitset && !(DIV&0x10)) APU::incDIVAPU();
}
// Timer
if (TAC&0x4) { // if bit 3 of mem(0xff07) is 1, timer enabled
uint16_t freq = timer_frequencies[TAC&0x03];
timer_counter += dt;
if (timer_counter>=freq) {
timer_counter -= freq;
if (TIMA<255)
TIMA++;
else {
TIMA = TMA;
sm83::interrupt(INTERRUPT_TIMER);
}
}
}
// APU
APU::update(dt);
// OAM DMA
if (dma_pos<160) {
dma_dots += dt;
while (dma_dots >= 4 && dma_pos<160) {
dma_dots -= 4;
hram[dma_pos] = mem::readMem(dma_address|dma_pos);
dma_pos++;
}
}
}
}