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