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