OpenCores
URL https://opencores.org/ocsvn/lxp32/lxp32/trunk

Subversion Repositories lxp32

[/] [lxp32/] [trunk/] [tools/] [src/] [lxp32asm/] [assembler.cpp] - Blame information for rev 6

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 ring0_mipt
/*
2
 * Copyright (c) 2016 by Alex I. Kuznetsov.
3
 *
4
 * Part of the LXP32 CPU IP core.
5
 *
6
 * This module implements members of the Assembler class.
7
 */
8
 
9
#include "assembler.h"
10
#include "utils.h"
11
 
12
#include <iostream>
13
#include <fstream>
14
#include <sstream>
15
#include <stdexcept>
16
#include <utility>
17
#include <limits>
18
#include <type_traits>
19
#include <cctype>
20
#include <cassert>
21
#include <cstdlib>
22
 
23
void Assembler::processFile(const std::string &filename) {
24
        auto nativePath=Utils::normalizeSeparators(filename);
25
        auto pos=nativePath.find_last_of('/');
26
        if(pos!=std::string::npos) nativePath=filename.substr(pos+1);
27
        _obj.setName(nativePath);
28
 
29
        _line=0;
30
        _state=Initial;
31
        _currentFileName=filename;
32
        processFileRecursive(filename);
33 6 ring0_mipt
 
34 2 ring0_mipt
// Examine symbol table
35
        for(auto const &sym: _obj.symbols()) {
36
                if(sym.second.type==LinkableObject::Unknown&&!sym.second.refs.empty()) {
37
                        std::ostringstream msg;
38
                        msg<<"Undefined symbol \""+sym.first+"\"";
39
                        msg<<" (referenced from "<<sym.second.refs[0].source;
40
                        msg<<":"<<sym.second.refs[0].line<<")";
41
                        throw std::runtime_error(msg.str());
42
                }
43
        }
44 6 ring0_mipt
 
45
        for(auto const &sym: _exportedSymbols) _obj.exportSymbol(sym);
46 2 ring0_mipt
}
47
 
48
void Assembler::processFileRecursive(const std::string &filename) {
49
        std::ifstream in(filename,std::ios_base::in);
50
        if(!in) throw std::runtime_error("Cannot open file \""+filename+"\"");
51
 
52
// Process input file line-by-line
53
        auto savedLine=_line;
54
        auto savedState=_state;
55
        auto savedFileName=_currentFileName;
56
 
57
        _line=1;
58
        _state=Initial;
59
        _currentFileName=filename;
60
 
61
        std::string line;
62
        while(std::getline(in,line)) {
63
                auto tokens=tokenize(line);
64
                expand(tokens);
65
                elaborate(tokens);
66
                _line++;
67
        }
68
 
69 6 ring0_mipt
        if(_state!=Initial) throw std::runtime_error("Unexpected end of file");
70
 
71 2 ring0_mipt
        _line=savedLine;
72
        _state=savedState;
73
        _currentFileName=savedFileName;
74
 
75 6 ring0_mipt
        if(!_currentLabels.empty())
76
                throw std::runtime_error("Symbol definition must be followed by an instruction or data definition statement");
77 2 ring0_mipt
}
78
 
79
void Assembler::addIncludeSearchDir(const std::string &dir) {
80
        auto ndir=Utils::normalizeSeparators(dir);
81
        if(!ndir.empty()&&ndir.back()!='/') ndir.push_back('/');
82
        _includeSearchDirs.push_back(std::move(ndir));
83
}
84
 
85
int Assembler::line() const {
86
        return _line;
87
}
88
 
89
std::string Assembler::currentFileName() const {
90
        return _currentFileName;
91
}
92
 
93
LinkableObject &Assembler::object() {
94
        return _obj;
95
}
96
 
97
const LinkableObject &Assembler::object() const {
98
        return _obj;
99
}
100
 
