1 ////////////////////////////////////////////////////////////////////////////////////////////////////
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
18 // Section descriptors
19 SECT sect[NSECTS]; // All sections...
20 int cursect; // Current section number
22 // These are copied from the section descriptor, the current code chunk descriptor and the current
23 // fixup chunk descriptor when a switch is made into a section. They are copied back to the
24 // descriptors when the section is left.
25 WORD scattr; // Section attributes
26 LONG sloc; // Current loc in section
28 CHUNK *scode; // Current (last) code chunk
29 LONG challoc; // #bytes alloc'd to code chunk
30 LONG ch_size; // #bytes used in code chunk
31 char *chptr; // Deposit point in code chunk buffer
33 CHUNK *sfix; // Current (last) fixup chunk
34 LONG fchalloc; // #bytes alloc'd to fixup chunk
35 LONG fchsize; // #bytes used in fixup chunk
36 PTR fchptr; // Deposit point in fixup chunk buffer
38 unsigned fwdjump[MAXFWDJUMPS]; // forward jump check table
39 unsigned fwindex = 0; // forward jump index
41 // Return a size (SIZB, SIZW, SIZL) or 0, depending on what kind of fixup is associated
43 static char fusiztab[] = {
54 // Offset to REAL fixup location
55 static char fusizoffs[] = {
67 // --- Make a New (Clean) Section ------------------------------------------------------------------
70 void mksect(int sno, WORD attr) {
71 SECT *p; // Section pointer
76 p->scode = p->sfcode = NULL;
77 p->sfix = p->sffix = NULL;
81 // --- Switch to Another Section (Copy Section & Chunk Descriptors to Global Vars for Fast Access) -
84 void switchsect(int sno) {
85 SECT *p; // Section pointer
86 CHUNK *cp; // Chunk pointer
91 scattr = p->scattr; // Copy section vars
96 if((cp = scode) != NULL) { // Copy code chunk vars
97 challoc = cp->challoc;
98 ch_size = cp->ch_size;
99 chptr = cp->chptr + ch_size;
100 } else challoc = ch_size = 0;
102 if((cp = sfix) != NULL) { // Copy fixup chunk vars
103 fchalloc = cp->challoc;
104 fchsize = cp->ch_size;
105 fchptr.cp = cp->chptr + fchsize;
106 } else fchalloc = fchsize = 0;
110 // --- Save Current Section ------------------------------------------------------------------------
118 p->scattr = scattr; // Bailout section vars
121 if(scode != NULL) // Bailout code chunk
122 scode->ch_size = ch_size;
124 if(sfix != NULL) // Bailout fixup chunk
125 sfix->ch_size = fchsize;
129 // --- Initialize Sections; Setup initial ABS, TEXT, DATA and BSS sections -------------------------
132 void init_sect(void) {
135 // Cleanup all sections
136 for(i = 0; i < NSECTS; ++i)
139 // Construct default sections, make TEXT the current section
140 mksect(ABS, SUSED|SABS|SBSS); // ABS
141 mksect(TEXT, SUSED|TEXT ); // TEXT
142 mksect(DATA, SUSED|DATA ); // DATA
143 mksect(BSS, SUSED|BSS |SBSS); // BSS
144 // mksect(M6502, SUSED|TEXT ); // 6502 code section
146 switchsect(TEXT); // Switch to TEXT for starters
150 // -------------------------------------------------------------------------------------------------
151 // Test to see if a location has a fixup sic'd on it. This is used by the listing
152 // generator to print 'xx's instead of '00's for forward references
153 // -------------------------------------------------------------------------------------------------
156 int fixtest(int sno, LONG loc) {
163 stopmark(); // Force update to sect[] variables
165 // Hairy, ugly linear search for a mark on our location;
166 // the speed doesn't matter, since this is only done when generating a listing, which is SLOW.
167 for(ch = sect[sno].sffix; ch != NULL; ch = ch->chnext) {
168 fup.cp = (char *)ch->chptr;
169 fuend = fup.cp + ch->ch_size;
171 while(fup.cp < fuend) {
173 xloc = *fup.lp++ + (int)fusizoffs[w & FUMASK];
177 return((int)fusiztab[w & FUMASK]);
190 // -------------------------------------------------------------------------------------------------
191 // Check that there are at least `amt' bytes left in the current chunk. If there are not,
192 // allocate another chunk of at least `amt' bytes (and probably more).
194 // If `amt' is zero, ensure there are at least CH_THRESHOLD bytes, likewise.
195 // -------------------------------------------------------------------------------------------------
198 int chcheck(LONG amt) {
202 if(scattr & SBSS) return(0); // If in BSS section, forget it
204 if(!amt) amt = CH_THRESHOLD;
206 if((int)(challoc - ch_size) >= (int)amt)
209 if(amt < CH_CODE_SIZE) amt = CH_CODE_SIZE;
211 cp = (CHUNK *)amem((long)(sizeof(CHUNK) + amt));
212 if(scode == NULL) { // First chunk in section
215 } else { // Add chunk to other chunks
218 scode->ch_size = ch_size; // Save old chunk's globals
221 // Setup chunk and global vars
224 challoc = cp->challoc = amt;
225 ch_size = cp->ch_size = 0;
226 chptr = cp->chptr = ((char *)cp) + sizeof(CHUNK);
227 scode = p->scode = cp;
233 // --- Arrange for a fixup on a location -----------------------------------------------------------
236 int fixup(WORD attr, LONG loc, TOKEN *fexpr) {
242 // Compute length of expression (could be faster); determine if it's the single-symbol case;
243 // no expression if it's just a mark. This code assumes 16 bit WORDs and 32 bit LONGs
244 if(*fexpr == SYMBOL && fexpr[2] == ENDEXPR)
245 //if((attr & 0x0F00) == FU_JR) {
246 if((attr & 0x0200) == FU_JR) {
247 i = 18; // Just a single symbol
253 for(len = 0; fexpr[len] != ENDEXPR; ++len)
254 if(fexpr[len] == CONST || fexpr[len] == SYMBOL)
256 ++len; // Add 1 for ENDEXPR
260 // Maybe alloc another fixup chunk for this one to fit in
261 if((fchalloc - fchsize) < i) {
263 cp = (CHUNK *)amem((long)(sizeof(CHUNK) + CH_FIXUP_SIZE));
264 if(sfix == NULL) { // First fixup chunk in section
267 } else { // Add to other chunks
270 sfix->ch_size = fchsize;
273 // Setup fixup chunk and its global vars
275 fchalloc = cp->challoc = CH_FIXUP_SIZE;
276 fchsize = cp->ch_size = 0;
277 fchptr.cp = cp->chptr = ((char *)cp) + sizeof(CHUNK);
281 // Record fixup type, fixup location, and the file number and line number the fixup is
285 *fchptr.wp++ = cfileno;
286 *fchptr.wp++ = (WORD)curlineno;
287 // Store postfix expression or pointer to a single symbol, or nothing for a mark.
289 *fchptr.wp++ = (WORD)len;
291 *fchptr.lp++ = (LONG)*fexpr++;
293 *fchptr.lp++ = (LONG)fexpr[1];
296 //if((attr & 0x0F00) == FU_JR) {
297 if((attr & 0x0200) == FU_JR) {
298 if(orgactive) *fchptr.lp++ = orgaddr;
299 else *fchptr.lp++ = 0x00000000;
308 // --- Resolve all Fixups --------------------------------------------------------------------------
312 unsigned i; // Iterator
315 if(glob_flag) // Make undefined symbols GLOBL
320 // We need to do a final check of forward 'jump' destination addresses that are external
321 for(i = 0; i < MAXFWDJUMPS; i++) {
324 sprintf(buf, "* \'jump\' at $%08X - destination address is external to this source file and "
325 "cannot have its aligment validated", fwdjump[i]);
326 if(listing > 0) ship_ln(buf);
327 if(err_flag) write(err_fd, buf, (LONG)strlen(buf));
328 else printf("%s\n", buf);
336 // --- Resolve Fixups in a Section -----------------------------------------------------------------
339 int resfix(int sno) {
342 PTR fup; // Current fixup
343 WORD *fuend; // End of last fixup (in this chunk)
344 CHUNK *cch; // Cached chunk for target
345 WORD w; // Fixup word (type+modes+flags)
346 char *locp; // Location to fix (in cached chunk)
347 LONG loc; // Location to fixup
348 VALUE eval; // Expression value
349 WORD eattr; // Expression attrib
350 SYM *esym; // External symbol involved in expr
351 SYM *sy; // (Temp) pointer to a symbol
352 WORD i; // (Temp) word
353 WORD tdb; // eattr & TDB
357 unsigned page_jump = 0;
358 unsigned address = 0;
359 unsigned j; // iterator
368 cch = sc->sfcode; // "cache" first chunk
369 if(cch == NULL) // Can't fixup a sect with nothing in it
373 fup.cp = ch->chptr; // fup -> start of chunk
374 fuend = (WORD *)(fup.cp + ch->ch_size); // fuend -> end of chunk
375 while(fup.wp < fuend) {
379 curlineno = (int)*fup.wp++;
382 // Search for chunk containing location to fix up; compute a pointer to the location
383 // (in the chunk). Often we will find the fixup is in the "cached" chunk, so the
384 // linear-search is seldom executed.
385 if(loc < cch->chloc || loc >= (cch->chloc + cch->ch_size)) {
386 for(cch = sc->sfcode; cch != NULL; cch = cch->chnext)
387 if(loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
390 interror(7); // Fixup (loc) out of range
394 locp = cch->chptr + (loc - cch->chloc);
398 // Compute expression/symbol value and attribs
399 if(w & FU_EXPR) { // Complex expression
401 if(evexpr(fup.tk, &eval, &eattr, &esym) != OK) {
406 } else { // Simple symbol
413 if((eattr & (GLOBAL|DEFINED)) == GLOBAL)
416 tdb = (WORD)(eattr & TDB);
417 // If the expression is undefined and no external symbol is involved, then it's an error.
418 if(!(eattr & DEFINED) && esym == NULL) {
424 if(((w & 0x0F00) == FU_MOVEI) && esym)
425 esym->sattre |= RISCSYM;
429 // If a PC-relative fixup is undefined, its value is *not* subtracted from the location
430 // (that will happen in the linker when the external reference is resolved).
432 // MWC expects PC-relative things to have the LOC subtracted from the value, if the
433 // value is external (that is, undefined at this point).
435 // PC-relative fixups must be DEFINED and either in the same section (whereupon the
436 // subtraction takes place) or ABS (with no subtract).
438 if(eattr & DEFINED) {
439 if(tdb == sno) eval -= (VALUE)loc;
441 error("PC-relative expr across sections");
445 if(sbra_flag && (w & FU_LBRA) && (eval + 0x80 < 0x100))
446 warn("unoptimized short branch");
448 if(obj_format == MWC) eval -= (VALUE)loc;
455 switch((int)(w & FUMASK)) {
456 // FU_BBRA fixes up a one-byte branch offset.
458 if(!(eattr & DEFINED)) {
459 error("external short branch");
463 if(eval + 0x80 >= 0x100)
466 error("illegal bra.s with zero offset");
469 *++locp = (char)eval;
471 // Fixup one-byte value at locp + 1.
475 // Fixup one-byte forward references
477 if(!(eattr & DEFINED)) {
478 error("external byte reference");
482 error("non-absolute byte reference");
485 if((w & FU_PCREL) && eval + 0x80 >= 0x100) goto range;
487 if(eval + 0x100 >= 0x200) goto range;
489 if(eval >= 0x100) goto range;
493 // Fixup WORD forward references;
494 // the word could be unaligned in the section buffer, so we have to be careful.
496 if(((w & 0x0F00) == FU_JR) || ((w & 0x0F00) == FU_MJR)) {
499 reg2 = (signed)((eval - (oaddr + 2)) / 2);// & 0x1F;
501 reg2 = (signed)((eval - (loc + 2)) / 2);// & 0x1F;
503 if((w & 0x0F00) == FU_MJR) {
504 // Main code destination alignment checking here for forward declared labels
505 address = (oaddr) ? oaddr : loc;
506 if(((address >= 0xF03000) && (address < 0xF04000) && (eval < 0xF03000)) ||
507 ((eval >= 0xF03000) && (eval < 0xF04000) && (address < 0xF03000)) ) {
508 warni("* \'jr\' at $%08X - cannot jump relative between "
509 "main memory and local gpu ram", address);
511 page_jump = (address & 0xFFFFFF00) - (eval & 0xFFFFFF00);
513 // This jump is to a page outside of the current 256 byte page
515 warni("* \'jr\' at $%08X - destination address not aligned for long page jump, "
516 "insert a \'nop\' before the destination address", address);
519 // This jump is in the current 256 byte page
521 warni("* \'jr\' at $%08X - destination address not aligned for short page jump, "
522 "insert a \'nop\' before the destination address", address);
527 if((reg2 < -16) || (reg2 > 15)) {
528 error("relative jump out of range");
531 *locp = (char)(*locp | ((reg2 >> 3) & 0x03));
533 *locp = (char)(*locp | ((reg2 & 0x07) << 5));
536 if((w & 0x0F00) == FU_NUM15) {
537 if(eval < -16 || eval > 15) {
538 error("constant out of range");
541 *locp = (char)(*locp | ((eval >> 3) & 0x03));
543 *locp = (char)(*locp | ((eval & 0x07) << 5));
546 if((w & 0x0F00) == FU_NUM31) {
547 if(eval < 0 || eval > 31) {
548 error("constant out of range");
551 *locp = (char)(*locp | ((eval >> 3) & 0x03));
553 *locp = (char)(*locp | ((eval & 0x07) << 5));
556 if((w & 0x0F00) == FU_NUM32) {
557 if(eval < 1 || eval > 32) {
558 error("constant out of range");
563 eval = (eval == 32) ? 0 : eval;
564 *locp = (char)(*locp | ((eval >> 3) & 0x03));
566 *locp = (char)(*locp | ((eval & 0x07) << 5));
569 if((w & 0x0F00) == FU_REGONE) {
570 if(eval < 0 || eval > 31) {
571 error("register value out of range");
574 *locp = (char)(*locp | ((eval >> 3) & 0x03));
576 *locp = (char)(*locp | ((eval & 0x07) << 5));
579 if((w & 0x0F00) == FU_REGTWO) {
580 if(eval < 0 || eval > 31) {
581 error("register value out of range");
585 *locp = (char)(*locp | (eval & 0x1F));
589 if(!(eattr & DEFINED)) {
593 rmark(sno, loc, 0, w, esym);
596 rmark(sno, loc, tdb, MWORD, NULL);
598 if(eval + 0x10000 >= 0x20000)
601 if(w & FU_ISBRA) { // Range-check BRA and DBRA
602 if(eval + 0x8000 >= 0x10000)
609 *locp++ = (char)(eval >> 8);
612 // Fixup LONG forward references;
613 // the long could be unaligned in the section buffer, so be careful (again).
615 if((w & 0x0F00) == FU_MOVEI) {
617 if(eattr & DEFINED) {
618 for(j = 0; j < fwindex; j++) {
619 if(fwdjump[j] == address) {
620 page_jump = (address & 0xFFFFFF00) - (eval & 0xFFFFFF00);
624 sprintf(buf, "* \'jump\' at $%08X - destination address not aligned for long page jump, "
625 "insert a \'nop\' before the destination address", address);
626 if(listing > 0) ship_ln(buf);
627 if(err_flag) write(err_fd, buf, (LONG)strlen(buf));
628 else printf("%s\n", buf);
631 if(!(eval & 0x0000000F) || ((eval - 2) % 4)) {
633 sprintf(buf, "* \'jump\' at $%08X - destination address not aligned for short page jump, "
634 "insert a \'nop\' before the destination address", address);
635 if(listing > 0) ship_ln(buf);
636 if(err_flag) write(err_fd, buf, (LONG)strlen(buf));
637 else printf("%s\n", buf);
640 // Clear this jump as it has been checked
646 eval = ((eval >> 16) & 0x0000FFFF) | ((eval << 16) & 0xFFFF0000);
647 flags = (MLONG|MMOVEI);
648 } else flags = MLONG;
649 if(!(eattr & DEFINED)) {
650 rmark(sno, loc, 0, flags, esym);
653 rmark(sno, loc, tdb, flags, NULL);
655 *locp++ = (char)(eval >> 24);
656 *locp++ = (char)(eval >> 16);
657 *locp++ = (char)(eval >> 8);
660 // Fixup a 3-bit "QUICK" reference in bits 9..1
661 // (range of 1..8) in a word. Really bits 1..3 in a byte.
663 if(!(eattr & DEFINED)) {
664 error("External quick reference");
668 if(eval < 1 || eval > 8)
670 *locp |= (eval & 7) << 1;
672 // Fix up 6502 funny branch
675 if(eval + 0x80 >= 0x100)
680 interror(4); // Bad fixup type
687 error("expression out of range");