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/ \
.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 = {
.disabled = AVR_IO_REGBIT(PRR,PRUSART0),
.name = '0',
.r_udr = UDR0,
- .udre = AVR_IO_REGBIT(UCSR0A, UDRE0),
.r_ucsra = UCSR0A,
.r_ucsrb = UCSR0B,
.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,
},
},
.disabled = AVR_IO_REGBIT(PRR,PRUSART1),
.name = '1',
.r_udr = UDR1,
- .udre = AVR_IO_REGBIT(UCSR1A, UDRE1),
.r_ucsra = UCSR1A,
.r_ucsrb = UCSR1B,
.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,
},
},
.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 = {
.disabled = AVR_IO_REGBIT(PRR,PRUSART0),
.name = '0',
.r_udr = UDR0,
- .udre = AVR_IO_REGBIT(UCSR0A, UDRE0),
.r_ucsra = UCSR0A,
.r_ucsrb = UCSR0B,
.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,
},
},
.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 = {
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;
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
#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__ */
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);
}
#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;
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__ */
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;
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 = {
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);
}
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);
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.
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
*
* 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+=<extra> needs to be added.
*/
_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);
}
}
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);
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;
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]);
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]);
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);
}
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);
} 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);
_avr_set_r(avr, R_ZH, z >> 8);
_avr_set_r(avr, R_ZL, z);
}
+ cycle += 2;
} break;
case 0x900c:
case 0x900d:
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++;
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++;
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++;
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++;
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++;
_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;
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:
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: {
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<<b, res);
_avr_set_ram(avr, io, res);
+ cycle++;
} break;
case 0x9900: { // SBIC - Skip if Bit in I/O Register is Cleared 1001 0111 AAAA Abbb
uint8_t io = ((opcode >> 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<<b, !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 0x9a00: { // SBI - Set Bit in I/O Register 1001 1000 AAAA Abbb
uint8_t io = ((opcode >> 3) & 0x1f) + 32;
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<<b, res);
_avr_set_ram(avr, io, res);
+ cycle++;
} break;
case 0x9b00: { // SBIS - Skip if Bit in I/O Register is Cleared 1001 0111 AAAA Abbb
uint8_t io = (opcode >> 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<<b, 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;
default:
switch (opcode & 0xfc00) {
short o = ((short)(opcode << 4)) >> 4;
STATE("rjmp .%d [%04x]\n", o, new_pc + (o << 1));
new_pc = new_pc + (o << 1);
+ cycle++;
TRACE_JUMP();
} break;
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)
} 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
}
+#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);\
}
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
#include <stdio.h>
#include <string.h>
#include <ctype.h>
+#include <getopt.h>
#include "simavr.h"
#include "sim_elf.h"
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;
}
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)
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);
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;
NULL
};
-int main(int argc, const char **argv)
+void display_usage()
+{
+ printf("usage: simavr [-t] [-m <device>] [-f <frequency>] 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++) {
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++)
* 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,
} 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
// 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
#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__*/