ioport: No longer need pcint to change the value
[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         uint8_t mask = 1 << irq->irq;
73                 // set the real PIN bit. ddr doesn't matter here as it's masked when read.
74         avr->data[p->r_pin] &= ~mask;
75         if (value)
76                 avr->data[p->r_pin] |= mask;
77         if (p->r_pcint) {
78                 // if the pcint bit is on, try to raise it
79                 int raise = avr->data[p->r_pcint] & mask;
80                 if (raise)
81                         avr_raise_interrupt(avr, &p->pcint);
82         }
83 }
84
85 static void avr_ioport_reset(avr_io_t * port)
86 {
87         avr_ioport_t * p = (avr_ioport_t *)port;
88         for (int i = 0; i < IOPORT_IRQ_PIN_ALL; i++) 
89                 avr_irq_register_notify(p->io.irq + i, avr_ioport_irq_notify, p);
90 }
91
92 static  avr_io_t        _io = {
93         .kind = "io",
94         .reset = avr_ioport_reset,
95 };
96
97 void avr_ioport_init(avr_t * avr, avr_ioport_t * p)
98 {
99         p->io = _io;
100 //      printf("%s PIN%c 0x%02x DDR%c 0x%02x PORT%c 0x%02x\n", __FUNCTION__,
101 //              p->name, p->r_pin,
102 //              p->name, p->r_ddr,
103 //              p->name, p->r_port);
104
105         // allocate this module's IRQ
106         p->io.irq_count = IOPORT_IRQ_COUNT;
107         p->io.irq = avr_alloc_irq(0, p->io.irq_count);
108         p->io.irq_ioctl_get = AVR_IOCTL_IOPORT_GETIRQ(p->name);
109         
110         avr_register_io(avr, &p->io);
111         avr_register_vector(avr, &p->pcint);
112
113         avr_register_io_write(avr, p->r_port, avr_ioport_write, p);
114         avr_register_io_read(avr, p->r_pin, avr_ioport_read, p);
115 }
116