Partial fix for bug #108 (Fixup cleanups).
[rmac] / sect.c
1 //
2 // RMAC - Reboot's Macro Assembler for all Atari computers
3 // SECT.C - Code Generation, Fixups and Section Management
4 // Copyright (C) 199x Landon Dyer, 2011-2018 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 "6502.h"
11 #include "direct.h"
12 #include "error.h"
13 #include "expr.h"
14 #include "listing.h"
15 #include "mach.h"
16 #include "mark.h"
17 #include "riscasm.h"
18 #include "symbol.h"
19 #include "token.h"
20
21
22 // Function prototypes
23 void MakeSection(int, uint16_t);
24 void SwitchSection(int);
25
26 // Section descriptors
27 SECT sect[NSECTS];              // All sections...
28 int cursect;                    // Current section number
29
30 // These are copied from the section descriptor, the current code chunk
31 // descriptor and the current fixup chunk descriptor when a switch is made into
32 // a section. They are copied back to the descriptors when the section is left.
33 uint16_t scattr;                // Section attributes
34 uint32_t sloc;                  // Current loc in section
35
36 CHUNK * scode;                  // Current (last) code chunk
37 uint32_t challoc;               // # bytes alloc'd to code chunk
38 uint32_t ch_size;               // # bytes used in code chunk
39 uint8_t * chptr;                // Deposit point in code chunk buffer
40 uint8_t * chptr_opcode; // Backup of chptr, updated before entering code generators
41
42 // Return a size (SIZB, SIZW, SIZL) or 0, depending on what kind of fixup is
43 // associated with a location.
44 static uint8_t 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 uint8_t 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 //
69 // Initialize sections; setup initial ABS, TEXT, DATA and BSS sections
70 //
71 void InitSection(void)
72 {
73         // Initialize all sections
74         for(int i=0; i<NSECTS; i++)
75                 MakeSection(i, 0);
76
77         // Construct default sections, make TEXT the current section
78         MakeSection(ABS,   SUSED | SABS | SBSS);        // ABS
79         MakeSection(TEXT,  SUSED | TEXT       );        // TEXT
80         MakeSection(DATA,  SUSED | DATA       );        // DATA
81         MakeSection(BSS,   SUSED | BSS  | SBSS);        // BSS
82         MakeSection(M6502, SUSED | TEXT       );        // 6502 code section
83
84         // Switch to TEXT for starters
85         SwitchSection(TEXT);
86 }
87
88
89 //
90 // Make a new (clean) section
91 //
92 void MakeSection(int sno, uint16_t attr)
93 {
94         SECT * p = &sect[sno];
95         p->scattr = attr;
96         p->sloc = 0;
97         p->orgaddr = 0;
98         p->scode = p->sfcode = NULL;
99         p->sfix = p->sffix = NULL;
100 }
101
102
103 //
104 // Switch to another section (copy section & chunk descriptors to global vars
105 // for fast access)
106 //
107 void SwitchSection(int sno)
108 {
109         CHUNK * cp;
110         cursect = sno;
111         SECT * p = &sect[sno];
112
113         m6502 = (sno == M6502); // Set 6502-mode flag
114
115         // Copy section vars
116         scattr = p->scattr;
117         sloc = p->sloc;
118         scode = p->scode;
119         orgaddr = p->orgaddr;
120
121         // Copy code chunk vars
122         if ((cp = scode) != NULL)
123         {
124                 challoc = cp->challoc;
125                 ch_size = cp->ch_size;
126                 chptr = cp->chptr + ch_size;
127
128                 // For 6502 mode, add the last org'd address
129                 if (m6502)
130                         chptr = cp->chptr + orgaddr;
131         }
132         else
133                 challoc = ch_size = 0;
134 }
135
136
137 //
138 // Save current section
139 //
140 void SaveSection(void)
141 {
142         SECT * p = &sect[cursect];
143
144         p->scattr = scattr;                             // Bailout section vars
145         p->sloc = sloc;
146         p->orgaddr = orgaddr;
147
148         if (scode != NULL)                              // Bailout code chunk
149                 scode->ch_size = ch_size;
150 }
151
152
153 //
154 // Test to see if a location has a fixup set on it. This is used by the
155 // listing generator to print 'xx's instead of '00's for forward references
156 //
157 int fixtest(int sno, uint32_t loc)
158 {
159         // Force update to sect[] variables
160         StopMark();
161
162         // Ugly linear search for a mark on our location. The speed doesn't
163         // matter, since this is only done when generating a listing, which is
164         // SLOW anyway.
165         for(FIXUP * fp=sect[sno].sffix; fp!=NULL; fp=fp->next)
166         {
167                 uint32_t w = fp->attr;
168                 uint32_t xloc = fp->loc + (int)fusizoffs[w & FUMASK];
169
170                 if (xloc == loc)
171                         return (int)fusiztab[w & FUMASK];
172         }
173
174         return 0;
175 }
176
177
178 //
179 // Check that there are at least 'amt' bytes left in the current chunk. If
180 // there are not, allocate another chunk of at least 'amt' bytes (and probably
181 // more).
182 //
183 // If 'amt' is zero, ensure there are at least CH_THRESHOLD bytes, likewise.
184 //
185 int chcheck(uint32_t amt)
186 {
187         DEBUG { printf("chcheck(%u)\n", amt); }
188
189         // If in BSS section, no allocation required
190         if (scattr & SBSS)
191                 return 0;
192
193         if (!amt)
194                 amt = CH_THRESHOLD;
195
196         DEBUG { printf("    challoc=%i, ch_size=%i, diff=%i\n", challoc, ch_size, challoc-ch_size); }
197
198         if ((int)(challoc - ch_size) >= (int)amt)
199                 return 0;
200
201         if (amt < CH_CODE_SIZE)
202                 amt = CH_CODE_SIZE;
203
204         DEBUG { printf("    amt (adjusted)=%u\n", amt); }
205         SECT * p = &sect[cursect];
206         CHUNK * cp = malloc(sizeof(CHUNK) + amt);
207
208         // First chunk in section
209         if (scode == NULL)
210         {
211                 cp->chprev = NULL;
212                 p->sfcode = cp;
213         }
214         // Add chunk to other chunks
215         else
216         {
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 = ((uint8_t *)cp) + sizeof(CHUNK);
228         scode = p->scode = cp;
229
230         return 0;
231 }
232
233
234 //
235 // Arrange for a fixup on a location
236 //
237 int AddFixup(uint32_t attr, uint32_t loc, TOKEN * fexpr)
238 {
239         uint16_t exprlen = 0;
240         SYM * symbol = NULL;
241         uint32_t _orgaddr = 0;
242
243         // First, check to see if the expression is a bare label, otherwise, force
244         // the FU_EXPR flag into the attributes and count the tokens.
245         if ((fexpr[0] == SYMBOL) && (fexpr[2] == ENDEXPR))
246         {
247                 symbol = symbolPtr[fexpr[1]];
248
249                 // Save the org address for JR RISC instruction
250                 if ((attr & FUMASKRISC) == FU_JR)
251                         _orgaddr = orgaddr;
252         }
253         else
254         {
255                 attr |= FU_EXPR;
256                 exprlen = ExpressionLength(fexpr);
257         }
258
259         // Allocate space for the fixup + any expression
260         FIXUP * fixup = malloc(sizeof(FIXUP) + (sizeof(TOKEN) * exprlen));
261
262         // Store the relevant fixup information in the FIXUP
263         fixup->next = NULL;
264         fixup->attr = attr;
265         fixup->loc = loc;
266         fixup->fileno = cfileno;
267         fixup->lineno = curlineno;
268         fixup->expr = NULL;
269         fixup->symbol = symbol;
270         fixup->orgaddr = _orgaddr;
271
272         // Copy the passed in expression to the FIXUP, if any
273         if (exprlen > 0)
274         {
275                 fixup->expr = (TOKEN *)((uint8_t *)fixup + sizeof(FIXUP));
276                 memcpy(fixup->expr, fexpr, sizeof(TOKEN) * exprlen);
277         }
278
279         // Finally, put the FIXUP in the current section's linked list
280         if (sect[cursect].sffix == NULL)
281         {
282                 sect[cursect].sffix = fixup;
283                 sect[cursect].sfix = fixup;
284         }
285         else
286         {
287                 sect[cursect].sfix->next = fixup;
288                 sect[cursect].sfix = fixup;
289         }
290
291         DEBUG { printf("AddFixup: sno=%u, l#=%u, attr=$%X, loc=$%X, expr=%p, sym=%p, org=$%X\n", cursect, fixup->lineno, fixup->attr, fixup->loc, (void *)fixup->expr, (void *)fixup->symbol, fixup->orgaddr);
292                 if (symbol != NULL)
293                         printf("          name: %s, value: $lX\n", symbol->sname, symbol->svalue);
294         }
295
296         return 0;
297 }
298
299
300 //
301 // Resolve fixups in a section
302 //
303 int ResolveFixups(int sno)
304 {
305         uint64_t eval;                  // Expression value
306         int reg2;
307         uint16_t flags;
308
309         SECT * sc = &sect[sno];
310
311         // "Cache" first chunk
312         CHUNK * cch = sc->sfcode;
313
314         // Can't fixup a section with nothing in it
315         if (cch == NULL)
316                 return 0;
317
318         // Wire the 6502 segment's size to its allocated size (64K)
319         if (sno == M6502)
320                 cch->ch_size = cch->challoc;
321
322         // Get first fixup for the passed in section
323         FIXUP * fixup = sect[sno].sffix;
324
325         while (fixup != NULL)
326         {
327                 // We do it this way because we have continues everywhere... :-P
328                 FIXUP * fup = fixup;
329                 fixup = fixup->next;
330
331                 uint32_t w = fup->attr;         // Fixup long (type+modes+flags)
332                 uint32_t loc = fup->loc;        // Location to fixup
333                 cfileno = fup->fileno;
334                 curlineno = fup->lineno;
335                 DEBUG { printf("ResolveFixups: sect#=%u, l#=%u, attr=$%X, loc=$%X, expr=%p, sym=%p, org=$%X\n", sno, fup->lineno, fup->attr, fup->loc, (void *)fup->expr, (void *)fup->symbol, fup->orgaddr); }
336
337                 // This is based on global vars cfileno, curfname :-P
338                 // This approach is kinda meh as well. I think we can do better
339                 // than this.
340                 SetFilenameForErrorReporting();
341
342                 // Search for chunk containing location to fix up; compute a
343                 // pointer to the location (in the chunk). Often we will find the
344                 // Fixup is in the "cached" chunk, so the linear-search is seldom
345                 // executed.
346                 if (loc < cch->chloc || loc >= (cch->chloc + cch->ch_size))
347                 {
348                         for(cch=sc->sfcode; cch!=NULL; cch=cch->chnext)
349                         {
350                                 if (loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
351                                         break;
352                         }
353
354                         if (cch == NULL)
355                         {
356                                 // Fixup (loc) is out of range--this should never happen!
357                                 // Once we call this function, it winds down immediately; it
358                                 // doesn't return.
359                                 interror(7);
360                         }
361                 }
362
363                 // Location to fix (in current chunk)
364                 // We use the address of the chunk that loc is actually in, then
365                 // subtract the chunk's starting location from loc to get the offset
366                 // into the current chunk.
367                 uint8_t * locp = cch->chptr + (loc - cch->chloc);
368                 uint16_t eattr = 0;                     // Expression attrib
369                 SYM * esym = NULL;                      // External symbol involved in expr
370
371                 // Compute expression/symbol value and attributes
372
373                 // Complex expression
374                 if (w & FU_EXPR)
375                 {
376                         if (evexpr(fup->expr, &eval, &eattr, &esym) != OK)
377                                 continue;
378                 }
379                 // Simple symbol
380                 else
381                 {
382                         SYM * sy = fup->symbol;
383                         eattr = sy->sattr;
384
385                         if (eattr & DEFINED)
386                                 eval = sy->svalue;
387                         else
388                                 eval = 0;
389
390                         // If the symbol is not defined, but global, set esym to sy
391                         if ((eattr & (GLOBAL | DEFINED)) == GLOBAL)
392                                 esym = sy;
393                 }
394
395                 uint16_t tdb = eattr & TDB;
396
397                 // If the expression/symbol is undefined and no external symbol is
398                 // involved, then that's an error.
399                 if (!(eattr & DEFINED) && (esym == NULL))
400                 {
401                         error(undef_error);
402                         continue;
403                 }
404
405                 // Do the fixup
406                 //
407                 // If a PC-relative fixup is undefined, its value is *not*
408                 // subtracted from the location (that will happen in the linker
409                 // when the external reference is resolved).
410                 //
411                 // MWC expects PC-relative things to have the LOC subtracted from
412                 // the value, if the value is external (that is, undefined at this
413                 // point).
414                 //
415                 // PC-relative fixups must be DEFINED and either in the same
416                 // section (whereupon the subtraction takes place) or ABS (with no
417                 // subtract).
418                 if (w & FU_PCREL)
419                 {
420                         if (eattr & DEFINED)
421                         {
422                                 if (tdb == sno)
423                                         eval -= loc;
424                                 else if (tdb)
425                                 {
426                                         // Allow cross-section PCREL fixups in Alcyon mode
427                                         if (prg_flag)
428                                         {
429                                                 switch (tdb)
430                                                 {
431                                                 case TEXT:
432 // Shouldn't there be a break here, since otherwise, it will point to the DATA section?
433 //                                                      break;
434                                                 case DATA:
435                                                         eval += sect[TEXT].sloc;
436                                                         break;
437                                                 case BSS:
438                                                         eval += sect[TEXT].sloc + sect[DATA].sloc;
439                                                         break;
440                                                 default:
441                                                         error("invalid section");
442                                                 break;
443                                                 }
444
445                                                 eval -= loc;
446                                         }
447                                         else
448                                         {
449                                                 error("PC-relative expr across sections");
450                                                 continue;
451                                         }
452                                 }
453
454                                 if (sbra_flag && (w & FU_LBRA) && (eval + 0x80 < 0x100))
455                                         warn("unoptimized short branch");
456                         }
457                         else if (obj_format == MWC)
458                                 eval -= loc;
459
460                         tdb = 0;
461                         eattr &= ~TDB;
462                 }
463
464                 // Handle fixup classes
465                 switch (w & FUMASK)
466                 {
467                 // FU_BBRA fixes up a one-byte branch offset.
468                 case FU_BBRA:
469                         if (!(eattr & DEFINED))
470                         {
471                                 error("external short branch");
472                                 continue;
473                         }
474
475                         eval -= 2;
476
477                         if (eval + 0x80 >= 0x100)
478                                 goto rangeErr;
479
480                         if (eval == 0)
481                         {
482                                 if (CHECK_OPTS(OPT_NULL_BRA))
483                                 {
484                                         // Just output a NOP
485                                         *locp++ = 0x4E;
486                                         *locp = 0x71;
487                                         continue;
488                                 }
489                                 else
490                                 {
491                                         error("illegal bra.s with zero offset");
492                                         continue;
493                                 }
494                         }
495
496                         *++locp = (uint8_t)eval;
497                         break;
498
499                 // Fixup one-byte value at locp + 1.
500                 case FU_WBYTE:
501                         locp++;
502                         // FALLTHROUGH
503
504                 // Fixup one-byte forward references
505                 case FU_BYTE:
506                         if (!(eattr & DEFINED))
507                         {
508                                 error("external byte reference");
509                                 continue;
510                         }
511
512                         if (tdb)
513                         {
514                                 error("non-absolute byte reference");
515                                 continue;
516                         }
517
518                         if ((w & FU_PCREL) && ((eval + 0x80) >= 0x100))
519                                 goto rangeErr;
520
521                         if (w & FU_SEXT)
522                         {
523                                 if ((eval + 0x100) >= 0x200)
524                                         goto rangeErr;
525                         }
526                         else if (eval >= 0x100)
527                                 goto rangeErr;
528
529                         *locp = (uint8_t)eval;
530                         break;
531
532                 // Fixup high/low byte off word for 6502
533                 case FU_BYTEH:
534                         if (!(eattr & DEFINED))
535                         {
536                                 error("external byte reference");
537                                 continue;
538                         }
539
540                         *locp = (uint8_t)((eval >> 8) & 0xFF);
541                         break;
542
543                 case FU_BYTEL:
544                         if (!(eattr & DEFINED))
545                         {
546                                 error("external byte reference");
547                                 continue;
548                         }
549
550                         *locp = (uint8_t)(eval & 0xFF);
551                         break;
552
553                 // Fixup WORD forward references; the word could be unaligned in
554                 // the section buffer, so we have to be careful.
555                 case FU_WORD:
556                         if ((w & FUMASKRISC) == FU_JR)
557                         {
558                                 if (fup->orgaddr)
559                                         reg2 = (signed)((eval - (fup->orgaddr + 2)) / 2);
560                                 else
561                                         reg2 = (signed)((eval - (loc + 2)) / 2);
562
563                                 if ((reg2 < -16) || (reg2 > 15))
564                                 {
565                                         error("relative jump out of range");
566                                         break;
567                                 }
568
569                                 *locp = (uint8_t)(*locp | ((reg2 >> 3) & 0x03));
570                                 locp++;
571                                 *locp = (uint8_t)(*locp | ((reg2 & 0x07) << 5));
572                                 break;
573                         }
574                         else if ((w & FUMASKRISC) == FU_NUM15)
575                         {
576                                 if (eval < -16 || eval > 15)
577                                 {
578                                         error("constant out of range");
579                                         break;
580                                 }
581
582                                 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
583                                 locp++;
584                                 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
585                                 break;
586                         }
587                         else if ((w & FUMASKRISC) == FU_NUM31)
588                         {
589                                 if (eval > 31)
590                                 {
591                                         error("constant out of range");
592                                         break;
593                                 }
594
595                                 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
596                                 locp++;
597                                 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
598                                 break;
599                         }
600                         else if ((w & FUMASKRISC) == FU_NUM32)
601                         {
602                                 if (eval < 1 || eval > 32)
603                                 {
604                                         error("constant out of range");
605                                         break;
606                                 }
607
608                                 if (w & FU_SUB32)
609                                         eval = (32 - eval);
610
611                                 eval = (eval == 32) ? 0 : eval;
612                                 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
613                                 locp++;
614                                 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
615                                 break;
616                         }
617                         else if ((w & FUMASKRISC) == FU_REGONE)
618                         {
619                                 if (eval > 31)
620                                 {
621                                         error("register value out of range");
622                                         break;
623                                 }
624
625                                 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
626                                 locp++;
627                                 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
628                                 break;
629                         }
630                         else if ((w & FUMASKRISC) == FU_REGTWO)
631                         {
632                                 if (eval > 31)
633                                 {
634                                         error("register value out of range");
635                                         break;
636                                 }
637
638                                 locp++;
639                                 *locp = (uint8_t)(*locp | (eval & 0x1F));
640                                 break;
641                         }
642
643                         if (!(eattr & DEFINED))
644                         {
645                                 flags = MWORD;
646
647                                 if (w & FU_PCREL)
648                                         flags |= MPCREL;
649
650                                 MarkRelocatable(sno, loc, 0, flags, esym);
651                         }
652                         else
653                         {
654                                 if (tdb)
655                                         MarkRelocatable(sno, loc, tdb, MWORD, NULL);
656
657                                 if (w & FU_SEXT)
658                                 {
659                                         if (eval + 0x10000 >= 0x20000)
660                                                 goto rangeErr;
661                                 }
662                                 else
663                                 {
664                                         // Range-check BRA and DBRA
665                                         if (w & FU_ISBRA)
666                                         {
667                                                 if (eval + 0x8000 >= 0x10000)
668                                                         goto rangeErr;
669                                         }
670                                         else if (eval >= 0x10000)
671                                                 goto rangeErr;
672                                 }
673                         }
674
675                         // 6502 words are little endian, so handle that here
676                         if (sno == M6502)
677                                 SETLE16(locp, 0, eval)
678                         else
679                                 SETBE16(locp, 0, eval)
680
681                         break;
682
683                 // Fixup LONG forward references;
684                 // the long could be unaligned in the section buffer, so be careful
685                 // (again).
686                 case FU_LONG:
687                         flags = MLONG;
688
689                         if ((w & FUMASKRISC) == FU_MOVEI)
690                         {
691                                 // Long constant in MOVEI # is word-swapped, so fix it here
692                                 eval = WORDSWAP32(eval);
693                                 flags |= MMOVEI;
694                         }
695
696                         // If the symbol is undefined, make sure to pass the symbol in
697                         // to the MarkRelocatable() function.
698                         if (!(eattr & DEFINED))
699                                 MarkRelocatable(sno, loc, 0, flags, esym);
700                         else if (tdb)
701                                 MarkRelocatable(sno, loc, tdb, flags, NULL);
702
703                         SETBE32(locp, 0, eval);
704                         break;
705
706                 // Fixup a 3-bit "QUICK" reference in bits 9..1
707                 // (range of 1..8) in a word. [Really bits 1..3 in a byte.]
708                 case FU_QUICK:
709                         if (!(eattr & DEFINED))
710                         {
711                                 error("External quick reference");
712                                 continue;
713                         }
714
715                         if ((eval < 1) || (eval > 8))
716                                 goto rangeErr;
717
718                         *locp |= (eval & 7) << 1;
719                         break;
720
721                 // Fix up 6502 funny branch
722                 case FU_6BRA:
723                         eval -= (loc + 1);
724
725                         if (eval + 0x80 >= 0x100)
726                                 goto rangeErr;
727
728                         *locp = (uint8_t)eval;
729                         break;
730
731                 default:
732                         // Bad fixup type--this should *never* happen!
733                         // Once we call this function, it winds down immediately; it
734                         // doesn't return.
735                         interror(4);
736                 }
737
738                 continue;
739 rangeErr:
740                 error("expression out of range");
741         }
742
743         return 0;
744 }
745
746
747 //
748 // Resolve all fixups
749 //
750 int ResolveAllFixups(void)
751 {
752         // Make undefined symbols GLOBL
753         if (glob_flag)
754                 ForceUndefinedSymbolsGlobal();
755
756         DEBUG printf("Resolving TEXT sections...\n");
757         ResolveFixups(TEXT);
758         DEBUG printf("Resolving DATA sections...\n");
759         ResolveFixups(DATA);
760         DEBUG printf("Resolving 6502 sections...\n");
761         ResolveFixups(M6502);           // Fixup 6502 section (if any)
762
763         return 0;
764 }
765