mirror of
https://github.com/holub/mame
synced 2025-07-01 16:19:38 +03:00
cpu/hcd62121,casio/pickytlk.cpp: Add support for more Picky Talk models (#13015)
This commit is contained in:
parent
325fa58f6b
commit
baeadecbaf
@ -66,9 +66,14 @@ hcd62121_cpu_device::hcd62121_cpu_device(const machine_config &mconfig, const ch
|
||||
, m_f(0)
|
||||
, m_time(0)
|
||||
, m_time_op(0)
|
||||
, m_unk_f5(0)
|
||||
, m_cycles_until_timeout(0)
|
||||
, m_is_timer_started(false)
|
||||
, m_is_timer_irq_enabled(false)
|
||||
, m_is_timer_irq_asserted(false)
|
||||
, m_is_timer_wait_elapsed(true)
|
||||
, m_is_infinite_timeout(false)
|
||||
, m_timer_cycles(0)
|
||||
, m_lar(0)
|
||||
, m_opt(0)
|
||||
, m_port(0)
|
||||
@ -145,12 +150,12 @@ void hcd62121_cpu_device::write_reg(int size, u8 op1)
|
||||
if (op1 & 0x80)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
m_reg[(op1 - i) & 0x7f] = m_temp1[i];
|
||||
set_reg((op1 - i) & 0x7f, m_temp1[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
m_reg[(op1 + i) & 0x7f] = m_temp1[i];
|
||||
set_reg((op1 + i) & 0x7f, m_temp1[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -219,13 +224,13 @@ void hcd62121_cpu_device::write_regreg(int size, u8 arg1, u8 arg2)
|
||||
{
|
||||
/* store in arg1 */
|
||||
for (int i = 0; i < size; i++)
|
||||
m_reg[(arg1 + i) & 0x7f] = m_temp1[i];
|
||||
set_reg((arg1 + i) & 0x7f, m_temp1[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* store in arg2 */
|
||||
for (int i = 0; i < size; i++)
|
||||
m_reg[(arg2 + i) & 0x7f] = m_temp1[i];
|
||||
set_reg((arg2 + i) & 0x7f, m_temp1[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,7 +291,7 @@ void hcd62121_cpu_device::write_iregreg(int size, u8 op1, u8 op2)
|
||||
{
|
||||
/* store in reg2 */
|
||||
for (int i = 0; i < size; i++)
|
||||
m_reg[(op2 + i) & 0x7f] = m_temp1[i];
|
||||
set_reg((op2 + i) & 0x7f, m_temp1[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,7 +302,7 @@ void hcd62121_cpu_device::write_iregreg2(int size, u8 op1, u8 op2)
|
||||
{
|
||||
/* store in reg2 */
|
||||
for (int i = 0; i < size; i++)
|
||||
m_reg[(op2 + i) & 0x7f] = m_temp2[i];
|
||||
set_reg((op2 + i) & 0x7f, m_temp2[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -574,17 +579,22 @@ void hcd62121_cpu_device::device_reset()
|
||||
{
|
||||
m_sp = 0x0000;
|
||||
m_ip = 0x0000;
|
||||
m_dsize = 0;
|
||||
m_cseg = 0;
|
||||
m_dseg = 0;
|
||||
m_sseg = 0;
|
||||
m_lar = 0;
|
||||
m_f = 0;
|
||||
m_time = 0;
|
||||
m_time_op = 0;
|
||||
m_unk_f5 = 0;
|
||||
m_cycles_until_timeout = 0;
|
||||
m_is_timer_started = false;
|
||||
m_is_timer_irq_enabled = false;
|
||||
m_is_timer_irq_asserted = false;
|
||||
m_is_timer_wait_elapsed = true;
|
||||
m_is_infinite_timeout = false;
|
||||
m_dsize = 0;
|
||||
m_timer_cycles = 0;
|
||||
m_lar = 0;
|
||||
m_opt = 0;
|
||||
m_port = 0;
|
||||
|
||||
@ -869,15 +879,29 @@ inline void hcd62121_cpu_device::op_sub(int size)
|
||||
}
|
||||
|
||||
|
||||
inline void hcd62121_cpu_device::op_pushb(u8 source)
|
||||
{
|
||||
m_program->write_byte((m_sseg << 16) | m_sp, source);
|
||||
m_sp--;
|
||||
}
|
||||
|
||||
|
||||
inline void hcd62121_cpu_device::op_pushw(u16 source)
|
||||
{
|
||||
m_program->write_byte(( m_sseg << 16) | m_sp, source & 0xff);
|
||||
m_program->write_byte((m_sseg << 16) | m_sp, source & 0xff);
|
||||
m_sp--;
|
||||
m_program->write_byte(( m_sseg << 16) | m_sp, source >> 8);
|
||||
m_program->write_byte((m_sseg << 16) | m_sp, source >> 8);
|
||||
m_sp--;
|
||||
}
|
||||
|
||||
|
||||
inline u8 hcd62121_cpu_device::op_popb()
|
||||
{
|
||||
m_sp++;
|
||||
return m_program->read_byte((m_sseg << 16) | m_sp);
|
||||
}
|
||||
|
||||
|
||||
inline u16 hcd62121_cpu_device::op_popw()
|
||||
{
|
||||
m_sp++;
|
||||
@ -888,14 +912,29 @@ inline u16 hcd62121_cpu_device::op_popw()
|
||||
return res;
|
||||
}
|
||||
|
||||
void hcd62121_cpu_device::timer_elapsed()
|
||||
{
|
||||
m_is_timer_wait_elapsed = true;
|
||||
|
||||
if (m_is_timer_irq_enabled)
|
||||
{
|
||||
m_is_timer_irq_asserted = true;
|
||||
|
||||
// Call timer interrupt vector.
|
||||
op_pushb(m_cseg);
|
||||
op_pushw(m_ip);
|
||||
m_cseg = 0x00;
|
||||
m_ip = 0x0008;
|
||||
}
|
||||
}
|
||||
|
||||
void hcd62121_cpu_device::execute_run()
|
||||
{
|
||||
do
|
||||
{
|
||||
if (m_ki_cb() != 0)
|
||||
if (m_ki_cb() != 0 && m_input_flag_cb() == 0)
|
||||
{
|
||||
m_cycles_until_timeout = 0;
|
||||
m_is_timer_wait_elapsed = true;
|
||||
m_is_infinite_timeout = false;
|
||||
}
|
||||
|
||||
@ -904,15 +943,26 @@ void hcd62121_cpu_device::execute_run()
|
||||
set_input_flag(false);
|
||||
}
|
||||
|
||||
if (m_is_infinite_timeout)
|
||||
if (m_is_infinite_timeout && !m_is_timer_wait_elapsed)
|
||||
{
|
||||
m_icount = 0;
|
||||
}
|
||||
else if (m_cycles_until_timeout > 0)
|
||||
else if (m_cycles_until_timeout > 0 && !m_is_timer_irq_asserted)
|
||||
{
|
||||
int cycles_to_consume = std::min(m_cycles_until_timeout, m_icount);
|
||||
m_cycles_until_timeout -= cycles_to_consume;
|
||||
m_icount -= cycles_to_consume;
|
||||
|
||||
if (!m_is_timer_wait_elapsed)
|
||||
{
|
||||
// It's a blocking wait, consume as much as possible in this quantum.
|
||||
m_icount -= cycles_to_consume;
|
||||
}
|
||||
|
||||
if (m_cycles_until_timeout <= 0)
|
||||
{
|
||||
m_cycles_until_timeout += m_timer_cycles;
|
||||
timer_elapsed();
|
||||
}
|
||||
}
|
||||
if (m_icount <= 0)
|
||||
{
|
||||
@ -1656,7 +1706,22 @@ void hcd62121_cpu_device::execute_run()
|
||||
break;
|
||||
|
||||
case 0x8E: /* unk_8E */
|
||||
logerror("%02x:%04x: unimplemented instruction %02x encountered\n", m_cseg, m_ip-1, op);
|
||||
{
|
||||
logerror("%02x:%04x: unimplemented instruction %02x encountered\n", m_cseg, m_ip-1, op);
|
||||
if (m_unk_f5 == 0xd0)
|
||||
{
|
||||
/*
|
||||
FIXME: Needs to be validated with hardware tests.
|
||||
|
||||
Instruction 0x8E is called before reading and testing inputs in some cases.
|
||||
While the timer is also configured, there's no blocking wait for it to expire.
|
||||
Instead, there's a busy wait on 2 bytes at register R76 that aren't updated directly by
|
||||
program code. So far only seen when instruction 0xF5 is executed with operand 0xD0, but
|
||||
maybe irrelevant, see JD-364 @ 21:b65f.
|
||||
*/
|
||||
m_is_timer_irq_enabled = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x90: /* retzh */
|
||||
@ -1685,6 +1750,12 @@ void hcd62121_cpu_device::execute_run()
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x9E: /* reti */
|
||||
m_ip = op_popw();
|
||||
m_cseg = op_popb();
|
||||
m_is_timer_irq_asserted = false;
|
||||
break;
|
||||
|
||||
case 0x9F: /* ret */
|
||||
m_ip = op_popw();
|
||||
break;
|
||||
@ -1738,7 +1809,72 @@ void hcd62121_cpu_device::execute_run()
|
||||
{
|
||||
u8 arg = read_op();
|
||||
|
||||
/*
|
||||
When timer control is set with operand 0xC0, the CPU periodically reads
|
||||
from external RAM (address range 0x7c00..0x7fff) at an interval of
|
||||
832 clock cycles, with the reads themselves taking another 64 clock cycles.
|
||||
This needs to be explicitly setup, involving writes to unknown segments
|
||||
0x11 and 0xE1 (see cfx9850.bin @ 00:00fe), otherwise there's only activity
|
||||
on address lines without any reads.
|
||||
|
||||
The total sum of these state reads can be approximated to the closest
|
||||
power of two to define timeout values. Multiple samples were averaged,
|
||||
as the state read interval can start at a distinct point in time from
|
||||
the timer wait execution.
|
||||
*/
|
||||
const u64 TIMER_STATE_READ_CYCLES = 832 + 64;
|
||||
m_is_infinite_timeout = false;
|
||||
m_is_timer_wait_elapsed = true;
|
||||
m_cycles_until_timeout = m_timer_cycles = 0;
|
||||
m_time_op = arg;
|
||||
switch (m_time_op)
|
||||
{
|
||||
case 0x01: case 0x03:
|
||||
// Likely only timeouts on KO enabled input.
|
||||
m_is_infinite_timeout = true;
|
||||
break;
|
||||
case 0x11: case 0x13: case 0x15: case 0x17: case 0x19: case 0x1b: case 0x1d: case 0x1f:
|
||||
case 0x31: case 0x33: case 0x35: case 0x37: case 0x39: case 0x3b: case 0x3d: case 0x3f:
|
||||
case 0x51: case 0x53: case 0x55: case 0x57: case 0x59: case 0x5b: case 0x5d: case 0x5f:
|
||||
case 0x71: case 0x73: case 0x75: case 0x77: case 0x79: case 0x7b: case 0x7d: case 0x7f:
|
||||
case 0x91: case 0x93: case 0x95: case 0x97: case 0x99: case 0x9b: case 0x9d: case 0x9f:
|
||||
case 0xb1: case 0xb3: case 0xb5: case 0xb7: case 0xb9: case 0xbb: case 0xbd: case 0xbf:
|
||||
case 0xd1: case 0xd3: case 0xd5: case 0xd7: case 0xd9: case 0xdb: case 0xdd: case 0xdf:
|
||||
// Approximately 814.32us
|
||||
m_timer_cycles = 0x4 * TIMER_STATE_READ_CYCLES;
|
||||
break;
|
||||
case 0x21: case 0x23: case 0x25: case 0x27: case 0x29: case 0x2b: case 0x2d: case 0x2f:
|
||||
case 0x61: case 0x63: case 0x65: case 0x67: case 0x69: case 0x6b: case 0x6d: case 0x6f:
|
||||
case 0xa1: case 0xa3: case 0xa5: case 0xa7: case 0xa9: case 0xab: case 0xad: case 0xaf:
|
||||
// Approximately 1.63ms
|
||||
m_timer_cycles = 0x8 * TIMER_STATE_READ_CYCLES;
|
||||
break;
|
||||
case 0x05: case 0x07: case 0x0d: case 0x0f:
|
||||
case 0x45: case 0x47: case 0x4d: case 0x4f:
|
||||
case 0xc5: case 0xc7: case 0xcd: case 0xcf:
|
||||
// Approximately 209.34ms
|
||||
m_timer_cycles = 0x400 * TIMER_STATE_READ_CYCLES;
|
||||
break;
|
||||
case 0x09: case 0x0b:
|
||||
case 0x49: case 0x4b:
|
||||
case 0xc9: case 0xcb:
|
||||
// Approximately 837.61ms
|
||||
m_timer_cycles = 0x1000 * TIMER_STATE_READ_CYCLES;
|
||||
break;
|
||||
case 0x41: case 0x43:
|
||||
case 0xc1: case 0xc3:
|
||||
// Approximately 1.68s
|
||||
m_timer_cycles = 0x2000 * TIMER_STATE_READ_CYCLES;
|
||||
break;
|
||||
case 0x81: case 0x83:
|
||||
// Approximately 100.58s
|
||||
m_timer_cycles = 0x7a800 * TIMER_STATE_READ_CYCLES;
|
||||
break;
|
||||
default:
|
||||
logerror("%02x:%04x: unimplemented timer value %02x encountered\n", m_cseg, m_ip-1, m_time_op);
|
||||
break;
|
||||
}
|
||||
m_cycles_until_timeout = m_timer_cycles;
|
||||
m_is_timer_started = true;
|
||||
}
|
||||
break;
|
||||
@ -1804,7 +1940,7 @@ void hcd62121_cpu_device::execute_run()
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
m_reg[(reg + i) & 0x7f] = read_op();
|
||||
set_reg((reg + i) & 0x7f, read_op());
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1848,7 +1984,18 @@ void hcd62121_cpu_device::execute_run()
|
||||
m_lar += pre_inc;
|
||||
if (arg1 & 0x80)
|
||||
{
|
||||
/*
|
||||
FIXME: Needs to be validated with hardware tests.
|
||||
|
||||
JD-368 @ 20:047a must write value 0x7f @ 40:1819, despite 0x7f7f being
|
||||
expected @ 20:3492. This routine is used to clear RAM, and will overflow
|
||||
if 40:1818 = 0xff7f7f, since it will loop beyond the expected 0x8000 size
|
||||
to clear.
|
||||
|
||||
Does this instruction zero-extend immediate values?
|
||||
*/
|
||||
m_program->write_byte((m_dseg << 16) | m_lar, arg2);
|
||||
arg2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1863,7 +2010,7 @@ void hcd62121_cpu_device::execute_run()
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
m_lar += pre_inc;
|
||||
m_reg[(arg2 + i) & 0x7f] = m_program->read_byte((m_dseg << 16) | m_lar);
|
||||
set_reg((arg2 + i) & 0x7f, m_program->read_byte((m_dseg << 16) | m_lar));
|
||||
m_lar += post_inc;
|
||||
}
|
||||
}
|
||||
@ -1970,16 +2117,16 @@ void hcd62121_cpu_device::execute_run()
|
||||
logerror("%06x: in0 read\n", (m_cseg << 16) | m_ip);
|
||||
u8 reg1 = read_op();
|
||||
|
||||
m_reg[reg1 & 0x7f] = m_in0_cb();
|
||||
set_reg(reg1 & 0x7f, m_in0_cb());
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xE1: /* movb reg,OPT */
|
||||
m_reg[read_op() & 0x7f] = m_opt;
|
||||
set_reg(read_op() & 0x7f, m_opt);
|
||||
break;
|
||||
|
||||
case 0xE2: /* in kb, reg */
|
||||
m_reg[read_op() & 0x7f] = m_ki_cb();
|
||||
set_reg(read_op() & 0x7f, m_ki_cb());
|
||||
break;
|
||||
|
||||
case 0xE3: /* movb reg,dsize */
|
||||
@ -1987,7 +2134,7 @@ void hcd62121_cpu_device::execute_run()
|
||||
u8 reg = read_op();
|
||||
if (reg & 0x80)
|
||||
fatalerror("%02x:%04x: unimplemented instruction %02x encountered with (arg & 0x80) != 0\n", m_cseg, m_ip-1, op);
|
||||
m_reg[reg & 0x7f] = m_dsize;
|
||||
set_reg(reg & 0x7f, m_dsize);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1996,24 +2143,24 @@ void hcd62121_cpu_device::execute_run()
|
||||
u8 reg = read_op();
|
||||
if (reg & 0x80)
|
||||
fatalerror("%02x:%04x: unimplemented instruction %02x encountered with (arg & 0x80) != 0\n", m_cseg, m_ip-1, op);
|
||||
m_reg[reg & 0x7f] = m_f;
|
||||
set_reg(reg & 0x7f, m_f);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xE5: /* movb reg,TIME */
|
||||
m_reg[read_op() & 0x7f] = m_time;
|
||||
set_reg(read_op() & 0x7f, m_time);
|
||||
break;
|
||||
|
||||
case 0xE6: /* movb reg,PORT */
|
||||
m_reg[read_op() & 0x7f] = m_port;
|
||||
set_reg(read_op() & 0x7f, m_port);
|
||||
break;
|
||||
|
||||
case 0xE8: /* movw r1,lar */
|
||||
{
|
||||
u8 reg1 = read_op();
|
||||
|
||||
m_reg[reg1 & 0x7f] = m_lar & 0xff;
|
||||
m_reg[(reg1 + 1) & 0x7f] = m_lar >> 8;
|
||||
set_reg(reg1 & 0x7f, m_lar & 0xff);
|
||||
set_reg((reg1 + 1) & 0x7f, m_lar >> 8);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -2021,8 +2168,8 @@ void hcd62121_cpu_device::execute_run()
|
||||
{
|
||||
u8 reg1 = read_op();
|
||||
|
||||
m_reg[reg1 & 0x7f] = m_ip & 0xff;
|
||||
m_reg[(reg1 + 1) & 0x7f] = m_ip >> 8;
|
||||
set_reg(reg1 & 0x7f, m_ip & 0xff);
|
||||
set_reg((reg1 + 1) & 0x7f, m_ip >> 8);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -2030,21 +2177,21 @@ void hcd62121_cpu_device::execute_run()
|
||||
{
|
||||
u8 reg1 = read_op();
|
||||
|
||||
m_reg[reg1 & 0x7f] = m_sp & 0xff;
|
||||
m_reg[(reg1 + 1) & 0x7f] = m_sp >> 8;
|
||||
set_reg(reg1 & 0x7f, m_sp & 0xff);
|
||||
set_reg((reg1 + 1) & 0x7f, m_sp >> 8);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xED: /* movb reg,ds */
|
||||
m_reg[read_op() & 0x7f] = m_dseg;
|
||||
set_reg(read_op() & 0x7f, m_dseg);
|
||||
break;
|
||||
|
||||
case 0xEE: /* movb reg,cs */
|
||||
m_reg[read_op() & 0x7f] = m_cseg;
|
||||
set_reg(read_op() & 0x7f, m_cseg);
|
||||
break;
|
||||
|
||||
case 0xEF: /* movb reg,ss */
|
||||
m_reg[read_op() & 0x7f] = m_sseg;
|
||||
set_reg(read_op() & 0x7f, m_sseg);
|
||||
break;
|
||||
|
||||
case 0xF0: /* movb OPT,reg */
|
||||
@ -2057,170 +2204,33 @@ void hcd62121_cpu_device::execute_run()
|
||||
m_port_cb(m_port);
|
||||
break;
|
||||
|
||||
case 0xF5: /* unk_F5 reg/i8 (out?) */
|
||||
logerror("%02x:%04x: unimplemented instruction %02x encountered\n", m_cseg, m_ip-1, op);
|
||||
m_unk_f5 = read_op();
|
||||
break;
|
||||
|
||||
case 0xF1: /* unk_F1 reg/i8 (out?) */
|
||||
case 0xF3: /* unk_F3 reg/i8 (out?) */
|
||||
case 0xF5: /* unk_F5 reg/i8 (out?) */
|
||||
case 0xF6: /* unk_F6 reg/i8 (out?) */
|
||||
case 0xF7: /* timer_ctrl i8 */
|
||||
case 0xF8: /* unk_F8 reg/i8 (out?) */
|
||||
logerror("%02x:%04x: unimplemented instruction %02x encountered\n", m_cseg, m_ip-1, op);
|
||||
read_op();
|
||||
break;
|
||||
|
||||
case 0xFC: /* unk_FC - disable interrupts/stop timer?? */
|
||||
logerror("%02x:%04x: unimplemented instruction %02x encountered\n", m_cseg, m_ip-1, op);
|
||||
case 0xFC: /* timer_clear */
|
||||
// FIXME: Needs to be validated with hardware tests.
|
||||
m_cycles_until_timeout = m_timer_cycles = 0;
|
||||
m_is_timer_irq_enabled = false;
|
||||
m_is_timer_irq_asserted = false;
|
||||
m_is_timer_wait_elapsed = true;
|
||||
break;
|
||||
|
||||
case 0xFD: /* timer_wait_low (no X1 clock, address or data bus activity) */
|
||||
case 0xFE: /* timer_wait */
|
||||
if (m_time_op & 0x01)
|
||||
if (BIT(m_time_op, 0))
|
||||
{
|
||||
/*
|
||||
When timer control is set with operand 0xC0, the CPU periodically reads
|
||||
from external RAM (address range 0x7c00..0x7fff) at an interval of
|
||||
832 clock cycles, with the reads themselves taking another 64 clock cycles.
|
||||
This needs to be explicitly setup, involving writes to unknown segments
|
||||
0x11 and 0xE1 (see cfx9850.bin @ 00:00fe), otherwise there's only activity
|
||||
on address lines without any reads.
|
||||
|
||||
The total sum of these state reads can be approximated to the closest
|
||||
power of two to define timeout values. Multiple samples were averaged,
|
||||
as the state read interval can start at a distinct point in time from
|
||||
the timer wait execution.
|
||||
*/
|
||||
const u64 TIMER_STATE_READ_CYCLES = 832 + 64;
|
||||
switch (m_time_op)
|
||||
{
|
||||
case 0x01:
|
||||
case 0x03:
|
||||
// Likely only timeouts on KO enabled input.
|
||||
m_is_infinite_timeout = true;
|
||||
break;
|
||||
case 0x11:
|
||||
case 0x13:
|
||||
case 0x15:
|
||||
case 0x17:
|
||||
case 0x19:
|
||||
case 0x1b:
|
||||
case 0x1d:
|
||||
case 0x1f:
|
||||
case 0x31:
|
||||
case 0x33:
|
||||
case 0x35:
|
||||
case 0x37:
|
||||
case 0x39:
|
||||
case 0x3b:
|
||||
case 0x3d:
|
||||
case 0x3f:
|
||||
case 0x51:
|
||||
case 0x53:
|
||||
case 0x55:
|
||||
case 0x57:
|
||||
case 0x59:
|
||||
case 0x5b:
|
||||
case 0x5d:
|
||||
case 0x5f:
|
||||
case 0x71:
|
||||
case 0x73:
|
||||
case 0x75:
|
||||
case 0x77:
|
||||
case 0x79:
|
||||
case 0x7b:
|
||||
case 0x7d:
|
||||
case 0x7f:
|
||||
case 0x91:
|
||||
case 0x93:
|
||||
case 0x95:
|
||||
case 0x97:
|
||||
case 0x99:
|
||||
case 0x9b:
|
||||
case 0x9d:
|
||||
case 0x9f:
|
||||
case 0xb1:
|
||||
case 0xb3:
|
||||
case 0xb5:
|
||||
case 0xb7:
|
||||
case 0xb9:
|
||||
case 0xbb:
|
||||
case 0xbd:
|
||||
case 0xbf:
|
||||
case 0xd1:
|
||||
case 0xd3:
|
||||
case 0xd5:
|
||||
case 0xd7:
|
||||
case 0xd9:
|
||||
case 0xdb:
|
||||
case 0xdd:
|
||||
case 0xdf:
|
||||
// Approximately 814.32us
|
||||
m_cycles_until_timeout = 0x4 * TIMER_STATE_READ_CYCLES;
|
||||
break;
|
||||
case 0x21:
|
||||
case 0x23:
|
||||
case 0x25:
|
||||
case 0x27:
|
||||
case 0x29:
|
||||
case 0x2b:
|
||||
case 0x2d:
|
||||
case 0x2f:
|
||||
case 0x61:
|
||||
case 0x63:
|
||||
case 0x65:
|
||||
case 0x67:
|
||||
case 0x69:
|
||||
case 0x6b:
|
||||
case 0x6d:
|
||||
case 0x6f:
|
||||
case 0xa1:
|
||||
case 0xa3:
|
||||
case 0xa5:
|
||||
case 0xa7:
|
||||
case 0xa9:
|
||||
case 0xab:
|
||||
case 0xad:
|
||||
case 0xaf:
|
||||
// Approximately 1.63ms
|
||||
m_cycles_until_timeout = 0x8 * TIMER_STATE_READ_CYCLES;
|
||||
break;
|
||||
case 0x05:
|
||||
case 0x07:
|
||||
case 0x0d:
|
||||
case 0x0f:
|
||||
case 0x45:
|
||||
case 0x47:
|
||||
case 0x4d:
|
||||
case 0x4f:
|
||||
case 0xc5:
|
||||
case 0xc7:
|
||||
case 0xcd:
|
||||
case 0xcf:
|
||||
// Approximately 209.34ms
|
||||
m_cycles_until_timeout = 0x400 * TIMER_STATE_READ_CYCLES;
|
||||
break;
|
||||
case 0x09:
|
||||
case 0x0b:
|
||||
case 0x49:
|
||||
case 0x4b:
|
||||
case 0xc9:
|
||||
case 0xcb:
|
||||
// Approximately 837.61ms
|
||||
m_cycles_until_timeout = 0x1000 * TIMER_STATE_READ_CYCLES;
|
||||
break;
|
||||
case 0x41:
|
||||
case 0x43:
|
||||
case 0xc1:
|
||||
case 0xc3:
|
||||
// Approximately 1.68s
|
||||
m_cycles_until_timeout = 0x2000 * TIMER_STATE_READ_CYCLES;
|
||||
break;
|
||||
case 0x81:
|
||||
case 0x83:
|
||||
// Approximately 100.58s
|
||||
m_cycles_until_timeout = 0x7a800 * TIMER_STATE_READ_CYCLES;
|
||||
break;
|
||||
default:
|
||||
logerror("%02x:%04x: unimplemented timer value %02x encountered\n", m_cseg, m_ip-1, m_time_op);
|
||||
break;
|
||||
}
|
||||
m_is_timer_wait_elapsed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -83,8 +83,12 @@ private:
|
||||
void op_addb(int size);
|
||||
void op_subb(int size);
|
||||
void op_sub(int size);
|
||||
void op_pushb(u8 source);
|
||||
void op_pushw(u16 source);
|
||||
u8 op_popb();
|
||||
u16 op_popw();
|
||||
void set_reg(u8 i, u8 val) { m_reg[i] = val; /*logerror("R%02X = %02x @ %06X\n", i, val, (m_cseg << 16) | m_ip);*/ }
|
||||
void timer_elapsed();
|
||||
|
||||
address_space_config m_program_config;
|
||||
|
||||
@ -98,9 +102,14 @@ private:
|
||||
u8 m_f;
|
||||
u8 m_time;
|
||||
u8 m_time_op;
|
||||
u8 m_unk_f5;
|
||||
s32 m_cycles_until_timeout;
|
||||
bool m_is_timer_started;
|
||||
bool m_is_timer_irq_enabled;
|
||||
bool m_is_timer_irq_asserted;
|
||||
bool m_is_timer_wait_elapsed;
|
||||
bool m_is_infinite_timeout;
|
||||
s32 m_timer_cycles;
|
||||
emu_timer *m_timer;
|
||||
u16 m_lar;
|
||||
u8 m_reg[0x80];
|
||||
|
@ -157,14 +157,14 @@ const hcd62121_disassembler::dasm hcd62121_disassembler::ops[256] =
|
||||
{ "movb", ARG_REG, ARG_CS }, { "movb", ARG_REG, ARG_SS },
|
||||
|
||||
/* 0xf0 */
|
||||
{ "movb", ARG_OPT, ARG_REG }, { "unF1?", ARG_I8, ARG_NONE },
|
||||
{ "movb", ARG_PORT, ARG_REG }, { "unF3?", ARG_I8, ARG_NONE },
|
||||
{ "unF4?", ARG_I8, ARG_NONE }, { "unF5?", ARG_I8, ARG_NONE },
|
||||
{ "unF6?", ARG_I8, ARG_NONE }, { "timer_ctrl", ARG_I8, ARG_NONE },
|
||||
{ "unF8?", ARG_NONE, ARG_NONE }, { "unF9?", ARG_NONE, ARG_NONE },
|
||||
{ "unFA?", ARG_NONE, ARG_NONE }, { "unFb?", ARG_NONE, ARG_NONE },
|
||||
{ "unFC?", ARG_NONE, ARG_NONE }, { "timer_wait_low", ARG_NONE, ARG_NONE },
|
||||
{ "timer_wait", ARG_NONE, ARG_NONE }, { "nop", ARG_NONE, ARG_NONE }
|
||||
{ "movb", ARG_OPT, ARG_REG }, { "unF1?", ARG_I8, ARG_NONE },
|
||||
{ "movb", ARG_PORT, ARG_REG }, { "unF3?", ARG_I8, ARG_NONE },
|
||||
{ "unF4?", ARG_I8, ARG_NONE }, { "unF5?", ARG_I8, ARG_NONE },
|
||||
{ "unF6?", ARG_I8, ARG_NONE }, { "timer_ctrl", ARG_I8, ARG_NONE },
|
||||
{ "unF8?", ARG_I8, ARG_NONE }, { "unF9?", ARG_NONE, ARG_NONE },
|
||||
{ "unFA?", ARG_NONE, ARG_NONE }, { "unFb?", ARG_NONE, ARG_NONE },
|
||||
{ "timer_clear", ARG_NONE, ARG_NONE }, { "timer_wait_low", ARG_NONE, ARG_NONE },
|
||||
{ "timer_wait", ARG_NONE, ARG_NONE }, { "nop", ARG_NONE, ARG_NONE }
|
||||
};
|
||||
|
||||
u32 hcd62121_disassembler::opcode_alignment() const
|
||||
|
@ -2,19 +2,16 @@
|
||||
// copyright-holders:QUFB
|
||||
/***************************************************************************
|
||||
|
||||
Driver for Casio Picky Talk
|
||||
Driver for Casio Picky Talk and Casio Plet's
|
||||
|
||||
TODO:
|
||||
|
||||
- Communication port;
|
||||
- Panel active buttons display;
|
||||
- Review PORT/OPT callbacks copied from CFX9850G;
|
||||
|
||||
Some points of interest can be accessed under the debugger:
|
||||
|
||||
1. bpset 0x200328
|
||||
2. ip=3fe (clock screen)
|
||||
3. ip=410 (main screen)
|
||||
- Fix unk_F8 loop when viewing calendar in JD-363/JD-364 models;
|
||||
- Fix busy loop @ 20:2981 in Plet's models;
|
||||
- Keyboard input for Plet's models;
|
||||
|
||||
Hardware
|
||||
--------
|
||||
@ -26,6 +23,25 @@
|
||||
- LSI3 (Static RAM): NEC D441000LGZ (1M-bit, 128K-word by 8-bit)
|
||||
- LSI5 (Mask ROM): NEC D23C8000XGX-C64 (8M-bit, 1M-word by 8-bit, pin compatible with AMD AM29F800B)
|
||||
|
||||
Plet's (MK-300):
|
||||
|
||||
- PCB revision: A141252-1 Z836-1
|
||||
- LSI1 (CPU): Unknown (instruction set compatible with Hitachi HCD62121)
|
||||
- LSI3 (Static RAM): Toshiba TC551001BFL-10V (1M-bit, 128K-word by 8-bit)
|
||||
- LSI5 (Mask ROM): NEC D23C8000XGX-C77 (8M-bit, 1M-word by 8-bit, pin compatible with AMD AM29F800B)
|
||||
|
||||
Plet's (MK-350):
|
||||
|
||||
- PCB revision: A141252-1 Z836-1
|
||||
- LSI1 (CPU): Unknown (instruction set compatible with Hitachi HCD62121)
|
||||
- LSI3 (Static RAM): NEC D441000LGW-B85X
|
||||
- LSI5 (Mask ROM): Oki M538032E-48
|
||||
|
||||
Hidden Features
|
||||
---------------
|
||||
|
||||
[JD-370] On the debugger, run "bpset 20adb2,,{ ip=20ad27; g; }" to access an unused test program.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
@ -46,10 +62,10 @@
|
||||
|
||||
namespace {
|
||||
|
||||
class pickytlk_state : public driver_device
|
||||
class pickytlk_base_state : public driver_device
|
||||
{
|
||||
public:
|
||||
pickytlk_state(const machine_config &mconfig, device_type type, const char *tag)
|
||||
pickytlk_base_state(const machine_config &mconfig, device_type type, const char *tag)
|
||||
: driver_device(mconfig, type, tag)
|
||||
, m_display_ram(*this, "display_ram")
|
||||
, m_maincpu(*this, "maincpu")
|
||||
@ -68,7 +84,7 @@ public:
|
||||
ioport_value pen_y_rescale_r();
|
||||
ioport_value pen_target_r();
|
||||
|
||||
private:
|
||||
protected:
|
||||
enum pen_target : u8
|
||||
{
|
||||
PEN_TARGET_LCD = 0,
|
||||
@ -96,11 +112,11 @@ private:
|
||||
TIMER_CALLBACK_MEMBER(io_timer_tick);
|
||||
u8 io_pen_x_read();
|
||||
u8 io_pen_y_read();
|
||||
u8 tablet_read(offs_t offset);
|
||||
virtual u8 tablet_read(offs_t offset);
|
||||
void tablet_write(offs_t offset, u8 data);
|
||||
|
||||
virtual void pickytlk_layout(machine_config &config);
|
||||
void update_crosshair(screen_device &screen);
|
||||
void pickytlk_palette(palette_device &palette) const;
|
||||
u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
|
||||
|
||||
void pickytlk_mem(address_map &map) ATTR_COLD;
|
||||
@ -128,19 +144,19 @@ private:
|
||||
u8 m_pen_target;
|
||||
};
|
||||
|
||||
void pickytlk_state::machine_start()
|
||||
void pickytlk_base_state::machine_start()
|
||||
{
|
||||
save_item(NAME(m_ko));
|
||||
save_item(NAME(m_port));
|
||||
save_item(NAME(m_opt));
|
||||
m_io_tablet_regs = make_unique_clear<u8[]>(0x100);
|
||||
save_pointer(NAME(m_io_tablet_regs), 0x100);
|
||||
m_io_timer = timer_alloc(FUNC(pickytlk_state::io_timer_tick), this);
|
||||
m_io_timer = timer_alloc(FUNC(pickytlk_base_state::io_timer_tick), this);
|
||||
save_item(NAME(m_pen_state));
|
||||
save_item(NAME(m_pen_target));
|
||||
}
|
||||
|
||||
void pickytlk_state::machine_reset()
|
||||
void pickytlk_base_state::machine_reset()
|
||||
{
|
||||
memset(m_io_tablet_regs.get(), 0, 0x100);
|
||||
m_io_timer->reset(attotime::never);
|
||||
@ -148,14 +164,14 @@ void pickytlk_state::machine_reset()
|
||||
m_pen_target = PEN_TARGET_LCD;
|
||||
}
|
||||
|
||||
CROSSHAIR_MAPPER_MEMBER(pickytlk_state::pen_y_mapper)
|
||||
CROSSHAIR_MAPPER_MEMBER(pickytlk_base_state::pen_y_mapper)
|
||||
{
|
||||
// Parameter `linear_value` is ignored, since we will read the input port directly
|
||||
// for adjustments, just need to return that value in the expected range [0.0f..1.0f].
|
||||
return (float) pen_y_rescale_r() / 0xff;
|
||||
}
|
||||
|
||||
ioport_value pickytlk_state::pen_y_rescale_r()
|
||||
ioport_value pickytlk_base_state::pen_y_rescale_r()
|
||||
{
|
||||
/*
|
||||
There are two distinct areas that can be interacted with the pen:
|
||||
@ -182,25 +198,12 @@ ioport_value pickytlk_state::pen_y_rescale_r()
|
||||
return adjusted_value;
|
||||
}
|
||||
|
||||
ioport_value pickytlk_state::pen_target_r()
|
||||
ioport_value pickytlk_base_state::pen_target_r()
|
||||
{
|
||||
return m_pen_target;
|
||||
}
|
||||
|
||||
void pickytlk_state::pickytlk_mem(address_map &map)
|
||||
{
|
||||
map(0x000000, 0x007fff).mirror(0x008000).rom();
|
||||
map(0x080000, 0x0807ff).ram();
|
||||
map(0x080300, 0x08030f).rw(FUNC(pickytlk_state::tablet_read), FUNC(pickytlk_state::tablet_write));
|
||||
// map(0x100000, 0x10ffff) // some memory mapped i/o???
|
||||
// map(0x110000, 0x11ffff) // some memory mapped i/o???
|
||||
map(0x200000, 0x2fffff).rom().region("mask_rom", 0);
|
||||
map(0x400000, 0x4007ff).ram().share("display_ram");
|
||||
map(0x400800, 0x41ffff).ram();
|
||||
// map(0xe10000, 0xe1ffff) // some memory mapped i/o???
|
||||
}
|
||||
|
||||
TIMER_CALLBACK_MEMBER(pickytlk_state::io_timer_tick)
|
||||
TIMER_CALLBACK_MEMBER(pickytlk_base_state::io_timer_tick)
|
||||
{
|
||||
if (m_pen_state == PEN_PRESS)
|
||||
{
|
||||
@ -208,7 +211,7 @@ TIMER_CALLBACK_MEMBER(pickytlk_state::io_timer_tick)
|
||||
}
|
||||
}
|
||||
|
||||
u8 pickytlk_state::io_pen_x_read()
|
||||
u8 pickytlk_base_state::io_pen_x_read()
|
||||
{
|
||||
// Pen callibration tests seem to check coordinates relative to the center of the LCD screen,
|
||||
// and those offsets also align with the LCD position relative to the full tablet surface.
|
||||
@ -218,7 +221,7 @@ u8 pickytlk_state::io_pen_x_read()
|
||||
return rescale(io_pen_x_pos, io_pen_x_min, io_pen_x_max, 0x20, 0xdf);
|
||||
}
|
||||
|
||||
u8 pickytlk_state::io_pen_y_read()
|
||||
u8 pickytlk_base_state::io_pen_y_read()
|
||||
{
|
||||
s16 io_pen_y_min = m_io_pen_y->field(0xff)->minval();
|
||||
s16 io_pen_y_max = m_io_pen_y->field(0xff)->maxval();
|
||||
@ -228,7 +231,260 @@ u8 pickytlk_state::io_pen_y_read()
|
||||
: rescale(io_pen_y_pos, io_pen_y_min, io_pen_y_max, 0xa0, 0xf0);
|
||||
}
|
||||
|
||||
u8 pickytlk_state::tablet_read(offs_t offset)
|
||||
u8 pickytlk_base_state::tablet_read(offs_t offset)
|
||||
{
|
||||
// Default (overriden by each system)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pickytlk_base_state::tablet_write(offs_t offset, u8 data)
|
||||
{
|
||||
LOGMASKED(LOG_TABLET, "%s: tablet_write [%02x] = %02x\n", machine().describe_context(), offset, data);
|
||||
m_io_tablet_regs[offset] = data;
|
||||
}
|
||||
|
||||
void pickytlk_base_state::kol_w(u8 data)
|
||||
{
|
||||
m_ko = (m_ko & 0xff00) | data;
|
||||
LOGMASKED(LOG_IO, "%s: KO = %04x\n", machine().describe_context(), m_ko);
|
||||
}
|
||||
|
||||
void pickytlk_base_state::koh_w(u8 data)
|
||||
{
|
||||
m_ko = (m_ko & 0x00ff) | (u16(data) << 8);
|
||||
LOGMASKED(LOG_IO, "%s: KO = %04x\n", machine().describe_context(), m_ko);
|
||||
}
|
||||
|
||||
void pickytlk_base_state::port_w(u8 data)
|
||||
{
|
||||
m_port = data;
|
||||
LOGMASKED(LOG_IO, "%s: PORT = %02x\n", machine().describe_context(), m_port);
|
||||
}
|
||||
|
||||
void pickytlk_base_state::opt_w(u8 data)
|
||||
{
|
||||
m_opt = data;
|
||||
LOGMASKED(LOG_IO, "%s: OPT = %02x\n", machine().describe_context(), m_opt);
|
||||
}
|
||||
|
||||
u8 pickytlk_base_state::ki_r()
|
||||
{
|
||||
if (BIT(m_io_buttons->read(), 6))
|
||||
{
|
||||
if (m_pen_state == PEN_RELEASE)
|
||||
{
|
||||
m_pen_state = PEN_PRESS;
|
||||
// FIXME: Adjust delay when more accurate instruction timings are implemented.
|
||||
// Program code waits for input flag to be stable by executing `mov DSIZE,0xff`
|
||||
// then `movq R00,R00` 15 times (see pickytlk ROM @ 2015f6).
|
||||
m_io_timer->adjust(attotime::from_msec(20), 0, attotime::never);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pen_state = PEN_RELEASE;
|
||||
m_io_timer->reset(attotime::never);
|
||||
}
|
||||
|
||||
return m_pen_state == PEN_PRESS ? 0x80 : 0;
|
||||
}
|
||||
|
||||
u8 pickytlk_base_state::in0_r()
|
||||
{
|
||||
// battery level?
|
||||
// bit4 -> if reset CPU keeps restarting (several unknown instructions before jumping to 0)
|
||||
// perhaps a battery present check?
|
||||
// bit 5 -> 0 = low battery
|
||||
|
||||
// --XX ---- VDET
|
||||
// ---- -X-- data-in
|
||||
return 0x30 & ~0x00;
|
||||
}
|
||||
|
||||
u8 pickytlk_base_state::input_flag_read()
|
||||
{
|
||||
return m_pen_state == PEN_PRESS || m_pen_state == PEN_HOLD ? 0 : 1;
|
||||
}
|
||||
|
||||
void pickytlk_base_state::pickytlk_layout(machine_config &config)
|
||||
{
|
||||
// Nothing (overriden by each system)
|
||||
}
|
||||
|
||||
void pickytlk_base_state::update_crosshair(screen_device &screen)
|
||||
{
|
||||
// Either screen crosshair or layout view's cursor should be visible at a time.
|
||||
machine().crosshair().get_crosshair(0).set_screen(m_pen_target ? CROSSHAIR_SCREEN_NONE : &screen);
|
||||
}
|
||||
|
||||
u32 pickytlk_base_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
|
||||
{
|
||||
update_crosshair(screen);
|
||||
|
||||
u16 offset = 0;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
int const x = i * 8;
|
||||
|
||||
for (int j = 0; j < 64; j++)
|
||||
{
|
||||
u16 *const row = &bitmap.pix(63 - j);
|
||||
|
||||
u8 const data1 = m_display_ram[offset];
|
||||
u8 const data2 = m_display_ram[offset + 0x400];
|
||||
|
||||
for (int b = 0; b < 8; b++)
|
||||
{
|
||||
if (x + b < 127)
|
||||
{
|
||||
row[x + b] = (BIT(data1, b) << 1) | BIT(data2, b);
|
||||
}
|
||||
}
|
||||
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static INPUT_PORTS_START(pickytlk)
|
||||
// TODO: On/Off/Reset
|
||||
PORT_START("BUTTONS")
|
||||
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Pen Down")
|
||||
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(pickytlk_base_state::pen_target_r))
|
||||
|
||||
PORT_START("PEN_X")
|
||||
PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("Pen X")
|
||||
|
||||
PORT_START("PEN_Y")
|
||||
PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("Pen Y") PORT_CROSSHAIR_MAPPER_MEMBER(FUNC(pickytlk_base_state::pen_y_mapper))
|
||||
|
||||
PORT_START("PEN_Y_RESCALE")
|
||||
PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(pickytlk_base_state::pen_y_rescale_r))
|
||||
INPUT_PORTS_END
|
||||
|
||||
|
||||
void pickytlk_base_state::pickytlk(machine_config &config)
|
||||
{
|
||||
HCD62121(config, m_maincpu, 4300000); /* X1 - 4.3 MHz */
|
||||
m_maincpu->kol_cb().set(FUNC(pickytlk_base_state::kol_w));
|
||||
m_maincpu->koh_cb().set(FUNC(pickytlk_base_state::koh_w));
|
||||
m_maincpu->port_cb().set(FUNC(pickytlk_base_state::port_w));
|
||||
m_maincpu->opt_cb().set(FUNC(pickytlk_base_state::opt_w));
|
||||
m_maincpu->ki_cb().set(FUNC(pickytlk_base_state::ki_r));
|
||||
m_maincpu->in0_cb().set(FUNC(pickytlk_base_state::in0_r));
|
||||
m_maincpu->input_flag_cb().set(FUNC(pickytlk_base_state::input_flag_read));
|
||||
|
||||
screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
|
||||
screen.set_refresh_hz(60);
|
||||
screen.set_size(127, 64);
|
||||
screen.set_visarea(0, 126, 0, 63);
|
||||
screen.set_screen_update(FUNC(pickytlk_base_state::screen_update));
|
||||
screen.set_palette("palette");
|
||||
|
||||
pickytlk_layout(config);
|
||||
}
|
||||
|
||||
|
||||
class pickytlk_monocolor_state : public pickytlk_base_state
|
||||
{
|
||||
public:
|
||||
pickytlk_monocolor_state(const machine_config &mconfig, device_type type, const char *tag)
|
||||
: pickytlk_base_state(mconfig, type, tag)
|
||||
{ }
|
||||
|
||||
void pickytlk_monocolor(machine_config &config);
|
||||
|
||||
private:
|
||||
virtual u8 tablet_read(offs_t offset) override;
|
||||
virtual void pickytlk_layout(machine_config &config) override;
|
||||
void pickytlk_mem(address_map &map);
|
||||
void pickytlk_palette(palette_device &palette) const;
|
||||
};
|
||||
|
||||
void pickytlk_monocolor_state::pickytlk_layout(machine_config &config)
|
||||
{
|
||||
config.set_default_layout(layout_pickytlk);
|
||||
}
|
||||
|
||||
u8 pickytlk_monocolor_state::tablet_read(offs_t offset)
|
||||
{
|
||||
LOGMASKED(LOG_TABLET, "%s: tablet_read [%02x] = %02x\n", machine().describe_context(), offset, m_io_tablet_regs[offset]);
|
||||
|
||||
if (BIT(offset, 0))
|
||||
{
|
||||
u8 y = BIT(m_ko, 3) ? io_pen_y_read() : 0;
|
||||
LOGMASKED(LOG_TABLET, "%s: pen y = %02x\n", machine().describe_context(), y);
|
||||
return BIT(m_ko, 0) ? y : (0xff - y);
|
||||
}
|
||||
else
|
||||
{
|
||||
u8 x = BIT(m_ko, 2) ? io_pen_x_read() : 0;
|
||||
LOGMASKED(LOG_TABLET, "%s: pen x = %02x\n", machine().describe_context(), x);
|
||||
return BIT(m_ko, 0) ? x : (0xff - x);
|
||||
}
|
||||
}
|
||||
|
||||
void pickytlk_monocolor_state::pickytlk_mem(address_map &map)
|
||||
{
|
||||
map(0x000000, 0x00bfff).rom();
|
||||
// map(0x040000, 0x04ffff) // Unknown
|
||||
map(0x080000, 0x0807ff).ram();
|
||||
// map(0x100000, 0x10ffff) // Unknown
|
||||
map(0x200000, 0x2fffff).rom().region("mask_rom", 0);
|
||||
map(0x400000, 0x4007ff).ram().share("display_ram");
|
||||
map(0x400800, 0x41ffff).ram();
|
||||
// Read after pen callibration
|
||||
map(0x800030, 0x80003f).rw(FUNC(pickytlk_monocolor_state::tablet_read), FUNC(pickytlk_monocolor_state::tablet_write));
|
||||
// Read before pen callibration
|
||||
map(0x800040, 0x80004f).rw(FUNC(pickytlk_monocolor_state::tablet_read), FUNC(pickytlk_monocolor_state::tablet_write));
|
||||
// map(0xe00000, 0xe0ffff) // LCD I/O
|
||||
}
|
||||
|
||||
void pickytlk_monocolor_state::pickytlk_palette(palette_device &palette) const
|
||||
{
|
||||
palette.set_pen_color(0, 0xee, 0xee, 0xcc);
|
||||
palette.set_pen_color(1, 0x11, 0x11, 0x11);
|
||||
palette.set_pen_color(2, 0x11, 0x11, 0x11);
|
||||
palette.set_pen_color(3, 0x11, 0x11, 0x11);
|
||||
}
|
||||
|
||||
void pickytlk_monocolor_state::pickytlk_monocolor(machine_config &config)
|
||||
{
|
||||
pickytlk_base_state::pickytlk(config);
|
||||
|
||||
m_maincpu->set_addrmap(AS_PROGRAM, &pickytlk_monocolor_state::pickytlk_mem);
|
||||
|
||||
// TODO: Verify palette. Colors can be changed by changing the contrast.
|
||||
PALETTE(config, "palette", FUNC(pickytlk_monocolor_state::pickytlk_palette), 4);
|
||||
}
|
||||
|
||||
|
||||
class pickytlk_multicolor_state : public pickytlk_base_state
|
||||
{
|
||||
public:
|
||||
pickytlk_multicolor_state(const machine_config &mconfig, device_type type, const char *tag)
|
||||
: pickytlk_base_state(mconfig, type, tag)
|
||||
{ }
|
||||
|
||||
void pickytlk_multicolor(machine_config &config);
|
||||
|
||||
private:
|
||||
virtual u8 tablet_read(offs_t offset) override;
|
||||
virtual void pickytlk_layout(machine_config &config) override;
|
||||
void pickytlk_mem(address_map &map);
|
||||
void pickytlk_palette(palette_device &palette) const;
|
||||
};
|
||||
|
||||
void pickytlk_multicolor_state::pickytlk_layout(machine_config &config)
|
||||
{
|
||||
config.set_default_layout(layout_pickytlk);
|
||||
}
|
||||
|
||||
u8 pickytlk_multicolor_state::tablet_read(offs_t offset)
|
||||
{
|
||||
/*
|
||||
Pen coordinates can return a mirrored value when bit 4 is not set.
|
||||
@ -268,82 +524,20 @@ u8 pickytlk_state::tablet_read(offs_t offset)
|
||||
}
|
||||
}
|
||||
|
||||
void pickytlk_state::tablet_write(offs_t offset, u8 data)
|
||||
void pickytlk_multicolor_state::pickytlk_mem(address_map &map)
|
||||
{
|
||||
LOGMASKED(LOG_TABLET, "%s: tablet_write [%02x] = %02x\n", machine().describe_context(), offset, data);
|
||||
m_io_tablet_regs[offset] = data;
|
||||
map(0x000000, 0x007fff).rom();
|
||||
map(0x080000, 0x0807ff).ram();
|
||||
map(0x080300, 0x08030f).rw(FUNC(pickytlk_multicolor_state::tablet_read), FUNC(pickytlk_multicolor_state::tablet_write));
|
||||
// map(0x100000, 0x10ffff) // Unknown
|
||||
// map(0x110000, 0x11ffff) // LCD I/O
|
||||
map(0x200000, 0x2fffff).rom().region("mask_rom", 0);
|
||||
map(0x400000, 0x4007ff).ram().share("display_ram");
|
||||
map(0x400800, 0x41ffff).ram();
|
||||
// map(0xe10000, 0xe1ffff) // LCD I/O
|
||||
}
|
||||
|
||||
void pickytlk_state::kol_w(u8 data)
|
||||
{
|
||||
m_ko = (m_ko & 0xff00) | data;
|
||||
LOGMASKED(LOG_IO, "%s: KO = %04x\n", machine().describe_context(), m_ko);
|
||||
}
|
||||
|
||||
void pickytlk_state::koh_w(u8 data)
|
||||
{
|
||||
m_ko = (m_ko & 0x00ff) | (u16(data) << 8);
|
||||
LOGMASKED(LOG_IO, "%s: KO = %04x\n", machine().describe_context(), m_ko);
|
||||
}
|
||||
|
||||
void pickytlk_state::port_w(u8 data)
|
||||
{
|
||||
m_port = data;
|
||||
LOGMASKED(LOG_IO, "%s: PORT = %02x\n", machine().describe_context(), m_port);
|
||||
}
|
||||
|
||||
void pickytlk_state::opt_w(u8 data)
|
||||
{
|
||||
m_opt = data;
|
||||
LOGMASKED(LOG_IO, "%s: OPT = %02x\n", machine().describe_context(), m_opt);
|
||||
}
|
||||
|
||||
u8 pickytlk_state::ki_r()
|
||||
{
|
||||
if (BIT(m_io_buttons->read(), 6))
|
||||
{
|
||||
if (m_pen_state == PEN_RELEASE)
|
||||
{
|
||||
m_pen_state = PEN_PRESS;
|
||||
// FIXME: Adjust delay when more accurate instruction timings are implemented.
|
||||
// Program code waits for input flag to be stable by executing `mov DSIZE,0xff`
|
||||
// then `movq R00,R00` 15 times (see pickytlk ROM @ 2015f6).
|
||||
m_io_timer->adjust(attotime::from_msec(1), 0, attotime::never);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pen_state = PEN_RELEASE;
|
||||
m_io_timer->reset(attotime::never);
|
||||
}
|
||||
|
||||
return m_pen_state == PEN_PRESS ? 0x80 : 0;
|
||||
}
|
||||
|
||||
u8 pickytlk_state::in0_r()
|
||||
{
|
||||
// battery level?
|
||||
// bit4 -> if reset CPU keeps restarting (several unknown instructions before jumping to 0)
|
||||
// perhaps a battery present check?
|
||||
// bit 5 -> 0 = low battery
|
||||
|
||||
// --XX ---- VDET
|
||||
// ---- -X-- data-in
|
||||
return 0x30 & ~0x00;
|
||||
}
|
||||
|
||||
u8 pickytlk_state::input_flag_read()
|
||||
{
|
||||
return m_pen_state == PEN_HOLD ? 0 : 1;
|
||||
}
|
||||
|
||||
void pickytlk_state::update_crosshair(screen_device &screen)
|
||||
{
|
||||
// Either screen crosshair or layout view's cursor should be visible at a time.
|
||||
machine().crosshair().get_crosshair(0).set_screen(m_pen_target ? CROSSHAIR_SCREEN_NONE : &screen);
|
||||
}
|
||||
|
||||
void pickytlk_state::pickytlk_palette(palette_device &palette) const
|
||||
void pickytlk_multicolor_state::pickytlk_palette(palette_device &palette) const
|
||||
{
|
||||
palette.set_pen_color(0, 0xee, 0xee, 0xcc);
|
||||
palette.set_pen_color(1, 0x11, 0x33, 0x99);
|
||||
@ -351,92 +545,112 @@ void pickytlk_state::pickytlk_palette(palette_device &palette) const
|
||||
palette.set_pen_color(3, 0xee, 0x77, 0x33);
|
||||
}
|
||||
|
||||
u32 pickytlk_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
|
||||
void pickytlk_multicolor_state::pickytlk_multicolor(machine_config &config)
|
||||
{
|
||||
update_crosshair(screen);
|
||||
pickytlk_base_state::pickytlk(config);
|
||||
|
||||
u16 offset = 0;
|
||||
m_maincpu->set_addrmap(AS_PROGRAM, &pickytlk_multicolor_state::pickytlk_mem);
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
int const x = i * 8;
|
||||
|
||||
for (int j = 0; j < 64; j++)
|
||||
{
|
||||
u16 *const row = &bitmap.pix(63 - j);
|
||||
|
||||
u8 const data1 = m_display_ram[offset];
|
||||
u8 const data2 = m_display_ram[offset + 0x400];
|
||||
|
||||
for (int b = 0; b < 8; b++)
|
||||
{
|
||||
if (x + b < 127)
|
||||
{
|
||||
row[x + b] = (BIT(data1, b) << 1) | BIT(data2, b);
|
||||
}
|
||||
}
|
||||
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
// TODO: Verify palette. Colors can be changed by changing the contrast.
|
||||
PALETTE(config, "palette", FUNC(pickytlk_multicolor_state::pickytlk_palette), 4);
|
||||
}
|
||||
|
||||
|
||||
static INPUT_PORTS_START(pickytlk)
|
||||
// TODO: On/Off/Reset
|
||||
PORT_START("BUTTONS")
|
||||
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Pen Down")
|
||||
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(pickytlk_state::pen_target_r))
|
||||
|
||||
PORT_START("PEN_X")
|
||||
PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("Pen X")
|
||||
|
||||
PORT_START("PEN_Y")
|
||||
PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("Pen Y") PORT_CROSSHAIR_MAPPER_MEMBER(FUNC(pickytlk_state::pen_y_mapper))
|
||||
|
||||
PORT_START("PEN_Y_RESCALE")
|
||||
PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(pickytlk_state::pen_y_rescale_r))
|
||||
INPUT_PORTS_END
|
||||
|
||||
|
||||
void pickytlk_state::pickytlk(machine_config &config)
|
||||
class plets_state : public pickytlk_multicolor_state
|
||||
{
|
||||
HCD62121(config, m_maincpu, 4300000); /* X1 - 4.3 MHz */
|
||||
m_maincpu->set_addrmap(AS_PROGRAM, &pickytlk_state::pickytlk_mem);
|
||||
m_maincpu->kol_cb().set(FUNC(pickytlk_state::kol_w));
|
||||
m_maincpu->koh_cb().set(FUNC(pickytlk_state::koh_w));
|
||||
m_maincpu->port_cb().set(FUNC(pickytlk_state::port_w));
|
||||
m_maincpu->opt_cb().set(FUNC(pickytlk_state::opt_w));
|
||||
m_maincpu->ki_cb().set(FUNC(pickytlk_state::ki_r));
|
||||
m_maincpu->in0_cb().set(FUNC(pickytlk_state::in0_r));
|
||||
m_maincpu->input_flag_cb().set(FUNC(pickytlk_state::input_flag_read));
|
||||
public:
|
||||
plets_state(const machine_config &mconfig, device_type type, const char *tag)
|
||||
: pickytlk_multicolor_state(mconfig, type, tag)
|
||||
{ }
|
||||
|
||||
screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
|
||||
screen.set_refresh_hz(60);
|
||||
screen.set_size(127, 64);
|
||||
screen.set_visarea(0, 126, 0, 63);
|
||||
screen.set_screen_update(FUNC(pickytlk_state::screen_update));
|
||||
screen.set_palette("palette");
|
||||
private:
|
||||
virtual void pickytlk_layout(machine_config &config) override;
|
||||
};
|
||||
|
||||
// TODO: Verify amount of colors and palette. Colors can be changed by changing the contrast.
|
||||
PALETTE(config, "palette", FUNC(pickytlk_state::pickytlk_palette), 4);
|
||||
|
||||
config.set_default_layout(layout_pickytlk);
|
||||
void plets_state::pickytlk_layout(machine_config &config)
|
||||
{
|
||||
// Nothing
|
||||
}
|
||||
|
||||
|
||||
#define CPU_ROM_MONOCOLOR \
|
||||
ROM_REGION(0xc000, "maincpu", 0) \
|
||||
ROM_LOAD("cpu.lsi1", 0x0000, 0xc000, CRC(67c76253) SHA1(2f7d368e584608d0ed3135159c36f35f02cdd8c4))
|
||||
|
||||
#define CPU_ROM_MULTICOLOR \
|
||||
ROM_REGION(0x8000, "maincpu", 0) \
|
||||
ROM_LOAD("cpu.lsi1", 0x0000, 0x8000, CRC(d58efff9) SHA1(a8d2c2a331d79c5299274e2f2d180deda60a5aed))
|
||||
|
||||
|
||||
ROM_START(jd363)
|
||||
CPU_ROM_MONOCOLOR
|
||||
|
||||
ROM_REGION(0x100000, "mask_rom", 0)
|
||||
ROM_LOAD("d23c8000lwgx-c11.lsi5", 0x00000, 0x100000, CRC(d4c6e7d2) SHA1(ef199725f04da977cd47293dba8086011f156baf))
|
||||
ROM_END
|
||||
|
||||
ROM_START(pickydis)
|
||||
CPU_ROM_MONOCOLOR
|
||||
|
||||
ROM_REGION(0x100000, "mask_rom", 0)
|
||||
ROM_LOAD("d23c8000lwgx-c14.lsi5", 0x00000, 0x100000, CRC(1a6273c7) SHA1(f0601873503272d9a620789b5c9798f6e1baf4e7))
|
||||
ROM_END
|
||||
|
||||
ROM_START(jd364)
|
||||
CPU_ROM_MULTICOLOR
|
||||
|
||||
ROM_REGION(0x100000, "mask_rom", 0)
|
||||
ROM_LOAD("d23c8000lwgx-c12.lsi5", 0x00000, 0x100000, CRC(c023b709) SHA1(0c081a62f00d0fbee496a5c9067fb145cb79c8cd))
|
||||
ROM_END
|
||||
|
||||
ROM_START(jd368)
|
||||
CPU_ROM_MULTICOLOR
|
||||
|
||||
ROM_REGION(0x100000, "mask_rom", 0)
|
||||
ROM_LOAD("d23c8000xgx-c42.lsi5", 0x00000, 0x100000, CRC(c396bafb) SHA1(0d5610d288ab2474017c05ed54fc816d2e82525f))
|
||||
ROM_END
|
||||
|
||||
ROM_START(pickytlk)
|
||||
ROM_REGION(0x8000, "maincpu", 0)
|
||||
ROM_LOAD("cpu.lsi1", 0x0000, 0x8000, CRC(d58efff9) SHA1(a8d2c2a331d79c5299274e2f2d180deda60a5aed))
|
||||
CPU_ROM_MULTICOLOR
|
||||
|
||||
ROM_REGION(0x100000, "mask_rom", 0)
|
||||
ROM_LOAD("d23c8000xgx-c64.lsi5", 0x00000, 0x100000, CRC(6ed6feae) SHA1(f9a63db3d048da0954cab052690deb01ec384b22))
|
||||
ROM_END
|
||||
|
||||
ROM_START(mk300)
|
||||
CPU_ROM_MULTICOLOR
|
||||
|
||||
ROM_REGION(0x100000, "mask_rom", 0)
|
||||
ROM_LOAD("d23c8000xgx-c77.lsi5", 0x00000, 0x100000, CRC(50ecb853) SHA1(5f2564ccb6ff7e0e5a21064ca32626f35dc81506))
|
||||
ROM_END
|
||||
|
||||
ROM_START(mk350)
|
||||
CPU_ROM_MULTICOLOR
|
||||
|
||||
ROM_REGION(0x100000, "mask_rom", 0)
|
||||
ROM_LOAD("m538032e-48.lsi5", 0x00000, 0x100000, CRC(42068a99) SHA1(24f8dc15a51a391d4c35cce7332d55f1fa4d8160))
|
||||
ROM_END
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
// "CASIO スーパーピッキートーク「グルタンの森」はやわかりビデオ" has copyright dates 1997,1998,1999
|
||||
COMP(1997, pickytlk, 0, 0, pickytlk, pickytlk, pickytlk_state, empty_init, "Casio", "Super Picky Talk - Forest of Gurutan", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
|
||||
// Release date 1995-09 from "Casio Game Perfect Catalogue"
|
||||
COMP(1995, jd363, 0, 0, pickytlk_monocolor, pickytlk, pickytlk_monocolor_state, empty_init, "Casio", "Picky Talk - Super Denshi Techou", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
|
||||
|
||||
// ROM date 9539K7001
|
||||
COMP(1995?, pickydis, 0, 0, pickytlk_monocolor, pickytlk, pickytlk_monocolor_state, empty_init, "Tsukuda Original", "Disney Characters - Tegaki Electronic Note", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
|
||||
|
||||
// Release date 1995-10 from "Casio Game Perfect Catalogue"
|
||||
COMP(1995, jd364, 0, 0, pickytlk_multicolor, pickytlk, pickytlk_multicolor_state, empty_init, "Casio", "Color Picky Talk - Super Denshi Techou", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
|
||||
|
||||
// ROM date 9737K7041
|
||||
COMP(1997?, jd368, 0, 0, pickytlk_multicolor, pickytlk, pickytlk_multicolor_state, empty_init, "Casio", "Super Picky Talk - Access Pet", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
|
||||
|
||||
// Release date 1998-07 from "Casio Game Perfect Catalogue"
|
||||
COMP(1998, pickytlk, 0, 0, pickytlk_multicolor, pickytlk, pickytlk_multicolor_state, empty_init, "Casio", "Super Picky Talk - Forest of Gurutan", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
|
||||
|
||||
// Release date 1999-09 from "Casio Game Perfect Catalogue"
|
||||
COMP(1999, mk300, 0, 0, pickytlk_multicolor, pickytlk, plets_state, empty_init, "Casio", "Plet's (MK-300)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
|
||||
|
||||
// ROM date 0019K7001
|
||||
COMP(2000?, mk350, 0, 0, pickytlk_multicolor, pickytlk, plets_state, empty_init, "Casio", "Plet's (MK-350)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING)
|
||||
|
@ -16138,7 +16138,13 @@ pb1000 // Casio PB-1000
|
||||
pb2000c // Casio PB-2000C
|
||||
|
||||
@source:casio/pickytlk.cpp
|
||||
pickytlk // Casio Picky Talk
|
||||
jd363 // Casio Picky Talk - Super Denshi Techou
|
||||
jd364 // Casio Color Picky Talk - Super Denshi Techou
|
||||
jd368 // Casio Super Picky Talk - Access Pet
|
||||
mk300 // Casio Plet's (MK-300)
|
||||
mk350 // Casio Plet's (MK-350)
|
||||
pickydis // Tsukuda Original Disney Characters - Tegaki Electronic Note
|
||||
pickytlk // Casio Super Picky Talk - Forest of Gurutan
|
||||
|
||||
@source:casio/pv1000.cpp
|
||||
pv1000 // Casio PV-1000
|
||||
|
Loading…
Reference in New Issue
Block a user