// license:BSD-3-Clause // copyright-holders:Mark McDougall, Angelo Salese /* * Yamaha YGV608 - PVDC2 Pattern mode Video Display Controller 2 * - Mark McDougall * * Notes: * ====== * * This implementation is far from complete. * There's enough here to emulate Namco ND-1 games. * Some functionality is missing, some is incomplete. * Also missing for ND-1 is rotation and scaling (cosmetic only). * * It could also do with some optimisation for speed! * * (Still lots of debugging info/options in here!) * * TODO: soon * create tilemaps at vh_start time and switch between at runtime * mark tiles dirty when VRAM is written to instead of dirtying entire * screen each frame * * T.B.D. (not critical to ND-1) * ====== * * Rotation * Scaling * Split-screen scrolling by row (by column supported) (see test mode) * Everything else! :) * * TODO (2017-2018 edition): * - move ports into device_address_map (done); * - add registers into own space, improve naming and variable usage; * - remove code repetition in tilemap drawing functions; * - add crtc section (done partially); * - fix garbage tiles in Mappy Arrange (done) * - fix tile encryption for Abnormal Check (sets extra bit in cuskey); * nopping bit 0 writes to 0x40081e makes gfxs to draw better!? * - fix Gynotai row scroll glitches; * - fix attract mode garbage for Namco Collection Vol. 2 (either transparent or page banking select registers) (done); * - fix tilemap dirty flags, move tilemap data in own space prolly helps; * - DMA from/to ROM; * - color palette accessors presumably accesses an internal RAMDAC with controllable auto-increment, convert to that; * - fix char getting cut off from GAME SELECT msg in NCV2 (done, sprite wraparound for sx & sy); * - clean-ups & documentation; * * */ #include "emu.h" #include "video/ygv608.h" #include "screen.h" // TODO: move these into enums // R#7(md) #define MD_2PLANE_8BIT 0x00 #define MD_2PLANE_16BIT 0x01 #define MD_1PLANE_16COLOUR 0x02 #define MD_1PLANE_256COLOUR 0x03 #define MD_1PLANE (MD_1PLANE_16COLOUR & MD_1PLANE_256COLOUR) #define MD_SHIFT 0 #define MD_MASK 0x03 // R#8 #define PGS_64X32 0x0 #define PGS_32X64 0x1 #define PGS_SHIFT 0 #define PGS_MASK 0x01 // R#9 #define SLV_SCREEN 0x00 #define SLV_8 0x04 #define SLV_16 0x05 #define SLV_32 0x06 #define SLV_64 0x07 #define SLH_SCREEN 0x00 #define SLH_8 0x04 #define SLH_16 0x05 #define SLH_32 0x06 #define SLH_64 0x07 #define PTS_8X8 0x00 #define PTS_16X16 0x01 #define PTS_32X32 0x02 #define PTS_64X64 0x03 // R#10 #define SPAS_SPRITESIZE false #define SPAS_SPRITEREVERSE true // R#10(spas)=1 #define SZ_8X8 0x00 #define SZ_16X16 0x01 #define SZ_32X32 0x02 #define SZ_64X64 0x03 // R#10(spas)=0 #define SZ_NOREVERSE 0x00 #define SZ_VERTREVERSE 0x01 #define SZ_HORIZREVERSE 0x02 #define SZ_BOTHREVERSE 0x03 // R#11(prm) #define PRM_SABDEX 0x00 #define PRM_ASBDEX 0x01 #define PRM_SEABDX 0x02 #define PRM_ASEBDX 0x03 // R#40 #define HDW_SHIFT 0 #define HDW_MASK 0x3f // R#44 #define VDW_SHIFT 0 #define VDW_MASK 0x3f #define _ENABLE_SPRITES #define _ENABLE_SCROLLX #define _ENABLE_SCROLLY //#define _ENABLE_SCREEN_RESIZE //#define _SHOW_VIDEO_DEBUG #define GFX_8X8_4BIT 0 #define GFX_16X16_4BIT 1 #define GFX_32X32_4BIT 2 #define GFX_64X64_4BIT 3 #define GFX_8X8_8BIT 4 #define GFX_16X16_8BIT 5 //************************************************************************** // GLOBAL VARIABLES //************************************************************************** // device type definition DEFINE_DEVICE_TYPE(YGV608, ygv608_device, "ygv608", "YGV608 VDP") /* text-layer characters */ static const uint32_t pts_4bits_layout_xoffset[64] = { STEP8( 0*256, 4 ), STEP8( 1*256, 4 ), STEP8( 4*256, 4 ), STEP8( 5*256, 4 ), STEP8( 16*256, 4 ), STEP8( 17*256, 4 ), STEP8( 20*256, 4 ), STEP8( 21*256, 4 ) }; static const uint32_t pts_4bits_layout_yoffset[64] = { STEP8( 0*256, 8*4 ), STEP8( 2*256, 8*4 ), STEP8( 8*256, 8*4 ), STEP8( 10*256, 8*4 ), STEP8( 32*256, 8*4 ), STEP8( 34*256, 8*4 ), STEP8( 40*256, 8*4 ), STEP8( 42*256, 8*4 ) }; static const gfx_layout pts_8x8_4bits_layout = { 8,8, /* 8*8 pixels */ RGN_FRAC(1,1), /* 65536 patterns */ 4, /* 4 bits per pixel */ { 0, 1, 2, 3 }, EXTENDED_XOFFS, EXTENDED_YOFFS, 8*8*4, pts_4bits_layout_xoffset, pts_4bits_layout_yoffset }; static const gfx_layout pts_16x16_4bits_layout = { 16,16, /* 16*16 pixels */ RGN_FRAC(1,1), /* 16384 patterns */ 4, /* 4 bits per pixel */ { 0, 1, 2, 3 }, EXTENDED_XOFFS, EXTENDED_YOFFS, 16*16*4, pts_4bits_layout_xoffset, pts_4bits_layout_yoffset }; static const gfx_layout pts_32x32_4bits_layout = { 32,32, /* 32*32 pixels */ RGN_FRAC(1,1), /* 4096 patterns */ 4, /* 4 bits per pixel */ { 0, 1, 2, 3 }, EXTENDED_XOFFS, EXTENDED_YOFFS, 32*32*4, pts_4bits_layout_xoffset, pts_4bits_layout_yoffset }; static const gfx_layout pts_64x64_4bits_layout = { 64,64, /* 32*32 pixels */ RGN_FRAC(1,1), /* 1024 patterns */ 4, /* 4 bits per pixel */ { 0, 1, 2, 3 }, EXTENDED_XOFFS, EXTENDED_YOFFS, 64*64*4, pts_4bits_layout_xoffset, pts_4bits_layout_yoffset }; static const gfx_layout pts_8x8_8bits_layout = { 8,8, /* 8*8 pixels */ RGN_FRAC(1,1), /* 32768 patterns */ 8, /* 8 bits per pixel */ { 0, 1, 2, 3, 4, 5, 6, 7 }, { STEP8( 0*512, 8 ) }, { STEP8( 0*512, 8*8 ) }, 8*8*8 }; static const gfx_layout pts_16x16_8bits_layout = { 16,16, /* 16*16 pixels */ RGN_FRAC(1,1), /* 8192 patterns */ 8, /* 8 bits per pixel */ { 0, 1, 2, 3, 4, 5, 6, 7 }, { STEP8( 0*512, 8 ), STEP8( 1*512, 8 ) }, { STEP8( 0*512, 8*8 ), STEP8( 2*512, 8*8 ) }, 16*16*8 }; static GFXDECODE_START( gfx_ygv608 ) GFXDECODE_DEVICE( DEVICE_SELF, 0x00000000, pts_8x8_4bits_layout, 0, 16 ) GFXDECODE_DEVICE( DEVICE_SELF, 0x00000000, pts_16x16_4bits_layout, 0, 16 ) GFXDECODE_DEVICE( DEVICE_SELF, 0x00000000, pts_32x32_4bits_layout, 0, 16 ) GFXDECODE_DEVICE( DEVICE_SELF, 0x00000000, pts_64x64_4bits_layout, 0, 16 ) GFXDECODE_DEVICE( DEVICE_SELF, 0x00000000, pts_8x8_8bits_layout, 0, 1 ) GFXDECODE_DEVICE( DEVICE_SELF, 0x00000000, pts_16x16_8bits_layout, 0, 1 ) GFXDECODE_END //************************************************************************** // LIVE DEVICE //************************************************************************** /*************************************** * * Internal I/O register structure * ***************************************/ // we use decimals here to match documentation void ygv608_device::regs_map(address_map &map) { // address pointers map(0, 0).rw(FUNC(ygv608_device::pattern_name_table_y_r), FUNC(ygv608_device::pattern_name_table_y_w)); map(1, 1).rw(FUNC(ygv608_device::pattern_name_table_x_r), FUNC(ygv608_device::pattern_name_table_x_w)); map(2, 2).rw(FUNC(ygv608_device::ram_access_ctrl_r), FUNC(ygv608_device::ram_access_ctrl_w)); map(3, 3).rw(FUNC(ygv608_device::sprite_address_r), FUNC(ygv608_device::sprite_address_w)); map(4, 4).rw(FUNC(ygv608_device::scroll_address_r), FUNC(ygv608_device::scroll_address_w)); map(5, 5).rw(FUNC(ygv608_device::palette_address_r), FUNC(ygv608_device::palette_address_w)); map(6, 6).rw(FUNC(ygv608_device::sprite_bank_r), FUNC(ygv608_device::sprite_bank_w)); // screen control map(7, 7).rw(FUNC(ygv608_device::screen_ctrl_7_r), FUNC(ygv608_device::screen_ctrl_7_w)); map(8, 8).rw(FUNC(ygv608_device::screen_ctrl_8_r), FUNC(ygv608_device::screen_ctrl_8_w)); map(9, 9).rw(FUNC(ygv608_device::screen_ctrl_9_r), FUNC(ygv608_device::screen_ctrl_9_w)); map(10, 10).rw(FUNC(ygv608_device::screen_ctrl_10_r), FUNC(ygv608_device::screen_ctrl_10_w)); map(11, 11).rw(FUNC(ygv608_device::screen_ctrl_11_r), FUNC(ygv608_device::screen_ctrl_11_w)); map(12, 12).rw(FUNC(ygv608_device::screen_ctrl_12_r), FUNC(ygv608_device::screen_ctrl_12_w)); map(13, 13).w(FUNC(ygv608_device::border_color_w)); // interrupt section map(14, 14).rw(FUNC(ygv608_device::irq_mask_r), FUNC(ygv608_device::irq_mask_w)); map(15, 16).rw(FUNC(ygv608_device::irq_ctrl_r), FUNC(ygv608_device::irq_ctrl_w)); // base address map(17, 24).w(FUNC(ygv608_device::base_address_w)); // ROZ parameters map(25, 27).w(FUNC(ygv608_device::roz_ax_w)); map(28, 29).w(FUNC(ygv608_device::roz_dx_w)); map(30, 31).w(FUNC(ygv608_device::roz_dxy_w)); map(32, 34).w(FUNC(ygv608_device::roz_ay_w)); map(35, 36).w(FUNC(ygv608_device::roz_dy_w)); map(37, 38).w(FUNC(ygv608_device::roz_dyx_w)); // CRTC map(39, 46).w(FUNC(ygv608_device::crtc_w)); // 47-48 ROM transfer control - DMA source address // 49 ROM transfer control - DMA size } /*************************************** * * Port Interface map * ***************************************/ void ygv608_device::port_map(address_map &map) { map(0x00, 0x00).rw(FUNC(ygv608_device::pattern_name_table_r), FUNC(ygv608_device::pattern_name_table_w)); map(0x01, 0x01).rw(FUNC(ygv608_device::sprite_data_r), FUNC(ygv608_device::sprite_data_w)); map(0x02, 0x02).rw(FUNC(ygv608_device::scroll_data_r), FUNC(ygv608_device::scroll_data_w)); map(0x03, 0x03).rw(FUNC(ygv608_device::palette_data_r), FUNC(ygv608_device::palette_data_w)); map(0x04, 0x04).rw(FUNC(ygv608_device::register_data_r), FUNC(ygv608_device::register_data_w)); map(0x05, 0x05).nopr().w(FUNC(ygv608_device::register_select_w)); map(0x06, 0x06).rw(FUNC(ygv608_device::status_port_r), FUNC(ygv608_device::status_port_w)); map(0x07, 0x07).rw(FUNC(ygv608_device::system_control_r), FUNC(ygv608_device::system_control_w)); } //------------------------------------------------- // ygv608_device - constructor //------------------------------------------------- ygv608_device::ygv608_device( const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock ) : device_t(mconfig, YGV608, tag, owner, clock), device_gfx_interface(mconfig, *this, gfx_ygv608, DEVICE_SELF), device_memory_interface(mconfig, *this), device_palette_interface(mconfig, *this), device_video_interface(mconfig, *this), m_io_space_config("io", ENDIANNESS_BIG, 8, 6, 0, address_map_constructor(FUNC(ygv608_device::regs_map), this)), m_namcond1_gfxbank(0), m_tilemap_A(nullptr), m_tilemap_B(nullptr), m_work_bitmap(0), m_bits16(0), m_page_x(0), m_page_y(0), m_pny_shift(0), m_na8_mask(0), m_col_shift(0), m_base_y_shift(0), m_screen_resize(false), m_tilemap_resize(false), m_color_state_r(0), m_color_state_w(0), m_p0_state(0), m_pattern_name_base_r(0), m_pattern_name_base_w(0), m_screen_status(0), m_dma_status(0), m_register_address(0), m_register_autoinc_r(false), m_register_autoinc_w(false), m_raster_irq_mask(false), m_vblank_irq_mask(false), m_raster_irq_hpos(0), m_raster_irq_vpos(0), m_raster_irq_mode(false), m_scroll_address(0), m_palette_address(0), m_sprite_address(0), m_sprite_bank(0), m_xtile_ptr(0), m_ytile_ptr(0), m_xtile_autoinc(false), m_ytile_autoinc(false), m_plane_select_access(false), m_mosaic_aplane(0), m_mosaic_bplane(0), m_sprite_disable(0), m_sprite_aux_mode(0), m_sprite_aux_reg(0), m_border_color(0), m_saar(false), m_saaw(false), m_scar(false), m_scaw(false), m_cpar(false), m_cpaw(false), m_ba_plane_scroll_select(false), m_dspe(false), m_md(0), m_zron(false), m_flip(false), m_dckm(false), m_page_size(false), m_h_display_size(0), m_v_display_size(0), m_roz_wrap_disable(false), m_scroll_wrap_disable(false), m_pattern_size(0), m_h_div_size(0), m_v_div_size(0), m_planeA_trans_enable(false), m_planeB_trans_enable(false), m_priority_mode(0), m_cbdr(false), m_yse(false), m_scm(0), m_planeA_color_fetch(0), m_planeB_color_fetch(0), m_sprite_color_fetch(0), m_vblank_handler(*this), m_raster_handler(*this), m_vblank_timer(nullptr), m_raster_timer(nullptr), m_ax(0), m_dx(0), m_dxy(0), m_ay(0), m_dy(0), m_dyx(0), m_raw_ax(0), m_raw_dx(0), m_raw_dxy(0), m_raw_ay(0), m_raw_dy(0), m_raw_dyx(0) { std::fill(std::begin(m_pattern_name_table), std::end(m_pattern_name_table), 0); std::fill(std::begin(m_tilemap_A_cache_8), std::end(m_tilemap_A_cache_8), nullptr); std::fill(std::begin(m_tilemap_A_cache_16), std::end(m_tilemap_A_cache_16), nullptr); std::fill(std::begin(m_tilemap_B_cache_8), std::end(m_tilemap_B_cache_8), nullptr); std::fill(std::begin(m_tilemap_B_cache_16), std::end(m_tilemap_B_cache_16), nullptr); for (int i = 0; i < 2; i++) { std::fill(std::begin(m_scroll_data_table[i]), std::end(m_scroll_data_table[i]), 0); std::fill(std::begin(m_base_addr[i]), std::end(m_base_addr[i]), 0); } for (int i = 0; i < 256; i++) std::fill(std::begin(m_colour_palette[i]), std::end(m_colour_palette[i]), 0); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void ygv608_device::device_start() { // memset(&m_ports, 0, sizeof(m_ports)); // memset(&m_regs, 0, sizeof(m_regs)); memset(&m_pattern_name_table, 0, sizeof(m_pattern_name_table)); memset(&m_sprite_attribute_table, 0, sizeof(m_sprite_attribute_table)); memset(&m_scroll_data_table, 0, sizeof(m_scroll_data_table)); memset(&m_colour_palette, 0, sizeof(m_colour_palette)); m_bits16 = 0; m_page_x = 0; m_page_y = 0; m_pny_shift = 0; m_na8_mask = 0; m_col_shift = 0; m_ax = 0; m_dx = 0; m_dxy = 0; m_ay = 0; m_dy = 0; m_dyx = 0; memset(&m_base_addr, 0, sizeof(m_base_addr)); m_base_y_shift = 0; // flag rebuild of the tilemaps m_screen_resize = true; m_tilemap_resize = true; m_namcond1_gfxbank = 0; save_item(NAME(m_namcond1_gfxbank)); /* create tilemaps of all sizes and combinations */ m_tilemap_A_cache_8[0] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(ygv608_device::get_tile_info_A_8)), tilemap_mapper_delegate(*this, FUNC(ygv608_device::get_tile_offset)), 8,8, 32,32); m_tilemap_A_cache_8[1] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(ygv608_device::get_tile_info_A_8)), tilemap_mapper_delegate(*this, FUNC(ygv608_device::get_tile_offset)), 8,8, 64,32); m_tilemap_A_cache_8[2] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(ygv608_device::get_tile_info_A_8)), tilemap_mapper_delegate(*this, FUNC(ygv608_device::get_tile_offset)), 8,8, 32,64); m_tilemap_A_cache_16[0] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(ygv608_device::get_tile_info_A_16)), tilemap_mapper_delegate(*this, FUNC(ygv608_device::get_tile_offset)), 16,16, 32,32); m_tilemap_A_cache_16[1] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(ygv608_device::get_tile_info_A_16)), tilemap_mapper_delegate(*this, FUNC(ygv608_device::get_tile_offset)), 16,16, 64,32); m_tilemap_A_cache_16[2] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(ygv608_device::get_tile_info_A_16)), tilemap_mapper_delegate(*this, FUNC(ygv608_device::get_tile_offset)), 16,16, 32,64); m_tilemap_B_cache_8[0] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(ygv608_device::get_tile_info_B_8)), tilemap_mapper_delegate(*this, FUNC(ygv608_device::get_tile_offset)), 8,8, 32,32); m_tilemap_B_cache_8[1] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(ygv608_device::get_tile_info_B_8)), tilemap_mapper_delegate(*this, FUNC(ygv608_device::get_tile_offset)), 8,8, 64,32); m_tilemap_B_cache_8[2] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(ygv608_device::get_tile_info_B_8)), tilemap_mapper_delegate(*this, FUNC(ygv608_device::get_tile_offset)), 8,8, 32,64); m_tilemap_B_cache_16[0] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(ygv608_device::get_tile_info_B_16)), tilemap_mapper_delegate(*this, FUNC(ygv608_device::get_tile_offset)), 16,16, 32,32); m_tilemap_B_cache_16[1] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(ygv608_device::get_tile_info_B_16)), tilemap_mapper_delegate(*this, FUNC(ygv608_device::get_tile_offset)), 16,16, 64,32); m_tilemap_B_cache_16[2] = &machine().tilemap().create(*this, tilemap_get_info_delegate(*this, FUNC(ygv608_device::get_tile_info_B_16)), tilemap_mapper_delegate(*this, FUNC(ygv608_device::get_tile_offset)), 16,16, 32,64); m_tilemap_A = nullptr; m_tilemap_B = nullptr; m_iospace = &space(AS_IO); // TODO: tagging configuration m_vblank_handler.resolve(); m_raster_handler.resolve(); m_vblank_timer = timer_alloc(VBLANK_TIMER); m_raster_timer = timer_alloc(RASTER_TIMER); register_state_save(); } //------------------------------------------------- // memory_space_config - return a description of // any address spaces owned by this device //------------------------------------------------- device_memory_interface::space_config_vector ygv608_device::memory_space_config() const { return space_config_vector { std::make_pair(AS_IO, &m_io_space_config) }; } inline void ygv608_device::vblank_irq_check() { if(m_vblank_irq_mask == true && m_screen_status & 8) m_vblank_handler(ASSERT_LINE); } inline void ygv608_device::raster_irq_check() { if(m_raster_irq_mask == true && m_screen_status & 0x10) m_raster_handler(ASSERT_LINE); } void ygv608_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { switch(id) { case VBLANK_TIMER: { m_screen_status |= 8; // FV vblank_irq_check(); break; } case RASTER_TIMER: { m_screen_status |= 0x10; // FP raster_irq_check(); // adjust for next one shot m_raster_timer->reset(); m_raster_timer->adjust(raster_sync_offset(), 0); break; } } } void ygv608_device::set_gfxbank(uint8_t gfxbank) { m_namcond1_gfxbank = gfxbank; m_tilemap_resize = true; } inline int ygv608_device::get_col_division(int raw_col) { if((m_v_div_size & 4) == 0) return 0; return ((raw_col >> m_col_shift) * 2) & 0x7f; } inline int ygv608_device::get_row_division(int raw_row) { if(m_h_div_size == 0) return 0; return (raw_row & (m_page_y/2 - 1)) * 2; } TILEMAP_MAPPER_MEMBER( ygv608_device::get_tile_offset ) { // this optimisation is not much good to us, // since we really need row,col in the get_tile_info() routines // - so just pack them into a uint32_t return( ( col << 6 ) | row ); } #define layout_total(x) \ (gfx(x)->elements()) TILE_GET_INFO_MEMBER( ygv608_device::get_tile_info_A_8 ) { // extract row,col packed into tile_index int col = tile_index >> 6; int row = tile_index & 0x3f; int translated_column = get_col_division(col); uint8_t attr = 0; int pattern_name_base = 0; int set = (m_md == MD_1PLANE_256COLOUR ? GFX_8X8_8BIT : GFX_8X8_4BIT ); int base = row >> m_base_y_shift; if( col >= m_page_x ) { SET_TILE_INFO_MEMBER(set, 0, 0, 0 ); } else if( row >= m_page_y ) { SET_TILE_INFO_MEMBER(set, 0, 0, 0 ); } else { int sx, sy, page; int i = pattern_name_base + (((row << m_pny_shift) + col) << m_bits16); int j = m_pattern_name_table[i]; int f = 0; if( m_bits16 ) { j += ((int)(m_pattern_name_table[i+1] & m_na8_mask )) << 8; // attribute only valid in 16 color mode if (set == GFX_8X8_4BIT) attr = m_pattern_name_table[i+1] >> 4; if (m_flip == true) { if (m_pattern_name_table[i+1] & (1<<3)) f |= TILE_FLIPX; if (m_pattern_name_table[i+1] & (1<<2)) f |= TILE_FLIPY; } } /* calculate page according to scroll data */ /* - assuming full-screen scroll only for now... */ if (m_v_div_size) { page = 0; } else { sy = (int)m_scroll_data_table[0][translated_column] + (((int)m_scroll_data_table[0][translated_column+1] & 0x0f ) << 8); sx = (int)m_scroll_data_table[0][0x80] + (((int)m_scroll_data_table[0][0x81] & 0x0f ) << 8); if (m_md == MD_2PLANE_16BIT) { page = ( ( sx + col * 8 ) % 1024 ) / 256; page += ( ( ( sy + row * 8 ) % 2048 ) / 256 ) * 4; } else if (m_page_size) { page = ( ( sx + col * 8 ) % 2048 ) / 512; page += ( ( ( sy + row * 8 ) % 2048 ) / 256 ) * 4; } else { page = ( ( sx + col * 8 ) % 2048 ) / 256; page += ( ( ( sy + row * 8 ) % 2048 ) / 512 ) * 8; } } page &= 0x1f; /* add page, base address to pattern name */ j += ( (int)m_scroll_data_table[0][0xc0+page] << 10 ); j += ( m_base_addr[0][base] << 8 ); if( j >= layout_total(set) ) { logerror( "A_8X8: tilemap=%d\n", j ); j = 0; } if (m_planeA_color_fetch != 0) { // attribute only valid in 16 color mode if( set == GFX_8X8_4BIT ) attr = ( j >> ( (m_planeA_color_fetch - 1 ) * 2 ) ) & 0x0f; } // banking if (set == GFX_8X8_4BIT) { j += m_namcond1_gfxbank * 0x10000; } else // 8x8x8 { j += m_namcond1_gfxbank * 0x8000; } SET_TILE_INFO_MEMBER(set, j, attr & 0x0F, f ); } } TILE_GET_INFO_MEMBER( ygv608_device::get_tile_info_B_8 ) { // extract row,col packed into tile_index int col = tile_index >> 6; int row = tile_index & 0x3f; int translated_column = get_col_division(col); uint8_t attr = 0; int pattern_name_base = ( ( m_page_y << m_pny_shift ) << m_bits16 ); int set = GFX_8X8_4BIT; int base = row >> m_base_y_shift; if (m_md & MD_1PLANE ) { SET_TILE_INFO_MEMBER(set, 0, 0, 0 ); } else if (col >= m_page_x) { SET_TILE_INFO_MEMBER(set, 0, 0, 0 ); } else if (row >= m_page_y) { SET_TILE_INFO_MEMBER(set, 0, 0, 0 ); } else { int sx, sy, page; int i = pattern_name_base + (((row << m_pny_shift) + col) << m_bits16); int j = m_pattern_name_table[i]; int f = 0; if (m_bits16) { j += ((int)(m_pattern_name_table[i+1] & m_na8_mask )) << 8; attr = m_pattern_name_table[i+1] >> 4; /*& 0x00; 0xf0;*/ if (m_flip == true) { if (m_pattern_name_table[i+1] & (1<<3)) f |= TILE_FLIPX; if (m_pattern_name_table[i+1] & (1<<2)) f |= TILE_FLIPY; } } /* calculate page according to scroll data */ /* - assuming full-screen scroll only for now... */ if (m_v_div_size) { page = 0; } else { sy = (int)m_scroll_data_table[1][translated_column] + (((int)m_scroll_data_table[1][translated_column+1] & 0x0f ) << 8); sx = (int)m_scroll_data_table[1][0x80] + (((int)m_scroll_data_table[1][0x81] & 0x0f ) << 8); if (m_md == MD_2PLANE_16BIT) { page = ( ( sx + col * 8 ) % 1024 ) / 256; page += ( ( ( sy + row * 8 ) % 2048 ) / 256 ) * 4; } else if (m_page_size) { page = ( ( sx + col * 8 ) % 2048 ) / 512; page += ( ( ( sy + row * 8 ) % 2048 ) / 256 ) * 4; } else { page = ( ( sx + col * 8 ) % 2048 ) / 256; page += ( ( ( sy + row * 8 ) % 2048 ) / 512 ) * 8; } } page &= 0x1f; /* add page, base address to pattern name */ j += ( (int)m_scroll_data_table[1][0xc0+page] << 10 ); j += ( m_base_addr[1][base] << 8 ); if( j >= layout_total(set) ) { logerror( "B_8X8: tilemap=%d\n", j ); j = 0; } if (m_planeB_color_fetch != 0) { uint8_t color = (m_planeB_color_fetch); /* assume 16 colour mode for now... */ attr = ( j >> ( (color - 1 ) * 2 ) ) & 0x0f; } // banking if (set == GFX_8X8_4BIT) { j += m_namcond1_gfxbank * 0x10000; } else // 8x8x8 { j += m_namcond1_gfxbank * 0x8000; } SET_TILE_INFO_MEMBER(set, j, attr, f ); } } TILE_GET_INFO_MEMBER( ygv608_device::get_tile_info_A_16 ) { // extract row,col packed into tile_index int col = tile_index >> 6; int row = tile_index & 0x3f; int translated_column = get_col_division(col); uint8_t attr = 0; int pattern_name_base = 0; int set = (m_md == MD_1PLANE_256COLOUR ? GFX_16X16_8BIT : GFX_16X16_4BIT ); int base = row >> m_base_y_shift; if( col >= m_page_x ) { SET_TILE_INFO_MEMBER(set, 0, 0, 0 ); } else if( row >= m_page_y ) { SET_TILE_INFO_MEMBER(set, 0, 0, 0 ); } else { int sx, sy, page; int j; int i = ( ( ( row << m_pny_shift ) + col ) << m_bits16 ); int f = 0; i += pattern_name_base; j = m_pattern_name_table[i]; if( m_bits16 ) { j += ((int)(m_pattern_name_table[i+1] & m_na8_mask )) << 8; // attribute only valid in 16 color mode if( set == GFX_16X16_4BIT ) attr = m_pattern_name_table[i+1] >> 4; if (m_flip == true) { if (m_pattern_name_table[i+1] & (1<<3)) f |= TILE_FLIPX; if (m_pattern_name_table[i+1] & (1<<2)) f |= TILE_FLIPY; } } /* calculate page according to scroll data */ /* - assuming full-screen scroll only for now... */ if (m_v_div_size) { page = 0; } else { sy = (int)m_scroll_data_table[0][translated_column] + (((int)m_scroll_data_table[0][translated_column+1] & 0x0f ) << 8); sx = (int)m_scroll_data_table[0][0x80] + (((int)m_scroll_data_table[0][0x81] & 0x0f ) << 8); if (m_md == MD_2PLANE_16BIT) { page = ( ( sx + col * 16 ) % 2048 ) / 512; page += ( ( sy + row * 16 ) / 512 ) * 4; } else if (m_page_size) { page = ( sx + col * 16 ) / 512; page += ( ( sy + row * 16 ) / 1024 ) * 8; } else { page = ( sx + col * 16 ) / 1024; page += ( ( sy + row * 16 ) / 512 ) * 4; } } page &= 0x1f; /* add page, base address to pattern name */ j += ( (int)m_scroll_data_table[0][0xc0+page] << 8 ); j += ( m_base_addr[0][base] << 8 ); if( j >= layout_total(set) ) { logerror( "A_16X16: tilemap=%d\n", j ); j = 0; } if (m_planeA_color_fetch != 0) { // attribute only valid in 16 color mode if( set == GFX_16X16_4BIT ) attr = ( j >> ( m_planeA_color_fetch * 2 ) ) & 0x0f; } // banking if (set == GFX_16X16_4BIT) { j += m_namcond1_gfxbank * 0x4000; } else // 8x8x8 { j += m_namcond1_gfxbank * 0x2000; } SET_TILE_INFO_MEMBER(set, j, attr, f ); } } TILE_GET_INFO_MEMBER( ygv608_device::get_tile_info_B_16 ) { // extract row,col packed into tile_index int col = tile_index >> 6; int row = tile_index & 0x3f; int translated_column = get_col_division(col); uint8_t attr = 0; int pattern_name_base = ( ( m_page_y << m_pny_shift ) << m_bits16 ); int set = GFX_16X16_4BIT; int base = row >> m_base_y_shift; if(m_md & MD_1PLANE ) { SET_TILE_INFO_MEMBER(set, 0, 0, 0 ); } if( col >= m_page_x ) { SET_TILE_INFO_MEMBER(set, 0, 0, 0 ); } else if( row >= m_page_y ) { SET_TILE_INFO_MEMBER(set, 0, 0, 0 ); } else { int sx, sy, page; int j; int i = ( ( ( row << m_pny_shift ) + col ) << m_bits16 ); int f = 0; i += pattern_name_base; j = m_pattern_name_table[i]; if( m_bits16 ) { j += ((int)(m_pattern_name_table[i+1] & m_na8_mask )) << 8; attr = m_pattern_name_table[i+1] >> 4; /*& 0x00; 0xf0;*/ if (m_flip == true) { if (m_pattern_name_table[i+1] & (1<<3)) f |= TILE_FLIPX; if (m_pattern_name_table[i+1] & (1<<2)) f |= TILE_FLIPY; } } /* calculate page according to scroll data */ /* - assuming full-screen scroll only for now... */ if (m_v_div_size) { page = 0; } else { sy = (int)m_scroll_data_table[1][translated_column] + (((int)m_scroll_data_table[1][translated_column+1] & 0x0f ) << 8); sx = (int)m_scroll_data_table[1][0x80] + (((int)m_scroll_data_table[1][0x81] & 0x0f ) << 8); if (m_md == MD_2PLANE_16BIT) { page = ( ( sx + col * 16 ) % 2048 ) / 512; page += ( ( sy + row * 16 ) / 512 ) * 4; } else if (m_page_size) { page = ( sx + col * 16 ) / 512; page += ( ( sy + row * 16 ) / 1024 ) * 8; } else { page = ( sx + col * 16 ) / 1024; page += ( ( sy + row * 16 ) / 512 ) * 4; } } page &= 0x1f; /* add page, base address to pattern name */ j += ( (int)m_scroll_data_table[1][0xc0+page] << 8 ); j += ( m_base_addr[1][base] << 8 ); if( j >= layout_total(set) ) { logerror( "B_16X16: tilemap=%d\n", j ); j = 0; } if (m_planeB_color_fetch != 0) { uint8_t color = (m_planeB_color_fetch); /* assume 16 colour mode for now... */ attr = ( j >> (color * 2)) & 0x0f; } // banking if (set == GFX_16X16_4BIT) { j += m_namcond1_gfxbank * 0x4000; } else // 8x8x8 { j += m_namcond1_gfxbank * 0x2000; } SET_TILE_INFO_MEMBER(set, j, attr, f ); } } void ygv608_device::device_post_load() { m_screen_resize = true; m_tilemap_resize = true; } void ygv608_device::register_state_save() { // save_item(NAME(m_ports.b)); // save_item(NAME(m_regs.b)); save_item(NAME(m_pattern_name_table)); save_item(NAME(m_sprite_attribute_table.b)); save_item(NAME(m_scroll_data_table)); save_item(NAME(m_colour_palette)); save_item(NAME(m_color_state_r)); save_item(NAME(m_color_state_w)); save_item(NAME(m_bits16)); save_item(NAME(m_page_x)); save_item(NAME(m_page_y)); save_item(NAME(m_pny_shift)); save_item(NAME(m_na8_mask)); save_item(NAME(m_col_shift)); save_item(NAME(m_base_addr)); save_item(NAME(m_base_y_shift)); save_item(NAME(m_screen_resize)); save_item(NAME(m_tilemap_resize)); save_item(NAME(m_p0_state)); save_item(NAME(m_pattern_name_base_r)); save_item(NAME(m_pattern_name_base_w)); save_item(NAME(m_screen_status)); save_item(NAME(m_dma_status)); save_item(NAME(m_register_address)); save_item(NAME(m_register_autoinc_r)); save_item(NAME(m_register_autoinc_w)); save_item(NAME(m_raster_irq_mask)); save_item(NAME(m_vblank_irq_mask)); save_item(NAME(m_raster_irq_hpos)); save_item(NAME(m_raster_irq_vpos)); save_item(NAME(m_raster_irq_mode)); save_item(NAME(m_scroll_address)); save_item(NAME(m_palette_address)); save_item(NAME(m_sprite_address)); save_item(NAME(m_sprite_bank)); save_item(NAME(m_xtile_ptr)); save_item(NAME(m_ytile_ptr)); save_item(NAME(m_xtile_autoinc)); save_item(NAME(m_ytile_autoinc)); save_item(NAME(m_plane_select_access)); save_item(NAME(m_mosaic_aplane)); save_item(NAME(m_mosaic_bplane)); save_item(NAME(m_sprite_disable)); save_item(NAME(m_sprite_aux_mode)); save_item(NAME(m_sprite_aux_reg)); save_item(NAME(m_border_color)); save_item(NAME(m_saar)); save_item(NAME(m_saaw)); save_item(NAME(m_scar)); save_item(NAME(m_scaw)); save_item(NAME(m_cpar)); save_item(NAME(m_cpaw)); save_item(NAME(m_ba_plane_scroll_select)); save_item(NAME(m_dspe)); save_item(NAME(m_md)); save_item(NAME(m_zron)); save_item(NAME(m_flip)); save_item(NAME(m_dckm)); save_item(NAME(m_page_size)); save_item(NAME(m_h_display_size)); save_item(NAME(m_v_display_size)); save_item(NAME(m_roz_wrap_disable)); save_item(NAME(m_scroll_wrap_disable)); save_item(NAME(m_pattern_size)); save_item(NAME(m_h_div_size)); save_item(NAME(m_v_div_size)); save_item(NAME(m_planeA_trans_enable)); save_item(NAME(m_planeB_trans_enable)); save_item(NAME(m_priority_mode)); save_item(NAME(m_cbdr)); save_item(NAME(m_yse)); save_item(NAME(m_scm)); save_item(NAME(m_planeA_color_fetch)); save_item(NAME(m_planeB_color_fetch)); save_item(NAME(m_sprite_color_fetch)); save_item(NAME(m_crtc.htotal)); save_item(NAME(m_crtc.vtotal)); save_item(NAME(m_crtc.display_hstart)); save_item(NAME(m_crtc.display_vstart)); save_item(NAME(m_crtc.display_width)); save_item(NAME(m_crtc.display_height)); save_item(NAME(m_crtc.display_hsync)); save_item(NAME(m_crtc.display_vsync)); save_item(NAME(m_crtc.border_width)); save_item(NAME(m_crtc.border_height)); save_item(NAME(m_ax)); save_item(NAME(m_dx)); save_item(NAME(m_dxy)); save_item(NAME(m_ay)); save_item(NAME(m_dy)); save_item(NAME(m_dyx)); save_item(NAME(m_raw_ax)); save_item(NAME(m_raw_dx)); save_item(NAME(m_raw_dxy)); save_item(NAME(m_raw_ay)); save_item(NAME(m_raw_dy)); save_item(NAME(m_raw_dyx)); } void ygv608_device::draw_sprites(bitmap_ind16 &bitmap, const rectangle &cliprect) { #ifdef _ENABLE_SPRITES const int sprite_limits[4] = { 512-8, 512-16, 512-32, 512-64 }; const uint32_t spritebank_size[4] = { 0x10000, 0x4000, 0x1000, 0x400 }; const int sprite_shift[4] = { 8, 6, 4, 2 }; const int sprite_mask[4] = { 0xff, 0xfc, 0xf0, 0xc0 }; const int spf_shift[4] = { -1, 0, +1, +2 }; // sprites are always clipped to 512x512 // - regardless of the visible display dimensions rectangle spriteClip(0, 512, 0, 512); SPRITE_ATTR *sa; int flipx = 0, flipy = 0; int i; /* ensure that sprites are enabled */ if( (m_dspe == false ) || (m_sprite_disable == true) ) return; /* draw sprites */ spriteClip &= cliprect; sa = &m_sprite_attribute_table.s[MAX_SPRITES-1]; for( i=0; iattr >> 4) & 0x0f; sx = ( (int)(sa->attr & 0x02) << 7 ) | (int)sa->sx; sy = ( ( ( (int)(sa->attr & 0x01) << 8 ) | (int)sa->sy ) + 1 ) & 0x1ff; attr = (sa->attr & 0x0c) >> 2; g_attr = m_sprite_aux_reg & 3; spf = m_sprite_color_fetch; if (m_sprite_aux_mode == SPAS_SPRITESIZE ) { size = g_attr; flipx = (attr & SZ_HORIZREVERSE) != 0; flipy = (attr & SZ_VERTREVERSE) != 0; } else { size = attr; flipx = (g_attr & SZ_HORIZREVERSE) != 0; flipy = (g_attr & SZ_VERTREVERSE) != 0; } // calculate code and apply sprite base address code = ( (int)(m_sprite_bank & sprite_mask[size]) << sprite_shift[size] ) | (int)sa->sn; // apply spf to color (invalidates individual attribute bits for color) if (spf != 0) color = ( code >> ( (spf + spf_shift[size]) * 2 ) ) & 0x0f; // check code boundary (TODO: do we really need this?) if( code >= layout_total(size) ) { logerror( "SZ_%d: sprite=%d\n", size, code ); code = 0; } // draw the sprite gfx(size)->transpen(bitmap,spriteClip, code+m_namcond1_gfxbank*spritebank_size[size], color, flipx,flipy, sx,sy,0x00); // draw with wraparound if(sx > sprite_limits[size] || sy > sprite_limits[size] ) { gfx(size)->transpen(bitmap,spriteClip, code+m_namcond1_gfxbank*spritebank_size[size], color, flipx,flipy, sx-512,sy,0x00); gfx(size)->transpen(bitmap,spriteClip, code+m_namcond1_gfxbank*spritebank_size[size], color, flipx,flipy, sx,sy-512,0x00); gfx(size)->transpen(bitmap,spriteClip, code+m_namcond1_gfxbank*spritebank_size[size], color, flipx,flipy, sx-512,sy-512,0x00); } } #endif } #ifdef _SHOW_VIDEO_DEBUG static const char *const mode[] = { "2PLANE_8BIT", "2PLANE_16BIT", "1PLANE_16COLORS", "1PLANE_256COLORS" }; static const char *const psize[] = { "8x8", "16x16", "32x32", "64x64" }; #endif inline void ygv608_device::draw_layer_roz(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, tilemap_t *source_tilemap) { //int xc, yc; //double r, alpha, sin_theta, cos_theta; //const rectangle &visarea = screen.visible_area(); uint32_t sx, sy; int ba_select = (source_tilemap == m_tilemap_A) ? 0 : 1; sy = (int)m_scroll_data_table[ba_select][0x00] + (((int)m_scroll_data_table[ba_select][0x01] & 0x0f ) << 8); sx = (int)m_scroll_data_table[ba_select][0x80] + (((int)m_scroll_data_table[ba_select][0x81] & 0x0f ) << 8); if( m_zron == true ) { // old code, for reference. //xc = m_ax >> 16; //yc = m_ay >> 16; //r = sqrt( (double)( xc * xc + yc * yc ) ); //alpha = atan( (double)xc / (double)yc ); //sin_theta = (double)m_dyx / (double)0x10000; //cos_theta = (double)m_dx / (double)0x10000; if (m_v_div_size) { sx = (sx & 0x1FF) ? (sx - 0x200) : 0; sy = (sy & 0x1FF) ? (sy - 0x200) : 0; } source_tilemap->draw_roz(screen, bitmap, cliprect, m_ax + (sx << 16), m_ay + (sy << 16), m_dx, m_dyx, m_dxy, m_dy, m_roz_wrap_disable == false, 0, 0 ); } else source_tilemap->draw(screen, bitmap, cliprect, 0, 0 ); } void ygv608_device::ygv608_draw_mosaic(bitmap_ind16 &bitmap, const rectangle &cliprect, int n) { int x, y, mask; if (n <= 0) { return; } // mask to drop the lowest n-bits mask = ~((1 << n) - 1); for (y = cliprect.min_y; y <= cliprect.max_y; y++) { for (x = cliprect.min_x; x <= cliprect.max_x; x++) { bitmap.pix16(y, x) = bitmap.pix16(y & mask, x & mask); } } } uint32_t ygv608_device::update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { #ifdef _SHOW_VIDEO_DEBUG char buffer[64]; #endif #ifdef _ENABLE_SCROLLY int col; #endif #ifdef _ENABLE_SCROLLX int row; #endif rectangle finalclip; const rectangle &visarea = screen.visible_area(); // clip to the current bitmap finalclip.set(0, screen.width() - 1, 0, screen.height() - 1); finalclip &= cliprect; // TODO: black/transparent pen if CBDR is 1 and border color is 0 bitmap.fill(m_border_color, visarea ); // punt if not initialized if (m_page_x == 0 || m_page_y == 0) { return 0; } if( m_screen_resize ) { m_work_bitmap.resize(screen.width(), screen.height()); // reset resize flag m_screen_resize = false; } if( m_tilemap_resize ) { int index; /* based on the page sizes, pick an index */ if (m_page_x == 64) index = 1; else if (m_page_y == 64) index = 2; else index = 0; if (m_pattern_size == PTS_8X8 ) m_tilemap_A = m_tilemap_A_cache_8[index]; else m_tilemap_A = m_tilemap_A_cache_16[index]; m_tilemap_A->mark_all_dirty(); m_tilemap_A->set_transparent_pen(m_border_color); if (m_h_div_size == 0) { m_tilemap_A->set_scroll_cols(m_page_x); m_tilemap_A->set_scroll_rows(1); } else { m_tilemap_A->set_scroll_cols(1); m_tilemap_A->set_scroll_rows(m_page_y); } if (m_pattern_size == PTS_8X8 ) m_tilemap_B = m_tilemap_B_cache_8[index]; else m_tilemap_B = m_tilemap_B_cache_16[index]; m_tilemap_B->mark_all_dirty(); if (m_h_div_size == 0) { m_tilemap_B->set_scroll_cols(m_page_x); m_tilemap_B->set_scroll_rows(1); } else { m_tilemap_B->set_scroll_cols(1); m_tilemap_B->set_scroll_rows(m_page_y); } // now clear the screen in case we change to 1-plane mode m_work_bitmap.fill(0, finalclip ); // reset resize flag m_tilemap_resize = false; } #ifdef _ENABLE_SCROLLY for( col=0; colset_scrolly(col, ( (int)m_scroll_data_table[1][translated_column] + ( (int)m_scroll_data_table[1][translated_column+1] << 8 ) ) ); m_tilemap_A->set_scrolly(col, ( (int)m_scroll_data_table[0][translated_column] + ( (int)m_scroll_data_table[0][translated_column+1] << 8 ) ) ); } #endif #ifdef _ENABLE_SCROLLX for( row=0; rowset_scrollx(row, ( (int)m_scroll_data_table[1][translated_row+0x80] + ( (int)m_scroll_data_table[1][translated_row+0x81] << 8 ) ) ); m_tilemap_A->set_scrollx(row, ( (int)m_scroll_data_table[0][translated_row+0x80] + ( (int)m_scroll_data_table[0][translated_row+0x81] << 8 ) ) ); } #endif m_tilemap_A->enable(m_dspe == true); if(m_md & MD_1PLANE ) m_tilemap_B->enable(0); else m_tilemap_B->enable(m_dspe == true); m_tilemap_A->mark_all_dirty(); m_tilemap_B->mark_all_dirty(); /* * now we can render the screen */ // LBO - need to implement proper pen marking for sprites as well as set aside a non-transparent // pen to be used for background fills when plane B is disabled. if (m_md & MD_1PLANE) { // If the background tilemap is disabled, we need to clear the bitmap to black m_work_bitmap.fill(0, finalclip); // m_work_bitmap.fill(1, *visarea); } else { draw_layer_roz(screen, m_work_bitmap, finalclip, m_tilemap_B); if(m_mosaic_bplane > 0) ygv608_draw_mosaic(m_work_bitmap, finalclip, m_mosaic_bplane); if(m_planeB_trans_enable == true) copybitmap_trans( bitmap, m_work_bitmap, 0, 0, 0, 0, finalclip, 0); else copybitmap( bitmap, m_work_bitmap, 0, 0, 0, 0, finalclip); } // for some reason we can't use an opaque m_tilemap_A // so use a transparent but clear the work bitmap first // - look at why this is the case?!? m_work_bitmap.fill(0, visarea ); if (m_priority_mode == PRM_ASBDEX || m_priority_mode == PRM_ASEBDX ) draw_sprites(bitmap, finalclip); draw_layer_roz(screen, m_work_bitmap, finalclip, m_tilemap_A); if(m_mosaic_aplane > 0) ygv608_draw_mosaic(m_work_bitmap, finalclip, m_mosaic_aplane); if(m_planeA_trans_enable == true) copybitmap_trans( bitmap, m_work_bitmap, 0, 0, 0, 0, finalclip, 0); else copybitmap( bitmap, m_work_bitmap, 0, 0, 0, 0, finalclip); if (m_priority_mode == PRM_SABDEX || m_priority_mode == PRM_SEABDX) draw_sprites(bitmap,finalclip ); #ifdef _SHOW_VIDEO_DEBUG /* show screen control information */ ui_draw_text( mode[m_md], 0, 0 ); sprintf( buffer, "%02ux%02u", m_page_x, m_page_y ); ui_draw_text( buffer, 0, 16 ); ui_draw_text( psize[m_pattern_size], 0, 32 ); sprintf( buffer, "A: SX:%d SY:%d", (int)m_scroll_data_table[0][0x80] + ( ( (int)m_scroll_data_table[0][0x81] & 0x0f ) << 8 ), (int)m_scroll_data_table[0][0x00] + ( ( (int)m_scroll_data_table[0][0x01] & 0x0f ) << 8 ) ); ui_draw_text( buffer, 0, 48 ); sprintf( buffer, "B: SX:%d SY:%d", (int)m_scroll_data_table[1][0x80] + ( ( (int)m_scroll_data_table[1][0x81] & 0x0f ) << 8 ), (int)m_scroll_data_table[1][0x00] + ( ( (int)m_scroll_data_table[1][0x01] & 0x0f ) << 8 ) ); ui_draw_text( buffer, 0, 64 ); #endif return 0; } /*************************************** * * Port Interface routines * ****************************************/ // P#0R - pattern name table data port READ8_MEMBER( ygv608_device::pattern_name_table_r ) { int pn = 0; switch (m_p0_state) { case 0: /* Are we reading from plane B? */ if (!(m_md & MD_1PLANE) && (m_plane_select_access == true)) m_pattern_name_base_r = ((m_page_y << m_pny_shift) << m_bits16); /* read character from ram */ pn = m_pattern_name_base_r + (((m_ytile_ptr << m_pny_shift) + m_xtile_ptr) << m_bits16); break; case 1: /* read character from ram */ pn = m_pattern_name_base_r + (((m_ytile_ptr << m_pny_shift) + m_xtile_ptr) << m_bits16) + 1; break; } if (pn > 4095) { logerror( "attempt (%d) to read pattern name %d\n" "mode = %d, pgs = %d (%dx%d)\n" "m_pattern_name_base_r = %d\n" "pnx = %d, pny = %d, pny_shift = %d, bits16 = %d\n", m_p0_state, pn, m_md, m_page_size, m_page_x, m_page_y, m_pattern_name_base_r, m_xtile_ptr, m_ytile_ptr, m_pny_shift, m_bits16 ); pn = 0; } m_p0_state++; if (m_md == MD_2PLANE_8BIT ) m_p0_state++; if (m_p0_state == 2) { pattern_name_autoinc_check(); m_p0_state = 0; m_pattern_name_base_r = 0; } return m_pattern_name_table[pn]; } // P#1R - sprite data port READ8_MEMBER( ygv608_device::sprite_data_r ) { uint8_t res = m_sprite_attribute_table.b[m_sprite_address]; if (m_saar == true) m_sprite_address++; return res; } // P#2R - scroll data port READ8_MEMBER( ygv608_device::scroll_data_r ) { uint8_t res = m_scroll_data_table[m_ba_plane_scroll_select][m_scroll_address]; if (m_scar == true) { m_scroll_address++; /* handle wrap to next plane */ if (m_scroll_address == 0) m_ba_plane_scroll_select ^= 1; } return res; } // P#3 - color palette data port READ8_MEMBER( ygv608_device::palette_data_r ) { uint8_t res = m_colour_palette[m_palette_address][m_color_state_r]; if( ++m_color_state_r == 3 ) { m_color_state_r = 0; if(m_cpar == true) m_palette_address++; } return res; } // P#4R - register data port READ8_MEMBER(ygv608_device::register_data_r) { int regNum = m_register_address & 0x3f; uint8_t res = m_iospace->read_byte(regNum); if (m_register_autoinc_r == true) { m_register_address ++; m_register_address &= 0x3f; #if 0 // we'll catch this in the logerror anyway if (regNum == 50) { regNum = 0; logerror( "warning: rn=50 after read increment\n" ); } #endif } return res; } // P#6R - status port /*** * ---x ---- FP Specified display position flag (R#15 & 16), reset by writing '1' * ---- x--- FV Vertical border interval start, reset by writing '1' * ---- -x-- FC Sprite collision flag, reset by writing '1' * ---- --x- HB 1 when horizontal border or retrace is in progress (read only) * ---- ---x VB 1 when vertical border or retrace is in progress (read only) ***/ READ8_MEMBER( ygv608_device::status_port_r ) { // TODO: we need to use h/vpos in case of border support instead due of how MAME framework works here. return (m_screen_status & 0x1c) | (screen().hblank()<<1) | screen().vblank(); } // P#7R - system control port READ8_MEMBER( ygv608_device::system_control_r ) { return m_dma_status; } // P#0W - pattern name table data write WRITE8_MEMBER(ygv608_device::pattern_name_table_w) { int pn = 0; switch (m_p0_state) { case 0: /* Are we reading from plane B? */ if (!(m_md & MD_1PLANE) && (m_plane_select_access == true)) m_pattern_name_base_w = ((m_page_y << m_pny_shift) << m_bits16); /* read character from ram */ pn = m_pattern_name_base_w + (((m_ytile_ptr << m_pny_shift) + m_xtile_ptr) << m_bits16); break; case 1: /* read character from ram */ pn = m_pattern_name_base_w + (((m_ytile_ptr << m_pny_shift) + m_xtile_ptr) << m_bits16) + 1; break; } if (pn > 4095) { logerror( "attempt (%d) to write pattern name %d\n" "mode = %d, pgs = %d (%dx%d)\n" "m_pattern_name_base_w = %d\n" "pnx = %d, pny = %d, pny_shift = %d, bits16 = %d\n", m_p0_state, pn, m_md, m_page_size, m_page_x, m_page_y, m_pattern_name_base_w, m_xtile_ptr, m_ytile_ptr, m_pny_shift, m_bits16 ); pn = 0; } m_pattern_name_table[pn] = data; m_p0_state++; if (m_md == MD_2PLANE_8BIT ) m_p0_state++; if (m_p0_state == 2) { pattern_name_autoinc_check(); m_p0_state = 0; m_pattern_name_base_w = 0; } } inline void ygv608_device::pattern_name_autoinc_check() { uint8_t xTile = m_xtile_ptr; uint8_t yTile = m_ytile_ptr; if (m_ytile_autoinc == true) { // we are incrementing in Y direction if (yTile++ == (m_page_y - 1)) { yTile = 0; if (xTile++ == (m_page_x - 1)) { xTile = 0; m_plane_select_access ^= 1; // flip A/B plane } } m_ytile_ptr = yTile; m_xtile_ptr = xTile; } else if (m_xtile_autoinc == true) { // we are incrementing in X direction if (xTile++ == (m_page_x - 1)) { xTile = 0; if (yTile++ == (m_page_y - 1)) { yTile = 0; m_plane_select_access ^= 1; // flip A/B plane } } m_ytile_ptr = yTile; m_xtile_ptr = xTile; } } // P#1W - sprite data port WRITE8_MEMBER( ygv608_device::sprite_data_w ) { m_sprite_attribute_table.b[m_sprite_address] = data; if( m_saaw == true) m_sprite_address++; } // P#2W - scroll data port WRITE8_MEMBER( ygv608_device::scroll_data_w ) { m_scroll_data_table[m_ba_plane_scroll_select][m_scroll_address] = data; if (m_scaw == true) { m_scroll_address++; /* handle wrap to next plane */ if (m_scroll_address == 0) m_ba_plane_scroll_select ^= 1; } } // P#3W - colour palette data port WRITE8_MEMBER( ygv608_device::palette_data_w ) { m_colour_palette[m_palette_address][m_color_state_w] = data; if (++m_color_state_w == 3) { m_color_state_w = 0; // if(m_colour_palette[m_palette_address][0] & 0x80) // Transparency designation, none of the Namco games enables it? set_pen_color(m_palette_address, pal6bit( m_colour_palette[m_palette_address][0] ), pal6bit( m_colour_palette[m_palette_address][1] ), pal6bit( m_colour_palette[m_palette_address][2] )); if(m_cpaw == true) m_palette_address++; } } // P#4W - register data port WRITE8_MEMBER( ygv608_device::register_data_w ) { uint8_t regNum = m_register_address & 0x3f; //logerror( "R#%d = $%02X\n", regNum, data ); m_iospace->write_byte(regNum, data); if (m_register_autoinc_w == true) { m_register_address ++; m_register_address &= 0x3f; #if 0 // we'll catch this in the logerror anyway if (regNum == 50) { regNum = 0; logerror( "warning: rn=50 after write increment\n" ); } #endif } } // P#5W - register select port WRITE8_MEMBER( ygv608_device::register_select_w ) { m_register_address = data & 0x3f; m_register_autoinc_r = BIT(data,6); m_register_autoinc_w = BIT(data,7); } // P#6W - status port WRITE8_MEMBER( ygv608_device::status_port_w ) { /* writing a '1' resets that bit */ m_screen_status &= ~data; // send an irq ack to the delegates accordingly if(data & 8) m_vblank_handler(CLEAR_LINE); if(data & 0x10) m_raster_handler(CLEAR_LINE); } // P#7W - system control port WRITE8_MEMBER( ygv608_device::system_control_w ) { m_dma_status = data; if (m_dma_status & 0x3e) HandleRomTransfers(data & 0x3e); if (m_dma_status & 0x01) HandleReset(); } // TODO: actual timing of this void ygv608_device::HandleReset() { int i; /* Clear ports #0-7 */ //memset( &m_ports.b[0], 0, 8 ); // most likely variables to be reset here from ports, there might be more m_pattern_name_base_w = 0; m_pattern_name_base_r = 0; m_register_address = 0; m_register_autoinc_r = false; m_register_autoinc_w = false; /* Clear registers #0-38, #47-49 */ for(i=0;i<39;i++) m_iospace->write_byte(i, 0x00); for(i=47;i<50;i++) m_iospace->write_byte(i, 0x00); // memset( &m_regs.b[0], 0, 39 ); // memset( &m_regs.b[47], 0, 3 ); /* Clear internal ram */ memset( m_pattern_name_table, 0, 4096 ); memset( m_sprite_attribute_table.b, 0, SPRITE_ATTR_TABLE_SIZE ); memset( m_scroll_data_table, 0, 2*256 ); memset( m_colour_palette, 0, 256*3 ); } /* The YGV608 has a function to block-move data from the rom into internal tables. This function is not used in NCV1, but I used it for testing trojan ROM software. - So leave it in! */ void ygv608_device::HandleRomTransfers(uint8_t type) { popmessage("ROM DMA used %02x",type); #if 0 // TODO: eventually update this code to latest static uint8_t *sdt = (uint8_t *)m_scroll_data_table; static uint8_t *sat = (uint8_t *)m_sprite_attribute_table.b; /* fudge copy from sprite data for now... */ uint8_t *RAM = machine.memory_region[0]; int i; int src = ( ( (int)m_regs.s.tb13 << 8 ) + (int)m_regs.s.tb5 ) << 5; int bytes = (int)m_regs.s.tn4 << 4; logerror( "Transferring data from rom...\n" ); /* pattern name table */ if( m_ports.s.tn ) { } /* scroll table */ if( m_ports.s.tl ) { int dest = (int)m_regs.s.sca; if( m_regs.s.p2_b_a ) dest += 0x100; /* fudge a transfer for now... */ for( i=0; i= m_page_y) // logerror ("%s:setting pny(%d) >= page_y(%d)\n", machine().describe_context(), // yTile, m_page_y ); m_ytile_ptr &= m_page_y -1; m_ytile_autoinc = BIT(data,7); m_plane_select_access = BIT(data,6); // TODO: done by Dig Dug Original if(m_ytile_autoinc == true && m_xtile_autoinc == true) logerror("%s: Warning both X/Y Tiles autoinc enabled!\n",this->tag()); } // R#1R - Pattern Name Table Access pointer X READ8_MEMBER( ygv608_device::pattern_name_table_x_r ) { return (m_xtile_autoinc << 7) | m_xtile_ptr; } // R#1W - Pattern Name Table Access pointer X WRITE8_MEMBER( ygv608_device::pattern_name_table_x_w ) { m_xtile_ptr = data & 0x3f; //if (xTile >= m_page_x) // logerror ("%s:setting pnx(%d) >= page_x(%d)\n", machine().describe_context(), // xTile, m_page_x ); m_xtile_ptr &= m_page_x -1; m_xtile_autoinc = BIT(data,7); // TODO: done by Dig Dug Original if(m_ytile_autoinc == true && m_xtile_autoinc == true) logerror("%s: Warning both X/Y Tiles autoinc enabled!\n",this->tag()); } // R#2R - Built in RAM access control /*** * x--- ---- CPAW Address autoincrements after color palette write * -x-- ---- CPAR Address autoincrements after color palette read * ---x ---- B/(A) P#2 plane access select (1=B Plane) * ---- x--- SCAW Address autoincrements after scroll data write * ---- -x-- SCAR Address autoincrements after scroll data read * ---- --x- SAAW Address autoincrements after sprite attribute table write * ---- ---x SAAR Address autoincrements after sprite attribute table read ***/ READ8_MEMBER( ygv608_device::ram_access_ctrl_r ) { return (m_cpaw<<7) | (m_cpar<<6) | (m_ba_plane_scroll_select<<4) | (m_scaw<<3) | (m_scar<<2) | (m_saaw<<1) | (m_saar<<0); } // R#2W - Built in RAM access control WRITE8_MEMBER( ygv608_device::ram_access_ctrl_w ) { m_saar = BIT(data,0); m_saaw = BIT(data,1); m_scar = BIT(data,2); m_scaw = BIT(data,3); m_ba_plane_scroll_select = BIT(data,4); m_cpar = BIT(data,6); m_cpaw = BIT(data,7); } // R#3R - sprite attribute table access pointer READ8_MEMBER( ygv608_device::sprite_address_r ) { return m_sprite_address; } // R#3W - sprite attribute table access pointer WRITE8_MEMBER( ygv608_device::sprite_address_w ) { m_sprite_address = data; } // R#4R - scroll table access pointer READ8_MEMBER( ygv608_device::scroll_address_r ) { return m_scroll_address; } // R#4W - scroll table access pointer WRITE8_MEMBER( ygv608_device::scroll_address_w ) { m_scroll_address = data; } // R#5R - color palette access pointer READ8_MEMBER( ygv608_device::palette_address_r ) { return m_palette_address; } // R#5W - color palette access pointer WRITE8_MEMBER( ygv608_device::palette_address_w ) { m_palette_address = data; } // R#6R - sprite generator base address READ8_MEMBER( ygv608_device::sprite_bank_r ) { return m_sprite_bank; } // R#6W - sprite generator base address WRITE8_MEMBER( ygv608_device::sprite_bank_w ) { m_sprite_bank = data; } // R#7R - screen control 7 /*** * x--- ---- DCKM dot clock frequency (0 = 1/2 1 = 1/4) * -x-- ---- FLIP reverse display in pattern name * ---- x--- ZRON enables ROZ features * ---- -xx- MDx planes display mode * ---- -11- 1 plane/256 colors (16 bits) * ---- -10- 1 plane/16 colors (16 bits) * ---- -01- 2 planes/16 bits * ---- -00- 2 planes/8 bits * ---- ---x DSPE display permission of pattern planes (screen blanked if 0) ***/ READ8_MEMBER( ygv608_device::screen_ctrl_7_r ) { return (m_dckm<<7)|(m_flip<<6)| (m_zron<<3)|((m_md & 3)<<1)|(m_dspe<<0); } // R#7W - screen control 7 WRITE8_MEMBER( ygv608_device::screen_ctrl_7_w ) { uint8_t new_md = (data >> 1) & 3; if( new_md != m_md) m_tilemap_resize = true; m_dckm = BIT(data,7); m_flip = BIT(data,6); m_zron = BIT(data,3); m_md = new_md; m_dspe = BIT(data,0); m_na8_mask = ((m_flip == true) ? 0x03 : 0x0f ); // changing mode resets the pattern name table states (Mappy Arrange) m_p0_state = 0; pattern_mode_setup(); // TODO: add dot clock into CRTC // screen_configure(); } inline void ygv608_device::pattern_mode_setup() { m_bits16 = (m_md == MD_2PLANE_8BIT ? 0 : 1 ); if(m_md == MD_2PLANE_16BIT ) m_page_x = m_page_y = 32; else { if (m_page_size == false ) { m_page_x = 64; m_page_y = 32; } else { m_page_x = 32; m_page_y = 64; } } m_pny_shift = ( m_page_x == 32 ? 5 : 6 ); /* bits to shift pattern y coordinate to extract base */ m_base_y_shift = ( m_page_y == 32 ? 2 : 3 ); } // R#8R - screen control 8 /*** * xx-- ---- HDS horizontal display domain size (0=4096, 3=512) * --xx ---- VDS vertical display domain size (0=4096, 3=512) * ---- x--- RLRT ROZ wraparound disable * ---- -x-- RLSC scroll wraparound disable * ---- ---x PGS page size (0=64x32, 1=32x64; Mode 2=32x32) ***/ READ8_MEMBER( ygv608_device::screen_ctrl_8_r ) { return (m_h_display_size<<6)|(m_v_display_size<<4)| (m_roz_wrap_disable<<3)|(m_scroll_wrap_disable<<2)| (m_page_size<<0); } // R#8W - screen control 8 WRITE8_MEMBER( ygv608_device::screen_ctrl_8_w ) { if( (data & 1) != m_page_size) m_tilemap_resize = true; /**/m_h_display_size = (data >> 6) & 3; /**/m_v_display_size = (data >> 4) & 3; m_roz_wrap_disable = BIT(data,3); /**/m_scroll_wrap_disable = BIT(data,2); m_page_size = BIT(data,0); pattern_mode_setup(); } // R#9R - screen control 9 /*** * xx-- ---- PTS: pattern size in pattern planes (8x8, 16x16, 32x32, 64x64) * --xx x--- SLH: size of horizontal division in screen division scrolling * ---- -xxx SLV: size of vertical division in screen division scrolling * ---- -111 64 dots division * ---- -110 32 dots division * ---- -101 16 dots division * ---- -100 8 dots division * ---- -000 entire screen ***/ READ8_MEMBER( ygv608_device::screen_ctrl_9_r ) { return (m_pattern_size<<6)| (m_h_div_size<<3)|(m_v_div_size<<0); } WRITE8_MEMBER( ygv608_device::screen_ctrl_9_w ) { uint8_t new_pts = (data >> 6) & 3; if(new_pts != m_pattern_size) m_tilemap_resize = true; m_pattern_size = new_pts; /**/m_h_div_size = (data >> 3) & 7; m_v_div_size = (data >> 0) & 7; //popmessage("%02x %02x",m_h_div_size,m_v_div_size); // TODO: this code is garbage ... if(m_v_div_size == 0) m_col_shift = 8; else { if (m_pattern_size == PTS_8X8 ) m_col_shift = (m_v_div_size) - 4; else m_col_shift = (m_v_div_size) - 5; if( m_col_shift < 0 ) { // we can't handle certain conditions logerror( "Unhandled slv condition (pts=$%X,slv=$%X)\n", m_pattern_size, m_v_div_size); m_col_shift = 8; } } } // R#10R - screen control 10: mosaic & sprite /*** * xx-- ---- SPAx: Auxiliary bits of sprite attribute table (0=8x8 or no flip, 1=16x16 or flipy, 2=32x32 or flipx, 3=64x64 or flipx & y ) * --x- ---- SPAS: Auxiliary function select (0=SPAx selects sprite size, 1=SPAx selects flipping) * ---x ---- SPRD: Sprite display disable * ---- xx-- MCBx: Mosaic enable on plane B * ---- --xx MCAx: Mosaic enable on plane A ***/ READ8_MEMBER( ygv608_device::screen_ctrl_10_r ) { return (m_sprite_aux_reg << 6) | ((m_sprite_aux_mode == true) << 5) | ((m_sprite_disable == true) << 4) | (m_mosaic_bplane << 2) | (m_mosaic_aplane & 3); } // R#10W - screen control: mosaic & sprite WRITE8_MEMBER( ygv608_device::screen_ctrl_10_w ) { m_sprite_aux_reg = (data & 0xc0) >> 6; m_sprite_aux_mode = BIT(data, 5); m_sprite_disable = BIT(data, 4); // check mosaic m_mosaic_bplane = (data & 0xc) >> 2; m_mosaic_aplane = data & 3; // if(m_mosaic_aplane || m_mosaic_bplane) // popmessage("Mosaic effect %02x %02x",m_mosaic_aplane,m_mosaic_bplane); } // R#11R - screen control 11 READ8_MEMBER( ygv608_device::screen_ctrl_11_r ) { return (m_scm<<6)|(m_yse<<5)|(m_cbdr<<4)| (m_priority_mode<<2)|(m_planeB_trans_enable<<1)|(m_planeA_trans_enable<<0); } // R#11W - screen control 11 WRITE8_MEMBER( ygv608_device::screen_ctrl_11_w ) { /**/m_scm = (data >> 6) & 3; /**/m_yse = BIT(data,5); /**/m_cbdr = BIT(data,4); m_priority_mode = (data >> 2) & 3; m_planeB_trans_enable = BIT(data,1); m_planeA_trans_enable = BIT(data,0); } // R#12R - screen control 12: color fetch modes READ8_MEMBER( ygv608_device::screen_ctrl_12_r ) { return (m_sprite_color_fetch<<6)|(m_planeB_color_fetch<<3)|(m_planeA_color_fetch<<0); } // R#12W - screen control 12: color fetch modes WRITE8_MEMBER( ygv608_device::screen_ctrl_12_w ) { m_sprite_color_fetch = (data >> 6) & 3; m_planeB_color_fetch = (data >> 3) & 7; m_planeA_color_fetch = (data >> 0) & 7; } // R#13W - border color WRITE8_MEMBER( ygv608_device::border_color_w ) { m_border_color = data; } // R#14R interrupt mask control READ8_MEMBER( ygv608_device::irq_mask_r ) { return (m_raster_irq_mask << 1) | (m_vblank_irq_mask << 0); } // R#14W interrupt mask control WRITE8_MEMBER( ygv608_device::irq_mask_w ) { m_vblank_irq_mask = BIT(data, 0); m_raster_irq_mask = BIT(data, 1); // check if we have an irq in the queue vblank_irq_check(); raster_irq_check(); } // R#15R / R#16R raster interrupt control READ8_MEMBER( ygv608_device::irq_ctrl_r ) { uint8_t res; if(offset == 0) // R#15 res = m_raster_irq_vpos & 0xff; else // R#16 { res = (m_raster_irq_mode << 7); res|= (BIT(m_raster_irq_vpos, 8) << 6); res|= (m_raster_irq_hpos / 32) & 0x1f; } return res; } // R#15W / R#16W raster interrupt control WRITE8_MEMBER( ygv608_device::irq_ctrl_w ) { if(offset == 0) // R#15 { m_raster_irq_vpos &= ~0xff; m_raster_irq_vpos |= data & 0xff; } else // R#16 { m_raster_irq_mode = BIT(data,7); m_raster_irq_vpos &= ~0x100; m_raster_irq_vpos |= BIT(data,6) << 8; m_raster_irq_hpos = (data & 0x1f) * 32; } // reset raster timer m_raster_timer->reset(); m_raster_timer->adjust(raster_sync_offset(), 0); //printf("%d %d %d %d %d\n",m_raster_irq_hpos,m_raster_irq_vpos,m_raster_irq_mode,m_crtc.htotal,m_crtc.vtotal); } // helper for validating and convert to screen position attotime ygv608_device::raster_sync_offset() { // don't care if h/v pos is higher than CRTC params (NCV2 POST) if(m_raster_irq_hpos > m_crtc.htotal || m_raster_irq_vpos > m_crtc.vtotal ) return attotime::never; // bail out and throw an error if this happens to be used someday if(m_raster_irq_mode == true) { popmessage("Raster IRQ used with mode = true"); return attotime::never; } // TODO: actual sync not taken into account, needs a better test than NCV2 limited case return screen().time_until_pos(m_raster_irq_vpos,m_raster_irq_hpos); } // R#17 / R#24 - base address /* * offset & 4 selects plane B * -xxx ---- write to base address + 1 * ---- -xxx write to base address */ WRITE8_MEMBER( ygv608_device::base_address_w ) { int plane = offset >> 2; int addr = ( offset << 1 ) & 0x07; m_base_addr[plane][addr] = data & 0x07; m_base_addr[plane][addr+1] = (data >> 4) & 0x7; m_tilemap_resize = true; } // R#25W - R#27W - X coordinate of initial value WRITE8_MEMBER( ygv608_device::roz_ax_w ) { m_ax = roz_convert_raw24(&m_raw_ax,offset,data); } // R#28W - R#29W - increment of coordinate in X direction WRITE8_MEMBER( ygv608_device::roz_dx_w ) { m_dx = roz_convert_raw16(&m_raw_dx,offset,data); } // R#30W - R#31W - increment of coordinate in X direction in movement toward Y direction WRITE8_MEMBER( ygv608_device::roz_dxy_w ) { m_dxy = roz_convert_raw16(&m_raw_dxy,offset,data); } // R#32W - R#34W - Y coordinate of initial value WRITE8_MEMBER( ygv608_device::roz_ay_w ) { m_ay = roz_convert_raw24(&m_raw_ay,offset,data); } // R#35W - R#36W - increment of coordinate in Y direction WRITE8_MEMBER( ygv608_device::roz_dy_w ) { m_dy = roz_convert_raw16(&m_raw_dy,offset,data); } // R#37W - R#38W - increment of coordinate in Y direction in movement toward X direction WRITE8_MEMBER( ygv608_device::roz_dyx_w ) { m_dyx = roz_convert_raw16(&m_raw_dyx,offset,data); } // ROZ assign helpers inline uint32_t ygv608_device::roz_convert_raw24(uint32_t *raw_reg, uint8_t offset, uint8_t data) { const uint32_t roz_data_mask24 = 0x1fffff; const uint32_t mem_mask = (0xff << offset*8) ^ ~0; uint32_t res; // substitute the new byte value into the raw register *raw_reg &= mem_mask; *raw_reg |= data << offset*8; // convert raw to the given register res = *raw_reg & roz_data_mask24; res <<= 7; if( res & 0x08000000 ) res |= 0xf8000000; // 2s complement return res; } inline uint32_t ygv608_device::roz_convert_raw16(uint16_t *raw_reg, uint8_t offset, uint8_t data) { const uint16_t roz_data_mask16 = 0x1fff; const uint16_t mem_mask = (0xff << offset*8) ^ ~0; uint32_t res; // substitute the new byte value into the raw register *raw_reg &= mem_mask; *raw_reg |= data << offset*8; // convert raw to the given register res = *raw_reg & roz_data_mask16; res <<= 7; if( res & 0x00080000 ) res |= 0xfff80000; // 2s complement return res; } // R#39W - R#46W display scan control write WRITE8_MEMBER( ygv608_device::crtc_w ) { //printf("[%d] <- %02x\n",offset+39,data); switch(offset+39) { case 39: { m_crtc.display_hsync = ((data >> 5) & 7) * 16; m_crtc.border_width = (data & 0x1f) * 16; break; } case 40: { int new_display_width = (data & 0x3f) * 16; m_crtc.htotal &= ~0x600; m_crtc.htotal |= ((data & 0xc0) << 3); if(new_display_width != m_crtc.display_width) m_screen_resize = true; m_crtc.display_width = new_display_width; break; } case 41: { m_crtc.display_hstart &= ~0x1fe; m_crtc.display_hstart |= (data & 0xff) << 1; break; } case 42: { m_crtc.htotal &= ~0x1fe; m_crtc.htotal |= ((data & 0xff) << 1); //printf("H %d %d %d %d %d\n",m_crtc.htotal,m_crtc.display_hstart,m_crtc.display_width,m_crtc.display_hsync,m_crtc.border_width); break; } case 43: { m_crtc.display_vsync = (data >> 5) & 7; m_crtc.border_height = (data & 0x1f) * 8; break; } case 44: { int new_display_height = (data & 0x3f) * 8; // TODO: VSLS, bit 6 if(new_display_height != m_crtc.display_height) m_screen_resize = true; m_crtc.display_height = new_display_height; break; } case 45: { m_crtc.vtotal &= ~0x100; m_crtc.vtotal |= BIT(data,7) << 8; // TODO: TRES, bit 6 m_crtc.display_vstart = data & 0x3f; break; } case 46: { m_crtc.vtotal &= ~0xff; m_crtc.vtotal |= data & 0xff; // TODO: call it for all mods in the CRTC, add sanity checks screen_configure(); //printf("V %d %d %d %d %d\n",m_crtc.vtotal,m_crtc.display_vstart,m_crtc.display_height,m_crtc.display_vsync,m_crtc.border_height); break; } } } // TODO: all horizontal values needs to be divided by 2, presumably some other register? // TODO: h/vstart not taken into account (needs video mods) void ygv608_device::screen_configure() { // int display_hend = (m_crtc.display_hstart + (m_crtc.display_width / 2)) - 1; int display_hend = (m_crtc.display_width / 2) - 1; // int display_vend = (m_crtc.display_vstart + m_crtc.display_height) - 1; int display_vend = (m_crtc.display_height) - 1; //rectangle visarea(m_crtc.display_hstart, display_hend, m_crtc.display_vstart, display_vend); rectangle visarea(0, display_hend, 0, display_vend); // TODO: Dig Dug Original wants this to be 60.60 Hz (like original Namco HW), lets compensate somehow // (clock is really 6144000 x 8 = 49152000, so it must have same parameters in practice) attoseconds_t period = HZ_TO_ATTOSECONDS(screen().clock()) * (m_crtc.vtotal + m_crtc.display_vsync) * ((m_crtc.htotal + 12 - m_crtc.display_hsync) / 2); screen().configure(m_crtc.htotal / 2, m_crtc.vtotal, visarea, period ); // reset vblank timer m_vblank_timer->reset(); //m_vblank_timer->adjust(screen().time_until_pos(m_crtc.display_vstart+m_crtc.display_height,0), 0, screen().frame_period()); m_vblank_timer->adjust(screen().time_until_pos(m_crtc.display_height,0), 0, screen().frame_period()); }