text: #include <unistd.h>
  #include <sys/stat.h>
  #include <fcntl.h>
  #include <stdlib.h>
  #include <stdbool.h>
  #include <string.h>
  #include <stdio.h>
  #include <libgen.h>
  #include <simavr/sim_avr.h>
  #include <simavr/sim_gdb.h>
  #include <simavr/avr_ioport.h>
  #include <simavr/sim_elf.h>
  #include <simavr/avr_uart.h>
  
  static avr_t *g_avr;
  static avr_irq_t *g_uart_irq;
  
  static struct {
      uint8_t argc;
      char args[3][16];
  } g_args;
  static int g_args_idx;
  static bool g_uart_xon;
  static bool g_status_ok;
  
  static void uart_tx_hook(struct avr_irq_t * irq, uint32_t value, void * param)
  {
      fputc(value, stdout);
      fflush(stdout);
  }
  
  static bool stdin_can_read()  
  {
    struct timeval tv;
    fd_set fds;
    tv.tv_sec = 0;
    tv.tv_usec = 0;
    FD_ZERO(&fds);
    FD_SET(STDIN_FILENO, &fds);
    select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
    return (FD_ISSET(0, &fds));
  }
  
  static void avr_logger(avr_t * avr, const int level, const char * format, va_list ap)
  {
      if ((!avr && level <= LOG_WARNING) || (avr && avr->log >= level)) {
          vfprintf(stderr , format, ap);
      }
  }
  
  static void uart_xon_hook(struct avr_irq_t * irq, uint32_t value, void * param)
  {
      g_uart_xon = true;
      int v;
      
      if (feof(stdin))
      {
          avr_raise_irq(&g_uart_irq[1], UART_INPUT_FE);
          return;
      }
  
      while (g_uart_xon)
      {
          if (g_args_idx < sizeof(g_args))
          {
              v = ((char*)&g_args)[g_args_idx++];
          }
          else if (stdin_can_read())
          {
              v = fgetc(stdin);
          }
          else
          {
              break;        
          }
          
          if (v != EOF)
          {
              avr_raise_irq(&g_uart_irq[1], v);
          }
          else
          {
              avr_raise_irq(&g_uart_irq[1], UART_INPUT_FE);
              break;
          }
      }
  }
  
  static void uart_xoff_hook(struct avr_irq_t * irq, uint32_t value, void * param)
  {
      g_uart_xon = false;
  }
  
  void init_uart()
  {
      const char *irq_names[2] = {"8<uart_in", "8>uart_out"};
      g_uart_irq = avr_alloc_irq(&g_avr->irq_pool, 0, 2, irq_names);
      avr_irq_register_notify(&g_uart_irq[0], &uart_tx_hook, NULL);
      
      uint32_t flags = 0;
      avr_ioctl(g_avr, AVR_IOCTL_UART_GET_FLAGS('0'), &flags);
      flags &= ~AVR_UART_FLAG_STDIO;
      avr_ioctl(g_avr, AVR_IOCTL_UART_SET_FLAGS('0'), &flags);
  
      avr_irq_t *src = avr_io_getirq(g_avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_OUTPUT);
      avr_irq_t *dst = avr_io_getirq(g_avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_INPUT);
      avr_connect_irq(src, &g_uart_irq[0]);
      avr_connect_irq(&g_uart_irq[1], dst);
      
      avr_irq_t *xon = avr_io_getirq(g_avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_OUT_XON);
      avr_irq_t *xoff = avr_io_getirq(g_avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_OUT_XOFF);
      avr_irq_register_notify(xon, uart_xon_hook, NULL);
      avr_irq_register_notify(xoff, uart_xoff_hook, NULL);
  }
  
  static void status_ok_hook(struct avr_irq_t * irq, uint32_t value, void * param)
  {
      g_status_ok = value;
  }
  
  int main(int argc, char *argv[])
  {
      avr_global_logger_set(&avr_logger);
      g_avr = avr_make_mcu_by_name("atmega1284");
      if (!g_avr)
      {
          fprintf(stderr, "avr_make_mcu_by_name failed\n");
          return 1;
      }
      
      if (argc < 2)
      {
          fprintf(stderr, "Usage: %s [-g] binary [args ...]\n", argv[0]);
          return 2;
      }
      
      const char *filename = argv[1];
      bool enable_gdb = false;
      int argc_offset = 2;
      
      if (strcmp(filename, "-g") == 0)
      {
          enable_gdb = true;
          argc_offset = 3;
          filename = argv[2];
      }
      
      elf_firmware_t firmware;
      elf_read_firmware(filename, &firmware);
      avr_init(g_avr);
  	avr_load_firmware(g_avr, &firmware);
  	g_avr->frequency = 8000000;
  
      if (enable_gdb)
      {
          g_avr->state = cpu_Stopped;
          g_avr->gdb_port = 1234;
          avr_gdb_init(g_avr);
      }
  
      init_uart();
      
      avr_irq_register_notify(avr_io_getirq(g_avr, AVR_IOCTL_IOPORT_GETIRQ('B'), 1), status_ok_hook, NULL);
  
      // Pass the rest of arguments to application inside simulator
      g_args.argc = argc - argc_offset;
      if (g_args.argc > 3) g_args.argc = 3;
      for (int i = 0; i < g_args.argc; i++)
      {
          strncpy(g_args.args[i], argv[i + argc_offset], 15);
      }
      
      while (1)
      {
  		int state = avr_run(g_avr);
  		if (state == cpu_Done)
  		    break;
  		    
     		if (state == cpu_Crashed)
     		{
     		    fprintf(stderr, "CPU Crashed\n");
  			return 3;
  		}
  	}
      
      if (g_status_ok)
      {
          return 0;
      }
      else
      {
          fprintf(stderr, "Received error status from simulation\n");
          return 5;    
      }
  }
  