kl5c80a12, kl5c80a16: Added emulation of KP63(A) Timer/Counter unit. This improves timings in animalc, haekaka, pyenaget and tdoboon.

* animalc: Pile kludge upon kludge for poorly understood video timing register
* gocowboy, itazuram: Increase frequency of one timer interrupt (and hopper timing in gocowboy)
* kc82, kp69: Modernize state_add syntax
This commit is contained in:
AJR 2020-09-14 23:32:58 -04:00
parent 949da6a0b0
commit 7e121d68bb
6 changed files with 633 additions and 52 deletions

View File

@ -49,6 +49,14 @@ if (CPU_INCLUDE_DRC) then
MAME_DIR .. "src/devices/cpu/drcbex86.h", MAME_DIR .. "src/devices/cpu/drcbex86.h",
} }
end end
if _OPTIONS["targetos"]=="macosx" and _OPTIONS["gcc"]~=nil then
if string.find(_OPTIONS["gcc"], "clang") and (str_to_version(_OPTIONS["gcc_version"]) < 80000) then
defines {
"TARGET_OS_OSX=1",
}
end
end
end end
-------------------------------------------------- --------------------------------------------------
@ -2624,6 +2632,8 @@ if (CPUS["Z80"]~=null) then
MAME_DIR .. "src/devices/cpu/z80/kl5c80a12.h", MAME_DIR .. "src/devices/cpu/z80/kl5c80a12.h",
MAME_DIR .. "src/devices/cpu/z80/kl5c80a16.cpp", MAME_DIR .. "src/devices/cpu/z80/kl5c80a16.cpp",
MAME_DIR .. "src/devices/cpu/z80/kl5c80a16.h", MAME_DIR .. "src/devices/cpu/z80/kl5c80a16.h",
MAME_DIR .. "src/devices/cpu/z80/kp63.cpp",
MAME_DIR .. "src/devices/cpu/z80/kp63.h",
MAME_DIR .. "src/devices/cpu/z80/kp69.cpp", MAME_DIR .. "src/devices/cpu/z80/kp69.cpp",
MAME_DIR .. "src/devices/cpu/z80/kp69.h", MAME_DIR .. "src/devices/cpu/z80/kp69.h",
MAME_DIR .. "src/devices/cpu/z80/ky80.cpp", MAME_DIR .. "src/devices/cpu/z80/ky80.cpp",

View File

@ -64,13 +64,11 @@ void kc82_device::device_start()
for (int n = 1; n <= 4; n++) for (int n = 1; n <= 4; n++)
{ {
state_add<u8>(KC82_B1 + n - 1, string_format("B%d", n).c_str(), state_add(KC82_B1 + n - 1, string_format("B%d", n).c_str(), m_mmu_b[n],
[this, n]() { return m_mmu_b[n]; },
[this, n](u8 data) { m_mmu_b[n] = data; mmu_remap_pages(); } [this, n](u8 data) { m_mmu_b[n] = data; mmu_remap_pages(); }
).mask(0x3f); ).mask(0x3f);
if (n != 4) if (n != 4)
state_add<u16>(KC82_A1 + n - 1, string_format("A%d", n).c_str(), state_add(KC82_A1 + n - 1, string_format("A%d", n).c_str(), m_mmu_a[n],
[this, n]() { return m_mmu_a[n]; },
[this, n](u16 data) { m_mmu_a[n] = data; mmu_remap_pages(); } [this, n](u16 data) { m_mmu_a[n] = data; mmu_remap_pages(); }
).mask(0x3ff); ).mask(0x3ff);
} }

View File

