summaryrefslogtreecommitdiffstatshomepage
path: root/scripts/src/netlist.lua
619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165
Commit message (Expand)AuthorAgeFilesLines
* -netlist: Added 82S123 256-bit (32x8) TTL bipolar PROM. [Ryan Holtz] MooglyGuy2016-12-241-0/+2
* -netlist: Added 82S126 4kbit (512x8) TTL bipolar PROM. [Ryan Holtz] therealmogminer@gmail.com2016-12-231-0/+2
* netlist: Added 4316 bilateral switch pack. Vas Crabb2016-12-231-0/+2
* -netlist: Added 74165, 74194, 7475, 7485, and DM9322 devices. [Ryan Holtz] therealmogminer@gmail.com2016-12-221-0/+10
* -netlist: Added shared RAM pointer, for use by netlist RAM devices which need... therealmogminer@gmail.com2016-12-211-0/+6
* -netlist: Added 74166 Parallel-Load 8-Bit Shift Register device. [Ryan Holtz] therealmogminer@gmail.com2016-12-211-0/+2
* -netlist: Added 2716 16 Kbit (2 Kbit x 8) UV EPROM. [Ryan Holtz] therealmogminer@gmail.com2016-12-211-0/+2
* -netlist: Added 74174 Hex D-Type Flip-Flop with Clear [Ryan Holtz] therealmogminer@gmail.com2016-12-211-0/+2
* netlist: Added 82S126 1K-bit bipolar PROM device. [Ryan Holtz] therealmogminer@gmail.com2016-12-211-0/+2
* netlist: Device additions: [Ryan Holtz] therealmogminer@gmail.com2016-12-211-0/+8
// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************

    drcbex64.c

    64-bit x64 back-end for the universal machine language.

****************************************************************************

    Future improvements/changes:

    * Add support for FP registers

    * Optimize to avoid unnecessary reloads

    * Identify common pairs and optimize output

    * Convert SUB a,0,b to NEG

    * Optimize, e.g., and [r5],i0,$FF to use rbx as temporary register
        (avoid initial move) if i0 is not needed going forward

****************************************************************************

    -------------------------
    ABI/conventions (Windows)
    -------------------------

    Registers:
        RAX        - volatile, function return value
        RBX        - non-volatile
        RCX        - volatile, integer function parameter 1
        RDX        - volatile, integer function parameter 2
        RSI        - non-volatile
        RDI        - non-volatile
        RBP        - non-volatile
        R8         - volatile, integer function parameter 3
        R9         - volatile, integer function parameter 4
        R10        - volatile
        R11        - volatile, scratch immediate storage
        R12        - non-volatile
        R13        - non-volatile
        R14        - non-volatile
        R15        - non-volatile

        XMM0       - volatile, FP function parameter 1
        XMM1       - volatile, FP function parameter 2
        XMM2       - volatile, FP function parameter 3
        XMM3       - volatile, FP function parameter 4
        XMM4       - volatile
        XMM5       - volatile
        XMM6       - non-volatile
        XMM7       - non-volatile
        XMM8       - non-volatile
        XMM9       - non-volatile
        XMM10      - non-volatile
        XMM11      - non-volatile
        XMM12      - non-volatile
        XMM13      - non-volatile
        XMM14      - non-volatile
        XMM15      - non-volatile


    -----------------------------
    ABI/conventions (Linux/MacOS)
    -----------------------------

    Registers:
        RAX        - volatile, function return value
        RBX        - non-volatile
        RCX        - volatile, integer function parameter 4
        RDX        - volatile, integer function parameter 3
        RSI        - volatile, integer function parameter 2
        RDI        - volatile, integer function parameter 1
        RBP        - non-volatile
        R8         - volatile, integer function parameter 5
        R9         - volatile, integer function parameter 6
        R10        - volatile
        R11        - volatile, scratch immediate storage
        R12        - non-volatile
        R13        - non-volatile
        R14        - non-volatile
        R15        - non-volatile

        XMM0       - volatile, FP function parameter 1
        XMM1       - volatile, FP function parameter 2
        XMM2       - volatile, FP function parameter 3
        XMM3       - volatile, FP function parameter 4
        XMM4       - volatile
        XMM5       - volatile
        XMM6       - volatile
        XMM7       - volatile
        XMM8       - volatile
        XMM9       - volatile
        XMM10      - volatile
        XMM11      - volatile
        XMM12      - volatile
        XMM13      - volatile
        XMM14      - volatile
        XMM15      - volatile


    ---------------
    Execution model
    ---------------

    Registers (Windows):
        RAX        - scratch register
        RBX        - maps to I0
        RCX        - scratch register
        RDX        - scratch register
        RSI        - maps to I1
        RDI        - maps to I2
        RBP        - pointer to code cache
        R8         - scratch register
        R9         - scratch register
        R10        - scratch register
        R11        - scratch register
        R12        - maps to I3
        R13        - maps to I4
        R14        - maps to I5
        R15        - maps to I6

    Registers (Linux/MacOS):
        RAX        - scratch register
        RBX        - maps to I0
        RCX        - scratch register
        RDX        - scratch register
        RSI        - unused
        RDI        - unused
        RBP        - pointer to code cache
        R8         - scratch register
        R9         - scratch register
        R10        - scratch register
        R11        - scratch register
        R12        - maps to I1
        R13        - maps to I2
        R14        - maps to I3
        R15        - maps to I4

    Entry point:
        Assumes 1 parameter passed, which is the codeptr of the code
        to execute once the environment is set up.

    Exit point:
        Assumes exit value is in RAX.

    Entry stack:
        [rsp]      - return

    Runtime stack:
        [rsp]      - r9 home
        [rsp+8]    - r8 home
        [rsp+16]   - rdx home
        [rsp+24]   - rcx home
        [rsp+40]   - saved r15
        [rsp+48]   - saved r14
        [rsp+56]   - saved r13
        [rsp+64]   - saved r12
        [rsp+72]   - saved ebp
        [rsp+80]   - saved edi
        [rsp+88]   - saved esi
        [rsp+96]   - saved ebx
        [rsp+104]  - ret

***************************************************************************/

#include "emu.h"
#include "drcbex64.h"

#include "debugger.h"
#include "emuopts.h"

#include <cstddef>


