X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=simavr%2Fsim%2Favr_ioport.c;h=24a8a9bd5d6b43497e021304389edc652ad06676;hb=a367ebb0e3cb1b8325d4a0e6a8d616238852a7d4;hp=6797a97e2b65f698e5e0bb16a49ae3a9a02f719c;hpb=1898613e4ff3926250bc98e9917fc57b078f48f0;p=simavr diff --git a/simavr/sim/avr_ioport.c b/simavr/sim/avr_ioport.c index 6797a97..24a8a9b 100644 --- a/simavr/sim/avr_ioport.c +++ b/simavr/sim/avr_ioport.c @@ -22,41 +22,62 @@ #include #include "avr_ioport.h" -static uint8_t avr_ioport_read(struct avr_t * avr, uint8_t addr, void * param) +static uint8_t avr_ioport_read(struct avr_t * avr, avr_io_addr_t addr, void * param) { avr_ioport_t * p = (avr_ioport_t *)param; - uint8_t v = avr->data[addr]; - - if (addr == p->r_pin) { - uint8_t ddr = avr->data[p->r_ddr]; - uint8_t v = (avr->data[p->r_pin] & ~ddr) | (avr->data[p->r_port] & ddr); - avr->data[addr] = v; - // made to trigger potential watchpoints - v = avr_core_watch_read(avr, addr); + uint8_t ddr = avr->data[p->r_ddr]; + uint8_t v = (avr->data[p->r_pin] & ~ddr) | (avr->data[p->r_port] & ddr); + avr->data[addr] = v; + // made to trigger potential watchpoints + v = avr_core_watch_read(avr, addr); // printf("** PIN%c(%02x) = %02x\n", p->name, addr, v); - } + return v; } -static void avr_ioport_write(struct avr_t * avr, uint8_t addr, uint8_t v, void * param) +static void avr_ioport_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) { avr_ioport_t * p = (avr_ioport_t *)param; - uint8_t oldv = avr->data[addr]; - if (addr == p->r_port) { + avr_core_watch_write(avr, addr, v); + // printf("PORT%c(%02x) = %02x (was %02x)\n", p->name, addr, v, oldv); - avr_core_watch_write(avr, addr, v); - if (v != oldv) { - // printf("PORT%c(%02x) = %02x (was %02x)\n", p->name, addr, v, oldv); - int mask = v ^ oldv; + // raise the internal IRQ callbacks + for (int i = 0; i < 8; i++) + avr_raise_irq(p->io.irq + i, (v >> i) & 1); + avr_raise_irq(p->io.irq + IOPORT_IRQ_PIN_ALL, v); +} - // raise the internal IRQ callbacks - for (int i = 0; i < 8; i++) - if (mask & (1 << i)) - avr_raise_irq(p->io.irq + i, (v >> i) & 1); - avr_raise_irq(p->io.irq + IOPORT_IRQ_PIN_ALL, v); - } - } +/* + * This is a reasonably new behaviour for the io-ports. Writing 1's to the PIN register + * toggles the PORT equivalent bit (regardless of direction + */ +static void avr_ioport_pin_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) +{ + avr_ioport_t * p = (avr_ioport_t *)param; + + avr_ioport_write(avr, p->r_port, avr->data[p->r_port] ^ v, param); +} + +/* + * This is a the callback for the DDR register. There is nothing much to do here, apart + * from triggering an IRQ in case any 'client' code is interested in the information, + * and restoring all PIN bits marked as output to PORT values. + */ +static void avr_ioport_ddr_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) +{ + avr_ioport_t * p = (avr_ioport_t *)param; + + avr_raise_irq(p->io.irq + IOPORT_IRQ_DIRECTION_ALL, v); + avr_core_watch_write(avr, addr, v); + + const uint8_t oldpin = avr->data[p->r_pin]; + const uint8_t pin = (oldpin & ~v) | (avr->data[p->r_port] & v); + avr_core_watch_write(avr, p->r_pin, pin); + for (int i = 0; i < 8; i++) + if (((oldpin ^ pin) >> i) & 1) + avr_raise_irq(p->io.irq + i, (pin >> i) & 1); + avr_raise_irq(p->io.irq + IOPORT_IRQ_PIN_ALL, pin); } /* @@ -69,13 +90,18 @@ void avr_ioport_irq_notify(struct avr_irq_t * irq, uint32_t value, void * param) avr_ioport_t * p = (avr_ioport_t *)param; avr_t * avr = p->io.avr; - // printf("pcint port%c pcint %02x:%02x\n", p->name, p->r_pcint, avr->data[p->r_pcint]); - if (p->r_pcint) { - uint8_t mask = 1 << irq->irq; + int output = value & AVR_IOPORT_OUTPUT; + value &= 0xff; + uint8_t mask = 1 << irq->irq; // set the real PIN bit. ddr doesn't matter here as it's masked when read. - avr->data[p->r_pin] &= ~mask; - if (value) - avr->data[p->r_pin] |= mask; + avr->data[p->r_pin] &= ~mask; + if (value) + avr->data[p->r_pin] |= mask; + + if (output) // if the IRQ was marked as Output, also do the IO write + avr_ioport_write(avr, p->r_port, (avr->data[p->r_port] & ~mask) | (value ? mask : 0), p); + + if (p->r_pcint) { // if the pcint bit is on, try to raise it int raise = avr->data[p->r_pcint] & mask; if (raise) @@ -90,9 +116,71 @@ static void avr_ioport_reset(avr_io_t * port) avr_irq_register_notify(p->io.irq + i, avr_ioport_irq_notify, p); } +static int avr_ioport_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_param) +{ + avr_ioport_t * p = (avr_ioport_t *)port; + avr_t * avr = p->io.avr; + int res = -1; + + switch(ctl) { + case AVR_IOCTL_IOPORT_GETIRQ_REGBIT: { + avr_ioport_getirq_t * r = (avr_ioport_getirq_t*)io_param; + + if (r->bit.reg == p->r_port || r->bit.reg == p->r_pin || r->bit.reg == p->r_ddr) { + // it's us ! check the special case when the "all pins" irq is requested + int o = 0; + if (r->bit.mask == 0xff) + r->irq[o++] = &p->io.irq[IOPORT_IRQ_PIN_ALL]; + else { + // otherwise fill up the ones needed + for (int bi = 0; bi < 8; bi++) + if (r->bit.mask & (1 << bi)) + r->irq[o++] = &p->io.irq[r->bit.bit + bi]; + } + if (o < 8) + r->irq[o] = NULL; + return o; + } + } break; + default: { + /* + * Return the port state if the IOCTL matches us. + */ + if (ctl == AVR_IOCTL_IOPORT_GETSTATE(p->name)) { + avr_ioport_state_t state = { + .name = p->name, + .port = avr->data[p->r_port], + .ddr = avr->data[p->r_ddr], + .pin = avr->data[p->r_pin], + }; + if (io_param) + *((avr_ioport_state_t*)io_param) = state; + res = 0; + } + } + } + + return res; +} + +static const char * irq_names[IOPORT_IRQ_COUNT] = { + [IOPORT_IRQ_PIN0] = "=pin0", + [IOPORT_IRQ_PIN1] = "=pin1", + [IOPORT_IRQ_PIN2] = "=pin2", + [IOPORT_IRQ_PIN3] = "=pin3", + [IOPORT_IRQ_PIN4] = "=pin4", + [IOPORT_IRQ_PIN5] = "=pin5", + [IOPORT_IRQ_PIN6] = "=pin6", + [IOPORT_IRQ_PIN7] = "=pin7", + [IOPORT_IRQ_PIN_ALL] = "=all", + [IOPORT_IRQ_DIRECTION_ALL] = ">ddr", +}; + static avr_io_t _io = { - .kind = "io", + .kind = "port", .reset = avr_ioport_reset, + .ioctl = avr_ioport_ioctl, + .irq_names = irq_names, }; void avr_ioport_init(avr_t * avr, avr_ioport_t * p) @@ -102,16 +190,18 @@ void avr_ioport_init(avr_t * avr, avr_ioport_t * p) // p->name, p->r_pin, // p->name, p->r_ddr, // p->name, p->r_port); - - // allocate this module's IRQ - p->io.irq_count = IOPORT_IRQ_COUNT; - p->io.irq = avr_alloc_irq(0, p->io.irq_count); - p->io.irq_ioctl_get = AVR_IOCTL_IOPORT_GETIRQ(p->name); avr_register_io(avr, &p->io); avr_register_vector(avr, &p->pcint); + // allocate this module's IRQ + avr_io_setirqs(&p->io, AVR_IOCTL_IOPORT_GETIRQ(p->name), IOPORT_IRQ_COUNT, NULL); + + for (int i = 0; i < IOPORT_IRQ_COUNT; i++) + p->io.irq[i].flags |= IRQ_FLAG_FILTERED; avr_register_io_write(avr, p->r_port, avr_ioport_write, p); avr_register_io_read(avr, p->r_pin, avr_ioport_read, p); + avr_register_io_write(avr, p->r_pin, avr_ioport_pin_write, p); + avr_register_io_write(avr, p->r_ddr, avr_ioport_ddr_write, p); }