// license:BSD-3-Clause
// copyright-holders:Juergen Buchmueller
/*****************************************************************************
 *
 *   Xerox AltoII CPU core interface
 *
 *****************************************************************************/
#ifndef MAME_CPU_ALTO2_ALTO2CPU_H
#define MAME_CPU_ALTO2_ALTO2CPU_H

#pragma once

#include "machine/diablo_hd.h"

#define ALTO2_TAG "alto2"

/**
 * \brief AltoII register names
 */
enum {
	// micro code task, micro program counter, next and next2
	A2_MPC = STATE_GENPC, A2_TASK = 0, A2_NEXT, A2_NEXT2,
	// BUS, ALU, temp, latch, memory latch and carry flags
	A2_BUS, A2_T, A2_ALU, A2_ALUC0, A2_L, A2_SHIFTER, A2_LALUC0, A2_M,
	A2_R,   // 32 R registers
	A2_AC3 = A2_R, A2_AC2, A2_AC1, A2_AC0, A2_R04, A2_R05, A2_PC,  A2_R07,
	A2_R10, A2_R11, A2_R12, A2_R13, A2_R14, A2_R15, A2_R16, A2_R17,
	A2_R20, A2_R21, A2_R22, A2_R23, A2_R24, A2_R25, A2_R26, A2_R27,
	A2_R30, A2_R31, A2_R32, A2_R33, A2_R34, A2_R35, A2_R36, A2_R37,
	A2_S,   // 32 S registers
	A2_S00 = A2_S, A2_S01, A2_S02, A2_S03, A2_S04, A2_S05, A2_S06, A2_S07,
	A2_S10, A2_S11, A2_S12, A2_S13, A2_S14, A2_S15, A2_S16, A2_S17,
	A2_S20, A2_S21, A2_S22, A2_S23, A2_S24, A2_S25, A2_S26, A2_S27,
	A2_S30, A2_S31, A2_S32, A2_S33, A2_S34, A2_S35, A2_S36, A2_S37,
	// DISK controller registers
	A2_DRIVE, A2_KADDR, A2_KADR, A2_KSTAT, A2_KCOM, A2_KRECNO,
	A2_SHIFTIN, A2_SHIFTOUT, A2_DATAIN, A2_DATAOUT, A2_KRWC,
	A2_KFER, A2_WDTSKENA, A2_WDINIT0, A2_WDINIT, A2_STROBE,
	A2_BITCLK, A2_DATIN, A2_BITCNT, A2_CARRY, A2_SECLATE,
	A2_SEEKOK, A2_OKTORUN, A2_READY
};

#ifndef ALTO2_DEBUG
//! Define to 1 to enable logerror() output.
#define ALTO2_DEBUG             1
#endif

//!< Define to 1 to use the F9318 priority encoder code (broken).
#define USE_PRIO_F9318          0

#define ALTO2_TASKS             16          //!< 16 task slots
#define ALTO2_REGS              32          //!< 32 16-bit words in the R register file
#define ALTO2_ALUF              16          //!< 16 ALU functions (74181)
#define ALTO2_BUSSRC            8           //!< 8 bus sources
#define ALTO2_F1MAX             16          //!< 16 F1 functions
#define ALTO2_F2MAX             16          //!< 16 F2 functions
#define ALTO2_UCYCLE            169542      //!< time in pico seconds for a CPU micro cycle: 29.4912MHz/5 -> 5.898240Hz ~= 169.542ns/clock

#define ALTO2_CONST_SIZE        256         //!< number words in the constant ROM

//! Inverted bits in the micro instruction 32 bit word.
#define ALTO2_UCODE_INVERTED    ((1 << 10) | (1 << 15) | (1 << 19))

/********************************************************************************
 * Bit field primitives
 * These are some macros to make it easier to access variable by the bit-
 * reversed notation that the Xerox Alto documents use all over the place.
 * Bit number 0 is the most significant there,
 * and bit number (width - 1) is the least significant.
 * The X_ is for Xerox and to avoid collisions with MAME generic macros.
 ********************************************************************************/

//! get the left shift required to access bit %to in a word of %width bits
#define X_BITSHIFT(width,to) ((width) - 1 - (to))

//! build a least significant bit mask for bits %from to %to (inclusive)
#define X_BITMASK(from,to) ((1ul << ((to) + 1 - (from))) - 1)

//! get a single bit number %bit value from %reg, a word of %width bits
#define X_BIT(reg,width,bit) (((reg) >> X_BITSHIFT(width,bit)) & 1)

//! get a bit field from %reg, a word of %width bits, starting at bit %from until bit %to
#define X_RDBITS(reg,width,from,to) (((reg) >> X_BITSHIFT(width,to)) & X_BITMASK(from,to))

//! put a value %val into %reg, a word of %width bits, starting at bit %from until bit %to
#define X_WRBITS(reg,width,from,to,val) do { \
	uint32_t mask = X_BITMASK(from,to) << X_BITSHIFT(width,to); \
	reg = ((reg) & ~mask) | (((val) << X_BITSHIFT(width,to)) & mask); \
} while (0)