// This is a trick to make it build on Android where the ARM SDK declares ::REG_Rn
// and the x64 SDK declares ::REG_Exx and ::REG_Rxx
namespace drc {

using namespace uml;

using namespace asmjit;
using namespace asmjit::x86;



//**************************************************************************
//  DEBUGGING
//**************************************************************************

#define LOG_HASHJMPS            (0)

#define USE_RCPSS_FOR_SINGLES   (0)
#define USE_RSQRTSS_FOR_SINGLES (0)
#define USE_RCPSS_FOR_DOUBLES   (0)
#define USE_RSQRTSS_FOR_DOUBLES (0)



//**************************************************************************
//  CONSTANTS
//**************************************************************************

const uint32_t PTYPE_M    = 1 << parameter::PTYPE_MEMORY;
const uint32_t PTYPE_I    = 1 << parameter::PTYPE_IMMEDIATE;
const uint32_t PTYPE_R    = 1 << parameter::PTYPE_INT_REGISTER;
const uint32_t PTYPE_F    = 1 << parameter::PTYPE_FLOAT_REGISTER;
//const uint32_t PTYPE_MI   = PTYPE_M | PTYPE_I;
//const uint32_t PTYPE_RI   = PTYPE_R | PTYPE_I;
const uint32_t PTYPE_MR   = PTYPE_M | PTYPE_R;
const uint32_t PTYPE_MRI  = PTYPE_M | PTYPE_R | PTYPE_I;
const uint32_t PTYPE_MF   = PTYPE_M | PTYPE_F;

#ifdef X64_WINDOWS_ABI

const Gp::Id REG_PARAM1    = Gp::kIdCx;
const Gp::Id REG_PARAM2    = Gp::kIdDx;
const Gp::Id REG_PARAM3    = Gp::kIdR8;
const Gp::Id REG_PARAM4    = Gp::kIdR9;

#else

const Gp::Id REG_PARAM1    = Gp::kIdDi;
const Gp::Id REG_PARAM2    = Gp::kIdSi;
const Gp::Id REG_PARAM3    = Gp::kIdDx;
const Gp::Id REG_PARAM4    = Gp::kIdCx;

#endif



//**************************************************************************
//  MACROS
//**************************************************************************

#define X86_CONDITION(condition)        (condition_map[condition - uml::COND_Z])
#define X86_NOT_CONDITION(condition)    negateCond(condition_map[condition - uml::COND_Z])

#define assert_no_condition(inst)       assert((inst).condition() == uml::COND_ALWAYS)
#define assert_any_condition(inst)      assert((inst).condition() == uml::COND_ALWAYS || ((inst).condition() >= uml::COND_Z && (inst).condition() < uml::COND_MAX))
#define assert_no_flags(inst)           assert((inst).flags() == 0)
#define assert_flags(inst, valid)       assert(((inst).flags() & ~(valid)) == 0)



//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

drcbe_x64::opcode_generate_func drcbe_x64::s_opcode_table[OP_MAX];

// size-to-mask table
//static const uint64_t size_to_mask[] = { 0, 0xff, 0xffff, 0, 0xffffffff, 0, 0, 0, 0xffffffffffffffffU };

// register mapping tables
static const Gp::Id int_register_map[REG_I_COUNT] =
{
#ifdef X64_WINDOWS_ABI
	Gp::kIdBx, Gp::kIdSi, Gp::kIdDi, Gp::kIdR12, Gp::kIdR13, Gp::kIdR14, Gp::kIdR15,
#else
	Gp::kIdBx, Gp::kIdR12, Gp::kIdR13, Gp::kIdR14, Gp::kIdR15
#endif
};

static uint32_t float_register_map[REG_F_COUNT] =
{
#ifdef X64_WINDOWS_ABI
	6, 7, 8, 9, 10, 11, 12, 13, 14, 15
#else
	// on AMD x64 ABI, XMM0-7 are FP function args.  since this code has no args, and we
	// save/restore them around CALLC, they should be safe for our use.
	0, 1, 2, 3, 4, 5, 6, 7
#endif
};

// condition mapping table
static const CondCode condition_map[uml::COND_MAX - uml::COND_Z] =
{
	CondCode::kZ,    // COND_Z = 0x80,    requires Z
	CondCode::kNZ,   // COND_NZ,          requires Z
	CondCode::kS,    // COND_S,           requires S
	CondCode::kNS,   // COND_NS,          requires S
	CondCode::kC,    // COND_C,           requires C
	CondCode::kNC,   // COND_NC,          requires C
	CondCode::kO,    // COND_V,           requires V
	CondCode::kNO,   // COND_NV,          requires V
	CondCode::kP,    // COND_U,           requires U
	CondCode::kNP,   // COND_NU,          requires U
	CondCode::kA,    // COND_A,           requires CZ
	CondCode::kBE,   // COND_BE,          requires CZ
	CondCode::kG,    // COND_G,           requires SVZ
	CondCode::kLE,   // COND_LE,          requires SVZ
	CondCode::kL,    // COND_L,           requires SV
	CondCode::kGE,   // COND_GE,          requires SV
};

#if 0
// rounding mode mapping table
static const uint8_t fprnd_map[4] =
{
	FPRND_CHOP,     // ROUND_TRUNC,   truncate
	FPRND_NEAR,     // ROUND_ROUND,   round
	FPRND_UP,       // ROUND_CEIL,    round up
	FPRND_DOWN      // ROUND_FLOOR    round down
};
#endif



//**************************************************************************
//  TABLES
//**************************************************************************

const drcbe_x64::opcode_table_entry drcbe_x64::s_opcode_table_source[] =
{
	// Compile-time opcodes
	{ uml::OP_HANDLE,  &drcbe_x64::op_handle },     // HANDLE  handle
	{ uml::OP_HASH,    &drcbe_x64::op_hash },       // HASH    mode,pc
	{ uml::OP_LABEL,   &drcbe_x64::op_label },      // LABEL   imm
	{ uml::OP_COMMENT, &drcbe_x64::op_comment },    // COMMENT string
	{ uml::OP_MAPVAR,  &drcbe_x64::op_mapvar },     // MAPVAR  mapvar,value

	// Control Flow Operations
	{ uml::OP_NOP,     &drcbe_x64::op_nop },        // NOP
	{ uml::OP_DEBUG,   &drcbe_x64::op_debug },      // DEBUG   pc
	{ uml::OP_EXIT,    &drcbe_x64::op_exit },       // EXIT    src1[,c]
	{ uml::OP_HASHJMP, &drcbe_x64::op_hashjmp },    // HASHJMP mode,pc,handle
	{ uml::OP_JMP,     &drcbe_x64::op_jmp },        // JMP     imm[,c]
	{ uml::OP_EXH,     &drcbe_x64::op_exh },        // EXH     handle,param[,c]
	{ uml::OP_CALLH,   &drcbe_x64::op_callh },      // CALLH   handle[,c]
	{ uml::OP_RET,     &drcbe_x64::op_ret },        // RET     [c]
	{ uml::OP_CALLC,   &drcbe_x64::op_callc },      // CALLC   func,ptr[,c]
	{ uml::OP_RECOVER, &drcbe_x64::op_recover },    // RECOVER dst,mapvar

	// Internal Register Operations
	{ uml::OP_SETFMOD, &drcbe_x64::op_setfmod },    // SETFMOD src
	{ uml::OP_GETFMOD, &drcbe_x64::op_getfmod },    // GETFMOD dst
	{ uml::OP_GETEXP,  &drcbe_x64::op_getexp },     // GETEXP  dst
	{ uml::OP_GETFLGS, &drcbe_x64::op_getflgs },    // GETFLGS dst[,f]
	{ uml::OP_SAVE,    &drcbe_x64::op_save },       // SAVE    dst
	{ uml::OP_RESTORE, &drcbe_x64::op_restore },    // RESTORE dst

	// Integer Operations
	{ uml::OP_LOAD,    &drcbe_x64::op_load },       // LOAD    dst,base,index,size
	{ uml::OP_LOADS,   &drcbe_x64::op_loads },      // LOADS   dst,base,index,size
	{ uml::OP_STORE,   &drcbe_x64::op_store },      // STORE   base,index,src,size
	{ uml::OP_READ,    &drcbe_x64::op_read },       // READ    dst,src1,spacesize
	{ uml::OP_READM,   &drcbe_x64::op_readm },      // READM   dst,src1,mask,spacesize
	{ uml::OP_WRITE,   &drcbe_x64::op_write },      // WRITE   dst,src1,spacesize
	{ uml::OP_WRITEM,  &drcbe_x64::op_writem },     // WRITEM  dst,src1,spacesize
	{ uml::OP_CARRY,   &drcbe_x64::op_carry },      // CARRY   src,bitnum
	{ uml::OP_SET,     &drcbe_x64::op_set },        // SET     dst,c
	{ uml::OP_MOV,     &drcbe_x64::op_mov },        // MOV     dst,src[,c]
	{ uml::OP_SEXT,    &drcbe_x64::op_sext },       // SEXT    dst,src
	{ uml::OP_ROLAND,  &drcbe_x64::op_roland },     // ROLAND  dst,src1,src2,src3
	{ uml::OP_ROLINS,  &drcbe_x64::op_rolins },     // ROLINS  dst,src1,src2,src3
	{ uml::OP_ADD,     &drcbe_x64::op_add },        // ADD     dst,src1,src2[,f]
	{ uml::OP_ADDC,    &drcbe_x64::op_addc },       // ADDC    dst,src1,src2[,f]
	{ uml::OP_SUB,     &drcbe_x64::op_sub },        // SUB     dst,src1,src2[,f]
	{ uml::OP_SUBB,    &drcbe_x64::op_subc },       // SUBB    dst,src1,src2[,f]
	{ uml::OP_CMP,     &drcbe_x64::op_cmp },        // CMP     src1,src2[,f]
	{ uml::OP_MULU,    &drcbe_x64::op_mulu },       // MULU    dst,edst,src1,src2[,f]
	{ uml::OP_MULS,    &drcbe_x64::op_muls },       // MULS    dst,edst,src1,src2[,f]
	{ uml::OP_DIVU,    &drcbe_x64::op_divu },       // DIVU    dst,edst,src1,src2[,f]
	{ uml::OP_DIVS,    &drcbe_x64::op_divs },       // DIVS    dst,edst,src1,src2[,f]
	{ uml::OP_AND,     &drcbe_x64::op_and },        // AND     dst,src1,src2[,f]
	{ uml::OP_TEST,    &drcbe_x64::op_test },       // TEST    src1,src2[,f]
	{ uml::OP_OR,      &drcbe_x64::op_or },         // OR      dst,src1,src2[,f]
	{ uml::OP_XOR,     &drcbe_x64::op_xor },        // XOR     dst,src1,src2[,f]
	{ uml::OP_LZCNT,   &drcbe_x64::op_lzcnt },      // LZCNT   dst,src[,f]
	{ uml::OP_TZCNT,   &drcbe_x64::op_tzcnt },      // TZCNT   dst,src[,f]
	{ uml::OP_BSWAP,   &drcbe_x64::op_bswap },      // BSWAP   dst,src
	{ uml::OP_SHL,     &drcbe_x64::op_shift<Inst::kIdShl> },        // SHL     dst,src,count[,f]
	{ uml::OP_SHR,     &drcbe_x64::op_shift<Inst::kIdShr> },        // SHR     dst,src,count[,f]
	{ uml::OP_SAR,     &drcbe_x64::op_shift<Inst::kIdSar> },        // SAR     dst,src,count[,f]
	{ uml::OP_ROL,     &drcbe_x64::op_shift<Inst::kIdRol> },        // ROL     dst,src,count[,f]
	{ uml::OP_ROLC,    &drcbe_x64::op_shift<Inst::kIdRcl> },        // ROLC    dst,src,count[,f]
	{ uml::OP_ROR,     &drcbe_x64::op_shift<Inst::kIdRor> },        // ROR     dst,src,count[,f]
	{ uml::OP_RORC,    &drcbe_x64::op_shift<Inst::kIdRcr> },        // RORC    dst,src,count[,f]

	// Floating Point Operations
	{ uml::OP_FLOAD,   &drcbe_x64::op_fload },      // FLOAD   dst,base,index
	{ uml::OP_FSTORE,  &drcbe_x64::op_fstore },     // FSTORE  base,index,src
	{ uml::OP_FREAD,   &drcbe_x64::op_fread },      // FREAD   dst,space,src1
	{ uml::OP_FWRITE,  &drcbe_x64::op_fwrite },     // FWRITE  space,dst,src1
	{ uml::OP_FMOV,    &drcbe_x64::op_fmov },       // FMOV    dst,src1[,c]
	{ uml::OP_FTOINT,  &drcbe_x64::op_ftoint },     // FTOINT  dst,src1,size,round
	{ uml::OP_FFRINT,  &drcbe_x64::op_ffrint },     // FFRINT  dst,src1,size
	{ uml::OP_FFRFLT,  &drcbe_x64::op_ffrflt },     // FFRFLT  dst,src1,size
	{ uml::OP_FRNDS,   &drcbe_x64::op_frnds },      // FRNDS   dst,src1
	{ uml::OP_FADD,    &drcbe_x64::op_fadd },       // FADD    dst,src1,src2
	{ uml::OP_FSUB,    &drcbe_x64::op_fsub },       // FSUB    dst,src1,src2
	{ uml::OP_FCMP,    &drcbe_x64::op_fcmp },       // FCMP    src1,src2
	{ uml::OP_FMUL,    &drcbe_x64::op_fmul },       // FMUL    dst,src1,src2
	{ uml::OP_FDIV,    &drcbe_x64::op_fdiv },       // FDIV    dst,src1,src2
	{ uml::OP_FNEG,    &drcbe_x64::op_fneg },       // FNEG    dst,src1
	{ uml::OP_FABS,    &drcbe_x64::op_fabs },       // FABS    dst,src1
	{ uml::OP_FSQRT,   &drcbe_x64::op_fsqrt },      // FSQRT   dst,src1
	{ uml::OP_FRECIP,  &drcbe_x64::op_frecip },     // FRECIP  dst,src1
	{ uml::OP_FRSQRT,  &drcbe_x64::op_frsqrt },     // FRSQRT  dst,src1
	{ uml::OP_FCOPYI,  &drcbe_x64::op_fcopyi },     // FCOPYI  dst,src
	{ uml::OP_ICOPYF,  &drcbe_x64::op_icopyf }      // ICOPYF  dst,src
};

class ThrowableErrorHandler : public ErrorHandler
{
public:
	void handleError(Error err, const char *message, BaseEmitter *origin) override
	{
		throw emu_fatalerror("asmjit error %d: %s", err, message);
	}
};


//**************************************************************************
//  INLINE FUNCTIONS
//**************************************************************************

//-------------------------------------------------
//  param_normalize - convert a full parameter
//  into a reduced set
//-------------------------------------------------

drcbe_x64::be_parameter::be_parameter(drcbe_x64 &drcbe, const parameter &param, uint32_t allowed)
{
	int regnum;

	switch (param.type())
	{
		// immediates pass through
		case parameter::PTYPE_IMMEDIATE:
			assert(allowed & PTYPE_I);
			*this = param.immediate();
			break;

		// memory passes through
		case parameter::PTYPE_MEMORY:
			assert(allowed & PTYPE_M);
			*this = make_memory(param.memory());
			break;

		// if a register maps to a register, keep it as a register; otherwise map it to memory
		case parameter::PTYPE_INT_REGISTER:
			assert(allowed & PTYPE_R);
			assert(allowed & PTYPE_M);
			regnum = int_register_map[param.ireg() - REG_I0];
			if (regnum != 0)
				*this = make_ireg(regnum);
			else
				*this = make_memory(&drcbe.m_state.r[param.ireg() - REG_I0]);
			break;

		// if a register maps to a register, keep it as a register; otherwise map it to memory
		case parameter::PTYPE_FLOAT_REGISTER:
			assert(allowed & PTYPE_F);
			assert(allowed & PTYPE_M);
			regnum = float_register_map[param.freg() - REG_F0];
			if (regnum != 0)
				*this = make_freg(regnum);
			else
				*this = make_memory(&drcbe.m_state.f[param.freg() - REG_F0]);
			break;

		// everything else is unexpected
		default:
			fatalerror("Unexpected parameter type\n");
	}
}


//-------------------------------------------------
//  select_register - select a register to use,
//  avoiding conflicts with the optional
//  checkparam
//-------------------------------------------------

inline Gp drcbe_x64::be_parameter::select_register(Gp defreg) const
{
	if (m_type == PTYPE_INT_REGISTER)
		return Gp(defreg, m_value);
	return defreg;
}

inline Xmm drcbe_x64::be_parameter::select_register(Xmm defreg) const
{
	if (m_type == PTYPE_FLOAT_REGISTER)
		return Xmm(m_value);
	return defreg;
}

Gp drcbe_x64::be_parameter::select_register(Gp defreg, be_parameter const &checkparam) const
{
	if (*this == checkparam)
		return defreg;
	return select_register(defreg);
}

Gp drcbe_x64::be_parameter::select_register(Gp defreg, be_parameter const &checkparam, be_parameter const &checkparam2) const
{
	if (*this == checkparam || *this == checkparam2)
		return defreg;
	return select_register(defreg);
}

Xmm drcbe_x64::be_parameter::select_register(Xmm defreg, be_parameter const &checkparam) const
{
	if (*this == checkparam)
		return defreg;
	return select_register(defreg);
}

//-------------------------------------------------
//  select_register - select a register to use,
//  avoiding conflicts with the optional
//  checkparam
//-------------------------------------------------

inline void drcbe_x64::normalize_commutative(be_parameter &inner, be_parameter &outer)
{
	// if the inner parameter is a memory operand, push it to the outer
	if (inner.is_memory())
	{
		be_parameter temp = inner;
		inner = outer;
		outer = temp;
	}

	// if the inner parameter is an immediate, push it to the outer
	if (inner.is_immediate())
	{
		be_parameter temp = inner;
		inner = outer;
		outer = temp;
	}
}


//-------------------------------------------------
//  offset_from_rbp - return the verified offset
//  from rbp
//-------------------------------------------------

inline int32_t drcbe_x64::offset_from_rbp(const void *ptr) const
{
	const int64_t delta = reinterpret_cast<const uint8_t *>(ptr) - m_rbpvalue;
	if (int32_t(delta) != delta)
		throw emu_fatalerror("drcbe_x64::offset_from_rbp: delta out of range");
	return int32_t(delta);
}


//-------------------------------------------------
//  get_base_register_and_offset - determine right
//  base register and offset to access the given
//  target address
//-------------------------------------------------

inline Gp drcbe_x64::get_base_register_and_offset(Assembler &a, void *target, Gp const &reg, int32_t &offset)
{
	const int64_t delta = reinterpret_cast<uint8_t *>(target) - m_rbpvalue;
	if (short_immediate(delta))
	{
		offset = delta;
		return rbp;
	}
	else
	{
		offset = 0;
		mov_r64_imm(a, reg, uintptr_t(target));                                         // mov   reg,target
		return reg;
	}
}


//-------------------------------------------------
//  smart_call_r64 - generate a call either
//  directly or via a call through pointer
//-------------------------------------------------

inline void drcbe_x64::smart_call_r64(Assembler &a, x86code *target, Gp const &reg)
{
	const int64_t delta = target - (x86code *)(a.code()->baseAddress() + a.offset() + 5);
	if (short_immediate(delta))
		a.call(imm(target));                                                            // call  target
	else
	{
		mov_r64_imm(a, reg, uintptr_t(target));                                         // mov   reg,target
		a.call(reg);                                                                    // call  reg
	}
}


//-------------------------------------------------
//  smart_call_m64 - generate a call either
//  directly or via a call through pointer
//-------------------------------------------------

inline void drcbe_x64::smart_call_m64(Assembler &a, x86code **target)
{
	const int64_t delta = *target - (x86code *)(a.code()->baseAddress() + a.offset() + 5);
	if (short_immediate(delta))
		a.call(imm(*target));                                                           // call  *target
	else
		a.call(MABS(target));                                                           // call  [target]
}



//**************************************************************************
//  BACKEND CALLBACKS
//**************************************************************************

//-------------------------------------------------
//  drcbe_x64 - constructor
//-------------------------------------------------

drcbe_x64::drcbe_x64(drcuml_state &drcuml, device_t &device, drc_cache &cache, uint32_t flags, int modes, int addrbits, int ignorebits)
	: drcbe_interface(drcuml, cache, device)
	, m_hash(cache, modes, addrbits, ignorebits)
	, m_map(cache, 0xaaaaaaaa5555)
	, m_log(nullptr)
	, m_log_asmjit(nullptr)
	, m_absmask32((uint32_t *)cache.alloc_near(16*2 + 15))
	, m_absmask64(nullptr)
	, m_rbpvalue(cache.near() + 0x80)
	, m_entry(nullptr)
	, m_exit(nullptr)
	, m_nocode(nullptr)
	, m_near(*(near_state *)cache.alloc_near(sizeof(m_near)))
{
	// build up necessary arrays
	static const uint32_t sse_control[4] =
	{
		0xff80,     // ROUND_TRUNC
		0x9f80,     // ROUND_ROUND
		0xdf80,     // ROUND_CEIL
		0xbf80      // ROUND_FLOOR
	};
	memcpy(m_near.ssecontrol, sse_control, sizeof(m_near.ssecontrol));
	m_near.single1 = 1.0f;
	m_near.double1 = 1.0;

	// create absolute value masks that are aligned to SSE boundaries
	m_absmask32 = (uint32_t *)(((uintptr_t)m_absmask32 + 15) & ~15);
	m_absmask32[0] = m_absmask32[1] = m_absmask32[2] = m_absmask32[3] = 0x7fffffff;
	m_absmask64 = (uint64_t *)&m_absmask32[4];
	m_absmask64[0] = m_absmask64[1] = 0x7fffffffffffffffU;

	// get pointers to C functions we need to call
	using debugger_hook_func = void (*)(device_debug *, offs_t);
	static const debugger_hook_func debugger_inst_hook = [] (device_debug *dbg, offs_t pc) { dbg->instruction_hook(pc); }; // TODO: kill trampoline if possible
	m_near.debug_cpu_instruction_hook = (x86code *)debugger_inst_hook;
	if (LOG_HASHJMPS)
	{
		m_near.debug_log_hashjmp = (x86code *)debug_log_hashjmp;
		m_near.debug_log_hashjmp_fail = (x86code *)debug_log_hashjmp_fail;
	}
	m_near.drcmap_get_value = (x86code *)&drc_map_variables::static_get_value;

	// build the flags map
	for (int entry = 0; entry < std::size(m_near.flagsmap); entry++)
	{
		uint8_t flags = 0;
		if (entry & 0x001) flags |= FLAG_C;
		if (entry & 0x004) flags |= FLAG_U;
		if (entry & 0x040) flags |= FLAG_Z;
		if (entry & 0x080) flags |= FLAG_S;
		if (entry & 0x800) flags |= FLAG_V;
		m_near.flagsmap[entry] = flags;
	}
	for (int entry = 0; entry < std::size(m_near.flagsunmap); entry++)
	{
		uint64_t flags = 0;
		if (entry & FLAG_C) flags |= 0x001;
		if (entry & FLAG_U) flags |= 0x004;
		if (entry & FLAG_Z) flags |= 0x040;
		if (entry & FLAG_S) flags |= 0x080;
		if (entry & FLAG_V) flags |= 0x800;
		m_near.flagsunmap[entry] = flags;
	}

	// resolve the actual addresses of the address space handlers
	auto const resolve_accessor =
			[] (resolved_handler &handler, address_space &space, auto accessor)
			{
				if (MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_ITANIUM)
				{
					struct { uintptr_t ptr; ptrdiff_t adj; } equiv;
					assert(sizeof(accessor) == sizeof(equiv));
					*reinterpret_cast<decltype(accessor) *>(&equiv) = accessor;
					handler.obj = uintptr_t(reinterpret_cast<u8 *>(&space) + equiv.adj);
					if (BIT(equiv.ptr, 0))
					{
						auto const vptr = *reinterpret_cast<u8 const *const *>(handler.obj) + equiv.ptr - 1;
						handler.func = *reinterpret_cast<x86code *const *>(vptr);
					}
					else
					{
						handler.func = reinterpret_cast<x86code *>(equiv.ptr);
					}
				}
				else if (MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_MSVC)
				{
					// interpret the pointer to member function ignoring the virtual inheritance variant
					struct single { uintptr_t ptr; };
					struct multi { uintptr_t ptr; int adj; };
					struct { uintptr_t ptr; int adj; int vadj; int vindex; } unknown;
					assert(sizeof(accessor) <= sizeof(unknown));
					*reinterpret_cast<decltype(accessor) *>(&unknown) = accessor;
					handler.func = reinterpret_cast<x86code *>(unknown.ptr);
					handler.obj = uintptr_t(&space);
					if ((sizeof(unknown) == sizeof(accessor)) && unknown.vindex)
					{
						handler.obj += unknown.vadj;
						auto const vptr = *reinterpret_cast<std::uint8_t const *const *>(handler.obj);
						handler.obj += *reinterpret_cast<int const *>(vptr + unknown.vindex);
					}
					if (sizeof(single) < sizeof(accessor))
						handler.obj += unknown.adj;

					// walk past thunks
					while (true)
					{
						if (0xe9 == handler.func[0])
						{
							// absolute jump with 32-bit displacement
							handler.func += 5 + *reinterpret_cast<s32 const *>(handler.func + 1);
						}
						else if ((0x48 == handler.func[0]) && (0x8b == handler.func[1]) && (0x01 == handler.func[2]) && (0xff == handler.func[3]) && ((0x60 == handler.func[4]) || (0xa0 == handler.func[4])))
						{
							// virtual function call thunk
							auto const vptr = *reinterpret_cast<std::uint8_t const *const *>(handler.obj);
							if (0x60 == handler.func[4])
								handler.func = *reinterpret_cast<x86code *const *>(vptr + *reinterpret_cast<s8 const *>(handler.func + 5));
							else
								handler.func = *reinterpret_cast<x86code *const *>(vptr + *reinterpret_cast<s32 const *>(handler.func + 5));
						}
						else
						{
							// not something we can easily bypass
							break;
						}
					}
				}
			};
	m_resolved_accessors.resize(m_space.size());
	for (int space = 0; m_space.size() > space; ++space)
	{
		if (m_space[space])
		{
			resolve_accessor(m_resolved_accessors[space].read_byte,         *m_space[space], static_cast<u8  (address_space::*)(offs_t)     >(&address_space::read_byte));
			resolve_accessor(m_resolved_accessors[space].read_word,         *m_space[space], static_cast<u16 (address_space::*)(offs_t)     >(&address_space::read_word));
			resolve_accessor(m_resolved_accessors[space].read_word_masked,  *m_space[space], static_cast<u16 (address_space::*)(offs_t, u16)>(&address_space::read_word));
			resolve_accessor(m_resolved_accessors[space].read_dword,        *m_space[space], static_cast<u32 (address_space::*)(offs_t)     >(&address_space::read_dword));
			resolve_accessor(m_resolved_accessors[space].read_dword_masked, *m_space[space], static_cast<u32 (address_space::*)(offs_t, u32)>(&address_space::read_dword));
			resolve_accessor(m_resolved_accessors[space].read_qword,        *m_space[space], static_cast<u64 (address_space::*)(offs_t)     >(&address_space::read_qword));
			resolve_accessor(m_resolved_accessors[space].read_qword_masked, *m_space[space], static_cast<u64 (address_space::*)(offs_t, u64)>(&address_space::read_qword));

			resolve_accessor(m_resolved_accessors[space].write_byte,         *m_space[space], static_cast<void (address_space::*)(offs_t, u8)      >(&address_space::write_byte));
			resolve_accessor(m_resolved_accessors[space].write_word,         *m_space[space], static_cast<void (address_space::*)(offs_t, u16)     >(&address_space::write_word));
			resolve_accessor(m_resolved_accessors[space].write_word_masked,  *m_space[space], static_cast<void (address_space::*)(offs_t, u16, u16)>(&address_space::write_word));
			resolve_accessor(m_resolved_accessors[space].write_dword,        *m_space[space], static_cast<void (address_space::*)(offs_t, u32)     >(&address_space::write_dword));
			resolve_accessor(m_resolved_accessors[space].write_dword_masked, *m_space[space], static_cast<void (address_space::*)(offs_t, u32, u32)>(&address_space::write_dword));
			resolve_accessor(m_resolved_accessors[space].write_qword,        *m_space[space], static_cast<void (address_space::*)(offs_t, u64)     >(&address_space::write_qword));
			resolve_accessor(m_resolved_accessors[space].write_qword_masked, *m_space[space], static_cast<void (address_space::*)(offs_t, u64, u64)>(&address_space::write_qword));
		}
	}

	// build the opcode table (static but it doesn't hurt to regenerate it)
	for (auto & elem : s_opcode_table_source)
		s_opcode_table[elem.opcode] = elem.func;

	// create the log
	if (device.machine().options().drc_log_native())
	{
		std::string filename = std::string("drcbex64_").append(device.shortname()).append(".asm");
		m_log = x86log_create_context(filename.c_str());
		m_log_asmjit = fopen(std::string("drcbex64_asmjit_").append(device.shortname()).append(".asm").c_str(), "w");
	}
}


//-------------------------------------------------
//  ~drcbe_x64 - destructor
//-------------------------------------------------

drcbe_x64::~drcbe_x64()
{
	// free the log context
	if (m_log != nullptr)
		x86log_free_context(m_log);

	if (m_log_asmjit)
		fclose(m_log_asmjit);
}

size_t drcbe_x64::emit(CodeHolder &ch)
{
	Error err;

	// the following three calls aren't currently required, but may be if
	// other asmjist features are used in future
	if (false)
	{
		err = ch.flatten();
		if (err)
			throw emu_fatalerror("asmjit::CodeHolder::flatten() error %d", err);

		err = ch.resolveUnresolvedLinks();
		if (err)
			throw emu_fatalerror("asmjit::CodeHolder::resolveUnresolvedLinks() error %d", err);

		err = ch.relocateToBase(ch.baseAddress());
		if (err)
			throw emu_fatalerror("asmjit::CodeHolder::relocateToBase() error %d", err);
	}

	size_t const alignment = ch.baseAddress() - uint64_t(m_cache.top());
	size_t const code_size = ch.codeSize();

	// test if enough room remains in drc cache
	drccodeptr *cachetop = m_cache.begin_codegen(alignment + code_size);
	if (cachetop == nullptr)
		return 0;

	err = ch.copyFlattenedData(drccodeptr(ch.baseAddress()), code_size, CopySectionFlags::kPadTargetBuffer);
	if (err)
		throw emu_fatalerror("asmjit::CodeHolder::copyFlattenedData() error %d", err);

	// update the drc cache and end codegen
	*cachetop += alignment + code_size;
	m_cache.end_codegen();

	return code_size;
}

//-------------------------------------------------
//  reset - reset back-end specific state
//-------------------------------------------------

void drcbe_x64::reset()
{
	// output a note to the log
	if (m_log != nullptr)
		x86log_printf(m_log, "%s", "\n\n===========\nCACHE RESET\n===========\n\n");

	// generate a little bit of glue code to set up the environment
	x86code *dst = (x86code *)m_cache.top();

	CodeHolder ch;
	ch.init(Environment::host(), uint64_t(dst));

	FileLogger logger(m_log_asmjit);
	if (logger.file())
	{
		logger.setFlags(FormatFlags::kHexOffsets | FormatFlags::kHexImms | FormatFlags::kMachineCode);
		logger.setIndentation(FormatIndentationGroup::kCode, 4);
		ch.setLogger(&logger);
	}

	Assembler a(&ch);
	if (logger.file())
		a.addDiagnosticOptions(DiagnosticOptions::kValidateIntermediate);

	// generate an entry point
	m_entry = (x86_entry_point_func)dst;
	a.bind(a.newNamedLabel("entry_point"));

	FuncDetail entry_point;
	entry_point.init(FuncSignatureT<uint32_t, uint8_t *, x86code *>(CallConvId::kHost), Environment::host());

	FuncFrame frame;
	frame.init(entry_point);
	frame.addDirtyRegs(rbx, rbp, rsi, rdi, r12, r13, r14, r15);
	FuncArgsAssignment args(&entry_point);
	args.assignAll(rbp);
	args.updateFuncFrame(frame);
	frame.finalize();

	a.emitProlog(frame);
	a.emitArgsAssignment(frame, args);

	a.sub(rsp, 32);
	a.mov(MABS(&m_near.hashstacksave), rsp);
	a.sub(rsp, 8);
	a.mov(MABS(&m_near.stacksave), rsp);
	a.stmxcsr(MABS(&m_near.ssemode));
	a.jmp(Gpq(REG_PARAM2));

	// generate an exit point
	m_exit = dst + a.offset();
	a.bind(a.newNamedLabel("exit_point"));
	a.ldmxcsr(MABS(&m_near.ssemode));
	a.mov(rsp, MABS(&m_near.hashstacksave));
	a.add(rsp, 32);
	a.emitEpilog(frame);

	// generate a no code point
	m_nocode = dst + a.offset();
	a.bind(a.newNamedLabel("nocode_point"));
	a.ret();

	// emit the generated code
	size_t bytes = emit(ch);

	if (m_log != nullptr)
	{
		x86log_disasm_code_range(m_log, "entry_point", dst, m_exit);
		x86log_disasm_code_range(m_log, "exit_point", m_exit, m_nocode);
		x86log_disasm_code_range(m_log, "nocode_point", m_nocode, dst + bytes);
	}

	// reset our hash tables
	m_hash.reset();
	m_hash.set_default_codeptr(m_nocode);
}


//-------------------------------------------------
//  execute - execute a block of code referenced
//  by the given handle
//-------------------------------------------------

int drcbe_x64::execute(code_handle &entry)
{
	// call our entry point which will jump to the destination
	m_cache.codegen_complete();
	return (*m_entry)(m_rbpvalue, (x86code *)entry.codeptr());
}


//-------------------------------------------------
//  drcbex64_generate - generate code
//-------------------------------------------------

void drcbe_x64::generate(drcuml_block &block, const instruction *instlist, uint32_t numinst)
{
	// tell all of our utility objects that a block is beginning
	m_hash.block_begin(block, instlist, numinst);
	m_map.block_begin(block);

	// compute the base by aligning the cache top to a cache line (assumed to be 64 bytes)
	x86code *dst = (x86code *)(uint64_t(m_cache.top() + 63) & ~63);

	CodeHolder ch;
	ch.init(Environment::host(), uint64_t(dst));
	ThrowableErrorHandler e;
	ch.setErrorHandler(&e);

	FileLogger logger(m_log_asmjit);
	if (logger.file())
	{
		logger.setFlags(FormatFlags::kHexOffsets | FormatFlags::kHexImms | FormatFlags::kMachineCode);
		logger.setIndentation(FormatIndentationGroup::kCode, 4);
		ch.setLogger(&logger);
	}

	Assembler a(&ch);
	if (logger.file())
		a.addDiagnosticOptions(DiagnosticOptions::kValidateIntermediate);

	// generate code
	std::string blockname;
	for (int inum = 0; inum < numinst; inum++)
	{
		const instruction &inst = instlist[inum];
		assert(inst.opcode() < std::size(s_opcode_table));

		// must remain in scope until output
		std::string dasm;

		// add a comment
		if (m_log != nullptr)
		{
			dasm = inst.disasm(&m_drcuml);

			x86log_add_comment(m_log, dst + a.offset(), "%s", dasm.c_str());
			a.setInlineComment(dasm.c_str());
		}

		// extract a blockname
		if (blockname.empty())
		{
			if (inst.opcode() == OP_HANDLE)
				blockname = inst.param(0).handle().string();
			else if (inst.opcode() == OP_HASH)
				blockname = string_format("Code: mode=%d PC=%08X", (uint32_t)inst.param(0).immediate(), (offs_t)inst.param(1).immediate());
		}

		// generate code
		(this->*s_opcode_table[inst.opcode()])(a, inst);
	}

	// emit the generated code
	size_t const bytes = emit(ch);
	if (!bytes)
		block.abort();

	// log it
	if (m_log != nullptr)
		x86log_disasm_code_range(m_log, (blockname.empty()) ? "Unknown block" : blockname.c_str(), dst, dst + bytes);

	// tell all of our utility objects that the block is finished
	m_hash.block_end(block);
	m_map.block_end(block);
}


//-------------------------------------------------
//  hash_exists - return true if the given mode/pc
//  exists in the hash table
//-------------------------------------------------

bool drcbe_x64::hash_exists(uint32_t mode, uint32_t pc)
{
	return m_hash.code_exists(mode, pc);
}


//-------------------------------------------------
//  get_info - return information about the
//  back-end implementation
//-------------------------------------------------

void drcbe_x64::get_info(drcbe_info &info)
{
	for (info.direct_iregs = 0; info.direct_iregs < REG_I_COUNT; info.direct_iregs++)
		if (int_register_map[info.direct_iregs] == 0)
			break;
	for (info.direct_fregs = 0; info.direct_fregs < REG_F_COUNT; info.direct_fregs++)
		if (float_register_map[info.direct_fregs] == 0)
			break;
}

void drcbe_x64::alu_op_param(Assembler &a, Inst::Id const opcode, Operand const &dst, be_parameter const &param, std::function<bool(Assembler &a, Operand const &dst, be_parameter const &src)> optimize)
{
	bool const is64 = dst.size() == 8;

	if (param.is_immediate())
	{
		if (!optimize(a, dst, param))
		{
			if (is64 && !short_immediate(param.immediate()))
			{
				// use scratch register for 64-bit immediate
				a.mov(r11, param.immediate());                                          // mov   r11,param
				a.emit(opcode, dst, r11);                                               // op    dst,r11
			}
			else
				a.emit(opcode, dst, param.immediate());                                 // op    dst,param
		}
	}
	else if (param.is_memory())
	{
		if (dst.isMem())
		{
			// use temporary register for memory,memory
			Gp const tmp = is64 ? param.select_register(rax) : param.select_register(eax);

			a.mov(tmp, MABS(param.memory()));                                           // mov   tmp,param
			a.emit(opcode, dst, tmp);                                                   // op    [dst],tmp
		}
		else if (opcode != Inst::kIdTest)
			// most instructions are register,memory
			a.emit(opcode, dst, MABS(param.memory()));                                  // op    dst,[param]
		else
			// test instruction requires memory,register
			a.emit(opcode, MABS(param.memory()), dst);                                  // op    [param],dst
	}
	else if (param.is_int_register())
	{
		Gp const src = Gp::fromTypeAndId(is64 ? RegType::kX86_Gpq : RegType::kX86_Gpd, param.ireg());

		a.emit(opcode, dst, src);                                                       // op    dst,param
	}
}

void drcbe_x64::shift_op_param(Assembler &a, Inst::Id const opcode, Operand const &dst, be_parameter const &param)
{
	Operand shift = cl;
	if (param.is_immediate())
		shift = imm(param.immediate());
	else
		mov_reg_param(a, ecx, param);

	a.emit(opcode, dst, shift);
}

void drcbe_x64::mov_reg_param(Assembler &a, Gp const &reg, be_parameter const &param, bool const keepflags)
{
	if (param.is_immediate())
	{
		if (param.immediate() == 0 && !keepflags)
			a.xor_(reg.r32(), reg.r32());                                               // xor   reg,reg
		else if (reg.isGpq())
			mov_r64_imm(a, reg, param.immediate());
		else
			a.mov(reg, param.immediate());                                              // mov   reg,param
	}
	else if (param.is_memory())
		a.mov(reg, MABS(param.memory()));                                               // mov   reg,[param]
	else if (param.is_int_register())
	{
		if (reg.id() != param.ireg())
			a.mov(reg, Gp(reg, param.ireg()));                                          // mov   reg,param
	}
}

void drcbe_x64::mov_param_reg(Assembler &a, be_parameter const &param, Gp const &reg)
{
	assert(!param.is_immediate());

	if (param.is_memory())
		a.mov(MABS(param.memory()), reg);                                               // mov   [param],reg
	else if (param.is_int_register())
	{
		if (reg.id() != param.ireg())
			a.mov(Gp(reg, param.ireg()), reg);                                          // mov   param,reg
	}
}

void drcbe_x64::mov_mem_param(Assembler &a, Mem const &mem, be_parameter const &param)
{
	bool const is64 = mem.size() == 8;

	if (param.is_immediate())
	{
		if (is64 && !short_immediate(param.immediate()))
		{
			a.mov(rax, param.immediate());                                              // mov   tmp,param
			a.mov(mem, rax);                                                            // mov   [mem],tmp
		}
		else
			a.mov(mem, param.immediate());                                              // mov   [mem],param
	}
	else if (param.is_memory())
	{
		Gp const tmp = Gp::fromTypeAndId(is64 ? RegType::kX86_Gpq : RegType::kX86_Gpd, Gp::kIdAx);

		a.mov(tmp, MABS(param.memory()));                                               // mov   tmp,[param]
		a.mov(mem, tmp);                                                                // mov   [mem],tmp
	}
	else if (param.is_int_register())
	{
		Gp const src = Gp::fromTypeAndId(is64 ? RegType::kX86_Gpq : RegType::kX86_Gpd, param.ireg());

		a.mov(mem, src);                                                                // mov   [mem],param
	}
}

void drcbe_x64::movsx_r64_p32(Assembler &a, Gp const &reg, be_parameter const &param)
{
	if (param.is_immediate())
	{
		if (param.immediate() == 0)
			a.xor_(reg.r32(), reg.r32());                                               // xor   reg,reg
		else if ((int32_t)param.immediate() >= 0)
			a.mov(reg.r32(), param.immediate());                                        // mov   reg,param
		else
			mov_r64_imm(a, reg, int32_t(param.immediate()));                            // mov   reg,param
	}
	else if (param.is_memory())
		a.movsxd(reg, MABS(param.memory()));                                            // movsxd reg,[param]
	else if (param.is_int_register())
		a.movsxd(reg, Gpd(param.ireg()));                                               // movsxd reg,param
}

void drcbe_x64::mov_r64_imm(Assembler &a, Gp const &reg, uint64_t const imm)
{
	if (u32(imm) == imm)
		a.mov(reg.r32(), imm);
	else if (s32(imm) == imm)
		a.mov(reg.r64(), s32(imm));
	else
		a.mov(reg.r64(), imm);
}


/***************************************************************************
    EMITTERS FOR FLOATING POINT OPERATIONS WITH PARAMETERS
***************************************************************************/

//-------------------------------------------------
//  movss_r128_p32 - move a 32-bit parameter
//  into a register
//-------------------------------------------------

void drcbe_x64::movss_r128_p32(Assembler &a, Xmm const &reg, be_parameter const &param)
{
	assert(!param.is_immediate());
	if (param.is_memory())
		a.movss(reg, MABS(param.memory(), 4));                                          // movss reg,[param]
	else if (param.is_float_register())
	{
		if (reg.id() != param.freg())
			a.movss(reg, Xmm(param.freg()));                                            // movss reg,param
	}
}


//-------------------------------------------------
//  movss_p32_r128 - move a register into a
//  32-bit parameter
//-------------------------------------------------

void drcbe_x64::movss_p32_r128(Assembler &a, be_parameter const &param, Xmm const &reg)
{
	assert(!param.is_immediate());
	if (param.is_memory())
		a.movss(MABS(param.memory(), 4), reg);                                          // movss [param],reg
	else if (param.is_float_register())
	{
		if (reg.id() != param.freg())
			a.movss(Xmm(param.freg()), reg);                                            // movss param,reg
	}
}


//-------------------------------------------------
//  movsd_r128_p64 - move a 64-bit parameter
//  into a register
//-------------------------------------------------

void drcbe_x64::movsd_r128_p64(Assembler &a, Xmm const &reg, be_parameter const &param)
{
	assert(!param.is_immediate());
	if (param.is_memory())
		a.movsd(reg, MABS(param.memory(), 8));                                          // movsd reg,[param]
	else if (param.is_float_register())
	{
		if (reg.id() != param.freg())
			a.movsd(reg, Xmm(param.freg()));                                            // movsd reg,param
	}
}


//-------------------------------------------------
//  movsd_p64_r128 - move a register into a
//  64-bit parameter
//-------------------------------------------------

void drcbe_x64::movsd_p64_r128(Assembler &a, be_parameter const &param, Xmm const &reg)
{
	assert(!param.is_immediate());
	if (param.is_memory())
		a.movsd(MABS(param.memory(), 8), reg);                                          // movsd [param],reg
	else if (param.is_float_register())
	{
		if (reg.id() != param.freg())
			a.movsd(Xmm(param.freg()), reg);                                            // movsd param,reg
	}
}


//**************************************************************************
//  DEBUG HELPERS
//**************************************************************************

//-------------------------------------------------
//  debug_log_hashjmp - callback to handle
//  logging of hashjmps
//-------------------------------------------------

void drcbe_x64::debug_log_hashjmp(offs_t pc, int mode)
{
	printf("mode=%d PC=%08X\n", mode, pc);
}


//-------------------------------------------------
//  debug_log_hashjmp - callback to handle
//  logging of hashjmps
//-------------------------------------------------

void drcbe_x64::debug_log_hashjmp_fail()
{
	printf("  (FAIL)\n");
}


/***************************************************************************
    COMPILE-TIME OPCODES
***************************************************************************/

//-------------------------------------------------
//  op_handle - process a HANDLE opcode
//-------------------------------------------------

void drcbe_x64::op_handle(Assembler &a, const instruction &inst)
{
	assert_no_condition(inst);
	assert_no_flags(inst);
	assert(inst.numparams() == 1);
	assert(inst.param(0).is_code_handle());

	// make a label for documentation
	Label handle = a.newNamedLabel(inst.param(0).handle().string());
	a.bind(handle);

	// emit a jump around the stack adjust in case code falls through here
	Label skip = a.newLabel();
	a.short_().jmp(skip);                                                               // jmp   skip

	// register the current pointer for the handle
	inst.param(0).handle().set_codeptr(drccodeptr(a.code()->baseAddress() + a.offset()));

	// by default, the handle points to prolog code that moves the stack pointer
	a.lea(rsp, ptr(rsp, -40));                                                          // lea   rsp,[rsp-40]
	a.bind(skip);                                                                   // skip:
}


//-------------------------------------------------
//  op_hash - process a HASH opcode
//-------------------------------------------------

void drcbe_x64::op_hash(Assembler &a, const instruction &inst)
{
	assert_no_condition(inst);
	assert_no_flags(inst);
	assert(inst.numparams() == 2);
	assert(inst.param(0).is_immediate());
	assert(inst.param(1).is_immediate());

	// register the current pointer for the mode/PC
	m_hash.set_codeptr(inst.param(0).immediate(), inst.param(1).immediate(), drccodeptr(a.code()->baseAddress() + a.offset()));
}


//-------------------------------------------------
//  op_label - process a LABEL opcode
//-------------------------------------------------

void drcbe_x64::op_label(Assembler &a, const instruction &inst)
{
	assert_no_condition(inst);
	assert_no_flags(inst);
	assert(inst.numparams() == 1);
	assert(inst.param(0).is_code_label());

	std::string labelName = util::string_format("PC$%x", inst.param(0).label());
	Label label = a.labelByName(labelName.c_str());
	if (!label.isValid())
		label = a.newNamedLabel(labelName.c_str());

	// register the current pointer for the label
	a.bind(label);
}


//-------------------------------------------------
//  op_comment - process a COMMENT opcode
//-------------------------------------------------

void drcbe_x64::op_comment(Assembler &a, const instruction &inst)
{
	assert_no_condition(inst);
	assert_no_flags(inst);
	assert(inst.numparams() == 1);
	assert(inst.param(0).is_string());

	// do nothing
}


//-------------------------------------------------
//  op_mapvar - process a MAPVAR opcode
//-------------------------------------------------

void drcbe_x64::op_mapvar(Assembler &a, const instruction &inst)
{
	assert_no_condition(inst);
	assert_no_flags(inst);
	assert(inst.numparams() == 2);
	assert(inst.param(0).is_mapvar());
	assert(inst.param(1).is_immediate());

	// set the value of the specified mapvar
	m_map.set_value(drccodeptr(a.code()->baseAddress() + a.offset()), inst.param(0).mapvar(), inst.param(1).immediate());
}



/***************************************************************************
    CONTROL FLOW OPCODES
***************************************************************************/

//-------------------------------------------------
//  op_nop - process a NOP opcode
//-------------------------------------------------

void drcbe_x64::op_nop(Assembler &a, const instruction &inst)
{
	// nothing
}


//-------------------------------------------------
//  op_debug - process a DEBUG opcode
//-------------------------------------------------

void drcbe_x64::op_debug(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	if ((m_device.machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
	{
		// normalize parameters
		be_parameter pcp(*this, inst.param(0), PTYPE_MRI);

		// test and branch
		mov_r64_imm(a, rax, (uintptr_t)&m_device.machine().debug_flags);                // mov   rax,&debug_flags
		a.test(dword_ptr(rax), DEBUG_FLAG_CALL_HOOK);                                   // test  [debug_flags],DEBUG_FLAG_CALL_HOOK
		Label skip = a.newLabel();
		a.short_().jz(skip);

		// push the parameter
		mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_device.debug());                   // mov   param1,device.debug
		mov_reg_param(a, Gpd(REG_PARAM2), pcp);                                         // mov   param2,pcp
		smart_call_m64(a, &m_near.debug_cpu_instruction_hook);                          // call  debug_cpu_instruction_hook

		a.bind(skip);
	}
}


//-------------------------------------------------
//  op_exit - process an EXIT opcode
//-------------------------------------------------

void drcbe_x64::op_exit(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter retp(*this, inst.param(0), PTYPE_MRI);

	// load the parameter into EAX
	mov_reg_param(a, eax, retp);                                                        // mov   eax,retp
	if (inst.condition() == uml::COND_ALWAYS)
		a.jmp(imm(m_exit));                                                             // jmp   exit
	else
		a.j(X86_CONDITION(inst.condition()), imm(m_exit));                              // jcc   exit
}


//-------------------------------------------------
//  op_hashjmp - process a HASHJMP opcode
//-------------------------------------------------

void drcbe_x64::op_hashjmp(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter modep(*this, inst.param(0), PTYPE_MRI);
	be_parameter pcp(*this, inst.param(1), PTYPE_MRI);
	const parameter &exp = inst.param(2);
	assert(exp.is_code_handle());

	if (LOG_HASHJMPS)
	{
		mov_reg_param(a, Gpd(REG_PARAM1), pcp);
		mov_reg_param(a, Gpd(REG_PARAM2), modep);
		smart_call_m64(a, &m_near.debug_log_hashjmp);
	}

	// load the stack base one word early so we end up at the right spot after our call below
	a.mov(rsp, MABS(&m_near.hashstacksave));                                            // mov   rsp,[hashstacksave]

	// fixed mode cases
	if (modep.is_immediate() && m_hash.is_mode_populated(modep.immediate()))
	{
		// a straight immediate jump is direct, though we need the PC in EAX in case of failure
		if (pcp.is_immediate())
		{
			uint32_t l1val = (pcp.immediate() >> m_hash.l1shift()) & m_hash.l1mask();
			uint32_t l2val = (pcp.immediate() >> m_hash.l2shift()) & m_hash.l2mask();
			a.call(MABS(&m_hash.base()[modep.immediate()][l1val][l2val]));              // call  hash[modep][l1val][l2val]
		}

		// a fixed mode but variable PC
		else
		{
			mov_reg_param(a, eax, pcp);                                                 // mov   eax,pcp
			a.mov(edx, eax);                                                            // mov   edx,eax
			a.shr(edx, m_hash.l1shift());                                               // shr   edx,l1shift
			a.and_(eax, m_hash.l2mask() << m_hash.l2shift());                           // and  eax,l2mask << l2shift
			a.mov(rdx, ptr(rbp, rdx, 3, offset_from_rbp(&m_hash.base()[modep.immediate()][0])));
																						// mov   rdx,hash[modep+edx*8]
			a.call(ptr(rdx, rax, 3 - m_hash.l2shift()));                                // call  [rdx+rax*shift]
		}
	}
	else
	{
		// variable mode
		Gp modereg = modep.select_register(ecx);
		mov_reg_param(a, modereg, modep);                                               // mov   modereg,modep
		a.mov(rcx, ptr(rbp, modereg, 3, offset_from_rbp(m_hash.base())));               // mov   rcx,hash[modereg*8]

		// fixed PC
		if (pcp.is_immediate())
		{
			uint32_t l1val = (pcp.immediate() >> m_hash.l1shift()) & m_hash.l1mask();
			uint32_t l2val = (pcp.immediate() >> m_hash.l2shift()) & m_hash.l2mask();
			a.mov(rdx, ptr(rcx, l1val * 8));                                            // mov   rdx,[rcx+l1val*8]
			a.call(ptr(rdx, l2val * 8));                                                // call  [l2val*8]
		}

		// variable PC
		else
		{
			mov_reg_param(a, eax, pcp);                                                 // mov   eax,pcp
			a.mov(edx, eax);                                                            // mov   edx,eax
			a.shr(edx, m_hash.l1shift());                                               // shr   edx,l1shift
			a.mov(rdx, ptr(rcx, rdx, 3));                                               // mov   rdx,[rcx+rdx*8]
			a.and_(eax, m_hash.l2mask() << m_hash.l2shift());                           // and   eax,l2mask << l2shift
			a.call(ptr(rdx, rax, 3 - m_hash.l2shift()));                                // call  [rdx+rax*shift]
		}
	}

	// in all cases, if there is no code, we return here to generate the exception
	if (LOG_HASHJMPS)
		smart_call_m64(a, &m_near.debug_log_hashjmp_fail);

	mov_mem_param(a, MABS(&m_state.exp, 4), pcp);                                       // mov   [exp],param
	a.sub(rsp, 8);                                                                      // sub   rsp,8
	a.call(MABS(exp.handle().codeptr_addr()));                                          // call  [exp]
}


//-------------------------------------------------
//  op_jmp - process a JMP opcode
//-------------------------------------------------

void drcbe_x64::op_jmp(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	const parameter &labelp = inst.param(0);
	assert(labelp.is_code_label());

	std::string labelName = util::string_format("PC$%x", labelp.label());
	Label jmptarget = a.labelByName(labelName.c_str());
	if (!jmptarget.isValid())
		jmptarget = a.newNamedLabel(labelName.c_str());

	if (inst.condition() == uml::COND_ALWAYS)
		a.jmp(jmptarget);                                                               // jmp   target
	else
		a.j(X86_CONDITION(inst.condition()), jmptarget);                                // jcc   target
}


//-------------------------------------------------
//  op_exh - process an EXH opcode
//-------------------------------------------------

void drcbe_x64::op_exh(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	const parameter &handp = inst.param(0);
	assert(handp.is_code_handle());
	be_parameter exp(*this, inst.param(1), PTYPE_MRI);

	// look up the handle target
	drccodeptr *targetptr = handp.handle().codeptr_addr();

	// perform the exception processing
	Label no_exception = a.newLabel();
	if (inst.condition() != uml::COND_ALWAYS)
		a.short_().j(X86_NOT_CONDITION(inst.condition()), no_exception);                // jcc   no_exception
	mov_mem_param(a, MABS(&m_state.exp, 4), exp);                                       // mov   [exp],exp
	if (*targetptr != nullptr)
		a.call(imm(*targetptr));                                                        // call  *targetptr
	else
		a.call(MABS(targetptr));                                                        // call  [targetptr]
	if (inst.condition() != uml::COND_ALWAYS)
		a.bind(no_exception);
}


//-------------------------------------------------
//  op_callh - process a CALLH opcode
//-------------------------------------------------

void drcbe_x64::op_callh(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	const parameter &handp = inst.param(0);
	assert(handp.is_code_handle());

	// look up the handle target
	drccodeptr *targetptr = handp.handle().codeptr_addr();

	// skip if conditional
	Label skip = a.newLabel();
	if (inst.condition() != uml::COND_ALWAYS)
		a.short_().j(X86_NOT_CONDITION(inst.condition()), skip);                        // jcc   skip

	// jump through the handle; directly if a normal jump
	if (*targetptr != nullptr)
		a.call(imm(*targetptr));                                                        // call  *targetptr
	else
		a.call(MABS(targetptr));                                                        // call  [targetptr]

	// resolve the conditional link
	if (inst.condition() != uml::COND_ALWAYS)
		a.bind(skip);                                                               // skip:
}


//-------------------------------------------------
//  op_ret - process a RET opcode
//-------------------------------------------------

void drcbe_x64::op_ret(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_any_condition(inst);
	assert_no_flags(inst);
	assert(inst.numparams() == 0);

	// skip if conditional
	Label skip = a.newLabel();
	if (inst.condition() != uml::COND_ALWAYS)
		a.short_().j(X86_NOT_CONDITION(inst.condition()), skip);                        // jcc   skip

	// return
	a.lea(rsp, ptr(rsp, 40));                                                           // lea   rsp,[rsp+40]
	a.ret();                                                                            // ret

	// resolve the conditional link
	if (inst.condition() != uml::COND_ALWAYS)
		a.bind(skip);                                                               // skip:
}


//-------------------------------------------------
//  op_callc - process a CALLC opcode
//-------------------------------------------------

void drcbe_x64::op_callc(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	const parameter &funcp = inst.param(0);
	assert(funcp.is_c_function());
	be_parameter paramp(*this, inst.param(1), PTYPE_M);

	// skip if conditional
	Label skip = a.newLabel();
	if (inst.condition() != uml::COND_ALWAYS)
		a.short_().j(X86_NOT_CONDITION(inst.condition()), skip);                        // jcc   skip

	// perform the call
	mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)paramp.memory());                        // mov   param1,paramp
	smart_call_r64(a, (x86code *)(uintptr_t)funcp.cfunc(), rax);                        // call  funcp

