/*************************************************************************** Cinemat/Leland driver Leland video hardware ***************************************************************************/ #include "driver.h" #include "leland.h" #include "sound/dac.h" /* constants */ #define VRAM_SIZE 0x10000 #define QRAM_SIZE 0x10000 #define VIDEO_WIDTH 0x28 #define VIDEO_HEIGHT 0x1e /* debugging */ #define LOG_COMM 0 struct vram_state_data { UINT16 addr; UINT8 latch[2]; }; /* video RAM */ UINT8 *ataxx_qram; UINT8 leland_last_scanline_int; static mame_bitmap *fgbitmap; static UINT8 *leland_video_ram; /* video RAM bitmap drawing */ static struct vram_state_data vram_state[2]; static UINT8 sync_next_write; /* partial screen updating */ static int next_update_scanline; /* scroll background registers */ static UINT16 xscroll; static UINT16 yscroll; static UINT8 gfxbank; /************************************* * * Start video hardware * *************************************/ VIDEO_START( leland ) { /* allocate memory */ leland_video_ram = auto_malloc(VRAM_SIZE); fgbitmap = auto_bitmap_alloc(VIDEO_WIDTH * 8, VIDEO_HEIGHT * 8, machine->screen[0].format); /* reset videoram */ memset(leland_video_ram, 0, VRAM_SIZE); } VIDEO_START( ataxx ) { /* first do the standard stuff */ video_start_leland(machine); /* allocate memory */ ataxx_qram = auto_malloc(QRAM_SIZE); /* reset QRAM */ memset(ataxx_qram, 0, QRAM_SIZE); } /************************************* * * Scrolling and banking * *************************************/ WRITE8_HANDLER( leland_gfx_port_w ) { /* adjust the proper scroll value */ switch (offset) { case -1: if (gfxbank != data) { video_screen_update_partial(0, leland_last_scanline_int); gfxbank = data; } break; case 0: if ((xscroll & 0xff) != data) { video_screen_update_partial(0, leland_last_scanline_int); xscroll = (xscroll & 0xff00) | (data & 0x00ff); } break; case 1: if ((xscroll >> 8) != data) { video_screen_update_partial(0, leland_last_scanline_int); xscroll = (xscroll & 0x00ff) | ((data << 8) & 0xff00); } break; case 2: if ((yscroll & 0xff) != data) { video_screen_update_partial(0, leland_last_scanline_int); yscroll = (yscroll & 0xff00) | (data & 0x00ff); } break; case 3: if ((yscroll >> 8) != data) { video_screen_update_partial(0, leland_last_scanline_int); yscroll = (yscroll & 0x00ff) | ((data << 8) & 0xff00); } break; } } /************************************* * * Video address setting * *************************************/ static void leland_video_addr_w(int offset, int data, int num) { struct vram_state_data *state = vram_state + num; if (!offset) state->addr = (state->addr & 0xfe00) | ((data << 1) & 0x01fe); else state->addr = ((data << 9) & 0xfe00) | (state->addr & 0x01fe); if (num == 0) sync_next_write = (state->addr >= 0xf000); } /************************************* * * Flush data from VRAM into our copy * *************************************/ static void update_for_scanline(int scanline) { int i, j; /* skip if we're behind the times */ if (scanline <= next_update_scanline) return; /* update all scanlines */ for (i = next_update_scanline; i < scanline; i++) if (i < VIDEO_HEIGHT * 8) { UINT8 scandata[VIDEO_WIDTH * 8]; UINT8 *dst = scandata; UINT8 *src = &leland_video_ram[i * 256]; for (j = 0; j < VIDEO_WIDTH * 8 / 2; j++) { UINT8 pix = *src++; *dst++ = pix >> 4; *dst++ = pix & 15; } draw_scanline8(fgbitmap, 0, i, VIDEO_WIDTH * 8, scandata, NULL, -1); } /* also update the DACs */ if (scanline >= VIDEO_HEIGHT * 8) scanline = 256; for (i = next_update_scanline; i < scanline; i++) { if (!(leland_dac_control & 0x01)) leland_dac_update(0, leland_video_ram[i * 256 + 160]); if (!(leland_dac_control & 0x02)) leland_dac_update(1, leland_video_ram[i * 256 + 161]); } /* set the new last update */ next_update_scanline = scanline; } /************************************* * * Common video RAM read * *************************************/ static int leland_vram_port_r(int offset, int num) { struct vram_state_data *state = vram_state + num; int addr = state->addr; int inc = (offset >> 2) & 2; int ret; switch (offset & 7) { case 3: /* read hi/lo (alternating) */ ret = leland_video_ram[addr]; addr += inc & (addr << 1); addr ^= 1; break; case 5: /* read hi */ ret = leland_video_ram[addr | 1]; addr += inc; break; case 6: /* read lo */ ret = leland_video_ram[addr & ~1]; addr += inc; break; default: logerror("CPU #%d %04x Warning: Unknown video port %02x read (address=%04x)\n", cpu_getactivecpu(),activecpu_get_pc(), offset, addr); ret = 0; break; } state->addr = addr; if (LOG_COMM && addr >= 0xf000) logerror("%04X:%s comm read %04X = %02X\n", activecpu_get_previouspc(), num ? "slave" : "master", addr, ret); return ret; } /************************************* * * Common video RAM write * *************************************/ static void leland_vram_port_w(int offset, int data, int num) { struct vram_state_data *state = vram_state + num; int addr = state->addr; int inc = (offset >> 2) & 2; int trans = (offset >> 4) & num; /* if we're writing "behind the beam", make sure we've cached what was there */ if (addr < 0xf000) { int cur_scanline = video_screen_get_vpos(0); int mod_scanline = addr / 256; if (cur_scanline != next_update_scanline && mod_scanline < cur_scanline) update_for_scanline(cur_scanline); } if (LOG_COMM && addr >= 0xf000) logerror("%04X:%s comm write %04X = %02X\n", activecpu_get_previouspc(), num ? "slave" : "master", addr, data); /* based on the low 3 bits of the offset, update the destination */ switch (offset & 7) { case 1: /* write hi = data, lo = latch */ leland_video_ram[addr & ~1] = state->latch[0]; leland_video_ram[addr | 1] = data; addr += inc; break; case 2: /* write hi = latch, lo = data */ leland_video_ram[addr & ~1] = data; leland_video_ram[addr | 1] = state->latch[1]; addr += inc; break; case 3: /* write hi/lo = data (alternating) */ if (trans) { if (!(data & 0xf0)) data |= leland_video_ram[addr] & 0xf0; if (!(data & 0x0f)) data |= leland_video_ram[addr] & 0x0f; } leland_video_ram[addr] = data; addr += inc & (addr << 1); addr ^= 1; break; case 5: /* write hi = data */ state->latch[1] = data; if (trans) { if (!(data & 0xf0)) data |= leland_video_ram[addr | 1] & 0xf0; if (!(data & 0x0f)) data |= leland_video_ram[addr | 1] & 0x0f; } leland_video_ram[addr | 1] = data; addr += inc; break; case 6: /* write lo = data */ state->latch[0] = data; if (trans) { if (!(data & 0xf0)) data |= leland_video_ram[addr & ~1] & 0xf0; if (!(data & 0x0f)) data |= leland_video_ram[addr & ~1] & 0x0f; } leland_video_ram[addr & ~1] = data; addr += inc; break; default: logerror("CPU #%d %04x Warning: Unknown video port write (address=%04x value=%02x)\n", cpu_getactivecpu(),activecpu_get_pc(), offset, addr); break; } /* update the address and plane */ state->addr = addr; } /************************************* * * Master video RAM read/write * *************************************/ WRITE8_HANDLER( leland_master_video_addr_w ) { leland_video_addr_w(offset, data, 0); } static TIMER_CALLBACK( leland_delayed_mvram_w ) { int num = (param >> 16) & 1; int offset = (param >> 8) & 0xff; int data = param & 0xff; leland_vram_port_w(offset, data, num); } WRITE8_HANDLER( leland_mvram_port_w ) { if (sync_next_write) { timer_call_after_resynch(0x00000 | (offset << 8) | data, leland_delayed_mvram_w); sync_next_write = 0; } else leland_vram_port_w(offset, data, 0); } READ8_HANDLER( leland_mvram_port_r ) { return leland_vram_port_r(offset, 0); } /************************************* * * Slave video RAM read/write * *************************************/ WRITE8_HANDLER( leland_slave_video_addr_w ) { leland_video_addr_w(offset, data, 1); } WRITE8_HANDLER( leland_svram_port_w ) { leland_vram_port_w(offset, data, 1); } READ8_HANDLER( leland_svram_port_r ) { return leland_vram_port_r(offset, 1); } /************************************* * * Ataxx master video RAM read/write * *************************************/ WRITE8_HANDLER( ataxx_mvram_port_w ) { offset = ((offset >> 1) & 0x07) | ((offset << 3) & 0x08) | (offset & 0x10); if (sync_next_write) { timer_call_after_resynch(0x00000 | (offset << 8) | data, leland_delayed_mvram_w); sync_next_write = 0; } else leland_vram_port_w(offset, data, 0); } WRITE8_HANDLER( ataxx_svram_port_w ) { offset = ((offset >> 1) & 0x07) | ((offset << 3) & 0x08) | (offset & 0x10); leland_vram_port_w(offset, data, 1); } /************************************* * * Ataxx slave video RAM read/write * *************************************/ READ8_HANDLER( ataxx_mvram_port_r ) { offset = ((offset >> 1) & 0x07) | ((offset << 3) & 0x08) | (offset & 0x10); return leland_vram_port_r(offset, 0); } READ8_HANDLER( ataxx_svram_port_r ) { offset = ((offset >> 1) & 0x07) | ((offset << 3) & 0x08) | (offset & 0x10); return leland_vram_port_r(offset, 1); } /************************************* * * End-of-frame routine * *************************************/ static TIMER_CALLBACK( scanline_reset ) { /* flush the remaining scanlines */ next_update_scanline = 0; /* turn off the DACs at the start of the frame */ leland_dac_control = 3; } VIDEO_EOF( leland ) { /* update anything remaining */ update_for_scanline(VIDEO_HEIGHT * 8); /* set a timer to go off at the top of the frame */ timer_set(video_screen_get_time_until_pos(0, 0, 0), 0, scanline_reset); } /************************************* * * ROM-based refresh routine * *************************************/ VIDEO_UPDATE( leland ) { const UINT8 *background_prom = memory_region(REGION_USER1); const gfx_element *gfx = machine->gfx[0]; int char_bank = ((gfxbank >> 4) & 0x03) * 0x0400; int prom_bank = ((gfxbank >> 3) & 0x01) * 0x2000; int xcoarse = xscroll / 8; int ycoarse = yscroll / 8; int xfine = xscroll % 8; int yfine = yscroll % 8; int x, y; /* update anything remaining */ update_for_scanline(cliprect->max_y); /* draw what's visible to the main bitmap */ for (y = cliprect->min_y / 8; y < cliprect->max_y / 8 + 2; y++) { int ysum = ycoarse + y; for (x = 0; x < VIDEO_WIDTH + 1; x++) { int xsum = xcoarse + x; int offs = ((xsum << 0) & 0x000ff) | ((ysum << 8) & 0x01f00) | prom_bank | ((ysum << 9) & 0x1c000); int code = background_prom[offs] | ((ysum << 2) & 0x300) | char_bank; int color = (code >> 5) & 7; /* draw to the bitmap */ drawgfx(bitmap, gfx, code, 8 * color, 0, 0, 8 * x - xfine, 8 * y - yfine, cliprect, TRANSPARENCY_NONE_RAW, 0); } } /* Merge the two bitmaps together */ copybitmap(bitmap, fgbitmap, 0, 0, 0, 0, cliprect, TRANSPARENCY_BLEND, 6); return 0; } /************************************* * * RAM-based refresh routine * *************************************/ VIDEO_UPDATE( ataxx ) { const gfx_element *gfx = machine->gfx[0]; int xcoarse = xscroll / 8; int ycoarse = yscroll / 8; int xfine = xscroll % 8; int yfine = yscroll % 8; int x, y; /* update anything remaining */ update_for_scanline(cliprect->max_y); /* draw what's visible to the main bitmap */ for (y = cliprect->min_y / 8; y < cliprect->max_y / 8 + 2; y++) { int ysum = ycoarse + y; for (x = 0; x < VIDEO_WIDTH + 1; x++) { int xsum = xcoarse + x; int offs = ((ysum & 0x40) << 9) + ((ysum & 0x3f) << 8) + (xsum & 0xff); int code = ataxx_qram[offs] | ((ataxx_qram[offs + 0x4000] & 0x7f) << 8); /* draw to the bitmap */ drawgfx(bitmap, gfx, code, 0, 0, 0, 8 * x - xfine, 8 * y - yfine, cliprect, TRANSPARENCY_NONE_RAW, 0); } } /* Merge the two bitmaps together */ copybitmap(bitmap, fgbitmap, 0, 0, 0, 0, cliprect, TRANSPARENCY_BLEND, 6); return 0; }