101
Assembler::TokenList Assembler::tokenize(const std::string &str) {
102
        TokenList tokenList;
103
        std::string word;
104
        std::size_t i;
105
        for(i=0;i<str.size();i++) {
106
                char ch=str[i];
107
                switch(_state) {
108
                case Initial:
109
                        if(ch==' '||ch=='\t'||ch=='\n'||ch=='\r') continue; // skip whitespace
110
                        else if(ch==','||ch==':') { // separator
111
                                tokenList.push_back(std::string(1,ch));
112
                        }
113
                        else if(std::isalnum(ch)||ch=='.'||ch=='#'||ch=='_'||ch=='-'||ch=='+') {
114
                                word=std::string(1,ch);
115
                                _state=Word;
116
                        }
117
                        else if(ch=='\"') {
118
                                word="\"";
119
                                _state=StringLiteral;
120
                        }
121
                        else if(ch=='/') {
122
                                if(++i>=str.size()) throw std::runtime_error("Unexpected end of line");
123
                                ch=str[i];
124
                                if(ch=='/') i=str.size(); // skip the rest of the line
125
                                else if(ch=='*') _state=BlockComment;
126
                                else throw std::runtime_error(std::string("Unexpected character: \"")+ch+"\"");
127
                        }
128
                        else throw std::runtime_error(std::string("Unexpected character: \"")+ch+"\"");
129
                        break;
130
                case Word:
131 6 ring0_mipt
                        if(std::isalnum(ch)||ch=='_'||ch=='@'||ch=='+'||ch=='-') word+=ch;
132 2 ring0_mipt
                        else {
133
                                i--;
134
                                _state=Initial;
135
                                tokenList.push_back(std::move(word));
136
                        }
137
                        break;
138
                case StringLiteral:
139
                        if(ch=='\\') {
140
                                if(++i>=str.size()) throw std::runtime_error("Unexpected end of line");
141
                                ch=str[i];
142
                                if(ch=='\\') word.push_back('\\');
143
                                else if(ch=='\"') word.push_back('\"');
144
                                else if(ch=='\'') word.push_back('\'');
145
                                else if(ch=='t') word.push_back('\t');
146
                                else if(ch=='n') word.push_back('\n');
147
                                else if(ch=='r') word.push_back('\r');
148
                                else if(ch=='x') { // hexadecimal sequence can be 1-2 digit long
149
                                        std::string seq;
150
                                        if(i+1<str.size()&&Utils::ishexdigit(str[i+1])) seq+=str[i+1];
151
                                        if(i+2<str.size()&&Utils::ishexdigit(str[i+2])) seq+=str[i+2];
152
                                        if(seq.empty()) throw std::runtime_error("Ill-formed escape sequence");
153
                                        try {
154
                                                word.push_back(static_cast<char>(std::stoul(seq,nullptr,16)));
155
                                        }
156
                                        catch(std::exception &) {
157
                                                throw std::runtime_error("Ill-formed escape sequence");
158
                                        }
159
                                        i+=seq.size();
160
                                }
161
                                else if(Utils::isoctdigit(ch)) { // octal sequence can be 1-3 digit long
162
                                        std::string seq(1,ch);
163
                                        if(i+1<str.size()&&Utils::isoctdigit(str[i+1])) seq+=str[i+1];
164
                                        if(i+2<str.size()&&Utils::isoctdigit(str[i+2])) seq+=str[i+2];
165
                                        unsigned long value;
166
                                        try {
167
                                                value=std::stoul(seq,nullptr,8);
168
                                        }
169
                                        catch(std::exception &) {
170
                                                throw std::runtime_error("Ill-formed escape sequence");
171
                                        }
172
 
173
                                        if(value>255) throw std::runtime_error("Octal value is out of range");
174
                                        word.push_back(static_cast<char>(value));
175
 
176
                                        i+=seq.size()-1;
177
                                }
178
                                else throw std::runtime_error(std::string("Unknown escape sequence: \"\\")+ch+"\"");
179
                        }
180
                        else if(ch=='\"') {
181
                                word.push_back('\"');
182
                                tokenList.push_back(std::move(word));
183
                                _state=Initial;
184
                        }
185
                        else word.push_back(ch);
186
                        break;
187
                case BlockComment:
188
                        if(ch=='*') {
189
                                if(++i>=str.size()) break;
190
                                ch=str[i];
191
                                if(ch=='/') _state=Initial;
192
                                else i--;
193
                        }
194
                        break;
195
                }
196
        }
197
 
198
        if(_state==StringLiteral) throw std::runtime_error("Unexpected end of line");
199
        if(_state==Word) tokenList.push_back(std::move(word)); // store last word
200
        if(_state!=BlockComment) _state=Initial; // reset state if not in block comment
201
 
202
        return tokenList;
203
}
204
 
205
void Assembler::expand(TokenList &list) {
206
        TokenList newlist;
207
// Perform macro substitution
208
        for(auto &token: list) {
209
                auto it=_macros.find(token);
210 6 ring0_mipt
// Note: we don't expand a macro identifier in the #define statement
211
// since that would lead to counter-intuitive results
212
                if(it==_macros.end()||
213
                        (newlist.size()==1&&newlist[0]=="#define")||
214
                        (newlist.size()==3&&newlist[1]==":"&&newlist[2]=="#define"))
215
                                newlist.push_back(std::move(token));
216 2 ring0_mipt
                else for(auto const &replace: it->second) newlist.push_back(replace);
217
        }
218
        list=std::move(newlist);
219
}
220
 
221
void Assembler::elaborate(TokenList &list) {
222
        if(list.empty()) return;
223
 
224
// Process label (if present)
225
        if(list.size()>=2&&list[1]==":") {
226
                if(!validateIdentifier(list[0]))
227
                        throw std::runtime_error("Ill-formed identifier: \""+list[0]+"\"");
228
                _currentLabels.push_back(std::move(list[0]));
229
                list.erase(list.begin(),list.begin()+2);
230
        }
231
 
232
        if(list.empty()) return;
233
 
234
// Process statement itself
235
        if(list[0][0]=='#') elaborateDirective(list);
236
        else {
237
                LinkableObject::Word rva;
238
                if(list[0][0]=='.') rva=elaborateDataDefinition(list);
239
                else rva=elaborateInstruction(list);
240
 
241
                for(auto const &label: _currentLabels) {
242 6 ring0_mipt
                        _obj.addSymbol(label,rva);
243 2 ring0_mipt
                }
244
                _currentLabels.clear();
245
        }
246
}
247
 
