/* 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 0 or 1, 
 * so 2^1 = 2^2, but it seems good enough as an example.
 */

enum {
    OP_MULTIPLY,
    OP_PUSH,
    OP_END
};

typedef uint64_t Tcl_WideInt;

/* Take an address of a Tcl_WideInt variable */
#define TclStoreCycleCount(v) \
    do { \
	/* Move the address of the variable into %ecx */ \
	asm ("movl %0,%%ecx" : : "r" (v)); \
	/* Read the counter (this clobbers eax and edx) */ \
	asm ("rdtsc" : : : "eax", "edx"); \
	/* Save 64-bits for the cycle count in the variable.*/ \
	asm ("movl %eax,(%ecx)"); \
	asm ("movl %edx,4(%ecx)"); \
    } while (0)

int main (int argc, char *argv[]) { 
    int stack[6];
    int codes[10];
    int *stackPtr = stack + 6;
    int exponent; 
    int ip = 0;
    volatile Tcl_WideInt startCycles = 0;
    volatile Tcl_WideInt endCycles = 0;
    Tcl_WideInt pushCycles = 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] = codes[1];
    codes[4] = OP_MULTIPLY;
    codes[5] = OP_END;
 
    run_codes:

    while (OP_END != codes[ip]) {
	switch (codes[ip]) {
	case OP_PUSH:
	    TclStoreCycleCount (&startCycles);
	    stackPtr--;
	    ip++;
	    *stackPtr = codes[ip];
	    ip++;
	    TclStoreCycleCount (&endCycles);
	    pushCycles += (endCycles - startCycles);
	    break;
	case OP_MULTIPLY:
	    stackPtr++;
	    *stackPtr *= *(stackPtr - 1);
	    ip++;
	    break;
	}
    }

    codes[1] = *stackPtr;

    --exponent;
    if (exponent > 0) {
	stackPtr++;
	ip = 0;
	goto run_codes;
    }

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

    return EXIT_SUCCESS;
}
