diff options
author | 2025-03-06 19:37:00 +0100 | |
---|---|---|
committer | 2025-03-06 19:37:08 +0100 | |
commit | ea8d28c70a2c01d37135f1180af17a593bcb66d7 (patch) | |
tree | 421958a3c4c7abbc7381746687915208ba395839 | |
parent | c0b4ed0f446e96a34d420c55df9a67c01c1dcc5c (diff) |
nec/pc88va_sgp.cpp: implement ROP and TPMOD functions, fix pitch bugs in boomer and shinraba
-rw-r--r-- | src/mame/nec/pc88va_sgp.cpp | 152 | ||||
-rw-r--r-- | src/mame/nec/pc88va_sgp.h | 30 |
2 files changed, 132 insertions, 50 deletions
diff --git a/src/mame/nec/pc88va_sgp.cpp b/src/mame/nec/pc88va_sgp.cpp index 9ff1e812b3e..d8f7670e103 100644 --- a/src/mame/nec/pc88va_sgp.cpp +++ b/src/mame/nec/pc88va_sgp.cpp @@ -7,10 +7,12 @@ NEC SGP (スーパーグラフィックプロセッサ / Super Graphic Processor Unknown part number, used as GPU for PC88VA TODO: -- timing details -- specifics about what exactly happens in work area when either SGP runs or is idle. +- timing details; +- unemulated CLS, LINE and SCAN; +- Uneven src/dst sizes or color depths (mostly for PATBLT); +- specifics about what exactly happens in work area when either SGP runs or is idle; - famista: during gameplay it BITBLT same source to destination 0x00037076 - with tp_mode = 3 and pitch = 0 (!?); + with tp_mode = 3, uneven color depths (src 1bpp, dst 4bpp) and pitch = 0, assume disabled; - rtype: during gameplay it does transfers with Pitch = 0xfff0, alias for negative draw? - basic fires a VABOT on loading; @@ -21,7 +23,6 @@ TODO: //#include <iostream> - #define LOG_COMMAND (1U << 1) #define VERBOSE (LOG_GENERAL) @@ -88,7 +89,7 @@ void pc88va_sgp_device::vdp_address_w(offs_t offset, u16 data, u16 mem_mask) void pc88va_sgp_device::control_w(u8 data) { if (data) - popmessage("SGP warning write %02x", data); + popmessage("SGP: control_w write %02x", data); } /* @@ -96,6 +97,11 @@ void pc88va_sgp_device::control_w(u8 data) */ u8 pc88va_sgp_device::status_r() { +// crude debug single-stepping +// if (machine().input().code_pressed(KEYCODE_S)) +// return 0; +// if (!machine().input().code_pressed_once(KEYCODE_A)) +// return 1; return 0; } @@ -183,7 +189,8 @@ void pc88va_sgp_device::start_exec() ptr->pixel_mode = (param1 & 0x03); ptr->hsize = m_data->read_word(vdp_pointer + 4) & 0x0fff; ptr->vsize = m_data->read_word(vdp_pointer + 6) & 0x0fff; - ptr->fb_pitch = m_data->read_word(vdp_pointer + 8) & 0xfffc; + // NOTE: & 0xfffc causes pitch issues in boomer intro/title text, shinraba gameplay + ptr->fb_pitch = m_data->read_word(vdp_pointer + 8) & 0xfffe; ptr->address = (m_data->read_word(vdp_pointer + 10) & 0xfffe) | (m_data->read_word(vdp_pointer + 12) << 16); @@ -223,7 +230,7 @@ void pc88va_sgp_device::start_exec() , cmd_mode ? "PATBLT" : "BITBLT" , draw_mode ); - cmd_blit(draw_mode, cmd_mode); + execute_blit(draw_mode, cmd_mode); next_pc += 2; break; @@ -239,7 +246,7 @@ void pc88va_sgp_device::start_exec() // in pixels const u16 h_size = m_data->read_word(vdp_pointer + 6); const u16 v_size = m_data->read_word(vdp_pointer + 8); - const u16 fb_pitch = m_data->read_word(vdp_pointer + 10) & 0xfffc; + const u16 fb_pitch = m_data->read_word(vdp_pointer + 10) & 0xfffe; const u32 src_address = (m_data->read_word(vdp_pointer + 12) & 0xfffe) | (m_data->read_word(vdp_pointer + 14) << 16); @@ -303,9 +310,58 @@ void pc88va_sgp_device::start_exec() } /**************************************** - * Commands + * Blitting ***************************************/ +const pc88va_sgp_device::rop_func pc88va_sgp_device::rop_table[16] = +{ + &pc88va_sgp_device::rop_0_fill_0, + &pc88va_sgp_device::rop_1_s_and_d, + &pc88va_sgp_device::rop_2_ns_and_d, + &pc88va_sgp_device::rop_3_d, + &pc88va_sgp_device::rop_4_s_and_nd, + &pc88va_sgp_device::rop_5_s, + &pc88va_sgp_device::rop_6_s_xor_d, + &pc88va_sgp_device::rop_7_s_or_d, + &pc88va_sgp_device::rop_8_ns_or_d, + &pc88va_sgp_device::rop_9_n_s_xor_d, + &pc88va_sgp_device::rop_A_n_s, + &pc88va_sgp_device::rop_B_n_s_or_d, + &pc88va_sgp_device::rop_C_nd, + &pc88va_sgp_device::rop_D_s_or_nd, + &pc88va_sgp_device::rop_E_n_s_and_d, + &pc88va_sgp_device::rop_F_fill_1, +}; + +u16 pc88va_sgp_device::rop_0_fill_0(u16 src, u16 dst) { return 0; } +u16 pc88va_sgp_device::rop_1_s_and_d(u16 src, u16 dst) { return src & dst; } +u16 pc88va_sgp_device::rop_2_ns_and_d(u16 src, u16 dst) { return (~src) & dst; } +u16 pc88va_sgp_device::rop_3_d(u16 src, u16 dst) { return dst; } +u16 pc88va_sgp_device::rop_4_s_and_nd(u16 src, u16 dst) { return src & (~dst); } +u16 pc88va_sgp_device::rop_5_s(u16 src, u16 dst) { return src; } +u16 pc88va_sgp_device::rop_6_s_xor_d(u16 src, u16 dst) { return src ^ dst; } +u16 pc88va_sgp_device::rop_7_s_or_d(u16 src, u16 dst) { return src | dst; } +u16 pc88va_sgp_device::rop_8_ns_or_d(u16 src, u16 dst) { return ~(src | dst); } +u16 pc88va_sgp_device::rop_9_n_s_xor_d(u16 src, u16 dst) { return ~(src ^ dst); } +u16 pc88va_sgp_device::rop_A_n_s(u16 src, u16 dst) { return ~src; } +u16 pc88va_sgp_device::rop_B_n_s_or_d(u16 src, u16 dst) { return (~src) | dst; } +u16 pc88va_sgp_device::rop_C_nd(u16 src, u16 dst) { return ~dst; } +u16 pc88va_sgp_device::rop_D_s_or_nd(u16 src, u16 dst) { return src | (~dst); } +u16 pc88va_sgp_device::rop_E_n_s_and_d(u16 src, u16 dst) { return ~(src & dst); } +u16 pc88va_sgp_device::rop_F_fill_1(u16 src, u16 dst) { return 0xffff; } + +const pc88va_sgp_device::tpmod_func pc88va_sgp_device::tpmod_table[4] = +{ + &pc88va_sgp_device::tpmod_0_always, + &pc88va_sgp_device::tpmod_1_src, + &pc88va_sgp_device::tpmod_2_dst, + &pc88va_sgp_device::tpmod_3_never +}; + +bool pc88va_sgp_device::tpmod_0_always(u16 src, u16 dst) { return true; } +bool pc88va_sgp_device::tpmod_1_src(u16 src, u16 dst) { return src != 0; } +bool pc88va_sgp_device::tpmod_2_dst(u16 src, u16 dst) { return dst == 0; } +bool pc88va_sgp_device::tpmod_3_never(u16 src, u16 dst) { return false; } /* * ---x ---- ---- ---- SF (0) shift source according to destination position @@ -315,7 +371,7 @@ void pc88va_sgp_device::start_exec() * ---- --00 ---- ---- transfer source as-is * ---- --01 ---- ---- do not transfer if source is 0 (transparent pen) * ---- --10 ---- ---- transfer only if destination is 0 - * ---- --11 ---- ---- <undocumented> + * ---- --11 ---- ---- <undocumented, assume never> * ---- ---- ---- xxxx LOGICAL OP * ---- ---- ---- 0000 0 * ---- ---- ---- 0001 Src AND Dst @@ -323,8 +379,8 @@ void pc88va_sgp_device::start_exec() * ---- ---- ---- 0011 NOP * ---- ---- ---- 0100 Src AND /Dst * ---- ---- ---- 0101 Src - * ---- ---- ---- 0110 Src XOR Dst - * ---- ---- ---- 0111 Src OR Dst + * ---- ---- ---- 0110 Src XOR Dst (ballbrkr) + * ---- ---- ---- 0111 Src OR Dst (boomer) * ---- ---- ---- 1000 /(Src OR Dst) * ---- ---- ---- 1001 /(Src XOR Dst) * ---- ---- ---- 1010 /Src @@ -337,10 +393,19 @@ void pc88va_sgp_device::start_exec() * PATBLT is identical to BITBLT except it repeats source copy * if it exceeds the clipping range. */ -void pc88va_sgp_device::cmd_blit(u16 draw_mode, bool is_patblt) +void pc88va_sgp_device::execute_blit(u16 draw_mode, bool is_patblt) { const u8 logical_op = draw_mode & 0xf; const u8 tp_mod = (draw_mode >> 8) & 0x3; +// const bool hd = !!BIT(draw_mode, 10); + // TODO: rtype gameplay enables VD +// const bool vd = !!BIT(draw_mode, 11); +// const bool sf = !!BIT(draw_mode, 12); + + if (draw_mode & 0xfc00) + { + popmessage("SGP: draw_mode = %04x (HD %d VD %d SF %d)", draw_mode, BIT(draw_mode, 10), BIT(draw_mode, 11), BIT(draw_mode, 12)); + } // TODO: boomer title screen if (is_patblt == true) @@ -349,19 +414,6 @@ void pc88va_sgp_device::cmd_blit(u16 draw_mode, bool is_patblt) // return; } - // ballbrkr: 6 - if (logical_op != 5) - { - LOG("BITBLT logical_op == %d\n", logical_op); - return; - } - - if (tp_mod > 1) - { - LOG("BITBLT tp_mod == %d\n", tp_mod); - return; - } - if (m_src.hsize != m_dst.hsize || m_src.vsize != m_dst.vsize) { LOG("BITBLT non-even sizes (%d x %d) x (%d x %d)\n", m_src.hsize, m_src.vsize, m_dst.hsize, m_dst.vsize); @@ -374,48 +426,52 @@ void pc88va_sgp_device::cmd_blit(u16 draw_mode, bool is_patblt) return; } - const u8 hsize_shift = m_src.pixel_mode == 1 ? 1 : 0; + //static const u8 shift_table = { 3, 1, 0, -1 }; + //const u8 hsize_shift = m_src.pixel_mode == 1 ? 1 : 0; for (int yi = 0; yi < m_src.vsize; yi ++) { u32 src_address = m_src.address + (yi * m_src.fb_pitch); u32 dst_address = m_dst.address + (yi * m_dst.fb_pitch); - for (int xi = 0; xi < (m_src.hsize >> hsize_shift); xi ++) + // TODO: should fetch on demand, depending on what's the color mode/the start dot etc. + for (int xi = 0; xi < m_src.hsize; xi ++) { - // TODO: not very efficient, we need a cleaner per-pixel RMW phase - const u8 src_dot = m_data->read_byte(src_address); - const u8 dst_dot = m_data->read_byte(dst_address); - u8 result = 0; - switch(m_src.pixel_mode) { // 4bpp (shinraba) case 1: - for (int pixi = 0; pixi < 2; pixi ++) + { + const u8 nibble = xi & 1; + u8 src = (m_data->read_byte(src_address + (xi >> 1)) >> (nibble * 4)) & 0xf; + u8 dst = m_data->read_byte(dst_address + (xi >> 1)); + u8 result = dst & (nibble ? 0x0f : 0xf0); + + if ((this->*tpmod_table[tp_mod])(src, dst)) { - u8 cur_pixel = (src_dot & 0xf); - if (cur_pixel || tp_mod == 0) - result |= (src_dot & 0xf) << (pixi * 4); - else - result |= (dst_dot & 0xf) << (pixi * 4); + result |= (this->*rop_table[logical_op])(src, dst) << (nibble * 4); + m_data->write_byte(dst_address + (xi >> 1), result); } + break; + } // 8bpp (tetris) case 2: - u8 cur_pixel = (src_dot & 0xff); - if (cur_pixel || tp_mod == 0) - result |= (cur_pixel & 0xff); - else - result |= (dst_dot & 0xff); + { + u8 src = m_data->read_byte(src_address + xi) & 0xff; + u8 dst = m_data->read_byte(dst_address + xi) & 0xff; + u8 result = dst; + + if ((this->*tpmod_table[tp_mod])(src, dst)) + { + result = (this->*rop_table[logical_op])(src, dst); + m_data->write_byte(dst_address + xi, result); + } break; + } } - - m_data->write_byte(dst_address, result); - src_address ++; - dst_address ++; } } } diff --git a/src/mame/nec/pc88va_sgp.h b/src/mame/nec/pc88va_sgp.h index 1ef011f72b0..24a53cc6f95 100644 --- a/src/mame/nec/pc88va_sgp.h +++ b/src/mame/nec/pc88va_sgp.h @@ -49,8 +49,34 @@ private: void start_exec(); - // commands - void cmd_blit(u16 draw_mode, bool is_patblt); + void execute_blit(u16 draw_mode, bool is_patblt); + + typedef u16 (pc88va_sgp_device::*rop_func)(u16 src, u16 dst); + + static const rop_func rop_table[16]; + u16 rop_0_fill_0(u16 src, u16 dst); + u16 rop_1_s_and_d(u16 src, u16 dst); + u16 rop_2_ns_and_d(u16 src, u16 dst); + u16 rop_3_d(u16 src, u16 dst); + u16 rop_4_s_and_nd(u16 src, u16 dst); + u16 rop_5_s(u16 src, u16 dst); + u16 rop_6_s_xor_d(u16 src, u16 dst); + u16 rop_7_s_or_d(u16 src, u16 dst); + u16 rop_8_ns_or_d(u16 src, u16 dst); + u16 rop_9_n_s_xor_d(u16 src, u16 dst); + u16 rop_A_n_s(u16 src, u16 dst); + u16 rop_B_n_s_or_d(u16 src, u16 dst); + u16 rop_C_nd(u16 src, u16 dst); + u16 rop_D_s_or_nd(u16 src, u16 dst); + u16 rop_E_n_s_and_d(u16 src, u16 dst); + u16 rop_F_fill_1(u16 src, u16 dst); + + typedef bool (pc88va_sgp_device::*tpmod_func)(u16 src, u16 dst); + static const tpmod_func tpmod_table[4]; + bool tpmod_0_always(u16 src, u16 dst); + bool tpmod_1_src(u16 src, u16 dst); + bool tpmod_2_dst(u16 src, u16 dst); + bool tpmod_3_never(u16 src, u16 dst); }; |