#if ALTO2_DEBUG
	enum LOG_TYPE_ENUM {
		LOG_0,
		LOG_CPU     = (1 <<  0),
		LOG_EMU     = (1 <<  1),
		LOG_T01     = (1 <<  2),
		LOG_T02     = (1 <<  3),
		LOG_T03     = (1 <<  4),
		LOG_KSEC    = (1 <<  5),
		LOG_T05     = (1 <<  6),
		LOG_T06     = (1 <<  7),
		LOG_ETH     = (1 <<  8),
		LOG_MRT     = (1 <<  9),
		LOG_DWT     = (1 << 10),
		LOG_CURT    = (1 << 11),
		LOG_DHT     = (1 << 12),
		LOG_DVT     = (1 << 13),
		LOG_PART    = (1 << 14),
		LOG_KWD     = (1 << 15),
		LOG_T17     = (1 << 16),
		LOG_MEM     = (1 << 17),
		LOG_RAM     = (1 << 18),
		LOG_DRIVE   = (1 << 19),
		LOG_DISK    = (1 << 20),
		LOG_DISPL   = (1 << 21),
		LOG_MOUSE   = (1 << 22),
		LOG_HW      = (1 << 23),
		LOG_KBD     = (1 << 24),
		LOG_ALL     = ((1 << 25) - 1)
	};
	class alto2_log_t
	{
	public:
		template <typename... Params>
		void operator()(device_t *device, int type, int level, const char* format, Params &&...args)
		{
			if (!(log_types & type))
				return;
			if (level > log_level)
				return;
			if (log_newline) {
				// last line had a \n - print type name
				for (int i = 0; i < type_name_count; i++)
					if (type & (1 << i))
						device->logerror("%-7s ", type_name[i]);
			}
			device->logerror(format, std::forward<Params>(args)...);
			log_newline = format[strlen(format) - 1] == '\n';
		}
	private:
		static int log_types;
		static int log_level;
		static bool log_newline;
		static const char *const type_name[];
		static const size_t type_name_count;
	};
	extern alto2_log_t logprintf;
#   define  LOG(x) logprintf x
#else
#   define  LOG(x)
#endif

//*******************************************
// define constants from the sub-devices
//*******************************************
#define ALTO2_DEFINE_CONSTANTS 1
#include "a2jkff.h"
#include "a2ram.h"
#include "a2hw.h"
#include "a2kbd.h"
#include "a2mouse.h"
#include "a2disk.h"
#include "a2disp.h"
#include "a2mem.h"
#include "a2emu.h"
#include "a2ksec.h"
#include "a2ether.h"
#include "a2mrt.h"
#include "a2dwt.h"
#include "a2curt.h"
#include "a2dht.h"
#include "a2dvt.h"
#include "a2part.h"
#include "a2dwt.h"
#include "a2kwd.h"
#undef ALTO2_DEFINE_CONSTANTS

class alto2_cpu_device :  public cpu_device
{
public:
	// construction/destruction
	alto2_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
	~alto2_cpu_device();

	auto kb_read_callback() { return m_kb_read_callback.bind(); }
	auto utilout_callback() { return m_utilout_callback.bind(); }

	//! driver interface to set diablo_hd_device
	template <typename T> void set_diablo(int unit, T &&tag) { m_drive[unit].set_tag(std::forward<T>(tag)); }

	//! call in for the next sector callback
	void next_sector(int unit);

	//! update the screen bitmap
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	//! set the vblank bit for the display to synch upon
	void screen_vblank();
	//! register a mouse motion in x direction
	DECLARE_INPUT_CHANGED_MEMBER( mouse_motion_x );
	//! register a mouse motion in y direction
	DECLARE_INPUT_CHANGED_MEMBER( mouse_motion_y );
	//! register a mouse button change
	DECLARE_INPUT_CHANGED_MEMBER( mouse_button_0 );
	DECLARE_INPUT_CHANGED_MEMBER( mouse_button_1 );
	DECLARE_INPUT_CHANGED_MEMBER( mouse_button_2 );

protected:
	//! device-level override for start
	virtual void device_start() override ATTR_COLD;
	//! device-level override for reset
	virtual void device_reset() override ATTR_COLD;

	//! device-level override for post reset
	void interface_post_reset() override;

	//! device_execute_interface overrides
	virtual uint32_t execute_min_cycles() const noexcept override { return 1; }
	virtual uint32_t execute_max_cycles() const noexcept override { return 1; }
	virtual void execute_run() override;
	virtual void execute_set_input(int inputnum, int state) override;

	//! device_memory_interface overrides
	virtual space_config_vector memory_space_config() const override;

	//! device (P)ROMs
	virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
	//! device_state_interface overrides
	virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;

	//! device_disasm_interface overrides
	virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;

private:
	devcb_read16 m_kb_read_callback;
	devcb_write16 m_utilout_callback;

	address_space_config m_ucode_config;
	address_space_config m_const_config;
	address_space_config m_iomem_config;

	address_space* m_iomem;

