#endif
}
- // if we just re-enabled the interrupts...
- // double buffer the I flag, to detect that edge
- if (avr->sreg[S_I] && !avr->i_shadow)
- avr->interrupts.pending_wait++;
- avr->i_shadow = avr->sreg[S_I];
-
// run the cycle timers, get the suggested sleep time
// until the next timer is due
avr_cycle_count_t sleep = avr_cycle_timer_process(avr);
#endif
}
- // if we just re-enabled the interrupts...
- // double buffer the I flag, to detect that edge
- if (avr->sreg[S_I] && !avr->i_shadow)
- avr->interrupts.pending_wait++;
- avr->i_shadow = avr->sreg[S_I];
-
// run the cycle timers, get the suggested sleep time
// until the next timer is due
avr_cycle_count_t sleep = avr_cycle_timer_process(avr);
avr->cycle += 1 + sleep;
}
// Interrupt servicing might change the PC too, during 'sleep'
- if (avr->state == cpu_Running || avr->state == cpu_Sleeping)
- avr_service_interrupts(avr);
+ if (avr->state == cpu_Running || avr->state == cpu_Sleeping) {
+ /* Note: checking interrupt_state here is completely superfluous, however
+ as interrupt_state tells us all we really need to know, here
+ a simple check here may be cheaper than a call not needed. */
+ if (avr->interrupt_state) avr_service_interrupts(avr);
+ }
}
// in the opcode decoder.
// This array is re-synthesized back/forth when SREG changes
uint8_t sreg[8];
- uint8_t i_shadow; // used to detect edges on I flag
+
+ /* Interrupt state:
+ 00: idle (no wait, no pending interrupts) or disabled
+ <0: wait till zero
+ >0: interrupt pending */
+ int8_t interrupt_state; // interrupt state
/*
* ** current PC **
if ((opcode & 0xff0f) == 0x9408) {
get_sreg_bit(opcode);
STATE("%s%c\n", opcode & 0x0080 ? "cl" : "se", _sreg_bit_name[b]);
- avr->sreg[b] = (opcode & 0x0080) == 0;
+ avr_sreg_set(avr, b, (opcode & 0x0080) == 0);
SREG();
} else switch (opcode) {
case 0x9588: { // SLEEP -- 1001 0101 1000 1000
new_pc = _avr_pop_addr(avr);
cycle += 1 + avr->address_size;
if (opcode & 0x10) // reti
- avr->sreg[S_I] = 1;
+ avr_sreg_set(avr, S_I, 1);
STATE("ret%s\n", opcode & 0x10 ? "i" : "");
TRACE_JUMP();
STACK_FRAME_POP();
}
avr->cycle += cycle;
- if( (avr->state == cpu_Running) &&
+ if ((avr->state == cpu_Running) &&
(avr->run_cycle_count > cycle) &&
- !(avr->sreg[S_I] && avr_has_pending_interrupts(avr)) )
+ (avr->interrupt_state == 0))
{
avr->run_cycle_count -= cycle;
avr->pc = new_pc;
dst |= (1 << i); \
}
+static inline void avr_sreg_set(avr_t * avr, uint8_t flag, uint8_t ival)
+{
+ /*
+ * clear interrupt_state if disabling interrupts.
+ * set wait if enabling interrupts.
+ * no change if interrupt flag does not change.
+ */
+
+ if (flag == S_I) {
+ if (ival) {
+ if (!avr->sreg[S_I])
+ avr->interrupt_state = -2;
+ } else
+ avr->interrupt_state = 0;
+ }
+
+ avr->sreg[flag] = ival;
+}
+
/**
* Splits the SREG value from src into the avr->sreg array.
*/
#define SET_SREG_FROM(avr, src) { \
for (int i = 0; i < 8; i++) \
- avr->sreg[i] = (src & (1 << i)) != 0; \
+ avr_sreg_set(avr, i, (src & (1 << i)) != 0); \
}
#ifdef __cplusplus
printf("%s\n", __func__);
avr_int_table_p table = &avr->interrupts;
table->pending_r = table->pending_w = 0;
- table->pending_wait = 0;
+ avr->interrupt_state = 0;
for (int i = 0; i < table->vector_count; i++)
table->vector[i]->pending = 0;
}
table->pending[table->pending_w++] = vector;
table->pending_w = INT_FIFO_MOD(table->pending_w);
- if (!table->pending_wait)
- table->pending_wait = 1; // latency on interrupts ??
+ if (avr->sreg[S_I] && avr->interrupt_state == 0)
+ avr->interrupt_state = 1;
if (avr->state == cpu_Sleeping) {
if (vector->trace)
printf("Waking CPU due to interrupt\n");
if (!avr->sreg[S_I])
return;
- if (!avr_has_pending_interrupts(avr))
+ if (avr->interrupt_state) {
+ if (avr->interrupt_state < 0) {
+ avr->interrupt_state++;
+ if (avr->interrupt_state == 0)
+ avr->interrupt_state = avr_has_pending_interrupts(avr);
+ return;
+ }
+ } else
return;
avr_int_table_p table = &avr->interrupts;
- if (!table->pending_wait) {
- table->pending_wait = 2; // for next one...
- return;
- }
- table->pending_wait--;
- if (table->pending_wait)
- return;
-
// how many are pending...
int cnt = table->pending_w > table->pending_r ?
table->pending_w - table->pending_r :
// could also have been disabled, or cleared
if (!avr_regbit_get(avr, vector->enable) || !vector->pending) {
vector->pending = 0;
+ avr->interrupt_state = avr_has_pending_interrupts(avr);
} else {
if (vector && vector->trace)
printf("%s calling %d\n", __FUNCTION__, (int)vector->vector);
_avr_push_addr(avr, avr->pc);
- avr->sreg[S_I] = 0;
+ avr_sreg_set(avr, S_I, 0);
avr->pc = vector->vector * avr->vector_size;
avr_clear_interrupt(avr, vector);
typedef struct avr_int_table_t {
avr_int_vector_t * vector[64];
uint8_t vector_count;
- uint8_t pending_wait; // number of cycles to wait for pending
avr_int_vector_t * pending[64]; // needs to be >= vectors and a power of two
uint8_t pending_w,
pending_r; // fifo cursors
if (avr->state == cpu_Running)
new_pc = avr_run_one(avr);
- // if we just re-enabled the interrupts...
- // double buffer the I flag, to detect that edge
- if (avr->sreg[S_I] && !avr->i_shadow)
- avr->interrupts.pending_wait++;
- avr->i_shadow = avr->sreg[S_I];
-
// run the cycle timers, get the suggested sleep time
// until the next timer is due
avr_cycle_count_t sleep = avr_cycle_timer_process(avr);