Path: blob/a-new-beginning/Cherry/Core/GearcolecoCore.cpp
2 views
/*1* Gearcoleco - ColecoVision Emulator2* Copyright (C) 2021 Ignacio Sanchez34* This program is free software: you can redistribute it and/or modify5* it under the terms of the GNU General Public License as published by6* the Free Software Foundation, either version 3 of the License, or7* any later version.89* This program is distributed in the hope that it will be useful,10* but WITHOUT ANY WARRANTY; without even the implied warranty of11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12* GNU General Public License for more details.1314* You should have received a copy of the GNU General Public License15* along with this program. If not, see http://www.gnu.org/licenses/16*17*/1819#include <iomanip>20#include "GearcolecoCore.h"21#include "CVMemory.h"22#include "Processor.h"23#include "Audio.h"24#include "Video.h"25#include "Input.h"26#include "Cartridge.h"27#include "ColecoVisionIOPorts.h"28#include "no_bios.h"29#include "common.h"3031GearcolecoCore::GearcolecoCore()32{33InitPointer(m_pMemory);34InitPointer(m_pProcessor);35InitPointer(m_pAudio);36InitPointer(m_pVideo);37InitPointer(m_pInput);38InitPointer(m_pCartridge);39InitPointer(m_pColecoVisionIOPorts);40m_bPaused = true;41m_pixelFormat = GC_PIXEL_RGB888;42}4344GearcolecoCore::~GearcolecoCore()45{46SafeDelete(m_pColecoVisionIOPorts);47SafeDelete(m_pCartridge);48SafeDelete(m_pInput);49SafeDelete(m_pVideo);50SafeDelete(m_pAudio);51SafeDelete(m_pProcessor);52SafeDelete(m_pMemory);53}5455void GearcolecoCore::Init(GC_Color_Format pixelFormat)56{57Log("Loading %s core %s by Ignacio Sanchez", GEARCOLECO_TITLE, GEARCOLECO_VERSION);5859m_pixelFormat = pixelFormat;6061m_pCartridge = new Cartridge();62m_pMemory = new CVMemory(m_pCartridge);63m_pProcessor = new Processor(m_pMemory);64m_pAudio = new Audio();65m_pVideo = new Video(m_pMemory, m_pProcessor);66m_pInput = new Input(m_pProcessor);67m_pColecoVisionIOPorts = new ColecoVisionIOPorts(m_pAudio, m_pVideo, m_pInput, m_pCartridge, m_pMemory, m_pProcessor);6869m_pMemory->Init();70m_pProcessor->Init();71m_pAudio->Init();72m_pVideo->Init();73m_pInput->Init();74m_pCartridge->Init();7576m_pProcessor->SetIOPOrts(m_pColecoVisionIOPorts);77}7879bool GearcolecoCore::RunToVBlank(u8* pFrameBuffer, s16* pSampleBuffer, int* pSampleCount, bool step, bool stopOnBreakpoints)80{81if (!m_pMemory->IsBiosLoaded())82{83RenderFrameBuffer(pFrameBuffer);84return false;85}8687bool breakpoint = false;8889if (!m_bPaused && m_pCartridge->IsReady())90{91bool vblank = false;92int totalClocks = 0;93while (!vblank)94{95#ifdef PERFORMANCE96unsigned int clockCycles = m_pProcessor->RunFor(75);97#else98unsigned int clockCycles = m_pProcessor->RunFor(1);99#endif100vblank = m_pVideo->Tick(clockCycles);101m_pAudio->Tick(clockCycles);102m_pMemory->Tick(clockCycles);103104totalClocks += clockCycles;105106#ifndef GEARCOLECO_DISABLE_DISASSEMBLER107if ((step || (stopOnBreakpoints && m_pProcessor->BreakpointHit())) && !m_pProcessor->DuringInputOpcode())108{109vblank = true;110if (m_pProcessor->BreakpointHit())111breakpoint = true;112}113#endif114115if (totalClocks > 702240)116vblank = true;117}118119m_pAudio->EndFrame(pSampleBuffer, pSampleCount);120RenderFrameBuffer(pFrameBuffer);121}122123return breakpoint;124}125126bool GearcolecoCore::LoadROM(const char* szFilePath, Cartridge::ForceConfiguration* config)127{128if (m_pCartridge->LoadFromFile(szFilePath))129{130if (IsValidPointer(config))131m_pCartridge->ForceConfig(*config);132133m_pMemory->SetupMapper();134Reset();135136m_pMemory->ResetRomDisassembledMemory();137m_pProcessor->DisassembleNextOpcode();138139return true;140}141else142return false;143}144145bool GearcolecoCore::LoadROMFromBuffer(const u8* buffer, int size, Cartridge::ForceConfiguration* config)146{147if (m_pCartridge->LoadFromBuffer(buffer, size))148{149if (IsValidPointer(config))150m_pCartridge->ForceConfig(*config);151152m_pMemory->SetupMapper();153Reset();154155m_pMemory->ResetRomDisassembledMemory();156m_pProcessor->DisassembleNextOpcode();157158return true;159}160else161return false;162}163164void GearcolecoCore::SaveDisassembledROM()165{166CVMemory::stDisassembleRecord** biosMap = m_pMemory->GetDisassembledBiosMemoryMap();167CVMemory::stDisassembleRecord** romMap = m_pMemory->GetDisassembledRomMemoryMap();168169if (m_pCartridge->IsReady() && (strlen(m_pCartridge->GetFilePath()) > 0) && IsValidPointer(romMap))170{171using namespace std;172173char path[512];174175strcpy(path, m_pCartridge->GetFilePath());176strcat(path, ".dis");177178Log("Saving Disassembled ROM %s...", path);179180ofstream myfile;181open_ofstream_utf8(myfile, path, ios::out | ios::trunc);182183if (myfile.is_open())184{185#define PAD_ADDR(digits) std::uppercase << std::hex << std::setw(digits) << std::setfill('0')186#define PAD_MEM(chars) std::setw(chars) << std::setfill(' ')187188for (int i = 0; i < 0x2000; i++)189{190if (IsValidPointer(biosMap[i]) && (biosMap[i]->name[0] != 0))191{192myfile << "BIOS $" << PAD_ADDR(4) << i << " " << PAD_MEM(12) << biosMap[i]->bytes << " " << biosMap[i]->name << "\n";193}194}195196for (int i = 0; i < MAX_ROM_SIZE; i++)197{198if (IsValidPointer(romMap[i]) && (romMap[i]->name[0] != 0))199{200myfile << "ROM $" << PAD_ADDR(4) << i + 0x8000 << " " << PAD_MEM(12) << romMap[i]->bytes << " " << romMap[i]->name << "\n";201}202}203204myfile.close();205}206207Debug("Disassembled ROM Saved");208}209}210211bool GearcolecoCore::GetRuntimeInfo(GC_RuntimeInfo& runtime_info)212{213runtime_info.screen_width = GC_RESOLUTION_WIDTH;214runtime_info.screen_height = GC_RESOLUTION_HEIGHT;215runtime_info.region = Region_NTSC;216217if (m_pCartridge->IsReady() && m_pMemory->IsBiosLoaded())218{219if (m_pVideo->GetOverscan() == Video::OverscanFull284)220runtime_info.screen_width = GC_RESOLUTION_WIDTH + GC_RESOLUTION_SMS_OVERSCAN_H_284_L + GC_RESOLUTION_SMS_OVERSCAN_H_284_R;221if (m_pVideo->GetOverscan() == Video::OverscanFull320)222runtime_info.screen_width = GC_RESOLUTION_WIDTH + GC_RESOLUTION_SMS_OVERSCAN_H_320_L + GC_RESOLUTION_SMS_OVERSCAN_H_320_R;223if (m_pVideo->GetOverscan() != Video::OverscanDisabled)224runtime_info.screen_height = GC_RESOLUTION_HEIGHT + (2 * (m_pCartridge->IsPAL() ? GC_RESOLUTION_OVERSCAN_V_PAL : GC_RESOLUTION_OVERSCAN_V));225runtime_info.region = m_pCartridge->IsPAL() ? Region_PAL : Region_NTSC;226return true;227}228229return false;230}231232CVMemory* GearcolecoCore::GetMemory()233{234return m_pMemory;235}236237Cartridge* GearcolecoCore::GetCartridge()238{239return m_pCartridge;240}241242Processor* GearcolecoCore::GetProcessor()243{244return m_pProcessor;245}246247Audio* GearcolecoCore::GetAudio()248{249return m_pAudio;250}251252Video* GearcolecoCore::GetVideo()253{254return m_pVideo;255}256257void GearcolecoCore::KeyPressed(GC_Controllers controller, GC_Keys key)258{259m_pInput->KeyPressed(controller, key);260}261262void GearcolecoCore::KeyReleased(GC_Controllers controller, GC_Keys key)263{264m_pInput->KeyReleased(controller, key);265}266267void GearcolecoCore::Spinner1(int movement)268{269m_pInput->Spinner1(movement);270}271272void GearcolecoCore::Spinner2(int movement)273{274m_pInput->Spinner2(movement);275}276277void GearcolecoCore::Pause(bool paused)278{279if (paused)280{281Log("Gearcoleco PAUSED");282}283else284{285Log("Gearcoleco RESUMED");286}287m_bPaused = paused;288}289290bool GearcolecoCore::IsPaused()291{292return m_bPaused;293}294295void GearcolecoCore::ResetROM(Cartridge::ForceConfiguration* config)296{297if (m_pCartridge->IsReady())298{299Log("Gearcoleco RESET");300301if (IsValidPointer(config))302m_pCartridge->ForceConfig(*config);303304Reset();305306m_pProcessor->DisassembleNextOpcode();307}308}309310void GearcolecoCore::ResetROMPreservingRAM(Cartridge::ForceConfiguration* config)311{312// TODO313314ResetROM(config);315}316317void GearcolecoCore::ResetSound()318{319m_pAudio->Reset(m_pCartridge->IsPAL());320}321322void GearcolecoCore::SaveRam()323{324SaveRam(NULL);325}326327void GearcolecoCore::SaveRam(const char*, bool)328{329// TODO330}331332void GearcolecoCore::LoadRam()333{334LoadRam(NULL);335}336337void GearcolecoCore::LoadRam(const char*, bool)338{339// TODO340}341342void GearcolecoCore::SaveState(int index)343{344Log("Creating save state %d...", index);345346SaveState(NULL, index);347348Debug("Save state %d created", index);349}350351void GearcolecoCore::SaveState(const char* szPath, int index)352{353Log("Creating save state...");354355using namespace std;356357size_t size;358SaveState(NULL, size);359360u8* buffer = new u8[size];361string path = "";362363if (IsValidPointer(szPath))364{365path += szPath;366path += "/";367path += m_pCartridge->GetFileName();368}369else370{371path = m_pCartridge->GetFilePath();372}373374string::size_type i = path.rfind('.', path.length());375376if (i != string::npos) {377path.replace(i + 1, 3, "state");378}379380std::stringstream sstm;381382if (index < 0)383sstm << szPath;384else385sstm << path << index;386387Log("Save state file: %s", sstm.str().c_str());388389ofstream file;390open_ofstream_utf8(file, sstm.str().c_str(), ios::out | ios::binary);391392SaveState(file, size);393394SafeDeleteArray(buffer);395396file.close();397398Debug("Save state created");399}400401bool GearcolecoCore::SaveState(u8* buffer, size_t& size)402{403bool ret = false;404405if (m_pCartridge->IsReady())406{407using namespace std;408409stringstream stream;410411if (SaveState(stream, size))412ret = true;413414if (IsValidPointer(buffer))415{416Log("Saving state to buffer [%d bytes]...", size);417memcpy(buffer, stream.str().c_str(), size);418ret = true;419}420}421else422{423Log("Invalid rom.");424}425426return ret;427}428429bool GearcolecoCore::SaveState(std::ostream& stream, size_t& size)430{431if (m_pCartridge->IsReady())432{433Debug("Gathering save state data...");434435m_pMemory->SaveState(stream);436m_pProcessor->SaveState(stream);437m_pAudio->SaveState(stream);438m_pVideo->SaveState(stream);439m_pInput->SaveState(stream);440441size = static_cast<size_t>(stream.tellp());442size += (sizeof(u32) * 2);443444u32 header_magic = GC_SAVESTATE_MAGIC;445u32 header_size = static_cast<u32>(size);446447stream.write(reinterpret_cast<const char*> (&header_magic), sizeof(header_magic));448stream.write(reinterpret_cast<const char*> (&header_size), sizeof(header_size));449450Debug("Save state size: %d", static_cast<size_t>(stream.tellp()));451452return true;453}454455Log("Invalid rom.");456457return false;458}459460void GearcolecoCore::LoadState(int index)461{462Log("Loading save state %d...", index);463464LoadState(NULL, index);465466Debug("State %d file loaded", index);467}468469void GearcolecoCore::LoadState(const char* szPath, int index)470{471Log("Loading save state...");472473using namespace std;474475string sav_path = "";476477if (IsValidPointer(szPath))478{479sav_path += szPath;480sav_path += "/";481sav_path += m_pCartridge->GetFileName();482}483else484{485sav_path = m_pCartridge->GetFilePath();486}487488string rom_path = sav_path;489490string::size_type i = sav_path.rfind('.', sav_path.length());491492if (i != string::npos) {493sav_path.replace(i + 1, 3, "state");494}495496std::stringstream sstm;497498if (index < 0)499sstm << szPath;500else501sstm << sav_path << index;502503Log("Opening save file: %s", sstm.str().c_str());504505ifstream file;506507open_ifstream_utf8(file, sstm.str().c_str(), ios::in | ios::binary);508509if (!file.fail())510{511if (LoadState(file))512{513Debug("Save state loaded");514}515}516else517{518Log("Save state file doesn't exist");519}520521file.close();522}523524bool GearcolecoCore::LoadState(const u8* buffer, size_t size)525{526if (m_pCartridge->IsReady() && (size > 0) && IsValidPointer(buffer))527{528Debug("Gathering load state data [%d bytes]...", size);529530using namespace std;531532stringstream stream;533534stream.write(reinterpret_cast<const char*> (buffer), size);535536return LoadState(stream);537}538539Log("Invalid rom or memory.");540541return false;542}543544bool GearcolecoCore::LoadState(std::istream& stream)545{546if (m_pCartridge->IsReady())547{548using namespace std;549550u32 header_magic = 0;551u32 header_size = 0;552553stream.seekg(0, ios::end);554size_t size = static_cast<size_t>(stream.tellg());555stream.seekg(0, ios::beg);556557Debug("Load state stream size: %d", size);558559stream.seekg(size - (2 * sizeof(u32)), ios::beg);560stream.read(reinterpret_cast<char*> (&header_magic), sizeof(header_magic));561stream.read(reinterpret_cast<char*> (&header_size), sizeof(header_size));562stream.seekg(0, ios::beg);563564Debug("Load state magic: 0x%08x", header_magic);565Debug("Load state size: %d", header_size);566567if ((header_size == size) && (header_magic == GC_SAVESTATE_MAGIC))568{569Log("Loading state...");570571m_pMemory->LoadState(stream);572m_pProcessor->LoadState(stream);573m_pAudio->LoadState(stream);574m_pVideo->LoadState(stream);575m_pInput->LoadState(stream);576577return true;578}579else580{581Log("Invalid save state size or header");582}583}584else585{586Log("Invalid rom");587}588589return false;590}591592void GearcolecoCore::Reset()593{594m_pMemory->Reset();595m_pProcessor->Reset();596m_pAudio->Reset(m_pCartridge->IsPAL());597m_pVideo->Reset(m_pCartridge->IsPAL());598m_pInput->Reset();599m_pColecoVisionIOPorts->Reset();600m_bPaused = false;601}602603void GearcolecoCore::RenderFrameBuffer(u8* finalFrameBuffer)604{605int size = m_pMemory->IsBiosLoaded() ? GC_RESOLUTION_WIDTH_WITH_OVERSCAN * GC_RESOLUTION_HEIGHT_WITH_OVERSCAN : GC_RESOLUTION_WIDTH * GC_RESOLUTION_HEIGHT;606u16* srcBuffer = (m_pMemory->IsBiosLoaded() ? m_pVideo->GetFrameBuffer() : kNoBiosImage);607608switch (m_pixelFormat)609{610case GC_PIXEL_RGB555:611case GC_PIXEL_BGR555:612case GC_PIXEL_RGB565:613case GC_PIXEL_BGR565:614{615m_pVideo->Render16bit(srcBuffer, finalFrameBuffer, m_pixelFormat, size, true);616break;617}618case GC_PIXEL_RGB888:619case GC_PIXEL_BGR888:620{621m_pVideo->Render24bit(srcBuffer, finalFrameBuffer, m_pixelFormat, size, true);622break;623}624}625}626627628