2 // RMAC - Renamed Macro Assembler for all Atari computers
3 // SECT.C - Code Generation, Fixups and Section Management
4 // Copyright (C) 199x Landon Dyer, 2011-2021 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
25 // Function prototypes
26 void MakeSection(int, uint16_t);
27 void SwitchSection(int);
29 // Section descriptors
30 SECT sect[NSECTS]; // All sections...
31 int cursect; // Current section number
33 // These are copied from the section descriptor, the current code chunk
34 // descriptor and the current fixup chunk descriptor when a switch is made into
35 // a section. They are copied back to the descriptors when the section is left.
36 uint16_t scattr; // Section attributes
37 uint32_t sloc; // Current loc in section
39 CHUNK * scode; // Current (last) code chunk
40 uint32_t challoc; // # bytes alloc'd to code chunk
41 uint32_t ch_size; // # bytes used in code chunk
42 uint8_t * chptr; // Deposit point in code chunk buffer
43 uint8_t * chptr_opcode; // Backup of chptr, updated before entering code generators
45 // Return a size (SIZB, SIZW, SIZL) or 0, depending on what kind of fixup is
46 // associated with a location.
47 static uint8_t fusiztab[] = {
58 // Offset to REAL fixup location
59 static uint8_t fusizoffs[] = {
72 // Initialize sections; setup initial ABS, TEXT, DATA and BSS sections
74 void InitSection(void)
76 // Initialize all sections
77 for(int i=0; i<NSECTS; i++)
80 // Construct default sections, make TEXT the current section
81 MakeSection(ABS, SUSED | SABS | SBSS); // ABS
82 MakeSection(TEXT, SUSED | TEXT ); // TEXT
83 MakeSection(DATA, SUSED | DATA ); // DATA
84 MakeSection(BSS, SUSED | BSS | SBSS); // BSS
85 MakeSection(M6502, SUSED | TEXT ); // 6502 code section
86 MakeSection(M56001P, SUSED | SABS ); // DSP 56001 Program RAM
87 MakeSection(M56001X, SUSED | SABS ); // DSP 56001 X RAM
88 MakeSection(M56001Y, SUSED | SABS ); // DSP 56001 Y RAM
90 // Switch to TEXT for starters
96 // Make a new (clean) section
98 void MakeSection(int sno, uint16_t attr)
100 SECT * sp = §[sno];
104 sp->scode = sp->sfcode = NULL;
105 sp->sfix = sp->sffix = NULL;
110 // Switch to another section (copy section & chunk descriptors to global vars
113 void SwitchSection(int sno)
117 SECT * sp = §[sno];
119 m6502 = (sno == M6502); // Set 6502-mode flag
125 orgaddr = sp->orgaddr;
127 // Copy code chunk vars
128 if ((cp = scode) != NULL)
130 challoc = cp->challoc;
131 ch_size = cp->ch_size;
132 chptr = cp->chptr + ch_size;
134 // For 6502 mode, add the last org'd address
137 Because the way this is set up it treats the 6502 assembly space as a single 64K space (+ 16 bytes, for some unknown reason) and just bobbles around inside that space and uses a stack of org "pointers" to show where the data ended up.
139 This is a shitty way to handle things, and we can do better than this! :-P
141 Really, there's no reason to have the 6502 (or DSP56001 for that matter) have their own private sections for this kind of thing, as there's literally *no* chance that it would be mingled with 68K+ code. It should be able to use the TEXT, DATA & BSS sections just like the 68K.
143 Or should it? After looking at the code, maybe it's better to keep the 56001 sections segregated from the rest. But we can still make the 6502 stuff better.
146 chptr = cp->chptr + orgaddr;
149 challoc = ch_size = 0;
154 // Save current section
156 void SaveSection(void)
158 SECT * sp = §[cursect];
160 sp->scattr = scattr; // Bailout section vars
162 sp->orgaddr = orgaddr;
164 if (scode != NULL) // Bailout code chunk (if any)
165 scode->ch_size = ch_size;
170 // Test to see if a location has a fixup set on it. This is used by the
171 // listing generator to print 'xx's instead of '00's for forward references
173 int fixtest(int sno, uint32_t loc)
175 // Force update to sect[] variables
178 // 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(FIXUP * fp=sect[sno].sffix; fp!=NULL; fp=fp->next)
183 uint32_t w = fp->attr;
184 uint32_t xloc = fp->loc + (int)fusizoffs[w & FUMASK];
187 return (int)fusiztab[w & FUMASK];
195 // Check that there are at least 'amt' bytes left in the current chunk. If
196 // there are not, allocate another chunk of at least CH_CODE_SIZE bytes or
197 // 'amt', whichever is larger.
199 // If 'amt' is zero, ensure there are at least CH_THRESHOLD bytes, likewise.
201 void chcheck(uint32_t amt)
203 DEBUG { printf("chcheck(%u)\n", amt); }
205 // If in BSS section, no allocation required
212 DEBUG { printf(" challoc=%i, ch_size=%i, diff=%i\n", challoc, ch_size, challoc - ch_size); }
214 if ((int)(challoc - ch_size) >= (int)amt)
217 if (amt < CH_CODE_SIZE)
220 DEBUG { printf(" amt (adjusted)=%u\n", amt); }
221 SECT * p = §[cursect];
222 CHUNK * cp = malloc(sizeof(CHUNK) + amt);
227 // First chunk in section
234 // Add second and on to previous chunk
237 scode->ch_size = ch_size; // Save old chunk's globals
240 // Setup chunk and global vars
242 So, whenever there's an ORG in a 56K section, it sets sloc TO THE ADDRESS IN THE ORG. Also, the loc/sloc are incremented by 1s, which means to alias correctly to the byte-oriented memory model we have here, we have to fix that kind of crap.
244 cp->chloc = sloc; // <-- HERE'S THE PROBLEM FOR 56K :-/
246 challoc = cp->challoc = amt;
247 ch_size = cp->ch_size = 0;
248 chptr = cp->chptr = ((uint8_t *)cp) + sizeof(CHUNK);
249 scode = p->scode = cp;
253 OK, so this is a bit shite, but at least it gets things working the way they should. The right way to do this is not rely on sloc & friends for the right fixup address but to have an accurate model of the thing. That will probably come with v2.0.1 :-P
255 So the problem is, d_org sets sloc to the address of the ORG statement, and that gives an incorrect base for the fixup. And so when a second (or later) chunk is allocated, it gets set wrong. Further complicating things is that the orgaddress *does not* get used in a typical way with the DSP56001 code, and, as such, causes incorrect addresses to be generated. All that has to be dealt with in order to get this right and do away with this kludge.
257 if (((cursect == M56001P) || (cursect == M56001X) || (cursect == M56001Y)) && !first)
258 cp->chloc = cp->chprev->chloc + cp->chprev->ch_size;
265 // Arrange for a fixup on a location
267 int AddFixup(uint32_t attr, uint32_t loc, TOKEN * fexpr)
269 uint16_t exprlen = 0;
271 uint32_t _orgaddr = 0;
273 // First, check to see if the expression is a bare label, otherwise, force
274 // the FU_EXPR flag into the attributes and count the tokens.
275 if ((fexpr[0] == SYMBOL) && (fexpr[2] == ENDEXPR))
277 symbol = symbolPtr[fexpr[1]];
279 // Save the org address for JR RISC instruction
280 if ((attr & FUMASKRISC) == FU_JR)
286 exprlen = ExpressionLength(fexpr);
289 // Second, check to see if it's a DSP56001 fixup, and force the FU_56001
290 // flag into the attributes if so; also save the current org address.
291 if (attr & FUMASKDSP)
294 // Save the exact spot in this chunk where the fixup should go
295 _orgaddr = chptr - scode->chptr + scode->chloc;
298 // Allocate space for the fixup + any expression
299 FIXUP * fixup = malloc(sizeof(FIXUP) + (sizeof(TOKEN) * exprlen)*2);
301 // Store the relevant fixup information in the FIXUP
305 fixup->fileno = cfileno;
306 fixup->lineno = curlineno;
308 fixup->symbol = symbol;
309 fixup->orgaddr = _orgaddr;
311 // Copy the passed in expression to the FIXUP, if any
314 // Here we used to to a plain memcpy and punt on trying to evaluate the expression by then.
315 // As discussed in bug #176, this can lead to robustness issues because some symbols might
316 // have changed by the time we perform the relocations (think of a symbol that's SET multiple
317 // times). So instead we perform a symbol-by-symbol copy and check to see if there are any
318 // resolved symbols that can be evaluated immediately. Those, we replace with constants.
319 // Also of note: because "fixup" can be larger than what ExpressionLength() returns
320 // (due to constants taking up more space than symbols), we allocate twice as RAM as we should
321 // without expansions just to be on the safe side. The "correct" thing to do would be to
322 // modify ExpressionLength() to cater for defined symbols and return the exact amount of items.
324 fixup->expr = (TOKEN *)((uint8_t *)fixup + sizeof(FIXUP));
327 dstexpr.u32 = fixup->expr;
329 for (i = 0; i < exprlen; i++)
331 if (*fexpr == SYMBOL)
333 sy = symbolPtr[fexpr[1]];
334 if (sy->sattr & DEFINED && !(sy->sattr & (TDB| M56KPXYL|M6502)))
336 // Only convert symbols that are defined and are absolute
337 *dstexpr.u32++ = CONST;
338 *dstexpr.u64++ = sy->svalue;
344 // Just copy the symbol
345 *dstexpr.u32++ = *fexpr++;
346 *dstexpr.u32++ = *fexpr++;
350 else if (*fexpr == CONST || *fexpr == FCONST)
353 *dstexpr.u32++ = *fexpr++;
354 *dstexpr.u32++ = *fexpr++;
355 *dstexpr.u32++ = *fexpr++;
359 *dstexpr.u32++ = *fexpr++;
363 // Finally, put the FIXUP in the current section's linked list
364 if (sect[cursect].sffix == NULL)
366 sect[cursect].sffix = fixup;
367 sect[cursect].sfix = fixup;
371 sect[cursect].sfix->next = fixup;
372 sect[cursect].sfix = fixup;
375 DEBUG { printf("AddFixup: sno=%u, l#=%u, attr=$%X, loc=$%X, expr=%p, sym=%p, org=$%X\n", cursect, fixup->lineno, fixup->attr, fixup->loc, (void *)fixup->expr, (void *)fixup->symbol, fixup->orgaddr);
377 printf(" name: %s, value: $%lX\n", symbol->sname, symbol->svalue);
385 // Resolve fixups in the passed in section
387 int ResolveFixups(int sno)
389 SECT * sc = §[sno];
391 // "Cache" first chunk
392 CHUNK * cch = sc->sfcode;
394 // Can't fixup a section with nothing in it
398 // Wire the 6502 segment's size to its allocated size (64K)
400 cch->ch_size = cch->challoc;
402 // Get first fixup for the passed in section
403 FIXUP * fixup = sect[sno].sffix;
405 while (fixup != NULL)
407 // We do it this way because we have continues everywhere... :-P
411 uint32_t dw = fup->attr; // Fixup long (type + modes + flags)
412 uint32_t loc = fup->loc; // Location to fixup
413 cfileno = fup->fileno;
414 curlineno = fup->lineno;
415 DEBUG { printf("ResolveFixups: sect#=%u, l#=%u, attr=$%X, loc=$%X, expr=%p, sym=%p, org=$%X\n", sno, fup->lineno, fup->attr, fup->loc, (void *)fup->expr, (void *)fup->symbol, fup->orgaddr); }
417 // This is based on global vars cfileno, curfname :-P
418 // This approach is kinda meh as well. I think we can do better
420 SetFilenameForErrorReporting();
422 if ((sno == M56001P) || (sno == M56001X) || (sno == M56001Y) || (sno == M56001L))
425 // Search for chunk containing location to fix up; compute a
426 // pointer to the location (in the chunk). Often we will find the
427 // Fixup is in the "cached" chunk, so the linear-search is seldom
429 if (loc < cch->chloc || loc >= (cch->chloc + cch->ch_size))
431 for(cch=sc->sfcode; cch!=NULL; cch=cch->chnext)
433 if (loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
439 // Fixup (loc) is out of range--this should never happen!
440 // Once we call this function, it winds down immediately; it
446 // Location to fix (in current chunk)
447 // We use the address of the chunk that loc is actually in, then
448 // subtract the chunk's starting location from loc to get the offset
449 // into the current chunk.
450 uint8_t * locp = cch->chptr + (loc - cch->chloc);
452 uint16_t eattr = 0; // Expression attrib
453 SYM * esym = NULL; // External symbol involved in expr
454 uint64_t eval; // Expression value
455 uint16_t flags; // Mark flags
457 // Compute expression/symbol value and attributes
459 // Complex expression
462 // evexpr presumably issues the errors/warnings here
463 if (evexpr(fup->expr, &eval, &eattr, &esym) != OK)
466 if ((CHECK_OPTS(OPT_PC_RELATIVE)) && (eattr & (DEFINED | REFERENCED | EQUATED)) == (DEFINED | REFERENCED))
468 error("relocation not allowed when o30 is enabled");
475 SYM * sy = fup->symbol;
478 if ((CHECK_OPTS(OPT_PC_RELATIVE)) && (eattr & (DEFINED | REFERENCED | EQUATED)) == (DEFINED | REFERENCED))
480 error("relocation not allowed when o30 is enabled");
489 // If the symbol is not defined, but global, set esym to sy
490 if ((eattr & (GLOBAL | DEFINED)) == GLOBAL)
493 DEBUG { printf(" name: %s, value: $%" PRIX64 "\n", sy->sname, sy->svalue); }
496 uint16_t tdb = eattr & TDB;
498 // If the expression/symbol is undefined and no external symbol is
499 // involved, then that's an error.
500 if (!(eattr & DEFINED) && (esym == NULL))
508 // If a PC-relative fixup is undefined, its value is *not* subtracted
509 // from the location (that will happen in the linker when the external
510 // reference is resolved).
512 // PC-relative fixups must be DEFINED and either in the same section
513 // (whereupon the subtraction takes place) or ABS (with no subtract).
514 if ((dw & FU_PCREL) || (dw & FU_PCRELX))
522 // In this instruction the PC is located a DWORD away
528 // Allow cross-section PCREL fixups in Alcyon mode
529 if (prg_flag || (obj_format == RAW))
534 // Shouldn't there be a break here, since otherwise, it will point to the DATA section?
537 eval += sect[TEXT].sloc;
540 eval += sect[TEXT].sloc + sect[DATA].sloc;
543 error("invalid section");
549 // In this instruction the PC is located a DWORD away
555 error("PC-relative expr across sections");
560 if (optim_warn_flag && (dw & FU_LBRA) && (eval + 0x80 < 0x100))
561 warn("unoptimized short branch");
564 // Be sure to clear any TDB flags, since we handled it just now
569 // Handle fixup classes
572 // FU_BBRA fixes up a one-byte branch offset.
574 if (!(eattr & DEFINED))
576 error("external short branch");
582 if (eval + 0x80 >= 0x100)
587 if (*locp) // optim_flags[OPT_NULL_BRA] is stored there, check the comment in mach.s under m_br
594 warn("o6: bra.s with zero offset converted to NOP");
600 error("illegal bra.s with zero offset");
605 *++locp = (uint8_t)eval;
608 // Fixup one-byte value at locp + 1.
613 // Fixup one-byte forward references
615 if (!(eattr & DEFINED))
617 error("external byte reference");
623 error("non-absolute byte reference");
627 if ((dw & FU_PCREL) && ((eval + 0x80) >= 0x100))
632 if ((eval + 0x100) >= 0x200)
635 else if (eval >= 0x100)
638 *locp = (uint8_t)eval;
641 // Fixup high/low byte off word for 6502
643 if (!(eattr & DEFINED))
645 error("external byte reference");
649 *locp = (uint8_t)(eval >> 8);
653 if (!(eattr & DEFINED))
655 error("external byte reference");
659 *locp = (uint8_t)eval;
662 // Fixup WORD forward references; the word could be unaligned in the
663 // section buffer, so we have to be careful. (? careful about what?)
665 if ((dw & FUMASKRISC) == FU_JR)
667 int reg = (signed)((eval - ((fup->orgaddr ? fup->orgaddr : loc) + 2)) / 2);
669 if ((reg < -16) || (reg > 15))
671 error("relative jump out of range");
675 *locp |= ((uint8_t)reg >> 3) & 0x03;
677 *locp |= ((uint8_t)reg & 0x07) << 5;
680 else if ((dw & FUMASKRISC) == FU_NUM15)
682 if (((int)eval < -16) || ((int)eval > 15))
684 error("constant out of range (-16 - +15)");
688 *locp |= ((uint8_t)eval >> 3) & 0x03;
690 *locp |= ((uint8_t)eval & 0x07) << 5;
693 else if ((dw & FUMASKRISC) == FU_NUM31)
697 error("constant out of range (0-31)");
701 *locp |= ((uint8_t)eval >> 3) & 0x03;
703 *locp |= ((uint8_t)eval & 0x07) << 5;
706 else if ((dw & FUMASKRISC) == FU_NUM32)
708 if ((eval < 1) || (eval > 32))
710 error("constant out of range (1-32)");
717 eval = (eval == 32) ? 0 : eval;
718 *locp |= ((uint8_t)eval >> 3) & 0x03;
720 *locp |= ((uint8_t)eval & 0x07) << 5;
723 else if ((dw & FUMASKRISC) == FU_REGONE)
728 error("register one value out of range");
732 *locp |= ((uint8_t)eval >> 3) & 0x03;
734 *locp |= ((uint8_t)eval & 0x07) << 5;
737 else if ((dw & FUMASKRISC) == FU_REGTWO)
742 error("register two value out of range");
747 *locp |= (uint8_t)eval & 0x1F;
751 if (!(eattr & DEFINED))
758 MarkRelocatable(sno, loc, 0, flags, esym);
763 MarkRelocatable(sno, loc, tdb, MWORD, NULL);
767 if (eval + 0x10000 >= 0x20000)
772 // Range-check BRA and DBRA
775 if (eval + 0x8000 >= 0x10000)
778 else if (eval >= 0x10000)
783 // 6502 words are little endian, so handle that here
785 SETLE16(locp, 0, eval)
787 SETBE16(locp, 0, eval)
791 // Fixup LONG forward references; the long could be unaligned in the
792 // section buffer, so be careful (again).
796 if ((dw & FUMASKRISC) == FU_MOVEI)
798 // Long constant in MOVEI # is word-swapped, so fix it here
799 eval = WORDSWAP32(eval);
803 // If the symbol is undefined, make sure to pass the symbol in
804 // to the MarkRelocatable() function.
805 if (!(eattr & DEFINED))
806 MarkRelocatable(sno, loc, 0, flags, esym);
808 MarkRelocatable(sno, loc, tdb, flags, NULL);
810 SETBE32(locp, 0, eval);
813 // Fixup QUAD forward references (mainly used by the OP assembler)
817 uint64_t quad = GETBE64(locp, 0);
818 uint64_t addr = eval;
820 //Hmm, not sure how this can be set, since it's only set if it's a DSP56001 fixup or a FU_JR... :-/
822 // addr = fup->orgaddr;
824 eval = (quad & 0xFFFFFC0000FFFFFFLL) | ((addr & 0x3FFFF8) << 21);
826 else if (dw & FU_OBJDATA)
828 // If it's in a TEXT or DATA section, be sure to mark for a
831 MarkRelocatable(sno, loc, tdb, MQUAD, NULL);
833 uint64_t quad = GETBE64(locp, 0);
834 uint64_t addr = eval;
836 //Hmm, not sure how this can be set, since it's only set if it's a DSP56001 fixup or a FU_JR... :-/
838 // addr = fup->orgaddr;
840 eval = (quad & 0x000007FFFFFFFFFFLL) | ((addr & 0xFFFFF8) << 40);
843 SETBE64(locp, 0, eval);
846 // Fixup a 3-bit "QUICK" reference in bits 9..1
847 // (range of 1..8) in a word. [Really bits 1..3 in a byte.]
849 if (!(eattr & DEFINED))
851 error("External quick reference");
855 if ((eval < 1) || (eval > 8))
858 *locp |= (eval & 7) << 1;
861 // Fix up 6502 funny branch
865 if (eval + 0x80 >= 0x100)
868 *locp = (uint8_t)eval;
871 // Fixup DSP56001 addresses
873 switch (dw & FUMASKDSP)
875 // DSPIMM5 actually is clamped from 0 to 23 for our purposes
876 // and does not use the full 5 bit range.
880 error("immediate value must be between 0 and 23");
887 // This is a 12-bit address encoded into the lower 12
888 // bits of a DSP word
892 error("address out of range ($0-$FFF)");
896 locp[1] |= eval >> 8;
897 locp[2] = eval & 0xFF;
900 // This is a full DSP word containing Effective Address Extension
903 if (eval >= 0x1000000)
905 error("value out of range ($0-$FFFFFF)");
909 locp[0] = (uint8_t)((eval >> 16) & 0xFF);
910 locp[1] = (uint8_t)((eval >> 8) & 0xFF);
911 locp[2] = (uint8_t)(eval & 0xFF);
914 // This is a 16bit absolute address into a 24bit field
918 error("address out of range ($0-$FFFF)");
922 locp[1] = (uint8_t)(eval >> 8);
923 locp[2] = (uint8_t)eval;
926 // This is 12-bit immediate short data
927 // The upper nibble goes into the last byte's low nibble
928 // while the remainder 8 bits go into the 2nd byte.
932 error("immediate out of range ($0-$FFF)");
936 locp[1] = (uint8_t)eval;
937 locp[2] |= (uint8_t)(eval >> 8);
940 // This is 8-bit immediate short data
941 // which goes into the middle byte of a DSP word.
945 error("immediate out of range ($0-$FF)");
949 locp[1] = (uint8_t)eval;
952 // This is a 6 bit absoulte short address. It occupies the low 6
953 // bits of the middle byte of a DSP word.
957 error("address must be between 0 and 63");
964 // This is a 6 bit absoulte short address. It occupies the low 6
965 // bits of the middle byte of a DSP word.
967 if (eval < 0xFFFFFFC0)
969 error("address must be between $FFC0 and $FFFF");
973 locp[1] |= eval & 0x3F;
976 // Shamus: I'm pretty sure these don't make any sense...
978 warn("FU_DSPIMMFL8 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
982 warn("FU_DSPIMMFL16 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
986 warn("FU_DSPIMMFL24 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
989 // Bad fixup type--this should *never* happen!
996 // Fixup a 4-byte float
998 warn("FU_FLOATSING missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
1001 // Fixup a 8-byte float
1003 warn("FU_FLOATDOUB missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
1006 // Fixup a 12-byte float
1008 warn("FU_FLOATEXT missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
1012 // Bad fixup type--this should *never* happen!
1013 // Once we call this function, it winds down immediately; it
1020 error("expression out of range");
1028 // Resolve all fixups
1030 int ResolveAllFixups(void)
1032 // Make undefined symbols GLOBL
1034 ForceUndefinedSymbolsGlobal();
1036 DEBUG printf("Resolving TEXT sections...\n");
1037 ResolveFixups(TEXT);
1038 DEBUG printf("Resolving DATA sections...\n");
1039 ResolveFixups(DATA);
1040 DEBUG printf("Resolving 6502 sections...\n");
1041 ResolveFixups(M6502); // Fixup 6502 section (if any)
1042 DEBUG printf("Resolving DSP56001 P: sections...\n");
1043 ResolveFixups(M56001P); // Fixup 56001 P: section (if any)
1044 DEBUG printf("Resolving DSP56001 X: sections...\n");
1045 ResolveFixups(M56001X); // Fixup 56001 X: section (if any)
1046 DEBUG printf("Resolving DSP56001 Y: sections...\n");
1047 ResolveFixups(M56001Y); // Fixup 56001 Y: section (if any)