# HG changeset patch # User Michael Pavone # Date 1708679798 28800 # Node ID a4f8fa24764b2a6980aef097b6341e58106d5bb6 # Parent cb62730d5c99c9c8cce438031f9505d8571983a1 Initial work on emulating the YMZ263B in the Copera diff -r cb62730d5c99 -r a4f8fa24764b Makefile --- a/Makefile Wed Feb 21 23:42:19 2024 -0800 +++ b/Makefile Fri Feb 23 01:16:38 2024 -0800 @@ -234,13 +234,13 @@ 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 coleco.o pico_pcm.o \ + $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o coleco.o pico_pcm.o ymz263b.o \ segacd.o lc8951.o cdimage.o cdd_mcu.o cd_graphics.o cdd_fader.o sft_mapper.o mediaplayer.o oscilloscope.o LIBOBJS=libblastem.o system.o genesis.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 coleco.o pico_pcm.o $(LIBZOBJS) \ - segacd.o lc8951.o cdimage.o cdd_mcu.o cd_graphics.o cdd_fader.o sft_mapper.o mediaplayer.o + $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o rom.db.o gen_player.o coleco.o pico_pcm.o ymz263b.o \ + segacd.o lc8951.o cdimage.o cdd_mcu.o cd_graphics.o cdd_fader.o sft_mapper.o mediaplayer.o $(LIBZOBJS) ifdef NONUKLEAR CFLAGS+= -DDISABLE_NUKLEAR diff -r cb62730d5c99 -r a4f8fa24764b genesis.c --- a/genesis.c Wed Feb 21 23:42:19 2024 -0800 +++ b/genesis.c Fri Feb 23 01:16:38 2024 -0800 @@ -688,9 +688,15 @@ uint32_t cur_target = gen->psg->cycles + MAX_SOUND_CYCLES; psg_run(gen->psg, cur_target); pico_pcm_run(gen->adpcm, cur_target); + if (gen->ymz) { + ymz263b_run(gen->ymz, cur_target); + } } psg_run(gen->psg, target); pico_pcm_run(gen->adpcm, target); + if (gen->ymz) { + ymz263b_run(gen->ymz, target); + } } static void adjust_int_cycle_pico(m68k_context *context, vdp_context *v_context) @@ -717,13 +723,8 @@ } } - if (mask < 3) { - uint32_t next_pcm_int = pico_pcm_next_int(gen->adpcm); - if (next_pcm_int != CYCLE_NEVER && next_pcm_int < context->int_cycle) { - context->int_cycle = next_pcm_int; - context->int_num = 3; - } - if (mask < 2 && (v_context->regs[REG_MODE_3] & BIT_EINT_EN) && gen->header.type == SYSTEM_GENESIS) { + if (mask < 4) { + if (v_context->regs[REG_MODE_3] & BIT_EINT_EN) { uint32_t next_eint_port0 = io_next_interrupt(gen->io.ports, context->current_cycle); uint32_t next_eint_port1 = io_next_interrupt(gen->io.ports + 1, context->current_cycle); uint32_t next_eint_port2 = io_next_interrupt(gen->io.ports + 2, context->current_cycle); @@ -738,6 +739,21 @@ } } } + if (mask < 3) { + uint32_t next_pcm_int = pico_pcm_next_int(gen->adpcm); + if (next_pcm_int != CYCLE_NEVER && next_pcm_int < context->int_cycle) { + context->int_cycle = next_pcm_int; + context->int_num = 3; + } + + if (mask < 2 && gen->ymz) { + uint32_t ymz_int = ymz263b_next_int(gen->ymz); + if (ymz_int < context->int_cycle) { + context->int_cycle = ymz_int; + context->int_num = 2; + } + } + } } } } @@ -834,6 +850,9 @@ } gen->psg->cycles -= deduction; gen->adpcm->cycle -= deduction; + if (gen->ymz) { + gen->ymz->cycle -= deduction; + } if (gen->reset_cycle != CYCLE_NEVER) { gen->reset_cycle -= deduction; } @@ -906,7 +925,7 @@ static m68k_context *int_ack(m68k_context *context) { genesis_context * gen = context->system; - if ((gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) || context->int_num > 4 || context->int_num < 3) { + if ((gen->header.type != SYSTEM_PICO && gen->header.type != SYSTEM_COPERA) || context->int_num > 3) { vdp_context * v_context = gen->vdp; //printf("acknowledging %d @ %d:%d, vcounter: %d, hslot: %d\n", context->int_ack, context->current_cycle, v_context->cycles, v_context->vcounter, v_context->hslot); vdp_run_context(v_context, context->current_cycle); @@ -1561,8 +1580,28 @@ static uint8_t copera_io_read(uint32_t location, void *vcontext) { - printf("Unhandled Copera 8-bit read %X\n", location); - return 0xFF; + uint8_t ret; + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; + switch (location & 0xFF) + { + case 1: + case 5: + ymz263b_run(gen->ymz, m68k->current_cycle); + ret = ymz263b_status_read(gen->ymz); + printf("Copera YMZ263 Status read - %X: %X\n", location, ret); + adjust_int_cycle_pico(gen->m68k, gen->vdp); + return ret; + case 3: + case 7: + ymz263b_run(gen->ymz, m68k->current_cycle); + ret = ymz263b_data_read(gen->ymz, location & 4); + printf("Copera YMZ263 Data read - %X: %X\n", location, ret); + return ret; + default: + printf("Unhandled Copera 8-bit read %X\n", location); + return 0xFF; + } } static void* copera_io_write_w(uint32_t location, void *vcontext, uint16_t value) @@ -1573,22 +1612,29 @@ static void* copera_io_write(uint32_t location, void *vcontext, uint8_t value) { + m68k_context *m68k = vcontext; + genesis_context *gen = m68k->system; switch (location & 0xFF) { case 1: case 5: + ymz263b_run(gen->ymz, m68k->current_cycle); printf("Copera YMZ263 Address write - %X: %X\n", location, value); + ymz263b_address_write(gen->ymz, value); break; case 3: case 7: + ymz263b_run(gen->ymz, m68k->current_cycle); printf("Copera YMZ263 Channel #%d Data write - %X: %X\n", ((location & 4) >> 2) + 1, location, value); + ymz263b_data_write(gen->ymz, location & 4, value); + adjust_int_cycle_pico(gen->m68k, gen->vdp); break; case 0x24: case 0x34: - printf("Copera YMF263 Address Part #%d write - %X: %X\n", ((location >> 4) & 1) + 1, location, value); + printf("Copera YMF262 Address Part #%d write - %X: %X\n", ((location >> 4) & 1) + 1, location, value); break; case 0x28: - printf("Copera YMF263 Data write - %X: %X\n", location, value); + printf("Copera YMF262 Data write - %X: %X\n", location, value); break; case 0x40: //Bit 4 = SCI @@ -2159,6 +2205,10 @@ if (gen->header.type == SYSTEM_PICO || gen->header.type == SYSTEM_COPERA) { pico_pcm_free(gen->adpcm); free(gen->adpcm); + if (gen->ymz) { + //TODO: call cleanup function once it exists + free(gen->ymz); + } } else { ym_free(gen->ym); } @@ -3125,6 +3175,14 @@ gen->adpcm = calloc(1, sizeof(pico_pcm)); pico_pcm_init(gen->adpcm, gen->master_clock, 42); + if (stype == SYSTEM_COPERA) { + gen->ymz = calloc(1, sizeof(*gen->ymz)); + //This divider is just a guess, PCB diagram in MAME shows no other crystal + //Datasheet says the typical clock is 16.9344 MHz + //Master clock / 3 is 17.897725 MHz which is reasonably close + ymz263b_init(gen->ymz, 3); + } + gen->work_ram = calloc(2, RAM_WORDS); if (!strcmp("random", tern_find_path_default(config, "system\0ram_init\0", (tern_val){.ptrval = "zero"}, TVAL_PTR).ptrval)) { diff -r cb62730d5c99 -r a4f8fa24764b genesis.h --- a/genesis.h Wed Feb 21 23:42:19 2024 -0800 +++ b/genesis.h Fri Feb 23 01:16:38 2024 -0800 @@ -18,6 +18,7 @@ #include "vdp.h" #include "psg.h" #include "pico_pcm.h" +#include "ymz263b.h" #include "io.h" #include "romdb.h" #include "arena.h" @@ -33,6 +34,7 @@ ym2612_context *ym; psg_context *psg; pico_pcm *adpcm; + ymz263b *ymz; uint16_t *cart; uint16_t *lock_on; uint16_t *work_ram; diff -r cb62730d5c99 -r a4f8fa24764b ymz263b.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ymz263b.c Fri Feb 23 01:16:38 2024 -0800 @@ -0,0 +1,276 @@ +#include +#include "ymz263b.h" +#include "backend.h" + +enum { + YMZ_SELT, + YMZ_LSI_TEST, + YMZ_TIMER0_LOW, + YMZ_TIMER0_HIGH, + YMZ_TIMER_BASE, + YMZ_TIMER1, + YMZ_TIMER2_LOW, + YMZ_TIMER2_HIGH, + YMZ_TIMER_CTRL, + YMZ_PCM_PLAY_CTRL, + YMZ_PCM_VOL, + YMZ_PCM_DATA, + YMZ_PCM_CTRL, + YMZ_MIDI_CTRL, + YMZ_MIDI_DATA +}; + +//YMZ_SELT +#define BIT_SELT 0x01 + +//YMZ_TIMER_CTRL +#define BIT_ST0 0x01 +#define BIT_ST1 0x02 +#define BIT_ST2 0x04 +#define BIT_STBC 0x08 +#define BIT_T0_MSK 0x10 +#define BIT_T1_MSK 0x20 +#define BIT_T2_MSK 0x40 + +#define TIMER_RUN_MASK (BIT_ST0|BIT_ST1|BIT_ST2) +#define TIMER_INT_MASK (BIT_T0_MSK|BIT_T1_MSK|BIT_T2_MSK) + +//YMZ_PCM_PLAY_CTRL +#define BIT_ADP_RST 0x80 + +//YMZ_MIDI_CTRL +#define BIT_MSK_RRQ 0x01 +#define BIT_MRC_RST 0x02 +#define BIT_MSK_TRQ 0x04 +#define BIT_MTR_RST 0x08 +#define BIT_MSK_MOV 0x10 +#define BIT_MSK_POV 0x20 + +#define STATUS_FIF1 0x01 +#define STATUS_FIF2 0x02 +#define STATUS_RRQ 0x04 +#define STATUS_TRQ 0x08 +#define STATUS_T0 0x10 +#define STATUS_T1 0x20 +#define STATUS_T2 0x40 +#define STATUS_OV 0x80 + +#define MIDI_BYTE_DIVIDER 170 + +#define FIFO_EMPTY 255 +void ymz263b_init(ymz263b *ymz, uint32_t clock_divider) +{ + memset(ymz, 0, sizeof(*ymz)); + ymz->clock_inc = clock_divider * 32; + ymz->base_regs[YMZ_SELT] = 1; + ymz->pcm[0].regs[0] = BIT_ADP_RST; + ymz->pcm[1].regs[0] = BIT_ADP_RST; + ymz->midi_regs[0] = BIT_MTR_RST | BIT_MRC_RST; + ymz->midi_trs.read = ymz->midi_rcv.read = FIFO_EMPTY; + ymz->status = 0; +} + +static uint8_t fifo_empty(ymz_midi_fifo *fifo) +{ + return fifo->read == FIFO_EMPTY; +} + +static uint8_t fifo_read(ymz_midi_fifo *fifo) +{ + uint8_t ret = fifo->fifo[fifo->read++]; + fifo->read &= 15; + if (fifo->read == fifo->write) { + fifo->read = FIFO_EMPTY; + } + return ret; +} + +static uint8_t fifo_write(ymz_midi_fifo *fifo, uint8_t value) +{ + uint8_t overflow = fifo->read == fifo->write; + if (fifo->read == FIFO_EMPTY) { + fifo->read = fifo->write; + } + fifo->fifo[fifo->write++] = value; + fifo->write &= 15; + return overflow; +} + +static uint8_t fifo_size(ymz_midi_fifo *fifo) +{ + if (fifo->read == FIFO_EMPTY) { + return 0; + } + if (fifo->read == fifo->write) { + return 16; + } + return (fifo->write - fifo->read) & 15; +} + +void ymz263b_run(ymz263b *ymz, uint32_t target_cycle) +{ + uint8_t timer_ctrl = ymz->base_regs[YMZ_TIMER_CTRL]; + for (; ymz->cycle < target_cycle; ymz->cycle += ymz->clock_inc) + { + if (timer_ctrl & BIT_ST0) { + if (ymz->timers[0]) { + ymz->timers[0]--; + } else { + ymz->timers[0] = ymz->base_regs[YMZ_TIMER0_HIGH] << 8 | ymz->base_regs[YMZ_TIMER0_LOW]; + ymz->status |= STATUS_T0; + } + } + if (timer_ctrl & BIT_STBC) { + if (ymz->timers[3]) { + ymz->timers[3]--; + } else { + ymz->timers[3] = ymz->base_regs[YMZ_TIMER1] << 8 & 0xF00; + ymz->timers[3] |= ymz->base_regs[YMZ_TIMER_BASE]; + + if (timer_ctrl & BIT_ST1) { + if (ymz->timers[1]) { + ymz->timers[1]--; + } else { + ymz->timers[1] = ymz->base_regs[YMZ_TIMER1] >> 4; + ymz->status |= STATUS_T1; + } + } + + if (timer_ctrl & BIT_ST2) { + if (ymz->timers[2]) { + ymz->timers[2]--; + } else { + ymz->timers[2] = ymz->base_regs[YMZ_TIMER2_HIGH] << 8 | ymz->base_regs[YMZ_TIMER2_LOW]; + ymz->status |= STATUS_T2; + } + } + } + } + if (!(ymz->midi_regs[0] & BIT_MTR_RST) && !fifo_empty(&ymz->midi_trs)) { + if (ymz->midi_transmit) { + --ymz->midi_transmit; + } else { + ymz->midi_transmit = MIDI_BYTE_DIVIDER - 1; + //TODO: send this byte to MIDI device + uint8_t byte = fifo_read(&ymz->midi_trs); + printf("MIDI Transmit: %X\n", byte); + if (fifo_empty(&ymz->midi_trs)) { + ymz->status |= STATUS_TRQ; + } + } + } + } +} + +uint32_t ymz263b_next_int(ymz263b *ymz) +{ + //TODO: Handle FIFO and MIDI receive interrupts + uint8_t enabled_ints = (~ymz->base_regs[YMZ_TIMER_CTRL]) & TIMER_INT_MASK; + if (!(ymz->base_regs[YMZ_MIDI_CTRL] & (BIT_MTR_RST|BIT_MSK_TRQ))) { + enabled_ints |= STATUS_TRQ; + } + if (!enabled_ints) { + return CYCLE_NEVER; + } + //Handle currently pending interrupts + if (enabled_ints & ymz->status) { + return ymz->cycle; + } + uint32_t ret = CYCLE_NEVER; + if (enabled_ints & STATUS_TRQ) { + uint8_t bytes = fifo_size(&ymz->midi_trs); + if (bytes) { + ret = ymz->cycle + (ymz->midi_transmit + 1) * ymz->clock_inc; + if (bytes > 1) { + ret += MIDI_BYTE_DIVIDER * ymz->clock_inc * (bytes - 1); + } + } + } + enabled_ints >>= 4; + //If timers aren't already expired, interrupts can't fire unless the timers are enabled + enabled_ints &= ymz->base_regs[YMZ_TIMER_CTRL]; + if (!(ymz->base_regs[YMZ_TIMER_CTRL] & BIT_STBC)) { + //Timer 1 and Timer 2 depend on the base timer + enabled_ints &= 1; + } + if (enabled_ints & BIT_ST0) { + uint32_t t0 = ymz->cycle + (ymz->timers[0] + 1) * ymz->clock_inc; + if (t0 < ret) { + ret = t0; + } + } + if (enabled_ints & (BIT_ST1|BIT_ST2)) { + uint32_t base = ymz->cycle + (ymz->timers[3] + 1) * ymz->clock_inc; + if (base < ret) { + uint32_t load = (ymz->base_regs[YMZ_TIMER1] << 8 & 0xF00) | ymz->base_regs[YMZ_TIMER_BASE]; + if (enabled_ints & BIT_ST1) { + uint32_t t1 = ymz->timers[1] * (load + 1) * ymz->clock_inc; + if (t1 < ret) { + ret = t1; + } + } + if (enabled_ints & BIT_ST2) { + uint32_t t2 = ymz->timers[2] * (load + 1) * ymz->clock_inc; + if (t2 < ret) { + ret = t2; + } + } + } + } + return ret; +} + +void ymz263b_address_write(ymz263b *ymz, uint8_t value) +{ + ymz->address = value; +} + +void ymz263b_data_write(ymz263b *ymz, uint32_t channel, uint8_t value) +{ + if (channel) { + if (ymz->address >= YMZ_PCM_PLAY_CTRL && ymz->address < YMZ_MIDI_CTRL) { + ymz->pcm[1].regs[ymz->address - YMZ_PCM_PLAY_CTRL] = value; + } + } else { + if (ymz->address < YMZ_PCM_PLAY_CTRL) { + ymz->base_regs[ymz->address] = value; + } else if (ymz->address < YMZ_MIDI_CTRL) { + ymz->pcm[0].regs[ymz->address - YMZ_PCM_PLAY_CTRL] = value; + } else { + ymz->midi_regs[ymz->address - YMZ_MIDI_CTRL] = value; + if (ymz->address == YMZ_MIDI_DATA) { + ymz->status &= ~STATUS_TRQ; + if (fifo_empty(&ymz->midi_trs)) { + ymz->midi_transmit = MIDI_BYTE_DIVIDER - 1; + } + fifo_write(&ymz->midi_trs, value); + } + } + } +} + +uint8_t ymz263b_data_read(ymz263b *ymz, uint32_t channel) +{ + //TODO: Supposedly only a few registers are actually readable + if (channel) { + if (ymz->address >= YMZ_PCM_PLAY_CTRL && ymz->address < YMZ_MIDI_CTRL) { + return ymz->pcm[1].regs[ymz->address - YMZ_PCM_PLAY_CTRL]; + } + } else { + if (ymz->address < YMZ_PCM_PLAY_CTRL) { + return ymz->base_regs[ymz->address]; + } else if (ymz->address < YMZ_MIDI_CTRL) { + return ymz->pcm[0].regs[ymz->address - YMZ_PCM_PLAY_CTRL]; + } else { + return ymz->midi_regs[ymz->address - YMZ_MIDI_CTRL]; + } + } + return 0XFF; +} + +uint8_t ymz263b_status_read(ymz263b *ymz) +{ + uint8_t ret = ymz->status; + ymz->status = 0;//&= ~(STATUS_T0|STATUS_T1|STATUS_T2); + return ret; +} diff -r cb62730d5c99 -r a4f8fa24764b ymz263b.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ymz263b.h Fri Feb 23 01:16:38 2024 -0800 @@ -0,0 +1,50 @@ +#ifndef YMZ263B_H_ +#define YMZ263B_H_ + +#include +#include "render_audio.h" +#include "oscilloscope.h" + +typedef struct { + uint8_t fifo[128]; + uint8_t fifo_read; + uint8_t fifo_write; + + uint8_t regs[4]; +} ymz263b_pcm; + +typedef struct { + uint8_t fifo[16]; + uint8_t read; + uint8_t write; +} ymz_midi_fifo; + +typedef struct { + audio_source *audio; + oscilloscope *scope; + ymz263b_pcm pcm[2]; + ymz_midi_fifo midi_rcv; + ymz_midi_fifo midi_trs; + + uint32_t clock_inc; + uint32_t cycle; + + uint16_t timers[4]; + + uint16_t status; + + uint8_t base_regs[9]; + uint8_t midi_regs[2]; + uint8_t address; + uint8_t midi_transmit; +} ymz263b; + +void ymz263b_init(ymz263b *ymz, uint32_t clock_divider); +void ymz263b_run(ymz263b *ymz, uint32_t target_cycle); +uint32_t ymz263b_next_int(ymz263b *ymz); +void ymz263b_address_write(ymz263b *ymz, uint8_t value); +void ymz263b_data_write(ymz263b *ymz, uint32_t channel, uint8_t value); +uint8_t ymz263b_data_read(ymz263b *ymz, uint32_t channel); +uint8_t ymz263b_status_read(ymz263b *ymz); + +#endif //YMZ263B_H_