1
0
mirror of https://github.com/cc65/cc65.git synced 2024-12-27 15:29:46 +00:00

Merge remote-tracking branch 'dmsc/xex-c' into upstream-master

This commit is contained in:
Christian Groessler 2019-02-23 01:14:54 +01:00
commit de519b969a
15 changed files with 323 additions and 42 deletions

24
cfg/atari-asm-xex.cfg Normal file
View File

@ -0,0 +1,24 @@
FEATURES {
STARTADDRESS: default = $2E00;
}
SYMBOLS {
__STARTADDRESS__: type = export, value = %S;
}
MEMORY {
ZP: file = "", define = yes, start = $0082, size = $007E;
MAIN: file = %O, define = yes, start = %S, size = $BC20 - %S;
}
FILES {
%O: format = atari;
}
FORMATS {
atari: runad = start;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp, optional = yes;
EXTZP: load = ZP, type = zp, optional = yes; # to enable modules to be able to link to C and assembler programs
CODE: load = MAIN, type = rw, define = yes;
RODATA: load = MAIN, type = ro optional = yes;
DATA: load = MAIN, type = rw optional = yes;
BSS: load = MAIN, type = bss, optional = yes, define = yes;
}

View File

@ -1,24 +1,56 @@
# Sample linker configuration for C programs using the Atari binary file support.
# Use with: cl65 -tatari -Catari-c-xex.cfg prog.c -o prog.xex
FEATURES {
STARTADDRESS: default = $2E00;
STARTADDRESS: default = $2000;
}
SYMBOLS {
__STARTADDRESS__: type = export, value = %S;
__SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk
__STACKSIZE__: type = weak, value = $0800; # 2k stack
__STARTADDRESS__: type = export, value = %S;
__RESERVED_MEMORY__: type = weak, value = $0000;
__SYSCHKHDR__: type = export, value = 0; # Disable system check header
__SYSCHKTRL__: type = export, value = 0; # Disable system check trailer
}
MEMORY {
ZP: file = "", define = yes, start = $0082, size = $007E;
MAIN: file = %O, define = yes, start = %S, size = $BC20 - %S;
ZP: file = "", define = yes, start = $0082, size = $007E;
# "system check" load chunk
SYSCHKCHNK: file = %O, start = $2E00, size = $0300;
# "main program" load chunk
MAIN: file = %O, define = yes, start = %S, size = $BC20 - __STACKSIZE__ - __RESERVED_MEMORY__ - %S;
}
FILES {
%O: format = atari;
}
FORMATS {
atari: runad = start;
atari: runad = start,
initad = SYSCHKCHNK: __SYSTEM_CHECK__;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp, optional = yes;
EXTZP: load = ZP, type = zp, optional = yes; # to enable modules to be able to link to C and assembler programs
CODE: load = MAIN, type = rw, define = yes;
RODATA: load = MAIN, type = ro optional = yes;
DATA: load = MAIN, type = rw optional = yes;
BSS: load = MAIN, type = bss, optional = yes, define = yes;
ZEROPAGE: load = ZP, type = zp;
EXTZP: load = ZP, type = zp, optional = yes;
SYSCHK: load = SYSCHKCHNK, type = rw, define = yes, optional = yes;
STARTUP: load = MAIN, type = ro, define = yes;
LOWBSS: load = MAIN, type = rw, optional = yes; # not zero initialized
LOWCODE: load = MAIN, type = ro, define = yes, optional = yes;
ONCE: load = MAIN, type = ro, optional = yes;
CODE: load = MAIN, type = ro, define = yes;
RODATA: load = MAIN, type = ro;
DATA: load = MAIN, type = rw;
INIT: load = MAIN, type = rw, optional = yes;
BSS: load = MAIN, type = bss, define = yes;
}
FEATURES {
CONDES: type = constructor,
label = __CONSTRUCTOR_TABLE__,
count = __CONSTRUCTOR_COUNT__,
segment = ONCE;
CONDES: type = destructor,
label = __DESTRUCTOR_TABLE__,
count = __DESTRUCTOR_COUNT__,
segment = RODATA;
CONDES: type = interruptor,
label = __INTERRUPTOR_TABLE__,
count = __INTERRUPTOR_COUNT__,
segment = RODATA,
import = __CALLIRQ__;
}

