added e0c6s46 buzzer

This commit is contained in:
hap 2015-05-15 03:13:22 +02:00
parent 624ea80c5d
commit 14eae1ec9b
3 changed files with 136 additions and 44 deletions

View File

@ -9,6 +9,8 @@
- K input interrupts - K input interrupts
- finish i/o ports - finish i/o ports
- serial interface - serial interface
- one-shot buzzer
- buzzer envelope
- add mask options to MCFG (eg. buzzer on output port R4x is optional) - add mask options to MCFG (eg. buzzer on output port R4x is optional)
*/ */
@ -80,10 +82,8 @@ void e0c6s46_device::device_start()
m_write_p3.resolve_safe(); m_write_p3.resolve_safe();
// create timers // create timers
m_clktimer_handle = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(e0c6s46_device::clktimer_cb), this)); m_core_256_handle = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(e0c6s46_device::core_256_cb), this));
m_clktimer_handle->adjust(attotime::from_ticks(128, unscaled_clock())); m_core_256_handle->adjust(attotime::from_ticks(64, unscaled_clock()));
m_stopwatch_handle = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(e0c6s46_device::stopwatch_cb), this));
m_stopwatch_handle->adjust(attotime::from_ticks(64, unscaled_clock()));
m_prgtimer_handle = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(e0c6s46_device::prgtimer_cb), this)); m_prgtimer_handle = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(e0c6s46_device::prgtimer_cb), this));
m_prgtimer_handle->adjust(attotime::never); m_prgtimer_handle->adjust(attotime::never);
m_buzzer_handle = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(e0c6s46_device::buzzer_cb), this)); m_buzzer_handle = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(e0c6s46_device::buzzer_cb), this));
@ -105,11 +105,11 @@ void e0c6s46_device::device_start()
m_lcd_control = 0; m_lcd_control = 0;
m_lcd_contrast = 0; m_lcd_contrast = 0;
m_256_src_pulse = 0;
m_watchdog_count = 0; m_watchdog_count = 0;
m_clktimer_count = 0; m_clktimer_count = 0;
m_stopwatch_on = 0; m_stopwatch_on = 0;
m_swl_src_pulse = 0;
m_swl_cur_pulse = 0; m_swl_cur_pulse = 0;
m_swl_slice = 0; m_swl_slice = 0;
m_swl_count = 0; m_swl_count = 0;
@ -122,6 +122,12 @@ void e0c6s46_device::device_start()
m_prgtimer_count = 0; m_prgtimer_count = 0;
m_prgtimer_reload = 0; m_prgtimer_reload = 0;
m_bz_43_on = 0;
m_bz_freq = 0;
m_bz_envelope = 0;
m_bz_duty_ratio = 0;
m_bz_pulse = 0;
// register for savestates // register for savestates
save_item(NAME(m_port_r)); save_item(NAME(m_port_r));
save_item(NAME(m_r_dir)); save_item(NAME(m_r_dir));
@ -138,11 +144,11 @@ void e0c6s46_device::device_start()
save_item(NAME(m_lcd_control)); save_item(NAME(m_lcd_control));
save_item(NAME(m_lcd_contrast)); save_item(NAME(m_lcd_contrast));
save_item(NAME(m_256_src_pulse));
save_item(NAME(m_watchdog_count)); save_item(NAME(m_watchdog_count));
save_item(NAME(m_clktimer_count)); save_item(NAME(m_clktimer_count));
save_item(NAME(m_stopwatch_on)); save_item(NAME(m_stopwatch_on));
save_item(NAME(m_swl_src_pulse));
save_item(NAME(m_swl_cur_pulse)); save_item(NAME(m_swl_cur_pulse));
save_item(NAME(m_swl_slice)); save_item(NAME(m_swl_slice));
save_item(NAME(m_swl_count)); save_item(NAME(m_swl_count));
@ -154,6 +160,12 @@ void e0c6s46_device::device_start()
save_item(NAME(m_prgtimer_cur_pulse)); save_item(NAME(m_prgtimer_cur_pulse));
save_item(NAME(m_prgtimer_count)); save_item(NAME(m_prgtimer_count));
save_item(NAME(m_prgtimer_reload)); save_item(NAME(m_prgtimer_reload));
save_item(NAME(m_bz_43_on));
save_item(NAME(m_bz_freq));
save_item(NAME(m_bz_envelope));
save_item(NAME(m_bz_duty_ratio));
save_item(NAME(m_bz_pulse));
} }
@ -277,9 +289,6 @@ void e0c6s46_device::write_r(UINT8 port, UINT8 data)
data &= 0xf; data &= 0xf;
m_port_r[port] = data; m_port_r[port] = data;
if (port == 4)
write_r4();
// ports R0x-R3x can be high-impedance // ports R0x-R3x can be high-impedance
UINT8 out = data; UINT8 out = data;
if (port < 4 && !(m_r_dir >> port & 1)) if (port < 4 && !(m_r_dir >> port & 1))
@ -291,17 +300,27 @@ void e0c6s46_device::write_r(UINT8 port, UINT8 data)
case 1: m_write_r1(port, out, 0xff); break; case 1: m_write_r1(port, out, 0xff); break;
case 2: m_write_r2(port, out, 0xff); break; case 2: m_write_r2(port, out, 0xff); break;
case 3: m_write_r3(port, out, 0xff); break; // TODO: R33 PTCLK/_SRDY case 3: m_write_r3(port, out, 0xff); break; // TODO: R33 PTCLK/_SRDY
// R4x: special output
case 4:
// d3: buzzer on: direct output or 1-shot output
if ((data >> 3 & 1) != m_bz_43_on)
{
m_bz_43_on = data >> 3 & 1;
reset_buzzer();
}
write_r4_out();
break;
} }
} }
void e0c6s46_device::write_r4() void e0c6s46_device::write_r4_out()
{ {
// R40: _FOUT(clock inverted output) // R40: _FOUT(clock inverted output)
// R42: FOUT or _BZ // R42: FOUT or _BZ
// R43: BZ(buzzer) on // R43: BZ(buzzer)
UINT8 data = m_port_r[4] & 2; UINT8 out = (m_port_r[4] & 2) | (m_bz_pulse << 3) | (m_bz_pulse << 2 ^ 4);
m_write_r4(4, out, 0xff);
m_write_r4(4, data, 0xff);
} }
@ -348,7 +367,25 @@ UINT8 e0c6s46_device::read_p(UINT8 port)
// timers // timers
//------------------------------------------------- //-------------------------------------------------
// clock timer TIMER_CALLBACK_MEMBER(e0c6s46_device::core_256_cb)
{
// clock-timer, stopwatch timer, and some features of the buzzer all run
// from the same internal 256hz timer (64 ticks high+low at default clock of 32768hz)
m_256_src_pulse ^= 1;
m_core_256_handle->adjust(attotime::from_ticks(64, unscaled_clock()));
// clock-timer is always running, advance it on falling edge
if (m_256_src_pulse == 0)
clock_clktimer();
// clock stopwatch on falling edge of pulse+on
m_swl_cur_pulse = m_256_src_pulse | (m_stopwatch_on ^ 1);
if (m_swl_cur_pulse == 0)
clock_stopwatch();
}
// clock-timer
void e0c6s46_device::clock_watchdog() void e0c6s46_device::clock_watchdog()
{ {
@ -361,7 +398,7 @@ void e0c6s46_device::clock_watchdog()
} }
} }
TIMER_CALLBACK_MEMBER(e0c6s46_device::clktimer_cb) void e0c6s46_device::clock_clktimer()
{ {
m_clktimer_count++; m_clktimer_count++;
@ -380,9 +417,6 @@ TIMER_CALLBACK_MEMBER(e0c6s46_device::clktimer_cb)
if (m_irqflag[IRQREG_CLKTIMER] & m_irqmask[IRQREG_CLKTIMER]) if (m_irqflag[IRQREG_CLKTIMER] & m_irqmask[IRQREG_CLKTIMER])
m_possible_irq = true; m_possible_irq = true;
// schedule next timeout (256hz at default clock of 32768hz)
m_clktimer_handle->adjust(attotime::from_ticks(128, unscaled_clock()));
// 1hz falling edge also clocks the watchdog timer // 1hz falling edge also clocks the watchdog timer
if (m_clktimer_count == 0) if (m_clktimer_count == 0)
clock_watchdog(); clock_watchdog();
@ -421,19 +455,6 @@ void e0c6s46_device::clock_stopwatch()
} }
} }
TIMER_CALLBACK_MEMBER(e0c6s46_device::stopwatch_cb)
{
m_swl_src_pulse ^= 1;
m_swl_cur_pulse = m_swl_src_pulse | (m_stopwatch_on ^ 1);
// clock stopwatch on falling edge of pulse+on
if (m_swl_cur_pulse == 0)
clock_stopwatch();
// schedule next timeout (256hz high+low at default clock of 32768hz)
m_stopwatch_handle->adjust(attotime::from_ticks(64, unscaled_clock()));
}
// programmable timer // programmable timer
@ -478,8 +499,34 @@ TIMER_CALLBACK_MEMBER(e0c6s46_device::prgtimer_cb)
// buzzer // buzzer
void e0c6s46_device::schedule_buzzer()
{
// only schedule next buzzer timeout if it's on
if (m_bz_43_on != 0)
return;
// pulse width differs per frequency selection
int mul = (m_bz_freq & 4) ? 1 : 2;
int high = ((m_bz_freq & 2) ? 12 : 8) - m_bz_duty_ratio;
int low = (16 + (m_bz_freq & 3)) - high;
m_buzzer_handle->adjust(attotime::from_ticks(m_bz_pulse ? high : low, mul * unscaled_clock()));
}
TIMER_CALLBACK_MEMBER(e0c6s46_device::buzzer_cb) TIMER_CALLBACK_MEMBER(e0c6s46_device::buzzer_cb)
{ {
// invert pulse wave and write to output
m_bz_pulse ^= 1;
write_r4_out();
schedule_buzzer();
}
void e0c6s46_device::reset_buzzer()
{
// don't reset if the timer is running
if (m_buzzer_handle->remaining() == attotime::never)
schedule_buzzer();
} }
@ -570,7 +617,7 @@ READ8_MEMBER(e0c6s46_device::io_r)
case 0x7e: case 0x7e:
return m_p_pullup; return m_p_pullup;
// clock timer (lo, hi) // clock-timer (lo, hi)
case 0x20: case 0x21: case 0x20: case 0x21:
return m_clktimer_count >> (4 * (offset & 1)) & 0xf; return m_clktimer_count >> (4 * (offset & 1)) & 0xf;
@ -592,6 +639,13 @@ READ8_MEMBER(e0c6s46_device::io_r)
case 0x79: case 0x79:
return m_prgtimer_select; return m_prgtimer_select;
// buzzer
case 0x74:
return m_bz_freq;
case 0x75:
// d3: 1-shot buzzer is running
return 0 | m_bz_envelope;
// OSC circuit // OSC circuit
case 0x70: case 0x70:
return m_osc; return m_osc;
@ -645,7 +699,7 @@ WRITE8_MEMBER(e0c6s46_device::io_w)
write_r(offset & 7, data); write_r(offset & 7, data);
break; break;
case 0x7b: case 0x7b:
// d0-d3: Rx* direction 0: high impedance, 1: output // d0-d3: Rx* direction 0: high-impedance, 1: output
if (data != m_r_dir) if (data != m_r_dir)
{ {
m_r_dir = data; m_r_dir = data;
@ -705,10 +759,10 @@ WRITE8_MEMBER(e0c6s46_device::io_w)
m_svd = data & 7; m_svd = data & 7;
break; break;
// clock timer // clock-timer
case 0x76: case 0x76:
// d0: reset watchdog // d0: reset watchdog
// d1: reset clock timer (hw glitch note, not emulated: this also "sometimes"(when??) // d1: reset clktimer (hw glitch note, not emulated: this also "sometimes"(when??)
// sets the clktimer interrupt and clocks the watchdog) // sets the clktimer interrupt and clocks the watchdog)
if (data & 1) if (data & 1)
m_watchdog_count = 0; m_watchdog_count = 0;
@ -727,7 +781,7 @@ WRITE8_MEMBER(e0c6s46_device::io_w)
m_swl_count = 0; m_swl_count = 0;
m_swl_slice = 0; m_swl_slice = 0;
} }
if (m_stopwatch_on && m_swl_cur_pulse && !m_swl_src_pulse) if (m_stopwatch_on && m_swl_cur_pulse && !m_256_src_pulse)
{ {
// clock stopwatch on falling edge of pulse+on // clock stopwatch on falling edge of pulse+on
m_swl_cur_pulse = 0; m_swl_cur_pulse = 0;
@ -773,6 +827,20 @@ WRITE8_MEMBER(e0c6s46_device::io_w)
m_prgtimer_select = data; m_prgtimer_select = data;
break; break;
// buzzer
case 0x74:
// d0-d2: frequency (8 steps, 4096hz to ~1170hz)
// d3: 1-shot buzzer duration 31.25ms or 62.5ms
m_bz_freq = data;
break;
case 0x75:
// d0: envelope on/off
// d1: envelope cycle selection
// d2: reset envelope
// d3: trigger one-shot buzzer
m_bz_envelope = data & 3;
break;
// read-only registers // read-only registers
case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25:

View File

@ -120,7 +120,7 @@ private:
devcb_read8 m_read_p0, m_read_p1, m_read_p2, m_read_p3; devcb_read8 m_read_p0, m_read_p1, m_read_p2, m_read_p3;
devcb_write8 m_write_p0, m_write_p1, m_write_p2, m_write_p3; devcb_write8 m_write_p0, m_write_p1, m_write_p2, m_write_p3;
void write_r(UINT8 port, UINT8 data); void write_r(UINT8 port, UINT8 data);
void write_r4(); void write_r4_out();
void write_p(UINT8 port, UINT8 data); void write_p(UINT8 port, UINT8 data);
UINT8 read_p(UINT8 port); UINT8 read_p(UINT8 port);
@ -133,12 +133,14 @@ private:
UINT8 m_dfk0; UINT8 m_dfk0;
// timers // timers
int m_256_src_pulse;
emu_timer *m_core_256_handle;
TIMER_CALLBACK_MEMBER(core_256_cb);
int m_watchdog_count; int m_watchdog_count;
void clock_watchdog(); void clock_watchdog();
UINT8 m_clktimer_count; UINT8 m_clktimer_count;
emu_timer *m_clktimer_handle; void clock_clktimer();
TIMER_CALLBACK_MEMBER(clktimer_cb);
UINT8 m_stopwatch_on; UINT8 m_stopwatch_on;
int m_swl_src_pulse; int m_swl_src_pulse;
@ -146,8 +148,6 @@ private:
int m_swl_slice; int m_swl_slice;
int m_swl_count; int m_swl_count;
int m_swh_count; int m_swh_count;
emu_timer *m_stopwatch_handle;
TIMER_CALLBACK_MEMBER(stopwatch_cb);
void clock_stopwatch(); void clock_stopwatch();
UINT8 m_prgtimer_select; UINT8 m_prgtimer_select;
@ -161,8 +161,15 @@ private:
bool prgtimer_reset_prescaler(); bool prgtimer_reset_prescaler();
void clock_prgtimer(); void clock_prgtimer();
UINT8 m_bz_43_on;
UINT8 m_bz_freq;
UINT8 m_bz_envelope;
UINT8 m_bz_duty_ratio;
int m_bz_pulse;
emu_timer *m_buzzer_handle; emu_timer *m_buzzer_handle;
TIMER_CALLBACK_MEMBER(buzzer_cb); TIMER_CALLBACK_MEMBER(buzzer_cb);
void schedule_buzzer();
void reset_buzzer();
}; };

