# HG changeset patch # User Michael Pavone # Date 1643272421 28800 # Node ID 7c1760b5b3e5885c8d8f76ea210c9fff6d791c3c # Parent f1c2415f4d1dee2cc53cd3deeba54f4923c15193 Implemented basic TOC functionality of CDD MCU diff -r f1c2415f4d1d -r 7c1760b5b3e5 Makefile --- a/Makefile Sun Jan 23 11:11:07 2022 -0800 +++ b/Makefile Thu Jan 27 00:33:41 2022 -0800 @@ -214,11 +214,11 @@ MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o $(RENDEROBJS) io.o romdb.o hash.o menu.o xband.o \ realtec.o i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ - $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o segacd.o lc8951.o cue.o + $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o segacd.o lc8951.o cue.o cdd_mcu.o LIBOBJS=libblastem.o system.o genesis.o debug.o gdb_remote.o vdp.o io.o romdb.o hash.o xband.o realtec.o \ i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ - $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o rom.db.o gen_player.o segacd.o lc8951.o $(LIBZOBJS) + $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o rom.db.o gen_player.o segacd.o lc8951.o cue.o cdd.o cdd_mcu.o $(LIBZOBJS) ifdef NONUKLEAR CFLAGS+= -DDISABLE_NUKLEAR diff -r f1c2415f4d1d -r 7c1760b5b3e5 blastem.c --- a/blastem.c Sun Jan 23 11:11:07 2022 -0800 +++ b/blastem.c Thu Jan 27 00:33:41 2022 -0800 @@ -239,7 +239,9 @@ romclose(f); if (!strcasecmp(dst->extension, "cue")) { if (parse_cue(dst)) { - *stype = SYSTEM_SEGACD; + if (stype) { + *stype = SYSTEM_SEGACD; + } } } diff -r f1c2415f4d1d -r 7c1760b5b3e5 cdd_mcu.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cdd_mcu.c Thu Jan 27 00:33:41 2022 -0800 @@ -0,0 +1,403 @@ +#include "cdd_mcu.h" +#include "backend.h" + +#define SCD_MCLKS 50000000 +#define CD_BLOCK_CLKS 16934400 +#define CDD_MCU_DIVIDER 8 +#define SECTOR_CLOCKS (CD_BLOCK_CLKS/75) +#define NIBBLE_CLOCKS (CDD_MCU_DIVIDER * 77) + +//lead in start max diameter 46 mm +//program area start max diameter 50 mm +//difference 4 mm = 4000 um +//radius difference 2 mm = 2000 um +//track pitch 1.6 um +//1250 physical tracks in between +//linear speed 1.2 m/s - 1.4 m/s +// 1.3 m = 1300 mm +// circumference at 46 mm ~ 144.51 mm +// circumference at 50 mm ~ 157.08 mm +// avg is 150.795 +// 75 sectors per second +// 17.3333 mm "typical" length of a sector +// ~8.7 sectors per track in lead-in area +#define LEADIN_SECTORS 10875 + +static uint32_t cd_block_to_mclks(uint32_t cycles) +{ + return ((uint64_t)cycles) * ((uint64_t)SCD_MCLKS) / ((uint64_t)CD_BLOCK_CLKS); +} + +static uint32_t mclks_to_cd_block(uint32_t cycles) +{ + return ((uint64_t)cycles) * ((uint64_t)CD_BLOCK_CLKS) / ((uint64_t)SCD_MCLKS); +} + +void cdd_mcu_init(cdd_mcu *context, system_media *media) +{ + context->next_int_cycle = CYCLE_NEVER; + context->last_subcode_cycle = CYCLE_NEVER; + context->last_nibble_cycle = CYCLE_NEVER; + context->requested_format = SF_NOTREADY; + context->media = media; + context->current_status_nibble = -1; + context->current_cmd_nibble = -1; +} + +enum { + GAO_CDD_CTRL, + GAO_CDD_STATUS, + GAO_CDD_CMD = GAO_CDD_STATUS+5 +}; + +static uint8_t checksum(uint8_t *vbuffer) +{ + uint8_t *buffer = vbuffer; + uint8_t sum = 0; + for (int i = 0; i < 9; i++) + { + sum += buffer[i]; + } + return (~sum) & 0xF; +} +#define SEEK_SPEED 2200 //made up number +static void handle_seek(cdd_mcu *context) +{ + if (context->seeking) { + if (context->seek_pba == context->head_pba) { + context->seeking = 0; + } else if (context->seek_pba > context->head_pba) { + context->head_pba += SEEK_SPEED; + if (context->head_pba > context->seek_pba) { + context->head_pba = context->seek_pba; + } + } else { + context->head_pba -= SEEK_SPEED; + if (context->head_pba < context->seek_pba) { + context->head_pba = context->seek_pba; + } + } + } +} + +static void lba_to_status(cdd_mcu *context, uint32_t lba) +{ + uint32_t seconds = lba / 75; + uint32_t frames = lba % 75; + uint32_t minutes = seconds / 60; + seconds = seconds % 60; + context->status_buffer.b.time.min_high = minutes / 10; + context->status_buffer.b.time.min_low = minutes % 10; + context->status_buffer.b.time.sec_high = seconds / 10; + context->status_buffer.b.time.sec_low = seconds % 10; + context->status_buffer.b.time.frame_high = frames / 10; + context->status_buffer.b.time.frame_low = frames % 10; +} + +static void update_status(cdd_mcu *context) +{ + switch (context->status) + { + case DS_PLAY: + handle_seek(context); + if (!context->seeking) { + context->head_pba++; + } + break; + case DS_PAUSE: + handle_seek(context); + break; + case DS_TOC_READ: + handle_seek(context); + if (!context->seeking) { + context->head_pba++; + if (context->media && context->media->type == MEDIA_CDROM && context->media->num_tracks) { + if (context->head_pba > 3*context->media->num_tracks + 1) { + context->toc_valid = 1; + context->seeking = 1; + context->seek_pba = LEADIN_SECTORS + context->media->tracks[0].start_lba + context->media->tracks[0].fake_pregap; + context->status = DS_PAUSE; + } + + } else { + context->status = DS_NO_DISC; + } + } + break; + + } + switch (context->requested_format) + { + case SF_ABSOLUTE: + if (context->toc_valid) { + lba_to_status(context, context->head_pba - LEADIN_SECTORS); + context->status_buffer.format = SF_ABSOLUTE; + } else { + context->status_buffer.format = SF_NOTREADY; + } + break; + case SF_RELATIVE: + if (context->toc_valid) { + uint32_t lba =context->head_pba - LEADIN_SECTORS; + for (uint32_t i = 0; i < context->media->num_tracks; i++) + { + if (lba < context->media->tracks[i].end_lba) { + if (context->media->tracks[i].fake_pregap) { + if (lba > context->media->tracks[i].fake_pregap) { + lba -= context->media->tracks[i].fake_pregap; + } else { + //relative time counts down to 0 in pregap + lba = context->media->tracks[i].fake_pregap - lba; + break; + } + } + if (lba < context->media->tracks[i].start_lba) { + //relative time counts down to 0 in pregap + lba = context->media->tracks[i].start_lba - lba; + } else { + lba -= context->media->tracks[i].start_lba; + } + break; + } else if (context->media->tracks[i].fake_pregap) { + lba -= context->media->tracks[i].fake_pregap; + } + } + lba_to_status(context, lba); + context->status_buffer.format = SF_ABSOLUTE; + } else { + context->status_buffer.format = SF_NOTREADY; + } + break; + case SF_TOCO: + if (context->toc_valid) { + lba_to_status(context, context->media->tracks[context->media->num_tracks - 1].end_lba + context->media->tracks[0].fake_pregap); + context->status_buffer.format = SF_TOCO; + } else { + context->status_buffer.format = SF_NOTREADY; + } + break; + case SF_TOCT: + if (context->toc_valid) { + context->status_buffer.b.toct.first_track_high = 0; + context->status_buffer.b.toct.first_track_low = 1; + context->status_buffer.b.toct.last_track_high = (context->media->num_tracks + 1) / 10; + context->status_buffer.b.toct.last_track_low = (context->media->num_tracks + 1) % 10; + context->status_buffer.b.toct.version = 0; + context->status_buffer.format = SF_TOCT; + } else { + context->status_buffer.format = SF_NOTREADY; + } + break; + case SF_TOCN: + if (context->toc_valid) { + lba_to_status(context, context->media->tracks[context->requested_track].start_lba + context->media->tracks[0].fake_pregap); + context->status_buffer.b.tocn.track_low = context->requested_track % 10; + context->status_buffer.format = SF_TOCN; + } else { + context->status_buffer.format = SF_NOTREADY; + } + break; + case SF_NOTREADY: + memset(&context->status_buffer, 0, sizeof(context->status_buffer) - 1); + context->status_buffer.format = SF_NOTREADY; + break; + } + if (context->error_status == DS_STOP) { + context->status_buffer.status = context->status; + } else { + context->status_buffer.status = context->error_status; + context->error_status = DS_STOP; + } + if (context->requested_format != SF_TOCN) { + context->status_buffer.b.time.flags = 0; //TODO: populate these + } + context->status_buffer.checksum = checksum((uint8_t *)&context->status_buffer); + if (context->status_buffer.format != SF_NOTREADY) { + printf("CDD Status %d%d.%d%d%d%d%d%d.%d%d\n", + context->status_buffer.status, context->status_buffer.format, + context->status_buffer.b.time.min_high, context->status_buffer.b.time.min_low, + context->status_buffer.b.time.sec_high, context->status_buffer.b.time.sec_low, + context->status_buffer.b.time.frame_high, context->status_buffer.b.time.frame_low, + context->status_buffer.b.time.flags, context->status_buffer.checksum + ); + } +} + +static void run_command(cdd_mcu *context) +{ + uint8_t check = checksum((uint8_t*)&context->cmd_buffer); + if (check != context->cmd_buffer.checksum) { + context->error_status = DS_SUM_ERROR; + return; + } + if (context->cmd_buffer.must_be_zero) { + context->error_status = DS_CMD_ERROR; + return; + } + switch (context->cmd_buffer.cmd_type) + { + case CMD_NOP: + break; + case CMD_STOP: + puts("CDD CMD: STOP"); + context->status = DS_STOP; + break; + case CMD_REPORT_REQUEST: + switch (context->cmd_buffer.b.format.status_type) + { + case SF_ABSOLUTE: + case SF_RELATIVE: + case SF_TRACK: + context->requested_format = context->cmd_buffer.b.format.status_type; + break; + case SF_TOCO: + if (context->toc_valid) { + context->requested_format = SF_TOCO; + } else { + context->error_status = DS_CMD_ERROR; + context->requested_format = SF_ABSOLUTE; + } + break; + case SF_TOCT: + if (context->toc_valid) { + if (context->status == DS_STOP) { + context->status = DS_TOC_READ; + context->seeking = 1; + context->seek_pba = 0; + } + } else { + context->status = DS_TOC_READ; + context->seeking = 1; + context->seek_pba = 0; + } + context->requested_format = SF_TOCT; + break; + case SF_TOCN: + context->requested_track = context->cmd_buffer.b.format.track_high * 10; + context->requested_track += context->cmd_buffer.b.format.track_low; + context->status = DS_TOC_READ; + context->seeking = 1; + context->seek_pba = 0; + context->requested_format = SF_TOCN; + break; + } + printf("CDD CMD: REPORT REQUEST(%d), format set to %d\n", context->cmd_buffer.b.format.status_type, context->requested_format); + break; + default: + printf("CDD CMD: Unimplemented(%d)\n", context->cmd_buffer.cmd_type); + } +} + +#define BIT_HOCK 0x4 +#define BIT_DRS 0x2 +#define BIT_DTS 0x1 + +void cdd_mcu_run(cdd_mcu *context, uint32_t cycle, uint16_t *gate_array) +{ + uint32_t cd_cycle = mclks_to_cd_block(cycle); + if (!(gate_array[GAO_CDD_CTRL] & BIT_HOCK)) { + //it's a little unclear if this gates the actual cd block clock or just handshaking + //assum it's actually the clock for now + context->cycle = cycle; + return; + } + uint32_t next_subcode = context->last_subcode_cycle + SECTOR_CLOCKS; + uint32_t next_nibble = context->current_status_nibble >= 0 ? context->last_nibble_cycle + NIBBLE_CLOCKS : CYCLE_NEVER; + uint32_t next_cmd_nibble = context->current_cmd_nibble >= 0 ? context->last_nibble_cycle + NIBBLE_CLOCKS : CYCLE_NEVER; + for (; context->cycle < cd_cycle; context->cycle += CDD_MCU_DIVIDER) + { + if (context->cycle >= next_subcode) { + context->last_subcode_cycle = context->cycle; + next_subcode = context->cycle + SECTOR_CLOCKS; + update_status(context); + next_nibble = context->cycle; + context->current_status_nibble = 0; + gate_array[GAO_CDD_STATUS] |= BIT_DRS; + } + if (context->cycle >= next_nibble) { + if (context->current_status_nibble == sizeof(cdd_status)) { + context->current_status_nibble = -1; + gate_array[GAO_CDD_STATUS] &= ~BIT_DRS; + if (context->cmd_recv_pending) { + context->cmd_recv_pending = 0; + context->current_cmd_nibble = 0; + gate_array[GAO_CDD_STATUS] |= BIT_DTS; + next_nibble = context->cycle + NIBBLE_CLOCKS; + } else { + context->cmd_recv_wait = 1; + next_nibble = CYCLE_NEVER; + } + } else { + uint8_t value = ((uint8_t *)&context->status_buffer)[context->current_status_nibble]; + int ga_index = GAO_CDD_STATUS + (context->current_status_nibble >> 1); + if (context->current_status_nibble & 1) { + gate_array[ga_index] = value | (gate_array[ga_index] & 0xFF00); + } else { + gate_array[ga_index] = (value << 8) | (gate_array[ga_index] & 0x00FF); + } + if (context->current_status_nibble == 7) { + context->int_pending = 1; + context->next_int_cycle = cd_block_to_mclks(cycle + SECTOR_CLOCKS); + } + context->current_status_nibble++; + context->last_nibble_cycle = context->cycle; + next_nibble = context->cycle + NIBBLE_CLOCKS; + } + } else if (context->cycle >= next_cmd_nibble) { + if (context->current_cmd_nibble == sizeof(cdd_cmd)) { + next_cmd_nibble = CYCLE_NEVER; + context->current_cmd_nibble = -1; + gate_array[GAO_CDD_STATUS] &= ~BIT_DTS; + run_command(context); + } else { + int ga_index = GAO_CDD_CMD + (context->current_cmd_nibble >> 1); + uint8_t value = (context->current_cmd_nibble & 1) ? gate_array[ga_index] : gate_array[ga_index] >> 8; + ((uint8_t *)&context->cmd_buffer)[context->current_cmd_nibble] = value; + context->current_cmd_nibble++; + context->last_nibble_cycle = context->cycle; + next_cmd_nibble = context->cycle + NIBBLE_CLOCKS; + } + } + } +} + +void cdd_mcu_start_cmd_recv(cdd_mcu *context, uint16_t *gate_array) +{ + if (context->cmd_recv_wait) { + context->current_cmd_nibble = 0; + gate_array[GAO_CDD_STATUS] |= BIT_DTS; + context->last_nibble_cycle = context->cycle; + context->cmd_recv_wait = 0; + } else { + context->cmd_recv_pending = 1; + } +} + +void cdd_hock_enabled(cdd_mcu *context) +{ + context->last_subcode_cycle = context->cycle; + context->next_int_cycle = cd_block_to_mclks(context->cycle + SECTOR_CLOCKS + 7 * NIBBLE_CLOCKS); +} + +void cdd_hock_disabled(cdd_mcu *context) +{ + context->last_subcode_cycle = CYCLE_NEVER; + context->next_int_cycle = CYCLE_NEVER; + context->last_nibble_cycle = CYCLE_NEVER; + context->current_status_nibble = -1; + context->current_cmd_nibble = -1; +} + +void cdd_mcu_adjust_cycle(cdd_mcu *context, uint32_t deduction) +{ + uint32_t cd_deduction = mclks_to_cd_block(deduction); + if (context->next_int_cycle != CYCLE_NEVER) { + context->next_int_cycle -= deduction; + } + if (context->last_subcode_cycle != CYCLE_NEVER) { + context->last_subcode_cycle -= cd_deduction; + } + if (context->last_nibble_cycle != CYCLE_NEVER) { + context->last_nibble_cycle -= cd_deduction; + } +} diff -r f1c2415f4d1d -r 7c1760b5b3e5 cdd_mcu.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cdd_mcu.h Thu Jan 27 00:33:41 2022 -0800 @@ -0,0 +1,164 @@ +#ifndef CDD_MCU_H_ +#define CDD_MCU_H_ +#include "system.h" + + +typedef enum { + SF_ABSOLUTE, + SF_RELATIVE, + SF_TRACK, + SF_TOCO, + SF_TOCT, + SF_TOCN, + SF_E, + SF_NOTREADY = 0xF +} status_format; + +typedef enum { + CMD_NOP, + CMD_STOP, + CMD_REPORT_REQUEST, + CMD_READ, + CMD_SEEK, + CMD_INVALID, + CMD_PAUSE, + CMD_PLAY, + CMD_FFWD, + CMD_RWD, + CMD_TRACK_SKIP, + CMD_TRACK_CUE, + CMD_DOOR_CLOSE, + CMD_DOOR_OPEN +} host_cmd; + +typedef enum { + DS_STOP, + DS_PLAY, + DS_SEEK, + DS_SCAN, + DS_PAUSE, + DS_DOOR_OPEN, + DS_SUM_ERROR, + DS_CMD_ERROR, + DS_FUNC_ERROR, + DS_TOC_READ, + DS_TRACKING, + DS_NO_DISC, + DS_DISC_LEADOUT, + DS_DISC_LEADIN, + DS_TRAY_MOVING, +} drive_status; + + +typedef struct { + uint8_t status; + uint8_t format; + union { + struct { + uint8_t min_high; + uint8_t min_low; + uint8_t sec_high; + uint8_t sec_low; + uint8_t frame_high; + uint8_t frame_low; + uint8_t flags; + } time; + struct { + uint8_t track_high; + uint8_t track_low; + uint8_t padding0; + uint8_t padding1; + uint8_t control; + uint8_t padding2; + uint8_t flags; + } track; + struct { + uint8_t first_track_high; + uint8_t first_track_low; + uint8_t last_track_high; + uint8_t last_track_low; + uint8_t version; + uint8_t padding; + uint8_t flags; + } toct; + struct { + uint8_t min_high; + uint8_t min_low; + uint8_t sec_high; + uint8_t sec_low; + uint8_t frame_high; + uint8_t frame_low; + uint8_t track_low; + } tocn; + struct { + } error; + } b; + uint8_t checksum; +} cdd_status; + +typedef struct { + uint8_t cmd_type; + uint8_t must_be_zero; + union { + struct { + uint8_t min_high; + uint8_t min_low; + uint8_t sec_high; + uint8_t sec_low; + uint8_t frame_high; + uint8_t frame_low; + } time; + struct { + uint8_t padding; + uint8_t status_type; + uint8_t track_high; + uint8_t track_low; + } format; + struct { + uint8_t padding; + uint8_t direction; + uint8_t tracks_highest; + uint8_t tracks_midhigh; + uint8_t tracks_midlow; + uint8_t tracks_lowest; + } skip; + struct { + uint8_t track_high; + uint8_t track_low; + } track; + } b; + uint8_t padding; + uint8_t checksum; +} cdd_cmd; + +typedef struct { + system_media *media; + uint32_t cycle; //this is in CD block CLKS + uint32_t next_int_cycle; //this is in SCD MCLKS + uint32_t last_subcode_cycle; + uint32_t last_nibble_cycle; + int current_status_nibble; + int current_cmd_nibble; + uint32_t head_pba; + uint32_t seek_pba; + cdd_status status_buffer; + cdd_cmd cmd_buffer; + status_format requested_format; + drive_status status; + drive_status error_status; + uint8_t requested_track; + uint8_t cmd_recv_wait; + uint8_t cmd_recv_pending; + uint8_t int_pending; + uint8_t toc_valid; + uint8_t seeking; +} cdd_mcu; + +void cdd_mcu_init(cdd_mcu *context, system_media *media); +void cdd_mcu_run(cdd_mcu *context, uint32_t cycle, uint16_t *gate_array); +void cdd_hock_enabled(cdd_mcu *context); +void cdd_hock_disabled(cdd_mcu *context); +void cdd_mcu_start_cmd_recv(cdd_mcu *context, uint16_t *gate_array); +void cdd_mcu_adjust_cycle(cdd_mcu *context, uint32_t deduction); + +#endif //CD_MCU_H_ diff -r f1c2415f4d1d -r 7c1760b5b3e5 cue.c --- a/cue.c Sun Jan 23 11:11:07 2022 -0800 +++ b/cue.c Thu Jan 27 00:33:41 2022 -0800 @@ -144,5 +144,7 @@ fseek(media->f, 16, SEEK_SET); media->size = fread(media->buffer, 1, 2048, media->f); } - return tracks > 0 && media->f != NULL; + uint8_t valid = tracks > 0 && media->f != NULL; + media->type = valid ? MEDIA_CDROM : MEDIA_CART; + return valid; } diff -r f1c2415f4d1d -r 7c1760b5b3e5 segacd.c --- a/segacd.c Sun Jan 23 11:11:07 2022 -0800 +++ b/segacd.c Thu Jan 27 00:33:41 2022 -0800 @@ -81,6 +81,9 @@ #define BIT_MASK_IEN5 0x0020 #define BIT_MASK_IEN6 0x0040 +//GA_CDD_CTRL +#define BIT_HOCK 0x0004 + static void *prog_ram_wp_write16(uint32_t address, void *vcontext, uint16_t value) { m68k_context *m68k = vcontext; @@ -308,19 +311,28 @@ segacd_context *cd = context->system; context->int_cycle = CYCLE_NEVER; uint8_t mask = context->status & 0x7; - if (mask < 3) { - uint32_t next_timer; - if (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN3) { - uint32_t next_timer_cycle = next_timer_int(cd); - if (next_timer_cycle < context->int_cycle) { - context->int_cycle = next_timer_cycle; - context->int_num = 3; + if (mask < 4) { + if (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN4) { + uint32_t cdd_cycle = cd->cdd.int_pending ? context->current_cycle : cd->cdd.next_int_cycle; + if (cdd_cycle < context->int_cycle) { + context->int_cycle = cdd_cycle; + context->int_num = 4; } } - if (mask < 2) { - if (cd->int2_cycle < context->int_cycle && (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN2)) { - context->int_cycle = cd->int2_cycle; - context->int_num = 2; + if (mask < 3) { + uint32_t next_timer; + if (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN3) { + uint32_t next_timer_cycle = next_timer_int(cd); + if (next_timer_cycle < context->int_cycle) { + context->int_cycle = next_timer_cycle; + context->int_num = 3; + } + } + if (mask < 2) { + if (cd->int2_cycle < context->int_cycle && (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN2)) { + context->int_cycle = cd->int2_cycle; + context->int_num = 2; + } } } } @@ -363,6 +375,14 @@ case GA_TIMER: timers_run(cd, m68k->current_cycle); return cd->gate_array[reg]; + case GA_CDD_STATUS0: + case GA_CDD_STATUS1: + case GA_CDD_STATUS2: + case GA_CDD_STATUS3: + case GA_CDD_STATUS4: + cdd_mcu_run(&cd->cdd, m68k->current_cycle, cd->gate_array + GA_CDD_CTRL); + return cd->gate_array[reg]; + break; case GA_FONT_DATA0: case GA_FONT_DATA1: case GA_FONT_DATA2: @@ -499,6 +519,33 @@ cd->gate_array[reg] = value & (BIT_MASK_IEN6|BIT_MASK_IEN5|BIT_MASK_IEN4|BIT_MASK_IEN3|BIT_MASK_IEN2|BIT_MASK_IEN1); calculate_target_cycle(m68k); break; + case GA_CDD_CTRL: { + cdd_mcu_run(&cd->cdd, m68k->current_cycle, cd->gate_array + GA_CDD_CTRL); + uint16_t changed = cd->gate_array[reg] ^ value; + cd->gate_array[reg] &= ~BIT_HOCK; + cd->gate_array[reg] |= value & BIT_HOCK; + if (changed & BIT_HOCK) { + if (value & BIT_HOCK) { + cdd_hock_enabled(&cd->cdd); + } else { + cdd_hock_disabled(&cd->cdd); + } + calculate_target_cycle(m68k); + } + break; + } + case GA_CDD_CMD0: + case GA_CDD_CMD1: + case GA_CDD_CMD2: + case GA_CDD_CMD3: + cdd_mcu_run(&cd->cdd, m68k->current_cycle, cd->gate_array + GA_CDD_CTRL); + cd->gate_array[reg] = value & 0x0F0F; + break; + case GA_CDD_CMD4: + cdd_mcu_run(&cd->cdd, m68k->current_cycle, cd->gate_array + GA_CDD_CTRL); + cd->gate_array[reg] = value & 0x0F0F; + cdd_mcu_start_cmd_recv(&cd->cdd, cd->gate_array + GA_CDD_CTRL); + break; case GA_FONT_COLOR: cd->gate_array[reg] = value & 0xFF; break; @@ -536,6 +583,15 @@ cd->gate_array[reg] = value << 8; } return vcontext; + case GA_CDD_CMD4: + if (!address) { + //byte write to $FF804A should not trigger transfer + cdd_mcu_run(&cd->cdd, m68k->current_cycle, cd->gate_array + GA_CDD_CTRL); + cd->gate_array[reg] &= 0x0F; + cd->gate_array[reg] |= (value << 8 & 0x0F00); + return vcontext; + } + //intentional fallthrough for $FF804B default: if (address & 1) { value16 = cd->gate_array[reg] & 0xFF00 | value; @@ -555,6 +611,7 @@ static void scd_peripherals_run(segacd_context *cd, uint32_t cycle) { timers_run(cd, cycle); + cdd_mcu_run(&cd->cdd, cycle, cd->gate_array + GA_CDD_CTRL); } static m68k_context *sync_components(m68k_context * context, uint32_t address) @@ -569,6 +626,8 @@ case 3: cd->timer_pending = 0; break; + case 4: + cd->cdd.int_pending = 0; } context->int_ack = 0; calculate_target_cycle(context); @@ -613,6 +672,7 @@ } else if (cd->periph_reset_cycle != CYCLE_NEVER) { cd->periph_reset_cycle -= deduction; } + cdd_mcu_adjust_cycle(&cd->cdd, deduction); } static uint16_t main_gate_read16(uint32_t address, void *vcontext) @@ -849,6 +909,10 @@ cd->gate_array[1] = 1; cd->gate_array[0x1B] = 0x100; lc8951_init(&cd->cdc); + if (media->chain && media->type != MEDIA_CDROM) { + media = media->chain; + } + cdd_mcu_init(&cd->cdd, media); return cd; } diff -r f1c2415f4d1d -r 7c1760b5b3e5 segacd.h --- a/segacd.h Sun Jan 23 11:11:07 2022 -0800 +++ b/segacd.h Thu Jan 27 00:33:41 2022 -0800 @@ -3,6 +3,7 @@ #include #include "genesis.h" #include "lc8951.h" +#include "cdd_mcu.h" typedef struct { m68k_context *m68k; @@ -27,6 +28,7 @@ uint8_t need_reset; uint8_t memptr_start_index; lc8951 cdc; + cdd_mcu cdd; } segacd_context; segacd_context *alloc_configure_segacd(system_media *media, uint32_t opts, uint8_t force_region, rom_info *info);