Jump to content
IGNORED

cc65 Link Options and Atari Bank Switching...


Cybernoid

Recommended Posts

The following is a question that I posted to the cc65 mailing list, but since there are many accomplished coders (hopefully some with C), I thought I would cross post this question here:

 

 

Hello,

 

I have a quick question about using the linker config file. Please let me know if you have some ideas....

 

Here is what I have:

 

I have some code that uses the Atari 130XE bank switching to do some things. In my link.cfg file, I have basically defined all code to be placed between the 0x8000 and 0xbc1f address space. However, my code has grown to a point where it will no longer fit in this space.

 

The Atari 130XE allows you to bank switch the address space from 0x4000-0x7fff, so initially I do not want to put any code here.

 

I would like to place code in the address spaces: 0x2800-0x3fff and 0x8000-0xbc1f. I am not sure exactly how to break this up.

 

 

Here is my link.cfg file (more comments below):

 

MEMORY {

ZP: start = $82, size = $7E, type = rw, define = yes;

HEADER: start = $0000, size = $6, file = %O;

RAM28: start = $2800, size = $1800, file = %O;

RAM: start = $8000, size = $3c1f, file = %O;# $8E1F: matches up bound $BC1F

}

SEGMENTS {

EXEHDR: load = HEADER, type = wprot;

CODE: load = RAM, type = wprot, define = yes;

RODATA: load = RAM, type = wprot;

DATA: load = RAM, type = rw;

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__ = $200; # 512B stack (was 2K stack)

}

---------------------------------------------------

 

I have tried changing the DATA line to:

DATA: load = RAM28, type = rw;

 

This compiles, but when I load the code, the computer crashes.

Does anyone know why this would be?

 

 

 

Also, a related question: Is there a way to specify which structures are placed into which address space? For example, say I want to use the 0x4000-0x7fff address space for code. I would then want to place a piece of code to handle the bank switching outside of this address space. But, how do I place one function at a certain location?

 

Thanks for the help, and sorry for the long post.

 

Cheers,

--Chris

Link to comment
Share on other sites

Hi Chris,

 

Here is my link.cfg file (more comments below):

 

MEMORY {

   ZP: start = $82, size = $7E, type = rw, define = yes;

   HEADER: start = $0000, size = $6, file = %O;

   RAM28: start = $2800, size = $1800, file = %O;    

   RAM: start = $8000, size = $3c1f, file = %O;# $8E1F: matches up bound $BC1F

}

SEGMENTS {

   EXEHDR: load = HEADER, type = wprot;

   CODE: load = RAM, type = wprot, define = yes;

   RODATA: load = RAM, type = wprot;

   DATA: load = RAM, type = rw;

   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__ = $200; # 512B stack (was 2K stack)

}

 

I'd change the segments section to:

 

MEMORY {

   ZP: start = $82, size = $7E, type = rw, define = yes;

   HEADER: start = $0000, size = $6, file = %O;

   RAM: start = $8000, size = $3c1f, file = %O;# $8E1F: matches up bound $BC1F

   HEADER2: start = $0006, size = $6, file = %O;

   RAM28: start = $2800, size = $1800, file = %O;    

}

SEGMENTS {

   EXEHDR: load = HEADER, type = wprot;

   CODE: load = RAM, type = wprot, define = yes;

   RODATA: load = RAM, type = wprot;

   BSS: load = RAM, type = bss, define = yes;

   H_DATA: load = HEADER2, type = wprot;

   DATA: load = RAM28, type = rw, define = yes;

   ZEROPAGE: load = ZP, type = zp;

   AUTOSTRT: load = RAM, type = wprot;

}

 

This compiles, but when I load the code, the computer crashes.

Does anyone know why this would be?

 

The data's initial values are still located in the initial loaded block of data.

These would have to be manually relocated, but the above method

handles this by splitting the load into 2 parts.

 

However, we do need to 'declare' this segment. Create an assembler file with the following code:

 

	.import __DATA_LOAD__, __DATA_SIZE__
;

.segment "H_DATA"
;

.word $FFFF

.word __DATA_LOAD__

.word __DATA_LOAD__ + __DATA_SIZE__ - 1
;

 

