Possible fix for bug #72 (no warning on unclosed .ifs).
[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
9 #include "procln.h"
10 #include "listing.h"
11 #include "amode.h"
12 #include "error.h"
13 #include "sect.h"
14 #include "expr.h"
15 #include "mach.h"
16 #include "direct.h"
17 #include "macro.h"
18 #include "symbol.h"
19 #include "riscasm.h"
20
21 #define DEF_KW                                  // Declare keyword values 
22 #include "kwtab.h"                              // Incl generated keyword tables & defs
23
24 #define DEF_MN                                  // Incl 68k keyword definitions
25 #define DECL_MN                                 // Incl 68k keyword state machine tables
26 #include "mntab.h"
27
28 #define DEF_MR
29 #define DECL_MR
30 #include "risckw.h"
31
32
33 IFENT * ifent;                                  // Current ifent
34 static IFENT ifent0;                    // Root ifent
35 static IFENT * f_ifent;                 // Freelist of ifents
36 static int disabled;                    // Assembly conditionally disabled
37 int just_bss;                                   // 1, ds.b in microprocessor mode 
38 VALUE pcloc;                                    // Value of "PC" at beginning of line 
39 SYM * lab_sym;                                  // Label on line (or NULL)
40
41 const char extra_stuff[] = "extra (unexpected) text found after addressing mode";
42 const char comma_error[] = "missing comma";
43 const char syntax_error[] = "syntax error";
44 const char locgl_error[] = "cannot GLOBL local symbol";
45 const 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 // Function prototypes
92 int HandleLabel(char *, int);
93
94
95 //
96 // Initialize line processor
97 //
98 void InitLineProcessor(void)
99 {
100         disabled = 0;
101         ifent = &ifent0;
102         f_ifent = ifent0.if_prev = NULL;
103         ifent0.if_state = 0;
104 }
105
106
107 //
108 // Line processor
109 //
110 void Assemble(void)
111 {
112         int state;                                      // Keyword machine state (output)
113         int j;                                          // Random int, must be fast
114         char * p;                                       // Random char ptr, must be fast
115         TOKEN * tk;                                     // First token in line
116         char * label;                           // Symbol (or NULL)
117         char * equate;                          // Symbol (or NULL)
118         int labtyp = 0;                         // Label type (':', DCOLON)
119         int equtyp = 0;                         // Equ type ('=', DEQUALS)
120         VALUE eval;                                     // Expression value
121         WORD eattr;                                     // Expression attributes
122         SYM * esym;                                     // External symbol involved in expr.
123         WORD siz = 0;                           // Size suffix to mnem/diretve/macro
124         LONG amsk0, amsk1;                      // Address-type masks for ea0, ea1
125         MNTAB * m;                                      // Code generation table pointer
126         SYM * sy, * sy2;                        // Symbol (temp usage)
127         char * opname = NULL;           // Name of dirctve/mnemonic/macro
128         int listflag;                           // 0: Don't call listeol()
129         WORD rmask;                                     // Register list, for REG
130         int registerbank;                       // RISC register bank
131         int riscreg;                            // RISC register
132         listflag = 0;                           // Initialise listing flag
133
134 loop:                                                   // Line processing loop label
135
136         // Get another line of tokens
137         if (TokenizeLine() == TKEOF)
138         {
139 if (debug) printf("Assemble: Found TKEOF flag...\n");
140                 if (list_flag && listflag)                      // Flush last line of source
141                         listeol();
142
143                 if (ifent->if_prev != NULL)                     // Check conditional token
144                         error("hit EOF without finding matching .endif");
145
146                 return;
147         }
148
149         DEBUG DumpTokenBuffer();
150
151         if (list_flag)
152         {
153                 if (listflag && listing > 0)
154                         listeol();                                              // Tell listing generator about EOL
155
156                 lstout((char)(disabled ? '-' : lntag)); // Prepare new line for listing
157                 listflag = 1;                                           // OK to call `listeol' now
158                 just_bss = 0;                                           // Reset just_bss mode
159         }
160
161         state = -3;                                                             // No keyword (just EOL)
162         label = NULL;                                                   // No label
163         lab_sym = NULL;                                                 // No (exported) label
164         equate = NULL;                                                  // No equate
165         tk = tok;                                                               // Save first token in line
166         pcloc = (VALUE)sloc;                                    // Set beginning-of-line PC
167
168 loop1:                                                                          // Internal line processing loop
169
170         if (*tok == EOL)                                                // Restart loop if end-of-line
171                 goto loop;
172
173         // First token MUST be a symbol (Shamus: not sure why :-/)
174         if (*tok != SYMBOL)
175         {
176                 error("syntax error; expected symbol");
177                 goto loop;
178         }
179
180         j = (int)tok[2];                                                // Skip equates (normal statements)
181
182         if (j == '=' || j == DEQUALS || j == SET || j == REG || j == EQUREG || j == CCDEF)
183         {
184                 equate = string[tok[1]];
185                 equtyp = j;
186                 tok += 3;
187                 goto normal;
188         }
189
190         // Skip past label (but record it)
191         if (j == ':' || j == DCOLON)
192         {
193 as68label:
194                 label = string[tok[1]];                         // Get label name
195                 labtyp = tok[2];                                        // Get label type
196                 tok += 3;                                                       // Go to next line token
197
198                 // AS68 MODE:
199                 // Looks like another label follows the previous one, so handle
200                 // the previous one until there aren't any more
201                 if (as68_flag && (*tok == SYMBOL && tok[2] == ':'))
202                 {
203                         if (HandleLabel(label, labtyp) != 0)
204                                 goto loop;
205
206                         goto as68label;
207                 }
208         }
209
210         // EOL is legal here...
211         if (*tok == EOL)
212                 goto normal;
213
214         // Next token MUST be a symbol
215         if (*tok++ != SYMBOL)
216         {
217                 error("syntax error; expected symbol");
218                 goto loop;
219         }
220
221         opname = p = string[*tok++];
222
223         // Check to see if the SYMBOL is a keyword (a mnemonic or directive).
224         // On output, `state' will have one of the values:
225         //    -3          there was no symbol (EOL)
226         //    -2..-1      the symbol didn't match any keyword
227         //    0..499      vanilla directives (dc, ds, etc.)
228         //    500..999    electric directives (macro, rept, etc.)
229         //    1000..+     mnemonics (move, lsr, etc.)
230         for(state=0; state>=0;)
231         {
232                 j = mnbase[state] + (int)tolowertab[*p];
233
234                 // Reject, character doesn't match
235                 if (mncheck[j] != state)
236                 {
237                         state = -1;                                             // No match
238                         break;
239                 }
240
241                 // Must accept or reject at EOS
242                 if (!*++p)
243                 {
244                         state = mnaccept[j];                    // (-1 on no terminal match)
245                         break;
246                 }
247
248                 state = mntab[j];
249         }
250
251         // Check for ".b" ".w" ".l" after directive, macro or mnemonic.
252         siz = SIZN;
253
254         if (*tok == DOTW) 
255                 siz = SIZW, tok++;
256         else if (*tok == DOTL)
257                 siz = SIZL, tok++;
258         else if (*tok == DOTB)
259                 siz = SIZB, tok++;
260
261         // Do special directives (500..999) (These must be handled in "real time")
262         if (state >= 500 && state < 1000)
263         {
264                 switch (state)
265                 {
266                 case MN_IF:
267                         d_if();
268                 goto loop;
269                 case MN_ELSE:
270                         d_else();
271                         goto loop;
272                 case MN_ENDIF:
273                         d_endif();
274                         goto loop;
275                 case MN_IIF:                                            // .iif --- immediate if
276                         if (disabled || expr(exprbuf, &eval, &eattr, &esym) != OK)
277                                 goto loop;
278
279                         if (!(eattr & DEFINED))
280                         {
281                                 error(undef_error);
282                                 goto loop;
283                         }
284
285                         if (*tok++ != ',')
286                         {
287                                 error(comma_error);
288                                 goto loop;
289                         }
290
291                         if (eval == 0)
292                                 goto loop;
293
294                         goto loop1;
295                 case MN_MACRO:                                          // .macro --- macro definition
296                         if (!disabled)
297                         {
298                                 // Label on a macro definition is bad mojo... Warn the user
299                                 if (label != NULL)
300                                         warn(lab_ignored);
301
302                                 DefineMacro();
303                         }
304
305                         goto loop;
306                 case MN_EXITM:                                          // .exitm --- exit macro
307                 case MN_ENDM:                                           // .endm --- same as .exitm
308                         if (!disabled)
309                         {
310                                 if (label != NULL)
311                                         warn(lab_ignored);
312
313                                 ExitMacro();
314                         }
315
316                         goto loop;
317                 case MN_REPT:
318                         if (!disabled)
319                         {
320                                 // Handle labels on REPT directive lines...
321                                 if (label)
322                                 {
323                                         if (HandleLabel(label, labtyp) != 0)
324                                                 goto loop;
325                                 }
326
327                                 defrept();
328                         }
329
330                         goto loop;
331                 case MN_ENDR:
332                         if (!disabled)
333                                 error("mis-nested .endr");
334
335                         goto loop;
336                 }
337         }
338
339 normal:
340         if (disabled)                                                   // Conditionally disabled code
341                 goto loop;
342
343         // Do equates
344         if (equate != NULL)
345         {
346                 // Pick global or local symbol enviroment
347                 j = (*equate == '.' ? curenv : 0);
348                 sy = lookup(equate, LABEL, j);
349
350                 if (sy == NULL)
351                 {
352                         sy = NewSymbol(equate, LABEL, j);
353                         sy->sattr = 0;
354
355                         if (equtyp == DEQUALS)
356                         {
357                                 // Can't GLOBAL a local symbol
358                                 if (j)
359                                 {
360                                         error(locgl_error);
361                                         goto loop;
362                                 }
363
364                                 sy->sattr = GLOBAL;
365                         }
366                 }
367                 else if ((sy->sattr & DEFINED) && equtyp != SET)
368                 {
369                         if ((equtyp == EQUREG) && (sy->sattre & UNDEF_EQUR))
370                         {
371 //REALLY?                               sy->sattre |= ~UNDEF_EQUR; 
372                                 sy->sattre &= ~UNDEF_EQUR; 
373                                 sy->svalue = 0;
374                         }
375                         else if ((equtyp == CCDEF) && (sy->sattre & UNDEF_CC))
376                         {
377 //REALLY?                               sy->sattre |= ~UNDEF_CC;
378                                 sy->sattre &= ~UNDEF_CC;
379                                 sy->svalue = 0;
380                         }
381                         else
382                         {
383                                 errors("multiple equate to '%s'", sy->sname);
384                                 goto loop;
385                         }
386                 }
387
388                 // Put symbol in "order of definition" list if it's not already there
389                 AddToSymbolDeclarationList(sy);
390
391                 // Parse value to equate symbol to;
392                 // o  .equr
393                 // o  .reg
394                 // o  everything else
395                 if (equtyp == EQUREG)
396                 {
397 //Linko's request to issue a warning on labels that equated to the same
398 //register would go here. Not sure how to implement it though. :-/
399 /*
400 Maybe like this way:
401 have an array of bools with 64 entries. Whenever a register is equated, set the
402 corresponding register bool to true. Whenever it's undef'ed, set it to false.
403 When checking to see if it's already been equated, issue a warning.
404 */
405                         // Check that we are in a RISC section
406                         if (!rgpu && !rdsp)
407                         {
408                                 error(".equr/.regequ must be defined in .gpu/.dsp section");
409                                 goto loop;
410                         }
411
412                         // Check for register to equate to
413                         if ((*tok >= KW_R0) && (*tok <= KW_R31))
414                         {
415 //                              sy->sattre  = EQUATEDREG | RISCSYM;     // Mark as equated register
416                                 sy->sattre  = EQUATEDREG;       // Mark as equated register
417                                 riscreg = (*tok - KW_R0);
418 //is there any reason to do this, since we're putting this in svalue?
419 //i'm thinking, no. Let's test that out! :-D
420 //                              sy->sattre |= (riscreg << 8);           // Store register number
421 //everything seems to build fine without it... We'll leave it here Just In Case(tm)
422
423 #define DEBODGE_REGBANK
424 #ifdef DEBODGE_REGBANK
425                                 // Default is current state of "regbank"
426                                 registerbank = regbank;
427 #else
428                                 // Default is no register bank specified
429                                 registerbank = BANK_N;
430 #endif
431
432                                 // Check for ",<bank #>" override notation
433                                 if ((tok[1] == ',') && (tok[2] == CONST))
434                                 {
435                                         // Advance token pointer to the constant
436                                         tok += 3;
437
438                                         // Anything other than a 0 or a 1 will result in "No Bank"
439                                         if (*tok == 0)
440                                                 registerbank = BANK_0;
441                                         else if (*tok == 1)
442                                                 registerbank = BANK_1;
443                                 }
444
445 #ifdef DEBODGE_REGBANK
446                                 sy->sattre |= registerbank;     // Store register bank
447 #else
448 // What needs to happen here is to prime registerbank with regbank, then use
449 // registerbank down below for the bank marking.
450 #warning "!!! regbank <-> registerbank confusion here !!!"
451 // The question here is why, if we're allowed to override the ".regbankN" rules
452 // above, then why is it using the one set by the directive in the extended
453 // attributes and not in what ends up in symbol->svalue?
454 // ".regbankN" is not an original Madmac directive, so it's suspect
455                                 sy->sattre |= regbank;          // Store register bank
456 #endif
457                                 eattr = ABS | DEFINED | GLOBAL;
458 // & what does this $80000080 constant mean???
459 //                              eval = 0x80000080 + (riscreg) + (registerbank << 8);
460                                 eval = riscreg;
461                                 tok++;
462                         }
463                         // Checking for a register symbol
464                         else if (tok[0] == SYMBOL)
465                         {
466                                 sy2 = lookup(string[tok[1]], LABEL, j);
467
468                                 // Make sure symbol is a valid equreg
469                                 if (!sy2 || !(sy2->sattre & EQUATEDREG))
470                                 {
471                                         error("invalid GPU/DSP .equr/.regequ definition");
472                                         goto loop;
473                                 }
474                                 else
475                                 {
476                                         eattr = ABS | DEFINED | GLOBAL; // Copy symbols attributes
477                                         sy->sattre = sy2->sattre;
478                                         eval = (sy2->svalue & 0xFFFFF0FF);
479                                         tok += 2;
480                                 }
481                         }
482                         else
483                         {
484                                 error("invalid GPU/DSP .equr/.regequ definition");
485                                 goto loop;
486                         }
487                 }
488                 else if (equtyp == REG)
489                 {
490                         if (reglist(&rmask) < 0)
491                                 goto loop;
492
493                         eval = (VALUE)rmask;
494                         eattr = ABS | DEFINED;
495                 }
496                 else if (equtyp == CCDEF)
497                 {
498                         sy->sattre |= EQUATEDCC;
499                         eattr = ABS | DEFINED | GLOBAL;
500
501                         if (tok[0] == SYMBOL)
502                         {
503                                 sy2 = lookup(string[tok[1]], LABEL, j);
504
505                                 if (!sy2 || !(sy2->sattre & EQUATEDCC))
506                                 {
507                                         error("invalid gpu/dsp .ccdef definition");
508                                         goto loop;
509                                 }
510                                 else
511                                 {
512                                         eattr = ABS | DEFINED | GLOBAL;
513                                         sy->sattre = sy2->sattre;
514                                         eval = sy2->svalue;
515                                         tok += 2;
516                                 }
517                         }
518                         else if (expr(exprbuf, &eval, &eattr, &esym) != OK)
519                                 goto loop;
520                 }
521                 //equ a equr
522                 else if (*tok == SYMBOL)
523                 {
524                         sy2 = lookup(string[tok[1]], LABEL, j);
525
526                         if (sy2 && (sy2->sattre & EQUATEDREG))
527                         {
528                                 sy->stype = sy2->stype;
529                                 sy->sattr = sy2->sattr;
530                                 sy->sattre = sy2->sattre;
531 //ICK                           sy->svalue = (sy2->svalue & 0xFFFFF0FF);
532                                 sy->svalue = sy2->svalue;
533                                 goto loop;
534                         }
535                         else if (expr(exprbuf, &eval, &eattr, &esym) != OK)
536                                 goto loop;
537                 }
538                 else if (expr(exprbuf, &eval, &eattr, &esym) != OK)
539                         goto loop;
540
541                 if (!(eattr & DEFINED))
542                 {
543                         error(undef_error);
544                         goto loop;
545                 }
546
547                 sy->sattr |= eattr | EQUATED;   // Symbol inherits value and attributes
548                 sy->svalue = eval;
549
550                 if (list_flag)                                  // Put value in listing
551                         listvalue(eval);
552
553                 at_eol();                                               // Must be at EOL now
554                 goto loop;
555         }
556
557         // Do labels
558         if (label != NULL)
559         {
560                 // Non-zero == error occurred
561                 if (HandleLabel(label, labtyp) != 0)
562                         goto loop;
563         }
564
565         // Punt on EOL
566         if (state == -3)
567                 goto loop;
568
569         // If we are in GPU or DSP mode and still in need of a mnemonic then search
570         // for one
571         if ((rgpu || rdsp) && (state < 0 || state >= 1000))
572         {
573                 for(state=0, p=opname; state>=0;)
574                 {
575                         j = mrbase[state] + (int)tolowertab[*p];
576
577                         // Reject, character doesn't match
578                         if (mrcheck[j] != state)
579                         {
580                                 state = -1;                                     // No match
581                                 break;
582                         }
583
584                         // Must accept or reject at EOS
585                         if (!*++p)
586                         {
587                                 state = mraccept[j];            // (-1 on no terminal match)
588                                 break;
589                         }
590
591                         state = mrtab[j];
592                 }
593
594                 // Call RISC code generator if we found a mnemonic
595                 if (state >= 3000)
596                 {
597                         GenerateRISCCode(state);
598                         goto loop;
599                 }
600         }
601
602         // Invoke macro or complain about bad mnemonic
603         if (state < 0)
604         {
605                 if ((sy = lookup(opname, MACRO, 0)) != NULL) 
606                         InvokeMacro(sy, siz);
607                 else
608                         errors("unknown op '%s'", opname);
609
610                 goto loop;
611         }
612
613         // Call directive handlers
614         if (state < 500)
615         {
616                 (*dirtab[state])(siz);
617                 goto loop;
618         }
619
620         // Do mnemonics
621         // o  can't deposit instrs in BSS or ABS
622         // o  do automatic .EVEN for instrs
623         // o  allocate space for largest possible instr
624         // o  can't do ".b" operations with an address register
625         if (scattr & SBSS)
626         {
627                 error("cannot initialize non-storage (BSS) section");
628                 goto loop;
629         }
630
631         if (sloc & 1)                                   // Automatic .even
632                 auto_even();
633
634         if (challoc - ch_size < 18)             // Make sure have space in current chunk
635                 chcheck(0);
636
637         m = &machtab[state - 1000];
638
639         // Call special-mode handler
640         if (m->mnattr & CGSPECIAL)
641         {
642                 (*m->mnfunc)(m->mninst, siz);
643                 goto loop;
644         }
645
646         if (amode(1) < 0)                               // Parse 0, 1 or 2 addr modes
647                 goto loop;
648
649         if (*tok != EOL)
650                 error(extra_stuff);
651
652         amsk0 = amsktab[am0];
653         amsk1 = amsktab[am1];
654
655         // Catch attempts to use ".B" with an address register (yes, this check
656         // does work at this level)
657         if (siz == SIZB && (am0 == AREG || am1 == AREG))
658         {
659                 error("cannot use '.b' with an address register");
660                 goto loop;
661         }
662
663         for(;;)
664         {
665                 if ((m->mnattr & siz) && (amsk0 & m->mn0) != 0 && (amsk1 & m->mn1) != 0)
666                 {
667                         (*m->mnfunc)(m->mninst, siz);
668                         goto loop;
669                 }
670
671                 m = &machtab[m->mncont];
672         }
673 }
674
675
676 //
677 // Handle the creation of labels
678 //
679 int HandleLabel(char * label, int labelType)
680 {
681         // Check for dot in front of label; means this is a local label if present
682         int environment = (*label == '.' ? curenv : 0);
683         SYM * symbol = lookup(label, LABEL, environment);
684
685         if (symbol == NULL)
686         {
687                 symbol = NewSymbol(label, LABEL, environment);
688                 symbol->sattr = 0;
689 //              symbol->sattre = RISCSYM;
690                 symbol->sattre = 0;
691         }
692         else if (symbol->sattr & DEFINED)
693                 return errors("multiply-defined label '%s'", label);
694
695         // Put symbol in "order of definition" list if it's not already in it
696         AddToSymbolDeclarationList(symbol);
697
698         if (orgactive)
699         {
700                 symbol->svalue = orgaddr;
701                 symbol->sattr |= ABS | DEFINED | EQUATED;
702         }
703         else
704         {
705                 symbol->svalue = sloc;
706                 symbol->sattr |= DEFINED | cursect;
707         }
708
709         lab_sym = symbol;
710
711         if (0 == environment)
712                 curenv++;
713
714         // Make label global if it has a double colon
715         if (labelType == DCOLON)
716         {
717                 if (environment != 0)
718                         return error(locgl_error);
719
720                 symbol->sattr |= GLOBAL;
721         }
722
723         return 0;
724 }
725
726
727 // 
728 // .if, Start conditional assembly
729 //
730 int d_if(void)
731 {
732         IFENT * rif;
733         WORD eattr;
734         VALUE eval;
735         SYM * esym;
736
737         // Alloc an IFENTRY
738         if ((rif = f_ifent) == NULL)
739                 rif = (IFENT *)malloc(sizeof(IFENT));
740         else
741                 f_ifent = rif->if_prev;
742
743         rif->if_prev = ifent;
744         ifent = rif;
745
746         if (!disabled)
747         {
748                 if (expr(exprbuf, &eval, &eattr, &esym) != OK)
749                         return 0;
750
751                 if ((eattr & DEFINED) == 0)
752                         return error(undef_error);
753
754                 disabled = !eval;
755         }
756
757         rif->if_state = (WORD)disabled;
758         return 0;
759 }
760
761
762 // 
763 // .else, Do alternate case for .if
764 //
765 int d_else(void)
766 {
767         IFENT * rif = ifent;
768
769         if (rif->if_prev == NULL)
770                 return error("mismatched .else");
771
772         if (disabled)
773                 disabled = rif->if_prev->if_state;
774         else
775                 disabled = 1;
776
777         rif->if_state = (WORD)disabled;
778         return 0;
779 }
780
781
782 //
783 // .endif, End of conditional assembly block
784 // This is also called by fpop() to pop levels of IFENTs in case a macro or
785 // include file exits early with `exitm' or `end'.
786 //
787 int d_endif (void)
788 {
789         IFENT * rif = ifent;
790
791         if (rif->if_prev == NULL)
792                 return error("mismatched .endif");
793
794         ifent = rif->if_prev;
795         disabled = rif->if_prev->if_state;
796         rif->if_prev = f_ifent;
797         f_ifent = rif;
798         return 0;
799 }
800