1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus - plugin.c *
3 * Mupen64Plus homepage: https://mupen64plus.org/ *
4 * Copyright (C) 2002 Hacktarux *
5 * Copyright (C) 2009 Richard Goedeken *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
21 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
22
23#include <stdint.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include "api/callbacks.h"
28#include "api/m64p_common.h"
29#include "api/m64p_plugin.h"
30#include "api/m64p_types.h"
31#include "device/memory/memory.h"
32#include "device/rcp/ai/ai_controller.h"
33#include "device/rcp/mi/mi_controller.h"
34#include "device/rcp/rdp/rdp_core.h"
35#include "device/rcp/rsp/rsp_core.h"
36#include "device/rcp/vi/vi_controller.h"
37#include "dummy_audio.h"
38#include "dummy_input.h"
39#include "dummy_rsp.h"
40#include "dummy_video.h"
41#include "main/main.h"
42#include "main/rom.h"
43#include "main/version.h"
44#include "osal/dynamiclib.h"
45#include "plugin.h"
46
47CONTROL Controls[4];
48
49/* global function pointers - initialized on core startup */
50gfx_plugin_functions gfx;
51audio_plugin_functions audio;
52input_plugin_functions input;
53rsp_plugin_functions rsp;
54
55/* local data structures and functions */
56static const gfx_plugin_functions dummy_gfx = {
57 dummyvideo_PluginGetVersion,
58 dummyvideo_ChangeWindow,
59 dummyvideo_InitiateGFX,
60 dummyvideo_MoveScreen,
61 dummyvideo_ProcessDList,
62 dummyvideo_ProcessRDPList,
63 dummyvideo_RomClosed,
64 dummyvideo_RomOpen,
65 dummyvideo_ShowCFB,
66 dummyvideo_UpdateScreen,
67 dummyvideo_ViStatusChanged,
68 dummyvideo_ViWidthChanged,
69 dummyvideo_ReadScreen2,
70 dummyvideo_SetRenderingCallback,
71 dummyvideo_ResizeVideoOutput,
72 dummyvideo_FBRead,
73 dummyvideo_FBWrite,
74 dummyvideo_FBGetFrameBufferInfo
75};
76
77static const audio_plugin_functions dummy_audio = {
78 dummyaudio_PluginGetVersion,
79 dummyaudio_AiDacrateChanged,
80 dummyaudio_AiLenChanged,
81 dummyaudio_InitiateAudio,
82 dummyaudio_ProcessAList,
83 dummyaudio_RomClosed,
84 dummyaudio_RomOpen,
85 dummyaudio_SetSpeedFactor,
86 dummyaudio_VolumeUp,
87 dummyaudio_VolumeDown,
88 dummyaudio_VolumeGetLevel,
89 dummyaudio_VolumeSetLevel,
90 dummyaudio_VolumeMute,
91 dummyaudio_VolumeGetString
92};
93
94static const input_plugin_functions dummy_input = {
95 dummyinput_PluginGetVersion,
96 dummyinput_ControllerCommand,
97 dummyinput_GetKeys,
98 dummyinput_InitiateControllers,
99 dummyinput_ReadController,
100 dummyinput_RomClosed,
101 dummyinput_RomOpen,
102 dummyinput_SDL_KeyDown,
103 dummyinput_SDL_KeyUp,
104 dummyinput_RenderCallback
105};
106
107static const rsp_plugin_functions dummy_rsp = {
108 dummyrsp_PluginGetVersion,
109 dummyrsp_DoRspCycles,
110 dummyrsp_InitiateRSP,
111 dummyrsp_RomClosed
112};
113
114static GFX_INFO gfx_info;
115static AUDIO_INFO audio_info;
116static CONTROL_INFO control_info;
117static RSP_INFO rsp_info;
118
119static int l_RspAttached = 0;
120static int l_InputAttached = 0;
121static int l_AudioAttached = 0;
122static int l_GfxAttached = 0;
123
124static unsigned int dummy;
125
126/* local functions */
127static void EmptyFunc(void)
128{
129}
130
131// Handy macro to avoid code bloat when loading symbols
132#define GET_FUNC(type, field, name) \
133 ((field = (type)osal_dynlib_getproc(plugin_handle, name)) != NULL)
134
135// code to handle backwards-compatibility to video plugins with API_VERSION < 02.1.0. This API version introduced a boolean
136// flag in the rendering callback, which told the core whether or not the current screen has been freshly redrawn since the
137// last time the callback was called.
138static void (*l_mainRenderCallback)(int) = NULL;
139static ptr_SetRenderingCallback l_old1SetRenderingCallback = NULL;
140
141static void backcompat_videoRenderCallback(int unused) // this function will be called by the video plugin as the render callback
142{
143 if (l_mainRenderCallback != NULL)
144 l_mainRenderCallback(1); // assume screen is always freshly redrawn (otherwise screenshots won't work w/ OSD enabled)
145}
146
147static void backcompat_setRenderCallbackIntercept(void (*callback)(int))
148{
149 l_mainRenderCallback = callback;
150}
151
152static void plugin_disconnect_gfx(void)
153{
154 gfx = dummy_gfx;
155 l_GfxAttached = 0;
156 l_mainRenderCallback = NULL;
157}
158
159static m64p_error plugin_connect_gfx(m64p_dynlib_handle plugin_handle)
160{
161 /* attach the Video plugin function pointers */
162 if (plugin_handle != NULL)
163 {
164 m64p_plugin_type PluginType;
165 int PluginVersion, APIVersion;
166
167 if (l_GfxAttached)
168 return M64ERR_INVALID_STATE;
169
170 /* set function pointers for required functions */
171 if (!GET_FUNC(ptr_PluginGetVersion, gfx.getVersion, "PluginGetVersion") ||
172 !GET_FUNC(ptr_ChangeWindow, gfx.changeWindow, "ChangeWindow") ||
173 !GET_FUNC(ptr_InitiateGFX, gfx.initiateGFX, "InitiateGFX") ||
174 !GET_FUNC(ptr_MoveScreen, gfx.moveScreen, "MoveScreen") ||
175 !GET_FUNC(ptr_ProcessDList, gfx.processDList, "ProcessDList") ||
176 !GET_FUNC(ptr_ProcessRDPList, gfx.processRDPList, "ProcessRDPList") ||
177 !GET_FUNC(ptr_RomClosed, gfx.romClosed, "RomClosed") ||
178 !GET_FUNC(ptr_RomOpen, gfx.romOpen, "RomOpen") ||
179 !GET_FUNC(ptr_ShowCFB, gfx.showCFB, "ShowCFB") ||
180 !GET_FUNC(ptr_UpdateScreen, gfx.updateScreen, "UpdateScreen") ||
181 !GET_FUNC(ptr_ViStatusChanged, gfx.viStatusChanged, "ViStatusChanged") ||
182 !GET_FUNC(ptr_ViWidthChanged, gfx.viWidthChanged, "ViWidthChanged") ||
183 !GET_FUNC(ptr_ReadScreen2, gfx.readScreen, "ReadScreen2") ||
184 !GET_FUNC(ptr_SetRenderingCallback, gfx.setRenderingCallback, "SetRenderingCallback") ||
185 !GET_FUNC(ptr_FBRead, gfx.fBRead, "FBRead") ||
186 !GET_FUNC(ptr_FBWrite, gfx.fBWrite, "FBWrite") ||
187 !GET_FUNC(ptr_FBGetFrameBufferInfo, gfx.fBGetFrameBufferInfo, "FBGetFrameBufferInfo"))
188 {
189 DebugMessage(M64MSG_ERROR, "broken Video plugin; function(s) not found.");
190 plugin_disconnect_gfx();
191 return M64ERR_INPUT_INVALID;
192 }
193
194 /* set function pointers for optional functions */
195 gfx.resizeVideoOutput = (ptr_ResizeVideoOutput)osal_dynlib_getproc(plugin_handle, "ResizeVideoOutput");
196
197 /* check the version info */
198 (*gfx.getVersion)(&PluginType, &PluginVersion, &APIVersion, NULL, NULL);
199 if (PluginType != M64PLUGIN_GFX || (APIVersion & 0xffff0000) != (GFX_API_VERSION & 0xffff0000))
200 {
201 DebugMessage(M64MSG_ERROR, "incompatible Video plugin");
202 plugin_disconnect_gfx();
203 return M64ERR_INCOMPATIBLE;
204 }
205
206 /* handle backwards-compatibility */
207 if (APIVersion < 0x020100)
208 {
209 DebugMessage(M64MSG_WARNING, "Fallback for Video plugin API (%02i.%02i.%02i) < 2.1.0. Screenshots may contain On Screen Display text", VERSION_PRINTF_SPLIT(APIVersion));
210 // tell the video plugin to make its rendering callback to me (it's old, and doesn't have the bScreenRedrawn flag)
211 gfx.setRenderingCallback(backcompat_videoRenderCallback);
212 l_old1SetRenderingCallback = gfx.setRenderingCallback; // save this just for future use
213 gfx.setRenderingCallback = (ptr_SetRenderingCallback) backcompat_setRenderCallbackIntercept;
214 }
215 if (APIVersion < 0x20200 || gfx.resizeVideoOutput == NULL)
216 {
217 DebugMessage(M64MSG_WARNING, "Fallback for Video plugin API (%02i.%02i.%02i) < 2.2.0. Resizable video will not work", VERSION_PRINTF_SPLIT(APIVersion));
218 gfx.resizeVideoOutput = dummyvideo_ResizeVideoOutput;
219 }
220
221 l_GfxAttached = 1;
222 }
223 else
224 plugin_disconnect_gfx();
225
226 return M64ERR_SUCCESS;
227}
228
229static m64p_error plugin_start_gfx(void)
230{
231 uint8_t media = *((uint8_t*)mem_base_u32(g_mem_base, MM_CART_ROM) + (0x3b ^ S8));
232
233 /* Here we feed 64DD IPL ROM header to GFX plugin if 64DD is present.
234 * We use g_media_loader.get_dd_rom to detect 64DD presence
235 * instead of g_dev because the latter is not yet initialized at plugin_start time */
236 /* XXX: Not sure it is the best way to convey which game is being played to the GFX plugin
237 * as 64DD IPL is the same for all 64DD games... */
238 char* dd_ipl_rom_filename = (g_media_loader.get_dd_rom == NULL)
239 ? NULL
240 : g_media_loader.get_dd_rom(g_media_loader.cb_data);
241
242 uint32_t rom_base = (dd_ipl_rom_filename != NULL && strlen(dd_ipl_rom_filename) != 0 && media != 'C')
243 ? MM_DD_ROM
244 : MM_CART_ROM;
245
246 free(dd_ipl_rom_filename);
247
248 /* fill in the GFX_INFO data structure */
249 gfx_info.HEADER = (unsigned char *)mem_base_u32(g_mem_base, rom_base);
250 gfx_info.RDRAM = (unsigned char *)mem_base_u32(g_mem_base, MM_RDRAM_DRAM);
251 gfx_info.DMEM = (unsigned char *)mem_base_u32(g_mem_base, MM_RSP_MEM);
252 gfx_info.IMEM = (unsigned char *)mem_base_u32(g_mem_base, MM_RSP_MEM + 0x1000);
253 gfx_info.MI_INTR_REG = &(g_dev.mi.regs[MI_INTR_REG]);
254 gfx_info.DPC_START_REG = &(g_dev.dp.dpc_regs[DPC_START_REG]);
255 gfx_info.DPC_END_REG = &(g_dev.dp.dpc_regs[DPC_END_REG]);
256 gfx_info.DPC_CURRENT_REG = &(g_dev.dp.dpc_regs[DPC_CURRENT_REG]);
257 gfx_info.DPC_STATUS_REG = &(g_dev.dp.dpc_regs[DPC_STATUS_REG]);
258 gfx_info.DPC_CLOCK_REG = &(g_dev.dp.dpc_regs[DPC_CLOCK_REG]);
259 gfx_info.DPC_BUFBUSY_REG = &(g_dev.dp.dpc_regs[DPC_BUFBUSY_REG]);
260 gfx_info.DPC_PIPEBUSY_REG = &(g_dev.dp.dpc_regs[DPC_PIPEBUSY_REG]);
261 gfx_info.DPC_TMEM_REG = &(g_dev.dp.dpc_regs[DPC_TMEM_REG]);
262 gfx_info.VI_STATUS_REG = &(g_dev.vi.regs[VI_STATUS_REG]);
263 gfx_info.VI_ORIGIN_REG = &(g_dev.vi.regs[VI_ORIGIN_REG]);
264 gfx_info.VI_WIDTH_REG = &(g_dev.vi.regs[VI_WIDTH_REG]);
265 gfx_info.VI_INTR_REG = &(g_dev.vi.regs[VI_V_INTR_REG]);
266 gfx_info.VI_V_CURRENT_LINE_REG = &(g_dev.vi.regs[VI_CURRENT_REG]);
267 gfx_info.VI_TIMING_REG = &(g_dev.vi.regs[VI_BURST_REG]);
268 gfx_info.VI_V_SYNC_REG = &(g_dev.vi.regs[VI_V_SYNC_REG]);
269 gfx_info.VI_H_SYNC_REG = &(g_dev.vi.regs[VI_H_SYNC_REG]);
270 gfx_info.VI_LEAP_REG = &(g_dev.vi.regs[VI_LEAP_REG]);
271 gfx_info.VI_H_START_REG = &(g_dev.vi.regs[VI_H_START_REG]);
272 gfx_info.VI_V_START_REG = &(g_dev.vi.regs[VI_V_START_REG]);
273 gfx_info.VI_V_BURST_REG = &(g_dev.vi.regs[VI_V_BURST_REG]);
274 gfx_info.VI_X_SCALE_REG = &(g_dev.vi.regs[VI_X_SCALE_REG]);
275 gfx_info.VI_Y_SCALE_REG = &(g_dev.vi.regs[VI_Y_SCALE_REG]);
276 gfx_info.CheckInterrupts = EmptyFunc;
277
278 gfx_info.version = 2; //Version 2 added SP_STATUS_REG and RDRAM_SIZE
279 gfx_info.SP_STATUS_REG = &g_dev.sp.regs[SP_STATUS_REG];
280 gfx_info.RDRAM_SIZE = (unsigned int*) &g_dev.rdram.dram_size;
281
282 /* call the audio plugin */
283 if (!gfx.initiateGFX(gfx_info))
284 return M64ERR_PLUGIN_FAIL;
285
286 return M64ERR_SUCCESS;
287}
288
289static void plugin_disconnect_audio(void)
290{
291 audio = dummy_audio;
292 l_AudioAttached = 0;
293}
294
295static m64p_error plugin_connect_audio(m64p_dynlib_handle plugin_handle)
296{
297 /* attach the Audio plugin function pointers */
298 if (plugin_handle != NULL)
299 {
300 m64p_plugin_type PluginType;
301 int PluginVersion, APIVersion;
302
303 if (l_AudioAttached)
304 return M64ERR_INVALID_STATE;
305
306 if (!GET_FUNC(ptr_PluginGetVersion, audio.getVersion, "PluginGetVersion") ||
307 !GET_FUNC(ptr_AiDacrateChanged, audio.aiDacrateChanged, "AiDacrateChanged") ||
308 !GET_FUNC(ptr_AiLenChanged, audio.aiLenChanged, "AiLenChanged") ||
309 !GET_FUNC(ptr_InitiateAudio, audio.initiateAudio, "InitiateAudio") ||
310 !GET_FUNC(ptr_ProcessAList, audio.processAList, "ProcessAList") ||
311 !GET_FUNC(ptr_RomOpen, audio.romOpen, "RomOpen") ||
312 !GET_FUNC(ptr_RomClosed, audio.romClosed, "RomClosed") ||
313 !GET_FUNC(ptr_SetSpeedFactor, audio.setSpeedFactor, "SetSpeedFactor") ||
314 !GET_FUNC(ptr_VolumeUp, audio.volumeUp, "VolumeUp") ||
315 !GET_FUNC(ptr_VolumeDown, audio.volumeDown, "VolumeDown") ||
316 !GET_FUNC(ptr_VolumeGetLevel, audio.volumeGetLevel, "VolumeGetLevel") ||
317 !GET_FUNC(ptr_VolumeSetLevel, audio.volumeSetLevel, "VolumeSetLevel") ||
318 !GET_FUNC(ptr_VolumeMute, audio.volumeMute, "VolumeMute") ||
319 !GET_FUNC(ptr_VolumeGetString, audio.volumeGetString, "VolumeGetString"))
320 {
321 DebugMessage(M64MSG_ERROR, "broken Audio plugin; function(s) not found.");
322 plugin_disconnect_audio();
323 return M64ERR_INPUT_INVALID;
324 }
325
326 /* check the version info */
327 (*audio.getVersion)(&PluginType, &PluginVersion, &APIVersion, NULL, NULL);
328 if (PluginType != M64PLUGIN_AUDIO || (APIVersion & 0xffff0000) != (AUDIO_API_VERSION & 0xffff0000))
329 {
330 DebugMessage(M64MSG_ERROR, "incompatible Audio plugin");
331 plugin_disconnect_audio();
332 return M64ERR_INCOMPATIBLE;
333 }
334
335 l_AudioAttached = 1;
336 }
337 else
338 plugin_disconnect_audio();
339
340 return M64ERR_SUCCESS;
341}
342
343static m64p_error plugin_start_audio(void)
344{
345 /* fill in the AUDIO_INFO data structure */
346 audio_info.RDRAM = (unsigned char *)mem_base_u32(g_mem_base, MM_RDRAM_DRAM);
347 audio_info.DMEM = (unsigned char *)mem_base_u32(g_mem_base, MM_RSP_MEM);
348 audio_info.IMEM = (unsigned char *)mem_base_u32(g_mem_base, MM_RSP_MEM + 0x1000);
349 audio_info.MI_INTR_REG = &(g_dev.mi.regs[MI_INTR_REG]);
350 audio_info.AI_DRAM_ADDR_REG = &(g_dev.ai.regs[AI_DRAM_ADDR_REG]);
351 audio_info.AI_LEN_REG = &(g_dev.ai.regs[AI_LEN_REG]);
352 audio_info.AI_CONTROL_REG = &(g_dev.ai.regs[AI_CONTROL_REG]);
353 audio_info.AI_STATUS_REG = &dummy;
354 audio_info.AI_DACRATE_REG = &(g_dev.ai.regs[AI_DACRATE_REG]);
355 audio_info.AI_BITRATE_REG = &(g_dev.ai.regs[AI_BITRATE_REG]);
356 audio_info.CheckInterrupts = EmptyFunc;
357
358 /* call the audio plugin */
359 if (!audio.initiateAudio(audio_info))
360 return M64ERR_PLUGIN_FAIL;
361
362 return M64ERR_SUCCESS;
363}
364
365static void plugin_disconnect_input(void)
366{
367 input = dummy_input;
368 l_InputAttached = 0;
369}
370
371static m64p_error plugin_connect_input(m64p_dynlib_handle plugin_handle)
372{
373 /* attach the Input plugin function pointers */
374 if (plugin_handle != NULL)
375 {
376 m64p_plugin_type PluginType;
377 int PluginVersion, APIVersion;
378
379 if (l_InputAttached)
380 return M64ERR_INVALID_STATE;
381
382 if (!GET_FUNC(ptr_PluginGetVersion, input.getVersion, "PluginGetVersion") ||
383 !GET_FUNC(ptr_ControllerCommand, input.controllerCommand, "ControllerCommand") ||
384 !GET_FUNC(ptr_GetKeys, input.getKeys, "GetKeys") ||
385 !GET_FUNC(ptr_InitiateControllers, input.initiateControllers, "InitiateControllers") ||
386 !GET_FUNC(ptr_ReadController, input.readController, "ReadController") ||
387 !GET_FUNC(ptr_RomOpen, input.romOpen, "RomOpen") ||
388 !GET_FUNC(ptr_RomClosed, input.romClosed, "RomClosed") ||
389 !GET_FUNC(ptr_SDL_KeyDown, input.keyDown, "SDL_KeyDown") ||
390 !GET_FUNC(ptr_SDL_KeyUp, input.keyUp, "SDL_KeyUp"))
391 {
392 DebugMessage(M64MSG_ERROR, "broken Input plugin; function(s) not found.");
393 plugin_disconnect_input();
394 return M64ERR_INPUT_INVALID;
395 }
396
397 /* check the version info */
398 (*input.getVersion)(&PluginType, &PluginVersion, &APIVersion, NULL, NULL);
399 if (PluginType != M64PLUGIN_INPUT || (APIVersion & 0xffff0000) != (INPUT_API_VERSION & 0xffff0000) || APIVersion < 0x020100)
400 {
401 DebugMessage(M64MSG_ERROR, "incompatible Input plugin");
402 plugin_disconnect_input();
403 return M64ERR_INCOMPATIBLE;
404 }
405
406 if (!GET_FUNC(ptr_RenderCallback, input.renderCallback, "RenderCallback"))
407 {
408 DebugMessage(M64MSG_INFO, "input plugin did not specify a render callback; there will be no on screen display by the input plugin.");
409 }
410
411 l_InputAttached = 1;
412 }
413 else
414 plugin_disconnect_input();
415
416 return M64ERR_SUCCESS;
417}
418
419static m64p_error plugin_start_input(void)
420{
421 int i;
422
423 /* fill in the CONTROL_INFO data structure */
424 control_info.Controls = Controls;
425 for (i=0; i<4; i++)
426 {
427 Controls[i].Present = 0;
428 Controls[i].RawData = 0;
429 Controls[i].Plugin = PLUGIN_NONE;
430 }
431
432 /* call the input plugin */
433 input.initiateControllers(control_info);
434
435 return M64ERR_SUCCESS;
436}
437
438static void plugin_disconnect_rsp(void)
439{
440 rsp = dummy_rsp;
441 l_RspAttached = 0;
442}
443
444static m64p_error plugin_connect_rsp(m64p_dynlib_handle plugin_handle)
445{
446 /* attach the RSP plugin function pointers */
447 if (plugin_handle != NULL)
448 {
449 m64p_plugin_type PluginType;
450 int PluginVersion, APIVersion;
451
452 if (l_RspAttached)
453 return M64ERR_INVALID_STATE;
454
455 if (!GET_FUNC(ptr_PluginGetVersion, rsp.getVersion, "PluginGetVersion") ||
456 !GET_FUNC(ptr_DoRspCycles, rsp.doRspCycles, "DoRspCycles") ||
457 !GET_FUNC(ptr_InitiateRSP, rsp.initiateRSP, "InitiateRSP") ||
458 !GET_FUNC(ptr_RomClosed, rsp.romClosed, "RomClosed"))
459 {
460 DebugMessage(M64MSG_ERROR, "broken RSP plugin; function(s) not found.");
461 plugin_disconnect_rsp();
462 return M64ERR_INPUT_INVALID;
463 }
464
465 /* check the version info */
466 (*rsp.getVersion)(&PluginType, &PluginVersion, &APIVersion, NULL, NULL);
467 if (PluginType != M64PLUGIN_RSP || (APIVersion & 0xffff0000) != (RSP_API_VERSION & 0xffff0000))
468 {
469 DebugMessage(M64MSG_ERROR, "incompatible RSP plugin");
470 plugin_disconnect_rsp();
471 return M64ERR_INCOMPATIBLE;
472 }
473
474 l_RspAttached = 1;
475 }
476 else
477 plugin_disconnect_rsp();
478
479 return M64ERR_SUCCESS;
480}
481
482static m64p_error plugin_start_rsp(void)
483{
484 /* fill in the RSP_INFO data structure */
485 rsp_info.RDRAM = (unsigned char *)mem_base_u32(g_mem_base, MM_RDRAM_DRAM);
486 rsp_info.DMEM = (unsigned char *)mem_base_u32(g_mem_base, MM_RSP_MEM);
487 rsp_info.IMEM = (unsigned char *)mem_base_u32(g_mem_base, MM_RSP_MEM + 0x1000);
488 rsp_info.MI_INTR_REG = &g_dev.mi.regs[MI_INTR_REG];
489 rsp_info.SP_MEM_ADDR_REG = &g_dev.sp.regs[SP_MEM_ADDR_REG];
490 rsp_info.SP_DRAM_ADDR_REG = &g_dev.sp.regs[SP_DRAM_ADDR_REG];
491 rsp_info.SP_RD_LEN_REG = &g_dev.sp.regs[SP_RD_LEN_REG];
492 rsp_info.SP_WR_LEN_REG = &g_dev.sp.regs[SP_WR_LEN_REG];
493 rsp_info.SP_STATUS_REG = &g_dev.sp.regs[SP_STATUS_REG];
494 rsp_info.SP_DMA_FULL_REG = &g_dev.sp.regs[SP_DMA_FULL_REG];
495 rsp_info.SP_DMA_BUSY_REG = &g_dev.sp.regs[SP_DMA_BUSY_REG];
496 rsp_info.SP_PC_REG = &g_dev.sp.regs2[SP_PC_REG];
497 rsp_info.SP_SEMAPHORE_REG = &g_dev.sp.regs[SP_SEMAPHORE_REG];
498 rsp_info.DPC_START_REG = &g_dev.dp.dpc_regs[DPC_START_REG];
499 rsp_info.DPC_END_REG = &g_dev.dp.dpc_regs[DPC_END_REG];
500 rsp_info.DPC_CURRENT_REG = &g_dev.dp.dpc_regs[DPC_CURRENT_REG];
501 rsp_info.DPC_STATUS_REG = &g_dev.dp.dpc_regs[DPC_STATUS_REG];
502 rsp_info.DPC_CLOCK_REG = &g_dev.dp.dpc_regs[DPC_CLOCK_REG];
503 rsp_info.DPC_BUFBUSY_REG = &g_dev.dp.dpc_regs[DPC_BUFBUSY_REG];
504 rsp_info.DPC_PIPEBUSY_REG = &g_dev.dp.dpc_regs[DPC_PIPEBUSY_REG];
505 rsp_info.DPC_TMEM_REG = &g_dev.dp.dpc_regs[DPC_TMEM_REG];
506 rsp_info.CheckInterrupts = EmptyFunc;
507 rsp_info.ProcessDlistList = gfx.processDList;
508 rsp_info.ProcessAlistList = audio.processAList;
509 rsp_info.ProcessRdpList = gfx.processRDPList;
510 rsp_info.ShowCFB = gfx.showCFB;
511
512 /* call the RSP plugin */
513 rsp.initiateRSP(rsp_info, NULL);
514
515 return M64ERR_SUCCESS;
516}
517
518/* global functions */
519m64p_error plugin_connect(m64p_plugin_type type, m64p_dynlib_handle plugin_handle)
520{
521 switch(type)
522 {
523 case M64PLUGIN_GFX:
524 if (plugin_handle != NULL && (l_AudioAttached || l_InputAttached || l_RspAttached))
525 DebugMessage(M64MSG_WARNING, "Front-end bug: plugins are attached in wrong order.");
526 return plugin_connect_gfx(plugin_handle);
527 case M64PLUGIN_AUDIO:
528 if (plugin_handle != NULL && (l_InputAttached || l_RspAttached))
529 DebugMessage(M64MSG_WARNING, "Front-end bug: plugins are attached in wrong order.");
530 return plugin_connect_audio(plugin_handle);
531 case M64PLUGIN_INPUT:
532 if (plugin_handle != NULL && (l_RspAttached))
533 DebugMessage(M64MSG_WARNING, "Front-end bug: plugins are attached in wrong order.");
534 return plugin_connect_input(plugin_handle);
535 case M64PLUGIN_RSP:
536 return plugin_connect_rsp(plugin_handle);
537 default:
538 return M64ERR_INPUT_INVALID;
539 }
540
541 return M64ERR_INTERNAL;
542}
543
544m64p_error plugin_start(m64p_plugin_type type)
545{
546 switch(type)
547 {
548 case M64PLUGIN_RSP:
549 return plugin_start_rsp();
550 case M64PLUGIN_GFX:
551 return plugin_start_gfx();
552 case M64PLUGIN_AUDIO:
553 return plugin_start_audio();
554 case M64PLUGIN_INPUT:
555 return plugin_start_input();
556 default:
557 return M64ERR_INPUT_INVALID;
558 }
559
560 return M64ERR_INTERNAL;
561}
562
563m64p_error plugin_check(void)
564{
565 if (!l_GfxAttached)
566 DebugMessage(M64MSG_WARNING, "No video plugin attached. There will be no video output.");
567 if (!l_RspAttached)
568 DebugMessage(M64MSG_WARNING, "No RSP plugin attached. The video output will be corrupted.");
569 if (!l_AudioAttached)
570 DebugMessage(M64MSG_WARNING, "No audio plugin attached. There will be no sound output.");
571 if (!l_InputAttached)
572 DebugMessage(M64MSG_WARNING, "No input plugin attached. You won't be able to control the game.");
573
574 return M64ERR_SUCCESS;
575}
576
577