summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/nes/mmc3_clones.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/nes/mmc3_clones.h')
0 files changed, 0 insertions, 0 deletions
'>49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
/*************************************************************************

    Asterix

*************************************************************************/
#include "video/k053251.h"
#include "video/k054156_k054157_k056832.h"
#include "video/k053244_k053245.h"
#include "video/konami_helper.h"

class asterix_state : public driver_device
{
public:
	enum
	{
		TIMER_NMI
	};

	asterix_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_audiocpu(*this, "audiocpu"),
		m_k056832(*this, "k056832"),
		m_k053244(*this, "k053244"),
		m_k053251(*this, "k053251") { }

	/* memory pointers */
//  UINT16 *    m_paletteram;    // currently this uses generic palette handling

	/* video-related */
	int         m_sprite_colorbase;
	int         m_layer_colorbase[4];
	int         m_layerpri[3];
	UINT16      m_spritebank;
	int         m_tilebanks[4];
	int         m_spritebanks[4];

	/* misc */
	UINT8       m_cur_control2;
	UINT16      m_prot[2];

	/* devices */
	required_device<cpu_device> m_maincpu;
	required_device<cpu_device> m_audiocpu;
	required_device<k056832_device> m_k056832;
	required_device<k05324x_device> m_k053244;
	required_device<k053251_device> m_k053251;
	DECLARE_READ16_MEMBER(control2_r);
	DECLARE_WRITE16_MEMBER(control2_w);
	DECLARE_WRITE8_MEMBER(sound_arm_nmi_w);
	DECLARE_WRITE16_MEMBER(sound_irq_w);
	DECLARE_WRITE16_MEMBER(protection_w);
	DECLARE_WRITE16_MEMBER(asterix_spritebank_w);
	DECLARE_DRIVER_INIT(asterix);
	virtual void machine_start();
	virtual void machine_reset();
	UINT32 screen_update_asterix(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	INTERRUPT_GEN_MEMBER(asterix_interrupt);
	K05324X_CB_MEMBER(sprite_callback);
	K056832_CB_MEMBER(tile_callback);
	void reset_spritebank();

protected:
	virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
};
>tmpz84c011_0_pa_r); DECLARE_READ8_MEMBER(tmpz84c011_0_pb_r); DECLARE_READ8_MEMBER(tmpz84c011_0_pc_r); DECLARE_READ8_MEMBER(tmpz84c011_0_pd_r); DECLARE_READ8_MEMBER(tmpz84c011_0_pe_r); DECLARE_WRITE8_MEMBER(tmpz84c011_0_pa_w); DECLARE_WRITE8_MEMBER(tmpz84c011_0_pb_w); DECLARE_WRITE8_MEMBER(tmpz84c011_0_pc_w); DECLARE_WRITE8_MEMBER(tmpz84c011_0_pd_w); DECLARE_WRITE8_MEMBER(tmpz84c011_0_pe_w); DECLARE_READ8_MEMBER(tmpz84c011_0_dir_pa_r); DECLARE_READ8_MEMBER(tmpz84c011_0_dir_pb_r); DECLARE_READ8_MEMBER(tmpz84c011_0_dir_pc_r); DECLARE_READ8_MEMBER(tmpz84c011_0_dir_pd_r); DECLARE_READ8_MEMBER(tmpz84c011_0_dir_pe_r); DECLARE_WRITE8_MEMBER(tmpz84c011_0_dir_pa_w); DECLARE_WRITE8_MEMBER(tmpz84c011_0_dir_pb_w); DECLARE_WRITE8_MEMBER(tmpz84c011_0_dir_pc_w); DECLARE_WRITE8_MEMBER(tmpz84c011_0_dir_pd_w); DECLARE_WRITE8_MEMBER(tmpz84c011_0_dir_pe_w); DECLARE_READ16_MEMBER(niyanpai_dipsw_r); DECLARE_READ16_MEMBER(musobana_inputport_0_r); DECLARE_WRITE16_MEMBER(musobana_inputport_w); DECLARE_READ16_MEMBER(niyanpai_palette_r); DECLARE_WRITE16_MEMBER(niyanpai_palette_w); DECLARE_WRITE16_MEMBER(niyanpai_blitter_0_w); DECLARE_WRITE16_MEMBER(niyanpai_blitter_1_w); DECLARE_WRITE16_MEMBER(niyanpai_blitter_2_w); DECLARE_READ16_MEMBER(niyanpai_blitter_0_r); DECLARE_READ16_MEMBER(niyanpai_blitter_1_r); DECLARE_READ16_MEMBER(niyanpai_blitter_2_r); DECLARE_WRITE16_MEMBER(niyanpai_clut_0_w); DECLARE_WRITE16_MEMBER(niyanpai_clut_1_w); DECLARE_WRITE16_MEMBER(niyanpai_clut_2_w); DECLARE_WRITE16_MEMBER(niyanpai_clutsel_0_w); DECLARE_WRITE16_MEMBER(niyanpai_clutsel_1_w); DECLARE_WRITE16_MEMBER(niyanpai_clutsel_2_w); DECLARE_CUSTOM_INPUT_MEMBER(musobana_outcoin_flag_r); DECLARE_DRIVER_INIT(niyanpai); DECLARE_WRITE16_MEMBER(tmp68301_parallel_port_w); UINT8 m_motor_on; virtual void machine_reset(); virtual void video_start(); UINT32 screen_update_niyanpai(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); INTERRUPT_GEN_MEMBER(niyanpai_interrupt); int niyanpai_blitter_r(int vram, int offset); void niyanpai_blitter_w(int vram, int offset, int data); void niyanpai_clutsel_w(int vram, int data); void niyanpai_clut_w(int vram, int offset, int data); void niyanpai_vramflip(int vram); void update_pixel(int vram, int x, int y); void niyanpai_gfxdraw(int vram); void niyanpai_soundbank_w(int data); required_device<cpu_device> m_maincpu; required_device<tmp68301_device> m_tmp68301; required_device<dac_device> m_dac1; required_device<dac_device> m_dac2; protected: virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); };
href='#n893'>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
// license:BSD-3-Clause
// copyright-holders:Juergen Buchmueller
/*****************************************************************************
 *
 *   Xerox AltoII ethernet task
 *
 *****************************************************************************/
#include "emu.h"
#include "alto2cpu.h"
#include "a2roms.h"

#define DEBUG_PACKETS   1


/**
 * @brief BPROMs P3601-1; 256x4; enet.a41 "PE1" and enet.a42 "PE2"
 *
 * Phase encoder
 *
 * a41: P3601-1; 256x4; "PE1"
 * a42: P3601-1; 256x4; "PE2"
 *
 * PE1/PE2 inputs
 * ----------------
 * A0  (5) OUTGO
 * A1  (6) XDATA
 * A2  (7) OSDATAG
 * A3  (4) XCLOCK
 * A4  (3) OCNTR0
 * A5  (2) OCNTR1
 * A6  (1) OCNTR2
 * A7 (15) OCNTR3
 *
 * PE1 outputs
 * ----------------
 * D0 (12) OCNTR0
 * D1 (11) OCNTR1
 * D2 (10) OCNTR2
 * D3  (9) OCNTR3
 *
 * PE2 outputs
 * ----------------
 * D0 (12) n.c.
 * D1 (11) to OSLOAD flip flop J and K'
 * D2 (10) XDATA
 * D3  (9) XCLOCK
 */
