- [FIX] Sampling rate pujat a 44100Hz, el aliasing quasi desapareix - [NEW] Canals 2 i 3 implementats - [FIX] El control de duració en els canals 1 i 2 era incorrecte
245 lines
7.0 KiB
C++
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<=0xFF3F) { 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<=0xFF3F) { // 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++;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|