mirror of
https://github.com/cc65/cc65.git
synced 2024-12-23 04:30:10 +00:00
1c92251a0e
git-svn-id: svn://svn.cc65.org/cc65/trunk@5928 b7a2c559-68d2-44c3-8de9-860c34a00d81
591 lines
18 KiB
Plaintext
591 lines
18 KiB
Plaintext
<!doctype linuxdoc system>
|
|
|
|
<article>
|
|
|
|
<title>Atari specific information for cc65
|
|
<author>Shawn Jefferson, <htmlurl
|
|
url="mailto:shawnjefferson@24fightingchickens.com"
|
|
name="shawnjefferson@24fightingchickens.com"> and
|
|
Christian Groessler, <htmlurl url="mailto:chris@groessler.org" name="chris@groessler.org">
|
|
<date>03-Jan-2006
|
|
|
|
<abstract>
|
|
An overview over the Atari runtime system as it is implemented for the cc65 C
|
|
compiler.
|
|
</abstract>
|
|
|
|
<!-- Table of contents -->
|
|
<toc>
|
|
|
|
<!-- Begin the document -->
|
|
|
|
<sect>Overview<p>
|
|
|
|
This file contains an overview of the Atari runtime system as it comes
|
|
with the cc65 C compiler. It describes the memory layout, Atari specific
|
|
header files, available drivers, and any pitfalls specific to that
|
|
platform.
|
|
|
|
Please note that Atari specific functions are just mentioned here, they are
|
|
described in detail in the separate <htmlurl url="funcref.html" name="function
|
|
reference">. Even functions marked as "platform dependent" may be available on
|
|
more than one platform. Please see the function reference for more
|
|
information.
|
|
|
|
|
|
<sect>Binary format<p>
|
|
|
|
The standard binary output format generated by the linker for the
|
|
Atari target is a machine language program with a standard executable
|
|
header (FF FF <2 byte start address> <2 bytes end address>
|
|
[program bytes]). These values are calculated in the crt0.s
|
|
file from the __STARTUP_LOAD__ and __ZPSAVE_LOAD__ values, so keep
|
|
this in mind if you create a custom linker config file and start
|
|
moving segments around (see section
|
|
<ref name="Reserving a memory area inside the program" id="memhole">).
|
|
You can override this behaviour by creating your own crt0.s file and
|
|
linking it into your program. A run vector is added to the end of the
|
|
file ($02E0 <run vector>) and is calculated using
|
|
__STARTUP_LOAD__ in crt0.s.
|
|
|
|
|
|
<sect>Memory layout<p>
|
|
|
|
The default linker script assumes that the BASIC ROM is disabled (or
|
|
the BASIC cartridge unplugged). This gives a usable memory range from
|
|
$2E00 - $BC1F. The library startup code examines the
|
|
current memory configuration, which depends on the size of the
|
|
installed memory and cartridges present, by inspecting the value in
|
|
the MEMTOP ($2E5) variable. Then the initial stack pointer,
|
|
which indicates the upper bound of memory used, is adjusted. The
|
|
default load address of $2E00 was chosen to accommodate having
|
|
a DOS loaded and a driver that resides in low memory such as the 850
|
|
R: handler. You can override this behaviour by creating a custom
|
|
linker config file or by using the "--start-addr" cl65 command line
|
|
argument or the "--start-addr" or "-S" ld65 command line arguments.
|
|
|
|
Special locations:
|
|
|
|
<descrip>
|
|
<tag/Text screen/
|
|
The text screen depends on the installed memory size and cartridges
|
|
and can be obtained from the SAVMSC variable ($58).
|
|
|
|
<tag/Stack/
|
|
The C runtime stack is located at MEMTOP and grows downwards,
|
|
regardless of how your linker config file is setup. This
|
|
accommodates the different memory configurations of the Atari
|
|
machines, as well as having a cartridge installed. You can override
|
|
this behaviour by writing your own crt0.s file and linking it to
|
|
your program (see also <ref name="Final note"
|
|
id="memhole_final_note">).
|
|
|
|
<tag/Heap/
|
|
The C heap is located at the end of the program and grows towards the C
|
|
runtime stack.
|
|
|
|
</descrip><p>
|
|
|
|
|
|
|
|
<sect>Platform specific header files<p>
|
|
|
|
Programs containing Atari specific code may use the <tt/atari.h/
|
|
header file.
|
|
|
|
|
|
<sect1>Atari specific functions<p>
|
|
|
|
The functions and global variable listed below are special for the Atari.
|
|
See the <htmlurl url="funcref.html" name="function reference"> for declaration and usage.
|
|
|
|
<itemize>
|
|
<item>get_ostype
|
|
<item>get_tv
|
|
<item>_dos_type
|
|
<item>_gtia_mkcolor
|
|
<item>_getcolor
|
|
<item>_getdefdev
|
|
<item>_graphics
|
|
<item>_rest_vecs
|
|
<item>_save_vecs
|
|
<item>_scroll
|
|
<item>_setcolor
|
|
<item>_setcolor_low
|
|
</itemize>
|
|
|
|
|
|
<sect1>Hardware access<p>
|
|
|
|
The following pseudo variables declared in the <tt/atari.h/ header
|
|
file do allow access to hardware located in the address space. Some
|
|
variables are structures, accessing the struct fields will access the
|
|
chip registers.
|
|
|
|
<descrip>
|
|
|
|
<tag><tt/GTIA_READ/ and <tt/GTIA_WRITE/</tag>
|
|
The <tt/GTIA_READ/ structure allows read access to the GTIA. The
|
|
<tt/GTIA_WRITE/ structure allows write access to the GTIA.
|
|
See the <tt/_gtia.h/ header file located in the include directory
|
|
for the declaration of the structure.
|
|
|
|
<tag><tt/POKEY_READ/ and <tt/POKEY_WRITE/</tag>
|
|
The <tt/POKEY_READ/ structure allows read access to the POKEY. The
|
|
<tt/POKEY_WRITE/ structure allows write access to the POKEY.
|
|
See the <tt/_pokey.h/ header file located in the include directory
|
|
for the declaration of the structure.
|
|
|
|
<tag><tt/ANTIC/</tag>
|
|
The <tt/ANTIC/ structure allows read access to the ANTIC.
|
|
See the <tt/_antic.h/ header file located in the include directory
|
|
for the declaration of the structure.
|
|
|
|
<tag><tt/PIA/</tag>
|
|
The <tt/PIA/ structure allows read access to the PIA 6520.
|
|
See the <tt/_pia.h/ header file located in the include directory
|
|
for the declaration of the structure.
|
|
|
|
</descrip><p>
|
|
|
|
|
|
|
|
<sect>Loadable drivers<p>
|
|
|
|
The names in the parentheses denote the symbols to be used for static linking of the drivers.
|
|
|
|
|
|
<sect1>Graphics drivers<p>
|
|
|
|
<descrip>
|
|
|
|
<tag><tt/atari10.tgi (atari_10)/</tag>
|
|
|
|
<tag><tt/atr10p2.tgi (atari_10p2)/</tag>
|
|
|
|
<tag><tt/atari11.tgi (atari_11)/</tag>
|
|
|
|
<tag><tt/atari14.tgi (atari_14)/</tag>
|
|
|
|
<tag><tt/atari15.tgi (atari_15)/</tag>
|
|
|
|
<tag><tt/atr15p2.tgi (atari_15p2)/</tag>
|
|
|
|
<tag><tt/atari3.tgi (atari_3)/</tag>
|
|
|
|
<tag><tt/atari4.tgi (atari_4)/</tag>
|
|
|
|
<tag><tt/atari5.tgi (atari_5)/</tag>
|
|
|
|
<tag><tt/atari6.tgi (atari_6)/</tag>
|
|
|
|
<tag><tt/atari7.tgi (atari_7)/</tag>
|
|
|
|
<tag><tt/atari8.tgi (atari_8)/</tag>
|
|
|
|
<tag><tt/atr8p2.tgi (atari_8p2)/</tag>
|
|
|
|
<tag><tt/atari9.tgi (atari_9)/</tag>
|
|
|
|
<tag><tt/atr9p2.tgi (atari_9p2)/</tag>
|
|
|
|
</descrip><p>
|
|
|
|
Many graphics modes require more memory than the text screen which is
|
|
in effect when the program starts up. Therefore the programmer has to
|
|
tell the program beforehand the memory requirements of the graphics
|
|
modes the program intends to use.
|
|
This can be done by using the __RESERVED_MEMORY__ linker config
|
|
variable. The number specified there describes the number of bytes to
|
|
subtract from the top of available memory as seen from the runtime
|
|
library. This memory is then used by the screen buffer.
|
|
|
|
The numbers for the different graphics modes presented below should
|
|
only be seen as a rule of thumb. Since the screen buffer memory needs
|
|
to start at specific boundaries, the numbers depend on the current top
|
|
of available memory.
|
|
The following numbers were determined by a BASIC program.
|
|
|
|
<table>
|
|
<tabular ca="rr">
|
|
graphics mode|reserved memory@<hline>
|
|
0|1@
|
|
1|1@
|
|
2|1@
|
|
3|1@
|
|
4|1@
|
|
5|182@
|
|
6|1182@
|
|
7|3198@
|
|
8|7120@
|
|
9|7146@
|
|
10|7146@
|
|
11|7146@
|
|
12|162@
|
|
13|1@
|
|
14|3278@
|
|
15|7120@
|
|
16|1@
|
|
17|1@
|
|
18|1@
|
|
19|1@
|
|
20|1@
|
|
21|184@
|
|
22|1192@
|
|
23|3208@
|
|
24|7146@
|
|
25|7146@
|
|
26|7146@
|
|
27|7146@
|
|
28|162@
|
|
29|1@
|
|
30|3304@
|
|
31|7146
|
|
</tabular>
|
|
<caption>reserved memory required for different graphics modes
|
|
</table>
|
|
|
|
The values of "1" are needed because the graphics command crashes if
|
|
it doesn't have at least one byte available. This seems to be a bug of
|
|
the Atari ROM code.
|
|
|
|
<sect1>Extended memory drivers<p>
|
|
|
|
<descrip>
|
|
|
|
<tag><tt/atr130xe.emd (atari_130xe)/</tag>
|
|
|
|
</descrip><p>
|
|
|
|
|
|
<sect1>Joystick drivers<p>
|
|
|
|
<descrip>
|
|
|
|
<tag><tt/ataristd.joy (atari_stdjoy)/</tag>
|
|
Supports up to four standard joysticks connected to the joystick ports of
|
|
the Atari.
|
|
|
|
<tag><tt/atarim8.joy (atari_multijoy)/</tag>
|
|
Supports up to eight standard joysticks connected to a MultiJoy adapter.
|
|
|
|
</descrip><p>
|
|
|
|
|
|
<sect1>Mouse drivers<p>
|
|
|
|
Currently no drivers available (in fact, the API for loadable mouse drivers
|
|
does not exist). There is a static driver you can use.
|
|
|
|
|
|
<sect1>RS232 device drivers<p>
|
|
|
|
Currently there are no RS232 loadable drivers available for the Atari
|
|
platform. There is a static driver you can use.
|
|
|
|
|
|
<sect>Limitations<p>
|
|
|
|
|
|
<sect>DIO implementation<label id="dio"><p>
|
|
|
|
The Atari supports disk drives with either 128 or 256 byte sectors.
|
|
The first three sectors of any disk are always 128 bytes long though. This is
|
|
because the system can only boot from 128 bytes sectors.
|
|
|
|
Therefore the DIO read and write functions transfer only 128 bytes
|
|
for sectors 1 to 3, regardless of the type of diskette.
|
|
|
|
|
|
<sect>CONIO implementation<label id="conio"><p>
|
|
|
|
The console I/O is speed optimized therefore support for XEP80 hardware
|
|
or f80.com software is missing. Of course you may use stdio.h functions.
|
|
|
|
|
|
<sect>Other hints<p>
|
|
|
|
|
|
<sect1>Function keys<p>
|
|
|
|
Function keys are mapped to Atari + number key.
|
|
|
|
|
|
<sect1>Passing arguments to the program<p>
|
|
|
|
Command line arguments can be passed to <tt/main()/ when DOS supports it.
|
|
|
|
<enum>
|
|
<item>Arguments are separated by spaces.
|
|
<item>Leading and trailing spaces around an argument are ignored.
|
|
<item>The first argument passed to <tt/main/ is the program name.
|
|
<item>A maximum number of 16 arguments (including the program name) are
|
|
supported.
|
|
</enum>
|
|
|
|
|
|
<sect1>Interrupts<p>
|
|
|
|
The runtime for the Atari uses routines marked as <tt/.INTERRUPTOR/ for
|
|
interrupt handlers. Such routines must be written as simple machine language
|
|
subroutines and will be called automatically by the VBI handler code
|
|
when they are linked into a program. See the discussion of the <tt/.CONDES/
|
|
feature in the <htmlurl url="ca65.html" name="assembler manual">.
|
|
|
|
|
|
<sect1>Reserving a memory area inside a program<label id="memhole"><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 to reserve space for the
|
|
buffers for display lists and screen memory.
|
|
<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>
|
|
Each load chunk of the executable starts with a 4 byte header which
|
|
defines its load address and size. In the following linker scripts
|
|
these headers are named HEADER and SECHDR (for the MEMORY layout), and
|
|
accordingly NEXEHDR and CHKHDR (for the SEGMENTS layout).
|
|
<p>
|
|
<sect2>Low code and high data example<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>
|
|
The main problem is that the EXE header generated by the cc65 runtime
|
|
lib is wrong. It defines a single load chunk with the sizes/addresses
|
|
of the STARTUP, LOWCODE, INIT, CODE, RODATA, and DATA segments (the whole user
|
|
program).
|
|
<p>
|
|
The contents of the EXE header come from the EXEHDR segment, which is
|
|
defined in crt0.s. This cannot be changed without modifying and
|
|
recompiling the cc65 atari runtime lib. Therefore the original EXE
|
|
header must be discarded. It will be replaced by a user created
|
|
one. The discarding is done by assigning the EXEHDR segment to the
|
|
BANK memory area. The BANK memory area is discarded in the new linker
|
|
script (written to file "").
|
|
<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 an example of a modified cc65 Atari linker configuration file
|
|
(split.cfg):
|
|
<tscreen><verb>
|
|
SYMBOLS {
|
|
__STACKSIZE__ = $800; # 2K stack
|
|
__RESERVED_MEMORY__: value = $0000, weak = yes;
|
|
}
|
|
FEATURES {
|
|
STARTADDRESS: default = $2E00;
|
|
}
|
|
MEMORY {
|
|
ZP: start = $82, size = $7E, type = rw, define = yes;
|
|
|
|
HEADER: start = $0000, size = $6, file = %O; # first load chunk
|
|
RAMLO: start = %S, size = $4000 - %S, 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
|
|
}
|
|
SEGMENTS {
|
|
EXEHDR: load = BANK, type = ro;
|
|
|
|
NEXEHDR: load = HEADER, type = ro; # first load chunk
|
|
STARTUP: load = RAMLO, type = ro, define = yes;
|
|
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;
|
|
ZPSAVE: load = RAM, type = bss, define = yes;
|
|
|
|
ZEROPAGE: load = ZP, type = zp;
|
|
AUTOSTRT: load = RAM, 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__;
|
|
}
|
|
</verb></tscreen>
|
|
<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>
|
|
The newly added NEXEHDR segment defines the correct EXE header. It
|
|
puts the STARTUP, LOWCODE, INIT, and CODE segments, which are the
|
|
segments containing only code, into load chunk #1 (RAMLO memory area).
|
|
<p>
|
|
The header for the second load chunk comes from the new CHKHDR
|
|
segment. It puts the RODATA, DATA, BSS, and ZPSAVE 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):
|
|
<tscreen><verb>
|
|
.import __CODE_LOAD__, __BSS_LOAD__, __CODE_SIZE__
|
|
.import __DATA_LOAD__, __RODATA_LOAD__, __STARTUP_LOAD__
|
|
|
|
.segment "NEXEHDR"
|
|
.word $FFFF
|
|
.word __STARTUP_LOAD__
|
|
.word __CODE_LOAD__ + __CODE_SIZE__ - 1
|
|
|
|
.segment "CHKHDR"
|
|
.word __RODATA_LOAD__
|
|
.word __BSS_LOAD__ - 1
|
|
</verb></tscreen>
|
|
<p>
|
|
Compile with
|
|
<tscreen><verb>
|
|
cl65 -t atari -C split.cfg -o prog.com prog.c split.s
|
|
</verb></tscreen>
|
|
|
|
<sect2>Low data and high code example<p>
|
|
|
|
|
|
Goal: Put RODATA and DATA into low memory and STARTUP, LOWCODE, INIT,
|
|
CODE, BSS, ZPSAVE into high memory (split2.cfg):
|
|
|
|
<tscreen><verb>
|
|
SYMBOLS {
|
|
__STACKSIZE__ = $800; # 2K stack
|
|
__RESERVED_MEMORY__: value = $0000, weak = yes;
|
|
}
|
|
FEATURES {
|
|
STARTADDRESS: default = $2E00;
|
|
}
|
|
MEMORY {
|
|
ZP: start = $82, size = $7E, type = rw, define = yes;
|
|
|
|
HEADER: start = $0000, size = $6, file = %O; # first load chunk
|
|
RAMLO: start = %S, size = $4000 - %S, 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
|
|
}
|
|
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
|
|
STARTUP: load = RAM, type = ro, define = yes;
|
|
INIT: load = RAM, type = ro, optional = yes;
|
|
CODE: load = RAM, type = ro, define = yes;
|
|
ZPSAVE: load = RAM, type = bss, define = yes;
|
|
BSS: load = RAM, type = bss, define = yes;
|
|
|
|
ZEROPAGE: load = ZP, type = zp;
|
|
AUTOSTRT: load = RAM, 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__;
|
|
}
|
|
</verb></tscreen>
|
|
|
|
New contents for NEXEHDR and CHKHDR are needed (split2.s):
|
|
<tscreen><verb>
|
|
.import __STARTUP_LOAD__, __ZPSAVE_LOAD__, __DATA_SIZE__
|
|
.import __DATA_LOAD__, __RODATA_LOAD__
|
|
|
|
.segment "NEXEHDR"
|
|
.word $FFFF
|
|
.word __RODATA_LOAD__
|
|
.word __DATA_LOAD__ + __DATA_SIZE__ - 1
|
|
|
|
.segment "CHKHDR"
|
|
.word __STARTUP_LOAD__
|
|
.word __ZPSAVE_LOAD__ - 1
|
|
</verb></tscreen>
|
|
|
|
Compile with
|
|
<tscreen><verb>
|
|
cl65 -t atari -C split2.cfg -o prog.com prog.c split2.s
|
|
</verb></tscreen>
|
|
|
|
<sect2>Final note<label id="memhole_final_note"><p>
|
|
|
|
There are two other memory areas which don't appear directly in the
|
|
linker script. They are the stack and the heap.
|
|
|
|
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__.
|
|
|
|
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.
|
|
|
|
common/_heap.s defines the location of the heap and atari/crt0.s
|
|
defines the location of the stack by initializing sp.
|
|
|
|
|
|
<sect>Bugs/Feedback<p>
|
|
|
|
If you have problems using the library, if you find any bugs, or if you're
|
|
doing something interesting with it, I would be glad to hear from you. Feel
|
|
free to contact me by email (<htmlurl url="mailto:uz@cc65.org"
|
|
name="uz@cc65.org"> or <htmlurl url="mailto:chris@groessler.org"
|
|
name="chris@groessler.org"> ).
|
|
|
|
|
|
|
|
<sect>License<p>
|
|
|
|
This software is provided 'as-is', without any expressed or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
<enum>
|
|
<item> The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
<item> Altered source versions must be plainly marked as such, and must not
|
|
be misrepresented as being the original software.
|
|
<item> This notice may not be removed or altered from any source
|
|
distribution.
|
|
</enum>
|
|
|
|
</article>
|