mirror of
https://github.com/n64dev/cen64.git
synced 2024-06-21 13:32:40 -04:00
pi: add support for IS Viewer 64
When -is-viewer is passed on the command line, create an IS Viewer object that intercepts writes at 0x13FF0000. This is used by Ocarina of Time Master Quest Debug to log debug messages. The messages are encoded in EUC-JP, so we bring in iconv to convert that to UTF-8 so the messages can be printed to modern consoles. Props to jrra (@jkbenaim) and spinout for reverse engineering the interface and documenting it: http://wiki.spinout182.com/w/IS64
This commit is contained in:
parent
ac8f2283af
commit
8d0e7bb627
|
@ -20,6 +20,7 @@ endif(APPLE)
|
|||
|
||||
find_package(OpenAL REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(Iconv REQUIRED)
|
||||
|
||||
# If using GCC, configure it accordingly.
|
||||
if (${CMAKE_C_COMPILER_ID} MATCHES GNU)
|
||||
|
@ -310,6 +311,7 @@ set(OS_X11_SOURCES
|
|||
|
||||
set(PI_SOURCES
|
||||
${PROJECT_SOURCE_DIR}/pi/controller.c
|
||||
${PROJECT_SOURCE_DIR}/pi/is_viewer.c
|
||||
)
|
||||
|
||||
set(RDP_SOURCES
|
||||
|
@ -441,6 +443,7 @@ target_link_libraries(cen64
|
|||
${EXTRA_OS_LIBS}
|
||||
${OPENAL_LIBRARY}
|
||||
${OPENGL_gl_LIBRARY}
|
||||
${ICONV_LIBRARIES}
|
||||
${X11_X11_LIB}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
|
14
cen64.c
14
cen64.c
|
@ -20,6 +20,7 @@
|
|||
#include "os/common/rom_file.h"
|
||||
#include "os/common/save_file.h"
|
||||
#include "os/cpuid.h"
|
||||
#include "pi/is_viewer.h"
|
||||
#include "thread.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
|
@ -49,6 +50,7 @@ int cen64_main(int argc, const char **argv) {
|
|||
struct save_file eeprom;
|
||||
struct save_file sram;
|
||||
struct save_file flashram;
|
||||
struct is_viewer is, *is_in = NULL;
|
||||
|
||||
if (!cart_db_is_well_formed()) {
|
||||
printf("Internal cart detection database is not well-formed.\n");
|
||||
|
@ -84,6 +86,7 @@ int cen64_main(int argc, const char **argv) {
|
|||
memset(&eeprom, 0, sizeof(eeprom));
|
||||
memset(&sram, 0, sizeof(sram));
|
||||
memset(&flashram, 0, sizeof(flashram));
|
||||
memset(&is, 0, sizeof(is));
|
||||
dd_variant = NULL;
|
||||
|
||||
if (load_roms(options.ddipl_path, options.ddrom_path, options.pifrom_path,
|
||||
|
@ -122,6 +125,15 @@ int cen64_main(int argc, const char **argv) {
|
|||
memset(flashram.ptr, 0xFF, FLASHRAM_SIZE);
|
||||
}
|
||||
|
||||
if (options.is_viewer_present) {
|
||||
if (!is_viewer_init(&is)) {
|
||||
cen64_alloc_cleanup();
|
||||
return EXIT_FAILURE;
|
||||
} else {
|
||||
is_in = &is;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate memory for and create the device.
|
||||
if (cen64_alloc(&cen64_device_mem, sizeof(*device), false) == NULL) {
|
||||
printf("Failed to allocate enough memory for a device.\n");
|
||||
|
@ -133,7 +145,7 @@ int cen64_main(int argc, const char **argv) {
|
|||
|
||||
if (device_create(device, &ddipl, dd_variant, &ddrom,
|
||||
&pifrom, &cart, &eeprom, &sram,
|
||||
&flashram, controller, options.no_audio, options.no_video) == NULL) {
|
||||
&flashram, is_in, controller, options.no_audio, options.no_video) == NULL) {
|
||||
printf("Failed to create a device.\n");
|
||||
status = EXIT_FAILURE;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,8 @@ struct cen64_device *device_create(struct cen64_device *device,
|
|||
const struct rom_file *ddrom,
|
||||
const struct rom_file *pifrom, const struct rom_file *cart,
|
||||
const struct save_file *eeprom, const struct save_file *sram,
|
||||
const struct save_file *flashram, const struct controller *controller,
|
||||
const struct save_file *flashram, const struct is_viewer *is,
|
||||
const struct controller *controller,
|
||||
bool no_audio, bool no_video) {
|
||||
|
||||
// Initialize the bus.
|
||||
|
@ -78,7 +79,7 @@ struct cen64_device *device_create(struct cen64_device *device,
|
|||
}
|
||||
|
||||
// Initialize the PI.
|
||||
if (pi_init(&device->pi, &device->bus, cart->ptr, cart->size, sram, flashram)) {
|
||||
if (pi_init(&device->pi, &device->bus, cart->ptr, cart->size, sram, flashram, is)) {
|
||||
debug("create_device: Failed to initialize the PI.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -60,7 +60,8 @@ cen64_cold struct cen64_device *device_create(struct cen64_device *device,
|
|||
const struct rom_file *ddrom,
|
||||
const struct rom_file *pifrom, const struct rom_file *cart,
|
||||
const struct save_file *eeprom, const struct save_file *sram,
|
||||
const struct save_file *flashram, const struct controller *controller,
|
||||
const struct save_file *flashram, const struct is_viewer *is,
|
||||
const struct controller *controller,
|
||||
bool no_audio, bool no_video);
|
||||
|
||||
cen64_cold void device_exit(struct bus_controller *bus);
|
||||
|
|
|
@ -24,6 +24,7 @@ const struct cen64_options default_cen64_options = {
|
|||
0, // eeprom_size
|
||||
NULL, // sram_path
|
||||
NULL, // flashram_path
|
||||
0, // is_viewer_present
|
||||
NULL, // controller
|
||||
#ifdef _WIN32
|
||||
false, // console
|
||||
|
@ -128,6 +129,9 @@ int parse_options(struct cen64_options *options, int argc, const char *argv[]) {
|
|||
options->flashram_path = argv[++i];
|
||||
}
|
||||
|
||||
else if (!strcmp(argv[i], "-is-viewer"))
|
||||
options->is_viewer_present = 1;
|
||||
|
||||
else if (!strcmp(argv[i], "-controller")) {
|
||||
int num;
|
||||
struct controller opt = { 0, };
|
||||
|
@ -258,6 +262,7 @@ void print_command_line_usage(const char *invokation_string) {
|
|||
" -headless : Run emulator without user-interface components.\n"
|
||||
" -noaudio : Run emulator without audio.\n"
|
||||
" -novideo : Run emulator without video.\n"
|
||||
" -is-viewer : IS Viewer 64 present.\n"
|
||||
"\n"
|
||||
"Controller Options:\n"
|
||||
" -controller num=<1-4> : Controller with no pak.\n"
|
||||
|
|
|
@ -23,6 +23,7 @@ struct cen64_options {
|
|||
size_t eeprom_size;
|
||||
const char *sram_path;
|
||||
const char *flashram_path;
|
||||
int is_viewer_present;
|
||||
|
||||
struct controller *controller;
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "bus/controller.h"
|
||||
#include "dd/controller.h"
|
||||
#include "pi/controller.h"
|
||||
#include "pi/is_viewer.h"
|
||||
#include "ri/controller.h"
|
||||
#include "vr4300/interface.h"
|
||||
#include <assert.h>
|
||||
|
@ -154,13 +155,14 @@ static int pi_dma_write(struct pi_controller *pi) {
|
|||
// Initializes the PI.
|
||||
int pi_init(struct pi_controller *pi, struct bus_controller *bus,
|
||||
const uint8_t *rom, size_t rom_size, const struct save_file *sram,
|
||||
const struct save_file *flashram) {
|
||||
const struct save_file *flashram, struct is_viewer *is_viewer) {
|
||||
pi->bus = bus;
|
||||
pi->rom = rom;
|
||||
pi->rom_size = rom_size;
|
||||
pi->sram = sram;
|
||||
pi->flashram_file = flashram;
|
||||
pi->flashram.data = flashram->ptr;
|
||||
pi->is_viewer = is_viewer;
|
||||
|
||||
pi->bytes_to_copy = 0;
|
||||
return 0;
|
||||
|
@ -171,6 +173,9 @@ int read_cart_rom(void *opaque, uint32_t address, uint32_t *word) {
|
|||
struct pi_controller *pi = (struct pi_controller *) opaque;
|
||||
unsigned offset = (address - ROM_CART_BASE_ADDRESS) & ~0x3;
|
||||
|
||||
if (pi->is_viewer && is_viewer_map(pi->is_viewer, address))
|
||||
return read_is_viewer(pi->is_viewer, address, word);
|
||||
|
||||
// TODO: Need to figure out correct behaviour.
|
||||
// Should this even happen to begin with?
|
||||
if (pi->rom == NULL || offset > (pi->rom_size - sizeof(*word))) {
|
||||
|
@ -197,7 +202,11 @@ int read_pi_regs(void *opaque, uint32_t address, uint32_t *word) {
|
|||
|
||||
// Writes a word to cartridge ROM.
|
||||
int write_cart_rom(void *opaque, uint32_t address, uint32_t word, uint32_t dqm) {
|
||||
//assert(0 && "Attempt to write to cart ROM.");
|
||||
struct pi_controller *pi = (struct pi_controller *) opaque;
|
||||
|
||||
if (pi->is_viewer && is_viewer_map(pi->is_viewer, address))
|
||||
return write_is_viewer(pi->is_viewer, address, word, dqm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ struct pi_controller {
|
|||
const struct save_file *sram;
|
||||
const struct save_file *flashram_file;
|
||||
struct flashram flashram;
|
||||
struct is_viewer *is_viewer;
|
||||
|
||||
uint64_t counter;
|
||||
uint32_t bytes_to_copy;
|
||||
|
@ -62,7 +63,7 @@ struct pi_controller {
|
|||
|
||||
cen64_cold int pi_init(struct pi_controller *pi, struct bus_controller *bus,
|
||||
const uint8_t *rom, size_t rom_size, const struct save_file *sram,
|
||||
const struct save_file *flashram);
|
||||
const struct save_file *flashram, struct is_viewer *is);
|
||||
|
||||
// Only invoke pi_cycle_ when the counter has expired (timeout).
|
||||
void pi_cycle_(struct pi_controller *pi);
|
||||
|
|
77
pi/is_viewer.c
Normal file
77
pi/is_viewer.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "is_viewer.h"
|
||||
|
||||
// arbitrarily chosen
|
||||
#define IS_BUFFER_SIZE 0x200
|
||||
|
||||
int is_viewer_init(struct is_viewer *is) {
|
||||
memset(is, 0, sizeof(*is));
|
||||
|
||||
// TODO support other addresses
|
||||
is->base_address = IS_VIEWER_BASE_ADDRESS;
|
||||
is->len = IS_VIEWER_ADDRESS_LEN;
|
||||
|
||||
is->buffer = calloc(IS_BUFFER_SIZE, 1);
|
||||
is->output_buffer = calloc(IS_BUFFER_SIZE, 1);
|
||||
is->output_buffer_conv = calloc(IS_BUFFER_SIZE * 3, 1);
|
||||
|
||||
is->cd = iconv_open("UTF-8", "EUC-JP");
|
||||
|
||||
if (is->buffer == NULL || is->output_buffer == NULL ||
|
||||
is->output_buffer_conv == NULL)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
int is_viewer_map(struct is_viewer *is, uint32_t address) {
|
||||
return address >= is->base_address && address + 4 <= is->base_address + is->len;
|
||||
}
|
||||
|
||||
int read_is_viewer(struct is_viewer *is, uint32_t address, uint32_t *word) {
|
||||
uint32_t offset = address - is->base_address;
|
||||
assert(offset + 4 <= is->len);
|
||||
|
||||
memcpy(word, is->buffer + offset, sizeof(*word));
|
||||
*word = byteswap_32(*word);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_is_viewer(struct is_viewer *is, uint32_t address, uint32_t word, uint32_t dqm) {
|
||||
uint32_t offset = address - is->base_address;
|
||||
assert(offset + 4 <= is->len);
|
||||
|
||||
if (offset == 0x14) {
|
||||
if (word > 0) {
|
||||
assert(is->output_buffer_pos + word < is->len);
|
||||
memcpy(is->output_buffer + is->output_buffer_pos, is->buffer + 0x20, word);
|
||||
is->output_buffer_pos += word;
|
||||
is->output_buffer[is->output_buffer_pos] = '\0';
|
||||
|
||||
// once a full line is present, convert the output from EUC to UTF-8
|
||||
if (memchr(is->output_buffer, '\n', is->output_buffer_pos)) {
|
||||
char *inptr = (char *)is->output_buffer;
|
||||
size_t len = strlen(inptr);
|
||||
size_t outlen = 3 * len;
|
||||
char *outptr = (char *)is->output_buffer_conv;
|
||||
memset(is->output_buffer_conv, 0, IS_BUFFER_SIZE * 3);
|
||||
iconv(is->cd, &inptr, &len, &outptr, &outlen);
|
||||
|
||||
printf("%s", is->output_buffer_conv);
|
||||
|
||||
memset(is->output_buffer, 0, is->output_buffer_pos);
|
||||
is->output_buffer_pos = 0;
|
||||
}
|
||||
}
|
||||
memset(is->buffer + 0x20, 0, word);
|
||||
} else {
|
||||
word = byteswap_32(word);
|
||||
memcpy(is->buffer + offset, &word, sizeof(word));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
27
pi/is_viewer.h
Normal file
27
pi/is_viewer.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef __IS_VIEWER_H__
|
||||
#define __IS_VIEWER_H__
|
||||
|
||||
#include <iconv.h>
|
||||
|
||||
// IS Viewer
|
||||
#define IS_VIEWER_BASE_ADDRESS 0x13FF0000
|
||||
#define IS_VIEWER_ADDRESS_LEN 0x00001000
|
||||
|
||||
struct is_viewer {
|
||||
uint32_t base_address;
|
||||
uint32_t len;
|
||||
|
||||
uint8_t *buffer;
|
||||
uint8_t *output_buffer;
|
||||
size_t output_buffer_pos;
|
||||
uint8_t *output_buffer_conv;
|
||||
|
||||
iconv_t cd;
|
||||
};
|
||||
|
||||
int is_viewer_init(struct is_viewer *is);
|
||||
int is_viewer_map(struct is_viewer *is, uint32_t address);
|
||||
int read_is_viewer(struct is_viewer *is, uint32_t address, uint32_t *word);
|
||||
int write_is_viewer(struct is_viewer *is, uint32_t address, uint32_t word, uint32_t dqm);
|
||||
|
||||
#endif /* __IS_VIEWER_H__ */
|
Loading…
Reference in a new issue