変数名を修正
[YACASL2.git] / src / assemble.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <assert.h>
6
7 #include "assemble.h"
8 #include "cerr.h"
9
10 /**
11  * アセンブルモード: src, label, onlylabel, asdetail, onlyassemble
12  */
13 ASMODE asmode = {false, false, false, false, false};
14
15 /**
16  * アセンブル時の、現在およびリテラルのアドレスとプログラム入口名: ptr, lptr, prog
17  */
18 ASPTR *asptr;
19
20 /**
21  * アセンブルのエラー定義
22  */
23 static CERR cerr_assemble[] = {
24     { 101, "label already defined" },
25     { 102, "label table is full" },
26     { 103, "label not found" },
27     { 106, "operand mismatch in assemble command" },
28     { 107, "no label in START" },
29     { 108, "not command of operand \"r\"" },
30     { 109, "not command of operand \"r1,r2\"" },
31     { 110, "not command of operand \"r,adr[,x]\"" },
32     { 111, "not command of operand \"adr[,x]\"" },
33     { 112, "not command of no operand" },
34     { 113, "operand too many in COMET II command" },
35     { 119, "out of COMET II memory" },
36     { 120, "GR0 in operand x" },
37     { 122, "cannot create hash table" },
38     { 124, "more than one character in literal" },
39     { 125, "not GR in operand x" },
40 };
41
42 WORD getadr(const char *prog, const char *str, PASS pass);
43
44 WORD getgr(const char *str, bool is_x);
45
46 WORD getliteral(const char *str, PASS pass);
47
48 bool assemblecmd(const CMDLINE *cmdl, PASS pass);
49
50 bool macrocmd(const CMDLINE *cmdl, PASS pass);
51
52 void writeIN(const char *ibuf, const char *len, PASS pass);
53
54 void writeOUT(const char *obuf, const char *len, PASS pass);
55
56 void writeRPUSH(PASS pass);
57
58 void writeRPOP(PASS pass);
59
60 bool cometcmd(const CMDLINE *cmdl, PASS pass);
61
62 void writememory(WORD word, WORD adr, PASS pass);
63
64 void writestr(const char *str, bool literal, PASS pass);
65
66 void writeDC(const char *str, PASS pass);
67
68 bool assembletok(const CMDLINE *cmdl, PASS pass);
69
70 bool assembleline(const char *line, PASS pass);
71
72 void printline(FILE *stream, const char *filename, int lineno, char *line);
73
74 /**
75  * 汎用レジスタを表す文字列「GR[0-7]」から、レジスタ番号[0-7]をWORD値で返す
76  * 文字列が汎用レジスタを表さない場合は、0xFFFFを返す
77  * is_xがtrueの場合は指標レジスタ。GR0が指定された場合は、COMET IIの仕様によりエラー発生
78  */
79 WORD getgr(const char *str, bool is_x)
80 {
81     assert(str != NULL);
82     WORD r;
83
84     /* 「GR[0-7]」以外の文字列では、0xFFFFを返して終了 */
85     if(!(strlen(str) == 3 && strncmp(str, "GR", 2) == 0 &&
86          (*(str+2) >= '0' && *(str+2) <= '0' + (GRSIZE - 1))))
87     {
88         return 0xFFFF;
89     }
90     r = (WORD)(*(str+2) - '0');
91     /* GR0は指標レジスタとして用いることができない */
92     if(is_x == true && r == 0x0) {
93         setcerr(120, NULL);    /* GR0 in operand x */
94         return 0x0;
95     }
96     return r;
97 }
98
99 /**
100  * 定数の前に等号(=)をつけて記述されるリテラルを返す
101  * リテラルには、10進定数/16進定数/文字定数が含まれる
102  */
103 WORD getliteral(const char *str, PASS pass)
104 {
105     assert(*str == '=');
106     WORD adr = asptr->lptr;
107
108     if(*(++str) == '\'') {    /* 文字定数 */
109         writestr(str, true, pass);
110     } else {
111         writememory(nh2word(str), (asptr->lptr)++, pass);
112     }
113     return adr;
114 }
115
116 /**
117  * アセンブラ命令をメモリに書込
118  * 実行に成功した場合はtrue、それ以外の場合はfalseを返す
119  */
120 bool assemblecmd(const CMDLINE *cmdl, PASS pass)
121 {
122     int i = 0;
123     ASCMDID cmdid = 0;
124     ASCMD ascmd[] = {
125         { START, 0, 1, "START" },
126         { END, 0, 0, "END" },
127         { DC, 1, OPDSIZE, "DC" },
128         { DS, 1, 1, "DS" },
129         { 0, 0, 0, NULL }
130     };
131
132     do {
133         if(strcmp(cmdl->cmd, ascmd[i].cmd) == 0) {
134             if(cmdl->opd->opdc < ascmd[i].opdc_min || cmdl->opd->opdc > ascmd[i].opdc_max) {
135                 setcerr(106, NULL);    /* operand count mismatch */
136                 return false;
137             }
138             cmdid = ascmd[i].cmdid;
139             break;
140         }
141     } while(ascmd[++i].cmdid != 0);
142     /* アセンブラ命令 */
143     switch(cmdid)
144     {
145     case START:
146         if(cmdl->label == NULL) {
147             setcerr(107, NULL);    /* no label in START */
148             return false;
149         }
150         /* プログラム名の設定 */
151         asptr->prog = strdup_chk(cmdl->label, "asptr.prog");
152         /* オペランドがある場合、実行開始アドレスを設定 */
153         if(pass == SECOND && cmdl->opd->opdc == 1) {
154             if((execptr->start = getlabel(asptr->prog, cmdl->opd->opdv[0])) == 0xFFFF) {
155                 setcerr(103, cmdl->opd->opdv[0]);    /* label not found */
156             }
157         }
158         break;
159     case END:
160         /* 1回目のアセンブルの場合は、リテラル領域開始アドレスを設定 */
161         if(pass == FIRST) {
162             asptr->lptr = asptr->ptr;
163         }
164         /* 2回目のアセンブルの場合は、リテラル領域終了アドレスを実行終了アドレスとして設定 */
165         else if(pass == SECOND) {
166             execptr->end = asptr->lptr;
167         }
168         FREE(asptr->prog);
169         break;
170     case DS:
171         for(i = 0; i < atoi(cmdl->opd->opdv[0]); i++) {
172             writememory(0x0, (asptr->ptr)++, pass);
173             if(cerr->num > 0) {
174                 break;
175             }
176         }
177         break;
178     case DC:
179         for(i = 0; i < cmdl->opd->opdc; i++) {
180             writeDC(cmdl->opd->opdv[i], pass);
181             if(cerr->num > 0) {
182                 break;
183             }
184         }
185         break;
186     default:
187         return false;
188     }
189     return (cerr->num == 0) ? true : false;
190 }
191
192 /**
193  *  macrocmd
194  *  マクロ命令をメモリに書込
195  *  書込に成功した場合はtrue、それ以外の場合はfalseを返す
196  */
197 bool macrocmd(const CMDLINE *cmdl, PASS pass)
198 {
199     int i = 0;
200     MACROCMDID cmdid = 0;
201     MACROCMD macrocmd[] = {
202         { IN, 2, 2, "IN" },
203         { OUT, 2, 2, "OUT" },
204         { RPUSH, 0, 0, "RPUSH" },
205         { RPOP, 0, 0, "RPOP" },
206         { 0, 0, 0, NULL }
207     };
208
209     do {
210         if(strcmp(cmdl->cmd, macrocmd[i].cmd) == 0) {
211             if(cmdl->opd->opdc < macrocmd[i].opdc_min ||
212                cmdl->opd->opdc > macrocmd[i].opdc_max)
213             {
214                 setcerr(106, NULL);    /* operand count mismatch */
215                 return false;
216             }
217             cmdid = macrocmd[i].cmdid;
218             break;
219         }
220     } while(macrocmd[++i].cmdid != 0);
221     switch(cmdid)
222     {
223     case IN:
224         writeIN(cmdl->opd->opdv[0], cmdl->opd->opdv[1], pass);
225         return true;
226     case OUT:
227         writeOUT(cmdl->opd->opdv[0], cmdl->opd->opdv[1], pass);
228         return true;
229     case RPUSH:
230         writeRPUSH(pass);
231         return true;
232     case RPOP:
233         writeRPOP(pass);
234         return true;
235     default:
236         return false;
237     }
238 }
239
240 /**
241  * マクロ命令「IN IBUF,LEN」をメモリに書込
242  *      PUSH 0,GR1
243  *      PUSH 0,GR2
244  *      LAD GR1,IBUF
245  *      LAD GR2,LEN
246  *      SVC 1
247  *      POP GR2
248  *      POP GR1
249  */
250 void writeIN(const char *ibuf, const char *len, PASS pass)
251 {
252     char *line = malloc_chk(LINESIZE + 1, "writeIN.line");
253
254     assembleline("    PUSH 0,GR1", pass);
255     assembleline("    PUSH 0,GR2", pass);
256     sprintf(line, "    LAD GR1,%s", ibuf);
257     assembleline(line, pass);
258     sprintf(line, "    LAD GR2,%s", len);
259     assembleline(line, pass);
260     assembleline("    SVC 1", pass);
261     assembleline("    POP GR2", pass);
262     assembleline("    POP GR1", pass);
263
264     FREE(line);
265 }
266
267 /**
268  *  マクロ命令「OUT OBUF,LEN」をメモリに書込
269  *      PUSH 0,GR1
270  *      PUSH 0,GR2
271  *      LAD GR1,OBUF
272  *      LAD GR2,LEN
273  *      SVC 2
274  *      LAD GR1,=#A
275  *      LAD GR2,=1
276  *      SVC 2
277  *      POP GR2
278  *      POP GR1
279  */
280 void writeOUT(const char *obuf, const char *len, PASS pass)
281 {
282     char *line = malloc_chk(LINESIZE + 1, "writeOUT.line");
283
284     assembleline("    PUSH 0,GR1", pass);
285     assembleline("    PUSH 0,GR2", pass);
286     sprintf(line, "    LAD GR1,%s", obuf);
287     assembleline(line, pass);
288     sprintf(line, "    LAD GR2,%s", len);
289     assembleline(line, pass);
290     assembleline("    SVC 2", pass);
291     assembleline("    LAD GR1,=#A", pass);
292     assembleline("    LAD GR2,=1", pass);
293     assembleline("    SVC 2", pass);
294     assembleline("    POP GR2", pass);
295     assembleline("    POP GR1", pass);
296     FREE(line);
297 }
298
299 /** マクロ命令「RPUSH」をメモリに書き込む
300  *       PUSH 0,GR1
301  *       PUSH 0,GR2
302  *       PUSH 0,GR3
303  *       PUSH 0,GR4
304  *       PUSH 0,GR5
305  *       PUSH 0,GR6
306  *       PUSH 0,GR7
307  */
308 void writeRPUSH(PASS pass)
309 {
310     int i;
311     char *line = malloc_chk(LINESIZE + 1, "writeRPUSH.line");
312
313     for(i = 1; i <= GRSIZE-1; i++) {
314         sprintf(line, "    PUSH 0,GR%d", i);
315         assembleline(line, pass);
316     }
317     FREE(line);
318 }
319
320 /**
321  * マクロ命令「RPOP」をメモリに書き込む
322  *      POP GR7
323  *      POP GR6
324  *      POP GR5
325  *      POP GR4
326  *      POP GR3
327  *      POP GR3
328  *      POP GR2
329  *      POP GR1
330  */
331 void writeRPOP(PASS pass)
332 {
333     int i;
334     char *line = malloc_chk(LINESIZE + 1, "writeRPOP.line");
335
336     for(i = GRSIZE-1; i >= 1; i--) {
337         sprintf(line, "    POP GR%d", i);
338         assembleline(line, pass);
339     }
340     FREE(line);
341 }
342
343 /**
344  * 機械語命令をメモリに書込
345  * 書込に、成功した場合はtrue、失敗した場合はfalse、を返す
346  */
347 bool cometcmd(const CMDLINE *cmdl, PASS pass)
348 {
349     WORD cmd, rr1, xr2, adr;
350
351     /* オペランドなし */
352     if(cmdl->opd->opdc == 0) {
353         if((cmd = getcmdcode(cmdl->cmd, NONE)) == 0xFFFF) {
354             setcerr(112, cmdl->cmd);    /* not command of no operand */
355             return false;
356         }
357         writememory(cmd, (asptr->ptr)++, pass);
358     }
359     /* 第1オペランドは汎用レジスタ */
360     else if((rr1 = getgr(cmdl->opd->opdv[0], false)) != 0xFFFF) {
361         /* オペランド数1 */
362         if(cmdl->opd->opdc == 1) {
363             if((cmd = getcmdcode(cmdl->cmd, R_)) == 0xFFFF) {
364                 setcerr(108, cmdl->cmd);    /* not command of operand "r" */
365                 return false;
366             }
367             cmd |= (rr1 << 4);
368             writememory(cmd, (asptr->ptr)++, pass);
369         }
370         /* オペランド数2。第2オペランドは汎用レジスタ */
371         else if(cmdl->opd->opdc == 2 && (xr2 = getgr(cmdl->opd->opdv[1], false)) != 0xFFFF) {
372             if((cmd = getcmdcode(cmdl->cmd, R1_R2)) == 0xFFFF) {
373                 setcerr(109, cmdl->cmd);    /* not command of operand "r1,r2" */
374                 return false;
375             }
376             cmd |= ((rr1 << 4) | xr2);               /* 第1オペランド、第2オペランドともに汎用レジスタ */
377             /* メモリへの書き込み */
378             writememory(cmd, (asptr->ptr)++, pass);
379         }
380         /* オペランド数2または3 */
381         else if(cmdl->opd->opdc == 2 || cmdl->opd->opdc == 3) {
382             if((cmd = getcmdcode(cmdl->cmd, R_ADR_X)) == 0xFFFF) {
383                 setcerr(110, cmdl->cmd);    /* not command of operand "r,adr[,x]" */
384                 return false;
385             }
386             cmd |= (rr1 << 4);                    /* 第1オペランドは汎用レジスタ */
387             /* オペランド数3の場合 */
388             if(cmdl->opd->opdc == 3) {             /* 第3オペランドは指標レジスタとして用いる汎用レジスタ */
389                 if((xr2 = getgr(cmdl->opd->opdv[2], true)) == 0xFFFF) {
390                     setcerr(125, cmdl->cmd);    /* not GR in operand x */
391                     return false;
392                 }
393                 cmd |= xr2;
394             }
395             adr = getadr(asptr->prog, cmdl->opd->opdv[1], pass); /* 第2オペランドはアドレス */
396             /* メモリへの書き込み */
397             writememory(cmd, (asptr->ptr)++, pass);
398             writememory(adr, (asptr->ptr)++, pass);
399         } else {
400             setcerr(113, cmdl->cmd);    /* operand too many in COMET II command */
401             return false;
402         }
403     }
404     /* オペランド数1または2。第1オペランドはアドレス */
405     else if(cmdl->opd->opdc == 1 || cmdl->opd->opdc == 2) {
406         if((cmd = getcmdcode(cmdl->cmd, ADR_X)) == 0xFFFF) {
407             setcerr(111, cmdl->cmd);    /* not command of operand "adr[,x]" */
408             return false;
409         }
410         /* オペランド数2の場合 */
411         if(cmdl->opd->opdc == 2) {             /* 第2オペランドは指標レジスタとして用いる汎用レジスタ */
412             xr2 = getgr(cmdl->opd->opdv[1], true);
413             if(cerr->num > 0) {
414                 return false;
415             }
416             cmd |= xr2;
417         }
418         /* CALLの場合はプログラムの入口名を表すラベルを取得 */
419         /* CALL以外の命令の場合と、プログラムの入口名を取得できない場合は、 */
420         /* 同一プログラム内のラベルを取得 */
421         if(pass == SECOND && cmd == 0x8000) {        /* CALL命令 */
422             adr = getlabel(NULL, cmdl->opd->opdv[0]);
423         }
424         if(cmd != 0x8000 || (pass == SECOND && adr == 0xFFFF)) {
425             adr = getadr(asptr->prog, cmdl->opd->opdv[0], pass);
426         }
427         /* メモリへの書き込み */
428         writememory(cmd, (asptr->ptr)++, pass);
429         writememory(adr, (asptr->ptr)++, pass);
430     }
431     return (cerr->num == 0) ? true : false;
432 }
433
434 /**
435  * COMET IIのメモリにアドレス値を書き込む
436  */
437 void writememory(WORD word, WORD adr, PASS pass)
438 {
439     /* COMET IIメモリオーバーの場合、エラー発生 */
440     if(adr >= sys->memsize) {
441         setcerr(119, word2n(adr));    /* out of COMET II memory */
442         return;
443     }
444     (sys->memory)[adr] = word;
445     if(pass == SECOND && asmode.asdetail == true) {
446         fprintf(stdout, "\t#%04X\t#%04X\n", adr, word);
447     }
448 }
449
450 /**
451  * 文字をメモリに書き込む
452  */
453 void writestr(const char *str, bool literal, PASS pass)
454 {
455     assert(*str == '\'');
456     const char *p = str + 1;
457     bool lw = false;
458
459     for(; ;) {
460         /* 閉じ「'」がないまま文字列が終了した場合 */
461         if(*p == '\0') {
462             setcerr(123, str);    /* unclosed quote */
463             break;
464         }
465         /* 「'」の場合、次の文字が「'」でない場合は正常終了 */
466         if(*p == '\'' && *(++p) != '\'') {
467             break;
468         } else if(literal == true && lw == true) {
469             setcerr(124, str);    /* more than one character in literal */
470             break;
471         }
472         /*リテラルの場合はリテラル領域に書込 */
473         if(literal == true) {
474             writememory(*(p++), (asptr->lptr)++, pass);
475             lw = true;
476         } else {
477             writememory(*(p++), (asptr->ptr)++, pass);
478         }
479     }
480 }
481
482 /**
483  * DC命令の内容を書き込む
484  */
485 void writeDC(const char *str, PASS pass)
486 {
487     WORD adr = 0x0;
488
489     if(*str == '\'') {
490         writestr(str, false, pass);
491     } else {
492         if(*str == '#' || isdigit(*str) || *str == '-') {
493             adr = nh2word(str);
494         } else {
495             if(pass == SECOND && (adr = getlabel(asptr->prog, str)) == 0xFFFF) {
496                 setcerr(103, str);    /* label not found */
497             }
498         }
499         writememory(adr, (asptr->ptr)++, pass);
500     }
501 }
502
503 /**
504  * トークンをアセンブル
505  */
506 bool assembletok(const CMDLINE *cmdl, PASS pass)
507 {
508     bool status = false;
509
510     /* 命令がない場合 */
511     if(cmdl->cmd == NULL){
512         ;
513     }
514     /* アセンブラ命令の処理 */
515     else if(cerr->num == 0 && assemblecmd(cmdl, pass) == true) {
516         ;
517     }
518     /* マクロ命令の書込 */
519     else if(cerr->num == 0 && macrocmd(cmdl, pass) == true) {
520         ;
521     }
522     /* 機械語命令の書込 */
523     else if(cerr->num == 0 && cometcmd(cmdl, pass) == true) {
524         ;
525     }
526     else if(cerr->num == 0) {
527         setcerr(113, cmdl->cmd);    /* operand too many in COMET II command */
528     }
529     /* エラーが発生していないか確認 */
530     if(cerr->num == 0) {
531         status = true;
532     }
533     return status;
534 }
535
536 /**
537  * ファイルストリームの現在行を番号付きで表示する
538  */
539 void printline(FILE *stream, const char *filename, int lineno, char *line)
540 {
541     fprintf(stream, "%s:%5d:%s", filename, lineno, line);
542 }
543
544 /**
545  * アドレスを返す
546  * アドレスには、リテラル/10進定数/16進定数/アドレス定数が含まれる
547  */
548 WORD getadr(const char *prog, const char *str, PASS pass)
549 {
550     WORD adr = 0x0;
551
552     if(*str == '=') {
553         adr = getliteral(str, pass);
554     } else if(isdigit(*str) || *str == '-' || *str == '#') {
555         adr = nh2word(str);
556     } else {
557         if(pass == SECOND && (adr = getlabel(prog, str)) == 0xFFFF) {
558             if(prog != NULL) {
559                 setcerr(103, str);    /* label not found */
560             }
561         }
562     }
563     return adr;
564 }
565
566
567 /**
568  * 1行をアセンブル
569  * passが1の場合はラベルを登録し、2の場合はラベルからアドレスを読み込む
570  */
571 bool assembleline(const char *line, PASS pass)
572 {
573     CMDLINE *cmdl;
574     bool stat = true;
575     int i;
576
577     cmdl = linetok(line);
578     stat = (cerr->num == 0) ? true : false;
579     if(cmdl != NULL) {
580         if(stat == true) {
581             if(pass == FIRST && cmdl->label != NULL) {
582                 stat = addlabel(asptr->prog, cmdl->label, asptr->ptr);
583             }
584             if(stat == true) {
585                 stat = assembletok(cmdl, pass);
586             }
587             FREE(cmdl->label);
588         }
589         if(cmdl->opd != NULL) {
590             for(i = 0; i < cmdl->opd->opdc; i++) {
591                 FREE(cmdl->opd->opdv[i]);
592             }
593         }
594         FREE(cmdl->opd);
595         FREE(cmdl->cmd);
596     }
597     FREE(cmdl);
598     return stat;
599 }
600
601 /**
602  * アセンブルのエラーをエラーリストに追加
603  */
604 void addcerrlist_assemble()
605 {
606     addcerrlist_tok();
607     addcerrlist_word();
608     addcerrlist_label();
609     addcerrlist(ARRAYSIZE(cerr_assemble), cerr_assemble);
610 }
611
612 /**
613  * 指定された名前のファイルをアセンブル
614  * アセンブル完了時はtrue、エラー発生時はfalseを返す
615  */
616 bool assemblefile(const char *file, PASS pass)
617 {
618     int lineno = 1;
619     char *line;
620     FILE *fp;
621
622     if((fp = fopen(file, "r")) == NULL) {
623         perror(file);
624         return false;
625     }
626     for(line = malloc_chk(LINESIZE + 1, "assemble.line"); fgets(line, LINESIZE, fp); lineno++) {
627         if((pass == FIRST && asmode.src == true) || (pass == SECOND && asmode.asdetail == true)) {
628             printline(stdout, file, lineno, line);
629         }
630         if(assembleline(line, pass) == false) {
631             break;
632         }
633     }
634     if(cerr->num > 0) {
635         fprintf(stderr, "Assemble error - %d: %s\n", cerr->num, cerr->msg);
636         printline(stderr, file, lineno, line);
637     }
638     FREE(line);
639     fclose(fp);
640     return (cerr->num == 0) ? true : false;
641 }
642
643 /**
644  * 引数で指定したファイルにアセンブル結果を書込
645  */
646 void outassemble(const char *file)
647 {
648     FILE *fp;
649
650     if((fp = fopen(file, "w")) == NULL) {
651         perror(file);
652         exit(-1);
653     }
654     fwrite(sys->memory, sizeof(WORD), execptr->end, fp);
655     fclose(fp);
656 }