Wierd Interrupt Issue
Moderator: BigEvilCorporation
Wierd Interrupt Issue
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.
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.
-
- Very interested
- Posts: 2984
- Joined: Fri Aug 17, 2007 9:33 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:
Z80:
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.
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);
}
Code: Select all
void spin_much()
{
while (1)
{
while (commands_in_ring())
{
get_command_from_ring();
execute_command();
}
}
}
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.
-
- Very interested
- Posts: 2984
- Joined: Fri Aug 17, 2007 9:33 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.
There are a number of cautions in the tech bulletins for the Genesis related to accessing stuff via the Z80's 68000 bank.
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.
-
- Very interested
- Posts: 2984
- Joined: Fri Aug 17, 2007 9:33 pm
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.
Yes, I meant stop, not halt.
'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?
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);
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?
-
- Very interested
- Posts: 2984
- Joined: Fri Aug 17, 2007 9:33 pm
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.
-
- Very interested
- Posts: 292
- Joined: Sat Apr 21, 2007 1:14 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.
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.
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:
And with the stop inside the loop, this:
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).
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>
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>
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).
-
- Very interested
- Posts: 2984
- Joined: Fri Aug 17, 2007 9:33 pm
Oh yeah. Hadn't noticed that. Here's a larger chunk of disassembled code:
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.
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>
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.
-
- Very interested
- Posts: 2984
- Joined: Fri Aug 17, 2007 9:33 pm