<?php


/*
Operations:

    
    
    

*/

$plan_desc_ops   = Array();
$plan_desc_modes = Array();

$plans = Array();
$descs = Array();



$modes = Array(
  
' {A}',
  
' #imm',
  
' rel8',
  
' $12',
  
' $12,x',
  
' $12,y',
  
' ($12,x)',
  
' ($12),y',
  
' $1234',
  
' $1234,x',
  
' $1234,y',
  
' ($1234)'
);

function 
transop($op$v)
{
  if(
preg_match('/^(02|12|22|32|42|52|62|72|92|B2|D2|F2)$/'sprintf('%02X',$v)))
    return 
"$op <kil>";
  if(
$op != 'nop'
  
&& preg_match('/[01357DF]C|8[029]|C2|E2|[0461357DF]4/'sprintf('%02X',$v)))
    return 
"$op <nop>";
  return 
$op;
}

function 
set($op$params)
{
  global 
$plans$modes$descs;
  
  
$intr preg_match('/brk|INT|NMI|RES/'$op);

  
$a_in_c preg_match('/ora|slo|'.
                        
'and|bit|rla|'.
                        
'eor|sre/'$op);
  
$comparison =
    
preg_match('/cmp|dcp|cpx|cpy/'$op);

  
// Source always A?
  
$source_always_A =
    
preg_match('/sta|tax|tay|pha|cmp|dcp|s[ab]x|sh[as]|ane|anc|asr|arr/'$op) || $a_in_c;
  
$source_always_X =
    
preg_match('/stx|txa|dex|inx|cpx|txs|s[ab]x|sh[axs]|ane/'$op);
  
$source_always_Y =
    
preg_match('/sty|dey|iny|tya|cpy|shy/'$op);
  
$source_always_S =
    
preg_match('/tsx|las/'$op);
  
$source_always_P =
    
preg_match('/php|se[cid]|cl[cidv]|beq|bne|b[cv][sc]|bmi|bpl/'$op) || $intr;
  
// Source is A unless memory operand given
  
$source_A_if_not_memory =
    
preg_match('/asl|lsr|rol|ror|rla|rra/'$op);
  
$source_can_be_memory =
    
preg_match('/and|eor|ora|dec|inc|lda|ldx|ldy|'.
                
'asl|lsr|rol|ror|bit|cmp|cpx|cpy|'.
                
'adc|sbc|anc|ane|arr|asr|'.
                
'dcp|isb|las|lax|lxa|'.
                
'rla|rra|'.
                
'slo|sre/'$op);

  
// Whether result should be saved into A?
  
$target_always_A =
    
preg_match('/and|eor|ora|slo|lda|txa|tya|pla|anc|ane|'.
                
'adc|sbc|isb|las|lax|lxa|rla|sre|rra|asr|arr/'$op);
  
$target_A_if_not_memory =
    
preg_match('/asl|lsr|rol|ror|rla|rra/'$op);
  
$target_X_if_not_memory 0;
  
$target_Y_if_not_memory 0;
  
$target_memory // unless index<2
    
preg_match('/dec|inc|sta|stx|sty|asl|lsr|rol|ror|'.
                
'arr|rla|rra|sax|slo|sre|dcp|isb|sh[asxy]/'$op);

  
$target_always_X =
    
preg_match('/dex|inx|ldx|tax|tsx|las|lax|lxa|sbx/'$op);
  
$target_always_Y =
    
preg_match('/dey|iny|ldy|tay/'$op);
  
$target_always_S =
    
preg_match('/txs|las|shs/'$op);
  
$target_always_P =
    
preg_match('/plp|rti|sec|sei|sed|clc|cli|cld|clv/'$op) || $intr

  
$nz $target_always_Y || $target_always_X || $target_always_A
     
|| $target_A_if_not_memory
     
|| $comparison
     
|| preg_match('/dec|inc/'$op);

  foreach(
explode("'"$params) as $index=>$v)
  {
    if(
$v == '--') continue;

    
$slow $index != && 
      
preg_match('/[ndt]op|'.                     // nop
                  
'se[cid]|cl[cidv]|'.            // flag set/clear
                  
'asl|rol|slo|anc|rla|'.         // read-modify-store?
                  
'lsr|ror|rra|sre|arr|asr|'.     // read-modify-store
                  
'dec|dex|dey|dcp|'.             // read-modify-store
                  
'inc|inx|iny|isb|'.             // read-modify-store
                  
'jsr|pha|php|'.                 // push
                  
'rts|pla|plp|'.                 // pop
                  
'brk|INT|NMI|RES|'.
                  
'tax|tay|txa|tya|txs|tsx/',     // cross-register
                  
$op);

    
$vv=$v;
    
$v hexdec($v);
    if(
$v || $v 0x102)
    {
      print 
"eh, $v? ($vv)\n";
      continue;
    }
    
    
$descs[$v] = transop($op,$v) . ' ' $modes[$index];

    
$plan = Array();
    
    if(
$op == 'NMI')               $plan[1000] = "addr = 0xFFFA;";
    if(
$op == 'RES')               $plan[1001] = "addr = 0xFFFC;";
    if(
$v == 0x00 || $op=='INT')   $plan[1002] = "addr = 0xFFFE;";

    if(
$index >= 2)                $plan[1005] = "addr = RB(PC++);";
    
//if($index == 3 || $index == 7) $plan[1010] = "addr = t;";

    
if($index == || $index == 4
    
|| $index == 6)                $plan[1010] = "d = X;"// for address
    
if($index == || $index ==10
    
|| $index == 5)                $plan[1020] = "d = Y;"// for address

    
if($index >= && $index <= 6$plan[1030] = "addr=u8(addr+d); d=0; tick();";

    if(
$index >= 8)
    {
                                   
#$plan[1050] = "addr = u8(addr) + 256 * RB(PC++);";
                                   
$plan[1050] = "addr=u8(addr);   addr+=256*RB(PC++);";
    }

    if(
$index == || $index == 7
    
|| $index == 11 || $intr)
    {
                                   
#$plan[1062] = "addr = RW(addr);";
                                   
$plan[1062] = "addr=RB(c=addr); addr+=256*RB(wrap(c,c+1));";
    }
    
    
$abs_index_mode $index == || $index == || $index == 10;
    if(
$abs_index_mode && !$target_memory)
                                   
$plan[1070] = "Misfire(addr, addr+d);";
    if(
$abs_index_mode && $target_memory)
                                   
$plan[1080] = "RB(wrap(addr, addr+d));";

    
#if($index == 7
    #|| $index == 9 || $index ==10) $plan[1090] = "addr = addr+t;";

    
if(($source_A_if_not_memory && $index 2)
    || 
$source_always_A)           $plan[1100] = "t &= A;";
    if(
$source_always_X)           $plan[1101] = "t &= X;";
    if(
$source_always_Y)           $plan[1102] = "t &= Y;";
    if(
$source_always_S)           $plan[1103] = "t &= S;";
    if(
$source_always_P)           $plan[1104] = "t &= P.raw|pbits; c = t;";

    if(
$a_in_c || $comparison || $op == 'sbx')
                                   
$plan[1120] = "c = t; t = 0xFF;";

    if(
$index >= && $source_can_be_memory)
                                   
$plan[1130] = "t &= RB(addr+d);";

    if(
$index == 1)                $plan[1140] = "t &= RB(PC++);";


    if(
preg_match('/bit/',$op))    $plan[1150] = "P.V = t & 0x40; P.N = t & 0x80;";
    if(
preg_match('/rol|rla|ror|rra|arr/',$op))
                                   
$plan[1170] = "sb = P.C;";
    if(
preg_match('/asl|rol|slo|rla|anc|arr/',$op))
                                   
$plan[1180] = "P.C = t & 0x80;";
    
// NOTE: ARR takes carry from HIGH bit but shifts RIHT
    
if(preg_match('/lsr|ror|rra|sre|asr/',$op))
                                   
$plan[1181] = "P.C = t & 0x01;";
    if(
preg_match('/asl|rol|slo|rla/',$op))
                                   
$plan[1190] = "t = (t << 1) | (sb << 0);"// note: always includes extra tick
    
if(preg_match('/lsr|ror|rra|sre|arr|asr/',$op))
                                   
$plan[1191] = "t = (t >> 1) | (sb << 7);"// note: always includes extra tick


    
if(preg_match('/dec|dex|dey|dcp/'$op))
                                   
$plan[1240] = "t = u8(t - 1);"// note: always includes extra tick
    
if(preg_match('/inc|inx|iny|isb/'$op))
                                   
$plan[1250] = "t = u8(t + 1);"// note: always includes extra tick


    
if($target_memory && $index >= 2)
    {
      if(
preg_match('/sh[xyas]/',$op) && $v != 0x93)
                                   
$plan[1262] = "WB(wrap(addr, addr+d), t &= ((addr+d) >> 8));"// tested
      
else
                                   
$plan[1261] = "WB(addr+d, t);";
                                   
//$plan[1262] = "t &= (((addr) >> 8) + 1);"; // nestopia
                                   //$plan[1262] = "t &= (((addr-d) >> 8) + 1);"; // fceux
                                   //neither one passes instr_test.nes
                                   // My code passes instr_test.nes.
                                   // However, op 93 is excluded, because cpu_timing_test.nes
                                   // is damaged if the page-wrapping version is used.
    
}

    if(
preg_match('/pla|plp|rti/'$op))
                                   
$plan[1450] = "tick(); t = Pop();";

    if(
preg_match('/rti|rts/'$op))
                                   
$plan[1460] = "RB(PC++); PC = Pop(); PC |= (Pop() << 8);";
    if(
preg_match('/rts/'$op))
                                   
$plan[1470] = "RB(PC++);";
    if(
preg_match('/jsr|brk|INT|NMI|RES/'$op))
    {
                                   
#$plan[1480] = "PushW(PC + (op?-1:1));"; // note: always includes extra tick
                                   
$plan[1480] = "d=PC+(op?-1:1); Push(d>>8); Push(d);";
    }
    if(
preg_match('/jsr|brk|INT|NMI|RES|jmp/'$op))
                                   
$plan[1490] = "PC = addr;";
    if(
preg_match('/pha|php|brk|INT|NMI|RES/'$op))
                                   
$plan[1495] = "Push(t);"// note: always includes extra tick


    
$f1op=preg_match('/sec|clc|bcs|bcc/'$op);     //38, 18, B0, 90
    
$f2op=preg_match('/beq|bne/'$op);             //        F0, D0
    
$f4op=preg_match('/sei|cli/'$op) || $intr;    //78, 58           00 100 101 102
    
$f8op=preg_match('/sed|cld/'$op);             //F8, D8
    
$f64op=preg_match('/bvs|bvc|clv/'$op);        //    B8, 70, 50
    
$f128op=preg_match('/bmi|bpl/'$op);           //        30, 10
                                                    //
                                                    // 00:04 08:xx
                                                    // 10:80 18:01
                                                    // 20:xx 28:xx
                                                    // 30:80 38:01
                                                    // 40:xx 48:xx
                                                    // 50:40 58:04
                                                    // 60:xx 68:xx
                                                    // 70:40 78:04
                                                    // 80:xx 88:xx
                                                    // 90:01 98:XX
                                                    // A0:xx A8:XX
                                                    // B0:01 B8:40
                                                    // C0:xx C8:xx
                                                    // D0:02 D8:08
                                                    // E0:xx E8:xx
                                                    // F0:02 F8:08
                                                    // 100:04

                                                    // 1 << ("\7\0\6\2\0\6\1\3"[(op>>6)*2+(op&8)/8)])
                                                    // 1 << ("2x70xx70xx62xx62xx0xxx06xx13xx132"[op>>3]-'0')
                                                    // "\200\1\100\4\0\100\2\10"[(op>>6)*2+(op&8)/8)]
                                                    
    
if($f1op||$f2op||$f4op||$f8op||$f64op||$f128op)
    
#                                $plan[1550] = "t = 1 << (\"2x70xx70xx62xx62xx0xxx06xx13xx132\"[op/8]&3);";
                                   
$plan[1550] = "t = 1;";
    if(
$f2op||$f8op||$f128op)
                                   
$plan[1560] = "t <<= 1;";
    if(
$f4op||$f8op||$f64op||$f128op)
                                   
$plan[1570] = "t <<= 2;";
    if(
$f64op||$f128op)
                                   
$plan[1580] = "t <<= 4;";

    if(
preg_match('/sbc|isb|clc|cli|cld|clv/'$op))
                                   
$plan[1590] = "t = u8(~t);";


    if(
preg_match('/ora|slo|sec|sei|sed/'$op) || $intr)
                                   
$plan[1600] = "t = c | t;";
    if(
preg_match('/and|bit|rla|clc|cli|cld|clv|'.
                   
'bcs|beq|bvs|bmi|bcc|bne|bvc|bpl/'$op))
                                   
$plan[1603] = "t = c & t;";
    if(
preg_match('/eor|sre/'$op))
                                   
$plan[1606] = "t = c ^ t;";

    if(
preg_match('/bcs|beq|bvs|bmi/'$op))
                                   
$plan[1610] = "if(t)  { tick(); Misfire(PC, addr = s8(addr) + PC); PC=addr; }";
    if(
preg_match('/bcc|bne|bvc|bpl/'$op))
                                   
$plan[1620] = "if(!t) { tick(); Misfire(PC, addr = s8(addr) + PC); PC=addr; }";

    if(
preg_match('/adc|sbc|isb|rra/'$op))
                                   
$plan[1660] = "c = t; t += A + P.C; ".
                                                 
"P.V = (c^t) & (A^t) & 0x80; ".
                                                 
"P.C = t & 0x100;";

    if(
$comparison || $op == 'sbx')$plan[1670] = "t = c - t; P.C = ~t & 0x100;";


    if((
$target_A_if_not_memory && $index 2)
    || 
$target_always_A)           $plan[1700] = "A = t;";
    if((
$target_X_if_not_memory && $index 2)
    || 
$target_always_X)           $plan[1710] = "X = t;";
    if((
$target_Y_if_not_memory && $index 2)
    || 
$target_always_Y)           $plan[1720] = "Y = t;";
    if(
$target_always_S)           $plan[1730] = "S = t;";
    if(
$target_always_P)           $plan[1740] = "P.raw = t & ~0x30;";

    if(
$nz)
                                   
$plan[1880] = "P.N = t & 0x80;";
    if(
$nz || preg_match('/bit/',$op))
                                   
$plan[1881] = "P.Z = u8(t) == 0;";
    if(
preg_match('/arr/',$op))    $plan[1882] = "P.V = (((t >> 5)+1)&2);";

    if(
$slow)
                                   
$plan[1299] = "tick();"// 1900

    
$plans[$v] = $plan;
    
//if($index > 1) break;

    
global $plan_desc_modes$plan_desc_ops;
    foreach(
$plan as $point => $v)
    {
      if(
$point <= 1090 || $point == 1430 || $point == 1440)
      {
        
$plan_desc_modes[$point][$index] = $index;
        if(
$point == 1090)
          
$plan_desc_ops[$point][$op] = $op;
      }
      else
        
$plan_desc_ops[$point][$op] = $op;
    }
  }
}

require 
'opdata.php';
set("NMI""100"); // nmi
set("RES""101"); // reset
set("INT""102"); // intr


/*
for($c=0; $c<256; ++$c)
{
  if($c     % 8 == 0) print '  ';
  #if(isset($data[$c]))
  #  print chr($data[$c]);
  #else
  #  print '@';
  @printf('0x%04X', $plans[$c]);
  if(($c+1) %32 == 0) print ''."\n";
  else                print ',';
}
print "\n";
*/
/*
for($c=0; $c<256; ++$c)
{
  if($c     % 64 == 0) print '        "';
  $opindex = @$data2[$c];
  if(isset($data2[$c]))
  {
    $s = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    print $s[$opindex];
  }
  else
  {
    print 'o';
  }

  if(($c+1) % 64 == 0) print '"'."\n";
}
*/

if(1)
{
  
ob_start();
  for(
$n=0$n<0x103; ++$n)
  {
    
printf("// %02X: "$n);
    if(!isset(
$descs[$n]))
    {
      print 
"// undefined\n";
      continue;
    }
    print 
"// ".$descs[$n]."\n";
    
print_r($plans[$n]);
  }
  
file_put_contents('php://stderr'ob_get_clean());
}



$points = Array();
foreach(
$plans as $plan)
  foreach(
$plan as $k=>$v)
    
$points[$k] = $v;
ksort($points);

$masks = Array();
for(
$n=0$n<0x103; ++$n$masks[$n] = 0;

define('MAX_CONDITION_LENGTH'37);

$f fopen('php://stderr''w');

$r '';
$bitno 0;
$charset " !";
 for(
$c=0$c<15; ++$c$charset .= chr(65+$c); // A-O
# for($c=0; $c<11; ++$c) $charset .= chr(80+$c); // P-Z
 
for($c=1$c<10; ++$c$charset .= chr(48+$c); // 0-9
 
for($c=0$c<15; ++$c$charset .= chr(97+$c); // a-o
 
for($c=0$c<11; ++$c$charset .= chr(112+$c); // p-z

$rangebytetranslation = Array();
$rangebytes = Array();
foreach(
$points as $point => $code)
{
  
$ops = Array();
  foreach(
$plans as $op => $plan) if(isset($plan[$point])) $ops[$op] = $op;
  
$ranges = Array();
  for(
$n=0$n*8<0x103; ++$n$ranges[] = 0;
  foreach(
$ops as $o)         $ranges[(int)($o 8)] |= << ($o 8);
  foreach(
$ranges as $v) @$rangebytes[$v] += 1;
}
ksort($rangebytes); #$rangebytes = array_reverse($rangebytes, true);

$n=94;
$tab ''$firstn=-1;
foreach(
$rangebytes as $v=>$tmp)
{
  if(
$v<=1$c 32+$v;
  
#elseif($v <= 26) $c = 64 + $v;
  
elseif($v <  64$c 40 $v;
  else { if(
$firstn<0$firstn $v;
         
$tab .= chr(32 $v $firstn);
         
$c $n++; }//?
  
$rangebytetranslation[$v] = chr($c);//$charset[$n++];
}
print_r($rangebytetranslation);
print 
"firstn=$firstn, tab=$tab\n";

foreach(
$points as $point => $code)
{
  
$ops = Array();
  foreach(
$plans as $op => $plan)
    if(isset(
$plan[$point]))
      
$ops[$op] = $op;

  
/**/
  
fwrite($f'//');
  foreach(
$ops as $o)
    
fprintf($f'%02X '$o);
  
fwrite($f"\n");
  
$cond str_repeat('x'999);
  
#$cond = CreateCondition($ops);
  #fwrite($f, "//$cond\n");

  
$cond str_pad($condMAX_CONDITION_LENGTH);
  
#if(strlen($cond) < MAX_CONDITION_LENGTH)
  #  $s = $cond;
  #else
  #{
    
$ranges = Array();
    for(
$n=0$n*8<0x103; ++$n//
      
$ranges[] = 0;
    foreach(
$ops as $o)
      
$ranges[(int)($o 8)] |= << ($o 8);
    
$s '';
    
#print json_encode($ranges)."\n";
    
for($n=0$n*8<0x103; ++$n//
    
{
      
$s .= $rangebytetranslation[$ranges[$n]];
    }
    
$s 'i%"'.$s.'"';
    
$s str_pad($sMAX_CONDITION_LENGTH);
  
#}

  
if(strlen($cond) <= MAX_CONDITION_LENGTH
  
&& (substr_count($cond' ') >= MAX_CONDITION_LENGTH-19
  
||  substr_count($cond' ')
   >= 
substr_count($s,    ' ') ))
  {
    
$s $cond;
  }

  
#$l = strlen($s);
  #$code = preg_replace('/;$/', '', $code);
  #$s = sprintf("        i(%-{$l}s, %s);", $s, $code);

  
$code trim($code);
  if(
strpos(preg_replace('/;$/'''$code), ';') !== false$code "{ $code }";
  
$s  "        if($s$code";

  if(
$point == 1000$s .= " // NMI vector location";
  if(
$point == 1001$s .= " // Reset vector location";
  if(
$point == 1002$s .= " // Interrupt vector location";
  if(
$point == 1010$s .= " // register index";
  if(
$point == 1030$s .="              // add zeropage-index";
  if(
$point == 1050$s .="       // absolute address";
  if(
$point == 1062$s .="// indirect w/ page wrap";
  if(
$point == 1070$s .= " // abs. load: extra misread when cross-page";
  if(
$point == 1080$s .=  "// abs. store: always issue a misread";
  
#if($point == 1090) $s .= "         // add absolute-index";

  
if($point == 1100$s .= " // Many operations take A or X as operand. Some try in";
  if(
$point == 1101$s .= " // error to take both; the outcome is an AND operation.";
  
  
#if($point == 1101) $s .= " // stx,dex,inx,txa,cpx,txs,many unofficial opcodes";
  
if($point == 1102$s .= " // sty,dey,iny,tya,cpy";
  if(
$point == 1103$s .= " // tsx, las";
  if(
$point == 1104$s .= "// php, flag test/set/clear, interrupts";

  if(
$point == 1120$s .= "        // save as second operand";
  if(
$point == 1130$s .= " // memory operand";
  if(
$point == 1140$s .="   // immediate operand";

  if(
$point == 1150$s .= " // bit";
  if(
$point == 1170$s .= "       // rol,rla, ror,rra,arr";
  if(
$point == 1180$s .= " // rol,rla, asl,slo,[arr,anc]";
  if(
$point == 1181$s .= " // lsr,sre, ror,rra,asr";
  if(
$point == 1240$s .="  // dec,dex,dey,dcp";
  if(
$point == 1250$s .="  // inc,inx,iny,isb";
  if(
$point == 1262$s .= " // [shx,shy,shs,sha?]";
  if(
$point == 1450$s .= "                        // pla,plp,rti";
  if(
$point == 1460$s .= " // rti,rts";
  if(
$point == 1470$s .="  // rts";
  if(
$point == 1480$s .="      // jsr, interrupts";
  if(
$point == 1490$s .=" // jmp, jsr, interrupts";
  if(
$point == 1495$s .="   // pha, php, interrupts";


  if(
$point == 1590$s .=  " // sbc, isb,      clear flag";
  if(
$point == 1600$s .= "  // ora, slo,      set flag";
  if(
$point == 1603$s .= "  // and, bit, rla, clear/test flag";
  if(
$point == 1606$s .= "  // eor, sre";
  if(
$point == 1670$s .= " // cmp,cpx,cpy, dcp, sbx";
  if(
$point == 1710$s .= " // ldx, dex, tax, inx, tsx,lax,las,sbx";
  if(
$point == 1720$s .= " // ldy, dey, tay, iny";
  if(
$point == 1730$s .= " // txs, las, shs";
  if(
$point == 1740$s .= " // plp, rti, flag set/clear";

  
#if($point == 1120) $s .= " // sbx, cmp, dcp, cpx, cpy";
  #if($point == 1800) $s .= " // sbx, cmp, dcp, cpx, cpy";
  #if($point == 1800) $s .= " // sbx, cmp, dcp";
  #if($point == 1810) $s .= " // cpx";
  #if($point == 1820) $s .= " // cpy";
  
if($point == 1299$s .= " // nop,flag ops,inc,dec,shifts,stack,transregister,interrupts";

  if(
$point == 1882$s .= "         // [arr]";

  
$s .= "\n";
  
  if(
$point == 1000$r .= "        /* Decode address operand */\n";
  if(
$point == 1100$r .= "        /* Load source operand */\n";
  if(
$point == 1150$r .= "        /* Operations that mogrify memory operands directly */\n";
  if(
$point == 1261$r .= "        /* Store modified value (memory) */\n";
  if(
$point == 1450$r .= "        /* Stack operations and unconditional jumps */\n";
  if(
$point == 1550$r .= "        /* Bitmasks */\n";
  if(
$point == 1610$r .= "        /* Conditional branches */\n";
  if(
$point == 1660$r .= "        /* Addition and subtraction */\n";
  if(
$point == 1700$r .= "        /* Store modified value (register) */\n";
  if(
$point == 1880$r .= "        /* Generic status flag updates */\n";
  if(
$point == 1299$r .= "        /* Some operations used up one clock cycle that we did not account for yet */\n";

  
$r .= $s;
  
  if(isset(
$plan_desc_modes[$point]))
    
fprintf($f"// $point Addressing modes: %s\n",
          
join(', '$plan_desc_modes[$point]));
  if(isset(
$plan_desc_ops[$point]))
    
fprintf($f"// $point Opcodes: %s\n",
          
join(', '$plan_desc_ops[$point]));
  
$n count($ops);
  
fprintf($f"// $n opcodes\n");

  
fwrite($f$s);
}

print 
$r;

function 
maskis($mask$value)
{
  return 
sprintf('i&0x%02X%02X'$mask$value);
}
function 
masknot($mask$value)
{
  if(
$value == 0) return sprintf('op&0x%02X'$mask);
  return 
sprintf('i^0x%02X%02X'$mask$value);
}
function 
maskislt($mask$value)
{
  return 
sprintf('i<0x%02X%02X'$mask$value);
}
function 
maskisgt($mask$value)
{
  return 
sprintf('i>0x%02X%02X'$mask$value);
}
function 
maskisle($mask$value)
{
  if((
$value&0xFF)==0xFF) return '$$$$$';
  return 
maskislt($mask$value+1);
}
function 
maskisge($mask$value)
{
  if((
$value&0xFF)==0) return '$$$$$';
  return 
maskisgt($mask$value-1);
}


function 
CreateCondition($values$included_so_far = Array())
{
  static 
$bitmasks;
  if(!
$bitmasks)
  {
    
$bitmasks = Array();
    for(
$bitmask 0x01$bitmask <= 0xFE; ++$bitmask)
      for(
$bitvalue 0x00$bitvalue <= 0xFE; ++$bitvalue)
        if( (
$bitmask $bitvalue) == $bitvalue )
          
$bitmasks[$bitmask][] = $bitvalue;
  }
  
  
/* From the following operations, choose the one that improves the solution best:
   *     && mask
   *     || mask
   *     || (missing ones)
   *     && !( negated set )
   */
  
$result '';
  
$lhs_op '&&'

  for(;;)
  {
    if(
strlen($result) > MAX_CONDITION_LENGTH)
    {
      return 
'ERROR: '.$result;
    }
    
$base_goodness 0;
    for(
$n=0$n<0x103; ++$n)
      if(isset(
$values[$n]) != isset($included_so_far[$n]))
        
$base_goodness -= 1;

    if(
$base_goodness == 0) break;
    
    
// CHOICE 1: Include everything missing
    
$choice1_str '';
    
$choice1_good$base_goodness;
    
$s = Array();
    foreach(
$values as $v)
      if(!isset(
$included_so_far[$v]))
      {
        
$s[] = sprintf('op==0x%02X'$v);
        ++
$choice1_good;
      }
    
$choice1_str join('||',$s);
    if(
$choice1_str == '' || $choice1_good $base_goodness)
      
$choice1_good = -999;
    else
      
$choice1_good = ($choice1_good $base_goodness) / strlen($choice1_str);

    
// CHOICE 2: Exclude everything that is wrong
    
$choice2_str '';
    
$choice2_good$base_goodness;
    
$s = Array();
    foreach(
$included_so_far as $v)
      if(!isset(
$values[$v]))
      {
        
$s[] = sprintf('op!=0x%02X'$v);
        ++
$choice2_good;
      }
    
$choice2_str join('&&',$s);
    if(
$choice2_str == '' || $choice2_good $base_goodness)
      
$choice2_good = -999;
    else
      
$choice2_good = ($choice2_good $base_goodness) / strlen($choice2_str);

    
// CHOICE 3: Include/limit everything below/above a certain limit
    
$choice3_str '';
    
$choice3_good$base_goodness;
    for(
$include=0$include<2; ++$include)
    for(
$below=0$below<2; ++$below)
    {
      
$test_good $base_goodness;
      for((
$below==$include) ? ($threshold=0x00)   : ($threshold=0x103);
          (
$below==$include) ? ($threshold<=0x103) : ($threshold>=0x00);
          (
$below==$include) ? (++$threshold)      : (--$threshold))
      {
        
$i = isset($included_so_far[$threshold])?1:0// already   value?
        
$s = isset($values[$threshold])?1:0;          // should-be value?

        // include,below:  // 0<n = add/keep,  >n = no change  (direction: up)
        // include,above:  // 0<n = no change, >n = add/keep   (direction: down)
        // limit,  below:  // 0<n = no change,>=n = remove     (direction: down)
        // limit,  above:  // 0<=n = remove,   >n = no change  (direction: up)

        
if($include)
        {
          
$test_good += ($i : ($s?1:-1));
        }
        else
        {
          
$test_good += ($i ? ($s?-1:1) : 0);
        }
        if(
$test_good >= $choice3_good)
        {
          
$v $include ? ($below ? ($threshold+1) : ($threshold-1)) : $threshold;
          
$choice3_good $test_good;
          
$choice3_str  sprintf('op%s0x%02X'$below?'<' '>'$v);
          
$choice3_include $include;
          
$choice3_below   $below;
          
$choice3_threshold $v;
        }
      }
    }
    if(
$choice3_str == '' || $choice3_good $base_goodness)
      
$choice3_good = -999;
    else
      
$choice3_good = ($choice3_good $base_goodness) / strlen($choice3_str);


    
$best_bitmask  0//  1 = include==,  2 = include!=  (MODE)
    
$best_bitvalue 0// -1 = exclude==, -2 = exclude!=
    
$best_bitmode  0//  3 = include< ,  4 = include>
    
$best_bitscore 0// -3 = exclude<=, -4 = exclude>=

    
for($bitmask 0x01$bitmask <= 0xFE; ++$bitmask)
      foreach(
$bitmasks[$bitmask] as $bitvalue)
      {
        
$include_goodness $base_goodness;
        
$exclude_goodness $base_goodness;
        
$includenot_goodness $base_goodness;
        
$excludenot_goodness $base_goodness;

        
$includele_goodness $base_goodness;
        
$excludele_goodness $base_goodness;
        
$includege_goodness $base_goodness;
        
$excludege_goodness $base_goodness;
        
$includelt_goodness $base_goodness;
        
$excludelt_goodness $base_goodness;
        
$includegt_goodness $base_goodness;
        
$excludegt_goodness $base_goodness;

        
$mask_str  maskis($bitmask$bitvalue);

        
// CHOICE : Include everything indicated by this mask
        // CHOICE : Exclude everything indicated by this mask
        // CHOICE : Include everything NOT indicated by this mask
        // CHOICE : Exclude everything NOT indicated by this mask
        
for($n=0$n<0x103; ++$n)
        {
          
$should = isset($values[$n]);
          
$has    = isset($included_so_far[$n]);
          if( (
$n $bitmask) == $bitvalue // Include and exclude
          
{
            if(
$has)  $exclude_goodness += $should ? -1;
            else      
$include_goodness += $should ?  : -1;
          }
          else 
//if( ($n & $bitmask) != $bitvalue ) // Include-NOT and exclude-NOT
          
{
            if(
$has)  $excludenot_goodness += $should ? -1;
            else      
$includenot_goodness += $should ?  : -1;
          }
          
/*
          if( ($n & $bitmask) <  $bitvalue ) // Include and exclude
          {
            if($has)  $excludelt_goodness += $should ? -1 : 1;
            else      $includelt_goodness += $should ?  1 : -1;
          }
          if( ($n & $bitmask) >  $bitvalue ) // Include and exclude
          {
            if($has)  $excludegt_goodness += $should ? -1 : 1;
            else      $includegt_goodness += $should ?  1 : -1;
          }
          if( ($n & $bitmask) <= $bitvalue ) // Include and exclude
          {
            if($has)  $excludele_goodness += $should ? -1 : 1;
            else      $includele_goodness += $should ?  1 : -1;
          }
          if( ($n & $bitmask) >= $bitvalue ) // Include and exclude
          {
            if($has)  $excludege_goodness += $should ? -1 : 1;
            else      $includege_goodness += $should ?  1 : -1;
          }
          */
        
}

        
$i = ($include_goodness $base_goodness) / strlen($mask_str);
        if(
$i $best_bitscore || ($i==$best_bitscore && abs($best_bitmode)>1))
        {
          
$best_bitscore $i;
          
$best_bitmask $bitmask;
          
$best_bitvalue $bitvalue;
          
$best_bitmode 1;
        }
        
$i = ($exclude_goodness $base_goodness) / strlen($mask_str);
        if(
$i $best_bitscore || ($i==$best_bitscore && abs($best_bitmode)>1))
        {
          
$best_bitscore $i;
          
$best_bitmask $bitmask;
          
$best_bitvalue $bitvalue;
          
$best_bitmode = -1;
        }
        
$i = ($includenot_goodness $base_goodness) / strlen($mask_str);
        if(
$i $best_bitscore || ($i==$best_bitscore && abs($best_bitmode)>2))
        {
          
$best_bitscore $i;
          
$best_bitmask $bitmask;
          
$best_bitvalue $bitvalue;
          
$best_bitmode 2;
        }
        
$i = ($excludenot_goodness $base_goodness) / strlen($mask_str);
        if(
$i $best_bitscore || ($i==$best_bitscore && abs($best_bitmode)>2))
        {
          
$best_bitscore $i;
          
$best_bitmask $bitmask;
          
$best_bitvalue $bitvalue;
          
$best_bitmode = -2;
        }
        
/*
        $i = ($includelt_goodness - $base_goodness) / strlen($mask_str);
        if($i > $best_bitscore)
        {
          $best_bitscore = $i;
          $best_bitmask = $bitmask;
          $best_bitvalue = $bitvalue;
          $best_bitmode = 3;
        }
        $i = ($includegt_goodness - $base_goodness) / strlen($mask_str);
        if($i > $best_bitscore)
        {
          $best_bitscore = $i;
          $best_bitmask = $bitmask;
          $best_bitvalue = $bitvalue;
          $best_bitmode =  4;
        }
        $i = ($excludege_goodness - $base_goodness) / strlen($mask_str);
        if($i > $best_bitscore)
        {
          $best_bitscore = $i;
          $best_bitmask = $bitmask;
          $best_bitvalue = $bitvalue;
          $best_bitmode = -3;
        }
        $i = ($excludele_goodness - $base_goodness) / strlen($mask_str);
        if($i > $best_bitscore)
        {
          $best_bitscore = $i;
          $best_bitmask = $bitmask;
          $best_bitvalue = $bitvalue;
          $best_bitmode = -4;
        }




        $i = ($includele_goodness - $base_goodness) / strlen($mask_str);
        if($i > $best_bitscore)
        {
          $best_bitscore = $i;
          $best_bitmask = $bitmask;
          $best_bitvalue = $bitvalue;
          $best_bitmode = 5;
        }
        $i = ($includege_goodness - $base_goodness) / strlen($mask_str);
        if($i > $best_bitscore)
        {
          $best_bitscore = $i;
          $best_bitmask = $bitmask;
          $best_bitvalue = $bitvalue;
          $best_bitmode =  6;
        }
        $i = ($excludegt_goodness - $base_goodness) / strlen($mask_str);
        if($i > $best_bitscore)
        {
          $best_bitscore = $i;
          $best_bitmask = $bitmask;
          $best_bitvalue = $bitvalue;
          $best_bitmode = -5;
        }
        $i = ($excludelt_goodness - $base_goodness) / strlen($mask_str);
        if($i > $best_bitscore)
        {
          $best_bitscore = $i;
          $best_bitmask = $bitmask;
          $best_bitvalue = $bitvalue;
          $best_bitmode = -6;
        }
        */
      
}

    
$best_goodness 0;
    
$choose 0;
    if(
$choice1_good $best_goodness)
      { 
$best_goodness $choice1_good$choose 'c1'; }
    if(
$choice2_good $best_goodness)
      { 
$best_goodness $choice2_good$choose 'c2'; }
    if(
$choice3_good $best_goodness)
      { 
$best_goodness $choice3_good$choose 'c3'; }
    if(
$best_bitmode != && $best_bitscore $best_goodness)
      { 
$best_goodness $best_bitscore$choose 'b'; }

    
$join_op  '&&';
    
$join_str '';
    
$str2_op  '&&';

    switch(
$choose)
    {
      case 
'b':
      {
        switch(
$best_bitmode)
        {
          case 
1// include
          
{
            for(
$n=0$n<0x103; ++$n)
              if( (
$n $best_bitmask) == $best_bitvalue )
                
$included_so_far[$n] = $n;

            
$join_op '||';
            
$join_str maskis($best_bitmask$best_bitvalue);
            break;
          }
          case -
1// exclude
          
{
            for(
$n=0$n<0x103; ++$n)
              if( (
$n $best_bitmask) == $best_bitvalue )
                unset(
$included_so_far[$n]);

            
$join_op '&&';
            
$join_str masknot($best_bitmask$best_bitvalue);
            break;
          }
          case 
2// include-not
          
{
            for(
$n=0$n<0x103; ++$n)
              if( (
$n $best_bitmask) != $best_bitvalue )
                
$included_so_far[$n] = $n;

            
$join_op '||';
            
$join_str masknot($best_bitmask$best_bitvalue);
            break;
          }
          case -
2// exclude-not
          
{
            for(
$n=0$n<0x103; ++$n)
              if( (
$n $best_bitmask) != $best_bitvalue )
                unset(
$included_so_far[$n]);

            
$join_op '&&';
            
$join_str maskis($best_bitmask$best_bitvalue);
            break;
          }


          case 
3// include<
          
{
            for(
$n=0$n<0x103; ++$n)
              if( (
$n $best_bitmask) < $best_bitvalue )
                
$included_so_far[$n] = $n;

            
$join_op '||';
            
$join_str maskislt($best_bitmask$best_bitvalue);
            break;
          }
          case 
4// include>
          
{
            for(
$n=0$n<0x103; ++$n)
              if( (
$n $best_bitmask) > $best_bitvalue )
                
$included_so_far[$n] = $n;

            
$join_op '||';
            
$join_str maskisgt($best_bitmask$best_bitvalue);
            break;
          }
          case -
3// exclude>=
          
{
            for(
$n=0$n<0x103; ++$n)
              if( (
$n $best_bitmask) >= $best_bitvalue )
                unset(
$included_so_far[$n]);

            
$join_op '&&';
            
$join_str maskislt($best_bitmask$best_bitvalue);
            break;
          }
          case -
4// exclude<=
          
{
            for(
$n=0$n<0x103; ++$n)
              if( (
$n $best_bitmask) <= $best_bitvalue )
                unset(
$included_so_far[$n]);

            
$join_op '&&';
            
$join_str maskisgt($best_bitmask$best_bitvalue);
            break;
          }


          case 
5// include<=
          
{
            for(
$n=0$n<0x103; ++$n)
              if( (
$n $best_bitmask) <= $best_bitvalue )
                
$included_so_far[$n] = $n;

            
$join_op '||';
            
$join_str maskisle($best_bitmask$best_bitvalue);
            break;
          }
          case 
6// include>=
          
{
            for(
$n=0$n<0x103; ++$n)
              if( (
$n $best_bitmask) >= $best_bitvalue )
                
$included_so_far[$n] = $n;

            
$join_op '||';
            
$join_str maskisge($best_bitmask$best_bitvalue);
            break;
          }
          case -
5// exclude>
          
{
            for(
$n=0$n<0x103; ++$n)
              if( (
$n $best_bitmask) > $best_bitvalue )
                unset(
$included_so_far[$n]);

            
$join_op '&&';
            
$join_str maskisle($best_bitmask$best_bitvalue);
            break;
          }
          case -
6// exclude<
          
{
            for(
$n=0$n<0x103; ++$n)
              if( (
$n $best_bitmask) < $best_bitvalue )
                unset(
$included_so_far[$n]);

            
$join_op '&&';
            
$join_str maskisge($best_bitmask$best_bitvalue);
            break;
          }

        }
        break;
      }
      case 
'c1':
      {
        foreach(
$values as $v)
          
$included_so_far[$v] = $v;
        
        
$join_op  '||';
        
$join_str $choice1_str;
        
$str2_op  '||';
        break;
      }
      case 
'c2':
      {
        foreach(
$included_so_far as $v)
          if(!isset(
$values[$v]))
            unset(
$included_so_far[$v]);
        
        
$join_op '&&';
        
$join_str $choice2_str;
        break;
      }
      case 
'c3':
      {
        if(
$choice3_include)
        {
          if(
$choice3_below)
            for(
$v=0$v<$choice3_threshold; ++$v)
              
$included_so_far[$v] = $v;
          else
            for(
$v=$choice3_threshold+1$v<=0x103; ++$v)
              
$included_so_far[$v] = $v;
          
$join_op  '||';
          
$join_str $choice3_str;
          
$str2_op  '||';
        }
        else 
// limit
        
{
          if(
$choice3_below// limit to <, remove >=
            
for($v=$choice3_threshold$v<=0x103; ++$v)
              unset(
$included_so_far[$v]);
          else 
// limit to >, remove <=
            
for($v=0$v<=$choice3_threshold; ++$v)
              unset(
$included_so_far[$v]);
          
$join_op '&&';
          
$join_str $choice3_str;
        }
        break;
      }
    }
    
#print "base($base_goodness)chosen($choose)join_op($join_op)join_str($join_str)now($result)\n";
    #flush();

    
if(empty($result))
    {
      
$result $join_str;
      
$lhs_op '&&';
    }
    else
    {
      if(
$join_op == '||')
      {
        
$result "$result || $join_str";
        if(
$str2_op == '||')
          
$lhs_op '||';
      }
      else
      {
        if(
$lhs_op == '||')
          
$result "($result)";
        
$result "$result && $join_str";
        
$lhs_op '&&';
      }
    }
  }
  return 
$result;
}