CPCWiki forum

General Category => Programming => Topic started by: Arnaud on 20:58, 08 January 16

Title: How correctly synchronize interruption with raster [CPCTelera]
Post by: Arnaud on 20:58, 08 January 16
Hello, and happy new year to the cpcwiki community.

I'm playing with Raster and CRTC to display the picture of introduction of my game and i don't know how correctly synchronize the palette change with the interruption.

All works very well with CPCE95 (i know, never trust emulator  ;) ), there's a little glitch with Winape (color changes in middle of a line) and with my real 6128+ it's totally wrong (color aren't set in the right area, and color changes in middle of a line)

Here my code, i keep only the minimum in the interrupt function to only change palette according to the interruption number.

void InterruptHandlerIntro()
{
    static u8 timer = 0;
   
    switch(timer++)
    {
        case 0 :
        {
            static const char pal[] = {    20,   20,   0,  11   };
            cpct_setPalette(pal, 4);
            return;
        }
   
        case 1 :
        {
            static const char pal[] = {    4,    20,   7,  12 };
            cpct_setPalette(pal, 4);   
            return;
        }
   
        case 2 :
        {
            static const char pal[] = {  4, 20,  11,  23 };
            cpct_setPalette(pal, 4);   
            return;       
        }
   
        case 3:
        {
            static const char pal[] = {    12, 20, 23, 11};
            cpct_setPalette(pal, 4);
            return;           
        }   
       
        case 4 :
        {
            static const char pal[] = { 6, 20, 23, 0 };
            cpct_setPalette(pal, 4);
            return;
        }   
       
        case 5 :
        {
            static const char pal[] = {    4, 12,  20,  28 };
            cpct_setPalette(pal, 4);
            timer = 0;   
            return;           
        }   
    }
}

void crtc()
{
    u16 firmwareCode;
    ReadFileBin("title.scr", (char*)0xC000);
   
    firmwareCode = cpct_disableFirmware();
    cpct_setBorder(20);   

    cpct_waitVSYNC();
    cpct_setInterruptHandler(InterruptHandlerIntro);

    CRTC( CRTC_R1, 30);
    CRTC( CRTC_R2, 41);
    CRTC( CRTC_R6, 33);
    CRTC( CRTC_R7, 34);
   
    while(1){};
}


Thanks,
Arnaud.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: arnoldemu on 21:18, 08 January 16
Your code is good but needs a couple of tweaks.

1. the wait for vsync will pass if vsync is already active, so you need to wait for vsync start, then end, and then start again. Not sure how to do this in cpctelera.
In asm:


ld b,&f5
l1:
in a,(c)
rra
jr nc,l1
l2:
in a,(c)
rra
jr c,l2
l3:
in a,(c)
rra
jr nc,l3


2. the interrupt happens at end of hsync. You will always need some nops to synchronise it with the start of the line this is true on the cpc, and it does depend on horizontal sync position. Use the real CPC to get this correct. To add nops put a __asm block in I think.

I hope cpct_setPalette sets the hardware colours and doesn't enable or disable interrupts. ronaldo can answer this.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: Xifos on 10:36, 09 January 16
Is CPCE95 enough accurate on CRTC emulation ?
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: Arnaud on 10:52, 09 January 16
Thanks for help @arnoldemu (http://www.cpcwiki.eu/forum/index.php?action=profile;u=122),
but my code doesn't work very and i have a new problem i don't understand.

- I have replace the switch case with a direct access to a multi-dimension array, i thought it was a faster way to access palette values.
But when i use the new function, my palette areas are not synchronised anymore and color are constantly flipping.

- I haven't see the difference on screen with your asm vsync code (with my switch case version) i always have a  little glitch with Winape (color changes in middle of a line), i'll try several NOP but it didn't improve my synchro, my NOP are not set in the right place ?

Well it's not easy to understand what's happened  ??? .

void InterruptHandlerIntro()
{
    static u8 timer = 0;
    static const char pal[6][4] = {
                                {    20,   20,  0,  11  },
                                {    4,   20,  7,  12   },
                                {    4,   20,  11,   23   },
                                {    12,   20,   23,  11   },
                                {     6,  20,   23,   0    },
                                {    4,   12,   20, 28    } 
                           };
    __asm
        NOP;   
        NOP;
    __endasm;
   
    cpct_setPalette(pal[timer++], 4);
    if (timer == 5)
        timer = 0;
}

void crtc()
{
    ReadFileBin("title.scr", (char*)0xC000);
    cpct_disableFirmware();
   
    CRTC( CRTC_R1, 30);
    CRTC( CRTC_R2, 41);
    CRTC( CRTC_R6, 33);
    CRTC( CRTC_R7, 34);
   
    cpct_setBorder(20);   

    __asm
        ld b,#0xf5
        l1:
            in a,(c)
            rra
            jr nc,l1
        l2:
            in a,(c)
            rra
            jr c,l2
        l3:
            in a,(c)
            rra
            jr nc,l3
    __endasm;

    cpct_setInterruptHandler(InterruptHandlerIntro);
   
    while(1){};
}
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: arnoldemu on 10:56, 09 January 16
@Arnaud (http://www.cpcwiki.eu/forum/index.php?action=profile;u=1424): Please would you make code which is restricted to interrupt and colour changing and nothing more.
Then please post it here and I will take a look and see if I can fix it. :)
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: Arnaud on 11:30, 09 January 16
Edit 2 : Line problem is always here, but with this code it's not visible. I think i had to fill screen with color lines to see the problem.

Edit : while writing my simplified code i have seen the problem of area flickering


    if (timer == 6)  :doh:
        timer = 0;


@arnoldemu (http://www.cpcwiki.eu/forum/profile/?u=122) : here my simplified code :

void InterruptHandler()
{
    static u8 timer = 0;
    static const char pal[6][4] = {
                                {    20,    20,  0,  11    },
                                {    4,      20    7,    12    },
                                {    4,   20,  11,   23     },
                                {    12,     20,    23,    11   },
                                {     6,     20, 23,   0  },
                                {    4,    12,  20, 28     }
                            };
    __asm
        NOP;   
        NOP;
    __endasm;
   
    cpct_setPalette(pal[timer++], 4);
    if (timer == 5)
        timer = 0;
}

void main(void)
{
    cpct_disableFirmware();
   
    __asm
        ld b,#0xf5
        l1:
            in a,(c)
            rra
            jr nc,l1
        l2:
            in a,(c)
            rra
            jr c,l2
        l3:
            in a,(c)
            rra
            jr nc,l3
    __endasm;

    cpct_setInterruptHandler(InterruptHandler);
   
    while(1){};
}


And Cpctelera project.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: Arnaud on 13:32, 09 January 16
Here is Cpctelera project with only interrupt and a picture on screen.

In this example there 's the problem on a line.

@arnoldemu (http://www.cpcwiki.eu/forum/index.php?action=profile;u=122) : With your vsync modification, i have exactly the same image on real cpc 6128+  :)

Arnaud.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: ronaldo on 20:22, 09 January 16
Quote from: arnoldemu on 21:18, 08 January 16
I hope cpct_setPalette sets the hardware colours and doesn't enable or disable interrupts. ronaldo can answer this.
Whenever in doubt, @arnoldemu (http://www.cpcwiki.eu/forum/index.php?action=profile;u=122), you can search by the name of the function in google, and find its assembly code in github. Every function's code from CPCtelera is available there. Here you have the link for cpct_setPalette. It does not enable/disable interrupts.

Quote from: Arnaud on 10:52, 09 January 16
- I haven't see the difference on screen with your asm vsync code (with my switch case version) i always have a  little glitch with Winape (color changes in middle of a line), i'll try several NOP but it didn't improve my synchro, my NOP are not set in the right place ?
If color changes in the middle of a line, you will probably need many NOPs to wait for the right border of the screen. Take into account that you are not developing directly in assembler: any minimal change you make to your C code may result in a totally different assembler code, probably with different timing.

You will probably need diferent amount of NOPs depending on the interrupt number (0-5) as they may occur on different columns.

With respect to properly waiting until the start of VSYNC, you can also do it this way:

cpct_waitVSYNC();
__asm
  halt
  halt
__endasm;
cpct_waitVSYNC();

This will probably save you some bytes.

And with respect to using a table rather than individual vectors, your code is probably similar in speed terms, but you have surely gained a lot of bytes from reducing code size :).
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: Arnaud on 20:53, 09 January 16
It works thanks to all.

I have a little cheated with the line drawed during interrupt because now i use the same colors for the two area.

I guess the NOP should be inserted in this way :

if (timer == 4)
{
    for (i= 0; i < X; i++)
        NOP
}

Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: Executioner on 02:56, 10 January 16
Quote from: Arnaud on 10:52, 09 January 16
-i always have a  little glitch with Winape (color changes in middle of a line), i'll try several NOP but it didn't improve my synchro, my NOP are not set in the right place ?

The latest version 2.0b1/2 has more accurate palette changes. There are differences in position between CPC and Plus, but they're only a few pixels. The only way it could be happening in the middle of the line is if your timing is completely wrong. Getting palette change timing working in C sounds like a real challenge to me, what if the next compiler has a new optimisation?
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: Arnaud on 14:04, 10 January 16
I don't understand why the synchronisation is wrong only for the first line of the last area (6th interruption).

For the last interruption is a temporisation or a synchronisation needed for the next screen refresh ?
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: arnoldemu on 14:32, 10 January 16
Quote from: Arnaud on 14:04, 10 January 16
I don't understand why the synchronisation is wrong only for the first line of the last area (6th interruption).

For the last interruption is a temporisation or a synchronisation needed for the next screen refresh ?
Does it ever move?
EDIT: Say reset CPC and load again, does it move?
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: Arnaud on 15:17, 10 January 16
It's always at the same place on emulator and on real cpc.

Here a screenshot (work in progress  :D ), i added a pink circle to show the flickering red line area.

The last area background is in black in order to show the problem and correspond to the last interrupt.

[attach=2]
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: arnoldemu on 17:34, 10 January 16
is colour change done first in interrupt?

I am thinking some other interrupt processing is happening before this and this delays your code.
I thought perhaps keyboard is being read on this interrupt (not always the same place) and this changes it's position.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: reidrac on 17:41, 10 January 16
Quote from: Arnaud on 20:53, 09 January 16
It works thanks to all.

I have a little cheated with the line drawed during interrupt because now i use the same colors for the two area.

I guess the NOP should be inserted in this way :

if (timer == 4)
{
    for (i= 0; i < X; i++)
        NOP
}


You probably want to write the NOP loop in ASM, otherwise the code the compiler generates will make really hard to adjust the timing (eg, the for loop, i being a local variable, etc).
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: ronaldo on 17:54, 10 January 16
@Arnaud (http://www.cpcwiki.eu/forum/index.php?action=profile;u=1424): That's a sincronization problem due to your code not taking the same amount of time to process every frame. Most probably, this issue is inside your interrupt routine. As @reidrac (http://www.cpcwiki.eu/forum/index.php?action=profile;u=1504) suggests, and @Executioner (http://www.cpcwiki.eu/forum/index.php?action=profile;u=17) and I have told you previously, you probably want to code your interrupt hander completely in asm. That's the only way you can guarantee an stable and concrete amount of processing time.

I don't now how your code looks like now, but the "if (timer==4)" creates two different branches on your code, with different timing. Things like this can make your code take different amounts of time. To know what happens, the best will be to have a look at the generated assembly code. That can give us an insight on the problem. But, most probably, the proper solution will be coding your interrupt routine in asm.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: arnoldemu on 18:01, 10 January 16
@Arnaud (http://www.cpcwiki.eu/forum/index.php?action=profile;u=1424): It's not the total processing time that is a problem as long as your total processing time is less than 32 scanlines.

What @ronaldo (http://www.cpcwiki.eu/forum/index.php?action=profile;u=1227) is saying is that with separate ifs, it takes longer to get to the last if than the first. The time is therefore different and you would need different number of nops to balance it.

Consider using a switch statement instead of an if.

e.g.

switch (interrupt_index)
{
case 0:
...
break;
case 1:
break;
}

the compiler should generate a constant time jump to each piece of code but it may choose not to depending on optimisation settings. (smaller code vs fastest)


Another thing is you could do this:

uchar pal1[16]={0x04a,0x054 .. };

const uchar *palettes[6]={pal1, pal2, pal3, pal4, pal5...};

void int_handler()
{
// add nops here.
  cpct_setPalette(palettes[interrupt_index]);

// now do processing which can take time here..
}

this would also be constant time and the compiler can't cause you trouble  ;)
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: Arnaud on 18:43, 10 January 16
Quote from: ronaldo on 17:54, 10 January 16
@Arnaud (http://www.cpcwiki.eu/forum/index.php?action=profile;u=1424): That's a sincronization problem due to your code not taking the same amount of time to process every frame. Most probably, this issue is inside your interrupt routine.

I thought the only condition for my raster to work was to have code interruption really faster than the duration of the interruption.
I also understand why the problem is only to the last interruption because of this :
if timer == 6
{
   timer = 0;
}


@arnoldemu (http://www.cpcwiki.eu/forum/index.php?action=profile;u=122):
I try with this function, now my red line is perfectly constant, but always here  ;D

void InterruptHandlerIntro()
{               
    __asm
        NOP
    __endasm;

    cpct_setPalette(gPal[gTimer], 4);
    gTimer = ++gTimer % 6;
}


I guess the modulo is responsive of the timing difference.

I'll see asm example on your site.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: Arnaud on 21:52, 10 January 16
Another try, not condition, not switch case, not aritmethic :

void InterruptHandlerIntro()
{
    static u8 index = 0;
    static const u8 nextPalIndex[] = {1,2,3,4,5,0};
               
    cpct_setPalette(gPal[index], 4);
    index = nextPalIndex[index];
}


Always not working, exactly the same flickering line red  :doh: .

Well, well i give up, assembler i have not choice.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: arnoldemu on 22:16, 10 January 16
@Arnaud (http://www.cpcwiki.eu/forum/index.php?action=profile;u=1424): please post again your source. And please post your lst/asm file that is generated. I am sure you are really close. I am not convinced using assembler will help.

I have looked at the cpctelera library code.

cpct_setInterruptHandler patches 0x0038 directly.
changing to use asm will not help here.

cpct_setPalette is setting the colours direct to the hardware. you can improve the code, but really it's not bad.

your updated

void InterruptHandlerIntro()
is fine, to me it is constant time for doing the palette change.

the modulo in your other example was fine too.

the important part was *when* you set the colours and in both pieces of code it is the same place and at the same time.

Which pen is turned red? pen 0?

I think there is something else in your code causing the problem or something that the compiler is generating that we don't see (perhaps it re-enables interrupts or something like that...)


Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: arnoldemu on 22:52, 10 January 16
those static variables inside the function are not good.
make them global.

for every local static variable, c code must check to see if it is initialised or not. then it initialises it.
so there are extra checks there.

I am making a small example file to post here.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: ronaldo on 09:49, 11 January 16
Quote from: arnoldemu on 22:52, 10 January 16
those static variables inside the function are not good.
make them global.

for every local static variable, c code must check to see if it is initialised or not. then it initialises it.
so there are extra checks there.
Well, that's not the case with SDCC. SDCC treats static variables exactly same as global variables. The only difference between them is visibility in C.

In fact, using SDCC+CPCtelera (http://lronaldo.github.io/cpctelera), neither global nor static variables can be initialized because of SDCC thinking it is producing code for ROM. Their initial value is always 0, because of their space being allocated as part of the loaded binary, and containing a 0 there.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: Arnaud on 21:20, 11 January 16
Hello,
i found the problem, it's my function ReadFileBin, when called it disturbs the interruption.

I don't know why, but without this function call, the palette changes is perfect :

void InterruptHandlerIntro()
{               
    static u8 index = 0;
   
    __asm
        NOP
    __endasm;
   
    cpct_setPalette(gPal[index], 4);
    index = ++index % 6;
}


Thanks to all for help.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: ronaldo on 21:52, 11 January 16
@Arnaud (http://www.cpcwiki.eu/forum/index.php?action=profile;u=1424): I remember seeing your function, and it used firmware for reading from files. Whenever you are reading a file, firmware is up, so interrupts are managed by firmware, not by your interrupt manager.

You need to design your application not to read from files while you are creating a synchronized raster effect. In fact, you should not reenable firmware, nor disable interrupts, while you are showing your effect.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: AMSDOS on 23:46, 11 January 16
Quote from: ronaldo on 09:49, 11 January 16
Well, that's not the case with SDCC. SDCC treats static variables exactly same as global variables. The only difference between them is visibility in C.


That's naughty!  :o
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: pelrun on 02:29, 12 January 16
Quote from: AMSDOS on 23:46, 11 January 16

That's naughty!  :o


Not at all. That's exactly how they *should* be handled. (seriously, what's the alternative?)


If you want an example of funny variable handling, look at SDCC for 8051. There isn't really any stack, so all local variables are actually hidden globals unless you mark the function as needing reentrancy. Woo!

Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: AMSDOS on 02:54, 12 January 16
Quote from: pelrun on 02:29, 12 January 16

Not at all. That's exactly how they *should* be handled. (seriously, what's the alternative?)


The alternative is to do it in-house, not globally.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: pelrun on 03:15, 12 January 16
"in-house"? If you're suggesting the compiler should generate code that dynamically allocates the static variable on function access, saves that pointer, and reuses it on subsequent access then you're suggesting something utterly ridiculous (also, where does that pointer get stored? In a "hidden" global? You've not replaced anything, just added a pointless wrapper.)


Variables (static/global/local) differ in two characteristics. Lifetime, and scope. Statics and globals have identical lifetime (infinite) but differing scopes (which means they all have to be allocated at program start), locals have different lifetimes (and so are allocated as and when those lifetimes start.) Therefore "x treats statics and globals identically except for visibility" isn't a design choice, it's just restating the definition.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: AMSDOS on 03:28, 12 January 16
I'm not sure what your suggesting, all I'm saying is a compiler shouldn't take a Locally defined variable and make it global.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: andycadley on 07:05, 12 January 16
Quote from: AMSDOS on 03:28, 12 January 16
I'm not sure what your suggesting, all I'm saying is a compiler shouldn't take a Locally defined variable and make it global.
It doesn't, but the only difference in this case between the two is the scope in which it is accessible, which is an entirely artificial construct of the compiler anyway. As long as it doesn't allow you to access the variable outside of it's scope, then the behaviour is correct.
Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: pelrun on 09:38, 12 January 16
Quote from: AMSDOS on 03:28, 12 January 16
I'm not sure what your suggesting, all I'm saying is a compiler shouldn't take a Locally defined variable and make it global.


Ah, that's where the misunderstanding is. Static variables in C aren't local variables, even if they're defined local to a function, and they're not global either. They're a distinct class which as mentioned before has the lifetime of a global but the scope of a local variable. Allocation and initialisation are done purely based on the lifetime of the variable; scope doesn't matter (and isn't affected.)

Title: Re: How correctly synchronize interruption with raster [CPCTelera]
Post by: reidrac on 09:51, 12 January 16
Just check the ASM code that SDCC generates and compare how it does it with a static variable in the function and using a global initialized variable. Then use the form that best suits your needs ;)

My experience with C is too often "high level" and I find reading the ASM code very educational. Sometimes is worth spending some time rewriting things just to find "the best C for SDCC".
Powered by SMFPacks Menu Editor Mod