added melody controller

This commit is contained in:
hap 2015-07-14 14:40:02 +02:00
parent 7db7f9e159
commit 5943989636
6 changed files with 183 additions and 38 deletions

View File

@ -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()));
}

View File

@ -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();
};

View File

@ -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
//-------------------------------------------------

View File

@ -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;
}

View File

@ -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;

View File

@ -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