ljx

FORK: LuaJIT with native 5.2 and 5.3 support
git clone https://git.neptards.moe/neptards/ljx.git
Log | Files | Refs | README

dynasm.lua (30989B)


      1 ------------------------------------------------------------------------------
      2 -- DynASM. A dynamic assembler for code generation engines.
      3 -- Originally designed and implemented for LuaJIT.
      4 --
      5 -- Copyright (C) 2005-2016 Mike Pall. All rights reserved.
      6 -- See below for full copyright notice.
      7 ------------------------------------------------------------------------------
      8 
      9 -- Application information.
     10 local _info = {
     11   name =	"DynASM",
     12   description =	"A dynamic assembler for code generation engines",
     13   version =	"1.4.0",
     14   vernum =	 10400,
     15   release =	"2015-10-18",
     16   author =	"Mike Pall",
     17   url =		"http://luajit.org/dynasm.html",
     18   license =	"MIT",
     19   copyright =	[[
     20 Copyright (C) 2005-2016 Mike Pall. All rights reserved.
     21 
     22 Permission is hereby granted, free of charge, to any person obtaining
     23 a copy of this software and associated documentation files (the
     24 "Software"), to deal in the Software without restriction, including
     25 without limitation the rights to use, copy, modify, merge, publish,
     26 distribute, sublicense, and/or sell copies of the Software, and to
     27 permit persons to whom the Software is furnished to do so, subject to
     28 the following conditions:
     29 
     30 The above copyright notice and this permission notice shall be
     31 included in all copies or substantial portions of the Software.
     32 
     33 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     34 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     35 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     36 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
     37 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     38 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     39 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     40 
     41 [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
     42 ]],
     43 }
     44 
     45 -- Cache library functions.
     46 local type, pairs, ipairs = type, pairs, ipairs
     47 local pcall, error, assert = pcall, error, assert
     48 local _s = string
     49 local sub, match, gmatch, gsub = _s.sub, _s.match, _s.gmatch, _s.gsub
     50 local format, rep, upper = _s.format, _s.rep, _s.upper
     51 local _t = table
     52 local insert, remove, concat, sort = _t.insert, _t.remove, _t.concat, _t.sort
     53 local exit = os.exit
     54 local io = io
     55 local stdin, stdout, stderr = io.stdin, io.stdout, io.stderr
     56 
     57 ------------------------------------------------------------------------------
     58 
     59 -- Program options.
     60 local g_opt = {}
     61 
     62 -- Global state for current file.
     63 local g_fname, g_curline, g_indent, g_lineno, g_synclineno, g_arch
     64 local g_errcount = 0
     65 
     66 -- Write buffer for output file.
     67 local g_wbuffer, g_capbuffer
     68 
     69 ------------------------------------------------------------------------------
     70 
     71 -- Write an output line (or callback function) to the buffer.
     72 local function wline(line, needindent)
     73   local buf = g_capbuffer or g_wbuffer
     74   buf[#buf+1] = needindent and g_indent..line or line
     75   g_synclineno = g_synclineno + 1
     76 end
     77 
     78 -- Write assembler line as a comment, if requestd.
     79 local function wcomment(aline)
     80   if g_opt.comment then
     81     wline(g_opt.comment..aline..g_opt.endcomment, true)
     82   end
     83 end
     84 
     85 -- Resync CPP line numbers.
     86 local function wsync()
     87   if g_synclineno ~= g_lineno and g_opt.cpp then
     88     wline("#line "..g_lineno..' "'..g_fname..'"')
     89     g_synclineno = g_lineno
     90   end
     91 end
     92 
     93 -- Dummy action flush function. Replaced with arch-specific function later.
     94 local function wflush(term)
     95 end
     96 
     97 -- Dump all buffered output lines.
     98 local function wdumplines(out, buf)
     99   for _,line in ipairs(buf) do
    100     if type(line) == "string" then
    101       assert(out:write(line, "\n"))
    102     else
    103       -- Special callback to dynamically insert lines after end of processing.
    104       line(out)
    105     end
    106   end
    107 end
    108 
    109 ------------------------------------------------------------------------------
    110 
    111 -- Emit an error. Processing continues with next statement.
    112 local function werror(msg)
    113   error(format("%s:%s: error: %s:\n%s", g_fname, g_lineno, msg, g_curline), 0)
    114 end
    115 
    116 -- Emit a fatal error. Processing stops.
    117 local function wfatal(msg)
    118   g_errcount = "fatal"
    119   werror(msg)
    120 end
    121 
    122 -- Print a warning. Processing continues.
    123 local function wwarn(msg)
    124   stderr:write(format("%s:%s: warning: %s:\n%s\n",
    125     g_fname, g_lineno, msg, g_curline))
    126 end
    127 
    128 -- Print caught error message. But suppress excessive errors.
    129 local function wprinterr(...)
    130   if type(g_errcount) == "number" then
    131     -- Regular error.
    132     g_errcount = g_errcount + 1
    133     if g_errcount < 21 then -- Seems to be a reasonable limit.
    134       stderr:write(...)
    135     elseif g_errcount == 21 then
    136       stderr:write(g_fname,
    137 	":*: warning: too many errors (suppressed further messages).\n")
    138     end
    139   else
    140     -- Fatal error.
    141     stderr:write(...)
    142     return true -- Stop processing.
    143   end
    144 end
    145 
    146 ------------------------------------------------------------------------------
    147 
    148 -- Map holding all option handlers.
    149 local opt_map = {}
    150 local opt_current
    151 
    152 -- Print error and exit with error status.
    153 local function opterror(...)
    154   stderr:write("dynasm.lua: ERROR: ", ...)
    155   stderr:write("\n")
    156   exit(1)
    157 end
    158 
    159 -- Get option parameter.
    160 local function optparam(args)
    161   local argn = args.argn
    162   local p = args[argn]
    163   if not p then
    164     opterror("missing parameter for option `", opt_current, "'.")
    165   end
    166   args.argn = argn + 1
    167   return p
    168 end
    169 
    170 ------------------------------------------------------------------------------
    171 
    172 -- Core pseudo-opcodes.
    173 local map_coreop = {}
    174 -- Dummy opcode map. Replaced by arch-specific map.
    175 local map_op = {}
    176 
    177 -- Forward declarations.
    178 local dostmt
    179 local readfile
    180 
    181 ------------------------------------------------------------------------------
    182 
    183 -- Map for defines (initially empty, chains to arch-specific map).
    184 local map_def = {}
    185 
    186 -- Pseudo-opcode to define a substitution.
    187 map_coreop[".define_2"] = function(params, nparams)
    188   if not params then return nparams == 1 and "name" or "name, subst" end
    189   local name, def = params[1], params[2] or "1"
    190   if not match(name, "^[%a_][%w_]*$") then werror("bad or duplicate define") end
    191   map_def[name] = def
    192 end
    193 map_coreop[".define_1"] = map_coreop[".define_2"]
    194 
    195 -- Define a substitution on the command line.
    196 function opt_map.D(args)
    197   local namesubst = optparam(args)
    198   local name, subst = match(namesubst, "^([%a_][%w_]*)=(.*)$")
    199   if name then
    200     map_def[name] = subst
    201   elseif match(namesubst, "^[%a_][%w_]*$") then
    202     map_def[namesubst] = "1"
    203   else
    204     opterror("bad define")
    205   end
    206 end
    207 
    208 -- Undefine a substitution on the command line.
    209 function opt_map.U(args)
    210   local name = optparam(args)
    211   if match(name, "^[%a_][%w_]*$") then
    212     map_def[name] = nil
    213   else
    214     opterror("bad define")
    215   end
    216 end
    217 
    218 -- Helper for definesubst.
    219 local gotsubst
    220 
    221 local function definesubst_one(word)
    222   local subst = map_def[word]
    223   if subst then gotsubst = word; return subst else return word end
    224 end
    225 
    226 -- Iteratively substitute defines.
    227 local function definesubst(stmt)
    228   -- Limit number of iterations.
    229   for i=1,100 do
    230     gotsubst = false
    231     stmt = gsub(stmt, "#?[%w_]+", definesubst_one)
    232     if not gotsubst then break end
    233   end
    234   if gotsubst then wfatal("recursive define involving `"..gotsubst.."'") end
    235   return stmt
    236 end
    237 
    238 -- Dump all defines.
    239 local function dumpdefines(out, lvl)
    240   local t = {}
    241   for name in pairs(map_def) do
    242     t[#t+1] = name
    243   end
    244   sort(t)
    245   out:write("Defines:\n")
    246   for _,name in ipairs(t) do
    247     local subst = map_def[name]
    248     if g_arch then subst = g_arch.revdef(subst) end
    249     out:write(format("  %-20s %s\n", name, subst))
    250   end
    251   out:write("\n")
    252 end
    253 
    254 ------------------------------------------------------------------------------
    255 
    256 -- Support variables for conditional assembly.
    257 local condlevel = 0
    258 local condstack = {}
    259 
    260 -- Evaluate condition with a Lua expression. Substitutions already performed.
    261 local function cond_eval(cond)
    262   local func, err
    263   if setfenv then
    264     func, err = loadstring("return "..cond, "=expr")
    265   else
    266     -- No globals. All unknown identifiers evaluate to nil.
    267     func, err = load("return "..cond, "=expr", "t", {})
    268   end
    269   if func then
    270     if setfenv then
    271       setfenv(func, {}) -- No globals. All unknown identifiers evaluate to nil.
    272     end
    273     local ok, res = pcall(func)
    274     if ok then
    275       if res == 0 then return false end -- Oh well.
    276       return not not res
    277     end
    278     err = res
    279   end
    280   wfatal("bad condition: "..err)
    281 end
    282 
    283 -- Skip statements until next conditional pseudo-opcode at the same level.
    284 local function stmtskip()
    285   local dostmt_save = dostmt
    286   local lvl = 0
    287   dostmt = function(stmt)
    288     local op = match(stmt, "^%s*(%S+)")
    289     if op == ".if" then
    290       lvl = lvl + 1
    291     elseif lvl ~= 0 then
    292       if op == ".endif" then lvl = lvl - 1 end
    293     elseif op == ".elif" or op == ".else" or op == ".endif" then
    294       dostmt = dostmt_save
    295       dostmt(stmt)
    296     end
    297   end
    298 end
    299 
    300 -- Pseudo-opcodes for conditional assembly.
    301 map_coreop[".if_1"] = function(params)
    302   if not params then return "condition" end
    303   local lvl = condlevel + 1
    304   local res = cond_eval(params[1])
    305   condlevel = lvl
    306   condstack[lvl] = res
    307   if not res then stmtskip() end
    308 end
    309 
    310 map_coreop[".elif_1"] = function(params)
    311   if not params then return "condition" end
    312   if condlevel == 0 then wfatal(".elif without .if") end
    313   local lvl = condlevel
    314   local res = condstack[lvl]
    315   if res then
    316     if res == "else" then wfatal(".elif after .else") end
    317   else
    318     res = cond_eval(params[1])
    319     if res then
    320       condstack[lvl] = res
    321       return
    322     end
    323   end
    324   stmtskip()
    325 end
    326 
    327 map_coreop[".else_0"] = function(params)
    328   if condlevel == 0 then wfatal(".else without .if") end
    329   local lvl = condlevel
    330   local res = condstack[lvl]
    331   condstack[lvl] = "else"
    332   if res then
    333     if res == "else" then wfatal(".else after .else") end
    334     stmtskip()
    335   end
    336 end
    337 
    338 map_coreop[".endif_0"] = function(params)
    339   local lvl = condlevel
    340   if lvl == 0 then wfatal(".endif without .if") end
    341   condlevel = lvl - 1
    342 end
    343 
    344 -- Check for unfinished conditionals.
    345 local function checkconds()
    346   if g_errcount ~= "fatal" and condlevel ~= 0 then
    347     wprinterr(g_fname, ":*: error: unbalanced conditional\n")
    348   end
    349 end
    350 
    351 ------------------------------------------------------------------------------
    352 
    353 -- Search for a file in the given path and open it for reading.
    354 local function pathopen(path, name)
    355   local dirsep = package and match(package.path, "\\") and "\\" or "/"
    356   for _,p in ipairs(path) do
    357     local fullname = p == "" and name or p..dirsep..name
    358     local fin = io.open(fullname, "r")
    359     if fin then
    360       g_fname = fullname
    361       return fin
    362     end
    363   end
    364 end
    365 
    366 -- Include a file.
    367 map_coreop[".include_1"] = function(params)
    368   if not params then return "filename" end
    369   local name = params[1]
    370   -- Save state. Ugly, I know. but upvalues are fast.
    371   local gf, gl, gcl, gi = g_fname, g_lineno, g_curline, g_indent
    372   -- Read the included file.
    373   local fatal = readfile(pathopen(g_opt.include, name) or
    374 			 wfatal("include file `"..name.."' not found"))
    375   -- Restore state.
    376   g_synclineno = -1
    377   g_fname, g_lineno, g_curline, g_indent = gf, gl, gcl, gi
    378   if fatal then wfatal("in include file") end
    379 end
    380 
    381 -- Make .include and conditionals initially available, too.
    382 map_op[".include_1"] = map_coreop[".include_1"]
    383 map_op[".if_1"] = map_coreop[".if_1"]
    384 map_op[".elif_1"] = map_coreop[".elif_1"]
    385 map_op[".else_0"] = map_coreop[".else_0"]
    386 map_op[".endif_0"] = map_coreop[".endif_0"]
    387 
    388 ------------------------------------------------------------------------------
    389 
    390 -- Support variables for macros.
    391 local mac_capture, mac_lineno, mac_name
    392 local mac_active = {}
    393 local mac_list = {}
    394 
    395 -- Pseudo-opcode to define a macro.
    396 map_coreop[".macro_*"] = function(mparams)
    397   if not mparams then return "name [, params...]" end
    398   -- Split off and validate macro name.
    399   local name = remove(mparams, 1)
    400   if not name then werror("missing macro name") end
    401   if not (match(name, "^[%a_][%w_%.]*$") or match(name, "^%.[%w_%.]*$")) then
    402     wfatal("bad macro name `"..name.."'")
    403   end
    404   -- Validate macro parameter names.
    405   local mdup = {}
    406   for _,mp in ipairs(mparams) do
    407     if not match(mp, "^[%a_][%w_]*$") then
    408       wfatal("bad macro parameter name `"..mp.."'")
    409     end
    410     if mdup[mp] then wfatal("duplicate macro parameter name `"..mp.."'") end
    411     mdup[mp] = true
    412   end
    413   -- Check for duplicate or recursive macro definitions.
    414   local opname = name.."_"..#mparams
    415   if map_op[opname] or map_op[name.."_*"] then
    416     wfatal("duplicate macro `"..name.."' ("..#mparams.." parameters)")
    417   end
    418   if mac_capture then wfatal("recursive macro definition") end
    419 
    420   -- Enable statement capture.
    421   local lines = {}
    422   mac_lineno = g_lineno
    423   mac_name = name
    424   mac_capture = function(stmt) -- Statement capture function.
    425     -- Stop macro definition with .endmacro pseudo-opcode.
    426     if not match(stmt, "^%s*.endmacro%s*$") then
    427       lines[#lines+1] = stmt
    428       return
    429     end
    430     mac_capture = nil
    431     mac_lineno = nil
    432     mac_name = nil
    433     mac_list[#mac_list+1] = opname
    434     -- Add macro-op definition.
    435     map_op[opname] = function(params)
    436       if not params then return mparams, lines end
    437       -- Protect against recursive macro invocation.
    438       if mac_active[opname] then wfatal("recursive macro invocation") end
    439       mac_active[opname] = true
    440       -- Setup substitution map.
    441       local subst = {}
    442       for i,mp in ipairs(mparams) do subst[mp] = params[i] end
    443       local mcom
    444       if g_opt.maccomment and g_opt.comment then
    445 	mcom = " MACRO "..name.." ("..#mparams..")"
    446 	wcomment("{"..mcom)
    447       end
    448       -- Loop through all captured statements
    449       for _,stmt in ipairs(lines) do
    450 	-- Substitute macro parameters.
    451 	local st = gsub(stmt, "[%w_]+", subst)
    452 	st = definesubst(st)
    453 	st = gsub(st, "%s*%.%.%s*", "") -- Token paste a..b.
    454 	if mcom and sub(st, 1, 1) ~= "|" then wcomment(st) end
    455 	-- Emit statement. Use a protected call for better diagnostics.
    456 	local ok, err = pcall(dostmt, st)
    457 	if not ok then
    458 	  -- Add the captured statement to the error.
    459 	  wprinterr(err, "\n", g_indent, "|  ", stmt,
    460 		    "\t[MACRO ", name, " (", #mparams, ")]\n")
    461 	end
    462       end
    463       if mcom then wcomment("}"..mcom) end
    464       mac_active[opname] = nil
    465     end
    466   end
    467 end
    468 
    469 -- An .endmacro pseudo-opcode outside of a macro definition is an error.
    470 map_coreop[".endmacro_0"] = function(params)
    471   wfatal(".endmacro without .macro")
    472 end
    473 
    474 -- Dump all macros and their contents (with -PP only).
    475 local function dumpmacros(out, lvl)
    476   sort(mac_list)
    477   out:write("Macros:\n")
    478   for _,opname in ipairs(mac_list) do
    479     local name = sub(opname, 1, -3)
    480     local params, lines = map_op[opname]()
    481     out:write(format("  %-20s %s\n", name, concat(params, ", ")))
    482     if lvl > 1 then
    483       for _,line in ipairs(lines) do
    484 	out:write("  |", line, "\n")
    485       end
    486       out:write("\n")
    487     end
    488   end
    489   out:write("\n")
    490 end
    491 
    492 -- Check for unfinished macro definitions.
    493 local function checkmacros()
    494   if mac_capture then
    495     wprinterr(g_fname, ":", mac_lineno,
    496 	      ": error: unfinished .macro `", mac_name ,"'\n")
    497   end
    498 end
    499 
    500 ------------------------------------------------------------------------------
    501 
    502 -- Support variables for captures.
    503 local cap_lineno, cap_name
    504 local cap_buffers = {}
    505 local cap_used = {}
    506 
    507 -- Start a capture.
    508 map_coreop[".capture_1"] = function(params)
    509   if not params then return "name" end
    510   wflush()
    511   local name = params[1]
    512   if not match(name, "^[%a_][%w_]*$") then
    513     wfatal("bad capture name `"..name.."'")
    514   end
    515   if cap_name then
    516     wfatal("already capturing to `"..cap_name.."' since line "..cap_lineno)
    517   end
    518   cap_name = name
    519   cap_lineno = g_lineno
    520   -- Create or continue a capture buffer and start the output line capture.
    521   local buf = cap_buffers[name]
    522   if not buf then buf = {}; cap_buffers[name] = buf end
    523   g_capbuffer = buf
    524   g_synclineno = 0
    525 end
    526 
    527 -- Stop a capture.
    528 map_coreop[".endcapture_0"] = function(params)
    529   wflush()
    530   if not cap_name then wfatal(".endcapture without a valid .capture") end
    531   cap_name = nil
    532   cap_lineno = nil
    533   g_capbuffer = nil
    534   g_synclineno = 0
    535 end
    536 
    537 -- Dump a capture buffer.
    538 map_coreop[".dumpcapture_1"] = function(params)
    539   if not params then return "name" end
    540   wflush()
    541   local name = params[1]
    542   if not match(name, "^[%a_][%w_]*$") then
    543     wfatal("bad capture name `"..name.."'")
    544   end
    545   cap_used[name] = true
    546   wline(function(out)
    547     local buf = cap_buffers[name]
    548     if buf then wdumplines(out, buf) end
    549   end)
    550   g_synclineno = 0
    551 end
    552 
    553 -- Dump all captures and their buffers (with -PP only).
    554 local function dumpcaptures(out, lvl)
    555   out:write("Captures:\n")
    556   for name,buf in pairs(cap_buffers) do
    557     out:write(format("  %-20s %4s)\n", name, "("..#buf))
    558     if lvl > 1 then
    559       local bar = rep("=", 76)
    560       out:write("  ", bar, "\n")
    561       for _,line in ipairs(buf) do
    562 	out:write("  ", line, "\n")
    563       end
    564       out:write("  ", bar, "\n\n")
    565     end
    566   end
    567   out:write("\n")
    568 end
    569 
    570 -- Check for unfinished or unused captures.
    571 local function checkcaptures()
    572   if cap_name then
    573     wprinterr(g_fname, ":", cap_lineno,
    574 	      ": error: unfinished .capture `", cap_name,"'\n")
    575     return
    576   end
    577   for name in pairs(cap_buffers) do
    578     if not cap_used[name] then
    579       wprinterr(g_fname, ":*: error: missing .dumpcapture ", name ,"\n")
    580     end
    581   end
    582 end
    583 
    584 ------------------------------------------------------------------------------
    585 
    586 -- Sections names.
    587 local map_sections = {}
    588 
    589 -- Pseudo-opcode to define code sections.
    590 -- TODO: Data sections, BSS sections. Needs extra C code and API.
    591 map_coreop[".section_*"] = function(params)
    592   if not params then return "name..." end
    593   if #map_sections > 0 then werror("duplicate section definition") end
    594   wflush()
    595   for sn,name in ipairs(params) do
    596     local opname = "."..name.."_0"
    597     if not match(name, "^[%a][%w_]*$") or
    598        map_op[opname] or map_op["."..name.."_*"] then
    599       werror("bad section name `"..name.."'")
    600     end
    601     map_sections[#map_sections+1] = name
    602     wline(format("#define DASM_SECTION_%s\t%d", upper(name), sn-1))
    603     map_op[opname] = function(params) g_arch.section(sn-1) end
    604   end
    605   wline(format("#define DASM_MAXSECTION\t\t%d", #map_sections))
    606 end
    607 
    608 -- Dump all sections.
    609 local function dumpsections(out, lvl)
    610   out:write("Sections:\n")
    611   for _,name in ipairs(map_sections) do
    612     out:write(format("  %s\n", name))
    613   end
    614   out:write("\n")
    615 end
    616 
    617 ------------------------------------------------------------------------------
    618 
    619 -- Replacement for customized Lua, which lacks the package library.
    620 local prefix = ""
    621 if not require then
    622   function require(name)
    623     local fp = assert(io.open(prefix..name..".lua"))
    624     local s = fp:read("*a")
    625     assert(fp:close())
    626     return assert(loadstring(s, "@"..name..".lua"))()
    627   end
    628 end
    629 
    630 -- Load architecture-specific module.
    631 local function loadarch(arch)
    632   if not match(arch, "^[%w_]+$") then return "bad arch name" end
    633   local ok, m_arch = pcall(require, "dasm_"..arch)
    634   if not ok then return "cannot load module: "..m_arch end
    635   g_arch = m_arch
    636   wflush = m_arch.passcb(wline, werror, wfatal, wwarn)
    637   m_arch.setup(arch, g_opt)
    638   map_op, map_def = m_arch.mergemaps(map_coreop, map_def)
    639 end
    640 
    641 -- Dump architecture description.
    642 function opt_map.dumparch(args)
    643   local name = optparam(args)
    644   if not g_arch then
    645     local err = loadarch(name)
    646     if err then opterror(err) end
    647   end
    648 
    649   local t = {}
    650   for name in pairs(map_coreop) do t[#t+1] = name end
    651   for name in pairs(map_op) do t[#t+1] = name end
    652   sort(t)
    653 
    654   local out = stdout
    655   local _arch = g_arch._info
    656   out:write(format("%s version %s, released %s, %s\n",
    657     _info.name, _info.version, _info.release, _info.url))
    658   g_arch.dumparch(out)
    659 
    660   local pseudo = true
    661   out:write("Pseudo-Opcodes:\n")
    662   for _,sname in ipairs(t) do
    663     local name, nparam = match(sname, "^(.+)_([0-9%*])$")
    664     if name then
    665       if pseudo and sub(name, 1, 1) ~= "." then
    666 	out:write("\nOpcodes:\n")
    667 	pseudo = false
    668       end
    669       local f = map_op[sname]
    670       local s
    671       if nparam ~= "*" then nparam = nparam + 0 end
    672       if nparam == 0 then
    673 	s = ""
    674       elseif type(f) == "string" then
    675 	s = map_op[".template__"](nil, f, nparam)
    676       else
    677 	s = f(nil, nparam)
    678       end
    679       if type(s) == "table" then
    680 	for _,s2 in ipairs(s) do
    681 	  out:write(format("  %-12s %s\n", name, s2))
    682 	end
    683       else
    684 	out:write(format("  %-12s %s\n", name, s))
    685       end
    686     end
    687   end
    688   out:write("\n")
    689   exit(0)
    690 end
    691 
    692 -- Pseudo-opcode to set the architecture.
    693 -- Only initially available (map_op is replaced when called).
    694 map_op[".arch_1"] = function(params)
    695   if not params then return "name" end
    696   local err = loadarch(params[1])
    697   if err then wfatal(err) end
    698   wline(format("#if DASM_VERSION != %d", _info.vernum))
    699   wline('#error "Version mismatch between DynASM and included encoding engine"')
    700   wline("#endif")
    701 end
    702 
    703 -- Dummy .arch pseudo-opcode to improve the error report.
    704 map_coreop[".arch_1"] = function(params)
    705   if not params then return "name" end
    706   wfatal("duplicate .arch statement")
    707 end
    708 
    709 ------------------------------------------------------------------------------
    710 
    711 -- Dummy pseudo-opcode. Don't confuse '.nop' with 'nop'.
    712 map_coreop[".nop_*"] = function(params)
    713   if not params then return "[ignored...]" end
    714 end
    715 
    716 -- Pseudo-opcodes to raise errors.
    717 map_coreop[".error_1"] = function(params)
    718   if not params then return "message" end
    719   werror(params[1])
    720 end
    721 
    722 map_coreop[".fatal_1"] = function(params)
    723   if not params then return "message" end
    724   wfatal(params[1])
    725 end
    726 
    727 -- Dump all user defined elements.
    728 local function dumpdef(out)
    729   local lvl = g_opt.dumpdef
    730   if lvl == 0 then return end
    731   dumpsections(out, lvl)
    732   dumpdefines(out, lvl)
    733   if g_arch then g_arch.dumpdef(out, lvl) end
    734   dumpmacros(out, lvl)
    735   dumpcaptures(out, lvl)
    736 end
    737 
    738 ------------------------------------------------------------------------------
    739 
    740 -- Helper for splitstmt.
    741 local splitlvl
    742 
    743 local function splitstmt_one(c)
    744   if c == "(" then
    745     splitlvl = ")"..splitlvl
    746   elseif c == "[" then
    747     splitlvl = "]"..splitlvl
    748   elseif c == "{" then
    749     splitlvl = "}"..splitlvl
    750   elseif c == ")" or c == "]" or c == "}" then
    751     if sub(splitlvl, 1, 1) ~= c then werror("unbalanced (), [] or {}") end
    752     splitlvl = sub(splitlvl, 2)
    753   elseif splitlvl == "" then
    754     return " \0 "
    755   end
    756   return c
    757 end
    758 
    759 -- Split statement into (pseudo-)opcode and params.
    760 local function splitstmt(stmt)
    761   -- Convert label with trailing-colon into .label statement.
    762   local label = match(stmt, "^%s*(.+):%s*$")
    763   if label then return ".label", {label} end
    764 
    765   -- Split at commas and equal signs, but obey parentheses and brackets.
    766   splitlvl = ""
    767   stmt = gsub(stmt, "[,%(%)%[%]{}]", splitstmt_one)
    768   if splitlvl ~= "" then werror("unbalanced () or []") end
    769 
    770   -- Split off opcode.
    771   local op, other = match(stmt, "^%s*([^%s%z]+)%s*(.*)$")
    772   if not op then werror("bad statement syntax") end
    773 
    774   -- Split parameters.
    775   local params = {}
    776   for p in gmatch(other, "%s*(%Z+)%z?") do
    777     params[#params+1] = gsub(p, "%s+$", "")
    778   end
    779   if #params > 16 then werror("too many parameters") end
    780 
    781   params.op = op
    782   return op, params
    783 end
    784 
    785 -- Process a single statement.
    786 dostmt = function(stmt)
    787   -- Ignore empty statements.
    788   if match(stmt, "^%s*$") then return end
    789 
    790   -- Capture macro defs before substitution.
    791   if mac_capture then return mac_capture(stmt) end
    792   stmt = definesubst(stmt)
    793 
    794   -- Emit C code without parsing the line.
    795   if sub(stmt, 1, 1) == "|" then
    796     local tail = sub(stmt, 2)
    797     wflush()
    798     if sub(tail, 1, 2) == "//" then wcomment(tail) else wline(tail, true) end
    799     return
    800   end
    801 
    802   -- Split into (pseudo-)opcode and params.
    803   local op, params = splitstmt(stmt)
    804 
    805   -- Get opcode handler (matching # of parameters or generic handler).
    806   local f = map_op[op.."_"..#params] or map_op[op.."_*"]
    807   if not f then
    808     if not g_arch then wfatal("first statement must be .arch") end
    809     -- Improve error report.
    810     for i=0,9 do
    811       if map_op[op.."_"..i] then
    812 	werror("wrong number of parameters for `"..op.."'")
    813       end
    814     end
    815     werror("unknown statement `"..op.."'")
    816   end
    817 
    818   -- Call opcode handler or special handler for template strings.
    819   if type(f) == "string" then
    820     map_op[".template__"](params, f)
    821   else
    822     f(params)
    823   end
    824 end
    825 
    826 -- Process a single line.
    827 local function doline(line)
    828   if g_opt.flushline then wflush() end
    829 
    830   -- Assembler line?
    831   local indent, aline = match(line, "^(%s*)%|(.*)$")
    832   if not aline then
    833     -- No, plain C code line, need to flush first.
    834     wflush()
    835     wsync()
    836     wline(line, false)
    837     return
    838   end
    839 
    840   g_indent = indent -- Remember current line indentation.
    841 
    842   -- Emit C code (even from macros). Avoids echo and line parsing.
    843   if sub(aline, 1, 1) == "|" then
    844     if not mac_capture then
    845       wsync()
    846     elseif g_opt.comment then
    847       wsync()
    848       wcomment(aline)
    849     end
    850     dostmt(aline)
    851     return
    852   end
    853 
    854   -- Echo assembler line as a comment.
    855   if g_opt.comment then
    856     wsync()
    857     wcomment(aline)
    858   end
    859 
    860   -- Strip assembler comments.
    861   aline = gsub(aline, "//.*$", "")
    862 
    863   -- Split line into statements at semicolons.
    864   if match(aline, ";") then
    865     for stmt in gmatch(aline, "[^;]+") do dostmt(stmt) end
    866   else
    867     dostmt(aline)
    868   end
    869 end
    870 
    871 ------------------------------------------------------------------------------
    872 
    873 -- Write DynASM header.
    874 local function dasmhead(out)
    875   out:write(format([[
    876 /*
    877 ** This file has been pre-processed with DynASM.
    878 ** %s
    879 ** DynASM version %s, DynASM %s version %s
    880 ** DO NOT EDIT! The original file is in "%s".
    881 */
    882 
    883 ]], _info.url,
    884     _info.version, g_arch._info.arch, g_arch._info.version,
    885     g_fname))
    886 end
    887 
    888 -- Read input file.
    889 readfile = function(fin)
    890   g_indent = ""
    891   g_lineno = 0
    892   g_synclineno = -1
    893 
    894   -- Process all lines.
    895   for line in fin:lines() do
    896     g_lineno = g_lineno + 1
    897     g_curline = line
    898     local ok, err = pcall(doline, line)
    899     if not ok and wprinterr(err, "\n") then return true end
    900   end
    901   wflush()
    902 
    903   -- Close input file.
    904   assert(fin == stdin or fin:close())
    905 end
    906 
    907 -- Write output file.
    908 local function writefile(outfile)
    909   local fout
    910 
    911   -- Open output file.
    912   if outfile == nil or outfile == "-" then
    913     fout = stdout
    914   else
    915     fout = assert(io.open(outfile, "w"))
    916   end
    917 
    918   -- Write all buffered lines
    919   wdumplines(fout, g_wbuffer)
    920 
    921   -- Close output file.
    922   assert(fout == stdout or fout:close())
    923 
    924   -- Optionally dump definitions.
    925   dumpdef(fout == stdout and stderr or stdout)
    926 end
    927 
    928 -- Translate an input file to an output file.
    929 local function translate(infile, outfile)
    930   g_wbuffer = {}
    931   g_indent = ""
    932   g_lineno = 0
    933   g_synclineno = -1
    934 
    935   -- Put header.
    936   wline(dasmhead)
    937 
    938   -- Read input file.
    939   local fin
    940   if infile == "-" then
    941     g_fname = "(stdin)"
    942     fin = stdin
    943   else
    944     g_fname = infile
    945     fin = assert(io.open(infile, "r"))
    946   end
    947   readfile(fin)
    948 
    949   -- Check for errors.
    950   if not g_arch then
    951     wprinterr(g_fname, ":*: error: missing .arch directive\n")
    952   end
    953   checkconds()
    954   checkmacros()
    955   checkcaptures()
    956 
    957   if g_errcount ~= 0 then
    958     stderr:write(g_fname, ":*: info: ", g_errcount, " error",
    959       (type(g_errcount) == "number" and g_errcount > 1) and "s" or "",
    960       " in input file -- no output file generated.\n")
    961     dumpdef(stderr)
    962     exit(1)
    963   end
    964 
    965   -- Write output file.
    966   writefile(outfile)
    967 end
    968 
    969 ------------------------------------------------------------------------------
    970 
    971 -- Print help text.
    972 function opt_map.help()
    973   stdout:write("DynASM -- ", _info.description, ".\n")
    974   stdout:write("DynASM ", _info.version, " ", _info.release, "  ", _info.url, "\n")
    975   stdout:write[[
    976 
    977 Usage: dynasm [OPTION]... INFILE.dasc|-
    978 
    979   -h, --help           Display this help text.
    980   -V, --version        Display version and copyright information.
    981 
    982   -o, --outfile FILE   Output file name (default is stdout).
    983   -I, --include DIR    Add directory to the include search path.
    984 
    985   -c, --ccomment       Use /* */ comments for assembler lines.
    986   -C, --cppcomment     Use // comments for assembler lines (default).
    987   -N, --nocomment      Suppress assembler lines in output.
    988   -M, --maccomment     Show macro expansions as comments (default off).
    989 
    990   -L, --nolineno       Suppress CPP line number information in output.
    991   -F, --flushline      Flush action list for every line.
    992 
    993   -D NAME[=SUBST]      Define a substitution.
    994   -U NAME              Undefine a substitution.
    995 
    996   -P, --dumpdef        Dump defines, macros, etc. Repeat for more output.
    997   -A, --dumparch ARCH  Load architecture ARCH and dump description.
    998 ]]
    999   exit(0)
   1000 end
   1001 
   1002 -- Print version information.
   1003 function opt_map.version()
   1004   stdout:write(format("%s version %s, released %s\n%s\n\n%s",
   1005     _info.name, _info.version, _info.release, _info.url, _info.copyright))
   1006   exit(0)
   1007 end
   1008 
   1009 -- Misc. options.
   1010 function opt_map.outfile(args) g_opt.outfile = optparam(args) end
   1011 function opt_map.include(args) insert(g_opt.include, 1, optparam(args)) end
   1012 function opt_map.ccomment() g_opt.comment = "/*|"; g_opt.endcomment = " */" end
   1013 function opt_map.cppcomment() g_opt.comment = "//|"; g_opt.endcomment = "" end
   1014 function opt_map.nocomment() g_opt.comment = false end
   1015 function opt_map.maccomment() g_opt.maccomment = true end
   1016 function opt_map.nolineno() g_opt.cpp = false end
   1017 function opt_map.flushline() g_opt.flushline = true end
   1018 function opt_map.dumpdef() g_opt.dumpdef = g_opt.dumpdef + 1 end
   1019 
   1020 ------------------------------------------------------------------------------
   1021 
   1022 -- Short aliases for long options.
   1023 local opt_alias = {
   1024   h = "help", ["?"] = "help", V = "version",
   1025   o = "outfile", I = "include",
   1026   c = "ccomment", C = "cppcomment", N = "nocomment", M = "maccomment",
   1027   L = "nolineno", F = "flushline",
   1028   P = "dumpdef", A = "dumparch",
   1029 }
   1030 
   1031 -- Parse single option.
   1032 local function parseopt(opt, args)
   1033   opt_current = #opt == 1 and "-"..opt or "--"..opt
   1034   local f = opt_map[opt] or opt_map[opt_alias[opt]]
   1035   if not f then
   1036     opterror("unrecognized option `", opt_current, "'. Try `--help'.\n")
   1037   end
   1038   f(args)
   1039 end
   1040 
   1041 -- Parse arguments.
   1042 local function parseargs(args)
   1043   -- Default options.
   1044   g_opt.comment = "//|"
   1045   g_opt.endcomment = ""
   1046   g_opt.cpp = true
   1047   g_opt.dumpdef = 0
   1048   g_opt.include = { "" }
   1049 
   1050   -- Process all option arguments.
   1051   args.argn = 1
   1052   repeat
   1053     local a = args[args.argn]
   1054     if not a then break end
   1055     local lopt, opt = match(a, "^%-(%-?)(.+)")
   1056     if not opt then break end
   1057     args.argn = args.argn + 1
   1058     if lopt == "" then
   1059       -- Loop through short options.
   1060       for o in gmatch(opt, ".") do parseopt(o, args) end
   1061     else
   1062       -- Long option.
   1063       parseopt(opt, args)
   1064     end
   1065   until false
   1066 
   1067   -- Check for proper number of arguments.
   1068   local nargs = #args - args.argn + 1
   1069   if nargs ~= 1 then
   1070     if nargs == 0 then
   1071       if g_opt.dumpdef > 0 then return dumpdef(stdout) end
   1072     end
   1073     opt_map.help()
   1074   end
   1075 
   1076   -- Translate a single input file to a single output file
   1077   -- TODO: Handle multiple files?
   1078   translate(args[args.argn], g_opt.outfile)
   1079 end
   1080 
   1081 ------------------------------------------------------------------------------
   1082 
   1083 -- Add the directory dynasm.lua resides in to the Lua module search path.
   1084 local arg = arg
   1085 if arg and arg[0] then
   1086   prefix = match(arg[0], "^(.*[/\\])")
   1087   if package and prefix then package.path = prefix.."?.lua;"..package.path end
   1088 end
   1089 
   1090 -- Start DynASM.
   1091 parseargs{...}
   1092 
   1093 ------------------------------------------------------------------------------
   1094