Page 1 of 1

CC65: Bank switching / Using memcpy for array

Posted: Tue Oct 24, 2017 12:37 pm
by bngrybt
I feel like this should be simple, but I've been banging my head against the wall for 2 days with it.

I'm making a game using mapper 11. I only have two 32k PRG banks, one for the main game engine and the other is used to store a bunch of arrays containing 96 bytes of level data. Everything is basically working like I want it to, except I can't use memcpy to copy the array. Here is the code I have at the same address in PRG0 and PRG1

Code: Select all

void Load_Array(void) {
	do_Switch = 1;
	while (do_Switch) {
		new_bank = PRG_bank + CHR_bank;
		change_reg(new_bank);			// Call to bank switching routine
		do_Switch = 0;					// Break the loop so we can return
	}

}

// At the same address in PRG1
void Load_Array1(void) {
	do_Switch = 1;
	while(do_Switch) {	
		new_bank = PRG_bank + CHR_bank;
		change_reg(new_bank);
		
		memcpy(L_MAP, All_Levels[level], 96);

		PRG_bank = 0x00;		// Send us back to PRG0
	}
}
If I use this, the game resets. I'm guessing that what is happening is the code for memcpy doesn't exist in PRG1, and the first code the game encounters is the reset code, which switches back to PRG0 and resets (as it should). I've tried using a for loop instead of memcpy, but I have the same problem. I can copy each byte individually, but that's it.

Code: Select all

	// I tried a for loop, but it also doesn't work.
	for (A = 0; A < 96; ++A) {
		L_MAP[A] = All_Levels[level][A];
	}
	
	// Even this doesn't work.
	L_MAP[A] = All_Levels[level][A];

	// This works, but is disgusting.
	L_MAP[0] = All_Levels[level][0];
	...
	L_MAP[95] = All_Levels[level][95];
Obviously this is hardly ideal, but at least it does show that I'm switching PRG banks successfully. Right now I just have extremely ugly, bloated code that I want to get rid of. I'm guessing that I'll have to write something in assembly that will copy data into my L_MAP array, but I'm having trouble wrapping my head around it for some reason.

Re: CC65: Bank switching / Using memcpy for array

Posted: Tue Oct 24, 2017 1:20 pm
by thefox
The compiler has a set of runtime library routines that it will sometimes insert calls into. You have no control when and if such calls are made. If you turn on optimizations, there will probably be less calls (and more inlining), but you can't count on there being no calls. Thus, you must make sure that these library routines are always banked in. In your case, you have to (in whatever way) duplicate them in both of your 32 KB banks, at equivalent addresses. By default they go in the CODE segment.

Two tips: 1) Look at the assembly listing of your code, 2) Use a debugger.

Re: CC65: Bank switching / Using memcpy for array

Posted: Tue Oct 24, 2017 1:24 pm
by dougeff
Since memcpy is a c library function... It is likely compiling into one bank and not the other.

My Solution. Compile the 2 banks separately, concatenate them together as a final step.

Re: CC65: Bank switching / Using memcpy for array

Posted: Tue Oct 24, 2017 1:53 pm
by bngrybt
dougeff wrote:Since memcpy is a c library function... It is likely compiling into one bank and not the other.

My Solution. Compile the 2 banks separately, concatenate them together as a final step.
I guess this probably sounds easiest. Looking at the assembly listing, it looks like using L_MAP[temp] = All_Levels[level][temp]; makes a call to pushax, which is probably what's messing things up. It'll save me from banging my head against the wall with assembly at least.

Re: CC65: Bank switching / Using memcpy for array

Posted: Tue Oct 24, 2017 7:00 pm
by bngrybt
So I tried concatenating two separate projects and while it does solve the memcpy problem, it actually used up 1k more space than my brute-force method. I think the added stability will be worth it in the future though, and I'm not exactly hurting for space. Just thought it was funny that it worked out that way though

Re: CC65: Bank switching / Using memcpy for array

Posted: Tue Oct 24, 2017 11:58 pm
by calima
Your transition, relying on the same address, is a bit fragile. I'd recommend writing the copy function in asm, and storing it in RAM, so you never have to jump to different code under the same function. You can achieve this by putting it to the DATA segment.

Re: CC65: Bank switching / Using memcpy for array

Posted: Wed Oct 25, 2017 10:55 am
by bngrybt
calima wrote:Your transition, relying on the same address, is a bit fragile. I'd recommend writing the copy function in asm, and storing it in RAM, so you never have to jump to different code under the same function. You can achieve this by putting it to the DATA segment.
I tried writing a copy function in asm, but I was having trouble wrapping my head around how to do it. I read some other topics about it, but for some reason I'm just finding it difficult to understand how to do it.

As of right now I have any code related to switching banks stored at $FF00. I'm not switching banks particularly often, only once at the start of each level to fetch the array data, so it's not like I'm doing it multiple times per cycle. Is it still something that could give me headaches later on?

Re: CC65: Bank switching / Using memcpy for array

Posted: Thu Oct 26, 2017 6:46 am
by calima
It'll probably work fine, but things like cc65 updates could break it.

You can cheat with cc65 to kickstart the asm. Use the memcpy version, and compile with -Ois and the memcpy call should be inlined.

Re: CC65: Bank switching / Using memcpy for array

Posted: Thu Oct 26, 2017 3:21 pm
by bngrybt
calima wrote:It'll probably work fine, but things like cc65 updates could break it.

You can cheat with cc65 to kickstart the asm. Use the memcpy version, and compile with -Ois and the memcpy call should be inlined.
I might give that a try. Since I have a fixed length for the arrays it could give me the opportunity to trim out some needless code.