Initial commit.
[rmac] / procln.c
1 ////////////////////////////////////////////////////////////////////////////////////////////////////
2 // RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
3 // PROCLN.C - Line Processing
4 // Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends
5 // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
6 // Source Utilised with the Kind Permission of Landon Dyer
7
8 #include "procln.h"
9 #include "listing.h"
10 #include "amode.h"
11 #include "error.h"
12 #include "sect.h"
13 #include "expr.h"
14 #include "mach.h"
15 #include "direct.h"
16 #include "macro.h"
17 #include "symbol.h"
18 #include "risca.h"
19
20 #define DEF_KW                                              // Declare keyword values 
21 #include "kwtab.h"                                          // Incl generated keyword tables & defs
22
23 #define DEF_MN                                              // Incl 68k keyword definitions
24 #define DECL_MN                                             // Incl 68k keyword state machine tables
25 #include "mntab.h"
26
27 #define DEF_MR
28 #define DECL_MR
29 #include "risckw.h"
30
31 IFENT *ifent;                                               // Current ifent
32 static IFENT ifent0;                                        // Root ifent
33 static IFENT *f_ifent;                                      // Freelist of ifents
34 static int disabled;                                        // Assembly conditionally disabled
35 int just_bss;                                               // 1, ds.b in microprocessor mode 
36 VALUE pcloc;                                                // Value of "PC" at beginning of line 
37 IFENT *ifent;                                               // Current ifent
38 SYM *lab_sym;                                               // Label on line (or NULL)
39
40
41 char extra_stuff[] = "extra (unexpected) text found after addressing mode";
42 char *comma_error = "missing comma";
43 char *syntax_error = "syntax error";
44 char *locgl_error = "cannot GLOBL local symbol";
45 char *lab_ignored = "label ignored";
46
47 // Table to convert an addressing-mode number to a bitmask.
48 LONG amsktab[0112] = {
49    M_DREG, M_DREG, M_DREG, M_DREG,
50    M_DREG, M_DREG, M_DREG, M_DREG,
51
52    M_AREG, M_AREG, M_AREG, M_AREG,
53    M_AREG, M_AREG, M_AREG, M_AREG,
54
55    M_AIND, M_AIND, M_AIND, M_AIND,
56    M_AIND, M_AIND, M_AIND, M_AIND,
57
58    M_APOSTINC, M_APOSTINC, M_APOSTINC, M_APOSTINC,
59    M_APOSTINC, M_APOSTINC, M_APOSTINC, M_APOSTINC,
60
61    M_APREDEC, M_APREDEC, M_APREDEC, M_APREDEC,
62    M_APREDEC, M_APREDEC, M_APREDEC, M_APREDEC,
63
64    M_ADISP, M_ADISP, M_ADISP, M_ADISP,
65    M_ADISP, M_ADISP, M_ADISP, M_ADISP,
66
67    M_AINDEXED, M_AINDEXED, M_AINDEXED, M_AINDEXED,
68    M_AINDEXED, M_AINDEXED, M_AINDEXED, M_AINDEXED,
69
70    M_ABSW,                                                  // 070
71    M_ABSL,                                                  // 071
72    M_PCDISP,                                                // 072
73    M_PCINDEXED,                                             // 073
74    M_IMMED,                                                 // 074
75    0L,                                                      // 075
76    0L,                                                      // 076
77    0L,                                                      // 077
78    M_ABASE,                                                 // 0100
79    M_MEMPOST,                                               // 0101 
80    M_MEMPRE,                                                // 0102 
81    M_PCBASE,                                                // 0103
82    M_PCMPOST,                                               // 0104
83    M_PCMPRE,                                                // 0105
84    M_AM_USP,                                                // 0106
85    M_AM_SR,                                                 // 0107 
86    M_AM_CCR,                                                // 0110
87    M_AM_NONE                                                // 0111 
88 };                                                          // 0112 length
89
90 //
91 // --- Initialize Line Processor -------------------------------------------------------------------
92 //
93
94 void init_procln(void) {
95    disabled = 0;
96    ifent = &ifent0;
97    f_ifent = ifent0.if_prev = NULL;
98    ifent0.if_state = 0;
99 }
100
101 //
102 // --- Line Processor ------------------------------------------------------------------------------
103 //
104
105 void assemble(void) {
106    int state;                                               // Keyword machine state (output)
107    int j;                                                   // Random int, must be fast
108    char *p;                                                 // Random char ptr, must be fast
109    TOKEN *tk;                                               // First token in line
110    char *label;                                             // Symbol (or NULL)
111    char *equate;                                            // Symbol (or NULL)
112    int labtyp = 0;                                          // Label type (':', DCOLON)
113    int equtyp = 0;                                          // Equ type ('=', DEQUALS)
114    VALUE eval;                                              // Expression value
115    WORD eattr;                                              // Expression attributes
116    SYM *esym;                                               // External symbol involved in expr.
117    WORD siz = 0;                                            // Size suffix to mnem/diretve/macro
118    LONG amsk0, amsk1;                                       // Address-type masks for ea0, ea1
119    MNTAB *m;                                                // Code generation table pointer
120    SYM *sy, *sy2;                                           // Symbol (temp usage)
121    char *opname = NULL;                                     // Name of dirctve/mnemonic/macro
122    int listflag;                                            // 0: Don't call listeol()
123    int as68mode = 0;                                        // 1: Handle multiple labels
124    WORD rmask;                                              // Register list, for REG
125    int registerbank;                                        // RISC register bank
126    int riscreg;                                             // RISC register
127
128    listflag = 0;                                            // Initialise listing flag
129
130    loop:                                                    // Line processing loop label
131
132    if(tokln() == TKEOF) {                                   // Get another line of tokens
133       if(list_flag && listflag)                             // Flush last line of source
134          listeol();
135       if(ifent->if_prev != NULL)                            // Check conditional token
136          error("hit EOF without finding matching .endif");
137       return;
138    }
139
140    if(list_flag) {
141       if(listflag && listing > 0) listeol();                // Tell listing generator about EOL
142       lstout((char)(disabled ? '-' : lntag));               // Prepare new line for listing
143       listflag = 1;                                         // OK to call `listeol' now
144       just_bss = 0;                                         // Reset just_bss mode
145    }
146
147    state = -3;                                              // No keyword (just EOL)
148    label = NULL;                                            // No label
149    lab_sym = NULL;                                          // No (exported) label
150    equate = NULL;                                           // No equate
151    tk = tok;                                                // Save first token in line
152    pcloc = (VALUE)sloc;                                     // Set beginning-of-line PC
153
154    loop1:                                                   // Internal line processing loop
155
156    if(*tok == EOL)                                          // Restart loop if end-of-line
157       goto loop;
158
159    if(*tok != SYMBOL) {                                     // First token MUST be a symbol
160       error(syntax_error);
161       goto loop;
162    }
163
164    j = (int)tok[2];                                         // Skip equates (normal statements)
165    if(j == '=' ||       j == DEQUALS || j == SET || j == REG || j == EQUREG || j == CCDEF) {   
166       equate = (char *)tok[1];
167       equtyp = j;
168       tok += 3;
169       goto normal;
170    }
171
172    if(j == ':' || j == DCOLON) {                            // Skip past label (but record it)
173    
174       as68label:
175
176       label = (char *)tok[1];                               // Get label name
177       labtyp = tok[2];                                      // Get label type
178       tok += 3;                                             // Go to next line token
179
180       // Handle multiple labels; if there's another label, go process it, 
181       // and come back at `as68label' above.
182       if(as68_flag) {
183          as68mode = 0;
184          if(*tok == SYMBOL && tok[2] == ':') {
185             as68mode = 1;
186             goto do_label;
187          }
188       }
189    }
190
191    if(*tok == EOL)                                          // EOL is legal here...
192       goto normal;
193
194    if(*tok++ != SYMBOL) {                                   // Next token MUST be a symbol
195       error(syntax_error);
196       goto loop;
197    }
198    opname = p = (char *)*tok++;                             // Store opcode name here
199
200    // Check to see if the SYMBOL is a keyword (a mnemonic or directive).
201    // On output, `state' will have one of the values:
202    //    -3          there was no symbol (EOL)
203    //    -2..-1      the symbol didn't match any keyword
204    //    0..499      vanilla directives (dc, ds, etc.)
205    //    500..999    electric directives (macro, rept, etc.)
206    //    1000..+     mnemonics (move, lsr, etc.)
207    for(state = 0; state >= 0;) {
208       j = mnbase[state] + (int)tolowertab[*p];
209       if(mncheck[j] != state) {                             // Reject, character doesn't match
210          state = -1;                                        // No match
211          break;
212       }
213       if(!*++p) {                                           // Must accept or reject at EOS
214          state = mnaccept[j];                               // (-1 on no terminal match)
215          break;
216       }
217       state = mntab[j];
218    }
219
220    // Check for ".b" ".w" ".l" after directive, macro or mnemonic.
221    siz = SIZN;
222    if(*tok == DOTW) 
223       siz = SIZW, ++tok;
224    else if(*tok == DOTL)
225       siz = SIZL, ++tok;
226    else if(*tok == DOTB)
227       siz = SIZB, ++tok;
228
229    // Do special directives (500..999) (These must be handled in "real time")
230    if(state >= 500 && state < 1000)
231       switch(state) {
232          case MN_IF:
233             d_if();
234             goto loop;
235          case MN_ELSE:
236             d_else();
237             goto loop;
238          case MN_ENDIF:
239             d_endif();
240             goto loop;
241          case MN_IIF:                                       // .iif --- immediate if
242             if(disabled || expr(exprbuf, &eval, &eattr, &esym) != OK)
243                goto loop;
244             if(!(eattr & DEFINED)) {
245                error(undef_error);
246                goto loop;
247             }
248             if(*tok++ != ',') {
249                error(comma_error);
250                goto loop;
251             }
252             if(eval == 0)
253                goto loop;
254             goto loop1;
255          case MN_MACRO:                                     // .macro --- macro definition
256             if(!disabled) {
257                if(label != NULL)
258                   warn(lab_ignored);
259                defmac();
260             }
261             goto loop;
262          case MN_EXITM:                                     // .exitm --- exit macro
263          case MN_ENDM:                                      // .endm --- same as .exitm
264             if(!disabled) {
265                if(label != NULL)
266                   warn(lab_ignored);
267                exitmac();
268             }
269             goto loop;
270          case MN_REPT:
271             if(!disabled) {
272                if(label != NULL)
273                   warn(lab_ignored);
274                defrept();
275             }
276             goto loop;
277          case MN_ENDR:
278             if(!disabled)
279                error("mis-nested .endr");
280             goto loop;
281       }
282
283    normal:
284
285    if(disabled)                                             // Conditionally disabled code
286       goto loop;
287
288    // Do equates
289    if(equate != NULL) {
290       j = 0;                                                // Pick global or local sym enviroment
291       if(*equate == '.')
292          j = curenv;
293
294       sy = lookup(equate, LABEL, j);
295       if(sy == NULL) {
296          sy = newsym(equate, LABEL, j);
297          sy->sattr = 0;
298          if(equtyp == DEQUALS) {
299             if(j) {                                         // Can't GLOBAL a local symbol
300                error(locgl_error);
301                goto loop;
302             }
303             sy->sattr = GLOBAL;
304          }
305       } else if((sy->sattr & DEFINED) && equtyp != SET) {
306          if((equtyp == EQUREG) && (sy->sattre & UNDEF_EQUR)) {
307             sy->sattre |= ~UNDEF_EQUR; 
308             sy->svalue  = 0;
309          } else if((equtyp == CCDEF) && (sy->sattre & UNDEF_CC)) {
310             sy->sattre |= ~UNDEF_CC;
311             sy->svalue = 0;
312          } else {
313             errors("multiple equate to '%s'", sy->sname);
314             goto loop;
315          }
316       }
317
318       // Put symbol in "order of definition" list
319       if(!(sy->sattr & SDECLLIST)) sym_decl(sy);
320
321       // Parse value to equate symbol to;
322       // o  .equr
323       // o  .reg
324       // o  everything else
325       if(equtyp == EQUREG) {
326          if(!rgpu && !rdsp) {                               // Check that we are in a RISC section
327             error(".equr/.regequ must be defined in .gpu/.dsp section");
328             goto loop;
329          }
330          if((*tok >= KW_R0) && (*tok <= KW_R31)) {          // Check for register to equate to
331             sy->sattre  = EQUATEDREG | RISCSYM;             // Mark as equated register
332             riscreg = (*tok - KW_R0);
333             sy->sattre |= (riscreg << 8);                   // Store register number
334             if((tok[1] == ',') && (tok[2] == CONST)) {
335                tok += 3;
336                if(*tok == 0) registerbank = BANK_0;
337                else if(*tok == 1) registerbank = BANK_1;
338                else registerbank = BANK_N;
339             } else {
340                registerbank = BANK_N;
341             }
342             sy->sattre |= regbank;                          // Store register bank
343             eattr = ABS | DEFINED | GLOBAL;
344             eval = 0x80000080 + (riscreg) + (registerbank << 8);
345             tok++;
346          } else if(tok[0] == SYMBOL) {                      // Checking for a register symbol
347             sy2 = lookup((char *)tok[1], LABEL, j);
348             if(!sy2 || !(sy2->sattre & EQUATEDREG)) {       // Make sure symbol is a valid equreg
349                error("invalid GPU/DSP .equr/.regequ definition");
350                goto loop;
351             } else {
352                eattr = ABS | DEFINED | GLOBAL;              // Copy symbols attributes
353                sy->sattre = sy2->sattre;
354                eval = (sy2->svalue & 0xFFFFF0FF);
355                tok += 2;
356             }
357          } else {
358             error("invalid GPU/DSP .equr/.regequ definition");
359             goto loop;
360          }
361       } else if(equtyp == REG) {
362          if(reglist(&rmask) < 0)
363             goto loop;
364          eval = (VALUE)rmask;
365          eattr = ABS | DEFINED;
366       } else if(equtyp == CCDEF) {
367          sy->sattre |= EQUATEDCC;
368          eattr = ABS | DEFINED | GLOBAL;
369          if(tok[0] == SYMBOL) {
370             sy2 = lookup((char *)tok[1], LABEL, j);
371             if(!sy2 || !(sy2->sattre & EQUATEDCC)) {
372                error("invalid gpu/dsp .ccdef definition");
373                goto loop;
374             } else {
375                eattr = ABS | DEFINED | GLOBAL;
376                sy->sattre = sy2->sattre;
377                eval = sy2->svalue;
378                tok += 2;
379             }
380          } else
381             if(expr(exprbuf, &eval, &eattr, &esym) != OK)
382                goto loop;
383       } else if(*tok == SYMBOL) {  //equ a equr
384          sy2 = lookup((char *)tok[1], LABEL, j);
385          if(sy2 && (sy2->sattre & EQUATEDREG)) {
386             sy->stype = sy2->stype;
387             sy->sattr = sy2->sattr;
388             sy->sattre = sy2->sattre;
389             sy->svalue = (sy2->svalue & 0xFFFFF0FF);
390             goto loop;
391          } else 
392             if(expr(exprbuf, &eval, &eattr, &esym) != OK)
393                goto loop;
394       } else
395          if(expr(exprbuf, &eval, &eattr, &esym) != OK)
396             goto loop;
397
398       if(!(eattr & DEFINED)) {
399          error(undef_error);
400          goto loop;
401       }
402
403       
404       sy->sattr |= eattr | EQUATED;                         // Symbol inherits value and attributes
405       sy->svalue = eval;
406       if(list_flag)                                         // Put value in listing
407          listvalue(eval);
408
409       at_eol();                                             // Must be at EOL now
410       goto loop;
411    }
412
413    // Do labels
414    if(label != NULL) {
415
416       do_label:
417
418       j = 0;
419       if(*label == '.')
420          j = curenv;
421       sy = lookup(label, LABEL, j);
422
423       if(sy == NULL) {
424          sy = newsym(label, LABEL, j);
425          sy->sattr = 0;
426          sy->sattre = RISCSYM;
427       } else if(sy->sattr & DEFINED) {
428          errors("multiply-defined label '%s'", label);
429          goto loop;
430       }
431
432       // Put symbol in "order of definition" list
433       if(!(sy->sattr & SDECLLIST)) sym_decl(sy);
434
435       if(orgactive) {
436          sy->svalue = orgaddr;
437          sy->sattr |= ABS | DEFINED | EQUATED;
438       } else {
439          sy->svalue = sloc;
440          sy->sattr |= DEFINED | cursect;
441       }
442
443       lab_sym = sy;
444       if(!j)
445          ++curenv;
446
447       if(labtyp == DCOLON) {                                // Make label global
448          if(j) {
449             error(locgl_error);
450             goto loop;
451          }
452          sy->sattr |= GLOBAL;
453       }
454
455       // If we're in as68 mode, and there's another label, go back and handle it
456       if(as68_flag && as68mode)
457          goto as68label;
458    }
459
460    // Punt on EOL
461    if(state == -3)
462       goto loop;
463
464    // If we are in GPU or DSP mode and still in need of a mnemonic then search for one
465    if((rgpu || rdsp) && (state < 0 || state >= 1000)) {
466       for(state = 0, p = opname; state >= 0;) {
467          j = mrbase[state] + (int)tolowertab[*p];
468          if(mrcheck[j] != state)        {                          // Reject, character doesn't match
469             state = -1;                                     // No match
470             break;
471          }
472
473          if(!*++p) {                                        // Must accept or reject at EOS
474             state = mraccept[j];                            // (-1 on no terminal match)
475             break;
476          }
477          state = mrtab[j];
478       }
479
480       // Call RISC code generator if we found a mnemonic
481       if(state >= 3000) {
482          risccg(state);
483          goto loop;
484       }
485    }
486
487    // Invoke macro or complain about bad mnemonic
488    if(state < 0) {
489       if((sy = lookup(opname, MACRO, 0)) != NULL) 
490          invokemac(sy, siz);
491       else errors("unknown op '%s'", opname);
492       goto loop;
493    }
494
495    // Call directive handlers
496    if(state < 500) {
497       (*dirtab[state])(siz);
498       goto loop;
499    }
500
501    // Do mnemonics
502    // o  can't deposit instrs in BSS or ABS
503    // o  do automatic .EVEN for instrs
504    // o  allocate space for largest possible instr
505    // o  can't do ".b" operations with an address register
506    if(scattr & SBSS) {
507       error("cannot initialize non-storage (BSS) section");
508       goto loop;
509    }
510
511    if(sloc & 1)                                             // Automatic .even
512       auto_even();
513
514    if(challoc - ch_size < 18)                               // Make sure have space in current chunk
515       chcheck(0L);
516
517    m = &machtab[state - 1000];
518    if(m->mnattr & CGSPECIAL) {                              // Call special-mode handler
519       (*m->mnfunc)(m->mninst, siz);
520       goto loop;
521    }
522
523    if(amode(1) < 0)                                         // Parse 0, 1 or 2 addr modes
524       goto loop;
525
526    if(*tok != EOL)
527       error(extra_stuff);
528
529    amsk0 = amsktab[am0];
530    amsk1 = amsktab[am1];
531
532    // Catch attempts to use ".B" with an address register (yes, this check does work at this level)
533    if(siz == SIZB && (am0 == AREG || am1 == AREG)) {
534       error("cannot use '.b' with an address register");
535       goto loop;
536    }
537
538    for(;;) {
539       if((m->mnattr & siz) && (amsk0 & m->mn0) != 0 && (amsk1 & m->mn1) != 0) {
540          (*m->mnfunc)(m->mninst, siz);
541          goto loop;
542       }
543       m = &machtab[m->mncont];
544    }
545 }
546
547 // 
548 // --- .if, Start Conditional Assembly -------------------------------------------------------------
549 //
550
551 int d_if(void) {
552    IFENT *rif;
553    WORD eattr;
554    VALUE eval;
555    SYM *esym;
556
557    // Alloc an IFENTRY
558    if((rif = f_ifent) == NULL) rif = (IFENT *)amem((LONG)sizeof(IFENT));
559    else f_ifent = rif->if_prev;
560
561    rif->if_prev = ifent;
562    ifent = rif;
563
564    if(!disabled) {
565       if(expr(exprbuf, &eval, &eattr, &esym) != OK) return(0);
566       if((eattr & DEFINED) == 0) return(error(undef_error));
567       disabled = !eval;
568    }
569    rif->if_state = (WORD)disabled;
570    return(0);
571 }
572
573 // 
574 // --- .else, Do Alternate Case For .if ------------------------------------------------------------
575 //
576
577 int d_else(void) {
578    IFENT *rif;
579
580    rif = ifent;
581
582    if(rif->if_prev == NULL) return(error("mismatched .else"));
583
584    if(disabled) disabled = rif->if_prev->if_state;
585    else disabled = 1;
586
587    rif->if_state = (WORD)disabled;
588    return(0);
589 }
590
591 //
592 // -------------------------------------------------------------------------------------------------
593 // .endif, End of conditional assembly block
594 // This is also called by fpop() to pop levels of IFENTs in case a macro or include file exits 
595 // early with `exitm' or `end'.
596 // -------------------------------------------------------------------------------------------------
597 //
598
599 int d_endif(void) {
600    IFENT *rif;
601
602    rif = ifent;
603    if(rif->if_prev == NULL) return(error("mismatched .endif"));
604
605    ifent = rif->if_prev;
606    disabled = rif->if_prev->if_state;
607    rif->if_prev = f_ifent;
608    f_ifent = rif;
609    return(0);
610 }
611