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, 2011 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
21 // Function prototypes
22 void MakeSection(int, WORD);
23 void SwitchSection(int);
25 // Section descriptors
26 SECT sect[NSECTS]; // All sections...
27 int cursect; // Current section number
29 // These are copied from the section descriptor, the current code chunk
30 // descriptor and the current fixup chunk descriptor when a switch is made into
31 // a section. They are copied back to the descriptors when the section is left.
32 WORD scattr; // Section attributes
33 LONG sloc; // Current loc in section
35 CHUNK * scode; // Current (last) code chunk
36 LONG challoc; // # bytes alloc'd to code chunk
37 LONG ch_size; // # bytes used in code chunk
38 char * chptr; // Deposit point in code chunk buffer
40 CHUNK * sfix; // Current (last) fixup chunk
41 LONG fchalloc; // # bytes alloc'd to fixup chunk
42 LONG fchsize; // # bytes used in fixup chunk
43 PTR fchptr; // Deposit point in fixup chunk buffer
45 // Return a size (SIZB, SIZW, SIZL) or 0, depending on what kind of fixup is
46 // associated with a location.
47 static char fusiztab[] = {
58 // Offset to REAL fixup location
59 static char fusizoffs[] = {
72 // Initialize sections; setup initial ABS, TEXT, DATA and BSS sections
74 void InitSection(void)
78 // Cleanup all sections
79 for(i=0; i<NSECTS; i++)
82 // Construct default sections, make TEXT the current section
83 MakeSection(ABS, SUSED | SABS | SBSS); // ABS
84 MakeSection(TEXT, SUSED | TEXT ); // TEXT
85 MakeSection(DATA, SUSED | DATA ); // DATA
86 MakeSection(BSS, SUSED | BSS | SBSS); // BSS
87 // MakeSection(M6502, SUSED | TEXT ); // 6502 code section
89 // Switch to TEXT for starters
95 // Make a new (clean) section
97 void MakeSection(int sno, WORD attr)
99 SECT * p = §[sno];
102 p->scode = p->sfcode = NULL;
103 p->sfix = p->sffix = NULL;
108 // Switch to another section (copy section & chunk descriptors to global vars
111 void SwitchSection(int sno)
115 SECT * p = §[sno];
123 // Copy code chunk vars
124 if ((cp = scode) != NULL)
126 challoc = cp->challoc;
127 ch_size = cp->ch_size;
128 chptr = cp->chptr + ch_size;
131 challoc = ch_size = 0;
133 // Copy fixup chunk vars
134 if ((cp = sfix) != NULL)
136 fchalloc = cp->challoc;
137 fchsize = cp->ch_size;
138 fchptr.cp = cp->chptr + fchsize;
141 fchalloc = fchsize = 0;
146 // Save current section
148 void SaveSection(void)
150 SECT * p = §[cursect];
152 p->scattr = scattr; // Bailout section vars
155 if (scode != NULL) // Bailout code chunk
156 scode->ch_size = ch_size;
158 if (sfix != NULL) // Bailout fixup chunk
159 sfix->ch_size = fchsize;
164 // Test to see if a location has a fixup sic'd on it. This is used by the
165 // listing generator to print 'xx's instead of '00's for forward references
167 int fixtest(int sno, LONG loc)
175 // Force update to sect[] variables
178 // Hairy, ugly linear search for a mark on our location; the speed doesn't
179 // matter, since this is only done when generating a listing, which is
181 for(ch=sect[sno].sffix; ch!=NULL; ch=ch->chnext)
183 fup.cp = (char *)ch->chptr;
184 fuend = fup.cp + ch->ch_size;
186 while (fup.cp < fuend)
189 xloc = *fup.lp++ + (int)fusizoffs[w & FUMASK];
193 return (int)fusiztab[w & FUMASK];
210 // Check that there are at least 'amt' bytes left in the current chunk. If
211 // there are not, allocate another chunk of at least 'amt' bytes (and probably
214 // If 'amt' is zero, ensure there are at least CH_THRESHOLD bytes, likewise.
216 int chcheck(LONG amt)
218 DEBUG { printf("chcheck(%u)\n", amt); }
219 // If in BSS section, no allocation required
226 DEBUG { printf(" challoc=%i, ch_size=%i, diff=%i\n", challoc, ch_size, challoc-ch_size); }
227 if ((int)(challoc - ch_size) >= (int)amt)
230 if (amt < CH_CODE_SIZE)
233 DEBUG { printf(" amt (adjusted)=%u\n", amt); }
234 SECT * p = §[cursect];
235 CHUNK * cp = malloc(sizeof(CHUNK) + amt);
237 // First chunk in section
243 // Add chunk to other chunks
248 scode->ch_size = ch_size; // Save old chunk's globals
251 // Setup chunk and global vars
254 challoc = cp->challoc = amt;
255 ch_size = cp->ch_size = 0;
256 chptr = cp->chptr = ((char *)cp) + sizeof(CHUNK);
257 scode = p->scode = cp;
263 // This is really wrong. We need to make some proper structures here so we
264 // don't have to count sizes of objects, that's what the compiler's for! :-P
265 #define FIXUP_BASE_SIZE (sizeof(WORD) + sizeof(LONG) + sizeof(WORD) + sizeof(WORD))
267 // Arrange for a fixup on a location
269 int AddFixup(WORD attr, LONG loc, TOKEN * fexpr)
275 // Shamus: Expression lengths are voodoo ATM (variable "i"). Need to fix
277 #warning "!!! AddFixup() is filled with VOODOO !!!"
278 DEBUG printf("FIXUP@$%X: $%X\n", loc, attr);
280 // Compute length of expression (could be faster); determine if it's the
281 // single-symbol case; no expression if it's just a mark. This code assumes
282 // 16 bit WORDs and 32 bit LONGs
283 if (*fexpr == SYMBOL && fexpr[2] == ENDEXPR)
285 // Just a single symbol
286 // SCPCD : correct bit mask for attr (else other FU_xxx will match)
288 if ((attr & FUMASKRISC) == FU_JR)
290 //printf("AddFixup: ((attr & FUMASKRISC) == FU_JR)\n");
292 // i = FIXUP_BASE_SIZE + (sizeof(LONG) * 2);
293 i = FIXUP_BASE_SIZE + sizeof(SYM *) + sizeof(LONG);
297 //printf("AddFixup: ((attr & FUMASKRISC) == FU_JR) ELSE\n");
299 i = FIXUP_BASE_SIZE + sizeof(SYM *);
304 //printf("AddFixup: !SYMBOL\n");
307 for(len=0; fexpr[len]!=ENDEXPR; len++)
309 if (fexpr[len] == CONST || fexpr[len] == SYMBOL)
313 len++; // Add 1 for ENDEXPR
314 // i = (len << 2) + 12;
315 i = FIXUP_BASE_SIZE + sizeof(WORD) + (len * sizeof(TOKEN));
318 // Alloc another fixup chunk for this one to fit in if necessary
319 if ((fchalloc - fchsize) < i)
322 cp = (CHUNK *)malloc(sizeof(CHUNK) + CH_FIXUP_SIZE);
324 // First fixup chunk in section
330 // Add to other chunks
335 sfix->ch_size = fchsize;
338 // Setup fixup chunk and its global vars
340 fchalloc = cp->challoc = CH_FIXUP_SIZE;
341 fchsize = cp->ch_size = 0;
342 fchptr.cp = cp->chptr = ((char *)cp) + sizeof(CHUNK);
346 // Record fixup type, fixup location, and the file number and line number
347 // the fixup is located at.
350 *fchptr.wp++ = cfileno;
351 *fchptr.wp++ = (WORD)curlineno;
353 // Store postfix expression or pointer to a single symbol, or nothing for a
357 *fchptr.wp++ = (WORD)len;
360 *fchptr.lp++ = (LONG)*fexpr++;
364 // *fchptr.lp++ = (LONG)fexpr[1];
365 *fchptr.sy++ = symbolPtr[fexpr[1]];
366 //printf("AddFixup: adding symbol (%s) [%08X]\n", symbolPtr[fexpr[1]]->sname, symbolPtr[fexpr[1]]->sattr);
369 // SCPCD : correct bit mask for attr (else other FU_xxx will match) NYAN !
370 if ((attr & FUMASKRISC) == FU_JR)
373 *fchptr.lp++ = orgaddr;
375 *fchptr.lp++ = 0x00000000;
384 // Resolve all fixups
386 int ResolveAllFixups(void)
391 // Make undefined symbols GLOBL
393 ForceUndefinedSymbolsGlobal();
395 DEBUG printf("Resolving TEXT sections...\n");
397 DEBUG printf("Resolving DATA sections...\n");
405 // Resolve fixups in a section
407 int ResolveFixups(int sno)
409 PTR fup; // Current fixup
410 WORD * fuend; // End of last fixup (in this chunk)
411 WORD w; // Fixup word (type+modes+flags)
412 char * locp; // Location to fix (in cached chunk)
413 LONG loc; // Location to fixup
414 VALUE eval; // Expression value
415 WORD eattr; // Expression attrib
416 SYM * esym; // External symbol involved in expr
417 SYM * sy; // (Temp) pointer to a symbol
418 WORD i; // (Temp) word
419 WORD tdb; // eattr & TDB
423 unsigned page_jump = 0;
424 unsigned address = 0;
428 SECT * sc = §[sno];
429 CHUNK * ch = sc->sffix;
434 // "Cache" first chunk
435 CHUNK * cch = sc->sfcode;
437 // Can't fixup a section with nothing in it
443 fup.cp = ch->chptr; // fup -> start of chunk
444 fuend = (WORD *)(fup.cp + ch->ch_size); // fuend -> end of chunk
446 while (fup.wp < fuend)
451 curlineno = (int)*fup.wp++;
452 DEBUG { printf("ResolveFixups: cfileno=%u\n", cfileno); }
453 // This is based on global vars cfileno, curfname :-P
454 // This approach is kinda meh as well. I think we can do better than this.
455 SetFilenameForErrorReporting();
459 // Search for chunk containing location to fix up; compute a
460 // pointer to the location (in the chunk). Often we will find the
461 // fixup is in the "cached" chunk, so the linear-search is seldom
463 if (loc < cch->chloc || loc >= (cch->chloc + cch->ch_size))
465 for(cch=sc->sfcode; cch!=NULL; cch=cch->chnext)
467 if (loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
473 // Fixup (loc) out of range
479 locp = cch->chptr + (loc - cch->chloc);
482 // Compute expression/symbol value and attribs
483 // Complex expression
488 if (evexpr(fup.tk, &eval, &eattr, &esym) != OK)
507 if ((eattr & (GLOBAL | DEFINED)) == GLOBAL)
511 tdb = (WORD)(eattr & TDB);
513 // If the expression is undefined and no external symbol is
514 // involved, then it's an error.
515 if (!(eattr & DEFINED) && (esym == NULL))
521 // It seems that this is completely unnecessary!
523 if (((w & FUMASKRISC) == FU_MOVEI) && esym)
525 //printf("DoFixups: Setting symbol attre to RISCSYM...\n");
526 esym->sattre |= RISCSYM;
532 // If a PC-relative fixup is undefined, its value is *not*
533 // subtracted from the location (that will happen in the linker
534 // when the external reference is resolved).
536 // MWC expects PC-relative things to have the LOC subtracted from
537 // the value, if the value is external (that is, undefined at this
540 // PC-relative fixups must be DEFINED and either in the same
541 // section (whereupon the subtraction takes place) or ABS (with no
551 error("PC-relative expr across sections");
555 if (sbra_flag && (w & FU_LBRA) && (eval + 0x80 < 0x100))
556 warn("unoptimized short branch");
558 else if (obj_format == MWC)
566 switch ((int)(w & FUMASK))
568 // FU_BBRA fixes up a one-byte branch offset.
570 if (!(eattr & DEFINED))
572 error("external short branch");
578 if (eval + 0x80 >= 0x100)
583 error("illegal bra.s with zero offset");
587 *++locp = (char)eval;
589 // Fixup one-byte value at locp + 1.
593 // Fixup one-byte forward references
595 if (!(eattr & DEFINED))
597 error("external byte reference");
603 error("non-absolute byte reference");
607 if ((w & FU_PCREL) && eval + 0x80 >= 0x100)
612 if (eval + 0x100 >= 0x200)
615 else if (eval >= 0x100)
620 // Fixup WORD forward references;
621 // the word could be unaligned in the section buffer, so we have to
624 if ((w & FUMASKRISC) == FU_JR)// || ((w & 0x0F00) == FU_MJR))
629 reg2 = (signed)((eval - (oaddr + 2)) / 2);// & 0x1F;
631 reg2 = (signed)((eval - (loc + 2)) / 2);// & 0x1F;
634 if ((w & 0x0F00) == FU_MJR)
636 // Main code destination alignment checking here for
637 // forward declared labels
638 address = (oaddr) ? oaddr : loc;
640 if (((address >= 0xF03000) && (address < 0xF04000)
641 && (eval < 0xF03000)) || ((eval >= 0xF03000)
642 && (eval < 0xF04000) && (address < 0xF03000)))
644 warni("* \'jr\' at $%08X - cannot jump relative between "
645 "main memory and local gpu ram", address);
649 page_jump = (address & 0xFFFFFF00) - (eval & 0xFFFFFF00);
653 // This jump is to a page outside of the
654 // current 256 byte page
657 warni("* \'jr\' at $%08X - destination address not aligned for long page jump, insert a \'nop\' before the destination address", address);
662 // This jump is in the current 256 byte page
665 warni("* \'jr\' at $%08X - destination address not aligned for short page jump, insert a \'nop\' before the destination address", address);
672 if ((reg2 < -16) || (reg2 > 15))
674 error("relative jump out of range");
678 *locp = (char)(*locp | ((reg2 >> 3) & 0x03));
680 *locp = (char)(*locp | ((reg2 & 0x07) << 5));
684 if ((w & FUMASKRISC) == FU_NUM15)
686 if (eval < -16 || eval > 15)
688 error("constant out of range");
692 *locp = (char)(*locp | ((eval >> 3) & 0x03));
694 *locp = (char)(*locp | ((eval & 0x07) << 5));
698 if ((w & FUMASKRISC) == FU_NUM31)
700 if (eval < 0 || eval > 31)
702 error("constant out of range");
706 *locp = (char)(*locp | ((eval >> 3) & 0x03));
708 *locp = (char)(*locp | ((eval & 0x07) << 5));
712 if ((w & FUMASKRISC) == FU_NUM32)
714 if (eval < 1 || eval > 32)
716 error("constant out of range");
723 eval = (eval == 32) ? 0 : eval;
724 *locp = (char)(*locp | ((eval >> 3) & 0x03));
726 *locp = (char)(*locp | ((eval & 0x07) << 5));
730 if ((w & FUMASKRISC) == FU_REGONE)
732 if (eval < 0 || eval > 31)
734 error("register value out of range");
738 *locp = (char)(*locp | ((eval >> 3) & 0x03));
740 *locp = (char)(*locp | ((eval & 0x07) << 5));
744 if ((w & FUMASKRISC) == FU_REGTWO)
746 if (eval < 0 || eval > 31)
748 error("register value out of range");
753 *locp = (char)(*locp | (eval & 0x1F));
757 if (!(eattr & DEFINED))
764 rmark(sno, loc, 0, w, esym);
769 rmark(sno, loc, tdb, MWORD, NULL);
773 if (eval + 0x10000 >= 0x20000)
778 // Range-check BRA and DBRA
781 if (eval + 0x8000 >= 0x10000)
784 else if (eval >= 0x10000)
789 *locp++ = (char)(eval >> 8);
792 // Fixup LONG forward references;
793 // the long could be unaligned in the section buffer, so be careful
796 if ((w & FUMASKRISC) == FU_MOVEI)
803 for(j=0; j<fwindex; j++)
805 if (fwdjump[j] == address)
807 page_jump = (address & 0xFFFFFF00) - (eval & 0xFFFFFF00);
814 sprintf(buf, "* \'jump\' at $%08X - destination address not aligned for long page jump, insert a \'nop\' before the destination address", address);
820 write(err_fd, buf, (LONG)strlen(buf));
827 if (!(eval & 0x0000000F) || ((eval - 2) % 4))
830 sprintf(buf, "* \'jump\' at $%08X - destination address not aligned for short page jump, insert a \'nop\' before the destination address", address);
836 write(err_fd, buf, (LONG)strlen(buf));
842 // Clear this jump as it has been checked
850 // Long constant in MOVEI # is word-swapped, so fix it here
851 eval = ((eval >> 16) & 0x0000FFFF) | ((eval << 16) & 0xFFFF0000);
852 flags = (MLONG | MMOVEI);
857 if (!(eattr & DEFINED))
859 //printf("Fixup (long): Symbol undefined. loc = $%X, long = $%X, flags = $%x\n", loc, eval, flags);
860 rmark(sno, loc, 0, flags, esym);
864 //printf("Fixup (long): TDB = $%X. loc =$%X, long = $%X, flags = $%x\n", tdb, loc, eval, flags);
865 rmark(sno, loc, tdb, flags, NULL);
868 //printf("Fixup (long): TDB = $%X. loc =$%X, long = $%X, flags = $%x\n", tdb, loc, eval, flags);
870 *locp++ = (char)(eval >> 24);
871 *locp++ = (char)(eval >> 16);
872 *locp++ = (char)(eval >> 8);
876 // Fixup a 3-bit "QUICK" reference in bits 9..1
877 // (range of 1..8) in a word. Really bits 1..3 in a byte.
879 if (!(eattr & DEFINED))
881 error("External quick reference");
885 if (eval < 1 || eval > 8)
888 *locp |= (eval & 7) << 1;
891 // Fix up 6502 funny branch
895 if (eval + 0x80 >= 0x100)
902 // Bad fixup type--this should *never* happen!
908 error("expression out of range");