update graphic compression documentation
This commit is contained in:
parent
3483be127a
commit
6099316fbc
|
@ -370,7 +370,7 @@ addresses in NES like Object Attribute Memory (OAM).
|
||||||
|
|
||||||
# Palette
|
# Palette
|
||||||
The NES supports 4 palettes for the nametables (background) and 4 palettes for
|
The NES supports 4 palettes for the nametables (background) and 4 palettes for
|
||||||
sprites. A palette is a set of 3 colors. For nametables, one palette is shared
|
sprites. A palette is a set of 4 colors. For nametables, one palette is shared
|
||||||
for each square 16x16 set of 4 pattern table tiles. For sprites, each tile in a
|
for each square 16x16 set of 4 pattern table tiles. For sprites, each tile in a
|
||||||
sprite has its own palette.
|
sprite has its own palette.
|
||||||
|
|
||||||
|
@ -388,7 +388,8 @@ level header starting at byte offset 15 (see `LEVEL_PALETTE_INDEX`). Each of
|
||||||
the next 8 bytes specify an entry into `game_palettes`. The first 4 bytes are
|
the next 8 bytes specify an entry into `game_palettes`. The first 4 bytes are
|
||||||
the nametable palette colors, and the next 4 bytes are the sprite palette
|
the nametable palette colors, and the next 4 bytes are the sprite palette
|
||||||
colors. Each entry in `game_palettes` specifies the 3 colors that make up the
|
colors. Each entry in `game_palettes` specifies the 3 colors that make up the
|
||||||
palette.
|
palette. Each palette always starts with a black color #$0f. Combined this
|
||||||
|
is how the 4 colors of each palette are determined.
|
||||||
|
|
||||||
## Fade-In Effect
|
## Fade-In Effect
|
||||||
_Contra_ supports a fade in effect for nametable palettes. This effect is used
|
_Contra_ supports a fade in effect for nametable palettes. This effect is used
|
||||||
|
@ -448,17 +449,33 @@ compression algorithm known as
|
||||||
|
|
||||||
The idea of this algorithm is that the graphics data will have long "runs" of
|
The idea of this algorithm is that the graphics data will have long "runs" of
|
||||||
repeated information. So instead of specifying the same byte over and over, it
|
repeated information. So instead of specifying the same byte over and over, it
|
||||||
can be encoded so that the number of repetitions is specified. Essentially,
|
can be encoded so that the number of repetitions is specified. In addition,
|
||||||
instead of
|
_Contra_'s algorithm specifies when a run of bytes isn't the same. Below is
|
||||||
* #$00 #$00 #$00 #$00 #$00 #$00 #$00 #$00 #$00
|
an example that can help clarify.
|
||||||
It can be represented like
|
|
||||||
* #$89 #$00
|
Below is an example of un-compressed graphics data
|
||||||
|
```
|
||||||
|
#$00 #$00 #$00 #00 #$00 #$00 #$0e #$1f #$07 #$04 #$c0
|
||||||
|
```
|
||||||
|
|
||||||
|
When compressed, it becomes
|
||||||
|
|
||||||
|
```
|
||||||
|
#$06 #$00 #$85 #$0e #$1f #$07 #$0f #$c0 #$ff
|
||||||
|
```
|
||||||
|
|
||||||
|
The special codes in this sequence are #$06, #$85, and #$ff
|
||||||
|
|
||||||
|
#$06 means the next byte #$00 is repeated 6 times. Since the next code has bit
|
||||||
|
7 set, #$85 means to write the next 5 bytes to the PPU. Finally, #$ff means the
|
||||||
|
sequence is complete.
|
||||||
|
|
||||||
Interestingly, this algorithm is implemented twice in _Contra_: once for pattern
|
Interestingly, this algorithm is implemented twice in _Contra_: once for pattern
|
||||||
table tiles (`write_graphic_data_to_ppu` and once again for super-tile
|
table tiles (`write_graphic_data_to_ppu` and once again for super-tile
|
||||||
screen indexes (`load_supertiles_screen_indexes`).
|
screen indexes (`load_supertiles_screen_indexes`). Each implementation is
|
||||||
|
slightly different.
|
||||||
|
|
||||||
## Pattern Table Decompression
|
## graphic_data_xx Decompression
|
||||||
The graphic data is loaded from ROM directly to the PPU. Not only is the
|
The graphic data is loaded from ROM directly to the PPU. Not only is the
|
||||||
graphic data compressed, it also includes command bytes that specify where in
|
graphic data compressed, it also includes command bytes that specify where in
|
||||||
the PPU to write the data to. This section outlines how the data is read and
|
the PPU to write the data to. This section outlines how the data is read and
|
||||||
|
@ -474,15 +491,77 @@ starting at PPU address $0680 (pattern table).
|
||||||
After the first two bytes are read, the following algorithm reads the graphics
|
After the first two bytes are read, the following algorithm reads the graphics
|
||||||
data section. When reading the graphic data, if the most significant bit of the
|
data section. When reading the graphic data, if the most significant bit of the
|
||||||
graphic data byte is set, i.e 1, then the byte is treated as a command byte.
|
graphic data byte is set, i.e 1, then the byte is treated as a command byte.
|
||||||
There are 3 command byte types
|
There are 4 command byte types, evaluated in the following order
|
||||||
|
|
||||||
* #$ff - specifies the end of graphic data
|
* #$ff - specifies the end of graphic data
|
||||||
* #$7f - tells the algorithm to change PPU write address to the next 2 bytes
|
* #$7f - command byte specifies to change PPU write address to the next 2
|
||||||
specified.
|
bytes specified.
|
||||||
* bit 7 set - any byte larger than #$7f, or alternatively, any byte that has
|
* less than #$7f - or alternatively, any command byte that has its most
|
||||||
its most significant bit set to 1 is treated as a RLE command to write the
|
significant bit clear is treated as a RLE command to write the subsequent
|
||||||
next byte of data multiple times to the PPU. This excludes #$ff, which
|
byte to the PPU multiple times. The number of times to repeatedly write the
|
||||||
always means end of graphic data
|
next byte is specified by the command byte value.
|
||||||
|
* greater than #$7f - or alternatively, any command byte that has its most
|
||||||
|
significant bit set is (but not #$ff) is interpreted as a command to write
|
||||||
|
the next string of bytes to the PPU. The number of bytes to write to the
|
||||||
|
PPU is specified by bits 0-6 of the command byte.
|
||||||
|
|
||||||
|
Below is some pseudocode for decompressing a graphics section and writing it to
|
||||||
|
the PPU. Note that due to the implementation, you can only flip graphics data
|
||||||
|
horizontally when that data only writes to a single PPU address location.
|
||||||
|
|
||||||
|
```
|
||||||
|
parse_ppu_address() {
|
||||||
|
byte b = read_next_byte();
|
||||||
|
write_to_PPUADDR(b);
|
||||||
|
b = read_next_byte();
|
||||||
|
write_to_PPUADDR(b);
|
||||||
|
|
||||||
|
if(flip_horizontally) {
|
||||||
|
read_next_byte();
|
||||||
|
read_next_byte();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
parse_ppu_address();
|
||||||
|
byte b = read_next_byte();
|
||||||
|
if(b == 0xff) {
|
||||||
|
// finished decompressing graphics data
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
if(b == 0x7f) {
|
||||||
|
// finished reading one section of compressed graphic
|
||||||
|
// next bytes are new PPU address
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(b < 0x7f) {
|
||||||
|
// write same byte multiple times
|
||||||
|
byte numberOfRepetitions = b;
|
||||||
|
for (byte i = 0; i < numberOfRepetitions; i++) {
|
||||||
|
if(flip_horizontally) {
|
||||||
|
b = flip_horizontally(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_to_PPUDATA(b);
|
||||||
|
}
|
||||||
|
} else if(b > 0x7f) {
|
||||||
|
// write next numberOfBytesToWrite bytes directly to PPU
|
||||||
|
byte numberOfBytesToWrite = b & 0x7f;
|
||||||
|
for (byte i = 0; i < numberOfBytesToWrite; i++) {
|
||||||
|
byte d = read_next_byte();
|
||||||
|
if(flip_horizontally) {
|
||||||
|
d = flip_horizontally(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_to_PPUDATA(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Level section data not encoded exactly the same way
|
Level section data not encoded exactly the same way
|
||||||
`level_1_supertiles_screen_ptr_table`
|
`level_1_supertiles_screen_ptr_table`
|
||||||
|
|
|
@ -2293,25 +2293,26 @@ write_graphic_data_sequences_to_ppu:
|
||||||
beq end_graphic_code ; loaded entire graphics data, restore previously loaded bank, re-init PPU
|
beq end_graphic_code ; loaded entire graphics data, restore previously loaded bank, re-init PPU
|
||||||
cmp #$7f ; used to specify the PPU write address should change to the address specified in the next 2 bytes)
|
cmp #$7f ; used to specify the PPU write address should change to the address specified in the next 2 bytes)
|
||||||
beq change_ppu_write_address
|
beq change_ppu_write_address
|
||||||
tay ; store number of repetitions in Y
|
tay ; store command code byte in y
|
||||||
bpl write_graphic_byte_a_times ; if byte is < #$7f, write the following byte multiple times
|
bpl write_graphic_byte_a_times ; branch if byte is < #$7f to write the next byte multiple times (RLE-command)
|
||||||
and #$7f ; byte is < #$7f, clear bit 7
|
and #$7f ; byte has bit 7 set (negative), code is writing a string of bytes
|
||||||
|
; clear bit 7 to get number of bytes to write from compressed data
|
||||||
sta $02 ; store positive portion in $02, this is the number of bytes to write to PPU
|
sta $02 ; store positive portion in $02, this is the number of bytes to write to PPU
|
||||||
ldy #$01 ; skip past size byte, prepare to read next n graphic bytes
|
ldy #$01 ; skip past size byte, prepare to read next n graphic bytes
|
||||||
|
|
||||||
; writes the next n bytes of the graphic compression sequence to the PPU, starting at offset Y
|
; writes the next n bytes of the compressed graphic data to the PPU, starting at offset Y
|
||||||
; input
|
; input
|
||||||
; * $02 - the number of bytes to write
|
; * $02 - the number of bytes to write (n)
|
||||||
; * y - the graphic data read offset
|
; * y - the graphic data read offset
|
||||||
write_next_n_sequence_bytes:
|
write_next_n_sequence_bytes:
|
||||||
lda ($00),y ; read graphic byte
|
lda ($00),y ; read graphic byte
|
||||||
ldx $04 ; load graphic data horizontal flip bit value
|
ldx $04 ; load graphic data horizontal flip bit value
|
||||||
bpl write_repetition_sequence_byte ; branch if not flipping graphic byte
|
bpl @write_byte_to_ppu ; branch if not flipping graphic byte
|
||||||
jsr horizontal_flip_graphic_byte ; flipping horizontally, flip data before writing to PPU
|
jsr horizontal_flip_graphic_byte ; flipping horizontally, flip data before writing to PPU
|
||||||
|
|
||||||
; writes value of a to the PPU
|
; writes value of a to the PPU
|
||||||
; then determines if done with n bytes of graphic data
|
; then determines if done with the n-length byte sequence of graphic data
|
||||||
write_repetition_sequence_byte:
|
@write_byte_to_ppu:
|
||||||
sta PPUDATA ; write graphic byte to PPU
|
sta PPUDATA ; write graphic byte to PPU
|
||||||
cpy $02 ; see if written all n repetitions
|
cpy $02 ; see if written all n repetitions
|
||||||
beq advance_graphic_read_addr_n_bytes ; written all n bytes, update base graphic read address
|
beq advance_graphic_read_addr_n_bytes ; written all n bytes, update base graphic read address
|
||||||
|
@ -2320,10 +2321,12 @@ write_repetition_sequence_byte:
|
||||||
|
|
||||||
; advances the address of the current graphic byte offset by n bytes
|
; advances the address of the current graphic byte offset by n bytes
|
||||||
; where n is the value in $02 + #$1
|
; where n is the value in $02 + #$1
|
||||||
|
; the #$01 is necessary to skip passed the command byte
|
||||||
advance_graphic_read_addr_n_bytes:
|
advance_graphic_read_addr_n_bytes:
|
||||||
lda #$01
|
lda #$01 ; advancing graphic byte read address by 1 (size of repetition string)
|
||||||
clc ; clear carry in preparation for addition
|
clc ; clear carry in preparation for addition
|
||||||
adc $02 ; set A to the number of bytes to skip (they've already been written to the PPU)
|
adc $02 ; skip over the bytes just written the PPU
|
||||||
|
; a now has the number of bytes to skip
|
||||||
|
|
||||||
advance_ppu_write_addr:
|
advance_ppu_write_addr:
|
||||||
ldx #$00 ; specifies that the 2-byte graphic read address is located at $00
|
ldx #$00 ; specifies that the 2-byte graphic read address is located at $00
|
||||||
|
|
Loading…
Reference in New Issue