#!/usr/bin/python
# Copyright (c) 2012 The Native Client Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# Creates byte_machines.rl file.  Each machine accepts exactly one byte.
#
# In the name of a machine "0" means "unset bit", "1" means "set bit", "x" or
# "X" mean "any bit", other letters are ignored.  Prefix "b_" is used as
# convention to distinguish these autogenerated machines from the hand-written
# ones, underscores are used to separate bitfields in a byte.
#
# For example:
#   b_00_xxx_100 = 0x04 | 0x0c | 0x14 | 0x1c | 0x24 | 0x2c | 0x34 | 0x3c;

from __future__ import print_function

import optparse
import sys

byte_machines = [
  # ModR/M parsing
  'b_00_xxx_100',
  'b_00_xxx_101',
  'b_01_xxx_100',
  'b_10_xxx_100',
  'b_00_xxx_xxx',
  'b_11_010_xxx',
  'b_01_xxx_xxx',
  'b_10_xxx_xxx',
  'b_11_010_111',
  'b_11_100_111',
  'b_11_100_xxx',
  'b_11_111_111',
  'b_11_111_xxx',
  'b_11_xxx_111',
  'b_11_xxx_xxx',
  'b_xx_xxx_000',
  'b_xx_xxx_001',
  'b_xx_xxx_010',
  'b_xx_xxx_011',
  'b_xx_xxx_100',
  'b_xx_xxx_101',
  'b_xx_xxx_110',
  'b_xx_xxx_111',

  # Opcodes
  'b_xx_000_xxx',
  'b_xx_001_xxx',
  'b_xx_010_xxx',
  'b_xx_011_xxx',
  'b_xx_100_xxx',
  'b_xx_101_xxx',
  'b_xx_110_xxx',
  'b_xx_111_xxx',

  # REX parsing.
  'b_0100_x000',
  'b_0100_0x00',
  'b_0100_00x0',
  'b_0100_000x',
  'b_0100_xx00',
  'b_0100_x0x0',
  'b_0100_x00x',
  'b_0100_0xx0',
  'b_0100_0x0x',
  'b_0100_00xx',
  'b_0100_xxx0',
  'b_0100_xx0x',
  'b_0100_x0xx',
  'b_0100_0xxx',
  'b_0100_xxxx',

  # REX parsing - special instructions
  'b_0100_0xx1',
  'b_0100_10x1',
  'b_0100_11x0',
  'b_0100_11x1',
  'b_0100_xxx1',

  'b_0100_1000',
  'b_0100_1x00',
  'b_0100_10x0',
  'b_0100_100x',
  'b_0100_1xx0',
  'b_0100_1x0x',
  'b_0100_10xx',
  'b_0100_1xxx',

  # VEX parsing - RXB bits
  'b_111_xxxxx',
  'b_x11_xxxxx',
  'b_1x1_xxxxx',
  'b_11x_xxxxx',
  'b_xx1_xxxxx',
  'b_x1x_xxxxx',
  'b_1xx_xxxxx',
  'b_xxx_xxxxx',

  # VEX parsing - opcode tables
  'b_xxx_00001',
  'b_xxx_00010',
  'b_xxx_00011',
  'b_xxx_01000',
  'b_xxx_01001',
  'b_xxx_01010',

  # VEX parsing - operands (canonical: three bytes)
  'b_0_1111_0_00',
  'b_0_1111_0_01',
  'b_0_1111_0_10',
  'b_0_1111_0_11',
  'b_0_1111_1_00',
  'b_0_1111_1_01',
  'b_0_1111_1_10',
  'b_0_1111_1_11',
  'b_0_1XXX_0_00',
  'b_0_1XXX_0_01',
  'b_0_1XXX_0_10',
  'b_0_1XXX_0_11',
  'b_0_1XXX_1_00',
  'b_0_1XXX_1_01',
  'b_0_1XXX_1_10',
  'b_0_1XXX_1_11',
  'b_0_XXXX_0_00',
  'b_0_XXXX_0_01',
  'b_0_XXXX_0_10',
  'b_0_XXXX_0_11',
  'b_0_XXXX_1_00',
  'b_0_XXXX_1_01',
  'b_0_XXXX_1_10',
  'b_0_XXXX_1_11',
  'b_1_1111_0_00',
  'b_1_1111_0_01',
  'b_1_1111_0_10',
  'b_1_1111_0_11',
  'b_1_1111_1_00',
  'b_1_1111_1_01',
  'b_1_1111_1_10',
  'b_1_1111_1_11',
  'b_1_1XXX_0_00',
  'b_1_1XXX_0_01',
  'b_1_1XXX_0_10',
  'b_1_1XXX_0_11',
  'b_1_1XXX_1_00',
  'b_1_1XXX_1_01',
  'b_1_1XXX_1_10',
  'b_1_1XXX_1_11',
  'b_1_XXXX_0_00',
  'b_1_XXXX_0_01',
  'b_1_XXXX_0_10',
  'b_1_XXXX_0_11',
  'b_1_XXXX_1_00',
  'b_1_XXXX_1_01',

  # VEX parsing - operands (short: two bytes)
  'b_X_1111_0_00',
  'b_X_1111_0_01',
  'b_X_1111_0_10',
  'b_X_1111_0_11',
  'b_X_1111_1_00',
  'b_X_1111_1_01',
  'b_X_1111_1_10',
  'b_X_1111_1_11',
  'b_X_XXXX_0_00',
  'b_X_XXXX_0_01',
  'b_X_XXXX_0_10',
  'b_X_XXXX_0_11',
  'b_X_XXXX_1_00',
  'b_X_XXXX_1_01',
  'b_X_XXXX_1_10',
  'b_X_XXXX_1_11',

  # Four-five operands instruction parsing.
  'b_0xxx_0000',
  'b_xxxx_0000',
  'b_0xxx_00xx',
  'b_xxxx_00xx'

]

def GenerateByteMachines(output, filename=None):
  if filename is None:
    print('/*', file=output)
  else:
    print('/* native_client/{0}'.format(filename), file=output)
  print(' * THIS FILE IS AUTO-GENERATED. DO NOT EDIT.', file=output)
  print(' */', file=output)
  print(file=output)
  print('%%{', file=output)
  print('  machine byte_machine;', file=output)
  print('  alphtype unsigned char;', file=output)
  print(file=output)

  for byte_machine in byte_machines:
    byte = []
    for b in byte_machine:
      if b == 'X': byte.append('x')
      if b in '01x': byte.append(b)
      if len(byte) == 8:
        break
    byte.reverse()
    line_prefix = '  ' + byte_machine + ' = '
    outline = line_prefix
    delimiter = ' | '
    for num in range(256):
      if all(restriction == 'x' or int(restriction) == ((num >> bit) & 1)
             for bit, restriction in enumerate(byte)):
        if len(outline) > 74:
          print(outline[:-len(delimiter)] + ' |', file=output)
          outline = ' ' * len(line_prefix) + '0x{0:02x}'.format(num) + delimiter
        else:
          outline += '0x{0:02x}'.format(num) + delimiter
    print(outline[:-len(delimiter)] + ';', file=output)

  print('}%%', file=output)

def Main():
  parser = optparse.OptionParser()
  parser.add_option('-o', '--output', dest='filename',
                    help='write output to FILE', metavar='FILE')
  (options, args) = parser.parse_args()
  if options.filename is None:
    GenerateByteMachines(sys.stdout)
  else:
    with open(options.filename, 'w') as f:
      GenerateByteMachines(f, options.filename)

if __name__ == '__main__':
  Main()