	uint32_t m_cram_config;                   //!< CROM/CRAM configuration (1 .. 3)
	uint32_t m_ucode_rom_pages;               //!< Number of CROM pages; derived from m_cram_config
	uint32_t m_ucode_ram_pages;               //!< Number of CRAM pages; derived from m_cram_config
	uint32_t m_ucode_ram_base;                //!< Base offset of the CRAM addresses
	uint32_t m_ucode_size;                    //!< Size of both, CROM and CRAM together
	uint32_t m_sreg_banks;                    //!< Number of S register banks; derived from m_cram_config

	std::unique_ptr<uint32_t[]> m_ucode_crom;
	std::unique_ptr<uint32_t[]> m_ucode_cram;
	std::unique_ptr<uint16_t[]> m_const_data;

	void ucode_map(address_map &map) ATTR_COLD;
	void const_map(address_map &map) ATTR_COLD;
	void iomem_map(address_map &map) ATTR_COLD;

	//! read microcode CROM or CRAM, depending on m_ucode_ram_base
	uint32_t crom_cram_r(offs_t offset);

	//! write microcode CRAM, depending on m_ucode_ram_base (ignore writes to CROM)
	void crom_cram_w(offs_t offset, uint32_t data);

	//! read constants PROM
	uint16_t const_r(offs_t offset);

	//! read i/o space RAM
	uint16_t ioram_r(offs_t offset);

	//!< write i/o space RAM
	void ioram_w(offs_t offset, uint16_t data);

	int m_icount;

	//! task numbers
	enum {
		task_emu,       //!< emulator task
		task_1,         //!< unused
		task_2,         //!< unused
		task_3,         //!< unused
		task_ksec,      //!< disk sector task
		task_5,         //!< unused
		task_6,         //!< unused
		task_ether,     //!< ethernet task
		task_mrt,       //!< memory refresh task
		task_dwt,       //!< display word task
		task_curt,      //!< cursor task
		task_dht,       //!< display horizontal task
		task_dvt,       //!< display vertical task
		task_part,      //!< parity task
		task_kwd,       //!< disk word task
		task_17         //!< unused task slot 017
	};

	//! register select values accessing R (Note: register numbers are octal)
	enum {
		rsel_ac3,       //!< AC3 used by emulator as accu 3. Also used by Mesa emulator to keep bytecode to execute after breakpoint
		rsel_ac2,       //!< AC2 used by emulator as accu 2. Also used by Mesa emulator as x register for xfer
		rsel_ac1,       //!< AC1 used by emulator as accu 1. Also used by Mesa emulator as r-temporary for return indices and values
		rsel_ac0,       //!< AC0 used by emulator as accu 0. Also used by Mesa emulator as new field bits for WF and friends
		rsel_r04,       //!< NWW state of the interrupt system
		rsel_r05,       //!< SAD. Also used by Mesa emulator as scratch R-register for counting
		rsel_pc,        //!< PC used by emulator as program counter
		rsel_r07,       //!< XREG. Also used by Mesa emulator as task hole, i.e. pigeonhole for saving things across tasks.
		rsel_r10,       //!< XH. Also used by Mesa emulator as instruction byte register
		rsel_r11,       //!< CLOCKTEMP - used in the MRT
		rsel_r12,       //!< ECNTR remaining words in buffer - ETHERNET
		rsel_r13,       //!< EPNTR points BEFORE next word in buffer - ETHERNET
		rsel_r14,
		rsel_r15,       //!< MPC. Used by the Mesa emulator as program counter
		rsel_r16,       //!< STKP. Used by the Mesa emulator as stack pointer [0-10] 0 empty, 10 full
		rsel_r17,       //!< XTSreg. Used by the Mesa emulator to xfer trap state
		rsel_r20,       //!< CURX. Holds cursor X; used by the cursor task
		rsel_r21,       //!< CURDATA. Holds the cursor data; used by the cursor task
		rsel_r22,       //!< CBA. Holds the address of the currently active DCB+1
		rsel_r23,       //!< AECL. Holds the address of the end of the current scanline's bitmap
		rsel_r24,       //!< SLC. Holds the number of scanlines remaining in currently active DCB
		rsel_r25,       //!< MTEMP. Holds the temporary cell
		rsel_r26,       //!< HTAB. Holds the number of tab words remaining on current scanline
		rsel_r27,       //!< YPOS
		rsel_r30,       //!< DWA. Holds the address of the bit map doubleword currently being fetched for transmission to the hardware buffer.
		rsel_r31,       //!< KWDCT. Used by the disk tasks as word counter
		rsel_r32,       //!< CKSUMR. Used by the disk tasks as checksum register (and *amble counter?)
		rsel_r33,       //!< KNMAR. Used by the disk tasks as transfer memory address register
		rsel_r34,       //!< DCBR. Used by the disk tasks to keep the current device control block
		rsel_r35,       //!< TEMP. Used by the Mesa emulator, and also by BITBLT
		rsel_r36,       //!< TEMP2. Used by the Mesa emulator, and also by BITBLT
		rsel_r37        //!< CLOCKREG. Low order bits of the real time clock
	};

