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
23 // Function prototypes
24 void MakeSection(int, uint16_t);
25 void SwitchSection(int);
27 // Section descriptors
28 SECT sect[NSECTS]; // All sections...
29 int cursect; // Current section number
31 // These are copied from the section descriptor, the current code chunk
32 // descriptor and the current fixup chunk descriptor when a switch is made into
33 // a section. They are copied back to the descriptors when the section is left.
34 uint16_t scattr; // Section attributes
35 uint32_t sloc; // Current loc in section
37 CHUNK * scode; // Current (last) code chunk
38 uint32_t challoc; // # bytes alloc'd to code chunk
39 uint32_t ch_size; // # bytes used in code chunk
40 uint8_t * chptr; // Deposit point in code chunk buffer
41 uint8_t * chptr_opcode; // Backup of chptr, updated before entering code generators
43 // Return a size (SIZB, SIZW, SIZL) or 0, depending on what kind of fixup is
44 // associated with a location.
45 static uint8_t fusiztab[] = {
56 // Offset to REAL fixup location
57 static uint8_t fusizoffs[] = {
70 // Initialize sections; setup initial ABS, TEXT, DATA and BSS sections
72 void InitSection(void)
74 // Initialize all sections
75 for(int i=0; i<NSECTS; i++)
78 // Construct default sections, make TEXT the current section
79 MakeSection(ABS, SUSED | SABS | SBSS); // ABS
80 MakeSection(TEXT, SUSED | TEXT ); // TEXT
81 MakeSection(DATA, SUSED | DATA ); // DATA
82 MakeSection(BSS, SUSED | BSS | SBSS); // BSS
83 MakeSection(M6502, SUSED | TEXT ); // 6502 code section
84 MakeSection(M56001P, SUSED | SABS ); // DSP 56001 Program RAM
85 MakeSection(M56001X, SUSED | SABS ); // DSP 56001 X RAM
86 MakeSection(M56001Y, SUSED | SABS ); // DSP 56001 Y RAM
88 // Switch to TEXT for starters
94 // Make a new (clean) section
96 void MakeSection(int sno, uint16_t attr)
98 SECT * sp = §[sno];
102 sp->scode = sp->sfcode = NULL;
103 sp->sfix = sp->sffix = NULL;
108 // Switch to another section (copy section & chunk descriptors to global vars
111 void SwitchSection(int sno)
115 SECT * sp = §[sno];
117 m6502 = (sno == M6502); // Set 6502-mode flag
123 orgaddr = sp->orgaddr;
125 // Copy code chunk vars
126 if ((cp = scode) != NULL)
128 challoc = cp->challoc;
129 ch_size = cp->ch_size;
130 chptr = cp->chptr + ch_size;
132 // For 6502 mode, add the last org'd address
135 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.
137 This is a shitty way to handle things, and we can do better than this! :-P
139 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.
141 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.
144 chptr = cp->chptr + orgaddr;
147 challoc = ch_size = 0;
152 // Save current section
154 void SaveSection(void)
156 SECT * sp = §[cursect];
158 sp->scattr = scattr; // Bailout section vars
160 sp->orgaddr = orgaddr;
162 if (scode != NULL) // Bailout code chunk (if any)
163 scode->ch_size = ch_size;
168 // Test to see if a location has a fixup set on it. This is used by the
169 // listing generator to print 'xx's instead of '00's for forward references
171 int fixtest(int sno, uint32_t loc)
173 // Force update to sect[] variables
176 // Ugly linear search for a mark on our location. The speed doesn't
177 // matter, since this is only done when generating a listing, which is
179 for(FIXUP * fp=sect[sno].sffix; fp!=NULL; fp=fp->next)
181 uint32_t w = fp->attr;
182 uint32_t xloc = fp->loc + (int)fusizoffs[w & FUMASK];
185 return (int)fusiztab[w & FUMASK];
193 // Check that there are at least 'amt' bytes left in the current chunk. If
194 // there are not, allocate another chunk of at least CH_CODE_SIZE bytes or
195 // 'amt', whichever is larger.
197 // If 'amt' is zero, ensure there are at least CH_THRESHOLD bytes, likewise.
199 void chcheck(uint32_t amt)
201 DEBUG { printf("chcheck(%u)\n", amt); }
203 // If in BSS section, no allocation required
210 DEBUG { printf(" challoc=%i, ch_size=%i, diff=%i\n", challoc, ch_size, challoc - ch_size); }
212 if ((int)(challoc - ch_size) >= (int)amt)
215 if (amt < CH_CODE_SIZE)
218 DEBUG { printf(" amt (adjusted)=%u\n", amt); }
219 SECT * p = §[cursect];
220 CHUNK * cp = malloc(sizeof(CHUNK) + amt);
225 // First chunk in section
232 // Add second and on to previous chunk
235 scode->ch_size = ch_size; // Save old chunk's globals
238 // Setup chunk and global vars
240 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.
242 cp->chloc = sloc; // <-- HERE'S THE PROBLEM FOR 56K :-/
244 challoc = cp->challoc = amt;
245 ch_size = cp->ch_size = 0;
246 chptr = cp->chptr = ((uint8_t *)cp) + sizeof(CHUNK);
247 scode = p->scode = cp;
251 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
253 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.
255 if (((cursect == M56001P) || (cursect == M56001X) || (cursect == M56001Y)) && !first)
256 cp->chloc = cp->chprev->chloc + cp->chprev->ch_size;
263 // Arrange for a fixup on a location
265 int AddFixup(uint32_t attr, uint32_t loc, TOKEN * fexpr)
267 uint16_t exprlen = 0;
269 uint32_t _orgaddr = 0;
271 // First, check to see if the expression is a bare label, otherwise, force
272 // the FU_EXPR flag into the attributes and count the tokens.
273 if ((fexpr[0] == SYMBOL) && (fexpr[2] == ENDEXPR))
275 symbol = symbolPtr[fexpr[1]];
277 // Save the org address for JR RISC instruction
278 if ((attr & FUMASKRISC) == FU_JR)
284 exprlen = ExpressionLength(fexpr);
287 // Second, check to see if it's a DSP56001 fixup, and force the FU_56001
288 // flag into the attributes if so; also save the current org address.
289 if (attr & FUMASKDSP)
292 // Save the exact spot in this chunk where the fixup should go
293 _orgaddr = chptr - scode->chptr + scode->chloc;
296 // Allocate space for the fixup + any expression
297 FIXUP * fixup = malloc(sizeof(FIXUP) + (sizeof(TOKEN) * exprlen)*2);
299 // Store the relevant fixup information in the FIXUP
303 fixup->fileno = cfileno;
304 fixup->lineno = curlineno;
306 fixup->symbol = symbol;
307 fixup->orgaddr = _orgaddr;
309 // Copy the passed in expression to the FIXUP, if any
312 // Here we used to to a plain memcpy and punt on trying to evaluate the expression by then.
313 // As discussed in bug #176, this can lead to robustness issues because some symbols might
314 // have changed by the time we perform the relocations (think of a symbol that's SET multiple
315 // times). So instead we perform a symbol-by-symbol copy and check to see if there are any
316 // resolved symbols that can be evaluated immediately. Those, we replace with constants.
317 // Also of note: because "fixup" can be larger than what ExpressionLength() returns
318 // (due to constants taking up more space than symbols), we allocate twice as RAM as we should
319 // without expansions just to be on the safe side. The "correct" thing to do would be to
320 // modify ExpressionLength() to cater for defined symbols and return the exact amount of items.
322 fixup->expr = (TOKEN *)((uint8_t *)fixup + sizeof(FIXUP));
325 dstexpr.u32 = fixup->expr;
327 for (i = 0; i < exprlen; i++)
329 if (*fexpr == SYMBOL)
331 sy = symbolPtr[fexpr[1]];
332 if (sy->sattr & DEFINED && !(sy->sattr & (TDB| M56KPXYL|M6502)))
334 // Only convert symbols that are defined and are absolute
335 *dstexpr.u32++ = CONST;
336 *dstexpr.u64++ = sy->svalue;
342 // Just copy the symbol
343 *dstexpr.u32++ = *fexpr++;
344 *dstexpr.u32++ = *fexpr++;
348 else if (*fexpr == CONST || *fexpr == FCONST)
351 *dstexpr.u32++ = *fexpr++;
352 *dstexpr.u32++ = *fexpr++;
353 *dstexpr.u32++ = *fexpr++;
357 *dstexpr.u32++ = *fexpr++;
361 // Finally, put the FIXUP in the current section's linked list
362 if (sect[cursect].sffix == NULL)
364 sect[cursect].sffix = fixup;
365 sect[cursect].sfix = fixup;
369 sect[cursect].sfix->next = fixup;
370 sect[cursect].sfix = fixup;
373 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);
375 printf(" name: %s, value: $%lX\n", symbol->sname, symbol->svalue);
383 // Resolve fixups in the passed in section
385 int ResolveFixups(int sno)
387 SECT * sc = §[sno];
389 // "Cache" first chunk
390 CHUNK * cch = sc->sfcode;
392 // Can't fixup a section with nothing in it
396 // Wire the 6502 segment's size to its allocated size (64K)
398 cch->ch_size = cch->challoc;
400 // Get first fixup for the passed in section
401 FIXUP * fixup = sect[sno].sffix;
403 while (fixup != NULL)
405 // We do it this way because we have continues everywhere... :-P
409 uint32_t dw = fup->attr; // Fixup long (type + modes + flags)
410 uint32_t loc = fup->loc; // Location to fixup
411 cfileno = fup->fileno;
412 curlineno = fup->lineno;
413 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); }
415 // This is based on global vars cfileno, curfname :-P
416 // This approach is kinda meh as well. I think we can do better
418 SetFilenameForErrorReporting();
420 if ((sno == M56001P) || (sno == M56001X) || (sno == M56001Y) || (sno == M56001L))
423 // Search for chunk containing location to fix up; compute a
424 // pointer to the location (in the chunk). Often we will find the
425 // Fixup is in the "cached" chunk, so the linear-search is seldom
427 if (loc < cch->chloc || loc >= (cch->chloc + cch->ch_size))
429 for(cch=sc->sfcode; cch!=NULL; cch=cch->chnext)
431 if (loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
437 // Fixup (loc) is out of range--this should never happen!
438 // Once we call this function, it winds down immediately; it
444 // Location to fix (in current chunk)
445 // We use the address of the chunk that loc is actually in, then
446 // subtract the chunk's starting location from loc to get the offset
447 // into the current chunk.
448 uint8_t * locp = cch->chptr + (loc - cch->chloc);
450 uint16_t eattr = 0; // Expression attrib
451 SYM * esym = NULL; // External symbol involved in expr
452 uint64_t eval; // Expression value
453 uint16_t flags; // Mark flags
455 // Compute expression/symbol value and attributes
457 // Complex expression
460 // evexpr presumably issues the errors/warnings here
461 if (evexpr(fup->expr, &eval, &eattr, &esym) != OK)
464 if ((CHECK_OPTS(OPT_PC_RELATIVE)) && (eattr & (DEFINED | REFERENCED | EQUATED)) == (DEFINED | REFERENCED))
466 error("relocation not allowed when o10 is enabled");
473 SYM * sy = fup->symbol;
476 if ((CHECK_OPTS(OPT_PC_RELATIVE)) && (eattr & (DEFINED | REFERENCED | EQUATED)) == (DEFINED | REFERENCED))
478 error("relocation not allowed when o10 is enabled");
487 // If the symbol is not defined, but global, set esym to sy
488 if ((eattr & (GLOBAL | DEFINED)) == GLOBAL)
491 DEBUG { printf(" name: %s, value: $%" PRIX64 "\n", sy->sname, sy->svalue); }
494 uint16_t tdb = eattr & TDB;
496 // If the expression/symbol is undefined and no external symbol is
497 // involved, then that's an error.
498 if (!(eattr & DEFINED) && (esym == NULL))
506 // If a PC-relative fixup is undefined, its value is *not* subtracted
507 // from the location (that will happen in the linker when the external
508 // reference is resolved).
510 // PC-relative fixups must be DEFINED and either in the same section
511 // (whereupon the subtraction takes place) or ABS (with no subtract).
512 if ((dw & FU_PCREL) || (dw & FU_PCRELX))
520 // In this instruction the PC is located a DWORD away
526 // Allow cross-section PCREL fixups in Alcyon mode
527 if (prg_flag || (obj_format == RAW))
532 // Shouldn't there be a break here, since otherwise, it will point to the DATA section?
535 eval += sect[TEXT].sloc;
538 eval += sect[TEXT].sloc + sect[DATA].sloc;
541 error("invalid section");
547 // In this instruction the PC is located a DWORD away
553 error("PC-relative expr across sections");
558 if (optim_warn_flag && (dw & FU_LBRA) && (eval + 0x80 < 0x100))
559 warn("unoptimized short branch");
562 // Be sure to clear any TDB flags, since we handled it just now
567 // Handle fixup classes
570 // FU_BBRA fixes up a one-byte branch offset.
572 if (!(eattr & DEFINED))
574 error("external short branch");
580 if (eval + 0x80 >= 0x100)
585 if (*locp) // optim_flags[OPT_NULL_BRA] is stored there, check the comment in mach.s under m_br
592 warn("bra.s with zero offset converted to NOP");
598 error("illegal bra.s with zero offset");
603 *++locp = (uint8_t)eval;
606 // Fixup one-byte value at locp + 1.
611 // Fixup one-byte forward references
613 if (!(eattr & DEFINED))
615 error("external byte reference");
621 error("non-absolute byte reference");
625 if ((dw & FU_PCREL) && ((eval + 0x80) >= 0x100))
630 if ((eval + 0x100) >= 0x200)
633 else if (eval >= 0x100)
636 *locp = (uint8_t)eval;
639 // Fixup high/low byte off word for 6502
641 if (!(eattr & DEFINED))
643 error("external byte reference");
647 *locp = (uint8_t)(eval >> 8);
651 if (!(eattr & DEFINED))
653 error("external byte reference");
657 *locp = (uint8_t)eval;
660 // Fixup WORD forward references; the word could be unaligned in the
661 // section buffer, so we have to be careful. (? careful about what?)
663 if ((dw & FUMASKRISC) == FU_JR)
665 int reg = (signed)((eval - ((fup->orgaddr ? fup->orgaddr : loc) + 2)) / 2);
667 if ((reg < -16) || (reg > 15))
669 error("relative jump out of range");
673 *locp |= ((uint8_t)reg >> 3) & 0x03;
675 *locp |= ((uint8_t)reg & 0x07) << 5;
678 else if ((dw & FUMASKRISC) == FU_NUM15)
680 if (((int)eval < -16) || ((int)eval > 15))
682 error("constant out of range (-16 - +15)");
686 *locp |= ((uint8_t)eval >> 3) & 0x03;
688 *locp |= ((uint8_t)eval & 0x07) << 5;
691 else if ((dw & FUMASKRISC) == FU_NUM31)
695 error("constant out of range (0-31)");
699 *locp |= ((uint8_t)eval >> 3) & 0x03;
701 *locp |= ((uint8_t)eval & 0x07) << 5;
704 else if ((dw & FUMASKRISC) == FU_NUM32)
706 if ((eval < 1) || (eval > 32))
708 error("constant out of range (1-32)");
715 eval = (eval == 32) ? 0 : eval;
716 *locp |= ((uint8_t)eval >> 3) & 0x03;
718 *locp |= ((uint8_t)eval & 0x07) << 5;
721 else if ((dw & FUMASKRISC) == FU_REGONE)
725 error("register one value out of range");
729 *locp |= ((uint8_t)eval >> 3) & 0x03;
731 *locp |= ((uint8_t)eval & 0x07) << 5;
734 else if ((dw & FUMASKRISC) == FU_REGTWO)
738 error("register two value out of range");
743 *locp |= (uint8_t)eval & 0x1F;
747 if (!(eattr & DEFINED))
754 MarkRelocatable(sno, loc, 0, flags, esym);
759 MarkRelocatable(sno, loc, tdb, MWORD, NULL);
763 if (eval + 0x10000 >= 0x20000)
768 // Range-check BRA and DBRA
771 if (eval + 0x8000 >= 0x10000)
774 else if (eval >= 0x10000)
779 // 6502 words are little endian, so handle that here
781 SETLE16(locp, 0, eval)
783 SETBE16(locp, 0, eval)
787 // Fixup LONG forward references; the long could be unaligned in the
788 // section buffer, so be careful (again).
792 if ((dw & FUMASKRISC) == FU_MOVEI)
794 // Long constant in MOVEI # is word-swapped, so fix it here
795 eval = WORDSWAP32(eval);
799 // If the symbol is undefined, make sure to pass the symbol in
800 // to the MarkRelocatable() function.
801 if (!(eattr & DEFINED))
802 MarkRelocatable(sno, loc, 0, flags, esym);
804 MarkRelocatable(sno, loc, tdb, flags, NULL);
806 SETBE32(locp, 0, eval);
809 // Fixup QUAD forward references (mainly used by the OP assembler)
813 uint64_t quad = GETBE64(locp, 0);
814 uint64_t addr = eval;
816 //Hmm, not sure how this can be set, since it's only set if it's a DSP56001 fixup or a FU_JR... :-/
818 // addr = fup->orgaddr;
820 eval = (quad & 0xFFFFFC0000FFFFFFLL) | ((addr & 0x3FFFF8) << 21);
822 else if (dw & FU_OBJDATA)
824 // If it's in a TEXT or DATA section, be sure to mark for a
827 MarkRelocatable(sno, loc, tdb, MQUAD, NULL);
829 uint64_t quad = GETBE64(locp, 0);
830 uint64_t addr = eval;
832 //Hmm, not sure how this can be set, since it's only set if it's a DSP56001 fixup or a FU_JR... :-/
834 // addr = fup->orgaddr;
836 eval = (quad & 0x000007FFFFFFFFFFLL) | ((addr & 0xFFFFF8) << 40);
839 SETBE64(locp, 0, eval);
842 // Fixup a 3-bit "QUICK" reference in bits 9..1
843 // (range of 1..8) in a word. [Really bits 1..3 in a byte.]
845 if (!(eattr & DEFINED))
847 error("External quick reference");
851 if ((eval < 1) || (eval > 8))
854 *locp |= (eval & 7) << 1;
857 // Fix up 6502 funny branch
861 if (eval + 0x80 >= 0x100)
864 *locp = (uint8_t)eval;
867 // Fixup DSP56001 addresses
869 switch (dw & FUMASKDSP)
871 // DSPIMM5 actually is clamped from 0 to 23 for our purposes
872 // and does not use the full 5 bit range.
876 error("immediate value must be between 0 and 23");
883 // This is a 12-bit address encoded into the lower 12
884 // bits of a DSP word
888 error("address out of range ($0-$FFF)");
892 locp[1] |= eval >> 8;
893 locp[2] = eval & 0xFF;
896 // This is a full DSP word containing Effective Address Extension
899 if (eval >= 0x1000000)
901 error("value out of range ($0-$FFFFFF)");
905 locp[0] = (uint8_t)((eval >> 16) & 0xFF);
906 locp[1] = (uint8_t)((eval >> 8) & 0xFF);
907 locp[2] = (uint8_t)(eval & 0xFF);
910 // This is a 16bit absolute address into a 24bit field
914 error("address out of range ($0-$FFFF)");
918 locp[1] = (uint8_t)(eval >> 8);
919 locp[2] = (uint8_t)eval;
922 // This is 12-bit immediate short data
923 // The upper nibble goes into the last byte's low nibble
924 // while the remainder 8 bits go into the 2nd byte.
928 error("immediate out of range ($0-$FFF)");
932 locp[1] = (uint8_t)eval;
933 locp[2] |= (uint8_t)(eval >> 8);
936 // This is 8-bit immediate short data
937 // which goes into the middle byte of a DSP word.
941 error("immediate out of range ($0-$FF)");
945 locp[1] = (uint8_t)eval;
948 // This is a 6 bit absoulte short address. It occupies the low 6
949 // bits of the middle byte of a DSP word.
953 error("address must be between 0 and 63");
960 // This is a 6 bit absoulte short address. It occupies the low 6
961 // bits of the middle byte of a DSP word.
963 if (eval < 0xFFFFFFC0)
965 error("address must be between $FFC0 and $FFFF");
969 locp[1] |= eval & 0x3F;
972 // Shamus: I'm pretty sure these don't make any sense...
974 warn("FU_DSPIMMFL8 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
978 warn("FU_DSPIMMFL16 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
982 warn("FU_DSPIMMFL24 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
985 // Bad fixup type--this should *never* happen!
992 // Fixup a 4-byte float
994 warn("FU_FLOATSING missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
997 // Fixup a 8-byte float
999 warn("FU_FLOATDOUB missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
1002 // Fixup a 12-byte float
1004 warn("FU_FLOATEXT missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
1008 // Bad fixup type--this should *never* happen!
1009 // Once we call this function, it winds down immediately; it
1016 error("expression out of range");
1024 // Resolve all fixups
1026 int ResolveAllFixups(void)
1028 // Make undefined symbols GLOBL
1030 ForceUndefinedSymbolsGlobal();
1032 DEBUG printf("Resolving TEXT sections...\n");
1033 ResolveFixups(TEXT);
1034 DEBUG printf("Resolving DATA sections...\n");
1035 ResolveFixups(DATA);
1036 DEBUG printf("Resolving 6502 sections...\n");
1037 ResolveFixups(M6502); // Fixup 6502 section (if any)
1038 DEBUG printf("Resolving DSP56001 P: sections...\n");
1039 ResolveFixups(M56001P); // Fixup 56001 P: section (if any)
1040 DEBUG printf("Resolving DSP56001 X: sections...\n");
1041 ResolveFixups(M56001X); // Fixup 56001 X: section (if any)
1042 DEBUG printf("Resolving DSP56001 Y: sections...\n");
1043 ResolveFixups(M56001Y); // Fixup 56001 Y: section (if any)