Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //
- // dd/controller.c: DD controller.
- //
- // CEN64: Cycle-Accurate Nintendo 64 Simulator.
- // Copyright (C) 2014, 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"
- #include <time.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 0x00010000U //Disk needed
- #define DD_CMD_SEEK_WRITE 0x00020000U //Disk needed
- #define DD_CMD_RECALIBRATE 0x00030000U // ??? Disk needed
- #define DD_CMD_SLEEP 0x00040000U
- #define DD_CMD_START 0x00050000U //Disk needed
- #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 0x000B0000U //Disk needed
- #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 0x80000000U
- #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
- int LBA; //GET LBA
- int BlockSizes[] = { 19720, 18360, 17680, 16320, 14960, 13600, 12240, 10880, 9520 };
- int LBAs[] = {292, 292, 274, 274, 274, 274, 274, 204, 0,
- 0, 292, 292, 274, 274, 274, 274, 274, 204};
- int Zones[7][16] = //[Type][Zone]
- {
- {0, 1, 2, 2, 1,
- 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3}, //Type 0
- {0, 1, 2, 3, 3, 2, 1,
- 4, 5, 6, 7, 8, 7, 6, 5, 4}, //Type 1
- {0, 1, 2, 3, 4, 4, 3, 2, 1,
- 5, 6, 7, 8, 7, 6, 5}, //Type 2
- {0, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1,
- 6, 7, 8, 7, 6}, //Type 3
- {0, 1, 2, 3, 4, 5, 6, 6, 5, 4, 3, 2, 1,
- 7, 8, 7}, //Type 4
- {0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1,
- 8}, //Type 5
- {0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1} //Type 6 (ROM only)
- };
- static int getROMLBAoffset(int LBAoffset, int type)
- {
- int offset = 0;
- int offset2 = 0;
- int offset3 = 0;
- for (int LBA = 0; LBA < LBAoffset; LBA++)
- {
- offset2 = 0;
- for (int i = 0; i <= offset3; i++)
- {
- if (i <= (type + 2))
- offset2 += LBAs[Zones[type][i]];
- else
- offset2 += LBAs[Zones[type][i] + 9];
- }
- if ((LBA >= offset2) && (offset3 < 5 + (type * 2)))
- offset3++;
- offset += BlockSizes[Zones[type][offset3]];
- }
- return offset;
- }
- // 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;
- dd->regs[DD_ASIC_ID_REG] = 0x00030000U;
- dd->regs[DD_ASIC_CMD_STATUS] = DD_STATUS_MTR_N_SPIN | DD_STATUS_HEAD_RTRCT;
- if (dd->rom_size == 0x3DEC800)
- dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_DISK_PRES;
- 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);
- //if (dd->rom_size == 0x3DEC800)
- //dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_DISK_PRES;
- *word = dd->regs[reg];
- //if (reg != DD_ASIC_CMD_STATUS)
- 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);
- //No matter what, the lower 16-bit is always ignored.
- word &= 0xFFFF0000U;
- // Command register written: do something.
- if (reg == DD_ASIC_CMD_STATUS) {
- time_t timer;
- struct tm * timeinfo;
- // Get time [minute/second]:
- if (word == DD_CMD_GET_MIN_SEC) {
- //Get Time
- time(&timer);
- timeinfo = localtime(&timer);
- //Put time in DATA as BCD
- uint8_t min = (uint8_t)(((timeinfo->tm_min / 10) << 4) | (timeinfo->tm_min % 10));
- uint8_t sec = (uint8_t)(((timeinfo->tm_sec / 10) << 4) | (timeinfo->tm_sec % 10));
- dd->regs[DD_ASIC_DATA] = (min << 24) | (sec << 16);
- }
- // Get time [day/hour]:
- else if (word == DD_CMD_GET_DAY_HOUR) {
- //Get Time
- time(&timer);
- timeinfo = localtime(&timer);
- //Put time in DATA as BCD
- uint8_t hour = (uint8_t)(((timeinfo->tm_hour / 10) << 4) | (timeinfo->tm_hour % 10));
- uint8_t day = (uint8_t)(((timeinfo->tm_mday / 10) << 4) | (timeinfo->tm_mday % 10));
- dd->regs[DD_ASIC_DATA] = (day << 24) | (hour << 16);
- }
- // Get time [year/month]:
- else if (word == DD_CMD_GET_YEAR_MONTH) {
- //Get Time
- time(&timer);
- timeinfo = localtime(&timer);
- //Put time in DATA as BCD
- uint8_t year = (uint8_t)(((timeinfo->tm_year / 10) << 4) | (timeinfo->tm_year % 10));
- uint8_t month = (uint8_t)((((timeinfo->tm_mon + 1) / 10) << 4) | ((timeinfo->tm_mon + 1) % 10));
- dd->regs[DD_ASIC_DATA] = (year << 24) | (month << 16);
- }
- //Clear Disk Change status bit
- else if (word == DD_CMD_CLR_DSK_CHNG)
- dd->regs[DD_ASIC_CMD_STATUS] &= ~DD_STATUS_DISK_CHNG;
- //Clear Reset status bit
- else if (word == DD_CMD_CLR_RESET)
- dd->regs[DD_ASIC_CMD_STATUS] &= ~DD_STATUS_RST_STATE;
- //Feature Inquiry
- else if (word == DD_CMD_FEATURE_INQ)
- dd->regs[DD_ASIC_DATA] = 0x00010000U;
- //Sleep
- else if (word == DD_CMD_SLEEP)
- dd->regs[DD_ASIC_CMD_STATUS] |= (DD_STATUS_MTR_N_SPIN | DD_STATUS_HEAD_RTRCT);
- //Standby
- else if (word == DD_CMD_STANDBY)
- {
- dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_HEAD_RTRCT;
- dd->regs[DD_ASIC_CMD_STATUS] &= ~DD_STATUS_MTR_N_SPIN;
- }
- //Start
- else if (word == DD_CMD_START)
- dd->regs[DD_ASIC_CMD_STATUS] &= ~(DD_STATUS_MTR_N_SPIN | DD_STATUS_HEAD_RTRCT);
- //SEEK READ
- else if (word == DD_CMD_SEEK_READ)
- {
- dd->regs[DD_ASIC_CUR_TK] = dd->regs[DD_ASIC_DATA] | 0x60000000U;
- dd->regs[DD_ASIC_CMD_STATUS] &= ~(DD_STATUS_MTR_N_SPIN | DD_STATUS_HEAD_RTRCT);
- }
- //SEEK WRITE
- else if (word == DD_CMD_SEEK_WRITE)
- {
- dd->regs[DD_ASIC_CUR_TK] = dd->regs[DD_ASIC_DATA] | 0x60000000U;
- dd->regs[DD_ASIC_CMD_STATUS] &= ~(DD_STATUS_MTR_N_SPIN | DD_STATUS_HEAD_RTRCT);
- }
- //RECALIB
- else if (word == DD_CMD_RECALIBRATE)
- dd->regs[DD_ASIC_DATA] = 0;
- //Index Lock Retry
- else if (word == DD_CMD_IDX_LOCK_RETRY)
- {
- //dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_C2_XFER;
- dd->regs[DD_ASIC_CUR_TK] |= 0x60000000U;
- }
- // 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] = 0;
- dd->regs[DD_ASIC_CMD_STATUS] &= ~(DD_STATUS_BM_INT | DD_STATUS_BM_ERR | DD_STATUS_DATA_RQ | DD_STATUS_C2_XFER);
- if (!(dd->regs[DD_ASIC_CMD_STATUS] & DD_STATUS_MECHA_INT))
- clear_dd_interrupt(dd->bus->vr4300);
- }
- if (word & DD_BM_CTL_MECHA_RST)
- {
- dd->regs[DD_ASIC_CMD_STATUS] &= ~DD_STATUS_MECHA_INT;
- if (!(dd->regs[DD_ASIC_CMD_STATUS] & DD_STATUS_BM_INT))
- clear_dd_interrupt(dd->bus->vr4300);
- }
- if (word == 0)
- dd->regs[DD_ASIC_BM_STATUS_CTL] = 0;
- if (word & DD_BM_CTL_BLK_TRANS)
- dd->regs[DD_ASIC_BM_STATUS_CTL] |= DD_BM_STATUS_BLOCK;
- else
- dd->regs[DD_ASIC_BM_STATUS_CTL] &= ~DD_BM_STATUS_BLOCK;
- //SET SECTOR
- if (word & DD_BM_CTL_MNGRMODE)
- if (!(word & DD_BM_CTL_BLK_TRANS))
- dd->regs[DD_ASIC_CUR_SECTOR] = word & 0x00FF0000U;
- //START BM
- if (word & DD_BM_CTL_START)
- {
- dd->regs[DD_ASIC_CMD_STATUS] &= ~(DD_STATUS_BM_INT | DD_STATUS_DATA_RQ | DD_STATUS_C2_XFER | DD_STATUS_BM_ERR);
- LBA = ((dd->regs[DD_ASIC_CUR_TK] & ~0x6000FFFFU) >> 15);
- if ((dd->regs[DD_ASIC_CUR_SECTOR] >> 16) >= 0x5A)
- LBA |= 1; //Block 1
- LBA ^= ((LBA & 2) >> 1); //Calculate LBA proper
- //Put data
- int offsetLBA = getROMLBAoffset(LBA, dd->rom[6] & 0x0F);
- if ((dd->regs[DD_ASIC_CUR_SECTOR] >> 16) < 0x5A)
- offsetLBA += (dd->regs[DD_ASIC_CUR_SECTOR] >> 16) * ((dd->regs[DD_ASIC_HOST_SECBYTE] >> 16) + 1);
- else
- offsetLBA += ((dd->regs[DD_ASIC_CUR_SECTOR] >> 16) - 5) * ((dd->regs[DD_ASIC_HOST_SECBYTE] >> 16) + 1);
- for (int i = 0; i < DD_DS_BUFFER_LEN; i++)
- dd->ds_buffer[i] = 0xFF;
- for (int i = 0; i < DD_C2S_BUFFER_LEN; i++)
- dd->c2s_buffer[i] = 0x00;
- if (dd->rom_size == 0x3DEC800)
- for (int i = 0; i <= (dd->regs[DD_ASIC_HOST_SECBYTE] >> 16); i++)
- dd->ds_buffer[i] = dd->rom[offsetLBA + i];
- //if (dd->regs[DD_ASIC_BM_STATUS_CTL] & DD_BM_STATUS_BLOCK)
- //dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_C2_XFER;
- dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_DATA_RQ; // | DD_STATUS_C2_XFER;
- //dd->regs[DD_ASIC_BM_STATUS_CTL] |= DD_BM_STATUS_C1CRR;
- if (LBA == 12)
- {
- //for (int i = 0; i <= (dd->regs[DD_ASIC_HOST_SECBYTE] >> 16); i++)
- //dd->ds_buffer[i] = 0xFF;
- dd->regs[DD_ASIC_BM_STATUS_CTL] |= DD_BM_STATUS_MICRO;
- dd->regs[DD_ASIC_CMD_STATUS] &= ~DD_STATUS_C2_XFER;
- }
- //Interrupt!
- dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_BM_INT;
- signal_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 if ((reg == DD_ASIC_CUR_TK) |
- (reg == DD_ASIC_ERR_SECTOR) |
- (reg == DD_ASIC_CUR_SECTOR) |
- (reg == DD_ASIC_C1_S0) |
- (reg == DD_ASIC_C1_S2) |
- (reg == DD_ASIC_C1_S4) |
- (reg == DD_ASIC_C1_S6) |
- (reg == DD_ASIC_CUR_ADDR) |
- (reg == DD_ASIC_ID_REG) |
- (reg == DD_ASIC_TEST_REG))
- {
- // Do nothing. Not writable.
- }
- 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;
- if (offset == 0)
- 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;
- memcpy(word, dd->ds_buffer + (address & 0xFC), sizeof(*word));
- *word = byteswap_32(*word);
- if ((offset == ((dd->regs[DD_ASIC_HOST_SECBYTE] >> 16) - 3)))
- {
- dd->regs[DD_ASIC_CUR_SECTOR] += 0x00010000U;
- LBA = ((dd->regs[DD_ASIC_CUR_TK] & ~0x6000FFFFU) >> 15);
- if ((dd->regs[DD_ASIC_CUR_SECTOR] >> 16) >= 0x5A)
- LBA |= 1; //Block 1
- LBA ^= ((LBA & 2) >> 1); //Calculate LBA proper
- int offsetLBA = getROMLBAoffset(LBA, dd->rom[6] & 0x0F);
- if ((dd->regs[DD_ASIC_CUR_SECTOR] >> 16) < 0x5A)
- offsetLBA += (dd->regs[DD_ASIC_CUR_SECTOR] >> 16) * ((dd->regs[DD_ASIC_HOST_SECBYTE] >> 16) + 1);
- else
- offsetLBA += ((dd->regs[DD_ASIC_CUR_SECTOR] >> 16) - 0x5A) * ((dd->regs[DD_ASIC_HOST_SECBYTE] >> 16) + 1);
- printf("dd: DMA LBA %4d [0x%08X]: Sector %2X\n", LBA, offsetLBA, (dd->regs[DD_ASIC_CUR_SECTOR] >> 16));
- for (int i = 0; i < DD_DS_BUFFER_LEN; i++)
- dd->ds_buffer[i] = 0xFF;
- if (dd->rom_size == 0x3DEC800)
- for (int i = 0; i <= (dd->regs[DD_ASIC_HOST_SECBYTE] >> 16); i++)
- dd->ds_buffer[i] = dd->rom[offsetLBA + i];
- if (((dd->regs[DD_ASIC_CUR_SECTOR] >> 16) >= (0x5A - 5)) && ((dd->regs[DD_ASIC_CUR_SECTOR] >> 16) < 0x5A))
- {
- dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_C2_XFER;
- dd->regs[DD_ASIC_CUR_SECTOR] = 0x005A0000U;
- }
- else if ((dd->regs[DD_ASIC_CUR_SECTOR] >> 16) >= (0xB4 - 5))
- {
- dd->regs[DD_ASIC_CMD_STATUS] |= DD_STATUS_C2_XFER;
- dd->regs[DD_ASIC_CUR_SECTOR] = 0x00000000U;
- }
- }
- //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;
- memcpy(word, dd->ms_ram + (address & 0x3C), sizeof(*word));
- 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;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement