]> Shamusworld >> Repos - rmac/blobdiff - sect.c
Version bump for last commit. :-)
[rmac] / sect.c
diff --git a/sect.c b/sect.c
index 49806a02b9d4e8d7570b19b5d27f35ca89b6dc86..12f9c89eae2f7eda973f63765816c21bb87f3c57 100644 (file)
--- a/sect.c
+++ b/sect.c
@@ -1,7 +1,7 @@
 //
-// RMAC - Reboot's Macro Assembler for all Atari computers
+// RMAC - Renamed Macro Assembler for all Atari computers
 // SECT.C - Code Generation, Fixups and Section Management
-// Copyright (C) 199x Landon Dyer, 2011-2019 Reboot and Friends
+// Copyright (C) 199x Landon Dyer, 2011-2021 Reboot and Friends
 // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
 // Source utilised with the kind permission of Landon Dyer
 //
@@ -18,7 +18,8 @@
 #include "riscasm.h"
 #include "symbol.h"
 #include "token.h"
-
+#define DEF_REGRISC
+#include "riscregs.h"
 
 // Function prototypes
 void MakeSection(int, uint16_t);
@@ -159,7 +160,7 @@ void SaveSection(void)
        sp->sloc = sloc;
        sp->orgaddr = orgaddr;
 
-       if (scode != NULL)                              // Bailout code chunk
+       if (scode != NULL)                              // Bailout code chunk (if any)
                scode->ch_size = ch_size;
 }
 
@@ -218,29 +219,43 @@ void chcheck(uint32_t amt)
        DEBUG { printf("    amt (adjusted)=%u\n", amt); }
        SECT * p = &sect[cursect];
        CHUNK * cp = malloc(sizeof(CHUNK) + amt);
+       int first = 0;
 
-       // First chunk in section
        if (scode == NULL)
        {
+               // First chunk in section
                cp->chprev = NULL;
                p->sfcode = cp;
+               first = 1;
        }
-       // Add chunk to other chunks
        else
        {
+               // Add second and on to previous chunk
                cp->chprev = scode;
                scode->chnext = cp;
                scode->ch_size = ch_size;       // Save old chunk's globals
        }
 
        // Setup chunk and global vars
-       cp->chloc = sloc;
+/*
+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.
+*/
+       cp->chloc = sloc; // <-- HERE'S THE PROBLEM FOR 56K  :-/
        cp->chnext = NULL;
        challoc = cp->challoc = amt;
        ch_size = cp->ch_size = 0;
        chptr = cp->chptr = ((uint8_t *)cp) + sizeof(CHUNK);
        scode = p->scode = cp;
 
+       // A quick kludge
+/*
+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
+
+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.
+*/
+       if (((cursect == M56001P) || (cursect == M56001X) || (cursect == M56001Y)) && !first)
+               cp->chloc = cp->chprev->chloc + cp->chprev->ch_size;
+
        return;
 }
 
@@ -276,11 +291,11 @@ int AddFixup(uint32_t attr, uint32_t loc, TOKEN * fexpr)
        {
                attr |= FU_56001;
                // Save the exact spot in this chunk where the fixup should go
-               _orgaddr = chptr - scode->chptr;
+               _orgaddr = chptr - scode->chptr + scode->chloc;
        }
 
        // Allocate space for the fixup + any expression
-       FIXUP * fixup = malloc(sizeof(FIXUP) + (sizeof(TOKEN) * exprlen));
+       FIXUP * fixup = malloc(sizeof(FIXUP) + (sizeof(TOKEN) * exprlen)*2);
 
        // Store the relevant fixup information in the FIXUP
        fixup->next = NULL;
