Would you like to share the code in ASM?
Sure:
$01/BA78 A9 0D LDA #$0D A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzc HC:0396 VC:000 FC:24 I:00
$01/BA7A 85 F0 STA $F0 [$00:01F0] A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzc HC:0412 VC:000 FC:24 I:00
$01/BA7C A9 10 LDA #$10 A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzc HC:0428 VC:000 FC:24 I:00
$01/BA7E 85 F2 STA $F2 [$00:01F2] A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzc HC:0444 VC:000 FC:24 I:00
$01/BA80 A9 50 LDA #$50 A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzc HC:0460 VC:000 FC:24 I:00
$01/BA82 85 F3 STA $F3 [$00:01F3] A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzc HC:0476 VC:000 FC:24 I:00
$01/BA84 A2 00 00 LDX #$0000 A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzc HC:0492 VC:000 FC:24 I:00
$01/BA87 A0 60 00 LDY #$0060 A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzc HC:0508 VC:000 FC:24 I:00
$01/BA8A 20 D0 BA JSR $BAD0 [$01:BAD0] A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzc HC:0524 VC:000 FC:24 I:00
$01/BA8D C2 20 REP #$20 A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzc HC:0548 VC:000 FC:24 I:00
$01/BA8F 8A TXA A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envmxdIzc HC:0564 VC:000 FC:24 I:00
$01/BA90 18 CLC A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envmxdIzc HC:0580 VC:000 FC:24 I:00
$01/BA91 69 40 07 ADC #$0740 A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envmxdIzc HC:0596 VC:000 FC:24 I:00
$01/BA94 AA TAX A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envmxdIzc HC:0612 VC:000 FC:24 I:00
$01/BA95 A5 F2 LDA $F2 [$00:01F2] A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envmxdIzc HC:0628 VC:000 FC:24 I:00
$01/BA97 69 00 01 ADC #$0100 A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envmxdIzc HC:0644 VC:000 FC:24 I:00
$01/BA9A 85 F2 STA $F2 [$00:01F2] A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envmxdIzc HC:0660 VC:000 FC:24 I:00
$01/BA9C E2 20 SEP #$20 A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envmxdIzc HC:0684 VC:000 FC:24 I:00
$01/BA9E C6 F0 DEC $F0 [$00:01F0] A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzc HC:0700 VC:000 FC:24 I:00
$01/BAA0 D0 E6 BNE $E6 [$BA88] A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzc HC:0716 VC:000 FC:24 I:00
$01/BAA2 64 F2 STZ $F2 [$00:01F2] A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzc HC:0732 VC:000 FC:24 I:00
$01/BAA4 64 F3 STZ $F3 [$00:01F3] A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzc HC:0748 VC:000 FC:24 I:00
$01/BAA6 60 RTS A:0C02 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIzcThis part of the routine essentially sets all of the variables that will be used 13 times over to copy the tiles for each character. The 0D written to 01F0 is used to count the number of times the routine loops. The 5010 written to 1F2 sets the address in VRAM where the data will be written (multiplied by 2, so it actually starts at $A020). The 0060 stored in Y counts the bytes of graphic data copied to VRAM - I'm only using the first $C0 bytes of each character, and since two bytes are written at a time we're counting down from half of C0. Register X functions as an index for the source of copied graphical data.
$01/BAD0 8B PHB A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0020 VC:000 FC:28 I:00
$01/BAD1 A9 00 LDA #$00 A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0036 VC:000 FC:28 I:00
$01/BAD3 48 PHA A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0052 VC:000 FC:28 I:00
$01/BAD4 AB PLB A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0068 VC:000 FC:28 I:00
$01/BAD5 8D 15 21 STA $2115 [$7E:2115] A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0084 VC:000 FC:28 I:00
$01/BAD8 C2 20 REP #$20 A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0108 VC:000 FC:28 I:00
$01/BADA A5 F2 LDA $F2 [$00:01F2] A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envmxdIZc HC:0124 VC:000 FC:28 I:00
$01/BADC 8D 16 21 STA $2116 [$7E:2116] A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envmxdIZc HC:0140 VC:000 FC:28 I:00
$01/BADF E2 20 SEP #$20 A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envmxdIZc HC:0164 VC:000 FC:28 I:00
$01/BAE1 BF 00 80 1A LDA $1A8000,x[$1A:3D0C] A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0180 VC:000 FC:28 I:00
$01/BAE5 EB XBA A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0196 VC:000 FC:28 I:00
$01/BAE6 E8 INX A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0212 VC:000 FC:28 I:00
$01/BAE7 BF 00 80 1A LDA $1A8000,x[$1A:3D0C] A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0228 VC:000 FC:28 I:00
$01/BAEB 8D 19 21 STA $2119 [$7E:2119] A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0244 VC:000 FC:28 I:00
$01/BAEE EB XBA A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0260 VC:000 FC:28 I:00
$01/BAEF 8D 18 21 STA $2118 [$7E:2118] A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0276 VC:000 FC:28 I:00
$01/BAF2 E8 INX A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0292 VC:000 FC:28 I:00
$01/BAF3 88 DEY A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0308 VC:000 FC:28 I:00
$01/BAF4 D0 EB BNE $EB [$BAE1] A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0324 VC:000 FC:28 I:00
$01/BAF6 7B TDC A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0340 VC:000 FC:28 I:00
$01/BAF7 EB XBA A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0356 VC:000 FC:28 I:00
$01/BAF8 AB PLB A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0372 VC:000 FC:28 I:00
$01/BAF9 60 RTS A:0C00 X:BD0C Y:0300 D:0100 DB:7E S:02DD P:envMxdIZc HC:0388 VC:000 FC:28 I:00And this is the part of the routine that actually copies the data into VRAM. You may notice that the routine writes the data one byte at a time by writing to $2119 and then $2118. I was originally under the impression that I could write a two-byte value to $2118, but it turns out that doing so copies the two bytes to bytes $00 and $02 in VRAM, which is weird and unhelpful. But I figured out this workaround, so no problem.
The end result of all this has the tile data written as characters 01, 02, 03, 04, 05, 06, 11, 12, 13... D6. This leaves a lot of unused space in VRAM, but is much easier to index.
Since the tutorial was more of introduction rather detail coding, would you like to do some practical coding? I'm going to give standalone tasks, that doesn't require zillions of line of code, you must make the ASM code. We will test the result in emulators. I found a small library that produce all the initialization you need, so you can focus mainly on the task that I'm going to give. It doesn't require specific library (so you can implement whatever is on your mind, not limiting yourself to specific) but has defined all PPU registers for easy use (by name rather numbers). So far it produces small 32k ROM files that you can load in any emulator.
Yeah, that sounds like a good exercise. Not sure when I'd be able to get to it, but let's see what you've got.