summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author angelosa <lordkale4@gmail.com>2025-08-30 12:07:27 +0200
committer angelosa <lordkale4@gmail.com>2025-08-30 12:09:03 +0200
commitae14e0a30830e8aba81e1cb26ac0d72d5cddaa3c (patch)
tree8d949c4288a61cfbda0a4f2f8d15d41bd35f6cac
parentcf87ff38ca1ff0f4ef2b35969bf8aa19572c72c6 (diff)
video/ym7101: add basic drawing
* enough to make Puzzle Construction playable
-rw-r--r--hash/megadriv.xml7
-rw-r--r--src/devices/video/ym7101.cpp527
-rw-r--r--src/devices/video/ym7101.h92
-rw-r--r--src/mame/pc/teradrive.cpp15
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);