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*/;
20 import iv
.olly
.disasm2
;
23 // ////////////////////////////////////////////////////////////////////////// //
27 uint line
; // index in lines
28 uint addr
; // 0: undefined
36 uint addr
; // 0: undefined
38 ubyte[] databuf
; // for "db" and such
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;
55 uint findLabelAddr (const(char)[] name
) {
56 foreach (const ref Label lbl
; labels
) {
57 if (name
== lbl
.name
) {
59 needMorePasses
= true; // undefined label used, need more passes
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
73 //{ import std.stdio; writefln("label '%s' at 0x%08x", lbl.name, addr); }
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
; }
88 if (!assembled
) assemble();
90 foreach (const ref Line ln
; lines
) len
+= ln
.am
.length
;
91 auto res
= new ubyte[](len
);
93 foreach (const ref Line ln
; lines
) {
94 if (ln
.databuf
.length
) {
95 res
[len
..len
+ln
.am
.length
] = ln
.databuf
[];
97 res
[len
..len
+ln
.am
.length
] = ln
.am
.code
[0..ln
.am
.length
];
101 assert(len
== res
.length
);
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");
112 lbl
.name
= name
.idup
;
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");
120 lbl
.line
= cast(uint)lines
.length
;
122 lbl
.name
= name
.idup
;
126 bool hasLabel (const(char)[] name
) {
127 foreach (const ref lbl
; labels
) if (lbl
.name
== name
) return true;
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) {
140 while (ep
< s
.length
&& s
[ep
] != '\n') ++ep
;
143 if (ep
>= s
.length
) break;
149 private void addLine(T
: const(char)[]) (T s
) {
151 static if (!is(T
== typeof(null))) {
152 import std
.ascii
: isAlpha
, isAlphaNum
;
154 while (xs
.length
> 0 && xs
[0] <= ' ') xs
= xs
[1..$];
155 if (xs
.length
== 0) return;
156 if (xs
[0] == ';') return;
158 if (isAlpha(xs
[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");
168 //{ import std.stdio; writeln("found label: [", ln, "]"); }
169 lbl
.line
= cast(uint)lines
.length
;
173 if (++pos
< xs
.length
) addLine(xs
[pos
..$]); // recursive, so allow many labels
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
);
187 // curpc is current pc ;-)
188 private void asmLine (uint lidx
) {
189 // try to assemble it
194 char[256] errtext
= 0;
195 char[256] lasterrtext
= 0;
196 int lasterrpos
= 1; // <=0: was error
197 auto line
= &lines
[lidx
];
200 if (curpc
== 0) throw new Exception("invalid pc");
201 fixLabelAddr(lidx
, curpc
);
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
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
; });
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) {
226 lasterrtext
[] = errtext
[];
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;
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;
246 line
.am
= lastgoodam
;
247 line
.databuf
= dbdata
;
248 curpc
+= lastgoodam
.length
;
251 private void asmPass () {
252 needMorePasses
= false;
254 foreach (uint lidx
; 0..cast(uint)lines
.length
) asmLine(lidx
);
257 // we finished parsing, now assemble it
259 if (assembled
) return;
260 bool firstPass
= true;
263 // check if we still have undefined labels after first pass
265 foreach (const ref Label lbl
; labels
) if (lbl
.addr
== 0) throw new Exception("undefined label '"~lbl
.name
~"'");
268 if (!needMorePasses
) break;
272 uint dasmOne (const(void)[] code
, uint ip
, uint ofs
) {
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
);
287 fprintf(stderr
, "0x%08x: %02x%-14s db\t0x%02x\n", ip
+ofs
, b
, "".ptr
, b
);
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
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
);
306 void disasmCode (const(ubyte)[] code
, uint orgpc
) {
308 while (ofs
< code
.length
) ofs
+= dasmOne(code
, orgpc
, ofs
);