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

Subversion Repositories lxp32

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

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

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