/* ....[@@@..[@@@..............[@.................. MUD++ is a written from ....[@..[@..[@..[@..[@..[@@@@@....[@......[@.... scratch multi-user swords and ....[@..[@..[@..[@..[@..[@..[@..[@@@@@..[@@@@@.. sorcery game written in C++. ....[@......[@..[@..[@..[@..[@....[@......[@.... This server is an ongoing ....[@......[@..[@@@@@..[@@@@@.................. development project. All ................................................ contributions are welcome. ....Copyright(C).1995.Melvin.Smith.............. Enjoy. ------------------------------------------------------------------------------ Melvin Smith (aka Fusion) msmith@hom.net MUD++ development mailing list mudpp@van.ml.org ------------------------------------------------------------------------------ asmparser.cc */ #include "config.h" #include "string.h" #include "io.h" #include "vmopcodes.h" #include "asmobjfile.h" #include "asmparser.h" #include "erratum.h" struct _parser_table AsmParser::parser_table[] = { {"aadd", VMOPC_AADD , &AsmParser::paramless, NULL }, {"abort", VMOPC_ABORT, &AsmParser::paramless, NULL }, {"add", VMOPC_ADD , &AsmParser::paramless, NULL }, {"alen", VMOPC_ALEN , &AsmParser::paramless, NULL }, {"apop", VMOPC_APOP , &AsmParser::paramless, NULL }, {"apush", VMOPC_APUSH , &AsmParser::paramless, NULL }, {"call", VMOPC_CALL, &AsmParser::read_unknown_call, NULL }, {"cast2f", VMOPC_CAST2F, &AsmParser::paramless, NULL }, {"cast2i", VMOPC_CAST2I, &AsmParser::paramless, NULL }, {"checkcast", VMOPC_CHECKCAST, &AsmParser::paramless, NULL}, {"clone", VMOPC_CLONE , &AsmParser::paramless, NULL }, {"dec", VMOPC_DEC , &AsmParser::paramless, NULL }, {"decl", VMOPC_DECL , &AsmParser::read_local_var, NULL }, {"div", VMOPC_DIV , &AsmParser::paramless, NULL }, {"exit", VMOPC_EXIT , &AsmParser::read_u8, NULL }, {"fcall", VMOPC_FCALL , &AsmParser::read_fun_call, NULL }, {"fcmp", VMOPC_FCMP, &AsmParser::read_cond_jump, NULL }, {"feval", VMOPC_FEVAL,&AsmParser::read_cond_jump, NULL }, {"getfield", VMOPC_GETFIELD,&AsmParser::read_field, NULL }, {"icall", VMOPC_ICALL , &AsmParser::read_interface_call, NULL }, {"icmp", VMOPC_ICMP, &AsmParser::read_cond_jump, NULL }, {"ieval", VMOPC_IEVAL,&AsmParser::read_cond_jump, NULL }, {"ilookupswitch", VMOPC_ILOOKUPSWITCH,&AsmParser::read_ilookupswitch, NULL }, {"inc", VMOPC_INC , &AsmParser::paramless, NULL }, {"incl", VMOPC_INCL , &AsmParser::read_local_var, NULL }, {"initl", VMOPC_INITL , &AsmParser::read_local_var, NULL }, {"initp", VMOPC_INITP , &AsmParser::read_local_var, NULL }, {"isnull", VMOPC_ISNULL, &AsmParser::read_cond_jump, NULL }, {"itableswitch", VMOPC_ITABLESWITCH,&AsmParser::read_itableswitch, NULL }, {"jmp", VMOPC_JMP , &AsmParser::read_jump, NULL }, {"licmp", VMOPC_LICMP, &AsmParser::read_cond_local_var_jump, NULL }, {"lieval", VMOPC_LIEVAL,&AsmParser::read_cond_local_var_jump, NULL }, {"mod", VMOPC_MOD , &AsmParser::paramless, NULL }, {"movc", VMOPC_MOVI , &AsmParser::read_local_var_constant, NULL }, {"movf", VMOPC_MOVI , &AsmParser::read_local_var_float, NULL }, {"movi", VMOPC_MOVI , &AsmParser::read_local_var_int, NULL }, {"mpop", VMOPC_MPOP , &AsmParser::read_u8, NULL }, {"mul", VMOPC_MUL , &AsmParser::paramless, NULL }, {"nop", VMOPC_NOP , &AsmParser::paramless, NULL }, {"ocmp", VMOPC_OCMP, &AsmParser::read_cond_jump, NULL }, {"pop", VMOPC_POP , &AsmParser::paramless, NULL }, {"popl", VMOPC_POPL , &AsmParser::read_local_var, NULL }, {"popstat", VMOPC_POPSTAT, &AsmParser::read_static, NULL }, {"pushc", VMOPC_PUSHC , &AsmParser::read_const, NULL }, {"pushf", VMOPC_PUSHF , &AsmParser::read_float, NULL }, {"pushfz", VMOPC_PUSHFZ, &AsmParser::paramless, NULL }, {"pushi", VMOPC_PUSHI, &AsmParser::read_s32, NULL }, {"pushiz", VMOPC_PUSHIZ, &AsmParser::paramless, NULL }, {"pushl", VMOPC_PUSHL, &AsmParser::read_local_var, NULL }, {"pushstat",VMOPC_PUSHSTAT, &AsmParser::read_static, NULL }, {"pushthis",VMOPC_PUSHTHIS, &AsmParser::paramless, NULL }, {"reducel", VMOPC_REDUCEL, &AsmParser::read_u16, NULL }, {"ret", VMOPC_RET , &AsmParser::paramless, NULL }, {"scharat", VMOPC_SCHARAT, &AsmParser::paramless, NULL }, {"scharatl", VMOPC_SCHARAT, &AsmParser::read_local_var, NULL }, {"scmp", VMOPC_SCMP, &AsmParser::read_cond_jump, NULL }, {"sconcat", VMOPC_SCONCAT , &AsmParser::paramless, NULL }, {"setfield", VMOPC_SETFIELD , &AsmParser::read_field, NULL }, {"sfloatcat", VMOPC_SFLOATCAT , &AsmParser::paramless, NULL }, {"sgetarg", VMOPC_SGETARG, &AsmParser::paramless, NULL }, {"sgetargrest", VMOPC_SGETARGREST, &AsmParser::paramless, NULL }, {"sintcat", VMOPC_SINTCAT , &AsmParser::paramless, NULL }, {"sisempty", VMOPC_SISEMPTY, &AsmParser::read_cond_jump, NULL }, {"sisnumber", VMOPC_SISNUMBER, &AsmParser::read_cond_jump, NULL }, {"sleep", VMOPC_SLEEP, &AsmParser::paramless, NULL }, {"slookupswitch", VMOPC_SLOOKUPSWITCH, &AsmParser::read_slookupswitch, NULL }, {"spushempty", VMOPC_SPUSHEMPTY, &AsmParser::paramless, NULL }, {"sstartargs", VMOPC_SSTARTARGS, &AsmParser::paramless, NULL }, {"stoint", VMOPC_STOINT, &AsmParser::paramless, NULL }, {"sub", VMOPC_SUB , &AsmParser::paramless, NULL }, {"swap", VMOPC_SWAP , &AsmParser::paramless, NULL }, {"upcast", VMOPC_UPCAST, &AsmParser::read_classname, NULL }, { NULL, VMOPC_ABORT, &AsmParser::paramless, NULL } }; const int parse_table_size = (sizeof( AsmParser::parser_table ) / sizeof( _parser_table ))-1; struct _parser_table * lookupOpcodeTable(char * mnem) { /* int i; for ( i =0; AsmParser::parser_table[i].name; i++) { if ( !strcmp(mnem, AsmParser::parser_table[i].name) ) return &AsmParser::parser_table[i]; } */ // Binary search int cmp, k, l,p; l = 0; p = parse_table_size; while ( l <= p ) { k = (l+p) >> 1; cmp = strcmp( mnem, AsmParser::parser_table[k].name ); if ( cmp < 0 ) p = k-1; else if ( cmp > 0 ) l = k+1; else return &AsmParser::parser_table[k]; } return NULL; } const u16 ADRESS_UNKNOWN = 0xffff; int AsmParser::parse( StaticInput & infr, Output & outfr ) { obj = new AsmObjFile(); char buf[BUF]; int asmerr = 0; int cnumber; infun = false; label * lab; String str; struct _parser_table * tbl; inf = &infr; outf = &outfr; memorycell cell; while ( true ) { inf->getword(buf); // Support for comments if( buf[0] == '#' ) { inf->skipline(); continue; } else if ( buf[0] == '!' ) { // CONTROL COMMANDS if ( !strcmp(buf, "!A") ) // Argument definition { if ( !infun ) { inf->error("AsmParser: '!A' outside of fun"); asmerr = -1; break; } readVariable(); args_count++; } else if ( !strcmp(buf, "!L") ) // Local variable definition { if ( !infun ) { inf->error("AsmParser: '!L' outside of fun"); asmerr = -1; break; } readVariable(); vars_count++; } else if ( !strcmp(buf, "!funstart") ) { if (infun) { inf->error( "AsmParser: '!funstart' inside another fun." ); asmerr = -1; break; } inf->getword(buf); infun = true; funstart = obj->getOffset(); strcpy(funname, buf); cell.vmint = 0; obj->putCell(cell); // for INITP obj->putCell(cell); // for INITL args_count = 0; vars_count = 0; } else if ( !strcmp( buf, "!funend") ) { int i; int reduced_length; if (!infun) { Cout << "AsmParser: '!funend' without matching funstart" <<endl; asmerr = -1; break; } infun = false; // check if all labels are resolved for_each( labels, lab) { if ( lab->adress == ADRESS_UNKNOWN ) { str <<"AsmParser:: fun ended but there is at least" << " one unresolved label: " << lab->name; inf->error(str); asmerr = -1; break; } } if ( vars_count && args_count ) { cell.vmint =0; cell.s.opcode = VMOPC_INITP; cell.s.u.number = args_count; obj->setCell(funstart, cell); cell.s.opcode = VMOPC_INITL; cell.s.u.number = vars_count; obj->setCell(funstart+1, cell); reduced_length = 0; } else if ( !vars_count ) { cell.vmint =0; cell.s.opcode = VMOPC_INITP; cell.s.u.number = args_count; obj->setCell(funstart+1, cell); reduced_length = -1; } else if ( !args_count ) { cell.vmint =0; cell.s.opcode = VMOPC_INITL; cell.s.u.number = vars_count; obj->setCell(funstart+1, cell); reduced_length = -1; } else // both arguments an locals missing { reduced_length = -2; } if ( reduced_length ) { for( i = shortjumps.length() - 1; i >=0; i-- ) { /* Cout << "Relocated cell " << shortjumps[i] << " by " << reduced_length << " in fun " << funname << endl;*/ obj->relocCell(shortjumps[i]+ funstart, reduced_length ); } } // reduced_length <= 0 so need -= instead of += funstart -= reduced_length; // it has to be before fun entry - AsmLoader 'feature' cnumber = obj->addConstant( VMT_CONST_STRING, funname, strlen(funname) + 1 ); vmfilefun vmff; vmff.start = funstart; vmff.end = obj->getOffset(); vmff.name = cnumber; obj->addConstant( VMT_EXPORT_FUN, &vmff, sizeof(vmfilefun) ); clearLabels(); locals.clr(); } else if ( !strcmp(buf, "!constant" ) ) { if (readConstant() < 0) { asmerr = -1; break; } } else if ( !strcmp(buf, "!end" ) ) { if ( infun ) { inf->error("AsmParser: '!end' occured inside of fun."); asmerr = -1; break; } asmerr = 0; break; } else { str << "AsmParser: unrecognized command " << buf; inf->error(str); asmerr = -1; break; } } else if ( buf[0] == ':' ) { // LABEL if ( !infun ) { inf->error("AsmParser: Label outside function."); asmerr = -1; break; } setLabel(&buf[1], obj->getOffset() - funstart); } else { // MNEMONIC tbl = lookupOpcodeTable(buf); if (!tbl) { str<< "Asmparser: - unknown mnemonic " << buf; inf->error(str); asmerr = -1; break; } // zero cell cell.vmint = 0; cell.s.opcode = tbl->opcode; if (! (this->*(tbl->mnem2code)) (cell)) { inf->error("Asmparser: error occured during mnemonic parsing"); asmerr = -1; break; } } } if ( !asmerr ) obj->writeTo(*outf); delete obj; clearLabels(); locals.clr(); return (asmerr); } // Params: name - name of label we want jump to // adress - point at which u16 absolute jump will be placed // Return: absolute offset of label ( or ADRESS_UNKNOWN(0xffff) // if unknown at time) // absolute offsets are counted from start of function u16 AsmParser::getLabel(char * name, int adress) { label * lab; for_each(labels, lab) { if ( lab->name == name ) { // Label already known ? if ( lab->adress != ADRESS_UNKNOWN ) { // Cout << "Set absolute jump " << name << " at " << (int) adress << " to " << (int) lab->adress <<endl; shortjumps.add(adress); return (lab->adress); } lab->to_resolve.add(adress); shortjumps.add(adress); return ADRESS_UNKNOWN; } } lab = new label; lab->name = name; lab->adress = ADRESS_UNKNOWN; lab->to_resolve.add(adress); labels.add(lab); shortjumps.add(adress); return ADRESS_UNKNOWN; } // Params: name - name of label we want to define // adress - place in code that label describes void AsmParser::setLabel(char * name, int adress) { label * lab; int i,size; if ( adress == ADRESS_UNKNOWN ) Error::dump("ADRESS_UNKNOWN passed to AsmParser::setLabel"); for_each(labels, lab) { if (lab->name == name) { // later redefine to local error not global if ( lab->adress != ADRESS_UNKNOWN ) Error::dump("Tried to redefine label at AsmParser::setLabel"); lab->adress = (u16)adress; size = lab->to_resolve.length(); for( i=0; i < size; i++ ) { // Cout << "Resolved one " << lab->name << " of " << size << endl; setAbsoluteJump( lab->to_resolve[i], adress ); } lab->to_resolve.clr(); return; } } lab = new label; lab->name = name; lab->adress = (u16)adress; labels.add(lab); } void AsmParser::clearLabels() { label * lab; labels.reset(); while ( (lab = labels.remove()) != NULL ) { delete lab; } shortjumps.clr(); } /* void AsmParser::setRelativeJump( int point, int to ) { // Cout << "Set relative jump at " << (int) point << " to " << (int) to <<endl; (*obj)[point].s.u.reloc = (s16) (to - point - 1); } */ void AsmParser::setAbsoluteJump( int point, int to ) { // Cout << "Set absolute jump at " << (int) point << " to " << (int) to <<endl; (*obj)[point+funstart].s.u.number = (u16) (to); } int AsmParser::readConstant() { char buf[BUF]; int i; inf->getword(buf); if ( !strcmp(buf, "string") ) { inf->getstring(buf); return obj->addConstant(VMT_CONST_STRING, buf, strlen(buf)+1); } else if ( !strcmp(buf, "funcall") ) { inf->getword(buf); return obj->addConstant(VMT_IMPORT_FUN, buf, strlen(buf)+1); } else if ( !strcmp(buf, "interfacecall") ) { inf->getword(buf); return obj->addConstant(VMT_IMPORT_INTERFACE, buf, strlen(buf)+1); } else if ( !strcmp(buf, "constant" ) ) { i = inf->getnum(); if ( i >= obj->numberOfConstants() ) { inf->error("AsmParser::readConstant - constant keyword with undefined const."); return -1; } return i; } else { inf->error("AsmParser::readConstant - only strings and imports supported for now."); return -1; } } void AsmParser::readVariable() { char buf[BUF]; inf->getword(buf); // type - ignored for now inf->getword(buf); // name locals.add(String(buf)); } bool AsmParser::read_float(memorycell cell) { obj->putCell(cell); obj->putFloat(inf->getfloat()); return true; } bool AsmParser::read_const(memorycell cell) { int i = readConstant(); if ( i < 0 ) return false; cell.s.u.number = i; obj->addRelocation(); obj->putCell(cell); return true; } u8 AsmParser::parseConditions(const char * txt) { int i; int len = strlen(txt); u8 answer = 0; for ( i=0; i < len; i++) { switch (txt[i]) { case 'a': answer = VMFLAG_EQUAL | VMFLAG_GREATER | VMFLAG_LESSER; break; case 'e': answer |= VMFLAG_EQUAL; break; case 'g': answer |= VMFLAG_GREATER; break; case 'l': answer |= VMFLAG_LESSER; break; default: answer = 255; inf->error("AsmParser::parseConditions - unknown letter"); break; } } return answer; } bool AsmParser::read_cond_jump(memorycell cell) { char buf[256]; inf->getword(buf); cell.s.conds = parseConditions(buf); inf->getword(buf); cell.s.u.number = getLabel(buf, obj->getOffset() -funstart); obj->putCell(cell); return true; } bool AsmParser::read_jump(memorycell cell) { char buf[256]; inf->getword(buf); cell.s.u.number = getLabel(buf, obj->getOffset() -funstart); obj->putCell(cell); return true; } bool AsmParser::read_fun_call(memorycell cell) { char buf[256]; inf->getword(buf); obj->addRelocation(); cell.s.u.number = obj->addConstant( VMT_IMPORT_FUN, buf, strlen(buf) + 1); obj->putCell(cell); return true; } bool AsmParser::read_interface_call(memorycell cell) { char buf[256]; inf->getword(buf); obj->addRelocation(); cell.s.u.number = obj->addConstant( VMT_IMPORT_INTERFACE, buf, strlen(buf) + 1); obj->putCell(cell); return true; } bool AsmParser::read_unknown_call(memorycell cell) { char buf[256]; inf->getword(buf); obj->addRelocation(); cell.s.u.number = obj->addConstant( VMT_IMPORT_FUN_OR_INTERFACE, buf, strlen(buf) + 1); obj->putCell(cell); return true; } bool AsmParser::read_static( memorycell cell ) { char buf[256]; inf->getword(buf); obj->addRelocation(); cell.s.u.number = obj->addConstant( VMT_IMPORT_STATIC, buf, strlen(buf) +1); obj->putCell(cell); return true; } // Later add checks for boundaries bool AsmParser::read_u8(memorycell cell) { cell.s.conds = inf->getnum(); obj->putCell(cell); return true; } bool AsmParser::read_u16(memorycell cell) { cell.s.u.number = inf->getnum(); obj->putCell(cell); return true; } bool AsmParser::read_s16(memorycell cell) { cell.s.u.reloc = inf->getnum(); obj->putCell(cell); return true; } bool AsmParser::read_s32(memorycell cell) { obj->putCell(cell); obj->putInt(inf->getnum()); return true; } bool AsmParser::paramless( memorycell cell ) { obj->putCell(cell); return true; } int AsmParser::lookupLocal( const char * txt ) { if ( *txt == '@' ) { int size = locals.length(); int i; const char * buf = txt +1; for ( i=0; i < size; i++ ) { if ( locals[i] == buf ) return i; } return -1; } if ( !isdigit(*txt) ) return -1; return atoi( txt ); } bool AsmParser::read_local_var( memorycell cell ) { char buf[256]; int number; number = lookupLocal(inf->getword(buf)); if ( number == -1 ) return false; cell.s.u.number = number; obj->putCell(cell); return true; } bool AsmParser::read_cond_local_var_jump( memorycell cell ) { char buf[256]; int number; inf->getword(buf); cell.s.conds = parseConditions(buf); number = lookupLocal(inf->getword(buf)); if ( number == -1 ) return false; cell.s.u.number = number; obj->putCell(cell); cell.vmint = 0; inf->getword(buf); cell.s.u.number = getLabel(buf, obj->getOffset() -funstart); obj->putCell(cell); return true; } bool AsmParser::read_local_var_int( memorycell cell ) { char buf[256]; int number; number = lookupLocal(inf->getword(buf)); if ( number == -1 ) return false; cell.s.u.number = number; obj->putCell(cell); obj->putInt(inf->getnum()); return true; } bool AsmParser::read_local_var_float( memorycell cell ) { char buf[256]; int number; number = lookupLocal(inf->getword(buf)); if ( number == -1 ) return false; cell.s.u.number = number; obj->putCell(cell); obj->putFloat(inf->getfloat()); return true; } bool AsmParser::read_local_var_constant( memorycell cell ) { char buf[256]; int number; number = lookupLocal(inf->getword(buf)); if ( number == -1 ) return false; cell.s.u.number = number; obj->putCell(cell); number = readConstant(); if ( number < 0 ) return false; obj->addRelocation(); cell.vmint =0; cell.s.u.number = number; obj->putCell(cell); return true; } bool AsmParser::read_ilookupswitch( memorycell cell ) { char buf[256]; int length = inf->getnum(); if ( length > 255 ) return false; cell.s.conds = length; inf->getword(buf); cell.s.u.number = getLabel( buf, obj->getOffset() - funstart ); obj->putCell(cell); for ( ; length > 0; length-- ) { cell.vmint = inf->getnum(); obj->putCell(cell); inf->getword(buf); cell.vmint = 0; cell.s.u.number = getLabel(buf, obj->getOffset() - funstart ); obj->putCell(cell); } return true; } bool AsmParser::read_slookupswitch( memorycell cell ) { char buf[256]; int length = inf->getnum(); if ( length > 255 ) return false; cell.s.conds = length; inf->getword(buf); cell.s.u.number = getLabel( buf, obj->getOffset() - funstart ); obj->putCell(cell); for ( ; length > 0; length-- ) { cell.vmint =0; inf->getstring(buf); cell.s.u.number = obj->addConstant(VMT_CONST_STRING, buf, strlen(buf)+1); obj->addRelocation(); obj->putCell(cell); inf->getword(buf); cell.vmint = 0; cell.s.u.number = getLabel(buf, obj->getOffset() - funstart ); obj->putCell(cell); } return true; } bool AsmParser::read_itableswitch( memorycell cell ) { int min,max,i; char buf[256]; char defaul[256]; inf->getword(defaul); cell.s.u.number = getLabel( defaul, obj->getOffset() - funstart ); obj->putCell(cell); min = inf->getnum(); max = inf->getnum(); if ( min > max ) return false; cell.vmint = min; obj->putCell(cell); cell.vmint = max; obj->putCell(cell); cell.vmint = 0; while ( min <= max ) { i = inf->getnum(); while (min < i) { cell.s.u.number = getLabel( defaul, obj->getOffset() - funstart ); obj->putCell(cell); min++; } inf->getword(buf); cell.s.u.number = getLabel( buf, obj->getOffset() - funstart ); obj->putCell(cell); min++; } return true; } bool AsmParser::read_field( memorycell cell ) { char buf1[256]; char buf2[256]; int s1, s2; inf->getword( buf1 ); inf->getword( buf2 ); s1 = strlen(buf1); s2 = strlen(buf2); memcpy( &buf1[s1+1], buf2, s2 +1 ); cell.s.u.number = obj->addConstant( VMT_FIELD_DATA, buf1, s1 + s2 + 2); obj->addRelocation(); obj->putCell(cell); return true; } bool AsmParser::read_classname( memorycell cell ) { char buf[128]; inf->getword(buf); cell.s.u.number = obj->addConstant(VMT_CLASS_TYPE, buf, strlen(buf)+1); obj->addRelocation(); obj->putCell(cell); return true; } // If I ever write run-time disassember of VM code this code will change // Table containg opcode -> parser_table mapping will be constructed at boot const char * lookupOpcodeName( u8 opcode ) { int i; for ( i =0; AsmParser::parser_table[i].name; i++) { if ( opcode == AsmParser::parser_table[i].opcode ) return AsmParser::parser_table[i].name; } return "UNKNOWN"; }