248
void Assembler::elaborateDirective(TokenList &list) {
249
        assert(!list.empty());
250
 
251
        if(list[0]=="#define") {
252 6 ring0_mipt
                if(list.size()<3)
253
                        throw std::runtime_error("Wrong number of tokens in the directive");
254
                if(_macros.find(list[1])!=_macros.end())
255
                        throw std::runtime_error("Macro \""+list[1]+"\" has been already defined");
256
                if(!validateIdentifier(list[1]))
257
                        throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\"");
258 2 ring0_mipt
                _macros.emplace(list[1],TokenList(list.begin()+2,list.end()));
259
        }
260 6 ring0_mipt
        else if(list[0]=="#export") {
261 2 ring0_mipt
                if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive");
262
                if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\"");
263 6 ring0_mipt
                _exportedSymbols.push_back(list[1]);
264 2 ring0_mipt
        }
265 6 ring0_mipt
        else if(list[0]=="#import") {
266
                if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive");
267
                if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\"");
268
                _obj.addImportedSymbol(list[1]);
269
        }
270 2 ring0_mipt
        else if(list[0]=="#include") {
271
                if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive");
272
                auto filename=Utils::dequoteString(list[1]);
273
                if(Utils::isAbsolutePath(filename)) return processFileRecursive(filename);
274
                else {
275
                        auto path=Utils::relativePath(currentFileName(),filename);
276
                        if(Utils::fileExists(path)) return processFileRecursive(path);
277
                        else {
278
                                for(auto const &dir: _includeSearchDirs) {
279
                                        path=Utils::nativeSeparators(dir+filename);
280
                                        if(Utils::fileExists(path)) return processFileRecursive(path);
281
                                }
282
                        }
283
                }
284
                throw std::runtime_error("Cannot locate include file \""+filename+"\"");
285
        }
286
        else if(list[0]=="#message") {
287
                if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive");
288
                auto msg=Utils::dequoteString(list[1]);
289
                std::cout<<currentFileName()<<":"<<line()<<": "<<msg<<std::endl;
290
        }
291
        else throw std::runtime_error("Unrecognized directive: \""+list[0]+"\"");
292
}
293
 
294
LinkableObject::Word Assembler::elaborateDataDefinition(TokenList &list) {
295
        assert(!list.empty());
296
 
297
        LinkableObject::Word rva=0;
298
 
299
        if(list[0]==".align") {
300
                if(list.size()>2) throw std::runtime_error("Unexpected token: \""+list[2]+"\"");
301
                std::size_t align=4;
302
                if(list.size()>1) align=static_cast<std::size_t>(numericLiteral(list[1]));
303 6 ring0_mipt
                if(!Utils::isPowerOf2(align)) throw std::runtime_error("Alignment must be a power of 2");
304
                if(align<4) throw std::runtime_error("Alignment must be at least 4");
305 2 ring0_mipt
                rva=_obj.addPadding(align);
306
        }
307
        else if(list[0]==".reserve") {
308
                if(list.size()<2) throw std::runtime_error("Unexpected end of statement");
309
                else if(list.size()>2) throw std::runtime_error("Unexpected token: \""+list[2]+"\"");
310
                auto n=static_cast<std::size_t>(numericLiteral(list[1]));
311
                rva=_obj.addZeros(n);
312
        }
313
        else if(list[0]==".word") {
314
                if(list.size()<2) throw std::runtime_error("Unexpected end of statement");
315
                for(std::size_t i=1;i<list.size();i++) {
316
                        if(i%2!=0) {
317
                                auto w=static_cast<LinkableObject::Word>(numericLiteral(list[i]));
318
                                auto r=_obj.addWord(w);
319
                                if(i==1) rva=r;
320
                        }
321
                        else {
322
                                if(list[i]!=",") throw std::runtime_error("Comma expected");
323
                                if(i+1==list.size()) throw std::runtime_error("Unexpected end of statement");
324
                        }
325
                }
326
        }
327
        else if(list[0]==".byte") {
328
                if(list.size()<2) throw std::runtime_error("Unexpected end of statement");
329
                for(std::size_t i=1;i<list.size();i++) {
330
                        if(i%2!=0) {
331
                                if(list[i].at(0)=='\"') { // string literal
332
                                        auto bytes=Utils::dequoteString(list[i]);
333
                                        auto r=_obj.addBytes(reinterpret_cast<const LinkableObject::Byte*>
334
                                                (bytes.c_str()),bytes.size());
335
                                        if(i==1) rva=r;
336
                                }
337
                                else {
338
                                        auto n=numericLiteral(list[i]);
339
 
340
                                        if(n>255||n<-128) throw std::runtime_error("\""+list[i]+"\": out of range");
341
 
342
                                        auto b=static_cast<LinkableObject::Byte>(n);
343
                                        auto r=_obj.addByte(b);
344
                                        if(i==1) rva=r;
345
                                }
346
                        }
347
                        else {
348
                                if(list[i]!=",") throw std::runtime_error("Comma expected");
349
                                if(i+1==list.size()) throw std::runtime_error("Unexpected end of statement");
350
                        }
351
                }
352
        }
353
        else throw std::runtime_error("Unrecognized statement: \""+list[0]+"\"");
354
 
355
        return rva;
356
}
357
 
