Add signed versions of .cmp/gt/ge
This commit is contained in:
parent
58f2fe73c8
commit
2c1e36ae9a
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2001 Stephen Williams (steve@icarus.com)
|
* Copyright (c) 2001 Stephen Williams (steve@icarus.com)
|
||||||
*
|
*
|
||||||
* $Id: README.txt,v 1.43 2003/03/10 23:37:07 steve Exp $
|
* $Id: README.txt,v 1.44 2003/04/11 05:15:38 steve Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
VVP SIMULATION ENGINE
|
VVP SIMULATION ENGINE
|
||||||
|
|
@ -502,11 +502,14 @@ similar:
|
||||||
|
|
||||||
<label> .cmp/ge <wid>, <symbols_list>;
|
<label> .cmp/ge <wid>, <symbols_list>;
|
||||||
<label> .cmp/gt <wid>, <symbols_list>;
|
<label> .cmp/gt <wid>, <symbols_list>;
|
||||||
|
<label> .cmp/ge.s <wid>, <symbols_list>;
|
||||||
|
<label> .cmp/gt.s <wid>, <symbols_list>;
|
||||||
|
|
||||||
Whereas the arithmetic statements create an array of functor outputs,
|
Whereas the arithmetic statements create an array of functor outputs,
|
||||||
there is only one useful functor output for the comparators. That
|
there is only one useful functor output for the comparators. That
|
||||||
functor output is 1 of the comparison is true, 0 if false, and x
|
functor output is 1 of the comparison is true, 0 if false, and x
|
||||||
otherwise.
|
otherwise. The plain versions do unsigned comparison, but the ".s"
|
||||||
|
versions to signed comparisons.
|
||||||
|
|
||||||
|
|
||||||
STRUCTURAL SHIFTER STATEMENTS:
|
STRUCTURAL SHIFTER STATEMENTS:
|
||||||
|
|
|
||||||
65
vvp/arith.cc
65
vvp/arith.cc
|
|
@ -17,7 +17,7 @@
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||||
*/
|
*/
|
||||||
#ifdef HAVE_CVS_IDENT
|
#ifdef HAVE_CVS_IDENT
|
||||||
#ident "$Id: arith.cc,v 1.24 2002/08/12 01:35:07 steve Exp $"
|
#ident "$Id: arith.cc,v 1.25 2003/04/11 05:15:38 steve Exp $"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
# include "arith.h"
|
# include "arith.h"
|
||||||
|
|
@ -380,6 +380,11 @@ void vvp_arith_sub::set(vvp_ipoint_t i, bool push, unsigned val, unsigned)
|
||||||
output_val_(base, push);
|
output_val_(base, push);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vvp_cmp_ge::vvp_cmp_ge(unsigned wid, bool flag)
|
||||||
|
: vvp_arith_(wid), signed_flag_(flag)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void vvp_cmp_ge::set(vvp_ipoint_t i, bool push, unsigned val, unsigned)
|
void vvp_cmp_ge::set(vvp_ipoint_t i, bool push, unsigned val, unsigned)
|
||||||
{
|
{
|
||||||
|
|
@ -415,6 +420,11 @@ void vvp_cmp_ge::set(vvp_ipoint_t i, bool push, unsigned val, unsigned)
|
||||||
put_oval(out_val, push);
|
put_oval(out_val, push);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vvp_cmp_gt::vvp_cmp_gt(unsigned wid, bool flag)
|
||||||
|
: vvp_arith_(wid), signed_flag_(flag)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void vvp_cmp_gt::set(vvp_ipoint_t i, bool push, unsigned val, unsigned)
|
void vvp_cmp_gt::set(vvp_ipoint_t i, bool push, unsigned val, unsigned)
|
||||||
{
|
{
|
||||||
put(i, val);
|
put(i, val);
|
||||||
|
|
@ -422,14 +432,42 @@ void vvp_cmp_gt::set(vvp_ipoint_t i, bool push, unsigned val, unsigned)
|
||||||
|
|
||||||
unsigned out_val = 0;
|
unsigned out_val = 0;
|
||||||
|
|
||||||
for (unsigned idx = wid_ ; idx > 0 ; idx -= 1) {
|
unsigned idx = wid_;
|
||||||
|
|
||||||
|
/* If this is a signed compare, then check the MSB of the
|
||||||
|
input vectors. If they are different, then the values are
|
||||||
|
on the different sides of zero, and we know the result. */
|
||||||
|
if (signed_flag_) {
|
||||||
|
vvp_ipoint_t ptr = ipoint_index(base, wid_-1);
|
||||||
|
functor_t obj = functor_index(ptr);
|
||||||
|
|
||||||
|
unsigned val = obj->ival;
|
||||||
|
if (val & 0x0a) {
|
||||||
|
out_val = 2;
|
||||||
|
goto check_for_x_complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned a = (val & 0x01)? 1 : 0;
|
||||||
|
unsigned b = (val & 0x04)? 1 : 0;
|
||||||
|
|
||||||
|
/* If a==0 and b==1, then a>=0 and b<0 so return true.
|
||||||
|
If a==1 and b==0, then a<0 and b>=0 so return false.
|
||||||
|
It turns out that out_val=b gets the right result. */
|
||||||
|
if (a ^ b) {
|
||||||
|
out_val = b;
|
||||||
|
idx = wid_-1;
|
||||||
|
goto check_for_x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (idx = wid_ ; idx > 0 ; idx -= 1) {
|
||||||
vvp_ipoint_t ptr = ipoint_index(base, idx-1);
|
vvp_ipoint_t ptr = ipoint_index(base, idx-1);
|
||||||
functor_t obj = functor_index(ptr);
|
functor_t obj = functor_index(ptr);
|
||||||
|
|
||||||
unsigned val = obj->ival;
|
unsigned val = obj->ival;
|
||||||
if (val & 0x0a) {
|
if (val & 0x0a) {
|
||||||
out_val = 2;
|
out_val = 2;
|
||||||
break;
|
goto check_for_x_complete;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned a = (val & 0x01)? 1 : 0;
|
unsigned a = (val & 0x01)? 1 : 0;
|
||||||
|
|
@ -446,6 +484,24 @@ void vvp_cmp_gt::set(vvp_ipoint_t i, bool push, unsigned val, unsigned)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_for_x:
|
||||||
|
/* Continue further checking bits, looking for unknown
|
||||||
|
results. */
|
||||||
|
while ((idx > 0) && (out_val != 2)) {
|
||||||
|
vvp_ipoint_t ptr = ipoint_index(base, idx-1);
|
||||||
|
functor_t obj = functor_index(ptr);
|
||||||
|
|
||||||
|
unsigned val = obj->ival;
|
||||||
|
if (val & 0x0a) {
|
||||||
|
out_val = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
check_for_x_complete:
|
||||||
|
|
||||||
put_oval(out_val, push);
|
put_oval(out_val, push);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -551,6 +607,9 @@ void vvp_shiftr::set(vvp_ipoint_t i, bool push, unsigned val, unsigned)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* $Log: arith.cc,v $
|
* $Log: arith.cc,v $
|
||||||
|
* Revision 1.25 2003/04/11 05:15:38 steve
|
||||||
|
* Add signed versions of .cmp/gt/ge
|
||||||
|
*
|
||||||
* Revision 1.24 2002/08/12 01:35:07 steve
|
* Revision 1.24 2002/08/12 01:35:07 steve
|
||||||
* conditional ident string using autoconfig.
|
* conditional ident string using autoconfig.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
17
vvp/arith.h
17
vvp/arith.h
|
|
@ -19,7 +19,7 @@
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||||
*/
|
*/
|
||||||
#ifdef HAVE_CVS_IDENT
|
#ifdef HAVE_CVS_IDENT
|
||||||
#ident "$Id: arith.h,v 1.14 2002/08/12 01:35:07 steve Exp $"
|
#ident "$Id: arith.h,v 1.15 2003/04/11 05:15:38 steve Exp $"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
# include "functor.h"
|
# include "functor.h"
|
||||||
|
|
@ -104,17 +104,21 @@ class vvp_arith_sub : public vvp_wide_arith_ {
|
||||||
class vvp_cmp_ge : public vvp_arith_ {
|
class vvp_cmp_ge : public vvp_arith_ {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit vvp_cmp_ge(unsigned wid) : vvp_arith_(wid) {}
|
explicit vvp_cmp_ge(unsigned wid, bool signed_flag);
|
||||||
|
|
||||||
void set(vvp_ipoint_t i, bool push, unsigned val, unsigned str);
|
void set(vvp_ipoint_t i, bool push, unsigned val, unsigned str);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool signed_flag_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class vvp_cmp_gt : public vvp_arith_ {
|
class vvp_cmp_gt : public vvp_arith_ {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit vvp_cmp_gt(unsigned wid) : vvp_arith_(wid) {}
|
explicit vvp_cmp_gt(unsigned wid, bool signed_flag);
|
||||||
|
|
||||||
void set(vvp_ipoint_t i, bool push, unsigned val, unsigned str);
|
void set(vvp_ipoint_t i, bool push, unsigned val, unsigned str);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool signed_flag_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class vvp_shiftl : public vvp_arith_ {
|
class vvp_shiftl : public vvp_arith_ {
|
||||||
|
|
@ -135,6 +139,9 @@ class vvp_shiftr : public vvp_arith_ {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* $Log: arith.h,v $
|
* $Log: arith.h,v $
|
||||||
|
* Revision 1.15 2003/04/11 05:15:38 steve
|
||||||
|
* Add signed versions of .cmp/gt/ge
|
||||||
|
*
|
||||||
* Revision 1.14 2002/08/12 01:35:07 steve
|
* Revision 1.14 2002/08/12 01:35:07 steve
|
||||||
* conditional ident string using autoconfig.
|
* conditional ident string using autoconfig.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||||
*/
|
*/
|
||||||
#ifdef HAVE_CVS_IDENT
|
#ifdef HAVE_CVS_IDENT
|
||||||
#ident "$Id: compile.cc,v 1.158 2003/03/28 02:33:56 steve Exp $"
|
#ident "$Id: compile.cc,v 1.159 2003/04/11 05:15:38 steve Exp $"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
# include "arith.h"
|
# include "arith.h"
|
||||||
|
|
@ -906,7 +906,8 @@ void compile_arith_sum(char*label, long wid, unsigned argc, struct symb_s*argv)
|
||||||
make_arith(arith, label, wid, argc, argv);
|
make_arith(arith, label, wid, argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void compile_cmp_ge(char*label, long wid, unsigned argc, struct symb_s*argv)
|
void compile_cmp_ge(char*label, long wid, bool signed_flag,
|
||||||
|
unsigned argc, struct symb_s*argv)
|
||||||
{
|
{
|
||||||
assert( wid > 0 );
|
assert( wid > 0 );
|
||||||
|
|
||||||
|
|
@ -916,12 +917,13 @@ void compile_cmp_ge(char*label, long wid, unsigned argc, struct symb_s*argv)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vvp_arith_ *arith = new vvp_cmp_ge(wid);
|
vvp_arith_ *arith = new vvp_cmp_ge(wid, signed_flag);
|
||||||
|
|
||||||
make_arith(arith, label, wid, argc, argv);
|
make_arith(arith, label, wid, argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void compile_cmp_gt(char*label, long wid, unsigned argc, struct symb_s*argv)
|
void compile_cmp_gt(char*label, long wid, bool signed_flag,
|
||||||
|
unsigned argc, struct symb_s*argv)
|
||||||
{
|
{
|
||||||
assert( wid > 0 );
|
assert( wid > 0 );
|
||||||
|
|
||||||
|
|
@ -931,7 +933,7 @@ void compile_cmp_gt(char*label, long wid, unsigned argc, struct symb_s*argv)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vvp_arith_ *arith = new vvp_cmp_gt(wid);
|
vvp_arith_ *arith = new vvp_cmp_gt(wid, signed_flag);
|
||||||
|
|
||||||
make_arith(arith, label, wid, argc, argv);
|
make_arith(arith, label, wid, argc, argv);
|
||||||
}
|
}
|
||||||
|
|
@ -1528,6 +1530,9 @@ void compile_param_string(char*label, char*name, char*str, char*value)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* $Log: compile.cc,v $
|
* $Log: compile.cc,v $
|
||||||
|
* Revision 1.159 2003/04/11 05:15:38 steve
|
||||||
|
* Add signed versions of .cmp/gt/ge
|
||||||
|
*
|
||||||
* Revision 1.158 2003/03/28 02:33:56 steve
|
* Revision 1.158 2003/03/28 02:33:56 steve
|
||||||
* Add support for division of real operands.
|
* Add support for division of real operands.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||||
*/
|
*/
|
||||||
#ifdef HAVE_CVS_IDENT
|
#ifdef HAVE_CVS_IDENT
|
||||||
#ident "$Id: compile.h,v 1.50 2003/03/10 23:37:07 steve Exp $"
|
#ident "$Id: compile.h,v 1.51 2003/04/11 05:15:39 steve Exp $"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
# include <stdio.h>
|
# include <stdio.h>
|
||||||
|
|
@ -107,9 +107,9 @@ extern void compile_arith_sum(char*label, long width,
|
||||||
unsigned argc, struct symb_s*argv);
|
unsigned argc, struct symb_s*argv);
|
||||||
extern void compile_arith_sub(char*label, long width,
|
extern void compile_arith_sub(char*label, long width,
|
||||||
unsigned argc, struct symb_s*argv);
|
unsigned argc, struct symb_s*argv);
|
||||||
extern void compile_cmp_ge(char*label, long width,
|
extern void compile_cmp_ge(char*label, long width, bool signed_flag,
|
||||||
unsigned argc, struct symb_s*argv);
|
unsigned argc, struct symb_s*argv);
|
||||||
extern void compile_cmp_gt(char*label, long width,
|
extern void compile_cmp_gt(char*label, long width, bool signed_flag,
|
||||||
unsigned argc, struct symb_s*argv);
|
unsigned argc, struct symb_s*argv);
|
||||||
extern void compile_shiftl(char*label, long width,
|
extern void compile_shiftl(char*label, long width,
|
||||||
unsigned argc, struct symb_s*argv);
|
unsigned argc, struct symb_s*argv);
|
||||||
|
|
@ -264,6 +264,9 @@ extern void compile_net(char*label, char*name,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* $Log: compile.h,v $
|
* $Log: compile.h,v $
|
||||||
|
* Revision 1.51 2003/04/11 05:15:39 steve
|
||||||
|
* Add signed versions of .cmp/gt/ge
|
||||||
|
*
|
||||||
* Revision 1.50 2003/03/10 23:37:07 steve
|
* Revision 1.50 2003/03/10 23:37:07 steve
|
||||||
* Direct support for string parameters.
|
* Direct support for string parameters.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||||
*/
|
*/
|
||||||
#if !defined(WINNT)
|
#if !defined(WINNT)
|
||||||
#ident "$Id: lexor.lex,v 1.39 2003/03/10 23:37:07 steve Exp $"
|
#ident "$Id: lexor.lex,v 1.40 2003/04/11 05:15:39 steve Exp $"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
# include "parse_misc.h"
|
# include "parse_misc.h"
|
||||||
|
|
@ -89,7 +89,9 @@
|
||||||
".arith/sub" { return K_ARITH_SUB; }
|
".arith/sub" { return K_ARITH_SUB; }
|
||||||
".arith/sum" { return K_ARITH_SUM; }
|
".arith/sum" { return K_ARITH_SUM; }
|
||||||
".cmp/ge" { return K_CMP_GE; }
|
".cmp/ge" { return K_CMP_GE; }
|
||||||
|
".cmp/ge.s" { return K_CMP_GE_S; }
|
||||||
".cmp/gt" { return K_CMP_GT; }
|
".cmp/gt" { return K_CMP_GT; }
|
||||||
|
".cmp/gt.s" { return K_CMP_GT_S; }
|
||||||
".event" { return K_EVENT; }
|
".event" { return K_EVENT; }
|
||||||
".event/or" { return K_EVENT_OR; }
|
".event/or" { return K_EVENT_OR; }
|
||||||
".functor" { return K_FUNCTOR; }
|
".functor" { return K_FUNCTOR; }
|
||||||
|
|
@ -177,6 +179,9 @@ int yywrap()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* $Log: lexor.lex,v $
|
* $Log: lexor.lex,v $
|
||||||
|
* Revision 1.40 2003/04/11 05:15:39 steve
|
||||||
|
* Add signed versions of .cmp/gt/ge
|
||||||
|
*
|
||||||
* Revision 1.39 2003/03/10 23:37:07 steve
|
* Revision 1.39 2003/03/10 23:37:07 steve
|
||||||
* Direct support for string parameters.
|
* Direct support for string parameters.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
20
vvp/parse.y
20
vvp/parse.y
|
|
@ -19,7 +19,7 @@
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||||
*/
|
*/
|
||||||
#if !defined(WINNT)
|
#if !defined(WINNT)
|
||||||
#ident "$Id: parse.y,v 1.52 2003/03/10 23:37:07 steve Exp $"
|
#ident "$Id: parse.y,v 1.53 2003/04/11 05:15:39 steve Exp $"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
# include "parse_misc.h"
|
# include "parse_misc.h"
|
||||||
|
|
@ -58,7 +58,7 @@ extern FILE*yyin;
|
||||||
|
|
||||||
|
|
||||||
%token K_ARITH_DIV K_ARITH_MOD K_ARITH_MULT K_ARITH_SUB K_ARITH_SUM
|
%token K_ARITH_DIV K_ARITH_MOD K_ARITH_MULT K_ARITH_SUB K_ARITH_SUM
|
||||||
%token K_CMP_GE K_CMP_GT
|
%token K_CMP_GE K_CMP_GE_S K_CMP_GT K_CMP_GT_S
|
||||||
%token K_EVENT K_EVENT_OR K_FUNCTOR K_NET K_NET_S K_PARAM
|
%token K_EVENT K_EVENT_OR K_FUNCTOR K_NET K_NET_S K_PARAM
|
||||||
%token K_RESOLV K_SCOPE K_SHIFTL K_SHIFTR K_THREAD K_TIMESCALE K_UFUNC
|
%token K_RESOLV K_SCOPE K_SHIFTL K_SHIFTR K_THREAD K_TIMESCALE K_UFUNC
|
||||||
%token K_UDP K_UDP_C K_UDP_S
|
%token K_UDP K_UDP_C K_UDP_S
|
||||||
|
|
@ -217,12 +217,21 @@ statement
|
||||||
|
|
||||||
| T_LABEL K_CMP_GE T_NUMBER ',' symbols ';'
|
| T_LABEL K_CMP_GE T_NUMBER ',' symbols ';'
|
||||||
{ struct symbv_s obj = $5;
|
{ struct symbv_s obj = $5;
|
||||||
compile_cmp_ge($1, $3, obj.cnt, obj.vect);
|
compile_cmp_ge($1, $3, false, obj.cnt, obj.vect);
|
||||||
|
}
|
||||||
|
|
||||||
|
| T_LABEL K_CMP_GE_S T_NUMBER ',' symbols ';'
|
||||||
|
{ struct symbv_s obj = $5;
|
||||||
|
compile_cmp_ge($1, $3, true, obj.cnt, obj.vect);
|
||||||
}
|
}
|
||||||
|
|
||||||
| T_LABEL K_CMP_GT T_NUMBER ',' symbols ';'
|
| T_LABEL K_CMP_GT T_NUMBER ',' symbols ';'
|
||||||
{ struct symbv_s obj = $5;
|
{ struct symbv_s obj = $5;
|
||||||
compile_cmp_gt($1, $3, obj.cnt, obj.vect);
|
compile_cmp_gt($1, $3, false, obj.cnt, obj.vect);
|
||||||
|
}
|
||||||
|
| T_LABEL K_CMP_GT_S T_NUMBER ',' symbols ';'
|
||||||
|
{ struct symbv_s obj = $5;
|
||||||
|
compile_cmp_gt($1, $3, true, obj.cnt, obj.vect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -575,6 +584,9 @@ int compile_design(const char*path)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* $Log: parse.y,v $
|
* $Log: parse.y,v $
|
||||||
|
* Revision 1.53 2003/04/11 05:15:39 steve
|
||||||
|
* Add signed versions of .cmp/gt/ge
|
||||||
|
*
|
||||||
* Revision 1.52 2003/03/10 23:37:07 steve
|
* Revision 1.52 2003/03/10 23:37:07 steve
|
||||||
* Direct support for string parameters.
|
* Direct support for string parameters.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue