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
24 // descriptor and the current fixup chunk descriptor when a switch is made into
25 // a section. They are copied back to the 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
43 // associated with a location.
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 *)malloc(sizeof(CHUNK) + amt);
231 // First chunk in section
237 // Add chunk to other chunks
242 scode->ch_size = ch_size; // Save old chunk's globals
245 // Setup chunk and global vars
248 challoc = cp->challoc = amt;
249 ch_size = cp->ch_size = 0;
250 chptr = cp->chptr = ((char *)cp) + sizeof(CHUNK);
251 scode = p->scode = cp;
258 // Arrange for a fixup on a location
260 int fixup(WORD attr, LONG loc, TOKEN * fexpr)
267 // Compute length of expression (could be faster); determine if it's the
268 // single-symbol case; no expression if it's just a mark. This code assumes
269 // 16 bit WORDs and 32 bit LONGs
270 if (*fexpr == SYMBOL && fexpr[2] == ENDEXPR)
272 if ((attr & 0x0F00) == FU_JR) // SCPCD : correct bit mask for attr (else other FU_xxx will match) NYAN !
274 i = 18; // Just a single symbol
285 for(len=0; fexpr[len]!=ENDEXPR; len++)
287 if (fexpr[len] == CONST || fexpr[len] == SYMBOL)
291 len++; // Add 1 for ENDEXPR
295 // Maybe alloc another fixup chunk for this one to fit in
296 if ((fchalloc - fchsize) < i)
299 // cp = (CHUNK *)amem((long)(sizeof(CHUNK) + CH_FIXUP_SIZE));
300 cp = (CHUNK *)malloc(sizeof(CHUNK) + CH_FIXUP_SIZE);
303 { // First fixup chunk in section
308 { // Add to other chunks
311 sfix->ch_size = fchsize;
314 // Setup fixup chunk and its global vars
316 fchalloc = cp->challoc = CH_FIXUP_SIZE;
317 fchsize = cp->ch_size = 0;
318 fchptr.cp = cp->chptr = ((char *)cp) + sizeof(CHUNK);
322 // Record fixup type, fixup location, and the file number and line number the fixup is
326 *fchptr.wp++ = cfileno;
327 *fchptr.wp++ = (WORD)curlineno;
329 // Store postfix expression or pointer to a single symbol, or nothing for a mark.
332 *fchptr.wp++ = (WORD)len;
335 *fchptr.lp++ = (LONG)*fexpr++;
339 *fchptr.lp++ = (LONG)fexpr[1];
342 if ((attr & 0x0F00) == FU_JR) // SCPCD : correct bit mask for attr (else other FU_xxx will match) NYAN !
345 *fchptr.lp++ = orgaddr;
347 *fchptr.lp++ = 0x00000000;
356 // Resolve all Fixups
363 // Make undefined symbols GLOBL
370 // We need to do a final check of forward 'jump' destination addresses that are external
371 for(i=0; i<MAXFWDJUMPS; i++)
376 sprintf(buf, "* \'jump\' at $%08X - destination address is external to this source file and cannot have its aligment validated", fwdjump[i]);
382 write(err_fd, buf, (LONG)strlen(buf));
393 // Resolve Fixups in a Section
397 SECT * sc; // Section
399 PTR fup; // Current fixup
400 WORD * fuend; // End of last fixup (in this chunk)
401 CHUNK * cch; // Cached chunk for target
402 WORD w; // Fixup word (type+modes+flags)
403 char * locp; // Location to fix (in cached chunk)
404 LONG loc; // Location to fixup
405 VALUE eval; // Expression value
406 WORD eattr; // Expression attrib
407 SYM * esym; // External symbol involved in expr
408 SYM * sy; // (Temp) pointer to a symbol
409 WORD i; // (Temp) word
410 WORD tdb; // eattr & TDB
414 unsigned page_jump = 0;
415 unsigned address = 0;
425 cch = sc->sfcode; // "cache" first chunk
427 if (cch == NULL) // Can't fixup a sect with nothing in it
432 fup.cp = ch->chptr; // fup -> start of chunk
433 fuend = (WORD *)(fup.cp + ch->ch_size); // fuend -> end of chunk
435 while (fup.wp < fuend)
440 curlineno = (int)*fup.wp++;
444 // Search for chunk containing location to fix up; compute a pointer to the location
445 // (in the chunk). Often we will find the fixup is in the "cached" chunk, so the
446 // linear-search is seldom executed.
447 if (loc < cch->chloc || loc >= (cch->chloc + cch->ch_size))
449 for(cch=sc->sfcode; cch!=NULL; cch=cch->chnext)
451 if (loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
457 interror(7); // Fixup (loc) out of range
462 locp = cch->chptr + (loc - cch->chloc);
466 // Compute expression/symbol value and attribs
468 { // Complex expression
471 if (evexpr(fup.tk, &eval, &eattr, &esym) != OK)
489 if ((eattr & (GLOBAL|DEFINED)) == GLOBAL)
493 tdb = (WORD)(eattr & TDB);
495 // If the expression is undefined and no external symbol is
496 // involved, then it's an error.
497 if (!(eattr & DEFINED) && esym == NULL)
503 if (((w & 0x0F00) == FU_MOVEI) && esym)
504 esym->sattre |= RISCSYM;
508 // If a PC-relative fixup is undefined, its value is *not* subtracted from the location
509 // (that will happen in the linker when the external reference is resolved).
511 // MWC expects PC-relative things to have the LOC subtracted from the value, if the
512 // value is external (that is, undefined at this point).
514 // PC-relative fixups must be DEFINED and either in the same section (whereupon the
515 // subtraction takes place) or ABS (with no subtract).
524 error("PC-relative expr across sections");
528 if (sbra_flag && (w & FU_LBRA) && (eval + 0x80 < 0x100))
529 warn("unoptimized short branch");
531 else if (obj_format == MWC)
539 switch ((int)(w & FUMASK))
541 // FU_BBRA fixes up a one-byte branch offset.
543 if (!(eattr & DEFINED))
545 error("external short branch");
551 if (eval + 0x80 >= 0x100)
556 error("illegal bra.s with zero offset");
560 *++locp = (char)eval;
562 // Fixup one-byte value at locp + 1.
566 // Fixup one-byte forward references
568 if (!(eattr & DEFINED))
570 error("external byte reference");
576 error("non-absolute byte reference");
580 if ((w & FU_PCREL) && eval + 0x80 >= 0x100)
585 if (eval + 0x100 >= 0x200)
588 else if (eval >= 0x100)
593 // Fixup WORD forward references;
594 // the word could be unaligned in the section buffer, so we have to be careful.
596 if (((w & 0x0F00) == FU_JR) || ((w & 0x0F00) == FU_MJR))
602 reg2 = (signed)((eval - (oaddr + 2)) / 2);// & 0x1F;
606 reg2 = (signed)((eval - (loc + 2)) / 2);// & 0x1F;
609 if ((w & 0x0F00) == FU_MJR)
611 // Main code destination alignment checking here for forward declared labels
612 address = (oaddr) ? oaddr : loc;
614 if (((address >= 0xF03000) && (address < 0xF04000)
615 && (eval < 0xF03000)) || ((eval >= 0xF03000)
616 && (eval < 0xF04000) && (address < 0xF03000)))
618 warni("* \'jr\' at $%08X - cannot jump relative between "
619 "main memory and local gpu ram", address);
623 page_jump = (address & 0xFFFFFF00) - (eval & 0xFFFFFF00);
627 // This jump is to a page outside of the current 256 byte page
630 warni("* \'jr\' at $%08X - destination address not aligned for long page jump, insert a \'nop\' before the destination address", address);
635 // This jump is in the current 256 byte page
638 warni("* \'jr\' at $%08X - destination address not aligned for short page jump, insert a \'nop\' before the destination address", address);
644 if ((reg2 < -16) || (reg2 > 15))
646 error("relative jump out of range");
650 *locp = (char)(*locp | ((reg2 >> 3) & 0x03));
652 *locp = (char)(*locp | ((reg2 & 0x07) << 5));
656 if ((w & 0x0F00) == FU_NUM15)
658 if (eval < -16 || eval > 15)
660 error("constant out of range");
664 *locp = (char)(*locp | ((eval >> 3) & 0x03));
666 *locp = (char)(*locp | ((eval & 0x07) << 5));
670 if ((w & 0x0F00) == FU_NUM31)
672 if (eval < 0 || eval > 31)
674 error("constant out of range");
678 *locp = (char)(*locp | ((eval >> 3) & 0x03));
680 *locp = (char)(*locp | ((eval & 0x07) << 5));
684 if ((w & 0x0F00) == FU_NUM32)
686 if (eval < 1 || eval > 32)
688 error("constant out of range");
695 eval = (eval == 32) ? 0 : eval;
696 *locp = (char)(*locp | ((eval >> 3) & 0x03));
698 *locp = (char)(*locp | ((eval & 0x07) << 5));
702 if ((w & 0x0F00) == FU_REGONE)
704 if (eval < 0 || eval > 31)
706 error("register value out of range");
710 *locp = (char)(*locp | ((eval >> 3) & 0x03));
712 *locp = (char)(*locp | ((eval & 0x07) << 5));
716 if ((w & 0x0F00) == FU_REGTWO)
718 if (eval < 0 || eval > 31)
720 error("register value out of range");
725 *locp = (char)(*locp | (eval & 0x1F));
729 if (!(eattr & DEFINED))
736 rmark(sno, loc, 0, w, esym);
741 rmark(sno, loc, tdb, MWORD, NULL);
745 if (eval + 0x10000 >= 0x20000)
750 // Range-check BRA and DBRA
753 if (eval + 0x8000 >= 0x10000)
756 else if (eval >= 0x10000)
761 *locp++ = (char)(eval >> 8);
764 // Fixup LONG forward references;
765 // the long could be unaligned in the section buffer, so be careful (again).
767 if ((w & 0x0F00) == FU_MOVEI)
773 for(j=0; j<fwindex; j++)
775 if (fwdjump[j] == address)
777 page_jump = (address & 0xFFFFFF00) - (eval & 0xFFFFFF00);
784 sprintf(buf, "* \'jump\' at $%08X - destination address not aligned for long page jump, insert a \'nop\' before the destination address", address);
790 write(err_fd, buf, (LONG)strlen(buf));
797 if (!(eval & 0x0000000F) || ((eval - 2) % 4))
800 sprintf(buf, "* \'jump\' at $%08X - destination address not aligned for short page jump, insert a \'nop\' before the destination address", address);
806 write(err_fd, buf, (LONG)strlen(buf));
812 // Clear this jump as it has been checked
819 eval = ((eval >> 16) & 0x0000FFFF) | ((eval << 16) & 0xFFFF0000);
820 flags = (MLONG|MMOVEI);
825 if (!(eattr & DEFINED))
827 rmark(sno, loc, 0, flags, esym);
831 rmark(sno, loc, tdb, flags, NULL);
834 *locp++ = (char)(eval >> 24);
835 *locp++ = (char)(eval >> 16);
836 *locp++ = (char)(eval >> 8);
839 // Fixup a 3-bit "QUICK" reference in bits 9..1
840 // (range of 1..8) in a word. Really bits 1..3 in a byte.
842 if (!(eattr & DEFINED))
844 error("External quick reference");
848 if (eval < 1 || eval > 8)
851 *locp |= (eval & 7) << 1;
853 // Fix up 6502 funny branch
857 if (eval + 0x80 >= 0x100)
863 interror(4); // Bad fixup type
868 error("expression out of range");