Skip to content

Commit 8066c6e

Browse files
authored
BCD (Part 2 lesson 6 or 7) (#115)
* chore: ignore Jetbrains IDE files * feat: decimal numbers introduction * feat: BCD introduction * feat: score variable introduction * feat: DDA explanation and score increase * feat: score increase on brick collision * chore: remove superfluous anchors from previous lesson * feat: digit tile set and score initialization * feat: updating the score board
1 parent e4e0863 commit 8066c6e

File tree

8 files changed

+980
-1
lines changed

8 files changed

+980
-1
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
/po/messages.pot
55
/galactic-armada/src/generated
66
/galactic-armada/dist/
7-
/galactic-armada/obj/
7+
/galactic-armada/obj/
8+
.idea

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
- [Input](part2/input.md)
3030
- [Collision](part2/collision.md)
3131
- [Bricks](part2/bricks.md)
32+
- [Decimal Numbers](part2/bcd.md)
3233
- [Work in progress](part2/wip.md)
3334

3435
# Part III — Our second game

src/assets/part2/img/bcd-tilemap.png

27 KB
Loading

src/assets/part2/img/bcd-tileset.png

1.77 KB
Loading

src/part2/bcd.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Decimal Numbers
2+
3+
Now that we can make the bricks disappear on impact, we should probably get some reward, like points!
4+
We'll start off with a score of 0 and then increase the score by 1 point each time a brick gets destroyed.
5+
Then we can display the score on a scoreboard.
6+
7+
## BCD
8+
9+
As we're stingy when it comes to memory use, we will only use one byte. There are different ways of saving and retrieving numbers as decimals, but this time we will choose something called "Packed Binary Coded Decimal" or packed BCD for short.
10+
11+
BCD is a way of storing decimal numbers in bytes, not using A-F, so $A would be 10 which consists of the digits 1 and 0.
12+
13+
Remember how bits, nibbles and bytes work? Go and have a look at the [Hexadeciamal](../part1/bin_and_hex.md) section if you need a reminder.
14+
15+
The "packed" part means that we pack 2 digits into one byte. A byte contains 8 bits and inside 4 bits we can already store numbers between `$0` (`%0000`) and `$F` (`%1111`), which is more than sufficent to store a number between 0 and 9.
16+
17+
For example the number 35 (my favorite Pokémon) contains the number 3 `%0011` and 5 `%0101` and as a packed BCD this is `%00110101`
18+
19+
## Calculating the score
20+
21+
Now let's start by defining a global variable (memory location) for the score:
22+
23+
```rgbasm,linenos,start={{#line_no_of "" ../../unbricked/bcd/main.asm:score-variable}}
24+
{{#include ../../unbricked/bcd/main.asm:score-variable}}
25+
```
26+
27+
And we'll set this to zero when initializing the other global variables.
28+
29+
```rgbasm,linenos,start={{#line_no_of "" ../../unbricked/bcd/main.asm:init-variables}}
30+
{{#include ../../unbricked/bcd/main.asm:init-variables}}
31+
```
32+
33+
Now we'll write a function to increase the score, right behind the `IsWallTile` function.
34+
Don't worry about the call to `UpdateScoreBoard`, we'll get into that in a bit.
35+
36+
```rgbasm,linenos,start={{#line_no_of "" ../../unbricked/bcd/main.asm:increase-score}}
37+
{{#include ../../unbricked/bcd/main.asm:increase-score}}
38+
```
39+
40+
Let's have a look at what's going on there:
41+
We set A to 1 and clear the carry flag
42+
We add the score variable (contents of memory location `wScore`) to a, so now A has our increased score.
43+
44+
So far so good, but what if the score was 9 and we add 1? The processor thinks in binary only and will do the following math:
45+
46+
`%00001001` + `%00000001` = `%00001010` = `$A`
47+
48+
That's a hexadecimal representation of 10, and we need to adjust it to become decimal. `DAA` or "Decimal Adjust after Addition," does just that.
49+
After executing `DAA` our accumulator will be adjusted from `%00001010` to `%00010000`; a 1 in the left nibble and a 0 in the right one. A more detailed article about `DAA` on the Game Boy can be found [here](https://blog.ollien.com/posts/gb-daa/).
50+
51+
Then we store the score back into `wScore` and finally, we call a function that will update the score board, which we will implement next.
52+
53+
Of course, we still need to call it on impact. To do this, we add a call to `IncreaseScorePackedBCD` after each collision handler (we had a left and a right collision) in `CheckAndHandleBrick`
54+
55+
```rgbasm,linenos,start={{#line_no_of "" ../../unbricked/bcd/main.asm:check-for-brick}}
56+
{{#include ../../unbricked/bcd/main.asm:check-for-brick}}
57+
```
58+
59+
## Digit tiles
60+
61+
Before we can display the score we'll need to add some graphics for the numbers 0-9. We already have some ready-made digits for this project, so you can copy [this premade file](https://github.com/gbdev/gb-asm-tutorial/raw/master/unbricked/bcd/digit-tileset.asm), and paste it at the end of your tile set, just before the `TilesEnd` label. Your tile set will look like this:
62+
63+
![Screenshot of tile set with digits added at the end](../assets/part2/img/bcd-tileset.png)
64+
65+
So we can easily remember where the digits start, let's add a constant called `DIGIT_OFFSET` to point us to where the digits are relative to the start of the tile set: `$1A`
66+
67+
```rgbasm,linenos,start={{#line_no_of "" ../../unbricked/bcd/main.asm:digit-offset}}
68+
{{#include ../../unbricked/bcd/main.asm:digit-offset}}
69+
```
70+
71+
Let's make an assumption, that we cannot get a score higher than 99 ([what could possibly go wrong](https://en.wikipedia.org/wiki/Year_2000_problem)) so two digits are enough.
72+
73+
We can start with showing two zeroes (the tile at offset `$1A`) on our initial map. Let's put them on row 3, starting 4 tiles to the left.
74+
You can copy-paste the tile set from [this file](https://github.com/gbdev/gb-asm-tutorial/raw/master/unbricked/bcd/tilemap.asm)
75+
76+
This should make the tile set look like this on start up:
77+
78+
![Screenshot of tile map with two zeroes added](../assets/part2/img/bcd-tilemap.png)
79+
80+
> **Tip:** You can find the address in VRAM in your emulator's tile map viewer by selecting the tile and looking at the index.
81+
> The screenshot above is from emulucious.
82+
83+
Let's remember their positions by defining a constant for VRAM location of the 10s and the 1s at the top of our file, behind the other constants.
84+
85+
```rgbasm,linenos,start={{#line_no_of "" ../../unbricked/bcd/main.asm:score-tile-location}}
86+
{{#include ../../unbricked/bcd/main.asm:score-tile-location}}
87+
```
88+
## Displaying the score
89+
90+
Now we need to write the missing `UpdateScoreBoard` function that will update the score board:
91+
92+
```rgbasm,linenos,start={{#line_no_of "" ../../unbricked/bcd/main.asm:update-score-board}}
93+
{{#include ../../unbricked/bcd/main.asm:update-score-board}}
94+
```
95+
96+
First we load the score (stored in the wScore memory location) into register A. Recall that the score is stored in packed BCD format, where the upper nibble contains the tens digit and the lower nibble contains the ones digit.
97+
98+
The `and %11110000` operation masks the lower nibble (the ones digit) so that only the upper nibble (the tens digit) remains in `A`.
99+
100+
The `rrca` instructions perform a rotate right operation on `A` four times. This effectively shifts the tens digit to the lower nibble, making it ready to map to a digit tile.
101+
102+
We then add the `DIGIT_OFFSET` constant to the tens digit to calculate the tile address for the digit. This address is stored in the `SCORE_TENS` VRAM location, which updates the display to show the tens digit.
103+
104+
Finally, we repeat the process for the ones digit: We mask the tens digit from `A` using `and %00001111`, no need to rotate this time.
105+
106+
Now we can display the score on the screen! We'll need to call `UpdateScoreBoard` after each time the score is updated. We've already done this in the `IncreaseScorePackedBCD` function, so we're all set!

unbricked/bcd/digit-tileset.asm

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
; digits
2+
; 0
3+
dw `33333333
4+
dw `33000033
5+
dw `30033003
6+
dw `30033003
7+
dw `30033003
8+
dw `30033003
9+
dw `33000033
10+
dw `33333333
11+
; 1
12+
dw `33333333
13+
dw `33300333
14+
dw `33000333
15+
dw `33300333
16+
dw `33300333
17+
dw `33300333
18+
dw `33000033
19+
dw `33333333
20+
; 2
21+
dw `33333333
22+
dw `33000033
23+
dw `30330003
24+
dw `33330003
25+
dw `33000333
26+
dw `30003333
27+
dw `30000003
28+
dw `33333333
29+
; 3
30+
dw `33333333
31+
dw `30000033
32+
dw `33330003
33+
dw `33000033
34+
dw `33330003
35+
dw `33330003
36+
dw `30000033
37+
dw `33333333
38+
; 4
39+
dw `33333333
40+
dw `33000033
41+
dw `30030033
42+
dw `30330033
43+
dw `30330033
44+
dw `30000003
45+
dw `33330033
46+
dw `33333333
47+
; 5
48+
dw `33333333
49+
dw `30000033
50+
dw `30033333
51+
dw `30000033
52+
dw `33330003
53+
dw `30330003
54+
dw `33000033
55+
dw `33333333
56+
; 6
57+
dw `33333333
58+
dw `33000033
59+
dw `30033333
60+
dw `30000033
61+
dw `30033003
62+
dw `30033003
63+
dw `33000033
64+
dw `33333333
65+
; 7
66+
dw `33333333
67+
dw `30000003
68+
dw `33333003
69+
dw `33330033
70+
dw `33300333
71+
dw `33000333
72+
dw `33000333
73+
dw `33333333
74+
; 8
75+
dw `33333333
76+
dw `33000033
77+
dw `30333003
78+
dw `33000033
79+
dw `30333003
80+
dw `30333003
81+
dw `33000033
82+
dw `33333333
83+
; 9
84+
dw `33333333
85+
dw `33000033
86+
dw `30330003
87+
dw `30330003
88+
dw `33000003
89+
dw `33330003
90+
dw `33000033
91+
dw `33333333

0 commit comments

Comments
 (0)