Jump to content
  • 0

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


RaichuBender
 Share

Question

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. 

Edited by RaichuBender
embedded code with syntax highlighting
Link to comment
Share on other sites

6 answers to this question

Recommended Posts

  • 0

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.

  • Like 1
Link to comment
Share on other sites

  • 0
Posted (edited)
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? 😛

Edited by RaichuBender
Link to comment
Share on other sites

  • 0

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

Edited by Greg King
Syntax-highlight the C code.
  • Like 1
Link to comment
Share on other sites

  • 0
Posted (edited)

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

Edited by RaichuBender
Trying to remove redundant quote
Link to comment
Share on other sites

  • 0

 

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?

  • Like 1
Link to comment
Share on other sites

  • 0
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.

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

Please review our Terms of Use