272f2f8b001a84a8241e15e14a98c761b15e64d5
[YACASL2.git] / src / assemble.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <assert.h>
6
7 #include "assemble.h"
8 #include "cerr.h"
9
10 /**
11  * アセンブルモード: src, label, onlylabel, asdetail, onlyassemble
12  */
13 ASMODE asmode = {false, false, false, false, false};
14
15 /**
16  * アセンブルのプロパティ
17  */
18 ASPROP *asprop;
19
20 /**
21  * アセンブルのエラー定義
22  */
23 static CERR cerr_assemble[] = {
24     { 101, "label already defined" },
25     { 102, "label table is full" },
26     { 103, "label not found" },
27     { 104, "label length is too long" },
28     { 105, "no command in the line" },
29     { 106, "operand mismatch in assemble command" },
30     { 107, "no label in START" },
31     { 108, "not command of operand \"r\"" },
32     { 109, "not command of operand \"r1,r2\"" },
33     { 110, "not command of operand \"r,adr[,x]\"" },
34     { 111, "not command of operand \"adr[,x]\"" },
35     { 112, "not command of no operand" },
36     { 113, "operand too many in COMET II command" },
37     { 117, "operand too many in DC" },
38     { 118, "operand length too long" },
39     { 119, "out of COMET II memory" },
40     { 120, "GR0 in operand x" },
41     { 121, "cannot get operand token" },
42     { 122, "cannot create hash table" },
43     { 123, "unclosed quote" },
44     { 124, "more than one character in literal" },
45     { 125, "not GR in operand x" },
46 };
47
48 #ifndef UNITTEST
49 static WORD getadr(const char *prog, const char *str, PASS pass);
50
51 static WORD getgr(const char *str, bool is_x);
52
53 static WORD getliteral(const char *str, PASS pass);
54
55 static bool assemblecmd(const CMDLINE *cmdl, PASS pass);
56
57 static bool macrocmd(const CMDLINE *cmdl, PASS pass);
58
59 static bool writeIN(const char *ibuf, const char *len, PASS pass);
60
61 static bool writeOUT(const char *obuf, const char *len, PASS pass);
62
63 static bool writeRPUSH(PASS pass);
64
65 static bool writeRPOP(PASS pass);
66
67 static bool cometcmd(const CMDLINE *cmdl, PASS pass);
68
69 static bool writememory(WORD word, WORD adr, PASS pass);
70
71 static void writestr(const char *str, bool literal, PASS pass);
72
73 static void writeDC(const char *str, PASS pass);
74
75 static bool assembleline(const CMDLINE *cmdl, PASS pass);
76
77 static void printline(FILE *stream, const char *filename, int lineno, char *line);
78 #endif
79
80 /**
81  * 汎用レジスタを表す文字列「GR[0-7]」から、レジスタ番号[0-7]をWORD値で返す
82  * 文字列が汎用レジスタを表さない場合は、0xFFFFを返す
83  * is_xがtrueの場合は指標レジスタ。GR0が指定された場合は、COMET IIの仕様によりエラー発生
84  */
85 WORD getgr(const char *str, bool is_x)
86 {
87     assert(str != NULL);
88     WORD r;
89     /* 「GR[0-7]」以外の文字列では、0xFFFFを返して終了 */
90     if(!(strlen(str) == 3 && strncmp(str, "GR", 2) == 0 &&
91          (*(str+2) >= '0' && *(str+2) <= '0' + (GRSIZE - 1))))
92     {
93         return 0xFFFF;
94     }
95     r = (WORD)(*(str+2) - '0');
96     /* GR0は指標レジスタとして用いることができない */
97     if(is_x == true && r == 0x0) {
98         setcerr(120, NULL);    /* GR0 in operand x */
99         return 0x0;
100     }
101     return r;
102 }
103
104 /**
105  * 定数の前に等号(=)をつけて記述されるリテラルを返す
106  * リテラルには、10進定数/16進定数/文字定数が含まれる
107  */
108 WORD getliteral(const char *str, PASS pass)
109 {
110     WORD adr = asprop->lptr;
111     assert(*str == '=');
112     if(*(++str) == '\'') {    /* 文字定数 */
113         writestr(str, true, pass);
114     } else {
115         writememory(nh2word(str), (asprop->lptr)++, pass);
116     }
117     return adr;
118 }
119
120 /**
121  * アセンブラ命令をメモリに書込
122  * 実行に成功した場合はtrue、それ以外の場合はfalseを返す
123  */
124 bool assemblecmd(const CMDLINE *cmdl, PASS pass)
125 {
126     int i = 0;
127     ASCMDID cmdid = 0;
128     bool status = false;
129     ASCMD ascmd[] = {
130         { START, 0, 1, "START" },
131         { END, 0, 0, "END" },
132         { DC, 1, OPDSIZE, "DC" },
133         { DS, 1, 1, "DS" },
134         { 0, 0, 0, NULL }
135     };
136
137     do {
138         if(strcmp(cmdl->cmd, ascmd[i].cmd) == 0) {
139             if(cmdl->opd->opdc < ascmd[i].opdc_min || cmdl->opd->opdc > ascmd[i].opdc_max) {
140                 setcerr(106, NULL);    /* operand count mismatch */
141                 return false;
142             }
143             cmdid = ascmd[i].cmdid;
144             break;
145         }
146     } while(ascmd[++i].cmdid != 0);
147     /* アセンブラ命令 */
148     switch(cmdid)
149     {
150     case START:
151         if(cmdl->label == NULL) {
152             setcerr(107, NULL);    /* no label in START */
153             return false;
154         }
155         /* プログラム名の設定 */
156         asprop->prog = strdup_chk(cmdl->label, "asprop.prog");
157         /* オペランドがある場合、実行開始番地を設定 */
158         if(pass == SECOND && cmdl->opd->opdc == 1) {
159             if((prog->start = getlabel(asprop->prog, cmdl->opd->opdv[0])) == 0xFFFF) {
160                 setcerr(103, cmdl->opd->opdv[0]);    /* label not found */
161             }
162         }
163         status = true;
164         break;
165     case END:
166         /* リテラル領域の設定 */
167         if(pass == FIRST) {
168             asprop->lptr = asprop->ptr;
169         }
170         /* 実行終了番地と次のプログラムの実行開始番地を設定 */
171         else if(pass == SECOND) {
172             prog->end = asprop->lptr;
173         }
174         asprop->prog = NULL;
175         status = true;
176         break;
177     case DS:
178         for(i = 0; i < atoi(cmdl->opd->opdv[0]); i++) {
179             writememory(0x0, (asprop->ptr)++, pass);
180             if(cerr->num > 0) {
181                 return false;
182             }
183         }
184         status = true;
185         break;
186     case DC:
187         for(i = 0; i < cmdl->opd->opdc; i++) {
188             writeDC(cmdl->opd->opdv[i], pass);
189             if(cerr->num > 0) {
190                 return false;
191             }
192         }
193         status = true;
194         break;
195     default:
196         return false;
197     }
198     if(cerr->num > 0) {
199         status = false;
200     }
201     return status;
202 }
203
204 /**
205  *  macrocmd
206  *  マクロ命令をメモリに書込
207  *  書込に成功した場合はtrue、それ以外の場合はfalseを返す
208  */
209 bool macrocmd(const CMDLINE *cmdl, PASS pass)
210 {
211     int i = 0;
212     MACROCMDID cmdid;
213     bool status = false;
214     MACROCMD macrocmd[] = {
215         { IN, 2, 2, "IN" },
216         { OUT, 2, 2, "OUT" },
217         { RPUSH, 0, 0, "RPUSH" },
218         { RPOP, 0, 0, "RPOP" },
219         { 0, 0, 0, NULL }
220     };
221
222     do {
223         if(strcmp(cmdl->cmd, macrocmd[i].cmd) == 0) {
224             if(cmdl->opd->opdc < macrocmd[i].opdc_min ||
225                cmdl->opd->opdc > macrocmd[i].opdc_max)
226             {
227                 setcerr(106, NULL);    /* operand count mismatch */
228                 return false;
229             }
230             cmdid = macrocmd[i].cmdid;
231             break;
232         }
233     } while(macrocmd[++i].cmdid != 0);
234     switch(cmdid)
235     {
236     case IN:
237         status = writeIN(cmdl->opd->opdv[0], cmdl->opd->opdv[1], pass);
238         break;
239     case OUT:
240         status = writeOUT(cmdl->opd->opdv[0], cmdl->opd->opdv[1], pass);
241         break;
242     case RPUSH:
243         status = writeRPUSH(pass);
244         break;
245     case RPOP:
246         status = writeRPOP(pass);
247         break;
248     default:
249         return false;
250     }
251     return status;
252 }
253
254 /**
255  * マクロ命令「IN IBUF,LEN」をメモリに書込
256  *      PUSH 0,GR1
257  *      PUSH 0,GR2
258  *      LAD GR1,IBUF
259  *      LAD GR2,LEN
260  *      SVC 1
261  *      POP GR2
262  *      POP GR1
263  */
264 bool writeIN(const char *ibuf, const char *len, PASS pass)
265 {
266     bool status = false;
267
268     /* PUSH 0,GR1 */
269     writememory(0x7001, (asprop->ptr)++, pass);
270     writememory(0x0, (asprop->ptr)++, pass);
271     /* PUSH 0,GR2 */
272     writememory(0x7002, (asprop->ptr)++, pass);
273     writememory(0x0, (asprop->ptr)++, pass);
274     /* LAD GR1,IBUF */
275     writememory(0x1210, (asprop->ptr)++, pass);
276     writememory(getadr(asprop->prog, ibuf, pass), (asprop->ptr)++, pass);
277     /* LAD GR2,LEN */
278     writememory(0x1220, (asprop->ptr)++, pass);
279     writememory(getadr(asprop->prog, len, pass), (asprop->ptr)++, pass);
280     /* SVC 1 */
281     writememory(0xF000, (asprop->ptr)++, pass);
282     writememory(0x0001, (asprop->ptr)++, pass);
283     /* POP GR2 */
284     writememory(0x7120, (asprop->ptr)++, pass);
285     /* POP GR1 */
286     writememory(0x7110, (asprop->ptr)++, pass);
287     if(cerr->num == 0) {
288         status = true;
289     }
290     return status;
291 }
292
293 /**
294  *  マクロ命令「OUT OBUF,LEN」をメモリに書込
295  *      PUSH 0,GR1
296  *      PUSH 0,GR2
297  *      LAD GR1,OBUF
298  *      LAD GR2,LEN
299  *      SVC 2
300  *      LAD GR1,=#A
301  *      LAD GR2,1
302  *      SVC 2
303  *      POP GR2
304  *      POP GR1
305  */
306 bool writeOUT(const char *obuf, const char *len, PASS pass)
307 {
308     bool status = false;
309
310     /* PUSH 0,GR1 */
311     writememory(0x7001, (asprop->ptr)++, pass);
312     writememory(0x0, (asprop->ptr)++, pass);
313     /* PUSH 0,GR2 */
314     writememory(0x7002, (asprop->ptr)++, pass);
315     writememory(0x0, (asprop->ptr)++, pass);
316     /* LAD GR1,OBUF */
317     writememory(0x1210, (asprop->ptr)++, pass);
318     writememory(getadr(asprop->prog, obuf, pass), (asprop->ptr)++, pass);
319     /* LAD GR2,OLEN */
320     writememory(0x1220, (asprop->ptr)++, pass);
321     writememory(getadr(asprop->prog, len, pass), (asprop->ptr)++, pass);
322     /* SVC 2 */
323     writememory(0xF000, (asprop->ptr)++, pass);
324     writememory(0x0002, (asprop->ptr)++, pass);
325     /* LAD GR1,=#A */
326     writememory(0x1210, (asprop->ptr)++, pass);
327     if(pass == FIRST) {
328         (asprop->ptr)++;
329     } else {
330         writememory(asprop->lptr, (asprop->ptr)++, pass);    /* リテラルのアドレスを書込 */
331     }
332     writememory(0xA, (asprop->lptr)++, pass);
333     /* LAD GR2,=1 */
334     writememory(0x1220, (asprop->ptr)++, pass);
335     if(pass == FIRST) {
336         (asprop->ptr)++;
337     } else {
338         writememory(asprop->lptr, (asprop->ptr)++, pass);    /* リテラルのアドレスを書込 */
339     }
340     writememory(0x1, (asprop->lptr)++, pass);
341     /* SVC 2 */
342     writememory(0xF000, (asprop->ptr)++, pass);
343     writememory(0x0002, (asprop->ptr)++, pass);
344     /* POP GR2 */
345     writememory(0x7120, (asprop->ptr)++, pass);
346     /* POP GR1 */
347     writememory(0x7110, (asprop->ptr)++, pass);
348     if(cerr->num == 0) {
349         status = true;
350     }
351     return status;
352 }
353
354 /** マクロ命令「RPUSH」をメモリに書き込む
355  *       PUSH 0,GR1
356  *       PUSH 0,GR2
357  *       PUSH 0,GR3
358  *       PUSH 0,GR4
359  *       PUSH 0,GR5
360  *       PUSH 0,GR6
361  *       PUSH 0,GR7
362  */
363 bool writeRPUSH(PASS pass)
364 {
365     int i;
366     bool status = false;
367
368     for(i = 1; i <= 7; i++) {
369         writememory(0x7000 + i, (asprop->ptr)++, pass);   /* PUSH GRn */
370         writememory(0x0, (asprop->ptr)++, pass);
371     }
372     if(cerr->num == 0) {
373         status = true;
374     }
375     return status;
376 }
377
378 /**
379  * マクロ命令「RPOP」をメモリに書き込む
380  *      POP GR7
381  *      POP GR6
382  *      POP GR5
383  *      POP GR4
384  *      POP GR3
385  *      POP GR3
386  *      POP GR2
387  *      POP GR1
388  */
389 bool writeRPOP(PASS pass)
390 {
391     int i;
392     bool status = false;
393     for(i = 7; i >= 1; i--) {
394         writememory((0x7100 + (i << 4)), (asprop->ptr)++, pass);  /* POP GRn */
395     }
396     if(cerr->num == 0) {
397         status = true;
398     }
399     return status;
400 }
401
402 /**
403  * 機械語命令をメモリに書込
404  * 書込に成功した場合はtrue、それ以外の場合はfalseを返す
405  */
406 bool cometcmd(const CMDLINE *cmdl, PASS pass)
407 {
408     WORD cmd, adr, r1, r2, x;
409     bool status = false;
410
411     /* オペランドなし */
412     if(cmdl->opd->opdc == 0) {
413         if((cmd = getcmdcode(cmdl->cmd, NONE)) == 0xFFFF) {
414             setcerr(112, cmdl->cmd);    /* not command of no operand */
415             return false;
416         }
417         if(writememory(cmd, (asprop->ptr)++, pass) == true) {
418             status = true;
419         }
420     }
421     /* 第1オペランドは汎用レジスタ */
422     else if((r1 = getgr(cmdl->opd->opdv[0], false)) != 0xFFFF) {
423         /* オペランド数1 */
424         if(cmdl->opd->opdc == 1) {
425             if((cmd = getcmdcode(cmdl->cmd, R_)) == 0xFFFF) {
426                 setcerr(108, cmdl->cmd);    /* not command of operand "r" */
427                 return false;
428             }
429             cmd |= (r1 << 4);
430             if(writememory(cmd, (asprop->ptr)++, pass) == true) {
431                 status = true;
432             }
433         }
434         /* オペランド数2。第2オペランドは汎用レジスタ */
435         else if(cmdl->opd->opdc == 2 && (r2 = getgr(cmdl->opd->opdv[1], false)) != 0xFFFF) {
436             if((cmd = getcmdcode(cmdl->cmd, R1_R2)) == 0xFFFF) {
437                 setcerr(109, cmdl->cmd);    /* not command of operand "r1,r2" */
438                 return false;
439             }
440             cmd |= ((r1 << 4) | r2);
441             if(cerr->num == 0 && writememory(cmd, (asprop->ptr)++, pass) == true) {
442                 status = true;
443             }
444         }
445         /* オペランド数2〜3。第2オペランドはアドレス、 */
446         /* 第3オペランドは指標レジスタとして用いる汎用レジスタ */
447         else if(cmdl->opd->opdc == 2 || cmdl->opd->opdc == 3) {
448             if((cmd = getcmdcode(cmdl->cmd, R_ADR_X_)) == 0xFFFF &&
449                (cmd = getcmdcode(cmdl->cmd, R_ADR_X)) == 0xFFFF)
450             {
451                 setcerr(110, cmdl->cmd);    /* not command of operand "r,adr[,x]" */
452                 return false;
453             }
454             cmd |= (r1 << 4);
455             /* オペランド数3 */
456             if(cmdl->opd->opdc == 3) {
457                 if((x = getgr(cmdl->opd->opdv[2], true)) == 0xFFFF) {
458                     setcerr(125, cmdl->cmd);    /* not GR in operand x */
459                     return false;
460                 }
461                 cmd |= x;
462             }
463             adr = getadr(asprop->prog, cmdl->opd->opdv[1], pass);
464             writememory(cmd, (asprop->ptr)++, pass);
465             writememory(adr, (asprop->ptr)++, pass);
466             if(cerr->num == 0) {
467                 status = true;
468             }
469         } else {
470             setcerr(113, cmdl->cmd);    /* operand too many in COMET II command */
471             return false;
472         }
473     }
474     /* オペランド数1〜2。第1オペランドはアドレス */
475     else if(cmdl->opd->opdc == 1 || cmdl->opd->opdc == 2) {
476         if((cmd = getcmdcode(cmdl->cmd, ADR_X)) == 0xFFFF) {
477             setcerr(111, cmdl->cmd);    /* not command of operand "adr[,x]" */
478             return false;
479         }
480         /* オペランド数2の場合、第2オペランドは指標レジスタとして用いる汎用レジスタ */
481         if(cmdl->opd->opdc == 2) {
482             x = getgr(cmdl->opd->opdv[1], true);
483             if(cerr->num > 0) {
484                 return false;
485             }
486             cmd |= x;
487         }
488         /* CALLの場合はプログラムの入口名を表すラベルを取得 */
489         /* CALL以外の命令の場合と、プログラムの入口名を取得できない場合は、 */
490         /* 同一プログラム内のラベルを取得 */
491         if(pass == SECOND && cmd == 0x8000) {        /* CALL命令 */
492             adr = getlabel(NULL, cmdl->opd->opdv[0]);
493         }
494         if(cmd != 0x8000 || (pass == SECOND && adr == 0xFFFF)) {
495             adr = getadr(asprop->prog, cmdl->opd->opdv[0], pass);
496         }
497         writememory(cmd, (asprop->ptr)++, pass);
498         writememory(adr, (asprop->ptr)++, pass);
499         if(cerr->num == 0) {
500             status = true;
501         }
502     }
503     return status;
504 }
505
506 /**
507  * COMET IIのメモリにアドレス値を書き込む
508  */
509 bool writememory(WORD word, WORD adr, PASS pass)
510 {
511     bool status = false;
512
513     /* COMET IIメモリオーバーの場合 */
514     if(adr >= sys->memsize) {
515         setcerr(119, word2n(adr));    /* out of COMET II memory */
516     }
517     if(cerr->num == 0) {
518         (sys->memory)[adr] = word;
519         if(pass == SECOND && asmode.asdetail == true) {
520             fprintf(stdout, "\t#%04X\t#%04X\n", adr, word);
521         }
522         status = true;
523     }
524     return status;
525 }
526
527 /**
528  * 文字をメモリに書き込む
529  */
530 void writestr(const char *str, bool literal, PASS pass)
531 {
532     assert(cerr->num == 0 && *str == '\'');
533     const char *p = str + 1;
534     bool lw = false;
535
536     for(; ;) {
537         /* 閉じ「'」がないまま文字列が終了した場合 */
538         if(*p == '\0') {
539             setcerr(123, str);    /* unclosed quote */
540             break;
541         }
542         /* 「'」の場合、次の文字が「'」でない場合は正常終了 */
543         if(*p == '\'' && *(++p) != '\'') {
544             break;
545         } else if(literal == true && lw == true) {
546             setcerr(124, str);    /* more than one character in literal */
547             break;
548         }
549         /*リテラルの場合はリテラル領域に書込 */
550         if(literal == true) {
551             writememory(*(p++), (asprop->lptr)++, pass);
552             lw = true;
553         } else {
554             writememory(*(p++), (asprop->ptr)++, pass);
555         }
556     }
557 }
558
559 /**
560  * DC命令の内容を書き込む
561  */
562 void writeDC(const char *str, PASS pass)
563 {
564     WORD adr = 0x0;
565     if(*str == '\'') {
566         writestr(str, false, pass);
567     } else {
568         if(*str == '#' || isdigit(*str) || *str == '-') {
569             adr = nh2word(str);
570         } else {
571             if(pass == SECOND && (adr = getlabel(asprop->prog, str)) == 0xFFFF) {
572                 setcerr(103, str);    /* label not found */
573             }
574         }
575         writememory(adr, (asprop->ptr)++, pass);
576     }
577 }
578
579 /**
580  * 1行をアセンブル
581  */
582 bool assembleline(const CMDLINE *cmdl, PASS pass)
583 {
584     bool status = false;
585     /* 命令がない場合 */
586     if(cmdl->cmd == NULL){
587         /* ラベルが定義されていて命令がない場合はエラー */
588         if(cmdl->label != NULL) {
589             setcerr(105, NULL);    /* no command in the line */
590         }
591     }
592     /* アセンブラ命令の処理 */
593     else if(cerr->num == 0 && assemblecmd(cmdl, pass) == true) {
594         ;
595     }
596     /* マクロ命令の書込 */
597     else if(cerr->num == 0 && macrocmd(cmdl, pass) == true) {
598         ;
599     }
600     /* 機械語命令の書込 */
601     else if(cerr->num == 0 && cometcmd(cmdl, pass) == true) {
602         ;
603     }
604     else if(cerr->num == 0) {
605         setcerr(113, cmdl->cmd);    /* operand too many in COMET II command */
606     }
607     /* エラーが発生していないか確認 */
608     if(cerr->num == 0) {
609         status = true;
610     }
611     return status;
612 }
613
614 /**
615  * ファイルストリームの現在行を番号付きで表示する
616  */
617 void printline(FILE *stream, const char *filename, int lineno, char *line)
618 {
619     fprintf(stream, "%s:%5d:%s", filename, lineno, line);
620 }
621
622 /**
623  * アドレスを返す
624  * アドレスには、リテラル/10進定数/16進定数/アドレス定数が含まれる
625  */
626 WORD getadr(const char *prog, const char *str, PASS pass)
627 {
628     WORD adr = 0x0;
629     if(*str == '=') {
630         adr = getliteral(str, pass);
631     } else if(isdigit(*str) || *str == '-' || *str == '#') {
632         adr = nh2word(str);
633     } else {
634         if(pass == SECOND && (adr = getlabel(prog, str)) == 0xFFFF) {
635             if(prog != NULL) {
636                 setcerr(103, str);    /* label not found */
637             }
638         }
639     }
640     return adr;
641 }
642
643 /**
644  * 指定された名前のファイルをアセンブル
645  * 2回実行される
646  */
647 bool assemble(const char *file, PASS pass)
648 {
649     int lineno = 0;
650     bool status = true;
651     CMDLINE *cmdl;
652     char *line;
653     FILE *fp;
654
655     addcerrlist(ARRAYSIZE(cerr_assemble), cerr_assemble);
656     if((fp = fopen(file, "r")) == NULL) {
657         perror(file);
658         return false;
659     }
660     for(; ;) {
661         cmdl = malloc_chk(sizeof(CMDLINE), "cmdl");
662         line = malloc_chk(LINESIZE + 1, "line");
663         if((line = fgets(line, LINESIZE, fp)) == NULL) {
664             break;
665         }
666         lineno++;
667         if((pass == FIRST && asmode.src == true) ||
668            (pass == SECOND && asmode.asdetail == true))
669         {
670             printline(stdout, file, lineno, line);
671         }
672         if((cmdl = linetok(line)) != NULL) {
673             if(pass == FIRST && cmdl->label != NULL) {
674                 if(addlabel(asprop->prog, cmdl->label, asprop->ptr) == false) {
675                     break;
676                 }
677             }
678             if(assembleline(cmdl, pass) == false) {
679                 break;
680             }
681         }
682         if(cerr->num > 0) {
683             break;
684         }
685         free_chk(line, "line");
686         free_chk(cmdl, "cmdl");
687     }
688     if(cerr->num > 0) {
689         fprintf(stderr, "Assemble error - %d: %s\n", cerr->num, cerr->msg);
690         printline(stderr, file, lineno, line);
691         status = false;
692     }
693     fclose(fp);
694     return status;
695 }
696
697 /**
698  * 引数で指定したファイルにアセンブル結果を書込
699  */
700 void outassemble(const char *file)
701 {
702     FILE *fp;
703
704     if((fp = fopen(file, "w")) == NULL) {
705         perror(file);
706         exit(-1);
707     }
708     fwrite(sys->memory, sizeof(WORD), prog->end, fp);
709     fclose(fp);
710 }