Annotation of uae/src/audio.c, revision 1.1.1.18

1.1       root        1:  /*
                      2:   * UAE - The Un*x Amiga Emulator
                      3:   *
1.1.1.16  root        4:   * Paula audio emulation
1.1       root        5:   *
                      6:   * Copyright 1995, 1996, 1997 Bernd Schmidt
                      7:   * Copyright 1996 Marcus Sundberg
                      8:   * Copyright 1996 Manfred Thole
1.1.1.18! root        9:   * Copyright 2005 Heikki Orsila
1.1.1.16  root       10:   * Copyright 2006 Toni Wilen
                     11:   *
                     12:   * new filter algorithm and anti&sinc interpolators by Antti S. Lankila
1.1       root       13:   */
                     14: 
                     15: #include "sysconfig.h"
                     16: #include "sysdeps.h"
                     17: 
1.1.1.17  root       18: #include <math.h>
                     19: 
1.1       root       20: #include "options.h"
                     21: #include "memory.h"
                     22: #include "custom.h"
1.1.1.7   root       23: #include "newcpu.h"
                     24: #include "autoconf.h"
1.1       root       25: #include "gensound.h"
                     26: #include "sounddep/sound.h"
                     27: #include "events.h"
                     28: #include "audio.h"
1.1.1.8   root       29: #include "savestate.h"
1.1.1.16  root       30: #include "sinctable.h"
                     31: #include "gui.h"
                     32: 
                     33: #define MAX_EV ~0ul
1.1       root       34: 
1.1.1.16  root       35: /* periods less than this value are replaced by this value. */
                     36: #define MIN_ALLOWED_PERIOD 16
                     37: /* reserve ~20 extra slots in sinc queue for cpu volume or some such updates
                     38:  * even at maximum period. This avoids sinc queue overflow on games like
                     39:  * battle squadron that write these low period values and do cpu-based
                     40:  * updates on paula registers, probably volume. */
                     41: #define NUMBER_OF_CPU_UPDATES_ALLOWED 20
                     42: 
                     43: #define SINC_QUEUE_LENGTH (SINC_QUEUE_MAX_AGE / MIN_ALLOWED_PERIOD + NUMBER_OF_CPU_UPDATES_ALLOWED)
                     44: 
                     45: typedef struct {
1.1.1.17  root       46:     int age, output;
1.1.1.16  root       47: } sinc_queue_t;
                     48: 
                     49: struct audio_channel_data {
                     50:     unsigned long adk_mask;
                     51:     unsigned long evtime;
                     52:     unsigned long per;
                     53:     uae_u8 dmaen, intreq2, data_written;
                     54:     uaecptr lc, pt;
                     55:     int state, wper;
                     56:     unsigned int wlen;
                     57:     int current_sample, last_sample;
                     58:     int vol;
                     59:     int *voltbl;
                     60:     uae_u16 dat, nextdat, len;
1.1.1.17  root       61:     int sample_accum, sample_accum_time;
1.1.1.16  root       62:     int sinc_output_state;
                     63:     sinc_queue_t sinc_queue[SINC_QUEUE_LENGTH];
                     64:     int sinc_queue_length;
                     65: };
                     66: 
                     67: static struct audio_channel_data audio_channel[4];
1.1.1.2   root       68: int sound_available = 0;
1.1       root       69: int sound_table[64][256];
1.1.1.2   root       70: void (*sample_handler) (void);
1.1.1.16  root       71: static void (*sample_prehandler) (unsigned long best_evtime);
1.1.1.7   root       72: 
1.1.1.16  root       73: static unsigned long scaled_sample_evtime;
1.1.1.2   root       74: static unsigned long last_cycles, next_sample_evtime;
1.1       root       75: 
1.1.1.16  root       76: unsigned int obtainedfreq;
                     77: 
1.1.1.2   root       78: void init_sound_table16 (void)
1.1       root       79: {
                     80:     int i,j;
                     81: 
                     82:     for (i = 0; i < 256; i++)
                     83:        for (j = 0; j < 64; j++)
1.1.1.18! root       84:            sound_table[j][i] = j * (uae_s8)i * 2;
1.1       root       85: }
                     86: 
                     87: #ifdef MULTIPLICATION_PROFITABLE
                     88: typedef uae_s8 sample8_t;
                     89: #define DO_CHANNEL_1(v, c) do { (v) *= audio_channel[c].vol; } while (0)
                     90: #define SBASEVAL16(logn) ((logn) == 1 ? SOUND16_BASE_VAL >> 1 : SOUND16_BASE_VAL)
1.1.1.17  root       91: #define FINISH_DATA(data, b, logn) do { if (14 - (b) + (logn) > 0) (data) >>= 14 - (b) + (logn); else (data) <<= (b) - 14 - (logn); } while (0);
1.1       root       92: #else
                     93: typedef uae_u8 sample8_t;
                     94: #define DO_CHANNEL_1(v, c) do { (v) = audio_channel[c].voltbl[(v)]; } while (0)
                     95: #define SBASEVAL16(logn) SOUND16_BASE_VAL
1.1.1.17  root       96: #define FINISH_DATA(data, b, logn)
1.1       root       97: #endif
                     98: 
1.1.1.17  root       99: static uae_u32 right_word_saved[SOUND_MAX_DELAY_BUFFER];
                    100: static uae_u32 left_word_saved[SOUND_MAX_DELAY_BUFFER];
