cen64/dd/controller.c
Derek "Turtle" Roe c4afd44ed7 See long description
Replaced all references to simulation with emulation
Updated copyright year
Updated .gitignore to reduce chances of random files being uploaded to
the repo
Added .gitattributes to normalize all text files, and to ignore binary
files (which includes the logo and the NEC PDF)
2015-07-03 08:18:16 -04:00

251 lines
7.7 KiB
C

//
// dd/controller.c: DD controller.
//
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
// Copyright (C) 2015, Tyler J. Stachecki.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//
//
// Thanks go out to OzOnE and Luigiblood (Seru-kun) for reverse
// engineering, documenting, and assisting with the reversal of
// this device!
//
//
// TODO: Currently, the DD IPL spams the controller with DD_CMD_NOOP.
// This is normal. Once you signify that a disk is present (using the
// DD_STATUS_DISK_PRES), the DD IPL attempts to start performing seeks.
//
#include "common.h"
#include "bus/address.h"
#include "bus/controller.h"
#include "dd/controller.h"
#include "vr4300/interface.h"
#ifdef DEBUG_MMIO_REGISTER_ACCESS
const char *dd_register_mnemonics[NUM_DD_REGISTERS] = {
#define X(reg) #reg,
#include "dd/registers.md"
#undef X
};
#endif
// ASIC_CMD_STATUS flags.
#define DD_CMD_NOOP 0x00000000U
#define DD_CMD_SEEK_READ 0x00010001U
#define DD_CMD_SEEK_WRITE 0x00020001U
#define DD_CMD_RECALIBRATE 0x00030001U // ???
#define DD_CMD_SLEEP 0x00040000U
#define DD_CMD_START 0x00050001U
#define DD_CMD_SET_STANDBY 0x00060000U
#define DD_CMD_SET_SLEEP 0x00070000U
#define DD_CMD_CLR_DSK_CHNG 0x00080000U
#define DD_CMD_CLR_RESET 0x00090000U
#define DD_CMD_READ_VERSION 0x000A0000U
#define DD_CMD_SET_DISK_TYPE 0x000B0001U
#define DD_CMD_REQUEST_STATUS 0x000C0000U
#define DD_CMD_STANDBY 0x000D0000U
#define DD_CMD_IDX_LOCK_RETRY 0x000E0000U // ???
#define DD_CMD_SET_YEAR_MONTH 0x000F0000U
#define DD_CMD_SET_DAY_HOUR 0x00100000U
#define DD_CMD_SET_MIN_SEC 0x00110000U
#define DD_CMD_GET_YEAR_MONTH 0x00120000U
#define DD_CMD_GET_DAY_HOUR 0x00130000U
#define DD_CMD_GET_MIN_SEC 0x00140000U
#define DD_CMD_FEATURE_INQ 0x001B0000U
#define DD_STATUS_DATA_RQ 0x40000000U
#define DD_STATUS_C2_XFER 0x10000000U
#define DD_STATUS_BM_ERR 0x08000000U
#define DD_STATUS_BM_INT 0x04000000U
#define DD_STATUS_MECHA_INT 0x02000000U
#define DD_STATUS_DISK_PRES 0x01000000U
#define DD_STATUS_BUSY_STATE 0x00800000U
#define DD_STATUS_RST_STATE 0x00400000U
#define DD_STATUS_MTR_N_SPIN 0x00100000U
#define DD_STATUS_HEAD_RTRCT 0x00080000U
#define DD_STATUS_WR_PR_ERR 0x00040000U
#define DD_STATUS_MECHA_ERR 0x00020000U
#define DD_STATUS_DISK_CHNG 0x00010000U
// ASIC_BM_STATUS_CTL flags.
#define DD_BM_STATUS_RUNNING 0x00000000U
#define DD_BM_STATUS_ERROR 0x04000000U
#define DD_BM_STATUS_MICRO 0x02000000U // ???
#define DD_BM_STATUS_BLOCK 0x01000000U
#define DD_BM_STATUS_C1CRR 0x00800000U
#define DD_BM_STATUS_C1DBL 0x00400000U
#define DD_BM_STATUS_C1SNG 0x00200000U
#define DD_BM_STATUS_C1ERR 0x00010000U // Typo ???
#define DD_BM_CTL_START 0x80000000U
#define DD_BM_CTL_MNGRMODE 0x40000000U
#define DD_BM_CTL_INTMASK 0x20000000U
#define DD_BM_CTL_RESET 0x10000000U
#define DD_BM_CTL_DIS_OR_CHK 0x08000000U // ???
#define DD_BM_CTL_DIS_C1_CRR 0x04000000U
#define DD_BM_CTL_BLK_TRANS 0x02000000U
#define DD_BM_CTL_MECHA_RST 0x01000000U
// Initializes the DD.
int dd_init(struct dd_controller *dd, struct bus_controller *bus,
const uint8_t *ddipl, const uint8_t *ddrom, size_t ddrom_size) {
dd->bus = bus;
dd->ipl_rom = ddipl;
dd->rom = ddrom;
dd->rom_size = ddrom_size;
return 0;
}
// Reads a word from the DD MMIO register space.
int read_dd_regs(void *opaque, uint32_t address, uint32_t *word) {
struct dd_controller *dd = (struct dd_controller *) opaque;
unsigned offset = address - DD_REGS_BASE_ADDRESS;
enum dd_register reg = (offset >> 2);
*word = dd->regs[reg];
debug_mmio_read(dd, dd_register_mnemonics[reg], *word);
return 0;
}
// Writes a word to the DD MMIO register space.
int write_dd_regs(void *opaque, uint32_t address, uint32_t word, uint32_t dqm) {
struct dd_controller *dd = (struct dd_controller *) opaque;
unsigned offset = address - DD_REGS_BASE_ADDRESS;
enum dd_register reg = (offset >> 2);
debug_mmio_write(dd, dd_register_mnemonics[reg], word, dqm);
// Command register written: do something.
if (reg == DD_ASIC_CMD_STATUS) {
// Get time [minute/second]:
if (word == DD_CMD_GET_MIN_SEC) {
// dd->regs[DD_ASIC_DATA] = ...
}
else if (word == DD_CMD_GET_DAY_HOUR) {
// dd->regs[DD_ASIC_DATA] = ...
}
else if (word == DD_CMD_GET_YEAR_MONTH) {
// dd->regs[DD_ASIC_DATA] = ...
}
else if (word == DD_CMD_CLR_RESET)
dd->regs[DD_ASIC_CMD_STATUS] &= ~DD_STATUS_RST_STATE;
// Always signal an interrupt in response.
dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_MECHA_INT;
signal_dd_interrupt(dd->bus->vr4300);
}
// Buffer manager control request: handle it.
else if (reg == DD_ASIC_BM_STATUS_CTL) {
if (word == DD_BM_CTL_RESET)
dd->regs[DD_ASIC_BM_STATUS_CTL] &= ~DD_BM_CTL_INTMASK;
else if (word == DD_BM_CTL_MECHA_RST)
dd->regs[DD_ASIC_CMD_STATUS] &= ~DD_STATUS_MECHA_INT;
clear_dd_interrupt(dd->bus->vr4300);
}
// This is done by the IPL and a lot of games. The only word
// ever know to be written to this register is 0xAAAA0000.
else if (reg == DD_ASIC_HARD_RESET) {
assert(word == 0xAAAA0000 && "dd: Hard reset without magic word?");
dd->regs[DD_ASIC_CMD_STATUS] = DD_STATUS_RST_STATE;
}
else {
dd->regs[reg] &= ~dqm;
dd->regs[reg] |= word;
}
return 0;
}
// Reads a word from the DD IPL ROM.
int read_dd_ipl_rom(void *opaque, uint32_t address, uint32_t *word) {
uint32_t offset = address - DD_IPL_ROM_ADDRESS;
struct dd_controller *dd = (struct dd_controller*) opaque;
if (!dd->ipl_rom)
memset(word, 0, sizeof(*word));
else {
memcpy(word, dd->ipl_rom + offset, sizeof(*word));
*word = byteswap_32(*word);
}
//debug_mmio_read(dd, "DD_IPL_ROM", *word);
return 0;
}
// Writes a word to the DD IPL ROM.
int write_dd_ipl_rom(void *opaque, uint32_t address, uint32_t word, uint32_t dqm) {
assert(0 && "Attempt to write to DD IPL ROM.");
return 0;
}
// Reads a word from the DD C2S buffer.
int read_dd_c2s_buffer(void *opaque, uint32_t address, uint32_t *word) {
struct dd_controller *dd = (struct dd_controller *) opaque;
unsigned offset = address - DD_C2S_BUFFER_ADDRESS;
debug_mmio_read(dd, "DD_C2S_BUFFER", *word);
return 0;
}
// Writes a word to the DD C2S BUFFER.
int write_dd_c2s_buffer(void *opaque, uint32_t address, uint32_t word, uint32_t dqm) {
struct dd_controller *dd = (struct dd_controller *) opaque;
unsigned offset = address - DD_C2S_BUFFER_ADDRESS;
debug_mmio_write(dd, "DD_C2S_BUFFER", word, dqm);
return 0;
}
// Reads a word from the DD DS buffer.
int read_dd_ds_buffer(void *opaque, uint32_t address, uint32_t *word) {
struct dd_controller *dd = (struct dd_controller *) opaque;
unsigned offset = address - DD_DS_BUFFER_ADDRESS;
debug_mmio_read(dd, "DD_DS_BUFFER", *word);
return 0;
}
// Writes a word to the DD DS BUFFER.
int write_dd_ds_buffer(void *opaque, uint32_t address, uint32_t word, uint32_t dqm) {
struct dd_controller *dd = (struct dd_controller *) opaque;
unsigned offset = address - DD_DS_BUFFER_ADDRESS;
debug_mmio_write(dd, "DD_DS_BUFFER", word, dqm);
return 0;
}
// Reads a word from the DD MS RAM.
int read_dd_ms_ram(void *opaque, uint32_t address, uint32_t *word) {
struct dd_controller *dd = (struct dd_controller *) opaque;
unsigned offset = address - DD_MS_RAM_ADDRESS;
debug_mmio_read(dd, "DD_MS_RAM", *word);
return 0;
}
// Writes a word to the DD MS RAM.
int write_dd_ms_ram(void *opaque, uint32_t address, uint32_t word, uint32_t dqm) {
struct dd_controller *dd = (struct dd_controller *) opaque;
unsigned offset = address - DD_MS_RAM_ADDRESS;
debug_mmio_write(dd, "DD_MS_RAM", word, dqm);
return 0;
}