zx: Start of an hardware-correct rewrite [O. Galibert]

Missing:
- hsync is not handled as it should yet, especially missing on the zx80
- the versions with an external charrom are not handled yet
- the zx97 is weird

On the other hand, the zx81 ain't bad and the hires modes work.
This commit is contained in:
Olivier Galibert 2015-10-28 09:54:26 +01:00
parent 47f56372ee
commit c4a787e527
6 changed files with 324 additions and 498 deletions

View File

@ -473,7 +473,11 @@ inline UINT8 z80_device::rop()
{ {
unsigned pc = PCD; unsigned pc = PCD;
PC++; PC++;
return m_decrypted_opcodes_direct->read_byte(pc); UINT8 res = m_decrypted_opcodes_direct->read_byte(pc);
m_icount -= 2;
m_refresh_cb((m_i << 8) | (m_r2 & 0x80) | ((m_r-1) & 0x7f));
m_icount += 2;
return res;
} }
/**************************************************************** /****************************************************************
@ -3141,6 +3145,9 @@ void z80_device::take_interrupt()
else else
irq_vector = m_irq_callback(*this, 0); irq_vector = m_irq_callback(*this, 0);
/* Say hi */
m_irqack_cb(true);
LOG(("Z80 '%s' single int. irq_vector $%02x\n", tag(), irq_vector)); LOG(("Z80 '%s' single int. irq_vector $%02x\n", tag(), irq_vector));
/* Interrupt mode 2. Call [i:databyte] */ /* Interrupt mode 2. Call [i:databyte] */
@ -3429,6 +3436,9 @@ void z80_device::device_start()
m_cc_xy = cc_xy; m_cc_xy = cc_xy;
m_cc_xycb = cc_xycb; m_cc_xycb = cc_xycb;
m_cc_ex = cc_ex; m_cc_ex = cc_ex;
m_irqack_cb.resolve_safe();
m_refresh_cb.resolve_safe();
} }
void nsc800_device::device_start() void nsc800_device::device_start()
@ -3705,7 +3715,9 @@ z80_device::z80_device(const machine_config &mconfig, const char *tag, device_t
cpu_device(mconfig, Z80, "Z80", tag, owner, clock, "z80", __FILE__), cpu_device(mconfig, Z80, "Z80", tag, owner, clock, "z80", __FILE__),
m_program_config("program", ENDIANNESS_LITTLE, 8, 16, 0), m_program_config("program", ENDIANNESS_LITTLE, 8, 16, 0),
m_decrypted_opcodes_config("decrypted_opcodes", ENDIANNESS_LITTLE, 8, 16, 0), m_decrypted_opcodes_config("decrypted_opcodes", ENDIANNESS_LITTLE, 8, 16, 0),
m_io_config("io", ENDIANNESS_LITTLE, 8, 16, 0) m_io_config("io", ENDIANNESS_LITTLE, 8, 16, 0),
m_irqack_cb(*this),
m_refresh_cb(*this)
{ {
} }
@ -3713,7 +3725,9 @@ z80_device::z80_device(const machine_config &mconfig, device_type type, const ch
cpu_device(mconfig, type, name, tag, owner, clock, shortname, source), cpu_device(mconfig, type, name, tag, owner, clock, shortname, source),
m_program_config("program", ENDIANNESS_LITTLE, 8, 16, 0), m_program_config("program", ENDIANNESS_LITTLE, 8, 16, 0),
m_decrypted_opcodes_config("decrypted_opcodes", ENDIANNESS_LITTLE, 8, 16, 0), m_decrypted_opcodes_config("decrypted_opcodes", ENDIANNESS_LITTLE, 8, 16, 0),
m_io_config("io", ENDIANNESS_LITTLE, 8, 16, 0) m_io_config("io", ENDIANNESS_LITTLE, 8, 16, 0),
m_irqack_cb(*this),
m_refresh_cb(*this)
{ {
} }

View File

@ -7,6 +7,12 @@
#include "z80daisy.h" #include "z80daisy.h"
#define MCFG_Z80_SET_IRQACK_CALLBACK(_devcb) \
devcb = &z80_device::set_irqack_cb(*device, DEVCB_##_devcb);
#define MCFG_Z80_SET_REFRESH_CALLBACK(_devcb) \
devcb = &z80_device::set_refresh_cb(*device, DEVCB_##_devcb);
enum enum
{ {
NSC800_RSTA = INPUT_LINE_IRQ0 + 1, NSC800_RSTA = INPUT_LINE_IRQ0 + 1,
@ -38,6 +44,8 @@ public:
DECLARE_WRITE_LINE_MEMBER( irq_line ); DECLARE_WRITE_LINE_MEMBER( irq_line );
void z80_set_cycle_tables(const UINT8 *op, const UINT8 *cb, const UINT8 *ed, const UINT8 *xy, const UINT8 *xycb, const UINT8 *ex); void z80_set_cycle_tables(const UINT8 *op, const UINT8 *cb, const UINT8 *ed, const UINT8 *xy, const UINT8 *xycb, const UINT8 *ex);
template<class _Object> static devcb_base &set_irqack_cb(device_t &device, _Object object) { return downcast<z80_device &>(device).m_irqack_cb.set_callback(object); }
template<class _Object> static devcb_base &set_refresh_cb(device_t &device, _Object object) { return downcast<z80_device &>(device).m_refresh_cb.set_callback(object); }
protected: protected:
z80_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); z80_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
@ -240,6 +248,8 @@ protected:
address_space *m_io; address_space *m_io;
direct_read_data *m_direct; direct_read_data *m_direct;
direct_read_data *m_decrypted_opcodes_direct; direct_read_data *m_decrypted_opcodes_direct;
devcb_write_line m_irqack_cb;
devcb_write16 m_refresh_cb;
PAIR m_prvpc; PAIR m_prvpc;
PAIR m_pc; PAIR m_pc;

View File

@ -1,5 +1,5 @@
// license:GPL-2.0+ // license:GPL-2.0+
// copyright-holders:Juergen Buchmueller, Krzysztof Strzecha, Robbbert // copyright-holders: Olivier Galibert, Juergen Buchmueller, Krzysztof Strzecha, Robbbert
/*************************************************************************** /***************************************************************************
zx.c zx.c
@ -9,7 +9,7 @@
Fixes and additions by Krzysztof Strzecha: Fixes and additions by Krzysztof Strzecha:
07.06.2004 Tape loading added. Some cleanups of debug code. 07.06.2004 Tape loading added. Some cleanups of debug code.
Fixed stupid bug in timings (vblank duration). Fixed stupid bug in timings (vblank duration).
MACHINE_NOT_WORKING flag removed. GAME_NOT_WORKING flag removed.
29.05.2004 CPU clock, number of scanlines, vblank duration corrected. 29.05.2004 CPU clock, number of scanlines, vblank duration corrected.
Some cleanups. Two non-working TESTDRIVERS added. Some cleanups. Two non-working TESTDRIVERS added.
14.05.2004 Finally fixed and readded. 14.05.2004 Finally fixed and readded.
@ -31,8 +31,8 @@
- Modernised. - Modernised.
To do / problems: To do / problems:
- Halt-on-nmi emulation needs a cycle-exact z80
- Some memory areas are not mirrored as they should. - Some memory areas are not mirrored as they should.
- Video hardware is not fully emulated, so it does not support pseudo hi-res and hi-res modes.
- The screen in pc8300/pow3000/lambda jumps when you type something. - The screen in pc8300/pow3000/lambda jumps when you type something.
- lambda/pow3000 32k memory pack is unemulated, because where is the upper 16k mirror going to be? - lambda/pow3000 32k memory pack is unemulated, because where is the upper 16k mirror going to be?
- h4th and tree4th need their address maps worked out (eg, the stack is set to FB80) - h4th and tree4th need their address maps worked out (eg, the stack is set to FB80)
@ -47,8 +47,18 @@
/* Memory Maps */ /* Memory Maps */
static ADDRESS_MAP_START( zx80_map, AS_PROGRAM, 8, zx_state ) static ADDRESS_MAP_START( zx80_map, AS_PROGRAM, 8, zx_state )
AM_RANGE(0x0000, 0x0fff) AM_ROM AM_MIRROR(0x3000)
AM_RANGE(0x4000, 0xffff) AM_RAM
ADDRESS_MAP_END
static ADDRESS_MAP_START( zx81_map, AS_PROGRAM, 8, zx_state )
AM_RANGE(0x0000, 0x1fff) AM_ROM AM_MIRROR(0x2000) AM_RANGE(0x0000, 0x1fff) AM_ROM AM_MIRROR(0x2000)
AM_RANGE(0xc000, 0xffff) AM_RAM_READ(zx_ram_r) AM_RANGE(0x4000, 0xffff) AM_RAM
ADDRESS_MAP_END
static ADDRESS_MAP_START( ula_map, AS_DECRYPTED_OPCODES, 8, zx_state )
AM_RANGE(0x0000, 0x7fff) AM_READ(ula_low_r)
AM_RANGE(0x8000, 0xffff) AM_READ(ula_high_r)
ADDRESS_MAP_END ADDRESS_MAP_END
static ADDRESS_MAP_START( zx80_io_map, AS_IO, 8, zx_state ) static ADDRESS_MAP_START( zx80_io_map, AS_IO, 8, zx_state )
@ -291,38 +301,6 @@ static INPUT_PORTS_START( pow3000 )
INPUT_PORTS_END INPUT_PORTS_END
/* F4 character display */
static const gfx_layout zx_gfx_layout =
{
8, 8, /* 8x8 pixels */
64, /* 64 codes */
1, /* 1 bit per pixel */
{0}, /* no bitplanes */
/* x offsets */
{0, 1, 2, 3, 4, 5, 6, 7},
/* y offsets */
{0 * 8, 1 * 8, 2 * 8, 3 * 8, 4 * 8, 5 * 8, 6 * 8, 7 * 8},
8 * 8 /* eight bytes per code */
};
/* Graphics Decode Information */
static GFXDECODE_START( zx80 )
GFXDECODE_ENTRY( "maincpu", 0x0e00, zx_gfx_layout, 0, 2 )
GFXDECODE_END
static GFXDECODE_START( zx81 )
GFXDECODE_ENTRY( "maincpu", 0x1e00, zx_gfx_layout, 0, 2 )
GFXDECODE_END
static GFXDECODE_START( pc8300 )
GFXDECODE_ENTRY( "gfx1", 0, zx_gfx_layout, 0, 2 )
GFXDECODE_END
/* Palette Initialization */ /* Palette Initialization */
@ -330,61 +308,34 @@ PALETTE_INIT_MEMBER(zx_state, zx)
{ {
palette.set_pen_color(0,rgb_t::white); /* white */ palette.set_pen_color(0,rgb_t::white); /* white */
palette.set_pen_color(1,rgb_t::black); /* black */ palette.set_pen_color(1,rgb_t::black); /* black */
palette.set_pen_color(2,rgb_t::black); /* black */
palette.set_pen_color(3,rgb_t::white); /* white */
} }
PALETTE_INIT_MEMBER(zx_state,ts1000) PALETTE_INIT_MEMBER(zx_state,ts1000)
{ {
palette.set_pen_color(0,rgb_t(64, 244, 244)); /* cyan */ palette.set_pen_color(0,rgb_t(64, 244, 244)); /* cyan */
palette.set_pen_color(1,rgb_t::black); /* black */ palette.set_pen_color(1,rgb_t::black); /* black */
palette.set_pen_color(2,rgb_t::black); /* black */
palette.set_pen_color(3,rgb_t(64, 244, 244)); /* cyan */
} }
#define ZX81_CPU_CLOCK 3250000
#define ZX81_CYCLES_PER_SCANLINE 207
#define ZX81_PIXELS_PER_SCANLINE 256
#define ZX81_CYCLES_PER_VBLANK 1235
#define ZX81_VBLANK_DURATION (1.0*ZX81_CYCLES_PER_VBLANK/ZX81_CPU_CLOCK*1000*1000)
#define ZX81_PAL_SCANLINES 304
#define ZX81_PAL_FRAMES_PER_SECOND (1.0*ZX81_CPU_CLOCK/(ZX81_PAL_SCANLINES*ZX81_CYCLES_PER_SCANLINE+ZX81_CYCLES_PER_VBLANK))
#define ZX81_NTSC_SCANLINES 256
#define ZX81_NTSC_FRAMES_PER_SECOND (1.0*ZX81_CPU_CLOCK/(ZX81_NTSC_SCANLINES*ZX81_CYCLES_PER_SCANLINE+ZX81_CYCLES_PER_VBLANK))
/* Machine Drivers */
static MACHINE_CONFIG_START( zx80, zx_state ) static MACHINE_CONFIG_START( zx80, zx_state )
/* basic machine hardware */ /* basic machine hardware */
MCFG_CPU_ADD("maincpu", Z80, ZX81_CPU_CLOCK) MCFG_CPU_ADD("maincpu", Z80, XTAL_6_5MHz/2)
MCFG_CPU_PROGRAM_MAP(zx80_map) MCFG_CPU_PROGRAM_MAP(zx80_map)
MCFG_CPU_IO_MAP(zx80_io_map) MCFG_CPU_IO_MAP(zx80_io_map)
MCFG_SCREEN_ADD("screen", RASTER) MCFG_CPU_DECRYPTED_OPCODES_MAP(ula_map)
MCFG_SCREEN_REFRESH_RATE(ZX81_PAL_FRAMES_PER_SECOND) MCFG_Z80_SET_REFRESH_CALLBACK(WRITE16(zx_state, refresh_w))
MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(ZX81_VBLANK_DURATION))
MCFG_SCREEN_ADD("screen", RASTER)
MCFG_SCREEN_REFRESH_RATE(XTAL_6_5MHz/2/64159.0) // 54223 for NTSC
/* video hardware */ /* video hardware */
MCFG_SCREEN_UPDATE_DRIVER(zx_state, screen_update) MCFG_SCREEN_UPDATE_DRIVER(zx_state, screen_update)
MCFG_SCREEN_SIZE(ZX81_PIXELS_PER_SCANLINE, ZX81_PAL_SCANLINES) MCFG_SCREEN_SIZE(384, 311)
MCFG_SCREEN_VISIBLE_AREA(0, ZX81_PIXELS_PER_SCANLINE-1, 0, ZX81_PAL_SCANLINES-1) MCFG_SCREEN_VISIBLE_AREA(0, 383, 0, 310)
MCFG_SCREEN_VBLANK_DRIVER(zx_state, screen_eof_zx)
MCFG_SCREEN_PALETTE("palette") MCFG_SCREEN_PALETTE("palette")
MCFG_GFXDECODE_ADD("gfxdecode", "palette", zx80) MCFG_PALETTE_ADD("palette", 2)
MCFG_PALETTE_ADD("palette", 4)
MCFG_PALETTE_INIT_OWNER(zx_state,zx) MCFG_PALETTE_INIT_OWNER(zx_state,zx)
/* sound hardware */
MCFG_SPEAKER_STANDARD_MONO("mono")
MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0) /* Used by pc8300/lambda/pow3000 */
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.75)
MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette")
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25)
MCFG_CASSETTE_ADD( "cassette" ) MCFG_CASSETTE_ADD( "cassette" )
MCFG_CASSETTE_FORMATS(zx80_o_format) MCFG_CASSETTE_FORMATS(zx80_o_format)
MCFG_CASSETTE_DEFAULT_STATE(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED) MCFG_CASSETTE_DEFAULT_STATE(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED)
@ -392,60 +343,52 @@ static MACHINE_CONFIG_START( zx80, zx_state )
/* internal ram */ /* internal ram */
MCFG_RAM_ADD(RAM_TAG) MCFG_RAM_ADD(RAM_TAG)
MCFG_RAM_DEFAULT_SIZE("1K") MCFG_RAM_DEFAULT_SIZE("1K")
MCFG_RAM_EXTRA_OPTIONS("16K") MCFG_RAM_EXTRA_OPTIONS("16K,32K,48K")
MACHINE_CONFIG_END MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( zx81, zx80 ) static MACHINE_CONFIG_DERIVED( zx81, zx80 )
MCFG_CPU_MODIFY("maincpu") MCFG_CPU_MODIFY("maincpu")
MCFG_CPU_PROGRAM_MAP(zx81_map)
MCFG_CPU_IO_MAP(zx81_io_map) MCFG_CPU_IO_MAP(zx81_io_map)
MCFG_GFXDECODE_MODIFY("gfxdecode", zx81)
MCFG_CASSETTE_MODIFY( "cassette" ) MCFG_CASSETTE_MODIFY( "cassette" )
MCFG_CASSETTE_FORMATS(zx81_p_format) MCFG_CASSETTE_FORMATS(zx81_p_format)
MACHINE_CONFIG_END MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( zx81_spk, zx81 )
/* sound hardware */
/* Used by pc8300/lambda/pow3000 */
MCFG_SPEAKER_STANDARD_MONO("mono")
MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0)
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.75)
MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette")
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25)
MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( ts1000, zx81 ) static MACHINE_CONFIG_DERIVED( ts1000, zx81 )
MCFG_PALETTE_MODIFY("palette") MCFG_PALETTE_MODIFY("palette")
MCFG_PALETTE_INIT_OWNER(zx_state,ts1000) MCFG_PALETTE_INIT_OWNER(zx_state, ts1000)
MACHINE_CONFIG_END MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( ts1500, ts1000 ) static MACHINE_CONFIG_DERIVED( ts1500, ts1000 )
/* internal ram */ /* internal ram */
MCFG_RAM_MODIFY(RAM_TAG) MCFG_RAM_MODIFY(RAM_TAG)
MCFG_RAM_DEFAULT_SIZE("16K") MCFG_RAM_DEFAULT_SIZE("16K")
MACHINE_CONFIG_END MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( pc8300, zx81 ) static MACHINE_CONFIG_DERIVED( pc8300, zx81_spk )
MCFG_CPU_MODIFY("maincpu") MCFG_CPU_MODIFY("maincpu")
MCFG_CPU_IO_MAP(pc8300_io_map) MCFG_CPU_IO_MAP(pc8300_io_map)
MCFG_MACHINE_RESET_OVERRIDE(zx_state,pc8300)
MCFG_SCREEN_MODIFY("screen")
MCFG_SCREEN_REFRESH_RATE(ZX81_NTSC_FRAMES_PER_SECOND)
MCFG_SCREEN_SIZE(ZX81_PIXELS_PER_SCANLINE, ZX81_NTSC_SCANLINES)
MCFG_SCREEN_VISIBLE_AREA(0, ZX81_PIXELS_PER_SCANLINE-1, 0, ZX81_NTSC_SCANLINES-1)
MCFG_GFXDECODE_MODIFY("gfxdecode", pc8300)
/* internal ram */ /* internal ram */
MCFG_RAM_MODIFY(RAM_TAG) MCFG_RAM_MODIFY(RAM_TAG)
MCFG_RAM_DEFAULT_SIZE("16K") MCFG_RAM_DEFAULT_SIZE("16K")
MACHINE_CONFIG_END MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( pow3000, zx81 ) static MACHINE_CONFIG_DERIVED( pow3000, zx81_spk )
MCFG_CPU_MODIFY("maincpu") MCFG_CPU_MODIFY("maincpu")
MCFG_CPU_IO_MAP(pow3000_io_map) MCFG_CPU_IO_MAP(pow3000_io_map)
MCFG_MACHINE_RESET_OVERRIDE(zx_state,pow3000)
MCFG_GFXDECODE_MODIFY("gfxdecode", pc8300)
/* internal ram */ /* internal ram */
MCFG_RAM_MODIFY(RAM_TAG) MCFG_RAM_MODIFY(RAM_TAG)
MCFG_RAM_DEFAULT_SIZE("2K") MCFG_RAM_DEFAULT_SIZE("2K")
@ -456,7 +399,7 @@ MACHINE_CONFIG_END
/* ROMs */ /* ROMs */
ROM_START(zx80) ROM_START(zx80)
ROM_REGION( 0x10000, "maincpu",0 ) ROM_REGION( 0x1000, "maincpu",0 )
ROM_SYSTEM_BIOS(0, "default", "BASIC") ROM_SYSTEM_BIOS(0, "default", "BASIC")
ROMX_LOAD( "zx80.rom", 0x0000, 0x1000, CRC(4c7fc597) SHA1(b6769a3197c77009e0933e038c15b43cf4c98c7a), ROM_BIOS(1) ) ROMX_LOAD( "zx80.rom", 0x0000, 0x1000, CRC(4c7fc597) SHA1(b6769a3197c77009e0933e038c15b43cf4c98c7a), ROM_BIOS(1) )
ROM_SYSTEM_BIOS(1, "aszmic", "ASZMIC") ROM_SYSTEM_BIOS(1, "aszmic", "ASZMIC")
@ -464,7 +407,7 @@ ROM_START(zx80)
ROM_END ROM_END
ROM_START(zx81) ROM_START(zx81)
ROM_REGION( 0x10000, "maincpu",0 ) ROM_REGION( 0x2000, "maincpu",0 )
ROM_SYSTEM_BIOS(0, "3rd", "3rd rev.") ROM_SYSTEM_BIOS(0, "3rd", "3rd rev.")
ROMX_LOAD( "zx81b.rom", 0x0000, 0x2000, CRC(522c37b8) SHA1(c6d8e06cb936989f6e1cc7a56d1f092da854a515), ROM_BIOS(1) ) ROMX_LOAD( "zx81b.rom", 0x0000, 0x2000, CRC(522c37b8) SHA1(c6d8e06cb936989f6e1cc7a56d1f092da854a515), ROM_BIOS(1) )
ROM_SYSTEM_BIOS(1, "1st", "1st rev.") ROM_SYSTEM_BIOS(1, "1st", "1st rev.")
@ -478,22 +421,22 @@ ROM_START(zx81)
ROM_END ROM_END
ROM_START(ts1000) ROM_START(ts1000)
ROM_REGION( 0x10000, "maincpu",0 ) ROM_REGION( 0x2000, "maincpu",0 )
ROM_LOAD( "zx81a.rom", 0x0000, 0x2000, CRC(4b1dd6eb) SHA1(7b143ee964e9ada89d1f9e88f0bd48d919184cfc) ) ROM_LOAD( "zx81a.rom", 0x0000, 0x2000, CRC(4b1dd6eb) SHA1(7b143ee964e9ada89d1f9e88f0bd48d919184cfc) )
ROM_END ROM_END
ROM_START(ts1500) ROM_START(ts1500)
ROM_REGION( 0x10000, "maincpu",0 ) ROM_REGION( 0x2000, "maincpu",0 )
ROM_LOAD( "d2364c_649.u2", 0x0000, 0x2000, CRC(7dd19c48) SHA1(3eb437359221b4406d236085ec66fa02278e7495) ) ROM_LOAD( "d2364c_649.u2", 0x0000, 0x2000, CRC(7dd19c48) SHA1(3eb437359221b4406d236085ec66fa02278e7495) )
ROM_END ROM_END
ROM_START(ringo470) ROM_START(ringo470)
ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF ) ROM_REGION( 0x2000, "maincpu", ROMREGION_ERASEFF )
ROM_LOAD( "ringo470.rom", 0x0000, 0x2000, CRC(b9c5abec) SHA1(191c4994adfffe4f83b98dc3959dde2724b1dbac) ) ROM_LOAD( "ringo470.rom", 0x0000, 0x2000, CRC(b9c5abec) SHA1(191c4994adfffe4f83b98dc3959dde2724b1dbac) )
ROM_END ROM_END
ROM_START(pc8300) ROM_START(pc8300)
ROM_REGION( 0x10000, "maincpu",0 ) ROM_REGION( 0x2000, "maincpu",0 )
ROM_LOAD( "8300_org.rom", 0x0000, 0x2000, CRC(a350f2b1) SHA1(6a9be484556cc27a9cd9d71085d2027c6243333f) ) ROM_LOAD( "8300_org.rom", 0x0000, 0x2000, CRC(a350f2b1) SHA1(6a9be484556cc27a9cd9d71085d2027c6243333f) )
ROM_REGION( 0x200, "gfx1", 0 ) ROM_REGION( 0x200, "gfx1", 0 )
@ -501,7 +444,7 @@ ROM_START(pc8300)
ROM_END ROM_END
ROM_START(pow3000) ROM_START(pow3000)
ROM_REGION( 0x10000, "maincpu",0 ) ROM_REGION( 0x2000, "maincpu",0 )
ROM_LOAD( "pow3000.rom", 0x0000, 0x2000, CRC(8a49b2c3) SHA1(9b22daf2f3a991aa6a358ef95b091654c3ca1bdf) ) ROM_LOAD( "pow3000.rom", 0x0000, 0x2000, CRC(8a49b2c3) SHA1(9b22daf2f3a991aa6a358ef95b091654c3ca1bdf) )
ROM_REGION( 0x200, "gfx1", 0 ) ROM_REGION( 0x200, "gfx1", 0 )
@ -509,7 +452,7 @@ ROM_START(pow3000)
ROM_END ROM_END
ROM_START(lambda) ROM_START(lambda)
ROM_REGION( 0x10000, "maincpu",0 ) ROM_REGION( 0x2000, "maincpu",0 )
ROM_LOAD( "lambda.rom", 0x0000, 0x2000, CRC(8a49b2c3) SHA1(9b22daf2f3a991aa6a358ef95b091654c3ca1bdf) ) ROM_LOAD( "lambda.rom", 0x0000, 0x2000, CRC(8a49b2c3) SHA1(9b22daf2f3a991aa6a358ef95b091654c3ca1bdf) )
ROM_REGION( 0x200, "gfx1", 0 ) ROM_REGION( 0x200, "gfx1", 0 )
@ -517,28 +460,26 @@ ROM_START(lambda)
ROM_END ROM_END
ROM_START( tk85 ) ROM_START( tk85 )
ROM_REGION( 0x10000, "maincpu", 0 ) ROM_REGION( 0x2800, "maincpu", 0 )
ROM_LOAD( "tk85.rom", 0x0000, 0x2800, CRC(8972d756) SHA1(7b961a1733fc047eb682150a32e17bca10a018d2) ) ROM_LOAD( "tk85.rom", 0x0000, 0x2800, CRC(8972d756) SHA1(7b961a1733fc047eb682150a32e17bca10a018d2) )
ROM_END ROM_END
/* This homebrew has 192k of RAM and 32k of ROM via bankswitching. One of the primary bankswitching lines is /M1, /* This homebrew has 192k of RAM and 32k of ROM via bankswitching. One of the primary bankswitching lines is /M1,
which is not emulated by MAME's z80. */ which is not emulated by MAME's z80. */
ROM_START( zx97 ) ROM_START( zx97 )
ROM_REGION( 0x10000, "maincpu", 0 ) ROM_REGION( 0x8000, "maincpu", 0 )
ROM_LOAD( "zx97.rom", 0x0000, 0x2000, CRC(5cf49744) SHA1(b2a486efdc7b2bc3dc8e5a441ea5532bfa3207bd) ) ROM_LOAD( "zx97.rom", 0x0000, 0x8000, CRC(5cf49744) SHA1(b2a486efdc7b2bc3dc8e5a441ea5532bfa3207bd) )
ROM_IGNORE( 0x6000 ) /* Unemulated bankswitched part */
ROM_END ROM_END
/* Game Drivers */ /* Game Drivers */
/* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME FLAGS */ COMP( 1980, zx80, 0, 0, zx80, zx80, zx_state, zx, "Sinclair Research Ltd", "ZX-80", MACHINE_NO_SOUND )
COMP( 1980, zx80, 0, 0, zx80, zx80, zx_state, zx, "Sinclair Research Ltd", "ZX-80", 0 ) COMP( 1981, zx81, 0, 0, zx81, zx81, zx_state, zx, "Sinclair Research Ltd", "ZX-81", MACHINE_NO_SOUND )
COMP( 1981, zx81, 0, 0, zx81, zx81, zx_state, zx, "Sinclair Research Ltd", "ZX-81", 0 ) COMP( 1982, ts1000, zx81, 0, ts1000, zx81, zx_state, zx, "Timex Sinclair", "Timex Sinclair 1000", MACHINE_NO_SOUND )
COMP( 1982, ts1000, zx81, 0, ts1000, zx81, zx_state, zx, "Timex Sinclair", "Timex Sinclair 1000", 0 ) COMP( 1983, ts1500, zx81, 0, ts1500, zx81, zx_state, zx, "Timex Sinclair", "Timex Sinclair 1500", MACHINE_NO_SOUND )
COMP( 1983, ts1500, zx81, 0, ts1500, zx81, zx_state, zx, "Timex Sinclair", "Timex Sinclair 1500", 0 ) COMP( 1983, tk85, zx81, 0, ts1000, zx81, zx_state, zx, "Microdigital", "TK85", MACHINE_NO_SOUND )
COMP( 1983, tk85, zx81, 0, ts1000, zx81, zx_state, zx, "Microdigital", "TK85", 0 ) COMP( 1983, ringo470, zx81, 0, ts1000, zx81, zx_state, zx, "Ritas do Brasil Ltda", "Ringo 470", MACHINE_NO_SOUND )
COMP( 1983, ringo470, zx81, 0, ts1000, zx81, zx_state, zx, "Ritas do Brasil Ltda", "Ringo 470", 0 )
COMP( 1984, pc8300, zx81, 0, pc8300, pc8300, zx_state, zx, "Your Computer", "PC8300", 0 ) COMP( 1984, pc8300, zx81, 0, pc8300, pc8300, zx_state, zx, "Your Computer", "PC8300", 0 )
COMP( 1983, pow3000, zx81, 0, pow3000, pow3000, zx_state, zx, "Creon Enterprises", "Power 3000", 0 ) COMP( 1983, pow3000, zx81, 0, pow3000, pow3000, zx_state, zx, "Creon Enterprises", "Power 3000", 0 )
COMP( 1982, lambda, zx81, 0, pow3000, pow3000, zx_state, zx, "Lambda Electronics Ltd", "Lambda 8300", 0 ) COMP( 1982, lambda, zx81, 0, pow3000, pow3000, zx_state, zx, "Lambda Electronics Ltd", "Lambda 8300", 0 )
COMP( 1997, zx97, zx81, 0, zx81, zx81, zx_state, zx, "Wilf Rigter", "ZX97", MACHINE_NOT_WORKING | MACHINE_UNOFFICIAL ) COMP( 1997, zx97, zx81, 0, zx81, zx81, zx_state, zx, "Wilf Rigter", "ZX97", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_UNOFFICIAL )

