// license:BSD-3-Clause // copyright-holders:Philip Bennett /************************************************************************* Turrett Tower video hardware *************************************************************************/ #include "emu.h" #include "machine/idectrl.h" #include "includes/turrett.h" inline uint8_t clamp_5bit(int8_t val) { if (val < 0) return 0; if (val > 31) return 31; return val; } uint32_t turrett_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { int page = (m_video_ctrl & 1) ^ 1; const uint16_t *vram = m_video_ram[page].get(); int8_t fade_b = m_video_fade & 0x1f; int8_t fade_g = (m_video_fade >> 5) & 0x1f; int8_t fade_r = (m_video_fade >> 10) & 0x1f; if (m_video_fade & 0x8000) { fade_b = -fade_b; fade_g = -fade_g; fade_r = -fade_r; } for (int y = cliprect.min_y; y <= cliprect.max_y; ++y) { const uint16_t *src = &vram[y * X_VISIBLE + cliprect.min_x]; uint16_t *dest = &bitmap.pix16(y, cliprect.min_x); if (m_video_fade != 0) { for (int x = cliprect.min_x; x <= cliprect.max_x; ++x) { uint16_t srcpix = *src++; uint8_t src_b = srcpix & 0x1f; uint8_t src_g = (srcpix >> 5) & 0x1f; uint8_t src_r = (srcpix >> 10) & 0x1f; uint8_t dst_b = clamp_5bit(src_b + fade_b); uint8_t dst_g = clamp_5bit(src_g + fade_g); uint8_t dst_r = clamp_5bit(src_r + fade_r); *dest++ = (dst_r << 10) | (dst_g << 5) | dst_b; } } else { for (int x = cliprect.min_x; x <= cliprect.max_x; ++x) { *dest++ = *src++ & 0x7fff; } } } return 0; } uint32_t turrett_state::write_video_ram(uint16_t data) { uint32_t clocks = 1; if (!m_skip_x && !m_skip_y) { // Handle hot spot test if (m_x_pos == m_hotspot_x && m_y_pos == (m_hotspot_y & 0xfff)) { m_hotspot_y |= 0x8000; } if (m_x_pos >= 0 && m_x_pos < X_VISIBLE && m_y_pos >= 0 && m_y_pos < Y_VISIBLE) { int address = m_y_pos * X_VISIBLE + m_x_pos; uint16_t *vramptr = &m_video_ram[m_video_ctrl & 1][address]; uint16_t srcpix = data; uint16_t dstpix = data; // Blending enabled? if (data & 0x8000) { dstpix = *vramptr; uint8_t src_b = srcpix & 0x1f; uint8_t src_g = (srcpix >> 5) & 0x1f; uint8_t src_r = (srcpix >> 10) & 0x1f; uint8_t dst_b = dstpix & 0x1f; uint8_t dst_g = (dstpix >> 5) & 0x1f; uint8_t dst_r = (dstpix >> 10) & 0x1f; // Additive if (m_video_ctrl & 2) { dst_b = clamp_5bit(src_b + dst_b); dst_g = clamp_5bit(src_g + dst_g); dst_r = clamp_5bit(src_r + dst_r); } else { // R always seems to be 0 for blended pixels if ((src_g & 1) && (src_b & 1)) { dst_b = clamp_5bit(dst_b - src_b); dst_g = clamp_5bit(dst_g - src_g); dst_r = clamp_5bit(dst_r - src_r); } else { // 75% source, 25% destination? dst_b = (src_b - (src_b >> 2)) + (dst_b >> 2); dst_g = (src_g - (src_g >> 2)) + (dst_g >> 2); dst_r = (src_r - (src_r >> 2)) + (dst_r >> 2); } } clocks += 2; *vramptr = (dst_r << 10) | (dst_g << 5) | dst_b; } else { clocks += 2; *vramptr = srcpix; } } } update_video_addr(); return clocks; } void turrett_state::update_video_addr(void) { // Handle auto-increment if (m_dx == m_x_mod) { m_dx = 0; m_scale_cnt_y += m_scale; if (m_scale_cnt_y & 0x800) { m_skip_y = false; m_scale_cnt_y &= 0x7ff; --m_y_pos; m_x_pos = m_x_start; } else { m_skip_y = true; } } else { ++m_dx; m_scale_cnt_x += m_scale; if (m_scale_cnt_x & 0x800) { m_scale_cnt_x &= 0x7ff; m_skip_x = false; ++m_x_pos; } else { m_skip_x = true; } } } READ32_MEMBER( turrett_state::video_r ) { uint32_t ret = 0; if (offset == 3 && mem_mask == 0x0000ffff) { // Collision detection flag ret = m_hotspot_y & 0x8000; } else { fatalerror("Unhandled video read (%x %x)!", offset, mem_mask); } return ret; } WRITE32_MEMBER( turrett_state::video_w ) { switch (offset) { case 0: { if (mem_mask == 0xffff0000) { data >>= 16; // TODO: Merge with DMA code? if ((data & 0xc400) == 0xc400) { // RLE word int count = (data & 0x3ff) + 1; // TODO: Cycle stalling while (count--) write_video_ram(m_last_pixel); } else { write_video_ram(data); // Store current pixel m_last_pixel = data; } } else { m_video_ctrl = data & 3; } break; } case 1: { m_x_mod = data & 0xffff; m_scale = 0x800 - (data >> 16); break; } case 2: { m_y_pos = data & 0xffff; m_x_pos = data >> 16; // Seems the logical place to set these m_x_start = m_x_pos; m_skip_x = false; m_skip_y = false; m_dx = 0; break; } case 3: { if (mem_mask == 0xffff0000) { m_video_fade = data >> 16; } else if (mem_mask == 0x0000ffff) { if (data & 0x4000) m_hotspot_y = data; else m_hotspot_x = data; } else { fatalerror("Unhandled"); } break; } default: fatalerror("Unhandled video write: %x %x\n", offset, data); } } TIMER_CALLBACK_MEMBER( turrett_state::dma_complete ) { m_dma_idle = true; } WRITE32_MEMBER( turrett_state::dma_w ) { int bank = ((offset & 2) >> 1) ^ 1; if ((offset & 1) == 0) { m_dma_addr[bank] = data; } else { uint32_t clocks = 0; uint32_t words = data & 0x0fffffff; // IDE to DRAM if (data & 0x10000000) { uint32_t addr = m_dma_addr[bank]; uint16_t *ram = bank ? m_bank_b : m_bank_a; while (words--) { ram[addr & DIMM_BANK_MASK] = m_ata->read_cs0(0); ++addr; } clocks = 500; // TODO: May be too high m_dma_addr[bank] = addr; } // IDE to video RAM else if (data & 0x40000000) { while (words--) { uint16_t data = m_ata->read_cs0(0); // TODO: Verify if this is correct if ((data & 0xc400) == 0xc400) { fatalerror("IDE RLE detected"); // RLE word int count = (data & 0x3ff) + 1; while (count--) write_video_ram(m_last_pixel); } else { write_video_ram(data); // Store current pixel m_last_pixel = data; } } clocks = 500; // TODO } // RAM to video RAM else if (data & 0x80000000) { uint32_t addr = m_dma_addr[bank]; uint16_t *ram = bank ? m_bank_b : m_bank_a; //bool first = true; // Does it matter? while (words--) { uint16_t val = ram[addr++]; //++clocks; switch (val & 0xc400) { // Transparent run case 0x8400: { int run = (((val & 0x3800) >> 1) | (val & 0x03ff)) + 1; while (run--) { update_video_addr(); //++clocks; } break; } case 0xc400: { int run = (((val & 0x3800) >> 1) | (val & 0x03ff)) + 1; while (run--) clocks += write_video_ram(m_last_pixel); break; } default: { m_last_pixel = val; clocks += write_video_ram(val); break; } } //first = false; } // clocks =1;///= 2; } else { popmessage("Unhandled DMA case: %.8x, contact MAMEdev!\n", data); } // Set the DMA completion timer m_dma_idle = false; m_dma_timer->adjust(attotime::from_nsec(10) * clocks, 0); } }