// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** Sega System 18 hardware ***************************************************************************/ #include "emu.h" #include "includes/segas18.h" /************************************* * * Debugging * *************************************/ #define DEBUG_VDP (0) /************************************* * * Video startup * *************************************/ void segas18_state::video_start() { m_temp_bitmap.allocate(m_screen->width(), m_screen->height()); m_grayscale_enable = 0; m_vdp_enable = 0; m_vdp_mixing = 0; // initialize the tile/text layers m_segaic16vid->tilemap_init( 0, segaic16_video_device::TILEMAP_16B, 0x000, 0, 8); save_item(NAME(m_grayscale_enable)); save_item(NAME(m_vdp_enable)); save_item(NAME(m_vdp_mixing)); save_item(NAME(m_temp_bitmap)); } /************************************* * * Miscellaneous setters * *************************************/ WRITE_LINE_MEMBER(segas18_state::set_grayscale) { if (state != m_grayscale_enable) { m_screen->update_partial(m_screen->vpos()); m_grayscale_enable = state; // osd_printf_debug("Grayscale = %02X\n", enable); } } WRITE_LINE_MEMBER(segas18_state::set_vdp_enable) { if (state != m_vdp_enable) { m_screen->update_partial(m_screen->vpos()); m_vdp_enable = state; #if DEBUG_VDP osd_printf_debug("VDP enable = %02X\n", enable); #endif } } void segas18_state::set_vdp_mixing(uint8_t mixing) { if (mixing != m_vdp_mixing) { m_screen->update_partial(m_screen->vpos()); m_vdp_mixing = mixing; #if DEBUG_VDP osd_printf_debug("VDP mixing = %02X\n", mixing); #endif } } /************************************* * * VDP drawing * *************************************/ void segas18_state::draw_vdp(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int priority) { bitmap_ind8 &priority_bitmap = screen.priority(); for (int y = cliprect.min_y; y <= cliprect.max_y; y++) { // uint16_t *src = vdp->m_render_line; // can't use this because we're not in RGB32, which we'll need to be if there are palette effects // uint16_t *src2 = vdp->m_render_line_raw; uint16_t *dst = &bitmap.pix(y); uint8_t *pri = &priority_bitmap.pix(y); for (int x = cliprect.min_x; x <= cliprect.max_x; x++) { if (m_vdp->m_render_line_raw[x] & 0x100) { int pix = m_vdp->m_render_line_raw[x] & 0x3f; if (pix & 0xf) { switch (pix & 0xc0) { case 0x00: dst[x] = pix + 0x1000; /* 0x1040 - would be shadow? */ break; case 0x40: case 0x80: dst[x] = pix + 0x1000; break; case 0xc0: dst[x] = pix + 0x1000; /* 0x1080 - would be higlight? */ break; } pri[x] |= priority; } } } } } /************************************* * * Video update * *************************************/ uint32_t segas18_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { /* Current understanding of VDP mixing: mixing = 0x00: astorm: layer = 0, pri = 0x00 or 0x01 lghost: layer = 0, pri = 0x00 or 0x01 mixing = 0x01: ddcrew: layer = 0, pri = 0x00 or 0x01 or 0x02 mixing = 0x02: never seen mixing = 0x03: ddcrew: layer = 1, pri = 0x00 or 0x01 or 0x02 mixing = 0x04: astorm: layer = 2 or 3, pri = 0x00 or 0x01 or 0x02 mwalk: layer = 2 or 3, pri = 0x00 or 0x01 or 0x02 mixing = 0x05: ddcrew: layer = 2, pri = 0x04 wwally: layer = 2, pri = 0x04 mixing = 0x06: never seen mixing = 0x07: cltchitr: layer = 1 or 2 or 3, pri = 0x02 or 0x04 or 0x08 mwalk: layer = 3, pri = 0x04 or 0x08 */ int vdplayer = (m_vdp_mixing >> 1) & 3; int vdppri = (m_vdp_mixing & 1) ? (1 << vdplayer) : 0; #if DEBUG_VDP if (machine().input().code_pressed(KEYCODE_Q)) vdplayer = 0; if (machine().input().code_pressed(KEYCODE_W)) vdplayer = 1; if (machine().input().code_pressed(KEYCODE_E)) vdplayer = 2; if (machine().input().code_pressed(KEYCODE_R)) vdplayer = 3; if (machine().input().code_pressed(KEYCODE_A)) vdppri = 0x00; if (machine().input().code_pressed(KEYCODE_S)) vdppri = 0x01; if (machine().input().code_pressed(KEYCODE_D)) vdppri = 0x02; if (machine().input().code_pressed(KEYCODE_F)) vdppri = 0x04; if (machine().input().code_pressed(KEYCODE_G)) vdppri = 0x08; #endif // if no drawing is happening, fill with black and get out if (!m_segaic16vid->m_display_enable) { bitmap.fill(m_palette->black_pen(), cliprect); return 0; } // start the sprites drawing m_sprites->draw_async(cliprect); // reset priorities screen.priority().fill(0, cliprect); // draw background opaquely first, not setting any priorities m_segaic16vid->tilemap_draw( screen, bitmap, cliprect, 0, segaic16_video_device::TILEMAP_BACKGROUND, 0 | TILEMAP_DRAW_OPAQUE, 0x00); m_segaic16vid->tilemap_draw( screen, bitmap, cliprect, 0, segaic16_video_device::TILEMAP_BACKGROUND, 1 | TILEMAP_DRAW_OPAQUE, 0x00); if (m_vdp_enable && vdplayer == 0) draw_vdp(screen, bitmap, cliprect, vdppri); // draw background again to draw non-transparent pixels over the VDP and set the priority m_segaic16vid->tilemap_draw( screen, bitmap, cliprect, 0, segaic16_video_device::TILEMAP_BACKGROUND, 0, 0x01); m_segaic16vid->tilemap_draw( screen, bitmap, cliprect, 0, segaic16_video_device::TILEMAP_BACKGROUND, 1, 0x02); if (m_vdp_enable && vdplayer == 1) draw_vdp(screen, bitmap, cliprect, vdppri); // draw foreground m_segaic16vid->tilemap_draw( screen, bitmap, cliprect, 0, segaic16_video_device::TILEMAP_FOREGROUND, 0, 0x02); m_segaic16vid->tilemap_draw( screen, bitmap, cliprect, 0, segaic16_video_device::TILEMAP_FOREGROUND, 1, 0x04); if (m_vdp_enable && vdplayer == 2) draw_vdp(screen, bitmap, cliprect, vdppri); // text layer m_segaic16vid->tilemap_draw( screen, bitmap, cliprect, 0, segaic16_video_device::TILEMAP_TEXT, 0, 0x04); m_segaic16vid->tilemap_draw( screen, bitmap, cliprect, 0, segaic16_video_device::TILEMAP_TEXT, 1, 0x08); if (m_vdp_enable && vdplayer == 3) draw_vdp(screen, bitmap, cliprect, vdppri); // mix in sprites bitmap_ind16 &sprites = m_sprites->bitmap(); for (const sparse_dirty_rect *rect = m_sprites->first_dirty_rect(cliprect); rect != nullptr; rect = rect->next()) { for (int y = rect->min_y; y <= rect->max_y; y++) { uint16_t *dest = &bitmap.pix(y); uint16_t *src = &sprites.pix(y); uint8_t *pri = &screen.priority().pix(y); for (int x = rect->min_x; x <= rect->max_x; x++) { // only process written pixels uint16_t pix = src[x]; if (pix != 0xffff) { // compare sprite priority against tilemap priority int priority = (pix >> 10) & 3; if ((1 << priority) > pri[x]) { // if the color is set to maximum, shadow pixels underneath us if ((pix & 0x03f0) == 0x03f0) dest[x] += m_palette_entries; // otherwise, just add in sprite palette base else dest[x] = 0x400 | (pix & 0x3ff); } } } } } #if DEBUG_VDP if (m_vdp_enable && machine().input().code_pressed(KEYCODE_V)) { bitmap.fill(m_palette->black_pen(), cliprect); update_system18_vdp(bitmap, cliprect); } if (vdp_enable && machine().input().code_pressed(KEYCODE_B)) { FILE *f = fopen("vdp.bin", "w"); fwrite(m_temp_bitmap->base(), 1, m_temp_bitmap->rowpixels() * (m_temp_bitmap->bpp() / 8) * m_temp_bitmap->height(), f); fclose(f); } #endif return 0; }