#!/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 do not matter and are used to separate bitfields in a byte
# for readability.
#
# For example:
#   b_00_xxx_100 = 0x04 | 0x0c | 0x14 | 0x1c | 0x24 | 0x2c | 0x34 | 0x3c;

# Only generating the byte machines that are needed by the validator or
# the decoder.
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 MatchingBytes(byte_machine):
  assert byte_machine.startswith('b_')
  mask = byte_machine[2:].replace('_', '').replace('X', 'x')
  mask = mask[::-1]
  assert len(mask) == 8
  assert all(mask_bit in '01x' for mask_bit in mask)

  matching_bytes = []
  for b in range(256):
    if all(mask_bit == 'x' or int(mask_bit) == ((b >> i) & 1)
           for i, mask_bit in enumerate(mask)):
      matching_bytes.append('0x%02x' % b)

  assert len(matching_bytes) > 0
  return ' | '.join(matching_bytes)


assert (MatchingBytes('b_00_xxx_100') ==
        '0x04 | 0x0c | 0x14 | 0x1c | 0x24 | 0x2c | 0x34 | 0x3c')


def main():
  print '/* THIS FILE IS AUTOGENERATED (by byte_machines.py). DO NOT EDIT. */'
  print
  print '%%{'
  print '  machine byte_machine;'
  print '  alphtype unsigned char;'
  print

  for byte_machine in byte_machines:
    print '  %s = %s;' % (byte_machine, MatchingBytes(byte_machine))

  print '}%%'


if __name__ == '__main__':
  main()
