#include <ctype.h>
#include "sim_avr.h"
#include "sim_core.h"
+#include "sim_gdb.h"
#include "avr_flash.h"
#include "avr_watchdog.h"
#define T(w) w
-#define REG_TOUCH(a, r) (a)->touched[(r) >> 5] |= (1 << ((r) & 0x1f))
-#define REG_ISTOUCHED(a, r) ((a)->touched[(r) >> 5] & (1 << ((r) & 0x1f)))
+#define REG_TOUCH(a, r) (a)->trace_data->touched[(r) >> 5] |= (1 << ((r) & 0x1f))
+#define REG_ISTOUCHED(a, r) ((a)->trace_data->touched[(r) >> 5] & (1 << ((r) & 0x1f)))
/*
- * This allows a "special case" to skip indtruction tracing when in these
- * symbols. since printf() is useful to have, but generates a lot of cycles
+ * This allows a "special case" to skip instruction tracing when in these
+ * symbols since printf() is useful to have, but generates a lot of cycles.
*/
int dont_trace(const char * name)
{
#define STATE(_f, args...) { \
if (avr->trace) {\
- if (avr->codeline && avr->codeline[avr->pc>>1]) {\
- const char * symn = avr->codeline[avr->pc>>1]->symbol; \
+ if (avr->trace_data->codeline && avr->trace_data->codeline[avr->pc>>1]) {\
+ const char * symn = avr->trace_data->codeline[avr->pc>>1]->symbol; \
int dont = 0 && dont_trace(symn);\
if (dont!=donttrace) { \
donttrace = dont;\
void avr_core_watch_write(avr_t *avr, uint16_t addr, uint8_t v)
{
if (addr > avr->ramend) {
- printf("*** Invalid write address PC=%04x SP=%04x O=%04x Address %04x=%02x out of ram\n",
- avr->pc, _avr_sp_get(avr), avr->flash[avr->pc] | (avr->flash[avr->pc]<<8), addr, v);
+ AVR_LOG(avr, LOG_ERROR, "CORE: *** Invalid write address PC=%04x SP=%04x O=%04x Address %04x=%02x out of ram\n",
+ avr->pc, _avr_sp_get(avr), avr->flash[avr->pc + 1] | (avr->flash[avr->pc]<<8), addr, v);
CRASH();
}
if (addr < 32) {
- printf("*** Invalid write address PC=%04x SP=%04x O=%04x Address %04x=%02x low registers\n",
- avr->pc, _avr_sp_get(avr), avr->flash[avr->pc] | (avr->flash[avr->pc]<<8), addr, v);
+ AVR_LOG(avr, LOG_ERROR, "CORE: *** Invalid write address PC=%04x SP=%04x O=%04x Address %04x=%02x low registers\n",
+ avr->pc, _avr_sp_get(avr), avr->flash[avr->pc + 1] | (avr->flash[avr->pc]<<8), addr, v);
CRASH();
}
#if AVR_STACK_WATCH
* 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 (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);
+ if (avr->trace_data->stack_frame_index > 1 && addr > avr->trace_data->stack_frame[avr->trace_data->stack_frame_index-2].sp) {
+ printf( FONT_RED "%04x : munching stack SP %04x, A=%04x <= %02x\n" FONT_DEFAULT, avr->pc, _avr_sp_get(avr), addr, v);
}
#endif
+
+ if (avr->gdb) {
+ avr_gdb_handle_watchpoints(avr, addr, AVR_GDB_WATCH_WRITE);
+ }
+
avr->data[addr] = v;
}
uint8_t avr_core_watch_read(avr_t *avr, uint16_t addr)
{
if (addr > avr->ramend) {
- printf("*** Invalid read address PC=%04x SP=%04x O=%04x Address %04x out of ram (%04x)\n",
- avr->pc, _avr_sp_get(avr), avr->flash[avr->pc] | (avr->flash[avr->pc]<<8), addr, avr->ramend);
+ AVR_LOG(avr, LOG_ERROR, FONT_RED "CORE: *** Invalid read address PC=%04x SP=%04x O=%04x Address %04x out of ram (%04x)\n" FONT_DEFAULT,
+ avr->pc, _avr_sp_get(avr), avr->flash[avr->pc + 1] | (avr->flash[avr->pc]<<8), addr, avr->ramend);
CRASH();
}
+
+ if (avr->gdb) {
+ avr_gdb_handle_watchpoints(avr, addr, AVR_GDB_WATCH_READ);
+ }
+
return avr->data[addr];
}
/*
* Set a register (r < 256)
- * if it's an IO regisrer (> 31) also (try to) call any callback that was
+ * if it's an IO register (> 31) also (try to) call any callback that was
* registered to track changes to that register.
*/
static inline void _avr_set_r(avr_t * avr, uint8_t r, uint8_t v)
if (r == R_SREG) {
avr->data[R_SREG] = v;
// unsplit the SREG
- for (int i = 0; i < 8; i++)
- avr->sreg[i] = (v & (1 << i)) != 0;
+ SET_SREG_FROM(avr, v);
SREG();
}
if (r > 31) {
* SREG is special it's reconstructed when read
* while the core itself uses the "shortcut" array
*/
- avr->data[R_SREG] = 0;
- for (int i = 0; i < 8; i++)
- if (avr->sreg[i] > 1) {
- printf("** Invalid SREG!!\n");
- CRASH();
- } else if (avr->sreg[i])
- avr->data[R_SREG] |= (1 << i);
+ READ_SREG_INTO(avr, avr->data[R_SREG]);
} else if (addr > 31 && addr < 256) {
uint8_t io = AVR_DATA_TO_IO(addr);
static void _avr_invalid_opcode(avr_t * avr)
{
#if CONFIG_SIMAVR_TRACE
- printf("\e[31m*** %04x: %-25s Invalid Opcode SP=%04x O=%04x \e[0m\n",
- avr->pc, avr->codeline[avr->pc>>1]->symbol, _avr_sp_get(avr), avr->flash[avr->pc] | (avr->flash[avr->pc+1]<<8));
+ printf( FONT_RED "*** %04x: %-25s Invalid Opcode SP=%04x O=%04x \n" FONT_DEFAULT,
+ avr->pc, avr->trace_data->codeline[avr->pc>>1]->symbol, _avr_sp_get(avr), avr->flash[avr->pc] | (avr->flash[avr->pc+1]<<8));
#else
- printf("\e[31m*** %04x: Invalid Opcode SP=%04x O=%04x \e[0m\n",
+ AVR_LOG(avr, LOG_ERROR, FONT_RED "CORE: *** %04x: Invalid Opcode SP=%04x O=%04x \n" FONT_DEFAULT,
avr->pc, _avr_sp_get(avr), avr->flash[avr->pc] | (avr->flash[avr->pc+1]<<8));
#endif
}
int doit = 0;
for (int r = 0; r < 3 && !doit; r++)
- if (avr->touched[r])
+ if (avr->trace_data->touched[r])
doit = 1;
if (!doit)
return;
*/
#if CONFIG_SIMAVR_TRACE
#define TRACE_JUMP()\
- avr->old[avr->old_pci].pc = avr->pc;\
- avr->old[avr->old_pci].sp = _avr_sp_get(avr);\
- avr->old_pci = (avr->old_pci + 1) & (OLD_PC_SIZE-1);\
+ avr->trace_data->old[avr->trace_data->old_pci].pc = avr->pc;\
+ avr->trace_data->old[avr->trace_data->old_pci].sp = _avr_sp_get(avr);\
+ avr->trace_data->old_pci = (avr->trace_data->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++;
+ avr->trace_data->stack_frame[avr->trace_data->stack_frame_index].pc = avr->pc;\
+ avr->trace_data->stack_frame[avr->trace_data->stack_frame_index].sp = _avr_sp_get(avr);\
+ avr->trace_data->stack_frame_index++;
#define STACK_FRAME_POP()\
- if (avr->stack_frame_index > 0) \
- avr->stack_frame_index--;
+ if (avr->trace_data->stack_frame_index > 0) \
+ avr->trace_data->stack_frame_index--;
#else
#define STACK_FRAME_PUSH()
#define STACK_FRAME_POP()
return (rd & ~rr & ~res) | (~rd & rr & res);
}
-static inline int _avr_is_instruction_32_bits(avr_t * avr, uint32_t pc)
+static inline int _avr_is_instruction_32_bits(avr_t * avr, avr_flashaddr_t pc)
{
uint16_t o = (avr->flash[pc] | (avr->flash[pc+1] << 8)) & 0xfc0f;
return o == 0x9200 || // STS ! Store Direct to Data Space
* + It also doesn't check whether the core it's
* emulating is supposed to have the fancy instructions, like multiply and such.
*
- * The nunber of cycles taken by instruction has been added, but might not be
+ * The number of cycles taken by instruction has been added, but might not be
* entirely accurate.
*/
-uint16_t avr_run_one(avr_t * avr)
+avr_flashaddr_t avr_run_one(avr_t * avr)
{
#if CONFIG_SIMAVR_TRACE
/*
STATE("RESET\n");
CRASH();
}
- avr->touched[0] = avr->touched[1] = avr->touched[2] = 0;
+ avr->trace_data->touched[0] = avr->trace_data->touched[1] = avr->trace_data->touched[2] = 0;
#endif
- uint32_t opcode = (avr->flash[avr->pc + 1] << 8) | avr->flash[avr->pc];
- uint32_t new_pc = avr->pc + 2; // future "default" pc
- int cycle = 1;
+ uint32_t opcode = (avr->flash[avr->pc + 1] << 8) | avr->flash[avr->pc];
+ avr_flashaddr_t new_pc = avr->pc + 2; // future "default" pc
+ int cycle = 1;
switch (opcode & 0xf000) {
case 0x0000: {
avr->sreg[S_S] = avr->sreg[S_N] ^ avr->sreg[S_V];
SREG();
} break;
- case 0x0800: { // SBC substract with carry 0000 10rd dddd rrrr
+ case 0x0800: { // SBC subtract with carry 0000 10rd dddd rrrr
get_r_d_10(opcode);
uint8_t res = vd - vr - avr->sreg[S_C];
STATE("sbc %s[%02x], %s[%02x] = %02x\n", avr_regname(d), avr->data[d], avr_regname(r), avr->data[r], res);
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;
+ cycle += 1; // 2 cycles, 3 for tinyavr
} break;
case 0xa008:
case 0x8008: { // LD (LDD) – Load Indirect using Y 10q0 qq0r rrrr 1qqq
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;
+ cycle += 1; // 2 cycles, 3 for tinyavr
} break;
default: _avr_invalid_opcode(avr);
}
} else switch (opcode) {
case 0x9588: { // SLEEP
STATE("sleep\n");
- avr->state = cpu_Sleeping;
+ /* Don't sleep if there are interrupts about to be serviced.
+ * Without this check, it was possible to incorrectly enter a state
+ * in which the cpu was sleeping and interrupts were disabled. For more
+ * details, see the commit message. */
+ if (!avr_has_pending_interrupts(avr) || !avr->sreg[S_I])
+ avr->state = cpu_Sleeping;
} break;
case 0x9598: { // BREAK
STATE("break\n");
case 0x95c8: { // LPM Load Program Memory R0 <- (Z)
uint16_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8);
STATE("lpm %s, (Z[%04x])\n", avr_regname(0), z);
+ cycle += 2; // 3 cycles
_avr_set_r(avr, 0, avr->flash[z]);
} break;
case 0x9408:case 0x9418:case 0x9428:case 0x9438:case 0x9448:case 0x9458:case 0x9468:
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++;
+ cycle++; // 2 cycles
} break;
case 0x9005:
case 0x9004: { // LPM Load Program Memory 1001 000d dddd 01oo
_avr_set_r(avr, R_ZH, z >> 8);
_avr_set_r(avr, R_ZL, z);
}
- cycle += 2;
+ cycle += 2; // 3 cycles
} break;
case 0x9006:
case 0x9007: { // ELPM Extended Load Program Memory 1001 000d dddd 01oo
_avr_set_r(avr, R_ZH, z >> 8);
_avr_set_r(avr, R_ZL, z);
}
- cycle += 2;
+ cycle += 2; // 3 cycles
} break;
/*
* Load store instructions
uint8_t r = (opcode >> 4) & 0x1f;
uint16_t x = (avr->data[R_XH] << 8) | avr->data[R_XL];
STATE("ld %s, %sX[%04x]%s\n", avr_regname(r), op == 2 ? "--" : "", x, op == 1 ? "++" : "");
-
+ cycle++; // 2 cycles (1 for tinyavr, except with inc/dec 2)
if (op == 2) x--;
_avr_set_r(avr, r, _avr_get_ram(avr, x));
if (op == 1) x++;
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++;
+ cycle++; // 2 cycles, except tinyavr
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++;
+ cycle++; // 2 cycles, except tinyavr
if (op == 2) y--;
_avr_set_r(avr, r, _avr_get_ram(avr, y));
if (op == 1) y++;
int op = opcode & 3;
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]);
+ 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]);
uint16_t x = (avr->flash[new_pc+1] << 8) | avr->flash[new_pc];
new_pc += 2;
STATE("sts 0x%04x, %s[%02x]\n", x, avr_regname(r), avr->data[r]);
+ cycle++;
_avr_set_ram(avr, x, avr->data[r]);
} break;
case 0x9001:
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 ? "++" : "");
+ cycle++;; // 2 cycles, except tinyavr
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]);
+ cycle++; // 2 cycles, except tinyavr
if (op == 2) z--;
_avr_set_ram(avr, z, avr->data[r]);
if (op == 1) z++;
avr->sreg[S_S] = avr->sreg[S_N] ^ avr->sreg[S_V];
SREG();
} break;
- case 0x9401: { // NEG – One’s Complement
+ case 0x9401: { // NEG – Two’s Complement
uint8_t r = (opcode >> 4) & 0x1f;
uint8_t rd = avr->data[r];
uint8_t res = 0x00 - rd;
} break;
case 0x940c:
case 0x940d: { // JMP Long Call to sub, 32 bits
- uint32_t a = ((opcode & 0x01f0) >> 3) | (opcode & 1);
+ avr_flashaddr_t a = ((opcode & 0x01f0) >> 3) | (opcode & 1);
uint16_t x = (avr->flash[new_pc+1] << 8) | avr->flash[new_pc];
a = (a << 16) | x;
STATE("jmp 0x%06x\n", a);
} break;
case 0x940e:
case 0x940f: { // CALL Long Call to sub, 32 bits
- uint32_t a = ((opcode & 0x01f0) >> 3) | (opcode & 1);
+ avr_flashaddr_t a = ((opcode & 0x01f0) >> 3) | (opcode & 1);
uint16_t x = (avr->flash[new_pc+1] << 8) | avr->flash[new_pc];
a = (a << 16) | x;
STATE("call 0x%06x\n", a);
new_pc += 2;
_avr_push16(avr, new_pc >> 1);
new_pc = a << 1;
- cycle += 3; // 4 cycles
+ cycle += 3; // 4 cycles; FIXME 5 on devices with 22 bit PC
TRACE_JUMP();
STACK_FRAME_PUSH();
} break;
get_r_d_10(opcode);
uint16_t res = vd * vr;
STATE("mul %s[%02x], %s[%02x] = %04x\n", avr_regname(d), vd, avr_regname(r), vr, res);
+ cycle++;
_avr_set_r(avr, 0, res);
_avr_set_r(avr, 1, res >> 8);
avr->sreg[S_Z] = res == 0;
case 0xc000: {
// RJMP 1100 kkkk kkkk kkkk
- short o = ((short)(opcode << 4)) >> 4;
+// int16_t o = ((int16_t)(opcode << 4)) >> 4; // CLANG BUG!
+ int16_t o = ((int16_t)((opcode << 4)&0xffff)) >> 4;
STATE("rjmp .%d [%04x]\n", o, new_pc + (o << 1));
new_pc = new_pc + (o << 1);
cycle++;
case 0xd000: {
// RCALL 1100 kkkk kkkk kkkk
- short o = ((short)(opcode << 4)) >> 4;
+// int16_t o = ((int16_t)(opcode << 4)) >> 4; // CLANG BUG!
+ int16_t o = ((int16_t)((opcode << 4)&0xffff)) >> 4;
STATE("rcall .%d [%04x]\n", o, new_pc + (o << 1));
_avr_push16(avr, new_pc >> 1);
new_pc = new_pc + (o << 1);
case 0xf200:
case 0xf400:
case 0xf600: { // All the SREG branches
- short o = ((short)(opcode << 6)) >> 9; // offset
+ int16_t o = ((int16_t)(opcode << 6)) >> 9; // offset
uint8_t s = opcode & 7;
int set = (opcode & 0x0400) == 0; // this bit means BRXC otherwise BRXS
int branch = (avr->sreg[s] && set) || (!avr->sreg[s] && !set);
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) {
- cycle++;
+ cycle++; // 2 cycles if taken, 1 otherwise
new_pc = new_pc + (o << 1);
}
} break;