Bug Outrunners

Ask anything your want about Megadrive/Genesis programming.

Moderator: BigEvilCorporation

Post Reply
mickagame
Very interested
Posts: 256
Joined: Sat Jun 07, 2008 7:37 am

Bug Outrunners

Post by mickagame »

I have a bug in outrunners with my emulator.
After debugging rom, i think it comes from the interrupt management on vdp side, in particular the way interrupt are masked/unmasked.

At start my code was :

Code: Select all


When 68k write into vdp register :

    case 0x00:			/* CTRL #1 */
        if (hint_pending && !(d & 0x10))
        {
            hint_pending = 0;
            updateIRQLine();
        }
        if (!(d & 0x02)) hc_latch = -1;
        break;

    case 0x01:			/* CTRL #2 */
        if (vint_pending && !(d & 0x20))
        {
            vint_pending = 0;
            updateIRQLine();
        }

void Vdp::updateIRQLine()
{
    if (vint_pending && (reg[1] & 0x20))
    {
        controller->vdpInterrupt(VDP_V_INT_LEVEL);
        status |= 0x0080;   /* V interrupt happened*/
        return;
    }

    if (hint_pending && (reg[0] & 0x10))
    {
        controller->vdpInterrupt(VDP_H_INT_LEVEL);
        return;
    }

    return;
}

The bugs was :

Image

-> The first line of sega top logo wasn't displayed

Image

-> The top of the sky was wrong in 2 screen

The IRQ Line was updated only when interrupt was masked by 68k.
So i correct and change the code (temporary code not optimized).

Code: Select all

      case 0x00:			/* CTRL #1 */
        if ((d & 0x10) != (reg[0] & 0x10))
        {
            if(d & 0x10) reg[0] |= 0x10;
            else reg[0] &= 0xEF;
            updateIRQLine();
        }
        if (!(d & 0x02)) hc_latch = -1;
        break;

    case 0x01:			/* CTRL #2 */
        if ((d & 0x20) != (reg[1] & 0x20))
        {
            if(d & 0x20) reg[1] |= 0x20;
            else reg[1] &= 0xDF;
            updateIRQLine();
        }
=> The Logo screen is now correct.

But the bug in top of the sky isn't gone. The only difference is that it flashes (some frame present and other frames not present).

Are they something specifics for this games to take in account?
How long does an interrupt remains active when it's masked vdp side?
When an interrupt is masked is the interrupt pending cleared?

Thank you for all informations :-)
Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke »

Code: Select all

    if (vint_pending && (reg[1] & 0x20))
    {
        controller->vdpInterrupt(VDP_V_INT_LEVEL);
        status |= 0x0080;   /* V interrupt happened*/
        return;
    }

    else if (hint_pending && (reg[0] & 0x10))
    {
        controller->vdpInterrupt(VDP_H_INT_LEVEL);
        return;
    }
    
    else
    {
        controller->vdpInterrupt(0);
        return;
    }
}

would probably be more correct

Now, 3 things:

- VINT occurence flag is set at quite the same time as the pending VINT pending, on line 224, whatever the state of VDP register 1

- there need to be some delay between VINT flag being set in VDP status and interrupt execution, to allow the cpu to read flag BEFORE interrupt is processed. Some games rely on this.

- if you trigger the interrupt line as soon as VDP register is changed, this will break one particular game (Sesame Street Counting Cafe), maybe more. There need to be some delay (one 68k instruction) between VDP register write and interrupt processing.

I guess it's related to latency (don't know if it's the correct term) between VDP & 68k hardwares: when the VDP change the state of IRQ lines, the CPU already has started executing the next instruction.If this instruction is not executed before the first instruction of VINT routine, the game will lock.

How long does an interrupt remains active when it's masked vdp side?
I leave them indefinitely pending but I don't know if it's correct (that doesn't break anything though): need test on real hardware with interrupt masked and VINT enabled
When an interrupt is masked is the interrupt pending cleared?
no
pending IRQ are handled by the VDP
interrupt masks are handled by the CPU
there is no way for the VDP to know the current 68k interrupt mask

actually, the only time I clear the pending flag is when the interrupt is acknowledged
mickagame
Very interested
Posts: 256
Joined: Sat Jun 07, 2008 7:37 am

Post by mickagame »

Thank you Eke, i will take in account your precious infos.

Another ting :

Horizontal interrupt is more priority than vertical interrupt.
So perhaps it will be better to have :

Code: Select all


    if (hint_pending && (reg[0] & 0x10))
    {
        controller->vdpInterrupt(VDP_H_INT_LEVEL);
        return;
    }   
    else if (vint_pending && (reg[1] & 0x20))
    {
        controller->vdpInterrupt(VDP_V_INT_LEVEL);
        // status |= 0x0080;   /* V interrupt happened => */
        return;
    }
    else
    {
        controller->vdpInterrupt(VDP_H_NULL_LEVEL);
        return;
    }
} 
I will set VINT occurence flag in status register at same time as vint_pending flag.

