mirror of
https://github.com/holub/mame
synced 2025-04-19 23:12:11 +03:00
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:
parent
47f56372ee
commit
c4a787e527
@ -473,7 +473,11 @@ inline UINT8 z80_device::rop()
|
||||
{
|
||||
unsigned pc = PCD;
|
||||
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
|
||||
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));
|
||||
|
||||
/* Interrupt mode 2. Call [i:databyte] */
|
||||
@ -3429,6 +3436,9 @@ void z80_device::device_start()
|
||||
m_cc_xy = cc_xy;
|
||||
m_cc_xycb = cc_xycb;
|
||||
m_cc_ex = cc_ex;
|
||||
|
||||
m_irqack_cb.resolve_safe();
|
||||
m_refresh_cb.resolve_safe();
|
||||
}
|
||||
|
||||
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__),
|
||||
m_program_config("program", 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),
|
||||
m_program_config("program", 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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,12 @@
|
||||
|
||||
#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
|
||||
{
|
||||
NSC800_RSTA = INPUT_LINE_IRQ0 + 1,
|
||||
@ -38,6 +44,8 @@ public:
|
||||
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);
|
||||
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:
|
||||
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;
|
||||
direct_read_data *m_direct;
|
||||
direct_read_data *m_decrypted_opcodes_direct;
|
||||
devcb_write_line m_irqack_cb;
|
||||
devcb_write16 m_refresh_cb;
|
||||
|
||||
PAIR m_prvpc;
|
||||
PAIR m_pc;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// license:GPL-2.0+
|
||||
// copyright-holders:Juergen Buchmueller, Krzysztof Strzecha, Robbbert
|
||||
// copyright-holders: Olivier Galibert, Juergen Buchmueller, Krzysztof Strzecha, Robbbert
|
||||
/***************************************************************************
|
||||
zx.c
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
Fixes and additions by Krzysztof Strzecha:
|
||||
07.06.2004 Tape loading added. Some cleanups of debug code.
|
||||
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.
|
||||
Some cleanups. Two non-working TESTDRIVERS added.
|
||||
14.05.2004 Finally fixed and readded.
|
||||
@ -31,8 +31,8 @@
|
||||
- Modernised.
|
||||
|
||||
To do / problems:
|
||||
- Halt-on-nmi emulation needs a cycle-exact z80
|
||||
- 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.
|
||||
- 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)
|
||||
@ -47,8 +47,18 @@
|
||||
/* Memory Maps */
|
||||
|
||||
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(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
|
||||
|
||||
static ADDRESS_MAP_START( zx80_io_map, AS_IO, 8, zx_state )
|
||||
@ -291,38 +301,6 @@ static INPUT_PORTS_START( pow3000 )
|
||||
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 */
|
||||
|
||||
|
||||
@ -330,61 +308,34 @@ PALETTE_INIT_MEMBER(zx_state, zx)
|
||||
{
|
||||
palette.set_pen_color(0,rgb_t::white); /* white */
|
||||
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.set_pen_color(0,rgb_t(64, 244, 244)); /* cyan */
|
||||
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 )
|
||||
/* 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_IO_MAP(zx80_io_map)
|
||||
MCFG_SCREEN_ADD("screen", RASTER)
|
||||
MCFG_SCREEN_REFRESH_RATE(ZX81_PAL_FRAMES_PER_SECOND)
|
||||
MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(ZX81_VBLANK_DURATION))
|
||||
MCFG_CPU_DECRYPTED_OPCODES_MAP(ula_map)
|
||||
MCFG_Z80_SET_REFRESH_CALLBACK(WRITE16(zx_state, refresh_w))
|
||||
|
||||
MCFG_SCREEN_ADD("screen", RASTER)
|
||||
MCFG_SCREEN_REFRESH_RATE(XTAL_6_5MHz/2/64159.0) // 54223 for NTSC
|
||||
|
||||
/* video hardware */
|
||||
MCFG_SCREEN_UPDATE_DRIVER(zx_state, screen_update)
|
||||
MCFG_SCREEN_SIZE(ZX81_PIXELS_PER_SCANLINE, ZX81_PAL_SCANLINES)
|
||||
MCFG_SCREEN_VISIBLE_AREA(0, ZX81_PIXELS_PER_SCANLINE-1, 0, ZX81_PAL_SCANLINES-1)
|
||||
MCFG_SCREEN_VBLANK_DRIVER(zx_state, screen_eof_zx)
|
||||
MCFG_SCREEN_SIZE(384, 311)
|
||||
MCFG_SCREEN_VISIBLE_AREA(0, 383, 0, 310)
|
||||
MCFG_SCREEN_PALETTE("palette")
|
||||
|
||||
MCFG_GFXDECODE_ADD("gfxdecode", "palette", zx80)
|
||||
MCFG_PALETTE_ADD("palette", 4)
|
||||
MCFG_PALETTE_ADD("palette", 2)
|
||||
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_FORMATS(zx80_o_format)
|
||||
MCFG_CASSETTE_DEFAULT_STATE(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED)
|
||||
@ -392,60 +343,52 @@ static MACHINE_CONFIG_START( zx80, zx_state )
|
||||
/* internal ram */
|
||||
MCFG_RAM_ADD(RAM_TAG)
|
||||
MCFG_RAM_DEFAULT_SIZE("1K")
|
||||
MCFG_RAM_EXTRA_OPTIONS("16K")
|
||||
MCFG_RAM_EXTRA_OPTIONS("16K,32K,48K")
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
static MACHINE_CONFIG_DERIVED( zx81, zx80 )
|
||||
|
||||
MCFG_CPU_MODIFY("maincpu")
|
||||
MCFG_CPU_PROGRAM_MAP(zx81_map)
|
||||
MCFG_CPU_IO_MAP(zx81_io_map)
|
||||
|
||||
MCFG_GFXDECODE_MODIFY("gfxdecode", zx81)
|
||||
|
||||
MCFG_CASSETTE_MODIFY( "cassette" )
|
||||
MCFG_CASSETTE_FORMATS(zx81_p_format)
|
||||
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 )
|
||||
MCFG_PALETTE_MODIFY("palette")
|
||||
MCFG_PALETTE_INIT_OWNER(zx_state,ts1000)
|
||||
MCFG_PALETTE_INIT_OWNER(zx_state, ts1000)
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
static MACHINE_CONFIG_DERIVED( ts1500, ts1000 )
|
||||
|
||||
/* internal ram */
|
||||
MCFG_RAM_MODIFY(RAM_TAG)
|
||||
MCFG_RAM_DEFAULT_SIZE("16K")
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
static MACHINE_CONFIG_DERIVED( pc8300, zx81 )
|
||||
|
||||
static MACHINE_CONFIG_DERIVED( pc8300, zx81_spk )
|
||||
MCFG_CPU_MODIFY("maincpu")
|
||||
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 */
|
||||
MCFG_RAM_MODIFY(RAM_TAG)
|
||||
MCFG_RAM_DEFAULT_SIZE("16K")
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
static MACHINE_CONFIG_DERIVED( pow3000, zx81 )
|
||||
|
||||
static MACHINE_CONFIG_DERIVED( pow3000, zx81_spk )
|
||||
MCFG_CPU_MODIFY("maincpu")
|
||||
MCFG_CPU_IO_MAP(pow3000_io_map)
|
||||
|
||||
MCFG_MACHINE_RESET_OVERRIDE(zx_state,pow3000)
|
||||
|
||||
MCFG_GFXDECODE_MODIFY("gfxdecode", pc8300)
|
||||
|
||||
/* internal ram */
|
||||
MCFG_RAM_MODIFY(RAM_TAG)
|
||||
MCFG_RAM_DEFAULT_SIZE("2K")
|
||||
@ -456,7 +399,7 @@ MACHINE_CONFIG_END
|
||||
/* ROMs */
|
||||
|
||||
ROM_START(zx80)
|
||||
ROM_REGION( 0x10000, "maincpu",0 )
|
||||
ROM_REGION( 0x1000, "maincpu",0 )
|
||||
ROM_SYSTEM_BIOS(0, "default", "BASIC")
|
||||
ROMX_LOAD( "zx80.rom", 0x0000, 0x1000, CRC(4c7fc597) SHA1(b6769a3197c77009e0933e038c15b43cf4c98c7a), ROM_BIOS(1) )
|
||||
ROM_SYSTEM_BIOS(1, "aszmic", "ASZMIC")
|
||||
@ -464,7 +407,7 @@ ROM_START(zx80)
|
||||
ROM_END
|
||||
|
||||
ROM_START(zx81)
|
||||
ROM_REGION( 0x10000, "maincpu",0 )
|
||||
ROM_REGION( 0x2000, "maincpu",0 )
|
||||
ROM_SYSTEM_BIOS(0, "3rd", "3rd rev.")
|
||||
ROMX_LOAD( "zx81b.rom", 0x0000, 0x2000, CRC(522c37b8) SHA1(c6d8e06cb936989f6e1cc7a56d1f092da854a515), ROM_BIOS(1) )
|
||||
ROM_SYSTEM_BIOS(1, "1st", "1st rev.")
|
||||
@ -478,22 +421,22 @@ ROM_START(zx81)
|
||||
ROM_END
|
||||
|
||||
ROM_START(ts1000)
|
||||
ROM_REGION( 0x10000, "maincpu",0 )
|
||||
ROM_REGION( 0x2000, "maincpu",0 )
|
||||
ROM_LOAD( "zx81a.rom", 0x0000, 0x2000, CRC(4b1dd6eb) SHA1(7b143ee964e9ada89d1f9e88f0bd48d919184cfc) )
|
||||
ROM_END
|
||||
|
||||
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_END
|
||||
|
||||
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_END
|
||||
|
||||
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_REGION( 0x200, "gfx1", 0 )
|
||||
@ -501,7 +444,7 @@ ROM_START(pc8300)
|
||||
ROM_END
|
||||
|
||||
ROM_START(pow3000)
|
||||
ROM_REGION( 0x10000, "maincpu",0 )
|
||||
ROM_REGION( 0x2000, "maincpu",0 )
|
||||
ROM_LOAD( "pow3000.rom", 0x0000, 0x2000, CRC(8a49b2c3) SHA1(9b22daf2f3a991aa6a358ef95b091654c3ca1bdf) )
|
||||
|
||||
ROM_REGION( 0x200, "gfx1", 0 )
|
||||
@ -509,7 +452,7 @@ ROM_START(pow3000)
|
||||
ROM_END
|
||||
|
||||
ROM_START(lambda)
|
||||
ROM_REGION( 0x10000, "maincpu",0 )
|
||||
ROM_REGION( 0x2000, "maincpu",0 )
|
||||
ROM_LOAD( "lambda.rom", 0x0000, 0x2000, CRC(8a49b2c3) SHA1(9b22daf2f3a991aa6a358ef95b091654c3ca1bdf) )
|
||||
|
||||
ROM_REGION( 0x200, "gfx1", 0 )
|
||||
@ -517,28 +460,26 @@ ROM_START(lambda)
|
||||
ROM_END
|
||||
|
||||
ROM_START( tk85 )
|
||||
ROM_REGION( 0x10000, "maincpu", 0 )
|
||||
ROM_REGION( 0x2800, "maincpu", 0 )
|
||||
ROM_LOAD( "tk85.rom", 0x0000, 0x2800, CRC(8972d756) SHA1(7b961a1733fc047eb682150a32e17bca10a018d2) )
|
||||
ROM_END
|
||||
|
||||
/* 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. */
|
||||
ROM_START( zx97 )
|
||||
ROM_REGION( 0x10000, "maincpu", 0 )
|
||||
ROM_LOAD( "zx97.rom", 0x0000, 0x2000, CRC(5cf49744) SHA1(b2a486efdc7b2bc3dc8e5a441ea5532bfa3207bd) )
|
||||
ROM_IGNORE( 0x6000 ) /* Unemulated bankswitched part */
|
||||
ROM_REGION( 0x8000, "maincpu", 0 )
|
||||
ROM_LOAD( "zx97.rom", 0x0000, 0x8000, CRC(5cf49744) SHA1(b2a486efdc7b2bc3dc8e5a441ea5532bfa3207bd) )
|
||||
ROM_END
|
||||
|
||||
/* 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", 0 )
|
||||
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", 0 )
|
||||
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", 0 )
|
||||
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( 1980, zx80, 0, 0, zx80, zx80, zx_state, zx, "Sinclair Research Ltd", "ZX-80", MACHINE_NO_SOUND )
|
||||
COMP( 1981, zx81, 0, 0, zx81, zx81, zx_state, zx, "Sinclair Research Ltd", "ZX-81", MACHINE_NO_SOUND )
|
||||
COMP( 1982, ts1000, zx81, 0, ts1000, zx81, zx_state, zx, "Timex Sinclair", "Timex Sinclair 1000", MACHINE_NO_SOUND )
|
||||
COMP( 1983, ts1500, zx81, 0, ts1500, zx81, zx_state, zx, "Timex Sinclair", "Timex Sinclair 1500", MACHINE_NO_SOUND )
|
||||
COMP( 1983, tk85, zx81, 0, ts1000, zx81, zx_state, zx, "Microdigital", "TK85", MACHINE_NO_SOUND )
|
||||
COMP( 1983, ringo470, zx81, 0, ts1000, zx81, zx_state, zx, "Ritas do Brasil Ltda", "Ringo 470", MACHINE_NO_SOUND )
|
||||
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( 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 )
|
||||
|
@ -21,13 +21,6 @@
|
||||
class zx_state : public driver_device
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
TIMER_TAPE_PULSE,
|
||||
TIMER_ULA_NMI,
|
||||
TIMER_ULA_IRQ
|
||||
};
|
||||
|
||||
zx_state(const machine_config &mconfig, device_type type, const char *tag)
|
||||
: driver_device(mconfig, type, tag),
|
||||
m_maincpu(*this, "maincpu"),
|
||||
@ -49,49 +42,36 @@ public:
|
||||
|
||||
UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
|
||||
|
||||
bitmap_ind16 m_bitmap;
|
||||
|
||||
DECLARE_READ8_MEMBER(zx_ram_r);
|
||||
DECLARE_READ8_MEMBER(ula_high_r);
|
||||
DECLARE_READ8_MEMBER(ula_low_r);
|
||||
DECLARE_WRITE16_MEMBER(refresh_w);
|
||||
DECLARE_READ8_MEMBER(zx80_io_r);
|
||||
DECLARE_READ8_MEMBER(zx81_io_r);
|
||||
DECLARE_READ8_MEMBER(pc8300_io_r);
|
||||
DECLARE_READ8_MEMBER(pow3000_io_r);
|
||||
DECLARE_WRITE8_MEMBER(zx80_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);
|
||||
virtual void machine_reset();
|
||||
virtual void video_start();
|
||||
DECLARE_PALETTE_INIT(zx);
|
||||
DECLARE_PALETTE_INIT(ts1000);
|
||||
DECLARE_MACHINE_RESET(pc8300);
|
||||
DECLARE_MACHINE_RESET(pow3000);
|
||||
void screen_eof_zx(screen_device &screen, bool state);
|
||||
TIMER_CALLBACK_MEMBER(zx_tape_pulse);
|
||||
TIMER_CALLBACK_MEMBER(zx_ula_nmi);
|
||||
TIMER_CALLBACK_MEMBER(zx_ula_irq);
|
||||
void zx_tape_input();
|
||||
void zx_ula_hsync();
|
||||
|
||||
UINT32 get_ram_size();
|
||||
|
||||
protected:
|
||||
enum
|
||||
{
|
||||
TIMER_TAPE_INPUT,
|
||||
TIMER_ULA_HSYNC
|
||||
};
|
||||
|
||||
required_device<cpu_device> m_maincpu;
|
||||
required_device<ram_device> m_ram;
|
||||
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;
|
||||
optional_memory_region m_region_gfx1;
|
||||
required_ioport m_io_row0;
|
||||
@ -105,8 +85,25 @@ protected:
|
||||
optional_ioport m_io_config;
|
||||
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);
|
||||
|
||||
void drop_sync();
|
||||
void recalc_hsync();
|
||||
};
|
||||
|
||||
#endif /* ZX_H_ */
|
||||
|
@ -1,5 +1,5 @@
|
||||
// license:GPL-2.0+
|
||||
// copyright-holders:Juergen Buchmueller, Krzysztof Strzecha, Robbbert
|
||||
// copyright Olivier Galibert, Buchmueller, Krzysztof Strzecha, Robbbert
|
||||
/***************************************************************************
|
||||
zx.c
|
||||
|
||||
@ -10,102 +10,112 @@
|
||||
|
||||
#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);
|
||||
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");
|
||||
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;
|
||||
if(m_ram->size() == 32768)
|
||||
m_program->unmap_readwrite(0x8000, 0xbfff);
|
||||
else if(m_ram->size() == 16384)
|
||||
m_program->unmap_readwrite(0x8000, 0xffff);
|
||||
else if(m_ram->size() < 16384)
|
||||
m_program->unmap_readwrite(0x4000 + m_ram->size(), 0xffff);
|
||||
}
|
||||
|
||||
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;
|
||||
m_prev_refresh = 0xff;
|
||||
|
||||
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_tape_bit = 0x80;
|
||||
m_cassette_cur_level = m_cassette->input();
|
||||
}
|
||||
|
||||
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));
|
||||
m_tape_bit = 0x80;
|
||||
}
|
||||
if (m_vsync_active) {
|
||||
UINT64 time = m_maincpu->total_cycles();
|
||||
m_vsync_active = false;
|
||||
m_cassette->output(-1.0);
|
||||
|
||||
TIMER_CALLBACK_MEMBER(zx_state::zx_tape_pulse)
|
||||
{
|
||||
m_tape_bit = 0x80;
|
||||
int xs = 2*((m_vsync_start_time - m_base_vsync_clock) % 207);
|
||||
int ys = (m_vsync_start_time - m_base_vsync_clock) / 207;
|
||||
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 )
|
||||
{
|
||||
/* 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 */
|
||||
/* 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 & 0x01))
|
||||
{
|
||||
if ((offset & 0x0100) == 0)
|
||||
data &= m_io_row0->read();
|
||||
@ -129,25 +139,13 @@ READ8_MEMBER( zx_state::zx80_io_r )
|
||||
|
||||
m_cassette->output(+1.0);
|
||||
|
||||
if (m_ula_irq_active)
|
||||
{
|
||||
zx_ula_bkgnd(0);
|
||||
m_ula_irq_active = 0;
|
||||
if (!m_vsync_active && !m_nmi_generator_active) {
|
||||
m_vsync_active = true;
|
||||
m_vsync_start_time = m_maincpu->total_cycles();
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
if(m_cassette_cur_level <= 0)
|
||||
data &= 0x7f;
|
||||
}
|
||||
|
||||
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 */
|
||||
|
||||
UINT8 data = 0xff;
|
||||
UINT8 offs = offset & 0xff;
|
||||
|
||||
if (offs == 0xfe)
|
||||
if (!(offset & 0x01))
|
||||
{
|
||||
if ((offset & 0x0100) == 0)
|
||||
data &= m_io_row0->read();
|
||||
@ -186,25 +183,13 @@ READ8_MEMBER( zx_state::zx81_io_r )
|
||||
|
||||
m_cassette->output(+1.0);
|
||||
|
||||
if (m_ula_irq_active)
|
||||
{
|
||||
zx_ula_bkgnd(0);
|
||||
m_ula_irq_active = 0;
|
||||
if (!m_vsync_active && !m_nmi_generator_active) {
|
||||
m_vsync_active = true;
|
||||
m_vsync_start_time = m_maincpu->total_cycles();
|
||||
}
|
||||
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;
|
||||
}
|
||||
if(m_cassette_cur_level <= 0)
|
||||
data &= 0x7f;
|
||||
}
|
||||
|
||||
return data;
|
||||
@ -248,26 +233,8 @@ READ8_MEMBER( zx_state::pc8300_io_r )
|
||||
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;
|
||||
}
|
||||
if(m_cassette_cur_level <= 0)
|
||||
data &= 0x7f;
|
||||
}
|
||||
|
||||
return data;
|
||||
@ -316,26 +283,8 @@ READ8_MEMBER( zx_state::pow3000_io_r )
|
||||
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;
|
||||
}
|
||||
if(m_cassette_cur_level <= 0)
|
||||
data &= 0x7f;
|
||||
}
|
||||
|
||||
return data;
|
||||
@ -360,31 +309,20 @@ WRITE8_MEMBER( zx_state::zx81_io_w )
|
||||
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();
|
||||
if (!(offset & 0x01) && !m_nmi_generator_active) {
|
||||
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();
|
||||
}
|
||||
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());
|
||||
if (!(offset & 0x02) && m_nmi_generator_active) {
|
||||
m_nmi_generator_active = false;
|
||||
if(m_nmi_on) {
|
||||
m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
|
||||
m_nmi_on = false;
|
||||
}
|
||||
}
|
||||
|
||||
drop_sync();
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// license:GPL-2.0+
|
||||
// copyright-holders:Juergen Buchmueller, Krzysztof Strzecha, Robbbert
|
||||
// copyright-holders: Olivier Galibert, Juergen Buchmueller, Krzysztof Strzecha, Robbbert
|
||||
/***************************************************************************
|
||||
zx.c
|
||||
|
||||
@ -25,14 +25,11 @@ void zx_state::device_timer(emu_timer &timer, device_timer_id id, int param, voi
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case TIMER_TAPE_PULSE:
|
||||
zx_tape_pulse(ptr, param);
|
||||
case TIMER_TAPE_INPUT:
|
||||
zx_tape_input();
|
||||
break;
|
||||
case TIMER_ULA_NMI:
|
||||
zx_ula_nmi(ptr, param);
|
||||
break;
|
||||
case TIMER_ULA_IRQ:
|
||||
zx_ula_irq(ptr, param);
|
||||
case TIMER_ULA_HSYNC:
|
||||
zx_ula_hsync();
|
||||
break;
|
||||
default:
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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)
|
||||
void zx_state::zx_ula_hsync()
|
||||
{
|
||||
int width = m_screen->width();
|
||||
int height = m_screen->height();
|
||||
const rectangle &visarea = m_screen->visible_area();
|
||||
|
||||
if (m_ula_frame_vsync == 0 && color != m_old_c)
|
||||
{
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.set(m_old_x, visarea.max_x, y, y);
|
||||
bitmap.fill(color, r);
|
||||
m_old_x = 0;
|
||||
}
|
||||
if (++y == height)
|
||||
y = 0;
|
||||
}
|
||||
m_old_x = (new_x + 1) % width;
|
||||
m_old_y = new_y;
|
||||
m_old_c = color;
|
||||
m_hsync_active = !m_hsync_active;
|
||||
if(m_hsync_active)
|
||||
m_ypos++;
|
||||
if(m_nmi_generator_active) {
|
||||
m_nmi_on = m_hsync_active;
|
||||
m_maincpu->set_input_line(INPUT_LINE_NMI, m_nmi_on ? ASSERT_LINE : CLEAR_LINE);
|
||||
}
|
||||
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)
|
||||
WRITE16_MEMBER(zx_state::refresh_w)
|
||||
{
|
||||
/*
|
||||
* An NMI is issued on the ZX81 every 64us for the blanked
|
||||
* scanlines at the top and bottom of the display.
|
||||
*/
|
||||
int height = m_screen->height();
|
||||
const rectangle& r1 = m_screen->visible_area();
|
||||
rectangle r;
|
||||
|
||||
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);
|
||||
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
|
||||
creg = m_maincpu->state_int(Z80_C);
|
||||
|
||||
chrgen = region->base();
|
||||
|
||||
if ((++m_ula_scanline_count == m_screen->height()) || (creg == 32))
|
||||
{
|
||||
m_ula_scanline_count = 0;
|
||||
m_offs1 = offs0;
|
||||
pixels = m_program->read_byte((data & 0xfe00) | ((m_ula_char_buffer & 0x3f) << 3) | (m_ypos & 7));
|
||||
if(m_ula_char_buffer & 0x80)
|
||||
pixels = ~pixels;
|
||||
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;
|
||||
}
|
||||
|
||||
m_ula_frame_vsync = 3;
|
||||
|
||||
m_charline_ptr = 0;
|
||||
|
||||
for (y = m_offs1+1; ((y < offs0) && (m_charline_ptr < ARRAY_LENGTH(m_charline))); y++)
|
||||
{
|
||||
m_charline[m_charline_ptr] = rom[y];
|
||||
m_charline_ptr++;
|
||||
}
|
||||
for (y = m_charline_ptr; y < ARRAY_LENGTH(m_charline); y++)
|
||||
m_charline[y] = 0;
|
||||
|
||||
timer_set(m_maincpu->cycles_to_attotime(((32 - m_charline_ptr) << 2)), TIMER_ULA_IRQ);
|
||||
m_ula_irq_active++;
|
||||
|
||||
scanline = &bitmap.pix16(m_ula_scanline_count);
|
||||
y = 0;
|
||||
|
||||
for (m_charline_ptr = 0; m_charline_ptr < ARRAY_LENGTH(m_charline); m_charline_ptr++)
|
||||
{
|
||||
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;
|
||||
scanline[y++] = (data >> 6) & 1;
|
||||
scanline[y++] = (data >> 5) & 1;
|
||||
scanline[y++] = (data >> 4) & 1;
|
||||
scanline[y++] = (data >> 3) & 1;
|
||||
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;
|
||||
m_ula_char_buffer = 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
void zx_state::recalc_hsync()
|
||||
{
|
||||
UINT64 time = machine().time().as_ticks(m_maincpu->clock());
|
||||
UINT32 step = (time - m_base_vsync_clock) % 207;
|
||||
UINT32 delta;
|
||||
if (m_hsync_active)
|
||||
delta = 207 - step;
|
||||
else {
|
||||
if(step < 192)
|
||||
delta = 192 - step;
|
||||
else
|
||||
delta = 399 - step;
|
||||
}
|
||||
|
||||
m_ula_hsync->adjust(m_maincpu->cycles_to_attotime(delta));
|
||||
}
|
||||
|
||||
READ8_MEMBER(zx_state::ula_low_r)
|
||||
{
|
||||
UINT8 cdata = m_program->read_byte(offset);
|
||||
if(space.debugger_access())
|
||||
return cdata;
|
||||
|
||||
if(m_maincpu->state_int(Z80_HALT))
|
||||
return cdata;
|
||||
|
||||
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);
|
||||
}
|
||||
return cdata;
|
||||
}
|
||||
|
||||
READ8_MEMBER(zx_state::ula_high_r)
|
||||
{
|
||||
UINT8 cdata = m_program->read_byte(offset);
|
||||
|
||||
if(space.debugger_access())
|
||||
return cdata;
|
||||
|
||||
if(m_maincpu->state_int(Z80_HALT))
|
||||
return cdata;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if(cdata & 0x40)
|
||||
return cdata;
|
||||
|
||||
m_ula_char_buffer = cdata;
|
||||
return 0x00; // nop
|
||||
}
|
||||
|
||||
void zx_state::video_start()
|
||||
{
|
||||
m_ula_nmi = timer_alloc(TIMER_ULA_NMI);
|
||||
m_ula_irq_active = 0;
|
||||
m_screen->register_screen_bitmap(m_bitmap);
|
||||
}
|
||||
m_ula_hsync = timer_alloc(TIMER_ULA_HSYNC);
|
||||
m_ula_char_buffer = 0xffff;
|
||||
|
||||
void zx_state::screen_eof_zx(screen_device &screen, bool state)
|
||||
{
|
||||
// rising edge
|
||||
if (state)
|
||||
{
|
||||
/* decrement video synchronization counter */
|
||||
if (m_ula_frame_vsync)
|
||||
--m_ula_frame_vsync;
|
||||
}
|
||||
m_bitmap_render = auto_bitmap_ind16_alloc(machine(), 384, 311);
|
||||
m_bitmap_buffer = auto_bitmap_ind16_alloc(machine(), 384, 311);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user