static const prom_load_t pl_enet_a41 =
{   /* P3601 256x4 BPROM; Ethernet phase encoder 1 "PE1" */
	"enet.a41",
	nullptr,
	"d5de8d86",
	"c134a4c898c73863124361a9b0218f7a7f00082a",
	/* size */  0400,
	/* amap */  AMAP_DEFAULT,
	/* axor */  0,
	/* dxor */  0,
	/* width */ 4,
	/* shift */ 0,
	/* dmap */  DMAP_DEFAULT,
	/* dand */  ZERO,
	/* type */  sizeof(uint8_t)
};

static const prom_load_t pl_enet_a42 =
{   /* P3601 256x4 BPROM; Ethernet phase encoder 2 "PE2" */
	"enet.a42",
	nullptr,
	"9d5c81bd",
	"ac7e63332a3dad0bef7cd0349b24e156a96a4bf0",
	/* size */  0400,
	/* amap */  AMAP_DEFAULT,
	/* axor */  0,
	/* dxor */  0,
	/* width */ 4,
	/* shift */ 0,
	/* dmap */  DMAP_DEFAULT,
	/* dand */  ZERO,
	/* type */  sizeof(uint8_t)
};

/**
 * @brief BPROM; P3601-1; 265x4 enet.a49 "AFIFO"
 *
 * FIFO control
 *
 * a49: P3601-1; 256x4; "AFIFO"
 *
 * inputs
 * ----------------
 * A0  (5) fifo_wr[0]
 * A1  (6) fifo_wr[1]
 * A2  (7) fifo_wr[2]
 * A3  (4) fifo_wr[3]
 * A4  (3) fifo_rd[0]
 * A5  (2) fifo_rd[1]
 * A6  (1) fifo_rd[2]
 * A7 (15) fifo_rd[3]
 *
 * outputs active low
 * ----------------------------
 * D0 (12) BE'    (buffer empty)
 * D1 (11) BNE'   (buffer nearly empty)
 * D2 (10) BNNE'  (buffer next nearly empty)
 * D3  (9) BF'    (buffer full)
 */
static const prom_load_t pl_enet_a49 =
{   /* P3601 256x4 BPROM; Ethernet FIFO control "AFIFO" */
	"enet.a49",
	nullptr,
	"4d2dcdb2",
	"583327a7d70cd02702c941c0e43c1e9408ff7fd0",
	/* size */  0400,
	/* amap */  AMAP_REVERSE_0_7,               // reverse address lines A0-A7
	/* axor */  0,
	/* dxor */  0,
	/* width */ 4,
	/* shift */ 0,
	/* dmap */  DMAP_DEFAULT,
	/* dand */  ZERO,
	/* type */  sizeof(uint8_t)
};

#define GET_ETH_WLF(st)         X_BIT(st,16,4)              //!< hardware status: write latch full/filled (? set by EODFCT)
#define PUT_ETH_WLF(st,val)     X_WRBITS(st,16,4,4,val)

#define GET_ETH_OEOT(st)        X_BIT(st,16,5)              //!< hardware status: output end of transmission (set by EEFCT)
#define PUT_ETH_OEOT(st,val)    X_WRBITS(st,16,5,5,val)

#define GET_ETH_IGONE(st)       X_BIT(st,16,6)              //!< hardware status: input gone
#define PUT_ETH_IGONE(st,val)   X_WRBITS(st,16,6,6,val)

#define GET_ETH_IBUSY(st)       X_BIT(st,16,7)              //!< hardware status: input busy (set by EISFCT, bit isn't visible to microcode)
#define PUT_ETH_IBUSY(st,val)   X_WRBITS(st,16,7,7,val)

#define GET_ETH_OGONE(st)       X_BIT(st,16,8)              //!< hardware status: output gone
#define PUT_ETH_OGONE(st,val)   X_WRBITS(st,16,8,8,val)

#define GET_ETH_OBUSY(st)       X_BIT(st,16,9)              //!< hardware status: output busy (set by EOSFCT, bit isn't visible to microcode)
#define PUT_ETH_OBUSY(st,val)   X_WRBITS(st,16,9,9,val)

#define GET_ETH_IDL(st)         X_BIT(st,16,10)             //!< hardware status: input data late
#define PUT_ETH_IDL(st,val)     X_WRBITS(st,16,10,10,val)

#define GET_ETH_COLL(st)        X_BIT(st,16,11)             //!< hardware status: collision
#define PUT_ETH_COLL(st,val)    X_WRBITS(st,16,11,11,val)

#define GET_ETH_CRC(st)         X_BIT(st,16,12)             //!< hardware status: CRC error
#define PUT_ETH_CRC(st,val)     X_WRBITS(st,16,12,12,val)

#define GET_ETH_ICMD(st)        X_BIT(st,16,13)             //!< hardware status: input command (set from BUS[14] on SIO, reset by EPFCT)
#define PUT_ETH_ICMD(st,val)    X_WRBITS(st,16,13,13,val)

#define GET_ETH_OCMD(st)        X_BIT(st,16,14)             //!< hardware status: output command (set from BUS[15] on SIO, reset by EPFCT)
#define PUT_ETH_OCMD(st,val)    X_WRBITS(st,16,14,14,val)

#define GET_ETH_IT(st)          X_BIT(st,16,15)             //!< hardware status: IT flip flop & ISRFULL'
#define PUT_ETH_IT(st,val)      X_WRBITS(st,16,15,15,val)

#define BE(a49)   ((a49 & ether_a49_BE) ? 1 : 0)        //! buffer empty
#define BNE(a49)  ((a49 & ether_a49_BNE) ? 1 : 0)       //! buffer next empty
#define BNNE(a49) ((a49 & ether_a49_BNNE) ? 1 : 0)      //! buffer next next empty
#define BF(a49)   ((a49 & ether_a49_BF) ? 1 : 0)        //! buffer full

#define BREATHLEN   ALTO2_ETHER_PACKET_SIZE //!< ethernet packet length
#define BREATHADDR  (0377<<8)               //!< destination (0377) and source (0000)
#define BREATHTYPE  0602                    //!< ethernet packet type
static const uint16_t breath_of_life_data[BREATHLEN] =
{
	BREATHADDR,     /* 3MB destination and source */
	BREATHTYPE,     /* ether packet type  */
	/* the rest is the contents of a breath of life packet.
	 * see <altosource>etherboot.dm (etherboot.asm) for the
	 * Alto assembly code.
	 */
	0022574, 0100000, 0040437, 0102000, 0034431, 0164000,
	0061005, 0102460, 0024567, 0034572, 0061006, 0024565, 0034570, 0061006,
	0024564, 0034566, 0061006, 0020565, 0034565, 0061005, 0125220, 0046573,
	0020576, 0061004, 0123400, 0030551, 0041211, 0004416, 0000000, 0001000,
	0000026, 0000244, 0000000, 0000000, 0000000, 0000000, 0000004, 0000000,
	0000000, 0000020, 0177777, 0055210, 0025400, 0107000, 0045400, 0041411,
	0020547, 0041207, 0020544, 0061004, 0006531, 0034517, 0030544, 0051606,
	0020510, 0041605, 0042526, 0102460, 0041601, 0020530, 0061004, 0021601,
	0101014, 0000414, 0061020, 0014737, 0000773, 0014517, 0000754, 0020517,
	0061004, 0030402, 0002402, 0000000, 0000732, 0034514, 0162414, 0000746,
	0021001, 0024511, 0106414, 0000742, 0021003, 0163400, 0035005, 0024501,
	0106415, 0175014, 0000733, 0021000, 0042465, 0034457, 0056445, 0055775,
	0055776, 0101300, 0041400, 0020467, 0041401, 0020432, 0041402, 0121400,
	0041403, 0021006, 0041411, 0021007, 0041412, 0021010, 0041413, 0021011,
	0041406, 0021012, 0041407, 0021013, 0041410, 0015414, 0006427, 0012434,
	0006426, 0020421, 0024437, 0134000, 0030417, 0002422, 0177035, 0000026,
	0000415, 0000427, 0000567, 0000607, 0000777, 0177751, 0177641, 0177600,
	0000225, 0177624, 0001013, 0000764, 0000431, 0000712, 0000634, 0000735,
	0000611, 0000567, 0000564, 0000566, 0000036, 0000002, 0000003, 0000015,
	0000030, 0000377, 0001000, 0177764, 0000436, 0054731, 0050750, 0020753,
	0040745, 0102460, 0040737, 0020762, 0061004, 0020734, 0105304, 0000406,
	0020743, 0101014, 0014741, 0000772, 0002712, 0034754, 0167700, 0116415,
	0024752, 0021001, 0106414, 0000754, 0021000, 0024703, 0106414, 0000750,
	0021003, 0163400, 0024736, 0106405, 0000404, 0121400, 0101404, 0000740,
	0044714, 0021005, 0042732, 0024664, 0122405, 0000404, 0101405, 0004404,
	0000727, 0010656, 0034654, 0024403, 0120500, 0101404, 0000777, 0040662,
	0040664, 0040664, 0102520, 0061004, 0020655, 0101015, 0000776, 0106415,
	0001400, 0014634, 0000761, 0020673, 0061004, 0000400, 0061005, 0102000,
	0143000, 0034672, 0024667, 0166400, 0061005, 0004670, 0020663, 0034664,
	0164000, 0147000, 0061005, 0024762, 0132414, 0133000, 0020636, 0034416,
	0101015, 0156415, 0131001, 0000754, 0024643, 0044625, 0101015, 0000750,
	0014623, 0004644, 0020634, 0061004, 0002000, 0176764, 0001401, 0041002
};