View File

@ -27,6 +27,8 @@ public:
required_device<e0c6s46_device> m_maincpu; required_device<e0c6s46_device> m_maincpu;
required_device<speaker_sound_device> m_speaker; required_device<speaker_sound_device> m_speaker;
DECLARE_WRITE8_MEMBER(speaker_w);
DECLARE_PALETTE_INIT(tama); DECLARE_PALETTE_INIT(tama);
DECLARE_INPUT_CHANGED_MEMBER(input_changed); DECLARE_INPUT_CHANGED_MEMBER(input_changed);
}; };
@ -59,7 +61,7 @@ static E0C6S46_PIXEL_UPDATE_CB(tama_pixel_update)
// above screen: 0:meal, 1:lamp, 2:play, 3:medicine // above screen: 0:meal, 1:lamp, 2:play, 3:medicine
// under screen: 4:bath, 5:scales, 6:shout, 7:attention // under screen: 4:bath, 5:scales, 6:shout, 7:attention
// they are on pin SEG08(x=35) + COM00-03, pin SEG28(x=36) + COM12-15 // they are on pin SEG8(x=35) + COM0-3, pin SEG28(x=36) + COM12-15
if (x == 35 && y < 4) if (x == 35 && y < 4)
output_set_lamp_value(y, state); output_set_lamp_value(y, state);
else if (x == 36 && y >= 12) else if (x == 36 && y >= 12)
@ -79,6 +81,20 @@ PALETTE_INIT_MEMBER(tamag1_state, tama)
/***************************************************************************
I/O
***************************************************************************/
WRITE8_MEMBER(tamag1_state::speaker_w)
{
// R43: speaker out
m_speaker->level_w(data >> 3 & 1);
}
/*************************************************************************** /***************************************************************************
Inputs Inputs
@ -115,6 +131,7 @@ static MACHINE_CONFIG_START( tama, tamag1_state )
/* basic machine hardware */ /* basic machine hardware */
MCFG_CPU_ADD("maincpu", E0C6S46, XTAL_32_768kHz) MCFG_CPU_ADD("maincpu", E0C6S46, XTAL_32_768kHz)
MCFG_E0C6S46_PIXEL_UPDATE_CB(tama_pixel_update) MCFG_E0C6S46_PIXEL_UPDATE_CB(tama_pixel_update)
MCFG_E0C6S46_WRITE_R_CB(4, WRITE8(tamag1_state, speaker_w))
/* video hardware */ /* video hardware */
MCFG_SCREEN_ADD("screen", LCD) MCFG_SCREEN_ADD("screen", LCD)