View File

@ -21,13 +21,6 @@
class zx_state : public driver_device class zx_state : public driver_device
{ {
public: public:
enum
{
TIMER_TAPE_PULSE,
TIMER_ULA_NMI,
TIMER_ULA_IRQ
};
zx_state(const machine_config &mconfig, device_type type, const char *tag) zx_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag), : driver_device(mconfig, type, tag),
m_maincpu(*this, "maincpu"), m_maincpu(*this, "maincpu"),
@ -49,49 +42,36 @@ public:
UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
bitmap_ind16 m_bitmap; DECLARE_READ8_MEMBER(ula_high_r);
DECLARE_READ8_MEMBER(ula_low_r);
DECLARE_READ8_MEMBER(zx_ram_r); DECLARE_WRITE16_MEMBER(refresh_w);
DECLARE_READ8_MEMBER(zx80_io_r); DECLARE_READ8_MEMBER(zx80_io_r);
DECLARE_READ8_MEMBER(zx81_io_r); DECLARE_READ8_MEMBER(zx81_io_r);
DECLARE_READ8_MEMBER(pc8300_io_r); DECLARE_READ8_MEMBER(pc8300_io_r);
DECLARE_READ8_MEMBER(pow3000_io_r); DECLARE_READ8_MEMBER(pow3000_io_r);
DECLARE_WRITE8_MEMBER(zx80_io_w); DECLARE_WRITE8_MEMBER(zx80_io_w);
DECLARE_WRITE8_MEMBER(zx81_io_w); DECLARE_WRITE8_MEMBER(zx81_io_w);
emu_timer *m_ula_nmi;
int m_ula_irq_active;
int m_ula_frame_vsync;
int m_ula_scanline_count;
UINT8 m_tape_bit;
UINT8 m_speaker_state;
int m_old_x;
int m_old_y;
UINT8 m_old_c;
UINT8 m_charline[32];
UINT8 m_charline_ptr;
int m_offs1;
void zx_ula_bkgnd(UINT8 color);
DECLARE_WRITE8_MEMBER(zx_ram_w);
DECLARE_DIRECT_UPDATE_MEMBER(zx_setdirect);
DECLARE_DIRECT_UPDATE_MEMBER(pc8300_setdirect);
DECLARE_DIRECT_UPDATE_MEMBER(pow3000_setdirect);
DECLARE_DRIVER_INIT(zx); DECLARE_DRIVER_INIT(zx);
virtual void machine_reset(); virtual void machine_reset();
virtual void video_start(); virtual void video_start();
DECLARE_PALETTE_INIT(zx); DECLARE_PALETTE_INIT(zx);
DECLARE_PALETTE_INIT(ts1000); DECLARE_PALETTE_INIT(ts1000);
DECLARE_MACHINE_RESET(pc8300); void zx_tape_input();
DECLARE_MACHINE_RESET(pow3000); void zx_ula_hsync();
void screen_eof_zx(screen_device &screen, bool state);
TIMER_CALLBACK_MEMBER(zx_tape_pulse); UINT32 get_ram_size();
TIMER_CALLBACK_MEMBER(zx_ula_nmi);
TIMER_CALLBACK_MEMBER(zx_ula_irq);
protected: protected:
enum
{
TIMER_TAPE_INPUT,
TIMER_ULA_HSYNC
};
required_device<cpu_device> m_maincpu; required_device<cpu_device> m_maincpu;
required_device<ram_device> m_ram; required_device<ram_device> m_ram;
required_device<cassette_image_device> m_cassette; required_device<cassette_image_device> m_cassette;
required_device<speaker_sound_device> m_speaker; optional_device<speaker_sound_device> m_speaker;
required_memory_region m_region_maincpu; required_memory_region m_region_maincpu;
optional_memory_region m_region_gfx1; optional_memory_region m_region_gfx1;
required_ioport m_io_row0; required_ioport m_io_row0;
@ -105,8 +85,25 @@ protected:
optional_ioport m_io_config; optional_ioport m_io_config;
required_device<screen_device> m_screen; required_device<screen_device> m_screen;
void zx_ula_r(int offs, memory_region *region, const UINT8 param); address_space *m_program;
emu_timer *m_tape_input, *m_ula_hsync;
bool m_vsync_active, m_hsync_active, m_nmi_on, m_nmi_generator_active;
UINT64 m_base_vsync_clock, m_vsync_start_time;
UINT32 m_ypos;
UINT8 m_prev_refresh;
UINT8 m_speaker_state;
bitmap_ind16 *m_bitmap_render, *m_bitmap_buffer;
UINT16 m_ula_char_buffer;
double m_cassette_cur_level;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
void drop_sync();
void recalc_hsync();
}; };
#endif /* ZX_H_ */ #endif /* ZX_H_ */