#if DEBUG_PACKETS
static void dump_ascii(device_t *device, const uint16_t *src, size_t size)
{
	device->logerror(" [");
	for (size_t offs = 0; offs < size; offs++) {
		char ch1 = src[offs] / 256;
		char ch2 = src[offs] % 256;
		device->logerror("%c", ch1 < 32 || ch1 > 126 ? '.' : ch1);
		device->logerror("%c", ch2 < 32 || ch2 > 126 ? '.' : ch2);
	}
	device->logerror("]\n");
}

static void dump_packet(device_t *device, const char* name, const uint16_t *src, size_t addr, size_t size)
{
	size_t offs;
	for (offs = 0; offs < size; offs++) {
		uint16_t word = src[offs];
		if (offs % 8) {
			device->logerror(" %06o", word);
		} else {
			if (offs > 0)
				dump_ascii(device, &src[offs-8], 8);
			device->logerror("%s\t%05o: %06o", name, static_cast<unsigned>(addr + offs), word);
		}
	}
	if (offs % 8) {
		dump_ascii(device, &src[offs - (offs % 8)], offs % 8);
	} else if (offs > 0) {
		dump_ascii(device, &src[offs - 8], 8);
	}
}
#endif

/**
 * @brief check for the various reasons to wakeup the ethernet task
 */
void alto2_cpu_device::eth_wakeup()
{
	int st = m_eth.status;
	LOG((this,LOG_ETH,0,"IBUSY=%d OBUSY=%d ", GET_ETH_IBUSY(st), GET_ETH_OBUSY(st)));
	uint8_t busy = GET_ETH_IBUSY(st) | GET_ETH_OBUSY(st);
	if (0 == busy) {
		// if not busy, reset the FIFO read and write counters
		m_eth.fifo_rd = 0;
		m_eth.fifo_wr = 0;
	}

	/*
	 * POST conditions to wakeup the Ether task:
	 *  input data late
	 *  output command
	 *  input command
	 *  output gone
	 *  input gone
	 */
	if (GET_ETH_IDL(st)) {
		LOG((this,LOG_ETH,0,"POST (input data late)\n"));
		m_task_wakeup |= 1 << task_ether;
		return;
	}
	if (GET_ETH_OCMD(st)) {
		LOG((this,LOG_ETH,0,"POST (output command)\n"));
		m_task_wakeup |= 1 << task_ether;
		return;
	}
	if (GET_ETH_ICMD(st)) {
		LOG((this,LOG_ETH,0,"POST (input command)\n"));
		m_task_wakeup |= 1 << task_ether;
		return;
	}
	if (GET_ETH_OGONE(st)) {
		LOG((this,LOG_ETH,0,"POST (output gone)\n"));
		m_task_wakeup |= 1 << task_ether;
		return;
	}
	if (GET_ETH_IGONE(st)) {
		LOG((this,LOG_ETH,0,"POST (input gone)\n"));
		m_task_wakeup |= 1 << task_ether;
		return;
	}

	/**
	 * IDR (input data ready) conditions to wakeup the Ether task
	 *  signal  meaining
	 * --------------------------------------
	 *  IBUSY   input busy
	 *  BNNE    buffer next nearly empty
	 *  BNE     buffer nearly empty
	 *  ETAC    ether task active
	 *
	 ************************************************************
	 *            +----+
	 * BNE'  >----|NAND| (i1)  +----+
	 *            |    o-------|NAND| (i2)  +----+
	 * ETAC' >----|    |       |    o-------|NAND|
	 *            +----+   +---|    |       |    o-----> IDR'
	 *                     |   +----+   +---|    |
	 *            +---+    |            |   +----+
	 * BNNE' >----|INVo----+            |
	 *            +---+                 |
	 *                                  |
	 * IBUSY >--------------------------+
	 *
	 ************************************************************
	 */
	uint8_t a49 = m_ether_a49[16 * m_eth.fifo_wr + m_eth.fifo_rd];
	uint8_t ETAC = m_task == task_ether ? 0 : 1;
	uint8_t i1 = ~(BNE(a49) & ETAC);
	uint8_t i2 = ~(~BNNE(a49) & i1);
	uint8_t IDR = ~(GET_ETH_IBUSY(st) & i2);
	if (0 == IDR) {
		m_task_wakeup |= 1 << task_ether;
		LOG((this,LOG_ETH,0,"IDR (input data ready)\n"));
		return;
	}

	/**
	 * ODR (output data ready) conditions to wakeup the Ether task
	 *  signal  meaining
	 * --------------------------------------
	 *  WLF    write latch full(?)
	 *  BF     buffer (FIFO) full
	 *  OEOT   output end of transmission
	 *  OBUSY  output busy
	 ************************************************************
	 *            +----+
	 * WLF'  >----|NAND| (o1)    +----+
	 *            |    o---------|NAND|
	 * BF'   >----|    |         |    |
	 *            +----+  +------|    o----> ODR'
	 *                    |      |    |
	 *                    |  +---|    |
	 * OEOT' >------------+  |   +----+
	 *                       |
	 *                       |
	 * OBUSY >---------------+
	 *
	 ************************************************************
	 */
	uint8_t o1 = ~(~GET_ETH_WLF(st) & BF(a49));
	uint8_t ODR = ~(GET_ETH_OBUSY(st) & ~GET_ETH_OEOT(st) & o1);
	if (0 == ODR) {
		m_task_wakeup |= 1 << task_ether;
		LOG((this,LOG_ETH,0,"ODR (output data ready)\n"));
		return;
	}

	/*
	 * EWFCT (ether wake function) conditions to wakeup the Ether task:
	 *  EWFCT   flip flop set by the F1 EWFCT
	 * The task is activated by the display code together with the
	 * next wakeup of the memory refresh task (MRT).
	 */
	if (m_ewfct) {
		m_task_wakeup |= 1 << task_ether;
		LOG((this,LOG_ETH,0,"EWFCT (ether wake function)\n"));
		return;
	}

	// otherwise no more wakeups for the ether task
	LOG((this,LOG_ETH,0,"stop wake\n"));
	m_task_wakeup &= ~(1 << task_ether);
}