1.1.1.7   root      101: static int saved_ptr;
1.1       root      102: 
1.1.1.17  root      103: static int mixed_on, mixed_stereo_size, mixed_mul1, mixed_mul2;
                    104: static int led_filter_forced, sound_use_filter, sound_use_filter_sinc, led_filter_on;
                    105: 
                    106: /* denormals are very small floating point numbers that force FPUs into slow
                    107:    mode. All lowpass filters using floats are suspectible to denormals unless
                    108:    a small offset is added to avoid very small floating point numbers. */
                    109: #define DENORMAL_OFFSET (1E-10)
                    110: 
                    111: static struct filter_state {
                    112:     float rc1, rc2, rc3, rc4, rc5;
                    113: } sound_filter_state[4];
                    114: 
                    115: static float a500e_filter1_a0;
                    116: static float a500e_filter2_a0;
                    117: static float filter_a0; /* a500 and a1200 use the same */
                    118: 
                    119: enum {
                    120:   FILTER_NONE = 0,
                    121:   FILTER_MODEL_A500,
                    122:   FILTER_MODEL_A1200
                    123: };
                    124: 
                    125: /* Amiga has two separate filtering circuits per channel, a static RC filter
                    126:  * on A500 and the LED filter. This code emulates both.
                    127:  *
                    128:  * The Amiga filtering circuitry depends on Amiga model. Older Amigas seem
                    129:  * to have a 6 dB/oct RC filter with cutoff frequency such that the -6 dB
                    130:  * point for filter is reached at 6 kHz, while newer Amigas have no filtering.
                    131:  *
                    132:  * The LED filter is complicated, and we are modelling it with a pair of
                    133:  * RC filters, the other providing a highboost. The LED starts to cut
                    134:  * into signal somewhere around 5-6 kHz, and there's some kind of highboost
                    135:  * in effect above 12 kHz. Better measurements are required.
                    136:  *
                    137:  * The current filtering should be accurate to 2 dB with the filter on,
                    138:  * and to 1 dB with the filter off.
                    139: */
                    140: 
                    141: static int filter(int input, struct filter_state *fs)
                    142: {
                    143:     int o;
                    144:     float normal_output, led_output;
                    145: 
                    146:     input = (uae_s16)input;
                    147:     switch (sound_use_filter) {
                    148:     case FILTER_NONE:
                    149:        return input;
                    150:     case FILTER_MODEL_A500:
                    151:        fs->rc1 = a500e_filter1_a0 * input + (1 - a500e_filter1_a0) * fs->rc1 + DENORMAL_OFFSET;
                    152:        fs->rc2 = a500e_filter2_a0 * fs->rc1 + (1-a500e_filter2_a0) * fs->rc2;
                    153:        normal_output = fs->rc2;
                    154: 
                    155:        fs->rc3 = filter_a0 * normal_output + (1 - filter_a0) * fs->rc3;
                    156:        fs->rc4 = filter_a0 * fs->rc3       + (1 - filter_a0) * fs->rc4;
                    157:        fs->rc5 = filter_a0 * fs->rc4       + (1 - filter_a0) * fs->rc5;
                    158: 
                    159:        led_output = fs->rc5;
                    160:        break;
                    161: 
                    162:     case FILTER_MODEL_A1200:
                    163:        normal_output = input;
                    164: 
                    165:        fs->rc2 = filter_a0 * normal_output + (1 - filter_a0) * fs->rc2 + DENORMAL_OFFSET;
                    166:        fs->rc3 = filter_a0 * fs->rc2       + (1 - filter_a0) * fs->rc3;
                    167:        fs->rc4 = filter_a0 * fs->rc3       + (1 - filter_a0) * fs->rc4;
                    168: 
                    169:        led_output = fs->rc4;
                    170:        break;
                    171:     }
                    172: 
                    173:     if (led_filter_on)
                    174:        o = led_output;
                    175:     else
                    176:        o = normal_output;
                    177: 
                    178:     if (o > 32767)
                    179:        o = 32767;
                    180:     else if (o < -32768)
                    181:        o = -32768;
                    182: 
                    183:     return o;
                    184: }
                    185: 
                    186: /* This computes the 1st order low-pass filter term b0.
                    187:  * The a1 term is 1.0 - b0. The center frequency marks the -3 dB point. */
                    188: #ifndef M_PI
                    189: #define M_PI 3.14159265358979323846
                    190: #endif
                    191: static float rc_calculate_a0 (int sample_rate, int cutoff_freq)
                    192: {
                    193:     float omega;
                    194:     /* The BLT correction formula below blows up if the cutoff is above nyquist. */
                    195:     if (cutoff_freq >= sample_rate / 2)
                    196:        return 1.0;
                    197: 
                    198:     omega = 2 * M_PI * cutoff_freq / sample_rate;
                    199:     /* Compensate for the bilinear transformation. This allows us to specify the
                    200:      * stop frequency more exactly, but the filter becomes less steep further
                    201:      * from stopband. */
                    202:     omega = tan (omega / 2) * 2;
                    203:     return 1 / (1 + 1 / omega);
                    204: }
                    205: 
                    206: /* Always put the right word before the left word.  */
                    207: 
1.1.1.7   root      208: STATIC_INLINE void put_sound_word_right (uae_u32 w)
1.1       root      209: {
1.1.1.17  root      210:     if (mixed_on) {
1.1.1.7   root      211:        right_word_saved[saved_ptr] = w;
                    212:        return;
1.1       root      213:     }
                    214: 
1.1.1.7   root      215:     PUT_SOUND_WORD_RIGHT (w);
1.1       root      216: }
                    217: 
1.1.1.7   root      218: STATIC_INLINE void put_sound_word_left (uae_u32 w)
1.1.1.5   root      219: {
1.1.1.17  root      220:     if (mixed_on) {
1.1.1.7   root      221:        uae_u32 rold, lold, rnew, lnew, tmp;
1.1.1.5   root      222: 
1.1.1.7   root      223:        left_word_saved[saved_ptr] = w;
                    224:        lnew = w - SOUND16_BASE_VAL;
                    225:        rnew = right_word_saved[saved_ptr] - SOUND16_BASE_VAL;
1.1.1.5   root      226: 
1.1.1.17  root      227:        saved_ptr = (saved_ptr + 1) & mixed_stereo_size;
                    228: 
1.1.1.7   root      229:        lold = left_word_saved[saved_ptr] - SOUND16_BASE_VAL;
1.1.1.17  root      230:        tmp = (rnew * mixed_mul2 + lold * mixed_mul1) / MIXED_STEREO_SCALE;
1.1.1.7   root      231:        tmp += SOUND16_BASE_VAL;
                    232:        PUT_SOUND_WORD_RIGHT (tmp);
1.1.1.5   root      233: 
1.1.1.7   root      234:        rold = right_word_saved[saved_ptr] - SOUND16_BASE_VAL;
1.1.1.17  root      235:        w = (lnew * mixed_mul2 + rold * mixed_mul1) / MIXED_STEREO_SCALE;
1.1.1.7   root      236:     }
                    237:     PUT_SOUND_WORD_LEFT (w);
                    238: }
