/* By George Peter Staplin */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

/* This is an example of counting clock cycles that VM opcodes use.
 *
 * This simple program calculates the power of a number.
 * It isn't perfect, because it doesn't handle exponents of 1, 
 * so 2^1 = 2^2, but it seems good enough as an example.
 */

enum {
 OP_MULTIPLY,
 OP_PUSH,
 OP_END
};

/* Take an address of a uint64_t variable */
#define store_cycle_count(v) \
 do { \
  asm ("movl %0,%%ecx" : : "r" (v)); \
  asm ("rdtsc"); \
  asm ("movl %eax,(%ecx)"); \
  asm ("movl %edx,4(%ecx)"); \
 } while (0)

int main (int argc, char *argv[]) { 
 int stack[6];
 int codes[10];
 int *stack_ptr = stack + 6;
 int exponent; 
 int ip = 0;
 volatile uint64_t cycles_start = 0;
 volatile uint64_t cycles_end = 0;
 uint64_t push_cycles = 0;

 /* appname n exponent */
 if (3 != argc) {
  fprintf (stderr, "syntax is: %s number exponent\n", argv[0]);
  return EXIT_FAILURE;
 }

 exponent = (atoi (argv[2]) - 1);

 codes[0] = OP_PUSH;
 codes[1] = atoi (argv[1]);
 codes[2] = OP_PUSH;
 codes[3] = atoi (argv[1]);
 codes[4] = OP_MULTIPLY;
 codes[5] = OP_END;
 
 run_codes:

 while (OP_END != codes[ip]) {
  switch (codes[ip]) {

   case OP_PUSH:
    store_cycle_count (&cycles_start);
    stack_ptr--;
    ip++;
    *stack_ptr = codes[ip];
    ip++;
    store_cycle_count (&cycles_end);
    push_cycles += (cycles_end - cycles_start);
   break;
   case OP_MULTIPLY:
    stack_ptr++;
    *stack_ptr *= *(stack_ptr - 1);
    ip++;
   break;
  }
 }

 codes[1] = *stack_ptr;
 
 --exponent;
 if (exponent > 0) {
  stack_ptr++;
  ip = 0;
  goto run_codes;
 }

 printf ("push cycles %llu\n", push_cycles);
 printf ("*stack_ptr %d\n", *stack_ptr);

 return EXIT_SUCCESS;
}