	// resolve the conditional link
	if (inst.condition() != uml::COND_ALWAYS)
		a.bind(skip);                                                               // skip:
}


//-------------------------------------------------
//  op_recover - process a RECOVER opcode
//-------------------------------------------------

void drcbe_x64::op_recover(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);

	// call the recovery code
	a.mov(rax, MABS(&m_near.stacksave));                                                // mov   rax,stacksave
	a.mov(rax, ptr(rax, -8));                                                           // mov   rax,[rax-8]
	mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)&m_map);                                 // mov   param1,m_map
	a.lea(Gpq(REG_PARAM2), ptr(rax, -1));                                               // lea   param2,[rax-1]
	mov_r64_imm(a, Gpq(REG_PARAM3), inst.param(1).mapvar());                            // mov   param3,param[1].value
	smart_call_m64(a, &m_near.drcmap_get_value);                                        // call  drcmap_get_value
	mov_param_reg(a, dstp, eax);                                                        // mov   dstp,eax
}



/***************************************************************************
    INTERNAL REGISTER OPCODES
***************************************************************************/

//-------------------------------------------------
//  op_setfmod - process a SETFMOD opcode
//-------------------------------------------------

void drcbe_x64::op_setfmod(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter srcp(*this, inst.param(0), PTYPE_MRI);

	// immediate case
	if (srcp.is_immediate())
	{
		int value = srcp.immediate() & 3;
		a.mov(MABS(&m_state.fmod), value);                                              // mov   [fmod],srcp
		a.ldmxcsr(MABS(&m_near.ssecontrol[value]));                                     // ldmxcsr fp_control[srcp]
	}

	// register/memory case
	else
	{
		mov_reg_param(a, eax, srcp);                                                    // mov   eax,srcp
		a.and_(eax, 3);                                                                 // and   eax,3
		a.mov(MABS(&m_state.fmod), al);                                                 // mov   [fmod],al
		a.ldmxcsr(ptr(rbp, rax, 2, offset_from_rbp(&m_near.ssecontrol[0])));            // ldmxcsr fp_control[eax]
	}
}


