#include <string.h>
#include "sim_irq.h"
+// internal structure for a hook, never seen by the notify procs
+typedef struct avr_irq_hook_t {
+ struct avr_irq_hook_t * next;
+ int busy; // prevent reentrance of callbacks
+
+ struct avr_irq_t * chain; // raise the IRQ on this too - optional if "notify" is on
+ avr_irq_notify_t notify; // called when IRQ is raised - optional if "chain" is on
+ void * param; // "notify" parameter
+} avr_irq_hook_t;
+
+static void
+_avr_irq_pool_add(
+ avr_irq_pool_t * pool,
+ avr_irq_t * irq)
+{
+ if ((pool->count & 0xf) == 0) {
+ pool->irq = (avr_irq_t**)realloc(pool->irq,
+ (pool->count + 16) * sizeof(avr_irq_t *));
+ }
+ pool->irq[pool->count++] = irq;
+ irq->pool = pool;
+}
+
+static void
+_avr_irq_pool_remove(
+ avr_irq_pool_t * pool,
+ avr_irq_t * irq)
+{
+ for (int i = 0; i < pool->count; i++)
+ if (pool->irq[i] == irq) {
+ pool->irq[i] = 0;
+ return;
+ }
+}
-void avr_init_irq(avr_irq_t * irq, uint32_t base, uint32_t count)
+void
+avr_init_irq(
+ avr_irq_pool_t * pool,
+ avr_irq_t * irq,
+ uint32_t base,
+ uint32_t count,
+ const char ** names /* optional */)
{
memset(irq, 0, sizeof(avr_irq_t) * count);
- for (int i = 0; i < count; i++)
+ for (int i = 0; i < count; i++) {
irq[i].irq = base + i;
+ irq[i].flags = IRQ_FLAG_INIT;
+ if (pool)
+ _avr_irq_pool_add(pool, &irq[i]);
+ if (names && names[i])
+ irq[i].name = strdup(names[i]);
+ }
}
-avr_irq_t * avr_alloc_irq(uint32_t base, uint32_t count)
+avr_irq_t *
+avr_alloc_irq(
+ avr_irq_pool_t * pool,
+ uint32_t base,
+ uint32_t count,
+ const char ** names /* optional */)
{
avr_irq_t * irq = (avr_irq_t*)malloc(sizeof(avr_irq_t) * count);
- avr_init_irq(irq, base, count);
+ avr_init_irq(pool, irq, base, count, names);
+ for (int i = 0; i < count; i++)
+ irq[i].flags |= IRQ_FLAG_ALLOC;
return irq;
}
-void avr_irq_register_notify(avr_irq_t * irq, avr_irq_notify_t notify, void * param)
+static avr_irq_hook_t *
+_avr_alloc_irq_hook(
+ avr_irq_t * irq)
+{
+ avr_irq_hook_t *hook = malloc(sizeof(avr_irq_hook_t));
+ memset(hook, 0, sizeof(avr_irq_hook_t));
+ hook->next = irq->hook;
+ irq->hook = hook;
+ return hook;
+}
+
+void
+avr_free_irq(
+ avr_irq_t * irq,
+ uint32_t count)
+{
+ if (!irq || !count)
+ return;
+ for (int i = 0; i < count; i++) {
+ avr_irq_t * iq = irq + i;
+ if (iq->pool)
+ _avr_irq_pool_remove(iq->pool, iq);
+ if (iq->name)
+ free((char*)iq->name);
+ iq->name = NULL;
+ // purge hooks
+ avr_irq_hook_t *hook = iq->hook;
+ while (hook) {
+ avr_irq_hook_t * next = hook->next;
+ free(hook);
+ hook = next;
+ }
+ iq->hook = NULL;
+ }
+ // if that irq list was allocated by us, free it
+ if (irq->flags & IRQ_FLAG_ALLOC)
+ free(irq);
+}
+
+void
+avr_irq_register_notify(
+ avr_irq_t * irq,
+ avr_irq_notify_t notify,
+ void * param)
{
if (!irq || !notify)
return;
return; // already there
hook = hook->next;
}
- hook = malloc(sizeof(avr_irq_hook_t));
- memset(hook, 0, sizeof(avr_irq_hook_t));
- hook->next = irq->hook;
+ hook = _avr_alloc_irq_hook(irq);
hook->notify = notify;
hook->param = param;
- irq->hook = hook;
}
-void avr_raise_irq(avr_irq_t * irq, uint32_t value)
+void
+avr_irq_unregister_notify(
+ avr_irq_t * irq,
+ avr_irq_notify_t notify,
+ void * param)
{
- if (!irq || irq->value == value)
+ avr_irq_hook_t *hook, *prev;
+ if (!irq || !notify)
+ return;
+
+ hook = irq->hook;
+ prev = NULL;
+ while (hook) {
+ if (hook->notify == notify && hook->param == param) {
+ if ( prev )
+ prev->next = hook->next;
+ else
+ irq->hook = hook->next;
+ free(hook);
+ return;
+ }
+ prev = hook;
+ hook = hook->next;
+ }
+}
+
+void
+avr_raise_irq(
+ avr_irq_t * irq,
+ uint32_t value)
+{
+ if (!irq)
return ;
+ uint32_t output = (irq->flags & IRQ_FLAG_NOT) ? !value : value;
+ // if value is the same but it's the first time, raise it anyway
+ if (irq->value == output &&
+ (irq->flags & IRQ_FLAG_FILTERED) && !(irq->flags & IRQ_FLAG_INIT))
+ return;
+ irq->flags &= ~IRQ_FLAG_INIT;
avr_irq_hook_t *hook = irq->hook;
while (hook) {
- if (hook->notify) {
+ avr_irq_hook_t * next = hook->next;
// prevents reentrance / endless calling loops
- if (hook->busy == 0) {
- hook->busy++;
- hook->notify(irq, value, hook->param);
- hook->busy--;
- }
- }
- hook = hook->next;
+ if (hook->busy == 0) {
+ hook->busy++;
+ if (hook->notify)
+ hook->notify(irq, output, hook->param);
+ if (hook->chain)
+ avr_raise_irq(hook->chain, output);
+ hook->busy--;
+ }
+ hook = next;
}
- irq->value = value;
+ // the value is set after the callbacks are called, so the callbacks
+ // can themselves compare for old/new values between their parameter
+ // they are passed (new value) and the previous irq->value
+ irq->value = output;
}
-static void _avr_irq_connect(avr_irq_t * irq, uint32_t value, void * param)
+void
+avr_connect_irq(
+ avr_irq_t * src,
+ avr_irq_t * dst)
{
- avr_irq_t * dst = (avr_irq_t*)param;
- avr_raise_irq(dst, value);
+ if (!src || !dst || src == dst) {
+ printf("avr_connect_irq invalid irq %p/%p", src, dst);
+ return;
+ }
+ avr_irq_hook_t *hook = src->hook;
+ while (hook) {
+ if (hook->chain == dst)
+ return; // already there
+ hook = hook->next;
+ }
+ hook = _avr_alloc_irq_hook(src);
+ hook->chain = dst;
}
-void avr_connect_irq(avr_irq_t * src, avr_irq_t * dst)
+void
+avr_unconnect_irq(
+ avr_irq_t * src,
+ avr_irq_t * dst)
{
- avr_irq_register_notify(src, _avr_irq_connect, dst);
+ avr_irq_hook_t *hook, *prev;
+
+ if (!src || !dst || src == dst) {
+ printf("error: avr_connect_irq invalid irq %p/%p", src, dst); fflush(stdout);
+ return;
+ }
+ hook = src->hook;
+ prev = NULL;
+ while (hook) {
+ if (hook->chain == dst) {
+ if ( prev )
+ prev->next = hook->next;
+ else
+ src->hook = hook->next;
+ free(hook);
+ return;
+ }
+ prev = hook;
+ hook = hook->next;
+ }
}