This page will focus on reviewing important C/C++ concepts needed for DS development. If you don't know C/C++ (which you must), the recommended tutorial to follow is http://www.cplusplus.com/doc/tutorial/ because it is very well written and structured with lots of useful examples to the C standard libraries (but don't use the C++ standard library in Nintendo DS projects, it's too heavy and memory hungry!). Although it is a C++ tutorial, it mostly applies to programming in plain C as well.
NOTE: The standard PAlib project main file is called main.c. This DOESN'T allow you to use C++. If you want to use C++ renaming main.c to main.cpp does the trick, idem for other source code files.
If you're new to programming in C/C++, you should find it beneficial to spend at least a week getting to grips with the basics before moving on to learning PAlib.
You are always welcome to ask for help on the forums, but keep in mind that you may simply be pointed to a specific link that refers to the topics of interest to you.
Operators are programming language elements that tell a given language how to combine, compare or modify the values of an expression. An expression is a statement in which operators operate on values (e.g., 3 + 4 —the + operator is operating on the values 3 and 4).
(add, subtract, multiply, divide, and perform other arithmetic operations)
+ addition ++ increment (add 1) - when placed between 2 numbers, performs subtraction -- decrement (subtract 1) * multiply / divide >> bit shift right << bit shift left ( ) define precedence (operations within parentheses happen first) % modulus (computes the remainder of dividing two numbers) - when placed before a number, reverses the sign of a number (negation)
(compare two values and determine if the comparison is TRUE or FALSE)
< is less than <= is less than or equal to == is equal to >= is greater than or equal to > is greater than != is not equal to
(also known as “Boolean” Operators) (compare conditions and determine whether one condition exists, all conditions exists, or the conditions are different)
! (not) && (and) || (or)
(assign the value on the right to the var. or other container on the left)
= simple assignment += add two numbers (or concatenate two strings) and assign the result to the var. on the left -= subtract the value on the right from that on the left and assign the result to the var. on left *= multiply the two values and assign the result to the var. on left /= divide the left value by the right value and assign result to var. on left %= compute the modulus of two numbers and assign result to var. on left >>= bit shift the left value by the right value and assign the result to the left value (right) <<= bit shift the left value by the right value and assign the result to the left value (left)
This is a special type of “operator” that allows you to use if statements in a function call.
comparison ? true_actions : false_actions;
An example is:
return (x < 1) ? 0 : x;
Floating point operations are much more CPU intensive than integer operations on the NDS because of the lack of a FPU (floating point unit), so seek alternative methods where applicable, either integers or fixed point math.
Integral Types
| Type | Size | Unsigned | Signed | AKA |
|---|---|---|---|---|
| char | 8 bits | 0 → 255 | -128 → 127 | u8,s8 |
| short | 16 bits | 0 → 65 535 | -32 768 → 32 767 | u16,s16 |
| long | 32 bits | 0 → 4 294 967 295 | -2 147 483 648 → 2 147 483 647 | u32,s32 |
int is special and is typically dependent upon the architecture; i.e 16 bits on systems with 16 bit CPUs and 32 bit on those with 32 bit CPUs(NDS). If an absolute range is required, for portability reasons, use either short or long.
char is also special and is typically dependent upon the compiler; It may be signed or unsigned, so care must be taken when using them. If in doubt over which integral type to use, consider using int. libnds (and consequently PAlib) provides clearer aliases for these types, e.g u8 for an unsigned 8bit integer and s32 for a signed 32bit integer, where the format is [signedness prefix, s or u][followed by the number of bits].
In C/C++, the Modulo_operation(%) operation does not wrap-around as you'd expect on negative values. e.g
-2 % 10
does not result in 8, but -2; To yield the expected result, you must add 10(the divisor) to the result.
if (lv < 0) result = (lv % rv) + rv; else result = (lv % rv);
where lv(the dividend) is -2 and rv(the divisor) is 10.
Bit shift is an alternative to multiplication and division, which are CPU intensive. Basically this is how it works… the computer interprets numbers in 1s and 0s. Each number as we see it is a series of 1s and 0s in a row. This is called the binary system. For example, 1 in binary is… well… 0001; 2 is 0010; 4 is 0100; and 5 is 0101. As you can see, the number's value is determined by the place it holds and the value of each place is equal to the previous one multiplied by 2.
| table of comparison | |||||||||
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 512 | 256 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
You can see that, as you go left, each value is the previous value multiplied by 2. Thus 0001 is 1 and 0010 is 2. You can also have numbers like 0011 which is 3 ( 2 + 1 ) and 1010 which is 10 ( 8 + 2 ).
Now to the main point. Bit shifting is a trick to multiply or divide without multiplying or dividing. For example, to bit shift 0010 left by a factor of two is to move its '1' left two places to 1000 or 8. This is equivalent to multiplying by 4. For 0110 (6) bit shifted left 1, it will become 1100 (12). It works the other way too. 1010 (10) bit shifted right 1 is 0101 (5). This is where it is useful to us PAlibers. Division is CPU intensive, thus to have an alternative for division (bit shifting) is to be able to have really small increments for smoother sprite movements. To see how you would implement this in actual code, go here and keep in mind that the « and » are bit shifts.
Please also note it works similar way as with multiplying or dividing by 10, 100, 1000 etc. in your every-day life - you just move the comma or add/remove zeroes. The only difference is it uses powers of two instead of ones of ten.
Eg. 256»8 = 1
As noted above, char is not always signed, therefore, if you are planning to use the return value to indicate both error and result, it may be better to use int over char to account for any possible over/underflow.
PA_WaitForVBL() is a common way of creating a timer and this is how to calculate it:
Conversion:
VBLs per second = 60 approx
time = 180 (change it to lengthen or shorten the timer. We will loop this many times in our code. For now we'll make it 180 times.)
ts = time in seconds
(seconds to VBLs) time = ts * vbls per second → time = 3 * 60 → time = 180 VBLs
(VBLs to seconds) ts = time / vbls per second → ts = 180 / 60 → ts = 3 seconds
Example code:
int time = 180; int i; for(i = 0; i < time; i ++) // waits three seconds roughly. PA_WaitForVBL();
A consistent coding style (and structure) yields more readable and consequently less buggy code, which may be beneficial to you later or when help is needed. (see External Links)
Recommended C/C++ tutorial
Frequently asked questions in C - mostly advanced
Frequently asked questions in C++ - mostly advanced
Fixed Point Math – the NDS has no dedicated floating point unit
C++ reference - applies to C as well, with good examples
C reference – less examples, but may be better for function documentation
Linux/Torvalds CodingStyle – C
Google StyleGuide – C++