sdpygfx: cosmetix
[iv.d.git] / olly / assembler.d
blobe7ff8d8708183011dd5fd876fcdc5ed78c3d9cf6
1 /* Written by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, version 3 of the License ONLY.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 module iv.olly.assembler /*is aliced*/;
18 import iv.alice;
19 import iv.olly.asm1;
20 import iv.olly.disasm2;
23 // ////////////////////////////////////////////////////////////////////////// //
24 class Assembler {
25 private:
26 static struct Label {
27 uint line; // index in lines
28 uint addr; // 0: undefined
29 string name;
31 Label[] labels;
33 static struct Line {
34 string str;
35 uint srclnum;
36 uint addr; // 0: undefined
37 AsmModel am;
38 ubyte[] databuf; // for "db" and such
40 Line[] lines;
42 uint startpc;
43 uint curpc;
44 bool needMorePasses;
45 bool assembled;
46 uint linesSeen;
48 bool isDataByte (uint addr) {
49 foreach (const ref Line ln; lines) {
50 if (ln.am.data && addr >= ln.addr && addr < ln.addr+ln.am.length) return true;
52 return false;
55 uint findLabelAddr (const(char)[] name) {
56 foreach (const ref Label lbl; labels) {
57 if (name == lbl.name) {
58 if (lbl.addr == 0) {
59 needMorePasses = true; // undefined label used, need more passes
60 return curpc;
62 return lbl.addr;
65 throw new Exception("undefined label");
68 void fixLabelAddr (uint lidx, uint addr) {
69 foreach (ref Label lbl; labels) {
70 if (lidx == lbl.line) {
71 if (lbl.addr != addr) needMorePasses = true; // label address changed, need more passes
72 lbl.addr = addr;
73 //{ import std.stdio; writefln("label '%s' at 0x%08x", lbl.name, addr); }
78 public:
79 this (uint stpc) {
80 if (stpc == 0) throw new Exception("invalid starting PC");
81 startpc = curpc = stpc;
84 @property uint orgpc () const pure nothrow @safe @nogc { return startpc; }
85 @property uint pc () const pure nothrow @safe @nogc { return curpc; }
87 ubyte[] getCode () {
88 if (!assembled) assemble();
89 uint len = 0;
90 foreach (const ref Line ln; lines) len += ln.am.length;
91 auto res = new ubyte[](len);
92 len = 0;
93 foreach (const ref Line ln; lines) {
94 if (ln.databuf.length) {
95 res[len..len+ln.am.length] = ln.databuf[];
96 } else {
97 res[len..len+ln.am.length] = ln.am.code[0..ln.am.length];
99 len += ln.am.length;
101 assert(len == res.length);
102 return res;
105 void addLabel (const(char)[] name, uint addr) {
106 if (addr == 0) throw new Exception("invalid label address");
107 if (name.length == 0) throw new Exception("invalid label name");
108 foreach (const ref lbl; labels) if (lbl.name == name) throw new Exception("label '"~lbl.name~"' already defined");
109 Label lbl;
110 lbl.line = uint.max;
111 lbl.addr = addr;
112 lbl.name = name.idup;
113 labels ~= lbl;
116 void addLabelHere (const(char)[] name) {
117 if (name.length == 0) throw new Exception("invalid label name");
118 foreach (const ref lbl; labels) if (lbl.name == name) throw new Exception("label '"~lbl.name~"' already defined");
119 Label lbl;
120 lbl.line = cast(uint)lines.length;
121 lbl.addr = 0;
122 lbl.name = name.idup;
123 labels ~= lbl;
126 bool hasLabel (const(char)[] name) {
127 foreach (const ref lbl; labels) if (lbl.name == name) return true;
128 return false;
131 uint labelAddr (const(char)[] name) {
132 foreach (const ref lbl; labels) if (lbl.name == name) return lbl.addr;
133 throw new Exception("label '"~name.idup~"' not found");
136 void addLines(T : const(char)[]) (T s) {
137 static if (!is(T == typeof(null))) {
138 while (s.length > 0) {
139 uint ep = 0;
140 while (ep < s.length && s[ep] != '\n') ++ep;
141 addLine(s[0..ep]);
142 ++ep;
143 if (ep >= s.length) break;
144 s = s[ep..$];
149 private void addLine(T : const(char)[]) (T s) {
150 ++linesSeen;
151 static if (!is(T == typeof(null))) {
152 import std.ascii : isAlpha, isAlphaNum;
153 auto xs = s;
154 while (xs.length > 0 && xs[0] <= ' ') xs = xs[1..$];
155 if (xs.length == 0) return;
156 if (xs[0] == ';') return;
157 // is this a label?
158 if (isAlpha(xs[0])) {
159 uint pos = 0;
160 while (pos < xs.length && isAlphaNum(xs[pos])) ++pos;
161 auto ln = xs[0..pos];
162 while (pos < xs.length && xs[pos] <= ' ') ++pos;
163 if (pos < xs.length && xs[pos] == ':') {
164 foreach (const ref lbl; labels) {
165 if (lbl.name == ln) throw new Exception("label '"~lbl.name~"' already defined");
167 Label lbl;
168 //{ import std.stdio; writeln("found label: [", ln, "]"); }
169 lbl.line = cast(uint)lines.length;
170 lbl.addr = 0;
171 lbl.name = ln.idup;
172 labels ~= lbl;
173 if (++pos < xs.length) addLine(xs[pos..$]); // recursive, so allow many labels
174 assembled = false;
175 return;
178 // save it
179 if (lines.length >= int.max/2) throw new Exception("too many code lines");
180 static if (is(T == string)) alias ss = s; else string ss = s.idup;
181 //{ import std.stdio; writeln("*[", ss, "]"); }
182 lines ~= Line(ss, linesSeen);
183 assembled = false;
187 // curpc is current pc ;-)
188 private void asmLine (uint lidx) {
189 // try to assemble it
190 AsmModel lastgoodam;
191 AsmModel am;
192 AsmOptions opts;
193 opts.ideal = true;
194 char[256] errtext = 0;
195 char[256] lasterrtext = 0;
196 int lasterrpos = 1; // <=0: was error
197 auto line = &lines[lidx];
198 ubyte[] dbdata;
200 if (curpc == 0) throw new Exception("invalid pc");
201 fixLabelAddr(lidx, curpc);
203 void throwError () {
204 if (lasterrpos > 0) return;
205 import core.stdc.stdio : stderr, fprintf;
206 fprintf(stderr, "ERROR at line %u: %s\n", line.srclnum, lasterrtext.ptr);
207 fprintf(stderr, "%.*s\n", cast(uint)line.str.length, line.str.ptr);
208 foreach (immutable _; 0..-lasterrpos-1) fprintf(stderr, "^");
209 fprintf(stderr, "^\n");
210 throw new Exception("asm error");
213 // try all possible variants, choose shorter one
214 int attempt = 0;
215 lastgoodam.length = lastgoodam.length.max;
216 mainloop: for (;; ++attempt) {
217 // try each operand size
218 int errcount = 0; // all 4 sizes gives an error? we are done
219 foreach (immutable int csize; 0..4) {
220 auto cmdlen = .assemble(line.str, curpc, &am, opts, attempt, csize, errtext[], &findLabelAddr, (uint addr, ubyte b) { dbdata ~= b; });
221 if (cmdlen <= 0) {
222 // error; if we have no previous command, and no previous error, register this one
223 // we are using the fact that 0th attempt with 0th operand size should always produce an instruction
224 if (lastgoodam.length == lastgoodam.length.max && lasterrpos > 0) {
225 lasterrpos = cmdlen;
226 lasterrtext[] = errtext[];
228 ++errcount;
229 continue;
231 // if this is data command, don't go further
232 if (am.data) { lastgoodam = am; break mainloop; }
233 // good command, check if we should save it
234 if (am.length >= lastgoodam.length) continue;
235 // i found her!
236 lastgoodam = am;
238 if (errcount == 4) break; // all 4 operand sizes gives us error, so no more commands can be found
240 throwError(); // if any
241 // if command size of address was changed, we need yet another pass to stabilize the things
242 if (line.addr != 0) {
243 if (line.am.length != lastgoodam.length || line.addr != curpc) needMorePasses = true;
245 line.addr = curpc;
246 line.am = lastgoodam;
247 line.databuf = dbdata;
248 curpc += lastgoodam.length;
251 private void asmPass () {
252 needMorePasses = false;
253 curpc = startpc;
254 foreach (uint lidx; 0..cast(uint)lines.length) asmLine(lidx);
257 // we finished parsing, now assemble it
258 void assemble () {
259 if (assembled) return;
260 bool firstPass = true;
261 for (;;) {
262 asmPass();
263 // check if we still have undefined labels after first pass
264 if (firstPass) {
265 foreach (const ref Label lbl; labels) if (lbl.addr == 0) throw new Exception("undefined label '"~lbl.name~"'");
266 firstPass = false;
268 if (!needMorePasses) break;
272 uint dasmOne (const(void)[] code, uint ip, uint ofs) {
273 // put labels
274 foreach (const ref Label lbl; labels) {
275 if (lbl.addr == ip+ofs) {
276 import core.stdc.stdio : stderr, fprintf;
277 fprintf(stderr, "0x%08x: %.*s:\n", ip+ofs, cast(uint)lbl.name.length, lbl.name.ptr);
281 if (isDataByte(ip+ofs)) {
282 import core.stdc.stdio : stderr, fprintf;
283 ubyte b = (cast(const(ubyte)[])code)[ofs];
284 if (b >= ' ' && b < 127) {
285 fprintf(stderr, "0x%08x: %02x%-14s db\t0x%02x ; '%c'\n", ip+ofs, b, "".ptr, b, b);
286 } else {
287 fprintf(stderr, "0x%08x: %02x%-14s db\t0x%02x\n", ip+ofs, b, "".ptr, b);
289 return 1;
291 DisasmData da;
292 DAConfig cfg;
293 cfg.tabarguments = true;
294 auto len = disasm(code[ofs..$], ip+ofs, &da, DA_DUMP|DA_TEXT|DA_HILITE, &cfg, (uint addr) {
295 foreach (const ref lbl; labels) if (lbl.addr == addr) return /*cast(const(char)[])*/lbl.name; // it is safe to cast here
296 return null;
298 if (len == 0) throw new Exception("ERROR: "~disErrMessage(da.errors, da.warnings));
300 import core.stdc.stdio : stderr, fprintf;
301 fprintf(stderr, "0x%08x: %-16s %s\n", da.ip, da.dump.ptr, da.result.ptr);
303 return da.size;
306 void disasmCode (const(ubyte)[] code, uint orgpc) {
307 uint ofs = 0;
308 while (ofs < code.length) ofs += dasmOne(code, orgpc, ofs);