From ca8cd90e2b650ab1362c4a6bcfa9b2cb00d99ce2 Mon Sep 17 00:00:00 2001 From: Patrick Mackinlay Date: Tue, 23 Jul 2019 17:55:17 +0700 Subject: [PATCH] bt431: new device (nw) --- scripts/src/video.lua | 12 ++ src/devices/video/bt431.cpp | 283 ++++++++++++++++++++++++++++++++++++ src/devices/video/bt431.h | 95 ++++++++++++ 3 files changed, 390 insertions(+) create mode 100644 src/devices/video/bt431.cpp create mode 100644 src/devices/video/bt431.h diff --git a/scripts/src/video.lua b/scripts/src/video.lua index b49bd961f95..60f6a934655 100644 --- a/scripts/src/video.lua +++ b/scripts/src/video.lua @@ -1218,3 +1218,15 @@ if (VIDEOS["BT47X"]~=null) then MAME_DIR .. "src/devices/video/bt47x.h", } end + +-------------------------------------------------- +-- +--@src/devices/video/bt431.h,VIDEOS["BT431"] = true +-------------------------------------------------- + +if (VIDEOS["BT431"]~=null) then + files { + MAME_DIR .. "src/devices/video/bt431.cpp", + MAME_DIR .. "src/devices/video/bt431.h", + } +end diff --git a/src/devices/video/bt431.cpp b/src/devices/video/bt431.cpp new file mode 100644 index 00000000000..c66b404c39f --- /dev/null +++ b/src/devices/video/bt431.cpp @@ -0,0 +1,283 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +/* + * Brooktree Bt431 Monolithic CMOS 64x64 Pixel Cursor Generator. + * + * Sources: + * - http://bitsavers.org/components/brooktree/_dataBooks/1993_Brooktree_Graphics_and_Imaging_Product_Databook.pdf + * + * TODO: + * - test, profile and optimize + */ + +#include "emu.h" +#include "bt431.h" + +#define LOG_GENERAL (1U << 0) + +//#define VERBOSE (LOG_GENERAL) +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(BT431, bt431_device, "bt431", "Bt431 64x64 Pixel Cursor Generator") + +bt431_device::bt431_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) + : device_t(mconfig, BT431, tag, owner, clock) +{ +} + +void bt431_device::device_start() +{ + save_item(NAME(m_address)); + save_item(NAME(m_command)); + + save_item(NAME(m_cursor_x)); + save_item(NAME(m_cursor_y)); + save_item(NAME(m_window_x)); + save_item(NAME(m_window_y)); + save_item(NAME(m_window_w)); + save_item(NAME(m_window_h)); + + save_item(NAME(m_ram)); +} + +void bt431_device::device_reset() +{ + m_address = 0; + m_command = 0; + + update(); +} + +void bt431_device::map(address_map &map) +{ + map(0x00, 0x00).rw(FUNC(bt431_device::addr_r<0>), FUNC(bt431_device::addr_w<0>)); + map(0x01, 0x01).rw(FUNC(bt431_device::addr_r<8>), FUNC(bt431_device::addr_w<8>)); + map(0x02, 0x02).rw(FUNC(bt431_device::ram_r), FUNC(bt431_device::ram_w)); + map(0x03, 0x03).rw(FUNC(bt431_device::reg_r), FUNC(bt431_device::reg_w)); +} + +u8 bt431_device::ram_r() +{ + u8 const data = m_ram[m_address & ADDRESS_MASK]; + + // increment address register + if (!machine().side_effects_disabled()) + m_address = (m_address + 1) & ADDRESS_MASK; + + return data; +} + +void bt431_device::ram_w(u8 data) +{ + m_ram[m_address & ADDRESS_MASK] = data; + + // increment address register + if (!machine().side_effects_disabled()) + m_address = (m_address + 1) & ADDRESS_MASK; +} + +u8 bt431_device::reg_r() +{ + u8 data = 0; + + switch (m_address & 0xf) + { + case REG_COMMAND: data = m_command; break; + + case REG_CURSOR_X_LO: data = m_cursor_x & 0xff; break; + case REG_CURSOR_X_HI: data = (m_cursor_x >> 8); break; + case REG_CURSOR_Y_LO: data = m_cursor_y & 0xff; break; + case REG_CURSOR_Y_HI: data = (m_cursor_y >> 8); break; + + case REG_WINDOW_X_LO: data = m_window_x & 0xff; break; + case REG_WINDOW_X_HI: data = (m_window_x >> 8); break; + case REG_WINDOW_Y_LO: data = m_window_y & 0xff; break; + case REG_WINDOW_Y_HI: data = (m_window_y >> 8); break; + + case REG_WINDOW_W_LO: data = m_window_w & 0xff; break; + case REG_WINDOW_W_HI: data = (m_window_w >> 8); break; + case REG_WINDOW_H_LO: data = m_window_h & 0xff; break; + case REG_WINDOW_H_HI: data = (m_window_h >> 8); break; + + default: + LOG("read from unknown address 0x%04x (%s)\n", + m_address, machine().describe_context()); + break; + } + + // increment address register + if (!machine().side_effects_disabled()) + m_address = (m_address + 1) & ADDRESS_MASK; + + return data; +} + +void bt431_device::reg_w(u8 data) +{ + switch (m_address & 0xf) + { + case REG_COMMAND: + m_command = data; + LOG("64x64 cursor %s, cross hair cursor %s, cursor format %s, cross hair thickness %d\n", + (data & CR_D6) ? "enable" : "disable", + (data & CR_D5) ? "enable" : "disable", + (data & CR_D4) ? "OR" : "XOR", + ((data & CR_D1D0) << 1) + 1); + break; + + case REG_CURSOR_X_LO: + m_cursor_x = (m_cursor_x & 0x0f00) | data; + LOG("cursor x low register: 0x%02x\n", data); + break; + case REG_CURSOR_X_HI: + m_cursor_x = ((data & 0xf) << 8) | (m_cursor_x & 0xff); + LOG("cursor x high register: 0x%02x\n", data); + break; + case REG_CURSOR_Y_LO: + m_cursor_y = (m_cursor_y & 0x0f00) | data; + LOG("cursor y low register: 0x%02x\n", data); + break; + case REG_CURSOR_Y_HI: + m_cursor_y = ((data & 0xf) << 8) | (m_cursor_y & 0xff); + LOG("cursor y high register: 0x%02x\n", data); + break; + + case REG_WINDOW_X_LO: + m_window_x = (m_window_x & 0x0f00) | data; + LOG("window x low register: 0x%02x\n", data); + break; + case REG_WINDOW_X_HI: + m_window_x = ((data & 0xf) << 8) | (m_window_x & 0xff); + LOG("window x high register: 0x%02x\n", data); + break; + case REG_WINDOW_Y_LO: + m_window_y = (m_window_y & 0x0f00) | data; + LOG("window y low register: 0x%02x\n", data); + break; + case REG_WINDOW_Y_HI: + m_window_y = ((data & 0xf) << 8) | (m_window_y & 0xff); + LOG("window y high register: 0x%02x\n", data); + break; + + case REG_WINDOW_W_LO: + m_window_w = (m_window_w & 0x0f00) | data; + LOG("window width low register: 0x%02x\n", data); + break; + case REG_WINDOW_W_HI: + m_window_w = ((data & 0xf) << 8) | (m_window_w & 0xff); + LOG("window width high register: 0x%02x\n", data); + break; + case REG_WINDOW_H_LO: + m_window_h = (m_window_h & 0x0f00) | data; + LOG("window height low register: 0x%02x\n", data); + break; + case REG_WINDOW_H_HI: + m_window_h = ((data & 0xf) << 8) | (m_window_h & 0xff); + LOG("window height high register: 0x%02x\n", data); + break; + + default: + LOG("write to unknown address 0x%04x data 0x%02x (%s)\n", + m_address, data, machine().describe_context()); + break; + } + + // increment address register + m_address = (m_address + 1) & ADDRESS_MASK; + + update(); +} + +void bt431_device::update() +{ + /* + * The cursor (x) value to be written is calculated as follows: + * + * Cx = desired display screen (x) position + D + H - P + * + * where + * + * P = 37 if 1:1 output multiplexing, 52 if 4:1 output multiplexing, + * 57 if 5:1 output multiplexing + * D = skew (in pixels) between the output cursor data and external pixel + * data + * H = number of pixels between the first rising edge of CLOCK + * following the falling edge of HSYNC* to active video + * + * The P value is one-half cursor RAM width + (internal pipeline delay in + * clock cycles * one, four or five, depending on multiplex selection). + * + * The cursor (y) value to be written is calculated as follows: + * + * Cy = desired display screen (y) position + V - 32 + * + * where + * + * V = number of scan lines from the first falling edge of HSYNC* that is + * two or more clock cycles after the falling edge of VSYNC* to + * active video. + * + * Values from $0FC0 (-64) to $0FBF (+4031) may be loaded into the + * cursor (y) register. The negative values ($0FC0 to $0FFF) are used + * in situations where V < 32, and the cursor must be moved off the + * top of the screen. + */ + const int cursor_x = m_cursor_x + ( + (m_command & CR_D3D2) == CR_D3D2_11 ? 37 : + (m_command & CR_D3D2) == CR_D3D2_41 ? 52 : + (m_command & CR_D3D2) == CR_D3D2_51 ? 57 : 0); + const int cursor_y = (m_cursor_y < 0xfc0 ? m_cursor_y : m_cursor_y - 0x1000) + 32; + + // update bitmap cursor drawing rectangle + m_bm_window.set(cursor_x - 31, cursor_x + 32, cursor_y - 31, cursor_y + 32); + + // update cross hair cursor drawing rectangles + const int thickness = m_command & CR_D1D0; + if (m_window_x == 0 && m_window_y == 0 && m_window_w == 0x0fff && m_window_h == 0x0fff) + { + // full screen cross hair cursor + m_ch_v.set(cursor_x - thickness, cursor_x + thickness, m_window_y, m_window_y + m_window_h - 1); + m_ch_h.set(m_window_x, m_window_x + m_window_w - 1, cursor_y - thickness, cursor_y + thickness); + } + else + { + // windowed cross hair cursor + const int window_x = m_window_x + ( + (m_command & CR_D3D2) == CR_D3D2_11 ? 5 : + (m_command & CR_D3D2) == CR_D3D2_41 ? 20 : + (m_command & CR_D3D2) == CR_D3D2_51 ? 25 : 0); + const int window_y = m_window_y; + + const int window_w = m_window_w + ( + (m_command & CR_D3D2) == CR_D3D2_11 ? 2 : + (m_command & CR_D3D2) == CR_D3D2_41 ? 8 : + (m_command & CR_D3D2) == CR_D3D2_51 ? 10 : 0); + const int window_h = m_window_h + 2; + + m_ch_v.set(cursor_x - thickness, cursor_x + thickness, window_y + 1, window_y + window_h - 2); + m_ch_h.set(window_x + 1, window_x + window_w - 2, cursor_y - thickness, cursor_y + thickness); + } +} + +bool bt431_device::cur_r(unsigned x, unsigned y) const +{ + bool data = false; + + // cross hair cursor + if ((m_command & CR_D5) && (m_ch_h.contains(x, y) || m_ch_v.contains(x, y))) + data = true; + + // bitmap cursor + if ((m_command & CR_D6) && m_bm_window.contains(x, y)) + { + bool const bit = BIT(m_ram[(y - m_bm_window.top()) * 8 + (x - m_bm_window.left()) / 8], 7 - (x - m_bm_window.left()) % 8); + + if (m_command & CR_D4) + data |= bit; + else + data ^= bit; + } + + return data; +} diff --git a/src/devices/video/bt431.h b/src/devices/video/bt431.h new file mode 100644 index 00000000000..86241a8d87b --- /dev/null +++ b/src/devices/video/bt431.h @@ -0,0 +1,95 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +#ifndef MAME_VIDEO_BT431_H +#define MAME_VIDEO_BT431_H + +#pragma once + +class bt431_device : public device_t +{ +public: + bt431_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); + + enum register_number : unsigned + { + REG_COMMAND = 0, + REG_CURSOR_X_LO = 1, + REG_CURSOR_X_HI = 2, + REG_CURSOR_Y_LO = 3, + REG_CURSOR_Y_HI = 4, + REG_WINDOW_X_LO = 5, + REG_WINDOW_X_HI = 6, + REG_WINDOW_Y_LO = 7, + REG_WINDOW_Y_HI = 8, + REG_WINDOW_W_LO = 9, + REG_WINDOW_W_HI = 10, + REG_WINDOW_H_LO = 11, + REG_WINDOW_H_HI = 12, + }; + + enum command_mask : u8 + { + CR_D1D0 = 0x03, // cross hair cursor thickness + CR_D3D2 = 0x0c, // multiplex control + CR_D4 = 0x10, // cursor format control + CR_D5 = 0x20, // cross hair cursor enable + CR_D6 = 0x40, // 64x64 cursor enable + }; + + enum cr_d1d0_mask : u8 + { + CR_D1D0_1PIX = 0x00, // 1 pixel + CR_D1D0_3PIX = 0x01, // 3 pixels + CR_D1D0_5PIX = 0x02, // 5 pixels + CR_D1D0_7PIX = 0x03, // 7 pixels + }; + enum cr_d3d2_mask : u8 + { + CR_D3D2_11 = 0x00, // 1:1 multiplexing + CR_D3D2_41 = 0x04, // 4:1 multiplexing + CR_D3D2_51 = 0x08, // 5:1 multiplexing + }; + + void map(address_map &map); + bool cur_r(unsigned x, unsigned y) const; + +protected: + static constexpr u16 ADDRESS_MASK = 0x01ff; + + // device_t overrides + virtual void device_start() override; + virtual void device_reset() override; + + template u8 addr_r() { return m_address >> S; } + template void addr_w(u8 data) { m_address = (m_address & (0xff00 >> S)) | (u16(data) << S); } + + u8 ram_r(); + void ram_w(u8 data); + + u8 reg_r(); + void reg_w(u8 data); + +private: + void update(); + + // registers + u16 m_address; + u8 m_command; + u16 m_cursor_x; + u16 m_cursor_y; + u16 m_window_x; + u16 m_window_y; + u16 m_window_w; + u16 m_window_h; + + u8 m_ram[512]; + + rectangle m_bm_window; + rectangle m_ch_h; + rectangle m_ch_v; +}; + +DECLARE_DEVICE_TYPE(BT431, bt431_device) + +#endif // MAME_VIDEO_BT431_H