News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_Arnaud

How correctly synchronize interruption with raster [CPCTelera]

Started by Arnaud, 20:58, 08 January 16

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Arnaud

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.

arnoldemu

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.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Xifos


Arnaud

Thanks for help @arnoldemu,
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){};
}

arnoldemu

@Arnaud: 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. :)
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Arnaud

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 : 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.

Arnaud

Here is Cpctelera project with only interrupt and a picture on screen.

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

@arnoldemu : With your vsync modification, i have exactly the same image on real cpc 6128+  :)

Arnaud.

ronaldo

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, 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 :).

Arnaud

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
}


Executioner

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?

Arnaud

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 ?

arnoldemu

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?
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Arnaud

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]

arnoldemu

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.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

reidrac

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).
Released The Return of Traxtor, Golden Tail, Magica, The Dawn of Kernel, Kitsune`s Curse, Brick Rick and Hyperdrive for the CPC.

If you like my games and want to show some appreciation, you can always buy me a coffee.

ronaldo

@Arnaud: 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 suggests, and @Executioner 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.

arnoldemu

@Arnaud: 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 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  ;)
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Arnaud

Quote from: ronaldo on 17:54, 10 January 16
@Arnaud: 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:
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.

Arnaud

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.

arnoldemu

@Arnaud: 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...)


My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

arnoldemu

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.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

ronaldo

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, 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.

Arnaud

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.

ronaldo

@Arnaud: 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.

AMSDOS

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
* Using the old Amstrad Languages :D   * with the Firmware :P
* I also like to problem solve code in BASIC :)   * And type-in Type-Ins! :D

Home Computing Weekly Programs
Popular Computing Weekly Programs
Your Computer Programs
Updated Other Program Links on Profile Page (Update April 16/15 phew!)
Programs for Turbo Pascal 3

Powered by SMFPacks Menu Editor Mod