/***************************************************************************
zx.c
machine driver
Juergen Buchmueller <pullmoll@t-online.de>, Dec 1999
****************************************************************************/
#include "includes/zx.h"
#define video_screen_get_refresh(screen) (((screen_config *)(screen)->inline_config)->refresh)
#define DEBUG_ZX81_PORTS 1
#define DEBUG_ZX81_VSYNC 1
#define LOG_ZX81_IOR(_comment) do { if (DEBUG_ZX81_PORTS) logerror("ZX81 IOR: %04x, Data: %02x, Scanline: %d (%s)\n", offset, data, space.machine().first_screen()->vpos(), _comment); } while (0)
#define LOG_ZX81_IOW(_comment) do { if (DEBUG_ZX81_PORTS) logerror("ZX81 IOW: %04x, Data: %02x, Scanline: %d (%s)\n", offset, data, space.machine().first_screen()->vpos(), _comment); } while (0)
#define LOG_ZX81_VSYNC do { if (DEBUG_ZX81_VSYNC) logerror("VSYNC starts in scanline: %d\n", space.machine().first_screen()->vpos()); } while (0)
WRITE8_MEMBER(zx_state::zx_ram_w)
{
UINT8 *RAM = m_region_maincpu->base();
RAM[offset + 0x4000] = data;
if (data & 0x40)
{
space.write_byte(offset | 0xc000, data);
RAM[offset | 0xc000] = data;
}
else
{
space.write_byte(offset | 0xc000, 0);
RAM[offset | 0xc000] = 0;
}
}
/* I know this looks really pointless... but it has to be here */
READ8_MEMBER( zx_state::zx_ram_r )
{
UINT8 *RAM = m_region_maincpu->base();
return RAM[offset | 0xc000];
}
DRIVER_INIT_MEMBER(zx_state,zx)
{
address_space &space = m_maincpu->space(AS_PROGRAM);
space.install_read_bank(0x4000, 0x4000 + m_ram->size() - 1, "bank1");
space.install_write_handler(0x4000, 0x4000 + m_ram->size() - 1, write8_delegate(FUNC(zx_state::zx_ram_w),this));
membank("bank1")->set_base(m_region_maincpu->base() + 0x4000);
}
DIRECT_UPDATE_MEMBER(zx_state::zx_setdirect)
{
if (address & 0xc000)
zx_ula_r(address, m_region_maincpu, 0);
return address;
}
DIRECT_UPDATE_MEMBER(zx_state::pc8300_setdirect)
{
if (address & 0xc000)
zx_ula_r(address, m_region_gfx1, 0);
return address;
}
DIRECT_UPDATE_MEMBER(zx_state::pow3000_setdirect)
{
if (address & 0xc000)
zx_ula_r(address, m_region_gfx1, 1);
return address;
}
void zx_state::machine_reset()
{
m_maincpu->space(AS_PROGRAM).set_direct_update_handler(direct_update_delegate(FUNC(zx_state::zx_setdirect), this));
m_tape_bit = 0x80;
}
MACHINE_RESET_MEMBER(zx_state,pow3000)
{
m_maincpu->space(AS_PROGRAM).set_direct_update_handler(direct_update_delegate(FUNC(zx_state::pow3000_setdirect), this));
m_tape_bit = 0x80;
}
MACHINE_RESET_MEMBER(zx_state,pc8300)
{
m_maincpu->space(AS_PROGRAM).set_direct_update_handler(direct_update_delegate(FUNC(zx_state::pc8300_setdirect), this));
m_tape_bit = 0x80;
}
TIMER_CALLBACK_MEMBER(zx_state::zx_tape_pulse)
{
m_tape_bit = 0x80;
}
READ8_MEMBER( zx_state::zx80_io_r )
{
/* port FE = read keyboard, NTSC/PAL diode, and cass bit; turn off HSYNC-generator/cass-out
The upper 8 bits are used to select a keyboard scan line */
UINT8 data = 0xff;
UINT8 offs = offset & 0xff;
if (offs == 0xfe)
{
if ((offset & 0x0100) == 0)
data &= m_io_row0->read();
if ((offset & 0x0200) == 0)
data &= m_io_row1->read();
if ((offset & 0x0400) == 0)
data &= m_io_row2->read();
if ((offset & 0x0800) == 0)
data &= m_io_row3->read();
if ((offset & 0x1000) == 0)
data &= m_io_row4->read();
if ((offset & 0x2000) == 0)
data &= m_io_row5->read();
if ((offset & 0x4000) == 0)
data &= m_io_row6->read();
if ((offset & 0x8000) == 0)
data &= m_io_row7->read();
if (!m_io_config->read())
data &= ~0x40;
m_cassette->output(+1.0);
if (m_ula_irq_active)
{
zx_ula_bkgnd(0);
m_ula_irq_active = 0;
}
// else
// {
if ((m_cassette->input() < -0.75) && m_tape_bit)
{
m_tape_bit = 0x00;
timer_set(attotime::from_usec(362), TIMER_TAPE_PULSE);
}
data &= ~m_tape_bit;
// }
if (m_ula_frame_vsync == 3)
{
m_ula_frame_vsync = 2;
}
}
return data;
}
READ8_MEMBER( zx_state::zx81_io_r )
{
/* port FB = read printer status, not emulated
FE = read keyboard, NTSC/PAL diode, and cass bit; turn off HSYNC-generator/cass-out
The upper 8 bits are used to select a keyboard scan line */
UINT8 data = 0xff;
UINT8 offs = offset & 0xff;
if (offs == 0xfe)
{
if ((offset & 0x0100) == 0)
data &= m_io_row0->read();
if ((offset & 0x0200) == 0)
data &= m_io_row1->read();
if ((offset & 0x0400) == 0)
data &= m_io_row2->read();
if ((offset & 0x0800) == 0)
data &= m_io_row3->read();
if ((offset & 0x1000) == 0)
data &= m_io_row4->read();
if ((offset & 0x2000) == 0)
data &= m_io_row5->read();
if ((offset & 0x4000) == 0)
data &= m_io_row6->read();
if ((offset & 0x8000) == 0)
data &= m_io_row7->read();
if (!m_io_config->read())
data &= ~0x40;
m_cassette->output(+1.0);
if (m_ula_irq_active)
{
zx_ula_bkgnd(0);
m_ula_irq_active = 0;
}
else
{
if ((m_cassette->input() < -0.75) && m_tape_bit)
{
m_tape_bit = 0x00;
timer_set(attotime::from_usec(362), TIMER_TAPE_PULSE);
}
data &= ~m_tape_bit;
}
if (m_ula_frame_vsync == 3)
{
m_ula_frame_vsync = 2;
}
}
return data;
}
READ8_MEMBER( zx_state::pc8300_io_r )
{
/* port F5 = sound
F6 = unknown
FB = read printer status, not emulated
FE = read keyboard and cass bit; turn off HSYNC-generator/cass-out
The upper 8 bits are used to select a keyboard scan line.
No TV diode */
UINT8 data = 0xff;
UINT8 offs = offset & 0xff;
if (offs == 0xf5)
{
m_speaker_state ^= 1;
m_speaker->level_w(m_speaker_state);
}
else
if (offs == 0xfe)
{
if ((offset & 0x0100) == 0)
data &= m_io_row0->read();
if ((offset & 0x0200) == 0)
data &= m_io_row1->read();
if ((offset & 0x0400) == 0)
data &= m_io_row2->read();
if ((offset & 0x0800) == 0)
data &= m_io_row3->read();
if ((offset & 0x1000) == 0)
data &= m_io_row4->read();
if ((offset & 0x2000) == 0)
data &= m_io_row5->read();
if ((offset & 0x4000) == 0)
data &= m_io_row6->read();
if ((offset & 0x8000) == 0)
data &= m_io_row7->read();
m_cassette->output(+1.0);
if (m_ula_irq_active)
{
zx_ula_bkgnd(0);
m_ula_irq_active = 0;
}
else
{
if ((m_cassette->input() < -0.75) && m_tape_bit)
{
m_tape_bit = 0x00;
timer_set(attotime::from_usec(362), TIMER_TAPE_PULSE);
}
data &= ~m_tape_bit;
}
if (m_ula_frame_vsync == 3)
{
m_ula_frame_vsync = 2;
}
}
return data;
}
READ8_MEMBER( zx_state::pow3000_io_r )
{
/* port 7E = read NTSC/PAL diode
F5 = sound
F6 = unknown
FB = read printer status, not emulated
FE = read keyboard and cass bit; turn off HSYNC-generator/cass-out
The upper 8 bits are used to select a keyboard scan line */
UINT8 data = 0xff;
UINT8 offs = offset & 0xff;
if (offs == 0x7e)
{
data = (m_io_config->read());
}
else
if (offs == 0xf5)
{
m_speaker_state ^= 1;
m_speaker->level_w(m_speaker_state);
}
else
if (offs == 0xfe)
{
if ((offset & 0x0100) == 0)
data &= m_io_row0->read();
if ((offset & 0x0200) == 0)
data &= m_io_row1->read();
if ((offset & 0x0400) == 0)
data &= m_io_row2->read();
if ((offset & 0x0800) == 0)
data &= m_io_row3->read();
if ((offset & 0x1000) == 0)
data &= m_io_row4->read();
if ((offset & 0x2000) == 0)
data &= m_io_row5->read();
if ((offset & 0x4000) == 0)
data &= m_io_row6->read();
if ((offset & 0x8000) == 0)
data &= m_io_row7->read();
m_cassette->output(+1.0);
if (m_ula_irq_active)
{
zx_ula_bkgnd(0);
m_ula_irq_active = 0;
}
else
{
if ((m_cassette->input() < -0.75) && m_tape_bit)
{
m_tape_bit = 0x00;
timer_set(attotime::from_usec(362), TIMER_TAPE_PULSE);
}
data &= ~m_tape_bit;
}
if (m_ula_frame_vsync == 3)
{
m_ula_frame_vsync = 2;
}
}
return data;
}
WRITE8_MEMBER( zx_state::zx80_io_w )
{
/* port FF = write HSYNC and cass data */
UINT8 offs = offset & 0xff;
if (offs == 0xff)
m_cassette->output(-1.0);
}
WRITE8_MEMBER( zx_state::zx81_io_w )
{
/* port F5 = unknown, pc8300/pow3000/lambda only
F6 = unknown, pc8300/pow3000/lambda only
FB = write data to printer, not emulated
FD = turn off NMI generator
FE = turn on NMI generator
FF = write HSYNC and cass data */
int height = m_screen->height();
UINT8 offs = offset & 0xff;
if (offs == 0xfd)
{
m_ula_nmi->reset();
}
else
if (offs == 0xfe)
{
m_ula_nmi->adjust(attotime::zero, 0, m_maincpu->cycles_to_attotime(207));
/* remove the IRQ */
m_ula_irq_active = 0;
}
else
if (offs == 0xff)
{
m_cassette->output(-1.0);
zx_ula_bkgnd(1);
if (m_ula_frame_vsync == 2)
{
m_maincpu->spin_until_time(m_screen->time_until_pos(height - 1, 0));
m_ula_scanline_count = height - 1;
logerror ("S: %d B: %d\n", m_screen->vpos(), m_screen->hpos());
}
}
}