# HG changeset patch # User Michael Pavone # Date 1642825488 28800 # Node ID 70260f6051ddcdb8d2fee9b82a8bf1d4e8d3241a # Parent 88deea42caf009e20d8887297ed3526525879653 Initial work on CDC emulation diff -r 88deea42caf0 -r 70260f6051dd Makefile --- a/Makefile Thu Jan 20 00:56:46 2022 -0800 +++ b/Makefile Fri Jan 21 20:24:48 2022 -0800 @@ -181,7 +181,7 @@ M68KOBJS=68kinst.o ifdef NEW_CORE -Z80OBJS=z80.o z80inst.o +Z80OBJS=z80.o z80inst.o M68KOBJS+= m68k.o CFLAGS+= -DNEW_CORE else @@ -197,7 +197,7 @@ endif endif AUDIOOBJS=ym2612.o psg.o wave.o vgm.o event_log.o render_audio.o -CONFIGOBJS=config.o tern.o util.o paths.o +CONFIGOBJS=config.o tern.o util.o paths.o NUKLEAROBJS=$(FONT) nuklear_ui/blastem_nuklear.o nuklear_ui/sfnt.o RENDEROBJS=ppm.o controller_info.o ifdef USE_FBDEV @@ -205,7 +205,7 @@ else RENDEROBJS+= render_sdl.o endif - + ifdef NOZLIB CFLAGS+= -DDISABLE_ZLIB else @@ -214,12 +214,12 @@ 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 + $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o segacd.o lc8951.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 $(LIBZOBJS) - + $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o rom.db.o gen_player.o segacd.o lc8951.o $(LIBZOBJS) + ifdef NONUKLEAR CFLAGS+= -DDISABLE_NUKLEAR else @@ -278,7 +278,7 @@ blastem$(EXE) : $(MAINOBJS) $(CC) -o $@ $^ $(LDFLAGS) $(PROFFLAGS) $(FIXUP) ./$@ - + blastjag$(EXE) : jaguar.o jag_video.o $(RENDEROBJS) serialize.o $(M68KOBJS) $(TRANSOBJS) $(CONFIGOBJS) $(CC) -o $@ $^ $(LDFLAGS) @@ -287,7 +287,7 @@ dis$(EXE) : dis.o 68kinst.o tern.o vos_program_module.o $(CC) -o $@ $^ $(OPT) - + jagdis : jagdis.o jagcpu.o tern.o $(CC) -o $@ $^ @@ -327,7 +327,7 @@ test_arm : test_arm.o gen_arm.o mem.o gen.o $(CC) -o test_arm test_arm.o gen_arm.o mem.o gen.o - + test_int_timing : test_int_timing.o vdp.o $(CC) -o $@ $^ @@ -354,7 +354,7 @@ %.o : %.c $(CC) $(CFLAGS) -c -o $@ $< - + %.o : %.m $(CC) $(CFLAGS) -c -o $@ $< diff -r 88deea42caf0 -r 70260f6051dd lc8951.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lc8951.c Fri Jan 21 20:24:48 2022 -0800 @@ -0,0 +1,163 @@ +#include "lc8951.h" + +enum { + COMIN, + IFSTAT, + DBCL, + DBCH, + HEAD0, + HEAD1, + HEAD2, + HEAD3, + PTL, + PTH, + WAL, + WAH, + STAT0, + STAT1, + STAT2, + STAT3, + + SBOUT = COMIN, + IFCTRL = IFSTAT, + DACL = HEAD0, + DACH = HEAD1, + DTTRG = HEAD2, + DTACK = HEAD3, + WAL_WRITE = PTL, + WAH_WRITE = PTH, + CTRL0 = WAL, + CTRL1 = WAH, + PTL_WRITE = STAT0, + PTH_WRITE = STAT1, + RESET = STAT3 +}; + +//IFCTRL +#define BIT_CMDIEN 0x80 +#define BIT_DTEIEN 0x40 +#define BIT_CECIEN 0x20 +#define BIT_CMDBK 0x10 +#define BIT_DTWAI 0x08 +#define BIT_STWAI 0x04 +#define BIT_DOUTEN 0x02 +#define BIT_SOUTEN 0x01 + +//IFSTAT +#define BIT_CMDI 0x80 +#define BIT_DTEI 0x40 +#define BIT_DECI 0x20 +#define BIT_DTBSY 0x08 +#define BIT_STBSY 0x04 +#define BIT_DTEN 0x02 +#define BIT_STEN 0x01 + +//datasheet timing info +//3 cycles for memory operation +//6 cycles min for DMA-mode host transfer + +void lc8951_init(lc8951 *context) +{ + //This seems to vary somewhat between Sega CD models + //unclear if the difference is in the lc8951 or gate array + context->regs[IFSTAT] = 0xFF; + context->ar_mask = 0x1F; +} + +void lc8951_reg_write(lc8951 *context, uint8_t value) +{ + switch (context->ar) + { + case SBOUT: + context->regs[context->ar] = value; + if (context->ifctrl & BIT_SOUTEN) { + context->regs[IFSTAT] &= ~BIT_STBSY; + } + break; + case IFCTRL: + context->ifctrl = value; + if (!(value & BIT_SOUTEN)) { + context->regs[IFSTAT] |= BIT_STBSY; + } + if (!(value & BIT_DOUTEN)) { + context->regs[IFSTAT] |= BIT_DTBSY; + } + break; + case DBCL: + context->regs[context->ar] = value; + break; + case DBCH: + context->regs[context->ar] = value & 0xF; + break; + case DACL: + context->dac &= 0xFF00; + context->dac |= value; + break; + case DACH: + context->dac &= 0xFF; + context->dac |= value << 8; + break; + case DTTRG: + if (value & BIT_DOUTEN) { + context->regs[IFSTAT] &= ~BIT_DTBSY; + } + break; + case DTACK: + context->regs[IFSTAT] |= BIT_DTEI; + break; + case WAL_WRITE: + context->regs[WAL] = value; + break; + case WAH_WRITE: + context->regs[WAH] = value; + break; + case PTL_WRITE: + context->regs[PTL] = value; + break; + case PTH_WRITE: + context->regs[PTH] = value; + break; + case RESET: + context->comin_count = 0; + context->regs[IFSTAT] = 0xFF; + break; + default: + break; + } + if (context->ar != SBOUT) { + context->ar++; + context->ar &= context->ar_mask; + } +} + +uint8_t lc8951_reg_read(lc8951 *context) +{ + uint8_t value; + if (context->ar == COMIN) { + if (!context->comin_count) { + return 0xFF; + } + value = context->comin[(context->comin_write - context->comin_count)&sizeof(context->comin)]; + context->comin_count--; + if (!context->comin_count) { + context->regs[IFSTAT] |= BIT_CMDI; + } + return value; + } + if (context->ar == STAT3) { + context->regs[IFSTAT] |= BIT_DECI; + } + if (context->ar >= sizeof(context->regs)) { + value = 0xFF; + } else { + value = context->regs[context->ar]; + } + context->ar++; + context->ar &= context->ar_mask; + return value; +} + +void lc8951_ar_write(lc8951 *context, uint8_t value) +{ + context->ar = value & context->ar_mask; +} diff -r 88deea42caf0 -r 70260f6051dd lc8951.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lc8951.h Fri Jan 21 20:24:48 2022 -0800 @@ -0,0 +1,30 @@ +#ifndef LC8951_H_ +#define LC8951_H_ + +#include + +typedef struct { + uint32_t cycles; + + uint8_t buffer[0x4000]; + + uint8_t regs[16]; + uint8_t comin[8]; + + uint16_t dac; + uint8_t comin_write; + uint8_t comin_count; + uint8_t ifctrl; + uint8_t ctrl0; + uint8_t ctrl1; + uint8_t ar; + uint8_t ar_mask; +} lc8951; + +void lc8951_init(lc8951 *context); +//void lc8951_run(lc8951 *context, uint32_t cycle); +void lc8951_reg_write(lc8951 *context, uint8_t value); +uint8_t lc8951_reg_read(lc8951 *context); +void lc8951_ar_write(lc8951 *context, uint8_t value); + +#endif //LC8951_H_ diff -r 88deea42caf0 -r 70260f6051dd segacd.c --- a/segacd.c Thu Jan 20 00:56:46 2022 -0800 +++ b/segacd.c Fri Jan 21 20:24:48 2022 -0800 @@ -355,6 +355,10 @@ } case GA_MEM_MODE: return cd->gate_array[reg] & 0xFF1F; + case GA_CDC_CTRL: + return cd->gate_array[reg] | cd->cdc.ar; + case GA_CDC_REG_DATA: + return lc8951_reg_read(&cd->cdc); case GA_STOP_WATCH: case GA_TIMER: timers_run(cd, m68k->current_cycle); @@ -457,6 +461,14 @@ cd->gate_array[reg] |= value & (BIT_RET|BIT_MEM_MODE|MASK_PRIORITY); break; } + case GA_CDC_CTRL: + lc8951_ar_write(&cd->cdc, value); + cd->gate_array[reg] &= 0xC000; + cd->gate_array[reg] = value & 0x0700; + break; + case GA_CDC_REG_DATA: + lc8951_reg_write(&cd->cdc, value); + break; case GA_STOP_WATCH: //docs say you should only write zero to reset //mcd-verificator comments suggest any value will reset @@ -517,6 +529,13 @@ //these registers treat all writes as word-wide value16 = value | (value << 8); break; + case GA_CDC_CTRL: + if (address & 1) { + lc8951_ar_write(&cd->cdc, value); + } else { + cd->gate_array[reg] = value << 8; + } + return vcontext; default: if (address & 1) { value16 = cd->gate_array[reg] & 0xFF00 | value; @@ -829,6 +848,7 @@ cd->memptr_start_index = 0; cd->gate_array[1] = 1; cd->gate_array[0x1B] = 0x100; + lc8951_init(&cd->cdc); return cd; } diff -r 88deea42caf0 -r 70260f6051dd segacd.h --- a/segacd.h Thu Jan 20 00:56:46 2022 -0800 +++ b/segacd.h Fri Jan 21 20:24:48 2022 -0800 @@ -2,6 +2,7 @@ #define SEGACD_H_ #include #include "genesis.h" +#include "lc8951.h" typedef struct { m68k_context *m68k; @@ -25,6 +26,7 @@ uint8_t reset; uint8_t need_reset; uint8_t memptr_start_index; + lc8951 cdc; } segacd_context; segacd_context *alloc_configure_segacd(system_media *media, uint32_t opts, uint8_t force_region, rom_info *info);