/**
 * @brief F9401 CRC checker
 * <PRE>
 *
 * The F9401 looks similiar to the SN74F401. However, in the schematics
 * there is a connection from pin 9 (labeled D9) to pin 2 (labeled Q8).
 * See below for the difference:
 *
 *           SN74F401                       F9401
 *         +---+-+---+                   +---+-+---+
 *         |   +-+   |                   |   +-+   |
 *    CP' -|1      14|-  Vcc       CLK' -|1      14|-  Vcc
 *         |         |                   |         |
 *     P' -|2      13|-  ER          P' -|2      13|-  CRCZ'
 *         |         |                   |         |
 *    S0  -|3      12|-  Q           Z  -|3      12|-  CRCDATA
 *         |         |                   |         |
 *    MR  -|4      11|-  D          MR  -|4      11|-  SDI
 *         |         |                   |         |
 *    S1  -|5      10|-  CWE         Y  -|5      10|-  SR
 *         |         |                   |         |
 *    NC  -|6       9|-  NC         D1  -|6       9|-  D9
 *         |         |                   |         |
 *   GND  -|7       8|-  S2        GND  -|7       8|-  X
 *         |         |                   |         |
 *         +---------+                   +---------+
 *
 * Functional description (SN74F401)
 *
 * The 'F401 is a 16-bit programmable device which operates on serial data
 * streams and provides a means of detecting transmission errors. Cyclic
 * encoding and decoding schemes for error detection are based on polynomial
 * manipulation in modulo arithmetic. For encoding, the data stream (message
 * polynomial) is divided by a selected polynomial. This division results
 * in a remainder which is appended to the message as check bits. For error
 * checking, the bit stream containing both data and check bits is divided
 * by the same selected polynomial. If there are no detectable errors, this
 * division results in a zero remainder. Although it is possible to choose
 * many generating polynomials of a given degree, standards exist that
 * specify a small number of useful polynomials. The 'F401 implements the
 * polynomials listed in Tabel I by applying the appropriate logic levels
 * to the select pins S0, S1 and S2.
 *
 * The 'F401 consists of a 16-bit register, a Read Only Memory (ROM) and
 * associated control circuitry as shown in the block diagram. The
 * polynomial control code presented at inputs S0, S1 and S2 is decoded
 * by the ROM, selecting the desired polynomial by establishing shift
 * mode operation on the register with Exclusive OR gates at appropriate
 * inputs. To generate check bits, the data stream is entered via the
 * Data inputs (D), using the HIGH-to-LOW transition of the Clock input
 * (CP'). This data is gated with the most significant output (Q) of
 * the register, and controls the Exclusive OR gates (Figure 1). The
 * Check Word Enable (CWE) must be held HIGH while the data is being
 * entered. After the last data bit is entered, the CWE is brought LOW
 * and the check bits are shifted out of the register and appended to
 * the data bits using external gating (Figure 2).
 *
 * To check an incoming message for errors, both the data and check bits
 * are entered through the D input with the CWE input held HIGH. The
 * 'F401 is not in the data path, but only monitors the message. The
 * Error output becomes valid after the last check bit has been entered
 * into the 'F401 by a HIGH-to-LOW transition of CP'. If no detectable
 * errors have occurred during the transmission, the resultant internal
 * register bits are all LOW and the Error Output (ER) is LOW.
 * If a detectable error has occurred, ER is HIGH.
 *
 * A HIGH on the Master Reset input (MR) asynchronously clears the
 * register. A LOW on the Preset input (P') asynchronously sets the
 * entire register if the control code inputs specify a 16-bit
 * polynomial; in the case of 12- or 8-bit check polynomials only the
 * most significant 12 or 8 register bits are set and the remaining
 * bits are cleared.
 *
 * [Table I]
 *
 * S2 S1 S0 polynomial                      remarks
 * ----------------------------------------------------------------
 * L  L  L  x^16+x^15+x^2+1                 CRC16
 * L  L  H  x^16+x^14+x+1                   CRC16 reverse
 * L  H  L  x^16+x^15+x^13+x^7+x^4+x^2+x+1  -/-
 * L  H  H  x^12+x^11+x^3+x^2+x+1           CRC-12
 * H  L  L  x^8+x^7+x^5+x^4+x+1             -/-
 * H  L  H  x^8+1                           LRC-8
 * H  H  L  X^16+x^12+x^5+1                 CRC-CCITT
 * H  H  H  X^16+x^11+x^4+1                 CRC-CCITT reverse
 *
 * </PRE>
 * The Alto ethernet interface seems to be using the last one of the polynomials,
 * or perhaps something entirely different?
 *
 * TODO: verify polynomial generator; build a lookup table to make it faster.
 *
 * @param crc previous CRC value
 * @param data 16 bit data
 * @return new CRC value after 16 bits
 */
uint32_t f9401_7(uint32_t crc, uint32_t data)
{
	static const uint32_t XOR = (1 << 10) | (1 << 3) | (1 << 0);
	crc ^= data;
	for (int i = 0; i < 16; i++)
		crc = (crc >> 1) ^ ((crc & 1) ? XOR : 0);
	return crc & 0177777;
}

/**
 * @brief HACK: pull the next word from the breath_of_life_data in the fifo
 *
 * This is probably lacking the updates to one or more of
 * the status flip flops.
 */
void alto2_cpu_device::rx_breath_of_life(void* ptr, int32_t arg)
{
	uint32_t data;

	if (arg == 0) {
		// on the first word set the IBUSY flip flop
		PUT_ETH_IBUSY(m_eth.status, 1);
		m_eth.rx_count = 0;
	}

	if (arg >= BREATHLEN) {
		// CRC after the data
		data = m_eth.rx_crc;
		arg++;
	} else {
		// next data word
		data = breath_of_life_data[arg++];
	}
	m_eth.rx_crc = f9401_7(m_eth.rx_crc, data);
	m_eth.fifo[m_eth.fifo_wr] = data;
	m_eth.fifo_wr = (m_eth.fifo_wr + 1) % ALTO2_ETHER_FIFO_SIZE;

	PUT_ETH_IT(m_eth.status, 1);    // set IT (input shift register full ...)?

	uint8_t a49 = m_ether_a49[16 * m_eth.fifo_wr + m_eth.fifo_rd];
	if (0 == BF(a49))
		PUT_ETH_IDL(m_eth.status, 1);   // fifo is overrun: set input data late flip flop

	if (arg > BREATHLEN) {
		/*
		 * TODO: if data comes from some other source,
		 * compare our CRC with the next word received
		 * and set the CRC error flag if they differ.
		 */
		m_eth.rx_crc = 0;
		PUT_ETH_IGONE(m_eth.status, 1);     // set the IGONE flip flop
		m_eth.rx_timer->adjust(attotime::from_seconds(m_eth.breath_of_life), 0);
	} else {
		// receive at a rate of 5.44us per word
		m_eth.rx_timer->adjust(attotime::from_usec(5.44), arg);
	}
	eth_wakeup();
}

/**
 * @brief transmit data from the FIFO to <nirvana for now>
 *
 * @param ptr unused pointer
 * @param arg word count if >= 0, -1 if CRC is to be transmitted (last word)
 */
