diff --git a/polymer/eduke32/source/lunatic/control.lua b/polymer/eduke32/source/lunatic/control.lua index 1e92b873b..54f17c7ee 100644 --- a/polymer/eduke32/source/lunatic/control.lua +++ b/polymer/eduke32/source/lunatic/control.lua @@ -19,6 +19,10 @@ local con_lang = require("con_lang") local byte = require("string").byte local setmetatable = setmetatable +local band, bor = bit.band, bit.bor +local rshift = bit.rshift +local tobit = bit.tobit + local assert = assert local error = error local ipairs = ipairs @@ -182,7 +186,7 @@ local D = { true } local function krandand(mask) - return bit.band(ffiC.krand(), mask) + return band(ffiC.krand(), mask) end local function check_allnumbers(...) @@ -325,7 +329,7 @@ local int16_st = ffi.typeof "struct { int16_t s; }" -- Get INT32_MIN for the following constant; passing 0x80000000 would be -- out of the range for an int32_t and thus undefined behavior! -local SHOOT_HARDCODED_ZVEL = bit.tobit(0x80000000) +local SHOOT_HARDCODED_ZVEL = tobit(0x80000000) function _shoot(i, tilenum, zvel) check_sprite_idx(i) @@ -337,19 +341,19 @@ function _shoot(i, tilenum, zvel) return CF.A_ShootWithZvel(i, tilenum, zvel) end -local BADGUY_MASK = bit.bor(con_lang.SFLAG.SFLAG_HARDCODED_BADGUY, con_lang.SFLAG.SFLAG_BADGUY) +local BADGUY_MASK = bor(con_lang.SFLAG.SFLAG_HARDCODED_BADGUY, con_lang.SFLAG.SFLAG_BADGUY) function isenemytile(tilenum) - return (bit.band(ffiC.g_tile[tilenum]._flags, BADGUY_MASK)~=0) + return (band(ffiC.g_tile[tilenum]._flags, BADGUY_MASK)~=0) end -- The 'rotatesprite' wrapper used by the CON commands. function _rotspr(x, y, zoom, ang, tilenum, shade, pal, orientation, alpha, cx1, cy1, cx2, cy2) check_tile_idx(tilenum) - orientation = bit.band(orientation, 4095) -- ROTATESPRITE_MAX-1 + orientation = band(orientation, 4095) -- ROTATESPRITE_MAX-1 - if (bit.band(orientation, 2048) == 0) then -- ROTATESPRITE_FULL16 + if (band(orientation, 2048) == 0) then -- ROTATESPRITE_FULL16 x = 65536*x y = 65536*y end @@ -363,7 +367,7 @@ function _rotspr(x, y, zoom, ang, tilenum, shade, pal, orientation, error(format("invalid coordinates (%.03f, %.03f)", x, y), 2) end - ffiC.rotatesprite_(x, y, zoom, ang, tilenum, shade, pal, bit.bor(2,orientation), + ffiC.rotatesprite_(x, y, zoom, ang, tilenum, shade, pal, bor(2,orientation), alpha, cx1, cy1, cx2, cy2) end @@ -372,7 +376,7 @@ function rotatesprite(x, y, zoom, ang, tilenum, shade, pal, orientation, alpha, cx1, cy1, cx2, cy2) -- Disallow <<16 coordinates from Lunatic. They only unnecessarily increase -- complexity; you already have more precision in the FP number fraction. - if (bit.band(orientation, 2048) ~= 0) then + if (band(orientation, 2048) ~= 0) then error('left-shift-by-16 coordinates forbidden', 2) end @@ -402,13 +406,11 @@ function _gettimedate() return v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7] end -local rshift = bit.rshift - function rnd(x) return (rshift(ffiC.krand(), 8) >= (255-x)) end --- Legacy operators +--- Legacy operators --- function _rand(x) return rshift(ffiC.krand()*(x+1), 16) @@ -418,19 +420,40 @@ function _displayrand(x) return rshift(math.random(0, 32767)*(x+1), 15) end -function _div(a,b) - if (b==0) then - error("divide by zero", 2) - end - -- NOTE: don't confuse with math.modf! - return (a - math.fmod(a,b))/b -end +do + -- Arithmetic operations -- + local INT32_MIN = tobit(0x80000000) + local INT32_MAX = tobit(0x7fffffff) -function _mod(a,b) - if (b==0) then - error("mod by zero", 2) + -- Trapping multiplication. + function _mulTR(a,b) + local c = a*b + if (not (c >= INT32_MIN and c <= INT32_MAX)) then + error("overflow in multiplication", 2) + end + return c + end + + -- Wrapping multiplication. + function _mulWR(a,b) + -- XXX: problematic if a*b in an infinity or NaN. + return tobit(a*b) + end + + function _div(a,b) + if (b==0) then + error("divide by zero", 2) + end + -- NOTE: don't confuse with math.modf! + return (a - math.fmod(a,b))/b + end + + function _mod(a,b) + if (b==0) then + error("mod by zero", 2) + end + return (math.fmod(a,b)) end - return (math.fmod(a,b)) end -- Sect_ToggleInterpolation() clone @@ -860,7 +883,7 @@ local function text_check_common(tilenum, orientation) error("invalid base tile number "..tilenum, 3) end - return bit.band(orientation, 4095) -- ROTATESPRITE_MAX-1 + return band(orientation, 4095) -- ROTATESPRITE_MAX-1 end function _gametext(tilenum, x, y, qnum, shade, pal, orientation, @@ -1076,7 +1099,7 @@ function _addinventory(ps, inv, amount, i) if (inv == ffiC.GET_ACCESS) then local pal = sprite[i].pal if (PALBITS[pal]) then - ps.got_access = bit.bor(ps.got_access, PALBITS[pal]) + ps.got_access = bor(ps.got_access, PALBITS[pal]) end else if (ICONS[inv]) then @@ -1097,7 +1120,7 @@ function _checkpinventory(ps, inv, amount, i) return ps.inv_amount[inv] ~= ps.max_shield_amount elseif (inv==ffiC.GET_ACCESS) then local palbit = PALBITS[sprite[i].pal] - return palbit and (bit.band(ps.got_access, palbit)~=0) + return palbit and (band(ps.got_access, palbit)~=0) else return ps.inv_amount[inv] ~= amount end @@ -1161,7 +1184,7 @@ function _addphealth(ps, aci, hlthadd) j = math.max(j, 0) if (hlthadd > 0) then - local qmaxhlth = bit.rshift(ps.max_player_health, 2) + local qmaxhlth = rshift(ps.max_player_health, 2) if (j-hlthadd < qmaxhlth and j >= qmaxhlth) then -- XXX: DUKE_GOTHEALTHATLOW _sound(aci, 229) @@ -1221,9 +1244,9 @@ function _operate(spritenum) if (tag.sector >= 0) then local sect = sector[tag.sector] local lotag = sect.lotag - if (NEAROP[bit.band(lotag, 0xff)]) then + if (NEAROP[band(lotag, 0xff)]) then if (lotag==23 or sect.floorz==sect.ceilingz) then - if (bit.band(lotag, 32768+16384) == 0) then + if (band(lotag, 32768+16384) == 0) then for j in spritesofsect(tag.sector) do if (ispic(sprite[j].picnum, "ACTIVATOR")) then return @@ -1300,7 +1323,7 @@ end -- "otherspr" is either player or holoduke sprite local function A_FurthestVisiblePoint(aci, otherspr) - if (bit.band(actor[aci]:get_count(), 63) ~= 0) then + if (band(actor[aci]:get_count(), 63) ~= 0) then return end @@ -1496,8 +1519,8 @@ end -- G_GetAngleDelta(a1, a2) function _angdiff(a1, a2) - a1 = bit.band(a1, 2047) - a2 = bit.band(a2, 2047) + a1 = band(a1, 2047) + a2 = band(a2, 2047) -- a1 and a2 are in [0, 2047] if (abs(a2-a1) < 1024) then return abs(a2-a1) @@ -1535,7 +1558,6 @@ function _ifp(flags, pli, aci) local l = flags local ps = player[pli] local vel = sprite[ps.i].xvel - local band = bit.band if (band(l,8)~=0 and ps.on_ground and holdskey(pli, "CROUCH")) then return true @@ -1591,7 +1613,7 @@ function _checkspace(sectnum, floorp) local sect = sector[sectnum] local picnum = floorp and sect.floorpicnum or sect.ceilingpicnum local stat = floorp and sect.floorstat or sect.ceilingstat - return bit.band(stat,1)~=0 and sect.ceilingpal == 0 and + return band(stat,1)~=0 and sect.ceilingpal == 0 and (ispic(picnum, "MOONSKY1") or ispic(picnum, "BIGORBIT1")) end @@ -1745,7 +1767,7 @@ function _startlevel(volume, level) ffiC.ud.display_bonus_screen = 0 -- TODO_MP - player[0].gm = bit.bor(player[0].gm, 0x00000008) -- MODE_EOL + player[0].gm = bor(player[0].gm, 0x00000008) -- MODE_EOL end function _setaspect(viewingrange, yxaspect) diff --git a/polymer/eduke32/source/lunatic/lunacon.lua b/polymer/eduke32/source/lunatic/lunacon.lua index 12cc71196..6e6a88015 100644 --- a/polymer/eduke32/source/lunatic/lunacon.lua +++ b/polymer/eduke32/source/lunatic/lunacon.lua @@ -124,10 +124,109 @@ local g_warn = { ["not-redefined"]=true, ["bad-identifier"]=false, -- Code generation and output options. local g_cgopt = { ["no"]=false, ["debug-lineinfo"]=false, ["gendir"]=nil, ["cache-sap"]=false, ["error-nostate"]=true, - ["playervar"]=true, } + ["playervar"]=true, ["trapv"]=false, ["wrapv"]=false, } local function csapp() return g_cgopt["cache-sap"] end +local function handle_cmdline_arg(str) + if (str:sub(1,1)=="-") then + if (#str == 1) then + printf("Warning: input from stdin not supported") + else + local ok = false + local kind = str:sub(2,2) + + -- -W(no-)*: warnings + if (kind=="W" and #str >= 3) then + local val = true + local warnstr = str:sub(3) + + if (warnstr == "all") then + -- Enable all warnings. + for wopt in pairs(g_warn) do + g_warn[wopt] = true + end + ok = true + else + -- Enable or disable a particular warning. + if (warnstr:sub(1,3)=="no-") then + val = false + warnstr = warnstr:sub(4) + end + + if (type(g_warn[warnstr])=="boolean") then + g_warn[warnstr] = val + ok = true + end + end + + -- -fno* special handling + elseif (str:sub(2)=="fno") then + -- Disable printing code entirely. + g_cgopt["no"] = true + ok = true + elseif (str:sub(2)=="fno=onlycheck") then + -- Disable printing code, only do syntax check of gen'd code. + g_cgopt["no"] = "onlycheck" + ok = true + + -- -fgendir=: specify directory for generated code + elseif (str:sub(2,9)=="fgendir=" and #str >= 10) then + g_cgopt["gendir"] = str:sub(10) + ok = true + + -- -f(no-)*: code generation options + elseif (kind=="f" and #str >= 3) then + local val = true + local cgstr = str:sub(3) + + if (cgstr:sub(1,3)=="no-") then + val = false + cgstr = cgstr:sub(4) + end + + if (type(g_cgopt[cgstr])=="boolean") then + g_cgopt[cgstr] = val + ok = true + end + + -- -I: default search directory (only ONCE, not search path) + elseif (kind=="I" and #str >= 3) then + g_defaultDir = str:sub(3) + ok = true + end + + if (not ffi and not ok) then + printf("Warning: Unrecognized option %s", str) + end + end + + return true + end +end + +-- Handle command line arguments. Has to happen before pattern construction, +-- because some of them depend on codegen options (specifically, -ftrapv, +-- -fwrapv). +if (string.dump) then + -- running stand-alone + local i = 1 + while (arg[i]) do + if (handle_cmdline_arg(arg[i])) then + table.remove(arg, i) -- remove processed cmdline arg + else + i = i+1 + end + end +else + -- running from EDuke32 + local i=0 + while (ffiC.g_argv[i] ~= nil) do + handle_cmdline_arg(ffi.string(ffiC.g_argv[i])) + i = i+1 + end +end + -- Stack with *true* on top if the innermost block is a "whilevar*n". local g_isWhile = {} -- Sequence number of 'while' statements, used to implement CON "break" inside @@ -199,6 +298,7 @@ local function new_initial_codetab() "local print, printf = print, printf", -- Cache a couple of often-used functions. + "local _div, _mod, _mulTR, _mulWR = _con._div, _con._mod, _con._mulTR, _con._mulWR", "local _band, _bor, _bxor = _bit.band, _bit.bor, _bit.bxor", "local _lsh, _rsh, _arsh = _bit.lshift, _bit.rshift, _bit.arshift", @@ -1616,20 +1716,25 @@ local Op = {} Op.var = cmd(W,D) Op.varvar = cmd(W,R) -function Op.varf(op) - if (#op <= 2) then - return Op.var / ("%1=%1"..op.."%2") +function Op.var_common(thecmd, defaultop, trapop, wrapop) + local theop = + g_cgopt["trapv"] and trapop or + g_cgopt["wrapv"] and wrapop or + assert(defaultop) + + if (#theop <= 2) then + return thecmd / ("%1=%1"..theop.."%2") else - return Op.var / ("%1="..op.."(%1,%2)") + return thecmd / ("%1="..theop.."(%1,%2)") end end -function Op.varvarf(op) - if (#op <= 2) then - return Op.varvar / ("%1=%1"..op.."%2") - else - return Op.varvar / ("%1="..op.."(%1,%2)") - end +function Op.varf(...) + return Op.var_common(Op.var, ...) +end + +function Op.varvarf(...) + return Op.var_common(Op.varvar, ...) end -- Allow nesting... stuff like @@ -2085,9 +2190,9 @@ local Cinner = { -- NOTE the space after the minus sign so that e.g. "subvar x -1" won't get -- translated to "x=x--1" (-- being the Lua line comment start). subvarvar = Op.varvarf "- ", - mulvarvar = Op.varvarf "*", - divvarvar = Op.varvarf "_con._div", - modvarvar = Op.varvarf "_con._mod", + mulvarvar = Op.varvarf("*", "_mulTR", "_mulWR"), + divvarvar = Op.varvarf "_div", + modvarvar = Op.varvarf "_mod", andvarvar = Op.varvarf "_band", orvarvar = Op.varvarf "_bor", xorvarvar = Op.varvarf "_bxor", @@ -2096,9 +2201,9 @@ local Cinner = { setvar = Op.var / "%1=%2", addvar = Op.varf "+", subvar = Op.varf "- ", - mulvar = Op.varf "*", - divvar = Op.varf "_con._div", - modvar = Op.varf "_con._mod", + mulvar = Op.varf("*", "_mulTR", "_mulWR"), + divvar = Op.varf "_div", + modvar = Op.varf "_mod", andvar = Op.varf "_band", orvar = Op.varf "_bor", xorvar = Op.varf "_bxor", @@ -3474,83 +3579,6 @@ function parse(contents) -- local g_newlineidxs = newlineidxs end -local function handle_cmdline_arg(str) - if (str:sub(1,1)=="-") then - if (#str == 1) then - printf("Warning: input from stdin not supported") - else - local ok = false - local kind = str:sub(2,2) - - -- -W(no-)*: warnings - if (kind=="W" and #str >= 3) then - local val = true - local warnstr = str:sub(3) - - if (warnstr == "all") then - -- Enable all warnings. - for wopt in pairs(g_warn) do - g_warn[wopt] = true - end - ok = true - else - -- Enable or disable a particular warning. - if (warnstr:sub(1,3)=="no-") then - val = false - warnstr = warnstr:sub(4) - end - - if (type(g_warn[warnstr])=="boolean") then - g_warn[warnstr] = val - ok = true - end - end - - -- -fno* special handling - elseif (str:sub(2)=="fno") then - -- Disable printing code entirely. - g_cgopt["no"] = true - ok = true - elseif (str:sub(2)=="fno=onlycheck") then - -- Disable printing code, only do syntax check of gen'd code. - g_cgopt["no"] = "onlycheck" - ok = true - - -- -fgendir=: specify directory for generated code - elseif (str:sub(2,9)=="fgendir=" and #str >= 10) then - g_cgopt["gendir"] = str:sub(10) - ok = true - - -- -f(no-)*: code generation options - elseif (kind=="f" and #str >= 3) then - local val = true - local cgstr = str:sub(3) - - if (cgstr:sub(1,3)=="no-") then - val = false - cgstr = cgstr:sub(4) - end - - if (type(g_cgopt[cgstr])=="boolean") then - g_cgopt[cgstr] = val - ok = true - end - - -- -I: default search directory (only ONCE, not search path) - elseif (kind=="I" and #str >= 3) then - g_defaultDir = str:sub(3) - ok = true - end - - if (not ffi and not ok) then - printf("Warning: Unrecognized option %s", str) - end - end - - return true - end -end - local function reset_all() reset_labels() reset_gamedata() @@ -3568,16 +3596,6 @@ if (string.dump) then -- running stand-alone local io = require("io") - -- Handle command-line arguments and remove those that have been processed. - local i = 1 - while (arg[i]) do - if (handle_cmdline_arg(arg[i])) then - table.remove(arg, i) - else - i = i+1 - end - end - local function compile(filename) reset_all() @@ -3654,13 +3672,8 @@ if (string.dump) then end end else - local i=0 - while (ffiC.g_argv[i] ~= nil) do - handle_cmdline_arg(ffi.string(ffiC.g_argv[i])) - i = i+1 - end - -- running from EDuke32 + function compile(filenames) -- TODO: pathsearchmode=1 set in G_CompileScripts