$title ('CODEMACRO SUBROUTINE MODULE') cmsubr: do; /* modified 4/7/81 R. Silberstein modified 4/13/81 R. Silberstein modified 5/5/81 R. Silberstein modified 9/2/81 R. Silberstein */ /* This is the module to 1) test if a set of operands matches a given instruction and 2) produce output code for matched instruction The module interfaces the CODEOUTPUT module to physically send code bytes to the HEX output file. */ $include (:f1:macro.lit) $include (:f1:equals.lit) $include (:f1:cmacd.lit) $include (:f1:outp.lit) $include (:f1:scan.ext) $include (:f1:subr1.ext) $INCLUDE (:F1:SUBR2.EXT) $include (:f1:outp.ext) $include (:f1:ermod.ext) $include (:f1:cmsubr.x86) $eject dcl /* global variables */ bytevar based macroptr byte, /* variables within codemacros */ addrvar based macroptr addr, emitbyte(80) byte, /* buffer of output codebytes */ emitindex byte, /* index of "emitbyte" */ bitcomtab(2) byte data /* legal commands within "DBIT" */ (mnumberbits,mformalbits); $eject /********** MICHELLANEOUS SUBROUTINES: **********/ clearcmindex: proc public; emitindex=0; end clearcmindex; emit: proc public; /* emit codebytes for an instruction */ dcl i byte; i=0ffh; do while (i:=i+1) < emitindex; call emitcodebyte(emitbyte(i),CSdata); end$while; end emit; emitdummies: proc public; /* emit dummy (NO-OP-) bytes if error */ dcl (i,j) byte,nodum(4) byte data(2,5,6,8); j=nooper; if j>3 then j=3; i=0ffh; do while (i:=i+1) < nodum(j); call emitcodebyte(90h,CSdata); /* 90H = NOP */ end$while; end emitdummies; emitsinglebyte: proc(ch); /* fill local emitbuffer with a new byte */ dcl ch byte; if noerror then$do emitbyte(emitindex)=ch; emitindex=emitindex+1; end$if; end emitsinglebyte; emitsingleword: proc (var); /* fill 2 new bytes into emitbuffer */ dcl var addr, byt1 byte at(.var), byt2 byte at(.var+1); call emitsinglebyte(byt1); call emitsinglebyte(byt2); end emitsingleword; incrmacroptr: proc; macroptr=macroptr+1; end incrmacroptr; getoperadr: proc address; dcl pt address; pt=.operands(bytevar); call incrmacroptr; return pt; end getoperadr; /* recognize codemacro command type */ commandtype: proc(comno,lg,pt) byte public; dcl (comno,lg,i) byte,pt address,ch based pt(1) byte; i=0ffh; do while (i:=i+1) < lg; if comno=ch(i) then$do call incrmacroptr; return i; end$if; end$while; return lg; end commandtype; $eject /******* CODEMACRO COMMAND SUBROUTINES: ********/ mDBNrout: proc public; call emitsinglebyte(bytevar); call incrmacroptr; end mDBNrout; mDBFrout: proc public; dcl pt address,opr based pt operandstruc; pt=getoperadr; call emitsinglebyte(opr.offset); end mDBFrout; mDWNrout: proc public; call emitsingleword(addrvar); call incrmacroptr; call incrmacroptr; end mDWNrout; mDWFrout: proc public; dcl pt address,opr based pt operandstruc; pt=getoperadr; call emitsingleword(opr.offset); end mDWFrout; mDDFrout: proc public; dcl pt address,opr based pt operandstruc; pt=getoperadr; if (opr.sflag and segmbit) = 0 then call errmsg(misssegminfo); call emitsingleword(opr.offset); call emitsingleword(opr.segment); end mDDFrout; mRELBrout: proc public; dcl pt address,opr based pt operandstruc,displ addr; pt=getoperadr; displ=opr.offset-cip-2; if (opr.segment <> csegvalue) or (typecalc(displ)=wrd) then$do call errmsg(laboutofrange); end$if; call emitsinglebyte(displ); IF ABSADDR (0) = SPACE THEN$DO CALL HEX2OUT (OPR.OFFSET, .ABSADDR); END$IF; end mRELBrout; mRELWrout: proc public; dcl pt address,opr based pt operandstruc; pt=getoperadr; if opr.segment <> csegvalue then call errmsg(laboutofrange); call emitsingleword(opr.offset-cip-3); IF ABSADDR (0) = SPACE THEN$DO CALL HEX2OUT (OPR.OFFSET, .ABSADDR); END$IF; end mRELWrout; mNOSEGFIXrout: proc public; dcl (segr,flag,segt) byte,pt address,opr based pt operandstruc; segr=bytevar; call incrmacroptr; pt=getoperadr; if (opr.baseindex and nooverridebit) = 0 then$do flag=opr.sflag; segt=shr(flag,segtypecount) and 3; noerror=(segt=segr); end$if; end mNOSEGFIXrout; mSEGFIXrout: proc public; dcl pt address,opr based pt operandstruc,(segr,override,sflag) byte; DSovertest: proc byte; segr=shr(opr.baseindex,baseregcount) and 1; return (((sflag and bregbit) <> 0) and (segr=1)); /* 1 = BP */ end DSovertest; pt=getoperadr; sflag=opr.sflag; if (opr.baseindex and nooverridebit) = 0 then$do segr=shr(sflag,segtypecount) and 3; do case segr; do; override=true; segr=ESover; end; /* ES */ do; override=true; segr=CSover; end; /* CS */ do; override=not DSovertest; segr=SSover; end; /* SS */ do; override=DSovertest; segr=DSover; end; /* DS */ end$case; if override then call emitsinglebyte(segr); end$if; end mSEGFIXrout; MODRM: proc (regfield,pt); dcl pt address,opr based pt operandstruc, (regfield,modfield,rmfield,dispflag,stype,sflag,segr) byte, BASEIND BYTE, offset addr, displow byte at(.offset),disphigh byte at (.offset+1); disptype: proc byte; if segr=rcs then return 2; /* disp always 2 for variable in CS */ if offset = 0 then return 0; return typecalc(offset); end disptype; indextype: proc byte; if (sflag and iregbit) <> 0 then$do if (sflag and bregbit) <> 0 then return 0; return 1; end$if; return 2; end indextype; offset=opr.offset; /* pick up operand attributes */ stype=opr.stype; sflag=opr.sflag; segr=shr(sflag,segtypecount) and 3; BASEIND = OPR.BASEINDEX AND (BASEREGBIT OR INDEXREGBIT); if stype=reg then$do rmfield=offset; modfield=11b; dispflag=0; else$do if (sflag and (iregbit or bregbit)) = 0 then$do rmfield=110b; modfield=0; dispflag=2; else$do dispflag=disptype; /* get no of DISP bytes */ modfield=dispflag; do case indextype; /* both base- and index-reg */ RMFIELD = BASEIND AND (INDEXREGBIT OR BASEREGBIT); /* index reg only */ RMFIELD = 100B OR (BASEIND AND INDEXREGBIT); do; /* base reg only */ IF (BASEIND AND BASEREGBIT) > 0 THEN$DO rmfield=110b; /* mod=00 and r/m=110B is a special case */ if dispflag=0 then$do dispflag,modfield=1; end$if; else$do rmfield=111b; end$if; end; end$case; end$if; end$if; regfield=shl(regfield,3) and 38h; modfield=shl(modfield,6) and 0c0h; call emitsinglebyte(regfield or modfield or rmfield); if dispflag > 0 then$do call emitsinglebyte(displow); if dispflag=2 then call emitsinglebyte(disphigh); end$if; end MODRM; mMODRM1rout: proc public; dcl regfield byte; regfield=bytevar; call incrmacroptr; call MODRM(regfield,getoperadr); end mMODRM1rout; mMODRM2rout: proc public; dcl regfield byte,pt address,opr based pt operandstruc; pt=getoperadr; regfield=opr.offset; call MODRM(regfield,getoperadr); end mMODRM2rout; mDBITrout: proc public; dcl (result,crbit) byte,bittab(8) byte data(1,2,4,8,16,32,64,128); join: proc(numb,nobit,noshift); dcl (numb,nobit,noshift) byte; if noshift > 0 then numb=shr(numb,noshift); if nobit < 8 then numb=shl(numb,8-nobit); do while (crbit <> 0ffh) and (nobit > 0); if (numb and 80h) <> 0 then result=result or bittab(crbit); crbit=crbit-1; nobit=nobit-1; numb=shl(numb,1); end$while; end join; NUMBERBITSrout: proc; dcl nobit byte; nobit=bytevar; call incrmacroptr; call join(bytevar,nobit,0); call incrmacroptr; end NUMBERBITSrout; FORMBITSrout: proc; dcl (nobit,numb) byte,pt address,opr based pt operandstruc; nobit=bytevar; call incrmacroptr; pt=getoperadr; numb=opr.offset; call join(numb,nobit,bytevar); call incrmacroptr; end FORMBITSrout; result=0; crbit=7; /* current bit position */ do while bytevar <> mendbit; /* do until ENDBIT command */ do case commandtype(bytevar,length(bitcomtab),.bitcomtab); call NUMBERBITSrout; call FORMBITSrout; do; end; end$case; end$while; call incrmacroptr; /* skip ENDBIT command */ call emitsinglebyte(result); end mDBITrout; $eject /********* ROUTINES TO MATCH OPERANDS TO INSTRUCTION ********/ /* test user operand against codemacro parameter */ matchsingleop: proc(opno) byte; dcl (match,specletter,modletter,range,rangetype) byte, (rangev1,rangev2,opno) byte, pt address, oper based pt operandstruc; rangetest: proc byte; /* perform rangetest */ dcl opervalue byte; rangev1=bytevar; call incrmacroptr; if range=doublerange then$do rangev2=bytevar; call incrmacroptr; end$if; opervalue=oper.offset; if range=doublerange then$do return ((opervalue>=rangev1) and (opervalue<=rangev2)); else$do return (opervalue=rangev1); end$if; end rangetest; modlettertest: proc byte; dcl numb addr,(styp,modbyt) byte; styp=oper.stype; if styp=lab then return true; modbyt=oper.sflag and typebit; if styp = reg then return (modbyt=modletter); if styp = variable then return ((modbyt=nomod) or (modbyt=modletter)); if styp=number then$do numb=oper.offset; do case modletter-1; return not wrdtest(numb); /* BYTE */ return wrdtest(numb); /* WORD */ return (typecalc(numb)=byt); /* signed BYTE */ return false; /* DWORD */ end$case; end$if; return false; end modlettertest; speclettertest: proc byte; dcl (opertype,locvalue,loctype) byte; memtest: proc byte; return (opertype=variable); end memtest; opertype=oper.stype; locvalue=oper.offset; loctype=oper.sflag and typebit; do case specletter; /* A - accumulator (AX or AL) */ return ((opertype=reg) and (locvalue=rax)); /* C - code reference,i.e. label */ return (opertype=lab); /* D - immediate data */ return (opertype=number); /* E - effective address, i.e. memory address or register */ return (memtest or (opertype=reg)); /* M - memory address */ return memtest; /* R - register except segment register */ return ((opertype=reg) and (loctype <> dwrd)); /* S - segment register */ return ((opertype=reg) and (loctype = dwrd)); /* X - memory address without indexing */ return ((opertype=variable) and ((oper.sflag and (iregbit or bregbit))=0)); end$case; end speclettertest; specletter=bytevar; /* pick up codemacro attributes */ call incrmacroptr; modletter=bytevar and modletter$bit; range=bytevar and range$and; rangetype=bytevar and rangetype$and; call incrmacroptr; pt=.operands(opno); /* address of current user operand */ match=true; if range <> norange then match=rangetest; if modletter <> 0 then match=match and modlettertest; if match then match=speclettertest; return match; end matchsingleop; /* test if operands match a specific codemacro */ matchingops: proc byte; dcl savept address,(nopara,match,parno) byte; savept=macroptr; call incrmacroptr; /* macroptr=macroptr+2 */ call incrmacroptr; nopara=bytevar; /* pick up no of parameters */ call incrmacroptr; /* advance to first formal */ if (nopara and prefix$on) <> 0 then return true; /* PREFIX */ if nopara <> nooper then$do match=false; else$do match=true; parno=0ffh; do while (parno:=parno+1) < nopara; match=match and matchsingleop(parno); end$while; end$if; if not match then macroptr=savept; return match; end matchingops; /* test if operands match instruction */ searchformatch: proc byte public; dcl next based macroptr address; macroptr=firstmacroptr; do forever; if matchingops then return true; if next=0 then return false; macroptr=next; end$forever; end searchformatch; end$module cmsubr;