Remember too to add this to your object file list.

 

Also, a related question: Is there a way to specify which structures are placed into which address space?  For example, say I want to use the 0x4000-0x7fff address space for code.  I would then want to place a piece of code to handle the bank switching outside of this address space.  But, how do I place one function at a certain location?

 

Depends on how you intend to populate these banks.

You'd need a load routine to:

1) set area 0x4000 to the required bank

2) load code from file to 0x4000

3) swap back 0x04000 to main mem.

 

The linker will know what the functions address is, you just have to make sure the right bank is place when you make the call ;)

 

I believe with the latest CC65 it maybe possible to make this transparent with macros or a single function (something like a 'far call') but I haven't investigated this yet. I will do because it comes in handy with cartridge development.

 

Also you can create a segment such as:

MORE_CODE: load = RAM2, run = RAM3, type = wprot, define = yes;

However its normally up to you to relocate the block

 

Regards,

 

Mark

Link to comment
Share on other sites

	.import __DATA_LOAD__, __DATA_SIZE__
;

.segment "H_DATA"
;

.word $FFFF

.word __DATA_LOAD__

.word __DATA_LOAD__ + __DATA_SIZE__ - 1
;

 

Remember too to add this to your object file list.

 

Thanks Mark! This information is very useful. I received a similar message from Shawn Jefferson basically saying to do the same thing. Unfortunately, I am still having some issues. I have created a file with the code above, but when I link, I receive the following:

 


ca65 -o segload.o  segload.S

cl65 -m ansiterm.map -Ln labels -C link.cfg -o ansiterm.com -t atari ansiterm.o segload.o

Unresolved external `__DATA_LOAD__' referenced in:

 segload.S(1)

Unresolved external `__DATA_SIZE__' referenced in:

 segload.S(1)

ld65: Error: 2 unresolved external(s) found - cannot create output file

Link to comment
Share on other sites

Hmm, did you add the 'define = yes' to the DATA segment?

 

Doh! :ponder:

 

Yeah, I forgot the define = yes to the Data segment.

 

Okay, I added this. Now, it compiles, but when I load the program from SpartaDOS, it gives me an error 146. Which, I think is "Function not implemented in handler". I am not sure what the problem is here...

Link to comment
Share on other sites

 

	.import __DATA_LOAD__, __DATA_SIZE__
;

.segment "H_DATA"
;

.word $FFFF

.word __DATA_LOAD__

.word __DATA_LOAD__ + __DATA_SIZE__ - 1
;

 

In my project (which did work last time I compiled it with cc65) I didn't need the FFFF' date=' so my header for the second load segment is only 4 bytes long.

 

Also' date=' a related question: Is there a way to specify which structures are placed into which address space? For example, say I want to use the 0x4000-0x7fff address space for code. I would then want to place a piece of code to handle the bank switching outside of this address space. But, how do I place one function at a certain location?[/quote']

 

There is a new segment in cc65 called something like LOCODE that solves this problem. I've never used it and I don't think the atari platform has an updated linker.cfg yet to implement this... Ask Ullrich about that on the cc65 list.

 

That aside, you can control where your C file is placed with #pragma codeseg("segment name"). That's how I did it with my project and I just split up my C files into one ram area or another so that they all fit and didn't interfere with $4000-7FFF.

 

Depends on how you intend to populate these banks.

You'd need a load routine to:

1) set area 0x4000 to the required bank

2) load code from file to 0x4000

3) swap back 0x04000 to main mem.

 

The linker will know what the functions address is, you just have to make sure the right bank is place when you make the call ;)

 

Maybe function wrappers to do the switching of memory?

 

I believe with the latest CC65 it maybe possible to make this transparent with macros or a single function (something like a 'far call') but I haven't investigated this yet. I will do because it comes in handy with cartridge development.

 

I haven't heard of anything like this, except that there is an extended memory API designed, but no drivers currently exist for the Atari platform. I don't think this would allow code to be called from extended memory banks though (it used a 256 byte window IIRC.)

 

Also you can create a segment such as:

MORE_CODE: load = RAM2, run = RAM3, type = wprot, define = yes;

However its normally up to you to relocate the block

 

