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-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 // Minimum size of a fixup record
23 #define MIN_FIXUP_MEM (3 * sizeof(uint16_t) + 1 * sizeof(uint32_t))
25 // Function prototypes
26 void MakeSection(int, uint16_t);
27 void SwitchSection(int);
29 // Section descriptors
30 SECT sect[NSECTS]; // All sections...
31 int cursect; // Current section number
33 // These are copied from the section descriptor, the current code chunk
34 // descriptor and the current fixup chunk descriptor when a switch is made into
35 // a section. They are copied back to the descriptors when the section is left.
36 uint16_t scattr; // Section attributes
37 uint32_t sloc; // Current loc in section
39 CHUNK * scode; // Current (last) code chunk
40 uint32_t challoc; // # bytes alloc'd to code chunk
41 uint32_t ch_size; // # bytes used in code chunk
42 uint8_t * chptr; // Deposit point in code chunk buffer
43 uint8_t * chptr_opcode; // Backup of chptr, updated before entering code generators
45 CHUNK * sfix; // Current (last) fixup chunk
46 uint32_t fchalloc; // # bytes alloc'd to fixup chunk
47 uint32_t fchsize; // # bytes used in fixup chunk
48 PTR fchptr; // Deposit point in fixup chunk buffer
50 // Return a size (SIZB, SIZW, SIZL) or 0, depending on what kind of fixup is
51 // associated with a location.
52 static uint8_t fusiztab[] = {
63 // Offset to REAL fixup location
64 static uint8_t fusizoffs[] = {
77 // Initialize sections; setup initial ABS, TEXT, DATA and BSS sections
79 void InitSection(void)
81 // Cleanup all sections
82 for(int i=0; i<NSECTS; i++)
85 // Construct default sections, make TEXT the current section
86 MakeSection(ABS, SUSED | SABS | SBSS); // ABS
87 MakeSection(TEXT, SUSED | TEXT ); // TEXT
88 MakeSection(DATA, SUSED | DATA ); // DATA
89 MakeSection(BSS, SUSED | BSS | SBSS); // BSS
90 MakeSection(M6502, SUSED | TEXT ); // 6502 code section
92 // Switch to TEXT for starters
98 // Make a new (clean) section
100 void MakeSection(int sno, uint16_t attr)
102 SECT * p = §[sno];
105 p->scode = p->sfcode = NULL;
106 p->sfix = p->sffix = NULL;
111 // Switch to another section (copy section & chunk descriptors to global vars
114 void SwitchSection(int sno)
118 SECT * p = §[sno];
120 m6502 = (sno == M6502); // Set 6502-mode flag
128 // Copy code chunk vars
129 if ((cp = scode) != NULL)
131 challoc = cp->challoc;
132 ch_size = cp->ch_size;
133 chptr = cp->chptr + ch_size;
135 // For 6502 mode, add the last org'd address
137 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;
269 // A fixup record is at least 4 pieces of data long, with some optional data at
270 // the end. Is of the form:
274 // FU_EXPR.W FU_EXPR.W fixup type
275 // loc.L loc.L location in section
276 // fileno.W fileno.W file number fixup occurred in
277 // lineno.W lineno.W line number fixup occurred in
278 // symbol.* size.W &symbol (32 or 64 bits) / size of expression
279 // token.L size (zero or more) TOKENS (32-bits each)
280 // ENDEXPR.L End of expression (with size > zero)
281 // JR.L Possible ORG address of RISC JR instruction
283 // Arrange for a fixup on a location
285 int AddFixup(uint16_t attr, uint32_t loc, TOKENPTR fexpr)
287 uint32_t i = MIN_FIXUP_MEM;
290 DEBUG printf("FIXUP@$%X: $%X\n", loc, attr);
292 // Compute length of expression (could be faster); determine if it's the
293 // single-symbol case; no expression if it's just a mark. (? is this true?)
294 if ((*fexpr.u32 == SYMBOL) && (fexpr.u32[2] == ENDEXPR))
296 // Just a single symbol, possibly followed by a DWORD
299 // SCPCD: Correct bit mask for attr (else other FU_xxx will match)
301 if ((attr & FUMASKRISC) == FU_JR)
302 i += sizeof(uint32_t);
308 // Count the # of tokens in the expression
309 for(len=0; fexpr.u32[len]!=ENDEXPR; len++)
311 // Add one to len for 2X tokens, two for 3X tokens
312 if (fexpr.u32[len] == SYMBOL)
314 else if (fexpr.u32[len] == CONST)
320 i += sizeof(uint16_t) + (len * sizeof(TOKEN));
323 // Alloc another fixup chunk for this one to fit in if necessary
324 if ((fchalloc - fchsize) < i)
326 SECT * p = §[cursect];
327 CHUNK * 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++ = curlineno;
358 // Store postfix expression or pointer to a single symbol, or nothing for a
359 // mark (a zero word is stored in this case--[? is it?]).
365 *fchptr.lp++ = *fexpr.u32++;
369 *fchptr.sy++ = symbolPtr[fexpr.u32[1]];
371 // SCPCD: Correct bit mask for attr (else other FU_xxx will match)
373 if ((attr & FUMASKRISC) == FU_JR)
376 *fchptr.lp++ = orgaddr;
378 *fchptr.lp++ = 0x00000000;
388 // Resolve fixups in a section
390 int ResolveFixups(int sno)
392 PTR fup; // Current fixup
393 uint64_t eval; // Expression value
394 SYM * sy; // (Temp) pointer to a symbol
395 uint16_t i; // (Temp) word
399 SECT * sc = §[sno];
400 CHUNK * ch = sc->sffix;
405 // "Cache" first chunk
406 CHUNK * cch = sc->sfcode;
408 // Can't fixup a section with nothing in it
412 // Wire the 6502 segment's size to its allocated size (64K)
414 cch->ch_size = cch->challoc;
418 fup.cp = ch->chptr; // fup -> start of chunk
419 uint16_t * fuend = (uint16_t *)(fup.cp + ch->ch_size); // fuend -> end of chunk
421 while (fup.wp < fuend)
423 uint16_t w = *fup.wp++; // Fixup word (type+modes+flags)
424 uint32_t loc = *fup.lp++; // Location to fixup
426 curlineno = *fup.wp++;
427 DEBUG { printf("ResolveFixups: cfileno=%u\n", cfileno); }
429 // This is based on global vars cfileno, curfname :-P
430 // This approach is kinda meh as well. I think we can do better
432 SetFilenameForErrorReporting();
434 SYM * esym = NULL; // External symbol involved in expr
436 // Search for chunk containing location to fix up; compute a
437 // pointer to the location (in the chunk). Often we will find the
438 // Fixup is in the "cached" chunk, so the linear-search is seldom
440 if (loc < cch->chloc || loc >= (cch->chloc + cch->ch_size))
442 for(cch=sc->sfcode; cch!=NULL; cch=cch->chnext)
444 if (loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
450 // Fixup (loc) out of range
456 // Location to fix (in cached chunk)
457 uint8_t * locp = cch->chptr + (loc - cch->chloc);
458 uint16_t eattr = 0; // Expression attrib
460 // Compute expression/symbol value and attribs
462 // Complex expression
467 if (evexpr((TOKENPTR)fup.tk, &eval, &eattr, &esym) != OK)
486 // If the symbol is not defined, but global, set esym to sy
487 if ((eattr & (GLOBAL | DEFINED)) == GLOBAL)
491 uint16_t tdb = eattr & TDB;
493 // If the expression is undefined and no external symbol is
494 // involved, then that's an error.
495 if (!(eattr & DEFINED) && (esym == NULL))
503 // If a PC-relative fixup is undefined, its value is *not*
504 // subtracted from the location (that will happen in the linker
505 // when the external reference is resolved).
507 // MWC expects PC-relative things to have the LOC subtracted from
508 // the value, if the value is external (that is, undefined at this
511 // PC-relative fixups must be DEFINED and either in the same
512 // section (whereupon the subtraction takes place) or ABS (with no
519 eval -= (uint32_t)loc;
522 error("PC-relative expr across sections");
526 if (sbra_flag && (w & FU_LBRA) && (eval + 0x80 < 0x100))
527 warn("unoptimized short branch");
529 else if (obj_format == MWC)
530 eval -= (uint32_t)loc;
539 // FU_BBRA fixes up a one-byte branch offset.
541 if (!(eattr & DEFINED))
543 error("external short branch");
549 if (eval + 0x80 >= 0x100)
554 error("illegal bra.s with zero offset");
558 *++locp = (uint8_t)eval;
560 // Fixup one-byte value at locp + 1.
564 // Fixup one-byte forward references
566 if (!(eattr & DEFINED))
568 error("external byte reference");
574 error("non-absolute byte reference");
578 if ((w & FU_PCREL) && eval + 0x80 >= 0x100)
583 if (eval + 0x100 >= 0x200)
586 else if (eval >= 0x100)
589 *locp = (uint8_t)eval;
591 // Fixup high/low byte off word for 6502
593 if (!(eattr & DEFINED))
595 error("external byte reference");
599 *locp = (uint8_t)((eval >> 8) & 0xFF);
602 if (!(eattr & DEFINED))
604 error("external byte reference");
608 *locp = (uint8_t)(eval & 0xFF);
610 // Fixup WORD forward references;
611 // the word could be unaligned in the section buffer, so we have to
614 if ((w & FUMASKRISC) == FU_JR)
616 uint32_t orgaddr = *fup.lp++;
619 reg2 = (signed)((eval - (orgaddr + 2)) / 2);
621 reg2 = (signed)((eval - (loc + 2)) / 2);
623 if ((reg2 < -16) || (reg2 > 15))
625 error("relative jump out of range");
629 *locp = (uint8_t)(*locp | ((reg2 >> 3) & 0x03));
631 *locp = (uint8_t)(*locp | ((reg2 & 0x07) << 5));
635 if ((w & FUMASKRISC) == FU_NUM15)
637 if (eval < -16 || eval > 15)
639 error("constant out of range");
643 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
645 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
649 if ((w & FUMASKRISC) == FU_NUM31)
651 if (eval < 0 || eval > 31)
653 error("constant out of range");
657 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
659 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
663 if ((w & FUMASKRISC) == FU_NUM32)
665 if (eval < 1 || eval > 32)
667 error("constant out of range");
674 eval = (eval == 32) ? 0 : eval;
675 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
677 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
681 if ((w & FUMASKRISC) == FU_REGONE)
683 if (eval < 0 || eval > 31)
685 error("register value out of range");
689 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
691 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
695 if ((w & FUMASKRISC) == FU_REGTWO)
697 if (eval < 0 || eval > 31)
699 error("register value out of range");
704 *locp = (uint8_t)(*locp | (eval & 0x1F));
708 if (!(eattr & DEFINED))
715 MarkRelocatable(sno, loc, 0, flags, esym);
720 MarkRelocatable(sno, loc, tdb, MWORD, NULL);
724 if (eval + 0x10000 >= 0x20000)
729 // Range-check BRA and DBRA
732 if (eval + 0x8000 >= 0x10000)
735 else if (eval >= 0x10000)
740 // 6502 words are little endian, so handle that here
742 SETLE16(locp, 0, eval)
744 SETBE16(locp, 0, eval)
747 // Fixup LONG forward references;
748 // the long could be unaligned in the section buffer, so be careful
753 if ((w & FUMASKRISC) == FU_MOVEI)
755 // Long constant in MOVEI # is word-swapped, so fix it here
756 eval = WORDSWAP32(eval);
760 // If the symbol is undefined, make sure to pass the symbol in
761 // to the MarkRelocatable() function.
762 if (!(eattr & DEFINED))
763 MarkRelocatable(sno, loc, 0, flags, esym);
765 MarkRelocatable(sno, loc, tdb, flags, NULL);
767 SETBE32(locp, 0, eval);
770 // Fixup a 3-bit "QUICK" reference in bits 9..1
771 // (range of 1..8) in a word. Really bits 1..3 in a byte.
773 if (!(eattr & DEFINED))
775 error("External quick reference");
779 if (eval < 1 || eval > 8)
782 *locp |= (eval & 7) << 1;
785 // Fix up 6502 funny branch
789 if (eval + 0x80 >= 0x100)
792 *locp = (uint8_t)eval;
796 // Bad fixup type--this should *never* happen!
802 error("expression out of range");
813 // Resolve all fixups
815 int ResolveAllFixups(void)
817 // Make undefined symbols GLOBL
819 ForceUndefinedSymbolsGlobal();
821 DEBUG printf("Resolving TEXT sections...\n");
823 DEBUG printf("Resolving DATA sections...\n");
825 DEBUG printf("Resolving 6502 sections...\n");
826 ResolveFixups(M6502); /* fixup 6502 section (if any) */