summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/cpu/tms34010/tms34010.c
blob: 5b0945ee3e5535d8e998868cca6f4ab3faab4cea (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/**********************************************************************

    LH5810/LH5811 Input/Output Port Controller

**********************************************************************/

#pragma once

#ifndef __LH5810__
#define __LH5810__

#include "emu.h"

//*************************************************************************
//  MACROS / CONSTANTS
//*************************************************************************

enum
{
	LH5810_RESET = 4,
	LH5810_U,
	LH5810_L,
	LH5820_F,
	LH5810_OPC,
	LH5810_G,
	LH5810_MSK,
	LH5810_IF,
	LH5810_DDA,
	LH5810_DDB,
	LH5810_OPA,
	LH5810_OPB
};


//*************************************************************************
//  INTERFACE CONFIGURATION MACROS
//*************************************************************************

#define MCFG_LH5810_PORTA_R_CB(_devcb) \
	devcb = &lh5810_device::set_porta_r_callback(*device, DEVCB2_##_devcb);

#define MCFG_LH5810_PORTA_W_CB(_devcb) \
	devcb = &lh5810_device::set_porta_w_callback(*device, DEVCB2_##_devcb);
	
#define MCFG_LH5810_PORTB_R_CB(_devcb) \
	devcb = &lh5810_device::set_portb_r_callback(*device, DEVCB2_##_devcb);
	
#define MCFG_LH5810_PORTB_W_CB(_devcb) \
	devcb = &lh5810_device::set_portb_w_callback(*device, DEVCB2_##_devcb);
	
#define MCFG_LH5810_PORTC_W_CB(_devcb) \
	devcb = &lh5810_device::set_portc_w_callback(*device, DEVCB2_##_devcb);
 
#define MCFG_LH5810_OUT_INT_CB(_devcb) \
	devcb = &lh5810_device::set_out_int_callback(*device, DEVCB2_##_devcb); //currently unused



//*************************************************************************
//  TYPE DEFINITIONS
//*************************************************************************

// ======================> lh5810_device

class lh5810_device :   public device_t
{
public:
	// construction/destruction
	lh5810_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);

	template<class _Object> static devcb2_base &set_porta_r_callback(device_t &device, _Object object) { return downcast<lh5810_device &>(device).m_porta_r_cb.set_callback(object); }
	template<class _Object> static devcb2_base &set_porta_w_callback(device_t &device, _Object object) { return downcast<lh5810_device &>(device).m_porta_w_cb.set_callback(object); }
	template<class _Object> static devcb2_base &set_portb_r_callback(device_t &device, _Object object) { return downcast<lh5810_device &>(device).m_portb_r_cb.set_callback(object); }
	template<class _Object> static devcb2_base &set_portb_w_callback(device_t &device, _Object object) { return downcast<lh5810_device &>(device).m_portb_w_cb.set_callback(object); }
	template<class _Object> static devcb2_base &set_portc_w_callback(device_t &device, _Object object) { return downcast<lh5810_device &>(device).m_portc_w_cb.set_callback(object); }
	template<class _Object> static devcb2_base &set_out_int_callback(device_t &device, _Object object) { return downcast<lh5810_device &>(device).m_out_int_cb.set_callback(object); }
	
	DECLARE_READ8_MEMBER( data_r );
	DECLARE_WRITE8_MEMBER( data_w );

protected:
	// device-level overrides
	virtual void device_start();
	virtual void device_reset();

private:

	devcb2_read8         m_porta_r_cb;       //port A read
	devcb2_write8        m_porta_w_cb;       //port A write
	devcb2_read8         m_portb_r_cb;       //port B read
	devcb2_write8        m_portb_w_cb;       //port B write
	devcb2_write8        m_portc_w_cb;       //port C write

	devcb2_write_line    m_out_int_cb;       //IRQ callback

	UINT8 m_reg[0x10];
	UINT8 m_irq;
};


// device type definition
extern const device_type LH5810;

#endif
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792
/***************************************************************************

    TMS34010: Portable Texas Instruments TMS34010 emulator

    Copyright Alex Pasadyn/Zsolt Vasvari
    Parts based on code by Aaron Giles

***************************************************************************/

#include "emu.h"
#include "debugger.h"
#include "tms34010.h"


/***************************************************************************
    DEBUG STATE & STRUCTURES
***************************************************************************/

#define VERBOSE 			0
#define LOG_CONTROL_REGS	0
#define LOG_GRAPHICS_OPS	0

#define LOG(x)	do { if (VERBOSE) logerror x; } while (0)



/***************************************************************************
    CORE STATE
***************************************************************************/

/* TMS34010 State */
typedef struct _XY XY;
struct _XY
{
#ifdef LSB_FIRST
	INT16 x;
	INT16 y;
#else
	INT16 y;
	INT16 x;
#endif
};

typedef struct _tms34010_state tms34010_state;
struct _tms34010_state
{
	UINT32				pc;
	UINT32				ppc;
	UINT32				st;
	void (*pixel_write)(tms34010_state *tms, offs_t offset, UINT32 data);
	UINT32 (*pixel_read)(tms34010_state *tms, offs_t offset);
	UINT32 (*raster_op)(tms34010_state *tms, UINT32 newpix, UINT32 oldpix);
	UINT32				convsp;
	UINT32				convdp;
	UINT32				convmp;
	UINT16 *			shiftreg;
	INT32				gfxcycles;
	UINT8				pixelshift;
	UINT8				is_34020;
	UINT8				reset_deferred;
	UINT8				hblank_stable;
	UINT8				external_host_access;
	UINT8				executing;
	device_irq_callback	irq_callback;
	legacy_cpu_device *device;
	address_space *program;
	direct_read_data *direct;
	const tms34010_config *config;
	screen_device *screen;
	emu_timer *			scantimer;
	int					icount;

	/* A registers 0-15 map to regs[0]-regs[15] */
	/* B registers 0-15 map to regs[30]-regs[15] */
	union
	{
		INT32 reg;
		XY xy;
	} regs[31];

	UINT16 IOregs[64];
};

INLINE tms34010_state *get_safe_token(device_t *device)
{
	assert(device != NULL);
	assert(device->type() == TMS34010 ||
		   device->type() == TMS34020);
	return (tms34010_state *)downcast<legacy_cpu_device *>(device)->token();
}

#include "34010ops.h"



/***************************************************************************
    GLOBAL VARIABLES
***************************************************************************/

/* default configuration */
static const tms34010_config default_config =
{
	0
};

static void check_interrupt(tms34010_state *tms);
static TIMER_CALLBACK( scanline_callback );
static STATE_POSTLOAD( tms34010_state_postload );



/***************************************************************************
    MACROS
***************************************************************************/

/* status register definitions */
#define STBIT_N			(1 << 31)
#define STBIT_C			(1 << 30)
#define STBIT_Z			(1 << 29)
#define STBIT_V			(1 << 28)
#define STBIT_P			(1 << 25)
#define STBIT_IE		(1 << 21)
#define STBIT_FE1		(1 << 11)
#define STBITS_F1		(0x1f << 6)
#define STBIT_FE0		(1 << 5)
#define STBITS_F0		(0x1f << 0)

/* register definitions and shortcuts */
#define N_FLAG(T)		((T)->st & STBIT_N)
#define Z_FLAG(T)		((T)->st & STBIT_Z)
#define C_FLAG(T)		((T)->st & STBIT_C)
#define V_FLAG(T)		((T)->st & STBIT_V)
#define P_FLAG(T)		((T)->st & STBIT_P)
#define IE_FLAG(T)		((T)->st & STBIT_IE)
#define FE0_FLAG(T)		((T)->st & STBIT_FE0)
#define FE1_FLAG(T)		((T)->st & STBIT_FE1)

/* register file access */
#define AREG(T,i)		((T)->regs[i].reg)
#define AREG_XY(T,i)	((T)->regs[i].xy)
#define AREG_X(T,i)		((T)->regs[i].xy.x)
#define AREG_Y(T,i)		((T)->regs[i].xy.y)
#define BREG(T,i)		((T)->regs[30 - (i)].reg)
#define BREG_XY(T,i)	((T)->regs[30 - (i)].xy)
#define BREG_X(T,i)		((T)->regs[30 - (i)].xy.x)
#define BREG_Y(T,i)		((T)->regs[30 - (i)].xy.y)
#define SP(T)			AREG(T,15)
#define FW(T,i)			(((T)->st >> (i ? 6 : 0)) & 0x1f)
#define FWEX(T,i)		(((T)->st >> (i ? 6 : 0)) & 0x3f)

/* opcode decode helpers */
#define SRCREG(O)		(((O) >> 5) & 0x0f)
#define DSTREG(O)		((O) & 0x0f)
#define SKIP_WORD(T)	((T)->pc += (2 << 3))
#define SKIP_LONG(T)	((T)->pc += (4 << 3))
#define PARAM_K(O)		(((O) >> 5) & 0x1f)
#define PARAM_N(O)		((O) & 0x1f)
#define PARAM_REL8(O)	((INT8)(O))

/* memory I/O */
#define WFIELD0(T,a,b)	(*tms34010_wfield_functions[FW(T,0)])(T,a,b)
#define WFIELD1(T,a,b)	(*tms34010_wfield_functions[FW(T,1)])(T,a,b)
#define RFIELD0(T,a)	(*tms34010_rfield_functions[FWEX(T,0)])(T,a)
#define RFIELD1(T,a)	(*tms34010_rfield_functions[FWEX(T,1)])(T,a)
#define WPIXEL(T,a,b)	(*(T)->pixel_write)(T,a,b)
#define RPIXEL(T,a)		(*(T)->pixel_read)(T,a)

/* Implied Operands */
#define SADDR(T)		BREG(T,0)
#define SADDR_X(T)		BREG_X(T,0)
#define SADDR_Y(T)		BREG_Y(T,0)
#define SADDR_XY(T)		BREG_XY(T,0)
#define SPTCH(T)		BREG(T,1)
#define DADDR(T)		BREG(T,2)
#define DADDR_X(T)		BREG_X(T,2)
#define DADDR_Y(T)		BREG_Y(T,2)
#define DADDR_XY(T)		BREG_XY(T,2)
#define DPTCH(T)		BREG(T,3)
#define OFFSET(T)		BREG(T,4)
#define WSTART_X(T)		BREG_X(T,5)
#define WSTART_Y(T)		BREG_Y(T,5)
#define WEND_X(T)		BREG_X(T,6)
#define WEND_Y(T)		BREG_Y(T,6)
#define DYDX_X(T)		BREG_X(T,7)
#define DYDX_Y(T)		BREG_Y(T,7)
#define COLOR0(T)		BREG(T,8)
#define COLOR1(T)		BREG(T,9)
#define COUNT(T)		BREG(T,10)
#define INC1_X(T)		BREG_X(T,11)
#define INC1_Y(T)		BREG_Y(T,11)
#define INC2_X(T)		BREG_X(T,12)
#define INC2_Y(T)		BREG_Y(T,12)
#define PATTRN(T)		BREG(T,13)
#define TEMP(T)			BREG(T,14)

/* I/O registers */
#define WINDOW_CHECKING(T)	((IOREG(T, REG_CONTROL) >> 6) & 0x03)



/***************************************************************************
    INLINE SHORTCUTS
***************************************************************************/

/* Break up Status Register into indiviual flags */
INLINE void SET_ST(tms34010_state *tms, UINT32 st)
{
	tms->st = st;
	/* interrupts might have been enabled, check it */
	check_interrupt(tms);
}

/* Intialize Status to 0x0010 */
INLINE void RESET_ST(tms34010_state *tms)
{
	SET_ST(tms, 0x00000010);
}

/* shortcuts for reading opcodes */
INLINE UINT32 ROPCODE(tms34010_state *tms)
{
	UINT32 pc = TOBYTE(tms->pc);
	tms->pc += 2 << 3;
	return tms->direct->read_decrypted_word(pc);
}

INLINE INT16 PARAM_WORD(tms34010_state *tms)
{
	UINT32 pc = TOBYTE(tms->pc);
	tms->pc += 2 << 3;
	return tms->direct->read_raw_word(pc);
}

INLINE INT32 PARAM_LONG(tms34010_state *tms)
{
	UINT32 pc = TOBYTE(tms->pc);
	tms->pc += 4 << 3;
	return (UINT16)tms->direct->read_raw_word(pc) | (tms->direct->read_raw_word(pc + 2) << 16);
}

INLINE INT16 PARAM_WORD_NO_INC(tms34010_state *tms)
{
	return tms->direct->read_raw_word(TOBYTE(tms->pc));
}

INLINE INT32 PARAM_LONG_NO_INC(tms34010_state *tms)
{
	UINT32 pc = TOBYTE(tms->pc);
	return (UINT16)tms->direct->read_raw_word(pc) | (tms->direct->read_raw_word(pc + 2) << 16);
}

/* read memory byte */
INLINE UINT32 RBYTE(tms34010_state *tms, offs_t offset)
{
	UINT32 ret;
	RFIELDMAC_8(tms);
	return ret;
}

/* write memory byte */
INLINE void WBYTE(tms34010_state *tms, offs_t offset, UINT32 data)
{
	WFIELDMAC_8(tms);
}

/* read memory long */
INLINE UINT32 RLONG(tms34010_state *tms, offs_t offset)
{
	RFIELDMAC_32(tms);
}

/* write memory long */
INLINE void WLONG(tms34010_state *tms, offs_t offset, UINT32 data)
{
	WFIELDMAC_32(tms);
}

/* pushes/pops a value from the stack */
INLINE void PUSH(tms34010_state *tms, UINT32 data)
{
	SP(tms) -= 0x20;
	WLONG(tms, SP(tms), data);
}

INLINE INT32 POP(tms34010_state *tms)
{
	INT32 ret = RLONG(tms, SP(tms));
	SP(tms) += 0x20;
	return ret;
}



/***************************************************************************
    PIXEL READS
***************************************************************************/

#define RP(T,m1,m2) 											\
	/* TODO: Plane masking */								\
	return (TMS34010_RDMEM_WORD(T, TOBYTE(offset & 0xfffffff0)) >> (offset & m1)) & m2;

static UINT32 read_pixel_1(tms34010_state *tms, offs_t offset) { RP(tms,0x0f,0x01) }
static UINT32 read_pixel_2(tms34010_state *tms, offs_t offset) { RP(tms,0x0e,0x03) }
static UINT32 read_pixel_4(tms34010_state *tms, offs_t offset) { RP(tms,0x0c,0x0f) }
static UINT32 read_pixel_8(tms34010_state *tms, offs_t offset) { RP(tms,0x08,0xff) }
static UINT32 read_pixel_16(tms34010_state *tms, offs_t offset)
{
	/* TODO: Plane masking */
	return TMS34010_RDMEM_WORD(tms, TOBYTE(offset & 0xfffffff0));
}
static UINT32 read_pixel_32(tms34010_state *tms, offs_t offset)
{
	/* TODO: Plane masking */
	return TMS34010_RDMEM_DWORD(tms, TOBYTE(offset & 0xffffffe0));
}

/* Shift register read */
static UINT32 read_pixel_shiftreg(tms34010_state *tms, offs_t offset)
{
	if (tms->config->to_shiftreg)
		tms->config->to_shiftreg(tms->program, offset, &tms->shiftreg[0]);
	else
		fatalerror("To ShiftReg function not set. PC = %08X\n", tms->pc);
	return tms->shiftreg[0];
}



/***************************************************************************
    PIXEL WRITES
***************************************************************************/

/* No Raster Op + No Transparency */
#define WP(T,m1,m2) 																		\
	UINT32 a = TOBYTE(offset & 0xfffffff0);													\
	UINT32 pix = TMS34010_RDMEM_WORD(T,a);													\
	UINT32 shiftcount = offset & m1;														\
																							\
	/* TODO: plane masking */																\
	data &= m2;																				\
	pix = (pix & ~(m2 << shiftcount)) | (data << shiftcount);								\
	TMS34010_WRMEM_WORD(T, a, pix);															\

/* No Raster Op + Transparency */
#define WP_T(T,m1,m2)																		\
	/* TODO: plane masking */																\
	data &= m2;																				\
	if (data)																				\
	{																						\
		UINT32 a = TOBYTE(offset & 0xfffffff0);												\
		UINT32 pix = TMS34010_RDMEM_WORD(T,a);												\
		UINT32 shiftcount = offset & m1;													\
																							\
		/* TODO: plane masking */															\
		pix = (pix & ~(m2 << shiftcount)) | (data << shiftcount);							\
		TMS34010_WRMEM_WORD(T, a, pix);														\
	}																						\

/* Raster Op + No Transparency */
#define WP_R(T,m1,m2)																		\
	UINT32 a = TOBYTE(offset & 0xfffffff0);													\
	UINT32 pix = TMS34010_RDMEM_WORD(T,a);													\
	UINT32 shiftcount = offset & m1;														\
																							\
	/* TODO: plane masking */																\
	data = (*(T)->raster_op)(tms, data & m2, (pix >> shiftcount) & m2) & m2;				\
	pix = (pix & ~(m2 << shiftcount)) | (data << shiftcount);								\
	TMS34010_WRMEM_WORD(T, a, pix);															\

/* Raster Op + Transparency */
#define WP_R_T(T,m1,m2) 																	\
	UINT32 a = TOBYTE(offset & 0xfffffff0);													\
	UINT32 pix = TMS34010_RDMEM_WORD(T,a);													\
	UINT32 shiftcount = offset & m1;														\
																							\
	/* TODO: plane masking */																\
	data = (*(T)->raster_op)(tms, data & m2, (pix >> shiftcount) & m2) & m2;				\
	if (data)																				\
	{																						\
		pix = (pix & ~(m2 << shiftcount)) | (data << shiftcount);							\
		TMS34010_WRMEM_WORD(T, a, pix);														\
	}																						\


/* No Raster Op + No Transparency */
static void write_pixel_1(tms34010_state *tms, offs_t offset, UINT32 data) { WP(tms, 0x0f, 0x01); }
static void write_pixel_2(tms34010_state *tms, offs_t offset, UINT32 data) { WP(tms, 0x0e, 0x03); }
static void write_pixel_4(tms34010_state *tms, offs_t offset, UINT32 data) { WP(tms, 0x0c, 0x0f); }
static void write_pixel_8(tms34010_state *tms, offs_t offset, UINT32 data) { WP(tms, 0x08, 0xff); }
static void write_pixel_16(tms34010_state *tms, offs_t offset, UINT32 data)
{
	/* TODO: plane masking */
	TMS34010_WRMEM_WORD(tms, TOBYTE(offset & 0xfffffff0), data);
}
static void write_pixel_32(tms34010_state *tms, offs_t offset, UINT32 data)
{
	/* TODO: plane masking */
	TMS34010_WRMEM_WORD(tms, TOBYTE(offset & 0xffffffe0), data);
}

/* No Raster Op + Transparency */
static void write_pixel_t_1(tms34010_state *tms, offs_t offset, UINT32 data) { WP_T(tms, 0x0f, 0x01); }
static void write_pixel_t_2(tms34010_state *tms, offs_t offset, UINT32 data) { WP_T(tms, 0x0e, 0x03); }
static void write_pixel_t_4(tms34010_state *tms, offs_t offset, UINT32 data) { WP_T(tms, 0x0c, 0x0f); }
static void write_pixel_t_8(tms34010_state *tms, offs_t offset, UINT32 data) { WP_T(tms, 0x08, 0xff); }
static void write_pixel_t_16(tms34010_state *tms, offs_t offset, UINT32 data)
{
	/* TODO: plane masking */
	if (data)
		TMS34010_WRMEM_WORD(tms, TOBYTE(offset & 0xfffffff0), data);
}
static void write_pixel_t_32(tms34010_state *tms, offs_t offset, UINT32 data)
{
	/* TODO: plane masking */
	if (data)
		TMS34010_WRMEM_DWORD(tms, TOBYTE(offset & 0xffffffe0), data);
}

/* Raster Op + No Transparency */
static void write_pixel_r_1(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R(tms, 0x0f, 0x01); }
static void write_pixel_r_2(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R(tms, 0x0e, 0x03); }
static void write_pixel_r_4(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R(tms, 0x0c, 0x0f); }
static void write_pixel_r_8(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R(tms, 0x08, 0xff); }
static void write_pixel_r_16(tms34010_state *tms, offs_t offset, UINT32 data)
{
	/* TODO: plane masking */
	UINT32 a = TOBYTE(offset & 0xfffffff0);
	TMS34010_WRMEM_WORD(tms, a, (*tms->raster_op)(tms, data, TMS34010_RDMEM_WORD(tms, a)));
}
static void write_pixel_r_32(tms34010_state *tms, offs_t offset, UINT32 data)
{
	/* TODO: plane masking */
	UINT32 a = TOBYTE(offset & 0xffffffe0);
	TMS34010_WRMEM_DWORD(tms, a, (*tms->raster_op)(tms, data, TMS34010_RDMEM_DWORD(tms, a)));
}

/* Raster Op + Transparency */
static void write_pixel_r_t_1(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R_T(tms, 0x0f,0x01); }
static void write_pixel_r_t_2(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R_T(tms, 0x0e,0x03); }
static void write_pixel_r_t_4(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R_T(tms, 0x0c,0x0f); }
static void write_pixel_r_t_8(tms34010_state *tms, offs_t offset, UINT32 data) { WP_R_T(tms, 0x08,0xff); }
static void write_pixel_r_t_16(tms34010_state *tms, offs_t offset, UINT32 data)
{
	/* TODO: plane masking */
	UINT32 a = TOBYTE(offset & 0xfffffff0);
	data = (*tms->raster_op)(tms, data, TMS34010_RDMEM_WORD(tms, a));

	if (data)
		TMS34010_WRMEM_WORD(tms, a, data);
}
static void write_pixel_r_t_32(tms34010_state *tms, offs_t offset, UINT32 data)
{
	/* TODO: plane masking */
	UINT32 a = TOBYTE(offset & 0xffffffe0);
	data = (*tms->raster_op)(tms, data, TMS34010_RDMEM_DWORD(tms, a));

	if (data)
		TMS34010_WRMEM_DWORD(tms, a, data);
}

/* Shift register write */
static void write_pixel_shiftreg(tms34010_state *tms, offs_t offset, UINT32 data)
{
	if (tms->config->from_shiftreg)
		tms->config->from_shiftreg(tms->program, offset, &tms->shiftreg[0]);
	else
		fatalerror("From ShiftReg function not set. PC = %08X\n", tms->pc);
}



/***************************************************************************
    RASTER OPS
***************************************************************************/

/* Raster operations */
static UINT32 raster_op_1(tms34010_state *tms, UINT32 newpix, UINT32 oldpix)  { return newpix & oldpix; }
static UINT32 raster_op_2(tms34010_state *tms, UINT32 newpix, UINT32 oldpix)  { return newpix & ~oldpix; }
static UINT32 raster_op_3(tms34010_state *tms, UINT32 newpix, UINT32 oldpix)  { return 0; }
static UINT32 raster_op_4(tms34010_state *tms, UINT32 newpix, UINT32 oldpix)  { return newpix | ~oldpix; }
static UINT32 raster_op_5(tms34010_state *tms, UINT32 newpix, UINT32 oldpix)  { return ~(newpix ^ oldpix); }
static UINT32 raster_op_6(tms34010_state *tms, UINT32 newpix, UINT32 oldpix)  { return ~oldpix; }
static UINT32 raster_op_7(tms34010_state *tms, UINT32 newpix, UINT32 oldpix)  { return ~(newpix | oldpix); }
static UINT32 raster_op_8(tms34010_state *tms, UINT32 newpix, UINT32 oldpix)  { return newpix | oldpix; }
static UINT32 raster_op_9(tms34010_state *tms, UINT32 newpix, UINT32 oldpix)  { return oldpix; }
static UINT32 raster_op_10(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return newpix ^ oldpix; }
static UINT32 raster_op_11(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return ~newpix & oldpix; }
static UINT32 raster_op_12(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return 0xffff; }
static UINT32 raster_op_13(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return ~newpix | oldpix; }
static UINT32 raster_op_14(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return ~(newpix & oldpix); }
static UINT32 raster_op_15(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return ~newpix; }
static UINT32 raster_op_16(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return newpix + oldpix; }
static UINT32 raster_op_17(tms34010_state *tms, UINT32 newpix, UINT32 oldpix)
{
	UINT32 max = (UINT32)0xffffffff >> (32 - IOREG(tms, REG_PSIZE));
	UINT32 res = newpix + oldpix;
	return (res > max) ? max : res;
}
static UINT32 raster_op_18(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return oldpix - newpix; }
static UINT32 raster_op_19(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return (oldpix > newpix) ? oldpix - newpix : 0; }
static UINT32 raster_op_20(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return (oldpix > newpix) ? oldpix : newpix; }
static UINT32 raster_op_21(tms34010_state *tms, UINT32 newpix, UINT32 oldpix) { return (oldpix > newpix) ? newpix : oldpix; }



/***************************************************************************
    OPCODE TABLE & IMPLEMENTATIONS
***************************************************************************/

#include "34010fld.c"

/* includes the static function prototypes and the master opcode table */
#include "34010tbl.c"

/* includes the actual opcode implementations */
#include "34010ops.c"
#include "34010gfx.c"



/***************************************************************************
    Internal interrupt check
****************************************************************************/

/* Generate pending interrupts. */
static void check_interrupt(tms34010_state *tms)
{
	int vector = 0;
	int irqline = -1;
	int irq;

	/* if we're not actively executing, skip it */
	if (!tms->executing)
		return;

	/* check for NMI first */
	if (IOREG(tms, REG_HSTCTLH) & 0x0100)
	{
		LOG(("TMS34010 '%s' takes NMI\n", tms->device->tag()));

		/* ack the NMI */
		IOREG(tms, REG_HSTCTLH) &= ~0x0100;

		/* handle NMI mode bit */
		if (!(IOREG(tms, REG_HSTCTLH) & 0x0200))
		{
			PUSH(tms, tms->pc);
			PUSH(tms, tms->st);
		}

		/* leap to the vector */
		RESET_ST(tms);
		tms->pc = RLONG(tms, 0xfffffee0);
		COUNT_CYCLES(tms,16);
		return;
	}

	/* early out if everything else is disabled */
	irq = IOREG(tms, REG_INTPEND) & IOREG(tms, REG_INTENB);
	if (!IE_FLAG(tms) || !irq)
		return;

	/* host interrupt */
	if (irq & TMS34010_HI)
	{
		LOG(("TMS34010 '%s' takes HI\n", tms->device->tag()));
		vector = 0xfffffec0;
	}

	/* display interrupt */
	else if (irq & TMS34010_DI)
	{
		LOG(("TMS34010 '%s' takes DI\n", tms->device->tag()));
		vector = 0xfffffea0;
	}

	/* window violation interrupt */
	else if (irq & TMS34010_WV)
	{
		LOG(("TMS34010 '%s' takes WV\n", tms->device->tag()));
		vector = 0xfffffe80;
	}

	/* external 1 interrupt */
	else if (irq & TMS34010_INT1)
	{
		LOG(("TMS34010 '%s' takes INT1\n", tms->device->tag()));
		vector = 0xffffffc0;
		irqline = 0;
	}

	/* external 2 interrupt */
	else if (irq & TMS34010_INT2)
	{
		LOG(("TMS34010 '%s' takes INT2\n", tms->device->tag()));
		vector = 0xffffffa0;
		irqline = 1;
	}

	/* if we took something, generate it */
	if (vector)
	{
		PUSH(tms, tms->pc);
		PUSH(tms, tms->st);
		RESET_ST(tms);
		tms->pc = RLONG(tms, vector);
		COUNT_CYCLES(tms,16);

		/* call the callback for externals */
		if (irqline >= 0)
			(void)(*tms->irq_callback)(tms->device, irqline);
	}
}



/***************************************************************************
    Reset the CPU emulation
***************************************************************************/

static CPU_INIT( tms34010 )
{
	const tms34010_config *configdata = device->baseconfig().static_config() ? (const tms34010_config *)device->baseconfig().static_config() : &default_config;
	tms34010_state *tms = get_safe_token(device);

	tms->external_host_access = FALSE;

	tms->config = configdata;
	tms->irq_callback = irqcallback;
	tms->device = device;
	tms->program = device->space(AS_PROGRAM);
	tms->direct = &tms->program->direct();
	tms->screen = downcast<screen_device *>(device->machine().device(configdata->screen_tag));

	/* set up the state table */
	{
		device_state_interface *state;
		device->interface(state);
		state->state_add(TMS34010_PC,     "PC",        tms->pc);
		state->state_add(STATE_GENPC,     "GENPC",     tms->pc).noshow();
		state->state_add(STATE_GENPCBASE, "GENPCBASE", tms->ppc).noshow();
		state->state_add(TMS34010_SP,     "SP",        tms->regs[15].reg);
		state->state_add(STATE_GENSP,     "GENSP",     tms->regs[15].reg).noshow();
		state->state_add(TMS34010_ST,     "ST",        tms->st);
		state->state_add(STATE_GENFLAGS,  "GENFLAGS",  tms->st).noshow().formatstr("%18s");

		astring tempstr;
		for (int regnum = 0; regnum < 15; regnum++)
			state->state_add(TMS34010_A0 + regnum, tempstr.format("A%d", regnum), tms->regs[regnum].reg);
		for (int regnum = 0; regnum < 15; regnum++)
			state->state_add(TMS34010_B0 + regnum, tempstr.format("B%d", regnum), tms->regs[30 - regnum].reg);
	}

	/* allocate a scanline timer and set it to go off at the start */
	tms->scantimer = device->machine().scheduler().timer_alloc(FUNC(scanline_callback), tms);
	tms->scantimer->adjust(attotime::zero);

	/* allocate the shiftreg */
	tms->shiftreg = auto_alloc_array(device->machine(), UINT16, SHIFTREG_SIZE/2);

	device->save_item(NAME(tms->pc));
	device->save_item(NAME(tms->st));
	device->save_item(NAME(tms->reset_deferred));
	device->save_pointer(NAME(tms->shiftreg), SHIFTREG_SIZE / 2);
	device->save_item(NAME(tms->IOregs));
	device->save_item(NAME(tms->convsp));
	device->save_item(NAME(tms->convdp));
	device->save_item(NAME(tms->convmp));
	device->save_item(NAME(tms->pixelshift));
	device->save_item(NAME(tms->gfxcycles));
	device->save_pointer(NAME(&tms->regs[0].reg), ARRAY_LENGTH(tms->regs));
	device->machine().state().register_postload(tms34010_state_postload, tms);
}

static CPU_RESET( tms34010 )
{
	/* zap the state and copy in the config pointer */
	tms34010_state *tms = get_safe_token(device);
	const tms34010_config *config = tms->config;
	screen_device *screen = tms->screen;
	UINT16 *shiftreg = tms->shiftreg;
	device_irq_callback save_irqcallback = tms->irq_callback;
	emu_timer *save_scantimer = tms->scantimer;

	memset(tms, 0, sizeof(*tms));

	tms->config = config;
	tms->screen = screen;
	tms->shiftreg = shiftreg;
	tms->irq_callback = save_irqcallback;
	tms->scantimer = save_scantimer;
	tms->device = device;
	tms->program = device->space(AS_PROGRAM);
	tms->direct = &tms->program->direct();

	/* fetch the initial PC and reset the state */
	tms->pc = RLONG(tms, 0xffffffe0) & 0xfffffff0;
	RESET_ST(tms);

	/* HALT the CPU if requested, and remember to re-read the starting PC */
	/* the first time we are run */
	tms->reset_deferred = tms->config->halt_on_reset;
	if (tms->config->halt_on_reset)
		tms34010_io_register_w(device->space(AS_PROGRAM), REG_HSTCTLH, 0x8000, 0xffff);
}


static CPU_RESET( tms34020 )
{
	tms34010_state *tms = get_safe_token(device);
	CPU_RESET_CALL(tms34010);
	tms->is_34020 = 1;
}



/***************************************************************************
    Shut down the CPU emulation
***************************************************************************/

static CPU_EXIT( tms34010 )
{
	tms34010_state *tms = get_safe_token(device);
	tms->shiftreg = NULL;
}



/***************************************************************************
    Set IRQ line state
***************************************************************************/

static void set_irq_line(tms34010_state *tms, int irqline, int linestate)
{
	LOG(("TMS34010 '%s' set irq line %d state %d\n", tms->device->tag(), irqline, linestate));

	/* set the pending interrupt */
	switch (irqline)
	{
		case 0:
			if (linestate != CLEAR_LINE)
				IOREG(tms, REG_INTPEND) |= TMS34010_INT1;
			else
				IOREG(tms, REG_INTPEND) &= ~TMS34010_INT1;
			break;

		case 1:
			if (linestate != CLEAR_LINE)
				IOREG(tms, REG_INTPEND) |= TMS34010_INT2;
			else
				IOREG(tms, REG_INTPEND) &= ~TMS34010_INT2;
			break;
	}
}



/***************************************************************************
    Generate internal interrupt
***************************************************************************/

static TIMER_CALLBACK( internal_interrupt_callback )
{
	tms34010_state *tms = (tms34010_state *)ptr;
	int type = param;

	/* call through to the CPU to generate the int */
	IOREG(tms, REG_INTPEND) |= type;
	LOG(("TMS34010 '%s' set internal interrupt $%04x\n", tms->device->tag(), type));

	/* generate triggers so that spin loops can key off them */
	device_triggerint(tms->device);
}



/***************************************************************************
    Execute
***************************************************************************/

static CPU_EXECUTE( tms34010 )
{
	tms34010_state *tms = get_safe_token(device);

	/* Get out if CPU is halted. Absolutely no interrupts must be taken!!! */
	if (IOREG(tms, REG_HSTCTLH) & 0x8000)
	{
		tms->icount = 0;
		return;
	}

	/* if the CPU's reset was deferred, do it now */
	if (tms->reset_deferred)
	{
		tms->reset_deferred = 0;
		tms->pc = RLONG(tms, 0xffffffe0);
	}

	/* check interrupts first */
	tms->executing = TRUE;
	check_interrupt(tms);
	if ((tms->device->machine().debug_flags & DEBUG_FLAG_ENABLED) == 0)
	{
		do
		{
			UINT16 op;
			tms->ppc = tms->pc;
			op = ROPCODE(tms);
			(*opcode_table[op >> 4])(tms, op);
		} while (tms->icount > 0);
	}
	else
	{
		do
		{
			UINT16 op;
			debugger_instruction_hook(tms->device, tms->pc);
			tms->ppc = tms->pc;
			op = ROPCODE(tms);
			(*opcode_table[op >> 4])(tms, op);
		} while (tms->icount > 0);
	}
	tms->executing = FALSE;
}



/***************************************************************************
    PIXEL OPS
***************************************************************************/

static void (*const pixel_write_ops[4][6])(tms34010_state *tms, offs_t offset, UINT32 data) =
{
	{ write_pixel_1,     write_pixel_2,     write_pixel_4,     write_pixel_8,     write_pixel_16,     write_pixel_32     },
	{ write_pixel_r_1,   write_pixel_r_2,   write_pixel_r_4,   write_pixel_r_8,   write_pixel_r_16,   write_pixel_r_32   },
	{ write_pixel_t_1,   write_pixel_t_2,   write_pixel_t_4,   write_pixel_t_8,   write_pixel_t_16,	  write_pixel_t_32   },
	{ write_pixel_r_t_1, write_pixel_r_t_2, write_pixel_r_t_4, write_pixel_r_t_8, write_pixel_r_t_16, write_pixel_r_t_32 }
};

static UINT32 (*const pixel_read_ops[6])(tms34010_state *tms, offs_t offset) =
{
	read_pixel_1,        read_pixel_2,      read_pixel_4,      read_pixel_8,      read_pixel_16,      read_pixel_32
};


static void set_pixel_function(tms34010_state *tms)
{
	UINT32 i1,i2;

	if (IOREG(tms, REG_DPYCTL) & 0x0800)
	{
		/* Shift Register Transfer */
		tms->pixel_write = write_pixel_shiftreg;
		tms->pixel_read  = read_pixel_shiftreg;
		return;
	}

	switch (IOREG(tms, REG_PSIZE))
	{
		default:
		case 0x01: i2 = 0; break;
		case 0x02: i2 = 1; break;
		case 0x04: i2 = 2; break;
		case 0x08: i2 = 3; break;
		case 0x10: i2 = 4; break;
		case 0x20: i2 = 5; break;
	}

	if (IOREG(tms, REG_CONTROL) & 0x20)
		i1 = tms->raster_op ? 3 : 2;
	else
		i1 = tms->raster_op ? 1 : 0;

	tms->pixel_write = pixel_write_ops[i1][i2];
	tms->pixel_read  = pixel_read_ops [i2];
}



/***************************************************************************
    RASTER OPS
***************************************************************************/

static UINT32 (*const raster_ops[32]) (tms34010_state *tms, UINT32 newpix, UINT32 oldpix) =
{
	           0, raster_op_1 , raster_op_2 , raster_op_3,
	raster_op_4 , raster_op_5 , raster_op_6 , raster_op_7,
	raster_op_8 , raster_op_9 , raster_op_10, raster_op_11,
	raster_op_12, raster_op_13, raster_op_14, raster_op_15,
	raster_op_16, raster_op_17, raster_op_18, raster_op_19,
	raster_op_20, raster_op_21,            0,            0,
	           0,            0,            0,            0,
	           0,            0,            0,            0,
};


static void set_raster_op(tms34010_state *tms)
{
	tms->raster_op = raster_ops[(IOREG(tms, REG_CONTROL) >> 10) & 0x1f];
}



/***************************************************************************
    VIDEO TIMING HELPERS
***************************************************************************/

static TIMER_CALLBACK( scanline_callback )
{
	tms34010_state *tms = (tms34010_state *)ptr;
	int vsblnk, veblnk, vtotal;
	int vcount = param;
	int enabled;
	int master;

	/* fetch the core timing parameters */
	const rectangle &current_visarea = tms->screen->visible_area();
	enabled = SMART_IOREG(tms, DPYCTL) & 0x8000;
	master = (tms->is_34020 || (SMART_IOREG(tms, DPYCTL) & 0x2000));
	vsblnk = SMART_IOREG(tms, VSBLNK);
	veblnk = SMART_IOREG(tms, VEBLNK);
	vtotal = SMART_IOREG(tms, VTOTAL);
	if (!master)
	{
		vtotal = MIN(tms->screen->height() - 1, vtotal);
		vcount = tms->screen->vpos();
	}

	/* update the VCOUNT */
	SMART_IOREG(tms, VCOUNT) = vcount;

	/* if we match the display interrupt scanline, signal an interrupt */
	if (enabled && vcount == SMART_IOREG(tms, DPYINT))
	{
		/* generate the display interrupt signal */
		internal_interrupt_callback(machine, tms, TMS34010_DI);
	}

	/* at the start of VBLANK, load the starting display address */
	if (vcount == vsblnk)
	{
		/* 34010 loads DPYADR with DPYSTRT, and inverts if the origin is 0 */
		if (!tms->is_34020)
		{
			IOREG(tms, REG_DPYADR) = IOREG(tms, REG_DPYSTRT);
			LOG(("Start of VBLANK, DPYADR = %04X\n", IOREG(tms, REG_DPYADR)));
		}

		/* 34020 loads DPYNXx with DPYSTx */
		else
		{
			IOREG(tms, REG020_DPYNXL) = IOREG(tms, REG020_DPYSTL) & 0xffe0;
			IOREG(tms, REG020_DPYNXH) = IOREG(tms, REG020_DPYSTH);
		}
	}

	/* at the end of the screen, update the display parameters */
	if (vcount == vtotal)
	{
		/* only do this if we have an incoming pixel clock */
		/* also, only do it if the HEBLNK/HSBLNK values are stable */
		if (master && tms->config->scanline_callback != NULL)
		{
			int htotal = SMART_IOREG(tms, HTOTAL);
			if (htotal > 0 && vtotal > 0)
			{
				attoseconds_t refresh = HZ_TO_ATTOSECONDS(tms->config->pixclock) * (htotal + 1) * (vtotal + 1);
				int width = (htotal + 1) * tms->config->pixperclock;
				int height = vtotal + 1;
				rectangle visarea;

				/* extract the visible area */
				visarea.min_x = SMART_IOREG(tms, HEBLNK) * tms->config->pixperclock;
				visarea.max_x = SMART_IOREG(tms, HSBLNK) * tms->config->pixperclock - 1;
				visarea.min_y = veblnk;
				visarea.max_y = vsblnk - 1;

				/* if everything looks good, set the info */
				if (visarea.min_x < visarea.max_x && visarea.max_x <= width && visarea.min_y < visarea.max_y && visarea.max_y <= height)
				{
					/* because many games play with the HEBLNK/HSBLNK for effects, we don't change
                       if they are the only thing that has changed, unless they are stable for a couple
                       of frames */
					int current_width  = tms->screen->width();
					int current_height = tms->screen->height();

					if (width != current_width || height != current_height || visarea.min_y != current_visarea.min_y || visarea.max_y != current_visarea.max_y ||
						(tms->hblank_stable > 2 && (visarea.min_x != current_visarea.min_x || visarea.max_x != current_visarea.max_x)))
					{
						tms->screen->configure(width, height, visarea, refresh);
					}
					tms->hblank_stable++;
				}

				LOG(("Configuring screen: HTOTAL=%3d BLANK=%3d-%3d VTOTAL=%3d BLANK=%3d-%3d refresh=%f\n",
						htotal, SMART_IOREG(tms, HEBLNK), SMART_IOREG(tms, HSBLNK), vtotal, veblnk, vsblnk, ATTOSECONDS_TO_HZ(refresh)));

				/* interlaced timing not supported */
				if ((SMART_IOREG(tms, DPYCTL) & 0x4000) == 0)
					fatalerror("Interlaced video configured on the TMS34010 (unsupported)");
			}
		}
	}

	/* force a partial update within the visible area */
	if (vcount >= current_visarea.min_y && vcount <= current_visarea.max_y && tms->config->scanline_callback != NULL)
		tms->screen->update_partial(vcount);

	/* if we are in the visible area, increment DPYADR by DUDATE */
	if (vcount >= veblnk && vcount < vsblnk)
	{
		/* 34010 increments by the DUDATE field in DPYCTL */
		if (!tms->is_34020)
		{
			UINT16 dpyadr = IOREG(tms, REG_DPYADR);
			if ((dpyadr & 3) == 0)
				dpyadr = ((dpyadr & 0xfffc) - (IOREG(tms, REG_DPYCTL) & 0x03fc)) | (IOREG(tms, REG_DPYSTRT) & 0x0003);
			else
				dpyadr = (dpyadr & 0xfffc) | ((dpyadr - 1) & 3);
			IOREG(tms, REG_DPYADR) = dpyadr;
		}

		/* 34020 updates based on the DINC register, including zoom */
		else
		{
			UINT32 dpynx = IOREG(tms, REG020_DPYNXL) | (IOREG(tms, REG020_DPYNXH) << 16);
			UINT32 dinc = IOREG(tms, REG020_DINCL) | (IOREG(tms, REG020_DINCH) << 16);
			dpynx = (dpynx & 0xffffffe0) | ((dpynx + dinc) & 0x1f);
			if ((dpynx & 0x1f) == 0)
				dpynx += dinc & 0xffffffe0;
			IOREG(tms, REG020_DPYNXL) = dpynx;
			IOREG(tms, REG020_DPYNXH) = dpynx >> 16;
		}
	}

	/* adjust for the next callback */
	vcount++;
	if (vcount > vtotal)
		vcount = 0;

	/* note that we add !master (0 or 1) as a attoseconds value; this makes no practical difference */
	/* but helps ensure that masters are updated first before slaves */
	tms->scantimer->adjust(tms->screen->time_until_pos(vcount) + attotime(0, !master), vcount);
}


void tms34010_get_display_params(device_t *cpu, tms34010_display_params *params)
{
	tms34010_state *tms = get_safe_token(cpu);

	params->enabled = ((SMART_IOREG(tms, DPYCTL) & 0x8000) != 0);
	params->vcount = SMART_IOREG(tms, VCOUNT);
	params->veblnk = SMART_IOREG(tms, VEBLNK);
	params->vsblnk = SMART_IOREG(tms, VSBLNK);
	params->heblnk = SMART_IOREG(tms, HEBLNK) * tms->config->pixperclock;
	params->hsblnk = SMART_IOREG(tms, HSBLNK) * tms->config->pixperclock;

	/* 34010 gets its address from DPYADR and DPYTAP */
	if (!tms->is_34020)
	{
		UINT16 dpyadr = IOREG(tms, REG_DPYADR);
		if (!(IOREG(tms, REG_DPYCTL) & 0x0400))
			dpyadr ^= 0xfffc;
		params->rowaddr = dpyadr >> 4;
		params->coladdr = ((dpyadr & 0x007c) << 4) | (IOREG(tms, REG_DPYTAP) & 0x3fff);
		params->yoffset = (IOREG(tms, REG_DPYSTRT) - IOREG(tms, REG_DPYADR)) & 3;
	}

	/* 34020 gets its address from DPYNX */
	else
	{
		params->rowaddr = IOREG(tms, REG020_DPYNXH);
		params->coladdr = IOREG(tms, REG020_DPYNXL) & 0xffe0;
		params->yoffset = 0;
		if ((IOREG(tms, REG020_DINCL) & 0x1f) != 0)
			params->yoffset = (IOREG(tms, REG020_DPYNXL) & 0x1f) / (IOREG(tms, REG020_DINCL) & 0x1f);
	}
}


SCREEN_UPDATE( tms340x0 )
{
	pen_t blackpen = get_black_pen(screen->machine());
	tms34010_display_params params;
	tms34010_state *tms = NULL;
	device_t *cpu;
	int x;

	/* find the owning CPU */
	for (cpu = screen->machine().m_devicelist.first(); cpu != NULL; cpu = cpu->next())
	{
		device_type type = cpu->type();
		if (type == TMS34010 || type == TMS34020)
		{
			tms = get_safe_token(cpu);
			if (tms->config != NULL && tms->config->scanline_callback != NULL && tms->screen == screen)
				break;
			tms = NULL;
		}
	}
	if (tms == NULL)
		fatalerror("Unable to locate matching CPU for screen '%s'\n", screen->tag());

	/* get the display parameters for the screen */
	tms34010_get_display_params(tms->device, &params);

	/* if the display is enabled, call the scanline callback */
	if (params.enabled)
	{
		/* call through to the callback */
		LOG(("  Update: scan=%3d ROW=%04X COL=%04X\n", cliprect->min_y, params.rowaddr, params.coladdr));
		(*tms->config->scanline_callback)(*screen, bitmap, cliprect->min_y, &params);
	}

	/* otherwise, just blank the current scanline */
	else
		params.heblnk = params.hsblnk = cliprect->max_x + 1;

	/* blank out the blank regions */
	if (bitmap->bpp == 16)
	{
		UINT16 *dest = BITMAP_ADDR16(bitmap, cliprect->min_y, 0);
		for (x = cliprect->min_x; x < params.heblnk; x++)
			dest[x] = blackpen;
		for (x = params.hsblnk; x <= cliprect->max_y; x++)
			dest[x] = blackpen;
	}
	else if (bitmap->bpp == 32)
	{
		UINT32 *dest = BITMAP_ADDR32(bitmap, cliprect->min_y, 0);
		for (x = cliprect->min_x; x < params.heblnk; x++)
			dest[x] = blackpen;
		for (x = params.hsblnk; x <= cliprect->max_y; x++)
			dest[x] = blackpen;
	}
	return 0;
}


/***************************************************************************
    I/O REGISTER WRITES
***************************************************************************/

static const char *const ioreg_name[] =
{
	"HESYNC", "HEBLNK", "HSBLNK", "HTOTAL",
	"VESYNC", "VEBLNK", "VSBLNK", "VTOTAL",
	"DPYCTL", "DPYSTART", "DPYINT", "CONTROL",
	"HSTDATA", "HSTADRL", "HSTADRH", "HSTCTLL",

	"HSTCTLH", "INTENB", "INTPEND", "CONVSP",
	"CONVDP", "PSIZE", "PMASK", "RESERVED",
	"RESERVED", "RESERVED", "RESERVED", "DPYTAP",
	"HCOUNT", "VCOUNT", "DPYADR", "REFCNT"
};

WRITE16_HANDLER( tms34010_io_register_w )
{
	tms34010_state *tms = get_safe_token(&space->device());
	int oldreg, newreg;

	/* Set register */
	oldreg = IOREG(tms, offset);
	IOREG(tms, offset) = data;

	switch (offset)
	{
		case REG_CONTROL:
			set_raster_op(tms);
			set_pixel_function(tms);
			break;

		case REG_PSIZE:
			set_pixel_function(tms);

			switch (data)
			{
				default:
				case 0x01: tms->pixelshift = 0; break;
				case 0x02: tms->pixelshift = 1; break;
				case 0x04: tms->pixelshift = 2; break;
				case 0x08: tms->pixelshift = 3; break;
				case 0x10: tms->pixelshift = 4; break;
			}
			break;

		case REG_PMASK:
			if (data) logerror("Plane masking not supported. PC=%08X\n", cpu_get_pc(&space->device()));
			break;

		case REG_DPYCTL:
			set_pixel_function(tms);
			break;

		case REG_HSTCTLH:
			/* if the CPU is halting itself, stop execution right away */
			if ((data & 0x8000) && !tms->external_host_access)
				tms->icount = 0;
			device_set_input_line(tms->device, INPUT_LINE_HALT, (data & 0x8000) ? ASSERT_LINE : CLEAR_LINE);

			/* NMI issued? */
			if (data & 0x0100)
				tms->device->machine().scheduler().synchronize(FUNC(internal_interrupt_callback), 0, tms);
			break;

		case REG_HSTCTLL:
			/* the TMS34010 can change MSGOUT, can set INTOUT, and can clear INTIN */
			if (!tms->external_host_access)
			{
				newreg = (oldreg & 0xff8f) | (data & 0x0070);
				newreg |= data & 0x0080;
				newreg &= data | ~0x0008;
			}

			/* the host can change MSGIN, can set INTIN, and can clear INTOUT */
			else
			{
				newreg = (oldreg & 0xfff8) | (data & 0x0007);
				newreg &= data | ~0x0080;
				newreg |= data & 0x0008;
			}
			IOREG(tms, offset) = newreg;

			/* the TMS34010 can set output interrupt? */
			if (!(oldreg & 0x0080) && (newreg & 0x0080))
			{
				if (tms->config->output_int)
					(*tms->config->output_int)(&space->device(), 1);
			}
			else if ((oldreg & 0x0080) && !(newreg & 0x0080))
			{
				if (tms->config->output_int)
					(*tms->config->output_int)(&space->device(), 0);
			}

			/* input interrupt? (should really be state-based, but the functions don't exist!) */
			if (!(oldreg & 0x0008) && (newreg & 0x0008))
				tms->device->machine().scheduler().synchronize(FUNC(internal_interrupt_callback), TMS34010_HI, tms);
			else if ((oldreg & 0x0008) && !(newreg & 0x0008))
				IOREG(tms, REG_INTPEND) &= ~TMS34010_HI;
			break;

		case REG_CONVSP:
			tms->convsp = 1 << (~data & 0x1f);
			break;

		case REG_CONVDP:
			tms->convdp = 1 << (~data & 0x1f);
			break;

		case REG_INTENB:
			check_interrupt(tms);
			break;

		case REG_INTPEND:
			/* X1P, X2P and HIP are read-only */
			/* WVP and DIP can only have 0's written to them */
			IOREG(tms, REG_INTPEND) = oldreg;
			if (!(data & TMS34010_WV))
				IOREG(tms, REG_INTPEND) &= ~TMS34010_WV;
			if (!(data & TMS34010_DI))
				IOREG(tms, REG_INTPEND) &= ~TMS34010_DI;
			break;

		case REG_HEBLNK:
		case REG_HSBLNK:
			if (oldreg != data)
				tms->hblank_stable = 0;
			break;
	}

//  if (LOG_CONTROL_REGS)
//      logerror("%s: %s = %04X (%d)\n", tms->device->machine().describe_context(), ioreg_name[offset], IOREG(tms, offset), tms->screen->vpos());
}


static const char *const ioreg020_name[] =
{
	"VESYNC", "HESYNC", "VEBLNK", "HEBLNK",
	"VSBLNK", "HSBLNK", "VTOTAL", "HTOTAL",
	"DPYCTL", "DPYSTRT", "DPYINT", "CONTROL",
	"HSTDATA", "HSTADRL", "HSTADRH", "HSTCTLL",

	"HSTCTLH", "INTENB", "INTPEND", "CONVSP",
	"CONVDP", "PSIZE", "PMASKL", "PMASKH",
	"CONVMP", "CONTROL2", "CONFIG", "DPYTAP",
	"VCOUNT", "HCOUNT", "DPYADR", "REFADR",

	"DPYSTL", "DPYSTH", "DPYNXL", "DPYNXH",
	"DINCL", "DINCH", "RES0", "HESERR",
	"RES1", "RES2", "RES3", "RES4",
	"SCOUNT", "BSFLTST", "DPYMSK", "RES5",

	"SETVCNT", "SETHCNT", "BSFLTDL", "BSFLTDH",
	"RES6", "RES7", "RES8", "RES9",
	"IHOST1L", "IHOST1H", "IHOST2L", "IHOST2H",
	"IHOST3L", "IHOST3H", "IHOST4L", "IHOST4H"
};

WRITE16_HANDLER( tms34020_io_register_w )
{
	tms34010_state *tms = get_safe_token(&space->device());
	int oldreg, newreg;

	/* Set register */
	oldreg = IOREG(tms, offset);
	IOREG(tms, offset) = data;

//  if (LOG_CONTROL_REGS)
//      logerror("%s: %s = %04X (%d)\n", device->machine().describe_context(), ioreg020_name[offset], IOREG(tms, offset), tms->screen->vpos());

	switch (offset)
	{
		case REG020_CONTROL:
		case REG020_CONTROL2:
			IOREG(tms, REG020_CONTROL) = data;
			IOREG(tms, REG020_CONTROL2) = data;
			set_raster_op(tms);
			set_pixel_function(tms);
			break;

		case REG020_PSIZE:
			set_pixel_function(tms);

			switch (data)
			{
				default:
				case 0x01: tms->pixelshift = 0; break;
				case 0x02: tms->pixelshift = 1; break;
				case 0x04: tms->pixelshift = 2; break;
				case 0x08: tms->pixelshift = 3; break;
				case 0x10: tms->pixelshift = 4; break;
				case 0x20: tms->pixelshift = 5; break;
			}
			break;

		case REG020_PMASKL:
		case REG020_PMASKH:
			if (data) logerror("Plane masking not supported. PC=%08X\n", cpu_get_pc(&space->device()));
			break;

		case REG020_DPYCTL:
			set_pixel_function(tms);
			break;

		case REG020_HSTCTLH:
			/* if the CPU is halting itself, stop execution right away */
			if ((data & 0x8000) && !tms->external_host_access)
				tms->icount = 0;
			device_set_input_line(tms->device, INPUT_LINE_HALT, (data & 0x8000) ? ASSERT_LINE : CLEAR_LINE);

			/* NMI issued? */
			if (data & 0x0100)
				tms->device->machine().scheduler().synchronize(FUNC(internal_interrupt_callback), 0, tms);
			break;

		case REG020_HSTCTLL:
			/* the TMS34010 can change MSGOUT, can set INTOUT, and can clear INTIN */
			if (!tms->external_host_access)
			{
				newreg = (oldreg & 0xff8f) | (data & 0x0070);
				newreg |= data & 0x0080;
				newreg &= data | ~0x0008;
			}

			/* the host can change MSGIN, can set INTIN, and can clear INTOUT */
			else
			{
				newreg = (oldreg & 0xfff8) | (data & 0x0007);
				newreg &= data | ~0x0080;
				newreg |= data & 0x0008;
			}
			IOREG(tms, offset) = newreg;

			/* the TMS34010 can set output interrupt? */
			if (!(oldreg & 0x0080) && (newreg & 0x0080))
			{
				if (tms->config->output_int)
					(*tms->config->output_int)(&space->device(), 1);
			}
			else if ((oldreg & 0x0080) && !(newreg & 0x0080))
			{
				if (tms->config->output_int)
					(*tms->config->output_int)(&space->device(), 0);
			}

			/* input interrupt? (should really be state-based, but the functions don't exist!) */
			if (!(oldreg & 0x0008) && (newreg & 0x0008))
				tms->device->machine().scheduler().synchronize(FUNC(internal_interrupt_callback), TMS34010_HI, tms);
			else if ((oldreg & 0x0008) && !(newreg & 0x0008))
				IOREG(tms, REG020_INTPEND) &= ~TMS34010_HI;
			break;

		case REG020_INTENB:
			check_interrupt(tms);
			break;

		case REG020_INTPEND:
			/* X1P, X2P and HIP are read-only */
			/* WVP and DIP can only have 0's written to them */
			IOREG(tms, REG020_INTPEND) = oldreg;
			if (!(data & TMS34010_WV))
				IOREG(tms, REG020_INTPEND) &= ~TMS34010_WV;
			if (!(data & TMS34010_DI))
				IOREG(tms, REG020_INTPEND) &= ~TMS34010_DI;
			break;

		case REG020_CONVSP:
			if (data & 0x001f)
			{
				if (data & 0x1f00)
					tms->convsp = (1 << (~data & 0x1f)) + (1 << (~(data >> 8) & 0x1f));
				else
					tms->convsp = 1 << (~data & 0x1f);
			}
			else
				tms->convsp = data;
			break;

		case REG020_CONVDP:
			if (data & 0x001f)
			{
				if (data & 0x1f00)
					tms->convdp = (1 << (~data & 0x1f)) + (1 << (~(data >> 8) & 0x1f));
				else
					tms->convdp = 1 << (~data & 0x1f);
			}
			else
				tms->convdp = data;
			break;

		case REG020_CONVMP:
			if (data & 0x001f)
			{
				if (data & 0x1f00)
					tms->convmp = (1 << (~data & 0x1f)) + (1 << (~(data >> 8) & 0x1f));
				else
					tms->convmp = 1 << (~data & 0x1f);
			}
			else
				tms->convmp = data;
			break;

		case REG020_DPYSTRT:
		case REG020_DPYADR:
		case REG020_DPYTAP:
			break;

		case REG020_HEBLNK:
		case REG020_HSBLNK:
			if (oldreg != data)
				tms->hblank_stable = 0;
			break;
	}
}



/***************************************************************************
    I/O REGISTER READS
***************************************************************************/

READ16_HANDLER( tms34010_io_register_r )
{
	tms34010_state *tms = get_safe_token(&space->device());
	int result, total;

//  if (LOG_CONTROL_REGS)
//      logerror("%s: read %s\n", device->machine().describe_context(), ioreg_name[offset]);

	switch (offset)
	{
		case REG_HCOUNT:
			/* scale the horizontal position from screen width to HTOTAL */
			result = tms->screen->hpos();
			total = IOREG(tms, REG_HTOTAL) + 1;
			result = result * total / tms->screen->width();

			/* offset by the HBLANK end */
			result += IOREG(tms, REG_HEBLNK);

			/* wrap around */
			if (result > total)
				result -= total;
			return result;

		case REG_REFCNT:
			return (tms->device->total_cycles() / 16) & 0xfffc;

		case REG_INTPEND:
			result = IOREG(tms, offset);

			/* Cool Pool loops in mainline code on the appearance of the DI, even though they */
			/* have an IRQ handler. For this reason, we return it signalled a bit early in order */
			/* to make it past these loops. */
			if (SMART_IOREG(tms, VCOUNT) + 1 == SMART_IOREG(tms, DPYINT) &&
				tms->scantimer->remaining() < attotime::from_hz(40000000/8/3))
				result |= TMS34010_DI;
			return result;
	}

	return IOREG(tms, offset);
}


READ16_HANDLER( tms34020_io_register_r )
{
	tms34010_state *tms = get_safe_token(&space->device());
	int result, total;

//  if (LOG_CONTROL_REGS)
//      logerror("%s: read %s\n", device->machine().describe_context(), ioreg_name[offset]);

	switch (offset)
	{
		case REG020_HCOUNT:
			/* scale the horizontal position from screen width to HTOTAL */
			result = tms->screen->hpos();
			total = IOREG(tms, REG020_HTOTAL) + 1;
			result = result * total / tms->screen->width();

			/* offset by the HBLANK end */
			result += IOREG(tms, REG020_HEBLNK);

			/* wrap around */
			if (result > total)
				result -= total;
			return result;

		case REG020_REFADR:
		{
			int refreshrate = (IOREG(tms, REG020_CONFIG) >> 8) & 7;
			if (refreshrate < 6)
				return (tms->device->total_cycles() / refreshrate) & 0xffff;
			break;
		}
	}

	return IOREG(tms, offset);
}



/***************************************************************************
    SAVE STATE
***************************************************************************/

static STATE_POSTLOAD( tms34010_state_postload )
{
	tms34010_state *tms = (tms34010_state *)param;
	set_raster_op(tms);
	set_pixel_function(tms);
}


/***************************************************************************
    HOST INTERFACE WRITES
***************************************************************************/

void tms34010_host_w(device_t *cpu, int reg, int data)
{
	address_space *space;
	tms34010_state *tms = get_safe_token(cpu);
	unsigned int addr;

	switch (reg)
	{
		/* upper 16 bits of the address */
		case TMS34010_HOST_ADDRESS_H:
			IOREG(tms, REG_HSTADRH) = data;
			break;

		/* lower 16 bits of the address */
		case TMS34010_HOST_ADDRESS_L:
			IOREG(tms, REG_HSTADRL) = data;
			break;

		/* actual data */
		case TMS34010_HOST_DATA:

			/* write to the address */
			addr = (IOREG(tms, REG_HSTADRH) << 16) | IOREG(tms, REG_HSTADRL);
			TMS34010_WRMEM_WORD(tms, TOBYTE(addr & 0xfffffff0), data);

			/* optional postincrement */
			if (IOREG(tms, REG_HSTCTLH) & 0x0800)
			{
				addr += 0x10;
				IOREG(tms, REG_HSTADRH) = addr >> 16;
				IOREG(tms, REG_HSTADRL) = (UINT16)addr;
			}
			break;

		/* control register */
		case TMS34010_HOST_CONTROL:
			tms->external_host_access = TRUE;
			space = tms->device->space(AS_PROGRAM);
			tms34010_io_register_w(space, REG_HSTCTLH, data & 0xff00, 0xffff);
			tms34010_io_register_w(space, REG_HSTCTLL, data & 0x00ff, 0xffff);
			tms->external_host_access = FALSE;
			break;

		/* error case */
		default:
			logerror("tms34010_host_control_w called on invalid register %d\n", reg);
			break;
	}
}



/***************************************************************************
    HOST INTERFACE READS
***************************************************************************/

int tms34010_host_r(device_t *cpu, int reg)
{
	tms34010_state *tms = get_safe_token(cpu);
	unsigned int addr;
	int result = 0;

	/* swap to the target cpu */

	switch (reg)
	{
		/* upper 16 bits of the address */
		case TMS34010_HOST_ADDRESS_H:
			result = IOREG(tms, REG_HSTADRH);
			break;

		/* lower 16 bits of the address */
		case TMS34010_HOST_ADDRESS_L:
			result = IOREG(tms, REG_HSTADRL);
			break;

		/* actual data */
		case TMS34010_HOST_DATA:

			/* read from the address */
			addr = (IOREG(tms, REG_HSTADRH) << 16) | IOREG(tms, REG_HSTADRL);
			result = TMS34010_RDMEM_WORD(tms, TOBYTE(addr & 0xfffffff0));

			/* optional postincrement (it says preincrement, but data is preloaded, so it
               is effectively a postincrement */
			if (IOREG(tms, REG_HSTCTLH) & 0x1000)
			{
				addr += 0x10;
				IOREG(tms, REG_HSTADRH) = addr >> 16;
				IOREG(tms, REG_HSTADRL) = (UINT16)addr;
			}
			break;

		/* control register */
		case TMS34010_HOST_CONTROL:
			result = (IOREG(tms, REG_HSTCTLH) & 0xff00) | (IOREG(tms, REG_HSTCTLL) & 0x00ff);
			break;

		/* error case */
		default:
			logerror("tms34010_host_control_r called on invalid register %d\n", reg);
			break;
	}

	return result;
}



/**************************************************************************
 * Generic set_info
 **************************************************************************/

static CPU_SET_INFO( tms34010 )
{
	tms34010_state *tms = get_safe_token(device);

	switch (state)
	{
		/* --- the following bits of info are set as 64-bit signed integers --- */
		case CPUINFO_INT_INPUT_STATE + 0:				set_irq_line(tms, 0, info->i);		break;
		case CPUINFO_INT_INPUT_STATE + 1:				set_irq_line(tms, 1, info->i);		break;
	}
}



/**************************************************************************
 * Generic get_info
 **************************************************************************/

CPU_EXPORT_STRING( tms34010 )
{
	tms34010_state *tms = get_safe_token(device);

	switch (entry.index())
	{
		case STATE_GENFLAGS:
			string.printf("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
				tms->st & 0x80000000 ? 'N':'.',
				tms->st & 0x40000000 ? 'C':'.',
				tms->st & 0x20000000 ? 'Z':'.',
				tms->st & 0x10000000 ? 'V':'.',
				tms->st & 0x02000000 ? 'P':'.',
				tms->st & 0x00200000 ? 'I':'.',
				tms->st & 0x00000800 ? 'E':'.',
				tms->st & 0x00000400 ? 'F':'.',
				tms->st & 0x00000200 ? 'F':'.',
				tms->st & 0x00000100 ? 'F':'.',
				tms->st & 0x00000080 ? 'F':'.',
				tms->st & 0x00000040 ? 'F':'.',
				tms->st & 0x00000020 ? 'E':'.',
				tms->st & 0x00000010 ? 'F':'.',
				tms->st & 0x00000008 ? 'F':'.',
				tms->st & 0x00000004 ? 'F':'.',
				tms->st & 0x00000002 ? 'F':'.',
				tms->st & 0x00000001 ? 'F':'.');
			break;
	}
}

CPU_GET_INFO( tms34010 )
{
	tms34010_state *tms = (device != NULL && device->token() != NULL) ? get_safe_token(device) : NULL;

	switch (state)
	{
		/* --- the following bits of info are returned as 64-bit signed integers --- */
		case CPUINFO_INT_CONTEXT_SIZE:					info->i = sizeof(tms34010_state);	break;
		case CPUINFO_INT_INPUT_LINES:					info->i = 2;						break;
		case CPUINFO_INT_DEFAULT_IRQ_VECTOR:			info->i = 0;						break;
		case DEVINFO_INT_ENDIANNESS:					info->i = ENDIANNESS_LITTLE;		break;
		case CPUINFO_INT_CLOCK_MULTIPLIER:				info->i = 1;						break;
		case CPUINFO_INT_CLOCK_DIVIDER:					info->i = 8;						break;
		case CPUINFO_INT_MIN_INSTRUCTION_BYTES:			info->i = 2;						break;
		case CPUINFO_INT_MAX_INSTRUCTION_BYTES:			info->i = 10;						break;
		case CPUINFO_INT_MIN_CYCLES:					info->i = 1;						break;
		case CPUINFO_INT_MAX_CYCLES:					info->i = 10000;					break;

		case DEVINFO_INT_DATABUS_WIDTH + AS_PROGRAM:			info->i = 16;						break;
		case DEVINFO_INT_ADDRBUS_WIDTH + AS_PROGRAM: 		info->i = 32;						break;
		case DEVINFO_INT_ADDRBUS_SHIFT + AS_PROGRAM: 		info->i = 3;						break;

		case CPUINFO_INT_INPUT_STATE + 0:				info->i = (IOREG(tms, REG_INTPEND) & TMS34010_INT1) ? ASSERT_LINE : CLEAR_LINE; break;
		case CPUINFO_INT_INPUT_STATE + 1:				info->i = (IOREG(tms, REG_INTPEND) & TMS34010_INT2) ? ASSERT_LINE : CLEAR_LINE; break;

		/* --- the following bits of info are returned as pointers to functions --- */
		case CPUINFO_FCT_SET_INFO:		info->setinfo = CPU_SET_INFO_NAME(tms34010);		break;
		case CPUINFO_FCT_INIT:			info->init = CPU_INIT_NAME(tms34010);				break;
		case CPUINFO_FCT_RESET:			info->reset = CPU_RESET_NAME(tms34010);				break;
		case CPUINFO_FCT_EXIT:			info->exit = CPU_EXIT_NAME(tms34010);				break;
		case CPUINFO_FCT_EXECUTE:		info->execute = CPU_EXECUTE_NAME(tms34010);			break;
		case CPUINFO_FCT_BURN:			info->burn = NULL;									break;
		case CPUINFO_FCT_DISASSEMBLE:	info->disassemble = CPU_DISASSEMBLE_NAME(tms34010);	break;
		case CPUINFO_FCT_EXPORT_STRING:	info->export_string = CPU_EXPORT_STRING_NAME(tms34010);	break;

		/* --- the following bits of info are returned as pointers --- */
		case CPUINFO_PTR_INSTRUCTION_COUNTER:			info->icount = &tms->icount;		break;

		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case DEVINFO_STR_NAME:							strcpy(info->s, "TMS34010");		break;
		case DEVINFO_STR_FAMILY:					strcpy(info->s, "Texas Instruments 340x0"); 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 Alex Pasadyn/Zsolt Vasvari\nParts based on code by Aaron Giles"); break;
	}
}


/**************************************************************************
 * CPU-specific set_info
 **************************************************************************/

CPU_GET_INFO( tms34020 )
{
	switch (state)
	{
		/* --- the following bits of info are returned as 64-bit signed integers --- */
		case CPUINFO_INT_CLOCK_DIVIDER:					info->i = 4;						break;

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case CPUINFO_FCT_RESET:			info->reset = CPU_RESET_NAME(tms34020);				break;
		case CPUINFO_FCT_DISASSEMBLE:	info->disassemble = CPU_DISASSEMBLE_NAME(tms34020);	break;

		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case DEVINFO_STR_NAME:							strcpy(info->s, "TMS34020");		break;

		default:										CPU_GET_INFO_CALL(tms34010);		break;
	}
}

DEFINE_LEGACY_CPU_DEVICE(TMS34010, tms34010);
DEFINE_LEGACY_CPU_DEVICE(TMS34020, tms34020);