Gameboy Development Forum

Discussion about software development for the old-school Gameboys, ranging from the "Gray brick" to Gameboy Color
(Launched in 2008)

You are not logged in.

Ads

#1 2018-06-07 11:31:19

andreai
Member
Registered: 2014-07-28
Posts: 21

palette swap during hblank?

Hi!

I'm trying to do something like the hicolor trick, but I'd like to swap my palettes only for each row of tiles (as opposed to every line like the original hicolor).
Is this even possible with gbdk?
I'm looking at the lcd interrupt with the LYC_REG, but no luck so far.

Thanks in advance, lets hope there's someone out there who understands this stuff better than I do : )

Offline

 

#2 2018-06-07 23:01:49

PinoBatch
Member
Registered: 2018-03-22
Posts: 24

Re: palette swap during hblank?

GBDK I'm not so sure; anything depending on hblank timing will probably need to be in assembly.

And I don't see being able to push all 64 bytes (for 32 colors, or 8 palettes of 4 colors each) to BCPD in a single hblank. Could you post a mock screenshot of the effect you want to achieve? Then others can either guide you through how to get it working or help you figure out a compromise.

Offline

 

#3 2018-06-08 02:00:07

andreai
Member
Registered: 2014-07-28
Posts: 21

Re: palette swap during hblank?

Hi PinoBatch, yes I am getting the feeling that hblank is quickly consumed with the lengthy c instructions.

The end result I'm trying to achieve is the following:

I have an artist making some ascii art using this software:
http://www.gridsagegames.com/rexpaint/index.html

He composes his art choosing symbols from this array of 16x16 custom tiles:
http://www.gridsagegames.com/rexpaint/f … _12x12.png

That ascii software lets him choose 2 arbitrary colors for each tile, we are restricting the total palette to 16 colors max, but I won't be able to restrict his workflow to using 8 palettes 4 colors each.
He's coming up with pictures like the first one on this page:
http://www.gridsagegames.com/rexpaint/gallery.html#

So, first thing I tried was processing his artwork with the standard hicolor technique, and it all went fine, it always converts perfectly, no color bleeds.

Now my thought is: hicolor is overkill in my case, I don't want 8 palettes swapped every 1 or 2 scanlines, I just need to recolor each row of tiles.

Final aim of my experiment is:
1) Export from the ascii art software each screen he makes, extracting a standard array of tiles id is of course the easy part.
2) I will collect palettes and tile attributes in a tiles-row fashion, restricting to 8 palettes of 4 colors per row (making offline colors optimization at this stage).
3) Where I'm stuck, the GBC code that loads these palettes per row of tiles.

Problem is, I'm learning ASM just to solve this problem, and I'm still very confused on how I'd go about swapping the palettes in memory during hblank.

First problem that comes to my mind is where the asm code would fit in my C code, would something like this work?
  while(1){
    asmHBlankCode();
    gameCode();
    wait_vbl_done();
  }

Last edited by andreai (2018-06-08 02:06:55)

Offline

 

#4 2018-06-08 07:38:35

ISSOtm
Member
From: Somewhere in Echo RAM
Registered: 2017-04-18
Posts: 107
Website

Re: palette swap during hblank?

Having the HBlank code run using a busy loop is basically a very bad idea. It would consume the whole frame just to display the effects. So, you should really use the STAT interrupt.
I would recommend the LYC bit, since you don't need to trigger on every scanline.

Here's how I recommend to do it:

1. Use a buffer (in HRAM) that stores the scanlines at which the LYC interrupt should trigger, how many palettes need to be refreshed, and the low byte of a pointer to the palette list
2. Have another buffer that stores the computed palettes
3. And one last buffer that stores palette lists

Since you'll be using a HBlank interrupt, you need to go fast. Unfortunately GBDK's interrupt system adds a very large overhead, so that probably won't work with GBDK. You should ditch it entirely if you plan to do hi-color. (And if you plan to do ASM, ditch it entirely no matter what. Really.)
To help go fast, it's best to align the buffers to 256 bytes, so you can only work with the low byte (the high byte being thus a constant).

Here's a proposition setup:

Code:

SECTION "Hi-color palette buffer", WRAM0,ALIGN[8]

wPaletteBuf::
    ds (...)

SECTION "Hi-color index buffer", WRAM0,ALIGN[8]

wIndexBuf::
    ds (...)

Run this during VBlank:

Code:

    ld de, wIndexBuf
.nextScanline
    ld a, [de] ; Get first scanline
    inc e ; Faster than `inc de`
    and a ; Palettes for 1st scanline need to be loaded during VBlank
    jr z, .screenTop

    dec a ; We need to be ready for the scanline, so we trigger *before*
    ldh [rLYC], a
    ld a, e ; Set pointer for handler
    ldh [hPaletteIndexLow], a


    ; Rest of VBlank interrupt


