// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/***************************************************************************
    Geneve 9640 Gate Array, PAL, and Genmod daughterboard

    This file contains the emulation of the gate array and of the PAL chip
    that is used to control wait state generation.

    Pins of the Gate Array:
    in:  A0..A15: Address bus
    in:  CLKOUT
    in:  IAQ/HOLDA
    in?: NMI*
    in?: RESET*

    i/o: D0..D7: Data bus

    out: KBDINT*:  Keyboard interrupt
    i/o: KBDCLK:   Keyboard clock line
    i/o: KBDDATA:  Keyboard data line

    out: SNDEN*: Sound chip select
    out: RTCEN*: RTC chip select

    out: RAS*
    out: CAS* (2x for two banks)
    out: DRA0..DRA8: Address bus for DRAM (1+18 bit = 512K)

    out: PSIEN*:   9901 enable
    out: CRUCLK*

    out: CSW*: v9938 write
    out: CSR*: v9938 read

    out: RAMENX*: SRAM expansion
    out: RAMEN*:  SRAM
    out: ROMEN*:  EPROM
    out: AB0, AB1, AB2: Mapped address bits (2^15, 2^14, 2^13)
    out: AMC, AMB, AMA: Higher address bits (2^18, 2^17, 2^16)
    in:  DBIN*
    ? :  ABUS* / HOLDA
    out: DBIN
    ? :  HOLD*
    ? :  READY*
    out: DBEN*: External data bus enable
    in:  MEMEN*
    in:  SNDRDY
    out: WE* / CRUCLK
    out: PhiCLK: System clock for 9901

    Onboard SRAM configuration
    ==========================

    Earlier versions of this emulation allowed for up to 384 KiB of SRAM.
    However, this did not reflect the technical options of the real device.
    In fact, there are only two select lines (RAMEN*, RAMEN-X*) that each
    select one SRAM chip of 32 KiB capacity. Accordingly, we now only offer
    the optional 32K expansion.

    Measurements on the real system proved that the zero waitstate access
    is only available for the banks of stock and expansion SRAM, not for
    the other areas (including the EPROM).

    The later operating systems of the Geneve (starting with 2.50s)
    assume the 32K expansion to be available. For this reason, this option is
    selected by default.

    Higher amounts of SRAM require extensive changes to the hardware (also
    with respect to the wait state generation).

    Geneve mapper
    ============

    In the Gate Array, a set of 8 map registers is used to expand the logical
    address to a physical address of 21 bits length. This is done by defining
    frames of 8 KiB size (13 bits); the most significant 3 bits select the
    map register, and its 8 bits are then prepended to the offset.

    Logical address:
    fffx xxxx xxxx xxxx
    \|/
     |    Map registers
     +--> 000: pppp pppp    ---> Physical address (21 bits):
          001: pppp pppp               p pppp pppx xxxx xxxx xxxx
          010: pppp pppp
          ...
          111: pppp pppp               = 000000 ... 1FFFFF (2 MiB space)

    The map registers are memory-mapped into the logical address space
    (Geneve mode: F110..F117, TI mode: 8000..8007) and set by writing the
    bytes into them.

    AMA/B/C decoding
    ================

    Since TI decided, for some obscure reason, to order the address bits in the
    direction from MSB to LSB, the highest address bit is called A0. This
    raises the problem that higher address bits lack a proper number (you
    don't want to use negative numbers for sure). The Peripheral Expansion Box
    already has three additional address lines: AMA (for the 2^16 position),
    AMB (2^17), AMC (2^18).

    The Flex Cable Interface (the card that allows the TI-99/4A console to
    be connected to the Peribox) sets these bits to 1. The Geneve, however,
    may use these lines to expand the usual 64K address space.

    The classic Peribox cards check ABA/B/C for being set to 1. Since all
    addresses starting with 00 or 01 are routed to the Geneve main board
    itself, and the 11 prefix is reserved for SRAM and the Boot EPROM, the
    64K address range of the Peribox cards is on map values 10 111 xxx, which
    means pages b8..bf. As the DSR (Device Service Routine, the card firmware)
    is expected on logical addresses 4000-5FFF, this corresponds to the page 0xba.

    A problem occurs with some 3rd party expansion cards which do not check
    AMA/B/C=1. In a normal TI system, this would have no effect anyway, but
    with the Geneve this leads to mirroring. The usual DSR space at 4000-5fff
    which would be reachable via page 0xba is then mirrored on a number of
    other pages:

    10 xxx 010x = 82, 8a, 92, 9a, a2, aa, b2, ba

    Another block to take care of is 0xbc which covers 8000-9fff since this
    area contains the speech synthesizer port at 9000/9400.

    Address map
    ===========

    p,q = page value bit (q = AMC, AMB, AMA)
    c = address offset within 8 KiB page

    p pqqq pppc cccc cccc cccc

    0 .... .... .... .... .... on-board bus (external drivers inactive)
    0 0... .... .... .... .... on-board DRAM 512 KiB
    0 1... .... .... .... .... on-board future expansion 512 KiB or Memex with Genmod

    1 0... .... .... .... .... external bus (p-box)
    1 0111 .... .... .... .... p-box (AMA/B/C=1)
    1 0111 000. .... .... ....   address block 0xxx
    1 0111 001. .... .... ....   address block 2xxx
    1 0111 010. .... .... ....   address block 4xxx (DSR)
    1 0111 011. .... .... ....   address block 6xxx
    1 0111 100. .... .... ....   address block 8xxx (Speech at 0x9000)
    1 0111 101. .... .... ....   address block axxx
    1 0111 110. .... .... ....   address block cxxx
    1 0111 111. .... .... ....   address block exxx

    1 1... .... .... .... .... on-board bus or external bus (unclear)
    1 10.. .... .... .... .... Future expansion
    1 1100 .... .... .... .... Future expansion

    1 1101 0... .... .... .... on-board sram (32K) - Optional 32 KiB expansion, 0 WS
    1 1101 1... .... .... .... on-board sram (32K) - stock 32 KiB SRAM, 0 WS

    1 111. ..0. .... .... .... on-board boot1
    1 111. ..1. .... .... .... on-board boot2


    Address operation
    =================

    For DRAM access, a separate address bus between the Gate Array and the
    DRAM circuits is used. The address bus has a width of 9 bits, which makes
    it 18 bit for the whole address (row/column). Also, two CAS* lines are
    used, selecting one set of DRAMs.

    For SRAM access, the least significant two map value bits (AB1, AB2)
    are prepended to the 13 bits of the offset from the logical address.
    The next bit (AB0) controls the RAMEN* / RAMENX* lines.

    For the EPROM access, the least significant bit of the map value is
    prepended to the 13 bits of the offset from the logical address. This
    yields a boot ROM size of 16K, mirrored on pages f0, f2, ..., fe, and
    f1, f3, ..., ff. Bigger EPROMs (or flash memory, see PFM) require to use
    the remaining bits AB1, AB0, and AMA.

    The external bus is selected by the two most significant map bits 10.


    GROM emulation and cartridge ROM space
    ======================================

    The Gate Array emulates a 64K GROM space in order to allow TI-99/4A
    cartridge images to be run on the Geneve in TI mode. Also, two 8K ROM
    pages are reserved to allow for emulating Extended Basic type cartridges.

    In TI mode, pages 38 to 3f constitute the 64K GROM space. Pages 36 and 37
    are the two 8K ROM banks. All are located in DRAM with 1 WS. Page 36
    (or page 37) is mapped to logical space 6000-7FFF, regardless of the
    mapper value at 8003 (can be set to any value without effect).

    CRU address >1EF8 determines the ROM size (1 or 2 banks):
        0 = 1 bank (page 36, fixed)
        1 = 2 banks (page 36 when writing a byte to 6000, 6004, ...,
                     page 37 when writing a byte to 6002, 6006, ...)

    CRU address >1EFA write-protects 6000-6FFF when set to 0.
    CRU address >1EFC write-protects 7000-7FFF when set to 0.
    Both do not apply for 2-bank settings.

    As with every page, the GROM pages may be mapped to any other memory
    area as well so that they may be randomly accessed.

    Unlike the real GROM, the GROM emulation allows free access to the whole
    8K of its page. Also, the emulation allows writing, so we essentially
    have a GRAM emulation.

    The Gate Array contains a 16-bit counter that represents the current
    GROM address. It wraps at 8K boundaries (>3FFE->3FFF->2000->2001). Reading
    from the GROM read port delivers the byte at the current address and
    increases the counter. Writing to the GROM port stores the byte at that
    address, respectively, and then increases the counter.

    Reading the address counter delivers first the MSB, then on every
    following access, the LSB. The counter itself contains the LSB in both
    bytes after the first read operation. Thus, its value must be restored
    after reading.

    Setting the address counter copies its LSB to the MSB and then writes
    the new byte into the LSB. After the second write (without intermediate
    data transfer), the counter is increased by one.


    Genmod expansion
    ================

    The objective of the Genmod is to allow the Geneve to access the full
    2 MiB physical address space, in conjunction with the MEMEX card that
    must be plugged into the p-box. There are actually two lines in the box
    that are unused; those are now defined as AMD and AME. They are located
    on the p-box bus on pins 8 and 9.

    Since the Gate Array does not output the first two address bits, they have
    to be reconstructed by two GAL chips on a daughterboard that must be
    soldered to the backside of the Gate Array.

    When ROMEN is active, the on-board EPROM is accessed, and the external
    bus is inactive.

    The MEMEX card allows for using 0 waitstate accesses. This means that
    the wait state generation for box accesses must be inhibited; this is
    done by cutting the trace from the Gate Array pin READY to the PAL.

    Some peripheral cards must now be modified to check for AMD and AME
    as well, or they will be mirrored into other memory areas.
    This GenMod feature is automatically applied to all peripheral cards in
    this emulation.

    The only remaining issue is that GROM access in the TI mode is under
    full control of the Gate Array; it will always activate one of the DRAM
    banks. To change this, the GA would need to be fully replaced. Instead,
    the real GenMod contains a small box with two switches, which is also
    emulation here:

    - Turbo mode: Activates or deactivates the wait state logic on the Geneve
      board. This switch may be changed at any time.
    - TI mode: Selects between the on-board memory, which is required
      for the GROM access, and the external Memex memory. This switch
      triggers a reset when changed.


    Logical address space layout
    ============================

    Geneve mode
    -----------
    Video:    F100 (port 0, rw),
              F102 (port 1, rw),
              F104 (port 2, w),
              F106 (port 3, w)
                                  1111 0001 0000 .xx0
    Mapper:   F110 - F117         1111 0001 0001 0xxx
    Keyboard: F118                1111 0001 0001 1...
    Clock:    F130 - F13F         1111 0001 0011 xxxx
    Sound:    F120                1111 0001 0010 ...0

    TI mode
    -------
    Video:    8800 (port 0, r), 8c00 (port 0, w),
              8802 (port 1, r), 8c02 (port 0, w),
                                8c04 (port 2, w),
                                8c06 (port 3, w)

                                  1000 1w.. .... .xx0
    Mapper:   8000 - 8007         1000 0000 0000 0xxx
    Keyboard: 8008 - 800F         1000 0000 0000 1...
    Clock:    8010 - 801F         1000 0000 0001 xxxx
    Sound:    8400 - 87FE         1000 01.. .... ...0
    Speech:   9000 / 9400         1001 0w.. .... ...0
    Grom:     9800 / 9802         1001 1w.. .... ..x0
              9c00 / 9c02

    Physical address space layout
    -----------------------------

    Start    End      Phys.pages
    000000 - 07FFFF   00-3F   512 KiB DRAM on-board
       06C000 - 06DFFF   36     Cartridge space first 8K
       06E000 - 06FFFF   37     Cartridge space second 8K
    080000 - 0FFFFF   40-7F   512 KiB on-board expansion (never used)
    100000 - 16FFFF   80-B7   448 KiB P-Box space (special cards, like MEMEX)
    170000 - 17FFFF   B8-BF    64 KiB P-Box space (current cards)
    180000 - 1CFFFF   C0-E7    Future expansion
    1D0000 - 1D7FFF   E8-EB    32 KiB SRAM expansion
    1D8000 - 1DFFFF   EC-EF    32 KiB stock SRAM
    1E0000 - 1FFFFF   F0-FF   128 KiB EPROM space; 16 KiB actually used, 8 mirrors


    GenMod modification
    -------------------
    TI mode
    000000 - 07FFFF   00-3F   512 KiB DRAM on-board
       06C000 - 06DFFF   36     Cartridge space first 8K
       06E000 - 06FFFF   37     Cartridge space second 8K
    080000 - 1DFFFF   40-EF  1408 KiB P-Box space
    1E0000 - 1FFFFF   F0-FF   128 KiB EPROM space; 16 KiB actually used, 8 mirrors

    Non-TI mode
    000000 - 1DFFFF   00-EF  1920 KiB P-Box space
    1E0000 - 1FFFFF   F0-FF   128 KiB EPROM space; 16 KiB actually used, 8 mirrors

    Waitstate handling
    ==================
    Waitstates are caused by a cleared READY line of the TMS9995 processor
    during an external memory cycle. That means that waitstates have no effect
    for operations within the on-chip memory, and only when an access to the
    external memory or other devices occurs, a delay will be noticed.

    The waitstates are generated by the custom Gate Array chip on the board
    and the PAL 16R4, both lacking proper documentation. All of the following
    numbers have been determined by experiments with the real machine.

    Waitstates are generated for all accesses except for the SRAM in pages
    E8..EF. Video accesses produce 15 waitstates, created by the counter
    in the PAL. However, these video wait states are effective after the video
    access has been completed. Wait states are not effective when the
    execution is running in on-chip RAM.
    Without additional wait states, the video access takes the usual 1 or 2 WS.

    Additional waitstates are created when one of the CRU bits is set. In that
    case, all delays are extended to 2 WS (including SRAM).

    Sound waitstates depend on the clock of the sound chip; the READY line is
    pulled down until the next clock pulse.


    CRU map
    =======
    ---- TMS 9901 ----
    0000: flag Clock mode
    0002: int  INTA* (pbox pin 17)
    0004: int  Video interrupt
    0006: in   Joystick button
    0008: in      left
    000A: in      right
    000C: in      down
    000E: in      up
    0010: int  Keyboard scancode available
    0012: in   (mirror of 003A)
    0014: in   Left mouse button
    0016: in   Real-time clock interrupt
    0018: in   INTB* (pbox pin 18)
    001A: in   (reflects 0032)
    001C: -
    001E: in   (reflects 002E)
    0020: out  Pbox reset
    0022: out  Video reset
    0024: out  Joystick select (0=Joystick 1, 1=Joystick 2)
    0026: -
    0028: out  PFM bank switch LSB
    002A: out  PFM output enable
    002C: out  Keyboard reset
    002E: out  System clock speed (external memory cycles)
    0030: -
    0032: out  Video wait state enable
    0034: in   (mirror of 0018)
    0036: in   (mirror of 0016)
    0038: in   (mirror of 0014)
    003A: out  PFM bank switch MSB
    003C: in   (mirror of 0010)
    003E: in   (mirror of 000E)

    ----Gate Array----
    13C0 - 13FE: Single step execution (not implemented, not available for most systems)

    ----TMS9995-internal flag registers----
    The values are all latched inside the CPU, but output values are visible
    on the bus outside the CPU
    1EE0-1EE8, 1FDA:     see tms9995.cpp
    1EEA:   PAL/NTSC flag
    1EEC:   (unused)
    1EEE:   CapsLock flag
    1EF0:   Keyboard clock
    1EF2:   Keyboard shift register enable
    1EF4:   Operation mode (native, GPL)
    1EF6:   Memory mode (unmapped, mapped)
    1EF8:   Cartridge size (0: 16 KiB, 1: 8 KiB)
    1EFA:   Protect 6xxx
    1EFC:   Protect 7xxx
    1EFE:   Additional wait state per memory access

    -------------------------------------------------------------------------

    Michael Zapf, October 2011
    February 2012: rewritten as class, restructured
    Aug 2015: PFM added
    Nov 2019: Extensive rewrite to provide a true emulation of the Gate Array
              and the PAL, and also to allow for different kinds of external
              keyboards.

***************************************************************************/
#include "emu.h"

#define LOG_WARN     (1U << 1)
#define LOG_DETAIL   (1U << 2)
#define LOG_READ     (1U << 3)
#define LOG_WRITE    (1U << 4)
#define LOG_KEYBOARD (1U << 5)
#define LOG_CLOCK    (1U << 6)
#define LOG_READY    (1U << 7)
#define LOG_SETTING  (1U << 8)
#define LOG_CRU      (1U << 9)
#define LOG_CRUKEY   (1U << 10)
#define LOG_DECODE   (1U << 11)
#define LOG_ADDRESS  (1U << 12)
#define LOG_LINES    (1U << 13)
#define LOG_WAIT     (1U << 14)
#define LOG_GROM     (1U << 15)
#define LOG_MAPPER   (1U << 16)

// Minimum log should be warnings
#define VERBOSE (LOG_GENERAL | LOG_WARN)

#include "genboard.h"
#include "logmacro.h"

DEFINE_DEVICE_TYPE(GENEVE_GATE_ARRAY, bus::ti99::internal::geneve_gate_array_device, "geneve_gate_array", "Geneve Gate Array")
DEFINE_DEVICE_TYPE(GENMOD_DECODER,    bus::ti99::internal::genmod_decoder_device, "genmod_decoder", "GenMod decoder circuit")
DEFINE_DEVICE_TYPE(GENEVE_PAL,        bus::ti99::internal::geneve_pal_device, "geneve_pal", "Geneve PAL circuit")

namespace bus::ti99::internal {

geneve_gate_array_device::geneve_gate_array_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, type, tag, owner, clock),
	m_have_waitstate(false),
	m_have_extra_waitstate(false),
	m_enable_extra_waitstates(false),
	m_extready(true),
	m_sndready(true),

	m_grom_address(0),
	m_cartridge_banked(false),
	m_cartridge_secondpage(false),
	m_cartridge6_writable(false),
	m_cartridge7_writable(false),
	m_load_lsb(false),

	m_geneve_mode(false),
	m_direct_mode(false),

	m_keyint(*this),
	m_keyb_clk(*this),
	m_keyb_data(*this),
	m_keyboard_shift_reg(0),
	m_keyboard_last_clock(CLEAR_LINE),
	m_keyboard_data_in(CLEAR_LINE),
	m_shift_reg_enabled(false),

	m_pal(*owner, GENEVE_PAL_TAG),
	m_peribox(*owner, TI_PERIBOX_TAG),

	m_debug(false)
{
}

geneve_gate_array_device::geneve_gate_array_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: geneve_gate_array_device(mconfig, GENEVE_GATE_ARRAY, tag, owner, clock)
{
}

void geneve_gate_array_device::cru_sstep_write(offs_t offset, uint8_t data)
{
	// Single step
	// 13c0 - 13fe: 0001 0011 11xx xxx0  (offset << 1)
	LOGMASKED(LOG_WARN, "Single step not implemented; bit %d set to %d\n", offset & 0x001f, data);
}

void geneve_gate_array_device::cru_ctrl_write(offs_t offset, uint8_t data)
{
	// This is just mirroring the internal flags of the 9995
	int bit = (offset & 0x000f);
	switch (bit)
	{
	case 5:
		// Unknown effect
		LOGMASKED(LOG_CRU, "Set PAL flag = %02x\n", data);
		// m_palvideo = (data!=0);
		break;
	case 7:
		// Capslock flag; just to keep track of the current state
		break;
	case 8:
		LOGMASKED(LOG_CRUKEY, "Set keyboard clock = %02x\n", data);
		set_keyboard_clock(data);
		break;
	case 9:
		LOGMASKED(LOG_CRUKEY, "Enable keyboard shift reg = %02x\n", data);
		enable_shift_register(data);
		break;
	case 10:
		LOGMASKED(LOG_CRU, "Operation mode = %s\n", (data!=0)? "native" : "GPL");
		m_geneve_mode = (data!=0);
		break;
	case 11:
		LOGMASKED(LOG_CRU, "Addressing mode = %s\n", (data!=0)? "unmapped" : "mapped");
		m_direct_mode = (data!=0);
		break;
	case 12:
		LOGMASKED(LOG_CRU, "Cartridge ROM: %s\n", (data==0)? "banked" : "unbanked");
		m_cartridge_banked = (data==0);
		break;
	case 13:
		LOGMASKED(LOG_CRU, "Cartridge ROM 6000-6fff %s\n", (data!=0)? "writable" : "protected");
		m_cartridge6_writable = (data!=0);
		break;
	case 14:
		LOGMASKED(LOG_CRU, "Cartridge ROM 7000-7fff %s\n", (data!=0)? "writable" : "protected");
		m_cartridge7_writable = (data!=0);
		break;
	case 15:
		LOGMASKED(LOG_CRU, "Extra wait states %s\n", (data==0)? "enabled" : "disabled");
		m_enable_extra_waitstates = (data==0);
		break;
	default:
		break;
	}
}

/******************************************************************
   Keyboard support
   XT protocol:

    Original XT: 0 1 bit0 bit1 bit2 bit3 bit4 bit5 bit6 bit7
    Some clones:   1 bit0 bit1 bit2 bit3 bit4 bit5 bit6 bit7

    bit0 = LSB, bit7 = MSB

   For now we assume that the Geneve needs the original XT keyboard.

   We can use the start 1 bit to control bit reception. When it reaches the
   rightmost position, we suspend transfer and raise the interrupt.

   With the flagging of the interrupt, the data line towards the keyboard
   is held low until the interrupt is cleared. This is done by clearing the
   shift register by setting the CRU address 1EF2 to 0.

   Note that lowering the clock line to 0 for more than 20ms will trigger
   a keyboard reset.

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

/*
    Pull down or release the clock line.
    Called by setting CRU bit 1EF0 to 0 or 1.
*/
WRITE_LINE_MEMBER( geneve_gate_array_device::set_keyboard_clock)
{
	m_keyb_clk(state);
}

/*
    Enable the shift register. Setting to 0 will clear the register and
    lock it. At the same time, the interrupt is cleared, and the data line
    is released. If further scancodes are expected, the shift register should
    immediately be enabled again.

    Called by setting CRU bit 1EF2 to 0 or 1
*/
WRITE_LINE_MEMBER( geneve_gate_array_device::enable_shift_register)
{
	m_shift_reg_enabled = (state==ASSERT_LINE);

	if (!m_shift_reg_enabled)
	{
		LOGMASKED(LOG_KEYBOARD, "Clear shift register, disable\n");
		m_keyboard_shift_reg = 0;
		shift_reg_changed();
	}
	else
		LOGMASKED(LOG_KEYBOARD, "Enable shift register\n");
}

void geneve_gate_array_device::shift_reg_changed()
{
	// The level of the data line is the inverse of the rightmost bit of
	// the shift register. This means that once the start bit reaches that
	// position, it will pull down the data line and stop the transfer.
	m_keyb_data(1 - (m_keyboard_shift_reg & 1));
	m_keyint((m_keyboard_shift_reg & 1)? ASSERT_LINE : CLEAR_LINE);
	if (m_keyboard_shift_reg & 1)
		LOGMASKED(LOG_KEYBOARD, "Scan code complete; raise interrupt, hold down data line\n");
	else
		LOGMASKED(LOG_KEYBOARD, "Clear keyboard interrupt, release data line\n");
}

/*
    Incoming keyboard strobe. When 0, push the current data line level into
    the shift register at the leftmost position.
*/
WRITE_LINE_MEMBER( geneve_gate_array_device::kbdclk )
{
	LOGMASKED(LOG_KEYBOARD, "Keyboard clock: %d\n", state);
	bool clock_falling_edge = (m_keyboard_last_clock == ASSERT_LINE && state == CLEAR_LINE);

	if (m_shift_reg_enabled && clock_falling_edge)
	{
		m_keyboard_shift_reg = (m_keyboard_shift_reg>>1) | (m_keyboard_data_in? 0x100 : 0x00);
		LOGMASKED(LOG_KEYBOARD, "Shift register = %02x\n", m_keyboard_shift_reg>>1);
		shift_reg_changed();
	}
	m_keyboard_last_clock = (line_state)state;
}

/*
    Latch the value of the incoming data line.
*/
WRITE_LINE_MEMBER( geneve_gate_array_device::kbddata )
{
	LOGMASKED(LOG_KEYBOARD, "Keyboard data: %d\n", state);
	m_keyboard_data_in = (line_state)state;
}

// sysspeed has no effect for vwait/speed

// TODO: Video write timing

/*****************************************************************
    Decoding functions
******************************************************************/

const geneve_gate_array_device::logentry_t geneve_gate_array_device::s_logmap[10] =
{
	{ 0xf100, 0x000e, 0x8800, 0x03fe, 0x0400, MLVIDEO,  "video" },
	{ 0xf110, 0x0007, 0x8000, 0x0007, 0x0000, MLMAPPER, "mapper" },
	{ 0xf118, 0x0007, 0x8008, 0x0007, 0x0000, MLKEY,    "keyboard" },
	{ 0xf120, 0x000e, 0x8400, 0x01ff, 0x0000, MLSOUND,  "sound" },
	{ 0x0000, 0x0000, 0x8600, 0x01ff, 0x0000, MLEXT,    "external bus" },
	{ 0xf130, 0x000f, 0x8010, 0x000f, 0x0000, MLCLOCK,  "clock" },
	{ 0x0000, 0x0000, 0x9000, 0x03fe, 0x0400, MLEXT,    "speech (ext. bus)" },
	{ 0x0000, 0x0000, 0x9800, 0x03fc, 0x0400, MLGROM,   "GROM data" },
	{ 0x0000, 0x0000, 0x9802, 0x03fc, 0x0400, MLGROMAD, "GROM address" },
	{ 0x0000, 0x0000, 0x6000, 0x1fff, 0x0000, MLCARTROM, "Cartridge ROM" }
};

void geneve_gate_array_device::decode_logical(geneve_gate_array_device::decdata* dec)
{
	dec->function = MUNDEF;
	int index = -1;

	for (int i = 0; (i < 10) && (index == -1); i++)
	{
		if (m_geneve_mode)
		{
			// Skip when genbase is 0
			if ((s_logmap[i].genbase != 0) && ((dec->offset & ~s_logmap[i].genmask) == s_logmap[i].genbase))
				index = i;
		}
		else
		{
			if (dec->read)
			{
				if ((dec->offset & ~s_logmap[i].timask) == s_logmap[i].tibase)
					index = i;
			}
			else
			{
				if ((dec->offset & ~s_logmap[i].timask) == (s_logmap[i].tibase | s_logmap[i].writeoff))
					index = i;
			}
		}
	}

	if (index != -1)
	{
		LOGMASKED(LOG_DECODE, "Decoded as %s: %04x\n", s_logmap[index].description, dec->offset);
		dec->function = s_logmap[index].function;
	}

	// Handle write operations to the cartridge ROM
	if (dec->function == MLCARTROM && !dec->read)
	{
		if (m_cartridge_banked) dec->function = MLCARTBANK;
		else
		{
			if ((((dec->offset & 0x1000)==0) && !m_cartridge6_writable)
				|| (((dec->offset & 0x1000)!=0) && !m_cartridge7_writable))
			{
				dec->function = CARTPROT;
				LOGMASKED(LOG_WARN, "Writing to protected cartridge space %04x ignored\n", dec->offset);
			}
		}
	}
}

/*
    Look up the mapper value (page) for the given logical address.
    Cartridges need special handling.
*/
void geneve_gate_array_device::get_page(geneve_gate_array_device::decdata* dec)
{
	int logpage = (dec->offset & 0xe000) >> 13;
	dec->physpage = 0;

	// Determine physical address
	if (m_direct_mode) dec->physpage = 0xf8; // points to boot eprom
	else
	{
		if (dec->function == MLCARTROM)
			dec->physpage = (m_cartridge_banked && m_cartridge_secondpage)? 0x37 : 0x36;
		else
		{
			if (dec->function == MLGROM)
				dec->physpage = ((m_grom_address >> 13) & 0x07) | 0x38;
			else
			{
				if (dec->function == MLEXT)
				{
					dec->physpage = 0xb8 | logpage;
				}
				else
					dec->physpage = m_map[logpage];
			}
		}
	}
}

const geneve_gate_array_device::physentry_t geneve_gate_array_device::s_physmap[7] =
{
	{ 0x00, 0x3f, MPDRAM,  "DRAM" },
	{ 0x40, 0x3f, MPEXP,  "Future exp (on-board)" },
	{ 0xe8, 0x03, MPSRAMX,  "SRAM exp" },
	{ 0xec, 0x03, MPSRAM,  "SRAM" },
	{ 0xf0, 0x0f, MPEPROM,  "Boot ROM/PFM" },
	{ 0x80, 0x7f, MBOX,  "external" }          // catch-all
};

/*
    Try to decode the physical address.
*/
void geneve_gate_array_device::decode_physical(geneve_gate_array_device::decdata* dec)
{
	// Search the map
	if (dec->function == MUNDEF || dec->function == MLCARTROM || dec->function == MLGROM)
	{
		if (dec->function == MLGROM)
		{
			// If map byte 6 is not set to 03, ignore the access
			// This seems to be a safety feature, or a glitch in the design
			// of the Gate array.
			if (m_map[6] != 0x03)
			{
				dec->function = MUNDEF;
				return;
			}

			// GROM is actually DRAM; substitute the address and update the address counter
			// The page has already been set in get_page
			LOGMASKED(LOG_GROM, "GROM address = %04x\n", m_grom_address);
			dec->offset = m_grom_address;  // Do not wipe the GROM number
			// Auto-increment here (not in clock_in, as we have two clock cycles by wait states)
			increase_grom_address();
			return;
		}

		// Search for the matching page interval. Cartridge space will be decoded as DRAM.
		bool found = false;
		for (int i = 0; (i < 6) && !found; i++)
		{
			if ((dec->physpage & ~s_physmap[i].mask) == s_physmap[i].base)
			{
				dec->function = s_physmap[i].function;
				LOGMASKED(LOG_DECODE, "Decoded as %s, page %02x\n", s_physmap[i].description, dec->physpage);
				found = true;
			}
		}

		if (!found)
			LOGMASKED(LOG_DECODE, "Unmapped address %06x\n", (get_prefix(0xff) | (dec->offset & 0x1fff)) & 0x1fffff);
	}
}

void geneve_gate_array_device::increase_grom_address()
{
	if (!m_debug)
	{
		m_grom_address = ((m_grom_address + 1) & 0x1fff) | (m_grom_address & 0xe000);
		m_load_lsb = false;
	}
}

/*
    Are we addressing DRAM?
*/
bool geneve_gate_array_device::accessing_dram()
{
	decdata* dec = (m_debug)? &m_decdebug : &m_decoded;
	return accessing_dram_s(dec->function);
}

bool geneve_gate_array_device::accessing_dram_s(int function)
{
	return (function == MPDRAM) || (function == MLGROM) || (function == MLCARTROM);
}

bool geneve_gate_array_device::accessing_sram_s(int function)
{
	return (function == MPSRAM) || (function == MPSRAMX);
}

bool geneve_gate_array_device::accessing_devs_s(int function)
{
	return (function == MLMAPPER) || (function == MLKEY) || (function == MLCLOCK) || (function == MLGROMAD);
}

bool geneve_gate_array_device::accessing_grom()
{
	decdata* dec = (m_debug)? &m_decdebug : &m_decoded;
	return (dec->function == MLGROM);
}

bool geneve_gate_array_device::accessing_box_s(int function, bool genmod)
{
	return (function == MLEXT) || (function == MBOX) || (genmod && function == MPEXP);
}

/*
    The Gate Array has a private address bus to the DRAM.
*/
offs_t geneve_gate_array_device::get_dram_address()
{
	offs_t physaddr = 0;
	decdata* dec = (m_debug)? &m_decdebug : &m_decoded;
	int addr13 = dec->offset & 0x1fff;

	// Cartridge access is also done in DRAM; the pages are fixed to
	// 36, 37 (ROM), 38...3f (GROM), regardless of the mapper.

	if (dec->function == MLGROM)
	{
		physaddr = (0x38 << 13) | dec->offset;
		// The GROM address counter is updated in decode_physical
	}
	else
	{
		if (dec->function == MPDRAM || dec->function == MLCARTROM)
		{
			physaddr = (get_prefix(0x3f) | addr13) & 0x7ffff;
		}
		else
			LOGMASKED(LOG_WARN, "Unknown decoding %d for DRAM\n", dec->function);
	}
	return physaddr;
}

/*
    Accept the address passed over the address bus and decode it appropriately.
    This decoding will later be used in the READ/WRITE member functions. Also,
    we initiate wait state creation here.
*/
void geneve_gate_array_device::setaddress(offs_t address, uint8_t busctrl)
{
	LOGMASKED(LOG_ADDRESS, "setaddress = %04x%s\n", address, m_debug? " (debug)" : "");
	decdata& dec = (m_debug)? m_decdebug : m_decoded;

	dec.offset = address;
	dec.read = ((busctrl & TMS99xx_BUS_DBIN)!=0);

	decode_logical(&dec);
	get_page(&dec);
	decode_physical(&dec);

	if (!m_debug)
	{
		m_have_extra_waitstate = m_enable_extra_waitstates;
		m_have_waitstate = (dec.function != MPSRAM && dec.function != MPSRAMX) || m_have_extra_waitstate;
	}
}

/*
   The Gate Array uses the clock to operate the wait state flags. The actual
   wait state generation is up to the PAL chip.
*/
WRITE_LINE_MEMBER( geneve_gate_array_device::clock_in )
{
	// Falling CLK
	if (state == CLEAR_LINE)
	{
		if (!m_have_extra_waitstate)
			m_have_waitstate = false;
		else
			m_have_extra_waitstate = false;
	}
}

/*
    READY line from the peribox. Together with the sndready and the READY output
    of the Gate Array itself, this forms a wired AND.
*/
WRITE_LINE_MEMBER( geneve_gate_array_device::extready_in )
{
	LOGMASKED(LOG_READY, "External READY = %d\n", state);
	m_extready = (state==ASSERT_LINE);
}

WRITE_LINE_MEMBER( geneve_gate_array_device::sndready_in )
{
	LOGMASKED(LOG_READY, "Sound READY = %d\n", state);
	m_sndready = (state==ASSERT_LINE);
}

READ_LINE_MEMBER(geneve_gate_array_device::csw_out)
{
	// Do not access a port-based device in debugger mode
	if (m_debug) return CLEAR_LINE;
	return ((m_decoded.function == MLVIDEO) && !m_decoded.read)? ASSERT_LINE : CLEAR_LINE;
}

READ_LINE_MEMBER(geneve_gate_array_device::csr_out)
{
	// Do not access a port-based device in debugger mode
	if (m_debug) return CLEAR_LINE;
	return ((m_decoded.function == MLVIDEO) && m_decoded.read)? ASSERT_LINE : CLEAR_LINE;
}

READ_LINE_MEMBER(geneve_gate_array_device::romen_out)
{
	// Do not restrict to read-only, as we could have a PFM here
	decdata* dec = (m_debug)? &m_decdebug : &m_decoded;
	return (dec->function == MPEPROM)? ASSERT_LINE : CLEAR_LINE;
}

READ_LINE_MEMBER(geneve_gate_array_device::ramen_out)
{
	decdata* dec = (m_debug)? &m_decdebug : &m_decoded;
	return (dec->function == MPSRAM)? ASSERT_LINE : CLEAR_LINE;
}

READ_LINE_MEMBER(geneve_gate_array_device::ramenx_out)
{
	decdata* dec = (m_debug)? &m_decdebug : &m_decoded;
	return (dec->function == MPSRAMX)? ASSERT_LINE : CLEAR_LINE;
}

READ_LINE_MEMBER(geneve_gate_array_device::rtcen_out)
{
	decdata* dec = (m_debug)? &m_decdebug : &m_decoded;
	return (dec->function == MLCLOCK)? ASSERT_LINE : CLEAR_LINE;
}

READ_LINE_MEMBER(geneve_gate_array_device::snden_out)
{
	decdata* dec = (m_debug)? &m_decdebug : &m_decoded;
	return ((dec->function == MLSOUND) && !dec->read)? ASSERT_LINE : CLEAR_LINE;
}

READ_LINE_MEMBER(geneve_gate_array_device::dben_out)
{
	decdata* dec = (m_debug)? &m_decdebug : &m_decoded;
	return accessing_box_s(dec->function, false)? ASSERT_LINE : CLEAR_LINE;
}

// After setaddress, pull down READY if
// - we have an extra waitstate  OR
// - we do not access SRAM(X) OR
// - extready = 0

// In Genmod, pull down READY if
// - we have an extra waitstate OR
// - we access the box and have turbo==0 OR
// - we access DRAM and have timode==1 OR
// - extready = 0

READ_LINE_MEMBER(geneve_gate_array_device::gaready_out)
{
	if (m_debug) return ASSERT_LINE;  // Always READY when debugging
	// Return true (READY=1) when we are accessing SRAM/SRAMX and when we do not have extra waitstates
	// return ((m_decoded.function == MPSRAM || m_decoded.function == MPSRAMX) && !m_have_extra_waitstate)? ASSERT_LINE : CLEAR_LINE;
	return (m_have_waitstate || m_have_extra_waitstate || !m_extready || !m_sndready)? CLEAR_LINE : ASSERT_LINE;
}


/**********************************************************
    Gate Array-internal functions
***********************************************************/

/*
    Changes the value of the parameter if one of the functions applies.
*/
void geneve_gate_array_device::readz(uint8_t& value)
{
	decdata* dec = (m_debug)? &m_decdebug : &m_decoded;
	uint8_t lsb = 0;

	switch (dec->function)
	{
	case MLMAPPER:
		value = m_map[dec->offset & 0x0007];
		LOGMASKED(LOG_READ, "Read mapper %04x -> %02x\n", dec->offset, value);
		break;
	case MLKEY:
		value = m_keyboard_shift_reg>>1;
		LOGMASKED(LOG_READ, "Read keyboard %04x -> %02x\n", dec->offset, value);
		break;
	case MLGROMAD:
		if (!m_debug)       // don't let the debugger mess with the address counter
		{
			value = (m_grom_address & 0xff00)>>8;
			lsb = (m_grom_address & 0xff);
			m_grom_address = lsb << 8 | lsb;
			m_load_lsb = false;
			LOGMASKED(LOG_READ, "Read GROM address %04x -> %02x\n", dec->offset, value);
		}
		break;
	case MLCARTBANK:
		break;
	default:
		break;
	}
}

/*
    Internal functions of the Gate Array. Returns without changes if none
    of the function applies.
*/
void geneve_gate_array_device::write(uint8_t data)
{
	decdata* dec = (m_debug)? &m_decdebug : &m_decoded;

	switch (dec->function)
	{
	case MLMAPPER:
		m_map[dec->offset & 0x0007] = data;
		// LOGMASKED(LOG_MAPPER, "Write mapper %04x <- %02x\n", dec->offset, data);
		LOGMASKED(LOG_MAPPER, "Set %04x mapper[%02x %02x %02x %02x %02x %02x %02x %02x]\n",
			dec->offset, m_map[0], m_map[1], m_map[2], m_map[3], m_map[4], m_map[5], m_map[6], m_map[7]);
		break;
	case MLGROMAD:
		if (!m_debug)   // don't let the debugger mess with the address counter
		{
			m_grom_address = (m_grom_address << 8 | data) & 0xffff;
			if (m_load_lsb) increase_grom_address();
			else m_load_lsb = true;
			LOGMASKED(LOG_GROM, "Write GROM address %04x <- %02x\n", dec->offset, data);
		}
		break;
	case MLCARTBANK:
		m_cartridge_secondpage = ((dec->offset & 0x0002)!=0);
		LOGMASKED(LOG_WRITE, "Set cartridge bank %04x <- %02x\n", dec->offset, data);
		break;
	case MLKEY:
		LOGMASKED(LOG_WRITE, "Write to keyboard ignored\n");
		break;
	default:
		break;
	}
}

/*
    Address lines that the Gate Array offers. They reflect the 8 bits of
    the mapper byte. AME and AMD are only used by GenMod.
    (AME, AMD,) AMC, AMB, AMA, AB0, AB1, AB2
*/
int geneve_gate_array_device::get_prefix(int lines)
{
	decdata* dec = (m_debug)? &m_decdebug : &m_decoded;
	return (dec->physpage & lines) << 13;
}

//====================================================================
//  Common device lifecycle
//====================================================================

void geneve_gate_array_device::device_start()
{
	m_keyint.resolve_safe();
	m_keyb_clk.resolve_safe();
	m_keyb_data.resolve_safe();

	m_geneve_mode = false;
	m_direct_mode = true;

	// State registration
	save_item(NAME(m_have_extra_waitstate));
	save_item(NAME(m_enable_extra_waitstates));
	save_item(NAME(m_extready));
	save_item(NAME(m_sndready));

	save_item(NAME(m_grom_address));
	save_item(NAME(m_cartridge_banked));
	save_item(NAME(m_cartridge_secondpage));
	save_item(NAME(m_cartridge6_writable));
	save_item(NAME(m_cartridge7_writable));
	save_item(NAME(m_load_lsb));

	save_item(NAME(m_geneve_mode));
	save_item(NAME(m_direct_mode));

	save_pointer(NAME(m_map), 8);

	save_item(NAME(m_decoded.read));
	save_item(NAME(m_decoded.function));
	save_item(NAME(m_decoded.offset));
	save_item(NAME(m_decoded.physpage));

	save_item(NAME(m_keyboard_shift_reg));
	save_item(NAME(m_shift_reg_enabled));
}

void geneve_gate_array_device::common_reset()
{
	m_have_extra_waitstate = false;
	m_enable_extra_waitstates = false;

	m_grom_address = 0;
	m_cartridge_banked = false;
	m_cartridge_secondpage = false;
	m_cartridge6_writable = false;
	m_cartridge7_writable = false;

	m_geneve_mode =false;
	m_direct_mode = true;

	// Clear map
	for (auto & elem : m_map) elem = 0;
}

void geneve_gate_array_device::device_reset()
{
	common_reset();
}

/* ========================================================================

    The PAL circuit on the Geneve main board. It is the actual waitstate
    generator, and its task is to control the READY line depending on the
    accessed device, and to control the outgoing MEMEN* and WE* lines into
    the peribox.

    The chip is a PAL16R4ACN

    Pin    Dir   Meaning
    ---------------------
    1      in    CLK (assert=H)
    2      in    WE* (write)
    3      in    READYIN (from Gate Array and sound chip)
    4      in    CSR* (Video read)
    5      in    CRU bit 23 (>002E) ("System clock speed")
    6      in    MEMEN* (memory access)
    7      in    DBEN* (external data bus enable)
    8      in    (Gate Array bit 36)
    9      in    CSW* (video write)
    10           GND
    11     in    OE*, hardwired to H
    12     out   WE*
    13     out   MEMEN*
    14           (output of FF1, disabled by pin 11)
    15           (output of FF2, disabled by pin 11)
    16           (output of FF3, disabled by pin 11)
    17           (output of FF4, disabled by pin 11)
    18     out   READYOUT
    19     in    VDPWAITEN
    20           Vcc

    ======================================================================== */

geneve_pal_device::geneve_pal_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, GENEVE_PAL, tag, owner, clock),
	  m_pin3(true),
	  m_pin4(true),
	  m_pin5(true),
	  m_pin9(true),
	  m_pin19(false),
	  m_pin14d(false),m_pin14q(false),
	  m_pin15d(false),m_pin15q(false),
	  m_pin16d(false),m_pin16q(false),
	  m_pin17d(false),m_pin17q(false),
	  m_prev_ready(CLEAR_LINE),
	  m_peribox(*owner, TI_PERIBOX_TAG),
	  m_ready(*this)
{
}

