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-2019 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 // 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[] = {
55 // Offset to REAL fixup location
56 static uint8_t fusizoffs[] = {
69 // Initialize sections; setup initial ABS, TEXT, DATA and BSS sections
71 void InitSection(void)
73 // Initialize all sections
74 for(int i=0; i<NSECTS; i++)
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
84 // Switch to TEXT for starters
90 // Make a new (clean) section
92 void MakeSection(int sno, uint16_t attr)
94 SECT * sp = §[sno];
98 sp->scode = sp->sfcode = NULL;
99 sp->sfix = sp->sffix = NULL;
104 // Switch to another section (copy section & chunk descriptors to global vars
107 void SwitchSection(int sno)
111 SECT * sp = §[sno];
113 m6502 = (sno == M6502); // Set 6502-mode flag
119 orgaddr = sp->orgaddr;
121 // Copy code chunk vars
122 if ((cp = scode) != NULL)
124 challoc = cp->challoc;
125 ch_size = cp->ch_size;
126 chptr = cp->chptr + ch_size;
128 // For 6502 mode, add the last org'd address
131 Because the way this is set up it treats the 6502 assembly space as a single 64K space (+ 16 bytes, for some reason) and just bobbles around inside that space and uses a stack of org "pointers" to show where the data ended up.
133 This is a piss poor way to handle things, and for fucks sake, we can do better than this!
136 chptr = cp->chptr + orgaddr;
139 challoc = ch_size = 0;
144 // Save current section
146 void SaveSection(void)
148 SECT * sp = §[cursect];
150 sp->scattr = scattr; // Bailout section vars
152 sp->orgaddr = orgaddr;
154 if (scode != NULL) // Bailout code chunk
155 scode->ch_size = ch_size;
160 // Test to see if a location has a fixup set on it. This is used by the
161 // listing generator to print 'xx's instead of '00's for forward references
163 int fixtest(int sno, uint32_t loc)
165 // Force update to sect[] variables
168 // Ugly linear search for a mark on our location. The speed doesn't
169 // matter, since this is only done when generating a listing, which is
171 for(FIXUP * fp=sect[sno].sffix; fp!=NULL; fp=fp->next)
173 uint32_t w = fp->attr;
174 uint32_t xloc = fp->loc + (int)fusizoffs[w & FUMASK];
177 return (int)fusiztab[w & FUMASK];
185 // Check that there are at least 'amt' bytes left in the current chunk. If
186 // there are not, allocate another chunk of at least CH_CODE_SIZE bytes or
187 // 'amt', whichever is larger.
189 // If 'amt' is zero, ensure there are at least CH_THRESHOLD bytes, likewise.
191 void chcheck(uint32_t amt)
193 DEBUG { printf("chcheck(%u)\n", amt); }
195 // If in BSS section, no allocation required
202 DEBUG { printf(" challoc=%i, ch_size=%i, diff=%i\n", challoc, ch_size, challoc - ch_size); }
204 if ((int)(challoc - ch_size) >= (int)amt)
207 if (amt < CH_CODE_SIZE)
210 DEBUG { printf(" amt (adjusted)=%u\n", amt); }
211 SECT * p = §[cursect];
212 CHUNK * cp = malloc(sizeof(CHUNK) + amt);
214 // First chunk in section
220 // Add chunk to other chunks
225 scode->ch_size = ch_size; // Save old chunk's globals
228 // Setup chunk and global vars
231 challoc = cp->challoc = amt;
232 ch_size = cp->ch_size = 0;
233 chptr = cp->chptr = ((uint8_t *)cp) + sizeof(CHUNK);
234 scode = p->scode = cp;
241 // Arrange for a fixup on a location
243 int AddFixup(uint32_t attr, uint32_t loc, TOKEN * fexpr)
245 uint16_t exprlen = 0;
247 uint32_t _orgaddr = 0;
249 // First, check to see if the expression is a bare label, otherwise, force
250 // the FU_EXPR flag into the attributes and count the tokens.
251 if ((fexpr[0] == SYMBOL) && (fexpr[2] == ENDEXPR))
253 symbol = symbolPtr[fexpr[1]];
255 // Save the org address for JR RISC instruction
256 if ((attr & FUMASKRISC) == FU_JR)
262 exprlen = ExpressionLength(fexpr);
265 // Second, check to see if it's a DSP56001 fixup, and force the FU_56001
266 // flag into the attributes if so; also save the current org address.
267 if (attr & FUMASKDSP)
273 // Allocate space for the fixup + any expression
274 FIXUP * fixup = malloc(sizeof(FIXUP) + (sizeof(TOKEN) * exprlen));
276 // Store the relevant fixup information in the FIXUP
280 fixup->fileno = cfileno;
281 fixup->lineno = curlineno;
283 fixup->symbol = symbol;
284 fixup->orgaddr = _orgaddr;
286 // Copy the passed in expression to the FIXUP, if any
289 fixup->expr = (TOKEN *)((uint8_t *)fixup + sizeof(FIXUP));
290 memcpy(fixup->expr, fexpr, sizeof(TOKEN) * exprlen);
293 // Finally, put the FIXUP in the current section's linked list
294 if (sect[cursect].sffix == NULL)
296 sect[cursect].sffix = fixup;
297 sect[cursect].sfix = fixup;
301 sect[cursect].sfix->next = fixup;
302 sect[cursect].sfix = fixup;
305 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);
307 printf(" name: %s, value: $%lX\n", symbol->sname, symbol->svalue);
315 // Resolve fixups in the passed in section
317 int ResolveFixups(int sno)
319 SECT * sc = §[sno];
321 // "Cache" first chunk
322 CHUNK * cch = sc->sfcode;
324 // Can't fixup a section with nothing in it
328 // Wire the 6502 segment's size to its allocated size (64K)
330 cch->ch_size = cch->challoc;
332 // Get first fixup for the passed in section
333 FIXUP * fixup = sect[sno].sffix;
335 while (fixup != NULL)
337 // We do it this way because we have continues everywhere... :-P
341 uint32_t dw = fup->attr; // Fixup long (type + modes + flags)
342 uint32_t loc = fup->loc; // Location to fixup
343 cfileno = fup->fileno;
344 curlineno = fup->lineno;
345 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); }
347 // This is based on global vars cfileno, curfname :-P
348 // This approach is kinda meh as well. I think we can do better
350 SetFilenameForErrorReporting();
352 // Search for chunk containing location to fix up; compute a
353 // pointer to the location (in the chunk). Often we will find the
354 // Fixup is in the "cached" chunk, so the linear-search is seldom
356 if (loc < cch->chloc || loc >= (cch->chloc + cch->ch_size))
358 for(cch=sc->sfcode; cch!=NULL; cch=cch->chnext)
360 if (loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
366 // Fixup (loc) is out of range--this should never happen!
367 // Once we call this function, it winds down immediately; it
373 // Location to fix (in current chunk)
374 // We use the address of the chunk that loc is actually in, then
375 // subtract the chunk's starting location from loc to get the offset
376 // into the current chunk.
377 uint8_t * locp = cch->chptr + (loc - cch->chloc);
379 uint16_t eattr = 0; // Expression attrib
380 SYM * esym = NULL; // External symbol involved in expr
381 uint64_t eval; // Expression value
382 uint16_t flags; // Mark flags
384 // Compute expression/symbol value and attributes
386 // Complex expression
389 if (evexpr(fup->expr, &eval, &eattr, &esym) != OK)
395 SYM * sy = fup->symbol;
403 // If the symbol is not defined, but global, set esym to sy
404 if ((eattr & (GLOBAL | DEFINED)) == GLOBAL)
408 uint16_t tdb = eattr & TDB;
410 // If the expression/symbol is undefined and no external symbol is
411 // involved, then that's an error.
412 if (!(eattr & DEFINED) && (esym == NULL))
420 // If a PC-relative fixup is undefined, its value is *not* subtracted
421 // from the location (that will happen in the linker when the external
422 // reference is resolved).
424 // MWC expects PC-relative things to have the LOC subtracted from the
425 // value, if the value is external (that is, undefined at this point).
427 // PC-relative fixups must be DEFINED and either in the same section
428 // (whereupon the subtraction takes place) or ABS (with no subtract).
437 // Allow cross-section PCREL fixups in Alcyon mode
443 // Shouldn't there be a break here, since otherwise, it will point to the DATA section?
446 eval += sect[TEXT].sloc;
449 eval += sect[TEXT].sloc + sect[DATA].sloc;
452 error("invalid section");
460 error("PC-relative expr across sections");
465 if (sbra_flag && (dw & FU_LBRA) && (eval + 0x80 < 0x100))
466 warn("unoptimized short branch");
468 else if (obj_format == MWC)
475 // Handle fixup classes
478 // FU_BBRA fixes up a one-byte branch offset.
480 if (!(eattr & DEFINED))
482 error("external short branch");
488 if (eval + 0x80 >= 0x100)
493 if (CHECK_OPTS(OPT_NULL_BRA))
502 error("illegal bra.s with zero offset");
507 *++locp = (uint8_t)eval;
510 // Fixup one-byte value at locp + 1.
515 // Fixup one-byte forward references
517 if (!(eattr & DEFINED))
519 error("external byte reference");
525 error("non-absolute byte reference");
529 if ((dw & FU_PCREL) && ((eval + 0x80) >= 0x100))
534 if ((eval + 0x100) >= 0x200)
537 else if (eval >= 0x100)
540 *locp = (uint8_t)eval;
543 // Fixup high/low byte off word for 6502
545 if (!(eattr & DEFINED))
547 error("external byte reference");
551 *locp = (uint8_t)(eval >> 8);
555 if (!(eattr & DEFINED))
557 error("external byte reference");
561 *locp = (uint8_t)eval;
564 // Fixup WORD forward references; the word could be unaligned in the
565 // section buffer, so we have to be careful. (? careful about what?)
567 if ((dw & FUMASKRISC) == FU_JR)
573 reg = (signed)((eval - (fup->orgaddr + 2)) / 2);
575 reg = (signed)((eval - (loc + 2)) / 2);
577 int reg = (signed)((eval - ((fup->orgaddr ? fup->orgaddr : loc) + 2)) / 2);
580 if ((reg < -16) || (reg > 15))
582 error("relative jump out of range");
586 *locp |= ((uint8_t)reg >> 3) & 0x03;
588 *locp |= ((uint8_t)reg & 0x07) << 5;
591 else if ((dw & FUMASKRISC) == FU_NUM15)
593 if (((int)eval < -16) || ((int)eval > 15))
595 error("constant out of range (-16 - +15)");
599 *locp |= ((uint8_t)eval >> 3) & 0x03;
601 *locp |= ((uint8_t)eval & 0x07) << 5;
604 else if ((dw & FUMASKRISC) == FU_NUM31)
608 error("constant out of range (0-31)");
612 *locp |= ((uint8_t)eval >> 3) & 0x03;
614 *locp |= ((uint8_t)eval & 0x07) << 5;
617 else if ((dw & FUMASKRISC) == FU_NUM32)
619 if ((eval < 1) || (eval > 32))
621 error("constant out of range (1-32)");
628 eval = (eval == 32) ? 0 : eval;
629 *locp |= ((uint8_t)eval >> 3) & 0x03;
631 *locp |= ((uint8_t)eval & 0x07) << 5;
634 else if ((dw & FUMASKRISC) == FU_REGONE)
638 error("register one value out of range");
642 *locp |= ((uint8_t)eval >> 3) & 0x03;
644 *locp |= ((uint8_t)eval & 0x07) << 5;
647 else if ((dw & FUMASKRISC) == FU_REGTWO)
651 error("register two value out of range");
656 *locp |= (uint8_t)eval & 0x1F;
660 if (!(eattr & DEFINED))
667 MarkRelocatable(sno, loc, 0, flags, esym);
672 MarkRelocatable(sno, loc, tdb, MWORD, NULL);
676 if (eval + 0x10000 >= 0x20000)
681 // Range-check BRA and DBRA
684 if (eval + 0x8000 >= 0x10000)
687 else if (eval >= 0x10000)
692 // 6502 words are little endian, so handle that here
694 SETLE16(locp, 0, eval)
696 SETBE16(locp, 0, eval)
700 // Fixup LONG forward references; the long could be unaligned in the
701 // section buffer, so be careful (again).
705 if ((dw & FUMASKRISC) == FU_MOVEI)
707 // Long constant in MOVEI # is word-swapped, so fix it here
708 eval = WORDSWAP32(eval);
712 // If the symbol is undefined, make sure to pass the symbol in
713 // to the MarkRelocatable() function.
714 if (!(eattr & DEFINED))
715 MarkRelocatable(sno, loc, 0, flags, esym);
717 MarkRelocatable(sno, loc, tdb, flags, NULL);
719 SETBE32(locp, 0, eval);
722 // Fixup QUAD forward references (mainly used by the OP assembler)
726 uint64_t quad = GETBE64(locp, 0);
727 uint64_t addr = eval;
729 //Hmm, not sure how this can be set, since it's only set if it's a DSP56001 fixup or a FU_JR... :-/
733 eval = (quad & 0xFFFFFC0000FFFFFFLL) | ((addr & 0x3FFFF8) << 21);
735 else if (dw & FU_OBJDATA)
737 // If it's in a TEXT or DATA section, be sure to mark for a
740 MarkRelocatable(sno, loc, tdb, MQUAD, NULL);
742 uint64_t quad = GETBE64(locp, 0);
743 uint64_t addr = eval;
745 //Hmm, not sure how this can be set, since it's only set if it's a DSP56001 fixup or a FU_JR... :-/
749 eval = (quad & 0x000007FFFFFFFFFFLL) | ((addr & 0xFFFFF8) << 40);
752 SETBE64(locp, 0, eval);
755 // Fixup a 3-bit "QUICK" reference in bits 9..1
756 // (range of 1..8) in a word. [Really bits 1..3 in a byte.]
758 if (!(eattr & DEFINED))
760 error("External quick reference");
764 if ((eval < 1) || (eval > 8))
767 *locp |= (eval & 7) << 1;
770 // Fix up 6502 funny branch
774 if (eval + 0x80 >= 0x100)
777 *locp = (uint8_t)eval;
780 // Fixup DSP56001 addresses
782 switch (dw & FUMASKDSP)
784 // DSPIMM5 actually is clamped from 0 to 23 for our purposes
785 // and does not use the full 5 bit range.
789 error("immediate value must be between 0 and 23");
796 // This is a 12-bit address encoded into the lower 12
797 // bits of a DSP word
801 error("address out of range ($000-$FFF)");
805 locp[1] |= eval >> 8;
806 locp[2] = eval & 0xFF;
809 // This is a full DSP word containing Effective Address Extension
812 if (eval >= 0x100000)
814 error("value out of range ($000-$FFFFFF)");
818 *locp++ = (uint32_t)eval >> 16;
819 *locp++ = ((uint32_t)eval >> 8) & 0xFF;
820 *locp++ = (uint32_t)eval & 0xFF;
823 // This is a 16bit absolute address into a 24bit field
827 error("address out of range ($0000-$FFFF)");
831 locp[1] = (uint8_t)(eval >> 8);
832 locp[2] = (uint8_t)eval;
835 // This is 12-bit immediate short data
836 // The upper nibble goes into the last byte's low nibble
837 // while the remainder 8 bits go into the 2nd byte.
841 error("immediate out of range ($000-$FFF)");
845 locp[1] = (uint8_t)eval;
846 locp[2] |= (uint8_t)(eval >> 8);
849 // This is 8-bit immediate short data
850 // which goes into the middle byte of a DSP word.
854 error("immediate out of range ($00-$FF)");
858 locp[1] = (uint8_t)eval;
861 // This is a 6 bit absoulte short address. It occupies
862 // the low 6 bits of the middle byte of a DSP word.
866 error("address must be between 0 and 63");
873 // This is a 6 bit absoulte short address. It occupies
874 // the low 6 bits of the middle byte of a DSP word.
876 if (eval < 0xFFFFFFC0)
878 error("address must be between $FFC0 and $FFFF");
882 locp[1] |= eval & 0x3F;
885 // Shamus: I'm pretty sure these don't make any sense...
887 warn("FU_DSPIMMFL8 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
891 warn("FU_DSPIMMFL16 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
895 warn("FU_DSPIMMFL24 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
898 // Bad fixup type--this should *never* happen!
905 // Fixup a 4-byte float
907 warn("FU_FLOATSING missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
910 // Fixup a 8-byte float
912 warn("FU_FLOATDOUB missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
915 // Fixup a 12-byte float
917 warn("FU_FLOATEXT missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
921 // Bad fixup type--this should *never* happen!
922 // Once we call this function, it winds down immediately; it
929 error("expression out of range");
937 // Resolve all fixups
939 int ResolveAllFixups(void)
941 // Make undefined symbols GLOBL
943 ForceUndefinedSymbolsGlobal();
945 DEBUG printf("Resolving TEXT sections...\n");
947 DEBUG printf("Resolving DATA sections...\n");
949 DEBUG printf("Resolving 6502 sections...\n");
950 ResolveFixups(M6502); // Fixup 6502 section (if any)