24a8a9bd5d6b43497e021304389edc652ad06676
[simavr] / simavr / sim / avr_ioport.c
1 /*
2         avr_ioport.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 <stdio.h>
23 #include "avr_ioport.h"
24
25 static uint8_t avr_ioport_read(struct avr_t * avr, avr_io_addr_t addr, void * param)
26 {
27         avr_ioport_t * p = (avr_ioport_t *)param;
28         uint8_t ddr = avr->data[p->r_ddr];
29         uint8_t v = (avr->data[p->r_pin] & ~ddr) | (avr->data[p->r_port] & ddr);
30         avr->data[addr] = v;
31         // made to trigger potential watchpoints
32         v = avr_core_watch_read(avr, addr);
33 //              printf("** PIN%c(%02x) = %02x\n", p->name, addr, v);
34
35         return v;
36 }
37
38 static void avr_ioport_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
39 {
40         avr_ioport_t * p = (avr_ioport_t *)param;
41
42         avr_core_watch_write(avr, addr, v);
43         //      printf("PORT%c(%02x) = %02x (was %02x)\n", p->name, addr, v, oldv);
44
45         // raise the internal IRQ callbacks
46         for (int i = 0; i < 8; i++)
47                 avr_raise_irq(p->io.irq + i, (v >> i) & 1);
48         avr_raise_irq(p->io.irq + IOPORT_IRQ_PIN_ALL, v);
49 }
50
51 /*
52  * This is a reasonably new behaviour for the io-ports. Writing 1's to the PIN register
53  * toggles the PORT equivalent bit (regardless of direction
54  */
55 static void avr_ioport_pin_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
56 {
57         avr_ioport_t * p = (avr_ioport_t *)param;
58
59         avr_ioport_write(avr, p->r_port, avr->data[p->r_port] ^ v, param);
60 }
61
62 /*
63  * This is a the callback for the DDR register. There is nothing much to do here, apart
64  * from triggering an IRQ in case any 'client' code is interested in the information,
65  * and restoring all PIN bits marked as output to PORT values.
66  */
67 static void avr_ioport_ddr_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
68 {
69         avr_ioport_t * p = (avr_ioport_t *)param;
70
71         avr_raise_irq(p->io.irq + IOPORT_IRQ_DIRECTION_ALL, v);
72         avr_core_watch_write(avr, addr, v);
73
74         const uint8_t oldpin = avr->data[p->r_pin];
75         const uint8_t pin = (oldpin & ~v) | (avr->data[p->r_port] & v);
76         avr_core_watch_write(avr, p->r_pin, pin);
77         for (int i = 0; i < 8; i++)
78                 if (((oldpin ^ pin) >> i) & 1)
79                         avr_raise_irq(p->io.irq + i, (pin >> i) & 1);
80         avr_raise_irq(p->io.irq + IOPORT_IRQ_PIN_ALL, pin);
81 }
82
83 /*
84  * this is our "main" pin change callback, it can be triggered by either the
85  * AVR code, or any external piece of code that see fit to do it.
86  * Either way, this will raise pin change interrupts, if needed
87  */
88 void avr_ioport_irq_notify(struct avr_irq_t * irq, uint32_t value, void * param)
89 {
90         avr_ioport_t * p = (avr_ioport_t *)param;
91         avr_t * avr = p->io.avr;
92
93         int output = value & AVR_IOPORT_OUTPUT;
94         value &= 0xff;
95         uint8_t mask = 1 << irq->irq;
96                 // set the real PIN bit. ddr doesn't matter here as it's masked when read.
97         avr->data[p->r_pin] &= ~mask;
98         if (value)
99                 avr->data[p->r_pin] |= mask;
100
101         if (output)     // if the IRQ was marked as Output, also do the IO write
102                 avr_ioport_write(avr, p->r_port, (avr->data[p->r_port] & ~mask) | (value ? mask : 0), p);
103
104         if (p->r_pcint) {
105                 // if the pcint bit is on, try to raise it
106                 int raise = avr->data[p->r_pcint] & mask;
107                 if (raise)
108                         avr_raise_interrupt(avr, &p->pcint);
109         }
110 }
111
112 static void avr_ioport_reset(avr_io_t * port)
113 {
114         avr_ioport_t * p = (avr_ioport_t *)port;
115         for (int i = 0; i < IOPORT_IRQ_PIN_ALL; i++) 
116                 avr_irq_register_notify(p->io.irq + i, avr_ioport_irq_notify, p);
117 }
118
119 static int avr_ioport_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_param)
120 {
121         avr_ioport_t * p = (avr_ioport_t *)port;
122         avr_t * avr = p->io.avr;
123         int res = -1;
124
125         switch(ctl) {
126                 case AVR_IOCTL_IOPORT_GETIRQ_REGBIT: {
127                         avr_ioport_getirq_t * r = (avr_ioport_getirq_t*)io_param;
128
129                         if (r->bit.reg == p->r_port || r->bit.reg == p->r_pin || r->bit.reg == p->r_ddr) {
130                                 // it's us ! check the special case when the "all pins" irq is requested
131                                 int o = 0;
132                                 if (r->bit.mask == 0xff)
133                                         r->irq[o++] = &p->io.irq[IOPORT_IRQ_PIN_ALL];
134                                 else {
135                                         // otherwise fill up the ones needed
136                                         for (int bi = 0; bi < 8; bi++)
137                                                 if (r->bit.mask & (1 << bi))
138                                                         r->irq[o++] = &p->io.irq[r->bit.bit + bi];
139                                 }
140                                 if (o < 8)
141                                         r->irq[o] = NULL;
142                                 return o;
143                         }
144                 }       break;
145                 default: {
146                         /*
147                          * Return the port state if the IOCTL matches us.
148                          */
149                         if (ctl == AVR_IOCTL_IOPORT_GETSTATE(p->name)) {
150                                 avr_ioport_state_t state = {
151                                         .name = p->name,
152                                         .port = avr->data[p->r_port],
153                                         .ddr = avr->data[p->r_ddr],
154                                         .pin = avr->data[p->r_pin],
155                                 };
156                                 if (io_param)
157                                         *((avr_ioport_state_t*)io_param) = state;
158                                 res = 0;
159                         }
160                 }
161         }
162
163         return res;
164 }
165
166 static const char * irq_names[IOPORT_IRQ_COUNT] = {
167         [IOPORT_IRQ_PIN0] = "=pin0",
168         [IOPORT_IRQ_PIN1] = "=pin1",
169         [IOPORT_IRQ_PIN2] = "=pin2",
170         [IOPORT_IRQ_PIN3] = "=pin3",
171         [IOPORT_IRQ_PIN4] = "=pin4",
172         [IOPORT_IRQ_PIN5] = "=pin5",
173         [IOPORT_IRQ_PIN6] = "=pin6",
174         [IOPORT_IRQ_PIN7] = "=pin7",
175         [IOPORT_IRQ_PIN_ALL] = "=all",
176         [IOPORT_IRQ_DIRECTION_ALL] = ">ddr",
177 };
178
179 static  avr_io_t        _io = {
180         .kind = "port",
181         .reset = avr_ioport_reset,
182         .ioctl = avr_ioport_ioctl,
183         .irq_names = irq_names,
184 };
185
186 void avr_ioport_init(avr_t * avr, avr_ioport_t * p)
187 {
188         p->io = _io;
189 //      printf("%s PIN%c 0x%02x DDR%c 0x%02x PORT%c 0x%02x\n", __FUNCTION__,
190 //              p->name, p->r_pin,
191 //              p->name, p->r_ddr,
192 //              p->name, p->r_port);
193         
194         avr_register_io(avr, &p->io);
195         avr_register_vector(avr, &p->pcint);
196         // allocate this module's IRQ
197         avr_io_setirqs(&p->io, AVR_IOCTL_IOPORT_GETIRQ(p->name), IOPORT_IRQ_COUNT, NULL);
198
199         for (int i = 0; i < IOPORT_IRQ_COUNT; i++)
200                 p->io.irq[i].flags |= IRQ_FLAG_FILTERED;
201
202         avr_register_io_write(avr, p->r_port, avr_ioport_write, p);
203         avr_register_io_read(avr, p->r_pin, avr_ioport_read, p);
204         avr_register_io_write(avr, p->r_pin, avr_ioport_pin_write, p);
205         avr_register_io_write(avr, p->r_ddr, avr_ioport_ddr_write, p);
206 }
207