Wierd Interrupt Issue

Ask anything your want about Megadrive/Genesis programming.

Moderator: BigEvilCorporation

Post Reply
Graz
Very interested
Posts: 81
Joined: Thu Aug 23, 2007 12:36 am
Location: Orlando, FL

Wierd Interrupt Issue

Post by Graz » Fri Sep 03, 2010 5:35 pm

I have an app that has a 68k component and a z80 component. The two components communicate with a ring buffer at the end of the z80's ram. The z80 spins waiting on the ring buffer to be filled by the 68k. This all seems to work fine, except that once I send a command to the z80 and it starts pulling data from ROM, my VINT routine doesn't seem to execute. So, if I start the z80 and send commands to it, busy waiting on the 68k side with a nop loop, everything works fine. I have a VINT handler that simply increments a volatile frame counter. If I instead busy wait on the frame counter to increase, it does, until the z80 starts working, then it doesn't.
The test application works on every emulator I've tried, but not on real hardware. I'm sure that I've got a bug and that I've not set something up right. Before I go on the giant bug hunt, though, I thought I'd ask if anyone had seen anything like that before.

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Fri Sep 03, 2010 8:43 pm

That's not very clear... which VINT stops, the Z80 or the 68000? You should probably make a simple pseudo-code list for each processor showing what it's doing for normal and interrupt code. You might notice your problem while you are doing that.
:)

Graz
Very interested
Posts: 81
Joined: Thu Aug 23, 2007 12:36 am
Location: Orlando, FL

Post by Graz » Sat Sep 04, 2010 8:30 pm

Sorry, yes, that was a bit vague now that I re-read it. It's the 68k VINT that seems to stop. Here's some pseudo-code:

68K:

Code: Select all

volatile int frame_count;

void vint(void)
{
    frame_count++;
}

void fill_z80_ring()
{
    while (space_in_ring())
    {
        append_command_to_ring();
    }
}

void working()
{
    while (1)
    {
        fill_z80_ring();
        for (n = 0; n < lots; n++)
            nop();
    }
}

void not_working()
{
    int old_frame_count = frame_count;

    while (1)
    {
        while (old_frame_count == frame_count)
        {
            /* do nothing */
        }
        fill_z80_ring();
    }
}

void verify_vint()
{
    do_something_with_frame_count(frame_count);
}
Z80:

Code: Select all

void spin_much()
{
    while (1)
    {
        while (commands_in_ring())
        {
             get_command_from_ring();
             execute_command();
        }
    }
}
The Z80 code is located in its own RAM. It doesn't touch ROM until a command comes along. The Z80 is running with interrupts disabled and doesn't use interrupts at all.

If I don't send commands to the Z80 (it's still running, just not picking anything out of the ring), then verify_vint() works. In my case, I have some LEDs hooked up to controller port 2 and I blink them on and off based on the value of frame_count. Note that this code doesn't actually run in the VINT, it just references a variable updated by the VINT routine. As soon as I put anything in the Z80 ring buffer, frame_count stops incrementing.

Filling the ring involves taking the bus from the Z80 and inspecting its RAM, although getting it booted requires that, and it works fine except for the apparent effect on VINT. I haven't investigated any other 68K interrupts, nor the effect on the Z80's VINT.

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Sun Sep 05, 2010 5:18 pm

Hmm - since it works until you send a command, I would guess it's something to do with the command. You say the Z80 uses its own ram until it gets a command, which implies that it accesses the 68000 bus to do a command. What is the bank set to? I'd suspect maybe a bad bank value, or possibly setting the bank wrong so the Z80 accesses something it shouldn't, causing the VDP to freeze or something.

There are a number of cautions in the tech bulletins for the Genesis related to accessing stuff via the Z80's 68000 bank.

Graz
Very interested
Posts: 81
Joined: Thu Aug 23, 2007 12:36 am
Location: Orlando, FL

Post by Graz » Mon Sep 06, 2010 1:24 am

Well, I think I've solved the problem - or at least worked around it. I'm not sure what the problem was, exactly. I was running the 68K with interrupts off most of the time and using halt 0x2000 to wait for an interrupt to occur, and that was working just fine until the Z80 started pulling data out of ROM. As far as I could tell, the 68K wasn't coming out of halt state. If I just turn on interrupts on the 68K and busy wait on my frame counter (no halt instruction), it seems to work. It doesn't make much difference to this application. I was just running with interrupts off to avoid oddness in software timed loops. However, I can just turn interrupts on right before I busy wait and then turn them off again once I'm done. That should work just fine.

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Tue Sep 07, 2010 1:12 am

I suppose you mean STOP... halt is a line on the bus. Remember that STOP #0x2000 enables the interrupts, then waits until one occurs. Whatever you pass as the immediate is stored to the SR. If your code counted on the interrupts being off after the STOP, that's not happening unless you turn them off again via something like move #2700,sr.

Graz
Very interested
Posts: 81
Joined: Thu Aug 23, 2007 12:36 am
Location: Orlando, FL

Post by Graz » Wed Sep 08, 2010 11:58 pm

Yes, I meant stop, not halt.

Code: Select all

asm volatile ("move.w   #0x2000, %sr");
do {
    // Apparently, stop #0x2000 doesn't seem to work when the Z80 is banging the bus!
    // asm volatile ("stop #0x2000");
} while (frame == frame_counter);
'frame' is a non-volatile local variable, and frame_counter is a volatile global variable that is incremented in the VINT handler (which is otherwise empty).
Replacing the stop instruction with the move.w above the loop is what fixed the problem. Nothing I have really relies on interrupts being disabled, I just disable them for some parts of the code and wanted to ensure that they were on during this loop. The original loop was only there to ensure that the source of the waking interrupt is the VINT. It's not like I need to save power here, so the busy wait is perfectly fine.
Could Z80 accesses be causing interrupts on another vector?

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Thu Sep 09, 2010 12:25 am

