Jump to content
Serguei2

Coco won't work well on real colecovision

Recommended Posts

coco_0000.png.7044b4452e79ee7a2261c2310359f53b.png

 

Ok. It's not often I show my own codes but since it's my first release of a colecovision game, it's fine for me and it's the way to learn new things.

 

Coco and the evil robots won't run well on real hardware or bluemsx (an emulator.)

 

So here the source. It might need to clean up as well.

 

coco.c

 

Please add your comments so I know what is changed.

 

Thanks in advance.

 

Share this post


Link to post
Share on other sites

ok i 've had similar problems with corruption.

 

your put_char calls all need to be inside a disable_nmi()  /  release_nmi() sandwich

 

disable_nmi();

put_char(x,y,c);

enable_nmi();

 

same with get_char & print_at and anything that prints or read tiles to the vram.

  • Thanks 1

Share this post


Link to post
Share on other sites
Posted (edited)

Be wary with sprinkling disable_nmi() around. At least in the version of the code I have, it does so by disabling interrupts at the VDP, which itself is an operation that can be interrupted. By default, this is still okay, it looks like the NMI will be off by the time that function call completes, but if you have a user nmi() function, and that function does /any/ VDP work, then it is possible for the write to the VDP register to be corrupted and interrupts will NOT be disabled.

 

It's best not to mess around with the NMI - take control of it and know when in your program it's going to occur, and ensure that point is safe. I'd recommend either all your VDP work inside the NMI handler - it's impossible for it to be interrupted there. Barring that, if your frame rate supports it, invert your paradigm and leave the NMI /disabled/ most of the time, enabling it only at fixed points in your loop. Particularly if you wait for it to occur before continuing, then the disable will always be safe.

 

It's hard to get right. "NMI" is a non-maskable interrupt, meaning that any operation can be interrupted by it unless you can turn it off at the source (the VDP), and a lot of Coleco software tries to balance things "just right" instead of working that basic fact of life into the design.

 

Looking through your code, it's generally pretty good. I'd recommend sprinkling some comments through it. The delay()s all need to go, unless they are for game reasons (but they are all around VDP access, so I suspect it's just trying to do the balancing act I mentioned above). The balancing act will always eventually fall down - especially with aging hardware and now clones, you can't count on 100% exact timing from machine to machine.

 

There are only three conditions on the ColecoVision that it's safe to access the VDP, and some of this depends on how the NMI handler was written:

 

1) the interrupt bit in VDP register 1 is turned off. When it's off, the NMI line is never activated. This is what disable_nmi() does. You can still poll the VDP status register to see if it's time to perform vertical blank operations. Note that writing to a VDP register is an unsafe operation (it takes two writes), so you have to turn it off in a safe way. The Coleco BIOS starts a game with it off.

2) The NMI is still in progress. Once the VDP activates the NMI line, it can't trigger again until the NMI has been cleared by reading the VDP status register. This means that you can safely do as much VDP work as you want inside the NMI, as long as you do it before reading the status register. It can even be multiple frames. As soon as you read the status register, though, all bets are off - it can even fire again on the very next instruction, depending on how late you clear it.

3) The interrupt has just happened. After an NMI triggers, you know that it will be at least 16ms until it happens again. It's important to note this time starts when the interrupt occurs, not when you clear it by reading the status register. If you know the timing of your software very well, you can safely put VDP activity inside that 16ms window. However, this is a still a race - in fact it is by definition. You are trying to finish your VDP work before that 16ms window closes. ;)

 

My library (which I'm not recommending, just commenting) uses the second option. I have a flag that controls whether the NMI handler is allowed to process the NMI - when it's set to disable, the NMI still happens (it's non-maskable, when it happens, it happens!), but no VDP access occurs. Thus, the status register is not read and the NMI is technically still happening. When my software is ready to resume, the enable function checks if it missed anything, and performs the processing at that point.

 

Anyway, a long way of saying you should plan your software around the NMI. Coco's loop looks pretty straight forward, I'd consider putting all the VDP access together and hiding it inside a single "disable_nmi()/enable_nmi()" block rather than putting that everywhere.  You can do the same with your init code (the prints, rle's, etc, bunch them together if you can). If you can do the writes immediately after the NMI occurs, then you also get the benefit that most changes will occur offscreen - no tearing. That's what the interrupt is for - to tell you that the VDP just finished drawing a frame. If you can't reorganize your code, then the disable/work/enable blocks should work.

 

Remember that ALL VDP operations need to be protected - all register writes, all memory writes, and all memory reads. (The status register read is the one exception, but if you manually read that, you will have different conflicts with the NMI...) 

 

Edited by Tursi
  • Like 2
  • Thanks 1

Share this post


Link to post
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
Reply to this topic...

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

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...