/*
    READY input from the Gate Array.
*/
WRITE_LINE_MEMBER(geneve_pal_device::gaready_in)
{
	bool prev = m_pin3;
	m_pin3 = (state==ASSERT_LINE);
	if (prev != m_pin3)
	{
		LOGMASKED(LOG_LINES, "READY(ga) <- %d\n", state);
		// When GAREADY=L, the video lines have immediate effect on the ready line
		set_ready();
	}
}

/*
    Video read (ASSERT=low).
*/
WRITE_LINE_MEMBER(geneve_pal_device::csr_in)
{
	bool prev = m_pin4;
	m_pin4 = (state==CLEAR_LINE);
	if (prev != m_pin4)
	{
		LOGMASKED(LOG_LINES, "CSR <- %d\n", state);
		set_ready();
	}
}

/*
    Video write (ASSERT=low).
*/
WRITE_LINE_MEMBER(geneve_pal_device::csw_in)
{
	bool prev = m_pin9;
	m_pin9 = (state==CLEAR_LINE);
	if (prev != m_pin9)
	{
		LOGMASKED(LOG_LINES, "CSW <- %d\n", state);
		set_ready();
	}
}

/*
    Memory enable (ASSERT=low); pass through
*/
WRITE_LINE_MEMBER(geneve_pal_device::memen)
{
	LOGMASKED(LOG_LINES, "MEMEN -> %d\n", state);
	m_peribox->memen_in(state);
}