358
LinkableObject::Word Assembler::elaborateInstruction(TokenList &list) {
359
        assert(!list.empty());
360
        auto rva=_obj.addPadding();
361
        if(list[0]=="add") encodeAdd(list);
362
        else if(list[0]=="and") encodeAnd(list);
363
        else if(list[0]=="call") encodeCall(list);
364
        else if(list[0].substr(0,4)=="cjmp") encodeCjmpxx(list);
365
        else if(list[0]=="divs") encodeDivs(list);
366
        else if(list[0]=="divu") encodeDivu(list);
367
        else if(list[0]=="hlt") encodeHlt(list);
368
        else if(list[0]=="jmp") encodeJmp(list);
369
        else if(list[0]=="iret") encodeIret(list);
370
        else if(list[0]=="lc") encodeLc(list);
371 6 ring0_mipt
        else if(list[0]=="lcs") encodeLcs(list);
372 2 ring0_mipt
        else if(list[0]=="lsb") encodeLsb(list);
373
        else if(list[0]=="lub") encodeLub(list);
374
        else if(list[0]=="lw") encodeLw(list);
375
        else if(list[0]=="mods") encodeMods(list);
376
        else if(list[0]=="modu") encodeModu(list);
377
        else if(list[0]=="mov") encodeMov(list);
378
        else if(list[0]=="mul") encodeMul(list);
379 6 ring0_mipt
        else if(list[0]=="neg") encodeNeg(list);
380 2 ring0_mipt
        else if(list[0]=="nop") encodeNop(list);
381
        else if(list[0]=="not") encodeNot(list);
382
        else if(list[0]=="or") encodeOr(list);
383
        else if(list[0]=="ret") encodeRet(list);
384
        else if(list[0]=="sb") encodeSb(list);
385
        else if(list[0]=="sl") encodeSl(list);
386
        else if(list[0]=="srs") encodeSrs(list);
387
        else if(list[0]=="sru") encodeSru(list);
388
        else if(list[0]=="sub") encodeSub(list);
389
        else if(list[0]=="sw") encodeSw(list);
390
        else if(list[0]=="xor") encodeXor(list);
391
        else throw std::runtime_error("Unrecognized instruction: \""+list[0]+"\"");
392
        return rva;
393
}
394
 
395
bool Assembler::validateIdentifier(const std::string &str) {
396
/*
397
 * Valid identifier must satisfy the following requirements:
398
 *  1. Must not be empty
399
 *  2. The first character must be either alphabetic or an underscore
400
 *  3. Subsequent characters must be either alphanumeric or underscores
401
 */
402
        if(str.empty()) return false;
403
        for(std::size_t i=0;i<str.size();i++) {
404
                char ch=str[i];
405
                if(i==0) {
406
                        if(!std::isalpha(ch)&&ch!='_') return false;
407
                }
408
                else {
409
                        if(!std::isalnum(ch)&&ch!='_') return false;
410
                }
411
        }
412
        return true;
413
}
414
 
415
Assembler::Integer Assembler::numericLiteral(const std::string &str) {
416
        std::size_t pos;
417
        Integer i;
418
        try {
419
                i=std::stoll(str,&pos,0);
420
        }
421
        catch(std::exception &) {
422
                throw std::runtime_error("Ill-formed numeric literal: \""+str+"\"");
423
        }
424
        if(pos<str.size()) throw std::runtime_error("Ill-formed numeric literal: \""+str+"\"");
425
 
426
        typedef std::make_signed<LinkableObject::Word>::type SignedWord;
427
 
428
        if(i>static_cast<Integer>(std::numeric_limits<LinkableObject::Word>::max())||
429
                i<static_cast<Integer>(std::numeric_limits<SignedWord>::min()))
430
                        throw std::runtime_error("\""+str+"\": out of range");
431
 
432
        return i;
433
}
434
 
