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
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 * p = §[sno];
98 p->scode = p->sfcode = NULL;
99 p->sfix = p->sffix = NULL;
104 // Switch to another section (copy section & chunk descriptors to global vars
107 void SwitchSection(int sno)
111 SECT * p = §[sno];
113 m6502 = (sno == M6502); // Set 6502-mode flag
119 orgaddr = p->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
130 chptr = cp->chptr + orgaddr;
133 challoc = ch_size = 0;
138 // Save current section
140 void SaveSection(void)
142 SECT * p = §[cursect];
144 p->scattr = scattr; // Bailout section vars
146 p->orgaddr = orgaddr;
148 if (scode != NULL) // Bailout code chunk
149 scode->ch_size = ch_size;
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
157 int fixtest(int sno, uint32_t loc)
159 // Force update to sect[] variables
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
165 for(FIXUP * fp=sect[sno].sffix; fp!=NULL; fp=fp->next)
167 uint32_t w = fp->attr;
168 uint32_t xloc = fp->loc + (int)fusizoffs[w & FUMASK];
171 return (int)fusiztab[w & FUMASK];
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 CH_CODE_SIZE bytes or
181 // 'amt', whichever is larger.
183 // If 'amt' is zero, ensure there are at least CH_THRESHOLD bytes, likewise.
185 void chcheck(uint32_t amt)
187 DEBUG { printf("chcheck(%u)\n", amt); }
189 // If in BSS section, no allocation required
196 DEBUG { printf(" challoc=%i, ch_size=%i, diff=%i\n", challoc, ch_size, challoc - ch_size); }
198 if ((int)(challoc - ch_size) >= (int)amt)
201 if (amt < CH_CODE_SIZE)
204 DEBUG { printf(" amt (adjusted)=%u\n", amt); }
205 SECT * p = §[cursect];
206 CHUNK * cp = malloc(sizeof(CHUNK) + amt);
208 // First chunk in section
214 // Add chunk to other chunks
219 scode->ch_size = ch_size; // Save old chunk's globals
222 // Setup chunk and global vars
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;
235 // Arrange for a fixup on a location
237 int AddFixup(uint32_t attr, uint32_t loc, TOKEN * fexpr)
239 uint16_t exprlen = 0;
241 uint32_t _orgaddr = 0;
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))
247 symbol = symbolPtr[fexpr[1]];
249 // Save the org address for JR RISC instruction
250 if ((attr & FUMASKRISC) == FU_JR)
256 exprlen = ExpressionLength(fexpr);
259 // Allocate space for the fixup + any expression
260 FIXUP * fixup = malloc(sizeof(FIXUP) + (sizeof(TOKEN) * exprlen));
262 // Store the relevant fixup information in the FIXUP
266 fixup->fileno = cfileno;
267 fixup->lineno = curlineno;
269 fixup->symbol = symbol;
270 fixup->orgaddr = _orgaddr;
272 // Copy the passed in expression to the FIXUP, if any
275 fixup->expr = (TOKEN *)((uint8_t *)fixup + sizeof(FIXUP));
276 memcpy(fixup->expr, fexpr, sizeof(TOKEN) * exprlen);
279 // Finally, put the FIXUP in the current section's linked list
280 if (sect[cursect].sffix == NULL)
282 sect[cursect].sffix = fixup;
283 sect[cursect].sfix = fixup;
287 sect[cursect].sfix->next = fixup;
288 sect[cursect].sfix = fixup;
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);
293 printf(" name: %s, value: $lX\n", symbol->sname, symbol->svalue);
301 // Resolve fixups in a section
303 int ResolveFixups(int sno)
305 uint64_t eval; // Expression value
309 SECT * sc = §[sno];
311 // "Cache" first chunk
312 CHUNK * cch = sc->sfcode;
314 // Can't fixup a section with nothing in it
318 // Wire the 6502 segment's size to its allocated size (64K)
320 cch->ch_size = cch->challoc;
322 // Get first fixup for the passed in section
323 FIXUP * fixup = sect[sno].sffix;
325 while (fixup != NULL)
327 // We do it this way because we have continues everywhere... :-P
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); }
337 // This is based on global vars cfileno, curfname :-P
338 // This approach is kinda meh as well. I think we can do better
340 SetFilenameForErrorReporting();
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
346 if (loc < cch->chloc || loc >= (cch->chloc + cch->ch_size))
348 for(cch=sc->sfcode; cch!=NULL; cch=cch->chnext)
350 if (loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
356 // Fixup (loc) is out of range--this should never happen!
357 // Once we call this function, it winds down immediately; it
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
371 // Compute expression/symbol value and attributes
373 // Complex expression
376 if (evexpr(fup->expr, &eval, &eattr, &esym) != OK)
382 SYM * sy = fup->symbol;
390 // If the symbol is not defined, but global, set esym to sy
391 if ((eattr & (GLOBAL | DEFINED)) == GLOBAL)
395 uint16_t tdb = eattr & TDB;
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))
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).
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
415 // PC-relative fixups must be DEFINED and either in the same
416 // section (whereupon the subtraction takes place) or ABS (with no
426 // Allow cross-section PCREL fixups in Alcyon mode
432 // Shouldn't there be a break here, since otherwise, it will point to the DATA section?
435 eval += sect[TEXT].sloc;
438 eval += sect[TEXT].sloc + sect[DATA].sloc;
441 error("invalid section");
449 error("PC-relative expr across sections");
454 if (sbra_flag && (w & FU_LBRA) && (eval + 0x80 < 0x100))
455 warn("unoptimized short branch");
457 else if (obj_format == MWC)
464 // Handle fixup classes
467 // FU_BBRA fixes up a one-byte branch offset.
469 if (!(eattr & DEFINED))
471 error("external short branch");
477 if (eval + 0x80 >= 0x100)
482 if (CHECK_OPTS(OPT_NULL_BRA))
491 error("illegal bra.s with zero offset");
496 *++locp = (uint8_t)eval;
499 // Fixup one-byte value at locp + 1.
504 // Fixup one-byte forward references
506 if (!(eattr & DEFINED))
508 error("external byte reference");
514 error("non-absolute byte reference");
518 if ((w & FU_PCREL) && ((eval + 0x80) >= 0x100))
523 if ((eval + 0x100) >= 0x200)
526 else if (eval >= 0x100)
529 *locp = (uint8_t)eval;
532 // Fixup high/low byte off word for 6502
534 if (!(eattr & DEFINED))
536 error("external byte reference");
540 *locp = (uint8_t)((eval >> 8) & 0xFF);
544 if (!(eattr & DEFINED))
546 error("external byte reference");
550 *locp = (uint8_t)(eval & 0xFF);
553 // Fixup WORD forward references; the word could be unaligned in
554 // the section buffer, so we have to be careful.
556 if ((w & FUMASKRISC) == FU_JR)
559 reg2 = (signed)((eval - (fup->orgaddr + 2)) / 2);
561 reg2 = (signed)((eval - (loc + 2)) / 2);
563 if ((reg2 < -16) || (reg2 > 15))
565 error("relative jump out of range");
569 *locp = (uint8_t)(*locp | ((reg2 >> 3) & 0x03));
571 *locp = (uint8_t)(*locp | ((reg2 & 0x07) << 5));
574 else if ((w & FUMASKRISC) == FU_NUM15)
576 if (eval < -16 || eval > 15)
578 error("constant out of range");
582 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
584 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
587 else if ((w & FUMASKRISC) == FU_NUM31)
591 error("constant out of range");
595 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
597 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
600 else if ((w & FUMASKRISC) == FU_NUM32)
602 if (eval < 1 || eval > 32)
604 error("constant out of range");
611 eval = (eval == 32) ? 0 : eval;
612 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
614 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
617 else if ((w & FUMASKRISC) == FU_REGONE)
621 error("register value out of range");
625 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
627 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
630 else if ((w & FUMASKRISC) == FU_REGTWO)
634 error("register value out of range");
639 *locp = (uint8_t)(*locp | (eval & 0x1F));
643 if (!(eattr & DEFINED))
650 MarkRelocatable(sno, loc, 0, flags, esym);
655 MarkRelocatable(sno, loc, tdb, MWORD, NULL);
659 if (eval + 0x10000 >= 0x20000)
664 // Range-check BRA and DBRA
667 if (eval + 0x8000 >= 0x10000)
670 else if (eval >= 0x10000)
675 // 6502 words are little endian, so handle that here
677 SETLE16(locp, 0, eval)
679 SETBE16(locp, 0, eval)
683 // Fixup LONG forward references;
684 // the long could be unaligned in the section buffer, so be careful
689 if ((w & FUMASKRISC) == FU_MOVEI)
691 // Long constant in MOVEI # is word-swapped, so fix it here
692 eval = WORDSWAP32(eval);
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);
701 MarkRelocatable(sno, loc, tdb, flags, NULL);
703 SETBE32(locp, 0, eval);
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.]
709 if (!(eattr & DEFINED))
711 error("External quick reference");
715 if ((eval < 1) || (eval > 8))
718 *locp |= (eval & 7) << 1;
721 // Fix up 6502 funny branch
725 if (eval + 0x80 >= 0x100)
728 *locp = (uint8_t)eval;
732 // Bad fixup type--this should *never* happen!
733 // Once we call this function, it winds down immediately; it
740 error("expression out of range");
748 // Resolve all fixups
750 int ResolveAllFixups(void)
752 // Make undefined symbols GLOBL
754 ForceUndefinedSymbolsGlobal();
756 DEBUG printf("Resolving TEXT sections...\n");
758 DEBUG printf("Resolving DATA sections...\n");
760 DEBUG printf("Resolving 6502 sections...\n");
761 ResolveFixups(M6502); // Fixup 6502 section (if any)