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.
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
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
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
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:
SECTION "Hi-color palette buffer", WRAM0,ALIGN[8] wPaletteBuf:: ds (...) SECTION "Hi-color index buffer", WRAM0,ALIGN[8] wIndexBuf:: ds (...)
Run this during VBlank:
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:
; @ 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:
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)
Offline
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
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.
Offline
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
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)
Offline
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
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.
Offline
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