That's my understanding too. You need to load the code into extended memory yourself and take care of switching banks yourself too.

Link to comment
Share on other sites

Thanks Shawn. Thanks Mark.

 

I now can compile and place code within the 0x2800-0x3fff or 0x8000-0xbc1f address spaces; however, the code no longer runs.

 

I have only placed one function in the 0x2800-0x3fff range, everything else is within 0x8000 and above.

 

 

I have surrounded this one function with:

 


#pragma codeseg("CODE2")

...

#pragma codeseg("CODE")

 

When I load the code in the emulator, the Atari hangs at PC address 0xc02c, which according to "Mapping the Atari" is the IRQ processor.

 

Here is my new link.cfg file:

 


MEMORY {

   ZP: start = $82, size = $7E, type = rw, define = yes;

   HEADER: start = $0000, size = $6, file = %O;

   RAM: start = $8000, size = $3c1f, file = %O;    # $8E1F: matches upper bound $BC1F

   RAM28: start = $2800, size = $1800, file = %O;    # $1800: matches upper bound $3FFF

   #BANK: start = $4000, size = $4000, type = ro;

   SECHDR: start = $0006, size = $4, file = %O;

}

SEGMENTS {

   EXEHDR: load = HEADER, type = wprot;

   CODE: load = RAM, type = wprot, define = yes;

   BSS: load = RAM, type = bss, define = yes;

   CHKHDR: load = SECHDR, type = wprot;

   CODE2: load = RAM28, type = wprot, define = yes;

   DATA: load = RAM, type = rw;

   RODATA: load = RAM, type = wprot;

   ZEROPAGE: load = ZP, type = zp;

   AUTOSTRT: load = RAM, type = wprot, define = yes;

}

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__ = $200; # 512B stack (was 2K stack)

}

 

I have linked this file in order to enable the second load...

 


   .import __CODE2_LOAD__, __AUTOSTRT_LOAD__


; CHUNK HEADER

   .segment "CHKHDR"

   .word __CODE2_LOAD__

   .word __AUTOSTRT_LOAD__ - 1

   .code

   .reloc



 

 

Any ideas?

 

I am working on a full color VT100/VT52 terminal program. (Well, okay it currently handles 8-color text, 8 color background, 40 columns with a virtual 80 column window.) This is close to working, but it currently only grabs input from the keyboard. I have other code for running the XIO commands to control the R:device, but I need to get this extra address space working before I can integrate this together.

 

Thanks again for the help....

 

Chris

Link to comment
Share on other sites

I have surrounded this one function with:

 


#pragma codeseg("CODE2")

...

#pragma codeseg("CODE")

 

Can you surround just the code you want with these or does it affect the whole file? The docs don't tell me much...

 

When I load the code in the emulator, the Atari hangs at PC address 0xc02c, which according to "Mapping the Atari" is the IRQ processor.

 

Have you created a map file during your compile and link? It tells you where in memory everything got put and how much of each segment/ram area is used.

 

Any ideas?  

 

I am working on a full color VT100/VT52 terminal program.  (Well, okay it currently handles 8-color text, 8 color background, 40 columns with a virtual 80 column window.)  This is close to working, but it currently only grabs input from the keyboard.  I have other code for running the XIO commands to control the R:device, but I need to get this extra address space working before I can integrate this together.

 

Thanks again for the help....

 

Take a look at the platform specific docs on the www.cc65.org site. There are some memory structures you can't change the location of unless you write your own crt0.s file. Specifically the C-stack will be at MEMTOP and grow down toward your program, and the HEAP will be at the end of your program and grow upward toward the C stack. Do you think your problems are caused by overwriting one of these structures maybe?

Link to comment
Share on other sites

Can you surround just the code you want with these or does it affect the whole file?  The docs don't tell me much...

 

You can definitely surround just the code you want to place within the segment. I have taken a look at both the .map file and the assembly output and it has only placed the one function within the CODE2 space (for a total of 0xe6 bytes).

 

Take a look at the platform specific docs on the www.cc65.org site.  There are some memory structures you can't change the location of unless you write your own crt0.s file.  Specifically the C-stack will be at MEMTOP and grow down toward your program, and the HEAP will be at the end of your program and grow upward toward the C stack.  Do you think your problems are caused by overwriting one of these structures maybe?

 

