/* $Id: test-i386-add.c,v 1.2 2009/01/27 15:58:54 potyra Exp $ 
 *
 * Copyright (C) 2008-2009 FAUcc Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */
#define addr_t uint32_t

#ifndef __CHIP_H__
#define __CHIP_H__

#include <inttypes.h>

/******************************************************************************/

#define true (!false)
#define false (0)

typedef int bool;

/******************************************************************************/

extern uint8_t get_uint8(void);
extern uint16_t get_uint16(void);
extern uint32_t get_uint32(void);
extern uint64_t get_uint64(void);

/******************************************************************************/

extern uint8_t load_uint8(addr_t addr);
extern uint16_t load_uint16(addr_t addr);
extern uint32_t load_uint32(addr_t addr);
extern uint64_t load_uint64(addr_t addr);

/******************************************************************************/

extern void store_uint8(uint8_t value, addr_t addr);
extern void store_uint16(uint16_t value, addr_t addr);
extern void store_uint32(uint32_t value, addr_t addr);
extern void store_uint64(uint64_t value, addr_t addr);

/******************************************************************************/

typedef struct _bit_t *bit_t;

extern bool bit_get(bit_t);
extern void bit_set(bit_t, bool value);
extern void bit_unset(bit_t);

/******************************************************************************/

/* only use at points never reached! */
extern __attribute__((__noreturn__)) void error(const char *file, int line);

#define ERROR() error(__FILE__, __LINE__)

/******************************************************************************/

#endif /* __CHIP_H__ */

/******************************************************************************/

enum operand_t {
	OPERAND_EAX = 0x0,
	OPERAND_ECX = 0x1,
	OPERAND_EDX = 0x2,
	OPERAND_EBX = 0x3,
	OPERAND_ESP = 0x4,
	OPERAND_EBP = 0x5,
	OPERAND_ESI = 0x6,
	OPERAND_EDI = 0x7,
	OPERAND_IMMEDIATE,
	OPERAND_ADDRESS,
	OPERAND_CS,
	OPERAND_SS,
	OPERAND_DS,
	OPERAND_ES,
	OPERAND_FS,
	OPERAND_GS
};

enum operand_size_t {
	OPERAND_SIZE_1 = 1,
	OPERAND_SIZE_2 = 2,
	OPERAND_SIZE_4 = 4
};

enum prefix_lock_repeat_t {
	PREFIX_LR_NONE,
	PREFIX_LR_LOCK,
	PREFIX_LR_REPNZ,
	PREFIX_LR_REPZ
};

enum prefix_segment_override_t {
	PREFIX_SO_NONE,
	PREFIX_SO_CS,
	PREFIX_SO_SS,
	PREFIX_SO_DS,
	PREFIX_SO_ES,
	PREFIX_SO_FS,
	PREFIX_SO_GS
};

/******************************************************************************
 *
 * Global variables (CPU State)
 *
 ******************************************************************************/

// General-purpose data registers
uint32_t eax;
uint32_t ecx;
uint32_t edx;
uint32_t ebx;
uint32_t esp;
uint32_t ebp;
uint32_t esi;
uint32_t edi;

// Segment Registers
uint16_t cs; // Code Segment
uint16_t ss; // Stack Segment
uint16_t ds; // Data Segment
uint16_t es; // Data Segment
uint16_t fs; // Data Segment
uint16_t gs; // Data Segment

// Status and Control Registers
//uint32_t EFLAGS;
//uint32_t EIP;

// EFLAGS (Status Flags)
bit_t cf; // Carry Flag
bit_t pf; // Parity Flag
bit_t af; // Auxiliary Carry Flag
bit_t zf; // Zero Flag
bit_t sf; // Sign Flag
bit_t of; // Overflow Flag

/******************************************************************************/

static inline uint32_t get_uint(enum operand_size_t size) {
	uint32_t value;

	switch (size) {
		case OPERAND_SIZE_1:
			value = get_uint8();
			break;
		case OPERAND_SIZE_2:
			value = get_uint16();
			break;
		case OPERAND_SIZE_4:
			value = get_uint32();
			break;
		// no default!
	}
	return value;
}

/******************************************************************************/

static inline uint32_t mask(uint32_t t, enum operand_size_t size) {
	uint32_t value;

	switch (size) {
		case OPERAND_SIZE_1:
			value = t & 0xff;
			break;
		case OPERAND_SIZE_2:
			value = t & 0xffff;
			break;
		case OPERAND_SIZE_4:
			value = t & 0xffffffff;
			break;
		// no default!
	}
	return value;
}

