-------------------------------------------------------------------------------
-- Copyright (c) 2005-2013 Kein-Hong Man, Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
--     Kein-Hong Man  - Initial implementation for Lua 5.0, part of Yueliang
--     Fabien Fleutot - Port to Lua 5.1, integration with Metalua
--
-------------------------------------------------------------------------------

----------------------------------------------------------------------
--
-- WARNING! You're entering a hackish area, proceed at your own risks!
--
-- This code results from the borrowing, then ruthless abuse, of
-- Yueliang's implementation of Lua 5.0 compiler. I claim
-- responsibility for all of the ugly, dirty stuff that you might spot
-- in it.
--
-- Eventually, this code will be rewritten, either in Lua or more
-- probably in C. Meanwhile, if you're interested into digging
-- metalua's sources, this is not the best part to invest your time
-- on.
--
-- End of warning.
--
----------------------------------------------------------------------

--[[--------------------------------------------------------------------

  $Id$

  lopcodes.lua
  Lua 5 virtual machine opcodes in Lua
  This file is part of Yueliang.

  Copyright (c) 2005 Kein-Hong Man <khman@users.sf.net>
  The COPYRIGHT file describes the conditions
  under which this software may be distributed.

  See the ChangeLog for more information.

------------------------------------------------------------------------

  [FF] Slightly modified, mainly to produce Lua 5.1 bytecode.

----------------------------------------------------------------------]]

--[[--------------------------------------------------------------------
-- Notes:
-- * an Instruction is a table with OP, A, B, C, Bx elements; this
--   should allow instruction handling to work with doubles and ints
-- * Added:
--   luaP:Instruction(i): convert field elements to a 4-char string
--   luaP:DecodeInst(x): convert 4-char string into field elements
-- * WARNING luaP:Instruction outputs instructions encoded in little-
--   endian form and field size and positions are hard-coded
----------------------------------------------------------------------]]

local function debugf() end

local luaP = { }

--[[
===========================================================================
  We assume that instructions are unsigned numbers.
  All instructions have an opcode in the first 6 bits.
  Instructions can have the following fields:
        'A' : 8 bits
        'B' : 9 bits
        'C' : 9 bits
        'Bx' : 18 bits ('B' and 'C' together)
        'sBx' : signed Bx

  A signed argument is represented in excess K; that is, the number
  value is the unsigned value minus K. K is exactly the maximum value
  for that argument (so that -max is represented by 0, and +max is
  represented by 2*max), which is half the maximum for the corresponding
  unsigned argument.
===========================================================================
--]]

luaP.OpMode = {"iABC", "iABx", "iAsBx"}  -- basic instruction format

------------------------------------------------------------------------
-- size and position of opcode arguments.
-- * WARNING size and position is hard-coded elsewhere in this script
------------------------------------------------------------------------
luaP.SIZE_C  = 9
luaP.SIZE_B  = 9
luaP.SIZE_Bx = luaP.SIZE_C + luaP.SIZE_B
luaP.SIZE_A  = 8

luaP.SIZE_OP = 6

luaP.POS_C  = luaP.SIZE_OP
luaP.POS_B  = luaP.POS_C + luaP.SIZE_C
luaP.POS_Bx = luaP.POS_C
luaP.POS_A  = luaP.POS_B + luaP.SIZE_B

--FF from 5.1
luaP.BITRK = 2^(luaP.SIZE_B - 1)
function luaP:ISK(x) return x >= self.BITRK end
luaP.MAXINDEXRK = luaP.BITRK - 1
function luaP:RKASK(x)
   if x < self.BITRK then return x+self.BITRK else return x end
end



------------------------------------------------------------------------
-- limits for opcode arguments.
-- we use (signed) int to manipulate most arguments,
-- so they must fit in BITS_INT-1 bits (-1 for sign)
------------------------------------------------------------------------
-- removed "#if SIZE_Bx < BITS_INT-1" test, assume this script is
-- running on a Lua VM with double or int as LUA_NUMBER

luaP.MAXARG_Bx  = math.ldexp(1, luaP.SIZE_Bx) - 1
luaP.MAXARG_sBx = math.floor(luaP.MAXARG_Bx / 2)  -- 'sBx' is signed

luaP.MAXARG_A = math.ldexp(1, luaP.SIZE_A) - 1
luaP.MAXARG_B = math.ldexp(1, luaP.SIZE_B) - 1
luaP.MAXARG_C = math.ldexp(1, luaP.SIZE_C) - 1