	//! ALU function numbers
	enum {
		/**
		 * \brief 00: ALU <- BUS
		 * PROM data for S3-0,M,C,T: 1111/1/0/0
		 * function F=A
		 * T source is ALU
		 */
		aluf_bus__alut,
		/**
		 * \brief 01: ALU <- T
		 * PROM data for S3-0,M,C,T: 1010/1/0/0
		 * function F=B
		 * T source is BUS
		 */
		aluf_treg,
		/**
		 * \brief 02: ALU <- BUS | T
		 * PROM data for S3-0,M,C,T: 1110/1/0/1
		 * function F=A|B
		 * T source is ALU
		 */
		aluf_bus_or_t__alut,
		/**
		 * \brief 03: ALU <- BUS & T
		 * PROM data for S3-0,M,C,T: 1011/1/0/0
		 * function F=A&B
		 * T source is BUS
		 */
		aluf_bus_and_t,
		/**
		 * \brief 04: ALU <- BUS ^ T
		 * PROM data for S3-0,M,C,T: 0110/1/0/0
		 * function F=A^B
		 * T source is BUS
		 */
		aluf_bus_xor_t,
		/**
		 * \brief 05: ALU <- BUS + 1
		 * PROM data for S3-0,M,C,T: 0000/0/0/1
		 * function F=A+1
		 * T source is ALU
		 */
		aluf_bus_plus_1__alut,
		/**
		 * \brief 06: ALU <- BUS - 1
		 * PROM data for S3-0,M,C,T: 1111/0/1/1
		 * function F=A-1
		 * T source is ALU
		 */
		aluf_bus_minus_1__alut,
		/**
		 * \brief 07: ALU <- BUS + T
		 * PROM data for S3-0,M,C,T: 1001/0/1/0
		 * function F=A+B
		 * T source is BUS
		 */
		aluf_bus_plus_t,
		/**
		 * \brief 10: ALU <- BUS - T
		 * PROM data for S3-0,M,C,T: 0110/0/0/0
		 * function F=A-B
		 * T source is BUS
		 */
		aluf_bus_minus_t,
		/**
		 * \brief 11: ALU <- BUS - T - 1
		 * PROM data for S3-0,M,C,T: 0110/0/1/0
		 * function F=A-B-1
		 * T source is BUS
		 */
		aluf_bus_minus_t_minus_1,
		/**
		 * \brief 12: ALU <- BUS + T + 1
		 * PROM data for S3-0,M,C: 1001/0/0
		 * function F=A+B+1
		 * T source is ALU
		 */
		aluf_bus_plus_t_plus_1__alut,
		/**
		 * \brief 13: ALU <- BUS + SKIP
		 * PROM data for S3-0,M,C,T: 0000/0/SKIP/1
		 * function F=A (SKIP=1) or F=A+1 (SKIP=0)
		 * T source is ALU
		 */
		aluf_bus_plus_skip__alut,
		/**
		 * \brief 14: ALU <- BUS & T
		 * PROM data for S3-0,M,C,T: 1011/1/0/1
		 * function F=A&B
		 * T source is ALU
		 */
		aluf_bus_and_t__alut,
		/**
		 * \brief 15: ALU <- BUS & ~T
		 * PROM data for S3-0,M,C,T: 0111/1/0/0
		 * function F=A&~B
		 * T source is BUS
		 */
		aluf_bus_and_not_t,
		/**
		 * \brief 16: ALU <- BUS
		 * PROM data for S3-0,M,C,T: 1111/1/0/1
		 * function F=A
		 * T source is ALU
		 */
		aluf_undef_16,
		/**
		 * \brief 17: ALU <- BUS
		 * PROM data for S3-0,M,C,T: 1111/1/0/1
		 * function F=A
		 * T source is ALU
		 */
		aluf_undef_17
	};

	//! BUS source selection numbers
	enum {
		bs_read_r,                          //!< BUS source is R register
		bs_load_r,                          //!< load R register from BUS
		bs_no_source,                       //!< BUS is open (0177777)
		bs_task_3,                          //!< BUS source is task specific
		bs_task_4,                          //!< BUS source is task specific
		bs_read_md,                         //!< BUS source is memory data
		bs_mouse,                           //!< BUS source is mouse data
		bs_disp                             //!< BUS source displacement (emulator task)
	};

	//! Function 1 numbers
	enum {
		f1_nop,                             //!< f1 00 no operation
		f1_load_mar,                        //!< f1 01 load memory address register
		f1_task,                            //!< f1 02 task switch
		f1_block,                           //!< f1 03 task block
		f1_l_lsh_1,                         //!< f1 04 left shift L once
		f1_l_rsh_1,                         //!< f1 05 right shift L once
		f1_l_lcy_8,                         //!< f1 06 cycle L 8 times
		f1_const,                           //!< f1 07 constant from PROM

		f1_task_10,                         //!< f1 10 task specific
		f1_task_11,                         //!< f1 11 task specific
		f1_task_12,                         //!< f1 12 task specific
		f1_task_13,                         //!< f1 13 task specific
		f1_task_14,                         //!< f1 14 task specific
		f1_task_15,                         //!< f1 15 task specific
		f1_task_16,                         //!< f1 16 task specific
		f1_task_17                          //!< f1 17 task specific
	};

