APU clarifications

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
APU clarifications
by on (#31434)
Is there any way to reset a channel's "timer" (the counter clocked at 1.79 MHz) or does it always keep counting until it's zero and reloads?

The wiki says: "The timer's period is the 12-bit value (%HHHL.LLLLLLL0) formed by timer high and timer low, plus two."

It is plus two or times two? The diagram looks shifted left once.

-----------------------------------------------------------------

This page http://nesdevwiki.org/index.php/APU_Length_Counter regarding the length counter, shows 5 load bits and gives a 4 bit lookup table. Which bits are used to load the counter and what, if anything, does the other bit do?

------------------------------------------------------------------

If the triangle's timer period is formed by timer_high and timer_low, plus one, is the timer really 12-bit or 11-bit and will it overflow? Or is it that it outputs a clock on underflow instead of zero?
Re: APU clarifications
by on (#31436)
kyuusaku wrote:
The wiki says: "The timer's period is the 12-bit value (%HHHL.LLLLLLL0) formed by timer high and timer low, plus two."

It is plus two or times two? The diagram looks shifted left once.

It's both: times two and then plus two, or plus one and then times two.

Quote:
This page http://nesdevwiki.org/index.php/APU_Length_Counter regarding the length counter, shows 5 load bits and gives a 4 bit lookup table. Which bits are used to load the counter and what, if anything, does the other bit do?

It's a 32-entry lookup table. The first four entries are 10, 254, 20, 2, and the last four entries are 16, 28, 32, 30.
Re: APU clarifications
by on (#31437)
tepples wrote:
It's both: times two and then plus two, or plus one and then times two.

Thanks, any idea about it overflowing?

Quote:
It's a 32-entry lookup table. The first four entries are 10, 254, 20, 2, and the last four entries are 16, 28, 32, 30.

Ah, I missed the bottom row.
Re: APU clarifications
by on (#31440)
kyuusaku wrote:
tepples wrote:
It's both: times two and then plus two, or plus one and then times two.

Thanks, any idea about it overflowing?

It doesn't "overflow." If the timer is programmed with the value 4, the count goes something like 4-3-2-1-0-4-3-2-1-0-4-3-2-1-0 and so on, thus taking 5 steps per cycle. On the square and noise channels, the period is doubled, though I'm not sure how. Perhaps the timer runs at 1/2 the CPU speed? Perhaps it runs at normal speed but only every other tick is acknowledged? Maybe the timer is 12 bits, with the LSB set to 1 at reload time? Whatever the reason, you have to double the priod to get the correct result. The doubling does NOT, however, occur on the triangle channel.

by on (#31442)
I meant if you set the reload value to %111111111110, would the "add 2" correction make it overflow or are there really 13 bits?

Also if it did overflow, would it output a clock or not?

by on (#31443)
You can emulate it using 13 bits, or 16 (a "half word" on 32-bit machines) for that matter. But what I think actually happens in the hardware is more like this:
Code:
if (apuChannels[ch].timer == 0) {
    apuChannels[ch].timer = (apuChannels[ch].period << 1) | 0x001;
    apuChannels[ch].sequencer = (apuChannels[ch].sequencer + 1) & 0x07;
} else {
    apuChannels[ch].timer -= 1;
}

by on (#31444)
Now you've lost me on the "| 0x001", what happened to "(period << 1) + 2" or "(period + 1) << 1"?

by on (#31447)
In software, ((period << 1) | 0x001) is identical in effect to ((period << 1) + 0x001). It just takes fewer gates in hardware to OR rather than add.

The key hypothesis that I'm making is that it doesn't count down and reset the counter on the same cycle. So (x * 2 + 1) cycles of counting down followed by 1 cycle of resetting (and not counting down) equals the observed (x * 2 + 2) cycles.

by on (#31448)
But remember the adding of one has to be done before the shift or else you have to add two, and you can't do either with an OR gate. If you could you could also simplify it out by tying the input high and using no hardware at all :)

I get how the counter synchronously loads, I just wasn't sure when the timer clocked the next stage-whether it was asynchronous after the counter became 0 or if it was synchronized to the timer's clock input at the transition to 0/-1/-2 (this is the underflow I was talking about). I originally thought that was why the +2 compensation was needed, but I guess now it's asynchronous after the arrival of 0/it doesn't matter.

by on (#31450)
kyuusaku wrote:
But remember the adding of one has to be done before the shift or else you have to add two

Let me explain it again, this time using a concrete example:

Assume that the NES program writes a period of 10, this should result in an effective period of 22. But under my conjecture, what gets copied into the countdown register is (period << 1) | 1 = 21.

t=0 -- counter=21 -- action=decrement
t=1 -- counter=20 -- action=decrement
t=2 -- counter=19 -- action=decrement
t=3 -- counter=18 -- action=decrement
t=4 -- counter=17 -- action=decrement
t=5 -- counter=16 -- action=decrement
t=6 -- counter=15 -- action=decrement
t=7 -- counter=14 -- action=decrement
t=8 -- counter=13 -- action=decrement
t=9 -- counter=12 -- action=decrement
t=10 -- counter=11 -- action=decrement
t=11 -- counter=10 -- action=decrement
t=12 -- counter=9 -- action=decrement
t=13 -- counter=8 -- action=decrement
t=14 -- counter=7 -- action=decrement
t=15 -- counter=6 -- action=decrement
t=16 -- counter=5 -- action=decrement
t=17 -- counter=4 -- action=decrement
t=18 -- counter=3 -- action=decrement
t=19 -- counter=2 -- action=decrement
t=20 -- counter=1 -- action=decrement
t=21 -- counter=0 -- action=load 21 into counter and clock sequencer
t=22 -- counter=21 -- action=decrement
t=23 -- counter=20 -- action=decrement
t=24 -- counter=19 -- action=decrement
t=25 -- counter=18 -- action=decrement
t=26 -- counter=17 -- action=decrement
t=27 -- counter=16 -- action=decrement
t=28 -- counter=15 -- action=decrement
t=29 -- counter=14 -- action=decrement
t=30 -- counter=13 -- action=decrement
t=31 -- counter=12 -- action=decrement
t=32 -- counter=11 -- action=decrement
t=33 -- counter=10 -- action=decrement
t=34 -- counter=9 -- action=decrement
t=35 -- counter=8 -- action=decrement
t=36 -- counter=7 -- action=decrement
t=37 -- counter=6 -- action=decrement
t=38 -- counter=5 -- action=decrement
t=39 -- counter=4 -- action=decrement
t=40 -- counter=3 -- action=decrement
t=41 -- counter=2 -- action=decrement
t=42 -- counter=1 -- action=decrement
t=43 -- counter=0 -- action=load 21 into counter and clock sequencer
t=44 -- counter=21 -- action=decrement
t=45 -- counter=20 -- action=decrement
t=46 -- counter=19 -- action=decrement
etc.

Quote:
, and you can't do either with an OR gate. If you could you could also simplify it out by tying the input high and using no hardware at all :)

That was my idea, but I was thinking more in terms of emulation on commodity PC hardware, where a program can't specify functions a bit at a time without using word-wide AND, OR, XOR, shifts, and the like.

by on (#31452)
Alright I get it, I never differentiated between the period and the load value, which aren't the same in this case. The triangle's period and load value are though right?