mirror of
https://github.com/holub/mame
synced 2025-04-19 15:11:37 +03:00
added melody controller
This commit is contained in:
parent
7db7f9e159
commit
5943989636
@ -6,6 +6,9 @@
|
||||
- SM510: x
|
||||
- SM511: x
|
||||
- SM512: x
|
||||
|
||||
Other chips that may be in the same family, investigate more when one of
|
||||
them needs to get emulated: SM500, SM530, SM531, ..
|
||||
|
||||
References:
|
||||
- 1990 Sharp Microcomputers Data Book
|
||||
@ -63,6 +66,7 @@ void sm510_base_device::device_start()
|
||||
m_c = 0;
|
||||
m_skip = false;
|
||||
m_w = 0;
|
||||
m_r = 0;
|
||||
m_div = 0;
|
||||
m_1s = false;
|
||||
m_k_active = false;
|
||||
@ -72,6 +76,11 @@ void sm510_base_device::device_start()
|
||||
m_bp = false;
|
||||
m_bc = false;
|
||||
m_halt = false;
|
||||
m_melody_rd = 0;
|
||||
m_melody_step_count = 0;
|
||||
m_melody_duty_count = 0;
|
||||
m_melody_duty_index = 0;
|
||||
m_melody_address = 0;
|
||||
|
||||
// register for savestates
|
||||
save_item(NAME(m_stack));
|
||||
@ -86,6 +95,7 @@ void sm510_base_device::device_start()
|
||||
save_item(NAME(m_c));
|
||||
save_item(NAME(m_skip));
|
||||
save_item(NAME(m_w));
|
||||
save_item(NAME(m_r));
|
||||
save_item(NAME(m_div));
|
||||
save_item(NAME(m_1s));
|
||||
save_item(NAME(m_k_active));
|
||||
@ -95,6 +105,11 @@ void sm510_base_device::device_start()
|
||||
save_item(NAME(m_bp));
|
||||
save_item(NAME(m_bc));
|
||||
save_item(NAME(m_halt));
|
||||
save_item(NAME(m_melody_rd));
|
||||
save_item(NAME(m_melody_step_count));
|
||||
save_item(NAME(m_melody_duty_count));
|
||||
save_item(NAME(m_melody_duty_index));
|
||||
save_item(NAME(m_melody_address));
|
||||
|
||||
// register state for debugger
|
||||
state_add(SM510_PC, "PC", m_pc).formatstr("%04X");
|
||||
@ -109,8 +124,10 @@ void sm510_base_device::device_start()
|
||||
|
||||
m_icountptr = &m_icount;
|
||||
|
||||
// init peripherals
|
||||
init_divider();
|
||||
init_lcd_driver();
|
||||
init_melody();
|
||||
}
|
||||
|
||||
|
||||
@ -132,6 +149,7 @@ void sm510_base_device::device_reset()
|
||||
m_bc = false;
|
||||
m_y = 0;
|
||||
|
||||
m_r = 0;
|
||||
m_write_r(0, 0, 0xff);
|
||||
}
|
||||
|
||||
@ -175,6 +193,7 @@ TIMER_CALLBACK_MEMBER(sm510_base_device::lcd_timer_cb)
|
||||
|
||||
void sm510_base_device::init_lcd_driver()
|
||||
{
|
||||
// note: in reality, this timer runs at high frequency off the main divider, strobing one segment at a time
|
||||
m_lcd_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(sm510_base_device::lcd_timer_cb), this));
|
||||
m_lcd_timer->adjust(attotime::from_ticks(0x200, unscaled_clock())); // 64hz default
|
||||
}
|
||||
@ -182,7 +201,83 @@ void sm510_base_device::init_lcd_driver()
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// interrupt/timer
|
||||
// melody controller
|
||||
//-------------------------------------------------
|
||||
|
||||
void sm510_base_device::clock_melody()
|
||||
{
|
||||
if (!m_melody_rom)
|
||||
return;
|
||||
|
||||
// tone cycle table (SM511/SM512 datasheet fig.5)
|
||||
// cmd 0 = cmd, 1 = stop, > 13 = illegal(unknown)
|
||||
static const UINT8 lut_tone_cycles[4*16] =
|
||||
{
|
||||
0, 0, 7, 8, 8, 9, 9, 10,11,11,12,13,14,14, 7*2, 8*2,
|
||||
0, 0, 8, 8, 9, 9, 10,11,11,12,13,13,14,15, 8*2, 8*2,
|
||||
0, 0, 8, 8, 9, 9, 10,10,11,12,12,13,14,15, 8*2, 8*2,
|
||||
0, 0, 8, 9, 9, 10,10,11,11,12,13,14,14,15, 8*2, 9*2
|
||||
};
|
||||
|
||||
UINT8 cmd = m_melody_rom[m_melody_address] & 0x3f;
|
||||
UINT8 out = 0;
|
||||
|
||||
// clock duty cycle if tone is active
|
||||
if ((cmd & 0xf) > 1)
|
||||
{
|
||||
out = m_melody_duty_index & m_melody_rd & 1;
|
||||
m_melody_duty_count++;
|
||||
int index = m_melody_duty_index << 4 | (cmd & 0xf);
|
||||
int shift = ~cmd >> 4 & 1; // OCT
|
||||
|
||||
if (m_melody_duty_count >= (lut_tone_cycles[index] << shift))
|
||||
{
|
||||
m_melody_duty_count = 0;
|
||||
m_melody_duty_index = (m_melody_duty_index + 1) & 3;
|
||||
}
|
||||
}
|
||||
else if ((cmd & 0xf) == 1)
|
||||
{
|
||||
// rest tell signal
|
||||
m_melody_rd |= 2;
|
||||
}
|
||||
|
||||
// clock time base on F8(d7)
|
||||
if ((m_div & 0x7f) == 0)
|
||||
{
|
||||
UINT8 mask = (cmd & 0x20) ? 0x1f : 0x0f;
|
||||
m_melody_step_count = (m_melody_step_count + 1) & mask;
|
||||
|
||||
if (m_melody_step_count == 0)
|
||||
m_melody_address++;
|
||||
}
|
||||
|
||||
// output to R pin
|
||||
if (out != m_r)
|
||||
{
|
||||
m_write_r(0, out, 0xff);
|
||||
m_r = out;
|
||||
}
|
||||
}
|
||||
|
||||
void sm510_base_device::init_melody()
|
||||
{
|
||||
if (!m_melody_rom)
|
||||
return;
|
||||
|
||||
// verify melody rom
|
||||
for (int i = 0; i < 0x100; i++)
|
||||
{
|
||||
UINT8 data = m_melody_rom[i];
|
||||
if (data & 0xc0 || (data & 0x0f) > 13)
|
||||
logerror("%s unknown melody ROM data $%02X at $%02X\n", tag(), data, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// interrupt/divider
|
||||
//-------------------------------------------------
|
||||
|
||||
bool sm510_base_device::wake_me_up()
|
||||
@ -213,28 +308,19 @@ void sm510_base_device::execute_set_input(int line, int state)
|
||||
|
||||
TIMER_CALLBACK_MEMBER(sm510_base_device::div_timer_cb)
|
||||
{
|
||||
// no need to increment it by 1 everytime, since only the
|
||||
// highest bits are accessible
|
||||
m_div = (m_div + 0x800) & 0x7fff;
|
||||
|
||||
m_div = (m_div + 1) & 0x7fff;
|
||||
|
||||
// 1S signal on overflow(falling edge of f1)
|
||||
if (m_div == 0)
|
||||
m_1s = true;
|
||||
|
||||
// schedule next timeout
|
||||
m_div_timer->adjust(attotime::from_ticks(0x800, unscaled_clock()));
|
||||
}
|
||||
|
||||
void sm510_base_device::reset_divider()
|
||||
{
|
||||
m_div = 0;
|
||||
m_div_timer->adjust(attotime::from_ticks(0x800, unscaled_clock()));
|
||||
|
||||
clock_melody();
|
||||
}
|
||||
|
||||
void sm510_base_device::init_divider()
|
||||
{
|
||||
m_div_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(sm510_base_device::div_timer_cb), this));
|
||||
reset_divider();
|
||||
m_div_timer->adjust(attotime::from_ticks(1, unscaled_clock()), 0, attotime::from_ticks(1, unscaled_clock()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -77,6 +77,7 @@ public:
|
||||
, m_stack_levels(stack_levels)
|
||||
, m_lcd_ram_a(*this, "lcd_ram_a"), m_lcd_ram_b(*this, "lcd_ram_b"), m_lcd_ram_c(*this, "lcd_ram_c")
|
||||
, m_write_sega(*this), m_write_segb(*this), m_write_segc(*this), m_write_segbs(*this)
|
||||
, m_melody_rom(*this, "music")
|
||||
, m_read_k(*this)
|
||||
, m_read_ba(*this), m_read_b(*this)
|
||||
, m_write_s(*this)
|
||||
@ -140,6 +141,7 @@ protected:
|
||||
UINT8 m_c;
|
||||
bool m_skip;
|
||||
UINT8 m_w;
|
||||
UINT8 m_r;
|
||||
bool m_k_active;
|
||||
bool m_halt;
|
||||
|
||||
@ -156,16 +158,27 @@ protected:
|
||||
TIMER_CALLBACK_MEMBER(lcd_timer_cb);
|
||||
virtual void init_lcd_driver();
|
||||
|
||||
// clock divider
|
||||
// melody controller
|
||||
optional_region_ptr<UINT8> m_melody_rom;
|
||||
UINT8 m_melody_rd;
|
||||
UINT8 m_melody_step_count;
|
||||
UINT8 m_melody_duty_count;
|
||||
UINT8 m_melody_duty_index;
|
||||
UINT8 m_melody_address;
|
||||
|
||||
void clock_melody();
|
||||
void init_melody();
|
||||
|
||||
// interrupt/divider
|
||||
emu_timer *m_div_timer;
|
||||
UINT16 m_div;
|
||||
bool m_1s;
|
||||
|
||||
TIMER_CALLBACK_MEMBER(div_timer_cb);
|
||||
bool wake_me_up();
|
||||
virtual void reset_divider();
|
||||
void init_divider();
|
||||
|
||||
virtual void reset_divider() { m_div = 0; }
|
||||
virtual void init_divider();
|
||||
virtual TIMER_CALLBACK_MEMBER(div_timer_cb);
|
||||
|
||||
// other i/o handlers
|
||||
devcb_read8 m_read_k;
|
||||
devcb_read_line m_read_ba;
|
||||
@ -175,7 +188,7 @@ protected:
|
||||
|
||||
// misc internal helpers
|
||||
void increment_pc();
|
||||
virtual void get_opcode_param() { } // -> child class
|
||||
virtual void get_opcode_param() { }
|
||||
virtual void update_w_latch() { }
|
||||
|
||||
UINT8 ram_r();
|
||||
@ -264,6 +277,11 @@ protected:
|
||||
virtual void get_opcode_param();
|
||||
|
||||
virtual void update_w_latch() { m_write_s(0, m_w, 0xff); } // W is connected directly to S
|
||||
|
||||
// divider
|
||||
virtual TIMER_CALLBACK_MEMBER(div_timer_cb);
|
||||
virtual void reset_divider();
|
||||
virtual void init_divider();
|
||||
};
|
||||
|
||||
|
||||
|
@ -43,6 +43,40 @@ offs_t sm510_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *op
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// divider
|
||||
//-------------------------------------------------
|
||||
|
||||
TIMER_CALLBACK_MEMBER(sm510_device::div_timer_cb)
|
||||
{
|
||||
// no need to increment it by 1 everytime, since only the
|
||||
// highest bits are accessible
|
||||
m_div = (m_div + 0x800) & 0x7fff;
|
||||
|
||||
// 1S signal on overflow(falling edge of f1)
|
||||
if (m_div == 0)
|
||||
m_1s = true;
|
||||
|
||||
// schedule next timeout
|
||||
m_div_timer->adjust(attotime::from_ticks(0x800, unscaled_clock()));
|
||||
}
|
||||
|
||||
void sm510_device::reset_divider()
|
||||
{
|
||||
m_div = 0;
|
||||
m_div_timer->adjust(attotime::from_ticks(0x800, unscaled_clock()));
|
||||
}
|
||||
|
||||
void sm510_device::init_divider()
|
||||
{
|
||||
m_div_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(sm510_device::div_timer_cb), this));
|
||||
reset_divider();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// execute
|
||||
//-------------------------------------------------
|
||||
|
@ -248,7 +248,11 @@ void sm510_base_device::op_atfc()
|
||||
void sm510_base_device::op_atr()
|
||||
{
|
||||
// ATR: output ACC to R
|
||||
m_write_r(0, m_acc & 3, 0xff);
|
||||
if (m_r != (m_acc & 3))
|
||||
{
|
||||
m_r = m_acc & 3;
|
||||
m_write_r(0, m_r, 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -387,26 +391,28 @@ void sm510_base_device::op_sm()
|
||||
|
||||
void sm510_base_device::op_pre()
|
||||
{
|
||||
// PRE x: x
|
||||
op_illegal();
|
||||
// PRE x: melody ROM pointer preset
|
||||
m_melody_address = m_param;
|
||||
m_melody_step_count = 0;
|
||||
}
|
||||
|
||||
void sm510_base_device::op_sme()
|
||||
{
|
||||
// SME: x
|
||||
op_illegal();
|
||||
// SME: set melody enable
|
||||
m_melody_rd |= 1;
|
||||
}
|
||||
|
||||
void sm510_base_device::op_rme()
|
||||
{
|
||||
// RME: x
|
||||
op_illegal();
|
||||
// RME: reset melody enable
|
||||
m_melody_rd &= ~1;
|
||||
}
|
||||
|
||||
void sm510_base_device::op_tmel()
|
||||
{
|
||||
// TMEL: x
|
||||
op_illegal();
|
||||
// TMEL: skip next if rest signal is set, reset it
|
||||
m_skip = ((m_melody_rd & 2) != 0);
|
||||
m_melody_rd &= ~2;
|
||||
}
|
||||
|
||||
|
||||
|
@ -64,7 +64,7 @@ sm512_device::sm512_device(const machine_config &mconfig, const char *tag, devic
|
||||
void sm511_device::get_opcode_param()
|
||||
{
|
||||
// LBL, PRE, TL, TML and prefix opcodes are 2 bytes
|
||||
if ((m_op >= 0x5f && m_op <= 0x61) || (m_op & 0xf0) == 0x70 || (m_op & 0xfc) == 0x68)
|
||||
if (m_op == 0x01 || (m_op >= 0x5f && m_op <= 0x61) || (m_op & 0xf0) == 0x70 || (m_op & 0xfc) == 0x68)
|
||||
{
|
||||
m_icount--;
|
||||
m_param = m_program->read_byte(m_pc);
|
||||
@ -102,6 +102,7 @@ void sm511_device::execute_one()
|
||||
switch (m_op)
|
||||
{
|
||||
case 0x00: op_rot(); break;
|
||||
// case 0x01: op_xxx(); break; // ?
|
||||
case 0x02: op_sbm(); break;
|
||||
case 0x03: op_atpl(); break;
|
||||
case 0x08: op_add(); break;
|
||||
@ -129,8 +130,6 @@ void sm511_device::execute_one()
|
||||
// case 0x65: op_idiv(); break;
|
||||
case 0x66: op_rc(); break;
|
||||
case 0x67: op_sc(); break;
|
||||
// case 0x68: op_tf1(); break;
|
||||
// case 0x69: op_tf4(); break;
|
||||
case 0x6c: op_decb(); break;
|
||||
case 0x6d: op_ptw(); break;
|
||||
case 0x6e: op_rtn0(); break;
|
||||
|
@ -261,10 +261,10 @@ static INPUT_PORTS_START( ktmnt )
|
||||
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_MEMBER(DEVICE_SELF, hh_sm510_state, input_changed, NULL)
|
||||
|
||||
PORT_START("IN.1")
|
||||
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, hh_sm510_state, input_changed, NULL)
|
||||
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_MEMBER(DEVICE_SELF, hh_sm510_state, input_changed, NULL)
|
||||
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_MEMBER(DEVICE_SELF, hh_sm510_state, input_changed, NULL)
|
||||
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_MEMBER(DEVICE_SELF, hh_sm510_state, input_changed, NULL)
|
||||
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_MEMBER(DEVICE_SELF, hh_sm510_state, input_changed, NULL) // game select
|
||||
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_CHANGED_MEMBER(DEVICE_SELF, hh_sm510_state, input_changed, NULL) // sound on/off
|
||||
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_CHANGED_MEMBER(DEVICE_SELF, hh_sm510_state, input_changed, NULL) // off
|
||||
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_CHANGED_MEMBER(DEVICE_SELF, hh_sm510_state, input_changed, NULL) // on/start
|
||||
|
||||
PORT_START("IN.2")
|
||||
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_CHANGED_MEMBER(DEVICE_SELF, hh_sm510_state, input_changed, NULL)
|
||||
@ -381,8 +381,10 @@ ROM_END
|
||||
|
||||
ROM_START( ktmnt )
|
||||
ROM_REGION( 0x1000, "maincpu", 0 )
|
||||
ROM_LOAD( "kms_73b_774.music", 0x0000, 0x100, CRC(8270d626) SHA1(bd91ca1d5cd7e2a62eef05c0033b19dcdbe441ca) ) //todo
|
||||
ROM_LOAD( "kms_73b_774.prog", 0x0000, 0x1000, CRC(a1064f87) SHA1(92156c35fbbb414007ee6804fe635128a741d5f1) )
|
||||
|
||||
ROM_REGION( 0x100, "maincpu:music", 0 )
|
||||
ROM_LOAD( "kms_73b_774.music", 0x000, 0x100, CRC(8270d626) SHA1(bd91ca1d5cd7e2a62eef05c0033b19dcdbe441ca) )
|
||||
ROM_END
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user