-- creates a mask with 'n' 1 bits at position 'p'
-- MASK1(n,p) deleted
-- creates a mask with 'n' 0 bits at position 'p'
-- MASK0(n,p) deleted

--[[--------------------------------------------------------------------
  Visual representation for reference:

   31     |     |     |          0      bit position
    +-----+-----+-----+----------+
    |  B  |  C  |  A  |  Opcode  |      iABC format
    +-----+-----+-----+----------+
    -  9  -  9  -  8  -    6     -      field sizes
    +-----+-----+-----+----------+
    |   [s]Bx   |  A  |  Opcode  |      iABx | iAsBx format
    +-----+-----+-----+----------+
----------------------------------------------------------------------]]

------------------------------------------------------------------------
-- the following macros help to manipulate instructions
-- * changed to a table object representation, very clean compared to
--   the [nightmare] alternatives of using a number or a string
------------------------------------------------------------------------

-- these accept or return opcodes in the form of string names
function luaP:GET_OPCODE(i) return self.ROpCode[i.OP] end
function luaP:SET_OPCODE(i, o) i.OP = self.OpCode[o] end

function luaP:GETARG_A(i) return i.A end
function luaP:SETARG_A(i, u) i.A = u end

function luaP:GETARG_B(i) return i.B end
function luaP:SETARG_B(i, b) i.B = b end

function luaP:GETARG_C(i) return i.C end
function luaP:SETARG_C(i, b) i.C = b end

function luaP:GETARG_Bx(i) return i.Bx end
function luaP:SETARG_Bx(i, b) i.Bx = b end

function luaP:GETARG_sBx(i) return i.Bx - self.MAXARG_sBx end
function luaP:SETARG_sBx(i, b) i.Bx = b + self.MAXARG_sBx end

function luaP:CREATE_ABC(o,a,b,c)
  return {OP = self.OpCode[o], A = a, B = b, C = c}
end

function luaP:CREATE_ABx(o,a,bc)
  return {OP = self.OpCode[o], A = a, Bx = bc}
end

------------------------------------------------------------------------
-- Bit shuffling stuffs
------------------------------------------------------------------------

if false and pcall (require, 'bit') then
   ------------------------------------------------------------------------
   -- Return a 4-char string little-endian encoded form of an instruction
   ------------------------------------------------------------------------
   function luaP:Instruction(i)
      --FIXME
   end
else
   ------------------------------------------------------------------------   
   -- Version without bit manipulation library.
   ------------------------------------------------------------------------
   local p2 = {1,2,4,8,16,32,64,128,256, 512, 1024, 2048, 4096}
   -- keeps [n] bits from [x]
   local function keep (x, n) return x % p2[n+1] end
   -- shifts bits of [x] [n] places to the right
   local function srb (x,n) return math.floor (x / p2[n+1]) end
   -- shifts bits of [x] [n] places to the left
   local function slb (x,n) return x * p2[n+1] end

   ------------------------------------------------------------------------
   -- Return a 4-char string little-endian encoded form of an instruction
   ------------------------------------------------------------------------
   function luaP:Instruction(i)
      -- printf("Instr->string: %s %s", self.opnames[i.OP], table.tostring(i))
      local c0, c1, c2, c3
      -- change to OP/A/B/C format if needed
      if i.Bx then i.C = keep (i.Bx, 9); i.B = srb (i.Bx, 9) end
      -- c0 = 6B from opcode + 2LSB from A (flushed to MSB)
      c0 = i.OP + slb (keep (i.A, 2), 6) 
      -- c1 = 6MSB from A + 2LSB from C (flushed to MSB)
      c1 = srb (i.A, 2) + slb (keep (i.C, 2), 6)
      -- c2 = 7MSB from C + 1LSB from B (flushed to MSB)
      c2 = srb (i.C, 2) + slb (keep (i.B, 1), 7)
      -- c3 = 8MSB from B
      c3 = srb (i.B, 1)
      --printf ("Instruction:   %s %s", self.opnames[i.OP], tostringv (i))
      --printf ("Bin encoding:  %x %x %x %x", c0, c1, c2, c3)  
      return string.char(c0, c1, c2, c3)
   end
end
------------------------------------------------------------------------
-- decodes a 4-char little-endian string into an instruction struct
------------------------------------------------------------------------
function luaP:DecodeInst(x)
  error "Not implemented"