View File

@ -1,5 +1,5 @@
// license:GPL-2.0+ // license:GPL-2.0+
// copyright-holders:Juergen Buchmueller, Krzysztof Strzecha, Robbbert // copyright Olivier Galibert, Buchmueller, Krzysztof Strzecha, Robbbert
/*************************************************************************** /***************************************************************************
zx.c zx.c
@ -10,102 +10,112 @@
#include "includes/zx.h" #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) DRIVER_INIT_MEMBER(zx_state,zx)
{ {
address_space &space = m_maincpu->space(AS_PROGRAM); m_program = &m_maincpu->space(AS_PROGRAM);
m_tape_input = timer_alloc(TIMER_TAPE_INPUT);
space.install_read_bank(0x4000, 0x4000 + m_ram->size() - 1, "bank1"); if(m_ram->size() == 32768)
space.install_write_handler(0x4000, 0x4000 + m_ram->size() - 1, write8_delegate(FUNC(zx_state::zx_ram_w),this)); m_program->unmap_readwrite(0x8000, 0xbfff);
membank("bank1")->set_base(m_region_maincpu->base() + 0x4000); else if(m_ram->size() == 16384)
} m_program->unmap_readwrite(0x8000, 0xffff);
else if(m_ram->size() < 16384)
DIRECT_UPDATE_MEMBER(zx_state::zx_setdirect) m_program->unmap_readwrite(0x4000 + m_ram->size(), 0xffff);
{
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() void zx_state::machine_reset()
{ {
m_maincpu->space(AS_PROGRAM).set_direct_update_handler(direct_update_delegate(FUNC(zx_state::zx_setdirect), this)); m_prev_refresh = 0xff;
m_tape_bit = 0x80;
m_vsync_active = false;
m_base_vsync_clock = 0;
m_ypos = 0;
m_nmi_on = false;
m_nmi_generator_active = false;
m_cassette_cur_level = 0;
m_tape_input->adjust(attotime::from_hz(44100), 0, attotime::from_hz(44100));
} }
MACHINE_RESET_MEMBER(zx_state,pow3000) void zx_state::zx_tape_input()
{ {
m_maincpu->space(AS_PROGRAM).set_direct_update_handler(direct_update_delegate(FUNC(zx_state::pow3000_setdirect), this)); m_cassette_cur_level = m_cassette->input();
m_tape_bit = 0x80;
} }
MACHINE_RESET_MEMBER(zx_state,pc8300) void zx_state::drop_sync()
{ {
m_maincpu->space(AS_PROGRAM).set_direct_update_handler(direct_update_delegate(FUNC(zx_state::pc8300_setdirect), this)); if (m_vsync_active) {
m_tape_bit = 0x80; UINT64 time = m_maincpu->total_cycles();
} m_vsync_active = false;
m_cassette->output(-1.0);
TIMER_CALLBACK_MEMBER(zx_state::zx_tape_pulse) int xs = 2*((m_vsync_start_time - m_base_vsync_clock) % 207);
{ int ys = (m_vsync_start_time - m_base_vsync_clock) / 207;
m_tape_bit = 0x80; int xe = 2*((time - m_base_vsync_clock) % 207);
int ye = (time - m_base_vsync_clock) / 207;
if(xs >= 384) {
xs = 0;
ys++;
}
if(xe >= 384) {
xe = 0;
ye++;
}
if(ys < 311) {
if(ye > 310) {
ye = 311;
xe = 0;
}
if(ys == ye) {
UINT16 *dest = &m_bitmap_render->pix16(ys, xs);
for(int x = xs; x < xe; x++)
*dest++ = 1;
} else {
UINT16 *dest = &m_bitmap_render->pix16(ys, xs);
for(int x = xs; x < 384; x++)
*dest++ = 1;
for(int y = ys+1; y < ye; y++) {
dest = &m_bitmap_render->pix16(y, 0);
for(int x = 0; x<384; x++)
*dest++ = 1;
}
dest = &m_bitmap_render->pix16(ye, 0);
for(int x = 0; x < xe; x++)
*dest++ = 1;
}
}
// Short is hsync
if(time - m_vsync_start_time > 1000) {
// Ignore too short frame times, they're cassette output
if(time - m_base_vsync_clock > 52000) {
logerror("frame time %d\n", int(time - m_base_vsync_clock));
rectangle rect(0, 383, 0, 310);
copybitmap(*m_bitmap_buffer, *m_bitmap_render, 0, 0, 0, 0, rect);
m_bitmap_render->fill(0);
m_base_vsync_clock = time;
m_ypos = 0;
}
if(m_nmi_on)
m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
m_nmi_on = m_hsync_active = false;
recalc_hsync();
} else
m_ypos = ((time-m_base_vsync_clock)%207) < 192 ? 0 : -1;
}
} }
READ8_MEMBER( zx_state::zx80_io_r ) READ8_MEMBER( zx_state::zx80_io_r )
{ {
/* port FE = read keyboard, NTSC/PAL diode, and cass bit; turn off HSYNC-generator/cass-out /* 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 */ The upper 8 bits are used to select a keyboard scan line */
UINT8 data = 0xff; UINT8 data = 0xff;
UINT8 offs = offset & 0xff;
if (offs == 0xfe) if (!(offset & 0x01))
{ {
if ((offset & 0x0100) == 0) if ((offset & 0x0100) == 0)
data &= m_io_row0->read(); data &= m_io_row0->read();
@ -129,25 +139,13 @@ READ8_MEMBER( zx_state::zx80_io_r )
m_cassette->output(+1.0); m_cassette->output(+1.0);
if (m_ula_irq_active) if (!m_vsync_active && !m_nmi_generator_active) {
{ m_vsync_active = true;
zx_ula_bkgnd(0); m_vsync_start_time = m_maincpu->total_cycles();
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_cassette_cur_level <= 0)
// } data &= 0x7f;
if (m_ula_frame_vsync == 3)
{
m_ula_frame_vsync = 2;
}
} }
return data; return data;
@ -160,9 +158,8 @@ READ8_MEMBER( zx_state::zx81_io_r )
The upper 8 bits are used to select a keyboard scan line */ The upper 8 bits are used to select a keyboard scan line */
UINT8 data = 0xff; UINT8 data = 0xff;
UINT8 offs = offset & 0xff;
if (offs == 0xfe) if (!(offset & 0x01))
{ {
if ((offset & 0x0100) == 0) if ((offset & 0x0100) == 0)
data &= m_io_row0->read(); data &= m_io_row0->read();
@ -186,25 +183,13 @@ READ8_MEMBER( zx_state::zx81_io_r )
m_cassette->output(+1.0); m_cassette->output(+1.0);
if (m_ula_irq_active) if (!m_vsync_active && !m_nmi_generator_active) {
{ m_vsync_active = true;
zx_ula_bkgnd(0); m_vsync_start_time = m_maincpu->total_cycles();
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_cassette_cur_level <= 0)
} data &= 0x7f;
if (m_ula_frame_vsync == 3)
{
m_ula_frame_vsync = 2;
}
} }
return data; return data;
@ -248,26 +233,8 @@ READ8_MEMBER( zx_state::pc8300_io_r )
data &= m_io_row7->read(); data &= m_io_row7->read();
m_cassette->output(+1.0); m_cassette->output(+1.0);
if(m_cassette_cur_level <= 0)
if (m_ula_irq_active) data &= 0x7f;
{
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; return data;
@ -316,26 +283,8 @@ READ8_MEMBER( zx_state::pow3000_io_r )
data &= m_io_row7->read(); data &= m_io_row7->read();
m_cassette->output(+1.0); m_cassette->output(+1.0);
if(m_cassette_cur_level <= 0)
if (m_ula_irq_active) data &= 0x7f;
{
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; return data;
@ -360,31 +309,20 @@ WRITE8_MEMBER( zx_state::zx81_io_w )
FE = turn on NMI generator FE = turn on NMI generator
FF = write HSYNC and cass data */ FF = write HSYNC and cass data */
int height = m_screen->height(); if (!(offset & 0x01) && !m_nmi_generator_active) {
UINT8 offs = offset & 0xff; m_nmi_generator_active = true;
m_nmi_on = m_hsync_active;
m_maincpu->set_input_line(INPUT_LINE_NMI, m_nmi_on ? ASSERT_LINE : CLEAR_LINE);
recalc_hsync();
}
if (offs == 0xfd) if (!(offset & 0x02) && m_nmi_generator_active) {
{ m_nmi_generator_active = false;
m_ula_nmi->reset(); if(m_nmi_on) {
m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
m_nmi_on = false;
}
} }
else
if (offs == 0xfe)
{
m_ula_nmi->adjust(attotime::zero, 0, m_maincpu->cycles_to_attotime(207));
/* remove the IRQ */ drop_sync();
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());
}
}
} }

