<commit_before>/* Copyright (c) 2013-2016 <NAME>
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gb/gb.h>

#include <mgba/internal/gb/io.h>
#include <mgba/internal/gb/mbc.h>
#include <mgba/internal/sm83/sm83.h>

#include <mgba/core/core.h>
#include <mgba/core/cheats.h>
#include <mgba-util/crc32.h>
#include <mgba-util/memory.h>
#include <mgba-util/math.h>
#include <mgba-util/patch.h>
#include <mgba-util/vfs.h>

#define CLEANUP_THRESHOLD 15

const uint32_t CGB_SM83_FREQUENCY = 0x800000;
const uint32_t SGB_SM83_FREQUENCY = 0x418B1E;

const uint32_t GB_COMPONENT_MAGIC = 0x400000;

static const uint8_t _knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66};

#define DMG_BIOS_CHECKSUM 0xC2F5CC97
#define DMG_2_BIOS_CHECKSUM 0x59C8598E
#define MGB_BIOS_CHECKSUM 0xE6920754
#define SGB_BIOS_CHECKSUM 0xEC8A83B9
#define SGB2_BIOS_CHECKSUM 0X53D0DD63
#define CGB_BIOS_CHECKSUM 0x41884E46

mLOG_DEFINE_CATEGORY(GB, "GB", "gb");

static void GBInit(void* cpu, struct mCPUComponent* component);
static void GBDeinit(struct mCPUComponent* component);
static void GBInterruptHandlerInit(struct SM83InterruptHandler* irqh);
static void GBProcessEvents(struct SM83Core* cpu);
static void GBSetInterrupts(struct SM83Core* cpu, bool enable);
static uint16_t GBIRQVector(struct SM83Core* cpu);
static void GBIllegal(struct SM83Core* cpu);
static void GBStop(struct SM83Core* cpu);

static void _enableInterrupts(struct mTiming* timing, void* user, uint32_t cyclesLate);

void GBCreate(struct GB* gb) {
	gb->d.id = GB_COMPONENT_MAGIC;
	gb->d.init = GBInit;
	gb->d.deinit = GBDeinit;
}

static void GBInit(void* cpu, struct mCPUComponent* component) {
	struct GB* gb = (struct GB*) component;
	gb->cpu = cpu;
	gb->sync = NULL;

	GBInterruptHandlerInit(&gb->cpu->irqh);
	GBMemoryInit(gb);

	gb->video.p = gb;
	GBVideoInit(&gb->video);

	gb->audio.p = gb;
	GBAudioInit(&gb->audio, 2048, &gb->memory.io[GB_REG_NR52], GB_AUDIO_DMG); // TODO: Remove magic constant

	gb->sio.p = gb;
	GBSIOInit(&gb->sio);

	gb->timer.p = gb;

	gb->model = GB_MODEL_AUTODETECT;

	gb->biosVf = NULL;
	gb->romVf = NULL;
	gb->sramVf = NULL;
	gb->sramRealVf = NULL;

	gb->isPristine = false;
	gb->pristineRomSize = 0;
	gb->yankedRomSize = 0;

	mCoreCallbacksListInit(&gb->coreCallbacks, 0);
	gb->stream = NULL;

	mTimingInit(&gb->timing, &gb->cpu->cycles, &gb->cpu->nextEvent);
	gb->audio.timing = &gb->timing;

	gb->eiPending.name = "GB EI";
	gb->eiPending.callback = _enableInterrupts;
	gb->eiPending.context = gb;
	gb->eiPending.priority = 0;
}

static void GBDeinit(struct mCPUComponent* component) {
	struct GB* gb = (struct GB*) component;
	mTimingDeinit(&gb->timing);
}

bool GBLoadROM(struct GB* gb, struct VFile* vf) {
	if (!vf) {
		return false;
	}
	GBUnloadROM(gb);
	gb->romVf = vf;
	gb->pristineRomSize = vf->size(vf);
	vf->seek(vf, 0, SEEK_SET);
	gb->isPristine = true;
	gb->memory.rom = vf->map(vf, gb->pristineRomSize, MAP_READ);
	if (!gb->memory.rom) {
		return false;
	}
	gb->yankedRomSize = 0;
	gb->memory.romBase = gb->memory.rom;
	gb->memory.romSize = gb->pristineRomSize;
	gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
	GBMBCInit(gb);

	if (gb->cpu) {
		struct SM83Core* cpu = gb->cpu;
		cpu->memory.setActiveRegion(cpu, cpu->pc);
	}

	// TODO: error check
	return true;
}

void GBYankROM(struct GB* gb) {
	gb->yankedRomSize = gb->memory.romSize;
	gb->yankedMbc = gb->memory.mbcType;
	gb->memory.romSize = 0;
	gb->memory.mbcType = GB_MBC_NONE;
	gb->memory.sramAccess = false;

	if (gb->cpu) {
		struct SM83Core* cpu = gb->cpu;
		cpu->memory.setActiveRegion(cpu, cpu->pc);
	}
}

static void GBSramDeinit(struct GB* gb) {
	if (gb->sramVf) {
		gb->sramVf->unmap(gb->sramVf, gb->memory.sram, gb->sramSize);
		if (gb->memory.mbcType == GB_MBC3_RTC && gb->sramVf == gb->sramRealVf) {
			GBMBCRTCWrite(gb);
		}
		gb->sramVf = NULL;
	} else if (gb->memory.sram) {
		mappedMemoryFree(gb->memory.sram, gb->sramSize);
	}
	gb->memory.sram = 0;
}

bool GBLoadSave(struct GB* gb, struct VFile* vf) {
	GBSramDeinit(gb);
	gb->sramVf = vf;
	gb->sramRealVf = vf;
	if (gb->sramSize) {
		GBResizeSram(gb, gb->sramSize);
		GBMBCSwitchSramBank(gb, gb->memory.sramCurrentBank);
	}
	return vf;
}

void GBResizeSram(struct GB* gb, size_t size) {
	if (gb->memory.sram && size <= gb->sramSize) {
		return;
	}
	struct VFile* vf = gb->sramVf;
	if (vf) {
		if (vf == gb->sramRealVf) {
			ssize_t vfSize = vf->size(vf);
			if (vfSize >= 0 && (size_t) vfSize < size) {
				uint8_t extdataBuffer[0x100];
				if (vfSize & 0xFF) {
					vf->seek(vf, -(vfSize & 0xFF), SEEK_END);
					vf->read(vf, extdataBuffer, vfSize & 0xFF);
				}
				if (gb->memory.sram) {
					vf->unmap(vf, gb->memory.sram, gb->sramSize);
				}
				vf->truncate(vf, size + (vfSize & 0xFF));
				if (vfSize & 0xFF) {
					vf->seek(vf, size, SEEK_SET);
					vf->write(vf, extdataBuffer, vfSize & 0xFF);<commit_msg>GB: Restore RTC state if loading save after reset<commit_after>/* Copyright (c) 2013-2016 <NAME>
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gb/gb.h>

#include <mgba/internal/gb/io.h>
#include <mgba/internal/gb/mbc.h>
#include <mgba/internal/sm83/sm83.h>

#include <mgba/core/core.h>
#include <mgba/core/cheats.h>
#include <mgba-util/crc32.h>
#include <mgba-util/memory.h>
#include <mgba-util/math.h>
#include <mgba-util/patch.h>
#include <mgba-util/vfs.h>

#define CLEANUP_THRESHOLD 15

const uint32_t CGB_SM83_FREQUENCY = 0x800000;
const uint32_t SGB_SM83_FREQUENCY = 0x418B1E;

const uint32_t GB_COMPONENT_MAGIC = 0x400000;

static const uint8_t _knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66};

#define DMG_BIOS_CHECKSUM 0xC2F5CC97
#define DMG_2_BIOS_CHECKSUM 0x59C8598E
#define MGB_BIOS_CHECKSUM 0xE6920754
#define SGB_BIOS_CHECKSUM 0xEC8A83B9
#define SGB2_BIOS_CHECKSUM 0X53D0DD63
#define CGB_BIOS_CHECKSUM 0x41884E46

mLOG_DEFINE_CATEGORY(GB, "GB", "gb");

static void GBInit(void* cpu, struct mCPUComponent* component);
static void GBDeinit(struct mCPUComponent* component);
static void GBInterruptHandlerInit(struct SM83InterruptHandler* irqh);
static void GBProcessEvents(struct SM83Core* cpu);
static void GBSetInterrupts(struct SM83Core* cpu, bool enable);
static uint16_t GBIRQVector(struct SM83Core* cpu);
static void GBIllegal(struct SM83Core* cpu);
static void GBStop(struct SM83Core* cpu);

static void _enableInterrupts(struct mTiming* timing, void* user, uint32_t cyclesLate);

void GBCreate(struct GB* gb) {
	gb->d.id = GB_COMPONENT_MAGIC;
	gb->d.init = GBInit;
	gb->d.deinit = GBDeinit;
}

static void GBInit(void* cpu, struct mCPUComponent* component) {
	struct GB* gb = (struct GB*) component;
	gb->cpu = cpu;
	gb->sync = NULL;

	GBInterruptHandlerInit(&gb->cpu->irqh);
	GBMemoryInit(gb);

	gb->video.p = gb;
	GBVideoInit(&gb->video);

	gb->audio.p = gb;
	GBAudioInit(&gb->audio, 2048, &gb->memory.io[GB_REG_NR52], GB_AUDIO_DMG); // TODO: Remove magic constant

	gb->sio.p = gb;
	GBSIOInit(&gb->sio);

	gb->timer.p = gb;

	gb->model = GB_MODEL_AUTODETECT;

	gb->biosVf = NULL;
	gb->romVf = NULL;
	gb->sramVf = NULL;
	gb->sramRealVf = NULL;

	gb->isPristine = false;
	gb->pristineRomSize = 0;
	gb->yankedRomSize = 0;

	mCoreCallbacksListInit(&gb->coreCallbacks, 0);
	gb->stream = NULL;

	mTimingInit(&gb->timing, &gb->cpu->cycles, &gb->cpu->nextEvent);
	gb->audio.timing = &gb->timing;

	gb->eiPending.name = "GB EI";
	gb->eiPending.callback = _enableInterrupts;
	gb->eiPending.context = gb;
	gb->eiPending.priority = 0;
}

static void GBDeinit(struct mCPUComponent* component) {
	struct GB* gb = (struct GB*) component;
	mTimingDeinit(&gb->timing);
}

bool GBLoadROM(struct GB* gb, struct VFile* vf) {
	if (!vf) {
		return false;
	}
	GBUnloadROM(gb);
	gb->romVf = vf;
	gb->pristineRomSize = vf->size(vf);
	vf->seek(vf, 0, SEEK_SET);
	gb->isPristine = true;
	gb->memory.rom = vf->map(vf, gb->pristineRomSize, MAP_READ);
	if (!gb->memory.rom) {
		return false;
	}
	gb->yankedRomSize = 0;
	gb->memory.romBase = gb->memory.rom;
	gb->memory.romSize = gb->pristineRomSize;
	gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
	GBMBCInit(gb);

	if (gb->cpu) {
		struct SM83Core* cpu = gb->cpu;
		cpu->memory.setActiveRegion(cpu, cpu->pc);
	}

	// TODO: error check
	return true;
}

void GBYankROM(struct GB* gb) {
	gb->yankedRomSize = gb->memory.romSize;
	gb->yankedMbc = gb->memory.mbcType;
	gb->memory.romSize = 0;
	gb->memory.mbcType = GB_MBC_NONE;
	gb->memory.sramAccess = false;

	if (gb->cpu) {
		struct SM83Core* cpu = gb->cpu;
		cpu->memory.setActiveRegion(cpu, cpu->pc);
	}
}

static void GBSramDeinit(struct GB* gb) {
	if (gb->sramVf) {
		gb->sramVf->unmap(gb->sramVf, gb->memory.sram, gb->sramSize);
		if (gb->memory.mbcType == GB_MBC3_RTC && gb->sramVf == gb->sramRealVf) {
			GBMBCRTCWrite(gb);
		}
		gb->sramVf = NULL;
	} else if (gb->memory.sram) {
		mappedMemoryFree(gb->memory.sram, gb->sramSize);
	}
	gb->memory.sram = 0;
}

bool GBLoadSave(struct GB* gb, struct VFile* vf) {
	GBSramDeinit(gb);
	gb->sramVf = vf;
	gb->sramRealVf = vf;
	if (gb->sramSize) {
		GBResizeSram(gb, gb->sramSize);
		GBMBCSwitchSramBank(gb, gb->memory.sramCurrentBank);

		if (gb->memory.mbcType == GB_MBC3_RTC) {
			GBMBCRTCRead(gb);
		}
	}
	return vf;
}

void GBResizeSram(struct GB* gb, size_t size) {
	if (gb->memory.sram && size <= gb->sramSize) {
		return;
	}
	struct VFile* vf = gb->sramVf;
	if (vf) {
		if (vf == gb->sramRealVf) {
			ssize_t vfSize = vf->size(vf);
			if (vfSize >= 0 && (size_t) vfSize < size) {
				uint8_t extdataBuffer[0x100];
				if (vfSize & 0xFF) {
					vf->seek(vf, -(vfSize & 0xFF), SEEK_END);
					vf->read(vf, extdataBuffer, vfSize & 0xFF);
				}
				if (gb->memory.sram) {
					vf->unmap(vf, gb->memory.sram, gb->sramSize);
				}
				vf->truncate(vf, size + (vfSize & 0xFF));
				if (vfSize & 0xFF) {
					vf->seek(vf, size, SEEK_SET);
					vf->write(vf, extdataBuffer, vfSize & 0xFF);
{'fraction_non_alphanumeric': 0.1575106026235329, 'fraction_numerical': 0.02988460400433968, 'mean_word_length': 4.320041972717734, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 234, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '286020', 'n_tokens_mistral': 4715, 'n_tokens_neox': 4348, 'n_words': 873}