Brainstorming a way to hscan colors in SGDK

SGDK only sub forum

Moderator: Stef

Post Reply
realbrucest
Interested
Posts: 27
Joined: Wed Sep 21, 2011 9:00 pm
Location: Sevilla, Spain
Contact:

Brainstorming a way to hscan colors in SGDK

Post by realbrucest » Mon Mar 30, 2020 11:23 pm

Hi .

I always loved the tricks used for the Sega Gensis providing a few more colors in different circunstances.
Just to set the context there're a few examples from licenced games:

Blastem's CRAM debug screen is a wonderful tool to peek what's cooking into the Sega Genesis color table.

Image
Samurai Showdown can be considered a "famous" case.

It was so clever to split colors for two "items" from the game wich will never share an horizontal coordinate: the time and energy hud and the battle referee. Six color are reloaded a bit after the HUD is drawn, meaning a bit lower. A few lines below the HUD the color reloading starts.

It is achieved dumping pairs of colors into the CRAM during three scanlines. Two is the maximum number of colors you can send at one hint time avoiding the appearance of CRAM dots trash on the screen.

The colors used for the HUD are not gonna be needed any more so this color room will be taken for six new colors for the referee sprite.

Image

Depite not being "ingame", the selection screens makes an even more impressive use of this technique. Three of the four palettes are rewritten, keeping just the one dedicated to the background texture. Colors are sent two by two during 24 scanlines. Doing that they achieved to have 15 exclusive colors for the lower row of faces, the upper ones are drawn with two palettes: 15 + 15 colors; those two become the palettes of the sprites at the bottom of the screen.

There're a couple of cram dots dancing around but it was a quite impressive tricky-work on that screen.

Image

Toughman Contest make use of color reloading in a way I discovered NBA Hangtime also did.

Image

The palettes (one in Toughman, two in a row in Hangtime) are reloaded with new colors in order to gain new ones for the faces of the contenders and team players.

In one game, the same palette is used to colourize the arena and afterwards, bellow, the contenders. The other has the same palettes used to dispose colors for the team badges first, and then for the players faces and the avatars background.

A difference between the re-colouring at those two games is that the boxing game sends the new colors to the CRAM one by one instead of two by two as NBA Hangtime and most other games do.

Image

Also, rewriting colors one by one works this effect in Sub-Terrania. Unlike the previous games, this time there is not only one batch (of single or pairs of colors) dumped scanline after scanline. We have three lots dumped, two times the CRAM is rewriten with a few colors at the background palette and one time, in between, giving a few new color values into the palette set for the foreground.

Image
Image

Very special mention, althouth this breathtaking coding falls out of the context of what we want to achieve, to Mickey Mania and its Moose Chase level. Jon Burtonback then found the way to send 16 colors during one scanline and with that he built a dinamic texture into CRAM (sooooooo crazy but worked flawlessly). It is quite cool watching it reel into the CRAM debug screen.

There are some others games like Comix Zone (1 palette color reloading ingame) or Addam's Family Values (1 color

-------------------------

And for an introduction ... I guess it's enough.

So I felt Iike trying to find a simple method for everyone to make use of hscan color reloading.

At the very beggining I was not aware, didn't have a clue, of how SGDK could lead with this kind of routines. I started trying this:

Code: Select all

#include <genesis.h>
#include "gfx.h"

// GLOBAL DATA ///////////////////////////////////////////
const u16 colors[24] =
{
  0x0E06, 0x0E04, 0x0E02, 0x0E00, 0x0C00, 0x0A00, 0x0800, 0x0600,
  0x0400, 0x0200, 0x0000, 0x0200, 0x0400, 0x0600, 0x0800, 0x0A00,
  0x0C00, 0x0E00, 0x0E02, 0x0E04, 0x0ECC, 0x0EAA, 0x0E88, 0x0E66
};

u16 *p_color;
u16 color;

// /////////////////////////////////////////////

