Using DMC IRQ as watchdog timer?

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Using DMC IRQ as watchdog timer?
by on (#179836)
I'm trying to use the DMC IRQ in an unusual way, and I'm not sure why it's not working. So I don't know if it can't be used like this, or if I'm simply doing something wrong.

What I'm trying to do is enable DMC IRQ, play a sample, and continually retrigger that sample to prevent it from generating an IRQ. I'm trying this because I'm making a bit-banged UART that can poll in the tightest loop possible without getting hung up if the other end isn't responding.

The result I'm getting is that I seem to get an IRQ when I retrigger the sample. I've tried things a few different ways, but I always seem to get interrupted.

Here's how I set it up, this part by itself seems OK. I can run this, do an SEI when communication begins, and it's fine.
Code:
      lda #$40
      sta $4017
      lda #$80
      sta $4010
      lda #$3f
      sta $4013

      lda #$10
      sta $4015
      sta $4015

      cli


But instead if I try to retrigger the sample, I seem to get an IRQ. I've tried different variations of the code with the same result, this is probably the most elaborate one of them:
Code:
      sei
      lda #0
      sta $4015
      lda #$10
      sta $4015
      lda $4015
      nop
      cli

Another interesting thing is that the retrigger code ideally would need to fit within 14 CPU cycles.

The IRQ routine itself reads $4015 to acknowledge the IRQ. The interrupt timer needed is something like 2 seconds long. Shorter time out would be OK, but NMI is out of the question because I'd have to sync to it. And frame IRQ, I don't know how it works exactly but I believe that's something that I can't reset, correct? If I have to sync up with it, it's not usable.

It's not a huge deal. It works if I don't try to retrigger it, and that's good enough for like 95% of the time I would need it. I can make a work-around that will mostly take care of the other situations, but it would be great if I could never leave the NES vulnerable to being locked up like that. I guess if I really had to know I'd be turning to Visual 2A03, but I haven't really used that enough to know exactly what I'd be looking for. This is already kind of a side-quest of a side-quest. But I was wondering if anyone else wanted to look at this, correct my misunderstanding of DMC IRQ, or shed any light on the subject otherwise.
Re: Using DMC IRQ as watchdog timer?
by on (#179837)
Memblers wrote:
And frame IRQ, I don't know how it works exactly but I believe that's something that I can't reset, correct? If I have to sync up with it, it's not usable.

Writing to $4017 restarts the frame counter's sequence. It's common for games to ping it once per frame to keep it synchronized with the music routine. It can optionally have a period of slightly longer than a frame (~20ms) if you need that. (Edit: Jahrmander points out the long period mode disables the IRQ, so not relevant here.)
Re: Using DMC IRQ as watchdog timer?
by on (#179839)
rainwarrior wrote:
Memblers wrote:
And frame IRQ, I don't know how it works exactly but I believe that's something that I can't reset, correct? If I have to sync up with it, it's not usable.

Writing to $4017 restarts the frame counter's sequence. It's common for games to ping it once per frame to keep it synchronized with the music routine. It can optionally have a period of slightly longer than a frame (~20ms) if you need that.

Unfortunately, in the ~50Hz mode, there's no IRQ fired at all.
Re: Using DMC IRQ as watchdog timer?
by on (#179840)
W.R.T. getting a spurious DPCM IRQ on retrigger, could you post a ROM that demonstrates this? (I'm curious about it.)
Re: Using DMC IRQ as watchdog timer?
by on (#179861)
Ah, it's another facepalm moment. I changed it to check the A button on the $4016 controller port instead of a serial device on $4017, and it worked perfectly. Looking again at my serial code, I'm reading the bits with LSR $4017 (the poll waiting part is BIT $4017 so it was safe). Yeah, a RMW instruction will certainly have an effect on those writable bits.. Jeebus. :oops: I wouldn't have expected it to trigger IRQ though?

Doing LDA $4017 / LSR A takes the same number of cycles and doesn't have this side effect. So it does work, when I'm not sabotaging it.

Probably not as interesting now, but I've attached the roms anyways.
Re: Using DMC IRQ as watchdog timer?
by on (#179863)
Memblers wrote:
Ah, it's another facepalm moment. I changed it to check the A button on the $4016 controller port instead of a serial device on $4017, and it worked perfectly. Looking again at my serial code, I'm reading the bits with LSR $4017 (the poll waiting part is BIT $4017 so it was safe). Yeah, a RMW instruction will certainly have an effect on those writable bits.. Jeebus. :oops: I wouldn't have expected it to trigger IRQ though?

If I'm reading the Wiki correctly, lsr $4017 sure as heck would induce an IRQ, depending on what ends up in bit 6 (7th bit) during the write phase: https://wiki.nesdev.com/w/index.php/APU ... .244017.29 (and see https://wiki.nesdev.com/w/index.php/APU_Frame_Counter too -- "The frame interrupt flag is connected to the CPU's IRQ line. It is set at a particular point in the 4-step sequence (see below) provided the interrupt inhibit flag in $4017 is clear, and can be cleared either by reading $4015 (which also returns its old status) or by setting the interrupt inhibit flag.")
Re: Using DMC IRQ as watchdog timer?
by on (#179875)
Memblers wrote:
So it does work, when I'm not sabotaging it.

Ahh good. I was wondering if there was some yet-unknown DPCM behaviour that I had to worry about. ;)
Re: Using DMC IRQ as watchdog timer?
by on (#179898)
koitsu wrote:
lsr $4017 sure as heck would induce an IRQ, depending on what ends up in bit 6 (7th bit) during the write phase:

Correct me if I'm wrong, but…
open bus $40/$41, write (1 to bit 6) shift right, $20, write (0 to bit 6)… (x8)
but how does that get affected by the 3/4 cycle $4017 write-effect delay, I wonder?
Memblers wrote:
I wouldn't have expected it to trigger IRQ though?
(NMI to controller read)(prev frame) - (NMI to controller read) (this frame) > 50 cycles , then it'd go off…
…maybe one could use that to detect game-processing overruns, if one's using a mainloop that spinwaits for an NMI-set flag to advance to processing the next frame of state.
So your watchdog timer test was inadvertently setting another watchdog and forgetting…?
Re: Using DMC IRQ as watchdog timer?
by on (#180053)
Thanks for mentioning that $4017 writes reset the frame counter, my initial tests shows that it may work for a shorter time-out. I'm transferring data in blocks, so the ideal situation might be a seconds long time-out waiting for block start with DMC IRQ, and then switch to a shorter timeout using the frame IRQ until the block ends. Once the block begins, it's 100% timed code so I'd rather not deal with DPCM stealing cycles. I think this will be OK as long my data blocks are the same or smaller than the block size used by the USB transmitter. If I sent a full USB block followed by a partial USB block while the NES waits on frame IRQ, it will become a race between the USB transmitter timing out (and sending the partial block) and the frame IRQ timing out..

Amusingly enough, I had to switch back to doing LSR $4017 anyways because I need to preserve A. But as long as I have IRQs disabled by SEI while using LSR $4017, that allows the open bus values it writes back to work for me. And that conveniently resets the frame counter, too.

I really need to move on to doing synchronous communication (like I mentioned, this is kind of side-quest), but I'm clearly having too much fun gilding the lily. :) Trying to set some kind of speed record by doing 115.2kbps receive while doing useful work instead of NOPs during the delays. I never would have predicted that I would get this much use out of the DMC or frame IRQ.