1.1.1.5   root      239: 
1.1.1.7   root      240: #define DO_CHANNEL(v, c) do { (v) &= audio_channel[c].adk_mask; data += v; } while (0);
1.1.1.5   root      241: 
1.1.1.17  root      242: static void anti_prehandler (unsigned long best_evtime)
                    243: {
                    244:     int i, output;
                    245:     struct audio_channel_data *acd;
                    246: 
                    247:     /* Handle accumulator antialiasiation */
                    248:     for (i = 0; i < 4; i++) {
                    249:        acd = &audio_channel[i];
                    250:        output = (acd->current_sample * acd->vol) & acd->adk_mask;
                    251:        acd->sample_accum += output * best_evtime;
                    252:        acd->sample_accum_time += best_evtime;
                    253:     }
                    254: }
                    255: 
                    256: STATIC_INLINE void samplexx_anti_handler (int *datasp)
                    257: {
                    258:     int i;
                    259:     for (i = 0; i < 4; i++) {
                    260:        datasp[i] = audio_channel[i].sample_accum_time ? (audio_channel[i].sample_accum / audio_channel[i].sample_accum_time) : 0;
                    261:        audio_channel[i].sample_accum = 0;
                    262:        audio_channel[i].sample_accum_time = 0;
                    263: 
                    264:     }
                    265: }
                    266: 
1.1.1.16  root      267: static void sinc_prehandler (unsigned long best_evtime)
                    268: {
                    269:     int i, j, output;
                    270:     struct audio_channel_data *acd;
                    271: 
                    272:     for (i = 0; i < 4; i++) {
                    273:        acd = &audio_channel[i];
                    274:        output = (acd->current_sample * acd->vol) & acd->adk_mask;
                    275: 
                    276:        /* age the sinc queue and truncate it when necessary */
                    277:        for (j = 0; j < acd->sinc_queue_length; j += 1) {
                    278:            acd->sinc_queue[j].age += best_evtime;
                    279:            if (acd->sinc_queue[j].age >= SINC_QUEUE_MAX_AGE) {
                    280:                acd->sinc_queue_length = j;
                    281:                break;
                    282:            }
                    283:        }
                    284:        /* if output state changes, record the state change and also
                    285:         * write data into sinc queue for mixing in the BLEP */
                    286:        if (acd->sinc_output_state != output) {
                    287:            if (acd->sinc_queue_length > SINC_QUEUE_LENGTH - 1) {
                    288:                write_log ("warning: sinc queue truncated. Last age: %d.\n",
                    289:                           acd->sinc_queue[SINC_QUEUE_LENGTH-1].age);
                    290:                acd->sinc_queue_length = SINC_QUEUE_LENGTH - 1;
                    291:            }
                    292:            /* make room for new and add the new value */
                    293:            memmove (&acd->sinc_queue[1], &acd->sinc_queue[0],
                    294:                     sizeof(acd->sinc_queue[0]) * acd->sinc_queue_length);
                    295:            acd->sinc_queue_length += 1;
                    296:            acd->sinc_queue[0].age = best_evtime;
                    297:            acd->sinc_queue[0].output = output - acd->sinc_output_state;
                    298:            acd->sinc_output_state = output;
                    299:        }
                    300:     }
                    301: }
                    302: 
                    303: 
                    304: /* this interpolator performs BLEP mixing (bleps are shaped like integrated sinc
                    305:  * functions) with a type of BLEP that matches the filtering configuration. */
                    306: STATIC_INLINE void samplexx_sinc_handler (int *datasp)
                    307: {
                    308:     int i, n;
                    309:     int const *winsinc;
                    310: 
                    311:     if (sound_use_filter_sinc) {
                    312:        n = (sound_use_filter_sinc == FILTER_MODEL_A500) ? 0 : 2;
                    313:        if (led_filter_on)
                    314:            n += 1;
                    315:     } else {
                    316:        n = 4;
                    317:     }
                    318:     winsinc = winsinc_integral[n];
                    319: 
                    320:     for (i = 0; i < 4; i += 1) {
                    321:        int j, v;
                    322:        struct audio_channel_data *acd = &audio_channel[i];
                    323:        /* The sum rings with harmonic components up to infinity... */
                    324:        int sum = acd->sinc_output_state << 17;
                    325:        /* ...but we cancel them through mixing in BLEPs instead */
                    326:        for (j = 0; j < acd->sinc_queue_length; j += 1)
                    327:            sum -= winsinc[acd->sinc_queue[j].age] * acd->sinc_queue[j].output;
                    328:        v = sum >> 17;
                    329:        if (v > 32767)
                    330:            v = 32767;
                    331:        else if (v < -32768)
                    332:            v = -32768;
                    333:        datasp[i] = v;
                    334:     }
                    335: }
                    336: 
1.1.1.17  root      337: static void sample16si_anti_handler (void)
                    338: {
                    339:     int datas[4], data1, data2;
                    340: 
                    341:     samplexx_anti_handler (datas);
                    342:     data1 = datas[0] + datas[3];
                    343:     data2 = datas[1] + datas[2];
                    344:     FINISH_DATA (data1, 16, 1);
                    345:     if (sound_use_filter)
                    346:        data1 = filter (data1, &sound_filter_state[0]);
                    347:     put_sound_word_right (data1);
                    348:     FINISH_DATA (data2, 16, 1);
                    349:     if (sound_use_filter)
                    350:        data2 = filter (data2, &sound_filter_state[1]);
                    351:     put_sound_word_left (data2);
                    352:     check_sound_buffers ();
                    353: }
                    354: 
1.1.1.16  root      355: static void sample16si_sinc_handler (void)
1.1       root      356: {
1.1.1.16  root      357:     int datas[4], data1, data2;
1.1       root      358: 
1.1.1.16  root      359:     samplexx_sinc_handler (datas);
                    360:     data1 = datas[0] + datas[3];
                    361:     data2 = datas[1] + datas[2];
                    362:     FINISH_DATA (data1, 16, 1);
1.1.1.17  root      363:     put_sound_word_right (data1);
1.1.1.16  root      364:     FINISH_DATA (data2, 16, 1);
1.1.1.17  root      365:     put_sound_word_left (data2);
1.1       root      366:     check_sound_buffers ();
                    367: }
                    368: 
