a2710f638081fe36c44aee0bd2997cb6c02bc74f
[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 v = avr->data[addr];
29
30         if (addr == p->r_pin) {
31                 uint8_t ddr = avr->data[p->r_ddr];
32                 uint8_t v = (avr->data[p->r_pin] & ~ddr) | (avr->data[p->r_port] & ddr);
33                 avr->data[addr] = v;
34                 // made to trigger potential watchpoints
35                 v = avr_core_watch_read(avr, addr);
36 //              printf("** PIN%c(%02x) = %02x\n", p->name, addr, v);
37         }
38         return v;
39 }
40
41 static void avr_ioport_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
42 {
43         avr_ioport_t * p = (avr_ioport_t *)param;
44         uint8_t oldv = avr->data[addr];
45
46         if (addr == p->r_port) {
47
48                 avr_core_watch_write(avr, addr, v);
49                 if (v != oldv) {
50                         //      printf("PORT%c(%02x) = %02x (was %02x)\n", p->name, addr, v, oldv);
51                         int mask = v ^ oldv;
52
53                         // raise the internal IRQ callbacks
54                         for (int i = 0; i < 8; i++)
55                                 if (mask & (1 << i))
56                                         avr_raise_irq(p->io.irq + i, (v >> i) & 1);
57                         avr_raise_irq(p->io.irq + IOPORT_IRQ_PIN_ALL, v);
58                 }
59         }
60 }
61
62 /*
63  * this is our "main" pin change callback, it can be triggered by either the
64  * AVR code, or any external piece of code that see fit to do it.
65  * Either way, this will raise pin change interrupts, if needed
66  */
67 void avr_ioport_irq_notify(struct avr_irq_t * irq, uint32_t value, void * param)
68 {
69         avr_ioport_t * p = (avr_ioport_t *)param;
70         avr_t * avr = p->io.avr;
71
72         int output = value & 0x100;
73         value &= 0xff;
74         uint8_t mask = 1 << irq->irq;
75                 // set the real PIN bit. ddr doesn't matter here as it's masked when read.
76         avr->data[p->r_pin] &= ~mask;
77         if (value)
78                 avr->data[p->r_pin] |= mask;
79
80         if (output)     // if the IRQ was marked as Output, also do the IO write
81                 avr_ioport_write(avr, p->r_port, (avr->data[p->r_port] & ~mask) | (value ? mask : 0), p);
82
83         if (p->r_pcint) {
84                 // if the pcint bit is on, try to raise it
85                 int raise = avr->data[p->r_pcint] & mask;
86                 if (raise)
87                         avr_raise_interrupt(avr, &p->pcint);
88         }
89 }
90
91 static void avr_ioport_reset(avr_io_t * port)
92 {
93         avr_ioport_t * p = (avr_ioport_t *)port;
94         for (int i = 0; i < IOPORT_IRQ_PIN_ALL; i++) 
95                 avr_irq_register_notify(p->io.irq + i, avr_ioport_irq_notify, p);
96 }
97
98 static int avr_ioport_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_param)
99 {
100         avr_ioport_t * p = (avr_ioport_t *)port;
101         int res = -1;
102
103         switch(ctl) {
104                 case AVR_IOCTL_IOPORT_GETIRQ_REGBIT: {
105                         avr_ioport_getirq_t * r = (avr_ioport_getirq_t*)io_param;
106
107                         if (r->bit.reg == p->r_port || r->bit.reg == p->r_pin || r->bit.reg == p->r_ddr) {
108                                 // it's us ! check the special case when the "all pins" irq is requested
109                                 int o = 0;
110                                 if (r->bit.mask == 0xff)
111                                         r->irq[o++] = &p->io.irq[IOPORT_IRQ_PIN_ALL];
112                                 else {
113                                         // otherwise fil up the ones needed
114                                         for (int bi = 0; bi < 8; bi++)
115                                                 if (r->bit.mask & (1 << bi))
116                                                         r->irq[o++] = &p->io.irq[r->bit.bit + bi];
117                                 }
118                                 if (o < 8)
119                                         r->irq[o] = NULL;
120                                 return o;
121                         }
122                 }       break;
123         }
124
125         return res;
126 }
127
128 static  avr_io_t        _io = {
129         .kind = "io",
130         .reset = avr_ioport_reset,
131         .ioctl = avr_ioport_ioctl,
132 };
133
134 void avr_ioport_init(avr_t * avr, avr_ioport_t * p)
135 {
136         p->io = _io;
137 //      printf("%s PIN%c 0x%02x DDR%c 0x%02x PORT%c 0x%02x\n", __FUNCTION__,
138 //              p->name, p->r_pin,
139 //              p->name, p->r_ddr,
140 //              p->name, p->r_port);
141
142         // allocate this module's IRQ
143         p->io.irq_count = IOPORT_IRQ_COUNT;
144         p->io.irq = avr_alloc_irq(0, p->io.irq_count);
145         p->io.irq_ioctl_get = AVR_IOCTL_IOPORT_GETIRQ(p->name);
146         
147         avr_register_io(avr, &p->io);
148         avr_register_vector(avr, &p->pcint);
149
150         avr_register_io_write(avr, p->r_port, avr_ioport_write, p);
151         avr_register_io_read(avr, p->r_pin, avr_ioport_read, p);
152 }
153