regbits: Added a "raw" variant to get/set
[simavr] / simavr / sim / sim_avr.c
index 0512c2a..e13117d 100644 (file)
@@ -41,8 +41,13 @@ int avr_init(avr_t * avr)
        // cpu is in limbo before init is finished.
        avr->state = cpu_Limbo;
        avr->frequency = 1000000;       // can be overriden via avr_mcu_section
+       if (avr->special_init)
+               avr->special_init(avr);
        if (avr->init)
                avr->init(avr);
+       // set default (non gdb) fast callbacks
+       avr->run = avr_callback_run_raw;
+       avr->sleep = avr_callback_sleep_raw;
        avr->state = cpu_Running;
        avr_reset(avr); 
        return 0;
@@ -50,9 +55,17 @@ int avr_init(avr_t * avr)
 
 void avr_terminate(avr_t * avr)
 {
-       if (avr->vcd)
+       if (avr->special_deinit)
+               avr->special_deinit(avr);
+       if (avr->vcd) {
                avr_vcd_close(avr->vcd);
-       avr->vcd = NULL;
+               avr->vcd = NULL;
+       }
+       avr_deallocate_ios(avr);
+
+       if (avr->flash) free(avr->flash);
+       if (avr->data) free(avr->data);
+       avr->flash = avr->data = NULL;
 }
 
 void avr_reset(avr_t * avr)
@@ -116,24 +129,56 @@ void avr_set_command_register(avr_t * avr, avr_io_addr_t addr)
                avr_register_io_write(avr, addr, _avr_io_command_write, NULL);
 }
 
+static void _avr_io_console_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+{
+       static char * buf = NULL;
+       static int size = 0, len = 0;
+
+       if (v == '\r') {
+               printf("O:" "%s" "" "\n", buf);
+               fflush(stdout);
+       }
+       if (len + 1 >= size) {
+               size += 128;
+               buf = (char*)realloc(buf, size);
+       }
+       buf[len++] = v;
+}
+
+void avr_set_console_register(avr_t * avr, avr_io_addr_t addr)
+{
+       if (addr)
+               avr_register_io_write(avr, addr, _avr_io_console_write, NULL);
+}
+
 void avr_loadcode(avr_t * avr, uint8_t * code, uint32_t size, uint32_t address)
 {
+       if (size > avr->flashend+1) {
+               fprintf(stderr, "avr_loadcode(): Attempted to load code of size %d but flash size is only %d.\n",
+                       size, avr->flashend+1);
+               abort();
+       }
        memcpy(avr->flash + address, code, size);
 }
 
+void avr_callback_sleep_gdb(avr_t * avr, avr_cycle_count_t howLong)
+{
+       uint32_t usec = avr_cycles_to_usec(avr, howLong);
+       while (avr_gdb_processor(avr, usec))
+               ;
+}
 
-int avr_run(avr_t * avr)
+void avr_callback_run_gdb(avr_t * avr)
 {
        avr_gdb_processor(avr, avr->state == cpu_Stopped);
 
        if (avr->state == cpu_Stopped)
-               return avr->state;
+               return ;
 
        // if we are stepping one instruction, we "run" for one..
        int step = avr->state == cpu_Step;
-       if (step) {
+       if (step)
                avr->state = cpu_Running;
-       }
        
        uint16_t new_pc = avr->pc;
 
@@ -145,16 +190,13 @@ int avr_run(avr_t * avr)
        }
 
        // if we just re-enabled the interrupts...
-       if (avr->sreg[S_I] && !(avr->data[R_SREG] & (1 << S_I))) {
-       //      printf("*** %s: Renabling interrupts\n", __FUNCTION__);
+       // double buffer the I flag, to detect that edge
+       if (avr->sreg[S_I] && !avr->i_shadow)
                avr->pending_wait++;
-       }
-       avr_io_t * port = avr->io_port;
-       while (port) {
-               if (port->run)
-                       port->run(port);
-               port = port->next;
-       }
+       avr->i_shadow = avr->sreg[S_I];
+
+       // run the cycle timers, get the suggested sleeo time
+       // until the next timer is due
        avr_cycle_count_t sleep = avr_cycle_timer_process(avr);
 
        avr->pc = new_pc;