1.1.1.2   root      369: void sample16s_handler (void)
1.1       root      370: {
1.1.1.13  root      371:     uae_u32 data0 = audio_channel[0].current_sample;
                    372:     uae_u32 data1 = audio_channel[1].current_sample;
                    373:     uae_u32 data2 = audio_channel[2].current_sample;
                    374:     uae_u32 data3 = audio_channel[3].current_sample;
                    375:     DO_CHANNEL_1 (data0, 0);
                    376:     DO_CHANNEL_1 (data1, 1);
                    377:     DO_CHANNEL_1 (data2, 2);
                    378:     DO_CHANNEL_1 (data3, 3);
                    379: 
                    380:     data0 &= audio_channel[0].adk_mask;
                    381:     data1 &= audio_channel[1].adk_mask;
                    382:     data2 &= audio_channel[2].adk_mask;
                    383:     data3 &= audio_channel[3].adk_mask;
1.1.1.15  root      384: 
1.1.1.13  root      385:     data0 += data3;
                    386:     {
                    387:        uae_u32 data = SBASEVAL16(1) + data0;
                    388:        FINISH_DATA (data, 16, 1);
1.1.1.17  root      389:        if (sound_use_filter)
                    390:            data = filter (data, &sound_filter_state[0]);
1.1.1.13  root      391:        put_sound_word_right (data);
                    392:     }
1.1       root      393: 
1.1.1.13  root      394:     data1 += data2;
                    395:     {
1.1.1.15  root      396:        uae_u32 data = SBASEVAL16(1) + data1;
1.1.1.13  root      397:        FINISH_DATA (data, 16, 1);
1.1.1.17  root      398:        if (sound_use_filter)
                    399:            data = filter (data, &sound_filter_state[1]);
1.1.1.13  root      400:        put_sound_word_left (data);
1.1       root      401:     }
1.1.1.7   root      402: 
1.1       root      403:     check_sound_buffers ();
                    404: }
                    405: 
1.1.1.16  root      406: void switch_audio_interpol (void)
1.1       root      407: {
1.1.1.16  root      408:     if (currprefs.sound_interpol == 0) {
                    409:        changed_prefs.sound_interpol = 1;
1.1.1.18! root      410:        write_log ("Resampler on: sinc\n");
1.1.1.16  root      411:     } else if (currprefs.sound_interpol == 1) {
                    412:        changed_prefs.sound_interpol = 2;
1.1.1.18! root      413:        write_log ("Resampler on: anti\n");
1.1       root      414:     } else {
1.1.1.16  root      415:        changed_prefs.sound_interpol = 0;
1.1.1.18! root      416:        write_log ("Resampler off\n");
1.1       root      417:     }
1.1.1.16  root      418:     return;
1.1       root      419: }
1.1.1.16  root      420:  
1.1.1.7   root      421: void schedule_audio (void)
                    422: {
1.1.1.16  root      423:     unsigned long best = MAX_EV;
1.1.1.7   root      424:     int i;
                    425: 
                    426:     eventtab[ev_audio].active = 0;
                    427:     eventtab[ev_audio].oldcycles = get_cycles ();
1.1.1.16  root      428:     for (i = 0; i < 4; i++) {
1.1.1.7   root      429:        struct audio_channel_data *cdp = audio_channel + i;
                    430: 
1.1.1.16  root      431:        if (cdp->evtime != MAX_EV) {
1.1.1.7   root      432:            if (best > cdp->evtime) {
                    433:                best = cdp->evtime;
                    434:                eventtab[ev_audio].active = 1;
                    435:            }
1.1.1.15  root      436:        }
1.1.1.7   root      437:     }
                    438:     eventtab[ev_audio].evtime = get_cycles () + best;
                    439: }
                    440: 
1.1.1.16  root      441: /*
                    442:  * TODO: This function has been moved here from the audio back-end layer
                    443:  * since it was common to all.
                    444:  * Needs further cleaning up and a better name - or replacing entirely.
                    445:  */
                    446: void update_sound (unsigned int freq)
                    447: {
                    448:     if (obtainedfreq) {
1.1.1.17  root      449:        if (currprefs.ntscmode)
                    450:            scaled_sample_evtime = (unsigned long)(MAXHPOS_NTSC * MAXVPOS_NTSC * freq * CYCLE_UNIT + obtainedfreq - 1) / obtainedfreq;
                    451:        else
                    452:            scaled_sample_evtime = (unsigned long)(MAXHPOS_PAL * MAXVPOS_PAL * freq * CYCLE_UNIT + obtainedfreq - 1) / obtainedfreq;
1.1.1.16  root      453:     }
                    454: }
                    455: 
                    456: static void audio_handler (unsigned int nr)
