The Basics ********** Bitfields ========= Natu uses `distinct types `__ for cases where several values are bit-packed into a single integer. These are documented similarly to object types: .. raw:: html
type Color = distinct uint16

A 15bpp BGR color value. The red, green and blue components can be accessed via the following fields:

Field

Type

Bits

Description

r

int

0-4

Red component

g

int

5-9

Green component

b

int

10-14

Blue component

Fields for these types are implemented as getters and setters, so you use them like so: .. code-block:: nim var c = Color(0) # initialise to binary zero. c.r = rand(0, 31) # assign a random red value. # c.g += 10 # not allowed. :( See the :doc:`bits` module if you would like to create such bitfields yourself. I/O Registers ============= Many modules expose "memory-mapped I/O registers". These are special variables which are used to interact directly with the hardware. Usually they are implemented as bitfields (but may sometimes be plain integer types or sets). For example: .. code-block:: nim dispcnt.blank = true # Turns the screen white! Some registers are **read-only** -- these are defined using `let` instead of `var`. Others are **write-only**, and attempting to read from them will give you a garbage result, e.g. .. code-block:: nim bgofs[0].x = 123 printf("%d", bgofs[0].x) # prints 1042 !!! For the registers which are bitfields, the macros `init` and `edit` are available. These can be used to set multiple fields at once, which is more efficient than setting fields individually: .. code-block:: nim dispcnt.init(mode = 4, bg2 = true) For any fields that you didn't specify, `init` will reset them to zero, while `edit` will leave them unchanged. Naturally `edit` is unavailable for write-only registers, since it would have to read the register in order to preserve the fields. .. _debugging: Using a debugger ================ It's recommended to test your game in mGBA using the `.elf` file instead of the `.gba` ROM, as this will make debug information (names of functions & variables, line numbers etc.) available to you. When your game encounters an assertion failure, out-of-range or out-of-bounds error, you will see a blue screen like this: .. image:: media/outofbounds.png :align: center Upon reaching this screen, the game deliberately enters an infinite loop, allowing you to attach a debugger to inspect the state of the program when the error occurred. To do this in mGBA, go to *"Tools -> Start GDB Server"* and click *"Start"*. Now open a terminal and use `arm-none-eabi-gdb` to read the symbols from your `.elf` and join the remote debugging session: .. code-block:: console $ arm-none-eabi-gdb -q YourGame.elf -ex "target remote localhost:2345" In the GDB session, you'll find the following commands useful: * `bt` (backtrace) - view a stack trace. * `frame ` - switch to a stack frame. * `info locals` - display the local variables in the current stack frame. * `print ` - display the value of a variable. .. note:: The debug info is based on the mangled names from the C code generated by Nim, making it somewhat difficult to read. It's possible to get used to it with a bit of practise. You can also pass the `--linedir` option to the Nim compiler, allowing you to step through lines of Nim code instead of C code. However, the accuracy of the reported line numbers isn't fantastic. You can also use GDB to hunt down more elusive problems such as logic errors and messy crashes. In these cases, you can use a shell script to launch mGBA in debug mode with the ``-g`` flag, put it into the background, then immediately connect to it (after a small delay to give it time to start up): .. tab:: Linux .. code-block:: console $ mgba-qt -g YourGame.elf & $ sleep 2 $ arm-none-eabi-gdb -q YourGame.elf -ex "target remote localhost:2345" .. tab:: Windows .. code-block:: console $ start "" /B "C:\Program Files\mGBA\mGBA.exe" -g YourGame.elf $ sleep 4 $ arm-none-eabi-gdb.exe YourGame.elf -ex "target remote localhost:2345" For such problems you will also find the following to be useful: * `b :` - set breakpoint - pause when a given line is reached. * `watch ` - set watchpoint - pause when a value changes in memory. * `s` - step forwards by 1 statement, going into any subroutines. * `n` - advance to next statement, skipping over any subroutines. * `c` - continue execution (use `Ctrl + C` to pause again). For more info on how to use GDB, I recommend `Beej's Quick Guide to GDB `_ and the `GDB Cheat Sheet `_ on DuckDuckGo (based on this `reference card `_). Caveman debugging ================= In many cases, using a step debugger is overkill, and you can get all the information you need via simpler means: * If the problem is graphical, consider what the tile, map, palette & sprite viewers in mGBA ought to be showing, and check them to see how they differ from your expectations. * Use `printf` from the :doc:`mgba` module: * Dummy strings can help to you know if a line of code is actually being reached at all. * To print an integer, use the ``%l`` format specifier. * To print a pointer, use ``%08X``.