Mercurial
comparison third_party/luajit/dynasm/dynasm.lua @ 186:8cf4ec5e2191 hg-web
Fixed merge conflict.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 23 Jan 2026 22:38:59 -0800 |
| parents | 94705b5986b3 |
| children |
comparison
equal
deleted
inserted
replaced
| 176:fed99fc04e12 | 186:8cf4ec5e2191 |
|---|---|
| 1 ------------------------------------------------------------------------------ | |
| 2 -- DynASM. A dynamic assembler for code generation engines. | |
| 3 -- Originally designed and implemented for LuaJIT. | |
| 4 -- | |
| 5 -- Copyright (C) 2005-2023 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.5.0", | |
| 14 vernum = 10500, | |
| 15 release = "2021-05-02", | |
| 16 author = "Mike Pall", | |
| 17 url = "https://luajit.org/dynasm.html", | |
| 18 license = "MIT", | |
| 19 copyright = [[ | |
| 20 Copyright (C) 2005-2023 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: https://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 _G._map_def = map_def | |
| 634 local ok, m_arch = pcall(require, "dasm_"..arch) | |
| 635 if not ok then return "cannot load module: "..m_arch end | |
| 636 g_arch = m_arch | |
| 637 wflush = m_arch.passcb(wline, werror, wfatal, wwarn) | |
| 638 m_arch.setup(arch, g_opt) | |
| 639 map_op, map_def = m_arch.mergemaps(map_coreop, map_def) | |
| 640 end | |
| 641 | |
| 642 -- Dump architecture description. | |
| 643 function opt_map.dumparch(args) | |
| 644 local name = optparam(args) | |
| 645 if not g_arch then | |
| 646 local err = loadarch(name) | |
| 647 if err then opterror(err) end | |
| 648 end | |
| 649 | |
| 650 local t = {} | |
| 651 for name in pairs(map_coreop) do t[#t+1] = name end | |
| 652 for name in pairs(map_op) do t[#t+1] = name end | |
| 653 sort(t) | |
| 654 | |
| 655 local out = stdout | |
| 656 local _arch = g_arch._info | |
| 657 out:write(format("%s version %s, released %s, %s\n", | |
| 658 _info.name, _info.version, _info.release, _info.url)) | |
| 659 g_arch.dumparch(out) | |
| 660 | |
| 661 local pseudo = true | |
| 662 out:write("Pseudo-Opcodes:\n") | |
| 663 for _,sname in ipairs(t) do | |
| 664 local name, nparam = match(sname, "^(.+)_([0-9%*])$") | |
| 665 if name then | |
| 666 if pseudo and sub(name, 1, 1) ~= "." then | |
| 667 out:write("\nOpcodes:\n") | |
| 668 pseudo = false | |
| 669 end | |
| 670 local f = map_op[sname] | |
| 671 local s | |
| 672 if nparam ~= "*" then nparam = nparam + 0 end | |
| 673 if nparam == 0 then | |
| 674 s = "" | |
| 675 elseif type(f) == "string" then | |
| 676 s = map_op[".template__"](nil, f, nparam) | |
| 677 else | |
| 678 s = f(nil, nparam) | |
| 679 end | |
| 680 if type(s) == "table" then | |
| 681 for _,s2 in ipairs(s) do | |
| 682 out:write(format(" %-12s %s\n", name, s2)) | |
| 683 end | |
| 684 else | |
| 685 out:write(format(" %-12s %s\n", name, s)) | |
| 686 end | |
| 687 end | |
| 688 end | |
| 689 out:write("\n") | |
| 690 exit(0) | |
| 691 end | |
| 692 | |
| 693 -- Pseudo-opcode to set the architecture. | |
| 694 -- Only initially available (map_op is replaced when called). | |
| 695 map_op[".arch_1"] = function(params) | |
| 696 if not params then return "name" end | |
| 697 local err = loadarch(params[1]) | |
| 698 if err then wfatal(err) end | |
| 699 wline(format("#if DASM_VERSION != %d", _info.vernum)) | |
| 700 wline('#error "Version mismatch between DynASM and included encoding engine"') | |
| 701 wline("#endif") | |
| 702 end | |
| 703 | |
| 704 -- Dummy .arch pseudo-opcode to improve the error report. | |
| 705 map_coreop[".arch_1"] = function(params) | |
| 706 if not params then return "name" end | |
| 707 wfatal("duplicate .arch statement") | |
| 708 end | |
| 709 | |
| 710 ------------------------------------------------------------------------------ | |
| 711 | |
| 712 -- Dummy pseudo-opcode. Don't confuse '.nop' with 'nop'. | |
| 713 map_coreop[".nop_*"] = function(params) | |
| 714 if not params then return "[ignored...]" end | |
| 715 end | |
| 716 | |
| 717 -- Pseudo-opcodes to raise errors. | |
| 718 map_coreop[".error_1"] = function(params) | |
| 719 if not params then return "message" end | |
| 720 werror(params[1]) | |
| 721 end | |
| 722 | |
| 723 map_coreop[".fatal_1"] = function(params) | |
| 724 if not params then return "message" end | |
| 725 wfatal(params[1]) | |
| 726 end | |
| 727 | |
| 728 -- Dump all user defined elements. | |
| 729 local function dumpdef(out) | |
| 730 local lvl = g_opt.dumpdef | |
| 731 if lvl == 0 then return end | |
| 732 dumpsections(out, lvl) | |
| 733 dumpdefines(out, lvl) | |
| 734 if g_arch then g_arch.dumpdef(out, lvl) end | |
| 735 dumpmacros(out, lvl) | |
| 736 dumpcaptures(out, lvl) | |
| 737 end | |
| 738 | |
| 739 ------------------------------------------------------------------------------ | |
| 740 | |
| 741 -- Helper for splitstmt. | |
| 742 local splitlvl | |
| 743 | |
| 744 local function splitstmt_one(c) | |
| 745 if c == "(" then | |
| 746 splitlvl = ")"..splitlvl | |
| 747 elseif c == "[" then | |
| 748 splitlvl = "]"..splitlvl | |
| 749 elseif c == "{" then | |
| 750 splitlvl = "}"..splitlvl | |
| 751 elseif c == ")" or c == "]" or c == "}" then | |
| 752 if sub(splitlvl, 1, 1) ~= c then werror("unbalanced (), [] or {}") end | |
| 753 splitlvl = sub(splitlvl, 2) | |
| 754 elseif splitlvl == "" then | |
| 755 return " \0 " | |
| 756 end | |
| 757 return c | |
| 758 end | |
| 759 | |
| 760 -- Split statement into (pseudo-)opcode and params. | |
| 761 local function splitstmt(stmt) | |
| 762 -- Convert label with trailing-colon into .label statement. | |
| 763 local label = match(stmt, "^%s*(.+):%s*$") | |
| 764 if label then return ".label", {label} end | |
| 765 | |
| 766 -- Split at commas and equal signs, but obey parentheses and brackets. | |
| 767 splitlvl = "" | |
| 768 stmt = gsub(stmt, "[,%(%)%[%]{}]", splitstmt_one) | |
| 769 if splitlvl ~= "" then werror("unbalanced () or []") end | |
| 770 | |
| 771 -- Split off opcode. | |
| 772 local op, other = match(stmt, "^%s*([^%s%z]+)%s*(.*)$") | |
| 773 if not op then werror("bad statement syntax") end | |
| 774 | |
| 775 -- Split parameters. | |
| 776 local params = {} | |
| 777 for p in gmatch(other, "%s*(%Z+)%z?") do | |
| 778 params[#params+1] = gsub(p, "%s+$", "") | |
| 779 end | |
| 780 if #params > 16 then werror("too many parameters") end | |
| 781 | |
| 782 params.op = op | |
| 783 return op, params | |
| 784 end | |
| 785 | |
| 786 -- Process a single statement. | |
| 787 dostmt = function(stmt) | |
| 788 -- Ignore empty statements. | |
| 789 if match(stmt, "^%s*$") then return end | |
| 790 | |
| 791 -- Capture macro defs before substitution. | |
| 792 if mac_capture then return mac_capture(stmt) end | |
| 793 stmt = definesubst(stmt) | |
| 794 | |
| 795 -- Emit C code without parsing the line. | |
| 796 if sub(stmt, 1, 1) == "|" then | |
| 797 local tail = sub(stmt, 2) | |
| 798 wflush() | |
| 799 if sub(tail, 1, 2) == "//" then wcomment(tail) else wline(tail, true) end | |
| 800 return | |
| 801 end | |
| 802 | |
| 803 -- Split into (pseudo-)opcode and params. | |
| 804 local op, params = splitstmt(stmt) | |
| 805 | |
| 806 -- Get opcode handler (matching # of parameters or generic handler). | |
| 807 local f = map_op[op.."_"..#params] or map_op[op.."_*"] | |
| 808 if not f then | |
| 809 if not g_arch then wfatal("first statement must be .arch") end | |
| 810 -- Improve error report. | |
| 811 for i=0,9 do | |
| 812 if map_op[op.."_"..i] then | |
| 813 werror("wrong number of parameters for `"..op.."'") | |
| 814 end | |
| 815 end | |
| 816 werror("unknown statement `"..op.."'") | |
| 817 end | |
| 818 | |
| 819 -- Call opcode handler or special handler for template strings. | |
| 820 if type(f) == "string" then | |
| 821 map_op[".template__"](params, f) | |
| 822 else | |
| 823 f(params) | |
| 824 end | |
| 825 end | |
| 826 | |
| 827 -- Process a single line. | |
| 828 local function doline(line) | |
| 829 if g_opt.flushline then wflush() end | |
| 830 | |
| 831 -- Assembler line? | |
| 832 local indent, aline = match(line, "^(%s*)%|(.*)$") | |
| 833 if not aline then | |
| 834 -- No, plain C code line, need to flush first. | |
| 835 wflush() | |
| 836 wsync() | |
| 837 wline(line, false) | |
| 838 return | |
| 839 end | |
| 840 | |
| 841 g_indent = indent -- Remember current line indentation. | |
| 842 | |
| 843 -- Emit C code (even from macros). Avoids echo and line parsing. | |
| 844 if sub(aline, 1, 1) == "|" then | |
| 845 if not mac_capture then | |
| 846 wsync() | |
| 847 elseif g_opt.comment then | |
| 848 wsync() | |
| 849 wcomment(aline) | |
| 850 end | |
| 851 dostmt(aline) | |
| 852 return | |
| 853 end | |
| 854 | |
| 855 -- Echo assembler line as a comment. | |
| 856 if g_opt.comment then | |
| 857 wsync() | |
| 858 wcomment(aline) | |
| 859 end | |
| 860 | |
| 861 -- Strip assembler comments. | |
| 862 aline = gsub(aline, "//.*$", "") | |
| 863 | |
| 864 -- Split line into statements at semicolons. | |
| 865 if match(aline, ";") then | |
| 866 for stmt in gmatch(aline, "[^;]+") do dostmt(stmt) end | |
| 867 else | |
| 868 dostmt(aline) | |
| 869 end | |
| 870 end | |
| 871 | |
| 872 ------------------------------------------------------------------------------ | |
| 873 | |
| 874 -- Write DynASM header. | |
| 875 local function dasmhead(out) | |
| 876 out:write(format([[ | |
| 877 /* | |
| 878 ** This file has been pre-processed with DynASM. | |
| 879 ** %s | |
| 880 ** DynASM version %s, DynASM %s version %s | |
| 881 ** DO NOT EDIT! The original file is in "%s". | |
| 882 */ | |
| 883 | |
| 884 ]], _info.url, | |
| 885 _info.version, g_arch._info.arch, g_arch._info.version, | |
| 886 g_fname)) | |
| 887 end | |
| 888 | |
| 889 -- Read input file. | |
| 890 readfile = function(fin) | |
| 891 g_indent = "" | |
| 892 g_lineno = 0 | |
| 893 g_synclineno = -1 | |
| 894 | |
| 895 -- Process all lines. | |
| 896 for line in fin:lines() do | |
| 897 g_lineno = g_lineno + 1 | |
| 898 g_curline = line | |
| 899 local ok, err = pcall(doline, line) | |
| 900 if not ok and wprinterr(err, "\n") then return true end | |
| 901 end | |
| 902 wflush() | |
| 903 | |
| 904 -- Close input file. | |
| 905 assert(fin == stdin or fin:close()) | |
| 906 end | |
| 907 | |
| 908 -- Write output file. | |
| 909 local function writefile(outfile) | |
| 910 local fout | |
| 911 | |
| 912 -- Open output file. | |
| 913 if outfile == nil or outfile == "-" then | |
| 914 fout = stdout | |
| 915 else | |
| 916 fout = assert(io.open(outfile, "w")) | |
| 917 end | |
| 918 | |
| 919 -- Write all buffered lines | |
| 920 wdumplines(fout, g_wbuffer) | |
| 921 | |
| 922 -- Close output file. | |
| 923 assert(fout == stdout or fout:close()) | |
| 924 | |
| 925 -- Optionally dump definitions. | |
| 926 dumpdef(fout == stdout and stderr or stdout) | |
| 927 end | |
| 928 | |
| 929 -- Translate an input file to an output file. | |
| 930 local function translate(infile, outfile) | |
| 931 g_wbuffer = {} | |
| 932 g_indent = "" | |
| 933 g_lineno = 0 | |
| 934 g_synclineno = -1 | |
| 935 | |
| 936 -- Put header. | |
| 937 wline(dasmhead) | |
| 938 | |
| 939 -- Read input file. | |
| 940 local fin | |
| 941 if infile == "-" then | |
| 942 g_fname = "(stdin)" | |
| 943 fin = stdin | |
| 944 else | |
| 945 g_fname = infile | |
| 946 fin = assert(io.open(infile, "r")) | |
| 947 end | |
| 948 readfile(fin) | |
| 949 | |
| 950 -- Check for errors. | |
| 951 if not g_arch then | |
| 952 wprinterr(g_fname, ":*: error: missing .arch directive\n") | |
| 953 end | |
| 954 checkconds() | |
| 955 checkmacros() | |
| 956 checkcaptures() | |
| 957 | |
| 958 if g_errcount ~= 0 then | |
| 959 stderr:write(g_fname, ":*: info: ", g_errcount, " error", | |
| 960 (type(g_errcount) == "number" and g_errcount > 1) and "s" or "", | |
| 961 " in input file -- no output file generated.\n") | |
| 962 dumpdef(stderr) | |
| 963 exit(1) | |
| 964 end | |
| 965 | |
| 966 -- Write output file. | |
| 967 writefile(outfile) | |
| 968 end | |
| 969 | |
| 970 ------------------------------------------------------------------------------ | |
| 971 | |
| 972 -- Print help text. | |
| 973 function opt_map.help() | |
| 974 stdout:write("DynASM -- ", _info.description, ".\n") | |
| 975 stdout:write("DynASM ", _info.version, " ", _info.release, " ", _info.url, "\n") | |
| 976 stdout:write[[ | |
| 977 | |
| 978 Usage: dynasm [OPTION]... INFILE.dasc|- | |
| 979 | |
| 980 -h, --help Display this help text. | |
| 981 -V, --version Display version and copyright information. | |
| 982 | |
| 983 -o, --outfile FILE Output file name (default is stdout). | |
| 984 -I, --include DIR Add directory to the include search path. | |
| 985 | |
| 986 -c, --ccomment Use /* */ comments for assembler lines. | |
| 987 -C, --cppcomment Use // comments for assembler lines (default). | |
| 988 -N, --nocomment Suppress assembler lines in output. | |
| 989 -M, --maccomment Show macro expansions as comments (default off). | |
| 990 | |
| 991 -L, --nolineno Suppress CPP line number information in output. | |
| 992 -F, --flushline Flush action list for every line. | |
| 993 | |
| 994 -D NAME[=SUBST] Define a substitution. | |
| 995 -U NAME Undefine a substitution. | |
| 996 | |
| 997 -P, --dumpdef Dump defines, macros, etc. Repeat for more output. | |
| 998 -A, --dumparch ARCH Load architecture ARCH and dump description. | |
| 999 ]] | |
| 1000 exit(0) | |
| 1001 end | |
| 1002 | |
| 1003 -- Print version information. | |
| 1004 function opt_map.version() | |
| 1005 stdout:write(format("%s version %s, released %s\n%s\n\n%s", | |
| 1006 _info.name, _info.version, _info.release, _info.url, _info.copyright)) | |
| 1007 exit(0) | |
| 1008 end | |
| 1009 | |
| 1010 -- Misc. options. | |
| 1011 function opt_map.outfile(args) g_opt.outfile = optparam(args) end | |
| 1012 function opt_map.include(args) insert(g_opt.include, 1, optparam(args)) end | |
| 1013 function opt_map.ccomment() g_opt.comment = "/*|"; g_opt.endcomment = " */" end | |
| 1014 function opt_map.cppcomment() g_opt.comment = "//|"; g_opt.endcomment = "" end | |
| 1015 function opt_map.nocomment() g_opt.comment = false end | |
| 1016 function opt_map.maccomment() g_opt.maccomment = true end | |
| 1017 function opt_map.nolineno() g_opt.cpp = false end | |
| 1018 function opt_map.flushline() g_opt.flushline = true end | |
| 1019 function opt_map.dumpdef() g_opt.dumpdef = g_opt.dumpdef + 1 end | |
| 1020 | |
| 1021 ------------------------------------------------------------------------------ | |
| 1022 | |
| 1023 -- Short aliases for long options. | |
| 1024 local opt_alias = { | |
| 1025 h = "help", ["?"] = "help", V = "version", | |
| 1026 o = "outfile", I = "include", | |
| 1027 c = "ccomment", C = "cppcomment", N = "nocomment", M = "maccomment", | |
| 1028 L = "nolineno", F = "flushline", | |
| 1029 P = "dumpdef", A = "dumparch", | |
| 1030 } | |
| 1031 | |
| 1032 -- Parse single option. | |
| 1033 local function parseopt(opt, args) | |
| 1034 opt_current = #opt == 1 and "-"..opt or "--"..opt | |
| 1035 local f = opt_map[opt] or opt_map[opt_alias[opt]] | |
| 1036 if not f then | |
| 1037 opterror("unrecognized option `", opt_current, "'. Try `--help'.\n") | |
| 1038 end | |
| 1039 f(args) | |
| 1040 end | |
| 1041 | |
| 1042 -- Parse arguments. | |
| 1043 local function parseargs(args) | |
| 1044 -- Default options. | |
| 1045 g_opt.comment = "//|" | |
| 1046 g_opt.endcomment = "" | |
| 1047 g_opt.cpp = true | |
| 1048 g_opt.dumpdef = 0 | |
| 1049 g_opt.include = { "" } | |
| 1050 | |
| 1051 -- Process all option arguments. | |
| 1052 args.argn = 1 | |
| 1053 repeat | |
| 1054 local a = args[args.argn] | |
| 1055 if not a then break end | |
| 1056 local lopt, opt = match(a, "^%-(%-?)(.+)") | |
| 1057 if not opt then break end | |
| 1058 args.argn = args.argn + 1 | |
| 1059 if lopt == "" then | |
| 1060 -- Loop through short options. | |
| 1061 for o in gmatch(opt, ".") do parseopt(o, args) end | |
| 1062 else | |
| 1063 -- Long option. | |
| 1064 parseopt(opt, args) | |
| 1065 end | |
| 1066 until false | |
| 1067 | |
| 1068 -- Check for proper number of arguments. | |
| 1069 local nargs = #args - args.argn + 1 | |
| 1070 if nargs ~= 1 then | |
| 1071 if nargs == 0 then | |
| 1072 if g_opt.dumpdef > 0 then return dumpdef(stdout) end | |
| 1073 end | |
| 1074 opt_map.help() | |
| 1075 end | |
| 1076 | |
| 1077 -- Translate a single input file to a single output file | |
| 1078 -- TODO: Handle multiple files? | |
| 1079 translate(args[args.argn], g_opt.outfile) | |
| 1080 end | |
| 1081 | |
| 1082 ------------------------------------------------------------------------------ | |
| 1083 | |
| 1084 -- Add the directory dynasm.lua resides in to the Lua module search path. | |
| 1085 local arg = arg | |
| 1086 if arg and arg[0] then | |
| 1087 prefix = match(arg[0], "^(.*[/\\])") | |
| 1088 if package and prefix then package.path = prefix.."?.lua;"..package.path end | |
| 1089 end | |
| 1090 | |
| 1091 -- Start DynASM. | |
| 1092 parseargs{...} | |
| 1093 | |
| 1094 ------------------------------------------------------------------------------ | |
| 1095 |