void alto2_cpu_device::tx_packet(void* ptr, int32_t arg)
{
	uint32_t data;

	// the last word sent is the CRC
	if (-1 == arg) {
		m_eth.tx_timer->reset();
		LOG((this,LOG_ETH,0," CRC:%06o\n", m_eth.tx_crc));
		// TODO: send the CRC as final word of the packet
		m_eth.tx_crc = 0;
		PUT_ETH_OGONE(m_eth.status, 1);     // set the OGONE flip flop
		eth_wakeup();
		return;
	}

	data = m_eth.fifo[m_eth.fifo_rd];
	m_eth.tx_crc = f9401_7(m_eth.tx_crc, data);
	m_eth.fifo_rd = (m_eth.fifo_rd + 1) % ALTO2_ETHER_FIFO_SIZE;

	uint8_t a49 = m_ether_a49[16 * m_eth.fifo_wr + m_eth.fifo_rd];
	if (0 == BE(a49)) {
		// the FIFO is empty now: clear the OBUSY and WLF flip flops
		PUT_ETH_OBUSY(m_eth.status, 0);
		PUT_ETH_WLF(m_eth.status, 0);
		m_eth.tx_timer->adjust(attotime::from_usec(5.44), -1);
	} else {
		// transmit the next word after 5.44us
		m_eth.tx_timer->adjust(attotime::from_usec(5.44), arg + 1);
	}
	eth_wakeup();
}

/**
 * @brief ethernet start function - called from the emulator task
 */
void alto2_cpu_device::eth_startf()
{
#if 0   // FIXME: does not yet work
	for (int sysclk = 0; sysclk < 2; sysclk++)
		update_sysclk(sysclk);
	PUT_ETH_OCMD(m_eth.status, m_eth.ff_35a & JKFF_Q ? 1 : 0);
	PUT_ETH_ICMD(m_eth.status, m_eth.ff_35b & JKFF_Q ? 1 : 0);
#else
	PUT_ETH_ICMD(m_eth.status, X_BIT(m_bus,16,14));
	PUT_ETH_OCMD(m_eth.status, X_BIT(m_bus,16,15));
#endif
	LOG((this,LOG_ETH,3, "   STARTF; ICMD=%u OCMD=%u\n", GET_ETH_ICMD(m_eth.status), GET_ETH_ICMD(m_eth.status)));
	eth_wakeup();
}

/**
 * @brief ethernet input data function
 *
 * Gates the contents of the FIFO to BUS[0-15], and increments
 * the read pointer at the end of the cycle.
 */
void alto2_cpu_device::bs_early_eidfct()
{
	uint16_t r = m_eth.fifo[m_eth.fifo_rd];
	LOG((this,LOG_ETH,3, "   <-EIDFCT; pull %06o from FIFO[%02o]\n", r, m_eth.fifo_rd));
	m_eth.fifo_rd = (m_eth.fifo_rd + 1) % ALTO2_ETHER_FIFO_SIZE;
	m_bus &= r;

#if DEBUG_PACKETS
	if (m_eth.rx_count < ALTO2_ETHER_PACKET_SIZE)
		m_eth.rx_packet[m_eth.rx_count] = r;
	m_eth.rx_count++;
	if (ALTO2_ETHER_PACKET_SIZE == m_eth.rx_count) {
		dump_packet(this,"RX", m_eth.rx_packet.get(), 0, m_eth.rx_count);
		m_eth.rx_count = 0;
	}
#endif
	eth_wakeup();
}

/**
 * @brief block the ethernet task
 */
void alto2_cpu_device::f1_early_eth_block()
{
	LOG((this,LOG_ETH,2,"    BLOCK %s\n", task_name(m_task)));
	m_task_wakeup &= ~(1 << task_ether);
}

/**
 * @brief ethernet input look function
 *
 * Gates the contents of the FIFO to BUS[0-15], but does not
 * increment the read pointer
 */
void alto2_cpu_device::f1_early_eilfct()
{
	uint16_t r = m_eth.fifo[m_eth.fifo_rd];
	LOG((this,LOG_ETH,3, "   <-EILFCT; %06o at FIFO[%02o]\n", r, m_eth.fifo_rd));
	m_bus &= r;
}

/**
 * @brief ethernet post function
 *
 * Gates the interface status to BUS[8-15]. Resets the interface
 * at the end of the function.
 *
 * The schematics suggest that just BUS[10-15] is modified.
 *
 * Also a comment from the microcode suggests this:
 *<PRE>
 * ;Ether Post Function - EPFCT.  Gate the hardware status
 * ;(LOW TRUE) to Bus [10:15], reset interface.
 *</PRE>
 */
void alto2_cpu_device::f1_early_epfct()
{
	uint16_t r = 0177777;
	uint16_t st = m_eth.status;
	m_eth.status = 0;
	m_eth.tx_count = 0;

	X_WRBITS(r,16,10,10,~GET_ETH_IDL(st));      // BUS[10] = IDL (input data late)
	X_WRBITS(r,16,11,11,~GET_ETH_COLL(st));     // BUS[11] = COLL (collision)
	X_WRBITS(r,16,12,12,~GET_ETH_CRC(st));      // BUS[12] = CRC (CRC error)
	X_WRBITS(r,16,13,13,~GET_ETH_ICMD(st));     // BUS[13] = ICMD (input command)
	X_WRBITS(r,16,14,14,~GET_ETH_OCMD(st));     // BUS[13] = OCMD (output command)
	X_WRBITS(r,16,15,15,~GET_ETH_IT(st));       // BUS[13] = IT (input ???)
	m_bus &= r;

	LOG((this,LOG_ETH,3, "   <-EPFCT; BUS[8-15] = STATUS (%#o)\n", r));
	LOG((this,LOG_ETH,5, "       IDL'    : %u\n", GET_ETH_IDL(r)));
	LOG((this,LOG_ETH,5, "       COLL'   : %u\n", GET_ETH_COLL(r)));
	LOG((this,LOG_ETH,5, "       CRC'    : %u\n", GET_ETH_CRC(r)));
	LOG((this,LOG_ETH,5, "       ICMD'   : %u\n", GET_ETH_ICMD(r)));
	LOG((this,LOG_ETH,5, "       OCMD'   : %u\n", GET_ETH_OCMD(r)));
	LOG((this,LOG_ETH,5, "       IT'     : %u\n", GET_ETH_IT(r)));
	eth_wakeup();
}

/**
 * @brief ethernet countdown wakeup function
 *
 * Sets a flip flop in the interface that will cause a wakeup to the
 * Ether task on the next tick of SWAKMRT (memory refresh task).
 * This function must be issued in the instruction after a TASK.
 * The resulting wakeup is cleared when the Ether task next runs.
 */
void alto2_cpu_device::f1_late_ewfct()
{
	/*
	 * Set a flag in the CPU to handle the next task switch
	 * to the task_mrt by also waking up the task_ether.
	 */
	m_ewfct = m_ether_enable;
}

/**
 * @brief ethernet output data function
 *
 * Loads the FIFO from BUS[0-15], then increments the write
 * pointer at the end of the cycle.
 *
 * Comment from the micro code:
 *<PRE>
 * Ether Output Data Function - EODFCT.  Copy the bus into the
 * interface data buffer, increment the write pointer, clears wakeup
 * request if the buffer is now nearly full (one slot available).
 *</PRE>
 */
