mirror of
https://github.com/n64dev/cen64.git
synced 2024-06-22 22:12:45 -04:00
pak: implement mempak and stub out rumble and transfer pak
This commit is contained in:
parent
701388da13
commit
e7bbc4f822
|
@ -337,6 +337,7 @@ set(RSP_SOURCES
|
|||
set(SI_SOURCES
|
||||
${PROJECT_SOURCE_DIR}/si/cic.c
|
||||
${PROJECT_SOURCE_DIR}/si/controller.c
|
||||
${PROJECT_SOURCE_DIR}/si/pak.c
|
||||
)
|
||||
|
||||
set(VI_SOURCES
|
||||
|
|
27
cen64.c
27
cen64.c
|
@ -24,13 +24,16 @@
|
|||
cen64_cold static int load_roms(const char *ddipl_path, const char *ddrom_path,
|
||||
const char *pifrom_path, const char *cart_path, struct rom_file *ddipl,
|
||||
struct rom_file *ddrom, struct rom_file *pifrom, struct rom_file *cart);
|
||||
cen64_cold static int load_paks(struct controller *controller);
|
||||
|
||||
cen64_cold static int run_device(struct cen64_device *device);
|
||||
cen64_cold static CEN64_THREAD_RETURN_TYPE run_device_thread(void *opaque);
|
||||
|
||||
// Called when another simulation instance is desired.
|
||||
int cen64_main(int argc, const char **argv) {
|
||||
struct controller controller[4] = { { NULL, }, };
|
||||
struct cen64_options options = default_cen64_options;
|
||||
options.controller = controller;
|
||||
struct rom_file ddipl, ddrom, pifrom, cart;
|
||||
struct save_file eeprom;
|
||||
struct cen64_mem cen64_device_mem;
|
||||
|
@ -71,6 +74,11 @@ int cen64_main(int argc, const char **argv) {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (load_paks(controller)) {
|
||||
cen64_alloc_cleanup();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (options.eeprom_path != NULL &&
|
||||
open_save_file(options.eeprom_path, options.eeprom_size, &eeprom, NULL)) {
|
||||
cen64_alloc_cleanup();
|
||||
|
@ -86,7 +94,7 @@ int cen64_main(int argc, const char **argv) {
|
|||
else {
|
||||
device = (struct cen64_device *) cen64_device_mem.ptr;
|
||||
|
||||
if (device_create(device, &ddipl, &ddrom, &pifrom, &cart, &eeprom) == NULL) {
|
||||
if (device_create(device, &ddipl, &ddrom, &pifrom, &cart, &eeprom, controller) == NULL) {
|
||||
printf("Failed to create a device.\n");
|
||||
status = EXIT_FAILURE;
|
||||
}
|
||||
|
@ -163,6 +171,23 @@ int load_roms(const char *ddipl_path, const char *ddrom_path,
|
|||
return 0;
|
||||
}
|
||||
|
||||
cen64_cold int load_paks(struct controller *controller) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
if (controller[i].pak == PAK_MEM && controller[i].mempak_path != NULL) {
|
||||
int created = 0;
|
||||
if (open_save_file(controller[i].mempak_path, MEMPAK_SIZE, &controller[i].mempak_save, &created) != 0) {
|
||||
printf("Can't open mempak file %s\n", controller[i].mempak_path);
|
||||
return -1;
|
||||
}
|
||||
if (created)
|
||||
controller_pak_format(controller[i].mempak_save.ptr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Spins the device until an exit request is received.
|
||||
int run_device(struct cen64_device *device) {
|
||||
cen64_thread thread;
|
||||
|
|
|
@ -35,7 +35,7 @@ cen64_flatten cen64_hot static int device_spin(struct cen64_device *device);
|
|||
struct cen64_device *device_create(struct cen64_device *device,
|
||||
const struct rom_file *ddipl, const struct rom_file *ddrom,
|
||||
const struct rom_file *pifrom, const struct rom_file *cart,
|
||||
const struct save_file *eeprom) {
|
||||
const struct save_file *eeprom, const struct controller *controller) {
|
||||
|
||||
// Initialize the bus.
|
||||
device->bus.ai = &device->ai;
|
||||
|
@ -82,7 +82,8 @@ struct cen64_device *device_create(struct cen64_device *device,
|
|||
|
||||
// Initialize the SI.
|
||||
if (si_init(&device->si, &device->bus, pifrom->ptr,
|
||||
cart->ptr, ddipl->ptr != NULL, eeprom->ptr, eeprom->size)) {
|
||||
cart->ptr, ddipl->ptr != NULL, eeprom->ptr, eeprom->size,
|
||||
controller)) {
|
||||
debug("create_device: Failed to initialize the SI.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ cen64_cold void device_destroy(struct cen64_device *device);
|
|||
cen64_cold struct cen64_device *device_create(struct cen64_device *device,
|
||||
const struct rom_file *ddipl, const struct rom_file *ddrom,
|
||||
const struct rom_file *pifrom, const struct rom_file *cart,
|
||||
const struct save_file *eeprom);
|
||||
const struct save_file *eeprom, const struct controller *controller);
|
||||
|
||||
cen64_cold void device_exit(struct bus_controller *bus);
|
||||
cen64_cold void device_run(struct cen64_device *device);
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
|
||||
#include "common.h"
|
||||
#include "options.h"
|
||||
#include "si/pak.h"
|
||||
|
||||
static int parse_controller_options(const char *str, int *num, struct controller *opt);
|
||||
|
||||
const struct cen64_options default_cen64_options = {
|
||||
NULL, // ddipl_path
|
||||
|
@ -19,6 +22,7 @@ const struct cen64_options default_cen64_options = {
|
|||
NULL, // debugger_addr
|
||||
NULL, // eeprom_path
|
||||
0, // eeprom_size
|
||||
{ { NULL, }, }, // controller
|
||||
#ifdef _WIN32
|
||||
false, // console
|
||||
#endif
|
||||
|
@ -91,6 +95,23 @@ int parse_options(struct cen64_options *options, int argc, const char *argv[]) {
|
|||
options->eeprom_size = 0x800; // 16 kbit
|
||||
}
|
||||
|
||||
else if (!strcmp(argv[i], "-controller")) {
|
||||
int num;
|
||||
struct controller opt = { NULL, };
|
||||
|
||||
if ((i + 1) >= (argc - 1)) {
|
||||
printf("-controller requires a controller description.\n\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!parse_controller_options(argv[++i], &num, &opt)) {
|
||||
printf("Incorrect option format\n\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
options->controller[num] = opt;
|
||||
}
|
||||
|
||||
// TODO: Handle this better.
|
||||
else
|
||||
break;
|
||||
|
@ -117,6 +138,51 @@ int parse_options(struct cen64_options *options, int argc, const char *argv[]) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int parse_controller_options(const char *str, int *num, struct controller *opt) {
|
||||
char *token;
|
||||
char mempak_path[4096];
|
||||
char *opt_string = strdup(str);
|
||||
|
||||
if (opt_string == NULL) {
|
||||
printf("Unable to dup a string. You're gonna have trouble running games.\n");
|
||||
exit(1);
|
||||
}
|
||||
*num = -1;
|
||||
|
||||
token = strtok(opt_string, ",");
|
||||
while (token != NULL) {
|
||||
if (sscanf(token, "num=%d", num) == 1)
|
||||
;
|
||||
else if (strcmp(token, "pak=rumble") == 0)
|
||||
opt->pak = PAK_RUMBLE;
|
||||
else if (sscanf(token, "mempak=%4095s", mempak_path) == 1)
|
||||
opt->pak = PAK_MEM;
|
||||
else {
|
||||
printf("Unrecognized controller option: %s\n", token);
|
||||
goto err;
|
||||
}
|
||||
// TODO transfer pak options
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
if (*num < 1 || *num > 4) {
|
||||
printf("Controller number invalid or unspecified.\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
--*num; // internally it's used as an index into an array
|
||||
mempak_path[4095] = '\0';
|
||||
opt->mempak_path = strdup(mempak_path);
|
||||
opt->present = 1;
|
||||
|
||||
free(opt_string);
|
||||
return 1;
|
||||
|
||||
err:
|
||||
free(opt_string);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Prints the command-line usage string.
|
||||
void print_command_line_usage(const char *invokation_string) {
|
||||
#ifdef _WIN32
|
||||
|
|
|
@ -22,6 +22,8 @@ struct cen64_options {
|
|||
const char *eeprom_path;
|
||||
size_t eeprom_size;
|
||||
|
||||
struct controller *controller;
|
||||
|
||||
#ifdef _WIN32
|
||||
bool console;
|
||||
#endif
|
||||
|
|
|
@ -38,7 +38,8 @@ static int eeprom_write(struct eeprom *eeprom, uint8_t *send_buf, uint8_t send_b
|
|||
// Initializes the SI.
|
||||
int si_init(struct si_controller *si, struct bus_controller *bus,
|
||||
const uint8_t *pif_rom, const uint8_t *cart_rom, bool dd_present,
|
||||
const uint8_t *eeprom, size_t eeprom_size) {
|
||||
const uint8_t *eeprom, size_t eeprom_size,
|
||||
const struct controller *controller) {
|
||||
uint32_t cic_seed;
|
||||
|
||||
si->bus = bus;
|
||||
|
@ -74,6 +75,9 @@ int si_init(struct si_controller *si, struct bus_controller *bus,
|
|||
si->eeprom.data = eeprom;
|
||||
si->eeprom.size = eeprom_size;
|
||||
|
||||
// controllers
|
||||
memcpy(si->controller, controller, sizeof(struct controller) * 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -90,14 +94,23 @@ int pif_perform_command(struct si_controller *si,
|
|||
case 0xFF:
|
||||
switch(channel) {
|
||||
case 0:
|
||||
// always return that controller 0 is connected so that users
|
||||
// who don't specify a controller on command line will still
|
||||
// have good experience
|
||||
recv_buf[0] = 0x05;
|
||||
recv_buf[1] = 0x00;
|
||||
recv_buf[2] = 0x01;
|
||||
recv_buf[2] = si->controller[channel].pak == PAK_NONE ? 0x00 : 0x01;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
if (si->controller[channel].present) {
|
||||
recv_buf[0] = 0x05;
|
||||
recv_buf[1] = 0x00;
|
||||
recv_buf[2] = si->controller[channel].pak == PAK_NONE ? 0x00 : 0x01;
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
|
||||
case 4:
|
||||
|
@ -136,28 +149,20 @@ int pif_perform_command(struct si_controller *si,
|
|||
|
||||
// Read from controller pak
|
||||
case 0x02:
|
||||
if (channel < 4) {
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
|
||||
if (channel < 4)
|
||||
return controller_pak_read(&si->controller[channel],
|
||||
send_buf, send_bytes, recv_buf, recv_bytes);
|
||||
else
|
||||
assert(0 && "Invalid channel for controller pak read");
|
||||
|
||||
return 1;
|
||||
|
||||
// Write to controller pak
|
||||
case 0x03:
|
||||
if (channel < 4) {
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
|
||||
if (channel < 4)
|
||||
return controller_pak_write(&si->controller[channel],
|
||||
send_buf, send_bytes, recv_buf, recv_bytes);
|
||||
else
|
||||
assert(0 && "Invalid channel for controller pak write");
|
||||
|
||||
return 1;
|
||||
|
||||
// EEPROM read
|
||||
case 0x04:
|
||||
if (channel != 4)
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#ifndef __si_controller_h__
|
||||
#define __si_controller_h__
|
||||
#include "common.h"
|
||||
#include "si/pak.h"
|
||||
|
||||
struct bus_controller *bus;
|
||||
|
||||
|
@ -40,11 +41,13 @@ struct si_controller {
|
|||
uint32_t pif_status;
|
||||
uint8_t input[4];
|
||||
struct eeprom eeprom;
|
||||
struct controller controller[4];
|
||||
};
|
||||
|
||||
cen64_cold int si_init(struct si_controller *si, struct bus_controller *bus,
|
||||
const uint8_t *pif_rom, const uint8_t *cart_rom, bool dd_present,
|
||||
const uint8_t *eeprom, size_t eeprom_size);
|
||||
const uint8_t *eeprom, size_t eeprom_size,
|
||||
const struct controller *controller);
|
||||
|
||||
int read_pif_ram(void *opaque, uint32_t address, uint32_t *word);
|
||||
int read_pif_rom(void *opaque, uint32_t address, uint32_t *word);
|
||||
|
|
124
si/pak.c
Normal file
124
si/pak.c
Normal file
|
@ -0,0 +1,124 @@
|
|||
//
|
||||
// si/pak.c: Controller pak routines
|
||||
//
|
||||
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
|
||||
// Copyright (C) 2016, Mike Ryan.
|
||||
//
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// 'LICENSE', which is part of this source code package.
|
||||
//
|
||||
|
||||
#include "pak.h"
|
||||
|
||||
static uint8_t controller_pak_crc(uint8_t *data);
|
||||
|
||||
int controller_pak_read(struct controller *controller,
|
||||
uint8_t *send_buf, uint8_t send_bytes,
|
||||
uint8_t *recv_buf, uint8_t recv_bytes) {
|
||||
|
||||
uint16_t address = send_buf[1] << 8 | send_buf[2];
|
||||
address &= ~0b11111; // lower 5 bits are address CRC
|
||||
// printf("read from %04x\n", address);
|
||||
|
||||
if (controller->pak == PAK_MEM) {
|
||||
if (address <= MEMPAK_SIZE - 0x20)
|
||||
memcpy(recv_buf, controller->mempak_save.ptr + address, 0x20);
|
||||
else
|
||||
assert(0 && "invalid mempak address");
|
||||
}
|
||||
|
||||
else {
|
||||
uint8_t peripheral = 0x00;
|
||||
if (controller->pak == PAK_RUMBLE)
|
||||
peripheral = 0x80;
|
||||
else if (controller->pak == PAK_TRANSFER)
|
||||
peripheral = 0x84;
|
||||
memset(recv_buf, peripheral, 0x20);
|
||||
}
|
||||
|
||||
recv_buf[0x20] = controller_pak_crc(recv_buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int controller_pak_write(struct controller *controller,
|
||||
uint8_t *send_buf, uint8_t send_bytes,
|
||||
uint8_t *recv_buf, uint8_t recv_bytes) {
|
||||
|
||||
uint16_t address = send_buf[1] << 8 | send_buf[2];
|
||||
address &= ~0b11111; // lower 5 bits are a checksum
|
||||
// printf("write to %04x\n", address);
|
||||
|
||||
if (address == 0x8000)
|
||||
; // initializing device, do nothing
|
||||
|
||||
else if (controller->pak == PAK_MEM) {
|
||||
if (address <= MEMPAK_SIZE - 0x20)
|
||||
memcpy(controller->mempak_save.ptr + address, send_buf + 3, 0x20);
|
||||
else
|
||||
assert(0 && "Attempt to write past end of mempak");
|
||||
}
|
||||
|
||||
else if (controller->pak == PAK_RUMBLE) {
|
||||
if (address == 0xC000) {
|
||||
if (send_buf[3] == 0x01)
|
||||
; // printf("Enable rumble\n");
|
||||
else
|
||||
; // printf("Disable rumble\n");
|
||||
}
|
||||
else
|
||||
; // printf("Unknown rumble address\n");
|
||||
}
|
||||
|
||||
recv_buf[0] = controller_pak_crc(send_buf+3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t controller_pak_crc(uint8_t *data) {
|
||||
size_t i;
|
||||
int mask;
|
||||
uint8_t crc = 0;
|
||||
|
||||
for (i = 0; i <= 0x20; ++i) {
|
||||
for (mask = 0x80; mask >= 1; mask >>= 1) {
|
||||
uint8_t xor_tap = (crc & 0x80) ? 0x85 : 0x00;
|
||||
crc <<= 1;
|
||||
if (i < 0x20 && (data[i] & mask))
|
||||
crc |= 1;
|
||||
crc ^= xor_tap;
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
void controller_pak_format(uint8_t *ptr) {
|
||||
off_t pos;
|
||||
|
||||
static uint8_t init[] = {
|
||||
0x81, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x1A, 0x5F, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0x66, 0x25, 0x99, 0xCD,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x1A, 0x5F, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0x66, 0x25, 0x99, 0xCD,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x1A, 0x5F, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0x66, 0x25, 0x99, 0xCD,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x1A, 0x5F, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0x66, 0x25, 0x99, 0xCD,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x71, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03,
|
||||
};
|
||||
|
||||
memcpy(ptr, init, sizeof(init));
|
||||
for (pos = sizeof(init); pos < MEMPAK_SIZE; pos += 2) {
|
||||
ptr[pos+0] = 0x00;
|
||||
ptr[pos+1] = 0x03;
|
||||
}
|
||||
}
|
43
si/pak.h
Normal file
43
si/pak.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// si/pak.h: Controller pak handling
|
||||
//
|
||||
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
|
||||
// Copyright (C) 2016, Mike Ryan.
|
||||
//
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// 'LICENSE', which is part of this source code package.
|
||||
//
|
||||
|
||||
#ifndef __si_pak_h__
|
||||
#define __si_pak_h__
|
||||
#include "common.h"
|
||||
#include "os/common/save_file.h"
|
||||
|
||||
#define MEMPAK_SIZE 0x8000
|
||||
|
||||
enum pak_type {
|
||||
PAK_NONE = 0,
|
||||
PAK_MEM,
|
||||
PAK_RUMBLE,
|
||||
PAK_TRANSFER,
|
||||
};
|
||||
|
||||
struct controller {
|
||||
const char *mempak_path;
|
||||
struct save_file mempak_save;
|
||||
const char *tpak_rom_path;
|
||||
const char *tpak_save_path;
|
||||
enum pak_type pak;
|
||||
int present;
|
||||
};
|
||||
|
||||
void controller_pak_format(uint8_t *ptr);
|
||||
|
||||
int controller_pak_read(struct controller *controller,
|
||||
uint8_t *send_buf, uint8_t send_bytes,
|
||||
uint8_t *recv_buf, uint8_t recv_bytes);
|
||||
int controller_pak_write(struct controller *controller,
|
||||
uint8_t *send_buf, uint8_t send_bytes,
|
||||
uint8_t *recv_buf, uint8_t recv_bytes);
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue