diff --git a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-API-v1.0.mediawiki b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-API-v1.0.mediawiki
index 5b117a2..ab8e50a 100644
--- a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-API-v1.0.mediawiki
+++ b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-API-v1.0.mediawiki
@@ -53,8 +53,13 @@ These are the debugger functions, which are also called only from the front-end.
These configuration functions are exported from the core library and are used by the core and the plugins to store all of their persistent configuration parameters. The front-end application may also use these functions to store its configuration options, or it may use a different mechanism. This section also contains two Operating System Abstraction functions. These functions are used by the core, plugins, and the front-end to find full filepaths to shared data files and user-specific data files.
+
== Plugin API ==
=== [[Mupen64Plus v2.0 Plugin API|Plugin API]] ===
This section lists all of the functions which are exported by the plugins. The front-end application should only use the PluginStartup, PluginShutdown, and PluginGetVersion functions. All other functions will only be called from the core.
+== Netplay ==
+
+Starting with version 2.6, Mupen64Plus has support for playing games with multiple players over a network. To support this, a few new command types were added to the CoreDoCommand function, as documented in [[Mupen64Plus v2.0 Core Front-End|front-end functions]]. The core handles the network I/O on its own, and the interface for handling netplay from the perspective of the front-end is very high level. The design of the netplay feature is such that a separate piece of server software (not part of Mupen64Plus) is also required. An example of such a server program is available [https://github.com/loganmc10/m64p-netplay-server here] from [https://github.com/loganmc10 loganmc10] on github. A description of the network protocol which is used for communication between the client emulators and the netplay server is available [[Mupen64Plus v2.0 Core Netplay Protocol|here]].
+
diff --git a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-Front-End.mediawiki b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-Front-End.mediawiki
index 4138307..f2dba4e 100644
--- a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-Front-End.mediawiki
+++ b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Core-Front-End.mediawiki
@@ -188,7 +188,7 @@ Most libmupen64plus functions return an m64p_error return code, which i
|-
|M64CMD_SET_MEDIA_LOADER
|This command allow frontends to register their media (such as GameBoy cartridge or 64DD disk) loading functions. These functions will be called appropriately by the core at startup and when a new media is inserted.
-|'''ParamInt''' must be sizeof(m64p_media_loader).'''
ParamPtr'''A pointer to the m64p_media_loader to register, cannot be NULL.
+|'''ParamInt''' must be sizeof(m64p_media_loader).'''
ParamPtr''' A pointer to the m64p_media_loader to register, cannot be NULL.
|None
|-
|M64CMD_NETPLAY_INIT
@@ -210,6 +210,11 @@ Most libmupen64plus functions return an m64p_error return code, which i
|Closes the netplay subsystem, as well as any connections to the netplay server.
|N/A
|None
+|-
+|M64CMD_PIF_OPEN
+|This will cause the core to read in a binary PIF image provided by the front-end.
+|'''ParamInt''' must be 2048.'''
ParamPtr''' Pointer to the uncompressed PIF image in memory.
+|The emulator cannot be currently running.
|}
diff --git a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Netplay-API.mediawiki b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Netplay-API.mediawiki
index 99f4d87..6f085a0 100644
--- a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Netplay-API.mediawiki
+++ b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-Netplay-API.mediawiki
@@ -1,6 +1,8 @@
-= Mupen64Plus Netplay API =
+[[Mupen64Plus v2.0 Core API v1.0|Mupen64Plus v2.0 API]]
-The Mupen64Plus netplay API is based on a client/server architecture. It supports syncing core settings and save files. It also supports raw input (tested with Raphnet input plugin).
+= Mupen64Plus Netplay Protocol =
+
+The Mupen64Plus netplay protocol is based on a client/server architecture. It supports syncing core settings and save files. It also supports raw input (tested with Raphnet input plugin).
TCP is used to deal with player registration, and syncing settings and saves.
UDP is used to transfer key input data.
Data is sent in Network Byte Order (Big Endian).
@@ -111,3 +113,4 @@ The server is responsible for maintaining healthy buffers and also for detecting
*** byte[0-3] = player registration ID
*** byte[4] = player plugin
*** byte[5] = whether the player is using raw data input
+
diff --git a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-headers.mediawiki b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-headers.mediawiki
index c2f3f97..39ada3d 100644
--- a/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-headers.mediawiki
+++ b/mupen64plus-core/doc/emuwiki-api-doc/Mupen64Plus-v2.0-headers.mediawiki
@@ -132,7 +132,8 @@
M64CMD_READ_SCREEN,
M64CMD_RESET,
M64CMD_ADVANCE_FRAME,
- M64CMD_SET_MEDIA_LOADER
+ M64CMD_SET_MEDIA_LOADER,
+ M64CMD_PIF_OPEN
} m64p_command;
typedef struct {
diff --git a/mupen64plus-core/src/api/frontend.c b/mupen64plus-core/src/api/frontend.c
index 702df80..ce00482 100644
--- a/mupen64plus-core/src/api/frontend.c
+++ b/mupen64plus-core/src/api/frontend.c
@@ -196,6 +196,12 @@ EXPORT m64p_error CALL CoreDoCommand(m64p_command Command, int ParamInt, void *P
cheat_delete_all(&g_cheat_ctx);
cheat_uninit(&g_cheat_ctx);
return close_rom();
+ case M64CMD_PIF_OPEN:
+ if (g_EmulatorRunning)
+ return M64ERR_INVALID_STATE;
+ if (ParamPtr == NULL || ParamInt != 2048)
+ return M64ERR_INPUT_ASSERT;
+ return open_pif((const unsigned char *) ParamPtr, ParamInt);
case M64CMD_ROM_GET_HEADER:
if (!l_ROMOpen)
return M64ERR_INVALID_STATE;
diff --git a/mupen64plus-core/src/api/m64p_types.h b/mupen64plus-core/src/api/m64p_types.h
index f073e3f..6e805d8 100644
--- a/mupen64plus-core/src/api/m64p_types.h
+++ b/mupen64plus-core/src/api/m64p_types.h
@@ -166,7 +166,8 @@ typedef enum {
M64CMD_NETPLAY_INIT,
M64CMD_NETPLAY_CONTROL_PLAYER,
M64CMD_NETPLAY_GET_VERSION,
- M64CMD_NETPLAY_CLOSE
+ M64CMD_NETPLAY_CLOSE,
+ M64CMD_PIF_OPEN
} m64p_command;
typedef struct {
diff --git a/mupen64plus-core/src/device/device.c b/mupen64plus-core/src/device/device.c
index c62d632..a8041a9 100644
--- a/mupen64plus-core/src/device/device.c
+++ b/mupen64plus-core/src/device/device.c
@@ -85,6 +85,7 @@ void init_device(struct device* dev,
unsigned int count_per_op,
int no_compiled_jump,
int randomize_interrupt,
+ uint32_t start_address,
/* ai */
void* aout, const struct audio_out_backend_interface* iaout,
/* si */
@@ -150,7 +151,7 @@ void init_device(struct device* dev,
{ A(MM_DD_ROM, 0x1ffffff), M64P_MEM_NOTHING, { NULL, RW(open_bus) } },
{ A(MM_DOM2_ADDR2, 0x1ffff), M64P_MEM_FLASHRAMSTAT, { &dev->cart, RW(cart_dom2) } },
{ A(MM_CART_ROM, rom_size-1), M64P_MEM_ROM, { &dev->cart.cart_rom, RW(cart_rom) } },
- { A(MM_PIF_MEM, 0xffff), M64P_MEM_PIF, { &dev->pif, RW(pif_ram) } }
+ { A(MM_PIF_MEM, 0xffff), M64P_MEM_PIF, { &dev->pif, RW(pif_mem) } }
};
/* init and map DD if present */
@@ -176,7 +177,7 @@ void init_device(struct device* dev,
init_rdram(&dev->rdram, mem_base_u32(base, MM_RDRAM_DRAM), dram_size, &dev->r4300);
init_r4300(&dev->r4300, &dev->mem, &dev->mi, &dev->rdram, interrupt_handlers,
- emumode, count_per_op, no_compiled_jump, randomize_interrupt);
+ emumode, count_per_op, no_compiled_jump, randomize_interrupt, start_address);
init_rdp(&dev->dp, &dev->sp, &dev->mi, &dev->mem, &dev->rdram, &dev->r4300);
init_rsp(&dev->sp, mem_base_u32(base, MM_RSP_MEM), &dev->mi, &dev->dp, &dev->ri);
init_ai(&dev->ai, &dev->mi, &dev->ri, &dev->vi, aout, iaout);
diff --git a/mupen64plus-core/src/device/device.h b/mupen64plus-core/src/device/device.h
index 0ecaeda..e99ecb8 100644
--- a/mupen64plus-core/src/device/device.h
+++ b/mupen64plus-core/src/device/device.h
@@ -120,6 +120,7 @@ void init_device(struct device* dev,
unsigned int count_per_op,
int no_compiled_jump,
int randomize_interrupt,
+ uint32_t start_address,
/* ai */
void* aout, const struct audio_out_backend_interface* iaout,
/* si */
diff --git a/mupen64plus-core/src/device/pif/bootrom_hle.c b/mupen64plus-core/src/device/pif/bootrom_hle.c
index 6be4432..e748f03 100644
--- a/mupen64plus-core/src/device/pif/bootrom_hle.c
+++ b/mupen64plus-core/src/device/pif/bootrom_hle.c
@@ -47,6 +47,8 @@ static unsigned int get_tv_type(void)
void pif_bootrom_hle_execute(struct r4300_core* r4300)
{
+ if (r4300->start_address == 0xbfc00000) return;
+
uint32_t pif24;
unsigned int seed; /* seed (depends on CIC version) */
unsigned int rom_type; /* 0:Cart, 1:DD */
diff --git a/mupen64plus-core/src/device/pif/pif.c b/mupen64plus-core/src/device/pif/pif.c
index de93316..fc22c3b 100644
--- a/mupen64plus-core/src/device/pif/pif.c
+++ b/mupen64plus-core/src/device/pif/pif.c
@@ -132,6 +132,7 @@ void init_pif(struct pif* pif,
{
size_t i;
+ pif->base = pif_base;
pif->ram = pif_base + 0x7c0;
for (i = 0; i < PIF_CHANNELS_COUNT; ++i) {
@@ -282,34 +283,28 @@ void poweron_pif(struct pif* pif)
reset_pif(pif, 0); /* cold reset */
}
-void read_pif_ram(void* opaque, uint32_t address, uint32_t* value)
+void read_pif_mem(void* opaque, uint32_t address, uint32_t* value)
{
struct pif* pif = (struct pif*)opaque;
- uint32_t addr = pif_ram_address(address);
+ uint32_t addr = pif_address(address);
- if (addr >= PIF_RAM_SIZE)
- {
- DebugMessage(M64MSG_ERROR, "Invalid PIF address: %08" PRIX32, address);
- *value = 0;
- return;
- }
-
- memcpy(value, pif->ram + addr, sizeof(*value));
- *value = tohl(*value);
+ memcpy(value, pif->base + addr, sizeof(*value));
+ if (addr >= PIF_ROM_SIZE)
+ *value = tohl(*value);
}
-void write_pif_ram(void* opaque, uint32_t address, uint32_t value, uint32_t mask)
+void write_pif_mem(void* opaque, uint32_t address, uint32_t value, uint32_t mask)
{
struct pif* pif = (struct pif*)opaque;
- uint32_t addr = pif_ram_address(address);
+ uint32_t addr = pif_address(address);
- if (addr >= PIF_RAM_SIZE)
+ if (addr < PIF_ROM_SIZE)
{
- DebugMessage(M64MSG_ERROR, "Invalid PIF address: %08" PRIX32, address);
+ DebugMessage(M64MSG_ERROR, "Invalid write to PIF ROM: %08" PRIX32, address);
return;
}
- masked_write((uint32_t*)(&pif->ram[addr]), fromhl(value), fromhl(mask));
+ masked_write((uint32_t*)(&pif->base[addr]), fromhl(value), fromhl(mask));
pif->si->dma_dir = SI_DMA_WRITE;
@@ -357,10 +352,17 @@ void process_pif_ram(struct pif* pif)
clrmask |= 0x08;
}
+ if (flags & 0x30)
+ {
+ pif->ram[0x3f] = 0x80;
+ }
+
+#ifdef DEBUG_PIF
if (flags & 0xf4)
{
DebugMessage(M64MSG_ERROR, "error in process_pif_ram(): %" PRIX8, flags);
}
+#endif
pif->ram[0x3f] &= ~clrmask;
}
diff --git a/mupen64plus-core/src/device/pif/pif.h b/mupen64plus-core/src/device/pif/pif.h
index 39af816..4bdb090 100644
--- a/mupen64plus-core/src/device/pif/pif.h
+++ b/mupen64plus-core/src/device/pif/pif.h
@@ -53,6 +53,7 @@ size_t setup_pif_channel(struct pif_channel* channel, uint8_t* buf);
struct pif
{
+ uint8_t* base;
uint8_t* ram;
struct pif_channel channels[PIF_CHANNELS_COUNT];
@@ -62,9 +63,9 @@ struct pif
struct si_controller* si;
};
-static osal_inline uint32_t pif_ram_address(uint32_t address)
+static osal_inline uint32_t pif_address(uint32_t address)
{
- return ((address & 0xfffc) - PIF_ROM_SIZE);
+ return (address & 0xfffc);
}
@@ -82,8 +83,8 @@ void reset_pif(struct pif* pif, unsigned int reset_type);
void setup_channels_format(struct pif* pif);
-void read_pif_ram(void* opaque, uint32_t address, uint32_t* value);
-void write_pif_ram(void* opaque, uint32_t address, uint32_t value, uint32_t mask);
+void read_pif_mem(void* opaque, uint32_t address, uint32_t* value);
+void write_pif_mem(void* opaque, uint32_t address, uint32_t value, uint32_t mask);
void process_pif_ram(struct pif* pif);
void update_pif_ram(struct pif* pif);
diff --git a/mupen64plus-core/src/device/r4300/interrupt.c b/mupen64plus-core/src/device/r4300/interrupt.c
index 3ed5ece..06b140d 100644
--- a/mupen64plus-core/src/device/r4300/interrupt.c
+++ b/mupen64plus-core/src/device/r4300/interrupt.c
@@ -468,8 +468,8 @@ void nmi_int_handler(void* opaque)
r4300->recomp.dyna_interp = 0;
#endif
// set next instruction address to reset vector
- r4300->cp0.last_addr = UINT32_C(0xa4000040);
- generic_jump_to(r4300, UINT32_C(0xa4000040));
+ r4300->cp0.last_addr = r4300->start_address;
+ generic_jump_to(r4300, r4300->start_address);
}
@@ -496,7 +496,7 @@ void reset_hard_handler(void* opaque)
poweron_device(dev);
pif_bootrom_hle_execute(r4300);
- r4300->cp0.last_addr = UINT32_C(0xa4000040);
+ r4300->cp0.last_addr = r4300->start_address;
*r4300_cp0_next_interrupt(&r4300->cp0) = 624999;
*r4300_cp0_cycle_count(&r4300->cp0) = 0;
init_interrupt(&r4300->cp0);
diff --git a/mupen64plus-core/src/device/r4300/pure_interp.c b/mupen64plus-core/src/device/r4300/pure_interp.c
index c7b71c4..09d1a4c 100644
--- a/mupen64plus-core/src/device/r4300/pure_interp.c
+++ b/mupen64plus-core/src/device/r4300/pure_interp.c
@@ -713,7 +713,7 @@ void run_pure_interpreter(struct r4300_core* r4300)
{
*r4300_stop(r4300) = 0;
*r4300_pc_struct(r4300) = &r4300->interp_PC;
- *r4300_pc(r4300) = r4300->cp0.last_addr = 0xa4000040;
+ *r4300_pc(r4300) = r4300->cp0.last_addr = r4300->start_address;
while (!*r4300_stop(r4300))
{
diff --git a/mupen64plus-core/src/device/r4300/r4300_core.c b/mupen64plus-core/src/device/r4300/r4300_core.c
index 714bc67..f6a3d6d 100644
--- a/mupen64plus-core/src/device/r4300/r4300_core.c
+++ b/mupen64plus-core/src/device/r4300/r4300_core.c
@@ -41,7 +41,7 @@
#include
void init_r4300(struct r4300_core* r4300, struct memory* mem, struct mi_controller* mi, struct rdram* rdram, const struct interrupt_handler* interrupt_handlers,
- unsigned int emumode, unsigned int count_per_op, int no_compiled_jump, int randomize_interrupt)
+ unsigned int emumode, unsigned int count_per_op, int no_compiled_jump, int randomize_interrupt, uint32_t start_address)
{
struct new_dynarec_hot_state* new_dynarec_hot_state =
#ifdef NEW_DYNAREC
@@ -62,6 +62,7 @@ void init_r4300(struct r4300_core* r4300, struct memory* mem, struct mi_controll
r4300->mi = mi;
r4300->rdram = rdram;
r4300->randomize_interrupt = randomize_interrupt;
+ r4300->start_address = start_address;
srand((unsigned int) time(NULL));
}
@@ -182,7 +183,7 @@ void run_r4300(struct r4300_core* r4300)
r4300->cached_interp.recompile_block = cached_interp_recompile_block;
init_blocks(&r4300->cached_interp);
- cached_interpreter_jump_to(r4300, UINT32_C(0xa4000040));
+ cached_interpreter_jump_to(r4300, r4300->start_address);
/* Prevent segfault on failed cached_interpreter_jump_to */
if (!r4300->cached_interp.actual->block) {
diff --git a/mupen64plus-core/src/device/r4300/r4300_core.h b/mupen64plus-core/src/device/r4300/r4300_core.h
index ff9b8f7..d79100e 100644
--- a/mupen64plus-core/src/device/r4300/r4300_core.h
+++ b/mupen64plus-core/src/device/r4300/r4300_core.h
@@ -195,6 +195,8 @@ struct r4300_core
struct rdram* rdram;
uint32_t randomize_interrupt;
+
+ uint32_t start_address;
};
#define R4300_KSEG0 UINT32_C(0x80000000)
@@ -209,7 +211,7 @@ struct r4300_core
offsetof(struct new_dynarec_hot_state, regs))
#endif
-void init_r4300(struct r4300_core* r4300, struct memory* mem, struct mi_controller* mi, struct rdram* rdram, const struct interrupt_handler* interrupt_handlers, unsigned int emumode, unsigned int count_per_op, int no_compiled_jump, int randomize_interrupt);
+void init_r4300(struct r4300_core* r4300, struct memory* mem, struct mi_controller* mi, struct rdram* rdram, const struct interrupt_handler* interrupt_handlers, unsigned int emumode, unsigned int count_per_op, int no_compiled_jump, int randomize_interrupt, uint32_t start_address);
void poweron_r4300(struct r4300_core* r4300);
void run_r4300(struct r4300_core* r4300);
diff --git a/mupen64plus-core/src/device/r4300/recomp.c b/mupen64plus-core/src/device/r4300/recomp.c
index cd5be0c..f4f0008 100644
--- a/mupen64plus-core/src/device/r4300/recomp.c
+++ b/mupen64plus-core/src/device/r4300/recomp.c
@@ -891,7 +891,7 @@ void dynarec_setup_code(void)
/* The dynarec jumps here after we call dyna_start and it prepares
* Here we need to prepare the initial code block and jump to it
*/
- dynarec_jump_to(r4300, UINT32_C(0xa4000040));
+ dynarec_jump_to(r4300, r4300->start_address);
/* Prevent segfault on failed dynarec_jump_to */
if (!r4300->cached_interp.actual->block || !r4300->cached_interp.actual->code) {
diff --git a/mupen64plus-core/src/main/main.c b/mupen64plus-core/src/main/main.c
index c747d54..5bf3dbd 100644
--- a/mupen64plus-core/src/main/main.c
+++ b/mupen64plus-core/src/main/main.c
@@ -107,6 +107,8 @@ struct cheat_ctx g_cheat_ctx;
*/
void* g_mem_base = NULL;
+uint32_t g_start_address = UINT32_C(0xa4000040);
+
struct device g_dev;
m64p_media_loader g_media_loader;
@@ -1573,6 +1575,7 @@ m64p_error main_run(void)
count_per_op,
no_compiled_jump,
randomize_interrupt,
+ g_start_address,
&g_dev.ai, &g_iaudio_out_backend_plugin_compat,
si_dma_duration,
rdram_size,
@@ -1743,3 +1746,34 @@ void main_stop(void)
}
#endif
}
+
+m64p_error open_pif(const unsigned char* pifimage, unsigned int size)
+{
+ md5_byte_t pif_ntsc_md5[] = {0x49, 0x21, 0xD5, 0xF2, 0x16, 0x5D, 0xEE, 0x6E, 0x24, 0x96, 0xF4, 0x38, 0x8C, 0x4C, 0x81, 0xDA};
+ md5_byte_t pif_pal_md5[] = {0x2B, 0x6E, 0xEC, 0x58, 0x6F, 0xAA, 0x43, 0xF3, 0x46, 0x23, 0x33, 0xB8, 0x44, 0x83, 0x45, 0x54};
+
+ uint32_t *dst32 = mem_base_u32(g_mem_base, MM_PIF_MEM);
+ uint32_t *src32 = (uint32_t*) pifimage;
+ md5_state_t state;
+ md5_byte_t digest[16];
+
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t*)pifimage, size);
+ md5_finish(&state, digest);
+
+ if (memcmp(digest, pif_ntsc_md5, 16) == 0)
+ DebugMessage(M64MSG_INFO, "Using NTSC PIF ROM");
+ else if (memcmp(digest, pif_pal_md5, 16) == 0)
+ DebugMessage(M64MSG_INFO, "Using PAL PIF ROM");
+ else
+ {
+ DebugMessage(M64MSG_ERROR, "Invalid PIF ROM");
+ return M64ERR_INPUT_INVALID;
+ }
+
+ for (int i = 0; i < size; i += 4)
+ *dst32++ = big32(*src32++);
+
+ g_start_address = UINT32_C(0xbfc00000);
+ return M64ERR_SUCCESS;
+}
diff --git a/mupen64plus-core/src/main/main.h b/mupen64plus-core/src/main/main.h
index dcfcef0..68efd5e 100644
--- a/mupen64plus-core/src/main/main.h
+++ b/mupen64plus-core/src/main/main.h
@@ -99,5 +99,7 @@ int main_volume_get_muted(void);
m64p_error main_reset(int do_hard_reset);
+m64p_error open_pif(const unsigned char* pifimage, unsigned int size);
+
#endif /* __MAIN_H__ */