Jump to content

Photo

[HELP] disable_nmi()


12 replies to this topic

#1 bfg.gamepassion OFFLINE  

bfg.gamepassion

    Chopper Commander

  • 228 posts
  • Location:Lille - France

Posted Fri Feb 25, 2011 7:02 AM

Hi,

i'm the code of Puzzli. The last version that i've compiler crash randomly on BlueMsx, or do some graphic glitch but always randomly. I've checked all my code and can't see why for the moment.

I'm wondering if it may come from the command enable_nmi()/disable_nmi() from Daniel Bienvenu library.

What the better thing to do :

void nmi() {} // for puzzli

[...]

for (x=0;x<...;x++)
for (y=0;y<...;y++)
{
disable_nmi();
put_char(...,...,...);
enable_nmi();
}

OR

disable_nmi();
for (x=0;x<...;x++)
for (y=0;y<...;y++)
{
put_char(...,...,...);
}
enable_nmi();

Is there recommandation to use these command ?

Thanks a lot !

#2 Kiwi OFFLINE  

Kiwi

    Dragonstomper

  • 589 posts

Posted Sat Feb 26, 2011 11:52 AM

What I know, disable_nmi() should be use if you are going to do anything to the VRAM in one shot, such as fill_vram(0x0000,tiles[],2048) and etc. If you mess with something to the VRAM while the enable_nmi(), there's a chance that it will corrupt the VRAM since it is doing commands in the nmi{} bracket, only bits of commands for the VRAM I believe should be OK. Yet, in C; We do not know how much cycles we have before the NMI(which I think is the end of the list of cycle, then the processor restart from the top).

This what I know from the NMI tags and still figuring this out. Sorry if any of my information might be wrong.

#3 bfg.gamepassion OFFLINE  

bfg.gamepassion

    Chopper Commander

  • Topic Starter
  • 228 posts
  • Location:Lille - France

Posted Sat Feb 26, 2011 10:51 PM

What I know, disable_nmi() should be use if you are going to do anything to the VRAM in one shot, such as fill_vram(0x0000,tiles[],2048) and etc. If you mess with something to the VRAM while the enable_nmi(), there's a chance that it will corrupt the VRAM since it is doing commands in the nmi{} bracket, only bits of commands for the VRAM I believe should be OK. Yet, in C; We do not know how much cycles we have before the NMI(which I think is the end of the list of cycle, then the processor restart from the top).

This what I know from the NMI tags and still figuring this out. Sorry if any of my information might be wrong.


Thanks, it seems that it's not good to use disable/enable to "fast" ...

But in my C code if i don't user nmi, can i disable it and never use it without crash ?

#4 newcoleco OFFLINE  

newcoleco

    Stargunner

  • 1,170 posts
  • In depression
  • Location:Quebec

Posted Sun Feb 27, 2011 3:33 AM

Any update to Video RAM (VRAM) should be done at an appropriate moment. When it's appropriate? When the screen display is turned off -or- during a short period of time right after the NMI interruption. In programming terms, turn off the screen display or put your update VRAM codes in NMI routine to be safe. Everything outside NMI or a turned off screen display is tricky and more exposed to bugs and video corruptions.

For example, for a massive VRAM update, the only solution is to turn off the screen and, of course, disable nmi.