Hmm - seems like a bug in the compiler then. I've run into similar bugs with the 68000 compiler before. The last one I ran into for some reason loaded the volatile value AFTER doing the compare instead of before, so the wrong value was compared and always failed. You should add -c -s (or is it -S?) to the makefile and look at the code generated... or use objdump on the object file and look at the loop. I'll bet your volatile variable isn't being used correctly in the STOP case.

Charles MacDonald
Very interested
Posts: 292
Joined: Sat Apr 21, 2007 1:14 am

Post by Charles MacDonald » Thu Sep 09, 2010 4:44 am

I had a similar problem on the Saturn with gcc, with a loop that was empty except for some inline assembly code. The compiler was optimizing the assembly code out of the loop or something like that.

If you add more 'stuff' to the loop so the compiler thinks it actually does something, it should work. I think it doesn't see the inline assembly and assumes the loop is empty, even though it isn't.

Graz
Very interested
Posts: 81
Joined: Thu Aug 23, 2007 12:36 am
Location: Orlando, FL

Post by Graz » Sat Sep 11, 2010 7:39 pm

I've never had a problem with GCC's code generator. It's C code generation is pretty decent, and the inline assembler surpasses any other compiler I've used in the past. If you've had problems with GCC nuking your assembly code or hoisting it out of loops, it could be because you haven't properly described the side effects of the instruction (lots of people forget memory references), or that you haven't marked it as volatile. I always use volatile.
FWIW, objdump -S on my binary gives this:

Code: Select all

          asm volatile ("move.w   #0x2000, %sr");
2c03de:       46fc 2000       movew #8192,%sr
          do {
              // Apparently, stop #0x2000 doesn't seem to work when the Z80 is banging the bus!
              // asm volatile ("stop #0x2000");
          } while (frame == frame_counter);
2c03e2:       2039 00ff 0fe4  movel ff0fe4 <frame_counter>,%d0
2c03e8:       b082            cmpl %d2,%d0
2c03ea:       67f6            beqs 2c03e2 <gdk_run_scene+0x4e>
And with the stop inside the loop, this:

Code: Select all

          // asm volatile ("move.w   #0x2000, %sr");
          do {
              // Apparently, stop #0x2000 doesn't seem to work when the Z80 is banging the bus!
              asm volatile ("stop #0x2000");
2c0410:       4e72 2000       stop #8192
          } while (frame == frame_counter);
2c0414:       2039 00ff 0fe4  movel ff0fe4 <frame_counter>,%d0
2c041a:       b082            cmpl %d2,%d0
2c041c:       66c0            bnes 2c03de <gdk_run_scene+0x4a>
As you can see, the stop instruction is generated correctly.
I'm using GCC 4.5.0 for 68K, 4.2.0 for SH2 (just because I haven't updated yet), GNU binutils 2.20.1 for all (including Z80).

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Sat Sep 11, 2010 8:47 pm

Uh - look at that second one again... there isn't enough code shown to determine if it's correct or not. The branch is the opposite and to much further back. Note how much different it is compared to the other code, even though the only difference is a move to sr versus a stop.

Graz
Very interested
Posts: 81
Joined: Thu Aug 23, 2007 12:36 am
Location: Orlando, FL

Post by Graz » Sat Sep 11, 2010 9:08 pm

Oh yeah. Hadn't noticed that. Here's a larger chunk of disassembled code:

Code: Select all

           // asm volatile ("move.w   #0x2000, %sr");
           do {
               // Apparently, stop #0x2000 doesn't seem to work when the Z80 is banging the bus!
               asm volatile ("stop #0x2000");
 2c0410:       4e72 2000       stop #8192
           } while (frame == frame_counter);
 2c0414:       2039 00ff 0fe4  movel ff0fe4 <frame_counter>,%d0
 2c041a:       b082            cmpl %d2,%d0
 2c041c:       66c0            bnes 2c03de <gdk_run_scene+0x4a>
   for (;;) {
       if (!(scene->flags & GDK_SCENE_FLAG_NO_VSYNC)) {
           // asm volatile ("move.w   #0x2000, %sr");
           do {
               // Apparently, stop #0x2000 doesn't seem to work when the Z80 is banging the bus!
               asm volatile ("stop #0x2000");
 2c041e:       4e72 2000       stop #8192
           } while (frame == frame_counter);
 2c0422:       2039 00ff 0fe4  movel ff0fe4 <frame_counter>,%d0
 2c0428:       b082            cmpl %d2,%d0
 2c042a:       67e4            beqs 2c0410 <gdk_run_scene+0x7c>
 2c042c:       60b0            bras 2c03de <gdk_run_scene+0x4a>
It seems that the compiler put two copies of the busy wait in there. The last beqs is the end of the second loop and loops to the top of the first. The first bnes breaks out of the loop. Not sure why the compiler did that - it's probably trying to unroll the loop to make my busy wait faster.
In any case, my point was that it didn't nuke anything from inside the loop. This code might be convoluted, but it is correct.

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Sat Sep 11, 2010 11:11 pm

Hmm - don't really have enough info to say what's going on. If you could chop out a section and make a stand-alone demo that had the same issue, we might be able to figure it out.

Post Reply