# HG changeset patch # User Michael Pavone # Date 1643601333 28800 # Node ID 8e51c0c3f2e3507b4b7bc5a48bb45f80c0ae57de # Parent f573f2c31bc9f533f4d98d916099ce741ae445d4 Initial attempt at implementing the Sega CD graphics hardware diff -r f573f2c31bc9 -r 8e51c0c3f2e3 Makefile --- a/Makefile Sun Jan 30 11:58:34 2022 -0800 +++ b/Makefile Sun Jan 30 19:55:33 2022 -0800 @@ -214,11 +214,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 segacd.o lc8951.o cue.o cdd_mcu.o + $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o \ + segacd.o lc8951.o cue.o cdd_mcu.o cd_graphics.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 cue.o cdd.o cdd_mcu.o $(LIBZOBJS) + $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o rom.db.o gen_player.o $(LIBZOBJS) \ + segacd.o lc8951.o cue.o cdd_mcu.o cd_graphics.o ifdef NONUKLEAR CFLAGS+= -DDISABLE_NUKLEAR diff -r f573f2c31bc9 -r 8e51c0c3f2e3 cd_graphics.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cd_graphics.c Sun Jan 30 19:55:33 2022 -0800 @@ -0,0 +1,280 @@ +#include "cd_graphics.h" +#include "backend.h" + +void cd_graphics_init(segacd_context *cd) +{ + cd->graphics_int_cycle = CYCLE_NEVER; +} + +#define BIT_HFLIP 0x8000 + +static uint8_t get_src_pixel(segacd_context *cd) +{ + uint16_t x = cd->graphics_x >> 11; + uint16_t y = cd->graphics_y >> 11; + cd->graphics_x += cd->graphics_dx; + cd->graphics_x &= 0xFFFFFF; + cd->graphics_y += cd->graphics_dy; + cd->graphics_y &= 0xFFFFFF; + uint16_t stamp_shift, pixel_mask; + uint16_t stamp_num_mask; + if (cd->gate_array[GA_STAMP_SIZE] & BIT_STS) { + //32x32 stamps + stamp_shift = 5; + pixel_mask = 0x1F; + stamp_num_mask = 0x3FC; + } else { + //16x16 stamps + stamp_shift = 4; + pixel_mask = 0xF; + stamp_num_mask = 0x3FF; + } + uint16_t stamp_x = x >> stamp_shift; + uint16_t stamp_y = y >> stamp_shift; + uint16_t max, base_mask; + uint32_t row_shift; + if (cd->gate_array[GA_STAMP_SIZE] & BIT_SMS) { + max = 4096 >> stamp_shift; + base_mask = 0xE000 << ((5 - stamp_shift) << 1); + //128 stamps in 32x32 mode, 256 stamps in 16x16 mode + row_shift = 12 - stamp_shift; + } else { + max = 256 >> stamp_shift; + base_mask = 0xFFE0 << ((5 - stamp_shift) << 1); + //8 stamps in 32x32 mode, 16 stamps in 16x16 mode + row_shift = 8 - stamp_shift; + } + if (stamp_x > max || stamp_y > max) { + if (cd->gate_array[GA_STAMP_SIZE] & BIT_RPT) { + stamp_x &= max - 1; + stamp_y &= max - 1; + } else { + return 0; + } + } + uint32_t address = (cd->gate_array[GA_STAMP_MAP_BASE] & base_mask) << 1; + address += (stamp_y << row_shift) + stamp_x; + uint16_t stamp_def = cd->word_ram[address]; + uint16_t stamp_num = stamp_def & stamp_num_mask; + uint16_t pixel_x = x & pixel_mask; + uint16_t pixel_y = y & pixel_mask; + if (stamp_def & BIT_HFLIP) { + pixel_x = pixel_mask - pixel_x; + } + uint16_t tmp; + switch (stamp_def >> 13 & 3) + { + case 0: + break; + case 1: + tmp = pixel_y; + pixel_y = pixel_x; + pixel_x = pixel_mask - tmp; + break; + case 2: + tmp = pixel_y; + pixel_y = pixel_mask - pixel_x; + pixel_x = pixel_mask - tmp; + break; + case 3: + tmp = pixel_y; + pixel_y = pixel_mask - pixel_x; + pixel_x = tmp; + break; + } + uint16_t cell_x = pixel_x >> 4; + uint32_t pixel_address = stamp_num << 6; + pixel_address += (pixel_y << 1) + (cell_x << (stamp_shift + 2)) + (pixel_x >> 2 & 1); + uint16_t word = cd->word_ram[pixel_address]; + switch (pixel_x & 3) + { + default: + case 0: + return word >> 12; + case 1: + return word >> 8 & 0xF; + case 2: + return word >> 4 & 0xF; + case 3: + return word & 0xF; + } + +} + +enum { + FETCH_X, + FETCH_Y, + FETCH_DX, + FETCH_DY, + PIXEL0, + PIXEL1, + PIXEL2, + PIXEL3, + DRAW +}; + +void draw_pixels(segacd_context *cd) +{ + uint16_t to_draw = 4 - (cd->graphics_dst_x & 3); + uint16_t x_end = cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3); + if (cd->graphics_dst_x + to_draw > x_end) { + to_draw = cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3) - cd->graphics_dst_x; + } + for(uint16_t i = 0; i < to_draw; i++) + { + uint32_t dst_address = cd->gate_array[GA_IMAGE_BUFFER_START] << 1; + dst_address += cd->graphics_dst_y << 4; + dst_address += cd->graphics_dst_x >> 2 & 1; + dst_address += ((cd->graphics_dst_x >> 3) * cd->gate_array[GA_IMAGE_BUFFER_VCELLS]) << 4; + uint16_t pixel_shift = 12 - 4 * (cd->graphics_dst_x & 3); + uint16_t pixel = cd->graphics_pixels[i] << pixel_shift; + uint16_t src_mask_check = 0xFF << pixel_shift; + uint16_t src_mask_keep = ~src_mask_check; + switch (cd->gate_array[1] >> 3 & 3) + { + case 0: + //priority mode off + cd->word_ram[dst_address] &= src_mask_keep; + cd->word_ram[dst_address] |= pixel; + break; + case 1: + //underwrite + if (pixel && ! (cd->word_ram[dst_address] & src_mask_check)) { + cd->word_ram[dst_address] &= src_mask_keep; + cd->word_ram[dst_address] |= pixel; + } + break; + case 3: + //overwrite + if (pixel) { + cd->word_ram[dst_address] &= src_mask_keep; + cd->word_ram[dst_address] |= pixel; + } + break; + } + } + cd->graphics_dst_x += to_draw; + if (cd->graphics_dst_x == x_end) { + cd->graphics_dst_y++; + --cd->gate_array[GA_IMAGE_BUFFER_LINES]; + cd->graphics_step = FETCH_X; + } else { + cd->graphics_step = PIXEL0; + } +} + +#define CHECK_CYCLES cd->graphics_step++; if(cd->graphics_cycle >= cycle) break +#define CHECK_ONLY if(cd->graphics_cycle >= cycle) break + +static void do_graphics(segacd_context *cd, uint32_t cycle) +{ + if (!cd->gate_array[GA_IMAGE_BUFFER_LINES]) { + return; + } + while (cd->graphics_cycle < cycle) + { + switch (cd->graphics_step) + { + case FETCH_X: + cd->graphics_x = cd->word_ram[cd->gate_array[GA_TRACE_VECTOR_BASE] << 1] << 8; + cd->graphics_cycle += 3*4; + cd->graphics_dst_x = cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3; + CHECK_CYCLES; + case FETCH_Y: + cd->graphics_y = cd->word_ram[cd->gate_array[GA_TRACE_VECTOR_BASE] << 1 + 1] << 8; + cd->graphics_cycle += 2*4; + CHECK_CYCLES; + case FETCH_DX: + cd->graphics_dx = cd->word_ram[cd->gate_array[GA_TRACE_VECTOR_BASE] << 1 + 2]; + if (cd->graphics_dx & 0x8000) { + cd->graphics_dx |= 0xFF0000; + } + cd->graphics_cycle += 2*4; + CHECK_CYCLES; + case FETCH_DY: + cd->graphics_dy = cd->word_ram[cd->gate_array[GA_TRACE_VECTOR_BASE] << 1 + 3]; + if (cd->graphics_dy & 0x8000) { + cd->graphics_dy |= 0xFF0000; + } + cd->graphics_cycle += 2*4; + CHECK_CYCLES; + case PIXEL0: + cd->graphics_pixels[0] = get_src_pixel(cd); + cd->graphics_cycle += 2*4; + if ((cd->graphics_dst_x & 3) == 3 || (cd->graphics_dst_x + 1 == cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3))) { + cd->graphics_step = DRAW; + CHECK_ONLY; + } else { + CHECK_CYCLES; + } + case PIXEL1: + cd->graphics_pixels[1] = get_src_pixel(cd); + cd->graphics_cycle += 2*4; + if ((cd->graphics_dst_x & 3) == 2 || (cd->graphics_dst_x + 2 == cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3))) { + cd->graphics_step = DRAW; + CHECK_ONLY; + } else { + CHECK_CYCLES; + } + case PIXEL2: + cd->graphics_pixels[2] = get_src_pixel(cd); + cd->graphics_cycle += 2*4; + if ((cd->graphics_dst_x & 3) == 1 || (cd->graphics_dst_x + 3 == cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3))) { + cd->graphics_step = DRAW; + CHECK_ONLY; + } else { + CHECK_CYCLES; + } + case PIXEL3: + cd->graphics_pixels[3] = get_src_pixel(cd); + cd->graphics_cycle += 2*4; + CHECK_CYCLES; + case DRAW: + draw_pixels(cd); + cd->graphics_cycle += 1*4; + if (!cd->gate_array[GA_IMAGE_BUFFER_LINES]) { + break; + } + CHECK_ONLY; + } + } +} + +void cd_graphics_run(segacd_context *cd, uint32_t cycle) +{ + while (cd->graphics_cycle < cycle) + { + if (cd->gate_array[GA_STAMP_SIZE] & BIT_GRON) { + do_graphics(cd, cycle); + //end calculation and actual emulated execution time probably don't 100% line up yet + //deal with that here for now + for(; cd->graphics_cycle < cycle; cd->graphics_cycle += 4) + { + } + if (cd->graphics_cycle >= cd->graphics_int_cycle) { + cd->gate_array[GA_STAMP_SIZE] &= ~BIT_GRON; + break; + } + } else { + cd->graphics_cycle = cycle; + } + } +} +void cd_graphics_start(segacd_context *cd) +{ + if (!(cd->gate_array[GA_STAMP_SIZE] & BIT_GRON)) { + printf("grahpics start @ %u\n", cd->graphics_cycle); + cd->gate_array[GA_STAMP_SIZE] |= BIT_GRON; + //Manual scan is bad, but formula appears to be + // vsize * (13 + 2 * hoffset + 9 * (hdots + hoffset - 1)) + //with an additional 13? cycle setup cost per line + uint32_t lines = cd->gate_array[GA_IMAGE_BUFFER_LINES]; + uint32_t hdots = cd->gate_array[GA_IMAGE_BUFFER_HDOTS]; + uint32_t hoffset = cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3; + cd->graphics_int_cycle = cd->graphics_cycle + 4 * lines * (13 + 2 * hoffset + 9 * (hdots + hoffset - 1)); + cd->graphics_dst_y = cd->gate_array[GA_IMAGE_BUFFER_OFFSET] >> 3; + } else { + printf("graphics start ignored @ %u\n", cd->graphics_cycle); + } + +} diff -r f573f2c31bc9 -r 8e51c0c3f2e3 cd_graphics.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cd_graphics.h Sun Jan 30 19:55:33 2022 -0800 @@ -0,0 +1,27 @@ +#ifndef CD_GRAPHICS_H_ +#define CD_GRAPHICS_H_ + +#include "segacd.h" + +enum { + GA_STAMP_SIZE = 0x58/2, + GA_STAMP_MAP_BASE, + GA_IMAGE_BUFFER_VCELLS, + GA_IMAGE_BUFFER_START, + GA_IMAGE_BUFFER_OFFSET, + GA_IMAGE_BUFFER_HDOTS, + GA_IMAGE_BUFFER_LINES, + GA_TRACE_VECTOR_BASE +}; + +//GA_STAMP_SIZE +#define BIT_GRON 0x8000 +#define BIT_SMS 0x0004 +#define BIT_STS 0x0002 +#define BIT_RPT 0x0001 + +void cd_graphics_init(segacd_context *cd); +void cd_graphics_run(segacd_context *cd, uint32_t cycle); +void cd_graphics_start(segacd_context *cd); + +#endif //CD_GRAPHICS_H_ diff -r f573f2c31bc9 -r 8e51c0c3f2e3 segacd.c --- a/segacd.c Sun Jan 30 11:58:34 2022 -0800 +++ b/segacd.c Sun Jan 30 19:55:33 2022 -0800 @@ -1,6 +1,6 @@ #include #include -#include "segacd.h" +#include "cd_graphics.h" #include "genesis.h" #include "util.h" @@ -367,6 +367,12 @@ context->int_cycle = cd->int2_cycle; context->int_num = 2; } + if (mask < 1) { + if (cd->graphics_int_cycle < context->int_cycle && (cd->gate_array[GA_INT_MASK] & BIT_MASK_IEN1)) { + context->int_cycle = cd->graphics_int_cycle; + context->int_num = 1; + } + } } } } @@ -451,6 +457,14 @@ value |= pixel << (i * 4); } return value; + case GA_STAMP_SIZE: + case GA_IMAGE_BUFFER_LINES: + //these two have bits that change based on graphics operations + cd_graphics_run(cd, m68k->current_cycle); + return cd->gate_array[reg]; + case GA_TRACE_VECTOR_BASE: + //write only + return 0xFFFF; } default: return cd->gate_array[reg]; @@ -613,6 +627,40 @@ case GA_FONT_BITS: cd->gate_array[reg] = value; break; + case GA_STAMP_SIZE: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] &= BIT_GRON; + cd->gate_array[reg] |= value & (BIT_SMS|BIT_STS|BIT_RPT); + break; + case GA_STAMP_MAP_BASE: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0xFFE0; + break; + case GA_IMAGE_BUFFER_VCELLS: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0x1F; + break; + case GA_IMAGE_BUFFER_START: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0xFFF8; + break; + case GA_IMAGE_BUFFER_OFFSET: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0x3F; + break; + case GA_IMAGE_BUFFER_HDOTS: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0x1FF; + break; + case GA_IMAGE_BUFFER_LINES: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0xFF; + break; + case GA_TRACE_VECTOR_BASE: + cd_graphics_run(cd, m68k->current_cycle); + cd->gate_array[reg] = value & 0xFFFE; + cd_graphics_start(cd); + break; default: printf("Unhandled gate array write %X:%X\n", address, value); } @@ -747,6 +795,7 @@ { timers_run(cd, cycle); cdd_run(cd, cycle); + cd_graphics_run(cd, cycle); } static m68k_context *sync_components(m68k_context * context, uint32_t address) @@ -755,6 +804,9 @@ scd_peripherals_run(cd, context->current_cycle); switch (context->int_ack) { + case 1: + cd->graphics_int_cycle = CYCLE_NEVER; + break; case 2: cd->int2_cycle = CYCLE_NEVER; break; @@ -816,6 +868,14 @@ } cdd_mcu_adjust_cycle(&cd->cdd, deduction); lc8951_adjust_cycles(&cd->cdc, deduction); + cd->graphics_cycle -= deduction; + if (cd->graphics_int_cycle != CYCLE_NEVER) { + if (cd->graphics_int_cycle > deduction) { + cd->graphics_int_cycle -= deduction; + } else { + cd->graphics_int_cycle = 0; + } + } } static uint16_t main_gate_read16(uint32_t address, void *vcontext) @@ -1106,6 +1166,7 @@ media = media->chain; } cdd_mcu_init(&cd->cdd, media); + cd_graphics_init(cd); return cd; } diff -r f573f2c31bc9 -r 8e51c0c3f2e3 segacd.h --- a/segacd.h Sun Jan 30 11:58:34 2022 -0800 +++ b/segacd.h Sun Jan 30 19:55:33 2022 -0800 @@ -18,8 +18,16 @@ uint8_t *bram; uint32_t stopwatch_cycle; uint32_t int2_cycle; + uint32_t graphics_int_cycle; uint32_t periph_reset_cycle; + uint32_t graphics_cycle; uint32_t base; + uint32_t graphics_x; + uint32_t graphics_y; + uint32_t graphics_dx; + uint32_t graphics_dy; + uint16_t graphics_dst_x; + uint8_t graphics_pixels[4]; uint8_t timer_pending; uint8_t timer_value; uint8_t busreq; @@ -31,6 +39,8 @@ cdd_mcu cdd; uint8_t cdc_dst_low; uint8_t cdc_int_ack; + uint8_t graphics_step; + uint8_t graphics_dst_y; } segacd_context; segacd_context *alloc_configure_segacd(system_media *media, uint32_t opts, uint8_t force_region, rom_info *info);