Implemented 80C52 interrupt handling

* mcs51 core now supports 4 interrupt levels depending on cpu type
This commit is contained in:
Couriersud 2008-10-24 21:23:41 +00:00
parent d64bda5e94
commit b1e8ff0a3e

View File

@ -68,10 +68,11 @@
* 1 means external access, bypassing internal ROM
* - T0 output clock ?
*
* - Implement 80C52 extended serial capabilities
* - Full Timer support (all modes)
* - Implement cmos features
* - Fix serial communication - This is a big hack (but working) right now.
* - Implement 87C751 in sslam.c
* - Implement 83C751 in sslam.c
* - Fix cardline.c
* - Fix sslam.c and cardline.c
* most likely due to different behaviour of I/O pins. The boards
@ -80,6 +81,7 @@
* a 0 written to it's latch. At least cardline expects a 1 here.
*
* Done:
* - Implemented 80C52 interrupt handling
* - Fix segas18.c (segaic16.c) memory handling.
* - Fix sslam.c
* - Fix limenko.c videopkr.c : Issue with core allocation of ram (duplicate savestate)
@ -112,10 +114,11 @@
enum
{
FEATURE_NONE = 0,
FEATURE_I8052_UART = 1,
FEATURE_CMOS_IDLE = 2,
FEATURE_CMOS_POWERDOWN = 4,
FEATURE_NONE = 0x00,
FEATURE_I8052 = 0x01,
FEATURE_CMOS_IDLE = 0x02,
FEATURE_CMOS_POWERDOWN = 0x04,
FEATURE_I80C52 = 0x08,
};
/* Internal address in SFR of registers */
@ -150,6 +153,11 @@ enum
ADDR_RCAP2H = 0xcb,
ADDR_TL2 = 0xcc,
ADDR_TH2 = 0xcd,
/* 80C52 Only registers */
ADDR_IPH = 0xb7,
ADDR_SADDR = 0xa9,
ADDR_SADEN = 0xb9,
};
/* PC vectors */
@ -190,8 +198,6 @@ struct _mcs51_regs
UINT16 ppc; //previous pc
UINT16 pc; //current pc
UINT16 features; //features of this cpu
UINT8 cur_irq; //Holds value of any current IRQ being serviced
UINT8 irq_priority; //Holds value of the current IRQ Priority Level
UINT8 rwm; //Signals that the current instruction is a read/write/modify instruction
int inst_cycles; /* cycles for the current instruction */
@ -203,6 +209,9 @@ struct _mcs51_regs
int t1_cnt; /* number of 0->1 transistions on T1 line */
int t2_cnt; /* number of 0->1 transistions on T2 line */
int t2ex_cnt; /* number of 0->1 transistions on T2EX line */
int cur_irq_prio; /* Holds value of the current IRQ Priority Level; -1 if no irq */
UINT8 irq_active; /* mask which irq levels are serviced */
UINT8 irq_prio[8]; /* interrupt priority */
mcs51_uart uart; /* internal uart */
@ -315,6 +324,11 @@ struct _mcs51_regs
#define TL2 SFR_A(ADDR_TL2)
#define TH2 SFR_A(ADDR_TH2)
/* 80C52 Only registers */
#define IPH SFR_A(ADDR_IPH)
#define SADDR SFR_A(ADDR_SADDR)
#define SADEN SFR_A(ADDR_SADEN)
/* WRITE accessors */
/* Shortcuts */
@ -497,7 +511,7 @@ struct _mcs51_regs
#endif
/* Clear Current IRQ */
#define CLEAR_CURRENT_IRQ() do { mcs51.cur_irq = 0xff; mcs51.irq_priority = 0; } while (0)
#define CLEAR_CURRENT_IRQ() clear_current_irq()
/***************************************************************************
GLOBAL VARIABLES
@ -524,6 +538,23 @@ static int (*hold_serial_rx_callback)(void);
INLINE FUNCTIONS
***************************************************************************/
INLINE void clear_current_irq(void)
{
//printf("reti cip %d, act %02x\n",mcs51.cur_irq_prio, mcs51.irq_active);
//assert(mcs51.cur_irq_prio >= 0 && mcs51.irq_active);
if (mcs51.cur_irq_prio >= 0)
mcs51.irq_active &= ~(1 << mcs51.cur_irq_prio);
if (mcs51.irq_active & 4)
mcs51.cur_irq_prio = 2;
else if (mcs51.irq_active & 2)
mcs51.cur_irq_prio = 1;
else if (mcs51.irq_active & 1)
mcs51.cur_irq_prio = 0;
else
mcs51.cur_irq_prio = -1;
}
INLINE UINT8 r_acc(void) { return SFR_A(ADDR_ACC); }
INLINE UINT8 r_psw(void) { return SFR_A(ADDR_PSW); }
@ -967,7 +998,7 @@ INLINE void update_timers(int cycles)
update_timer_t0(cycles);
update_timer_t1(cycles);
if (mcs51.features & FEATURE_I8052_UART)
if (mcs51.features & FEATURE_I8052)
{
update_timer_t2(cycles);
}
@ -1023,13 +1054,21 @@ INLINE void serial_receive(void)
}
}
//Check and update status of serial port
/* Check and update status of serial port */
INLINE void update_serial(int cycles)
{
while (--cycles>=0)
transmit_receive(0);
}
/* Check and update status of serial port */
INLINE void update_irq_prio(UINT8 ipl, UINT8 iph)
{
int i;
for (i=0; i<8; i++)
mcs51.irq_prio[i] = ((ipl >> i) & 1) | (((iph >>i ) & 1) << 1);
}
/***************************************************************************
CALLBACKS - TODO: Remove
***************************************************************************/
@ -1421,15 +1460,14 @@ static void check_irqs(void)
{
UINT8 ints = (GET_IE0 | (GET_TF0<<1) | (GET_IE1<<2) | (GET_TF1<<3)
| ((GET_RI|GET_TI)<<4));
UINT8 ip = IP;
UINT8 int_vec = 0;
UINT8 priority_request = 0;
int priority_request = -1;
int i;
//If All Inerrupts Disabled or no pending abort..
if(!GET_EA) return;
if (mcs51.features & FEATURE_I8052_UART)
if (mcs51.features & FEATURE_I8052)
ints |= ((GET_TF2|GET_EXF2)<<5);
/* mask out interrupts not enabled */
@ -1437,27 +1475,24 @@ static void check_irqs(void)
if (!ints) return;
if(mcs51.irq_priority)
{
LOG(("high priority irq in progress, skipping irq request\n"));
return;
}
for (i=0; i<mcs51.num_interrupts; i++)
{
if (ints & (1<<i))
{
if (mcs51.irq_prio[i] > priority_request)
{
priority_request = mcs51.irq_prio[i];
int_vec = (i<<3) | 3;
priority_request = (ip & (1<<i));
if (priority_request)
break;
}
}
}
//Skip the interrupt request if currently processing is lo priority, and the new request IS NOT HI PRIORITY!
if(mcs51.cur_irq < 0xff && !priority_request)
/* Skip the interrupt request if currently processing interrupt
* and the new request does not have a higher priority
*/
if(mcs51.irq_active && (priority_request <= mcs51.cur_irq_prio))
{
LOG(("low priority irq in progress already, skipping low irq request\n"));
LOG(("higher or equal priority irq in progress already, skipping ...\n"));
return;
}
@ -1470,11 +1505,13 @@ static void check_irqs(void)
mcs51.inst_cycles += 2;
//Set current Irq & Priority being serviced
mcs51.cur_irq = int_vec;
mcs51.irq_priority = priority_request;
mcs51.cur_irq_prio = priority_request;
mcs51.irq_active |= (1 << priority_request);
//printf("irq cip %d, act %02x\n",mcs51.cur_irq_prio, mcs51.irq_active);
//Clear any interrupt flags that should be cleared since we're servicing the irq!
switch(mcs51.cur_irq) {
switch(int_vec) {
case V_IE0:
//External Int Flag only cleared when configured as Edge Triggered..
if(GET_IT0) /* for some reason having this, breaks alving dmd games */
@ -1599,7 +1636,7 @@ static void mcs51_set_irq_line(int irqline, int state)
break;
case MCS51_T2_LINE:
if (mcs51.features & FEATURE_I8052_UART)
if (mcs51.features & FEATURE_I8052)
{
if (GET_BIT(tr_state, MCS51_T2_LINE))
mcs51.t2_cnt++;
@ -1609,7 +1646,7 @@ static void mcs51_set_irq_line(int irqline, int state)
break;
case MCS51_T2EX_LINE:
if (mcs51.features & FEATURE_I8052_UART)
if (mcs51.features & FEATURE_I8052)
{
if (GET_TR2 && GET_EXEN2)
if (GET_BIT(tr_state, MCS51_T2EX_LINE))
@ -1686,10 +1723,10 @@ static void mcs51_sfr_write(size_t offset, UINT8 data)
case ADDR_SBUF: serial_transmit(data); break;
case ADDR_PSW: SET_PARITY(); break;
case ADDR_ACC: SET_PARITY(); break;
case ADDR_IP: update_irq_prio(data, 0); break;
/* R_SBUF = data; //This register is used only for "Receiving data coming in!" */
case ADDR_B:
case ADDR_IP:
case ADDR_SP:
case ADDR_DPL:
case ADDR_DPH:
@ -1765,14 +1802,15 @@ static void mcs51_init(int index, int clock, const void *config, int (*irqcallba
state_save_register_item("mcs51", index, mcs51.ppc);
state_save_register_item("mcs51", index, mcs51.pc);
state_save_register_item("mcs51", index, mcs51.rwm );
state_save_register_item("mcs51", index, mcs51.cur_irq );
state_save_register_item("mcs51", index, mcs51.irq_priority );
state_save_register_item("mcs51", index, mcs51.cur_irq_prio );
state_save_register_item("mcs51", index, mcs51.last_line_state );
state_save_register_item("mcs51", index, mcs51.t0_cnt );
state_save_register_item("mcs51", index, mcs51.t1_cnt );
state_save_register_item("mcs51", index, mcs51.t2_cnt );
state_save_register_item("mcs51", index, mcs51.t2ex_cnt );
state_save_register_item("mcs51", index, mcs51.recalc_parity );
state_save_register_item_array("mcs51", index, mcs51.irq_prio );
state_save_register_item("mcs51", index, mcs51.irq_active );
}
static void i80c51_init(int index, int clock, const void *config, int (*irqcallback)(int))
@ -1797,6 +1835,9 @@ static void mcs51_reset(void)
mcs51.t1_cnt = 0;
mcs51.t2_cnt = 0;
mcs51.t2ex_cnt = 0;
/* Flag as NO IRQ in Progress */
mcs51.irq_active = 0;
mcs51.cur_irq_prio = -1;
/* these are all defined reset states */
PC = 0;
@ -1807,6 +1848,7 @@ static void mcs51_reset(void)
DPL = 0;
B = 0;
IP = 0;
update_irq_prio(IP, 0);
IE = 0;
SCON = 0;
TCON = 0;
@ -1821,8 +1863,6 @@ static void mcs51_reset(void)
SET_P1(0xff);
SET_P0(0xff);
/* Flag as NO IRQ in Progress */
CLEAR_CURRENT_IRQ();
}
/* Shut down CPU core */
@ -1876,18 +1916,11 @@ static void i8052_init (int index, int clock, const void *config, int (*irqcallb
mcs51.ram_mask = 0xFF; /* 256 bytes of ram */
mcs51.num_interrupts = 6; /* 6 interrupts */
mcs51.features |= FEATURE_I8052_UART;
mcs51.features |= FEATURE_I8052;
mcs51.sfr_read = i8052_sfr_read;
mcs51.sfr_write = i8052_sfr_write;
}
static void i80c52_init(int index, int clock, const void *config, int (*irqcallback)(int))
{
i8052_init(index, clock, config, irqcallback);
mcs51.features |= (FEATURE_CMOS_IDLE | FEATURE_CMOS_POWERDOWN);
}
static void i8052_reset(void)
{
mcs51_reset();
@ -1900,6 +1933,64 @@ static void i8052_reset(void)
TH2 = 0;
}
/****************************************************************************
* 8052 Section
****************************************************************************/
static void i80c52_sfr_write(size_t offset, UINT8 data)
{
switch (offset)
{
/* 80c52 family specific */
case ADDR_IP:
update_irq_prio(data, IPH);
break;
case ADDR_IPH:
update_irq_prio(IP, data);
break;
case ADDR_SADDR:
case ADDR_SADEN:
break;
default:
i8052_sfr_write(offset, data);
return;
}
data_write_byte_8le((size_t) offset | 0x100, data);
}
static UINT8 i80c52_sfr_read(size_t offset)
{
switch (offset)
{
/* 80c52 family specific */
case ADDR_IPH:
case ADDR_SADDR:
case ADDR_SADEN:
return data_read_byte_8le((size_t) offset | 0x100);
default:
return i8052_sfr_read(offset);
}
}
static void i80c52_init(int index, int clock, const void *config, int (*irqcallback)(int))
{
i8052_init(index, clock, config, irqcallback);
mcs51.features |= (FEATURE_I80C52 | FEATURE_CMOS_IDLE | FEATURE_CMOS_POWERDOWN);
mcs51.sfr_read = i80c52_sfr_read;
mcs51.sfr_write = i80c52_sfr_write;
}
static void i80c52_reset(void)
{
i8052_reset();
IRAM_W(ADDR_IPH, 0);
SADDR = 0;
SADEN = 0;
}
/***************************************************************************
ADDRESS MAPS
***************************************************************************/
@ -2209,6 +2300,7 @@ void i80c32_get_info(UINT32 state, cpuinfo *info)
switch (state)
{
case CPUINFO_PTR_INIT: info->init = i80c52_init; break;
case CPUINFO_PTR_RESET: info->reset = i80c52_reset; break;
case CPUINFO_STR_NAME: strcpy(info->s, "I80C32"); break;
default: i8032_get_info(state, info); break;
}
@ -2219,6 +2311,7 @@ void i80c52_get_info(UINT32 state, cpuinfo *info)
switch (state)
{
case CPUINFO_PTR_INIT: info->init = i80c52_init; break;
case CPUINFO_PTR_RESET: info->reset = i80c52_reset; break;
case CPUINFO_STR_NAME: strcpy(info->s, "I80C52"); break;
default: i8052_get_info(state, info); break;
}
@ -2239,6 +2332,7 @@ void i87c52_get_info(UINT32 state, cpuinfo *info)
switch (state)
{
case CPUINFO_PTR_INIT: info->init = i80c52_init; break;
case CPUINFO_PTR_RESET: info->reset = i80c52_reset; break;
case CPUINFO_STR_NAME: strcpy(info->s, "I87C52"); break;
default: i8752_get_info(state, info); break;
}