; ; File: SonoraDriver.a ; ; Contains: This file contains the video driver for use by the Macintosh ; OS for the Sonora hardware. ; ; Written by: Mike Puckett, December 3, 1991. ; ; Copyright: © 1991-1993 by Apple Computer, Inc. All rights reserved. ; ; Change History (most recent first): ; ; 10/6/93 RC Added Dark Star Support by adding the Control call SetSync and ; the Status call GetSync ; 09-23-93 jmp Made the SetDefaultMode call more Display Manager friendly. ; 08-06-93 jmp Updated the GetConnection call to support all three types of ; multiscan display codes. ; 08-03-93 jmp Began cleaning up the support for dynamically allocating RAM in ; PDM for video, as well as added initial support for the three ; new Apple multiscan displays. ; <16> 7/2/93 IH #1096920: Slot Manager not correctly updated in set resolution ; call. Added SUpdateSRT call. ; <15> 6/25/93 IH As per Mike's suggestion, make new Display Manager control and ; status calls clear only those fields that they use. ; <14> 6/22/93 IH Fix connection info status call. ; 6/1/93 IH Update mode switch calls to VDSWitchInfo record and add more ; support for mode timings. ; 5/28/93 IH Added VGA mode to MultiSync family. ; 04-07-93 jmp Added initial low-level support for the Display Manager. ; 04-01-93 jmp Fixed yet another bug in the GetEntries code. ; 04-01-93 jmp Beefed-up the SetDefaultMode call to support the (hopefully) ; temporary interface to the RAM allocation on boot for video in ; PDM. ; 3/9/93 jmp Fixed a couple of long-standing out-of-range bugs in ; Get/SetEntries, and now turn video blanking on when the video ; driver is closed. ; 1/12/93 SAM Disabled VRAM cache flushing at every VBL on PDMs. Copyback ; VRAM just doesn't look good. ; 11/6/92 jmp Added a little more PDM bring-up code. ; 11/5/92 SWC Changed VideoEqu.a->Video.a and ShutdownEqu.a->Shutdown.a. ; 11/2/92 kc Don't include SonicEqu.a. ; 10-21-92 jmp Added code to effectively flush the copyback framebuffer for ; PDM. ; 10-17-92 jmp Added temporary hack to support PDMÕs (broken) bring-up ; hardware. ; 10/6/92 GDW New location for ROMLink tool. ; ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ ; Pre-ROMLink comments begin here. ; ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ ; 09-29-92 jmp (jmp,H16) Fixed a problem in SetGamma where various randomly bad ; things could have occurred when going from a 3-to-1 or 1-to-3 ; channel gamma tables (not something thatÕs done very often, but ; still...). ; 09-03-92 jmp (jmp,H15) Corrected .s vs. non-.s branches and odd-alignment ; problems. ; 6/18/92 KW (jmp,H14) Updated the Rubik 512<->560 routine to accomodate the ; Omega-2 parameters. ; 6/4/92 KW (NJV,H13) Added new sndSonoraReverseDFAC equate in place of ; hard-coded value ; (jmp,H12) Fixed a problem where I wasnÕt always returning the ; correct frame buffer base address where I was supposed to. ; (NJV,H11) Temporarily using hard-coded $57 for DFAC Sound ; initialization to get rid of annoying playthrough. ; (NJV,H10) Fixed bug with DFAC setup for sound ; (jmp,H9) Added a small optimization to the interrupt handler. ; (JC,H8) Fix problem in Uncorrected Gamma in 16-bit mode. ; 5/2/92 kc Roll in Horror. Comments follow: ; 04/24/92 jmp Added constants to support for the Òswitch-on-the-flyÓ Rubik-512 ; to Rubik-560 and vice-versa call for DoubleExposure. ;
04/20/92 jmp In order to fully support family modes, changed the way I was ; dealing with the Rubik-560 mode in GrayScreen. ;
4/13/92 JC Replace references to SonoraAddr (it is going away) with ; references to VIA1Addr. ;

01/22/92 jmp Updated the original ÒNo ConnectÓ code to take full advantage of ; the newly-defined extended sense codes. Changed the name from ; ÒNoConnectÓ to ÒAltSense.Ó ;

01/11/92 jmp Eliminated a BoxFlag dependency. ;

12/19/91 jmp Added the initial support for Rubik-560 mode for Sonora. ; <1> 12/12/91 jmp first checked in STRING C PRINT OFF LOAD 'StandardEqu.d' INCLUDE 'IOPrimitiveEqu.a' INCLUDE 'HardwarePrivateEqu.a' INCLUDE 'EgretEqu.a' INCLUDE 'ROMEqu.a' INCLUDE 'Video.a' INCLUDE 'GestaltEqu.a' INCLUDE 'SlotMgrEqu.a' INCLUDE 'UniversalEqu.a' INCLUDE 'DepVideoEqu.a' PRINT ON SEG '_sSonoraDriver' BLANKS ON STRING ASIS MACHINE MC68020 ; This is device storage which is stored in the dCtlStorage field of the AuxDCE. SonoraVidPrivates RECORD 0 saveBaseAddr DS.L 1 ; the screen base address saveSQElPtr DS.L 1 ; the SQ element pointer (for _SIntRemove). saveGammaPtr DS.L 1 ; the pointer to the Gamma correction table saveGamDispPtr DS.L 1 ; the pointer to the Gamma block saveVidPtr Ds.l 1 ; the pointer to the vidParams block saveVDACBase DS.L 1 ; the base addr of the VDAC saveSonoraBase Ds.l 1 ; the base addr of Sonora GFlags DS.B 1 ; flags word (hi-order byte, actually) noVRAM Ds.b 1 ; the no real VRAM flag saveMode DS.W 1 ; the current mode setting saveMonID DS.W 1 ; monitor type ID saveSizeVRAM Ds.b 1 ; amount of vRAM/RAM SonoraVidPrivSize EQU * ENDR LSonoraDriver MAIN EXPORT ;------------------------------------------------------------------- ; Video Driver Header ;------------------------------------------------------------------- ; SonoraVidDrvr DC.W $4C00 ; ctl,status,needsLock DC.W 0,0,0 ; not an ornament ; Entry point offset table DC.W SonoraVidOpen-SonoraVidDrvr ; open routine DC.W SonoraVidDrvr-SonoraVidDrvr ; no prime in normal video drivers DC.W SonoraVidCtl-SonoraVidDrvr ; control DC.W SonoraVidStatus-SonoraVidDrvr ; status DC.W SonoraVidClose-SonoraVidDrvr ; close STRING Pascal SonoraVidTitle DC.B '.Display_Video_Apple_Sonora' ALIGN 2 ; make sure we're aligned DC.W CurSonoraDrvrVersion ; current version STRING ASIS ; ; SonoraCLUTTbl contains information required to write to the CLUT in the different screen depths. ; Each depth's information has three values. The first is the number of entries-1 in this depth ; for range checking. The second is the address of the first active CLUT position for that ; screen depth. The last number is the ÒskipÓ factor. This is required because, in 1-4bpp, the ; entries are distributed throughout the CLUT address space. As a result, we use sequential CLUT mode ; ONLY in 8/16bpp modes. The skip factor is the address difference between adjacent active positions ; in each mode. ; ; Generally, these rules are true for any particular depth: ; #entries = (2^^depth)-1 ; startposition = (256 / (2^^depth))-1 ; skipfactor = 256 / (2^^depth) SonoraCLUTTbl DC.B $01,$7F,$00,$80 ; for one-bit mode DC.B $03,$3F,$00,$40 ; for two-bit mode DC.B $0F,$0F,$00,$10 ; for four-bit mode DC.B $FF,$00,$00,$01 ; for eight-bit mode DC.B $1F,$00,$00,$01 ; for sixteen-bit mode SonoraCLUTRec RECORD 0 ; scRange DS.B 1 ; maximum index value in this depth scStart DS.B 1 ; lowest active CLUT address scSkip DS.W 1 ; skip value between active addresses SonoraCLUTSize Equ * ENDR ; ; These are the bit patterns for grays in each depth ; SonoraPats Dc.l OneBitGray,TwoBitGray,FourBitGray,EightBitGray,SixteenBitGray ; ; The following table is used by the SwitchMode call to determine two things. First, ; it must decide whether the requested mode to switch to is valid or not. If ; the requested mode is not in the SwitchTable, the call will fail. Also, even ; if the requested mode is in table, if the current bit depth is too large ; for the requested mode, SwitchMode must still fail. ; SwitchTable Dc.b sRsrc_Vid_Sonora_GSM, FifthVidMode Dc.b sRsrc_Vid_Sonora_MSB1, FifthVidMode Dc.b sRsrc_Vid_Sonora_MSB2, FourthVidMode Dc.w -1 ; ; The following tables are used to support the Get/SetAltSense codes. The first set of ; tables are pairs of codes and indices, terminated by a -1. Each of these tables ; is composed of a particular sense-code types (i.e., 3, 5, 6, and 7). The final ; table is also composed of pairs -- the first value is the sense-code type, and ; the corresponding second value is an offset to the sense-code index tables. ; Type_0_MonIDs Dc.b indexedSenseFP, indexedSenseFP Dc.b indexedSenseRubik, indexedSenseRubik Dc.b indexedSenseRGBFP, indexedSenseRGBFP Dc.b indexedSenseHR, indexedSenseHR Dc.b indexedNoConnect, indexedNoConnect Dc.w -1 Type_6_MonIDs Dc.b extendedMSB1, indexedSenseMSB1 Dc.b extendedMSB2, indexedSenseMSB2 Dc.b extendedMSB3, indexedSenseMSB3 Dc.w -1 Type_7_MonIDs Dc.b extendedSenseVGA, indexedSenseVGA Dc.b extendedSenseGF, indexedSenseGF Dc.b extendedNoConnect, indexedNoConnect Dc.w -1 MonIDsTbl Dc.w 0, Type_0_MonIDs-MonIDsTbl Dc.w 3, 0 Dc.w 5, 0 Dc.w 6, Type_6_MonIDs-MonIDsTbl Dc.w 7, Type_7_MonIDs-MonIDsTbl Dc.w -1 ; ; Unlike the SwitchTable above, the DepthTable tells SetDefaultMode what to write in the SP_Depth ; field of sPRAM. If the depth value is zero, SetDefaultMode is just supposed to leave ; the SP_Depth field alone. If the depth values is negative one, then SetDefaultMode is ; supposed to check to see if the current mode is valid or not. ; DepthTable Dc.b sRsrc_Vid_Sonora_FP, FirstVidMode Dc.b sRsrc_Vid_Sonora_FPc, FourthVidMode Dc.b sRsrc_Vid_Sonora_GS, FirstVidMode Dc.b sRsrc_Vid_Sonora_GSa, FourthVidMode Dc.b sRsrc_Vid_Sonora_GSb, FifthVidMode Dc.b sRsrc_Vid_Sonora_GSM, 0 Dc.b sRsrc_Vid_Sonora_RGBFP, FirstVidMode Dc.b sRsrc_Vid_Sonora_RGBFPc, FourthVidMode Dc.b sRsrc_Vid_Sonora_HR, FirstVidMode Dc.b sRsrc_Vid_Sonora_HRb, FourthVidMode Dc.b sRsrc_Vid_Sonora_HRc, FifthVidMode Dc.b sRsrc_Vid_Sonora_MSB1, 0 Dc.b sRsrc_Vid_Sonora_VGA, FirstVidMode Dc.b sRsrc_Vid_Sonora_VGAb, FourthVidMode Dc.b sRsrc_Vid_Sonora_VGAc, FifthVidMode Dc.b sRsrc_Vid_Sonora_GF, FirstVidMode Dc.b sRsrc_Vid_Sonora_GFb, FourthVidMode Dc.b sRsrc_Vid_Sonora_MSB2, -1 Dc.w -1 ********************************************************************** * * VidOpen allocates private storage for the device in the AuxDCE and locks * it down for perpetuity. Also, it installs the interrupt handler and enables * the (VBL) interrupts. * * Entry: A0 = param block pointer * A1 = AuxDCE pointer * * Locals: A3 = pointer to private storage * * A4/D2/D3 used as scratch * ********************************************************************** WITH VDPageInfo,SlotIntQElement,SonoraVidPrivates SonoraVidOpen ; ; Allocate private storage (since block is CLEAR, GFlags are zeroed) and get ; a pointer to it in A3 ; MOVEQ #SonoraVidPrivSize,D0 ; get size of parameters _ResrvMem ,SYS ; make room as low as possible MOVEQ #SonoraVidPrivSize,D0 ; get size of parameters _NewHandle ,SYS,CLEAR ; get some memory for private storage BNE @OpError1 ; => return an error in open MOVE.L A0,dCtlStorage(A1) ; save returned handle in AuxDCE _HLock ; and lock it down forever MOVE.L (A0),D0 ; get a pointer to it _StripAddress ; clean it up MOVE.L D0,A3 ; get pointer to privates in A3 ; ; Remember the VDAC, Sonora, and framebuffer base addresses since they're non-trivial to ; look up. ; WITH ProductInfo,DecoderInfo,VideoInfo MOVE.L UnivInfoPtr,A0 ; get a pointer to universal data ADDA.L DecoderInfoPtr(A0),A0 ; point to the base address table MOVE.L VDACAddr(A0),saveVDACBase(A3) ; save VDACÕs base address Move.l VIA1Addr(A0),saveSonoraBase(A3) ; save SonoraÕs base address (same as VIA1)

