Re: [cc65] Atari: Loading code with DOS 2.0 help? (LONG)

From: Christian Groessler <chris1groessler.org>
Date: 2004-08-03 22:22:08
Hi,

I wanted to play around with this since some time. Recently motivated
by Chris Martin's problem I've tried to make a program where code
loads below $4000 (starting at $2E00) and the rest at $8000.


The main problem is that the generated EXE header is wrong. It defines
a single load chunk with the sizes/addresses of the code, rodata, and
data segments (the whole user program).

I need to have 2 load chunks, one with code and one with the rest.

The contents of the EXE header come from the EXEHDR segment, which is
defined in crt0.s. Since this cannot be changed w/o modifiying and
recompiling the cc65 atari runtime lib, I've added a second segment
NEXEHDR to the linker script, which defines the correct header.
This one puts only the code segment into load chunk #1.

I've also added a CHKHDR segment, which provides the contents of the
header for load chunk #2.

This is the modified cc65 Atari linker configuration file (split.cfg):

------------------
MEMORY {
    ZP: start = $82, size = $7E, type = rw, define = yes;
    HEADER: start = $0000, size = $6, file = %O;
    RAMLO: start = $2E00, size = $1200, file = %O;

    SECHDR: start = $0000, size = $4, file = %O;
    BANK: start = $4000, size = $4000, file = "";	# "" is used to discard the contents
    RAM: start = $8000, size = $3C20, file = %O;	# $3C20: matches upper bound $BC1F
}
SEGMENTS {
    EXEHDR: load = BANK, type = wprot;
    NEXEHDR: load = HEADER, type = wprot;		# new EXE header and 1st chunk of file data
    CODE: load = RAMLO, type = wprot, define = yes;

    CHKHDR: load = SECHDR, type = wprot;		# header for 2nd chunk of file data

    RODATA: load = RAM, type = wprot, define = yes;
    DATA: load = RAM, type = rw, define = yes;
    BSS: load = RAM, type = bss, define = yes;
    ZEROPAGE: load = ZP, type = zp;
    AUTOSTRT: load = RAM, type = wprot;
}
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
}
------------------

The contents of the old EXEHDR segment should be discarded. Therefore
they're relocated into the BANK memory area, which isn't written to
the output file.

The contents of the new NEXEHDR and CHKHDR segments come from this
file (split.s):

------------------
	.import	__CODE_LOAD__, __BSS_LOAD__, __CODE_SIZE__
	.import	__DATA_LOAD__, __RODATA_LOAD__

	.segment "NEXEHDR"
	.word	 $FFFF
	.word	 __CODE_LOAD__
	.word	 __CODE_LOAD__ + __CODE_SIZE__ - 1

	.segment "CHKHDR"
	.word	 __RODATA_LOAD__
	.word	 __BSS_LOAD__ - 1
------------------


That's the demo program I've used:

------------------
/* $Id: mem.c,v 1.2 2004/08/01 19:50:43 chris Exp $
 *
 * show some memory stuff
 *
 * 06-Mar-2003, chris
 */

#include <stdio.h>
#include <conio.h>
#include <atari.h>

extern char _dos_type;
extern unsigned char _graphmode_used;

unsigned int *APPMHI = (unsigned int *)14;      /* 14,15 */
unsigned char *RAMTOP = (unsigned char *)106;   /* in pages */
unsigned int *LOMEM = (unsigned int *)128;      /* used by BASIC */
unsigned int *MEMTOP = (unsigned int *)741;
unsigned int *MEMLO = (unsigned int *)743;

int main(void)
{

  clrscr();

  printf("   RAMTOP = %02X (%u) - $%04X (%u)\n",
         *RAMTOP, *RAMTOP, *RAMTOP * 256, *RAMTOP * 256);
  printf("   APPMHI = $%04X (%u)\n", *APPMHI, *APPMHI);
  printf("   LOMEM  = $%04X (%u)  <BASIC only>\n", *LOMEM, *LOMEM);
  printf("   MEMTOP = $%04X (%u)\n", *MEMTOP, *MEMTOP);
  printf("   MEMLO  = $%04X (%u)\n", *MEMLO, *MEMLO);

  printf("   ----------------------\n");
  printf("   main:            $%04X  (code)\n", &main);
  printf("   _graphmode_used: $%04X  (data)\n", &_graphmode_used);
  printf("   _dos_type:       $%04X  (bss)\n", &_dos_type);

  if (_dos_type != 1) cgetc();
  return(0);
}
------------------

Compile with

cl65 -t atari -C split.cfg -o mt.com mem.c split.s

Running it:

[snip]
   main:            $2E8F  (code)
   _graphmode_used: $8286  (data)
   _dos_type:       $82D9  (bss)


Nice, this seems to work.


Now let's try a different configuration...

Put RODATA and DATA into low memory (below $4000) and CODE with BSS
into high memory (split2.cfg):

------------------
MEMORY {
    ZP: start = $82, size = $7E, type = rw, define = yes;
    HEADER: start = $0000, size = $6, file = %O;
    RAMLO: start = $2E00, size = $1200, file = %O;

    SECHDR: start = $0000, size = $4, file = %O;
    BANK: start = $4000, size = $4000, file = "";	# "" is used to discard the contents
    RAM: start = $8000, size = $3C20, file = %O;	# $3C20: matches upper bound $BC1F
}
SEGMENTS {
    EXEHDR: load = BANK, type = wprot;
    NEXEHDR: load = HEADER, type = wprot;		# new EXE header and 1st chunk of file data
    RODATA: load = RAMLO, type = wprot, define = yes;
    DATA: load = RAMLO, type = rw, define = yes;

    CHKHDR: load = SECHDR, type = wprot;		# header for 2nd chunk of file data

    CODE: load = RAM, type = wprot, define = yes;
    BSS: load = RAM, type = bss, define = yes;
    ZEROPAGE: load = ZP, type = zp;
    AUTOSTRT: load = RAM, type = wprot;
}
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
}
------------------

New contents for NEXEHDR and CHKHDR are needed (split2.s):

------------------
	.import	__CODE_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	 __CODE_LOAD__
	.word	 __BSS_LOAD__ - 1
------------------

Compiling it with

cl65 -t atari -C split2.cfg -o mt.com mem.c split2.s

and running it:

[snip]
   main:            $808F  (code)
   _graphmode_used: $3086  (data)
   _dos_type:       $8C86  (bss)


Good. Works as well :-)


You have to be careful about 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 (end of RAM in theory, but on the Atari dynamically set from
the APPMHI system variable). 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 you don't want to place BSS and/or the stack at the end of the
program, you'll have to replace/modify some parts of the cc65 runtime
lib.

runtime/_heap.s defines the location of the heap and atari/crt0.s
defines the location of the stack by initializing sp.

I haven't tried such a configuration yet, so maybe there's something
else to care about...

regards,
chris

----------------------------------------------------------------------
To unsubscribe from the list send mail to majordomo@musoftware.de with
the string "unsubscribe cc65" in the body(!) of the mail.
Received on Tue Aug 3 22:22:24 2004

This archive was generated by hypermail 2.1.8 : 2004-08-03 22:22:33 CEST