summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/video/tms9927.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/video/tms9927.c')
-rw-r--r--trunk/src/emu/video/tms9927.c364
1 files changed, 364 insertions, 0 deletions
diff --git a/trunk/src/emu/video/tms9927.c b/trunk/src/emu/video/tms9927.c
new file mode 100644
index 00000000000..1c9d006b847
--- /dev/null
+++ b/trunk/src/emu/video/tms9927.c
@@ -0,0 +1,364 @@
+/**********************************************************************
+
+ TI TMS9927 and compatible CRT controller emulation
+
+ Copyright Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+**********************************************************************/
+
+#include "emu.h"
+#include "tms9927.h"
+
+
+static const UINT8 chars_per_row_value[8] = { 20, 32, 40, 64, 72, 80, 96, 132 };
+static const UINT8 skew_bits_value[4] = { 0, 1, 2, 2 };
+
+
+#define HCOUNT(t) ((t)->reg[0] + 1)
+#define INTERLACED(t) (((t)->reg[1] >> 7) & 0x01)
+#define HSYNC_WIDTH(t) (((t)->reg[1] >> 4) & 0x0f)
+#define HSYNC_DELAY(t) (((t)->reg[1] >> 0) & 0x07)
+#define SCANS_PER_DATA_ROW(t) ((((t)->reg[2] >> 3) & 0x0f) + 1)
+#define CHARS_PER_DATA_ROW(t) (chars_per_row_value[((t)->reg[2] >> 0) & 0x07])
+#define SKEW_BITS(t) (skew_bits_value[((t)->reg[3] >> 6) & 0x03])
+#define DATA_ROWS_PER_FRAME(t) ((((t)->reg[3] >> 0) & 0x3f) + 1)
+#define SCAN_LINES_PER_FRAME(t) (((t)->reg[4] * 2) + 256)
+#define VERTICAL_DATA_START(t) ((t)->reg[5])
+#define LAST_DISP_DATA_ROW(t) ((t)->reg[6] & 0x3f)
+#define CURSOR_CHAR_ADDRESS(t) ((t)->reg[7])
+#define CURSOR_ROW_ADDRESS(t) ((t)->reg[8] & 0x3f)
+
+
+typedef struct _tms9927_state tms9927_state;
+struct _tms9927_state
+{
+ /* driver-controlled state */
+ const tms9927_interface *intf;
+ screen_device *screen;
+ const UINT8 *selfload;
+
+ /* live state */
+ UINT32 clock;
+ UINT8 reg[9];
+ UINT8 start_datarow;
+ UINT8 reset;
+ UINT8 hpixels_per_column;
+
+ /* derived state; no need to save */
+ UINT8 valid_config;
+ UINT16 total_hpix, total_vpix;
+ UINT16 visible_hpix, visible_vpix;
+};
+
+
+static void tms9927_state_save_postload(tms9927_state *state);
+static void recompute_parameters(tms9927_state *tms, int postload);
+
+
+const tms9927_interface tms9927_null_interface = { 0 };
+
+
+/* makes sure that the passed in device is the right type */
+INLINE tms9927_state *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == TMS9927);
+ return (tms9927_state *)downcast<legacy_device_base *>(device)->token();
+}
+
+
+static void tms9927_state_save_postload(tms9927_state *state)
+{
+ recompute_parameters(state, TRUE);
+}
+
+
+static void generic_access(device_t *device, offs_t offset)
+{
+ tms9927_state *tms = get_safe_token(device);
+
+ switch (offset)
+ {
+ case 0x07: /* Processor Self Load */
+ case 0x0f: /* Non-processor self-load */
+ if (tms->selfload != NULL)
+ {
+ int cur;
+
+ for (cur = 0; cur < 7; cur++)
+ tms9927_w(device, cur, tms->selfload[cur]);
+ for (cur = 0; cur < 1; cur++)
+ tms9927_w(device, cur + 0xc, tms->selfload[cur + 7]);
+ }
+ else
+ popmessage("tms9927: self-load initiated with no PROM!");
+
+ /* processor self-load waits with reset enabled;
+ non-processor just goes ahead */
+ tms->reset = (offset == 0x07);
+ break;
+
+ case 0x0a: /* Reset */
+ if (!tms->reset)
+ {
+ tms->screen->update_now();
+ tms->reset = TRUE;
+ }
+ break;
+
+ case 0x0b: /* Up scroll */
+mame_printf_debug("Up scroll\n");
+ tms->screen->update_now();
+ tms->start_datarow = (tms->start_datarow + 1) % DATA_ROWS_PER_FRAME(tms);
+ break;
+
+ case 0x0e: /* Start timing chain */
+ if (tms->reset)
+ {
+ tms->screen->update_now();
+ tms->reset = FALSE;
+ recompute_parameters(tms, FALSE);
+ }
+ break;
+ }
+}
+
+
+WRITE8_DEVICE_HANDLER( tms9927_w )
+{
+ tms9927_state *tms = get_safe_token(device);
+
+ switch (offset)
+ {
+ case 0x00: /* HORIZONTAL CHARACTER COUNT */
+ case 0x01: /* INTERLACED / HSYNC WIDTH / HSYNC DELAY */
+ case 0x02: /* SCANS PER DATA ROW / CHARACTERS PER DATA ROW */
+ case 0x03: /* SKEW BITS / DATA ROWS PER FRAME */
+ case 0x04: /* SCAN LINES / FRAME */
+ case 0x05: /* VERTICAL DATA START */
+ case 0x06: /* LAST DISPLAYED DATA ROW */
+ tms->reg[offset] = data;
+ recompute_parameters(tms, FALSE);
+ break;
+
+ case 0x0c: /* LOAD CURSOR CHARACTER ADDRESS */
+ case 0x0d: /* LOAD CURSOR ROW ADDRESS */
+mame_printf_debug("Cursor address changed\n");
+ tms->reg[offset - 0x0c + 7] = data;
+ recompute_parameters(tms, FALSE);
+ break;
+
+ default:
+ generic_access(device, offset);
+ break;
+ }
+}
+
+
+READ8_DEVICE_HANDLER( tms9927_r )
+{
+ tms9927_state *tms = get_safe_token(device);
+
+ switch (offset)
+ {
+ case 0x08: /* READ CURSOR CHARACTER ADDRESS */
+ case 0x09: /* READ CURSOR ROW ADDRESS */
+ return tms->reg[offset - 0x08 + 7];
+
+ default:
+ generic_access(device, offset);
+ break;
+ }
+ return 0xff;
+}
+
+
+int tms9927_screen_reset(device_t *device)
+{
+ tms9927_state *tms = get_safe_token(device);
+ return tms->reset;
+}
+
+
+int tms9927_upscroll_offset(device_t *device)
+{
+ tms9927_state *tms = get_safe_token(device);
+ return tms->start_datarow;
+}
+
+
+int tms9927_cursor_bounds(device_t *device, rectangle &bounds)
+{
+ tms9927_state *tms = get_safe_token(device);
+ int cursorx = CURSOR_CHAR_ADDRESS(tms);
+ int cursory = CURSOR_ROW_ADDRESS(tms);
+
+ bounds.min_x = cursorx * tms->hpixels_per_column;
+ bounds.max_x = bounds.min_x + tms->hpixels_per_column - 1;
+ bounds.min_y = cursory * SCANS_PER_DATA_ROW(tms);
+ bounds.max_y = bounds.min_y + SCANS_PER_DATA_ROW(tms) - 1;
+
+ return (cursorx < HCOUNT(tms) && cursory <= LAST_DISP_DATA_ROW(tms));
+}
+
+
+static void recompute_parameters(tms9927_state *tms, int postload)
+{
+ UINT16 offset_hpix, offset_vpix;
+ attoseconds_t refresh;
+ rectangle visarea;
+
+ if (tms->intf == NULL || tms->reset)
+ return;
+
+ /* compute the screen sizes */
+ tms->total_hpix = HCOUNT(tms) * tms->hpixels_per_column;
+ tms->total_vpix = SCAN_LINES_PER_FRAME(tms);
+
+ /* determine the visible area, avoid division by 0 */
+ tms->visible_hpix = CHARS_PER_DATA_ROW(tms) * tms->hpixels_per_column;
+ tms->visible_vpix = (LAST_DISP_DATA_ROW(tms) + 1) * SCANS_PER_DATA_ROW(tms);
+
+ /* determine the horizontal/vertical offsets */
+ offset_hpix = HSYNC_DELAY(tms) * tms->hpixels_per_column;
+ offset_vpix = VERTICAL_DATA_START(tms);
+
+ mame_printf_debug("TMS9937: Total = %dx%d, Visible = %dx%d, Offset=%dx%d, Skew=%d\n", tms->total_hpix, tms->total_vpix, tms->visible_hpix, tms->visible_vpix, offset_hpix, offset_vpix, SKEW_BITS(tms));
+
+ /* see if it all makes sense */
+ tms->valid_config = TRUE;
+ if (tms->visible_hpix > tms->total_hpix || tms->visible_vpix > tms->total_vpix)
+ {
+ tms->valid_config = FALSE;
+ logerror("tms9927: invalid visible size (%dx%d) versus total size (%dx%d)\n", tms->visible_hpix, tms->visible_vpix, tms->total_hpix, tms->total_vpix);
+ }
+
+ /* update */
+ if (!tms->valid_config)
+ return;
+
+ /* create a visible area */
+ /* fix me: how do the offsets fit in here? */
+ visarea.set(0, tms->visible_hpix - 1, 0, tms->visible_vpix - 1);
+
+ refresh = HZ_TO_ATTOSECONDS(tms->clock) * tms->total_hpix * tms->total_vpix;
+
+ tms->screen->configure(tms->total_hpix, tms->total_vpix, visarea, refresh);
+}
+
+
+/* device interface */
+static DEVICE_START( tms9927 )
+{
+ tms9927_state *tms = get_safe_token(device);
+
+ /* validate arguments */
+ assert(device != NULL);
+
+ tms->intf = (const tms9927_interface *)device->static_config();
+
+ if (tms->intf != NULL)
+ {
+ assert(device->clock() > 0);
+ assert(tms->intf->hpixels_per_column > 0);
+
+ /* copy the initial parameters */
+ tms->clock = device->clock();
+ tms->hpixels_per_column = tms->intf->hpixels_per_column;
+
+ /* get the screen device */
+ tms->screen = downcast<screen_device *>(device->machine().device(tms->intf->screen_tag));
+ assert(tms->screen != NULL);
+
+ /* get the self-load PROM */
+ if (tms->intf->selfload_region != NULL)
+ {
+ tms->selfload = device->machine().root_device().memregion(tms->intf->selfload_region)->base();
+ assert(tms->selfload != NULL);
+ }
+ }
+
+ /* register for state saving */
+ device->machine().save().register_postload(save_prepost_delegate(FUNC(tms9927_state_save_postload), tms));
+
+ device->save_item(NAME(tms->clock));
+ device->save_item(NAME(tms->reg));
+ device->save_item(NAME(tms->start_datarow));
+ device->save_item(NAME(tms->reset));
+ device->save_item(NAME(tms->hpixels_per_column));
+}
+
+
+static DEVICE_STOP( tms9927 )
+{
+ tms9927_state *tms = get_safe_token(device);
+
+ mame_printf_debug("TMS9937: Final params: (%d, %d, %d, %d, %d, %d, %d)\n",
+ tms->clock,
+ tms->total_hpix,
+ 0, tms->visible_hpix,
+ tms->total_vpix,
+ 0, tms->visible_vpix);
+}
+
+
+static DEVICE_RESET( tms9927 )
+{
+}
+
+
+DEVICE_GET_INFO( tms9927 )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(tms9927_state); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
+
+ /* --- the following bits of info are returned as pointers to functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(tms9927); break;
+ case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME(tms9927); break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(tms9927); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "TMS9927"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "TMS9927 CRTC"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
+ }
+}
+
+DEVICE_GET_INFO( crt5027 )
+{
+ switch (state)
+ {
+ case DEVINFO_STR_NAME: strcpy(info->s, "CRT5027"); break;
+ default: DEVICE_GET_INFO_CALL(tms9927); break;
+ }
+}
+
+DEVICE_GET_INFO( crt5037 )
+{
+ switch (state)
+ {
+ case DEVINFO_STR_NAME: strcpy(info->s, "CRT5037"); break;
+ default: DEVICE_GET_INFO_CALL(tms9927); break;
+ }
+}
+
+DEVICE_GET_INFO( crt5057 )
+{
+ switch (state)
+ {
+ case DEVINFO_STR_NAME: strcpy(info->s, "CRT5057"); break;
+ default: DEVICE_GET_INFO_CALL(tms9927); break;
+ }
+}
+
+
+DEFINE_LEGACY_DEVICE(TMS9927, tms9927);
+DEFINE_LEGACY_DEVICE(CRT5027, crt5027);
+DEFINE_LEGACY_DEVICE(CRT5037, crt5037);
+DEFINE_LEGACY_DEVICE(CRT5057, crt5057);