------------------------------------------------------------------------------ -- DynASM PPC/PPC64 module. -- -- Copyright (C) 2005-2022 Mike Pall. All rights reserved. -- See dynasm.lua for full copyright notice. -- -- Support for various extensions contributed by Caio Souza Oliveira. ------------------------------------------------------------------------------ -- Module information: local _info = { arch = "ppc", description = "DynASM PPC module", version = "1.5.0", vernum = 10500, release = "2021-05-02", author = "Mike Pall", license = "MIT", } -- Exported glue functions for the arch-specific module. local _M = { _info = _info } -- Cache library functions. local type, tonumber, pairs, ipairs = type, tonumber, pairs, ipairs local assert, setmetatable = assert, setmetatable local _s = string local sub, format, byte, char = _s.sub, _s.format, _s.byte, _s.char local match, gmatch = _s.match, _s.gmatch local concat, sort = table.concat, table.sort local bit = bit or require("bit") local band, shl, shr, sar = bit.band, bit.lshift, bit.rshift, bit.arshift local tohex = bit.tohex -- Inherited tables and callbacks. local g_opt, g_arch local wline, werror, wfatal, wwarn -- Action name list. -- CHECK: Keep this in sync with the C code! local action_names = { "STOP", "SECTION", "ESC", "REL_EXT", "ALIGN", "REL_LG", "LABEL_LG", "REL_PC", "LABEL_PC", "IMM", "IMMSH" } -- Maximum number of section buffer positions for dasm_put(). -- CHECK: Keep this in sync with the C code! local maxsecpos = 25 -- Keep this low, to avoid excessively long C lines. -- Action name -> action number. local map_action = {} for n,name in ipairs(action_names) do map_action[name] = n-1 end -- Action list buffer. local actlist = {} -- Argument list for next dasm_put(). Start with offset 0 into action list. local actargs = { 0 } -- Current number of section buffer positions for dasm_put(). local secpos = 1 ------------------------------------------------------------------------------ -- Dump action names and numbers. local function dumpactions(out) out:write("DynASM encoding engine action codes:\n") for n,name in ipairs(action_names) do local num = map_action[name] out:write(format(" %-10s %02X %d\n", name, num, num)) end out:write("\n") end -- Write action list buffer as a huge static C array. local function writeactions(out, name) local nn = #actlist if nn == 0 then nn = 1; actlist[0] = map_action.STOP end out:write("static const unsigned int ", name, "[", nn, "] = {\n") for i = 1,nn-1 do assert(out:write("0x", tohex(actlist[i]), ",\n")) end assert(out:write("0x", tohex(actlist[nn]), "\n};\n\n")) end ------------------------------------------------------------------------------ -- Add word to action list. local function wputxw(n) assert(n >= 0 and n <= 0xffffffff and n % 1 == 0, "word out of range") actlist[#actlist+1] = n end -- Add action to list with optional arg. Advance buffer pos, too. local function waction(action, val, a, num) local w = assert(map_action[action], "bad action name `"..action.."'") wputxw(w * 0x10000 + (val or 0)) if a then actargs[#actargs+1] = a end if a or num then secpos = secpos + (num or 1) end end -- Flush action list (intervening C code or buffer pos overflow). local function wflush(term) if #actlist == actargs[1] then return end -- Nothing to flush. if not term then waction("STOP") end -- Terminate action list. wline(format("dasm_put(Dst, %s);", concat(actargs, ", ")), true) actargs = { #actlist } -- Actionlist offset is 1st arg to next dasm_put(). secpos = 1 -- The actionlist offset occupies a buffer position, too. end -- Put escaped word. local function wputw(n) if n <= 0xffffff then waction("ESC") end wputxw(n) end -- Reserve position for word. local function wpos() local pos = #actlist+1 actlist[pos] = "" return pos end -- Store word to reserved position. local function wputpos(pos, n) assert(n >= 0 and n <= 0xffffffff and n % 1 == 0, "word out of range") actlist[pos] = n end ------------------------------------------------------------------------------ -- Global label name -> global label number. With auto assignment on 1st use. local next_global = 20 local map_global = setmetatable({}, { __index = function(t, name) if not match(name, "^[%a_][%w_]*$") then werror("bad global label") end local n = next_global if n > 2047 then werror("too many global labels") end next_global = n + 1 t[name] = n return n end}) -- Dump global labels. local function dumpglobals(out, lvl) local t = {} for name, n in pairs(map_global) do t[n] = name end out:write("Global labels:\n") for i=20,next_global-1 do out:write(format(" %s\n", t[i])) end out:write("\n") end -- Write global label enum. local function writeglobals(out, prefix) local t = {} for name, n in pairs(map_global) do t[n] = name end out:write("enum {\n") for i=20,next_global-1 do out:write(" ", prefix, t[i], ",\n") end out:write(" ", prefix, "_MAX\n};\n") end -- Write global label names. local function writeglobalnames(out, name) local t = {} for name, n in pairs(map_global) do t[n] = name end out:write("static const char *const ", name, "[] = {\n") for i=20,next_global-1 do out:write(" \"", t[i], "\",\n") end out:write(" (const char *)0\n};\n") end ------------------------------------------------------------------------------ -- Extern label name -> extern label number. With auto assignment on 1st use. local next_extern = 0 local map_extern_ = {} local map_extern = setmetatable({}, { __index = function(t, name) -- No restrictions on the name for now. local n = next_extern if n > 2047 then werror("too many extern labels") end next_extern = n + 1 t[name] = n map_extern_[n] = name return n end}) -- Dump extern labels. local function dumpexterns(out, lvl) out:write("Extern labels:\n") for i=0,next_extern-1 do out:write(format(" %s\n", map_extern_[i])) end out:write("\n") end -- Write extern label names. local function writeexternnames(out, name) out:write("static const char *const ", name, "[] = {\n") for i=0,next_extern-1 do out:write(" \"", map_extern_[i], "\",\n") end out:write(" (const char *)0\n};\n") end ------------------------------------------------------------------------------ -- Arch-specific maps. local map_archdef = { sp = "r1" } -- Ext. register name -> int. name. local map_type = {} -- Type name -> { ctype, reg } local ctypenum = 0 -- Type number (for Dt... macros). -- Reverse defines for registers. function _M.revdef(s) if s == "r1" then return "sp" end return s end local map_cond = { lt = 0, gt = 1, eq = 2, so = 3, ge = 4, le = 5, ne = 6, ns = 7, } ------------------------------------------------------------------------------ local map_op, op_template local function op_alias(opname, f) return function(params, nparams) if not params then return "-> "..opname:sub(1, -3) end f(params, nparams) op_template(params, map_op[opname], nparams) end end -- Template strings for PPC instructions. map_op = { tdi_3 = "08000000ARI", twi_3 = "0c000000ARI", mulli_3 = "1c000000RRI", subfic_3 = "20000000RRI", cmplwi_3 = "28000000XRU", cmplwi_2 = "28000000-RU", cmpldi_3 = "28200000XRU", cmpldi_2 = "28200000-RU", cmpwi_3 = "2c000000XRI", cmpwi_2 = "2c000000-RI", cmpdi_3 = "2c200000XRI", cmpdi_2 = "2c200000-RI", addic_3 = "30000000RRI", ["addic._3"] = "34000000RRI", addi_3 = "38000000RR0I", li_2 = "38000000RI", la_2 = "38000000RD", addis_3 = "3c000000RR0I", lis_2 = "3c000000RI", lus_2 = "3c000000RU", bc_3 = "40000000AAK", bcl_3 = "40000001AAK", bdnz_1 = "42000000K", bdz_1 = "42400000K", sc_0 = "44000000", b_1 = "48000000J", bl_1 = "48000001J", rlwimi_5 = "50000000RR~AAA.", rlwinm_5 = "54000000RR~AAA.", rlwnm_5 = "5c000000RR~RAA.", ori_3 = "60000000RR~U", nop_0 = "60000000", oris_3 = "64000000RR~U", xori_3 = "68000000RR~U", xoris_3 = "6c000000RR~U", ["andi._3"] = "70000000RR~U", ["andis._3"] = "74000000RR~U", lwz_2 = "80000000RD", lwzu_2 = "84000000RD", lbz_2 = "88000000RD", lbzu_2 = "8c000000RD", stw_2 = "90000000RD", stwu_2 = "94000000RD", stb_2 = "98000000RD", stbu_2 = "9c000000RD", lhz_2 = "a0000000RD", lhzu_2 = "a4000000RD", lha_2 = "a8000000RD", lhau_2 = "ac000000RD", sth_2 = "b0000000RD", sthu_2 = "b4000000RD", lmw_2 = "b8000000RD", stmw_2 = "bc000000RD", lfs_2 = "c0000000FD", lfsu_2 = "c4000000FD", lfd_2 = "c8000000FD", lfdu_2 = "cc000000FD", stfs_2 = "d0000000FD", stfsu_2 = "d4000000FD", stfd_2 = "d8000000FD", stfdu_2 = "dc000000FD", ld_2 = "e8000000RD", -- NYI: displacement must be divisible by 4. ldu_2 = "e8000001RD", lwa_2 = "e8000002RD", std_2 = "f8000000RD", stdu_2 = "f8000001RD", subi_3 = op_alias("addi_3", function(p) p[3] = "-("..p[3]..")" end), subis_3 = op_alias("addis_3", function(p) p[3] = "-("..p[3]..")" end), subic_3 = op_alias("addic_3", function(p) p[3] = "-("..p[3]..")" end), ["subic._3"] = op_alias("addic._3", function(p) p[3] = "-("..p[3]..")" end), rotlwi_3 = op_alias("rlwinm_5", function(p) p[4] = "0"; p[5] = "31" end), rotrwi_3 = op_alias("rlwinm_5", function(p) p[3] = "32-("..p[3]..")"; p[4] = "0"; p[5] = "31" end), rotlw_3 = op_alias("rlwnm_5", function(p) p[4] = "0"; p[5] = "31" end), slwi_3 = op_alias("rlwinm_5", function(p) p[5] = "31-("..p[3]..")"; p[4] = "0" end), srwi_3 = op_alias("rlwinm_5", function(p) p[4] = p[3]; p[3] = "32-("..p[3]..")"; p[5] = "31" end), clrlwi_3 = op_alias("rlwinm_5", function(p) p[4] = p[3]; p[3] = "0"; p[5] = "31" end), clrrwi_3 = op_alias("rlwinm_5", function(p) p[5] = "31-("..p[3]..")"; p[3] = "0"; p[4] = "0" end), -- Primary opcode 4: mulhhwu_3 = "10000010RRR.", machhwu_3 = "10000018RRR.", mulhhw_3 = "10000050RRR.", nmachhw_3 = "1000005cRRR.", machhwsu_3 = "10000098RRR.", machhws_3 = "100000d8RRR.", nmachhws_3 = "100000dcRRR.", mulchwu_3 = "10000110RRR.", macchwu_3 = "10000118RRR.", mulchw_3 = "10000150RRR.", macchw_3 = "10000158RRR.", nmacchw_3 = "1000015cRRR.", macchwsu_3 = "10000198RRR.", macchws_3 = "100001d8RRR.", nmacchws_3 = "100001dcRRR.", mullhw_3 = "10000350RRR.", maclhw_3 = "10000358RRR.", nmaclhw_3 = "1000035cRRR.", maclhwsu_3 = "10000398RRR.", maclhws_3 = "100003d8RRR.", nmaclhws_3 = "100003dcRRR.", machhwuo_3 = "10000418RRR.", nmachhwo_3 = "1000045cRRR.", machhwsuo_3 = "10000498RRR.", machhwso_3 = "100004d8RRR.", nmachhwso_3 = "100004dcRRR.", macchwuo_3 = "10000518RRR.", macchwo_3 = "10000558RRR.", nmacchwo_3 = "1000055cRRR.", macchwsuo_3 = "10000598RRR.", macchwso_3 = "100005d8RRR.", nmacchwso_3 = "100005dcRRR.", maclhwo_3 = "10000758RRR.", nmaclhwo_3 = "1000075cRRR.", maclhwsuo_3 = "10000798RRR.", maclhwso_3 = "100007d8RRR.", nmaclhwso_3 = "100007dcRRR.", vaddubm_3 = "10000000VVV", vmaxub_3 = "10000002VVV", vrlb_3 = "10000004VVV", vcmpequb_3 = "10000006VVV", vmuloub_3 = "10000008VVV", vaddfp_3 = "1000000aVVV", vmrghb_3 = "1000000cVVV", vpkuhum_3 = "1000000eVVV", vmhaddshs_4 = "10000020VVVV", vmhraddshs_4 = "10000021VVVV", vmladduhm_4 = "10000022VVVV", vmsumubm_4 = "10000024VVVV", vmsummbm_4 = "10000025VVVV", vmsumuhm_4 = "10000026VVVV", vmsumuhs_4 = "10000027VVVV", vmsumshm_4 = "10000028VVVV", vmsumshs_4 = "10000029VVVV", vsel_4 = "1000002aVVVV", vperm_4 = "1000002bVVVV", vsldoi_4 = "1000002cVVVP", vpermxor_4 = "1000002dVVVV", vmaddfp_4 = "1000002eVVVV~", vnmsubfp_4 = "1000002fVVVV~", vaddeuqm_4 = "1000003cVVVV", vaddecuq_4 = "1000003dVVVV", vsubeuqm_4 = "1000003eVVVV", vsubecuq_4 = "1000003fVVVV", vadduhm_3 = "10000040VVV", vmaxuh_3 = "10000042VVV", vrlh_3 = "10000044VVV", vcmpequh_3 = "10000046VVV", vmulouh_3 = "10000048VVV", vsubfp_3 = "1000004aVVV", vmrghh_3 = "1000004cVVV", vpkuwum_3 = "1000004eVVV", vadduwm_3 = "10000080VVV", vmaxuw_3 = "10000082VVV", vrlw_3 = "10000084VVV", vcmpequw_3 = "10000086VVV", vmulouw_3 = "10000088VVV", vmuluwm_3 = "10000089VVV", vmrghw_3 = "1000008cVVV", vpkuhus_3 = "1000008eVVV", vaddudm_3 = "100000c0VVV", vmaxud_3 = "100000c2VVV", vrld_3 = "100000c4VVV", vcmpeqfp_3 = "100000c6VVV", vcmpequd_3 = "100000c7VVV", vpkuwus_3 = "100000ceVVV", vadduqm_3 = "10000100VVV", vmaxsb_3 = "10000102VVV", vslb_3 = "10000104VVV", vmulosb_3 = "10000108VVV", vrefp_2 = "1000010aV-V", vmrglb_3 = "1000010cVVV", vpkshus_3 = "1000010eVVV", vaddcuq_3 = "10000140VVV", vmaxsh_3 = "10000142VVV", vslh_3 = "10000144VVV", vmulosh_3 = "10000148VVV", vrsqrtefp_2 = "1000014aV-V", vmrglh_3 = "1000014cVVV", vpkswus_3 = "1000014eVVV", vaddcuw_3 = "10000180VVV", vmaxsw_3 = "10000182VVV", vslw_3 = "10000184VVV", vmulosw_3 = "10000188VVV", vexptefp_2 = "1000018aV-V", vmrglw_3 = "1000018cVVV", vpkshss_3 = "1000018eVVV", vmaxsd_3 = "100001c2VVV", vsl_3 = "100001c4VVV", vcmpgefp_3 = "100001c6VVV", vlogefp_2 = "100001caV-V", vpkswss_3 = "100001ceVVV", vadduhs_3 = "10000240VVV", vminuh_3 = "10000242VVV", vsrh_3 = "10000244VVV", vcmpgtuh_3 = "10000246VVV", vmuleuh_3 = "10000248VVV", vrfiz_2 = "1000024aV-V", vsplth_3 = "1000024cVV3", vupkhsh_2 = "1000024eV-V", vminuw_3 = "10000282VVV", vminud_3 = "100002c2VVV", vcmpgtud_3 = "100002c7VVV", vrfim_2 = "100002caV-V", vcmpgtsb_3 = "10000306VVV", vcfux_3 = "1000030aVVA~", vaddshs_3 = "10000340VVV", vminsh_3 = "10000342VVV", vsrah_3 = "10000344VVV", vcmpgtsh_3 = "10000346VVV", vmulesh_3 = "10000348VVV", vcfsx_3 = "1000034aVVA~", vspltish_2 = "1000034cVS", vupkhpx_2 = "1000034eV-V", vaddsws_3 = "10000380VVV", vminsw_3 = "10000382VVV", vsraw_3 = "10000384VVV", vcmpgtsw_3 = "10000386VVV", vmulesw_3 = "10000388VVV", vctuxs_3 = "1000038aVVA~", vspltisw_2 = "1000038cVS", vminsd_3 = "100003c2VVV", vsrad_3 = "100003c4VVV", vcmpbfp_3 = "100003c6VVV", vcmpgtsd_3 = "100003c7VVV", vctsxs_3 = "100003caVVA~", vupklpx_2 = "100003ceV-V", vsububm_3 = "10000400VVV", ["bcdadd._4"] = "10000401VVVy.", vavgub_3 = "10000402VVV", vand_3 = "10000404VVV", ["vcmpequb._3"] = "10000406VVV", vmaxfp_3 = "1000040aVVV", vsubuhm_3 = "10000440VVV", ["bcdsub._4"] = "10000441VVVy.", vavguh_3 = "10000442VVV", vandc_3 = "10000444VVV", ["vcmpequh._3"] = "10000446VVV", vminfp_3 = "1000044aVVV", vpkudum_3 = "1000044eVVV", vsubuwm_3 = "10000480VVV", vavguw_3 = "10000482VVV", vor_3 = "10000484VVV", ["vcmpequw._3"] = "10000486VVV", vpmsumw_3 = "10000488VVV", ["vcmpeqfp._3"] = "100004c6VVV", ["vcmpequd._3"] = "100004c7VVV", vpkudus_3 = "100004ceVVV", vavgsb_3 = "10000502VVV", vavgsh_3 = "10000542VVV", vorc_3 = "10000544VVV", vbpermq_3 = "1000054cVVV", vpksdus_3 = "1000054eVVV", vavgsw_3 = "10000582VVV", vsld_3 = "100005c4VVV", ["vcmpgefp._3"] = "100005c6VVV", vpksdss_3 = "100005ceVVV", vsububs_3 = "10000600VVV", mfvscr_1 = "10000604V--", vsum4ubs_3 = "10000608VVV", vsubuhs_3 = "10000640VVV", mtvscr_1 = "10000644--V", ["vcmpgtuh._3"] = "10000646VVV", vsum4shs_3 = "10000648VVV", vupkhsw_2 = "1000064eV-V", vsubuws_3 = "10000680VVV", vshasigmaw_4 = "10000682VVYp", veqv_3 = "10000684VVV", vsum2sws_3 = "10000688VVV", vmrgow_3 = "1000068cVVV", vshasigmad_4 = "100006c2VVYp", vsrd_3 = "100006c4VVV", ["vcmpgtud._3"] = "100006c7VVV", vupklsw_2 = "100006ceV-V", vupkslw_2 = "100006ceV-V", vsubsbs_3 = "10000700VVV", vclzb_2 = "10000702V-V", vpopcntb_2 = "10000703V-V", ["vcmpgtsb._3"] = "10000706VVV", vsum4sbs_3 = "10000708VVV", vsubshs_3 = "10000740VVV", vclzh_2 = "10000742V-V", vpopcnth_2 = "10000743V-V", ["vcmpgtsh._3"] = "10000746VVV", vsubsws_3 = "10000780VVV", vclzw_2 = "10000782V-V", vpopcntw_2 = "10000783V-V", ["vcmpgtsw._3"] = "10000786VVV", vsumsws_3 = "10000788VVV", vmrgew_3 = "1000078cVVV", vclzd_2 = "100007c2V-V", vpopcntd_2 = "100007c3V-V", ["vcmpbfp._3"] = "100007c6VVV", ["vcmpgtsd._3"] = "100007c7VVV", -- Primary opcode 19: mcrf_2 = "4c000000XX", isync_0 = "4c00012c", crnor_3 = "4c000042CCC", crnot_2 = "4c000042CC=", crandc_3 = "4c000102CCC", crxor_3 = "4c000182CCC", crclr_1 = "4c000182C==", crnand_3 = "4c0001c2CCC", crand_3 = "4c000202CCC", creqv_3 = "4c000242CCC", crset_1 = "4c000242C==", crorc_3 = "4c000342CCC", cror_3 = "4c000382CCC", crmove_2 = "4c000382CC=", bclr_2 = "4c000020AA", bclrl_2 = "4c000021AA", bcctr_2 = "4c000420AA", bcctrl_2 = "4c000421AA", bctar_2 = "4c000460AA", bctarl_2 = "4c000461AA", blr_0 = "4e800020", blrl_0 = "4e800021", bctr_0 = "4e800420", bctrl_0 = "4e800421", -- Primary opcode 31: cmpw_3 = "7c000000XRR", cmpw_2 = "7c000000-RR", cmpd_3 = "7c200000XRR", cmpd_2 = "7c200000-RR", tw_3 = "7c000008ARR", lvsl_3 = "7c00000cVRR", subfc_3 = "7c000010RRR.", subc_3 = "7c000010RRR~.", mulhdu_3 = "7c000012RRR.", addc_3 = "7c000014RRR.", mulhwu_3 = "7c000016RRR.", isel_4 = "7c00001eRRRC", isellt_3 = "7c00001eRRR", iselgt_3 = "7c00005eRRR", iseleq_3 = "7c00009eRRR", mfcr_1 = "7c000026R", mfocrf_2 = "7c100026RG", mtcrf_2 = "7c000120GR", mtocrf_2 = "7c100120GR", lwarx_3 = "7c000028RR0R", ldx_3 = "7c00002aRR0R", lwzx_3 = "7c00002eRR0R", slw_3 = "7c000030RR~R.", cntlzw_2 = "7c000034RR~", sld_3 = "7c000036RR~R.", and_3 = "7c000038RR~R.", cmplw_3 = "7c000040XRR", cmplw_2 = "7c000040-RR", cmpld_3 = "7c200040XRR", cmpld_2 = "7c200040-RR", lvsr_3 = "7c00004cVRR", subf_3 = "7c000050RRR.", sub_3 = "7c000050RRR~.", lbarx_3 = "7c000068RR0R", ldux_3 = "7c00006aRR0R", dcbst_2 = "7c00006c-RR", lwzux_3 = "7c00006eRR0R", cntlzd_2 = "7c000074RR~", andc_3 = "7c000078RR~R.", td_3 = "7c000088ARR", lvewx_3 = "7c00008eVRR", mulhd_3 = "7c000092RRR.", addg6s_3 = "7c000094RRR", mulhw_3 = "7c000096RRR.", dlmzb_3 = "7c00009cRR~R.", ldarx_3 = "7c0000a8RR0R", dcbf_2 = "7c0000ac-RR", lbzx_3 = "7c0000aeRR0R", lvx_3 = "7c0000ceVRR", neg_2 = "7c0000d0RR.", lharx_3 = "7c0000e8RR0R", lbzux_3 = "7c0000eeRR0R", popcntb_2 = "7c0000f4RR~", not_2 = "7c0000f8RR~%.", nor_3 = "7c0000f8RR~R.", stvebx_3 = "7c00010eVRR", subfe_3 = "7c000110RRR.", sube_3 = "7c000110RRR~.", adde_3 = "7c000114RRR.", stdx_3 = "7c00012aRR0R", ["stwcx._3"] = "7c00012dRR0R.", stwx_3 = "7c00012eRR0R", prtyw_2 = "7c000134RR~", stvehx_3 = "7c00014eVRR", stdux_3 = "7c00016aRR0R", ["stqcx._3"] = "7c00016dR:R0R.", stwux_3 = "7c00016eRR0R", prtyd_2 = "7c000174RR~", stvewx_3 = "7c00018eVRR", subfze_2 = "7c000190RR.", addze_2 = "7c000194RR.", ["stdcx._3"] = "7c0001adRR0R.", stbx_3 = "7c0001aeRR0R", stvx_3 = "7c0001ceVRR", subfme_2 = "7c0001d0RR.", mulld_3 = "7c0001d2RRR.", addme_2 = "7c0001d4RR.", mullw_3 = "7c0001d6RRR.", dcbtst_2 = "7c0001ec-RR", stbux_3 = "7c0001eeRR0R", bpermd_3 = "7c0001f8RR~R", lvepxl_3 = "7c00020eVRR", add_3 = "7c000214RRR.", lqarx_3 = "7c000228R:R0R", dcbt_2 = "7c00022c-RR", lhzx_3 = "7c00022eRR0R", cdtbcd_2 = "7c000234RR~", eqv_3 = "7c000238RR~R.", lvepx_3 = "7c00024eVRR", eciwx_3 = "7c00026cRR0R", lhzux_3 = "7c00026eRR0R", cbcdtd_2 = "7c000274RR~", xor_3 = "7c000278RR~R.", mfspefscr_1 = "7c0082a6R", mfxer_1 = "7c0102a6R", mflr_1 = "7c0802a6R", mfctr_1 = "7c0902a6R", lwax_3 = "7c0002aaRR0R", lhax_3 = "7c0002aeRR0R", mftb_1 = "7c0c42e6R", mftbu_1 = "7c0d42e6R", lvxl_3 = "7c0002ceVRR", lwaux_3 = "7c0002eaRR0R", lhaux_3 = "7c0002eeRR0R", popcntw_2 = "7c0002f4RR~", divdeu_3 = "7c000312RRR.", divweu_3 = "7c000316RRR.", sthx_3 = "7c00032eRR0R", orc_3 = "7c000338RR~R.", ecowx_3 = "7c00036cRR0R", sthux_3 = "7c00036eRR0R", or_3 = "7c000378RR~R.", mr_2 = "7c000378RR~%.", divdu_3 = "7c000392RRR.", divwu_3 = "7c000396RRR.", mtspefscr_1 = "7c0083a6R", mtxer_1 = "7c0103a6R", mtlr_1 = "7c0803a6R", mtctr_1 = "7c0903a6R", dcbi_2 = "7c0003ac-RR", nand_3 = "7c0003b8RR~R.", dsn_2 = "7c0003c6-RR", stvxl_3 = "7c0003ceVRR", divd_3 = "7c0003d2RRR.", divw_3 = "7c0003d6RRR.", popcntd_2 = "7c0003f4RR~", cmpb_3 = "7c0003f8RR~R.", mcrxr_1 = "7c000400X", lbdx_3 = "7c000406RRR", subfco_3 = "7c000410RRR.", subco_3 = "7c000410RRR~.", addco_3 = "7c000414RRR.", ldbrx_3 = "7c000428RR0R", lswx_3 = "7c00042aRR0R", lwbrx_3 = "7c00042cRR0R", lfsx_3 = "7c00042eFR0R", srw_3 = "7c000430RR~R.", srd_3 = "7c000436RR~R.", lhdx_3 = "7c000446RRR", subfo_3 = "7c000450RRR.", subo_3 = "7c000450RRR~.", lfsux_3 = "7c00046eFR0R", lwdx_3 = "7c000486RRR", lswi_3 = "7c0004aaRR0A", sync_0 = "7c0004ac", lwsync_0 = "7c2004ac", ptesync_0 = "7c4004ac", lfdx_3 = "7c0004aeFR0R", lddx_3 = "7c0004c6RRR", nego_2 = "7c0004d0RR.", lfdux_3 = "7c0004eeFR0R", stbdx_3 = "7c000506RRR", subfeo_3 = "7c000510RRR.", subeo_3 = "7c000510RRR~.", addeo_3 = "7c000514RRR.", stdbrx_3 = "7c000528RR0R", stswx_3 = "7c00052aRR0R", stwbrx_3 = "7c00052cRR0R", stfsx_3 = "7c00052eFR0R", sthdx_3 = "7c000546RRR", ["stbcx._3"] = "7c00056dRRR", stfsux_3 = "7c00056eFR0R", stwdx_3 = "7c000586RRR", subfzeo_2 = "7c000590RR.", addzeo_2 = "7c000594RR.", stswi_3 = "7c0005aaRR0A", ["sthcx._3"] = "7c0005adRRR", stfdx_3 = "7c0005aeFR0R", stddx_3 = "7c0005c6RRR", subfmeo_2 = "7c0005d0RR.", mulldo_3 = "7c0005d2RRR.", addmeo_2 = "7c0005d4RR.", mullwo_3 = "7c0005d6RRR.", dcba_2 = "7c0005ec-RR", stfdux_3 = "7c0005eeFR0R", stvepxl_3 = "7c00060eVRR", addo_3 = "7c000614RRR.", lhbrx_3 = "7c00062cRR0R", lfdpx_3 = "7c00062eF:RR", sraw_3 = "7c000630RR~R.", srad_3 = "7c000634RR~R.", lfddx_3 = "7c000646FRR", stvepx_3 = "7c00064eVRR", srawi_3 = "7c000670RR~A.", sradi_3 = "7c000674RR~H.", eieio_0 = "7c0006ac", lfiwax_3 = "7c0006aeFR0R", divdeuo_3 = "7c000712RRR.", divweuo_3 = "7c000716RRR.", sthbrx_3 = "7c00072cRR0R", stfdpx_3 = "7c00072eF:RR", extsh_2 = "7c000734RR~.", stfddx_3 = "7c000746FRR", divdeo_3 = "7c000752RRR.", divweo_3 = "7c000756RRR.", extsb_2 = "7c000774RR~.", divduo_3 = "7c000792RRR.", divwou_3 = "7c000796RRR.", icbi_2 = "7c0007ac-RR", stfiwx_3 = "7c0007aeFR0R", extsw_2 = "7c0007b4RR~.", divdo_3 = "7c0007d2RRR.", divwo_3 = "7c0007d6RRR.", dcbz_2 = "7c0007ec-RR", ["tbegin._1"] = "7c00051d1", ["tbegin._0"] = "7c00051d", ["tend._1"] = "7c00055dY", ["tend._0"] = "7c00055d", ["tendall._0"] = "7e00055d", tcheck_1 = "7c00059cX", ["tsr._1"] = "7c0005dd1", ["tsuspend._0"] = "7c0005dd", ["tresume._0"] = "7c2005dd", ["tabortwc._3"] = "7c00061dARR", ["tabortdc._3"] = "7c00065dARR", ["tabortwci._3"] = "7c00069dARS", ["tabortdci._3"] = "7c0006ddARS", ["tabort._1"] = "7c00071d-R-", ["treclaim._1"] = "7c00075d-R", ["trechkpt._0"] = "7c0007dd", lxsiwzx_3 = "7c000018QRR", lxsiwax_3 = "7c000098QRR", mfvsrd_2 = "7c000066-Rq", mfvsrwz_2 = "7c0000e6-Rq", stxsiwx_3 = "7c000118QRR", mtvsrd_2 = "7c000166QR", mtvsrwa_2 = "7c0001a6QR", lxvdsx_3 = "7c000298QRR", lxsspx_3 = "7c000418QRR", lxsdx_3 = "7c000498QRR", stxsspx_3 = "7c000518QRR", stxsdx_3 = "7c000598QRR", lxvw4x_3 = "7c000618QRR", lxvd2x_3 = "7c000698QRR", stxvw4x_3 = "7c000718QRR", stxvd2x_3 = "7c000798QRR", -- Primary opcode 30: rldicl_4 = "78000000RR~HM.", rldicr_4 = "78000004RR~HM.", rldic_4 = "78000008RR~HM.", rldimi_4 = "7800000cRR~HM.", rldcl_4 = "78000010RR~RM.", rldcr_4 = "78000012RR~RM.", rotldi_3 = op_alias("rldicl_4", function(p) p[4] = "0" end), rotrdi_3 = op_alias("rldicl_4", function(p) p[3] = "64-("..p[3]..")"; p[4] = "0" end), rotld_3 = op_alias("rldcl_4", function(p) p[4] = "0" end), sldi_3 = op_alias("rldicr_4", function(p) p[4] = "63-("..p[3]..")" end), srdi_3 = op_alias("rldicl_4", function(p) p[4] = p[3]; p[3] = "64-("..p[3]..")" end), clrldi_3 = op_alias("rldicl_4", function(p) p[4] = p[3]; p[3] = "0" end), clrrdi_3 = op_alias("rldicr_4", function(p) p[4] = "63-("..p[3]..")"; p[3] = "0" end), -- Primary opcode 56: lq_2 = "e0000000R:D", -- NYI: displacement must be divisible by 8. -- Primary opcode 57: lfdp_2 = "e4000000F:D", -- NYI: displacement must be divisible by 4. -- Primary opcode 59: fdivs_3 = "ec000024FFF.", fsubs_3 = "ec000028FFF.", fadds_3 = "ec00002aFFF.", fsqrts_2 = "ec00002cF-F.", fres_2 = "ec000030F-F.", fmuls_3 = "ec000032FF-F.", frsqrtes_2 = "ec000034F-F.", fmsubs_4 = "ec000038FFFF~.", fmadds_4 = "ec00003aFFFF~.", fnmsubs_4 = "ec00003cFFFF~.", fnmadds_4 = "ec00003eFFFF~.", fcfids_2 = "ec00069cF-F.", fcfidus_2 = "ec00079cF-F.", dadd_3 = "ec000004FFF.", dqua_4 = "ec000006FFFZ.", dmul_3 = "ec000044FFF.", drrnd_4 = "ec000046FFFZ.", dscli_3 = "ec000084FF6.", dquai_4 = "ec000086SF~FZ.", dscri_3 = "ec0000c4FF6.", drintx_4 = "ec0000c61F~FZ.", dcmpo_3 = "ec000104XFF", dtstex_3 = "ec000144XFF", dtstdc_3 = "ec000184XF6", dtstdg_3 = "ec0001c4XF6", drintn_4 = "ec0001c61F~FZ.", dctdp_2 = "ec000204F-F.", dctfix_2 = "ec000244F-F.", ddedpd_3 = "ec000284ZF~F.", dxex_2 = "ec0002c4F-F.", dsub_3 = "ec000404FFF.", ddiv_3 = "ec000444FFF.", dcmpu_3 = "ec000504XFF", dtstsf_3 = "ec000544XFF", drsp_2 = "ec000604F-F.", dcffix_2 = "ec000644F-F.", denbcd_3 = "ec000684YF~F.", diex_3 = "ec0006c4FFF.", -- Primary opcode 60: xsaddsp_3 = "f0000000QQQ", xsmaddasp_3 = "f0000008QQQ", xxsldwi_4 = "f0000010QQQz", xsrsqrtesp_2 = "f0000028Q-Q", xssqrtsp_2 = "f000002cQ-Q", xxsel_4 = "f0000030QQQQ", xssubsp_3 = "f0000040QQQ", xsmaddmsp_3 = "f0000048QQQ", xxpermdi_4 = "f0000050QQQz", xsresp_2 = "f0000068Q-Q", xsmulsp_3 = "f0000080QQQ", xsmsubasp_3 = "f0000088QQQ", xxmrghw_3 = "f0000090QQQ", xsdivsp_3 = "f00000c0QQQ", xsmsubmsp_3 = "f00000c8QQQ", xsadddp_3 = "f0000100QQQ", xsmaddadp_3 = "f0000108QQQ", xscmpudp_3 = "f0000118XQQ", xscvdpuxws_2 = "f0000120Q-Q", xsrdpi_2 = "f0000124Q-Q", xsrsqrtedp_2 = "f0000128Q-Q", xssqrtdp_2 = "f000012cQ-Q", xssubdp_3 = "f0000140QQQ", xsmaddmdp_3 = "f0000148QQQ", xscmpodp_3 = "f0000158XQQ", xscvdpsxws_2 = "f0000160Q-Q", xsrdpiz_2 = "f0000164Q-Q", xsredp_2 = "f0000168Q-Q", xsmuldp_3 = "f0000180QQQ", xsmsubadp_3 = "f0000188QQQ", xxmrglw_3 = "f0000190QQQ", xsrdpip_2 = "f00001a4Q-Q", xstsqrtdp_2 = "f00001a8X-Q", xsrdpic_2 = "f00001acQ-Q", xsdivdp_3 = "f00001c0QQQ", xsmsubmdp_3 = "f00001c8QQQ", xsrdpim_2 = "f00001e4Q-Q", xstdivdp_3 = "f00001e8XQQ", xvaddsp_3 = "f0000200QQQ", xvmaddasp_3 = "f0000208QQQ", xvcmpeqsp_3 = "f0000218QQQ", xvcvspuxws_2 = "f0000220Q-Q", xvrspi_2 = "f0000224Q-Q", xvrsqrtesp_2 = "f0000228Q-Q", xvsqrtsp_2 = "f000022cQ-Q", xvsubsp_3 = "f0000240QQQ", xvmaddmsp_3 = "f0000248QQQ", xvcmpgtsp_3 = "f0000258QQQ", xvcvspsxws_2 = "f0000260Q-Q", xvrspiz_2 = "f0000264Q-Q", xvresp_2 = "f0000268Q-Q", xvmulsp_3 = "f0000280QQQ", xvmsubasp_3 = "f0000288QQQ", xxspltw_3 = "f0000290QQg~", xvcmpgesp_3 = "f0000298QQQ", xvcvuxwsp_2 = "f00002a0Q-Q", xvrspip_2 = "f00002a4Q-Q", xvtsqrtsp_2 = "f00002a8X-Q", xvrspic_2 = "f00002acQ-Q", xvdivsp_3 = "f00002c0QQQ", xvmsubmsp_3 = "f00002c8QQQ", xvcvsxwsp_2 = "f00002e0Q-Q", xvrspim_2 = "f00002e4Q-Q", xvtdivsp_3 = "f00002e8XQQ", xvadddp_3 = "f0000300QQQ", xvmaddadp_3 = "f0000308QQQ", xvcmpeqdp_3 = "f0000318QQQ", xvcvdpuxws_2 = "f0000320Q-Q", xvrdpi_2 = "f0000324Q-Q", xvrsqrtedp_2 = "f0000328Q-Q", xvsqrtdp_2 = "f000032cQ-Q", xvsubdp_3 = "f0000340QQQ", xvmaddmdp_3 = "f0000348QQQ", xvcmpgtdp_3 = "f0000358QQQ", xvcvdpsxws_2 = "f0000360Q-Q", xvrdpiz_2 = "f0000364Q-Q", xvredp_2 = "f0000368Q-Q", xvmuldp_3 = "f0000380QQQ", xvmsubadp_3 = "f0000388QQQ", xvcmpgedp_3 = "f0000398QQQ", xvcvuxwdp_2 = "f00003a0Q-Q", xvrdpip_2 = "f00003a4Q-Q", xvtsqrtdp_2 = "f00003a8X-Q", xvrdpic_2 = "f00003acQ-Q", xvdivdp_3 = "f00003c0QQQ", xvmsubmdp_3 = "f00003c8QQQ", xvcvsxwdp_2 = "f00003e0Q-Q", xvrdpim_2 = "f00003e4Q-Q", xvtdivdp_3 = "f00003e8XQQ", xsnmaddasp_3 = "f0000408QQQ", xxland_3 = "f0000410QQQ", xscvdpsp_2 = "f0000424Q-Q", xscvdpspn_2 = "f000042cQ-Q", xsnmaddmsp_3 = "f0000448QQQ", xxlandc_3 = "f0000450QQQ", xsrsp_2 = "f0000464Q-Q", xsnmsubasp_3 = "f0000488QQQ", xxlor_3 = "f0000490QQQ", xscvuxdsp_2 = "f00004a0Q-Q", xsnmsubmsp_3 = "f00004c8QQQ", xxlxor_3 = "f00004d0QQQ", xscvsxdsp_2 = "f00004e0Q-Q", xsmaxdp_3 = "f0000500QQQ", xsnmaddadp_3 = "f0000508QQQ", xxlnor_3 = "f0000510QQQ", xscvdpuxds_2 = "f0000520Q-Q", xscvspdp_2 = "f0000524Q-Q", xscvspdpn_2 = "f000052cQ-Q", xsmindp_3 = "f0000540QQQ", xsnmaddmdp_3 = "f0000548QQQ", xxlorc_3 = "f0000550QQQ", xscvdpsxds_2 = "f0000560Q-Q", xsabsdp_2 = "f0000564Q-Q", xscpsgndp_3 = "f0000580QQQ", xsnmsubadp_3 = "f0000588QQQ", xxlnand_3 = "f0000590QQQ", xscvuxddp_2 = "f00005a0Q-Q", xsnabsdp_2 = "f00005a4Q-Q", xsnmsubmdp_3 = "f00005c8QQQ", xxleqv_3 = "f00005d0QQQ", xscvsxddp_2 = "f00005e0Q-Q", xsnegdp_2 = "f00005e4Q-Q", xvmaxsp_3 = "f0000600QQQ", xvnmaddasp_3 = "f0000608QQQ", ["xvcmpeqsp._3"] = "f0000618QQQ", xvcvspuxds_2 = "f0000620Q-Q", xvcvdpsp_2 = "f0000624Q-Q", xvminsp_3 = "f0000640QQQ", xvnmaddmsp_3 = "f0000648QQQ", ["xvcmpgtsp._3"] = "f0000658QQQ", xvcvspsxds_2 = "f0000660Q-Q", xvabssp_2 = "f0000664Q-Q", xvcpsgnsp_3 = "f0000680QQQ", xvnmsubasp_3 = "f0000688QQQ", ["xvcmpgesp._3"] = "f0000698QQQ", xvcvuxdsp_2 = "f00006a0Q-Q", xvnabssp_2 = "f00006a4Q-Q", xvnmsubmsp_3 = "f00006c8QQQ", xvcvsxdsp_2 = "f00006e0Q-Q", xvnegsp_2 = "f00006e4Q-Q", xvmaxdp_3 = "f0000700QQQ", xvnmaddadp_3 = "f0000708QQQ", ["xvcmpeqdp._3"] = "f0000718QQQ", xvcvdpuxds_2 = "f0000720Q-Q", xvcvspdp_2 = "f0000724Q-Q", xvmindp_3 = "f0000740QQQ", xvnmaddmdp_3 = "f0000748QQQ", ["xvcmpgtdp._3"] = "f0000758QQQ", xvcvdpsxds_2 = "f0000760Q-Q", xvabsdp_2 = "f0000764Q-Q", xvcpsgndp_3 = "f0000780QQQ", xvnmsubadp_3 = "f0000788QQQ", ["xvcmpgedp._3"] = "f0000798QQQ", xvcvuxddp_2 = "f00007a0Q-Q", xvnabsdp_2 = "f00007a4Q-Q", xvnmsubmdp_3 = "f00007c8QQQ", xvcvsxddp_2 = "f00007e0Q-Q", xvnegdp_2 = "f00007e4Q-Q", -- Primary opcode 61: stfdp_2 = "f4000000F:D", -- NYI: displacement must be divisible by 4. -- Primary opcode 62: stq_2 = "f8000002R:D", -- NYI: displacement must be divisible by 8. -- Primary opcode 63: fdiv_3 = "fc000024FFF.", fsub_3 = "fc000028FFF.", fadd_3 = "fc00002aFFF.", fsqrt_2 = "fc00002cF-F.", fsel_4 = "fc00002eFFFF~.", fre_2 = "fc000030F-F.", fmul_3 = "fc000032FF-F.", frsqrte_2 = "fc000034F-F.", fmsub_4 = "fc000038FFFF~.", fmadd_4 = "fc00003aFFFF~.", fnmsub_4 = "fc00003cFFFF~.", fnmadd_4 = "fc00003eFFFF~.", fcmpu_3 = "fc000000XFF", fcpsgn_3 = "fc000010FFF.", fcmpo_3 = "fc000040XFF", mtfsb1_1 = "fc00004cA", fneg_2 = "fc000050F-F.", mcrfs_2 = "fc000080XX", mtfsb0_1 = "fc00008cA", fmr_2 = "fc000090F-F.", frsp_2 = "fc000018F-F.", fctiw_2 = "fc00001cF-F.", fctiwz_2 = "fc00001eF-F.", ftdiv_2 = "fc000100X-F.", fctiwu_2 = "fc00011cF-F.", fctiwuz_2 = "fc00011eF-F.", mtfsfi_2 = "fc00010cAA", -- NYI: upshift. fnabs_2 = "fc000110F-F.", ftsqrt_2 = "fc000140X-F.", fabs_2 = "fc000210F-F.", frin_2 = "fc000310F-F.", friz_2 = "fc000350F-F.", frip_2 = "fc000390F-F.", frim_2 = "fc0003d0F-F.", mffs_1 = "fc00048eF.", -- NYI: mtfsf, mtfsb0, mtfsb1. fctid_2 = "fc00065cF-F.", fctidz_2 = "fc00065eF-F.", fmrgow_3 = "fc00068cFFF", fcfid_2 = "fc00069cF-F.", fctidu_2 = "fc00075cF-F.", fctiduz_2 = "fc00075eF-F.", fmrgew_3 = "fc00078cFFF", fcfidu_2 = "fc00079cF-F.", daddq_3 = "fc000004F:F:F:.", dquaq_4 = "fc000006F:F:F:Z.", dmulq_3 = "fc000044F:F:F:.", drrndq_4 = "fc000046F:F:F:Z.", dscliq_3 = "fc000084F:F:6.", dquaiq_4 = "fc000086SF:~F:Z.", dscriq_3 = "fc0000c4F:F:6.", drintxq_4 = "fc0000c61F:~F:Z.", dcmpoq_3 = "fc000104XF:F:", dtstexq_3 = "fc000144XF:F:", dtstdcq_3 = "fc000184XF:6", dtstdgq_3 = "fc0001c4XF:6", drintnq_4 = "fc0001c61F:~F:Z.", dctqpq_2 = "fc000204F:-F:.", dctfixq_2 = "fc000244F:-F:.", ddedpdq_3 = "fc000284ZF:~F:.", dxexq_2 = "fc0002c4F:-F:.", dsubq_3 = "fc000404F:F:F:.", ddivq_3 = "fc000444F:F:F:.", dcmpuq_3 = "fc000504XF:F:", dtstsfq_3 = "fc000544XF:F:", drdpq_2 = "fc000604F:-F:.", dcffixq_2 = "fc000644F:-F:.", denbcdq_3 = "fc000684YF:~F:.", diexq_3 = "fc0006c4F:FF:.", -- Primary opcode 4, SPE APU extension: evaddw_3 = "10000200RRR", evaddiw_3 = "10000202RAR~", evsubw_3 = "10000204RRR~", evsubiw_3 = "10000206RAR~", evabs_2 = "10000208RR", evneg_2 = "10000209RR", evextsb_2 = "1000020aRR", evextsh_2 = "1000020bRR", evrndw_2 = "1000020cRR", evcntlzw_2 = "1000020dRR", evcntlsw_2 = "1000020eRR", brinc_3 = "1000020fRRR", evand_3 = "10000211RRR", evandc_3 = "10000212RRR", evxor_3 = "10000216RRR", evor_3 = "10000217RRR", evmr_2 = "10000217RR=", evnor_3 = "10000218RRR", evnot_2 = "10000218RR=", eveqv_3 = "10000219RRR", evorc_3 = "1000021bRRR", evnand_3 = "1000021eRRR", evsrwu_3 = "10000220RRR", evsrws_3 = "10000221RRR", evsrwiu_3 = "10000222RRA", evsrwis_3 = "10000223RRA", evslw_3 = "10000224RRR", evslwi_3 = "10000226RRA", evrlw_3 = "10000228RRR", evsplati_2 = "10000229RS", evrlwi_3 = "1000022aRRA", evsplatfi_2 = "1000022bRS", evmergehi_3 = "1000022cRRR", evmergelo_3 = "1000022dRRR", evcmpgtu_3 = "10000230XRR", evcmpgtu_2 = "10000230-RR", evcmpgts_3 = "10000231XRR", evcmpgts_2 = "10000231-RR", evcmpltu_3 = "10000232XRR", evcmpltu_2 = "10000232-RR", evcmplts_3 = "10000233XRR", evcmplts_2 = "10000233-RR", evcmpeq_3 = "10000234XRR", evcmpeq_2 = "10000234-RR", evsel_4 = "10000278RRRW", evsel_3 = "10000278RRR", evfsadd_3 = "10000280RRR", evfssub_3 = "10000281RRR", evfsabs_2 = "10000284RR", evfsnabs_2 = "10000285RR", evfsneg_2 = "10000286RR", evfsmul_3 = "10000288RRR", evfsdiv_3 = "10000289RRR", evfscmpgt_3 = "1000028cXRR", evfscmpgt_2 = "1000028c-RR", evfscmplt_3 = "1000028dXRR", evfscmplt_2 = "1000028d-RR", evfscmpeq_3 = "1000028eXRR", evfscmpeq_2 = "1000028e-RR", evfscfui_2 = "10000290R-R", evfscfsi_2 = "10000291R-R", evfscfuf_2 = "10000292R-R", evfscfsf_2 = "10000293R-R", evfsctui_2 = "10000294R-R", evfsctsi_2 = "10000295R-R", evfsctuf_2 = "10000296R-R", evfsctsf_2 = "10000297R-R", evfsctuiz_2 = "10000298R-R", evfsctsiz_2 = "1000029aR-R", evfststgt_3 = "1000029cXRR", evfststgt_2 = "1000029c-RR", evfststlt_3 = "1000029dXRR", evfststlt_2 = "1000029d-RR", evfststeq_3 = "1000029eXRR", evfststeq_2 = "1000029e-RR", efsadd_3 = "100002c0RRR", efssub_3 = "100002c1RRR", efsabs_2 = "100002c4RR", efsnabs_2 = "100002c5RR", efsneg_2 = "100002c6RR", efsmul_3 = "100002c8RRR", efsdiv_3 = "100002c9RRR", efscmpgt_3 = "100002ccXRR", efscmpgt_2 = "100002cc-RR", efscmplt_3 = "100002cdXRR", efscmplt_2 = "100002cd-RR", efscmpeq_3 = "100002ceXRR", efscmpeq_2 = "100002ce-RR", efscfd_2 = "100002cfR-R", efscfui_2 = "100002d0R-R", efscfsi_2 = "100002d1R-R", efscfuf_2 = "100002d2R-R", efscfsf_2 = "100002d3R-R", efsctui_2 = "100002d4R-R", efsctsi_2 = "100002d5R-R", efsctuf_2 = "100002d6R-R", efsctsf_2 = "100002d7R-R", efsctuiz_2 = "100002d8R-R", efsctsiz_2 = "100002daR-R", efststgt_3 = "100002dcXRR", efststgt_2 = "100002dc-RR", efststlt_3 = "100002ddXRR", efststlt_2 = "100002dd-RR", efststeq_3 = "100002deXRR", efststeq_2 = "100002de-RR", efdadd_3 = "100002e0RRR", efdsub_3 = "100002e1RRR", efdcfuid_2 = "100002e2R-R", efdcfsid_2 = "100002e3R-R", efdabs_2 = "100002e4RR", efdnabs_2 = "100002e5RR", efdneg_2 = "100002e6RR", efdmul_3 = "100002e8RRR", efddiv_3 = "100002e9RRR", efdctuidz_2 = "100002eaR-R", efdctsidz_2 = "100002ebR-R", efdcmpgt_3 = "100002ecXRR", efdcmpgt_2 = "100002ec-RR", efdcmplt_3 = "100002edXRR", efdcmplt_2 = "100002ed-RR", efdcmpeq_3 = "100002eeXRR", efdcmpeq_2 = "100002ee-RR", efdcfs_2 = "100002efR-R", efdcfui_2 = "100002f0R-R", efdcfsi_2 = "100002f1R-R", efdcfuf_2 = "100002f2R-R", efdcfsf_2 = "100002f3R-R", efdctui_2 = "100002f4R-R", efdctsi_2 = "100002f5R-R", efdctuf_2 = "100002f6R-R", efdctsf_2 = "100002f7R-R", efdctuiz_2 = "100002f8R-R", efdctsiz_2 = "100002faR-R", efdtstgt_3 = "100002fcXRR", efdtstgt_2 = "100002fc-RR", efdtstlt_3 = "100002fdXRR", efdtstlt_2 = "100002fd-RR", efdtsteq_3 = "100002feXRR", efdtsteq_2 = "100002fe-RR", evlddx_3 = "10000300RR0R", evldd_2 = "10000301R8", evldwx_3 = "10000302RR0R", evldw_2 = "10000303R8", evldhx_3 = "10000304RR0R", evldh_2 = "10000305R8", evlwhex_3 = "10000310RR0R", evlwhe_2 = "10000311R4", evlwhoux_3 = "10000314RR0R", evlwhou_2 = "10000315R4", evlwhosx_3 = "10000316RR0R", evlwhos_2 = "10000317R4", evstddx_3 = "10000320RR0R", evstdd_2 = "10000321R8", evstdwx_3 = "10000322RR0R", evstdw_2 = "10000323R8", evstdhx_3 = "10000324RR0R", evstdh_2 = "10000325R8", evstwhex_3 = "10000330RR0R", evstwhe_2 = "10000331R4", evstwhox_3 = "10000334RR0R", evstwho_2 = "10000335R4", evstwwex_3 = "10000338RR0R", evstwwe_2 = "10000339R4", evstwwox_3 = "1000033cRR0R", evstwwo_2 = "1000033dR4", evmhessf_3 = "10000403RRR", evmhossf_3 = "10000407RRR", evmheumi_3 = "10000408RRR", evmhesmi_3 = "10000409RRR", evmhesmf_3 = "1000040bRRR", evmhoumi_3 = "1000040cRRR", evmhosmi_3 = "1000040dRRR", evmhosmf_3 = "1000040fRRR", evmhessfa_3 = "10000423RRR", evmhossfa_3 = "10000427RRR", evmheumia_3 = "10000428RRR", evmhesmia_3 = "10000429RRR", evmhesmfa_3 = "1000042bRRR", evmhoumia_3 = "1000042cRRR", evmhosmia_3 = "1000042dRRR", evmhosmfa_3 = "1000042fRRR", evmwhssf_3 = "10000447RRR", evmwlumi_3 = "10000448RRR", evmwhumi_3 = "1000044cRRR", evmwhsmi_3 = "1000044dRRR", evmwhsmf_3 = "1000044fRRR", evmwssf_3 = "10000453RRR", evmwumi_3 = "10000458RRR", evmwsmi_3 = "10000459RRR", evmwsmf_3 = "1000045bRRR", evmwhssfa_3 = "10000467RRR", evmwlumia_3 = "10000468RRR", evmwhumia_3 = "1000046cRRR", evmwhsmia_3 = "1000046dRRR", evmwhsmfa_3 = "1000046fRRR", evmwssfa_3 = "10000473RRR", evmwumia_3 = "10000478RRR", evmwsmia_3 = "10000479RRR", evmwsmfa_3 = "1000047bRRR", evmra_2 = "100004c4RR", evdivws_3 = "100004c6RRR", evdivwu_3 = "100004c7RRR", evmwssfaa_3 = "10000553RRR", evmwumiaa_3 = "10000558RRR", evmwsmiaa_3 = "10000559RRR", evmwsmfaa_3 = "1000055bRRR", evmwssfan_3 = "100005d3RRR", evmwumian_3 = "100005d8RRR", evmwsmian_3 = "100005d9RRR", evmwsmfan_3 = "100005dbRRR", evmergehilo_3 = "1000022eRRR", evmergelohi_3 = "1000022fRRR", evlhhesplatx_3 = "10000308RR0R", evlhhesplat_2 = "10000309R2", evlhhousplatx_3 = "1000030cRR0R", evlhhousplat_2 = "1000030dR2", evlhhossplatx_3 = "1000030eRR0R", evlhhossplat_2 = "1000030fR2", evlwwsplatx_3 = "10000318RR0R", evlwwsplat_2 = "10000319R4", evlwhsplatx_3 = "1000031cRR0R", evlwhsplat_2 = "1000031dR4", evaddusiaaw_2 = "100004c0RR", evaddssiaaw_2 = "100004c1RR", evsubfusiaaw_2 = "100004c2RR", evsubfssiaaw_2 = "100004c3RR", evaddumiaaw_2 = "100004c8RR", evaddsmiaaw_2 = "100004c9RR", evsubfumiaaw_2 = "100004caRR", evsubfsmiaaw_2 = "100004cbRR", evmheusiaaw_3 = "10000500RRR", evmhessiaaw_3 = "10000501RRR", evmhessfaaw_3 = "10000503RRR", evmhousiaaw_3 = "10000504RRR", evmhossiaaw_3 = "10000505RRR", evmhossfaaw_3 = "10000507RRR", evmheumiaaw_3 = "10000508RRR", evmhesmiaaw_3 = "10000509RRR", evmhesmfaaw_3 = "1000050bRRR", evmhoumiaaw_3 = "1000050cRRR", evmhosmiaaw_3 = "1000050dRRR", evmhosmfaaw_3 = "1000050fRRR", evmhegumiaa_3 = "10000528RRR", evmhegsmiaa_3 = "10000529RRR", evmhegsmfaa_3 = "1000052bRRR", evmhogumiaa_3 = "1000052cRRR", evmhogsmiaa_3 = "1000052dRRR", evmhogsmfaa_3 = "1000052fRRR", evmwlusiaaw_3 = "10000540RRR", evmwlssiaaw_3 = "10000541RRR", evmwlumiaaw_3 = "10000548RRR", evmwlsmiaaw_3 = "10000549RRR", evmheusianw_3 = "10000580RRR", evmhessianw_3 = "10000581RRR", evmhessfanw_3 = "10000583RRR", evmhousianw_3 = "10000584RRR", evmhossianw_3 = "10000585RRR", evmhossfanw_3 = "10000587RRR", evmheumianw_3 = "10000588RRR", evmhesmianw_3 = "10000589RRR", evmhesmfanw_3 = "1000058bRRR", evmhoumianw_3 = "1000058cRRR", evmhosmianw_3 = "1000058dRRR", evmhosmfanw_3 = "1000058fRRR", evmhegumian_3 = "100005a8RRR", evmhegsmian_3 = "100005a9RRR", evmhegsmfan_3 = "100005abRRR", evmhogumian_3 = "100005acRRR", evmhogsmian_3 = "100005adRRR", evmhogsmfan_3 = "100005afRRR", evmwlusianw_3 = "100005c0RRR", evmwlssianw_3 = "100005c1RRR", evmwlumianw_3 = "100005c8RRR", evmwlsmianw_3 = "100005c9RRR", -- NYI: Book E instructions. } -- Add mnemonics for "." variants. do local t = {} for k,v in pairs(map_op) do if type(v) == "string" and sub(v, -1) == "." then local v2 = sub(v, 1, 7)..char(byte(v, 8)+1)..sub(v, 9, -2) t[sub(k, 1, -3).."."..sub(k, -2)] = v2 end end for k,v in pairs(t) do map_op[k] = v end end -- Add more branch mnemonics. for cond,c in pairs(map_cond) do local b1 = "b"..cond local c1 = shl(band(c, 3), 16) + (c < 4 and 0x01000000 or 0) -- bX[l] map_op[b1.."_1"] = tohex(0x40800000 + c1).."K" map_op[b1.."y_1"] = tohex(0x40a00000 + c1).."K" map_op[b1.."l_1"] = tohex(0x40800001 + c1).."K" map_op[b1.."_2"] = tohex(0x40800000 + c1).."-XK" map_op[b1.."y_2"] = tohex(0x40a00000 + c1).."-XK" map_op[b1.."l_2"] = tohex(0x40800001 + c1).."-XK" -- bXlr[l] map_op[b1.."lr_0"] = tohex(0x4c800020 + c1) map_op[b1.."lrl_0"] = tohex(0x4c800021 + c1) map_op[b1.."ctr_0"] = tohex(0x4c800420 + c1) map_op[b1.."ctrl_0"] = tohex(0x4c800421 + c1) -- bXctr[l] map_op[b1.."lr_1"] = tohex(0x4c800020 + c1).."-X" map_op[b1.."lrl_1"] = tohex(0x4c800021 + c1).."-X" map_op[b1.."ctr_1"] = tohex(0x4c800420 + c1).."-X" map_op[b1.."ctrl_1"] = tohex(0x4c800421 + c1).."-X" end ------------------------------------------------------------------------------ local function parse_gpr(expr) local tname, ovreg = match(expr, "^([%w_]+):(r[1-3]?[0-9])$") local tp = map_type[tname or expr] if tp then local reg = ovreg or tp.reg if not reg then werror("type `"..(tname or expr).."' needs a register override") end expr = reg end local r = match(expr, "^r([1-3]?[0-9])$") if r then r = tonumber(r) if r <= 31 then return r, tp end end werror("bad register name `"..expr.."'") end local function parse_fpr(expr) local r = match(expr, "^f([1-3]?[0-9])$") if r then r = tonumber(r) if r <= 31 then return r end end werror("bad register name `"..expr.."'") end local function parse_vr(expr) local r = match(expr, "^v([1-3]?[0-9])$") if r then r = tonumber(r) if r <= 31 then return r end end werror("bad register name `"..expr.."'") end local function parse_vs(expr) local r = match(expr, "^vs([1-6]?[0-9])$") if r then r = tonumber(r) if r <= 63 then return r end end werror("bad register name `"..expr.."'") end local function parse_cr(expr) local r = match(expr, "^cr([0-7])$") if r then return tonumber(r) end werror("bad condition register name `"..expr.."'") end local function parse_cond(expr) local r, cond = match(expr, "^4%*cr([0-7])%+(%w%w)$") if r then r = tonumber(r) local c = map_cond[cond] if c and c < 4 then return r*4+c end end werror("bad condition bit name `"..expr.."'") end local parse_ctx = {} local loadenv = setfenv and function(s) local code = loadstring(s, "") if code then setfenv(code, parse_ctx) end return code end or function(s) return load(s, "", nil, parse_ctx) end -- Try to parse simple arithmetic, too, since some basic ops are aliases. local function parse_number(n) local x = tonumber(n) if x then return x end local code = loadenv("return "..n) if code then local ok, y = pcall(code) if ok then return y end end return nil end local function parse_imm(imm, bits, shift, scale, signed) local n = parse_number(imm) if n then local m = sar(n, scale) if shl(m, scale) == n then if signed then local s = sar(m, bits-1) if s == 0 then return shl(m, shift) elseif s == -1 then return shl(m + shl(1, bits), shift) end else if sar(m, bits) == 0 then return shl(m, shift) end end end werror("out of range immediate `"..imm.."'") elseif match(imm, "^[rfv]([1-3]?[0-9])$") or match(imm, "^vs([1-6]?[0-9])$") or match(imm, "^([%w_]+):(r[1-3]?[0-9])$") then werror("expected immediate operand, got register") else waction("IMM", (signed and 32768 or 0)+scale*1024+bits*32+shift, imm) return 0 end end local function parse_shiftmask(imm, isshift) local n = parse_number(imm) if n then if shr(n, 6) == 0 then local lsb = band(n, 31) local msb = n - lsb return isshift and (shl(lsb, 11)+shr(msb, 4)) or (shl(lsb, 6)+msb) end werror("out of range immediate `"..imm.."'") elseif match(imm, "^r([1-3]?[0-9])$") or match(imm, "^([%w_]+):(r[1-3]?[0-9])$") then werror("expected immediate operand, got register") else waction("IMMSH", isshift and 1 or 0, imm) return 0; end end local function parse_disp(disp) local imm, reg = match(disp, "^(.*)%(([%w_:]+)%)$") if imm then local r = parse_gpr(reg) if r == 0 then werror("cannot use r0 in displacement") end return shl(r, 16) + parse_imm(imm, 16, 0, 0, true) end local reg, tailr = match(disp, "^([%w_:]+)%s*(.*)$") if reg and tailr ~= "" then local r, tp = parse_gpr(reg) if r == 0 then werror("cannot use r0 in displacement") end if tp then waction("IMM", 32768+16*32, format(tp.ctypefmt, tailr)) return shl(r, 16) end end werror("bad displacement `"..disp.."'") end local function parse_u5disp(disp, scale) local imm, reg = match(disp, "^(.*)%(([%w_:]+)%)$") if imm then local r = parse_gpr(reg) if r == 0 then werror("cannot use r0 in displacement") end return shl(r, 16) + parse_imm(imm, 5, 11, scale, false) end local reg, tailr = match(disp, "^([%w_:]+)%s*(.*)$") if reg and tailr ~= "" then local r, tp = parse_gpr(reg) if r == 0 then werror("cannot use r0 in displacement") end if tp then waction("IMM", scale*1024+5*32+11, format(tp.ctypefmt, tailr)) return shl(r, 16) end end werror("bad displacement `"..disp.."'") end local function parse_label(label, def) local prefix = sub(label, 1, 2) -- =>label (pc label reference) if prefix == "=>" then return "PC", 0, sub(label, 3) end -- ->name (global label reference) if prefix == "->" then return "LG", map_global[sub(label, 3)] end if def then -- [1-9] (local label definition) if match(label, "^[1-9]$") then return "LG", 10+tonumber(label) end else -- [<>][1-9] (local label reference) local dir, lnum = match(label, "^([<>])([1-9])$") if dir then -- Fwd: 1-9, Bkwd: 11-19. return "LG", lnum + (dir == ">" and 0 or 10) end -- extern label (extern label reference) local extname = match(label, "^extern%s+(%S+)$") if extname then return "EXT", map_extern[extname] end end werror("bad label `"..label.."'") end ------------------------------------------------------------------------------ -- Handle opcodes defined with template strings. op_template = function(params, template, nparams) if not params then return sub(template, 9) end local op = tonumber(sub(template, 1, 8), 16) local n, rs = 1, 26 -- Limit number of section buffer positions used by a single dasm_put(). -- A single opcode needs a maximum of 3 positions (rlwinm). if secpos+3 > maxsecpos then wflush() end local pos = wpos() -- Process each character. for p in gmatch(sub(template, 9), ".") do if p == "R" then rs = rs - 5; op = op + shl(parse_gpr(params[n]), rs); n = n + 1 elseif p == "F" then rs = rs - 5; op = op + shl(parse_fpr(params[n]), rs); n = n + 1 elseif p == "V" then rs = rs - 5; op = op + shl(parse_vr(params[n]), rs); n = n + 1 elseif p == "Q" then local vs = parse_vs(params[n]); n = n + 1; rs = rs - 5 local sh = rs == 6 and 2 or 3 + band(shr(rs, 1), 3) op = op + shl(band(vs, 31), rs) + shr(band(vs, 32), sh) elseif p == "q" then local vs = parse_vs(params[n]); n = n + 1 op = op + shl(band(vs, 31), 21) + shr(band(vs, 32), 5) elseif p == "A" then rs = rs - 5; op = op + parse_imm(params[n], 5, rs, 0, false); n = n + 1 elseif p == "S" then rs = rs - 5; op = op + parse_imm(params[n], 5, rs, 0, true); n = n + 1 elseif p == "I" then op = op + parse_imm(params[n], 16, 0, 0, true); n = n + 1 elseif p == "U" then op = op + parse_imm(params[n], 16, 0, 0, false); n = n + 1 elseif p == "D" then op = op + parse_disp(params[n]); n = n + 1 elseif p == "2" then op = op + parse_u5disp(params[n], 1); n = n + 1 elseif p == "4" then op = op + parse_u5disp(params[n], 2); n = n + 1 elseif p == "8" then op = op + parse_u5disp(params[n], 3); n = n + 1 elseif p == "C" then rs = rs - 5; op = op + shl(parse_cond(params[n]), rs); n = n + 1 elseif p == "X" then rs = rs - 5; op = op + shl(parse_cr(params[n]), rs+2); n = n + 1 elseif p == "1" then rs = rs - 5; op = op + parse_imm(params[n], 1, rs, 0, false); n = n + 1 elseif p == "g" then rs = rs - 5; op = op + parse_imm(params[n], 2, rs, 0, false); n = n + 1 elseif p == "3" then rs = rs - 5; op = op + parse_imm(params[n], 3, rs, 0, false); n = n + 1 elseif p == "P" then rs = rs - 5; op = op + parse_imm(params[n], 4, rs, 0, false); n = n + 1 elseif p == "p" then op = op + parse_imm(params[n], 4, rs, 0, false); n = n + 1 elseif p == "6" then rs = rs - 6; op = op + parse_imm(params[n], 6, rs, 0, false); n = n + 1 elseif p == "Y" then rs = rs - 5; op = op + parse_imm(params[n], 1, rs+4, 0, false); n = n + 1 elseif p == "y" then rs = rs - 5; op = op + parse_imm(params[n], 1, rs+3, 0, false); n = n + 1 elseif p == "Z" then rs = rs - 5; op = op + parse_imm(params[n], 2, rs+3, 0, false); n = n + 1 elseif p == "z" then rs = rs - 5; op = op + parse_imm(params[n], 2, rs+2, 0, false); n = n + 1 elseif p == "W" then op = op + parse_cr(params[n]); n = n + 1 elseif p == "G" then op = op + parse_imm(params[n], 8, 12, 0, false); n = n + 1 elseif p == "H" then op = op + parse_shiftmask(params[n], true); n = n + 1 elseif p == "M" then op = op + parse_shiftmask(params[n], false); n = n + 1 elseif p == "J" or p == "K" then local mode, m, s = parse_label(params[n], false) if p == "K" then m = m + 2048 end waction("REL_"..mode, m, s, 1) n = n + 1 elseif p == "0" then if band(shr(op, rs), 31) == 0 then werror("cannot use r0") end elseif p == "=" or p == "%" then local t = band(shr(op, p == "%" and rs+5 or rs), 31) rs = rs - 5 op = op + shl(t, rs) elseif p == "~" then local mm = shl(31, rs) local lo = band(op, mm) local hi = band(op, shl(mm, 5)) op = op - lo - hi + shl(lo, 5) + shr(hi, 5) elseif p == ":" then if band(shr(op, rs), 1) ~= 0 then werror("register pair expected") end elseif p == "-" then rs = rs - 5 elseif p == "." then -- Ignored. else assert(false) end end wputpos(pos, op) end map_op[".template__"] = op_template ------------------------------------------------------------------------------ -- Pseudo-opcode to mark the position where the action list is to be emitted. map_op[".actionlist_1"] = function(params) if not params then return "cvar" end local name = params[1] -- No syntax check. You get to keep the pieces. wline(function(out) writeactions(out, name) end) end -- Pseudo-opcode to mark the position where the global enum is to be emitted. map_op[".globals_1"] = function(params) if not params then return "prefix" end local prefix = params[1] -- No syntax check. You get to keep the pieces. wline(function(out) writeglobals(out, prefix) end) end -- Pseudo-opcode to mark the position where the global names are to be emitted. map_op[".globalnames_1"] = function(params) if not params then return "cvar" end local name = params[1] -- No syntax check. You get to keep the pieces. wline(function(out) writeglobalnames(out, name) end) end -- Pseudo-opcode to mark the position where the extern names are to be emitted. map_op[".externnames_1"] = function(params) if not params then return "cvar" end local name = params[1] -- No syntax check. You get to keep the pieces. wline(function(out) writeexternnames(out, name) end) end ------------------------------------------------------------------------------ -- Label pseudo-opcode (converted from trailing colon form). map_op[".label_1"] = function(params) if not params then return "[1-9] | ->global | =>pcexpr" end if secpos+1 > maxsecpos then wflush() end local mode, n, s = parse_label(params[1], true) if mode == "EXT" then werror("bad label definition") end waction("LABEL_"..mode, n, s, 1) end ------------------------------------------------------------------------------ -- Pseudo-opcodes for data storage. map_op[".long_*"] = function(params) if not params then return "imm..." end for _,p in ipairs(params) do local n = tonumber(p) if not n then werror("bad immediate `"..p.."'") end if n < 0 then n = n + 2^32 end wputw(n) if secpos+2 > maxsecpos then wflush() end end end -- Alignment pseudo-opcode. map_op[".align_1"] = function(params) if not params then return "numpow2" end if secpos+1 > maxsecpos then wflush() end local align = tonumber(params[1]) if align then local x = align -- Must be a power of 2 in the range (2 ... 256). for i=1,8 do x = x / 2 if x == 1 then waction("ALIGN", align-1, nil, 1) -- Action byte is 2**n-1. return end end end werror("bad alignment") end ------------------------------------------------------------------------------ -- Pseudo-opcode for (primitive) type definitions (map to C types). map_op[".type_3"] = function(params, nparams) if not params then return nparams == 2 and "name, ctype" or "name, ctype, reg" end local name, ctype, reg = params[1], params[2], params[3] if not match(name, "^[%a_][%w_]*$") then werror("bad type name `"..name.."'") end local tp = map_type[name] if tp then werror("duplicate type `"..name.."'") end -- Add #type to defines. A bit unclean to put it in map_archdef. map_archdef["#"..name] = "sizeof("..ctype..")" -- Add new type and emit shortcut define. local num = ctypenum + 1 map_type[name] = { ctype = ctype, ctypefmt = format("Dt%X(%%s)", num), reg = reg, } wline(format("#define Dt%X(_V) (int)(ptrdiff_t)&(((%s *)0)_V)", num, ctype)) ctypenum = num end map_op[".type_2"] = map_op[".type_3"] -- Dump type definitions. local function dumptypes(out, lvl) local t = {} for name in pairs(map_type) do t[#t+1] = name end sort(t) out:write("Type definitions:\n") for _,name in ipairs(t) do local tp = map_type[name] local reg = tp.reg or "" out:write(format(" %-20s %-20s %s\n", name, tp.ctype, reg)) end out:write("\n") end ------------------------------------------------------------------------------ -- Set the current section. function _M.section(num) waction("SECTION", num) wflush(true) -- SECTION is a terminal action. end ------------------------------------------------------------------------------ -- Dump architecture description. function _M.dumparch(out) out:write(format("DynASM %s version %s, released %s\n\n", _info.arch, _info.version, _info.release)) dumpactions(out) end -- Dump all user defined elements. function _M.dumpdef(out, lvl) dumptypes(out, lvl) dumpglobals(out, lvl) dumpexterns(out, lvl) end ------------------------------------------------------------------------------ -- Pass callbacks from/to the DynASM core. function _M.passcb(wl, we, wf, ww) wline, werror, wfatal, wwarn = wl, we, wf, ww return wflush end -- Setup the arch-specific module. function _M.setup(arch, opt) g_arch, g_opt = arch, opt end -- Merge the core maps and the arch-specific maps. function _M.mergemaps(map_coreop, map_def) setmetatable(map_op, { __index = map_coreop }) setmetatable(map_def, { __index = map_archdef }) return map_op, map_def end return _M ------------------------------------------------------------------------------