#include "assembler.h"

#include <stdlib.h>

/*
gcc -c -ansi -Wall -Wextra -Wpedantic -Werror:
	-- warns about trailing comma in enumerations.
	-- kept because of chad coding standard.
	-- unused functions for now, obviously...
clang -c -ansi -Weverything -Werror:
	-- exactly same warnings as with gcc above.
splint -fcnuse +boolint +charint assembler.c:
	-- we obviously ignore unused functions for now.
	-- we assume bool is int, which is true.
	-- we assume char is int, which is true.
	-- assembler.c:167:13: Function fabs shadows outer declaration
	-- ^ function from <math.h>, which we don't even use.
splint assembler.h:
	-- no warnings at all.
*/

#define REGULAR_START   (ADD)
#define REGULAR_COUNT   (UMUL - REGULAR_START)
#define IRREGULAR_START (UMUL)
#define IRREGULAR_COUNT (NOP - IRREGULAR_START)
#define SPECIAL_1_START (NOP)
#define SPECIAL_1_COUNT (SYSENTER - SPECIAL_1_START)
#define SPECIAL_2_START (SYSENTER)
#define SPECIAL_2_COUNT (ENTER - SPECIAL_2_START)

typedef signed   int  form;
typedef unsigned int  next;
typedef unsigned char byte;

static next   input_count; /* SHOULD I EXPOSE THESE? */
static form * input_array;
static next   output_count;
static byte * output_array;

static void place (form when, byte data) {
	output_array [output_count] = data;

	output_count += (next) when;
}

static form valid (form data) { return ((data >= 0) && (data <= 15)); }
static form lower (form data) { return ((data >= 0) && (data <=  7)); }
static form upper (form data) { return ((data >= 8) && (data <= 15)); }

static void build_short_prefix (form when) {
	place (when, 0X66);
}

static void build_long_prefix (form       when,
                               size_index size,
                               type_index to,
                               form       destination,
                               type_index from,
                               form       source) {
	place (when, (byte) (0X40
	      + 0X01 * ((to   == REG) && (upper (destination)))
	      + 0X04 * ((from == REG) && (upper (source)))
	      + 0X08 *  (size == D64)));
}

static void build_register_direction (form when,
                                      form destination,
                                      form source) { /* LENGTH */
	place (when, (byte) (0XC0 + 0X01 * (destination % 8) + 0X08 * (source % 8)));
}

static void build_register_redirection (form when,
                                        form direction) {
	place (when, (byte) (0X05 + 0X08 * (direction % 8)));
}

static void build_constant (form       when,
                            size_index size) {
	place (when, (byte) (0X80 + 0X01 * (size != D8)));
}

static void build_regular_instruction (form       when,
                                       byte       code,
                                       size_index size,
                                       type_index to,
                                       form       destination,
                                       type_index from) {
	place (when, (byte) (code
	      + destination % 8 * ((to == REG) && (from == IMM))
	      + 0X01 *  (size != D8)
	      + 0X02 * ((from == MEM) && (to == REG))
	      + 0X04 * ((from == IMM) && (to == MEM))
	      + 0XC0 * ((from == IMM) && (to == REG))));
}

static void build_regular (operation_index operation,
                           size_index      size,
                           type_index      to,
                           form            destination,
                           type_index      from,
                           form            source) {
	build_short_prefix (size == D16);

	build_long_prefix ((size == D64)
	               || ((to   == REG) && (upper (destination)))
	               || ((from == REG) && (upper (source))),
	                   size, to, destination, from, source);

	build_constant (from == IMM, size);

	build_regular_instruction (1, (byte) (0X08 * operation),
	                           size, to, destination, from);

	build_register_direction ((to == REG) && (from == REG),
	                          destination, source);

	build_register_redirection ((to == REG) && (from == MEM), destination);
	build_register_redirection ((to == MEM) && (from == REG), source);
}

static void build_special_1 (operation_index operation) {
	const byte data [1 * SPECIAL_1_COUNT] = {
		0X90, 0XC3, 0XCB, 0XC9, 0XF0, 0XF4
	};

	place (1, data [operation - SPECIAL_1_START]);
}

static void build_special_2 (operation_index operation) {
	const byte data [2 * SPECIAL_2_COUNT] = {
		0X0F, 0X0F, 0X0F, 0X0F, 0XF3, 0X0F,
		0X34, 0X35, 0X05, 0X07, 0X90, 0XA2
	};

	place (1, data [operation - SPECIAL_2_START]);
	place (1, data [operation - SPECIAL_2_START + SPECIAL_2_COUNT]);
}