//---------------------------------------------------------
// TEST HINT COLOR
//---------------------------------------------------------
void HBlankColorLandscape()
{
    color = *p_color++;

    asm("move.l #0xc0000000, 0xc00004.l");
    asm("move.w color, 0xc00000.l");
}

/**********************************************************
 MAIN
**********************************************************/
int main( )
{
	// Initialization lot _____________________________________________________

	VDP_setScreenWidth320();
       VDP_setReg(10, 0x08);  // HInt routine callback ecah 8 lines


    // Process ________________________________________________________________

    // Dump the image to A plan
    VDP_drawImageEx(PLAN_A, &backalley_image, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, TILE_USERINDEX), 0, 0, FALSE, TRUE);

    // HInt config
    VDP_setHInterrupt(1);
    SYS_setHIntCallback(HBlankColorLandscape);

    // MAIN LOOP
    while(1)
    {
        // Restore pointer to first color from the array
        puntero_color = &colores[0];

        VDP_waitVSync();
        
        // Reload palette
         VDP_setPaletteColors(0, (u16*) backalley_image.palette->data, 16);

    }


	return (0);
}
It was made for changing the backdrop color each row (8 pixels height). I thought it could be useful to make some simple fades, and I wanted the only change needed for anyone was the edition of the color array.

Some colleagues point me out that sending two colors in a row was an actual possibility as well. That made us think of dithered fadings such as this:

Image

It looked not so bad at Ryu's stage.

Image

And I also did a quick non dithered test using that "two color row by row reloading" with this gameboy Taz screenshot.

Image

BUT ...

CRAM DOTS everytime.
Code was too slow jumping from the first callback routine to the asm HINT routine, code takes more than a scanline drawing time to be completed.

Image

Prettending to fit this code into the Hint Callback ... too much dreaming.

Image

The assembler code I got was this:

Code: Select all


    .align 2

*--------------------------------------------------*
*  LABELS                                                  *
*--------------------------------------------------*
    .set VDP_CTRL, 0xC00004
    .set VDP_DATA, 0xC00000

    .set PAL0_COL0, 0xC0000000
    .set PAL0_COL1, 0xC0020000
    .set PAL0_COL2, 0xC0040000
    .set PAL0_COL3, 0xC0060000
    .set PAL0_COL4, 0xC0080000
    .set PAL0_COL5, 0xC00A0000
    .set PAL0_COL6, 0xC00C0000
    .set PAL0_COL7, 0xC00E0000
    .set PAL0_COL8, 0xC0100000
    .set PAL0_COL9, 0xC0120000
    .set PAL0_COLA, 0xC0140000
    .set PAL0_COLB, 0xC0160000
    .set PAL0_COLC, 0xC0180000
    .set PAL0_COLD, 0xC01A0000
    .set PAL0_COLE, 0xC01C0000
    .set PAL0_COLF, 0xC01E0000

*--------------------------------------------------*
*  DATA                                              *
*--------------------------------------------------*
    .data

    | Color pointer
    .globl  pcolores
    .comm   pcolores, 4, 4

    | Color pairs array
    .globl  Colores
    .type   Colores, @object
    .size   Colores, 24
Colores:
    .long 0x00E0000E, 0X026A0026, 0X0E000A00, 0X0600000E
    .long 0X000A0006, 0X00EE00AA, 0X0EEE0AEA, 0X06E60000
    .long 0x0EAA0EAA, 0x0C880A66, 0x08440622, 0x04000EE8
    .long 0x0EEE0200, 0x06420C84, 0x0EC60448, 0x0A0E022E
    .long 0x0246068A, 0x04680246, 0x00240002, 0x000008AC
    .long 0x00060004, 0x04440888, 0x0EEE0222, 0x008E0CCC


/****************************************************
 * HBLANKFONDOCOLORES                               *
 *--------------------------------------------------*
 *                                                  *
 *                                                  *
 ****************************************************/
    .align  2
    .text

    .globl  HBlankFondoColores
    .type   HBlankFondoColores, @function

HBlankFondoColores:

    move.l #PAL0_COL0, VDP_CTRL
    move.l pcolores, %a0
    move.l (%a0)+, VDP_DATA
    move.l %a0, pcolores
    rts
