]> Shamusworld >> Repos - rmac/blob - sect.c
Code cleanups.
[rmac] / sect.c
1 //
2 // RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
3 // SECT.C - Code Generation, Fixups and Section Management
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 "sect.h"
10 #include "error.h"
11 #include "mach.h"
12 #include "token.h"
13 #include "mark.h"
14 #include "expr.h"
15 #include "symbol.h"
16 #include "risca.h"
17 #include "listing.h"
18
19 // Section descriptors
20 SECT sect[NSECTS];                                          // All sections... 
21 int cursect;                                                // Current section number
22
23 // These are copied from the section descriptor, the current code chunk descriptor and the current
24 // fixup chunk descriptor when a switch is made into a section.  They are copied back to the 
25 // descriptors when the section is left.
26 WORD scattr;                                                // Section attributes 
27 LONG sloc;                                                  // Current loc in section 
28
29 CHUNK *scode;                                               // Current (last) code chunk 
30 LONG challoc;                                               // #bytes alloc'd to code chunk 
31 LONG ch_size;                                               // #bytes used in code chunk 
32 char *chptr;                                                // Deposit point in code chunk buffer 
33
34 CHUNK *sfix;                                                // Current (last) fixup chunk
35 LONG fchalloc;                                              // #bytes alloc'd to fixup chunk
36 LONG fchsize;                                               // #bytes used in fixup chunk
37 PTR fchptr;                                                 // Deposit point in fixup chunk buffer
38
39 unsigned fwdjump[MAXFWDJUMPS];                              // forward jump check table
40 unsigned fwindex = 0;                                       // forward jump index
41
42 // Return a size (SIZB, SIZW, SIZL) or 0, depending on what kind of fixup is associated
43 // with a location.
44 static char fusiztab[] = {
45    0,                                                       // FU_QUICK
46    1,                                                       // FU_BYTE
47    2,                                                       // FU_WORD
48    2,                                                       // FU_WBYTE
49    4,                                                       // FU_LONG
50    1,                                                       // FU_BBRA
51    0,                                                       // (unused)
52    1,                                                       // FU_6BRA
53 };
54
55 // Offset to REAL fixup location
56 static char fusizoffs[] = {
57    0,                                                       // FU_QUICK
58    0,                                                       // FU_BYTE
59    0,                                                       // FU_WORD
60    1,                                                       // FU_WBYTE
61    0,                                                       // FU_LONG
62    1,                                                       // FU_BBRA
63    0,                                                       // (unused)
64    0,                                                       // FU_6BRA
65 };
66
67 //
68 // --- Make a New (Clean) Section ------------------------------------------------------------------
69 //
70
71 void mksect(int sno, WORD attr) {
72    SECT *p;                                                 // Section pointer
73
74    p = &sect[sno];
75    p->scattr = attr;
76    p->sloc = 0;
77    p->scode = p->sfcode = NULL;
78    p->sfix = p->sffix = NULL;
79 }
80
81 //
82 // --- Switch to Another Section (Copy Section & Chunk Descriptors to Global Vars for Fast Access) -
83 //
84
85 void switchsect(int sno) {
86    SECT *p;                                                 // Section pointer
87    CHUNK *cp;                                               // Chunk pointer
88
89    cursect = sno;
90    p = &sect[sno];
91
92    scattr = p->scattr;                                      // Copy section vars
93    sloc = p->sloc;
94    scode = p->scode;
95    sfix = p->sfix;
96
97    if((cp = scode) != NULL) {                               // Copy code chunk vars
98       challoc = cp->challoc;
99       ch_size = cp->ch_size;
100       chptr = cp->chptr + ch_size;
101    } else challoc = ch_size = 0;
102
103    if((cp = sfix) != NULL) {                                // Copy fixup chunk vars 
104       fchalloc = cp->challoc;
105       fchsize = cp->ch_size;
106       fchptr.cp = cp->chptr + fchsize;
107    } else fchalloc = fchsize = 0;
108 }
109
110 //
111 // --- Save Current Section ------------------------------------------------------------------------
112 //
113
114 void savsect(void) {
115    SECT *p;
116
117    p = &sect[cursect];
118
119    p->scattr = scattr;                                      // Bailout section vars
120    p->sloc = sloc;
121
122    if(scode != NULL)                                        // Bailout code chunk
123       scode->ch_size = ch_size;
124
125    if(sfix != NULL)                                         // Bailout fixup chunk
126       sfix->ch_size = fchsize;
127 }
128
129 //
130 // --- Initialize Sections; Setup initial ABS, TEXT, DATA and BSS sections -------------------------
131 //
132
133 void init_sect(void) {
134    int i;                                                   // Iterator
135
136    // Cleanup all sections
137    for(i = 0; i < NSECTS; ++i)
138       mksect(i, 0);
139
140    // Construct default sections, make TEXT the current section
141    mksect(ABS,   SUSED|SABS|SBSS);                          // ABS
142    mksect(TEXT,  SUSED|TEXT     );                          // TEXT
143    mksect(DATA,  SUSED|DATA     );                          // DATA
144    mksect(BSS,   SUSED|BSS |SBSS);                          // BSS
145 //   mksect(M6502, SUSED|TEXT     );                          // 6502 code section
146
147    switchsect(TEXT);                                        // Switch to TEXT for starters
148 }
149
150 //
151 // -------------------------------------------------------------------------------------------------
152 // Test to see if a location has a fixup sic'd on it.  This is used by the listing
153 // generator to print 'xx's instead of '00's for forward references
154 // -------------------------------------------------------------------------------------------------
155 //
156
157 int fixtest(int sno, LONG loc) {
158    CHUNK *ch;
159    PTR fup;
160    char *fuend;
161    WORD w;
162    LONG xloc;
163
164    stopmark();                                              // Force update to sect[] variables
165
166    // Hairy, ugly linear search for a mark on our location;
167    // the speed doesn't matter, since this is only done when generating a listing, which is SLOW.
168    for(ch = sect[sno].sffix; ch != NULL; ch = ch->chnext) {
169       fup.cp = (char *)ch->chptr;
170       fuend = fup.cp + ch->ch_size;
171
172       while(fup.cp < fuend) {
173          w = *fup.wp++;
174          xloc = *fup.lp++ + (int)fusizoffs[w & FUMASK];
175          fup.wp += 2;
176
177          if(xloc == loc)
178             return((int)fusiztab[w & FUMASK]);
179
180          if(w & FU_EXPR) {
181             w = *fup.wp++;
182             fup.lp += w;
183          } else ++fup.lp;
184       }
185    }
186
187    return(0);
188 }
189
190 // 
191 // -------------------------------------------------------------------------------------------------
192 // Check that there are at least `amt' bytes left in the current chunk.  If there are not, 
193 // allocate another chunk of at least `amt' bytes (and probably more).
194 // 
195 // If `amt' is zero, ensure there are at least CH_THRESHOLD bytes, likewise.
196 // -------------------------------------------------------------------------------------------------
197 //
198
199 int chcheck(LONG amt) {
200    CHUNK *cp;
201    SECT *p;
202
203    if(scattr & SBSS) return(0);                             // If in BSS section, forget it
204
205    if(!amt) amt = CH_THRESHOLD;
206
207    if((int)(challoc - ch_size) >= (int)amt) 
208       return(0);
209
210    if(amt < CH_CODE_SIZE) amt = CH_CODE_SIZE;
211    p = &sect[cursect];
212    cp = (CHUNK *)amem((long)(sizeof(CHUNK) + amt));
213    if(scode == NULL) {                                      // First chunk in section
214       cp->chprev = NULL;
215       p->sfcode = cp;
216    } else {                                                 // Add chunk to other chunks
217       cp->chprev = scode;
218       scode->chnext = cp;
219       scode->ch_size = ch_size;                             // Save old chunk's globals 
220    }
221
222    // Setup chunk and global vars
223    cp->chloc = sloc;
224    cp->chnext = NULL;
225    challoc = cp->challoc = amt;
226    ch_size = cp->ch_size = 0;
227    chptr = cp->chptr = ((char *)cp) + sizeof(CHUNK);
228    scode = p->scode = cp;
229
230    return(0);
231 }
232
233 //
234 // --- Arrange for a fixup on a location -----------------------------------------------------------
235 //
236
237 int fixup(WORD attr, LONG loc, TOKEN *fexpr) {
238    LONG i;
239    LONG len = 0;
240    CHUNK *cp;
241    SECT *p;
242
243    // Compute length of expression (could be faster); determine if it's the single-symbol case;
244    // no expression if it's just a mark. This code assumes 16 bit WORDs and 32 bit LONGs
245    if(*fexpr == SYMBOL && fexpr[2] == ENDEXPR)
246       //if((attr & 0x0F00) == FU_JR) {
247       if((attr & 0x0200) == FU_JR) {
248          i = 18;                  // Just a single symbol
249       } else {
250          i = 14;
251       }
252    else {
253       attr |= FU_EXPR;
254       for(len = 0; fexpr[len] != ENDEXPR; ++len)
255          if(fexpr[len] == CONST || fexpr[len] == SYMBOL)
256             ++len;
257       ++len;                                                // Add 1 for ENDEXPR 
258       i = (len << 2) + 12;
259    }
260
261    // Maybe alloc another fixup chunk for this one to fit in
262    if((fchalloc - fchsize) < i) {
263       p = &sect[cursect];
264           cp = (CHUNK *)amem((long)(sizeof(CHUNK) + CH_FIXUP_SIZE));
265       if(sfix == NULL) {                                 // First fixup chunk in section
266          cp->chprev = NULL;
267          p->sffix = cp;
268       } else {                                           // Add to other chunks
269          cp->chprev = sfix;
270          sfix->chnext = cp;
271          sfix->ch_size = fchsize;
272       }
273
274       // Setup fixup chunk and its global vars
275       cp->chnext = NULL;
276       fchalloc = cp->challoc = CH_FIXUP_SIZE;
277       fchsize = cp->ch_size = 0;
278       fchptr.cp = cp->chptr = ((char *)cp) + sizeof(CHUNK);
279       sfix = p->sfix = cp;
280    }
281
282    // Record fixup type, fixup location, and the file number and line number the fixup is 
283    // located at.
284    *fchptr.wp++ = attr;
285    *fchptr.lp++ = loc;
286    *fchptr.wp++ = cfileno;
287    *fchptr.wp++ = (WORD)curlineno;
288    // Store postfix expression or pointer to a single symbol, or nothing for a mark.
289    if(attr & FU_EXPR) {
290       *fchptr.wp++ = (WORD)len;
291       while(len--)
292          *fchptr.lp++ = (LONG)*fexpr++;
293    } else  {
294       *fchptr.lp++ = (LONG)fexpr[1];
295    }
296
297    //if((attr & 0x0F00) == FU_JR) {
298    if((attr & 0x0200) == FU_JR) {
299       if(orgactive) *fchptr.lp++ = orgaddr;
300       else *fchptr.lp++ = 0x00000000;
301    }
302
303    fchsize += i;
304
305    return(0);
306 }
307
308 //
309 // --- Resolve all Fixups --------------------------------------------------------------------------
310 //
311
312 int fixups(void) {
313    unsigned i;                                              // Iterator
314    char buf[EBUFSIZ];
315
316    if(glob_flag)                                            // Make undefined symbols GLOBL
317       syg_fix();
318    resfix(TEXT);
319    resfix(DATA);
320    
321    // We need to do a final check of forward 'jump' destination addresses that are external
322    for(i = 0; i < MAXFWDJUMPS; i++) {
323       if(fwdjump[i]) {
324          err_setup();
325          sprintf(buf, "* \'jump\' at $%08X - destination address is external to this source file and "
326                  "cannot have its aligment validated", fwdjump[i]);
327          if(listing > 0) ship_ln(buf);
328          if(err_flag) write(err_fd, buf, (LONG)strlen(buf));
329          else printf("%s\n", buf);
330       }
331    }
332
333    return(0);
334 }
335
336 //
337 // --- Resolve Fixups in a Section -----------------------------------------------------------------
338 //
339
340 int resfix(int sno) {
341    SECT *sc;                                                // Section
342    CHUNK *ch;
343    PTR fup;                                                 // Current fixup
344    WORD *fuend;                                             // End of last fixup (in this chunk)
345    CHUNK *cch;                                              // Cached chunk for target
346    WORD w;                                                  // Fixup word (type+modes+flags)
347    char *locp;                                              // Location to fix (in cached chunk) 
348    LONG loc;                                                // Location to fixup
349    VALUE eval;                                              // Expression value 
350    WORD eattr;                                              // Expression attrib
351    SYM *esym;                                               // External symbol involved in expr
352    SYM *sy;                                                 // (Temp) pointer to a symbol
353    WORD i;                                                  // (Temp) word
354    WORD tdb;                                                // eattr & TDB
355    LONG oaddr;
356    int reg2;
357    WORD flags;
358    unsigned page_jump = 0;
359    unsigned address = 0;
360    unsigned j;                                              // iterator
361    char buf[EBUFSIZ];
362    
363    sc = &sect[sno];
364    ch = sc->sffix;
365
366    if(ch == NULL)
367       return(0);
368
369    cch = sc->sfcode;                                        // "cache" first chunk
370    if(cch == NULL)                                          // Can't fixup a sect with nothing in it
371       return(0);
372
373    do {
374       fup.cp = ch->chptr;                                   // fup -> start of chunk
375       fuend = (WORD *)(fup.cp + ch->ch_size);               // fuend -> end of chunk
376       while(fup.wp < fuend) {
377          w = *fup.wp++;
378          loc = *fup.lp++;
379          cfileno = *fup.wp++;
380          curlineno = (int)*fup.wp++;
381
382          esym = NULL;
383          // Search for chunk containing location to fix up; compute a pointer to the location 
384          // (in the chunk). Often we will find the fixup is in the "cached" chunk, so the 
385          // linear-search is seldom executed.
386          if(loc < cch->chloc || loc >= (cch->chloc + cch->ch_size)) {
387             for(cch = sc->sfcode; cch != NULL; cch = cch->chnext)
388                if(loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
389                   break;
390             if(cch == NULL) {
391                interror(7);                                 // Fixup (loc) out of range 
392                                         // NOTREACHED
393             }
394          }
395          locp = cch->chptr + (loc - cch->chloc);
396
397          eattr = 0;
398
399          // Compute expression/symbol value and attribs
400          if(w & FU_EXPR) {                                  // Complex expression
401             i = *fup.wp++;
402             if(evexpr(fup.tk, &eval, &eattr, &esym) != OK) {
403                fup.lp += i;
404                continue;
405             }
406             fup.lp += i;
407          } else {                                           // Simple symbol
408             sy = *fup.sy++;
409             eattr = sy->sattr;
410             if(eattr & DEFINED)
411                eval = sy->svalue;
412             else eval = 0;
413
414             if((eattr & (GLOBAL|DEFINED)) == GLOBAL)
415                esym = sy;
416          }
417          tdb = (WORD)(eattr & TDB);
418          // If the expression is undefined and no external symbol is involved, then it's an error.
419          if(!(eattr & DEFINED) && esym == NULL) {
420             error(undef_error);
421             continue;
422          }
423
424
425          if(((w & 0x0F00) == FU_MOVEI) && esym)
426             esym->sattre |= RISCSYM;
427
428          // Do the fixup
429          // 
430          // If a PC-relative fixup is undefined, its value is *not* subtracted from the location
431          // (that will happen in the linker when the external reference is resolved).
432          // 
433          // MWC expects PC-relative things to have the LOC subtracted from the value, if the 
434          // value is external (that is, undefined at this point).
435          // 
436          // PC-relative fixups must be DEFINED and either in the same section (whereupon the 
437          // subtraction takes place) or ABS (with no subtract).
438          if(w & FU_PCREL) {
439             if(eattr & DEFINED) {
440                if(tdb == sno) eval -= (VALUE)loc;
441                else if(tdb) {
442                   error("PC-relative expr across sections");
443                   continue;
444                }
445
446                if(sbra_flag && (w & FU_LBRA) && (eval + 0x80 < 0x100))
447                   warn("unoptimized short branch");
448             } else 
449                if(obj_format == MWC) eval -= (VALUE)loc;
450
451             tdb = 0;
452             eattr &= ~TDB;
453          }
454
455          // Do fixup classes
456          switch((int)(w & FUMASK)) {
457             // FU_BBRA fixes up a one-byte branch offset.
458             case FU_BBRA:
459                if(!(eattr & DEFINED)) {
460                   error("external short branch");
461                   continue;
462                }
463                eval -= 2;
464                if(eval + 0x80 >= 0x100)
465                   goto range;
466                if(eval == 0) {
467                   error("illegal bra.s with zero offset");
468                   continue;
469                }
470                *++locp = (char)eval;
471                break;
472             // Fixup one-byte value at locp + 1.
473             case FU_WBYTE:
474                ++locp;
475                // FALLTHROUGH
476                // Fixup one-byte forward references
477             case FU_BYTE:
478                if(!(eattr & DEFINED)) {
479                   error("external byte reference");
480                   continue;
481                }
482                if(tdb) {
483                   error("non-absolute byte reference");
484                   continue;
485                }
486                if((w & FU_PCREL) && eval + 0x80 >= 0x100) goto range;
487                if(w & FU_SEXT) {
488                   if(eval + 0x100 >= 0x200) goto range;
489                } else 
490                   if(eval >= 0x100) goto range;
491
492                *locp = (char)eval;
493                break;
494             // Fixup WORD forward references; 
495             // the word could be unaligned in the section buffer, so we have to be careful.
496             case FU_WORD:
497                if(((w & 0x0F00) == FU_JR) || ((w & 0x0F00) == FU_MJR)) {
498                   oaddr = *fup.lp++;
499                   if(oaddr) {
500                      reg2 = (signed)((eval - (oaddr + 2)) / 2);// & 0x1F;
501                   } else {
502                      reg2 = (signed)((eval - (loc + 2)) / 2);// & 0x1F;
503                   }
504                   if((w & 0x0F00) == FU_MJR) {
505                      // Main code destination alignment checking here for forward declared labels
506                      address = (oaddr) ? oaddr : loc;
507                      if(((address >= 0xF03000) && (address < 0xF04000) && (eval    < 0xF03000)) ||
508                         ((eval    >= 0xF03000) && (eval    < 0xF04000) && (address < 0xF03000)) ) {
509                         warni("* \'jr\' at $%08X - cannot jump relative between "
510                               "main memory and local gpu ram", address);
511                      } else {
512                         page_jump = (address & 0xFFFFFF00) - (eval & 0xFFFFFF00);
513                         if(page_jump) {
514                            // This jump is to a page outside of the current 256 byte page
515                            if(eval % 4) {
516                               warni("* \'jr\' at $%08X - destination address not aligned for long page jump, "
517                                    "insert a \'nop\' before the destination address", address);
518                            }
519                         } else {
520                            // This jump is in the current 256 byte page
521                            if((eval - 2) % 4) {
522                               warni("* \'jr\' at $%08X - destination address not aligned for short page jump, "
523                                    "insert a \'nop\' before the destination address", address);
524                            }
525                         }
526                      }
527                   }
528                   if((reg2 < -16) || (reg2 > 15)) {
529                      error("relative jump out of range");
530                      break;
531                   }
532                   *locp = (char)(*locp | ((reg2 >> 3) & 0x03));
533                   locp++;
534                   *locp = (char)(*locp | ((reg2 & 0x07) << 5));
535                   break;
536                }
537                if((w & 0x0F00) == FU_NUM15) {
538                   if(eval < -16 || eval > 15) {
539                      error("constant out of range");
540                      break;
541                   }
542                   *locp = (char)(*locp | ((eval >> 3) & 0x03));
543                   locp++;
544                   *locp = (char)(*locp | ((eval & 0x07) << 5));
545                   break;
546                }
547                if((w & 0x0F00) == FU_NUM31) {
548                   if(eval < 0 || eval > 31) {
549                      error("constant out of range");
550                      break;
551                   }
552                   *locp = (char)(*locp | ((eval >> 3) & 0x03));
553                   locp++;
554                   *locp = (char)(*locp | ((eval & 0x07) << 5));
555                   break;
556                }
557                if((w & 0x0F00) == FU_NUM32) {
558                   if(eval < 1 || eval > 32) {
559                      error("constant out of range");
560                      break;
561                   }
562                   if(w & FU_SUB32)
563                      eval = (32 - eval);
564                   eval = (eval == 32) ? 0 : eval;
565                   *locp = (char)(*locp | ((eval >> 3) & 0x03));
566                   locp++;
567                   *locp = (char)(*locp | ((eval & 0x07) << 5));
568                   break;
569                }
570                if((w & 0x0F00) == FU_REGONE) {
571                   if(eval < 0 || eval > 31) {
572                      error("register value out of range");
573                      break;
574                   }
575                   *locp = (char)(*locp | ((eval >> 3) & 0x03));
576                   locp++;
577                   *locp = (char)(*locp | ((eval & 0x07) << 5));
578                   break;
579                }
580                if((w & 0x0F00) == FU_REGTWO) {
581                   if(eval < 0 || eval > 31) {
582                      error("register value out of range");
583                      break;
584                   }
585                   locp++;
586                   *locp = (char)(*locp | (eval & 0x1F));
587                   break;
588                }
589
590                if(!(eattr & DEFINED)) {
591                   if(w & FU_PCREL)
592                      w = MPCREL | MWORD;
593                   else w = MWORD;
594                   rmark(sno, loc, 0, w, esym);
595                } else {
596                   if(tdb)
597                      rmark(sno, loc, tdb, MWORD, NULL);
598                   if(w & FU_SEXT) {
599                      if(eval + 0x10000 >= 0x20000)
600                         goto range;
601                   } else 
602                      if(w & FU_ISBRA) {                     // Range-check BRA and DBRA
603                         if(eval + 0x8000 >= 0x10000)
604                            goto range;
605                      } else 
606                         if(eval >= 0x10000)
607                            goto range;
608                }
609
610                *locp++ = (char)(eval >> 8);
611                *locp = (char)eval;
612                break;
613             // Fixup LONG forward references;
614             // the long could be unaligned in the section buffer, so be careful (again).
615             case FU_LONG:
616                if((w & 0x0F00) == FU_MOVEI) {
617                   address = loc + 4;
618                   if(eattr & DEFINED) {
619                      for(j = 0; j < fwindex; j++) {
620                         if(fwdjump[j] == address) {
621                            page_jump = (address & 0xFFFFFF00) - (eval & 0xFFFFFF00);
622                            if(page_jump) {
623                               if(eval % 4) {
624                                  err_setup();
625                                  sprintf(buf, "* \'jump\' at $%08X - destination address not aligned for long page jump, "
626                                          "insert a \'nop\' before the destination address", address);
627                                  if(listing > 0) ship_ln(buf);
628                                  if(err_flag) write(err_fd, buf, (LONG)strlen(buf));
629                                  else printf("%s\n", buf);
630                               }          
631                            } else {
632                               if(!(eval & 0x0000000F) || ((eval - 2) % 4)) {
633                                  err_setup();
634                                  sprintf(buf, "* \'jump\' at $%08X - destination address not aligned for short page jump, "
635                                          "insert a \'nop\' before the destination address", address);
636                                  if(listing > 0) ship_ln(buf);
637                                  if(err_flag) write(err_fd, buf, (LONG)strlen(buf));
638                                  else printf("%s\n", buf);
639                               }          
640                            }
641                            // Clear this jump as it has been checked
642                            fwdjump[j] = 0;
643                            j = fwindex;
644                         }
645                      }
646                   }
647                   eval = ((eval >> 16) & 0x0000FFFF) | ((eval << 16) & 0xFFFF0000);
648                   flags = (MLONG|MMOVEI);
649                } else flags = MLONG;
650                if(!(eattr & DEFINED)) {
651                   rmark(sno, loc, 0, flags, esym);
652                }
653                else if(tdb) {
654                   rmark(sno, loc, tdb, flags, NULL);
655                }
656                *locp++ = (char)(eval >> 24);
657                *locp++ = (char)(eval >> 16);
658                *locp++ = (char)(eval >> 8);
659                *locp = (char)eval;
660                break;
661             // Fixup a 3-bit "QUICK" reference in bits 9..1
662             // (range of 1..8) in a word.  Really bits 1..3 in a byte.
663             case FU_QUICK:
664                if(!(eattr & DEFINED)) {
665                   error("External quick reference");
666                   continue;
667                }
668
669                if(eval < 1 || eval > 8)
670                   goto range;
671                *locp |= (eval & 7) << 1;
672                break;
673             // Fix up 6502 funny branch
674             case FU_6BRA:
675                eval -= (loc + 1);
676                if(eval + 0x80 >= 0x100)
677                   goto range;
678                *locp = (char)eval;
679                break;
680             default:
681                interror(4);                                 // Bad fixup type
682                // NOTREACHED
683          }
684          continue;
685
686          range:
687
688          error("expression out of range");
689       }
690
691       ch = ch->chnext;
692    } while(ch != NULL);
693
694    return(0);
695 }