I did have problems running out of room, once the code started to grow to about address 0xa900. I believe that this was because I was starting to overwrite heap/stack. However, I do not think that this is problem now, since I have optimized the code and made sure that it would all work from the CODE (0x8000-0xBC1F) address space, first. Then, I tried to break it up and place one function in the CODE2 space. Putting anything in CODE2 doesn't work, for some reason... I just hang the machine at address 0xc02c. :? :x :ponder: (okay, over-use of the emoticons... :D )

 

--Chris

Link to comment
Share on other sites

Can you place a BREAK xxxx in your emulator for the _main function?

When the debugger hits this then check that your routine in CODE2 is

actually there. The c02c loop is a symptom of unhandled interrupts, maybe a BRK, making either a crash or infinite loop.

 

I think your fault lies in the MEMORY section. SECHDR should occur before RAM28 as this is the order in which things appear in your object file.

 

Good luck,

Mark

Link to comment
Share on other sites

Can you place a BREAK xxxx in your emulator for the _main function?

When the debugger hits this then check that your routine in CODE2 is

actually there. The c02c loop is a symptom of unhandled interrupts, maybe a BRK, making either a crash or infinite loop.

 

Ah, I did not know that the BREAK command existed in the emulator. That is cool (and useful). I tried setting the BREAK command at the address of _main, but the computer never reaches _main, so I think that the problem lies before that. I have managed to set break-points for SpartaDOS and that seems to work... hmm...

 

I think your fault lies in the MEMORY section. SECHDR should occur before RAM28 as this is the order in which things appear in your object file.

 

I have tried re-ording the MEMORY section so that SECHDR is before RAM28, but that has no effect. :(

 

could this be dependent on the version of CC65 that I am using (2.9.5)?

 

Thanks,

Chris

Link to comment
Share on other sites

Here is my new link.cfg file:

 


MEMORY {

   ZP: start = $82, size = $7E, type = rw, define = yes;

   HEADER: start = $0000, size = $6, file = %O;

   RAM: start = $8000, size = $3c1f, file = %O;    # $8E1F: matches upper bound $BC1F

   RAM28: start = $2800, size = $1800, file = %O;    # $1800: matches upper bound $3FFF

   #BANK: start = $4000, size = $4000, type = ro;

   SECHDR: start = $0006, size = $4, file = %O;

}

SEGMENTS {

   EXEHDR: load = HEADER, type = wprot;

   CODE: load = RAM, type = wprot, define = yes;

   BSS: load = RAM, type = bss, define = yes;

   CHKHDR: load = SECHDR, type = wprot;

   CODE2: load = RAM28, type = wprot, define = yes;

   DATA: load = RAM, type = rw;

   RODATA: load = RAM, type = wprot;

   ZEROPAGE: load = ZP, type = zp;

   AUTOSTRT: load = RAM, type = wprot, define = yes;

}

 

I just noticed that your DATA and RODATA segments are in your binary file after the code2 section. Either move them up into the RAM section of your segments list, or put them into RAM28.

 

I'm pretty sure that those two sections (well, at least RODATA) gets written into the executable file. Check out your binary with a hex editor to make sure everything is in the right place.

Link to comment
Share on other sites

I just noticed that your DATA and RODATA segments are in your binary file after the code2 section. Either move them up into the RAM section of your segments list, or put them into RAM28.

 

I'm pretty sure that those two sections (well, at least RODATA) gets written into the executable file.  Check out your binary with a hex editor to make sure everything is in the right place.

 

Thanks Shawn! That's got it! I moved RODATA and DATA to RAM28, and it started working. Alright, now I am back on track... and I have more room to add code.

 

I wish that the RAMBO and the COMPY upgrades were compatible with ANTIC only accesses to the extended memory. If this were the case, then I would have just loaded my code into the whole range from 0x2800 to 0xbc1f, and tell ANTIC to use the extended banks for Screen data. But, I don't think that the "after-market" memory upgrades allow this feature. :(

 

Again, thanks Shawn and thanks Mark, you guys saved the day... :)

 

Cheers,

Chris

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...