//-------------------------------------------------
//  op_getfmod - process a GETFMOD opcode
//-------------------------------------------------

void drcbe_x64::op_getfmod(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);

	Mem fmod = MABS(&m_state.fmod);
	fmod.setSize(1);

	// fetch the current mode and store to the destination
	if (dstp.is_int_register())
		a.movzx(Gpd(dstp.ireg()), fmod);                                                // movzx reg,[fmod]
	else
	{
		a.movzx(eax, fmod);                                                             // movzx eax,[fmod]
		a.mov(MABS(dstp.memory()), eax);                                                // mov   [dstp],eax
	}
}


//-------------------------------------------------
//  op_getexp - process a GETEXP opcode
//-------------------------------------------------

void drcbe_x64::op_getexp(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);

	// fetch the exception parameter and store to the destination
	if (dstp.is_int_register())
		a.mov(Gpd(dstp.ireg()), MABS(&m_state.exp));                                    // mov   reg,[exp]
	else
	{
		a.mov(eax, MABS(&m_state.exp));                                                 // mov   eax,[exp]
		a.mov(MABS(dstp.memory()), eax);                                                // mov   [dstp],eax
	}
}


//-------------------------------------------------
//  op_getflgs - process a GETFLGS opcode
//-------------------------------------------------

void drcbe_x64::op_getflgs(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter maskp(*this, inst.param(1), PTYPE_I);

	// pick a target register for the general case
	Gp dstreg = dstp.select_register(eax);

	// compute mask for flags
	uint32_t flagmask = 0;
	if (maskp.immediate() & FLAG_C) flagmask |= 0x001;
	if (maskp.immediate() & FLAG_V) flagmask |= 0x800;
	if (maskp.immediate() & FLAG_Z) flagmask |= 0x040;
	if (maskp.immediate() & FLAG_S) flagmask |= 0x080;
	if (maskp.immediate() & FLAG_U) flagmask |= 0x004;

	switch (maskp.immediate())
	{
		// single flags only
		case FLAG_C:
			a.setc(al);                                                                 // setc   al
			a.movzx(dstreg, al);                                                        // movzx  dstreg,al
			break;

		case FLAG_V:
			a.seto(al);                                                                 // seto   al
			a.movzx(dstreg, al);                                                        // movzx  dstreg,al
			a.shl(dstreg, 1);                                                           // shl    dstreg,1
			break;

		case FLAG_Z:
			a.setz(al);                                                                 // setz   al
			a.movzx(dstreg, al);                                                        // movzx  dstreg,al
			a.shl(dstreg, 2);                                                           // shl    dstreg,2
			break;

		case FLAG_S:
			a.sets(al);                                                                 // sets   al
			a.movzx(dstreg, al);                                                        // movzx  dstreg,al
			a.shl(dstreg, 3);                                                           // shl    dstreg,3
			break;

		case FLAG_U:
			a.setp(al);                                                                 // setp   al
			a.movzx(dstreg, al);                                                        // movzx  dstreg,al
			a.shl(dstreg, 4);                                                           // shl    dstreg,4
			break;

		// carry plus another flag
		case FLAG_C | FLAG_V:
			a.setc(al);                                                                 // setc   al
			a.seto(cl);                                                                 // seto   cl
			a.movzx(eax, al);                                                           // movzx  eax,al
			a.movzx(ecx, cl);                                                           // movzx  ecx,cl
			a.lea(dstreg, ptr(eax, ecx, 1));                                            // lea    dstreg,[eax+ecx*2]
			break;

		case FLAG_C | FLAG_Z:
			a.setc(al);                                                                 // setc   al
			a.setz(cl);                                                                 // setz   cl
			a.movzx(eax, al);                                                           // movzx  eax,al
			a.movzx(ecx, cl);                                                           // movzx  ecx,cl
			a.lea(dstreg, ptr(eax, ecx, 2));                                            // lea    dstreg,[eax+ecx*4]
			break;

		case FLAG_C | FLAG_S:
			a.setc(al);                                                                 // setc   al
			a.sets(cl);                                                                 // sets   cl
			a.movzx(eax, al);                                                           // movzx  eax,al
			a.movzx(ecx, cl);                                                           // movzx  ecx,cl
			a.lea(dstreg, ptr(eax, ecx, 3));                                            // lea    dstreg,[eax+ecx*8]
			break;

		// overflow plus another flag
		case FLAG_V | FLAG_Z:
			a.seto(al);                                                                 // seto   al
			a.setz(cl);                                                                 // setz   cl
			a.movzx(eax, al);                                                           // movzx  eax,al
			a.movzx(ecx, cl);                                                           // movzx  ecx,cl
			a.lea(dstreg, ptr(eax, ecx, 1));                                            // lea    dstreg,[eax+ecx*2]
			a.shl(dstreg, 1);                                                           // shl    dstreg,1
			break;

		case FLAG_V | FLAG_S:
			a.seto(al);                                                                 // seto   al
			a.sets(cl);                                                                 // sets   cl
			a.movzx(eax, al);                                                           // movzx  eax,al
			a.movzx(ecx, cl);                                                           // movzx  ecx,cl
			a.lea(dstreg, ptr(eax, ecx, 2));                                            // lea    dstreg,[eax+ecx*4]
			a.shl(dstreg, 1);                                                           // shl    dstreg,1
			break;

		// zero plus another flag
		case FLAG_Z | FLAG_S:
			a.setz(al);                                                                 // setz   al
			a.sets(cl);                                                                 // sets   cl
			a.movzx(eax, al);                                                           // movzx  eax,al
			a.movzx(ecx, cl);                                                           // movzx  ecx,cl
			a.lea(dstreg, ptr(eax, ecx, 1));                                            // lea    dstreg,[eax+ecx*2]
			a.shl(dstreg, 2);                                                           // shl    dstreg,2
			break;

		// default cases
		default:
			a.pushfq();                                                                 // pushf
			a.pop(eax);                                                                 // pop    eax
			a.and_(eax, flagmask);                                                      // and    eax,flagmask
			a.movzx(dstreg, byte_ptr(rbp, rax, 0, offset_from_rbp(&m_near.flagsmap[0]))); // movzx  dstreg,[flags_map]
			break;
	}

	// 32-bit form
	if (inst.size() == 4)
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg

	// 64-bit form
	else if (inst.size() == 8)
		mov_param_reg(a, dstp, dstreg.r64());                                           // mov   dstp,dstreg
}


//-------------------------------------------------
//  op_save - process a SAVE opcode
//-------------------------------------------------

void drcbe_x64::op_save(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_M);

	// copy live state to the destination
	mov_r64_imm(a, rcx, (uintptr_t)dstp.memory());                                      // mov    rcx,dstp

	// copy flags
	a.pushfq();                                                                         // pushf
	a.pop(rax);                                                                         // pop    rax
	a.and_(eax, 0x8c5);                                                                 // and    eax,0x8c5
	a.mov(al, ptr(rbp, rax, 0, offset_from_rbp(&m_near.flagsmap[0])));                  // mov    al,[flags_map]
	a.mov(ptr(rcx, offsetof(drcuml_machine_state, flags)), al);                         // mov    state->flags,al

	// copy fmod and exp
	a.mov(al, MABS(&m_state.fmod));                                                     // mov    al,[fmod]
	a.mov(ptr(rcx, offsetof(drcuml_machine_state, fmod)), al);                          // mov    state->fmod,al
	a.mov(eax, MABS(&m_state.exp));                                                     // mov    eax,[exp]
	a.mov(ptr(rcx, offsetof(drcuml_machine_state, exp)), eax);                          // mov    state->exp,eax

	// copy integer registers
	int regoffs = offsetof(drcuml_machine_state, r);
	for (int regnum = 0; regnum < std::size(m_state.r); regnum++)
	{
		if (int_register_map[regnum] != 0)
			a.mov(ptr(rcx, regoffs + 8 * regnum), Gpq(regnum));
		else
		{
			a.mov(rax, MABS(&m_state.r[regnum].d));
			a.mov(ptr(rcx, regoffs + 8 * regnum), rax);
		}
	}

	// copy FP registers
	regoffs = offsetof(drcuml_machine_state, f);
	for (int regnum = 0; regnum < std::size(m_state.f); regnum++)
	{
		if (float_register_map[regnum] != 0)
			a.movsd(ptr(rcx, regoffs + 8 * regnum), Xmm(regnum));
		else
		{
			a.mov(rax, MABS(&m_state.f[regnum].d));
			a.mov(ptr(rcx, regoffs + 8 * regnum), rax);
		}
	}
}


//-------------------------------------------------
//  op_restore - process a RESTORE opcode
//-------------------------------------------------

void drcbe_x64::op_restore(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4);
	assert_no_condition(inst);

	// normalize parameters
	be_parameter srcp(*this, inst.param(0), PTYPE_M);

	// copy live state from the destination
	mov_r64_imm(a, rcx, (uintptr_t)srcp.memory());                                      // mov    rcx,dstp

	// copy integer registers
	int regoffs = offsetof(drcuml_machine_state, r);
	for (int regnum = 0; regnum < std::size(m_state.r); regnum++)
	{
		if (int_register_map[regnum] != 0)
			a.mov(Gpq(regnum), ptr(rcx, regoffs + 8 * regnum));
		else
		{
			a.mov(rax, ptr(rcx, regoffs + 8 * regnum));
			a.mov(MABS(&m_state.r[regnum].d), rax);
		}
	}

	// copy FP registers
	regoffs = offsetof(drcuml_machine_state, f);
	for (int regnum = 0; regnum < std::size(m_state.f); regnum++)
	{
		if (float_register_map[regnum] != 0)
			a.movsd(Xmm(regnum), ptr(rcx, regoffs + 8 * regnum));
		else
		{
			a.mov(rax, ptr(rcx, regoffs + 8 * regnum));
			a.mov(MABS(&m_state.f[regnum].d), rax);
		}
	}

	Mem fmod = MABS(&m_state.fmod);
	fmod.setSize(1);

	// copy fmod and exp
	a.movzx(eax, byte_ptr(rcx, offsetof(drcuml_machine_state, fmod)));                  // movzx  eax,state->fmod
	a.and_(eax, 3);                                                                     // and    eax,3
	a.mov(MABS(&m_state.fmod), al);                                                     // mov    [fmod],al
	a.ldmxcsr(ptr(rbp, rax, 2, offset_from_rbp(&m_near.ssecontrol[0])));                // ldmxcsr fp_control[eax]
	a.mov(eax, ptr(rcx, offsetof(drcuml_machine_state, exp)));                          // mov    eax,state->exp
	a.mov(MABS(&m_state.exp), eax);                                                     // mov    [exp],eax

	// copy flags
	a.movzx(eax, byte_ptr(rcx, offsetof(drcuml_machine_state, flags)));                 // movzx  eax,state->flags
	a.push(ptr(rbp, rax, 3, offset_from_rbp(&m_near.flagsunmap[0])));                   // push   flags_unmap[eax*8]
	a.popfq();                                                                          // popf
}



/***************************************************************************
    INTEGER OPERATIONS
***************************************************************************/

//-------------------------------------------------
//  op_load - process a LOAD opcode
//-------------------------------------------------

void drcbe_x64::op_load(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter basep(*this, inst.param(1), PTYPE_M);
	be_parameter indp(*this, inst.param(2), PTYPE_MRI);
	const parameter &scalesizep = inst.param(3);
	assert(scalesizep.is_size_scale());
	int size = scalesizep.size();

	// determine the pointer base
	int32_t baseoffs;
	Gp basereg = get_base_register_and_offset(a, basep.memory(), rdx, baseoffs);

	// pick a target register for the general case
	Gp dstreg = dstp.select_register(eax);

	// immediate index
	if (indp.is_immediate())
	{
		s32 const offset = baseoffs + (s32(indp.immediate()) << scalesizep.scale());

		if (size == SIZE_BYTE)
			a.movzx(dstreg, byte_ptr(basereg, offset));                                 // movzx dstreg,[basep + scale*indp]
		else if (size == SIZE_WORD)
			a.movzx(dstreg, word_ptr(basereg, offset));                                 // movzx dstreg,[basep + scale*indp]
		else if (size == SIZE_DWORD)
			a.mov(dstreg, ptr(basereg, offset));                                        // mov   dstreg,[basep + scale*indp]
		else if (size == SIZE_QWORD)
			a.mov(dstreg.r64(), ptr(basereg, offset));                                  // mov   dstreg,[basep + scale*indp]
	}

	// other index
	else
	{
		Gp indreg = indp.select_register(rcx);
		movsx_r64_p32(a, indreg, indp);
		if (size == SIZE_BYTE)
			a.movzx(dstreg, byte_ptr(basereg, indreg, scalesizep.scale(), baseoffs));   // movzx dstreg,[basep + scale*indp]
		else if (size == SIZE_WORD)
			a.movzx(dstreg, word_ptr(basereg, indreg, scalesizep.scale(), baseoffs));   // movzx dstreg,[basep + scale*indp]
		else if (size == SIZE_DWORD)
			a.mov(dstreg, ptr(basereg, indreg, scalesizep.scale(), baseoffs));          // mov   dstreg,[basep + scale*indp]
		else if (size == SIZE_QWORD)
			a.mov(dstreg.r64(), ptr(basereg, indreg, scalesizep.scale(), baseoffs));    // mov   dstreg,[basep + scale*indp]
	}

	// store result
	if (inst.size() == 4)
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	else
		mov_param_reg(a, dstp, dstreg.r64());                                           // mov   dstp,dstreg
}


//-------------------------------------------------
//  op_loads - process a LOADS opcode
//-------------------------------------------------

void drcbe_x64::op_loads(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter basep(*this, inst.param(1), PTYPE_M);
	be_parameter indp(*this, inst.param(2), PTYPE_MRI);
	const parameter &scalesizep = inst.param(3);
	assert(scalesizep.is_size_scale());
	int size = scalesizep.size();

	// determine the pointer base
	int32_t baseoffs;
	Gp basereg = get_base_register_and_offset(a, basep.memory(), rdx, baseoffs);

	// pick a target register for the general case
	Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax) : dstp.select_register(rax);

	// immediate index
	if (indp.is_immediate())
	{
		s32 const offset = baseoffs + (s32(indp.immediate()) << scalesizep.scale());

		if (size == SIZE_BYTE)
			a.movsx(dstreg, byte_ptr(basereg, offset));                                 // movsx dstreg,[basep + scale*indp]
		else if (size == SIZE_WORD)
			a.movsx(dstreg, word_ptr(basereg, offset));                                 // movsx dstreg,[basep + scale*indp]
		else if (size == SIZE_DWORD && inst.size() == 4)
			a.mov(dstreg, ptr(basereg, offset));                                        // mov   dstreg,[basep + scale*indp]
		else if (size == SIZE_DWORD)
			a.movsxd(dstreg, ptr(basereg, offset));                                     // movsxd dstreg,[basep + scale*indp]
		else if (size == SIZE_QWORD)
			a.mov(dstreg, ptr(basereg, offset));                                        // mov   dstreg,[basep + scale*indp]
	}

	// other index
	else
	{
		Gp indreg = indp.select_register(rcx);
		movsx_r64_p32(a, indreg, indp);
		if (size == SIZE_BYTE)
			a.movsx(dstreg, byte_ptr(basereg, indreg, scalesizep.scale(), baseoffs));   // movsx dstreg,[basep + scale*indp]
		else if (size == SIZE_WORD)
			a.movsx(dstreg, word_ptr(basereg, indreg, scalesizep.scale(), baseoffs));   // movsx dstreg,[basep + scale*indp]
		else if (size == SIZE_DWORD && inst.size() == 4)
			a.mov(dstreg, ptr(basereg, indreg, scalesizep.scale(), baseoffs));          // mov   dstreg,[basep + scale*indp]
		else if (size == SIZE_DWORD)
			a.movsxd(dstreg, ptr(basereg, indreg, scalesizep.scale(), baseoffs));       // movsxd dstreg,[basep + scale*indp]
		else if (size == SIZE_QWORD)
			a.mov(dstreg, ptr(basereg, indreg, scalesizep.scale(), baseoffs));          // mov   dstreg,[basep + scale*indp]
	}

	// store result
	mov_param_reg(a, dstp, dstreg);                                                     // mov   dstp,dstreg
}


//-------------------------------------------------
//  op_store - process a STORE opcode
//-------------------------------------------------

void drcbe_x64::op_store(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter basep(*this, inst.param(0), PTYPE_M);
	be_parameter indp(*this, inst.param(1), PTYPE_MRI);
	be_parameter srcp(*this, inst.param(2), PTYPE_MRI);
	const parameter &scalesizep = inst.param(3);
	int size = scalesizep.size();

	// determine the pointer base
	int32_t baseoffs;
	Gp basereg = get_base_register_and_offset(a, basep.memory(), rdx, baseoffs);

	// pick a source register for the general case
	Gp srcreg = srcp.select_register(rax);

	// degenerate case: constant index
	if (indp.is_immediate())
	{
		s32 const offset = baseoffs + (s32(indp.immediate()) << scalesizep.scale());

		// immediate source
		if (srcp.is_immediate())
		{
			if (size == SIZE_QWORD)
			{
				if (short_immediate(srcp.immediate()))
					a.mov(qword_ptr(basereg, offset), s32(srcp.immediate()));           // mov   [basep + scale*indp],srcp
				else
				{
					a.mov(ptr(basereg, offset + 0), u32(srcp.immediate() >>  0));       // mov   [basep + scale*indp],srcp
					a.mov(ptr(basereg, offset + 4), u32(srcp.immediate() >> 32));       // mov   [basep + scale*indp + 4],srcp >> 32
				}
			}
			else
				a.mov(ptr(basereg, offset, 1 << size), srcp.immediate());               // mov   [basep + scale*indp],srcp
		}

		// variable source
		else
		{
			if (size != SIZE_QWORD)
				mov_reg_param(a, srcreg.r32(), srcp);                                   // mov   srcreg,srcp
			else
				mov_reg_param(a, srcreg.r64(), srcp);                                   // mov   srcreg,srcp
			if (size == SIZE_BYTE)
				a.mov(ptr(basereg, offset), srcreg.r8());                               // mov   [basep + scale*indp],srcreg
			else if (size == SIZE_WORD)
				a.mov(ptr(basereg, offset), srcreg.r16());                              // mov   [basep + scale*indp],srcreg
			else if (size == SIZE_DWORD)
				a.mov(ptr(basereg, offset), srcreg.r32());                              // mov   [basep + scale*indp],srcreg
			else if (size == SIZE_QWORD)
				a.mov(ptr(basereg, offset), srcreg.r64());                              // mov   [basep + scale*indp],srcreg
		}
	}

	// normal case: variable index
	else
	{
		Gp indreg = indp.select_register(rcx);
		movsx_r64_p32(a, indreg, indp);                                                 // mov   indreg,indp

		// immediate source
		if (srcp.is_immediate())
		{
			if (size == SIZE_QWORD)
			{
				if (short_immediate(srcp.immediate()))
					a.mov(qword_ptr(basereg, indreg, scalesizep.scale(), baseoffs), s32(srcp.immediate()));     // mov   [basep + scale*indp],srcp
				else
				{
					a.mov(ptr(basereg, indreg, scalesizep.scale(), baseoffs + 0), u32(srcp.immediate() >>  0)); // mov   [basep + scale*ecx],srcp
					a.mov(ptr(basereg, indreg, scalesizep.scale(), baseoffs + 4), u32(srcp.immediate() >> 32)); // mov   [basep + scale*ecx + 4],srcp >> 32
				}
			}
			else
				a.mov(ptr(basereg, indreg, scalesizep.scale(), baseoffs, 1 << size), srcp.immediate());         // mov   [basep + scale*ecx],srcp
		}

		// variable source
		else
		{
			if (size != SIZE_QWORD)
				mov_reg_param(a, srcreg.r32(), srcp);                                   // mov   srcreg,srcp
			else
				mov_reg_param(a, srcreg.r64(), srcp);                                   // mov   edx:srcreg,srcp
			if (size == SIZE_BYTE)
				a.mov(ptr(basereg, indreg, scalesizep.scale(), baseoffs), srcreg.r8()); // mov   [basep + scale*ecx],srcreg
			else if (size == SIZE_WORD)
				a.mov(ptr(basereg, indreg, scalesizep.scale(), baseoffs), srcreg.r16());// mov   [basep + scale*ecx],srcreg
			else if (size == SIZE_DWORD)
				a.mov(ptr(basereg, indreg, scalesizep.scale(), baseoffs), srcreg.r32());// mov   [basep + scale*ecx],srcreg
			else if (size == SIZE_QWORD)
				a.mov(ptr(basereg, indreg, scalesizep.scale(), baseoffs), srcreg.r64());// mov   [basep + scale*ecx],srcreg
		}
	}
}


