mirror of https://github.com/yasm/yasm.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
417 lines
9.7 KiB
417 lines
9.7 KiB
/* $IdPath$ |
|
* |
|
* Copyright (C) 2001 Peter Johnson |
|
* |
|
* This file is part of YASM. |
|
* |
|
* YASM is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation; either version 2 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* YASM is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program; if not, write to the Free Software |
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
*/ |
|
#include "util.h" |
|
|
|
#include "check.h" |
|
|
|
#include "bitvect.h" |
|
|
|
#include "errwarn.h" |
|
|
|
#include "expr.h" |
|
#include "intnum.h" |
|
#include "floatnum.h" |
|
|
|
#include "bytecode.h" |
|
#include "arch.h" |
|
#include "x86-int.h" |
|
|
|
typedef enum { |
|
REG_AX = 0, |
|
REG_CX = 1, |
|
REG_DX = 2, |
|
REG_BX = 3, |
|
REG_SP = 4, |
|
REG_BP = 5, |
|
REG_SI = 6, |
|
REG_DI = 7 |
|
} reg16type; |
|
|
|
/* Memory expression building functions. |
|
* These try to exactly match how a parser will build up the expr for _in, |
|
* and exactly what the output expr should be for _out. |
|
*/ |
|
|
|
/* [5] */ |
|
static expr * |
|
gen_5_in(void) |
|
{ |
|
return expr_new_ident(ExprInt(intnum_new_int(5))); |
|
} |
|
#define gen_5_out gen_5_in |
|
/* [1.2] */ |
|
static expr * |
|
gen_1pt2_in(void) |
|
{ |
|
return expr_new_ident(ExprFloat(floatnum_new("1.2"))); |
|
} |
|
/* No _out, it's invalid */ |
|
/* [ecx] */ |
|
static expr * |
|
gen_ecx_in(void) |
|
{ |
|
return expr_new_ident(ExprReg(REG_CX, 32)); |
|
} |
|
#define gen_ecx_out NULL |
|
/* [di] */ |
|
static expr * |
|
gen_di_in(void) |
|
{ |
|
return expr_new_ident(ExprReg(REG_DI, 16)); |
|
} |
|
#define gen_di_out NULL |
|
/* [di-si+si+126] */ |
|
static expr * |
|
gen_dimsipsip126_in(void) |
|
{ |
|
return expr_new_tree( |
|
expr_new_tree( |
|
expr_new_tree( |
|
expr_new_ident(ExprReg(REG_DI, 16)), |
|
EXPR_SUB, |
|
expr_new_ident(ExprReg(REG_SI, 16))), |
|
EXPR_ADD, |
|
expr_new_ident(ExprReg(REG_SI, 16))), |
|
EXPR_ADD, |
|
expr_new_ident(ExprInt(intnum_new_int(126)))); |
|
} |
|
#define gen_dimsipsip126_out NULL |
|
/* [bx-(bx-di)+bx-2] */ |
|
static expr * |
|
gen_bxmqbxmdiqpbxm2_in(void) |
|
{ |
|
return expr_new_tree( |
|
expr_new_tree( |
|
expr_new_tree( |
|
expr_new_ident(ExprReg(REG_BX, 16)), |
|
EXPR_SUB, |
|
expr_new_tree( |
|
expr_new_ident(ExprReg(REG_BX, 16)), |
|
EXPR_SUB, |
|
expr_new_ident(ExprReg(REG_DI, 16)))), |
|
EXPR_ADD, |
|
expr_new_ident(ExprReg(REG_BX, 16))), |
|
EXPR_SUB, |
|
expr_new_ident(ExprInt(intnum_new_int(2)))); |
|
} |
|
static expr * |
|
gen_bxmqbxmdiqpbxm2_out(void) |
|
{ |
|
return expr_new_ident(ExprInt(intnum_new_int(-2))); |
|
} |
|
/* [bp] */ |
|
static expr * |
|
gen_bp_in(void) |
|
{ |
|
return expr_new_ident(ExprReg(REG_BP, 16)); |
|
} |
|
#define gen_bp_out NULL |
|
/* [bp*1+500] */ |
|
static expr * |
|
gen_bpx1p500_in(void) |
|
{ |
|
return expr_new_tree( |
|
expr_new_tree( |
|
expr_new_ident(ExprReg(REG_BP, 16)), |
|
EXPR_MUL, |
|
expr_new_ident(ExprInt(intnum_new_int(1)))), |
|
EXPR_ADD, |
|
expr_new_ident(ExprInt(intnum_new_int(500)))); |
|
} |
|
static expr * |
|
gen_bpx1p500_out(void) |
|
{ |
|
return expr_new_ident(ExprInt(intnum_new_int(500))); |
|
} |
|
|
|
typedef struct CheckEA_InOut { |
|
/* Function to generate input/output expr. */ |
|
expr *(*expr_gen)(void); |
|
unsigned char addrsize; |
|
unsigned char bits; |
|
unsigned char nosplit; |
|
unsigned char displen; |
|
unsigned char modrm; |
|
unsigned char v_modrm; |
|
unsigned char n_modrm; |
|
unsigned char sib; |
|
unsigned char v_sib; |
|
unsigned char n_sib; |
|
} CheckEA_InOut; |
|
|
|
typedef struct CheckEA_Entry { |
|
const char *ascii; /* Text description of input */ |
|
CheckEA_InOut in; /* Input Parameter Values */ |
|
int retval; /* Return value */ |
|
CheckEA_InOut out; /* Correct output Parameter Values |
|
(N/A if retval=0) */ |
|
} CheckEA_Entry; |
|
|
|
/* Values used for tests */ |
|
static CheckEA_Entry bits16_vals[] = { |
|
{ |
|
"[5]", |
|
{gen_5_in , 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff}, |
|
1, |
|
{gen_5_out, 16, 16, 0, 2, 0x06, 1, 1, 0, 0, 0} |
|
}, |
|
{ |
|
"a16 [5]", |
|
{gen_5_in , 16, 16, 0, 0, 0, 0, 1, 0, 0, 0xff}, |
|
1, |
|
{gen_5_out, 16, 16, 0, 2, 0x06, 1, 1, 0, 0, 0} |
|
}, |
|
{ |
|
"a32 [5]", |
|
{gen_5_in , 32, 16, 0, 0, 0, 0, 1, 0, 0, 0xff}, |
|
1, |
|
{gen_5_out, 32, 16, 0, 4, 0x05, 1, 1, 0x25, 1, 1} |
|
}, |
|
{ |
|
"[word 5]", |
|
{gen_5_in , 0, 16, 0, 2, 0, 0, 1, 0, 0, 0xff}, |
|
1, |
|
{gen_5_out, 16, 16, 0, 2, 0x06, 1, 1, 0, 0, 0} |
|
}, |
|
{ |
|
"[dword 5]", |
|
{gen_5_in , 0, 16, 0, 4, 0, 0, 1, 0, 0, 0xff}, |
|
1, |
|
{gen_5_out, 32, 16, 0, 4, 0x05, 1, 1, 0x25, 1, 1} |
|
}, |
|
{ |
|
"a16 [dword 5]", |
|
{gen_5_in, 16, 16, 0, 4, 0, 0, 1, 0, 0, 0xff}, |
|
0, |
|
{NULL , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} |
|
}, |
|
/* should error */ |
|
{ |
|
"[di+1.2]", |
|
{gen_1pt2_in, 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff}, |
|
0, |
|
{NULL , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} |
|
}, |
|
{ |
|
"[ecx]", |
|
{gen_ecx_in , 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff}, |
|
1, |
|
{gen_ecx_out, 32, 16, 0, 0, 0x01, 1, 1, 0, 0, 0} |
|
}, |
|
/* should error */ |
|
{ |
|
"a16 [ecx]", |
|
{gen_ecx_in, 16, 16, 0, 0, 0, 0, 1, 0, 0, 0xff}, |
|
0, |
|
{NULL , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} |
|
}, |
|
{ |
|
"[di]", |
|
{gen_di_in , 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff}, |
|
1, |
|
{gen_di_out, 16, 16, 0, 0, 0x05, 1, 1, 0, 0, 0} |
|
}, |
|
{ |
|
"[di-si+si+126]", |
|
{gen_dimsipsip126_in , 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff}, |
|
1, |
|
{gen_dimsipsip126_out, 16, 16, 0, 1, 0x45, 1, 1, 0, 0, 0} |
|
}, |
|
{ |
|
"[bx-(bx-di)+bx-2]", |
|
{gen_bxmqbxmdiqpbxm2_in , 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff}, |
|
1, |
|
{gen_bxmqbxmdiqpbxm2_out, 16, 16, 0, 1, 0x41, 1, 1, 0, 0, 0} |
|
}, |
|
{ |
|
"[bp]", |
|
{gen_bp_in , 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff}, |
|
1, |
|
{gen_bp_out, 16, 16, 0, 1, 0x46, 1, 1, 0, 0, 0} |
|
}, |
|
{ |
|
"[bp*1+500]", |
|
{gen_bpx1p500_in , 0, 16, 0, 0, 0, 0, 1, 0, 0, 0xff}, |
|
1, |
|
{gen_bpx1p500_out, 16, 16, 0, 2, 0x86, 1, 1, 0, 0, 0} |
|
}, |
|
}; |
|
|
|
/* input expression */ |
|
expr *expn; |
|
|
|
/* failure messages */ |
|
static char result_msg[1024]; |
|
|
|
int error_triggered; |
|
|
|
/* Replace errwarn functions */ |
|
void InternalError_(const char *file, unsigned int line, const char *msg) |
|
{ |
|
exit(EXIT_FAILURE); |
|
} |
|
|
|
void |
|
Fatal(fatal_num num) |
|
{ |
|
exit(EXIT_FAILURE); |
|
} |
|
|
|
void |
|
Error(const char *msg, ...) |
|
{ |
|
error_triggered = 1; |
|
} |
|
|
|
void |
|
Warning(const char *msg, ...) |
|
{ |
|
} |
|
|
|
void |
|
ErrorAt(const char *filename, unsigned long line, const char *fmt, ...) |
|
{ |
|
error_triggered = 1; |
|
} |
|
|
|
void |
|
WarningAt(const char *filename, unsigned long line, const char *fmt, ...) |
|
{ |
|
} |
|
|
|
static int |
|
x86_checkea_check(CheckEA_Entry *val) |
|
{ |
|
CheckEA_InOut chk = val->in; /* local structure copy of inputs */ |
|
int retval; |
|
|
|
error_triggered = 0; |
|
|
|
/* execute function and check return value */ |
|
retval = x86_expr_checkea(&expn, &chk.addrsize, chk.bits, chk.nosplit, |
|
&chk.displen, &chk.modrm, &chk.v_modrm, |
|
&chk.n_modrm, &chk.sib, &chk.v_sib, &chk.n_sib); |
|
if (retval != val->retval) { |
|
sprintf(result_msg, "%s: incorrect %s (expected %d, got %d)", |
|
val->ascii, "return value", val->retval, retval); |
|
return 1; |
|
} |
|
|
|
/* If returned 0 (failure), check to see if ErrorAt() was called */ |
|
if (retval == 0) { |
|
if (error_triggered == 0) { |
|
sprintf(result_msg, "%s: didn't call ErrorAt() and returned 0", |
|
val->ascii); |
|
return 1; |
|
} |
|
|
|
return 0; /* don't check other return values */ |
|
} |
|
|
|
/* check expr result */ |
|
/* TODO */ |
|
|
|
/* Check other outputs */ |
|
if (chk.addrsize != val->out.addrsize) { |
|
sprintf(result_msg, "%s: incorrect %s (expected %d, got %d)", |
|
val->ascii, "addrsize", (int)val->out.addrsize, |
|
(int)chk.addrsize); |
|
return 1; |
|
} |
|
if (chk.displen != val->out.displen) { |
|
sprintf(result_msg, "%s: incorrect %s (expected %d, got %d)", |
|
val->ascii, "displen", (int)val->out.displen, |
|
(int)chk.displen); |
|
return 1; |
|
} |
|
if (chk.modrm != val->out.modrm) { |
|
sprintf(result_msg, "%s: incorrect %s (expected %03o, got %03o)", |
|
val->ascii, "modrm", (int)val->out.modrm, (int)chk.modrm); |
|
return 1; |
|
} |
|
if (chk.v_modrm != val->out.v_modrm) { |
|
sprintf(result_msg, "%s: incorrect %s (expected %d, got %d)", |
|
val->ascii, "v_modrm", (int)val->out.v_modrm, |
|
(int)chk.v_modrm); |
|
return 1; |
|
} |
|
if (chk.n_modrm != val->out.n_modrm) { |
|
sprintf(result_msg, "%s: incorrect %s (expected %d, got %d)", |
|
val->ascii, "n_modrm", (int)val->out.n_modrm, |
|
(int)chk.n_modrm); |
|
return 1; |
|
} |
|
if (chk.sib != val->out.sib) { |
|
sprintf(result_msg, "%s: incorrect %s (expected %03o, got %03o)", |
|
val->ascii, "sib", (int)val->out.sib, (int)chk.sib); |
|
return 1; |
|
} |
|
if (chk.v_sib != val->out.v_sib) { |
|
sprintf(result_msg, "%s: incorrect %s (expected %d, got %d)", |
|
val->ascii, "v_sib", (int)val->out.v_sib, (int)chk.v_sib); |
|
return 1; |
|
} |
|
if (chk.n_sib != val->out.n_sib) { |
|
sprintf(result_msg, "%s: incorrect %s (expected %x, got %x)", |
|
val->ascii, "n_sib", (int)val->out.n_sib, (int)chk.n_sib); |
|
return 1; |
|
} |
|
return 0; |
|
} |
|
|
|
START_TEST(test_x86_checkea_bits16) |
|
{ |
|
CheckEA_Entry *vals = bits16_vals; |
|
int i, num = sizeof(bits16_vals)/sizeof(CheckEA_Entry); |
|
|
|
for (i=0; i<num; i++) { |
|
expn = vals[i].in.expr_gen(); |
|
fail_unless(x86_checkea_check(&vals[i]) == 0, result_msg); |
|
expr_delete(expn); |
|
} |
|
} |
|
END_TEST |
|
|
|
static Suite * |
|
memexpr_suite(void) |
|
{ |
|
Suite *s = suite_create("memexpr"); |
|
TCase *tc_x86_checkea = tcase_create("x86_checkea"); |
|
|
|
suite_add_tcase(s, tc_x86_checkea); |
|
tcase_add_test(tc_x86_checkea, test_x86_checkea_bits16); |
|
|
|
return s; |
|
} |
|
|
|
int |
|
main(void) |
|
{ |
|
int nf; |
|
Suite *s = memexpr_suite(); |
|
SRunner *sr = srunner_create(s); |
|
BitVector_Boot(); |
|
srunner_run_all(sr, CRNORMAL); |
|
nf = srunner_ntests_failed(sr); |
|
srunner_free(sr); |
|
suite_free(s); |
|
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; |
|
}
|
|
|