Skip to navigation


Utility routines: UnpackToRAM

[NES version, Bank 7]

Name: UnpackToRAM [Show more] Type: Subroutine Category: Utility routines Summary: Unpack compressed image data to RAM Deep dive: Image and data compression
Context: See this subroutine in context in the source code References: This subroutine is called as follows: * DrawBigLogo calls UnpackToRAM * GetHeadshot calls UnpackToRAM * GetSystemBack calls UnpackToRAM * SetViewAttrs calls UnpackToRAM

This routine unpacks compressed data into RAM. The data is typically nametable or pattern data that is unpacked into the nametable or pattern buffers.
.UnpackToRAM LDY #0 ; We work our way through the packed data at SC(1 0), so ; set an index counter in Y, starting from the first ; data byte at offset zero .upac1 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX #0 ; Set X = 0, so we can use a LDA (V,X) instruction below ; to fetch the next data byte from V(1 0), as the 6502 ; doesn't have a LDA (V) instruction LDA (V,X) ; Set A to the byte of packed data at V(1 0), which is ; the next byte of data to unpack ; ; As X = 0, this instruction is effectively LDA (V), ; which isn't a valid 6502 instruction on its own INC V ; Increment V(1 0) to point to the next byte of packed BNE upac2 ; data INC V+1 .upac2 CMP #$40 ; If A >= $40, jump to unpac12 to output the data in A BCS upac12 ; as it is, and move on to the next byte ; If we get here then we know that the data byte in A is ; of the form $0x, $1x, $2x or $3x TAX ; Store the packed data byte in X so we can retrieve it ; below AND #$0F ; If the data byte in A is in the format $x0, jump to BEQ upac11 ; upac11 to output the data in X as it is, and move on ; to the next byte CPX #$3F ; If the data byte in X is $3F, then this indicates we BEQ upac13 ; have reached the end of the packed data, so jump to ; upac13 to return from the subroutine, as we are done TXA ; Set A back to the unpacked data byte, which we stored ; in X above CMP #$20 ; If A >= $20, jump to upac6 to process values of $2x BCS upac6 ; and $3x (as we already processed values above $40) ; If we get here then we know that the data byte in A is ; of the form $0x or $1x (and not $x0) CMP #$10 ; If A >= $10, set the C flag AND #$0F ; Set X to the low nibble of A, so it contains the TAX ; number of zeroes or $FF bytes that we need to output ; when the data byte is $0x or $1x BCS upac5 ; If the data byte in A was >= $10, then we know that A ; is of the form $1x, so jump to upac5 to output the ; number of $FF bytes specified in X ; If we get here then we know that A is of the form ; $0x, so we need to output the number of zero bytes ; specified in X LDA #0 ; Set A as the byte to write to SC(1 0), so we output ; zeroes .upac3 ; This loop writes byte A to SC(1 0), X times STA (SC),Y ; Write the byte in A to SC(1 0) INY ; Increment Y to point to the next data byte BNE upac4 ; If Y has now wrapped round to zero, loop back to upac1 ; to unpack the next data byte INC SC+1 ; Otherwise Y is now zero, so increment the high byte of ; SC(1 0) to point to the next page, so that SC(1 0) + Y ; still points to the next data byte .upac4 DEX ; Decrement the byte counter in X BNE upac3 ; Loop back to upac3 to write the byte in A again, until ; we have written it X times JMP upac1 ; Jump back to upac1 to unpack the next byte .upac5 ; If we get here then we know that A is of the form ; $1x, so we need to output the number of $FF bytes ; specified in X LDA #$FF ; Set A as the byte to write to SC(1 0), so we output ; $FF BNE upac3 ; Jump to the loop at upac3 to output $FF to SC(1 0), ; X times (this BNE is effectively a JMP as A is never ; zero) .upac6 ; If we get here then we know that the data byte in A is ; of the form $2x or $3x (and not $x0) LDX #0 ; Set X = 0, so we can use a LDA (V,X) instruction below ; to fetch the next data byte from V(1 0), as the 6502 ; doesn't have a LDA (V) instruction CMP #$30 ; If A >= $30 then jump to upac7 to process bytes in the BCS upac7 ; for $3x ; If we get here then we know that the data byte in A is ; of the form $2x (and not $x0) AND #$0F ; Set T to the low nibble of A, so it contains the STA T ; number of times that we need to output the byte ; following the $2x data byte LDA (V,X) ; Set A to the byte of packed data at V(1 0), which is ; the next byte of data to unpack, i.e. the byte that we ; need to write X times ; ; As X = 0, this instruction is effectively LDA (V), ; which isn't a valid 6502 instruction on its own LDX T ; Set X to the number of times we need to output the ; byte in A INC V ; Increment V(1 0) to point to the next data byte (as we BNE upac3 ; just read the one after the $2x data byte), and jump INC V+1 ; to the loop in upac3 to output the byte in A, X times JMP upac3 .upac7 ; If we get here then we know that the data byte in A is ; of the form $3x (and not $x0), and we jump here with ; X set to 0 AND #$0F ; Set T to the low nibble of A, so it contains the STA T ; number of unchanged bytes that we need to output ; following the $3x data byte .upac8 LDA (V,X) ; Set A to the byte of packed data at V(1 0), which is ; the next byte of data to unpack ; ; As X = 0, this instruction is effectively LDA (V), ; which isn't a valid 6502 instruction on its own INC V ; Increment V(1 0) to point to the next data byte (as we BNE upac9 ; just read the one after the $2x data byte) INC V+1 ; We now loop T times, outputting the next data byte on ; each iteration, so we end up writing the next T bytes ; unchanged .upac9 STA (SC),Y ; Write the unpacked data in A to the Y-th byte of ; SC(1 0) INY ; Increment Y to point to the next data byte BNE upac10 ; If Y has now wrapped round to zero, increment the INC SC+1 ; high byte of SC(1 0) to point to the next page, so ; that SC(1 0) + Y still points to the next data byte .upac10 DEC T ; Decrement the loop counter in T BNE upac8 ; Loop back until we have copied the next T bytes ; unchanged from V(1 0) to SC(1 0) JMP upac1 ; Jump back to upac1 to unpack the next byte .upac11 TXA ; Set A back to the unpacked data byte, which we stored ; in X before jumping here .upac12 STA (SC),Y ; Write the unpacked data in A to the Y-th byte of ; SC(1 0) INY ; Increment Y to point to the next data byte BNE upac1 ; If Y has now wrapped round to zero, loop back to upac1 ; to unpack the next data byte INC SC+1 ; Otherwise Y is now zero, so increment the high byte of JMP upac1 ; SC(1 0) to point to the next page, so that SC(1 0) + Y ; still points to the next data byte .upac13 RTS ; Return from the subroutine