/*************************************************************************** Taito Qix hardware driver by John Butler, Ed Mueller, Aaron Giles ***************************************************************************/ #include "driver.h" #include "qix.h" #include "video/crtc6845.h" /* Constants */ #define SCANLINE_INCREMENT 1 /* Globals */ UINT8 *qix_videoaddress; UINT8 qix_cocktail_flip; /* Local variables */ static UINT8 vram_mask; static UINT8 qix_palettebank; static UINT8 leds; static emu_timer *scanline_timer; static UINT8 scanline_latch; /************************************* * * Static function prototypes * *************************************/ static void qix_display_enable_changed(int display_enabled); static TIMER_CALLBACK( scanline_callback ); static void *qix_begin_update(running_machine *machine, int screen, mame_bitmap *bitmap, const rectangle *cliprect); static void qix_update_row(mame_bitmap *bitmap, const rectangle *cliprect, UINT16 ma, UINT8 ra, UINT16 y, UINT8 x_count, void *param); /************************************* * * Video startup * *************************************/ static const crtc6845_interface crtc6845_intf = { 0, /* screen we are acting on */ QIX_CHARACTER_CLOCK, /* the clock (pin 21) of the chip */ 8, /* number of pixels per video memory address */ qix_begin_update, /* before pixel update callback */ qix_update_row, /* row update callback */ 0, /* after pixel update callback */ qix_display_enable_changed /* call back for display state changes */ }; VIDEO_START( qix ) { /* configure the CRT controller */ crtc6845_config(0, &crtc6845_intf); /* allocate memory for the full video RAM */ videoram = auto_malloc(256 * 256); /* initialize the mask for games that don't use it */ vram_mask = 0xff; /* allocate a timer */ scanline_timer = timer_alloc(scanline_callback); timer_adjust(scanline_timer, video_screen_get_time_until_pos(0, 1, 0), 1, attotime_zero); /* set up save states */ state_save_register_global_pointer(videoram, 256 * 256); state_save_register_global(qix_cocktail_flip); state_save_register_global(vram_mask); state_save_register_global(qix_palettebank); state_save_register_global(leds); } /************************************* * * Scanline caching * *************************************/ static TIMER_CALLBACK( scanline_callback ) { int scanline = param; /* force a partial update */ video_screen_update_partial(0, scanline - 1); /* set a timer for the next increment */ scanline += SCANLINE_INCREMENT; if (scanline > machine->screen[0].visarea.max_y) scanline = SCANLINE_INCREMENT; timer_adjust(scanline_timer, video_screen_get_time_until_pos(0, scanline, 0), scanline, attotime_zero); } /************************************* * * Current scanline read * *************************************/ static void qix_display_enable_changed(int display_enabled) { /* on the rising edge, latch the scanline */ if (display_enabled) { UINT16 ma = crtc6845_get_ma(0); UINT8 ra = crtc6845_get_ra(0); /* RA0-RA2 goes to D0-D2 and MA5-MA9 goes to D3-D7 */ scanline_latch = ((ma >> 2) & 0xf8) | (ra & 0x07); } } READ8_HANDLER( qix_scanline_r ) { return scanline_latch; } /************************************* * * Video RAM mask * *************************************/ WRITE8_HANDLER( slither_vram_mask_w ) { /* Slither appears to extend the basic hardware by providing */ /* a mask register which controls which data bits get written */ /* to video RAM */ vram_mask = data; } /************************************* * * Direct video RAM read/write * * The screen is 256x256 with eight * bit pixels (64K). The screen is * divided into two halves each half * mapped by the video CPU at * $0000-$7FFF. The high order bit * of the address latch at $9402 * specifies which half of the screen * is being accessed. * *************************************/ READ8_HANDLER( qix_videoram_r ) { /* add in the upper bit of the address latch */ offset += (qix_videoaddress[0] & 0x80) << 8; return videoram[offset]; } WRITE8_HANDLER( qix_videoram_w ) { /* add in the upper bit of the address latch */ offset += (qix_videoaddress[0] & 0x80) << 8; /* blend the data */ videoram[offset] = (videoram[offset] & ~vram_mask) | (data & vram_mask); } /************************************* * * Latched video RAM read/write * * The address latch works as follows. * When the video CPU accesses $9400, * the screen address is computed by * using the values at $9402 (high * byte) and $9403 (low byte) to get * a value between $0000-$FFFF. The * value at that location is either * returned or written. * *************************************/ READ8_HANDLER( qix_addresslatch_r ) { /* compute the value at the address latch */ offset = (qix_videoaddress[0] << 8) | qix_videoaddress[1]; return videoram[offset]; } WRITE8_HANDLER( qix_addresslatch_w ) { /* compute the value at the address latch */ offset = (qix_videoaddress[0] << 8) | qix_videoaddress[1]; /* blend the data */ videoram[offset] = (videoram[offset] & ~vram_mask) | (data & vram_mask); } /************************************* * * Palette RAM * *************************************/ WRITE8_HANDLER( qix_paletteram_w ) { /* this conversion table should be about right. It gives a reasonable */ /* gray scale in the test screen, and the red, green and blue squares */ /* in the same screen are barely visible, as the manual requires. */ static UINT8 table[16] = { 0x00, /* value = 0, intensity = 0 */ 0x12, /* value = 0, intensity = 1 */ 0x24, /* value = 0, intensity = 2 */ 0x49, /* value = 0, intensity = 3 */ 0x12, /* value = 1, intensity = 0 */ 0x24, /* value = 1, intensity = 1 */ 0x49, /* value = 1, intensity = 2 */ 0x92, /* value = 1, intensity = 3 */ 0x5b, /* value = 2, intensity = 0 */ 0x6d, /* value = 2, intensity = 1 */ 0x92, /* value = 2, intensity = 2 */ 0xdb, /* value = 2, intensity = 3 */ 0x7f, /* value = 3, intensity = 0 */ 0x91, /* value = 3, intensity = 1 */ 0xb6, /* value = 3, intensity = 2 */ 0xff /* value = 3, intensity = 3 */ }; int bits, intensity, red, green, blue; /* set the palette RAM value */ paletteram[offset] = data; /* compute R, G, B from the table */ intensity = (data >> 0) & 0x03; bits = (data >> 6) & 0x03; red = table[(bits << 2) | intensity]; bits = (data >> 4) & 0x03; green = table[(bits << 2) | intensity]; bits = (data >> 2) & 0x03; blue = table[(bits << 2) | intensity]; /* update the palette */ palette_set_color(Machine, offset, MAKE_RGB(red, green, blue)); } WRITE8_HANDLER( qix_palettebank_w ) { /* set the bank value */ if (qix_palettebank != (data & 3)) { video_screen_update_partial(0, video_screen_get_vpos(0) - 1); qix_palettebank = data & 3; } /* LEDs are in the upper 6 bits */ leds = ~data & 0xfc; } /************************************* * * CRTC callbacks for updating * the screen * *************************************/ static void *qix_begin_update(running_machine *machine, int screen, mame_bitmap *bitmap, const rectangle *cliprect) { #if 0 // note the confusing bit order! popmessage("self test leds: %d%d %d%d%d%d",BIT(leds,7),BIT(leds,5),BIT(leds,6),BIT(leds,4),BIT(leds,2),BIT(leds,3)); #endif /* return the pens we are going to use to update the display */ return (void *)&machine->pens[qix_palettebank * 256]; } static void qix_update_row(mame_bitmap *bitmap, const rectangle *cliprect, UINT16 ma, UINT8 ra, UINT16 y, UINT8 x_count, void *param) { UINT16 x; UINT8 scanline[256]; pen_t *pens = (pen_t *)param; /* the memory is hooked up to the MA, RA lines this way */ offs_t offs = ((ma << 6) & 0xf800) | ((ra << 8) & 0x0700); offs_t offs_xor = qix_cocktail_flip ? 0xffff : 0; for (x = 0; x < x_count * 8; x++) scanline[x] = videoram[(offs + x) ^ offs_xor]; draw_scanline8(bitmap, 0, y, x_count * 8, scanline, pens, -1); }