The basic concept of RLE is that instead of storing just a series of pixel colors, we also store the number of times each pixel is repeated. As described in the previous post, we know that this hardware uses the lower nibble of each byte to store the color number. We will use the upper nibble to indicate repetitions as well as other commands, which we'll get to later...
Using '0' for the number of repetitions makes no sense, so it will never be used when the image is encoded. (repeat "red" pixels 0 times? nope.) So we'll use '0' in the top nibble to indicate commands. A few commands that we will need are:
$00 - End of image (stop rendering, return)
$0F - End of line (no more pixels on this line, start over vertically down one pixel from the start of this line)
Which leaves $01 through $0E, which we will use as a "skip". Advance the screen position, but do not draw any pixels to the screen. We can use this to allow images to have "transparency".
One thing to deal with was that after 255 bytes (at most), the referencing will go into another bank. If everything fits in one bank, that's fine, but the screen itself is 4 banks, so this was something that needed to be addressed. (HA! Addressed! I'm hilarious!) If this isn't dealt with, and we only are incrementing the lower byte of a two byte address, we'll just keep reading (or writing) forever inside of one bank. $41FE, $41FF, $4100, etc rather than $41FE, $41FF, $4200, $4201 ...
So basically instead of just incrementing the screen pointer by one, indirectly using
I instead had to add a '1' to it, then add the carry bit onto the high byte of the value. I need to take a step back here. The 6502 only really has grasp of 8 bit (one byte) values. It can use 16 bit values for addresses, stored as two bytes, but all math functions happen on the one-byte scale.inc IMGPTR ; will wrap around inside a bank. bad.
clc ; clear the carry bit (Carry = 0) lda IMGPTR ; A = *IMGPTR adc #$01 ; A = A + 1 + C sta IMGPTR ; *IMGPTR = A ; at this point, the carry bit is either set or not, ; so we will add 0 into the next byte with carry lda IMGPTR+1 ; A = *IMGPTR+1 adc $#00 ; A = A + 0 + C sta IMGPTR+1 ; *IMGPTR+1 = A
Why use RLE? A couple reasons. First of all, it will save ROM space. The RLE encoded (compressed) images should take a bit less space inside the rom. An alternate we could do is to store color data in both nibbles of the byte, then just shift them out to the screen. We would lose the ability for transparency, but you're guaranteed 50% space savings with the system we have here.
The full source code for this project is over at github.
The image shown at the top of this post shows three sprites stored in the ROM. They were hand-encoded from graph paper sketches of various sources. The rainbow was just coded by scratch to test out everything.
The red ghost is obviously borrowed from Namco's "Pac-Man" arcade game. The mouse is borrowed from Nintendo's "Goonies" arcade game. Both are used for educational/demonstrative purposes here.