summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/video/saa5050.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/video/saa5050.cpp')
-rw-r--r--src/devices/video/saa5050.cpp662
1 files changed, 662 insertions, 0 deletions
diff --git a/src/devices/video/saa5050.cpp b/src/devices/video/saa5050.cpp
new file mode 100644
index 00000000000..12ab5d41a16
--- /dev/null
+++ b/src/devices/video/saa5050.cpp
@@ -0,0 +1,662 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder, Nigel Barnes
+/**********************************************************************
+
+ Mullard SAA5050 Teletext Character Generator emulation
+
+ http://www.bighole.nl/pub/mirror/homepage.ntlworld.com/kryten_droid/teletext/spec/teletext_spec_1974.htm
+
+**********************************************************************/
+
+/*
+
+ TODO:
+
+ - remote controller input
+ - boxing
+
+*/
+
+#include "saa5050.h"
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type SAA5050 = &device_creator<saa5050_device>;
+const device_type SAA5051 = &device_creator<saa5051_device>;
+const device_type SAA5052 = &device_creator<saa5052_device>;
+const device_type SAA5053 = &device_creator<saa5053_device>;
+const device_type SAA5054 = &device_creator<saa5054_device>;
+const device_type SAA5055 = &device_creator<saa5055_device>;
+const device_type SAA5056 = &device_creator<saa5056_device>;
+const device_type SAA5057 = &device_creator<saa5057_device>;
+
+
+//-------------------------------------------------
+// ROM( saa5050 )
+//-------------------------------------------------
+
+ROM_START( saa5050 )
+ ROM_REGION( 0x500, "chargen", 0 )
+ ROM_LOAD("saa5050", 0x0140, 0x03c0, BAD_DUMP CRC(6298fc0b) SHA1(ae38e7f51dd33733bacfa896425ca105682b31d6))
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( saa5051 )
+//-------------------------------------------------
+
+ROM_START( saa5051 )
+ ROM_REGION( 0x500, "chargen", 0 )
+ ROM_LOAD("saa5051", 0x0140, 0x03c0, BAD_DUMP CRC(a770611c) SHA1(9ab9d24b845fe2964fba2f4770d54025d2c8026a))
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( saa5052 )
+//-------------------------------------------------
+
+ROM_START( saa5052 )
+ ROM_REGION( 0x500, "chargen", 0 )
+ ROM_LOAD("saa5052", 0x0140, 0x03c0, BAD_DUMP CRC(2eb76737) SHA1(ec4bc515e28e851a6433f7ca0a11ede0f1d21a68))
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( saa5053 )
+//-------------------------------------------------
+
+ROM_START( saa5053 )
+ ROM_REGION( 0x500, "chargen", 0 )
+ ROM_LOAD("saa5053", 0x0140, 0x03c0, BAD_DUMP CRC(46288c33) SHA1(1e471a1b5670d7163e9f62d31be7cab0330a07cd))
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( saa5054 )
+//-------------------------------------------------
+
+ROM_START( saa5054 )
+ ROM_REGION( 0x500, "chargen", 0 )
+ ROM_LOAD("saa5054", 0x0140, 0x03c0, BAD_DUMP CRC(56298472) SHA1(7a273ad7270507dca4ce621fc1e6b51a1ac25085))
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( saa5055 )
+//-------------------------------------------------
+
+ROM_START( saa5055 )
+ ROM_REGION( 0x500, "chargen", 0 )
+ ROM_LOAD("saa5055", 0x0140, 0x03c0, BAD_DUMP CRC(f95b9c8c) SHA1(c5ce7fe84df6de6a317fa0e87bda413c82c04618))
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( saa5056 )
+//-------------------------------------------------
+
+ROM_START( saa5056 )
+ ROM_REGION( 0x500, "chargen", 0 )
+ ROM_LOAD("saa5056", 0x0140, 0x03c0, BAD_DUMP CRC(86ab8b85) SHA1(2d1ff08b4dda15cf70832881750a962189455f41))
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( saa5057 )
+//-------------------------------------------------
+
+ROM_START( saa5057 )
+ ROM_REGION( 0x500, "chargen", 0 )
+ ROM_LOAD("saa5057", 0x0140, 0x08c0, BAD_DUMP CRC(d6664fb3) SHA1(5a93445dde03066073e2909a935900e5f8439d81))
+ROM_END
+
+
+//-------------------------------------------------
+// rom_region - device-specific ROM region
+//-------------------------------------------------
+
+const rom_entry *saa5050_device::device_rom_region() const
+{
+ return ROM_NAME( saa5050 );
+}
+
+const rom_entry *saa5051_device::device_rom_region() const
+{
+ return ROM_NAME( saa5051 );
+}
+
+const rom_entry *saa5052_device::device_rom_region() const
+{
+ return ROM_NAME( saa5052 );
+}
+
+const rom_entry *saa5053_device::device_rom_region() const
+{
+ return ROM_NAME( saa5053 );
+}
+
+const rom_entry *saa5054_device::device_rom_region() const
+{
+ return ROM_NAME( saa5054 );
+}
+
+const rom_entry *saa5055_device::device_rom_region() const
+{
+ return ROM_NAME( saa5055 );
+}
+
+const rom_entry *saa5056_device::device_rom_region() const
+{
+ return ROM_NAME( saa5056 );
+}
+
+const rom_entry *saa5057_device::device_rom_region() const
+{
+ return ROM_NAME( saa5057 );
+}
+
+
+#define ALPHANUMERIC 0x01
+#define CONTIGUOUS 0x02
+#define SEPARATED 0x03
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// saa5050_device - constructor
+//-------------------------------------------------
+
+saa5050_device::saa5050_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) :
+ device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ m_char_rom(*this, "chargen"),
+ m_read_d(*this),
+ m_frame_count(0),
+ m_cols(0),
+ m_rows(0),
+ m_size(0)
+{
+}
+
+saa5050_device::saa5050_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, SAA5050, "SAA5050 Video", tag, owner, clock, "saa5050", __FILE__),
+ m_char_rom(*this, "chargen"),
+ m_read_d(*this),
+ m_frame_count(0),
+ m_cols(0),
+ m_rows(0),
+ m_size(0)
+{
+}
+
+saa5051_device::saa5051_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : saa5050_device(mconfig, SAA5051, "SAA5051", tag, owner, clock, "saa5051", __FILE__) { }
+
+saa5052_device::saa5052_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : saa5050_device(mconfig, SAA5052, "SAA5052", tag, owner, clock, "saa5052", __FILE__) { }
+
+saa5053_device::saa5053_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : saa5050_device(mconfig, SAA5053, "SAA5053", tag, owner, clock, "saa5053", __FILE__) { }
+
+saa5054_device::saa5054_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : saa5050_device(mconfig, SAA5054, "SAA5054", tag, owner, clock, "saa5054", __FILE__) { }
+
+saa5055_device::saa5055_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : saa5050_device(mconfig, SAA5055, "SAA5055", tag, owner, clock, "saa5055", __FILE__) { }
+
+saa5056_device::saa5056_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : saa5050_device(mconfig, SAA5056, "SAA5056", tag, owner, clock, "saa5056", __FILE__) { }
+
+saa5057_device::saa5057_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : saa5050_device(mconfig, SAA5057, "SAA5057", tag, owner, clock, "saa5057", __FILE__) { }
+
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void saa5050_device::device_start()
+{
+ // resolve callbacks
+ m_read_d.resolve_safe(0);
+
+ // register for state saving
+ save_item(NAME(m_code));
+ save_item(NAME(m_held_char));
+ save_item(NAME(m_char_data));
+ save_item(NAME(m_bit));
+ save_item(NAME(m_color));
+ save_item(NAME(m_ra));
+ save_item(NAME(m_bg));
+ save_item(NAME(m_fg));
+ save_item(NAME(m_prev_col));
+ save_item(NAME(m_graphics));
+ save_item(NAME(m_separated));
+ save_item(NAME(m_flash));
+ save_item(NAME(m_boxed));
+ save_item(NAME(m_double_height));
+ save_item(NAME(m_double_height_old));
+ save_item(NAME(m_double_height_bottom_row));
+ save_item(NAME(m_double_height_prev_row));
+ save_item(NAME(m_hold_char));
+ save_item(NAME(m_hold_clear));
+ save_item(NAME(m_hold_off));
+ save_item(NAME(m_frame_count));
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific reset
+//-------------------------------------------------
+
+void saa5050_device::device_reset()
+{
+ m_ra = 0;
+ m_double_height = false;
+ m_double_height_bottom_row = false;
+}
+
+
+//-------------------------------------------------
+// process_control_character -
+//-------------------------------------------------
+
+void saa5050_device::process_control_character(UINT8 data)
+{
+ m_hold_clear = false;
+ m_hold_off = false;
+
+ switch (data)
+ {
+ case ALPHA_RED:
+ case ALPHA_GREEN:
+ case ALPHA_YELLOW:
+ case ALPHA_BLUE:
+ case ALPHA_MAGENTA:
+ case ALPHA_CYAN:
+ case ALPHA_WHITE:
+ m_graphics = false;
+ m_hold_clear = true;
+ m_fg = data & 0x07;
+ set_next_chartype();
+ break;
+
+ case FLASH:
+ m_flash = true;
+ break;
+
+ case STEADY:
+ m_flash = false;
+ break;
+
+ case END_BOX:
+ case START_BOX:
+ // TODO
+ break;
+
+ case NORMAL_HEIGHT:
+ case DOUBLE_HEIGHT:
+ m_double_height = !!(data & 1);
+ if (m_double_height) m_double_height_prev_row = true;
+ break;
+
+ case GRAPHICS_RED:
+ case GRAPHICS_GREEN:
+ case GRAPHICS_YELLOW:
+ case GRAPHICS_BLUE:
+ case GRAPHICS_MAGENTA:
+ case GRAPHICS_CYAN:
+ case GRAPHICS_WHITE:
+ m_graphics = true;
+ m_fg = data & 0x07;
+ set_next_chartype();
+ break;
+
+ case CONCEAL_DISPLAY:
+ m_fg = m_prev_col = m_bg;
+ break;
+
+ case CONTIGUOUS_GFX:
+ m_separated = false;
+ set_next_chartype();
+ break;
+
+ case SEPARATED_GFX:
+ m_separated = true;
+ set_next_chartype();
+ break;
+
+ case BLACK_BACKGROUND:
+ m_bg = 0;
+ break;
+
+ case NEW_BACKGROUND:
+ m_bg = m_fg;
+ break;
+
+ case HOLD_GRAPHICS:
+ m_hold_char = true;
+ break;
+
+ case RELEASE_GRAPHICS:
+ m_hold_off = true;
+ break;
+ }
+}
+
+
+void saa5050_device::set_next_chartype()
+{
+ if (m_graphics)
+ {
+ if (m_separated)
+ m_next_chartype = SEPARATED;
+ else
+ m_next_chartype = CONTIGUOUS;
+ }
+ else
+ {
+ m_next_chartype = ALPHANUMERIC;
+ }
+};
+
+
+//-------------------------------------------------
+// get_gfx_data - graphics generator
+//-------------------------------------------------
+
+UINT16 saa5050_device::get_gfx_data(UINT8 data, offs_t row, bool separated)
+{
+ UINT16 c = 0;
+ switch (row >> 1)
+ {
+ case 0: case 1:
+ c += (data & 0x01) ? 0xfc0 : 0; // bit 1 top left
+ c += (data & 0x02) ? 0x03f : 0; // bit 2 top right
+ if (separated) c &= 0x3cf;
+ break;
+ case 2:
+ if (separated) break;
+ c += (data & 0x01) ? 0xfc0 : 0; // bit 1 top left
+ c += (data & 0x02) ? 0x03f : 0; // bit 2 top right
+ break;
+ case 3: case 4: case 5:
+ c += (data & 0x04) ? 0xfc0 : 0; // bit 3 middle left
+ c += (data & 0x08) ? 0x03f : 0; // bit 4 middle right
+ if (separated) c &= 0x3cf;
+ break;
+ case 6:
+ if (separated) break;
+ c += (data & 0x04) ? 0xfc0 : 0; // bit 3 middle left
+ c += (data & 0x08) ? 0x03f : 0; // bit 4 middle right
+ break;
+ case 7: case 8:
+ c += (data & 0x10) ? 0xfc0 : 0; // bit 5 bottom left
+ c += (data & 0x40) ? 0x03f : 0; // bit 7 bottom right
+ if (separated) c &= 0x3cf;
+ break;
+ case 9:
+ if (separated) break;
+ c += (data & 0x10) ? 0xfc0 : 0; // bit 5 bottom left
+ c += (data & 0x40) ? 0x03f : 0; // bit 7 bottom right
+ break;
+ }
+ return c;
+}
+
+
+//-------------------------------------------------
+// get_rom_data - read rom
+//-------------------------------------------------
+
+UINT16 saa5050_device::get_rom_data(UINT8 data, offs_t row)
+{
+ UINT16 c;
+ if (row < 0 || row >= 20)
+ {
+ c = 0;
+ }
+ else
+ {
+ c = m_char_rom[(data * 10) + (row >> 1)];
+ c = ((c & 0x01) * 0x03) + ((c & 0x02) * 0x06) + ((c & 0x04) * 0x0c) + ((c & 0x08) * 0x18) + ((c & 0x10) * 0x30);
+ }
+ return c;
+}
+
+
+//-------------------------------------------------
+// character_rounding
+//-------------------------------------------------
+
+UINT16 saa5050_device::character_rounding(UINT16 a, UINT16 b)
+{
+ return a | ((a >> 1) & b & ~(b >> 1)) | ((a << 1) & b & ~(b << 1));
+}
+
+
+//-------------------------------------------------
+// get_character_data -
+//-------------------------------------------------
+
+void saa5050_device::get_character_data(UINT8 data)
+{
+ m_double_height_old = m_double_height;
+ m_prev_col = m_fg;
+ m_curr_chartype = m_next_chartype;
+
+ if (data < 0x20)
+ {
+ process_control_character(data);
+ if (m_hold_char && m_double_height == m_double_height_old)
+ {
+ data = m_held_char;
+ if (data >= 0x40 && data < 0x60) data = 0x20;
+ m_curr_chartype = m_held_chartype;
+ }
+ else
+ {
+ data = 0x20;
+ }
+ }
+ else if (m_graphics)
+ {
+ m_held_char = data;
+ m_held_chartype = m_curr_chartype;
+ }
+
+ offs_t ra = m_ra;
+ if (m_double_height_old)
+ {
+ ra >>= 1;
+ if (m_double_height_bottom_row) ra += 10;
+ }
+
+ if (m_flash && (m_frame_count > 38)) data = 0x20;
+ if (m_double_height_bottom_row && !m_double_height) data = 0x20;
+
+ if (m_hold_off)
+ {
+ m_hold_char = false;
+ m_held_char = 32;
+ }
+ if (m_hold_clear)
+ {
+ m_held_char = 32;
+ }
+
+ if (m_curr_chartype == ALPHANUMERIC || !BIT(data,5))
+ m_char_data = character_rounding(get_rom_data(data, ra), get_rom_data(data, ra + ((ra & 1) ? 1 : -1)));
+ else
+ m_char_data = get_gfx_data(data, ra, (m_curr_chartype == SEPARATED));
+}
+
+
+//-------------------------------------------------
+// crs_w - character rounding select
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( saa5050_device::crs_w )
+{
+ m_crs = !(state & 1);
+}
+
+
+//-------------------------------------------------
+// dew_w - data entry window
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( saa5050_device::dew_w )
+{
+ if (state)
+ {
+ m_ra = 19;
+
+ m_frame_count++;
+ if (m_frame_count > 50) m_frame_count = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// lose_w - load output shift register enable
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( saa5050_device::lose_w )
+{
+ if (state)
+ {
+ m_ra++;
+ m_ra %= 20;
+
+ if (m_ra == 19)
+ {
+ if (m_double_height_bottom_row)
+ m_double_height_bottom_row = false;
+ else
+ m_double_height_bottom_row = m_double_height_prev_row;
+ }
+
+ m_fg = 7;
+ m_bg = 0;
+ m_graphics = false;
+ m_separated = false;
+ m_flash = false;
+ m_boxed = false;
+ m_hold_char = false;
+ m_held_char = 0x20;
+ m_next_chartype = ALPHANUMERIC;
+ m_held_chartype = ALPHANUMERIC;
+ m_double_height = false;
+ m_double_height_prev_row = false;
+ m_bit = 11;
+ }
+}
+
+
+//-------------------------------------------------
+// write - character data write
+//-------------------------------------------------
+
+void saa5050_device::write(UINT8 data)
+{
+ m_code = data & 0x7f;
+}
+
+
+//-------------------------------------------------
+// f1_w - character clock
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( saa5050_device::f1_w )
+{
+ if (state)
+ {
+ get_character_data(m_code);
+ }
+}
+
+
+//-------------------------------------------------
+// tr6_w - pixel clock
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( saa5050_device::tr6_w )
+{
+ if (state)
+ {
+ m_color = BIT(m_char_data, m_bit) ? m_prev_col : m_bg;
+
+ m_bit--;
+ if (m_bit < 0) m_bit = 11;
+ }
+}
+
+
+//-------------------------------------------------
+// get_rgb - get output color
+//-------------------------------------------------
+
+int saa5050_device::get_rgb()
+{
+ return m_color;
+}
+
+
+//-------------------------------------------------
+// screen_update -
+//-------------------------------------------------
+
+UINT32 saa5050_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ dew_w(1);
+ dew_w(0);
+
+ for (int y = 0; y < m_rows * 20; y++)
+ {
+ int sy = y / 20;
+ int x = 0;
+
+ lose_w(1);
+ lose_w(0);
+
+ int ssy = m_double_height_bottom_row ? sy - 1 : sy;
+ offs_t video_ram_addr = ssy * m_size;
+
+ for (int sx = 0; sx < m_cols; sx++)
+ {
+ UINT8 code = m_read_d(video_ram_addr++);
+
+ write(code & 0x7f);
+
+ f1_w(1);
+ f1_w(0);
+
+ for (int bit = 0; bit < 12; bit++)
+ {
+ tr6_w(1);
+ tr6_w(0);
+
+ int color = get_rgb();
+
+ if (BIT(code, 7)) color ^= 0x07;
+
+ int r = BIT(color, 0) * 0xff;
+ int g = BIT(color, 1) * 0xff;
+ int b = BIT(color, 2) * 0xff;
+
+ rgb_t rgb = rgb_t(r, g, b);
+
+ bitmap.pix32(y, x++) = rgb;
+ }
+ }
+ }
+
+ return 0;
+}