Page 1 of 1

Trying to wait for VSYNC that never happens crashes my game (C programming)

Posted: Fri Jun 18, 2021 2:41 am
by RaichuBender

Heya,



I'm trying to wait for a VSYNC to occur, so the title screen of my game won't flicker.

However, when setting the VSYNC bit of the EIN-register, it appears ISR is never set. Am I missing something?

My code:

 


#define EIN (*(u8*)0x9F26)
#define ISR (*(u8*)0x9F27)

#define VSYNC() \
{   EIN |= 1;\
    
while( !(ISR & 1) );    }

/* ... */

static void show_choices(TITLE *ttl)
{
    u8 y
,hand_pos;

    
while (1)
    
{
        SET_TILE_STR
(12, 14,    "+--------------+", TITLE_COL_MENU);
        SET_TILE_STR
(12, 15,    "|              |", TITLE_COL_MENU);
        
for (= 0; y < ttl->num_options; y++)
        
{
            SET_TILE_STR
(12, y+16,"| -            |", TITLE_COL_MENU);
            SET_TILE_STR
(16, y+16,ttl->OPTIONS_STR[y], TITLE_COL_MENU);
        
}
        SET_TILE_STR
(12, y+16,  "|              |", TITLE_COL_MENU);
        SET_TILE_STR
(12, y+17,  "+--------------+", TITLE_COL_MENU);

        SET_TILE
(13, hand_pos+16, '>', TITLE_COL_BG);
        hand_pos 
= (hand_pos + 1) & 3;
        VSYNC
();
    
}
}



 


SET_TILE_STR and TILE_COL_*** are macros that deal with updating the tile maps, so they aren't important for this question. It's the VSYNC at the bottom that locks up the system...

EDIT: I realize this function is within a while-loop that never get escaped out of. However, that is intentional, as I'm still working on the title screen. The problem is that the VSYNC never occurs as far as the game is concerned. 


Trying to wait for VSYNC that never happens crashes my game (C programming)

Posted: Fri Jun 18, 2021 2:51 am
by Elektron72

When the VSYNC interrupt occurs, the system jumps through the vector located at $0314, which points to the kernal interrupt handler (unless the vector has been modified). This routine performs several functions (polling the keyboard, updating the system timer, etc.). Before returning, the routine clears the interrupt status in ISR to prevent the interrupt from immediately triggering again. To properly wait for VSYNC, you will need to modify the vector at $0314 to point to your own interrupt handler. A custom handler will likely set a value somewhere in memory indicating that an interrupt has occured, and then jump to the original handler.


Trying to wait for VSYNC that never happens crashes my game (C programming)

Posted: Fri Jun 18, 2021 2:56 am
by RaichuBender


3 hours ago, Elektron72 said:




When the VSYNC interrupt occurs, the system jumps through the vector located at $0314, which points to the kernal interrupt handler (unless the vector has been modified). This routine performs several functions (polling the keyboard, updating the system timer, etc.). Before returning, the routine clears the interrupt status in ISR to prevent the interrupt from immediately triggering again. To properly wait for VSYNC, you will need to modify the vector at $0314 to point to your own interrupt handler. A custom handler will likely set a value somewhere in memory indicating that an interrupt has occured, and then jump to the original handler.



Ah, that must be it. thank you!

It seems I'm misunderstanding the workings of the ISR register then. 

 

UPDATE:

I did it!

Here's a video of it VSYNC-ing after every BG line drawn: (PETSCII characters are placeholders for now.)





Can you guess where this placeholder BG is a reference to? ?


Trying to wait for VSYNC that never happens crashes my game (C programming)

Posted: Fri Jun 18, 2021 7:03 am
by Greg King

In order to do what you want, your program needs two things:


  1. It needs to know when the vertical blanking is happenning.


  2. It needs to keep control of the computer.


Your current code has number 1; but, number 1 can't work because it doesn't have number 2 (Kernal takes control and cancels number 1).

Your program can get number 2 by temporarily preventing the CPU from seeing interrupts.  The usual way in C code is to use a "SEI()" macro.  Then, your code waits for the VSYNC flag. After it sees that flag, your code updates the menu.  Then, it allows the CPU to notice interrupts again [a CLI() macro].  After that, your code gets player input, then possibly repeats the loop.  [Notice that VSYNC() is done at the beginning of the loop, not at the end.]

#define VSYNC() while (!(ISR & 1))

do {
  SEI
();
  VSYNC
();

 
// Redraw the new menu.
 
// And update the positions of "carots" and highlights.

  CLI
();
 
// Handle input from the player.
} while (!finished);

How your code gets the SEI() and CLI() macros depends on which C compiler you're using (your code looks like KickC).


Trying to wait for VSYNC that never happens crashes my game (C programming)

Posted: Fri Jun 18, 2021 10:04 am
by RaichuBender

Currently, I'm wrapping a check whether the VSYNC occurred around the interrupt handler, which in turn sets a `vsync` boolean to true if it has.

Will append the code of my implementation when I'm home in a bit, so you can give your input if you want ?

Also, I've always put the VSYNC delay at the end of any drawing loop. As there any benefit of putting it at the start? Most of my programming work so far has been in BASIC, so I might have been doing it in a not optimal way forever... 


Trying to wait for VSYNC that never happens crashes my game (C programming)

Posted: Fri Jun 18, 2021 10:11 am
by desertfish

 


6 minutes ago, RaichuBender said:




the interrupt handler, which in turn sets a `vsync` boolean to true if it has.



So you already have an interrupt handler? so just do the vsync processing in there, why even wait for a vsync elsewhere?


Trying to wait for VSYNC that never happens crashes my game (C programming)

Posted: Fri Jun 18, 2021 10:42 am
by RaichuBender


24 minutes ago, desertfish said:




So you already have an interrupt handler? so just do the vsync processing in there, why even wait for a vsync elsewhere?



Simply because I want to have precise control over my code, including when it should wait for a VSYNC, and when tearing and flickering might be ok for speed's sake.

But I could probably move all the logic to the interrupt handler as you suggest, and keep the control by specifying when the routine should be skipped or not based on macros in my loops. 

I guess the best way to explain what I want to be able to achieve is screen fill/wipe animations like the fancy Pokémon battle start animations that fill the screen with tiles in a maze-like pattern for example. Having precise control of interrupts without having to retrofit everything in a large event is the best approach I find.