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