MOVE.L UnivInfoPtr,A0 ADDA.L VideoInfoPtr(A0),A0 ; point to the VideoInfo record MOVE.L VRAMLogAddr32(A0),saveBaseAddr(A3) ; save base address too ENDWITH ; ; Get and install the interrupt handler. Call the EnableVGuts utility code to do ; this. This utility also starts the interrupts going. If there is an error ; condition, EnableVGuts returns with the Z-bit cleared. MOVEQ #sqHDSize,D0 ; allocate a slot queue element _NewPtr ,SYS,CLEAR ; get it from system heap cleared BNE @OpError2 ; if not allocated, return bad MOVE.L A0,saveSQElPtr(A3) ; save the SQ element pointer. ; If the video driver was closed, letÕs ensure that video is unblanked before we ; re-enable interrupts. ; Movea.l saveSonoraBase(A3),A0 ; Get the Sonora base address. Adda.l #SonoraVdCtlBase,A0 ; Point to the video control register space. Bclr #SonoraVidBlnkBit,SonoraVdModeReg(A0) ; Turn off video blanking. BSR SonoraEnableVGuts ; do it BNE @OpError2 ; ; ; Load the default gamma table from the slot resource list. ; WITH SpBlock SUBA #spBlockSize,SP ; make a slot parameter block MOVE.L SP,A0 ; get pointer to block in A0 MOVE.B dCtlSlot(A1),spSlot(A0) ; copy the slot number MOVE.B dCtlSlotId(A1),spID(A0) ; copy the spID of the video sRsrc CLR.B spExtDev(A0) ; _sRsrcInfo ; get the spsPointer BNE @OpError3 ; if failed, then quit. MOVE.B #sGammaDir,spID(A0) ; look for the gamma directory _sFindStruct ; get that baby BNE.S @DoLinear ; if failed, then do linear MOVE.B #128,spID(A0) ; get the default gamma table, (always 128) _sGetBlock ; we can use this since we want it on the sys heap BNE.S @DoLinear ; if failed, then do linear ; Skip over gamma header. MOVE.L spResult(A0),A0 ; point to head of the block MOVE.L A0,saveGamDispPtr(A3) ; save the ptr to the gamma block ADDA #2,A0 ; skip resID @Name TST.B (A0)+ ; skip over gamma name BNE.S @Name ; MOVE.L A0,D0 ; get in d-reg ADDQ #1,D0 ; word align pointer BCLR #0,D0 ; round it off MOVE.L D0,saveGammaPtr(A3) ; put it in private storage Bra.s @VidParams ; Jump around linear code. ; ; Build a linear default gamma table if necessary. ; @DoLinear Moveq #gFormulaData,D0 ; Get gamma table header size. Add #256,D0 ; Add in one-byte per entry. _NewPtr ,SYS,CLEAR ; Clear it. Bne @OpError3 ; If failed, quit. Move.l A0,saveGamDispPtr(A3) ; Save head of gamma table for disposal. Move.l A0,saveGammaPtr(A3) ; Head and top are same here. Move.w #drHwSonora,gType(A0) ; Set up gType. Move.w #1,gChanCnt(A0) ; Set up gChanCnt. Move.w #256,gDataCnt(A0) ; Set up gDataCnt. Move.w #8,gDataWidth(A0) ; Set up gDataWidth. Adda #gFormulaData+256,A0 ; Point to end of data table. Move.w #255,D0 ; Set up loop counter. @Loop Move.b D0,-(A0) ; Write out value. Dbra D0,@Loop ; Loop. ; ; Get a pointer to the video hardware setup parameter block. Use this functional spID's spsPointer ; found above in the gamma section. ; @VidParams Move.l Sp,A0 ; Point to the spBlock on the stack. Clr.w spID(A0) ; Start looking at spID 0, no external devices. Clr.b spTBMask(A0) ; Only look for the board sRsrc. Move.w #catBoard,spCategory(A0) ; Look for: catBoard, Move.w #typBoard,spCType(A0) ; typBoard, Clr.w spDrvrSW(A0) ; 0, Clr.w spDrvrHW(A0) ; 0. Clr.l spParamData(A0) ; (The board sRsrc must be enabled.) Bset #foneslot,spParamData+3(A0) ; Limit search to slot 0. _GetTypeSRsrc ; Get the spsPointer. Bne @OpError4 ; If failed, quit. MOVE.B #sVidParmDir,spID(A0) ; look for the video parameters dir _sFindStruct ; Try to load it. Bne.s @OpError4 ; If failed, quit. MOVE.B dCtlSlotId(A1),spID(A0) ; look in the directory for this config's parameters _sGetBlock ; Try to load it. Bne.s @OpError4 ; If failed, quit. MOVE.L spResult(A0),saveVidPtr(A3) ; get pointer to it ; ; At PrimaryInit time, we used the sense lines to determine the type of attached display. For extended ; sense displays, we just mapped them to the end of indexed-sense displays. Since the gamma-correction ; code uses the monitor ID to determine if the passed-in table is applicable, we need to know the ÒrealÓ ; monitor ID. At PrimaryInit time, we store the real monitor ID in slot pRAM. So, we extract that ; information out here. Also, it should be noted that it would actually be inappropriate for us ; to re-read the sense-lines now, in that someone could potentially change/unplug the attached ; display between PrimaryInit and VidOpen, and that would cause us all sorts of havoc. ; With SP_Params Move.l Sp,A0 ; Point to spBlock on the stack. Move.b dCtlSlot(A1),spSlot(A0) ; Put slot into spBlock. Suba #sizeSPRamRec,Sp ; Allocate an SPRam block on the stack. Move.l Sp,spResult(A0) ; Point to it. _SReadPRAMRec ; Read Slot PRam. Bne.s @OpError5 ; If failed quit. Moveq #0,D2 ; Clear D2.w. Move.b SP_MonID(Sp),D2 ; Get the monID (itÕs byte sized). Move.b SP_Flags(Sp),D1 ; Get the flags. Adda #sizeSPRamRec+spBlockSize,Sp ; Clean up the stack. EndWith ; ; Do a little bookkeepingÉ ; Move.w D2,saveMonID(A3) ; Save the monID for later. Btst #spNoVRAM,D1 ; Test for real VRAM or not. Sne noVRAM(A3) ; Remeber it for later. Bfextu D1{spVRamBits:numSPVRamBits},D1 ; Extract the vRAM/RAM size from the flags byteÉ Move.b D1,saveSizeVRAM(A3) ; É and save it for later. ; ; Set GFlags to reflect monochrome-only displays. ; Cmp.w #indexedSenseFP,saveMonID(A3) ; If this is a Mono-Only Full Page, Beq.s @SetMonoFlags ; then say so. Bra.s @ChkRubik560 ; Otherwise, skip. @SetMonoFlags Bset #IsMono,GFlags(A3) ; Turn on the IsMono and Bset #GrayFlag,GFlags(A3) ; GrayFlag flags. ; ; Set GFlags to reflect whether we started up in Rubik-560 mode or not. ; @ChkRubik560 Cmp.b #sRsrc_Vid_Sonora_GS560a,dCtlSlotId(A1) ; If weÕre in Rubik-560a mode, Beq.s @SetRubik560 ; then say so. Cmp.b #sRsrc_Vid_Sonora_GS560b,dCtlSlotId(A1) ; If weÕre not in Rubik-560b mode, Bne.s @AllDone ; then just go on. @SetRubik560 Bset #InRubik560Mode,GFlags(A3) ; Remember that we started up in Rubik-560 mode. ; ; All done! ; @AllDone MOVEQ #noErr,D0 ; no error @EndOpen RTS ; return @OpError5 Adda #sizeSPRamRec,Sp ; Release the SPRam block. @OpError4 Move.l saveGamDispPtr(A3),A0 ; Set up to dispose of gamma table. _DisposPtr ; Dispose it. @OpError3 ADDA #spBlockSize,SP ; release the spBlock @OpError2 MOVE.L dCtlStorage(A1),A0 ; get the private storage back _DisposHandle ; release the driver private storage @OpError1 MOVE.L #OpenErr,D0 ; say can't open driver BRA.S @EndOpen ENDWITH ********************************************************************** * * Video Driver Control Call Handler. There are 11 standard calls: * * ($00) Reset (VAR mode, page: INTEGER; VAR BaseAddr: Ptr); * ($01) KillIO * ($02) SetMode(mode, page: INTEGER; VAR BaseAddr: Ptr); * ($03) SetEntries (Table: Ptr; Start,Count : integer ); * ($04) SetGamma (Table : Ptr ); * ($05) GrayPage (page); * ($06) SetGray (csMode = 0 for color, 1 for gray) * ($07) SetInterrupt (csMode = 0 for enable, non-zero for disable); * ($08) DirectSetEntries (Table: Ptr; Start,Count : integer ); * ($09) SetDefaultMode (csMode = mode to set); * ($0A) SwitchMode(csMode, csData, csPage, csBaseAddr); * * The following calls are Sonora-specific: * * ($81) SetRubik560Mode (csMode = 0 for enable, non-zero for disable); * ($83) SetAltSense(csMode = byte 0 is sense code, byte 1 is type); * * Entry: A0 = param block pointer * A1 = AuxDCE pointer * Uses: A2 = cs parameters (ie. A2 <- csParam(A0)) (must be preserved) * A3 = ptr to our privates/scrarch (doesnÕt need to be preserved) * A4 = scratch (must be preserved) * D0-D3 = scratch (don't need to be preserved) * * Exit: D0 = error code * ********************************************************************** ; ; Decode the callÉ ; SonoraVidCtl MOVEM.L A0/A1,-(SP) ; Save exit registers. MOVE.L csParam(A0),A2 ; A2 <- Ptr to control parameters MOVE.L dCtlStorage(A1),A3 ; A3 <- Ptr to private storage MOVE.L (A3),D0 ; _StripAddress ; MOVE.L D0,A3 ; MOVE.W csCode(A0),D0 ; get routine selector Cmp.w #cscRubik560,D0 ; If we got the Rubik560 mode call, Beq SonoraSetRubik560Mode ; hop to it. Cmp.w #cscAltSense,D0 ; If we got the alternate senseID call, Beq SonoraSetAltSense ; hop to it. CMP.W #$0B,D0 ; IF csCode NOT IN [0..$0A] THEN ; BHI.S SonoraCtlBad ; Error, csCode out of bounds. MOVE.W SonoraCtlJumpTbl(PC,D0.W*2),D0 ; Get the relative offset to the routine. JMP SonoraCtlJumpTbl(PC,D0.W) ; GOTO the proper routine. SonoraCtlJumpTbl DC.W SonoraVidReset-SonoraCtlJumpTbl ; $00 => VidReset DC.W SonoraCtlGood-SonoraCtlJumpTbl ; $01 => CtlGood (no async routines here) DC.W SonoraSetVidMode-SonoraCtlJumpTbl ; $02 => SetVidMode DC.W SonoraSetEntries-SonoraCtlJumpTbl ; $03 => SetEntries DC.W SonoraSetGamma-SonoraCtlJumpTbl ; $04 => SetGamma DC.W SonoraGrayPage-SonoraCtlJumpTbl ; $05 => GrayPage DC.W SonoraSetGray-SonoraCtlJumpTbl ; $06 => SetGray DC.W SonoraSetInterrupt-SonoraCtlJumpTbl ; $07 => SetInterrupt DC.W SonoraDirectSetEntries-SonoraCtlJumpTbl ; $08 => DirectSetEntries DC.W SonoraSetDefaultMode-SonoraCtlJumpTbl ; $09 => SetDefaultMode DC.W SonoraSwitchMode-SonoraCtlJumpTbl ; $0A => SwitchMode DC.W SonoraSetSync-SonoraCtlJumpTbl ; $0B => SetSync ; SonoraCtlBad MOVEQ #controlErr,D0 ; else say we don't do this one BRA.S SonoraCtlDone ; and return SonoraCtlGood MOVEQ #noErr,D0 ; return no error SonoraCtlDone MOVEM.L (SP)+,A0/A1 ; Restore Exit registers. BRA SonoraExitDrvr SonoraVidReset ;--------------------------------------------------------------------- ; ; Reset the card to its default ; ;--------------------------------------------------------------------- WITH SonoraVidPrivates MOVE #FirstVidMode,csMode(A2) ; return default mode MOVE #FirstVidMode,saveMode(A3) ; remember FirstVidMode as the requested mode MOVEQ #0,D1 ; get default depth in D1 (#firstVidMode-#firstVidMode) MOVEQ #0,D0 ; get page in D0 MOVE D0,csPage(A2) ; return the page BSR SonoraSetDepth ; set the depth from D1 BCLR #IsDirect,GFlags(A3) ; turn off direct mode bit Move.l saveBaseAddr(A3),csBaseAddr(A2) ; return the base address BSR SonoraGrayScreen ; paint the screen gray BRA.S SonoraCtlGood ; => no error ENDWITH SonoraSetVidMode ;--------------------------------------------------------------------- ; ; Set the card to the specified mode. Only page zero is possible, ; so we need to check that the request was OK. ; ; If the card is already set to the specified mode, then do nothing. ; ;--------------------------------------------------------------------- WITH SonoraVidPrivates MOVE.W csMode(A2),D1 ; D1 = mode BSR SonoraChkMode ; check mode and convert BNE.S SonoraCtlBad ; => not a valid mode TST.W csPage(A2) ; only page zero is valid BNE.S SonoraCtlBad ; => not a valid page ; Only set if mode has changed. SonoraSetVidModeGuts MOVE.W csMode(A2),D2 ; get the mode spID (D1 has the zero-based mode) CMP saveMode(A3),D2 ; has the mode changed? BEQ @ModeOK1 ; if not, then skip graying ; Remember the newly requested mode. MOVE.W D2,saveMode(A3) ; remember requested mode ; Set the entire color table to gray before switching to avoid screen anomalies. Movem.l A4-A6,-(Sp) ; Save gamma-table registers. Move.l saveGammaPtr(A3),A0 ; Get pointer to gamma data structure. Lea gFormulaData(A0),A4 ; Point to first gamma table. Adda.w gFormulaSize(A0),A4 ; Move.l A4,A5 ; Point to green data (assuming gChanCnt = 1). Move.l A4,A6 ; Point to red data (assuming gChanCnt = 1). Cmp.w #1,gChanCnt(A0) ; If thereÕs only one table, Beq.s @OnlyOneTable ; then weÕre set. Move.w gDataWidth(A0),D2 ; Get width of each entry (in bits). Move.w gDataCnt(A0),D0 ; Get # of entries in table. Addq #7,D2 ; Round to nearest byte. Lsr.w #3,D2 ; Get bytes per entry. Mulu D2,D0 ; Get size of table in bytes. Adda.w D0,A5 ; Calc base of green (red base + D0). Adda.w D0,A6 ; Calc baseÉ Adda.w D0,A6 ; Éof blue (red base + D0 + D0). @OnlyOneTable Move.w gDataCnt(A0),D3 ; Save number of gamma entries. MOVE.L saveVDACBase(A3),A0 ; get the VDAC base addr ADDA #ArielDataReg,A0 ; point to data register CLR.B ArielAddrReg-ArielDataReg(A0) ; start at the beginning of CLUT MOVE.W SR,-(SP) ; preserve the status register BSR SonoraWaitVSync ; wait for next blanking period (preserves A0) ; Write out gamma-corrected true-gray CLUTÉ ; Move.w D3,D0 ; Init loop counter. Subq #1,D0 ; Zero base it. Move.w GFlags(A3),D2 ; Get the GFlags into a convenient register. Lsr.w #1,D3 ; Get midpoint of table(s). @Repeat Btst #IsMono,D2 ; If this is not a mono-only display Beq.s @DoRGB ; then do the standard RGB stuff. Clr.b (A0) ; Otherwise, just write black out Clr.b (A0) ; to the red & green channels. Bra.s @DoMono ; @DoRGB Move.b (A4,D3),(A0) ; Write: red, Move.b (A5,D3),(A0) ; green, @DoMono Move.b (A6,D3),(A0) ; blue. Dbra D0,@Repeat MOVE (SP)+,SR ; restore the status reg Movem.l (Sp)+,A4-A6 ; Restore gamma-table registers. BSR SonoraSetDepth ; set the depth from D1 ; Finish up the bookkeeping. CMP.W #FifthVidMode,saveMode(A3) ; was it a direct mode? BLT.S @BitOff ; no, so turn flag off BSET #IsDirect,GFlags(A3) ; turn on bit BRA.S @ModeOK1 ; @BitOff BCLR #IsDirect,GFlags(A3) ; turn off bit @ModeOK1 Move.l saveBaseAddr(A3),csBaseAddr(A2) ; return the base addr BRA SonoraCtlGood ; return no error ENDWITH SonoraSetEntries ;--------------------------------------------------------------------- ; ; Input : ; csParam -> datablock ; datablock = csTable -> table of colorSpecs (not colortable) ; csStart -> where to start setting, or -1 ; csCount -> # of entries to change ; ; This call has two modes. In SEQUENCE mode, csCount entries are changed ; in the CLUT, starting at csStart. In INDEX mode, csCount entries are ; installed into the CLUT at the positions specified by their .value fields. ; This mode is selected by passing csStart = -1. In both cases, entries are ; range-checked to the dynamic range of the video mode (bits/pixel). ; ;--------------------------------------------------------------------- ; ; Set the CLUT ; A0 = Ptr to the table ; A1 = Ptr to AuxDCE ; A2 = Ptr to cs parameter record ; A3 = Ptr to private data, later to CLUT constants table ; A4 = Ptr to gamma red table ; A5 = Ptr to gamma green table ; A6 = Ptr to gamma blue table ; ; D0-D3 = Scratch ; D4 = Size of stack color table buffer ; D5 = GFlags word ; D6 = Index range [0..n] ; D7 = gamma channel size in bits ; ;--------------------------------------------------------------------- ; Initialize loop. WITH SonoraVidPrivates,SonoraCLUTRec BTST #IsDirect,GFlags(A3) ; are we in a direct mode? BNE SonoraCtlBad ; error if so SonoraSEGuts TST.L csTable(A2) ; Check for a nil pointer BEQ SonoraCtlBad ; MOVEM.L A1/A4-A6/D4-D7,-(SP) ; save registers for gamma MOVE.W GFlags(A3),D5 ; get GFlags word in D5 CMP.W #indexEntries,csStart(A2) ; was it indexed mode? BEQ.S @SkipSeq ; if so, then leave bit off (it's never turned on in GFlags) Bset #PsuedoIndex,D5 ; turn on the bit that denotes a seq write that was xlated to indexed Cmp.w #FourthVidMode,saveMode(A3) ; If itÕs not 8bbp or 16bpp, Blt.s @SkipSeq ; need to use ÒindexedÓ. BSET #UseSeq,D5 ; otherwise, turn on sequential mode bit @SkipSeq MOVE.L saveGammaPtr(A3),A0 ; get pointer to gamma data structure MOVE.W gFormulaSize(A0),D0 ; get the size of formula data LEA gFormulaData(A0,D0),A4 ; red correction table starts here MOVE.L A4,A5 ; get default pointer to green data MOVE.L A4,A6 ; get default pointer to blue data MOVE gDataWidth(A0),D7 ; get width of each entry in bits CMP #1,gChanCnt(A0) ; if only only one table, we're set BEQ.S @OneTbl ; => just one table MOVE gDataCnt(A0),D0 ; get # entries in table MOVE D7,D1 ; copy it to goof around ADDQ #7,D1 ; round to nearest byte LSR #3,D1 ; get bytes per entry MULU D1,D0 ; get size of table in bytes ADDA D0,A5 ; calc base of green ADDA D0,A6 ; calc base of blue ADDA D0,A6 ; calc base of blue @OneTbl ; ; Get the maximum number of entries, zero based from a convenient table. ; MOVE.W saveMode(A3),D1 ; get the current video mode SUB.W #FirstVidMode,D1 ; convert to a index Moveq #0,D4 ; clear all of D4 for .w compare. Lea SonoraCLUTTbl,A0 ; Point to the table of CLUT data. Lea (A0,D1*SonoraCLUTSize),A0 ; Point to the right entry. Move.b scRange(A0),D4 ; Get the range. ; ; Allocate a temporary color table on the stack. We'll pre-process all the entries that will ; change here so we can hit the hardware as quickly as possible. ; Move.w csCount(A2),D3 ; Get the number of entries to change, Bmi SonoraSEBadExit ; and hike if itÕs out of range. Cmp.w D4,D3 ; If D3-D4 > 0 (count - range > 0), Bhi SonoraSEBadExit ; then hike. Tst.w csStart(A2) ; If weÕre doing indexed entries (-1), Bmi.s @skipStartChk ; then just go on. Add.w csStart(A2),D3 ; Adjust count for starting position. Cmp.w D4,D3 ; If D3-D4 > 0 (count - range > 0), Bhi SonoraSEBadExit ; then hike. Move.w csCount(A2),D3 ; Otherwise, re-get the count. @skipStartChk Move.w D3,D4 ; Make a copy of the table size (zero-based). Addq #1,D4 ; Make it a counting number. Btst #UseSeq,D5 ; If weÕre not in sequential mode, Beq.s @IsIndex ; then do the indexed stuff. Mulu #3,D4 ; Make room for just R,G,B in sequential mode. Bra.s @AllocIt ; And continue. @IsIndex Asl.w #2,D4 ; Make room for i,R,G,B in indexed mode. @AllocIt Sub.w D4,Sp ; Allocate the table on the stack. ; ; Construct the stack version of the color table. It looks like a color table, but each of the ; components is only eight bits (rather than 16). ; Move.l A3,D6 ; WeÕre about to torch A3, so save it for later. Move.l A0,A3 ; Save the CLUT table entry pointer. MOVE.L SP,A0 ; copy the stack buffer pointer MOVE.L csTable(A2),A1 ; get colorSpec pointer into A1 ; Death! Totally out of registers in this routine, so I'm using the top half of D4 (the temp buffer ; size) as the sequence counter used in most video modes to translate sequential requests into ; the indexed write that the hardware needs. SWAP D4 ; flip the buffer size to the top half MOVE.W csStart(A2),D4 ; pick up the sequential start position. It might ; be -1 on a true indexed write, but we won't ; use it below if it is. ; ; Write the index if in indexed mode. If in sequential (8/16) mode, blow it off completely, ; since it won't be needed. @SetupLoop MOVE.W (A1)+,D0 ; get index BTST #UseSeq,D5 ; is it sequence mode? BNE.S @SLSeq ; yup, so go there Btst #PsuedoIndex,D5 ; If we are doing an ÒindexedÓ mode, Beq.s @IndexPresent ; then go there now. ; This case is a sequential request in a screen depth that does not allow sequential CLUT writes ; (any non-8/16 bit mode). In this case, we substitute the sequence counter for D0 on each ; entry. Move.w D4,D0 ; Copy the sequence counter to D0. Addq #1,D4 ; Increment it. @IndexPresent Mulu.w scSkip(A3),D0 ; Calculate the new position at this depth. Add.b scStart(A3),D0 ; Add in the first entry offset. Move.b D0,(A0)+ ; Write out this index. @SLSeq MOVE.W (A1)+,D0 ; get red MOVE.W (A1)+,D1 ; get green MOVE.W (A1)+,D2 ; get blue TST D5 ; test hi bit of the flags BPL.S @NoGray ; if not set, don't luminence map BTST #IsDirect,D5 ; test for direct mode as well BNE.S @NoGray ; don't allow luminence mapping in direct mode ; We're luminence mapping here. MULU #$4CCC,D0 ; multiply by red weight (0.30) MULU #$970A,D1 ; multiply by green weight (0.59) MULU #$1C29,D2 ; multiply by blue weight (0.11) ADD.L D1,D0 ; sum red and green ADD.L D2,D0 ; blue also BFEXTU D0{0:D7},D0 ; get gChanWidth bits for gamma table lookup MOVE.W D0,D1 ; copy into green register MOVE.W D0,D2 ; copy into blue register BRA.S @WriteSP ; go on and write it in the stack buffer @NoGray BFEXTU D0{16:D7},D0 ; get gChanWidth bits of red BFEXTU D1{16:D7},D1 ; get gChanWidth bits of green BFEXTU D2{16:D7},D2 ; get gChanWidth bits of blue @WriteSP BTST #IsMono,D5 ; if monochrome display, write black to red & green BEQ.S @Brighter ; if not, then set all three channels CLR.B (A0)+ ; write black for red CLR.B (A0)+ ; and green BRA.S @Looper ; write out normal blue @Brighter MOVE.B (A4,D0),(A0)+ ; write gamma corrected red MOVE.B (A5,D1),(A0)+ ; write gamma corrected green @Looper MOVE.B (A6,D2),(A0)+ ; write gamma corrected blue DBRA D3,@SetupLoop ; and loop for each entry Swap D4 ; Put the temp buffer size back in the lo-half. ; ; OK, the stack table is set up. Now let's load the hardware. ; Move.l D6,A3 ; Get our privates pointer back into A3 (for WaitVSync). Move.w Sr,-(SP) ; Preserve the status register. BSR SonoraWaitVSync ; Wait for next blanking period (preserves A0/D0). MOVE.W csCount(A2),D3 ; get the count again MOVE.L saveVDACBase(A3),A3 ; get the VDAC base LEA ArielDataReg(A3),A3 ; point to VDAC data register LEA 2(SP),A0 ; point to the stack buffer again BTST #UseSeq,D5 ; is it sequence mode? BNE.S SonoraSeqWrite ; yup, sequence mode, so go there ; ; Here's the loop that actually writes to the hardware when in indexed mode. ; SonoraIndexWrite MOVE.B (A0)+,ArielAddrReg-ArielDataReg(A3) ; write the index value to the CLUT address MOVE.B (A0)+,(A3) ; write red MOVE.B (A0)+,(A3) ; write green MOVE.B (A0)+,(A3) ; write blue DBRA D3,SonoraIndexWrite ; and loop BRA.S SonoraSEDone ; ; ; Write the translated starting position for sequence mode. ; SonoraSeqWrite MOVE.W csStart(A2),D0 ; get sequence start address MOVE.B D0,ArielAddrReg-ArielDataReg(A3) ; write the sequence start position ; ; Here's the loop that actually writes to the hardware when in sequence mode. ; @SeqLoop MOVE.B (A0)+,(A3) ; write red MOVE.B (A0)+,(A3) ; write green MOVE.B (A0)+,(A3) ; write blue DBRA D3,@SeqLoop ; and loop ; ; Clean up and go home. ; SonoraSEDone MOVE.W (SP)+,SR ; restore status register ADD.W D4,SP ; release stack buffer MOVEM.L (SP)+,A1/A4-A6/D4-D7 ; restore registers BRA SonoraCtlGood ; return O-Tay! SonoraSEBadExit MOVEM.L (SP)+,A1/A4-A6/D4-D7 ; restore registers BRA SonoraCtlBad ; return an error code ENDWITH SonoraSetGamma ;--------------------------------------------------------------------- ; ; Set the gamma table. This call copies the supplied gTable so the ; caller does not have to put the source on the system heap. It ; tests if the gamma table is exactly a match to the currently ; connected monitor, or always allows it if the monitor number in ; the FormulaData is -1. If supplied gamma table ptr is NIL, then ; it loads a linear gamma table into the private table ; ; A0 = Ptr to private storage ; A1 = Ptr to AuxDCE ; A2 = Ptr to cs parameter record ; ;--------------------------------------------------------------------- WITH SonoraVidPrivates ; Get new gamma table and check that we know how to handle it. MOVE.L csGTable(A2),D0 ; test for a NIL pointer BEQ @LinearTab ; if so, then set up a linear gamma table MOVE.L D0,A2 ; get pointer to new gamma table TST.W gVersion(A2) ; version = 0? BNE SonoraCtlBad ; => no, return error Tst.w gType(A2) ; Test the hardwareID. Beq.s @ChangeTable ; If 0, then accept a TFB-style gamma table. CMP.W #drHwSonora,gType(A2) ; type = Sonora? BNE SonoraCtlBad ; => no, return error TST.W gFormulaSize(A2) ; if gType=Sonora, then check for monID in gFormulaData BEQ.S @ChangeTable ; if zero, then generic, so continue MOVE.W gFormulaData(A2),D0 ; get the monitor ID this table was intended for CMP.W saveMonID(A3),D0 ; is this the monitor? BEQ.S @ChangeTable ; yes, so do it ADDQ #1,D0 ; was it -1? BNE SonoraCtlBad ; nope, so must be wrong monitor ; If new table is a different size, reallocate memory. @ChangeTable MOVE.L saveGammaPtr(A3),A0 ; get current gamma in A0 MOVE gFormulaSize(A2),D0 ; get size of formula in new CMP gFormulaSize(A0),D0 ; same as current gamma table BNE.S @GetNew ; =>no, resize pointer MOVE gChanCnt(A2),D0 ; get number of tables in new CMP gChanCnt(A0),D0 ; same as current gamma table? BEQ.S @SizeOK ; => yes, data size ok BGT.S @GetNew ; => new one is bigger, save old one @NewSize Move.l saveGamDispPtr(A3),A0 ; if new one is smaller, _DisposPtr ; dispose old one CLR.L saveGamDispPtr(A3) ; flag it's been disposed @GetNew Moveq #0,D0 ; (_NewPtr takes a long, so clear D0 for later.) MOVE gDataCnt(A2),D0 ; get number of entries MULU gChanCnt(A2),D0 ; multiply by number of tables ADD gFormulaSize(A2),D0 ; add size of formula data ADD #gFormulaData,D0 ; add gamma table header size _NewPtr ,Sys ; and allocate a new pointer BNE SonoraCtlBad ; => unable to allocate storage MOVE.L saveGamDispPtr(A3),D0 ; get old gamma table MOVE.L A0,saveGammaPtr(A3) ; save new gamma table TST.L D0 ; was there an old one? BEQ.S @SizeOK ; => no, already disposed MOVE.L D0,A0 ; else get old table _DisposPtr ; and dispose of old gamma table MOVE.L saveGammaPtr(A3),A0 ; get new gamma table back Move.l A0,saveGamDispPtr(A3) ; save it for disposal ; Copy the gamma table header. @SizeOK MOVE gChanCnt(A2),D0 ; get number of tables MOVE gFormulaSize(A2),D1 ; get size of formula data MOVE gDataCnt(A2),D2 ; get number of entries MOVE.L (A2)+,(A0)+ ; copy gamma header MOVE.L (A2)+,(A0)+ ; which is MOVE.L (A2)+,(A0)+ ; 12 bytes long ; Copy the data. MULU D0,D2 ; multiply by number of tables ADD D1,D2 ; add in size of formula data SUBQ #1,D2 ; get count - 1 @NxtByte MOVE.B (A2)+,D0 ; get a byte MOVE.B D0,(A0)+ ; move a byte DBRA D2,@NxtByte ; => repeat for all bytes Bra.s @GammaDone ; Check to see if itÕs a direct device. ; ; Set up a linear gamma table. To prevent memory thrash, build this new one ; the same size as the existing one (one or three channel). ; @LinearTab MOVE.L saveGammaPtr(A3),A0 ; get current gamma in A0 MOVE.W gFormulaSize(A0),D0 ; get size of formula in new MOVE.W gChanCnt(A0),D2 ; get the number of tables SUBQ #1,D2 ; zero based, of course Move.w gDataCnt(A0),D3 ; get the number of entries Subq #1,D3 ; zero base ADDA #gFormulaData,A0 ; point to tables ADDA D0,A0 ; point past monID, if present @ChanLoop MOVE.W D3,D0 ; loop count within each channel @entryLoop MOVE.B D0,(A0) ; write this value out Not.b (A0)+ ; invert to make table ramp properly DBRA D0,@entryLoop ; for each entry in channel DBRA D2,@ChanLoop ; and each channel @GammaDone BTST #IsDirect,GFlags(A3) ; are we in a direct mode? BEQ.S @Out ; if not, then we're done BSR SonoraDirectCLUTSet ; if so, then set up direct CLUT ramps @Out BRA SonoraCtlGood ; => return no error ENDWITH SonoraGrayPage ;--------------------------------------------------------------------- ; ; Clear the specified page in the current mode to gray ; ; A0 = Ptr to private storage ; A1 = Ptr to AuxDCE ; A2 = Ptr to cs parameter record ; A3 = Ptr to driver privates ; ;--------------------------------------------------------------------- WITH SonoraVidPrivates MOVE saveMode(A3),D1 ; D1 = mode BSR SonoraChkMode ; convert mode to depth in D1 BNE SonoraCtlBad ; => not a valid depth MOVE csPage(A2),D0 ; D0 = page BNE SonoraCtlBad ; => not a valid page BSR SonoraGrayScreen ; paint the screen gray BTST #IsDirect,GFlags(A3) ; are we in a direct mode? BEQ.S @Out ; if not, then we're done BSR SonoraDirectCLUTSet ; if so, then set up direct CLUT ramps @Out BRA SonoraCtlGood ; => return no error ENDWITH SonoraSetGray ;--------------------------------------------------------------------- ; ; Set luminance mapping on (csMode = 1) or off (csMode = 0) ; ; When luminance mapping is on, RGB values passed to setEntries are mapped ; to grayscale equivalents before they are written to the CLUT. ; ; A1 = Ptr to AuxDCE ; A2 = Ptr to cs parameter record ; ;--------------------------------------------------------------------- WITH SonoraVidPrivates BTST #IsMono,GFlags(A3) ; is this a mono-only monitor? BEQ.S @1 ; if not, then go ahead MOVE.B #1,csMode(A2) ; always turn on for mono devices @1 MOVEQ #0,D1 ; set up for BFEXTU to point to GrayFlag BSR.S SonoraSetIntCom ; call common code BRA SonoraCtlGood ; all done ; ; This shared routine setup up a flag in GFlags. It takes a pointer to ; private storage in A3, and the bit field start location in D1. ; SonoraSetIntCom MOVE.B csMode(A2),D0 ; get boolean BFINS D0,GFlags(A3){D1:1} ; set flag bit RTS ; and return ENDWITH SonoraSetInterrupt ;--------------------------------------------------------------------- ; ; Enable (csMode = 0) or disable (csMode = 1) VBL interrupts ; ; As a future performance enhancement, interrupts on the card can be ; disabled or enabled from software. For instance, if the cursor is ; not on a screen, and there is nothing in the Slot Interrupt Queue ; for that device, interrupts may be disabled reducing interrupt ; overhead for the system. ; ; The slot interrupt queue element is always allocated by the Open call. ; This routine just inserts and removes it from the slot interrupt task queue. ; ; A1 = Ptr to AuxDCE ; A2 = Ptr to cs parameter record ; A3 = Ptr to private storage ; ;--------------------------------------------------------------------- WITH VDPageInfo,SlotIntQElement,SonoraVidPrivates MOVEQ #1,D1 ; set up for BFEXTU to point to IntDisFlag BSR.S SonoraSetIntCom ; call common code BNE.S @DisableThem ; if zero, then enable ; This code enables interrupts and installs the interrupt handler. ; BSR.S SonoraEnableVGuts ; call common code BNE SonoraCtlBad ; error, flag problem BRA SonoraCtlGood ; and go home ; This code disables VBL interrupts, then removes the interrupt handler. ; @DisableThem BSR.S SonoraDisableVGuts ; jump to the disabling utility BRA SonoraCtlGood ; all done ; The following two routines are common code shared between the Open/Close calls ; and the SetInterrupt control call. ; SonoraDisableVGuts MOVE.W SR,-(SP) ; preserve the status register BSR SonoraWaitVSync ; to be safe, wait for the next VBL Move.l saveSonoraBase(A3),A0 ; Get the Sonora base address. Adda.l #SonoraVIA2Base,A0 ; Point to the interrupt register space. Move.b #$40,SonoraSlotIER(A0) ; Disable Slot $0 interrupts. MOVE (SP)+,SR ; re-enable cursor interrupts CLR D0 ; setup slot # for _SIntRemove (slot zero!) MOVE.L saveSQElPtr(A3),A0 ; get the SQ element pointer _SIntRemove ; remove the interrupt handler RTS SonoraEnableVGuts MOVE.L saveSQElPtr(A3),A0 ; get the queue element LEA SonoraBeginIH,A2 ; save Pointer to interrupt handler MOVE.W #SIQType,SQType(A0) ; setup queue ID MOVE.L A2,SQAddr(A0) ; setup int routine address MOVE.L A3,SQParm(A0) ; pass pointer to privates as the parameter CLR.W D0 ; setup slot zero _SIntInstall ; and do install BNE.S @IntBad Move.l A0,-(Sp) ; Save the queue element pointer. Move.l saveSonoraBase(A3),A0 ; Get the Sonora base address. Adda.l #SonoraVIA2Base,A0 ; Point to the interrupt register space. Move.b #$C0,SonoraSlotIER(A0) ; Enable Slot $0 interrupts. Move.l (Sp)+,A0 ; Restore the queue elemetn pointer. CMP D0,D0 ; clear z-bit for good result @IntBad RTS ; return home (if bad, z-bit is set above, so just leave) <8> ENDWITH SonoraDirectSetEntries ;--------------------------------------------------------------------- ; ; Change the CLUT in a direct mode. ; ; A1 = Ptr to AuxDCE ; A2 = Ptr to cs parameter record ; A3 = Ptr to private storage ; ; This routine allows knowledgeable programs modify the contents ; of the CLUT in direct modes (usually for limited color previewing). ; It takes the same parameter block as SetEntries, but SetEntries ; intentionally does not operate when the card is in a direct pixMode. ; This routine takes the same data and operates ONLY when in direct ; modes. It calls the same SetEntries guts as the regular routine. ; ;--------------------------------------------------------------------- BTST #IsDirect,GFlags(A3) ; are we in a direct mode? BEQ SonoraCtlBad ; error if not BRA SonoraSEGuts ; jump to SetEntries internals if it's OK SonoraSetDefaultMode ;--------------------------------------------------------------------- ; ; Write the card default mode into slot pRAM. ; ; A1 = Ptr to AuxDCE (but trashed on exit) ; A2 = Ptr to cs parameter record ; A3 = Ptr to private storage ; ; This routine is called by Monitors when somebody selects an alternate ; video mode family in the Options dialog. ; ;--------------------------------------------------------------------- WITH spBlock,SonoraVidPrivates,SP_Params ; ; Set up a slot parameter block on the stack. ; SUBA #spBlockSize,SP ; make an slot parameter block on stack MOVE.L SP,A0 ; get pointer to parm block now MOVE.B dCtlSlot(A1),spSlot(A0) ; put slot in pBlock CLR.B spExtDev(A0) ; external device = 0 ; ; Read the slot pRAM to determine what the currently saved mode is. The first ; word is the board ID, followed by the default screen depth. Built-in video keeps the video ; sRsrc spID in VendorUse2. ; SUBA #SizesPRAMRec,SP ; allocate block for pRAM record MOVE.L SP,spResult(A0) ; point to it _sReadPRAMRec ; read it ; ; Since PrimaryInit relies on the default mode being set correctly, we check to see that ; the mode to be set is actually valid. Monitors can only see valid sRsrcIDs, so ; it probably wonÕt cause a problem. But we should check it anyway for unsavory ; applications. ; Move.b csMode(A2),spID(A0) ; Look for the passed in spID. Clr.l spParamData(A0) ; Clear the fNext flag; we want THIS sRsrc. Ori.b #(1< new max mode, Bgt SonoraCtlBad ; then punt. Move.w D1,D0 ; Save the indexed mode. ; Switch to the new resolutionÉ ; Cmp.b dCtlSlotID(A1),D2 ; If weÕre already in the requested resolution, Beq SonoraSetVidModeGuts ; then go try the depth switch. Move.b D2,D1 ; Set up to do the resolution switch. Bsr SonoraSetResolution ; Switch to the new resolution. Move.w D0,D1 ; Set up to do the depth switch. Bra SonoraSetVidModeGuts ; Switch to the new depth. Endwith SonoraSetSync ; ;--------------------------------------------------------------------- ; ; Enable (csMode = 0) or disable (csMode = 1) the DB15 Sync ; ; This routine will disable the sync lines going to the DB15 connector ; so that "green-aware" monitors can go into power-saving mode. If we ; are not driving a monitor out of that port, it will return an error. ; ; Note: It might be that in the future, there will be several levels of ; power-saving; Dropping CSYNC- will be one, HSYNC-, another, and ; both syncs another. Since Civic can only drop both, I will ignore ; a second undefined parameter. ; ; A2 = Ptr to cs parameter record ; A3 = Ptr to private storage ; ;--------------------------------------------------------------------- WITH SonoraVidPrivates ; ; movea.l saveSonoraBase(a3),a0 ; Get Sonora Base (VIA1 base $50f00000) ; adda.l #SonoraVdCtlBase,a0 ; add the difference to get Vid Reg ($28000) ; Move.b SonoraVdModeReg(A0),D0 ; Get the appropriate monitor code. ; ; Bset #SonoraVidBlnkBit,D0 ; Set up for SonoraWrite ; MOVE.b csMode(A2),D1 ; get the mode ; Bne.s @WriteToSyncs ; To disable, write a 1 to SonoraSyncEnable ; Bclr #SonoraVidBlnkBit,D0 ; else, write a 0 to enable ; @WriteToSyncs ; Move.b D0,SonoraVdModeReg(A0) ; And disable or enable the sycns ; ; BRA SonoraStatGood ; => return no error ; ; ENDWITH ; SonoraSetRubik560Mode ;--------------------------------------------------------------------- ; ; Enable (csMode = 0) or disable (csMode = non-zero) Rubik-560 mode. ; ; Note: ÒEnableÓ means Òswitch from 512 to 560Ó if youÕre ; not already in 560 mode, and ÒdisableÓ means Òswitch ; from Ò560 to 512Ó if youÕre not already in 512 mode. ; ; Also, note that this call will do nothing if we ; were Òstarted upÓ in 560 mode. ; ; Finally, this call only switches the Slot Manager, ; video driver, and hardware data and data structures. ; It is up to the calling application to inform the ; graphics environment (e.g., QuickDraw) that things ; have changed. ; ; A1 = Ptr to AuxDCE ; A2 = Ptr to cs parameter record ; A3 = Ptr to private storage ; ;--------------------------------------------------------------------- With SonoraVidPrivates,SpBlock ; Check to see if we even need to be here or notÉ ; Btst #InRubik560Mode,GFlags(A3) ; If we Òstarted upÓ in Rubik-560 mode, Bne SonoraCtlGood ; then thereÕs nothing to do here. ; Check to see if we can even do what was requestedÉ ; Tst.b csMode(A2) ; If weÕre supposed to be ÒenablingÓ 560 mode, Beq.s @Chk512 ; check to see if weÕre in 512 mode. Cmp.b #sRsrc_Vid_Sonora_GS560a,dCtlSlotID(A1) ; If weÕre in 560a mode then, Beq.s @Setup512a ; set up to switch back to 512a mode. Cmp.b #sRsrc_Vid_Sonora_GS560b,dCtlSlotID(A1) ; If weÕre in 560b mode then, Beq.s @Setup512b ; set up to switch back to 512b mode. Bra SonoraCtlBad ; Otherwise, weÕre not supposed to be here! @Chk512 Cmp.b #sRsrc_Vid_Sonora_GSa,dCtlSlotId(A1) ; If weÕre in 512a mode then, Beq.s @Setup560a ; set up to switch to 560a mode. Cmp.b #sRsrc_Vid_Sonora_GSb,dCtlSlotId(A1) ; If weÕre in 512b mode then, Beq.s @Setup560b ; set up to switch to 560b mode. Bra SonoraCtlBad ; Otherwise, weÕre not supposed to here! ; Do the required switchÉ ; @Setup512a Move.b #sRsrc_Vid_Sonora_GSa,D1 ; Set up for 512a mode. Bra.s @DoIt @Setup512b Move.b #sRsrc_Vid_Sonora_GSb,D1 ; Set up for 512b mode. Bra.s @DoIt @Setup560a Move.b #sRsrc_Vid_Sonora_GS560a,D1 ; Set up for 560a mode. Bra.s @DoIt @Setup560b Move.b #sRsrc_Vid_Sonora_GS560b,D1 ; Set up for 560b mode. @DoIt Bsr SonoraSetResolution ; Switch to the desired mode. Bra SonoraCtlGood ; Return good result. Endwith SonoraSetAltSense ;--------------------------------------------------------------------- ; ; SetAltSense sets up the alternate senseID pRam byte to contain ; a valid ÒindexÓ code if a valid sense code is passed ; in csMode (byte 0 is the sense code, byte 1 is the type). ; ; A1 = Ptr to AuxDCE/Ptr to MonIDs table (not restored) ; A2 = Ptr to cs parameter record ; A3 = Ptr to private storage ; ;--------------------------------------------------------------------- With SpBlock,SonoraVidPrivates,SP_Params ; ; First, set up a slot parameter block on the stack. ; Suba.w #spBlockSize,Sp ; Make an SpBlock on the stack. Move.l Sp,A0 ; Get a pointer to it into A0. Move.b dCtlSlot(A1),spSlot(A0) ; Set it up. Clr.b spExtDev(A0) ; ; Next, read the current pRam so that we can set it appropriatelyÉ ; Suba.w #SizesPRAMRec,Sp ; Make an sPRAM block on the stack. Move.l Sp,spResult(A0) ; Point to it. _sReadPRamRec ; Get pRAM. ; ; See what we need to doÉ ; Moveq #0,D0 ; Clear D0 for good measure. Move.b csMode+1(A2),D0 ; Get the sense-code type. Lea MonIDsTbl,A1 ; Point to the MonIDs table. @TypeLoop Cmp.w (A1)+,D0 ; If weÕve come to the end of the table, Bmi.s @MonIDNotValid ; then leave with an error. Beq.s @ChkOffset ; If weÕve found a match, then check it. Tst.w (A1)+ ; Otherwise, skip the offset. Bra.s @TypeLoop ; And keep looping until weÕre done. @ChkOffset Move.w (A1)+,D1 ; If the offset to the code table is nil, Beq.s @MonIDNotValid ; then leave with an error. Lea MonIDsTbl,A1 ; Otherwise, re-point to the MonIDsTbl. Adda.w D1,A1 ; And point to the appropriate sense-code table. Move.b csMode(A2),D0 ; Get the sense code. @CodeLoop Cmp.b (A1)+,D0 ; If weÕve come to the end of the table, Bmi.s @MonIDNotValid ; then leave with an error. Beq.s @ChkIt ; If weÕve found a match, then check it. Tst.b (A1)+ ; Otherwise, skip the indexed code. Bra.s @CodeLoop ; And keep looping until weÕre done. @ChkIt Move.b (A1)+,D1 ; Get the indexed ID for this code. Cmpi.b #indexedNoConnect,D1 ; If itÕs the no-connect code, Beq.s @ClearIt ; then say so. Move.b D1,SP_AltSense(Sp) ; Write out ÒindexÓ to pRam record. Ori.b #spAltSenseValidMask,SP_AltSense(Sp) ; Validate it. Bra.s @WritePRam @ClearIt Clr.b SP_AltSense(Sp) ; Invalidate no-connect byte. @WritePRam Move.l Sp,spsPointer(A0) ; Set up to whack pRam. _sPutPRAMRec ; Whack it. Adda.w #SizesPRAMRec+spBlockSize,Sp ; Restore stackÉ Bra SonoraCtlGood ; Éand leave. @MonIDNotValid Adda.w #SizesPRAMRec+spBlockSize,Sp ; Restore stackÉ Bra SonoraCtlBad ; Éleave with error. Endwith ********************************************************************** * * VideoClose releases the device's private storage and removes the * interrupt handler. * * * Entry: A0 = param block pointer * A1 = AuxDCE pointer * * Other: A2 = temporary AuxDCE pointer copy * ********************************************************************** SonoraVidClose WITH SonoraVidPrivates MOVE.L dCtlStorage(A1),A3 ; A3 <- Ptr to private storage MOVE.L (A3),D0 ; _StripAddress ; MOVE.L D0,A3 ; ; Disable interruptsÉ ; BSR SonoraDisableVGuts ; call utility to deactivate interrupts ; Blank the screenÉ ; Movea.l saveSonoraBase(A3),A0 ; Get the Sonora base address. Adda.l #SonoraVdCtlBase,A0 ; Point to the video control register space. Bset #SonoraVidBlnkBit,SonoraVdModeReg(A0) ; Turn on video blanking. ; Dispose of storageÉ ; MOVE.L saveSQElPtr(A3),A0 ; get the slot interrupt queue element ptr _DisposPtr MOVE.L saveGamDispPtr(A3),A0 ; get pointer to gamma table block _DisposPtr ; and dispose it Move.l saveVidPtr(A3),A0 ; Get pointer to video parameters block, _DisposPtr ; and dispose of it. MOVE.L dCtlStorage(A1),A0 ; dispose of the private storage _DisposHandle ; ; Return no errorÉ ; MOVEQ #noErr,D0 ; no error RTS ; and return ENDWITH ********************************************************************** * * Video Driver Status Call Handler. There are eleven standard calls: * * ($00) Error * ($01) Error * ($02) GetMode * ($03) GetEntries * ($04) GetPage * ($05) GetPageBase * ($06) GetGray * ($07) GetInterrupt * ($08) GetGamma * ($09) GetDefaultMode * ($0A) GetCurMode * * The following calls are Sonora-specific: * * ($83) GetAltSense * * Entry: A0 = paramblock pointer * A1 = AuxDCE pointer * Uses: A2 = cs parameters * A3 = pointer to private storage * D0-D3 = scratch (don't need to be preserved) * * Exit: D0 = error code * ********************************************************************** SonoraVidStatus MOVEM.L A0/A1,-(SP) ; Save exit registers. MOVE.L csParam(A0),A2 ; A2 <- Ptr to control parameters MOVE.L dCtlStorage(A1),A3 ; A3 <- Ptr to private storage MOVE.L (A3),D0 ; _StripAddress ; MOVE.L D0,A3 ; MOVE.W csCode(A0),D0 ; get routine selector Cmp.w #cscAltSense,D0 ; If we got the AltSense call, Beq SonoraGetAltSense ; hop to it. CMP.W #$0D,D0 ;IF csCode NOT IN [0..$0A] THEN BHI.S SonoraStatBad ; Error, csCode out of bounds. MOVE.W SonoraStatJumpTbl(PC,D0.W*2),D0 ;Get the relative offset to the routine. JMP SonoraStatJumpTbl(PC,D0.W) ;GOTO the proper routine. SonoraStatJumpTbl DC.W SonoraStatBad-SonoraStatJumpTbl ;$00 => Error DC.W SonoraStatBad-SonoraStatJumpTbl ;$01 => Error DC.W SonoraGetMode-SonoraStatJumpTbl ;$02 => GetMode DC.W SonoraGetEntries-SonoraStatJumpTbl ;$03 => GetEntries DC.W SonoraGetPage-SonoraStatJumpTbl ;$04 => GetPage DC.W SonoraGetPageBase-SonoraStatJumpTbl ;$05 => GetPageBase DC.W SonoraGetGray-SonoraStatJumpTbl ;$06 => GetGray DC.W SonoraGetInterrupt-SonoraStatJumpTbl ;$07 => GetInterrupt DC.W SonoraGetGamma-SonoraStatJumpTbl ;$08 => GetGamma DC.W SonoraGetDefaultMode-SonoraStatJumpTbl ;$09 => GetDefaultMode DC.W SonoraGetCurMode-SonoraStatJumpTbl ;$0A => GetCurMode DC.W SonoraGetSyncs-SonoraStatJumpTbl ;$0B => cscGetSync ; DC.W SonoraGetConnection-SonoraStatJumpTbl ;$0C => cscGetConnection DC.W SonoraGetModeTiming-SonoraStatJumpTbl ;$0D => cscGetModeTiming SonoraStatBad MOVEQ #statusErr,D0 ; else say we don't do this one BRA.S SonoraStatDone ; and return SonoraStatGood MOVEQ #noErr,D0 ; return no error SonoraStatDone MOVEM.L (SP)+,A0/A1 ; Restore exit registers. BRA SonoraExitDrvr SonoraGetMode ;--------------------------------------------------------------------- ; ; Return the current mode ; ; Inputs : A2 = pointer to csParams ; A3 = pointer to private storage ; ;--------------------------------------------------------------------- WITH SonoraVidPrivates MOVE.W saveMode(A3),csMode(A2) ; return the mode Clr.w csPage(A2) ; return the page number (always 0) Move.l saveBaseAddr(A3),csBaseAddr(A2) ; Return the screen baseAddr. BRA.S SonoraStatGood ; => return no error ENDWITH SonoraGetEntries ;--------------------------------------------------------------------- ; ; Read the current contents of the CLUT. These values were gamma corrected ; when they were set (by SonoraSetEntries), so they may not match the source ; cSpec array. ; ; Inputs : A1 = pointer to AuxDCE ; A2 = pointer to csParams/CLUT read register (not restored to csParams) ; A3 = pointer to private storage ; ;--------------------------------------------------------------------- With SonoraCLUTRec Movem.l D4-D6,-(Sp) ; Save work registers. Tst.l -(Sp) ; Make some room. Move.l csTable(A2),D0 ; If we were handed a nil pointer, Beq @GEErr ; then hike. _StripAddress ; Make table pointer 32-bit clean. Move.l D0,(Sp) ; And save it. Move.w saveMode(A3),D1 ; Get the current video mode. Subi.w #firstVidMode,D1 ; Convert it to an index. Moveq #0,D3 ; clear all of D3 for .w compare. Lea SonoraCLUTTbl,A0 ; Point to the table of CLUT data. Lea (A0,D1*SonoraCLUTSize),A0 ; Point to the right entry. Move.b scRange(A0),D3 ; Get the CLUT range. Move.w csCount(A2),D4 ; Get the number of entries to change, Bmi @GEErr ; and hike if itÕs out of range. Cmp.w D3,D4 ; If D4-D3 > 0 (count - range > 0), Bhi @GEErr ; then hike. Tst.w csStart(A2) ; If weÕre doing indexed entries (-1), Bmi.s @skipStartChk ; then just go on. Add.w csStart(A2),D4 ; Adjust count for starting position. Cmp.w D3,D4 ; If D4-D3 > 0 (count - range > 0), Bhi @GEErr ; then hike. @skipStartChk Move.b scStart(A0),D2 ; Get the starting position. Move.w scSkip(A0),D5 ; Get the inter-entry skip factor. Move.w csCount(A2),D6 ; Remember the csCount. Cmpi.w #indexEntries,csStart(A2) ; If table accesses are to be indexed, Beq.s @GECom ; then go on. ; The following code is BAD, BAD, BAD! We should build our own table here so ; as to NOT mess up the userÕs data. But all the previous Apple video drivers ; have done the same thing here, so weÕll continue the trend for now. Move.l (Sp),A0 ; Get ptr to csTable. Move.w D6,D1 ; Get count. @TableLoop Move.w D4,value(A0,D1*colorSpecSize) ; Write the index into the table. Subq #1,D4 ; Decrement index. Dbra D1,@TableLoop ; @GECom Move.l (Sp)+,A0 ; Get/restore ptr to csTable. Move.l saveVDACBase(A3),A2 ; Get VDAC base address. Add.w #ArielDataReg,A2 ; Add offset to Palette read register. Move.w Sr,-(Sp) ; Save current interrupt level Bsr SonoraWaitVSync ; Wait for VBL. @Repeat Move.w value(A0),D1 ; Get the NEXT Clut position into D1. Cmp.w D3,D1 ; If this position is out of range, Bhi.s @Until ; then go on. Mulu.w D5,D1 ; Multiply index by skip value. Add.b D2,D1 ; Add in the starting posistion. Move.b D1,ArielAddrReg-ArielDataReg(A2) ; Tell the Clut where to read from. Move.b (A2),D1 ; Get Red: Move.b D1,rgb+red(A0) ; --> $rrXX Move.b D1,rgb+red+1(A0) ; --> $rrrr Move.b (A2),D1 ; Get Green: Move.b D1,rgb+green(A0) ; --> $ggXX Move.b D1,rgb+green+1(A0) ; --> $gggg Move.b (A2),D1 ; Get Blue: Move.b D1,rgb+blue(A0) ; --> $bbXX Move.b D1,rgb+blue+1(A0) ; --> $bbbb @Until Addq #colorSpecSize,A0 ; Point to next entry ColorTable. Dbra D6,@Repeat Move.w (Sp)+,Sr ; Restore previous interrupt level. Movem.l (Sp)+,D4-D6 ; Restore work registers. Bra SonoraStatGood ; Return noError. @GEErr Tst.l (Sp)+ ; Clean up stack. Movem.l (Sp)+,D4-D6 ; Restore work registers. Bra SonoraStatBad ; Return statError. SonoraGetPage ;--------------------------------------------------------------------- ; ; Return the number of pages in the specified mode. It's pretty simple; ; every mode has only one page. We do check if it's valid, however. ; ;--------------------------------------------------------------------- WITH SonoraVidPrivates MOVE csMode(A2),D1 ; get the mode BSR SonoraChkMode ; is this mode OK? BNE SonoraStatBad ; => not a valid mode MOVE.W #1,csPage(A2) ; return page count BRA SonoraStatGood ; => return no error ENDWITH SonoraGetPageBase ;--------------------------------------------------------------------- ; ; Return the base address for the specified page in the current mode ; ;--------------------------------------------------------------------- WITH SonoraVidPrivates TST.W csPage(A2) ; are we returning page zero info? BNE SonoraStatBad ; only page 0 is valid Move.l saveBaseAddr(A3),csBaseAddr(A2) ; Return the screen baseAddr. BRA SonoraStatGood ; => return no error ENDWITH SonoraGetGray ;--------------------------------------------------------------------- ; ; Return a boolean, set true if luminance mapping is on ; ;--------------------------------------------------------------------- WITH SonoraVidPrivates MOVEQ #0,D1 ; set up for BFEXTU SonoraGetFlagCom BFEXTU GFlags(A3){D1:1},D0 ; get the state of flag MOVE.B D0,csMode(A2) ; return value BRA SonoraStatGood ; => and return ENDWITH SonoraGetInterrupt ;--------------------------------------------------------------------- ; ; Return a boolean in csMode, set true if VBL interrupts are disabled ; ;--------------------------------------------------------------------- WITH SonoraVidPrivates MOVEQ #1,D1 ; set up BFEXTU to point at IntDisFlag BRA.S SonoraGetFlagCom ; and use common code ENDWITH SonoraGetGamma ;--------------------------------------------------------------------- ; ; Return the pointer to the current gamma table ; ;--------------------------------------------------------------------- WITH SonoraVidPrivates MOVE.L saveGammaPtr(A3),csGTable(A2) ; return the pointer to the structure BRA SonoraStatGood ; and return a good result ENDWITH SonoraGetDefaultMode ;--------------------------------------------------------------------- ; ; Read the card default mode from slot pRAM. ; ; A1 = Ptr to AuxDCE ; A2 = Ptr to cs parameter record ; A3 = Ptr to private storage ; ;--------------------------------------------------------------------- WITH spBlock,SonoraVidPrivates,SP_Params ; ; Set up a slot parameter block on the stack. ; SUBA #spBlockSize,SP ; make an slot parameter block on stack MOVE.L SP,A0 ; get pointer to parm block now MOVE.B dCtlSlot(A1),spSlot(A0) ; put slot in pBlock CLR.B spExtDev(A0) ; external device = 0 ; ; Read the slot pRAM to determine what the currently saved mode is. The first ; byte is the board ID, followed by the default mode. Built-in video keeps the last ; selected video sRsrc spID in VendorUse2. ; SUBA #SizesPRAMRec,SP ; allocate block for pRAM record MOVE.L SP,spResult(A0) ; point to it _sReadPRAMRec ; read it MOVE.B SP_LastConfig(SP),csMode(A2) ; return the result ADDA #SizesPRAMRec+spBlockSize,SP ; release buffer BRA SonoraStatGood ; ENDWITH SonoraGetCurMode ;--------------------------------------------------------------------- ; ; This call will generally be used in conjuntion with the ; the SwitchMode control call. Its function is ; basically to fill out the VDPageInfo with the ; current status. Note that, here, csData, is the ; current SlotID (sResource number) in use. ; ; Note: The implementation of this Status call can be used to ; determine whether the SwitchMode control call is ; implemented or currently accessible. ; ;--------------------------------------------------------------------- With SonoraVidPrivates,VDSwitchInfo ; Check to see if we even need to be here or notÉ ; Cmpi.w #indexedSenseMSB1,saveMonID(A3) ; If weÕre driving a multiscan display, Beq.s @EndMSChk ; then just keep going. Cmpi.w #indexedSenseMSB2,saveMonID(A3) ; If weÕre driving a multiscan display, Beq.s @EndMSChk ; then just keep going. Cmpi.w #indexedSenseMSB3,saveMonID(A3) ; If weÕre driving a multiscan display, Beq.s @EndMSChk ; then just keep going. Bra SonoraStatBad ; Otherwise, just go away. @EndMSChk ; Return the appropriate information if all is wellÉ ; Move.w saveMode(A3),csMode(A2) ; Return the current mode (bit depth). Moveq #0,D0 ; Clear out return-value register. Move.b dCtlSlotID(A1),D0 ; Get the byte-sized spID. Move.l D0,csData(A2) ; Make it long. Move.l saveBaseAddr(A3),csBaseAddr(A2) ; Return the base address for this page. Clr.w csPage(A2) ; But we only support page #0. Bra SonoraStatGood ; Vamoose. Endwith SonoraGetSyncs ; ;--------------------------------------------------------------------- ; ; Return a boolean, set true if RGB Syncs are OFF ; Clear the 2nd byte, saying we don't support levels of power-saving. ; ;--------------------------------------------------------------------- WITH SonoraVidPrivates ; ; movea.l saveSonoraBase(a3),a0 ; Get Sonora Base (VIA1 base $50f00000) ; adda.l #SonoraVdCtlBase,a0 ; add the difference to get Vid Reg ($28000) ; Move.b SonoraVdModeReg(A0),D0 ; Get the appropriate monitor code. ; ; Andi.l #$80,D0 ; and look at bit 7 ; Sne csMode(A2) ; if bit is 1, RGB Syncs are OFF, return true ; Clr.b csMode+1(A2) ; and the next byte for good measure. ; BRA SonoraStatGood ; => and return ; ; ENDWITH ; SonoraGetModeTiming ;--------------------------------------------------------------------- ; ; This call is used by Display Mgr to determine if a mode is ; valid on a given display. There are 5 possible results ; ; (1) The driver has no idea what this call is and returns statErr. ; This does not apply to the sonora driver ; ; (2) The driver knows with certainty (via sense codes) or via a ; direct connection to the display that the mode in question ; is good. In this case the timing need not be checked with ; an external component for the display. This is the case with ; non-multisync devices and panel displays directly connected by Apple. ; ; (3) The driver is multimode aware and knows there is a multimode monitor ; attached. The driver does not know the exact range of frequencies ; supported by the display and does not know if this mode is supported. ; ; (4) The driver is modeless (many many mode) aware and ; ; (5) The driver gives up control of mode searching to a table in the ; decl ROM. This will be the case for sonora driver. ; ; ; Note: This is a driver call so that ; (1) patches cannot easily be made to the decl data, but can be ; made to the driver. If we want to replace information, ; we can do so in the driver. ; (2) we are not tied to a decl data table format for modeless (many ; many mode) drivers in the future. And if we go to a different ; bus architeture (as people seem to want to) we will not be ; hosed by table based implementations. We may however be ; hosed by driver based implementations, but we can only do ; so much planning for the future and still ship product. ; ;--------------------------------------------------------------------- With SonoraVidPrivates,VDTimingInfo Cmpi.w #indexedSenseMSB1,saveMonID(A3) ; If weÕre driving a multiscan display, Beq.s @EndMSChk ; then just keep going. Cmpi.w #indexedSenseMSB2,saveMonID(A3) ; If weÕre driving a multiscan display, Beq.s @EndMSChk ; then just keep going. Cmpi.w #indexedSenseMSB3,saveMonID(A3) ; If weÕre driving a multiscan display, Beq.s @EndMSChk ; then just keep going. Bra SonoraStatBad ; Otherwise, just go away. @EndMSChk Move.l #kDeclROMtables,csTimingFormat(A2) Clr.l csTimingData(A2) Clr.l csTimingFlags(A2) Bra SonoraStatBad ; Bail Anyway (so DisplayMgr will look in decl ROM). ENDWITH SonoraGetConnection ;--------------------------------------------------------------------- ; ; This call is used by Display Mgr to get information about the connection ; betrween the display card and the display. ; ; The assumption is that GetConnetion only alters those fields in the ; VDDisplayConnectInfo record for which it has actual information. ; In this case for example, I do not know what the display component ; is so I do not touch it. Same for the csConnectReserved field ; ;--------------------------------------------------------------------- With SonoraVidPrivates,VDDisplayConnectInfo ; ¥¥¥ ; For now if we donÕt have a multiscan display we just say that its fixed. (Note: If we really ; wanted to support the Vail-specific family modes here, weÕd want to denote the GS/GS-560 ; and the HR/HR-560 display types. Maybe someday.) ; Move.w #kFixedModeCRTConnect,csDisplayType(A2) Clr.l csConnectFlags(A2) ; ¥¥¥ Danger: Tagging stuff not complete! ¥¥¥ Clr.w csConnectTagged(A2) ; ; ¥¥¥ Cmpi.w #indexedSenseMSB1,saveMonID(A3) ; If weÕre driving a multiscan display, Beq.s @EndMSChk1 ; then just keep going. Cmpi.w #indexedSenseMSB2,saveMonID(A3) ; If weÕre driving a multiscan display, Beq.s @EndMSChk2 ; then just keep going. Cmpi.w #indexedSenseMSB3,saveMonID(A3) ; If weÕre driving a multiscan display, Beq.s @EndMSChk3 ; then just keep going. Bra SonoraStatGood ; Otherwise, just go away. @EndMSChk1 Move.w #kMultiModeCRT1Connect,csDisplayType(A2) Bra.s @EndMSChk @EndMSChk2 Move.w #kMultiModeCRT2Connect,csDisplayType(A2) Bra.s @EndMSChk @EndMSChk3 Move.w #kMultiModeCRT3Connect,csDisplayType(A2) @EndMSChk Move.l #(1< no, not immediate RTS ; otherwise, it was an immediate call SonoraGoIODone MOVE.L JIODone,-(Sp) ; Get the IODone address, Rts ; and go there. ;--------------------------------------------------------------------- ; ; ChkMode ; ; Verifies the requested mode is legal. Converts spID in D1 into ; zero-based mode number since lots of people want it that way. ; ; <-> D1: Mode ; -> A3: Pointer to driver privates ; ; All registers preserved ; ; Returns EQ if mode is valid. ; With SonoraVidPrivates,SonoraVidParams SonoraChkMode Movem.l A0/D0,-(Sp) ; Save work registers. Sub.w #FirstVidMode,D1 ; Make mode zero-based. Blt.s @ModeBad ; If the passed-in mode is < 0, ; then punt. Move.l saveVidPtr(A3),A0 ; Get a pointer to the video params. Tst.b noVRAM(A3) ; If we donÕt have VRAM, then Bne.s @NoVRAM ; say so. Adda.w #svpMaxModeBase,A0 ; Point to the base of the max mode values. Bra.s @GetSize ; Skip the no-VRAM case. @NoVRAM Adda.w #svpNoRAMMaxBase,A0 ; Point to the base of the no-VRAM modes. @GetSize Move.b saveSizeVRAM(A3),D0 ; Get the vRAM size index. Move.b (A0,D0),D0 ; Get the maximum mode for this config. Sub.w #FirstVidMode,D0 ; Make the max mode zero-based. Cmp.w D0,D1 ; If the passed-in mode is > max mode, Bgt.s @ModeBad ; then punt. Cmp.w D1,D1 ; Set Eq when okay. @ModeBad Movem.l (Sp)+,A0/D0 ; Restore work registers. Rts Endwith ;--------------------------------------------------------------------- ; ; Wait for vertical blanking. Interrupts are set to level-2 in ; this routine. ; ; A3 = pointer to private storage ;--------------------------------------------------------------------- With SonoraVidPrivates SonoraWaitVSync MOVE.L A0,-(SP) ; Save work registers. MOVE.L D0,-(SP) ; (Two MOVEs are faster than a MOVEM.) MOVE.W SR,-(SP) ; Get the status register on stack. MOVEQ #7,D0 ; Get mask into D0. AND.B (SP),D0 ; Get the interrupt level. SUBQ.B #2,D0 ; BGE.S @OK ; If ³, then don't change. ORI.W #$0200,SR ; Raise above level-2. ANDI.W #$FAFF,SR ; Make it level-2 @OK Tst.w (Sp)+ ; Restore stack. Move.l saveSonoraBase(A3),A0 ; Get the Sonora base address. Adda.l #SonoraVIA2Base,A0 ; Point to the interrupt register space. Move.b #$40,SonoraSlotIFR(A0) ; Clear any pending interrupts. Adda.w #SonoraSlotIFR,A0 ; Point to the interrupt flag register. @SyncLoop Move.b (A0),D0 ; Read the VBL state. Btst #6,D0 ; If itÕs none pending (0=pending), Bne.s @SyncLoop ; then keep looping. @Done MOVE.L (SP)+,D0 ; Restore work registers. MOVE.L (SP)+,A0 ; (Two MOVEs are faster than a MOVEM.) RTS Endwith ;--------------------------------------------------------------------- ; ; SetDepth sets the depth in the DAC and the framebuffer. ; ; D1 contains the spID of the depth - $80 (the zero based mode ID) ; A1 = AuxDCE POINTER ; A2 = parameter block pointer ; A3 = dCtlStorage pointer ; ; Preserves all registers ; ;--------------------------------------------------------------------- SonoraSetDepth With SonoraVidPrivates Movem.l A0/D0-D2,-(Sp) ; Save our work registers. ; Wait for the next VBL before trying to switch depths. ; Move.w Sr,-(Sp) ; Save the current interrupt level. Bsr.s SonoraWaitVSync ; Wait for the next VBL. ; Switch the framebufferÕs depth. ; Move.l saveSonoraBase(A3),A0 ; Get the Sonora base address. Adda.l #SonoraVdCtlBase,A0 ; Point to the video control register space. Move.b D1,SonoraVdColrReg(A0) ; Set the depth. ; Switch the CLUT/DACÕs depth. ; Move.l saveVDACBase(A3),A0 ; Point to Ariel. Move.b ArielConfigReg(A0),D0 ; Save the current state. Andi.b #ClrDepthBitsMask,D0 ; Clear the depth bits. Or.b D1,D0 ; Turn on the depth bits. Move.b D0,ArielConfigReg(A0) ; Set the depth. ; Go home. ; Move.w (Sp)+,Sr ; Restore the interrupt level. Movem.l (Sp)+,A0/D0-D2 ; Restore the work registers. Rts ; Return to caller. Endwith ;--------------------------------------------------------------------- ; ; SetResolution gets the video driver and the framebuffer controller ; set up for being switched to a different resolution. ; ; D1 containts the spID to the resolution (mode) to enable. ; A1 = AuxDCE POINTER ; A2 = parameter block pointer ; A3 = dCtlStorage pointer ; ; Preserves all registers ; ;--------------------------------------------------------------------- SonoraSetResolution With SonoraVidParams,SpBlock,EgretPB,SonoraOmega Movem.l D0-D1/A0/A4-A6,-(Sp) ; Save work registers. Move.b dCtlSlotId(A1),D0 ; Remember which sRsrc to disable. Move.b D1,dCtlSlotId(A1) ; Remember which sRsrc to enable. ; Do the Slot Manager changesÉ ; Suba.w #spBlockSize,Sp ; Allocate a slot parameter block on the stack. Move.l Sp,A0 ; Point to it with A0. Clr.b spSlot(A0) ; Say that weÕre Slot $0. Clr.b spExtDev(A0) ; DonÕt ask why, just clear this guy. Move.b D0,spID(A0) ; Write out the spID of the sRsrc. _SRsrcInfo ; Update our SpBlock for SUpdateSRT call below. Move.l #1,spParamData(A0) ; Say that we want it disabled. _SetSRsrcState ; Do it. Move.b dCtlSlotId(A1),spID(A0) ; Write out the spID of the sRsrc. Move.l #0,spParamData(A0) ; Say that we want it enabled. _SetSRsrcState ; Do it. _SUpdateSRT ; Tell Slot Manager to update itself (it should really know, but it does not). ; Load the ÒnewÓ video hardware setup blockÉ ; Clr.w spID(A0) ; Start looking at spID 0, no external devices. Clr.b spTBMask(A0) ; Only look for the board sRsrc. Move.w #catBoard,spCategory(A0) ; Look for: catBoard, Move.w #typBoard,spCType(A0) ; typBoard, Clr.w spDrvrSW(A0) ; 0, Clr.w spDrvrHW(A0) ; 0. Clr.l spParamData(A0) ; (The board sRsrc must be enabled.) Bset #foneslot,spParamData+3(A0) ; Limit search to slot 0. _GetTypeSRsrc ; Get the spsPointer. Move.b #sVidParmDir,spID(A0) ; Look for the video parameters dir. _sFindStruct ; Load it. Move.b dCtlSlotId(A1),spID(A0) ; Look in the directory for this config's parameters. _sGetBlock ; Load it. Move.l spResult(A0),-(Sp) ; Save the new privates. Movea.l saveVidPtr(A3),A0 ; Point to the old privates. _DisposPtr ; Dispose them. Move.l (Sp)+,saveVidPtr(A3) ; Start using the new ones. Adda.w #spBlockSize,Sp ; De-allocate the slot parameter block. ; Change the hardwareÉ ; Movea.l saveVidPtr(A3),A4 ; Point to vidParams. Move.l UnivInfoPtr,A6 ; Get a pointer to the ProductInfo table. Adda.l ProductInfo.DecoderInfoPtr(A6),A6 ; Point to the base address table. Move.l DecoderInfo.VIA1Addr(A6),A6 ; Get the Sonora base address. Adda.l #SonoraVdCtlBase,A6 ; Point to the video control address space. Bset #SonoraVidBlnkBit,SonoraVdModeReg(A6) ; Blank video (to eliminate visual artifacts). Adda.l #SonoraVIA2Base-SonoraVdCtlBase,A6 ; Point to the VIA2. Move.b SonoraVRAMSize(A6),D0 ; Pick up the VRAM Sizing parameters. Adda.l #SonoraVdCtlBase-SonoraVIA2Base,A6 ; Point to the video control address space. Move.w Sr,-(Sp) ; Save current interrupt level Bsr SonoraWaitVSync ; Wait for next VBL. Tst.b noVRAM(A3) ; If we donÕt have real VRAM, then Bne.s @SkipDotClock ; skip the dot-clock setup. Movea.l A4,A5 ; Copy the vidParams Ptr. Cmpi.b #Sonora512Kb,D0 ; If weÕve got 512K on the motherboard Beq.s @Omega2 ; then weÕve got a new Omega. Cmpi.b #Sonora768Kb,D0 ; Bne.s @SetOmega ; @Omega2 Addq #SOmegaSize,A5 ; Skip past the Omega-1 parameters. @SetOmega Moveq #0,D0 ; n d p <- 0000000 0000000 00 Move.b SOmegaN(A5),D0 ; n d p <- 0000000 00NNNNN NN Lsl.w #OmegaDBits,D0 ; n d p <- 00NNNNN NN00000 00 Move.b SOmegaD(A5),D1 ; n d p <- 00NNNNN NN00000 00 (0DDDDDDD) Or.b D1,D0 ; n d p <- 00NNNNN NNDDDDD DD Move.b SOmegaP(A5),D1 ; n d p <- 00NNNNN NNDDDDD DD (000000PP) Lsl.w #OmegaPBits,D0 ; n d p <- NNNNNNN DDDDDDD 00 (000000PP) Or.b D1,D0 ; n d p <- NNNNNNN DDDDDDD PP Swap D0 ; Move the ndp value into the MSW. Or.b #sndSonoraReverseDFAC,D0 ; Move our sound value in. Suba.w #EgretPBSize,Sp ; Allocate space for the Egret param block on the stack. Move.l Sp,A0 ; Point to it with A0. Move.w #(PseudoPkt<<8)+WrDFAC,pbCmdType(A0) ; Say that weÕre writing DFAC. Move.l D0,pbParam(A0) ; Put out the DFAC data. Move.w #4,pbByteCnt(A0) ; WeÕre sending 4 bytes. Clr.w pbResult(A0) ; Clear the result word. Clr.l pbCompletion(A0) ; No completion routines here. _EgretDispatch ; Do it. Adda.w #EgretPBSize,Sp ; De-allocate the Egret parameter block. @SkipDotClock Move.b svpMonitorCode(A4),SonoraVdModeReg(A6) ; Write out the appropriate monitor code. Bsr SonoraWaitVSync ; Wait for next VBL. Bclr #SonoraVidBlnkBit,SonoraVdModeReg(A6) ; Unblank display. Move.w (Sp)+,Sr ; Restore the previous interrupt level. Movem.l (Sp)+,D0-D1/A0/A4-A6 ; Restore the work register. Rts Endwith ;--------------------------------------------------------------------- ; ; Fill the screen with a 50% dithered gray pattern. To have gotten here ; we must have had a valid display connected, so there are not tests ; for inactive displays here. ; ; D1 = spID of screen depth - FirstVidMode ; A1 = Ptr to AuxDCE ; A3 = driver private storage ; ; All registers are preserved ; With SonoraVidPrivates,SonoraVidParams SonoraGrayScreen Movem.l D0-D5/A1-A2,-(Sp) ; Save our work registers. Move.l saveBaseAddr(A3),A2 ; Get the base address of the screen. Move.b dCtlSlotId(A1),D5 ; Get the slotID (which is non-zero). Cmp.b #sRsrc_Vid_Sonora_GS560a,D5 ; If weÕre driving the Rubik-560a, Beq.s @Chk1bpp ; then check for 1bpp. Cmp.b #sRsrc_Vid_Sonora_GS560b,D5 ; If weÕre not driving the Rubik-560b, Bne.s @GetParms ; then just go on @Chk1bpp Tst.b D1 ; If weÕre not in 1bpp, Bne.s @GetParms ; then just go on. Clr.b D5 ; Otherwise, say rowbytes is wrong. @GetParms Move.l saveVidPtr(A3),A1 ; Get a pointer to the vidParams. Move.w svpNumRows(A1),D4 ; Get the number of rows to gray. Adda.w #SVPHdrSize,A1 ; Skip past the header info. Move.w (A1,D1*2),D3 ; Get the number of longwords/row. Lea SonoraPats,A1 ; Point to the table of gray patterns. Move.l (A1,D1*4),D2 ; Get the gray pattern. Moveq #true32b,D0 ; Set up to flip into 32-bit addressing mode. _SwapMMUMode ; Do flip. @NxtRow Move.l D3,D1 ; Get the number of longswords/row. @NxtLong Move.l D2,(A2)+ ; Write out gray to the frame bufferÉ Dbra D1,@NxtLong ; Éfor each scanline. Tst.b D5 ; If this is not a Rubik-560 at 1bpp, Bne.s @Skip560 ; then just go on. Move.w D2,(A2)+ ; Otherwise, write out 16 more pixels. @Skip560 Not.l D2 ; Invert the pattern for the next row. Dbra D4,@NxtRow ; Repeat for each row. _SwapMMUMode ; Switch back to the previous addressing mode. Movem.l (Sp)+,D0-D5/A1-A2 ; Restore the work registers. Rts ; Return to caller. Endwith ;--------------------------------------------------------------------- ; ; Trans5to8 ; ; <-> D1: 5-bit value to be converted into an 8-bit index. ; SonoraTrans5to8 Move.l D0,-(Sp) ; Save D0 as scratch. Moveq #0,D0 ; Clear it. Move.w D1,D0 ; D1 = ---43210, D0 = ---43210. Lsl.w #3,D1 ; D1 = 43210---, D0 = ---43210. Lsr.w #2,D0 ; D1 = 43210---, D0 = -----432. Or.w D0,D1 ; D1 = 43210432. Move.l (Sp)+,D0 ; Restore D0. Rts ;--------------------------------------------------------------------- ; ; DirectCLUTSet writes gamma-corrected ascending grayscale-like ramps ; into the CLUT ; ; A3 = dCtlStorage pointer ; ; Preserves all registers used. ; ;--------------------------------------------------------------------- With SonoraVidPrivates SonoraDirectCLUTSet MOVEM.L D0-D2/A0/A4-A6,-(SP) ; save registers MOVE.L saveGammaPtr(A3),A0 ; get pointer to gamma data structure MOVE.W gFormulaSize(A0),D0 ; get the size of formula data LEA gFormulaData(A0),A4 ; point to formula data ADD D0,A4 ; red correction table starts here MOVE.L A4,A5 ; get default pointer to green data MOVE.L A4,A6 ; get default pointer to blue data CMP.W #1,gChanCnt(A0) ; if only only one table, we're set BEQ.S @OneTbl ; => just one table MOVE gDataWidth(A0),D1 ; get width of each entry in bits ADDQ #7,D1 ; round to nearest byte LSR #3,D1 ; get bytes per entry MULU gDataCnt(A0),D1 ; get size of table in bytes ADDA D1,A5 ; calc base of green ADDA D1,A6 ; calc baseÉ ADDA D1,A6 ; Éof blue @OneTbl MOVE.W gDataCnt(A0),D2 ; Get number of entries. Subq #1,D2 ; Make it zero based. MOVE.L saveVDACBase(A3),A0 ; point to the hardware ADDA #ArielDataReg,A0 ; point to data register CLR.B ArielAddrReg-ArielDataReg(A0) ; start at the beginning of CLUT Move.w #$1F,D2 ; There are only 32 entries to whack in 16bpp mode. MOVE.W SR,-(SP) ; preserve the status register BSR SonoraWaitVSync ; wait for next blanking period (preserves A0) ; Write an incrementing grayscale ramp. Moveq #0,D0 ; Start the ramp at zero. @Repeat Move.w D0,D1 ; Copy the current index. Bsr.s SonoraTrans5to8 ; Translate from 5-bits to 8. MOVE.B (A4,D1),(A0) ; Write gamma-corrected red. MOVE.B (A5,D1),(A0) ; Write gamma-corrected green MOVE.B (A6,D1),(A0) ; Write gamma-corrected blue. Addq #1,D0 ; Increment the ramp index. DBRA D2,@Repeat ; Loop until done. MOVE.W (SP)+,SR ; restore the status reg MOVEM.L (SP)+,D0-D2/A0/A4-A6 ; restore saved registers RTS Endwith ;------------------------------------------------------------- ; The Interrupt handler for the Sonora Built-In Video ;------------------------------------------------------------- ; On entry A1 contains the pointer to the driver's private storage. ; With SonoraVidPrivates SonoraBeginIH Move.l saveSonoraBase(A1),A0 ; Get the Sonora base address. Adda.l #SonoraVIA2Base,A0 ; Point to the interrupt register space. Move.b #$40,SonoraSlotIFR(A0) ; Clear this interrupt. Moveq #0,D0 ; Set up for Slot $0É Jsr ([jVBLTask]) ; Éand call the VBL Task Manager. MOVEQ #1,D0 ; signal that int was serviced RTS ; and return to caller Endwith END