X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=simavr%2Fsim%2Favr_ioport.c;h=24a8a9bd5d6b43497e021304389edc652ad06676;hb=a367ebb0e3cb1b8325d4a0e6a8d616238852a7d4;hp=157a624236dc948b6b319603b3e50259c16f0654;hpb=d8e5774323d5408e119b5fa3cce1c73c7345e8f7;p=simavr diff --git a/simavr/sim/avr_ioport.c b/simavr/sim/avr_ioport.c index 157a624..24a8a9b 100644 --- a/simavr/sim/avr_ioport.c +++ b/simavr/sim/avr_ioport.c @@ -22,81 +22,186 @@ #include #include "avr_ioport.h" -static void avr_ioport_run(avr_t * avr, avr_io_t * port) +static uint8_t avr_ioport_read(struct avr_t * avr, avr_io_addr_t addr, void * param) { - //printf("%s\n", __FUNCTION__); + avr_ioport_t * p = (avr_ioport_t *)param; + 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 uint8_t avr_ioport_read(struct avr_t * avr, uint8_t addr, 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 v = avr->data[addr]; - if (addr == p->r_pin) { - uint8_t v = avr->data[p->r_port]; - 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; + avr_core_watch_write(avr, addr, v); + // printf("PORT%c(%02x) = %02x (was %02x)\n", p->name, addr, 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); } -static void avr_ioport_write(struct avr_t * avr, uint8_t addr, uint8_t v, void * param) +/* + * 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; - uint8_t oldv = avr->data[addr]; - if (addr == p->r_port) { - // printf("PORT%c(%02x) = %02x (was %02x)\n", p->name, addr, v, oldv); + avr_ioport_write(avr, p->r_port, avr->data[p->r_port] ^ v, param); +} - avr_core_watch_write(avr, addr, v); - if (v != oldv) { - int raise = 1; - int mask = v ^ oldv; - if (p->r_pcint) - raise = avr->data[p->r_pcint] & mask; - if (raise) - avr_raise_interupt(avr, &p->pcint); - } +/* + * 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); - if (p->name == 'D') { - static int cs = -1; - if ((oldv & 0xf0) != (v & 0xf0)) { - for (int i = 0; i < 4; i++) { - + 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); +} + +/* + * this is our "main" pin change callback, it can be triggered by either the + * AVR code, or any external piece of code that see fit to do it. + * Either way, this will raise pin change interrupts, if needed + */ +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; + + 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; + + 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) + avr_raise_interrupt(avr, &p->pcint); + } +} + +static void avr_ioport_reset(avr_io_t * port) +{ + avr_ioport_t * p = (avr_ioport_t *)port; + for (int i = 0; i < IOPORT_IRQ_PIN_ALL; i++) + 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; } } } -} -static void avr_ioport_reset(avr_t * avr, avr_io_t * port) -{ + 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", - .run = avr_ioport_run, + .kind = "port", .reset = avr_ioport_reset, + .ioctl = avr_ioport_ioctl, + .irq_names = irq_names, }; -void avr_ioport_init(avr_t * avr, avr_ioport_t * port) +void avr_ioport_init(avr_t * avr, avr_ioport_t * p) { - port->io = _io; - printf("%s PIN%c 0x%02x DDR%c 0x%02x PORT%c 0x%02x\n", - __FUNCTION__, - port->name, port->r_pin, - port->name, port->r_ddr, - port->name, port->r_port); - - avr_register_io(avr, &port->io); - avr_register_vector(avr, &port->pcint); - - avr_register_io_write(avr, port->r_port, avr_ioport_write, port); - avr_register_io_read(avr, port->r_pin, avr_ioport_read, port); + p->io = _io; +// printf("%s PIN%c 0x%02x DDR%c 0x%02x PORT%c 0x%02x\n", __FUNCTION__, +// p->name, p->r_pin, +// p->name, p->r_ddr, +// p->name, p->r_port); + + 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); }