//-------------------------------------------------
//  op_read - process a READ opcode
//-------------------------------------------------

void drcbe_x64::op_read(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter addrp(*this, inst.param(1), PTYPE_MRI);
	const parameter &spacesizep = inst.param(2);
	assert(spacesizep.is_size_space());

	// pick a target register for the general case
	Gp dstreg = dstp.select_register(eax);

	// set up a call to the read handler
	auto &trampolines = m_accessors[spacesizep.space()];
	auto &resolved = m_resolved_accessors[spacesizep.space()];
	mov_reg_param(a, Gpd(REG_PARAM2), addrp);                                           // mov    param2,addrp
	if (spacesizep.size() == SIZE_BYTE)
	{
		if (resolved.read_byte.func)
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_byte.obj);                    // mov    param1,space
			smart_call_r64(a, resolved.read_byte.func, rax);                            // call   read_byte
		}
		else
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacesizep.space()]);    // mov    param1,space
			smart_call_m64(a, (x86code **)&trampolines.read_byte);                      // call   read_byte
		}
		a.movzx(dstreg, al);                                                            // movzx  dstreg,al
	}
	else if (spacesizep.size() == SIZE_WORD)
	{
		if (resolved.read_word.func)
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_word.obj);                    // mov    param1,space
			smart_call_r64(a, resolved.read_word.func, rax);                            // call   read_word
		}
		else
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacesizep.space()]);    // mov    param1,space
			smart_call_m64(a, (x86code **)&trampolines.read_word);                      // call   read_word
		}
		a.movzx(dstreg, ax);                                                            // movzx  dstreg,ax
	}
	else if (spacesizep.size() == SIZE_DWORD)
	{
		if (resolved.read_dword.func)
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_dword.obj);                   // mov    param1,space
			smart_call_r64(a, resolved.read_dword.func, rax);                           // call   read_dword
		}
		else
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacesizep.space()]);    // mov    param1,space
			smart_call_m64(a, (x86code **)&trampolines.read_dword);                     // call   read_dword
		}
		if (dstreg != eax || inst.size() == 8)
			a.mov(dstreg, eax);                                                         // mov    dstreg,eax
	}
	else if (spacesizep.size() == SIZE_QWORD)
	{
		if (resolved.read_qword.func)
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_qword.obj);                   // mov    param1,space
			smart_call_r64(a, resolved.read_qword.func, rax);                           // call   read_qword
		}
		else
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacesizep.space()]);    // mov    param1,space
			smart_call_m64(a, (x86code **)&trampolines.read_qword);                     // call   read_qword
		}
		if (dstreg != eax)
			a.mov(dstreg.r64(), rax);                                                   // mov    dstreg,rax
	}

	// store result
	if (inst.size() == 4)
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	else
		mov_param_reg(a, dstp, dstreg.r64());                                           // mov   dstp,dstreg
}


//-------------------------------------------------
//  op_readm - process a READM opcode
//-------------------------------------------------

void drcbe_x64::op_readm(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter addrp(*this, inst.param(1), PTYPE_MRI);
	be_parameter maskp(*this, inst.param(2), PTYPE_MRI);
	const parameter &spacesizep = inst.param(3);
	assert(spacesizep.is_size_space());

	// pick a target register for the general case
	Gp dstreg = dstp.select_register(eax);

	// set up a call to the read handler
	auto &trampolines = m_accessors[spacesizep.space()];
	auto &resolved = m_resolved_accessors[spacesizep.space()];
	mov_reg_param(a, Gpd(REG_PARAM2), addrp);                                           // mov    param2,addrp
	if (spacesizep.size() != SIZE_QWORD)
		mov_reg_param(a, Gpd(REG_PARAM3), maskp);                                       // mov    param3,maskp
	else
		mov_reg_param(a, Gpq(REG_PARAM3), maskp);                                       // mov    param3,maskp
	if (spacesizep.size() == SIZE_WORD)
	{
		if (resolved.read_word_masked.func)
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_word_masked.obj);             // mov    param1,space
			smart_call_r64(a, resolved.read_word_masked.func, rax);                     // call   read_byte_masked
		}
		else
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacesizep.space()]);    // mov    param1,space
			smart_call_m64(a, (x86code **)&trampolines.read_word_masked);               // call   read_word_masked
		}
		a.movzx(dstreg, ax);                                                            // movzx  dstreg,ax
	}
	else if (spacesizep.size() == SIZE_DWORD)
	{
		if (resolved.read_dword_masked.func)
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_dword_masked.obj);            // mov    param1,space
			smart_call_r64(a, resolved.read_dword_masked.func, rax);                    // call   read_dword_masked
		}
		else
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacesizep.space()]);    // mov    param1,space
			smart_call_m64(a, (x86code **)&trampolines.read_dword_masked);              // call   read_word_masked
		}
		if (dstreg != eax || inst.size() == 8)
			a.mov(dstreg, eax);                                                         // mov    dstreg,eax
	}
	else if (spacesizep.size() == SIZE_QWORD)
	{
		if (resolved.read_qword_masked.func)
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), resolved.read_qword_masked.obj);            // mov    param1,space
			smart_call_r64(a, resolved.read_qword_masked.func, rax);                    // call   read_qword_masked
		}
		else
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacesizep.space()]);    // mov    param1,space
			smart_call_m64(a, (x86code **)&trampolines.read_qword_masked);              // call   read_word_masked
		}
		if (dstreg != eax)
			a.mov(dstreg.r64(), rax);                                                   // mov    dstreg,rax
	}

	// store result
	if (inst.size() == 4)
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	else
		mov_param_reg(a, dstp, dstreg.r64());                                           // mov   dstp,dstreg
}


//-------------------------------------------------
//  op_write - process a WRITE opcode
//-------------------------------------------------

void drcbe_x64::op_write(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter addrp(*this, inst.param(0), PTYPE_MRI);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
	const parameter &spacesizep = inst.param(2);
	assert(spacesizep.is_size_space());

	// set up a call to the write handler
	auto &trampolines = m_accessors[spacesizep.space()];
	auto &resolved = m_resolved_accessors[spacesizep.space()];
	mov_reg_param(a, Gpd(REG_PARAM2), addrp);                                           // mov    param2,addrp
	if (spacesizep.size() != SIZE_QWORD)
		mov_reg_param(a, Gpd(REG_PARAM3), srcp);                                        // mov    param3,srcp
	else
		mov_reg_param(a, Gpq(REG_PARAM3), srcp);                                        // mov    param3,srcp
	if (spacesizep.size() == SIZE_BYTE)
	{
		if (resolved.write_byte.func)
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_byte.obj);                   // mov    param1,space
			smart_call_r64(a, resolved.write_byte.func, rax);                           // call   write_byte
		}
		else
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacesizep.space()]);    // mov    param1,space
			smart_call_m64(a, (x86code **)&trampolines.write_byte);                     // call   write_byte
		}
	}
	else if (spacesizep.size() == SIZE_WORD)
	{
		if (resolved.write_word.func)
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_word.obj);                   // mov    param1,space
			smart_call_r64(a, resolved.write_word.func, rax);                           // call   write_word
		}
		else
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacesizep.space()]);    // mov    param1,space
			smart_call_m64(a, (x86code **)&trampolines.write_word);                     // call   write_word
		}
	}
	else if (spacesizep.size() == SIZE_DWORD)
	{
		if (resolved.write_dword.func)
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_dword.obj);                  // mov    param1,space
			smart_call_r64(a, resolved.write_dword.func, rax);                          // call   write_dword
		}
		else
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacesizep.space()]);    // mov    param1,space
			smart_call_m64(a, (x86code **)&trampolines.write_dword);                    // call   write_dword
		}
	}
	else if (spacesizep.size() == SIZE_QWORD)
	{
		if (resolved.write_qword.func)
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_qword.obj);                  // mov    param1,space
			smart_call_r64(a, resolved.write_qword.func, rax);                          // call   write_qword
		}
		else
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacesizep.space()]);    // mov    param1,space
			smart_call_m64(a, (x86code **)&trampolines.write_qword);                    // call   write_qword
		}
	}
}


//-------------------------------------------------
//  op_writem - process a WRITEM opcode
//-------------------------------------------------

void drcbe_x64::op_writem(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter addrp(*this, inst.param(0), PTYPE_MRI);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
	be_parameter maskp(*this, inst.param(2), PTYPE_MRI);
	const parameter &spacesizep = inst.param(3);
	assert(spacesizep.is_size_space());

	// set up a call to the write handler
	auto &trampolines = m_accessors[spacesizep.space()];
	auto &resolved = m_resolved_accessors[spacesizep.space()];
	mov_reg_param(a, Gpd(REG_PARAM2), addrp);                                           // mov    param2,addrp
	if (spacesizep.size() != SIZE_QWORD)
	{
		mov_reg_param(a, Gpd(REG_PARAM3), srcp);                                        // mov    param3,srcp
		mov_reg_param(a, Gpd(REG_PARAM4), maskp);                                       // mov    param4,maskp
	}
	else
	{
		mov_reg_param(a, Gpq(REG_PARAM3), srcp);                                        // mov    param3,srcp
		mov_reg_param(a, Gpq(REG_PARAM4), maskp);                                       // mov    param4,maskp
	}
	if (spacesizep.size() == SIZE_WORD)
	{
		if (resolved.write_word.func)
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_word_masked.obj);            // mov    param1,space
			smart_call_r64(a, resolved.write_word_masked.func, rax);                    // call   write_word_masked
		}
		else
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacesizep.space()]);    // mov    param1,space
			smart_call_m64(a, (x86code **)&trampolines.write_word_masked);              // call   write_word_masked
		}
	}
	else if (spacesizep.size() == SIZE_DWORD)
	{
		if (resolved.write_word.func)
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_dword_masked.obj);           // mov    param1,space
			smart_call_r64(a, resolved.write_dword_masked.func, rax);                   // call   write_dword_masked
		}
		else
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacesizep.space()]);    // mov    param1,space
			smart_call_m64(a, (x86code **)&trampolines.write_dword_masked);             // call   write_dword_masked
		}
	}
	else if (spacesizep.size() == SIZE_QWORD)
	{
		if (resolved.write_word.func)
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), resolved.write_qword_masked.obj);           // mov    param1,space
			smart_call_r64(a, resolved.write_qword_masked.func, rax);                   // call   write_qword_masked
		}
		else
		{
			mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacesizep.space()]);    // mov    param1,space
			smart_call_m64(a, (x86code **)&trampolines.write_qword_masked);             // call   write_qword_masked
		}
	}
}


//-------------------------------------------------
//  op_carry - process a CARRY opcode
//-------------------------------------------------

void drcbe_x64::op_carry(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C);

	// normalize parameters
	be_parameter srcp(*this, inst.param(0), PTYPE_MRI);
	be_parameter bitp(*this, inst.param(1), PTYPE_MRI);

	Gp const src = Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, srcp.ireg());

	// degenerate case: source is immediate
	if (srcp.is_immediate() && bitp.is_immediate())
	{
		if (srcp.immediate() & ((uint64_t)1 << bitp.immediate()))
			a.stc();
		else
			a.clc();
	}

	// load non-immediate bit numbers into a register
	if (!bitp.is_immediate())
	{
		mov_reg_param(a, ecx, bitp);
		a.and_(ecx, inst.size() * 8 - 1);
	}

	if (bitp.is_immediate())
	{
		if (srcp.is_memory())
			a.bt(MABS(srcp.memory(), inst.size()), bitp.immediate());                   // bt     [srcp],bitp
		else if (srcp.is_int_register())
			a.bt(src, bitp.immediate());                                                // bt     srcp,bitp
	}
	else
	{
		if (srcp.is_memory())
			a.bt(MABS(srcp.memory(), inst.size()), ecx);                                // bt     [srcp],ecx
		else if (srcp.is_int_register())
			a.bt(src, ecx);                                                             // bt     srcp,ecx
	}
}


//-------------------------------------------------
//  op_set - process a SET opcode
//-------------------------------------------------

void drcbe_x64::op_set(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);

	// pick a target register for the general case
	Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax) : dstp.select_register(rax);

	// set to AL
	a.set(X86_CONDITION(inst.condition()), al);                                         // setcc  al
	a.movzx(dstreg.r32(), al);                                                          // movzx  dstreg,al
	mov_param_reg(a, dstp, dstreg);                                                     // mov   dstp,dstreg
}


//-------------------------------------------------
//  op_mov - process a MOV opcode
//-------------------------------------------------

void drcbe_x64::op_mov(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);

	// add a conditional branch unless a conditional move is possible
	Label skip = a.newLabel();
	if (inst.condition() != uml::COND_ALWAYS && !(dstp.is_int_register() && !srcp.is_immediate()))
		a.short_().j(X86_NOT_CONDITION(inst.condition()), skip);                        // jcc   skip

	// register to memory
	if (dstp.is_memory() && srcp.is_int_register())
	{
		Gp const src = Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, srcp.ireg());

		a.mov(MABS(dstp.memory()), src);                                                // mov   [dstp],srcp
	}
	// immediate to memory
	else if (dstp.is_memory() && srcp.is_immediate() && short_immediate(srcp.immediate()))
		a.mov(MABS(dstp.memory(), inst.size()), s32(srcp.immediate()));                 // mov   [dstp],srcp

	// conditional memory to register
	else if (inst.condition() != 0 && dstp.is_int_register() && srcp.is_memory())
	{
		Gp const dst = Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, dstp.ireg());
		a.cmov(X86_CONDITION(inst.condition()), dst, MABS(srcp.memory()));              // cmovcc dstp,[srcp]
	}

	// conditional register to register
	else if (inst.condition() != 0 && dstp.is_int_register() && srcp.is_int_register())
	{
		Gp const src = Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, srcp.ireg());
		Gp const dst = Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, dstp.ireg());
		a.cmov(X86_CONDITION(inst.condition()), dst, src);                              // cmovcc dstp,srcp
	}

	// general case
	else
	{
		Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax) : dstp.select_register(rax);

		mov_reg_param(a, dstreg, srcp, true);                                           // mov   dstreg,srcp
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}

	// resolve the jump
	if (inst.condition() != uml::COND_ALWAYS && !(dstp.is_int_register() && !srcp.is_immediate()))
		a.bind(skip);
}


//-------------------------------------------------
//  op_sext - process a SEXT opcode
//-------------------------------------------------

void drcbe_x64::op_sext(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_S | FLAG_Z);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
	const parameter &sizep = inst.param(2);
	assert(sizep.is_size());

	Gp dstreg = dstp.select_register(rax);

	// 32-bit form
	if (inst.size() == 4)
	{
		dstreg = dstreg.r32();

		// general case
		if (srcp.is_memory())
		{
			if (sizep.size() == SIZE_BYTE)
				a.movsx(dstreg, MABS(srcp.memory(), 1));                                // movsx dstreg,[srcp]
			else if (sizep.size() == SIZE_WORD)
				a.movsx(dstreg, MABS(srcp.memory(), 2));                                // movsx dstreg,[srcp]
			else if (sizep.size() == SIZE_DWORD)
				a.mov(dstreg, MABS(srcp.memory()));                                     // mov   dstreg,[srcp]
		}
		else if (srcp.is_int_register())
		{
			if (sizep.size() == SIZE_BYTE)
				a.movsx(dstreg, GpbLo(srcp.ireg()));                                    // movsx dstreg,srcp
			else if (sizep.size() == SIZE_WORD)
				a.movsx(dstreg, Gpw(srcp.ireg()));                                      // movsx dstreg,srcp
			else if (sizep.size() == SIZE_DWORD)
				a.mov(dstreg, Gpd(srcp.ireg()));                                        // mov   dstreg,srcp
		}
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		if (srcp.is_memory())
		{
			if (sizep.size() == SIZE_BYTE)
				a.movsx(dstreg, MABS(srcp.memory(), 1));                                // movsx dstreg,[srcp]
			else if (sizep.size() == SIZE_WORD)
				a.movsx(dstreg, MABS(srcp.memory(), 2));                                // movsx dstreg,[srcp]
			else if (sizep.size() == SIZE_DWORD)
				a.movsxd(dstreg, MABS(srcp.memory(), 4));                               // movsxd dstreg,[srcp]
			else if (sizep.size() == SIZE_QWORD)
				a.mov(dstreg, MABS(srcp.memory()));                                     // mov   dstreg,[srcp]
		}
		else if (srcp.is_int_register())
		{
			if (sizep.size() == SIZE_BYTE)
				a.movsx(dstreg, GpbLo(srcp.ireg()));                                    // movsx dstreg,srcp
			else if (sizep.size() == SIZE_WORD)
				a.movsx(dstreg, Gpw(srcp.ireg()));                                      // movsx dstreg,srcp
			else if (sizep.size() == SIZE_DWORD)
				a.movsxd(dstreg, Gpd(srcp.ireg()));                                     // movsxd dstreg,srcp
			else if (sizep.size() == SIZE_QWORD)
				a.mov(dstreg, Gpq(srcp.ireg()));                                        // mov   dstreg,srcp
		}
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}

	if (inst.flags() != 0)
		a.test(dstreg, dstreg);                                                         // test  dstreg,dstreg
}


//-------------------------------------------------
//  op_roland - process an ROLAND opcode
//-------------------------------------------------

void drcbe_x64::op_roland(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_S | FLAG_Z);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
	be_parameter shiftp(*this, inst.param(2), PTYPE_MRI);
	be_parameter maskp(*this, inst.param(3), PTYPE_MRI);

	// pick a target register
	Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax, shiftp, maskp) : dstp.select_register(rax, shiftp, maskp);

	mov_reg_param(a, dstreg, srcp);                                                     // mov   dstreg,srcp
	if (!shiftp.is_immediate_value(0))
		shift_op_param(a, Inst::kIdRol, dstreg, shiftp);                                // rol   dstreg,shiftp
	alu_op_param(a, Inst::kIdAnd, dstreg, maskp,                                        // and   dstreg,maskp
		[inst](Assembler &a, Operand const &dst, be_parameter const &src)
		{
			// optimize all-zero and all-one cases
			if (!inst.flags() && !src.immediate())
			{
				a.xor_(dst.as<Gpd>(), dst.as<Gpd>());
				return true;
			}
			else if (!inst.flags() && ones(src.immediate(), inst.size()))
				return true;

			return false;
		});
	mov_param_reg(a, dstp, dstreg);                                                     // mov   dstp,dstreg
}


//-------------------------------------------------
//  op_rolins - process an ROLINS opcode
//-------------------------------------------------

void drcbe_x64::op_rolins(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_S | FLAG_Z);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
	be_parameter shiftp(*this, inst.param(2), PTYPE_MRI);
	be_parameter maskp(*this, inst.param(3), PTYPE_MRI);

	// 32-bit form
	if (inst.size() == 4)
	{
		// pick a target register
		Gp dstreg = dstp.select_register(ecx, shiftp, maskp);

		mov_reg_param(a, eax, srcp);                                                    // mov   eax,srcp
		if (!shiftp.is_immediate_value(0))
			shift_op_param(a, Inst::kIdRol, eax, shiftp);                               // rol   eax,shiftp
		mov_reg_param(a, dstreg, dstp);                                                 // mov   dstreg,dstp
		if (maskp.is_immediate())
		{
			a.and_(eax, maskp.immediate());                                             // and   eax,maskp
			a.and_(dstreg, ~maskp.immediate());                                         // and   dstreg,~maskp
		}
		else
		{
			mov_reg_param(a, edx, maskp);                                               // mov   edx,maskp
			a.and_(eax, edx);                                                           // and   eax,edx
			a.not_(edx);                                                                // not   edx
			a.and_(dstreg, edx);                                                        // and   dstreg,edx
		}
		a.or_(dstreg, eax);                                                             // or    dstreg,eax
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// pick a target register
		Gp dstreg = dstp.select_register(rcx, shiftp, maskp);

		mov_reg_param(a, rax, srcp);                                                    // mov   rax,srcp
		mov_reg_param(a, rdx, maskp);                                                   // mov   rdx,maskp
		if (!shiftp.is_immediate_value(0))
			shift_op_param(a, Inst::kIdRol, rax, shiftp);                               // rol   rax,shiftp
		mov_reg_param(a, dstreg, dstp);                                                 // mov   dstreg,dstp
		a.and_(rax, rdx);                                                               // and   eax,rdx
		a.not_(rdx);                                                                    // not   rdx
		a.and_(dstreg, rdx);                                                            // and   dstreg,rdx
		a.or_(dstreg, rax);                                                             // or    dstreg,rax
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}
}


//-------------------------------------------------
//  op_add - process a ADD opcode
//-------------------------------------------------