@@ -168,32 +210,70 @@ int avr_run(avr_t * avr)
                /*
                 * try to sleep for as long as we can (?)
                 */
-               uint32_t usec = avr_cycles_to_usec(avr, sleep);
-       //      printf("sleep usec %d cycles %d\n", usec, sleep);
-               if (avr->gdb) {
-                       while (avr_gdb_processor(avr, usec))
-                               ;
-               } else
-                       usleep(usec);
+               avr->sleep(avr, sleep);
                avr->cycle += 1 + sleep;
        }
-       // Interrupt servicing might change the PC too
-       if (avr->state == cpu_Running || avr->state == cpu_Sleeping) {
+       // Interrupt servicing might change the PC too, during 'sleep'
+       if (avr->state == cpu_Running || avr->state == cpu_Sleeping)
                avr_service_interrupts(avr);
+       
+       // if we were stepping, use this state to inform remote gdb
+       if (step)
+               avr->state = cpu_StepDone;
+
+}
+
+void avr_callback_sleep_raw(avr_t * avr, avr_cycle_count_t howLong)
+{
+       uint32_t usec = avr_cycles_to_usec(avr, howLong);
+       usleep(usec);
+}
+
+void avr_callback_run_raw(avr_t * avr)
+{
+
+       uint16_t new_pc = avr->pc;
 
-               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);
+       if (avr->state == cpu_Running) {
+               new_pc = avr_run_one(avr);
+#if CONFIG_SIMAVR_TRACE
+               avr_dump_state(avr);
+#endif
        }
 
-       if (step) {
-               avr->state = cpu_StepDone;
+       // if we just re-enabled the interrupts...
+       // double buffer the I flag, to detect that edge
+       if (avr->sreg[S_I] && !avr->i_shadow)
+               avr->pending_wait++;
+       avr->i_shadow = avr->sreg[S_I];
+
+       // run the cycle timers, get the suggested sleeo time
+       // until the next timer is due
+       avr_cycle_count_t sleep = avr_cycle_timer_process(avr);
+
+       avr->pc = new_pc;
+
+       if (avr->state == cpu_Sleeping) {
+               if (!avr->sreg[S_I]) {
+                       printf("simavr: sleeping with interrupts off, quitting gracefully\n");
+                       avr_terminate(avr);
+                       exit(0);
+               }
+               /*
+                * try to sleep for as long as we can (?)
+                */
+               avr->sleep(avr, sleep);
+               avr->cycle += 1 + sleep;
        }
+       // Interrupt servicing might change the PC too, during 'sleep'
+       if (avr->state == cpu_Running || avr->state == cpu_Sleeping)
+               avr_service_interrupts(avr);
+}
 
+
+int avr_run(avr_t * avr)
+{
+       avr->run(avr);
        return avr->state;
 }
 
@@ -201,15 +281,19 @@ int avr_run(avr_t * avr)
 extern avr_kind_t tiny13;
 extern avr_kind_t tiny2313;
 extern avr_kind_t tiny25,tiny45,tiny85;
+extern avr_kind_t tiny24,tiny44,tiny84;
 extern avr_kind_t mega48,mega88,mega168,mega328;
 extern avr_kind_t mega164,mega324,mega644;
+extern avr_kind_t mega128;
 
 avr_kind_t * avr_kind[] = {
        &tiny13,
        &tiny2313,
        &tiny25, &tiny45, &tiny85,
+       &tiny24, &tiny44, &tiny84,
        &mega48, &mega88, &mega168, &mega328,
        &mega164, &mega324, &mega644,
+       &mega128,
        NULL
 };