// license:BSD-3-Clause // copyright-holders:Patrick Mackinlay /* * An implementation of the Brooktree Bt45x family (except the Bt459) of RAMDAC * devices. The 453, 454 and 455 are simpler, and have no command or control * registers, whereas the others in the family use these to support additional * options such as multiplexing, blinking etc. Otherwise the devices are quite * similar, and vary only in terms of maximum clock speed, number of color or * overlay bits, and quantity and number bits of resolution of DAC output. * * Max. Input Output * Part Clock Color Overlay Num. Levels Other * Bt451 165MHz 8 bit 2 bit 3 4 bit blinking, multiplexing * Bt453 66MHz 8 bit 2 bit 3 8 bit * Bt454 170MHz 4 bit 1 bit 3 4 bit * Bt455 170MHz 4 bit 1 bit 1 4 bit * Bt457 165Mhz 8 bit 2 bit 1 8 bit blinking, multiplexing * Bt458 165MHz 8 bit 2 bit 3 8 bit blinking, multiplexing * Bt467 220MHz 8 bit 2 bit 3 8 bit blinking, multiplexing [NOTE: Specs are assumed based on Bt458 compatibility) * * Reference: http://www.bitsavers.org/components/brooktree/_dataBooks/1991_Brooktree_Product_Databook.pdf * * The bt45x_mono_device_base uses the standard red/green/blue read/write * cycles defined in the databook, with color data active on the green cycle. * * The Bt467 is specified in its datasheet as register-compatible with the Bt458. * As such, it is currently implemented as a simple alias of the Bt458. * * TODO * - refactor to separate devices with registers * - implement blinking and rgb device overlay * - unsure about address masking when accessing overlay colors */ #include "emu.h" #include "bt45x.h" #define LOG_READS (1U << 0) #define LOG_WRITES (1U << 1) #define VERBOSE (0) #include "logmacro.h" DEFINE_DEVICE_TYPE(BT451, bt451_device, "bt451", "Brooktree Bt451 256 Color RAMDAC") DEFINE_DEVICE_TYPE(BT453, bt453_device, "bt453", "Brooktree Bt453 256 Color RAMDAC") DEFINE_DEVICE_TYPE(BT454, bt454_device, "bt454", "Brooktree Bt454 16 Color RAMDAC") DEFINE_DEVICE_TYPE(BT455, bt455_device, "bt455", "Brooktree Bt455 16 Color RAMDAC") DEFINE_DEVICE_TYPE(BT457, bt457_device, "bt457", "Brooktree Bt457 256 Color RAMDAC") DEFINE_DEVICE_TYPE(BT458, bt458_device, "bt458", "Brooktree Bt458 256 Color RAMDAC") DEFINE_DEVICE_TYPE(BT467, bt467_device, "bt467", "Brooktree Bt467 256 Color RAMDAC") void bt45x_device_base::map(address_map &map) { map(0x00, 0x00).rw(FUNC(bt45x_device_base::address_r), FUNC(bt45x_device_base::address_w)); map(0x01, 0x01).rw(FUNC(bt45x_device_base::palette_r), FUNC(bt45x_device_base::palette_w)); map(0x02, 0x02).rw(FUNC(bt45x_device_base::register_r), FUNC(bt45x_device_base::register_w)); map(0x03, 0x03).rw(FUNC(bt45x_device_base::overlay_r), FUNC(bt45x_device_base::overlay_w)); } void bt453_device::map(address_map &map) { map(0x00, 0x00).rw(FUNC(bt453_device::address_r), FUNC(bt453_device::address_w)); map(0x01, 0x01).rw(FUNC(bt453_device::palette_r), FUNC(bt453_device::palette_w)); map(0x02, 0x02).rw(FUNC(bt453_device::address_r), FUNC(bt453_device::address_w)); map(0x03, 0x03).rw(FUNC(bt453_device::overlay_r), FUNC(bt453_device::overlay_w)); } void bt454_device::map(address_map &map) { map(0x00, 0x00).rw(FUNC(bt454_device::address_r), FUNC(bt454_device::address_w)); map(0x01, 0x01).rw(FUNC(bt454_device::palette_r), FUNC(bt454_device::palette_w)); // FIXME: not clear what happens here //map(0x02, 0x02).rw(FUNC(bt454_device::address_r), FUNC(bt454_device::address_w)); map(0x03, 0x03).rw(FUNC(bt454_device::overlay_r), FUNC(bt454_device::overlay_w)); } void bt455_device::map(address_map &map) { map(0x00, 0x00).rw(FUNC(bt455_device::address_r), FUNC(bt455_device::address_w)); map(0x01, 0x01).rw(FUNC(bt455_device::palette_r), FUNC(bt455_device::palette_w)); // FIXME: not clear what happens here //map(0x02, 0x02).rw(FUNC(bt455_device::address_r), FUNC(bt455_device::address_w)); map(0x03, 0x03).rw(FUNC(bt455_device::overlay_r), FUNC(bt455_device::overlay_w)); } bt45x_device_base::bt45x_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, const XTAL &clock, const u32 palette_colors, const u32 overlay_colors) : device_t(mconfig, type, tag, owner, clock) , m_palette_colors(palette_colors) , m_overlay_colors(overlay_colors) { } bt45x_rgb_device_base::bt45x_rgb_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, const XTAL &clock, const u32 palette_colors, const u32 overlay_colors) : bt45x_device_base(mconfig, type, tag, owner, clock, palette_colors, overlay_colors) , device_palette_interface(mconfig, *this) { } bt45x_mono_device_base::bt45x_mono_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, const XTAL &clock, const u32 palette_colors, const u32 overlay_colors) : bt45x_device_base(mconfig, type, tag, owner, clock, palette_colors, overlay_colors) { } bt451_device::bt451_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : bt45x_rgb_device_base(mconfig, BT451, tag, owner, clock, 256, 4) { } bt453_device::bt453_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : bt45x_rgb_device_base(mconfig, BT453, tag, owner, clock, 256, 4) { } bt454_device::bt454_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : bt45x_rgb_device_base(mconfig, BT454, tag, owner, clock, 16, 1) { } bt455_device::bt455_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : bt45x_mono_device_base(mconfig, BT455, tag, owner, clock, 16, 1) { } bt457_device::bt457_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : bt45x_mono_device_base(mconfig, BT457, tag, owner, clock, 256, 4) { } bt458_device::bt458_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, const XTAL &clock) : bt45x_rgb_device_base(mconfig, type, tag, owner, clock, 256, 4) { } bt458_device::bt458_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : bt458_device(mconfig, BT458, tag, owner, clock) { } bt467_device::bt467_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : bt458_device(mconfig, BT467, tag, owner, clock) { } void bt45x_device_base::device_start() { save_item(NAME(m_address)); save_item(NAME(m_address_rgb)); save_item(NAME(m_read_mask)); save_item(NAME(m_blink_mask)); save_item(NAME(m_command)); save_item(NAME(m_control)); save_item(NAME(m_blink_start)); } void bt45x_rgb_device_base::device_start() { bt45x_device_base::device_start(); m_color_ram = std::make_unique[]>(m_palette_colors + m_overlay_colors); save_pointer(NAME(m_color_ram), m_palette_colors + m_overlay_colors); } void bt45x_mono_device_base::device_start() { bt45x_device_base::device_start(); m_color_ram = std::make_unique(m_palette_colors + m_overlay_colors); save_pointer(NAME(m_color_ram), m_palette_colors + m_overlay_colors); } void bt45x_device_base::device_reset() { m_blink_start = -1; } u8 bt45x_device_base::address_r() { LOGMASKED(LOG_READS, "%s: address_r 0x%02x\n", machine().describe_context(), m_address & (m_palette_colors - 1)); if (!machine().side_effects_disabled()) m_address_rgb = 0; return m_address & (m_palette_colors - 1); } void bt45x_device_base::address_w(u8 data) { LOGMASKED(LOG_WRITES, "%s: address_w 0x%02x\n", machine().describe_context(), data); m_address_rgb = 0; m_address = data & (m_palette_colors - 1); } void bt45x_device_base::increment_address(const bool side_effects) { if (!machine().side_effects_disabled() || side_effects) { // increment component index and address register m_address_rgb = (m_address_rgb + 1) % 3; if (m_address_rgb == 0) m_address = (m_address + 1) & (m_palette_colors - 1); } } void bt457_device::increment_address(const bool side_effects) { if (!machine().side_effects_disabled() || side_effects) { // check for rgb mode if (m_control & RGB) { // increment component index and address register m_address_rgb = (m_address_rgb + 1) % 3; if (m_address_rgb == 0) m_address++; } else m_address++; } } u8 bt45x_rgb_device_base::palette_r(address_space &space) { const u8 data = m_color_ram[m_address][m_address_rgb]; increment_address(); LOGMASKED(LOG_READS, "%s: palette_r 0x%02x\n", machine().describe_context(), data & get_mask()); return data & get_mask(); } u8 bt45x_mono_device_base::palette_r(address_space &space) { u8 data = space.unmap(); if (m_address_rgb == 1) data = m_color_ram[m_address]; increment_address(); LOGMASKED(LOG_READS, "%s: palette_r 0x%02x\n", machine().describe_context(), data & get_mask()); return data & get_mask(); } u8 bt457_device::palette_r(address_space &space) { u8 data = space.unmap(); // normal mode or rgb mode and selected if (!(m_control & RGB) || (m_control & RGB) == (1 << m_address_rgb)) data = m_color_ram[m_address]; increment_address(); LOGMASKED(LOG_READS, "%s: palette_r 0x%02x\n", machine().describe_context(), data); return data; } void bt45x_rgb_device_base::palette_w(u8 data) { LOGMASKED(LOG_WRITES, "%s: palette_w 0x%02x\n", machine().describe_context(), data); m_color_ram[m_address][m_address_rgb] = data & get_mask(); // update the mame palette to match the device if (m_address_rgb == 2) set_pen_color(m_address, rgb_t(m_color_ram[m_address][0], m_color_ram[m_address][1], m_color_ram[m_address][2])); increment_address(true); } void bt45x_mono_device_base::palette_w(u8 data) { LOGMASKED(LOG_WRITES, "%s: palette_w 0x%02x\n", machine().describe_context(), data); if (m_address_rgb == 1) m_color_ram[m_address] = data & get_mask(); increment_address(true); } void bt457_device::palette_w(u8 data) { LOGMASKED(LOG_WRITES, "%s: palette_w 0x%02x\n", machine().describe_context(), data); // device in normal mode, or rgb mode and selected if (!(m_control & RGB) || (m_control & RGB) == (1 << m_address_rgb)) m_color_ram[m_address] = data; increment_address(true); } u8 bt45x_device_base::register_r(address_space &space) { LOGMASKED(LOG_READS, "%s: register_r 0x%02x\n", machine().describe_context(), m_address); switch (m_address) { case REG_READ_MASK: return m_read_mask; case REG_BLINK_MASK: return m_blink_mask; case REG_COMMAND: return m_command; case REG_CONTROL: return m_control; } return space.unmap(); } void bt45x_device_base::register_w(u8 data) { switch (m_address) { case REG_READ_MASK: LOGMASKED(LOG_WRITES, "%s: register_w: read mask 0x%02x\n", machine().describe_context(), data); m_read_mask = data; break; case REG_BLINK_MASK: LOGMASKED(LOG_WRITES, "%s: register_w: blink mask 0x%02x\n", machine().describe_context(), data); m_blink_mask = data; break; case REG_COMMAND: LOGMASKED(LOG_WRITES, "%s: register_w: command 0x%02x, %d:1 multiplexing, use %s, %s, OL1 %s blinking, OL0 %s blinking, OL1 display %s, OL0 display %s\n", machine().describe_context(), data, (data & CR7) ? 5 : 4, (data & CR6) ? "color palette RAM" : "overlay color 0", (data & CR54) == CR54_6464 ? "64 on 64 off (50/50)" : (data & CR54) == CR54_3232 ? "32 on 32 off (50/50)" : (data & CR54) == CR54_1616 ? "16 on 16 off (50/50)" : "16 on 48 off (25/75)", (data & CR3) ? "enable" : "disable", (data & CR2) ? "enable" : "disable", (data & CR1) ? "enable" : "disable", (data & CR0) ? "enable" : "disable"); m_command = data; break; case REG_CONTROL: LOGMASKED(LOG_WRITES, "%s: register_w: control 0x%02x, %s nibble%s%s%s\n", data, (data & D3) ? "low" : "high", (data & D2) ? ", blue channel enable" : "", (data & D1) ? ", green channel enable" : "", (data & D0) ? ", red channel enable" : ""); m_control = data & 0xf; break; } } u8 bt45x_rgb_device_base::overlay_r(address_space &space) { // address is ignored for 1 bit overlay devices const u8 address = (m_overlay_colors == 1) ? 0 : m_address; u8 data = space.unmap(); if (address < m_overlay_colors) data = m_color_ram[m_palette_colors + address][m_address_rgb]; increment_address(); LOGMASKED(LOG_READS, "%s: overlay_r 0x%02x\n", machine().describe_context(), data & get_mask()); return data & get_mask(); } u8 bt45x_mono_device_base::overlay_r(address_space &space) { // address is ignored for 1 bit overlay devices const u8 address = (m_overlay_colors == 1) ? 0 : m_address; u8 data = space.unmap(); if (address < m_overlay_colors && m_address_rgb == 1) data = m_color_ram[m_palette_colors + address]; increment_address(); LOGMASKED(LOG_READS, "%s: overlay_r 0x%02x\n", machine().describe_context(), data & get_mask()); return data & get_mask(); } u8 bt457_device::overlay_r(address_space &space) { u8 data = space.unmap(); if (m_address < m_overlay_colors) { // device in normal mode, or rgb mode and selected if (!(m_control & RGB) || (m_control & RGB) == (1 << m_address_rgb)) data = m_color_ram[m_address + m_palette_colors]; } increment_address(); LOGMASKED(LOG_READS, "%s: overlay_r 0x%02x\n", machine().describe_context(), data); return data; } void bt45x_rgb_device_base::overlay_w(u8 data) { LOGMASKED(LOG_WRITES, "%s: overlay_w 0x%02x\n", machine().describe_context(), data); // address is ignored for 1 bit overlay devices const u8 address = (m_overlay_colors == 1) ? 0 : m_address; if (address < m_overlay_colors) { m_color_ram[m_palette_colors + address][m_address_rgb] = data & get_mask(); // update the mame palette to match the device if (m_address_rgb == 2) set_pen_color(m_palette_colors + address, rgb_t( m_color_ram[m_palette_colors + address][0], m_color_ram[m_palette_colors + address][1], m_color_ram[m_palette_colors + address][2])); } increment_address(true); } void bt45x_mono_device_base::overlay_w(u8 data) { LOGMASKED(LOG_WRITES, "%s: overlay_w 0x%02x\n", machine().describe_context(), data); // address is ignored for 1 bit overlay devices const u8 address = (m_overlay_colors == 1) ? 0 : m_address; if (address < m_overlay_colors) m_color_ram[m_palette_colors + address] = data & get_mask(); increment_address(true); } void bt457_device::overlay_w(u8 data) { LOGMASKED(LOG_WRITES, "%s: overlay_w 0x%02x\n", machine().describe_context(), data); if (m_address < m_overlay_colors) { // device in normal mode, or rgb mode and selected if (!(m_control & RGB) || (m_control & RGB) == (1 << m_address_rgb)) m_color_ram[m_palette_colors + m_address] = data; } increment_address(true); }