void drcbe_x64::op_add(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
	normalize_commutative(src1p, src2p);

	// dstp == src1p in memory
	if (dstp.is_memory() && dstp == src1p)
		alu_op_param(a, Inst::kIdAdd, MABS(dstp.memory(), inst.size()), src2p,          // add   [dstp],src2p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize zero case
				return (!inst.flags() && !src.immediate());
			});

	// dstp == src2p in memory
	else if (dstp.is_memory() && dstp == src2p)
		alu_op_param(a, Inst::kIdAdd, MABS(dstp.memory(), inst.size()), src1p,          // add   [dstp],src1p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize zero case
				return (!inst.flags() && !src.immediate());
			});

	// reg = reg + imm
	else if (dstp.is_int_register() && src1p.is_int_register() && src2p.is_immediate() && short_immediate(src2p.immediate()) && !inst.flags())
	{
		Gp const dst = Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, dstp.ireg());
		Gp const src1 = Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, src1p.ireg());

		a.lea(dst, ptr(src1, src2p.immediate()));                                       // lea   dstp,[src1p+src2p]
	}

	// reg = reg + reg
	else if (dstp.is_int_register() && src1p.is_int_register() && src2p.is_int_register() && !inst.flags())
	{
		Gp const dst = Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, dstp.ireg());
		Gp const src1 = Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, src1p.ireg());
		Gp const src2 = Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, src2p.ireg());

		a.lea(dst, ptr(src1, src2));                                                    // lea   dstp,[src1p+src2p]
	}

	// general case
	else
	{
		// pick a target register for the general case
		Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax, src2p) : dstp.select_register(rax, src2p);

		mov_reg_param(a, dstreg, src1p);                                                // mov   dstreg,src1p
		alu_op_param(a, Inst::kIdAdd, dstreg, src2p,                                    // add   dstreg,src2p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize zero case
				return (!inst.flags() && !src.immediate());
			});
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}
}


//-------------------------------------------------
//  op_addc - process a ADDC opcode
//-------------------------------------------------

void drcbe_x64::op_addc(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
	normalize_commutative(src1p, src2p);

	// dstp == src1p in memory
	if (dstp.is_memory() && dstp == src1p)
		alu_op_param(a, Inst::kIdAdc, MABS(dstp.memory(), inst.size()), src2p);         // adc   [dstp],src2p

	// general case
	else
	{
		// pick a target register for the general case
		Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax, src2p) : dstp.select_register(rax, src2p);

		mov_reg_param(a, dstreg, src1p, true);                                          // mov   dstreg,src1p
		alu_op_param(a, Inst::kIdAdc, dstreg, src2p);                                   // adc   dstreg,src2p
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}
}


//-------------------------------------------------
//  op_sub - process a SUB opcode
//-------------------------------------------------

void drcbe_x64::op_sub(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);

	// dstp == src1p in memory
	if (dstp.is_memory() && dstp == src1p)
		alu_op_param(a, Inst::kIdSub, MABS(dstp.memory(), inst.size()), src2p,          // sub   [dstp],src2p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize zero case
				return (!inst.flags() && !src.immediate());
			});

	// reg = reg - imm
	else if (dstp.is_int_register() && src1p.is_int_register() && src2p.is_immediate() && short_immediate(src2p.immediate()) && !inst.flags())
	{
		Gp const dst = Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, dstp.ireg());
		Gp const src1 = Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, src1p.ireg());

		a.lea(dst, ptr(src1, -src2p.immediate()));                                      // lea   dstp,[src1p-src2p]
	}

	// general case
	else
	{
		// pick a target register for the general case
		Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax, src2p) : dstp.select_register(rax, src2p);

		mov_reg_param(a, dstreg, src1p);                                                // mov   dstreg,src1p
		alu_op_param(a, Inst::kIdSub, dstreg, src2p,                                    // sub   dstreg,src2p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize zero case
				return (!inst.flags() && !src.immediate());
			});
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}
}


//-------------------------------------------------
//  op_subc - process a SUBC opcode
//-------------------------------------------------

void drcbe_x64::op_subc(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);

	// dstp == src1p in memory
	if (dstp.is_memory() && dstp == src1p)
		alu_op_param(a, Inst::kIdSbb, MABS(dstp.memory(), inst.size()), src2p);         // sbb   [dstp],src2p

	// general case
	else
	{
		// pick a target register for the general case
		Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax, src2p) : dstp.select_register(rax, src2p);

		mov_reg_param(a, dstreg, src1p, true);                                          // mov   dstreg,src1p
		alu_op_param(a, Inst::kIdSbb, dstreg, src2p);                                   // sbb   dstreg,src2p
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}
}


//-------------------------------------------------
//  op_cmp - process a CMP opcode
//-------------------------------------------------

void drcbe_x64::op_cmp(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter src1p(*this, inst.param(0), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(1), PTYPE_MRI);

	// memory versus anything
	if (src1p.is_memory())
		alu_op_param(a, Inst::kIdCmp, MABS(src1p.memory(), inst.size()), src2p);        // cmp   [dstp],src2p

	// general case
	else
	{
		// pick a target register for the general case
		Gp src1reg = (inst.size() == 4) ? src1p.select_register(eax) : src1p.select_register(rax);

		if (src1p.is_immediate())
		{
			if (inst.size() == 4)
				a.mov(src1reg, src1p.immediate());                                      // mov   src1reg,imm
			else
				mov_r64_imm(a, src1reg, src1p.immediate());                             // mov   src1reg,imm
		}
		alu_op_param(a, Inst::kIdCmp, src1reg, src2p);                                  // cmp   src1reg,src2p
	}
}


//-------------------------------------------------
//  op_mulu - process a MULU opcode
//-------------------------------------------------

void drcbe_x64::op_mulu(Assembler &a, const instruction &inst)
{
	uint8_t zsflags = inst.flags() & (FLAG_Z | FLAG_S);
	uint8_t vflag = inst.flags() & FLAG_V;

	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter edstp(*this, inst.param(1), PTYPE_MR);
	be_parameter src1p(*this, inst.param(2), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(3), PTYPE_MRI);
	normalize_commutative(src1p, src2p);
	bool compute_hi = (dstp != edstp);

	// 32-bit form
	if (inst.size() == 4)
	{
		// general case
		mov_reg_param(a, eax, src1p);                                                   // mov   eax,src1p
		if (src2p.is_memory())
			a.mul(MABS(src2p.memory(), 4));                                             // mul   [src2p]
		else if (src2p.is_int_register())
			a.mul(Gpd(src2p.ireg()));                                                   // mul   src2p
		else if (src2p.is_immediate())
		{
			a.mov(edx, src2p.immediate());                                              // mov   edx,src2p
			a.mul(edx);                                                                 // mul   edx
		}
		mov_param_reg(a, dstp, eax);                                                    // mov   dstp,eax
		if (compute_hi)
			mov_param_reg(a, edstp, edx);                                               // mov   edstp,edx

		// compute flags
		if (inst.flags() != 0)
		{
			if (zsflags != 0)
			{
				if (vflag)
					a.pushfq();                                                         // pushf
				if (compute_hi)
				{
					if (zsflags == FLAG_Z)
						a.or_(edx, eax);                                                // or    edx,eax
					else if (zsflags == FLAG_S)
						a.test(edx, edx);                                               // test  edx,edx
					else
					{
						a.movzx(ecx, ax);                                               // movzx ecx,ax
						a.shr(eax, 16);                                                 // shr   eax,16
						a.or_(edx, ecx);                                                // or    edx,ecx
						a.or_(edx, eax);                                                // or    edx,eax
					}
				}
				else
					a.test(eax, eax);                                                   // test  eax,eax

				// we rely on the fact that OF is cleared by all logical operations above
				if (vflag)
				{
					a.pushfq();                                                         // pushf
					a.pop(rax);                                                         // pop   rax
					a.and_(qword_ptr(rsp), ~0x84);                                      // and   [rsp],~0x84
					a.or_(ptr(rsp), rax);                                               // or    [rsp],rax
					a.popfq();                                                          // popf
				}
			}
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		mov_reg_param(a, rax, src1p);                                                   // mov   rax,src1p
		if (src2p.is_memory())
			a.mul(MABS(src2p.memory(), 8));                                             // mul   [src2p]
		else if (src2p.is_int_register())
			a.mul(Gpq(src2p.ireg()));                                                   // mul   src2p
		else if (src2p.is_immediate())
		{
			mov_r64_imm(a, rdx, src2p.immediate());                                     // mov   rdx,src2p
			a.mul(rdx);                                                                 // mul   rdx
		}
		mov_param_reg(a, dstp, rax);                                                    // mov   dstp,rax
		if (compute_hi)
			mov_param_reg(a, edstp, rdx);                                               // mov   edstp,rdx

		// compute flags
		if (inst.flags() != 0)
		{
			if (zsflags != 0)
			{
				if (vflag)
					a.pushfq();                                                         // pushf
				if (compute_hi)
				{
					if (zsflags == FLAG_Z)
						a.or_(rdx, rax);                                                // or    rdx,rax
					else if (zsflags == FLAG_S)
						a.test(rdx, rdx);                                               // test  rdx,rdx
					else
					{
						a.mov(ecx, eax);                                                // mov   ecx,eax
						a.shr(rax, 32);                                                 // shr   rax,32
						a.or_(rdx, rcx);                                                // or    rdx,rcx
						a.or_(rdx, rax);                                                // or    rdx,rax
					}
				}
				else
					a.test(rax, rax);                                                   // test  rax,rax

				// we rely on the fact that OF is cleared by all logical operations above
				if (vflag)
				{
					a.pushfq();                                                         // pushf
					a.pop(rax);                                                         // pop   rax
					a.and_(qword_ptr(rsp), ~0x84);                                      // and   [rsp],~0x84
					a.or_(ptr(rsp), rax);                                               // or    [rsp],rax
					a.popfq();                                                          // popf
				}
			}
		}
	}
}


//-------------------------------------------------
//  op_muls - process a MULS opcode
//-------------------------------------------------

void drcbe_x64::op_muls(Assembler &a, const instruction &inst)
{
	uint8_t zsflags = inst.flags() & (FLAG_Z | FLAG_S);
	uint8_t vflag =  inst.flags() & FLAG_V;

	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter edstp(*this, inst.param(1), PTYPE_MR);
	be_parameter src1p(*this, inst.param(2), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(3), PTYPE_MRI);
	normalize_commutative(src1p, src2p);
	bool compute_hi = (dstp != edstp);

	// 32-bit form
	if (inst.size() == 4)
	{
		// 32-bit destination with memory/immediate or register/immediate
		if (!compute_hi && !src1p.is_immediate() && src2p.is_immediate())
		{
			Gp dstreg = dstp.is_int_register() ? Gpd(dstp.ireg()) : eax;
			if (src1p.is_memory())
				a.imul(dstreg, MABS(src1p.memory()), src2p.immediate());                // imul  dstreg,[src1p],src2p
			else if (src1p.is_int_register())
				a.imul(dstreg, Gpd(src1p.ireg()), src2p.immediate());                   // imul  dstreg,src1p,src2p
			mov_param_reg(a, dstp, dstreg);                                             // mov   dstp,eax
		}

		// 32-bit destination, general case
		else if (!compute_hi)
		{
			Gp dstreg = dstp.is_int_register() ? Gpd(dstp.ireg()) : eax;
			mov_reg_param(a, dstreg, src1p);                                            // mov   dstreg,src1p
			if (src2p.is_memory())
				a.imul(dstreg, MABS(src2p.memory()));                                   // imul  dstreg,[src2p]
			else if (src2p.is_int_register())
				a.imul(dstreg, Gpd(src2p.ireg()));                                      // imul  dstreg,src2p
			mov_param_reg(a, dstp, dstreg);                                             // mov   dstp,dstreg
		}

		// 64-bit destination, general case
		else
		{
			mov_reg_param(a, eax, src1p);                                               // mov   eax,src1p
			if (src2p.is_memory())
				a.imul(MABS(src2p.memory(), 4));                                        // imul  [src2p]
			else if (src2p.is_int_register())
				a.imul(Gpd(src2p.ireg()));                                              // imul  src2p
			else if (src2p.is_immediate())
			{
				a.mov(edx, src2p.immediate());                                          // mov   edx,src2p
				a.imul(edx);                                                            // imul  edx
			}
			mov_param_reg(a, dstp, eax);                                                // mov   dstp,eax
			mov_param_reg(a, edstp, edx);                                               // mov   edstp,edx
		}

		// compute flags
		if (inst.flags() != 0)
		{
			if (zsflags != 0)
			{
				if (vflag)
					a.pushfq();                                                         // pushf
				if (compute_hi)
				{
					if (zsflags == FLAG_Z)
						a.or_(edx, eax);                                                // or    edx,eax
					else if (zsflags == FLAG_S)
						a.test(edx, edx);                                               // test  edx,edx
					else
					{
						a.movzx(ecx, ax);                                               // movzx ecx,ax
						a.shr(eax, 16);                                                 // shr   eax,16
						a.or_(edx, ecx);                                                // or    edx,ecx
						a.or_(edx, eax);                                                // or    edx,eax
					}
				}
				else
					a.test(eax, eax);                                                   // test  eax,eax

				// we rely on the fact that OF is cleared by all logical operations above
				if (vflag)
				{
					a.pushfq();                                                         // pushf
					a.pop(rax);                                                         // pop   rax
					a.and_(qword_ptr(rsp), ~0x84);                                      // and   [rsp],~0x84
					a.or_(ptr(rsp), rax);                                               // or    [rsp],rax
					a.popfq();                                                          // popf
				}
			}
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// 64-bit destination with memory/immediate or register/immediate
		if (!compute_hi && !src1p.is_immediate() && src2p.is_immediate() && short_immediate(src2p.immediate()))
		{
			Gp dstreg = dstp.is_int_register() ? Gpq(dstp.ireg()) : rax;
			if (src1p.is_memory())
				a.imul(dstreg, MABS(src1p.memory()), src2p.immediate());                // imul  dstreg,[src1p],src2p
			else if (src1p.is_int_register())
				a.imul(dstreg, Gpq(src1p.ireg()), src2p.immediate());                   // imul  rax,src1p,src2p
			mov_param_reg(a, dstp, dstreg);                                             // mov   dstp,rax
		}

		// 64-bit destination, general case
		else if (!compute_hi)
		{
			Gp dstreg = dstp.is_int_register() ? Gpq(dstp.ireg()) : rax;
			mov_reg_param(a, dstreg, src1p);                                            // mov   dstreg,src1p
			if (src2p.is_memory())
				a.imul(dstreg, MABS(src2p.memory()));                                   // imul  dstreg,[src2p]
			else if (src2p.is_int_register())
				a.imul(dstreg, Gpq(src2p.ireg()));                                      // imul  dstreg,src2p
			mov_param_reg(a, dstp, dstreg);                                             // mov   dstp,dstreg
		}

		// 128-bit destination, general case
		else
		{
			mov_reg_param(a, rax, src1p);                                               // mov   rax,src1p
			if (src2p.is_memory())
				a.imul(MABS(src2p.memory(), 8));                                        // imul  [src2p]
			else if (src2p.is_int_register())
				a.imul(Gpq(src2p.ireg()));                                              // imul  src2p
			else if (src2p.is_immediate())
			{
				mov_r64_imm(a, rdx, src2p.immediate());                                 // mov   rdx,src2p
				a.imul(rdx);                                                            // imul  rdx
			}
			mov_param_reg(a, dstp, rax);                                                // mov   dstp,rax
			mov_param_reg(a, edstp, rdx);                                               // mov   edstp,rdx
		}

		// compute flags
		if (inst.flags() != 0)
		{
			if (zsflags != 0)
			{
				if (vflag)
					a.pushfq();                                                         // pushf
				if (compute_hi)
				{
					if (zsflags == FLAG_Z)
						a.or_(rdx, rax);                                                // or    rdx,rax
					else if (zsflags == FLAG_S)
						a.test(rdx, rdx);                                               // test  rdx,rdx
					else
					{
						a.mov(ecx, eax);                                                // mov   ecx,eax
						a.shr(rax, 32);                                                 // shr   rax,32
						a.or_(rdx, rcx);                                                // or    rdx,rcx
						a.or_(rdx, rax);                                                // or    rdx,rax
					}
				}
				else
					a.test(rax, rax);                                                   // test  rax,rax

				// we rely on the fact that OF is cleared by all logical operations above
				if (vflag)
				{
					a.pushfq();                                                         // pushf
					a.pop(rax);                                                         // pop   rax
					a.and_(qword_ptr(rsp), ~0x84);                                      // and   [rsp],~0x84
					a.or_(ptr(rsp), rax);                                               // or    [rsp],rax
					a.popfq();                                                          // popf
				}
			}
		}
	}
}


//-------------------------------------------------
//  op_divu - process a DIVU opcode
//-------------------------------------------------

void drcbe_x64::op_divu(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter edstp(*this, inst.param(1), PTYPE_MR);
	be_parameter src1p(*this, inst.param(2), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(3), PTYPE_MRI);
	bool compute_rem = (dstp != edstp);

	Label skip = a.newLabel();

	// 32-bit form
	if (inst.size() == 4)
	{
		// general case
		mov_reg_param(a, ecx, src2p);                                                   // mov   ecx,src2p
		if (inst.flags() != 0)
		{
			a.mov(eax, 0xa0000000);                                                     // mov   eax,0xa0000000
			a.add(eax, eax);                                                            // add   eax,eax
		}
		a.short_().jecxz(skip);                                                         // jecxz skip
		mov_reg_param(a, eax, src1p);                                                   // mov   eax,src1p
		a.xor_(edx, edx);                                                               // xor   edx,edx
		a.div(ecx);                                                                     // div   ecx
		mov_param_reg(a, dstp, eax);                                                    // mov   dstp,eax
		if (compute_rem)
			mov_param_reg(a, edstp, edx);                                               // mov   edstp,edx
		if (inst.flags() != 0)
			a.test(eax, eax);                                                           // test  eax,eax
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		mov_reg_param(a, rcx, src2p);                                                   // mov   rcx,src2p
		if (inst.flags() != 0)
		{
			a.mov(eax, 0xa0000000);                                                     // mov   eax,0xa0000000
			a.add(eax, eax);                                                            // add   eax,eax
		}
		a.short_().jecxz(skip);                                                         // jrcxz skip
		mov_reg_param(a, rax, src1p);                                                   // mov   rax,src1p
		a.xor_(edx, edx);                                                               // xor   edx,edx
		a.div(rcx);                                                                     // div   rcx
		mov_param_reg(a, dstp, rax);                                                    // mov   dstp,rax
		if (compute_rem)
			mov_param_reg(a, edstp, rdx);                                               // mov   edstp,rdx
		if (inst.flags() != 0)
			a.test(rax, rax);                                                           // test  eax,eax
	}

	a.bind(skip);                                                                   // skip:
}


//-------------------------------------------------
//  op_divs - process a DIVS opcode
//-------------------------------------------------

void drcbe_x64::op_divs(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_V | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter edstp(*this, inst.param(1), PTYPE_MR);
	be_parameter src1p(*this, inst.param(2), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(3), PTYPE_MRI);
	bool compute_rem = (dstp != edstp);

	Label skip = a.newLabel();

	// 32-bit form
	if (inst.size() == 4)
	{
		// general case
		mov_reg_param(a, ecx, src2p);                                                   // mov   ecx,src2p
		if (inst.flags() != 0)
		{
			a.mov(eax, 0xa0000000);                                                     // mov   eax,0xa0000000
			a.add(eax, eax);                                                            // add   eax,eax
		}
		a.short_().jecxz(skip);                                                         // jecxz skip
		mov_reg_param(a, eax, src1p);                                                   // mov   eax,src1p
		a.cdq();                                                                        // cdq
		a.idiv(ecx);                                                                    // idiv  ecx
		mov_param_reg(a, dstp, eax);                                                    // mov   dstp,eax
		if (compute_rem)
			mov_param_reg(a, edstp, edx);                                               // mov   edstp,edx
		if (inst.flags() != 0)
			a.test(eax, eax);                                                           // test  eax,eax
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// general case
		mov_reg_param(a, rcx, src2p);                                                   // mov   rcx,src2p
		if (inst.flags() != 0)
		{
			a.mov(eax, 0xa0000000);                                                     // mov   eax,0xa0000000
			a.add(eax, eax);                                                            // add   eax,eax
		}
		a.short_().jecxz(skip);                                                         // jrcxz skip
		mov_reg_param(a, rax, src1p);                                                   // mov   rax,src1p
		a.cqo();                                                                        // cqo
		a.idiv(rcx);                                                                    // idiv  rcx
		mov_param_reg(a, dstp, rax);                                                    // mov   dstp,rax
		if (compute_rem)
			mov_param_reg(a, edstp, rdx);                                               // mov   edstp,rdx
		if (inst.flags() != 0)
			a.test(rax, rax);                                                           // test  eax,eax
	}

	a.bind(skip);                                                                   // skip:
}


//-------------------------------------------------
//  op_and - process a AND opcode
//-------------------------------------------------

void drcbe_x64::op_and(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
	normalize_commutative(src1p, src2p);

	// pick a target register
	Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax, src2p) : dstp.select_register(rax, src2p);

	// dstp == src1p in memory
	if (dstp.is_memory() && dstp == src1p)
		alu_op_param(a, Inst::kIdAnd, MABS(dstp.memory(), inst.size()), src2p,          // and   [dstp],src2p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize all-zero and all-one cases
				if (!inst.flags() && !src.immediate())
				{
					a.mov(dst.as<Mem>(), imm(0));
					return true;
				}
				else if (!inst.flags() && ones(src.immediate(), inst.size()))
					return true;

				return false;
			});

	// dstp == src2p in memory
	else if (dstp.is_memory() && dstp == src2p)
		alu_op_param(a, Inst::kIdAnd, MABS(dstp.memory(), inst.size()), src1p,          // and   [dstp],src1p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize all-zero and all-one cases
				if (!inst.flags() && !src.immediate())
				{
					a.mov(dst.as<Mem>(), imm(0));
					return true;
				}
				else if (!inst.flags() && ones(src.immediate(), inst.size()))
					return true;

				return false;
			});

	// immediate 0xff
	else if (src2p.is_immediate_value(0xff) && !inst.flags())
	{
		if (src1p.is_int_register())
			a.movzx(dstreg, GpbLo(src1p.ireg()));                                       // movzx dstreg,src1p
		else if (src1p.is_memory())
			a.movzx(dstreg, MABS(src1p.memory(), 1));                                   // movzx dstreg,[src1p]
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}

	// immediate 0xffff
	else if (src2p.is_immediate_value(0xffff) && !inst.flags())
	{
		if (src1p.is_int_register())
			a.movzx(dstreg, Gpw(src1p.ireg()));                                         // movzx dstreg,src1p
		else if (src1p.is_memory())
			a.movzx(dstreg, MABS(src1p.memory(), 2));                                   // movzx dstreg,[src1p]
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}

	// immediate 0xffffffff
	else if (src2p.is_immediate_value(0xffffffff) && !inst.flags() && inst.size() == 8)
	{
		if (dstp.is_int_register() && src1p == dstp)
			a.mov(dstreg.r32(), dstreg.r32());                                          // mov   dstreg,dstreg
		else
		{
			mov_reg_param(a, dstreg.r32(), src1p);                                      // mov   dstreg,src1p
			mov_param_reg(a, dstp, dstreg);                                             // mov   dstp,dstreg
		}
	}

	// general case
	else
	{
		mov_reg_param(a, dstreg, src1p);                                                // mov   dstreg,src1p
		alu_op_param(a, Inst::kIdAnd, dstreg, src2p,                                    // and   dstreg,src2p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize all-zero and all-one cases
				if (!inst.flags() && !src.immediate())
				{
					a.xor_(dst.as<Gpd>(), dst.as<Gpd>());
					return true;
				}
				else if (!inst.flags() && ones(src.immediate(), inst.size()))
					return true;

				return false;
			});
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}
}


