LunaCON: add -ftrapv, -fwrapv opts, providing trapping/wrapping arith semantics.

Currently only for multiplication.

git-svn-id: https://svn.eduke32.com/eduke32@3949 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
helixhorned 2013-07-13 21:04:50 +00:00
parent 75204d782a
commit adbd6394fc
2 changed files with 179 additions and 144 deletions

View file

@ -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)

View file

@ -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=<directory>: 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<directory>: 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=<directory>: 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<directory>: 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