If you don't want to turn off the screen display, you have 2 options : 1 - If it's outside NMI(), use "delay(0);" ( or maybe it's delay(1); ) right before your update VRAM lines ... you may not need to encapsulate with disable_nmi() and enable_nmi(); delay() waits for the next NMI interuptions. 2 - Put your VRAM update lines into the NMI routine (without the disable and enable nmi lines), and use a flag variable to tell your program (with an IF statement) to run these lines when appropriate.

Remember, disable_nmi and enable_nmi don't prevent video corruption, it just prevents the non-maskable interrupt to occurs during the execution of the encapsulated lines. And these routines simply set and reset the NMI flag of the video register, nothing mysterious or strange about that... it's just annoying that other Z80 cpu based systems don't use this NMI thing.

#5 bfg.gamepassion OFFLINE  

bfg.gamepassion

    Chopper Commander

  • Topic Starter
  • 228 posts
  • Location:Lille - France

Posted Sun Feb 27, 2011 4:22 AM

Any update to Video RAM (VRAM) should be done at an appropriate moment. When it's appropriate? When the screen display is turned off -or- during a short period of time right after the NMI interruption. In programming terms, turn off the screen display or put your update VRAM codes in NMI routine to be safe. Everything outside NMI or a turned off screen display is tricky and more exposed to bugs and video corruptions.

For example, for a massive VRAM update, the only solution is to turn off the screen and, of course, disable nmi.

If you don't want to turn off the screen display, you have 2 options : 1 - If it's outside NMI(), use "delay(0);" ( or maybe it's delay(1); ) right before your update VRAM lines ... you may not need to encapsulate with disable_nmi() and enable_nmi(); delay() waits for the next NMI interuptions. 2 - Put your VRAM update lines into the NMI routine (without the disable and enable nmi lines), and use a flag variable to tell your program (with an IF statement) to run these lines when appropriate.

Remember, disable_nmi and enable_nmi don't prevent video corruption, it just prevents the non-maskable interrupt to occurs during the execution of the encapsulated lines. And these routines simply set and reset the NMI flag of the video register, nothing mysterious or strange about that... it's just annoying that other Z80 cpu based systems don't use this NMI thing.


(Je pense que vous savez lire le Français d'après votre blog donc je vais faire une version Anglaise comme je peux et une Française :) ).

English Version :)

Thanks for the explication ... But you say :

"-or- during a short period of time right after the NMI interruption" and "or put your update VRAM codes in NMI routine to be safe".

I understand that. Here for example a portion of my code :

for (x=0;x<6;x++)
for (y=0;y<9;y++)
{
show_object(3+(x*3),5+(y*2),laMer_poisson[x][y]); // Use put_char
}
If i put this portion of code in the NMI and this one is too long to "execute", can't it crash the program ?

Français :

Merci pour les explications. Mais j'ai quelques porblème de compréhension. SI je mets du code dans le NMI() celui ci doit s'executer rapidement non ? Du moins avant la prochaine interruption ?

Ce code :
for (x=0;x<6;x++)
for (y=0;y<9;y++)
{
show_object(3+(x*3),5+(y*2),laMer_poisson[x][y]); // utilise put_char
}
ne s'execute pas je pense en 1/50 ou 1/60 de seconde, ou alors je mélange tout ?

Merci d'avance !!

Edited by bfg.gamepassion, Sun Feb 27, 2011 4:26 AM.


#6 newcoleco OFFLINE  

newcoleco

    Stargunner

  • 1,170 posts
  • In depression
  • Location:Quebec

Posted Mon Feb 28, 2011 1:10 PM

First thing you should know right away is that Z80 don't do things like x*3 as one operation but as multiple additions which is annoying and time consuming. Try to, and this is a trick you will use A LOT in retro videogame projects, use a variable for the offset value.

Instead of this :
for (x=0;x<6;x++)
        for (y=0;y<9;y++)
            {
                show_object(3+(x*3),5+(y*2),laMer_poisson[x][y]); // Use put_char
       	    }


Do this :
/* assume that offset_x and offset_y are declared as well as x and y */

offset_x = 3;
for (x=0;x<6;x++)
{
    offset_y = 5;
    for (y=0;y<9;y++)
    {
        show_object(offset_x,offset_y,laMer_poisson[x][y]); // Use put_char
        offset_y += 2;
    }
    offset += 3;
}

Your comment says "Use put_char"... because you are using "blocks" of chars, you should consider using put_frame instead which is a native ColecoVision bios routine that is faster than doing multiple put_char calls.

Another thing you can do to optimize the video output speed (and avoid a possible corruption) is to update only the parts which need to be updated, not "the whole screen". So, instead of a "brute force" way, you can do the same with a more strategic and smart way... but it's not necessary I think. In my project GhostBlaster, I'm using a brute force technic by updating all the time the entire game screen and it works well, but in a game like Defender, because most of the screen is a void space that doesn't need to be refreshed, it's a more strategic way that is used to do the side scrolling effect.

And, of course, there are other tricks like doing this crucial part more low level with a custom routine in assembly language to speed up even more the computations. You can also take a look at the library source code and try to do something more adapted for your needs instead of using my generic routines all the time.

#7 bfg.gamepassion OFFLINE  

bfg.gamepassion

    Chopper Commander

  • Topic Starter
  • 228 posts
  • Location:Lille - France

Posted Mon Feb 28, 2011 4:53 PM

First thing you should know right away is that Z80 don't do things like x*3 as one operation but as multiple additions which is annoying and time consuming. Try to, and this is a trick you will use A LOT in retro videogame projects, use a variable for the offset value.

Instead of this :

for (x=0;x<6;x++)
        for (y=0;y<9;y++)
            {
                show_object(3+(x*3),5+(y*2),laMer_poisson[x][y]); // Use put_char
       	    }


Do this :
/* assume that offset_x and offset_y are declared as well as x and y */

offset_x = 3;
for (x=0;x<6;x++)
{
    offset_y = 5;
    for (y=0;y<9;y++)
    {
        show_object(offset_x,offset_y,laMer_poisson[x][y]); // Use put_char
        offset_y += 2;
    }
    offset += 3;
}

Your comment says "Use put_char"... because you are using "blocks" of chars, you should consider using put_frame instead which is a native ColecoVision bios routine that is faster than doing multiple put_char calls.

Another thing you can do to optimize the video output speed (and avoid a possible corruption) is to update only the parts which need to be updated, not "the whole screen". So, instead of a "brute force" way, you can do the same with a more strategic and smart way... but it's not necessary I think. In my project GhostBlaster, I'm using a brute force technic by updating all the time the entire game screen and it works well, but in a game like Defender, because most of the screen is a void space that doesn't need to be refreshed, it's a more strategic way that is used to do the side scrolling effect.

And, of course, there are other tricks like doing this crucial part more low level with a custom routine in assembly language to speed up even more the computations. You can also take a look at the library source code and try to do something more adapted for your needs instead of using my generic routines all the time.


Thanks for the offset trick !! I was thinking that the compiler was optimizing such thing ... I was wrong.

I don't update the whole screen, just the part who need when there is something to update. And damned, i haven't see put frame !! It's exactly the routine i need !

Thanks a lot for these advices, i will use them for Puzzli, even if the game is stable now, and near ending.

#8 Kiwi OFFLINE  

Kiwi

    Dragonstomper

  • 589 posts

Posted Tue Mar 1, 2011 6:10 PM


I appreciate this information a lot, thank you Daniel.

#9 Kiwi OFFLINE  

Kiwi

    Dragonstomper

  • 589 posts

Posted Fri Mar 11, 2011 8:47 PM

First thing you should know right away is that Z80 don't do things like x*3 as one operation but as multiple additions which is annoying and time consuming. Try to, and this is a trick you will use A LOT in retro videogame projects, use a variable for the offset value.


This was probably primary the cause of the reset bug in Text Adventure. I was using multiplication in my function to clear the text box space producally.

old code:
static void ClearTextBox(void)
	{
	byte q;
	q = 0;
	while(q < 4)
		{
		fill_vram(0x1a43+32*q,0x80,26);
		q++;
		}
	fill_vram(0x1a03,0x81,26);
	}
new code:
static const void ClearTextBox(void)
	{
	byte q;

	q = 0;
	while(q < 96)
		{
		fill_vram(0x1a23+32+q,0x80,26);
		q+=32;
		}
	fill_vram(0x1a03,0x81,26);

	}

static const function... yes I was that desperate to get this game functioning again. Other codes may have multication, but this is the most use function. I'm going through it and fixing them. Thanks you gracefully for your wonderful advice that could help others with their project like myself.

Off to fill this thing with magic.

Sorry for the double post.

Edited by Kiwi, Fri Mar 11, 2011 8:48 PM.


#10 parkfun101 OFFLINE  

parkfun101

    Star Raider

  • 78 posts
  • Location:Richmond, VA

Posted Sat Jul 19, 2014 9:14 AM

I know this post is old, but thank you newcoleco for posting.  I followed what you said and it fixed the graphic glitches I was getting.  I've now tested over 400 times to repeat the error and it is flawless!!!!!!!  :grin: :grin: :grin: :grin: :grin: :grin:

 

I changed all my vram code to use this format.

 

disable_nmi();
delay(1);    

    rle2vram (FontTitle,0); //Load my Title screen Font
enable_nmi();



#11 hardhat ONLINE  

hardhat

    Moonsweeper

  • 311 posts
  • ColecoVision programmer/advocate
  • Location:Toronto, Canada

Posted Thu Jul 24, 2014 7:31 PM

Actually, delay(1); enables the nmi or requires for it to be enabled. So it would be better to:

delay(1);
disable_nmi();
rle2vram (FontTitle,0); //Load my Title screen Font
enable_nmi();

#12 bfg.gamepassion OFFLINE  

bfg.gamepassion

    Chopper Commander

  • Topic Starter
  • 228 posts
  • Location:Lille - France

Posted Sat Jul 26, 2014 6:49 AM

I know this post is old, but thank you newcoleco for posting.  I followed what you said and it fixed the graphic glitches I was getting.  I've now tested over 400 times to repeat the error and it is flawless!!!!!!!  :grin: :grin: :grin: :grin: :grin: :grin:

 

I changed all my vram code to use this format.

 

disable_nmi();
delay(1);    

    rle2vram (FontTitle,0); //Load my Title screen Font
enable_nmi();

 

 

If you can read French here is a tutorial who explain 3 method to write into Vram and avoid corruption problem :

 

http://bfglesite.sit...ision/vram.html


Edited by bfg.gamepassion, Sat Jul 26, 2014 6:50 AM.


#13 parkfun101 OFFLINE  

parkfun101

    Star Raider

  • 78 posts
  • Location:Richmond, VA

Posted Sat Aug 2, 2014 8:03 PM

The french tutorial works fine for me since the code is just code, plus I can learn a new language.  :)






0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users