This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

neslib rle function, sometime extra characters shown

neslib rle function, sometime extra characters shown
by on (#224909)
I took from neslib the rle function since I now encode some data with nesst, which reduce the size to something reasonable. My only issue is sometime some extra characters are shown or it doesn't load properly at all.

I disable the bg/spr before loading the data (similar to ppuOn/Off from neslib) before using this function so it should be enough but the problem seems random. For example, I loaded 2 screens with an interval of 2~3 second and it was failing. Just adding some code to clear the nametables made it work after.

Since I'm using it inside my code, I remapped the variable used but I do not thing this is the code but still need to inspect it.

What I want to confirm is if the rle function that comes with the neslib example has issues? If not then it's something it my code that I will need to figure out.

edit:

I think it's not related to neslib at all. It just the method expect the vram to not be touched but the NMI that runds in the backround is possbilty updating the palette, which could cause the corruption. I guess I need to adapt the method a little bit. This is the only probable cause that I can see.
Re: neslib rle function, sometime extra characters shown
by on (#224912)
I haven't had any issues with neslib's unrle func. So probably the integration like you thought. Is there some reason you want to take just pieces from neslib?
Re: neslib rle function, sometime extra characters shown
by on (#224920)
Which version on NES Screen Tool are you using. There was a bug in the older version's rle encoder.
Re: neslib rle function, sometime extra characters shown
by on (#224931)
Slightly related, is Shiru's implementation of RLE described somewhere? I'd like to add it to one of my converters so I don't need to manually open nesst and load the .nam to compress it.
Re: neslib rle function, sometime extra characters shown
by on (#224937)
It's defined by its decoder. Though the decoder lacks comments, I was able to understand enough of it to add a doc comment describing the format and a comment before each step. But unless I missed something, a stream cannot contain all 256 distinct byte values; the value that marks the start of a run can never appear in the output.

Code:
RLE_LOW: .res 1
RLE_HIGH: .res 1
RLE_TAG: .res 1
RLE_BYTE: .res 1

;;
; Decompresses a stream compressed with Shiru RLE to
; video memory.
; The first byte in the stream is the run marker.  After that,
; any byte other than the run marker is copied literally to the
; output.  A run marker followed by a byte with value 1-255 writes
; that many copies of the most recently written byte to the stream.
; A run marker followed by 0 ends the stream.
; There appears to be no way to include the run marker in the stream.
; @param XA pointer to start of stream
_vram_unrle:
  ; Set up
  tay
  stx <RLE_HIGH
  lda #0
  sta <RLE_LOW

  ; Read byte that does not appear in data, used to signal a run
  lda (RLE_LOW),y
  sta <RLE_TAG
  iny
  bne @tag_nowrap
    inc <RLE_HIGH
  @tag_nowrap:

@decodeloop:
  ; Read a byte from the stream
  lda (RLE_LOW),y
  iny
  bne @main_nowrap
    inc <RLE_HIGH
  @main_nowrap:

  ; If it doesn't match the run marker, output it
  cmp <RLE_TAG
  beq @is_rle
    sta PPU_DATA
    sta <RLE_BYTE
    bne @decode_loop
  @is_rle:

  ; We just saw a run marker.  Load the length, stopping if zero
  lda (RLE_LOW),y
  beq @done
  iny
  bne @len_nowrap
    inc <RLE_HIGH
  @len_nowrap:
  tax  ; X = length of run

  ; Output the most recent byte X times
  lda <RLE_BYTE
  @runloop:
    sta PPU_DATA
    dex
    bne @runloop
  beq @decodeloop

@done:
  rts

The NESST source code contains an encoder embedded in save_data(). I'd write an encoder in Python, but I don't know what to do if the input contains all 256 distinct byte values.
Re: neslib rle function, sometime extra characters shown
by on (#224938)
Quote:
I'd write an encoder in Python, but I don't know what to do if the input contains all 256 distinct byte values.


NES Screen Tool just throws an error message if all 256 bytes are used, and you try to save as compressed rle.
Re: neslib rle function, sometime extra characters shown
by on (#224941)
One might consider changing the format to treat run marker followed by $01 as literal insertion of the run marker. It'd add 8 bytes and 5 cycles per run.
Code:
  ; If it doesn't match the run marker, output it
  cmp <RLE_TAG
  beq @is_rle
  @is_literal:
    sta PPU_DATA
    sta <RLE_BYTE
    bne @decode_loop
  @is_rle:

  ; We just saw a run marker.  Load the length, stopping if zero
  lda (RLE_LOW),y
  beq @done
  iny
  bne @len_nowrap
    inc <RLE_HIGH
  @len_nowrap:

  ; The run marker followed by $01 means itself
  cmp #$01
  bcs @is_run
    lda <RLE_TAG
    bcc @is_literal
  @is_run:
  tax  ; X = length of run

Then change the encoder to reflect this:
Code:
// Replace this in the encoder

            if(len) dst[pp++]=sym;
            if(len>1)
            {
               if(len==2)
               {
                  dst[pp++]=sym;
               }

// with something like this

            if(len)
            {
               dst[pp++]=sym;
               if (sym==tag) dst[pp++]=1;
            }

            if(len>1)
            {
               if(len==2)
               {
                  dst[pp++]=sym;
                  if (sym==tag) dst[pp++]=1;
               }
Re: neslib rle function, sometime extra characters shown
by on (#224947)
I have to say, creating a format that's not capable of representing certain sequences of bytes is a very lousy decision to make.
Re: neslib rle function, sometime extra characters shown
by on (#224952)
@calima

Neslib is what got me back doing some nes coding first by figuring out a way to re-compile the c runtime so I could use the latest cc65 (now I have a very nice makefile for all tasks in my project), doing some hello world in C with it to figure out how cc65 work then analysing one method at a time in the lib and crt0 to have an overall idea. The problem for me was when I started to re-use neslib in my code, it has specific needs like a nmi for allowing buffering of screen or palette, some code in crt0 that included in a specific way, some specific zp variable here and there that conflicted with my own code or way or some setting that were not necessary for my needs. For example I use dynamic NMI that can be set at runtime so to adapt each nmi so neslib would work was an issue.

After checking all the methods, I decided to keep only 1 at first, the joypad one since it was already managing trigger which I didn't do yet (no point of rewriting that when it's working). Now that my project is moving forward there is a few use-case that have already been done in neslib like using rle data for screen, so it is better to use the one already existing than rewriting them. I guess I didn't check the impact to use with my code properly yet. I asked on the forum, just in case but I was too exhausted recently so my judgment of the cause was bad. I'm sure it's my integration that is wrong and I didn't check the impact of using such code. I need to review the code again.

@dougeff

I think I'm using the latest version. I know that after loading a few thing (I do not know the pattern to reproduce the bug yet), it seems to fail when loading nametable data but I didn't find how yet. In those case I just restart the program. I'm not sure yet if I got broken rle files once or it was just my code that was acting up. I need to recheck my integration first. Is nes screentool as a version number? I will check later.

@tokumaru

What do you mean? Is the current rle version be problematic or this is related to the post in this thread? I just want to confirm since I just started to use the rle code and right now it is more than enough for my needs.
Re: neslib rle function, sometime extra characters shown
by on (#224954)
Banshaku wrote:
What do you mean? Is the current rle version be problematic or this is related to the post in this thread? I just want to confirm since I just started to use the rle code and right now it is more than enough for my needs.

I don't know, I never used these tools myself, I just read that the RLE format used is not capable of encoding data containing all 256 byte values, which sounds like a pretty annoying design flaw to me. Sometimes I just complain about stuff... sorry if I sound like a jerk.
Re: neslib rle function, sometime extra characters shown
by on (#224955)
tokumaru wrote:
I don't know, I never used these tools myself, I just read that the RLE format used is not capable of encoding data containing all 256 byte values, which sounds like a pretty annoying design flaw to me. Sometimes I just complain about stuff... sorry if I sound like a jerk.


I don't think that the comment is inappropriate or rude it just since I do not know much about encoding, it raised a warning flag in my head about if I use it in other place than nametable later, I could have issues if I don't know more about the inner working of that method. I just used it blindly without looking at the logic so I would have not been aware about that.

So does that mean that some value cannot be used? What are the limitation? I think this thread is becoming more interesting already ;)
Re: neslib rle function, sometime extra characters shown
by on (#224956)
Banshaku wrote:
So does that mean that some value cannot be used? What are the limitation?

It seems like the encoder selects one specific byte value to represent the start of a compressed RLE run, and that value can't show up in the data itself, or it'd be mistaken for the start of a run. Most compression schemes would still allow special values to be used, even if inefficiently (e.g. a run of length 1 using the special value). It seems like tepples found a good solution.
Re: neslib rle function, sometime extra characters shown
by on (#224958)
I see. He found a solution but this means that the data cannot be encoded with the tool anymore and must be with an external one, right?
Re: neslib rle function, sometime extra characters shown
by on (#224966)
The intent is for Shiru to integrate the new encoder into the next version of NES Screen Tool. Once that happens:

- The improved decoder is compatible with old streams and new streams.
- The streams produced by the improved encoder are still compatible with the old decoder so long as they don't use all 256 byte values.

Anyone want to forward this change request?
Re: neslib rle function, sometime extra characters shown
by on (#224971)
Since Shiru is not active these days on nesdev and I'm not aware of any update of those tools, he may not update them? ^^;;;

Maybe the best way is use the tool to create the nametable data then use a script to rle with the new format. And if there is a known bug that saving in rle sometime corrupt the data then this can help avoid this bug too.

Maybe a python script could do the job, I guess? I didn't do binary parsing with python yet so I cannot tell. For now no rush for it, the information is already very useful in this thread.
Re: neslib rle function, sometime extra characters shown
by on (#224973)
Sorry for the double post.

edited:

Found the issue. there was a stray write in the buffering method to PPU_DATA, causing random bugs. I guess it must have occurred while adapting the code with my old one that did not cache the palette and I didn't realize that write was left there. Removing that single line fixed the issue.

Old code.. At the least, when I'm awake I can found the issues a lot faster ^^;;; I guess I should review all the code since there is maybe more hidden "gems" like that :lol:

Thank you everyone for information on the subject, I learned more about RLE even though that was not the cause.

edit2:

I had some "unusual" bugs but didn't know the cause. I didn't even check the rle code properly and didn't realize that it expect the Nametable address to be set before being used, which make sense ^^;;; Maybe I read the method comment too fast, it must have been written.

Since my engine save the address I just now reset it before uncompressing the data. 2 bugs fixed today :) (I should be ashamed since they are silly bugs but, oh well ^^;;)
Re: neslib rle function, sometime extra characters shown
by on (#224976)
tepples wrote:
It's defined by its decoder. Though the decoder lacks comments, I was able to understand enough of it to add a doc comment describing the format and a comment before each step. But unless I missed something, a stream cannot contain all 256 distinct byte values; the value that marks the start of a run can never appear in the output.

Code:
RLE_LOW: .res 1
RLE_HIGH: .res 1
RLE_TAG: .res 1
RLE_BYTE: .res 1

;;
; Decompresses a stream compressed with Shiru RLE to
; video memory.
; The first byte in the stream is the run marker.  After that,
; any byte other than the run marker is copied literally to the
; output.  A run marker followed by a byte with value 1-255 writes
; that many copies of the most recently written byte to the stream.
; A run marker followed by 0 ends the stream.
; There appears to be no way to include the run marker in the stream.
; @param XA pointer to start of stream
_vram_unrle:
  ; Set up
  tay
  stx <RLE_HIGH
  lda #0
  sta <RLE_LOW

  ; Read byte that does not appear in data, used to signal a run
  lda (RLE_LOW),y
  sta <RLE_TAG
  iny
  bne @tag_nowrap
    inc <RLE_HIGH
  @tag_nowrap:

@decodeloop:
  ; Read a byte from the stream
  lda (RLE_LOW),y
  iny
  bne @main_nowrap
    inc <RLE_HIGH
  @main_nowrap:

  ; If it doesn't match the run marker, output it
  cmp <RLE_TAG
  beq @is_rle
    sta PPU_DATA
    sta <RLE_BYTE
    bne @decode_loop
  @is_rle:

  ; We just saw a run marker.  Load the length, stopping if zero
  lda (RLE_LOW),y
  beq @done
  iny
  bne @len_nowrap
    inc <RLE_HIGH
  @len_nowrap:
  tax  ; X = length of run

  ; Output the most recent byte X times
  lda <RLE_BYTE
  @runloop:
    sta PPU_DATA
    dex
    bne @runloop
  beq @decodeloop

@done:
  rts

The NESST source code contains an encoder embedded in save_data(). I'd write an encoder in Python, but I don't know what to do if the input contains all 256 distinct byte values.


Thank you, Tepples.