X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=simavr%2Fsim%2Fsim_core.c;h=3550771268a05b97044e5206014fbb488e11697d;hb=9567c05d43d21ecc4fcb526109773f7174a6c4cd;hp=94de8f6fdcf03de518aa6509a1c8eaf6c9a5161d;hpb=5b4715f65f3f199bdff178dce139912ed64addb8;p=simavr diff --git a/simavr/sim/sim_core.c b/simavr/sim/sim_core.c index 94de8f6..3550771 100644 --- a/simavr/sim/sim_core.c +++ b/simavr/sim/sim_core.c @@ -25,6 +25,8 @@ #include #include "sim_avr.h" #include "sim_core.h" +#include "avr_flash.h" +#include "avr_watchdog.h" // SREG bit names const char * _sreg_bit_name = "cznvshti"; @@ -58,7 +60,7 @@ int donttrace = 0; #define STATE(_f, args...) { \ if (avr->trace) {\ - if (avr->codeline[avr->pc>>1]) {\ + if (avr->codeline && avr->codeline[avr->pc>>1]) {\ const char * symn = avr->codeline[avr->pc>>1]->symbol; \ int dont = 0 && dont_trace(symn);\ if (dont!=donttrace) { \ @@ -83,6 +85,41 @@ int donttrace = 0; #define SREG() #endif +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); + 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); + CRASH(); + } +#if AVR_STACK_WATCH + /* + * 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 (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; +} + +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); + CRASH(); + } + return avr->data[addr]; +} + /* * Set a register (r < 256) * if it's an IO regisrer (> 31) also (try to) call any callback that was @@ -93,10 +130,10 @@ static inline void _avr_set_r(avr_t * avr, uint8_t r, uint8_t v) REG_TOUCH(avr, r); if (r == R_SREG) { - avr->data[r] = v; + avr->data[R_SREG] = v; // unsplit the SREG for (int i = 0; i < 8; i++) - avr->sreg[i] = (avr->data[R_SREG] & (1 << i)) != 0; + avr->sreg[i] = (v & (1 << i)) != 0; SREG(); } if (r > 31) { @@ -144,8 +181,22 @@ static inline void _avr_set_ram(avr_t * avr, uint16_t addr, uint8_t v) */ static inline uint8_t _avr_get_ram(avr_t * avr, uint16_t addr) { - if (addr > 31 && addr < 256) { + if (addr == R_SREG) { + /* + * 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); + + } else if (addr > 31 && addr < 256) { uint8_t io = AVR_DATA_TO_IO(addr); + if (avr->io[io].r.c) avr->data[addr] = avr->io[io].r.c(avr, addr, avr->io[io].r.param); @@ -264,7 +315,7 @@ void avr_dump_state(avr_t * avr) #define get_r_d_10(o) \ const uint8_t r = ((o >> 5) & 0x10) | (o & 0xf); \ const uint8_t d = (o >> 4) & 0x1f;\ - const uint8_t vd = avr->data[d], vr =avr->data[r]; + const uint8_t vd = avr->data[d], vr = avr->data[r]; #define get_k_r16(o) \ const uint8_t r = 16 + ((o >> 4) & 0xf); \ const uint8_t k = ((o & 0x0f00) >> 4) | (o & 0xf); @@ -388,6 +439,7 @@ static inline int _avr_is_instruction_32_bits(avr_t * avr, uint32_t pc) */ uint16_t avr_run_one(avr_t * avr) { +#if CONFIG_SIMAVR_TRACE /* * this traces spurious reset or bad jumps */ @@ -396,15 +448,13 @@ uint16_t avr_run_one(avr_t * avr) STATE("RESET\n"); CRASH(); } + avr->touched[0] = avr->touched[1] = avr->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; -#if CONFIG_SIMAVR_TRACE - avr->touched[0] = avr->touched[1] = avr->touched[2] = 0; -#endif - switch (opcode & 0xf000) { case 0x0000: { switch (opcode) { @@ -467,8 +517,8 @@ uint16_t avr_run_one(avr_t * avr) _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; + int8_t r = 16 + (opcode & 0xf); + int8_t d = 16 + ((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); @@ -657,7 +707,8 @@ uint16_t avr_run_one(avr_t * avr) uint8_t res = vr - k - avr->sreg[S_C]; STATE("sbci %s[%02x], 0x%02x = %02x\n", avr_regname(r), avr->data[r], k, res); _avr_set_r(avr, r, res); - avr->sreg[S_Z] = res == 0; + if (res) + avr->sreg[S_Z] = 0; avr->sreg[S_N] = (res >> 7) & 1; avr->sreg[S_C] = (k + avr->sreg[S_C]) > vr; avr->sreg[S_S] = avr->sreg[S_N] ^ avr->sreg[S_V]; @@ -763,22 +814,31 @@ uint16_t avr_run_one(avr_t * avr) } break; case 0x95a8: { // WDR STATE("wdr\n"); + avr_ioctl(avr, AVR_IOCTL_WATCHDOG_RESET, 0); } break; - case 0x9409: { // IJMP Indirect jump - 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(); + case 0x95e8: { // SPM + STATE("spm\n"); + avr_ioctl(avr, AVR_IOCTL_FLASH_SPM, 0); } break; - case 0x9509: { // ICALL Indirect Call to Subroutine + case 0x9409: // IJMP Indirect jump 1001 0100 0000 1001 + case 0x9419: // EIJMP Indirect jump 1001 0100 0001 1001 bit 4 is "indirect" + case 0x9509: // ICALL Indirect Call to Subroutine 1001 0101 0000 1001 + case 0x9519: { // EICALL Indirect Call to Subroutine 1001 0101 0001 1001 bit 8 is "push pc" + int e = opcode & 0x10; + int p = opcode & 0x100; + if (e && !avr->eind) + _avr_invalid_opcode(avr); 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); + if (e) + z |= avr->data[avr->eind] << 16; + STATE("%si%s Z[%04x]\n", e?"e":"", p?"call":"jmp", z << 1); + if (p) { + cycle++; + _avr_push16(avr, new_pc >> 1); + } new_pc = z << 1; - cycle += 2; + cycle++; TRACE_JUMP(); - STACK_FRAME_PUSH(); } break; case 0x9518: // RETI case 0x9508: { // RET @@ -835,6 +895,23 @@ uint16_t avr_run_one(avr_t * avr) } cycle += 2; } break; + case 0x9006: + case 0x9007: { // ELPM Extended Load Program Memory 1001 000d dddd 01oo + if (!avr->rampz) + _avr_invalid_opcode(avr); + uint16_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8) | (avr->data[avr->rampz] << 16); + uint8_t r = (opcode >> 4) & 0x1f; + int op = opcode & 3; + STATE("elpm %s, (Z[%02x:%04x]%s)\n", avr_regname(r), z >> 16, z&0xffff, opcode?"+":""); + _avr_set_r(avr, r, avr->flash[z]); + if (op == 3) { + z++; + _avr_set_r(avr, avr->rampz, z >> 16); + _avr_set_r(avr, R_ZH, z >> 8); + _avr_set_r(avr, R_ZL, z); + } + cycle += 2; + } break; case 0x900c: case 0x900d: case 0x900e: { // LD Load Indirect from Data using X 1001 000r rrrr 11oo @@ -1156,15 +1233,12 @@ uint16_t avr_run_one(avr_t * avr) uint8_t r = (opcode >> 4) & 0x1f; uint8_t A = ((((opcode >> 9) & 3) << 4) | ((opcode) & 0xf)) + 32; STATE("out %s, %s[%02x]\n", avr_regname(A), avr_regname(r), avr->data[r]); - // todo: store to IO register _avr_set_ram(avr, A, avr->data[r]); - // avr->data[A] = ; } break; case 0xb000: { // IN Rd,A 1011 0AAr rrrr AAAA uint8_t r = (opcode >> 4) & 0x1f; uint8_t A = ((((opcode >> 9) & 3) << 4) | ((opcode) & 0xf)) + 32; STATE("in %s, %s[%02x]\n", avr_regname(r), avr_regname(A), avr->data[A]); - // todo: get the IO register _avr_set_r(avr, r, _avr_get_ram(avr, A)); } break; default: _avr_invalid_opcode(avr);