/******************************************************************************/

static inline void store(uint32_t t,
			 enum operand_t operand,
			 enum operand_size_t size,
			 uint32_t addr) {
	switch (operand) {
		case OPERAND_EAX:
			eax = mask(t, size);
			break;
		case OPERAND_ECX:
			ecx = mask(t, size);
			break;
		case OPERAND_EDX:
			edx = mask(t, size);
			break;
		case OPERAND_EBX:
			ebx = mask(t, size);
			break;
		case OPERAND_ESP:
			esp = mask(t, size);
			break;
		case OPERAND_EBP:
			ebp = mask(t, size);
			break;
		case OPERAND_ESI:
			esi = mask(t, size);
			break;
		case OPERAND_EDI:
			edi = mask(t, size);
			break;
		case OPERAND_IMMEDIATE:
			ERROR();
			break;
		case OPERAND_ADDRESS:
			switch (size) {
				case OPERAND_SIZE_1:
					store_uint8(t, addr);
					break;
				case OPERAND_SIZE_2:
					store_uint16(t, addr);
					break;
				case OPERAND_SIZE_4:
					store_uint32(t, addr);
					break;
				// no default!
			}
			break;
		case OPERAND_CS:
			cs = mask(t, size);
			break;
		case OPERAND_SS:
			ss = mask(t, size);
			break;
		case OPERAND_DS:
			ds = mask(t, size);
			break;
		case OPERAND_ES:
			es = mask(t, size);
			break;
		case OPERAND_FS:
			fs = mask(t, size);
			break;
		case OPERAND_GS:
			gs = mask(t, size);
			break;
		// no default!
	}
}

static inline uint32_t load(enum operand_t operand,
			    enum operand_size_t size,
			    uint32_t addr) {
	uint32_t value;

	switch (operand) {
		case OPERAND_EAX:
			value = mask(eax, size);
			break;
		case OPERAND_ECX:
			value = mask(ecx, size);
			break;
		case OPERAND_EDX:
			value = mask(edx, size);
			break;
		case OPERAND_EBX:
			value = mask(ebx, size);
			break;
		case OPERAND_ESP:
			value = mask(esp, size);
			break;
		case OPERAND_EBP:
			value = mask(ebp, size);
			break;
		case OPERAND_ESI:
			value = mask(esi, size);
			break;
		case OPERAND_EDI:
			value = mask(edi, size);
			break;
		case OPERAND_IMMEDIATE:
			value = get_uint(size);
			break;
		case OPERAND_ADDRESS:
			switch (size) {
				case OPERAND_SIZE_1:
					value = load_uint8(addr);
					break;
				case OPERAND_SIZE_2:
					value = load_uint16(addr);
					break;
				case OPERAND_SIZE_4:
					value = load_uint32(addr);
					break;
				// no default!
			}
			break;
		case OPERAND_CS:
			value = mask(cs, size);
			break;
		case OPERAND_SS:
			value = mask(ss, size);
			break;
		case OPERAND_DS:
			value = mask(ds, size);
			break;
		case OPERAND_ES:
			value = mask(es, size);
			break;
		case OPERAND_FS:
			value = mask(fs, size);
			break;
		case OPERAND_GS:
			value = mask(gs, size);
			break;
		// no default!
	}
	return value;
}

/******************************************************************************
 *
 * SF - Sign flag
 *
 * Set equal to the most-significant bit of the result, which is the sign bit
 * of a signed integer. (0 indicates a positive value and 1 indicates a negative
 * value.)
 *
 ******************************************************************************/

static inline void update_sf(uint32_t t, enum operand_size_t size) {
	switch (size) {
		case OPERAND_SIZE_1:
			bit_set(sf, t >> 7);
			break;
		case OPERAND_SIZE_2:
			bit_set(sf, t >> 15);
			break;
		case OPERAND_SIZE_4:
			bit_set(sf, t >> 31);
			break;
		// no default!
	}
}

/******************************************************************************
 *
 * ZF - Zero flag
 *
 * Set if the result is zero; cleared otherwise.
 *
 ******************************************************************************/

static inline void update_zf(uint32_t t) {
	bit_set(zf, t == 0);
}

/******************************************************************************
 *
 * PF - Parity flag
 *
 * Set if the least-significant byte of the result contains an even number
 * of 1 bits; cleared otherwise.
 *
 ******************************************************************************/

