📄 dynasm.lua
字号:
end) g_synclineno = 0end-- Dump all captures and their buffers (with -PP only).local function dumpcaptures(out, lvl) out:write("Captures:\n") for name,buf in pairs(cap_buffers) do out:write(format(" %-20s %4s)\n", name, "("..#buf)) if lvl > 1 then local bar = rep("=", 76) out:write(" ", bar, "\n") for _,line in ipairs(buf) do out:write(" ", line, "\n") end out:write(" ", bar, "\n\n") end end out:write("\n")end-- Check for unfinished or unused captures.local function checkcaptures() if cap_name then wprinterr(g_fname, ":", cap_lineno, ": error: unfinished .capture `", cap_name,"'\n") return end for name in pairs(cap_buffers) do if not cap_used[name] then wprinterr(g_fname, ":*: error: missing .dumpcapture ", name ,"\n") end endend-------------------------------------------------------------------------------- Sections names.local map_sections = {}-- Pseudo-opcode to define code sections.-- TODO: Data sections, BSS sections. Needs extra C code and API.map_coreop[".section_*"] = function(params) if not params then return "name..." end if #map_sections > 0 then werror("duplicate section definition") end wflush() for sn,name in ipairs(params) do local opname = "."..name.."_0" if not match(name, "^[%a][%w_]*$") or map_op[opname] or map_op["."..name.."_*"] then werror("bad section name `"..name.."'") end map_sections[#map_sections+1] = name wline(format("#define DASM_SECTION_%s\t%d", upper(name), sn-1)) map_op[opname] = function(params) g_arch.section(sn-1) end end wline(format("#define DASM_MAXSECTION\t\t%d", #map_sections))end-- Dump all sections.local function dumpsections(out, lvl) out:write("Sections:\n") for _,name in ipairs(map_sections) do out:write(format(" %s\n", name)) end out:write("\n")end-------------------------------------------------------------------------------- Load architecture-specific module.local function loadarch(arch) if not match(arch, "^[%w_]+$") then return "bad arch name" end local ok, m_arch = pcall(require, "dasm_"..arch) if not ok then return "cannot load module: "..m_arch end g_arch = m_arch wflush = m_arch.passcb(wline, werror, wfatal, wwarn) m_arch.setup(arch, g_opt) map_op, map_def = m_arch.mergemaps(map_coreop, map_def)end-- Dump architecture description.function opt_map.dumparch(args) local name = optparam(args) if not g_arch then local err = loadarch(name) if err then opterror(err) end end local t = {} for name in pairs(map_coreop) do t[#t+1] = name end for name in pairs(map_op) do t[#t+1] = name end sort(t) local out = stdout local _arch = g_arch._info out:write(format("%s version %s, released %s, %s\n", _info.name, _info.version, _info.release, _info.url)) g_arch.dumparch(out) local pseudo = true out:write("Pseudo-Opcodes:\n") for _,sname in ipairs(t) do local name, nparam = match(sname, "^(.+)_([0-9%*])$") if name then if pseudo and sub(name, 1, 1) ~= "." then out:write("\nOpcodes:\n") pseudo = false end local f = map_op[sname] local s if nparam ~= "*" then nparam = nparam + 0 end if nparam == 0 then s = "" elseif type(f) == "string" then s = map_op[".template__"](nil, f, nparam) else s = f(nil, nparam) end if type(s) == "table" then for _,s2 in ipairs(s) do out:write(format(" %-12s %s\n", name, s2)) end else out:write(format(" %-12s %s\n", name, s)) end end end out:write("\n") exit(0)end-- Pseudo-opcode to set the architecture.-- Only initially available (map_op is replaced when called).map_op[".arch_1"] = function(params) if not params then return "name" end local err = loadarch(params[1]) if err then wfatal(err) endend-- Dummy .arch pseudo-opcode to improve the error report.map_coreop[".arch_1"] = function(params) if not params then return "name" end wfatal("duplicate .arch statement")end-------------------------------------------------------------------------------- Dummy pseudo-opcode. Don't confuse '.nop' with 'nop'.map_coreop[".nop_*"] = function(params) if not params then return "[ignored...]" endend-- Pseudo-opcodes to raise errors.map_coreop[".error_1"] = function(params) if not params then return "message" end werror(params[1])endmap_coreop[".fatal_1"] = function(params) if not params then return "message" end wfatal(params[1])end-- Dump all user defined elements.local function dumpdef(out) local lvl = g_opt.dumpdef if lvl == 0 then return end dumpsections(out, lvl) dumpdefines(out, lvl) if g_arch then g_arch.dumpdef(out, lvl) end dumpmacros(out, lvl) dumpcaptures(out, lvl)end-------------------------------------------------------------------------------- Helper for splitstmt.local splitlvllocal function splitstmt_one(c) if c == "(" then splitlvl = ")"..splitlvl elseif c == "[" then splitlvl = "]"..splitlvl elseif c == ")" or c == "]" then if sub(splitlvl, 1, 1) ~= c then werror("unbalanced () or []") end splitlvl = sub(splitlvl, 2) elseif splitlvl == "" then return " \0 " end return cend-- Split statement into (pseudo-)opcode and params.local function splitstmt(stmt) -- Convert label with trailing-colon into .label statement. local label = match(stmt, "^%s*(.+):%s*$") if label then return ".label", {label} end -- Split at commas and equal signs, but obey parentheses and brackets. splitlvl = "" stmt = gsub(stmt, "[,%(%)%[%]]", splitstmt_one) if splitlvl ~= "" then werror("unbalanced () or []") end -- Split off opcode. local op, other = match(stmt, "^%s*([^%s%z]+)%s*(.*)$") if not op then werror("bad statement syntax") end -- Split parameters. local params = {} for p in gmatch(other, "%s*(%Z+)%z?") do params[#params+1] = gsub(p, "%s+$", "") end if #params > 16 then werror("too many parameters") end params.op = op return op, paramsend-- Process a single statement.dostmt = function(stmt) -- Ignore empty statements. if match(stmt, "^%s*$") then return end -- Capture macro defs before substitution. if mac_capture then return mac_capture(stmt) end stmt = definesubst(stmt) -- Emit C code without parsing the line. if sub(stmt, 1, 1) == "|" then local tail = sub(stmt, 2) wflush() if sub(tail, 1, 2) == "//" then wcomment(tail) else wline(tail, true) end return end -- Split into (pseudo-)opcode and params. local op, params = splitstmt(stmt) -- Get opcode handler (matching # of parameters or generic handler). local f = map_op[op.."_"..#params] or map_op[op.."_*"] if not f then if not g_arch then wfatal("first statement must be .arch") end -- Improve error report. for i=0,16 do if map_op[op.."_"..i] then werror("wrong number of parameters for `"..op.."'") end end werror("unknown statement `"..op.."'") end -- Call opcode handler or special handler for template strings. if type(f) == "string" then map_op[".template__"](params, f) else f(params) endend-- Process a single line.local function doline(line) if g_opt.flushline then wflush() end -- Assembler line? local indent, aline = match(line, "^(%s*)%|(.*)$") if not aline then -- No, plain C code line, need to flush first. wflush() wsync() wline(line, false) return end g_indent = indent -- Remember current line indentation. -- Emit C code (even from macros). Avoids echo and line parsing. if sub(aline, 1, 1) == "|" then if not mac_capture then wsync() elseif g_opt.comment then wsync() wcomment(aline) end dostmt(aline) return end -- Echo assembler line as a comment. if g_opt.comment then wsync() wcomment(aline) end -- Strip assembler comments. aline = gsub(aline, "//.*$", "") -- Split line into statements at semicolons. if match(aline, ";") then for stmt in gmatch(aline, "[^;]+") do dostmt(stmt) end else dostmt(aline) endend-------------------------------------------------------------------------------- Write DynASM header.local function dasmhead(out) out:write(format([[/*** This file has been pre-processed with DynASM.** %s** DynASM version %s, DynASM %s version %s** DO NOT EDIT! The original file is in "%s".*/#if DASM_VERSION != %d#error "Version mismatch between DynASM and included encoding engine"#endif]], _info.url, _info.version, g_arch._info.arch, g_arch._info.version, g_fname, _info.vernum))end-- Read input file.readfile = function(fin) g_indent = "" g_lineno = 0 g_synclineno = -1 -- Process all lines. for line in fin:lines() do g_lineno = g_lineno + 1 g_curline = line local ok, err = pcall(doline, line) if not ok and wprinterr(err, "\n") then return true end end wflush() -- Close input file. assert(fin == stdin or fin:close())end-- Write output file.local function writefile(outfile) local fout -- Open output file. if outfile == nil or outfile == "-" then fout = stdout else fout = assert(io.open(outfile, "w")) end -- Write all buffered lines wdumplines(fout, g_wbuffer) -- Close output file. assert(fout == stdout or fout:close()) -- Optionally dump definitions. dumpdef(fout == stdout and stderr or stdout)end-- Translate an input file to an output file.local function translate(infile, outfile) g_wbuffer = {} g_indent = "" g_lineno = 0 g_synclineno = -1 -- Put header. wline(dasmhead) -- Read input file. local fin if infile == "-" then g_fname = "(stdin)" fin = stdin else g_fname = infile fin = assert(io.open(infile, "r")) end readfile(fin) -- Check for errors. if not g_arch then wprinterr(g_fname, ":*: error: missing .arch directive\n") end checkconds() checkmacros() checkcaptures() if g_errcount ~= 0 then stderr:write(g_fname, ":*: info: ", g_errcount, " error", (type(g_errcount) == "number" and g_errcount > 1) and "s" or "", " in input file -- no output file generated.\n") dumpdef(stderr) exit(1) end -- Write output file. writefile(outfile)end-------------------------------------------------------------------------------- Print help text.function opt_map.help() stdout:write("DynASM -- ", _info.description, ".\n") stdout:write("DynASM ", _info.version, " ", _info.release, " ", _info.url, "\n") stdout:write[[Usage: dynasm [OPTION]... INFILE.dasc|- -h, --help Display this help text. -V, --version Display version and copyright information. -o, --outfile FILE Output file name (default is stdout). -I, --include DIR Add directory to the include search path. -c, --ccomment Use /* */ comments for assembler lines. -C, --cppcomment Use // comments for assembler lines (default). -N, --nocomment Suppress assembler lines in output. -M, --maccomment Show macro expansions as comments (default off). -L, --nolineno Suppress CPP line number information in output. -F, --flushline Flush action list for every line. -D NAME[=SUBST] Define a substitution. -U NAME Undefine a substitution. -P, --dumpdef Dump defines, macros, etc. Repeat for more output. -A, --dumparch ARCH Load architecture ARCH and dump description.]] exit(0)end-- Print version information.function opt_map.version() stdout:write(format("%s version %s, released %s\n%s\n\n%s", _info.name, _info.version, _info.release, _info.url, _info.copyright)) exit(0)end-- Misc. options.function opt_map.outfile(args) g_opt.outfile = optparam(args) endfunction opt_map.include(args) insert(g_opt.include, 1, optparam(args)) endfunction opt_map.ccomment() g_opt.comment = "/*|"; g_opt.endcomment = " */" endfunction opt_map.cppcomment() g_opt.comment = "//|"; g_opt.endcomment = "" endfunction opt_map.nocomment() g_opt.comment = false endfunction opt_map.maccomment() g_opt.maccomment = true endfunction opt_map.nolineno() g_opt.cpp = false endfunction opt_map.flushline() g_opt.flushline = true endfunction opt_map.dumpdef() g_opt.dumpdef = g_opt.dumpdef + 1 end-------------------------------------------------------------------------------- Short aliases for long options.local opt_alias = { h = "help", ["?"] = "help", V = "version", o = "outfile", I = "include", c = "ccomment", C = "cppcomment", N = "nocomment", M = "maccomment", L = "nolineno", F = "flushline", P = "dumpdef", A = "dumparch",}-- Parse single option.local function parseopt(opt, args) opt_current = #opt == 1 and "-"..opt or "--"..opt local f = opt_map[opt] or opt_map[opt_alias[opt]] if not f then opterror("unrecognized option `", opt_current, "'. Try `--help'.\n") end f(args)end-- Parse arguments.local function parseargs(args) -- Default options. g_opt.comment = "//|" g_opt.endcomment = "" g_opt.cpp = true g_opt.dumpdef = 0 g_opt.include = { "" } -- Process all option arguments. args.argn = 1 repeat local a = args[args.argn] if not a then break end local lopt, opt = match(a, "^%-(%-?)(.+)") if not opt then break end args.argn = args.argn + 1 if lopt == "" then -- Loop through short options. for o in gmatch(opt, ".") do parseopt(o, args) end else -- Long option. parseopt(opt, args) end until false -- Check for proper number of arguments. local nargs = #args - args.argn + 1 if nargs ~= 1 then if nargs == 0 then if g_opt.dumpdef > 0 then return dumpdef(stdout) end end opt_map.help() end -- Translate a single input file to a single output file -- TODO: Handle multiple files? translate(args[args.argn], g_opt.outfile)end-------------------------------------------------------------------------------- Add the directory dynasm.lua resides in to the Lua module search path.local arg = argif arg and arg[0] then local prefix = match(arg[0], "^(.*/)") if prefix then package.path = prefix.."?.lua;"..package.path endend-- Start DynASM.parseargs{...}------------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -