diff --git a/cdc/cdc.cpp b/cdc/cdc.cpp new file mode 100644 index 0000000..0e7b949 --- /dev/null +++ b/cdc/cdc.cpp @@ -0,0 +1,209 @@ +#include + +#include "sh2.h" +#include "serial.h" + +volatile uint16_t * const DATATRNS = (volatile uint16_t *)0x25890000; // 0 +volatile uint16_t * const DATASTAT = (volatile uint16_t *)0x25890004; // 1 (not implemented in mednafen) +volatile uint16_t * const HIRQREQ = (volatile uint16_t *)0x25890008; // 2 +volatile uint16_t * const HIRQMSK = (volatile uint16_t *)0x2589000c; // 3 +volatile uint16_t * const CDATA0 = (volatile uint16_t *)0x25890018; // 6 +volatile uint16_t * const CDATA1 = (volatile uint16_t *)0x2589001c; // 7 +volatile uint16_t * const CDATA2 = (volatile uint16_t *)0x25890020; // 8 +volatile uint16_t * const CDATA3 = (volatile uint16_t *)0x25890024; // 9 +volatile uint16_t * const MPEGRGB = (volatile uint16_t *)0x25890028; // a (not implemented in mednafen) + +/* Bit names of interrupt factor registers (HIRQREQ, HIRQMSK) */ +#define HIRQ__CMOK (1 << 0) /* Command can be issued */ +#define HIRQ__DRDY (1 << 1) /* Data transfer ready */ +#define HIRQ__CSCT (1 << 2) /* 1 sector read completed */ +#define HIRQ__BFUL (1 << 3) /* CD buffer full */ +#define HIRQ__PEND (1 << 4) /* End of CD playback */ +#define HIRQ__DCHG (1 << 5) /* Disk replacement occurrence */ +#define HIRQ__ESEL (1 << 6) /* End of selector setting process */ +#define HIRQ__EHST (1 << 7) /* End of host I/O processing */ +#define HIRQ__ECPY (1 << 8) /* End of copy/move processing */ +#define HIRQ__EFLS (1 << 9) /* End of file system processing */ +#define HIRQ__SCDQ (1 << 10) /* Subcode Q update completed */ +#define HIRQ__MPED (1 << 11) /* End of MPEG-related processing */ +#define HIRQ__MPCM (1 << 12) /* End of MPEG operation undefined interval */ +#define HIRQ__MPST (1 << 13) /* MPEG interrupt status notification */ + +struct cd_command { + uint16_t CDATA[4]; +}; + +struct cd_response { + uint16_t CDATA[4]; +}; + +int cd_wait_hirq(uint16_t status) +{ + for (int i = 0; i < 0x240000; i++) { + uint16_t hirq_temp = *HIRQREQ; + if ((hirq_temp & status) != 0) { + return 0; + } + } + return -1; +} + +int cd_command_response(cd_command * command, cd_response * response) +{ + uint16_t hirq = *HIRQREQ; + serial_string("cd_command_response hirq: "); + serial_integer(hirq, 8, '\n'); + if ((hirq & HIRQ__CMOK) == 0) { + return -1; + } + + *HIRQREQ &= ~HIRQ__CMOK; + + *CDATA0 = command->CDATA[0]; + *CDATA1 = command->CDATA[1]; + *CDATA2 = command->CDATA[2]; + *CDATA3 = command->CDATA[3]; + + if (cd_wait_hirq(HIRQ__CMOK) != 0) { + return -1; + } + + response->CDATA[0] = *CDATA0; + response->CDATA[1] = *CDATA1; + response->CDATA[2] = *CDATA2; + response->CDATA[3] = *CDATA3; + + return 0; +} + +int cd_data_end() +{ + struct cd_command command = {{ + 0x0600, + 0, + 0, + 0 + }}; + struct cd_response response = {0}; + + int ret = cd_command_response(&command, &response); + + *HIRQREQ &= ~HIRQ__DRDY; + + return ret; +} + +int cd_wait_data_ready() +{ + int ret = cd_wait_hirq(HIRQ__DRDY); + if (ret != 0) + cd_data_end(); + + *HIRQREQ &= ~HIRQ__DRDY; + + return ret; +} + +int cd_get_data(uint16_t * data, int size) +{ + int ret; + + ret = cd_wait_data_ready(); + if (ret != 0) + return -1; + + while (size > 0) { + *data = *DATATRNS; + data += 1; + size -= 1; + } + + ret = cd_data_end(); + if (ret != 0) + return -1; + + return 0; +} + +int cd_get_toc(uint32_t * toc) +{ + struct cd_command command = {{ + 0x0200, + 0, + 0, + 0 + }}; + struct cd_response response = {0}; + + int ret = cd_command_response(&command, &response); + if (ret != 0) + return -1; + + int size = ((response.CDATA[0] & 0xff) << 16) + | ((response.CDATA[1] & 0xffff) << 0); + ret = cd_get_data((uint16_t *)toc, size); + if (ret != 0) + return -1; + + return 0; +} + +int cd_init() +{ + struct cd_command command = {{ + 0x0400, + 0, + 0, + 0x040f, + }}; + struct cd_response response = {0}; + + int ret = cd_command_response(&command, &response); + return ret; +} + +void test_cd() +{ + int ret; + ret = cd_init(); + serial_string("cd_init: "); + serial_integer(ret, 8, '\n'); + if (ret != 0) + return; + + uint32_t toc[102]; + ret = cd_get_toc(toc); + serial_string("cd_get_toc: "); + serial_integer(ret, 8, '\n'); + if (ret != 0) + return; + + for (int i = 0; i < 102; i++) { + serial_integer(toc[i], 8, '\n'); + } +} + +void main() +{ + // serial mode register + sh2.reg.SMR = 0 + | SMR__CA__ASYNCHRONOUS_MODE + | SMR__CHR__EIGHT_BIT_DATA + | SMR__PE__PARITY_BIT_NOT_ADDED_OR_CHECKED + | SMR__OE__EVEN_PARITY + | SMR__STOP__ONE_STOP_BIT + | SMR__MP__DISABLED + | SMR__CKS__PHI_4; + + // actual baud rate at phi_4 = 3579545 / (16 * (5 + 1)) + // 37286.927 ~= 38400 + sh2.reg.BRR = 5; + + sh2.reg.SCR = SCR__TE__TRANSMITTER_ENABLE | SCR__RE__RECEIVER_ENABLE; + + sh2.reg.SSR = 0; + + test_cd(); + + while (1); +} diff --git a/cdc/serial.c b/cdc/serial.c new file mode 100644 index 0000000..f3fbf58 --- /dev/null +++ b/cdc/serial.c @@ -0,0 +1,50 @@ +#include "serial.h" + +#include "sh2.h" + +void serial_char(const char c) +{ + while ((sh2.reg.SSR & SSR__TDRE) == 0); // wait for transmit data empty + sh2.reg.TDR = c; +} + +void serial_string(const char * s) +{ + while (*s) { + while ((sh2.reg.SSR & SSR__TDRE) == 0); // wait for transmit data empty + sh2.reg.TDR = *s++; + } +} + +void serial_bytes(const char * s, uint32_t length) +{ + while (length > 0) { + while ((sh2.reg.SSR & SSR__TDRE) == 0); // wait for transmit data empty + sh2.reg.TDR = *s++; + length -= 1; + } +} + +static void hex(char * c, uint32_t len, uint32_t n) +{ + while (len > 0) { + uint32_t nib = n & 0xf; + n = n >> 4; + if (nib > 9) { + nib += (97 - 10); + } else { + nib += (48 - 0); + } + + c[--len] = nib; + } +} + +void serial_integer(const uint32_t n, const uint32_t length, const char end) +{ + char num_buf[length]; + hex(num_buf, length, n); + serial_string("0x"); + serial_bytes(num_buf, length); + serial_char(end); +} diff --git a/cdc/serial.h b/cdc/serial.h new file mode 100644 index 0000000..af29d82 --- /dev/null +++ b/cdc/serial.h @@ -0,0 +1,16 @@ +#include + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void serial_char(const char c); +void serial_string(const char * s); +void serial_bytes(const char * s, uint32_t length); +void serial_integer(const uint32_t n, const uint32_t length, const char end); + +#ifdef __cplusplus +} +#endif diff --git a/cdc/toc.h b/cdc/toc.h new file mode 100644 index 0000000..edfb182 --- /dev/null +++ b/cdc/toc.h @@ -0,0 +1,44 @@ +#include + +struct track { + const uint8_t control_adr; + const uint8_t fad[3]; +}; +static_assert((sizeof (track)) == 4); + +struct start_track { + const uint8_t control_adr; + const uint8_t start_track_number; + const uint8_t _zero[2]; +}; +static_assert((sizeof (start_track)) == 4); + +struct end_track { + const uint8_t control_adr; + const uint8_t end_track_number; + const uint8_t _zero[2]; +}; +static_assert((sizeof (end_track)) == 4); + +struct toc { + struct track track[99]; + struct start_track start_track; + struct end_track end_track; + struct track lead_out; +}; +static_assert((sizeof (toc)) == 408); + +uint32_t track_fad(struct track * t) const +{ + return (t->fad[0] << 16) | (t->fad[1] << 8) | (t->fad[2] << 0); +} + +uint8_t track_control(struct track * t) const +{ + return (t->control_adr >> 4) & 0xf; +} + +uint8_t track_adr(struct track * t) const +{ + return (t->control_adr >> 0) & 0xf; +}