void alto2_cpu_device::f2_late_eodfct()
{
	LOG((this,LOG_ETH,3, "   EODFCT<-; push %06o into FIFO[%02o]\n", m_bus, m_eth.fifo_wr));
	m_eth.fifo[m_eth.fifo_wr] = m_bus;
	m_eth.fifo_wr = (m_eth.fifo_wr + 1) % ALTO2_ETHER_FIFO_SIZE;

#if DEBUG_PACKETS
	if (m_eth.tx_count < ALTO2_ETHER_PACKET_SIZE)
		m_eth.tx_packet[m_eth.tx_count] = m_bus;
	m_eth.tx_count++;
	if (ALTO2_ETHER_PACKET_SIZE == m_eth.tx_count) {
		dump_packet(this,"TX", m_eth.tx_packet.get(), 0, m_eth.tx_count);
		m_eth.tx_count = 0;
	}
#endif

	PUT_ETH_WLF(m_eth.status, 1);           // set WLF (write latch full)
	PUT_ETH_OBUSY(m_eth.status, 1);         // set OBUSY (output busy)
	eth_wakeup();
	// if the FIFO is full, stop wakeup and kick off the timer
	uint8_t a49 = m_ether_a49[16 * m_eth.fifo_wr + m_eth.fifo_rd];
	if (0 == BF(a49)) {
		m_task_wakeup &= ~(1 << task_ether);
		m_eth.tx_timer->adjust(attotime::from_usec(5.44), 0);
	}
}

/**
 * @brief ethernet output start function
 *
 * Sets the OBUSY flip flop in the interface, starting data
 * wakeups to fill the FIFO for output. When the FIFO is full,
 * or EEFCT has been issued, the interface will wait for silence
 * on the Ether and begin transmitting.
 */
void alto2_cpu_device::f2_late_eosfct()
{
	LOG((this,LOG_ETH,3, "   EOSFCT\n"));
	PUT_ETH_WLF(m_eth.status, 1);
	PUT_ETH_OBUSY(m_eth.status, 1);
	eth_wakeup();
}

/**
 * @brief ethernet reset branch function
 *
 * This command dispatch function merges the ICMD and OCMD flip flops
 * into NEXT[6-7]. These flip flops are the means of communication
 * between the emulator task and the ethernet task. The emulator
 * task sets them up from BUS[14-15] with the STARTF function,
 * causing the ethernet task to wakeup, dispatch on them and then
 * reset them with EPFCT.
 */
void alto2_cpu_device::f2_late_erbfct()
{
	uint16_t r = 0;
	X_WRBITS(r,10,6,6,GET_ETH_ICMD(m_eth.status));
	X_WRBITS(r,10,7,7,GET_ETH_OCMD(m_eth.status));
	LOG((this,LOG_ETH,3, "   ERBFCT; NEXT[6-7] = ICMD,OCMD (%#o | %#o)\n", m_next2, r));
	m_next2 |= r;
	eth_wakeup();
}

/**
 * @brief ethernet end of transmission function
 *
 * This function is issued when all of the main memory output buffer
 * has been transferred to the FIFO. EEFCT disables further data
 * wakeups.
 */
void alto2_cpu_device::f2_late_eefct()
{
	PUT_ETH_OBUSY(m_eth.status, 1);
	PUT_ETH_OEOT(m_eth.status, 1);
	// end transmitting the packet
	m_eth.tx_timer->adjust(attotime::from_usec(5.44), -1);
	eth_wakeup();
}

/**
 * @brief ethernet branch function
 *
 * ORs a 1 into NEXT[6] if a collision is detected.
 * ORs a 1 into NEXT[7] if
 *     an input data late is detected,
 *     or a SIO with AC0[14-15] non-zero is issued (ICMD or OCMD),
 *     or if the receiver is gone (IGONE)
 *     or if the transmitter is gone (OGONE).
 */
void alto2_cpu_device::f2_late_ebfct()
{
	uint16_t r = 0;
	X_WRBITS(r,10,6,6, GET_ETH_COLL(m_eth.status));
	X_WRBITS(r,10,7,7,
				GET_ETH_IDL(m_eth.status) |
				GET_ETH_ICMD(m_eth.status) |
				GET_ETH_OCMD(m_eth.status) |
				GET_ETH_IGONE(m_eth.status) |
				GET_ETH_OGONE(m_eth.status));
	LOG((this,LOG_ETH,3, "   EBFCT; NEXT ... (%#o | %#o)\n", m_next2, r));
	m_next2 |= r;
}

/**
 * @brief ethernet countdown branch function
 *
 * The BE' (buffer empty) signal is output D0 of PROM a49
 * ORs a one into NEXT[7] if the FIFO is not empty.
 */
void alto2_cpu_device::f2_late_ecbfct()
{
	uint16_t r = 0;
	uint8_t a49 = m_ether_a49[16 * m_eth.fifo_wr + m_eth.fifo_rd];
	X_WRBITS(r,10,7,7,~BE(a49));
	LOG((this,LOG_ETH,3, "   ECBFCT; NEXT[7] = FIFO %sempty (%#o | %#o)\n", r ? "not " : "is ", m_next2, r));
	m_next2 |= r;
}

/**
 * @brief ethernet input start function
 *
 * Sets the IBUSY flip flop in the interface, causing it to hunt
 * for the beginning of a packet: silence on the Ether followed
 * by a transition. When the interface has collected two words,
 * it will begin generating data wakeups to the microcode.
 */
void alto2_cpu_device::f2_late_eisfct()
{
	LOG((this,LOG_ETH,3, "   EISFCT\n"));
	PUT_ETH_IBUSY(m_eth.status, 1);
	eth_wakeup();
}

/** @brief called by the CPU when the ethernet task becomes active
 *
 * Reset the Ether wake flip flop
 */
void alto2_cpu_device::activate_eth()
{
	m_ewfct = 0;
}

/**
 * @brief update the ethernet circuit JK flip-flops that depend on SYSCLK
 * @param sysclk current SYSCLK level 0 or 1
 */
