Project Organization

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Project Organization

Post by Celius »

So I have mainly one big project I'm working on, and it's going pretty well in some spots, and not so well in other spots. Having no real training on game programming, and starting out on the NES, this whole time I haven't been keeping my projects organized like a game designer should.

Until a couple weeks ago, I had my projects all located at a pretty long directory (in a folder in a folder in a folder in a folder in a folder on my desktop). So recently, I moved my game projects to C:\Game Projects.

So all of my NES projects are in C:\Game Projects\NES. Then it branches out to the projects, so all the stuff for ChateauLevania would be at C:\Game Projects\NES\ChateauLeVania. This is a much shorter directory than it was before.

Then comes the part where I might be messy (I don't know what other people's projects are organized like, so I could be wrong).

I have WLA-DX, and a main asm file with some other asm files which are basically lists of .include statements. Luckily, I've figured out how to use relative include directories so in C:\Game Projects\NES\ChateauLeVania" I could type:

.incdir "Data"

To change the include directory to "C:\Game Projects\NES\ChateauLeVania\Data". The advantage of this is that I can move the project folder to anywhere without having it looking up a directory that doesn't exist, because it's relative to the working directory.

So then I have "Code" and "Data" folders. The "Code" folder branches out to a couple more folders, but the big folders it branches to are "Window" and "Game World". In the "Window" folder are writes to all PPU/APU registers, where all the code that puts stuff on screen/makes noise is. The "Game World" folder contains all the code that's just game logic and whatnot. There's also a folder that contains useful macros like SetScroll and things that I'll use all the time. Of course, the folders in the "Code" folder have folders in them for specific subgroups and stuff.

"Data" is set up the same way. It contains all game data like maps and graphics in different folders. There's also AI which is made of code, but AI to me is data, regardless of whether or not it contains 6502 instructions.

So I ask, how are game projects usually set up? And is the way I'm going about it sloppy? Do I have too many folders, too little, or should I be going about it in a different way?
User avatar
Sivak
Posts: 316
Joined: Tue Jul 17, 2007 9:04 am
Location: Somewhere
Contact:

Post by Sivak »

Organize with whatever works best. I mainly make directories for data that has many parts to it.

I have directories for Level and tileset data, enemy data, music, and CHR data. Everything else seems general enough to stick in the root directory.

But there's not really any standard.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

I suppose there really isn't a standard, but I imagine people take more advantage of the assembler than I do. I seriously do almost nothing with the assembler. The only asm file that is part of the command line is main.asm. It takes header.bin and slaps it onto the beginning of main.asm assembled. Of course, main essentially includes lots and lots of files.

Should I be using my assembler to link more things together rather than them being linked by .include statements? I think I was reading a while ago that when making big programs in C, you want to link files together in the command line and not with .include statements.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