435
std::vector<Assembler::Operand> Assembler::getOperands(const TokenList &list) {
436
        std::vector<Operand> arglist;
437
        for(std::size_t i=1;i<list.size();i++) {
438
                if(i%2!=0) {
439
                        Operand a;
440
                        a.str=list[i];
441
 
442
                        if(!list[i].empty()&&list[i][0]=='r') {
443
// Is argument a register?
444
                                char *endptr;
445
                                auto regstr=list[i].substr(1);
446
                                auto reg=std::strtol(regstr.c_str(),&endptr,10);
447
 
448
                                if(!*endptr&&reg>=0&&reg<=255) {
449
                                        a.type=Operand::Register;
450
                                        a.reg=static_cast<std::uint8_t>(reg);
451
                                        arglist.push_back(std::move(a));
452
                                        continue;
453
                                }
454
                        }
455
 
456
// Try alternative register names
457
                        if(list[i]=="sp") { // stack pointer
458
                                a.type=Operand::Register;
459
                                a.reg=255;
460
                                arglist.push_back(std::move(a));
461
                        }
462
                        else if(list[i]=="rp") { // return pointer
463
                                a.type=Operand::Register;
464
                                a.reg=254;
465
                                arglist.push_back(std::move(a));
466
                        }
467
                        else if(list[i]=="irp") { // interrupt return pointer
468
                                a.type=Operand::Register;
469
                                a.reg=253;
470
                                arglist.push_back(std::move(a));
471
                        }
472
                        else if(list[i]=="cr") { // control register
473
                                a.type=Operand::Register;
474
                                a.reg=252;
475
                                arglist.push_back(std::move(a));
476
                        }
477
                        else if(list[i].size()==3&&list[i].substr(0,2)=="iv"&&
478 6 ring0_mipt
                                list[i][2]>='0'&&list[i][2]<='7') // interrupt vector
479 2 ring0_mipt
                        {
480
                                a.type=Operand::Register;
481
                                a.reg=240+(list[i][2]-'0');
482
                                arglist.push_back(std::move(a));
483
                        }
484
                        else if(validateIdentifier(list[i])) {
485
// Is argument an identifier?
486
                                a.type=Operand::Identifier;
487
                                arglist.push_back(std::move(a));
488
                        }
489
                        else {
490
                                auto atpos=list[i].find_first_of('@');
491
                                if(atpos!=std::string::npos) {
492
// Identifier with an offset?
493
                                        a.type=Operand::Identifier;
494
                                        a.str=list[i].substr(0,atpos);
495
                                        if(!validateIdentifier(a.str)) throw std::runtime_error("Ill-formed identifier");
496
                                        a.i=numericLiteral(list[i].substr(atpos+1));
497
                                        arglist.push_back(std::move(a));
498
                                }
499
                                else {
500
// Numeric literal?
501
                                        a.type=Operand::NumericLiteral;
502
                                        a.i=numericLiteral(list[i]);
503
                                        arglist.push_back(std::move(a));
504
                                }
505
                        }
506
                }
507
                else {
508
                        if(list[i]!=",") throw std::runtime_error("Comma expected");
509
                        if(i+1==list.size()) throw std::runtime_error("Unexpected end of line");
510
                }
511
        }
512
        return arglist;
513
}
514
 
515
/*
516
 * Member functions to encode LXP32 instructions
517
 */
518
 
519
void Assembler::encodeDstOperand(LinkableObject::Word &word,const Operand &arg) {
520
        if(arg.type!=Operand::Register)
521
                throw std::runtime_error("\""+arg.str+"\": must be a register");
522
        word|=arg.reg<<16;
523
}
524
 
525
void Assembler::encodeRd1Operand(LinkableObject::Word &word,const Operand &arg) {
526
        if(arg.type==Operand::Register) {
527
                word|=0x02000000;
528
                word|=arg.reg<<8;
529
        }
530
        else if(arg.type==Operand::NumericLiteral) {
531
                if((arg.i<-128||arg.i>127)&&(arg.i<0xFFFFFF80||arg.i>0xFFFFFFFF))
532
                        throw std::runtime_error("\""+arg.str+"\": out of range");
533
                auto b=static_cast<LinkableObject::Byte>(arg.i);
534
                word|=b<<8;
535
        }
536
        else throw std::runtime_error("\""+arg.str+"\": bad argument");
537
}
538
 
539
void Assembler::encodeRd2Operand(LinkableObject::Word &word,const Operand &arg) {
540
        if(arg.type==Operand::Register) {
541
                word|=0x01000000;
542
                word|=arg.reg;
543
        }
544
        else if(arg.type==Operand::NumericLiteral) {
545
                if((arg.i<-128||arg.i>127)&&(arg.i<0xFFFFFF80||arg.i>0xFFFFFFFF))
546
                        throw std::runtime_error("\""+arg.str+"\": out of range");
547
                auto b=static_cast<LinkableObject::Byte>(arg.i);
548
                word|=b;
549
        }
550
        else throw std::runtime_error("\""+arg.str+"\": bad argument");
551
}
552
 
553
void Assembler::encodeAdd(const TokenList &list) {
554
        auto args=getOperands(list);
555
        if(args.size()!=3) throw std::runtime_error("add instruction requires 3 operands");
556
        LinkableObject::Word w=0x40000000;
557
        encodeDstOperand(w,args[0]);
558
        encodeRd1Operand(w,args[1]);
559
        encodeRd2Operand(w,args[2]);
560
        _obj.addWord(w);
561
}
562
 
563
void Assembler::encodeAnd(const TokenList &list) {
564
        auto args=getOperands(list);
565
        if(args.size()!=3) throw std::runtime_error("and instruction requires 3 operands");
566
        LinkableObject::Word w=0x60000000;
567
        encodeDstOperand(w,args[0]);
568
        encodeRd1Operand(w,args[1]);
569
        encodeRd2Operand(w,args[2]);
570
        _obj.addWord(w);
571
}
572
 
573
void Assembler::encodeCall(const TokenList &list) {
574
        auto args=getOperands(list);
575
        if(args.size()!=1) throw std::runtime_error("call instruction requires 1 operand");
576
        if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register");
577
        LinkableObject::Word w=0x86FE0000;
578
        encodeRd1Operand(w,args[0]);
579
        _obj.addWord(w);
580
}
581
 