void alto2_cpu_device::update_sysclk(int sysclk)
{
	uint8_t s0, s1;

	/*
	 * JK flip-flop 35a (SIO' and SYSCLK clocked)
	 * (Sheet 7)
	 *
	 * Note: SIO is the emulator F1 STARTF
	 *
	 * CLK  (SIO & SYSCLK)'
	 * J    BUS[15]
	 * K'   1
	 * S'   1
	 * C'   ERESET'
	 * Q    OCMD
	 * Q'   OCMD'
	 */
	s0 = m_eth.ff_35a;
	s1 = (f1() == f1_emu_startf && sysclk) ? JKFF_CLK : JKFF_0;
	if (X_BIT(m_bus,16,15))
		s1 |= JKFF_J;
	s1 |= JKFF_K;
	s1 |= JKFF_C;       // ERESET' not now
	m_eth.ff_35a = update_jkff(s0, s1, "35a OCMD   ");

	/*
	 * JK flip-flop 35b (SIO' and SYSCLK clocked)
	 * (Sheet 7)
	 *
	 * Note: SIO is the emulator F1 STARTF
	 *
	 * CLK  (SIO & SYSCLK)'
	 * J    BUS[14]
	 * K'   1
	 * S'   1
	 * C'   ERESET'
	 * Q    ICMD
	 * Q'   ICMD'
	 */
	s0 = m_eth.ff_35b;
	s1 = (f1() == f1_emu_startf && sysclk) ? JKFF_CLK : JKFF_0;
	if (X_BIT(m_bus,16,14))
		s1 |= JKFF_J;
	s1 |= JKFF_K;
	s1 |= JKFF_C;       // ERESET' not now
	m_eth.ff_35b = update_jkff(s0, s1, "35b ICMD   ");

	/*
	 * JK flip-flop 10a IBUSY (Sheet 13)
	 *
	 * CLK  SYSCLK'
	 * J    0
	 * K'   EISFCT'
	 * S'   ERESET'
	 * C'   1
	 * Q    IBUSY'
	 * Q'   IBUSY
	 */
	s0 = m_eth.ff_10a;
	s1 = sysclk ? JKFF_CLK : JKFF_0;
	if (f2() != f2_ether_eisfct)
		s1 |= JKFF_K;
	s1 |= JKFF_C;
	m_eth.ff_10a = update_jkff(s0, s1, "10a IBUSY    ");

	/*
	 * DEMUX 74S157 76
	 * 1A   (SYSCLK & EODFCT)'
	 * 2A   OSLOAD'
	 * 3A   OSLOAD
	 * 4A   (SYSCLK & EODFCT)'
	 * 1B   ISRFULL
	 * 2B   (SYSCLK & EIDFCT)'
	 * 3B   EILDFCTA = (EIDFCT' & EILFCT')'
	 * 4B   WLF'
	 * SEL  IBUSY
	 * 1Y   WLLOAD
	 * 2Y   RDCNT'
	 * 3Y   RR
	 * 4Y   WLL'
	 */
	uint8_t WLLOAD;
	uint8_t RDCNT0;
	uint8_t RR;
	uint8_t WLL0;
	if (m_eth.ff_10a & JKFF_Q) {
		WLLOAD = ~(sysclk & (f2() == f2_ether_eodfct)) & 1;
		RDCNT0 = m_eth.ff_52b & JKFF_Q ? 1 : 0;
		RR     = m_eth.ff_52b & JKFF_Q0 ? 1 : 0;
		WLL0   = ~(sysclk & (f2() == f2_ether_eodfct)) & 1;
	} else {
		// ISRFULL
		WLLOAD = (m_eth.serin >> 1) & 1;
		RDCNT0 = ~(sysclk & (bs() == bs_ether_eidfct)) & 1;
		RR     = bs() == bs_ether_eidfct || f1() == f1_ether_eilfct;
		WLL0   = m_eth.ff_77b & JKFF_Q0 ? 1 : 0;
	}
	// TODO: use the signals
	(void)RDCNT0;
	(void)RR;
	(void)WLL0;

	/*
	 * JK flip-flop 10b OBUSY (Sheet 13)
	 *
	 * CLK  SYSCLK'
	 * J    0
	 * K'   EOSFCT'
	 * S'   ERESET'
	 * C'   1
	 * Q    OBUSY'
	 * Q'   OBUSY
	 */
	s0 = m_eth.ff_10b;
	s1 = sysclk ? JKFF_CLK : JKFF_0;
	if (f2() != f2_ether_eosfct)
		s1 |= JKFF_K;
	m_eth.ff_10b = update_jkff(s0, s1, "10b OBUSY    ");

	/*
	 * JK flip-flop 51a EWFCT latch (Sheet 19)
	 *
	 * CLK  SYSCLK'
	 * J    OCDW
	 * K'   EWFCT'
	 * S'   ERESET'
	 * C'   1
	 * Q    EWFCT latch(?)
	 * Q'   ---
	 */
	s0 = m_eth.ff_51a;
	s1 = sysclk ? JKFF_CLK : JKFF_0;
	m_eth.ff_51a = update_jkff(s0, s1, "51a EWFCT_L  ");

	/*
	 * JK flip-flop 31b OEOT (Sheet 19)
	 *
	 * CLK  SYSCLK'
	 * J    0
	 * K'   EEFCT'
	 * S'   ERESET'
	 * C'   1
	 * Q    OEOT'
	 * Q'   ---
	 */
	s0 = m_eth.ff_31b;
	s1 = sysclk ? JKFF_CLK : JKFF_0;
	m_eth.ff_31b = update_jkff(s0, s1, "31b OEOT     ");

	/*
	 * JK flip-flop 69a IT (Sheet 14)
	 *
	 * CLK  ARC'
	 * J    (BNE & ILOC & IMID & WR')
	 * K'   1
	 * S'   1
	 * C'   ERESET'
	 * Q    INGONE
	 * Q'   INGONE'
	 */
	s0 = m_eth.ff_69a;
	s1 = JKFF_CLK;
	m_eth.ff_69a = update_jkff(s0, s1, "69a IT       ");

	/*
	 * JK flip-flop 65a IDL (Sheet 10)
	 *
	 * CLK  ARC'
	 * J    65b Q
	 * K'   1
	 * S'   1
	 * C'   ERESET'
	 * Q    IDL
	 * Q'   IDL'
	 */
	s0 = m_eth.ff_65a;
	s1 = JKFF_CLK;
	m_eth.ff_65a = update_jkff(s0, s1, "65a IDL      ");

	/*
	 * JK flip-flop 65b IO (Sheet 10)
	 *
	 * CLK  ISRFULL
	 * J    WLF
	 * K'   1
	 * S'   1
	 * C'   ERESET
	 * Q    to 65a J
	 * Q'   ---
	 */
	s0 = m_eth.ff_65b;
	s1 = JKFF_CLK;
	m_eth.ff_65b = update_jkff(s0, s1, "65b IO       ");

	/*
	 * JK flip-flop 77b WLF (Sheet 10)
	 *
	 * CLK  WLLOAD
	 * J    1
	 * K'   1
	 * S'   1
	 * C'   (BUSY | WE')
	 * Q    WLF
	 * Q'   WLF'
	 */
	s0 = m_eth.ff_77b;
	s1 = WLLOAD ? JKFF_CLK : JKFF_0;
	m_eth.ff_77b = update_jkff(s0, s1, "77b WLF      ");

	/*
	 * JK flip-flop 77a WR (Sheet 10)
	 *
	 * CLK  ARC'
	 * J    RW'
	 * K'   (WLF & BF')'
	 * S'   BUSY
	 * C'   1
	 * Q    WR'
	 * Q'   WR
	 */
	s0 = m_eth.ff_77a;
	s1 = JKFF_CLK;
	if (m_eth.ff_77b)
	m_eth.ff_77a = update_jkff(s0, s1, "77a WR       ");

	/*
	 * JK flip-flop 69b INON (Sheet 14)
	 *
	 * CLK  ARC'
	 * J    CARRIER'
	 * K'   (IMID' & ILOC)'
	 * S'   1
	 * C'   IBUSY
	 * Q    INON
	 * Q'   INON'
	 */
	s0 = m_eth.ff_69b;
	s1 = JKFF_CLK;
	m_eth.ff_69b = update_jkff(s0, s1, "69b INON     ");

	/*
	 * JK flip-flop 70b ILOC (Sheet 14)
	 *
	 * CLK  CARRIER'
	 * J    1
	 * K'   1
	 * S'   1
	 * C'   INON
	 * Q    ILOC
	 * Q'   ILOC'
	 */
	s0 = m_eth.ff_70b;
	s1 = JKFF_CLK;
	m_eth.ff_70b = update_jkff(s0, s1, "70b ILOC     ");

	/*
	 * JK flip-flop 51b OCDW (Sheet 19)
	 *
	 * CLK  ARC'
	 * J    (EWFCT latch | SWAKMRT')'
	 * K'   ETAC'
	 * S'   1
	 * C'   ERESET'
	 * Q    OCDW
	 * Q'   OCDW'
	 */
	s0 = m_eth.ff_51b;
	s1 = JKFF_CLK;
	m_eth.ff_51b = update_jkff(s0, s1, "51b OCDW     ");

	/*
	 * JK flip-flop 21a OUTON (Sheet 19)
	 *
	 * CLK  OTHER'
	 * J    OUTON
	 * K'   1
	 * S'   1
	 * C'   OBUSY
	 * Q    to FF 21b J and K'
	 * Q'   ---
	 */
	s0 = m_eth.ff_21a;
	s1 = JKFF_CLK;
	m_eth.ff_21a = update_jkff(s0, s1, "21a OUTON    ");

	/*
	 * JK flip-flop 21b COLL (Sheet 19)
	 *
	 * CLK  ARC'
	 * J    from FF 21a Q
	 * K'    dito
	 * S'   1
	 * C'   OBUSY
	 * Q    COLL
	 * Q'   COLL'
	 */
	s0 = m_eth.ff_21b;
	s1 = JKFF_CLK;
	m_eth.ff_21b = update_jkff(s0, s1, "21b COLL     ");

	/*
	 * JK flip-flop 31a OUTGONE (Sheet 19)
	 *
	 * CLK  OUTON'
	 * J    1
	 * K'   1
	 * S'   1
	 * C'   OBUSY
	 * Q    OUTGONE
	 * Q'   OUTGONE'
	 */
	s0 = m_eth.ff_31a;
	s1 = JKFF_CLK;
	m_eth.ff_31a = update_jkff(s0, s1, "31a OUTGONE  ");
}

