ref: b06cc48f434d10fe1974dae898013fd978f01d84
parent: b043b943449c64f14c220d78fe731cfa47143031
author: allkern <lisandroaalarcon@gmail.com>
date: Sat Jun 15 06:30:56 EDT 2024
New CDROM core Compatibility seems to be pretty high, though there's a bunch of (pretty painful) regressions: Mega Man X5/6, Syphon Filter, and probably more
--- a/build-win64.ps1
+++ b/build-win64.ps1
@@ -16,8 +16,8 @@
-I"$($SDL2_DIR)\include\SDL2" `
"psx\*.c" `
"psx\dev\*.c" `
+ "psx\dev\cdrom\*.c" `
"psx\input\*.c" `
- "psx\disc\*.c" `
"frontend\*.c" `
-o "bin\psxe.exe" `
-DREP_VERSION="`"$($VERSION_TAG)`"" `
--- a/frontend/main.c
+++ b/frontend/main.c
@@ -1,7 +1,7 @@
#include "../psx/psx.h"
#include "../psx/input/sda.h"
#include "../psx/input/guncon.h"
-#include "../psx/disc/cue.h"
+#include "../psx/dev/cdrom/cdrom.h"
#include "screen.h"
#include "config.h"
@@ -12,7 +12,7 @@
psx_cdrom_t* cdrom = ((psx_t*)ud)->cdrom;
psx_spu_t* spu = ((psx_t*)ud)->spu;
- psx_cdrom_get_cdda_samples(cdrom, buf, size, spu);
+ psx_cdrom_get_audio_samples(cdrom, buf, size, spu);
for (int i = 0; i < (size >> 2); i++) {
uint32_t sample = psx_spu_get_sample(spu);
--- a/psx/bus_init.h
+++ b/psx/bus_init.h
@@ -1,6 +1,7 @@
#ifndef BUS_INIT_H
#define BUS_INIT_H
+#include "dev/cdrom/cdrom.h"
#include "dev/bios.h"
#include "dev/ram.h"
#include "dev/dma.h"
@@ -14,7 +15,6 @@
#include "dev/gpu.h"
#include "dev/spu.h"
#include "dev/timer.h"
-#include "dev/cdrom.h"
#include "dev/pad.h"
#include "dev/mdec.h"
--- a/psx/dev/cdrom.c
+++ /dev/null
@@ -1,2328 +1,0 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
-#include "cdrom.h"
-#include "../log.h"
-#include "../msf.h"
-
-/*
- Drive Status 1st Response 2nd Response
- Door Open INT5(11h,80h) N/A
- Spin-up INT5(01h,80h) N/A
- Detect busy INT5(03h,80h) N/A
- No Disk INT3(stat) INT5(08h,40h, 00h,00h, 00h,00h,00h,00h)
- Audio Disk INT3(stat) INT5(0Ah,90h, 00h,00h, 00h,00h,00h,00h)
- Unlicensed:Mode1 INT3(stat) INT5(0Ah,80h, 00h,00h, 00h,00h,00h,00h)
- Unlicensed:Mode2 INT3(stat) INT5(0Ah,80h, 20h,00h, 00h,00h,00h,00h)
- Unlicensed:Mode2+Audio INT3(stat) INT5(0Ah,90h, 20h,00h, 00h,00h,00h,00h)
- Debug/Yaroze:Mode2 INT3(stat) INT2(02h,00h, 20h,00h, 20h,20h,20h,20h)
- Licensed:Mode2 INT3(stat) INT2(02h,00h, 20h,00h, 53h,43h,45h,4xh)
- Modchip:Audio/Mode1 INT3(stat) INT2(02h,00h, 00h,00h, 53h,43h,45h,4xh)
-*/
-
-msf_t cdrom_get_track_addr(psx_cdrom_t* cdrom, msf_t msf) {
- uint32_t lba = msf_to_address(msf);
-
- int num_tracks, track;
-
- psx_disc_get_track_count(cdrom->disc, &num_tracks);
-
- for (track = 1; track < num_tracks - 1; track++) {
- msf_t curr, next;
-
- psx_disc_get_track_addr(cdrom->disc, &curr, track);
- psx_disc_get_track_addr(cdrom->disc, &next, track + 1);
-
- uint32_t curr_lba = msf_to_address(curr);
- uint32_t next_lba = msf_to_address(next);
-
- // printf("lba=%02u:%02u:%02u (%08x) curr=%02u:%02u:%02u (%08x) next=%02u:%02u:%02u (%08x)\n",
- // msf.m,
- // msf.s,
- // msf.f,
- // lba,
- // curr.m,
- // curr.s,
- // curr.f,
- // curr_lba,
- // next.m,
- // next.s,
- // next.f,
- // next_lba
- // );
-
- if ((lba >= curr_lba) && (lba < next_lba))
- break;
- }
-
- msf_t track_msf;
-
- psx_disc_get_track_addr(cdrom->disc, &track_msf, track);
-
- return track_msf;
-}
-
-void cdrom_fetch_video_sector(psx_cdrom_t* cdrom) {
- while (true) {
- if (psx_disc_seek(cdrom->disc, cdrom->seek_msf))
- return;
-
- psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
-
- msf_add_f(&cdrom->seek_msf, 1);
-
- return;
- // Check RT and Video/Data bit
- // if (cdrom->dfifo[0x12] & 4)
- // continue;
-
- // If we get here it means this is a real-time video sector.
- // If the XA filter is disabled, we're done
- // if (!(cdrom->mode & MODE_XA_FILTER))
- // return;
-
- // // Else check XA file/channel
- // int file_eq = cdrom->dfifo[0x10] == cdrom->xa_file;
- // int channel_eq = cdrom->dfifo[0x11] == cdrom->xa_channel;
-
- // // If they are equal to our filter values, we're done
- // // else keep searching
- // if (file_eq && channel_eq)
- // return;
- }
-}
-
-#define GETID_RESPONSE_SIZE 8
-#define GETID_RESPONSE_END (GETID_RESPONSE_SIZE - 1)
-
-static const int16_t g_zigzag_table0[] = {
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, -0x0002, 0x000a, -0x0022,
- 0x0041, -0x0054, 0x0034, 0x0009,
- -0x010a, 0x0400, -0x0a78, 0x234c,
- 0x6794, -0x1780, 0x0bcd, -0x0623,
- 0x0350, -0x016d, 0x006b, 0x000a,
- -0x0010, 0x0011, -0x0008, 0x0003,
- -0x0001
-};
-
-static const int16_t g_zigzag_table1[] = {
- 0x0000, 0x0000, 0x0000, -0x0002,
- 0x0000, 0x0003, -0x0013, 0x003c,
- -0x004b, 0x00a2, -0x00e3, 0x0132,
- -0x0043, -0x0267, 0x0c9d, 0x74bb,
- -0x11b4, 0x09b8, -0x05bf, 0x0372,
- -0x01a8, 0x00a6, -0x001b, 0x0005,
- 0x0006, -0x0008, 0x0003, -0x0001,
- 0x0000
-};
-
-static const int16_t g_zigzag_table2[] = {
- 0x0000, 0x0000, -0x0001, 0x0003,
- -0x0002, -0x0005, 0x001f, -0x004a,
- 0x00b3, -0x0192, 0x02b1, -0x039e,
- 0x04f8, -0x05a6, 0x7939, -0x05a6,
- 0x04f8, -0x039e, 0x02b1, -0x0192,
- 0x00b3, -0x004a, 0x001f, -0x0005,
- -0x0002, 0x0003, -0x0001, 0x0000,
- 0x0000
-};
-
-static const int16_t g_zigzag_table3[] = {
- 0x0000, -0x0001, 0x0003, -0x0008,
- 0x0006, 0x0005, -0x001b, 0x00a6,
- -0x01a8, 0x0372, -0x05bf, 0x09b8,
- -0x11b4, 0x74bb, 0x0c9d, -0x0267,
- -0x0043, 0x0132, -0x00e3, 0x00a2,
- -0x004b, 0x003c, -0x0013, 0x0003,
- 0x0000, -0x0002, 0x0000, 0x0000,
- 0x0000
-};
-
-static const int16_t g_zigzag_table4[] = {
- -0x0001, 0x0003, -0x0008, 0x0011,
- -0x0010, 0x000a, 0x006b, -0x016d,
- 0x0350, -0x0623, 0x0bcd, -0x1780,
- 0x6794, 0x234c, -0x0a78, 0x0400,
- -0x010a, 0x0009, 0x0034, -0x0054,
- 0x0041, -0x0022, 0x000a, -0x0001,
- 0x0000, 0x0001, 0x0000, 0x0000,
- 0x0000
-};
-
-static const int16_t g_zigzag_table5[] = {
- 0x0002, -0x0008, 0x0010, -0x0023,
- 0x002b, 0x001a, -0x00eb, 0x027b,
- -0x0548, 0x0afa, -0x16fa, 0x53e0,
- 0x3c07, -0x1249, 0x080e, -0x0347,
- 0x015b, -0x0044, -0x0017, 0x0046,
- -0x0023, 0x0011, -0x0005, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000
-};
-
-static const int16_t g_zigzag_table6[] = {
- -0x0005, 0x0011, -0x0023, 0x0046,
- -0x0017, -0x0044, 0x015b, -0x0347,
- 0x080e, -0x1249, 0x3c07, 0x53e0,
- -0x16fa, 0x0afa, -0x0548, 0x027b,
- -0x00eb, 0x001a, 0x002b, -0x0023,
- 0x0010, -0x0008, 0x0002, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000
-};
-
-static const int16_t* g_zigzag_table[] = {
- g_zigzag_table0,
- g_zigzag_table1,
- g_zigzag_table2,
- g_zigzag_table3,
- g_zigzag_table4,
- g_zigzag_table5,
- g_zigzag_table6
-};
-
-static const uint8_t g_getid_no_disc[] = {
- 0x08, 0x40, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00
-};
-
-static const uint8_t g_getid_audio[] = {
- 0x0a, 0x90, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00
-};
-
-static const uint8_t g_getid_unlicensed[] = {
- 0x0a, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00
-};
-
-static const uint8_t g_getid_licensed[] = {
- 0x02, 0x00, 0x20, 0x00,
- 'S' , 'C' , 'E' , 'A'
-};
-
-#define RESP_PUSH(data) { \
- cdrom->rfifo[cdrom->rfifo_index++] = data; \
- cdrom->rfifo_index &= 15; \
- SET_BITS(status, STAT_RSLRRDY_MASK, STAT_RSLRRDY_MASK); }
-
-#define PFIFO_POP (cdrom->pfifo[--cdrom->pfifo_index])
-
-#define VALID_BCD(v) (((v & 0xf) <= 9) && ((v & 0xf0) <= 0x90))
-
-void cdrom_cmd_error(psx_cdrom_t* cdrom) {
- SET_BITS(ifr, IFR_INT, IFR_INT5);
- RESP_PUSH(cdrom->error);
- RESP_PUSH(GETSTAT_MOTOR | cdrom->error_flags);
-
- cdrom->pfifo_index = 0;
- cdrom->delayed_command = CDL_NONE;
- cdrom->state = CD_STATE_RECV_CMD;
-}
-void cdrom_cmd_unimplemented(psx_cdrom_t* cdrom) {
- // log_set_quiet(0);
- log_fatal("Unimplemented CDROM command (%u)", cdrom->command);
- log_set_quiet(1);
-
- exit(1);
-}
-void cdrom_cmd_getstat(psx_cdrom_t* cdrom) {
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- // if (cdrom->ongoing_read_command) {
- // cdrom->status |= STAT_BUSYSTS_MASK;
- // // printf("command=%02x\n", cdrom->ongoing_read_command);
- // cdrom->state = CD_STATE_SEND_RESP2;
- // cdrom->delayed_command = cdrom->ongoing_read_command;
- // cdrom->irq_delay = DELAY_1MS;
-
- // return;
- // }
-
- if (cdrom->pfifo_index) {
- log_fatal("CdlGetStat: Expected exactly 0 parameters");
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_PCOUNT;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- cdrom->irq_delay = DELAY_1MS * 2;
- cdrom->state = CD_STATE_SEND_RESP1;
- cdrom->delayed_command = CDL_GETSTAT;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(
- GETSTAT_MOTOR |
- (cdrom->cdda_playing ? GETSTAT_PLAY : 0) |
- (cdrom->ongoing_read_command ? GETSTAT_READ : 0) |
- (cdrom->disc ? 0 : GETSTAT_TRAYOPEN)
- );
-
- if (cdrom->ongoing_read_command) {
- // printf("getstat command=%02x\n", cdrom->ongoing_read_command);
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = cdrom->ongoing_read_command;
- cdrom->irq_delay = DELAY_1MS * 2;
- } else {
- cdrom->delayed_command = CDL_NONE;
- cdrom->state = CD_STATE_RECV_CMD;
- }
- } break;
- }
-}
-void cdrom_cmd_setloc(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- if (cdrom->pfifo_index != 3) {
- log_fatal("CdlSetloc: Expected exactly 3 parameters, got %u instead",
- cdrom->pfifo_index
- );
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_PCOUNT;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- int f = PFIFO_POP;
- int s = PFIFO_POP;
- int m = PFIFO_POP;
-
- if (!(VALID_BCD(m) && VALID_BCD(s) && VALID_BCD(f) && (f < 0x75))) {
- printf("setloc: invalid msf\n");
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_INVSUBF;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- cdrom->seek_msf.m = m;
- cdrom->seek_msf.s = s;
- cdrom->seek_msf.f = f;
-
- msf_from_bcd(&cdrom->seek_msf);
-
- cdrom->cdda_msf = cdrom->seek_msf;
-
- cdrom->seek_pending = 1;
-
- // printf("setloc: %02x:%02x:%02x\n",
- // cdrom->seek_msf.m,
- // cdrom->seek_msf.s,
- // cdrom->seek_msf.f
- // );
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_SETLOC;
- cdrom->state = CD_STATE_SEND_RESP1;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(GETSTAT_MOTOR | GETSTAT_SEEK);
-
- if (cdrom->ongoing_read_command) {
- // printf("command=%02x\n", cdrom->ongoing_read_command);
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = cdrom->ongoing_read_command;
- cdrom->irq_delay = DELAY_1MS;
- } else {
- cdrom->delayed_command = CDL_NONE;
- cdrom->state = CD_STATE_RECV_CMD;
- }
- } break;
-
- // Read ongoing
- case CD_STATE_SEND_RESP2: {
- int f = PFIFO_POP;
- int s = PFIFO_POP;
- int m = PFIFO_POP;
-
- if (!(VALID_BCD(m) && VALID_BCD(s) && VALID_BCD(f) && (f < 0x75))) {
- cdrom->ongoing_read_command = false;
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_INVSUBF;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- cdrom->seek_msf.m = m;
- cdrom->seek_msf.s = s;
- cdrom->seek_msf.f = f;
- } break;
- }
-}
-void cdrom_cmd_play(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- int track = 0;
-
- // if (cdrom->cdda_playing) {
- // cdrom->pfifo_index = 0;
-
- // cdrom->irq_delay = DELAY_1MS;
- // cdrom->state = CD_STATE_SEND_RESP1;
- // cdrom->delayed_command = CDL_PLAY;
-
- // return;
- // }
-
- // Optional track number parameter
- if (cdrom->pfifo_index)
- track = PFIFO_POP;
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP1;
- cdrom->delayed_command = CDL_PLAY;
-
- printf("play track %u\n", track);
-
- if (track) {
- psx_disc_get_track_addr(cdrom->disc, &cdrom->cdda_msf, track);
-
- msf_to_bcd(&cdrom->cdda_msf);
-
- cdrom->cdda_track = track;
- cdrom->seek_msf.m = cdrom->cdda_msf.m;
- cdrom->seek_msf.s = cdrom->cdda_msf.s;
- cdrom->seek_msf.f = cdrom->cdda_msf.f;
-
- cdrom->seek_pending = 1;
- }
-
- if (cdrom->seek_pending) {
- cdrom->seek_pending = 0;
-
- // printf("Seeked to location\n");
-
- cdrom->cdda_msf = cdrom->seek_msf;
-
- // Seek to that address and read sector
- psx_disc_seek(cdrom->disc, cdrom->cdda_msf);
- psx_disc_read_sector(cdrom->disc, cdrom->cdda_buf);
-
- // Increment sector
- msf_add_f(&cdrom->cdda_msf, 1);
-
- cdrom->cdda_sector_offset = 0;
- }
-
- cdrom->cdda_playing = 1;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(GETSTAT_MOTOR | GETSTAT_PLAY);
-
- cdrom->delayed_command = CDL_NONE;
- cdrom->state = CD_STATE_RECV_CMD;
- } break;
- }
-}
-void cdrom_cmd_readn(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
- cdrom->ongoing_read_command = CDL_READN;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- log_fatal("CdlReadN: CD_STATE_RECV_CMD");
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP1;
- cdrom->delayed_command = CDL_READN;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- log_fatal("CdlReadN: CD_STATE_SEND_RESP1");
-
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(GETSTAT_MOTOR);
-
- if (cdrom->mode & MODE_XA_ADPCM) {
- cdrom->xa_msf = cdrom->seek_msf;
- cdrom->xa_current_msf = cdrom->xa_msf;
- cdrom->xa_playing = 1;
- cdrom->xa_remaining_samples = 0;
-
- SET_BITS(status, STAT_ADPBUSY_MASK, STAT_ADPBUSY_MASK);
-
- printf("Play XA-ADPCM encoded song at %02u:%02u:%02u, filter=%u, file=%02x, channel=%02x (ReadN)\n",
- cdrom->xa_msf.m,
- cdrom->xa_msf.s,
- cdrom->xa_msf.f,
- (cdrom->mode & MODE_XA_FILTER) != 0,
- cdrom->xa_file,
- cdrom->xa_channel
- );
- }
-
- int err = psx_disc_seek(cdrom->disc, cdrom->seek_msf);
-
- if (err) {
- // log_set_quiet(0);
- log_fatal("CdlReadN: Out of bounds seek");
- log_set_quiet(1);
-
- cdrom->irq_delay = DELAY_1MS * 600;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_INVSUBF;
- cdrom->error_flags = GETSTAT_SEEKERROR;
-
- return;
- }
-
- psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
-
- int double_speed = cdrom->mode & MODE_SPEED;
-
- cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = CDL_READN;
-
- if (cdrom->spin_delay) {
- cdrom->irq_delay += cdrom->spin_delay;
- cdrom->spin_delay = 0;
- }
- } break;
-
- case CD_STATE_SEND_RESP2: {
- log_fatal("CdlReadS: CD_STATE_SEND_RESP2");
-
- // Returning only non-ADPCM sectors causes desync for some
- // reason. I'll keep returning all sectors for now
-
- if (cdrom->mode & MODE_XA_ADPCM) {
- // printf("ReadS fetching non ADPCM sector...\n");
- cdrom_fetch_video_sector(cdrom);
-
- // printf("%02u:%02u:%02u - file=%02x channel=%02x sm=%02x ci=%02x\n",
- // cdrom->seek_msf.m,
- // cdrom->seek_msf.s,
- // cdrom->seek_msf.f,
- // cdrom->dfifo[0x10],
- // cdrom->dfifo[0x11],
- // cdrom->dfifo[0x12],
- // cdrom->dfifo[0x13]
- // );
- } else {
- psx_disc_seek(cdrom->disc, cdrom->seek_msf);
- psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
-
- msf_add_f(&cdrom->seek_msf, 1);
- }
-
- int double_speed = cdrom->mode & MODE_SPEED;
-
- cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = CDL_READN;
- cdrom->dfifo_index = 0;
-
- SET_BITS(ifr, IFR_INT, IFR_INT1);
- RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
- } break;
- }
-}
-void cdrom_cmd_motoron(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- cdrom->ongoing_read_command = CDL_NONE;
- cdrom->cdda_playing = 0;
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP1;
- cdrom->delayed_command = CDL_MOTORON;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, 3);
- RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
-
- int double_speed = cdrom->mode & MODE_SPEED;
-
- cdrom->irq_delay = DELAY_1MS * (double_speed ? 70 : 65);
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = CDL_MOTORON;
- } break;
-
- case CD_STATE_SEND_RESP2: {
- SET_BITS(ifr, IFR_INT, 2);
- RESP_PUSH(GETSTAT_MOTOR);
-
- cdrom->state = CD_STATE_RECV_CMD;
- cdrom->delayed_command = CDL_NONE;
- } break;
- }
-}
-void cdrom_cmd_stop(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- cdrom->ongoing_read_command = CDL_NONE;
- cdrom->cdda_playing = 0;
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP1;
- cdrom->delayed_command = CDL_STOP;
- cdrom->seek_msf.m = 0;
- cdrom->seek_msf.s = 0;
- cdrom->seek_msf.f = 0;
-
- cdrom->cdda_msf = cdrom->seek_msf;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, 3);
- RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
-
- int double_speed = cdrom->mode & MODE_SPEED;
-
- cdrom->irq_delay = DELAY_1MS * (double_speed ? 70 : 65);
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = CDL_STOP;
- } break;
-
- case CD_STATE_SEND_RESP2: {
- SET_BITS(ifr, IFR_INT, 2);
- RESP_PUSH(GETSTAT_MOTOR);
-
- cdrom->state = CD_STATE_RECV_CMD;
- cdrom->delayed_command = CDL_NONE;
- } break;
- }
-}
-void cdrom_cmd_pause(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- cdrom->ongoing_read_command = CDL_NONE;
- cdrom->cdda_playing = 0;
- cdrom->xa_playing = 0;
-
- SET_BITS(status, STAT_ADPBUSY_MASK, 0);
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP1;
- cdrom->delayed_command = CDL_PAUSE;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, 3);
- RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
-
- int double_speed = cdrom->mode & MODE_SPEED;
-
- cdrom->irq_delay = DELAY_1MS * (double_speed ? 70 : 65);
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = CDL_PAUSE;
- } break;
-
- case CD_STATE_SEND_RESP2: {
- SET_BITS(ifr, IFR_INT, 2);
- RESP_PUSH(GETSTAT_MOTOR);
-
- cdrom->state = CD_STATE_RECV_CMD;
- cdrom->delayed_command = CDL_NONE;
- } break;
- }
-}
-void cdrom_cmd_init(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP1;
- cdrom->delayed_command = CDL_INIT;
- cdrom->ongoing_read_command = CDL_NONE;
- cdrom->mode = 0;
- cdrom->dfifo_index = 0;
- cdrom->dfifo_full = 0;
- cdrom->pfifo_index = 0;
- cdrom->rfifo_index = 0;
- cdrom->seek_msf.m = 0;
- cdrom->seek_msf.s = 2;
- cdrom->seek_msf.f = 0;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, 3);
- RESP_PUSH(GETSTAT_MOTOR);
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = CDL_INIT;
- } break;
-
- case CD_STATE_SEND_RESP2: {
- SET_BITS(ifr, IFR_INT, 2);
- RESP_PUSH(GETSTAT_MOTOR);
-
- cdrom->state = CD_STATE_RECV_CMD;
- cdrom->delayed_command = CDL_NONE;
- } break;
- }
-}
-void cdrom_cmd_mute(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- if (cdrom->pfifo_index) {
- log_fatal("CdlMute: Expected exactly 0 parameters");
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_PCOUNT;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_MUTE;
- cdrom->state = CD_STATE_SEND_RESP1;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(cdrom->stat);
-
- if (cdrom->ongoing_read_command) {
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = cdrom->ongoing_read_command;
- cdrom->irq_delay = DELAY_1MS;
- } else {
- cdrom->delayed_command = CDL_NONE;
- cdrom->state = CD_STATE_RECV_CMD;
- }
- } break;
- }
-}
-void cdrom_cmd_unmute(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- if (cdrom->pfifo_index) {
- log_fatal("CdlUnmute: Expected exactly 0 parameters");
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_PCOUNT;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_DEMUTE;
- cdrom->state = CD_STATE_SEND_RESP1;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(cdrom->stat);
-
- if (cdrom->ongoing_read_command) {
- // printf("command=%02x\n", cdrom->ongoing_read_command);
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = cdrom->ongoing_read_command;
- cdrom->irq_delay = DELAY_1MS;
- } else {
- cdrom->delayed_command = CDL_NONE;
- cdrom->state = CD_STATE_RECV_CMD;
- }
- } break;
- }
-}
-void cdrom_cmd_setfilter(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- if (cdrom->pfifo_index != 2) {
- log_fatal("CdlSetfilter: Expected exactly 2 parameter");
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_PCOUNT;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- cdrom->xa_channel = PFIFO_POP;
- cdrom->xa_file = PFIFO_POP;
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_SETFILTER;
- cdrom->state = CD_STATE_SEND_RESP1;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- cdrom->delayed_command = CDL_NONE;
-
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(cdrom->stat);
-
- if (cdrom->ongoing_read_command) {
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = cdrom->ongoing_read_command;
- cdrom->state = CD_STATE_SEND_RESP2;
- }
-
- cdrom->state = CD_STATE_RECV_CMD;
- } break;
- }
-}
-void cdrom_cmd_setmode(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- // Not doing this fixes a graphical issue in
- // Castlevania - Symphony of the Night, but breaks
- // Road Rash.
- // cdrom->ongoing_read_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- if (cdrom->pfifo_index != 1) {
- log_fatal("CdlSetmode: Expected exactly 1 parameter");
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_PCOUNT;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- int prev_speed = cdrom->mode & MODE_SPEED;
-
- cdrom->mode = PFIFO_POP;
-
- if ((cdrom->mode & MODE_SPEED) != prev_speed)
- cdrom->spin_delay = 0; // DELAY_1MS * 650;
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_SETMODE;
- cdrom->state = CD_STATE_SEND_RESP1;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- cdrom->delayed_command = CDL_NONE;
-
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(GETSTAT_MOTOR);
-
- if (cdrom->ongoing_read_command) {
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = cdrom->ongoing_read_command;
- cdrom->state = CD_STATE_SEND_RESP2;
-
- return;
- }
-
- cdrom->state = CD_STATE_RECV_CMD;
- } break;
- }
-}
-void cdrom_cmd_getparam(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_GETPARAM;
- cdrom->state = CD_STATE_SEND_RESP1;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- cdrom->delayed_command = CDL_NONE;
-
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(cdrom->xa_channel);
- RESP_PUSH(cdrom->xa_file);
- RESP_PUSH(0x00);
- RESP_PUSH(cdrom->mode);
- RESP_PUSH(GETSTAT_MOTOR);
-
- cdrom->state = CD_STATE_RECV_CMD;
- } break;
- }
-}
-void cdrom_cmd_getlocl(psx_cdrom_t* cdrom) {
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- cdrom->irq_delay = DELAY_1MS * 4;
- cdrom->delayed_command = CDL_GETLOCL;
- cdrom->state = CD_STATE_SEND_RESP1;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(cdrom->xa_sector_buf[0x13]);
- RESP_PUSH(cdrom->xa_sector_buf[0x12]);
- RESP_PUSH(cdrom->xa_sector_buf[0x11]);
- RESP_PUSH(cdrom->xa_sector_buf[0x10]);
- RESP_PUSH(cdrom->xa_sector_buf[0x0f]);
- RESP_PUSH(cdrom->xa_sector_buf[0x0e]);
- RESP_PUSH(cdrom->xa_sector_buf[0x0d]);
- RESP_PUSH(cdrom->xa_sector_buf[0x0c]);
-
- if (cdrom->ongoing_read_command) {
- // printf("command=%02x\n", cdrom->ongoing_read_command);
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = cdrom->ongoing_read_command;
- cdrom->irq_delay = DELAY_1MS * 4;
- } else {
- cdrom->delayed_command = CDL_NONE;
- cdrom->state = CD_STATE_RECV_CMD;
- }
- } break;
- }
-}
-void cdrom_cmd_getlocp(psx_cdrom_t* cdrom) {
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- cdrom->irq_delay = DELAY_1MS * 8;
- cdrom->delayed_command = CDL_GETLOCP;
- cdrom->state = CD_STATE_SEND_RESP1;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- msf_t absolute = cdrom->seek_msf;
- msf_t relative = absolute;
- msf_t track_msf = cdrom_get_track_addr(cdrom, absolute);
-
- relative.m -= track_msf.m;
- relative.s -= track_msf.s;
- relative.f -= track_msf.f;
-
- msf_adjust_sub(&relative);
-
- // printf("abs=%02u:%02u:%02u tra=%02u:%02u:%02u rel=%02u:%02u:%02u\n",
- // absolute.m,
- // absolute.s,
- // absolute.f,
- // track_msf.m,
- // track_msf.s,
- // track_msf.f,
- // relative.m,
- // relative.s,
- // relative.f
- // );
-
- msf_to_bcd(&absolute);
- msf_to_bcd(&relative);
-
- // printf("getlocp 01 01 %02x:%02x:%02x %02x:%02x:%02x\n",
- // relative.m,
- // relative.s,
- // relative.f,
- // absolute.m,
- // absolute.s,
- // absolute.f
- // );
-
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(absolute.f);
- RESP_PUSH(absolute.s);
- RESP_PUSH(absolute.m);
- RESP_PUSH(relative.f);
- RESP_PUSH(relative.s);
- RESP_PUSH(relative.m);
- RESP_PUSH(0x01);
- RESP_PUSH(0x15);
-
- if (cdrom->ongoing_read_command) {
- printf("getlocp command=%02x\n", cdrom->ongoing_read_command);
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = cdrom->ongoing_read_command;
- cdrom->irq_delay = DELAY_1MS * 8;
- } else {
- cdrom->delayed_command = CDL_NONE;
- cdrom->state = CD_STATE_RECV_CMD;
- }
- } break;
- }
-}
-void cdrom_cmd_setsession(psx_cdrom_t* cdrom) {
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_SETSESSION;
- cdrom->state = CD_STATE_SEND_RESP1;
-
- cdrom->pfifo_index = 0;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(GETSTAT_SEEK | GETSTAT_MOTOR);
-
- cdrom->delayed_command = CDL_SETSESSION;
- cdrom->state = CD_STATE_SEND_RESP2;
- } break;
-
- case CD_STATE_SEND_RESP2: {
- SET_BITS(ifr, IFR_INT, IFR_INT2);
- RESP_PUSH(GETSTAT_SEEK | GETSTAT_MOTOR);
-
- cdrom->delayed_command = CDL_NONE;
- cdrom->state = CD_STATE_RECV_CMD;
- } break;
- }
-}
-void cdrom_cmd_gettn(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- if (cdrom->pfifo_index) {
- log_fatal("CdlGetTN: Expected exactly 0 parameters");
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_PCOUNT;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_GETTN;
- cdrom->state = CD_STATE_SEND_RESP1;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- int tn;
-
- psx_disc_get_track_count(cdrom->disc, &tn);
-
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(tn);
- RESP_PUSH(0x01);
- RESP_PUSH(GETSTAT_MOTOR);
-
- if (cdrom->ongoing_read_command) {
- //// printf("command=%02x\n", cdrom->ongoing_read_command);
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = cdrom->ongoing_read_command;
- cdrom->irq_delay = DELAY_1MS;
- } else {
- cdrom->delayed_command = CDL_NONE;
- cdrom->state = CD_STATE_RECV_CMD;
- }
- } break;
- }
-}
-void cdrom_cmd_gettd(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- if (cdrom->pfifo_index != 1) {
- log_fatal("CdlGetTD: Expected exactly 1 parameter");
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_PCOUNT;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- cdrom->gettd_track = PFIFO_POP;
-
- if (!VALID_BCD(cdrom->gettd_track)) {
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_INVSUBF;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- int err = psx_disc_get_track_addr(cdrom->disc, NULL, cdrom->gettd_track);
-
- if (err) {
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_INVSUBF;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_GETTD;
- cdrom->state = CD_STATE_SEND_RESP1;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- msf_t td;
-
- psx_disc_get_track_addr(cdrom->disc, &td, cdrom->gettd_track);
-
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(ITOB(td.s));
- RESP_PUSH(ITOB(td.m));
- RESP_PUSH(GETSTAT_MOTOR);
-
- cdrom->delayed_command = CDL_NONE;
- cdrom->state = CD_STATE_RECV_CMD;
- } break;
- }
-}
-void cdrom_cmd_seekl(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- if (cdrom->pfifo_index) {
- log_fatal("CdlSeekL: Expected exactly 0 parameters");
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_PCOUNT;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP1;
- cdrom->delayed_command = CDL_SEEKL;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, 3);
- RESP_PUSH(GETSTAT_MOTOR);
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = CDL_SEEKL;
-
- psx_disc_seek(cdrom->disc, cdrom->seek_msf);
-
- cdrom->seek_pending = 0;
- } break;
-
- case CD_STATE_SEND_RESP2: {
- SET_BITS(ifr, IFR_INT, 2);
- RESP_PUSH(GETSTAT_MOTOR);
-
- cdrom->state = CD_STATE_RECV_CMD;
- cdrom->delayed_command = CDL_NONE;
- } break;
- }
-}
-void cdrom_cmd_seekp(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- if (cdrom->pfifo_index) {
- log_fatal("CdlSeekP: Expected exactly 0 parameters");
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_PCOUNT;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP1;
- cdrom->delayed_command = CDL_SEEKP;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, 3);
- RESP_PUSH(GETSTAT_MOTOR);
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = CDL_SEEKP;
-
- psx_disc_seek(cdrom->disc, cdrom->seek_msf);
-
- cdrom->seek_pending = 0;
- } break;
-
- case CD_STATE_SEND_RESP2: {
- SET_BITS(ifr, IFR_INT, 2);
- RESP_PUSH(GETSTAT_MOTOR);
-
- cdrom->state = CD_STATE_RECV_CMD;
- cdrom->delayed_command = CDL_NONE;
- } break;
- }
-}
-void cdrom_cmd_test(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- if (cdrom->pfifo_index != 1) {
- log_fatal("CdlTest: Expected exactly 1 parameter");
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_PCOUNT;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- if (PFIFO_POP != 0x20) {
- log_fatal("CdlTest: Unhandled subcommand");
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_INVSUBF;
-
- return;
- }
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_TEST;
- cdrom->state = CD_STATE_SEND_RESP1;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- cdrom->delayed_command = CDL_NONE;
-
- // 95h,05h,16h,C1h
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(0xc0);
- RESP_PUSH(0x19);
- RESP_PUSH(0x09);
- RESP_PUSH(0x94);
-
- cdrom->state = CD_STATE_RECV_CMD;
- } break;
- }
-}
-void cdrom_cmd_getid(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- if (cdrom->pfifo_index) {
- log_fatal("CdlGetID: Expected exactly 0 parameters");
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_PCOUNT;
- cdrom->error_flags = GETSTAT_ERROR;
-
- return;
- }
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP1;
- cdrom->delayed_command = CDL_GETID;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, 3);
- RESP_PUSH(GETSTAT_MOTOR);
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = CDL_GETID;
- } break;
-
- case CD_STATE_SEND_RESP2: {
- if (cdrom->disc) {
- SET_BITS(ifr, IFR_INT, 2);
-
- switch (cdrom->cd_type) {
- case CDT_LICENSED: {
- for (int i = 0; i < GETID_RESPONSE_SIZE; i++)
- RESP_PUSH(g_getid_licensed[GETID_RESPONSE_END - i]);
- } break;
-
- case CDT_AUDIO: {
- for (int i = 0; i < GETID_RESPONSE_SIZE; i++)
- RESP_PUSH(g_getid_audio[GETID_RESPONSE_END - i]);
- } break;
-
- case CDT_UNKNOWN: {
- for (int i = 0; i < GETID_RESPONSE_SIZE; i++)
- RESP_PUSH(g_getid_unlicensed[GETID_RESPONSE_END - i]);
- } break;
- }
- } else {
- SET_BITS(ifr, IFR_INT, 5);
-
- for (int i = 0; i < GETID_RESPONSE_SIZE; i++)
- RESP_PUSH(g_getid_no_disc[GETID_RESPONSE_END - i]);
- }
-
- cdrom->state = CD_STATE_RECV_CMD;
- cdrom->delayed_command = CDL_NONE;
- } break;
- }
-}
-void cdrom_cmd_reads(psx_cdrom_t* cdrom) {
- cdrom->delayed_command = CDL_NONE;
- cdrom->ongoing_read_command = CDL_READS;
-
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- log_fatal("CdlReadS: CD_STATE_RECV_CMD");
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP1;
- cdrom->delayed_command = CDL_READS;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- printf("CdlReadS: CD_STATE_SEND_RESP1\n");
- log_fatal("CdlReadS: CD_STATE_SEND_RESP1");
-
- SET_BITS(ifr, IFR_INT, IFR_INT3);
- RESP_PUSH(GETSTAT_MOTOR);
-
- if (cdrom->mode & MODE_XA_ADPCM) {
- cdrom->xa_msf = cdrom->seek_msf;
- cdrom->xa_current_msf = cdrom->xa_msf;
- cdrom->xa_playing = 1;
- cdrom->xa_remaining_samples = 0;
-
- SET_BITS(status, STAT_ADPBUSY_MASK, STAT_ADPBUSY_MASK);
-
- printf("Play XA-ADPCM encoded song at %02u:%02u:%02u, filter=%u, file=%02x, channel=%02x (ReadS)\n",
- cdrom->xa_msf.m,
- cdrom->xa_msf.s,
- cdrom->xa_msf.f,
- (cdrom->mode & MODE_XA_FILTER) != 0,
- cdrom->xa_file,
- cdrom->xa_channel
- );
-
- int double_speed = cdrom->mode & MODE_SPEED;
-
- cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = CDL_READS;
-
- // if (cdrom->spin_delay) {
- // cdrom->irq_delay += cdrom->spin_delay;
- // cdrom->spin_delay = 0;
- // }
-
- return;
- }
-
- int err = psx_disc_seek(cdrom->disc, cdrom->seek_msf);
-
- if (err) {
- log_fatal("CdlReadS: Out of bounds seek");
-
- cdrom->irq_delay = DELAY_1MS * 600;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_INVSUBF;
- cdrom->error_flags = GETSTAT_SEEKERROR;
-
- return;
- }
-
- int double_speed = cdrom->mode & MODE_SPEED;
-
- cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = CDL_READS;
-
- if (cdrom->spin_delay) {
- cdrom->irq_delay += cdrom->spin_delay;
- cdrom->spin_delay = 0;
- }
- } break;
-
- case CD_STATE_SEND_RESP2: {
- log_fatal("CdlReadS: CD_STATE_SEND_RESP2");
-
- // Returning only non-ADPCM sectors causes desync for some
- // reason. I'll keep returning all sectors for now
-
- if (cdrom->mode & MODE_XA_ADPCM) {
- // printf("ReadS fetching non ADPCM sector...\n");
- cdrom_fetch_video_sector(cdrom);
-
- // printf("%02u:%02u:%02u - file=%02x channel=%02x sm=%02x ci=%02x\n",
- // cdrom->seek_msf.m,
- // cdrom->seek_msf.s,
- // cdrom->seek_msf.f,
- // cdrom->dfifo[0x10],
- // cdrom->dfifo[0x11],
- // cdrom->dfifo[0x12],
- // cdrom->dfifo[0x13]
- // );
- } else {
- psx_disc_seek(cdrom->disc, cdrom->seek_msf);
- psx_disc_read_sector(cdrom->disc, cdrom->dfifo);
-
- msf_add_f(&cdrom->seek_msf, 1);
- }
-
- int double_speed = cdrom->mode & MODE_SPEED;
-
- cdrom->irq_delay = double_speed ? READ_DOUBLE_DELAY : READ_SINGLE_DELAY;
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = CDL_READS;
- cdrom->dfifo_index = 0;
-
- SET_BITS(ifr, IFR_INT, IFR_INT1);
- RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
- } break;
- }
-}
-void cdrom_cmd_readtoc(psx_cdrom_t* cdrom) {
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- cdrom->status |= STAT_BUSYSTS_MASK;
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP1;
- cdrom->delayed_command = CDL_READTOC;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- SET_BITS(ifr, IFR_INT, 3);
- RESP_PUSH(GETSTAT_MOTOR | GETSTAT_READ);
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP2;
- cdrom->delayed_command = CDL_READTOC;
- } break;
-
- case CD_STATE_SEND_RESP2: {
- SET_BITS(ifr, IFR_INT, 2);
- RESP_PUSH(GETSTAT_MOTOR);
-
- cdrom->state = CD_STATE_RECV_CMD;
- cdrom->delayed_command = CDL_NONE;
- } break;
- }
-}
-void cdrom_cmd_videocd(psx_cdrom_t* cdrom) {
- switch (cdrom->state) {
- case CD_STATE_RECV_CMD: {
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_SEND_RESP1;
- cdrom->delayed_command = CDL_VIDEOCD;
- cdrom->pfifo_index = 0;
- } break;
-
- case CD_STATE_SEND_RESP1: {
- printf("VideoCD task %02x\n", cdrom->pfifo[4]);
- SET_BITS(ifr, IFR_INT, 3);
-
- switch (cdrom->pfifo[4]) {
- case 0: {
- RESP_PUSH(0x00);
- RESP_PUSH(0x00);
- RESP_PUSH(0x00);
- RESP_PUSH(0x00);
- RESP_PUSH(0x00);
- RESP_PUSH(GETSTAT_MOTOR);
- } break;
-
- case 1: {
- RESP_PUSH(0x00);
- RESP_PUSH(0x00);
- RESP_PUSH(0x00);
- RESP_PUSH(0x00);
- RESP_PUSH(0x81);
- RESP_PUSH(GETSTAT_MOTOR);
- } break;
-
- case 2: {
- RESP_PUSH(0x00);
- RESP_PUSH(0x00);
- RESP_PUSH(0x00);
- RESP_PUSH(0x00);
- RESP_PUSH(0x05);
- RESP_PUSH(GETSTAT_MOTOR);
- } break;
- }
-
- cdrom->irq_delay = DELAY_1MS;
- cdrom->state = CD_STATE_RECV_CMD;
- cdrom->delayed_command = CDL_NONE;
- } break;
- }
-}
-
-typedef void (*cdrom_cmd_t)(psx_cdrom_t*);
-
-const char* g_psx_cdrom_command_names[] = {
- "CdlUnimplemented",
- "CdlGetstat",
- "CdlSetloc",
- "CdlPlay",
- "CdlUnimplemented",
- "CdlUnimplemented",
- "CdlReadn",
- "CdlMotoron",
- "CdlStop",
- "CdlPause",
- "CdlInit",
- "CdlMute",
- "CdlUnmute",
- "CdlSetfilter",
- "CdlSetmode",
- "CdlGetparam",
- "CdlGetlocl",
- "CdlGetlocp",
- "CdlSetsession",
- "CdlGettn",
- "CdlGettd",
- "CdlSeekl",
- "CdlSeekp",
- "CdlUnimplemented",
- "CdlUnimplemented",
- "CdlTest",
- "CdlGetid",
- "CdlReads",
- "CdlUnimplemented",
- "CdlUnimplemented",
- "CdlReadtoc",
- "CdlVideoCD"
-};
-
-cdrom_cmd_t g_psx_cdrom_command_table[] = {
- cdrom_cmd_unimplemented,
- cdrom_cmd_getstat,
- cdrom_cmd_setloc,
- cdrom_cmd_play,
- cdrom_cmd_unimplemented,
- cdrom_cmd_unimplemented,
- cdrom_cmd_readn,
- cdrom_cmd_motoron,
- cdrom_cmd_stop,
- cdrom_cmd_pause,
- cdrom_cmd_init,
- cdrom_cmd_mute,
- cdrom_cmd_unmute,
- cdrom_cmd_setfilter,
- cdrom_cmd_setmode,
- cdrom_cmd_getparam,
- cdrom_cmd_getlocl,
- cdrom_cmd_getlocp,
- cdrom_cmd_setsession,
- cdrom_cmd_gettn,
- cdrom_cmd_gettd,
- cdrom_cmd_seekl,
- cdrom_cmd_seekp,
- cdrom_cmd_unimplemented,
- cdrom_cmd_unimplemented,
- cdrom_cmd_test,
- cdrom_cmd_getid,
- cdrom_cmd_reads,
- cdrom_cmd_unimplemented,
- cdrom_cmd_unimplemented,
- cdrom_cmd_readtoc,
- cdrom_cmd_videocd,
-
- // Actually an unimplemented command, we use this
- // index for CD error handling
- cdrom_cmd_error
-};
-
-typedef uint8_t (*psx_cdrom_read_function_t)(psx_cdrom_t*);
-typedef void (*psx_cdrom_write_function_t)(psx_cdrom_t*, uint8_t);
-
-uint8_t cdrom_read_status(psx_cdrom_t* cdrom) {
- return cdrom->status;
-}
-
-uint8_t cdrom_read_rfifo(psx_cdrom_t* cdrom) {
- if (cdrom->rfifo_index < 0)
- return 0;
-
- uint8_t data = cdrom->rfifo[--cdrom->rfifo_index];
-
- if (cdrom->rfifo_index == 0)
- SET_BITS(status, STAT_RSLRRDY_MASK, 0);
-
- return data;
-}
-
-uint8_t cdrom_read_dfifo(psx_cdrom_t* cdrom) {
- if (!cdrom->dfifo_full)
- return 0;
-
- int sector_size_bit = cdrom->mode & MODE_SECTOR_SIZE;
-
- uint32_t sector_size = sector_size_bit ? 0x924 : 0x800;
- uint32_t offset = sector_size_bit ? 12 : 24;
-
- if (cdrom->dfifo_index < sector_size) {
- SET_BITS(status, STAT_DRQSTS_MASK, STAT_DRQSTS_MASK);
-
- uint8_t data = cdrom->dfifo[offset + (cdrom->dfifo_index++)];
-
- if (cdrom->dfifo_index >= sector_size)
- SET_BITS(status, STAT_DRQSTS_MASK, 0);
-
- return data;
- }
-
- return 0;
-}
-
-uint8_t cdrom_read_ier(psx_cdrom_t* cdrom) {
- return cdrom->ier;
-}
-
-uint8_t cdrom_read_ifr(psx_cdrom_t* cdrom) {
- return 0xe0 | cdrom->ifr;
-}
-
-void cdrom_write_status(psx_cdrom_t* cdrom, uint8_t value) {
- SET_BITS(status, STAT_INDEX_MASK, value);
-}
-
-void cdrom_write_cmd(psx_cdrom_t* cdrom, uint8_t value) {
- printf("%s(%02x) %u params=[%02x, %02x, %02x, %02x, %02x, %02x]\n",
- g_psx_cdrom_command_names[value],
- value,
- cdrom->pfifo_index,
- cdrom->pfifo[0],
- cdrom->pfifo[1],
- cdrom->pfifo[2],
- cdrom->pfifo[3],
- cdrom->pfifo[4],
- cdrom->pfifo[5]
- );
-
- cdrom->command = value;
- cdrom->state = CD_STATE_RECV_CMD;
-
- // Required for Spyro - The Year of the Dragon
- if (!cdrom->command) {
- cdrom->irq_delay = DELAY_1MS * 600;
- cdrom->delayed_command = CDL_ERROR;
- cdrom->state = CD_STATE_ERROR;
- cdrom->error = ERR_INVSUBF;
- cdrom->error_flags = GETSTAT_SEEKERROR;
-
- return;
- }
-
- g_psx_cdrom_command_table[value](cdrom);
-}
-
-void cdrom_write_pfifo(psx_cdrom_t* cdrom, uint8_t value) {
- cdrom->pfifo[(cdrom->pfifo_index++) & 0xf] = value;
-
- SET_BITS(status, STAT_PRMWRDY_MASK, (cdrom->pfifo_index & 0x10) ? 0x0 : 0xff);
-
- cdrom->pfifo_index &= 0x1f;
-}
-
-void cdrom_write_req(psx_cdrom_t* cdrom, uint8_t value) {
- if (value & REQ_BFRD) {
- SET_BITS(status, STAT_DRQSTS_MASK, STAT_DRQSTS_MASK);
-
- cdrom->dfifo_full = 1;
- } else {
- SET_BITS(status, STAT_DRQSTS_MASK, 0);
-
- cdrom->dfifo_full = 0;
- }
-}
-
-void cdrom_write_smdout(psx_cdrom_t* cdrom, uint8_t value) {
- log_fatal("Sound map data out unimplemented");
-}
-
-void cdrom_write_ier(psx_cdrom_t* cdrom, uint8_t value) {
- cdrom->ier = value;
-}
-
-void cdrom_write_ifr(psx_cdrom_t* cdrom, uint8_t value) {
- cdrom->ifr &= ~(value & 0x1f);
-
- // Clear Parameter FIFO
- if (value & 0x40) {
- cdrom->pfifo_index = 0;
-
- SET_BITS(
- status,
- (STAT_PRMEMPT_MASK | STAT_PRMWRDY_MASK),
- (STAT_PRMEMPT_MASK | STAT_PRMWRDY_MASK)
- );
- }
-}
-
-void cdrom_write_sminfo(psx_cdrom_t* cdrom, uint8_t value) {
- log_fatal("Sound map coding info unimplemented");
-}
-
-void cdrom_write_lcdlspuv(psx_cdrom_t* cdrom, uint8_t value) {
- cdrom->vapp[0] = value;
-}
-
-void cdrom_write_rcdrspuv(psx_cdrom_t* cdrom, uint8_t value) {
- cdrom->vapp[3] = value;
-}
-
-void cdrom_write_rcdlspuv(psx_cdrom_t* cdrom, uint8_t value) {
- cdrom->vapp[2] = value;
-}
-
-void cdrom_write_lcdrspuv(psx_cdrom_t* cdrom, uint8_t value) {
- cdrom->vapp[1] = value;
-}
-
-void cdrom_write_volume(psx_cdrom_t* cdrom, uint8_t value) {
- cdrom->xa_mute = value & 1;
-
- if (value & 0x20) {
- cdrom->vol[0] = cdrom->vapp[0];
- cdrom->vol[1] = cdrom->vapp[1];
- cdrom->vol[2] = cdrom->vapp[2];
- cdrom->vol[3] = cdrom->vapp[3];
- }
-}
-
-psx_cdrom_read_function_t g_psx_cdrom_read_table[] = {
- cdrom_read_status, cdrom_read_rfifo, cdrom_read_dfifo, cdrom_read_ier,
- cdrom_read_status, cdrom_read_rfifo, cdrom_read_dfifo, cdrom_read_ifr,
- cdrom_read_status, cdrom_read_rfifo, cdrom_read_dfifo, cdrom_read_ier,
- cdrom_read_status, cdrom_read_rfifo, cdrom_read_dfifo, cdrom_read_ifr
-};
-
-psx_cdrom_write_function_t g_psx_cdrom_write_table[] = {
- cdrom_write_status, cdrom_write_cmd , cdrom_write_pfifo , cdrom_write_req ,
- cdrom_write_status, cdrom_write_smdout , cdrom_write_ier , cdrom_write_ifr ,
- cdrom_write_status, cdrom_write_sminfo , cdrom_write_lcdlspuv, cdrom_write_lcdrspuv,
- cdrom_write_status, cdrom_write_rcdrspuv, cdrom_write_rcdlspuv, cdrom_write_volume
-};
-
-const char* g_psx_cdrom_read_names_table[] = {
- "cdrom_read_status", "cdrom_read_rfifo", "cdrom_read_dfifo", "cdrom_read_ier",
- "cdrom_read_status", "cdrom_read_rfifo", "cdrom_read_dfifo", "cdrom_read_ifr",
- "cdrom_read_status", "cdrom_read_rfifo", "cdrom_read_dfifo", "cdrom_read_ier",
- "cdrom_read_status", "cdrom_read_rfifo", "cdrom_read_dfifo", "cdrom_read_ifr"
-};
-
-const char* g_psx_cdrom_write_names_table[] = {
- "cdrom_write_status", "cdrom_write_cmd" , "cdrom_write_pfifo" , "cdrom_write_req" ,
- "cdrom_write_status", "cdrom_write_smdout" , "cdrom_write_ier" , "cdrom_write_ifr" ,
- "cdrom_write_status", "cdrom_write_sminfo" , "cdrom_write_lcdlspuv", "cdrom_write_lcdrspuv",
- "cdrom_write_status", "cdrom_write_rcdrspuv", "cdrom_write_rcdlspuv", "cdrom_write_volume"
-};
-
-psx_cdrom_t* psx_cdrom_create(void) {
- return (psx_cdrom_t*)malloc(sizeof(psx_cdrom_t));
-}
-
-#define XA_STEREO_SAMPLES 2016
-#define XA_MONO_SAMPLES 4032
-#define XA_DECODED_SAMPLES 37632
-#define XA_RINGBUF_SIZE 32
-#define XA_STEREO_RESAMPLE_SIZE 2352
-#define XA_MONO_RESAMPLE_SIZE 4704
-
-void psx_cdrom_init(psx_cdrom_t* cdrom, psx_ic_t* ic) {
- memset(cdrom, 0, sizeof(psx_cdrom_t));
-
- cdrom->io_base = PSX_CDROM_BEGIN;
- cdrom->io_size = PSX_CDROM_SIZE;
-
- cdrom->ic = ic;
- cdrom->status = STAT_PRMEMPT_MASK | STAT_PRMWRDY_MASK | STAT_RSLRRDY_MASK;
- cdrom->dfifo = malloc(CD_SECTOR_SIZE);
- cdrom->cdda_buf = malloc(CD_SECTOR_SIZE);
-
- // Initialize XA state
- cdrom->xa_sector_buf = malloc(CD_SECTOR_SIZE);
- cdrom->xa_left_buf = malloc(XA_STEREO_SAMPLES * sizeof(int16_t));
- cdrom->xa_right_buf = malloc(XA_STEREO_SAMPLES * sizeof(int16_t));
- cdrom->xa_mono_buf = malloc(XA_MONO_SAMPLES * sizeof(int16_t));
- cdrom->xa_upsample_buf = malloc(((14112 * 2) + 6) * sizeof(int16_t));
- cdrom->xa_left_resample_buf = malloc((XA_STEREO_RESAMPLE_SIZE * 2) * sizeof(int16_t));
- cdrom->xa_right_resample_buf = malloc((XA_STEREO_RESAMPLE_SIZE * 2) * sizeof(int16_t));
- cdrom->xa_mono_resample_buf = malloc((XA_MONO_RESAMPLE_SIZE * 2) * sizeof(int16_t));
- cdrom->xa_step = 6;
-
- // We will use this whenever we implement proper
- // XA interpolation
- (void)g_zigzag_table;
-
- memset(cdrom->xa_left_buf, 0, XA_STEREO_SAMPLES * sizeof(int16_t));
- memset(cdrom->xa_right_buf, 0, XA_STEREO_SAMPLES * sizeof(int16_t));
- memset(cdrom->xa_mono_buf, 0, XA_MONO_SAMPLES * sizeof(int16_t));
- memset(cdrom->xa_upsample_buf, 0, ((14112 * 2) + 6) * sizeof(int16_t));
- memset(cdrom->xa_left_resample_buf, 0, (XA_STEREO_RESAMPLE_SIZE * 2) * sizeof(int16_t));
- memset(cdrom->xa_right_resample_buf, 0, (XA_STEREO_RESAMPLE_SIZE * 2) * sizeof(int16_t));
- memset(cdrom->xa_mono_resample_buf, 0, (XA_MONO_RESAMPLE_SIZE * 2) * sizeof(int16_t));
-
- cdrom->vol[0] = 0x80;
- cdrom->vol[1] = 0x00;
- cdrom->vol[2] = 0x80;
- cdrom->vol[3] = 0x00;
- cdrom->vapp[0] = 0x80;
- cdrom->vapp[1] = 0x00;
- cdrom->vapp[2] = 0x80;
- cdrom->vapp[3] = 0x00;
-
- cdrom->seek_msf.m = 0;
- cdrom->seek_msf.s = 2;
- cdrom->seek_msf.f = 0;
-}
-
-uint32_t psx_cdrom_read32(psx_cdrom_t* cdrom, uint32_t offset) {
- // log_set_quiet(0);
- log_fatal("Unhandled 32-bit CDROM read at offset %08x", offset);
-
- // exit(1);
-
- return 0x0;
-}
-
-uint16_t psx_cdrom_read16(psx_cdrom_t* cdrom, uint32_t offset) {
- // log_set_quiet(0);
- log_fatal("Unhandled 16-bit CDROM read at offset %08x", offset);
-
- // exit(1);
-
- return 0x0;
-}
-
-uint8_t psx_cdrom_read8(psx_cdrom_t* cdrom, uint32_t offset) {
- uint8_t data = g_psx_cdrom_read_table[(STAT_INDEX << 2) | offset](cdrom);
-
- if (((STAT_INDEX << 2) | offset) == 2)
- return data;
-
- // // log_set_quiet(0);
- // log_fatal("%s (read %02x)", g_psx_cdrom_read_names_table[(STAT_INDEX << 2) | offset], data);
- // log_set_quiet(1);
-
- return data;
-}
-
-void psx_cdrom_write32(psx_cdrom_t* cdrom, uint32_t offset, uint32_t value) {
- // log_set_quiet(0);
- log_fatal("Unhandled 32-bit CDROM write at offset %08x (%08x)", offset, value);
-
- // exit(1);
-}
-
-void psx_cdrom_write16(psx_cdrom_t* cdrom, uint32_t offset, uint16_t value) {
- // log_set_quiet(0);
- log_fatal("Unhandled 16-bit CDROM write at offset %08x (%04x)", offset, value);
-
- // exit(1);
-}
-
-void psx_cdrom_write8(psx_cdrom_t* cdrom, uint32_t offset, uint8_t value) {
- // // log_set_quiet(0);
- // log_fatal("%s (write %02x)", g_psx_cdrom_write_names_table[(STAT_INDEX << 2) | offset], value);
- // log_set_quiet(1);
-
- g_psx_cdrom_write_table[(STAT_INDEX << 2) | offset](cdrom, value);
-}
-
-void psx_cdrom_update(psx_cdrom_t* cdrom, int cyc) {
- if (cdrom->irq_delay) {
- cdrom->irq_delay -= cyc;
-
- if (cdrom->irq_delay <= 0) {
- if (!cdrom->irq_disable) {
- psx_ic_irq(cdrom->ic, IC_CDROM);
- } else {
- cdrom->irq_disable = 0;
- }
-
- cdrom->irq_delay = 0;
-
- if (cdrom->delayed_command) {
- // // log_set_quiet(0);
- // log_fatal("%s(%02x) (Delayed)",
- // g_psx_cdrom_command_names[cdrom->delayed_command],
- // cdrom->delayed_command
- // );
- // log_set_quiet(1);
- g_psx_cdrom_command_table[cdrom->delayed_command](cdrom);
- }
-
- // // log_set_quiet(0);
- // log_fatal("CDROM INT%u", cdrom->ifr & 0x7);
- // log_set_quiet(1);
- }
- }
-}
-
-const char* g_psx_cdrom_extensions[] = {
- "cue",
- "bin",
- 0
-};
-
-enum {
- CD_EXT_CUE,
- CD_EXT_BIN,
- CD_EXT_UNSUPPORTED
-};
-
-int cdrom_get_extension(const char* path) {
- const char* ptr = &path[strlen(path) - 1];
- int i = 0;
-
- while ((*ptr != '.') && (ptr != path))
- ptr--;
-
- if (ptr == path)
- return CD_EXT_UNSUPPORTED;
-
- while (g_psx_cdrom_extensions[i]) {
- if (!strcmp(ptr + 1, g_psx_cdrom_extensions[i]))
- return i;
-
- ++i;
- }
-
- return CD_EXT_UNSUPPORTED;
-}
-
-void cdrom_check_cd_type(psx_cdrom_t* cdrom) {
- char buf[CD_SECTOR_SIZE];
-
- // Seek to Primary Volume Descriptor
- msf_t pvd = { 0, 2, 16 };
-
- // If the disc is smaller than 16 sectors
- // then it can't be a PlayStation game.
- // Audio discs should also have ISO volume
- // descriptors, so it's probably something else
- // entirely.
- if (psx_disc_seek(cdrom->disc, pvd)) {
- cdrom->cd_type = CDT_UNKNOWN;
-
- return;
- }
-
- psx_disc_read_sector(cdrom->disc, buf);
-
- // Check for the "PLAYSTATION" string at PVD offset 20h
-
- // Patch 20 byte so comparison is done correctly
- buf[0x2b] = 0;
-
- if (strncmp(&buf[0x20], "PLAYSTATION", 12)) {
- cdrom->cd_type = CDT_AUDIO;
-
- return;
- }
-
- cdrom->cd_type = CDT_LICENSED;
-}
-
-void psx_cdrom_open(psx_cdrom_t* cdrom, const char* path) {
- cdrom->disc = psx_disc_create();
-
- int len = strlen(path);
-
- char* lower = malloc(len + 1);
-
- for (int i = 0; i < len; i++)
- lower[i] = tolower(path[i]);
-
- lower[len] = '\0';
-
- int ext = cdrom_get_extension(lower);
- int error = 0;
-
- switch (ext) {
- case CD_EXT_CUE: {
- psxd_cue_t* cue = psxd_cue_create();
-
- psxd_cue_init_disc(cue, cdrom->disc);
- psxd_cue_init(cue);
-
- error = psxd_cue_load(cue, path);
-
- if (error)
- break;
-
- cdrom_check_cd_type(cdrom);
- } break;
-
- case CD_EXT_BIN: {
- psxd_bin_t* bin = psxd_bin_create();
-
- psxd_bin_init_disc(bin, cdrom->disc);
- psxd_bin_init(bin);
-
- error = psxd_bin_load(bin, path);
-
- if (error)
- break;
-
- cdrom_check_cd_type(cdrom);
- } break;
-
- case CD_EXT_UNSUPPORTED: {
- log_fatal("Unsupported disc format");
-
- cdrom->cd_type = CDT_UNKNOWN;
- } break;
- }
-
- free(lower);
-
- if (error) {
- log_fatal("Error loading file \'%s\'", path);
-
- exit(1);
- }
-}
-
-static const int g_spu_pos_adpcm_table[] = {
- 0, +60, +115, +98, +122
-};
-
-static const int g_spu_neg_adpcm_table[] = {
- 0, 0, -52, -55, -60
-};
-
-void cdrom_resample_xa_buf(psx_cdrom_t* cdrom, int16_t* dst, int16_t* src, int stereo, int16_t ls) {
- int f18khz = ((cdrom->xa_sector_buf[0x13] >> 2) & 1) == 1;
- int sample_count = stereo ? XA_STEREO_SAMPLES : XA_MONO_SAMPLES;
- int resample_count = stereo ? XA_STEREO_RESAMPLE_SIZE : XA_MONO_RESAMPLE_SIZE;
-
- resample_count *= f18khz + 1;
-
- // Nearest neighbor
- // for (int i = 0; i < sample_count; i++)
- // for (int k = 0; k < 7; k++)
- // cdrom->xa_upsample_buf[(i*7)+k] = src[i];
-
- // Linear Upsampling
- int16_t a = ls;
- int16_t b = src[0];
-
- for (int k = 0; k < 7; k++)
- cdrom->xa_upsample_buf[k] = a + ((k+1)/8) * (b - a);
-
- for (int i = 1; i < sample_count; i++) {
- a = b;
- b = src[i];
-
- for (int k = 0; k < 7; k++)
- cdrom->xa_upsample_buf[(i*7)+k] =
- a + ((k+1)/8) * (b - a);
- }
-
- int m = f18khz ? 3 : 6;
-
- for (int i = 0; i < resample_count; i++)
- dst[i] = cdrom->xa_upsample_buf[i*m];
-
- cdrom->xa_remaining_samples = resample_count;
-}
-
-void cdrom_decode_xa_block(psx_cdrom_t* cdrom, int idx, int blk, int nib, int16_t* buf, int16_t* h) {
- int shift = 12 - (cdrom->xa_sector_buf[idx + 4 + blk * 2 + nib] & 0x0F);
- int filter = (cdrom->xa_sector_buf[idx + 4 + blk * 2 + nib] & 0x30) >> 4;
-
- int32_t f0 = g_spu_pos_adpcm_table[filter];
- int32_t f1 = g_spu_neg_adpcm_table[filter];
-
- for (int j = 0; j < 28; j++) {
- uint16_t n = (cdrom->xa_sector_buf[idx + 16 + blk + j * 4] >> (nib * 4)) & 0x0f;
-
- int16_t t = (int16_t)(n << 12) >> 12;
- int16_t s = (t << shift) + (((h[0] * f0) + (h[1] * f1) + 32) / 64);
-
- s = (s < INT16_MIN) ? INT16_MIN : ((s > INT16_MAX) ? INT16_MAX : s);
-
- h[1] = h[0];
- h[0] = s;
-
- buf[j] = s;
- }
-}
-
-void cdrom_decode_xa_sector(psx_cdrom_t* cdrom, void* buf) {
- int src = 24;
-
- int16_t left[28];
- int16_t right[28];
-
- int16_t* left_ptr = cdrom->xa_left_buf;
- int16_t* right_ptr = cdrom->xa_right_buf;
- int16_t* mono_ptr = cdrom->xa_mono_buf;
-
- for (int i = 0; i < 18; i++) {
- for (int blk = 0; blk < 4; blk++) {
- if (cdrom->xa_sector_buf[0x13] & 1) {
- cdrom_decode_xa_block(cdrom, src, blk, 0, left, cdrom->xa_left_h);
- cdrom_decode_xa_block(cdrom, src, blk, 1, right, cdrom->xa_right_h);
-
- for (int i = 0; i < 28; i++) {
- *left_ptr++ = left[i];
- *right_ptr++ = right[i];
- }
- } else {
- cdrom_decode_xa_block(cdrom, src, blk, 0, left, cdrom->xa_left_h);
-
- for (int i = 0; i < 28; i++)
- *mono_ptr++ = left[i];
-
- cdrom_decode_xa_block(cdrom, src, blk, 1, left, cdrom->xa_left_h);
-
- for (int i = 0; i < 28; i++)
- *mono_ptr++ = left[i];
- }
- }
-
- src += 128;
- }
-}
-
-void cdrom_fetch_xa_sector(psx_cdrom_t* cdrom) {
- while (true) {
- if (psx_disc_seek(cdrom->disc, cdrom->xa_msf)) {
- cdrom->xa_playing = 0;
- cdrom->xa_remaining_samples = 0;
-
- return;
- }
-
- psx_disc_read_sector(cdrom->disc, cdrom->xa_sector_buf);
-
- msf_add_f(&cdrom->xa_msf, 1);
-
- // Check for EOR bit
- if (cdrom->xa_sector_buf[0x12] & 1)
- return;
-
- // Check Audio bit
- if (!(cdrom->xa_sector_buf[0x12] & 4))
- continue;
-
- // If we get here it means this is a real-time audio sector.
- // If the XA filter is disabled, we're done
- if (!(cdrom->mode & MODE_XA_FILTER))
- return;
-
- // Else check XA file/channel
- int file_eq = cdrom->xa_sector_buf[0x10] == cdrom->xa_file;
- int channel_eq = cdrom->xa_sector_buf[0x11] == cdrom->xa_channel;
-
- // If they are equal to our filter values, we're done
- // else keep searching
- if (file_eq && channel_eq)
- return;
- }
-}
-
-void cdrom_apply_volume_settings(psx_cdrom_t* cdrom) {
- int16_t* ptr = cdrom->cdda_buf;
-
- float ll_vol = (((float)cdrom->vol[0]) / 255.0f);
- float lr_vol = (((float)cdrom->vol[1]) / 255.0f);
- float rl_vol = (((float)cdrom->vol[2]) / 255.0f);
- float rr_vol = (((float)cdrom->vol[3]) / 255.0f);
-
- for (int i = 0; i < CD_SECTOR_SIZE >> 1; i += 2) {
- ptr[i ] = ptr[i ] * ll_vol + ptr[i+1] * rl_vol;
- ptr[i+1] = ptr[i+1] * rr_vol + ptr[i ] * lr_vol;
- }
-}
-
-void psx_cdrom_get_cdda_samples(psx_cdrom_t* cdrom, void* buf, int size, psx_spu_t* spu) {
- memset(buf, 0, size);
-
- if (!cdrom->disc)
- return;
-
- if (cdrom->xa_playing) {
- int16_t* ptr = (int16_t*)buf;
-
- for (int i = 0; i < (size >> 2); i++) {
- int stereo = (cdrom->xa_sector_buf[0x13] & 1) == 1;
-
- if (!cdrom->xa_remaining_samples) {
- cdrom_fetch_xa_sector(cdrom);
-
- if (cdrom->xa_sector_buf[0x12] & 0x01) {
- SET_BITS(status, STAT_ADPBUSY_MASK, 0);
-
- printf("Pausing XA-ADPCM playback\n");
-
- cdrom->xa_playing = 0;
- cdrom->xa_remaining_samples = 0;
-
- return;
- }
-
- msf_add_f(&cdrom->xa_current_msf, 1);
-
- stereo = (cdrom->xa_sector_buf[0x13] & 1) == 1;
-
- cdrom_decode_xa_sector(cdrom, buf);
-
- memset(cdrom->xa_upsample_buf, 0, ((14112 * 2) + 6) * sizeof(int16_t));
- memset(cdrom->xa_left_resample_buf, 0, (XA_STEREO_RESAMPLE_SIZE * 2) * sizeof(int16_t));
- memset(cdrom->xa_right_resample_buf, 0, (XA_STEREO_RESAMPLE_SIZE * 2) * sizeof(int16_t));
-
- if (stereo) {
- cdrom_resample_xa_buf(cdrom, cdrom->xa_left_resample_buf, cdrom->xa_left_buf, stereo, cdrom->xa_last_left_sample);
- cdrom_resample_xa_buf(cdrom, cdrom->xa_right_resample_buf, cdrom->xa_right_buf, stereo, cdrom->xa_last_right_sample);
- } else {
- cdrom_resample_xa_buf(cdrom, cdrom->xa_mono_resample_buf, cdrom->xa_mono_buf, stereo, cdrom->xa_last_mono_sample);
- }
-
- cdrom->xa_sample_idx = 0;
- }
-
- if (cdrom->xa_mute) {
- *ptr++ = 0;
- *ptr++ = 0;
-
- return;
- }
-
- float ll_vol = (((float)cdrom->vol[0]) / 255.0f);
- float rr_vol = (((float)cdrom->vol[3]) / 255.0f);
-
- if (stereo) {
- cdrom->xa_last_left_sample = cdrom->xa_left_resample_buf[cdrom->xa_sample_idx];
- cdrom->xa_last_right_sample = cdrom->xa_right_resample_buf[cdrom->xa_sample_idx++];
-
- float lr_vol = (((float)cdrom->vol[1]) / 255.0f);
- float rl_vol = (((float)cdrom->vol[2]) / 255.0f);
-
- *ptr++ = (cdrom->xa_last_left_sample * ll_vol) + (cdrom->xa_last_right_sample * rl_vol);
- *ptr++ = (cdrom->xa_last_left_sample * lr_vol) + (cdrom->xa_last_right_sample * rr_vol);
-
- } else {
- cdrom->xa_last_mono_sample = cdrom->xa_mono_resample_buf[cdrom->xa_sample_idx++];
-
- *ptr++ = cdrom->xa_last_mono_sample * ll_vol;
- *ptr++ = cdrom->xa_last_mono_sample * rr_vol;
- }
-
- --cdrom->xa_remaining_samples;
- }
-
- return;
- }
-
- if (!cdrom->cdda_playing) {
- memset(buf, 0, size);
-
- return;
- }
-
- // Seek to that address and read sector
- if (psx_disc_seek(cdrom->disc, cdrom->cdda_msf))
- cdrom->cdda_playing = 0;
-
- psx_disc_read_sector(cdrom->disc, cdrom->cdda_buf);
-
- ++cdrom->cdda_sectors_played;
-
- // Increment sector
- msf_add_f(&cdrom->cdda_msf, 1);
-
- cdrom_apply_volume_settings(cdrom);
-
- memcpy(buf, cdrom->cdda_buf, size);
-
- psx_spu_update_cdda_buffer(spu, cdrom->cdda_buf);
-
- // Handle report IRQ
- if (cdrom->cdda_sectors_played == CD_SECTORS_PS) {
- if (cdrom->mode & MODE_REPORT) {
- SET_BITS(ifr, IFR_INT, 1);
-
- msf_t track, current = cdrom->cdda_msf;
-
- msf_from_bcd(¤t);
-
- psx_disc_get_track_addr(cdrom->disc, &track, cdrom->cdda_track);
-
- unsigned int track_s = (track.m * 60) + track.s;
- unsigned int current_s = (current.m * 60) + current.s;
- unsigned int diff = current_s - track_s;
-
- current.s = diff;
- current.m = 0;
-
- msf_adjust(¤t);
- //msf_to_bcd(¤t);
-
- RESP_PUSH(0);
- RESP_PUSH(0);
- RESP_PUSH(cdrom->cdda_msf.f);
- RESP_PUSH(current.s | 0x80);
- RESP_PUSH(current.m);
- RESP_PUSH(0);
- RESP_PUSH(cdrom->cdda_track);
- RESP_PUSH(GETSTAT_PLAY);
-
- psx_ic_irq(cdrom->ic, IC_CDROM);
- }
-
- cdrom->cdda_sectors_played = 0;
- }
-}
-
-void psx_cdrom_destroy(psx_cdrom_t* cdrom) {
- if (cdrom->disc)
- psx_disc_destroy(cdrom->disc);
-
- free(cdrom);
-}
\ No newline at end of file
--- a/psx/dev/cdrom.h
+++ /dev/null
@@ -1,334 +1,0 @@
-#ifndef CDROM_H
-#define CDROM_H
-
-#include <stdint.h>
-#include <stdio.h>
-
-#include "ic.h"
-#include "../disc.h"
-#include "../disc/cue.h"
-#include "../disc/bin.h"
-#include "../msf.h"
-#include "spu.h"
-
-#define DELAY_1MS (0xc4e1)
-// #define READ_SINGLE_DELAY (0x6e1cd)
-// #define READ_DOUBLE_DELAY (0x36cd2)
-// #define DELAY_1MS (PSX_CPU_CPS / 1000)
-#define READ_SINGLE_DELAY (PSX_CPU_CPS / 75)
-#define READ_DOUBLE_DELAY (PSX_CPU_CPS / (2 * 75))
-
-#define PSX_CDROM_BEGIN 0x1f801800
-#define PSX_CDROM_SIZE 0x4
-#define PSX_CDROM_END 0x1f801803
-
-enum {
- CD_STATE_RECV_CMD,
- CD_STATE_SEND_RESP1,
- CD_STATE_SEND_RESP2,
- CD_STATE_ERROR
-};
-
-#define CDL_NONE 0x00
-#define CDL_GETSTAT 0x01
-#define CDL_SETLOC 0x02
-#define CDL_PLAY 0x03
-#define CDL_FORWARD 0x04
-#define CDL_BACKWARD 0x05
-#define CDL_READN 0x06
-#define CDL_MOTORON 0x07
-#define CDL_STOP 0x08
-#define CDL_PAUSE 0x09
-#define CDL_INIT 0x0a
-#define CDL_MUTE 0x0b
-#define CDL_DEMUTE 0x0c
-#define CDL_SETFILTER 0x0d
-#define CDL_SETMODE 0x0e
-#define CDL_GETPARAM 0x0f
-#define CDL_GETLOCL 0x10
-#define CDL_GETLOCP 0x11
-#define CDL_SETSESSION 0x12
-#define CDL_GETTN 0x13
-#define CDL_GETTD 0x14
-#define CDL_SEEKL 0x15
-#define CDL_SEEKP 0x16
-#define CDL_TEST 0x19
-#define CDL_GETID 0x1a
-#define CDL_READS 0x1b
-#define CDL_RESET 0x1c
-#define CDL_GETQ 0x1d
-#define CDL_READTOC 0x1e
-#define CDL_VIDEOCD 0x1f
-#define CDL_ERROR 0x20
-
-#define STAT_INDEX_MASK 0x3
-#define STAT_ADPBUSY_MASK 0x4
-#define STAT_PRMEMPT_MASK 0x8
-#define STAT_PRMWRDY_MASK 0x10
-#define STAT_RSLRRDY_MASK 0x20
-#define STAT_DRQSTS_MASK 0x40
-#define STAT_BUSYSTS_MASK 0x80
-#define STAT_INDEX (cdrom->status & STAT_INDEX_MASK)
-#define STAT_ADPBUSY (cdrom->status & STAT_ADPBUSY_MASK)
-#define STAT_PRMEMPT (cdrom->status & STAT_PRMEMPT_MASK)
-#define STAT_PRMWRDY (cdrom->status & STAT_PRMWRDY_MASK)
-#define STAT_RSLRRDY (cdrom->status & STAT_RSLRRDY_MASK)
-#define STAT_DRQSTS (cdrom->status & STAT_DRQSTS_MASK)
-#define STAT_BUSYSTS (cdrom->status & STAT_BUSYSTS_MASK)
-#define SET_BITS(reg, mask, v) { cdrom->reg &= ~mask; cdrom->reg |= v & mask; }
-#define IFR_INT 0x07
-#define IFR_INT1 0x01
-#define IFR_INT2 0x02
-#define IFR_INT3 0x03
-#define IFR_INT4 0x04
-#define IFR_INT5 0x05
-#define IFR_INT6 0x06
-#define IFR_INT7 0x07
-
-#define GETSTAT_ERROR 0x01
-#define GETSTAT_MOTOR 0x02
-#define GETSTAT_SEEKERROR 0x04
-#define GETSTAT_IDERROR 0x08
-#define GETSTAT_TRAYOPEN 0x10
-#define GETSTAT_READ 0x20
-#define GETSTAT_SEEK 0x40
-#define GETSTAT_PLAY 0x80
-
-/*
- 7 Speed (0=Normal speed, 1=Double speed)
- 6 XA-ADPCM (0=Off, 1=Send XA-ADPCM sectors to SPU Audio Input)
- 5 Sector Size (0=800h=DataOnly, 1=924h=WholeSectorExceptSyncBytes)
- 4 Ignore Bit (0=Normal, 1=Ignore Sector Size and Setloc position)
- 3 XA-Filter (0=Off, 1=Process only XA-ADPCM sectors that match Setfilter)
- 2 Report (0=Off, 1=Enable Report-Interrupts for Audio Play)
- 1 AutoPause (0=Off, 1=Auto Pause upon End of Track) ;for Audio Play
- 0 CDDA (0=Off, 1=Allow to Read CD-DA Sectors; ignore missing EDC)
-*/
-
-#define MODE_CDDA 0x01
-#define MODE_AUTOPAUSE 0x02
-#define MODE_REPORT 0x04
-#define MODE_XA_FILTER 0x08
-#define MODE_IGNORE 0x10
-#define MODE_SECTOR_SIZE 0x20
-#define MODE_XA_ADPCM 0x40
-#define MODE_SPEED 0x80
-
-/*
- 0-4 0 Not used (should be zero)
- 5 SMEN Want Command Start Interrupt on Next Command (0=No change, 1=Yes)
- 6 BFWR ...
- 7 BFRD Want Data (0=No/Reset Data Fifo, 1=Yes/Load Data Fifo)
-*/
-
-#define REQ_SMEN 0x20
-#define REQ_BFWR 0x40
-#define REQ_BFRD 0x80
-
-/*
- ___These values appear in the FIRST response; with stat.bit0 set___
- 10h - Invalid Sub_function (for command 19h), or invalid parameter value
- 20h - Wrong number of parameters
- 40h - Invalid command
- 80h - Cannot respond yet (eg. required info was not yet read from disk yet)
- (namely, TOC not-yet-read or so)
- (also appears if no disk inserted at all)
- ___These values appear in the SECOND response; with stat.bit2 set___
- 04h - Seek failed (when trying to use SeekL on Audio CDs)
- ___These values appear even if no command was sent; with stat.bit2 set___
- 08h - Drive door became opened
-*/
-
-#define ERR_INVSUBF 0x10
-#define ERR_PCOUNT 0x20
-#define ERR_INVALID 0x40
-#define ERR_BUSY 0x80
-#define ERR_SEEK 0x04
-#define ERR_LIDOPEN 0x08
-
-enum {
- CDT_LICENSED,
- CDT_AUDIO,
- CDT_UNKNOWN
-};
-
-typedef struct {
- uint32_t bus_delay;
- uint32_t io_base, io_size;
-
- psx_ic_t* ic;
-
- uint8_t status;
- uint8_t ifr;
- uint8_t ier;
-
- uint8_t pfifo[16];
- uint8_t rfifo[16];
- int pfifo_index;
- int rfifo_index;
-
- uint8_t* read_buf;
- uint8_t* dfifo;
- int dfifo_index;
- int dfifo_full;
-
- // GetStat bits
- uint8_t stat;
-
- // API
- int tray_open;
-
- // Setloc
- msf_t seek_msf;
- uint32_t seek_offset;
- int seek_pending;
-
- // Setmode
- uint8_t mode;
-
- int irq_delay;
- int irq_disable;
- uint8_t command;
- uint8_t delayed_command;
- uint8_t int_number;
- int state;
- int delayed_response;
- int spin_delay;
- uint8_t error;
- uint8_t error_flags;
- int ongoing_read_command;
- int gettd_track;
-
- // CDDA
- uint8_t* cdda_buf;
- int cdda_sector_offset;
- msf_t cdda_msf;
- int cdda_playing;
- int cdda_sectors_played;
- int cdda_track;
-
- // XA-ADPCM
- uint8_t* xa_sector_buf;
- msf_t xa_msf;
- msf_t xa_current_msf;
- int xa_playing;
- int xa_mute;
- uint8_t xa_file;
- uint8_t xa_channel;
- uint8_t xa_coding;
- int16_t xa_left_h[2];
- int16_t xa_right_h[2];
- int16_t* xa_left_buf;
- int16_t* xa_right_buf;
- int16_t* xa_mono_buf;
- int16_t* xa_decoded_buf;
- int16_t* xa_left_ring_buf;
- int16_t* xa_right_ring_buf;
- uint32_t xa_sample_idx;
- int xa_remaining_samples;
- uint32_t xa_step;
- uint32_t xa_ringbuf_pos;
- int16_t* xa_left_resample_buf;
- int16_t* xa_right_resample_buf;
- int16_t* xa_mono_resample_buf;
- int16_t* xa_upsample_buf;
- int16_t xa_last_left_sample;
- int16_t xa_last_right_sample;
- int16_t xa_last_mono_sample;
- uint8_t vol[4];
- uint8_t vapp[4];
-
- const char* path;
- psx_disc_t* disc;
-
- int cd_type;
-} psx_cdrom_t;
-
-psx_cdrom_t* psx_cdrom_create(void);
-void psx_cdrom_init(psx_cdrom_t*, psx_ic_t*);
-uint32_t psx_cdrom_read32(psx_cdrom_t*, uint32_t);
-uint16_t psx_cdrom_read16(psx_cdrom_t*, uint32_t);
-uint8_t psx_cdrom_read8(psx_cdrom_t*, uint32_t);
-void psx_cdrom_write32(psx_cdrom_t*, uint32_t, uint32_t);
-void psx_cdrom_write16(psx_cdrom_t*, uint32_t, uint16_t);
-void psx_cdrom_write8(psx_cdrom_t*, uint32_t, uint8_t);
-void psx_cdrom_update(psx_cdrom_t*, int);
-void psx_cdrom_destroy(psx_cdrom_t*);
-void psx_cdrom_open(psx_cdrom_t*, const char*);
-void psx_cdrom_get_cdda_samples(psx_cdrom_t*, void*, int, psx_spu_t*);
-
-/*
- Command Parameters Response(s)
- 00h - - INT5(11h,40h) ;reportedly "Sync" uh?
- 01h Getstat - INT3(stat)
- 02h Setloc E amm,ass,asect INT3(stat)
- 03h Play E (track) INT3(stat), optional INT1(report bytes)
- 04h Forward E - INT3(stat), optional INT1(report bytes)
- 05h Backward E - INT3(stat), optional INT1(report bytes)
- 06h ReadN E - INT3(stat), INT1(stat), datablock
- 07h MotorOn E - INT3(stat), INT2(stat)
- 08h Stop E - INT3(stat), INT2(stat)
- 09h Pause E - INT3(stat), INT2(stat)
- 0Ah Init - INT3(late-stat), INT2(stat)
- 0Bh Mute E - INT3(stat)
- 0Ch Demute E - INT3(stat)
- 0Dh Setfilter E file,channel INT3(stat)
- 0Eh Setmode mode INT3(stat)
- 0Fh Getparam - INT3(stat,mode,null,file,channel)
- 10h GetlocL E - INT3(amm,ass,asect,mode,file,channel,sm,ci)
- 11h GetlocP E - INT3(track,index,mm,ss,sect,amm,ass,asect)
- 12h SetSession E session INT3(stat), INT2(stat)
- 13h GetTN E - INT3(stat,first,last) ;BCD
- 14h GetTD E track (BCD) INT3(stat,mm,ss) ;BCD
- 15h SeekL E - INT3(stat), INT2(stat) ;\use prior Setloc
- 16h SeekP E - INT3(stat), INT2(stat) ;/to set target
- 17h - - INT5(11h,40h) ;reportedly "SetClock" uh?
- 18h - - INT5(11h,40h) ;reportedly "GetClock" uh?
- 19h Test sub_function depends on sub_function (see below)
- 1Ah GetID E - INT3(stat), INT2/5(stat,flg,typ,atip,"SCEx")
- 1Bh ReadS E?- INT3(stat), INT1(stat), datablock
- 1Ch Reset - INT3(stat), Delay ;-not DTL-H2000
- 1Dh GetQ E adr,point INT3(stat), INT2(10bytesSubQ,peak_lo) ;\not
- 1Eh ReadTOC - INT3(late-stat), INT2(stat) ;/vC0
- 1Fh VideoCD sub,a,b,c,d,e INT3(stat,a,b,c,d,e) ;<-- SCPH-5903 only
- 1Fh..4Fh - - INT5(11h,40h) ;-Unused/invalid
- 50h Secret 1 - INT5(11h,40h) ;\
- 51h Secret 2 "Licensed by" INT5(11h,40h) ;
- 52h Secret 3 "Sony" INT5(11h,40h) ; Secret Unlock Commands
- 53h Secret 4 "Computer" INT5(11h,40h) ; (not in version vC0, and,
- 54h Secret 5 "Entertainment" INT5(11h,40h) ; nonfunctional in japan)
- 55h Secret 6 "<region>" INT5(11h,40h) ;
- 56h Secret 7 - INT5(11h,40h) ;/
- 57h SecretLock - INT5(11h,40h) ;-Secret Lock Command
- 58h..5Fh Crash - Crashes the HC05 (jumps into a data area)
- 6Fh..FFh - - INT5(11h,40h) ;-Unused/invalid
-*/
-
-void cdrom_cmd_unimplemented(psx_cdrom_t*);
-void cdrom_cmd_getstat(psx_cdrom_t*);
-void cdrom_cmd_setloc(psx_cdrom_t*);
-void cdrom_cmd_play(psx_cdrom_t*);
-void cdrom_cmd_readn(psx_cdrom_t*);
-void cdrom_cmd_motoron(psx_cdrom_t*);
-void cdrom_cmd_stop(psx_cdrom_t*);
-void cdrom_cmd_pause(psx_cdrom_t*);
-void cdrom_cmd_init(psx_cdrom_t*);
-void cdrom_cmd_mute(psx_cdrom_t*);
-void cdrom_cmd_unmute(psx_cdrom_t*);
-void cdrom_cmd_setfilter(psx_cdrom_t*);
-void cdrom_cmd_setmode(psx_cdrom_t*);
-void cdrom_cmd_getparam(psx_cdrom_t*);
-void cdrom_cmd_getlocl(psx_cdrom_t*);
-void cdrom_cmd_getlocp(psx_cdrom_t*);
-void cdrom_cmd_setsession(psx_cdrom_t*);
-void cdrom_cmd_gettn(psx_cdrom_t*);
-void cdrom_cmd_gettd(psx_cdrom_t*);
-void cdrom_cmd_seekl(psx_cdrom_t*);
-void cdrom_cmd_seekp(psx_cdrom_t*);
-void cdrom_cmd_test(psx_cdrom_t*);
-void cdrom_cmd_getid(psx_cdrom_t*);
-void cdrom_cmd_reads(psx_cdrom_t*);
-void cdrom_cmd_readtoc(psx_cdrom_t*);
-void cdrom_cmd_videocd(psx_cdrom_t*);
-
-#endif
\ No newline at end of file
--- /dev/null
+++ b/psx/dev/cdrom/audio.c
@@ -1,0 +1,420 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "cdrom.h"
+
+#define ITOB(b) itob_table[b]
+
+static const uint8_t itob_table[] = {+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31,
+ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x60, 0x61, 0x62, 0x63,
+ 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
+ 0x96, 0x97, 0x98, 0x99, 0xa0, 0xa1, 0xa2, 0xa3,
+ 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xb0, 0xb1,
+ 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5,
+ 0xd6, 0xd7, 0xd8, 0xd9, 0xe0, 0xe1, 0xe2, 0xe3,
+ 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xf0, 0xf1,
+ 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31,
+ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x60, 0x61, 0x62, 0x63,
+ 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
+};
+
+static const int pos_adpcm_table[] = {+ 0, +60, +115, +98, +122
+};
+
+static const int neg_adpcm_table[] = {+ 0, 0, -52, -55, -60
+};
+
+void cdrom_resample_xa_buf(psx_cdrom_t* cdrom, int16_t* dst, int16_t* src, int stereo, int16_t ls) {+ int f18khz = ((cdrom->xa_buf[0x13] >> 2) & 1) == 1;
+ int sample_count = stereo ? XA_STEREO_SAMPLES : XA_MONO_SAMPLES;
+ int resample_count = stereo ? XA_STEREO_RESAMPLE_SIZE : XA_MONO_RESAMPLE_SIZE;
+
+ resample_count *= f18khz + 1;
+
+ // Nearest neighbor
+ // for (int i = 0; i < sample_count; i++)
+ // for (int k = 0; k < 7; k++)
+ // cdrom->xa_upsample_buf[(i*7)+k] = src[i];
+
+ // Linear Upsampling
+ int16_t a = ls;
+ int16_t b = src[0];
+
+ for (int k = 0; k < 7; k++)
+ cdrom->xa_upsample_buf[k] = a + ((k+1)/8) * (b - a);
+
+ for (int i = 1; i < sample_count; i++) {+ a = b;
+ b = src[i];
+
+ for (int k = 0; k < 7; k++)
+ cdrom->xa_upsample_buf[(i*7)+k] =
+ a + ((k+1)/8) * (b - a);
+ }
+
+ int m = f18khz ? 3 : 6;
+
+ for (int i = 0; i < resample_count; i++)
+ dst[i] = cdrom->xa_upsample_buf[i*m];
+
+ cdrom->xa_remaining_samples = resample_count;
+}
+
+void cdrom_decode_xa_block(psx_cdrom_t* cdrom, int idx, int blk, int nib, int16_t* buf, int16_t* h) {+ int shift = 12 - (cdrom->xa_buf[idx + 4 + blk * 2 + nib] & 0x0F);
+ int filter = (cdrom->xa_buf[idx + 4 + blk * 2 + nib] & 0x30) >> 4;
+
+ int32_t f0 = pos_adpcm_table[filter];
+ int32_t f1 = neg_adpcm_table[filter];
+
+ for (int j = 0; j < 28; j++) {+ uint16_t n = (cdrom->xa_buf[idx + 16 + blk + j * 4] >> (nib * 4)) & 0x0f;
+
+ int16_t t = (int16_t)(n << 12) >> 12;
+ int16_t s = (t << shift) + (((h[0] * f0) + (h[1] * f1) + 32) / 64);
+
+ s = (s < INT16_MIN) ? INT16_MIN : ((s > INT16_MAX) ? INT16_MAX : s);
+
+ h[1] = h[0];
+ h[0] = s;
+
+ buf[j] = s;
+ }
+}
+
+void cdrom_decode_xa_sector(psx_cdrom_t* cdrom, void* buf) {+ int src = 24;
+
+ int16_t left[28];
+ int16_t right[28];
+
+ int16_t* left_ptr = cdrom->xa_left_buf;
+ int16_t* right_ptr = cdrom->xa_right_buf;
+ int16_t* mono_ptr = cdrom->xa_mono_buf;
+
+ for (int i = 0; i < 18; i++) {+ for (int blk = 0; blk < 4; blk++) {+ if (cdrom->xa_buf[0x13] & 1) {+ cdrom_decode_xa_block(cdrom, src, blk, 0, left, cdrom->xa_left_h);
+ cdrom_decode_xa_block(cdrom, src, blk, 1, right, cdrom->xa_right_h);
+
+ for (int i = 0; i < 28; i++) {+ *left_ptr++ = left[i];
+ *right_ptr++ = right[i];
+ }
+ } else {+ cdrom_decode_xa_block(cdrom, src, blk, 0, left, cdrom->xa_left_h);
+
+ for (int i = 0; i < 28; i++)
+ *mono_ptr++ = left[i];
+
+ cdrom_decode_xa_block(cdrom, src, blk, 1, left, cdrom->xa_left_h);
+
+ for (int i = 0; i < 28; i++)
+ *mono_ptr++ = left[i];
+ }
+ }
+
+ src += 128;
+ }
+}
+
+int cdrom_fetch_xa_sector(psx_cdrom_t* cdrom) {+ while (1) {+ int ts = psx_disc_read(cdrom->disc, cdrom->xa_lba, cdrom->xa_buf);
+
+ if (ts == TS_FAR)
+ return 0;
+
+ if (cdrom->xa_buf[0x12] & 1)
+ return 0;
+
+ ++cdrom->xa_lba;
+
+ // Check Audio bit
+ if (!(cdrom->xa_buf[0x12] & 4))
+ continue;
+
+ // If we get here it means this is a real-time audio sector.
+ // If the XA filter is disabled, we're done
+ if (!(cdrom->mode & MODE_XA_FILTER))
+ return 1;
+
+ // printf("fetch_xa_sector: lba=%u %02x:%02x:%02x mode=%02x file=%02x channel=%02x sm=%02x ci=%02x xafile=%02x xachannel=%02x\n",+ // cdrom->xa_lba,
+ // cdrom->xa_buf[0x0c],
+ // cdrom->xa_buf[0x0d],
+ // cdrom->xa_buf[0x0e],
+ // cdrom->xa_buf[0x0f],
+ // cdrom->xa_buf[0x10],
+ // cdrom->xa_buf[0x11],
+ // cdrom->xa_buf[0x12],
+ // cdrom->xa_buf[0x13],
+ // cdrom->xa_file,
+ // cdrom->xa_channel
+ // );
+
+ // Else check XA file/channel
+ int file_eq = cdrom->xa_buf[0x10] == cdrom->xa_file;
+ int channel_eq = cdrom->xa_buf[0x11] == cdrom->xa_channel;
+
+ // If they are equal to our filter values, we're done
+ // else keep searching
+ if (file_eq && channel_eq)
+ return 1;
+ }
+}
+
+int cdrom_get_xa_samples(psx_cdrom_t* cdrom, void* buf, size_t size) {+ if ((!cdrom->xa_playing) || !(cdrom->mode & MODE_XA_ADPCM)) {+ cdrom->xa_remaining_samples = 0;
+ cdrom->xa_sample_index = 0;
+
+ memset(buf, 0, size);
+
+ return 0;
+ }
+
+ float ll_vol = (((float)cdrom->vol[0]) / 255.0f);
+ float lr_vol = (((float)cdrom->vol[1]) / 255.0f);
+ float rr_vol = (((float)cdrom->vol[2]) / 255.0f);
+ float rl_vol = (((float)cdrom->vol[3]) / 255.0f);
+
+ int16_t* ptr = (int16_t*)buf;
+
+ for (int i = 0; i < (size >> 2); i++) {+ int stereo = (cdrom->xa_buf[0x13] & 1) == 1;
+
+ if (!cdrom->xa_remaining_samples) {+ if (!cdrom_fetch_xa_sector(cdrom)) {+ cdrom->xa_playing = 0;
+ cdrom->xa_remaining_samples = 0;
+
+ return;
+ }
+
+ stereo = (cdrom->xa_buf[0x13] & 1) == 1;
+
+ cdrom_decode_xa_sector(cdrom, buf);
+
+ if (stereo) {+ cdrom_resample_xa_buf(
+ cdrom,
+ cdrom->xa_left_resample_buf,
+ cdrom->xa_left_buf,
+ stereo,
+ cdrom->xa_prev_left_sample
+ );
+
+ cdrom_resample_xa_buf(
+ cdrom,
+ cdrom->xa_right_resample_buf,
+ cdrom->xa_right_buf,
+ stereo,
+ cdrom->xa_prev_right_sample
+ );
+ } else {+ cdrom_resample_xa_buf(
+ cdrom,
+ cdrom->xa_mono_resample_buf,
+ cdrom->xa_mono_buf,
+ stereo,
+ cdrom->xa_prev_left_sample
+ );
+ }
+
+ cdrom->xa_sample_index = 0;
+ }
+
+ if (cdrom->xa_mute || cdrom->mute) {+ *ptr++ = 0;
+ *ptr++ = 0;
+
+ return;
+ }
+
+ if (stereo) {+ cdrom->xa_prev_left_sample = cdrom->xa_left_resample_buf[cdrom->xa_sample_index];
+ cdrom->xa_prev_right_sample = cdrom->xa_right_resample_buf[cdrom->xa_sample_index++];
+
+ *ptr++ = (cdrom->xa_prev_left_sample * ll_vol) + (cdrom->xa_prev_right_sample * rl_vol);
+ *ptr++ = (cdrom->xa_prev_left_sample * lr_vol) + (cdrom->xa_prev_right_sample * rr_vol);
+
+ } else {+ cdrom->xa_prev_left_sample = cdrom->xa_mono_resample_buf[cdrom->xa_sample_index++];
+
+ *ptr++ = cdrom->xa_prev_left_sample * ll_vol;
+ *ptr++ = cdrom->xa_prev_left_sample * rr_vol;
+ }
+
+ --cdrom->xa_remaining_samples;
+ }
+
+ return 1;
+}
+
+void cdrom_reload_cdda_buffer(psx_cdrom_t* cdrom, void* buf, size_t size) {+ int ts = psx_disc_read(cdrom->disc, cdrom->lba, cdrom->cdda_buf);
+
+ // We hit the end of the disc
+ if (ts == TS_FAR) {+ cdrom->cdda_remaining_samples = 0;
+ cdrom->cdda_sample_index = 0;
+
+ memset(buf, 0, size);
+
+ cdrom->state = CD_STATE_IDLE;
+
+ return;
+ }
+
+ // We hit pregap (end of previous track)
+ if ((ts == TS_PREGAP) && (cdrom->mode & MODE_AUTOPAUSE)) {+ cdrom->cdda_remaining_samples = 0;
+ cdrom->cdda_sample_index = 0;
+
+ memset(buf, 0, size);
+
+ cdrom->state = CD_STATE_IDLE;
+
+ return;
+ }
+
+ cdrom->cdda_remaining_samples = CD_SECTOR_SIZE >> 1;
+ cdrom->cdda_sample_index = 0;
+
+ cdrom->pending_lba = ++cdrom->lba;
+}
+
+void cdrom_send_report_irq(psx_cdrom_t* cdrom) {+ if (!(cdrom->mode & MODE_REPORT))
+ return;
+
+ cdrom_set_int(cdrom, 1);
+
+ int track = psx_disc_get_track_number(cdrom->disc, cdrom->lba);
+ int track_lba = psx_disc_get_track_lba(cdrom->disc, track);
+
+ int32_t diff = cdrom->lba - track_lba;
+
+ if (diff < 0)
+ diff = -diff;
+
+ int mm = diff / (60 * 75);
+ int ss = (diff % (60 * 75)) / 75;
+ int ff = (diff % (60 * 75)) % 75;
+
+ printf("report: track %u %02u:%02u:%02u\n",+ track,
+ mm, ss, ff
+ );
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+ queue_push(cdrom->response, ITOB(track));
+ queue_push(cdrom->response, 1);
+ queue_push(cdrom->response, ITOB(mm));
+ queue_push(cdrom->response, ITOB(ss) | 0x80);
+ queue_push(cdrom->response, ITOB(ff));
+ queue_push(cdrom->response, 0);
+ queue_push(cdrom->response, 0);
+
+ psx_ic_irq(cdrom->ic, IC_CDROM);
+}
+
+void psx_cdrom_get_audio_samples(psx_cdrom_t* cdrom, void* buf, size_t size, psx_spu_t* spu) {+ memset(buf, 0, size);
+
+ if (!cdrom->disc)
+ return;
+
+ if (cdrom_get_xa_samples(cdrom, buf, size))
+ return;
+
+ if (!cdrom->cdda_playing) {+ cdrom->cdda_remaining_samples = 0;
+ cdrom->cdda_sample_index = 0;
+
+ return;
+ }
+
+ int16_t* ptr = buf;
+
+ float ll_vol = (((float)cdrom->vol[0]) / 255.0f);
+ float lr_vol = (((float)cdrom->vol[1]) / 255.0f);
+ float rr_vol = (((float)cdrom->vol[2]) / 255.0f);
+ float rl_vol = (((float)cdrom->vol[3]) / 255.0f);
+
+ for (int i = 0; i < size >> 1;) {+ if (!cdrom->cdda_remaining_samples) {+ cdrom_reload_cdda_buffer(cdrom, buf, size);
+
+ ++cdrom->cdda_sectors_played;
+
+ if (cdrom->cdda_sectors_played == 75) {+ cdrom_send_report_irq(cdrom);
+
+ cdrom->cdda_sectors_played = 0;
+ }
+ }
+
+ if (cdrom->mute) {+ ptr[i++] = 0;
+ ptr[i++] = 0;
+
+ cdrom->cdda_remaining_samples -= 2;
+ cdrom->cdda_sample_index += 2;
+
+ continue;
+ }
+
+ // if (cdrom->cdda_sample_index >= (2352 >> 1)) {+ // printf("ERROR %08x %u rem=%08x %d\n",+ // cdrom->cdda_sample_index,
+ // cdrom->cdda_sample_index,
+ // cdrom->cdda_remaining_samples,
+ // cdrom->cdda_remaining_samples
+ // );
+
+ // exit(1);
+ // }
+
+ int16_t left = cdrom->cdda_buf[cdrom->cdda_sample_index++];
+ int16_t right = cdrom->cdda_buf[cdrom->cdda_sample_index++];
+
+ // Apply volume settings to CDDA
+ ptr[i++] = left * ll_vol + right * rl_vol;
+ ptr[i++] = right * rr_vol + left * lr_vol;
+
+ cdrom->cdda_remaining_samples -= 2;
+
+ // if (cdrom->cdda_remaining_samples == 0) {+ // printf("ERROR rem=%08x %d", cdrom->cdda_remaining_samples, cdrom->cdda_remaining_samples);+
+ // exit(1);
+ // }
+ }
+}
\ No newline at end of file
--- /dev/null
+++ b/psx/dev/cdrom/cdrom.c
@@ -1,0 +1,703 @@
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "cdrom.h"
+
+typedef void (*cdrom_cmd_func)(psx_cdrom_t* cdrom);
+
+void cdrom_cmd_getstat(psx_cdrom_t* cdrom);
+void cdrom_cmd_setloc(psx_cdrom_t* cdrom);
+void cdrom_cmd_play(psx_cdrom_t* cdrom);
+void cdrom_cmd_forward(psx_cdrom_t* cdrom);
+void cdrom_cmd_backward(psx_cdrom_t* cdrom);
+void cdrom_cmd_readn(psx_cdrom_t* cdrom);
+void cdrom_cmd_motoron(psx_cdrom_t* cdrom);
+void cdrom_cmd_stop(psx_cdrom_t* cdrom);
+void cdrom_cmd_pause(psx_cdrom_t* cdrom);
+void cdrom_cmd_init(psx_cdrom_t* cdrom);
+void cdrom_cmd_mute(psx_cdrom_t* cdrom);
+void cdrom_cmd_demute(psx_cdrom_t* cdrom);
+void cdrom_cmd_setfilter(psx_cdrom_t* cdrom);
+void cdrom_cmd_setmode(psx_cdrom_t* cdrom);
+void cdrom_cmd_getparam(psx_cdrom_t* cdrom);
+void cdrom_cmd_getlocl(psx_cdrom_t* cdrom);
+void cdrom_cmd_getlocp(psx_cdrom_t* cdrom);
+void cdrom_cmd_setsession(psx_cdrom_t* cdrom);
+void cdrom_cmd_gettn(psx_cdrom_t* cdrom);
+void cdrom_cmd_gettd(psx_cdrom_t* cdrom);
+void cdrom_cmd_seekl(psx_cdrom_t* cdrom);
+void cdrom_cmd_seekp(psx_cdrom_t* cdrom);
+void cdrom_cmd_test(psx_cdrom_t* cdrom);
+void cdrom_cmd_getid(psx_cdrom_t* cdrom);
+void cdrom_cmd_reads(psx_cdrom_t* cdrom);
+void cdrom_cmd_reset(psx_cdrom_t* cdrom);
+void cdrom_cmd_getq(psx_cdrom_t* cdrom);
+void cdrom_cmd_readtoc(psx_cdrom_t* cdrom);
+void cdrom_cmd_videocd(psx_cdrom_t* cdrom);
+
+cdrom_cmd_func cdrom_cmd_table[] = {+ (cdrom_cmd_func)0,
+ cdrom_cmd_getstat,
+ cdrom_cmd_setloc,
+ cdrom_cmd_play,
+ cdrom_cmd_forward,
+ cdrom_cmd_backward,
+ cdrom_cmd_readn,
+ cdrom_cmd_motoron,
+ cdrom_cmd_stop,
+ cdrom_cmd_pause,
+ cdrom_cmd_init,
+ cdrom_cmd_mute,
+ cdrom_cmd_demute,
+ cdrom_cmd_setfilter,
+ cdrom_cmd_setmode,
+ cdrom_cmd_getparam,
+ cdrom_cmd_getlocl,
+ cdrom_cmd_getlocp,
+ cdrom_cmd_setsession,
+ cdrom_cmd_gettn,
+ cdrom_cmd_gettd,
+ cdrom_cmd_seekl,
+ cdrom_cmd_seekp,
+ (cdrom_cmd_func)0,
+ (cdrom_cmd_func)0,
+ cdrom_cmd_test,
+ cdrom_cmd_getid,
+ cdrom_cmd_reads,
+ cdrom_cmd_reset,
+ cdrom_cmd_getq,
+ cdrom_cmd_readtoc,
+ cdrom_cmd_videocd
+};
+
+static const char* cdrom_cmd_names[] = {+ "<unimplemented>",
+ "CdlGetstat",
+ "CdlSetloc",
+ "CdlPlay",
+ "CdlForward",
+ "CdlBackward",
+ "CdlReadn",
+ "CdlMotoron",
+ "CdlStop",
+ "CdlPause",
+ "CdlInit",
+ "CdlMute",
+ "CdlDemute",
+ "CdlSetfilter",
+ "CdlSetmode",
+ "CdlGetparam",
+ "CdlGetlocl",
+ "CdlGetlocp",
+ "CdlSetsession",
+ "CdlGettn",
+ "CdlGettd",
+ "CdlSeekl",
+ "CdlSeekp",
+ "<unimplemented>",
+ "<unimplemented>",
+ "CdlTest",
+ "CdlGetid",
+ "CdlReads",
+ "CdlReset",
+ "CdlGetq",
+ "CdlReadtoc",
+ "CdlVideocd"
+};
+
+void cdrom_write_stat(psx_cdrom_t* cdrom, uint8_t data);
+void cdrom_write_cmd(psx_cdrom_t* cdrom, uint8_t data);
+void cdrom_write_null(psx_cdrom_t* cdrom, uint8_t data);
+void cdrom_write_parm(psx_cdrom_t* cdrom, uint8_t data);
+void cdrom_write_ier(psx_cdrom_t* cdrom, uint8_t data);
+void cdrom_write_ifr(psx_cdrom_t* cdrom, uint8_t data);
+void cdrom_write_req(psx_cdrom_t* cdrom, uint8_t data);
+void cdrom_write_vol0(psx_cdrom_t* cdrom, uint8_t data);
+void cdrom_write_vol1(psx_cdrom_t* cdrom, uint8_t data);
+void cdrom_write_vol2(psx_cdrom_t* cdrom, uint8_t data);
+void cdrom_write_vol3(psx_cdrom_t* cdrom, uint8_t data);
+void cdrom_write_vapp(psx_cdrom_t* cdrom, uint8_t data);
+
+psx_cdrom_t* psx_cdrom_create(void) {+ return malloc(sizeof(psx_cdrom_t));
+}
+
+void psx_cdrom_init(psx_cdrom_t* cdrom, psx_ic_t* ic) {+ memset(cdrom, 0, sizeof(psx_cdrom_t));
+
+ cdrom->io_base = PSX_CDROM_BEGIN;
+ cdrom->io_size = PSX_CDROM_SIZE;
+
+ cdrom->data = queue_create();
+ cdrom->response = queue_create();
+ cdrom->parameters = queue_create();
+ cdrom->ic = ic;
+
+ queue_init(cdrom->data, CD_SECTOR_SIZE);
+ queue_init(cdrom->response, 32);
+ queue_init(cdrom->parameters, 32);
+
+ cdrom->version = CDR_VERSION_C0A;
+ cdrom->region = CDR_REGION_AMERICA;
+
+ cdrom->vol[0] = 0x80;
+ cdrom->vol[1] = 0x00;
+ cdrom->vol[2] = 0x80;
+ cdrom->vol[3] = 0x00;
+
+ cdrom->pending_lba = 150;
+ cdrom->lba = 150;
+ cdrom->seek_precision = 1;
+ cdrom->fake_getlocl_data = 1;
+}
+
+void psx_cdrom_open(psx_cdrom_t* cdrom, const char* path) {+ if (!path)
+ return;
+
+ cdrom->disc = psx_disc_create();
+ cdrom->disc_type = psx_disc_open(cdrom->disc, path);
+
+ if (cdrom->disc_type == CDT_ERROR)
+ psx_cdrom_close(cdrom);
+}
+
+void psx_cdrom_close(psx_cdrom_t* cdrom) {+ if (cdrom->disc) {+ psx_disc_destroy(cdrom->disc);
+
+ cdrom->disc = NULL;
+ }
+}
+
+void cdrom_process_setloc(psx_cdrom_t* cdrom) {+ if (!cdrom->pending_lba)
+ return;
+
+ cdrom->lba = cdrom->pending_lba;
+
+ cdrom->pending_lba = 0;
+}
+
+void cdrom_set_int(psx_cdrom_t* cdrom, int n) {+ cdrom->ifr = n;
+}
+
+int cdrom_get_read_delay(psx_cdrom_t* cdrom) {+ return (cdrom->mode & MODE_SPEED) ? CD_DELAY_READ_DS : CD_DELAY_READ_SS;
+}
+
+int cdrom_get_pause_delay(psx_cdrom_t* cdrom) {+ return (cdrom->mode & MODE_SPEED) ?
+ (CD_DELAY_1MS * (70/2)) : (CD_DELAY_1MS * 70);
+}
+
+int cdrom_get_seek_delay(psx_cdrom_t* cdrom, int ts) {+ int delay = CD_DELAY_FR;
+
+ return delay;
+
+ delay += cdrom->pending_speed_switch_delay;
+ cdrom->pending_speed_switch_delay = 0;
+
+ // Ridiculous delays for seeking to an audio sector
+ // or out of the disk
+ if (ts == TS_FAR) delay = 650 * CD_DELAY_1MS;
+ if (ts == TS_AUDIO) delay = 4000 * CD_DELAY_1MS;
+
+ return delay;
+}
+
+void cdrom_error(psx_cdrom_t* cdrom, uint8_t stat, uint8_t err) {+ cdrom->ifr = 5;
+
+ queue_reset(cdrom->parameters);
+ queue_reset(cdrom->response);
+
+ if ((stat & CD_STAT_IDERROR) || (stat & CD_STAT_SEEKERROR)) {+ queue_push(cdrom->response, stat);
+ } else {+ queue_push(cdrom->response, CD_STAT_ERROR | stat);
+ }
+
+ queue_push(cdrom->response, err);
+
+ cdrom->prev_state = CD_STATE_IDLE;
+ cdrom->state = CD_STATE_IDLE;
+ cdrom->pending_command = 0;
+ cdrom->busy = 0;
+}
+
+void cdrom_handle_resp1(psx_cdrom_t* cdrom) {+ cdrom->busy = 0;
+
+ // Check for no disc, some commands can be issued with no disc
+ // in the drive (e.g. Test, Setmode, Init, etc.)
+ // i.e. INT5(11h, 80h)
+ if (!cdrom->disc) {+ switch (cdrom->pending_command) {+ case CDL_SETLOC:
+ case CDL_PLAY:
+ case CDL_FORWARD:
+ case CDL_BACKWARD:
+ case CDL_READN:
+ case CDL_MOTORON:
+ case CDL_STOP:
+ case CDL_PAUSE:
+ case CDL_MUTE:
+ case CDL_DEMUTE:
+ case CDL_SETFILTER:
+ case CDL_GETLOCL:
+ case CDL_GETLOCP:
+ case CDL_SETSESSION:
+ case CDL_GETTN:
+ case CDL_GETTD:
+ case CDL_SEEKL:
+ case CDL_SEEKP:
+ case CDL_GETID:
+ case CDL_READS:
+ case CDL_GETQ: {+ cdrom_error(cdrom, CD_STAT_SHELLOPEN, CD_ERR_NO_DISC);
+
+ return;
+ } break;
+ }
+ }
+
+ // Check for version-specific unsupported commands
+ switch (cdrom->pending_command) {+ case CDL_RESET: {+ if (cdrom->version == CDR_VERSION_01) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE,
+ CD_ERR_INVALID_COMMAND
+ );
+
+ return;
+ }
+ } break;
+
+ case CDL_GETQ:
+ case CDL_READTOC: {+ if (cdrom->version < CDR_VERSION_C1A) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE,
+ CD_ERR_INVALID_COMMAND
+ );
+
+ return;
+ }
+ } break;
+ }
+
+ // Check for wrong number of parameters and invalid commands
+ // i.e. INT5(03h, 20h), INT5(03h, 40h)
+ switch (cdrom->pending_command) {+ case CDL_GETSTAT:
+ case CDL_FORWARD:
+ case CDL_BACKWARD:
+ case CDL_READN:
+ case CDL_MOTORON:
+ case CDL_STOP:
+ case CDL_PAUSE:
+ case CDL_INIT:
+ case CDL_MUTE:
+ case CDL_DEMUTE:
+ case CDL_GETPARAM:
+ case CDL_GETLOCL:
+ case CDL_GETLOCP:
+ case CDL_GETTN:
+ case CDL_SEEKL:
+ case CDL_SEEKP:
+ case CDL_GETID:
+ case CDL_READS:
+ case CDL_RESET:
+ case CDL_READTOC: {+ // These commands take no parameters
+ if (queue_size(cdrom->parameters)) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE,
+ CD_ERR_WRONG_PARAMETER_COUNT
+ );
+
+ return;
+ }
+ } break;
+
+ case CDL_SETLOC: {+ // Setloc takes exactly 3 parameters
+ if (queue_size(cdrom->parameters) != 3) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE,
+ CD_ERR_WRONG_PARAMETER_COUNT
+ );
+
+ return;
+ }
+ } break;
+
+ case CDL_PLAY: {+ // Play may take either 0 or 1 parameter
+ if (queue_size(cdrom->parameters) > 1) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE,
+ CD_ERR_WRONG_PARAMETER_COUNT
+ );
+
+ return;
+ }
+ } break;
+
+ case CDL_SETFILTER:
+ case CDL_GETQ: {+ // Setfilter and GetQ both take exactly 2 parameters
+ if (queue_size(cdrom->parameters) != 2) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE,
+ CD_ERR_WRONG_PARAMETER_COUNT
+ );
+
+ return;
+ }
+ } break;
+
+ case CDL_SETMODE:
+ case CDL_GETTD:
+ case CDL_TEST: {+ // Setmode and GetTD both take exactly 1 parameter
+
+ // Test may actually take additional parameters depending
+ // on the subfunction, but we only emulate subfunction 20h
+ // for now, which takes no extra parameters
+ if (queue_size(cdrom->parameters) != 1) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE,
+ CD_ERR_WRONG_PARAMETER_COUNT
+ );
+
+ return;
+ }
+ } break;
+
+ case CDL_VIDEOCD: {+ // To-do: Check for model
+ // VideoCD is only supported on the SCPH-5903
+ // Should return invalid command normally
+
+ // VideoCD takes exactly 6 parameters
+ if (queue_size(cdrom->parameters) != 6) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE,
+ CD_ERR_WRONG_PARAMETER_COUNT
+ );
+
+ return;
+ }
+ } break;
+
+ default: {+ // Invalid command
+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE,
+ CD_ERR_INVALID_COMMAND
+ );
+
+ return;
+ } break;
+ }
+
+ // If everything is alright (i.e. disc present, valid command,
+ // correct number of parameters) then send "execute command"
+ cdrom_cmd_table[cdrom->pending_command](cdrom);
+}
+
+uint8_t cdrom_get_stat(psx_cdrom_t* cdrom) {+ return ((cdrom->cdda_playing) ? CD_STAT_PLAY : 0) |
+ ((cdrom->read_ongoing) ? CD_STAT_READ : 0) |
+ ((cdrom->mode & 0x10) ? CD_STAT_IDERROR : 0) |
+ ((!cdrom->disc) ? CD_STAT_SHELLOPEN : 0) |
+ CD_STAT_SPINDLE;
+}
+
+void cdrom_handle_read(psx_cdrom_t* cdrom) {+ cdrom_process_setloc(cdrom);
+
+ int ts = psx_disc_query(cdrom->disc, cdrom->lba);
+
+ if (ts == TS_FAR) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE | CD_STAT_SEEKERROR,
+ CD_ERR_INVALID_SUBFUNCTION
+ );
+
+ return;
+ }
+
+ if (ts == TS_AUDIO) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE | CD_STAT_SEEKERROR,
+ CD_ERR_SEEK_FAILED
+ );
+
+ return;
+ }
+
+ cdrom_set_int(cdrom, 1);
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ // Read sector into our data FIFO
+ psx_disc_read(cdrom->disc, cdrom->lba, cdrom->data->buf);
+
+ int size_bit = cdrom->mode & MODE_SECTOR_SIZE;
+
+ cdrom->data->read_index = size_bit ? 12 : 24;
+ cdrom->data->write_index = size_bit ? 0x924 : 0x800;
+ cdrom->data->write_index += cdrom->data->read_index;
+
+ // printf("size=%x off=%u lba=%d\n",+ // cdrom->data->write_index,
+ // cdrom->data->read_index,
+ // cdrom->lba
+ // );
+
+ cdrom->pending_lba = cdrom->lba + 1;
+ cdrom->delay = cdrom_get_read_delay(cdrom);
+}
+
+void psx_cdrom_update(psx_cdrom_t* cdrom, int cycles) {+ if (cdrom->delay > 0) {+ cdrom->delay -= cycles;
+
+ if (cdrom->delay > 0)
+ return;
+ }
+
+ cdrom->delay = 0;
+
+ if (cdrom->state == CD_STATE_IDLE)
+ return;
+
+ if (cdrom->state == CD_STATE_PLAY)
+ return;
+
+ psx_ic_irq(cdrom->ic, IC_CDROM);
+
+ if (cdrom->state == CD_STATE_TX_RESP1) {+ cdrom_handle_resp1(cdrom);
+
+ switch (cdrom->pending_command) {+ case CDL_READN:
+ case CDL_READS:
+ return;
+ }
+
+ // Switching to read mode after executing a command
+ // has a 500ms penalty
+ if (cdrom->state == CD_STATE_READ) {+ cdrom_process_setloc(cdrom);
+
+ int ts = psx_disc_query(cdrom->disc, cdrom->lba);
+
+ cdrom->state = CD_STATE_READ;
+ cdrom->prev_state = CD_STATE_READ;
+ cdrom->delay = CD_DELAY_ONGOING_READ;
+ }
+
+ return;
+ }
+
+ if (cdrom->state == CD_STATE_TX_RESP2) {+ cdrom_cmd_table[cdrom->pending_command](cdrom);
+
+ // Switching to read mode after executing a command
+ // has a 500ms penalty
+ if (cdrom->state == CD_STATE_READ) {+ cdrom_process_setloc(cdrom);
+
+ int ts = psx_disc_query(cdrom->disc, cdrom->lba);
+
+ cdrom->state = CD_STATE_READ;
+ cdrom->prev_state = CD_STATE_READ;
+ cdrom->delay = CD_DELAY_ONGOING_READ;
+ }
+
+ return;
+ }
+
+ if (cdrom->state == CD_STATE_READ) {+ cdrom_handle_read(cdrom);
+
+ return;
+ }
+}
+
+uint8_t cdrom_read_status(psx_cdrom_t* cdrom) {+ int data_empty = queue_is_empty(cdrom->data) || !cdrom->data_req;
+
+ return (cdrom->index << 0) |
+ (cdrom->xa_playing << 2) |
+ (queue_is_empty(cdrom->parameters) << 3) |
+ ((!queue_is_full(cdrom->parameters)) << 4) |
+ ((!queue_is_empty(cdrom->response)) << 5) |
+ ((!data_empty) << 6) |
+ (cdrom->busy << 7);
+}
+
+uint8_t cdrom_read_data(psx_cdrom_t* cdrom) {+ if (!cdrom->data_req)
+ return 0;
+
+ // printf("read=%x write=%x data=%02x |%c|\n",+ // cdrom->data->read_index,
+ // cdrom->data->write_index,
+ // queue_peek(cdrom->data),
+ // isprint(queue_peek(cdrom->data)) ? queue_peek(cdrom->data) : '.'
+ // );
+
+ return queue_pop(cdrom->data);
+}
+
+uint32_t psx_cdrom_read8(psx_cdrom_t* cdrom, uint32_t addr) {+ switch (addr) {+ case 0: return cdrom_read_status(cdrom);
+ case 1: return queue_pop(cdrom->response);
+ case 2: return cdrom_read_data(cdrom);
+ case 3: return (cdrom->index & 1) ? (0xe0 | cdrom->ifr) : cdrom->ier;
+ }
+}
+void psx_cdrom_write8(psx_cdrom_t* cdrom, uint32_t addr, uint32_t value) {+ switch ((cdrom->index << 2) | addr) {+ case 0: cdrom_write_stat(cdrom, value); break;
+ case 1: cdrom_write_cmd(cdrom, value); break;
+ case 2: cdrom_write_parm(cdrom, value); break;
+ case 3: cdrom_write_req(cdrom, value); break;
+ case 4: cdrom_write_stat(cdrom, value); break;
+ case 5: cdrom_write_null(cdrom, value); break;
+ case 6: cdrom_write_ier(cdrom, value); break;
+ case 7: cdrom_write_ifr(cdrom, value); break;
+ case 8: cdrom_write_stat(cdrom, value); break;
+ case 9: cdrom_write_null(cdrom, value); break;
+ case 10: cdrom_write_vol0(cdrom, value); break;
+ case 11: cdrom_write_vol1(cdrom, value); break;
+ case 12: cdrom_write_stat(cdrom, value); break;
+ case 13: cdrom_write_vol2(cdrom, value); break;
+ case 14: cdrom_write_vol3(cdrom, value); break;
+ case 15: cdrom_write_vapp(cdrom, value); break;
+ }
+}
+
+void psx_cdrom_destroy(psx_cdrom_t* cdrom) {+ psx_cdrom_close(cdrom);
+ free(cdrom);
+}
+
+void cdrom_write_stat(psx_cdrom_t* cdrom, uint8_t data) {+ cdrom->index = data & 3;
+}
+
+void cdrom_write_cmd(psx_cdrom_t* cdrom, uint8_t data) {+ cdrom->prev_state = cdrom->state;
+ cdrom->state = CD_STATE_TX_RESP1;
+
+ cdrom->pending_command = data;
+ cdrom->delay = CD_DELAY_FR;
+
+ // if (cdrom->pending_command == CDL_INIT)
+ // cdrom->delay = CD_DELAY_INIT_FR;
+
+ if (cdrom->pending_command == CDL_GETLOCP)
+ cdrom->delay *= 4;
+
+ if (cdrom->state == CD_STATE_READ)
+ cdrom->busy = 1;
+
+ printf("cdrom: %-16s (%02x) params: ", cdrom_cmd_names[data], data);+
+ if (queue_is_empty(cdrom->parameters)) {+ puts("(none)");+
+ return;
+ }
+
+ for (int i = 0; i < cdrom->parameters->write_index; i++)
+ printf("%02x ", cdrom->parameters->buf[i]);+
+ putchar('\n');+
+ return;
+}
+
+void cdrom_write_null(psx_cdrom_t* cdrom, uint8_t data) {+ /* Ignore writes */
+}
+
+void cdrom_write_parm(psx_cdrom_t* cdrom, uint8_t data) {+ queue_push(cdrom->parameters, data);
+}
+
+void cdrom_write_ier(psx_cdrom_t* cdrom, uint8_t data) {+ cdrom->ier = data;
+}
+
+void cdrom_write_ifr(psx_cdrom_t* cdrom, uint8_t data) {+ if (data & 0x40)
+ queue_clear(cdrom->parameters);
+
+ cdrom->ifr &= ~(data & 0x1f);
+
+ // If an INT is acknowledged, then the response
+ // FIFO is cleared
+ // if ((cdrom->ifr & 0x1f) == 0)
+ // queue_clear(cdrom->response);
+}
+
+void cdrom_write_req(psx_cdrom_t* cdrom, uint8_t data) {+ cdrom->data_req = data & 0x80;
+}
+
+void cdrom_write_vol0(psx_cdrom_t* cdrom, uint8_t data) {+ cdrom->vol_pending[0] = data;
+}
+
+void cdrom_write_vol1(psx_cdrom_t* cdrom, uint8_t data) {+ cdrom->vol_pending[1] = data;
+}
+
+void cdrom_write_vol2(psx_cdrom_t* cdrom, uint8_t data) {+ cdrom->vol_pending[2] = data;
+}
+
+void cdrom_write_vol3(psx_cdrom_t* cdrom, uint8_t data) {+ cdrom->vol_pending[3] = data;
+}
+
+void cdrom_write_vapp(psx_cdrom_t* cdrom, uint8_t data) {+ cdrom->xa_mute = data & 1;
+ cdrom->vol[0] = cdrom->vol_pending[0];
+ cdrom->vol[1] = cdrom->vol_pending[1];
+ cdrom->vol[2] = cdrom->vol_pending[2];
+ cdrom->vol[3] = cdrom->vol_pending[3];
+}
+
+uint32_t psx_cdrom_read32(psx_cdrom_t* cdrom, uint32_t addr) {+ assert(("32-bit CDROM reads are not supported", 0));+}
+
+uint32_t psx_cdrom_read16(psx_cdrom_t* cdrom, uint32_t addr) {+ assert(("16-bit CDROM reads are not supported", 0));+
+ // The CDROM controller is connected to the SUB-BUS which is a 16-bit
+ // bus, but the output from the controller itself is 8-bit. I think
+ // 16-bit accesses are handled as a pair of 8-bit accesses
+ return psx_cdrom_read8(cdrom, addr) << 8 | psx_cdrom_read8(cdrom, addr+1);
+}
+
+void psx_cdrom_write32(psx_cdrom_t* cdrom, uint32_t addr, uint32_t value) {+ assert(("32-bit CDROM writes are not supported", 0));+}
+
+void psx_cdrom_write16(psx_cdrom_t* cdrom, uint32_t addr, uint32_t value) {+ assert(("16-bit CDROM writes are not supported", 0));+}
\ No newline at end of file
--- /dev/null
+++ b/psx/dev/cdrom/cdrom.h
@@ -1,0 +1,313 @@
+#ifndef CDROM_H
+#define CDROM_H
+
+#include <stdint.h>
+
+#include "queue.h"
+#include "disc.h"
+#include "../ic.h"
+#include "../spu.h"
+
+#define PSX_CDROM_BEGIN 0x1f801800
+#define PSX_CDROM_END 0x1f801803
+#define PSX_CDROM_SIZE 0x4
+
+/*
+ Command Average Min Max
+ GetStat (normal) 000c4e1h 0004a73h..003115bh
+ GetStat (when stopped) 0005cf4h 000483bh..00093f2h
+ Pause (single speed) 021181ch 020eaefh..0216e3ch ;\time equal to
+ Pause (double speed) 010bd93h 010477Ah..011B302h ;/about 5 sectors
+ Pause (when paused) 0001df2h 0001d25h..0001f22h
+ Stop (single speed) 0d38acah 0c3bc41h..0da554dh
+ Stop (double speed) 18a6076h 184476bh..192b306h
+ Stop (when stopped) 0001d7bh 0001ce8h..0001eefh
+ GetID 0004a00h 0004922h..0004c2bh
+ Init 0013cceh 000f820h..00xxxxxh
+*/
+
+#define CD_DELAY_1MS 33869
+#define CD_DELAY_FR 50401
+#define CD_DELAY_INIT_FR 81102
+#define CD_DELAY_PAUSE_SS 2168860
+#define CD_DELAY_PAUSE_DS 1097107
+#define CD_DELAY_STOP_SS 13863626
+#define CD_DELAY_STOP_DS 25845878
+#define CD_DELAY_READ_SS (33868800 / 75)
+#define CD_DELAY_READ_DS (33868800 / (2*75))
+#define CD_DELAY_START_READ \
+ (cdrom_get_read_delay(cdrom) + \
+ cdrom_get_seek_delay(cdrom, ts))
+
+#define CD_DELAY_ONGOING_READ \
+ (cdrom_get_read_delay(cdrom) + \
+ cdrom_get_seek_delay(cdrom, ts) + \
+ (CD_DELAY_1MS * 4))
+
+#define XA_STEREO_SAMPLES 2016 // Samples per sector
+#define XA_MONO_SAMPLES 4032 // Samples per sector
+#define XA_STEREO_RESAMPLE_SIZE 2352 // 2352 * 2
+#define XA_MONO_RESAMPLE_SIZE 4704 // 4704 * 2
+#define XA_UPSAMPLE_SIZE 28224 // 4032 * 7
+#define XA_RINGBUF_SIZE 32
+
+/*
+ 7 Speed (0=Normal speed, 1=Double speed)
+ 6 XA-ADPCM (0=Off, 1=Send XA-ADPCM sectors to SPU Audio Input)
+ 5 Sector Size (0=800h=DataOnly, 1=924h=WholeSectorExceptSyncBytes)
+ 4 Ignore Bit (0=Normal, 1=Ignore Sector Size and Setloc position)
+ 3 XA-Filter (0=Off, 1=Process only XA-ADPCM sectors that match Setfilter)
+ 2 Report (0=Off, 1=Enable Report-Interrupts for Audio Play)
+ 1 AutoPause (0=Off, 1=Auto Pause upon End of Track) ;for Audio Play
+ 0 CDDA (0=Off, 1=Allow to Read CD-DA Sectors; ignore missing EDC)
+*/
+
+#define MODE_CDDA 0x01
+#define MODE_AUTOPAUSE 0x02
+#define MODE_REPORT 0x04
+#define MODE_XA_FILTER 0x08
+#define MODE_IGNORE 0x10
+#define MODE_SECTOR_SIZE 0x20
+#define MODE_XA_ADPCM 0x40
+#define MODE_SPEED 0x80
+
+/*
+ Command Parameters Response(s)
+ 00h - - INT5(11h,40h) ;reportedly "Sync" uh?
+ 01h Getstat - INT3(stat)
+ 02h Setloc E amm,ass,asect INT3(stat)
+ 03h Play E (track) INT3(stat), optional INT1(report bytes)
+ 04h Forward E - INT3(stat), optional INT1(report bytes)
+ 05h Backward E - INT3(stat), optional INT1(report bytes)
+ 06h ReadN E - INT3(stat), INT1(stat), datablock
+ 07h MotorOn E - INT3(stat), INT2(stat)
+ 08h Stop E - INT3(stat), INT2(stat)
+ 09h Pause E - INT3(stat), INT2(stat)
+ 0Ah Init - INT3(late-stat), INT2(stat)
+ 0Bh Mute E - INT3(stat)
+ 0Ch Demute E - INT3(stat)
+ 0Dh Setfilter E file,channel INT3(stat)
+ 0Eh Setmode mode INT3(stat)
+ 0Fh Getparam - INT3(stat,mode,null,file,channel)
+ 10h GetlocL E - INT3(amm,ass,asect,mode,file,channel,sm,ci)
+ 11h GetlocP E - INT3(track,index,mm,ss,sect,amm,ass,asect)
+ 12h SetSession E session INT3(stat), INT2(stat)
+ 13h GetTN E - INT3(stat,first,last) ;BCD
+ 14h GetTD E track (BCD) INT3(stat,mm,ss) ;BCD
+ 15h SeekL E - INT3(stat), INT2(stat) ;\use prior Setloc
+ 16h SeekP E - INT3(stat), INT2(stat) ;/to set target
+ 17h - - INT5(11h,40h) ;reportedly "SetClock" uh?
+ 18h - - INT5(11h,40h) ;reportedly "GetClock" uh?
+ 19h Test sub_function depends on sub_function (see below)
+ 1Ah GetID E - INT3(stat), INT2/5(stat,flg,typ,atip,"SCEx")
+ 1Bh ReadS E?- INT3(stat), INT1(stat), datablock
+ 1Ch Reset - INT3(stat), Delay
+ 1Dh GetQ E adr,point INT3(stat), INT2(10bytesSubQ,peak_lo) ;\not
+ 1Eh ReadTOC - INT3(late-stat), INT2(stat) ;/vC0
+ 1Fh VideoCD sub,a,b,c,d,e INT3(stat,a,b,c,d,e) ;<-- SCPH-5903 only
+ 1Fh..4Fh - - INT5(11h,40h) ;-Unused/invalid
+ 50h Secret 1 - INT5(11h,40h) ;\
+ 51h Secret 2 "Licensed by" INT5(11h,40h) ;
+ 52h Secret 3 "Sony" INT5(11h,40h) ; Secret Unlock Commands
+ 53h Secret 4 "Computer" INT5(11h,40h) ; (not in version vC0, and,
+ 54h Secret 5 "Entertainment" INT5(11h,40h) ; nonfunctional in japan)
+ 55h Secret 6 "<region>" INT5(11h,40h) ;
+ 56h Secret 7 - INT5(11h,40h) ;/
+ 57h SecretLock - INT5(11h,40h) ;-Secret Lock Command
+ 58h..5Fh Crash - Crashes the HC05 (jumps into a data area)
+ 6Fh..FFh - - INT5(11h,40h) ;-Unused/invalid
+*/
+
+#define CDL_GETSTAT 0x01
+#define CDL_SETLOC 0x02
+#define CDL_PLAY 0x03
+#define CDL_FORWARD 0x04
+#define CDL_BACKWARD 0x05
+#define CDL_READN 0x06
+#define CDL_MOTORON 0x07
+#define CDL_STOP 0x08
+#define CDL_PAUSE 0x09
+#define CDL_INIT 0x0a
+#define CDL_MUTE 0x0b
+#define CDL_DEMUTE 0x0c
+#define CDL_SETFILTER 0x0d
+#define CDL_SETMODE 0x0e
+#define CDL_GETPARAM 0x0f
+#define CDL_GETLOCL 0x10
+#define CDL_GETLOCP 0x11
+#define CDL_SETSESSION 0x12
+#define CDL_GETTN 0x13
+#define CDL_GETTD 0x14
+#define CDL_SEEKL 0x15
+#define CDL_SEEKP 0x16
+#define CDL_TEST 0x19
+#define CDL_GETID 0x1a
+#define CDL_READS 0x1b
+#define CDL_RESET 0x1c
+#define CDL_GETQ 0x1d
+#define CDL_READTOC 0x1e
+#define CDL_VIDEOCD 0x1f
+
+/*
+ ___These values appear in the FIRST response; with stat.bit0 set___
+ 10h - Invalid Sub_function (for command 19h), or invalid parameter value
+ 20h - Wrong number of parameters (most CD commands need an exact number of parameters)
+ 40h - Invalid command
+ 80h - Cannot respond yet (eg. required info was not yet read from disk yet)
+ (namely, TOC not-yet-read or so)
+ (also appears if no disk inserted at all)
+ ___These values appear in the SECOND response; with stat.bit2 set___
+ 04h - Seek failed (when trying to use SeekL on Audio CDs)
+ ___These values appear even if no command was sent; with stat.bit2 set___
+ 08h - Drive door became opened
+*/
+
+#define CD_ERR_SEEK_FAILED 0x04
+#define CD_ERR_DOOR_OPENED 0x08
+#define CD_ERR_INVALID_SUBFUNCTION 0x10
+#define CD_ERR_WRONG_PARAMETER_COUNT 0x20
+#define CD_ERR_INVALID_COMMAND 0x40
+#define CD_ERR_NO_DISC 0x80
+
+/*
+ 7 Play Playing CD-DA ;\only ONE of these bits can be set
+ 6 Seek Seeking ; at a time (ie. Read/Play won't get
+ 5 Read Reading data sectors ;/set until after Seek completion)
+ 4 ShellOpen Once shell open (0=Closed, 1=Is/was Open)
+ 3 IdError (0=Okay, 1=GetID denied) (also set when Setmode.Bit4=1)
+ 2 SeekError (0=Okay, 1=Seek error) (followed by Error Byte)
+ 1 Spindle Motor (0=Motor off, or in spin-up phase, 1=Motor on)
+ 0 Error Invalid Command/parameters (followed by Error Byte)
+*/
+
+#define CD_STAT_PLAY 0x80
+#define CD_STAT_SEEK 0x40
+#define CD_STAT_READ 0x20
+#define CD_STAT_SHELLOPEN 0x10
+#define CD_STAT_IDERROR 0x08
+#define CD_STAT_SEEKERROR 0x04
+#define CD_STAT_SPINDLE 0x02
+#define CD_STAT_ERROR 0x01
+
+// #define SET_INT(n) cdrom->ifr = n;
+
+enum {+ CD_STATE_IDLE,
+ CD_STATE_TX_RESP1,
+ CD_STATE_TX_RESP2,
+ CD_STATE_READ,
+ CD_STATE_PLAY
+};
+
+enum {+ QUERY_TRACK_COUNT,
+ QUERY_TRACK_ADDR,
+ QUERY_TRACK_TYPE
+};
+
+typedef struct {+ int mute;
+ uint32_t bus_delay;
+ uint32_t io_base, io_size;
+ psx_disc_t* disc;
+ psx_ic_t* ic;
+ int disc_type;
+ int version;
+ int region;
+ int index;
+ int pending_speed_switch_delay;
+ int seek_precision;
+ int fake_getlocl_data;
+ uint8_t ier;
+ uint8_t ifr;
+ uint8_t vol_pending[4];
+ uint8_t vol[4];
+ uint8_t mode;
+ int data_req;
+ queue_t* data;
+ queue_t* response;
+ queue_t* parameters;
+ uint8_t pending_command;
+ int busy;
+ uint32_t xa_lba;
+ int xa_playing;
+ int xa_mute;
+ int xa_channel;
+ int xa_file;
+ int xa_remaining_samples;
+ int16_t xa_left_h[2];
+ int16_t xa_right_h[2];
+ int16_t xa_prev_left_sample;
+ int16_t xa_prev_right_sample;
+ int xa_sample_index;
+ int state;
+ int prev_state;
+ int int1_pending;
+ int int2_pending;
+ int delay;
+ uint32_t pending_lba;
+ uint32_t lba;
+ int16_t cdda_buf[CD_SECTOR_SIZE >> 1];
+ int32_t cdda_remaining_samples;
+ uint32_t cdda_sample_index;
+ uint32_t cdda_sectors_played;
+ int cdda_playing;
+ int read_ongoing;
+ uint8_t xa_buf[CD_SECTOR_SIZE];
+ int16_t xa_left_buf[XA_STEREO_SAMPLES];
+ int16_t xa_right_buf[XA_STEREO_SAMPLES];
+ int16_t xa_mono_buf[XA_MONO_SAMPLES];
+ int16_t xa_upsample_buf[XA_UPSAMPLE_SIZE];
+ int16_t xa_left_resample_buf[XA_STEREO_RESAMPLE_SIZE];
+ int16_t xa_right_resample_buf[XA_STEREO_RESAMPLE_SIZE];
+ int16_t xa_mono_resample_buf[XA_MONO_RESAMPLE_SIZE];
+} psx_cdrom_t;
+
+enum {+ CDR_VERSION_01, // DTL-H2000 (??-???-????)
+ CDR_VERSION_C0A, // PSX (PU-7) (19-Sep-1994)
+ CDR_VERSION_C0B, // PSX (PU-7) (18-Nov-1994)
+ CDR_VERSION_C1A, // PSX (EARLY-PU-8) (16-May-1995)
+ CDR_VERSION_C1B, // PSX (LATE-PU-8) (24-Jul-1995)
+ CDR_VERSION_D1, // PSX (LATE-PU-8, Debug) (24-Jul-1995)
+ CDR_VERSION_C2V, // PSX (PU-16, Video CD) (15-Aug-1996)
+ CDR_VERSION_C1Y, // PSX (LATE-PU-8, Yaroze) (18-Aug-1996)
+ CDR_VERSION_C2J, // PSX (PU-18) (J) (12-Sep-1996)
+ CDR_VERSION_C2A, // PSX (PU-18) (U/E) (10-Jan-1997)
+ CDR_VERSION_C2B, // PSX (PU-20) (14-Aug-1997)
+ CDR_VERSION_C3A, // PSX (PU-22) (10-Jul-1998)
+ CDR_VERSION_C3B, // PSX/PSone (PU-23, PM-41) (01-Feb-1999)
+ CDR_VERSION_C3C // PSone/late (PM-41(2)) (06-Jun-2001)
+};
+
+enum {+ CDR_REGION_JAPAN,
+ CDR_REGION_EUROPE,
+ CDR_REGION_AMERICA
+};
+
+uint8_t cdrom_get_stat(psx_cdrom_t* cdrom);
+void cdrom_error(psx_cdrom_t* cdrom, uint8_t stat, uint8_t err);
+int cdrom_get_seek_delay(psx_cdrom_t* cdrom, int ts);
+int cdrom_get_pause_delay(psx_cdrom_t* cdrom);
+int cdrom_get_read_delay(psx_cdrom_t* cdrom);
+void cdrom_set_int(psx_cdrom_t* cdrom, int n);
+void cdrom_process_setloc(psx_cdrom_t* cdrom);
+
+psx_cdrom_t* psx_cdrom_create(void);
+void psx_cdrom_init(psx_cdrom_t* cdrom, psx_ic_t* ic);
+void psx_cdrom_set_version(psx_cdrom_t* cdrom, int version);
+void psx_cdrom_set_region(psx_cdrom_t* cdrom, int region);
+void psx_cdrom_open(psx_cdrom_t* cdrom, const char* path);
+void psx_cdrom_close(psx_cdrom_t* cdrom);
+uint32_t psx_cdrom_read32(psx_cdrom_t* cdrom, uint32_t addr);
+uint32_t psx_cdrom_read16(psx_cdrom_t* cdrom, uint32_t addr);
+uint32_t psx_cdrom_read8(psx_cdrom_t* cdrom, uint32_t addr);
+void psx_cdrom_write32(psx_cdrom_t* cdrom, uint32_t addr, uint32_t value);
+void psx_cdrom_write16(psx_cdrom_t* cdrom, uint32_t addr, uint32_t value);
+void psx_cdrom_write8(psx_cdrom_t* cdrom, uint32_t addr, uint32_t value);
+void psx_cdrom_update(psx_cdrom_t* cdrom, int cycles);
+void psx_cdrom_get_audio_samples(psx_cdrom_t* cdrom, void* buf, size_t size, psx_spu_t* spu);
+void psx_cdrom_destroy(psx_cdrom_t* cdrom);
+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/psx/dev/cdrom/cue.c
@@ -1,0 +1,605 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "cue.h"
+
+static const char* cue_keywords[] = {+ "4CH",
+ "AIFF",
+ "AUDIO",
+ "BINARY",
+ "CATALOG",
+ "CDG",
+ "CDI/2336",
+ "CDI/2352",
+ "CDTEXTFILE",
+ "DCP",
+ "FILE",
+ "FLAGS",
+ "INDEX",
+ "ISRC",
+ "MODE1/2048",
+ "MODE1/2352",
+ "MODE2/2336",
+ "MODE2/2352",
+ "MOTOROLA",
+ "MP3",
+ "PERFORMER",
+ "POSTGAP",
+ "PRE",
+ "PREGAP",
+ "REM",
+ "SCMS",
+ "SONGWRITER",
+ "TITLE",
+ "TRACK",
+ "WAVE",
+ 0
+};
+
+char* strapp(char* dst, const char* a, const char* b) {+ char* d = dst;
+
+ while (*a)
+ *dst++ = *a++;
+
+ while (*b)
+ *dst++ = *b++;
+
+ *dst = '\0';
+
+ return d;
+}
+
+const char* find_last_slash(const char* a) {+ if (!a)
+ return NULL;
+
+ const char* b = a;
+
+ while (*a) {+ if (*a == '/' || *a == '\\')
+ b = a + 1;
+
+ ++a;
+ }
+
+ return b;
+}
+
+char* get_root_path(char* dst, const char* a) {+ if (!a) {+ *dst = '\0';
+
+ return dst;
+ }
+
+ const char* b = a;
+ const char* c = a;
+ char* d = dst;
+
+ while (*a) {+ if (*a == '/' || *a == '\\')
+ b = a + 1;
+
+ ++a;
+ }
+
+ while (c != b)
+ *dst++ = *c++;
+
+ *dst = '\0';
+
+ return d;
+}
+
+int cue_parse_keyword(cue_t* cue) {+ char buf[256];
+ char* ptr = buf;
+
+ while (isalpha(cue->c) || isdigit(cue->c) || cue->c == '/') {+ *ptr++ = cue->c;
+
+ cue->c = fgetc(cue->file);
+ }
+
+ *ptr = '\0';
+
+ int i = 0;
+
+ const char* keyword = cue_keywords[i];
+
+ while (keyword) {+ if (!strcmp(keyword, buf)) {+ return i;
+ } else {+ keyword = cue_keywords[++i];
+ }
+ }
+
+ return -1;
+}
+
+int cue_parse_number(cue_t* cue) {+ if (!isdigit(cue->c))
+ return 0;
+
+ char buf[4];
+
+ char* ptr = buf;
+
+ while (isdigit(cue->c)) {+ *ptr++ = cue->c;
+
+ cue->c = fgetc(cue->file);
+ }
+
+ *ptr = '\0';
+
+ return atoi(buf);
+}
+
+uint32_t cue_parse_msf(cue_t* cue) {+ int m = 0;
+ int s = 0;
+ int f = 0;
+
+ if (!isdigit(cue->c))
+ return 0;
+
+ m = cue_parse_number(cue);
+
+ if (cue->c != ':')
+ return 0;
+
+ cue->c = fgetc(cue->file);
+
+ s = cue_parse_number(cue);
+
+ if (cue->c != ':')
+ return 0;
+
+ cue->c = fgetc(cue->file);
+
+ f = cue_parse_number(cue);
+
+ // 1 second = 75 frames (sectors)
+ // 1 minute = 60 seconds = 4500 frames
+ return f + (s * 75) + (m * 4500);
+}
+
+void cue_parse_index(cue_t* cue) {+ cue_track_t* track = list_back(cue->tracks)->data;
+
+ while (isspace(cue->c))
+ cue->c = fgetc(cue->file);
+
+ if (!isdigit(cue->c))
+ return;
+
+ int i = cue_parse_number(cue);
+
+ while (isspace(cue->c))
+ cue->c = fgetc(cue->file);
+
+ if (i > 1)
+ return;
+
+ track->index[i] = cue_parse_msf(cue);
+}
+
+cue_track_t* cue_parse_track(cue_t* cue) {+ while (isspace(cue->c))
+ cue->c = fgetc(cue->file);
+
+ if (!isdigit(cue->c))
+ return NULL;
+
+ cue_track_t* track = malloc(sizeof(cue_track_t));
+
+ track->end = 0;
+ track->start = 0;
+ track->pregap = 0;
+ track->index[0] = -1;
+ track->index[1] = -1;
+ track->file = list_back(cue->files)->data;
+ track->number = cue_parse_number(cue);
+
+ while (isspace(cue->c))
+ cue->c = fgetc(cue->file);
+
+ track->mode = cue_parse_keyword(cue);
+
+ return track;
+}
+
+cue_file_t* cue_parse_file(cue_t* cue, const char* p, const char* s) {+ while (isspace(cue->c))
+ cue->c = fgetc(cue->file);
+
+ if (cue->c != '\"')
+ return NULL;
+
+ cue_file_t* file = malloc(sizeof(cue_file_t));
+
+ file->tracks = list_create();
+ file->name = malloc(512);
+
+ // Append root path to track file path
+ char* ptr = file->name;
+
+ while (p != s)
+ *ptr++ = *p++;
+
+ cue->c = fgetc(cue->file);
+
+ while (cue->c != '\"') {+ *ptr++ = cue->c;
+
+ cue->c = fgetc(cue->file);
+ }
+
+ *ptr = '\0';
+
+ cue->c = fgetc(cue->file);
+
+ // Ignore file type
+ while (isspace(cue->c))
+ cue->c = fgetc(cue->file);
+
+ while (isalpha(cue->c))
+ cue->c = fgetc(cue->file);
+
+ return file;
+}
+
+cue_t* cue_create(void) {+ return malloc(sizeof(cue_t));
+}
+
+void cue_init(cue_t* cue) {+ cue->files = list_create();
+ cue->tracks = list_create();
+}
+
+int cue_parse(cue_t* cue, const char* path) {+ cue->file = fopen(path, "rb");
+
+ if (!cue->file)
+ return CUE_FILE_NOT_FOUND;
+
+ const char* s = find_last_slash(path);
+
+ cue->c = fgetc(cue->file);
+
+ while (isspace(cue->c))
+ cue->c = fgetc(cue->file);
+
+ while (!feof(cue->file)) {+ int kw = cue_parse_keyword(cue);
+
+ switch (kw) {+ case CUE_FILE: {+ list_push_back(cue->files, cue_parse_file(cue, path, s));
+ } break;
+
+ case CUE_TRACK: {+ cue_track_t* track = cue_parse_track(cue);
+ cue_file_t* file = list_back(cue->files)->data;
+
+ list_push_back(cue->tracks, track);
+ list_push_back(file->tracks, track);
+ } break;
+
+ case CUE_INDEX: {+ cue_parse_index(cue);
+ } break;
+
+ case CUE_REM: case CUE_PREGAP: case CUE_FLAGS: case CUE_POSTGAP: {+ // Ignore everything until a newline (handle CRLF and LF)
+ while ((cue->c != '\n') && (cue->c != '\r'))
+ cue->c = fgetc(cue->file);
+
+ while ((cue->c == '\n') && (cue->c == '\r'))
+ cue->c = fgetc(cue->file);
+ } break;
+
+ default: {+ printf("Unknown keyword: %s (%u)\n", cue_keywords[kw], kw);+
+ return 1;
+ } break;
+ }
+
+ while (isspace(cue->c))
+ cue->c = fgetc(cue->file);
+ }
+
+ return 0;
+}
+
+size_t get_file_size(FILE* file) {+ fseek(file, 0, SEEK_END);
+
+ size_t size = ftell(file);
+
+ fseek(file, 0, SEEK_SET);
+
+ return size;
+}
+
+/*
+(0 - 0 ) = 150
+(0 - 0 ) + 150 + 315000 = 315150
+(0 - 0 ) + 315150 + 375 = 315525
+(150 - 0 ) + 315525 + 390 = 316065
+(195 - 150) + 316065 + 390 = 316500
+(155 - 190) + 316500 + 390 =
+*/
+
+int prev_pregap = 0;
+
+uint32_t init_tracks(cue_file_t* file, uint32_t* lba) {+ node_t* node = list_front(file->tracks);
+
+ // 1 track per file case
+ if (file->tracks->size == 1) {+ cue_track_t* data = node->data;
+
+ data->pregap = 0;
+
+ if ((data->index[0] != -1) && (data->index[1] != -1))
+ data->pregap = data->index[1];
+
+ int pregap = data->pregap;
+
+ if (data->pregap)
+ pregap -= prev_pregap;
+
+ *lba += pregap;
+
+ data->start = *lba;
+ data->end = data->start + (file->size / 0x930);
+
+ *lba += (file->size / 0x930);
+
+ prev_pregap = data->pregap;
+
+ return file->size / 0x930;
+ }
+
+ // Multiple tracks per file
+ while (node) {+ cue_track_t* data = node->data;
+
+ // If this is the last track
+ if (!node->next) {+ data->pregap = 0;
+ data->start = data->index[1] + 150;
+ data->end = file->size / 0x930;
+
+ return 0;
+ }
+
+ cue_track_t* next = node->next->data;
+
+ data->start = data->index[1] + 150;
+ data->end = (next->index[1] + 150) - 1;
+ data->pregap = 0;
+
+ node = node->next;
+ }
+
+ return 0;
+}
+
+int cue_load(cue_t* cue, int mode) {+ node_t* node = list_front(cue->files);
+
+ // 00:02:00
+ uint32_t lba = 2 * 75;
+
+ while (node) {+ cue_file_t* data = node->data;
+
+ FILE* file = fopen(data->name, "rb");
+
+ if (!file)
+ return CUE_TRACK_FILE_NOT_FOUND;
+
+ data->buf_mode = mode;
+ data->size = get_file_size(file);
+
+ // printf("Loaded \'%s\': size=%llx, sectors=%llu\n",+ // data->name,
+ // data->size,
+ // data->size / 0x930
+ // );
+
+ if (data->buf_mode == LD_BUFFERED) {+ data->buf = malloc(data->size);
+
+ fseek(file, 0, SEEK_SET);
+ fread(data->buf, 1, data->size, file);
+
+ fclose(file);
+ } else {+ data->buf = file;
+ }
+
+ data->start = lba;
+
+ init_tracks(data, &lba);
+
+ node = node->next;
+ }
+
+ return CUE_OK;
+}
+
+void cue_destroy(cue_t* cue) {+ node_t* node = list_front(cue->files);
+
+ while (node) {+ cue_file_t* file = node->data;
+
+ if (file->buf_mode == LD_BUFFERED) {+ free(file->buf);
+ } else {+ fclose((FILE*)file->buf);
+ }
+
+ list_destroy(file->tracks);
+
+ free(file->name);
+ free(file);
+
+ node = node->next;
+ }
+
+ list_destroy(cue->files);
+
+ node = list_front(cue->tracks);
+
+ while (node) {+ free(node->data);
+
+ node = node->next;
+ }
+
+ list_destroy(cue->tracks);
+
+ free(cue);
+}
+
+cue_track_t* get_sector_track(cue_t* cue, uint32_t lba) {+ node_t* node = list_front(cue->tracks);
+
+ while (node) {+ cue_track_t* track = node->data;
+
+ if ((lba >= track->start) && (lba < track->end))
+ return track;
+
+ node = node->next;
+ }
+
+ return NULL;
+}
+
+cue_track_t* get_sector_track_in_pregap(cue_t* cue, uint32_t lba) {+ node_t* node = list_front(cue->tracks);
+
+ while (node) {+ cue_track_t* track = node->data;
+
+ if (!node->next)
+ return track;
+
+ cue_track_t* next = node->next->data;
+
+ // Ignore sector number
+ int curr_start = track->start - (track->start % 75);
+ int next_start = next->start - (next->start % 75);
+
+ if ((lba >= curr_start) && (lba < next_start))
+ return track;
+
+ node = node->next;
+ }
+
+ return NULL;
+}
+
+int cue_query(cue_t* cue, uint32_t lba) {+ if (lba >= ((cue_track_t*)list_back(cue->tracks)->data)->end)
+ return TS_FAR;
+
+ cue_track_t* track = get_sector_track(cue, lba);
+
+ // If the LBA isn't too far but the track wasn't found
+ // then we are being requested a pregap sector. Clear buffer
+ // and initialize sync data (not actually needed)
+ if (!track)
+ return TS_PREGAP;
+
+ return (track->mode == CUE_MODE2_2352) ? TS_DATA : TS_AUDIO;
+}
+
+int cue_read(cue_t* cue, uint32_t lba, void* buf) {+ if (lba >= ((cue_track_t*)list_back(cue->tracks)->data)->end)
+ return TS_FAR;
+
+ cue_track_t* track = get_sector_track(cue, lba);
+
+ // If the LBA isn't too far but the track wasn't found
+ // then we are being requested a pregap sector. Clear buffer
+ // and initialize sync data (not actually needed)
+ if (!track) {+ memset(buf, 0, 2352);
+ memset(buf + 1, 255, 10);
+
+ return TS_PREGAP;
+ }
+
+ cue_file_t* file = track->file;
+
+ // printf("Reading sector %u at track %u, file=%s (%u), offset=%u (%08x)\n",+ // lba,
+ // track->number,
+ // track->file->name,
+ // file->start,
+ // lba - file->start,
+ // (lba - file->start) * 2352
+ // );
+
+ if (file->buf_mode == LD_BUFFERED) {+ uint8_t* ptr = file->buf + ((lba - file->start) * 2352);
+
+ memcpy(buf, ptr, 2352);
+ } else {+ fseek(file->buf, (lba - file->start) * 2352, SEEK_SET);
+
+ fread(buf, 1, 2352, file->buf);
+ }
+
+ return (track->mode == CUE_MODE2_2352) ? TS_DATA : TS_AUDIO;
+}
+
+int cue_get_track_number(cue_t* cue, uint32_t lba) {+ cue_track_t* track = get_sector_track_in_pregap(cue, lba);
+
+ if (cue_query(cue, lba) == TS_PREGAP)
+ return track->number + 1;
+
+ return track->number;
+}
+
+int cue_get_track_count(cue_t* cue) {+ return cue->tracks->size;
+}
+
+int cue_get_track_lba(cue_t* cue, int track) {+ if (!track)
+ return ((cue_track_t*)list_back(cue->tracks)->data)->end;
+
+ if (track > cue->tracks->size)
+ return TS_FAR;
+
+ cue_track_t* data = list_at(cue->tracks, track - 1)->data;
+
+ return data->start;
+}
+
+void cue_init_disc(cue_t* cue, psx_disc_t* disc) {+ disc->udata = cue;
+ disc->read_sector = cue_read;
+ disc->query_sector = cue_query;
+ disc->get_track_number = cue_get_track_number;
+ disc->get_track_count = cue_get_track_count;
+ disc->get_track_lba = cue_get_track_lba;
+ disc->destroy = cue_destroy;
+}
\ No newline at end of file
--- /dev/null
+++ b/psx/dev/cdrom/cue.h
@@ -1,0 +1,99 @@
+#ifndef CUE_H
+#define CUE_H
+
+#include "list.h"
+#include "disc.h"
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+
+enum {+ CUE_OK = 0,
+ CUE_FILE_NOT_FOUND,
+ CUE_TRACK_FILE_NOT_FOUND
+};
+
+enum {+ CUE_4CH = 0,
+ CUE_AIFF,
+ CUE_AUDIO,
+ CUE_BINARY,
+ CUE_CATALOG,
+ CUE_CDG,
+ CUE_CDI_2336,
+ CUE_CDI_2352,
+ CUE_CDTEXTFILE,
+ CUE_DCP,
+ CUE_FILE,
+ CUE_FLAGS,
+ CUE_INDEX,
+ CUE_ISRC,
+ CUE_MODE1_2048,
+ CUE_MODE1_2352,
+ CUE_MODE2_2336,
+ CUE_MODE2_2352,
+ CUE_MOTOROLA,
+ CUE_MP3,
+ CUE_PERFORMER,
+ CUE_POSTGAP,
+ CUE_PRE,
+ CUE_PREGAP,
+ CUE_REM,
+ CUE_SCMS,
+ CUE_SONGWRITER,
+ CUE_TITLE,
+ CUE_TRACK,
+ CUE_WAVE,
+ CUE_NONE = 255
+};
+
+enum {+ LD_BUFFERED,
+ LD_FILE
+};
+
+typedef struct {+ char* name;
+ int buf_mode;
+ void* buf;
+ size_t size;
+ uint32_t start;
+ list_t* tracks;
+} cue_file_t;
+
+typedef struct {+ int number;
+ int mode;
+
+ int32_t index[2];
+ uint32_t pregap;
+ uint32_t start;
+ uint32_t end;
+
+ cue_file_t* file;
+} cue_track_t;
+
+typedef struct {+ list_t* files;
+ list_t* tracks;
+
+ char c;
+ FILE* file;
+} cue_t;
+
+cue_t* cue_create(void);
+void cue_init(cue_t* cue);
+int cue_parse(cue_t* cue, const char* path);
+int cue_load(cue_t* cue, int mode);
+
+// Disc interface
+int cue_read(cue_t* cue, uint32_t lba, void* buf);
+int cue_query(cue_t* cue, uint32_t lba);
+int cue_get_track_number(cue_t* cue, uint32_t lba);
+int cue_get_track_count(cue_t* cue);
+int cue_get_track_lba(cue_t* cue, int track);
+void cue_init_disc(cue_t* cue, psx_disc_t* disc);
+void cue_destroy(cue_t* cue);
+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/psx/dev/cdrom/disc.c
@@ -1,0 +1,113 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "disc.h"
+#include "cue.h"
+
+#define MSF_TO_LBA(m, s, f) ((m * 4500) + (s * 75) + f)
+
+const char* disc_cd_extensions[] = {+ "cue",
+ "bin",
+ "iso",
+ 0
+};
+
+psx_disc_t* psx_disc_create(void) {+ return malloc(sizeof(psx_disc_t));
+}
+
+int disc_get_extension(const char* path) {+ const char* ptr = &path[strlen(path) - 1];
+ int i = 0;
+
+ while ((*ptr != '.') && (ptr != path))
+ --ptr;
+
+ if (ptr == path)
+ return CD_EXT_UNSUPPORTED;
+
+ while (disc_cd_extensions[i]) {+ if (!strcmp(ptr + 1, disc_cd_extensions[i]))
+ return i;
+
+ ++i;
+ }
+
+ return CD_EXT_UNSUPPORTED;
+}
+
+int disc_get_cd_type(psx_disc_t* disc) {+ char buf[CD_SECTOR_SIZE];
+
+ // If the disc is smaller than 16 sectors
+ // then it can't be a PlayStation game.
+ // Audio discs should also have ISO volume
+ // descriptors, so it's probably something else
+ // entirely.
+ if (!psx_disc_read(disc, MSF_TO_LBA(0, 2, 16), buf))
+ return CDT_UNKNOWN;
+
+ // Check for the "PLAYSTATION" string at PVD offset 20h
+ // Patch 20 byte so comparison is done correctly
+ buf[0x2b] = 0;
+
+ if (strncmp(&buf[0x20], "PLAYSTATION", 12))
+ return CDT_AUDIO;
+
+ return CDT_LICENSED;
+}
+
+int psx_disc_open(psx_disc_t* disc, const char* path) {+ if (!path)
+ return CDT_ERROR;
+
+ int ext = disc_get_extension(path);
+
+ return psx_disc_open_as(disc, path, ext);
+}
+
+int psx_disc_open_as(psx_disc_t* disc, const char* path, int type) {+ switch (type) {+ case CD_EXT_CUE: {+ cue_t* cue = cue_create();
+
+ cue_init(cue);
+ cue_init_disc(cue, disc);
+
+ if (cue_parse(cue, path))
+ return CDT_ERROR;
+
+ if (cue_load(cue, LD_FILE))
+ return CDT_ERROR;
+ } break;
+ }
+
+ return disc_get_cd_type(disc);
+}
+
+int psx_disc_read(psx_disc_t* disc, uint32_t lba, void* buf) {+ return disc->read_sector(disc->udata, lba, buf);
+}
+
+int psx_disc_query(psx_disc_t* disc, uint32_t lba) {+ return disc->query_sector(disc->udata, lba);
+}
+
+int psx_disc_get_track_number(psx_disc_t* disc, uint32_t lba) {+ return disc->get_track_number(disc->udata, lba);
+}
+
+int psx_disc_get_track_count(psx_disc_t* disc) {+ return disc->get_track_count(disc->udata);
+}
+
+int psx_disc_get_track_lba(psx_disc_t* disc, int track) {+ return disc->get_track_lba(disc->udata, track);
+}
+
+void psx_disc_destroy(psx_disc_t* disc) {+ disc->destroy(disc->udata);
+
+ free(disc);
+}
\ No newline at end of file
--- /dev/null
+++ b/psx/dev/cdrom/disc.h
@@ -1,0 +1,73 @@
+#ifndef DISC_H
+#define DISC_H
+
+#include <stdint.h>
+
+/*
+ PSX disc reader API version 2 specification:
+
+ Mandatory formats:
+ - BIN/CUE (Multi track)
+ - BIN (Single track)
+
+ Optional (but encouraged) formats:
+ - ISO (Raw ISO9660 images)
+ - CHD (Compressed MAME "Hunks of Data")
+
+ Optional formats:
+ - MDS/MDF (Alcohol 120% images)
+*/
+
+enum {+ TS_FAR = 0,
+ TS_DATA,
+ TS_AUDIO,
+ TS_PREGAP
+};
+
+enum {+ CD_EXT_CUE = 0,
+ CD_EXT_BIN,
+ CD_EXT_ISO,
+ CD_EXT_RAW,
+ CD_EXT_UNSUPPORTED
+};
+
+enum {+ CDT_ERROR = 0,
+ CDT_LICENSED,
+ CDT_AUDIO,
+ CDT_UNKNOWN
+};
+
+#define CD_SECTOR_SIZE 2352
+
+typedef int (*read_sector_func)(void*, uint32_t, void*);
+typedef int (*query_sector_func)(void*, uint32_t);
+typedef int (*get_track_number_func)(void*, uint32_t);
+typedef int (*get_track_count_func)(void*);
+typedef uint32_t (*get_track_lba_func)(void*, int);
+typedef void (*destroy_func)(void*);
+
+typedef struct {+ void* udata;
+ read_sector_func read_sector;
+ query_sector_func query_sector;
+ get_track_number_func get_track_number;
+ get_track_count_func get_track_count;
+ get_track_lba_func get_track_lba;
+ destroy_func destroy;
+} psx_disc_t;
+
+psx_disc_t* psx_disc_create(void);
+int psx_disc_open(psx_disc_t* disc, const char* path);
+int psx_disc_open_as(psx_disc_t* disc, const char* path, int type);
+int psx_disc_read(psx_disc_t* disc, uint32_t lba, void* buf);
+int psx_disc_query(psx_disc_t* disc, uint32_t lba);
+int psx_disc_get_track_number(psx_disc_t* disc, uint32_t lba);
+int psx_disc_get_track_count(psx_disc_t* disc);
+int psx_disc_get_track_lba(psx_disc_t* disc, int track);
+void psx_disc_close(psx_disc_t* disc);
+void psx_disc_destroy(psx_disc_t* disc);
+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/psx/dev/cdrom/impl.c
@@ -1,0 +1,724 @@
+#include "cdrom.h"
+
+#define BTOI(b) btoi_table[b]
+#define ITOB(b) itob_table[b]
+
+#define VALID_BCD(bcd) \
+ (((bcd & 0xf0) <= 0x90) && ((bcd & 0xf) <= 9))
+
+#define VALID_MSF(m, s, f) \
+ (VALID_BCD(m) && VALID_BCD(s) && VALID_BCD(f) && (f < 0x75) && (s < 0x60))
+
+static const uint8_t btoi_table[] = {+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
+ 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
+ 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+ 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
+ 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
+ 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
+ 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+ 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
+ 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91,
+ 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93,
+ 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
+ 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d,
+ 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5,
+};
+
+static const uint8_t itob_table[] = {+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31,
+ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x60, 0x61, 0x62, 0x63,
+ 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
+ 0x96, 0x97, 0x98, 0x99, 0xa0, 0xa1, 0xa2, 0xa3,
+ 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xb0, 0xb1,
+ 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5,
+ 0xd6, 0xd7, 0xd8, 0xd9, 0xe0, 0xe1, 0xe2, 0xe3,
+ 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xf0, 0xf1,
+ 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31,
+ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x58, 0x59, 0x60, 0x61, 0x62, 0x63,
+ 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
+};
+
+static const uint8_t cdrom_cd_getid[] = {+ 0x08, 0x40, 0x00, 0x00, // No Disk
+ 0x02, 0x00, 0x20, 0x00, // Licensed
+ 0x0a, 0x90, 0x00, 0x00, // Audio Disk
+ 0x0a, 0x80, 0x00, 0x00 // Unlicensed
+};
+
+static const uint8_t cdrom_version_id[] = {+ 0x94, 0x09, 0x19, 0x01, // DTL-H2000 (date unknown)
+ 0x94, 0x09, 0x19, 0xc0,
+ 0x94, 0x11, 0x18, 0xc0,
+ 0x95, 0x05, 0x16, 0xc1,
+ 0x95, 0x07, 0x24, 0xc1,
+ 0x95, 0x07, 0x24, 0xd1,
+ 0x96, 0x08, 0x15, 0xc2,
+ 0x96, 0x08, 0x18, 0xc1,
+ 0x96, 0x09, 0x12, 0xc2,
+ 0x97, 0x01, 0x10, 0xc2,
+ 0x97, 0x08, 0x14, 0xc2,
+ 0x98, 0x06, 0x10, 0xc3,
+ 0x99, 0x02, 0x01, 0xc3,
+ 0xa1, 0x03, 0x06, 0xc3
+};
+
+static const char cdrom_region_letter[] = {+ 'I', 'E', 'A'
+};
+
+void cdrom_restore_state(psx_cdrom_t* cdrom) {+ cdrom->state = CD_STATE_IDLE;
+
+ if (cdrom->prev_state == CD_STATE_PLAY ||
+ cdrom->prev_state == CD_STATE_READ)
+ cdrom->state = cdrom->prev_state;
+
+ cdrom->pending_command = 0;
+}
+
+void cdrom_cmd_getstat(psx_cdrom_t* cdrom) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom_restore_state(cdrom);
+}
+
+void cdrom_cmd_setloc(psx_cdrom_t* cdrom) {+ int m = queue_pop(cdrom->parameters);
+ int s = queue_pop(cdrom->parameters);
+ int f = queue_pop(cdrom->parameters);
+
+ if (!VALID_MSF(m, s, f)) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE,
+ CD_ERR_INVALID_SUBFUNCTION
+ );
+
+ return;
+ }
+
+ cdrom_set_int(cdrom, 3);
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom->pending_lba = (BTOI(m) * 4500) + (BTOI(s) * 75) + BTOI(f);
+ cdrom_restore_state(cdrom);
+}
+
+void cdrom_cmd_play(psx_cdrom_t* cdrom) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ int track = -1;
+
+ if (!queue_is_empty(cdrom->parameters)) {+ int track = BTOI(queue_pop(cdrom->parameters));
+
+ if (track)
+ cdrom->pending_lba = psx_disc_get_track_lba(cdrom->disc, track);
+ }
+
+ cdrom_process_setloc(cdrom);
+
+ int mm = cdrom->lba / (60 * 75);
+ int ss = (cdrom->lba % (60 * 75)) / 75;
+ int ff = (cdrom->lba % (60 * 75)) % 75;
+
+ printf("play song at lba=%08x %02u:%02u:%02u track=%d\n", cdrom->lba, mm, ss, ff, track);+
+ cdrom->prev_state = CD_STATE_PLAY;
+ cdrom->state = CD_STATE_PLAY;
+ cdrom->pending_command = 0;
+ cdrom->cdda_playing = 1;
+ cdrom->cdda_remaining_samples = 0;
+ cdrom->cdda_sample_index = 0;
+
+ cdrom_restore_state(cdrom);
+}
+
+void cdrom_cmd_forward(psx_cdrom_t* cdrom) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom->lba += 35;
+
+ cdrom_restore_state(cdrom);
+}
+
+void cdrom_cmd_backward(psx_cdrom_t* cdrom) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom->lba -= 35;
+
+ if (cdrom->lba < 150)
+ cdrom->lba = 150;
+
+ cdrom_restore_state(cdrom);
+}
+
+void cdrom_cmd_readn(psx_cdrom_t* cdrom) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom_process_setloc(cdrom);
+
+ // Preload sector
+ int ts = psx_disc_read(cdrom->disc, cdrom->lba, cdrom->data->buf);
+
+ if (cdrom->mode & MODE_XA_ADPCM) {+ cdrom->xa_playing = 1;
+ cdrom->xa_remaining_samples = 0;
+ cdrom->xa_sample_index = 0;
+ cdrom->xa_lba = cdrom->lba;
+ }
+
+ cdrom->state = CD_STATE_READ;
+ cdrom->prev_state = CD_STATE_READ;
+ cdrom->delay = CD_DELAY_START_READ;
+ cdrom->read_ongoing = 1;
+}
+
+void cdrom_cmd_motoron(psx_cdrom_t* cdrom) {+ if (cdrom->state == CD_STATE_TX_RESP1) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom->delay = CD_DELAY_FR;
+ cdrom->state = CD_STATE_TX_RESP2;
+ } else {+ cdrom_set_int(cdrom, 2);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ // Doesn't pause!
+ cdrom_restore_state(cdrom);
+ }
+}
+
+void cdrom_cmd_stop(psx_cdrom_t* cdrom) {+ if (cdrom->state == CD_STATE_TX_RESP1) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom->delay = CD_DELAY_FR;
+ cdrom->state = CD_STATE_TX_RESP2;
+ } else {+ cdrom_set_int(cdrom, 2);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ // Reset to 00:02:00
+ cdrom->pending_lba = 150;
+
+ cdrom_process_setloc(cdrom);
+
+ cdrom->prev_state = CD_STATE_IDLE;
+ cdrom->state = CD_STATE_IDLE;
+ cdrom->pending_command = 0;
+ cdrom->busy = 0;
+ cdrom->cdda_playing = 0;
+ cdrom->xa_playing = 0;
+ cdrom->read_ongoing = 0;
+ }
+}
+
+void cdrom_cmd_pause(psx_cdrom_t* cdrom) {+ if (cdrom->state == CD_STATE_TX_RESP1) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ // Pausing at 1x takes 70ms, 2x takes 35ms
+ // but setting delays that high breaks games
+ cdrom->delay = CD_DELAY_1MS;
+ cdrom->state = CD_STATE_TX_RESP2;
+ } else {+ cdrom_set_int(cdrom, 2);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom->prev_state = CD_STATE_IDLE;
+ cdrom->state = CD_STATE_IDLE;
+ cdrom->pending_command = 0;
+ cdrom->busy = 0;
+ cdrom->cdda_playing = 0;
+ cdrom->xa_playing = 0;
+ cdrom->read_ongoing = 0;
+ }
+}
+
+void cdrom_cmd_init(psx_cdrom_t* cdrom) {+ // Init sends the "same" thing twice. On real hardware
+ // it would probably send something different, but that's
+ // not really important.
+ if (cdrom->state == CD_STATE_TX_RESP1) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom->delay = CD_DELAY_FR;
+ cdrom->state = CD_STATE_TX_RESP2;
+ } else {+ cdrom_set_int(cdrom, 2);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom->prev_state = CD_STATE_IDLE;
+ cdrom->state = CD_STATE_IDLE;
+ cdrom->pending_command = 0;
+ cdrom->busy = 0;
+ cdrom->cdda_playing = 0;
+ cdrom->xa_playing = 0;
+ cdrom->read_ongoing = 0;
+ }
+}
+
+void cdrom_cmd_mute(psx_cdrom_t* cdrom) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom->mute = 1;
+
+ cdrom_restore_state(cdrom);
+}
+
+void cdrom_cmd_demute(psx_cdrom_t* cdrom) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom->mute = 0;
+
+ cdrom_restore_state(cdrom);
+}
+
+void cdrom_cmd_setfilter(psx_cdrom_t* cdrom) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ // 0x0d byte has to be sent (and ignored) for some reason
+ // queue_pop(cdrom->parameters);
+
+ cdrom->xa_file = queue_pop(cdrom->parameters);
+ cdrom->xa_channel = queue_pop(cdrom->parameters);
+
+ cdrom_restore_state(cdrom);
+}
+
+void cdrom_cmd_setmode(psx_cdrom_t* cdrom) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ int prev_speed = cdrom->mode & MODE_SPEED;
+
+ cdrom->mode = queue_pop(cdrom->parameters);
+
+ // Big speed switch delay
+ if (prev_speed != (cdrom->mode & MODE_SPEED))
+ cdrom->pending_speed_switch_delay = CD_DELAY_1MS;
+
+ cdrom_restore_state(cdrom);
+}
+
+void cdrom_cmd_getparam(psx_cdrom_t* cdrom) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+ queue_push(cdrom->response, cdrom->mode);
+ queue_push(cdrom->response, 0);
+ queue_push(cdrom->response, cdrom->xa_file);
+ queue_push(cdrom->response, cdrom->xa_channel);
+
+ cdrom_restore_state(cdrom);
+}
+
+void cdrom_cmd_getlocl(psx_cdrom_t* cdrom) {+ // GetlocL only works while reading. This is
+ // Somewhat of a hack, but should still work
+ // if (queue_is_empty(cdrom->data) && !cdrom->fake_getlocl_data) {+ // printf("getlocl error\n");+ // cdrom_error(cdrom,
+ // CD_STAT_SPINDLE,
+ // CD_ERR_NO_DISC
+ // );
+
+ // return;
+ // }
+
+ cdrom->fake_getlocl_data = 0;
+
+ // printf("getlocl: lba=%u %02x:%02x:%02x mode=%02x file=%02x channel=%02x sm=%02x ci=%02x\n",+ // cdrom->lba,
+ // cdrom->data->buf[0x0c],
+ // cdrom->data->buf[0x0d],
+ // cdrom->data->buf[0x0e],
+ // cdrom->data->buf[0x0f],
+ // cdrom->data->buf[0x10],
+ // cdrom->data->buf[0x11],
+ // cdrom->data->buf[0x12],
+ // cdrom->data->buf[0x13]
+ // );
+
+ cdrom_set_int(cdrom, 3);
+ queue_push(cdrom->response, cdrom->data->buf[0x0c]);
+ queue_push(cdrom->response, cdrom->data->buf[0x0d]);
+ queue_push(cdrom->response, cdrom->data->buf[0x0e]);
+ queue_push(cdrom->response, cdrom->data->buf[0x0f]);
+ queue_push(cdrom->response, cdrom->data->buf[0x10]);
+ queue_push(cdrom->response, cdrom->data->buf[0x11]);
+ queue_push(cdrom->response, cdrom->data->buf[0x12]);
+ queue_push(cdrom->response, cdrom->data->buf[0x13]);
+
+ cdrom_restore_state(cdrom);
+}
+
+void cdrom_cmd_getlocp(psx_cdrom_t* cdrom) {+ int track = psx_disc_get_track_number(cdrom->disc, cdrom->lba);
+ int track_lba = psx_disc_get_track_lba(cdrom->disc, track);
+
+ int lba = cdrom->lba;
+
+ if (!cdrom->seek_precision)
+ lba -= 25;
+
+ int32_t diff = lba - track_lba;
+
+ if (diff < 0)
+ diff = -diff;
+
+ int rmm = diff / (60 * 75);
+ int rss = (diff % (60 * 75)) / 75;
+ int rff = (diff % (60 * 75)) % 75;
+ int amm = lba / (60 * 75);
+ int ass = (lba % (60 * 75)) / 75;
+ int aff = (lba % (60 * 75)) % 75;
+
+ int index = psx_disc_query(cdrom->disc, lba) != TS_PREGAP;
+
+ // printf("getlocp: track %u (%02x) relative: %02u:%02u:%02u absolute: %02u:%02u:%02u pregap=%u\n",+ // track, ITOB(track),
+ // rmm, rss, rff,
+ // amm, ass, aff,
+ // index
+ // );
+
+ cdrom_set_int(cdrom, 3);
+ queue_push(cdrom->response, ITOB(track));
+ queue_push(cdrom->response, ITOB(index));
+ queue_push(cdrom->response, ITOB(rmm));
+ queue_push(cdrom->response, ITOB(rss));
+ queue_push(cdrom->response, ITOB(rff));
+ queue_push(cdrom->response, ITOB(amm));
+ queue_push(cdrom->response, ITOB(ass));
+ queue_push(cdrom->response, ITOB(aff));
+
+ cdrom_restore_state(cdrom);
+}
+
+// To-do: Implement SetSession errors
+void cdrom_cmd_setsession(psx_cdrom_t* cdrom) {+ if (cdrom->state == CD_STATE_TX_RESP1) {+ cdrom_set_int(cdrom, 3);
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom->delay = CD_DELAY_FR;
+ cdrom->state = CD_STATE_TX_RESP2;
+ cdrom->busy = 1;
+ } else {+ cdrom_set_int(cdrom, 2);
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom_restore_state(cdrom);
+ }
+}
+
+void cdrom_cmd_gettn(psx_cdrom_t* cdrom) {+ cdrom_set_int(cdrom, 3);
+
+ int tn = psx_disc_get_track_count(cdrom->disc);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+ queue_push(cdrom->response, 1);
+ queue_push(cdrom->response, ITOB(tn));
+
+ cdrom_restore_state(cdrom);
+}
+
+void cdrom_cmd_gettd(psx_cdrom_t* cdrom) {+ int bcd = queue_pop(cdrom->parameters);
+
+ // Check BCD
+ if (!VALID_BCD(bcd)) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE,
+ CD_ERR_INVALID_SUBFUNCTION
+ );
+
+ return;
+ }
+
+ int track = BTOI(bcd);
+ int f = psx_disc_get_track_lba(cdrom->disc, track);
+
+ if (f == TS_FAR) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE,
+ CD_ERR_INVALID_SUBFUNCTION
+ );
+
+ return;
+ }
+
+ cdrom_set_int(cdrom, 3);
+
+ int mm = f / (60 * 75);
+ int ss = (f % (60 * 75)) / 75;
+ int ff = (f % (60 * 75)) % 75;
+
+ // printf("gettd: track %u lba=%08x (%u) %02u:%02u:%02u\n",+ // track,
+ // f, f,
+ // mm, ss, ff
+ // );
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+ queue_push(cdrom->response, ITOB(mm));
+ queue_push(cdrom->response, ITOB(ss));
+
+ cdrom_restore_state(cdrom);
+}
+
+void cdrom_cmd_seekl(psx_cdrom_t* cdrom) {+ if (cdrom->state == CD_STATE_TX_RESP1) {+ cdrom_set_int(cdrom, 3);
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ int ts = psx_disc_query(cdrom->disc, cdrom->pending_lba);
+
+ cdrom->delay = CD_DELAY_1MS; // cdrom_get_seek_delay(cdrom, ts);
+ cdrom->state = CD_STATE_TX_RESP2;
+ } else {+ int ts = psx_disc_query(cdrom->disc, cdrom->pending_lba);
+
+ if (ts == TS_FAR) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE | CD_STAT_SEEKERROR,
+ CD_ERR_INVALID_SUBFUNCTION
+ );
+
+ return;
+ }
+
+ if (ts == TS_AUDIO) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE | CD_STAT_SEEKERROR,
+ CD_ERR_SEEK_FAILED
+ );
+
+ return;
+ }
+
+ // If everything is right, then seek is successful
+ cdrom_process_setloc(cdrom);
+ cdrom->seek_precision = 1;
+
+ cdrom_set_int(cdrom, 2);
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom_restore_state(cdrom);
+ }
+}
+
+void cdrom_cmd_seekp(psx_cdrom_t* cdrom) {+ if (cdrom->state == CD_STATE_TX_RESP1) {+ cdrom_set_int(cdrom, 3);
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom->delay = CD_DELAY_1MS; // cdrom_get_seek_delay(cdrom, ts);
+ cdrom->state = CD_STATE_TX_RESP2;
+ cdrom->busy = 1;
+ } else {+ cdrom_set_int(cdrom, 2);
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom_process_setloc(cdrom);
+ cdrom->seek_precision = 0;
+
+ cdrom_restore_state(cdrom);
+ }
+}
+
+void cdrom_cmd_test(psx_cdrom_t* cdrom) {+ int subf = queue_pop(cdrom->parameters);
+
+ // To-do: Handle other subfunctions (hard)
+ // assert(subf == 32);
+ if (subf != 32) {+ cdrom_error(cdrom,
+ CD_STAT_SPINDLE,
+ CD_ERR_INVALID_SUBFUNCTION
+ );
+
+ return;
+ }
+
+ int v = cdrom->version * 4;
+
+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_version_id[v+0]);
+ queue_push(cdrom->response, cdrom_version_id[v+1]);
+ queue_push(cdrom->response, cdrom_version_id[v+2]);
+ queue_push(cdrom->response, cdrom_version_id[v+3]);
+
+ cdrom_restore_state(cdrom);
+}
+
+void cdrom_cmd_getid(psx_cdrom_t* cdrom) {+ if (cdrom->state == CD_STATE_TX_RESP1) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom->state = CD_STATE_TX_RESP2;
+ cdrom->delay = CD_DELAY_FR;
+ } else {+ cdrom_set_int(cdrom, 2);
+
+ int t = cdrom->disc_type * 4;
+
+ queue_push(cdrom->response, cdrom_cd_getid[t+0]);
+ queue_push(cdrom->response, cdrom_cd_getid[t+1]);
+ queue_push(cdrom->response, cdrom_cd_getid[t+2]);
+ queue_push(cdrom->response, cdrom_cd_getid[t+3]);
+
+ if (cdrom->disc_type == CDT_LICENSED) {+ queue_push(cdrom->response, 'S');
+ queue_push(cdrom->response, 'C');
+ queue_push(cdrom->response, 'E');
+ queue_push(cdrom->response, cdrom_region_letter[cdrom->region]);
+ } else {+ queue_push(cdrom->response, 0);
+ queue_push(cdrom->response, 0);
+ queue_push(cdrom->response, 0);
+ queue_push(cdrom->response, 0);
+ }
+
+ cdrom_restore_state(cdrom);
+ }
+}
+
+void cdrom_cmd_reads(psx_cdrom_t* cdrom) {+ cdrom_set_int(cdrom, 3);
+
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom_process_setloc(cdrom);
+
+ // Preload sector
+ int ts = psx_disc_read(cdrom->disc, cdrom->lba, cdrom->data->buf);
+
+ if (cdrom->mode & MODE_XA_ADPCM) {+ cdrom->xa_playing = 1;
+ cdrom->xa_remaining_samples = 0;
+ cdrom->xa_sample_index = 0;
+ cdrom->xa_lba = cdrom->lba;
+
+ // printf("reads: xa_lba=%u (%08x) lba=%u (%08x) %02x:%02x:%02x mode=%02x file=%02x channel=%02x sm=%02x ci=%02x\n",+ // cdrom->xa_lba, cdrom->xa_lba,
+ // cdrom->lba, cdrom->lba,
+ // cdrom->data->buf[0x0c],
+ // cdrom->data->buf[0x0d],
+ // cdrom->data->buf[0x0e],
+ // cdrom->data->buf[0x0f],
+ // cdrom->data->buf[0x10],
+ // cdrom->data->buf[0x11],
+ // cdrom->data->buf[0x12],
+ // cdrom->data->buf[0x13]
+ // );
+ }
+
+ cdrom->state = CD_STATE_READ;
+ cdrom->prev_state = CD_STATE_READ;
+ cdrom->delay = CD_DELAY_START_READ;
+ cdrom->read_ongoing = 1;
+}
+
+void cdrom_cmd_reset(psx_cdrom_t* cdrom) {+
+}
+
+void cdrom_cmd_getq(psx_cdrom_t* cdrom) {+
+}
+
+void cdrom_cmd_readtoc(psx_cdrom_t* cdrom) {+ if (cdrom->state == CD_STATE_TX_RESP1) {+ cdrom_set_int(cdrom, 3);
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom->delay = CD_DELAY_1MS * 1000;
+ cdrom->state = CD_STATE_TX_RESP2;
+ cdrom->busy = 1;
+ } else {+ cdrom_set_int(cdrom, 2);
+ queue_push(cdrom->response, cdrom_get_stat(cdrom));
+
+ cdrom_restore_state(cdrom);
+ }
+}
+
+void cdrom_cmd_videocd(psx_cdrom_t* cdrom) {+
+}
\ No newline at end of file
--- /dev/null
+++ b/psx/dev/cdrom/list.c
@@ -1,0 +1,122 @@
+#include "list.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+list_t* list_create(void) {+ list_t* list = malloc(sizeof(list_t));
+
+ list_init(list);
+
+ return list;
+}
+
+void list_init(list_t* list) {+ list->first = NULL;
+ list->last = NULL;
+ list->size = 0;
+}
+
+void list_push_front(list_t* list, void* data) {+ node_t* node = malloc(sizeof(node_t));
+
+ node->data = data;
+ node->next = list->first;
+
+ list->first = node;
+
+ if (!list->last)
+ list->last = list->first;
+
+ ++list->size;
+}
+
+void list_push_back(list_t* list, void* data) {+ node_t* node = malloc(sizeof(node_t));
+
+ node->data = data;
+ node->next = NULL;
+
+ if (!list->last) {+ list->first = node;
+ list->last = node;
+ } else {+ list->last->next = node;
+ list->last = node;
+ }
+
+ ++list->size;
+}
+
+void list_pop_front(list_t* list) {+ if (!list->first)
+ return;
+
+ node_t* next = list->first->next;
+
+ free(list->first);
+
+ list->first = next;
+
+ --list->size;
+}
+
+void list_pop_back(list_t* list) {+ if (!list->last)
+ return;
+
+ node_t* node = list->first;
+
+ while (node->next != list->last)
+ node = node->next;
+
+ free(node->next);
+
+ node->next = NULL;
+
+ list->last = node;
+}
+
+node_t* list_front(list_t* list) {+ return list->first;
+}
+
+node_t* list_back(list_t* list) {+ return list->last;
+}
+
+node_t* list_at(list_t* list, int index) {+ if (index > list->size)
+ return NULL;
+
+ node_t* node = list->first;
+
+ for (int i = 0; i < index; i++)
+ node = node->next;
+
+ return node;
+}
+
+void list_iterate(list_t* list, void (*func)(void*)) {+ node_t* node = list->first;
+
+ while (node) {+ func(node->data);
+
+ node = node->next;
+ }
+}
+
+void list_destroy(list_t* list) {+ node_t* node = list->first;
+
+ while (node) {+ node_t* next = node->next;
+
+ free(node);
+
+ node = next;
+ }
+
+ free(list);
+}
\ No newline at end of file
--- /dev/null
+++ b/psx/dev/cdrom/list.h
@@ -1,0 +1,31 @@
+#ifndef LIST_H
+#define LIST_H
+
+#include <stddef.h>
+
+typedef struct node_t node_t;
+
+typedef struct node_t {+ node_t* next;
+ void* data;
+} node_t;
+
+typedef struct {+ node_t* first;
+ node_t* last;
+ size_t size;
+} list_t;
+
+list_t* list_create(void);
+void list_init(list_t* list);
+void list_push_front(list_t* list, void* data);
+void list_push_back(list_t* list, void* data);
+void list_pop_front(list_t* list);
+void list_pop_back(list_t* list);
+node_t* list_front(list_t* list);
+node_t* list_back(list_t* list);
+node_t* list_at(list_t* list, int index);
+void list_iterate(list_t* list, void (*func)(void*));
+void list_destroy(list_t* list);
+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/psx/dev/cdrom/queue.c
@@ -1,0 +1,75 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "queue.h"
+
+queue_t* queue_create(void) {+ return malloc(sizeof(queue_t));
+}
+
+void queue_init(queue_t* queue, size_t size) {+ queue->buf = malloc(size);
+ queue->read_index = 0;
+ queue->write_index = 0;
+ queue->size = size;
+}
+
+void queue_push(queue_t* queue, uint8_t value) {+ if (queue_is_full(queue))
+ return;
+
+ queue->buf[queue->write_index++] = value;
+}
+
+uint8_t queue_pop(queue_t* queue) {+ if (queue_is_empty(queue))
+ return 0;
+
+ uint8_t data = queue->buf[queue->read_index++];
+
+ if (queue_is_empty(queue))
+ queue_reset(queue);
+
+ return data;
+}
+
+uint8_t queue_peek(queue_t* queue) {+ if (queue_is_empty(queue))
+ return 0;
+
+ return queue->buf[queue->read_index];
+}
+
+int queue_is_empty(queue_t* queue) {+ return queue->read_index == queue->write_index;
+}
+
+int queue_is_full(queue_t* queue) {+ return queue->write_index == queue->size;
+}
+
+void queue_reset(queue_t* queue) {+ queue->write_index = 0;
+ queue->read_index = 0;
+}
+
+void queue_clear(queue_t* queue) {+ for (int i = 0; i < queue->write_index; i++)
+ queue->buf[i] = 0;
+
+ queue_reset(queue);
+}
+
+int queue_size(queue_t* queue) {+ return queue->write_index - queue->read_index;
+}
+
+int queue_max_size(queue_t* queue) {+ return queue->size;
+}
+
+void queue_destroy(queue_t* queue) {+ free(queue->buf);
+ free(queue);
+}
\ No newline at end of file
--- /dev/null
+++ b/psx/dev/cdrom/queue.h
@@ -1,0 +1,26 @@
+#ifndef QUEUE_H
+#define QUEUE_H
+
+#include <stdint.h>
+
+typedef struct {+ uint8_t* buf;
+ size_t read_index;
+ size_t write_index;
+ size_t size;
+} queue_t;
+
+queue_t* queue_create(void);
+void queue_init(queue_t* queue, size_t size);
+void queue_push(queue_t* queue, uint8_t value);
+uint8_t queue_pop(queue_t* queue);
+uint8_t queue_peek(queue_t* queue);
+int queue_is_empty(queue_t* queue);
+int queue_is_full(queue_t* queue);
+void queue_reset(queue_t* queue);
+void queue_clear(queue_t* queue);
+int queue_size(queue_t* queue);
+int queue_max_size(queue_t* queue);
+void queue_destroy(queue_t* queue);
+
+#endif
\ No newline at end of file
--- a/psx/dev/gpu.c
+++ b/psx/dev/gpu.c
@@ -1547,11 +1547,11 @@
for (int y = gpu->v0.y; y < (gpu->v0.y + gpu->ysiz); y++) {
for (int x = gpu->v0.x; x < (gpu->v0.x + gpu->xsiz); x++) {
// This shouldn't be needed
- int bc = (x >= gpu->draw_x1) && (x <= gpu->draw_x2) &&
- (y >= gpu->draw_y1) && (y <= gpu->draw_y2);
+ // int bc = (x >= gpu->draw_x1) && (x <= gpu->draw_x2) &&
+ // (y >= gpu->draw_y1) && (y <= gpu->draw_y2);
- if (!bc)
- continue;
+ // if (!bc)
+ // continue;
if ((x < 1024) && (y < 512) && (x >= 0) && (y >= 0))
gpu->vram[x + (y * 1024)] = color;
--- a/psx/disc.c
+++ /dev/null
@@ -1,46 +1,0 @@
-/*
- This file is part of the PSXE Emulator Project
-
- Disc Reader API
-*/
-
-#include "disc.h"
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#define CD_SECTOR_SIZE 0x930
-#define CD_SECTORS_PER_SECOND 75
-
-uint32_t disc_get_addr(msf_t msf) {
- uint32_t sectors = (((msf.m * 60) + msf.s) * CD_SECTORS_PER_SECOND) + msf.f;
-
- return sectors * CD_SECTOR_SIZE;
-}
-
-psx_disc_t* psx_disc_create(void) {
- return (psx_disc_t*)malloc(sizeof(psx_disc_t));
-}
-
-int psx_disc_seek(psx_disc_t* disc, msf_t msf) {
- return disc->seek_func(disc->udata, msf);
-}
-
-int psx_disc_read_sector(psx_disc_t* disc, void* buf) {
- return disc->read_sector_func(disc->udata, buf);
-}
-
-int psx_disc_get_track_addr(psx_disc_t* disc, msf_t* msf, int track) {
- return disc->get_track_addr_func(disc->udata, msf, track);
-}
-
-int psx_disc_get_track_count(psx_disc_t* disc, int* count) {
- return disc->get_track_count_func(disc->udata, count);
-}
-
-void psx_disc_destroy(psx_disc_t* disc) {
- disc->destroy_func(disc->udata);
-
- free(disc);
-}
\ No newline at end of file
--- a/psx/disc.h
+++ /dev/null
@@ -1,45 +1,0 @@
-/*
- This file is part of the PSXE Emulator Project
-
- Disc Reader API
-*/
-
-#ifndef DISC_H
-#define DISC_H
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "log.h"
-#include "msf.h"
-
-enum {
- DISC_ERR_TRACK_OUT_OF_BOUNDS = 1,
- DISC_ERR_ADDR_OUT_OF_BOUNDS
-};
-
-typedef int (*disc_seek_t)(void*, msf_t);
-typedef int (*disc_read_sector_t)(void*, void*);
-typedef int (*disc_get_track_addr_t)(void*, msf_t*, int);
-typedef int (*disc_get_track_count_t)(void*, int*);
-typedef void (*disc_destroy_t)(void*);
-
-typedef struct {
- void* udata;
-
- disc_seek_t seek_func;
- disc_read_sector_t read_sector_func;
- disc_get_track_addr_t get_track_addr_func;
- disc_get_track_count_t get_track_count_func;
- disc_destroy_t destroy_func;
-} psx_disc_t;
-
-psx_disc_t* psx_disc_create(void);
-int psx_disc_seek(psx_disc_t*, msf_t);
-int psx_disc_read_sector(psx_disc_t*, void*);
-int psx_disc_get_track_addr(psx_disc_t*, msf_t*, int);
-int psx_disc_get_track_count(psx_disc_t*, int*);
-void psx_disc_destroy(psx_disc_t*);
-
-#endif
\ No newline at end of file
--- a/psx/disc/bin.c
+++ /dev/null
@@ -1,114 +1,0 @@
-/*
- This file is part of the PSXE Emulator Project
-
- BIN Loader
-*/
-
-#include "bin.h"
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-
-psxd_bin_t* psxd_bin_create(void) {
- return (psxd_bin_t*)malloc(sizeof(psxd_bin_t));
-}
-
-void psxd_bin_init(psxd_bin_t* bin) {
- memset(bin, 0, sizeof(psxd_bin_t));
-}
-
-int psxd_bin_load(psxd_bin_t* bin, const char* path) {
- log_fatal("Loading CD image...");
-
- FILE* file = fopen(path, "rb");
-
- if (ferror(file) || !file) {
- fclose(file);
-
- return 1;
- }
-
- fseek(file, 0, SEEK_END);
-
- bin->buf_size = ftell(file);
-
- fseek(file, 0, SEEK_SET);
-
- bin->buf = malloc(bin->buf_size);
-
- if (!fread(bin->buf, 1, bin->buf_size, file)) {
- perror("Error reading BIN CD image file data");
-
- exit(1);
- }
-
- msf_from_address(&bin->end, bin->buf_size);
-
- fclose(file);
-
- log_fatal("Loaded BIN image, size=%08x, end=%02u:%02u:%02u",
- bin->buf_size,
- bin->end.m,
- bin->end.s,
- bin->end.f
- );
-
- return 0;
-}
-
-int psxd_bin_seek(void* udata, msf_t msf) {
- psxd_bin_t* bin = udata;
-
- msf.s -= 2;
-
- bin->seek_offset = msf_to_address(msf);
-
- log_fatal("BIN seek to %02u:%02u:%02u (%08x < %08x)", msf.m, msf.s, msf.f, bin->seek_offset, bin->buf_size);
-
- if (bin->seek_offset >= bin->buf_size)
- return DISC_ERR_ADDR_OUT_OF_BOUNDS;
-
- return 0;
-}
-
-int psxd_bin_read_sector(void* udata, void* buf) {
- psxd_bin_t* bin = udata;
-
- log_fatal("BIN reading sector at offset %08x", bin->seek_offset);
-
- memcpy(buf, bin->buf + bin->seek_offset, CD_SECTOR_SIZE);
-
- return 0;
-}
-
-int psxd_bin_get_track_addr(void* udata, msf_t* msf, int track) {
- if (track > 1)
- return DISC_ERR_TRACK_OUT_OF_BOUNDS;
-
- msf->m = 0;
- msf->s = 2;
-
- return 0;
-}
-
-int psxd_bin_get_track_count(void* udata, int* count) {
- *count = 1;
-
- return 0;
-}
-
-void psxd_bin_init_disc(psxd_bin_t* bin, psx_disc_t* disc) {
- disc->udata = bin;
- disc->seek_func = psxd_bin_seek;
- disc->read_sector_func = psxd_bin_read_sector;
- disc->get_track_addr_func = psxd_bin_get_track_addr;
- disc->get_track_count_func = psxd_bin_get_track_count;
- disc->destroy_func = (disc_destroy_t)psxd_bin_destroy;
-}
-
-void psxd_bin_destroy(psxd_bin_t* bin) {
- free(bin->buf);
- free(bin);
-}
\ No newline at end of file
--- a/psx/disc/bin.h
+++ /dev/null
@@ -1,30 +1,0 @@
-/*
- This file is part of the PSXE Emulator Project
-
- BIN Loader
-*/
-
-#ifndef BIN_H
-#define BIN_H
-
-#include "../disc.h"
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-
-typedef struct {
- char* buf;
- uint32_t buf_size;
- uint32_t seek_offset;
- msf_t end;
-} psxd_bin_t;
-
-psxd_bin_t* psxd_bin_create(void);
-void psxd_bin_init(psxd_bin_t*);
-int psxd_bin_load(psxd_bin_t*, const char*);
-void psxd_bin_init_disc(psxd_bin_t*, psx_disc_t*);
-void psxd_bin_destroy(psxd_bin_t*);
-
-#endif
\ No newline at end of file
--- a/psx/disc/cue.c
+++ /dev/null
@@ -1,495 +1,0 @@
-/*
- This file is part of the PSXE Emulator Project
-
- CUE Parser + Loader
-*/
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <string.h>
-#include <ctype.h>
-
-#include "cue.h"
-#include "../log.h"
-
-#define CUE_BUF_SIZE 256
-
-static const char* g_psxd_cue_errors[] = {
- "PE_NO_ERROR",
- "PE_EXPECTED_KEYWORD",
- "PE_EXPECTED_STRING",
- "PE_EXPECTED_NUMBER",
- "PE_EXPECTED_COLON",
- "PE_NON_SEQUENTIAL_TRACKS",
- "PE_UNEXPECTED_TOKEN"
-};
-
-static const char* g_psxd_cue_tokens[] = {
- "4CH",
- "AIFF",
- "AUDIO",
- "BINARY",
- "CATALOG",
- "CDG",
- "CDI_2336",
- "CDI_2352",
- "CDTEXTFILE",
- "DCP",
- "FILE",
- "FLAGS",
- "INDEX",
- "ISRC",
- "MODE1_2048",
- "MODE1_2352",
- "MODE2_2336",
- "MODE2_2352",
- "MOTOROLA",
- "MP3",
- "PERFORMER",
- "POSTGAP",
- "PRE",
- "PREGAP",
- "REM",
- "SCMS",
- "SONGWRITER",
- "TITLE",
- "TRACK",
- "WAVE",
- 0
-};
-
-#define EXPECT_KEYWORD(kw) \
- if (cue_parse_keyword(cue)) \
- return cue->error; \
- if (cue_get_keyword(cue) != kw) \
- ERROR_OUT(PE_UNEXPECTED_TOKEN);
-
-void cue_add_track(psxd_cue_t* cue) {
- ++cue->num_tracks;
-
- cue_track_t** new_track = realloc(cue->track, cue->num_tracks * sizeof(cue_track_t*));
-
- if (!new_track) {
- printf("Fatal error: Couldn't allocate a new CUE track\n");
-
- exit(1);
- }
-
- cue->track = new_track;
-
- cue->track[cue->num_tracks - 1] = malloc(sizeof(cue_track_t));
-
- memset(cue->track[cue->num_tracks - 1], 0, sizeof(cue_track_t));
-}
-
-void* cue_alloc_block(void* buf, size_t* block_size, size_t ext) {
- *block_size += ext;
-
- return realloc(buf, *block_size);
-}
-
-void cue_ignore_whitespace(psxd_cue_t* cue) {
- while (isspace(cue->c))
- cue->c = fgetc(cue->file);
-}
-
-int cue_get_keyword(psxd_cue_t* cue) {
- int i = 0;
-
- const char* token = g_psxd_cue_tokens[i];
-
- while (token) {
- if (!strcmp(token, cue->buf)) {
- return i;
- } else {
- token = g_psxd_cue_tokens[++i];
- }
- }
-
- return -1;
-}
-
-#define ERROR_OUT(err) \
- { cue->error = err; return err; }
-
-int cue_parse_keyword(psxd_cue_t* cue) {
- if (!isalpha(cue->c))
- ERROR_OUT(PE_EXPECTED_KEYWORD);
-
- while (isalnum(cue->c) || (cue->c == '/')) {
- *cue->ptr++ = cue->c;
-
- cue->c = fgetc(cue->file);
- }
-
- *cue->ptr = 0;
-
- cue->ptr = cue->buf;
-
- cue_ignore_whitespace(cue);
-
- return 0;
-}
-
-int cue_parse_string(psxd_cue_t* cue) {
- if (cue->c != '\"')
- ERROR_OUT(PE_EXPECTED_STRING);
-
- cue->c = fgetc(cue->file);
-
- while (cue->c != '\"') {
- *cue->ptr++ = cue->c;
-
- cue->c = fgetc(cue->file);
- }
-
- *cue->ptr = 0;
-
- cue->c = fgetc(cue->file);
-
- cue->ptr = cue->buf;
-
- cue_ignore_whitespace(cue);
-
- return 0;
-}
-
-int cue_parse_number(psxd_cue_t* cue) {
- if (!isdigit(cue->c))
- ERROR_OUT(PE_EXPECTED_NUMBER);
-
- while (isdigit(cue->c)) {
- *cue->ptr++ = cue->c;
-
- cue->c = fgetc(cue->file);
- }
-
- *cue->ptr = 0;
-
- cue->ptr = cue->buf;
-
- cue_ignore_whitespace(cue);
-
- return 0;
-}
-
-int cue_parse_msf(psxd_cue_t* cue, msf_t* msf) {
- if (cue_parse_number(cue))
- return cue->error;
-
- if (cue->c != ':')
- ERROR_OUT(PE_EXPECTED_COLON);
-
- cue->c = fgetc(cue->file);
-
- msf->m = atoi(cue->buf);
-
- if (cue_parse_number(cue))
- return cue->error;
-
- if (cue->c != ':')
- ERROR_OUT(PE_EXPECTED_COLON);
-
- cue->c = fgetc(cue->file);
-
- msf->s = atoi(cue->buf);
-
- if (cue_parse_number(cue))
- return cue->error;
-
- msf->f = atoi(cue->buf);
-
- return 0;
-}
-
-int cue_parse(psxd_cue_t* cue, FILE* file) {
- cue->file = file;
- cue->c = fgetc(file);
-
- msf_t msf;
-
- EXPECT_KEYWORD(CUE_FILE);
-
- parse_file:
-
- if (cue_parse_string(cue))
- return cue->error;
-
- strcpy(cue->current_file, cue->buf);
-
- EXPECT_KEYWORD(CUE_BINARY);
- EXPECT_KEYWORD(CUE_TRACK);
-
- parse_track:
-
- if (cue_parse_number(cue))
- return cue->error;
-
- int track = atoi(cue->buf) - 1;
-
- if (track != cue->num_tracks)
- ERROR_OUT(PE_NON_SEQUENTIAL_TRACKS);
-
- cue_add_track(cue);
-
- cue->track[track]->filename = malloc(strlen(cue->current_file) + 1);
-
- // Copy current file to track filename
- strcpy(cue->track[track]->filename, cue->current_file);
-
- if (cue_parse_keyword(cue))
- return cue->error;
-
- cue->track[track]->type = cue_get_keyword(cue);
-
- // Expecting at least 1 index
- EXPECT_KEYWORD(CUE_INDEX);
-
- parse_index:
-
- if (cue_parse_number(cue))
- return cue->error;
-
- int index = atoi(cue->buf);
-
- if (cue_parse_msf(cue, &msf))
- return cue->error;
-
- cue->track[track]->index[index] = msf;
-
- if (feof(cue->file)) {
- fclose(file);
-
- return 0;
- }
-
- if (cue_parse_keyword(cue))
- return cue->error;
-
- switch (cue_get_keyword(cue)) {
- case CUE_INDEX:
- goto parse_index;
-
- case CUE_FILE:
- goto parse_file;
-
- case CUE_TRACK:
- goto parse_track;
-
- default:
- ERROR_OUT(PE_UNEXPECTED_TOKEN);
- }
-}
-
-psxd_cue_t* psxd_cue_create(void) {
- return (psxd_cue_t*)malloc(sizeof(psxd_cue_t));
-}
-
-void psxd_cue_init(psxd_cue_t* cue) {
- memset(cue, 0, sizeof(psxd_cue_t));
-
- cue->buf = malloc(CUE_BUF_SIZE);
- cue->ptr = cue->buf;
- cue->current_file = malloc(CUE_BUF_SIZE);
-}
-
-char* cue_get_directory(const char* path) {
- const char* ptr = &path[strlen(path) - 1];
- char* dir = NULL;
-
- while ((*ptr != '/') && (*ptr != '\\') && (ptr != path))
- ptr--;
-
- // If no directory specified, assume CWD
- if (ptr == path) {
- dir = malloc(3);
-
- strcpy(dir, "./");
-
- return dir;
- }
-
- ptrdiff_t len = (ptr - path) + 1;
-
- dir = malloc(len + 1);
-
- strncpy(dir, path, len);
-
- dir[len] = 0;
-
- return dir;
-}
-
-int psxd_cue_load(psxd_cue_t* cue, const char* path) {
- FILE* file = fopen(path, "rb");
-
- if (!file) {
- log_fatal("Couldn't open file \'%s\'", path);
-
- return 1;
- }
-
- log_fatal("Parsing CUE...");
-
- if (cue_parse(cue, file)) {
- log_fatal("CUE error %s (%u)",
- g_psxd_cue_errors[cue->error],
- cue->error
- );
-
- exit(1);
- }
-
- log_fatal("Loading CD image...");
-
- size_t offset = 0;
-
- char* directory = cue_get_directory(path);
- size_t directory_len = strlen(directory);
-
- for (int i = 0; i < cue->num_tracks; i++) {
- cue_track_t* track = cue->track[i];
-
- int len = strlen(track->filename) + directory_len;
-
- char* full_path = malloc(len + 2);
-
- strcpy(full_path, directory);
- strcat(full_path, track->filename);
-
- log_fatal("Loading track %u at \'%s\'...", i + 1, full_path);
-
- FILE* track_file = fopen(full_path, "rb");
-
- if (ferror(track_file) || !track_file) {
- fclose(track_file);
-
- return 1;
- }
-
- uint32_t data_offset = msf_to_address(track->index[1]);
-
- // Get track size
- fseek(track_file, 0, SEEK_END);
-
- // Account for index 1 offset
- track->size = ftell(track_file);
-
- cue->buf_size += track->size;
-
- // Calculate track MS(F)
- msf_from_address(&track->disc_offset, offset + data_offset);
- msf_add_s(&track->disc_offset, 2);
-
- cue->buf = cue_alloc_block(cue->buf, &offset, track->size);
-
- fseek(track_file, 0, SEEK_SET);
-
- if (!fread(cue->buf + (offset - track->size), 1, track->size, track_file)) {
- perror("Error reading CUE image file data");
-
- exit(1);
- }
-
- fclose(track_file);
- free(full_path);
- }
-
- // Calculate disc end MSF
- msf_from_address(&cue->end, offset);
- msf_add_s(&cue->end, 2);
-
- free(directory);
-
- log_fatal("Loaded CUE image, size=%08x, end=%02u:%02u:%02u",
- cue->buf_size,
- cue->end.m,
- cue->end.s,
- cue->end.f
- );
-
- return 0;
-}
-
-int psxd_cue_seek(void* udata, msf_t msf) {
- psxd_cue_t* cue = udata;
-
- // To-do: Check for OOB seeks
-
- uint32_t sectors = (((msf.m * 60) + msf.s - 2) * CD_SECTORS_PS) + msf.f;
-
- cue->seek_offset = sectors * CD_SECTOR_SIZE;
-
- // log_fatal("CUE seek to %02u:%02u:%02u (%08x < %08x)", msf.m, msf.s, msf.f, cue->seek_offset, cue->buf_size);
-
- if (cue->seek_offset >= cue->buf_size)
- return DISC_ERR_ADDR_OUT_OF_BOUNDS;
-
- return 0;
-}
-
-int psxd_cue_read_sector(void* udata, void* buf) {
- psxd_cue_t* cue = udata;
-
- log_fatal("Reading sector at offset %08x", cue->seek_offset);
-
- memcpy(buf, cue->buf + cue->seek_offset, CD_SECTOR_SIZE);
-
- return 0;
-}
-
-int psxd_cue_get_track_addr(void* udata, msf_t* msf, int track) {
- psxd_cue_t* cue = udata;
-
- track = BTOI(track);
-
- if (track > cue->num_tracks)
- return DISC_ERR_TRACK_OUT_OF_BOUNDS;
-
- if (!msf)
- return 0;
-
- if (!track) {
- msf->m = cue->end.m;
- msf->s = cue->end.s;
- msf->f = 0;
-
- return 0;
- }
-
- msf->m = cue->track[track - 1]->disc_offset.m;
- msf->s = cue->track[track - 1]->disc_offset.s;
- msf->f = 0;
-
- return 0;
-}
-
-int psxd_cue_get_track_count(void* udata, int* count) {
- psxd_cue_t* cue = udata;
-
- *count = ITOB(cue->num_tracks);
-
- return 0;
-}
-
-void psxd_cue_init_disc(psxd_cue_t* cue, psx_disc_t* disc) {
- disc->udata = cue;
- disc->seek_func = psxd_cue_seek;
- disc->read_sector_func = psxd_cue_read_sector;
- disc->get_track_addr_func = psxd_cue_get_track_addr;
- disc->get_track_count_func = psxd_cue_get_track_count;
- disc->destroy_func = (disc_destroy_t)psxd_cue_destroy;
-}
-
-void psxd_cue_destroy(psxd_cue_t* cue) {
- for (int i = 0; i < cue->num_tracks; i++) {
- free(cue->track[i]->filename);
- free(cue->track[i]);
- }
-
- free(cue->track);
- free(cue->current_file);
- free(cue->buf);
- free(cue);
-}
\ No newline at end of file
--- a/psx/disc/cue.h
+++ /dev/null
@@ -1,91 +1,0 @@
-/*
- This file is part of the PSXE Emulator Project
-
- CUE Parser + Loader
-*/
-
-#ifndef CUE_H
-#define CUE_H
-
-#include "../disc.h"
-#include "../msf.h"
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdio.h>
-
-enum {
- PE_EXPECTED_KEYWORD = 1,
- PE_EXPECTED_STRING,
- PE_EXPECTED_NUMBER,
- PE_EXPECTED_COLON,
- PE_NON_SEQUENTIAL_TRACKS,
- PE_UNEXPECTED_TOKEN
-};
-
-enum {
- CUE_4CH = 0,
- CUE_AIFF,
- CUE_AUDIO,
- CUE_BINARY,
- CUE_CATALOG,
- CUE_CDG,
- CUE_CDI_2336,
- CUE_CDI_2352,
- CUE_CDTEXTFILE,
- CUE_DCP,
- CUE_FILE,
- CUE_FLAGS,
- CUE_INDEX,
- CUE_ISRC,
- CUE_MODE1_2048,
- CUE_MODE1_2352,
- CUE_MODE2_2336,
- CUE_MODE2_2352,
- CUE_MOTOROLA,
- CUE_MP3,
- CUE_PERFORMER,
- CUE_POSTGAP,
- CUE_PRE,
- CUE_PREGAP,
- CUE_REM,
- CUE_SCMS,
- CUE_SONGWRITER,
- CUE_TITLE,
- CUE_TRACK,
- CUE_WAVE,
- CUE_NONE = 255
-};
-
-typedef struct {
- char* filename;
- int type;
- void* buf;
- msf_t index[2];
- msf_t disc_offset;
- size_t size;
-} cue_track_t;
-
-typedef struct {
- int preload;
- char* buf;
- char* ptr;
- char c;
- int error;
- FILE* file;
- int num_tracks;
- cue_track_t** track;
- char* current_file;
- char* directory;
- uint32_t seek_offset;
- uint32_t buf_size;
- msf_t end;
-} psxd_cue_t;
-
-psxd_cue_t* psxd_cue_create(void);
-void psxd_cue_init(psxd_cue_t*);
-int psxd_cue_load(psxd_cue_t*, const char*);
-void psxd_cue_init_disc(psxd_cue_t*, psx_disc_t*);
-void psxd_cue_destroy(psxd_cue_t*);
-
-#endif
\ No newline at end of file
--- a/psx/msf.c
+++ /dev/null
@@ -1,86 +1,0 @@
-#include "msf.h"
-#include "log.h"
-
-uint8_t msf_btoi(uint8_t b) {
- return ((b >> 4) * 10) + (b & 0xf);
-}
-
-uint8_t msf_itob(int i) {
- return i + (6 * (i / 10));
-}
-
-void msf_copy(msf_t* dst, msf_t src) {
- dst->m = src.m;
- dst->s = src.s;
- dst->f = src.f;
-}
-
-void msf_adjust(msf_t* msf) {
- if (msf->f > 75) {
- int s = msf->f / CD_SECTORS_PS;
-
- msf->s += s;
- msf->f -= CD_SECTORS_PS * s;
- }
-
- if (msf->s >= 60) {
- int m = msf->s / 60;
-
- msf->m += m;
- msf->s -= 60 * m;
- }
-}
-
-void msf_adjust_sub(msf_t* msf) {
- if ((int)msf->f < 0) {
- int f = ((int)msf->f) * -1;
- int s = (f / 60) + 1;
-
- msf->s -= s;
- msf->f += CD_SECTORS_PS * f;
- }
-
- if ((int)msf->s < 0) {
- int s = ((int)msf->s) * -1;
- int m = (s / 60) + 1;
-
- msf->m -= m;
- msf->s += 60 * m;
- }
-}
-
-void msf_to_bcd(msf_t* msf) {
- msf->m = ITOB(msf->m);
- msf->s = ITOB(msf->s);
- msf->f = ITOB(msf->f);
-}
-
-void msf_from_bcd(msf_t* msf) {
- msf->m = BTOI(msf->m);
- msf->s = BTOI(msf->s);
- msf->f = BTOI(msf->f);
-}
-
-uint32_t msf_to_address(msf_t msf) {
- return (((msf.m * 60) * CD_SECTORS_PS) + (msf.s * 75) + msf.f) * CD_SECTOR_SIZE;
-}
-
-void msf_from_address(msf_t* msf, uint32_t addr) {
- msf->f = addr / CD_SECTOR_SIZE;
- msf->s = msf->f / CD_SECTORS_PS;
- msf->m = msf->s / 60;
- msf->s -= msf->m * 60;
- msf->f -= ((msf->m * 60) + msf->s) * CD_SECTORS_PS;
-}
-
-void msf_add_f(msf_t* msf, int f) {
- msf->f += f;
-
- msf_adjust(msf);
-}
-
-void msf_add_s(msf_t* msf, int s) {
- msf->s += s;
-
- msf_adjust(msf);
-}
\ No newline at end of file
--- a/psx/msf.h
+++ /dev/null
@@ -1,100 +1,0 @@
-#ifndef MSF_H
-#define MSF_H
-
-#include <stdint.h>
-
-#define CD_SECTOR_SIZE 0x930
-#define CD_SECTORS_PS 75
-
-// #define BTOI(b) msf_btoi(b)
-// #define ITOB(b) msf_itob(b)
-#define BTOI(b) g_btoi_table[b]
-#define ITOB(b) g_itob_table[b]
-
-static const uint8_t g_btoi_table[] = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
- 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
- 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
- 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
- 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
- 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
- 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
- 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
- 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
- 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43,
- 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
- 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
- 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
- 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
- 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
- 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61,
- 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
- 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
- 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
- 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
- 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d,
- 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
- 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
- 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
- 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91,
- 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93,
- 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
- 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d,
- 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5,
-};
-
-static const uint8_t g_itob_table[] = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
- 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23,
- 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31,
- 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
- 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
- 0x56, 0x57, 0x58, 0x59, 0x60, 0x61, 0x62, 0x63,
- 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71,
- 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
- 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
- 0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
- 0x96, 0x97, 0x98, 0x99, 0xa0, 0xa1, 0xa2, 0xa3,
- 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xb0, 0xb1,
- 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
- 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
- 0xc8, 0xc9, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5,
- 0xd6, 0xd7, 0xd8, 0xd9, 0xe0, 0xe1, 0xe2, 0xe3,
- 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xf0, 0xf1,
- 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
- 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23,
- 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31,
- 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
- 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
- 0x56, 0x57, 0x58, 0x59, 0x60, 0x61, 0x62, 0x63,
- 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71,
- 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
- 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
- 0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
-};
-
-typedef struct {
- uint32_t m;
- uint32_t s;
- uint32_t f;
-} msf_t;
-
-void msf_copy(msf_t*, msf_t);
-void msf_adjust(msf_t*);
-void msf_adjust_sub(msf_t*);
-void msf_to_bcd(msf_t*);
-void msf_from_bcd(msf_t*);
-uint32_t msf_to_address(msf_t);
-void msf_from_address(msf_t*, uint32_t);
-void msf_add_f(msf_t*, int);
-void msf_add_s(msf_t*, int);
-
-#endif
\ No newline at end of file
--
⑨