X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fld65%2Fxex.c;h=18190c063641e4f1c036afa69b8d942b69a625d8;hb=2aa5b4cafee3277580005cf6399a2ece8e06b405;hp=71065ea5755f26c742b63b2ac4f560f83cc7b521;hpb=8e3fe2ef86054aae8401616daffe5cc202b88839;p=cc65 diff --git a/src/ld65/xex.c b/src/ld65/xex.c index 71065ea57..18190c063 100644 --- a/src/ld65/xex.c +++ b/src/ld65/xex.c @@ -64,6 +64,10 @@ struct XexDesc { unsigned Undef; /* Count of undefined externals */ FILE* F; /* Output file */ const char* Filename; /* Name of output file */ + Import* RunAd; /* Run Address */ + unsigned long HeadPos; /* Position in the file of current header */ + unsigned long HeadEnd; /* End address of current header */ + unsigned long HeadSize; /* Last header size, can be removed if zero */ }; @@ -84,6 +88,10 @@ XexDesc* NewXexDesc (void) D->Undef = 0; D->F = 0; D->Filename = 0; + D->RunAd = 0; + D->HeadPos = 0; + D->HeadEnd = 0; + D->HeadSize = 0; /* Return the created struct */ return D; @@ -99,6 +107,14 @@ void FreeXexDesc (XexDesc* D) +void XexSetRunAd (XexDesc* D, Import *RunAd) +/* Set the RUNAD export */ +{ + D->RunAd = RunAd; +} + + + static unsigned XexWriteExpr (ExprNode* E, int Signed, unsigned Size, unsigned long Offs attribute ((unused)), void* Data) @@ -120,65 +136,81 @@ static void PrintNumVal (const char* Name, unsigned long V) -static void XexWriteMem (XexDesc* D, MemoryArea* M) -/* Write the segments of one memory area to a file */ +static void XexStartSegment (XexDesc *D, unsigned long Addr, unsigned long Size) { - unsigned I; - - /* Get the start address and size of this memory area */ - unsigned long Addr = M->Start; + /* Skip segment without size */ + if (!Size) + return; + + /* Store current position */ + unsigned long Pos = ftell (D->F); + unsigned long End = Addr + Size - 1; + + /* See if last header can be expanded into this one */ + if (D->HeadPos && ((D->HeadEnd + 1) == Addr)) { + /* Expand current header */ + D->HeadEnd = End; + D->HeadSize += Size; + fseek (D->F, D->HeadPos + 2, SEEK_SET); + Write16 (D->F, End); + /* Seek to old position */ + fseek (D->F, Pos, SEEK_SET); + } + else + { + if (D->HeadSize == 0) { + /* Last header had no data, replace */ + Pos = D->HeadPos; + fseek (D->F, Pos, SEEK_SET); + } - /* Walk over segments twice: first to get real area size, then to write - * all segments. */ - for (I = 0; I < CollCount (&M->SegList); ++I) { + /* If we are at start of file, write XEX heder */ + if (Pos == 0) + Write16 (D->F, 0xFFFF); - /* Get the segment */ - SegDesc* S = CollAtUnchecked (&M->SegList, I); + /* Writes a new segment header */ + D->HeadPos = ftell (D->F); + D->HeadEnd = End; + D->HeadSize = Size; + Write16 (D->F, Addr); + Write16 (D->F, End); + } +} - /* Keep the user happy */ - Print (stdout, 1, " Allocating `%s'\n", GetString (S->Name)); - /* If this is the run memory area, we must apply run alignment. If - ** this is not the run memory area but the load memory area (which - ** means that both are different), we must apply load alignment. - ** Beware: DoWrite may be true even if this is the run memory area, - ** because it may be also the load memory area. - */ - if (S->Run == M) { - /* Handle ALIGN and OFFSET/START */ - if (S->Flags & SF_ALIGN) { - /* Align the address */ - Addr = AlignAddr (Addr, S->RunAlignment); - } else if (S->Flags & (SF_OFFSET | SF_START)) { - Addr = S->Addr; - if (S->Flags & SF_OFFSET) { - /* It's an offset, not a fixed address, make an address */ - Addr += M->Start; - } - } +static void XexFakeSegment (XexDesc *D, unsigned long Addr) +{ + /* See if last header can be expanded into this one, we are done */ + if (D->HeadPos && ((D->HeadEnd + 1) == Addr)) + return; + + /* If we are at start of file, write XEX heder */ + if (ftell (D->F) == 0) + Write16 (D->F, 0xFFFF); + + /* Writes a new (invalid) segment header */ + D->HeadPos = ftell (D->F); + D->HeadEnd = Addr - 1; + D->HeadSize = 0; + Write16 (D->F, Addr); + Write16 (D->F, D->HeadEnd); +} - } else if (S->Load == M) { - /* Handle ALIGN_LOAD */ - if (S->Flags & SF_ALIGN_LOAD) { - /* Align the address */ - Addr = AlignAddr (Addr, S->LoadAlignment); - } - } +static void XexWriteMem (XexDesc* D, MemoryArea* M) +/* Write the segments of one memory area to a file */ +{ + unsigned I; - /* Calculate the new address */ - Addr += S->Seg->Size; - } + /* Always write a segment header for each memory area */ + D->HeadPos = 0; - /* Write header */ - Write16(D->F, 0xFFFF); - Write16(D->F, M->Start); - Write16(D->F, Addr-1); + /* Get the start address and size of this memory area */ + unsigned long Addr = M->Start; - /* Redo */ - Addr = M->Start; + /* Walk over all segments in this memory area */ for (I = 0; I < CollCount (&M->SegList); ++I) { int DoWrite; @@ -187,7 +219,7 @@ static void XexWriteMem (XexDesc* D, MemoryArea* M) SegDesc* S = CollAtUnchecked (&M->SegList, I); /* Keep the user happy */ - Print (stdout, 1, " Allocating `%s'\n", GetString (S->Name)); + Print (stdout, 1, " ATARI EXE Writing `%s'\n", GetString (S->Name)); /* Writes do only occur in the load area and not for BSS segments */ DoWrite = (S->Flags & SF_BSS) == 0 && /* No BSS segment */ @@ -207,6 +239,7 @@ static void XexWriteMem (XexDesc* D, MemoryArea* M) /* Align the address */ unsigned long NewAddr = AlignAddr (Addr, S->RunAlignment); if (DoWrite || (M->Flags & MF_FILL) != 0) { + XexStartSegment (D, Addr, NewAddr - Addr); WriteMult (D->F, M->FillVal, NewAddr - Addr); PrintNumVal ("SF_ALIGN", NewAddr - Addr); } @@ -218,10 +251,12 @@ static void XexWriteMem (XexDesc* D, MemoryArea* M) NewAddr += M->Start; } if (DoWrite || (M->Flags & MF_FILL) != 0) { - /* Seek in "overwrite" segments */ + /* "overwrite" segments are not supported */ if (S->Flags & SF_OVERWRITE) { - fseek (D->F, NewAddr - M->Start, SEEK_SET); + Error ("ATARI file format does not support overwrite for segment '%s'.", + GetString (S->Name)); } else { + XexStartSegment (D, Addr, NewAddr - Addr); WriteMult (D->F, M->FillVal, NewAddr-Addr); PrintNumVal ("SF_OFFSET", NewAddr - Addr); } @@ -236,6 +271,7 @@ static void XexWriteMem (XexDesc* D, MemoryArea* M) /* Align the address */ unsigned long NewAddr = AlignAddr (Addr, S->LoadAlignment); if (DoWrite || (M->Flags & MF_FILL) != 0) { + XexStartSegment (D, Addr, NewAddr - Addr); WriteMult (D->F, M->FillVal, NewAddr - Addr); PrintNumVal ("SF_ALIGN_LOAD", NewAddr - Addr); } @@ -248,10 +284,16 @@ static void XexWriteMem (XexDesc* D, MemoryArea* M) ** if the memory area is the load area. */ if (DoWrite) { + /* Start a segment with only one byte, will fix later */ + XexFakeSegment (D, Addr); unsigned long P = ftell (D->F); SegWrite (D->Filename, D->F, S->Seg, XexWriteExpr, D); - PrintNumVal ("Wrote", (unsigned long) (ftell (D->F) - P)); + unsigned long Size = ftell (D->F) - P; + /* Fix segment size */ + XexStartSegment (D, Addr, Size); + PrintNumVal ("Wrote", Size); } else if (M->Flags & MF_FILL) { + XexStartSegment (D, Addr, S->Seg->Size); WriteMult (D->F, S->Seg->FillVal, S->Seg->Size); PrintNumVal ("Filled", (unsigned long) S->Seg->Size); } @@ -270,9 +312,15 @@ static void XexWriteMem (XexDesc* D, MemoryArea* M) unsigned long ToFill = M->Size - M->FillLevel; Print (stdout, 2, " Filling 0x%lx bytes with 0x%02x\n", ToFill, M->FillVal); + XexStartSegment (D, Addr, ToFill); WriteMult (D->F, M->FillVal, ToFill); M->FillLevel = M->Size; } + + /* If the last segment is empty, remove */ + if (D->HeadSize == 0 && D->HeadPos) { + fseek (D->F, D->HeadPos, SEEK_SET); + } } @@ -321,10 +369,17 @@ void XexWriteTarget (XexDesc* D, struct File* F) for (I = 0; I < CollCount (&F->MemoryAreas); ++I) { /* Get this entry */ MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I); - Print (stdout, 1, " XEX Dumping `%s'\n", GetString (M->Name)); + Print (stdout, 1, " ATARI EXE Dumping `%s'\n", GetString (M->Name)); XexWriteMem (D, M); } + /* Write RUNAD at file end */ + if (D->RunAd) { + Write16 (D->F, 0x2E0); + Write16 (D->F, 0x2E1); + Write16 (D->F, GetExportVal (D->RunAd->Exp)); + } + /* Close the file */ if (fclose (D->F) != 0) { Error ("Cannot write to `%s': %s", D->Filename, strerror (errno));