Skip to content

Commit

Permalink
Cleanup in preparation for initial release (#93)
Browse files Browse the repository at this point in the history
* tte: remove metadata from top of file

* Remove "#ch-" anchor from the top of some chapters

* Replace manual TOC in a couple of chapters

* Rework intro to be more up-to-date

Removed diagram (it demonstrates the old tonc structure, not relevant now libtonc is included with devkitpro)

I changed the '3 parts' from text+code+bin to text+examples+libtonc, as that is the main split that readers have to understand today.

Instead of linking to zip files I now link to our git repos.

Removed prerequisites as they are covered in the setup chapter. The note about C programming seemed still important though so I moved that into a different place where it still works.

Updated disclaimer to remove bits that aren't true anymore

* Replace "tonclib" with "libtonc"

* Prevent the bit numbers from wrapping in register diagrams
  • Loading branch information
exelotl committed Dec 27, 2023
1 parent cf5083d commit aebd77f
Show file tree
Hide file tree
Showing 27 changed files with 101 additions and 122 deletions.
2 changes: 1 addition & 1 deletion content/affbg.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 12. Affine backgrounds {#ch-}
# 12. Affine backgrounds

<!-- toc -->

Expand Down
2 changes: 1 addition & 1 deletion content/affobj.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 11. Affine sprites {#ch-}
# 11. Affine sprites

<!-- toc -->

Expand Down
4 changes: 2 additions & 2 deletions content/asm.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 23. Whirlwind Tour of ARM Assembly {#ch-}
# 23. Whirlwind Tour of ARM Assembly

<!-- toc -->

Expand Down Expand Up @@ -1961,7 +1961,7 @@ m5_plot_thumb: @ Start of function definition
bx lr
```

The functions above show the basic template for functions: three lines of directives, and a label for the function. Note that there is no required order for the four directives, so you may see others as well. In fact, the `.global` directive can be separated completely from the rest of the function's code if you want. Also note the use of `.extern` to allow access to `vid_page`, which in tonclib always points to the current back buffer. To be honest, it isn't even necessary because GAS assumes that all unknown identifiers come from other files; nevertheless, I'd suggest you use it anyway, just for maintenance sake.
The functions above show the basic template for functions: three lines of directives, and a label for the function. Note that there is no required order for the four directives, so you may see others as well. In fact, the `.global` directive can be separated completely from the rest of the function's code if you want. Also note the use of `.extern` to allow access to `vid_page`, which in libtonc always points to the current back buffer. To be honest, it isn't even necessary because GAS assumes that all unknown identifiers come from other files; nevertheless, I'd suggest you use it anyway, just for maintenance sake.

And yes, these two functions do actually form functional mode 5 pixel plotters. As an exercise, try to figure out how they work and why they're coded the way they are. Also, notice that the Thumb function is only two instructions longer than the ARM version; if this were ROM-code, the Thumb version would be a whole lot faster due to the buswidth, which is exactly why Thumb code is recommended there.

Expand Down
8 changes: 4 additions & 4 deletions content/bitmaps.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 5. The Bitmap modes (mode 3, 4, 5) {#ch-}
# 5. The Bitmap modes (mode 3, 4, 5)

<!-- toc -->

Expand Down Expand Up @@ -251,7 +251,7 @@ void m3_fill(COLOR clr)
Now, note what I'm doing here: instead of treating VRAM as an array of 16-bit values which are appropriate for 16bpp colors, I'm using a 32-bit pointer and filling VRAM with a 32-bit variable containing two colors. When filling large chunks of memory, it makes no difference if I fill it in *N* 16-bit chunks, or ½*N* 32-bit chunks. However, because you only use half the number of iterations in the latter case, it's roughly twice as fast. In C, it's perfectly legal to do something like this (provided that strict aliasing is satisfied) and often actually useful. This is why it's important to know the principles of [data and memory](#sec-data). Also note that I'm using pointer arithmetic here instead of array indices. While the compiler generally make the conversion itself, doing it manually is still often a little faster. (When in doubt, read the assembly language that GCC generates.)
While this method is already twice as fast as the ‘normal’ method, there are actually much faster methods as well. We will meet these later, when we stop using separate toolkit files and start using tonclib, the code library for tonc. Tonclib contains the functions described above (only faster), as well as 8bpp variations of the `bmp16_` routines and interfaces for mode 4 and mode 5.
While this method is already twice as fast as the ‘normal’ method, there are actually much faster methods as well. We will meet these later, when we stop using separate toolkit files and start using libtonc, the code library for tonc. Tonclib contains the functions described above (only faster), as well as 8bpp variations of the `bmp16_` routines and interfaces for mode 4 and mode 5.
Below you can find the main code for *m3_demo*, which uses the `m3_` functions to draw some items on the screen. Technically, it's bad form to use this many magic numbers, but for demonstration purposes it should be okay. The result can be seen in {@fig:m3-demo}.
Expand Down Expand Up @@ -334,7 +334,7 @@ void generic_rect(int left, int top, int right, int bottom, COLOR clr)
}
```

This is the generic template for a rectangle drawing routine. As long as you have a functional pixel plotter, you're in business. However, business will be *very* slow in mode 4, because of the complicated form of the plotter. In all likelihood, it'll be so slow to make it useless for games. There is a way out, though. The reason `m4_plot()` is slow is because you have to take care not to overwrite the other pixel. However, when you're drawing a horizontal line (basically the `ix` loop here), chances are that you'll have to give that other pixel the same color anyway, so you needn't bother with read-mask-write stuff except at the edges. The implementation of this faster (*much* faster) line algorithm and subsequently rectangle drawer is left as an exercise for the reader. Or you can seek out *tonc_bmp8.c* in tonclib.
This is the generic template for a rectangle drawing routine. As long as you have a functional pixel plotter, you're in business. However, business will be *very* slow in mode 4, because of the complicated form of the plotter. In all likelihood, it'll be so slow to make it useless for games. There is a way out, though. The reason `m4_plot()` is slow is because you have to take care not to overwrite the other pixel. However, when you're drawing a horizontal line (basically the `ix` loop here), chances are that you'll have to give that other pixel the same color anyway, so you needn't bother with read-mask-write stuff except at the edges. The implementation of this faster (*much* faster) line algorithm and subsequently rectangle drawer is left as an exercise for the reader. Or you can seek out *tonc_bmp8.c* in libtonc.

:::warning VRAM vs. byte writes

Expand Down Expand Up @@ -961,7 +961,7 @@ Both of these have to do with the basic function of `memcpy()`, namely to be a f
1. When both source and destinations are word aligned.
2. When you are copying more than 16 bytes.

This is usually the case so I figured it'd be safe enough for the demos. There are also look-alikes in tonclib that do the same thing only better, namely `memcpy16()` and `memcpy32()`, but these are in assembly so I thought I wouldn't lay them on you so soon. Highly recommended for later though.
This is usually the case so I figured it'd be safe enough for the demos. There are also look-alikes in libtonc that do the same thing only better, namely `memcpy16()` and `memcpy32()`, but these are in assembly so I thought I wouldn't lay them on you so soon. Highly recommended for later though.

On a related subject, there is also `memset()` for memory fills. Be careful with that one, because that will *only* work with bytes. Tonclib also includes 16- and 32-bit versions of this routine, but also in assembly.

Expand Down
2 changes: 1 addition & 1 deletion content/dma.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ In short: DMA fills need addresses, not direct values. Globals will always work,

### DMA; don't wear it out {#ssec-func-use}

DMA is fast, there's no question about that. It can be up to [ten times as fast](text.html#ssec-demo-se2) as array copies. However, think twice about using it for every copy. While it is fast, it doesn't quite blow every other transfer routine out of the water. CpuFastSet() comes within 10% of it for copies and is actually 10% faster for fills. The speed gain isn't that big a deal. Another problem is that it stops the CPU, which can screw up [interrupts](interrupts.html), causing seemingly random bugs. It does have its specific uses, usually in conjunction with timers or interrupts, but for general copies, you might consider other things as well. CpuFastSet() is a good routine, but tonclib also comes with `memcpy16()/32()` and `memset16()/32()` routines that are safer than that, and less restrictions. They are assembly routines, though, so you'll need to know how to assemble or use libraries.
DMA is fast, there's no question about that. It can be up to [ten times as fast](text.html#ssec-demo-se2) as array copies. However, think twice about using it for every copy. While it is fast, it doesn't quite blow every other transfer routine out of the water. CpuFastSet() comes within 10% of it for copies and is actually 10% faster for fills. The speed gain isn't that big a deal. Another problem is that it stops the CPU, which can screw up [interrupts](interrupts.html), causing seemingly random bugs. It does have its specific uses, usually in conjunction with timers or interrupts, but for general copies, you might consider other things as well. CpuFastSet() is a good routine, but libtonc also comes with `memcpy16()/32()` and `memset16()/32()` routines that are safer than that, and less restrictions. They are assembly routines, though, so you'll need to know how to assemble or use libraries.

## DMA demo : circular windows {#sec-demo}

Expand Down
4 changes: 2 additions & 2 deletions content/edmake.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# E. Make via editors {#ch-}
# E. Make via editors

<!-- toc -->

Expand Down Expand Up @@ -130,7 +130,7 @@ make -f tonc.mak build

Why? Because we won't be using the standard VC make (NMAKE), but the GNU make (make). Why? Because it's free, platform-independent and usually comes with the devkit, making your project more portable, is more powerful and better documented as well. Why? Because ... just because, OK? This is the command that is executed when you press Rebuild (F7). The -f flag says which makefile to use. Inside a makefile you can have multiple sub-projects; in this case the one called build is the active one.

The other settings aren't important for our purposes so leave them as they are. Yes, the output filename too; the makefile will take care of that. By the way, note that the workspace in @fig:msvc-make-cfg shows three projects: tonc and tonclib for actual tonc stuff, and a vault project. A standard practice of mine to have one vault project where I can store source-files I don't want compiled but do want to have available for reference (such as templates and examples). All my workspaces have one and I can highly recommend them.
The other settings aren't important for our purposes so leave them as they are. Yes, the output filename too; the makefile will take care of that. By the way, note that the workspace in @fig:msvc-make-cfg shows three projects: tonc and libtonc for actual tonc stuff, and a vault project. A standard practice of mine to have one vault project where I can store source-files I don't want compiled but do want to have available for reference (such as templates and examples). All my workspaces have one and I can highly recommend them.

<div class="cpt" style="width:584px">

Expand Down
10 changes: 5 additions & 5 deletions content/first.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Naturally, this will expand the total lines of code a bit. Quite a bit, in fact.
//
// === NOTES ===
// * This is a _small_ set of typedefs, #defines and inlines that can
// be found in tonclib, and might not represent the
// be found in libtonc, and might not represent the
// final forms.


Expand Down Expand Up @@ -190,7 +190,7 @@ I've also added a <dfn>conceptual typedef</dfn>. While it's true that, in princi

To be able to work directly specific addresses in memory, you'll have to cast them to pointers or arrays and work with those. In this demo's case, the addresses we're interested in are `0600:0000` (VRAM) and `0400:0000` (the display control register). In the first demo I did the casts manually, but it's better to use names for them so that you don't have to remember all the numbers and also because nobody else would have any clue to what's going on.

For the IO registers I'm using the official names, which are recognized by all parties. The display control is known as REG_DISPCNT, and is defined as the word at `0400:0000`. Note that neither the name nor the type are set in stone: you could as easily have called it “BOO” and even used a halfword pointer. The full list of register #defines can be found in tonclib's *regs.h*.
For the IO registers I'm using the official names, which are recognized by all parties. The display control is known as REG_DISPCNT, and is defined as the word at `0400:0000`. Note that neither the name nor the type are set in stone: you could as easily have called it “BOO” and even used a halfword pointer. The full list of register #defines can be found in libtonc's *regs.h*.

For those who aren't as familiar with pointers as you should (boy, are you gonna be in trouble <kbd>:P</kbd>), here is the structure of the REG_DISPCNT #define. I'm using `vu32` as a typedef for ‘volatile u32’ here.

Expand Down Expand Up @@ -229,15 +229,15 @@ A similar procedure is carried out for VRAM, only this is still in its pointer f
The IO registers (not to be confused with the CPU registers) are a collection of switches in the form of bitfields that control various operations of the GBA. The IO registers can be found in the `0400:0000` range of memory, and are usually clumped into words or halfwords according to personal preference. To get anything done, you have to set specific bits of the IO registers. While you can try to remember all the numerical values of these bits, it's more convenient to use #defines instead.
The toolbox header lists a number of the #defines I use for REG_DISPCNT. The full list can be found in *vid.h* of tonclib, and the register itself is described in the [video](video.html) chapter. For now, we only need DCNT_MODE3 and DCNT_BG2. The former sets the video mode to mode 3, which is simplest of the 3 available [bitmap modes](bitmaps.html), and the latter activates background 2. Out of a total of four, bg 2 is the only one available in the bitmap modes and you have to switch it on if you want anything to show up. You have to admit that these names are a lot more descriptive than `0x0003` and `0x0400`, right? <!-- If not, you are _banned_ from programming :P -->
The toolbox header lists a number of the #defines I use for REG_DISPCNT. The full list can be found in *vid.h* of libtonc, and the register itself is described in the [video](video.html) chapter. For now, we only need DCNT_MODE3 and DCNT_BG2. The former sets the video mode to mode 3, which is simplest of the 3 available [bitmap modes](bitmaps.html), and the latter activates background 2. Out of a total of four, bg 2 is the only one available in the bitmap modes and you have to switch it on if you want anything to show up. You have to admit that these names are a lot more descriptive than `0x0003` and `0x0400`, right? <!-- If not, you are _banned_ from programming :P -->
I've also added a list of useful color defines, even though I'm not using them in *second.c*. They may or may not be useful in the future, though, so it's good to have them around.
<br>
Creating the register #defines is probably the biggest part of header files. As a rough estimate, there are 100 registers with 16 bits each, so that would be 1600 #defines. That's a lot. The exact number may be smaller, but it is still large. Because the names of the #defines in and of themselves aren't important, you can expect different naming schemes for different people. I am partial to my own set of names, other older GBA coders may use PERN's names and more recent ones may use libgba's, which comes with devkitARM. Take your pick.
#### Macros and inline functions
You can also create #defines that work a little like functions. These are called <dfn>macros</dfn>. I'm not using them here, but there are plenty to be found in tonclib's headers. Like all #defines, macros are part of the preprocessor, not the compiler, which means that the debugger never sees them and they can have many hidden errors in them. For that reason, they have been depreciated in favor of [<dfn>inline functions</dfn>](http://gcc.gnu.org/onlinedocs/gcc-4.0.2/gcc/Inline.html). They have all the benefits of macros (i.e., integrated into the functions that call them so that they're fast), but are still function-like in syntax and resolved at compile time. At least that's the theory, in practice they're not *quite* as speedy as macros, but often preferable anyway.
You can also create #defines that work a little like functions. These are called <dfn>macros</dfn>. I'm not using them here, but there are plenty to be found in libtonc's headers. Like all #defines, macros are part of the preprocessor, not the compiler, which means that the debugger never sees them and they can have many hidden errors in them. For that reason, they have been depreciated in favor of [<dfn>inline functions</dfn>](http://gcc.gnu.org/onlinedocs/gcc-4.0.2/gcc/Inline.html). They have all the benefits of macros (i.e., integrated into the functions that call them so that they're fast), but are still function-like in syntax and resolved at compile time. At least that's the theory, in practice they're not *quite* as speedy as macros, but often preferable anyway.
One inline function I'm using is `m3_plot()`, which, as you may have guessed, is used to plot pixels in mode 3. In mode 3, VRAM is just a matrix of 16bit colors, so all we have to do to plot a pixel is enter a halfword in the right array element. `m3_plot()` looks exactly like a normal function, but because the ‘`static inline`’ in front of it makes it an inline function. Note that inlining is only a recommendation to the compiler, not a commandment, and it only works if optimizations are switched on.
Expand All @@ -258,7 +258,7 @@ Making use of the contents of *toolbox.h* makes the code of the demo much more u
The first line in `main()` sets a few bits in the display control, commonly known as REG_DISPCNT. I use `DCNT_MODE3` to set the video mode to mode 3, and activate background 2 with `DCNT_BG2`. This translates to `0x0403` as before, but this method gives a better indication of what's happening than entering the raw number. Using a variable-like #define instead of the raw dereferenced pointer is also preferable; especially as the latter is sure to wig out people new to C.

So how do I know what bit does what to create the #defines in the first place? Simple, I looked them up in [GBATEK](https://problemkaputt.de/gbatek.htm), the essential reference to GBA programming. For every IO register I use in these pages I'll give a description of the bits and a list of #defines as they're defined in tonclib. The formats for these descriptions were given in the [preface](intro.html#ssec-note-reg), and the table for REG_DISPCNT can be found in the [video chapter](video.html#sec-vid-regs).
So how do I know what bit does what to create the #defines in the first place? Simple, I looked them up in [GBATEK](https://problemkaputt.de/gbatek.htm), the essential reference to GBA programming. For every IO register I use in these pages I'll give a description of the bits and a list of #defines as they're defined in libtonc. The formats for these descriptions were given in the [preface](intro.html#ssec-note-reg), and the table for REG_DISPCNT can be found in the [video chapter](video.html#sec-vid-regs).

Actually plotting the pixels is now done with the inline function `m3_plot()`, which is formatted much the same way as every kind of pixel plotter in existence: 2 coordinates and the color. Much better than raw memory access, even though it works exactly the same way. The colors themselves are now created with an inline too: `RGB15` takes 3 numbers for the red, green and blue components and ties them together to form a valid 16-bit color.

Expand Down
Loading

0 comments on commit aebd77f

Please sign in to comment.