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 // Section descriptors
22 SECT sect[NSECTS]; // All sections...
23 int cursect; // Current section number
25 // These are copied from the section descriptor, the current code chunk
26 // descriptor and the current fixup chunk descriptor when a switch is made into
27 // a section. They are copied back to the descriptors when the section is left.
28 WORD scattr; // Section attributes
29 LONG sloc; // Current loc in section
31 CHUNK * scode; // Current (last) code chunk
32 LONG challoc; // # bytes alloc'd to code chunk
33 LONG ch_size; // # bytes used in code chunk
34 char * chptr; // Deposit point in code chunk buffer
36 CHUNK * sfix; // Current (last) fixup chunk
37 LONG fchalloc; // # bytes alloc'd to fixup chunk
38 LONG fchsize; // # bytes used in fixup chunk
39 PTR fchptr; // Deposit point in fixup chunk buffer
41 unsigned fwdjump[MAXFWDJUMPS]; // forward jump check table
42 unsigned fwindex = 0; // forward jump index
44 // Return a size (SIZB, SIZW, SIZL) or 0, depending on what kind of fixup is
45 // associated with a location.
46 static char fusiztab[] = {
57 // Offset to REAL fixup location
58 static char fusizoffs[] = {
71 // Make a New (Clean) Section
73 void mksect(int sno, WORD attr)
75 SECT * p = §[sno];
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 CHUNK * cp; // Chunk pointer
91 SECT * p = §[sno];
93 scattr = p->scattr; // Copy section vars
98 // Copy code chunk vars
99 if ((cp = scode) != NULL)
101 challoc = cp->challoc;
102 ch_size = cp->ch_size;
103 chptr = cp->chptr + ch_size;
106 challoc = ch_size = 0;
108 // Copy fixup chunk vars
109 if ((cp = sfix) != NULL)
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
176 // listing, which is SLOW.
177 for(ch=sect[sno].sffix; ch!=NULL; ch=ch->chnext)
179 fup.cp = (char *)ch->chptr;
180 fuend = fup.cp + ch->ch_size;
182 while (fup.cp < fuend)
185 xloc = *fup.lp++ + (int)fusizoffs[w & FUMASK];
189 return (int)fusiztab[w & FUMASK];
206 // Check that there are at least `amt' bytes left in the current chunk. If
207 // there are not, allocate another chunk of at least `amt' bytes (and probably
210 // If `amt' is zero, ensure there are at least CH_THRESHOLD bytes, likewise.
212 int chcheck(LONG amt)
214 DEBUG { printf("chcheck(%u)\n", amt); }
215 // If in BSS section, no allocation required
222 DEBUG { printf(" challoc=%i, ch_size=%i, diff=%i\n", challoc, ch_size, challoc-ch_size); }
223 if ((int)(challoc - ch_size) >= (int)amt)
226 if (amt < CH_CODE_SIZE)
229 DEBUG { printf(" amt (adjusted)=%u\n", amt); }
230 SECT * p = §[cursect];
231 CHUNK * cp = malloc(sizeof(CHUNK) + amt);
233 // First chunk in section
239 // Add chunk to other chunks
244 scode->ch_size = ch_size; // Save old chunk's globals
247 // Setup chunk and global vars
250 challoc = cp->challoc = amt;
251 ch_size = cp->ch_size = 0;
252 chptr = cp->chptr = ((char *)cp) + sizeof(CHUNK);
253 scode = p->scode = cp;
259 // This is really wrong. We need to make some proper structures here so we don't
260 // have to count sizes of objects, that's what the compiler's for! :-P
261 #define FIXUP_BASE_SIZE (sizeof(WORD) + sizeof(LONG) + sizeof(WORD) + sizeof(WORD))
263 // Arrange for a fixup on a location
265 int fixup(WORD attr, LONG loc, TOKEN * fexpr)
271 // Shamus: Expression lengths are voodoo ATM (varibale "i"). Need to fix this.
272 DEBUG printf("FIXUP@$%X: $%X\n", loc, attr);
274 // Compute length of expression (could be faster); determine if it's the
275 // single-symbol case; no expression if it's just a mark. This code assumes
276 // 16 bit WORDs and 32 bit LONGs
277 if (*fexpr == SYMBOL && fexpr[2] == ENDEXPR)
279 // Just a single symbol
280 // SCPCD : correct bit mask for attr (else other FU_xxx will match) NYAN !
281 if ((attr & 0x0F00) == FU_JR)
284 // i = FIXUP_BASE_SIZE + (sizeof(LONG) * 2);
285 i = FIXUP_BASE_SIZE + sizeof(SYM *) + sizeof(LONG);
290 i = FIXUP_BASE_SIZE + sizeof(SYM *);
297 for(len=0; fexpr[len]!=ENDEXPR; len++)
299 if (fexpr[len] == CONST || fexpr[len] == SYMBOL)
303 len++; // Add 1 for ENDEXPR
304 // i = (len << 2) + 12;
305 i = FIXUP_BASE_SIZE + sizeof(WORD) + (len * sizeof(TOKEN));
308 // Maybe alloc another fixup chunk for this one to fit in
309 if ((fchalloc - fchsize) < i)
312 cp = (CHUNK *)malloc(sizeof(CHUNK) + CH_FIXUP_SIZE);
314 // First fixup chunk in section
320 // Add to other chunks
325 sfix->ch_size = fchsize;
328 // Setup fixup chunk and its global vars
330 fchalloc = cp->challoc = CH_FIXUP_SIZE;
331 fchsize = cp->ch_size = 0;
332 fchptr.cp = cp->chptr = ((char *)cp) + sizeof(CHUNK);
336 // Record fixup type, fixup location, and the file number and line number
337 // the fixup is located at.
340 *fchptr.wp++ = cfileno;
341 *fchptr.wp++ = (WORD)curlineno;
343 // Store postfix expression or pointer to a single symbol, or nothing for a
347 *fchptr.wp++ = (WORD)len;
350 *fchptr.lp++ = (LONG)*fexpr++;
354 // *fchptr.lp++ = (LONG)fexpr[1];
355 *fchptr.sy++ = symbolPtr[fexpr[1]];
358 // SCPCD : correct bit mask for attr (else other FU_xxx will match) NYAN !
359 if ((attr & 0x0F00) == FU_JR)
362 *fchptr.lp++ = orgaddr;
364 *fchptr.lp++ = 0x00000000;
373 // Resolve all Fixups
375 int ResolveAllFixups(void)
380 // Make undefined symbols GLOBL
384 DEBUG printf("Resolving TEXT sections...\n");
386 DEBUG printf("Resolving DATA sections...\n");
389 // We need to do a final check of forward 'jump' destination addresses that
391 for(i=0; i<MAXFWDJUMPS; i++)
396 sprintf(buf, "* \'jump\' at $%08X - destination address is external to this source file and cannot have its aligment validated", fwdjump[i]);
402 write(err_fd, buf, (LONG)strlen(buf));
413 // Resolve Fixups in a Section
415 int ResolveFixups(int sno)
417 PTR fup; // Current fixup
418 WORD * fuend; // End of last fixup (in this chunk)
419 WORD w; // Fixup word (type+modes+flags)
420 char * locp; // Location to fix (in cached chunk)
421 LONG loc; // Location to fixup
422 VALUE eval; // Expression value
423 WORD eattr; // Expression attrib
424 SYM * esym; // External symbol involved in expr
425 SYM * sy; // (Temp) pointer to a symbol
426 WORD i; // (Temp) word
427 WORD tdb; // eattr & TDB
431 unsigned page_jump = 0;
432 unsigned address = 0;
436 SECT * sc = §[sno];
437 CHUNK * ch = sc->sffix;
442 CHUNK * cch = sc->sfcode; // "cache" first chunk
444 if (cch == NULL) // Can't fixup a sect with nothing in it
449 fup.cp = ch->chptr; // fup -> start of chunk
450 fuend = (WORD *)(fup.cp + ch->ch_size); // fuend -> end of chunk
452 while (fup.wp < fuend)
457 curlineno = (int)*fup.wp++;
458 DEBUG { printf("ResolveFixups: cfileno=%u\n", cfileno); }
459 // This is based on global vars cfileno, curfname :-P
460 // This approach is kinda meh as well. I think we can do better than this.
461 SetFilenameForErrorReporting();
465 // Search for chunk containing location to fix up; compute a
466 // pointer to the location (in the chunk). Often we will find the
467 // fixup is in the "cached" chunk, so the linear-search is seldom
469 if (loc < cch->chloc || loc >= (cch->chloc + cch->ch_size))
471 for(cch=sc->sfcode; cch!=NULL; cch=cch->chnext)
473 if (loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
479 interror(7); // Fixup (loc) out of range
484 locp = cch->chptr + (loc - cch->chloc);
487 // Compute expression/symbol value and attribs
488 // Complex expression
493 if (evexpr(fup.tk, &eval, &eattr, &esym) != OK)
512 if ((eattr & (GLOBAL | DEFINED)) == GLOBAL)
516 tdb = (WORD)(eattr & TDB);
518 // If the expression is undefined and no external symbol is
519 // involved, then it's an error.
520 if (!(eattr & DEFINED) && esym == NULL)
526 if (((w & 0x0F00) == FU_MOVEI) && esym)
527 esym->sattre |= RISCSYM;
531 // If a PC-relative fixup is undefined, its value is *not*
532 // subtracted from the location (that will happen in the linker
533 // when the external reference is resolved).
535 // MWC expects PC-relative things to have the LOC subtracted from
536 // the value, if the value is external (that is, undefined at this
539 // PC-relative fixups must be DEFINED and either in the same
540 // section (whereupon the subtraction takes place) or ABS (with no
550 error("PC-relative expr across sections");
554 if (sbra_flag && (w & FU_LBRA) && (eval + 0x80 < 0x100))
555 warn("unoptimized short branch");
557 else if (obj_format == MWC)
565 switch ((int)(w & FUMASK))
567 // FU_BBRA fixes up a one-byte branch offset.
569 if (!(eattr & DEFINED))
571 error("external short branch");
577 if (eval + 0x80 >= 0x100)
582 error("illegal bra.s with zero offset");
586 *++locp = (char)eval;
588 // Fixup one-byte value at locp + 1.
592 // Fixup one-byte forward references
594 if (!(eattr & DEFINED))
596 error("external byte reference");
602 error("non-absolute byte reference");
606 if ((w & FU_PCREL) && eval + 0x80 >= 0x100)
611 if (eval + 0x100 >= 0x200)
614 else if (eval >= 0x100)
619 // Fixup WORD forward references;
620 // the word could be unaligned in the section buffer, so we have to
623 if (((w & 0x0F00) == FU_JR) || ((w & 0x0F00) == FU_MJR))
629 reg2 = (signed)((eval - (oaddr + 2)) / 2);// & 0x1F;
633 reg2 = (signed)((eval - (loc + 2)) / 2);// & 0x1F;
636 if ((w & 0x0F00) == FU_MJR)
638 // Main code destination alignment checking here for
639 // forward declared labels
640 address = (oaddr) ? oaddr : loc;
642 if (((address >= 0xF03000) && (address < 0xF04000)
643 && (eval < 0xF03000)) || ((eval >= 0xF03000)
644 && (eval < 0xF04000) && (address < 0xF03000)))
646 warni("* \'jr\' at $%08X - cannot jump relative between "
647 "main memory and local gpu ram", address);
651 page_jump = (address & 0xFFFFFF00) - (eval & 0xFFFFFF00);
655 // This jump is to a page outside of the
656 // current 256 byte page
659 warni("* \'jr\' at $%08X - destination address not aligned for long page jump, insert a \'nop\' before the destination address", address);
664 // This jump is in the current 256 byte page
667 warni("* \'jr\' at $%08X - destination address not aligned for short page jump, insert a \'nop\' before the destination address", address);
673 if ((reg2 < -16) || (reg2 > 15))
675 error("relative jump out of range");
679 *locp = (char)(*locp | ((reg2 >> 3) & 0x03));
681 *locp = (char)(*locp | ((reg2 & 0x07) << 5));
685 if ((w & 0x0F00) == FU_NUM15)
687 if (eval < -16 || eval > 15)
689 error("constant out of range");
693 *locp = (char)(*locp | ((eval >> 3) & 0x03));
695 *locp = (char)(*locp | ((eval & 0x07) << 5));
699 if ((w & 0x0F00) == FU_NUM31)
701 if (eval < 0 || eval > 31)
703 error("constant out of range");
707 *locp = (char)(*locp | ((eval >> 3) & 0x03));
709 *locp = (char)(*locp | ((eval & 0x07) << 5));
713 if ((w & 0x0F00) == FU_NUM32)
715 if (eval < 1 || eval > 32)
717 error("constant out of range");
724 eval = (eval == 32) ? 0 : eval;
725 *locp = (char)(*locp | ((eval >> 3) & 0x03));
727 *locp = (char)(*locp | ((eval & 0x07) << 5));
731 if ((w & 0x0F00) == FU_REGONE)
733 if (eval < 0 || eval > 31)
735 error("register value out of range");
739 *locp = (char)(*locp | ((eval >> 3) & 0x03));
741 *locp = (char)(*locp | ((eval & 0x07) << 5));
745 if ((w & 0x0F00) == FU_REGTWO)
747 if (eval < 0 || eval > 31)
749 error("register value out of range");
754 *locp = (char)(*locp | (eval & 0x1F));
758 if (!(eattr & DEFINED))
765 rmark(sno, loc, 0, w, esym);
770 rmark(sno, loc, tdb, MWORD, NULL);
774 if (eval + 0x10000 >= 0x20000)
779 // Range-check BRA and DBRA
782 if (eval + 0x8000 >= 0x10000)
785 else if (eval >= 0x10000)
790 *locp++ = (char)(eval >> 8);
793 // Fixup LONG forward references;
794 // the long could be unaligned in the section buffer, so be careful
797 if ((w & 0x0F00) == 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
849 eval = ((eval >> 16) & 0x0000FFFF) | ((eval << 16) & 0xFFFF0000);
850 flags = (MLONG|MMOVEI);
855 if (!(eattr & DEFINED))
857 rmark(sno, loc, 0, flags, esym);
861 rmark(sno, loc, tdb, flags, NULL);
864 *locp++ = (char)(eval >> 24);
865 *locp++ = (char)(eval >> 16);
866 *locp++ = (char)(eval >> 8);
869 // Fixup a 3-bit "QUICK" reference in bits 9..1
870 // (range of 1..8) in a word. Really bits 1..3 in a byte.
872 if (!(eattr & DEFINED))
874 error("External quick reference");
878 if (eval < 1 || eval > 8)
881 *locp |= (eval & 7) << 1;
883 // Fix up 6502 funny branch
887 if (eval + 0x80 >= 0x100)
893 interror(4); // Bad fixup type
898 error("expression out of range");