diff options
| author | 2025-08-30 12:07:27 +0200 | |
|---|---|---|
| committer | 2025-08-30 12:09:03 +0200 | |
| commit | ae14e0a30830e8aba81e1cb26ac0d72d5cddaa3c (patch) | |
| tree | 8d949c4288a61cfbda0a4f2f8d15d41bd35f6cac | |
| parent | cf87ff38ca1ff0f4ef2b35969bf8aa19572c72c6 (diff) | |
video/ym7101: add basic drawing
* enough to make Puzzle Construction playable
| -rw-r--r-- | hash/megadriv.xml | 7 | ||||
| -rw-r--r-- | src/devices/video/ym7101.cpp | 527 | ||||
| -rw-r--r-- | src/devices/video/ym7101.h | 92 | ||||
| -rw-r--r-- | src/mame/pc/teradrive.cpp | 15 |
4 files changed, 597 insertions, 44 deletions
diff --git a/hash/megadriv.xml b/hash/megadriv.xml index 28985dfb4fa..faddba58436 100644 --- a/hash/megadriv.xml +++ b/hash/megadriv.xml @@ -33585,7 +33585,8 @@ Has missing sound samples (i.e. tire screech, "time bonus" on checkpoints) <year>1993</year> <publisher>Victor</publisher> <notes><![CDATA[ -Unsupported Mega-CD/Wondermega cart allowing EBXA format to be played back +https://segaretro.org/Wonder_Library +Unsupported [Mega CD]/[Wondermega] cart allowing EBXA format to be played back ]]></notes> <info name="alt_title" value="ワンダーライブラリ"/> <part name="cart" interface="megadriv_cart"> @@ -35474,7 +35475,7 @@ Crashes often if you use level select in options menu, particularly later on. <publisher>Virgin Interactive</publisher> <notes><![CDATA[ https://segaretro.org/Flux -Needs Mega CD addon +Needs [Mega CD] addon ]]></notes> <!-- TODO: check compatibility --> <part name="cart" interface="megadriv_cart"> @@ -35490,7 +35491,7 @@ Needs Mega CD addon <publisher>Virgin Interactive</publisher> <notes><![CDATA[ https://segaretro.org/Flux -Needs Mega CD addon +Needs [Mega CD] addon ]]></notes> <part name="cart" interface="megadriv_cart"> <feature name="pcb" value="171-5694-01"/> diff --git a/src/devices/video/ym7101.cpp b/src/devices/video/ym7101.cpp index 0f012d09721..8ce27764c76 100644 --- a/src/devices/video/ym7101.cpp +++ b/src/devices/video/ym7101.cpp @@ -2,7 +2,7 @@ // copyright-holders:Angelo Salese /************************************************************************************************** -Rewrite of Sega 315-5315 MD VDP (tied to Teradrive) +Rewrite of Sega 315-5313 MD VDP (tied to Teradrive) Notes: - Teradrive actually has 128 KiB compared to stock 64, this means that d_titov2 won't possibly work @@ -15,88 +15,300 @@ here; #include "ym7101.h" #define LOG_REGS (1U << 1) +#define LOG_REGSDMA (1U << 2) #define VERBOSE (LOG_GENERAL | LOG_REGS) //#define LOG_OUTPUT_FUNC osd_printf_info #include "logmacro.h" -#define LOGREGS(...) LOGMASKED(LOG_REGS, __VA_ARGS__) +#define LOGREGS(...) LOGMASKED(LOG_REGS, __VA_ARGS__) +#define LOGREGSDMA(...) LOGMASKED(LOG_REGSDMA, __VA_ARGS__) - -DEFINE_DEVICE_TYPE(YM7101, ym7101_device, "ym7101", "Yamaha YM7101") +DEFINE_DEVICE_TYPE(YM7101, ym7101_device, "ym7101", "Yamaha YM7101 VDP") ym7101_device::ym7101_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, YM7101, tag, owner, clock) , device_memory_interface(mconfig, *this) , device_video_interface(mconfig, *this) + , device_gfx_interface(mconfig, *this, nullptr, "palette") , device_mixer_interface(mconfig, *this) - , m_space_vram_config("videoram", ENDIANNESS_BIG, 8, 17, 0, address_map_constructor(FUNC(ym7101_device::vram_map), this)) + , m_space_vram_config("vram", ENDIANNESS_BIG, 16, 16 + 1, 0, address_map_constructor(FUNC(ym7101_device::vram_map), this)) + , m_space_cram_config("cram", ENDIANNESS_BIG, 16, 7, 0, address_map_constructor(FUNC(ym7101_device::cram_map), this)) + , m_space_vsram_config("vsram", ENDIANNESS_BIG, 16, 8, 0, address_map_constructor(FUNC(ym7101_device::vsram_map), this)) , m_space_regs_config("regs", ENDIANNESS_BIG, 8, 6, 0, address_map_constructor(FUNC(ym7101_device::regs_map), this)) + , m_vram(*this, "vram") + , m_cram(*this, "cram") + , m_vsram(*this, "vsram") , m_vint_callback(*this) // , m_hint_callback(*this) , m_sint_callback(*this) + , m_mreq_cb(*this, 0) + , m_dtack_cb(*this) + , m_palette(*this, "palette") , m_psg(*this, "psg") { } +//const u16 dot_a = m_vram[(tile_a << 4) + BIT(xi ^ flipx_a, 2) + ((y_char_a ^ flipy_a) << 1)] >> (((flipx_a ^ xi) & 3) * 4) & 0xf; +// NOTE: can't use RGN_FRAC(1,1) in devices +static const gfx_layout layout_8x8x4 = +{ + 8,8, + 0x1000, + 4, + { STEP4(0,1) }, + { 2*4, 3*4, 0*4, 1*4, 6*4, 7*4, 4*4, 5*4 }, + { STEP8(0,4*8) }, + 4*8*8 +}; + void ym7101_device::device_start() { + if (m_palette && !m_palette->started()) + throw device_missing_dependencies(); + + screen().register_screen_bitmap(m_bitmap); + + // TODO: eventually merge these m_scan_timer = timer_alloc(FUNC(ym7101_device::scan_timer_callback), this); + m_vint_on_timer = timer_alloc(FUNC(ym7101_device::vint_trigger_callback), this); + m_dma_timer = timer_alloc(FUNC(ym7101_device::dma_callback), this); + // NOTE: DMA still runs on reset + m_dma_timer->adjust(attotime::never); + m_dma.source_address = 0; + m_dma.length = 0; + m_dma.fill = 0; + m_dma.active = false; + m_sprite_cache = std::make_unique<u16[]>(80 * 4); + m_sprite_line = std::make_unique<u8[]>(320); + + set_gfx(0, std::make_unique<gfx_element>( + m_palette, + layout_8x8x4, + (u8 *)(m_vram.target()), + 0, 4, 0 + )); + + save_pointer(NAME(m_sprite_cache), 80 * 4); } void ym7101_device::device_reset() { + m_command.write_state = command_write_state_t::FIRST_WORD; + m_command.latch = 0; + m_command.address = 0; + m_command.code = 0; + + m_de = false; m_ie0 = false; + m_vint_pending = 0; + m_plane_a_name_table = 0; + m_plane_b_name_table = 0; + m_background_color = 0; + m_vint_on_timer->adjust(attotime::never); m_scan_timer->adjust(screen().time_until_pos(224), 224); } void ym7101_device::device_add_mconfig(machine_config &config) { + PALETTE(config, m_palette); + m_palette->set_entries(0x40 + 0x40 * 2); + SEGAPSG(config, m_psg, DERIVED_CLOCK(4, 15)).add_route(ALL_OUTPUTS, *this, 1.0, 0); } device_memory_interface::space_config_vector ym7101_device::memory_space_config() const { return space_config_vector { - std::make_pair(AS_DATA, &m_space_vram_config), - std::make_pair(AS_IO, &m_space_regs_config) + std::make_pair(AS_VDP_VRAM, &m_space_vram_config), + std::make_pair(AS_VDP_CRAM, &m_space_cram_config), + std::make_pair(AS_VDP_VSRAM, &m_space_vsram_config), + std::make_pair(AS_VDP_IO, &m_space_regs_config) }; } +void ym7101_device::update_command_state() +{ + m_command.address = (m_command.latch & 0x3fff) | ((m_command.latch & 0x07'0000) >> 2); + m_command.code = ((m_command.latch & 0xc000) >> 14) | ((m_command.latch & 0xf0'0000) >> 18); +} + +u16 ym7101_device::data_port_r(offs_t offset, u16 mem_mask) +{ + m_command.write_state = command_write_state_t::FIRST_WORD; + + if (BIT(m_command.code, 0)) + { + LOG("data_port_r: illegal read on write code %d & %04x\n", m_command.code, mem_mask); + return 0xffff; + } + + u16 res = 0; + + switch (m_command.code >> 1) + { + case 0: + res = space(AS_VDP_VRAM).read_word(m_command.address, mem_mask); + break; + case 2: + res = space(AS_VDP_VSRAM).read_word(m_command.address, mem_mask); + break; + case 4: + res = space(AS_VDP_CRAM).read_word(m_command.address, mem_mask); + break; + + case 6: + LOG("data_port_r: undocumented vram 8-bit & %04x\n", mem_mask); + break; + default: + LOG("data_port_r: illegal code %d & %04x\n", m_command.code >> 1, mem_mask); + break; + } + + m_command.address += m_auto_increment; + m_command.address &= 0x1ffff; + + return res; +} + +void ym7101_device::data_port_w(offs_t offset, u16 data, u16 mem_mask) +{ + m_command.write_state = command_write_state_t::FIRST_WORD; + + if (!BIT(m_command.code, 0)) + { + LOG("data_port_w: illegal write on read code %d data %04x & %04x\n", m_command.code, data, mem_mask); + return; + } + + if (m_dma.active && m_dma.mode == VRAM_FILL && BIT(m_command.code, 0)) + { + m_dma.fill = data; + m_dma_timer->adjust(attotime::from_ticks(8, clock())); + return; + } + + switch (m_command.code >> 1) + { + case 0: + space(AS_VDP_VRAM).write_word(m_command.address, data, mem_mask); + break; + case 1: + space(AS_VDP_CRAM).write_word(m_command.address, data, mem_mask); + break; + case 2: + space(AS_VDP_VSRAM).write_word(m_command.address, data, mem_mask); + break; + default: + LOG("data_port_w: illegal code %d data %04x & %04x\n", m_command.code >> 1, data, mem_mask); + break; + } + + m_command.address += m_auto_increment; + m_command.address &= 0x1ffff; +} void ym7101_device::if_map(address_map &map) { + map(0x00, 0x01).mirror(2).rw(FUNC(ym7101_device::data_port_r), FUNC(ym7101_device::data_port_w)); // Control Port map(0x04, 0x05).mirror(2).lrw16( NAME([this] (offs_t offset, u16 mem_mask) { - return screen().vblank() << 3; + return (m_vint_pending << 7) + | (screen().vblank() << 3) + | (m_dma.active << 1); }), NAME([this] (offs_t offset, u16 data, u16 mem_mask) { //printf("%04x %04x\n", data, mem_mask); if (ACCESSING_BITS_0_15) { - // TODO: as FIFO + if (m_command.write_state == command_write_state_t::SECOND_WORD) + { + m_command.latch = (data << 16) | (m_command.latch & 0xffff); + update_command_state(); + m_dma.active = !!(m_m1 && BIT(m_command.code, 5)); + + if (m_dma.mode != VRAM_FILL && m_dma.active && BIT(m_command.code, 0)) + { + // printf("%08x %08x %04x %02x %02x %d\n", m_dma.source_address, m_command.address, m_dma.length, m_command.code, m_dma.mode, m_auto_increment); + m_dma_timer->adjust(attotime::from_ticks(8, clock())); + } + + m_command.write_state = command_write_state_t::FIRST_WORD; + return; + } + + m_command.latch = data | (m_command.latch & 0xffff0000); + update_command_state(); + if ((data & 0xc000) == 0x8000) { - space(AS_IO).write_byte((data >> 8) & 0x3f, data & 0xff); + space(AS_VDP_IO).write_byte((data >> 8) & 0x3f, data & 0xff); + } + else + { + m_command.write_state = command_write_state_t::SECOND_WORD; } } }) ); map(0x11, 0x11).mirror(6).w(m_psg, FUNC(segapsg_device::write)); + // TODO: debug port at $18/$1c +} + +void ym7101_device::vram_w(offs_t offset, u16 data, u16 mem_mask) +{ + COMBINE_DATA(&m_vram[offset]); + gfx(0)->mark_dirty(offset >> 4); + + const u32 sprite_table = m_sprite_attribute_table >> 1; + + // TODO: check akumajo Stage 6-3 + // TODO: check segacd:snatcheru (H32, puts sprite_table at $fe00) + // TODO: only Y & Link parameter is cached + if (offset == std::clamp(offset, sprite_table, (sprite_table + 80 * 4) - 1)) + { + m_sprite_cache[offset - sprite_table] = m_vram[offset]; + } } void ym7101_device::vram_map(address_map &map) { - if (!has_configured_map(0)) - map(0x00000, 0x1ffff).ram(); + // TODO: configurable way to make it stock 64 KiB +// if (!has_configured_map(AS_VDP_VRAM)) + map(0x00000, 0x1ffff).ram().share("vram").w(FUNC(ym7101_device::vram_w)); +} + +void ym7101_device::cram_w(offs_t offset, u16 data, u16 mem_mask) +{ + COMBINE_DATA(&m_cram[offset]); + static constexpr u8 level[15] = {0,29,52,70,87,101,116,130,144,158,172,187,206,228,255}; + + int r = (m_cram[offset] & 0x000e) >> 1; + int g = (m_cram[offset] & 0x00e0) >> 5; + int b = (m_cram[offset] & 0x0e00) >> 9; + + // normal + m_palette->set_pen_color(offset, level[r << 1], level[g << 1], level[b << 1]); + // TODO: shadow & highlight +} + +void ym7101_device::cram_map(address_map &map) +{ + map(0x00, 0x7f).ram().share("cram").w(FUNC(ym7101_device::cram_w)); } +void ym7101_device::vsram_map(address_map &map) +{ + map(0x00, 0x4f).ram().share("vsram"); +} + + +static const char *const size_names[] = { "256 pixels/32 cells", "512 pixels/64 cells", "<invalid>", "1024 pixels/128 cells" }; + // https://plutiedev.com/vdp-registers // https://segaretro.org/Sega_Mega_Drive/VDP_registers // NOTE: in decimal units, classic Yamaha -static const char *const size_names[] = { "256/32", "512/64", "<invalid>", "1024/128" }; - void ym7101_device::regs_map(address_map &map) { map(0, 0).lw8(NAME([this] (u8 data) { @@ -111,20 +323,34 @@ void ym7101_device::regs_map(address_map &map) })); map(1, 1).lw8(NAME([this] (u8 data) { LOGREGS("#01: Mode Register 2 %02x\n", data); + m_vr = !!BIT(data, 7); + m_de = !!BIT(data, 6); m_ie0 = !!BIT(data, 5); + m_m1 = !!BIT(data, 4); + //m_dma.active = !!(m_m1 && BIT(m_command.code, 5)); + + if (m_ie0 && m_vint_pending) + { + m_vint_on_timer->adjust(attotime::from_ticks(16, clock())); + } + else + { + m_vint_callback(0); + } LOGREGS("\tVR %d DE %d IE0: %d M1: %d M2: %d M5: %d\n" - , BIT(data, 7) - , BIT(data, 6) + , m_vr + , m_de , m_ie0 - , BIT(data, 4) + , m_m1 , BIT(data, 3) , BIT(data, 2) ); })); map(2, 2).lw8(NAME([this] (u8 data) { + m_plane_a_name_table = (data & 0x78) << 10; LOGREGS("#02: Plane A Name Table %02x (%05x)\n" , data - , (data & 0x78) << 10 + , m_plane_a_name_table ); })); map(3, 3).lw8(NAME([this] (u8 data) { @@ -134,12 +360,14 @@ void ym7101_device::regs_map(address_map &map) ); })); map(4, 4).lw8(NAME([this] (u8 data) { + m_plane_b_name_table = (data & 0xf) << 13; LOGREGS("#04: Plane B Name Table %02x (%05x)\n" , data - , (data & 0xf) << 13 + , m_plane_b_name_table ); })); map(5, 5).lw8(NAME([this] (u8 data) { + m_sprite_attribute_table = data << 9; LOGREGS("#05: Sprite Table %02x (%05x)\n" , data , data << 9 @@ -150,6 +378,7 @@ void ym7101_device::regs_map(address_map &map) })); map(7, 7).lw8(NAME([this] (u8 data) { LOGREGS("#07: Background Color %02x & 0x3f\n", data); + m_background_color = data & 0x3f; })); // map(8, 8) Master System h scroll // map(9, 9) Master System v scroll @@ -180,6 +409,7 @@ void ym7101_device::regs_map(address_map &map) popmessage("ym7101.cpp: invalid RS setting %02x", data & 0x81); })); map(13, 13).lw8(NAME([this] (u8 data) { + m_hscroll_address = (data & 0x7f) << 10; LOGREGS("#13: Horizontal Scroll address %02x (%05x)\n", data, (data & 0x7f) << 10); })); map(14, 14).lw8(NAME([this] (u8 data) { @@ -187,16 +417,21 @@ void ym7101_device::regs_map(address_map &map) LOGREGS("#14: 128 KiB plane address %02x (%02x)\n", data, data & 0x11); })); map(15, 15).lw8(NAME([this] (u8 data) { - LOGREGS("#15: autoincrement %02x\n", data); + m_auto_increment = data; + LOGREGS("#15: Auto Increment %02x\n", data); })); map(16, 16).lw8(NAME([this] (u8 data) { + m_vsz = (data & 0x30) >> 4; + m_hsz = data & 3; LOGREGS("#16: Plane Size %02x\n", data); - LOGREGS("\tH %d (%s) V %d (%s)\n" - , (data & 0x30) >> 4 - , size_names[(data & 0x30) >> 4] - , (data & 3) - , size_names[(data & 0x3)] + LOGREGS("\tH %d (%s) x V %d (%s)\n" + , m_hsz + , size_names[m_hsz] + , m_vsz + , size_names[m_vsz] ); + if (m_hsz == 2 || m_vsz == 2 || (m_vsz == 3 && m_hsz != 0) || (m_hsz == 3 && m_vsz != 0)) + popmessage("ym7101.cpp: illegal plane size set %d %d", m_hsz, m_vsz); })); map(17, 17).lw8(NAME([this] (u8 data) { LOGREGS("#17: Window Plane Horizontal position %02x\n", data); @@ -213,38 +448,217 @@ void ym7101_device::regs_map(address_map &map) ); })); map(19, 19).lw8(NAME([this] (u8 data) { - LOGREGS("#19: DMA length low %02x\n", data); + LOGREGSDMA("#19: DMA length low %02x\n", data); + m_dma.length = data | (m_dma.length & 0xff00); })); map(20, 20).lw8(NAME([this] (u8 data) { - LOGREGS("#20: DMA length high %02x\n", data); + LOGREGSDMA("#20: DMA length high %02x\n", data); + m_dma.length = (data << 8) | (m_dma.length & 0xff); })); map(21, 21).lw8(NAME([this] (u8 data) { - LOGREGS("#21: DMA source low %02x\n", data); + LOGREGSDMA("#21: DMA source low %02x\n", data); + m_dma.source_address = (data << 1) | (m_dma.source_address & 0xfffe00); })); map(22, 22).lw8(NAME([this] (u8 data) { - LOGREGS("#22: DMA source mid %02x\n", data); + LOGREGSDMA("#22: DMA source mid %02x\n", data); + m_dma.source_address = (data << 9) | (m_dma.source_address & 0xfe01fe); })); map(23, 23).lw8(NAME([this] (u8 data) { - LOGREGS("#23: DMA source high %02x data %02x mode %02x\n", data, data & 0x3f, data >> 6); + LOGREGSDMA("#23: DMA source high %02x | data %02x mode %02x\n", data, data & 0x3f, data >> 6); + m_dma.source_address = ((data & 0x3f) << 17) | (m_dma.source_address & 0x01fffe); + if (BIT(data, 7)) + { + m_dma.source_address &= 0x7fffff; + m_dma.mode = BIT(data, 6) ? VRAM_COPY : VRAM_FILL; + } + else + { + m_dma.mode = MEMORY_TO_VRAM; + m_dma.source_address |= (BIT(data, 6) << 23); + } })); } +TIMER_CALLBACK_MEMBER(ym7101_device::vint_trigger_callback) +{ + if (m_ie0) + m_vint_callback(1); +} + + +void ym7101_device::prepare_sprite_line(int scanline) +{ + std::fill_n(&m_sprite_line[0], 320, 0); + + int num_sprites = 0; + u16 link = 0; + u16 offset = 0; + int y, x; + u16 height, width; + + do { + const u16 *cache = &m_sprite_cache[offset]; + const u16 *vram = &m_vram[(m_sprite_attribute_table >> 1) + offset]; + + y = (cache[0] & 0x1ff) - 128; + height = (((cache[1] >> 8) & 0x3) + 1) * 8; + + if (scanline == std::clamp(scanline, y, y + height - 1)) + { + num_sprites ++; + width = (((cache[1] >> 10) & 0x3) + 1) * 8; + x = (vram[3] & 0x1ff) - 128; + + const u16 id_flags = vram[2]; + const u16 tile = id_flags & 0x7ff; + const bool flipx = !!BIT(id_flags, 11); + const bool flipy = !!BIT(id_flags, 12); + const u8 color = ((id_flags >> 13) & 3) << 4; + //const bool high_priority = !!BIT(id_flags, 15); + + const int yi = scanline - y; + + for (int xi = 0; xi < width; xi ++) + { + if (x + xi < 0 || x + xi >= 320) + continue; + + const int x_offs = flipx ? width - xi - 1 : xi; + const int y_offs = flipy ? height - yi - 1 : yi; + const u8 x_char = x_offs & 7; + const u8 y_char = y_offs & 7; + // https://plutiedev.com/sprites#sprite-format + // column first + const u32 sprite_offs = ((x_offs >> 3) * (height >> 3) + (y_offs >> 3)) << 4; + + const u8 dot = m_vram[(tile << 4) + BIT(x_char, 2) + (y_char << 1) + sprite_offs] >> (((3 ^ x_char) & 3) * 4) & 0xf; + + if ((m_sprite_line[x + xi] & 0xf) == 0) + m_sprite_line[x + xi] = (color) | (dot & 0xf); + // sprite collision + // else if (dot) + } + } + + link = cache[1] & 0x7f; + + // TODO: https://plutiedev.com/mirror/kabuto-hardware-notes#sprite-rendering-beyond-80 + if (link >= 80) + { + // teradrive pzlcnst game.exe tends to use a link = 0x7f on piece removals ... + if (link != 0x7f) + popmessage("ym7101: attempt to access link $%d, aborted", link); + break; + } + + offset = link * 4; + + } while(num_sprites < 21 && link != 0); + + // sprite overflow, here +} + +void ym7101_device::render_line(int scanline) +{ + if (scanline >= 224) + return; + + uint32_t *p = &m_bitmap.pix(scanline); + + //int y = scanline >> 3; + int yi = scanline & 7; + + if (!m_de) + { + const rectangle scanclip(0, 320, scanline, scanline); + m_bitmap.fill(m_palette->pen(m_background_color), scanclip); + return; + } + + const u16 vram_mask = 0x7ff; + //const u16 page_mask[] = { 0x7ff, 0x1fff, 0x1fff, 0x1fff }; + + const u16 page_masks[] = { 32, 64, 1, 128 }; + + const u16 h_page = page_masks[m_hsz]; + const u16 v_page = page_masks[m_vsz]; + + prepare_sprite_line(scanline); + + for (int x = 0; x < 40; x ++) + { + // TODO: prettify, shouldn't need scrolly in branch + const u16 scrollx_a = m_vram[m_hscroll_address >> 1]; + const u16 scrollx_a_frac = 0; //scrollx_a & 7; + const u16 scrolly_a = m_vsram[0]; + const u16 scrolly_a_frac = scrolly_a & 7; + const u16 vcolumn_a = (scrolly_a + scanline) & ((v_page * 8) - 1); + const u32 tile_offset_a = ((x - (scrollx_a >> 3)) & ((h_page * 1) - 1)) + ((vcolumn_a >> 3) * (h_page >> 0)); + const u16 id_flags_a = m_vram[((m_plane_a_name_table >> 1) + tile_offset_a) & 0x1ffff]; + const u16 tile_a = id_flags_a & vram_mask; + const u8 flipx_a = BIT(id_flags_a, 11) ? 4 : 3; + const u8 flipy_a = BIT(id_flags_a, 12) ? 7 : 0; + const u8 color_a = ((id_flags_a >> 13) & 3) << 4; + //const bool high_priority_a = !!BIT(id_flags_a, 15); + + const u16 scrollx_b = m_vram[(m_hscroll_address >> 1) + 1]; + const u16 scrollx_b_frac = 0; //scrollx_b & 7; + const u16 scrolly_b = m_vsram[1]; + const u16 scrolly_b_frac = scrolly_b & 7; + const u16 vcolumn_b = (scrolly_b + scanline) & ((v_page * 8) - 1); + const u32 tile_offset_b = ((x - (scrollx_b >> 3)) & ((h_page * 1) - 1)) + ((vcolumn_b >> 3) * (h_page >> 0)); + const u16 id_flags_b = m_vram[((m_plane_b_name_table >> 1) + tile_offset_b) & 0x1ffff]; + const u16 tile_b = id_flags_b & vram_mask; + const u8 flipx_b = BIT(id_flags_b, 11) ? 4 : 3; + const u8 flipy_b = BIT(id_flags_b, 12) ? 7 : 0; + const u8 color_b = ((id_flags_b >> 13) & 3) << 4; + //const bool high_priority_b = !!BIT(id_flags_b, 15); + + for (int xi = 0; xi < 8; xi++) + { + u8 pen = m_background_color; + + const u8 x_char_a = (xi + scrollx_a_frac) & 7; + const u8 y_char_a = (yi + scrolly_a_frac) & 7; + const u16 dot_a = m_vram[(tile_a << 4) + BIT(x_char_a ^ flipx_a, 2) + ((y_char_a ^ flipy_a) << 1)] >> (((flipx_a ^ x_char_a) & 3) * 4) & 0xf; + + const u8 x_char_b = (xi + scrollx_b_frac) & 7; + const u8 y_char_b = (yi + scrolly_b_frac) & 7; + const u16 dot_b = m_vram[(tile_b << 4) + BIT(x_char_b ^ flipx_b, 2) + ((y_char_b ^ flipy_b) << 1)] >> (((flipx_b ^ x_char_b) & 3) * 4) & 0xf; + + if (dot_b) + pen = (dot_b) | color_b; + + if (dot_a) + pen = (dot_a) | color_a; + + if (m_sprite_line[x * 8 + xi] & 0xf) + pen = m_sprite_line[x * 8 + xi] & 0x3f; + + p[(x << 3) + xi] = m_palette->pen(pen); + } + } +} + TIMER_CALLBACK_MEMBER(ym7101_device::scan_timer_callback) { int scanline = param; // TODO: to execution pipeline - if (scanline == 224 && m_ie0) + if (scanline == 224) { - m_vint_callback(true); + m_vint_on_timer->adjust(attotime::from_ticks(16, clock())); + m_vint_pending = 1; } // TODO: correct? if (scanline == 240) - m_sint_callback(true); + m_sint_callback(1); if (scanline == 241) - m_sint_callback(false); + m_sint_callback(0); + + render_line(scanline); scanline ++; scanline %= screen().height(); @@ -252,8 +666,53 @@ TIMER_CALLBACK_MEMBER(ym7101_device::scan_timer_callback) m_scan_timer->adjust(screen().time_until_pos(scanline), scanline); } +TIMER_CALLBACK_MEMBER(ym7101_device::dma_callback) +{ + if (!BIT(m_command.code, 0) || !m_dma.active) + { + m_dtack_cb(0); + return; + } + + const u8 code_dest = (m_command.code >> 1) & 3; + assert(code_dest != 3); + + const u32 mask_values[] = { 0x1ffff, 0x7f, 0x7f, 0 }; + u32 mask = mask_values[code_dest]; + const u32 src = m_dma.source_address; + const u32 dst = m_command.address; + + u16 res = 0; + + switch (m_dma.mode) + { + case MEMORY_TO_VRAM: res = m_mreq_cb(src); m_dtack_cb(1); break; + case VRAM_FILL: res = m_dma.fill; break; + case VRAM_COPY: res = space(code_dest).read_word((src >> 0) & mask, 0xffff); break; + } + + space(code_dest).write_word((dst >> 0) & mask, res, 0xffff); + + m_dma.source_address = (m_dma.source_address & 0xfe0000) | ((m_dma.source_address + 2) & 0x1ffff); + m_command.address += m_auto_increment; + m_command.address &= 0x1ffff; + + m_dma.length --; + if (m_dma.length == 0) + { + m_dtack_cb(0); + m_dma.active = false; + m_dma_timer->adjust(attotime::never); + } + else + { + m_dma_timer->adjust(attotime::from_ticks(8, clock())); + } +} + u32 ym7101_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { + copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect); return 0; } diff --git a/src/devices/video/ym7101.h b/src/devices/video/ym7101.h index 46d798ed4df..2b3dc650062 100644 --- a/src/devices/video/ym7101.h +++ b/src/devices/video/ym7101.h @@ -11,10 +11,12 @@ #include "emupal.h" #include "screen.h" +#include "tilemap.h" class ym7101_device : public device_t, public device_memory_interface, public device_video_interface, + public device_gfx_interface, public device_mixer_interface { public: @@ -25,6 +27,8 @@ public: auto vint_cb() { return m_vint_callback.bind(); } //auto hint_cb() { return m_lv4irqline_callback.bind(); } auto sint_cb() { return m_sint_callback.bind(); } + auto dtack_cb() { return m_dtack_cb.bind(); } + auto mreq_cb() { return m_mreq_cb.bind(); } u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); @@ -33,11 +37,11 @@ public: if (machine().side_effects_disabled()) return; - //if (m_vint_pending) - //{ - // m_vint_pending = 0; - m_vint_callback(false); - //} + if (m_vint_pending) + { + m_vint_pending = 0; + m_vint_callback(0); + } //else if (m_hint_pending) //{ // m_hint_pending = 0; @@ -51,23 +55,101 @@ protected: virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; virtual space_config_vector memory_space_config() const override; +// virtual u32 palette_entries() const noexcept override { return 0x40 + 0x40 * 2; } private: void vram_map(address_map &map) ATTR_COLD; + void cram_map(address_map &map) ATTR_COLD; + void vsram_map(address_map &map) ATTR_COLD; void regs_map(address_map &map) ATTR_COLD; const address_space_config m_space_vram_config; + const address_space_config m_space_cram_config; + const address_space_config m_space_vsram_config; const address_space_config m_space_regs_config; + required_shared_ptr<u16> m_vram; + required_shared_ptr<u16> m_cram; + required_shared_ptr<u16> m_vsram; + devcb_write_line m_vint_callback; // devcb_write_line m_hint_callback; devcb_write_line m_sint_callback; + devcb_read16 m_mreq_cb; + devcb_write_line m_dtack_cb; + required_device<palette_device> m_palette; required_device<segapsg_device> m_psg; + std::unique_ptr<u16[]> m_sprite_cache; + std::unique_ptr<u8[]> m_sprite_line; + TIMER_CALLBACK_MEMBER(scan_timer_callback); + TIMER_CALLBACK_MEMBER(vint_trigger_callback); + TIMER_CALLBACK_MEMBER(dma_callback); emu_timer *m_scan_timer; + emu_timer *m_vint_on_timer; + emu_timer *m_dma_timer; + + enum command_write_state_t : u8 { + FIRST_WORD, + SECOND_WORD + }; + + struct { + u32 latch; + u32 address; + u8 code; + command_write_state_t write_state; + } m_command; + + void update_command_state(); + + enum dma_mode_t : u8 { + MEMORY_TO_VRAM, + VRAM_FILL, + VRAM_COPY + }; + + struct { + u32 source_address; + u16 length; + dma_mode_t mode; + bool active; + u16 fill; + } m_dma; + + enum + { + AS_VDP_VRAM = 0, + AS_VDP_CRAM = 1, + AS_VDP_VSRAM = 2, + AS_VDP_IO = 3, + }; + + u16 data_port_r(offs_t offset, u16 mem_mask); + void data_port_w(offs_t offset, u16 data, u16 mem_mask); + + void vram_w(offs_t offset, u16 data, u16 mem_mask); + void cram_w(offs_t offset, u16 data, u16 mem_mask); + bool m_vr; // 128 KiB VRAM mode (undocumented) + bool m_de; bool m_ie0; + bool m_m1; // DMA enable + int m_vint_pending; + u32 m_hscroll_address; + u8 m_hsz, m_vsz; + u8 m_auto_increment; + u32 m_plane_a_name_table; + u32 m_plane_b_name_table; + u32 m_sprite_attribute_table; + u8 m_background_color; + + bitmap_rgb32 m_bitmap; + void render_line(int scanline); + void prepare_sprite_line(int scanline); + + DECLARE_GFXDECODE_MEMBER(gfxinfo); }; DECLARE_DEVICE_TYPE(YM7101, ym7101_device) diff --git a/src/mame/pc/teradrive.cpp b/src/mame/pc/teradrive.cpp index 90c47e12228..14c3bd1dc50 100644 --- a/src/mame/pc/teradrive.cpp +++ b/src/mame/pc/teradrive.cpp @@ -177,7 +177,7 @@ void isa16_ibm_79f2661::md_mem_map(address_map &map) return (u16)(res | (res << 8)); } - return m_md_space->read_word(base_address, mem_mask); + return swapendian_int16(m_md_space->read_word(base_address, mem_mask)); }), NAME([this] (offs_t offset, u16 data, u16 mem_mask) { const u32 base_address = (offset << 1) + (m_68k_address << 12); @@ -186,7 +186,7 @@ void isa16_ibm_79f2661::md_mem_map(address_map &map) else if (!ACCESSING_BITS_8_15) m_md_space->write_byte(base_address, data); else - m_md_space->write_word(base_address, data, mem_mask); + m_md_space->write_word(base_address, swapendian_int16(data), mem_mask); }) ); } @@ -937,6 +937,17 @@ void teradrive_state::teradrive(machine_config &config) YM7101(config, m_md_vdp, md_master_xtal / 4); m_md_vdp->set_screen(m_mdscreen); + // TODO: actual DTACK + // TODO: accessing 68k bus from x86, defers access? + m_md_vdp->dtack_cb().set_inputline(m_md68kcpu, INPUT_LINE_HALT); + m_md_vdp->mreq_cb().set([this] (offs_t offset) { + // TODO: can't read outside of base cart and RAM + //printf("%06x\n", offset); + address_space &space68k = m_md68kcpu->space(); + u16 ret = space68k.read_word(offset, 0xffff); + + return ret; + }); m_md_vdp->vint_cb().set_inputline(m_md68kcpu, 6); // m_md_vdp->hint_cb().set_inputline(m_md68kcpu, 4); m_md_vdp->sint_cb().set_inputline(m_mdz80cpu, INPUT_LINE_IRQ0); |