582
void Assembler::encodeCjmpxx(const TokenList &list) {
583
        auto args=getOperands(list);
584
        if(args.size()!=3) throw std::runtime_error("cjmpxx instruction requires 3 operands");
585
 
586
        LinkableObject::Word w;
587
        bool reverse=false;
588
/*
589
 * Note: cjmpul, cjmpule, cjmpsl and cjmpsle don't have distinct opcodes;
590
 * instead, they are aliases for respective "g" or "ge" instructions
591
 * with reversed operand order.
592
 */
593
        if(list[0]=="cjmpe") w=0xE0000000;
594
        else if(list[0]=="cjmpne") w=0xD0000000;
595
        else if(list[0]=="cjmpug"||list[0]=="cjmpul") w=0xC8000000;
596
        else if(list[0]=="cjmpuge"||list[0]=="cjmpule") w=0xE8000000;
597
        else if(list[0]=="cjmpsg"||list[0]=="cjmpsl") w=0xC4000000;
598
        else if(list[0]=="cjmpsge"||list[0]=="cjmpsle") w=0xE4000000;
599
        else throw std::runtime_error("Unrecognized instruction: \""+list[0]+"\"");
600
 
601
        if(list[0]=="cjmpul"||list[0]=="cjmpule"||
602
                list[0]=="cjmpsl"||list[0]=="cjmpsle") reverse=true;
603
 
604
        encodeDstOperand(w,args[0]);
605
 
606
        if(!reverse) {
607
                encodeRd1Operand(w,args[1]);
608
                encodeRd2Operand(w,args[2]);
609
        }
610
        else {
611
                encodeRd1Operand(w,args[2]);
612
                encodeRd2Operand(w,args[1]);
613
        }
614
        _obj.addWord(w);
615
}
616
 
617
void Assembler::encodeDivs(const TokenList &list) {
618
        auto args=getOperands(list);
619
        if(args.size()!=3) throw std::runtime_error("divs instruction requires 3 operands");
620
        LinkableObject::Word w=0x54000000;
621
        encodeDstOperand(w,args[0]);
622
        encodeRd1Operand(w,args[1]);
623
        encodeRd2Operand(w,args[2]);
624
        _obj.addWord(w);
625
}
626
 
627
void Assembler::encodeDivu(const TokenList &list) {
628
        auto args=getOperands(list);
629
        if(args.size()!=3) throw std::runtime_error("divu instruction requires 3 operands");
630
        LinkableObject::Word w=0x50000000;
631
        encodeDstOperand(w,args[0]);
632
        encodeRd1Operand(w,args[1]);
633
        encodeRd2Operand(w,args[2]);
634
        _obj.addWord(w);
635
}
636
 
637
void Assembler::encodeHlt(const TokenList &list) {
638
        auto args=getOperands(list);
639
        if(!args.empty()) throw std::runtime_error("hlt instruction doesn't take operands");
640
        _obj.addWord(0x08000000);
641
}
642
 
643
void Assembler::encodeJmp(const TokenList &list) {
644
        auto args=getOperands(list);
645
        if(args.size()!=1) throw std::runtime_error("jmp instruction requires 1 operand");
646
        if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register");
647
        LinkableObject::Word w=0x82000000;
648
        encodeRd1Operand(w,args[0]);
649
        _obj.addWord(w);
650
}
651
 
652
void Assembler::encodeIret(const TokenList &list) {
653
// Note: "iret" is not a real instruction, but an alias for "jmp irp"
654
        auto args=getOperands(list);
655
        if(!args.empty()) throw std::runtime_error("iret instruction doesn't take operands");
656
        _obj.addWord(0x8200FD00);
657
}
658
 
659
void Assembler::encodeLc(const TokenList &list) {
660
        auto args=getOperands(list);
661
        if(args.size()!=2) throw std::runtime_error("lc instruction requires 2 operands");
662
 
663
        LinkableObject::Word w=0x04000000;
664
        encodeDstOperand(w,args[0]);
665
        _obj.addWord(w);
666
 
667
        if(args[1].type==Operand::Identifier) {
668 6 ring0_mipt
                LinkableObject::Reference ref;
669
                ref.source=currentFileName();
670
                ref.line=line();
671
                ref.rva=_obj.addWord(0);
672
                ref.offset=args[1].i;
673
                ref.type=LinkableObject::Regular;
674
                _obj.addReference(args[1].str,ref);
675 2 ring0_mipt
        }
676
        else if(args[1].type==Operand::NumericLiteral) {
677
                _obj.addWord(static_cast<LinkableObject::Word>(args[1].i));
678
        }
679
        else throw std::runtime_error("\""+args[1].str+"\": bad argument");
680
}
681
 