79
cfg/atarixl-xex.cfg Normal file
View File

@ -0,0 +1,79 @@
# Sample linker configuration for C programs using the Atari binary file support.
# Use with: cl65 -tatarixl -Catarixl-c-xex.cfg prog.c -o prog.xex
FEATURES {
STARTADDRESS: default = $2400;
}
SYMBOLS {
__SYSTEM_CHECK__: type = import; # force inclusion of "system check" load chunk
__STACKSIZE__: type = weak, value = $0800; # 2k stack
__STARTADDRESS__: type = export, value = %S;
__SYSCHKHDR__: type = export, value = 0; # Disable system check header
__SYSCHKTRL__: type = export, value = 0; # Disable system check trailer
}
MEMORY {
ZP: file = "", define = yes, start = $0082, size = $007E;
# "system check" load chunk
SYSCHKCHNK: file = %O, start = $2E00, size = $0300;
# "shadow RAM preparation" load chunk
SRPREPCHNK: file = %O, define = yes, start = %S, size = $7C20 - %S - $07FF; # $07FF: space for temp. chargen buffer, 1K aligned
# "main program" load chunk
MAIN: file = %O, define = yes, start = %S + __LOWBSS_SIZE__, size = $D000 - __STACKSIZE__ - %S - __LOWBSS_SIZE__;
# memory beneath the ROM preceeding the character generator
HIDDEN_RAM2: file = "", define = yes, start = $D800, size = $0800;
# address of relocated character generator (same addess as ROM version)
CHARGEN: file = "", define = yes, start = $E000, size = $0400;
# memory beneath the ROM
HIDDEN_RAM: file = "", define = yes, start = $E400, size = $FFFA - $E400;
# UNUSED - hide
UNUSED: file = "", start = $0, size = $10;
}
FILES {
%O: format = atari;
}
FORMATS {
atari: runad = start,
initad = SYSCHKCHNK: __SYSTEM_CHECK__,
initad = SRPREPCHNK: sramprep;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp;
EXTZP: load = ZP, type = zp, optional = yes;
SYSCHK: load = SYSCHKCHNK, type = rw, define = yes, optional = yes;
LOWBSS: load = SRPREPCHNK, type = bss, define = yes; # shared btw. SRPREPCHNK and RAM, not zero initialized
SRPREP: load = SRPREPCHNK, type = rw, define = yes;
SHADOW_RAM: load = SRPREPCHNK, run = HIDDEN_RAM, type = rw, define = yes, optional = yes;
SHADOW_RAM2: load = SRPREPCHNK, run = HIDDEN_RAM2, type = rw, define = yes, optional = yes;
STARTUP: load = MAIN, type = ro, define = yes;
LOWCODE: load = MAIN, type = ro, define = yes, optional = yes;
ONCE: load = MAIN, type = ro, optional = yes;
CODE: load = MAIN, type = ro, define = yes;
RODATA: load = MAIN, type = ro;
DATA: load = MAIN, type = rw;
INIT: load = MAIN, type = rw, optional = yes;
BSS: load = MAIN, type = bss, define = yes;
SRPREPHDR: load = UNUSED, type = ro;
SRPREPTRL: load = UNUSED, type = ro;
}
FEATURES {
CONDES: type = constructor,
label = __CONSTRUCTOR_TABLE__,
count = __CONSTRUCTOR_COUNT__,
segment = ONCE;
CONDES: type = destructor,
label = __DESTRUCTOR_TABLE__,
count = __DESTRUCTOR_COUNT__,
segment = RODATA;
CONDES: type = interruptor,
label = __INTERRUPTOR_TABLE__,
count = __INTERRUPTOR_COUNT__,
segment = RODATA,
import = __CALLIRQ__;
}

View File

