From: Michel Pollet Date: Mon, 30 Nov 2009 21:36:55 +0000 (+0000) Subject: Cores, decoder, uart, ioports - lots of changes X-Git-Tag: v1.0a1~76 X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=6dc37c42aa8c60ee6e41f20718c858e1374dd2c6;p=sx%2Fsimavr.git Cores, decoder, uart, ioports - lots of changes + Cores now use eeprom declare macro + Cores now use the "TXCE" etc bits + Uart now raise TXC interupt/flag + ioports now use new internal IRQ system + New command line options for mcu, freq and trace + Decoder updates: - Fixed the last known "crash" bug. - Added cycles to most multi-cycle opcodes. - Added optional stack frame watcher - Skip instruction now handle 32 bits skips Signed-off-by: Michel Pollet --- diff --git a/simavr/Makefile b/simavr/Makefile index b076154..ee25d00 100644 --- a/simavr/Makefile +++ b/simavr/Makefile @@ -45,7 +45,7 @@ all: obj ${target} obj: @mkdir -p obj -obj/sim_%.o : cores/sim_%.h cores/sim_core_declare.h sim/*.h +obj/sim_%.o : cores/sim_%.h ${wildcard cores/*.h} ${wildcard sim/*.h} obj/sim_%.o : cores/sim_%.c @gcc $(CFLAGS) \ -I/usr/lib/avr/include/ \ diff --git a/simavr/cores/sim_mega644.c b/simavr/cores/sim_mega644.c index 813e551..a063ca2 100644 --- a/simavr/cores/sim_mega644.c +++ b/simavr/cores/sim_mega644.c @@ -49,21 +49,7 @@ static struct mcu_t { .init = init, .reset = reset, }, - .eeprom = { - .size = E2END+1, - .r_eearh = EEARH, - .r_eearl = EEARL, - .r_eedr = EEDR, - .r_eecr = EECR, - .eepm = { AVR_IO_REGBIT(EECR, EEPM0), AVR_IO_REGBIT(EECR, EEPM1) }, - .eempe = AVR_IO_REGBIT(EECR, EEMPE), - .eepe = AVR_IO_REGBIT(EECR, EEPE), - .eere = AVR_IO_REGBIT(EECR, EERE), - .ready = { - .enable = AVR_IO_REGBIT(EECR, EERIE), - .vector = EE_READY_vect, - }, - }, + AVR_EEPROM_DECLARE(EE_READY_vect), .porta = { .name = 'A', .r_port = PORTA, .r_ddr = DDRA, .r_pin = PINA, .pcint = { @@ -105,7 +91,6 @@ static struct mcu_t { .disabled = AVR_IO_REGBIT(PRR,PRUSART0), .name = '0', .r_udr = UDR0, - .udre = AVR_IO_REGBIT(UCSR0A, UDRE0), .r_ucsra = UCSR0A, .r_ucsrb = UCSR0B, @@ -114,14 +99,17 @@ static struct mcu_t { .r_ubrrh = UBRR0H, .rxc = { .enable = AVR_IO_REGBIT(UCSR0B, RXCIE0), + .raised = AVR_IO_REGBIT(UCSR0A, RXC0), .vector = USART0_RX_vect, }, .txc = { .enable = AVR_IO_REGBIT(UCSR0B, TXCIE0), + .raised = AVR_IO_REGBIT(UCSR0A, TXC0), .vector = USART0_TX_vect, }, .udrc = { .enable = AVR_IO_REGBIT(UCSR0B, UDRIE0), + .raised = AVR_IO_REGBIT(UCSR0A, UDRE0), .vector = USART0_UDRE_vect, }, }, @@ -129,7 +117,6 @@ static struct mcu_t { .disabled = AVR_IO_REGBIT(PRR,PRUSART1), .name = '1', .r_udr = UDR1, - .udre = AVR_IO_REGBIT(UCSR1A, UDRE1), .r_ucsra = UCSR1A, .r_ucsrb = UCSR1B, @@ -138,14 +125,17 @@ static struct mcu_t { .r_ubrrh = UBRR1H, .rxc = { .enable = AVR_IO_REGBIT(UCSR1B, RXCIE1), + .raised = AVR_IO_REGBIT(UCSR1A, RXC1), .vector = USART1_RX_vect, }, .txc = { .enable = AVR_IO_REGBIT(UCSR1B, TXCIE1), + .raised = AVR_IO_REGBIT(UCSR1A, TXC1), .vector = USART1_TX_vect, }, .udrc = { .enable = AVR_IO_REGBIT(UCSR1B, UDRIE1), + .raised = AVR_IO_REGBIT(UCSR1A, UDRE1), .vector = USART1_UDRE_vect, }, }, diff --git a/simavr/cores/sim_megax8.h b/simavr/cores/sim_megax8.h index cbe6de2..682b2c4 100644 --- a/simavr/cores/sim_megax8.h +++ b/simavr/cores/sim_megax8.h @@ -62,21 +62,7 @@ struct mcu_t SIM_CORENAME = { .init = mx8_init, .reset = mx8_reset, }, - .eeprom = { - .size = E2END+1, - .r_eearh = EEARH, - .r_eearl = EEARL, - .r_eedr = EEDR, - .r_eecr = EECR, - .eepm = { AVR_IO_REGBIT(EECR, EEPM0), AVR_IO_REGBIT(EECR, EEPM1) }, - .eempe = AVR_IO_REGBIT(EECR, EEMPE), - .eepe = AVR_IO_REGBIT(EECR, EEPE), - .eere = AVR_IO_REGBIT(EECR, EERE), - .ready = { - .enable = AVR_IO_REGBIT(EECR, EERIE), - .vector = EE_READY_vect, - }, - }, + AVR_EEPROM_DECLARE(EE_READY_vect), .portb = { .name = 'B', .r_port = PORTB, .r_ddr = DDRB, .r_pin = PINB, .pcint = { @@ -109,7 +95,6 @@ struct mcu_t SIM_CORENAME = { .disabled = AVR_IO_REGBIT(PRR,PRUSART0), .name = '0', .r_udr = UDR0, - .udre = AVR_IO_REGBIT(UCSR0A, UDRE0), .r_ucsra = UCSR0A, .r_ucsrb = UCSR0B, @@ -118,14 +103,17 @@ struct mcu_t SIM_CORENAME = { .r_ubrrh = UBRR0H, .rxc = { .enable = AVR_IO_REGBIT(UCSR0B, RXCIE0), + .raised = AVR_IO_REGBIT(UCSR0A, RXC0), .vector = USART_RX_vect, }, .txc = { .enable = AVR_IO_REGBIT(UCSR0B, TXCIE0), + .raised = AVR_IO_REGBIT(UCSR0A, TXC0), .vector = USART_TX_vect, }, .udrc = { .enable = AVR_IO_REGBIT(UCSR0B, UDRIE0), + .raised = AVR_IO_REGBIT(UCSR0A, UDRE0), .vector = USART_UDRE_vect, }, }, diff --git a/simavr/cores/sim_tiny85.c b/simavr/cores/sim_tiny85.c index 850005c..e4d7af3 100644 --- a/simavr/cores/sim_tiny85.c +++ b/simavr/cores/sim_tiny85.c @@ -47,21 +47,7 @@ static struct mcu_t { .init = init, .reset = reset, }, - .eeprom = { - .size = E2END+1, - .r_eearh = EEARH, - .r_eearl = EEARL, - .r_eedr = EEDR, - .r_eecr = EECR, - .eepm = { AVR_IO_REGBIT(EECR, EEPM0), AVR_IO_REGBIT(EECR, EEPM1) }, - .eempe = AVR_IO_REGBIT(EECR, EEMPE), - .eepe = AVR_IO_REGBIT(EECR, EEPE), - .eere = AVR_IO_REGBIT(EECR, EERE), - .ready = { - .enable = AVR_IO_REGBIT(EECR, EERIE), - .vector = EE_RDY_vect,// EE_READY_vect, - }, - }, + AVR_EEPROM_DECLARE(EE_RDY_vect), .portb = { .name = 'B', .r_port = PORTB, .r_ddr = DDRB, .r_pin = PINB, .pcint = { diff --git a/simavr/sim/avr_eeprom.c b/simavr/sim/avr_eeprom.c index f7a2d60..4045027 100644 --- a/simavr/sim/avr_eeprom.c +++ b/simavr/sim/avr_eeprom.c @@ -57,7 +57,7 @@ static void avr_eeprom_write(struct avr_t * avr, uint8_t addr, uint8_t v, void * if (eempe && avr_regbit_get(avr, p->eepe)) { // write operation uint16_t addr = avr->data[p->r_eearl] | (avr->data[p->r_eearh] << 8); - printf("eeprom write %04x <- %02x\n", addr, avr->data[p->r_eedr]); + // printf("eeprom write %04x <- %02x\n", addr, avr->data[p->r_eedr]); p->eeprom[addr] = avr->data[p->r_eedr]; // automaticaly clears that bit (?) p->eempe_clear_timer = 0; @@ -68,7 +68,7 @@ static void avr_eeprom_write(struct avr_t * avr, uint8_t addr, uint8_t v, void * if (avr_regbit_get(avr, p->eere)) { // read operation uint16_t addr = avr->data[p->r_eearl] | (avr->data[p->r_eearh] << 8); avr->data[p->r_eedr] = p->eeprom[addr]; - printf("eeprom read %04x : %02x\n", addr, p->eeprom[addr]); + // printf("eeprom read %04x : %02x\n", addr, p->eeprom[addr]); } // autocleared diff --git a/simavr/sim/avr_eeprom.h b/simavr/sim/avr_eeprom.h index 37585bf..c7f1dcd 100644 --- a/simavr/sim/avr_eeprom.h +++ b/simavr/sim/avr_eeprom.h @@ -58,4 +58,27 @@ typedef struct avr_eeprom_desc_t { #define AVR_IOCTL_EEPROM_GET AVR_IOCTL_DEF('e','e','g','p') #define AVR_IOCTL_EEPROM_SET AVR_IOCTL_DEF('e','e','s','p') + +/* + * the eeprom block seems to be very similar across AVRs, + * so here is a macro to declare a "typical" one in a core. + */ + +#define AVR_EEPROM_DECLARE(_vector) \ + .eeprom = {\ + .size = E2END+1,\ + .r_eearh = EEARH,\ + .r_eearl = EEARL,\ + .r_eedr = EEDR,\ + .r_eecr = EECR,\ + .eepm = { AVR_IO_REGBIT(EECR, EEPM0), AVR_IO_REGBIT(EECR, EEPM1) },\ + .eempe = AVR_IO_REGBIT(EECR, EEMPE),\ + .eepe = AVR_IO_REGBIT(EECR, EEPE),\ + .eere = AVR_IO_REGBIT(EECR, EERE),\ + .ready = {\ + .enable = AVR_IO_REGBIT(EECR, EERIE),\ + .vector = _vector,\ + },\ + } + #endif /* __AVR_EEPROM_H__ */ diff --git a/simavr/sim/avr_ioport.c b/simavr/sim/avr_ioport.c index 157a624..2bb4483 100644 --- a/simavr/sim/avr_ioport.c +++ b/simavr/sim/avr_ioport.c @@ -52,51 +52,76 @@ static void avr_ioport_write(struct avr_t * avr, uint8_t addr, uint8_t v, void * 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); + + // raise the internal IRQ callbacks + for (int i = 0; i < 8; i++) + if (mask & (1 << i)) + avr_raise_irq(avr, p->irq + i, (v >> i) & 1); + avr_raise_irq(avr, p->irq + IOPORT_IRQ_PIN_ALL, v); } + } +} + +/* + * this is out "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 interupts, if needed + */ +void avr_ioport_irq_notify(avr_t * avr, struct avr_irq_t * irq, uint32_t value, void * param) +{ + avr_ioport_t * p = (avr_ioport_t *)param; + if (p->r_pcint) { + int raise = avr->data[p->r_pcint] & (1 << irq->irq); + if (raise) + avr_raise_interupt(avr, &p->pcint); + } + +} +static int avr_ioport_ioctl(avr_t * avr, avr_io_t * port, uint32_t ctl, void * io_param) +{ + avr_ioport_t * p = (avr_ioport_t *)port; + int res = -1; - if (p->name == 'D') { - static int cs = -1; - if ((oldv & 0xf0) != (v & 0xf0)) { - for (int i = 0; i < 4; i++) { - - } - } - { - } - } + switch(ctl) { + case AVR_IOCTL_IOPORT_GETIRQ ... AVR_IOCTL_IOPORT_GETIRQ + IOPORT_IRQ_PIN_ALL: { + printf("%s: AVR_IOCTL_IOPORT_GETIRQ %d\n", __FUNCTION__, 0); + } break; } + + return res; } static void avr_ioport_reset(avr_t * avr, 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(avr, p->irq + i, avr_ioport_irq_notify, p); } static avr_io_t _io = { .kind = "io", .run = avr_ioport_run, .reset = avr_ioport_reset, + .ioctl = avr_ioport_ioctl, }; -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; + p->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); + p->name, p->r_pin, + p->name, p->r_ddr, + p->name, p->r_port); - avr_register_io(avr, &port->io); - avr_register_vector(avr, &port->pcint); + p->irq = avr_alloc_irq(avr, 0, IOPORT_IRQ_PIN_ALL+1); + + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->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); + avr_register_io_write(avr, p->r_port, avr_ioport_write, p); + avr_register_io_read(avr, p->r_pin, avr_ioport_read, p); } diff --git a/simavr/sim/avr_ioport.h b/simavr/sim/avr_ioport.h index 9f38995..c2daad5 100644 --- a/simavr/sim/avr_ioport.h +++ b/simavr/sim/avr_ioport.h @@ -24,6 +24,16 @@ #include "simavr.h" +enum { + IOPORT_IRQ_PIN0 = 0, + IOPORT_IRQ_PIN1,IOPORT_IRQ_PIN2,IOPORT_IRQ_PIN3,IOPORT_IRQ_PIN4, + IOPORT_IRQ_PIN5,IOPORT_IRQ_PIN6,IOPORT_IRQ_PIN7, + IOPORT_IRQ_PIN_ALL +}; + +// add IOPORT_IRQ_PIN* to this to get the real IRQ +#define AVR_IOCTL_IOPORT_GETIRQ AVR_IOCTL_DEF('i','o','g',0) + typedef struct avr_ioport_t { avr_io_t io; char name; @@ -33,9 +43,11 @@ typedef struct avr_ioport_t { avr_int_vector_t pcint; // PCINT vector uint8_t r_pcint; // pcint 8 pins mask - + + avr_irq_t * irq; } avr_ioport_t; void avr_ioport_init(avr_t * avr, avr_ioport_t * port); + #endif /* __AVR_IOPORT_H__ */ diff --git a/simavr/sim/avr_uart.c b/simavr/sim/avr_uart.c index 06aca7a..f016721 100644 --- a/simavr/sim/avr_uart.c +++ b/simavr/sim/avr_uart.c @@ -46,7 +46,10 @@ static void avr_uart_write(struct avr_t * avr, uint8_t addr, uint8_t v, void * p if (addr == p->r_udr) { // printf("UDR%c(%02x) = %02x\n", p->name, addr, v); avr_core_watch_write(avr, addr, v); - avr_regbit_set(avr, p->udre); + + // if the interupts are not used, still raised the UDRE and TXC flaga + avr_raise_interupt(avr, &p->udrc); + avr_raise_interupt(avr, &p->txc); static char buf[128]; static int l = 0; @@ -57,12 +60,25 @@ static void avr_uart_write(struct avr_t * avr, uint8_t addr, uint8_t v, void * p printf("\e[32m%s\e[0m\n", buf); } } + if (addr == p->r_ucsra) { + // get the bits before the write + uint8_t udre = avr_regbit_get(avr, p->udrc.raised); + uint8_t txc = avr_regbit_get(avr, p->txc.raised); + + avr_core_watch_write(avr, addr, v); + + // if writing one to a one, clear bit + if (udre && avr_regbit_get(avr, p->udrc.raised)) + avr_regbit_clear(avr, p->udrc.raised); + if (txc && avr_regbit_get(avr, p->txc.raised)) + avr_regbit_clear(avr, p->txc.raised); + } } void avr_uart_reset(avr_t * avr, struct avr_io_t *io) { avr_uart_t * p = (avr_uart_t *)io; - avr_regbit_set(avr, p->udre); + avr_regbit_set(avr, p->udrc.raised); } static avr_io_t _io = { @@ -79,6 +95,7 @@ void avr_uart_init(avr_t * avr, avr_uart_t * p) printf("%s UART%c UDR=%02x\n", __FUNCTION__, p->name, p->r_udr); avr_register_io_write(avr, p->r_udr, avr_uart_write, p); + avr_register_io_write(avr, p->r_ucsra, avr_uart_write, p); avr_register_io_read(avr, p->r_udr, avr_uart_read, p); } diff --git a/simavr/sim/avr_uart.h b/simavr/sim/avr_uart.h index 7ac6092..dec4cee 100644 --- a/simavr/sim/avr_uart.h +++ b/simavr/sim/avr_uart.h @@ -38,10 +38,7 @@ typedef struct avr_uart_t { avr_int_vector_t rxc; avr_int_vector_t txc; - avr_int_vector_t udrc; - - avr_regbit_t udre; // AVR_IO_REGBIT(UCSR0A, UDRE0), - + avr_int_vector_t udrc; } avr_uart_t; void avr_uart_init(avr_t * avr, avr_uart_t * port); diff --git a/simavr/sim/sim_core.c b/simavr/sim/sim_core.c index 53271e1..0980ab5 100644 --- a/simavr/sim/sim_core.c +++ b/simavr/sim/sim_core.c @@ -252,6 +252,19 @@ void avr_dump_state(avr_t * avr) avr->old[avr->old_pci].sp = _avr_sp_get(avr);\ avr->old_pci = (avr->old_pci + 1) & (OLD_PC_SIZE-1);\ +#if AVR_STACK_WATCH +#define STACK_FRAME_PUSH()\ + avr->stack_frame[avr->stack_frame_index].pc = avr->pc;\ + avr->stack_frame[avr->stack_frame_index].sp = _avr_sp_get(avr);\ + avr->stack_frame_index++; +#define STACK_FRAME_POP()\ + if (avr->stack_frame_index > 0) \ + avr->stack_frame_index--; +#else +#define STACK_FRAME_PUSH() +#define STACK_FRAME_POP() +#endif + /****************************************************************************\ * * Helper functions for calculating the status register bit values. @@ -313,6 +326,17 @@ get_compare_overflow (uint8_t res, uint8_t rd, uint8_t rr) return (rd & ~rr & ~res) | (~rd & rr & res); } +static inline int _avr_is_instruction_32_bits(avr_t * avr, uint32_t pc) +{ + uint16_t o = (avr->flash[pc] | (avr->flash[pc+1] << 8)) & 0xfc0f; + return o == 0x9200 || // STS ! Store Direct to Data Space + o == 0x9000 || // LDS Load Direct from Data Space + o == 0x940c || // JMP Long Jump + o == 0x940d || // JMP Long Jump + o == 0x940e || // CALL Long Call to sub + o == 0x940f; // CALL Long Call to sub +} + /* * Main opcode decoder * @@ -323,10 +347,9 @@ get_compare_overflow (uint8_t res, uint8_t rd, uint8_t rr) * ones scream to be factored). * I assume that the decoder could easily be 2/3 of it's current size. * - * The core 'almost' work. There is a case where it munches the stack, problem to be - * debugged. - * - * It lacks a couple of multiply instructions, and the "extended" XMega jumps. + * + It lacks the "extended" XMega jumps. + * + It also doesn't check wether the core it's + * emulating is suposed to have the fancy instructions, like multiply and such. * * for now all instructions take "one" cycle, the cycle+= needs to be added. */ @@ -408,6 +431,56 @@ uint16_t avr_run_one(avr_t * avr) _avr_set_r(avr, d, avr->data[r]); _avr_set_r(avr, d+1, avr->data[r+1]); } break; + case 0x0200: { // MULS – Multiply Signed 0000 0010 dddd rrrr + int8_t r = opcode & 0xf; + int8_t d = (opcode >> 4) & 0xf; + int16_t res = ((int8_t)avr->data[r]) * ((int8_t)avr->data[d]); + STATE("muls %s[%d], %s[%02x] = %d\n", avr_regname(d), ((int8_t)avr->data[d]), avr_regname(r), ((int8_t)avr->data[r]), res); + _avr_set_r(avr, 0, res); + _avr_set_r(avr, 1, res >> 8); + avr->sreg[S_C] = (res >> 15) & 1; + avr->sreg[S_Z] = res == 0; + SREG(); + } break; + case 0x0300: { // multiplications + int8_t r = 16 + (opcode & 0x7); + int8_t d = 16 + ((opcode >> 4) & 0x7); + int16_t res = 0; + uint8_t c = 0; + const char * name = ""; + switch (opcode & 0x88) { + case 0x00: // MULSU – Multiply Signed Unsigned 0000 0011 0ddd 0rrr + res = ((uint8_t)avr->data[r]) * ((int8_t)avr->data[d]); + c = (res >> 15) & 1; + name = "mulsu"; + break; + case 0x08: // FMUL Fractional Multiply Unsigned 0000 0011 0ddd 1rrr + res = ((uint8_t)avr->data[r]) * ((uint8_t)avr->data[d]); + c = (res >> 15) & 1; + res <<= 1; + name = "fmul"; + break; + case 0x80: // FMULS – Multiply Signed 0000 0011 1ddd 0rrr + res = ((int8_t)avr->data[r]) * ((int8_t)avr->data[d]); + c = (res >> 15) & 1; + res <<= 1; + name = "fmuls"; + break; + case 0x88: // FMULSU – Multiply Signed Unsigned 0000 0011 1ddd 0rrr + res = ((uint8_t)avr->data[r]) * ((int8_t)avr->data[d]); + c = (res >> 15) & 1; + res <<= 1; + name = "fmulsu"; + break; + } + cycle++; + STATE("%s %s[%d], %s[%02x] = %d\n", name, avr_regname(d), ((int8_t)avr->data[d]), avr_regname(r), ((int8_t)avr->data[r]), res); + _avr_set_r(avr, 0, res); + _avr_set_r(avr, 1, res >> 8); + avr->sreg[S_C] = c; + avr->sreg[S_Z] = res == 0; + SREG(); + } break; default: _avr_invalid_opcode(avr); } } @@ -434,8 +507,13 @@ uint16_t avr_run_one(avr_t * avr) get_r_d_10(opcode); uint16_t res = vd == vr; STATE("cpse %s[%02x], %s[%02x]\t; Will%s skip\n", avr_regname(d), avr->data[d], avr_regname(r), avr->data[r], res ? "":"not "); - if (res) - new_pc += 2; + if (res) { + if (_avr_is_instruction_32_bits(avr, new_pc)) { + new_pc += 4; cycle += 2; + } else { + new_pc += 2; cycle++; + } + } } break; case 0x1400: { // CP Compare 0000 10 rd dddd rrrr get_r_d_10(opcode); @@ -466,21 +544,7 @@ uint16_t avr_run_one(avr_t * avr) avr->sreg[S_S] = avr->sreg[S_N] ^ avr->sreg[S_V]; SREG(); } break; - default: - switch (opcode & 0xff00) { - case 0x0200: { // MULS – Multiply Signed 0000 0010 dddd rrrr - int8_t r = opcode & 0xf; - int8_t d = (opcode >> 4) & 0xf; - int16_t res = ((int8_t)avr->data[r]) * ((int8_t)avr->data[d]); - STATE("muls %s[%d], %s[%02x] = %d\n", avr_regname(d), ((int8_t)avr->data[d]), avr_regname(r), ((int8_t)avr->data[r]), res); - _avr_set_r(avr, 0, res); - _avr_set_r(avr, 1, res >> 8); - avr->sreg[S_C] = (res >> 15) & 1; - avr->sreg[S_Z] = res == 0; - SREG(); - } break; - default: _avr_invalid_opcode(avr); - } + default: _avr_invalid_opcode(avr); } } break; @@ -609,7 +673,7 @@ uint16_t avr_run_one(avr_t * avr) case 0x8000: { // LD (LDD) – Load Indirect using Z 10q0 qq0r rrrr 0qqq uint16_t v = avr->data[R_ZL] | (avr->data[R_ZH] << 8); uint8_t r = (opcode >> 4) & 0x1f; - uint8_t q = ((opcode & 0x2000) >> 7) | ((opcode & 0x0c00) >> 7) | (opcode & 0x7); + uint8_t q = ((opcode & 0x2000) >> 8) | ((opcode & 0x0c00) >> 7) | (opcode & 0x7); if (opcode & 0x0200) { STATE("st (Z+%d[%04x]), %s[%02x]\n", q, v+q, avr_regname(r), avr->data[r]); @@ -618,12 +682,13 @@ uint16_t avr_run_one(avr_t * avr) STATE("ld %s, (Z+%d[%04x])=[%02x]\n", avr_regname(r), q, v+q, avr->data[v+q]); _avr_set_r(avr, r, _avr_get_ram(avr, v+q)); } + cycle += 2; } break; case 0xa008: case 0x8008: { // LD (LDD) – Load Indirect using Y 10q0 qq0r rrrr 1qqq uint16_t v = avr->data[R_YL] | (avr->data[R_YH] << 8); uint8_t r = (opcode >> 4) & 0x1f; - uint8_t q = ((opcode & 0x2000) >> 7) | ((opcode & 0x0c00) >> 7) | (opcode & 0x7); + uint8_t q = ((opcode & 0x2000) >> 8) | ((opcode & 0x0c00) >> 7) | (opcode & 0x7); if (opcode & 0x0200) { STATE("st (Y+%d[%04x]), %s[%02x]\n", q, v+q, avr_regname(r), avr->data[r]); @@ -632,6 +697,7 @@ uint16_t avr_run_one(avr_t * avr) STATE("ld %s, (Y+%d[%04x])=[%02x]\n", avr_regname(r), q, v+q, avr->data[v+q]); _avr_set_r(avr, r, _avr_get_ram(avr, v+q)); } + cycle += 2; } break; default: _avr_invalid_opcode(avr); } @@ -659,23 +725,27 @@ uint16_t avr_run_one(avr_t * avr) uint16_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8); STATE("ijmp Z[%04x]\n", z << 1); new_pc = z << 1; + cycle++; TRACE_JUMP(); } break; case 0x9509: { // ICALL Indirect Call to Subroutine uint16_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8); STATE("icall Z[%04x]\n", z << 1); - _avr_push16(avr, new_pc >> 1); new_pc = z << 1; + cycle += 2; TRACE_JUMP(); + STACK_FRAME_PUSH(); } break; case 0x9518: // RETI case 0x9508: { // RET new_pc = _avr_pop16(avr) << 1; if (opcode & 0x10) // reti avr->sreg[S_I] = 1; + cycle += 3; STATE("ret%s\n", opcode & 0x10 ? "i" : ""); TRACE_JUMP(); + STACK_FRAME_POP(); } break; case 0x95c8: { // LPM Load Program Memory R0 <- (Z) uint16_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8); @@ -700,6 +770,14 @@ uint16_t avr_run_one(avr_t * avr) } break; default: { switch (opcode & 0xfe0f) { + case 0x9000: { // LDS Load Direct from Data Space, 32 bits + uint8_t r = (opcode >> 4) & 0x1f; + uint16_t x = (avr->flash[new_pc+1] << 8) | avr->flash[new_pc]; + new_pc += 2; + STATE("lds %s[%02x], 0x%04x\n", avr_regname(r), avr->data[r], x); + _avr_set_r(avr, r, _avr_get_ram(avr, x)); + cycle++; + } break; case 0x9005: case 0x9004: { // LPM Load Program Memory 1001 000d dddd 01oo uint16_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8); @@ -712,6 +790,7 @@ uint16_t avr_run_one(avr_t * avr) _avr_set_r(avr, R_ZH, z >> 8); _avr_set_r(avr, R_ZL, z); } + cycle += 2; } break; case 0x900c: case 0x900d: @@ -734,7 +813,7 @@ uint16_t avr_run_one(avr_t * avr) uint8_t r = (opcode >> 4) & 0x1f; uint16_t x = (avr->data[R_XH] << 8) | avr->data[R_XL]; STATE("st %sX[%04x]%s, %s[%02x] \n", op == 2 ? "--" : "", x, op == 1 ? "++" : "", avr_regname(r), avr->data[r]); - + cycle++; if (op == 2) x--; _avr_set_ram(avr, x, avr->data[r]); if (op == 1) x++; @@ -747,7 +826,7 @@ uint16_t avr_run_one(avr_t * avr) uint8_t r = (opcode >> 4) & 0x1f; uint16_t y = (avr->data[R_YH] << 8) | avr->data[R_YL]; STATE("ld %s, %sY[%04x]%s\n", avr_regname(r), op == 2 ? "--" : "", y, op == 1 ? "++" : ""); - + cycle++; if (op == 2) y--; _avr_set_r(avr, r, _avr_get_ram(avr, y)); if (op == 1) y++; @@ -760,7 +839,7 @@ uint16_t avr_run_one(avr_t * avr) uint8_t r = (opcode >> 4) & 0x1f; uint16_t y = (avr->data[R_YH] << 8) | avr->data[R_YL]; STATE("st %sY[%04x]%s, %s[%02x] \n", op == 2 ? "--" : "", y, op == 1 ? "++" : "", avr_regname(r), avr->data[r]); - + cycle++; if (op == 2) y--; _avr_set_ram(avr, y, avr->data[r]); if (op == 1) y++; @@ -780,7 +859,6 @@ uint16_t avr_run_one(avr_t * avr) uint8_t r = (opcode >> 4) & 0x1f; uint16_t z = (avr->data[R_ZH] << 8) | avr->data[R_ZL]; STATE("ld %s, %sZ[%04x]%s\n", avr_regname(r), op == 2 ? "--" : "", z, op == 1 ? "++" : ""); - if (op == 2) z--; _avr_set_r(avr, r, _avr_get_ram(avr, z)); if (op == 1) z++; @@ -793,7 +871,6 @@ uint16_t avr_run_one(avr_t * avr) uint8_t r = (opcode >> 4) & 0x1f; uint16_t z = (avr->data[R_ZH] << 8) | avr->data[R_ZL]; STATE("st %sZ[%04x]%s, %s[%02x] \n", op == 2 ? "--" : "", z, op == 1 ? "++" : "", avr_regname(r), avr->data[r]); - if (op == 2) z--; _avr_set_ram(avr, z, avr->data[r]); if (op == 1) z++; @@ -805,19 +882,14 @@ uint16_t avr_run_one(avr_t * avr) _avr_set_r(avr, r, _avr_pop8(avr)); uint16_t sp = _avr_sp_get(avr); STATE("pop %s (@%04x)[%02x]\n", avr_regname(r), sp, avr->data[sp]); + cycle++; } break; case 0x920f: { // PUSH 1001 001d dddd 1111 uint8_t r = (opcode >> 4) & 0x1f; _avr_push8(avr, avr->data[r]); uint16_t sp = _avr_sp_get(avr); STATE("push %s[%02x] (@%04x)\n", avr_regname(r), avr->data[r], sp); - } break; - case 0x9000: { // LDS Load Direct from Data Space, 32 bits - uint8_t r = (opcode >> 4) & 0x1f; - uint16_t x = (avr->flash[new_pc+1] << 8) | avr->flash[new_pc]; - new_pc += 2; - STATE("lds %s[%02x], 0x%04x\n", avr_regname(r), avr->data[r], x); - _avr_set_r(avr, r, _avr_get_ram(avr, x)); + cycle++; } break; case 0x9400: { // COM – One’s Complement uint8_t r = (opcode >> 4) & 0x1f; @@ -917,9 +989,9 @@ uint16_t avr_run_one(avr_t * avr) uint32_t a = ((opcode & 0x01f0) >> 3) | (opcode & 1); uint16_t x = (avr->flash[new_pc+1] << 8) | avr->flash[new_pc]; a = (a << 16) | x; - // printf("jmp %06x\n", a << 1); STATE("jmp 0x%06x\n", a); new_pc = a << 1; + cycle += 2; TRACE_JUMP(); } break; case 0x940e: @@ -927,12 +999,13 @@ uint16_t avr_run_one(avr_t * avr) uint32_t a = ((opcode & 0x01f0) >> 3) | (opcode & 1); uint16_t x = (avr->flash[new_pc+1] << 8) | avr->flash[new_pc]; a = (a << 16) | x; - // printf("call %06x\n", a << 1); STATE("call 0x%06x\n", a); new_pc += 2; _avr_push16(avr, new_pc >> 1); new_pc = a << 1; + cycle += 3; // 4 cycles TRACE_JUMP(); + STACK_FRAME_PUSH(); } break; default: { @@ -977,14 +1050,20 @@ uint16_t avr_run_one(avr_t * avr) uint8_t res = _avr_get_ram(avr, io) & ~(1 << b); STATE("cbi %s[%04x], 0x%02x = %02x\n", avr_regname(io), avr->data[io], 1<> 3) & 0x1f) + 32; uint8_t b = opcode & 0x7; uint8_t res = _avr_get_ram(avr, io) & (1 << b); STATE("sbic %s[%04x], 0x%02x\t; Will%s branch\n", avr_regname(io), avr->data[io], 1<> 3) & 0x1f) + 32; @@ -992,14 +1071,20 @@ uint16_t avr_run_one(avr_t * avr) uint8_t res = _avr_get_ram(avr, io) | (1 << b); STATE("sbi %s[%04x], 0x%02x = %02x\n", avr_regname(io), avr->data[io], 1<> 3) & 0x1f; uint8_t b = opcode & 0x7; uint8_t res = _avr_get_ram(avr, io + 32) & (1 << b); STATE("sbis %s[%04x], 0x%02x\t; Will%s branch\n", avr_regname(io), avr->data[io], 1<> 4; STATE("rjmp .%d [%04x]\n", o, new_pc + (o << 1)); new_pc = new_pc + (o << 1); + cycle++; TRACE_JUMP(); } break; @@ -1057,7 +1143,12 @@ uint16_t avr_run_one(avr_t * avr) STATE("rcall .%d [%04x]\n", o, new_pc + (o << 1)); _avr_push16(avr, new_pc >> 1); new_pc = new_pc + (o << 1); - TRACE_JUMP(); + cycle += 2; + // 'rcall .1' is used as a cheap "push 16 bits of room on the stack" + if (o != 0) { + TRACE_JUMP(); + STACK_FRAME_PUSH(); + } } break; case 0xe000: { // LDI Rd, K 1110 KKKK RRRR KKKK -- aka SER (LDI r, 0xff) @@ -1086,8 +1177,10 @@ uint16_t avr_run_one(avr_t * avr) } else { STATE("%s%c .%d [%04x]\t; Will%s branch\n", set ? "brbs" : "brbc", _sreg_bit_name[s], o, new_pc + (o << 1), branch ? "":" not"); } - if (branch) + if (branch) { + cycle++; new_pc = new_pc + (o << 1); + } } break; case 0xf800: case 0xf900: { // BLD – Bit Store from T into a Bit in Register 1111 100r rrrr 0bbb diff --git a/simavr/sim/sim_core.h b/simavr/sim/sim_core.h index 0fc5dd5..743979b 100644 --- a/simavr/sim/sim_core.h +++ b/simavr/sim/sim_core.h @@ -55,15 +55,27 @@ void avr_dump_state(avr_t * avr); } +#if AVR_STACK_WATCH +#define DUMP_STACK() \ + for (int i = avr->stack_frame_index; i; i--) {\ + int pci = i-1;\ + printf("\e[31m*** %04x: %-25s sp %04x\e[0m\n",\ + avr->stack_frame[pci].pc, avr->codeline[avr->stack_frame[pci].pc>>1]->symbol, avr->stack_frame[pci].sp);\ + } +#else +#define DUMP_STACK() +#endif + #define CRASH() {\ DUMP_REG();\ - printf("*** CYCLE %lld\n", avr->cycle);\ + printf("*** CYCLE %lld PC %04x\n", avr->cycle, avr->pc);\ for (int i = OLD_PC_SIZE-1; i > 0; i--) {\ int pci = (avr->old_pci + i) & 0xf;\ printf("\e[31m*** %04x: %-25s RESET -%d; sp %04x\e[0m\n",\ avr->old[pci].pc, avr->codeline[avr->old[pci].pc>>1]->symbol, OLD_PC_SIZE-i, avr->old[pci].sp);\ }\ printf("Stack Ptr %04x/%04x = %d \n", _avr_sp_get(avr), avr->ramend, avr->ramend - _avr_sp_get(avr));\ + DUMP_STACK();\ exit(1);\ } diff --git a/simavr/sim/sim_elf.c b/simavr/sim/sim_elf.c index 29552a9..32b2334 100644 --- a/simavr/sim/sim_elf.c +++ b/simavr/sim/sim_elf.c @@ -88,9 +88,8 @@ int elf_read_firmware(const char * file, elf_firmware_t * firmware) firmware->bsssize = s->d_size; } else if (!strcmp(name, ".mmcu")) { Elf_Data *s = elf_getdata(scn, NULL); - long f_cpu = s ? *((long*)s->d_buf) : 0; firmware->mmcu = *((avr_mcu_t*)s->d_buf); - printf("%s: setting speed to %ld\n", __FUNCTION__, f_cpu); + // printf("%s: setting speed to %ld\n", __FUNCTION__, f_cpu); // avr->frequency = f_cpu; } #if ELF_SYMBOLS diff --git a/simavr/sim/simavr.c b/simavr/sim/simavr.c index 39481dc..5181f27 100644 --- a/simavr/sim/simavr.c +++ b/simavr/sim/simavr.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "simavr.h" #include "sim_elf.h" @@ -61,11 +62,12 @@ int avr_init(avr_t * avr) avr->data = malloc(avr->ramend + 1); memset(avr->data, 0, avr->ramend + 1); - avr->state = cpu_Running; + // cpu is in limbo before init is finished. + avr->state = cpu_Limbo; avr->frequency = 1000000; // can be overriden via avr_mcu_section - if (avr->init) avr->init(avr); + avr->state = cpu_Running; avr_reset(avr); return 0; } @@ -135,27 +137,32 @@ int avr_is_interupt_pending(avr_t * avr, avr_int_vector_t * vector) return avr->pending[vector->vector >> 5] & (1 << (vector->vector & 0x1f)); } -void avr_raise_interupt(avr_t * avr, avr_int_vector_t * vector) +int avr_raise_interupt(avr_t * avr, avr_int_vector_t * vector) { - if (!vector->vector) - return; + if (!vector || !vector->vector) + return 0; // printf("%s raising %d\n", __FUNCTION__, vector->vector); + // always mark the 'raised' flag to one, even if the interuot is disabled + // this allow "pooling" for the "raised" flag, like for non-interupt + // driven UART and so so. These flags are often "write one to clear" + if (vector->raised.reg) + avr_regbit_set(avr, vector->raised); if (vector->enable.reg) { if (!avr_regbit_get(avr, vector->enable)) - return; + return 0; } if (!avr_is_interupt_pending(avr, vector)) { if (!avr->pending_wait) avr->pending_wait = 2; // latency on interupts ?? avr->pending[vector->vector >> 5] |= (1 << (vector->vector & 0x1f)); - if (vector->raised.reg) - avr_regbit_set(avr, vector->raised); if (avr->state != cpu_Running) { // printf("Waking CPU due to interrupt\n"); avr->state = cpu_Running; // in case we were sleeping } } + // return 'raised' even if it was already pending + return 1; } static void avr_clear_interupt(avr_t * avr, int v) @@ -169,6 +176,69 @@ static void avr_clear_interupt(avr_t * avr, int v) avr_regbit_clear(avr, vector->raised); } +void avr_init_irq(avr_t * avr, avr_irq_t * irq, uint32_t base, uint32_t count) +{ + memset(irq, 0, sizeof(avr_irq_t) * count); + + for (int i = 0; i < count; i++) + irq[i].irq = base + i; +} + +avr_irq_t * avr_alloc_irq(avr_t * avr, uint32_t base, uint32_t count) +{ + avr_irq_t * irq = (avr_irq_t*)malloc(sizeof(avr_irq_t) * count); + avr_init_irq(avr, irq, base, count); + return irq; +} + +void avr_irq_register_notify(avr_t * avr, avr_irq_t * irq, avr_irq_notify_t notify, void * param) +{ + if (!irq || !notify) + return; + + avr_irq_hook_t *hook = irq->hook; + while (hook) { + if (hook->notify == notify && hook->param == param) + return; // already there + hook = hook->next; + } + hook = malloc(sizeof(avr_irq_hook_t)); + memset(hook, 0, sizeof(avr_irq_hook_t)); + hook->next = irq->hook; + hook->notify = notify; + hook->param = param; + irq->hook = hook; +} + +void avr_raise_irq(avr_t * avr, avr_irq_t * irq, uint32_t value) +{ + if (!irq || irq->value == value) + return ; + avr_irq_hook_t *hook = irq->hook; + while (hook) { + if (hook->notify) { + if (hook->busy == 0) { + hook->busy++; + hook->notify(avr, irq, value, hook->param); + hook->busy--; + } + } + hook = hook->next; + } + irq->value = value; +} + +static void _avr_irq_connect(avr_t * avr, avr_irq_t * irq, uint32_t value, void * param) +{ + avr_irq_t * dst = (avr_irq_t*)param; + avr_raise_irq(avr, dst, value != 0); +} + +void avr_connect_irq(avr_t * avr, avr_irq_t * src, avr_irq_t * dst) +{ + avr_irq_register_notify(avr, src, _avr_irq_connect, dst); +} + void avr_loadcode(avr_t * avr, uint8_t * code, uint32_t size, uint32_t address) { memcpy(avr->flash + address, code, size); @@ -186,15 +256,14 @@ void avr_core_watch_write(avr_t *avr, uint16_t addr, uint8_t v) avr->pc, _avr_sp_get(avr), avr->flash[avr->pc] | (avr->flash[avr->pc]<<8), addr, v); CRASH(); } -#if 0 +#if AVR_STACK_WATCH /* - * this only happend when the compiler is doctoring the stack before calls. Or - * if there is an invalid pointer somewhere... + * this checks that the current "function" is not doctoring the stack frame that is located + * higher on the stack than it should be. It's a sign of code that has overrun it's stack + * frame and is munching on it's own return address. */ - if (addr > _avr_sp_get(avr)) { - avr->trace++; - STATE("\e[31mmunching stack SP %04x, A=%04x <= %02x\e[0m\n", _avr_sp_get(avr), addr, v); - avr->trace--; + if (avr->stack_frame_index > 1 && addr > avr->stack_frame[avr->stack_frame_index-2].sp) { + printf("\e[31m%04x : munching stack SP %04x, A=%04x <= %02x\e[0m\n", avr->pc, _avr_sp_get(avr), addr, v); } #endif avr->data[addr] = v; @@ -315,13 +384,58 @@ avr_kind_t * avr_kind[] = { NULL }; -int main(int argc, const char **argv) +void display_usage() +{ + printf("usage: simavr [-t] [-m ] [-f ] firmware\n"); + printf(" -t: run full scale decoder trace\n"); + exit(1); +} + +int main(int argc, char *argv[]) { elf_firmware_t f; + long f_cpu = 0; + int trace = 0; + char name[16] = ""; + int option_count; + int option_index = 0; + + struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"mcu", required_argument, 0, 'm'}, + {"freq", required_argument, 0, 'f'}, + {"trace", no_argument, 0, 't'}, + {0, 0, 0, 0} + }; + + if (argc == 1) + display_usage(); + + while ((option_count = getopt_long(argc, argv, "thm:f:", long_options, &option_index)) != -1) { + switch (option_count) { + case 'h': + display_usage(); + break; + case 'm': + strcpy(name, optarg); + break; + case 'f': + f_cpu = atoi(optarg); + break; + case 't': + trace++; + break; + } + } + + elf_read_firmware(argv[argc-1], &f); - elf_read_firmware(argv[1], &f); + if (strlen(name)) + strcpy(f.mmcu.name, name); + if (f_cpu) + f.mmcu.f_cpu = f_cpu; - printf("firmware %s f=%ld mmcu=%s\n", argv[1], f.mmcu.f_cpu, f.mmcu.name); + printf("firmware %s f=%ld mmcu=%s\n", argv[argc-1], f.mmcu.f_cpu, f.mmcu.name); avr_kind_t * maker = NULL; for (int i = 0; avr_kind[i] && !maker; i++) { @@ -347,7 +461,7 @@ int main(int argc, const char **argv) avr_eeprom_desc_t d = { .ee = f.eeprom, .offset = 0, .size = f.eesize }; avr_ioctl(avr, AVR_IOCTL_EEPROM_SET, &d); } -// avr->trace = 1; + avr->trace = trace; for (long long i = 0; i < 8000000*10; i++) // for (long long i = 0; i < 80000; i++) diff --git a/simavr/sim/simavr.h b/simavr/sim/simavr.h index bc1fd3f..af67f78 100644 --- a/simavr/sim/simavr.h +++ b/simavr/sim/simavr.h @@ -50,6 +50,7 @@ enum { * Core states. This will need populating with debug states for gdb */ enum { + cpu_Limbo = 0, // before initialization is finished cpu_Stopped, cpu_Running, cpu_Sleeping, @@ -135,6 +136,17 @@ typedef struct avr_t { } old[OLD_PC_SIZE]; // catches reset.. int old_pci; +#if AVR_STACK_WATCH + #define STACK_FRAME_SIZE 32 + // this records the call/ret pairs, to try to catch + // code that munches the stack -under- their own frame + struct { + uint32_t pc; + uint16_t sp; + } stack_frame[STACK_FRAME_SIZE]; + int stack_frame_index; +#endif + // DEBUG ONLY // keeps track of wich registers gets touched by instructions // reset before each new instructions. Allows meaningful traces @@ -226,7 +238,8 @@ int avr_ioctl(avr_t *avr, uint32_t ctl, void * io_param); // register an interupt vector. It's only needed if you want to use the "r_raised" flags void avr_register_vector(avr_t *avr, avr_int_vector_t * vector); // raise an interupt (if enabled). The interupt is latched and will be called later -void avr_raise_interupt(avr_t * avr, avr_int_vector_t * vector); +// return non-zero if the interupt was raised and is now pending +int avr_raise_interupt(avr_t * avr, avr_int_vector_t * vector); // return non-zero if the AVR core has any pending interupts int avr_has_pending_interupts(avr_t * avr); // return nonzero if a soecific interupt vector is pending @@ -308,6 +321,45 @@ static inline uint8_t avr_regbit_get_array(avr_t * avr, avr_regbit_t *rb, int co #define AVR_IO_REGBIT(_io, _bit) { . reg = (_io), .bit = (_bit), .mask = 1 } #define AVR_IO_REGBITS(_io, _bit, _mask) { . reg = (_io), .bit = (_bit), .mask = (_mask) } +/* + * Internal IRQ system + * + * This subsystem allow any piece of code to "register" a hook to be called when an IRQ is + * raised. The IRQ definition is up to the module defining it, for example a IOPORT pin change + * might be an IRQ in wich case any oiece of code can be notified when a pin has changed state + * + * The notify hooks are chained, and duplicates are filtered out so you can't register a + * notify hook twice on one particylar IRQ + * + * IRQ calling order is not defined, so don't rely on it. + * + * IRQ hook needs to be registered in reset() handlers, ie after all modules init() bits + * have been called, to prevent race condition of the initialization order. + */ +// internal structure for a hook, never seen by the notify procs +struct avr_irq_t; + +typedef void (*avr_irq_notify_t)(avr_t * avr, struct avr_irq_t * irq, uint32_t value, void * param); + +typedef struct avr_irq_hook_t { + struct avr_irq_hook_t * next; + void * param; + int busy; // prevent reentrance of callbacks + avr_irq_notify_t notify; +} avr_irq_hook_t; + +typedef struct avr_irq_t { + uint32_t irq; + uint32_t value; + avr_irq_hook_t * hook; +} avr_irq_t; + +avr_irq_t * avr_alloc_irq(avr_t * avr, uint32_t base, uint32_t count); +void avr_init_irq(avr_t * avr, avr_irq_t * irq, uint32_t base, uint32_t count); +void avr_raise_irq(avr_t * avr, avr_irq_t * irq, uint32_t value); +// this connects a "source" IRQ to a "destination" IRQ +void avr_connect_irq(avr_t * avr, avr_irq_t * src, avr_irq_t * dst); +void avr_irq_register_notify(avr_t * avr, avr_irq_t * irq, avr_irq_notify_t notify, void * param); #endif /*__SIMAVR_H__*/