static inline void update_pf(uint32_t t) {
	static const uint8_t parity_table[256] = {
		1, 0, 0, 1, 0, 1, 1, 0,	0, 1, 1, 0, 1, 0, 0, 1, // 0x00 - 0x0f
		0, 1, 1, 0, 1, 0, 0, 1,	1, 0, 0, 1, 0, 1, 1, 0, // 0x10 - 0x1f
		0, 1, 1, 0, 1, 0, 0, 1,	1, 0, 0, 1, 0, 1, 1, 0, // 0x20 - 0x2f
		1, 0, 0, 1, 0, 1, 1, 0,	0, 1, 1, 0, 1, 0, 0, 1, // 0x30 - 0x3f
		0, 1, 1, 0, 1, 0, 0, 1,	1, 0, 0, 1, 0, 1, 1, 0, // 0x40 - 0x4f
		1, 0, 0, 1, 0, 1, 1, 0,	0, 1, 1, 0, 1, 0, 0, 1, // 0x50 - 0x5f
		1, 0, 0, 1, 0, 1, 1, 0,	0, 1, 1, 0, 1, 0, 0, 1, // 0x60 - 0x6f
		0, 1, 1, 0, 1, 0, 0, 1,	1, 0, 0, 1, 0, 1, 1, 0, // 0x70 - 0x7f
		0, 1, 1, 0, 1, 0, 0, 1,	1, 0, 0, 1, 0, 1, 1, 0, // 0x80 - 0x8f
		1, 0, 0, 1, 0, 1, 1, 0,	0, 1, 1, 0, 1, 0, 0, 1, // 0x90 - 0x9f
		1, 0, 0, 1, 0, 1, 1, 0,	0, 1, 1, 0, 1, 0, 0, 1, // 0xa0 - 0xaf
		0, 1, 1, 0, 1, 0, 0, 1,	1, 0, 0, 1, 0, 1, 1, 0, // 0xb0 - 0xbf
		1, 0, 0, 1, 0, 1, 1, 0,	0, 1, 1, 0, 1, 0, 0, 1, // 0xc0 - 0xcf
		0, 1, 1, 0, 1, 0, 0, 1,	1, 0, 0, 1, 0, 1, 1, 0, // 0xd0 - 0xdf
		0, 1, 1, 0, 1, 0, 0, 1,	1, 0, 0, 1, 0, 1, 1, 0, // 0xe0 - 0xef
		1, 0, 0, 1, 0, 1, 1, 0,	0, 1, 1, 0, 1, 0, 0, 1, // 0xf0 - 0xff
	};
	bit_set(pf, parity_table[(uint8_t) t]);
}

/******************************************************************************
 *
 * 'Result' - Flags
 *
 * Update SF, ZF and PF.
 *
 ******************************************************************************/

static inline void update_sf_zf_pf(uint32_t t, enum operand_size_t size) {
	update_sf(t, size);
	update_zf(t);
	update_pf(t);
}

/******************************************************************************
 *
 * Arithmetic and Logic Generator Functions
 *
 ******************************************************************************/

/* Add */
static inline void add(enum operand_t operand_a, enum operand_size_t size_a, enum operand_t operand_b, enum operand_size_t size_b, uint32_t addr) {
	uint32_t t0, t1, t2;

	t1 = load(operand_a, size_a, addr);
	t2 = load(operand_b, size_b, addr);
	t0 = mask(t1 + t2, size_a);
	store(t0, operand_a, size_a, addr);
	update_sf_zf_pf(t0, size_a);
	bit_set(cf, t0 < t1);
	bit_set(of, (t1 ^ t2 ^ -1) & (t1 ^ t0));
	bit_set(af, (t1 ^ t2 ^ t0) & 0x10);
}

/******************************************************************************/

void add_eax_4_eax_4(uint32_t addr) {
	add(OPERAND_EAX, OPERAND_SIZE_4, OPERAND_EAX, OPERAND_SIZE_4, addr);
}

void add_edx_2_ebx_2(uint32_t addr) {
	add(OPERAND_EDX, OPERAND_SIZE_2, OPERAND_EBX, OPERAND_SIZE_2, addr);
}

void add_ebx_4_addr_4(uint32_t addr) {
	add(OPERAND_EBX, OPERAND_SIZE_4, OPERAND_ADDRESS, OPERAND_SIZE_4, addr);
}

void add_ecx_4_imm_4(uint32_t addr) {
	add(OPERAND_ECX, OPERAND_SIZE_4, OPERAND_IMMEDIATE, OPERAND_SIZE_4, addr);
}

void add_eax_4_imm_1(uint32_t addr) {
	add(OPERAND_EAX, OPERAND_SIZE_4, OPERAND_IMMEDIATE, OPERAND_SIZE_1, addr);
}

