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 // 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, TOKEN * 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 == SYMBOL) && (fexpr[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[len]!=ENDEXPR; len++)
311 // Add one to len for 2X tokens
312 if (fexpr[len] == CONST || fexpr[len] == SYMBOL)
318 i += sizeof(uint16_t) + (len * sizeof(TOKEN));
321 // Alloc another fixup chunk for this one to fit in if necessary
322 if ((fchalloc - fchsize) < i)
324 SECT * p = §[cursect];
325 CHUNK * cp = (CHUNK *)malloc(sizeof(CHUNK) + CH_FIXUP_SIZE);
327 // First fixup chunk in section
333 // Add to other chunks
338 sfix->ch_size = fchsize;
341 // Setup fixup chunk and its global vars
343 fchalloc = cp->challoc = CH_FIXUP_SIZE;
344 fchsize = cp->ch_size = 0;
345 fchptr.cp = cp->chptr = ((uint8_t *)cp) + sizeof(CHUNK);
349 // Record fixup type, fixup location, and the file number and line number
350 // the fixup is located at.
353 *fchptr.wp++ = cfileno;
354 *fchptr.wp++ = curlineno;
356 // Store postfix expression or pointer to a single symbol, or nothing for a
357 // mark (a zero word is stored in this case--[? is it?]).
363 *fchptr.lp++ = *fexpr++;
367 *fchptr.sy++ = symbolPtr[fexpr[1]];
369 // SCPCD: Correct bit mask for attr (else other FU_xxx will match)
371 if ((attr & FUMASKRISC) == FU_JR)
374 *fchptr.lp++ = orgaddr;
376 *fchptr.lp++ = 0x00000000;
386 // Resolve fixups in a section
388 int ResolveFixups(int sno)
390 PTR fup; // Current fixup
391 VALUE eval; // Expression value
392 SYM * sy; // (Temp) pointer to a symbol
393 uint16_t i; // (Temp) word
397 SECT * sc = §[sno];
398 CHUNK * ch = sc->sffix;
403 // "Cache" first chunk
404 CHUNK * cch = sc->sfcode;
406 // Can't fixup a section with nothing in it
410 // Wire the 6502 segment's size to its allocated size (64K)
412 cch->ch_size = cch->challoc;
416 fup.cp = ch->chptr; // fup -> start of chunk
417 uint16_t * fuend = (uint16_t *)(fup.cp + ch->ch_size); // fuend -> end of chunk
419 while (fup.wp < fuend)
421 uint16_t w = *fup.wp++; // Fixup word (type+modes+flags)
422 uint32_t loc = *fup.lp++; // Location to fixup
424 curlineno = *fup.wp++;
425 DEBUG { printf("ResolveFixups: cfileno=%u\n", cfileno); }
427 // This is based on global vars cfileno, curfname :-P
428 // This approach is kinda meh as well. I think we can do better
430 SetFilenameForErrorReporting();
432 SYM * esym = NULL; // External symbol involved in expr
434 // Search for chunk containing location to fix up; compute a
435 // pointer to the location (in the chunk). Often we will find the
436 // Fixup is in the "cached" chunk, so the linear-search is seldom
438 if (loc < cch->chloc || loc >= (cch->chloc + cch->ch_size))
440 for(cch=sc->sfcode; cch!=NULL; cch=cch->chnext)
442 if (loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
448 // Fixup (loc) out of range
454 // Location to fix (in cached chunk)
455 uint8_t * locp = cch->chptr + (loc - cch->chloc);
456 uint16_t eattr = 0; // Expression attrib
458 // Compute expression/symbol value and attribs
460 // Complex expression
465 if (evexpr(fup.tk, &eval, &eattr, &esym) != OK)
484 // If the symbol is not defined, but global, set esym to sy
485 if ((eattr & (GLOBAL | DEFINED)) == GLOBAL)
489 uint16_t tdb = eattr & TDB;
491 // If the expression is undefined and no external symbol is
492 // involved, then that's an error.
493 if (!(eattr & DEFINED) && (esym == NULL))
501 // If a PC-relative fixup is undefined, its value is *not*
502 // subtracted from the location (that will happen in the linker
503 // when the external reference is resolved).
505 // MWC expects PC-relative things to have the LOC subtracted from
506 // the value, if the value is external (that is, undefined at this
509 // PC-relative fixups must be DEFINED and either in the same
510 // section (whereupon the subtraction takes place) or ABS (with no
520 error("PC-relative expr across sections");
524 if (sbra_flag && (w & FU_LBRA) && (eval + 0x80 < 0x100))
525 warn("unoptimized short branch");
527 else if (obj_format == MWC)
537 // FU_BBRA fixes up a one-byte branch offset.
539 if (!(eattr & DEFINED))
541 error("external short branch");
547 if (eval + 0x80 >= 0x100)
552 error("illegal bra.s with zero offset");
556 *++locp = (uint8_t)eval;
558 // Fixup one-byte value at locp + 1.
562 // Fixup one-byte forward references
564 if (!(eattr & DEFINED))
566 error("external byte reference");
572 error("non-absolute byte reference");
576 if ((w & FU_PCREL) && eval + 0x80 >= 0x100)
581 if (eval + 0x100 >= 0x200)
584 else if (eval >= 0x100)
587 *locp = (uint8_t)eval;
589 // Fixup high/low byte off word for 6502
591 if (!(eattr & DEFINED))
593 error("external byte reference");
597 *locp = (uint8_t)((eval >> 8) & 0xFF);
600 if (!(eattr & DEFINED))
602 error("external byte reference");
606 *locp = (uint8_t)(eval & 0xFF);
608 // Fixup WORD forward references;
609 // the word could be unaligned in the section buffer, so we have to
612 if ((w & FUMASKRISC) == FU_JR)
614 uint32_t orgaddr = *fup.lp++;
617 reg2 = (signed)((eval - (orgaddr + 2)) / 2);
619 reg2 = (signed)((eval - (loc + 2)) / 2);
621 if ((reg2 < -16) || (reg2 > 15))
623 error("relative jump out of range");
627 *locp = (uint8_t)(*locp | ((reg2 >> 3) & 0x03));
629 *locp = (uint8_t)(*locp | ((reg2 & 0x07) << 5));
633 if ((w & FUMASKRISC) == FU_NUM15)
635 if (eval < -16 || eval > 15)
637 error("constant out of range");
641 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
643 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
647 if ((w & FUMASKRISC) == FU_NUM31)
649 if (eval < 0 || eval > 31)
651 error("constant out of range");
655 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
657 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
661 if ((w & FUMASKRISC) == FU_NUM32)
663 if (eval < 1 || eval > 32)
665 error("constant out of range");
672 eval = (eval == 32) ? 0 : eval;
673 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
675 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
679 if ((w & FUMASKRISC) == FU_REGONE)
681 if (eval < 0 || eval > 31)
683 error("register value out of range");
687 *locp = (uint8_t)(*locp | ((eval >> 3) & 0x03));
689 *locp = (uint8_t)(*locp | ((eval & 0x07) << 5));
693 if ((w & FUMASKRISC) == FU_REGTWO)
695 if (eval < 0 || eval > 31)
697 error("register value out of range");
702 *locp = (uint8_t)(*locp | (eval & 0x1F));
706 if (!(eattr & DEFINED))
713 MarkRelocatable(sno, loc, 0, flags, esym);
718 MarkRelocatable(sno, loc, tdb, MWORD, NULL);
722 if (eval + 0x10000 >= 0x20000)
727 // Range-check BRA and DBRA
730 if (eval + 0x8000 >= 0x10000)
733 else if (eval >= 0x10000)
738 // 6502 words are little endian, so handle that here
740 SETLE16(locp, 0, eval)
742 SETBE16(locp, 0, eval)
745 // Fixup LONG forward references;
746 // the long could be unaligned in the section buffer, so be careful
751 if ((w & FUMASKRISC) == FU_MOVEI)
753 // Long constant in MOVEI # is word-swapped, so fix it here
754 eval = WORDSWAP32(eval);
758 // If the symbol is undefined, make sure to pass the symbol in
759 // to the MarkRelocatable() function.
760 if (!(eattr & DEFINED))
761 MarkRelocatable(sno, loc, 0, flags, esym);
763 MarkRelocatable(sno, loc, tdb, flags, NULL);
765 SETBE32(locp, 0, eval);
768 // Fixup a 3-bit "QUICK" reference in bits 9..1
769 // (range of 1..8) in a word. Really bits 1..3 in a byte.
771 if (!(eattr & DEFINED))
773 error("External quick reference");
777 if (eval < 1 || eval > 8)
780 *locp |= (eval & 7) << 1;
783 // Fix up 6502 funny branch
787 if (eval + 0x80 >= 0x100)
790 *locp = (uint8_t)eval;
794 // Bad fixup type--this should *never* happen!
800 error("expression out of range");
811 // Resolve all fixups
813 int ResolveAllFixups(void)
815 // Make undefined symbols GLOBL
817 ForceUndefinedSymbolsGlobal();
819 DEBUG printf("Resolving TEXT sections...\n");
821 DEBUG printf("Resolving DATA sections...\n");
823 DEBUG printf("Resolving 6502 sections...\n");
824 ResolveFixups(M6502); /* fixup 6502 section (if any) */