diff options
author | Mike Pall <mike> | 2009-12-08 19:46:35 +0100 |
---|---|---|
committer | Mike Pall <mike> | 2009-12-08 19:46:35 +0100 |
commit | 55b16959717084884fd4a0cbae6d19e3786c20c7 (patch) | |
tree | c8a07a43c13679751ed25a9d06796e9e7b2134a6 /dynasm/dynasm.lua | |
download | luajit-2.0.0-beta1.tar.gz luajit-2.0.0-beta1.tar.bz2 luajit-2.0.0-beta1.zip |
RELEASE LuaJIT-2.0.0-beta1v2.0.0-beta1
Diffstat (limited to 'dynasm/dynasm.lua')
-rw-r--r-- | dynasm/dynasm.lua | 1070 |
1 files changed, 1070 insertions, 0 deletions
diff --git a/dynasm/dynasm.lua b/dynasm/dynasm.lua new file mode 100644 index 00000000..20ff9cf5 --- /dev/null +++ b/dynasm/dynasm.lua | |||
@@ -0,0 +1,1070 @@ | |||
1 | ------------------------------------------------------------------------------ | ||
2 | -- DynASM. A dynamic assembler for code generation engines. | ||
3 | -- Originally designed and implemented for LuaJIT. | ||
4 | -- | ||
5 | -- Copyright (C) 2005-2009 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.2.1", | ||
14 | vernum = 10201, | ||
15 | release = "2009-04-16", | ||
16 | author = "Mike Pall", | ||
17 | url = "http://luajit.org/dynasm.html", | ||
18 | license = "MIT", | ||
19 | copyright = [[ | ||
20 | Copyright (C) 2005-2009 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("# "..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 = loadstring("return "..cond) | ||
263 | if func then | ||
264 | setfenv(func, {}) -- No globals. All unknown identifiers evaluate to nil. | ||
265 | local ok, res = pcall(func) | ||
266 | if ok then | ||
267 | if res == 0 then return false end -- Oh well. | ||
268 | return not not res | ||
269 | end | ||
270 | err = res | ||
271 | end | ||
272 | wfatal("bad condition: "..err) | ||
273 | end | ||
274 | |||
275 | -- Skip statements until next conditional pseudo-opcode at the same level. | ||
276 | local function stmtskip() | ||
277 | local dostmt_save = dostmt | ||
278 | local lvl = 0 | ||
279 | dostmt = function(stmt) | ||
280 | local op = match(stmt, "^%s*(%S+)") | ||
281 | if op == ".if" then | ||
282 | lvl = lvl + 1 | ||
283 | elseif lvl ~= 0 then | ||
284 | if op == ".endif" then lvl = lvl - 1 end | ||
285 | elseif op == ".elif" or op == ".else" or op == ".endif" then | ||
286 | dostmt = dostmt_save | ||
287 | dostmt(stmt) | ||
288 | end | ||
289 | end | ||
290 | end | ||
291 | |||
292 | -- Pseudo-opcodes for conditional assembly. | ||
293 | map_coreop[".if_1"] = function(params) | ||
294 | if not params then return "condition" end | ||
295 | local lvl = condlevel + 1 | ||
296 | local res = cond_eval(params[1]) | ||
297 | condlevel = lvl | ||
298 | condstack[lvl] = res | ||
299 | if not res then stmtskip() end | ||
300 | end | ||
301 | |||
302 | map_coreop[".elif_1"] = function(params) | ||
303 | if not params then return "condition" end | ||
304 | if condlevel == 0 then wfatal(".elif without .if") end | ||
305 | local lvl = condlevel | ||
306 | local res = condstack[lvl] | ||
307 | if res then | ||
308 | if res == "else" then wfatal(".elif after .else") end | ||
309 | else | ||
310 | res = cond_eval(params[1]) | ||
311 | if res then | ||
312 | condstack[lvl] = res | ||
313 | return | ||
314 | end | ||
315 | end | ||
316 | stmtskip() | ||
317 | end | ||
318 | |||
319 | map_coreop[".else_0"] = function(params) | ||
320 | if condlevel == 0 then wfatal(".else without .if") end | ||
321 | local lvl = condlevel | ||
322 | local res = condstack[lvl] | ||
323 | condstack[lvl] = "else" | ||
324 | if res then | ||
325 | if res == "else" then wfatal(".else after .else") end | ||
326 | stmtskip() | ||
327 | end | ||
328 | end | ||
329 | |||
330 | map_coreop[".endif_0"] = function(params) | ||
331 | local lvl = condlevel | ||
332 | if lvl == 0 then wfatal(".endif without .if") end | ||
333 | condlevel = lvl - 1 | ||
334 | end | ||
335 | |||
336 | -- Check for unfinished conditionals. | ||
337 | local function checkconds() | ||
338 | if g_errcount ~= "fatal" and condlevel ~= 0 then | ||
339 | wprinterr(g_fname, ":*: error: unbalanced conditional\n") | ||
340 | end | ||
341 | end | ||
342 | |||
343 | ------------------------------------------------------------------------------ | ||
344 | |||
345 | -- Search for a file in the given path and open it for reading. | ||
346 | local function pathopen(path, name) | ||
347 | local dirsep = match(package.path, "\\") and "\\" or "/" | ||
348 | for _,p in ipairs(path) do | ||
349 | local fullname = p == "" and name or p..dirsep..name | ||
350 | local fin = io.open(fullname, "r") | ||
351 | if fin then | ||
352 | g_fname = fullname | ||
353 | return fin | ||
354 | end | ||
355 | end | ||
356 | end | ||
357 | |||
358 | -- Include a file. | ||
359 | map_coreop[".include_1"] = function(params) | ||
360 | if not params then return "filename" end | ||
361 | local name = params[1] | ||
362 | -- Save state. Ugly, I know. but upvalues are fast. | ||
363 | local gf, gl, gcl, gi = g_fname, g_lineno, g_curline, g_indent | ||
364 | -- Read the included file. | ||
365 | local fatal = readfile(pathopen(g_opt.include, name) or | ||
366 | wfatal("include file `"..name.."' not found")) | ||
367 | -- Restore state. | ||
368 | g_synclineno = -1 | ||
369 | g_fname, g_lineno, g_curline, g_indent = gf, gl, gcl, gi | ||
370 | if fatal then wfatal("in include file") end | ||
371 | end | ||
372 | |||
373 | -- Make .include initially available, too. | ||
374 | map_op[".include_1"] = map_coreop[".include_1"] | ||
375 | |||
376 | ------------------------------------------------------------------------------ | ||
377 | |||
378 | -- Support variables for macros. | ||
379 | local mac_capture, mac_lineno, mac_name | ||
380 | local mac_active = {} | ||
381 | local mac_list = {} | ||
382 | |||
383 | -- Pseudo-opcode to define a macro. | ||
384 | map_coreop[".macro_*"] = function(mparams) | ||
385 | if not mparams then return "name [, params...]" end | ||
386 | -- Split off and validate macro name. | ||
387 | local name = remove(mparams, 1) | ||
388 | if not name then werror("missing macro name") end | ||
389 | if not (match(name, "^[%a_][%w_%.]*$") or match(name, "^%.[%w_%.]+$")) then | ||
390 | wfatal("bad macro name `"..name.."'") | ||
391 | end | ||
392 | -- Validate macro parameter names. | ||
393 | local mdup = {} | ||
394 | for _,mp in ipairs(mparams) do | ||
395 | if not match(mp, "^[%a_][%w_]*$") then | ||
396 | wfatal("bad macro parameter name `"..mp.."'") | ||
397 | end | ||
398 | if mdup[mp] then wfatal("duplicate macro parameter name `"..mp.."'") end | ||
399 | mdup[mp] = true | ||
400 | end | ||
401 | -- Check for duplicate or recursive macro definitions. | ||
402 | local opname = name.."_"..#mparams | ||
403 | if map_op[opname] or map_op[name.."_*"] then | ||
404 | wfatal("duplicate macro `"..name.."' ("..#mparams.." parameters)") | ||
405 | end | ||
406 | if mac_capture then wfatal("recursive macro definition") end | ||
407 | |||
408 | -- Enable statement capture. | ||
409 | local lines = {} | ||
410 | mac_lineno = g_lineno | ||
411 | mac_name = name | ||
412 | mac_capture = function(stmt) -- Statement capture function. | ||
413 | -- Stop macro definition with .endmacro pseudo-opcode. | ||
414 | if not match(stmt, "^%s*.endmacro%s*$") then | ||
415 | lines[#lines+1] = stmt | ||
416 | return | ||
417 | end | ||
418 | mac_capture = nil | ||
419 | mac_lineno = nil | ||
420 | mac_name = nil | ||
421 | mac_list[#mac_list+1] = opname | ||
422 | -- Add macro-op definition. | ||
423 | map_op[opname] = function(params) | ||
424 | if not params then return mparams, lines end | ||
425 | -- Protect against recursive macro invocation. | ||
426 | if mac_active[opname] then wfatal("recursive macro invocation") end | ||
427 | mac_active[opname] = true | ||
428 | -- Setup substitution map. | ||
429 | local subst = {} | ||
430 | for i,mp in ipairs(mparams) do subst[mp] = params[i] end | ||
431 | local mcom | ||
432 | if g_opt.maccomment and g_opt.comment then | ||
433 | mcom = " MACRO "..name.." ("..#mparams..")" | ||
434 | wcomment("{"..mcom) | ||
435 | end | ||
436 | -- Loop through all captured statements | ||
437 | for _,stmt in ipairs(lines) do | ||
438 | -- Substitute macro parameters. | ||
439 | local st = gsub(stmt, "[%w_]+", subst) | ||
440 | st = definesubst(st) | ||
441 | st = gsub(st, "%s*%.%.%s*", "") -- Token paste a..b. | ||
442 | if mcom and sub(st, 1, 1) ~= "|" then wcomment(st) end | ||
443 | -- Emit statement. Use a protected call for better diagnostics. | ||
444 | local ok, err = pcall(dostmt, st) | ||
445 | if not ok then | ||
446 | -- Add the captured statement to the error. | ||
447 | wprinterr(err, "\n", g_indent, "| ", stmt, | ||
448 | "\t[MACRO ", name, " (", #mparams, ")]\n") | ||
449 | end | ||
450 | end | ||
451 | if mcom then wcomment("}"..mcom) end | ||
452 | mac_active[opname] = nil | ||
453 | end | ||
454 | end | ||
455 | end | ||
456 | |||
457 | -- An .endmacro pseudo-opcode outside of a macro definition is an error. | ||
458 | map_coreop[".endmacro_0"] = function(params) | ||
459 | wfatal(".endmacro without .macro") | ||
460 | end | ||
461 | |||
462 | -- Dump all macros and their contents (with -PP only). | ||
463 | local function dumpmacros(out, lvl) | ||
464 | sort(mac_list) | ||
465 | out:write("Macros:\n") | ||
466 | for _,opname in ipairs(mac_list) do | ||
467 | local name = sub(opname, 1, -3) | ||
468 | local params, lines = map_op[opname]() | ||
469 | out:write(format(" %-20s %s\n", name, concat(params, ", "))) | ||
470 | if lvl > 1 then | ||
471 | for _,line in ipairs(lines) do | ||
472 | out:write(" |", line, "\n") | ||
473 | end | ||
474 | out:write("\n") | ||
475 | end | ||
476 | end | ||
477 | out:write("\n") | ||
478 | end | ||
479 | |||
480 | -- Check for unfinished macro definitions. | ||
481 | local function checkmacros() | ||
482 | if mac_capture then | ||
483 | wprinterr(g_fname, ":", mac_lineno, | ||
484 | ": error: unfinished .macro `", mac_name ,"'\n") | ||
485 | end | ||
486 | end | ||
487 | |||
488 | ------------------------------------------------------------------------------ | ||
489 | |||
490 | -- Support variables for captures. | ||
491 | local cap_lineno, cap_name | ||
492 | local cap_buffers = {} | ||
493 | local cap_used = {} | ||
494 | |||
495 | -- Start a capture. | ||
496 | map_coreop[".capture_1"] = function(params) | ||
497 | if not params then return "name" end | ||
498 | wflush() | ||
499 | local name = params[1] | ||
500 | if not match(name, "^[%a_][%w_]*$") then | ||
501 | wfatal("bad capture name `"..name.."'") | ||
502 | end | ||
503 | if cap_name then | ||
504 | wfatal("already capturing to `"..cap_name.."' since line "..cap_lineno) | ||
505 | end | ||
506 | cap_name = name | ||
507 | cap_lineno = g_lineno | ||
508 | -- Create or continue a capture buffer and start the output line capture. | ||
509 | local buf = cap_buffers[name] | ||
510 | if not buf then buf = {}; cap_buffers[name] = buf end | ||
511 | g_capbuffer = buf | ||
512 | g_synclineno = 0 | ||
513 | end | ||
514 | |||
515 | -- Stop a capture. | ||
516 | map_coreop[".endcapture_0"] = function(params) | ||
517 | wflush() | ||
518 | if not cap_name then wfatal(".endcapture without a valid .capture") end | ||
519 | cap_name = nil | ||
520 | cap_lineno = nil | ||
521 | g_capbuffer = nil | ||
522 | g_synclineno = 0 | ||
523 | end | ||
524 | |||
525 | -- Dump a capture buffer. | ||
526 | map_coreop[".dumpcapture_1"] = function(params) | ||
527 | if not params then return "name" end | ||
528 | wflush() | ||
529 | local name = params[1] | ||
530 | if not match(name, "^[%a_][%w_]*$") then | ||
531 | wfatal("bad capture name `"..name.."'") | ||
532 | end | ||
533 | cap_used[name] = true | ||
534 | wline(function(out) | ||
535 | local buf = cap_buffers[name] | ||
536 | if buf then wdumplines(out, buf) end | ||
537 | end) | ||
538 | g_synclineno = 0 | ||
539 | end | ||
540 | |||
541 | -- Dump all captures and their buffers (with -PP only). | ||
542 | local function dumpcaptures(out, lvl) | ||
543 | out:write("Captures:\n") | ||
544 | for name,buf in pairs(cap_buffers) do | ||
545 | out:write(format(" %-20s %4s)\n", name, "("..#buf)) | ||
546 | if lvl > 1 then | ||
547 | local bar = rep("=", 76) | ||
548 | out:write(" ", bar, "\n") | ||
549 | for _,line in ipairs(buf) do | ||
550 | out:write(" ", line, "\n") | ||
551 | end | ||
552 | out:write(" ", bar, "\n\n") | ||
553 | end | ||
554 | end | ||
555 | out:write("\n") | ||
556 | end | ||
557 | |||
558 | -- Check for unfinished or unused captures. | ||
559 | local function checkcaptures() | ||
560 | if cap_name then | ||
561 | wprinterr(g_fname, ":", cap_lineno, | ||
562 | ": error: unfinished .capture `", cap_name,"'\n") | ||
563 | return | ||
564 | end | ||
565 | for name in pairs(cap_buffers) do | ||
566 | if not cap_used[name] then | ||
567 | wprinterr(g_fname, ":*: error: missing .dumpcapture ", name ,"\n") | ||
568 | end | ||
569 | end | ||
570 | end | ||
571 | |||
572 | ------------------------------------------------------------------------------ | ||
573 | |||
574 | -- Sections names. | ||
575 | local map_sections = {} | ||
576 | |||
577 | -- Pseudo-opcode to define code sections. | ||
578 | -- TODO: Data sections, BSS sections. Needs extra C code and API. | ||
579 | map_coreop[".section_*"] = function(params) | ||
580 | if not params then return "name..." end | ||
581 | if #map_sections > 0 then werror("duplicate section definition") end | ||
582 | wflush() | ||
583 | for sn,name in ipairs(params) do | ||
584 | local opname = "."..name.."_0" | ||
585 | if not match(name, "^[%a][%w_]*$") or | ||
586 | map_op[opname] or map_op["."..name.."_*"] then | ||
587 | werror("bad section name `"..name.."'") | ||
588 | end | ||
589 | map_sections[#map_sections+1] = name | ||
590 | wline(format("#define DASM_SECTION_%s\t%d", upper(name), sn-1)) | ||
591 | map_op[opname] = function(params) g_arch.section(sn-1) end | ||
592 | end | ||
593 | wline(format("#define DASM_MAXSECTION\t\t%d", #map_sections)) | ||
594 | end | ||
595 | |||
596 | -- Dump all sections. | ||
597 | local function dumpsections(out, lvl) | ||
598 | out:write("Sections:\n") | ||
599 | for _,name in ipairs(map_sections) do | ||
600 | out:write(format(" %s\n", name)) | ||
601 | end | ||
602 | out:write("\n") | ||
603 | end | ||
604 | |||
605 | ------------------------------------------------------------------------------ | ||
606 | |||
607 | -- Load architecture-specific module. | ||
608 | local function loadarch(arch) | ||
609 | if not match(arch, "^[%w_]+$") then return "bad arch name" end | ||
610 | local ok, m_arch = pcall(require, "dasm_"..arch) | ||
611 | if not ok then return "cannot load module: "..m_arch end | ||
612 | g_arch = m_arch | ||
613 | wflush = m_arch.passcb(wline, werror, wfatal, wwarn) | ||
614 | m_arch.setup(arch, g_opt) | ||
615 | map_op, map_def = m_arch.mergemaps(map_coreop, map_def) | ||
616 | end | ||
617 | |||
618 | -- Dump architecture description. | ||
619 | function opt_map.dumparch(args) | ||
620 | local name = optparam(args) | ||
621 | if not g_arch then | ||
622 | local err = loadarch(name) | ||
623 | if err then opterror(err) end | ||
624 | end | ||
625 | |||
626 | local t = {} | ||
627 | for name in pairs(map_coreop) do t[#t+1] = name end | ||
628 | for name in pairs(map_op) do t[#t+1] = name end | ||
629 | sort(t) | ||
630 | |||
631 | local out = stdout | ||
632 | local _arch = g_arch._info | ||
633 | out:write(format("%s version %s, released %s, %s\n", | ||
634 | _info.name, _info.version, _info.release, _info.url)) | ||
635 | g_arch.dumparch(out) | ||
636 | |||
637 | local pseudo = true | ||
638 | out:write("Pseudo-Opcodes:\n") | ||
639 | for _,sname in ipairs(t) do | ||
640 | local name, nparam = match(sname, "^(.+)_([0-9%*])$") | ||
641 | if name then | ||
642 | if pseudo and sub(name, 1, 1) ~= "." then | ||
643 | out:write("\nOpcodes:\n") | ||
644 | pseudo = false | ||
645 | end | ||
646 | local f = map_op[sname] | ||
647 | local s | ||
648 | if nparam ~= "*" then nparam = nparam + 0 end | ||
649 | if nparam == 0 then | ||
650 | s = "" | ||
651 | elseif type(f) == "string" then | ||
652 | s = map_op[".template__"](nil, f, nparam) | ||
653 | else | ||
654 | s = f(nil, nparam) | ||
655 | end | ||
656 | if type(s) == "table" then | ||
657 | for _,s2 in ipairs(s) do | ||
658 | out:write(format(" %-12s %s\n", name, s2)) | ||
659 | end | ||
660 | else | ||
661 | out:write(format(" %-12s %s\n", name, s)) | ||
662 | end | ||
663 | end | ||
664 | end | ||
665 | out:write("\n") | ||
666 | exit(0) | ||
667 | end | ||
668 | |||
669 | -- Pseudo-opcode to set the architecture. | ||
670 | -- Only initially available (map_op is replaced when called). | ||
671 | map_op[".arch_1"] = function(params) | ||
672 | if not params then return "name" end | ||
673 | local err = loadarch(params[1]) | ||
674 | if err then wfatal(err) end | ||
675 | end | ||
676 | |||
677 | -- Dummy .arch pseudo-opcode to improve the error report. | ||
678 | map_coreop[".arch_1"] = function(params) | ||
679 | if not params then return "name" end | ||
680 | wfatal("duplicate .arch statement") | ||
681 | end | ||
682 | |||
683 | ------------------------------------------------------------------------------ | ||
684 | |||
685 | -- Dummy pseudo-opcode. Don't confuse '.nop' with 'nop'. | ||
686 | map_coreop[".nop_*"] = function(params) | ||
687 | if not params then return "[ignored...]" end | ||
688 | end | ||
689 | |||
690 | -- Pseudo-opcodes to raise errors. | ||
691 | map_coreop[".error_1"] = function(params) | ||
692 | if not params then return "message" end | ||
693 | werror(params[1]) | ||
694 | end | ||
695 | |||
696 | map_coreop[".fatal_1"] = function(params) | ||
697 | if not params then return "message" end | ||
698 | wfatal(params[1]) | ||
699 | end | ||
700 | |||
701 | -- Dump all user defined elements. | ||
702 | local function dumpdef(out) | ||
703 | local lvl = g_opt.dumpdef | ||
704 | if lvl == 0 then return end | ||
705 | dumpsections(out, lvl) | ||
706 | dumpdefines(out, lvl) | ||
707 | if g_arch then g_arch.dumpdef(out, lvl) end | ||
708 | dumpmacros(out, lvl) | ||
709 | dumpcaptures(out, lvl) | ||
710 | end | ||
711 | |||
712 | ------------------------------------------------------------------------------ | ||
713 | |||
714 | -- Helper for splitstmt. | ||
715 | local splitlvl | ||
716 | |||
717 | local function splitstmt_one(c) | ||
718 | if c == "(" then | ||
719 | splitlvl = ")"..splitlvl | ||
720 | elseif c == "[" then | ||
721 | splitlvl = "]"..splitlvl | ||
722 | elseif c == ")" or c == "]" then | ||
723 | if sub(splitlvl, 1, 1) ~= c then werror("unbalanced () or []") end | ||
724 | splitlvl = sub(splitlvl, 2) | ||
725 | elseif splitlvl == "" then | ||
726 | return " \0 " | ||
727 | end | ||
728 | return c | ||
729 | end | ||
730 | |||
731 | -- Split statement into (pseudo-)opcode and params. | ||
732 | local function splitstmt(stmt) | ||
733 | -- Convert label with trailing-colon into .label statement. | ||
734 | local label = match(stmt, "^%s*(.+):%s*$") | ||
735 | if label then return ".label", {label} end | ||
736 | |||
737 | -- Split at commas and equal signs, but obey parentheses and brackets. | ||
738 | splitlvl = "" | ||
739 | stmt = gsub(stmt, "[,%(%)%[%]]", splitstmt_one) | ||
740 | if splitlvl ~= "" then werror("unbalanced () or []") end | ||
741 | |||
742 | -- Split off opcode. | ||
743 | local op, other = match(stmt, "^%s*([^%s%z]+)%s*(.*)$") | ||
744 | if not op then werror("bad statement syntax") end | ||
745 | |||
746 | -- Split parameters. | ||
747 | local params = {} | ||
748 | for p in gmatch(other, "%s*(%Z+)%z?") do | ||
749 | params[#params+1] = gsub(p, "%s+$", "") | ||
750 | end | ||
751 | if #params > 16 then werror("too many parameters") end | ||
752 | |||
753 | params.op = op | ||
754 | return op, params | ||
755 | end | ||
756 | |||
757 | -- Process a single statement. | ||
758 | dostmt = function(stmt) | ||
759 | -- Ignore empty statements. | ||
760 | if match(stmt, "^%s*$") then return end | ||
761 | |||
762 | -- Capture macro defs before substitution. | ||
763 | if mac_capture then return mac_capture(stmt) end | ||
764 | stmt = definesubst(stmt) | ||
765 | |||
766 | -- Emit C code without parsing the line. | ||
767 | if sub(stmt, 1, 1) == "|" then | ||
768 | local tail = sub(stmt, 2) | ||
769 | wflush() | ||
770 | if sub(tail, 1, 2) == "//" then wcomment(tail) else wline(tail, true) end | ||
771 | return | ||
772 | end | ||
773 | |||
774 | -- Split into (pseudo-)opcode and params. | ||
775 | local op, params = splitstmt(stmt) | ||
776 | |||
777 | -- Get opcode handler (matching # of parameters or generic handler). | ||
778 | local f = map_op[op.."_"..#params] or map_op[op.."_*"] | ||
779 | if not f then | ||
780 | if not g_arch then wfatal("first statement must be .arch") end | ||
781 | -- Improve error report. | ||
782 | for i=0,16 do | ||
783 | if map_op[op.."_"..i] then | ||
784 | werror("wrong number of parameters for `"..op.."'") | ||
785 | end | ||
786 | end | ||
787 | werror("unknown statement `"..op.."'") | ||
788 | end | ||
789 | |||
790 | -- Call opcode handler or special handler for template strings. | ||
791 | if type(f) == "string" then | ||
792 | map_op[".template__"](params, f) | ||
793 | else | ||
794 | f(params) | ||
795 | end | ||
796 | end | ||
797 | |||
798 | -- Process a single line. | ||
799 | local function doline(line) | ||
800 | if g_opt.flushline then wflush() end | ||
801 | |||
802 | -- Assembler line? | ||
803 | local indent, aline = match(line, "^(%s*)%|(.*)$") | ||
804 | if not aline then | ||
805 | -- No, plain C code line, need to flush first. | ||
806 | wflush() | ||
807 | wsync() | ||
808 | wline(line, false) | ||
809 | return | ||
810 | end | ||
811 | |||
812 | g_indent = indent -- Remember current line indentation. | ||
813 | |||
814 | -- Emit C code (even from macros). Avoids echo and line parsing. | ||
815 | if sub(aline, 1, 1) == "|" then | ||
816 | if not mac_capture then | ||
817 | wsync() | ||
818 | elseif g_opt.comment then | ||
819 | wsync() | ||
820 | wcomment(aline) | ||
821 | end | ||
822 | dostmt(aline) | ||
823 | return | ||
824 | end | ||
825 | |||
826 | -- Echo assembler line as a comment. | ||
827 | if g_opt.comment then | ||
828 | wsync() | ||
829 | wcomment(aline) | ||
830 | end | ||
831 | |||
832 | -- Strip assembler comments. | ||
833 | aline = gsub(aline, "//.*$", "") | ||
834 | |||
835 | -- Split line into statements at semicolons. | ||
836 | if match(aline, ";") then | ||
837 | for stmt in gmatch(aline, "[^;]+") do dostmt(stmt) end | ||
838 | else | ||
839 | dostmt(aline) | ||
840 | end | ||
841 | end | ||
842 | |||
843 | ------------------------------------------------------------------------------ | ||
844 | |||
845 | -- Write DynASM header. | ||
846 | local function dasmhead(out) | ||
847 | out:write(format([[ | ||
848 | /* | ||
849 | ** This file has been pre-processed with DynASM. | ||
850 | ** %s | ||
851 | ** DynASM version %s, DynASM %s version %s | ||
852 | ** DO NOT EDIT! The original file is in "%s". | ||
853 | */ | ||
854 | |||
855 | #if DASM_VERSION != %d | ||
856 | #error "Version mismatch between DynASM and included encoding engine" | ||
857 | #endif | ||
858 | |||
859 | ]], _info.url, | ||
860 | _info.version, g_arch._info.arch, g_arch._info.version, | ||
861 | g_fname, _info.vernum)) | ||
862 | end | ||
863 | |||
864 | -- Read input file. | ||
865 | readfile = function(fin) | ||
866 | g_indent = "" | ||
867 | g_lineno = 0 | ||
868 | g_synclineno = -1 | ||
869 | |||
870 | -- Process all lines. | ||
871 | for line in fin:lines() do | ||
872 | g_lineno = g_lineno + 1 | ||
873 | g_curline = line | ||
874 | local ok, err = pcall(doline, line) | ||
875 | if not ok and wprinterr(err, "\n") then return true end | ||
876 | end | ||
877 | wflush() | ||
878 | |||
879 | -- Close input file. | ||
880 | assert(fin == stdin or fin:close()) | ||
881 | end | ||
882 | |||
883 | -- Write output file. | ||
884 | local function writefile(outfile) | ||
885 | local fout | ||
886 | |||
887 | -- Open output file. | ||
888 | if outfile == nil or outfile == "-" then | ||
889 | fout = stdout | ||
890 | else | ||
891 | fout = assert(io.open(outfile, "w")) | ||
892 | end | ||
893 | |||
894 | -- Write all buffered lines | ||
895 | wdumplines(fout, g_wbuffer) | ||
896 | |||
897 | -- Close output file. | ||
898 | assert(fout == stdout or fout:close()) | ||
899 | |||
900 | -- Optionally dump definitions. | ||
901 | dumpdef(fout == stdout and stderr or stdout) | ||
902 | end | ||
903 | |||
904 | -- Translate an input file to an output file. | ||
905 | local function translate(infile, outfile) | ||
906 | g_wbuffer = {} | ||
907 | g_indent = "" | ||
908 | g_lineno = 0 | ||
909 | g_synclineno = -1 | ||
910 | |||
911 | -- Put header. | ||
912 | wline(dasmhead) | ||
913 | |||
914 | -- Read input file. | ||
915 | local fin | ||
916 | if infile == "-" then | ||
917 | g_fname = "(stdin)" | ||
918 | fin = stdin | ||
919 | else | ||
920 | g_fname = infile | ||
921 | fin = assert(io.open(infile, "r")) | ||
922 | end | ||
923 | readfile(fin) | ||
924 | |||
925 | -- Check for errors. | ||
926 | if not g_arch then | ||
927 | wprinterr(g_fname, ":*: error: missing .arch directive\n") | ||
928 | end | ||
929 | checkconds() | ||
930 | checkmacros() | ||
931 | checkcaptures() | ||
932 | |||
933 | if g_errcount ~= 0 then | ||
934 | stderr:write(g_fname, ":*: info: ", g_errcount, " error", | ||
935 | (type(g_errcount) == "number" and g_errcount > 1) and "s" or "", | ||
936 | " in input file -- no output file generated.\n") | ||
937 | dumpdef(stderr) | ||
938 | exit(1) | ||
939 | end | ||
940 | |||
941 | -- Write output file. | ||
942 | writefile(outfile) | ||
943 | end | ||
944 | |||
945 | ------------------------------------------------------------------------------ | ||
946 | |||
947 | -- Print help text. | ||
948 | function opt_map.help() | ||
949 | stdout:write("DynASM -- ", _info.description, ".\n") | ||
950 | stdout:write("DynASM ", _info.version, " ", _info.release, " ", _info.url, "\n") | ||
951 | stdout:write[[ | ||
952 | |||
953 | Usage: dynasm [OPTION]... INFILE.dasc|- | ||
954 | |||
955 | -h, --help Display this help text. | ||
956 | -V, --version Display version and copyright information. | ||
957 | |||
958 | -o, --outfile FILE Output file name (default is stdout). | ||
959 | -I, --include DIR Add directory to the include search path. | ||
960 | |||
961 | -c, --ccomment Use /* */ comments for assembler lines. | ||
962 | -C, --cppcomment Use // comments for assembler lines (default). | ||
963 | -N, --nocomment Suppress assembler lines in output. | ||
964 | -M, --maccomment Show macro expansions as comments (default off). | ||
965 | |||
966 | -L, --nolineno Suppress CPP line number information in output. | ||
967 | -F, --flushline Flush action list for every line. | ||
968 | |||
969 | -D NAME[=SUBST] Define a substitution. | ||
970 | -U NAME Undefine a substitution. | ||
971 | |||
972 | -P, --dumpdef Dump defines, macros, etc. Repeat for more output. | ||
973 | -A, --dumparch ARCH Load architecture ARCH and dump description. | ||
974 | ]] | ||
975 | exit(0) | ||
976 | end | ||
977 | |||
978 | -- Print version information. | ||
979 | function opt_map.version() | ||
980 | stdout:write(format("%s version %s, released %s\n%s\n\n%s", | ||
981 | _info.name, _info.version, _info.release, _info.url, _info.copyright)) | ||
982 | exit(0) | ||
983 | end | ||
984 | |||
985 | -- Misc. options. | ||
986 | function opt_map.outfile(args) g_opt.outfile = optparam(args) end | ||
987 | function opt_map.include(args) insert(g_opt.include, 1, optparam(args)) end | ||
988 | function opt_map.ccomment() g_opt.comment = "/*|"; g_opt.endcomment = " */" end | ||
989 | function opt_map.cppcomment() g_opt.comment = "//|"; g_opt.endcomment = "" end | ||
990 | function opt_map.nocomment() g_opt.comment = false end | ||
991 | function opt_map.maccomment() g_opt.maccomment = true end | ||
992 | function opt_map.nolineno() g_opt.cpp = false end | ||
993 | function opt_map.flushline() g_opt.flushline = true end | ||
994 | function opt_map.dumpdef() g_opt.dumpdef = g_opt.dumpdef + 1 end | ||
995 | |||
996 | ------------------------------------------------------------------------------ | ||
997 | |||
998 | -- Short aliases for long options. | ||
999 | local opt_alias = { | ||
1000 | h = "help", ["?"] = "help", V = "version", | ||
1001 | o = "outfile", I = "include", | ||
1002 | c = "ccomment", C = "cppcomment", N = "nocomment", M = "maccomment", | ||
1003 | L = "nolineno", F = "flushline", | ||
1004 | P = "dumpdef", A = "dumparch", | ||
1005 | } | ||
1006 | |||
1007 | -- Parse single option. | ||
1008 | local function parseopt(opt, args) | ||
1009 | opt_current = #opt == 1 and "-"..opt or "--"..opt | ||
1010 | local f = opt_map[opt] or opt_map[opt_alias[opt]] | ||
1011 | if not f then | ||
1012 | opterror("unrecognized option `", opt_current, "'. Try `--help'.\n") | ||
1013 | end | ||
1014 | f(args) | ||
1015 | end | ||
1016 | |||
1017 | -- Parse arguments. | ||
1018 | local function parseargs(args) | ||
1019 | -- Default options. | ||
1020 | g_opt.comment = "//|" | ||
1021 | g_opt.endcomment = "" | ||
1022 | g_opt.cpp = true | ||
1023 | g_opt.dumpdef = 0 | ||
1024 | g_opt.include = { "" } | ||
1025 | |||
1026 | -- Process all option arguments. | ||
1027 | args.argn = 1 | ||
1028 | repeat | ||
1029 | local a = args[args.argn] | ||
1030 | if not a then break end | ||
1031 | local lopt, opt = match(a, "^%-(%-?)(.+)") | ||
1032 | if not opt then break end | ||
1033 | args.argn = args.argn + 1 | ||
1034 | if lopt == "" then | ||
1035 | -- Loop through short options. | ||
1036 | for o in gmatch(opt, ".") do parseopt(o, args) end | ||
1037 | else | ||
1038 | -- Long option. | ||
1039 | parseopt(opt, args) | ||
1040 | end | ||
1041 | until false | ||
1042 | |||
1043 | -- Check for proper number of arguments. | ||
1044 | local nargs = #args - args.argn + 1 | ||
1045 | if nargs ~= 1 then | ||
1046 | if nargs == 0 then | ||
1047 | if g_opt.dumpdef > 0 then return dumpdef(stdout) end | ||
1048 | end | ||
1049 | opt_map.help() | ||
1050 | end | ||
1051 | |||
1052 | -- Translate a single input file to a single output file | ||
1053 | -- TODO: Handle multiple files? | ||
1054 | translate(args[args.argn], g_opt.outfile) | ||
1055 | end | ||
1056 | |||
1057 | ------------------------------------------------------------------------------ | ||
1058 | |||
1059 | -- Add the directory dynasm.lua resides in to the Lua module search path. | ||
1060 | local arg = arg | ||
1061 | if arg and arg[0] then | ||
1062 | local prefix = match(arg[0], "^(.*[/\\])") | ||
1063 | if prefix then package.path = prefix.."?.lua;"..package.path end | ||
1064 | end | ||
1065 | |||
1066 | -- Start DynASM. | ||
1067 | parseargs{...} | ||
1068 | |||
1069 | ------------------------------------------------------------------------------ | ||
1070 | |||