/*
    Write external mem cycles (0=long, 1=short)
    System clock speed (PAL pin 5). (ASSERT=high)
    The function from the equations could not be verified on a real machine.
    This function seems to have no effect. So either the equations are
    wrong, or something else is going on.
*/
WRITE_LINE_MEMBER(geneve_pal_device::sysspeed)
{
	bool prev = m_pin5;
	m_pin5 = (state == ASSERT_LINE);
	if (prev != m_pin5)
	{
		LOGMASKED(LOG_SETTING, "System clock speed set to %d\n", state);
		set_ready();
	}
}

/*
    Write vdp wait cycles (1=add 14 cycles, 0=add none)
    see above for waitstate handling
*/
WRITE_LINE_MEMBER(geneve_pal_device::vwaiten)
{
	bool prev = m_pin19;
	m_pin19 = (state==ASSERT_LINE);
	if (prev != m_pin19)
	{
		LOGMASKED(LOG_SETTING, "Video wait states %s\n", (state!=0)? "enabled" : "disabled");
		set_ready();
	}
}

/*
    Clock input. This controls the state of the waitstate counter.
*/
WRITE_LINE_MEMBER(geneve_pal_device::clock_in)
{
	LOGMASKED(LOG_CLOCK, "CLK%s\n", state? "?" : "?");
	// Set the FF
	if (state==ASSERT_LINE)
	{
		m_pin14q = m_pin14d;
		m_pin15q = m_pin15d;
		m_pin16q = m_pin16d;
		m_pin17q = m_pin17d;
		set_ready();
	}
}

