1 module gbaid.gba.instable; 2 3 import std.range : iota; 4 import std.traits : hasUDA; 5 import std.meta : aliasSeqOf; 6 import std.algorithm.searching : count; 7 import std..string : format; 8 9 import gbaid.util; 10 11 public import gbaid.gba.register; 12 public import gbaid.gba.memory; 13 14 public alias Executor = void function(Registers*, MemoryBus*, int); 15 16 public Executor[] createTable(alias nullInstruction)(int bitCount) { 17 auto table = new Executor[1 << bitCount]; 18 foreach (i, t; table) { 19 table[i] = &nullInstruction; 20 } 21 return table; 22 } 23 24 public void addSubTable(string bits, alias instructionFamily, alias nullInstruction)(Executor[] table) { 25 // Generate the subtable 26 auto subTable = createTable!(instructionFamily, bits.count('t'), nullInstruction)(); 27 // Check that there are as many bits as in the table length 28 int bitCount = cast(int) bits.length; 29 if (1 << bitCount != table.length) { 30 throw new Exception("Wrong number of bits"); 31 } 32 // Count don't cares and table bits (and validate bit types) 33 int dontCareCount = 0; 34 int tableBitCount = 0; 35 foreach (b; bits) { 36 switch (b) { 37 case '0': 38 case '1': 39 break; 40 case 'd': 41 dontCareCount++; 42 break; 43 case 't': 44 tableBitCount++; 45 break; 46 default: 47 throw new Exception("Unknown bit type: '" ~ b ~ "'"); 48 } 49 } 50 if (1 << tableBitCount != subTable.length) { 51 throw new Exception("Wrong number of sub-table bits"); 52 } 53 // Enumerate combinations generated by the bit string 54 // 0 and 1 literals, d for don't care, t for table 55 // Start with all the 1 literals, which won't change 56 int fixed = 0; 57 foreach (int i, b; bits) { 58 if (b == '1') { 59 fixed.setBit(bitCount - 1 - i, 1); 60 } 61 } 62 // Now for every combination of don't cares create an 63 // intermediary value, which is only missing table bits 64 foreach (dontCareValue; 0 .. 1 << dontCareCount) { 65 int intermediary = fixed; 66 int dc = dontCareCount - 1; 67 foreach (int i, b; bits) { 68 if (b == 'd') { 69 intermediary.setBit(bitCount - 1 - i, dontCareValue.getBit(dc)); 70 dc--; 71 } 72 } 73 // Now for every combination of table bits create the 74 // final value and assign the pointer in the table 75 foreach (tableBitValue; 0 .. 1 << tableBitCount) { 76 // Ignore null instructions 77 if (subTable[tableBitValue] is &nullInstruction) { 78 continue; 79 } 80 int index = intermediary; 81 int tc = tableBitCount - 1; 82 foreach (int i, b; bits) { 83 if (b == 't') { 84 index.setBit(bitCount - 1 - i, tableBitValue.getBit(tc)); 85 tc--; 86 } 87 } 88 // Check if there's a conflict first 89 if (table[index] !is &nullInstruction) { 90 throw new Exception(format("The entry at index %d in sub-table with bits \"%s\" conflicts with " 91 ~ "a previously added one", tableBitValue, bits)); 92 } 93 table[index] = subTable[tableBitValue]; 94 } 95 } 96 } 97 98 private Executor[] createTable(alias instructionFamily, int bitCount, alias unsupported)() { 99 static if (bitCount == 0) { 100 Executor[] table = [&instructionFamily!()]; 101 } else { 102 Executor[] table; 103 foreach (i; aliasSeqOf!(iota(0, 1 << bitCount))) { 104 static if (hasUDA!(instructionFamily!i, "unsupported")) { 105 table ~= [&unsupported]; 106 } else { 107 table ~= [&instructionFamily!i]; 108 } 109 } 110 } 111 return table; 112 }