1.1       root      457: {
                    458:     struct audio_channel_data *cdp = audio_channel + nr;
                    459: 
1.1.1.16  root      460:     cdp->evtime = MAX_EV;
1.1       root      461:     switch (cdp->state) {
                    462:      case 0:
1.1.1.11  root      463:        write_log ("Bug in sound code\n");
1.1       root      464:        break;
                    465: 
                    466:      case 1:
                    467:        /* We come here at the first hsync after DMA was turned on. */
1.1.1.7   root      468:        cdp->evtime = maxhpos * CYCLE_UNIT;
1.1       root      469: 
                    470:        cdp->state = 5;
                    471:        INTREQ(0x8000 | (0x80 << nr));
                    472:        if (cdp->wlen != 1)
1.1.1.9   root      473:            cdp->wlen = (cdp->wlen - 1) & 0xFFFF;
1.1.1.17  root      474:        cdp->nextdat = chipmem_agnus_wget (cdp->pt);
1.1       root      475: 
                    476:        cdp->pt += 2;
                    477:        break;
                    478: 
                    479:      case 5:
                    480:        /* We come here at the second hsync after DMA was turned on. */
                    481:        if (currprefs.produce_sound == 0)
1.1.1.7   root      482:            cdp->per = PERIOD_MAX;
1.1       root      483: 
1.1.1.2   root      484:        cdp->evtime = cdp->per;
1.1       root      485:        cdp->dat = cdp->nextdat;
1.1.1.5   root      486:        cdp->last_sample = cdp->current_sample;
1.1       root      487:        cdp->current_sample = (sample8_t)(cdp->dat >> 8);
                    488: 
                    489:        cdp->state = 2;
                    490:        {
                    491:            int audav = adkcon & (1 << nr);
                    492:            int audap = adkcon & (16 << nr);
                    493:            int napnav = (!audav && !audap) || audav;
                    494:            if (napnav)
                    495:                cdp->data_written = 2;
                    496:        }
                    497:        break;
                    498: 
                    499:      case 2:
                    500:        /* We come here when a 2->3 transition occurs */
                    501:        if (currprefs.produce_sound == 0)
1.1.1.7   root      502:            cdp->per = PERIOD_MAX;
1.1       root      503: 
1.1.1.5   root      504:        cdp->last_sample = cdp->current_sample;
1.1       root      505:        cdp->current_sample = (sample8_t)(cdp->dat & 0xFF);
1.1.1.2   root      506:        cdp->evtime = cdp->per;
1.1       root      507: 
                    508:        cdp->state = 3;
                    509: 
                    510:        /* Period attachment? */
                    511:        if (adkcon & (0x10 << nr)) {
                    512:            if (cdp->intreq2 && cdp->dmaen)
1.1.1.9   root      513:                INTREQ (0x8000 | (0x80 << nr));
1.1       root      514:            cdp->intreq2 = 0;
                    515: 
                    516:            cdp->dat = cdp->nextdat;
                    517:            if (cdp->dmaen)
                    518:                cdp->data_written = 2;
                    519:            if (nr < 3) {
                    520:                if (cdp->dat == 0)
1.1.1.7   root      521:                    (cdp+1)->per = PERIOD_MAX;
                    522:                else if (cdp->dat < maxhpos * CYCLE_UNIT / 2 && currprefs.produce_sound < 3)
                    523:                    (cdp+1)->per = maxhpos * CYCLE_UNIT / 2;
1.1       root      524:                else
1.1.1.7   root      525:                    (cdp+1)->per = cdp->dat * CYCLE_UNIT;
1.1       root      526:            }
                    527:        }
                    528:        break;
                    529: 
                    530:      case 3:
                    531:        /* We come here when a 3->2 transition occurs */
                    532:        if (currprefs.produce_sound == 0)
1.1.1.7   root      533:            cdp->per = PERIOD_MAX;
1.1       root      534: 
1.1.1.2   root      535:        cdp->evtime = cdp->per;
1.1       root      536: 
1.1.1.17  root      537:        if ((INTREQR () & (0x80 << nr)) && !cdp->dmaen) {
1.1       root      538:            cdp->state = 0;
1.1.1.16  root      539:            cdp->evtime = MAX_EV;
1.1.1.5   root      540:            cdp->last_sample = 0;
1.1       root      541:            cdp->current_sample = 0;
                    542:            break;
                    543:        } else {
                    544:            int audav = adkcon & (1 << nr);
                    545:            int audap = adkcon & (16 << nr);
                    546:            int napnav = (!audav && !audap) || audav;
                    547:            cdp->state = 2;
                    548: 
                    549:            if ((cdp->intreq2 && cdp->dmaen && napnav)
                    550:                || (napnav && !cdp->dmaen))
                    551:                INTREQ(0x8000 | (0x80 << nr));
                    552:            cdp->intreq2 = 0;
                    553: 
                    554:            cdp->dat = cdp->nextdat;
1.1.1.5   root      555:            cdp->last_sample = cdp->current_sample;
1.1       root      556:            cdp->current_sample = (sample8_t)(cdp->dat >> 8);
                    557: 
                    558:            if (cdp->dmaen && napnav)
                    559:                cdp->data_written = 2;
                    560: 
                    561:            /* Volume attachment? */
                    562:            if (audav) {
                    563:                if (nr < 3) {
                    564:                    (cdp+1)->vol = cdp->dat;
                    565: #ifndef MULTIPLICATION_PROFITABLE
                    566:                    (cdp+1)->voltbl = sound_table[cdp->dat];
                    567: #endif
                    568:                }
                    569:            }
                    570:        }
                    571:        break;
                    572: 
                    573:      default:
                    574:        cdp->state = 0;
                    575:        break;
                    576:     }
                    577: }
                    578: 
1.1.1.16  root      579: static void audio_channel_enable_dma (struct audio_channel_data *cdp)
1.1.1.10  root      580: {
1.1.1.16  root      581:     if (cdp->evtime == MAX_EV) {
1.1.1.10  root      582:        cdp->state = 1;
                    583:        cdp->pt = cdp->lc;
                    584:        cdp->wper = cdp->per;
                    585:        cdp->wlen = cdp->len;
                    586:        cdp->data_written = 2;
                    587:        cdp->evtime = eventtab[ev_hsync].evtime - get_cycles ();
                    588:     }
                    589: }
                    590: 
1.1.1.16  root      591: static void audio_channel_disable_dma (struct audio_channel_data *cdp)
1.1.1.10  root      592: {
                    593:     if (cdp->state == 1 || cdp->state == 5) {
                    594:        cdp->state = 0;
1.1.1.16  root      595:        cdp->evtime = MAX_EV;
1.1.1.10  root      596:        cdp->last_sample = 0;
                    597:        cdp->current_sample = 0;
                    598:     }
                    599: }
                    600: 
1.1       root      601: void audio_reset (void)
                    602: {
1.1.1.8   root      603:     int i;
1.1.1.16  root      604:     struct audio_channel_data *cdp;
                    605: 
1.1.1.17  root      606:     memset (sound_filter_state, 0, sizeof sound_filter_state);
1.1.1.8   root      607:     if (savestate_state != STATE_RESTORE) {
1.1.1.16  root      608:        for (i = 0; i < 4; i++) {
                    609:            cdp = &audio_channel[i];
                    610:            memset (cdp, 0, sizeof *audio_channel);
                    611:            cdp->per = PERIOD_MAX;
                    612:            cdp->vol = 0;
                    613:            cdp->evtime = MAX_EV;
                    614:        }
1.1.1.8   root      615:     } else
                    616:        for (i = 0; i < 4; i++)
                    617:            audio_channel[i].dmaen = (dmacon & 0x200) && (dmacon & (1 << i));
                    618: 
                    619: #ifndef MULTIPLICATION_PROFITABLE
1.1.1.13  root      620:     for (i = 0; i < 4; i++)
1.1.1.16  root      621:        audio_channel[i].voltbl = sound_table[audio_channel[i].vol];
1.1.1.8   root      622: #endif
1.1.1.2   root      623: 
1.1.1.16  root      624:     last_cycles = get_cycles ();
1.1.1.7   root      625:     next_sample_evtime = scaled_sample_evtime;
1.1.1.8   root      626:     schedule_audio ();
1.1.1.16  root      627:     events_schedule ();
1.1.1.2   root      628: }
                    629: 
