Annotation of Examples/DriverKit/QVision/QVision_reloc.tproj/QVisionSetMode.m, revision 1.1

1.1     ! root        1: /* Copyright (c) 1993 by NeXT Computer, Inc as an unpublished work.
        !             2:  * All rights reserved.
        !             3:  *
        !             4:  * QVisionSetMode.m -- Mode support for the QVision.
        !             5:  *
        !             6:  * NOTE: view this file with tabs set to 4 spaces
        !             7:  *
        !             8:  * Author:  Derek B Clegg      21 May 1993
        !             9:  *     Based on work by Joe Pasqua.
        !            10:  * Tue Aug 16 16:53:03 PDT 1994 James C. Lee
        !            11:  *     Add 3.3 bus support & 8-bit color support.
        !            12:  * Thu Sep 22 16:39:18 PDT 1994 James C. Lee
        !            13:  *     Use the new PCI API that returns IOReturn instead of BOOL.
        !            14:  */
        !            15: #import <string.h>
        !            16: #import <driverkit/generalFuncs.h>
        !            17: #import <driverkit/i386/ioPorts.h>
        !            18: #import <driverkit/i386/directDevice.h>
        !            19: #import <driverkit/i386/IOPCIDeviceDescription.h>
        !            20: #import <driverkit/i386/IOPCIDirectDevice.h>
        !            21: #import <string.h>
        !            22: #import <stdio.h>
        !            23: //#import <stdlib.h>
        !            24: 
        !            25: #import "QVision.h"
        !            26: 
        !            27: 
        !            28: /* private interfaces for SetMode category of QVision */
        !            29: /*@interface QVision (SetMode_Private)
        !            30: - (ConfigBusType) determineConfigBusType;
        !            31: - (QVAdapterType) determineVLCardType;
        !            32: - (QVAdapterType) determinePCICardType;
        !            33: - (QVAdapterType) determineEISACardType;
        !            34: @end
        !            35: */
        !            36: /* The `SetMode' category of `QVision'. */
        !            37: 
        !            38: @implementation QVision (SetMode)
        !            39: 
        !            40: - (ConfigBusType) determineConfigBusType
        !            41: {
        !            42:        const char              *busTypeName;
        !            43:        IOConfigTable   *configTable;
        !            44:        
        !            45:        configTable = [[self deviceDescription] configTable];
        !            46:        busTypeName = [configTable valueForStringKey:"Bus Type"];
        !            47:        if (strcmp(busTypeName, "PCI")==0) {
        !            48:                busType = BusPCI;
        !            49:        } else if (strcmp(busTypeName, "EISA")==0) {
        !            50:                busType = BusEISA;
        !            51:        } else {
        !            52:                /* default to ISA or VL */
        !            53:                busType = BusISAorVL;
        !            54:        }
        !            55:        return busType;
        !            56: }
        !            57: 
        !            58: /* assume dac type is determined already. This method is called by
        !            59:  * determineConfiguration only */
        !            60: - (QVAdapterType) determineVLCardType
        !            61: {
        !            62:        QVAdapterType adapterType;
        !            63:        
        !            64:        adapterType = UnknownAdapter;
        !            65:        switch (dac) {
        !            66:                case Bt484:
        !            67:                        adapterType = OrionAdapter;
        !            68:                        break;
        !            69:                case Bt485:
        !            70:                case Bt485A:
        !            71:                case ATT20C505:
        !            72:                        adapterType = Orion12Adapter;
        !            73:                        break;
        !            74:                default:
        !            75:                        break;
        !            76:        }
        !            77:        return adapterType;
        !            78: }
        !            79: 
        !            80: /* TODO: clean up error handling, i.e. return something meaningful rather
        !            81:  * than returning [super free]; */
        !            82: - (QVAdapterType) determinePCICardType
        !            83: {
        !            84:     unsigned int               physicalAddress;
        !            85:        int                                     numRanges;
        !            86:        IOPCIConfigSpace        configSpace;
        !            87:        unsigned char           devNum, funcNum, busNum;
        !            88:        unsigned long           vendorDeviceID;
        !            89:        unsigned short          vendorID, deviceID;
        !            90:        char                            idString[11];
        !            91:        id                                      deviceDescription;
        !            92:        QVAdapterType           adapterType;
        !            93:        IOConfigTable           *configTable;
        !            94:        IORange                         *oldRange, newRange[3];
        !            95: 
        !            96:        adapterType = UnknownAdapter;
        !            97:        if (![self isPCIPresent]) {
        !            98:                IOLog ("%s: No PCI card found.\n", [self name]);
        !            99:                return UnknownAdapter;
        !           100:        }
        !           101:        deviceDescription = [self deviceDescription]; 
        !           102:        [deviceDescription getPCIdevice:&devNum function:&funcNum bus:&busNum];
        !           103:        IOLog_dbg(("%s: PCI Dev:%d Func:%d Bus:%d\n", [self name], devNum,
        !           104:                funcNum, busNum));
        !           105:        [self getPCIConfigData:&vendorDeviceID atRegister:0x00];
        !           106:        vendorID = (unsigned short) vendorDeviceID;
        !           107:        deviceID = (unsigned short) (vendorDeviceID >> 16);
        !           108:        IOLog("%s: vendorID=%04x deviceID=%04x\n", [self name], vendorID,
        !           109:                deviceID);
        !           110: 
        !           111:        /* go through "Auto Detect IDs" and make sure we are okay */
        !           112:        configTable = [[self deviceDescription] configTable];
        !           113:        sprintf(idString, "%08lx", vendorDeviceID);
        !           114:        if (strstr([configTable valueForStringKey:"Auto Detect IDs"], idString)
        !           115:                == NULL)
        !           116:        {
        !           117:                IOLog("%s: VenderDeviceID %08lx not found in instance table.\n",
        !           118:                        [self name], vendorDeviceID);
        !           119:                return UnknownAdapter;
        !           120:        } else {
        !           121:                /* add more card types here if there are PCI cards other than 1280P
        !           122:                 * actually, if there are more than one PCI cards, we should determine
        !           123:                 * which PCI card here */
        !           124:                adapterType = QVision1280P;
        !           125:        }
        !           126: 
        !           127:        /* need to do set framebuffer address for PCI */
        !           128:        [self getPCIConfigSpace:&configSpace];
        !           129:        physicalAddress = configSpace.BaseAddress[0];
        !           130:        physicalAddress &= 0xfffffff0;  /* mask out lower 4 bits */
        !           131: 
        !           132:        if (physicalAddress) {
        !           133:                IOLog_dbg(("%s: try to set physical address to 0x%08x\n",
        !           134:                        [self name], physicalAddress));
        !           135:                /* PCI does report where the frame buffer address is */
        !           136:                oldRange = [deviceDescription memoryRangeList];
        !           137:                numRanges = [deviceDescription numMemoryRanges];
        !           138:                if (numRanges==3) {
        !           139:                        int     ret;
        !           140:                        int     i;
        !           141: 
        !           142:                        /* replace the address */
        !           143:                        for (i=0; i<numRanges; i++) {
        !           144:                                newRange[i] = oldRange[i];
        !           145:                        }
        !           146:                        newRange[0].start = physicalAddress;
        !           147:                        ret = [deviceDescription setMemoryRangeList:newRange num:3];
        !           148:                        if (ret) {
        !           149:                                /* can't set to new memory range */
        !           150:                                IOLog("%s: Can't set memory range, using default.\n",
        !           151:                                [self name]);
        !           152:                                for (i=0; i<numRanges; i++) {
        !           153:                                        newRange[i] = oldRange[i];
        !           154:                                }
        !           155:                                physicalAddress = newRange[0].start;
        !           156:                                ret = [deviceDescription setMemoryRangeList:newRange
        !           157:                                        num:3];
        !           158:                                if (ret) {
        !           159:                                        /* can't set to old range-->major problem! */
        !           160:                                        IOLog("%s: Can't set to default range either!\n",
        !           161:                                                [self name]);
        !           162:                                        return UnknownAdapter;
        !           163:                                }
        !           164:                        }
        !           165:                } else {
        !           166:                        IOLog("%s: Incorrect number of address ranges: %d.\n",
        !           167:                                [self name], numRanges);
        !           168:                        return UnknownAdapter;
        !           169:                }
        !           170:        } else {
        !           171:                IOLog_dbg(("%s: PCI doesn't tell us the physical address.\n",
        !           172:                        [self name]));
        !           173:                physicalAddress = [deviceDescription memoryRangeList] -> start;
        !           174:                configSpace.BaseAddress[0] = physicalAddress;
        !           175:                [self setPCIConfigSpace:&configSpace];
        !           176:        }
        !           177:        return adapterType;
        !           178: }
        !           179: 
        !           180: 
        !           181: /* helper method to -determineEISACardType */
        !           182: - (QVAdapterType)adapterTypeFromEISAID:(unsigned int)cardID
        !           183: {
        !           184:        QVAdapterType   adapterType;
        !           185: 
        !           186:        adapterType = UnknownAdapter;
        !           187:        IOLog_dbg(("%s: adapterTypeFromEISAID cardID=0x%08x\n", [self name],
        !           188:                cardID));
        !           189:        switch (cardID) {
        !           190:                case QVISION_EISA_ID:
        !           191:                        adapterType = QVisionAdapter;
        !           192:                        break;
        !           193:                case ORION_EISA_ID:
        !           194:                        adapterType = OrionAdapter;
        !           195:                        break;
        !           196:                case ORION12_EISA_ID:
        !           197:                        adapterType = Orion12Adapter;
        !           198:                        break;
        !           199:                case QVISION_ISA_ID:
        !           200:                case ORION_ISA_ID:
        !           201:                case ORION12_ISA_ID:
        !           202:                        IOLog("%s: Sorry, ISA cards are not supported (id=0x%08x).\n",
        !           203:                                [self name], cardID);
        !           204:                        break;
        !           205:                default:
        !           206:                        /* We found some other EISA card.  Just ignore it. */
        !           207:                        break;
        !           208:        }
        !           209:        return adapterType;
        !           210: }
        !           211: 
        !           212: /* helper method to -determineEISACardType */
        !           213: - (QVAdapterType)autoScanEISAForCardType
        !           214: {
        !           215:        int                             slot;
        !           216:        QVAdapterType   adapterType;
        !           217:        unsigned int    cardID;
        !           218: 
        !           219:        IOLog_dbg(("%s: doing auto-scan on EISA bus.\n", [self name]));
        !           220:                
        !           221:        adapterType = UnknownAdapter;
        !           222:        for (slot=1; slot<16; slot++) {
        !           223:                if ([self getEISAId:&cardID forSlot:slot]) {
        !           224:                        adapterType = [self adapterTypeFromEISAID:cardID];
        !           225:                }
        !           226:                if (adapterType != UnknownAdapter) {
        !           227:                        IOLog_dbg(("%s: found card in slot %d.\n", [self name], slot));
        !           228:                        return adapterType;
        !           229:                }
        !           230:        }
        !           231:        return UnknownAdapter;
        !           232: }
        !           233: 
        !           234: /* can't use atoi() nor sscanf() */
        !           235: - (int) getFirstNumber:(char *)s
        !           236: {
        !           237:        char    *cptr;
        !           238:        int             n;
        !           239:        
        !           240:        cptr = s;
        !           241:        n = -1;
        !           242:        while(*cptr && ((*cptr<'0') || (*cptr>'9'))) cptr++;
        !           243:        while(*cptr && ((*cptr>='0') && (*cptr<='9'))) {
        !           244:                if (n==-1) n = 0;
        !           245:                n = n*10 + (*cptr - '0');
        !           246:                cptr++;
        !           247:        }
        !           248:        return n;
        !           249: }
        !           250: 
        !           251: /* TODO: clean up error handling, i.e. return something meaningful rather
        !           252:  * than returning [super free]; */
        !           253: - (QVAdapterType) determineEISACardType
        !           254: {
        !           255:        QVAdapterType   adapterType;
        !           256:        int                             mySlot;
        !           257:        id                              deviceDescription;
        !           258:        const char              *slotValue;
        !           259:        IOConfigTable   *configTable;
        !           260:        unsigned int    cardID;
        !           261:        
        !           262:        adapterType = UnknownAdapter;
        !           263:        if (![self isEISAPresent]) {
        !           264:                IOLog ("%s: Not an EISA system.\n", [self name]);
        !           265:                return UnknownAdapter;
        !           266:        }
        !           267:        deviceDescription = [self deviceDescription]; 
        !           268:        configTable = [[self deviceDescription] configTable];
        !           269: 
        !           270:        /* see what slot we're supposed be in */
        !           271:        slotValue = [configTable valueForStringKey:"Location"];
        !           272:        if (strstr(slotValue, "Slot")) {
        !           273:                mySlot = [self getFirstNumber:(char *)slotValue];
        !           274:                IOLog_dbg(("%s: we should be in slot %d\n", [self name], mySlot));
        !           275:        } else {
        !           276:                /* instance table doesn't tell us what slot we're in */
        !           277:                return [self autoScanEISAForCardType];
        !           278:        }
        !           279:        
        !           280:        /* TODO: need to check for auto detect id's */
        !           281:        if (mySlot > 0) {
        !           282:                if ([self getEISAId:&cardID forSlot:mySlot]) {
        !           283:                        adapterType = [self adapterTypeFromEISAID:cardID];
        !           284:                } else {
        !           285:                        /* can't find card in the specified slot, do auto-scan */
        !           286:                        adapterType = [self autoScanEISAForCardType];
        !           287:                }
        !           288:        } else {
        !           289:                /* slot not specified, do auto-scan */
        !           290:                adapterType = [self autoScanEISAForCardType];
        !           291:        }
        !           292:        return adapterType;
        !           293: }
        !           294: 
        !           295: - (void)reportConfiguration
        !           296: {
        !           297:        const char *adapterString, *dacString;
        !           298: 
        !           299:        switch (adapter) {
        !           300:                case QVisionAdapter: adapterString = "QVision"; break;
        !           301:                case OrionAdapter: adapterString = "Orion"; break;
        !           302:                case Orion12Adapter: adapterString = "Orion12"; break;
        !           303:                case QVision1280P: adapterString = "QVision1280P"; break;
        !           304:                default: adapterString = "unknown"; break;
        !           305:        }
        !           306: 
        !           307:        switch (dac) {
        !           308:                case Bt484: dacString = "Brooktree 484"; break;
        !           309:                case Bt485: dacString = "Brooktree 485"; break;
        !           310:                case Bt485A: dacString = "Brooktree 485A"; break;
        !           311:                case ATT20C505: dacString = "AT&T 20C505"; break;
        !           312:                default: dacString = "unknown"; break;
        !           313:        }
        !           314: 
        !           315:        IOLog("%s: %s adapter; %s DAC.\n", [self name], adapterString, dacString);
        !           316: }
        !           317: 
        !           318: - determineConfiguration
        !           319: {
        !           320:        adapter = UnknownAdapter;
        !           321: 
        !           322:        [self determineConfigBusType];
        !           323:        [self determineDACType];
        !           324:        switch(busType) {
        !           325:                case BusISAorVL:
        !           326:                        adapter = [self determineVLCardType];
        !           327:                        break;
        !           328:                case BusPCI:
        !           329:                        adapter = [self determinePCICardType];
        !           330:                        break;
        !           331:                case BusEISA:
        !           332:                        adapter = [self determineEISACardType];
        !           333:                        break;
        !           334:        }
        !           335:        [self reportConfiguration];
        !           336:        if (adapter==UnknownAdapter || dac==UnknownDAC) return nil;
        !           337:        return self;
        !           338: }
        !           339: 
        !           340: - selectMode
        !           341: {
        !           342:        int k, mode;
        !           343:        const QVisionMode *qvMode;
        !           344:        BOOL validModes[QVisionModeTableCount];
        !           345: 
        !           346:        for (k = 0; k < QVisionModeTableCount; k++) {
        !           347:                qvMode = QVisionModeTable[k].parameters;
        !           348:                validModes[k] = (qvMode->adapter <= adapter);
        !           349:        }
        !           350: 
        !           351:        mode = [self selectMode:QVisionModeTable count:QVisionModeTableCount
        !           352:        valid:validModes];
        !           353: 
        !           354:        if (mode < 0) {
        !           355:                IOLog("%s: Sorry, cannot use requested display mode.\n", [self name]);
        !           356:                switch (adapter) {
        !           357:                        case Orion12Adapter:
        !           358:                                mode = DEFAULT_ORION12_MODE;
        !           359:                                break;
        !           360:                        case OrionAdapter:
        !           361:                                mode = DEFAULT_ORION_MODE;
        !           362:                                break;
        !           363:                        case QVision1280P:
        !           364:                                mode = DEFAULT_1280P_MODE;
        !           365:                                break;
        !           366:                        case QVisionAdapter:
        !           367:                        default:
        !           368:                                mode = DEFAULT_QVISION_MODE;
        !           369:                                break;
        !           370:                }
        !           371:        }
        !           372:        *[self displayInfo] = QVisionModeTable[mode];
        !           373:        return self;
        !           374: }
        !           375: 
        !           376: - initializeMode
        !           377: {
        !           378:        unsigned int i;
        !           379:        const QVisionMode *mode;
        !           380:        const IODisplayInfo *displayInfo;
        !           381: 
        !           382:        displayInfo = [self displayInfo];
        !           383:        mode = displayInfo->parameters;
        !           384: 
        !           385:        /* Turn off video while setting all of the registers. */
        !           386:        inb(VGA_INPUT_STATUS_1);
        !           387:        outb(VGA_ATTR_INDEX, 0x00);
        !           388:        
        !           389:        /* Set the sequencer registers. */
        !           390:        for (i = 0; i < VGA_SEQ_COUNT; i++) {
        !           391:        outb(VGA_SEQ_INDEX, i);
        !           392:        outb(VGA_SEQ_DATA, mode->vgaData.seqx[i]);
        !           393:        }
        !           394:        outb(VGA_SEQ_INDEX, 0x00);
        !           395:        outb(VGA_SEQ_DATA, 0x03);       /* Restart the sequencer. */
        !           396:        
        !           397:        /* Unlock extended graphics registers. */
        !           398:        outw(VGA_GRFX_INDEX, 0x050f);
        !           399: 
        !           400:        /* Unlock more extended registers. */
        !           401:        outb(VGA_GRFX_INDEX, 0x10);
        !           402:        outb(VGA_GRFX_DATA, 0x08);
        !           403: 
        !           404:        /* Set Advanced VGA mode (set bit 0 of Ctrl Reg 0). */
        !           405:        outb(VGA_GRFX_INDEX, 0x40);
        !           406:        outb(VGA_GRFX_DATA, 0x01);
        !           407:        
        !           408:        /* Fix sequencer pixel mask for 8 bits. */
        !           409:        outb(VGA_SEQ_INDEX, SEQ_PIXEL_WR_MSK);
        !           410:        outb(VGA_SEQ_DATA, 0xff);
        !           411:        
        !           412:        outb(CTRL_REG_1, mode->ctrlReg1);
        !           413:        if (mode->adapter >= OrionAdapter) {
        !           414:        /* Set access level & enable high address map. */
        !           415:        outb(QVGA_CTL_2, 0x14);
        !           416:        /* Select 2 Meg mode. */
        !           417:        outb(QVGA_CTL_3, 0x05);
        !           418:        }
        !           419:        
        !           420:        /* Set miscellaneous output register. */
        !           421:        outb(VGA_MISC_OUTPUT, mode->vgaData.miscOutput);
        !           422:        
        !           423:        [self programDAC];
        !           424: 
        !           425:        /* Load CRTC registers. */
        !           426:        outb(VGA_CRTC_INDEX, 0x11);             /* Unlock CRTC regs 0-7. */
        !           427:        outb(VGA_CRTC_DATA, 0x00);
        !           428:        for (i = 0; i < VGA_CRTC_COUNT; i++) {
        !           429:        outb(VGA_CRTC_INDEX, i);
        !           430:        outb(VGA_CRTC_DATA, mode->vgaData.crtc[i]);
        !           431:        }
        !           432: 
        !           433:        /* Load overflow registers. */
        !           434:        outb(VGA_GRFX_INDEX, 0x42);
        !           435:        outb(VGA_GRFX_DATA, mode->overflow1);
        !           436:        outb(VGA_GRFX_INDEX, 0x51);
        !           437:        outb(VGA_GRFX_DATA, mode->overflow2);
        !           438:        
        !           439:        /* Load attribute registers. */
        !           440: 
        !           441:        inb(VGA_INPUT_STATUS_1);        /* Reset latch. */
        !           442:        for (i = 0; i < VGA_ATTR_COUNT; i++) {
        !           443:        outb(VGA_ATTR_INDEX, i);
        !           444:        outb(VGA_ATTR_DATA, mode->vgaData.attr[i]);
        !           445:        }
        !           446:        
        !           447:        /* Load graphics registers. */
        !           448:        for (i = 0; i < VGA_GRFX_COUNT; i++) {
        !           449:        outb(VGA_GRFX_INDEX, i);
        !           450:        outb(VGA_GRFX_DATA, mode->vgaData.grfx[i]);
        !           451:        }
        !           452:        
        !           453:        [self setGammaTable];
        !           454:        
        !           455:        /* Re-enable video display. */
        !           456:        inb(VGA_INPUT_STATUS_1);
        !           457:        outb(VGA_ATTR_INDEX, 0x20);
        !           458: 
        !           459:        return self;
        !           460: }
        !           461: 
        !           462: - enableLinearFrameBuffer
        !           463: {
        !           464:        const IODisplayInfo *displayInfo;
        !           465:        unsigned char tmp;
        !           466:        
        !           467:        /* Override the high address map disable, thus allowing access to
        !           468:         * the high address map of the current board, even when the board
        !           469:         * is disabled. */
        !           470: 
        !           471:        outb(VGA_GRFX_INDEX, HI_ADDR_MAP+1);
        !           472:        tmp = inb(VGA_GRFX_DATA);
        !           473:        outb(VGA_GRFX_DATA, tmp | 0x80);
        !           474: 
        !           475:        /* Map VRAM.  Tell the adapter where to decode the framebuffer. */
        !           476: 
        !           477:        /* Set low 8 bits */
        !           478:        outb(VGA_GRFX_INDEX, HI_ADDR_MAP);
        !           479:        outb(VGA_GRFX_DATA, (videoRamAddress >> 20) & 0xFF);
        !           480: 
        !           481:        /* Set upper 4 bits */
        !           482:        outb(VGA_GRFX_INDEX, HI_ADDR_MAP + 1);
        !           483:        outb(VGA_GRFX_DATA, (videoRamAddress >> 28) & 0x0F);
        !           484: 
        !           485:        /* Leave them with a nice clear screen. */
        !           486: 
        !           487:        displayInfo = [self displayInfo];
        !           488:        memset(displayInfo->frameBuffer, 0, 
        !           489:           displayInfo->rowBytes * displayInfo->height);
        !           490: 
        !           491:        return self;
        !           492: }
        !           493: 
        !           494: - resetVGA
        !           495: {
        !           496:        const IODisplayInfo *displayInfo;
        !           497:        const QVisionMode *mode;
        !           498: 
        !           499:        displayInfo = [self displayInfo];
        !           500:        mode = displayInfo->parameters;
        !           501: 
        !           502:        /* Clear the QVision extended mode bit. This is bit 0 of CTRL_REG_1. */
        !           503: 
        !           504:        outb(CTRL_REG_1, inb(CTRL_REG_1) & 0xFE);
        !           505: 
        !           506:        if (mode != 0 && mode->adapter >= OrionAdapter) {
        !           507:        /* Select 1 meg mode. */
        !           508:        outb(QVGA_CTL_3, 0x00);
        !           509:        /* Reset access level & disable high address map. */
        !           510:        outb(QVGA_CTL_2, 0x00);
        !           511:        }
        !           512:        
        !           513:        /* Clear the extended 256 color bit. This is bit 0 of 3CF.40. */
        !           514:        outb(VGA_GRFX_INDEX, 0x40);
        !           515:        outb(VGA_GRFX_DATA, (inb(VGA_GRFX_DATA) & 0xFE));
        !           516:        
        !           517:        /* Clear the page registers - 3CF.45 and 3CF.46. */
        !           518:        outb(VGA_GRFX_INDEX, PAGE_REG_0);
        !           519:        outb(VGA_GRFX_DATA, 0x00);
        !           520:        outb(VGA_GRFX_INDEX, PAGE_REG_1);
        !           521:        outb(VGA_GRFX_DATA, 0x00);
        !           522:        
        !           523:        [self resetDAC];
        !           524: 
        !           525:        /* Clear the overflow registers. */
        !           526:        outb(VGA_GRFX_INDEX, 0x42);
        !           527:        outb(VGA_GRFX_DATA, 0x00);
        !           528:        outb(VGA_GRFX_INDEX, 0x51);
        !           529:        outb(VGA_GRFX_DATA, 0x00);
        !           530: 
        !           531:        VGASetMode(0x03);
        !           532: 
        !           533:        return self;
        !           534: }
        !           535: @end

unix.superglobalmegacorp.com

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