miasm/jitter/op_semantics.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <math.h>
#include "op_semantics.h"
const uint8_t parity_table[256] = {
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
};
uint16_t bcdadd_16(uint16_t a, uint16_t b)
{
int carry = 0;
int i,j = 0;
uint16_t res = 0;
int nib_a, nib_b;
for (i = 0; i < 16; i += 4) {
nib_a = (a >> i) & (0xF);
nib_b = (b >> i) & (0xF);
j = (carry + nib_a + nib_b);
if (j >= 10) {
carry = 1;
j -= 10;
j &=0xf;
}
else {
carry = 0;
}
res += j << i;
}
return res;
}
uint16_t bcdadd_cf_16(uint16_t a, uint16_t b)
{
int carry = 0;
int i,j = 0;
int nib_a, nib_b;
for (i = 0; i < 16; i += 4) {
nib_a = (a >> i) & (0xF);
nib_b = (b >> i) & (0xF);
j = (carry + nib_a + nib_b);
if (j >= 10) {
carry = 1;
j -= 10;
j &=0xf;
}
else {
carry = 0;
}
}
return carry;
}
unsigned int mul_lo_op(unsigned int size, unsigned int a, unsigned int b)
{
unsigned int mask;
switch (size) {
case 8: mask = 0xff; break;
case 16: mask = 0xffff; break;
case 32: mask = 0xffffffff; break;
default: fprintf(stderr, "inv size in mul %d\n", size); exit(EXIT_FAILURE);
}
a &= mask;
b &= mask;
return ((int64_t)a * (int64_t) b) & mask;
}
unsigned int mul_hi_op(unsigned int size, unsigned int a, unsigned int b)
{
uint64_t res = 0;
unsigned int mask;
switch (size) {
case 8: mask = 0xff; break;
case 16: mask = 0xffff; break;
case 32: mask = 0xffffffff; break;
default: fprintf(stderr, "inv size in mul %d\n", size); exit(EXIT_FAILURE);
}
a &= mask;
b &= mask;
res = ((uint64_t)a * (uint64_t)b);
return (res >> 32) & mask;
}
unsigned int imul_lo_op_08(char a, char b)
{
return a*b;
}
unsigned int imul_lo_op_16(short a, short b)
{
return a*b;
}
unsigned int imul_lo_op_32(int a, int b)
{
return a*b;
}
int imul_hi_op_08(char a, char b)
{
int64_t res = 0;
res = a*b;
return (int)(res>>8);
}
int imul_hi_op_16(short a, short b)
{
int64_t res = 0;
res = a*b;
return (int)(res>>16);
}
int imul_hi_op_32(int a, int b)
{
int64_t res = 0;
res = (int64_t)a*(int64_t)b;
return (int)(res>>32ULL);
}
unsigned int umul16_lo(unsigned short a, unsigned short b)
{
return (a*b) & 0xffff;
}
unsigned int umul16_hi(unsigned short a, unsigned short b)
{
uint32_t c;
c = a*b;
return (c>>16) & 0xffff;
}
uint64_t rot_left(uint64_t size, uint64_t a, uint64_t b)
{
uint64_t tmp;
b = b & 0x3F;
b %= size;
switch(size){
case 8:
tmp = (a << b) | ((a & 0xFF) >> (size - b));
return tmp & 0xFF;
case 16:
tmp = (a << b) | ((a & 0xFFFF) >> (size - b));
return tmp & 0xFFFF;
case 32:
tmp = (a << b) | ((a & 0xFFFFFFFF) >> (size - b));
return tmp & 0xFFFFFFFF;
case 64:
tmp = (a << b) | ((a&0xFFFFFFFFFFFFFFFF) >> (size - b));
return tmp & 0xFFFFFFFFFFFFFFFF;
/* Support cases for rcl */
case 9:
tmp = (a << b) | ((a & 0x1FF) >> (size - b));
return tmp & 0x1FF;
case 17:
tmp = (a << b) | ((a & 0x1FFFF) >> (size - b));
return tmp & 0x1FFFF;
case 33:
tmp = (a << b) | ((a & 0x1FFFFFFFF) >> (size - b));
return tmp & 0x1FFFFFFFF;
/* TODO XXX: support rcl in 64 bit mode */
default:
fprintf(stderr, "inv size in rotleft %"PRIX64"\n", size);
exit(EXIT_FAILURE);
}
}
uint64_t rot_right(uint64_t size, uint64_t a, uint64_t b)
{
uint64_t tmp;
b = b & 0x3F;
b %= size;
switch(size){
case 8:
tmp = ((a & 0xFF) >> b) | (a << (size - b));
return tmp & 0xff;
case 16:
tmp = ((a & 0xFFFF) >> b) | (a << (size - b));
return tmp & 0xFFFF;
case 32:
tmp = ((a & 0xFFFFFFFF) >> b) | (a << (size - b));
return tmp & 0xFFFFFFFF;
case 64:
tmp = ((a & 0xFFFFFFFFFFFFFFFF) >> b) | (a << (size - b));
return tmp & 0xFFFFFFFFFFFFFFFF;
/* Support cases for rcr */
case 9:
tmp = ((a & 0x1FF) >> b) | (a << (size - b));
return tmp & 0x1FF;
case 17:
tmp = ((a & 0x1FFFF) >> b) | (a << (size - b));
return tmp & 0x1FFFF;
case 33:
tmp = ((a & 0x1FFFFFFFF) >> b) | (a << (size - b));
return tmp & 0x1FFFFFFFF;
/* TODO XXX: support rcr in 64 bit mode */
default:
fprintf(stderr, "inv size in rotright %"PRIX64"\n", size);
exit(EXIT_FAILURE);
}
}
/*
* Count leading zeros - count the number of zero starting at the most
* significant bit
*
* Example:
* - cntleadzeros(size=32, src=2): 30
* - cntleadzeros(size=32, src=0): 32
*/
uint64_t cntleadzeros(uint64_t size, uint64_t src)
{
int64_t i;
for (i=(int64_t)size-1; i>=0; i--){
if (src & (1ull << i))
return (uint64_t)(size - (i + 1));
}
return (uint64_t)size;
}
/*
* Count trailing zeros - count the number of zero starting at the least
* significant bit
*
* Example:
* - cnttrailzeros(size=32, src=2): 1
* - cnttrailzeros(size=32, src=0): 32
*/
unsigned int cnttrailzeros(uint64_t size, uint64_t src)
{
uint64_t i;
for (i=0; i<size; i++){
if (src & (1ull << i))
return (unsigned int)i;
}
return (unsigned int)size;
}
unsigned int my_imul08(unsigned int a, unsigned int b)
{
char a08, b08;
short a16;
a08 = a&0xFF;
b08 = b&0xFF;
a16 = a08*b08;
return (int)a16;
}
unsigned int x86_cpuid(unsigned int a, unsigned int reg_num)
{
if (reg_num >3){
fprintf(stderr, "not implemented x86_cpuid reg %x\n", reg_num);
exit(EXIT_FAILURE);
}
// cases are output: EAX: 0; EBX: 1; ECX: 2; EDX: 3
if (a == 0){
switch(reg_num){
case 0:
return 0xa;
// "GenuineIntel"
case 1:
return 0x756E6547;
case 2:
return 0x6C65746E;
case 3:
return 0x49656E69;
}
}
else if (a == 1){
switch(reg_num){
case 0:
// Using a version too high will enable recent
// instruction set
return 0x000006FB;
//return 0x00020652;
case 1:
//return 0x02040800;
return 0x00000800;
case 2:
//return 0x0004E3BD;
return 0x00000209;
case 3:
return (/* fpu */ 1 << 0) |
(/* tsc */ 1 << 4) |
(/* cx8 */ 1 << 8) |
(/* cmov */ 1 << 15) |
(/* mmx */ 1 << 23) |
(/* sse */ 1 << 25) |
(/* sse2 */ 1 << 26) |
(/* ia64 */ 1 << 30);
}
}
// Cache and TLB
else if (a == 2){
switch(reg_num){
case 0:
return 0x00000000;
case 1:
return 0x00000000;
case 2:
return 0x00000000;
case 3:
return 0x00000000;
}
}
// Intel thread/core and cache topology
else if (a == 4){
switch(reg_num){
case 0:
return 0x00000000;
case 1:
return 0x00000000;
case 2:
return 0x00000000;
case 3:
return 0x00000000;
}
}
// Extended features
else if (a == 7){
switch(reg_num){
case 0:
return 0x00000000;
case 1:
return (/* fsgsbase */ 1 << 0) | (/* bmi1 */ 1 << 3);
case 2:
return 0x00000000;
case 3:
return 0x00000000;
}
}
// Extended Function CPUID Information
else if (a == 0x80000000){
switch(reg_num){
case 0:
// Pentium 4 Processor supporting Hyper-Threading
// Technology to Intel Xeon Processor 5100 Series
return 0x80000008;
case 1:
return 0x00000000;
case 2:
return 0x00000000;
case 3:
return 0x00000000;
}
}
else if (a == 0x80000001){
switch(reg_num){
case 0:
// Extended Processor Signature and Extended Feature
// Bits
return 0x00000000;
case 1:
return 0x00000000;
case 2:
return (/* LAHF-SAHF */ 1 << 0)
| (/* LZCNT */ 0 << 5)
| (/* PREFETCHW */ 1 << 8);
case 3:
return (/* SYSCALL/SYSRET */ 1 << 11)
| (/* Execute Disable Bit available */ 0 << 20)
| (/* 1-GByte pages available */ 0 << 26)
| (/* RDTSCP and IA32_TSC_AUX available */ 0 << 27)
| (/* Intel ® 64 Architecture available */ 1 << 29);
}
}
else{
fprintf(stderr, "WARNING not implemented x86_cpuid index %X!\n", a);
exit(EXIT_FAILURE);
}
return 0;
}
//#define DEBUG_MIASM_DOUBLE
void dump_float(void)
{
/*
printf("%e\n", vmmngr.float_st0);
printf("%e\n", vmmngr.float_st1);
printf("%e\n", vmmngr.float_st2);
printf("%e\n", vmmngr.float_st3);
printf("%e\n", vmmngr.float_st4);
printf("%e\n", vmmngr.float_st5);
printf("%e\n", vmmngr.float_st6);
printf("%e\n", vmmngr.float_st7);
*/
}
typedef union {
uint32_t u32;
float flt;
} float_uint32_t;
typedef union {
uint64_t u64;
double dbl;
} double_uint64_t;
uint32_t fpu_fadd32(uint32_t a, uint32_t b)
{
float_uint32_t a_cast, b_cast, c_cast;
a_cast.u32 = a;
b_cast.u32 = b;
c_cast.flt = a_cast.flt + b_cast.flt;
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e + %e -> %e\n", a, b, c_cast.flt);
#endif
return c_cast.u32;
}
uint64_t fpu_fadd64(uint64_t a, uint64_t b)
{
double_uint64_t a_cast, b_cast, c_cast;
a_cast.u64 = a;
b_cast.u64 = b;
c_cast.dbl = a_cast.dbl + b_cast.dbl;
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e + %e -> %e\n", a, b, c_cast.dbl);
#endif
return c_cast.u64;
}
uint32_t fpu_fsub32(uint32_t a, uint32_t b)
{
float_uint32_t a_cast, b_cast, c_cast;
a_cast.u32 = a;
b_cast.u32 = b;
c_cast.flt = a_cast.flt - b_cast.flt;
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e + %e -> %e\n", a, b, c_cast.flt);
#endif
return c_cast.u32;
}
uint64_t fpu_fsub64(uint64_t a, uint64_t b)
{
double_uint64_t a_cast, b_cast, c_cast;
a_cast.u64 = a;
b_cast.u64 = b;
c_cast.dbl = a_cast.dbl - b_cast.dbl;
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e + %e -> %e\n", a, b, c_cast.dbl);
#endif
return c_cast.u64;
}
uint32_t fpu_fmul32(uint32_t a, uint32_t b)
{
float_uint32_t a_cast, b_cast, c_cast;
a_cast.u32 = a;
b_cast.u32 = b;
c_cast.flt = a_cast.flt * b_cast.flt;
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e * %e -> %e\n", a, b, c_cast.flt);
#endif
return c_cast.u32;
}
uint64_t fpu_fmul64(uint64_t a, uint64_t b)
{
double_uint64_t a_cast, b_cast, c_cast;
a_cast.u64 = a;
b_cast.u64 = b;
c_cast.dbl = a_cast.dbl * b_cast.dbl;
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e * %e -> %e\n", a, b, c_cast.dbl);
#endif
return c_cast.u64;
}
uint32_t fpu_fdiv32(uint32_t a, uint32_t b)
{
float_uint32_t a_cast, b_cast, c_cast;
a_cast.u32 = a;
b_cast.u32 = b;
c_cast.flt = a_cast.flt / b_cast.flt;
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e * %e -> %e\n", a, b, c_cast.flt);
#endif
return c_cast.u32;
}
uint64_t fpu_fdiv64(uint64_t a, uint64_t b)
{
double_uint64_t a_cast, b_cast, c_cast;
a_cast.u64 = a;
b_cast.u64 = b;
c_cast.dbl = a_cast.dbl / b_cast.dbl;
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e * %e -> %e\n", a, b, c_cast.dbl);
#endif
return c_cast.u64;
}
double fpu_ftan(double a)
{
double b;
b = tan(a);
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e tan %e\n", a, b);
#endif
return b;
}
double fpu_frndint(double a)
{
int64_t b;
double c;
b = (int64_t)a;
c = (double)b;
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e double %e\n", a, c);
#endif
return c;
}
double fpu_fsin(double a)
{
double b;
b = sin(a);
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e sin %e\n", a, b);
#endif
return b;
}
double fpu_fcos(double a)
{
double b;
b = cos(a);
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e cos %e\n", a, b);
#endif
return b;
}
double fpu_fscale(double a, double b)
{
double c;
c = a * exp2(trunc(b));
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e *exp2 %e -> %e\n", a, b, c);
#endif
return c;
}
double fpu_f2xm1(double a)
{
double b;
b = exp2(a)-1;
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e exp2 -1 %e\n", a, b);
#endif
return b;
}
uint32_t fpu_fsqrt32(uint32_t a)
{
float_uint32_t a_cast;
a_cast.u32 = a;
a_cast.flt = sqrtf(a_cast.flt);
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e sqrt %e\n", a, a_cast.flt);
#endif
return a_cast.u32;
}
uint64_t fpu_fsqrt64(uint64_t a)
{
double_uint64_t a_cast;
a_cast.u64 = a;
a_cast.dbl = sqrt(a_cast.dbl);
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e sqrt %e\n", a, a_cast.dbl);
#endif
return a_cast.u64;
}
uint64_t fpu_fabs64(uint64_t a)
{
double_uint64_t a_cast;
a_cast.u64 = a;
a_cast.dbl = fabs(a_cast.dbl);
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e abs %e\n", a, a_cast.dbl);
#endif
return a_cast.u64;
}
uint64_t fpu_fprem64(uint64_t a, uint64_t b)
{
double_uint64_t a_cast, b_cast, c_cast;
a_cast.u64 = a;
b_cast.u64 = b;
c_cast.dbl = fmod(a_cast.dbl, b_cast.dbl);
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e %% %e -> %e\n", a, b, c);
#endif
return c_cast.u64;
}
double fpu_fchs(double a)
{
double b;
b = -a;
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf(" - %e -> %e\n", a, b);
#endif
return b;
}
double fpu_fyl2x(double a, double b)
{
double c;
c = b * (log(a) / log(2));
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("%e * log(%e) -> %e\n", b, a, c);
#endif
return c;
}
double fpu_fpatan(double a, double b)
{
double c;
c = atan2(b, a);
#ifdef DEBUG_MIASM_DOUBLE
dump_float();
printf("arctan(%e / %e) -> %e\n", b, a, c);
#endif
return c;
}
unsigned int fpu_fcom_c0(double a, double b)
{
if (isnan(a) || isnan(b))
return 1;
if (a>=b)
return 0;
return 1;
}
unsigned int fpu_fcom_c1(double a, double b)
{
//XXX
return 0;
}
unsigned int fpu_fcom_c2(double a, double b)
{
if (isnan(a) || isnan(b))
return 1;
return 0;
}
unsigned int fpu_fcom_c3(double a, double b)
{
if (isnan(a) || isnan(b))
return 1;
if (a==b)
return 1;
return 0;
}
uint64_t sint_to_fp_64(int64_t a)
{
double_uint64_t a_cast;
a_cast.dbl = (double) a;
return a_cast.u64;
}
uint32_t sint_to_fp_32(int32_t a)
{
float_uint32_t a_cast;
a_cast.flt = (float) a;
return a_cast.u32;
}
int32_t fp32_to_sint32(uint32_t a)
{
// Enforce nearbyint (IEEE-754 behavior)
float rounded;
float_uint32_t a_cast;
a_cast.u32 = a;
rounded = nearbyintf(a_cast.flt);
return (int32_t) rounded;
}
int64_t fp64_to_sint64(uint64_t a)
{
// Enforce nearbyint (IEEE-754 behavior)
double rounded;
double_uint64_t a_cast;
a_cast.u64 = a;
rounded = nearbyint(a_cast.dbl);
return (int64_t) rounded;
}
int32_t fp64_to_sint32(uint64_t a)
{
// Enforce nearbyint (IEEE-754 behavior)
double rounded;
double_uint64_t a_cast;
a_cast.u64 = a;
rounded = nearbyint(a_cast.dbl);
return (int32_t) rounded;
}
uint32_t fp64_to_fp32(uint64_t a)
{
float_uint32_t a_cast32;
double_uint64_t a_cast64;
a_cast64.u64 = a;
a_cast32.flt = (float)a_cast64.dbl;
return a_cast32.u32;
}
uint64_t fp32_to_fp64(uint32_t a)
{
float_uint32_t a_cast32;
double_uint64_t a_cast64;
a_cast32.u32 = a;
a_cast64.dbl = (double)a_cast32.flt;
return a_cast64.u64;
}
uint32_t fpround_towardszero_fp32(uint32_t a)
{
float_uint32_t a_cast;
a_cast.u32 = a;
a_cast.flt = truncf(a_cast.flt);
return a_cast.u32;
}
uint64_t fpround_towardszero_fp64(uint64_t a)
{
double_uint64_t a_cast;
a_cast.u64 = a;
a_cast.dbl = trunc(a_cast.dbl);
return a_cast.u64;
}
UDIV(8)
UDIV(16)
UDIV(32)
UDIV(64)
UMOD(8)
UMOD(16)
UMOD(32)
UMOD(64)
SDIV(8)
SDIV(16)
SDIV(32)
SDIV(64)
SMOD(8)
SMOD(16)
SMOD(32)
SMOD(64)