Path: blob/master/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp
2 views
//TODO - find out somehow when the parent process is gone (check a pid or some slot on that for existence and match vs logged value?)1// we cant just check the state of a socket or named pipe or whatever in the case of IPCRingBuffer23//TODO - clean up signaling namespaces.. hooks vs callbacks vs etc. (unify them, at least)4//maybe turn into signal vs break concept (signals would be synchronous [expecting the frontend to always field them immediately and allow execution to resume immediately]) vs breaks (which the frontend receives control after)5//also a COMMAND concept (comes from frontend.. run, init, etc.)6//7//TODO - consolidate scanline breakpoint into this, with something like a set_break(type,address,size) which could be used in the future for read/write/execute but for now, for scanline (address would be the scanline)8//9//TODO - factor out modular components (ringbuffer and the like)1011//types of messages:12//cmd: frontend->core: "command to core" a command from the frontend which causes emulation to proceed. when sending a command, the frontend should wait for an eMessage_BRK_Complete before proceeding, although a debugger might proceed after any BRK13//query: frontend->core: "query to core" a query from the frontend which can (and should) be satisfied immediately by the core but which does not result in emulation processes (notably, nothing resembling a CMD and nothing which can trigger a BRK)14//sig: core->frontend: "core signal" a synchronous operation called from the emulation process which the frontend should handle immediately without issuing any calls into the core15//brk: core->frontend: "core break" the emulation process has suspended. the frontend is free to do whatever it wishes.16//(and there are other assorted special messages...)171819#include <Windows.h>2021#define LIBSNES_IMPORT22#include "snes/snes.hpp"23#include "libsnes.hpp"2425#include <libco/libco.h>2627#include <string.h>28#include <stdio.h>29#include <stdlib.h>3031#include <map>32#include <string>33#include <vector>3435extern SNES::Interface *iface;3637typedef uint8 u8;38typedef int32 s32;39typedef uint32 u32;40typedef uint16 u16;4142enum eMessage : int3243{44eMessage_NotSet,4546eMessage_SetBuffer,47eMessage_BeginBufferIO,48eMessage_EndBufferIO,49eMessage_ResumeAfterBRK, //resumes execution of the core, after a BRK. no change to current CMD50eMessage_Shutdown,5152eMessage_QUERY_library_id,53eMessage_QUERY_library_revision_major,54eMessage_QUERY_library_revision_minor,55eMessage_QUERY_get_region,56eMessage_QUERY_get_mapper,57eMessage_QUERY_get_memory_size,58eMessage_QUERY_get_memory_data, //note: this function isnt used and hasnt been tested in a while59eMessage_QUERY_peek,60eMessage_QUERY_poke,61eMessage_QUERY_serialize_size,62eMessage_QUERY_poll_message,63eMessage_QUERY_dequeue_message,64eMessage_QUERY_set_color_lut,65eMessage_QUERY_GetMemoryIdName,66eMessage_QUERY_state_hook_exec,67eMessage_QUERY_state_hook_read,68eMessage_QUERY_state_hook_write,69eMessage_QUERY_state_hook_nmi,70eMessage_QUERY_state_hook_irq,71eMessage_QUERY_enable_trace,72eMessage_QUERY_enable_scanline,73eMessage_QUERY_enable_audio,74eMessage_QUERY_set_layer_enable,75eMessage_QUERY_set_backdropColor,76eMessage_QUERY_peek_logical_register,77eMessage_QUERY_peek_cpu_regs,78eMessage_QUERY_set_cdl,7980eMessage_CMD_FIRST,81eMessage_CMD_init,82eMessage_CMD_power,83eMessage_CMD_reset,84eMessage_CMD_run,85eMessage_CMD_serialize,86eMessage_CMD_unserialize,87eMessage_CMD_load_cartridge_normal,88eMessage_CMD_load_cartridge_super_game_boy,89eMessage_CMD_term,90eMessage_CMD_unload_cartridge,91eMessage_CMD_LAST,9293eMessage_SIG_video_refresh,94eMessage_SIG_input_poll,95eMessage_SIG_input_state,96eMessage_SIG_input_notify,97eMessage_SIG_audio_flush,98eMessage_SIG_path_request,99eMessage_SIG_trace_callback,100eMessage_SIG_allocSharedMemory, //?101eMessage_SIG_freeSharedMemory, //?102103eMessage_BRK_Complete,104eMessage_BRK_hook_exec,105eMessage_BRK_hook_read,106eMessage_BRK_hook_write,107eMessage_BRK_hook_nmi,108eMessage_BRK_hook_irq,109eMessage_BRK_scanlineStart, //implemented as a BRK because that's really what it is, its just a graphical event and not a CPU event110};111112113enum eEmulationExitReason114{115eEmulationExitReason_NotSet,116eEmulationExitReason_BRK,117eEmulationExitReason_SIG,118eEmulationExitReason_CMD_Complete,119};120121enum eEmulationCallback122{123eEmulationCallback_snes_video_refresh,124eEmulationCallback_snes_audio_flush,125eEmulationCallback_snes_scanline,126eEmulationCallback_snes_input_poll,127eEmulationCallback_snes_input_state,128eEmulationCallback_snes_input_notify,129eEmulationCallback_snes_path_request,130131eEmulationCallback_snes_allocSharedMemory,132eEmulationCallback_snes_freeSharedMemory,133134eEmulationCallback_snes_trace135};136137struct EmulationControl138{139volatile eMessage command;140volatile eEmulationExitReason exitReason;141142//the result code for a CMD143s32 cmd_result;144145union146{147struct148{149volatile eMessage hookExitType;150uint32 hookAddr;151uint8 hookValue;152};153154struct155{156volatile eEmulationCallback exitCallbackType;157union158{159struct160{161const uint32_t *data;162unsigned width;163unsigned height;164} cb_video_refresh_params;165struct166{167int32_t scanline;168} cb_scanline_params;169struct170{171unsigned port, device, index, id;172int16_t result;173} cb_input_state_params;174struct175{176int index;177} cb_input_notify_params;178struct179{180int slot;181const char* hint;182//yuck183char result[MAX_PATH];184} cb_path_request_params;185struct186{187const char* memtype;188size_t amt;189void* result;190} cb_allocSharedMemory_params;191struct192{193void* ptr;194} cb_freeSharedMemory_params;195struct196{197const char* msg;198} cb_trace_params;199};200};201};202};203204static EmulationControl s_EmulationControl;205206class IPCRingBuffer207{208private:209HANDLE mmf;210u8* mmvaPtr;211volatile u8* begin;212volatile s32* head, *tail;213int bufsize;214215//this code depends on conventional interpretations of volatile and sequence points which are unlikely to be violated on any system an emulator would be running on216217void Setup(int size)218{219bool init = size != -1;220Owner = init;221222mmvaPtr = (u8*)MapViewOfFile(mmf, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);223224//setup management area225head = (s32*)mmvaPtr;226tail = (s32*)mmvaPtr + 1;227s32* bufsizeptr = (s32*)mmvaPtr + 2;228begin = mmvaPtr + 12;229230if (init)231*bufsizeptr = bufsize = size - 12;232else bufsize = *bufsizeptr;233}234235void WaitForWriteCapacity(int amt)236{237for (; ; )238{239//dont return when available == amt because then we would consume the buffer and be unable to distinguish between full and empty240if (Available() > amt)241return;242//this is a greedy spinlock.243SwitchToThread();244}245}246247int Size()248{249int h = *head;250int t = *tail;251int size = h - t;252if (size >= bufsize)253{254//shouldnt be possible for size to be anything but bufsize here255size = 0;256}257else if (size < 0) size += bufsize;258return size;259}260261int Available()262{263return bufsize - Size();264}265266public:267bool Owner;268269//void Allocate(int size) //not supported270271void Open(const std::string& id)272{273HANDLE h = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, id.c_str());274if(h == INVALID_HANDLE_VALUE)275return;276277mmf = h;278279Setup(-1);280}281282~IPCRingBuffer()283{284if (mmf == NULL) return;285CloseHandle(mmf);286mmf = NULL;287}288289int WaitForSomethingToRead()290{291for (; ; )292{293int available = Size();294if (available > 0)295return available;296//this is a greedy spinlock.297SwitchToThread();298//NOTE: it's annoying right now because libsnes processes die and eat a whole core.299//we need to gracefully exit somehow300}301}302303void Write(const void* ptr, int amt)304{305u8* bptr = (u8*)ptr;306int ofs = 0;307while (amt > 0)308{309int todo = amt;310311//make sure we don't write a big chunk beyond the end of the buffer312int remain = bufsize - *head;313if (todo > remain) todo = remain;314315//dont request the entire buffer. we would never get that much available, because we never completely fill up the buffer316if (todo > bufsize - 1) todo = bufsize - 1;317318//a super efficient approach would chunk this several times maybe instead of waiting for the buffer to be emptied before writing again. but who cares319WaitForWriteCapacity(todo);320321//messages are likely to be small. we should probably just loop to copy in here. but for now..322memcpy((u8*)begin + *head, bptr + ofs, todo);323324amt -= todo;325ofs += todo;326*head += todo;327if (*head >= bufsize) *head -= bufsize;328}329}330331void Read(void* ptr, int amt)332{333u8* bptr = (u8*)ptr;334int ofs = 0;335while (amt > 0)336{337int available = WaitForSomethingToRead();338int todo = amt;339if (todo > available) todo = available;340341//make sure we don't read a big chunk beyond the end of the buffer342int remain = bufsize - *tail;343if (todo > remain) todo = remain;344345//messages are likely to be small. we should probably just loop to copy in here. but for now..346memcpy(bptr + ofs, (u8*)begin + *tail, todo);347348amt -= todo;349ofs += todo;350*tail += todo;351if (*tail >= bufsize) *tail -= bufsize;352}353}354}; //class IPCRingBuffer355356static bool bufio = false;357static IPCRingBuffer *rbuf = NULL, *wbuf = NULL;358359HANDLE hPipe, hMapFile, hEvent;360void* hMapFilePtr;361static bool running = false;362363cothread_t co_control, co_emu, co_emu_suspended;364365#define SETCONTROL \366{ \367co_emu_suspended = co_active(); \368co_switch(co_control); \369}370371#define SETEMU \372{ \373cothread_t temp = co_emu_suspended; \374co_emu_suspended = NULL; \375co_switch(temp); \376}377378379void ReadPipeBuffer(void* buf, int len)380{381if(bufio)382{383rbuf->Read(buf,len);384return;385}386DWORD bytesRead;387BOOL result = ReadFile(hPipe, buf, len, &bytesRead, NULL);388if(!result || bytesRead != len)389exit(1);390}391392template<typename T> T ReadPipe()393{394T ret;395ReadPipeBuffer(&ret,sizeof(ret));396return ret;397}398399char* ReadPipeSharedPtr()400{401return (char*)hMapFilePtr + ReadPipe<int>();402}403404template<> bool ReadPipe<bool>()405{406return !!ReadPipe<char>();407}408409410void WritePipeBuffer(const void* buf, int len)411{412if(co_active() != co_control)413{414printf("WARNING: WRITING FROM NON-CONTROL THREAD\n");415}416//static FILE* outf = NULL;417//if(!outf) outf = fopen("c:\\trace.bin","wb"); fwrite(buf,1,len,outf); fflush(outf);418419if(bufio)420{421wbuf->Write(buf,len);422return;423}424425DWORD bytesWritten;426BOOL result = WriteFile(hPipe, buf, len, &bytesWritten, NULL);427if(!result || bytesWritten != len)428exit(1);429}430431//remove volatile qualifier...... crazy?432template<typename T> void WritePipe(volatile const T& val)433{434WritePipeBuffer((T*)&val, sizeof(val));435}436437template<typename T> void WritePipe(const T& val)438{439WritePipeBuffer(&val, sizeof(val));440}441442void WritePipeString(const char* str)443{444int len = strlen(str);445WritePipe(len);446WritePipeBuffer(str,len);447}448449std::string ReadPipeString()450{451int len = ReadPipe<int>();452std::string ret;453ret.resize(len);454if(len!=0)455ReadPipeBuffer(&ret[0],len);456return ret;457}458459typedef std::vector<char> Blob;460461void WritePipeBlob(void* buf, int len)462{463WritePipe(len);464WritePipeBuffer(buf,len);465}466467Blob ReadPipeBlob()468{469int len = ReadPipe<int>();470Blob ret;471ret.resize(len);472if(len!=0)473ReadPipeBuffer(&ret[0],len);474return ret;475}476477void snes_video_refresh(const uint32_t *data, unsigned width, unsigned height)478{479s_EmulationControl.exitReason = eEmulationExitReason_SIG;480s_EmulationControl.exitCallbackType = eEmulationCallback_snes_video_refresh;481s_EmulationControl.cb_video_refresh_params.data = data;482s_EmulationControl.cb_video_refresh_params.width = width;483s_EmulationControl.cb_video_refresh_params.height = height;484485SETCONTROL;486}487488bool audio_en = false;489static const int AUDIOBUFFER_SIZE = 44100*2;490uint16_t audiobuffer[AUDIOBUFFER_SIZE];491int audiobuffer_idx = 0;492493void SIG_FlushAudio()494{495s_EmulationControl.exitReason = eEmulationExitReason_SIG;496s_EmulationControl.exitCallbackType = eEmulationCallback_snes_audio_flush;497SETCONTROL;498}499500//this is the raw callback from the emulator internals when a new audio sample is available501void snes_audio_sample(uint16_t left, uint16_t right)502{503if(!audio_en) return;504505//if theres no room in the audio buffer, we need to send a flush signal506if(audiobuffer_idx == AUDIOBUFFER_SIZE)507SIG_FlushAudio();508509audiobuffer[audiobuffer_idx++] = left;510audiobuffer[audiobuffer_idx++] = right;511}512513void snes_input_poll(void)514{515s_EmulationControl.exitReason = eEmulationExitReason_SIG;516s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_poll;517SETCONTROL;518}519int16_t snes_input_state(unsigned port, unsigned device, unsigned index, unsigned id)520{521s_EmulationControl.exitReason = eEmulationExitReason_SIG;522s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_state;523s_EmulationControl.cb_input_state_params.port = port;524s_EmulationControl.cb_input_state_params.device = device;525s_EmulationControl.cb_input_state_params.index = index;526s_EmulationControl.cb_input_state_params.id = id;527SETCONTROL;528return s_EmulationControl.cb_input_state_params.result;529}530void snes_input_notify(int index)531{532s_EmulationControl.exitReason = eEmulationExitReason_SIG;533s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_notify;534s_EmulationControl.cb_input_notify_params.index = index;535SETCONTROL;536}537538void snes_trace(const char *msg)539{540s_EmulationControl.exitReason = eEmulationExitReason_SIG;541s_EmulationControl.exitCallbackType = eEmulationCallback_snes_trace;542s_EmulationControl.cb_trace_params.msg = msg;543SETCONTROL;544}545546const char* snes_path_request(int slot, const char* hint)547{548s_EmulationControl.exitReason = eEmulationExitReason_SIG;549s_EmulationControl.exitCallbackType = eEmulationCallback_snes_path_request;550s_EmulationControl.cb_path_request_params.slot = slot;551s_EmulationControl.cb_path_request_params.hint = hint;552SETCONTROL;553554return (const char*)s_EmulationControl.cb_path_request_params.result;555}556557void RunControlMessageLoop();558void snes_scanlineStart(int line)559{560s_EmulationControl.exitReason = eEmulationExitReason_BRK;561s_EmulationControl.hookExitType = eMessage_BRK_scanlineStart;562s_EmulationControl.cb_scanline_params.scanline = line;563SETCONTROL;564}565566class SharedMemoryBlock567{568public:569std::string memtype;570HANDLE handle;571};572573static std::map<void*,SharedMemoryBlock*> memHandleTable;574575void* implementation_snes_allocSharedMemory()576{577const char* memtype = s_EmulationControl.cb_allocSharedMemory_params.memtype;578size_t amt = s_EmulationControl.cb_allocSharedMemory_params.amt;579580if(!running)581{582s_EmulationControl.cb_allocSharedMemory_params.result = NULL;583return NULL;584}585586//printf("WritePipe(eMessage_SIG_allocSharedMemory)\n");587WritePipe(eMessage_SIG_allocSharedMemory);588WritePipeString(memtype);589WritePipe(amt);590591std::string blockname = ReadPipeString();592593auto mapfile = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, blockname.c_str());594595if(mapfile == INVALID_HANDLE_VALUE)596return NULL;597598auto ptr = MapViewOfFile(mapfile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);599600auto smb = new SharedMemoryBlock();601smb->memtype = memtype;602smb->handle = mapfile;603604memHandleTable[ptr] = smb;605606s_EmulationControl.cb_allocSharedMemory_params.result = ptr;607608return ptr;609}610611void* snes_allocSharedMemory(const char* memtype, size_t amt)612{613//its important that this happen before the message marshaling because allocation/free attempts can happen before the marshaling is setup (or at shutdown time, in case of errors?)614if(!running) return NULL;615616s_EmulationControl.exitReason = eEmulationExitReason_SIG;617s_EmulationControl.exitCallbackType = eEmulationCallback_snes_allocSharedMemory;618s_EmulationControl.cb_allocSharedMemory_params.memtype = memtype;619s_EmulationControl.cb_allocSharedMemory_params.amt = amt;620SETCONTROL;621622return s_EmulationControl.cb_allocSharedMemory_params.result;623}624625void implementation_snes_freeSharedMemory()626{627void* ptr = s_EmulationControl.cb_freeSharedMemory_params.ptr;628if(!ptr) return;629auto smb = memHandleTable.find(ptr)->second;630UnmapViewOfFile(ptr);631CloseHandle(smb->handle);632//printf("WritePipe(eMessage_SIG_freeSharedMemory);\n");633WritePipe(eMessage_SIG_freeSharedMemory);634WritePipeString(smb->memtype.c_str());635}636637void snes_freeSharedMemory(void* ptr)638{639//its important that this happen before the message marshaling because allocation/free attempts can happen before the marshaling is setup (or at shutdown time, in case of errors?)640if(!running) return;641642s_EmulationControl.exitReason = eEmulationExitReason_SIG;643s_EmulationControl.exitCallbackType = eEmulationCallback_snes_freeSharedMemory;644s_EmulationControl.cb_freeSharedMemory_params.ptr = ptr;645SETCONTROL;646}647648void InitBsnes()649{650//setup all hooks to forward messages to the frontend651snes_set_video_refresh(snes_video_refresh);652snes_set_audio_sample(snes_audio_sample);653snes_set_input_poll(snes_input_poll);654snes_set_input_state(snes_input_state);655snes_set_input_notify(snes_input_notify);656snes_set_path_request(snes_path_request);657658snes_set_allocSharedMemory(snes_allocSharedMemory);659snes_set_freeSharedMemory(snes_freeSharedMemory);660}661662663static void debug_op_exec(uint24 addr)664{665s_EmulationControl.exitReason = eEmulationExitReason_BRK;666s_EmulationControl.hookExitType = eMessage_BRK_hook_exec;667s_EmulationControl.hookAddr = (uint32)addr;668SETCONTROL;669//WritePipe(eMessage_snes_cb_hook_exec);670//WritePipe((uint32)addr);671}672673static void debug_op_read(uint24 addr)674{675s_EmulationControl.exitReason = eEmulationExitReason_BRK;676s_EmulationControl.hookExitType = eMessage_BRK_hook_read;677s_EmulationControl.hookAddr = (uint32)addr;678SETCONTROL;679//WritePipe(eMessage_snes_cb_hook_read);680//WritePipe((uint32)addr);681}682683static void debug_op_write(uint24 addr, uint8 value)684{685s_EmulationControl.exitReason = eEmulationExitReason_BRK;686s_EmulationControl.hookExitType = eMessage_BRK_hook_write;687s_EmulationControl.hookAddr = (uint32)addr;688s_EmulationControl.hookValue = value;689SETCONTROL;690//WritePipe(eMessage_snes_cb_hook_write);691//WritePipe((uint32)addr);692//WritePipe(value);693}694695static void debug_op_nmi()696{697WritePipe(eMessage_BRK_hook_nmi);698}699700static void debug_op_irq()701{702WritePipe(eMessage_BRK_hook_irq);703}704705void HandleMessage_QUERY(eMessage msg)706{707}708709bool Handle_QUERY(eMessage msg)710{711switch(msg)712{713default:714return false;715716case eMessage_QUERY_library_id:717WritePipeString(snes_library_id());718break;719case eMessage_QUERY_library_revision_major:720WritePipe(snes_library_revision_major());721break;722case eMessage_QUERY_library_revision_minor:723WritePipe(snes_library_revision_minor());724break;725726case eMessage_QUERY_get_region:727WritePipe((char)snes_get_region());728break;729730case eMessage_QUERY_get_mapper:731WritePipe(snes_get_mapper());732break;733734case eMessage_QUERY_get_memory_size:735WritePipe((u32)snes_get_memory_size(ReadPipe<u32>()));736break;737738case eMessage_QUERY_get_memory_data:739{740unsigned int id = ReadPipe<u32>();741char* dstbuf = ReadPipeSharedPtr();742uint8_t* srcbuf = snes_get_memory_data(id);743memcpy(dstbuf,srcbuf,snes_get_memory_size(id));744WritePipe(eMessage_BRK_Complete);745break;746}747748case eMessage_QUERY_peek:749{750int id = ReadPipe<s32>();751unsigned int addr = ReadPipe<u32>();752uint8_t ret;753if(id == SNES_MEMORY_SYSBUS)754ret = bus_read(addr);755else ret = snes_get_memory_data(id)[addr];756WritePipe(ret);757}758break;759760case eMessage_QUERY_poke:761{762int id = ReadPipe<s32>();763unsigned int addr = ReadPipe<u32>();764uint8_t val = ReadPipe<uint8_t>();765if(id == SNES_MEMORY_SYSBUS)766bus_write(addr,val);767else snes_get_memory_data(id)[addr] = val;768break;769}770break;771772case eMessage_QUERY_serialize_size:773WritePipe((u32)snes_serialize_size());774break;775case eMessage_QUERY_poll_message:776//TBD777WritePipe(-1);778break;779case eMessage_QUERY_dequeue_message:780//TBD781break;782783case eMessage_QUERY_set_color_lut:784{785auto blob = ReadPipeBlob();786snes_set_color_lut((uint32_t*)&blob[0]);787break;788}789break;790791case eMessage_QUERY_enable_trace:792if(!!ReadPipe<char>())793snes_set_trace_callback(snes_trace);794else snes_set_trace_callback(NULL);795break;796797case eMessage_QUERY_enable_scanline:798if(ReadPipe<bool>())799snes_set_scanlineStart(snes_scanlineStart);800else snes_set_scanlineStart(NULL);801break;802803case eMessage_QUERY_enable_audio:804audio_en = ReadPipe<bool>();805break;806807case eMessage_QUERY_set_layer_enable:808{809int layer = ReadPipe<s32>();810int priority = ReadPipe<s32>();811bool enable = ReadPipe<bool>();812snes_set_layer_enable(layer,priority,enable);813break;814}815816case eMessage_QUERY_set_backdropColor:817snes_set_backdropColor(ReadPipe<s32>());818break;819820case eMessage_QUERY_peek_logical_register:821WritePipe(snes_peek_logical_register(ReadPipe<s32>()));822break;823824case eMessage_QUERY_peek_cpu_regs:825{826//watch it! the size of this struct is important!827#ifdef _MSC_VER828#pragma pack(push,1)829#endif830struct {831u32 pc;832u16 a,x,y,z,s,d,vector; //7x833u8 p, nothing;834u32 aa,rd;835u8 sp, dp, db, mdr;836}837#ifndef _MSC_VER838__attribute__((__packed__))839#endif840cpuregs;841#ifdef _MSC_VER842#pragma pack(pop)843#endif844845cpuregs.pc = (u32)SNES::cpu.regs.pc;846cpuregs.a = SNES::cpu.regs.a;847cpuregs.x = SNES::cpu.regs.x;848cpuregs.y = SNES::cpu.regs.y;849cpuregs.z = SNES::cpu.regs.z;850cpuregs.s = SNES::cpu.regs.s;851cpuregs.d = SNES::cpu.regs.d;852cpuregs.aa = (u32)SNES::cpu.aa;853cpuregs.rd = (u32)SNES::cpu.rd;854cpuregs.sp = SNES::cpu.sp;855cpuregs.dp = SNES::cpu.dp;856cpuregs.db = SNES::cpu.regs.db;857cpuregs.mdr = SNES::cpu.regs.mdr;858cpuregs.vector = SNES::cpu.regs.vector;859cpuregs.p = SNES::cpu.regs.p;860cpuregs.nothing = 0;861862WritePipeBuffer(&cpuregs,32); //watch it! the size of this struct is important!863}864break;865866case eMessage_QUERY_GetMemoryIdName:867{868uint32 id = ReadPipe<uint32>();869const char* ret = snes_get_memory_id_name(id);870if(!ret) ret = "";871WritePipeString(ret);872break;873}874875case eMessage_QUERY_state_hook_exec:876SNES::cpu.debugger.op_exec = ReadPipe<bool>() ? debug_op_exec : hook<void (uint24)>();877break;878879case eMessage_QUERY_state_hook_read:880SNES::cpu.debugger.op_read = ReadPipe<bool>() ? debug_op_read : hook<void (uint24)>();881break;882883case eMessage_QUERY_state_hook_write:884SNES::cpu.debugger.op_write = ReadPipe<bool>() ? debug_op_write : hook<void (uint24, uint8)>();885break;886887case eMessage_QUERY_state_hook_nmi:888SNES::cpu.debugger.op_nmi = ReadPipe<bool>() ? debug_op_nmi : hook<void ()>();889break;890891case eMessage_QUERY_state_hook_irq:892SNES::cpu.debugger.op_irq = ReadPipe<bool>() ? debug_op_irq : hook<void ()>();893break;894895case eMessage_QUERY_set_cdl:896for (int i = 0; i<eCDLog_AddrType_NUM; i++)897{898cdlInfo.blocks[i] = ReadPipe<uint8_t*>();899cdlInfo.blockSizes[i] = ReadPipe<uint32_t>();900}901break;902903}904return true;905}906907bool Handle_CMD(eMessage msg)908{909if(msg == eMessage_ResumeAfterBRK)910{911//careful! dont switch back to co_emu, we were in another cothread probably when the BRK happened.912//i'm not sure its completely safe to be returning to co_emu below in the normal CMD handler, either...913co_switch(co_emu_suspended);914return true;915}916917if(msg<=eMessage_CMD_FIRST || msg>=eMessage_CMD_LAST) return false;918919s_EmulationControl.command = msg;920s_EmulationControl.exitReason = eEmulationExitReason_NotSet;921co_switch(co_emu);922return true;923}924925void Handle_SIG_audio_flush()926{927WritePipe(eMessage_SIG_audio_flush);928929int nsamples = audiobuffer_idx;930WritePipe(nsamples);931char* buf = ReadPipeSharedPtr();932memcpy(buf,audiobuffer,nsamples*2);933//extra just in case we had to unexpectedly flush audio and then carry on with some other process... yeah, its rickety.934WritePipe(0); //dummy synchronization935936//wait for frontend to consume data937938ReadPipe<int>(); //dummy synchronization939WritePipe(0); //dummy synchronization940audiobuffer_idx = 0;941}942943void MessageLoop()944{945for(;;)946{947TOP:948switch(s_EmulationControl.exitReason)949{950case eEmulationExitReason_NotSet:951goto HANDLEMESSAGES;952953case eEmulationExitReason_CMD_Complete:954//printf("eEmulationExitReason_CMD_Complete (command:%d)\n",s_EmulationControl.command);955//MessageBox(0,"ZING","ZING",MB_OK);956//printf("WRITING COMPLETE\n");957WritePipe(eMessage_BRK_Complete);958959//special post-completion messages (return values)960switch(s_EmulationControl.command)961{962case eMessage_CMD_load_cartridge_normal:963case eMessage_CMD_load_cartridge_super_game_boy:964case eMessage_CMD_serialize:965case eMessage_CMD_unserialize:966WritePipe(s_EmulationControl.cmd_result);967break;968}969970s_EmulationControl.exitReason = eEmulationExitReason_NotSet;971s_EmulationControl.command = eMessage_NotSet;972goto TOP;973974case eEmulationExitReason_SIG:975s_EmulationControl.exitReason = eEmulationExitReason_NotSet;976switch(s_EmulationControl.exitCallbackType)977{978case eEmulationCallback_snes_video_refresh:979{980WritePipe(eMessage_SIG_video_refresh);981WritePipe(s_EmulationControl.cb_video_refresh_params.width);982WritePipe(s_EmulationControl.cb_video_refresh_params.height);983int destOfs = ReadPipe<int>();984char* buf = (char*)hMapFilePtr + destOfs;985int bufsize = 512 * 480 * 4;986memcpy(buf,s_EmulationControl.cb_video_refresh_params.data,bufsize);987WritePipe((char)0); //dummy synchronization (alert frontend we're done with buffer)988break;989}990991case eEmulationCallback_snes_audio_flush:992Handle_SIG_audio_flush();993break;994case eEmulationCallback_snes_input_poll:995WritePipe(eMessage_SIG_input_poll);996break;997case eEmulationCallback_snes_input_state:998WritePipe(eMessage_SIG_input_state);999WritePipe(s_EmulationControl.cb_input_state_params.port);1000WritePipe(s_EmulationControl.cb_input_state_params.device);1001WritePipe(s_EmulationControl.cb_input_state_params.index);1002WritePipe(s_EmulationControl.cb_input_state_params.id);1003s_EmulationControl.cb_input_state_params.result = ReadPipe<int16_t>();1004break;1005case eEmulationCallback_snes_input_notify:1006WritePipe(eMessage_SIG_input_notify);1007WritePipe(s_EmulationControl.cb_input_notify_params.index);1008break;1009case eEmulationCallback_snes_path_request:1010{1011WritePipe(eMessage_SIG_path_request);1012WritePipe(s_EmulationControl.cb_path_request_params.slot);1013WritePipeString(s_EmulationControl.cb_path_request_params.hint);1014std::string temp = ReadPipeString();1015//yucky! use strncpy and ARRAY_SIZE or something!1016strcpy(s_EmulationControl.cb_path_request_params.result,temp.c_str());1017}1018break;1019case eEmulationCallback_snes_allocSharedMemory:1020implementation_snes_allocSharedMemory();1021break;1022case eEmulationCallback_snes_freeSharedMemory:1023implementation_snes_freeSharedMemory();1024break;1025case eEmulationCallback_snes_trace:1026WritePipe(eMessage_SIG_trace_callback);1027WritePipeString(s_EmulationControl.cb_trace_params.msg);1028break;1029}1030//when callbacks finish, we automatically resume emulation. be careful to go back to top!!!!!!!!1031SETEMU;1032goto TOP;10331034case eEmulationExitReason_BRK:1035s_EmulationControl.exitReason = eEmulationExitReason_NotSet;1036switch(s_EmulationControl.hookExitType)1037{1038case eMessage_BRK_scanlineStart:1039WritePipe(eMessage_BRK_scanlineStart);1040WritePipe((uint32)s_EmulationControl.hookAddr);1041break;1042case eMessage_BRK_hook_exec:1043WritePipe(eMessage_BRK_hook_exec);1044WritePipe((uint32)s_EmulationControl.hookAddr);1045break;1046case eMessage_BRK_hook_read:1047WritePipe(eMessage_BRK_hook_read);1048WritePipe((uint32)s_EmulationControl.hookAddr);1049break;1050case eMessage_BRK_hook_write:1051WritePipe(eMessage_BRK_hook_write);1052WritePipe((uint32)s_EmulationControl.hookAddr);1053WritePipe((uint8)s_EmulationControl.hookValue);1054break;1055}1056goto TOP;1057}10581059HANDLEMESSAGES:10601061//printf("Reading message from pipe...\n");1062eMessage msg = ReadPipe<eMessage>();1063//printf("...slam: %08X\n",msg);10641065if(Handle_QUERY(msg))1066goto TOP;1067if(Handle_CMD(msg))1068goto TOP;10691070switch(msg)1071{1072case eMessage_BRK_Complete:1073return;10741075case eMessage_Shutdown:1076//terminate this dll process1077return;10781079case eMessage_SetBuffer:1080{1081printf("eMessage_SetBuffer\n");1082int which = ReadPipe<s32>();1083std::string name = ReadPipeString();1084IPCRingBuffer* ipcrb = new IPCRingBuffer();1085ipcrb->Open(name);1086if(which==0) rbuf = ipcrb;1087else wbuf = ipcrb;1088break;1089}10901091case eMessage_BeginBufferIO:1092bufio = true;1093break;10941095case eMessage_EndBufferIO:1096bufio = false;1097break;10981099} //switch(msg)1100}1101}11021103static DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter)1104{1105MessageLoop();1106//send a message to the other thread to synchronize the shutdown of this thread1107//after that message is received, this thread (and the whole dll instance) is dead.1108WritePipe(eMessage_BRK_Complete);1109return 0;1110}111111121113void OpenConsole()1114{1115AllocConsole();1116freopen("CONOUT$", "w", stdout);1117freopen("CONOUT$", "w", stderr);1118freopen("CONIN$", "r", stdin);1119}11201121void EMUTHREAD_handleCommand_LoadCartridgeNormal()1122{1123Blob xml = ReadPipeBlob();1124xml.push_back(0); //make sure the xml is null terminated1125const char* xmlptr = NULL;1126if(xml.size() != 1) xmlptr = &xml[0];11271128Blob rom_data = ReadPipeBlob();1129const unsigned char* rom_ptr = NULL;1130if(rom_data.size() != 0) rom_ptr = (unsigned char*)&rom_data[0];11311132bool ret = snes_load_cartridge_normal(xmlptr,rom_ptr,rom_data.size());1133s_EmulationControl.cmd_result = ret?1:0;1134}11351136void EMUTHREAD_handleCommand_LoadCartridgeSuperGameBoy()1137{1138std::string rom_xml = ReadPipeString();1139const char* rom_xmlptr = NULL;1140if(rom_xml != "") rom_xmlptr = rom_xml.c_str();1141Blob rom_data = ReadPipeBlob();1142uint32 rom_length = rom_data.size();11431144std::string dmg_xml = ReadPipeString();1145const char* dmg_xmlptr = NULL;1146if(dmg_xml != "") dmg_xmlptr = dmg_xml.c_str();1147Blob dmg_data = ReadPipeBlob();1148uint32 dmg_length = dmg_data.size();11491150bool ret = snes_load_cartridge_super_game_boy(rom_xmlptr,(uint8*)&rom_data[0],rom_length, dmg_xmlptr,(uint8*)&dmg_data[0], dmg_length);1151s_EmulationControl.cmd_result = ret?1:0;1152}11531154void EMUTHREAD_handle_CMD_serialize()1155{1156int size = ReadPipe<s32>();1157int destOfs = ReadPipe<s32>();1158char* buf = (char*)hMapFilePtr + destOfs;1159bool ret = snes_serialize((uint8_t*)buf,size);1160s_EmulationControl.cmd_result = ret?1:0;1161}11621163void EMUTHREAD_handle_CMD_unserialize()1164{1165int size = ReadPipe<s32>();1166int destOfs = ReadPipe<s32>();1167char* buf = (char*)hMapFilePtr + destOfs;1168bool ret = snes_unserialize((uint8_t*)buf ,size);1169s_EmulationControl.cmd_result = ret?1:0;1170}11711172void emuthread()1173{1174for(;;)1175{1176switch(s_EmulationControl.command)1177{1178case eMessage_CMD_init:1179snes_init();1180break;1181case eMessage_CMD_power:1182snes_power();1183break;1184case eMessage_CMD_reset:1185snes_reset();1186break;1187case eMessage_CMD_term:1188snes_term();1189break;1190case eMessage_CMD_unload_cartridge:1191snes_unload_cartridge();1192break;1193case eMessage_CMD_load_cartridge_normal:1194EMUTHREAD_handleCommand_LoadCartridgeNormal();1195break;1196case eMessage_CMD_load_cartridge_super_game_boy:1197EMUTHREAD_handleCommand_LoadCartridgeSuperGameBoy();1198break;11991200case eMessage_CMD_serialize:1201EMUTHREAD_handle_CMD_serialize();1202break;1203case eMessage_CMD_unserialize:1204EMUTHREAD_handle_CMD_unserialize();1205break;12061207case eMessage_CMD_run:1208SIG_FlushAudio();12091210//we could avoid this if we saved the current thread before jumping back to co_control, instead of always jumping back to co_emu1211//in effect, we're scrambling the scheduler1212//EDIT - well, we changed that, but.. we still want this probably, for debugging and stuff1213for(;;)1214{1215SNES::scheduler.sync = SNES::Scheduler::SynchronizeMode::None;1216SNES::scheduler.clearExitReason();1217SNES::scheduler.enter();1218if(SNES::scheduler.exit_reason() == SNES::Scheduler::ExitReason::FrameEvent)1219{1220SNES::video.update();1221break;1222}1223//not used yet1224if(SNES::scheduler.exit_reason() == SNES::Scheduler::ExitReason::DebuggerEvent)1225break;1226}12271228SIG_FlushAudio();1229break;1230}12311232s_EmulationControl.exitReason = eEmulationExitReason_CMD_Complete;1233SETCONTROL;1234}1235}12361237BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved)1238{1239return TRUE;1240}12411242extern "C" dllexport bool __cdecl DllInit(const char* ipcname)1243{1244printf("NEW INSTANCE: %08X\n", &s_EmulationControl);12451246char pipename[256];1247char eventname[256];1248sprintf(pipename, "\\\\.\\Pipe\\%s",ipcname);1249sprintf(eventname, "%s-event", ipcname);12501251printf("pipe: %s\n",pipename);1252printf("event: %s\n",eventname);12531254hPipe = CreateFile(pipename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);12551256if(hPipe == INVALID_HANDLE_VALUE)1257return false;12581259hMapFile = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, ipcname);1260if(hMapFile == INVALID_HANDLE_VALUE)1261return false;12621263hMapFilePtr = MapViewOfFile(hMapFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);12641265//make a coroutine thread to run the emulation in. we'll switch back to this thread when communicating with the frontend1266co_control = co_active();1267co_emu = co_create(65536*sizeof(void*),emuthread);12681269running = true;1270printf("running\n");12711272DWORD tid;1273CreateThread(nullptr, 0, &ThreadProc, nullptr, 0, &tid);12741275return true;1276}127712781279void pwrap_init()1280{1281//bsnes's interface initialization calls into this after initializing itself, so we can get a chance to mod it for pwrap functionalities1282InitBsnes();1283}12841285