#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>

/**********/
void myprintf_real(const char *fmt, ...);
/**********/

void myprintf_gcc_interpo(const char *fmt, ...)
{
  // before
  printf("gcc before\n");
  void *arg = __builtin_apply_args();
  void *ret = __builtin_apply((void*)printf, arg, 100);
  // after
  printf("gcc after\n");
  __builtin_return(ret);
}


void myprintf_asm_intepo(const char *fmt, ...) {
  //void (*target)(const char *fmt, ...) = myprintf_real;

  /* do not put anything here, mcgdb will `finish` mcgdb_thread_can_run
   * and `step` into global_microtask(). */
  printf("asm before\n");
  
  asm volatile("mov    -0xb8(%rbp),%rdi\n\t"
               "mov    -0xa8(%rbp),%rsi\n\t"
               "mov    -0xa0(%rbp),%rdx\n\t"
               "mov    -0x98(%rbp),%rcx\n\t"
               "mov    -0x90(%rbp),%r8\n\t"
               "mov    -0x88(%rbp),%r9\n\t"
               "add     $0xc0,%rsp\n\t");
  
  volatile register void ** rsp asm ("rsp");
  
  //movq	$[addrs], -8(%%rsp) // I can't compile that ...
  *(rsp-1) = myprintf_real;
  
  asm volatile(
    "mov    %rbp,%rsp\n\t"
    "pop     %rbp\n\t"
    "mov     (%rax), %rax\n\t"
    "jmpq    *%rax");

}

/**********/
void (*myprintf)(const char *, ...) = myprintf_gcc_interpo;
/**********/

int main() {
  #define A 3
  #define B 5

  myprintf("myprintf(%d, %d) works.\n", A, B);
}

void myprintf_real(const char *fmt, ...) {
  const char *p;
  va_list argp;
  int i;
  char *s;
  char fmtbuf[256];
  int test = 0;
  
  va_start(argp, fmt);
  
  for(p = fmt; *p != '\0'; p++) {
    if(*p != '%') {
      putchar(*p);
      continue;
    }
    
    switch(*++p) {
    case 'c':
      i = va_arg(argp, int);
      putchar(i);
      break;
    case 'd':
      i = va_arg(argp, int);
      sprintf(fmtbuf,"%d",i);
      fputs(fmtbuf, stdout);
      break;
    }
    test++;
  }
}
