From 0257b7cc4c04b4dbbf984bc157d9c6c12c1c735e Mon Sep 17 00:00:00 2001 From: Patrick Mackinlay Date: Fri, 23 Feb 2018 19:34:58 +0700 Subject: interpro: graphics improvements (#3263) * interpro: graphics improvements * basic bitblt and line drawing is working well enough for now * de-static'd MCFG stuff * some keyboard notes tweaked * interpro: oops (nw) --- src/devices/bus/interpro/keyboard/hle.cpp | 52 +- src/devices/bus/interpro/keyboard/hle.h | 4 +- src/devices/bus/interpro/sr/gt.cpp | 1311 +++++++++++++++++++++++++++-- src/devices/bus/interpro/sr/gt.h | 326 ++++++- src/devices/bus/interpro/sr/sr.cpp | 29 +- src/devices/bus/interpro/sr/sr.h | 18 +- src/devices/bus/interpro/sr/sr_cards.cpp | 3 + 7 files changed, 1593 insertions(+), 150 deletions(-) diff --git a/src/devices/bus/interpro/keyboard/hle.cpp b/src/devices/bus/interpro/keyboard/hle.cpp index 9bbc2fefe78..9201cfd4206 100644 --- a/src/devices/bus/interpro/keyboard/hle.cpp +++ b/src/devices/bus/interpro/keyboard/hle.cpp @@ -11,8 +11,8 @@ * function keys in groups of 9, 36 and 12 from left to right. * * The following describes the key labels and positions according to the - * standard US English keyboard layout. It's unknown what other variations - * existed at this point. + * standard US English keyboard layout. At least a German keyboard variant is + * known to have existed. * * Upper bank keys indicated here with asterisks are printed in white, as are * all the A*, B* and C* keys; all the others are printed in brown. @@ -68,23 +68,30 @@ * * From Host Purpose Expected Response * --------- ------- ----------------- - * D Diagnostic 0xff , where status bits are set to - * indicate diagnostic failure source: 0x8=EPROM - * checksum, 0x10=RAM error, 0x20=ROM checksum + * 0x1b 0x42 probably controls several functions: * - * B ( Bell On None + * 0x01 sound bell? + * 0x04 keyclick on + * 0x08 membrane click on + * 0x20 bell tone loud/soft/none? (seems only loud/none) * - * B ) Bell Off None + * 0x1b 0x44 reset/diag 0xff , where status bits are set to + * indicate diagnostic failure source: 0x8=EPROM + * checksum, 0x10=RAM error, 0x20=ROM checksum * - * B Unknown None + * 0x1b 0x55 activate scan code keyup/down mode? * * The keyboard has a keyboard click function, and the LED indicators described * earlier, meaning that there are additional commands to enable and disable * these functions. * - * The keyboard transmits ASCII codes corresponding to the keycap labels for - * keys which map to the ASCII character set. Modifiers are applied by the - * keyboard itself, and do not generate make/break codes of their own. + * The keyboard has at least two operating modes: a simple ASCII mode which is + * active after reset, and a scancode mode generating make/break codes with + * full support for all the qualifiers and other non-ASCII keys. + * + * In ASCII mode, the keyboard transmits ASCII codes corresponding to the key + * labels for keys which map to the ASCII character set. Modifiers are applied + * by the keyboard itself, and do not generate make/break codes of their own. * * The following non-ASCII sequences are recognised in the system software, * and likely correspond to specific keyboard keys or buttons: @@ -101,6 +108,7 @@ * - auto-repeat * - key click and LED commands * - alternative layouts + * - scancode make/break mode * */ #include "emu.h" @@ -389,39 +397,43 @@ void hle_device_base::received_byte(u8 byte) switch (m_rx_state) { - case RX_ESC: + case RX_COMMAND: switch (byte) { - case 'B': // bell - m_rx_state = RX_BELL; + case 0x42: // configure flags + m_rx_state = RX_FLAGS; break; - case 'D': // diagnostic + case 0x44: // reset/diagnostic transmit_byte(0xff); transmit_byte(0x00); m_rx_state = RX_IDLE; break; + + case 0x55: + break; } break; - case RX_BELL: + case RX_FLAGS: + // FIXME: this logic is wrong (should decode the various fields), but + // generates bell sounds at the right time for now switch (byte) { - case '(': + case 0x28: LOG("bell on\n"); m_beeper_state |= u8(BEEPER_BELL); m_beeper->set_state(m_beeper_state ? 1 : 0); break; - case ')': + case 0x29: LOG("bell off\n"); m_beeper_state &= ~u8(BEEPER_BELL); m_beeper->set_state(m_beeper_state ? 1 : 0); break; default: - // FIXME: boot code sends 0x8, unknown meaning break; } m_rx_state = RX_IDLE; @@ -429,7 +441,7 @@ void hle_device_base::received_byte(u8 byte) case RX_IDLE: if (byte == 0x1b) - m_rx_state = RX_ESC; + m_rx_state = RX_COMMAND; break; } } diff --git a/src/devices/bus/interpro/keyboard/hle.h b/src/devices/bus/interpro/keyboard/hle.h index fac0b48d3be..07f92a7e0d2 100644 --- a/src/devices/bus/interpro/keyboard/hle.h +++ b/src/devices/bus/interpro/keyboard/hle.h @@ -66,8 +66,8 @@ private: enum : u8 { RX_IDLE, - RX_ESC, - RX_BELL + RX_COMMAND, + RX_FLAGS }; enum : u8 { diff --git a/src/devices/bus/interpro/sr/gt.cpp b/src/devices/bus/interpro/sr/gt.cpp index 2b852cff6cd..b82f0894ced 100644 --- a/src/devices/bus/interpro/sr/gt.cpp +++ b/src/devices/bus/interpro/sr/gt.cpp @@ -2,14 +2,51 @@ // copyright-holders:Patrick Mackinlay /* - * An emulation of GT graphics for Intergraph InterPro. + * An emulation of GT graphics, also known as Memory Mapped Graphics (MMG), for + * Intergraph InterPro systems. * * TODO - * - control register (including primary/secondary buffer selection) - * - DP8510V BITBLT unit - * - custom Bresenham line drawing ASIC - * - support GT+, GTII, GTII+ + * - pixel-perfect line drawing to match diagnostics + * - many other diagnostic test failures + * - RI aliased and anti-aliased line drawing + * - fifos (no information at this point) + * - 76Hz refresh, 2MPix boards + * - support GT+, GTII * - reset behaviour + * + * GT: original 2020 graphics + * GT+: double buffered + * GTII: double buffered, highlight + * + * 068 2400 Graphics f/1 1Mp Monitor (V-60) + * 069 2400 Graphics f/2 1Mp Monitors (V-60) + * GT+ 070 2400 Graphics f/1 1Mp Monitor (V-76) + * GT+ 071 2400 Graphics f/2 1Mp Monitors (V-76) + * GT+ (2MPix) 081 2400 Graphics f/1 2Mp Monitor (V-60/76) + * GT+ 101 2400 Graphics f/1 1Mp Monitor (V-76) + * GT+ 102 2400 Graphics f/2 1Mp Monitors (V-76) + * 135 GT II Graphics f/1 2Mp Monitor (V-60/76) + * 136 GT II Graphics f/2 2Mp Monitor (V-60/76) + * GT/MMG 963 2000 Graphics f/1 1Mp Monitor + * GT/MMG A79 2000 Graphics f/2 1Mp Monitors + * B67 GT Plus Graphics f/1 1Mp Monitor (V-76) How is this different to 070/101? + * GTII B68 GT II Graphics f/1 1Mp Monitor (V-76) + * GTII B70 GT II Graphics f/2 1Mp Monitors (V-76) + * GTII B92 GT II Graphics f/1 2Mp Monitor (V-60/76) 6400 board + * GTII B93 GT II Graphics f/2 2Mp Monitors (V-60/76) + * GTII C05 25Mhz GTII Graphics f/1 1Mp Monitor + * GTII C06 25Mhz GTII Graphics f/2 1Mp Monitors + * GTII C41 GTII 60/76Hz Graphics f/1 2Mp Monitor Functionally equivalant to MPCBB92. Will operate on the 67XX series. (maybe GTDB, i.e. GT graphics for 6000) + * GTII C42 GTII 60/76Hz Graphics f/2 2Mp Monitor Functionally equivalant to MPCBB93. Will operate on the 67XX series. (maybe GTDB, i.e. GT graphics for 6000) + * + * Board idprom feature byte 0 contains various flags: + * + * 0x01 ? single : dual + * 0x02 ? 1 MPix (1184x884) : 2 MPix (1664x1248) + * 0x04 ? gt (memsize 0x00100000/1M) : gtplus (memsize 0x01000000/16M) + * + * 1MPix boards: (feature[0] & 0xc0) == 0x80 ? 76Hz (xoff 264, yoff 57) : 60Hz (xoff 296, yoff 34) + * 2MPix boards: (feature[0] & 0xc0) == 0x80 ? 76Hz (xoff 391, yoff 74) : (feature[0] & 0xc0) == 0x00 ? 60Hz (xoff 407, yoff 48) : 60/76Hz (check control register) */ #include "emu.h" @@ -17,26 +54,119 @@ #include "gt.h" -#define VERBOSE 0 +#define LOG_GENERAL (1U << 0) +#define LOG_LINE (1U << 1) +#define LOG_BLIT (1U << 2) + +//#define VERBOSE (LOG_GENERAL | LOG_LINE | LOG_BLIT) +//#define VERBOSE (LOG_GENERAL | LOG_LINE) + #include "logmacro.h" -ADDRESS_MAP_START(mpcb963_device::map) +DEFINE_DEVICE_TYPE(MPCB963, mpcb963_device, "mpcb963", "2000 Graphics f/1 1Mp Monitor") +DEFINE_DEVICE_TYPE(MPCBA79, mpcba79_device, "mpcba79", "2000 Graphics f/2 1Mp Monitors") +DEFINE_DEVICE_TYPE(MPCB070, mpcb070_device, "mpcb070", "2400 Graphics f/1 1Mp Monitor (V-76)") +DEFINE_DEVICE_TYPE(MPCB071, mpcb071_device, "mpcb071", "2400 Graphics f/2 1Mp Monitors (V-76)") +DEFINE_DEVICE_TYPE(MPCB081, mpcb081_device, "mpcb081", "2400 Graphics f/1 2Mp Monitor (V-60/76)") + +ADDRESS_MAP_START(gt_device_base::map) + AM_RANGE(0x0a0, 0x0a3) AM_READWRITE8(contrast_dac_r, contrast_dac_w, 0x000000ff) // w/o? + AM_RANGE(0x0b0, 0x0b3) AM_READWRITE(control_r, control_w) + + AM_RANGE(0x0c0, 0x0c3) AM_READWRITE(blit_src_address_r, blit_src_address_w) // w/o? + AM_RANGE(0x0c4, 0x0c7) AM_READWRITE(blit_dst_address_r, blit_dst_address_w) // w/o? + AM_RANGE(0x0c8, 0x0cb) AM_READWRITE16(blit_width_r, blit_width_w, 0x0000ffff) // w/o? + + AM_RANGE(0x0d0, 0x0d3) AM_WRITE(blit_control_w) + AM_RANGE(0x0d4, 0x0d7) AM_READWRITE8(plane_enable_r, plane_enable_w, 0x000000ff) + AM_RANGE(0x0d8, 0x0db) AM_READWRITE8(plane_data_r, plane_data_w, 0x000000ff) + + AM_RANGE(0x100, 0x103) AM_READWRITE16(bsga_width_r, bsga_width_w, 0x0000ffff) + AM_RANGE(0x100, 0x103) AM_READWRITE16(bsga_tmp_r, bsga_tmp_w, 0xffff0000) + + AM_RANGE(0x104, 0x107) AM_WRITE16(bsga_xmin_w, 0x0000ffff) + AM_RANGE(0x108, 0x10b) AM_WRITE16(bsga_ymin_w, 0x0000ffff) + AM_RANGE(0x10c, 0x10f) AM_READWRITE16(bsga_xmin_r, bsga_xmin_w, 0x0000ffff) + AM_RANGE(0x10c, 0x10f) AM_READWRITE16(bsga_ymin_r, bsga_ymin_w, 0xffff0000) + + AM_RANGE(0x110, 0x113) AM_READ16(bsga_acc0_r, 0x0000ffff) + AM_RANGE(0x110, 0x113) AM_READ16(bsga_acc1_r, 0xffff0000) + + AM_RANGE(0x114, 0x117) AM_WRITE16(bsga_xmax_w, 0x0000ffff) + AM_RANGE(0x118, 0x11b) AM_WRITE16(bsga_ymax_w, 0x0000ffff) + AM_RANGE(0x11c, 0x11f) AM_READWRITE16(bsga_xmax_r, bsga_xmax_w, 0x0000ffff) + AM_RANGE(0x11c, 0x11f) AM_READWRITE16(bsga_ymax_r, bsga_ymax_w, 0xffff0000) + + AM_RANGE(0x120, 0x123) AM_READ16(bsga_src0_r, 0x0000ffff) + AM_RANGE(0x120, 0x123) AM_READ16(bsga_src1_r, 0xffff0000) + + AM_RANGE(0x124, 0x127) AM_WRITE16(bsga_xin1_w, 0x0000ffff) + AM_RANGE(0x128, 0x12b) AM_WRITE16(bsga_yin1_w, 0x0000ffff) + AM_RANGE(0x12c, 0x12f) AM_READ16(bsga_xin_r, 0x0000ffff) + AM_RANGE(0x12c, 0x12f) AM_READ16(bsga_yin_r, 0xffff0000) + AM_RANGE(0x12c, 0x12f) AM_WRITE(bsga_xin1yin1_w) + AM_RANGE(0x130, 0x133) AM_READ16(bsga_status_r, 0x0000ffff) + AM_RANGE(0x134, 0x137) AM_WRITE16(bsga_xin2_w, 0x0000ffff) + AM_RANGE(0x138, 0x13b) AM_WRITE16(bsga_yin2_w, 0x0000ffff) + AM_RANGE(0x13c, 0x13f) AM_WRITE(bsga_xin2yin2_w) + + // FDMDISK says all the ri registers are write only? + // possibly also all 24 bit + AM_RANGE(0x140, 0x143) AM_READWRITE(ri_initial_distance_r, ri_initial_distance_w) + AM_RANGE(0x144, 0x147) AM_READWRITE(ri_distance_both_r, ri_distance_both_w) + AM_RANGE(0x148, 0x14b) AM_READWRITE(ri_distance_major_r, ri_distance_major_w) + AM_RANGE(0x14c, 0x14f) AM_READWRITE(ri_initial_address_r, ri_initial_address_w) + AM_RANGE(0x150, 0x153) AM_READWRITE(ri_address_both_r, ri_address_both_w) + AM_RANGE(0x154, 0x157) AM_READWRITE(ri_address_major_r, ri_address_major_w) + AM_RANGE(0x158, 0x15b) AM_READWRITE(ri_initial_error_r, ri_initial_error_w) + AM_RANGE(0x15c, 0x15f) AM_READWRITE(ri_error_both_r, ri_error_both_w) + AM_RANGE(0x160, 0x163) AM_READWRITE(ri_error_major_r, ri_error_major_w) + AM_RANGE(0x164, 0x167) AM_READWRITE(ri_stop_count_r, ri_stop_count_w) // 16 bit? + + AM_RANGE(0x16c, 0x16f) AM_READWRITE(ri_control_r, ri_control_w) // mask 1ff? + + //AM_RANGE(0x174, 0x177) AM_READWRITE(ri_xfer_r, ri_xfer_w) + //AM_RANGE(0x178, 0x17b) AM_READWRITE(ri_xfer_r, ri_xfer_w) + AM_RANGE(0x17c, 0x17f) AM_READWRITE(ri_xfer_r, ri_xfer_w) + + AM_RANGE(0x1a4, 0x1a7) AM_WRITE(bsga_float_w) + + //AM_RANGE(0x1c0, 0x1c3) + //AM_RANGE(0x1c4, 0x1c7) + //AM_RANGE(0x1c8, 0x1cb) + //AM_RANGE(0x1cc, 0x1cf) // write32 - float conversion control (inhibit/enable overflow detection?) + +/* + * Don't know where/how these fifos come into play yet: + * + #define GT_FIFO_CONTROL(slot) GT_BASE(slot, 0x300) + #define GT_FIFO_STATUS(slot) GT_BASE(slot, 0x304) + #define GT_FIFO_LOW_WATER(slot) GT_BASE(slot, 0x330) + #define GT_FIFO_HI_WATER(slot) GT_BASE(slot, 0x334) + + #define GT_FIFO_LW_ENB 0x08 + #define GT_FIFO_LW_INTR 0x40 + #define GT_FIFO_HW_INTR 0x80 + */ +ADDRESS_MAP_END + +ADDRESS_MAP_START(single_gt_device_base::map) + AM_IMPORT_FROM(gt_device_base::map) + AM_RANGE(0x00000000, 0x0000007f) AM_READ(idprom_r) - AM_RANGE(0x00000080, 0x0000008f) AM_DEVICE8("ramdac0", bt459_device, map, 0x00ff) - AM_RANGE(0x000000b0, 0x000000b3) AM_READWRITE16(control_r, control_w, 0xffff) - //AM_RANGE(0x000000d4, 0x000000d7) AM_READWRITE8(, 0xff) // currently unknown - //AM_RANGE(0x000000a0, 0x000000a0) AM_READWRITE8(, 0xff) // currently unknown - AM_RANGE(0x00400000, 0x005fffff) AM_READWRITE(vram_r, vram_w) + AM_RANGE(0x00000080, 0x0000008f) AM_DEVICE8("ramdac0", bt459_device, map, 0x000000ff) + + AM_RANGE(0x00400000, 0x005fffff) AM_READWRITE(buffer_r, buffer_w) ADDRESS_MAP_END -ADDRESS_MAP_START(mpcba79_device::map) +ADDRESS_MAP_START(dual_gt_device_base::map) + AM_IMPORT_FROM(gt_device_base::map) + AM_RANGE(0x00000000, 0x0000007f) AM_READ(idprom_r) - AM_RANGE(0x00000080, 0x0000008f) AM_DEVICE8("ramdac0", bt459_device, map, 0x00ff) - AM_RANGE(0x00000090, 0x0000009f) AM_DEVICE8("ramdac1", bt459_device, map, 0x00ff) - AM_RANGE(0x000000b0, 0x000000b3) AM_READWRITE16(control_r, control_w, 0xffff) - //AM_RANGE(0x000000d4, 0x000000d7) AM_READWRITE8(, 0xff) // currently unknown - //AM_RANGE(0x000000a0, 0x000000a0) AM_READWRITE8(, 0xff) // currently unknown - AM_RANGE(0x00400000, 0x007fffff) AM_READWRITE(vram_r, vram_w) + AM_RANGE(0x00000080, 0x0000008f) AM_DEVICE8("ramdac0", bt459_device, map, 0x000000ff) + AM_RANGE(0x00000090, 0x0000009f) AM_DEVICE8("ramdac1", bt459_device, map, 0x000000ff) + + AM_RANGE(0x00400000, 0x007fffff) AM_READWRITE(buffer_r, buffer_w) ADDRESS_MAP_END ROM_START(mpcb963) @@ -46,34 +176,87 @@ ROM_END ROM_START(mpcba79) ROM_REGION(0x80, "idprom", 0) - ROM_LOAD32_BYTE("mpcba79a.bin", 0x0, 0x20, CRC(b3b98324) SHA1(77b4ed0bbc6ed19646c4536d9976563f78961408)) + ROM_LOAD32_BYTE("mpcba79a.bin", 0x0, 0x20, CRC(a223fb92) SHA1(6dbd2aa75f1052b2467638e7d6e72c151fe23cfd)) ROM_END -DEFINE_DEVICE_TYPE(MPCB963, mpcb963_device, "mpcb963", "2000 Graphics f/1 1Mp Monitor") -DEFINE_DEVICE_TYPE(MPCBA79, mpcba79_device, "mpcba79", "2000 Graphics f/2 1Mp Monitors") +ROM_START(mpcb070) + ROM_REGION(0x80, "idprom", 0) + ROM_LOAD32_BYTE("mpcb070b.bin", 0x0, 0x20, CRC(e0c302d1) SHA1(df2086fbd667605dd2c4a61d48dbcd0a38376e20)) +ROM_END -/* - * The raw screen data here is just a guess for now. Real values will need to - * be determined to ensure the cursor positions generated by the Bt459 are - * aligned properly on the screen. - */ +ROM_START(mpcb071) + ROM_REGION(0x80, "idprom", 0) + ROM_LOAD32_BYTE("mpcb071b.bin", 0x0, 0x20, CRC(a9b635d7) SHA1(1b39d98a2f74cfa0a7b430b838412fbc1ade0e5d)) +ROM_END + +ROM_START(mpcb081) + ROM_REGION(0x80, "idprom", 0) + ROM_LOAD32_BYTE("mpcb081b.bin", 0x0, 0x20, CRC(90291129) SHA1(cc6debd0307280ed73dbf5b555aa7448c2bcb7ad)) +ROM_END + +// FIXME: can't account for this delta yet +#define GT_X_DELTA 20 + +// FIXME: the screen parameters below match what's coded in the system software, +// (except for the unexplained X_DELTA above), but produce an off-by-one mismatch +// in the cursor diagnostic tests. Visual mouse cursor positioning does seem to +// be correct however, so don't quite understand where the problem is. + +// GT, 60Hz, single 1MPix MACHINE_CONFIG_START(mpcb963_device::device_add_mconfig) MCFG_SCREEN_ADD("screen0", RASTER) - MCFG_SCREEN_RAW_PARAMS(GT_PIXCLOCK, GT_XPIXELS, 0, GT_XPIXELS, GT_YPIXELS, 0, GT_YPIXELS) + MCFG_SCREEN_RAW_PARAMS(GT_PIXCLOCK, GT_XPIXELS + 296 + GT_X_DELTA, 296 + GT_X_DELTA, GT_XPIXELS + 296 + GT_X_DELTA, GT_YPIXELS + 34, 34, GT_YPIXELS + 34) MCFG_SCREEN_UPDATE_DEVICE("", mpcb963_device, screen_update0) MCFG_DEVICE_ADD("ramdac0", BT459, 0) + MCFG_DEVICE_ADD("bpu0", DP8510, 0) MACHINE_CONFIG_END +// GT, 60Hz, dual 1MPix MACHINE_CONFIG_START(mpcba79_device::device_add_mconfig) MCFG_SCREEN_ADD("screen0", RASTER) - MCFG_SCREEN_RAW_PARAMS(GT_PIXCLOCK, GT_XPIXELS, 0, GT_XPIXELS, GT_YPIXELS, 0, GT_YPIXELS) + MCFG_SCREEN_RAW_PARAMS(GT_PIXCLOCK, GT_XPIXELS + 296 + GT_X_DELTA, 296 + GT_X_DELTA, GT_XPIXELS + 296 + GT_X_DELTA, GT_YPIXELS + 34, 34, GT_YPIXELS + 34) MCFG_SCREEN_UPDATE_DEVICE("", mpcba79_device, screen_update0) MCFG_DEVICE_ADD("ramdac0", BT459, 0) + MCFG_DEVICE_ADD("bpu0", DP8510, 0) MCFG_SCREEN_ADD("screen1", RASTER) - MCFG_SCREEN_RAW_PARAMS(GT_PIXCLOCK, GT_XPIXELS, 0, GT_XPIXELS, GT_YPIXELS, 0, GT_YPIXELS) + MCFG_SCREEN_RAW_PARAMS(GT_PIXCLOCK, GT_XPIXELS + 296 + GT_X_DELTA, 296 + GT_X_DELTA, GT_XPIXELS + 296 + GT_X_DELTA, GT_YPIXELS + 34, 34, GT_YPIXELS + 34) MCFG_SCREEN_UPDATE_DEVICE("", mpcba79_device, screen_update1) MCFG_DEVICE_ADD("ramdac1", BT459, 0) + MCFG_DEVICE_ADD("bpu1", DP8510, 0) +MACHINE_CONFIG_END + +// GT+, 76Hz, single 1MPix +MACHINE_CONFIG_START(mpcb070_device::device_add_mconfig) + MCFG_SCREEN_ADD("screen0", RASTER) + MCFG_SCREEN_RAW_PARAMS(GT_PIXCLOCK, GT_XPIXELS + 264 + GT_X_DELTA, 264 + GT_X_DELTA, GT_XPIXELS + 264 + GT_X_DELTA, GT_YPIXELS + 57, 57, GT_YPIXELS + 57) + MCFG_SCREEN_UPDATE_DEVICE("", mpcb070_device, screen_update0) + MCFG_DEVICE_ADD("ramdac0", BT459, 0) + MCFG_DEVICE_ADD("bpu0", DP8510, 0) +MACHINE_CONFIG_END + +// GT+, 76Hz, dual 1MPix +MACHINE_CONFIG_START(mpcb071_device::device_add_mconfig) + MCFG_SCREEN_ADD("screen0", RASTER) + MCFG_SCREEN_RAW_PARAMS(GT_PIXCLOCK, GT_XPIXELS + 264 + GT_X_DELTA, 264 + GT_X_DELTA, GT_XPIXELS + 264 + GT_X_DELTA, GT_YPIXELS + 57, 57, GT_YPIXELS + 57) + MCFG_SCREEN_UPDATE_DEVICE("", mpcb071_device, screen_update0) + MCFG_DEVICE_ADD("ramdac0", BT459, 0) + MCFG_DEVICE_ADD("bpu0", DP8510, 0) + + MCFG_SCREEN_ADD("screen1", RASTER) + MCFG_SCREEN_RAW_PARAMS(GT_PIXCLOCK, GT_XPIXELS + 264 + GT_X_DELTA, 264 + GT_X_DELTA, GT_XPIXELS + 264 + GT_X_DELTA, GT_YPIXELS + 57, 57, GT_YPIXELS + 57) + MCFG_SCREEN_UPDATE_DEVICE("", mpcb071_device, screen_update1) + MCFG_DEVICE_ADD("ramdac1", BT459, 0) + MCFG_DEVICE_ADD("bpu1", DP8510, 0) +MACHINE_CONFIG_END + +// GT+, 76Hz, single 2MPix +MACHINE_CONFIG_START(mpcb081_device::device_add_mconfig) + MCFG_SCREEN_ADD("screen0", RASTER) + MCFG_SCREEN_RAW_PARAMS(GT_PIXCLOCK, 1664 + 391 + GT_X_DELTA, 391 + GT_X_DELTA, 1664 + 391 + GT_X_DELTA, 1248 + 74, 74, 1248 + 74) + MCFG_SCREEN_UPDATE_DEVICE("", mpcb081_device, screen_update0) + MCFG_DEVICE_ADD("ramdac0", BT459, 0) + MCFG_DEVICE_ADD("bpu0", DP8510, 0) MACHINE_CONFIG_END gt_device_base::gt_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) @@ -81,9 +264,40 @@ gt_device_base::gt_device_base(const machine_config &mconfig, device_type type, { } +single_gt_device_base::single_gt_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : gt_device_base(mconfig, type, tag, owner, clock) + , m_screen{ { { *this, "ramdac0" },{ *this, "bpu0" } } } +{ +} + +dual_gt_device_base::dual_gt_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : gt_device_base(mconfig, type, tag, owner, clock) + , m_screen{ { { *this, "ramdac0" },{ *this, "bpu0" } },{ { *this, "ramdac1" },{ *this, "bpu1" } } } +{ +} + mpcb963_device::mpcb963_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : gt_device_base(mconfig, MPCB963, tag, owner, clock) - , m_screen{ { { *this, "ramdac0" }, {}, true } } + : single_gt_device_base(mconfig, MPCB963, tag, owner, clock) +{ +} + +mpcba79_device::mpcba79_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : dual_gt_device_base(mconfig, MPCBA79, tag, owner, clock) +{ +} + +mpcb070_device::mpcb070_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : single_gt_device_base(mconfig, MPCB070, tag, owner, clock) +{ +} + +mpcb071_device::mpcb071_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : dual_gt_device_base(mconfig, MPCB071, tag, owner, clock) +{ +} + +mpcb081_device::mpcb081_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : single_gt_device_base(mconfig, MPCB081, tag, owner, clock) { } @@ -92,102 +306,1055 @@ const tiny_rom_entry *mpcb963_device::device_rom_region() const return ROM_NAME(mpcb963); } -void mpcb963_device::device_start() +const tiny_rom_entry *mpcba79_device::device_rom_region() const +{ + return ROM_NAME(mpcba79); +} + +const tiny_rom_entry *mpcb070_device::device_rom_region() const +{ + return ROM_NAME(mpcb070); +} + +const tiny_rom_entry *mpcb071_device::device_rom_region() const { - gt_device_base::device_start(); + return ROM_NAME(mpcb071); +} - // allocate double-buffered vram - m_screen[0].vram.reset(new u8[GT_VRAM_SIZE]); +const tiny_rom_entry *mpcb081_device::device_rom_region() const +{ + return ROM_NAME(mpcb081); +} + +void gt_device_base::device_start() +{ + sr_card_device_base::device_start(); save_item(NAME(m_control)); - save_pointer(NAME(m_screen[0].vram.get()), GT_VRAM_SIZE); + + for (int i = 0; i < get_screen_count(); i++) + { + gt_screen_t &screen = get_screen(i); + + // FIXME: handle different buffer sizes + screen.buffer.reset(new u8[GT_BUFFER_SIZE * 2]); + screen.mask.reset(new u8[GT_BUFFER_SIZE * 2]); + + save_pointer(screen.buffer.get(), util::string_format("buffer%i", i).c_str(), GT_BUFFER_SIZE * 2); + save_pointer(screen.mask.get(), util::string_format("mask%i", i).c_str(), GT_BUFFER_SIZE * 2); + } + + // allocate timers + m_blit_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gt_device_base::blit), this)); + m_line_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gt_device_base::line), this)); + m_done_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gt_device_base::done), this)); + + // FIXME: 76Hz boards + m_vblank_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gt_device_base::vblank), this)); + + // TODO: turn this off for now because it's not right + //m_vblank_timer->adjust(attotime::from_hz(60), ASSERT_LINE); } +TIMER_CALLBACK_MEMBER(gt_device_base::vblank) +{ + // restart timer + if (param == ASSERT_LINE) + { + m_control |= GFX_VERT_BLNK; + m_vblank_timer->adjust(attotime::from_msec(5), CLEAR_LINE); -WRITE16_MEMBER(mpcb963_device::control_w) + irq(param); + } + else + { + m_control &= ~GFX_VERT_BLNK; + m_vblank_timer->adjust(attotime::attotime::from_hz(60), ASSERT_LINE); + } +} + +WRITE32_MEMBER(gt_device_base::control_w) { - LOG("control_w 0x%04x\n", data); + LOG("control_w 0x%08x\n", data); + if (data & GFX_BSGA_RST) + { + // set graphics busy and schedule a reset + m_control |= GFX_GRPHCS_BUSY; + + // reset the bitblt fifo pointers + for (int i = 0; i < get_screen_count(); i++) + get_screen(i).bpu->reset(); + + m_done_timer->adjust(attotime::from_msec(10), GFX_GRPHCS_BUSY); + } + + // pass direction to bpu + for (int i = 0; i < get_screen_count(); i++) + get_screen(i).bpu->barrel_input_select((data & GFX_BLIT_DIR) ? ASSERT_LINE : CLEAR_LINE); m_control = data; } -READ32_MEMBER(mpcb963_device::vram_r) +WRITE32_MEMBER(single_gt_device_base::blit_control_w) { - return ((u32 *)m_screen[(offset >> 19) & 1].vram.get())[offset & 0x7ffff]; + m_screen[0].bpu->control_w( + ((data & BLIT0_CONTROL_FS) >> 4) << 12 | + ((data & BLIT0_CONTROL_SN) >> 12) << 8 | + ((data & BLIT0_CONTROL_LM) >> 20) << 4 | + ((data & BLIT0_CONTROL_RM) >> 28) << 0); } -WRITE32_MEMBER(mpcb963_device::vram_w) +WRITE32_MEMBER(dual_gt_device_base::blit_control_w) { - const gt_screen_t >_screen = m_screen[(offset >> 19) & 1]; + // TODO: find a test case with blits to second screen and verify + m_screen[0].bpu->control_w( + ((data & BLIT0_CONTROL_FS) >> 4) << 12 | + ((data & BLIT0_CONTROL_SN) >> 12) << 8 | + ((data & BLIT0_CONTROL_LM) >> 20) << 4 | + ((data & BLIT0_CONTROL_RM) >> 28) << 0); - ((u32 *)gt_screen.vram.get())[offset & 0x7ffff] = (((u32 *)gt_screen.vram.get())[offset & 0x7ffff] & ~mem_mask) | (data & mem_mask); + m_screen[1].bpu->control_w( + ((data & BLIT1_CONTROL_FS) >> 0) << 12 | + ((data & BLIT1_CONTROL_SN) >> 8) << 8 | + ((data & BLIT1_CONTROL_LM) >> 16) << 4 | + ((data & BLIT1_CONTROL_RM) >> 24) << 0); } -u32 mpcb963_device::screen_update0(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +void gt_device_base::bsga_clip_status(s16 x, s16 y) { - const gt_screen_t >_screen = m_screen[0]; + // compute Cohen-Sutherland clipping outcode + LOG("bsga_clip_status previous 0x%04x\n", m_bsga_status); + m_bsga_status &= STATUS_CLIP1_MASK; + m_bsga_status <<= 4; - gt_screen.ramdac->screen_update(screen, bitmap, cliprect, >_screen.vram[gt_screen.primary ? 0x000000 : GT_BUFFER_SIZE]); + LOG("bsga_clip_status shifted 0x%04x\n", m_bsga_status); + LOG("bsga_clip_status (%d,%d) (%d,%d,%d,%d)\n", x, y, (s16)m_bsga_xmin, (s16)m_bsga_xmax, (s16)m_bsga_ymin, (s16)m_bsga_ymax); - return 0; + if ((s16)m_bsga_xmin < (s16)m_bsga_xmax && (s16)m_bsga_ymin < (s16)m_bsga_ymax) + { + // clip x coordinate + if (x < (s16)m_bsga_xmin) + m_bsga_status |= STATUS_CLIP1_XMIN; + if (x > (s16)m_bsga_xmax) + m_bsga_status |= STATUS_CLIP1_XMAX; + + // clip y coordinate + if (y < (s16)m_bsga_ymin) + m_bsga_status |= STATUS_CLIP1_YMIN; + if (y > (s16)m_bsga_ymax) + m_bsga_status |= STATUS_CLIP1_YMAX; + + if (m_bsga_status & (STATUS_CLIP1_MASK | STATUS_CLIP0_MASK)) + m_bsga_status |= STATUS_CLIP_ANY; + } + else + m_bsga_status |= STATUS_CLIP1_MASK; + + if (((m_bsga_status & STATUS_CLIP0_MASK) >> 4) & (m_bsga_status & STATUS_CLIP1_MASK)) + m_bsga_status |= STATUS_CLIP_BOTH; + + LOG("bsga_clip_status result 0x%04x\n", m_bsga_status); } -mpcba79_device::mpcba79_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : gt_device_base(mconfig, MPCBA79, tag, owner, clock) - , m_screen { { { *this, "ramdac0" }, {}, true }, { { *this, "ramdac1" }, {}, true } } +WRITE32_MEMBER(gt_device_base::ri_xfer_w) { + LOG("ri_xfer_w 0x%08x mem_mask 0x%08x (%s)\n", data, mem_mask, machine().describe_context()); + + // initiate ri line draw + const gt_screen_t >_screen = active_screen(); + + int address = m_ri_initial_address; + int error = m_ri_initial_error; + + for (int i = 0; i < m_ri_stop_count; i++) + { + // FIXME: how do we know which buffer to write? + if (i || m_control & GFX_DRAW_FIRST) + write_vram(gt_screen, address, m_plane_data); + + if (error >= 0) + { + address += m_ri_address_both; + error -= m_ri_error_both; + } + else + { + address += m_ri_address_major; + error += m_ri_error_major; + } + } } -const tiny_rom_entry *mpcba79_device::device_rom_region() const +WRITE32_MEMBER(gt_device_base::bsga_xin1yin1_w) { - return ROM_NAME(mpcba79); + m_bsga_xin1 = (m_bsga_xin1 & ~(mem_mask >> 0)) | ((data & mem_mask) >> 0); + m_bsga_yin1 = (m_bsga_yin1 & ~(mem_mask >> 16)) | ((data & mem_mask) >> 16); + + LOG("bsga_xin1yin1_w data 0x%08x mem_mask 0x%08x xin1 0x%04x yin1 0x%04x\n", data, mem_mask, m_bsga_xin1, m_bsga_yin1); + bsga_clip_status(m_bsga_xin1, m_bsga_yin1); + + m_bsga_xin = m_bsga_xin1; + m_bsga_yin = m_bsga_yin1; + + // FIXME: always xin1, or some kind of counter? + m_bsga_tmp = m_bsga_xin1; } -void mpcba79_device::device_start() +WRITE32_MEMBER(gt_device_base::bsga_xin2yin2_w) { - gt_device_base::device_start(); + m_bsga_xin2 = (m_bsga_xin2 & ~(mem_mask >> 0)) | ((data & mem_mask) >> 0); + m_bsga_yin2 = (m_bsga_yin2 & ~(mem_mask >> 16)) | ((data & mem_mask) >> 16); - m_screen[0].vram.reset(new u8[GT_VRAM_SIZE]); - m_screen[1].vram.reset(new u8[GT_VRAM_SIZE]); + LOG("bsga_xin2yin2_w data 0x%08x mem_mask 0x%08x xin2 0x%04x yin2 0x%04x control 0x%04x\n", data, mem_mask, m_bsga_xin2, m_bsga_yin2, m_control); - save_item(NAME(m_control)); - save_pointer(NAME(m_screen[0].vram.get()), GT_VRAM_SIZE); - save_pointer(NAME(m_screen[1].vram.get()), GT_VRAM_SIZE); + // set busy status + m_control |= GFX_GRPHCS_BUSY; + + // compute clipping status + bsga_clip_status(m_bsga_xin2, m_bsga_yin2); + + // trigger line drawing + if (GT_DIAG) + m_line_timer->adjust(attotime::zero); + else + line(nullptr, 0); } -WRITE16_MEMBER(mpcba79_device::control_w) +WRITE16_MEMBER(gt_device_base::bsga_yin2_w) { - LOG("control_w 0x%04x\n", data); + COMBINE_DATA(&m_bsga_yin2); - m_control = data; + // set busy status + m_control |= GFX_GRPHCS_BUSY; + + // compute clipping status + bsga_clip_status(m_bsga_xin2, m_bsga_yin2); + + // trigger line drawing + if (GT_DIAG) + m_line_timer->adjust(attotime::zero); + else + line(nullptr, 0); } -READ32_MEMBER(mpcba79_device::vram_r) +READ16_MEMBER(gt_device_base::bsga_status_r) { - return ((u32 *)m_screen[(offset >> 19) & 1].vram.get())[offset & 0x7ffff]; + LOG("bsga_status_r 0x%04x (%s)\n", m_bsga_status, machine().describe_context()); + + return m_bsga_status; } -WRITE32_MEMBER(mpcba79_device::vram_w) +WRITE32_MEMBER(gt_device_base::bsga_float_w) { - const gt_screen_t >_screen = m_screen[(offset >> 19) & 1]; + // TODO: when we figure out exactly what this is supposed to do, convert it + // to use softfloat instead. - ((u32 *)gt_screen.vram.get())[offset & 0x7ffff] = (((u32 *)gt_screen.vram.get())[offset & 0x7ffff] & ~mem_mask) | (data & mem_mask); + const u8 exponent = (data & 0x7f800000) >> 23; + const int shift = 150 - exponent; + const u32 mantissa = (data & 0x7fffff) | 0x800000; + const bool sign = data & 0x80000000; + + LOG("bsga_float_w data 0x%08x value %f exponent 0x%02x shift %d mantissa 0x%08x mantissa sign %d\n", data, u2f(data), exponent, shift, mantissa, sign); + + m_bsga_status &= ~STATUS_FLOAT_OFLOW; + + if (shift > 0) + { + if (shift > 23) + m_bsga_xin = 0; + else + m_bsga_xin = mantissa >> shift; + + if (sign) + m_bsga_xin = -m_bsga_xin; + + // TODO: conditional on something? + if (shift <= 8) + m_bsga_status |= STATUS_FLOAT_OFLOW; + } + else + { + m_bsga_xin = 0; + m_bsga_status |= STATUS_FLOAT_OFLOW; + } + + LOG("bsga_float_w result 0x%04x overflow %s\n", m_bsga_xin, m_bsga_status & STATUS_FLOAT_OFLOW ? "set" : "clear"); } -u32 mpcba79_device::screen_update0(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +WRITE16_MEMBER(gt_device_base::blit_width_w) +{ + // writing to blit width starts blit operation + LOG("blit_width_w 0x%04x (%s)\n", data, machine().describe_context()); + + COMBINE_DATA(&m_blit_width); + + // set busy status and schedule blit + m_control |= (GFX_GRPHCS_BUSY | GFX_BLIT_BUSY); + + // trigger blit operation + if (GT_DIAG) + m_blit_timer->adjust(attotime::zero); + else + blit(nullptr, 0); +} + +/* + * Pixel data is stored as 8bpp pixels grouped in 32 bit aligned words, but is + * processed by the BPU 16 bits at a time. This is accomplished by driving the + * BPU through the address range twice, the first time processing the high 4 + * bits of each pixel (packing 4 pixels into 16 bits), and the second time the + * low 4 bits of each pixel. The cycles must be run sequentially to ensure the + * barrel input latch in the bpu contains the right data to make barrel shift + * operations work correctly. + * + * Working theories which are not yet fully tested: + * + * - GFX_DRAW_FIRST indicates we need to load the barrel input latch from the + * first word of source data. + * - GFX_MASK_ENA indicates we need to read masked source data as plane data + * when masked. + */ +#define HI(b0,b1,b2,b3) ((((b0) & 0xf0) << 8) | (((b1) & 0xf0) << 4) | (((b2) & 0xf0) << 0) | (((b3) & 0xf0) >> 4)) +#define LO(b0,b1,b2,b3) ((((b0) & 0x0f) << 12) | (((b1) & 0x0f) << 8) | (((b2) & 0x0f) << 4) | (((b3) & 0x0f) << 0)) + +TIMER_CALLBACK_MEMBER(gt_device_base::blit) +{ + LOGMASKED(LOG_BLIT, "blit bsga_control 0x%08x src 0x%08x dst 0x%08x width 0x%04x (count %d, %s)\n", + m_control, m_blit_src_address, m_blit_dst_address, m_blit_width, + m_blit_width >> 2, (m_control & GFX_BLIT_DIR) ? "decrementing" : "incrementing"); + + const gt_screen_t &screen = active_screen(); + + const int delta = (m_control & GFX_BLIT_DIR) ? -4 : 4; + const int count = m_blit_width >> 2; + + // first cycle + u32 src_address = m_blit_src_address & ~0x3; + u32 dst_address = m_blit_dst_address & ~0x3; + + // load barrel input latch + if (!(m_control & GFX_DRAW_FIRST)) + { + if (!(m_control & GFX_DATA_SEL)) + { + const u8 *src_data = &screen.buffer[src_address]; + const u8 *src_mask = &screen.mask[src_address]; + + if (m_control & GFX_MASK_ENA) + screen.bpu->source_w(HI( + src_mask[0] ? m_plane_data : src_data[0], + src_mask[1] ? m_plane_data : src_data[1], + src_mask[2] ? m_plane_data : src_data[2], + src_mask[3] ? m_plane_data : src_data[3]), false); + else + screen.bpu->source_w(HI(src_data[0], src_data[1], src_data[2], src_data[3]), false); + + src_address += delta; + } + else + screen.bpu->source_w(HI(m_plane_data, m_plane_data, m_plane_data, m_plane_data), false); + } + + // bitblt address loop + for (int word = 0; word < count; word++) + { + // drive left and right mask enables + screen.bpu->left_mask_enable(word == ((m_control & GFX_BLIT_DIR) ? count - 1 : 0)); + screen.bpu->right_mask_enable(word == ((m_control & GFX_BLIT_DIR) ? 0 : count - 1)); + + // load bpu source + if (!(m_control & GFX_DATA_SEL)) + { + const u8 *src_data = &screen.buffer[src_address]; + const u8 *src_mask = &screen.mask[src_address]; + + LOGMASKED(LOG_BLIT, "blit src data %3d address 0x%08x %02x %02x %02x %02x\n", + word, src_address, src_data[0], src_data[1], src_data[2], src_data[3]); + + if (m_control & GFX_MASK_ENA) + { + LOGMASKED(LOG_BLIT, "blit src mask %3d address 0x%08x %02x %02x %02x %02x\n", + word, src_address, src_mask[0], src_mask[1], src_mask[2], src_mask[3]); + + screen.bpu->source_w(HI( + src_mask[0] ? m_plane_data : src_data[0], + src_mask[1] ? m_plane_data : src_data[1], + src_mask[2] ? m_plane_data : src_data[2], + src_mask[3] ? m_plane_data : src_data[3])); + } + else + screen.bpu->source_w(HI(src_data[0], src_data[1], src_data[2], src_data[3])); + + src_address += delta; + } + else + screen.bpu->source_w(HI(m_plane_data, m_plane_data, m_plane_data, m_plane_data)); + + // load bpu destination + { + const u8 *dst_data = (m_control & GFX_MASK_SEL) ? &screen.mask[dst_address] : &screen.buffer[dst_address]; + + LOGMASKED(LOG_BLIT, "blit dst data %3d address 0x%08x %02x %02x %02x %02x\n", + word, dst_address, dst_data[0], dst_data[1], dst_data[2], dst_data[3]); + + screen.bpu->destination_w(HI(dst_data[0], dst_data[1], dst_data[2], dst_data[3])); + } + + // fetch bpu output + const u16 output = screen.bpu->output_r(); + + if (!(m_control & GFX_MASK_SEL)) + { + // write output to pixel ram + u8 *out_data = &screen.buffer[dst_address]; + const u8 *out_mask = &screen.mask[dst_address]; + + if (!(m_control & GFX_MASK_ENA) || out_mask[0] == 0) + out_data[0] = (out_data[0] & ~(0xf0 | m_plane_enable)) | (((output & 0xf000) >> 8) & m_plane_enable); + if (!(m_control & GFX_MASK_ENA) || out_mask[1] == 0) + out_data[1] = (out_data[1] & ~(0xf0 | m_plane_enable)) | (((output & 0x0f00) >> 4) & m_plane_enable); + if (!(m_control & GFX_MASK_ENA) || out_mask[2] == 0) + out_data[2] = (out_data[2] & ~(0xf0 | m_plane_enable)) | (((output & 0x00f0) >> 0) & m_plane_enable); + if (!(m_control & GFX_MASK_ENA) || out_mask[3] == 0) + out_data[3] = (out_data[3] & ~(0xf0 | m_plane_enable)) | (((output & 0x000f) << 4) & m_plane_enable); + } + else + { + // write output to mask ram + u8 *out_data = &screen.mask[dst_address]; + + out_data[0] = output & 0x8000 ? 0x80 : 0x00; + out_data[1] = output & 0x0800 ? 0x80 : 0x00; + out_data[2] = output & 0x0080 ? 0x80 : 0x00; + out_data[3] = output & 0x0008 ? 0x80 : 0x00; + + LOGMASKED(LOG_BLIT, "blit out mask %3d address 0x%08x %02x %02x %02x %02x\n", + word, dst_address, out_data[0], out_data[1], out_data[2], out_data[3]); + } + + dst_address += delta; + } + + // second cycle + src_address = m_blit_src_address & ~0x3; + dst_address = m_blit_dst_address & ~0x3; + + // load barrel input latch + if (!(m_control & GFX_DRAW_FIRST)) + { + if (!(m_control & GFX_DATA_SEL)) + { + const u8 *src_data = &screen.buffer[src_address]; + const u8 *src_mask = &screen.mask[src_address]; + + if (m_control & GFX_MASK_ENA) + screen.bpu->source_w(LO( + src_mask[0] ? m_plane_data : src_data[0], + src_mask[1] ? m_plane_data : src_data[1], + src_mask[2] ? m_plane_data : src_data[2], + src_mask[3] ? m_plane_data : src_data[3]), false); + else + screen.bpu->source_w(LO(src_data[0], src_data[1], src_data[2], src_data[3]), false); + } + else + screen.bpu->source_w(LO(m_plane_data, m_plane_data, m_plane_data, m_plane_data), false); + + src_address += delta; + } + + // bitblt address loop + for (int word = 0; word < count; word++) + { + // drive left and right mask enables + screen.bpu->left_mask_enable(word == ((m_control & GFX_BLIT_DIR) ? count - 1 : 0)); + screen.bpu->right_mask_enable(word == ((m_control & GFX_BLIT_DIR) ? 0 : count - 1)); + + // load bpu source + if (!(m_control & GFX_DATA_SEL)) + { + const u8 *src_data = &screen.buffer[src_address]; + const u8 *src_mask = &screen.mask[src_address]; + + if (m_control & GFX_MASK_ENA) + { + screen.bpu->source_w(LO( + src_mask[0] ? m_plane_data : src_data[0], + src_mask[1] ? m_plane_data : src_data[1], + src_mask[2] ? m_plane_data : src_data[2], + src_mask[3] ? m_plane_data : src_data[3])); + } + else + screen.bpu->source_w(LO(src_data[0], src_data[1], src_data[2], src_data[3])); + + src_address += delta; + } + else + screen.bpu->source_w(LO(m_plane_data, m_plane_data, m_plane_data, m_plane_data)); + + // load bpu destination + { + const u8 *dst_data = (m_control & GFX_MASK_SEL) ? &screen.mask[dst_address] : &screen.buffer[dst_address]; + + screen.bpu->destination_w(LO(dst_data[0], dst_data[1], dst_data[2], dst_data[3])); + } + + // fetch bpu output + const u16 output = screen.bpu->output_r(); + + if (!(m_control & GFX_MASK_SEL)) + { + // write output to pixel ram + u8 *out_data = &screen.buffer[dst_address]; + const u8 *out_mask = &screen.mask[dst_address]; + + if (!(m_control & GFX_MASK_ENA) || out_mask[0] == 0) + out_data[0] = (out_data[0] & ~(0x0f | m_plane_enable)) | (((output & 0xf000) >> 12) & m_plane_enable); + if (!(m_control & GFX_MASK_ENA) || out_mask[1] == 0) + out_data[1] = (out_data[1] & ~(0x0f | m_plane_enable)) | (((output & 0x0f00) >> 8) & m_plane_enable); + if (!(m_control & GFX_MASK_ENA) || out_mask[2] == 0) + out_data[2] = (out_data[2] & ~(0x0f | m_plane_enable)) | (((output & 0x00f0) >> 4) & m_plane_enable); + if (!(m_control & GFX_MASK_ENA) || out_mask[3] == 0) + out_data[3] = (out_data[3] & ~(0x0f | m_plane_enable)) | (((output & 0x000f) >> 0) & m_plane_enable); + + LOGMASKED(LOG_BLIT, "blit dst mask %3d address 0x%08x %02x %02x %02x %02x\n", + word, dst_address, out_mask[0], out_mask[1], out_mask[2], out_mask[3]); + LOGMASKED(LOG_BLIT, "blit out data %3d address 0x%08x %02x %02x %02x %02x\n", + word, dst_address, out_data[0], out_data[1], out_data[2], out_data[3]); + } + + dst_address += delta; + } + + // clear mask enable lines + screen.bpu->left_mask_enable(CLEAR_LINE); + screen.bpu->right_mask_enable(CLEAR_LINE); + + // complete with delay + if (GT_DIAG) + m_done_timer->adjust(attotime::from_msec(10), GFX_GRPHCS_BUSY | GFX_BLIT_BUSY); + else + done(nullptr, GFX_GRPHCS_BUSY | GFX_BLIT_BUSY); +} + +TIMER_CALLBACK_MEMBER(gt_device_base::line) +{ + // draw a clipped line + + // FIXME: fix clipping to use >= min and < max (don't subtract 1) + kuzmin_clip(m_bsga_xin1, m_bsga_yin1, m_bsga_xin2, m_bsga_yin2, m_bsga_xmin, m_bsga_ymin, m_bsga_xmax - 1, m_bsga_ymax - 1); + + // point #2 becomes point #1 + m_bsga_xin1 = m_bsga_xin2; + m_bsga_yin1 = m_bsga_yin2; + + m_bsga_xin = m_bsga_xin1; + m_bsga_yin = m_bsga_yin1; + + // complete with delay + if (GT_DIAG) + m_done_timer->adjust(attotime::from_nsec(100), GFX_GRPHCS_BUSY); + else + done(nullptr, GFX_GRPHCS_BUSY); +} + +TIMER_CALLBACK_MEMBER(gt_device_base::done) +{ + m_control &= ~(u32)param; +} + +WRITE8_MEMBER(gt_device_base::plane_enable_w) +{ + LOG("plane enable 0x%02x\n", data); + + if (!(m_control & GFX_GRPHCS_BUSY)) + m_plane_enable = data; +} + +WRITE8_MEMBER(gt_device_base::plane_data_w) +{ + LOG("plane data 0x%02x\n", data); + + if (!(m_control & GFX_GRPHCS_BUSY)) + m_plane_data = data; +} + +#define U32(b0,b1,b2,b3) (((b0) << 24) | ((b1) << 16) | ((b2) << 8) | ((b3) << 0)) +u32 gt_device_base::buffer_read(const gt_screen_t &screen, const offs_t offset) const +{ + const u32 src_address = offset << 2; + + const u8 *src = &screen.buffer[src_address]; + const u8 *mask = &screen.mask[src_address]; + + if (m_control & GFX_MASK_SEL) + return U32(mask[3], mask[2], mask[1], mask[0]); + else if (m_control & GFX_DATA_SEL) + return U32(m_plane_data, m_plane_data, m_plane_data, m_plane_data); + else if (m_control & GFX_MASK_ENA) + return U32( + mask[3] ? m_plane_data : src[3], + mask[2] ? m_plane_data : src[2], + mask[1] ? m_plane_data : src[1], + mask[0] ? m_plane_data : src[0]); + else + return U32(src[3], src[2], src[1], src[0]); +} + +void gt_device_base::buffer_write(const gt_screen_t &screen, const offs_t offset, const u32 data, const u32 mask) +{ + const u32 dst_address = offset << 2; + + u8 out_data[] = { + u8(((m_control & GFX_DATA_SEL) ? m_plane_data : (data >> 0)) & m_plane_enable), + u8(((m_control & GFX_DATA_SEL) ? m_plane_data : (data >> 8)) & m_plane_enable), + u8(((m_control & GFX_DATA_SEL) ? m_plane_data : (data >> 16)) & m_plane_enable), + u8(((m_control & GFX_DATA_SEL) ? m_plane_data : (data >> 24)) & m_plane_enable) + }; + u32 out_mask = mask; + + if (m_control & GFX_RMW_MD) + { + // read/modify/write mode, use bpu to compute result + const u8 *dst_data = &screen.buffer[dst_address]; + + screen.bpu->source_w(HI(out_data[0], out_data[1], out_data[2], out_data[3])); + screen.bpu->destination_w(HI(dst_data[0], dst_data[1], dst_data[2], dst_data[3])); + + const u16 out_hi = screen.bpu->output_r(); + + screen.bpu->source_w(LO(out_data[0], out_data[1], out_data[2], out_data[3])); + screen.bpu->destination_w(LO(dst_data[0], dst_data[1], dst_data[2], dst_data[3])); + + const u16 out_lo = screen.bpu->output_r(); + + out_data[0] = ((out_hi & 0x000f) << 28 | (out_lo & 0x000f) << 24); + out_data[1] = ((out_hi & 0x00f0) << 16 | (out_lo & 0x00f0) << 12); + out_data[2] = ((out_hi & 0x0f00) << 4 | (out_lo & 0x0f00) >> 0); + out_data[3] = ((out_hi & 0xf000) >> 8 | (out_lo & 0xf000) >> 12); + } + else if (m_control & GFX_DATA_SEL) + { + // bottom four bits control writing plane data to each of 4 bytes in the word + out_mask = (data & 0x1) ? (out_mask | 0x000000ff) : (out_mask & ~0x000000ff); + out_mask = (data & 0x2) ? (out_mask | 0x0000ff00) : (out_mask & ~0x0000ff00); + out_mask = (data & 0x4) ? (out_mask | 0x00ff0000) : (out_mask & ~0x00ff0000); + out_mask = (data & 0x8) ? (out_mask | 0xff000000) : (out_mask & ~0xff000000); + } + + if (m_control & GFX_MASK_SEL) + { + // write to mask + u8 *dst_data = &screen.mask[dst_address]; + + if (out_mask & 0x000000ff) dst_data[0] = out_data[0] & 0x80; + if (out_mask & 0x0000ff00) dst_data[1] = out_data[1] & 0x80; + if (out_mask & 0x00ff0000) dst_data[2] = out_data[2] & 0x80; + if (out_mask & 0xff000000) dst_data[3] = out_data[3] & 0x80; + } + else if (m_control & GFX_MASK_ENA) + { + // masked write to buffer + u8 *dst_mask = &screen.mask[dst_address]; + u8 *dst_data = &screen.buffer[dst_address]; + + if ((out_mask & 0x000000ff) && (dst_mask[0] == 0)) dst_data[0] = out_data[0]; + if ((out_mask & 0x0000ff00) && (dst_mask[1] == 0)) dst_data[1] = out_data[1]; + if ((out_mask & 0x00ff0000) && (dst_mask[2] == 0)) dst_data[2] = out_data[2]; + if ((out_mask & 0xff000000) && (dst_mask[3] == 0)) dst_data[3] = out_data[3]; + } + else + { + // unmasked write to buffer + u8 *dst_data = &screen.buffer[dst_address]; + + if (out_mask & 0x000000ff) dst_data[0] = out_data[0]; + if (out_mask & 0x0000ff00) dst_data[1] = out_data[1]; + if (out_mask & 0x00ff0000) dst_data[2] = out_data[2]; + if (out_mask & 0xff000000) dst_data[3] = out_data[3]; + } +} + +u32 single_gt_device_base::screen_update0(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { const gt_screen_t >_screen = m_screen[0]; - gt_screen.ramdac->screen_update(screen, bitmap, cliprect, >_screen.vram[gt_screen.primary ? 0x000000 : GT_BUFFER_SIZE]); + gt_screen.ramdac->screen_update(screen, bitmap, cliprect, m_control & GFX_SCREEN0_DISP_BUF1 ? >_screen.buffer[GT_BUFFER_SIZE] : >_screen.buffer[0]); return 0; } -u32 mpcba79_device::screen_update1(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +u32 dual_gt_device_base::screen_update0(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + const gt_screen_t >_screen = m_screen[0]; + + gt_screen.ramdac->screen_update(screen, bitmap, cliprect, m_control & GFX_SCREEN0_DISP_BUF1 ? >_screen.buffer[GT_BUFFER_SIZE] : >_screen.buffer[0]); + + return 0; +} + +u32 dual_gt_device_base::screen_update1(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { const gt_screen_t >_screen = m_screen[1]; - gt_screen.ramdac->screen_update(screen, bitmap, cliprect, >_screen.vram[gt_screen.primary ? 0x000000 : GT_BUFFER_SIZE]); + gt_screen.ramdac->screen_update(screen, bitmap, cliprect, m_control & GFX_SCREEN1_DISP_BUF1 ? >_screen.buffer[GT_BUFFER_SIZE] : >_screen.buffer[0]); return 0; } + +/* + * This function implements the technique described in "Bresenham's Line + * Generation Algorithm with Built-in Clipping", by Yevgeny P. Kuzmin. It has + * been adapted to take advantage of the Cohen-Sutherland clipping outcodes + * computed in the BSGA to reject trivial cases. + */ +bool gt_device_base::kuzmin_clip(s16 x1, s16 y1, s16 x2, s16 y2, s16 clip_xmin, s16 clip_ymin, s16 clip_xmax, s16 clip_ymax) +{ + LOG("kuzmin_clip line (%d,%d)-(%d,%d) clip (%d,%d,%d,%d) plane_data 0x%02x plane_enable 0x%02x\n", + x1, y1, x2, y2, clip_xmin, clip_ymin, clip_xmax, clip_ymax, + m_plane_data, m_plane_enable); + + // all trivial cases are handled by STATUS_CLIP_BOTH + if (m_bsga_status & STATUS_CLIP_BOTH) + { + LOG("kuzmin_clip Cohen-Sutherland reject\n"); + + return false; + } + + // horizontal line case + if (y1 == y2) + { + LOG("kuzmin_clip horizontal line case\n"); + if (x1 <= x2) + { + // left to right + x1 = std::max(x1, clip_xmin); + x2 = std::min(x2, clip_xmax); + + bresenham_line(x1, y1, 1, 0, x2 - x1 + 1, 0, 0, 0, true); + } + else + { + // right to left + x2 = std::max(x2, clip_xmin); + x1 = std::min(x1, clip_xmax); + + bresenham_line(x1, y1, -1, 0, x1 - x2 + 1, 0, 0, 0, true); + } + + return true; + } + + // vertical line case + if (x1 == x2) + { + LOG("kuzmin_clip vertical line case\n"); + if (y1 <= y2) + { + // top to bottom + y1 = std::max(y1, clip_ymin); + y2 = std::min(y2, clip_ymax); + + bresenham_line(y1, x1, 1, 0, y2 - y1 + 1, 0, 0, 0, false); + } + else + { + // bottom to top + y2 = std::max(y2, clip_ymin); + y1 = std::min(y1, clip_ymax); + + bresenham_line(y1, x1, -1, 0, y1 - y2 + 1, 0, 0, 0, false); + } + + return true; + } + + // TODO: try to eliminate sign inversion + int sign_x = 1; + if (x1 > x2) + { + // invert sign, invert again before output + x1 = -x1; + x2 = -x2; + clip_xmin = -clip_xmax; + clip_xmax = -clip_xmin; + + sign_x = -1; + } + + int sign_y = 1; + if (y1 > y2) + { + // invert sign, invert again before output + y1 = -y1; + y2 = -y2; + clip_ymin = -clip_ymax; + clip_ymax = -clip_ymin; + + sign_y = -1; + } + + const s16 delta_x = x2 - x1; + const s16 delta_y = y2 - y1; + + int delta_x_step = 2 * delta_x; + int delta_y_step = 2 * delta_y; + + // output coordinates + s16 x_pos = x1; + s16 x_pos_end = x2; + s16 y_pos = y1; + s16 y_pos_end = y2; + + if (delta_x >= delta_y) + { + LOG("kuzmin_clip mostly horizontal line case\n"); + + // mostly horizontal case + s16 error = delta_y_step - delta_x; + + // skip clipping if not required + if (m_bsga_status & STATUS_CLIP_ANY) + { + bool set_exit = false; + + // line starts above clip boundary + if (y1 < clip_ymin) + { + // compute x offset at intersection of top clip boundary + div_t div = std::div(delta_x_step * (clip_ymin - y1) - delta_x, delta_y_step); + + x_pos += div.quot; + + // line does not intersect clip boundary + if (x_pos > clip_xmax) + { + LOG("kuzmin_clip line wholly left or right of clipping boundary\n"); + + return 0; + } + + // line intersects top clip boundary + if (x_pos >= clip_xmin) + { + y_pos = clip_ymin; + error -= div.rem + delta_x; + + if (div.rem > 0) + { + x_pos += 1; + error += delta_y_step; + } + + set_exit = true; + } + } + + // line starts left of clip boundary + if (!set_exit && (x1 < clip_xmin)) + { + // compute y offset at intersection of left clip boundary + div_t div = std::div(delta_y_step * (clip_xmin - x1), delta_x_step); + + y_pos += div.quot; + + // line does not intersect clip boundary + if ((y_pos > clip_ymax) || ((y_pos == clip_ymax && div.rem >= delta_x))) + { + LOG("kuzmin_clip line wholly above or below clipping boundary\n"); + + return 0; + } + + // line intersects left clip boundary + if (y_pos >= clip_ymin) + { + x_pos = clip_xmin; + error += div.rem; + + if (div.rem >= delta_x) + { + y_pos += 1; + error -= delta_x_step; + } + } + } + + // clip the end point to the bottom clip boundary + if (y2 > clip_ymax) + { + // compute x offset at intersection of bottom clip boundary + div_t div = std::div(delta_x_step * (clip_ymax - y1) + delta_x, delta_y_step); + + x_pos_end = x1 + div.quot; + + if (div.rem == 0) + x_pos_end -= 1; + } + + x_pos_end = std::min(x_pos_end, clip_xmax); + + } + + // revert sign inversion + if (sign_y == -1) + y_pos = -y_pos; + + if (sign_x == -1) + { + x_pos = -x_pos; + x_pos_end = -x_pos_end; + } + + delta_x_step -= delta_y_step; + + LOG("kuzmin_clip clipped line (%d,%d)-(%d,%d)\n", x_pos, y_pos, x_pos_end, y_pos_end); + + // draw the line + bresenham_line(x_pos, y_pos, sign_x, sign_y, abs(x_pos_end - x_pos) + 1, error, delta_x_step, delta_y_step, true); + } + else + { + LOG("kuzmin_clip mostly vertical line case\n"); + + // mostly vertical case (same as previous with swapped x/y) + s16 error = delta_x_step - delta_y; + + // skip clipping if not required + if (m_bsga_status & STATUS_CLIP_ANY) + { + bool set_exit = false; + + // line starts left of the clip window. + if (x1 < clip_xmin) + { + // compute y offset at intersection of left clip boundary + div_t div = std::div(2 * delta_y * (clip_xmin - x1) - delta_y, delta_x_step); + + y_pos += div.quot; + + // line does not intersect clip boundary + if (y_pos > clip_ymax) + { + LOG("kuzmin_clip line wholly left or right of clipping boundary\n"); + + return 0; + } + + // line intersects left clip boundary + if (y_pos >= clip_ymin) + { + x_pos = clip_xmin; + error -= div.rem + delta_y; + + if (div.rem > 0) + { + y_pos += 1; + error += delta_x_step; + } + + set_exit = true; + } + } + + // line starts above clip boundary + if (!set_exit && (y1 < clip_ymin)) + { + // compute x offset at intersection of top clip boundary + div_t div = std::div(delta_x_step * (clip_ymin - y1), delta_y_step); + + x_pos += div.quot; + + // line does not intersect clip boundary + if ((x_pos > clip_xmax) || ((x_pos == clip_xmax && div.rem >= delta_y))) + { + LOG("kuzmin_clip line wholly above or below clipping boundary\n"); + + return 0; + } + + // line intersects top clip boundary + y_pos = clip_ymin; + error += div.rem; + + if (div.rem >= delta_y) + { + x_pos += 1; + error -= delta_y_step; + } + } + + // clip the end point to the right clip boundary + if (x2 > clip_xmax) + { + // compute y offset at intersection of right clip boundary + div_t div = std::div(delta_y_step * (clip_xmax - x1) + delta_y, delta_x_step); + + y_pos_end = y1 + div.quot; + + if (div.rem == 0) + y_pos_end -= 1; + } + + y_pos_end = std::min(y_pos_end, clip_ymax); + } + + // revert sign inversion + if (sign_x == -1) + x_pos = -x_pos; + + if (sign_y == -1) + { + y_pos = -y_pos; + y_pos_end = -y_pos_end; + } + + delta_y_step -= delta_x_step; + + LOG("kuzmin_clip clipped line (%d,%d)-(%d,%d)\n", x_pos, y_pos, x_pos_end, y_pos_end); + + // draw the line + bresenham_line(y_pos, x_pos, sign_y, sign_x, abs(y_pos_end - y_pos) + 1, error, delta_y_step, delta_x_step, false); + } + + return true; +} + +void gt_device_base::bresenham_line(s16 major, s16 minor, s16 major_step, s16 minor_step, int steps, s16 error, s16 error_major, s16 error_minor, bool shallow) +{ + const gt_screen_t >_screen = active_screen(); + + LOG("bresenham_line begin %d,%d steps %d\n", shallow ? major : minor, shallow ? minor : major, steps); + + for (int i = 0; i < steps; i++) + { + // FIXME: which buffer? + if (i || m_control & GFX_DRAW_FIRST) + write_vram(gt_screen, shallow ? minor * GT_XPIXELS + major : major * GT_XPIXELS + minor, m_plane_data); + + if (error >= 0) + { + minor += minor_step; + error -= error_major; + } + else + error += error_minor; + + major += major_step; + } + + LOG("bresenham_line end %d,%d\n", shallow ? major : minor, shallow ? minor : major); +} + +// TODO: eliminate this function completely when we fully understand the buffer write pipeline. +void gt_device_base::write_vram(const gt_screen_t &screen, const offs_t offset, const u8 data) +{ + if (false) + { + const int shift_amount = ((offset & 0x3) << 3); + + buffer_write(screen, offset >> 2, data << shift_amount, 0xff << shift_amount); + } + else + screen.buffer[offset] = data; +} diff --git a/src/devices/bus/interpro/sr/gt.h b/src/devices/bus/interpro/sr/gt.h index c7b0aa515eb..6d9098c7043 100644 --- a/src/devices/bus/interpro/sr/gt.h +++ b/src/devices/bus/interpro/sr/gt.h @@ -7,96 +7,354 @@ #include "sr.h" #include "video/bt459.h" +#include "video/dp8510.h" class gt_device_base : public sr_card_device_base { protected: gt_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + virtual void map(address_map &map) override; + public: static const u32 GT_PIXCLOCK = 80'000'000; // just a guess static const int GT_XPIXELS = 1184; static const int GT_YPIXELS = 884; static const int GT_BUFFER_SIZE = 0x100000; // 1 megabyte - static const int GT_VRAM_SIZE = 0x200000; // 1 megabyte double buffered + static const u32 GT_BUFFER_MASK = (GT_BUFFER_SIZE - 1); + + // FIXME: enable delays to pass diagnostic tests + static const bool GT_DIAG = false; enum control_mask { - CONTROL_SCREEN = 0x1000, // possibly selects screen 0 or 1? - CONTROL_BUSY = 0x8000 + GFX_VERT_BLNK = 0x00000001, + GFX_HILITE_SEL = 0x00000004, + GFX_SOFT_BLNK = 0x00000008, + GFX_DRAW_FIRST = 0x00000010, // draw first point + GFX_BLIT_DIR = 0x00000020, // bitblt addresses decrement + GFX_RMW_MD = 0x00000040, // enable rmw mode + GFX_DATA_SEL = 0x00000080, // enable plane data as source + GFX_ANTI_MD = 0x00000100, // enable antialias mode + GFX_ADD_MD = 0x00000200, // enable antialias additive mode + GFX_MASK_ENA = 0x00000400, // enable pixel write mask + GFX_MASK_SEL = 0x00000800, // select mask ram + GFX_SCR1_SEL = 0x00001000, // select screen 1 + GFX_BSGA_RST = 0x00002000, + GFX_BLIT_BUSY = 0x00004000, + GFX_GRPHCS_BUSY = 0x00008000, + GFX_VFIFO_EMPTY = 0x00010000, + GFX_SCREEN0_DISP_BUF1 = 0x00020000, // select buffer 1 (screen 0) + GFX_SCREEN1_DISP_BUF1 = 0x00040000, // select buffer 1 (screen 1) + GFX_STEREO_EN = 0x00080000, + GFX_INTERLACE = 0x00100000, + GFX_STEREO_POLRITY_NORMAL = 0x00200000, + GFX_STEREO_GLASSES_EN = 0x00400000, + GFX_FIELD_1 = 0x00800000, + //GFX_MASK_READ = 0x01000000, // enable pixel read mask? + GFX_MONSENSE_MASK = 0x0e000000, + GFX_MONSENSE_60HZ = 0x0e000000 + }; + + DECLARE_READ32_MEMBER(control_r) { return m_control; }; + DECLARE_WRITE32_MEMBER(control_w); + + DECLARE_READ8_MEMBER(contrast_dac_r) { return m_contrast_dac; } + DECLARE_WRITE8_MEMBER(contrast_dac_w) { m_contrast_dac = data; } + + DECLARE_READ32_MEMBER(blit_src_address_r) { return m_blit_src_address; } + DECLARE_WRITE32_MEMBER(blit_src_address_w) { COMBINE_DATA(&m_blit_src_address); } + DECLARE_READ32_MEMBER(blit_dst_address_r) { return m_blit_dst_address; } + DECLARE_WRITE32_MEMBER(blit_dst_address_w) { COMBINE_DATA(&m_blit_dst_address); } + DECLARE_READ16_MEMBER(blit_width_r) { return m_blit_width; } + DECLARE_WRITE16_MEMBER(blit_width_w); + + enum blit_control_mask + { + BLIT0_CONTROL_FS = 0x000000f0, + BLIT1_CONTROL_FS = 0x0000000f, + BLIT0_CONTROL_SN = 0x0000f000, + BLIT1_CONTROL_SN = 0x00000f00, + BLIT0_CONTROL_LM = 0x00f00000, + BLIT1_CONTROL_LM = 0x000f0000, + BLIT0_CONTROL_RM = 0xf0000000, + BLIT1_CONTROL_RM = 0x0f000000 + }; + virtual DECLARE_WRITE32_MEMBER(blit_control_w) = 0; + + DECLARE_READ8_MEMBER(plane_enable_r) { return m_plane_enable; } + DECLARE_WRITE8_MEMBER(plane_enable_w); + DECLARE_READ8_MEMBER(plane_data_r) { return m_plane_data; } + DECLARE_WRITE8_MEMBER(plane_data_w); + + DECLARE_READ16_MEMBER(bsga_width_r) { return m_bsga_width; } + DECLARE_WRITE16_MEMBER(bsga_width_w) { COMBINE_DATA(&m_bsga_width); } + DECLARE_READ16_MEMBER(bsga_tmp_r) { return m_bsga_tmp; } + DECLARE_WRITE16_MEMBER(bsga_tmp_w) { COMBINE_DATA(&m_bsga_tmp); } + + DECLARE_READ16_MEMBER(bsga_xmin_r) { return m_bsga_xmin; } + DECLARE_WRITE16_MEMBER(bsga_xmin_w) { COMBINE_DATA(&m_bsga_xmin); } + DECLARE_READ16_MEMBER(bsga_ymin_r) { return m_bsga_ymin; } + DECLARE_WRITE16_MEMBER(bsga_ymin_w) { COMBINE_DATA(&m_bsga_ymin); } + + DECLARE_READ16_MEMBER(bsga_acc0_r) { return (m_bsga_width - m_bsga_xin1); } + DECLARE_READ16_MEMBER(bsga_acc1_r) { return -(m_bsga_width - m_bsga_xin1); } + + DECLARE_READ16_MEMBER(bsga_xmax_r) { return m_bsga_xmax; } + DECLARE_WRITE16_MEMBER(bsga_xmax_w) { COMBINE_DATA(&m_bsga_xmax); } + DECLARE_READ16_MEMBER(bsga_ymax_r) { return m_bsga_ymax; } + DECLARE_WRITE16_MEMBER(bsga_ymax_w) { COMBINE_DATA(&m_bsga_ymax); } + + DECLARE_READ16_MEMBER(bsga_src0_r) { return m_bsga_xin1; } + DECLARE_READ16_MEMBER(bsga_src1_r) { return m_bsga_xin1; } + + DECLARE_WRITE16_MEMBER(bsga_xin1_w) { COMBINE_DATA(&m_bsga_xin1); m_bsga_xin = m_bsga_xin1; m_bsga_tmp = m_bsga_xin1; } + DECLARE_WRITE16_MEMBER(bsga_yin1_w) { COMBINE_DATA(&m_bsga_yin1); m_bsga_yin = m_bsga_yin1; } + DECLARE_WRITE32_MEMBER(bsga_xin1yin1_w); + + enum bsga_status_mask + { + STATUS_CLIP0_YMAX = 0x1000, // y1 > max y + STATUS_CLIP0_YMIN = 0x0800, // y1 < min y + STATUS_CLIP0_XMAX = 0x0400, // x1 > max x + STATUS_CLIP0_XMIN = 0x0200, // x1 < min x + STATUS_CLIP1_YMAX = 0x0100, // y2 > max y + STATUS_CLIP1_YMIN = 0x0080, // y2 < min y + STATUS_CLIP1_XMAX = 0x0040, // x2 > max x + STATUS_CLIP1_XMIN = 0x0020, // x2 < min x + + STATUS_FLOAT_OFLOW = 0x0010, + + STATUS_CLIP_BOTH = 0x0008, // set if both inputs fall outside clipping region + STATUS_CLIP_ANY = 0x0004, // set if any input falls outside clipping region + + STATUS_CLIP0_MASK = 0x1e00, // x1,y1 clip result + STATUS_CLIP1_MASK = 0x01e0, // x2,y2 clip result + STATUS_CLIP_MASK = 0x1fe0 // both clip results }; + DECLARE_READ16_MEMBER(bsga_status_r); + + DECLARE_WRITE32_MEMBER(bsga_xin2yin2_w); + + DECLARE_WRITE16_MEMBER(bsga_xin2_w) { COMBINE_DATA(&m_bsga_xin2); m_bsga_xin = m_bsga_xin2; } + DECLARE_WRITE16_MEMBER(bsga_yin2_w); + + // FIXME: perhaps tmp is a counter of xin/yin and can be used to return correct value? + DECLARE_READ16_MEMBER(bsga_xin_r) { return m_bsga_xin; } + DECLARE_READ16_MEMBER(bsga_yin_r) { return m_bsga_yin; } + + DECLARE_READ32_MEMBER(ri_initial_distance_r) { return m_ri_initial_distance; } + DECLARE_WRITE32_MEMBER(ri_initial_distance_w) { COMBINE_DATA(&m_ri_initial_distance); } + DECLARE_READ32_MEMBER(ri_distance_both_r) { return m_ri_distance_both; } + DECLARE_WRITE32_MEMBER(ri_distance_both_w) { COMBINE_DATA(&m_ri_distance_both); } + DECLARE_READ32_MEMBER(ri_distance_major_r) { return m_ri_distance_major; } + DECLARE_WRITE32_MEMBER(ri_distance_major_w) { COMBINE_DATA(&m_ri_distance_major); } + DECLARE_READ32_MEMBER(ri_initial_address_r) { return m_ri_initial_address; } + DECLARE_WRITE32_MEMBER(ri_initial_address_w) { COMBINE_DATA(&m_ri_initial_address); } + DECLARE_READ32_MEMBER(ri_address_both_r) { return m_ri_address_both; } + DECLARE_WRITE32_MEMBER(ri_address_both_w) { COMBINE_DATA(&m_ri_address_both); } + DECLARE_READ32_MEMBER(ri_address_major_r) { return m_ri_address_major; } + DECLARE_WRITE32_MEMBER(ri_address_major_w) { COMBINE_DATA(&m_ri_address_major); } + DECLARE_READ32_MEMBER(ri_initial_error_r) { return m_ri_initial_error; } + DECLARE_WRITE32_MEMBER(ri_initial_error_w) { COMBINE_DATA(&m_ri_initial_error); } + DECLARE_READ32_MEMBER(ri_error_both_r) { return m_ri_error_both; } + DECLARE_WRITE32_MEMBER(ri_error_both_w) { COMBINE_DATA(&m_ri_error_both); } + DECLARE_READ32_MEMBER(ri_error_major_r) { return m_ri_error_major; } + DECLARE_WRITE32_MEMBER(ri_error_major_w) { COMBINE_DATA(&m_ri_error_major); } + + DECLARE_READ32_MEMBER(ri_stop_count_r) { return m_ri_stop_count; } + DECLARE_WRITE32_MEMBER(ri_stop_count_w) { COMBINE_DATA(&m_ri_stop_count); } + DECLARE_READ32_MEMBER(ri_control_r) { return m_ri_control; } + DECLARE_WRITE32_MEMBER(ri_control_w) { COMBINE_DATA(&m_ri_control); } + DECLARE_READ32_MEMBER(ri_xfer_r) { return m_ri_xfer; } + DECLARE_WRITE32_MEMBER(ri_xfer_w); + + DECLARE_WRITE32_MEMBER(bsga_float_w); protected: + virtual void device_start() override; + typedef struct { required_device ramdac; - std::unique_ptr vram; + required_device bpu; + + std::unique_ptr buffer; + std::unique_ptr mask; + } + gt_screen_t; + + virtual const int get_screen_count() const = 0; + virtual gt_screen_t &get_screen(const int number) = 0; + virtual const gt_screen_t &active_screen() const = 0; + + u32 buffer_read(const gt_screen_t &screen, const offs_t offset) const; + void buffer_write(const gt_screen_t &screen, const offs_t offset, const u32 data, const u32 mask); + + TIMER_CALLBACK_MEMBER(vblank); + TIMER_CALLBACK_MEMBER(blit); + TIMER_CALLBACK_MEMBER(line); + TIMER_CALLBACK_MEMBER(done); + + void bsga_clip_status(s16 xin, s16 yin); + + bool kuzmin_clip(s16 sx1, s16 sy1, s16 sx2, s16 sy2, s16 wx1, s16 wy1, s16 wx2, s16 wy2); + void bresenham_line(s16 major, s16 minor, s16 major_step, s16 minor_step, int steps, s16 error, s16 error_major, s16 error_minor, bool shallow); + void write_vram(const gt_screen_t &screen, const offs_t offset, const u8 data); + + u8 m_contrast_dac; + + u32 m_control; + + u32 m_blit_src_address; + u32 m_blit_dst_address; + u16 m_blit_width; - bool primary; - } gt_screen_t; + u8 m_plane_enable; + u8 m_plane_data; - virtual DECLARE_READ16_MEMBER(control_r) = 0; - virtual DECLARE_WRITE16_MEMBER(control_w) = 0; - virtual DECLARE_READ32_MEMBER(vram_r) = 0; - virtual DECLARE_WRITE32_MEMBER(vram_w) = 0; + u16 m_bsga_width; + u16 m_bsga_tmp; + + u16 m_bsga_xmin; // clipping boundary minimum x (left) + u16 m_bsga_ymin; // clipping boundary minimum y (top) + u16 m_bsga_xmax; // clipping boundary maximum x (right) + u16 m_bsga_ymax; // clipping boundary maximum y (bottom) + + u16 m_bsga_status; + + u16 m_bsga_xin1; + u16 m_bsga_yin1; + u16 m_bsga_xin2; + u16 m_bsga_yin2; + + u16 m_bsga_xin; // most recently written xin1/xin2 + u16 m_bsga_yin; // most recently written yin1/yin2 + + u32 m_ri_initial_distance; + u32 m_ri_distance_both; + u32 m_ri_distance_major; + u32 m_ri_initial_address; + u32 m_ri_address_both; + u32 m_ri_address_major; + u32 m_ri_initial_error; + u32 m_ri_error_both; + u32 m_ri_error_major; + u32 m_ri_stop_count; + u32 m_ri_control; + u32 m_ri_xfer; + + emu_timer *m_vblank_timer; + emu_timer *m_blit_timer; + emu_timer *m_line_timer; + emu_timer *m_done_timer; }; -class mpcb963_device : public gt_device_base +class single_gt_device_base : public gt_device_base { -public: - mpcb963_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +protected: + single_gt_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + + virtual void map(address_map &map) override; + + virtual DECLARE_WRITE32_MEMBER(blit_control_w) override; + + DECLARE_READ32_MEMBER(buffer_r) { return buffer_read(m_screen[0], offset); } + DECLARE_WRITE32_MEMBER(buffer_w) { buffer_write(m_screen[0], offset, data, mem_mask); } + virtual const int get_screen_count() const override { return GT_SCREEN_COUNT; } + virtual gt_screen_t &get_screen(const int number) override { return m_screen[number]; } + virtual const gt_screen_t &active_screen() const override { return m_screen[0]; } + + u32 screen_update0(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); + +private: static const int GT_SCREEN_COUNT = 1; + gt_screen_t m_screen[GT_SCREEN_COUNT]; +}; + +class dual_gt_device_base : public gt_device_base +{ protected: - virtual const tiny_rom_entry *device_rom_region() const override; - virtual void device_add_mconfig(machine_config &config) override; - virtual void device_start() override; + dual_gt_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); virtual void map(address_map &map) override; - virtual DECLARE_READ16_MEMBER(control_r) override { return m_control; } - virtual DECLARE_WRITE16_MEMBER(control_w) override; - virtual DECLARE_READ32_MEMBER(vram_r) override; - virtual DECLARE_WRITE32_MEMBER(vram_w) override; + virtual DECLARE_WRITE32_MEMBER(blit_control_w) override; + + DECLARE_READ32_MEMBER(buffer_r) { return buffer_read(m_screen[(offset & 0x80000) ? 1 : 0], offset); } + DECLARE_WRITE32_MEMBER(buffer_w) { buffer_write(m_screen[(offset & 0x80000) ? 1 : 0], offset, data, mem_mask); } + + virtual const int get_screen_count() const override { return GT_SCREEN_COUNT; } + virtual gt_screen_t &get_screen(const int number) override { return m_screen[number]; } + virtual const gt_screen_t &active_screen() const override { return m_screen[(m_control & GFX_SCR1_SEL) ? 1 : 0]; } u32 screen_update0(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); + u32 screen_update1(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); private: - u16 m_control; + static const int GT_SCREEN_COUNT = 2; gt_screen_t m_screen[GT_SCREEN_COUNT]; }; -class mpcba79_device : public gt_device_base +class mpcb963_device : public single_gt_device_base +{ +public: + mpcb963_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + +protected: + virtual const tiny_rom_entry *device_rom_region() const override; + virtual void device_add_mconfig(machine_config &config) override; +}; + +class mpcba79_device : public dual_gt_device_base { public: mpcba79_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - static const int GT_SCREEN_COUNT = 2; +protected: + virtual const tiny_rom_entry *device_rom_region() const override; + virtual void device_add_mconfig(machine_config &config) override; +}; + + +class mpcb070_device : public single_gt_device_base +{ +public: + mpcb070_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); protected: virtual const tiny_rom_entry *device_rom_region() const override; virtual void device_add_mconfig(machine_config &config) override; - virtual void device_start() override; +}; - u32 screen_update0(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); - u32 screen_update1(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); +class mpcb071_device : public dual_gt_device_base +{ +public: + mpcb071_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - virtual void map(address_map &map) override; +protected: + virtual const tiny_rom_entry *device_rom_region() const override; + virtual void device_add_mconfig(machine_config &config) override; +}; - virtual DECLARE_READ16_MEMBER(control_r) override { return m_control; } - virtual DECLARE_WRITE16_MEMBER(control_w) override; - virtual DECLARE_READ32_MEMBER(vram_r) override; - virtual DECLARE_WRITE32_MEMBER(vram_w) override; +class mpcb081_device : public single_gt_device_base +{ +public: + mpcb081_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); -private: - u16 m_control; - gt_screen_t m_screen[GT_SCREEN_COUNT]; +protected: + virtual const tiny_rom_entry *device_rom_region() const override; + virtual void device_add_mconfig(machine_config &config) override; }; // device type definition DECLARE_DEVICE_TYPE(MPCB963, mpcb963_device) DECLARE_DEVICE_TYPE(MPCBA79, mpcba79_device) +DECLARE_DEVICE_TYPE(MPCB070, mpcb070_device) +DECLARE_DEVICE_TYPE(MPCB071, mpcb071_device) +DECLARE_DEVICE_TYPE(MPCB081, mpcb081_device) #endif // MAME_BUS_INTERPRO_SR_GT_H diff --git a/src/devices/bus/interpro/sr/sr.cpp b/src/devices/bus/interpro/sr/sr.cpp index 112aa582746..29fc59c8294 100644 --- a/src/devices/bus/interpro/sr/sr.cpp +++ b/src/devices/bus/interpro/sr/sr.cpp @@ -2,8 +2,7 @@ // copyright-holders:Patrick Mackinlay /* - * An initial very primitive emulation of the SR bus for the Intergraph - * InterPro. + * Shared Resource (SR) Bus emulation for Intergraph InterPro systems. * * The bus is referred to by several different names at different places * in the system code, such as SR, SR bus, SRX, SRX/C bus, CBUS and some @@ -270,12 +269,10 @@ sr_slot_device::sr_slot_device(const machine_config &mconfig, const char *tag, d { } -void sr_slot_device::static_set_sr_slot(device_t &device, const char *tag, const char *slot_tag) +void sr_slot_device::set_sr_slot(const char *tag, const char *slot_tag) { - sr_slot_device &sr_card = dynamic_cast(device); - - sr_card.m_sr_tag = tag; - sr_card.m_sr_slot_tag = slot_tag; + m_sr_tag = tag; + m_sr_slot_tag = slot_tag; } void sr_slot_device::device_start() @@ -297,13 +294,11 @@ sr_device::sr_device(const machine_config &mconfig, const char *tag, device_t *o { } -void sr_device::static_set_memory(device_t &device, const char *const tag, const int data_spacenum, const int io_spacenum) +void sr_device::set_memory(const char *const tag, const int main_spacenum, const int io_spacenum) { - sr_device &sr = dynamic_cast(device); - - sr.m_memory_tag = tag; - sr.m_data_spacenum = data_spacenum; - sr.m_io_spacenum = io_spacenum; + m_memory_tag = tag; + m_main_spacenum = main_spacenum; + m_io_spacenum = io_spacenum; } void sr_device::device_start() @@ -313,7 +308,7 @@ void sr_device::device_start() // get the memory spaces device_memory_interface *memory; siblingdevice(m_memory_tag)->interface(memory); - m_data_space = &memory->space(m_data_spacenum); + m_main_space = &memory->space(m_main_spacenum); m_io_space = &memory->space(m_io_spacenum); // resolve callbacks @@ -360,6 +355,12 @@ void device_sr_card_interface::set_sr_device() m_sr->install_card(*this, &device_sr_card_interface::map); } +void device_sr_card_interface::irq(int state) +{ + // FIXME: hard-wire to irq0 for now + m_sr->irq0_w(state); +} + sr_card_device_base::sr_card_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, const char *idprom_region) : device_t(mconfig, type, tag, owner, clock) , device_sr_card_interface(mconfig, *this) diff --git a/src/devices/bus/interpro/sr/sr.h b/src/devices/bus/interpro/sr/sr.h index f53e6e031ad..02b2734d05d 100644 --- a/src/devices/bus/interpro/sr/sr.h +++ b/src/devices/bus/interpro/sr/sr.h @@ -18,13 +18,14 @@ #define MCFG_SR_SLOT_ADD(_srtag, _tag, _slot_intf, _def_slot, _fixed) \ MCFG_DEVICE_ADD(_tag, SR_SLOT, 0) \ MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, _fixed) \ - sr_slot_device::static_set_sr_slot(*device, _srtag, _tag); + downcast(*device).set_sr_slot(_srtag, _tag); #define MCFG_SR_SLOT_REMOVE(_tag) \ MCFG_DEVICE_REMOVE(_tag) -#define MCFG_SR_MEMORY(_tag, _data_spacenum, _io_spacenum) \ - sr_device::static_set_memory(*device, _tag, _data_spacenum, _io_spacenum); +#define MCFG_SR_MEMORY(_tag, _main_spacenum, _io_spacenum) \ + downcast(*device).set_memory(_tag, _main_spacenum, _io_spacenum); + class sr_slot_device : public device_t, public device_slot_interface { @@ -33,7 +34,7 @@ public: sr_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); // inline configuration - static void static_set_sr_slot(device_t &device, const char *tag, const char *slot_tag); + void set_sr_slot(const char *tag, const char *slot_tag); protected: sr_slot_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock); @@ -59,7 +60,7 @@ public: template static devcb_base &set_out_irq1_callback(device_t &device, Object &&cb) { return downcast(device).m_out_irq1_cb.set_callback(std::forward(cb)); } template static devcb_base &set_out_irq2_callback(device_t &device, Object &&cb) { return downcast(device).m_out_irq2_cb.set_callback(std::forward(cb)); } - static void static_set_memory(device_t &device, const char *const tag, const int data_spacenum, const int io_spacenum); + void set_memory(const char *const tag, const int main_spacenum, const int io_spacenum); static const u32 SR_BASE = 0x87000000; static const u32 SR_SIZE = 0x08000000; @@ -80,7 +81,7 @@ public: offs_t end = start + (SR_SIZE - 1); // install the device address map - m_data_space->install_device(start, end, device, map); + m_main_space->install_device(start, end, device, map); m_io_space->install_device(start, end, device, map); m_slot_count++; @@ -94,7 +95,7 @@ protected: virtual void device_reset() override; // internal state - address_space *m_data_space; + address_space *m_main_space; address_space *m_io_space; devcb_write_line m_out_irq0_cb; @@ -106,7 +107,7 @@ private: int m_slot_count; const char *m_memory_tag; - int m_data_spacenum; + int m_main_spacenum; int m_io_spacenum; }; @@ -117,6 +118,7 @@ public: virtual ~device_sr_card_interface(); void set_sr_device(); + void irq(int status); // inline configuration static void static_set_sr_tag(device_t &device, const char *tag, const char *slot_tag); diff --git a/src/devices/bus/interpro/sr/sr_cards.cpp b/src/devices/bus/interpro/sr/sr_cards.cpp index 9a626b04a6e..ce15e772492 100644 --- a/src/devices/bus/interpro/sr/sr_cards.cpp +++ b/src/devices/bus/interpro/sr/sr_cards.cpp @@ -21,4 +21,7 @@ SLOT_INTERFACE_START(sr_cards) SLOT_INTERFACE("mpcb963", MPCB963) SLOT_INTERFACE("mpcba79", MPCBA79) + SLOT_INTERFACE("mpcb070", MPCB070) + SLOT_INTERFACE("mpcb071", MPCB071) + SLOT_INTERFACE("mpcb081", MPCB081) SLOT_INTERFACE_END -- cgit v1.2.3