Annotation of generator/sn76496/sn76496.c, revision 1.1

1.1     ! root        1: /* This code comes from MAME 0.53 and according to changes log was written
        !             2:    by Nicola Salmoria.  The MAME license says:
        !             3: 
        !             4:    VI. Reuse of Source Code
        !             5:    --------------------------
        !             6:    This chapter might not apply to specific portions of MAME (e.g. CPU
        !             7:    emulators) which bear different copyright notices.
        !             8:    The source code cannot be used in a commercial product without the written
        !             9:    authorization of the authors. Use in non-commercial products is allowed, and
        !            10:    indeed encouraged.  If you use portions of the MAME source code in your
        !            11:    program, however, you must make the full source code freely available as
        !            12:    well.
        !            13:    Usage of the _information_ contained in the source code is free for any use.
        !            14:    However, given the amount of time and energy it took to collect this
        !            15:    information, if you find new information we would appreciate if you made it
        !            16:    freely available as well.
        !            17: 
        !            18: */
        !            19: 
        !            20: /***************************************************************************
        !            21: 
        !            22:   sn76496.c
        !            23: 
        !            24:   Routines to emulate the Texas Instruments SN76489 / SN76496 programmable
        !            25:   tone /noise generator. Also known as (or at least compatible with) TMS9919.
        !            26: 
        !            27:   Noise emulation is not accurate due to lack of documentation. The noise
        !            28:   generator uses a shift register with a XOR-feedback network, but the exact
        !            29:   layout is unknown. It can be set for either period or white noise; again,
        !            30:   the details are unknown.
        !            31: 
        !            32: ***************************************************************************/
        !            33: 
        !            34: /* get uint16 definition */
        !            35: #include "generator.h"
        !            36: 
        !            37: #include "sn76496.h"
        !            38: 
        !            39: #define MAX_OUTPUT 0x7fff
        !            40: #define STEP 0x10000
        !            41: 
        !            42: /* Formulas for noise generator */
        !            43: /* bit0 = output */
        !            44: 
        !            45: /* noise feedback for white noise mode */
        !            46: #define FB_WNOISE 0x12000       /* bit15.d(16bits) = bit0(out) ^ bit2 */
        !            47: //#define FB_WNOISE 0x14000     /* bit15.d(16bits) = bit0(out) ^ bit1 */
        !            48: //#define FB_WNOISE 0x28000     /* bit16.d(17bits) = bit0(out) ^ bit2 (same to AY-3-8910) */
        !            49: //#define FB_WNOISE 0x50000     /* bit17.d(18bits) = bit0(out) ^ bit2 */
        !            50: 
        !            51: /* noise feedback for periodic noise mode */
        !            52: /* it is correct maybe (it was in the Megadrive sound manual) */
        !            53: //#define FB_PNOISE 0x10000     /* 16bit rorate */
        !            54: #define FB_PNOISE 0x08000       /* JH 981127 - fixes Do Run Run */
        !            55: 
        !            56: /* noise generator start preset (for periodic noise) */
        !            57: #define NG_PRESET 0x0f35
        !            58: 
        !            59: struct SN76496 sn[MAX_76496];
        !            60: 
        !            61: void SN76496Write(int chip, int data)
        !            62: {
        !            63:   struct SN76496 *R = &sn[chip];
        !            64: 
        !            65:   /* should update buffer before getting here */
        !            66: 
        !            67:   if (data & 0x80) {
        !            68:     int r = (data & 0x70) >> 4;
        !            69:     int c = r / 2;
        !            70: 
        !            71:     R->LastRegister = r;
        !            72:     R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f);
        !            73:     switch (r) {
        !            74:     case 0:                    /* tone 0 : frequency */
        !            75:     case 2:                    /* tone 1 : frequency */
        !            76:     case 4:                    /* tone 2 : frequency */
        !            77:       R->Period[c] = R->UpdateStep * R->Register[r];
        !            78:       if (R->Period[c] == 0)
        !            79:         R->Period[c] = R->UpdateStep;
        !            80:       if (r == 4) {
        !            81:         /* update noise shift frequency */
        !            82:         if ((R->Register[6] & 0x03) == 0x03)
        !            83:           R->Period[3] = 2 * R->Period[2];
        !            84:       }
        !            85:       break;
        !            86:     case 1:                    /* tone 0 : volume */
        !            87:     case 3:                    /* tone 1 : volume */
        !            88:     case 5:                    /* tone 2 : volume */
        !            89:     case 7:                    /* noise  : volume */
        !            90:       R->Volume[c] = R->VolTable[data & 0x0f];
        !            91:       break;
        !            92:     case 6:                    /* noise  : frequency, mode */
        !            93:       {
        !            94:         int n = R->Register[6];
        !            95:         R->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE;
        !            96:         n &= 3;
        !            97:         /* N/512,N/1024,N/2048,Tone #3 output */
        !            98:         R->Period[3] =
        !            99:           (n == 3) ? 2 * R->Period[2] : (R->UpdateStep << (5 + n));
        !           100: 
        !           101:         /* reset noise shifter */
        !           102:         R->RNG = NG_PRESET;
        !           103:         R->Output[3] = R->RNG & 1;
        !           104:       }
        !           105:       break;
        !           106:     }
        !           107:   } else {
        !           108:     int r = R->LastRegister;
        !           109:     int c = r / 2;
        !           110: 
        !           111:     switch (r) {
        !           112:     case 0:                    /* tone 0 : frequency */
        !           113:     case 2:                    /* tone 1 : frequency */
        !           114:     case 4:                    /* tone 2 : frequency */
        !           115:       R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4);
        !           116:       R->Period[c] = R->UpdateStep * R->Register[r];
        !           117:       if (R->Period[c] == 0)
        !           118:         R->Period[c] = R->UpdateStep;
        !           119:       if (r == 4) {
        !           120:         /* update noise shift frequency */
        !           121:         if ((R->Register[6] & 0x03) == 0x03)
        !           122:           R->Period[3] = 2 * R->Period[2];
        !           123:       }
        !           124:       break;
        !           125:     }
        !           126:   }
        !           127: }
        !           128: 
        !           129: void SN76496Update(int chip, uint16 *buffer, int length)
        !           130: {
        !           131:   int i;
        !           132:   struct SN76496 *R = &sn[chip];
        !           133: 
        !           134:   /* If the volume is 0, increase the counter */
        !           135:   for (i = 0; i < 4; i++) {
        !           136:     if (R->Volume[i] == 0) {
        !           137:       /* note that I do count += length, NOT count = length + 1. You might
        !           138:          think it's the same since the volume is 0, but doing the latter
        !           139:          could cause interferencies when the program is rapidly modulating
        !           140:          the volume. */
        !           141:       if (R->Count[i] <= length * STEP)
        !           142:         R->Count[i] += length * STEP;
        !           143:     }
        !           144:   }
        !           145: 
        !           146:   while (length > 0) {
        !           147:     int vol[4];
        !           148:     unsigned int out;
        !           149:     int left;
        !           150: 
        !           151:     /* vol[] keeps track of how long each square wave stays */
        !           152:     /* in the 1 position during the sample period. */
        !           153:     vol[0] = vol[1] = vol[2] = vol[3] = 0;
        !           154: 
        !           155:     for (i = 0; i < 3; i++) {
        !           156:       if (R->Output[i])
        !           157:         vol[i] += R->Count[i];
        !           158:       R->Count[i] -= STEP;
        !           159:       /* Period[i] is the half period of the square wave. Here, in each */
        !           160:       /* loop I add Period[i] twice, so that at the end of the loop the */
        !           161:       /* square wave is in the same status (0 or 1) it was at the start. */
        !           162:       /* vol[i] is also incremented by Period[i], since the wave has been 1 */
        !           163:       /* exactly half of the time, regardless of the initial position. */
        !           164:       /* If we exit the loop in the middle, Output[i] has to be inverted */
        !           165:       /* and vol[i] incremented only if the exit status of the square */
        !           166:       /* wave is 1. */
        !           167:       while (R->Count[i] <= 0) {
        !           168:         R->Count[i] += R->Period[i];
        !           169:         if (R->Count[i] > 0) {
        !           170:           R->Output[i] ^= 1;
        !           171:           if (R->Output[i])
        !           172:             vol[i] += R->Period[i];
        !           173:           break;
        !           174:         }
        !           175:         R->Count[i] += R->Period[i];
        !           176:         vol[i] += R->Period[i];
        !           177:       }
        !           178:       if (R->Output[i])
        !           179:         vol[i] -= R->Count[i];
        !           180:     }
        !           181: 
        !           182:     left = STEP;
        !           183:     do {
        !           184:       int nextevent;
        !           185: 
        !           186:       if (R->Count[3] < left)
        !           187:         nextevent = R->Count[3];
        !           188:       else
        !           189:         nextevent = left;
        !           190:       if (R->Output[3])
        !           191:         vol[3] += R->Count[3];
        !           192:       R->Count[3] -= nextevent;
        !           193:       if (R->Count[3] <= 0) {
        !           194:         if (R->RNG & 1)
        !           195:           R->RNG ^= R->NoiseFB;
        !           196:         R->RNG >>= 1;
        !           197:         R->Output[3] = R->RNG & 1;
        !           198:         R->Count[3] += R->Period[3];
        !           199:         if (R->Output[3])
        !           200:           vol[3] += R->Period[3];
        !           201:       }
        !           202:       if (R->Output[3])
        !           203:         vol[3] -= R->Count[3];
        !           204:       left -= nextevent;
        !           205:     } while (left > 0);
        !           206: 
        !           207:     out = vol[0] * R->Volume[0] + vol[1] * R->Volume[1] +
        !           208:       vol[2] * R->Volume[2] + vol[3] * R->Volume[3];
        !           209:     if (out > MAX_OUTPUT * STEP)
        !           210:       out = MAX_OUTPUT * STEP;
        !           211:     *(buffer++) = out / STEP;
        !           212:     length--;
        !           213:   }
        !           214: }
        !           215: 
        !           216: static void SN76496_set_clock(int chip, int clock)
        !           217: {
        !           218:   struct SN76496 *R = &sn[chip];
        !           219: 
        !           220:   /* the base clock for the tone generators is the chip clock divided by 16; */
        !           221:   /* for the noise generator, it is clock / 256. */
        !           222:   /* Here we calculate the number of steps which happen during one sample */
        !           223:   /* at the given sample rate. No. of events = sample rate / (clock/16). */
        !           224:   /* STEP is a multiplier used to turn the fraction into a fixed point */
        !           225:   /* number. */
        !           226:   R->UpdateStep = ((double)STEP * R->SampleRate * 16) / clock;
        !           227: }
        !           228: 
        !           229: static void SN76496_set_gain(int chip, int gain)
        !           230: {
        !           231:   struct SN76496 *R = &sn[chip];
        !           232:   int i;
        !           233:   double out;
        !           234: 
        !           235:   gain &= 0xff;
        !           236:   /* increase max output basing on gain (0.2 dB per step) */
        !           237:   out = MAX_OUTPUT / 3;
        !           238:   while (gain-- > 0)
        !           239:     out *= 1.023292992;         /* = (10 ^ (0.2/20)) */
        !           240:   /* build volume table (2dB per step) */
        !           241:   for (i = 0; i < 15; i++) {
        !           242:     /* limit volume to avoid clipping */
        !           243:     if (out > MAX_OUTPUT / 3)
        !           244:       R->VolTable[i] = MAX_OUTPUT / 3;
        !           245:     else
        !           246:       R->VolTable[i] = out;
        !           247:     out /= 1.258925412;         /* = 10 ^ (2/20) = 2dB */
        !           248:   }
        !           249:   R->VolTable[15] = 0;
        !           250: }
        !           251: 
        !           252: int SN76496Init(int chip, int clock, int gain, int sample_rate)
        !           253: {
        !           254:   int i;
        !           255:   struct SN76496 *R = &sn[chip];
        !           256:   char name[40];
        !           257: 
        !           258:   R->SampleRate = sample_rate;
        !           259:   SN76496_set_clock(chip, clock);
        !           260: 
        !           261:   for (i = 0; i < 4; i++)
        !           262:     R->Volume[i] = 0;
        !           263: 
        !           264:   R->LastRegister = 0;
        !           265:   for (i = 0; i < 8; i += 2) {
        !           266:     R->Register[i] = 0;
        !           267:     R->Register[i + 1] = 0x0f;  /* volume = 0 */
        !           268:   }
        !           269: 
        !           270:   for (i = 0; i < 4; i++) {
        !           271:     R->Output[i] = 0;
        !           272:     R->Period[i] = R->Count[i] = R->UpdateStep;
        !           273:   }
        !           274:   R->RNG = NG_PRESET;
        !           275:   R->Output[3] = R->RNG & 1;
        !           276:   SN76496_set_gain(chip, gain & 0xff);
        !           277: 
        !           278:   return 0;
        !           279: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.