2 // RMAC - Reboot's Macro Assembler for all Atari computers
3 // SECT.C - Code Generation, Fixups and Section Management
4 // Copyright (C) 199x Landon Dyer, 2011-2019 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
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);
222 // First chunk in section
228 // Add chunk to other chunks
233 scode->ch_size = ch_size; // Save old chunk's globals
236 // Setup chunk and global vars
239 challoc = cp->challoc = amt;
240 ch_size = cp->ch_size = 0;
241 chptr = cp->chptr = ((uint8_t *)cp) + sizeof(CHUNK);
242 scode = p->scode = cp;
249 // Arrange for a fixup on a location
251 int AddFixup(uint32_t attr, uint32_t loc, TOKEN * fexpr)
253 uint16_t exprlen = 0;
255 uint32_t _orgaddr = 0;
257 // First, check to see if the expression is a bare label, otherwise, force
258 // the FU_EXPR flag into the attributes and count the tokens.
259 if ((fexpr[0] == SYMBOL) && (fexpr[2] == ENDEXPR))
261 symbol = symbolPtr[fexpr[1]];
263 // Save the org address for JR RISC instruction
264 if ((attr & FUMASKRISC) == FU_JR)
270 exprlen = ExpressionLength(fexpr);
273 // Second, check to see if it's a DSP56001 fixup, and force the FU_56001
274 // flag into the attributes if so; also save the current org address.
275 if (attr & FUMASKDSP)
278 // Save the exact spot in this chunk where the fixup should go
279 _orgaddr = chptr - scode->chptr;
282 // Allocate space for the fixup + any expression
283 FIXUP * fixup = malloc(sizeof(FIXUP) + (sizeof(TOKEN) * exprlen));
285 // Store the relevant fixup information in the FIXUP
289 fixup->fileno = cfileno;
290 fixup->lineno = curlineno;
292 fixup->symbol = symbol;
293 fixup->orgaddr = _orgaddr;
295 // Copy the passed in expression to the FIXUP, if any
298 fixup->expr = (TOKEN *)((uint8_t *)fixup + sizeof(FIXUP));
299 memcpy(fixup->expr, fexpr, sizeof(TOKEN) * exprlen);
302 // Finally, put the FIXUP in the current section's linked list
303 if (sect[cursect].sffix == NULL)
305 sect[cursect].sffix = fixup;
306 sect[cursect].sfix = fixup;
310 sect[cursect].sfix->next = fixup;
311 sect[cursect].sfix = fixup;
314 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);
316 printf(" name: %s, value: $%lX\n", symbol->sname, symbol->svalue);
324 // Resolve fixups in the passed in section
326 int ResolveFixups(int sno)
328 SECT * sc = §[sno];
330 // "Cache" first chunk
331 CHUNK * cch = sc->sfcode;
333 // Can't fixup a section with nothing in it
337 // Wire the 6502 segment's size to its allocated size (64K)
339 cch->ch_size = cch->challoc;
341 // Get first fixup for the passed in section
342 FIXUP * fixup = sect[sno].sffix;
344 while (fixup != NULL)
346 // We do it this way because we have continues everywhere... :-P
350 uint32_t dw = fup->attr; // Fixup long (type + modes + flags)
351 uint32_t loc = fup->loc; // Location to fixup
352 cfileno = fup->fileno;
353 curlineno = fup->lineno;
354 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); }
356 // This is based on global vars cfileno, curfname :-P
357 // This approach is kinda meh as well. I think we can do better
359 SetFilenameForErrorReporting();
361 if ((sno == M56001P) || (sno == M56001X) || (sno == M56001Y) || (sno == M56001L))
364 // Search for chunk containing location to fix up; compute a
365 // pointer to the location (in the chunk). Often we will find the
366 // Fixup is in the "cached" chunk, so the linear-search is seldom
368 if (loc < cch->chloc || loc >= (cch->chloc + cch->ch_size))
370 for(cch=sc->sfcode; cch!=NULL; cch=cch->chnext)
372 if (loc >= cch->chloc && loc < (cch->chloc + cch->ch_size))
378 // Fixup (loc) is out of range--this should never happen!
379 // Once we call this function, it winds down immediately; it
385 // Location to fix (in current chunk)
386 // We use the address of the chunk that loc is actually in, then
387 // subtract the chunk's starting location from loc to get the offset
388 // into the current chunk.
389 uint8_t * locp = cch->chptr + (loc - cch->chloc);
391 uint16_t eattr = 0; // Expression attrib
392 SYM * esym = NULL; // External symbol involved in expr
393 uint64_t eval; // Expression value
394 uint16_t flags; // Mark flags
396 // Compute expression/symbol value and attributes
398 // Complex expression
401 if (evexpr(fup->expr, &eval, &eattr, &esym) != OK)
407 SYM * sy = fup->symbol;
415 // If the symbol is not defined, but global, set esym to sy
416 if ((eattr & (GLOBAL | DEFINED)) == GLOBAL)
420 uint16_t tdb = eattr & TDB;
422 // If the expression/symbol is undefined and no external symbol is
423 // involved, then that's an error.
424 if (!(eattr & DEFINED) && (esym == NULL))
432 // If a PC-relative fixup is undefined, its value is *not* subtracted
433 // from the location (that will happen in the linker when the external
434 // reference is resolved).
436 // MWC expects PC-relative things to have the LOC subtracted from the
437 // value, if the value is external (that is, undefined at this point).
439 // PC-relative fixups must be DEFINED and either in the same section
440 // (whereupon the subtraction takes place) or ABS (with no subtract).
449 // Allow cross-section PCREL fixups in Alcyon mode
455 // Shouldn't there be a break here, since otherwise, it will point to the DATA section?
458 eval += sect[TEXT].sloc;
461 eval += sect[TEXT].sloc + sect[DATA].sloc;
464 error("invalid section");
472 error("PC-relative expr across sections");
477 if (sbra_flag && (dw & FU_LBRA) && (eval + 0x80 < 0x100))
478 warn("unoptimized short branch");
480 else if (obj_format == MWC)
487 // Handle fixup classes
490 // FU_BBRA fixes up a one-byte branch offset.
492 if (!(eattr & DEFINED))
494 error("external short branch");
500 if (eval + 0x80 >= 0x100)
505 if (CHECK_OPTS(OPT_NULL_BRA))
514 error("illegal bra.s with zero offset");
519 *++locp = (uint8_t)eval;
522 // Fixup one-byte value at locp + 1.
527 // Fixup one-byte forward references
529 if (!(eattr & DEFINED))
531 error("external byte reference");
537 error("non-absolute byte reference");
541 if ((dw & FU_PCREL) && ((eval + 0x80) >= 0x100))
546 if ((eval + 0x100) >= 0x200)
549 else if (eval >= 0x100)
552 *locp = (uint8_t)eval;
555 // Fixup high/low byte off word for 6502
557 if (!(eattr & DEFINED))
559 error("external byte reference");
563 *locp = (uint8_t)(eval >> 8);
567 if (!(eattr & DEFINED))
569 error("external byte reference");
573 *locp = (uint8_t)eval;
576 // Fixup WORD forward references; the word could be unaligned in the
577 // section buffer, so we have to be careful. (? careful about what?)
579 if ((dw & FUMASKRISC) == FU_JR)
581 int reg = (signed)((eval - ((fup->orgaddr ? fup->orgaddr : loc) + 2)) / 2);
583 if ((reg < -16) || (reg > 15))
585 error("relative jump out of range");
589 *locp |= ((uint8_t)reg >> 3) & 0x03;
591 *locp |= ((uint8_t)reg & 0x07) << 5;
594 else if ((dw & FUMASKRISC) == FU_NUM15)
596 if (((int)eval < -16) || ((int)eval > 15))
598 error("constant out of range (-16 - +15)");
602 *locp |= ((uint8_t)eval >> 3) & 0x03;
604 *locp |= ((uint8_t)eval & 0x07) << 5;
607 else if ((dw & FUMASKRISC) == FU_NUM31)
611 error("constant out of range (0-31)");
615 *locp |= ((uint8_t)eval >> 3) & 0x03;
617 *locp |= ((uint8_t)eval & 0x07) << 5;
620 else if ((dw & FUMASKRISC) == FU_NUM32)
622 if ((eval < 1) || (eval > 32))
624 error("constant out of range (1-32)");
631 eval = (eval == 32) ? 0 : eval;
632 *locp |= ((uint8_t)eval >> 3) & 0x03;
634 *locp |= ((uint8_t)eval & 0x07) << 5;
637 else if ((dw & FUMASKRISC) == FU_REGONE)
641 error("register one value out of range");
645 *locp |= ((uint8_t)eval >> 3) & 0x03;
647 *locp |= ((uint8_t)eval & 0x07) << 5;
650 else if ((dw & FUMASKRISC) == FU_REGTWO)
654 error("register two value out of range");
659 *locp |= (uint8_t)eval & 0x1F;
663 if (!(eattr & DEFINED))
670 MarkRelocatable(sno, loc, 0, flags, esym);
675 MarkRelocatable(sno, loc, tdb, MWORD, NULL);
679 if (eval + 0x10000 >= 0x20000)
684 // Range-check BRA and DBRA
687 if (eval + 0x8000 >= 0x10000)
690 else if (eval >= 0x10000)
695 // 6502 words are little endian, so handle that here
697 SETLE16(locp, 0, eval)
699 SETBE16(locp, 0, eval)
703 // Fixup LONG forward references; the long could be unaligned in the
704 // section buffer, so be careful (again).
708 if ((dw & FUMASKRISC) == FU_MOVEI)
710 // Long constant in MOVEI # is word-swapped, so fix it here
711 eval = WORDSWAP32(eval);
715 // If the symbol is undefined, make sure to pass the symbol in
716 // to the MarkRelocatable() function.
717 if (!(eattr & DEFINED))
718 MarkRelocatable(sno, loc, 0, flags, esym);
720 MarkRelocatable(sno, loc, tdb, flags, NULL);
722 SETBE32(locp, 0, eval);
725 // Fixup QUAD forward references (mainly used by the OP assembler)
729 uint64_t quad = GETBE64(locp, 0);
730 uint64_t addr = eval;
732 //Hmm, not sure how this can be set, since it's only set if it's a DSP56001 fixup or a FU_JR... :-/
734 // addr = fup->orgaddr;
736 eval = (quad & 0xFFFFFC0000FFFFFFLL) | ((addr & 0x3FFFF8) << 21);
738 else if (dw & FU_OBJDATA)
740 // If it's in a TEXT or DATA section, be sure to mark for a
743 MarkRelocatable(sno, loc, tdb, MQUAD, NULL);
745 uint64_t quad = GETBE64(locp, 0);
746 uint64_t addr = eval;
748 //Hmm, not sure how this can be set, since it's only set if it's a DSP56001 fixup or a FU_JR... :-/
750 // addr = fup->orgaddr;
752 eval = (quad & 0x000007FFFFFFFFFFLL) | ((addr & 0xFFFFF8) << 40);
755 SETBE64(locp, 0, eval);
758 // Fixup a 3-bit "QUICK" reference in bits 9..1
759 // (range of 1..8) in a word. [Really bits 1..3 in a byte.]
761 if (!(eattr & DEFINED))
763 error("External quick reference");
767 if ((eval < 1) || (eval > 8))
770 *locp |= (eval & 7) << 1;
773 // Fix up 6502 funny branch
777 if (eval + 0x80 >= 0x100)
780 *locp = (uint8_t)eval;
783 // Fixup DSP56001 addresses
785 switch (dw & FUMASKDSP)
787 // DSPIMM5 actually is clamped from 0 to 23 for our purposes
788 // and does not use the full 5 bit range.
792 error("immediate value must be between 0 and 23");
799 // This is a 12-bit address encoded into the lower 12
800 // bits of a DSP word
804 error("address out of range ($0-$FFF)");
808 locp[1] |= eval >> 8;
809 locp[2] = eval & 0xFF;
812 // This is a full DSP word containing Effective Address Extension
815 if (eval >= 0x1000000)
817 error("value out of range ($0-$FFFFFF)");
821 locp[0] = (uint8_t)((eval >> 16) & 0xFF);
822 locp[1] = (uint8_t)((eval >> 8) & 0xFF);
823 locp[2] = (uint8_t)(eval & 0xFF);
826 // This is a 16bit absolute address into a 24bit field
830 error("address out of range ($0-$FFFF)");
834 locp[1] = (uint8_t)(eval >> 8);
835 locp[2] = (uint8_t)eval;
838 // This is 12-bit immediate short data
839 // The upper nibble goes into the last byte's low nibble
840 // while the remainder 8 bits go into the 2nd byte.
844 error("immediate out of range ($0-$FFF)");
848 locp[1] = (uint8_t)eval;
849 locp[2] |= (uint8_t)(eval >> 8);
852 // This is 8-bit immediate short data
853 // which goes into the middle byte of a DSP word.
857 error("immediate out of range ($0-$FF)");
861 locp[1] = (uint8_t)eval;
864 // This is a 6 bit absoulte short address. It occupies
865 // the low 6 bits of the middle byte of a DSP word.
869 error("address must be between 0 and 63");
876 // This is a 6 bit absoulte short address. It occupies
877 // the low 6 bits of the middle byte of a DSP word.
879 if (eval < 0xFFFFFFC0)
881 error("address must be between $FFC0 and $FFFF");
885 locp[1] |= eval & 0x3F;
888 // Shamus: I'm pretty sure these don't make any sense...
890 warn("FU_DSPIMMFL8 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
894 warn("FU_DSPIMMFL16 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
898 warn("FU_DSPIMMFL24 missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
901 // Bad fixup type--this should *never* happen!
908 // Fixup a 4-byte float
910 warn("FU_FLOATSING missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
913 // Fixup a 8-byte float
915 warn("FU_FLOATDOUB missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
918 // Fixup a 12-byte float
920 warn("FU_FLOATEXT missing implementation\n%s", "And you may ask yourself, \"Self, how did I get here?\"");
924 // Bad fixup type--this should *never* happen!
925 // Once we call this function, it winds down immediately; it
932 error("expression out of range");
940 // Resolve all fixups
942 int ResolveAllFixups(void)
944 // Make undefined symbols GLOBL
946 ForceUndefinedSymbolsGlobal();
948 DEBUG printf("Resolving TEXT sections...\n");
950 DEBUG printf("Resolving DATA sections...\n");
952 DEBUG printf("Resolving 6502 sections...\n");
953 ResolveFixups(M6502); // Fixup 6502 section (if any)
954 DEBUG printf("Resolving DSP56001 P: sections...\n");
955 ResolveFixups(M56001P); // Fixup 56001 P: section (if any)
956 DEBUG printf("Resolving DSP56001 X: sections...\n");
957 ResolveFixups(M56001X); // Fixup 56001 X: section (if any)
958 DEBUG printf("Resolving DSP56001 Y: sections...\n");
959 ResolveFixups(M56001Y); // Fixup 56001 Y: section (if any)