	//! Function 2 numbers
	enum {
		f2_nop,                             //!< f2 00 no operation
		f2_bus_eq_zero,                     //!< f2 01 branch on bus equals 0
		f2_shifter_lt_zero,                 //!< f2 02 branch on shifter less than 0
		f2_shifter_eq_zero,                 //!< f2 03 branch on shifter equals 0
		f2_bus,                             //!< f2 04 branch on BUS[6-15]
		f2_alucy,                           //!< f2 05 branch on (latched) ALU carry
		f2_load_md,                         //!< f2 06 load memory data
		f2_const,                           //!< f2 07 constant from PROM

		f2_task_10,                         //!< f2 10 task specific
		f2_task_11,                         //!< f2 11 task specific
		f2_task_12,                         //!< f2 12 task specific
		f2_task_13,                         //!< f2 13 task specific
		f2_task_14,                         //!< f2 14 task specific
		f2_task_15,                         //!< f2 15 task specific
		f2_task_16,                         //!< f2 16 task specific
		f2_task_17                          //!< f2 17 task specific
	};

	//! enumeration of the micro code word bits
	//! Note: The Alto documents enumerate bits from left (MSB = 0) to right (LSB = 31)
	enum {
		DRSEL0, DRSEL1, DRSEL2, DRSEL3, DRSEL4,
		DALUF0, DALUF1, DALUF2, DALUF3,
		DBS0, DBS1, DBS2,
		DF1_0, DF1_1, DF1_2, DF1_3,
		DF2_0, DF2_1, DF2_2, DF2_3,
		DLOADT,
		DLOADL,
		NEXT0, NEXT1, NEXT2, NEXT3, NEXT4, NEXT5, NEXT6, NEXT7, NEXT8, NEXT9
	};

	//! get the normally accessed bank number from a bank register
	static inline uint16_t GET_BANK_NORMAL(uint16_t breg) { return X_RDBITS(breg,16,12,13); }

	//! get the extended bank number (accessed via XMAR) from a bank register
	static inline uint16_t GET_BANK_EXTENDED(uint16_t breg) { return X_RDBITS(breg,16,14,15); }

	//! get an ignored bit field from a control RAM address
	static inline uint16_t GET_CRAM_IGNORE(uint16_t addr) { return X_RDBITS(addr,16,0,1); }

	//! get the bank select bit field from a control RAM address
	static inline uint16_t GET_CRAM_BANKSEL(uint16_t addr) { return X_RDBITS(addr,16,2,3); }

	//! get the ROM/RAM flag from a control RAM address
	static inline uint16_t GET_CRAM_RAMROM(uint16_t addr) { return X_RDBITS(addr,16,4,4); }

	//! get the half select flag from a control RAM address
	static inline uint16_t GET_CRAM_HALFSEL(uint16_t addr) { return X_RDBITS(addr,16,5,5); }

	//! get the word address bit field from a control RAM address
	static inline uint16_t GET_CRAM_WORDADDR(uint16_t addr) { return X_RDBITS(addr,16,6,15); }

	uint16_t m_task_mpc[ALTO2_TASKS];                 //!< per task micro program counter
	uint16_t m_task_next2[ALTO2_TASKS];               //!< per task address modifier
	uint8_t m_task;                                   //!< active task
	uint8_t m_next_task;                              //!< next micro instruction's task
	uint8_t m_next2_task;                             //!< next but one micro instruction's task
	uint16_t m_mpc;                                   //!< micro program counter
	uint32_t m_mir;                                   //!< micro instruction register

	inline uint32_t rsel() const { return X_RDBITS(m_mir, 32, DRSEL0, DRSEL4); }
	inline uint32_t aluf() const { return X_RDBITS(m_mir, 32, DALUF0, DALUF3); }
	inline uint32_t bs() const { return X_RDBITS(m_mir, 32, DBS0, DBS2); }
	inline uint32_t f1() const { return X_RDBITS(m_mir, 32, DF1_0, DF1_3); }
	inline uint32_t f2() const { return X_RDBITS(m_mir, 32, DF2_0, DF2_3); }
	inline uint32_t loadt() const { return X_BIT(m_mir, 32, DLOADT); }
	inline uint32_t loadl() const { return X_BIT(m_mir, 32, DLOADL); }
	inline uint32_t next() const { return X_RDBITS(m_mir, 32, NEXT0, NEXT9); }

	/**
	 * \brief current micro instruction's register selection
	 * The emulator F2s ACSOURCE and ACDEST modify this.
	 * Note: The S registers are addressed by the original RSEL[0-4],
	 * even when the emulator modifies this.
	 */
	uint8_t m_rsel;
	uint16_t m_next;                                  //!< current micro instruction's next
	uint16_t m_next2;                                 //!< next micro instruction's next
	uint16_t m_r[ALTO2_REGS];                         //!< R register file
	uint16_t m_s[8][ALTO2_REGS];                      //!< S register file(s) (1 or 8 are used)
	uint16_t m_bus;                                   //!< wired-AND bus
	uint16_t m_t;                                     //!< T register
	uint16_t m_alu;                                   //!< the current ALU
	uint16_t m_aluc0;                                 //!< the current ALU carry output
	uint16_t m_l;                                     //!< L register
	uint16_t m_shifter;                               //!< shifter output
	uint16_t m_laluc0;                                //!< the latched ALU carry output
	uint16_t m_myl;                                     //!< M register of RAM related tasks (MYL latch in the schematics)
	uint16_t m_cram_addr;                             //!< constant RAM address
	uint16_t m_task_wakeup;                           //!< task wakeup: bit 1<<n set if task n requesting service