1.1.1.6   root      630: STATIC_INLINE int sound_prefs_changed (void)
1.1.1.2   root      631: {
                    632:     return (changed_prefs.produce_sound != currprefs.produce_sound
1.1.1.13  root      633:            || changed_prefs.sound_stereo != currprefs.sound_stereo
1.1.1.9   root      634:            || changed_prefs.sound_maxbsiz != currprefs.sound_maxbsiz
1.1.1.17  root      635:            || changed_prefs.sound_freq != currprefs.sound_freq);
1.1.1.2   root      636: }
                    637: 
                    638: void check_prefs_changed_audio (void)
                    639: {
1.1.1.17  root      640:     int old_mixed_on = mixed_on;
                    641:     int old_mixed_size = mixed_stereo_size;
                    642:     int sep, delay;
                    643: 
                    644:     /* Some options we can just apply without reinitializing the sound
                    645:        backend.  */
                    646:     currprefs.sound_interpol = changed_prefs.sound_interpol;
                    647:     currprefs.sound_filter = changed_prefs.sound_filter;
                    648:     currprefs.sound_filter_type = changed_prefs.sound_filter_type;
                    649: 
                    650:     sep = currprefs.sound_stereo_separation = changed_prefs.sound_stereo_separation;
                    651:     delay = currprefs.sound_mixed_stereo_delay = changed_prefs.sound_mixed_stereo_delay;
                    652:     mixed_mul1 = MIXED_STEREO_SCALE / 2 - sep;
                    653:     mixed_mul2 = MIXED_STEREO_SCALE / 2 + sep;
                    654:     mixed_stereo_size = delay > 0 ? (1 << (delay - 1)) - 1 : 0;
                    655:     mixed_on = (sep > 0 && sep < MIXED_STEREO_MAX) || mixed_stereo_size > 0;
                    656:     if (mixed_on && old_mixed_size != mixed_stereo_size) {
                    657:        saved_ptr = 0;
                    658:        memset (right_word_saved, 0, sizeof right_word_saved);
                    659:     }
                    660: 
1.1.1.6   root      661:     if (sound_available && sound_prefs_changed ()) {
1.1.1.17  root      662:        if (currprefs.produce_sound >= 2)
                    663:            close_sound ();
1.1.1.2   root      664: 
1.1.1.6   root      665:        currprefs.produce_sound = changed_prefs.produce_sound;
1.1.1.13  root      666:        currprefs.sound_stereo = changed_prefs.sound_stereo;
1.1.1.6   root      667:        currprefs.sound_freq = changed_prefs.sound_freq;
1.1.1.9   root      668:        currprefs.sound_maxbsiz = changed_prefs.sound_maxbsiz;
1.1.1.6   root      669:        if (currprefs.produce_sound >= 2) {
1.1.1.17  root      670:            if (!init_audio ()) {
1.1.1.6   root      671:                if (! sound_available) {
1.1.1.11  root      672:                    write_log ("Sound is not supported.\n");
1.1.1.6   root      673:                } else {
1.1.1.11  root      674:                    write_log ("Sorry, can't initialize sound.\n");
1.1.1.6   root      675:                    currprefs.produce_sound = 0;
                    676:                    /* So we don't do this every frame */
                    677:                    changed_prefs.produce_sound = 0;
                    678:                }
1.1.1.17  root      679:            }
                    680:            next_sample_evtime = scaled_sample_evtime;
                    681:            last_cycles = get_cycles () - 1;
                    682:            compute_vsynctime ();
                    683:        }
                    684:        if (currprefs.produce_sound == 0) {
                    685:            eventtab[ev_audio].active = 0;
                    686:            events_schedule ();
1.1.1.6   root      687:        }
1.1.1.2   root      688:     }
1.1.1.17  root      689: 
                    690:     led_filter_forced = -1; // always off
                    691:     sound_use_filter = sound_use_filter_sinc = 0;
                    692:     if (currprefs.sound_filter != FILTER_SOUND_OFF) {
                    693:        if (currprefs.sound_filter == FILTER_SOUND_ON)
                    694:            led_filter_forced = 1;
                    695:        if (currprefs.sound_filter == FILTER_SOUND_EMUL)
                    696:            led_filter_forced = 0;
                    697:        if (currprefs.sound_filter_type == FILTER_SOUND_TYPE_A500)
                    698:            sound_use_filter = FILTER_MODEL_A500;
                    699:        else if (currprefs.sound_filter_type == FILTER_SOUND_TYPE_A1200)
                    700:            sound_use_filter = FILTER_MODEL_A1200;
                    701:     }
                    702:     a500e_filter1_a0 = rc_calculate_a0(currprefs.sound_freq, 6200);
                    703:     a500e_filter2_a0 = rc_calculate_a0(currprefs.sound_freq, 20000);
                    704:     filter_a0 = rc_calculate_a0(currprefs.sound_freq, 7000);
                    705:     led_filter_audio();
                    706: 
1.1.1.6   root      707:     /* Select the right interpolation method.  */
1.1.1.18! root      708:     if (sample_handler == sample16s_handler
        !           709:        || sample_handler == sample16si_sinc_handler
        !           710:        || sample_handler == sample16si_anti_handler)
1.1.1.17  root      711:     {
1.1.1.6   root      712:        sample_handler = (currprefs.sound_interpol == 0 ? sample16s_handler
1.1.1.18! root      713:                          : currprefs.sound_interpol == 1 ? sample16si_sinc_handler
1.1.1.17  root      714:                          : sample16si_anti_handler);
1.1.1.18! root      715:     }
1.1.1.16  root      716:     sample_prehandler = NULL;
1.1.1.18! root      717:     if (currprefs.sound_interpol == 1) {
1.1.1.17  root      718:        sound_use_filter_sinc = sound_use_filter;
                    719:        sound_use_filter = 0;
1.1.1.16  root      720:        sample_prehandler = sinc_prehandler;
1.1.1.18! root      721:     } else if (currprefs.sound_interpol == 2) {
1.1.1.17  root      722:        sample_prehandler = anti_prehandler;
1.1.1.7   root      723:     }
1.1.1.2   root      724: }
                    725: 
                    726: void update_audio (void)
                    727: {
                    728:     unsigned long int n_cycles;
                    729: 
1.1.1.8   root      730:     if (currprefs.produce_sound == 0 || savestate_state == STATE_RESTORE)
1.1.1.2   root      731:        return;
                    732: 
1.1.1.7   root      733:     n_cycles = get_cycles () - last_cycles;
1.1.1.2   root      734:     for (;;) {
                    735:        unsigned long int best_evtime = n_cycles + 1;
1.1.1.16  root      736:        if (audio_channel[0].evtime != MAX_EV && best_evtime > audio_channel[0].evtime)
1.1.1.10  root      737:            best_evtime = audio_channel[0].evtime;
1.1.1.16  root      738:        if (audio_channel[1].evtime != MAX_EV && best_evtime > audio_channel[1].evtime)
1.1.1.10  root      739:            best_evtime = audio_channel[1].evtime;
1.1.1.16  root      740:        if (audio_channel[2].evtime != MAX_EV && best_evtime > audio_channel[2].evtime)
1.1.1.10  root      741:            best_evtime = audio_channel[2].evtime;
1.1.1.16  root      742:        if (audio_channel[3].evtime != MAX_EV && best_evtime > audio_channel[3].evtime)
1.1.1.10  root      743:            best_evtime = audio_channel[3].evtime;
1.1.1.7   root      744:        if (currprefs.produce_sound > 1 && best_evtime > next_sample_evtime)
1.1.1.2   root      745:            best_evtime = next_sample_evtime;
                    746: 
                    747:        if (best_evtime > n_cycles)
                    748:            break;
                    749: 
1.1.1.16  root      750:        if (audio_channel[0].evtime != MAX_EV)
                    751:            audio_channel[0].evtime -= best_evtime;
                    752:        if (audio_channel[1].evtime != MAX_EV)
                    753:            audio_channel[1].evtime -= best_evtime;
                    754:        if (audio_channel[2].evtime != MAX_EV)
                    755:            audio_channel[2].evtime -= best_evtime;
                    756:        if (audio_channel[3].evtime != MAX_EV)
                    757:            audio_channel[3].evtime -= best_evtime;
1.1.1.2   root      758:        n_cycles -= best_evtime;
1.1.1.16  root      759:        if (currprefs.produce_sound > 1) {
                    760:            next_sample_evtime -= best_evtime;
                    761:            if (sample_prehandler)
                    762:                sample_prehandler (best_evtime / CYCLE_UNIT);
                    763:            if (next_sample_evtime == 0) {
                    764:                next_sample_evtime = scaled_sample_evtime;
                    765:                (*sample_handler) ();
                    766:            }
1.1.1.2   root      767:        }
1.1.1.16  root      768:        if (audio_channel[0].evtime == 0)
1.1.1.2   root      769:            audio_handler (0);
1.1.1.16  root      770:        if (audio_channel[1].evtime == 0)
1.1.1.2   root      771:            audio_handler (1);
1.1.1.16  root      772:        if (audio_channel[2].evtime == 0)
1.1.1.2   root      773:            audio_handler (2);
1.1.1.16  root      774:        if (audio_channel[3].evtime == 0)
1.1.1.2   root      775:            audio_handler (3);
                    776:     }
1.1.1.7   root      777:     last_cycles = get_cycles () - n_cycles;
                    778: }
                    779: 
