Merge pull request #740 from laubzega/master

Add segment type "overwrite".
This commit is contained in:
greg-king5 2018-09-14 00:59:47 -04:00 committed by GitHub
commit d8ad89c4b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 118 additions and 25 deletions

View File

@ -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/
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
specify a segment attribute. There are four possible segment attributes:
specify a segment attribute. There are five possible segment attributes:
<tscreen><verb>
ro means readonly
rw means read/write
bss means that this is an uninitialized segment
zp a zeropage segment
ro means readonly
rw means read/write
bss means that this is an uninitialized segment
zp a zeropage segment
overwrite a segment that overwrites (parts of) another one
</verb></tscreen>
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
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>
Let us look now at a more complex example. Say, you've successfully tested

View File

@ -193,8 +193,13 @@ static void BinWriteMem (BinDesc* D, MemoryArea* M)
NewAddr += M->Start;
}
if (DoWrite || (M->Flags & MF_FILL) != 0) {
WriteMult (D->F, M->FillVal, NewAddr-Addr);
PrintNumVal ("SF_OFFSET", NewAddr - Addr);
/* Seek in "overwrite" segments */
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;
}

View File

@ -653,6 +653,7 @@ static void ParseSegments (void)
{ "RW", CFGTOK_RW },
{ "BSS", CFGTOK_BSS },
{ "ZP", CFGTOK_ZP },
{ "OVERWRITE", CFGTOK_OVERWRITE },
};
unsigned Count;
@ -753,11 +754,12 @@ static void ParseSegments (void)
FlagAttr (&S->Attr, SA_TYPE, "TYPE");
CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
switch (CfgTok) {
case CFGTOK_RO: S->Flags |= SF_RO; break;
case CFGTOK_RW: /* Default */ break;
case CFGTOK_BSS: S->Flags |= SF_BSS; break;
case CFGTOK_ZP: S->Flags |= (SF_BSS | SF_ZP); break;
default: Internal ("Unexpected token: %d", CfgTok);
case CFGTOK_RO: S->Flags |= SF_RO; break;
case CFGTOK_RW: /* Default */ break;
case CFGTOK_BSS: S->Flags |= SF_BSS; break;
case CFGTOK_ZP: S->Flags |= (SF_BSS | SF_ZP); break;
case CFGTOK_OVERWRITE: S->Flags |= (SF_OVERWRITE | SF_RO); break;
default: Internal ("Unexpected token: %d", CfgTok);
}
CfgNextTok ();
break;
@ -1795,6 +1797,7 @@ unsigned CfgProcess (void)
for (I = 0; I < CollCount (&MemoryAreas); ++I) {
unsigned J;
unsigned long Addr;
unsigned Overwrites = 0;
/* Get the next memory area */
MemoryArea* M = CollAtUnchecked (&MemoryAreas, I);
@ -1848,6 +1851,27 @@ unsigned CfgProcess (void)
/* Remember the start address before handling this segment */
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
** area.
*/
@ -1896,22 +1920,33 @@ unsigned CfgProcess (void)
/* An offset was given, no address, make an address */
NewAddr += M->Start;
}
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');
if (S->Flags & SF_OVERWRITE) {
if (NewAddr < M->Start) {
CfgError (GetSourcePos (S->LI),
"Segment `%s' begins before memory area `%s'",
GetString (S->Name), GetString (M->Name));
} 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');
Addr = NewAddr;
}
} 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;
}
}
}

View File

@ -96,6 +96,7 @@ struct SegDesc {
#define SF_RUN_DEF 0x0200 /* RUN symbols already defined */
#define SF_LOAD_DEF 0x0400 /* LOAD symbols already defined */
#define SF_FILLVAL 0x0800 /* Segment has separate fill value */
#define SF_OVERWRITE 0x1000 /* Segment can overwrite (part of) another one */

View File

@ -105,6 +105,7 @@ typedef enum {
CFGTOK_RW,
CFGTOK_BSS,
CFGTOK_ZP,
CFGTOK_OVERWRITE,
CFGTOK_O65,
CFGTOK_BIN,