/**
 * @brief update the ethernet circuit JK flip-flops that depend on RCLK
 * @param rclk current RCLK level 0 or 1
 */
void alto2_cpu_device::update_rclk(int rclk)
{
	uint8_t s0, s1;

	/*
	 * JK flip-flop 70a IMID (Sheet 14)
	 *
	 * CLK  RCLK
	 * J    ISR00
	 * K'   1
	 * S'   1
	 * C'   INON
	 * Q    IMID
	 * Q'   IMID'
	 */
	s0 = m_eth.ff_70a;
	s1 = rclk ? JKFF_CLK : JKFF_0;
	m_eth.ff_70a = update_jkff(s0, s1, "70a IMID     ");

	/*
	 * JK flip-flop 47a OUTON (Sheet 15)
	 *
	 * CLK  RCLK
	 * J    (ISR15 | ISRFULL)'
	 * K'    dito
	 * S'   INON
	 * C'   1
	 * Q    ---
	 * Q'   ISR14
	 */
	s0 = m_eth.ff_47a;
	s1 = rclk ? JKFF_CLK : JKFF_0;
	m_eth.ff_47a = update_jkff(s0, s1, "47a ISR14    ");

	/*
	 * JK flip-flop 47b COLL (Sheet 15)
	 *
	 * CLK  RCLK
	 * J    RDATA
	 * K'    dito
	 * S'   1
	 * C'   INON
	 * Q    ISR15
	 * Q'   ---
	 */
	s0 = m_eth.ff_47b;
	s1 = rclk ? JKFF_CLK : JKFF_0;
	m_eth.ff_47b = update_jkff(s0, s1, "47b ISR15    ");
}

/**
 * @brief update the ethernet circuit JK flip-flops that depend on TCLK
 * @param tclk current TCLK level 0 or 1
 */
void alto2_cpu_device::update_tclk(int tclk)
{
	uint8_t s0, s1;

	/*
	 * JK flip-flop 52b OSLOAD (Sheet 17)
	 *
	 * CLK  TCLK'
	 * J    PROM a42 O2
	 * K'    dito
	 * S'   1
	 * C'   1
	 * Q    OSLOAD'
	 * Q'   OSLOAD
	 */
	s0 = m_eth.ff_52b;
	s1 = tclk ? JKFF_0 : JKFF_CLK;
	m_eth.ff_52b = update_jkff(s0, s1, "52b OSLOAD   ");

	/*
	 * JK flip-flop 61a CRCGO (Sheet 21)
	 *
	 * CLK  TCLK'
	 * J    (OSLOAD & BE)
	 * K'   1
	 * S'   1
	 * C'   OUTEND'
	 * Q    CRCGO
	 * Q'   CRCGO'
	 */
	s0 = m_eth.ff_61a;
	s1 = tclk ? JKFF_0 : JKFF_CLK;
	m_eth.ff_61a = update_jkff(s0, s1, "61a CRCGO    ");

	/*
	 * JK flip-flop 61b OUTRGO (Sheet 21)
	 *
	 * CLK  TCLK'
	 * J    OUTGO
	 * K'    dito
	 * S'   1
	 * C'   OUTEND'
	 * Q    OUTRGO
	 * Q'   ---
	 */
	s0 = m_eth.ff_61b;
	s1 = tclk ? JKFF_0 : JKFF_CLK;
	m_eth.ff_61b = update_jkff(s0, s1, "61b OUTRGO   ");

	/*
	 * JK flip-flop 62a OUTGO (Sheet 21)
	 *
	 * CLK  TCLK'
	 * J    OUTON
	 * K'   1
	 * S'   1
	 * C'   OUTEND'
	 * Q    OUTGO
	 * Q'   ---
	 */
	s0 = m_eth.ff_62a;
	s1 = tclk ? JKFF_0 : JKFF_CLK;
	m_eth.ff_62a = update_jkff(s0, s1, "62a OUTGO    ");

	/*
	 * JK flip-flop 62b OUTON (Sheet 21)
	 *
	 * CLK  TCLK'
	 * J    (FEOT' | OOK')'
	 * K'   (CRCGO & OSLOAD)'
	 * S'   1
	 * C'   PESTOP'
	 * Q    OUTON
	 * Q'   OUTON'
	 */
	s0 = m_eth.ff_62b;
	s1 = tclk ? JKFF_0 : JKFF_CLK;
	m_eth.ff_62b = update_jkff(s0, s1, "62b OUTON    ");
}


/**
 * @brief ethernet task slot initialization
 */
void alto2_cpu_device::init_ether(int task)
{
	// intialize all ethernet variables
	memset(&m_eth, 0, sizeof(m_eth));
	save_item(NAME(m_eth.fifo));
	save_item(NAME(m_eth.fifo_rd));
	save_item(NAME(m_eth.fifo_wr));
	save_item(NAME(m_eth.status));
	save_item(NAME(m_eth.rx_crc));
	save_item(NAME(m_eth.tx_crc));
	save_item(NAME(m_eth.rx_count));
	save_item(NAME(m_eth.tx_count));
	save_item(NAME(m_eth.breath_of_life));

	m_ether_a41 = prom_load(machine(), &pl_enet_a41, memregion("ether_a41")->base());
	m_ether_a42 = prom_load(machine(), &pl_enet_a42, memregion("ether_a42")->base());
	m_ether_a49 = prom_load(machine(), &pl_enet_a49, memregion("ether_a49")->base());

	m_eth.rx_packet = std::make_unique<uint16_t[]>(sizeof(uint16_t)*ALTO2_ETHER_PACKET_SIZE);
	m_eth.tx_packet = std::make_unique<uint16_t[]>(sizeof(uint16_t)*ALTO2_ETHER_PACKET_SIZE);

	m_eth.tx_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(alto2_cpu_device::tx_packet),this));
	m_eth.tx_timer->reset();

	m_eth.rx_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(alto2_cpu_device::rx_breath_of_life),this));
	m_eth.rx_timer->reset();
}

void alto2_cpu_device::exit_ether()
{
	// nothing to do yet
}

//! delay between two breath_of_lifes in seconds
static const int breath_of_life_sec[8] = {
	0, 5, 10, 15, 30, 60, 90, 120
};
void alto2_cpu_device::reset_ether()
{
	memset(m_eth.fifo, 0, sizeof(m_eth.fifo));
	m_eth.fifo_rd = 0;
	m_eth.fifo_wr = 0;
	m_eth.status = 0;
	m_eth.rx_crc = 0;
	m_eth.tx_crc = 0;
	m_eth.rx_count = 0;
	m_eth.tx_count = 0;
	m_eth.breath_of_life = 0;
	m_eth.rx_timer->reset();
	m_eth.tx_timer->reset();
	ioport_port* config = ioport(":CONFIG");
	// config should be valid, unless the driver doesn't define it
	if (config)
		m_eth.breath_of_life = breath_of_life_sec[(config->read() >> 4) & 7];
	logerror("Ethernet breath_of_life %d sec\n", m_eth.breath_of_life);
	if (m_eth.breath_of_life)
		m_eth.rx_timer->adjust(attotime::from_seconds(m_eth.breath_of_life), 0);
}