@ -0,0 +1,477 @@
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************
Kawasaki Steel (Kawatetsu) KP63(A) Timer/Counter
These macro cells provide 4 independent 16-bit down counters (reduced
to 3 in some versions) driven by an 8-bit prescaler attached to the
system clock. This prescaler is not fully emulated here, since its
operations are mostly transparent, though a divide-by-4 clock output
(SYNC) may be selected to appear on a port pin.
Each counter has a single and optional external input (GATEn), which
on the KP63 can only be used to gate a divide-by-4 count but can also
be configured as an input clock on the KP63A.
Two outputs are generated for each counter. The pulse or toggle output
(OUTPn) has configurable polarity and can be used for 8-bit PWM. The
strobe output (OUTSn) goes active high for 4 clock cycles when the
counter underflows and is connected to the interrupt controller.
Writing the initial count register (CR) and reading the current count
are two-step processes, effective at the second write or first read.
These must not be overlapped with each other since they share a
temporary register.
***************************************************************************/
#include "emu.h"
#include "kp63.h"
#define VERBOSE 1
#include "logmacro.h"
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
// device type definitions
DEFINE_DEVICE_TYPE(KP63_3CHANNEL, kp63_3channel_device, "kp63_3channel", "Kawasaki Steel KP63 Timer/Counter (3 channels)")
DEFINE_DEVICE_TYPE(KP63A, kp63a_device, "kp63a", "Kawasaki Steel KP63A Timer/Counter")
const char *const kp63_device::s_count_modes[4] =
{
"one-shot",
"continuous count",
"WDT",
"PWM"
};
//**************************************************************************
// KP63 DEVICE
//**************************************************************************
//-------------------------------------------------
// kp63_device - constructor
//-------------------------------------------------
kp63_device::kp63_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u8 num_counters, u8 mode_mask)
: device_t(mconfig, type, tag, owner, clock)
, m_out_pulse_callback(*this)
, m_out_strobe_callback(*this)
, c_num_counters(num_counters)
, c_mode_mask(mode_mask)
, m_timer{0}
, m_strobe_timer{0}
, m_pwm_timer{0}
, m_cr{0}
, m_last_count{0}
, m_count_tmp{0}
, m_status{0}
, m_rw_seq(0)
, m_timer_started(0)
, m_gate_input(0xf)
{
}
//-------------------------------------------------
// kp63_3channel_device - constructor
//-------------------------------------------------
kp63_3channel_device::kp63_3channel_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: kp63_device(mconfig, KP63_3CHANNEL, tag, owner, clock, 3, 0x1f)
{
}
//-------------------------------------------------
// kp63a_device - constructor
//-------------------------------------------------
kp63a_device::kp63a_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: kp63_device(mconfig, KP63A, tag, owner, clock, 4, 0x3f)
{
}
//-------------------------------------------------
// device_resolve_objects - resolve objects that
// may be needed for other devices to set
// initial conditions at start time
//-------------------------------------------------
void kp63_device::device_resolve_objects()
{
// Resolve output callbacks
m_out_pulse_callback.resolve_all_safe();
m_out_strobe_callback.resolve_all_safe();
}
//-------------------------------------------------
// timer_expired - handle timed count underflow
//-------------------------------------------------
template <int N>
TIMER_CALLBACK_MEMBER(kp63_device::timer_expired)
{
timer_pulse(N);
}
//-------------------------------------------------
// strobe_off - handle end of strobe output
//-------------------------------------------------
template <int N>
TIMER_CALLBACK_MEMBER(kp63_device::strobe_off)
{
m_out_strobe_callback[N](0);
}
//-------------------------------------------------
// pwm_off - handle PWM phase change
//-------------------------------------------------
template <int N>
TIMER_CALLBACK_MEMBER(kp63_device::pwm_off)
{
m_status[N] &= 0x7f;
m_out_pulse_callback[N](BIT(m_status[N], 4) ? 1 : 0);
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void kp63_device::device_start()
{
// Setup timers
m_timer[0] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(kp63_device::timer_expired<0>), this));
m_strobe_timer[0] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(kp63_device::strobe_off<0>), this));
m_pwm_timer[0] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(kp63_device::pwm_off<0>), this));
m_timer[1] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(kp63_device::timer_expired<1>), this));
m_strobe_timer[1] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(kp63_device::strobe_off<1>), this));
m_pwm_timer[1] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(kp63_device::pwm_off<1>), this));
m_timer[2] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(kp63_device::timer_expired<2>), this));
m_strobe_timer[2] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(kp63_device::strobe_off<2>), this));
m_pwm_timer[2] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(kp63_device::pwm_off<2>), this));
if (c_num_counters > 3)
{
m_timer[3] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(kp63_device::timer_expired<3>), this));
m_strobe_timer[3] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(kp63_device::strobe_off<3>), this));
m_pwm_timer[3] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(kp63_device::pwm_off<3>), this));
}
// Save state
save_item(NAME(m_cr));
save_item(NAME(m_last_count));
save_item(NAME(m_count_tmp));
save_item(NAME(m_status));
save_item(NAME(m_rw_seq));
save_item(NAME(m_timer_started));
save_item(NAME(m_gate_input));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void kp63_device::device_reset()
{
for (unsigned n = 0; n < c_num_counters; n++)
{
// Turn off timers
m_timer[n]->adjust(attotime::never);
m_strobe_timer[n]->adjust(attotime::never);
m_pwm_timer[n]->adjust(attotime::never);
// Reset status and count
m_status[n] = 0;
m_cr[n] = 0xffff;
m_last_count[n] = 0xffff;
// Clear outputs
m_out_pulse_callback[n](0);
m_out_strobe_callback[n](0);
}
// Clear read/write sequence for all counters
m_rw_seq = 0;
m_timer_started = 0;
}
//-------------------------------------------------
// timer_pulse - change outputs and stop or
// reload timer as count underflows
//-------------------------------------------------
void kp63_device::timer_pulse(unsigned n)
{
// Toggle pulse output
m_status[n] ^= 0x80;
m_out_pulse_callback[n](BIT(m_status[n], 7) != BIT(m_status[n], 4) ? 1 : 0);
// Begin strobe output
m_out_strobe_callback[n](1);
m_strobe_timer[n]->adjust(clocks_to_attotime(4));
// Reload timer in continuous count and PWM modes
if (BIT(m_status[n], 2))
timer_reload(n);
else
{
// Stop count at FFFF in one-shot and WDT modes
m_last_count[n] = 0xffff;
m_timer_started &= ~(1 << n);
}
}
//-------------------------------------------------
// timer_reload - reload timer from CR
//-------------------------------------------------
void kp63_device::timer_reload(unsigned n)
{
m_timer_started |= 1 << n;
if (BIT(m_status[n], 5) || ((m_status[n] & 0x03) == 0x03 && !BIT(m_gate_input, n)))
m_last_count[n] = m_cr[n];
else
{
unsigned prescale = BIT(m_status[n], 1) ? 4 : BIT(m_status[n], 0) ? 16 : 256;
if ((m_status[n] & 0x0c) == 0x0c)
{
// PWM
m_timer[n]->adjust(clocks_to_attotime(prescale * ((m_cr[n] & 0x00ff) + 1)));
m_pwm_timer[n]->adjust(clocks_to_attotime(prescale * ((m_cr[n] >> 8) + 1)));
}
else
m_timer[n]->adjust(clocks_to_attotime(prescale * (u32(m_cr[n]) + 1)));
}
}
//-------------------------------------------------
// timer_resume_count - start counting again
//-------------------------------------------------
void kp63_device::timer_resume_count(unsigned n)
{
if (!BIT(m_status[n], 5) || ((m_status[n] & 0x03) != 0x03 || BIT(m_gate_input, n)))
{
unsigned prescale = BIT(m_status[n], 1) ? 4 : BIT(m_status[n], 0) ? 16 : 256;
if ((m_status[n] & 0x0c) == 0x0c)
{
// PWM
m_timer[n]->adjust(clocks_to_attotime(prescale * ((m_last_count[n] & 0x00ff) + 1)));
m_pwm_timer[n]->adjust(clocks_to_attotime(prescale * ((m_last_count[n] >> 8) + 1)));
}
else
m_timer[n]->adjust(clocks_to_attotime(prescale * (u32(m_last_count[n]) + 1)));
}
}
//-------------------------------------------------
// timer_get_count - obtain the instant count in
// case of a readout or pause
//-------------------------------------------------
u16 kp63_device::timer_get_count(unsigned n) const
{
if (!BIT(m_timer_started, n) || BIT(m_status[n], 5) || ((m_status[n] & 0x03) == 0x03 && !BIT(m_gate_input, n)))
return m_last_count[n];
else
{
unsigned prescale = BIT(m_status[n], 1) ? 4 : BIT(m_status[n], 0) ? 16 : 256;
if ((m_status[n] & 0x0c) == 0x0c)
{
// PWM
u8 ticks = attotime_to_clocks(m_timer[n]->remaining()) / prescale;
return ticks | ((m_cr[n] - (u16(ticks) << 8)) & 0xff00);
}
else
return attotime_to_clocks(m_timer[n]->remaining()) / prescale;
}
}
//-------------------------------------------------
// read - read count or status register
//-------------------------------------------------
u8 kp63_device::read(offs_t offset)
{
const unsigned n = offset >> 1;
assert(n < c_num_counters);
if (BIT(offset, 0))
{
// Status read clears read/write sequence
if (!machine().side_effects_disabled())
m_rw_seq &= ~(1 << n);
return m_status[n];
}
else if (BIT(m_rw_seq, n))
{
// Second step of counter readout
if (!machine().side_effects_disabled())
m_rw_seq &= ~(1 << n);
return m_count_tmp[n];
}
else
{
// First step of counter readout
u16 count = timer_get_count(n);
if (!machine().side_effects_disabled())
{
// Latch high byte into TMP register
m_rw_seq |= 1 << n;
m_count_tmp[n] = count >> 8;
}
return count & 0x00ff;
}
}
//-------------------------------------------------
// write - set CR or mode register
//-------------------------------------------------
void kp63_device::write(offs_t offset, u8 data)
{
const unsigned n = offset >> 1;
assert(n < c_num_counters);
if (BIT(offset, 0))
{
bool old_outp = BIT(m_status[n], 7) != BIT(m_status[n], 4);
// Stop count before setting mode
if (BIT(m_timer_started, n))
{
if (!BIT(m_status[n], 5) || ((m_status[n] & 0x03) != 0x03 || BIT(m_gate_input, n)))
{
m_last_count[n] = timer_get_count(n);
m_timer[n]->adjust(attotime::never);
m_pwm_timer[n]->adjust(attotime::never);
}
m_timer_started &= ~(1 << n);
}
if (BIT(data & c_mode_mask, 5))
LOG("%s: Timer #%d configured for %s mode, %s edges of GATE, initial output %c\n",
machine().describe_context(),
n,
s_count_modes[BIT(data, 2, 2)],
BIT(data, 1) ? "???" : BIT(data, 0) ? "falling" : "rising",
BIT(data, 4) ? 'H' : 'L');
else
LOG("%s: Timer #%d configured for %s mode, 1/%d system clock (GATE %s), initial output %c\n",
machine().describe_context(),
n,
s_count_modes[BIT(data, 2, 2)],
BIT(data, 1) ? 4 : BIT(data, 0) ? 16 : 256,
(data == 0x03) == 0x03 ? "effective" : "ignored",
BIT(data, 4) ? 'H' : 'L');
m_status[n] = data & c_mode_mask;
// Update OUTP
if (old_outp != BIT(data, 4))
m_out_pulse_callback[n](BIT(data, 4) ? 1 : 0);
}
else if ((m_status[n] & 0x0c) == 0x08)
{
// WDT retrigger (data ignored; initial count must be written using a different mode)
timer_reload(n);
}
else if (BIT(m_rw_seq, n))
{
// Second step of initial count write
m_rw_seq &= ~(1 << n);
m_cr[n] = u16(data) << 8 | m_count_tmp[n];
LOG("%s: Timer #%d initial count = %d\n", machine().describe_context(), n, (m_status[n] == 0x0c) ? m_cr[n] & 0x00ff : m_cr[n]);
// Automatic retrigger in one-shot and continuous modes
if (!BIT(m_status[n], 3) || !BIT(m_timer_started, n))
{
if (!BIT(m_status[n], 7))
{
// Toggle OUTP
m_status[n] |= 0x80;
m_out_pulse_callback[n](BIT(m_status[n], 4) ? 0 : 1);
}
timer_reload(n);
}
}
else
{
// First step of initial count write (held in TMP register)
m_rw_seq |= 1 << n;
m_count_tmp[n] = data;
}
}
//-------------------------------------------------
// write_gate - handle gate inputs
//-------------------------------------------------
void kp63_device::write_gate(unsigned n, bool state)
{
assert(n < c_num_counters);
if (BIT(m_gate_input, n) != state)
return;
if (state)
m_gate_input |= 1 << n;
else
m_gate_input &= ~(1 << n);
if (BIT(m_timer_started, n))
{
if ((m_status[n] & 0x23) == 0x03)
{
// Timer gated on or off
if (state)
timer_resume_count(n);
else
{
m_last_count[n] = timer_get_count(n);
m_timer[n]->adjust(attotime::never);
}
}
else if ((m_status[n] & 0x23) == (state ? 0x21 : 0x20))
{
// Count edges of gate input
if ((m_status[n] & 0x0c) == 0x0c)
{
// PWM: count is in lower 8 bits
if ((m_last_count[n] & 0x00ff) == 0)
timer_pulse(n);
else
{
// Decrement both halves and check for underflow in upper half
m_last_count[n] -= 0x0101;
if (m_last_count[n] >= 0xff00)
{
m_status[n] &= 0x7f;
m_out_pulse_callback[n](BIT(m_status[n], 4) ? 1 : 0);
}
}
}
else if (m_last_count[n]-- == 0)
timer_pulse(n);
}
}
}