1.1.1.16  root      780: void update_audio_dmacon (void)
                    781: {
                    782:     unsigned int i;
                    783:     update_audio ();
                    784: 
                    785:     for (i = 0; i < 4; i++) {
                    786:        struct audio_channel_data *cdp = audio_channel + i;
                    787:        int chan_ena = (dmacon & 0x200) && (dmacon & (1<<i));
                    788:        if (cdp->dmaen == chan_ena)
                    789:            continue;
                    790:        cdp->dmaen = chan_ena;
                    791:        if (cdp->dmaen)
                    792:            audio_channel_enable_dma (cdp);
                    793:        else
                    794:            audio_channel_disable_dma (cdp);
                    795:     }
                    796:     schedule_audio ();
                    797: }
                    798: 
1.1.1.7   root      799: void audio_evhandler (void)
                    800: {
                    801:     if (currprefs.produce_sound == 0)
                    802:        abort ();
                    803: 
                    804:     update_audio ();
                    805:     schedule_audio ();
1.1.1.2   root      806: }
                    807: 
1.1.1.15  root      808: void audio_hsync (int dmaaction)
                    809: {
                    810:     int nr;
                    811: 
                    812:     update_audio ();
                    813: 
                    814:     /* Sound data is fetched at the beginning of each line */
                    815:     for (nr = 0; nr < 4; nr++) {
                    816:        struct audio_channel_data *cdp = audio_channel + nr;
                    817: 
                    818:        if (cdp->data_written == 2) {
                    819:            cdp->data_written = 0;
1.1.1.17  root      820:            cdp->nextdat = chipmem_agnus_wget (cdp->pt);
1.1.1.15  root      821:            cdp->pt += 2;
                    822:            if (cdp->state == 2 || cdp->state == 3) {
                    823:                if (cdp->wlen == 1) {
                    824:                    cdp->pt = cdp->lc;
                    825:                    cdp->wlen = cdp->len;
                    826:                    cdp->intreq2 = 1;
                    827:                } else
                    828:                    cdp->wlen = (cdp->wlen - 1) & 0xFFFF;
                    829:            }
                    830:        }
                    831:     }
                    832: }
                    833: 
1.1.1.17  root      834: void AUDxDAT (int nr, uae_u16 v)
1.1.1.2   root      835: {
                    836:     struct audio_channel_data *cdp = audio_channel + nr;
                    837: 
1.1.1.7   root      838:     if (currprefs.produce_sound == 0)
                    839:        return;
                    840: 
1.1.1.2   root      841:     update_audio ();
                    842: 
                    843:     cdp->dat = v;
1.1.1.17  root      844:     if (cdp->state == 0 && !(INTREQR () & (0x80 << nr))) {
1.1.1.2   root      845:        cdp->state = 2;
1.1.1.17  root      846:        INTREQ (0x8000 | (0x80 << nr));
1.1.1.2   root      847:        /* data_written = 2 ???? */
                    848:        cdp->evtime = cdp->per;
1.1.1.7   root      849:        schedule_audio ();
                    850:        events_schedule ();
1.1.1.2   root      851:     }
                    852: }
                    853: 
1.1.1.17  root      854: void AUDxLCH (int nr, uae_u16 v)
1.1.1.2   root      855: {
                    856:     update_audio ();
                    857: 
                    858:     audio_channel[nr].lc = (audio_channel[nr].lc & 0xffff) | ((uae_u32)v << 16);
                    859: }
                    860: 
