Annotation of 43BSDReno/sys/hp300/clock.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (c) 1988 University of Utah.
                      3:  * Copyright (c) 1982, 1990 The Regents of the University of California.
                      4:  * All rights reserved.
                      5:  *
                      6:  * This code is derived from software contributed to Berkeley by
                      7:  * the Systems Programming Group of the University of Utah Computer
                      8:  * Science Department.
                      9:  *
                     10:  * Redistribution is only permitted until one year after the first shipment
                     11:  * of 4.4BSD by the Regents.  Otherwise, redistribution and use in source and
                     12:  * binary forms are permitted provided that: (1) source distributions retain
                     13:  * this entire copyright notice and comment, and (2) distributions including
                     14:  * binaries display the following acknowledgement:  This product includes
                     15:  * software developed by the University of California, Berkeley and its
                     16:  * contributors'' in the documentation or other materials provided with the
                     17:  * distribution and in all advertising materials mentioning features or use
                     18:  * of this software.  Neither the name of the University nor the names of
                     19:  * its contributors may be used to endorse or promote products derived from
                     20:  * this software without specific prior written permission.
                     21:  * THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
                     22:  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
                     23:  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
                     24:  *
                     25:  * from: Utah $Hdr: clock.c 1.17 89/11/30$
                     26:  *
                     27:  *     @(#)clock.c     7.2 (Berkeley) 6/22/90
                     28:  */
                     29: 
                     30: #include "param.h"
                     31: #include "user.h"
                     32: #include "kernel.h"
                     33: #include "../hpdev/hilreg.h"
                     34: #include "clockreg.h"
                     35: 
                     36: #include "machine/psl.h"
                     37: #include "machine/cpu.h"
                     38: 
                     39: #if defined(GPROF) && defined(PROFTIMER)
                     40: #include "gprof.h"
                     41: #endif
                     42: 
                     43: int    clkstd[] = { IOV(0x5F8000) };
                     44: 
                     45: static int month_days[12] = {
                     46:        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
                     47: };
                     48: struct bbc_tm *gmt_to_bbc();
                     49: u_char bbc_registers[13];
                     50: u_char write_bbc_reg(), read_bbc_reg();
                     51: struct hil_dev *bbcaddr = NULL;
                     52: 
                     53: /*
                     54:  * Machine-dependent clock routines.
                     55:  *
                     56:  * Startrtclock restarts the real-time clock, which provides
                     57:  * hardclock interrupts to kern_clock.c.
                     58:  *
                     59:  * Inittodr initializes the time of day hardware which provides
                     60:  * date functions.
                     61:  *
                     62:  * Resettodr restores the time of day hardware after a time change.
                     63:  *
                     64:  * A note on the real-time clock:
                     65:  * We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL.
                     66:  * This is because the counter decrements to zero after N+1 enabled clock
                     67:  * periods where N is the value loaded into the counter.
                     68:  */
                     69: 
                     70: /*
                     71:  * Start the real-time clock.
                     72:  */
                     73: startrtclock()
                     74: {
                     75:        register struct clkreg *clk = (struct clkreg *)clkstd[0];
                     76: 
                     77:        clk->clk_cr2 = CLK_CR1;
                     78:        clk->clk_cr1 = CLK_RESET;
                     79:        clk->clk_cr2 = CLK_CR3;
                     80:        clk->clk_cr3 = 0;
                     81:        clk->clk_msb1 = (CLK_INTERVAL-1) >> 8 & 0xFF;
                     82:        clk->clk_lsb1 = (CLK_INTERVAL-1) & 0xFF;
                     83:        clk->clk_msb2 = 0;
                     84:        clk->clk_lsb2 = 0;
                     85:        clk->clk_msb3 = 0;
                     86:        clk->clk_lsb3 = 0;
                     87:        clk->clk_cr2 = CLK_CR1;
                     88:        clk->clk_cr1 = CLK_IENAB;
                     89: }
                     90: 
                     91: /*
                     92:  * Returns number of usec since last recorded clock "tick"
                     93:  * (i.e. clock interrupt).
                     94:  */
                     95: clkread()
                     96: {
                     97:        register struct clkreg *clk = (struct clkreg *) clkstd[0];
                     98:        register int high, low;
                     99: 
                    100:        high = clk->clk_msb1;
                    101:        low = clk->clk_lsb1;
                    102:        if (high != clk->clk_msb1)
                    103:                high = clk->clk_msb1;
                    104: 
                    105:        high = (CLK_INTERVAL-1) - ((high << 8) | low);
                    106:        /*
                    107:         * Pending interrupt indicates that the counter has wrapped
                    108:         * since we went to splhigh().  Need to compensate.
                    109:         */
                    110:        if (clk->clk_sr & CLK_INT1)
                    111:                high += CLK_INTERVAL;
                    112:        return((high * tick) / CLK_INTERVAL);
                    113: }
                    114: 
                    115: #include "clock.h"
                    116: #if NCLOCK > 0
                    117: /*
                    118:  * /dev/clock: mappable high resolution timer.
                    119:  *
                    120:  * This code implements a 32-bit recycling counter (with a 4 usec period)
                    121:  * using timers 2 & 3 on the 6840 clock chip.  The counter can be mapped
                    122:  * RO into a user's address space to achieve low overhead (no system calls),
                    123:  * high-precision timing.
                    124:  *
                    125:  * Note that timer 3 is also used for the high precision profiling timer
                    126:  * (PROFTIMER code above).  Care should be taken when both uses are
                    127:  * configured as only a token effort is made to avoid conflicting use.
                    128:  */
                    129: #include "proc.h"
                    130: #include "ioctl.h"
                    131: #include "mapmem.h"
                    132: #include "malloc.h"
                    133: #include "clockioctl.h"
                    134: 
                    135: int clockon = 0;               /* non-zero if high-res timer enabled */
                    136: #ifdef PROFTIMER
                    137: int  profprocs = 0;            /* # of procs using profiling timer */
                    138: #endif
                    139: #ifdef DEBUG
                    140: int clockdebug = 0;
                    141: #endif
                    142: 
                    143: /*ARGSUSED*/
                    144: clockopen(dev, flags)
                    145:        dev_t dev;
                    146: {
                    147: #ifdef PROFTIMER
                    148: #ifdef GPROF
                    149:        /*
                    150:         * Kernel profiling enabled, give up.
                    151:         */
                    152:        if (profiling)
                    153:                return(EBUSY);
                    154: #endif
                    155:        /*
                    156:         * If any user processes are profiling, give up.
                    157:         */
                    158:        if (profprocs)
                    159:                return(EBUSY);
                    160: #endif
                    161:        if (!clockon) {
                    162:                startclock();
                    163:                clockon++;
                    164:        }
                    165:        return(0);
                    166: }
                    167: 
                    168: /*ARGSUSED*/
                    169: clockclose(dev, flags)
                    170:        dev_t dev;
                    171: {
                    172: #ifdef MAPMEM
                    173:        (void) clockunmmap(dev, (caddr_t)0);
                    174: #endif
                    175:        stopclock();
                    176:        clockon = 0;
                    177:        return(0);
                    178: }
                    179: 
                    180: /*ARGSUSED*/
                    181: clockioctl(dev, cmd, data, flag)
                    182:        dev_t dev;
                    183:        caddr_t data;
                    184: {
                    185:        int error = 0;
                    186:        
                    187:        switch (cmd) {
                    188: 
                    189: #ifdef MAPMEM
                    190:        case CLOCKMAP:
                    191:                error = clockmmap(dev, (caddr_t *)data);
                    192:                break;
                    193: 
                    194:        case CLOCKUNMAP:
                    195:                error = clockunmmap(dev, *(caddr_t *)data);
                    196:                break;
                    197: 
                    198:        case CLOCKGETRES:
                    199:                *(int *)data = CLK_RESOLUTION;
                    200:                break;
                    201: #endif
                    202: 
                    203:        default:
                    204:                error = EINVAL;
                    205:                break;
                    206:        }
                    207:        return(error);
                    208: }
                    209: 
                    210: /*ARGSUSED*/
                    211: clockmap(dev, off, prot)
                    212:        dev_t dev;
                    213: {
                    214: #ifdef MMAP
                    215:        return((off + (IOBASE+CLKSR-1)) >> PGSHIFT);
                    216: #endif
                    217: }
                    218: 
                    219: #ifdef MAPMEM
                    220: 
                    221: struct mapmemops clockops = {
                    222:        (int (*)())0, (int (*)())0, (int (*)())0, (int (*)())0
                    223: };
                    224: 
                    225: clockmmap(dev, addrp)
                    226:        dev_t dev;
                    227:        caddr_t *addrp;
                    228: {
                    229:        struct proc *p = u.u_procp;             /* XXX */
                    230:        struct mapmem *mp;
                    231:        int id, error, clockmapin();
                    232: 
                    233:        id = minor(dev);        /* XXX */
                    234:        error = mmalloc(p, id, addrp, NBPG, MM_RO|MM_CI|MM_NOCORE,
                    235:                        &clockops, &mp);
                    236: #ifdef DEBUG
                    237:        if (clockdebug)
                    238:                printf("clockmmap(%d): addr %x\n", p->p_pid, *addrp);
                    239: #endif
                    240:        if (error == 0)
                    241:                if (error = mmmapin(p, mp, clockmapin))
                    242:                        (void) mmfree(p, mp);
                    243:        return(error);
                    244: }
                    245: 
                    246: clockunmmap(dev, addr)
                    247:        dev_t dev;
                    248:        caddr_t addr;
                    249: {
                    250:        struct proc *p = u.u_procp;             /* XXX */
                    251:        register struct mapmem *mp, **mpp;
                    252:        int found, id;
                    253: 
                    254: #ifdef DEBUG
                    255:        if (clockdebug)
                    256:                printf("clockunmmap(%d): addr %x\n", p->p_pid, addr);
                    257: #endif
                    258:        id = minor(dev);        /* XXX */
                    259:        found = 0;
                    260:        mpp = &u.u_mmap;
                    261:        for (mp = *mpp; mp; mp = *mpp) {
                    262:                if (mp->mm_id != id || mp->mm_ops != &clockops) {
                    263:                        mpp = &mp->mm_next;
                    264:                        continue;
                    265:                }
                    266:                if (addr &&
                    267:                    (addr < mp->mm_uva || addr >= mp->mm_uva+mp->mm_size)) {
                    268:                        mpp = &mp->mm_next;
                    269:                        continue;
                    270:                }
                    271:                mmmapout(p, mp);
                    272:                (void) mmfree(p, mp);
                    273:                found++;
                    274:        }
                    275:        return(found ? 0 : EINVAL);
                    276: }
                    277: 
                    278: /*ARGSUSED*/
                    279: clockmapin(mp, off)
                    280:        struct mapmem *mp;
                    281: {
                    282:        return((off + (IOBASE+CLKSR-1)) >> PGSHIFT);
                    283: }
                    284: #endif
                    285: 
                    286: startclock()
                    287: {
                    288:        register struct clkreg *clk = (struct clkreg *)clkstd[0];
                    289: 
                    290:        clk->clk_msb2 = -1; clk->clk_lsb2 = -1;
                    291:        clk->clk_msb3 = -1; clk->clk_lsb3 = -1;
                    292: 
                    293:        clk->clk_cr2 = CLK_CR3;
                    294:        clk->clk_cr3 = CLK_OENAB|CLK_8BIT;
                    295:        clk->clk_cr2 = CLK_CR1;
                    296:        clk->clk_cr1 = CLK_IENAB;
                    297: }
                    298: 
                    299: stopclock()
                    300: {
                    301:        register struct clkreg *clk = (struct clkreg *)clkstd[0];
                    302: 
                    303:        clk->clk_cr2 = CLK_CR3;
                    304:        clk->clk_cr3 = 0;
                    305:        clk->clk_cr2 = CLK_CR1;
                    306:        clk->clk_cr1 = CLK_IENAB;
                    307: }
                    308: #endif
                    309: 
                    310: #ifdef PROFTIMER
                    311: /*
                    312:  * This code allows the hp300 kernel to use one of the extra timers on
                    313:  * the clock chip for profiling, instead of the regular system timer.
                    314:  * The advantage of this is that the profiling timer can be turned up to
                    315:  * a higher interrupt rate, giving finer resolution timing. The profclock
                    316:  * routine is called from the lev6intr in locore, and is a specialized
                    317:  * routine that calls addupc. The overhead then is far less than if
                    318:  * hardclock/softclock was called. Further, the context switch code in
                    319:  * locore has been changed to turn the profile clock on/off when switching
                    320:  * into/out of a process that is profiling (startprofclock/stopprofclock).
                    321:  * This reduces the impact of the profiling clock on other users, and might
                    322:  * possibly increase the accuracy of the profiling. 
                    323:  */
                    324: int  profint   = PRF_INTERVAL; /* Clock ticks between interrupts */
                    325: int  profscale = 0;            /* Scale factor from sys clock to prof clock */
                    326: char profon    = 0;            /* Is profiling clock on? */
                    327: 
                    328: /* profon values - do not change, locore.s assumes these values */
                    329: #define PRF_NONE       0x00
                    330: #define        PRF_USER        0x01
                    331: #define        PRF_KERNEL      0x80
                    332: 
                    333: initprofclock()
                    334: {
                    335: #if NCLOCK > 0
                    336:        /*
                    337:         * If the high-res timer is running, force profiling off.
                    338:         * Unfortunately, this gets reflected back to the user not as
                    339:         * an error but as a lack of results.
                    340:         */
                    341:        if (clockon) {
                    342:                u.u_prof.pr_scale = 0;
                    343:                return;
                    344:        }
                    345:        /*
                    346:         * Keep track of the number of user processes that are profiling
                    347:         * by checking the scale value.
                    348:         *
                    349:         * XXX: this all assumes that the profiling code is well behaved;
                    350:         * i.e. profil() is called once per process with pcscale non-zero
                    351:         * to turn it on, and once with pcscale zero to turn it off.
                    352:         * Also assumes you don't do any forks or execs.  Oh well, there
                    353:         * is always adb...
                    354:         */
                    355:        if (u.u_prof.pr_scale)
                    356:                profprocs++;
                    357:        else
                    358:                profprocs--;
                    359: #endif
                    360:        /*
                    361:         * The profile interrupt interval must be an even divisor
                    362:         * of the CLK_INTERVAL so that scaling from a system clock
                    363:         * tick to a profile clock tick is possible using integer math.
                    364:         */
                    365:        if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0)
                    366:                profint = CLK_INTERVAL;
                    367:        profscale = CLK_INTERVAL / profint;
                    368: }
                    369: 
                    370: startprofclock()
                    371: {
                    372:        register struct clkreg *clk = (struct clkreg *)clkstd[0];
                    373: 
                    374:        clk->clk_msb3 = (profint-1) >> 8 & 0xFF;
                    375:        clk->clk_lsb3 = (profint-1) & 0xFF;
                    376: 
                    377:        clk->clk_cr2 = CLK_CR3;
                    378:        clk->clk_cr3 = CLK_IENAB;
                    379: }
                    380: 
                    381: stopprofclock()
                    382: {
                    383:        register struct clkreg *clk = (struct clkreg *)clkstd[0];
                    384: 
                    385:        clk->clk_cr2 = CLK_CR3;
                    386:        clk->clk_cr3 = 0;
                    387: }
                    388: 
                    389: #ifdef GPROF
                    390: /*
                    391:  * profclock() is expanded in line in lev6intr() unless profiling kernel.
                    392:  * Assumes it is called with clock interrupts blocked.
                    393:  */
                    394: profclock(pc, ps)
                    395:        caddr_t pc;
                    396:        int ps;
                    397: {
                    398:        /*
                    399:         * Came from user mode.
                    400:         * If this process is being profiled record the tick.
                    401:         */
                    402:        if (USERMODE(ps)) {
                    403:                if (u.u_prof.pr_scale)
                    404:                        addupc(pc, &u.u_prof, 1);
                    405:        }
                    406:        /*
                    407:         * Came from kernel (supervisor) mode.
                    408:         * If we are profiling the kernel, record the tick.
                    409:         */
                    410:        else if (profiling < 2) {
                    411:                register int s = pc - s_lowpc;
                    412: 
                    413:                if (s < s_textsize)
                    414:                        kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
                    415:        }
                    416:        /*
                    417:         * Kernel profiling was on but has been disabled.
                    418:         * Mark as no longer profiling kernel and if all profiling done,
                    419:         * disable the clock.
                    420:         */
                    421:        if (profiling && (profon & PRF_KERNEL)) {
                    422:                profon &= ~PRF_KERNEL;
                    423:                if (profon == PRF_NONE)
                    424:                        stopprofclock();
                    425:        }
                    426: }
                    427: #endif
                    428: #endif
                    429: 
                    430: /*
                    431:  * Initialize the time of day register, based on the time base which is, e.g.
                    432:  * from a filesystem.
                    433:  */
                    434: inittodr(base)
                    435:        time_t base;
                    436: {
                    437:        u_long timbuf = base;   /* assume no battery clock exists */
                    438:        static int bbcinited = 0;
                    439: 
                    440:        /* XXX */
                    441:        if (!bbcinited) {
                    442:                if (badbaddr(&BBCADDR->hil_stat))
                    443:                        printf("WARNING: no battery clock\n");
                    444:                else
                    445:                        bbcaddr = BBCADDR;
                    446:                bbcinited = 1;
                    447:        }
                    448: 
                    449:        /*
                    450:         * bbc_to_gmt converts and stores the gmt in timbuf.
                    451:         * If an error is detected in bbc_to_gmt, or if the filesystem
                    452:         * time is more recent than the gmt time in the clock,
                    453:         * then use the filesystem time and warn the user.
                    454:         */
                    455:        if (!bbc_to_gmt(&timbuf) || timbuf < base) {
                    456:                printf("WARNING: bad date in battery clock\n");
                    457:                timbuf = base;
                    458:        }
                    459:        if (base < 5*SECYR) {
                    460:                printf("WARNING: preposterous time in file system");
                    461:                timbuf = 6*SECYR + 186*SECDAY + SECDAY/2;
                    462:                printf(" -- CHECK AND RESET THE DATE!\n");
                    463:        }
                    464:        
                    465:        /* Battery clock does not store usec's, so forget about it. */
                    466:        time.tv_sec = timbuf;
                    467: }
                    468: 
                    469: resettodr()
                    470: {
                    471:        register int i;
                    472:        register struct bbc_tm *tmptr;
                    473: 
                    474:        tmptr = gmt_to_bbc(time.tv_sec);
                    475: 
                    476:        decimal_to_bbc(0, 1,  tmptr->tm_sec);
                    477:        decimal_to_bbc(2, 3,  tmptr->tm_min);
                    478:        decimal_to_bbc(4, 5,  tmptr->tm_hour);
                    479:        decimal_to_bbc(7, 8,  tmptr->tm_mday);
                    480:        decimal_to_bbc(9, 10, tmptr->tm_mon);
                    481:        decimal_to_bbc(11, 12, tmptr->tm_year);
                    482: 
                    483:        /* Some bogusness to deal with seemingly broken hardware. Nonsense */
                    484:        bbc_registers[5] = ((tmptr->tm_hour / 10) & 0x03) + 8;
                    485: 
                    486:        write_bbc_reg(15, 13);  /* reset prescalar */
                    487: 
                    488:        for (i = 0; i <= NUM_BBC_REGS; i++)
                    489:                if (bbc_registers[i] != write_bbc_reg(i, bbc_registers[i])) {
                    490:                        printf("Cannot set battery backed clock\n");
                    491:                        break;
                    492:                }
                    493: }
                    494: 
                    495: struct bbc_tm *
                    496: gmt_to_bbc(tim)
                    497:        long tim;
                    498: {
                    499:        register int i;
                    500:        register long hms, day;
                    501:        static struct bbc_tm rt;
                    502: 
                    503:        day = tim / SECDAY;
                    504:        hms = tim % SECDAY;
                    505: 
                    506:        /* Hours, minutes, seconds are easy */
                    507:        rt.tm_hour = hms / 3600;
                    508:        rt.tm_min  = (hms % 3600) / 60;
                    509:        rt.tm_sec  = (hms % 3600) % 60;
                    510: 
                    511:        /* Number of years in days */
                    512:        for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++)
                    513:                day -= days_in_year(i);
                    514:        rt.tm_year = i;
                    515:        
                    516:        /* Number of months in days left */
                    517:        if (leapyear(rt.tm_year))
                    518:                days_in_month(FEBRUARY) = 29;
                    519:        for (i = 1; day >= days_in_month(i); i++)
                    520:                day -= days_in_month(i);
                    521:        days_in_month(FEBRUARY) = 28;
                    522:        rt.tm_mon = i;
                    523: 
                    524:        /* Days are what is left over (+1) from all that. */
                    525:        rt.tm_mday = day + 1;  
                    526:        
                    527:        return(&rt);
                    528: }
                    529: 
                    530: bbc_to_gmt(timbuf)
                    531:        u_long *timbuf;
                    532: {
                    533:        register int i;
                    534:        register u_long tmp;
                    535:        int year, month, day, hour, min, sec;
                    536: 
                    537:        read_bbc();
                    538: 
                    539:        sec = bbc_to_decimal(1, 0);
                    540:        min = bbc_to_decimal(3, 2);
                    541: 
                    542:        /*
                    543:         * Hours are different for some reason. Makes no sense really.
                    544:         */
                    545:        hour  = ((bbc_registers[5] & 0x03) * 10) + bbc_registers[4];
                    546:        day   = bbc_to_decimal(8, 7);
                    547:        month = bbc_to_decimal(10, 9);
                    548:        year  = bbc_to_decimal(12, 11) + 1900;
                    549: 
                    550:        range_test(hour, 0, 23);
                    551:        range_test(day, 1, 31);
                    552:        range_test(month, 1, 12);
                    553:        range_test(year, STARTOFTIME, 2000);
                    554: 
                    555:        tmp = 0;
                    556: 
                    557:        for (i = STARTOFTIME; i < year; i++)
                    558:                tmp += days_in_year(i);
                    559:        if (leapyear(year) && month > FEBRUARY)
                    560:                tmp++;
                    561: 
                    562:        for (i = 1; i < month; i++)
                    563:                tmp += days_in_month(i);
                    564:        
                    565:        tmp += (day - 1);
                    566:        tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec;
                    567: 
                    568:        *timbuf = tmp;
                    569:        return(1);
                    570: }
                    571: 
                    572: read_bbc()
                    573: {
                    574:        register int i, read_okay;
                    575: 
                    576:        read_okay = 0;
                    577:        while (!read_okay) {
                    578:                read_okay = 1;
                    579:                for (i = 0; i <= NUM_BBC_REGS; i++)
                    580:                        bbc_registers[i] = read_bbc_reg(i);
                    581:                for (i = 0; i <= NUM_BBC_REGS; i++)
                    582:                        if (bbc_registers[i] != read_bbc_reg(i))
                    583:                                read_okay = 0;
                    584:        }
                    585: }
                    586: 
                    587: u_char
                    588: read_bbc_reg(reg)
                    589:        int reg;
                    590: {
                    591:        u_char data = reg;
                    592: 
                    593:        if (bbcaddr) {
                    594:                send_hil_cmd(bbcaddr, BBC_SET_REG, &data, 1, NULL);
                    595:                send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &data);
                    596:        }
                    597:        return(data);
                    598: }
                    599: 
                    600: u_char
                    601: write_bbc_reg(reg, data)
                    602:        u_int data;
                    603: {
                    604:        u_char tmp;
                    605: 
                    606:        tmp = (u_char) ((data << HIL_SSHIFT) | reg);
                    607: 
                    608:        if (bbcaddr) {
                    609:                send_hil_cmd(bbcaddr, BBC_SET_REG, &tmp, 1, NULL);
                    610:                send_hil_cmd(bbcaddr, BBC_WRITE_REG, NULL, 0, NULL);
                    611:                send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &tmp);
                    612:        }
                    613:        return(tmp);
                    614: }      

unix.superglobalmegacorp.com

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