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