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, 2017 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
22 // Function prototypes
23 void MakeSection(int, uint16_t);
24 void SwitchSection(int);
26 // Section descriptors
27 SECT sect[NSECTS]; // All sections...
28 int cursect; // Current section number
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
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
42 CHUNK * sfix; // Current (last) fixup chunk
43 uint32_t fchalloc; // # bytes alloc'd to fixup chunk
44 uint32_t fchsize; // # bytes used in fixup chunk
45 PTR fchptr; // Deposit point in fixup chunk buffer
47 // Return a size (SIZB, SIZW, SIZL) or 0, depending on what kind of fixup is
48 // associated with a location.
49 static uint8_t fusiztab[] = {
60 // Offset to REAL fixup location
61 static uint8_t fusizoffs[] = {
74 // Initialize sections; setup initial ABS, TEXT, DATA and BSS sections
76 void InitSection(void)
80 // Cleanup all sections
81 for(i=0; i<NSECTS; i++)
84 // Construct default sections, make TEXT the current section
85 MakeSection(ABS, SUSED | SABS | SBSS); // ABS
86 MakeSection(TEXT, SUSED | TEXT ); // TEXT
87 MakeSection(DATA, SUSED | DATA ); // DATA
88 MakeSection(BSS, SUSED | BSS | SBSS); // BSS
89 MakeSection(M6502, SUSED | TEXT ); // 6502 code section
91 // Switch to TEXT for starters
97 // Make a new (clean) section
99 void MakeSection(int sno, uint16_t attr)
101 SECT * p = §[sno];
104 p->scode = p->sfcode = NULL;
105 p->sfix = p->sffix = NULL;
110 // Switch to another section (copy section & chunk descriptors to global vars
113 void SwitchSection(int sno)
117 SECT * p = §[sno];
119 m6502 = (sno == M6502); /* set 6502-mode */
127 // Copy code chunk vars
128 if ((cp = scode) != NULL)
130 challoc = cp->challoc;
131 ch_size = cp->ch_size;
132 chptr = cp->chptr + ch_size;
135 // For 6502 mode, add the last org'd address
136 chptr = cp->chptr + orgaddr;
140 challoc = ch_size = 0;
142 // Copy fixup chunk vars
143 if ((cp = sfix) != NULL)
145 fchalloc = cp->challoc;
146 fchsize = cp->ch_size;
147 fchptr.cp = cp->chptr + fchsize;
150 fchalloc = fchsize = 0;
155 // Save current section
157 void SaveSection(void)
159 SECT * p = §[cursect];
161 p->scattr = scattr; // Bailout section vars
164 if (scode != NULL) // Bailout code chunk
165 scode->ch_size = ch_size;
167 if (sfix != NULL) // Bailout fixup chunk
168 sfix->ch_size = fchsize;
173 // Test to see if a location has a fixup sic'd on it. This is used by the
174 // listing generator to print 'xx's instead of '00's for forward references
176 int fixtest(int sno, uint32_t loc)
180 // Force update to sect[] variables
183 // Hairy, ugly linear search for a mark on our location; the speed doesn't
184 // matter, since this is only done when generating a listing, which is
186 for(CHUNK * ch=sect[sno].sffix; ch!=NULL; ch=ch->chnext)
188 fup.cp = (uint8_t *)ch->chptr;
189 uint8_t * fuend = fup.cp + ch->ch_size;
191 while (fup.cp < fuend)
193 uint16_t w = *fup.wp++;
194 uint32_t xloc = *fup.lp++ + (int)fusizoffs[w & FUMASK];
198 return (int)fusiztab[w & FUMASK];
215 // Check that there are at least 'amt' bytes left in the current chunk. If
216 // there are not, allocate another chunk of at least 'amt' bytes (and probably
219 // If 'amt' is zero, ensure there are at least CH_THRESHOLD bytes, likewise.
221 int chcheck(uint32_t amt)
223 DEBUG { printf("chcheck(%u)\n", amt); }
224 // If in BSS section, no allocation required
231 DEBUG { printf(" challoc=%i, ch_size=%i, diff=%i\n", challoc, ch_size, challoc-ch_size); }
232 if ((int)(challoc - ch_size) >= (int)amt)
235 if (amt < CH_CODE_SIZE)
238 DEBUG { printf(" amt (adjusted)=%u\n", amt); }
239 SECT * p = §[cursect];
240 CHUNK * cp = malloc(sizeof(CHUNK) + amt);
242 // First chunk in section
248 // Add chunk to other chunks
253 scode->ch_size = ch_size; // Save old chunk's globals
256 // Setup chunk and global vars
259 challoc = cp->challoc = amt;
260 ch_size = cp->ch_size = 0;
261 chptr = cp->chptr = ((uint8_t *)cp) + sizeof(CHUNK);
262 scode = p->scode = cp;
268 // This is really wrong. We need to make some proper structures here so we
269 // don't have to count sizes of objects, that's what the compiler's for! :-P
270 #define FIXUP_BASE_SIZE (sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t))
272 // Arrange for a fixup on a location
274 int AddFixup(uint16_t attr, uint32_t loc, TOKEN * fexpr)
280 // Shamus: Expression lengths are voodoo ATM (variable "i"). Need to fix
282 WARNING(!!! AddFixup() is filled with VOODOO !!!)
283 DEBUG printf("FIXUP@$%X: $%X\n", loc, attr);
285 // Compute length of expression (could be faster); determine if it's the
286 // single-symbol case; no expression if it's just a mark. This code assumes
287 // 16 bit WORDs and 32 bit LONGs
288 if (*fexpr == SYMBOL && fexpr[2] == ENDEXPR)
290 // Just a single symbol
291 // SCPCD : correct bit mask for attr (else other FU_xxx will match)
293 if ((attr & FUMASKRISC) == FU_JR)
295 //printf("AddFixup: ((attr & FUMASKRISC) == FU_JR)\n");
297 // i = FIXUP_BASE_SIZE + (sizeof(uint32_t) * 2);
298 i = FIXUP_BASE_SIZE + sizeof(SYM *) + sizeof(uint32_t);
302 //printf("AddFixup: ((attr & FUMASKRISC) == FU_JR) ELSE\n");
304 i = FIXUP_BASE_SIZE + sizeof(SYM *);
309 //printf("AddFixup: !SYMBOL\n");
312 for(len=0; fexpr[len]!=ENDEXPR; len++)
314 if (fexpr[len] == CONST || fexpr[len] == SYMBOL)
318 len++; // Add 1 for ENDEXPR
319 // i = (len << 2) + 12;
320 i = FIXUP_BASE_SIZE + sizeof(uint16_t) + (len * sizeof(TOKEN));
323 // Alloc another fixup chunk for this one to fit in if necessary
324 if ((fchalloc - fchsize) < i)
327 cp = (CHUNK *)malloc(sizeof(CHUNK) + CH_FIXUP_SIZE);
329 // First fixup chunk in section
335 // Add to other chunks
340 sfix->ch_size = fchsize;
343 // Setup fixup chunk and its global vars
345 fchalloc = cp->challoc = CH_FIXUP_SIZE;
346 fchsize = cp->ch_size = 0;
347 fchptr.cp = cp->chptr = ((uint8_t *)cp) + sizeof(CHUNK);
351 // Record fixup type, fixup location, and the file number and line number
352 // the fixup is located at.
355 *fchptr.wp++ = cfileno;
356 *fchptr.wp++ = (uint16_t)curlineno;
358 // Store postfix expression or pointer to a single symbol, or nothing for a
362 *fchptr.wp++ = (uint16_t)len;
365 *fchptr.lp++ = (uint32_t)*fexpr++;
369 *fchptr.sy++ = symbolPtr[fexpr[1]];
370 //printf("AddFixup: adding symbol (%s) [%08X]\n", symbolPtr[fexpr[1]]->sname, symbolPtr[fexpr[1]]->sattr);
373 // SCPCD : correct bit mask for attr (else other FU_xxx will match) NYAN !
374 if ((attr & FUMASKRISC) == FU_JR)
377 *fchptr.lp++ = orgaddr;
379 *fchptr.lp++ = 0x00000000;
388 // Resolve fixups in a section
390 int ResolveFixups(int sno)
392 PTR fup; // Current fixup
393 uint16_t * fuend; // End of last fixup (in this chunk)
394 uint16_t w; // Fixup word (type+modes+flags)
395 uint8_t * locp; // Location to fix (in cached chunk)
396 uint32_t loc; // Location to fixup
397 VALUE eval; // Expression value
398 uint16_t eattr; // Expression attrib
399 SYM * esym; // External symbol involved in expr
400 SYM * sy; // (Temp) pointer to a symbol
401 uint16_t i; // (Temp) word
402 uint16_t tdb; // eattr & TDB
407 SECT * sc = §[sno];
408 CHUNK * ch = sc->sffix;
413 // "Cache" first chunk
414 CHUNK * cch = sc->sfcode;
416 // Can't fixup a section with nothing in it
421 * Wire the 6502 segment's size to its allocated size (64K)
424 cch->ch_size = cch->challoc;
428 fup.cp = ch->chptr; // fup -> start of chunk
429 fuend = (uint16_t *)(fup.cp + ch->ch_size); // fuend -> end of chunk
431 while (fup.wp < fuend)
436 curlineno = (int)*fup.wp++;
437 DEBUG { printf("ResolveFixups: cfileno=%u\n", cfileno); }
439 // This is based on global vars cfileno, curfname :-P
440 // This approach is kinda meh as well. I think we can do better
442 SetFilenameForErrorReporting();
446 // Search for chunk containing location to fix up; compute a
447 // pointer to the location (in the chunk). Often we will find the
448 // Fixup is in the "cached" chunk, so the linear-search is seldom
450 if (loc < cch->chloc || loc >= (cch->chloc + cch->ch_size))
452 for(cch=sc->sfcode; cch!=NULL; cch=cch->chnext)
454 if (loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
460 // Fixup (loc) out of range
466 locp = cch->chptr + (loc - cch->chloc);
469 // Compute expression/symbol value and attribs
471 // Complex expression
476 if (evexpr(fup.tk, &eval, &eattr, &esym) != OK)
495 // If the symbol is not defined, but global, set esym to sy
496 if ((eattr & (GLOBAL | DEFINED)) == GLOBAL)
500 tdb = (uint16_t)(eattr & TDB);
502 // If the expression is undefined and no external symbol is
503 // involved, then that's an error.
504 if (!(eattr & DEFINED) && (esym == NULL))
512 // If a PC-relative fixup is undefined, its value is *not*
513 // subtracted from the location (that will happen in the linker
514 // when the external reference is resolved).
516 // MWC expects PC-relative things to have the LOC subtracted from
517 // the value, if the value is external (that is, undefined at this
520 // PC-relative fixups must be DEFINED and either in the same
521 // section (whereupon the subtraction takes place) or ABS (with no
531 error("PC-relative expr across sections");
535 if (sbra_flag && (w & FU_LBRA) && (eval + 0x80 < 0x100))
536 warn("unoptimized short branch");
538 else if (obj_format == MWC)
546 switch ((int)(w & FUMASK))
548 // FU_BBRA fixes up a one-byte branch offset.
550 if (!(eattr & DEFINED))
552 error("external short branch");
558 if (eval + 0x80 >= 0x100)
563 error("illegal bra.s with zero offset");
567 *++locp = (uint8_t)eval;
569 // Fixup one-byte value at locp + 1.
573 // Fixup one-byte forward references
575 if (!(eattr & DEFINED))
577 error("external byte reference");
583 error("non-absolute byte reference");
587 if ((w & FU_PCREL) && eval + 0x80 >= 0x100)
592 if (eval + 0x100 >= 0x200)
595 else if (eval >= 0x100)
598 *locp = (uint8_t)eval;
600 // Fixup high/low byte off word for 6502
602 if (!(eattr & DEFINED))
604 error("external byte reference");
608 *locp = (uint8_t)((eval >> 8) & 0xFF);
611 if (!(eattr & DEFINED))
613 error("external byte reference");
617 *locp = (uint8_t)(eval & 0xFF);
619 // Fixup WORD forward references;
620 // the word could be unaligned in the section buffer, so we have to
623 if ((w & FUMASKRISC) == FU_JR)
628 reg2 = (signed)((eval - (oaddr + 2)) / 2);// & 0x1F;
630 reg2 = (signed)((eval - (loc + 2)) / 2);// & 0x1F;
632 if ((reg2 < -16) || (reg2 > 15))
634 error("relative jump out of range");
638 *locp = (uint8_t)(*locp | ((reg2 >> 3) & 0x03));
640 *locp = (uint8_t)(*locp | ((reg2 & 0x07) << 5));
644 if ((w & FUMASKRISC) == FU_NUM15)
646 if (eval < -16 || eval > 15)
648 error("constant out of range");
652 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
654 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
658 if ((w & FUMASKRISC) == FU_NUM31)
660 if (eval < 0 || eval > 31)
662 error("constant out of range");
666 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
668 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
672 if ((w & FUMASKRISC) == FU_NUM32)
674 if (eval < 1 || eval > 32)
676 error("constant out of range");
683 eval = (eval == 32) ? 0 : eval;
684 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
686 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
690 if ((w & FUMASKRISC) == FU_REGONE)
692 if (eval < 0 || eval > 31)
694 error("register value out of range");
698 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
700 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
704 if ((w & FUMASKRISC) == FU_REGTWO)
706 if (eval < 0 || eval > 31)
708 error("register value out of range");
713 *locp = (uint8_t)(*locp | (eval & 0x1F));
717 if (!(eattr & DEFINED))
724 MarkRelocatable(sno, loc, 0, flags, esym);
729 MarkRelocatable(sno, loc, tdb, MWORD, NULL);
733 if (eval + 0x10000 >= 0x20000)
738 // Range-check BRA and DBRA
741 if (eval + 0x8000 >= 0x10000)
744 else if (eval >= 0x10000)
749 // 6502 words are little endian, so handle that here
751 SETLE16(locp, 0, eval)
753 SETBE16(locp, 0, eval)
756 // Fixup LONG forward references;
757 // the long could be unaligned in the section buffer, so be careful
762 if ((w & FUMASKRISC) == FU_MOVEI)
764 // Long constant in MOVEI # is word-swapped, so fix it here
765 eval = WORDSWAP32(eval);
769 // If the symbol is undefined, make sure to pass the symbol in
770 // to the MarkRelocatable() function.
771 if (!(eattr & DEFINED))
772 MarkRelocatable(sno, loc, 0, flags, esym);
774 MarkRelocatable(sno, loc, tdb, flags, NULL);
776 SETBE32(locp, 0, eval);
779 // Fixup a 3-bit "QUICK" reference in bits 9..1
780 // (range of 1..8) in a word. Really bits 1..3 in a byte.
782 if (!(eattr & DEFINED))
784 error("External quick reference");
788 if (eval < 1 || eval > 8)
791 *locp |= (eval & 7) << 1;
794 // Fix up 6502 funny branch
798 if (eval + 0x80 >= 0x100)
801 *locp = (uint8_t)eval;
805 // Bad fixup type--this should *never* happen!
811 error("expression out of range");
822 // Resolve all fixups
824 int ResolveAllFixups(void)
826 // Make undefined symbols GLOBL
828 ForceUndefinedSymbolsGlobal();
830 DEBUG printf("Resolving TEXT sections...\n");
832 DEBUG printf("Resolving DATA sections...\n");
834 DEBUG printf("Resolving 6502 sections...\n");
835 ResolveFixups(M6502); /* fixup 6502 section (if any) */