palm: improve interrupt handling

* fix debugger register access
* consolidate internal state
* add ros selection and bus out callbacks
* add internal control instruction handler
This commit is contained in:
Patrick Mackinlay 2024-04-01 12:39:16 +07:00
parent 8203e170a1
commit 34b7c397c1
2 changed files with 83 additions and 25 deletions

View File

@ -8,7 +8,7 @@
* - IBM 5100 Maintenance Information Manual, SY31-0405-3, Fourth Edition (October 1979), International Business Machines Corporation
*
* TODO:
* - machine check/interrupts
* - machine check
* - instruction timing
*/
@ -30,6 +30,8 @@ template <typename T, typename U, typename V> constexpr T IBIT(T x, U n, V w)
return BIT(x, sizeof(T) * 8 - n - w, w);
}
static u8 constexpr il_priority[] = { 0, 1, 2, 2, 3, 3, 3, 3 };
DEFINE_DEVICE_TYPE(PALM, palm_device, "palm", "IBM PALM")
palm_device::palm_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
@ -38,29 +40,41 @@ palm_device::palm_device(machine_config const &mconfig, char const *tag, device_
, m_rws_config("rws", ENDIANNESS_BIG, 16, 16)
, m_ioc_config("ioc", ENDIANNESS_BIG, 8, 4)
, m_iod_config("iod", ENDIANNESS_BIG, 8, 4)
, m_getb_bus(*this)
, m_select_ros(*this)
, m_icount(0)
, m_r{}
, m_il(0)
, m_il_pending(0)
, m_irpt_req(0)
{
}
enum ff_mask : u8
{
FF_IPL = 0x80, // initial program load
FF_MSS = 0x40, // microprogram storage switch
FF_IE = 0x08, // interrupt enable
FF_IR3 = 0x04, // interrupt request 3
FF_IR2 = 0x02, // interrupt request 2
FF_IR1 = 0x01, // interrupt request 1
FF_IR = 0x07, // interrupt request
};
void palm_device::device_start()
{
set_icountptr(m_icount);
state_add(STATE_GENPC, "GENPC", m_r[m_il][0]).mask(0xfffe).noshow();
state_add(STATE_GENPCBASE, "CURPC", m_r[m_il][0]).mask(0xfffe);
state_add(STATE_GENPC, "GENPC", m_pc).noshow();
state_add<u16>(STATE_GENPCBASE, "CURPC", [this]() { return m_r[m_il][0] & 0xfffeU; });
state_add(0, "IL", m_il);
for (unsigned i = 0; i < std::size(m_r[m_il]); i++)
state_add(i + 1, util::string_format("R%d", i).c_str(), m_r[m_il][i]);
state_add<u16>(i + 1, util::string_format("R%d", i).c_str(), [this, i]() { return m_r[m_il][i]; }, [this, i](u16 data) { m_r[m_il][i] = data; });
save_item(NAME(m_pc));
save_item(NAME(m_r));
save_item(NAME(m_il));
save_item(NAME(m_il_pending));
save_item(NAME(m_irpt_req));
save_item(NAME(m_ff));
space(AS_ROS).specific(m_ros);
space(AS_RWS).specific(m_rws);
@ -72,10 +86,13 @@ void palm_device::device_reset()
{
space(AS_RWS).install_ram(0, sizeof(m_r) - 1, m_r);
m_il = 0;
// select instruction source
m_ff = FF_IPL | FF_MSS;
m_select_ros((m_ff & FF_MSS) && !(m_ff & FF_IPL));
// read initial PC from ROS
m_r[m_il][0] = m_ros.read_word(0);
m_il = 0;
m_pc = m_r[m_il][0] = m_ros.read_word(0);
}
#define Rx r[IBIT(op, 4, 4)]
@ -88,13 +105,14 @@ void palm_device::execute_run()
{
while (m_icount > 0)
{
// immediately switch to highest pending interrupt level
if (m_il != m_il_pending)
// handle pending interrupts
u8 const il = il_priority[m_ff & FF_IR];
if ((m_ff & FF_IE) && m_il != il)
{
m_il = m_il_pending;
m_il = il;
// notify the debugger
if (m_il && machine().debug_flags & DEBUG_FLAG_ENABLED)
if (m_il && (machine().debug_flags & DEBUG_FLAG_ENABLED))
debug()->interrupt_hook(m_il - 1, m_r[m_il][0] & ~1);
}
@ -133,7 +151,13 @@ void palm_device::execute_run()
case 0xf: Ry += (7 - count_leading_ones_32(u32(m_iod.read_byte(DA)) << 24)) * 2; break; // get to register and add
}
break;
case 0x1: m_ioc.write_byte(DA, IMM); break; // control
case 0x1:
// control
if (DA == 0)
control(IMM);
m_ioc.write_byte(DA, IMM);
break;
case 0x2: Rx = m_rws.read_word(IMM * 2); break; // load halfword direct
case 0x3: m_rws.write_word(IMM * 2, Rx); break; // store halfword direct
case 0x4:
@ -185,6 +209,8 @@ void palm_device::execute_run()
}
else
{
m_getb_bus(DA, Ry);
if (MOD < 0xc)
{
// get byte
@ -201,13 +227,12 @@ void palm_device::execute_run()
// TODO: average instruction time quoted as 1.75µs (~27 machine cycles)
m_icount -= 27;
m_pc = r[0];
}
}
void palm_device::execute_set_input(int irqline, int state)
{
u8 const il_priority[] = { 0, 1, 2, 3, 3, 3, 3, 3 };
switch (irqline)
{
case INPUT_LINE_NMI:
@ -217,12 +242,9 @@ void palm_device::execute_set_input(int irqline, int state)
default:
// interrupt lines are active low
if (!state)
m_irpt_req |= 1U << irqline;
m_ff |= 1U << irqline;
else
m_irpt_req &= ~(1U << irqline);
// update pending interrupt level
m_il_pending = il_priority[m_irpt_req & 7];
m_ff &= ~(1U << irqline);
break;
}
}
@ -278,3 +300,31 @@ s16 palm_device::modifier(unsigned const modifier) const
else
return 0;
}
void palm_device::control(u8 data)
{
LOG("control 0x%02x (%s)\n", data, machine().describe_context());
// 0 reset controller errors
// 1: 0=disable interrupts
// 2: 0=enable interrupts
if (!IBIT(data, 1))
m_ff &= ~FF_IE;
else if (!IBIT(data, 2))
m_ff |= FF_IE;
// TODO: 3 not used?
// 4 not used (0:display & select frame on)
// 5 state transition
if (!IBIT(data, 5))
{
m_ff &= ~FF_IPL;
m_ff ^= FF_MSS;
m_select_ros((m_ff & FF_MSS) && !(m_ff & FF_IPL));
}
// TODO: 6..7 not used (frame bit #1,2?)
}

View File

@ -20,6 +20,9 @@ public:
static unsigned constexpr AS_IOC = AS_IO;
static unsigned constexpr AS_IOD = 4;
auto getb_bus() { return m_getb_bus.bind(); }
auto select_ros() { return m_select_ros.bind(); }
palm_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
protected:
@ -43,6 +46,8 @@ protected:
bool condition(unsigned const modifier, u16 const data, u8 const mask) const;
s16 modifier(unsigned const modifier) const;
void control(u8 data);
private:
// address spaces
address_space_config const m_ros_config;
@ -55,13 +60,16 @@ private:
memory_access<4, 0, 0, ENDIANNESS_BIG>::specific m_ioc;
memory_access<4, 0, 0, ENDIANNESS_BIG>::specific m_iod;
devcb_write8 m_getb_bus;
devcb_write_line m_select_ros;
// mame state
int m_icount;
u16 m_pc;
u16 m_r[4][16]; // registers
u8 m_il; // interrupt level
u8 m_il_pending; // pending interrupt level
u8 m_irpt_req; // interrupt line state
u8 m_ff; // controller flip-flops
};
DECLARE_DEVICE_TYPE(PALM, palm_device)