pak: implement mempak and stub out rumble and transfer pak

This commit is contained in:
Mike Ryan 2016-01-24 12:26:35 -08:00
parent 701388da13
commit e7bbc4f822
10 changed files with 291 additions and 21 deletions

View file

@ -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
View file

@ -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;

View file

@ -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;
}

View file

@ -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);

View file

@ -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

View file

@ -22,6 +22,8 @@ struct cen64_options {
const char *eeprom_path;
size_t eeprom_size;
struct controller *controller;
#ifdef _WIN32
bool console;
#endif

View file

@ -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)

View file

@ -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
View 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
View 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