@@ -295,8 +310,53 @@ int AddFixup(uint32_t attr, uint32_t loc, TOKEN * fexpr)
        // Copy the passed in expression to the FIXUP, if any
        if (exprlen > 0)
        {
+               // Here we used to to a plain memcpy and punt on trying to evaluate the expression by then.
+               // As discussed in bug #176, this can lead to robustness issues because some symbols might
+               // have changed by the time we perform the relocations (think of a symbol that's SET multiple
+               // times). So instead we perform a symbol-by-symbol copy and check to see if there are any
+               // resolved symbols that can be evaluated immediately. Those, we replace with constants.
+               // Also of note: because "fixup" can be larger than what ExpressionLength() returns
+               // (due to constants taking up more space than symbols), we allocate twice as RAM as we should
+               // without expansions just to be on the safe side. The "correct" thing to do would be to
+               // modify ExpressionLength() to cater for defined symbols and return the exact amount of items.
+
                fixup->expr = (TOKEN *)((uint8_t *)fixup + sizeof(FIXUP));
-               memcpy(fixup->expr, fexpr, sizeof(TOKEN) * exprlen);
+               int i;
+               PTR dstexpr;
+               dstexpr.u32 = fixup->expr;
+               SYM *sy;
+               for (i = 0; i < exprlen; i++)
+               {
+                       if (*fexpr == SYMBOL)
+                       {
+                               sy = symbolPtr[fexpr[1]];
+                               if (sy->sattr & DEFINED && !(sy->sattr & (TDB| M56KPXYL|M6502)))
+                               {
+                                       // Only convert symbols that are defined and are absolute
+                                       *dstexpr.u32++ = CONST;
+                                       *dstexpr.u64++ = sy->svalue;
+                                       fexpr += 2;
+                                       i++;
+                               }
+                               else
+                               {
+                                       // Just copy the symbol
+                                       *dstexpr.u32++ = *fexpr++;
+                                       *dstexpr.u32++ = *fexpr++;
+                                       i++;
+                               }
+                       }
+                       else if (*fexpr == CONST || *fexpr == FCONST)
+                       {
+                               // Copy constants
+                               *dstexpr.u32++ = *fexpr++;
+                               *dstexpr.u32++ = *fexpr++;
+                               *dstexpr.u32++ = *fexpr++;
+                               i += 2;
+                       }
+                       else
+                               *dstexpr.u32++ = *fexpr++;
+               }
        }
 
        // Finally, put the FIXUP in the current section's linked list
@@ -398,8 +458,24 @@ int ResolveFixups(int sno)
                // Complex expression
                if (dw & FU_EXPR)
                {
+                       // evexpr presumably issues the errors/warnings here
                        if (evexpr(fup->expr, &eval, &eattr, &esym) != OK)
                                continue;
+
+                       if (esym)
+                               if (!(esym->sattr & DEFINED) && eval==0)
+                               {
+                                       // If our expression still has an undefined symbol at this stage, it's bad news.
+                                       // The linker is never going to resolve the expression, so that's an error.
+                                       error("cannot export complex expression with unresloved symbol '%s'", esym->sname);
+                                       continue;
+                               }
+
+                       if ((CHECK_OPTS(OPT_PC_RELATIVE)) && (eattr & (DEFINED | REFERENCED | EQUATED)) == (DEFINED | REFERENCED))
+                       {
+                               error("relocation not allowed when o30 is enabled");
+                               continue;
+                       }
                }
                // Simple symbol
                else
@@ -407,6 +483,12 @@ int ResolveFixups(int sno)
                        SYM * sy = fup->symbol;
                        eattr = sy->sattr;
 
+                       if ((CHECK_OPTS(OPT_PC_RELATIVE)) && (eattr & (DEFINED | REFERENCED | EQUATED)) == (DEFINED | REFERENCED))
+                       {
+                               error("relocation not allowed when o30 is enabled");
+                               continue;
+                       }
+
                        if (eattr & DEFINED)
                                eval = sy->svalue;
                        else
@@ -415,6 +497,8 @@ int ResolveFixups(int sno)
                        // If the symbol is not defined, but global, set esym to sy
                        if ((eattr & (GLOBAL | DEFINED)) == GLOBAL)
                                esym = sy;
+
+                       DEBUG { printf("               name: %s, value: $%" PRIX64 "\n", sy->sname, sy->svalue); }
                }
 
                uint16_t tdb = eattr & TDB;
