// license:BSD-3-Clause
// copyright-holders:Krzysztof Strzecha, Nathan Woods
/***************************************************************************

  machine/dai.c

  Functions to emulate general aspects of DAI (RAM, ROM, interrupts, I/O ports)

  Krzysztof Strzecha
  Nathan Woods

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

#include "emu.h"
#include "dai.h"

#define LOG_PORTS (1U << 1)

#define VERBOSE (0)
#include "logmacro.h"


TIMER_DEVICE_CALLBACK_MEMBER(dai_state::tms_timer)
{
	m_tms5501->xi7_w(BIT(m_io_keyboard[8]->read(), 2));
	m_tms_timer->adjust(attotime::from_hz(100));
}


/* Discrete I/O devices */


/* Memory */

void dai_state::stack_interrupt_circuit_w(uint8_t data)
{
	m_tms5501->sens_w(1);
	m_tms5501->sens_w(0);
}

uint8_t dai_state::keyboard_r()
{
	uint8_t data = 0x00;

	for (u8 i = 0; i < 8; i++)
		if (m_keyboard_scan_mask & (1 << i))
			data |= m_io_keyboard[i]->read();

	return data;
}

void dai_state::keyboard_w(uint8_t data)
{
	m_keyboard_scan_mask = data;
}

IRQ_CALLBACK_MEMBER(dai_state::int_ack)
{
	return m_tms5501->get_vector();
}

void dai_state::machine_start()
{
	membank("bank2")->configure_entries(0, 4, m_rom + 0x2000, 0x1000);
	m_tms_timer->adjust(attotime::from_hz(100));
	save_item(NAME(m_paddle_select));
	save_item(NAME(m_paddle_enable));
	save_item(NAME(m_cassette_motor));
	save_item(NAME(m_keyboard_scan_mask));
	save_item(NAME(m_4_colours_palette));
}

void dai_state::machine_reset()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	program.install_rom(0x0000, 0x07ff, m_rom);   // do it here for F3
	m_rom_shadow_tap.remove();
	m_rom_shadow_tap = program.install_read_tap(
			0xc000, 0xc7ff,
			"rom_shadow_r",
			[this] (offs_t offset, u8 &data, u8 mem_mask)
			{
				if (!machine().side_effects_disabled())
				{
					// delete this tap
					m_rom_shadow_tap.remove();

					// reinstall RAM over the ROM shadow
					m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x07ff, m_ram);
				}
			},
			&m_rom_shadow_tap);
}

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

    Discrete Devices IO

    FD00    POR1:   IN  bit 0   -
                bit 1   -
                bit 2   PIPGE: Page signal
                bit 3   PIDTR: Serial output ready
                bit 4   PIBU1: Button on paddle 1 (1 = closed)
                bit 5   PIBU1: Button on paddle 2 (1 = closed)
                bit 6   PIRPI: Random data
                bit 7   PICAI: Cassette input data

    FD01    PDLST:  IN  Single pulse used to trigger paddle timer circuit

    FD04    POR1:   OUT bit 0-3 Volume oscillator channel 0
                bit 4-7 Volume oscillator channel 1

    FD05    POR1:   OUT bit 0-3 Volume oscillator channel 2
                bit 4-7 Volume random noise generator

    FD06    POR0:   OUT bit 0   POCAS: Cassette data output
                bit 1-2 PDLMSK: Paddle select
                bit 3   PDPNA:  Paddle enable
                bit 4   POCM1:  Cassette 1 motor control (0 = run)
                bit 5   POCM2:  Cassette 2 motor control (0 = run)
                bit 6-7         ROM bank switching
***************************************************************************/

uint8_t dai_state::io_discrete_devices_r(offs_t offset)
{
	uint8_t data = 0xff;

	switch(offset & 0x0f) {
	case 0:
		data = m_io_keyboard[8]->read();
		data |= 0x08;           // serial ready
		if (machine().rand()&0x01)
			data |= 0x40;       // random number generator
		if (m_cassette->input() > 0.01)
			data |= 0x80;       // tape input
		break;

	default:
		LOGMASKED(LOG_PORTS, "DAI port read : %04x, Data: %02x (discrete devices - unmapped)\n", offset, data);
		break;
	}
	return data;
}

void dai_state::io_discrete_devices_w(offs_t offset, uint8_t data)
{
	switch(offset & 0x000f) {
	case 0x04:
		m_sound->set_volume(offset, data);
		LOGMASKED(LOG_PORTS, "DAI port write: %04x, Data: %02x (discrete devices - osc. 0 volume)\n", offset, data & 0x0f);
		LOGMASKED(LOG_PORTS, "DAI port write: %04x, Data: %02x (discrete devices - osc. 1 volume)\n", offset, BIT(data, 4, 4));
		break;

	case 0x05:
		m_sound->set_volume(offset, data);
		LOGMASKED(LOG_PORTS, "DAI port write: %04x, Data: %02x (discrete devices - osc. 2 volume)\n", offset, data & 0x0f);
		LOGMASKED(LOG_PORTS, "DAI port write: %04x, Data: %02x (discrete devices - noise volume)\n", offset, BIT(data, 4, 4));
		break;

	case 0x06:
		m_paddle_select = BIT(data, 1, 2);
		m_paddle_enable = BIT(data, 3);
		m_cassette_motor[0] = BIT(data, 4);
		m_cassette_motor[1] = BIT(data, 5);
		m_cassette->change_state(m_cassette_motor[0]?CASSETTE_MOTOR_DISABLED:CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
		m_cassette->output(BIT(data, 0) ? -1.0 : 1.0);
		membank("bank2")->set_entry(BIT(data, 6, 2));
		LOGMASKED(LOG_PORTS, "DAI port write: %04x, Data: %02x (discrete devices - paddle select)\n", offset, BIT(data, 1, 2));
		LOGMASKED(LOG_PORTS, "DAI port write: %04x, Data: %02x (discrete devices - paddle enable)\n", offset, BIT(data, 3));
		LOGMASKED(LOG_PORTS, "DAI port write: %04x, Data: %02x (discrete devices - cassette motor 1)\n", offset, BIT(data, 4));
		LOGMASKED(LOG_PORTS, "DAI port write: %04x, Data: %02x (discrete devices - cassette motor 2)\n", offset, BIT(data, 5));
		LOGMASKED(LOG_PORTS, "DAI port write: %04x, Data: %02x (discrete devices - ROM bank)\n", offset, BIT(data, 6, 2));
		break;

	default:
		LOGMASKED(LOG_PORTS, "DAI port write: %04x, Data: %02x (discrete devices - unmapped)\n", offset, data);
		break;
	}
}

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

    PIT8253

    Offset need to be shifted by 1 to right, because the PIT is
    connected to A1 and A2

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

uint8_t dai_state::pit_r(offs_t offset)
{
	return m_pit->read(BIT(offset, 1, 2));
}

void dai_state::pit_w(offs_t offset, uint8_t data)
{
	m_pit->write(BIT(offset, 1, 2), data);
}

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

    AMD 9911 mathematical coprocesor

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

uint8_t dai_state::amd9511_r()
{
	/* optional and not present at this moment */
	return 0xff;
}

void dai_state::amd9511_w(offs_t offset, uint8_t data)
{
	logerror("Writing to AMD9511 math chip, %04x, %02x\n", offset, data);
}