The C code:

Code: Select all

******************************************************************************/
#include <genesis.h>

#include "gfx.h"

#include "hintcode.h"


/**********************************************************
 MAIN
**********************************************************/
int main( )
{
	// Initialization lot _____________________________________________________

	VDP_setScreenWidth320();

  	VDP_setReg(10, 0x08);


  // Proceso ________________________________________________________________

  SPR_init(16, 256, 256);

  VDP_drawImageEx(PLAN_A, &backalley_image, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, TILE_USERINDEX), 0, 0, FALSE, TRUE);
  VDP_setPaletteColors(0, (u16*) backalley_image.palette->data, 16);

  VDP_setHInterrupt(1);
  SYS_setHIntCallback(HBlankFondoColores);

  // MAIN LOOP
  while(1)
  {
    pcolores = Colores[0];

    //SPR_update();

    VDP_waitVSync();
  }

	return (0);
}
-------------------------

Any ideas about making SGDK a bit more friendly for hint-color routines?

At least for those "two color reload each row, always same indexe" and also for "any colors, 2 by 2, increasing color index".
Last edited by realbrucest on Wed Apr 01, 2020 9:24 am, edited 2 times in total.

Miquel
Very interested
Posts: 514
Joined: Sat Jul 30, 2016 12:33 am

Re: Brainstorming a way to hscan colors in SGDK

Post by Miquel » Tue Mar 31, 2020 1:41 am

Set the exception to jump to ram, there jump again to the desired h-int routine, you can use an absolute word jump, then wrote your routine in asm. It’s important to only push/pop the registers you’re going to use if you are cpu constrained; also there is the user stack pointer (usp) so you can swap it with one address register without using the stack.

Another possibility is to use ram memory to execute the routine, also gives the possibility to inject directly values into the code without the need to load variables and use registers. Every time you need a new routine you copy it.

Depending on what you need there are two approaches, for some plans you will need speed, speedy code or cpu performance will vastly decrease. On other occasions you will need pretty exact timing, and again you will only obtain it with certainty with assembler.

That’s all I have learned so far.
HELP. Spanish TVs are brain washing people to be hostile to me.

cero
Very interested
Posts: 338
Joined: Mon Nov 30, 2015 1:55 pm

Re: Brainstorming a way to hscan colors in SGDK

Post by cero » Tue Mar 31, 2020 8:23 am

You may have to wait for the next hblank in your code.

Code: Select all

-------------
         *>>>
>>>>>>>>=====
-------------
Pretty ascii art. Say the h irq fires at *, which is too late for your code to hit hblank. Insert nops or other constant-time waiting, >, the right amount so that your code, =, hits exactly the right spot.

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Brainstorming a way to hscan colors in SGDK

Post by Stef » Tue Mar 31, 2020 12:19 pm

SGDK HInt handler is definitely slow when you need to do a specific task at each scanline...
The interrupt process itself consume up to 44 cycles + ~124 cycles (just quickly did head cycle counting) for the ASM wrapper + 16 cycles for RTS from C routine means that you have ~184 cycles on the ~484 available per scanline already lost (~38% of CPU time), so only ~300 cycles remaining for the C code which can reasonably be used for the next scanline if you plan to do specific hblank process task as cero said.
I think in that case it's just better to just override the default SGDK handler and put your assembly code in place. It's why SGDK let you the opportunity to customize the sega.s file (which is always duplicated into your project by the makefile) so you can change it as you need :)
Also when you need to do some fast process like that in your HInt function, try to use code that don't modify registers so you don't need to save / restore them. You can also prepare / cache some registers at the end of the vint process (making A4 pointing on VDP CTRL port for instance) but it's unlikely to work when you are programming in C unfortunately..

realbrucest
Interested
Posts: 27
Joined: Wed Sep 21, 2011 9:00 pm
Location: Sevilla, Spain
Contact:

Re: Brainstorming a way to hscan colors in SGDK

Post by realbrucest » Tue Mar 31, 2020 6:13 pm

