|
|
@@ -0,0 +1,1799 @@
|
|
|
+------------------------------------------------------------------------------
|
|
|
+-- DynASM x86 module.
|
|
|
+--
|
|
|
+-- Copyright (C) 2005-2009 Mike Pall. All rights reserved.
|
|
|
+-- See dynasm.lua for full copyright notice.
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- Module information:
|
|
|
+local _info = {
|
|
|
+ arch = "x86",
|
|
|
+ description = "DynASM x86 (i386) module",
|
|
|
+ version = "1.2.1",
|
|
|
+ vernum = 10201,
|
|
|
+ release = "2009-04-16",
|
|
|
+ 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, unpack = assert, unpack
|
|
|
+local _s = string
|
|
|
+local sub, format, byte, char = _s.sub, _s.format, _s.byte, _s.char
|
|
|
+local find, match, gmatch, gsub = _s.find, _s.match, _s.gmatch, _s.gsub
|
|
|
+local concat, sort = table.concat, table.sort
|
|
|
+local char, unpack = string.char, unpack
|
|
|
+
|
|
|
+-- 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 = {
|
|
|
+ -- int arg, 1 buffer pos:
|
|
|
+ "DISP", "IMM_S", "IMM_B", "IMM_W", "IMM_D", "IMM_WB", "IMM_DB",
|
|
|
+ -- action arg (1 byte), int arg, 1 buffer pos (reg/num):
|
|
|
+ "VREG", "SPACE",
|
|
|
+ -- ptrdiff_t arg, 1 buffer pos (address): !x64
|
|
|
+ "SETLABEL", "REL_A",
|
|
|
+ -- action arg (1 byte) or int arg, 2 buffer pos (link, offset):
|
|
|
+ "REL_LG", "REL_PC",
|
|
|
+ -- action arg (1 byte) or int arg, 1 buffer pos (link):
|
|
|
+ "IMM_LG", "IMM_PC",
|
|
|
+ -- action arg (1 byte) or int arg, 1 buffer pos (offset):
|
|
|
+ "LABEL_LG", "LABEL_PC",
|
|
|
+ -- action arg (1 byte), 1 buffer pos (offset):
|
|
|
+ "ALIGN",
|
|
|
+ -- action args (2 bytes), no buffer pos.
|
|
|
+ "EXTERN",
|
|
|
+ -- action arg (1 byte), no buffer pos.
|
|
|
+ "ESC",
|
|
|
+ -- no action arg, no buffer pos.
|
|
|
+ "MARK",
|
|
|
+ -- action arg (1 byte), no buffer pos, terminal action:
|
|
|
+ "SECTION",
|
|
|
+ -- no args, no buffer pos, terminal action:
|
|
|
+ "STOP"
|
|
|
+}
|
|
|
+
|
|
|
+-- 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 (dynamically generated below).
|
|
|
+local map_action = {}
|
|
|
+-- First action number. Everything below does not need to be escaped.
|
|
|
+local actfirst = 256-#action_names
|
|
|
+
|
|
|
+-- Action list buffer and string (only used to remove dupes).
|
|
|
+local actlist = {}
|
|
|
+local actstr = ""
|
|
|
+
|
|
|
+-- 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
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- Compute action numbers for action names.
|
|
|
+for n,name in ipairs(action_names) do
|
|
|
+ local num = actfirst + n - 1
|
|
|
+ map_action[name] = num
|
|
|
+end
|
|
|
+
|
|
|
+-- 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
|
|
|
+ local last = actlist[nn] or 255
|
|
|
+ actlist[nn] = nil -- Remove last byte.
|
|
|
+ if nn == 0 then nn = 1 end
|
|
|
+ out:write("static const unsigned char ", name, "[", nn, "] = {\n")
|
|
|
+ local s = " "
|
|
|
+ for n,b in ipairs(actlist) do
|
|
|
+ s = s..b..","
|
|
|
+ if #s >= 75 then
|
|
|
+ assert(out:write(s, "\n"))
|
|
|
+ s = " "
|
|
|
+ end
|
|
|
+ end
|
|
|
+ out:write(s, last, "\n};\n\n") -- Add last byte back.
|
|
|
+end
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- Add byte to action list.
|
|
|
+local function wputxb(n)
|
|
|
+ assert(n >= 0 and n <= 255 and n % 1 == 0, "byte out of range")
|
|
|
+ actlist[#actlist+1] = n
|
|
|
+end
|
|
|
+
|
|
|
+-- Add action to list with optional arg. Advance buffer pos, too.
|
|
|
+local function waction(action, a, num)
|
|
|
+ wputxb(assert(map_action[action], "bad action name `"..action.."'"))
|
|
|
+ if a then actargs[#actargs+1] = a end
|
|
|
+ if a or num then secpos = secpos + (num or 1) end
|
|
|
+end
|
|
|
+
|
|
|
+-- Add call to embedded DynASM C code.
|
|
|
+local function wcall(func, args)
|
|
|
+ wline(format("dasm_%s(Dst, %s);", func, concat(args, ", ")), true)
|
|
|
+end
|
|
|
+
|
|
|
+-- Delete duplicate action list chunks. A tad slow, but so what.
|
|
|
+local function dedupechunk(offset)
|
|
|
+ local al, as = actlist, actstr
|
|
|
+ local chunk = char(unpack(al, offset+1, #al))
|
|
|
+ local orig = find(as, chunk, 1, true)
|
|
|
+ if orig then
|
|
|
+ actargs[1] = orig-1 -- Replace with original offset.
|
|
|
+ for i=offset+1,#al do al[i] = nil end -- Kill dupe.
|
|
|
+ else
|
|
|
+ actstr = as..chunk
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+-- Flush action list (intervening C code or buffer pos overflow).
|
|
|
+local function wflush(term)
|
|
|
+ local offset = actargs[1]
|
|
|
+ if #actlist == offset then return end -- Nothing to flush.
|
|
|
+ if not term then waction("STOP") end -- Terminate action list.
|
|
|
+ dedupechunk(offset)
|
|
|
+ wcall("put", actargs) -- Add call to dasm_put().
|
|
|
+ actargs = { #actlist } -- Actionlist offset is 1st arg to next dasm_put().
|
|
|
+ secpos = 1 -- The actionlist offset occupies a buffer position, too.
|
|
|
+end
|
|
|
+
|
|
|
+-- Put escaped byte.
|
|
|
+local function wputb(n)
|
|
|
+ if n >= actfirst then waction("ESC") end -- Need to escape byte.
|
|
|
+ wputxb(n)
|
|
|
+end
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- Global label name -> global label number. With auto assignment on 1st use.
|
|
|
+local next_global = 10
|
|
|
+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 > 246 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=10,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=10,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=10,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 = -1
|
|
|
+local map_extern = setmetatable({}, { __index = function(t, name)
|
|
|
+ -- No restrictions on the name for now.
|
|
|
+ local n = next_extern
|
|
|
+ if n < -256 then werror("too many extern labels") end
|
|
|
+ next_extern = n - 1
|
|
|
+ t[name] = n
|
|
|
+ return n
|
|
|
+end})
|
|
|
+
|
|
|
+-- Dump extern labels.
|
|
|
+local function dumpexterns(out, lvl)
|
|
|
+ local t = {}
|
|
|
+ for name, n in pairs(map_extern) do t[-n] = name end
|
|
|
+ out:write("Extern labels:\n")
|
|
|
+ for i=1,-next_extern-1 do
|
|
|
+ out:write(format(" %s\n", t[i]))
|
|
|
+ end
|
|
|
+ out:write("\n")
|
|
|
+end
|
|
|
+
|
|
|
+-- Write extern label names.
|
|
|
+local function writeexternnames(out, name)
|
|
|
+ local t = {}
|
|
|
+ for name, n in pairs(map_extern) do t[-n] = name end
|
|
|
+ out:write("static const char *const ", name, "[] = {\n")
|
|
|
+ for i=1,-next_extern-1 do
|
|
|
+ out:write(" \"", t[i], "\",\n")
|
|
|
+ end
|
|
|
+ out:write(" (const char *)0\n};\n")
|
|
|
+end
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- Arch-specific maps.
|
|
|
+local map_archdef = {} -- Ext. register name -> int. name.
|
|
|
+local map_reg_rev = {} -- Int. register name -> ext. name.
|
|
|
+local map_reg_num = {} -- Int. register name -> register number.
|
|
|
+local map_reg_opsize = {} -- Int. register name -> operand size.
|
|
|
+local map_reg_valid_base = {} -- Int. register name -> valid base register?
|
|
|
+local map_reg_valid_index = {} -- Int. register name -> valid index register?
|
|
|
+local reg_list = {} -- Canonical list of int. register names.
|
|
|
+
|
|
|
+local map_type = {} -- Type name -> { ctype, reg }
|
|
|
+local ctypenum = 0 -- Type number (for _PTx macros).
|
|
|
+
|
|
|
+local addrsize = "d" -- Size for address operands. !x64
|
|
|
+
|
|
|
+-- Helper function to fill register maps.
|
|
|
+local function mkrmap(sz, cl, names)
|
|
|
+ local cname = format("@%s", sz)
|
|
|
+ reg_list[#reg_list+1] = cname
|
|
|
+ map_archdef[cl] = cname
|
|
|
+ map_reg_rev[cname] = cl
|
|
|
+ map_reg_num[cname] = -1
|
|
|
+ map_reg_opsize[cname] = sz
|
|
|
+ if sz == addrsize then
|
|
|
+ map_reg_valid_base[cname] = true
|
|
|
+ map_reg_valid_index[cname] = true
|
|
|
+ end
|
|
|
+ for n,name in ipairs(names) do
|
|
|
+ local iname = format("@%s%x", sz, n-1)
|
|
|
+ reg_list[#reg_list+1] = iname
|
|
|
+ map_archdef[name] = iname
|
|
|
+ map_reg_rev[iname] = name
|
|
|
+ map_reg_num[iname] = n-1
|
|
|
+ map_reg_opsize[iname] = sz
|
|
|
+ if sz == addrsize then
|
|
|
+ map_reg_valid_base[iname] = true
|
|
|
+ map_reg_valid_index[iname] = true
|
|
|
+ end
|
|
|
+ end
|
|
|
+ reg_list[#reg_list+1] = ""
|
|
|
+end
|
|
|
+
|
|
|
+-- Integer registers (dword, word and byte sized).
|
|
|
+mkrmap("d", "Rd", {"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"})
|
|
|
+map_reg_valid_index[map_archdef.esp] = false
|
|
|
+mkrmap("w", "Rw", {"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"})
|
|
|
+mkrmap("b", "Rb", {"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"})
|
|
|
+map_archdef["Ra"] = "@"..addrsize
|
|
|
+
|
|
|
+-- FP registers (internally tword sized, but use "f" as operand size).
|
|
|
+mkrmap("f", "Rf", {"st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7"})
|
|
|
+
|
|
|
+-- SSE registers (oword sized, but qword and dword accessible).
|
|
|
+mkrmap("o", "xmm", {"xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7"})
|
|
|
+
|
|
|
+-- Operand size prefixes to codes.
|
|
|
+local map_opsize = {
|
|
|
+ byte = "b", word = "w", dword = "d", qword = "q", oword = "o", tword = "t",
|
|
|
+ aword = addrsize,
|
|
|
+}
|
|
|
+
|
|
|
+-- Operand size code to number.
|
|
|
+local map_opsizenum = {
|
|
|
+ b = 1, w = 2, d = 4, q = 8, o = 16, t = 10,
|
|
|
+}
|
|
|
+
|
|
|
+-- Operand size code to name.
|
|
|
+local map_opsizename = {
|
|
|
+ b = "byte", w = "word", d = "dword", q = "qword", o = "oword", t = "tword",
|
|
|
+ f = "fpword",
|
|
|
+}
|
|
|
+
|
|
|
+-- Valid index register scale factors.
|
|
|
+local map_xsc = {
|
|
|
+ ["1"] = 0, ["2"] = 1, ["4"] = 2, ["8"] = 3,
|
|
|
+}
|
|
|
+
|
|
|
+-- Condition codes.
|
|
|
+local map_cc = {
|
|
|
+ o = 0, no = 1, b = 2, nb = 3, e = 4, ne = 5, be = 6, nbe = 7,
|
|
|
+ s = 8, ns = 9, p = 10, np = 11, l = 12, nl = 13, le = 14, nle = 15,
|
|
|
+ c = 2, nae = 2, nc = 3, ae = 3, z = 4, nz = 5, na = 6, a = 7,
|
|
|
+ pe = 10, po = 11, nge = 12, ge = 13, ng = 14, g = 15,
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+-- Reverse defines for registers.
|
|
|
+function _M.revdef(s)
|
|
|
+ return gsub(s, "@%w+", map_reg_rev)
|
|
|
+end
|
|
|
+
|
|
|
+-- Dump register names and numbers
|
|
|
+local function dumpregs(out)
|
|
|
+ out:write("Register names, sizes and internal numbers:\n")
|
|
|
+ for _,reg in ipairs(reg_list) do
|
|
|
+ if reg == "" then
|
|
|
+ out:write("\n")
|
|
|
+ else
|
|
|
+ local name = map_reg_rev[reg]
|
|
|
+ local num = map_reg_num[reg]
|
|
|
+ local opsize = map_opsizename[map_reg_opsize[reg]]
|
|
|
+ out:write(format(" %-5s %-8s %s\n", name, opsize,
|
|
|
+ num < 0 and "(variable)" or num))
|
|
|
+ end
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- Put action for label arg (IMM_LG, IMM_PC, REL_LG, REL_PC).
|
|
|
+local function wputlabel(aprefix, imm, num)
|
|
|
+ if type(imm) == "number" then
|
|
|
+ if imm < 0 then
|
|
|
+ waction("EXTERN")
|
|
|
+ wputxb(aprefix == "IMM_" and 0 or 1)
|
|
|
+ imm = -imm-1
|
|
|
+ else
|
|
|
+ waction(aprefix.."LG", nil, num);
|
|
|
+ end
|
|
|
+ wputxb(imm)
|
|
|
+ else
|
|
|
+ waction(aprefix.."PC", imm, num)
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+-- Put signed byte or arg.
|
|
|
+local function wputsbarg(n)
|
|
|
+ if type(n) == "number" then
|
|
|
+ if n < -128 or n > 127 then
|
|
|
+ werror("signed immediate byte out of range")
|
|
|
+ end
|
|
|
+ if n < 0 then n = n + 256 end
|
|
|
+ wputb(n)
|
|
|
+ else waction("IMM_S", n) end
|
|
|
+end
|
|
|
+
|
|
|
+-- Put unsigned byte or arg.
|
|
|
+local function wputbarg(n)
|
|
|
+ if type(n) == "number" then
|
|
|
+ if n < 0 or n > 255 then
|
|
|
+ werror("unsigned immediate byte out of range")
|
|
|
+ end
|
|
|
+ wputb(n)
|
|
|
+ else waction("IMM_B", n) end
|
|
|
+end
|
|
|
+
|
|
|
+-- Put unsigned word or arg.
|
|
|
+local function wputwarg(n)
|
|
|
+ if type(n) == "number" then
|
|
|
+ if n < 0 or n > 65535 then
|
|
|
+ werror("unsigned immediate word out of range")
|
|
|
+ end
|
|
|
+ local r = n%256; n = (n-r)/256; wputb(r); wputb(n);
|
|
|
+ else waction("IMM_W", n) end
|
|
|
+end
|
|
|
+
|
|
|
+-- Put signed or unsigned dword or arg.
|
|
|
+local function wputdarg(n)
|
|
|
+ local tn = type(n)
|
|
|
+ if tn == "number" then
|
|
|
+ if n < 0 then n = n + 4294967296 end
|
|
|
+ local r = n%256; n = (n-r)/256; wputb(r);
|
|
|
+ r = n%256; n = (n-r)/256; wputb(r);
|
|
|
+ r = n%256; n = (n-r)/256; wputb(r); wputb(n);
|
|
|
+ elseif tn == "table" then
|
|
|
+ wputlabel("IMM_", n[1], 1)
|
|
|
+ else
|
|
|
+ waction("IMM_D", n)
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+-- Put operand-size dependent number or arg (defaults to dword).
|
|
|
+local function wputszarg(sz, n)
|
|
|
+ if not sz or sz == "d" then wputdarg(n)
|
|
|
+ elseif sz == "w" then wputwarg(n)
|
|
|
+ elseif sz == "b" then wputbarg(n)
|
|
|
+ elseif sz == "s" then wputsbarg(n)
|
|
|
+ else werror("bad operand size") end
|
|
|
+end
|
|
|
+
|
|
|
+-- Put multi-byte opcode with operand-size dependent modifications.
|
|
|
+local function wputop(sz, op)
|
|
|
+ local r
|
|
|
+ if sz == "w" then wputb(102) end
|
|
|
+ -- Needs >32 bit numbers, but only for crc32 eax, word [ebx]
|
|
|
+ if op >= 4294967296 then r = op%4294967296 wputb((op-r)/4294967296) op = r end
|
|
|
+ if op >= 16777216 then r = op % 16777216 wputb((op-r) / 16777216) op = r end
|
|
|
+ if op >= 65536 then r = op % 65536 wputb((op-r) / 65536) op = r end
|
|
|
+ if op >= 256 then r = op % 256 wputb((op-r) / 256) op = r end
|
|
|
+ if sz == "b" then op = op - 1 end
|
|
|
+ wputb(op)
|
|
|
+end
|
|
|
+
|
|
|
+-- Put ModRM or SIB formatted byte.
|
|
|
+local function wputmodrm(m, s, rm, vs, vrm)
|
|
|
+ assert(m < 4 and s < 8 and rm < 8, "bad modrm operands")
|
|
|
+ wputb(64*m + 8*s + rm)
|
|
|
+end
|
|
|
+
|
|
|
+-- Put ModRM/SIB plus optional displacement.
|
|
|
+local function wputmrmsib(t, imark, s, vsreg)
|
|
|
+ local vreg, vxreg
|
|
|
+ local reg, xreg = t.reg, t.xreg
|
|
|
+ if reg and reg < 0 then reg = 0; vreg = t.vreg end
|
|
|
+ if xreg and xreg < 0 then xreg = 0; vxreg = t.vxreg end
|
|
|
+ if s < 0 then s = 0 end
|
|
|
+
|
|
|
+ -- Register mode.
|
|
|
+ if sub(t.mode, 1, 1) == "r" then
|
|
|
+ wputmodrm(3, s, reg)
|
|
|
+ if vsreg then waction("VREG", vsreg); wputxb(2) end
|
|
|
+ if vreg then waction("VREG", vreg); wputxb(0) end
|
|
|
+ return
|
|
|
+ end
|
|
|
+
|
|
|
+ local disp = t.disp
|
|
|
+ local tdisp = type(disp)
|
|
|
+ -- No base register?
|
|
|
+ if not reg then
|
|
|
+ if xreg then
|
|
|
+ -- Indexed mode with index register only.
|
|
|
+ -- [xreg*xsc+disp] -> (0, s, esp) (xsc, xreg, ebp)
|
|
|
+ wputmodrm(0, s, 4)
|
|
|
+ if imark then waction("MARK") end
|
|
|
+ if vsreg then waction("VREG", vsreg); wputxb(2) end
|
|
|
+ wputmodrm(t.xsc, xreg, 5)
|
|
|
+ if vxreg then waction("VREG", vxreg); wputxb(3) end
|
|
|
+ else
|
|
|
+ -- Pure displacement.
|
|
|
+ wputmodrm(0, s, 5) -- [disp] -> (0, s, ebp)
|
|
|
+ if imark then waction("MARK") end
|
|
|
+ if vsreg then waction("VREG", vsreg); wputxb(2) end
|
|
|
+ end
|
|
|
+ wputdarg(disp)
|
|
|
+ return
|
|
|
+ end
|
|
|
+
|
|
|
+ local m
|
|
|
+ if tdisp == "number" then -- Check displacement size at assembly time.
|
|
|
+ if disp == 0 and reg ~= 5 then -- [ebp] -> [ebp+0] (in SIB, too)
|
|
|
+ if not vreg then m = 0 end -- Force DISP to allow [Rd(5)] -> [ebp+0]
|
|
|
+ elseif disp >= -128 and disp <= 127 then m = 1
|
|
|
+ else m = 2 end
|
|
|
+ elseif tdisp == "table" then
|
|
|
+ m = 2
|
|
|
+ end
|
|
|
+
|
|
|
+ -- Index register present or esp as base register: need SIB encoding.
|
|
|
+ if xreg or reg == 4 then
|
|
|
+ wputmodrm(m or 2, s, 4) -- ModRM.
|
|
|
+ if m == nil or imark then waction("MARK") end
|
|
|
+ if vsreg then waction("VREG", vsreg); wputxb(2) end
|
|
|
+ wputmodrm(t.xsc or 0, xreg or 4, reg) -- SIB.
|
|
|
+ if vxreg then waction("VREG", vxreg); wputxb(3) end
|
|
|
+ if vreg then waction("VREG", vreg); wputxb(1) end
|
|
|
+ else
|
|
|
+ wputmodrm(m or 2, s, reg) -- ModRM.
|
|
|
+ if (imark and (m == 1 or m == 2)) or
|
|
|
+ (m == nil and (vsreg or vreg)) then waction("MARK") end
|
|
|
+ if vsreg then waction("VREG", vsreg); wputxb(2) end
|
|
|
+ if vreg then waction("VREG", vreg); wputxb(1) end
|
|
|
+ end
|
|
|
+
|
|
|
+ -- Put displacement.
|
|
|
+ if m == 1 then wputsbarg(disp)
|
|
|
+ elseif m == 2 then wputdarg(disp)
|
|
|
+ elseif m == nil then waction("DISP", disp) end
|
|
|
+end
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- Return human-readable operand mode string.
|
|
|
+local function opmodestr(op, args)
|
|
|
+ local m = {}
|
|
|
+ for i=1,#args do
|
|
|
+ local a = args[i]
|
|
|
+ m[#m+1] = sub(a.mode, 1, 1)..(a.opsize or "?")
|
|
|
+ end
|
|
|
+ return op.." "..concat(m, ",")
|
|
|
+end
|
|
|
+
|
|
|
+-- Convert number to valid integer or nil.
|
|
|
+local function toint(expr)
|
|
|
+ local n = tonumber(expr)
|
|
|
+ if n then
|
|
|
+ if n % 1 ~= 0 or n < -2147483648 or n > 4294967295 then
|
|
|
+ werror("bad integer number `"..expr.."'")
|
|
|
+ end
|
|
|
+ return n
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+-- Parse immediate expression.
|
|
|
+local function immexpr(expr)
|
|
|
+ -- &expr (pointer)
|
|
|
+ if sub(expr, 1, 1) == "&" then
|
|
|
+ return "iPJ", format("(ptrdiff_t)(%s)", sub(expr,2))
|
|
|
+ end
|
|
|
+
|
|
|
+ local prefix = sub(expr, 1, 2)
|
|
|
+ -- =>expr (pc label reference)
|
|
|
+ if prefix == "=>" then
|
|
|
+ return "iJ", sub(expr, 3)
|
|
|
+ end
|
|
|
+ -- ->name (global label reference)
|
|
|
+ if prefix == "->" then
|
|
|
+ return "iJ", map_global[sub(expr, 3)]
|
|
|
+ end
|
|
|
+
|
|
|
+ -- [<>][1-9] (local label reference)
|
|
|
+ local dir, lnum = match(expr, "^([<>])([1-9])$")
|
|
|
+ if dir then -- Fwd: 247-255, Bkwd: 1-9.
|
|
|
+ return "iJ", lnum + (dir == ">" and 246 or 0)
|
|
|
+ end
|
|
|
+
|
|
|
+ local extname = match(expr, "^extern%s+(%S+)$")
|
|
|
+ if extname then
|
|
|
+ return "iJ", map_extern[extname]
|
|
|
+ end
|
|
|
+
|
|
|
+ -- expr (interpreted as immediate)
|
|
|
+ return "iI", expr
|
|
|
+end
|
|
|
+
|
|
|
+-- Parse displacement expression: +-num, +-expr, +-opsize*num
|
|
|
+local function dispexpr(expr)
|
|
|
+ local disp = expr == "" and 0 or toint(expr)
|
|
|
+ if disp then return disp end
|
|
|
+ local c, dispt = match(expr, "^([+-])%s*(.+)$")
|
|
|
+ if c == "+" then
|
|
|
+ expr = dispt
|
|
|
+ elseif not c then
|
|
|
+ werror("bad displacement expression `"..expr.."'")
|
|
|
+ end
|
|
|
+ local opsize, tailops = match(dispt, "^(%w+)%s*%*%s*(.+)$")
|
|
|
+ local ops, imm = map_opsize[opsize], toint(tailops)
|
|
|
+ if ops and imm then
|
|
|
+ if c == "-" then imm = -imm end
|
|
|
+ return imm*map_opsizenum[ops]
|
|
|
+ end
|
|
|
+ local mode, iexpr = immexpr(dispt)
|
|
|
+ if mode == "iJ" then
|
|
|
+ if c == "-" then werror("cannot invert label reference") end
|
|
|
+ return { iexpr }
|
|
|
+ end
|
|
|
+ return expr -- Need to return original signed expression.
|
|
|
+end
|
|
|
+
|
|
|
+-- Parse register or type expression.
|
|
|
+local function rtexpr(expr)
|
|
|
+ if not expr then return end
|
|
|
+ local tname, ovreg = match(expr, "^([%w_]+):(@[%w_]+)$")
|
|
|
+ local tp = map_type[tname or expr]
|
|
|
+ if tp then
|
|
|
+ local reg = ovreg or tp.reg
|
|
|
+ local rnum = map_reg_num[reg]
|
|
|
+ if not rnum then
|
|
|
+ werror("type `"..(tname or expr).."' needs a register override")
|
|
|
+ end
|
|
|
+ if not map_reg_valid_base[reg] then
|
|
|
+ werror("bad base register override `"..(map_reg_rev[reg] or reg).."'")
|
|
|
+ end
|
|
|
+ return reg, rnum, tp
|
|
|
+ end
|
|
|
+ return expr, map_reg_num[expr]
|
|
|
+end
|
|
|
+
|
|
|
+-- Parse operand and return { mode, opsize, reg, xreg, xsc, disp, imm }.
|
|
|
+local function parseoperand(param)
|
|
|
+ local t = {}
|
|
|
+
|
|
|
+ local expr = param
|
|
|
+ local opsize, tailops = match(param, "^(%w+)%s*(.+)$")
|
|
|
+ if opsize then
|
|
|
+ t.opsize = map_opsize[opsize]
|
|
|
+ if t.opsize then expr = tailops end
|
|
|
+ end
|
|
|
+
|
|
|
+ local br = match(expr, "^%[%s*(.-)%s*%]$")
|
|
|
+ repeat
|
|
|
+ if br then
|
|
|
+ t.mode = "xm"
|
|
|
+
|
|
|
+ -- [disp]
|
|
|
+ t.disp = toint(br)
|
|
|
+ if t.disp then
|
|
|
+ t.mode = "xmO"
|
|
|
+ break
|
|
|
+ end
|
|
|
+
|
|
|
+ -- [reg...]
|
|
|
+ local tp
|
|
|
+ local reg, tailr = match(br, "^([@%w_:]+)%s*(.*)$")
|
|
|
+ reg, t.reg, tp = rtexpr(reg)
|
|
|
+ if not t.reg then
|
|
|
+ -- [expr]
|
|
|
+ t.mode = "xmO"
|
|
|
+ t.disp = dispexpr("+"..br)
|
|
|
+ break
|
|
|
+ end
|
|
|
+
|
|
|
+ if t.reg == -1 then
|
|
|
+ t.vreg, tailr = match(tailr, "^(%b())(.*)$")
|
|
|
+ if not t.vreg then werror("bad variable register expression") end
|
|
|
+ end
|
|
|
+
|
|
|
+ -- [xreg*xsc] or [xreg*xsc+-disp] or [xreg*xsc+-expr]
|
|
|
+ local xsc, tailsc = match(tailr, "^%*%s*([1248])%s*(.*)$")
|
|
|
+ if xsc then
|
|
|
+ if not map_reg_valid_index[reg] then
|
|
|
+ werror("bad index register `"..map_reg_rev[reg].."'")
|
|
|
+ end
|
|
|
+ t.xsc = map_xsc[xsc]
|
|
|
+ t.xreg = t.reg
|
|
|
+ t.vxreg = t.vreg
|
|
|
+ t.reg = nil
|
|
|
+ t.vreg = nil
|
|
|
+ t.disp = dispexpr(tailsc)
|
|
|
+ break
|
|
|
+ end
|
|
|
+ if not map_reg_valid_base[reg] then
|
|
|
+ werror("bad base register `"..map_reg_rev[reg].."'")
|
|
|
+ end
|
|
|
+
|
|
|
+ -- [reg] or [reg+-disp]
|
|
|
+ t.disp = toint(tailr) or (tailr == "" and 0)
|
|
|
+ if t.disp then break end
|
|
|
+
|
|
|
+ -- [reg+xreg...]
|
|
|
+ local xreg, tailx = match(tailr, "^+%s*([@%w_:]+)%s*(.*)$")
|
|
|
+ xreg, t.xreg, tp = rtexpr(xreg)
|
|
|
+ if not t.xreg then
|
|
|
+ -- [reg+-expr]
|
|
|
+ t.disp = dispexpr(tailr)
|
|
|
+ break
|
|
|
+ end
|
|
|
+ if not map_reg_valid_index[xreg] then
|
|
|
+ werror("bad index register `"..map_reg_rev[xreg].."'")
|
|
|
+ end
|
|
|
+
|
|
|
+ if t.xreg == -1 then
|
|
|
+ t.vxreg, tailx = match(tailx, "^(%b())(.*)$")
|
|
|
+ if not t.vxreg then werror("bad variable register expression") end
|
|
|
+ end
|
|
|
+
|
|
|
+ -- [reg+xreg*xsc...]
|
|
|
+ local xsc, tailsc = match(tailx, "^%*%s*([1248])%s*(.*)$")
|
|
|
+ if xsc then
|
|
|
+ t.xsc = map_xsc[xsc]
|
|
|
+ tailx = tailsc
|
|
|
+ end
|
|
|
+
|
|
|
+ -- [...] or [...+-disp] or [...+-expr]
|
|
|
+ t.disp = dispexpr(tailx)
|
|
|
+ else
|
|
|
+ -- imm or opsize*imm
|
|
|
+ local imm = toint(expr)
|
|
|
+ if not imm and sub(expr, 1, 1) == "*" and t.opsize then
|
|
|
+ imm = toint(sub(expr, 2))
|
|
|
+ if imm then
|
|
|
+ imm = imm * map_opsizenum[t.opsize]
|
|
|
+ t.opsize = nil
|
|
|
+ end
|
|
|
+ end
|
|
|
+ if imm then
|
|
|
+ if t.opsize then werror("bad operand size override") end
|
|
|
+ local m = "i"
|
|
|
+ if imm == 1 then m = m.."1" end
|
|
|
+ if imm >= 4294967168 and imm <= 4294967295 then imm = imm-4294967296 end
|
|
|
+ if imm >= -128 and imm <= 127 then m = m.."S" end
|
|
|
+ t.imm = imm
|
|
|
+ t.mode = m
|
|
|
+ break
|
|
|
+ end
|
|
|
+
|
|
|
+ local tp
|
|
|
+ local reg, tailr = match(expr, "^([@%w_:]+)%s*(.*)$")
|
|
|
+ reg, t.reg, tp = rtexpr(reg)
|
|
|
+ if t.reg then
|
|
|
+ if t.reg == -1 then
|
|
|
+ t.vreg, tailr = match(tailr, "^(%b())(.*)$")
|
|
|
+ if not t.vreg then werror("bad variable register expression") end
|
|
|
+ end
|
|
|
+ -- reg
|
|
|
+ if tailr == "" then
|
|
|
+ if t.opsize then werror("bad operand size override") end
|
|
|
+ t.opsize = map_reg_opsize[reg]
|
|
|
+ if t.opsize == "f" then
|
|
|
+ t.mode = t.reg == 0 and "fF" or "f"
|
|
|
+ else
|
|
|
+ if reg == "@w4" then wwarn("bad idea, try again with `esp'") end
|
|
|
+ t.mode = t.reg == 0 and "rmR" or (reg == "@b1" and "rmC" or "rm")
|
|
|
+ end
|
|
|
+ break
|
|
|
+ end
|
|
|
+
|
|
|
+ -- type[idx], type[idx].field, type->field -> [reg+offset_expr]
|
|
|
+ if not tp then werror("bad operand `"..param.."'") end
|
|
|
+ t.mode = "xm"
|
|
|
+ t.disp = format(tp.ctypefmt, tailr)
|
|
|
+ else
|
|
|
+ t.mode, t.imm = immexpr(expr)
|
|
|
+ if sub(t.mode, -1) == "J" then
|
|
|
+ if t.opsize and t.opsize ~= addrsize then
|
|
|
+ werror("bad operand size override")
|
|
|
+ end
|
|
|
+ t.opsize = addrsize
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+ until true
|
|
|
+ return t
|
|
|
+end
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+-- x86 Template String Description
|
|
|
+-- ===============================
|
|
|
+--
|
|
|
+-- Each template string is a list of [match:]pattern pairs,
|
|
|
+-- separated by "|". The first match wins. No match means a
|
|
|
+-- bad or unsupported combination of operand modes or sizes.
|
|
|
+--
|
|
|
+-- The match part and the ":" is omitted if the operation has
|
|
|
+-- no operands. Otherwise the first N characters are matched
|
|
|
+-- against the mode strings of each of the N operands.
|
|
|
+--
|
|
|
+-- The mode string for each operand type is (see parseoperand()):
|
|
|
+-- Integer register: "rm", +"R" for eax, ax, al, +"C" for cl
|
|
|
+-- FP register: "f", +"F" for st0
|
|
|
+-- Index operand: "xm", +"O" for [disp] (pure offset)
|
|
|
+-- Immediate: "i", +"S" for signed 8 bit, +"1" for 1,
|
|
|
+-- +"I" for arg, +"P" for pointer
|
|
|
+-- Any: +"J" for valid jump targets
|
|
|
+--
|
|
|
+-- So a match character "m" (mixed) matches both an integer register
|
|
|
+-- and an index operand (to be encoded with the ModRM/SIB scheme).
|
|
|
+-- But "r" matches only a register and "x" only an index operand
|
|
|
+-- (e.g. for FP memory access operations).
|
|
|
+--
|
|
|
+-- The operand size match string starts right after the mode match
|
|
|
+-- characters and ends before the ":". "dwb" is assumed, if empty.
|
|
|
+-- The effective data size of the operation is matched against this list.
|
|
|
+--
|
|
|
+-- If only the regular "b", "w", "d", "q", "t" operand sizes are
|
|
|
+-- present, then all operands must be the same size. Unspecified sizes
|
|
|
+-- are ignored, but at least one operand must have a size or the pattern
|
|
|
+-- won't match (use the "byte", "word", "dword", "qword", "tword"
|
|
|
+-- operand size overrides. E.g.: mov dword [eax], 1).
|
|
|
+--
|
|
|
+-- If the list has a "1" or "2" prefix, the operand size is taken
|
|
|
+-- from the respective operand and any other operand sizes are ignored.
|
|
|
+-- If the list contains only ".", all operand sizes are ignored.
|
|
|
+-- If the list has a "/" prefix, the concatenated (mixed) operand sizes
|
|
|
+-- are compared to the match.
|
|
|
+--
|
|
|
+-- E.g. "rrdw" matches for either two dword registers or two word
|
|
|
+-- registers. "Fx2dq" matches an st0 operand plus an index operand
|
|
|
+-- pointing to a dword (float) or qword (double).
|
|
|
+--
|
|
|
+-- Every character after the ":" is part of the pattern string:
|
|
|
+-- Hex chars are accumulated to form the opcode (left to right).
|
|
|
+-- "n" disables the standard opcode mods
|
|
|
+-- (otherwise: -1 for "b", o16 prefix for "w")
|
|
|
+-- "r"/"R" adds the reg. number from the 1st/2nd operand to the opcode.
|
|
|
+-- "m"/"M" generates ModRM/SIB from the 1st/2nd operand.
|
|
|
+-- The spare 3 bits are either filled with the last hex digit or
|
|
|
+-- the result from a previous "r"/"R". The opcode is restored.
|
|
|
+--
|
|
|
+-- All of the following characters force a flush of the opcode:
|
|
|
+-- "o"/"O" stores a pure 32 bit disp (offset) from the 1st/2nd operand.
|
|
|
+-- "S" stores a signed 8 bit immediate from the last operand.
|
|
|
+-- "U" stores an unsigned 8 bit immediate from the last operand.
|
|
|
+-- "W" stores an unsigned 16 bit immediate from the last operand.
|
|
|
+-- "i" stores an operand sized immediate from the last operand.
|
|
|
+-- "I" dito, but generates an action code to optionally modify
|
|
|
+-- the opcode (+2) for a signed 8 bit immediate.
|
|
|
+-- "J" generates one of the REL action codes from the last operand.
|
|
|
+--
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- Template strings for x86 instructions. Ordered by first opcode byte.
|
|
|
+-- Unimplemented opcodes (deliberate omissions) are marked with *.
|
|
|
+local map_op = {
|
|
|
+ -- 00-05: add...
|
|
|
+ -- 06: *push es
|
|
|
+ -- 07: *pop es
|
|
|
+ -- 08-0D: or...
|
|
|
+ -- 0E: *push cs
|
|
|
+ -- 0F: two byte opcode prefix
|
|
|
+ -- 10-15: adc...
|
|
|
+ -- 16: *push ss
|
|
|
+ -- 17: *pop ss
|
|
|
+ -- 18-1D: sbb...
|
|
|
+ -- 1E: *push ds
|
|
|
+ -- 1F: *pop ds
|
|
|
+ -- 20-25: and...
|
|
|
+ es_0 = "26",
|
|
|
+ -- 27: *daa
|
|
|
+ -- 28-2D: sub...
|
|
|
+ cs_0 = "2E",
|
|
|
+ -- 2F: *das
|
|
|
+ -- 30-35: xor...
|
|
|
+ ss_0 = "36",
|
|
|
+ -- 37: *aaa
|
|
|
+ -- 38-3D: cmp...
|
|
|
+ ds_0 = "3E",
|
|
|
+ -- 3F: *aas
|
|
|
+ inc_1 = "rdw:40r|m:FF0m",
|
|
|
+ dec_1 = "rdw:48r|m:FF1m",
|
|
|
+ push_1 = "rdw:50r|mdw:FF6m|S.:6AS|ib:n6Ai|i.:68i",
|
|
|
+ pop_1 = "rdw:58r|mdw:8F0m",
|
|
|
+ -- 60: *pusha, *pushad, *pushaw
|
|
|
+ -- 61: *popa, *popad, *popaw
|
|
|
+ -- 62: *bound rdw,x
|
|
|
+ -- 63: *arpl mw,rw
|
|
|
+ fs_0 = "64",
|
|
|
+ gs_0 = "65",
|
|
|
+ o16_0 = "66",
|
|
|
+ a16_0 = "67",
|
|
|
+ -- 68: push idw
|
|
|
+ -- 69: imul rdw,mdw,idw
|
|
|
+ -- 6A: push ib
|
|
|
+ -- 6B: imul rdw,mdw,S
|
|
|
+ -- 6C: *insb
|
|
|
+ -- 6D: *insd, *insw
|
|
|
+ -- 6E: *outsb
|
|
|
+ -- 6F: *outsd, *outsw
|
|
|
+ -- 70-7F: jcc lb
|
|
|
+ -- 80: add... mb,i
|
|
|
+ -- 81: add... mdw,i
|
|
|
+ -- 82: *undefined
|
|
|
+ -- 83: add... mdw,S
|
|
|
+ test_2 = "mr:85Rm|rm:85rM|Ri:A9ri|mi:F70mi",
|
|
|
+ -- 86: xchg rb,mb
|
|
|
+ -- 87: xchg rdw,mdw
|
|
|
+ -- 88: mov mb,r
|
|
|
+ -- 89: mov mdw,r
|
|
|
+ -- 8A: mov r,mb
|
|
|
+ -- 8B: mov r,mdw
|
|
|
+ -- 8C: *mov mdw,seg
|
|
|
+ lea_2 = "rxd:8DrM",
|
|
|
+ -- 8E: *mov seg,mdw
|
|
|
+ -- 8F: pop mdw
|
|
|
+ nop_0 = "90",
|
|
|
+ xchg_2 = "Rrdw:90R|rRdw:90r|rm:87rM|mr:87Rm",
|
|
|
+ cbw_0 = "6698",
|
|
|
+ cwde_0 = "98",
|
|
|
+ cwd_0 = "6699",
|
|
|
+ cdq_0 = "99",
|
|
|
+ -- 9A: *call iw:idw
|
|
|
+ wait_0 = "9B",
|
|
|
+ fwait_0 = "9B",
|
|
|
+ pushf_0 = "9C",
|
|
|
+ pushfw_0 = "669C",
|
|
|
+ pushfd_0 = "9C",
|
|
|
+ popf_0 = "9D",
|
|
|
+ popfw_0 = "669D",
|
|
|
+ popfd_0 = "9D",
|
|
|
+ sahf_0 = "9E",
|
|
|
+ lahf_0 = "9F",
|
|
|
+ mov_2 = "OR:A3o|RO:A1O|mr:89Rm|rm:8BrM|rib:nB0ri|ridw:B8ri|mi:C70mi",
|
|
|
+ movsb_0 = "A4",
|
|
|
+ movsw_0 = "66A5",
|
|
|
+ movsd_0 = "A5",
|
|
|
+ cmpsb_0 = "A6",
|
|
|
+ cmpsw_0 = "66A7",
|
|
|
+ cmpsd_0 = "A7",
|
|
|
+ -- A8: test Rb,i
|
|
|
+ -- A9: test Rdw,i
|
|
|
+ stosb_0 = "AA",
|
|
|
+ stosw_0 = "66AB",
|
|
|
+ stosd_0 = "AB",
|
|
|
+ lodsb_0 = "AC",
|
|
|
+ lodsw_0 = "66AD",
|
|
|
+ lodsd_0 = "AD",
|
|
|
+ scasb_0 = "AE",
|
|
|
+ scasw_0 = "66AF",
|
|
|
+ scasd_0 = "AF",
|
|
|
+ -- B0-B7: mov rb,i
|
|
|
+ -- B8-BF: mov rdw,i
|
|
|
+ -- C0: rol... mb,i
|
|
|
+ -- C1: rol... mdw,i
|
|
|
+ ret_1 = "i.:nC2W",
|
|
|
+ ret_0 = "C3",
|
|
|
+ -- C4: *les rdw,mq
|
|
|
+ -- C5: *lds rdw,mq
|
|
|
+ -- C6: mov mb,i
|
|
|
+ -- C7: mov mdw,i
|
|
|
+ -- C8: *enter iw,ib
|
|
|
+ leave_0 = "C9",
|
|
|
+ -- CA: *retf iw
|
|
|
+ -- CB: *retf
|
|
|
+ int3_0 = "CC",
|
|
|
+ int_1 = "i.:nCDU",
|
|
|
+ into_0 = "CE",
|
|
|
+ -- CF: *iret
|
|
|
+ -- D0: rol... mb,1
|
|
|
+ -- D1: rol... mdw,1
|
|
|
+ -- D2: rol... mb,cl
|
|
|
+ -- D3: rol... mb,cl
|
|
|
+ -- D4: *aam ib
|
|
|
+ -- D5: *aad ib
|
|
|
+ -- D6: *salc
|
|
|
+ -- D7: *xlat
|
|
|
+ -- D8-DF: floating point ops
|
|
|
+ -- E0: *loopne
|
|
|
+ -- E1: *loope
|
|
|
+ -- E2: *loop
|
|
|
+ -- E3: *jcxz, *jecxz
|
|
|
+ -- E4: *in Rb,ib
|
|
|
+ -- E5: *in Rdw,ib
|
|
|
+ -- E6: *out ib,Rb
|
|
|
+ -- E7: *out ib,Rdw
|
|
|
+ call_1 = "md:FF2m|J.:E8J",
|
|
|
+ jmp_1 = "md:FF4m|J.:E9J", -- short: EB
|
|
|
+ -- EA: *jmp iw:idw
|
|
|
+ -- EB: jmp ib
|
|
|
+ -- EC: *in Rb,dx
|
|
|
+ -- ED: *in Rdw,dx
|
|
|
+ -- EE: *out dx,Rb
|
|
|
+ -- EF: *out dx,Rdw
|
|
|
+ -- F0: *lock
|
|
|
+ int1_0 = "F1",
|
|
|
+ repne_0 = "F2",
|
|
|
+ repnz_0 = "F2",
|
|
|
+ rep_0 = "F3",
|
|
|
+ repe_0 = "F3",
|
|
|
+ repz_0 = "F3",
|
|
|
+ -- F4: *hlt
|
|
|
+ cmc_0 = "F5",
|
|
|
+ -- F6: test... mb,i; div... mb
|
|
|
+ -- F7: test... mdw,i; div... mdw
|
|
|
+ clc_0 = "F8",
|
|
|
+ stc_0 = "F9",
|
|
|
+ -- FA: *cli
|
|
|
+ cld_0 = "FC",
|
|
|
+ std_0 = "FD",
|
|
|
+ -- FE: inc... mb
|
|
|
+ -- FF: inc... mdw
|
|
|
+
|
|
|
+ -- misc ops
|
|
|
+ not_1 = "m:F72m",
|
|
|
+ neg_1 = "m:F73m",
|
|
|
+ mul_1 = "m:F74m",
|
|
|
+ imul_1 = "m:F75m",
|
|
|
+ div_1 = "m:F76m",
|
|
|
+ idiv_1 = "m:F77m",
|
|
|
+
|
|
|
+ imul_2 = "rmdw:0FAFrM|rIdw:69rmI|rSdw:6BrmS|ridw:69rmi",
|
|
|
+ imul_3 = "rmIdw:69rMI|rmSdw:6BrMS|rmidw:69rMi",
|
|
|
+
|
|
|
+ movzx_2 = "rm/db:0FB6rM|rm/wb:0FB6rM|rm/dw:0FB7rM",
|
|
|
+ movsx_2 = "rm/db:0FBErM|rm/wb:0FBErM|rm/dw:0FBFrM",
|
|
|
+
|
|
|
+ bswap_1 = "rd:0FC8r",
|
|
|
+ bsf_2 = "rmdw:0FBCrM",
|
|
|
+ bsr_2 = "rmdw:0FBDrM",
|
|
|
+ bt_2 = "mrdw:0FA3Rm|midw:0FBA4mU",
|
|
|
+ btc_2 = "mrdw:0FBBRm|midw:0FBA7mU",
|
|
|
+ btr_2 = "mrdw:0FB3Rm|midw:0FBA6mU",
|
|
|
+ bts_2 = "mrdw:0FABRm|midw:0FBA5mU",
|
|
|
+
|
|
|
+ rdtsc_0 = "0F31", -- P1+
|
|
|
+ cpuid_0 = "0FA2", -- P1+
|
|
|
+
|
|
|
+ -- floating point ops
|
|
|
+ fst_1 = "ff:DDD0r|xd:D92m|xq:DD2m",
|
|
|
+ fstp_1 = "ff:DDD8r|xd:D93m|xq:DD3m|xt:DB7m",
|
|
|
+ fld_1 = "ff:D9C0r|xd:D90m|xq:DD0m|xt:DB5m",
|
|
|
+
|
|
|
+ fpop_0 = "DDD8", -- Alias for fstp st0.
|
|
|
+
|
|
|
+ fist_1 = "xw:nDF2m|xd:DB2m",
|
|
|
+ fistp_1 = "xw:nDF3m|xd:DB3m|xq:DF7m",
|
|
|
+ fild_1 = "xw:nDF0m|xd:DB0m|xq:DF5m",
|
|
|
+
|
|
|
+ fxch_0 = "D9C9",
|
|
|
+ fxch_1 = "ff:D9C8r",
|
|
|
+ fxch_2 = "fFf:D9C8r|Fff:D9C8R",
|
|
|
+
|
|
|
+ fucom_1 = "ff:DDE0r",
|
|
|
+ fucom_2 = "Fff:DDE0R",
|
|
|
+ fucomp_1 = "ff:DDE8r",
|
|
|
+ fucomp_2 = "Fff:DDE8R",
|
|
|
+ fucomi_1 = "ff:DBE8r", -- P6+
|
|
|
+ fucomi_2 = "Fff:DBE8R", -- P6+
|
|
|
+ fucomip_1 = "ff:DFE8r", -- P6+
|
|
|
+ fucomip_2 = "Fff:DFE8R", -- P6+
|
|
|
+ fcomi_1 = "ff:DBF0r", -- P6+
|
|
|
+ fcomi_2 = "Fff:DBF0R", -- P6+
|
|
|
+ fcomip_1 = "ff:DFF0r", -- P6+
|
|
|
+ fcomip_2 = "Fff:DFF0R", -- P6+
|
|
|
+ fucompp_0 = "DAE9",
|
|
|
+ fcompp_0 = "DED9",
|
|
|
+
|
|
|
+ fldcw_1 = "xw:nD95m",
|
|
|
+ fstcw_1 = "xw:n9BD97m",
|
|
|
+ fnstcw_1 = "xw:nD97m",
|
|
|
+ fstsw_1 = "Rw:n9BDFE0|xw:n9BDD7m",
|
|
|
+ fnstsw_1 = "Rw:nDFE0|xw:nDD7m",
|
|
|
+ fclex_0 = "9BDBE2",
|
|
|
+ fnclex_0 = "DBE2",
|
|
|
+
|
|
|
+ fnop_0 = "D9D0",
|
|
|
+ -- D9D1-D9DF: unassigned
|
|
|
+
|
|
|
+ fchs_0 = "D9E0",
|
|
|
+ fabs_0 = "D9E1",
|
|
|
+ -- D9E2: unassigned
|
|
|
+ -- D9E3: unassigned
|
|
|
+ ftst_0 = "D9E4",
|
|
|
+ fxam_0 = "D9E5",
|
|
|
+ -- D9E6: unassigned
|
|
|
+ -- D9E7: unassigned
|
|
|
+ fld1_0 = "D9E8",
|
|
|
+ fldl2t_0 = "D9E9",
|
|
|
+ fldl2e_0 = "D9EA",
|
|
|
+ fldpi_0 = "D9EB",
|
|
|
+ fldlg2_0 = "D9EC",
|
|
|
+ fldln2_0 = "D9ED",
|
|
|
+ fldz_0 = "D9EE",
|
|
|
+ -- D9EF: unassigned
|
|
|
+
|
|
|
+ f2xm1_0 = "D9F0",
|
|
|
+ fyl2x_0 = "D9F1",
|
|
|
+ fptan_0 = "D9F2",
|
|
|
+ fpatan_0 = "D9F3",
|
|
|
+ fxtract_0 = "D9F4",
|
|
|
+ fprem1_0 = "D9F5",
|
|
|
+ fdecstp_0 = "D9F6",
|
|
|
+ fincstp_0 = "D9F7",
|
|
|
+ fprem_0 = "D9F8",
|
|
|
+ fyl2xp1_0 = "D9F9",
|
|
|
+ fsqrt_0 = "D9FA",
|
|
|
+ fsincos_0 = "D9FB",
|
|
|
+ frndint_0 = "D9FC",
|
|
|
+ fscale_0 = "D9FD",
|
|
|
+ fsin_0 = "D9FE",
|
|
|
+ fcos_0 = "D9FF",
|
|
|
+
|
|
|
+ -- SSE, SSE2
|
|
|
+ andnpd_2 = "rmo:660F55rM",
|
|
|
+ andnps_2 = "rmo:0F55rM",
|
|
|
+ andpd_2 = "rmo:660F54rM",
|
|
|
+ andps_2 = "rmo:0F54rM",
|
|
|
+ clflush_1 = "x.:0FAE7m",
|
|
|
+ cmppd_3 = "rmio:660FC2rMU",
|
|
|
+ cmpps_3 = "rmio:0FC2rMU",
|
|
|
+ cmpsd_3 = "rmio:F20FC2rMU",
|
|
|
+ cmpss_3 = "rmio:F30FC2rMU",
|
|
|
+ comisd_2 = "rmo:660F2FrM",
|
|
|
+ comiss_2 = "rmo:0F2FrM",
|
|
|
+ cvtdq2pd_2 = "rro:F30FE6rM|rx/oq:",
|
|
|
+ cvtdq2ps_2 = "rmo:0F5BrM",
|
|
|
+ cvtpd2dq_2 = "rmo:F20FE6rM",
|
|
|
+ cvtpd2ps_2 = "rmo:660F5ArM",
|
|
|
+ cvtpi2pd_2 = "rx/oq:660F2ArM",
|
|
|
+ cvtpi2ps_2 = "rx/oq:0F2ArM",
|
|
|
+ cvtps2dq_2 = "rmo:660F5BrM",
|
|
|
+ cvtps2pd_2 = "rro:0F5ArM|rx/oq:",
|
|
|
+ cvtsd2si_2 = "rr/do:F20F2DrM|rx/dq:",
|
|
|
+ cvtsd2ss_2 = "rro:F20F5ArM|rx/oq:",
|
|
|
+ cvtsi2sd_2 = "rm/od:F20F2ArM",
|
|
|
+ cvtsi2ss_2 = "rm/od:F30F2ArM",
|
|
|
+ cvtss2sd_2 = "rro:F30F5ArM|rx/od:",
|
|
|
+ cvtss2si_2 = "rr/do:F20F2CrM|rx/dd:",
|
|
|
+ cvttpd2dq_2 = "rmo:660FE6rM",
|
|
|
+ cvttps2dq_2 = "rmo:F30F5BrM",
|
|
|
+ cvttsd2si_2 = "rr/do:F20F2CrM|rx/dq:",
|
|
|
+ cvttss2si_2 = "rr/do:F30F2CrM|rx/dd:",
|
|
|
+ ldmxcsr_1 = "xd:0FAE2m",
|
|
|
+ lfence_0 = "0FAEE8",
|
|
|
+ maskmovdqu_2 = "rro:660FF7rM",
|
|
|
+ mfence_0 = "0FAEF0",
|
|
|
+ movapd_2 = "rmo:660F28rM|mro:660F29Rm",
|
|
|
+ movaps_2 = "rmo:0F28rM|mro:0F29Rm",
|
|
|
+ movd_2 = "rm/od:660F6ErM|mr/do:660F7ERm",
|
|
|
+ movdqa_2 = "rmo:660F6FrM|mro:660F7FRm",
|
|
|
+ movdqu_2 = "rmo:F30F6FrM|mro:F30F7FRm",
|
|
|
+ movhlps_2 = "rro:0F12rM",
|
|
|
+ movhpd_2 = "rx/oq:660F16rM|xr/qo:660F17Rm",
|
|
|
+ movhps_2 = "rx/oq:0F16rM|xr/qo:0F17Rm",
|
|
|
+ movlhps_2 = "rro:0F16rM",
|
|
|
+ movlpd_2 = "rx/oq:660F12rM|xr/qo:660F13Rm",
|
|
|
+ movlps_2 = "rx/oq:0F12rM|xr/qo:0F13Rm",
|
|
|
+ movmskpd_2 = "rr/do:660F50rM",
|
|
|
+ movmskps_2 = "rr/do:0F50rM",
|
|
|
+ movntdq_2 = "xro:660FE7Rm",
|
|
|
+ movnti_2 = "xrd:0FC3Rm",
|
|
|
+ movntpd_2 = "xro:660F2BRm",
|
|
|
+ movntps_2 = "xro:0F2BRm",
|
|
|
+ movq_2 = "rro:F30F7ErM|rx/oq:|xr/qo:660FD6Rm",
|
|
|
+ movsd_2 = "rro:F20F10rM|rx/oq:|xr/qo:F20F11Rm",
|
|
|
+ movss_2 = "rro:F30F10rM|rx/od:|xr/do:F30F11Rm",
|
|
|
+ movupd_2 = "rmo:660F10rM|mro:660F11Rm",
|
|
|
+ movups_2 = "rmo:0F10rM|mro:0F11Rm",
|
|
|
+ orpd_2 = "rmo:660F56rM",
|
|
|
+ orps_2 = "rmo:0F56rM",
|
|
|
+ packssdw_2 = "rmo:660F6BrM",
|
|
|
+ packsswb_2 = "rmo:660F63rM",
|
|
|
+ packuswb_2 = "rmo:660F67rM",
|
|
|
+ paddb_2 = "rmo:660FFCrM",
|
|
|
+ paddd_2 = "rmo:660FFErM",
|
|
|
+ paddq_2 = "rmo:660FD4rM",
|
|
|
+ paddsb_2 = "rmo:660FECrM",
|
|
|
+ paddsw_2 = "rmo:660FEDrM",
|
|
|
+ paddusb_2 = "rmo:660FDCrM",
|
|
|
+ paddusw_2 = "rmo:660FDDrM",
|
|
|
+ paddw_2 = "rmo:660FFDrM",
|
|
|
+ pand_2 = "rmo:660FDBrM",
|
|
|
+ pandn_2 = "rmo:660FDFrM",
|
|
|
+ pause_0 = "F390",
|
|
|
+ pavgb_2 = "rmo:660FE0rM",
|
|
|
+ pavgw_2 = "rmo:660FE3rM",
|
|
|
+ pcmpeqb_2 = "rmo:660F74rM",
|
|
|
+ pcmpeqd_2 = "rmo:660F76rM",
|
|
|
+ pcmpeqw_2 = "rmo:660F75rM",
|
|
|
+ pcmpgtb_2 = "rmo:660F64rM",
|
|
|
+ pcmpgtd_2 = "rmo:660F66rM",
|
|
|
+ pcmpgtw_2 = "rmo:660F65rM",
|
|
|
+ pextrw_3 = "rri/do:660FC5rMU|xri/wo:660F3A15nrMU", -- Mem op: SSE4.1 only.
|
|
|
+ pinsrw_3 = "rri/od:660FC4rMU|rxi/ow:",
|
|
|
+ pmaddwd_2 = "rmo:660FF5rM",
|
|
|
+ pmaxsw_2 = "rmo:660FEErM",
|
|
|
+ pmaxub_2 = "rmo:660FDErM",
|
|
|
+ pminsw_2 = "rmo:660FEArM",
|
|
|
+ pminub_2 = "rmo:660FDArM",
|
|
|
+ pmovmskb_2 = "rr/do:660FD7rM",
|
|
|
+ pmulhuw_2 = "rmo:660FE4rM",
|
|
|
+ pmulhw_2 = "rmo:660FE5rM",
|
|
|
+ pmullw_2 = "rmo:660FD5rM",
|
|
|
+ pmuludq_2 = "rmo:660FF4rM",
|
|
|
+ por_2 = "rmo:660FEBrM",
|
|
|
+ prefetchnta_1 = "xb:n0F180m",
|
|
|
+ prefetcht0_1 = "xb:n0F181m",
|
|
|
+ prefetcht1_1 = "xb:n0F182m",
|
|
|
+ prefetcht2_1 = "xb:n0F183m",
|
|
|
+ psadbw_2 = "rmo:660FF6rM",
|
|
|
+ pshufd_3 = "rmio:660F70rMU",
|
|
|
+ pshufhw_3 = "rmio:F30F70rMU",
|
|
|
+ pshuflw_3 = "rmio:F20F70rMU",
|
|
|
+ pslld_2 = "rmo:660FF2rM|rio:660F726mU",
|
|
|
+ pslldq_2 = "rio:660F737mU",
|
|
|
+ psllq_2 = "rmo:660FF3rM|rio:660F736mU",
|
|
|
+ psllw_2 = "rmo:660FF1rM|rio:660F716mU",
|
|
|
+ psrad_2 = "rmo:660FE2rM|rio:660F724mU",
|
|
|
+ psraw_2 = "rmo:660FE1rM|rio:660F714mU",
|
|
|
+ psrld_2 = "rmo:660FD2rM|rio:660F722mU",
|
|
|
+ psrldq_2 = "rio:660F733mU",
|
|
|
+ psrlq_2 = "rmo:660FD3rM|rio:660F732mU",
|
|
|
+ psrlw_2 = "rmo:660FD1rM|rio:660F712mU",
|
|
|
+ psubb_2 = "rmo:660FF8rM",
|
|
|
+ psubd_2 = "rmo:660FFArM",
|
|
|
+ psubq_2 = "rmo:660FFBrM",
|
|
|
+ psubsb_2 = "rmo:660FE8rM",
|
|
|
+ psubsw_2 = "rmo:660FE9rM",
|
|
|
+ psubusb_2 = "rmo:660FD8rM",
|
|
|
+ psubusw_2 = "rmo:660FD9rM",
|
|
|
+ psubw_2 = "rmo:660FF9rM",
|
|
|
+ punpckhbw_2 = "rmo:660F68rM",
|
|
|
+ punpckhdq_2 = "rmo:660F6ArM",
|
|
|
+ punpckhqdq_2 = "rmo:660F6DrM",
|
|
|
+ punpckhwd_2 = "rmo:660F69rM",
|
|
|
+ punpcklbw_2 = "rmo:660F60rM",
|
|
|
+ punpckldq_2 = "rmo:660F62rM",
|
|
|
+ punpcklqdq_2 = "rmo:660F6CrM",
|
|
|
+ punpcklwd_2 = "rmo:660F61rM",
|
|
|
+ pxor_2 = "rmo:660FEFrM",
|
|
|
+ rcpps_2 = "rmo:0F53rM",
|
|
|
+ rcpss_2 = "rmo:F30F53rM",
|
|
|
+ rsqrtps_2 = "rmo:0F52rM",
|
|
|
+ rsqrtss_2 = "rmo:F30F52rM",
|
|
|
+ sfence_0 = "0FAEF8",
|
|
|
+ shufpd_3 = "rmio:660FC6rMU",
|
|
|
+ shufps_3 = "rmio:0FC6rMU",
|
|
|
+ stmxcsr_1 = "xd:0FAE3m",
|
|
|
+ ucomisd_2 = "rmo:660F2ErM",
|
|
|
+ ucomiss_2 = "rmo:0F2ErM",
|
|
|
+ unpckhpd_2 = "rmo:660F15rM",
|
|
|
+ unpckhps_2 = "rmo:0F15rM",
|
|
|
+ unpcklpd_2 = "rmo:660F14rM",
|
|
|
+ unpcklps_2 = "rmo:0F14rM",
|
|
|
+ xorpd_2 = "rmo:660F57rM",
|
|
|
+ xorps_2 = "rmo:0F57rM",
|
|
|
+
|
|
|
+ -- SSE3 ops
|
|
|
+ fisttp_1 = "xw:nDF1m|xd:DB1m|xq:DD1m",
|
|
|
+ addsubpd_2 = "rmo:660FD0rM",
|
|
|
+ addsubps_2 = "rmo:F20FD0rM",
|
|
|
+ haddpd_2 = "rmo:660F7CrM",
|
|
|
+ haddps_2 = "rmo:F20F7CrM",
|
|
|
+ hsubpd_2 = "rmo:660F7DrM",
|
|
|
+ hsubps_2 = "rmo:F20F7DrM",
|
|
|
+ lddqu_2 = "rxo:F20FF0rM",
|
|
|
+ movddup_2 = "rmo:F20F12rM",
|
|
|
+ movshdup_2 = "rmo:F30F16rM",
|
|
|
+ movsldup_2 = "rmo:F30F12rM",
|
|
|
+
|
|
|
+ -- SSSE3 ops
|
|
|
+ pabsb_2 = "rmo:660F381CrM",
|
|
|
+ pabsd_2 = "rmo:660F381ErM",
|
|
|
+ pabsw_2 = "rmo:660F381DrM",
|
|
|
+ palignr_3 = "rmio:660F3A0FrMU",
|
|
|
+ phaddd_2 = "rmo:660F3802rM",
|
|
|
+ phaddsw_2 = "rmo:660F3803rM",
|
|
|
+ phaddw_2 = "rmo:660F3801rM",
|
|
|
+ phsubd_2 = "rmo:660F3806rM",
|
|
|
+ phsubsw_2 = "rmo:660F3807rM",
|
|
|
+ phsubw_2 = "rmo:660F3805rM",
|
|
|
+ pmaddubsw_2 = "rmo:660F3804rM",
|
|
|
+ pmulhrsw_2 = "rmo:660F380BrM",
|
|
|
+ pshufb_2 = "rmo:660F3800rM",
|
|
|
+ psignb_2 = "rmo:660F3808rM",
|
|
|
+ psignd_2 = "rmo:660F380ArM",
|
|
|
+ psignw_2 = "rmo:660F3809rM",
|
|
|
+
|
|
|
+ -- SSE4.1 ops
|
|
|
+ blendpd_3 = "rmio:660F3A0DrMU",
|
|
|
+ blendps_3 = "rmio:660F3A0CrMU",
|
|
|
+ blendvpd_3 = "rmRo:660F3815rM",
|
|
|
+ blendvps_3 = "rmRo:660F3814rM",
|
|
|
+ dppd_3 = "rmio:660F3A41rMU",
|
|
|
+ dpps_3 = "rmio:660F3A40rMU",
|
|
|
+ extractps_3 = "mri/do:660F3A17RmU",
|
|
|
+ insertps_3 = "rrio:660F3A41rMU|rxi/od:",
|
|
|
+ movntdqa_2 = "rmo:660F382ArM",
|
|
|
+ mpsadbw_3 = "rmio:660F3A42rMU",
|
|
|
+ packusdw_2 = "rmo:660F382BrM",
|
|
|
+ pblendvb_3 = "rmRo:660F3810rM",
|
|
|
+ pblendw_3 = "rmio:660F3A0ErMU",
|
|
|
+ pcmpeqq_2 = "rmo:660F3829rM",
|
|
|
+ pextrb_3 = "rri/do:660F3A14nRmU|xri/bo:",
|
|
|
+ pextrd_3 = "mri/do:660F3A16RmU",
|
|
|
+ -- x64: pextrq
|
|
|
+ -- pextrw is SSE2, mem operand is SSE4.1 only
|
|
|
+ phminposuw_2 = "rmo:660F3841rM",
|
|
|
+ pinsrb_3 = "rri/od:660F3A20nrMU|rxi/ob:",
|
|
|
+ pinsrd_3 = "rmi/od:660F3A22rMU",
|
|
|
+ -- x64: pinsrq
|
|
|
+ pmaxsb_2 = "rmo:660F383CrM",
|
|
|
+ pmaxsd_2 = "rmo:660F383DrM",
|
|
|
+ pmaxud_2 = "rmo:660F383FrM",
|
|
|
+ pmaxuw_2 = "rmo:660F383ErM",
|
|
|
+ pminsb_2 = "rmo:660F3838rM",
|
|
|
+ pminsd_2 = "rmo:660F3839rM",
|
|
|
+ pminud_2 = "rmo:660F383BrM",
|
|
|
+ pminuw_2 = "rmo:660F383ArM",
|
|
|
+ pmovsxbd_2 = "rro:660F3821rM|rx/od:",
|
|
|
+ pmovsxbq_2 = "rro:660F3822rM|rx/ow:",
|
|
|
+ pmovsxbw_2 = "rro:660F3820rM|rx/oq:",
|
|
|
+ pmovsxdq_2 = "rro:660F3825rM|rx/oq:",
|
|
|
+ pmovsxwd_2 = "rro:660F3823rM|rx/oq:",
|
|
|
+ pmovsxwq_2 = "rro:660F3824rM|rx/od:",
|
|
|
+ pmovzxbd_2 = "rro:660F3831rM|rx/od:",
|
|
|
+ pmovzxbq_2 = "rro:660F3832rM|rx/ow:",
|
|
|
+ pmovzxbw_2 = "rro:660F3830rM|rx/oq:",
|
|
|
+ pmovzxdq_2 = "rro:660F3835rM|rx/oq:",
|
|
|
+ pmovzxwd_2 = "rro:660F3833rM|rx/oq:",
|
|
|
+ pmovzxwq_2 = "rro:660F3834rM|rx/od:",
|
|
|
+ pmuldq_2 = "rmo:660F3828rM",
|
|
|
+ pmulld_2 = "rmo:660F3840rM",
|
|
|
+ ptest_2 = "rmo:660F3817rM",
|
|
|
+ roundpd_3 = "rmio:660F3A09rMU",
|
|
|
+ roundps_3 = "rmio:660F3A08rMU",
|
|
|
+ roundsd_3 = "rrio:660F3A0BrMU|rxi/oq:",
|
|
|
+ roundss_3 = "rrio:660F3A0ArMU|rxi/od:",
|
|
|
+
|
|
|
+ -- SSE4.2 ops
|
|
|
+ crc32_2 = "rmd:F20F38F1rM|rm/dw:66F20F38F1rM|rm/db:F20F38F0nrM",
|
|
|
+ pcmpestri_3 = "rmio:660F3A61rMU",
|
|
|
+ pcmpestrm_3 = "rmio:660F3A60rMU",
|
|
|
+ pcmpgtq_2 = "rmo:660F3837rM",
|
|
|
+ pcmpistri_3 = "rmio:660F3A63rMU",
|
|
|
+ pcmpistrm_3 = "rmio:660F3A62rMU",
|
|
|
+ popcnt_2 = "rmdw:F30FB8rM",
|
|
|
+
|
|
|
+ -- SSE4a
|
|
|
+ extrq_2 = "rro:660F79rM",
|
|
|
+ extrq_3 = "riio:660F780mUU",
|
|
|
+ insertq_2 = "rro:F20F79rM",
|
|
|
+ insertq_4 = "rriio:F20F78rMUU",
|
|
|
+ lzcnt_2 = "rmdw:F30FBDrM",
|
|
|
+ movntsd_2 = "xr/qo:F20F2BRm",
|
|
|
+ movntss_2 = "xr/do:F30F2BRm",
|
|
|
+ -- popcnt is also in SSE4.2
|
|
|
+}
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- Arithmetic ops.
|
|
|
+for name,n in pairs{ add = 0, ["or"] = 1, adc = 2, sbb = 3,
|
|
|
+ ["and"] = 4, sub = 5, xor = 6, cmp = 7 } do
|
|
|
+ local n8 = n * 8
|
|
|
+ map_op[name.."_2"] = format(
|
|
|
+ "mr:%02XRm|rm:%02XrM|mI1dw:81%XmI|mS1dw:83%XmS|Ri1dwb:%02Xri|mi1dwb:81%Xmi",
|
|
|
+ 1+n8, 3+n8, n, n, 5+n8, n)
|
|
|
+end
|
|
|
+
|
|
|
+-- Shift ops.
|
|
|
+for name,n in pairs{ rol = 0, ror = 1, rcl = 2, rcr = 3,
|
|
|
+ shl = 4, shr = 5, sar = 7, sal = 4 } do
|
|
|
+ map_op[name.."_2"] = format("m1:D1%Xm|mC1dwb:D3%Xm|mi:C1%XmU", n, n, n)
|
|
|
+end
|
|
|
+
|
|
|
+-- Conditional ops.
|
|
|
+for cc,n in pairs(map_cc) do
|
|
|
+ map_op["j"..cc.."_1"] = format("J.:0F8%XJ", n) -- short: 7%X
|
|
|
+ map_op["set"..cc.."_1"] = format("mb:n0F9%X2m", n)
|
|
|
+ map_op["cmov"..cc.."_2"] = format("rmdw:0F4%XrM", n) -- P6+
|
|
|
+end
|
|
|
+
|
|
|
+-- FP arithmetic ops.
|
|
|
+for name,n in pairs{ add = 0, mul = 1, com = 2, comp = 3,
|
|
|
+ sub = 4, subr = 5, div = 6, divr = 7 } do
|
|
|
+ local nc = 192 + n * 8
|
|
|
+ local nr = nc + (n < 4 and 0 or (n % 2 == 0 and 8 or -8))
|
|
|
+ local fn = "f"..name
|
|
|
+ map_op[fn.."_1"] = format("ff:D8%02Xr|xd:D8%Xm|xq:DC%Xm", nc, n, n)
|
|
|
+ if n == 2 or n == 3 then
|
|
|
+ map_op[fn.."_2"] = format("Fff:D8%02XR|Fx2d:D8%XM|Fx2q:DC%XM", nc, n, n)
|
|
|
+ else
|
|
|
+ map_op[fn.."_2"] = format("Fff:D8%02XR|fFf:DC%02Xr|Fx2d:D8%XM|Fx2q:DC%XM", nc, nr, n, n)
|
|
|
+ map_op[fn.."p_1"] = format("ff:DE%02Xr", nr)
|
|
|
+ map_op[fn.."p_2"] = format("fFf:DE%02Xr", nr)
|
|
|
+ end
|
|
|
+ map_op["fi"..name.."_1"] = format("xd:DA%Xm|xw:nDE%Xm", n, n)
|
|
|
+end
|
|
|
+
|
|
|
+-- FP conditional moves.
|
|
|
+for cc,n in pairs{ b=0, e=1, be=2, u=3, nb=4, ne=5, nbe=6, nu=7 } do
|
|
|
+ local n4 = n % 4
|
|
|
+ local nc = 56000 + n4 * 8 + (n-n4) * 64
|
|
|
+ map_op["fcmov"..cc.."_1"] = format("ff:%04Xr", nc) -- P6+
|
|
|
+ map_op["fcmov"..cc.."_2"] = format("Fff:%04XR", nc) -- P6+
|
|
|
+end
|
|
|
+
|
|
|
+-- SSE FP arithmetic ops.
|
|
|
+for name,n in pairs{ sqrt = 1, add = 8, mul = 9,
|
|
|
+ sub = 12, min = 13, div = 14, max = 15 } do
|
|
|
+ map_op[name.."ps_2"] = format("rmo:0F5%XrM", n)
|
|
|
+ map_op[name.."ss_2"] = format("rro:F30F5%XrM|rx/od:", n)
|
|
|
+ map_op[name.."pd_2"] = format("rmo:660F5%XrM", n)
|
|
|
+ map_op[name.."sd_2"] = format("rro:F20F5%XrM|rx/oq:", n)
|
|
|
+end
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- Process pattern string.
|
|
|
+local function dopattern(pat, args, sz, op)
|
|
|
+ local digit, addin
|
|
|
+ local opcode = 0
|
|
|
+ local szov = sz
|
|
|
+ local narg = 1
|
|
|
+
|
|
|
+ -- Limit number of section buffer positions used by a single dasm_put().
|
|
|
+ -- A single opcode needs a maximum of 2 positions. !x64
|
|
|
+ if secpos+2 > maxsecpos then wflush() end
|
|
|
+
|
|
|
+ -- Process each character.
|
|
|
+ for c in gmatch(pat.."|", ".") do
|
|
|
+ if match(c, "%x") then -- Hex digit.
|
|
|
+ digit = byte(c) - 48
|
|
|
+ if digit > 48 then digit = digit - 39
|
|
|
+ elseif digit > 16 then digit = digit - 7 end
|
|
|
+ opcode = opcode*16 + digit
|
|
|
+ addin = nil
|
|
|
+ elseif c == "n" then -- Disable operand size mods for opcode.
|
|
|
+ szov = nil
|
|
|
+ elseif c == "r" then -- Merge 1st operand regno. into opcode.
|
|
|
+ addin = args[1]; opcode = opcode + addin.reg
|
|
|
+ if narg < 2 then narg = 2 end
|
|
|
+ elseif c == "R" then -- Merge 2nd operand regno. into opcode.
|
|
|
+ addin = args[2]; opcode = opcode + addin.reg
|
|
|
+ narg = 3
|
|
|
+ elseif c == "m" or c == "M" then -- Encode ModRM/SIB.
|
|
|
+ local s
|
|
|
+ if addin then
|
|
|
+ s = addin.reg
|
|
|
+ opcode = opcode - s -- Undo regno opcode merge.
|
|
|
+ else
|
|
|
+ s = opcode % 16 -- Undo last digit.
|
|
|
+ opcode = (opcode - s) / 16
|
|
|
+ end
|
|
|
+ wputop(szov, opcode); opcode = nil
|
|
|
+ local imark = (sub(pat, -1) == "I") -- Force a mark (ugly).
|
|
|
+ -- Put ModRM/SIB with regno/last digit as spare.
|
|
|
+ local nn = c == "m" and 1 or 2
|
|
|
+ wputmrmsib(args[nn], imark, s, addin and addin.vreg)
|
|
|
+ if narg <= nn then narg = nn + 1 end
|
|
|
+ addin = nil
|
|
|
+ else
|
|
|
+ if opcode then -- Flush opcode.
|
|
|
+ if addin and addin.reg == -1 then
|
|
|
+ wputop(szov, opcode + 1)
|
|
|
+ waction("VREG", addin.vreg); wputxb(0)
|
|
|
+ else
|
|
|
+ wputop(szov, opcode)
|
|
|
+ end
|
|
|
+ opcode = nil
|
|
|
+ end
|
|
|
+ if c == "|" then break end
|
|
|
+ if c == "o" then -- Offset (pure 32 bit displacement).
|
|
|
+ wputdarg(args[1].disp); if narg < 2 then narg = 2 end
|
|
|
+ elseif c == "O" then
|
|
|
+ wputdarg(args[2].disp); narg = 3
|
|
|
+ else
|
|
|
+ -- Anything else is an immediate operand.
|
|
|
+ local a = args[narg]
|
|
|
+ narg = narg + 1
|
|
|
+ local mode, imm = a.mode, a.imm
|
|
|
+ if mode == "iJ" and not match("iIJ", c) then
|
|
|
+ werror("bad operand size for label")
|
|
|
+ end
|
|
|
+ if c == "S" then
|
|
|
+ wputsbarg(imm)
|
|
|
+ elseif c == "U" then
|
|
|
+ wputbarg(imm)
|
|
|
+ elseif c == "W" then
|
|
|
+ wputwarg(imm)
|
|
|
+ elseif c == "i" or c == "I" then
|
|
|
+ if mode == "iJ" then
|
|
|
+ wputlabel("IMM_", imm, 1)
|
|
|
+ elseif mode == "iI" and c == "I" then
|
|
|
+ waction(sz == "w" and "IMM_WB" or "IMM_DB", imm)
|
|
|
+ else
|
|
|
+ wputszarg(sz, imm)
|
|
|
+ end
|
|
|
+ elseif c == "J" then
|
|
|
+ if mode == "iPJ" then
|
|
|
+ waction("REL_A", imm) -- !x64 (secpos)
|
|
|
+ else
|
|
|
+ wputlabel("REL_", imm, 2)
|
|
|
+ end
|
|
|
+ else
|
|
|
+ werror("bad char `"..c.."' in pattern `"..pat.."' for `"..op.."'")
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- Mapping of operand modes to short names. Suppress output with '#'.
|
|
|
+local map_modename = {
|
|
|
+ r = "reg", R = "eax", C = "cl", x = "mem", m = "mrm", i = "imm",
|
|
|
+ f = "stx", F = "st0", J = "lbl", ["1"] = "1",
|
|
|
+ I = "#", S = "#", O = "#",
|
|
|
+}
|
|
|
+
|
|
|
+-- Return a table/string showing all possible operand modes.
|
|
|
+local function templatehelp(template, nparams)
|
|
|
+ if nparams == 0 then return "" end
|
|
|
+ local t = {}
|
|
|
+ for tm in gmatch(template, "[^%|]+") do
|
|
|
+ local s = map_modename[sub(tm, 1, 1)]
|
|
|
+ s = s..gsub(sub(tm, 2, nparams), ".", function(c)
|
|
|
+ return ", "..map_modename[c]
|
|
|
+ end)
|
|
|
+ if not match(s, "#") then t[#t+1] = s end
|
|
|
+ end
|
|
|
+ return t
|
|
|
+end
|
|
|
+
|
|
|
+-- Match operand modes against mode match part of template.
|
|
|
+local function matchtm(tm, args)
|
|
|
+ for i=1,#args do
|
|
|
+ if not match(args[i].mode, sub(tm, i, i)) then return end
|
|
|
+ end
|
|
|
+ return true
|
|
|
+end
|
|
|
+
|
|
|
+-- Handle opcodes defined with template strings.
|
|
|
+map_op[".template__"] = function(params, template, nparams)
|
|
|
+ if not params then return templatehelp(template, nparams) end
|
|
|
+ local args = {}
|
|
|
+
|
|
|
+ -- Zero-operand opcodes have no match part.
|
|
|
+ if #params == 0 then
|
|
|
+ dopattern(template, args, "d", params.op)
|
|
|
+ return
|
|
|
+ end
|
|
|
+
|
|
|
+ -- Determine common operand size (coerce undefined size) or flag as mixed.
|
|
|
+ local sz, szmix
|
|
|
+ for i,p in ipairs(params) do
|
|
|
+ args[i] = parseoperand(p)
|
|
|
+ local nsz = args[i].opsize
|
|
|
+ if nsz then
|
|
|
+ if sz and sz ~= nsz then szmix = true else sz = nsz end
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ -- Try all match:pattern pairs (separated by '|').
|
|
|
+ local gotmatch, lastpat
|
|
|
+ for tm in gmatch(template, "[^%|]+") do
|
|
|
+ -- Split off size match (starts after mode match) and pattern string.
|
|
|
+ local szm, pat = match(tm, "^(.-):(.*)$", #args+1)
|
|
|
+ if pat == "" then pat = lastpat else lastpat = pat end
|
|
|
+ if matchtm(tm, args) then
|
|
|
+ local prefix = sub(szm, 1, 1)
|
|
|
+ if prefix == "/" then -- Match both operand sizes.
|
|
|
+ if args[1].opsize == sub(szm, 2, 2) and
|
|
|
+ args[2].opsize == sub(szm, 3, 3) then
|
|
|
+ dopattern(pat, args, sz, params.op) -- Process pattern string.
|
|
|
+ return
|
|
|
+ end
|
|
|
+ else -- Match common operand size.
|
|
|
+ local szp = sz
|
|
|
+ if szm == "" then szm = "dwb" end -- Default size match.
|
|
|
+ if prefix == "1" then szp = args[1].opsize; szmix = nil
|
|
|
+ elseif prefix == "2" then szp = args[2].opsize; szmix = nil end
|
|
|
+ if not szmix and (prefix == "." or match(szm, szp or "#")) then
|
|
|
+ dopattern(pat, args, szp, params.op) -- Process pattern string.
|
|
|
+ return
|
|
|
+ end
|
|
|
+ end
|
|
|
+ gotmatch = true
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ local msg = "bad operand mode"
|
|
|
+ if gotmatch then
|
|
|
+ if szmix then
|
|
|
+ msg = "mixed operand size"
|
|
|
+ else
|
|
|
+ msg = sz and "bad operand size" or "missing operand size"
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ werror(msg.." in `"..opmodestr(params.op, args).."'")
|
|
|
+end
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- Pseudo-opcodes for data storage.
|
|
|
+local function op_data(params)
|
|
|
+ if not params then return "imm..." end
|
|
|
+ local sz = sub(params.op, 2, 2)
|
|
|
+ if sz == "a" then sz = addrsize end
|
|
|
+ for _,p in ipairs(params) do
|
|
|
+ local a = parseoperand(p)
|
|
|
+ if sub(a.mode, 1, 1) ~= "i" or (a.opsize and a.opsize ~= sz) then
|
|
|
+ werror("bad mode or size in `"..p.."'")
|
|
|
+ end
|
|
|
+ if a.mode == "iJ" then
|
|
|
+ wputlabel("IMM_", a.imm, 1)
|
|
|
+ else
|
|
|
+ wputszarg(sz, a.imm)
|
|
|
+ end
|
|
|
+ end
|
|
|
+end
|
|
|
+
|
|
|
+map_op[".byte_*"] = op_data
|
|
|
+map_op[".sbyte_*"] = op_data
|
|
|
+map_op[".word_*"] = op_data
|
|
|
+map_op[".dword_*"] = op_data
|
|
|
+map_op[".aword_*"] = op_data
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- 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_2"] = function(params)
|
|
|
+ if not params then return "[1-9] | ->global | =>pcexpr [, addr]" end
|
|
|
+ local a = parseoperand(params[1])
|
|
|
+ local mode, imm = a.mode, a.imm
|
|
|
+ if type(imm) == "number" and (mode == "iJ" or (imm >= 1 and imm <= 9)) then
|
|
|
+ -- Local label (1: ... 9:) or global label (->global:).
|
|
|
+ waction("LABEL_LG", nil, 1)
|
|
|
+ wputxb(imm)
|
|
|
+ elseif mode == "iJ" then
|
|
|
+ -- PC label (=>pcexpr:).
|
|
|
+ waction("LABEL_PC", imm)
|
|
|
+ else
|
|
|
+ werror("bad label definition")
|
|
|
+ end
|
|
|
+ -- SETLABEL must immediately follow LABEL_LG/LABEL_PC.
|
|
|
+ local addr = params[2]
|
|
|
+ if addr then
|
|
|
+ local a = parseoperand(params[2])
|
|
|
+ if a.mode == "iPJ" then
|
|
|
+ waction("SETLABEL", a.imm) -- !x64 (secpos)
|
|
|
+ else
|
|
|
+ werror("bad label assignment")
|
|
|
+ end
|
|
|
+ end
|
|
|
+end
|
|
|
+map_op[".label_1"] = map_op[".label_2"]
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- Alignment pseudo-opcode.
|
|
|
+map_op[".align_1"] = function(params)
|
|
|
+ if not params then return "numpow2" end
|
|
|
+ local align = tonumber(params[1]) or map_opsizenum[map_opsize[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", nil, 1)
|
|
|
+ wputxb(align-1) -- Action byte is 2**n-1.
|
|
|
+ return
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+ werror("bad alignment")
|
|
|
+end
|
|
|
+
|
|
|
+-- Spacing pseudo-opcode.
|
|
|
+map_op[".space_2"] = function(params)
|
|
|
+ if not params then return "num [, filler]" end
|
|
|
+ waction("SPACE", params[1])
|
|
|
+ local fill = params[2]
|
|
|
+ if fill then
|
|
|
+ fill = tonumber(fill)
|
|
|
+ if not fill or fill < 0 or fill > 255 then werror("bad filler") end
|
|
|
+ end
|
|
|
+ wputxb(fill or 0)
|
|
|
+end
|
|
|
+map_op[".space_1"] = map_op[".space_2"]
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|
|
|
+-- 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
|
|
|
+ if reg and not map_reg_valid_base[reg] then
|
|
|
+ werror("bad base register `"..(map_reg_rev[reg] or reg).."'")
|
|
|
+ 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 and map_reg_rev[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")
|
|
|
+ wputxb(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))
|
|
|
+ dumpregs(out)
|
|
|
+ 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
|
|
|
+
|
|
|
+------------------------------------------------------------------------------
|
|
|
+
|