1 #include <lightrec.h>
2 #include <stdbool.h>
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <signal.h>
6 
7 #include "../cdrom.h"
8 #include "../gpu.h"
9 #include "../gte.h"
10 #include "../mdec.h"
11 #include "../psxdma.h"
12 #include "../psxhw.h"
13 #include "../psxmem.h"
14 #include "../r3000a.h"
15 
16 #include "../frontend/main.h"
17 
18 #define ARRAY_SIZE(x) (sizeof(x) ? sizeof(x) / sizeof((x)[0]) : 0)
19 
20 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
21 #	define LE32TOH(x)	__builtin_bswap32(x)
22 #	define HTOLE32(x)	__builtin_bswap32(x)
23 #	define LE16TOH(x)	__builtin_bswap16(x)
24 #	define HTOLE16(x)	__builtin_bswap16(x)
25 #else
26 #	define LE32TOH(x)	(x)
27 #	define HTOLE32(x)	(x)
28 #	define LE16TOH(x)	(x)
29 #	define HTOLE16(x)	(x)
30 #endif
31 
32 #ifdef __GNUC__
33 #	define likely(x)       __builtin_expect(!!(x),1)
34 #	define unlikely(x)     __builtin_expect(!!(x),0)
35 #else
36 #	define likely(x)       (x)
37 #	define unlikely(x)     (x)
38 #endif
39 
40 static struct lightrec_state *lightrec_state;
41 
42 static char *name = "retroarch.exe";
43 
44 static bool use_lightrec_interpreter;
45 static bool use_pcsx_interpreter;
46 static bool lightrec_debug;
47 static bool lightrec_very_debug;
48 static u32 lightrec_begin_cycles;
49 
50 int stop;
51 u32 cycle_multiplier;
52 int new_dynarec_hacks;
53 
54 /* Unused for now */
55 u32 event_cycles[PSXINT_COUNT];
56 u32 next_interupt;
57 
new_dyna_before_save()58 void new_dyna_before_save() {}
new_dyna_after_save()59 void new_dyna_after_save() {}
new_dyna_freeze(void * f,int i)60 void new_dyna_freeze(void *f, int i) {}
61 
62 enum my_cp2_opcodes {
63 	OP_CP2_RTPS		= 0x01,
64 	OP_CP2_NCLIP		= 0x06,
65 	OP_CP2_OP		= 0x0c,
66 	OP_CP2_DPCS		= 0x10,
67 	OP_CP2_INTPL		= 0x11,
68 	OP_CP2_MVMVA		= 0x12,
69 	OP_CP2_NCDS		= 0x13,
70 	OP_CP2_CDP		= 0x14,
71 	OP_CP2_NCDT		= 0x16,
72 	OP_CP2_NCCS		= 0x1b,
73 	OP_CP2_CC		= 0x1c,
74 	OP_CP2_NCS		= 0x1e,
75 	OP_CP2_NCT		= 0x20,
76 	OP_CP2_SQR		= 0x28,
77 	OP_CP2_DCPL		= 0x29,
78 	OP_CP2_DPCT		= 0x2a,
79 	OP_CP2_AVSZ3		= 0x2d,
80 	OP_CP2_AVSZ4		= 0x2e,
81 	OP_CP2_RTPT		= 0x30,
82 	OP_CP2_GPF		= 0x3d,
83 	OP_CP2_GPL		= 0x3e,
84 	OP_CP2_NCCT		= 0x3f,
85 };
86 
87 static void (*cp2_ops[])(struct psxCP2Regs *) = {
88 	[OP_CP2_RTPS] = gteRTPS,
89 	[OP_CP2_RTPS] = gteRTPS,
90 	[OP_CP2_NCLIP] = gteNCLIP,
91 	[OP_CP2_OP] = gteOP,
92 	[OP_CP2_DPCS] = gteDPCS,
93 	[OP_CP2_INTPL] = gteINTPL,
94 	[OP_CP2_MVMVA] = gteMVMVA,
95 	[OP_CP2_NCDS] = gteNCDS,
96 	[OP_CP2_CDP] = gteCDP,
97 	[OP_CP2_NCDT] = gteNCDT,
98 	[OP_CP2_NCCS] = gteNCCS,
99 	[OP_CP2_CC] = gteCC,
100 	[OP_CP2_NCS] = gteNCS,
101 	[OP_CP2_NCT] = gteNCT,
102 	[OP_CP2_SQR] = gteSQR,
103 	[OP_CP2_DCPL] = gteDCPL,
104 	[OP_CP2_DPCT] = gteDPCT,
105 	[OP_CP2_AVSZ3] = gteAVSZ3,
106 	[OP_CP2_AVSZ4] = gteAVSZ4,
107 	[OP_CP2_RTPT] = gteRTPT,
108 	[OP_CP2_GPF] = gteGPF,
109 	[OP_CP2_GPL] = gteGPL,
110 	[OP_CP2_NCCT] = gteNCCT,
111 };
112 
113 static char cache_buf[64 * 1024];
114 
cop0_mfc(struct lightrec_state * state,u32 op,u8 reg)115 static u32 cop0_mfc(struct lightrec_state *state, u32 op, u8 reg)
116 {
117 	return psxRegs.CP0.r[reg];
118 }
119 
cop2_mfc_cfc(struct lightrec_state * state,u8 reg,bool cfc)120 static u32 cop2_mfc_cfc(struct lightrec_state *state, u8 reg, bool cfc)
121 {
122 	if (cfc)
123 		return psxRegs.CP2C.r[reg];
124 	else
125 		return MFC2(reg);
126 }
127 
cop2_mfc(struct lightrec_state * state,u32 op,u8 reg)128 static u32 cop2_mfc(struct lightrec_state *state, u32 op, u8 reg)
129 {
130 	return cop2_mfc_cfc(state, reg, false);
131 }
132 
cop2_cfc(struct lightrec_state * state,u32 op,u8 reg)133 static u32 cop2_cfc(struct lightrec_state *state, u32 op, u8 reg)
134 {
135 	return cop2_mfc_cfc(state, reg, true);
136 }
137 
cop0_mtc_ctc(struct lightrec_state * state,u8 reg,u32 value,bool ctc)138 static void cop0_mtc_ctc(struct lightrec_state *state,
139 			 u8 reg, u32 value, bool ctc)
140 {
141 	switch (reg) {
142 	case 1:
143 	case 4:
144 	case 8:
145 	case 14:
146 	case 15:
147 		/* Those registers are read-only */
148 		break;
149 	case 12: /* Status */
150 		if ((psxRegs.CP0.n.Status & ~value) & (1 << 16)) {
151 			memcpy(psxM, cache_buf, sizeof(cache_buf));
152 			lightrec_invalidate_all(state);
153 		} else if ((~psxRegs.CP0.n.Status & value) & (1 << 16)) {
154 			memcpy(cache_buf, psxM, sizeof(cache_buf));
155 		}
156 
157 		psxRegs.CP0.n.Status = value;
158 		lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
159 		break;
160 	case 13: /* Cause */
161 		psxRegs.CP0.n.Cause &= ~0x0300;
162 		psxRegs.CP0.n.Cause |= value & 0x0300;
163 		lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
164 		break;
165 	default:
166 		psxRegs.CP0.r[reg] = value;
167 		break;
168 	}
169 }
170 
cop2_mtc_ctc(struct lightrec_state * state,u8 reg,u32 value,bool ctc)171 static void cop2_mtc_ctc(struct lightrec_state *state,
172 			 u8 reg, u32 value, bool ctc)
173 {
174 	if (ctc)
175 		CTC2(value, reg);
176 	else
177 		MTC2(value, reg);
178 }
179 
cop0_mtc(struct lightrec_state * state,u32 op,u8 reg,u32 value)180 static void cop0_mtc(struct lightrec_state *state, u32 op, u8 reg, u32 value)
181 {
182 	cop0_mtc_ctc(state, reg, value, false);
183 }
184 
cop0_ctc(struct lightrec_state * state,u32 op,u8 reg,u32 value)185 static void cop0_ctc(struct lightrec_state *state, u32 op, u8 reg, u32 value)
186 {
187 	cop0_mtc_ctc(state, reg, value, true);
188 }
189 
cop2_mtc(struct lightrec_state * state,u32 op,u8 reg,u32 value)190 static void cop2_mtc(struct lightrec_state *state, u32 op, u8 reg, u32 value)
191 {
192 	cop2_mtc_ctc(state, reg, value, false);
193 }
194 
cop2_ctc(struct lightrec_state * state,u32 op,u8 reg,u32 value)195 static void cop2_ctc(struct lightrec_state *state, u32 op, u8 reg, u32 value)
196 {
197 	cop2_mtc_ctc(state, reg, value, true);
198 }
199 
cop0_op(struct lightrec_state * state,u32 func)200 static void cop0_op(struct lightrec_state *state, u32 func)
201 {
202 	fprintf(stderr, "Invalid access to COP0\n");
203 }
204 
cop2_op(struct lightrec_state * state,u32 func)205 static void cop2_op(struct lightrec_state *state, u32 func)
206 {
207 	psxRegs.code = func;
208 
209 	if (unlikely(!cp2_ops[func & 0x3f]))
210 		fprintf(stderr, "Invalid CP2 function %u\n", func);
211 	else
212 		cp2_ops[func & 0x3f](&psxRegs.CP2);
213 }
214 
hw_write_byte(struct lightrec_state * state,u32 op,void * host,u32 mem,u8 val)215 static void hw_write_byte(struct lightrec_state *state,
216 			  u32 op, void *host, u32 mem, u8 val)
217 {
218 	psxRegs.cycle = lightrec_current_cycle_count(state);
219 
220 	psxHwWrite8(mem, val);
221 	lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
222 
223 	lightrec_reset_cycle_count(state, psxRegs.cycle);
224 }
225 
hw_write_half(struct lightrec_state * state,u32 op,void * host,u32 mem,u16 val)226 static void hw_write_half(struct lightrec_state *state,
227 			  u32 op, void *host, u32 mem, u16 val)
228 {
229 	psxRegs.cycle = lightrec_current_cycle_count(state);
230 
231 	psxHwWrite16(mem, val);
232 	lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
233 
234 	lightrec_reset_cycle_count(state, psxRegs.cycle);
235 }
236 
hw_write_word(struct lightrec_state * state,u32 op,void * host,u32 mem,u32 val)237 static void hw_write_word(struct lightrec_state *state,
238 			  u32 op, void *host, u32 mem, u32 val)
239 {
240 	psxRegs.cycle = lightrec_current_cycle_count(state);
241 
242 	psxHwWrite32(mem, val);
243 	lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
244 
245 	lightrec_reset_cycle_count(state, psxRegs.cycle);
246 }
247 
hw_read_byte(struct lightrec_state * state,u32 op,void * host,u32 mem)248 static u8 hw_read_byte(struct lightrec_state *state, u32 op, void *host, u32 mem)
249 {
250 	u8 val;
251 
252 	psxRegs.cycle = lightrec_current_cycle_count(state);
253 
254 	lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
255 	val = psxHwRead8(mem);
256 	lightrec_reset_cycle_count(state, psxRegs.cycle);
257 
258 	return val;
259 }
260 
hw_read_half(struct lightrec_state * state,u32 op,void * host,u32 mem)261 static u16 hw_read_half(struct lightrec_state *state,
262 			u32 op, void *host, u32 mem)
263 {
264 	u16 val;
265 
266 	psxRegs.cycle = lightrec_current_cycle_count(state);
267 
268 	lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
269 	val = psxHwRead16(mem);
270 	lightrec_reset_cycle_count(state, psxRegs.cycle);
271 
272 	return val;
273 }
274 
hw_read_word(struct lightrec_state * state,u32 op,void * host,u32 mem)275 static u32 hw_read_word(struct lightrec_state *state,
276 			u32 op, void *host, u32 mem)
277 {
278 	u32 val;
279 
280 	psxRegs.cycle = lightrec_current_cycle_count(state);
281 
282 	lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
283 	val = psxHwRead32(mem);
284 	lightrec_reset_cycle_count(state, psxRegs.cycle);
285 
286 	return val;
287 }
288 
289 static struct lightrec_mem_map_ops hw_regs_ops = {
290 	.sb = hw_write_byte,
291 	.sh = hw_write_half,
292 	.sw = hw_write_word,
293 	.lb = hw_read_byte,
294 	.lh = hw_read_half,
295 	.lw = hw_read_word,
296 };
297 
298 static u32 cache_ctrl;
299 
cache_ctrl_write_word(struct lightrec_state * state,u32 op,void * host,u32 mem,u32 val)300 static void cache_ctrl_write_word(struct lightrec_state *state,
301 				  u32 op, void *host, u32 mem, u32 val)
302 {
303 	cache_ctrl = val;
304 }
305 
cache_ctrl_read_word(struct lightrec_state * state,u32 op,void * host,u32 mem)306 static u32 cache_ctrl_read_word(struct lightrec_state *state,
307 				u32 op, void *host, u32 mem)
308 {
309 	return cache_ctrl;
310 }
311 
312 static struct lightrec_mem_map_ops cache_ctrl_ops = {
313 	.sw = cache_ctrl_write_word,
314 	.lw = cache_ctrl_read_word,
315 };
316 
317 static struct lightrec_mem_map lightrec_map[] = {
318 	[PSX_MAP_KERNEL_USER_RAM] = {
319 		/* Kernel and user memory */
320 		.pc = 0x00000000,
321 		.length = 0x200000,
322 	},
323 	[PSX_MAP_BIOS] = {
324 		/* BIOS */
325 		.pc = 0x1fc00000,
326 		.length = 0x80000,
327 	},
328 	[PSX_MAP_SCRATCH_PAD] = {
329 		/* Scratch pad */
330 		.pc = 0x1f800000,
331 		.length = 0x400,
332 	},
333 	[PSX_MAP_PARALLEL_PORT] = {
334 		/* Parallel port */
335 		.pc = 0x1f000000,
336 		.length = 0x10000,
337 	},
338 	[PSX_MAP_HW_REGISTERS] = {
339 		/* Hardware registers */
340 		.pc = 0x1f801000,
341 		.length = 0x2000,
342 		.ops = &hw_regs_ops,
343 	},
344 	[PSX_MAP_CACHE_CONTROL] = {
345 		/* Cache control */
346 		.pc = 0x5ffe0130,
347 		.length = 4,
348 		.ops = &cache_ctrl_ops,
349 	},
350 
351 	/* Mirrors of the kernel/user memory */
352 	[PSX_MAP_MIRROR1] = {
353 		.pc = 0x00200000,
354 		.length = 0x200000,
355 		.mirror_of = &lightrec_map[PSX_MAP_KERNEL_USER_RAM],
356 	},
357 	[PSX_MAP_MIRROR2] = {
358 		.pc = 0x00400000,
359 		.length = 0x200000,
360 		.mirror_of = &lightrec_map[PSX_MAP_KERNEL_USER_RAM],
361 	},
362 	[PSX_MAP_MIRROR3] = {
363 		.pc = 0x00600000,
364 		.length = 0x200000,
365 		.mirror_of = &lightrec_map[PSX_MAP_KERNEL_USER_RAM],
366 	},
367 };
368 
369 static const struct lightrec_ops lightrec_ops = {
370 	.cop0_ops = {
371 		.mfc = cop0_mfc,
372 		.cfc = cop0_mfc,
373 		.mtc = cop0_mtc,
374 		.ctc = cop0_ctc,
375 		.op = cop0_op,
376 	},
377 	.cop2_ops = {
378 		.mfc = cop2_mfc,
379 		.cfc = cop2_cfc,
380 		.mtc = cop2_mtc,
381 		.ctc = cop2_ctc,
382 		.op = cop2_op,
383 	},
384 };
385 
lightrec_plugin_init(void)386 static int lightrec_plugin_init(void)
387 {
388 	lightrec_map[PSX_MAP_KERNEL_USER_RAM].address = psxM;
389 	lightrec_map[PSX_MAP_BIOS].address = psxR;
390 	lightrec_map[PSX_MAP_SCRATCH_PAD].address = psxH;
391 	lightrec_map[PSX_MAP_PARALLEL_PORT].address = psxP;
392 
393 	lightrec_debug = !!getenv("LIGHTREC_DEBUG");
394 	lightrec_very_debug = !!getenv("LIGHTREC_VERY_DEBUG");
395 	use_lightrec_interpreter = !!getenv("LIGHTREC_INTERPRETER");
396 	if (getenv("LIGHTREC_BEGIN_CYCLES"))
397 	  lightrec_begin_cycles = (unsigned int) strtol(
398 				  getenv("LIGHTREC_BEGIN_CYCLES"), NULL, 0);
399 
400 	lightrec_state = lightrec_init(name,
401 			lightrec_map, ARRAY_SIZE(lightrec_map),
402 			&lightrec_ops);
403 
404 	fprintf(stderr, "M=0x%lx, P=0x%lx, R=0x%lx, H=0x%lx\n",
405 			(uintptr_t) psxM,
406 			(uintptr_t) psxP,
407 			(uintptr_t) psxR,
408 			(uintptr_t) psxH);
409 
410 #ifndef _WIN32
411 	signal(SIGPIPE, exit);
412 #endif
413 	return 0;
414 }
415 
hash_calculate_le(const void * buffer,u32 count)416 static u32 hash_calculate_le(const void *buffer, u32 count)
417 {
418 	unsigned int i;
419 	u32 *data = (u32 *) buffer;
420 	u32 hash = 0xffffffff;
421 
422 	count /= 4;
423 	for(i = 0; i < count; ++i) {
424 		hash += LE32TOH(data[i]);
425 		hash += (hash << 10);
426 		hash ^= (hash >> 6);
427 	}
428 
429 	hash += (hash << 3);
430 	hash ^= (hash >> 11);
431 	hash += (hash << 15);
432 	return hash;
433 }
434 
hash_calculate(const void * buffer,u32 count)435 static u32 hash_calculate(const void *buffer, u32 count)
436 {
437 	unsigned int i;
438 	u32 *data = (u32 *) buffer;
439 	u32 hash = 0xffffffff;
440 
441 	count /= 4;
442 	for(i = 0; i < count; ++i) {
443 		hash += data[i];
444 		hash += (hash << 10);
445 		hash ^= (hash >> 6);
446 	}
447 
448 	hash += (hash << 3);
449 	hash ^= (hash >> 11);
450 	hash += (hash << 15);
451 	return hash;
452 }
453 
454 static const char * const mips_regs[] = {
455 	"zero",
456 	"at",
457 	"v0", "v1",
458 	"a0", "a1", "a2", "a3",
459 	"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
460 	"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
461 	"t8", "t9",
462 	"k0", "k1",
463 	"gp", "sp", "fp", "ra",
464 	"lo", "hi",
465 };
466 
print_for_big_ass_debugger(void)467 static void print_for_big_ass_debugger(void)
468 {
469 	unsigned int i;
470 
471 	printf("CYCLE 0x%08x PC 0x%08x", psxRegs.cycle, psxRegs.pc);
472 
473 	if (lightrec_very_debug)
474 		printf(" RAM 0x%08x SCRATCH 0x%08x HW 0x%08x",
475 				hash_calculate_le(psxM, 0x200000),
476 				hash_calculate_le(psxH, 0x400),
477 				hash_calculate_le(psxH + 0x1000, 0x2000));
478 
479 	printf(" CP0 0x%08x CP2D 0x%08x CP2C 0x%08x INT 0x%04x INTCYCLE 0x%08x GPU 0x%08x",
480 			hash_calculate(&psxRegs.CP0.r,
481 				sizeof(psxRegs.CP0.r)),
482 			hash_calculate(&psxRegs.CP2D.r,
483 				sizeof(psxRegs.CP2D.r)),
484 			hash_calculate(&psxRegs.CP2C.r,
485 				sizeof(psxRegs.CP2C.r)),
486 			psxRegs.interrupt,
487 			hash_calculate(psxRegs.intCycle,
488 				sizeof(psxRegs.intCycle)),
489 			LE32TOH(HW_GPU_STATUS));
490 
491 	if (lightrec_very_debug)
492 		for (i = 0; i < 34; i++)
493 			printf(" %s 0x%08x", mips_regs[i], psxRegs.GPR.r[i]);
494 	else
495 		printf(" GPR 0x%08x", hash_calculate(&psxRegs.GPR.r,
496 					sizeof(psxRegs.GPR.r)));
497 	printf("\n");
498 }
499 
500 
501 extern void intExecuteBlock();
502 
503 static u32 old_cycle_counter;
504 
lightrec_plugin_execute_block(void)505 static void lightrec_plugin_execute_block(void)
506 {
507 	u32 old_pc = psxRegs.pc;
508 	u32 flags;
509 
510 	if (use_pcsx_interpreter) {
511 		intExecuteBlock();
512 	} else {
513 		lightrec_reset_cycle_count(lightrec_state, psxRegs.cycle);
514 		lightrec_restore_registers(lightrec_state, psxRegs.GPR.r);
515 
516 		if (use_lightrec_interpreter)
517 			psxRegs.pc = lightrec_run_interpreter(lightrec_state,
518 							      psxRegs.pc);
519 		else
520 			psxRegs.pc = lightrec_execute_one(lightrec_state,
521 							  psxRegs.pc);
522 
523 		psxRegs.cycle = lightrec_current_cycle_count(lightrec_state);
524 
525 		lightrec_dump_registers(lightrec_state, psxRegs.GPR.r);
526 		flags = lightrec_exit_flags(lightrec_state);
527 
528 		if (flags & LIGHTREC_EXIT_SEGFAULT) {
529 			fprintf(stderr, "Exiting at cycle 0x%08x\n",
530 				psxRegs.cycle);
531 			exit(1);
532 		}
533 
534 		if (flags & LIGHTREC_EXIT_SYSCALL)
535 			psxException(0x20, 0);
536 	}
537 
538 	psxBranchTest();
539 
540 	if (lightrec_debug && psxRegs.cycle >= lightrec_begin_cycles
541 			&& psxRegs.pc != old_pc)
542 		print_for_big_ass_debugger();
543 
544 	if ((psxRegs.CP0.n.Cause & psxRegs.CP0.n.Status & 0x300) &&
545 			(psxRegs.CP0.n.Status & 0x1)) {
546 		/* Handle software interrupts */
547 		psxRegs.CP0.n.Cause &= ~0x7c;
548 		psxException(psxRegs.CP0.n.Cause, 0);
549 	}
550 
551 	if ((psxRegs.cycle & ~0xfffffff) != old_cycle_counter) {
552 		SysDLog("RAM usage: Lightrec %u KiB, IR %u KiB, CODE %u KiB, "
553 		       "MIPS %u KiB, TOTAL %u KiB, avg. IPI %f\n",
554 		       lightrec_get_mem_usage(MEM_FOR_LIGHTREC) / 1024,
555 		       lightrec_get_mem_usage(MEM_FOR_IR) / 1024,
556 		       lightrec_get_mem_usage(MEM_FOR_CODE) / 1024,
557 		       lightrec_get_mem_usage(MEM_FOR_MIPS_CODE) / 1024,
558 		       lightrec_get_total_mem_usage() / 1024,
559 		       lightrec_get_average_ipi());
560 		old_cycle_counter = psxRegs.cycle & ~0xfffffff;
561 	}
562 }
563 
lightrec_plugin_execute(void)564 static void lightrec_plugin_execute(void)
565 {
566 	extern int stop;
567 
568 	while (!stop)
569 		lightrec_plugin_execute_block();
570 }
571 
lightrec_plugin_clear(u32 addr,u32 size)572 static void lightrec_plugin_clear(u32 addr, u32 size)
573 {
574 	if (addr == 0 && size == UINT32_MAX)
575 		lightrec_invalidate_all(lightrec_state);
576 	else
577 		/* size * 4: PCSX uses DMA units */
578 		lightrec_invalidate(lightrec_state, addr, size * 4);
579 }
580 
lightrec_plugin_shutdown(void)581 static void lightrec_plugin_shutdown(void)
582 {
583 	lightrec_destroy(lightrec_state);
584 }
585 
lightrec_plugin_reset(void)586 static void lightrec_plugin_reset(void)
587 {
588 	lightrec_plugin_shutdown();
589 	lightrec_plugin_init();
590 }
591 
592 R3000Acpu psxRec =
593 {
594 	lightrec_plugin_init,
595 	lightrec_plugin_reset,
596 	lightrec_plugin_execute,
597 	lightrec_plugin_execute_block,
598 	lightrec_plugin_clear,
599 	lightrec_plugin_shutdown,
600 };
601