View File

@ -0,0 +1,98 @@
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************
Kawasaki Steel (Kawatetsu) KP63(A) Timer/Counter
***************************************************************************/
#ifndef MAME_CPU_Z80_KP63_H
#define MAME_CPU_Z80_KP63_H
#pragma once
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
class kp63_device : public device_t
{
public:
// callback configuration
template <int N> auto outp_callback() { return m_out_pulse_callback[N].bind(); }
template <int N> auto outs_callback() { return m_out_strobe_callback[N].bind(); }
// register interface
u8 read(offs_t offset);
void write(offs_t offset, u8 data);
// input line interface
template <int N> DECLARE_WRITE_LINE_MEMBER(gate_w) { write_gate(N, state); }
protected:
// construction/destruction
kp63_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u8 num_counters, u8 mode_mask);
// device-level overrides
virtual void device_resolve_objects() override;
virtual void device_start() override;
virtual void device_reset() override;
private:
static const char *const s_count_modes[4];
// timer callbacks
template <int N> TIMER_CALLBACK_MEMBER(timer_expired);
template <int N> TIMER_CALLBACK_MEMBER(strobe_off);
template <int N> TIMER_CALLBACK_MEMBER(pwm_off);
// internal helpers
void timer_pulse(unsigned n);
void timer_reload(unsigned n);
void timer_resume_count(unsigned n);
u16 timer_get_count(unsigned n) const;
void write_gate(unsigned n, bool state);
// callback objects
devcb_write_line::array<4> m_out_pulse_callback;
devcb_write_line::array<4> m_out_strobe_callback;
// constant parameters
const u8 c_num_counters;
const u8 c_mode_mask;
// internal timers
emu_timer *m_timer[4];
emu_timer *m_strobe_timer[4];
emu_timer *m_pwm_timer[4];
// internal state
u16 m_cr[4];
u16 m_last_count[4];
u8 m_count_tmp[4];
u8 m_status[4];
u8 m_rw_seq;
u8 m_timer_started;
u8 m_gate_input;
};
class kp63_3channel_device : public kp63_device
{
public:
// device type constructor
kp63_3channel_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
};
class kp63a_device : public kp63_device
{
public:
// device type constructor
kp63a_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
};
// device type declarations
DECLARE_DEVICE_TYPE(KP63_3CHANNEL, kp63_3channel_device)
DECLARE_DEVICE_TYPE(KP63A, kp63a_device)
#endif // MAME_CPU_Z80_KP63_H

