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. 10local _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 = [[ 20Copyright (C) 2005-2023 Mike Pall. All rights reserved. 21 22Permission is hereby granted, free of charge, to any person obtaining 23a copy of this software and associated documentation files (the 24"Software"), to deal in the Software without restriction, including 25without limitation the rights to use, copy, modify, merge, publish, 26distribute, sublicense, and/or sell copies of the Software, and to 27permit persons to whom the Software is furnished to do so, subject to 28the following conditions: 29 30The above copyright notice and this permission notice shall be 31included in all copies or substantial portions of the Software. 32 33THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 34EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 35MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 36IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 37CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 38TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 39SOFTWARE 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. 46local type, pairs, ipairs = type, pairs, ipairs 47local pcall, error, assert = pcall, error, assert 48local _s = string 49local sub, match, gmatch, gsub = _s.sub, _s.match, _s.gmatch, _s.gsub 50local format, rep, upper = _s.format, _s.rep, _s.upper 51local _t = table 52local insert, remove, concat, sort = _t.insert, _t.remove, _t.concat, _t.sort 53local exit = os.exit 54local io = io 55local stdin, stdout, stderr = io.stdin, io.stdout, io.stderr 56 57------------------------------------------------------------------------------ 58 59-- Program options. 60local g_opt = {} 61 62-- Global state for current file. 63local g_fname, g_curline, g_indent, g_lineno, g_synclineno, g_arch 64local g_errcount = 0 65 66-- Write buffer for output file. 67local g_wbuffer, g_capbuffer 68 69------------------------------------------------------------------------------ 70 71-- Write an output line (or callback function) to the buffer. 72local 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 76end 77 78-- Write assembler line as a comment, if requested. 79local function wcomment(aline) 80 if g_opt.comment then 81 wline(g_opt.comment..aline..g_opt.endcomment, true) 82 end 83end 84 85-- Resync CPP line numbers. 86local 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 91end 92 93-- Dummy action flush function. Replaced with arch-specific function later. 94local function wflush(term) 95end 96 97-- Dump all buffered output lines. 98local 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 107end 108 109------------------------------------------------------------------------------ 110 111-- Emit an error. Processing continues with next statement. 112local function werror(msg) 113 error(format("%s:%s: error: %s:\n%s", g_fname, g_lineno, msg, g_curline), 0) 114end 115 116-- Emit a fatal error. Processing stops. 117local function wfatal(msg) 118 g_errcount = "fatal" 119 werror(msg) 120end 121 122-- Print a warning. Processing continues. 123local function wwarn(msg) 124 stderr:write(format("%s:%s: warning: %s:\n%s\n", 125 g_fname, g_lineno, msg, g_curline)) 126end 127 128-- Print caught error message. But suppress excessive errors. 129local 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 144end 145 146------------------------------------------------------------------------------ 147 148-- Map holding all option handlers. 149local opt_map = {} 150local opt_current 151 152-- Print error and exit with error status. 153local function opterror(...) 154 stderr:write("dynasm.lua: ERROR: ", ...) 155 stderr:write("\n") 156 exit(1) 157end 158 159-- Get option parameter. 160local 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 168end 169 170------------------------------------------------------------------------------ 171 172-- Core pseudo-opcodes. 173local map_coreop = {} 174-- Dummy opcode map. Replaced by arch-specific map. 175local map_op = {} 176 177-- Forward declarations. 178local dostmt 179local readfile 180 181------------------------------------------------------------------------------ 182 183-- Map for defines (initially empty, chains to arch-specific map). 184local map_def = {} 185 186-- Pseudo-opcode to define a substitution. 187map_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 192end 193map_coreop[".define_1"] = map_coreop[".define_2"] 194 195-- Define a substitution on the command line. 196function 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 206end 207 208-- Undefine a substitution on the command line. 209function 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 216end 217 218-- Helper for definesubst. 219local gotsubst 220 221local function definesubst_one(word) 222 local subst = map_def[word] 223 if subst then gotsubst = word; return subst else return word end 224end 225 226-- Iteratively substitute defines. 227local 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 236end 237 238-- Dump all defines. 239local 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") 252end 253 254------------------------------------------------------------------------------ 255 256-- Support variables for conditional assembly. 257local condlevel = 0 258local condstack = {} 259 260-- Evaluate condition with a Lua expression. Substitutions already performed. 261local 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) 281end 282 283-- Skip statements until next conditional pseudo-opcode at the same level. 284local 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 298end 299 300-- Pseudo-opcodes for conditional assembly. 301map_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 308end 309 310map_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() 325end 326 327map_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 336end 337 338map_coreop[".endif_0"] = function(params) 339 local lvl = condlevel 340 if lvl == 0 then wfatal(".endif without .if") end 341 condlevel = lvl - 1 342end 343 344-- Check for unfinished conditionals. 345local function checkconds() 346 if g_errcount ~= "fatal" and condlevel ~= 0 then 347 wprinterr(g_fname, ":*: error: unbalanced conditional\n") 348 end 349end 350 351------------------------------------------------------------------------------ 352 353-- Search for a file in the given path and open it for reading. 354local 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 364end 365 366-- Include a file. 367map_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 379end 380 381-- Make .include and conditionals initially available, too. 382map_op[".include_1"] = map_coreop[".include_1"] 383map_op[".if_1"] = map_coreop[".if_1"] 384map_op[".elif_1"] = map_coreop[".elif_1"] 385map_op[".else_0"] = map_coreop[".else_0"] 386map_op[".endif_0"] = map_coreop[".endif_0"] 387 388------------------------------------------------------------------------------ 389 390-- Support variables for macros. 391local mac_capture, mac_lineno, mac_name 392local mac_active = {} 393local mac_list = {} 394 395-- Pseudo-opcode to define a macro. 396map_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 467end 468 469-- An .endmacro pseudo-opcode outside of a macro definition is an error. 470map_coreop[".endmacro_0"] = function(params) 471 wfatal(".endmacro without .macro") 472end 473 474-- Dump all macros and their contents (with -PP only). 475local 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") 490end 491 492-- Check for unfinished macro definitions. 493local function checkmacros() 494 if mac_capture then 495 wprinterr(g_fname, ":", mac_lineno, 496 ": error: unfinished .macro `", mac_name ,"'\n") 497 end 498end 499 500------------------------------------------------------------------------------ 501 502-- Support variables for captures. 503local cap_lineno, cap_name 504local cap_buffers = {} 505local cap_used = {} 506 507-- Start a capture. 508map_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 525end 526 527-- Stop a capture. 528map_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 535end 536 537-- Dump a capture buffer. 538map_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 551end 552 553-- Dump all captures and their buffers (with -PP only). 554local 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") 568end 569 570-- Check for unfinished or unused captures. 571local 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 582end 583 584------------------------------------------------------------------------------ 585 586-- Sections names. 587local map_sections = {} 588 589-- Pseudo-opcode to define code sections. 590-- TODO: Data sections, BSS sections. Needs extra C code and API. 591map_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)) 606end 607 608-- Dump all sections. 609local 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") 615end 616 617------------------------------------------------------------------------------ 618 619-- Replacement for customized Lua, which lacks the package library. 620local prefix = "" 621if 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 628end 629 630-- Load architecture-specific module. 631local 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) 640end 641 642-- Dump architecture description. 643function 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) 691end 692 693-- Pseudo-opcode to set the architecture. 694-- Only initially available (map_op is replaced when called). 695map_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") 702end 703 704-- Dummy .arch pseudo-opcode to improve the error report. 705map_coreop[".arch_1"] = function(params) 706 if not params then return "name" end 707 wfatal("duplicate .arch statement") 708end 709 710------------------------------------------------------------------------------ 711 712-- Dummy pseudo-opcode. Don't confuse '.nop' with 'nop'. 713map_coreop[".nop_*"] = function(params) 714 if not params then return "[ignored...]" end 715end 716 717-- Pseudo-opcodes to raise errors. 718map_coreop[".error_1"] = function(params) 719 if not params then return "message" end 720 werror(params[1]) 721end 722 723map_coreop[".fatal_1"] = function(params) 724 if not params then return "message" end 725 wfatal(params[1]) 726end 727 728-- Dump all user defined elements. 729local 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) 737end 738 739------------------------------------------------------------------------------ 740 741-- Helper for splitstmt. 742local splitlvl 743 744local 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 758end 759 760-- Split statement into (pseudo-)opcode and params. 761local 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 784end 785 786-- Process a single statement. 787dostmt = 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 825end 826 827-- Process a single line. 828local 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 870end 871 872------------------------------------------------------------------------------ 873 874-- Write DynASM header. 875local 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)) 887end 888 889-- Read input file. 890readfile = 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()) 906end 907 908-- Write output file. 909local 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) 927end 928 929-- Translate an input file to an output file. 930local 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) 968end 969 970------------------------------------------------------------------------------ 971 972-- Print help text. 973function 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 978Usage: 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) 1001end 1002 1003-- Print version information. 1004function 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) 1008end 1009 1010-- Misc. options. 1011function opt_map.outfile(args) g_opt.outfile = optparam(args) end 1012function opt_map.include(args) insert(g_opt.include, 1, optparam(args)) end 1013function opt_map.ccomment() g_opt.comment = "/*|"; g_opt.endcomment = " */" end 1014function opt_map.cppcomment() g_opt.comment = "//|"; g_opt.endcomment = "" end 1015function opt_map.nocomment() g_opt.comment = false end 1016function opt_map.maccomment() g_opt.maccomment = true end 1017function opt_map.nolineno() g_opt.cpp = false end 1018function opt_map.flushline() g_opt.flushline = true end 1019function opt_map.dumpdef() g_opt.dumpdef = g_opt.dumpdef + 1 end 1020 1021------------------------------------------------------------------------------ 1022 1023-- Short aliases for long options. 1024local 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. 1033local 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) 1040end 1041 1042-- Parse arguments. 1043local 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) 1080end 1081 1082------------------------------------------------------------------------------ 1083 1084-- Add the directory dynasm.lua resides in to the Lua module search path. 1085local arg = arg 1086if arg and arg[0] then 1087 prefix = match(arg[0], "^(.*[/\\])") 1088 if package and prefix then package.path = prefix.."?.lua;"..package.path end 1089end 1090 1091-- Start DynASM. 1092parseargs{...} 1093 1094------------------------------------------------------------------------------ 1095 1096