@ -203,7 +203,7 @@ is <it/left out/, keep this in mind.
The values you assign to the two symbols <tt/__AUTOSTART__/ and <tt/__EXEHDR__/
don't matter.
<sect2><tt/atari-xex.cfg/<p>
<sect2><tt/atari-asm-xex.cfg/<p>
This config file allows writing multi segment binaries easily, without having to
write the header explicitly on each segment.
@ -213,7 +213,8 @@ format support on LD65 instead of the standard binary output, so it does not
have the <tt/__AUTOSTART/ nor the <tt/__EXEHDR__/ symbols.
Note that each <tt/MEMORY/ area in the configuration file will have it's own
segment in the output file with the correct headers.
segment in the output file with the correct headers, and you can specify and
init address INITAD) for each memory area.
<sect2><tt/atari-cart.cfg/<p>
@ -242,6 +243,18 @@ would need to be split in more parts and the parts to be loaded manually.
To write the generated file to a cassette, a utility (<tt/w2cas.com/) to run
on an Atari is provided in the <tt/util/ directory of <tt/atari/ target dir.
<sect2><tt/atari-xex.cfg/<p>
This config file shows how to write a binary using the ATARI (xex) file format
support on LD65, this simplifies the memory areas and allows to add new memory
areas easily without writing new headers and trailers.
Note that the default C library includes the system-check chunk, so in this
linker configuration we suppress the importing of the header and trailer for
this chunk by defining the standard import symbols to a 0 value. For the
initialization address of the system-check chunk, the INITAD is set directly in
the configuration.
<sect1><tt/atarixl/ config files<p>
<sect2>default config file (<tt/atarixl.cfg/)<p>
@ -265,6 +278,15 @@ The files generated by this config file include the
<ref name="&dquot;system check&dquot;" id="syschkxl"> load chunk. It can
optionally be left out, see <ref name="Getting rid of the &dquot;system check&dquot; load chunk" id="nosyschk">.
<sect2><tt/atarixl-xex.cfg/<p>
Similar to the <tt/atari-xex.cfg/ above, this config file shows how to write a
binary using the ATARI (xex) file format support on LD65.
In addition to the suppressing of the system-check headers and trailers, this
also suppresses the shadow-ram-preparation headers and trailers, but does this
by defining an "UNUSED" memory area that is not written to the output file.
<sect>Platform specific header files<p>

View File

