dfc4aa25f6f6a74647dda4813ae667fef46b7abe
[YACASL2.git] / src / assemble.c
1 #include "casl2.h"
2 #include "assemble.h"
3
4 /* アセンブルモード: src, label, onlylabel, asdetail, onlyassemble */
5 ASMODE asmode = {false, false, false, false, false};
6
7 /* 値を格納するポインタ */
8 WORD ptr;
9
10 /* リテラル(=付きの値)を格納するポインタ */
11 WORD lptr;
12
13 /* 他のプログラムで参照する入口名 */
14 char *prog;
15
16 /* アセンブルのエラー定義 */
17 CERRARRAY cerr_assemble[] = {
18     { 101, "label already defined" },
19     { 102, "label table is full" },
20     { 103, "label not found" },
21     { 104, "label length is too long" },
22     { 105, "no command in the line" },
23     { 106, "operand mismatch in assemble command" },
24     { 107, "no label in START" },
25     { 108, "not command of operand \"r\"" },
26     { 109, "not command of operand \"r1,r2\"" },
27     { 110, "not command of operand \"r,adr[,x]\"" },
28     { 111, "not command of operand \"adr[,x]\"" },
29     { 112, "not command of no operand" },
30     { 113, "operand too many in COMET II command" },
31     { 117, "operand too many in DC" },
32     { 118, "operand length too long" },
33     { 119, "out of COMET II memory" },
34     { 120, "GR0 in operand x" },
35     { 121, "cannot get operand token" },
36     { 122, "cannot create hash table" },
37     { 123, "unclosed quote" },
38     { 124, "more than one character in literal" },
39     { 125, "not GR in operand x" },
40 };
41
42 bool addcerrlist_assemble()
43 {
44     return addcerrlist(ARRAYSIZE(cerr_assemble), cerr_assemble);
45 }
46
47 /* 汎用レジスタを表す文字列「GR[0-7]」から、レジスタ番号[0-7]をWORD値で返す */
48 /* 文字列が汎用レジスタを表さない場合は、0xFFFFを返す */
49 /* is_xがtrueの場合は指標レジスタ。GR0は、COMET IIの仕様により、エラー発生 */
50 WORD getgr(const char *str, bool is_x)
51 {
52     assert(str != NULL);
53     WORD r;
54     /* 「GR[0-7]」以外の文字列では、0xFFFFを返して終了 */
55     if(!(strlen(str) == 3 && strncmp(str, "GR", 2) == 0 &&
56          (*(str+2) >= '0' && *(str+2) <= '7')))
57     {
58         return 0xFFFF;
59     }
60     r = (WORD)(*(str+2) - '0');
61     /* GR0は指標レジスタとして用いることができない */
62     if(is_x == true && r == 0x0) {
63         setcerr(120, NULL);    /* GR0 in operand x */
64         return 0x0;
65     }
66     return r;
67 }
68
69 /* アドレスを返す */
70 /* アドレスには、リテラル/10進定数/16進定数/アドレス定数が含まれる */
71 WORD getadr(const char *prog, const char *str, PASS pass)
72 {
73     WORD adr = 0x0;
74     if(*str == '=') {
75         adr = getliteral(str, pass);
76     } else if((*str == '#') || isdigit(*str) || *str == '-') {
77         adr = nh2word(str);
78     } else {
79         if(pass == SECOND && (adr = getlabel(prog, str)) == 0xFFFF) {
80             if(prog != NULL) {
81                 setcerr(103, str);    /* label not found */
82             }
83         }
84     }
85     return adr;
86 }
87
88 /* WORD値wordをアドレスadrに書込 */
89 /* 書込に成功した場合はtrue、失敗した場合はfalseを返す */
90 bool writememory(WORD word, WORD adr, PASS pass)
91 {
92     bool status = false;
93
94     /* COMET IIメモリオーバーの場合 */
95     if(adr >= memsize) {
96         setcerr(119, word2n(adr));    /* out of COMET II memory */
97     }
98     if(cerrno == 0) {
99         memory[adr] = word;
100         if(pass == SECOND && asmode.asdetail == true) {
101             fprintf(stdout, "\t#%04X\t#%04X\n", adr, word);
102         }
103         status = true;
104     }
105     return status;
106 }
107
108 /* 定数の前に等号(=)をつけて記述されるリテラルを返す */
109 /* リテラルには、10進定数/16進定数/文字定数が含まれる */
110 WORD getliteral(const char *str, PASS pass)
111 {
112     WORD adr = lptr;
113     assert(*str == '=');
114     if(*(++str) == '\'') {    /* 文字定数 */
115         writestr(str, true, pass);
116     } else {
117         writememory(nh2word(str), lptr++, pass);
118     }
119     return adr;
120 }
121
122 /* ' 'で囲まれた文字定数をメモリに書込 */
123 /* DC命令とリテラルで使い、リテラルの場合はリテラル領域に書込 */
124 void writestr(const char *str, bool literal, PASS pass)
125 {
126     assert(cerrno == 0 && *str == '\'');
127     const char *p = str + 1;
128     bool lw = false;
129
130     for(; ;) {
131         /* 閉じ「'」がないまま文字列が終了した場合 */
132         if(*p == '\0') {
133             setcerr(123, str);    /* unclosed quote */
134             break;
135         }
136         /* 「'」の場合、次の文字が「'」でない場合は正常終了 */
137         if(*p == '\'' && *(++p) != '\'') {
138             break;
139         } else if(literal == true && lw == true) {
140             setcerr(124, str);    /* more than one character in literal */
141             break;
142         }
143         /*リテラルの場合はリテラル領域に書込 */
144         if(literal == true) {
145             writememory(*(p++), lptr++, pass);
146             lw = true;
147         } else {
148             writememory(*(p++), ptr++, pass);
149         }
150     }
151 }
152
153 /* アセンブラ命令DCをメモリに書込 */
154 void writeDC(const char *str, PASS pass)
155 {
156     WORD adr = 0x0;
157     if(*str == '\'') {
158         writestr(str, false, pass);
159     } else {
160         if(*str == '#' || isdigit(*str) || *str == '-') {
161             adr = nh2word(str);
162         } else {
163             if(pass == SECOND && (adr = getlabel(prog, str)) == 0xFFFF) {
164                 setcerr(103, str);    /* label not found */
165             }
166         }
167         writememory(adr, ptr++, pass);
168     }
169 }
170
171 /* 命令がアセンブラ命令の場合は処理を実行 */
172 /* 実行に成功した場合はtrue、それ以外の場合はfalseを返す */
173 bool assemblecmd(const CMDLINE *cmdl, PASS pass)
174 {
175     int i = 0;
176     CASLCMD cmd = 0;
177     bool status = false;
178     CMDARRAY ascmd[] = {
179         { START, 0, 1, "START" },
180         { END, 0, 0, "END" },
181         { DC, 1, OPDSIZE, "DC" },
182         { DS, 1, 1, "DS" },
183         { 0, 0, 0, NULL }
184     };
185
186     do {
187         if(strcmp(cmdl->cmd, ascmd[i].cmd) == 0) {
188             if(cmdl->opd->opdc < ascmd[i].opdc_min || cmdl->opd->opdc > ascmd[i].opdc_max) {
189                 setcerr(106, NULL);    /* operand count mismatch */
190                 return false;
191             }
192             cmd = ascmd[i].cmdid;
193             break;
194         }
195     } while(ascmd[++i].cmdid != 0);
196     /* アセンブラ命令 */
197     switch(cmd)
198     {
199     case START:
200         if(cmdl->label == NULL) {
201             setcerr(107, NULL);    /* no label in START */
202             return false;
203         }
204         /* プログラム名の設定 */
205         prog = strdup(cmdl->label);
206         /* オペランドがある場合、実行開始番地を設定 */
207         if(pass == SECOND && cmdl->opd->opdc == 1) {
208             if((startptr = getlabel(prog, cmdl->opd->opdv[0])) == 0xFFFF) {
209                 setcerr(103, cmdl->opd->opdv[0]);    /* label not found */
210             }
211         }
212         status = true;
213         break;
214     case END:
215         /* リテラル領域の設定 */
216         if(pass == FIRST) {
217             lptr = ptr;
218         }
219         /* 実行終了番地と次のプログラムの実行開始番地を設定 */
220         else if(pass == SECOND) {
221             endptr = lptr;
222         }
223         prog = NULL;
224         status = true;
225         break;
226     case DS:
227         for(i = 0; i < atoi(cmdl->opd->opdv[0]); i++) {
228             writememory(0x0, ptr++, pass);
229             if(cerrno > 0) {
230                 return false;
231             }
232         }
233         status = true;
234         break;
235     case DC:
236         for(i = 0; i < cmdl->opd->opdc; i++) {
237             writeDC(cmdl->opd->opdv[i], pass);
238             if(cerrno > 0) {
239                 return false;
240             }
241         }
242         status = true;
243         break;
244     default:
245         return false;
246     }
247     if(cerrno > 0) {
248         status = false;
249     }
250     return status;
251 }
252
253 /* 命令がマクロ命令の場合はメモリに書込 */
254 /* 書込に成功した場合はtrue、それ以外の場合はfalseを返す */
255 bool macrocmd(const CMDLINE *cmdl, PASS pass)
256 {
257     int i = 0;
258     CASLCMD cmd;
259     bool status = false;
260     CMDARRAY macrocmd[] = {
261         { IN, 2, 2, "IN" },
262         { OUT, 2, 2, "OUT" },
263         { RPUSH, 0, 0, "RPUSH" },
264         { RPOP, 0, 0, "RPOP" },
265         { 0, 0, 0, NULL }
266     };
267
268     do {
269         if(strcmp(cmdl->cmd, macrocmd[i].cmd) == 0) {
270             if(cmdl->opd->opdc < macrocmd[i].opdc_min || cmdl->opd->opdc > macrocmd[i].opdc_max) {
271                 setcerr(106, NULL);    /* operand count mismatch */
272                 return false;
273             }
274             cmd = macrocmd[i].cmdid;
275             break;
276         }
277     } while(macrocmd[++i].cmdid != 0);
278     switch(cmd)
279     {
280     case IN:
281         status = writeIN(cmdl->opd->opdv[0], cmdl->opd->opdv[1], pass);
282         break;
283     case OUT:
284         status = writeOUT(cmdl->opd->opdv[0], cmdl->opd->opdv[1], pass);
285         break;
286     case RPUSH:
287         status = writeRPUSH(pass);
288         break;
289     case RPOP:
290         status = writeRPOP(pass);
291         break;
292     default:
293         return false;
294     }
295     return status;
296 }
297
298 /* 機械語命令の書込 */
299 /* 書込に成功した場合はtrue、それ以外の場合はfalseを返す */
300 bool cometcmd(const CMDLINE *cmdl, PASS pass)
301 {
302     WORD cmd, adr, r1, r2, x;
303     bool status = false;
304
305     /* オペランドなし */
306     if(cmdl->opd->opdc == 0) {
307         if((cmd = getcmdcode(cmdl->cmd, NONE)) == 0xFFFF) {
308             setcerr(112, cmdl->cmd);    /* not command of no operand */
309             return false;
310         }
311         if(writememory(cmd, ptr++, pass) == true) {
312             status = true;
313         }
314     }
315     /* 第1オペランドは汎用レジスタ */
316     else if((r1 = getgr(cmdl->opd->opdv[0], false)) != 0xFFFF) {
317         /* オペランド数1 */
318         if(cmdl->opd->opdc == 1) {
319             if((cmd = getcmdcode(cmdl->cmd, R_)) == 0xFFFF) {
320                 setcerr(108, cmdl->cmd);    /* not command of operand "r" */
321                 return false;
322             }
323             cmd |= (r1 << 4);
324             if(writememory(cmd, ptr++, pass) == true) {
325                 status = true;
326             }
327         }
328         /* オペランド数2。第2オペランドは汎用レジスタ */
329         else if(cmdl->opd->opdc == 2 && (r2 = getgr(cmdl->opd->opdv[1], false)) != 0xFFFF) {
330             if((cmd = getcmdcode(cmdl->cmd, R1_R2)) == 0xFFFF) {
331                 setcerr(109, cmdl->cmd);    /* not command of operand "r1,r2" */
332                 return false;
333             }
334             cmd |= ((r1 << 4) | r2);
335             if(cerrno == 0 && writememory(cmd, ptr++, pass) == true) {
336                 status = true;
337             }
338         }
339         /* オペランド数2〜3。第2オペランドはアドレス、 */
340         /* 第3オペランドは指標レジスタとして用いる汎用レジスタ */
341         else if(cmdl->opd->opdc == 2 || cmdl->opd->opdc == 3) {
342             if((cmd = getcmdcode(cmdl->cmd, R_ADR_X_)) == 0xFFFF &&
343                (cmd = getcmdcode(cmdl->cmd, R_ADR_X)) == 0xFFFF)
344             {
345                 setcerr(110, cmdl->cmd);    /* not command of operand "r,adr[,x]" */
346                 return false;
347             }
348             cmd |= (r1 << 4);
349             /* オペランド数3 */
350             if(cmdl->opd->opdc == 3) {
351                 if((x = getgr(cmdl->opd->opdv[2], true)) == 0xFFFF) {
352                     setcerr(125, cmdl->cmd);    /* not GR in operand x */
353                     return false;
354                 }
355                 cmd |= x;
356             }
357             adr = getadr(prog, cmdl->opd->opdv[1], pass);
358             writememory(cmd, ptr++, pass);
359             writememory(adr, ptr++, pass);
360             if(cerrno == 0) {
361                 status = true;
362             }
363         } else {
364             setcerr(113, cmdl->cmd);    /* operand too many in COMET II command */
365             return false;
366         }
367     }
368     /* オペランド数1〜2。第1オペランドはアドレス */
369     else if(cmdl->opd->opdc == 1 || cmdl->opd->opdc == 2) {
370         if((cmd = getcmdcode(cmdl->cmd, ADR_X)) == 0xFFFF) {
371             setcerr(111, cmdl->cmd);    /* not command of operand "adr[,x]" */
372             return false;
373         }
374         /* オペランド数2の場合、第2オペランドは指標レジスタとして用いる汎用レジスタ */
375         if(cmdl->opd->opdc == 2) {
376             x = getgr(cmdl->opd->opdv[1], true);
377             if(cerrno > 0) {
378                 return false;
379             }
380             cmd |= x;
381         }
382         /* CALLの場合はプログラムの入口名を表すラベルを取得 */
383         /* CALL以外の命令の場合と、プログラムの入口名を取得できない場合は、 */
384         /* 同一プログラム内のラベルを取得 */
385         if(pass == SECOND && cmd == 0x8000) {        /* CALL命令 */
386             adr = getlabel(NULL, cmdl->opd->opdv[0]);
387         }
388         if(cmd != 0x8000 || (pass == SECOND && adr == 0xFFFF)) {
389             adr = getadr(prog, cmdl->opd->opdv[0], pass);
390         }
391         writememory(cmd, ptr++, pass);
392         writememory(adr, ptr++, pass);
393         if(cerrno == 0) {
394             status = true;
395         }
396     }
397     return status;
398 }
399
400 /* 命令行を1行アセンブルする */
401 bool assembleline(const CMDLINE *cmdl, PASS pass)
402 {
403     bool status = false;
404     /* 命令がない場合 */
405     if(cmdl->cmd == NULL){
406         /* ラベルが定義されていて命令がない場合はエラー */
407         if(cmdl->label != NULL) {
408             setcerr(105, NULL);    /* no command in the line */
409         }
410     }
411     /* アセンブラ命令の処理 */
412     else if(cerrno == 0 && assemblecmd(cmdl, pass) == true) {
413         ;
414     }
415     /* マクロ命令の書込 */
416     else if(cerrno == 0 && macrocmd(cmdl, pass) == true) {
417         ;
418     }
419     /* 機械語命令の書込 */
420     else if(cerrno == 0 && cometcmd(cmdl, pass) == true) {
421         ;
422     }
423     else if(cerrno == 0) {
424         setcerr(113, cmdl->cmd);    /* operand too many in COMET II command */
425     }
426     /* エラーが発生していないか確認 */
427     if(cerrno == 0) {
428         status = true;
429     }
430     return status;
431 }
432
433 void printline(FILE *stream, const char *filename, int lineno, char *line) {
434     fprintf(stream, "%s:%5d:%s", filename, lineno, line);
435 }
436
437 /* 指定された名前のファイルをアセンブル */
438 /* 2回実行される */
439 bool assemble(const char *file, PASS pass)
440 {
441     int lineno = 0;
442     bool status = true;
443     CMDLINE *cmdl;
444     char *line;
445     FILE *fp;
446
447     addcerrlist_assemble();
448     if((fp = fopen(file, "r")) == NULL) {
449         perror(file);
450         return false;
451     }
452     for(; ;) {
453         cmdl = malloc(sizeof(CMDLINE));
454         line = malloc(LINESIZE + 1);
455         if((line = fgets(line, LINESIZE, fp)) == NULL) {
456             break;
457         }
458         lineno++;
459         if((pass == FIRST && asmode.src == true) ||
460            (pass == SECOND && asmode.asdetail == true))
461         {
462             printline(stdout, file, lineno, line);
463         }
464         if((cmdl = linetok(line)) != NULL) {
465             if(pass == FIRST && cmdl->label != NULL) {
466                 if(addlabel(prog, cmdl->label, ptr) == false) {
467                     break;
468                 }
469             }
470             if(assembleline(cmdl, pass) == false) {
471                 break;
472             }
473         }
474         if(cerrno > 0) {
475             break;
476         }
477     }
478     if(cerrno > 0) {
479         fprintf(stderr, "Assemble error - %d: %s\n", cerrno, cerrmsg);
480         printline(stderr, file, lineno, line);
481         status = false;
482     }
483     fclose(fp);
484     return status;
485 }