	uint16_t m_reset_mode;                            //!< reset mode register: bit 1<<n set if task n starts in ROM
	bool m_rdram_flag;                              //!< set by rdram, action happens on next cycle
	bool m_wrtram_flag;                             //!< set by wrtram, action happens on next cycle

	uint8_t m_s_reg_bank[ALTO2_TASKS];                //!< active S register bank per task
	uint8_t m_bank_reg[ALTO2_TASKS];                  //!< normal and extended RAM banks per task
	bool m_ether_enable;                            //!< set to true, if the ethernet should be simulated
	bool m_ewfct;                                   //!< set by Ether task when it want's a wakeup at switch to task_mrt
	attoseconds_t m_display_time;                   //!< display time accu (display state machine)
	attoseconds_t m_unload_time;                    //!< unload word time accu (display FIFO)
	int m_unload_word;                              //!< unload word number
	attoseconds_t m_bitclk_time;                    //!< bitclk call time accu (disk shift register)
	int m_bitclk_index;                             //!< bitclk index (bit number in sector)

	static const char *task_name(int task);         //!< human readable task names
	static const char *r_name(uint8_t reg);           //!< human readable register names
	static const char *aluf_name(uint8_t aluf);       //!< human readable ALU function names
	static const char *bs_name(uint8_t bs);           //!< human readable bus source names
	static const char *f1_name(uint8_t f1);           //!< human readable F1 function names
	static const char *f2_name(uint8_t f2);           //!< human readable F2 function names

	/**
	 * @brief 2KCTL PROM u3 - 256x4
	 * <PRE>
	 * PROM u3 is 256x4 type 3601-1, looks like SN74387, and it
	 * controls NEXT[6-9]', i.e. the outputs are wire-AND to NEXT
	 *
	 *           SN74387
	 *         +---+-+---+
	 *         |   +-+   |
	 *    A6  -|1      16|-  Vcc
	 *         |         |
	 *    A5  -|2      15|-  A7
	 *         |         |
	 *    A4  -|3      14|-  FE1'
	 *         |         |
	 *    A3  -|4      13|-  FE2'
	 *         |         |
	 *    A0  -|5      12|-  D0
	 *         |         |
	 *    A1  -|6      11|-  D1
	 *         |         |
	 *    A2  -|7      10|-  D2
	 *         |         |
	 *   GND  -|8       9|-  D3
	 *         |         |
	 *         +---------+
	 *
	 *
	 * It is enabled whenever the Emulator task is active and:
	 *  both F2[0] and F2[1] are 1  F2 functions 014, 015, 016, 017
	 *  F2=14 is 0                  not for F2 = 14 (load IR<-)
	 *  IR[0] is 0                  not for arithmetic group
	 *
	 * This means it controls the F2 functions 015:IDISP<- and 016:<-ACSOURCE
	 *
	 * Its address lines are:
	 *  line   pin   connected to         load swap
	 *  -------------------------------------------------------------------
	 *  A0     5     F2[2] (i.e. MIR[18]) IR[07]
	 *  A1     6     IR[01]               IR[06]
	 *  A2     7     IR[02]               IR[05]
	 *  A3     4     IR[03]               IR[04]
	 *  A4     3     IR[04]               IR[03]
	 *  A5     2     IR[05]               IR[02]
	 *  A6     1     IR[06]               IR[01]
	 *  A7     15    IR[07]               F2[2]
	 *
	 * Its data lines are:
	 *  line   pin   connected to         load
	 *  -------------------------------------------------------------------
	 *  D3     9     NEXT[06]'            NEXT[06]
	 *  D2     10    NEXT[07]'            NEXT[07]
	 *  D1     11    NEXT[08]'            NEXT[08]
	 *  D0     12    NEXT[09]'            NEXT[09]
	 *
	 * Its address lines are reversed at load time to make it easier to
	 * access it. Also both, address and data lines, are inverted.
	 * </PRE>
	 */
	std::unique_ptr<uint8_t[]> m_ctl2k_u3;

	/**
	 * @brief 2KCTL PROM u38; 82S23; 32x8 bit
	 * <PRE>
	 *
	 *            82S23
	 *         +---+-+---+
	 *         |   +-+   |
	 *    B0  -|1      16|-  Vcc
	 *         |         |
	 *    B1  -|2      15|-  EN'
	 *         |         |
	 *    B2  -|3      14|-  A4
	 *         |         |
	 *    B3  -|4      13|-  A3
	 *         |         |
	 *    B4  -|5      12|-  A2
	 *         |         |
	 *    B5  -|6      11|-  A1
	 *         |         |
	 *    B6  -|7      10|-  A0
	 *         |         |
	 *   GND  -|8       9|-  B7
	 *         |         |
	 *         +---------+
	 *
	 * Task priority encoder
	 *
	 *  line   pin    signal
	 *  -------------------------------
	 *  A0     10     CT1 (current task LSB)
	 *  A1     11     CT2
	 *  A2     12     CT4
	 *  A3     13     CT8 (current task MSB)
	 *  A4     14     0 (GND)
	 *
	 *  line   pin    signal
	 *  -------------------------------
	 *  B0     1      RDCT8'
	 *  B1     2      RDCT4'
	 *  B2     3      RDCT2'
	 *  B3     4      RDCT1'
	 *  B4     5      NEXT[09]'
	 *  B5     6      NEXT[08]'
	 *  B6     7      NEXT[07]'
	 *  B7     9      NEXT[06]'
	 * </PRE>
	 */
	std::unique_ptr<uint8_t[]> m_ctl2k_u38;