end

------------------------------------------------------------------------
-- invalid register that fits in 8 bits
------------------------------------------------------------------------
luaP.NO_REG = luaP.MAXARG_A

------------------------------------------------------------------------
-- R(x) - register
-- Kst(x) - constant (in constant table)
-- RK(x) == if x < MAXSTACK then R(x) else Kst(x-MAXSTACK)
------------------------------------------------------------------------

------------------------------------------------------------------------
-- grep "ORDER OP" if you change these enums
------------------------------------------------------------------------

--[[--------------------------------------------------------------------
Lua virtual machine opcodes (enum OpCode):
------------------------------------------------------------------------
name          args    description
------------------------------------------------------------------------
OP_MOVE       A B     R(A) := R(B)
OP_LOADK      A Bx    R(A) := Kst(Bx)
OP_LOADBOOL   A B C   R(A) := (Bool)B; if (C) PC++
OP_LOADNIL    A B     R(A) := ... := R(B) := nil
OP_GETUPVAL   A B     R(A) := UpValue[B]
OP_GETGLOBAL  A Bx    R(A) := Gbl[Kst(Bx)]
OP_GETTABLE   A B C   R(A) := R(B)[RK(C)]
OP_SETGLOBAL  A Bx    Gbl[Kst(Bx)] := R(A)
OP_SETUPVAL   A B     UpValue[B] := R(A)
OP_SETTABLE   A B C   R(A)[RK(B)] := RK(C)
OP_NEWTABLE   A B C   R(A) := {} (size = B,C)
OP_SELF       A B C   R(A+1) := R(B); R(A) := R(B)[RK(C)]
OP_ADD        A B C   R(A) := RK(B) + RK(C)
OP_SUB        A B C   R(A) := RK(B) - RK(C)
OP_MUL        A B C   R(A) := RK(B) * RK(C)
OP_DIV        A B C   R(A) := RK(B) / RK(C)
OP_POW        A B C   R(A) := RK(B) ^ RK(C)
OP_UNM        A B     R(A) := -R(B)
OP_NOT        A B     R(A) := not R(B)
OP_CONCAT     A B C   R(A) := R(B).. ... ..R(C)
OP_JMP        sBx     PC += sBx
OP_EQ         A B C   if ((RK(B) == RK(C)) ~= A) then pc++
OP_LT         A B C   if ((RK(B) <  RK(C)) ~= A) then pc++
OP_LE         A B C   if ((RK(B) <= RK(C)) ~= A) then pc++
OP_TEST       A B C   if (R(B) <=> C) then R(A) := R(B) else pc++
OP_CALL       A B C   R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1))
OP_TAILCALL   A B C   return R(A)(R(A+1), ... ,R(A+B-1))
OP_RETURN     A B     return R(A), ... ,R(A+B-2)  (see note)
OP_FORLOOP    A sBx   R(A)+=R(A+2); if R(A) <?= R(A+1) then PC+= sBx
OP_TFORLOOP   A C     R(A+2), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
                      if R(A+2) ~= nil then pc++
OP_TFORPREP   A sBx   if type(R(A)) == table then R(A+1):=R(A), R(A):=next;
                      PC += sBx
OP_SETLIST    A Bx    R(A)[Bx-Bx%FPF+i] := R(A+i), 1 <= i <= Bx%FPF+1
OP_SETLISTO   A Bx    (see note)
OP_CLOSE      A       close all variables in the stack up to (>=) R(A)
OP_CLOSURE    A Bx    R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n))
----------------------------------------------------------------------]]

luaP.opnames = {}  -- opcode names
luaP.OpCode = {}   -- lookup name -> number
luaP.ROpCode = {}  -- lookup number -> name

local i = 0
for v in string.gfind([[
MOVE -- 0
LOADK
LOADBOOL
LOADNIL
GETUPVAL
GETGLOBAL -- 5
GETTABLE
SETGLOBAL
SETUPVAL
SETTABLE
NEWTABLE -- 10
SELF
ADD
SUB
MUL
DIV -- 15
MOD
POW
UNM
NOT
LEN -- 20
CONCAT
JMP
EQ
LT
LE -- 25
TEST
TESTSET
CALL
TAILCALL
RETURN -- 30
FORLOOP
FORPREP
TFORLOOP
SETLIST
CLOSE -- 35
CLOSURE
VARARG
]], "[%a]+") do
  local n = "OP_"..v
  luaP.opnames[i] = v
  luaP.OpCode[n] = i
  luaP.ROpCode[i] = n
  i = i + 1
