core: Simplify changes to SREG
[simavr] / simavr / sim / sim_core.c
index 197b719..31c2ab3 100644 (file)
@@ -25,6 +25,8 @@
 #include <ctype.h>
 #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);
@@ -301,7 +352,7 @@ void avr_dump_state(avr_t * avr)
 /****************************************************************************\
  *
  * Helper functions for calculating the status register bit values.
- * See the Atmel data sheet for the instuction set for more info.
+ * See the Atmel data sheet for the instruction set for more info.
  *
 \****************************************************************************/
 
@@ -375,36 +426,35 @@ static inline int _avr_is_instruction_32_bits(avr_t * avr, uint32_t pc)
  * 
  * The decoder was written by following the datasheet in no particular order.
  * As I went along, I noticed "bit patterns" that could be used to factor opcodes
- * However, a lot of these only becane apparent later on, so SOME instructions
+ * However, a lot of these only became apparent later on, so SOME instructions
  * (skip of bit set etc) are compact, and some could use some refactoring (the ALU
  * ones scream to be factored).
  * I assume that the decoder could easily be 2/3 of it's current size.
  * 
  * + 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.
+ * + It also doesn't check whether the core it's
+ *   emulating is supposed to have the fancy instructions, like multiply and such.
  * 
  * for now all instructions take "one" cycle, the cycle+=<extra> needs to be added.
  */
 uint16_t avr_run_one(avr_t * avr)
 {
+#if CONFIG_SIMAVR_TRACE
        /*
-        * this traces spurious reset or bad jump/opcodes and dumps the last 32 "jumps" to track it down
+        * this traces spurious reset or bad jumps
         */
        if ((avr->pc == 0 && avr->cycle > 0) || avr->pc >= avr->codeend) {
                avr->trace = 1;
                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) {
@@ -763,6 +813,11 @@ 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 0x95e8: { // SPM
+                                       STATE("spm\n");
+                                       avr_ioctl(avr, AVR_IOCTL_FLASH_SPM, 0);
                                }       break;
                                case 0x9409: { // IJMP Indirect jump
                                        uint16_t z = avr->data[R_ZL] | (avr->data[R_ZH] << 8);
@@ -1156,15 +1211,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);