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