/*
    Called from clock_in and input pin functions. Set the state of the
    READY line. Since this is the only source for the state, we always have
    0 or 1, but no Z state.

    FIXME: The video write wait state handling is still not correct. Unfortunately,
    the equations of the PAL did not help, in contrast, the problem got worse.
    Problem: After initiating a video write (CSW*=0), the READY line must
    remain high for the next falling clock edge so that the CPU can complete
    the command cycle. By these equations, the READY line immediately goes
    low when the clock line rises, and thus on the falling edge, a wait state
    is produced.
    The effect is that in the real machine, if all code runs in on-chip memory,
    a video write does not cause any wait state, while in this emulation, it
    always causes the full 14 cycles of wait states. In rare situations, this
    may heavily slow down the processing.
*/
void geneve_pal_device::set_ready()
{
	line_state ready_line;

	// Original equations from the PAL
/*
    ready_line = ((!m_pin4 && !m_pin3) ||
                 (!m_pin9 && !m_pin3) ||
                 (m_pin9 && m_pin4 && !m_pin14q) ||
                 (m_pin5 && m_pin4 && !m_pin15q) ||
                 (m_pin5 && m_pin4 && !m_pin16q) ||
                 (m_pin9 && m_pin4 && !m_pin17q))? CLEAR_LINE : ASSERT_LINE;

    m_pin17d = (m_pin9 && m_pin4 && !m_pin17q && !m_pin14q) ||
             (m_pin9 && m_pin4 && !m_pin17q && !m_pin15q) ||
             (m_pin9 && m_pin4 && !m_pin17q && !m_pin16q) ||
             (m_pin9 && m_pin4 && m_pin3 && m_pin16q && m_pin15q && m_pin14q) ||
             (!m_pin19 && !m_pin17q && !m_pin14q) ||
             (!m_pin19 && !m_pin17q && !m_pin15q) ||
             (!m_pin19 && !m_pin17q && !m_pin16q) ||
             (m_pin3 && !m_pin19 && m_pin16q && m_pin15q && m_pin14q);

    m_pin16d = (m_pin9 && m_pin4 && m_pin17q && !m_pin16q) ||
             (m_pin9 && m_pin4 && !m_pin17q && m_pin16q) ||
             (m_pin9 && m_pin4 && m_pin16q && m_pin15q && m_pin14q) ||
             !m_pin19;

    m_pin15d = (m_pin9 && m_pin4 && m_pin15q && m_pin14q) ||
             (m_pin9 && m_pin4 && !m_pin16q && m_pin15q) ||
             (m_pin9 && m_pin4 && !m_pin17q && m_pin15q) ||
             (m_pin9 && m_pin4 && m_pin17q && m_pin16q && !m_pin15q) ||
             !m_pin19;

    m_pin14d = (m_pin9 && m_pin4 && m_pin14q) ||
              (m_pin9 && m_pin4 && m_pin17q && m_pin16q && m_pin15q) ||
              !m_pin19;
*/

	// Simplified equations for better performance

	bool pin4_9 = m_pin9 && m_pin4;

	ready_line = ((!m_pin3 && !pin4_9) ||
				  (pin4_9 && (!m_pin14q || !m_pin17q)) ||
				  (m_pin5 && m_pin4 && (!m_pin15q || !m_pin16q)))? CLEAR_LINE : ASSERT_LINE;

	m_pin17d = (pin4_9 && !m_pin17q && (!m_pin14q || !m_pin15q || !m_pin16q)) ||
			 (((pin4_9 && m_pin3) || (m_pin3 && !m_pin19)) && m_pin16q && m_pin15q && m_pin14q) ||
			 (!m_pin19 && !m_pin17q && (!m_pin14q || !m_pin15q || !m_pin16q));

	m_pin16d = (pin4_9 && m_pin17q && !m_pin16q) ||
			 (pin4_9 && !m_pin17q && m_pin16q) ||
			 (pin4_9 && m_pin16q && m_pin15q && m_pin14q) ||
			 !m_pin19;

	m_pin15d = (pin4_9 && m_pin15q && (m_pin14q || !m_pin16q || !m_pin17q)) ||
			 (pin4_9 && m_pin17q && m_pin16q && !m_pin15q) ||
			 !m_pin19;

	m_pin14d = (pin4_9 && m_pin14q) ||
			  (pin4_9 && m_pin17q && m_pin16q && m_pin15q) ||
			  !m_pin19;

	if (m_prev_ready != ready_line)
	{
		LOGMASKED(LOG_WAIT, "READY = %d (%d %d %d %d, %d %d %d, %d %d)\n", ready_line, m_pin14d, m_pin15d, m_pin16d, m_pin17d, m_pin3, m_pin4, m_pin9, m_pin5, m_pin19);
		m_prev_ready = ready_line;
	}
	m_ready(ready_line);
}