View File

@ -107,12 +107,12 @@ void kp69_device::device_start()
void kp69_base_device::add_to_state(device_state_interface &state, int index) void kp69_base_device::add_to_state(device_state_interface &state, int index)
{ {
state.state_add<u16>(index, "IRR", [this]() { return m_irr; }, [this](u16 data) { set_irr(data); }); state.state_add(index, "IRR", m_irr, [this](u16 data) { set_irr(data); });
state.state_add<u16>(index + 1, "ISR", [this]() { return m_isr; }, [this](u16 data) { set_isr(data); }); state.state_add(index + 1, "ISR", m_isr, [this](u16 data) { set_isr(data); });
state.state_add(index + 2, "IVR", m_ivr).mask(0xe0); state.state_add(index + 2, "IVR", m_ivr).mask(0xe0);
state.state_add<u16>(index + 3, "LER", [this]() { return m_ler; }, [this](u16 data) { set_ler(data); }); state.state_add(index + 3, "LER", m_ler, [this](u16 data) { set_ler(data); });
state.state_add<u16>(index + 4, "PGR", [this]() { return m_pgr; }, [this](u16 data) { set_pgr(data); }); state.state_add(index + 4, "PGR", m_pgr, [this](u16 data) { set_pgr(data); });
state.state_add<u16>(index + 5, "IMR", [this]() { return m_imr; }, [this](u16 data) { set_imr(data); }); state.state_add(index + 5, "IMR", m_imr, [this](u16 data) { set_imr(data); });
} }