@@ -433,21 +517,24 @@ int ResolveFixups(int sno)
                // from the location (that will happen in the linker when the external
                // reference is resolved).
                //
-               // MWC expects PC-relative things to have the LOC subtracted from the
-               // value, if the value is external (that is, undefined at this point).
-               //
                // PC-relative fixups must be DEFINED and either in the same section
                // (whereupon the subtraction takes place) or ABS (with no subtract).
-               if (dw & FU_PCREL)
+               if ((dw & FU_PCREL) || (dw & FU_PCRELX))
                {
                        if (eattr & DEFINED)
                        {
                                if (tdb == sno)
+                               {
                                        eval -= loc;
+
+                                       // In this instruction the PC is located a DWORD away
+                                       if (dw & FU_PCRELX)
+                                               eval += 2;
+                               }
                                else if (tdb)
                                {
                                        // Allow cross-section PCREL fixups in Alcyon mode
-                                       if (prg_flag)
+                                       if (prg_flag || (obj_format == RAW))
                                        {
                                                switch (tdb)
                                                {
@@ -466,6 +553,13 @@ int ResolveFixups(int sno)
                                                }
 
                                                eval -= loc;
+
+                                               // In this instruction the PC is located a DWORD away
+                                               if (dw & FU_PCRELX)
+                                                       eval += 2;
+                                               
+                                               if ((int64_t)eval > 0x7fff || (int64_t)eval < -32768)
+                                                       error(range_error);
                                        }
                                        else
                                        {
@@ -474,12 +568,11 @@ int ResolveFixups(int sno)
                                        }
                                }
 
-                               if (sbra_flag && (dw & FU_LBRA) && (eval + 0x80 < 0x100))
+                               if (optim_warn_flag && (dw & FU_LBRA) && (eval + 0x80 < 0x100))
                                        warn("unoptimized short branch");
                        }
-                       else if (obj_format == MWC)
-                               eval -= loc;
 
+                       // Be sure to clear any TDB flags, since we handled it just now
                        tdb = 0;
                        eattr &= ~TDB;
                }
@@ -502,11 +595,15 @@ int ResolveFixups(int sno)
 
                        if (eval == 0)
                        {
-                               if (CHECK_OPTS(OPT_NULL_BRA))
+                               if (*locp) // optim_flags[OPT_NULL_BRA] is stored there, check the comment in mach.s under m_br
                                {
                                        // Just output a NOP
                                        *locp++ = 0x4E;
                                        *locp = 0x71;
+
+                                       if (optim_warn_flag)
+                                               warn("o6: bra.s with zero offset converted to NOP");
+
                                        continue;
                                }
                                else
@@ -636,6 +733,7 @@ int ResolveFixups(int sno)
                        }
                        else if ((dw & FUMASKRISC) == FU_REGONE)
                        {
+                               eval -= REGRISC_R0;
                                if (eval > 31)
                                {
                                        error("register one value out of range");
@@ -649,6 +747,7 @@ int ResolveFixups(int sno)
                        }
                        else if ((dw & FUMASKRISC) == FU_REGTWO)
                        {
+                               eval -= REGRISC_R0;
                                if (eval > 31)
                                {
                                        error("register two value out of range");
@@ -861,8 +960,8 @@ int ResolveFixups(int sno)
                                locp[1] = (uint8_t)eval;
                                break;
 
-                       // This is a 6 bit absoulte short address. It occupies
-                       // the low 6 bits of the middle byte of a DSP word.
+                       // This is a 6 bit absoulte short address. It occupies the low 6
+                       // bits of the middle byte of a DSP word.
                        case FU_DSPADR06:
                                if (eval > 63)
                                {
@@ -873,8 +972,8 @@ int ResolveFixups(int sno)
                                locp[1] |= eval;
                                break;
 
-                       // This is a 6 bit absoulte short address. It occupies
-                       // the low 6 bits of the middle byte of a DSP word.
+                       // This is a 6 bit absoulte short address. It occupies the low 6
+                       // bits of the middle byte of a DSP word.
                        case FU_DSPPP06:
                                if (eval < 0xFFFFFFC0)
                                {