wasmboy
Version:
Gameboy / Gameboy Color Emulator written for Web Assembly using AssemblyScript. Shell/Debugger in Preact
187 lines (161 loc) • 5.85 kB
text/typescript
// Imports
import { WASMBOY_WASM_PAGES, WASMBOY_STATE_LOCATION } from './constants';
import { Config } from './config';
import { resetCycles } from './cycles';
import { resetSteps } from './execute';
import { Cpu, initializeCpu } from './cpu/index';
import { Graphics, initializeGraphics, initializePalette } from './graphics/index';
import { Interrupts, initializeInterrupts } from './interrupts/index';
import { Joypad } from './joypad/index';
import { Memory, initializeCartridge, initializeDma, eightBitStoreIntoGBMemory, eightBitLoadFromGBMemory } from './memory/index';
import { Timers, initializeTimers } from './timers/index';
import { Sound, initializeSound, Channel1, Channel2, Channel3, Channel4 } from './sound/index';
import { initializeSerial } from './serial/serial';
// Grow our memory to the specified size
if (memory.size() < WASMBOY_WASM_PAGES) {
memory.grow(WASMBOY_WASM_PAGES - memory.size());
}
// Function to track if the core has started
let hasStarted: boolean = false;
export function setHasCoreStarted(value: boolean): void {
hasStarted = value;
}
export function hasCoreStarted(): i32 {
return <i32>hasStarted;
}
// Function to configure & initialize wasmboy
export function config(
enableBootRom: i32,
useGbcWhenAvailable: i32,
audioBatchProcessing: i32,
graphicsBatchProcessing: i32,
timersBatchProcessing: i32,
graphicsDisableScanlineRendering: i32,
audioAccumulateSamples: i32,
tileRendering: i32,
tileCaching: i32,
enableAudioDebugging: i32
): void {
// TODO: depending on the boot rom, initialization may be different
// From: http://www.codeslinger.co.uk/pages/projects/gameboy/hardware.html
// All values default to zero in memory, so not setting them yet
// log('initializing (includeBootRom=$0)', 1, enableBootRom);
Config.enableBootRom = enableBootRom > 0;
Config.useGbcWhenAvailable = useGbcWhenAvailable > 0;
Config.audioBatchProcessing = audioBatchProcessing > 0;
Config.graphicsBatchProcessing = graphicsBatchProcessing > 0;
Config.timersBatchProcessing = timersBatchProcessing > 0;
Config.graphicsDisableScanlineRendering = graphicsDisableScanlineRendering > 0;
Config.audioAccumulateSamples = audioAccumulateSamples > 0;
Config.tileRendering = tileRendering > 0;
Config.tileCaching = tileCaching > 0;
Config.enableAudioDebugging = enableAudioDebugging > 0;
initialize();
}
// Function to initiialize the core
function initialize(): void {
// Initialization variables from BGB
// First, try to switch to Gameboy Color Mode
// Get our GBC support from the cartridge header
// http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header
let gbcType = eightBitLoadFromGBMemory(0x0143);
// Detecting GBC http://bgb.bircd.org/pandocs.htm#cgbregisters
if (gbcType === 0xc0 || (Config.useGbcWhenAvailable && gbcType === 0x80)) {
Cpu.GBCEnabled = true;
} else {
Cpu.GBCEnabled = false;
}
// Reset hasStarted, since we are now reset
setHasCoreStarted(false);
// Reset our cycles ran
resetCycles();
resetSteps();
if (Config.enableBootRom) {
Cpu.BootROMEnabled = true;
} else {
Cpu.BootROMEnabled = false;
}
// Call our respective classes intialization
// NOTE: Boot ROM Only handles some initialization, thus we need to check in each one
// respecitvely :p
initializeCpu();
initializeCartridge();
initializeDma();
initializeGraphics();
initializePalette();
initializeSound();
initializeInterrupts();
initializeTimers();
initializeSerial();
initializeVarious();
}
function initializeVarious(): void {
// Various Other Registers
if (Cpu.GBCEnabled) {
// Various other registers
eightBitStoreIntoGBMemory(0xff70, 0xf8);
eightBitStoreIntoGBMemory(0xff4f, 0xfe);
eightBitStoreIntoGBMemory(0xff4d, 0x7e);
eightBitStoreIntoGBMemory(0xff00, 0xcf);
eightBitStoreIntoGBMemory(0xff0f, 0xe1);
// 0xFFFF = 0x00
// Undocumented from Pandocs
eightBitStoreIntoGBMemory(0xff6c, 0xfe);
eightBitStoreIntoGBMemory(0xff75, 0x8f);
} else {
eightBitStoreIntoGBMemory(0xff70, 0xff);
eightBitStoreIntoGBMemory(0xff4f, 0xff);
eightBitStoreIntoGBMemory(0xff4d, 0xff);
eightBitStoreIntoGBMemory(0xff00, 0xcf);
eightBitStoreIntoGBMemory(0xff0f, 0xe1);
// 0xFFFF = 0x00
}
}
// Function to return if we are currently playing a GBC ROM
export function isGBC(): i32 {
return <i32>Cpu.GBCEnabled;
}
// Function to return an address to store into save state memory
// this is to regulate our 20 slots
// https://docs.google.com/spreadsheets/d/17xrEzJk5-sCB9J2mMJcVnzhbE-XH_NvczVSQH9OHvRk/edit?usp=sharing
// Inlined because closure compiler inlines
export function getSaveStateMemoryOffset(offset: i32, saveStateSlot: i32): i32 {
// 50 bytes per save state memory partiton sli32
return WASMBOY_STATE_LOCATION + offset + 50 * saveStateSlot;
}
// Function to save state to memory for all of our classes
export function saveState(): void {
Cpu.saveState();
Graphics.saveState();
Interrupts.saveState();
Joypad.saveState();
Memory.saveState();
Timers.saveState();
Sound.saveState();
Channel1.saveState();
Channel2.saveState();
Channel3.saveState();
Channel4.saveState();
// Reset hasStarted, since we are now reset
setHasCoreStarted(false);
// Don't want to reset cycles here, as this does not reset the emulator
}
// Function to load state from memory for all of our classes
export function loadState(): void {
Cpu.loadState();
Graphics.loadState();
Interrupts.loadState();
Joypad.loadState();
Memory.loadState();
Timers.loadState();
Sound.loadState();
Channel1.loadState();
Channel2.loadState();
Channel3.loadState();
Channel4.loadState();
// Reset hasStarted, since we are now reset
setHasCoreStarted(false);
// Reset our cycles ran
resetCycles();
resetSteps();
}