7bca5fab5b
git-svn-id: svn://svn.compuextreme.de/Viitor/V962/Viitor_cc65@4352 504e572c-2e33-0410-9681-be2bf7408885
250 lines
8.4 KiB
HTML
250 lines
8.4 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
|
<HTML>
|
|
<HEAD>
|
|
<META NAME="GENERATOR" CONTENT="LinuxDoc-Tools 0.9.20">
|
|
<TITLE>Atari specific information for cc65: Other hints</TITLE>
|
|
<LINK HREF="atari-9.html" REL=next>
|
|
<LINK HREF="atari-7.html" REL=previous>
|
|
<LINK HREF="atari.html#toc8" REL=contents>
|
|
</HEAD>
|
|
<BODY>
|
|
<A HREF="atari-9.html">Next</A>
|
|
<A HREF="atari-7.html">Previous</A>
|
|
<A HREF="atari.html#toc8">Contents</A>
|
|
<HR>
|
|
<H2><A NAME="s8">8.</A> <A HREF="atari.html#toc8">Other hints</A></H2>
|
|
|
|
|
|
|
|
<H2><A NAME="ss8.1">8.1</A> <A HREF="atari.html#toc8.1">Function keys</A>
|
|
</H2>
|
|
|
|
|
|
<P>These are defined to be Atari + number key.</P>
|
|
|
|
<H2><A NAME="memhole"></A> <A NAME="ss8.2">8.2</A> <A HREF="atari.html#toc8.2">Reserving a memory area inside a program</A>
|
|
</H2>
|
|
|
|
|
|
<P>The Atari 130XE maps its additional memory into CPU memory in 16K
|
|
chunks at address $4000 to $7FFF. One might want to
|
|
prevent this memory area from being used by cc65. Other reasons to
|
|
prevent the use of some memory area could be the buffers for display
|
|
lists and screen memory.</P>
|
|
<P>The Atari executable format allows holes inside a program, e.g. one
|
|
part loads into $2E00 to $3FFF, going below the reserved
|
|
memory area (assuming a reserved area from $4000 to
|
|
$7FFF), and another part loads into $8000 to
|
|
$BC1F.</P>
|
|
<P>Each load chunk of the executable starts with a 4 byte header which
|
|
defines its load address and size.</P>
|
|
|
|
<H3>Low code and high data example</H3>
|
|
|
|
<P>Goal: Create an executable with 2 load chunks which doesn't use the
|
|
memory area from $4000 to $7FFF. The CODE segment of
|
|
the program should go below $4000 and the DATA and RODATA
|
|
segments should go above $7FFF.</P>
|
|
<P>The main problem is that the EXE header generated by the cc65 runtine
|
|
lib is wrong. It defines a single load chunk with the sizes/addresses
|
|
of the LOWCODE, INIT, CODE, RODATA, and DATA segments (the whole user
|
|
program).</P>
|
|
<P>The contents of the EXE header come from the EXEHDR segment, which is
|
|
defined in crt0.s. This cannot be changed w/o modifiying and
|
|
recompiling the cc65 atari runtime lib. Therefore the original EXE
|
|
header must be discarded. It will be replaced by a user created
|
|
one.</P>
|
|
<P>The user needs to create a customized linker config file which adds
|
|
new memory areas and segments to hold the new EXE header and the
|
|
header data for the second load chunk. Also an assembly source file
|
|
needs to be created which defines the contents of the new EXE header
|
|
and the second load chunk header.</P>
|
|
|
|
<P>This is a modified cc65 Atari linker configuration file (split.cfg):
|
|
<BLOCKQUOTE><CODE>
|
|
<PRE>
|
|
MEMORY {
|
|
ZP: start = $82, size = $7E, type = rw, define = yes;
|
|
|
|
HEADER: start = $0000, size = $6, file = %O; # first load chunk
|
|
RAMLO: start = $2E00, size = $1200, file = %O;
|
|
|
|
BANK: start = $4000, size = $4000, file = "";
|
|
|
|
SECHDR: start = $0000, size = $4, file = %O; # second load chunk
|
|
RAM: start = $8000, size = $3C20, file = %O; # $3C20: matches upper bound $BC1F
|
|
TRAILER: start = $0000, size = $0006, file = %O;
|
|
}
|
|
SEGMENTS {
|
|
EXEHDR: load = BANK, type = ro;
|
|
|
|
NEXEHDR: load = HEADER, type = ro; # first load chunk
|
|
LOWCODE: load = RAMLO, type = ro, define = yes, optional = yes;
|
|
INIT: load = RAMLO, type = ro, optional = yes;
|
|
CODE: load = RAMLO, type = ro, define = yes;
|
|
|
|
CHKHDR: load = SECHDR, type = ro; # second load chunk
|
|
RODATA: load = RAM, type = ro, define = yes;
|
|
DATA: load = RAM, type = rw, define = yes;
|
|
BSS: load = RAM, type = bss, define = yes;
|
|
|
|
ZEROPAGE: load = ZP, type = zp;
|
|
AUTOSTRT: load = TRAILER, type = ro; # defines program entry point
|
|
}
|
|
FEATURES {
|
|
CONDES: segment = RODATA,
|
|
type = constructor,
|
|
label = __CONSTRUCTOR_TABLE__,
|
|
count = __CONSTRUCTOR_COUNT__;
|
|
CONDES: segment = RODATA,
|
|
type = destructor,
|
|
label = __DESTRUCTOR_TABLE__,
|
|
count = __DESTRUCTOR_COUNT__;
|
|
}
|
|
SYMBOLS {
|
|
__STACKSIZE__ = $800; # 2K stack
|
|
__RESERVED_MEMORY__: value = $0, weak = yes;
|
|
}
|
|
</PRE>
|
|
</CODE></BLOCKQUOTE>
|
|
</P>
|
|
|
|
<P>A new memory area BANK was added which describes the reserved area.
|
|
It gets loaded with the contents of the old EXEHDR segment. But the
|
|
memory area isn't written to the output file. This way the contents of
|
|
the EXEHDR segment get discarded.</P>
|
|
<P>The added NEXEHDR segment defines the correct EXE header. It puts only
|
|
the CODE segment into load chunk #1 (RAMLO memory area).</P>
|
|
<P>The header for the second load chunk comes from the new CHKHDR
|
|
segment. It puts the RODATA and DATA segments into load chunk #2 (RAM
|
|
memory area).</P>
|
|
|
|
<P>The contents of the new NEXEHDR and CHKHDR segments come from this
|
|
file (split.s):
|
|
<BLOCKQUOTE><CODE>
|
|
<PRE>
|
|
.import __LOWCODE_LOAD__, __BSS_LOAD__, __CODE_SIZE__
|
|
.import __CODE_LOAD__, __DATA_LOAD__, __RODATA_LOAD__
|
|
|
|
.segment "NEXEHDR"
|
|
.word $FFFF ; EXE file magic number
|
|
; 1st load chunk
|
|
.word __LOWCODE_LOAD__
|
|
.word __CODE_LOAD__ + __CODE_SIZE__ - 1
|
|
|
|
.segment "CHKHDR"
|
|
; 2nd load chunk (contains with AUTOSTRT in fact a 3rd load chunk)
|
|
.word __RODATA_LOAD__
|
|
.word __BSS_LOAD__ - 1
|
|
</PRE>
|
|
</CODE></BLOCKQUOTE>
|
|
</P>
|
|
<P>Compile with
|
|
<BLOCKQUOTE><CODE>
|
|
<PRE>
|
|
cl65 -t atari -C split.cfg -o prog.com prog.c split.s
|
|
</PRE>
|
|
</CODE></BLOCKQUOTE>
|
|
</P>
|
|
|
|
<H3>Low data and high code example</H3>
|
|
|
|
|
|
|
|
<P>Goal: Put RODATA and DATA into low memory and LOWCODE, INIT, CODE, BSS
|
|
into high memory (split2.cfg):</P>
|
|
<P>
|
|
<BLOCKQUOTE><CODE>
|
|
<PRE>
|
|
MEMORY {
|
|
ZP: start = $82, size = $7E, type = rw, define = yes;
|
|
|
|
HEADER: start = $0000, size = $6, file = %O; # first load chunk
|
|
RAMLO: start = $2E00, size = $1200, file = %O;
|
|
|
|
BANK: start = $4000, size = $4000, file = "";
|
|
|
|
SECHDR: start = $0000, size = $4, file = %O; # second load chunk
|
|
RAM: start = $8000, size = $3C20, file = %O; # $3C20: matches upper bound $BC1F
|
|
TRAILER: start = $0000, size = $0006, file = %O;
|
|
}
|
|
SEGMENTS {
|
|
EXEHDR: load = BANK, type = ro; # discarded old EXE header
|
|
|
|
NEXEHDR: load = HEADER, type = ro; # first load chunk
|
|
RODATA: load = RAMLO, type = ro, define = yes;
|
|
DATA: load = RAMLO, type = rw, define = yes;
|
|
|
|
CHKHDR: load = SECHDR, type = ro; # second load chunk
|
|
LOWCODE: load = RAM, type = ro, define = yes, optional = yes;
|
|
INIT: load = RAM, type = ro, optional = yes;
|
|
CODE: load = RAM, type = ro, define = yes;
|
|
BSS: load = RAM, type = bss, define = yes;
|
|
|
|
ZEROPAGE: load = ZP, type = zp;
|
|
AUTOSTRT: load = TRAILER, type = ro; # defines program entry point
|
|
}
|
|
FEATURES {
|
|
CONDES: segment = RODATA,
|
|
type = constructor,
|
|
label = __CONSTRUCTOR_TABLE__,
|
|
count = __CONSTRUCTOR_COUNT__;
|
|
CONDES: segment = RODATA,
|
|
type = destructor,
|
|
label = __DESTRUCTOR_TABLE__,
|
|
count = __DESTRUCTOR_COUNT__;
|
|
}
|
|
SYMBOLS {
|
|
__STACKSIZE__ = $800; # 2K stack
|
|
__RESERVED_MEMORY__: value = $0, weak = yes;
|
|
}
|
|
</PRE>
|
|
</CODE></BLOCKQUOTE>
|
|
</P>
|
|
<P>New contents for NEXEHDR and CHKHDR are needed (split2.s):
|
|
<BLOCKQUOTE><CODE>
|
|
<PRE>
|
|
.import __LOWCODE_LOAD__, __BSS_LOAD__, __DATA_SIZE__
|
|
.import __DATA_LOAD__, __RODATA_LOAD__
|
|
|
|
.segment "NEXEHDR"
|
|
.word $FFFF
|
|
.word __RODATA_LOAD__
|
|
.word __DATA_LOAD__ + __DATA_SIZE__ - 1
|
|
|
|
.segment "CHKHDR"
|
|
.word __LOWCODE_LOAD__
|
|
.word __BSS_LOAD__ - 1
|
|
</PRE>
|
|
</CODE></BLOCKQUOTE>
|
|
</P>
|
|
<P>Compile with
|
|
<BLOCKQUOTE><CODE>
|
|
<PRE>
|
|
cl65 -t atari -C split2.cfg -o prog.com prog.c split2.s
|
|
</PRE>
|
|
</CODE></BLOCKQUOTE>
|
|
</P>
|
|
|
|
<H3><A NAME="memhole_final_note"></A> Final note</H3>
|
|
|
|
|
|
<P>There are two other memory areas which don't appear directly in the
|
|
linker script. They are the stack and the heap.</P>
|
|
<P>The cc65 runtime lib places the stack location at the end of available
|
|
memory. This is dynamically set from the MEMTOP system variable at
|
|
startup. The heap is located in the area between the end of the BSS
|
|
segment and the top of the stack as defined by __STACKSIZE__.</P>
|
|
<P>If BSS and/or the stack shouldn't stay at the end of the program,
|
|
some parts of the cc65 runtime lib need to be replaced/modified.</P>
|
|
<P>common/_heap.s defines the location of the heap and atari/crt0.s
|
|
defines the location of the stack by initializing sp.</P>
|
|
|
|
|
|
<HR>
|
|
<A HREF="atari-9.html">Next</A>
|
|
<A HREF="atari-7.html">Previous</A>
|
|
<A HREF="atari.html#toc8">Contents</A>
|
|
</BODY>
|
|
</HTML>
|