	//! output lines of the 2KCTL U38 PROM
	enum {
		U38_RDCT8,
		U38_RDCT4,
		U38_RDCT2,
		U38_RDCT1,
		U38_NEXT09,
		U38_NEXT08,
		U38_NEXT07,
		U38_NEXT06
	};

	/**
	 * @brief 2KCTL PROM u76; P3601-1; 256x4; PC0I and PC1I decoding
	 * <PRE>
	 * Replacement for u51, which is used in 1KCTL
	 *
	 *           SN74387
	 *         +---+-+---+
	 *         |   +-+   |
	 *    A6  -|1      16|-  Vcc
	 *         |         |
	 *    A5  -|2      15|-  A7
	 *         |         |
	 *    A4  -|3      14|-  FE1'
	 *         |         |
	 *    A3  -|4      13|-  FE2'
	 *         |         |
	 *    A0  -|5      12|-  D0
	 *         |         |
	 *    A1  -|6      11|-  D1
	 *         |         |
	 *    A2  -|7      10|-  D2
	 *         |         |
	 *   GND  -|8       9|-  D3
	 *         |         |
	 *         +---------+
	 *
	 *  input line    signal
	 *  ----------------------------
	 *  A7    15      EMACT'
	 *  A6    1       F1(0)
	 *  A5    2       F1(1)'
	 *  A4    3       F1(2)'
	 *  A3    4       F1(3)'
	 *  A2    7       0 (GND)
	 *  A1    6       PC1O
	 *  A0    5       PC0O
	 *
	 *  output line   signal
	 *  ----------------------------
	 *  D0     12     PC1T
	 *  D1     11     PC1F
	 *  D2     10     PC0T
	 *  D3     9      PC0F
	 *
	 * The outputs are connected to a dual 4:1 demultiplexer 74S153, so that
	 * depending on NEXT01' and RESET the following signals are passed through:
	 *
	 *  RESET  NEXT[01]'  PC0I    PC1I
	 *  --------------------------------------
	 *  0      0          PC0T    PC1T
	 *  0      1          PC0F    PC1F
	 *  1      0          PC0I4   T14 (?)
	 *  1      1          -"-     -"-
	 *
	 * This selects the microcode "page" to jump to on SWMODE (F1 = 010)
	 * depending on the current NEXT[01]' level.
	 * </PRE>
	 */
	std::unique_ptr<uint8_t[]> m_ctl2k_u76;

	/**
	 * @brief 3k CRAM PROM a37
	 */
	std::unique_ptr<uint8_t[]> m_cram3k_a37;

	/**
	 * @brief memory addressing PROM a64
	 */
	std::unique_ptr<uint8_t[]> m_madr_a64;

	/**
	 * @brief memory addressing PROM a65
	 */
	std::unique_ptr<uint8_t[]> m_madr_a65;

	/**
	 * @brief unused PROM a90
	 * Data sheet 05a_AIM.pdf page 14
	 * inputs A0-A7 from R0-R7 (?)
	 * output   signal
	 * -------------------
	 * Q0       KP3
	 * Q1       KP4
	 * Q2       KP5
	 * Q3       unused
	 *
	 * I haven't found yet where KP3-KP5 are used
	 */
	std::unique_ptr<uint8_t[]> m_madr_a90;

	/**
	 * @brief unused PROM a91
	 * Data sheet 05a_AIM.pdf page 14
	 * inputs A0-A7 from R0-R7 (?)
	 *
	 * Output   Signal
	 * -------------------
	 * Q0       KP0
	 * Q1       KP1
	 * Q2       KP2
	 * Q3       unused
	 * KP0-KP3 are decoded using 7442 a78 to select
	 * the keyboard row enable
	 *
	 * Enable   Key[0]  Key[1]  Key[2]  Key[3]  Key[4]  Key[5]  Key[6]  Key[7]
	 * ------------------------------------------------------------------------
	 * KE(0)    KB(R)   KB(1)   KB(3)   KB(5)   KB(T)   KB(ESC)  KB(2)  KB(4)
	 * KE(1)    KB(G)   KB(TAB) KB(W)   KB(6)   KB(Y)   KB(F)    KB(0)  KB(E)
	 * KE(2)    KB(H)   KB(CTL) KB(S)   KB(7)   KB(8)   KB(C)    KB(A)  KB(D)
	 * KE(3)    KB(N)   KB(J)   KB(9)   KB(U)   KB(M)   KB(B)    KB(I)  KB(V)
	 * KE(4)    KB(LCK) KB(Z)   KB(X)   KB(Q)   KB(SPC) KB(^R)   KB(O)  KB(K)
	 * KE(5)    KB([)   KB(.)   KB(L)   KB(-)   KB(+)   KB(;)    KB(,)  KB(P)
	 * KE(6)    KB(^L)  KB(RTN) KB(")   KB(/)   KB(S3)  KB(<-)    KB(])  KB(\)
	 * KE(7)    KB(S1)  KB(DEL) KB(S2)  KB(LF)  KB(S4)  KB(S5)   KB(BW) KB(BS)
	 */
	std::unique_ptr<uint8_t[]> m_madr_a91;