.screenTop
    ; Load 1st scanline's palettes
    ld h, HIGH(wPaletteBuf)
    ld a, X ; This depends on which palette(s) you want to use hi-color on ($80 for palette 0, $88 for palette 1, etc.)
    ldh [rBCPS], a

    ld c, LOW(rBCPD)
.nextPalette
    ld a, [de] ; Read 1 palette's index
    inc e
    ld l, a ; Store to point `hl` to it
    inc a ; Check if we read $FF, which signifies list end
    jr z, .nextScanline ; Set up system for next scanline

    ld b, 2 * 4 ; Size of a palette ; if you don't need to overwrite a full palette, only a few colors, this can be reduced
.loadPalette
    ; Blast palette bytes. If more speed is required, this loop can be unrolled
    ld a, [hli]
    ld [$ff00+c], a
    dec b
    jr nz, .loadPalette
    jr .nextPalette

And set this as your STAT handler:

Code:

; @ 0048
    push af
    push bc
    push hl
    ldh a, [hPaletteIndexLow], a
    ld e, a
    jr STATHandler ; Can be a `jp` instead, but `jr` is better if possible (you'd have to move the `ld e, a` to make room for the extra byte)

(...)

STATHandler:
    ld d, HIGH(wIndexBuf)
    ld h, HIGH(wPaletteBuf)
    ld a, X ; This depends on which palette(s) you want to use hi-color on ($80 for palette 0, $88 for palette 1, etc.)
    ldh [rBCPS], a

    ld c, LOW(rBCPD)
.nextPalette
    ld a, [de] ; Read 1 palette's index
    inc e
    ld l, a ; Store to point `hl` to it
    inc a ; Check if we read $FF, which signifies list end
    jr z, .done ; Set up system for next scanline

    ld b, 2 * 4 ; Size of a palette ; if you don't need to overwrite a full palette, only a few colors, this can be reduced
.loadPalette
    ; Blast palette bytes. If more speed is required, this loop can be unrolled
    ld a, [hli]
    ld [$ff00+c], a
    dec b
    jr nz, .loadPalette
    jr .nextPalette

.done
    ld a, [de] ; Read next scanline's #
    ldh [rLYC], a
    ld a, e
    ldh [hPaletteIndexLow], a

    pop hl
    pop bc
    pop af
    ret

And here's the format of an entry in the index buffer:

Code:

byte - # of the scanline the palette(s) will be applied to. If larger than $99, the process stops. MAKE SURE TO NEVER PUT A VALUE BETWEEN $90 AND $99!!
Repeat
  byte - low byte of the pointer to the palette (in wPaletteBuf). If $FF, this ends the `Repeat`
End

Entries are contiguous.


Anyways, the thing that you should actually be doing is tell your artist that there are limts, and the he should conform to them :p
Unless it's vital that you use hi-color.

Last edited by ISSOtm (2018-06-08 07:40:48)


The French Lord Of Laziness.
Legend of Zelda and Undertale fan, I also tend to break Pokémon R/B/Y a little too much.

Twitter | Me on GCL | Discord : ISSOtm#9015 | Skype : isso.tm (I don't login anymore)

Offline

 

#5 2018-06-08 08:44:42

andreai
Member
Registered: 2014-07-28
Posts: 21

Re: palette swap during hblank?

Thanks ISSOtm!

This is super useful, as well as super hard to understand just yet.

Before being able to start using that code I need a step back to really understand if I can have the palette/index asm routine enclosed in a C outer frame, and if yes, understand where this asm routine fits in the C code.

I know, you said it's not adviced for a game, but this game is really just a branching tree of static screens, like a game-book, so nothing else really goes on in the main loop aside from querying the joypad.

My guesses as to how to proceed:
Option A, the asm function is called from the main game while loop:
  while(1){
    asmCode(); // will this be in sync with the STAT register to understand when is the right time to swap palette?
    gameCode();
    wait_vbl_done(); // ?? do I need this here or is best moved inside the asm function?
  }

Option B, invoking the asm function from a GBDK interrupt handler instead.
But I guess GBDK is invoking a C function where I'm invoking the asmFunction, so I guess I'm wasting all the cycles I need to do the palette job, right?

Option C, can I somehow invoke an asm function before entering the main game loop and from that asm function setup the asm interrupts that will handle the palette swap?
main(){
asmCode();

  while(1){
    gameCode();
    wait_vbl_done();
  }
}

This would be the best of both worlds, as I'd keep my C loop while the interrupt jumps to the right asm instruction automatically.

Is one of these options viable or am I completely off?

Offline

 

#6 2018-06-08 09:12:18

ISSOtm
Member
From: Somewhere in Echo RAM
Registered: 2017-04-18
Posts: 107
Website

Re: palette swap during hblank?

Option A implies a busy loop to do the palette manipulation, which means your game code has to last in VBlank. It's possible, but then really, really limited.

With option B, the problem is that the interrupts handlers are hardcoded by GBDK, so you can't avoid the overhead if you're using GBDK at all.

Option C doesn't work, because as I said, the interrupt handlers are hardcoded.


The French Lord Of Laziness.
Legend of Zelda and Undertale fan, I also tend to break Pokémon R/B/Y a little too much.

Twitter | Me on GCL | Discord : ISSOtm#9015 | Skype : isso.tm (I don't login anymore)

Offline

 

#7 2018-06-08 11:14:44

andreai
Member
Registered: 2014-07-28
Posts: 21

Re: palette swap during hblank?

Thank you ISSOtm! I am working on option A, I'll go trhough one step at the time.
Maybe by the end of this exercise I'll have enough understanding of the gbz80 that I will rewrite the whole thing in asm : )

For now I have a first success, I'm setting some test palettes for each row of tiles from asm code, also thanks to your snippets!

I do get flickers depending on what I do though, no idea why...I'll see what happens from here and post back the results (hopefully soon).

Offline

 

#8 2018-06-08 17:43:41

ISSOtm
Member
From: Somewhere in Echo RAM
Registered: 2017-04-18
Posts: 107
Website

Re: palette swap during hblank?

Flickers occur because the game code runs past the end of VBlank, and thus the code that loads the palettes waits until the condition occurs again, which only occurs on the next frame.
If you're already getting the flickers, you should give up GBDK now. It can't get any better.

Last edited by ISSOtm (2018-06-08 17:44:01)


The French Lord Of Laziness.
Legend of Zelda and Undertale fan, I also tend to break Pokémon R/B/Y a little too much.

Twitter | Me on GCL | Discord : ISSOtm#9015 | Skype : isso.tm (I don't login anymore)

Offline

 

#9 2018-06-09 15:49:05

tmk
Member
Registered: 2017-05-01
Posts: 29

Re: palette swap during hblank?

You might try different approach. Instead of abusing HBlank, try converting your pictures with bmp2cgb. For example, such picture gives me following output:

Bitmap size: 640 * 1056 px
Character/Attribute map: 80 * 132 = 10560 chars
Palettes usage: 8/8 slots
Tiles used: 57 (10503 duplicates removed: 10277 normal, 226 horizontal, 0 vertical, 0 horizontal & vertical, 0 transparent)

It's a bit overkill but serves as example that it could be displayed without any hassle. Of course your data might differ but you won't know unless you try.

Hm, just to clarify: above output is from w.i.p version, not available on GH yet. The old one couldn't handle whole image but with this excerpt I got:

Bitmap size: 256 * 256 px
Character/Attribute map: 32 * 32 chars
Palettes usage: 3/8 slots
Tiles used: 29 (995 duplicates removed: 967 normal, 28 horizontal, 0 vertical, 0 horizontal & vertical)

Last edited by tmk (2018-06-09 16:12:10)

Offline

 

#10 2018-06-09 16:11:41

ISSOtm
Member
From: Somewhere in Echo RAM
Registered: 2017-04-18
Posts: 107
Website

Re: palette swap during hblank?

It was suggested to do that (stick to the GBC palette limitations), but OP said he didn't want to:

andreai wrote:

That ascii software lets him choose 2 arbitrary colors for each tile, we are restricting the total palette to 16 colors max, but I won't be able to restrict his workflow to using 8 palettes 4 colors each.

I agree that it would really, really better to just do that. bmp2cgb is a possible tool to obtain usable data from pictures, although you may want to write your own if you're going to use your own format.


The French Lord Of Laziness.
Legend of Zelda and Undertale fan, I also tend to break Pokémon R/B/Y a little too much.

Twitter | Me on GCL | Discord : ISSOtm#9015 | Skype : isso.tm (I don't login anymore)

Offline

 

#11 2018-06-10 01:45:35

andreai
Member
Registered: 2014-07-28
Posts: 21

Re: palette swap during hblank?

Hi guys, yes, as I keep on studying the matter at hand, I'm also convinced the hblank trick is overkill.

The artwork from the textmode is 16 colors max, the app already gives me all the info I need in terms of tiles id and colors used (2 colors per tile), here's how to the converter should work I think:
# 1 collect unique tiles used
# 2 unify tiles that can be flipped and populate attribute array
# 3 collect the 2colors palette for each cell and associate each unique palette with each unique tile
# 4 compose 4x8 palettes merging the 2colors palettes and making sure coupled colors are kept together (not splitted across the 4colors palettes)
# (if colors can't form 4x8 palette warn artist he is a bad person)
# 5 for each unique tile collected in step 1, duplicate it for as many 2colors palettes associated in step 3 and swap pixel-colorId to match the proper 4 colors palette, keep populating the attribute array with palette-id per tile association
# (if total number of tiles used is more than 384, warn artist and detonate his computer)

Using both video memory banks I think I have all I need (and more) to handle that kind of artworks : )

Offline

 

Board footer

Powered by PunBB
© Copyright 2002–2005 Rickard Andersson