/*************************************************************************** Irem M52 hardware ***************************************************************************/ #include "emu.h" #include "video/resnet.h" #include "includes/m52.h" #define BGHEIGHT 64 /************************************* * * Palette configuration * *************************************/ PALETTE_INIT( m52 ) { const UINT8 *char_pal = color_prom + 0x000; const UINT8 *back_pal = color_prom + 0x200; const UINT8 *sprite_pal = color_prom + 0x220; const UINT8 *sprite_table = color_prom + 0x240; static const int resistances_3[3] = { 1000, 470, 220 }; static const int resistances_2[2] = { 470, 220 }; double weights_r[3], weights_g[3], weights_b[3], scale; int i; machine.colortable = colortable_alloc(machine, 512 + 32 + 32); /* compute palette information for characters/backgrounds */ scale = compute_resistor_weights(0, 255, -1.0, 3, resistances_3, weights_r, 0, 0, 3, resistances_3, weights_g, 0, 0, 2, resistances_2, weights_b, 0, 0); /* character palette */ for (i = 0; i < 512; i++) { UINT8 promval = char_pal[i]; int r = combine_3_weights(weights_r, BIT(promval,0), BIT(promval,1), BIT(promval,2)); int g = combine_3_weights(weights_g, BIT(promval,3), BIT(promval,4), BIT(promval,5)); int b = combine_2_weights(weights_b, BIT(promval,6), BIT(promval,7)); colortable_palette_set_color(machine.colortable, i, MAKE_RGB(r,g,b)); } /* background palette */ for (i = 0; i < 32; i++) { UINT8 promval = back_pal[i]; int r = combine_3_weights(weights_r, BIT(promval,0), BIT(promval,1), BIT(promval,2)); int g = combine_3_weights(weights_g, BIT(promval,3), BIT(promval,4), BIT(promval,5)); int b = combine_2_weights(weights_b, BIT(promval,6), BIT(promval,7)); colortable_palette_set_color(machine.colortable, 512+i, MAKE_RGB(r,g,b)); } /* compute palette information for sprites */ compute_resistor_weights(0, 255, scale, 2, resistances_2, weights_r, 470, 0, 3, resistances_3, weights_g, 470, 0, 3, resistances_3, weights_b, 470, 0); /* sprite palette */ for (i = 0; i < 32; i++) { UINT8 promval = sprite_pal[i]; int r = combine_2_weights(weights_r, BIT(promval,6), BIT(promval,7)); int g = combine_3_weights(weights_g, BIT(promval,3), BIT(promval,4), BIT(promval,5)); int b = combine_3_weights(weights_b, BIT(promval,0), BIT(promval,1), BIT(promval,2)); colortable_palette_set_color(machine.colortable, 512 + 32 + i, MAKE_RGB(r,g,b)); } /* character lookup table */ for (i = 0; i < 512; i++) colortable_entry_set_value(machine.colortable, i, i); /* sprite lookup table */ for (i = 0; i < 16 * 4; i++) { UINT8 promval = sprite_table[(i & 3) | ((i & ~3) << 1)]; colortable_entry_set_value(machine.colortable, 512 + i, 512 + 32 + promval); } /* background */ /* the palette is a 32x8 PROM with many colors repeated. The address of */ /* the colors to pick is as follows: */ /* xbb00: mountains */ /* 0xxbb: hills */ /* 1xxbb: city */ colortable_entry_set_value(machine.colortable, 512+16*4+0*4+0, 512); colortable_entry_set_value(machine.colortable, 512+16*4+0*4+1, 512+4); colortable_entry_set_value(machine.colortable, 512+16*4+0*4+2, 512+8); colortable_entry_set_value(machine.colortable, 512+16*4+0*4+3, 512+12); colortable_entry_set_value(machine.colortable, 512+16*4+1*4+0, 512); colortable_entry_set_value(machine.colortable, 512+16*4+1*4+1, 512+1); colortable_entry_set_value(machine.colortable, 512+16*4+1*4+2, 512+2); colortable_entry_set_value(machine.colortable, 512+16*4+1*4+3, 512+3); colortable_entry_set_value(machine.colortable, 512+16*4+2*4+0, 512); colortable_entry_set_value(machine.colortable, 512+16*4+2*4+1, 512+16+1); colortable_entry_set_value(machine.colortable, 512+16*4+2*4+2, 512+16+2); colortable_entry_set_value(machine.colortable, 512+16*4+2*4+3, 512+16+3); } /************************************* * * Tilemap info callback * *************************************/ static TILE_GET_INFO( get_tile_info ) { m52_state *state = machine.driver_data(); UINT8 video = state->m_videoram[tile_index]; UINT8 color = state->m_colorram[tile_index]; int flag = 0; int code = 0; code = video; if (color & 0x80) { code |= 0x100; } if (tile_index / 32 <= 6) { flag |= TILE_FORCE_LAYER0; /* lines 0 to 6 are opaqe? */ } SET_TILE_INFO(0, code, color & 0x3f, flag); } /************************************* * * Video startup * *************************************/ VIDEO_START( m52 ) { m52_state *state = machine.driver_data(); state->m_bg_tilemap = tilemap_create(machine, get_tile_info, tilemap_scan_rows, 8, 8, 32, 32); tilemap_set_transparent_pen(state->m_bg_tilemap, 0); tilemap_set_scrolldx(state->m_bg_tilemap, 128 - 1, -1); tilemap_set_scrolldy(state->m_bg_tilemap, 16, 16); tilemap_set_scroll_rows(state->m_bg_tilemap, 4); /* only lines 192-256 scroll */ state->save_item(NAME(state->m_bg1xpos)); state->save_item(NAME(state->m_bg1ypos)); state->save_item(NAME(state->m_bg2xpos)); state->save_item(NAME(state->m_bg2ypos)); state->save_item(NAME(state->m_bgcontrol)); } /************************************* * * Scroll registers * *************************************/ WRITE8_HANDLER( m52_scroll_w ) { m52_state *state = space->machine().driver_data(); /* According to the schematics there is only one video register that holds the X scroll value with a NAND gate on the V64 and V128 lines to control when it's read, and when 255 (via 8 pull up resistors) is used. So we set the first 3 quarters to 255 and the last to the scroll value */ tilemap_set_scrollx(state->m_bg_tilemap, 0, 255); tilemap_set_scrollx(state->m_bg_tilemap, 1, 255); tilemap_set_scrollx(state->m_bg_tilemap, 2, 255); tilemap_set_scrollx(state->m_bg_tilemap, 3, -data); } /************************************* * * Video RAM write handlers * *************************************/ WRITE8_HANDLER( m52_videoram_w ) { m52_state *state = space->machine().driver_data(); state->m_videoram[offset] = data; tilemap_mark_tile_dirty(state->m_bg_tilemap, offset); } WRITE8_HANDLER( m52_colorram_w ) { m52_state *state = space->machine().driver_data(); state->m_colorram[offset] = data; tilemap_mark_tile_dirty(state->m_bg_tilemap, offset); } /************************************* * * Custom protection * *************************************/ /* This looks like some kind of protection implemented by a custom chip on the scroll board. It mangles the value written to the port m52_bg1xpos_w, as follows: result = popcount(value & 0x7f) ^ (value >> 7) */ READ8_HANDLER( m52_protection_r ) { m52_state *state = space->machine().driver_data(); int popcount = 0; int temp; for (temp = state->m_bg1xpos & 0x7f; temp != 0; temp >>= 1) popcount += temp & 1; return popcount ^ (state->m_bg1xpos >> 7); } /************************************* * * Background control write handlers * *************************************/ WRITE8_HANDLER( m52_bg1ypos_w ) { m52_state *state = space->machine().driver_data(); state->m_bg1ypos = data; } WRITE8_HANDLER( m52_bg1xpos_w ) { m52_state *state = space->machine().driver_data(); state->m_bg1xpos = data; } WRITE8_HANDLER( m52_bg2xpos_w ) { m52_state *state = space->machine().driver_data(); state->m_bg2xpos = data; } WRITE8_HANDLER( m52_bg2ypos_w ) { m52_state *state = space->machine().driver_data(); state->m_bg2ypos = data; } WRITE8_HANDLER( m52_bgcontrol_w ) { m52_state *state = space->machine().driver_data(); state->m_bgcontrol = data; } /************************************* * * Outputs * *************************************/ WRITE8_HANDLER( m52_flipscreen_w ) { /* screen flip is handled both by software and hardware */ flip_screen_set(space->machine(), (data & 0x01) ^ (~input_port_read(space->machine(), "DSW2") & 0x01)); coin_counter_w(space->machine(), 0, data & 0x02); coin_counter_w(space->machine(), 1, data & 0x20); } WRITE8_HANDLER( alpha1v_flipscreen_w ) { flip_screen_set(space->machine(), data & 0x01); } /************************************* * * Background rendering * *************************************/ static void draw_background(running_machine &machine, bitmap_t *bitmap, const rectangle *cliprect, int xpos, int ypos, int image) { rectangle rect; const rectangle &visarea = machine.primary_screen->visible_area(); if (flip_screen_get(machine)) { xpos = 255 - xpos; ypos = 255 - ypos - BGHEIGHT; } xpos += 128; /* this may not be correct */ ypos = ypos + (22 - 8); drawgfx_transpen(bitmap, cliprect, machine.gfx[image], 0, 0, flip_screen_get(machine), flip_screen_get(machine), xpos, ypos, 0); drawgfx_transpen(bitmap, cliprect, machine.gfx[image], 0, 0, flip_screen_get(machine), flip_screen_get(machine), xpos - 256, ypos, 0); rect.min_x = visarea.min_x; rect.max_x = visarea.max_x; if (flip_screen_get(machine)) { rect.min_y = ypos - BGHEIGHT; rect.max_y = ypos - 1; } else { rect.min_y = ypos + BGHEIGHT; rect.max_y = ypos + 2 * BGHEIGHT - 1; } bitmap_fill(bitmap, &rect, machine.gfx[image]->color_base + 3); } /************************************* * * Video render * *************************************/ SCREEN_UPDATE( m52 ) { m52_state *state = screen->machine().driver_data(); int offs; bitmap_fill(bitmap, cliprect, 0); if (!(state->m_bgcontrol & 0x20)) { if (!(state->m_bgcontrol & 0x10)) draw_background(screen->machine(), bitmap, cliprect, state->m_bg2xpos, state->m_bg2ypos, 2); /* distant mountains */ if (!(state->m_bgcontrol & 0x02)) draw_background(screen->machine(), bitmap, cliprect, state->m_bg1xpos, state->m_bg1ypos, 3); /* hills */ if (!(state->m_bgcontrol & 0x04)) draw_background(screen->machine(), bitmap, cliprect, state->m_bg1xpos, state->m_bg1ypos, 4); /* cityscape */ } tilemap_set_flip(state->m_bg_tilemap, flip_screen_get(screen->machine()) ? TILEMAP_FLIPX | TILEMAP_FLIPY : 0); tilemap_draw(bitmap, cliprect, state->m_bg_tilemap, 0, 0); /* draw the sprites */ for (offs = 0xfc; offs >= 0; offs -= 4) { int sy = 257 - state->m_spriteram[offs]; int color = state->m_spriteram[offs + 1] & 0x3f; int flipx = state->m_spriteram[offs + 1] & 0x40; int flipy = state->m_spriteram[offs + 1] & 0x80; int code = state->m_spriteram[offs + 2]; int sx = state->m_spriteram[offs + 3]; rectangle clip; /* sprites from offsets $00-$7F are processed in the upper half of the frame */ /* sprites from offsets $80-$FF are processed in the lower half of the frame */ clip = *cliprect; if (!(offs & 0x80)) clip.min_y = 0, clip.max_y = 127; else clip.min_y = 128, clip.max_y = 255; /* adjust for flipping */ if (flip_screen_get(screen->machine())) { int temp = clip.min_y; clip.min_y = 255 - clip.max_y; clip.max_y = 255 - temp; flipx = !flipx; flipy = !flipy; sx = 240 - sx; sy = 257 + 11 - sy; } sx += 128; /* in theory anyways; in practice, some of the molecule-looking guys get clipped */ #ifdef SPLIT_SPRITES sect_rect(&clip, cliprect); #else clip = *cliprect; #endif drawgfx_transmask(bitmap, &clip, screen->machine().gfx[1], code, color, flipx, flipy, sx, sy, colortable_get_transpen_mask(screen->machine().colortable, screen->machine().gfx[1], color, 512 + 32)); } return 0; }