//-------------------------------------------------
//  op_test - process a TEST opcode
//-------------------------------------------------

void drcbe_x64::op_test(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter src1p(*this, inst.param(0), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(1), PTYPE_MRI);
	normalize_commutative(src1p, src2p);

	// src1p in memory
	if (src1p.is_memory())
		alu_op_param(a, Inst::kIdTest, MABS(src1p.memory(), inst.size()), src2p);       // test  [src1p],src2p

	// general case
	else
	{
		// pick a target register for the general case
		Gp src1reg = (inst.size() == 4) ? src1p.select_register(eax) : src1p.select_register(rax);

		mov_reg_param(a, src1reg, src1p);                                               // mov   src1reg,src1p
		alu_op_param(a, Inst::kIdTest, src1reg, src2p);                                 // test  src1reg,src2p
	}
}


//-------------------------------------------------
//  op_or - process a OR opcode
//-------------------------------------------------

void drcbe_x64::op_or(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
	normalize_commutative(src1p, src2p);

	// dstp == src1p in memory
	if (dstp.is_memory() && dstp == src1p)
		alu_op_param(a, Inst::kIdOr, MABS(dstp.memory(), inst.size()), src2p,           // or    [dstp],src2p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize all-zero and all-one cases
				if (!inst.flags() && ones(src.immediate(), inst.size()))
				{
					a.mov(dst.as<Mem>(), imm(-1));
					return true;
				}
				else if (!inst.flags() && !src.immediate())
					return true;

				return false;
			});

	// dstp == src2p in memory
	else if (dstp.is_memory() && dstp == src2p)
		alu_op_param(a, Inst::kIdOr, MABS(dstp.memory(), inst.size()), src1p,           // or    [dstp],src1p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize all-zero and all-one cases
				if (!inst.flags() && ones(src.immediate(), inst.size()))
				{
					a.mov(dst.as<Mem>(), imm(-1));
					return true;
				}
				else if (!inst.flags() && !src.immediate())
					return true;

				return false;
			});

	// general case
	else
	{
		// pick a target register for the general case
		Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax, src2p) : dstp.select_register(rax, src2p);

		mov_reg_param(a, dstreg, src1p);                                                // mov   dstreg,src1p
		alu_op_param(a, Inst::kIdOr, dstreg, src2p,                                     // or    dstreg,src2p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize all-zero and all-one cases
				if (!inst.flags() && ones(src.immediate(), inst.size()))
				{
					a.mov(dst.as<Gp>(), imm(-1));
					return true;
				}
				else if (!inst.flags() && !src.immediate())
					return true;

				return false;
			});
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}
}


//-------------------------------------------------
//  op_xor - process a XOR opcode
//-------------------------------------------------

void drcbe_x64::op_xor(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);
	normalize_commutative(src1p, src2p);

	// dstp == src1p in memory
	if (dstp.is_memory() && dstp == src1p)
		alu_op_param(a, Inst::kIdXor, MABS(dstp.memory(), inst.size()), src2p,          // xor   [dstp],src2p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize all-zero and all-one cases
				if (!inst.flags() && ones(src.immediate(), inst.size()))
				{
					a.not_(dst.as<Mem>());
					return true;
				}
				else if (!inst.flags() && !src.immediate())
					return true;

				return false;
			});

	// dstp == src2p in memory
	else if (dstp.is_memory() && dstp == src2p)
		alu_op_param(a, Inst::kIdXor, MABS(dstp.memory(), inst.size()), src1p,          // xor   [dstp],src1p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize all-zero and all-one cases
				if (!inst.flags() && ones(src.immediate(), inst.size()))
				{
					a.not_(dst.as<Mem>());
					return true;
				}
				else if (!inst.flags() && !src.immediate())
					return true;

				return false;
			});

	// dstp == src1p register
	else if (dstp.is_int_register() && dstp == src1p)
	{
		Gp const dst = Gp::fromTypeAndId((inst.size() == 4) ? RegType::kX86_Gpd : RegType::kX86_Gpq, dstp.ireg());

		alu_op_param(a, Inst::kIdXor, dst, src2p,                                       // xor   dstp,src2p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize all-zero and all-one cases
				if (!inst.flags() && ones(src.immediate(), inst.size()))
				{
					a.not_(dst.as<Gp>());
					return true;
				}
				else if (!inst.flags() && !src.immediate())
					return true;

				return false;
			});
	}
	// general case
	else
	{
		// pick a target register for the general case
		Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax, src2p) : dstp.select_register(rax, src2p);

		mov_reg_param(a, dstreg, src1p);                                                // mov   dstreg,src1p
		alu_op_param(a, Inst::kIdXor, dstreg, src2p,                                    // xor   dstreg,src2p
			[inst](Assembler &a, Operand const &dst, be_parameter const &src)
			{
				// optimize all-zero and all-one cases
				if (!inst.flags() && ones(src.immediate(), inst.size()))
				{
					a.not_(dst.as<Gp>());
					return true;
				}
				else if (!inst.flags() && !src.immediate())
					return true;

				return false;
			});
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}
}


//-------------------------------------------------
//  op_lzcnt - process a LZCNT opcode
//-------------------------------------------------

void drcbe_x64::op_lzcnt(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);

	// 32-bit form
	if (inst.size() == 4)
	{
		// pick a target register
		Gp dstreg = dstp.select_register(eax);

		mov_reg_param(a, dstreg, srcp);                                                 // mov   dstreg,src1p
		a.mov(ecx, 32 ^ 31);                                                            // mov   ecx,32 ^ 31
		a.bsr(dstreg, dstreg);                                                          // bsr   dstreg,dstreg
		a.cmovz(dstreg, ecx);                                                           // cmovz dstreg,ecx
		a.xor_(dstreg, 31);                                                             // xor   dstreg,31
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// pick a target register
		Gp dstreg = dstp.select_register(rax);

		mov_reg_param(a, dstreg, srcp);                                                 // mov   dstreg,src1p
		a.mov(ecx, 64 ^ 63);                                                            // mov   ecx,64 ^ 63
		a.bsr(dstreg, dstreg);                                                          // bsr   dstreg,dstreg
		a.cmovz(dstreg, rcx);                                                           // cmovz dstreg,rcx
		a.xor_(dstreg, 63);                                                             // xor   dstreg,63
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}
}


//-------------------------------------------------
//  op_tzcnt - process a TZCNT opcode
//-------------------------------------------------

void drcbe_x64::op_tzcnt(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);

	// 32-bit form
	if (inst.size() == 4)
	{
		Gp dstreg = dstp.select_register(eax);

		mov_reg_param(a, dstreg, srcp);                                                 // mov   dstreg,srcp
		a.mov(ecx, 32);                                                                 // mov   ecx,32
		a.bsf(dstreg, dstreg);                                                          // bsf   dstreg,dstreg
		a.cmovz(dstreg, ecx);                                                           // cmovz dstreg,ecx
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		Gp dstreg = dstp.select_register(rax);

		mov_reg_param(a, dstreg, srcp);                                                 // mov   dstreg,srcp
		a.mov(ecx, 64);                                                                 // mov   ecx,64
		a.bsf(dstreg, dstreg);                                                          // bsf   dstreg,dstreg
		a.cmovz(dstreg, rcx);                                                           // cmovz dstreg,rcx
		mov_param_reg(a, dstp, dstreg);                                                 // mov   dstp,dstreg
	}
}


//-------------------------------------------------
//  op_bswap - process a BSWAP opcode
//-------------------------------------------------

void drcbe_x64::op_bswap(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);

	// pick a target register
	Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax) : dstp.select_register(rax);

	mov_reg_param(a, dstreg, srcp);                                                     // mov   dstreg,src1p
	a.bswap(dstreg);                                                                    // bswap dstreg
	if (inst.flags() != 0)
		a.test(dstreg, dstreg);                                                         // test  dstreg,dstreg
	mov_param_reg(a, dstp, dstreg);                                                     // mov   dstp,dstreg
}

template <Inst::Id Opcode> void drcbe_x64::op_shift(Assembler &a, const uml::instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_Z | FLAG_S);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter src1p(*this, inst.param(1), PTYPE_MRI);
	be_parameter src2p(*this, inst.param(2), PTYPE_MRI);

	const bool carry = (Opcode == Inst::kIdRcl) || (Opcode == Inst::kIdRcr);

	// optimize immediate zero case
	if (carry || inst.flags() || !src2p.is_immediate_value(0))
	{
		// dstp == src1p in memory
		if (dstp.is_memory() && dstp == src1p)
			shift_op_param(a, Opcode, MABS(dstp.memory(), inst.size()), src2p);         // op   [dstp],src2p

		// general case
		else
		{
			// pick a target register
			Gp dstreg = (inst.size() == 4) ? dstp.select_register(eax, src2p) : dstp.select_register(rax, src2p);

			if (carry)
				mov_reg_param(a, dstreg, src1p, true);                                  // mov   dstreg,src1p
			else
				mov_reg_param(a, dstreg, src1p);                                        // mov   dstreg,src1p
			shift_op_param(a, Opcode, dstreg, src2p);                                   // op    dstreg,src2p
			mov_param_reg(a, dstp, dstreg);                                             // mov   dstp,dstreg
		}
	}
}


/***************************************************************************
    FLOATING POINT OPERATIONS
***************************************************************************/

//-------------------------------------------------
//  op_fload - process a FLOAD opcode
//-------------------------------------------------

void drcbe_x64::op_fload(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter basep(*this, inst.param(1), PTYPE_M);
	be_parameter indp(*this, inst.param(2), PTYPE_MRI);

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0);

	// determine the pointer base
	int32_t baseoffs;
	Gp basereg = get_base_register_and_offset(a, basep.memory(), rdx, baseoffs);

	// 32-bit form
	if (inst.size() == 4)
	{
		if (indp.is_immediate())
			a.movss(dstreg, ptr(basereg, baseoffs + 4*indp.immediate()));               // movss  dstreg,[basep + 4*indp]
		else
		{
			Gp indreg = indp.select_register(ecx);
			mov_reg_param(a, indreg, indp);                                             // mov    indreg,indp
			a.movss(dstreg, ptr(basereg, indreg, 2, baseoffs));                         // movss  dstreg,[basep + 4*indp]
		}
		movss_p32_r128(a, dstp, dstreg);                                                // movss  dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		if (indp.is_immediate())
			a.movsd(dstreg, ptr(basereg, baseoffs + 8*indp.immediate()));               // movsd  dstreg,[basep + 8*indp]
		else
		{
			Gp indreg = indp.select_register(ecx);
			mov_reg_param(a, indreg, indp);                                             // mov    indreg,indp
			a.movsd(dstreg, ptr(basereg, indreg, 3, baseoffs));                         // movsd  dstreg,[basep + 8*indp]
		}
		movsd_p64_r128(a, dstp, dstreg);                                                // movsd  dstp,dstreg
	}
}


//-------------------------------------------------
//  op_fstore - process a FSTORE opcode
//-------------------------------------------------

void drcbe_x64::op_fstore(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter basep(*this, inst.param(0), PTYPE_M);
	be_parameter indp(*this, inst.param(1), PTYPE_MRI);
	be_parameter srcp(*this, inst.param(2), PTYPE_MF);

	// pick a target register for the general case
	Xmm srcreg = srcp.select_register(xmm0);

	// determine the pointer base
	int32_t baseoffs;
	Gp basereg = get_base_register_and_offset(a, basep.memory(), rdx, baseoffs);

	// 32-bit form
	if (inst.size() == 4)
	{
		movss_r128_p32(a, srcreg, srcp);                                                // movss  srcreg,srcp
		if (indp.is_immediate())
			a.movss(ptr(basereg, baseoffs + 4*indp.immediate()), srcreg);               // movss  [basep + 4*indp],srcreg
		else
		{
			Gp indreg = indp.select_register(ecx);
			mov_reg_param(a, indreg, indp);                                             // mov    indreg,indp
			a.movss(ptr(basereg, indreg, 2, baseoffs), srcreg);                         // movss  [basep + 4*indp],srcreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		movsd_r128_p64(a, srcreg, srcp);                                                // movsd  srcreg,srcp
		if (indp.is_immediate())
			a.movsd(ptr(basereg, baseoffs + 8*indp.immediate()), srcreg);               // movsd  [basep + 8*indp],srcreg
		else
		{
			Gp indreg = indp.select_register(ecx);
			mov_reg_param(a, indreg, indp);                                             // mov    indreg,indp
			a.movsd(ptr(basereg, indreg, 3, baseoffs), srcreg);                         // movsd  [basep + 8*indp],srcreg
		}
	}
}


//-------------------------------------------------
//  op_fread - process a FREAD opcode
//-------------------------------------------------

void drcbe_x64::op_fread(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter addrp(*this, inst.param(1), PTYPE_MRI);
	const parameter &spacep = inst.param(2);
	assert(spacep.is_size_space());
	assert((1 << spacep.size()) == inst.size());

	// set up a call to the read dword/qword handler
	mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacep.space()]);                // mov    param1,space
	mov_reg_param(a, Gpd(REG_PARAM2), addrp);                                           // mov    param2,addrp
	if (inst.size() == 4)
		smart_call_m64(a, (x86code **)&m_accessors[spacep.space()].read_dword);         // call   read_dword
	else if (inst.size() == 8)
		smart_call_m64(a, (x86code **)&m_accessors[spacep.space()].read_qword);         // call   read_qword

	// store result
	if (inst.size() == 4)
	{
		if (dstp.is_memory())
			a.mov(MABS(dstp.memory()), eax);                                            // mov   [dstp],eax
		else if (dstp.is_float_register())
			a.movd(Xmm(dstp.freg()), eax);                                              // movd  dstp,eax
	}
	else if (inst.size() == 8)
	{
		if (dstp.is_memory())
			a.mov(MABS(dstp.memory()), rax);                                            // mov   [dstp],rax
		else if (dstp.is_float_register())
			a.movq(Xmm(dstp.freg()), rax);                                              // movq  dstp,rax
	}
}


//-------------------------------------------------
//  op_fwrite - process a FWRITE opcode
//-------------------------------------------------

void drcbe_x64::op_fwrite(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter addrp(*this, inst.param(0), PTYPE_MRI);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);
	const parameter &spacep = inst.param(2);
	assert(spacep.is_size_space());
	assert((1 << spacep.size()) == inst.size());

	// general case
	mov_r64_imm(a, Gpq(REG_PARAM1), (uintptr_t)m_space[spacep.space()]);                // mov    param1,space
	mov_reg_param(a, Gpd(REG_PARAM2), addrp);                                           // mov    param21,addrp

	// 32-bit form
	if (inst.size() == 4)
	{
		if (srcp.is_memory())
			a.mov(Gpd(REG_PARAM3), MABS(srcp.memory()));                                // mov    param3,[srcp]
		else if (srcp.is_float_register())
			a.movd(Gpd(REG_PARAM3), Xmm(srcp.freg()));                                  // movd   param3,srcp
		smart_call_m64(a, (x86code **)&m_accessors[spacep.space()].write_dword);        // call   write_dword
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		if (srcp.is_memory())
			a.mov(Gpq(REG_PARAM3), MABS(srcp.memory()));                                // mov    param3,[srcp]
		else if (srcp.is_float_register())
			a.movq(Gpq(REG_PARAM3), Xmm(srcp.freg()));                                  // movq   param3,srcp
		smart_call_m64(a, (x86code **)&m_accessors[spacep.space()].write_qword);        // call   write_qword
	}
}


//-------------------------------------------------
//  op_fmov - process a FMOV opcode
//-------------------------------------------------

void drcbe_x64::op_fmov(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_any_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0);

	// always start with a jmp
	Label skip = a.newLabel();
	if (inst.condition() != uml::COND_ALWAYS)
		a.short_().j(X86_NOT_CONDITION(inst.condition()), skip);                        // jcc   skip

	// 32-bit form
	if (inst.size() == 4)
	{
		if (srcp.is_float_register())
		{
			movss_p32_r128(a, dstp, Xmm(srcp.freg()));                                  // movss dstp,srcp
		}
		else
		{
			movss_r128_p32(a, dstreg, srcp);                                            // movss dstreg,srcp
			movss_p32_r128(a, dstp, dstreg);                                            // movss dstp,dstreg
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		if (srcp.is_float_register())
		{
			movsd_p64_r128(a, dstp, Xmm(srcp.freg()));                                  // movsd dstp,srcp
		}
		else
		{
			movsd_r128_p64(a, dstreg, srcp);                                            // movsd dstreg,srcp
			movsd_p64_r128(a, dstp, dstreg);                                            // movsd dstp,dstreg
		}
	}

	// resolve the jump
	if (inst.condition() != uml::COND_ALWAYS)
		a.bind(skip);                                                               // skip:
}


//-------------------------------------------------
//  op_ftoint - process a FTOINT opcode
//-------------------------------------------------

void drcbe_x64::op_ftoint(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);
	const parameter &sizep = inst.param(2);
	assert(sizep.is_size());
	const parameter &roundp = inst.param(3);
	assert(roundp.is_rounding());

	// pick a target register for the general case
	Gp dstreg = (sizep.size() == SIZE_DWORD) ? dstp.select_register(eax) : dstp.select_register(rax);

	// set rounding mode if necessary
	if (roundp.rounding() != ROUND_DEFAULT && roundp.rounding() != ROUND_TRUNC)
	{
		a.stmxcsr(MABS(&m_near.ssemodesave));                                           // stmxcsr [ssemodesave]
		a.ldmxcsr(MABS(&m_near.ssecontrol[roundp.rounding()]));                         // ldmxcsr fpcontrol[mode]
	}

	// 32-bit form
	if (inst.size() == 4)
	{
		if (srcp.is_memory())
		{
			if (roundp.rounding() != ROUND_TRUNC)
				a.cvtss2si(dstreg, MABS(srcp.memory()));                                // cvtss2si dstreg,[srcp]
			else
				a.cvttss2si(dstreg, MABS(srcp.memory()));                               // cvttss2si dstreg,[srcp]
		}
		else if (srcp.is_float_register())
		{
			if (roundp.rounding() != ROUND_TRUNC)
				a.cvtss2si(dstreg, Xmm(srcp.freg()));                                   // cvtss2si dstreg,srcp
			else
				a.cvttss2si(dstreg, Xmm(srcp.freg()));                                  // cvttss2si dstreg,srcp
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		if (srcp.is_memory())
		{
			if (roundp.rounding() != ROUND_TRUNC)
				a.cvtsd2si(dstreg, MABS(srcp.memory()));                                // cvtsd2si dstreg,[srcp]
			else
				a.cvttsd2si(dstreg, MABS(srcp.memory()));                               // cvttsd2si dstreg,[srcp]
		}
		else if (srcp.is_float_register())
		{
			if (roundp.rounding() != ROUND_TRUNC)
				a.cvtsd2si(dstreg, Xmm(srcp.freg()));                                   // cvtsd2si dstreg,srcp
			else
				a.cvttsd2si(dstreg, Xmm(srcp.freg()));                                  // cvttsd2si dstreg,srcp
		}
	}

	mov_param_reg(a, dstp, dstreg);                                                     // mov   dstp,dstreg

	// restore rounding mode
	if (roundp.rounding() != ROUND_DEFAULT && roundp.rounding() != ROUND_TRUNC)
		a.ldmxcsr(MABS(&m_near.ssemodesave));                                           // ldmxcsr [ssemodesave]
}