View File

@ -1,5 +1,5 @@
// license:GPL-2.0+ // license:GPL-2.0+
// copyright-holders:Juergen Buchmueller, Krzysztof Strzecha, Robbbert // copyright-holders: Olivier Galibert, Juergen Buchmueller, Krzysztof Strzecha, Robbbert
/*************************************************************************** /***************************************************************************
zx.c zx.c
@ -25,14 +25,11 @@ void zx_state::device_timer(emu_timer &timer, device_timer_id id, int param, voi
{ {
switch (id) switch (id)
{ {
case TIMER_TAPE_PULSE: case TIMER_TAPE_INPUT:
zx_tape_pulse(ptr, param); zx_tape_input();
break; break;
case TIMER_ULA_NMI: case TIMER_ULA_HSYNC:
zx_ula_nmi(ptr, param); zx_ula_hsync();
break;
case TIMER_ULA_IRQ:
zx_ula_irq(ptr, param);
break; break;
default: default:
assert_always(FALSE, "Unknown id in zx_state::device_timer"); assert_always(FALSE, "Unknown id in zx_state::device_timer");
@ -40,184 +37,113 @@ void zx_state::device_timer(emu_timer &timer, device_timer_id id, int param, voi
} }
/* void zx_state::zx_ula_hsync()
* Toggle the video output between black and white.
* This happens whenever the ULA scanline IRQs are enabled/disabled.
* Normally this is done during the synchronized zx_ula_r() function,
* which outputs 8 pixels per code, but if the video sync is off
* (during tape IO or sound output) zx_ula_bkgnd() is used to
* simulate the display of a ZX80/ZX81.
*/
void zx_state::zx_ula_bkgnd(UINT8 color)
{ {
int width = m_screen->width(); m_hsync_active = !m_hsync_active;
int height = m_screen->height(); if(m_hsync_active)
const rectangle &visarea = m_screen->visible_area(); m_ypos++;
if(m_nmi_generator_active) {
if (m_ula_frame_vsync == 0 && color != m_old_c) m_nmi_on = m_hsync_active;
{ m_maincpu->set_input_line(INPUT_LINE_NMI, m_nmi_on ? ASSERT_LINE : CLEAR_LINE);
int y, new_x, new_y;
rectangle r;
bitmap_ind16 &bitmap = m_bitmap;
new_y = machine().first_screen()->vpos();
new_x = machine().first_screen()->hpos();
/* logerror("zx_ula_bkgnd: %3d,%3d - %3d,%3d\n", state->m_old_x, state->m_old_y, new_x, new_y);*/
y = m_old_y;
for (;;)
{
if (y == new_y)
{
r.set(m_old_x, new_x, y, y);
bitmap.fill(color, r);
break;
} }
recalc_hsync();
}
WRITE16_MEMBER(zx_state::refresh_w)
{
if((data ^ m_prev_refresh) & 0x40)
m_maincpu->set_input_line(INPUT_LINE_IRQ0, data & 0x40 ? CLEAR_LINE : ASSERT_LINE);
m_prev_refresh = data;
if(m_ula_char_buffer != 0xffff) {
UINT64 time = m_maincpu->total_cycles();
int x = 2*((time-m_base_vsync_clock) % 207);
int y = (time-m_base_vsync_clock) / 207;
UINT8 pixels;
if(m_region_gfx1)
pixels = m_region_gfx1->base()[((m_ula_char_buffer & 0x3f) << 3) | (m_ypos & 7)];
else else
{ pixels = m_program->read_byte((data & 0xfe00) | ((m_ula_char_buffer & 0x3f) << 3) | (m_ypos & 7));
r.set(m_old_x, visarea.max_x, y, y); if(m_ula_char_buffer & 0x80)
bitmap.fill(color, r); pixels = ~pixels;
m_old_x = 0; if(x < 384-8 && y < 311) {
UINT16 *dest = &m_bitmap_render->pix16(y, x);
for(int i=0; i<8; i++)
*dest++ |= pixels & (0x80 >> i) ? 1 : 0;
} }
if (++y == height) m_ula_char_buffer = 0xffff;
y = 0;
}
m_old_x = (new_x + 1) % width;
m_old_y = new_y;
m_old_c = color;
} }
} }
/* void zx_state::recalc_hsync()
* PAL: 310 total lines,
* 0.. 55 vblank
* 56..247 192 visible lines
* 248..303 vblank
* 304... vsync
* NTSC: 262 total lines
* 0.. 31 vblank
* 32..223 192 visible lines
* 224..233 vblank
*/
TIMER_CALLBACK_MEMBER(zx_state::zx_ula_nmi)
{ {
/* UINT64 time = machine().time().as_ticks(m_maincpu->clock());
* An NMI is issued on the ZX81 every 64us for the blanked UINT32 step = (time - m_base_vsync_clock) % 207;
* scanlines at the top and bottom of the display. UINT32 delta;
*/ if (m_hsync_active)
int height = m_screen->height(); delta = 207 - step;
const rectangle& r1 = m_screen->visible_area(); else {
rectangle r; if(step < 192)
delta = 192 - step;
bitmap_ind16 &bitmap = m_bitmap;
r.set(r1.min_x, r1.max_x, m_ula_scanline_count, m_ula_scanline_count);
bitmap.fill(1, r);
// logerror("ULA %3d[%d] NMI, R:$%02X, $%04x\n", machine().first_screen()->vpos(), ula_scancode_count, (unsigned) m_maincpu->state_int(Z80_R), (unsigned) m_maincpu->state_int(Z80_PC));
m_maincpu->set_input_line(INPUT_LINE_NMI, PULSE_LINE);
if (++m_ula_scanline_count == height)
m_ula_scanline_count = 0;
}
TIMER_CALLBACK_MEMBER(zx_state::zx_ula_irq)
{
/*
* An IRQ is issued on the ZX80/81 whenever the R registers
* bit 6 goes low. In MESS this IRQ timed from the first read
* from the copy of the DFILE in the upper 32K in zx_ula_r().
*/
if (m_ula_irq_active)
{
// logerror("ULA %3d[%d] IRQ, R:$%02X, $%04x\n", machine().first_screen()->vpos(), ula_scancode_count, (unsigned) m_maincpu->state_int(Z80_R), (unsigned) m_maincpu->state_int(Z80_PC));
m_ula_irq_active = 0;
m_maincpu->set_input_line(0, HOLD_LINE);
}
}
void zx_state::zx_ula_r(int offs, memory_region *region, const UINT8 param)
{
int offs0 = offs & 0x7fff;
UINT8 *rom = m_region_maincpu->base();
UINT8 chr = rom[offs0];
if ((!m_ula_irq_active) && (chr == 0x76))
{
bitmap_ind16 &bitmap = m_bitmap;
UINT16 y, *scanline;
UINT16 ireg = m_maincpu->state_int(Z80_I) << 8;
UINT8 data, *chrgen, creg;
if (param)
creg = m_maincpu->state_int(Z80_B);
else else
creg = m_maincpu->state_int(Z80_C); delta = 399 - step;
chrgen = region->base();
if ((++m_ula_scanline_count == m_screen->height()) || (creg == 32))
{
m_ula_scanline_count = 0;
m_offs1 = offs0;
} }
m_ula_frame_vsync = 3; m_ula_hsync->adjust(m_maincpu->cycles_to_attotime(delta));
}
m_charline_ptr = 0; READ8_MEMBER(zx_state::ula_low_r)
{
UINT8 cdata = m_program->read_byte(offset);
if(space.debugger_access())
return cdata;
for (y = m_offs1+1; ((y < offs0) && (m_charline_ptr < ARRAY_LENGTH(m_charline))); y++) if(m_maincpu->state_int(Z80_HALT))
{ return cdata;
m_charline[m_charline_ptr] = rom[y];
m_charline_ptr++; if(m_nmi_on) {
UINT64 time = m_maincpu->total_cycles();
int pos = (time-m_base_vsync_clock) % 207;
if(pos >= 192)
m_maincpu->adjust_icount(pos - 207);
} }
for (y = m_charline_ptr; y < ARRAY_LENGTH(m_charline); y++) return cdata;
m_charline[y] = 0; }
timer_set(m_maincpu->cycles_to_attotime(((32 - m_charline_ptr) << 2)), TIMER_ULA_IRQ); READ8_MEMBER(zx_state::ula_high_r)
m_ula_irq_active++; {
UINT8 cdata = m_program->read_byte(offset);
scanline = &bitmap.pix16(m_ula_scanline_count); if(space.debugger_access())
y = 0; return cdata;
for (m_charline_ptr = 0; m_charline_ptr < ARRAY_LENGTH(m_charline); m_charline_ptr++) if(m_maincpu->state_int(Z80_HALT))
{ return cdata;
chr = m_charline[m_charline_ptr];
data = chrgen[ireg | ((chr & 0x3f) << 3) | ((8 - creg)&7) ];
if (chr & 0x80) data ^= 0xff;
scanline[y++] = (data >> 7) & 1; if(m_nmi_on) {
scanline[y++] = (data >> 6) & 1; UINT64 time = m_maincpu->total_cycles();
scanline[y++] = (data >> 5) & 1; int pos = (time-m_base_vsync_clock) % 207;
scanline[y++] = (data >> 4) & 1; if(pos >= 192)
scanline[y++] = (data >> 3) & 1; m_maincpu->adjust_icount(pos - 207);
scanline[y++] = (data >> 2) & 1;
scanline[y++] = (data >> 1) & 1;
scanline[y++] = (data >> 0) & 1;
m_charline[m_charline_ptr] = 0;
} }
if (creg == 1) m_offs1 = offs0; if(cdata & 0x40)
} return cdata;
m_ula_char_buffer = cdata;
return 0x00; // nop
} }
void zx_state::video_start() void zx_state::video_start()
{ {
m_ula_nmi = timer_alloc(TIMER_ULA_NMI); m_ula_hsync = timer_alloc(TIMER_ULA_HSYNC);
m_ula_irq_active = 0; m_ula_char_buffer = 0xffff;
m_screen->register_screen_bitmap(m_bitmap);
}
void zx_state::screen_eof_zx(screen_device &screen, bool state) m_bitmap_render = auto_bitmap_ind16_alloc(machine(), 384, 311);
{ m_bitmap_buffer = auto_bitmap_ind16_alloc(machine(), 384, 311);
// rising edge
if (state)
{
/* decrement video synchronization counter */
if (m_ula_frame_vsync)
--m_ula_frame_vsync;
}
} }
UINT32 zx_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) UINT32 zx_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{ {
copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect); copybitmap(bitmap, *m_bitmap_buffer, 0, 0, 0, 0, cliprect);
return 0; return 0;
} }