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