void geneve_pal_device::device_start()
{
	m_ready.resolve_safe();

	save_item(NAME(m_pin3));
	save_item(NAME(m_pin4));
	save_item(NAME(m_pin5));
	save_item(NAME(m_pin9));
	save_item(NAME(m_pin19));
	save_item(NAME(m_pin17q));
	save_item(NAME(m_pin16q));
	save_item(NAME(m_pin15q));
	save_item(NAME(m_pin14q));
}

/********************************************************************
  Genmod daughterboard, soldered to the back of the Gate Array
  The main task of the Genmod is to route all memory accesses to the
  Memex card in the peripheral box.

  Also, the Genmod may inhibit wait states by flipping on the TURBO switch.

  The TIMODE switch blocks the box access for the DRAM space (pages
  00-3F) so that the GROM emulator remains functional. Without this GROM
  emulation (which is hardwired to the board DRAM), the TI-99/4A
  software cannot run.

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

genmod_decoder_device::genmod_decoder_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, GENMOD_DECODER, tag, owner, clock),
	m_debug(false),
	m_turbo(false),
	m_timode(false),
	m_function(0),
	m_function_debug(0),
	m_page(0),
	m_page_debug(0),
	m_gaready(0),
	m_extready(ASSERT_LINE),
	m_sndready(ASSERT_LINE)
{
}

void genmod_decoder_device::set_function(int func, int page)
{
	if (m_debug)
	{
		m_function_debug = func;
		m_page_debug = page;
	}
	else
	{
		m_function = func;  // values from logical map or physical map
		m_page = page;
	}
}

WRITE_LINE_MEMBER(genmod_decoder_device::gaready_in)
{
	m_gaready = state;
}

/*
    READY line from the box. Do not ignore this line, as it is important
    for device operation.
*/
WRITE_LINE_MEMBER(genmod_decoder_device::extready_in)
{
	m_extready = state;
}

