92c2c17b394b509986e12f29a696ad49f147cd3c
[simavr] / simavr / sim / sim_irq.c
1 /*
2         sim_irq.c
3
4         Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
5
6         This file is part of simavr.
7
8         simavr is free software: you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation, either version 3 of the License, or
11         (at your option) any later version.
12
13         simavr is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with simavr.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include "sim_irq.h"
26
27 // internal structure for a hook, never seen by the notify procs
28 typedef struct avr_irq_hook_t {
29         struct avr_irq_hook_t * next;
30         int busy;       // prevent reentrance of callbacks
31         
32         struct avr_irq_t * chain;       // raise the IRQ on this too - optional if "notify" is on
33         avr_irq_notify_t notify;        // called when IRQ is raised - optional if "chain" is on
34         void * param;                           // "notify" parameter
35 } avr_irq_hook_t;
36
37 void avr_init_irq(avr_irq_t * irq, uint32_t base, uint32_t count)
38 {
39         memset(irq, 0, sizeof(avr_irq_t) * count);
40
41         for (int i = 0; i < count; i++)
42                 irq[i].irq = base + i;
43 }
44
45 avr_irq_t * avr_alloc_irq(uint32_t base, uint32_t count)
46 {
47         avr_irq_t * irq = (avr_irq_t*)malloc(sizeof(avr_irq_t) * count);
48         avr_init_irq(irq, base, count);
49         for (int i = 0; i < count; i++)
50                 irq[i].flags |= IRQ_FLAG_ALLOC; 
51         return irq;
52 }
53
54 static avr_irq_hook_t * _avr_alloc_irq_hook(avr_irq_t * irq)
55 {
56         avr_irq_hook_t *hook = malloc(sizeof(avr_irq_hook_t));
57         memset(hook, 0, sizeof(avr_irq_hook_t));
58         hook->next = irq->hook;
59         irq->hook = hook;
60         return hook;
61 }
62
63 void avr_free_irq(avr_irq_t * irq, uint32_t count)
64 {
65         if (!irq || !count)
66                 return;
67         for (int i = 0; i < count; i++) {
68                 // purge hooks
69                 avr_irq_hook_t *hook = irq->hook;
70                 while (hook) {
71                         avr_irq_hook_t * next = hook->next;
72                         free(hook);
73                         hook = next;
74                 }
75                 irq->hook = NULL;
76         }
77         // if that irq list was allocated by us, free it
78         if (irq->flags & IRQ_FLAG_ALLOC)
79                 free(irq);
80 }
81
82 void avr_irq_register_notify(avr_irq_t * irq, avr_irq_notify_t notify, void * param)
83 {
84         if (!irq || !notify)
85                 return;
86         
87         avr_irq_hook_t *hook = irq->hook;
88         while (hook) {
89                 if (hook->notify == notify && hook->param == param)
90                         return; // already there
91                 hook = hook->next;
92         }
93         hook = _avr_alloc_irq_hook(irq);
94         hook->notify = notify;
95         hook->param = param;
96 }
97
98 void avr_raise_irq(avr_irq_t * irq, uint32_t value)
99 {
100         if (!irq)
101                 return ;
102         uint32_t output = (irq->flags & IRQ_FLAG_NOT) ? !value : value;
103         if (irq->value == output && (irq->flags & IRQ_FLAG_FILTERED))
104                 return;
105         avr_irq_hook_t *hook = irq->hook;
106         while (hook) {
107                 avr_irq_hook_t * next = hook->next;
108                         // prevents reentrance / endless calling loops
109                 if (hook->busy == 0) {
110                         hook->busy++;
111                         if (hook->notify)
112                                 hook->notify(irq, output,  hook->param);
113                         if (hook->chain)
114                                 avr_raise_irq(hook->chain, output);
115                         hook->busy--;
116                 }                       
117                 hook = next;
118         }
119         // the value is set after the callbacks are called, so the callbacks
120         // can themselves compare for old/new values between their parameter
121         // they are passed (new value) and the previous irq->value
122         irq->value = output;
123 }
124
125 void avr_connect_irq(avr_irq_t * src, avr_irq_t * dst)
126 {
127         if (!src || !dst || src == dst) {
128                 printf("avr_connect_irq invalid irq %p/%p", src, dst);
129                 return;
130         }
131         avr_irq_hook_t *hook = src->hook;
132         while (hook) {
133                 if (hook->chain == dst)
134                         return; // already there
135                 hook = hook->next;
136         }
137         hook = _avr_alloc_irq_hook(src);
138         hook->chain = dst;
139 }