NTSC Box -- experiments with aggressive optimizations
[ScratchNES.git] / src / CPU / build-cpu.js
1 /* build-cpu.js
2 * this is the heart of the metaprogram
3 * at the entrypoint, the instruction set is complete and annotated,
4 * and the reference document is available.
5 * We can just sit back, relax, and put together parts :-)
6 */
7
8 var fs = require("fs");
9
10 // avoid excessive disk use
11 var instruction_cache = {};
12 var addressing_cache = {};
13
14 // read the table in
15 var table = JSON.parse(fs.readFileSync("bin/table.json").toString());
16
17 // jump table emission, etc.
18 var emission = [
19 "mapper read PC",
20 "set opcode to M"
21 ];
22
23 var sources = table.map(function(x, i) {
24 if(x) {
25 var instruction = [];
26
27 // add stub for addressing mode
28 // load if not already in-memory
29
30 if(!addressing_cache[x.addressing]) {
31 addressing_cache[x.addressing] =
32 fs.readFileSync("addressing/" + x.addressing)
33 .toString().trim().split("\n");
34
35 if(addressing_cache[x.addressing][0].trim().length == 0)
36 addressing_cache[x.addressing] = [];
37 }
38
39 instruction = instruction.concat(addressing_cache[x.addressing]);
40
41 // add stub for instruction
42 if(!instruction_cache[x.name]) {
43 instruction_cache[x.name] =
44 fs.readFileSync("instructions/" + x.name)
45 .toString().trim().split("\n");
46 }
47
48 // follow the flags
49 var flags = instruction_cache[x.name][0].replace(/ /g, '').split(',');
50 var negQ = false, zeroQ = false, carryQ = false, mode = null, operand = null;
51
52 flags.forEach(function(flag) {
53 if(flag == "N") negQ = true;
54 else if(flag == "Z") zeroQ = true;
55 else if(flag == "C") carryQ = true;
56 else if(["R", "RW", "IMPLIED", "RAW", "BRANCH", "W"].indexOf(flag) > -1)
57 mode = flag;
58 else if (["A", "X", "Y", "tmp", "OP"].indexOf(flag) > -1)
59 operand = flag;
60 else
61 console.error("Unknown flag " + flag + " for instruction " + x.name);
62 });
63
64 var ins = instruction_cache[x.name].slice(1);
65
66 if(mode == "R") {
67 instruction.push("mapper read address");
68 instruction.push("set OP to M");
69 } else if(mode == "RW") {
70 if(x.addressing == "accumulator") {
71 operand = "A";
72 ins = ins.map(function(q) {
73 return q.replace(/OP/g, "A");
74 });
75 } else {
76 instruction.push("mapper read address");
77 instruction.push("set OP to M");
78 }
79
80 ins = ins.map(function(q) {
81 if(x.addressing != "accumulator") {
82 return q.replace(/set OP to/, "mapper write address");
83 } else {
84 return q;
85 }
86 });
87 } else if(mode == "W") {
88 ins = ins.map(function(q) {
89 return q.replace(/set OP to/, "mapper write address");
90 });
91 } else if(mode == "RAW") {
92 ins = ins.map(function(q) {
93 return q.replace(/OP/g, "address");
94 });
95 } else if(mode == "IMPLIED" || mode == "BRANCH") {
96
97 } else {
98 console.error("Unsupported mode " + mode);
99 }
100
101 // add the actual code of the instruction
102 instruction = instruction.concat(ins);
103
104 if(negQ) {
105 instruction.push("set flagN to <" + operand + " > 127>");
106 }
107
108 if(zeroQ) {
109 instruction.push("set flagZ to <" + operand + " = 0>");
110 }
111
112 if(carryQ) {
113 instruction.push("set flagC to <" + operand + " > 255 or " + operand + " < 0>");
114 }
115
116 instruction.push('say "' + x.assembler + '"');
117
118 if(x.name != "JMP" && x.name != "JSR") {
119 instruction.push("change PC by " + x.size);
120 }
121
122 // cycle count
123 if(mode == "BRANCH") {
124 // TODO: skip
125 instruction.push("change cycles by 3");
126 } else {
127 if(x.cycles.length == 2) {
128 // TODO: skip a cycle if needed
129 instruction.push("change cycles by " + x.cycles[0]);
130 } else {
131 instruction.push("change cycles by " + x.cycles);
132 }
133 }
134
135 return instruction.join("\n");
136 } else {
137 return [
138 'say "Illegal Opcode ' + i + ' used, ignoring." for 2 secs',
139 'change PC by 1'
140 ].join("\n");
141 }
142 });
143
144 // dump out an 8 level deep BST
145 console.log(bst(sources, 0, 255).join('\n'));
146
147 function bst(sources, start, end) {
148 if(start == end)
149 return [sources[start]];
150
151 if(start + 1 == end)
152 return [
153 "if opcode = " + start + " then",
154 sources[start],
155 "else",
156 sources[end],
157 "end"
158 ];
159
160 var emission = ["if opcode < " + (start+end+1)/2 + " then"]
161 .concat(bst(sources, start, start + (end-start-1) / 2))
162 .concat(["else"])
163 .concat(bst(sources, start + (end-start+1) / 2, end))
164 .concat(["end"]);
165
166 return emission;
167 }
This page took 0.092733 seconds and 4 git commands to generate.