public class EnvelopeGenerator extends Object
In addition, another counter is used to implement the exponential envelope decay, in effect further dividing the clock to the envelope counter. The period of this counter is set to 1, 2, 4, 8, 16, 30 at the envelope counter values 255, 93, 54, 26, 14, 6, respectively.
| Modifier and Type | Class and Description |
|---|---|
static class |
EnvelopeGenerator.State |
| Modifier and Type | Field and Description |
|---|---|
protected int |
attack |
protected int |
decay |
protected int |
envelope_counter |
protected int |
exponential_counter |
protected int |
exponential_counter_period |
protected int |
gate |
protected boolean |
hold_zero |
protected int |
rate_counter |
protected static int[] |
rate_counter_period
Lookup table to convert from attack, decay, or release value to rate
counter period.
|
protected int |
rate_period |
protected int |
release |
protected EnvelopeGenerator.State |
state
ATTACK/DECAY_SUSTAIN/RELEASE
|
protected int |
sustain |
protected static int[] |
sustain_level
The 16 selectable sustain levels.
|
| Constructor and Description |
|---|
EnvelopeGenerator()
Constructor.
|
| Modifier and Type | Method and Description |
|---|---|
void |
clock()
SID clocking - 1 cycle.
|
void |
clock(int delta_t)
SID clocking - delta_t cycles.
|
int |
output()
8-bit envelope output.
|
int |
readENV() |
void |
reset()
SID reset.
|
void |
writeATTACK_DECAY(int attack_decay) |
void |
writeCONTROL_REG(int control)
Register functions.
|
void |
writeSUSTAIN_RELEASE(int sustain_release) |
protected int rate_counter
protected int rate_period
protected int exponential_counter
protected int exponential_counter_period
protected int envelope_counter
protected boolean hold_zero
protected int attack
protected int decay
protected int sustain
protected int release
protected int gate
protected EnvelopeGenerator.State state
protected static int[] rate_counter_period
Rate counter periods are calculated from the Envelope Rates table in the Programmer's Reference Guide. The rate counter period is the number of cycles between each increment of the envelope counter. The rates have been verified by sampling ENV3.
The rate counter is a 16 bit register which is incremented each cycle. When the counter reaches a specific comparison value, the envelope counter is incremented (attack) or decremented (decay/release) and the counter is zeroed.
NB! Sampling ENV3 shows that the calculated values are not exact. It may seem like most calculated values have been rounded (.5 is rounded down) and 1 has beed added to the result. A possible explanation for this is that the SID designers have used the calculated values directly as rate counter comparison values, not considering a one cycle delay to zero the counter. This would yield an actual period of comparison value + 1.
The time of the first envelope count can not be exactly controlled, except possibly by resetting the chip. Because of this we cannot do cycle exact sampling and must devise another method to calculate the rate counter periods.
The exact rate counter periods can be determined e.g. by counting the number of cycles from envelope level 1 to envelope level 129, and dividing the number of cycles by 128. CIA1 timer A and B in linked mode can perform the cycle count. This is the method used to find the rates below.
To avoid the ADSR delay bug, sampling of ENV3 should be done using sustain = release = 0. This ensures that the attack state will not lower the current rate counter period.
The ENV3 sampling code below yields a maximum timing error of 14 cycles.
lda #$01
l1: cmp $d41c
bne l1
...
lda #$ff
l2: cmp $d41c
bne l2
This yields a maximum error for the calculated rate period of 14/128
cycles. The described method is thus sufficient for exact calculation of
the rate periods.protected static int[] sustain_level
For decay and release, the clock to the envelope counter is sequentially divided by 1, 2, 4, 8, 16, 30, 1 to create a piece-wise linear approximation of an exponential. The exponential counter period is loaded at the envelope counter values 255, 93, 54, 26, 14, 6, 0. The period can be different for the same envelope counter value, depending on whether the envelope has been rising (attack -> release) or sinking (decay/release).
Since it is not possible to reset the rate counter (the test bit has no influence on the envelope generator whatsoever) a method must be devised to do cycle exact sampling of ENV3 to do the investigation. This is possible with knowledge of the rate period for A=0, found above.
The CPU can be synchronized with ENV3 by first synchronizing with the rate counter by setting A=0 and wait in a carefully timed loop for the envelope counter _not_ to change for 9 cycles. We can then wait for a specific value of ENV3 with another timed loop to fully synchronize with ENV3.
At the first period when an exponential counter period larger than one is used (decay or relase), one extra cycle is spent before the envelope is decremented. The envelope output is then delayed one cycle until the state is changed to attack. Now one cycle less will be spent before the envelope is incremented, and the situation is normalized.
The delay is probably caused by the comparison with the exponential counter, and does not seem to affect the rate counter. This has been verified by timing 256 consecutive complete envelopes with A = D = R = 1, S = 0, using CIA1 timer A and B in linked mode. If the rate counter is not affected the period of each complete envelope is
(255 + 162*1 + 39*2 + 28*4 + 12*8 + 8*16 + 6*30)*32 = 756*32 = 32352
which corresponds exactly to the timed value divided by the number of complete envelopes.
NB! This one cycle delay is not modeled.
From the sustain levels it follows that both the low and high 4 bits of the envelope counter are compared to the 4-bit sustain value. This has been verified by sampling ENV3.
public void clock()
public void clock(int delta_t)
public int output()
Read the envelope generator output.
public void reset()
public void writeCONTROL_REG(int control)
control - control registerpublic void writeATTACK_DECAY(int attack_decay)
attack_decay - attack/decay valuepublic void writeSUSTAIN_RELEASE(int sustain_release)
sustain_release - sustain/release valuepublic int readENV()
Copyright © 2014. All rights reserved.