- NESICIDE developer
- Posts: 1097
- Joined: Mon Oct 13, 2008 7:55 pm
- Location: Minneapolis, MN
Shiru, very nice article! One thing I'd added to NESICIDE is the ability to see mixed-mode source during debugging. That helps -- I think, anyway -- people that want to develop in C see exactly what the C compiler generates for any particular C source line of code. Example:Shiru wrote:Read the article
Also, this statement:
is not really true. NESICIDE can do that. I've been stepping through AlterEgo at the C level for quite a while now. Thank you for such a polished and useful example of programming for NES in C.Shiru wrote:The problem is that there are no comfortable debuggers for C code compiled into 6502 assembly around (yet) - ones that allow to put breakpoints on random C lines and see what the variables contain at the moment. Usually you only have an assembly level debugger in some emulators, and it is not very helpful with compiled code.
Something I don't understand so well :
Then you write C code that is less portable and less readable, and more low level. I don't have anything against it but doesn't this kill the purpose of using C instead of assembly ?Due to very limited NES resources, such as CPU speed, RAM and ROM size, writting a proper, clean C code isn't very effective. To make it faster and shorter you have to do things that otherwise aren't considered acceptable. They are disable some of C advantages, making the code more low level and less structured.
There are suggestions that will make your code more effective, but certainly less readable:
Random example, you have a level map that is larger than 256 bytes, lets say 32x32, and need to get a value from it using two 8-bit vars, mx and my. So, for C code:
Code: Select all
Code: Select all
lda my sta ptr_l lda #0 sta ptr_h dup 5 asl ptr_l ;<<5 rol ptr_h edup lda ptr_l clc adc mx sta ptr_l lda ptr_h adc #0 sta ptr_h lda ptr_l clc adc #<map sta ptr_l lda ptr_h adc #>map sta ptr_h ldy #0 lda [ptr_l],y
In the past Tepples has stated that the Koei simulation games were written in C, and that is probably why they seem sluggish. I bet that we could do better.
I seriously considered making some sort of an RPG as my next nesdev project, and coding part of it in C, but I lack the time to take on such a project right now.
You'll run into the other problem with RPG battle logic in C - the resulting code will be huge, so the problem with bankswitching have to be solved somehow.
Not sure what exactly you're asking here. As long as the runtime functions (pusha/popa etc) are placed in a fixed bank it should work OK. The switching itself has to be done manually of course.mic_ wrote:Do you know if CC65 handles cross-bank calls well (linking symbols that have been compiled into separate banks)? This was quite a hassle with SDCC (Z80) and I ended up having to write some custom tools for it, and it still didn't work perfectly.
It doesn't happen automagically, but depending on the calling convention used, you may be able to write a bunch of stubs that make cross-bank calls using a trampoline in the fixed bank.mic_ wrote:Nice info and cleanly written.
Do you know if CC65 handles cross-bank calls well (linking symbols that have been compiled into separate banks)?
Let's say you split your code/data into separate banks which are compiled and linked separately (to avoid having duplicates of the same stuff for different combinations of banks) and then combined into a .nes file.Not sure what exactly you're asking here.Do you know if CC65 handles cross-bank calls well
bank0.c -> bank0.obj -> bank0.bin
bank1.c -> bank1.obj -> bank1.bin
header + bank0.bin + bank1.bin + ... -> game.nes
If bank0 wants to call a function in bank1, can you tell CC65 to resolve the address of that function without actually putting a copy of the function in bank0 as well? Using hardcoded addresses is a PITA, and using a proxy function at a fixed address to delegate calls isn't really that nice either IMO.
With SDCC I ended up writing a tool that parsed the .SYM files generated by the linker and it would output a header file with named function pointers for any given bank that other banks could use. It didn't work when there were cross-dependencies (bank X and bank Y both wanted to call eachother), so in some instances I still had to use hardcoded addresses.