So the VINT occurence flag is set to 0 at the same time as vint_pending flag is set to 0, when interrupt is acknowledged?

When i say :
When an interrupt is masked is the interrupt pending cleared?
I would say that if interrupt is desactived with reg[0] or reg[1] register if the pending interrupt was cleared?

when you say :
I guess it's related to latency (don't know if it's the correct term) between VDP & 68k hardwares: when the VDP change the state of IRQ lines, the CPU already has started executing the next instruction.If this instruction is not executed before the first instruction of VINT routine, the game will lock.
Does it have relation with FIFO? Between the moment 68k write data and the moment register and IRQ lines are modified 68k has time to execute one instruction?

Note : I add a note on genesis plus source code. I found something strange :

Code: Select all

case 0x01: /* CTRL #2 */
      if (vint_pending && ((d&0x20) != (reg[1]&0x20)))
      {
        /* update IRQ status */
        irq_status &= 0x20;
        irq_status |= 0x110;
        if (d & 0x20) irq_status |= 6;
        else if (hint_pending && (reg[0] & 0x10)) irq_status |= 4;
      }
I don't know exactly what compose irq_status but i found the mask with 0x110 strange. Isn't it?
Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke »

Horizontal interrupt is more priority than vertical interrupt.
no, HINT is level 4 interrupt whereas VINT is level 6
level 6 has higher priority than level 4
However there are some cases where HINT being processed on the same line as VINT would make the CPU miss the VINT occurence: don't know if it's necessary to emulate this but this should be handled in a very specific way (see below)
So the VINT occurence flag is set to 0 at the same time as vint_pending flag is set to 0, when interrupt is acknowledged?
yes

I would say that if interrupt is desactived with reg[0] or reg[1] register if the pending interrupt was cleared?
no, I don't think so

Does it have relation with FIFO?
FIFO only handles DATA port writes, not register writes
but yes, it's related to the fact that any CPU writes to VDP probably don't have an "immediate" effect

I don't know exactly what compose irq_status but i found the mask with 0x110 strange. Isn't it?
this is a dirty way to keep irq latency , irq update flag & irq line state in a single variable (irq_status) ;-)

- bits 0-2 give the irq level

- bit 4 (0x10 mask) is a flag to indicate that irq level need to be updated

- bit 5 (0x20 mask) is a flag to indicate that VINT has been triggered: this was meant to be used to emulate an odd case where HINT is acknowledged after VINT being triggered by the VDP (in that case, HINT should not be acknowledged then executed again and VINT should be missed)

- bit 8-15 give the amount of instructions to execute before triggering the interupt (so called "latency")

Since I don't want to execute "latency" cycles within the VDP register write routine (it would mess everything) and should not trigger interrupt lines immediately, I rather postpound IRQ updates: after each instruction executed, I simply check irq_status to know if IRQ need to be updated and if I need to execute some additional instructions

I know this is really a very complicated way but I didn't really took the time to design a better/faster implementation and this works perfectly like this so ;-)
mickagame
Very interested
Posts: 256
Joined: Sat Jun 07, 2008 7:37 am

Post by mickagame »

- bit 5 (0x20 mask) is a flag to indicate that VINT has been triggered: this was meant to be used to emulate an odd case where HINT is acknowledged after VINT being triggered by the VDP (in that case, HINT should not be acknowledged then executed again and VINT should be missed)
I don't understand exactly when this case can be happen?
Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke »

Image
mickagame
Very interested
Posts: 256
Joined: Sat Jun 07, 2008 7:37 am

Post by mickagame »

Very interesting doc :-)
Last edited by mickagame on Thu Apr 02, 2009 9:05 pm, edited 1 time in total.
mickagame
Very interested
Posts: 256
Joined: Sat Jun 07, 2008 7:37 am

Post by mickagame »

Finally ....

Image

Image

On the road again !!!
Post Reply