682 6 ring0_mipt
void Assembler::encodeLcs(const TokenList &list) {
683
        auto args=getOperands(list);
684
        if(args.size()!=2) throw std::runtime_error("lcs instruction requires 2 operands");
685
 
686
        LinkableObject::Word w=0xA0000000;
687
        encodeDstOperand(w,args[0]);
688
 
689
        if(args[1].type==Operand::NumericLiteral) {
690
                if((args[1].i<-1048576||args[1].i>1048575)&&(args[1].i<0xFFF00000||args[1].i>0xFFFFFFFF))
691
                        throw std::runtime_error("\""+args[1].str+"\": out of range");
692
                auto c=static_cast<LinkableObject::Word>(args[1].i)&0x1FFFFF;
693
                w|=(c&0xFFFF);
694
                w|=((c<<8)&0x1F000000);
695
                _obj.addWord(w);
696
        }
697
        else if(args[1].type==Operand::Identifier) {
698
                LinkableObject::Reference ref;
699
                ref.source=currentFileName();
700
                ref.line=line();
701
                ref.rva=_obj.addWord(w);
702
                ref.offset=args[1].i;
703
                ref.type=LinkableObject::Short;
704
                _obj.addReference(args[1].str,ref);
705
        }
706
        else throw std::runtime_error("\""+args[1].str+"\": bad argument");
707
}
708
 
709 2 ring0_mipt
void Assembler::encodeLsb(const TokenList &list) {
710
        auto args=getOperands(list);
711
        if(args.size()!=2) throw std::runtime_error("lsb instruction requires 2 operands");
712
        if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register");
713
        LinkableObject::Word w=0x2E000000;
714
        encodeDstOperand(w,args[0]);
715
        encodeRd1Operand(w,args[1]);
716
        _obj.addWord(w);
717
}
718
 
719
void Assembler::encodeLub(const TokenList &list) {
720
        auto args=getOperands(list);
721
        if(args.size()!=2) throw std::runtime_error("lub instruction requires 2 operands");
722
        if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register");
723
        LinkableObject::Word w=0x2A000000;
724
        encodeDstOperand(w,args[0]);
725
        encodeRd1Operand(w,args[1]);
726
        _obj.addWord(w);
727
}
728
 
729
void Assembler::encodeLw(const TokenList &list) {
730
        auto args=getOperands(list);
731
        if(args.size()!=2) throw std::runtime_error("lw instruction requires 2 operands");
732
        if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register");
733
        LinkableObject::Word w=0x22000000;
734
        encodeDstOperand(w,args[0]);
735
        encodeRd1Operand(w,args[1]);
736
        _obj.addWord(w);
737
}
738
 
739
void Assembler::encodeMods(const TokenList &list) {
740
        auto args=getOperands(list);
741
        if(args.size()!=3) throw std::runtime_error("mods instruction requires 3 operands");
742
        LinkableObject::Word w=0x5C000000;
743
        encodeDstOperand(w,args[0]);
744
        encodeRd1Operand(w,args[1]);
745
        encodeRd2Operand(w,args[2]);
746
        _obj.addWord(w);
747
}
748
 
749
void Assembler::encodeModu(const TokenList &list) {
750
        auto args=getOperands(list);
751
        if(args.size()!=3) throw std::runtime_error("modu instruction requires 3 operands");
752
        LinkableObject::Word w=0x58000000;
753
        encodeDstOperand(w,args[0]);
754
        encodeRd1Operand(w,args[1]);
755
        encodeRd2Operand(w,args[2]);
756
        _obj.addWord(w);
757
}
758
 
759
void Assembler::encodeMov(const TokenList &list) {
760
// Note: "mov" is not a real instruction, but an alias for "add dst, src, 0"
761
        auto args=getOperands(list);
762
        if(args.size()!=2) throw std::runtime_error("mov instruction requires 2 operands");
763
        LinkableObject::Word w=0x40000000;
764
        encodeDstOperand(w,args[0]);
765
        encodeRd1Operand(w,args[1]);
766
        _obj.addWord(w);
767
}
768
 
769
void Assembler::encodeMul(const TokenList &list) {
770
        auto args=getOperands(list);
771
        if(args.size()!=3) throw std::runtime_error("mul instruction requires 3 operands");
772
        LinkableObject::Word w=0x48000000;
773
        encodeDstOperand(w,args[0]);
774
        encodeRd1Operand(w,args[1]);
775
        encodeRd2Operand(w,args[2]);
776
        _obj.addWord(w);
777
}
778
 
779 6 ring0_mipt
void Assembler::encodeNeg(const TokenList &list) {
780
// Note: "neg" is not a real instruction, but an alias for "sub dst, 0, src"
781
        auto args=getOperands(list);
782
        if(args.size()!=2) throw std::runtime_error("neg instruction requires 2 operands");
783
        LinkableObject::Word w=0x44000000;
784
        encodeDstOperand(w,args[0]);
785
        encodeRd2Operand(w,args[1]);
786
        _obj.addWord(w);
787
}
788
 
789 2 ring0_mipt
void Assembler::encodeNop(const TokenList &list) {
790
        auto args=getOperands(list);
791
        if(!args.empty()) throw std::runtime_error("nop instruction doesn't take operands");
792
        _obj.addWord(0);
793
}
794
 
795
void Assembler::encodeNot(const TokenList &list) {
796
// Note: "not" is not a real instruction, but an alias for "xor dst, src, -1"
797
        auto args=getOperands(list);
798
        if(args.size()!=2) throw std::runtime_error("not instruction requires 2 operands");
799
        LinkableObject::Word w=0x680000FF;
800
        encodeDstOperand(w,args[0]);
801
        encodeRd1Operand(w,args[1]);
802
        _obj.addWord(w);
803
}
804
 
