Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp
2 views
1
//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?)
2
// we cant just check the state of a socket or named pipe or whatever in the case of IPCRingBuffer
3
4
//TODO - clean up signaling namespaces.. hooks vs callbacks vs etc. (unify them, at least)
5
//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)
6
//also a COMMAND concept (comes from frontend.. run, init, etc.)
7
//
8
//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)
9
//
10
//TODO - factor out modular components (ringbuffer and the like)
11
12
//types of messages:
13
//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 BRK
14
//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)
15
//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 core
16
//brk: core->frontend: "core break" the emulation process has suspended. the frontend is free to do whatever it wishes.
17
//(and there are other assorted special messages...)
18
19
20
#include <Windows.h>
21
22
#define LIBSNES_IMPORT
23
#include "snes/snes.hpp"
24
#include "libsnes.hpp"
25
26
#include <libco/libco.h>
27
28
#include <string.h>
29
#include <stdio.h>
30
#include <stdlib.h>
31
32
#include <map>
33
#include <string>
34
#include <vector>
35
36
extern SNES::Interface *iface;
37
38
typedef uint8 u8;
39
typedef int32 s32;
40
typedef uint32 u32;
41
typedef uint16 u16;
42
43
enum eMessage : int32
44
{
45
eMessage_NotSet,
46
47
eMessage_SetBuffer,
48
eMessage_BeginBufferIO,
49
eMessage_EndBufferIO,
50
eMessage_ResumeAfterBRK, //resumes execution of the core, after a BRK. no change to current CMD
51
eMessage_Shutdown,
52
53
eMessage_QUERY_library_id,
54
eMessage_QUERY_library_revision_major,
55
eMessage_QUERY_library_revision_minor,
56
eMessage_QUERY_get_region,
57
eMessage_QUERY_get_mapper,
58
eMessage_QUERY_get_memory_size,
59
eMessage_QUERY_get_memory_data, //note: this function isnt used and hasnt been tested in a while
60
eMessage_QUERY_peek,
61
eMessage_QUERY_poke,
62
eMessage_QUERY_serialize_size,
63
eMessage_QUERY_poll_message,
64
eMessage_QUERY_dequeue_message,
65
eMessage_QUERY_set_color_lut,
66
eMessage_QUERY_GetMemoryIdName,
67
eMessage_QUERY_state_hook_exec,
68
eMessage_QUERY_state_hook_read,
69
eMessage_QUERY_state_hook_write,
70
eMessage_QUERY_state_hook_nmi,
71
eMessage_QUERY_state_hook_irq,
72
eMessage_QUERY_enable_trace,
73
eMessage_QUERY_enable_scanline,
74
eMessage_QUERY_enable_audio,
75
eMessage_QUERY_set_layer_enable,
76
eMessage_QUERY_set_backdropColor,
77
eMessage_QUERY_peek_logical_register,
78
eMessage_QUERY_peek_cpu_regs,
79
eMessage_QUERY_set_cdl,
80
81
eMessage_CMD_FIRST,
82
eMessage_CMD_init,
83
eMessage_CMD_power,
84
eMessage_CMD_reset,
85
eMessage_CMD_run,
86
eMessage_CMD_serialize,
87
eMessage_CMD_unserialize,
88
eMessage_CMD_load_cartridge_normal,
89
eMessage_CMD_load_cartridge_super_game_boy,
90
eMessage_CMD_term,
91
eMessage_CMD_unload_cartridge,
92
eMessage_CMD_LAST,
93
94
eMessage_SIG_video_refresh,
95
eMessage_SIG_input_poll,
96
eMessage_SIG_input_state,
97
eMessage_SIG_input_notify,
98
eMessage_SIG_audio_flush,
99
eMessage_SIG_path_request,
100
eMessage_SIG_trace_callback,
101
eMessage_SIG_allocSharedMemory, //?
102
eMessage_SIG_freeSharedMemory, //?
103
104
eMessage_BRK_Complete,
105
eMessage_BRK_hook_exec,
106
eMessage_BRK_hook_read,
107
eMessage_BRK_hook_write,
108
eMessage_BRK_hook_nmi,
109
eMessage_BRK_hook_irq,
110
eMessage_BRK_scanlineStart, //implemented as a BRK because that's really what it is, its just a graphical event and not a CPU event
111
};
112
113
114
enum eEmulationExitReason
115
{
116
eEmulationExitReason_NotSet,
117
eEmulationExitReason_BRK,
118
eEmulationExitReason_SIG,
119
eEmulationExitReason_CMD_Complete,
120
};
121
122
enum eEmulationCallback
123
{
124
eEmulationCallback_snes_video_refresh,
125
eEmulationCallback_snes_audio_flush,
126
eEmulationCallback_snes_scanline,
127
eEmulationCallback_snes_input_poll,
128
eEmulationCallback_snes_input_state,
129
eEmulationCallback_snes_input_notify,
130
eEmulationCallback_snes_path_request,
131
132
eEmulationCallback_snes_allocSharedMemory,
133
eEmulationCallback_snes_freeSharedMemory,
134
135
eEmulationCallback_snes_trace
136
};
137
138
struct EmulationControl
139
{
140
volatile eMessage command;
141
volatile eEmulationExitReason exitReason;
142
143
//the result code for a CMD
144
s32 cmd_result;
145
146
union
147
{
148
struct
149
{
150
volatile eMessage hookExitType;
151
uint32 hookAddr;
152
uint8 hookValue;
153
};
154
155
struct
156
{
157
volatile eEmulationCallback exitCallbackType;
158
union
159
{
160
struct
161
{
162
const uint32_t *data;
163
unsigned width;
164
unsigned height;
165
} cb_video_refresh_params;
166
struct
167
{
168
int32_t scanline;
169
} cb_scanline_params;
170
struct
171
{
172
unsigned port, device, index, id;
173
int16_t result;
174
} cb_input_state_params;
175
struct
176
{
177
int index;
178
} cb_input_notify_params;
179
struct
180
{
181
int slot;
182
const char* hint;
183
//yuck
184
char result[MAX_PATH];
185
} cb_path_request_params;
186
struct
187
{
188
const char* memtype;
189
size_t amt;
190
void* result;
191
} cb_allocSharedMemory_params;
192
struct
193
{
194
void* ptr;
195
} cb_freeSharedMemory_params;
196
struct
197
{
198
const char* msg;
199
} cb_trace_params;
200
};
201
};
202
};
203
};
204
205
static EmulationControl s_EmulationControl;
206
207
class IPCRingBuffer
208
{
209
private:
210
HANDLE mmf;
211
u8* mmvaPtr;
212
volatile u8* begin;
213
volatile s32* head, *tail;
214
int bufsize;
215
216
//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 on
217
218
void Setup(int size)
219
{
220
bool init = size != -1;
221
Owner = init;
222
223
mmvaPtr = (u8*)MapViewOfFile(mmf, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
224
225
//setup management area
226
head = (s32*)mmvaPtr;
227
tail = (s32*)mmvaPtr + 1;
228
s32* bufsizeptr = (s32*)mmvaPtr + 2;
229
begin = mmvaPtr + 12;
230
231
if (init)
232
*bufsizeptr = bufsize = size - 12;
233
else bufsize = *bufsizeptr;
234
}
235
236
void WaitForWriteCapacity(int amt)
237
{
238
for (; ; )
239
{
240
//dont return when available == amt because then we would consume the buffer and be unable to distinguish between full and empty
241
if (Available() > amt)
242
return;
243
//this is a greedy spinlock.
244
SwitchToThread();
245
}
246
}
247
248
int Size()
249
{
250
int h = *head;
251
int t = *tail;
252
int size = h - t;
253
if (size >= bufsize)
254
{
255
//shouldnt be possible for size to be anything but bufsize here
256
size = 0;
257
}
258
else if (size < 0) size += bufsize;
259
return size;
260
}
261
262
int Available()
263
{
264
return bufsize - Size();
265
}
266
267
public:
268
bool Owner;
269
270
//void Allocate(int size) //not supported
271
272
void Open(const std::string& id)
273
{
274
HANDLE h = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, id.c_str());
275
if(h == INVALID_HANDLE_VALUE)
276
return;
277
278
mmf = h;
279
280
Setup(-1);
281
}
282
283
~IPCRingBuffer()
284
{
285
if (mmf == NULL) return;
286
CloseHandle(mmf);
287
mmf = NULL;
288
}
289
290
int WaitForSomethingToRead()
291
{
292
for (; ; )
293
{
294
int available = Size();
295
if (available > 0)
296
return available;
297
//this is a greedy spinlock.
298
SwitchToThread();
299
//NOTE: it's annoying right now because libsnes processes die and eat a whole core.
300
//we need to gracefully exit somehow
301
}
302
}
303
304
void Write(const void* ptr, int amt)
305
{
306
u8* bptr = (u8*)ptr;
307
int ofs = 0;
308
while (amt > 0)
309
{
310
int todo = amt;
311
312
//make sure we don't write a big chunk beyond the end of the buffer
313
int remain = bufsize - *head;
314
if (todo > remain) todo = remain;
315
316
//dont request the entire buffer. we would never get that much available, because we never completely fill up the buffer
317
if (todo > bufsize - 1) todo = bufsize - 1;
318
319
//a super efficient approach would chunk this several times maybe instead of waiting for the buffer to be emptied before writing again. but who cares
320
WaitForWriteCapacity(todo);
321
322
//messages are likely to be small. we should probably just loop to copy in here. but for now..
323
memcpy((u8*)begin + *head, bptr + ofs, todo);
324
325
amt -= todo;
326
ofs += todo;
327
*head += todo;
328
if (*head >= bufsize) *head -= bufsize;
329
}
330
}
331
332
void Read(void* ptr, int amt)
333
{
334
u8* bptr = (u8*)ptr;
335
int ofs = 0;
336
while (amt > 0)
337
{
338
int available = WaitForSomethingToRead();
339
int todo = amt;
340
if (todo > available) todo = available;
341
342
//make sure we don't read a big chunk beyond the end of the buffer
343
int remain = bufsize - *tail;
344
if (todo > remain) todo = remain;
345
346
//messages are likely to be small. we should probably just loop to copy in here. but for now..
347
memcpy(bptr + ofs, (u8*)begin + *tail, todo);
348
349
amt -= todo;
350
ofs += todo;
351
*tail += todo;
352
if (*tail >= bufsize) *tail -= bufsize;
353
}
354
}
355
}; //class IPCRingBuffer
356
357
static bool bufio = false;
358
static IPCRingBuffer *rbuf = NULL, *wbuf = NULL;
359
360
HANDLE hPipe, hMapFile, hEvent;
361
void* hMapFilePtr;
362
static bool running = false;
363
364
cothread_t co_control, co_emu, co_emu_suspended;
365
366
#define SETCONTROL \
367
{ \
368
co_emu_suspended = co_active(); \
369
co_switch(co_control); \
370
}
371
372
#define SETEMU \
373
{ \
374
cothread_t temp = co_emu_suspended; \
375
co_emu_suspended = NULL; \
376
co_switch(temp); \
377
}
378
379
380
void ReadPipeBuffer(void* buf, int len)
381
{
382
if(bufio)
383
{
384
rbuf->Read(buf,len);
385
return;
386
}
387
DWORD bytesRead;
388
BOOL result = ReadFile(hPipe, buf, len, &bytesRead, NULL);
389
if(!result || bytesRead != len)
390
exit(1);
391
}
392
393
template<typename T> T ReadPipe()
394
{
395
T ret;
396
ReadPipeBuffer(&ret,sizeof(ret));
397
return ret;
398
}
399
400
char* ReadPipeSharedPtr()
401
{
402
return (char*)hMapFilePtr + ReadPipe<int>();
403
}
404
405
template<> bool ReadPipe<bool>()
406
{
407
return !!ReadPipe<char>();
408
}
409
410
411
void WritePipeBuffer(const void* buf, int len)
412
{
413
if(co_active() != co_control)
414
{
415
printf("WARNING: WRITING FROM NON-CONTROL THREAD\n");
416
}
417
//static FILE* outf = NULL;
418
//if(!outf) outf = fopen("c:\\trace.bin","wb"); fwrite(buf,1,len,outf); fflush(outf);
419
420
if(bufio)
421
{
422
wbuf->Write(buf,len);
423
return;
424
}
425
426
DWORD bytesWritten;
427
BOOL result = WriteFile(hPipe, buf, len, &bytesWritten, NULL);
428
if(!result || bytesWritten != len)
429
exit(1);
430
}
431
432
//remove volatile qualifier...... crazy?
433
template<typename T> void WritePipe(volatile const T& val)
434
{
435
WritePipeBuffer((T*)&val, sizeof(val));
436
}
437
438
template<typename T> void WritePipe(const T& val)
439
{
440
WritePipeBuffer(&val, sizeof(val));
441
}
442
443
void WritePipeString(const char* str)
444
{
445
int len = strlen(str);
446
WritePipe(len);
447
WritePipeBuffer(str,len);
448
}
449
450
std::string ReadPipeString()
451
{
452
int len = ReadPipe<int>();
453
std::string ret;
454
ret.resize(len);
455
if(len!=0)
456
ReadPipeBuffer(&ret[0],len);
457
return ret;
458
}
459
460
typedef std::vector<char> Blob;
461
462
void WritePipeBlob(void* buf, int len)
463
{
464
WritePipe(len);
465
WritePipeBuffer(buf,len);
466
}
467
468
Blob ReadPipeBlob()
469
{
470
int len = ReadPipe<int>();
471
Blob ret;
472
ret.resize(len);
473
if(len!=0)
474
ReadPipeBuffer(&ret[0],len);
475
return ret;
476
}
477
478
void snes_video_refresh(const uint32_t *data, unsigned width, unsigned height)
479
{
480
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
481
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_video_refresh;
482
s_EmulationControl.cb_video_refresh_params.data = data;
483
s_EmulationControl.cb_video_refresh_params.width = width;
484
s_EmulationControl.cb_video_refresh_params.height = height;
485
486
SETCONTROL;
487
}
488
489
bool audio_en = false;
490
static const int AUDIOBUFFER_SIZE = 44100*2;
491
uint16_t audiobuffer[AUDIOBUFFER_SIZE];
492
int audiobuffer_idx = 0;
493
494
void SIG_FlushAudio()
495
{
496
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
497
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_audio_flush;
498
SETCONTROL;
499
}
500
501
//this is the raw callback from the emulator internals when a new audio sample is available
502
void snes_audio_sample(uint16_t left, uint16_t right)
503
{
504
if(!audio_en) return;
505
506
//if theres no room in the audio buffer, we need to send a flush signal
507
if(audiobuffer_idx == AUDIOBUFFER_SIZE)
508
SIG_FlushAudio();
509
510
audiobuffer[audiobuffer_idx++] = left;
511
audiobuffer[audiobuffer_idx++] = right;
512
}
513
514
void snes_input_poll(void)
515
{
516
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
517
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_poll;
518
SETCONTROL;
519
}
520
int16_t snes_input_state(unsigned port, unsigned device, unsigned index, unsigned id)
521
{
522
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
523
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_state;
524
s_EmulationControl.cb_input_state_params.port = port;
525
s_EmulationControl.cb_input_state_params.device = device;
526
s_EmulationControl.cb_input_state_params.index = index;
527
s_EmulationControl.cb_input_state_params.id = id;
528
SETCONTROL;
529
return s_EmulationControl.cb_input_state_params.result;
530
}
531
void snes_input_notify(int index)
532
{
533
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
534
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_notify;
535
s_EmulationControl.cb_input_notify_params.index = index;
536
SETCONTROL;
537
}
538
539
void snes_trace(const char *msg)
540
{
541
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
542
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_trace;
543
s_EmulationControl.cb_trace_params.msg = msg;
544
SETCONTROL;
545
}
546
547
const char* snes_path_request(int slot, const char* hint)
548
{
549
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
550
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_path_request;
551
s_EmulationControl.cb_path_request_params.slot = slot;
552
s_EmulationControl.cb_path_request_params.hint = hint;
553
SETCONTROL;
554
555
return (const char*)s_EmulationControl.cb_path_request_params.result;
556
}
557
558
void RunControlMessageLoop();
559
void snes_scanlineStart(int line)
560
{
561
s_EmulationControl.exitReason = eEmulationExitReason_BRK;
562
s_EmulationControl.hookExitType = eMessage_BRK_scanlineStart;
563
s_EmulationControl.cb_scanline_params.scanline = line;
564
SETCONTROL;
565
}
566
567
class SharedMemoryBlock
568
{
569
public:
570
std::string memtype;
571
HANDLE handle;
572
};
573
574
static std::map<void*,SharedMemoryBlock*> memHandleTable;
575
576
void* implementation_snes_allocSharedMemory()
577
{
578
const char* memtype = s_EmulationControl.cb_allocSharedMemory_params.memtype;
579
size_t amt = s_EmulationControl.cb_allocSharedMemory_params.amt;
580
581
if(!running)
582
{
583
s_EmulationControl.cb_allocSharedMemory_params.result = NULL;
584
return NULL;
585
}
586
587
//printf("WritePipe(eMessage_SIG_allocSharedMemory)\n");
588
WritePipe(eMessage_SIG_allocSharedMemory);
589
WritePipeString(memtype);
590
WritePipe(amt);
591
592
std::string blockname = ReadPipeString();
593
594
auto mapfile = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, blockname.c_str());
595
596
if(mapfile == INVALID_HANDLE_VALUE)
597
return NULL;
598
599
auto ptr = MapViewOfFile(mapfile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
600
601
auto smb = new SharedMemoryBlock();
602
smb->memtype = memtype;
603
smb->handle = mapfile;
604
605
memHandleTable[ptr] = smb;
606
607
s_EmulationControl.cb_allocSharedMemory_params.result = ptr;
608
609
return ptr;
610
}
611
612
void* snes_allocSharedMemory(const char* memtype, size_t amt)
613
{
614
//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?)
615
if(!running) return NULL;
616
617
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
618
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_allocSharedMemory;
619
s_EmulationControl.cb_allocSharedMemory_params.memtype = memtype;
620
s_EmulationControl.cb_allocSharedMemory_params.amt = amt;
621
SETCONTROL;
622
623
return s_EmulationControl.cb_allocSharedMemory_params.result;
624
}
625
626
void implementation_snes_freeSharedMemory()
627
{
628
void* ptr = s_EmulationControl.cb_freeSharedMemory_params.ptr;
629
if(!ptr) return;
630
auto smb = memHandleTable.find(ptr)->second;
631
UnmapViewOfFile(ptr);
632
CloseHandle(smb->handle);
633
//printf("WritePipe(eMessage_SIG_freeSharedMemory);\n");
634
WritePipe(eMessage_SIG_freeSharedMemory);
635
WritePipeString(smb->memtype.c_str());
636
}
637
638
void snes_freeSharedMemory(void* ptr)
639
{
640
//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?)
641
if(!running) return;
642
643
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
644
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_freeSharedMemory;
645
s_EmulationControl.cb_freeSharedMemory_params.ptr = ptr;
646
SETCONTROL;
647
}
648
649
void InitBsnes()
650
{
651
//setup all hooks to forward messages to the frontend
652
snes_set_video_refresh(snes_video_refresh);
653
snes_set_audio_sample(snes_audio_sample);
654
snes_set_input_poll(snes_input_poll);
655
snes_set_input_state(snes_input_state);
656
snes_set_input_notify(snes_input_notify);
657
snes_set_path_request(snes_path_request);
658
659
snes_set_allocSharedMemory(snes_allocSharedMemory);
660
snes_set_freeSharedMemory(snes_freeSharedMemory);
661
}
662
663
664
static void debug_op_exec(uint24 addr)
665
{
666
s_EmulationControl.exitReason = eEmulationExitReason_BRK;
667
s_EmulationControl.hookExitType = eMessage_BRK_hook_exec;
668
s_EmulationControl.hookAddr = (uint32)addr;
669
SETCONTROL;
670
//WritePipe(eMessage_snes_cb_hook_exec);
671
//WritePipe((uint32)addr);
672
}
673
674
static void debug_op_read(uint24 addr)
675
{
676
s_EmulationControl.exitReason = eEmulationExitReason_BRK;
677
s_EmulationControl.hookExitType = eMessage_BRK_hook_read;
678
s_EmulationControl.hookAddr = (uint32)addr;
679
SETCONTROL;
680
//WritePipe(eMessage_snes_cb_hook_read);
681
//WritePipe((uint32)addr);
682
}
683
684
static void debug_op_write(uint24 addr, uint8 value)
685
{
686
s_EmulationControl.exitReason = eEmulationExitReason_BRK;
687
s_EmulationControl.hookExitType = eMessage_BRK_hook_write;
688
s_EmulationControl.hookAddr = (uint32)addr;
689
s_EmulationControl.hookValue = value;
690
SETCONTROL;
691
//WritePipe(eMessage_snes_cb_hook_write);
692
//WritePipe((uint32)addr);
693
//WritePipe(value);
694
}
695
696
static void debug_op_nmi()
697
{
698
WritePipe(eMessage_BRK_hook_nmi);
699
}
700
701
static void debug_op_irq()
702
{
703
WritePipe(eMessage_BRK_hook_irq);
704
}
705
706
void HandleMessage_QUERY(eMessage msg)
707
{
708
}
709
710
bool Handle_QUERY(eMessage msg)
711
{
712
switch(msg)
713
{
714
default:
715
return false;
716
717
case eMessage_QUERY_library_id:
718
WritePipeString(snes_library_id());
719
break;
720
case eMessage_QUERY_library_revision_major:
721
WritePipe(snes_library_revision_major());
722
break;
723
case eMessage_QUERY_library_revision_minor:
724
WritePipe(snes_library_revision_minor());
725
break;
726
727
case eMessage_QUERY_get_region:
728
WritePipe((char)snes_get_region());
729
break;
730
731
case eMessage_QUERY_get_mapper:
732
WritePipe(snes_get_mapper());
733
break;
734
735
case eMessage_QUERY_get_memory_size:
736
WritePipe((u32)snes_get_memory_size(ReadPipe<u32>()));
737
break;
738
739
case eMessage_QUERY_get_memory_data:
740
{
741
unsigned int id = ReadPipe<u32>();
742
char* dstbuf = ReadPipeSharedPtr();
743
uint8_t* srcbuf = snes_get_memory_data(id);
744
memcpy(dstbuf,srcbuf,snes_get_memory_size(id));
745
WritePipe(eMessage_BRK_Complete);
746
break;
747
}
748
749
case eMessage_QUERY_peek:
750
{
751
int id = ReadPipe<s32>();
752
unsigned int addr = ReadPipe<u32>();
753
uint8_t ret;
754
if(id == SNES_MEMORY_SYSBUS)
755
ret = bus_read(addr);
756
else ret = snes_get_memory_data(id)[addr];
757
WritePipe(ret);
758
}
759
break;
760
761
case eMessage_QUERY_poke:
762
{
763
int id = ReadPipe<s32>();
764
unsigned int addr = ReadPipe<u32>();
765
uint8_t val = ReadPipe<uint8_t>();
766
if(id == SNES_MEMORY_SYSBUS)
767
bus_write(addr,val);
768
else snes_get_memory_data(id)[addr] = val;
769
break;
770
}
771
break;
772
773
case eMessage_QUERY_serialize_size:
774
WritePipe((u32)snes_serialize_size());
775
break;
776
case eMessage_QUERY_poll_message:
777
//TBD
778
WritePipe(-1);
779
break;
780
case eMessage_QUERY_dequeue_message:
781
//TBD
782
break;
783
784
case eMessage_QUERY_set_color_lut:
785
{
786
auto blob = ReadPipeBlob();
787
snes_set_color_lut((uint32_t*)&blob[0]);
788
break;
789
}
790
break;
791
792
case eMessage_QUERY_enable_trace:
793
if(!!ReadPipe<char>())
794
snes_set_trace_callback(snes_trace);
795
else snes_set_trace_callback(NULL);
796
break;
797
798
case eMessage_QUERY_enable_scanline:
799
if(ReadPipe<bool>())
800
snes_set_scanlineStart(snes_scanlineStart);
801
else snes_set_scanlineStart(NULL);
802
break;
803
804
case eMessage_QUERY_enable_audio:
805
audio_en = ReadPipe<bool>();
806
break;
807
808
case eMessage_QUERY_set_layer_enable:
809
{
810
int layer = ReadPipe<s32>();
811
int priority = ReadPipe<s32>();
812
bool enable = ReadPipe<bool>();
813
snes_set_layer_enable(layer,priority,enable);
814
break;
815
}
816
817
case eMessage_QUERY_set_backdropColor:
818
snes_set_backdropColor(ReadPipe<s32>());
819
break;
820
821
case eMessage_QUERY_peek_logical_register:
822
WritePipe(snes_peek_logical_register(ReadPipe<s32>()));
823
break;
824
825
case eMessage_QUERY_peek_cpu_regs:
826
{
827
//watch it! the size of this struct is important!
828
#ifdef _MSC_VER
829
#pragma pack(push,1)
830
#endif
831
struct {
832
u32 pc;
833
u16 a,x,y,z,s,d,vector; //7x
834
u8 p, nothing;
835
u32 aa,rd;
836
u8 sp, dp, db, mdr;
837
}
838
#ifndef _MSC_VER
839
__attribute__((__packed__))
840
#endif
841
cpuregs;
842
#ifdef _MSC_VER
843
#pragma pack(pop)
844
#endif
845
846
cpuregs.pc = (u32)SNES::cpu.regs.pc;
847
cpuregs.a = SNES::cpu.regs.a;
848
cpuregs.x = SNES::cpu.regs.x;
849
cpuregs.y = SNES::cpu.regs.y;
850
cpuregs.z = SNES::cpu.regs.z;
851
cpuregs.s = SNES::cpu.regs.s;
852
cpuregs.d = SNES::cpu.regs.d;
853
cpuregs.aa = (u32)SNES::cpu.aa;
854
cpuregs.rd = (u32)SNES::cpu.rd;
855
cpuregs.sp = SNES::cpu.sp;
856
cpuregs.dp = SNES::cpu.dp;
857
cpuregs.db = SNES::cpu.regs.db;
858
cpuregs.mdr = SNES::cpu.regs.mdr;
859
cpuregs.vector = SNES::cpu.regs.vector;
860
cpuregs.p = SNES::cpu.regs.p;
861
cpuregs.nothing = 0;
862
863
WritePipeBuffer(&cpuregs,32); //watch it! the size of this struct is important!
864
}
865
break;
866
867
case eMessage_QUERY_GetMemoryIdName:
868
{
869
uint32 id = ReadPipe<uint32>();
870
const char* ret = snes_get_memory_id_name(id);
871
if(!ret) ret = "";
872
WritePipeString(ret);
873
break;
874
}
875
876
case eMessage_QUERY_state_hook_exec:
877
SNES::cpu.debugger.op_exec = ReadPipe<bool>() ? debug_op_exec : hook<void (uint24)>();
878
break;
879
880
case eMessage_QUERY_state_hook_read:
881
SNES::cpu.debugger.op_read = ReadPipe<bool>() ? debug_op_read : hook<void (uint24)>();
882
break;
883
884
case eMessage_QUERY_state_hook_write:
885
SNES::cpu.debugger.op_write = ReadPipe<bool>() ? debug_op_write : hook<void (uint24, uint8)>();
886
break;
887
888
case eMessage_QUERY_state_hook_nmi:
889
SNES::cpu.debugger.op_nmi = ReadPipe<bool>() ? debug_op_nmi : hook<void ()>();
890
break;
891
892
case eMessage_QUERY_state_hook_irq:
893
SNES::cpu.debugger.op_irq = ReadPipe<bool>() ? debug_op_irq : hook<void ()>();
894
break;
895
896
case eMessage_QUERY_set_cdl:
897
for (int i = 0; i<eCDLog_AddrType_NUM; i++)
898
{
899
cdlInfo.blocks[i] = ReadPipe<uint8_t*>();
900
cdlInfo.blockSizes[i] = ReadPipe<uint32_t>();
901
}
902
break;
903
904
}
905
return true;
906
}
907
908
bool Handle_CMD(eMessage msg)
909
{
910
if(msg == eMessage_ResumeAfterBRK)
911
{
912
//careful! dont switch back to co_emu, we were in another cothread probably when the BRK happened.
913
//i'm not sure its completely safe to be returning to co_emu below in the normal CMD handler, either...
914
co_switch(co_emu_suspended);
915
return true;
916
}
917
918
if(msg<=eMessage_CMD_FIRST || msg>=eMessage_CMD_LAST) return false;
919
920
s_EmulationControl.command = msg;
921
s_EmulationControl.exitReason = eEmulationExitReason_NotSet;
922
co_switch(co_emu);
923
return true;
924
}
925
926
void Handle_SIG_audio_flush()
927
{
928
WritePipe(eMessage_SIG_audio_flush);
929
930
int nsamples = audiobuffer_idx;
931
WritePipe(nsamples);
932
char* buf = ReadPipeSharedPtr();
933
memcpy(buf,audiobuffer,nsamples*2);
934
//extra just in case we had to unexpectedly flush audio and then carry on with some other process... yeah, its rickety.
935
WritePipe(0); //dummy synchronization
936
937
//wait for frontend to consume data
938
939
ReadPipe<int>(); //dummy synchronization
940
WritePipe(0); //dummy synchronization
941
audiobuffer_idx = 0;
942
}
943
944
void MessageLoop()
945
{
946
for(;;)
947
{
948
TOP:
949
switch(s_EmulationControl.exitReason)
950
{
951
case eEmulationExitReason_NotSet:
952
goto HANDLEMESSAGES;
953
954
case eEmulationExitReason_CMD_Complete:
955
//printf("eEmulationExitReason_CMD_Complete (command:%d)\n",s_EmulationControl.command);
956
//MessageBox(0,"ZING","ZING",MB_OK);
957
//printf("WRITING COMPLETE\n");
958
WritePipe(eMessage_BRK_Complete);
959
960
//special post-completion messages (return values)
961
switch(s_EmulationControl.command)
962
{
963
case eMessage_CMD_load_cartridge_normal:
964
case eMessage_CMD_load_cartridge_super_game_boy:
965
case eMessage_CMD_serialize:
966
case eMessage_CMD_unserialize:
967
WritePipe(s_EmulationControl.cmd_result);
968
break;
969
}
970
971
s_EmulationControl.exitReason = eEmulationExitReason_NotSet;
972
s_EmulationControl.command = eMessage_NotSet;
973
goto TOP;
974
975
case eEmulationExitReason_SIG:
976
s_EmulationControl.exitReason = eEmulationExitReason_NotSet;
977
switch(s_EmulationControl.exitCallbackType)
978
{
979
case eEmulationCallback_snes_video_refresh:
980
{
981
WritePipe(eMessage_SIG_video_refresh);
982
WritePipe(s_EmulationControl.cb_video_refresh_params.width);
983
WritePipe(s_EmulationControl.cb_video_refresh_params.height);
984
int destOfs = ReadPipe<int>();
985
char* buf = (char*)hMapFilePtr + destOfs;
986
int bufsize = 512 * 480 * 4;
987
memcpy(buf,s_EmulationControl.cb_video_refresh_params.data,bufsize);
988
WritePipe((char)0); //dummy synchronization (alert frontend we're done with buffer)
989
break;
990
}
991
992
case eEmulationCallback_snes_audio_flush:
993
Handle_SIG_audio_flush();
994
break;
995
case eEmulationCallback_snes_input_poll:
996
WritePipe(eMessage_SIG_input_poll);
997
break;
998
case eEmulationCallback_snes_input_state:
999
WritePipe(eMessage_SIG_input_state);
1000
WritePipe(s_EmulationControl.cb_input_state_params.port);
1001
WritePipe(s_EmulationControl.cb_input_state_params.device);
1002
WritePipe(s_EmulationControl.cb_input_state_params.index);
1003
WritePipe(s_EmulationControl.cb_input_state_params.id);
1004
s_EmulationControl.cb_input_state_params.result = ReadPipe<int16_t>();
1005
break;
1006
case eEmulationCallback_snes_input_notify:
1007
WritePipe(eMessage_SIG_input_notify);
1008
WritePipe(s_EmulationControl.cb_input_notify_params.index);
1009
break;
1010
case eEmulationCallback_snes_path_request:
1011
{
1012
WritePipe(eMessage_SIG_path_request);
1013
WritePipe(s_EmulationControl.cb_path_request_params.slot);
1014
WritePipeString(s_EmulationControl.cb_path_request_params.hint);
1015
std::string temp = ReadPipeString();
1016
//yucky! use strncpy and ARRAY_SIZE or something!
1017
strcpy(s_EmulationControl.cb_path_request_params.result,temp.c_str());
1018
}
1019
break;
1020
case eEmulationCallback_snes_allocSharedMemory:
1021
implementation_snes_allocSharedMemory();
1022
break;
1023
case eEmulationCallback_snes_freeSharedMemory:
1024
implementation_snes_freeSharedMemory();
1025
break;
1026
case eEmulationCallback_snes_trace:
1027
WritePipe(eMessage_SIG_trace_callback);
1028
WritePipeString(s_EmulationControl.cb_trace_params.msg);
1029
break;
1030
}
1031
//when callbacks finish, we automatically resume emulation. be careful to go back to top!!!!!!!!
1032
SETEMU;
1033
goto TOP;
1034
1035
case eEmulationExitReason_BRK:
1036
s_EmulationControl.exitReason = eEmulationExitReason_NotSet;
1037
switch(s_EmulationControl.hookExitType)
1038
{
1039
case eMessage_BRK_scanlineStart:
1040
WritePipe(eMessage_BRK_scanlineStart);
1041
WritePipe((uint32)s_EmulationControl.hookAddr);
1042
break;
1043
case eMessage_BRK_hook_exec:
1044
WritePipe(eMessage_BRK_hook_exec);
1045
WritePipe((uint32)s_EmulationControl.hookAddr);
1046
break;
1047
case eMessage_BRK_hook_read:
1048
WritePipe(eMessage_BRK_hook_read);
1049
WritePipe((uint32)s_EmulationControl.hookAddr);
1050
break;
1051
case eMessage_BRK_hook_write:
1052
WritePipe(eMessage_BRK_hook_write);
1053
WritePipe((uint32)s_EmulationControl.hookAddr);
1054
WritePipe((uint8)s_EmulationControl.hookValue);
1055
break;
1056
}
1057
goto TOP;
1058
}
1059
1060
HANDLEMESSAGES:
1061
1062
//printf("Reading message from pipe...\n");
1063
eMessage msg = ReadPipe<eMessage>();
1064
//printf("...slam: %08X\n",msg);
1065
1066
if(Handle_QUERY(msg))
1067
goto TOP;
1068
if(Handle_CMD(msg))
1069
goto TOP;
1070
1071
switch(msg)
1072
{
1073
case eMessage_BRK_Complete:
1074
return;
1075
1076
case eMessage_Shutdown:
1077
//terminate this dll process
1078
return;
1079
1080
case eMessage_SetBuffer:
1081
{
1082
printf("eMessage_SetBuffer\n");
1083
int which = ReadPipe<s32>();
1084
std::string name = ReadPipeString();
1085
IPCRingBuffer* ipcrb = new IPCRingBuffer();
1086
ipcrb->Open(name);
1087
if(which==0) rbuf = ipcrb;
1088
else wbuf = ipcrb;
1089
break;
1090
}
1091
1092
case eMessage_BeginBufferIO:
1093
bufio = true;
1094
break;
1095
1096
case eMessage_EndBufferIO:
1097
bufio = false;
1098
break;
1099
1100
} //switch(msg)
1101
}
1102
}
1103
1104
static DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter)
1105
{
1106
MessageLoop();
1107
//send a message to the other thread to synchronize the shutdown of this thread
1108
//after that message is received, this thread (and the whole dll instance) is dead.
1109
WritePipe(eMessage_BRK_Complete);
1110
return 0;
1111
}
1112
1113
1114
void OpenConsole()
1115
{
1116
AllocConsole();
1117
freopen("CONOUT$", "w", stdout);
1118
freopen("CONOUT$", "w", stderr);
1119
freopen("CONIN$", "r", stdin);
1120
}
1121
1122
void EMUTHREAD_handleCommand_LoadCartridgeNormal()
1123
{
1124
Blob xml = ReadPipeBlob();
1125
xml.push_back(0); //make sure the xml is null terminated
1126
const char* xmlptr = NULL;
1127
if(xml.size() != 1) xmlptr = &xml[0];
1128
1129
Blob rom_data = ReadPipeBlob();
1130
const unsigned char* rom_ptr = NULL;
1131
if(rom_data.size() != 0) rom_ptr = (unsigned char*)&rom_data[0];
1132
1133
bool ret = snes_load_cartridge_normal(xmlptr,rom_ptr,rom_data.size());
1134
s_EmulationControl.cmd_result = ret?1:0;
1135
}
1136
1137
void EMUTHREAD_handleCommand_LoadCartridgeSuperGameBoy()
1138
{
1139
std::string rom_xml = ReadPipeString();
1140
const char* rom_xmlptr = NULL;
1141
if(rom_xml != "") rom_xmlptr = rom_xml.c_str();
1142
Blob rom_data = ReadPipeBlob();
1143
uint32 rom_length = rom_data.size();
1144
1145
std::string dmg_xml = ReadPipeString();
1146
const char* dmg_xmlptr = NULL;
1147
if(dmg_xml != "") dmg_xmlptr = dmg_xml.c_str();
1148
Blob dmg_data = ReadPipeBlob();
1149
uint32 dmg_length = dmg_data.size();
1150
1151
bool ret = snes_load_cartridge_super_game_boy(rom_xmlptr,(uint8*)&rom_data[0],rom_length, dmg_xmlptr,(uint8*)&dmg_data[0], dmg_length);
1152
s_EmulationControl.cmd_result = ret?1:0;
1153
}
1154
1155
void EMUTHREAD_handle_CMD_serialize()
1156
{
1157
int size = ReadPipe<s32>();
1158
int destOfs = ReadPipe<s32>();
1159
char* buf = (char*)hMapFilePtr + destOfs;
1160
bool ret = snes_serialize((uint8_t*)buf,size);
1161
s_EmulationControl.cmd_result = ret?1:0;
1162
}
1163
1164
void EMUTHREAD_handle_CMD_unserialize()
1165
{
1166
int size = ReadPipe<s32>();
1167
int destOfs = ReadPipe<s32>();
1168
char* buf = (char*)hMapFilePtr + destOfs;
1169
bool ret = snes_unserialize((uint8_t*)buf ,size);
1170
s_EmulationControl.cmd_result = ret?1:0;
1171
}
1172
1173
void emuthread()
1174
{
1175
for(;;)
1176
{
1177
switch(s_EmulationControl.command)
1178
{
1179
case eMessage_CMD_init:
1180
snes_init();
1181
break;
1182
case eMessage_CMD_power:
1183
snes_power();
1184
break;
1185
case eMessage_CMD_reset:
1186
snes_reset();
1187
break;
1188
case eMessage_CMD_term:
1189
snes_term();
1190
break;
1191
case eMessage_CMD_unload_cartridge:
1192
snes_unload_cartridge();
1193
break;
1194
case eMessage_CMD_load_cartridge_normal:
1195
EMUTHREAD_handleCommand_LoadCartridgeNormal();
1196
break;
1197
case eMessage_CMD_load_cartridge_super_game_boy:
1198
EMUTHREAD_handleCommand_LoadCartridgeSuperGameBoy();
1199
break;
1200
1201
case eMessage_CMD_serialize:
1202
EMUTHREAD_handle_CMD_serialize();
1203
break;
1204
case eMessage_CMD_unserialize:
1205
EMUTHREAD_handle_CMD_unserialize();
1206
break;
1207
1208
case eMessage_CMD_run:
1209
SIG_FlushAudio();
1210
1211
//we could avoid this if we saved the current thread before jumping back to co_control, instead of always jumping back to co_emu
1212
//in effect, we're scrambling the scheduler
1213
//EDIT - well, we changed that, but.. we still want this probably, for debugging and stuff
1214
for(;;)
1215
{
1216
SNES::scheduler.sync = SNES::Scheduler::SynchronizeMode::None;
1217
SNES::scheduler.clearExitReason();
1218
SNES::scheduler.enter();
1219
if(SNES::scheduler.exit_reason() == SNES::Scheduler::ExitReason::FrameEvent)
1220
{
1221
SNES::video.update();
1222
break;
1223
}
1224
//not used yet
1225
if(SNES::scheduler.exit_reason() == SNES::Scheduler::ExitReason::DebuggerEvent)
1226
break;
1227
}
1228
1229
SIG_FlushAudio();
1230
break;
1231
}
1232
1233
s_EmulationControl.exitReason = eEmulationExitReason_CMD_Complete;
1234
SETCONTROL;
1235
}
1236
}
1237
1238
BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved)
1239
{
1240
return TRUE;
1241
}
1242
1243
extern "C" dllexport bool __cdecl DllInit(const char* ipcname)
1244
{
1245
printf("NEW INSTANCE: %08X\n", &s_EmulationControl);
1246
1247
char pipename[256];
1248
char eventname[256];
1249
sprintf(pipename, "\\\\.\\Pipe\\%s",ipcname);
1250
sprintf(eventname, "%s-event", ipcname);
1251
1252
printf("pipe: %s\n",pipename);
1253
printf("event: %s\n",eventname);
1254
1255
hPipe = CreateFile(pipename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
1256
1257
if(hPipe == INVALID_HANDLE_VALUE)
1258
return false;
1259
1260
hMapFile = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, ipcname);
1261
if(hMapFile == INVALID_HANDLE_VALUE)
1262
return false;
1263
1264
hMapFilePtr = MapViewOfFile(hMapFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
1265
1266
//make a coroutine thread to run the emulation in. we'll switch back to this thread when communicating with the frontend
1267
co_control = co_active();
1268
co_emu = co_create(65536*sizeof(void*),emuthread);
1269
1270
running = true;
1271
printf("running\n");
1272
1273
DWORD tid;
1274
CreateThread(nullptr, 0, &ThreadProc, nullptr, 0, &tid);
1275
1276
return true;
1277
}
1278
1279
1280
void pwrap_init()
1281
{
1282
//bsnes's interface initialization calls into this after initializing itself, so we can get a chance to mod it for pwrap functionalities
1283
InitBsnes();
1284
}
1285