/*
 * Found this code in
 * http://www.systems.ethz.ch/education/hs11/casp/exercises/ccodes.c
 * Completed it and expanded it to my needs. Manel Guerrero
 */


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

/* Enumeration of the supported assembly instructions. */
enum instructions { CMPL, TESTL, ADDL, SUBL, ADDB, SUBB };

/* A struct to store four important condition codes. */
struct ccodes {
  char cf, zf, sf, of;
}; 

/* Extract the CF, ZF, SF, and OF condition codes from the
   contents of the EFLAGS register passed in as an argument. */
struct ccodes getccodes(unsigned eflags) 
{
  struct ccodes ccodes;
  /********************************************************************
   * EDIT CODE BELOW
   *
   * Your task is to extract the condition codes from the EFLAGS
   * register (stored in parameter eflags) and fill the ccodes struct
   * appropriatly.
   *
   * You can find out about the bits that correspond to the condition
   * flags from the Intel Architecture Software Developer's Manual
   * (Volume 1, Section 3.4.3); the link to the document is indicated
   * on the course webpage.
   *********************************************************************/

  // Extract the carry flag from eflags such that bit 0 of ccodes.cf 
  // correspond to the value of the carry flag. #0
  ccodes.cf = (eflags >> 0) & 1;

  // Extract the zero flag from eflags.		#6
  ccodes.zf = (eflags >> 6) & 1;

  // Extract the sign flag from eflags.		#7
  ccodes.sf = (eflags >> 7) & 1;

  // Extract the overflow flag from eflags.	#11
  ccodes.of = (eflags >> 11) & 1;

  /********************************************************************
   * END EDITING
   ********************************************************************/
  
  return ccodes;
}

/* Execute the given instruction with the two operands specified and
   return the resulting contents of the EFLAGS register. */
unsigned geteflags(unsigned instr, int arg1, int arg2, int *parg3) 
{
  unsigned eflags;
  switch (instr) {
  case CMPL:
    
    asm("movl %2, %%eax; cmpl %1, %%eax; pushfl; popl %0" 
	// the assembly code string with placeholders
	
	: "=r" (eflags)          
	// output:    %0 is an output register whose value will be copied to eflags
	
	: "r" (arg1), "r" (arg2) 
	// input:     %1 and %2 are two input registers whose values are taken from arg1 and arg2
	
	: "%eax");               
        // overwrite: the %eax register is overwritten by the instruction sequence
    break;
  case TESTL:
    asm("movl %2, %%eax; testl %1, %%eax; pushfl; popl %0"
	: "=r" (eflags)
	: "r" (arg1), "r" (arg2)
	: "%eax");
    break;
  case ADDL:
    asm("movl %2, %%eax; addl %1, %%eax; pushfl; popl %0"
	: "=r" (eflags)
	: "r" (arg1), "r" (arg2)
	: "%eax");
    break;
  case SUBL:
    asm("movl %2, %%eax; subl %1, %%eax; pushfl; popl %0"
	: "=r" (eflags)
	: "r" (arg1), "r" (arg2)
	: "%eax");
    break;
  case ADDB:
    asm("movb %2, %%al; addb %1, %%al; pushfl; popl %0"
	: "=r" (eflags)
	: "r" ((unsigned char) arg1), "r" ((unsigned char) arg2)
	: "%eax");
    break;
  case SUBB:
    // asm("movb %2, %%al; subb %1, %%al; pushfl; popl %0"
    asm("movb %3, %%al; subb %2, %%al; pushfl; pushl %%eax; popl %1; popl %0"
	: "=r" (eflags), "=g" (*parg3)
	: "r" ((unsigned char) arg1), "r" ((unsigned char) arg2)
	: "%eax");
    break;

  default:
    fprintf(stderr, "Error: unsupported instruction.");
    exit(1);
  }
  return eflags;
}

/* Execute the given assembly instruction and print the CD, ZF, SF,
   and OF condition codes to stdout. */ 
void printccodes(unsigned instr, int arg1, int arg2) 
{
  char *instrstr;
  unsigned eflags;
  struct ccodes ccodes;
  int arg3;
  int *parg3 = &arg3;

  switch (instr) {
  case CMPL:  instrstr = "cmpl"; break;
  case TESTL: instrstr = "testl"; break;
  case ADDL:  instrstr = "addl"; break;
  case SUBL:  instrstr = "subl"; break;
  case ADDB:  instrstr = "addb"; break;
  case SUBB:  instrstr = "subb"; break;
  default: fprintf(stderr, "Error: unsupported instruction."); exit(1);
  }

  eflags = geteflags(instr, arg1, arg2, parg3);
  ccodes = getccodes(eflags);

  switch (instr) {
  case CMPL:
  case TESTL:
  case ADDL:
  case SUBL:
    printf("%s 0x%x, 0x%x\n\tEAX = (%d) [0x%x] -> CF = %1d, ZF = %1d, SF = %1d, OF = %1d\n", 
      instrstr, arg1, arg2, arg3, arg3, ccodes.cf, ccodes.zf, ccodes.sf, ccodes.of);
    break;
  case ADDB:
  case SUBB:
    printf("%s 0x%02x, 0x%02x\n\tAL = (%3d) [0x%02x] -> CF = %1d, ZF = %1d, SF = %1d, OF = %1d\n", 
      instrstr, (unsigned char) arg1, (unsigned char) arg2, (unsigned char) arg3, (unsigned char) arg3, ccodes.cf, ccodes.zf, ccodes.sf, ccodes.of);
  break;
  default: fprintf(stderr, "Error: unsupported instruction."); exit(1);
  }

}

/* The main function. Execute a set of assembly instructions and
   print the resulting condition codes to stdout. */
int main(int argc, char *argv[]) 
{
  printccodes(CMPL, -4, 0xfffffffc);
  printccodes(CMPL, 4, 0xfffffffc);
  printccodes(CMPL, -1, 1);
  printccodes(CMPL, 2, 0x80000000);
  printccodes(CMPL, 0x7fffffff, 0x80000000);
  printccodes(CMPL, 0x80000000, 0x7fffffff);
  printccodes(CMPL, 1, 0x7fffffff);
  printccodes(CMPL, 0x80000000, 0x80000000);
  printccodes(CMPL, 0x7fffffff, 0xffffffff);

  printccodes(ADDL, 0xffffffff, 0xffffffff);
  printccodes(ADDB, -1, 1);
  printccodes(SUBL, -1, 1);

  printccodes(SUBB, -1, 1);
  printccodes(SUBB, 1, -1);
  printccodes(SUBB, -1, -1);
  printccodes(SUBB, 1, 1);
  printccodes(SUBB, 0, 1);
  printccodes(SUBB, 1, 0);
  printccodes(SUBB, 0, -1);
  printccodes(SUBB, -1, 0);

  exit(0);
}
