Merge pull request #740 from laubzega/master
Add segment type "overwrite".
This commit is contained in:
@@ -512,13 +512,15 @@ What we are doing here is telling the linker, that all segments go into the
|
|||||||
the linker will first write the <tt/CODE/ segment, then the <tt/RODATA/
|
the linker will first write the <tt/CODE/ segment, then the <tt/RODATA/
|
||||||
segment, then the <tt/DATA/ segment - but it will not write the <tt/BSS/
|
segment, then the <tt/DATA/ segment - but it will not write the <tt/BSS/
|
||||||
segment. Why? Here enters the segment type: For each segment specified, you may also
|
segment. Why? Here enters the segment type: For each segment specified, you may also
|
||||||
specify a segment attribute. There are four possible segment attributes:
|
specify a segment attribute. There are five possible segment attributes:
|
||||||
|
|
||||||
<tscreen><verb>
|
<tscreen><verb>
|
||||||
ro means readonly
|
ro means readonly
|
||||||
rw means read/write
|
rw means read/write
|
||||||
bss means that this is an uninitialized segment
|
bss means that this is an uninitialized segment
|
||||||
zp a zeropage segment
|
zp a zeropage segment
|
||||||
|
overwrite a segment that overwrites (parts of) another one
|
||||||
|
|
||||||
</verb></tscreen>
|
</verb></tscreen>
|
||||||
|
|
||||||
So, because we specified that the segment with the name BSS is of type bss,
|
So, because we specified that the segment with the name BSS is of type bss,
|
||||||
@@ -618,6 +620,55 @@ the command line, with "-1.bin" and "-2.bin" appended respectively. Because
|
|||||||
'%' is used as an escape char, the sequence "%%" has to be used if a single
|
'%' is used as an escape char, the sequence "%%" has to be used if a single
|
||||||
percent sign is required.
|
percent sign is required.
|
||||||
|
|
||||||
|
<sect1>OVERWRITE segments<p>
|
||||||
|
|
||||||
|
There are situations when you may wish to overwrite some part (or parts) of a
|
||||||
|
segment with another one. Perhaps you are modifying an OS ROM that has its
|
||||||
|
public subroutines at fixed, well-known addresses, and you want to prevent them
|
||||||
|
from shifting to other locations in memory if your changed code takes less
|
||||||
|
space. Or you are updating a block of code available in binary-only form with
|
||||||
|
fixes that are scattered in various places. Generally, whenever you want to
|
||||||
|
minimize disturbance to an existing code brought on by your updates, OVERWRITE
|
||||||
|
segments are worth considering.
|
||||||
|
|
||||||
|
Here is an example:
|
||||||
|
|
||||||
|
<tscreen><verb>
|
||||||
|
MEMORY {
|
||||||
|
RAM: file = "", start = $6000, size = $2000, type=rw;
|
||||||
|
ROM: file = %O, start = $8000, size = $8000, type=ro;
|
||||||
|
}
|
||||||
|
</verb></tscreen>
|
||||||
|
|
||||||
|
Nothing unusual so far, just two memory blocks - one RAM, one ROM. Now let's
|
||||||
|
look at the segment configuration:
|
||||||
|
|
||||||
|
<tscreen><verb>
|
||||||
|
SEGMENTS {
|
||||||
|
RAM: load = RAM, type = bss;
|
||||||
|
ORIGINAL: load = ROM, type = ro;
|
||||||
|
FASTCOPY: load = ROM, start=$9000, type = overwrite;
|
||||||
|
JMPPATCH1: load = ROM, start=$f7e8, type = overwrite;
|
||||||
|
DEBUG: load = ROM, start=$8000, type = overwrite;
|
||||||
|
VERSION: load = ROM, start=$e5b7, type = overwrite;
|
||||||
|
}
|
||||||
|
</verb></tscreen>
|
||||||
|
|
||||||
|
Segment named ORIGINAL contains the original code, disassembled or provided in
|
||||||
|
a binary form (i.e. using <tt/.INCBIN/ directive; see the <tt/ca65/ assembler
|
||||||
|
document). Subsequent four segments will be relocated to addresses specified
|
||||||
|
by their "start" attributes ("offset" can also be used) and then will overwrite
|
||||||
|
whatever was at these locations in the ORIGINAL segment. In the end, resulting
|
||||||
|
binary output file will thus contain original data with the exception of four
|
||||||
|
sequences starting at $9000, $f7e8, $8000 and $e5b7, which will sport code from
|
||||||
|
their respective segments. How long these sequences will be depends on the
|
||||||
|
lengths of corresponding segments - they can even overlap, so think what you're
|
||||||
|
doing.
|
||||||
|
|
||||||
|
Finally, note that OVERWRITE segments should be the final segments loaded to a
|
||||||
|
particular memory area, and that they need at least one of "start" or "offset"
|
||||||
|
attributes specified.
|
||||||
|
|
||||||
<sect1>LOAD and RUN addresses (ROMable code)<p>
|
<sect1>LOAD and RUN addresses (ROMable code)<p>
|
||||||
|
|
||||||
Let us look now at a more complex example. Say, you've successfully tested
|
Let us look now at a more complex example. Say, you've successfully tested
|
||||||
|
|||||||
@@ -193,8 +193,13 @@ static void BinWriteMem (BinDesc* D, MemoryArea* M)
|
|||||||
NewAddr += M->Start;
|
NewAddr += M->Start;
|
||||||
}
|
}
|
||||||
if (DoWrite || (M->Flags & MF_FILL) != 0) {
|
if (DoWrite || (M->Flags & MF_FILL) != 0) {
|
||||||
WriteMult (D->F, M->FillVal, NewAddr-Addr);
|
/* Seek in "overwrite" segments */
|
||||||
PrintNumVal ("SF_OFFSET", NewAddr - Addr);
|
if (S->Flags & SF_OVERWRITE) {
|
||||||
|
fseek (D->F, NewAddr - M->Start, SEEK_SET);
|
||||||
|
} else {
|
||||||
|
WriteMult (D->F, M->FillVal, NewAddr-Addr);
|
||||||
|
PrintNumVal ("SF_OFFSET", NewAddr - Addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Addr = NewAddr;
|
Addr = NewAddr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -653,6 +653,7 @@ static void ParseSegments (void)
|
|||||||
{ "RW", CFGTOK_RW },
|
{ "RW", CFGTOK_RW },
|
||||||
{ "BSS", CFGTOK_BSS },
|
{ "BSS", CFGTOK_BSS },
|
||||||
{ "ZP", CFGTOK_ZP },
|
{ "ZP", CFGTOK_ZP },
|
||||||
|
{ "OVERWRITE", CFGTOK_OVERWRITE },
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned Count;
|
unsigned Count;
|
||||||
@@ -753,11 +754,12 @@ static void ParseSegments (void)
|
|||||||
FlagAttr (&S->Attr, SA_TYPE, "TYPE");
|
FlagAttr (&S->Attr, SA_TYPE, "TYPE");
|
||||||
CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
|
CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
|
||||||
switch (CfgTok) {
|
switch (CfgTok) {
|
||||||
case CFGTOK_RO: S->Flags |= SF_RO; break;
|
case CFGTOK_RO: S->Flags |= SF_RO; break;
|
||||||
case CFGTOK_RW: /* Default */ break;
|
case CFGTOK_RW: /* Default */ break;
|
||||||
case CFGTOK_BSS: S->Flags |= SF_BSS; break;
|
case CFGTOK_BSS: S->Flags |= SF_BSS; break;
|
||||||
case CFGTOK_ZP: S->Flags |= (SF_BSS | SF_ZP); break;
|
case CFGTOK_ZP: S->Flags |= (SF_BSS | SF_ZP); break;
|
||||||
default: Internal ("Unexpected token: %d", CfgTok);
|
case CFGTOK_OVERWRITE: S->Flags |= (SF_OVERWRITE | SF_RO); break;
|
||||||
|
default: Internal ("Unexpected token: %d", CfgTok);
|
||||||
}
|
}
|
||||||
CfgNextTok ();
|
CfgNextTok ();
|
||||||
break;
|
break;
|
||||||
@@ -1795,6 +1797,7 @@ unsigned CfgProcess (void)
|
|||||||
for (I = 0; I < CollCount (&MemoryAreas); ++I) {
|
for (I = 0; I < CollCount (&MemoryAreas); ++I) {
|
||||||
unsigned J;
|
unsigned J;
|
||||||
unsigned long Addr;
|
unsigned long Addr;
|
||||||
|
unsigned Overwrites = 0;
|
||||||
|
|
||||||
/* Get the next memory area */
|
/* Get the next memory area */
|
||||||
MemoryArea* M = CollAtUnchecked (&MemoryAreas, I);
|
MemoryArea* M = CollAtUnchecked (&MemoryAreas, I);
|
||||||
@@ -1848,6 +1851,27 @@ unsigned CfgProcess (void)
|
|||||||
/* Remember the start address before handling this segment */
|
/* Remember the start address before handling this segment */
|
||||||
unsigned long StartAddr = Addr;
|
unsigned long StartAddr = Addr;
|
||||||
|
|
||||||
|
/* Take note of "overwrite" segments and make sure there are no
|
||||||
|
** other segment types following them in current memory region.
|
||||||
|
*/
|
||||||
|
if (S->Flags & SF_OVERWRITE) {
|
||||||
|
if (S->Flags & (SF_OFFSET | SF_START)) {
|
||||||
|
++Overwrites;
|
||||||
|
} else {
|
||||||
|
CfgError (GetSourcePos (M->LI),
|
||||||
|
"Segment `%s' of type `overwrite' requires either"
|
||||||
|
" `Start' or `Offset' attribute to be specified",
|
||||||
|
GetString (S->Name));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (Overwrites > 0) {
|
||||||
|
CfgError (GetSourcePos (M->LI),
|
||||||
|
"Segment `%s' is preceded by at least one segment"
|
||||||
|
" of type `overwrite'",
|
||||||
|
GetString (S->Name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Some actions depend on whether this is the load or run memory
|
/* Some actions depend on whether this is the load or run memory
|
||||||
** area.
|
** area.
|
||||||
*/
|
*/
|
||||||
@@ -1896,22 +1920,33 @@ unsigned CfgProcess (void)
|
|||||||
/* An offset was given, no address, make an address */
|
/* An offset was given, no address, make an address */
|
||||||
NewAddr += M->Start;
|
NewAddr += M->Start;
|
||||||
}
|
}
|
||||||
if (NewAddr < Addr) {
|
|
||||||
/* Offset already too large */
|
if (S->Flags & SF_OVERWRITE) {
|
||||||
++Overflows;
|
if (NewAddr < M->Start) {
|
||||||
if (S->Flags & SF_OFFSET) {
|
CfgError (GetSourcePos (S->LI),
|
||||||
CfgWarning (GetSourcePos (S->LI),
|
"Segment `%s' begins before memory area `%s'",
|
||||||
"Segment `%s' offset is too small in `%s' by %lu byte%c",
|
GetString (S->Name), GetString (M->Name));
|
||||||
GetString (S->Name), GetString (M->Name),
|
|
||||||
Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's');
|
|
||||||
} else {
|
} else {
|
||||||
CfgWarning (GetSourcePos (S->LI),
|
Addr = NewAddr;
|
||||||
"Segment `%s' start address is too low in `%s' by %lu byte%c",
|
|
||||||
GetString (S->Name), GetString (M->Name),
|
|
||||||
Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's');
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Addr = NewAddr;
|
if (NewAddr < Addr) {
|
||||||
|
/* Offset already too large */
|
||||||
|
++Overflows;
|
||||||
|
if (S->Flags & SF_OFFSET) {
|
||||||
|
CfgWarning (GetSourcePos (S->LI),
|
||||||
|
"Segment `%s' offset is too small in `%s' by %lu byte%c",
|
||||||
|
GetString (S->Name), GetString (M->Name),
|
||||||
|
Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's');
|
||||||
|
} else {
|
||||||
|
CfgWarning (GetSourcePos (S->LI),
|
||||||
|
"Segment `%s' start address is too low in `%s' by %lu byte%c",
|
||||||
|
GetString (S->Name), GetString (M->Name),
|
||||||
|
Addr - NewAddr, (Addr - NewAddr == 1) ? ' ' : 's');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Addr = NewAddr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ struct SegDesc {
|
|||||||
#define SF_RUN_DEF 0x0200 /* RUN symbols already defined */
|
#define SF_RUN_DEF 0x0200 /* RUN symbols already defined */
|
||||||
#define SF_LOAD_DEF 0x0400 /* LOAD symbols already defined */
|
#define SF_LOAD_DEF 0x0400 /* LOAD symbols already defined */
|
||||||
#define SF_FILLVAL 0x0800 /* Segment has separate fill value */
|
#define SF_FILLVAL 0x0800 /* Segment has separate fill value */
|
||||||
|
#define SF_OVERWRITE 0x1000 /* Segment can overwrite (part of) another one */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ typedef enum {
|
|||||||
CFGTOK_RW,
|
CFGTOK_RW,
|
||||||
CFGTOK_BSS,
|
CFGTOK_BSS,
|
||||||
CFGTOK_ZP,
|
CFGTOK_ZP,
|
||||||
|
CFGTOK_OVERWRITE,
|
||||||
|
|
||||||
CFGTOK_O65,
|
CFGTOK_O65,
|
||||||
CFGTOK_BIN,
|
CFGTOK_BIN,
|
||||||
|
|||||||
Reference in New Issue
Block a user