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
19 // Section descriptors
20 SECT sect[NSECTS]; // All sections...
21 int cursect; // Current section number
23 // These are copied from the section descriptor, the current code chunk descriptor and the current
24 // fixup chunk descriptor when a switch is made into a section. They are copied back to the
25 // descriptors when the section is left.
26 WORD scattr; // Section attributes
27 LONG sloc; // Current loc in section
29 CHUNK * scode; // Current (last) code chunk
30 LONG challoc; // #bytes alloc'd to code chunk
31 LONG ch_size; // #bytes used in code chunk
32 char * chptr; // Deposit point in code chunk buffer
34 CHUNK * sfix; // Current (last) fixup chunk
35 LONG fchalloc; // #bytes alloc'd to fixup chunk
36 LONG fchsize; // #bytes used in fixup chunk
37 PTR fchptr; // Deposit point in fixup chunk buffer
39 unsigned fwdjump[MAXFWDJUMPS]; // forward jump check table
40 unsigned fwindex = 0; // forward jump index
42 // Return a size (SIZB, SIZW, SIZL) or 0, depending on what kind of fixup is associated
44 static char fusiztab[] = {
55 // Offset to REAL fixup location
56 static char fusizoffs[] = {
69 // Make a New (Clean) Section
71 void mksect(int sno, WORD attr)
73 SECT * p; // Section pointer
78 p->scode = p->sfcode = NULL;
79 p->sfix = p->sffix = NULL;
84 // Switch to Another Section (Copy Section & Chunk Descriptors to Global Vars
87 void switchsect(int sno)
89 SECT * p; // Section pointer
90 CHUNK * cp; // Chunk pointer
95 scattr = p->scattr; // Copy section vars
100 if ((cp = scode) != NULL)
101 { // Copy code chunk vars
102 challoc = cp->challoc;
103 ch_size = cp->ch_size;
104 chptr = cp->chptr + ch_size;
107 challoc = ch_size = 0;
109 if ((cp = sfix) != NULL)
110 { // Copy fixup chunk vars
111 fchalloc = cp->challoc;
112 fchsize = cp->ch_size;
113 fchptr.cp = cp->chptr + fchsize;
116 fchalloc = fchsize = 0;
121 // Save Current Section
125 SECT * p = §[cursect];
127 p->scattr = scattr; // Bailout section vars
130 if (scode != NULL) // Bailout code chunk
131 scode->ch_size = ch_size;
133 if (sfix != NULL) // Bailout fixup chunk
134 sfix->ch_size = fchsize;
139 // Initialize Sections; Setup initial ABS, TEXT, DATA and BSS sections
145 // Cleanup all sections
146 for(i=0; i<NSECTS; ++i)
149 // Construct default sections, make TEXT the current section
150 mksect(ABS, SUSED|SABS|SBSS); // ABS
151 mksect(TEXT, SUSED|TEXT ); // TEXT
152 mksect(DATA, SUSED|DATA ); // DATA
153 mksect(BSS, SUSED|BSS |SBSS); // BSS
154 // mksect(M6502, SUSED|TEXT ); // 6502 code section
156 switchsect(TEXT); // Switch to TEXT for starters
161 // Test to see if a location has a fixup sic'd on it. This is used by the
162 // listing generator to print 'xx's instead of '00's for forward references
164 int fixtest(int sno, LONG loc)
172 stopmark(); // Force update to sect[] variables
174 // Hairy, ugly linear search for a mark on our location;
175 // the speed doesn't matter, since this is only done when generating a listing, which is SLOW.
176 for(ch=sect[sno].sffix; ch!=NULL; ch=ch->chnext)
178 fup.cp = (char *)ch->chptr;
179 fuend = fup.cp + ch->ch_size;
181 while (fup.cp < fuend)
184 xloc = *fup.lp++ + (int)fusizoffs[w & FUMASK];
188 return (int)fusiztab[w & FUMASK];
205 // Check that there are at least `amt' bytes left in the current chunk. If
206 // there are not, allocate another chunk of at least `amt' bytes (and probably
209 // If `amt' is zero, ensure there are at least CH_THRESHOLD bytes, likewise.
211 int chcheck(LONG amt)
217 return 0; // If in BSS section, forget it
222 if ((int)(challoc - ch_size) >= (int)amt)
225 if (amt < CH_CODE_SIZE)
229 cp = (CHUNK *)amem((long)(sizeof(CHUNK) + amt));
232 { // First chunk in section
237 { // Add chunk to other chunks
240 scode->ch_size = ch_size; // Save old chunk's globals
243 // Setup chunk and global vars
246 challoc = cp->challoc = amt;
247 ch_size = cp->ch_size = 0;
248 chptr = cp->chptr = ((char *)cp) + sizeof(CHUNK);
249 scode = p->scode = cp;
256 // Arrange for a fixup on a location
258 int fixup(WORD attr, LONG loc, TOKEN * fexpr)
265 // Compute length of expression (could be faster); determine if it's the single-symbol case;
266 // no expression if it's just a mark. This code assumes 16 bit WORDs and 32 bit LONGs
267 if (*fexpr == SYMBOL && fexpr[2] == ENDEXPR)
269 //if ((attr & 0x0F00) == FU_JR) {
270 if ((attr & 0x0200) == FU_JR)
272 i = 18; // Just a single symbol
283 for(len=0; fexpr[len]!=ENDEXPR; ++len)
285 if (fexpr[len] == CONST || fexpr[len] == SYMBOL)
289 ++len; // Add 1 for ENDEXPR
293 // Maybe alloc another fixup chunk for this one to fit in
294 if ((fchalloc - fchsize) < i)
297 cp = (CHUNK *)amem((long)(sizeof(CHUNK) + CH_FIXUP_SIZE));
300 { // First fixup chunk in section
305 { // Add to other chunks
308 sfix->ch_size = fchsize;
311 // Setup fixup chunk and its global vars
313 fchalloc = cp->challoc = CH_FIXUP_SIZE;
314 fchsize = cp->ch_size = 0;
315 fchptr.cp = cp->chptr = ((char *)cp) + sizeof(CHUNK);
319 // Record fixup type, fixup location, and the file number and line number the fixup is
323 *fchptr.wp++ = cfileno;
324 *fchptr.wp++ = (WORD)curlineno;
326 // Store postfix expression or pointer to a single symbol, or nothing for a mark.
329 *fchptr.wp++ = (WORD)len;
332 *fchptr.lp++ = (LONG)*fexpr++;
336 *fchptr.lp++ = (LONG)fexpr[1];
339 //if ((attr & 0x0F00) == FU_JR) {
340 if ((attr & 0x0200) == FU_JR)
343 *fchptr.lp++ = orgaddr;
345 *fchptr.lp++ = 0x00000000;
355 // Resolve all Fixups
362 // Make undefined symbols GLOBL
369 // We need to do a final check of forward 'jump' destination addresses that are external
370 for(i=0; i<MAXFWDJUMPS; i++)
375 sprintf(buf, "* \'jump\' at $%08X - destination address is external to this source file and cannot have its aligment validated", fwdjump[i]);
381 write(err_fd, buf, (LONG)strlen(buf));
392 // Resolve Fixups in a Section
396 SECT * sc; // Section
398 PTR fup; // Current fixup
399 WORD * fuend; // End of last fixup (in this chunk)
400 CHUNK * cch; // Cached chunk for target
401 WORD w; // Fixup word (type+modes+flags)
402 char * locp; // Location to fix (in cached chunk)
403 LONG loc; // Location to fixup
404 VALUE eval; // Expression value
405 WORD eattr; // Expression attrib
406 SYM * esym; // External symbol involved in expr
407 SYM * sy; // (Temp) pointer to a symbol
408 WORD i; // (Temp) word
409 WORD tdb; // eattr & TDB
413 unsigned page_jump = 0;
414 unsigned address = 0;
424 cch = sc->sfcode; // "cache" first chunk
426 if (cch == NULL) // Can't fixup a sect with nothing in it
431 fup.cp = ch->chptr; // fup -> start of chunk
432 fuend = (WORD *)(fup.cp + ch->ch_size); // fuend -> end of chunk
434 while (fup.wp < fuend)
439 curlineno = (int)*fup.wp++;
443 // Search for chunk containing location to fix up; compute a pointer to the location
444 // (in the chunk). Often we will find the fixup is in the "cached" chunk, so the
445 // linear-search is seldom executed.
446 if (loc < cch->chloc || loc >= (cch->chloc + cch->ch_size))
448 for(cch=sc->sfcode; cch!=NULL; cch=cch->chnext)
450 if (loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
456 interror(7); // Fixup (loc) out of range
461 locp = cch->chptr + (loc - cch->chloc);
465 // Compute expression/symbol value and attribs
467 { // Complex expression
470 if (evexpr(fup.tk, &eval, &eattr, &esym) != OK)
488 if ((eattr & (GLOBAL|DEFINED)) == GLOBAL)
492 tdb = (WORD)(eattr & TDB);
494 // If the expression is undefined and no external symbol is
495 // involved, then it's an error.
496 if (!(eattr & DEFINED) && esym == NULL)
502 if (((w & 0x0F00) == FU_MOVEI) && esym)
503 esym->sattre |= RISCSYM;
507 // If a PC-relative fixup is undefined, its value is *not* subtracted from the location
508 // (that will happen in the linker when the external reference is resolved).
510 // MWC expects PC-relative things to have the LOC subtracted from the value, if the
511 // value is external (that is, undefined at this point).
513 // PC-relative fixups must be DEFINED and either in the same section (whereupon the
514 // subtraction takes place) or ABS (with no subtract).
523 error("PC-relative expr across sections");
527 if (sbra_flag && (w & FU_LBRA) && (eval + 0x80 < 0x100))
528 warn("unoptimized short branch");
530 else if (obj_format == MWC)
538 switch ((int)(w & FUMASK))
540 // FU_BBRA fixes up a one-byte branch offset.
542 if (!(eattr & DEFINED))
544 error("external short branch");
550 if (eval + 0x80 >= 0x100)
555 error("illegal bra.s with zero offset");
559 *++locp = (char)eval;
561 // Fixup one-byte value at locp + 1.
565 // Fixup one-byte forward references
567 if (!(eattr & DEFINED))
569 error("external byte reference");
575 error("non-absolute byte reference");
579 if ((w & FU_PCREL) && eval + 0x80 >= 0x100)
584 if (eval + 0x100 >= 0x200)
587 else if (eval >= 0x100)
592 // Fixup WORD forward references;
593 // the word could be unaligned in the section buffer, so we have to be careful.
595 if (((w & 0x0F00) == FU_JR) || ((w & 0x0F00) == FU_MJR))
601 reg2 = (signed)((eval - (oaddr + 2)) / 2);// & 0x1F;
605 reg2 = (signed)((eval - (loc + 2)) / 2);// & 0x1F;
608 if ((w & 0x0F00) == FU_MJR)
610 // Main code destination alignment checking here for forward declared labels
611 address = (oaddr) ? oaddr : loc;
613 if (((address >= 0xF03000) && (address < 0xF04000)
614 && (eval < 0xF03000)) || ((eval >= 0xF03000)
615 && (eval < 0xF04000) && (address < 0xF03000)))
617 warni("* \'jr\' at $%08X - cannot jump relative between "
618 "main memory and local gpu ram", address);
622 page_jump = (address & 0xFFFFFF00) - (eval & 0xFFFFFF00);
626 // This jump is to a page outside of the current 256 byte page
629 warni("* \'jr\' at $%08X - destination address not aligned for long page jump, insert a \'nop\' before the destination address", address);
634 // This jump is in the current 256 byte page
637 warni("* \'jr\' at $%08X - destination address not aligned for short page jump, insert a \'nop\' before the destination address", address);
643 if ((reg2 < -16) || (reg2 > 15))
645 error("relative jump out of range");
649 *locp = (char)(*locp | ((reg2 >> 3) & 0x03));
651 *locp = (char)(*locp | ((reg2 & 0x07) << 5));
655 if ((w & 0x0F00) == FU_NUM15)
657 if (eval < -16 || eval > 15)
659 error("constant out of range");
663 *locp = (char)(*locp | ((eval >> 3) & 0x03));
665 *locp = (char)(*locp | ((eval & 0x07) << 5));
669 if ((w & 0x0F00) == FU_NUM31)
671 if (eval < 0 || eval > 31)
673 error("constant out of range");
677 *locp = (char)(*locp | ((eval >> 3) & 0x03));
679 *locp = (char)(*locp | ((eval & 0x07) << 5));
683 if ((w & 0x0F00) == FU_NUM32)
685 if (eval < 1 || eval > 32)
687 error("constant out of range");
694 eval = (eval == 32) ? 0 : eval;
695 *locp = (char)(*locp | ((eval >> 3) & 0x03));
697 *locp = (char)(*locp | ((eval & 0x07) << 5));
701 if ((w & 0x0F00) == FU_REGONE)
703 if (eval < 0 || eval > 31)
705 error("register value out of range");
709 *locp = (char)(*locp | ((eval >> 3) & 0x03));
711 *locp = (char)(*locp | ((eval & 0x07) << 5));
715 if ((w & 0x0F00) == FU_REGTWO)
717 if (eval < 0 || eval > 31)
719 error("register value out of range");
724 *locp = (char)(*locp | (eval & 0x1F));
728 if (!(eattr & DEFINED))
735 rmark(sno, loc, 0, w, esym);
740 rmark(sno, loc, tdb, MWORD, NULL);
744 if (eval + 0x10000 >= 0x20000)
749 // Range-check BRA and DBRA
752 if (eval + 0x8000 >= 0x10000)
755 else if (eval >= 0x10000)
760 *locp++ = (char)(eval >> 8);
763 // Fixup LONG forward references;
764 // the long could be unaligned in the section buffer, so be careful (again).
766 if ((w & 0x0F00) == FU_MOVEI)
772 for(j=0; j<fwindex; j++)
774 if (fwdjump[j] == address)
776 page_jump = (address & 0xFFFFFF00) - (eval & 0xFFFFFF00);
783 sprintf(buf, "* \'jump\' at $%08X - destination address not aligned for long page jump, insert a \'nop\' before the destination address", address);
789 write(err_fd, buf, (LONG)strlen(buf));
796 if (!(eval & 0x0000000F) || ((eval - 2) % 4))
799 sprintf(buf, "* \'jump\' at $%08X - destination address not aligned for short page jump, insert a \'nop\' before the destination address", address);
805 write(err_fd, buf, (LONG)strlen(buf));
811 // Clear this jump as it has been checked
818 eval = ((eval >> 16) & 0x0000FFFF) | ((eval << 16) & 0xFFFF0000);
819 flags = (MLONG|MMOVEI);
824 if (!(eattr & DEFINED))
826 rmark(sno, loc, 0, flags, esym);
830 rmark(sno, loc, tdb, flags, NULL);
833 *locp++ = (char)(eval >> 24);
834 *locp++ = (char)(eval >> 16);
835 *locp++ = (char)(eval >> 8);
838 // Fixup a 3-bit "QUICK" reference in bits 9..1
839 // (range of 1..8) in a word. Really bits 1..3 in a byte.
841 if (!(eattr & DEFINED))
843 error("External quick reference");
847 if (eval < 1 || eval > 8)
850 *locp |= (eval & 7) << 1;
852 // Fix up 6502 funny branch
856 if (eval + 0x80 >= 0x100)
862 interror(4); // Bad fixup type
867 error("expression out of range");