Annotation of tme/machine/sun4/sun4-timer.c, revision 1.1.1.1

1.1       root        1: /* $Id: sun4-timer.c,v 1.2 2006/11/16 02:42:16 fredette Exp $ */
                      2: 
                      3: /* machine/sun4/sun4-timer.c - implementation of Sun 4 timer emulation: */
                      4: 
                      5: /*
                      6:  * Copyright (c) 2006 Matt Fredette
                      7:  * All rights reserved.
                      8:  *
                      9:  * Redistribution and use in source and binary forms, with or without
                     10:  * modification, are permitted provided that the following conditions
                     11:  * are met:
                     12:  * 1. Redistributions of source code must retain the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer.
                     14:  * 2. Redistributions in binary form must reproduce the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer in the
                     16:  *    documentation and/or other materials provided with the distribution.
                     17:  * 3. All advertising materials mentioning features or use of this software
                     18:  *    must display the following acknowledgement:
                     19:  *      This product includes software developed by Matt Fredette.
                     20:  * 4. The name of the author may not be used to endorse or promote products
                     21:  *    derived from this software without specific prior written permission.
                     22:  *
                     23:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     24:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
                     25:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
                     26:  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
                     27:  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
                     28:  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
                     29:  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     30:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
                     31:  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
                     32:  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     33:  * POSSIBILITY OF SUCH DAMAGE.
                     34:  */
                     35: 
                     36: #include <tme/common.h>
                     37: _TME_RCSID("$Id: sun4-timer.c,v 1.2 2006/11/16 02:42:16 fredette Exp $");
                     38: 
                     39: /* includes: */
                     40: #include <tme/generic/bus-device.h>
                     41: #include "sun4-impl.h"
                     42: 
                     43: /* macros: */
                     44: 
                     45: /* real sun4/4c timer bits: */
                     46: #define TME_SUN4_32_TIMER_LIMIT                TME_BIT(31)
                     47: #define        TME_SUN44C_TIMER_MASK           (0x7ffffc00)
                     48: #define        TME_SUN4M_TIMER_MASK            (0x7ffffe00)
                     49: 
                     50: /* define this to track interrupt rates, reporting once every N
                     51:    seconds: */
                     52: #if 1
                     53: #define TME_SUN4_TIMER_TRACK_INT_RATE          (10)
                     54: #endif
                     55: 
                     56: /* this makes timer callouts.  it must be called with the mutex held: */
                     57: static void
                     58: _tme_sun4_timer_callout(struct tme_sun4 *sun4)
                     59: {
                     60:   struct tme_bus_connection *conn_bus;
                     61:   struct tme_sun4_timer *timer;
                     62:   unsigned int int_asserted;
                     63:   int again;
                     64:   int rc;
                     65: 
                     66:   /* if this function is already running in another thread, return
                     67:      now.  the other thread will do our work: */
                     68:   if (sun4->tme_sun4_timer_callouts_running) {
                     69:     return;
                     70:   }
                     71: 
                     72:   /* callouts are now running: */
                     73:   sun4->tme_sun4_timer_callouts_running = TRUE;
                     74: 
                     75:   /* get our bus connection: */
                     76:   conn_bus = sun4->tme_sun4_buses[TME_SUN4_32_CONN_REG_TIMER];
                     77: 
                     78:   /* loop forever: */
                     79:   for (again = TRUE; again;) {
                     80:     again = FALSE;
                     81: 
                     82:     /* check all of the timers for changes: */
                     83:     timer = &sun4->tme_sun4_timers[0];
                     84:     do {
                     85:       
                     86:       /* if this timer needs an interrupt callout: */
                     87:       int_asserted = (timer->tme_sun4_timer_counter & TME_SUN4_32_TIMER_LIMIT) != 0;
                     88:       if (!int_asserted
                     89:          != !timer->tme_sun4_timer_int_asserted) {
                     90: 
                     91:        /* unlock our mutex: */
                     92:        tme_mutex_unlock(&sun4->tme_sun4_mutex);
                     93: 
                     94:        /* call out the bus interrupt signal edge: */
                     95:        rc = (*conn_bus->tme_bus_signal)
                     96:          (conn_bus,
                     97:           ((timer == &sun4->tme_sun4_timers[0]
                     98:             ? TME_BUS_SIGNAL_INT(10)
                     99:             : TME_BUS_SIGNAL_INT(14))
                    100:            | (int_asserted
                    101:               ? TME_BUS_SIGNAL_LEVEL_ASSERTED
                    102:               : TME_BUS_SIGNAL_LEVEL_NEGATED)));
                    103: 
                    104:        /* lock our mutex: */
                    105:        tme_mutex_lock(&sun4->tme_sun4_mutex);
                    106: 
                    107:        /* if this callout was successful, note the new state of the
                    108:           interrupt signal: */
                    109:        if (rc == TME_OK) {
                    110:          timer->tme_sun4_timer_int_asserted = int_asserted;
                    111:          again = TRUE;
                    112:        }
                    113: 
                    114:        /* otherwise, abort: */
                    115:        else {
                    116:          abort();
                    117:        }
                    118:       }
                    119:     } while (++timer != (&sun4->tme_sun4_timers[0] + TME_ARRAY_ELS(sun4->tme_sun4_timers)));
                    120: 
                    121:   }
                    122: 
                    123:   /* there are no more callouts to make: */
                    124:   sun4->tme_sun4_timer_callouts_running = FALSE;
                    125: }
                    126: 
                    127: /* this can be called to force an immediate timer interrupt: */
                    128: void
                    129: _tme_sun4_timer_int_force(struct tme_sun4 *sun4,
                    130:                          struct tme_sun4_timer *timer)
                    131: {
                    132: 
                    133:   /* lock our mutex: */
                    134:   tme_mutex_lock(&sun4->tme_sun4_mutex);
                    135: 
                    136:   /* force an immediate timer interrupt: */
                    137:   timer->tme_sun4_timer_counter = TME_SUN4_32_TIMER_LIMIT;
                    138:   timer->tme_sun4_timer_limit |= TME_SUN4_32_TIMER_LIMIT;
                    139: 
                    140:   /* call out any interrupts: */
                    141:   _tme_sun4_timer_callout(sun4);
                    142: 
                    143:   /* unlock our mutex: */
                    144:   tme_mutex_unlock(&sun4->tme_sun4_mutex);
                    145: }
                    146: 
                    147: /* the sun4 timer update function.  it must be called with the mutex
                    148:    locked: */
                    149: static void
                    150: _tme_sun4_timer_update(struct tme_sun4_timer *timer, struct timeval *now, struct timeval *sleep)
                    151: {
                    152: 
                    153:   /* get the current time: */
                    154:   gettimeofday(now, NULL);
                    155: 
                    156: #ifdef TME_SUN4_TIMER_TRACK_INT_RATE
                    157: 
                    158:   /* if the sample time has finished: */
                    159:   if (timer->tme_sun4_timer_track_sample.tv_sec < now->tv_sec
                    160:       || (timer->tme_sun4_timer_track_sample.tv_sec == now->tv_sec
                    161:          && timer->tme_sun4_timer_track_sample.tv_usec <= now->tv_usec)) {
                    162: 
                    163:     /* if the timer has made any interrupts during the sample time: */
                    164:     if (timer->tme_sun4_timer_track_ints > 0) {
                    165: 
                    166:       /* log the interrupt rate: */
                    167:       tme_log(TME_SUN4_LOG_HANDLE(timer->tme_sun4_timer_sun4),
                    168:              0, TME_OK,
                    169:              (TME_SUN4_LOG_HANDLE(timer->tme_sun4_timer_sun4),
                    170:               "level %d timer interrupt rate: %ld/sec",
                    171:               (timer == &timer->tme_sun4_timer_sun4->tme_sun4_timers[0]
                    172:                ? 10
                    173:                : 14),
                    174:               (timer->tme_sun4_timer_track_ints
                    175:                / (now->tv_sec
                    176:                   - (timer->tme_sun4_timer_track_sample.tv_sec
                    177:                      - TME_SUN4_TIMER_TRACK_INT_RATE)))));
                    178:     }
                    179: 
                    180:     /* reset the sampling: */
                    181:     timer->tme_sun4_timer_track_ints = 0;
                    182:     timer->tme_sun4_timer_track_sample = *now;
                    183:     timer->tme_sun4_timer_track_sample.tv_sec += TME_SUN4_TIMER_TRACK_INT_RATE;
                    184:   }
                    185: 
                    186: #endif /* TME_SUN4_TIMER_TRACK_INT_RATE */
                    187: 
                    188:   /* if this timer has not reached its next limit: */
                    189:   if (__tme_predict_false(timer->tme_sun4_timer_limit_next.tv_sec > now->tv_sec
                    190:                          || (timer->tme_sun4_timer_limit_next.tv_sec == now->tv_sec
                    191:                              && timer->tme_sun4_timer_limit_next.tv_usec > now->tv_usec))) {
                    192: 
                    193:     /* sleep until this timer reaches its next limit: */
                    194:     sleep->tv_sec = timer->tme_sun4_timer_limit_next.tv_sec - now->tv_sec;
                    195:     sleep->tv_usec = timer->tme_sun4_timer_limit_next.tv_usec - now->tv_usec;
                    196:     if (timer->tme_sun4_timer_limit_next.tv_usec < now->tv_usec) {
                    197:       sleep->tv_sec--;
                    198:       sleep->tv_usec += 1000000;
                    199:     }
                    200:     return;
                    201:   }
                    202: 
                    203:   /* set this timer's next limit time: */
                    204:   do {
                    205:     timer->tme_sun4_timer_limit_next.tv_sec += timer->tme_sun4_timer_period.tv_sec;
                    206:     timer->tme_sun4_timer_limit_next.tv_usec += timer->tme_sun4_timer_period.tv_usec;
                    207:     if (__tme_predict_false(timer->tme_sun4_timer_limit_next.tv_usec >= 1000000)) {
                    208:       timer->tme_sun4_timer_limit_next.tv_usec -= 1000000;
                    209:       timer->tme_sun4_timer_limit_next.tv_sec += 1;
                    210:     }
                    211:   } while (timer->tme_sun4_timer_limit_next.tv_sec < now->tv_sec
                    212:           || (timer->tme_sun4_timer_limit_next.tv_sec == now->tv_sec
                    213:               && timer->tme_sun4_timer_limit_next.tv_usec <= now->tv_usec));
                    214: 
                    215:   /* mark this timer as having reached its limit: */
                    216: #ifdef TME_SUN4_TIMER_TRACK_INT_RATE
                    217:   if (!(timer->tme_sun4_timer_counter
                    218:        & TME_SUN4_32_TIMER_LIMIT)) {
                    219:     timer->tme_sun4_timer_track_ints++;
                    220:   }
                    221: #endif /* TME_SUN4_TIMER_TRACK_INT_RATE */
                    222:   timer->tme_sun4_timer_counter = TME_SUN4_32_TIMER_LIMIT;
                    223:   timer->tme_sun4_timer_limit |= TME_SUN4_32_TIMER_LIMIT;
                    224: 
                    225:   /* sleep for the normal period: */
                    226:   *sleep = timer->tme_sun4_timer_period;
                    227: }
                    228: 
                    229: /* this resets a timer: */
                    230: static void
                    231: _tme_sun4_timer_reset(struct tme_sun4_timer *timer)
                    232: {
                    233:   tme_uint32_t counter_one;
                    234:   tme_uint32_t ticks;
                    235:   tme_uint32_t ticks_max;
                    236:   tme_uint32_t usecs;
                    237: 
                    238:   /* to keep things simpler, we always use the sun4m 500ns tick: */
                    239:   counter_one = TME_SUN4_IS_SUN44C(timer->tme_sun4_timer_sun4) ? 2 : 1;
                    240:   ticks_max = (TME_SUN4M_TIMER_MASK / _TME_FIELD_MASK_FACTOR(TME_SUN4M_TIMER_MASK)) + 1;
                    241: 
                    242:   /* get this timer's period, in 500ns ticks.  NB that we account for
                    243:      the fact that timers count from [1..limit), and not [0..limit): */
                    244:   /* XXX FIXME - we assume that the limit value of one gives the
                    245:      longest possible period.  is this right? */
                    246:   ticks = TME_FIELD_MASK_EXTRACTU(timer->tme_sun4_timer_limit, TME_SUN4M_TIMER_MASK);
                    247:   ticks = (ticks - counter_one) & (ticks_max - counter_one);
                    248:   if (__tme_predict_false(ticks == 0)) {
                    249:     ticks = ticks_max;
                    250:   }
                    251: 
                    252:   /* convert the timer's period from 500ns ticks to a struct timeval
                    253:      and save it: */
                    254:   usecs = ticks / 2;
                    255:   timer->tme_sun4_timer_period.tv_sec = 0;
                    256:   if (__tme_predict_false(usecs >= 1000000)) {
                    257:     timer->tme_sun4_timer_period.tv_sec = usecs / 1000000;
                    258:     usecs %= 1000000;
                    259:   }
                    260:   timer->tme_sun4_timer_period.tv_usec = usecs;
                    261: 
                    262:   /* set the next limit time for this timer: */
                    263:   gettimeofday(&timer->tme_sun4_timer_limit_next, NULL);
                    264:   timer->tme_sun4_timer_limit_next.tv_sec += timer->tme_sun4_timer_period.tv_sec;
                    265:   timer->tme_sun4_timer_limit_next.tv_usec += timer->tme_sun4_timer_period.tv_usec;
                    266:   if (timer->tme_sun4_timer_limit_next.tv_usec >= 1000000) {
                    267:     timer->tme_sun4_timer_limit_next.tv_usec -= 1000000;
                    268:     timer->tme_sun4_timer_limit_next.tv_sec += 1;
                    269:   }
                    270: }
                    271: 
                    272: /* the sun4 timer thread: */
                    273: static void
                    274: _tme_sun4_timer_th(struct tme_sun4_timer *timer)
                    275: {
                    276:   struct tme_sun4 *sun4;
                    277:   struct timeval now;
                    278:   struct timeval sleep;
                    279: 
                    280:   /* recover our sun4: */
                    281:   sun4 = timer->tme_sun4_timer_sun4;
                    282: 
                    283:   /* lock our mutex: */
                    284:   tme_mutex_lock(&sun4->tme_sun4_mutex);
                    285: 
                    286:   /* loop forever: */
                    287:   for (;;) {
                    288: 
                    289:     /* update this timer: */
                    290:     _tme_sun4_timer_update(timer, &now, &sleep);
                    291: 
                    292:     /* call out any interrupts: */
                    293:     _tme_sun4_timer_callout(sun4);
                    294: 
                    295:     /* sleep, but wake up if our timer configuration changes: */
                    296:     tme_cond_sleep_yield(&timer->tme_sun4_timer_cond,
                    297:                         &sun4->tme_sun4_mutex,
                    298:                         &sleep);
                    299:   }
                    300:   /* NOTREACHED */
                    301: }
                    302: 
                    303: /* the sun4 timer control bus cycle handler: */
                    304: int
                    305: _tme_sun4_timer_cycle_control(void *_sun4, struct tme_bus_cycle *cycle_init)
                    306: {
                    307:   struct tme_sun4 *sun4;
                    308:   unsigned int timer_i;
                    309:   struct tme_sun4_timer *timer;
                    310:   tme_uint32_t reg;
                    311:   tme_uint32_t value32;
                    312:   struct tme_bus_cycle cycle_resp;
                    313:   struct timeval now;
                    314:   struct timeval last_reset;
                    315:   tme_uint32_t counter_one;
                    316:   tme_uint32_t usecs;
                    317:   tme_uint32_t ticks;
                    318: 
                    319:   /* recover our sun4: */
                    320:   sun4 = (struct tme_sun4 *) _sun4;
                    321: 
                    322:   /* this must be a full 32-bit register access: */
                    323:   if ((cycle_init->tme_bus_cycle_address % sizeof(tme_uint32_t)) != 0
                    324:       || cycle_init->tme_bus_cycle_size != sizeof(tme_uint32_t)) {
                    325:     abort();
                    326:   }
                    327: 
                    328:   /* get the timer and register accessed: */
                    329:   if (TME_SUN4_IS_SUN44C(sun4)) {
                    330:     timer_i = cycle_init->tme_bus_cycle_address / TME_SUN44C_TIMER_SIZ_REG;
                    331:     reg = cycle_init->tme_bus_cycle_address & TME_SUN4_32_TIMER_SIZ_COUNTER;
                    332:   }
                    333:   else {
                    334:     abort();
                    335:   }
                    336:   timer = &sun4->tme_sun4_timers[timer_i];
                    337: 
                    338:   /* lock our mutex: */
                    339:   tme_mutex_lock(&sun4->tme_sun4_mutex);
                    340: 
                    341:   /* if this is a read: */
                    342:   if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_READ) {
                    343: 
                    344:     /* dispatch on the register: */
                    345:     switch (reg) {
                    346:     default: assert(FALSE);
                    347:     case TME_SUN4_32_TIMER_REG_COUNTER:
                    348: 
                    349:       /* update the timers: */
                    350:       _tme_sun4_timer_update(timer, &now, &last_reset);
                    351: 
                    352:       /* get the time of the last reset: */
                    353:       last_reset = timer->tme_sun4_timer_limit_next;
                    354:       if (last_reset.tv_usec < timer->tme_sun4_timer_period.tv_usec) {
                    355:        last_reset.tv_sec -= 1;
                    356:        last_reset.tv_usec += 1000000;
                    357:       }
                    358:       last_reset.tv_sec -= timer->tme_sun4_timer_period.tv_sec;
                    359:       last_reset.tv_usec -= timer->tme_sun4_timer_period.tv_usec;
                    360: 
                    361:       /* get the number of microseconds since the last reset: */
                    362:       usecs = now.tv_sec - last_reset.tv_sec;
                    363:       usecs *= 1000000;
                    364:       usecs
                    365:        += (((tme_int32_t) now.tv_usec)
                    366:            - ((tme_int32_t) last_reset.tv_usec));
                    367: 
                    368:       /* to keep things simpler, we always use the sun4m 500ns tick: */
                    369:       counter_one = TME_SUN4_IS_SUN44C(sun4) ? 2 : 1;
                    370: 
                    371:       /* convert the number of microseconds until this timer resets again,
                    372:         to the 500ns tick counter value for the timer.  NB that we
                    373:         account for the fact that timers count from [1..limit), and not
                    374:         [0..limit): */
                    375:       ticks = (usecs * 2) + counter_one;
                    376:       TME_FIELD_MASK_DEPOSITU(timer->tme_sun4_timer_counter, 
                    377:                              TME_SUN4M_TIMER_MASK,
                    378:                              ticks);
                    379: 
                    380:       /* read this timer's counter register: */
                    381:       value32 = timer->tme_sun4_timer_counter;
                    382:       break;
                    383: 
                    384:     case TME_SUN4_32_TIMER_REG_LIMIT:
                    385: 
                    386:       /* read this timer's limit register: */
                    387:       value32 = timer->tme_sun4_timer_limit;   
                    388: 
                    389:       /* a read of the limit register is used to acknowledge an
                    390:         interrupt, which probably means clearing the limit bit on the
                    391:         counter register: */
                    392:       timer->tme_sun4_timer_counter = 0;
                    393:       timer->tme_sun4_timer_limit &= ~TME_SUN4_32_TIMER_LIMIT;
                    394:       break;
                    395:     }
                    396: 
                    397:     tme_log(TME_SUN4_LOG_HANDLE(sun4), 2000, TME_OK,
                    398:            (TME_SUN4_LOG_HANDLE(sun4),
                    399:             _("timer #%d %s -> 0x%08x"),
                    400:             timer_i,
                    401:             (reg == TME_SUN4_32_TIMER_REG_COUNTER
                    402:              ? "counter"
                    403:              : "limit"),
                    404:             value32));
                    405: 
                    406:     /* byteswap the register value: */
                    407:     value32 = tme_htobe_u32(value32);
                    408:   }
                    409: 
                    410:   /* run the bus cycle: */
                    411:   cycle_resp.tme_bus_cycle_buffer = (tme_uint8_t *) &value32;
                    412:   cycle_resp.tme_bus_cycle_buffer_increment = 1;
                    413:   cycle_resp.tme_bus_cycle_lane_routing = cycle_init->tme_bus_cycle_lane_routing;
                    414:   cycle_resp.tme_bus_cycle_address = 0;
                    415:   cycle_resp.tme_bus_cycle_type = (cycle_init->tme_bus_cycle_type
                    416:                                   ^ (TME_BUS_CYCLE_WRITE
                    417:                                      | TME_BUS_CYCLE_READ));
                    418:   cycle_resp.tme_bus_cycle_port = TME_BUS_CYCLE_PORT(0, TME_BUS32_LOG2);
                    419:   tme_bus_cycle_xfer(cycle_init, &cycle_resp);
                    420: 
                    421:   /* if this is a write: */
                    422:   if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
                    423: 
                    424:     /* byteswap the register value: */
                    425:     value32 = tme_htobe_u32(value32);
                    426: 
                    427:     tme_log(TME_SUN4_LOG_HANDLE(sun4), 2000, TME_OK,
                    428:            (TME_SUN4_LOG_HANDLE(sun4),
                    429:             _("timer #%d %s <- 0x%08x"),
                    430:             timer_i,
                    431:             (reg == TME_SUN4_32_TIMER_REG_COUNTER
                    432:              ? "counter"
                    433:              : "limit"),
                    434:             value32));
                    435: 
                    436:     /* dispatch on the register: */
                    437:     switch (reg) {
                    438:     default: assert(FALSE);
                    439:     case TME_SUN4_32_TIMER_REG_COUNTER:
                    440:       abort();
                    441: 
                    442:     case TME_SUN4_32_TIMER_REG_LIMIT:
                    443: 
                    444:       /* write the timer's limit register: */
                    445:       timer->tme_sun4_timer_limit = value32;
                    446: 
                    447:       /* reset this timer: */
                    448:       _tme_sun4_timer_reset(timer);
                    449: 
                    450:       /* wake up the thread for this timer: */
                    451:       tme_cond_notify(&timer->tme_sun4_timer_cond, FALSE);
                    452:       break;
                    453:     }
                    454:   }
                    455: 
                    456:   /* make any callouts: */
                    457:   _tme_sun4_timer_callout(sun4);
                    458:     
                    459:   /* unlock the mutex: */
                    460:   tme_mutex_unlock(&sun4->tme_sun4_mutex);
                    461: 
                    462:   /* no faults: */
                    463:   return (TME_OK);
                    464: }
                    465: 
                    466: /* this creates the sun4 timers: */
                    467: void
                    468: _tme_sun4_timer_new(struct tme_sun4 *sun4)
                    469: {
                    470:   struct tme_sun4_timer *timer;
                    471: 
                    472:   /* loop over the timers: */
                    473:   timer = &sun4->tme_sun4_timers[0];
                    474:   do {
                    475: 
                    476:     /* initialize and reset the timer: */
                    477:     timer->tme_sun4_timer_sun4 = sun4;
                    478:     tme_cond_init(&timer->tme_sun4_timer_cond);
                    479:     _tme_sun4_timer_reset(timer);
                    480: 
                    481:     /* start the thread for this timer: */
                    482:     tme_thread_create((tme_thread_t) _tme_sun4_timer_th, timer);
                    483: 
                    484:   } while (++timer != (&sun4->tme_sun4_timers[0] + TME_ARRAY_ELS(sun4->tme_sun4_timers)));
                    485: }

unix.superglobalmegacorp.com

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