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