	/**
	 * @brief ALU function to 74181 operation lookup PROM
	 */
	std::unique_ptr<uint8_t[]> m_alu_a10;

	//! output lines of the ALU a10 PROM
	enum {
		A10_UNUSED  = (1 << 0),
		A10_TSELECT = (1 << 1),
		A10_ALUCI   = (1 << 2),
		A10_ALUM    = (1 << 3),
		A10_ALUS0   = (1 << 4),
		A10_ALUS1   = (1 << 5),
		A10_ALUS2   = (1 << 6),
		A10_ALUS3   = (1 << 7),
		A10_ALUIN   = (A10_ALUM|A10_ALUCI|A10_ALUS0|A10_ALUS1|A10_ALUS2|A10_ALUS3)
	};

	//! no operating function to put in the m_bs, m_f1 and m_f2 slots
	void noop() {}

	bool m_ram_related[ALTO2_TASKS];                //!< set when task is RAM related

	uint64_t m_cycle;                                 //!< number of cycles executed in the current slice

	uint64_t cycle() { return m_cycle; }              //!< return the current CPU cycle
	uint64_t ntime() { return m_cycle*ALTO2_UCYCLE/1000; }    //!< return the current nano seconds

	void hard_reset();                              //!< reset the various registers
	void soft_reset();                              //!< soft reset

	[[noreturn]] void bs_early_bad();               //! bs dummy early function
	[[noreturn]] void bs_late_bad();                //! bs dummy late function

	[[noreturn]] void f1_early_bad();               //! f1 dummy early function
	[[noreturn]] void f1_late_bad();                //! f1 dummy late function

	[[noreturn]] void f2_early_bad();               //! f2 dummy early function
	[[noreturn]] void f2_late_bad();                //! f2 dummy late function

	uint16_t noop_r (offs_t offset);                //!< read open bus (0177777)
	void noop_w(offs_t offset, uint16_t data);      //!< write open bus

	uint16_t bank_reg_r(offs_t offset);             //!< read bank register in memory mapped I/O range
	void bank_reg_w(offs_t offset, uint16_t data);  //!< write bank register in memory mapped I/O range

	void bs_early_read_r();                         //!< bus source: drive bus by R register
	void bs_early_load_r();                         //!< bus source: load R places 0 on the BUS
	void bs_late_load_r();                          //!< bus source: load R from SHIFTER
	void bs_early_read_md();                        //!< bus source: drive BUS from read memory data
	void bs_early_mouse();                          //!< bus source: drive bus by mouse
	void bs_early_disp();                           //!< bus source: drive bus by displacement (which?)

	void f1_early_block();                          //!< F1 func: block active task
	void f1_late_load_mar();                        //!< F1 func: load memory address register
	void f1_early_task();                           //!< F1 func: task switch
	void f1_late_l_lsh_1();                         //!< F1 func: SHIFTER = left shift L once
	void f1_late_l_rsh_1();                         //!< F1 func: SHIFTER = right shift L once
	void f1_late_l_lcy_8();                         //!< F1 func: SHIFTER = byte swap L

	void f2_late_bus_eq_zero();                     //!< F2 func: branch on bus equals zero
	void f2_late_shifter_lt_zero();                 //!< F2 func: branch on shifter less than zero
	void f2_late_shifter_eq_zero();                 //!< F2 func: branch on shifter equals zero
	void f2_late_bus();                             //!< F2 func: branch on bus bits BUS[6-15]
	void f2_late_alucy();                           //!< F2 func: branch on latched ALU carry
	void f2_late_load_md();                         //!< F2 func: load memory data

	uint32_t alu_74181(uint32_t a, uint32_t b, uint8_t smc);

	void rdram();                                   //!< read the microcode ROM/RAM halfword
	void wrtram();                                  //!< write the microcode RAM from M register and ALU

	uint8_t m_ether_id;                               //!< configured Ethernet ID for this machine

//*******************************************
// inline the sub-devices
//*******************************************
#include "a2jkff.h"
#include "a2ram.h"
#include "a2hw.h"
#include "a2kbd.h"
#include "a2mouse.h"
#include "a2disk.h"
#include "a2disp.h"
#include "a2mem.h"
#include "a2emu.h"
#include "a2ksec.h"
#include "a2ether.h"
#include "a2mrt.h"
#include "a2dwt.h"
#include "a2curt.h"
#include "a2dht.h"
#include "a2dvt.h"
#include "a2part.h"
#include "a2dwt.h"
#include "a2kwd.h"
};

DECLARE_DEVICE_TYPE(ALTO2, alto2_cpu_device)

#endif // MAME_CPU_ALTO2_ALTO2CPU_H