end
luaP.NUM_OPCODES = i

--[[
===========================================================================
  Notes:
  (1) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
      and can be 0: OP_CALL then sets 'top' to last_result+1, so
      next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use 'top'.

  (2) In OP_RETURN, if (B == 0) then return up to 'top'

  (3) For comparisons, B specifies what conditions the test should accept.

  (4) All 'skips' (pc++) assume that next instruction is a jump

  (5) OP_SETLISTO is used when the last item in a table constructor is a
      function, so the number of elements set is up to top of stack
===========================================================================
--]]

------------------------------------------------------------------------
-- masks for instruction properties
------------------------------------------------------------------------
-- was enum OpModeMask:
luaP.OpModeBreg = 2  -- B is a register
luaP.OpModeBrk  = 3  -- B is a register/constant
luaP.OpModeCrk  = 4  -- C is a register/constant
luaP.OpModesetA = 5  -- instruction set register A
luaP.OpModeK    = 6  -- Bx is a constant
luaP.OpModeT    = 1  -- operator is a test

------------------------------------------------------------------------
-- get opcode mode, e.g. "iABC"
------------------------------------------------------------------------
function luaP:getOpMode(m)
   --printv(m)
   --printv(self.OpCode[m])
   --printv(self.opmodes [self.OpCode[m]+1])
   return self.OpMode[tonumber(string.sub(self.opmodes[self.OpCode[m] + 1], 7, 7))]
end

------------------------------------------------------------------------
-- test an instruction property flag
-- * b is a string, e.g. "OpModeBreg"
------------------------------------------------------------------------
function luaP:testOpMode(m, b)
  return (string.sub(self.opmodes[self.OpCode[m] + 1], self[b], self[b]) == "1")
end

-- number of list items to accumulate before a SETLIST instruction
-- (must be a power of 2)
-- * used in lparser, lvm, ldebug, ltests
luaP.LFIELDS_PER_FLUSH = 50 --FF updated to match 5.1

-- luaP_opnames[] is set above, as the luaP.opnames table
-- opmode(t,b,bk,ck,sa,k,m) deleted

--[[--------------------------------------------------------------------
  Legend for luaP:opmodes:
  1 T  -> T (is a test?)
  2 B  -> B is a register
  3 b  -> B is an RK register/constant combination
  4 C  -> C is an RK register/constant combination
  5 A  -> register A is set by the opcode
  6 K  -> Bx is a constant
  7 m  -> 1 if iABC  layout,
          2 if iABx  layout, 
          3 if iAsBx layout
----------------------------------------------------------------------]]

luaP.opmodes = {
-- TBbCAKm      opcode
  "0100101", -- OP_MOVE      0
  "0000112", -- OP_LOADK
  "0000101", -- OP_LOADBOOL
  "0100101", -- OP_LOADNIL
  "0000101", -- OP_GETUPVAL
  "0000112", -- OP_GETGLOBAL 5
  "0101101", -- OP_GETTABLE
  "0000012", -- OP_SETGLOBAL
  "0000001", -- OP_SETUPVAL
  "0011001", -- OP_SETTABLE
  "0000101", -- OP_NEWTABLE 10
  "0101101", -- OP_SELF
  "0011101", -- OP_ADD
  "0011101", -- OP_SUB
  "0011101", -- OP_MUL
  "0011101", -- OP_DIV      15
  "0011101", -- OP_MOD
  "0011101", -- OP_POW
  "0100101", -- OP_UNM
  "0100101", -- OP_NOT
  "0100101", -- OP_LEN      20
  "0101101", -- OP_CONCAT
  "0000003", -- OP_JMP
  "1011001", -- OP_EQ
  "1011001", -- OP_LT
  "1011001", -- OP_LE       25
  "1000101", -- OP_TEST
  "1100101", -- OP_TESTSET
  "0000001", -- OP_CALL
  "0000001", -- OP_TAILCALL
  "0000001", -- OP_RETURN   30
  "0000003", -- OP_FORLOOP
  "0000103", -- OP_FORPREP
  "1000101", -- OP_TFORLOOP
  "0000001", -- OP_SETLIST
  "0000001", -- OP_CLOSE    35
  "0000102", -- OP_CLOSURE
  "0000101"  -- OP_VARARG
}

return luaP