Thanks to you all three. I get quite interesting info for everything you mention.

I was not confident at all about smashing SGDK files, because I wasn't aware of that matter with the duplicate "sega.s" file content. Now, knowing that it is safe messing with that file inside the project folder I guess I have the propper approach for me to keep digging.

I hope I can start dropping here any brand new (not too complicated) tutorial-demos on hscan-recolring over time.

Cheers.

Sik
Very interested
Posts: 939
Joined: Thu Apr 10, 2008 3:03 pm
Contact:

Re: Brainstorming a way to hscan colors in SGDK

Post by Sik » Wed Apr 01, 2020 4:48 am

Not related to your question, but how did you manage to miss the Samurai Shodown character select? (especially in 2P) It takes palette swapping even further.

Also yeah, timing is a big issue, even if you use asm honestly, because when the 68000 responds to the interrupt the VDP will be likely in a point where it's horribly problematic — you're better off setting up an interrupt to respond to the previous line and wait it out (maybe do whatever extra computations you need while you wait).
Sik is pronounced as "seek", not as "sick".

realbrucest
Interested
Posts: 27
Joined: Wed Sep 21, 2011 9:00 pm
Location: Sevilla, Spain
Contact:

Re: Brainstorming a way to hscan colors in SGDK

Post by realbrucest » Wed Apr 01, 2020 8:25 am

Sik wrote:
Wed Apr 01, 2020 4:48 am
Not related to your question, but how did you manage to miss the Samurai Shodown character select? (especially in 2P) It takes palette swapping even further.
I checked a lot of games one by one looking for this type of trickery, but I already knew that Samurai Shodown did it at the fightings scenes (I did not know about its player select screen), so when I took the emulator for taking the screenshot I didn't go further of the attrack sequence, I missed the player select screen... (dumb me).

* I've just updated the first post with that.

Noted that hint about working with custom interrupts as well to save/split proccess time. Thanks again.

EDIT for not to make the first post a bit eternal:

Image

Comix Zone reloads the palette used for the lower vignette scenario. The line where the color reloading starts varies depending on the map vertical scroll such as the common water coloring effects do.

cloudstrifer
Very interested
Posts: 118
Joined: Mon Feb 19, 2018 7:31 pm

Re: Brainstorming a way to hscan colors in SGDK

Post by cloudstrifer » Sun Apr 26, 2020 2:52 pm

Hi, I'm trying to change colors in palette during hblank, but i got fickering.
Do you found any solution?

Thank you.

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Brainstorming a way to hscan colors in SGDK

Post by Stef » Mon Apr 27, 2020 3:02 pm

The idea is to change only a few colors at once and do it in no active display, you can use the HCounter for that, loop waiting for a specific HCounter value then do your CRAM transfer as fast as possible. Repeat the operation on several scanline until you transferred the desired amount of color.

cloudstrifer
Very interested
Posts: 118
Joined: Mon Feb 19, 2018 7:31 pm

Re: Brainstorming a way to hscan colors in SGDK

Post by cloudstrifer » Mon Apr 27, 2020 11:12 pm

With this code I can change the first 2 colors, how can I change the 3rd color for example?

Code: Select all

  asm("move.l #0xC0000000, 0x00C00004");
  asm("move.b #0x00, 0x00C00000");
  asm("move.b #0x00, 0x00C00000");
Thank you.

Sik
Very interested
Posts: 939
Joined: Thu Apr 10, 2008 3:03 pm
Contact:

Re: Brainstorming a way to hscan colors in SGDK

Post by Sik » Wed Apr 29, 2020 12:10 am

Um, you're supposed to do word writes, not byte writes (incidentally, a color is word-sized). You can use longword too (which the VDP sees as two words). Byte writes are likely not doing what you expect.

Also this should give you an idea:
https://plutiedev.com/tiles-and-palette ... g-palettes

The macro used to set the address is defined here (I probably should reference the page…):
https://plutiedev.com/writing-video#setting-address
Sik is pronounced as "seek", not as "sick".

Post Reply