Many more changes, timed callbacks etc
[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, uint8_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, uint8_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         //      printf("pcint port%c pcint %02x:%02x\n", p->name, p->r_pcint, avr->data[p->r_pcint]);
73         if (p->r_pcint) {
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                 // if the pcint bit is on, try to raise it
80                 int raise = avr->data[p->r_pcint] & mask;
81                 if (raise)
82                         avr_raise_interrupt(avr, &p->pcint);
83         }
84 }
85
86 static void avr_ioport_reset(avr_io_t * port)
87 {
88         avr_ioport_t * p = (avr_ioport_t *)port;
89         for (int i = 0; i < IOPORT_IRQ_PIN_ALL; i++) 
90                 avr_irq_register_notify(p->io.irq + i, avr_ioport_irq_notify, p);
91 }
92
93 static  avr_io_t        _io = {
94         .kind = "io",
95         .reset = avr_ioport_reset,
96 };
97
98 void avr_ioport_init(avr_t * avr, avr_ioport_t * p)
99 {
100         p->io = _io;
101 //      printf("%s PIN%c 0x%02x DDR%c 0x%02x PORT%c 0x%02x\n", __FUNCTION__,
102 //              p->name, p->r_pin,
103 //              p->name, p->r_ddr,
104 //              p->name, p->r_port);
105
106         // allocate this module's IRQ
107         p->io.irq_count = IOPORT_IRQ_COUNT;
108         p->io.irq = avr_alloc_irq(0, p->io.irq_count);
109         p->io.irq_ioctl_get = AVR_IOCTL_IOPORT_GETIRQ(p->name);
110         
111         avr_register_io(avr, &p->io);
112         avr_register_vector(avr, &p->pcint);
113
114         avr_register_io_write(avr, p->r_port, avr_ioport_write, p);
115         avr_register_io_read(avr, p->r_pin, avr_ioport_read, p);
116 }
117