//-------------------------------------------------
//  op_ffrint - process a FFRINT opcode
//-------------------------------------------------

void drcbe_x64::op_ffrint(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MRI);
	const parameter &sizep = inst.param(2);
	assert(sizep.is_size());

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0);

	// 32-bit form
	if (inst.size() == 4)
	{
		// 32-bit integer source
		if (sizep.size() == SIZE_DWORD)
		{
			if (srcp.is_memory())
				a.cvtsi2ss(dstreg, MABS(srcp.memory(), 4));                             // cvtsi2ss dstreg,[srcp]
			else
			{
				Gp srcreg = srcp.select_register(eax);
				mov_reg_param(a, srcreg, srcp);                                         // mov      srcreg,srcp
				a.cvtsi2ss(dstreg, srcreg);                                             // cvtsi2ss dstreg,srcreg
			}
		}

		// 64-bit integer source
		else
		{
			if (srcp.is_memory())
				a.cvtsi2ss(dstreg, MABS(srcp.memory(), 8));                             // cvtsi2ss dstreg,[srcp]
			else
			{
				Gp srcreg = srcp.select_register(rax);
				mov_reg_param(a, srcreg, srcp);                                         // mov      srcreg,srcp
				a.cvtsi2ss(dstreg, srcreg);                                             // cvtsi2ss dstreg,srcreg
			}
		}
		movss_p32_r128(a, dstp, dstreg);                                                // movss    dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		// 32-bit integer source
		if (sizep.size() == SIZE_DWORD)
		{
			if (srcp.is_memory())
				a.cvtsi2sd(dstreg, MABS(srcp.memory(), 4));                             // cvtsi2sd dstreg,[srcp]
			else
			{
				Gp srcreg = srcp.select_register(eax);
				mov_reg_param(a, srcreg, srcp);                                         // mov      srcreg,srcp
				a.cvtsi2sd(dstreg, srcreg);                                             // cvtsi2sd dstreg,srcreg
			}
		}

		// 64-bit integer source
		else
		{
			if (srcp.is_memory())
				a.cvtsi2sd(dstreg, MABS(srcp.memory(), 8));                             // cvtsi2sd dstreg,[srcp]
			else
			{
				Gp srcreg = srcp.select_register(rax);
				mov_reg_param(a, srcreg, srcp);                                         // mov      srcreg,srcp
				a.cvtsi2sd(dstreg, srcreg);                                             // cvtsi2sd dstreg,srcreg
			}
		}
		movsd_p64_r128(a, dstp, dstreg);                                                // movsd    dstp,dstreg
	}
}


//-------------------------------------------------
//  op_ffrflt - process a FFRFLT opcode
//-------------------------------------------------

void drcbe_x64::op_ffrflt(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);
	const parameter &sizep = inst.param(2);
	assert(sizep.is_size());

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0);

	// single-to-double
	if (inst.size() == 8 && sizep.size() == SIZE_DWORD)
	{
		if (srcp.is_memory())
			a.cvtss2sd(dstreg, MABS(srcp.memory()));                                    // cvtss2sd dstreg,[srcp]
		else if (srcp.is_float_register())
			a.cvtss2sd(dstreg, Xmm(srcp.freg()));                                       // cvtss2sd dstreg,srcp
		movsd_p64_r128(a, dstp, dstreg);                                                // movsd    dstp,dstreg
	}

	// double-to-single
	else if (inst.size() == 4 && sizep.size() == SIZE_QWORD)
	{
		if (srcp.is_memory())
			a.cvtsd2ss(dstreg, MABS(srcp.memory()));                                    // cvtsd2ss dstreg,[srcp]
		else if (srcp.is_float_register())
			a.cvtsd2ss(dstreg, Xmm(srcp.freg()));                                       // cvtsd2ss dstreg,srcp
		movss_p32_r128(a, dstp, dstreg);                                                // movss    dstp,dstreg
	}
}


//-------------------------------------------------
//  op_frnds - process a FRNDS opcode
//-------------------------------------------------

void drcbe_x64::op_frnds(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0);

	// 64-bit form
	if (srcp.is_memory())
		a.cvtsd2ss(dstreg, MABS(srcp.memory(), 8));                                     // cvtsd2ss dstreg,[srcp]
	else if (srcp.is_float_register())
		a.cvtsd2ss(dstreg, Xmm(srcp.freg()));                                           // cvtsd2ss dstreg,srcp
	a.cvtss2sd(dstreg, dstreg);                                                         // cvtss2sd dstreg,dstreg
	movsd_p64_r128(a, dstp, dstreg);                                                    // movsd    dstp,dstreg
}


//-------------------------------------------------
//  op_fadd - process a FADD opcode
//-------------------------------------------------

void drcbe_x64::op_fadd(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter src1p(*this, inst.param(1), PTYPE_MF);
	be_parameter src2p(*this, inst.param(2), PTYPE_MF);
	normalize_commutative(src1p, src2p);

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		movss_r128_p32(a, dstreg, src1p);                                               // movss dstreg,src1p
		if (src2p.is_memory())
			a.addss(dstreg, MABS(src2p.memory()));                                      // addss dstreg,[src2p]
		else if (src2p.is_float_register())
			a.addss(dstreg, Xmm(src2p.freg()));                                         // addss dstreg,src2p
		movss_p32_r128(a, dstp, dstreg);                                                // movss dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		movsd_r128_p64(a, dstreg, src1p);                                               // movsd dstreg,src1p
		if (src2p.is_memory())
			a.addsd(dstreg, MABS(src2p.memory()));                                      // addsd dstreg,[src2p]
		else if (src2p.is_float_register())
			a.addsd(dstreg, Xmm(src2p.freg()));                                         // addsd dstreg,src2p
		movsd_p64_r128(a, dstp, dstreg);                                                // movsd dstp,dstreg
	}
}


//-------------------------------------------------
//  op_fsub - process a FSUB opcode
//-------------------------------------------------

void drcbe_x64::op_fsub(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter src1p(*this, inst.param(1), PTYPE_MF);
	be_parameter src2p(*this, inst.param(2), PTYPE_MF);

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		movss_r128_p32(a, dstreg, src1p);                                               // movss dstreg,src1p
		if (src2p.is_memory())
			a.subss(dstreg, MABS(src2p.memory()));                                      // subss dstreg,[src2p]
		else if (src2p.is_float_register())
			a.subss(dstreg, Xmm(src2p.freg()));                                         // subss dstreg,src2p
		movss_p32_r128(a, dstp, dstreg);                                                // movss dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		movsd_r128_p64(a, dstreg, src1p);                                               // movsd dstreg,src1p
		if (src2p.is_memory())
			a.subsd(dstreg, MABS(src2p.memory()));                                      // subsd dstreg,[src2p]
		else if (src2p.is_float_register())
			a.subsd(dstreg, Xmm(src2p.freg()));                                         // subsd dstreg,src2p
		movsd_p64_r128(a, dstp, dstreg);                                                // movsd dstp,dstreg
	}
}


//-------------------------------------------------
//  op_fcmp - process a FCMP opcode
//-------------------------------------------------

void drcbe_x64::op_fcmp(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_flags(inst, FLAG_C | FLAG_Z | FLAG_U);

	// normalize parameters
	be_parameter src1p(*this, inst.param(0), PTYPE_MF);
	be_parameter src2p(*this, inst.param(1), PTYPE_MF);

	// pick a target register for the general case
	Xmm src1reg = src1p.select_register(xmm0);

	// 32-bit form
	if (inst.size() == 4)
	{
		movss_r128_p32(a, src1reg, src1p);                                              // movss src1reg,src1p
		if (src2p.is_memory())
			a.comiss(src1reg, MABS(src2p.memory()));                                    // comiss src1reg,[src2p]
		else if (src2p.is_float_register())
			a.comiss(src1reg, Xmm(src2p.freg()));                                       // comiss src1reg,src2p
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		movsd_r128_p64(a, src1reg, src1p);                                              // movsd src1reg,src1p
		if (src2p.is_memory())
			a.comisd(src1reg, MABS(src2p.memory()));                                    // comisd src1reg,[src2p]
		else if (src2p.is_float_register())
			a.comisd(src1reg, Xmm(src2p.freg()));                                       // comisd src1reg,src2p
	}
}


//-------------------------------------------------
//  op_fmul - process a FMUL opcode
//-------------------------------------------------

void drcbe_x64::op_fmul(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter src1p(*this, inst.param(1), PTYPE_MF);
	be_parameter src2p(*this, inst.param(2), PTYPE_MF);
	normalize_commutative(src1p, src2p);

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		movss_r128_p32(a, dstreg, src1p);                                               // movss dstreg,src1p
		if (src2p.is_memory())
			a.mulss(dstreg, MABS(src2p.memory()));                                      // mulss dstreg,[src2p]
		else if (src2p.is_float_register())
			a.mulss(dstreg, Xmm(src2p.freg()));                                         // mulss dstreg,src2p
		movss_p32_r128(a, dstp, dstreg);                                                // movss dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		movsd_r128_p64(a, dstreg, src1p);                                               // movsd dstreg,src1p
		if (src2p.is_memory())
			a.mulsd(dstreg, MABS(src2p.memory()));                                      // mulsd dstreg,[src2p]
		else if (src2p.is_float_register())
			a.mulsd(dstreg, Xmm(src2p.freg()));                                         // mulsd dstreg,src2p
		movsd_p64_r128(a, dstp, dstreg);                                                // movsd dstp,dstreg
	}
}


//-------------------------------------------------
//  op_fdiv - process a FDIV opcode
//-------------------------------------------------

void drcbe_x64::op_fdiv(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter src1p(*this, inst.param(1), PTYPE_MF);
	be_parameter src2p(*this, inst.param(2), PTYPE_MF);

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0, src2p);

	// 32-bit form
	if (inst.size() == 4)
	{
		movss_r128_p32(a, dstreg, src1p);                                               // movss dstreg,src1p
		if (src2p.is_memory())
			a.divss(dstreg, MABS(src2p.memory()));                                      // divss dstreg,[src2p]
		else if (src2p.is_float_register())
			a.divss(dstreg, Xmm(src2p.freg()));                                         // divss dstreg,src2p
		movss_p32_r128(a, dstp, dstreg);                                                // movss dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		movsd_r128_p64(a, dstreg, src1p);                                               // movsd dstreg,src1p
		if (src2p.is_memory())
			a.divsd(dstreg, MABS(src2p.memory()));                                      // divsd dstreg,[src2p]
		else if (src2p.is_float_register())
			a.divsd(dstreg, Xmm(src2p.freg()));                                         // divsd dstreg,src2p
		movsd_p64_r128(a, dstp, dstreg);                                                // movsd dstp,dstreg
	}
}


//-------------------------------------------------
//  op_fneg - process a FNEG opcode
//-------------------------------------------------

void drcbe_x64::op_fneg(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0, srcp);

	// 32-bit form
	if (inst.size() == 4)
	{
		a.xorps(dstreg, dstreg);                                                        // xorps dstreg,dstreg
		if (srcp.is_memory())
			a.subss(dstreg, MABS(srcp.memory()));                                       // subss dstreg,[srcp]
		else if (srcp.is_float_register())
			a.subss(dstreg, Xmm(srcp.freg()));                                          // subss dstreg,srcp
		movss_p32_r128(a, dstp, dstreg);                                                // movss dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		a.xorpd(dstreg, dstreg);                                                        // xorpd dstreg,dstreg
		if (srcp.is_memory())
			a.subsd(dstreg, MABS(srcp.memory()));                                       // subsd dstreg,[srcp]
		else if (srcp.is_float_register())
			a.subsd(dstreg, Xmm(srcp.freg()));                                          // subsd dstreg,srcp
		movsd_p64_r128(a, dstp, dstreg);                                                // movsd dstp,dstreg
	}
}


//-------------------------------------------------
//  op_fabs - process a FABS opcode
//-------------------------------------------------

void drcbe_x64::op_fabs(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0, srcp);

	// 32-bit form
	if (inst.size() == 4)
	{
		movss_r128_p32(a, dstreg, srcp);                                                // movss dstreg,srcp
		a.andps(dstreg, MABS(m_absmask32));                                             // andps dstreg,[absmask32]
		movss_p32_r128(a, dstp, dstreg);                                                // movss dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		movsd_r128_p64(a, dstreg, srcp);                                                // movsd dstreg,srcp
		a.andpd(dstreg, MABS(m_absmask64));                                             // andpd dstreg,[absmask64]
		movsd_p64_r128(a, dstp, dstreg);                                                // movsd dstp,dstreg
	}
}


//-------------------------------------------------
//  op_fsqrt - process a FSQRT opcode
//-------------------------------------------------

void drcbe_x64::op_fsqrt(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0);

	// 32-bit form
	if (inst.size() == 4)
	{
		if (srcp.is_memory())
			a.sqrtss(dstreg, MABS(srcp.memory()));                                      // sqrtss dstreg,[srcp]
		else if (srcp.is_float_register())
			a.sqrtss(dstreg, Xmm(srcp.freg()));                                         // sqrtss dstreg,srcp
		movss_p32_r128(a, dstp, dstreg);                                                // movss dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		if (srcp.is_memory())
			a.sqrtsd(dstreg, MABS(srcp.memory()));                                      // sqrtsd dstreg,[srcp]
		else if (srcp.is_float_register())
			a.sqrtsd(dstreg, Xmm(srcp.freg()));                                         // sqrtsd dstreg,srcp
		movsd_p64_r128(a, dstp, dstreg);                                                // movsd dstp,dstreg
	}
}


//-------------------------------------------------
//  op_frecip - process a FRECIP opcode
//-------------------------------------------------

void drcbe_x64::op_frecip(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0);

	// 32-bit form
	if (inst.size() == 4)
	{
		if (USE_RCPSS_FOR_SINGLES)
		{
			if (srcp.is_memory())
				a.rcpss(dstreg, MABS(srcp.memory()));                                   // rcpss dstreg,[srcp]
			else if (srcp.is_float_register())
				a.rcpss(dstreg, Xmm(srcp.freg()));                                      // rcpss dstreg,srcp
			movss_p32_r128(a, dstp, dstreg);                                            // movss dstp,dstreg
		}
		else
		{
			a.movss(xmm1, MABS(&m_near.single1));                                       // movss xmm1,1.0
			if (srcp.is_memory())
				a.divss(xmm1, MABS(srcp.memory()));                                     // divss xmm1,[srcp]
			else if (srcp.is_float_register())
				a.divss(xmm1, Xmm(srcp.freg()));                                        // divss xmm1,srcp
			movss_p32_r128(a, dstp, xmm1);                                              // movss dstp,xmm1
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		if (USE_RCPSS_FOR_DOUBLES)
		{
			if (srcp.is_memory())
				a.cvtsd2ss(dstreg, MABS(srcp.memory()));                                // cvtsd2ss dstreg,[srcp]
			else if (srcp.is_float_register())
				a.cvtsd2ss(dstreg, Xmm(srcp.freg()));                                   // cvtsd2ss dstreg,srcp
			a.rcpss(dstreg, dstreg);                                                    // rcpss dstreg,dstreg
			a.cvtss2sd(dstreg, dstreg);                                                 // cvtss2sd dstreg,dstreg
			movsd_p64_r128(a, dstp, dstreg);                                            // movsd dstp,dstreg
		}
		else
		{
			a.movsd(xmm1, MABS(&m_near.double1));                                       // movsd xmm1,1.0
			if (srcp.is_memory())
				a.divsd(xmm1, MABS(srcp.memory()));                                     // divsd xmm1,[srcp]
			else if (srcp.is_float_register())
				a.divsd(xmm1, Xmm(srcp.freg()));                                        // divsd xmm1,srcp
			movsd_p64_r128(a, dstp, xmm1);                                              // movsd dstp,xmm1
		}
	}
}


//-------------------------------------------------
//  op_frsqrt - process a FRSQRT opcode
//-------------------------------------------------

void drcbe_x64::op_frsqrt(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0);

	// 32-bit form
	if (inst.size() == 4)
	{
		if (USE_RSQRTSS_FOR_SINGLES)
		{
			if (srcp.is_memory())
				a.rsqrtss(dstreg, MABS(srcp.memory()));                                 // rsqrtss dstreg,[srcp]
			else if (srcp.is_float_register())
				a.rsqrtss(dstreg, Xmm(srcp.freg()));                                    // rsqrtss dstreg,srcp
		}
		else
		{
			if (srcp.is_memory())
				a.sqrtss(xmm1, MABS(srcp.memory()));                                    // sqrtss xmm1,[srcp]
			else if (srcp.is_float_register())
				a.sqrtss(xmm1, Xmm(srcp.freg()));                                       // sqrtss xmm1,srcp
			a.movss(dstreg, MABS(&m_near.single1));                                     // movss dstreg,1.0
			a.divss(dstreg, xmm1);                                                      // divss dstreg,xmm1
		}
		movss_p32_r128(a, dstp, dstreg);                                                // movss dstp,dstreg
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		if (USE_RSQRTSS_FOR_DOUBLES)
		{
			if (srcp.is_memory())
				a.cvtsd2ss(dstreg, MABS(srcp.memory()));                                // cvtsd2ss dstreg,[srcp]
			else if (srcp.is_float_register())
				a.cvtsd2ss(dstreg, Xmm(srcp.freg()));                                   // cvtsd2ss dstreg,srcp
			a.rsqrtss(dstreg, dstreg);                                                  // rsqrtss dstreg,dstreg
			a.cvtss2sd(dstreg, dstreg);                                                 // cvtss2sd dstreg,dstreg
		}
		else
		{
			if (srcp.is_memory())
				a.sqrtsd(xmm1, MABS(srcp.memory()));                                    // sqrtsd xmm1,[srcp]
			else if (srcp.is_float_register())
				a.sqrtsd(xmm1, Xmm(srcp.freg()));                                       // sqrtsd xmm1,srcp
			a.movsd(dstreg, MABS(&m_near.double1));                                     // movsd dstreg,1.0
			a.divsd(dstreg, xmm1);                                                      // divsd dstreg,xmm1
		}
		movsd_p64_r128(a, dstp, dstreg);                                                // movsd dstp,dstreg
	}
}


//-------------------------------------------------
//  op_fcopyi - process a FCOPYI opcode
//-------------------------------------------------

void drcbe_x64::op_fcopyi(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MF);
	be_parameter srcp(*this, inst.param(1), PTYPE_MR);

	// pick a target register for the general case
	Xmm dstreg = dstp.select_register(xmm0);

	// 32-bit form
	if (inst.size() == 4)
	{
		if (srcp.is_memory())
		{
			a.movd(dstreg, MABS(srcp.memory()));                                        // movd     dstreg,[srcp]
			movss_p32_r128(a, dstp, dstreg);                                            // movss    dstp,dstreg
		}
		else
		{
			if (dstp.is_memory())
			{
				mov_param_reg(a, dstp, Gpd(srcp.ireg()));                               // mov      dstp,srcp
			}
			else
			{
				a.movd(dstreg, Gpd(srcp.ireg()));                                       // movd     dstreg,srcp
				movss_p32_r128(a, dstp, dstreg);                                        // movss    dstp,dstreg
			}
		}

	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		if (srcp.is_memory())
		{
			a.movq(dstreg, MABS(srcp.memory()));                                        // movq     dstreg,[srcp]
			movsd_p64_r128(a, dstp, dstreg);                                            // movsd    dstp,dstreg
		}
		else
		{
			if (dstp.is_memory())
			{
				mov_param_reg(a, dstp, Gpq(srcp.ireg()));                               // mov      dstp,srcp
			}
			else
			{
				a.movq(dstreg, Gpq(srcp.ireg()));                                       // movq     dstreg,srcp
				movsd_p64_r128(a, dstp, dstreg);                                        // movsd    dstp,dstreg
			}
		}

	}
}


//-------------------------------------------------
//  op_icopyf - process a ICOPYF opcode
//-------------------------------------------------

void drcbe_x64::op_icopyf(Assembler &a, const instruction &inst)
{
	// validate instruction
	assert(inst.size() == 4 || inst.size() == 8);
	assert_no_condition(inst);
	assert_no_flags(inst);

	// normalize parameters
	be_parameter dstp(*this, inst.param(0), PTYPE_MR);
	be_parameter srcp(*this, inst.param(1), PTYPE_MF);

	// 32-bit form
	if (inst.size() == 4)
	{
		if (srcp.is_memory())
		{
			Gp dstreg = dstp.select_register(eax);
			a.mov(dstreg, MABS(srcp.memory()));                                         // mov      dstreg,[srcp]
			mov_param_reg(a, dstp, dstreg);                                             // mov      dstp,dstreg
		}
		else
		{
			if (dstp.is_memory())
			{
				a.movd(MABS(dstp.memory()), Xmm(srcp.freg()));                          // movd     dstp,srcp
			}
			else
			{
				a.movd(Gpd(dstp.ireg()), Xmm(srcp.freg()));                             // movd     dstp,srcp
			}
		}
	}

	// 64-bit form
	else if (inst.size() == 8)
	{
		if (srcp.is_memory())
		{
			Gp dstreg = dstp.select_register(rax);
			a.mov(dstreg, MABS(srcp.memory()));                                         // mov      dstreg,[srcp]
			mov_param_reg(a, dstp, dstreg);                                             // mov      dstp,dstreg
		}
		else
		{
			if (dstp.is_memory())
			{
				a.movq(MABS(dstp.memory()), Xmm(srcp.freg()));                          // movq     dstp,srcp
			}
			else
			{
				a.movq(Gpq(dstp.ireg()), Xmm(srcp.freg()));                             // movq     dstp,srcp
			}
		}
	}
}

} // namespace drc