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 }