@ -938,9 +938,24 @@ has several attributes that may be defined here.
}
</verb></tscreen>
The Atari file format has only one attribute, <tt/RUNAD/ that allows to specify
a symbol as the run address of the binary. If the attribute is omiteed, no run
address is specified.
The Atari file format has two attributes:
<descrip>
<tag><tt>RUNAD = symbol</tt></tag>
Specify a symbol as the run address of the binary, the loader will call this
address after all the file is loaded in memory. If the attribute is omitted,
no run address is included in the file.
<tag><tt>INITAD = memory_area : symbol</tt></tag>
Specify a symbol as the initialization address for the given memory area.
The binary loader will call this address just after the memory area is loaded
into memory, before continuing loading the rest of the file.
</descrip>
<tscreen><verb>
FORMATS {

View File

@ -16,8 +16,7 @@
;DEBUG = 1
.export __SYSTEM_CHECK__: absolute = 1
.import __SYSCHK_LOAD__
.export __SYSTEM_CHECK__, __SYSCHK_END__
.import __STARTADDRESS__
; the following imports are only needed for the 'atari' target version
@ -25,10 +24,12 @@
.import __STACKSIZE__
.import __RESERVED_MEMORY__
; import our header and trailers
.forceimport __SYSCHKHDR__, __SYSCHKTRL__
.include "zeropage.inc"
.include "atari.inc"
.macro print_string text
.local start, cont
jmp cont
@ -229,25 +230,10 @@ delay1: ldx #0
.endproc
end:
__SYSTEM_CHECK__=syschk
__SYSCHK_END__:
.ifndef __ATARIXL__
tmp: ; outside of the load chunk, some kind of poor man's .bss
.endif
; ------------------------------------------------------------------------
; Chunk header
.segment "SYSCHKHDR"
.word __SYSCHK_LOAD__
.word end - 1
; ------------------------------------------------------------------------
; Chunk "trailer" - sets INITAD
.segment "SYSCHKTRL"
.word INITAD
.word INITAD+1
.word syschk

View File

@ -0,0 +1,16 @@
;
; Atari startup system check headers
;
; Christian Groessler, chris@groessler.org, 2013
;
.export __SYSCHKHDR__: absolute = 1
.import __SYSCHK_LOAD__, __SYSCHK_END__
; ------------------------------------------------------------------------
; Chunk header
.segment "SYSCHKHDR"
.word __SYSCHK_LOAD__
.word __SYSCHK_END__ - 1

View File

@ -0,0 +1,17 @@
;
; Atari startup system check headers
;
; Christian Groessler, chris@groessler.org, 2013
;
.export __SYSCHKTRL__: absolute = 1
.import __SYSTEM_CHECK__
.include "atari.inc"
; ------------------------------------------------------------------------
; Chunk "trailer" - sets INITAD
.segment "SYSCHKTRL"
.word INITAD
.word INITAD+1
.word __SYSTEM_CHECK__

View File

@ -1007,6 +1007,7 @@ static void ParseXex (void)
{
static const IdentTok Attributes [] = {
{ "RUNAD", CFGTOK_RUNAD },
{ "INITAD", CFGTOK_INITAD },
};
/* Remember the attributes read */
@ -1017,6 +1018,8 @@ static void ParseXex (void)
};
unsigned AttrFlags = atNone;
Import *RunAd = 0;
Import *InitAd;
MemoryArea *InitMem;
/* Read the attributes */
while (CfgTok == CFGTOK_IDENT) {
@ -1046,6 +1049,24 @@ static void ParseXex (void)
CfgNextTok ();
break;
case CFGTOK_INITAD:
/* We expect a memory area followed by a colon and an identifier */
CfgAssureIdent ();
InitMem = CfgGetMemory (GetStrBufId (&CfgSVal));
CfgNextTok ();
CfgConsumeColon ();
CfgAssureIdent ();
/* Generate an import for the symbol */
InitAd = InsertImport (GenImport (GetStrBufId (&CfgSVal), ADDR_SIZE_ABS));
/* Remember the file position */
CollAppend (&InitAd->RefLines, GenLineInfo (&CfgErrorPos));
/* Eat the identifier token */
CfgNextTok ();
/* Add to XEX */
if (XexAddInitAd (XexFmtDesc, InitMem, InitAd))
CfgError (&CfgErrorPos, "INITAD already given for memory area");
break;
default:
FAIL ("Unexpected attribute token");

View File

@ -94,6 +94,7 @@ typedef enum {
CFGTOK_VERSION,
CFGTOK_FORMAT,
CFGTOK_RUNAD,
CFGTOK_INITAD,
CFGTOK_LOAD,
CFGTOK_RUN,

View File

@ -58,6 +58,12 @@
/* Data */
/*****************************************************************************/
/* Linked list of memory area initialization addresses */
typedef struct XexInitAd {
MemoryArea *InitMem;
Import *InitAd;
struct XexInitAd *next;
} XexInitAd;
struct XexDesc {
@ -65,13 +71,13 @@ struct XexDesc {
FILE* F; /* Output file */
const char* Filename; /* Name of output file */
Import* RunAd; /* Run Address */
XexInitAd* InitAds; /* List of Init Addresses */
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 */
};
/*****************************************************************************/
/* Code */
/*****************************************************************************/
@ -89,6 +95,7 @@ XexDesc* NewXexDesc (void)
D->F = 0;
D->Filename = 0;
D->RunAd = 0;
D->InitAds = 0;
D->HeadPos = 0;
D->HeadEnd = 0;
D->HeadSize = 0;
@ -113,8 +120,35 @@ void XexSetRunAd (XexDesc* D, Import *RunAd)
D->RunAd = RunAd;
}
XexInitAd* XexSearchInitMem(XexDesc* D, MemoryArea *InitMem)
{
XexInitAd* I;
for (I=D->InitAds; I != 0; I=I->next)
{
if (I->InitMem == InitMem)
return I;
}
return NULL;
}
int XexAddInitAd (XexDesc* D, MemoryArea *InitMem, Import *InitAd)
/* Sets and INITAD for the given memory area */
{
XexInitAd* I;
/* Search for repeated entry */
if (XexSearchInitMem (D, InitMem))
return 1;
I = xmalloc (sizeof (XexInitAd));
I->InitAd = InitAd;
I->InitMem = InitMem;
I->next = D->InitAds;
D->InitAds = I;
return 0;
}
static unsigned XexWriteExpr (ExprNode* E, int Signed, unsigned Size,
unsigned long Offs attribute ((unused)),
void* Data)
@ -199,11 +233,14 @@ static void XexFakeSegment (XexDesc *D, unsigned long Addr)
static void XexWriteMem (XexDesc* D, MemoryArea* M)
static unsigned long XexWriteMem (XexDesc* D, MemoryArea* M)
/* Write the segments of one memory area to a file */
{
unsigned I;
/* Store initial position to get total file size */
unsigned long StartPos = ftell (D->F);
/* Always write a segment header for each memory area */
D->HeadPos = 0;
@ -321,6 +358,8 @@ static void XexWriteMem (XexDesc* D, MemoryArea* M)
if (D->HeadSize == 0 && D->HeadPos) {
fseek (D->F, D->HeadPos, SEEK_SET);
}
return ftell (D->F) - StartPos;
}
@ -369,8 +408,14 @@ void XexWriteTarget (XexDesc* D, struct File* F)
for (I = 0; I < CollCount (&F->MemoryAreas); ++I) {
/* Get this entry */
MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I);
/* See if we have an init address for this area */
XexInitAd* I = XexSearchInitMem (D, M);
Print (stdout, 1, " ATARI EXE Dumping `%s'\n", GetString (M->Name));
XexWriteMem (D, M);
if (XexWriteMem (D, M) && I) {
Write16 (D->F, 0x2E2);
Write16 (D->F, 0x2E3);
Write16 (D->F, GetExportVal (I->InitAd->Exp));
}
}
/* Write RUNAD at file end */

View File

@ -69,6 +69,8 @@ void XexWriteTarget (XexDesc* D, File* F);
void XexSetRunAd (XexDesc* D, Import *RunAd);
/* Set the RUNAD export */
int XexAddInitAd (XexDesc* D, MemoryArea *InitMem, Import *InitAd);
/* Sets and INITAD for the given memory area */
/* End of xex.h */

View File

@ -3,10 +3,12 @@ FEATURES {
}
MEMORY {
ZP: file = "", define = yes, start = $0082, size = $007E;
# First memory segment in file, show message
LOADER: file = %O, start = $680, size = 128;
# First memory segment in file, load over COLOR registers:
COLOR: file = %O, start = $2C4, size = 5;
# Second memory segment, load at page 6:
PAGE6: file = %O, start = $600, size = 256;
PAGE6: file = %O, start = $600, size = 128;
# Third memory segment in file, load over SDLST register:
SDLST: file = %O, start = $230, size = 2;
# Main segment, load at "STARTADDRESS"
@ -16,11 +18,13 @@ FILES {
%O: format = atari;
}
FORMATS {
atari: runad = start;
atari: runad = start,
initad = LOADER: show_load;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp, optional = yes;
# Place segments in memory areas:
LOADER: load = LOADER, type = rw;
COLOR: load = COLOR, type = rw;
PAGE6: load = PAGE6, type = rw;
SDLST: load = SDLST, type = rw;

View File

@ -14,8 +14,25 @@
.macpack atari
; Default RUNAD is "start", export that:
.export start
.export start, show_load
; Loader
.segment "LOADER"
show_load:
ldx #0 ; channel 0
lda #<msg_load
sta ICBAL,x ; address
lda #>msg_load
sta ICBAH,x
lda #$FF
sta ICBLL,x ; length
sta ICBLH,x
lda #PUTREC
sta ICCOM,x
jmp CIOV
msg_load:
.byte "Loading....", ATEOL
; We load color values directly into registers
.segment "COLOR"

Binary file not shown.