/*
    READY line from the sound chip.
*/
WRITE_LINE_MEMBER(genmod_decoder_device::sndready_in)
{
	m_sndready = state;
}

/*
    Wait state generation
    The Genmod board does not look inside the Gate Array. The call we are using
    is just a shorthand for evaluating the states of the select lines.
*/
READ_LINE_MEMBER(genmod_decoder_device::gaready_out)
{
	int func = m_debug? m_function_debug : m_function;
	int page = m_debug? m_page_debug : m_page;

	// External or sound READY must always be respected
	if (m_extready==CLEAR_LINE || m_sndready==CLEAR_LINE) return CLEAR_LINE;

	// When TURBO is off, pass through
	if (!m_turbo) return m_gaready;

	// When accessing internal devices, pass through
	if (geneve_gate_array_device::accessing_devs_s(func)) return m_gaready;

	// In TURBO mode:
	// When TIMODE is active, and we access the DRAM area, pass through
	if (m_timode && ((page & 0xc0)==0)) return m_gaready;

	// When accessing SRAM, SRAMX, EPROM, or page BA, pass through
	if ((((page & 0xf0)==0xf0) || ((page&0xf8)==0xe8) || page == 0xba)) return m_gaready;

	// else no wait states
	return ASSERT_LINE;
}

/*
    Genmod accesses the box also for the DRAM range, but only if timode==0.
    (This includes GROM and cartridge ROM access.)
    Note: It is not sufficient to check for the page area; we need to check
    the select lines (via the static functions).
*/
READ_LINE_MEMBER(genmod_decoder_device::dben_out)
{
	int func = m_debug? m_function_debug : m_function;

	if (geneve_gate_array_device::accessing_box_s(func, true))
		return ASSERT_LINE;

	if (!m_timode && geneve_gate_array_device::accessing_dram_s(func))
		return ASSERT_LINE;

	// This needs to be verified with a real device.
	if (geneve_gate_array_device::accessing_sram_s(func))
		return ASSERT_LINE;

	return CLEAR_LINE;
}

void genmod_decoder_device::device_start()
{
	save_item(NAME(m_debug));
	save_item(NAME(m_turbo));
	save_item(NAME(m_timode));
	save_item(NAME(m_function));
	save_item(NAME(m_function_debug));
	save_item(NAME(m_page));
	save_item(NAME(m_page_debug));
	save_item(NAME(m_gaready));
	save_item(NAME(m_extready));
	save_item(NAME(m_sndready));
}

} // end namespace bus::ti99::internal