805
void Assembler::encodeOr(const TokenList &list) {
806
        auto args=getOperands(list);
807
        if(args.size()!=3) throw std::runtime_error("or instruction requires 3 operands");
808
        LinkableObject::Word w=0x64000000;
809
        encodeDstOperand(w,args[0]);
810
        encodeRd1Operand(w,args[1]);
811
        encodeRd2Operand(w,args[2]);
812
        _obj.addWord(w);
813
}
814
 
815
void Assembler::encodeRet(const TokenList &list) {
816
// Note: "ret" is not a real instruction, but an alias for "jmp rp"
817
        auto args=getOperands(list);
818
        if(!args.empty()) throw std::runtime_error("ret instruction doesn't take operands");
819
        _obj.addWord(0x8200FE00);
820
}
821
 
822
void Assembler::encodeSb(const TokenList &list) {
823
        auto args=getOperands(list);
824
        if(args.size()!=2) throw std::runtime_error("sb instruction requires 2 operands");
825
        if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register");
826
        if(args[1].type==Operand::NumericLiteral) {
827
// If numeric literal value is between 128 and 255 (inclusive), convert
828
// it to a signed byte to avoid exception in encodeRd2Operand()
829
                if(args[1].i>=128&&args[1].i<=255) args[1].i-=256;
830
        }
831
        LinkableObject::Word w=0x3A000000;
832
        encodeRd1Operand(w,args[0]);
833
        encodeRd2Operand(w,args[1]);
834
        _obj.addWord(w);
835
}
836
 
837
void Assembler::encodeSl(const TokenList &list) {
838
        auto args=getOperands(list);
839
        if(args.size()!=3) throw std::runtime_error("sl instruction requires 3 operands");
840
        if(args[2].type==Operand::NumericLiteral&&
841
                (args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word))))
842
        {
843
                        std::cerr<<currentFileName()<<":"<<line()<<": ";
844
                        std::cerr<<"Warning: Bitwise shift result is undefined when "
845
                        "the second operand is negative or greater than 31"<<std::endl;
846
        }
847
 
848
        LinkableObject::Word w=0x70000000;
849
        encodeDstOperand(w,args[0]);
850
        encodeRd1Operand(w,args[1]);
851
        encodeRd2Operand(w,args[2]);
852
        _obj.addWord(w);
853
}
854
 
855
void Assembler::encodeSrs(const TokenList &list) {
856
        auto args=getOperands(list);
857
        if(args.size()!=3) throw std::runtime_error("srs instruction requires 3 operands");
858
        if(args[2].type==Operand::NumericLiteral&&
859
                (args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word))))
860
        {
861
                        std::cerr<<currentFileName()<<":"<<line()<<": ";
862
                        std::cerr<<"Warning: Bitwise shift result is undefined when "
863
                        "the second operand is negative or greater than 31"<<std::endl;
864
        }
865
 
866
        LinkableObject::Word w=0x7C000000;
867
        encodeDstOperand(w,args[0]);
868
        encodeRd1Operand(w,args[1]);
869
        encodeRd2Operand(w,args[2]);
870
        _obj.addWord(w);
871
}
872
 
873
void Assembler::encodeSru(const TokenList &list) {
874
        auto args=getOperands(list);
875
        if(args.size()!=3) throw std::runtime_error("sru instruction requires 3 operands");
876
        if(args[2].type==Operand::NumericLiteral&&
877
                (args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word))))
878
        {
879
                        std::cerr<<currentFileName()<<":"<<line()<<": ";
880
                        std::cerr<<"Warning: Bitwise shift result is undefined when "
881
                        "the second operand is negative or greater than 31"<<std::endl;
882
        }
883
 
884
        LinkableObject::Word w=0x78000000;
885
        encodeDstOperand(w,args[0]);
886
        encodeRd1Operand(w,args[1]);
887
        encodeRd2Operand(w,args[2]);
888
        _obj.addWord(w);
889
}
890
 
891
void Assembler::encodeSub(const TokenList &list) {
892
        auto args=getOperands(list);
893
        if(args.size()!=3) throw std::runtime_error("sub instruction requires 3 operands");
894
        LinkableObject::Word w=0x44000000;
895
        encodeDstOperand(w,args[0]);
896
        encodeRd1Operand(w,args[1]);
897
        encodeRd2Operand(w,args[2]);
898
        _obj.addWord(w);
899
}
900
 
901
void Assembler::encodeSw(const TokenList &list) {
902
        auto args=getOperands(list);
903
        if(args.size()!=2) throw std::runtime_error("sw instruction requires 2 operands");
904
        if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register");
905
        LinkableObject::Word w=0x32000000;
906
        encodeRd1Operand(w,args[0]);
907
        encodeRd2Operand(w,args[1]);
908
        _obj.addWord(w);
909
}
910
 
911
void Assembler::encodeXor(const TokenList &list) {
912
        auto args=getOperands(list);
913
        if(args.size()!=3) throw std::runtime_error("xor instruction requires 3 operands");
914
        LinkableObject::Word w=0x68000000;
915
        encodeDstOperand(w,args[0]);
916
        encodeRd1Operand(w,args[1]);
917
        encodeRd2Operand(w,args[2]);
918
        _obj.addWord(w);
919
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.