View File

@ -102,7 +102,7 @@ To Do:
to "coin" (it probably changes the number of reads from port $C0). to "coin" (it probably changes the number of reads from port $C0).
I guess the reset_delay mechanism should be implemented with a timer in eeprom.c. I guess the reset_delay mechanism should be implemented with a timer in eeprom.c.
- pyenaget intro: when the theater scrolls out to the left, the train should scroll in from the right, - pyenaget intro: when the theater scrolls out to the left, the train should scroll in from the right,
with no visible gaps. It currently leaves the screen empty instead, for several seconds. with no visible gaps. It currently leaves a small gap.
- tdoboon: no smoke from hit planes as shown in the video? Tiles are present (f60-125f) and used in demo mode. - tdoboon: no smoke from hit planes as shown in the video? Tiles are present (f60-125f) and used in demo mode.
- dashhero does not acknowledge the button bashing correctly, it's very hard to win (a slower pace works better!) - dashhero does not acknowledge the button bashing correctly, it's very hard to win (a slower pace works better!)
- dodghero and sushimar often write zeroes to 81XX1 and 00XX1 for some reason (maybe just sloppy coding?) - dodghero and sushimar often write zeroes to 81XX1 and 00XX1 for some reason (maybe just sloppy coding?)
@ -283,6 +283,7 @@ public:
sammymdl_state(const machine_config &mconfig, device_type type, const char *tag) sammymdl_state(const machine_config &mconfig, device_type type, const char *tag)
: sigmab98_base_state(mconfig, type, tag) : sigmab98_base_state(mconfig, type, tag)
, m_maincpu(*this, "maincpu") , m_maincpu(*this, "maincpu")
, m_kp69(*this, "maincpu:kp69")
, m_eeprom(*this, "eeprom") , m_eeprom(*this, "eeprom")
, m_hopper(*this, "hopper") , m_hopper(*this, "hopper")
, m_hopper_small(*this, "hopper_small") , m_hopper_small(*this, "hopper_small")
@ -301,13 +302,13 @@ public:
void init_itazuram(); void init_itazuram();
void init_animalc(); void init_animalc();
void init_haekaka(); void init_haekaka();
void init_gocowboy();
protected: protected:
virtual void machine_start() override { m_leds.resolve(); } virtual void machine_start() override { m_leds.resolve(); }
private: private:
TIMER_DEVICE_CALLBACK_MEMBER(sammymdl_irq); TIMER_DEVICE_CALLBACK_MEMBER(gocowboy_int);
TIMER_DEVICE_CALLBACK_MEMBER(timer_1khz);
uint8_t coin_counter_r(); uint8_t coin_counter_r();
void coin_counter_w(uint8_t data); void coin_counter_w(uint8_t data);
@ -339,6 +340,7 @@ private:
// Required devices // Required devices
required_device<kl5c80a12_device> m_maincpu; required_device<kl5c80a12_device> m_maincpu;
required_device<kp69_device> m_kp69;
required_device<eeprom_serial_93cxx_device> m_eeprom; required_device<eeprom_serial_93cxx_device> m_eeprom;
// Optional devices // Optional devices
@ -348,10 +350,6 @@ private:
output_finder<8> m_leds; output_finder<8> m_leds;
uint8_t m_vblank_vector;
uint8_t m_timer0_vector;
uint8_t m_timer1_vector;
uint8_t m_out[3]; uint8_t m_out[3];
}; };
@ -946,7 +944,7 @@ uint8_t sigmab98_base_state::vblank_r()
{ {
// mask 0x04 must be set before writing sprite list // mask 0x04 must be set before writing sprite list
// mask 0x10 must be set or irq/00 hangs? // mask 0x10 must be set or irq/00 hangs?
return m_vblank | 0x14; return (m_vblank & ~0x01) | 0x14;
} }
void sigmab98_base_state::vblank_w(uint8_t data) void sigmab98_base_state::vblank_w(uint8_t data)
@ -1632,23 +1630,29 @@ void lufykzku_state::lufykzku(machine_config &config)
Sammy Medal Games Sammy Medal Games
***************************************************************************/ ***************************************************************************/
TIMER_DEVICE_CALLBACK_MEMBER(sammymdl_state::sammymdl_irq) TIMER_DEVICE_CALLBACK_MEMBER(sammymdl_state::gocowboy_int)
{ {
int scanline = param; int scanline = param;
uint16_t irqs = 0;
// TODO: what really triggers these?
if (scanline == 240) if (scanline == 240)
irqs |= 1 << ((m_vblank_vector & 0x1e) >> 1); {
m_kp69->ir_w<0>(1);
m_kp69->ir_w<0>(0);
}
if (scanline == 128) if (scanline == 128)
irqs |= 1 << ((m_timer0_vector & 0x1e) >> 1); {
m_kp69->ir_w<1>(1);
m_kp69->ir_w<1>(0);
}
}
if (scanline == 32) TIMER_DEVICE_CALLBACK_MEMBER(sammymdl_state::timer_1khz)
irqs |= 1 << ((m_timer1_vector & 0x1e) >> 1); {
// FIXME: this is an internally generated timer interrupt
// FIXME: this is not much less of a hack than HOLD_LINE m_kp69->ir_w<11>(1);
if (irqs != 0) m_kp69->ir_w<11>(0);
m_maincpu->set_state_int(kl5c80a12_device::KP69_IRR, m_maincpu->state_int(kl5c80a12_device::KP69_IRR) | irqs);
} }
void sammymdl_state::sammymdl(machine_config &config) void sammymdl_state::sammymdl(machine_config &config)
@ -1657,8 +1661,6 @@ void sammymdl_state::sammymdl(machine_config &config)
m_maincpu->set_addrmap(AS_PROGRAM, &sammymdl_state::animalc_map); m_maincpu->set_addrmap(AS_PROGRAM, &sammymdl_state::animalc_map);
m_maincpu->set_addrmap(AS_IO, &sammymdl_state::animalc_io); m_maincpu->set_addrmap(AS_IO, &sammymdl_state::animalc_io);
TIMER(config, "scantimer").configure_scanline(FUNC(sammymdl_state::sammymdl_irq), "screen", 0, 1);
NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // battery backed RAM NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); // battery backed RAM
EEPROM_93C46_8BIT(config, "eeprom"); EEPROM_93C46_8BIT(config, "eeprom");
@ -1673,7 +1675,7 @@ void sammymdl_state::sammymdl(machine_config &config)
m_screen->set_size(0x140, 0x100); m_screen->set_size(0x140, 0x100);
m_screen->set_visarea(0, 0x140-1, 0, 0xf0-1); m_screen->set_visarea(0, 0x140-1, 0, 0xf0-1);
m_screen->set_screen_update(FUNC(sammymdl_state::screen_update)); m_screen->set_screen_update(FUNC(sammymdl_state::screen_update));
m_screen->screen_vblank().set(FUNC(sammymdl_state::screen_vblank_sammymdl)); m_screen->screen_vblank().set(m_kp69, FUNC(kp69_device::ir_w<0>));
m_screen->set_palette(m_palette); m_screen->set_palette(m_palette);
GFXDECODE(config, m_gfxdecode, m_palette, gfx_sigmab98); GFXDECODE(config, m_gfxdecode, m_palette, gfx_sigmab98);
@ -1706,9 +1708,14 @@ void sammymdl_state::gocowboy(machine_config &config)
m_maincpu->set_addrmap(AS_PROGRAM, &sammymdl_state::gocowboy_map); m_maincpu->set_addrmap(AS_PROGRAM, &sammymdl_state::gocowboy_map);
m_maincpu->set_addrmap(AS_IO, &sammymdl_state::gocowboy_io); m_maincpu->set_addrmap(AS_IO, &sammymdl_state::gocowboy_io);
TIMER(config, "scantimer").configure_scanline(FUNC(sammymdl_state::gocowboy_int), "screen", 0, 1);
TIMER(config, "1khztimer").configure_periodic(FUNC(sammymdl_state::timer_1khz), attotime::from_msec(1));
config.device_remove("hopper"); config.device_remove("hopper");
TICKET_DISPENSER(config, m_hopper_small, attotime::from_msec(1000), TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_LOW ); TICKET_DISPENSER(config, m_hopper_small, attotime::from_msec(200), TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_LOW );
TICKET_DISPENSER(config, m_hopper_large, attotime::from_msec(1000), TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_LOW ); TICKET_DISPENSER(config, m_hopper_large, attotime::from_msec(200), TICKET_MOTOR_ACTIVE_LOW, TICKET_STATUS_ACTIVE_LOW );
m_screen->screen_vblank().set_nop();
} }
void sammymdl_state::haekaka(machine_config &config) void sammymdl_state::haekaka(machine_config &config)
@ -1717,14 +1724,21 @@ void sammymdl_state::haekaka(machine_config &config)
m_maincpu->set_addrmap(AS_PROGRAM, &sammymdl_state::haekaka_map); m_maincpu->set_addrmap(AS_PROGRAM, &sammymdl_state::haekaka_map);
m_maincpu->set_addrmap(AS_IO, &sammymdl_state::haekaka_io); m_maincpu->set_addrmap(AS_IO, &sammymdl_state::haekaka_io);
m_screen->screen_vblank().set(m_kp69, FUNC(kp69_device::ir_w<2>));
} }
void sammymdl_state::itazuram(machine_config &config) void sammymdl_state::itazuram(machine_config &config)
{ {
sammymdl(config); sammymdl(config);
TIMER(config, "scantimer").configure_scanline(FUNC(sammymdl_state::gocowboy_int), "screen", 0, 1);
TIMER(config, "1khztimer").configure_periodic(FUNC(sammymdl_state::timer_1khz), attotime::from_msec(1));
m_maincpu->set_addrmap(AS_PROGRAM, &sammymdl_state::itazuram_map); m_maincpu->set_addrmap(AS_PROGRAM, &sammymdl_state::itazuram_map);
m_maincpu->set_addrmap(AS_IO, &sammymdl_state::itazuram_io); m_maincpu->set_addrmap(AS_IO, &sammymdl_state::itazuram_io);
m_screen->screen_vblank().set_nop();
} }
void sammymdl_state::pyenaget(machine_config &config) void sammymdl_state::pyenaget(machine_config &config)
@ -1733,6 +1747,8 @@ void sammymdl_state::pyenaget(machine_config &config)
m_maincpu->set_addrmap(AS_PROGRAM, &sammymdl_state::haekaka_map); m_maincpu->set_addrmap(AS_PROGRAM, &sammymdl_state::haekaka_map);
m_maincpu->set_addrmap(AS_IO, &sammymdl_state::pyenaget_io); m_maincpu->set_addrmap(AS_IO, &sammymdl_state::pyenaget_io);
m_screen->screen_vblank().set(m_kp69, FUNC(kp69_device::ir_w<2>));
} }
void sammymdl_state::tdoboon(machine_config &config) void sammymdl_state::tdoboon(machine_config &config)
@ -1742,6 +1758,7 @@ void sammymdl_state::tdoboon(machine_config &config)
m_maincpu->set_addrmap(AS_PROGRAM, &sammymdl_state::tdoboon_map); m_maincpu->set_addrmap(AS_PROGRAM, &sammymdl_state::tdoboon_map);
m_maincpu->set_addrmap(AS_IO, &sammymdl_state::tdoboon_io); m_maincpu->set_addrmap(AS_IO, &sammymdl_state::tdoboon_io);
m_screen->screen_vblank().set(m_kp69, FUNC(kp69_device::ir_w<2>));
m_screen->set_visarea(0,0x140-1, 0+4,0xf0+4-1); m_screen->set_visarea(0,0x140-1, 0+4,0xf0+4-1);
} }
@ -2184,10 +2201,6 @@ void sammymdl_state::init_animalc()
// force jump out of BIOS loop // force jump out of BIOS loop
rom[0x005ac] = 0xc3; rom[0x005ac] = 0xc3;
m_vblank_vector = 0x00; // increment counter
m_timer0_vector = 0x1c; // read hopper state
m_timer1_vector = 0x1e; // drive hopper motor
} }
/*************************************************************************** /***************************************************************************
@ -2225,13 +2238,6 @@ ROM_START( gocowboy )
ROM_LOAD( "vm1212f01.u5.jed", 0x0000, 0x5cde, CRC(b86a1825) SHA1(cc2e633fb8a24cfc93291a778b0964089f6b8ac7) ) ROM_LOAD( "vm1212f01.u5.jed", 0x0000, 0x5cde, CRC(b86a1825) SHA1(cc2e633fb8a24cfc93291a778b0964089f6b8ac7) )
ROM_END ROM_END
void sammymdl_state::init_gocowboy()
{
m_vblank_vector = 0x00;
m_timer0_vector = 0x02;
m_timer1_vector = 0x16;
}
/*************************************************************************** /***************************************************************************
Itazura Monkey ( VX1902L02 ITZRMONKY 200011211639 SAMMY CORP. AM ) Itazura Monkey ( VX1902L02 ITZRMONKY 200011211639 SAMMY CORP. AM )
@ -2260,10 +2266,6 @@ void sammymdl_state::init_itazuram()
// force jump out of BIOS loop // force jump out of BIOS loop
rom[0x005ac] = 0xc3; rom[0x005ac] = 0xc3;
m_vblank_vector = 0x00;
m_timer0_vector = 0x02;
m_timer1_vector = 0x16;
} }
/*************************************************************************** /***************************************************************************
@ -2366,10 +2368,6 @@ void sammymdl_state::init_haekaka()
// force jump out of BIOS loop // force jump out of BIOS loop
rom[0x005ac] = 0xc3; rom[0x005ac] = 0xc3;
m_vblank_vector = 0x04;
m_timer0_vector = 0x1a;
m_timer1_vector = 0x1c;
} }
/*************************************************************************** /***************************************************************************
@ -2396,4 +2394,4 @@ GAME( 2000, itazuram, sammymdl, itazuram, sammymdl, sammymdl_state, init_itazura
GAME( 2000, pyenaget, sammymdl, pyenaget, sammymdl, sammymdl_state, init_haekaka, ROT0, "Sammy", "Pye-nage Taikai", 0 ) GAME( 2000, pyenaget, sammymdl, pyenaget, sammymdl, sammymdl_state, init_haekaka, ROT0, "Sammy", "Pye-nage Taikai", 0 )
GAME( 2000, tdoboon, sammymdl, tdoboon, haekaka, sammymdl_state, init_haekaka, ROT0, "Sammy", "Taihou de Doboon", 0 ) GAME( 2000, tdoboon, sammymdl, tdoboon, haekaka, sammymdl_state, init_haekaka, ROT0, "Sammy", "Taihou de Doboon", 0 )
GAME( 2001, haekaka, sammymdl, haekaka, haekaka, sammymdl_state, init_haekaka, ROT0, "Sammy", "Hae Hae Ka Ka Ka", 0 ) GAME( 2001, haekaka, sammymdl, haekaka, haekaka, sammymdl_state, init_haekaka, ROT0, "Sammy", "Hae Hae Ka Ka Ka", 0 )
GAME( 2003, gocowboy, 0, gocowboy, gocowboy, sammymdl_state, init_gocowboy, ROT0, "Sammy", "Go Go Cowboy (English, prize)", 0 ) GAME( 2003, gocowboy, 0, gocowboy, gocowboy, sammymdl_state, empty_init, ROT0, "Sammy", "Go Go Cowboy (English, prize)", 0 )