7edb7865580a9a3bdca7b0978e6f6437b5445382
[simavr] / simavr / sim / sim_io.c
1 /*
2         sim_io.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
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <stdint.h>
28 #include "sim_io.h"
29
30 int
31 avr_ioctl(
32                 avr_t *avr,
33                 uint32_t ctl,
34                 void * io_param)
35 {
36         avr_io_t * port = avr->io_port;
37         int res = -1;
38         while (port && res == -1) {
39                 if (port->ioctl)
40                         res = port->ioctl(port, ctl, io_param);
41                 port = port->next;
42         }
43         return res;
44 }
45
46 void
47 avr_register_io(
48                 avr_t *avr,
49                 avr_io_t * io)
50 {
51         io->next = avr->io_port;
52         io->avr = avr;
53         avr->io_port = io;
54 }
55
56 void
57 avr_register_io_read(
58                 avr_t *avr,
59                 avr_io_addr_t addr,
60                 avr_io_read_t readp,
61                 void * param)
62 {
63         avr_io_addr_t a = AVR_DATA_TO_IO(addr);
64         if (avr->io[a].r.param || avr->io[a].r.c) {
65                 if (avr->io[a].r.param != param || avr->io[a].r.c != readp) {
66                         fprintf(stderr,
67                                         "Error: avr_register_io_read(): Already registered, refusing to override.\n");
68                         fprintf(stderr,
69                                         "Error: avr_register_io_read(%04x : %p/%p): %p/%p\n", a,
70                                         avr->io[a].r.c, avr->io[a].r.param, readp, param);
71                         abort();
72                 }
73         }
74         avr->io[a].r.param = param;
75         avr->io[a].r.c = readp;
76 }
77
78 static void
79 _avr_io_mux_write(
80                 avr_t * avr,
81                 avr_io_addr_t addr,
82                 uint8_t v,
83                 void * param)
84 {
85         int io = (intptr_t)param;
86         for (int i = 0; i < avr->io_shared_io[io].used; i++) {
87                 avr_io_write_t c = avr->io_shared_io[io].io[i].c;
88                 if (c)
89                         c(avr, addr, v, avr->io_shared_io[io].io[i].param);
90         }
91 }
92
93 void
94 avr_register_io_write(
95                 avr_t *avr,
96                 avr_io_addr_t addr,
97                 avr_io_write_t writep,
98                 void * param)
99 {
100         avr_io_addr_t a = AVR_DATA_TO_IO(addr);
101
102         if (a >= MAX_IOs) {
103                 fprintf(stderr,
104                                 "Error: avr_register_io_write(): IO address 0x%04x out of range (max 0x%04x).\n",
105                                         a, MAX_IOs);
106                 abort();
107         }
108         /*
109          * Verifying that some other piece of code is not installed to watch write
110          * on this address. If there is, this code installs a "dispatcher" callback
111          * instead to handle multiple clients, otherwise, it continues as usual
112          */
113         if (avr->io[a].w.param || avr->io[a].w.c) {
114                 if (avr->io[a].w.param != param || avr->io[a].w.c != writep) {
115                         // if the muxer not already installed, allocate a new slot
116                         if (avr->io[a].w.c != _avr_io_mux_write) {
117                                 int no = avr->io_shared_io_count++;
118                                 if (avr->io_shared_io_count > 4) {
119                                         fprintf(stderr,
120                                                         "Error: avr_register_io_write(): Too many shared IO registers.\n");
121                                         abort();
122                                 }
123                                 fprintf(stderr,
124                                                 "Note: avr_register_io_write(%04x): Installing muxer on register.\n", addr);
125                                 avr->io_shared_io[no].used = 1;
126                                 avr->io_shared_io[no].io[0].param = avr->io[a].w.param;
127                                 avr->io_shared_io[no].io[0].c = avr->io[a].w.c;
128                                 avr->io[a].w.param = (void*)(intptr_t)no;
129                                 avr->io[a].w.c = _avr_io_mux_write;
130                         }
131                         int no = (intptr_t)avr->io[a].w.param;
132                         int d = avr->io_shared_io[no].used++;
133                         if (avr->io_shared_io[no].used > 4) {
134                                 fprintf(stderr,
135                                                 "Error: avr_register_io_write(): Too many callbacks on %04x.\n", addr);
136                                 abort();
137                         }
138                         avr->io_shared_io[no].io[d].param = param;
139                         avr->io_shared_io[no].io[d].c = writep;
140                 }
141         }
142
143         avr->io[a].w.param = param;
144         avr->io[a].w.c = writep;
145 }
146
147 avr_irq_t *
148 avr_io_getirq(
149                 avr_t * avr,
150                 uint32_t ctl,
151                 int index)
152 {
153         avr_io_t * port = avr->io_port;
154         while (port) {
155                 if (port->irq && port->irq_ioctl_get == ctl && port->irq_count > index)
156                         return port->irq + index;
157                 port = port->next;
158         }
159         return NULL;
160         
161 }
162
163 avr_irq_t *
164 avr_iomem_getirq(
165                 avr_t * avr,
166                 avr_io_addr_t addr,
167                 int index)
168 {
169         avr_io_addr_t a = AVR_DATA_TO_IO(addr);
170         if (avr->io[a].irq == NULL) {
171                 avr->io[a].irq = avr_alloc_irq(&avr->irq_pool, 0, 9, NULL);
172                 // mark the pin ones as filtered, so they only are raised when changing
173                 for (int i = 0; i < 8; i++)
174                         avr->io[a].irq[i].flags |= IRQ_FLAG_FILTERED;
175         }
176         return index < 9 ? avr->io[a].irq + index : NULL;
177 }
178
179 avr_irq_t *
180 avr_io_setirqs(
181                 avr_io_t * io,
182                 uint32_t ctl,
183                 int count,
184                 avr_irq_t * irqs )
185 {
186         // allocate this module's IRQ
187         io->irq_count = count;
188
189         if (!irqs) {
190                 const char ** irq_names = NULL;
191
192                 if (io->irq_names) {
193                         irq_names = malloc(count * sizeof(char*));
194                         memset(irq_names, 0, count * sizeof(char*));
195                         char buf[64];
196                         for (int i = 0; i < count; i++) {
197                                 /*
198                                  * this bit takes the io module 'kind' ("port")
199                                  * the IRQ name ("=0") and the last character of the ioctl ('p','o','r','A')
200                                  * to create a full name "=porta.0"
201                                  */
202                                 char * dst = buf;
203                                 // copy the 'flags' of the name out
204                                 const char * kind = io->irq_names[i];
205                                 while (!isalpha(*kind))
206                                         *dst++ = *kind++;
207                                 // add avr name
208 //                              strcpy(dst, io->avr->mmcu);
209                                 strcpy(dst, "avr");
210                                 dst += strlen(dst);
211                                 *dst ++ = '.';
212                                 // add module 'kind'
213                                 strcpy(dst, io->kind);
214                                 dst += strlen(dst);
215                                 // add port name, if any
216                                 if ((ctl & 0xff) > ' ')
217                                         *dst ++ = tolower(ctl & 0xff);
218                                 *dst ++ = '.';
219                                 // add the rest of the irq name
220                                 strcpy(dst, kind);
221                                 dst += strlen(dst);
222                                 *dst = 0;
223
224 //                              printf("%s\n", buf);
225                                 irq_names[i] = strdup(buf);
226                         }
227                 }
228                 irqs = avr_alloc_irq(&io->avr->irq_pool, 0,
229                                                 count, irq_names);
230                 if (irq_names) {
231                         for (int i = 0; i < count; i++)
232                                 free((char*)irq_names[i]);
233                         free((char*)irq_names);
234                 }
235         }
236
237         io->irq = irqs;
238         io->irq_ioctl_get = ctl;
239         return io->irq;
240 }
241
242 static void
243 avr_deallocate_io(
244                 avr_io_t * io)
245 {
246         if (io->dealloc)
247                 io->dealloc(io);
248         avr_free_irq(io->irq, io->irq_count);
249         io->irq_count = 0;
250         io->irq_ioctl_get = 0;
251         io->avr = NULL;
252         io->next = NULL;
253 }
254
255 void
256 avr_deallocate_ios(
257                 avr_t * avr)
258 {
259         avr_io_t * port = avr->io_port;
260         while (port) {
261                 avr_io_t * next = port->next;
262                 avr_deallocate_io(port);
263                 port = next;
264         }
265         avr->io_port = NULL;
266 }