Celius wrote:Should I be using my assembler to link more things together rather than them being linked by .include statements?
Assembling to object files and then using the linker ('ld65' in CC65 or whatever it's called in WLA-DX) can improve encapsulation by hiding symbols that don't need to be used outside of a translation unit, which allows a programmer to keep track of fewer side effects. In addition, a build tool like GNU Make can be set to recompile only those object files whose corresponding source code has changed.

Another thing I do in newer projects (RAC, Lockjaw) is put my source files in src/ and object files in obj/nes/ (or obj/win32/ or obj/gba/ or obj/ds/), and the makefile can be written to toss the files where they go.
Celius wrote:I think I was reading a while ago that when making big programs in C, you want to link files together in the command line and not with .include statements.
True. That's one of the things the regulars on gbadev.org pound into newbies' heads, and for good reason.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Is there any specific reason that you really don't want to use .include statements in a big C program in particular?
User avatar
Banshaku
Posts: 2404
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Post by Banshaku »

Celius wrote:Is there any specific reason that you really don't want to use .include statements in a big C program in particular?
It depends how you look at it. "Technically", C programs have include statement but not in the same way that WLA do it.

In the case of WLA (and other assembler that uses include statement), it's like if you copy/pasted the code directly in your main file: so you have basically one huge file at the end. In the case of C, it will include a header file that will tell what the file have access to.

The problem with includes is function and variable have global scope: this mean function have access to everything. In assembler (and maybe in 6502 on the nes), this may seems less an issue because the amount of memory is limited and you may know about all the address range etc but sometime there is some variable that you don't want function A to have access to: only function B should be able to access it.

By creating modules like with CC65 (maybe WLA can do it), you can decide the scope of your variables and function. This way, you could have a variable , let say, myCounter in module A and only module A could read it. So module B will not see it, reducing the risk that some day module B modify the content of a variable that it's not supposed to. And you could have a helper function for A that only A should call but not B since it does some specific job for A only.

Put it simply, you want to give scope to your variables/function to reduce the amount of possible future coding error. By putting your myCounter in an include, anybody can use it. this is maybe not what you want in the first place. By defining the scope, the variable and/or function will be accessible in that scope only. In a C program, if the module is very huge, the change of someone using that global variable and putting the wrong data by accident is bigger so you have to be careful about who access what (especially if you're many people working on the same project).

If you want to see an example of scoping, you could always look at my old sample I made long time ago. this could give you some idea on how to do it. But be warned that I'm using that sample for learning 6502 and to try to find (like you're doing right now) how to put things in the right order for that platform. So you don't have to follow what I do exactly, just reading it may give you some ideas on how to define your own way of organizing your project.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

In WLA, this is how I simulate local variables:

.DEFINE Counter ZTempVar1
.DEFINE XHolder ZTempVar2
function:
lda blah
cmp blah
bcc whatever
...
do something with Counter and XHolder
...
rts
.UNDEFINE Counter, XHolder

WLA allows you to define the values of variables for a certain section of code. By the way, ZTempVar1 is the first temporary variable in Zero Page of the section I assigned to them. It's basically a local variable that any routine can use as a counter, or just somewhere to save a value temporarily.

Once you Undefine them, you can use the names "Counter" or "XHolder" anywhere else in the code. This is really handy, because it's nice to have really really general names for some things.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

Well, I have the sound code (with it's music and SFX data) assembled separately in WLA-DX than the rest of the game, I guess that makes 2 modules.
Until not so long I used to have one really big "main.asm", but evenutally it became really tedious finding stuff in it, so I split it in smaller asm files with .include directives (it's still quite big but it's much easier to find stuff in it).

However, it seems I can use labels from main.asm in sound.asm and vice-versa without using any special import or export commands, so even if they are assembled separately maybe they're still not 2 different modules. Anyway I'm pretty sure WLA-DX is much less flexible than CA65 when it comes to that, but there is other things that WLA can do which are usefull, and that maybe CA65 can do too but I'm less sure (it has good provisions for ROM and RAM bankswitching).

I can never use local variables in assembly because that's just not a possible thing without having a C-like stack, which is ackward to implement and ineficient. But doing this sure makes programming more structured and easier.
It's also great to be able to definie variables and be sure it may not be erronously overwritten by a part of the programm that isn't supposed to, but again in assembly all code have acess to everything so there is no real way to go arround that. (even if labels aren't acessible, you could just overwrite vraibles erounously with indexed or indirect adressing).
Also I guess the big advantage is to be able to reuse the same names again (something genreal like Counter) in many different routines, and all the counters are separate in real memory. In assembly you're typically forced to use the same counter (but then you can't use it more than once a time), or to have something like Counter1, Counter2, .... up to how many you need at the same time, which is annoying.
Useless, lumbering half-wits don't scare us.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Yeah, the main bad thing about my code and using tempvars is that I can really only go to one function level, meaning I can't go into a function when I'm in a function. This is because in all of my routines, I use random tempvars that aren't accessed by a stack or anything. But I can still work with it. No math routines touch tempvars; they use variables that are strictly for them.

There's always the real hardware stack to do stuff with. Though I don't feel it's very safe to use some times, because if you push too much on it, some stuff maybe like addresses will get destroyed. Though this will only happen if you push like 200 bytes on and you've jumped to 30 levels of subroutines (JSR in JSR 30 times). Oh, and not to forget other things that are put on the stack. But in my case, I have 16 local variables (one for the game loop and one for NMI so there aren't any RAM conflicts). So I could really be using some of them in one routine, and if I need to go to a function which uses them also, then all I have to do is:

lda ZTempVar1
pha
lda ZTempVar2
pha

jsr Function
tax ;Returns with a value in A

pla
sta ZTempVar2
pla
sta ZTempVar1

The advantage of using tempvars in Zero Page is that they are really fast. So pla sta ZTempVar only takes 7 cycles to execute. However, in the long run this is a rather slow method (especially if you're in a loop). Then there's always just using the stack without any other RAM. But that has many disadvantages.

Most of the time, I'm not using all 16 tempvars. So I usually assign functions to use first ZTempVar1, ZTempVar2, then ZTempVar3, etc. all the way to however many it needs. So if a function only uses 5 of those variables, and I'm in a subroutine that needs to be using tempvars, I can use ZTempVar6-ZTempVar16 and still go to that function without worrying about RAM conflicts.
User avatar
Sivak
Posts: 316
Joined: Tue Jul 17, 2007 9:04 am
Location: Somewhere
Contact:

Post by Sivak »

I was going back over this and was looking at the linking. I recall doing this for the stuff in my ASM class with MASM.

The only thing is that with the .include statements, everything seems to work just fine. Say you have your main file and then you have:

Code: Select all

.include "file1"
.include "file2"
File2 would just start right where file1 left off. Is linking assembler specific?
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

I know, that's one of my main issues with using .include statements. There's always the .org statment to force it to start at a certain location:

.include "File1"
.org $C800
.include "File2"

Though for some reason, WLA seems to act really stupid when you try and just do .org $C800, it's all like "Can't org in a section" or something, and it's really stupid.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

It's not stupid, it's because you're tring to org in a section, which doesn't make sense.
Sections are blocks of consectutive bytes you never wants to be taken apart. They are (likely) the equivalent of a module in CA65 (altough I'm not entierely sure).
It makes no sense to put a .org inside it.
Useless, lumbering half-wits don't scare us.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Is it possible to do away with sections all together? I think you should be able to specify where a certain piece of code is starting without having to start new sections and stuff.

Say I have some tables that are each $80 bytes or less that I want to start at $8680, but I want them to each start on multiples of $80, so I never cross a page boundary. So it would be really nice to be able to do something like this:

.org $8680
;define table 1
.org $8700
;define table 2
.org $8780
;define table 3
etc.

Like I said, some of them may be less than $80 bytes, but they do not exceed $80 bytes. It would be really handy if you could just specify where the following code/data begins without dealing with sections.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

You can't with WLA, but what's the big deal of dealing with section ? Place each table into separate section, and align them to $80 and you're done.
Useless, lumbering half-wits don't scare us.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

It really isn't that big of a deal, you're right. But I just think it'd be quicker to type .org $8680 instead of the stuff to close a section and open a new one. But this isn't that big of a deal.

For project organization, it's possible to have one file that organizes everything without doing anything special with the command line. It could look like this:

Code: Select all

.bank 8 SLOT 1
.orga $C000
.section "FixedBank" FORCE
.incdir "IncludeFiles"
.include "FixedInclude.asm"
.ends

.bank 7 SLOT 0
.orga $8000
.section "MapData" FORCE
.incdir "IncludeFiles"
.include "MapInclude.asm"
.ends
...

.bank 1 SLOT 0
.orga $8000
.section "CHRRAM1" FORCE
.incdir "IncludeFiles"
.include "CHR1Include.asm"
.ends
.incdir makes the include directory (workingdirectory)\IncludeFiles.

This file points to files which include all the necessary data/code for particular banks. So see in Bank 1, CHR1Include.asm is included. This file would probably look like:

Code: Select all

.incdir "Data\Graphics"
.incbin "CHR1.chr"
.incbin "CHR2.chr"
This way, all you'd have to assemble is this one file that bundles it all together. Though it might be too organized where it's not really intuitive, but it makes the working directory really uncluttered.
Post Reply