#include #include "types.h" #include "debug.h" #include "alloc.h" #include "global.h" #include "lset.h" #include "aux.h" #include "../../../h/em_spec.h" #include "../../../h/em_mnem.h" struct class { byte src_class; byte res_class; }; typedef struct class *class_p; #define NOCLASS 0 #define CLASS1 1 #define CLASS2 2 #define CLASS3 3 #define CLASS4 4 #define CLASS5 5 #define CLASS6 6 #define CLASS7 7 #define CLASS8 8 #define CLASS9 9 #define CLASS10 10 #define CLASS11 11 #include "classdefs.h" /* The file classdefs.h contains the table classtab. It is * generated automatically from the file classdefs.src. */ STATIC bool classes(instr,src_out,res_out) int instr; int *src_out, *res_out; { /* Determine the classes of the given instruction */ class_p c; if (instr < sp_fmnem || instr > sp_lmnem) return FALSE; c = &classtab[instr]; if (c->src_class == NOCLASS) return FALSE; *src_out = c->src_class; *res_out = c->res_class; return TRUE; } STATIC bool uses_arg(class) int class; { /* See if a member of the given class uses * an argument. */ switch(class) { case CLASS1: case CLASS2: case CLASS3: case CLASS4: case CLASS11: return TRUE; default: return FALSE; } /* NOTREACHED */ } STATIC bool uses_2args(class) int class; { /* See if a member of the given class uses * 2 arguments. */ return class == CLASS10; } STATIC bool parse_locs(l,c1_out,c2_out) line_p l; offset *c1_out, *c2_out; { if (INSTR(l) == op_loc && INSTR(PREV(l)) == op_loc) { *c1_out = off_set(l); *c2_out = off_set(PREV(l)); return TRUE; } return FALSE; } STATIC bool check_args(l,src_class,res_class,arg1_out,arg2_out) line_p l; int src_class,res_class; offset *arg1_out, *arg2_out; { /* Several EM instructions have an argument * giving the size of the operand(s) of * the instruction. E.g. a 'adi 4' is a 4-byte * addition. The size may also be put on the * stack. In this case we give up our * efforts to recognize the parameter expression. * Some instructions (e.g. CIU) use 2 arguments * that are both on the stack. In this case we * check if both arguments are LOCs (the usual case), * else we give up. */ if (uses_2args(src_class) || uses_2args(res_class)) { return parse_locs(PREV(l),arg1_out,arg2_out); } if (uses_arg(src_class) || uses_arg(res_class)) { if (TYPE(l) == OPSHORT) { *arg1_out = (offset) SHORT(l); return TRUE; } else { if (TYPE(l) == OPOFFSET) { *arg1_out = OFFSET(l); } else { return FALSE; } } } return TRUE; /* no argument needed */ } STATIC offset nrbytes(class,arg1,arg2) int class; offset arg1,arg2; { /* Determine the number of bytes of the given * arguments and class. */ offset n; switch(class) { case CLASS1: n = arg1; break; case CLASS2: n = 2 * arg1; break; case CLASS3: n = arg1 + ws; break; case CLASS4: n = arg1 + ps; break; case CLASS5: n = ws; break; case CLASS6: n = 2 * ws; break; case CLASS7: n = ps; break; case CLASS8: n = 2 * ps; break; case CLASS9: n = 0; break; case CLASS10: n = arg2 + 2*ws; break; case CLASS11: n = arg1 + 2*ps; break; default: assert(FALSE); } return n; } STATIC attrib(l,expect_out,srcb_out,resb_out) line_p l; offset *expect_out, *srcb_out, *resb_out; { /* Determine a number of attributes of an EM * instruction appearing in an expression. * If it is something we don't * expect in such expression (e.g. a store) * expect_out is set to FALSE. Else we * determine the number of bytes popped from * the stack by the instruction and the * number of bytes pushed on the stack as * result. */ int src_class,res_class; offset arg1, arg2; if (l == (line_p) 0 || !classes(INSTR(l),&src_class,&res_class) || !check_args(l,src_class,res_class,&arg1,&arg2)) { *expect_out = FALSE; } else { *expect_out = TRUE; *srcb_out = nrbytes(src_class,arg1,arg2); *resb_out = nrbytes(res_class,arg1,arg2); } } bool parse(l,nbytes,l_out,level,action0) line_p l, *l_out; offset nbytes; int level; int (*action0) (); { /* This is a recursive descent parser for * EM expressions. * It tries to recognize EM code that loads exactly * 'nbytes' bytes on the stack. * 'l' is the last instruction of this code. * As EM is essentially postfix, this instruction * can be regarded as the root node of an expression * tree. The EM code is traversed from right to left, * i.e. top down. On success, TRUE is returned and * 'l_out' will point to the first instruction * of the recognized code. On toplevel, when an * expression has been recognized, the procedure-parameter * 'action0' is called, with parameters: the first and * last instruction of the expression and the number of * bytes recognized. */ offset more, expected, sourcebytes,resultbytes; line_p lnp; more = nbytes; /* #bytes to be recognized */ while (more > 0) { attrib(l,&expected,&sourcebytes,&resultbytes); /* Get the attributes of EM instruction 'l'. * 'expected' denotes if it is something we can use; * 'sourcebytes' and 'resultbytes' are the number of * bytes popped resp. pushed by the instruction * (e.g. 'adi 2' pops 4 bytes and pushes 2 bytes). */ if (!expected || (more -= resultbytes) < 0) return FALSE; if (sourcebytes == 0) { /* a leaf of the expression tree */ lnp = l; } else { if (!parse(PREV(l),sourcebytes,&lnp,level+1,action0)) { return FALSE; } } if (level == 0) { /* at toplevel */ (*action0) (lnp,l,resultbytes); } l = PREV(lnp); } /* Now we've recognized a number of expressions that * together push nbytes on the stack. */ *l_out = lnp; return TRUE; }