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 */
};
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;
+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)
-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;
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 */
/* 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);
}
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);
}
/* 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);
}
** 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);
}
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);
+ }
}
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));