<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:cpg@aladdin.de" name="cpg@aladdin.de">
-<date>14-Sep-2004
+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
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 __CODE_LOAD__ and __BSS_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
-__CODE_LOAD__ in 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>
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 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.
+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:
<tag/Stack/
The C runtime stack is located at MEMTOP and grows downwards,
regardless of how your linker config file is setup. This
- accomodates the different memory configurations of the Atari
+ 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"
<sect1>Atari specific functions<p>
-The functions listed below are special for the Atari. See the <htmlurl
-url="funcref.html" name="function reference"> for declaration and usage.
+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
<sect1>Graphics drivers<p>
Currently there are no graphics drivers available for the Atari platform.
+However, the runtime library provides a function named _graphics, with
+a mode parameter just like the BASIC GRAPHICS command. This function will
+switch to the requested graphics mode.
+There are currently no functions available to access the graphics
+memory. The access must be implemented manually.
+
+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/atari-stdjoy.joy/</tag>
+ <tag><tt/ataristd.joy/</tag>
Supports up to four standard joysticks connected to the joystick ports of
the Atari.
+ <tag><tt/atarim8.joy/</tag>
+ Supports up to eight standard joysticks connected to a MultiJoy adapter.
+
</descrip><p>
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>
-These are defined to be Atari + number key.
+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 the buffers for display
-lists and screen memory.
+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
$BC1F.
<p>
Each load chunk of the executable starts with a 4 byte header which
-defines its load address and size.
+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
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 runtine
+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 CODE, RODATA, and DATA segments (the whole user program).
+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 w/o modifiying and
+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.
+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
and the second load chunk header.
<p>
<p>
-This is a modified cc65 Atari linker configuration file (split.cfg):
+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 = $2E00, size = $1200, file = %O;
+ RAMLO: start = %S, size = $4000 - %S, file = %O;
BANK: start = $4000, size = $4000, file = "";
RAM: start = $8000, size = $3C20, file = %O; # $3C20: matches upper bound $BC1F
}
SEGMENTS {
- EXEHDR: load = BANK, type = wprot;
+ EXEHDR: load = BANK, type = ro;
- NEXEHDR: load = HEADER, type = wprot; # first load chunk
- CODE: load = RAMLO, type = wprot, define = yes;
+ 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 = wprot; # second load chunk
- RODATA: load = RAM, type = wprot, 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 = wprot; # defines program entry point
+ AUTOSTRT: load = RAM, type = ro; # defines program entry point
}
FEATURES {
CONDES: segment = RODATA,
label = __DESTRUCTOR_TABLE__,
count = __DESTRUCTOR_COUNT__;
}
-SYMBOLS {
- __STACKSIZE__ = $800; # 2K stack
-}
</verb></tscreen>
<p>
memory area isn't written to the output file. This way the contents of
the EXEHDR segment get discarded.
<p>
-The added NEXEHDR segment defines the correct EXE header. It puts only
-the CODE segment into load chunk #1 (RAMLO memory area).
+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 and DATA segments into load chunk #2 (RAM
-memory area).
+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__
-
- .segment "NEXEHDR"
- .word $FFFF ; EXE file magic number
- ; 1st load chunk
- .word __CODE_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
+ .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
<sect2>Low data and high code example<p>
-Goal: Put RODATA and DATA into low memory and CODE with BSS into high
-memory (split2.cfg):
+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 = $2E00, size = $1200, file = %O;
+ RAMLO: start = %S, size = $4000 - %S, file = %O;
BANK: start = $4000, size = $4000, file = "";
RAM: start = $8000, size = $3C20, file = %O; # $3C20: matches upper bound $BC1F
}
SEGMENTS {
- EXEHDR: load = BANK, type = wprot; # discarded old EXE header
+ EXEHDR: load = BANK, type = ro; # discarded old EXE header
- NEXEHDR: load = HEADER, type = wprot; # first load chunk
- RODATA: load = RAMLO, type = wprot, define = yes;
+ 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 = wprot; # second load chunk
- CODE: load = RAM, type = wprot, 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 = wprot; # defines program entry point
+ AUTOSTRT: load = RAM, type = ro; # defines program entry point
}
FEATURES {
CONDES: segment = RODATA,
label = __DESTRUCTOR_TABLE__,
count = __DESTRUCTOR_COUNT__;
}
-SYMBOLS {
- __STACKSIZE__ = $800; # 2K stack
-}
</verb></tscreen>
New contents for NEXEHDR and CHKHDR are needed (split2.s):
<tscreen><verb>
- .import __CODE_LOAD__, __BSS_LOAD__, __DATA_SIZE__
- .import __DATA_LOAD__, __RODATA_LOAD__
+ .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 "NEXEHDR"
+ .word $FFFF
+ .word __RODATA_LOAD__
+ .word __DATA_LOAD__ + __DATA_SIZE__ - 1
- .segment "CHKHDR"
- .word __CODE_LOAD__
- .word __BSS_LOAD__ - 1
+ .segment "CHKHDR"
+ .word __STARTUP_LOAD__
+ .word __ZPSAVE_LOAD__ - 1
</verb></tscreen>
Compile with
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:cpg@aladdin.de"
-name="cpg@aladdin.de">).
+name="uz@cc65.org"> or <htmlurl url="mailto:chris@groessler.org"
+name="chris@groessler.org"> ).