Quick 4017 question

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Quick 4017 question
by on (#1441)
Still inside the recent 4017h discussion, is the following code correct?

Code:
case 4017:
   cpu_irqcancel(TIRQ_FRA);
   frameIRQ = value;
   frameCNT = 0; //APU step 0..3 or 4 [reset]
   if(value & 0x80) {
      atl_index = 5; //Max steps=5
      psg_step_run(); //APU clock [sweep unit]
   } else {
      atl_index = 4; //Max steps=4
   }
   frameCYC = 0; //APU divider (1/4th of frame_cycles) [reset]
   break;


It means the APU step as '1' (by counting from zero) after an immediate clock on 4017h:80h = 1.

by on (#1442)
AFAIK, writing to 4017 only clears the frame IRQ flag if bit 6 is on. Otherwise, if an IRQ is pending, it stays pending until disabled (4017 with bit 6 on) or acknowledged (4015 read)

The rest (as I understand it), looks right... although:
Code:
   frameCYC = 0;  //APU divider (1/4th of frame_cycles) [reset]


Is that the number of cycles until the next step through the frame sequencer? That looks like it will clock it immediately -- which isn't what you want (unless you're doing a count-up system instead of a count-down -- in which case it looks right)

Here's my code which has served me well:

Code:
case 0x4017:
  RunAPU(nCPUCycle);
  bAPUFrameMode =           (v & 0x80) ? 1 : 0; // 4/5 step sequence selection
  bFrameIRQEnabled =        !(v & 0x40);

  if(!bFrameIRQEnabled)     bFrameIRQPending = 0;  //if IRQs are disabled, clear IRQ flag

  if(bAPUFrameMode)         nAPUFrameTicks = 0;  //in 5-step mode, clock frame sequencer asap
  else                      nAPUFrameTicks = (bPALMode ? NES_APUFRAME_PAL : NES_APUFRAME_NTSC);  // else, reset to 1/4 frame of time

  nAPUFrameCur = 0;  //restart at step 0
  if(!flgI)                 FindNextIRQ();
  break;

by on (#1443)
I've just found some new information about $4017 handling while writing a ROM to test it, but haven't updated the NesDevWiki yet. Disch's code looks correct based on this new information. Here's a test ROM and source to verify $4017 length counter clocking and frame interrupt operation (see asm source for operation):

it's now http://h1.ripway.com/blargg/temp/test_apu_4017.zip

By the look of it, frame handling might also be incorrect, unless you clock the units differently based on atl_index (or bAPUFrameMode in Disch's code), as follows:

Code:
if ( frameCNT < 4 )
    clock_envelopes_and_linear_counter(); // clock on 0, 1, 2, 3

if ( atl_index == 4 && (frameCNT & 1) ) // clock on 1 and 3
    clock_length_and_sweep();

if ( atl_index == 5 && !(frameCNT & 1) ) // clock on 0 and 2
    clock_length_and_sweep();

frameCNT++;
if ( frameCNT >= atl_index )
    frameCNT = 0;

by on (#1454)
Disch wrote:
AFAIK, writing to 4017 only clears the frame IRQ flag if bit 6 is on. Otherwise, if an IRQ is pending, it stays pending until disabled (4017 with bit 6 on) or acknowledged (4015 read)


Well, frame-IRQs don't work yet... I hope to get it fixed, thanks.

Disch wrote:
The rest (as I understand it), looks right... although:
Code:
   frameCYC = 0;  //APU divider (1/4th of frame_cycles) [reset]


Is that the number of cycles until the next step through the frame sequencer? That looks like it will clock it immediately -- which isn't what you want (unless you're doing a count-up system instead of a count-down -- in which case it looks right)


frameCYC += cpu clock cycles (basically), count-up, yes.

Disch wrote:
Code:
if(bAPUFrameMode)         nAPUFrameTicks = 0;  //in 5-step mode, clock frame sequencer asap
  else                      nAPUFrameTicks = (bPALMode ? NES_APUFRAME_PAL : NES_APUFRAME_NTSC);  // else, reset to 1/4 frame of time

  nAPUFrameCur = 0;  //restart at step 0
  if(!flgI)                 FindNextIRQ();
  break;


If I'm using a count-up system, then the values (for nAPUFrameTicks on yours) must be reversed (?) here (for frameCYC on mine).

by on (#1455)
You make a 'nAPUFrameTicks=0' to force an immediate clock, but 'immediate' AFAIK would be 'immediate' on 4017 write, not later... Am I wrong? :| A way to check the correctness is to play SMB3: the dash & cracking block sounds must be audible and clean; otherwise, with distortion.

edit-- well, it worked in both ways. :)

by on (#1458)
Fx3 wrote:
frameCYC += cpu clock cycles (basically), count-up, yes.


Okay... then what you're doing would be right.

Fx3 wrote:
If I'm using a count-up system, then the values (for nAPUFrameTicks on yours) must be reversed (?) here (for frameCYC on mine).


The way you have it set up looks right for a count up system.

Fx3 wrote:
You make a 'nAPUFrameTicks=0' to force an immediate clock, but 'immediate' AFAIK would be 'immediate' on 4017 write, not later... Am I wrong?


No, you're right. By setting nAPUFrameTicks to 0, I'm signalling that the frame sequencer will be clocked at the very start of the next call to RunAPU (My function which does all the APU stuff and produces samples). It has the exact same effect as clocking the sequencer inside my $4017 code, only this way I don't have to copy/paste anything or do an extra function call.

by on (#1459)
blargg wrote:
By the look of it, frame handling might also be incorrect, unless you clock the units differently based on atl_index (or bAPUFrameMode in Disch's code), as follows:


That's it, except I use a CASE statement (very smart). ;)