1.1.1.17  root      861: void AUDxLCL (int nr, uae_u16 v)
1.1.1.2   root      862: {
                    863:     update_audio ();
                    864: 
                    865:     audio_channel[nr].lc = (audio_channel[nr].lc & ~0xffff) | (v & 0xFFFE);
                    866: }
                    867: 
1.1.1.17  root      868: void AUDxPER (int nr, uae_u16 v)
1.1.1.2   root      869: {
1.1.1.7   root      870:     unsigned long per = v * CYCLE_UNIT;
1.1.1.2   root      871:     update_audio ();
                    872: 
1.1.1.7   root      873:     if (per == 0)
                    874:        per = PERIOD_MAX;
1.1.1.2   root      875: 
1.1.1.7   root      876:     if (per < maxhpos * CYCLE_UNIT / 2 && currprefs.produce_sound < 3)
                    877:        per = maxhpos * CYCLE_UNIT / 2;
1.1.1.16  root      878:     /* the sinc code registers paula output state changes, but has a finite
                    879:      * buffer in which to do so. Hence, we forbid very low values; this should
                    880:      * only limit the accurate rendering of supersonic sounds, which are
                    881:      * filtered away on the sinc output path anyway. */
                    882:     if (currprefs.produce_sound == 3 && sample_handler == sample16si_sinc_handler && per < MIN_ALLOWED_PERIOD * CYCLE_UNIT)
                    883:        per = MIN_ALLOWED_PERIOD * CYCLE_UNIT;
1.1.1.2   root      884: 
1.1.1.16  root      885:     if (audio_channel[nr].per == PERIOD_MAX && per != PERIOD_MAX
                    886:        && audio_channel[nr].evtime != MAX_EV) {
1.1.1.8   root      887:        audio_channel[nr].evtime = CYCLE_UNIT;
1.1.1.9   root      888:        if (currprefs.produce_sound > 0) {
                    889:            schedule_audio ();
                    890:            events_schedule ();
                    891:        }
1.1.1.8   root      892:     }
1.1.1.16  root      893: 
1.1.1.7   root      894:     audio_channel[nr].per = per;
1.1.1.2   root      895: }
                    896: 
1.1.1.17  root      897: void AUDxLEN (int nr, uae_u16 v)
1.1.1.2   root      898: {
                    899:     update_audio ();
                    900:     audio_channel[nr].len = v;
                    901: }
                    902: 
1.1.1.17  root      903: void AUDxVOL (int nr, uae_u16 v)
1.1.1.2   root      904: {
                    905:     int v2 = v & 64 ? 63 : v & 63;
                    906: 
                    907:     update_audio ();
                    908: 
                    909:     audio_channel[nr].vol = v2;
                    910: #ifndef MULTIPLICATION_PROFITABLE
                    911:     audio_channel[nr].voltbl = sound_table[v2];
                    912: #endif
1.1       root      913: }
                    914: 
1.1.1.15  root      915: void update_adkmasks (void)
                    916: {
                    917:     unsigned long t;
                    918: 
                    919:     t = adkcon | (adkcon >> 4);
                    920:     audio_channel[0].adk_mask = (((t >> 0) & 1) - 1);
                    921:     audio_channel[1].adk_mask = (((t >> 1) & 1) - 1);
                    922:     audio_channel[2].adk_mask = (((t >> 2) & 1) - 1);
                    923:     audio_channel[3].adk_mask = (((t >> 3) & 1) - 1);
                    924: }
                    925: 
1.1.1.7   root      926: int init_audio (void)
1.1       root      927: {
1.1.1.16  root      928:     int result = init_sound ();
                    929:     update_sound (vblank_hz);
                    930:     return result;
1.1.1.7   root      931: }
                    932: 
1.1.1.17  root      933: void led_filter_audio (void)
                    934: {
                    935:     led_filter_on = 0;
                    936:     if (led_filter_forced > 0 || (gui_data.powerled && led_filter_forced >= 0))
                    937:        led_filter_on = 1;
                    938:     gui_led (0, gui_data.powerled);
                    939: }
                    940: 
1.1.1.8   root      941: /* audio save/restore code FIXME: not working correctly */
                    942: /* help needed */
                    943: 
1.1.1.16  root      944: const uae_u8 *restore_audio (int i, const uae_u8 *src)
1.1.1.8   root      945: {
                    946:     struct audio_channel_data *acd;
                    947:     uae_u16 p;
                    948: 
                    949:     acd = audio_channel + i;
                    950:     acd->state = restore_u8 ();
                    951:     acd->vol = restore_u8 ();
                    952:     acd->intreq2 = restore_u8 ();
                    953:     acd->data_written = restore_u8 ();
                    954:     acd->len = restore_u16 ();
                    955:     acd->wlen = restore_u16 ();
                    956:     p = restore_u16 ();
                    957:     acd->per = p ? p * CYCLE_UNIT : PERIOD_MAX;
                    958:     p = restore_u16 ();
                    959:     acd->wper = p ? p * CYCLE_UNIT : PERIOD_MAX;
                    960:     acd->lc = restore_u32 ();
                    961:     acd->pt = restore_u32 ();
                    962:     acd->evtime = restore_u32 ();
                    963: 
                    964:     return src;
                    965: }
                    966: 
1.1.1.15  root      967: uae_u8 *save_audio (int i, int *len)
1.1.1.8   root      968: {
                    969:     struct audio_channel_data *acd;
                    970:     uae_u8 *dst = malloc (100);
                    971:     uae_u8 *dstbak = dst;
                    972:     uae_u16 p;
                    973: 
                    974:     acd = audio_channel + i;
                    975:     save_u8 ((uae_u8)acd->state);
                    976:     save_u8 (acd->vol);
                    977:     save_u8 (acd->intreq2);
                    978:     save_u8 (acd->data_written);
                    979:     save_u16 (acd->len);
                    980:     save_u16 (acd->wlen);
                    981:     p = acd->per == PERIOD_MAX ? 0 : acd->per / CYCLE_UNIT;
                    982:     save_u16 (p);
                    983:     p = acd->per == PERIOD_MAX ? 0 : acd->wper / CYCLE_UNIT;
                    984:     save_u16 (p);
                    985:     save_u32 (acd->lc);
                    986:     save_u32 (acd->pt);
                    987:     save_u32 (acd->evtime);
                    988:     *len = dst - dstbak;
                    989:     return dstbak;
                    990: }

unix.superglobalmegacorp.com

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